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
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
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
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
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
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
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
57bea05aa3
feat(editor): add custom Image extension with upload-state NodeView
2026-06-22 15:25:25 +08:00
8ce9c58997
feat(editor): maintain window.__tiptap_uploads for Rust polling
2026-06-22 15:24:37 +08:00
6506931c1d
feat(editor): add runUpload and retryUpload to UploadCoordinator
2026-06-22 15:23:57 +08:00
e06667f792
feat(editor): add UploadCoordinator skeleton with pending map and node removal
2026-06-22 15:22:42 +08:00
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
6ce8915e6c
fix(editor): 修复 focus() 把源码视图滚动到底部
...
切换到源码模式时 sourceTextarea.focus() 会触发浏览器将光标
所在行滚动进可视区域,光标默认在内容末尾,导致 textarea 被
拉到底部覆盖已设的滚动位置。改为 focus 后立即恢复 scrollTop。
同时移除调试日志。
2026-06-16 16:13:46 +08:00
ae6d80c9cc
fix(editor): 修复滚动比例同步失效导致切换到底部
...
原 syncScrollRatio 封装在 display:'none' 之后重复读取源容器
scrollTop,此时浏览器已将其归零,导致比例计算为 0。改为提前
读取比例并直接应用,移除二次封装与调试日志。
2026-06-16 15:58:50 +08:00
914f168551
fix(editor): 切换视图时按滚动比例同步位置
...
修复切回富文本时编辑器滚动到底部的问题:移除 commands.focus()
(会强制滚动到光标位置),改用滚动比例跨模式同步,富文本与源码
视图按相同比例定位,保持视觉位置一致。
2026-06-16 15:53:28 +08:00
480aa92bb4
feat(editor): 新增 Markdown 源码视图切换
...
在 Tiptap 编辑器右上角增加 </> 悬浮按钮,支持在富文本与
Markdown 源码视图间双向切换,内容自动同步。getMarkdown() 在
源码模式下返回 textarea 内容,使 Rust 端提交逻辑无需改动。
2026-06-16 15:50:09 +08:00
078e9b1da1
fix(tiptap-editor): configure link via StarterKit to avoid duplicate extension warning
2026-06-05 18:19:24 +08:00
ad208a4603
fix(tiptap-editor): correct setContent call signature for markdown parsing
2026-06-05 18:05:47 +08:00
26eb62c2ae
fix(upload): disable default body limit and fix error object serialization
2026-06-05 16:52:52 +08:00
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
00c5a880f4
feat(editor): add FileHandler extension for image paste/drop upload
2026-06-05 15:19:31 +08:00
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
956b2d7374
修复编辑器暗色主题和列表样式
...
- CSS 暗色主题选择器从 [data-theme="dark"] 改为 .dark
- 恢复无序列表圆点(覆盖 Tailwind preflight 重置)
- placeholder 改用 :has() 选择器适配 Tiptap v3
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 18:02:52 +08:00
f6d60520eb
集成 Tiptap Markdown 编辑器到文章撰写页面
...
- 新增 libs/tiptap-editor/ 打包子项目(Tiptap Core + StarterKit + Markdown)
- 构建产物输出到 public/tiptap/
- 替换原有的 textarea + pulldown_cmark 预览为 WYSIWYG 编辑器
- Makefile 新增 build-editor target
- Dioxus.toml 引入 editor.js 和 editor.css
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:46:04 +08:00