oh-my-muse/docs/memorys/2026-05-24-muse-studio写作台与AI协作实现.md
zizi 317dfd4d13 feat(muse-studio): 搭建 P2 用户端工作台
完成 Muse Studio React SPA 基座和六个用户端功能域,接入 MSW Mock、Tiptap 写作台、知识库资料管理、智能体槽位预检绑定、市场授权安装和个人中心用量权益视图。
2026-05-25 10:15:28 +08:00

3.0 KiB
Raw Blame History

muse-studio 写作台与 AI 协作实现留痕

任务背景

muse-studio 前端子项目集成 Tiptap 富文本编辑器,实现本地 IndexedDB 影子层与远程 API 的双向防抖同步,接入 SSE AI 流式生成文本,并实现字符级高亮差异比对及采纳替换的闭环流程。

核心设计与踩坑总结

1. 分级 Diff 字符比对设计 (Coarse-to-Fine)

  • 挑战:经典的 DP动态规划LCS 算法时间复杂度为 $O(N \times M)$。在长篇小说场景下,对 3000 字以上的内容进行字符级 LCS 对比,计算矩阵元素高达近千万,直接导致浏览器 UI 线程卡死ANR
  • 优化实现
    1. 段落级粗筛:以段落(\n)为元素单元,首先运行一遍粗粒度的段落 LCS。
    2. 局部字符 LCS 细筛:如果段落被判定为 modified(即相邻的一段被删除、一段被新增,视为修改),且单段字数在安全限制(乘积小于 1,000,000才对其进行细粒度的字符级 LCS 比对。
    3. 非空安全断言:在 TS 极严模式(noUncheckedIndexedAccess: true)下,对二维矩阵 dp[i] 级别的读取,使用局部变量承接和非空断言(!),消除对象可能为 undefined 的编译问题。
  • 效果:长文本 Diff 运算时长由秒级缩短至 4 毫秒,彻底消除了卡顿风险。

2. 编辑器双防抖与临别落库机制

  • ** IndexedDB IO 去抖 (500ms)**:打字时内容先进入本地 IndexedDB 去抖保存,避免高频按键对磁盘读写的密集事务挤占,优化 INP 打字卡顿。
  • API 远程去抖 (2000ms):延迟 2 秒调用 PUT 接口,同步成功后销毁本地影子草稿以释放磁盘。
  • 临别落库 (Flush):为防范打字结束 2 秒内用户突然离开页面导致最新修改丢失,扩展 debounce 模块提供 .cancel().flush() 能力。在编辑器 useEffect 卸载 cleanup 中:
    useEffect(() => {
      return () => {
        debouncedAPISave.flush();  // 立即将挂起的定时任务提交 API 执行
        debouncedAPISave.cancel(); // 释放定时器引用
      };
    }, [blockId]);
    

3. Zustand Selector 精准状态更新

  • 挑战多栏式写作台包含章节大纲、编辑器、AI 面板。如果解构式订阅 Zustand Store打字时高频的 isDirty 变化会触发整棵组件树重渲染,造成严重输入卡顿。
  • 避坑:组件必须使用精细 Selector 单独订阅特定状态,严禁解构大对象,阻断不必要的 React Re-render
    const markDirty = useEditorStore((state) => state.markDirty);
    

4. 远程 API 同步失败异常捕获 (sync_failed)

  • 实现:如果 PUT 同步接口失败,通过 catch 捕获异常,立刻将 IndexedDB 对应小节的本地缓存状态覆写为 status: 'sync_failed'
  • 恢复:编辑器组件初始化挂载时,查询该小节是否存在 sync_failed 状态记录,若存在则提示恢复横幅“恢复草稿 / 忽略并覆写”,闭环了离线保存安全网。