6e5ff72a29
feat(editor): 挂载 CodeBlockNodeView + editor.storage 注入 onRunCode
...
CodeBlockLowlight.extend({ addNodeView }) 接入自定义 NodeView。
EditorOptions 新增 onRunCode 字段;编辑器初始化时挂到 editor.storage,
NodeView 经 storage key 读取(仿 upload-coordinator 范式)。
2026-07-06 18:00:15 +08:00
82280e3d4f
feat(editor): CodeBlockNodeView 显示语言标签与运行按钮
...
自定义 NodeView 包裹 <pre><code>,顶部工具栏显示语言(extractLang 提取),
runnable 块额外显示运行按钮。contentDOM 指向 <code>,保证 CodeBlockLowlight
decoration(高亮)正常生效。运行走 editor.storage.__onRunCode 回调。
2026-07-06 17:59:01 +08:00
0adc6d8382
fix(editor): 暗色 type 色值修正 + extractLang 小写化
...
- .dark .hljs-type/.hljs-title.class_ 由 #8839ef(紫) 改为 #f9e2af(黄),
与 spec 表、官方 @catppuccin/highlightjs、读者侧 highlight.css 三处对齐
- extractLang 加 toLowerCase,与后端 parse_fence_info(languages.rs:85)对齐,
避免大写语言名触发 lowlight Unknown language 抛错
2026-07-06 16:13:08 +08:00
4de48cd16c
style(editor): biome 修正 import 排序(highlight/index)
...
TDD 阶段子代理未跑 biome check --write,补齐 import 字母序以过 make lint。
2026-07-06 15:53:16 +08:00
8493e4c4c4
feat(editor): 代码块语法高亮配色(Catppuccin Latte/Mocha)
...
色值取自 @catppuccin/highlightjs@1.0.1,与读者侧 highlight.css 同一 palette。
亮/暗双套,.dark 前缀切换,scope 到 .tiptap-editor 避免污染全站。
2026-07-06 15:50:35 +08:00
e86fe0767d
feat(editor): 用 CodeBlockLowlight 替换 StarterKit 自带 CodeBlock
...
禁用 StarterKit codeBlock,引入 CodeBlockLowlight + lowlight 实例。
代码块现在有实时语法高亮(ProseMirror decoration),language 属性
仍承载完整 info string(markdown 往返不变)。
2026-07-06 15:48:02 +08:00
e79e0d9ee2
feat(editor): 新增 lowlight 实例与 extractLang 语言提取
...
common 集(~35 种语言)注册到 lowlight;extractLang 从完整 info string
(如 'python runnable {...}')提取首 token,使 runnable 块按正确语言高亮。
2026-07-06 15:45:42 +08:00
0b4346ecd4
fix(editor): 语言下拉展开时 Enter 不误触发插入
...
<select> 展开时 Enter 用于确认选项,不应提交整个弹框。
HTMLSelectElement.open 浏览器存在但 TS lib.dom 未声明,用交叉类型断言。
2026-07-06 14:15:25 +08:00
9b4c0ea36e
style(editor): biome 格式化 runnable 模态框代码
...
biome check --write 修正 import 排序 + 格式化 openRunnableModal 链式调用
(TDD 阶段子代理未跑 make fix,此处补齐以过 make lint)。
2026-07-06 14:08:06 +08:00
01ceef2694
feat(editor): 可运行代码块模态框样式 + runnable 块绿色边框
...
模态框复用 Catppuccin Latte/Mocha 配色;编辑器内含 runnable 标记的
代码块加左侧绿色边框(与 Code Runner 运行按钮主色一致)。
2026-07-06 14:04:52 +08:00
d6fc98d253
feat(editor): 斜杠菜单新增「可运行代码块」条目
...
触发 openRunnableModal,配置后插入标准 CodeBlock(language 承载
'python runnable {...}' info string)。
2026-07-06 14:02:12 +08:00
5929ab68b4
feat(editor): 新增 openRunnableModal 模态框配置可运行代码块
...
原生 DOM 模态框(遮罩+卡片),语言 select + 超时/内存/网络 3 项 overrides
+ 实时预览。确认插入 setCodeBlock({language: 'python runnable {...}'}),
Esc/遮罩/取消关闭不插入。
2026-07-06 13:58:51 +08:00
84b3da9db6
feat(editor): 新增 buildRunnableInfo 构造 runnable fence info string
...
纯函数,把语言+overrides 配置转成 'python runnable {...}' 形态。
dirty=false 省略 JSON,dirty=true 写 timeout/memory/network 三项,
字段顺序固定。
2026-07-06 13:45:11 +08:00
90fc6977b0
feat(editor): Tiptap 编辑器配色迁移到 Catppuccin(Latte/Mocha)
...
编辑器 CSS 此前用硬编码 GitHub 风格色(#0366d6/#d73a49/#24292e 等),
独立于 paper-* 语义 token。逐元素重映射到 Catppuccin 调色板:
亮色 Latte:正文 text、标题 text、placeholder overlay0、
行内 code surface0/red、pre crust、blockquote surface1/subtext0、
hr surface1、链接 blue、selection surface0、表格 surface1/crust、
slash 弹层 mantle/surface0、checkbox/选中态 green(主强调)
暗色 Mocha:对应角色映射(text/cdd6f4 等)
上传遮罩的 rgba(255,255,255,*) 白字与 rgba(239,68,68,*) 红删除按钮
属中性覆盖层,保留不动。
2026-07-03 14:18:03 +08:00
a47e1e6027
refactor(build): workspace 根迁移至 libs/,Makefile 整合 lint/fix
...
将 pnpm workspace 根从项目根移到 libs/,项目根保持纯 Rust,JS 工作区自治:
- package.json/pnpm-workspace.yaml/pnpm-lock.yaml/biome.json 迁入 libs/
- pnpm-workspace.yaml glob 从 'libs/*' 改为 '*'(已在 libs/ 内)
- biome.json 关闭 vcs.useIgnoreFile(libs/ 无独立 .gitignore,
files.includes 已显式排除 node_modules/dist)
- Makefile 所有 pnpm/biome 调用加 'cd libs &&' 前缀
Makefile 同步整合 lint/fix(替代独立 clippy/fix target):
- make lint = biome check (libs) + cargo clippy
- make fix = biome format --write (libs) + cargo fix
- 删除 8 个 build-*-incremental/build-* target,改为 build-libs 聚合 +
build-<name> 用 pnpm --filter 单库便利 target
修复 biome 格式化回归:tiptap-editor/src/index.ts:83 的 sourceTextarea!.value
被 biome 误转为可选链 ?.,导致 TS2345(string | undefined 不匹配 string),
已还原为非空断言。
2026-07-02 18:02:52 +08:00
b1b1c88a74
chore(biome): 引入 Biome v2.5 monorepo 配置并格式化全量源码
...
新增根 biome.json(v2.5,v2 起原生支持 monorepo,子包自动向上查找):
- formatter: 2 空格缩进、单引号、行宽 100、LF、尾逗号、箭头函数括号
(匹配现有代码风格)
- linter: recommended preset + 项目化裁剪
- useTemplate/useTemplate off(geometry.ts 故意保留字符串拼接,
且测试用精确浮点断言锁定行为)
- noNonNullAssertion off(测试与 DOM 访问里的 ! 是惯用写法)
- noConsole off(4 个 lib 用 console.error/warn 做错误上报)
- CSS 关闭 noDescendingSpecificity/noDuplicateProperties
(手写 CSS 的 fallback 模式)
- useImportType/useNodejsImportProtocol/noUnusedVariables warn
- vcs.useIgnoreFile 自动读 .gitignore;排除 public/target/node_modules/
dist/.dioxus/static + Tailwind 入口 input.css(含 @theme 等 Biome 无法
解析的 at-rules)
首次格式化覆盖 29 个源文件:import 排序、node: 协议、引号/缩进统一,
并修复 index.ts 两处 forEach 回调隐式返回值(useIterableCallbackReturn)。
90 个 vitest 用例全绿,确认无语义改动。
2026-07-02 17:54:46 +08:00
79ceb307ad
chore(workspace): 建立 pnpm workspace 与共享 tsconfig
...
将 libs/ 下 4 个独立 pnpm 项目整合为 workspace:
- 新增根 package.json/pnpm-workspace.yaml,4 份重复的 devDeps
(happy-dom/typescript/vite/vitest,版本零分歧)上提到根,各 lib 只留运行时依赖
- 新增 libs/tsconfig.base.json,4 份逐字节相同的 tsconfig.json 改为 extends
- per-lib pnpm-lock.yaml 合并为根 lockfile;冗余的 libs/{lightbox,yggdrasil-core}/.gitignore 删除
(根 .gitignore 的 **/**/node_modules 已覆盖)
收益:依赖共享安装(node_modules 去重)、构建/测试可用 pnpm -r 统一调度,
单库仍可 pnpm --filter @yggdrasil/<name> 独立操作。构建与测试均验证通过(90 个 vitest 用例全绿)。
2026-07-02 17:50:52 +08:00
0772951867
fix(editor): 修复升级后空项 Enter 不退出列表(根因:畸形文档)
...
bug 表现:- [ ] 123 升级成任务列表后,空项按 Enter 不退出,而是继续新建空项。
根因有两处,都在 appendTransaction 构造新节点时:
1. stripPrefix 作用域错误:原代码 cut 作用在 listItem.content(把段落节点
当原子),cut(N) 会切进段落的标签边界,产生畸形文档——段落文本前缀残留
(如' 未完成'多出空格)、nodeSize 与实际不符。修正为作用在段落内部的
content 上(用 para.type.create 重建段落),并用 Fragment.replaceChild
替换 listItem 的第一个子节点。
2. 光标位置少算一层:+1 进 taskList 内容,+offset 到命中 taskItem,+1 进
taskItem 内容(段落节点位置),还需 +1 进段落内容(文本),原代码漏了
这最后一个 +1,导致光标落在文本中间而非末尾,splitListItem 在错误位置
分裂(把'123'拆成'12'+'3')。
畸形文档 + 光标错位 → splitListItem 在错误位置分裂,产生的'空'taskItem
实际不空,二次 Enter 走分裂路径而非退出路径。
新增回归测试:段落文本无残留 + 完整 Enter 链路(升级→Enter 空项→Enter 退出)。
2026-07-02 15:27:37 +08:00
53b53d3169
fix(editor): appendTransaction 加重入防护 + Enter 行为测试
...
用户反馈:升级成 taskList 后,空项按 Enter 未退出列表(与斜杠命令创建的
行为不一致)。
单测用 splitListItem 命令验证:升级后的 taskList,有内容项 Enter 分裂出空
taskItem,空项 Enter 正确退出列表(doc 末尾产生 paragraph)。行为与斜杠
命令创建的一致。说明 appendTransaction 产生的节点结构合法。
追加防护:给 appendTransaction 返回的 transaction 打 pluginKey 标记,
重入时跳过,避免 Enter 等操作的文档变化被本插件二次干扰。
happy-dom 无法可靠模拟真实 keydown 链路(派发 KeyboardEvent 时光标映射
不准),Enter 的 bug 需真实浏览器复现。
2026-07-02 14:59:53 +08:00
873ccb1a80
fix(editor): task list label 内 checkbox 居中
...
label 默认是 inline 元素,内部 checkbox(inline-block)受父级行高影响会产生
基线间距,导致 checkbox 在 label 盒子内偏上,即便 li 用了 align-items: center
也只对齐了 label 盒子而非 checkbox 本身。
给 label 加 display: flex + align-items: center + line-height: 0,消除行高
撑出的基线空间,让 checkbox 严格居于 label 盒子中心。
2026-07-02 14:42:52 +08:00
4e96243407
style(editor): 任务列表 checkbox 与文本垂直中线对齐
...
与前台 input.css 同步,编辑器内 task list 同样改为 align-items: center。
- li: flex-start → center,实现真正的中线对齐
- label: 去掉 margin-top: 0.3em(center 已处理)
- 新增 li > div > p { margin: 0 }:task list 项内段落清掉默认 .6em 0
上下 margin,否则 margin 撑高 div 导致 checkbox 与文本错位、项间距过大
2026-07-02 14:38:59 +08:00
250fe653c1
fix(editor): 修复任务列表升级后光标被甩到下一行
...
appendTransaction 用 replaceWith 整段替换 bulletList→taskList,ProseMirror 的
选区映射无法把原 listItem 内的光标正确映射到新 taskItem 内,默认落到替换
区域之后(下一行)。
显式用 TextSelection.near 把光标设到命中的 taskItem 段落文本末尾:
按命中项在 newItems 中的索引 + 段落文本长度算出目标 pos。
新增光标位置断言(selection.from === 3,即 doc>taskList>taskItem>paragraph 内)。
2026-07-02 14:25:57 +08:00
4a27236859
fix(editor): TaskInputRule 改用 appendTransaction(逐字符输入才生效)
...
input rule 方案对逐字符输入无效:BulletList 在用户打 - (第2字符)时就抢先
触发,把行变成 bulletList>listItem,之后 [ ] 在 listItem 内无法被任何
input rule 升级。提高 priority 也无济于事——BulletList 在更短文本上抢先。
改用 appendTransaction 监听文档变化:BulletList 触发后,用户在 listItem 里
打出 [ ] / [x] 前缀时,把该 bulletList 整体替换成 taskList,对应 listItem
转成带 checked 的 taskItem 并删除已识别的文本前缀。
- 测试模拟逐字符时序:先 - (applyInputRules 触发 BulletList),再 [ ]
(appendTransaction 升级),5 个用例覆盖未勾选/已勾选/大写/不误判/星号
- * + - 三种 marker 的 bulletList 都会升级(BulletList 对三者创建同种节点)
2026-07-02 14:16:34 +08:00
4a76397af8
feat(editor): 支持 - [ ] 手动输入创建任务列表
...
Tiptap 官方 TaskItem 的 input rule 只匹配 [ ] 不含 marker,且 StarterKit
的 BulletList input rule /^\s*([-+*])\s$/ 在用户打 - 时就抢先触发,把这行
变成无序列表;后续的 [ ] 沦为字面文本,getMarkdown 序列化时转义成 \[ \],
前台无法渲染成 checkbox。
- 新增 TaskInputRule 扩展,priority 1000 抢在 BulletList(默认 100)之前,
正则 /^(\s*-\s)\[( |x|X)\]\s$/ 匹配 - [ ] / - [x],
wrappingInputRule 包裹成 taskList > taskItem(findWrapping 自动补齐两层)
- 仅识别减号 marker(* + 仍走 BulletList),Enter 续行复用 TaskItem 内置
splitListItem,不重复实现
- 新增 task-input-rule 测试(真实 Editor + happy-dom):覆盖未勾选/已勾选/
大写 X/不误判普通列表/星号 marker
2026-07-02 13:56:11 +08:00
8ec4ecd310
chore: migrate JS subprojects from npm to pnpm
...
CI / check (push) Failing after 4m41s
CI / build (push) Has been skipped
Switch tiptap-editor, lightbox, and yggdrasil-core from npm to pnpm.
Replace package-lock.json with pnpm-lock.yaml and update Makefile accordingly.
2026-06-29 10:35:04 +08:00
fb31ecb5ae
fix(tiptap): expose EditorOptions as runtime class for wasm-bindgen
...
EditorOptions was a TS interface (erased at runtime), but the Rust
wasm-bindgen extern uses #[wasm_bindgen(constructor)] which generates
'new EditorOptions()' — requiring a global constructor.
Vite 5/Rollup accidentally leaked the name into IIFE global scope,
masking this. Vite 8/Rolldown strictly erases interfaces, exposing it
as 'EditorOptions is not defined'.
Fix: make EditorOptions a class (retains runtime constructor) and mount
it on window, since the IIFE bundle name is taken by TiptapEditor.
2026-06-24 10:27:07 +08:00
08c2539dfc
test(tiptap): add isValidUrl tests covering scheme validation edge cases
2026-06-24 10:10:39 +08:00
90cff2d0cc
test(tiptap): add UploadImageNodeView tests for rendering/update/callbacks/destroy
2026-06-24 10:09:36 +08:00
f7042d3ab4
test(tiptap): add UploadCoordinator tests for counts/lifecycle/emit/idempotency
2026-06-24 10:07:43 +08:00
8ff051689f
refactor(tiptap): export isValidUrl and UploadImageNodeView for testability
2026-06-24 10:04:59 +08:00
19532670d3
build(tiptap): add vitest config with happy-dom environment
2026-06-24 10:04:23 +08:00
21c0359f5e
build(tiptap): upgrade to vite 8/vitest 4/typescript 6/tiptap 3.27, migrate to rolldownOptions
2026-06-24 10:03:47 +08:00
939e0de171
build(tiptap): run tsc --noEmit before vite build
2026-06-23 18:45:17 +08:00
6ffcd55c94
fix(tiptap): correct link command order + validate URL scheme in slash commands
2026-06-23 18:42:57 +08:00
e580c3ab18
refactor(tiptap): narrow updateNodeAttrs type, extract findImageNodeByUploadId helper
2026-06-23 18:42:05 +08:00
d099db4140
refactor(tiptap): move coordinator to editor.storage, drop dead Markdown html option
2026-06-23 18:39:38 +08:00
6b44a9c036
fix(tiptap): clean up pending upload on NodeView.destroy to prevent blob leak
2026-06-23 18:36:47 +08:00
2e239908a4
fix(tiptap): revoke blob on node destroy + maintain upload counts internally
2026-06-23 18:36:07 +08:00
71df6abe5d
refactor(tiptap): drop redundant single-arg mergeAttributes call
2026-06-23 18:35:05 +08:00
18ae6a915e
refactor(tiptap): type coordinatorRef via Pick<UploadCoordinator> instead of duck typing
2026-06-23 18:34:37 +08:00
558191e922
refactor(tiptap): type CommandItem.editor as Editor, explicit SlashPopup return type
2026-06-23 18:33:59 +08:00
073f6936e3
build(tiptap): add tsconfig + typecheck script
2026-06-23 18:32:26 +08:00
a3896c24a5
refactor(write): replace all js_sys::eval with tiptap_bridge bindings
...
write.rs changes (Tasks 7-11):
- use_drop: EditorHandle::drop replaces eval destroy + global reset (#1 )
- init use_effect: closure-driven bridge.create replaces eval initEditor
script + 100ms ready polling (#2/#3/#4/#5)
- getMarkdown: read via EditorHandle instead of eval (#8 )
- delete upload polling use_future: driven by onUploadEvent closure (#6/#7)
- removeUploadByUploadId: call via EditorHandle instead of eval (#9 )
Bridge fixes during integration:
- UploadCountsJs: drop Copy derive (wasm_bindgen types can't be Copy)
- split module: shared types compile on both targets, wasm externs gated
- add WritableExt/Readable/Writable trait imports for .write()/.set()
- use FnMut closures (Signal write/set take &mut self)
- consume_upload_event takes &mut Signal params
Also: remove stale window.__tiptap_content in index.ts source-mode handler.
2026-06-23 11:25:06 +08:00
92cfda7d5a
refactor(tiptap): EditorOptions onReady/onUploadEvent callbacks replace polling
2026-06-23 10:52:30 +08:00
b09216ef29
refactor(tiptap): UploadCoordinator emit callback replaces window global
2026-06-23 10:52:20 +08:00
876cb55e91
style(editor): smooth blur/grayscale transition on placeholder image
...
给 img 加 transition: filter 0.2s ease,blur 出现和消失(上传中→成功/
失败)平滑过渡,而非瞬间切换。transition 写在基础 img 规则上,
确保 is-uploading class 移除时过渡也能触发。
2026-06-22 16:27:02 +08:00
6e04940edc
style(editor): blur uploading placeholder image, keep overlay text sharp
...
上传中占位符图片加高斯模糊,遮罩文字保持清晰。
- .is-uploading img 加 filter: blur(8px)
- container.is-uploading 加 overflow: hidden 防止 blur 边缘渗出
- overlay 文字(spinner/"上传中…")不受影响:overlay 是 img 的兄弟元素
而非后代,CSS filter 只作用于目标元素本身,不波及兄弟
2026-06-22 16:24:02 +08:00
cee4f3f3ef
fix(upload): address code review findings
...
final code review 后的修复:
1. blob 检查收紧(Important): md.contains("blob:") 误伤合法讨论 blob URL
的代码块/正文,改为检测 markdown 图片语法 ](blob: 才算占位符泄漏
2. 重试错误原地更新(Minor): seen_error_ids 去重逻辑导致重试后再失败时
顶部提示停留在旧错误文案;改为已存在 id 时原地更新 message
3. destroy 清理 coordinatorRef(Minor): destroy() 调 setUploadCoordinator(null),
避免 NodeView 指向已销毁的 coordinator(防御性)
4. 删除 NodeView 未用的 editor 字段(Nit): 死代码清理
验证: cargo test (384 passed) / clippy (本次零警告) / dx check / tiptap build 全过
2026-06-22 15:49:23 +08:00
8ffb4451b1
style(editor): add upload placeholder NodeView styles (overlay/spinner/error)
2026-06-22 15:32:32 +08:00
34c8b47436
feat(editor): route slash-command image upload through coordinator
...
- slash-command.ts: SlashCommandOptions 加 onInsertUploading option
- slash-command.ts: 上传图片命令优先走 onInsertUploading(coordinator 占位符+上传),
退回 uploadFn 直接上传(无占位符)
- index.ts: SlashCommand.configure 注入 onInsertUploading,
闭包延迟读取 this.coordinator(在 editor 创建后才实例化)
2026-06-22 15:30:39 +08:00