52 Commits

Author SHA1 Message Date
xfy
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
xfy
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
xfy
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
xfy
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
xfy
8ec4ecd310 chore: migrate JS subprojects from npm to pnpm
Some checks failed
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
xfy
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
xfy
08c2539dfc test(tiptap): add isValidUrl tests covering scheme validation edge cases 2026-06-24 10:10:39 +08:00
xfy
90cff2d0cc test(tiptap): add UploadImageNodeView tests for rendering/update/callbacks/destroy 2026-06-24 10:09:36 +08:00
xfy
f7042d3ab4 test(tiptap): add UploadCoordinator tests for counts/lifecycle/emit/idempotency 2026-06-24 10:07:43 +08:00
xfy
8ff051689f refactor(tiptap): export isValidUrl and UploadImageNodeView for testability 2026-06-24 10:04:59 +08:00
xfy
19532670d3 build(tiptap): add vitest config with happy-dom environment 2026-06-24 10:04:23 +08:00
xfy
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
xfy
939e0de171 build(tiptap): run tsc --noEmit before vite build 2026-06-23 18:45:17 +08:00
xfy
6ffcd55c94 fix(tiptap): correct link command order + validate URL scheme in slash commands 2026-06-23 18:42:57 +08:00
xfy
e580c3ab18 refactor(tiptap): narrow updateNodeAttrs type, extract findImageNodeByUploadId helper 2026-06-23 18:42:05 +08:00
xfy
d099db4140 refactor(tiptap): move coordinator to editor.storage, drop dead Markdown html option 2026-06-23 18:39:38 +08:00
xfy
6b44a9c036 fix(tiptap): clean up pending upload on NodeView.destroy to prevent blob leak 2026-06-23 18:36:47 +08:00
xfy
2e239908a4 fix(tiptap): revoke blob on node destroy + maintain upload counts internally 2026-06-23 18:36:07 +08:00
xfy
71df6abe5d refactor(tiptap): drop redundant single-arg mergeAttributes call 2026-06-23 18:35:05 +08:00
xfy
18ae6a915e refactor(tiptap): type coordinatorRef via Pick<UploadCoordinator> instead of duck typing 2026-06-23 18:34:37 +08:00
xfy
558191e922 refactor(tiptap): type CommandItem.editor as Editor, explicit SlashPopup return type 2026-06-23 18:33:59 +08:00
xfy
073f6936e3 build(tiptap): add tsconfig + typecheck script 2026-06-23 18:32:26 +08:00
xfy
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
xfy
92cfda7d5a refactor(tiptap): EditorOptions onReady/onUploadEvent callbacks replace polling 2026-06-23 10:52:30 +08:00
xfy
b09216ef29 refactor(tiptap): UploadCoordinator emit callback replaces window global 2026-06-23 10:52:20 +08:00
xfy
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
xfy
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
xfy
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
xfy
8ffb4451b1 style(editor): add upload placeholder NodeView styles (overlay/spinner/error) 2026-06-22 15:32:32 +08:00
xfy
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
xfy
5d527778a5 feat(editor): wire UploadCoordinator and custom Image in TiptapEditorInstance
- index.ts: 替换 Image.configure 为 UploadImage(自定义扩展+NodeView)
- index.ts: editor 创建后实例化 UploadCoordinator 并注入 NodeView
- index.ts: FileHandler onPaste/onDrop 改走 coordinator.insertUploading
- index.ts: 暴露 removeUploadByUploadId 供 Rust ×关闭调用
- index.ts: destroy 清理 coordinator
- upload-image.ts: 修正 import 来源(Image 来自 extension-image, mergeAttributes 来自 core)
2026-06-22 15:28:40 +08:00
xfy
57bea05aa3 feat(editor): add custom Image extension with upload-state NodeView 2026-06-22 15:25:25 +08:00
xfy
8ce9c58997 feat(editor): maintain window.__tiptap_uploads for Rust polling 2026-06-22 15:24:37 +08:00
xfy
6506931c1d feat(editor): add runUpload and retryUpload to UploadCoordinator 2026-06-22 15:23:57 +08:00
xfy
e06667f792 feat(editor): add UploadCoordinator skeleton with pending map and node removal 2026-06-22 15:22:42 +08:00
xfy
6ff37322b2 feat(editor): add image upload to slash command and build tiptap in make dev
斜杠命令图片上传 + Makefile dev 构建修复

1. 斜杠命令接入图片上传管线
   - slash-command.ts: SlashCommand 扩展改为 Extension.create<Options>,
     新增 onImageUpload 选项;原单一"图片"命令拆成两个:
     · "上传图片"(📤): 仅在 onImageUpload 可用时出现,触发原生文件选择器
       (accept 限定 jpeg/png/gif/webp,与 upload 端点 MIME 一致),
       上传成功后 setImage 插入,失败 console.error(与 FileHandler 一致)
     · "图片链接"(🖼): 保留原 window.prompt URL 方式
   - index.ts: SlashCommand.configure({ onImageUpload }) 透传宿主回调,
     复用 write.rs 已注入的 /api/upload 管线,无需 Rust 端改动

2. Makefile: make dev 不再忽略 tiptap 构建
   - 根因: 原 dev target 只起 tailwindcss watch + dx serve,从不构建
     tiptap 编辑器,导致 dev server 始终 serve 上次 make build 残留的
     旧 editor.js(本次新增的"上传图片"命令在 dev 下永远不生效)
   - 新增 build-editor-incremental target (跳过 npm ci,直接 vite build,~1s)
   - dev 现在依赖 build-editor-incremental,启动前先增量构建编辑器

3. Makefile: clean 不再删 public/tiptap
   - public/tiptap 是跨子项目(npm)构建产物,删除后必须重跑 make build-editor
     (含 npm ci) 才能恢复;原 clean 会删它是陷阱(make clean && make dev
     会导致编辑器直接消失)。现在 clean 只清 Rust 产物和缓存
2026-06-22 14:18:05 +08:00
xfy
6ce8915e6c fix(editor): 修复 focus() 把源码视图滚动到底部
切换到源码模式时 sourceTextarea.focus() 会触发浏览器将光标
所在行滚动进可视区域,光标默认在内容末尾,导致 textarea 被
拉到底部覆盖已设的滚动位置。改为 focus 后立即恢复 scrollTop。

同时移除调试日志。
2026-06-16 16:13:46 +08:00
xfy
ae6d80c9cc fix(editor): 修复滚动比例同步失效导致切换到底部
原 syncScrollRatio 封装在 display:'none' 之后重复读取源容器
scrollTop,此时浏览器已将其归零,导致比例计算为 0。改为提前
读取比例并直接应用,移除二次封装与调试日志。
2026-06-16 15:58:50 +08:00
xfy
914f168551 fix(editor): 切换视图时按滚动比例同步位置
修复切回富文本时编辑器滚动到底部的问题:移除 commands.focus()
(会强制滚动到光标位置),改用滚动比例跨模式同步,富文本与源码
视图按相同比例定位,保持视觉位置一致。
2026-06-16 15:53:28 +08:00
xfy
480aa92bb4 feat(editor): 新增 Markdown 源码视图切换
在 Tiptap 编辑器右上角增加 </> 悬浮按钮,支持在富文本与
Markdown 源码视图间双向切换,内容自动同步。getMarkdown() 在
源码模式下返回 textarea 内容,使 Rust 端提交逻辑无需改动。
2026-06-16 15:50:09 +08:00
xfy
012704d0a5 chore(tiptap-editor): update package-lock.json 2026-06-08 15:54:27 +08:00
xfy
72ba8ea9ec chore(tiptap-editor): update package-lock.json 2026-06-06 00:29:53 +08:00
xfy
078e9b1da1 fix(tiptap-editor): configure link via StarterKit to avoid duplicate extension warning 2026-06-05 18:19:24 +08:00
xfy
ad208a4603 fix(tiptap-editor): correct setContent call signature for markdown parsing 2026-06-05 18:05:47 +08:00
xfy
26eb62c2ae fix(upload): disable default body limit and fix error object serialization 2026-06-05 16:52:52 +08:00
xfy
c2c7b46958 fix(tiptap-editor): use insertContentAt with drop position in onDrop handler
Previously dropped images were inserted at the current cursor position
instead of the actual drop position. Now uses insertContentAt(pos, ...)
to place images at the correct location.
2026-06-05 15:21:38 +08:00
xfy
00c5a880f4 feat(editor): add FileHandler extension for image paste/drop upload 2026-06-05 15:19:31 +08:00
xfy
3d7dd5b5a5 deps(editor): add @tiptap/extension-file-handler 2026-06-05 15:17:39 +08:00
xfy
a8704e199f feat(editor): add slash command, table, task list, image and link extensions
- Add @tiptap/suggestion-based slash command menu with 12 commands
- Add TableKit for table support (3x3 with header)
- Add Image extension with base64 support
- Add Link extension with autolink and paste detection
- Add TaskList/TaskItem extensions for checklists
- Upgrade all tiptap packages from 3.23.6 to 3.25.0
- Add CSS styles for all new features including dark theme
2026-06-05 10:20:47 +08:00
xfy
cd45ab17de build: simplify tiptap build by setting fixed output filename in vite config 2026-06-04 16:26:25 +08:00