dsh-codinput 0.3.8 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/NOTICE +37 -4
  3. package/README.en.md +28 -13
  4. package/README.md +28 -13
  5. package/TESTING.md +96 -9
  6. package/assets/donate-qr.jpg +0 -0
  7. package/lib/client.js +2875 -98
  8. package/package.json +5 -2
  9. package/src/client/cm.ts +19 -1
  10. package/src/client/command.ts +39 -8
  11. package/src/client/components/CodinputSurface.tsx +80 -27
  12. package/src/client/components/EditorPane.tsx +3 -1
  13. package/src/client/components/InputSlotSeat.tsx +56 -1
  14. package/src/client/components/MenuSurface.tsx +64 -12
  15. package/src/client/components/ModelMenu.tsx +5 -17
  16. package/src/client/components/PermissionMenu.tsx +8 -3
  17. package/src/client/components/SlashMenu.tsx +294 -41
  18. package/src/client/components/StatsRow.tsx +40 -23
  19. package/src/client/components/Toolbar.tsx +284 -48
  20. package/src/client/components/UserMessageMarkdown.tsx +158 -115
  21. package/src/client/components/VoiceActivity.tsx +30 -69
  22. package/src/client/components/slotEntry.tsx +37 -0
  23. package/src/client/host-types.ts +11 -3
  24. package/src/client/hotkey.ts +38 -0
  25. package/src/client/i18n.ts +18 -4
  26. package/src/client/index.tsx +9 -0
  27. package/src/client/markdown/code.ts +281 -0
  28. package/src/client/markdown/extensions.ts +576 -0
  29. package/src/client/markdown/heavy-assets.d.ts +11 -0
  30. package/src/client/markdown/heavy-shared.ts +50 -0
  31. package/src/client/markdown/heavy.ts +159 -0
  32. package/src/client/markdown/index.ts +289 -0
  33. package/src/client/markdown/math.ts +134 -0
  34. package/src/client/markdown/mermaid.ts +358 -0
  35. package/src/client/markdown/sanitize.ts +103 -0
  36. package/src/client/markdown/styles.ts +171 -0
  37. package/src/client/prefs.ts +206 -23
  38. package/src/client/settings.tsx +15 -3
  39. package/src/client/stats.ts +92 -20
  40. package/src/client/styles.ts +55 -30
package/CHANGELOG.md CHANGED
@@ -2,6 +2,77 @@
2
2
 
3
3
  本项目遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/) 与 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.4.0] — 2026-11-10
6
+
7
+ 本版本适配 DSH `0.2.2-rc.1` 桌面/网页宿主,带来 Markdown 渲染升级(Obsidian 风格,全内置离线)与编辑器字面字符渲染(移除字体连字)。
8
+
9
+ ### 修复
10
+
11
+ - **`/codinput` 退出丢草稿(用户报告)**:宿主的命令通道在调用插件 `ui.run()` 之前已把命令 token 从草稿消费掉,旧实现随后 `setTimeout(80ms)` **无条件 `setDraft('')`**,把 token 之外的用户草稿整体清空。现改为**同步** `stripCommandToken()`:只在草稿确实以 `/codinput` 开头(token 后跟空白或行尾)时剥掉 token,其余内容原样保留;普通 / 悬浮 / 侧栏三形态与三条退出入口均不再丢草稿。
12
+ - **编辑器「丢字」实为字体连字(用户报告)**:JetBrains Mono 的连字把 `->` 画成 `→`、把 `//` 画成合并字形,用户视角是「编辑侧的 `-` / 首个 `/` 消失」,与消息记录(纯文本)不一致;隔离宿主逐键码点取证确认**文档 / 机器草稿 / 持久化草稿里的字符一个不少**,纯属渲染层。现默认关闭编辑器字体连字:`.cm-scroller` / `.cm-content` 输出 `font-variant-ligatures: none` 与 `font-feature-settings: "liga" 0, "calt" 0`;本版同时移除「字体连字」设置(理由见下)。
13
+ - **桌面端 calt shaping bug 结论**:DSH 桌面 `0.2.2-rc.1`(Electron 44.7 / Chromium 152)在「`calt`/`liga` 连字 + 后续任意字符」时会把连字第一格画空,**只在 CodeMirror 编辑器面**出现;普通 DOM(预览/用户消息/代码块)正常,web/Edge 157 正常。实测修补字体(calt 搬 liga)、12 种 CSS/合成层 workaround 均无法规避 → **编辑器保持字面是桌面端唯一稳定解**;Markdown 渲染面可以使用连字(见下)。
14
+ - **麦克风按钮消失(0.2.2 回归)**:0.2.2 的 voice-input 条目 `inject()` 新增 `hooks.microphoneDevice` / `hooks.pluginNavigation`,`VoiceInput` 需要 `useSpeechReadiness` / `useMicrophoneDevice` / `usePluginNavigation`;旧 `VoiceActivity` 只绑定 `speechReadiness`、丢弃其余 hooks → 组件内 `usePluginNavigation is not a function` 抛错被条目边界静默,麦克风整体不渲染。改为 `slotEntry.entryInjectProps` 通用折叠:`hooks` 表任意键 → `use<Name>` 选择器钩子、其余字段原样。
15
+ - **带图用户消息渲染错误(0.2.2 回归)**:0.2.2 移除了座位 prop `renderMessageImages`,用户消息附件行改由 `conversation.chat.user.content` factory 渲染、图片走 `conversation.chat.user.images` 槽位(官方 `MessageImages` 缩略图 + 原图灯箱)。旧实现拿不到新 prop → 回退文件卡 `image.png PNG 53KB` 且点击无法放大。现在附件行整体委托官方 factory(`content.text=""` → 官方只渲染附件行),图片/灯箱完全走官方;旧宿主回退链保留(`renderMessageImages` → 文件卡)。
16
+ - **Shift+Enter 换行失效(B1)**:旧版偏好迁移把 `lineBreakHotkey` 写成 `shift-enter`(规范分隔符是 `+`)→ 永远不匹配。迁移值改为 `shift+enter` 并修复已存坏值;另修行为层:自定义换行快捷键显式插入 `\n` 后选区未随之前移,光标留在插入点之前导致后续输入落回上一行 → `EditorPane` dispatch 现带 `selection: { anchor: head + 1 }`。
17
+ - **快捷键被旧迁移反复覆盖(B2)**:旧字段 `sendKey` / `lineBreakKey` / `floating` 一直留在存储里,每次加载都重新迁移、覆盖用户改过的快捷键。改为一次性迁移:迁移后删除旧字段、只写已知字段;用户已改过的新值优先。
18
+ - **偏好设置无类型校验(B3)**:存储写坏(坏 JSON、错误类型、数组、未知键、越界数值)会抛错或污染界面。新增 `normalizePrefs`:每字段独立类型/枚举/范围校验并回退默认,任意输入不抛错;保存前同样归一化。偏好键 `dsh-codinput.prefs.v2` → `.v3` 一次性迁移(旧字段清理、坏值修复)。
19
+ - **统计与缓存命中边界文本(B4)**:`formatCacheHitPercent` 完整复刻官方 0.2.2 算法(`99.9 / 99.99 / 99.999…`);`formatTokens` K 位舍入到 1000 时进位 M(`999_950 → 1M tok`,永不 1000K);tps/duration 补 NaN/±Infinity 防护;数据行胶囊、详情面板、上下文小圈全部走安全函数。
20
+ - **用户消息可伪造会话 chip(B5,安全)**:marked 默认透传原始 HTML、DOMPurify 白名单允许 `span` + `data-dci-wire` → 手写 `<span data-dci-wire="x">` 会被装饰成会话 chip。现在原始 HTML 一律转义为文本,`data-dci-wire` 只能由插件自身的 Markdown 扩展产生;伪造 span/script/onerror/style/iframe/javascript: 均不执行、不产生 chip。
21
+ - **「问答被渲染成用户侧消息」**:确认是宿主 0.2.2 官方行为——会把 `ask_user_question` 的回答渲染成官方 **question-reply 回执卡片**(DOM 带 `data-question-reply`),不是本插件气泡;插件另加防御:遇到 `source.kind === 'user-question-reply'` 的节点让位官方渲染。
22
+ - **Markdown 渲染缺陷(验收发现并复验)**:callout `question` 家族误用背景语义 token 作前景 accent(浅/深对比度仅 1.15/1.47)→ 8 家族统一改为前景派生,最低 4.93(浅)/7.87(深);用户消息图片 `width` 属性被 legacy `.dci-umsg-md img { width:auto }` 覆盖 → 删除该规则,Obsidian 尺寸语法两侧一致;预览「后开」时 `activateMarkdown` effect 缺 `previewOn` 依赖 → KaTeX/Mermaid/复制按钮未激活,补齐依赖;用户消息 h1–h6/th/td 因 legacy `font:` 简写未跟随等宽字体 → mono 态显式补 family/连字。
23
+ - **构建 P0**:`scripts/build.mjs` banner 拼接双重转义(`.join('\n')`)产出字面 `\n`,整包 JS 语法无效(宿主 `import failed`)→ 修复为 `.join('\n')` 并新增构建末尾 `node --check` 与单文件断言,坏产物直接阻断构建。
24
+
25
+ ### 变更
26
+
27
+ - **底部工具行逐值对齐官方 `0.2.2-rc.1` InputBar**:`+` 指令钮 28×28、`conversation.input.permission` / `.plan` 槽位镜像(官方权限选择器 / 计划 chip)、`input.left` / `.right` / `.model` 座位顺序、语音 activity 座位(麦克风钮)、sending 阶段独立停止钮与旋转发送态、官方 stop / sending / steer·queue 文案、行 `container-type: inline-size` / padding `2px 8px 6px` / gap 12 / `@container` 窄宽 gap 8、模型文字 compact 收起;圆形按钮补 `corner-shape: round`(宿主默认 squircle)。
28
+ - **编辑器字面渲染**:`->`、`//` 等始终按字面字符显示,与预览 / 用户消息的纯文本一致;「字体连字」设置项移除(prefs 遗留字段忽略并丢弃)。
29
+ - **用户消息 Markdown 统一渲染管线**:预览 `.dci-preview` 与用户消息 `.dci-umsg-md` 共用同一 `renderMarkdown`,输出一致;会话 chip 装饰、附件 factory 委托、question-reply 让位、官方气泡外层均保留。
30
+ - **候选菜单四向自适应浮层**:悬浮 / 右侧栏形态的 `/` `@` 候选菜单从「头部行下方固定弹出、覆盖编辑区」改为 `createPortal(document.body)` 的 `position: fixed` 浮层——以 `.dci-card` 实时矩形为不可覆盖区,在上/下/左/右按可用空间自适应选位,打开期间逐帧复算;普通形态保持卡片上方浮层。
31
+ - 预览面板 `overflow-y: auto` → `overflow: auto`(宽表格/代码块可横向滚动)。
32
+ - 设置项调整:移除「字体连字」;新增「Markdown 等宽连字」(默认开)。
33
+ - 数据行详情面板 portal 化:三枚面板(会话统计 / Token 用量 / 上下文分段)`createPortal(document.body)` + `position: fixed`,跟随滚动/尺寸变化、Escape 与点外关闭、三枚互斥。
34
+
35
+ ### 新增
36
+
37
+ - **Markdown 渲染(Obsidian 风格,全内置离线)**:GFM 表格(逐列对齐、宽表横滚)/ 任务清单(只读勾选、嵌套)/ 删除线 / 自动链接;**脚注**(上标 + 文末脚注区 + 回跳);**8 类 Obsidian Callouts**(note/tip/warning/danger/info/success/question/quote,图标/配色/折叠,浅深主题对比度达标);Obsidian 扩展语法 `==高亮==`、`%%注释%%`(隐藏、不误伤代码)、`[[wikilink|别名]]`(chip,点击复制目标)、`![[file]]`(样式化降级)、图片尺寸 `![alt|300](url)`、图片点击灯箱。
38
+ - **代码块**:highlight.js 46 种语言高亮 + 语言标签 + 复制按钮(未知语言不高亮不崩)。
39
+ - **数学公式**:KaTeX 行内 `$...$` / 块级 `$$...$$`;错误公式回退原文;20 个 woff2 字体全部内联 data URI,运行时**零网络请求**。
40
+ - **Mermaid**:13 类图表(flowchart / sequence / class / state / ER / gantt / pie / journey / gitGraph / info / mindmap / timeline),可见时懒渲染、浅深主题跟随、`securityLevel:'strict'`、失败回退源码;ELK/cose 与 cytoscape 构建期排除。
41
+ - **Markdown 正文等宽连字(默认开、设置可关)**:预览与用户消息正文改用 JetBrains Mono,`->`/`=>`/`//` 等靠**字体连字**显示箭头/连字(DOM、草稿、复制、发送内容仍是原文);代码块/行内代码使用各自代码字体并同样显示连字;用户消息 h1–h6/th/td 同步等宽。
42
+ - 构建:`scripts/build.mjs` 新增 `katex-css-inline` / `mermaid-trim` 两个构建期 plugin(带断言防依赖升级静默失效)+ 产物 `node --check` / 单文件自检。
43
+ - 新依赖:`highlight.js ^11.12.0`(BSD-3)、`katex ^0.18.0`(MIT)、`mermaid ^12.3.0`(MIT);`NOTICE` 补三方许可与传递依赖(ELK/cytoscape 未分发)。
44
+
45
+ ### 说明
46
+
47
+ - 本版同时包含编辑器字面渲染、Markdown 渲染与正文/行内代码连字等能力(详见上文章节)。
48
+ - **体积**:`lib/client.js` ≈ 3.55 MB(gzip ≈ 1.10 MiB / brotli ≈ 0.94 MiB);相对 0.3.9 基线(≈ 540 KB)增加约 2.84 MiB raw——全部 Markdown 渲染依赖(KaTeX 字体、highlight.js、Mermaid)内联打包,换来**离线可用、零网络请求**;构建约 1–7 s,单文件、连续构建字节一致。
49
+ - **安全**:原始 HTML 一律转义;外链 `target=_blank rel=noopener noreferrer`、`javascript:`/`data:` 协议拦截;KaTeX `trust:false`;Mermaid `securityLevel:'strict'` + SVG 过 DOMPurify;代码高亮只输出 class/文本。
50
+ - **已知限制**:Mermaid 仅内置 13 类常用图(其余回退源码);`[[wikilink]]` 仅复制目标名、不做仓库导航;`![[图片]]` 只做文本降级;**编辑器不做字体连字**(桌面 Chromium 152 无解,等宿主升级);系统未安装 JetBrains Mono 时正文等宽回退到宿主 code font(无连字则箭头退化为字面)。
51
+ - **「Markdown 等宽连字」开关语义**:开启时预览与用户消息的正文/标题/表格/行内代码均用 JBM 显示连字;关闭后**预览行内代码**回到阅读字体字面、正文恢复阅读字体;**用户消息行内代码**回到宿主代码字体(该字体默认连字,视觉上仍可能显示箭头,开关不改变它)。
52
+ - **验收记录**(隔离 web DSH `0.2.2-rc.1` + 无头 Edge;隔离桌面 Electron 44.7 / Chromium 152 抽验):底部工具行/麦克风、带图用户消息与问答、prefs 迁移与统计边界、Markdown 渲染(GFM/脚注/Callouts/Obsidian 语法/图片尺寸与灯箱/代码高亮/KaTeX/Mermaid/正文与行内代码连字)、安全与回归全部 PASS;桌面抽验(编辑器字面渲染、预览正文连字、无第一格空白)PASS;`__dshCodinputErrs` / console error 全为 0。
53
+ - **发布材料**:`npm run typecheck` 通过;`lib/client.js` 由当前源码重建(md5 `a7708d2d00ce005de5e599cc7819296f`,3,555,033 B;gzip 1,157,872 B / brotli 958,709 B;构建自检 `node --check` OK)。
54
+
55
+ [0.4.0]: https://github.com/Witherwithwinter/DSH-Codinput/compare/v0.3.9...v0.4.0
56
+
57
+ ## [0.3.9] — 2026-11-06
58
+
59
+ 修复 dsh `0.2.2-rc.1` 上模型 / 权限 / 设置下拉的整体错位、布局变化不跟随与开合失效:菜单改为 portal 到 `document.body`,锚点改按元素实时读取。
60
+
61
+ ### 修复
62
+
63
+ - **菜单包含块被劫持,整体错位**(`MenuSurface`):`.dci-menu` 虽为 `position: fixed`,但祖先链上的 `transform` / `filter` / `backdrop-filter` / `contain` 会把包含块从视口换成该祖先,`left/top` 按错误坐标系解释(实测可偏出 400px+)。现改为 `createPortal(document.body)`,与官方 ModelSelect 菜单、数据行详情面板同路数。
64
+ - **锚点是矩形快照,布局变化不跟随**(`MenuSurface`):原实现保存 `getBoundingClientRect()` 快照,`scroll` / `resize` 重算读同一份快照、结果恒等被丢弃。现改为按元素实时读取;打开期间逐帧比对锚点位置(1px 阈值)兜住不产生 scroll/resize 的布局重排;锚点未布局或菜单零尺寸时不落位。
65
+ - **点触发器开合失效**(`MenuSurface` / `ModelMenu`):全局 `pointerdown` 捕获监听把落在触发器上的点击也当作外部点击先关闭,触发器 `onClick` 随后读到 `open=false` 又开回来(点一次收不起来)。现外部点击判定跳过锚点、开合交给触发器 toggle;`ModelMenu` 移除自管点外关闭。模型 / 权限 / 设置三处调用方同步改为传元素 + 打开瞬间矩形。
66
+
67
+ ### 说明
68
+
69
+ - 发布材料:`npm run typecheck` 通过;两次发布构建字节一致(`lib/client.js` sha256 `4ef5a5917614b811b7345adbc5166e649014718dbb63929ee3654e8b2656460c`,与入库产物一致)。
70
+ - 兼容验证:隔离 web 宿主 `@deepseek-ai/dsh@0.2.2-rc.1`(临时 prefix + 独立 `DSH_HOME` + 无头 Edge 自动化探针,`link:` 安装本仓库构建产物)33/33 断言通过——菜单为 `body` 直接子节点 / `position: fixed`;模型菜单 top = 触发器 top − 8 − 菜单高(逐像素 0 误差、右对齐);祖先 `transform: translateY(150px)` 下仍 0 误差;布局位移 120px 后 2 帧内回贴;连点两次 开→收;点外部关闭、点菜单内不关;设置下拉 top = 触发器 bottom + 4(0 误差)且不被设置容器裁剪;窗口 1440×900→1100×760 后重算跟随。全程 console error / pageerror 为 0、`__dshCodinputErrs` 为空。
71
+ - 未覆盖:模型子页长列表滚动(隔离宿主无凭据、目录为空)、视口 12px 夹取分支、真实模型/权限切换提交。
72
+ - 0.3.8 既有功能与行为不变;本版仅调整菜单定位与开合实现(`lib/client.js` 同步重建)。PR #5(@LittleFishStars)。
73
+
74
+ [0.3.9]: https://github.com/Witherwithwinter/DSH-Codinput/compare/v0.3.8...v0.3.9
75
+
5
76
  ## [0.3.8] — 2026-10-30
6
77
 
7
78
  数据行三枚详情面板改为官方同构的 portal + fixed 锚定定位(滚动/尺寸变化跟随、Escape 与点外关闭、三面板单一展开);设置页开关样式与面板背景/圆角 token 对齐官方;修复 patchReload live 热更新后新增样式类不注入;修复 0.3.7 带图用户消息渲染崩溃(P0)并收紧用户消息 Markdown 的安全基线(P1)。
package/NOTICE CHANGED
@@ -33,7 +33,7 @@ alongside this repository's LICENSE file.
33
33
  Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/)
34
34
  Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
35
35
 
36
- - DOMPurify 3.4.15
36
+ - DOMPurify 3.4.17
37
37
  (MPL-2.0 OR Apache-2.0) 双许可,本项目按 Apache-2.0 条款使用
38
38
  Copyright (c) Cure53 and contributors (https://cure53.de/)
39
39
 
@@ -47,6 +47,39 @@ alongside this repository's LICENSE file.
47
47
  crelt 1.0.7
48
48
  Copyright (C) 2020 by Marijn Haverbeke <marijn@haverbeke.berlin>
49
49
 
50
- 以上版本为本仓库 v0.3.8 的 package-lock.json 锁定值。react / react-dom 为宿主平台
51
- 模块(bundle external,未随本包分发),esbuild / typescript 仅为构建期开发依赖,
52
- 均不在内置清单内。
50
+ - Markdown 渲染依赖(代码高亮 / 数学公式 / Mermaid 图表,随 bundle 内置):
51
+
52
+ highlight.js 11.12.0
53
+ BSD 3-Clause License
54
+ Copyright (c) 2006, Ivan Sagalaev.
55
+
56
+ KaTeX 0.18.0(20 个 woff2 字体以 data URI 内联,随 KaTeX 许可分发)
57
+ MIT License
58
+ Copyright (c) 2013-2020 Khan Academy and other contributors
59
+
60
+ Mermaid 12.3.0
61
+ MIT License
62
+ Copyright (c) 2014 - 2022 Knut Sveidqvist
63
+
64
+ @mermaid-js/parser 2.0.1(Mermaid 解析器;其打包产物内含 chevrotain 13.2.0,Apache-2.0)
65
+ MIT License
66
+ Copyright (c) 2023 Yokozuna59
67
+
68
+ - Markdown 渲染依赖的传递依赖(均随 bundle 内置;完整许可文本见各 npm 包内 LICENSE):
69
+ dagre-d3-es 7.0.14 (MIT)、lodash-es 4.18.1 (MIT)、marked 16.4.2 (MIT,
70
+ 与既有 marked 15.0.12 并存)、marked 15.0.12 (MIT)、dompurify 3.4.17
71
+ (MPL-2.0 OR Apache-2.0,本项目按 Apache-2.0 条款使用)、roughjs 4.6.6 (MIT)、
72
+ dayjs 1.11.23 (MIT)、khroma 2.1.0 (MIT)、stylis 4.4.0 (MIT)、ts-dedent 2.3.0
73
+ (MIT)、es-toolkit 1.53.2 (MIT)、@iconify/utils 3.1.7 (MIT)、
74
+ @braintree/sanitize-url 7.1.2 (MIT)、d3 7.9.2 及 d3-* 子模块 (ISC;
75
+ d3-ease 为 BSD-3-Clause,Copyright 2010-2026 Mike Bostock / 2001 Robert Penner)。
76
+ 以上各包版权归其上游贡献者所有。
77
+
78
+ 构建期裁剪说明:Mermaid 仅内置 13 种常用图类型(flowchart / sequence / class /
79
+ state / er / gantt / pie / journey / gitGraph / info / mindmap / timeline),未内置
80
+ 图类型在渲染时回退为「源码 + 错误提示」;ELK(elkjs,EPL-2.0 OR
81
+ GPL-3.0-or-later)与 cytoscape 布局依赖已被构建脚本排除,**未**随本包分发。
82
+
83
+ 以上版本为本仓库 v0.4.0 的 package-lock.json 锁定值(含 Markdown 渲染新增
84
+ 依赖)。react / react-dom 为宿主平台模块(bundle external,未随本包分发),
85
+ esbuild / typescript 仅为构建期开发依赖,均不在内置清单内。
package/README.en.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://www.npmjs.com/package/dsh-codinput"><img src="https://img.shields.io/npm/v/dsh-codinput" alt="npm Version" /></a>
9
- <a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/DSH-0.2.0--rc.2%2B-blue" alt="DSH Version" /></a>
9
+ <a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/DSH-0.2.2--rc.1%2B-blue" alt="DSH Version" /></a>
10
10
  <a href="https://www.npmjs.com/package/dsh-codinput"><img src="https://img.shields.io/npm/dm/dsh-codinput" alt="Downloads" /></a>
11
11
  <a href="https://github.com/Witherwithwinter/DSH-Codinput/stargazers"><img src="https://img.shields.io/github/stars/Witherwithwinter/DSH-Codinput" alt="GitHub Stars" /></a>
12
12
  <a href="./LICENSE"><img src="https://img.shields.io/github/license/Witherwithwinter/DSH-Codinput" alt="License" /></a>
@@ -37,16 +37,19 @@
37
37
  | Floating `float` | drag the blank area of the card's top row | Pops out into a floating window: draggable, resizable from all four edges and four corners (position and size are remembered) |
38
38
  | Sidebar tab `side` | open the “Codinput” tab in the right sidebar | The input lives in the sidebar panel; **collapsing the sidebar does not give the composer back** — the round ball under the title bar brings it back; `/codinput` from the sidebar exits to the official composer and closes the tab |
39
39
 
40
- - **Editor**: line numbers (optional), current-line and gutter highlighting, soft wrap, Tab indentation, a line/column indicator, and an **edit + preview split** (live markdown, exactly half and half).
41
- - **Undo history that survives mode switches**: drag into floating mode, hop into the sidebar, come back — `Ctrl+Z` still works. All three modes share the same official input machine, whose draft is the single source of truth (it survives mode switches, plugin toggles and reloads).
42
- - **`/` `@`**: entirely the official trigger pipeline (track → official menu store → a faithful MenuView replica): candidate menu, drill-down, crumbs and skeletons match the official UI; `/codinput` slots into the head of the commands section alphabetically; everything else keeps the official usage order.
40
+ - **Editor**: line numbers (optional), current-line and gutter highlighting, soft wrap, Tab indentation, a line/column indicator, and an **edit + preview split** (live markdown, exactly half and half); **the editor always renders literal characters** (no ligatures — `->` / `//` stay as typed, which avoids host font-shaping differences and “missing character” misreads, and matches the preview / message plain-text rendering).
41
+ - **Undo history that survives mode switches**: drag into floating mode, hop into the sidebar, come back — `Ctrl+Z` still works. All three modes share the same official input machine, whose draft is the single source of truth (it survives mode switches, `/codinput` exit/re-entry, plugin toggles and reloads).
42
+ - **`/` `@`**: entirely the official trigger pipeline (track → official menu store → a faithful MenuView replica): candidate menu, drill-down, crumbs and skeletons match the official UI; `/codinput` slots into the head of the commands section alphabetically; everything else keeps the official usage order. In floating / sidebar modes the candidate menu becomes an adaptive four-way flyout around the card (top / bottom / left / right free space) so it never covers the input area or reflows the card.
43
+ - **Bottom control row (0.4.0)**: value-for-value aligned with the official `0.2.2-rc.1` InputBar — `+` commands button, `conversation.input.permission` / `.plan` slot mirrors (official permission selector / plan chip), `input.left` / `.right` / `.model` seats, the voice activity seat (microphone), the official separate stop button and sending spinner, official stop / sending / steer·queue labels, `@container` narrow-width gaps and model-text compaction.
43
44
  - **Model & reasoning effort**: read from the official shared directory (the same state source as `/model`). **Permissions**: the three official presets plus the RiskConfirmation modal for full access.
44
45
  - **Attachments**: slash Add · File (the takeover surface binds the shell's file-picker contract, so the entry keeps working while Codinput owns the composer), **paste images directly**, or **drop files onto the card** (clicking an image thumbnail opens the original-image lightbox, an ImageLightbox replica; the rail is a faithful replica of the official one: 64×64 image thumbnails, file cards with type glyph/name/size, upload spinner, progress bar, retry on failure).
45
46
  - **Voice input sync (official desktop app)**: when the official desktop composer ships a voice button, Codinput renders **the very same button in the very same place** — it renders the official voice component itself (mic trigger, recording waveform, transcription, setup prompt and all copy are official code); hosts without the native voice button (all web channels) render none.
46
47
  - **Performance & usage**: a replica of the official stats dock — turns/steps · tok/s, total tok · cache hit, plus the context-usage dial — with value-for-value parity in data and formatting, each opening a detail panel; available in all three modes. The three detail panels (session stats / token usage / context segments) are portal-positioned, follow scrolling and size changes, close on Escape or outside click, and only one is open at a time.
47
- - **User-message Markdown (0.3.7)**: messages sent by the user render as Markdown (GFM + breaks via marked, sanitised with DOMPurify) while keeping the official bubble chrome — attachment rows, @file/@session//skill reference chips (clickable), copy button and timestamp, reference summary and hover reveal. Settings → Codinput → User-message Markdown rendering (on by default); turning it off restores official plain text without a reload.
48
+ - **User-message Markdown (0.3.7 / 0.4.0)**: messages sent by the user render as Markdown through the **same rendering pipeline as the editor preview** (unified in 0.4.0), while keeping the official bubble chrome — attachment rows, @file/@session//skill reference chips (clickable), copy button and timestamp, reference summary and hover reveal; **image attachments render through the official image slot (thumbnails + the official full-image lightbox on click), and raw HTML is escaped to text (chips cannot be forged)**. Settings → Codinput → User-message Markdown rendering (on by default); turning it off restores official plain text without a reload.
49
+ - **Markdown rendering (0.4.0, Obsidian-style, fully bundled and offline)**: GFM tables (alignment, horizontally scrollable when wide) / task lists (read-only checkboxes, nesting) / strikethrough / autolinks; **footnotes** (superscripts + end-of-document footnote area + back-jump); **Obsidian callouts** (`> [!note|tip|warning|danger|info|success|question|quote]`, icons/colours/collapsible, readable in light and dark themes); Obsidian syntax `==highlight==`, `%%comments%%` (hidden, never mis-parsed inside code), `[[wikilink|alias]]` (chip, click copies the target), `![[file]]` (styled text fallback), image sizing `![alt|300](url)`; Markdown images open a lightbox on click. **Code blocks**: highlight.js with 46 languages + language label + copy button; **math**: KaTeX inline `$...$` and block `$$...$$` (20 woff2 fonts inlined as data URIs — **zero network requests**, failed formulas fall back to source); **Mermaid**: 13 diagram types (flowchart / sequence / class / state / ER / gantt / pie / journey / gitGraph / info / mindmap / timeline), lazily rendered when visible, follows the light/dark theme, `securityLevel:'strict'`, syntax errors and unsupported diagram types fall back to “source + error note”. Security: raw HTML is always escaped, external links get `target=_blank rel=noopener noreferrer`, `javascript:`/`data:` protocols are blocked, KaTeX runs with `trust:false`, and Mermaid SVGs go through DOMPurify.
48
50
  - **Plugin-page icon (0.3.7)**: the packaged `assets/icon.png` is exposed through the `package.json` top-level `icon` field (manifest-relative path, SVG/PNG/JPEG/WebP, 256 KiB max), so the installed-plugins list no longer falls back to the default image.
49
- - **Settings**: Settings → Codinput (takeover on/off, line numbers, font, **fully custom send / line-break shortcuts** via a recorder, default view); with the takeover off, **opening the Codinput tab in the right sidebar enables it directly**.
51
+ - **Markdown body ligatures (0.4.0, on by default)**: the preview and user-message **body text uses the JetBrains Mono monospace font**, so `->` / `=>` / `//` etc. are drawn as ligature arrows (the DOM text and the sent message stay literal — no character replacement); it can be turned off in Settings (body returns to the reading font); **the editor keeps literal characters** (the host Chromium 152 CodeMirror ligature bug has no workaround). Code blocks and inline code use their own code font (**and also show the ligature arrows**).
52
+ - **Settings**: Settings → Codinput (takeover on/off, line numbers, font, **Markdown body ligatures (on by default)**, **fully custom send / line-break shortcuts** via a recorder, default view); with the takeover off, **opening the Codinput tab in the right sidebar enables it directly**.
50
53
 
51
54
  ## Installation
52
55
 
@@ -91,7 +94,7 @@ To pin a version or maintain the profile by hand, the manual equivalent of **Met
91
94
  ```jsonc
92
95
  {
93
96
  "dependencies": {
94
- "dsh-codinput": "^0.3.8" // for local development: "link:/abs/path/to/DSH-Codinput"
97
+ "dsh-codinput": "^0.4.0" // for local development: "link:/abs/path/to/DSH-Codinput"
95
98
  },
96
99
  "dsh": {
97
100
  "profile": {
@@ -110,18 +113,19 @@ After editing the profile by hand, run `pnpm install` inside the profile directo
110
113
 
111
114
  ## Compatibility
112
115
 
113
- The host publishes **rc** builds on npm: `latest` is the current stable rc, `next` the following rc, `alpha` further ahead (as of 2026-10-30, `latest` / `next` both point at `0.2.1-rc.5`, `alpha` is `0.2.2-alpha.2`), so a plain install gives you an rc.
116
+ The host publishes **rc** builds on npm: `latest` is the current stable rc, `next` the following rc, `alpha` further ahead (as of 2026-11-06, `latest` / `next` both point at `0.2.2-rc.1`, `alpha` is `0.2.2-alpha.3`), so a plain install gives you an rc.
114
117
 
115
118
  | Host version | Status |
116
119
  | --- | --- |
117
- | Desktop app (Electron, `0.2.1-rc.3`) | ✅ Static alignment (0.3.8 extracts this build's official CSS Modules and theme tokens from `app.asar` and checks them value by value: Switch rules, the stats / ContextMeter panels' portal+fixed placement, background and radius, and `--dsh-frame-top-clearance` all match; the macOS vibrancy/opacity difference was not eyeballed on a real desktop). The `0.2.1-rc.2` real-instance run recorded in 0.3.2 still holds (official voice-input button synced — mic permission → recording → transcription verified end to end; the button hides itself when the voice plugin is absent or off) |
118
- | web `0.2.0-rc.2` | ✅ Verified on a real instance (this machine's current web host; the slot mirrors, model-seat delegation, user-message Markdown and three-panel exclusivity from 0.3.5–0.3.7 were verified on this build with Chromium automation probes; the 0.3.8 portal-panel placement/Escape/outside-close and Markdown hardening were additionally re-checked in an isolated host with headless Edge — see CHANGELOG) |
120
+ | Desktop app (Electron, `0.2.2-rc.1`) | ✅ 0.4.0 target snapshot: the bottom control row, microphone, user-message images and drafts were checked value by value against that version's official packages (npm `@deepseek-ai/dsh@0.2.2-rc.1`, the same version bundled in the desktop `app.asar`) and verified on an isolated web host of the same version with headless Edge probes (see CHANGELOG); 0.4.0 was additionally run on an isolated desktop instance (Electron 44.7 / Chromium 152, separate `--user-data-dir` plus a copied `DSH_HOME`, never touching a live instance) to verify literal-character rendering (ligatures removed) and typing regressions; final desktop eyeballing is confirmed by the user. The `0.2.1-rc.2` voice end-to-end run recorded in 0.3.2 still holds |
121
+ | web `0.2.0-rc.2` | ✅ Verified on a real instance (this machine's earlier web host; the slot mirrors, model-seat delegation, user-message Markdown and three-panel exclusivity from 0.3.5–0.3.7 were verified on this build with Chromium automation probes; the 0.3.8 portal-panel placement/Escape/outside-close and Markdown hardening were additionally re-checked in an isolated host with headless Edge; the 0.4.0 draft retention on `/codinput` exit, ligature and floating/sidebar candidate-menu flyout were verified on this build in an isolated host with headless Edge probes — see CHANGELOG) |
122
+ | web `0.2.2-rc.1` | ✅ Verified on a real instance (0.3.9 menu-fix pass 33/33 assertions; the 0.4.0 bottom-row parity, microphone seat, official image thumbnails + lightbox, single official question-reply card, and the prefs migration / stats boundary work were verified on this build in an isolated host with headless Edge probes; 0.4.0's Markdown rendering upgrade — unified pipeline / GFM / footnotes / callouts / Obsidian syntax / image sizing and lightbox / code highlighting + copy / KaTeX (offline inlined fonts) / Mermaid with 13 diagram types — was fully verified on this build in an isolated host with headless Edge probes, see CHANGELOG) |
119
123
  | `0.1.7-rc.2` | ✅ Verified on a real instance (the acrylic candidate menu, the relocated context meter and the dock row layout are aligned to this build) |
120
124
  | `0.1.5-rc.2` | ✅ Verified on a real instance |
121
125
  | `0.1.6-alpha.2` | ✅ Current development baseline; every feature verified end to end |
122
- | others (incl. current `latest` / `next` = `0.2.1-rc.5`) | untested; older hosts lack the rc.2 tokens, so the fallback chain keeps each host's official look |
126
+ | others (incl. current `latest` / `next` = `0.2.2-rc.1`) | untested; older hosts lack the rc.2 tokens, so the fallback chain keeps each host's official look |
123
127
 
124
- For `0.1.7-rc.2` / web `0.2.0-rc.2`, on top of the real-instance run, the official CSS for the candidate menu, the context meter, the stats dock and the user-message bubble were extracted value by value from the corresponding version's client packages (`dsh-client-ui-theme` / `dsh-client-ui-input-trigger` / `dsh-client-ui-conversation` / `dsh-client-ui-chat`) and checked against the replicas. The contracts this plugin relies on were verified per version: the slots it uses (`conversation.composer.bar`, `sidebar.right.pane.tab`, `settings.section`, plus `conversation.chat.node` and the `conversation.input.*` sub-seats), the `sidebarRight` / `sidebarRightTabs` / `locale` services and `openTabIn`, the sidebar expansion planner (dockkit `planSetExpanded`), the file-picker and tab-close contracts (`shell.bindFilePicker` / `tab.actions.close`), and the official primitives the replicas are copied from (`fileSizeText` / `fileExtension` and the attachment glyph paths, byte for byte); the desktop `0.2.1-rc.3` build was additionally checked statically against the official CSS Modules and theme tokens extracted from its `app.asar`. For how to re-run this on another channel, see the “Verifying on another channel” section of [TESTING.md](./TESTING.md).
128
+ For `0.1.7-rc.2` / web `0.2.0-rc.2`, on top of the real-instance run, the official CSS for the candidate menu, the context meter, the stats dock and the user-message bubble were extracted value by value from the corresponding version's client packages (`dsh-client-ui-theme` / `dsh-client-ui-input-trigger` / `dsh-client-ui-conversation` / `dsh-client-ui-chat`) and checked against the replicas. The contracts this plugin relies on were verified per version: the slots it uses (`conversation.composer.bar`, `sidebar.right.pane.tab`, `settings.section`, plus `conversation.chat.node` and the `conversation.input.*` sub-seats), the `sidebarRight` / `sidebarRightTabs` / `locale` services and `openTabIn`, the sidebar expansion planner (dockkit `planSetExpanded`), the file-picker and tab-close contracts (`shell.bindFilePicker` / `tab.actions.close`), and the official primitives the replicas are copied from (`fileSizeText` / `fileExtension` and the attachment glyph paths, byte for byte); the 0.3.9 menu fix was additionally verified on an isolated web `0.2.2-rc.1` host with headless Edge automation (33/33 assertions); the 0.4.0 draft retention, ligature and candidate-menu flyout work was verified on an isolated web `0.2.0-rc.2` host with headless Edge automation; the 0.4.0 bottom-row parity, microphone, user-message image lightbox and prefs/stats work was verified on an isolated web `0.2.2-rc.1` host with headless Edge automation; 0.4.0 was additionally verified on an isolated desktop Electron 44.7 (Chromium 152) instance for literal-character rendering (no ligatures; `->` / `//` stay as typed) and typing regressions; 0.4.0's Markdown rendering upgrade was fully verified on an isolated web `0.2.2-rc.1` host with headless Edge probes (GFM / footnotes / callouts / Obsidian syntax / image sizing and lightbox / code highlighting + copy / offline KaTeX / Mermaid / security / consistency and regressions), with the final desktop eyeballing confirmed by the user. The desktop `0.2.2-rc.1` build is the same package version as that isolated host. For how to re-run this on another channel, see the “Verifying on another channel” section of [TESTING.md](./TESTING.md).
125
129
 
126
130
  The plugin leans on host internals (slot shadowing, `useTabInfo`, `sidebarRight`, `--dsw-*` tokens). Every call site guards with `typeof` checks and degrades gracefully, but a major host upgrade can still break it. **After upgrading the host, walk through [TESTING.md](./TESTING.md).**
127
131
 
@@ -137,11 +141,16 @@ Codinput ships no colors of its own: every color comes from the host's `--dsw-*`
137
141
  ## Known limitations
138
142
 
139
143
  - `/` `@` candidates come from host directories (skills / commands / files / sessions); when the host provides none, the menu is empty — the plugin cannot fill that in.
140
- - **No syntax highlighting** and **no Esc behaviour** — deliberate product decisions (this is an input panel, not a code reader).
144
+ - **The editor has no syntax highlighting** (the preview, messages and code blocks do have highlight.js highlighting) and **no Esc behaviour** — deliberate product decisions (this is an input panel, not a code reader; the edit side stays plain and WYSIWYG).
145
+ - **Markdown rendering is fully bundled and offline-first** (0.4.0): KaTeX fonts, highlight.js and Mermaid all ship with the plugin with zero runtime network requests; the trade-off is `lib/client.js` growing from ~540KB to ~3.4MB (gzip ~1.1MB).
146
+ - **Mermaid bundles only 13 common diagram types**: the other 24 (sankey, xychart, kanban, quadrantChart, …) are not shipped and fall back to “source + error note”; the ELK / cytoscape layout engines are not shipped either (licence and size), so diagrams requesting ELK fall back to dagre as mermaid itself does.
147
+ - **`[[wikilink]]` is a styled chip (click copies the target name)**, with no repository navigation (the host has no such target); `![[image]]` likewise only degrades to styled text and never loads repository files.
148
+ - **Raw HTML in Markdown is always escaped** (security decision): unlike Obsidian, `<div>` / `<script>` etc. are shown as visible text — never executed, never part of the layout.
141
149
  - While a Codinput sidebar tab exists the composer is hidden entirely (single input surface rule; browsing another tab in the same pane does not bring it back). The official stats dock and context dial live inside the official composer, so they disappear with it and the replicated stats row takes over.
142
150
  - Duplicating the “Codinput” tab into a second split pane shows a notice instead of a second editor (the input surface stays unique).
143
151
  - The floating gesture only keeps “drag back to the composer area = normal”; “drag into the right sidebar = sidebar mode” was dropped by product decision.
144
152
  - After a page reload, if the sidebar layout restores a Codinput tab that has not been opened yet, the composer shows up first — dockkit mounts restored tab bodies lazily; opening the tab once restores the sidebar carry.
153
+ - **No font ligatures in the editor**: `->`, `//` etc. are drawn literally (no arrow / merged glyphs). Reason: ligature glyphs read as “missing characters”, and DSH desktop `0.2.2-rc.1` (Electron 44.7 / Chromium 152) has a `calt` shaping bug (the first cell of a ligature followed by any subsequent character is drawn blank) that no CSS can reliably work around; the preview / message area is plain text anyway, so the editor now matches it — what you see is what is stored.
145
154
 
146
155
  ## Development
147
156
 
@@ -175,6 +184,12 @@ Parts of this project **replicate DeepSeek Harness** (`@deepseek-ai/dsh`, MIT Li
175
184
 
176
185
  Bundled third-party dependencies: CodeMirror 6 (MIT), marked (MIT), DOMPurify (Apache-2.0 / MPL-2.0 dual licensed, used under Apache-2.0) — the minified bundle strips license comments, so their copyright and license notices are retained in [NOTICE](./NOTICE).
177
186
 
187
+ ## Support
188
+
189
+ If this project helps you, you are welcome to buy me a bowl of rice 🍚:
190
+
191
+ <img src="./assets/donate-qr.jpg" alt="0x2f's tip QR code (buy me a bowl of rice)" width="280" />
192
+
178
193
  ## License
179
194
 
180
195
  [MIT](./LICENSE)
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://www.npmjs.com/package/dsh-codinput"><img src="https://img.shields.io/npm/v/dsh-codinput" alt="npm Version" /></a>
9
- <a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/DSH-0.2.0--rc.2%2B-blue" alt="DSH Version" /></a>
9
+ <a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/DSH-0.2.2--rc.1%2B-blue" alt="DSH Version" /></a>
10
10
  <a href="https://www.npmjs.com/package/dsh-codinput"><img src="https://img.shields.io/npm/dm/dsh-codinput" alt="Downloads" /></a>
11
11
  <a href="https://github.com/Witherwithwinter/DSH-Codinput/stargazers"><img src="https://img.shields.io/github/stars/Witherwithwinter/DSH-Codinput" alt="GitHub Stars" /></a>
12
12
  <a href="./LICENSE"><img src="https://img.shields.io/github/license/Witherwithwinter/DSH-Codinput" alt="License" /></a>
@@ -37,16 +37,19 @@
37
37
  | 悬浮 `float` | 按住卡片顶部行空白区拖动 | 脱出为悬浮窗:可拖动位置、可拖边界/角落缩放(位置与尺寸记忆) |
38
38
  | 侧栏标签 `side` | 打开右侧栏的「Codinput」标签 | 输入在侧栏面板内;**侧栏收起不让位**,由标题栏下方的圆形小球唤回;侧栏里 `/codinput` 直接退回官方输入框并关闭本标签 |
39
39
 
40
- - **编辑器**:行号(可关)、当前行与行号高亮、软换行、Tab 缩进、行列指示,**编辑 + 预览分屏**(markdown 实时渲染,严格对半)。
41
- - **撤销历史跨形态保留**:拖成悬浮、切到侧栏再回来,`Ctrl+Z` 依旧有效——三态共用同一台官方输入机,草稿即唯一事实源(形态切换、插件禁用、重启都不丢)。
42
- - **`/` `@` 触发管线**:完全走官方链路(track → 官方 menu store → 逐项复刻的 MenuView),候选菜单、钻取、crumbs、骨架屏与官方逐一对齐;`/codinput` 按字典序排入指令节首,其余保持官方使用序。
40
+ - **编辑器**:行号(可关)、当前行与行号高亮、软换行、Tab 缩进、行列指示,**编辑 + 预览分屏**(markdown 实时渲染,严格对半);**编辑器按字面字符渲染**(不做字体连字:`->`、`//` 等原样显示,避免宿主字体 shaping 差异造成「丢字符」误判,也与预览 / 消息区的纯文本渲染一致)。
41
+ - **撤销历史跨形态保留**:拖成悬浮、切到侧栏再回来,`Ctrl+Z` 依旧有效——三态共用同一台官方输入机,草稿即唯一事实源(形态切换、`/codinput` 退出再进入、插件禁用、重启都不丢)。
42
+ - **`/` `@` 触发管线**:完全走官方链路(track → 官方 menu store → 逐项复刻的 MenuView),候选菜单、钻取、crumbs、骨架屏与官方逐一对齐;`/codinput` 按字典序排入指令节首,其余保持官方使用序;悬浮 / 侧栏形态下候选菜单为四向自适应浮层(贴卡片上 / 下 / 左 / 右空位弹出,不遮输入区、不挤压卡片布局)。
43
+ - **底部工具行(0.4.0)**:与官方 `0.2.2-rc.1` InputBar 逐值对齐——`+` 指令钮、`conversation.input.permission` / `.plan` 槽位镜像(官方权限选择器 / 计划 chip)、`input.left` / `.right` / `.model` 座位、语音 activity 座位(麦克风钮)、运行中 sending 态的独立停止钮与旋转发送钮、官方式 stop / sending / steer·queue 标签、`@container` 窄宽间距与模型文字 compact 收起。
43
44
  - **模型与权限**:模型与思考强度读官方共享目录(与 `/model` 同一状态源);权限为官方三预设,含完全权限的风险确认模态。
44
45
  - **附件**:slash「添加 · 文件」、**粘贴图片**、**拖文件进卡片**三条路径;点图片缩略图弹**原图预览**(官方 ImageLightbox 同构);上传中/失败有状态芯片,失败可整卡重试。
45
46
  - **语音输入同步(官方桌面版)**:官方桌面版的输入框带语音按钮时,Codinput 在一模一样的位置渲染**一模一样的按钮**——直接渲染官方语音组件本体(麦克风钮、录音波形、转写、未就绪引导与文案全部是官方代码);原生没有语音按钮的宿主(web 端各版本)同样不渲染。
46
47
  - **性能与用量**:复刻官方 stats dock——轮次/步数 · tok/s、累计 tok · 缓存命中、上下文占用小圈,取数与格式化逐值同官方,均可点开详情面板;三形态都有。三枚详情面板(会话统计 / Token 用量 / 上下文分段)经 portal 定位、滚动与尺寸变化跟随,Escape 与点外关闭,任意时刻至多一个展开。
47
- - **用户消息 Markdown(0.3.7)**:右侧自己发出的消息按 Markdown 排版(GFM + breaks,marked + DOMPurify 净化),保留官方气泡形态——附件行、@文件/@会话//技能 引用 chip(可点开)、复制钮与时间戳、引用摘要、悬停显形;设置 → Codinput「用户消息 Markdown 渲染」开关默认开,关闭即还原官方纯文本、无需刷新页面。
48
+ - **用户消息 Markdown(0.3.7 / 0.4.0)**:右侧自己发出的消息按 Markdown 排版,与编辑器预览共用**同一套渲染管线**(0.4.0 起统一),保留官方气泡形态——附件行、@文件/@会话//技能 引用 chip(可点开)、复制钮与时间戳、引用摘要、悬停显形;**带图消息的图片附件走官方图片槽位渲染(缩略图 + 点击官方大图灯箱),原始 HTML 一律按文本转义(不可伪造 chip)**。设置 → Codinput「用户消息 Markdown 渲染」开关默认开,关闭即还原官方纯文本、无需刷新页面。
49
+ - **Markdown 渲染(0.4.0,Obsidian 风格,全内置离线)**:GFM 表格(对齐、宽表可横向滚动)/ 任务清单(只读勾选、嵌套)/ 删除线 / 自动链接;**脚注**(上标 + 文末脚注区 + 回跳);**Obsidian Callouts**(`> [!note|tip|warning|danger|info|success|question|quote]`,图标/配色/可折叠,浅深色对比度均 ≥ 标准);Obsidian 扩展语法 `==高亮==`、`%%注释%%`(隐藏、不误伤代码)、`[[wikilink|别名]]`(chip,点击复制目标)、`![[file]]`(样式化降级文本)、图片尺寸 `![alt|300](url)`;Markdown 图片点击打开大图灯箱。**代码块**:highlight.js 46 种语言高亮 + 语言标签 + 复制按钮;**数学公式**:KaTeX 行内 `$...$` / 块级 `$$...$$`(20 个 woff2 字体内联 data URI,**零网络请求**,错误公式回退原文);**Mermaid**:13 类图表(flowchart / sequence / class / state / ER / gantt / pie / journey / gitGraph / info / mindmap / timeline),可见时懒渲染、浅深色跟随、`securityLevel:'strict'`,语法错误或未内置图类型回退「源码 + 错误提示」。安全:原始 HTML 一律转义、外链 `target=_blank rel=noopener noreferrer`、`javascript:`/`data:` 协议拦截、KaTeX `trust:false`、Mermaid SVG 过 DOMPurify。
48
50
  - **插件页图标(0.3.7)**:包内 `assets/icon.png` 经 `package.json` 顶层 `icon` 字段接入宿主插件列表(manifest 相对路径、限 SVG/PNG/JPEG/WebP、≤256 KiB),已装插件不再落默认图。
49
- - **设置**:设置 → Codinput——启用接管、行号、字体、发送/换行快捷键**任意组合录制**、默认视图;未启用时**打开右侧栏的 Codinput 标签即直接启用**。
51
+ - **Markdown 正文等宽连字(0.4.0,默认开)**:预览与用户消息的**正文改用 JetBrains Mono 等宽字体**,`->`/`=>`/`//` 等靠字体连字显示成箭头/连字(DOM 与发送内容仍是原文,不是字符替换);设置里可关(关后正文恢复普通阅读字体);**编辑器输入框保持字面**(宿主 Chromium 152 的 CodeMirror 连字 bug 无解)。代码块与行内代码使用各自代码字体(**同样显示连字箭头**)。
52
+ - **设置**:设置 → Codinput——启用接管、行号、字体、**Markdown 等宽连字(默认开)**、发送/换行快捷键**任意组合录制**、默认视图;未启用时**打开右侧栏的 Codinput 标签即直接启用**。
50
53
 
51
54
  ## 安装
52
55
 
@@ -91,7 +94,7 @@ dsh plugin --profile web add link:/abs/path/to/DSH-Codinput
91
94
  ```jsonc
92
95
  {
93
96
  "dependencies": {
94
- "dsh-codinput": "^0.3.8" // 本地开发用 "link:/abs/path/to/DSH-Codinput"
97
+ "dsh-codinput": "^0.4.0" // 本地开发用 "link:/abs/path/to/DSH-Codinput"
95
98
  },
96
99
  "dsh": {
97
100
  "profile": {
@@ -110,18 +113,19 @@ dsh plugin --profile web add link:/abs/path/to/DSH-Codinput
110
113
 
111
114
  ## 兼容性
112
115
 
113
- 宿主通过 npm 发的是 **rc** 版本:`latest` 是当前稳定 rc、`next` 是下个 rc、`alpha` 更超前(2026-10-30 实测 `latest` / `next` 同指 `0.2.1-rc.5`,`alpha` 为 `0.2.2-alpha.2`),所以普通用户装到的就是 rc。
116
+ 宿主通过 npm 发的是 **rc** 版本:`latest` 是当前稳定 rc、`next` 是下个 rc、`alpha` 更超前(2026-11-06 实测 `latest` / `next` 同指 `0.2.2-rc.1`,`alpha` 为 `0.2.2-alpha.3`),所以普通用户装到的就是 rc。
114
117
 
115
118
  | 宿主版本 | 状态 |
116
119
  | --- | --- |
117
- | 桌面版(Electron,`0.2.1-rc.3`) | ✅ 静态对齐(0.3.8 从该版 `app.asar` 取出官方 CSS Modules 与主题 token 逐值核对:Switch 规则、stats 面板 / ContextMeter 面板的 portal+fixed 与背景/圆角、`--dsh-frame-top-clearance` 均一致;macOS 的毛玻璃/不透明度差异未实机目检)。0.3.2 记录的 `0.2.1-rc.2` 实机实测仍有效(官方语音输入按钮同步——授权 → 录音 → 转写端到端验证;语音插件缺席或关闭时按钮自动隐藏) |
118
- | web `0.2.0-rc.2` | ✅ 实机实测通过(本机当前 web 宿主;0.3.5–0.3.7 的座位镜像、模型座让位、用户消息 Markdown 与三面板互斥均在此版本以 Chromium 自动化探针实测;0.3.8 的 portal 面板定位/Escape/点外关闭与 Markdown 安全硬化另在隔离宿主以无头 Edge 探针复核,见 CHANGELOG) |
120
+ | 桌面版(Electron,`0.2.2-rc.1`) | ✅ 0.4.0 目标快照:底部工具行 / 麦克风 / 用户消息图片 / 草稿等均以该版官方包(npm `@deepseek-ai/dsh@0.2.2-rc.1`,与桌面 `app.asar` 同版本)逐值核对,并在同版本隔离 web 宿主以无头 Edge 探针验收(见 CHANGELOG);0.4.0 另在隔离桌面实例(Electron 44.7 / Chromium 152,独立 `--user-data-dir` + 复制 `DSH_HOME`,不触碰在用实例)实测编辑器字面渲染(移除连字)与输入回归;桌面端最终目检由用户实机确认。0.3.2 记录的 `0.2.1-rc.2` 语音输入端到端实测仍有效 |
121
+ | web `0.2.0-rc.2` | ✅ 实机实测通过(本机此前 web 宿主;0.3.5–0.3.7 的座位镜像、模型座让位、用户消息 Markdown 与三面板互斥均在此版本以 Chromium 自动化探针实测;0.3.8 的 portal 面板定位/Escape/点外关闭与 Markdown 安全硬化另在隔离宿主以无头 Edge 探针复核;0.4.0 的 `/codinput` 退出草稿保留、连字与悬浮/侧栏候选菜单四向浮层同在此版本以隔离宿主 + 无头 Edge 探针验收,见 CHANGELOG) |
122
+ | web `0.2.2-rc.1` | ✅ 实机实测通过(0.3.9 菜单修复专项 33/33 断言;0.4.0 的底部工具行逐值对齐 / 麦克风座位 / 带图消息官方缩略图+灯箱 / 问答回执仅官方一处 / prefs 迁移与统计边界均在此版本隔离宿主以无头 Edge 探针验收;0.4.0 的 Markdown 渲染升级——统一管线 / GFM / 脚注 / callouts / Obsidian 语法 / 图片尺寸与灯箱 / 代码高亮+复制 / KaTeX(离线字体内联)/ Mermaid 13 图——同在此版本隔离宿主以无头 Edge 全量验收,见 CHANGELOG) |
119
123
  | `0.1.7-rc.2` | ✅ 实机实测通过(候选菜单亚克力、上下文小圈挪位、dock 行布局即为此版对齐) |
120
124
  | `0.1.5-rc.2` | ✅ 实机实测通过 |
121
125
  | `0.1.6-alpha.2` | ✅ 开发基线,全部功能端到端实测通过 |
122
- | 其它(含现网 `latest` / `next` = `0.2.1-rc.5`) | 未实测;旧宿主无 rc.2 新 token,回退链自动保持各版官方原形态 |
126
+ | 其它(含现网 `latest` / `next` = `0.2.2-rc.1`) | 未实测;旧宿主无 rc.2 新 token,回退链自动保持各版官方原形态 |
123
127
 
124
- 对 `0.1.7-rc.2` / web `0.2.0-rc.2`,除实机验证外,还从对应版本的客户端包(`dsh-client-ui-theme` / `dsh-client-ui-input-trigger` / `dsh-client-ui-conversation` / `dsh-client-ui-chat`)逐值提取了候选菜单、上下文小圈、stats dock 与用户消息气泡的官方 CSS 核对;桌面 `0.2.1-rc.3` 另从该版 `app.asar` 取出官方 CSS Modules 与主题 token 做静态逐值核对。插件依赖的契约按版本核对过:用到的槽位(`conversation.composer.bar`、`sidebar.right.pane.tab`、`settings.section`,以及 `conversation.chat.node` 与 `conversation.input.*` 子座)、`sidebarRight` / `sidebarRightTabs` / `locale` 服务与 `openTabIn`、侧栏展开规划(dockkit `planSetExpanded`)、文件拾取与 tab 关闭契约(`shell.bindFilePicker` / `tab.actions.close`)、以及复刻所依据的官方原语(`fileSizeText` / `fileExtension` 与附件 glyph 路径,逐字一致)。插件对宿主内部结构的依赖较多(槽位遮蔽、`useTabInfo`、`sidebarRight`、`--dsw-*` 令牌),代码里处处用 `typeof` 守卫 + `try/catch` 降级,但宿主大版本升级仍可能失效。**升级宿主后请按 [TESTING.md](./TESTING.md) 走一遍冒烟清单。**
128
+ 对 `0.1.7-rc.2` / web `0.2.0-rc.2`,除实机验证外,还从对应版本的客户端包(`dsh-client-ui-theme` / `dsh-client-ui-input-trigger` / `dsh-client-ui-conversation` / `dsh-client-ui-chat`)逐值提取了候选菜单、上下文小圈、stats dock 与用户消息气泡的官方 CSS 核对;桌面 `0.2.1-rc.3` 另从该版 `app.asar` 取出官方 CSS Modules 与主题 token 做静态逐值核对。插件依赖的契约按版本核对过:用到的槽位(`conversation.composer.bar`、`sidebar.right.pane.tab`、`settings.section`,以及 `conversation.chat.node` 与 `conversation.input.*` 子座)、`sidebarRight` / `sidebarRightTabs` / `locale` 服务与 `openTabIn`、侧栏展开规划(dockkit `planSetExpanded`)、文件拾取与 tab 关闭契约(`shell.bindFilePicker` / `tab.actions.close`)、以及复刻所依据的官方原语(`fileSizeText` / `fileExtension` 与附件 glyph 路径,逐字一致);0.3.9 的菜单修复另在隔离 web `0.2.2-rc.1` 上以无头 Edge 自动化探针验收(33/33 断言通过);0.4.0 的草稿保留、连字与候选菜单浮层在隔离 web `0.2.0-rc.2` 上以无头 Edge 自动化探针验收;0.4.0 的 bottom row 逐值对齐 / 麦克风 / 用户消息图片灯箱 / prefs 迁移与统计边界在隔离 web `0.2.2-rc.1` 上以无头 Edge 自动化探针验收;0.4.0 另在隔离桌面 Electron 44.7(Chromium 152)实例上实测编辑器字面渲染(`->`、`//` 原样显示、不再有合并字形)与输入回归;0.4.0 的 Markdown 渲染升级在隔离 web `0.2.2-rc.1` 宿主以无头 Edge 全量验收(GFM/脚注/callouts/Obsidian 语法/图片尺寸与灯箱/代码高亮+复制/KaTeX 离线/Mermaid/安全/一致性与回归),桌面最终目检由用户实机确认。插件对宿主内部结构的依赖较多(槽位遮蔽、`useTabInfo`、`sidebarRight`、`--dsw-*` 令牌),代码里处处用 `typeof` 守卫 + `try/catch` 降级,但宿主大版本升级仍可能失效。**升级宿主后请按 [TESTING.md](./TESTING.md) 走一遍冒烟清单。**
125
129
 
126
130
  ### 主题 / 皮肤插件兼容(皮肤作者必读)
127
131
 
@@ -135,12 +139,17 @@ Codinput 不自带配色:全部颜色取自宿主的 `--dsw-*` 设计 token(
135
139
  ## 已知限制
136
140
 
137
141
  - `/` `@` 的候选内容来自宿主目录(技能 / 命令 / 文件 / 会话);宿主不提供时为空,插件侧无法补。
138
- - **没有语法高亮**、**没有 Esc 功能**——产品决策(输入面板不是代码阅读器)。
142
+ - **编辑器没有语法高亮**(预览与消息区/代码块有 highlight.js 高亮)、**没有 Esc 功能**——产品决策(输入面板不是代码阅读器,编辑侧保持纯文本可见即所得)。
143
+ - **Markdown 渲染全内置、离线优先**(0.4.0):KaTeX 字体、highlight.js、Mermaid 全部随插件打包,运行时零网络请求;代价是 `lib/client.js` 体积从 ~540KB 增至 ~3.4MB(gzip ~1.1MB)。
144
+ - **Mermaid 只内置 13 种常用图类型**:其余 24 种(如 sankey、xychart、kanban、quadrantChart 等)不随包分发,渲染时回退「源码 + 错误提示」;ELK / cytoscape 布局引擎未分发(许可与体积原因),请求 ELK 的图按 mermaid 官方逻辑回退 dagre。
145
+ - **`[[wikilink]]` 是样式化 chip(点击复制目标名)**,不做仓库文件跳转(宿主没有对应导航目标);`![[图片]]` 同理只做样式化文本降级,不会加载仓库文件。
146
+ - **Markdown 里的原始 HTML 一律转义**(安全决策):与 Obsidian 允许内联 HTML 不同,`<div>`/`<script>` 等按可见文本显示,不执行、不参与排版。
139
147
  - **Codinput 标签存在期间主输入整体隐藏**(唯一输入入口约定,浏览同栏其他标签也不恢复);官方 stats dock 与上下文小圈都渲染在官方输入框内部,因此随之消失、由本插件的复刻数据行接管。
140
148
  - **第三方子座位的镜像边界**:左右座(list)与模型座(single)已镜像;`input.attachments` / `overlay` / `dock` 生态暂无租客,留扩展位。模型座上**带 `inject` 面且依赖宿主 ctx 服务**的插件(如 dsh-model-accordion)在插件侧无法复现其 inject 绑定,会降级回自绘模型芯片(不空、不崩,`__dshCodinputErrs` 记一条 `slot.inject` 诊断);纯 UI 替换类(reasoning-slider、dsh-effort-dial)已实测可正常显示与交互。
141
149
  - 同一 pane 复制出第二个「Codinput」标签时,第二个面板只出提示、不渲染第二个编辑面(输入入口唯一)。
142
150
  - 悬浮模式的手势只保留「拖回底部输入区 = 普通」;「拖入右侧栏 = 侧栏模式」已按产品决策舍弃。
143
151
  - 页面重载后,若侧栏布局恢复了 Codinput 标签但尚未被点开过,主输入会先出现——dockkit 对重载恢复的标签是惰性挂载,点一次标签即恢复侧栏承载。
152
+ - **编辑器不做字体连字**:`->`、`//` 等按字面字符显示(不再显示成箭头/合并字形)。原因:连字字形会造成「丢字符」的视觉误判,且 DSH 桌面 `0.2.2-rc.1`(Electron 44.7 / Chromium 152)的 `calt` shaping bug(连字 + 后续任意字符时把连字第一格画空)无法用 CSS 稳定规避;预览 / 消息区本就是纯文本,编辑器与之一致,所见即所存。
144
153
 
145
154
  ## 开发
146
155
 
@@ -177,6 +186,12 @@ npm run typecheck
177
186
 
178
187
  内置第三方依赖:CodeMirror 6(MIT)、marked(MIT)、DOMPurify(Apache-2.0 / MPL-2.0 双许可,按 Apache-2.0 使用)——压缩产物不保留许可注释,其版权与许可声明集中保留在 [NOTICE](./NOTICE)。
179
188
 
189
+ ## 支持
190
+
191
+ 如果这个项目对你有帮助,欢迎请我吃碗白饭 🍚:
192
+
193
+ <img src="./assets/donate-qr.jpg" alt="0x2f 的赞赏码(请我吃白饭)" width="280" />
194
+
180
195
  ## 许可
181
196
 
182
197
  [MIT](./LICENSE)
package/TESTING.md CHANGED
@@ -30,7 +30,7 @@
30
30
  - [ ] **侧栏收起不让位**:收起右侧栏 → 主输入**不**回来,右上角出现圆形小球(标题栏下方、右对齐、与官方侧栏按钮不重叠)
31
31
  - [ ] **小球唤回**:点小球 → 侧栏展开且 Codinput 可见,小球消失
32
32
  - [ ] **退出侧栏**:关闭 Codinput 标签 → 主输入按 `prefs.mode` 原样恢复(普通卡片或悬浮)
33
- - [ ] **侧栏内 /codinput 退出**:侧栏编辑器输入 `/codinput` 回车 → 接管关闭,**官方输入框**回到原位,**Codinput 标签自动关闭**(经 tab 域 `actions.close`;关闭唯一标签时侧栏随之收起);重开标签或 /codinput → 侧栏形态恢复(开标签自动启用)
33
+ - [ ] **侧栏内 /codinput 退出**:侧栏编辑器输入 `/codinput` 回车 → 接管关闭,**官方输入框**回到原位且**草稿保留**(命令 token 消失、其余内容不丢),**Codinput 标签自动关闭**(经 tab 域 `actions.close`;关闭唯一标签时侧栏随之收起);重开标签或 /codinput → 侧栏形态恢复(开标签自动启用)
34
34
  - [ ] **复制标签**:右侧栏「分栏」后,两栏各开一个 Codinput 标签 → 编辑面跟随后被查看的一栏,两栏都可见时先开者持有、另一个只出提示;全局仍只有 1 个 `.cm-editor`
35
35
 
36
36
  ## 2. 编辑与草稿
@@ -40,11 +40,14 @@
40
40
  - [ ] **撤销历史跨形态**:打字 → 拖成悬浮 → `Ctrl+Z` 能撤销刚才的字(不是撤销成空)
41
41
  - [ ] **侧栏键入回写**:在侧栏标签里打字 → 关掉标签,主输入位应带着刚打的内容;侧栏内移动光标,「行, 列」指示实时更新(回归点:编辑器 hooks 是模块级单槽,若卸载时无条件清空,晚卸载的旧实例会把新实例的 hooks 摘掉——症状就是侧栏里打字不落草稿、行列指示不动、发送手势失效)
42
42
  - [ ] **官方侧灌入不进撤销栈**:发送一条消息后按 `Ctrl+Z`,不应把已发送内容拉回编辑器
43
+ - [ ] **`/codinput` 退出不丢草稿(0.4.0 回归点)**:先打一段草稿(如 `DRAFT-MARKER`),再经三条入口退出接管——① 行首输入 `/codinput` 后菜单内回车、② 鼠标点选 `/codinput` 候选行、③ 工具行指令按钮程序化唤起菜单后点选——退出后官方输入框应保留草稿(仅命令 token 不残留);再次 `/codinput` 进入接管,编辑器里草稿仍在
44
+ - [ ] **编辑器字面渲染(0.4.0 起不做字体连字)**:编辑器里输入 `->`、`//` → 显示字面两个字符(不显示箭头/合并字形);`.cm-content` computed `font-variant-ligatures: none`、`font-feature-settings` 含 `"calt" 0`;与预览 / 用户消息的纯文本一致;设置里不再有「字体连字」项
45
+ - [ ] **旧版偏好升级(0.4.0)**:浏览器 localStorage 里若只有旧键 `dsh-codinput.prefs.v2` → 首次加载后生成 `dsh-codinput.prefs.v3`(旧坏值 `shift-enter`→`shift+enter`、旧字段删除;遗留 `fontLigatures` 字段被忽略并丢弃)、旧键删除;用户自己改过的快捷键/字体/形态等偏好保留
43
46
  - [ ] 草稿在 形态切换 / 刷新页面 / 切换会话 后都不丢
44
47
 
45
48
  ## 3. 触发管线与菜单
46
49
 
47
- - [ ] 输入 `/` → 官方候选菜单在卡片上方弹出(侧栏/悬浮态改为头部行下方向下弹出),组标题本地化
50
+ - [ ] 输入 `/` → normal 形态候选菜单在卡片上方弹出(`bottom = 卡片顶 − 4`、`max-height = min(400, 卡片 top − 16)`);侧栏 / 悬浮态改为 portal 到 body 的 `position: fixed` 四向自适应浮层——落在卡片周围空位(右侧栏优先左侧对话区),**与卡片零交叠、不遮编辑区**、完整在视口内(margin 12)、内容超高时菜单内滚动;拖动悬浮卡 / 缩放窗口 / 拉伸侧栏时菜单跟随;组标题本地化
48
51
  - [ ] 输入 `@` → 文件/会话候选、section 小节标题、钻取(Tab/chevron)、crumbs 正常
49
52
  - [ ] 菜单打开时 ↑↓/Enter/Tab 由菜单消费;**菜单关闭时 Enter 是换行**(不被吞)
50
53
  - [ ] 鼠标点击候选能补全;卡片内按下不关闭菜单
@@ -73,7 +76,7 @@
73
76
  - [ ] 设置 → Codinput:启用接管(关闭后官方输入框原样恢复、草稿不丢)、行号、字体、发送/换行快捷键录制(任意组合、冲突拒绝)
74
77
  - [ ] **用户消息 Markdown 开关**:设置 → Codinput 打开/关闭「用户消息 Markdown 渲染」→ 已落库的用户消息即时在 Markdown / 官方纯文本间切换(不刷新页面),关闭状态刷新后保持
75
78
  - [ ] **设置页开关样式**:36×20、999px 圆角;ON 轨道颜色随宿主 `--dsw-alias-brand-primary`(浅/深主题各自跟随)、滑块走 `--dsw-alias-switch-thumb`;disabled 半透明、键盘 focus-visible 出官方 focus ring
76
- - [ ] 偏好刷新后保持(`localStorage['dsh-codinput.prefs.v2']`)
79
+ - [ ] 偏好刷新后保持(`localStorage['dsh-codinput.prefs.v3']`;旧键 `.v2` 只会被一次性迁移后删除)
77
80
 
78
81
  ## 6. 回归提示
79
82
 
@@ -85,19 +88,19 @@
85
88
  宿主在 npm 上有三个通道,**普通用户拿到的不是 alpha**:
86
89
 
87
90
  ```bash
88
- npm view @deepseek-ai/dsh dist-tags # latest / next = 当前 rc(2026-10-30 同指 0.2.1-rc.5);alpha = 更超前
91
+ npm view @deepseek-ai/dsh dist-tags # latest / next = 当前 rc(2026-11-06 同指 0.2.2-rc.1);alpha = 更超前(0.2.2-alpha.3)
89
92
  dsh --version # 你本机实际跑的那个
90
93
  ```
91
94
 
92
- `latest` 通道发的是 rc(当前 `latest` / `next` 同指 `0.2.1-rc.5`),所以「装了 dsh 插件却跑不起来」的报障大概率发生在 rc 上。开发机可以待在 alpha,但**对外声称的兼容范围应当以 rc 为准**,至少在 rc 上跑一遍第 1、3 节。
95
+ `latest` 通道发的是 rc(当前 `latest` / `next` 同指 `0.2.2-rc.1`),所以「装了 dsh 插件却跑不起来」的报障大概率发生在 rc 上。开发机可以待在 alpha,但**对外声称的兼容范围应当以 rc 为准**,至少在 rc 上跑一遍第 1、3 节。
93
96
 
94
- > **已验记录**:web 宿主 `0.2.0-rc.2` 为当前实机验证基线(0.3.5–0.3.7 均在本机 link 安装下以 Chromium 自动化探针实测,见 CHANGELOG);桌面版 `0.2.1-rc.2` 的语音同步于 0.3.2 实测通过;现网 `latest` / `next` = `0.2.1-rc.5`(2026-10-30 实测)。换宿主版本后请重新走一遍下面这节。
97
+ > **已验记录**:web 宿主 `0.2.0-rc.2` 为 0.3.x/0.4.x 验证基线(0.3.5–0.3.7 均在本机 link 安装下以 Chromium 自动化探针实测,见 CHANGELOG);桌面版 `0.2.1-rc.2` 的语音同步于 0.3.2 实测通过;现网 `latest` / `next` = `0.2.2-rc.1`(2026-11-06 实测);web 宿主 `0.2.2-rc.1` 的菜单专项(0.3.9 / PR #5)已在隔离宿主以无头 Edge 自动化探针验收(33/33 断言通过:portal / 几何 / 跟随 / 开合 / resize);0.4.0 的 `/codinput` 退出草稿保留、连字与悬浮/侧栏候选菜单四向浮层已在隔离 web `0.2.0-rc.2` 宿主以无头 Edge 探针验收;**0.4.0 的底部工具行逐值对齐 / 麦克风恢复 / 用户消息图片灯箱 / prefs 迁移与统计边界 / 编辑器字面渲染(移除连字)已在隔离 web `0.2.2-rc.1` 宿主以无头 Edge 探针验收**(宿主版本与用户桌面版 `0.2.2-rc.1` 相同)。换宿主版本后请重新走一遍下面这节。
95
98
 
96
99
  不动现有安装的验证方式(独立 prefix + 独立 `DSH_HOME`,不碰你在用的 profile 与会话):
97
100
 
98
101
  ```bash
99
102
  # 1) 把另一个通道装到临时 prefix,不覆盖全局安装
100
- npm i --prefix /tmp/dsh-rc @deepseek-ai/dsh@0.2.1-rc.5
103
+ npm i --prefix /tmp/dsh-rc @deepseek-ai/dsh@0.2.2-rc.1
101
104
 
102
105
  # 2) 复制一份独立 home(含带 codinput 的 profile)
103
106
  cp -r ~/.dsh /tmp/dsh-rc-home
@@ -108,13 +111,97 @@ DSH_HOME=/tmp/dsh-rc-home /tmp/dsh-rc/node_modules/.bin/dsh web --port 3081 --no
108
111
 
109
112
  重点复验:第 1 节**三态与侧栏承载**(`conversation.composer.bar` 遮蔽、`sidebar.right.pane.tab` 承载判定、收起后的唤回小球)与第 3 节**触发管线与菜单**——宿主契约变化最先断在这两处,其余功能是自绘的,跨通道风险低。
110
113
 
111
- ## 8. 0.3.7 / 0.3.8 新功能与回归
114
+ ## 8. 0.3.8 / 0.3.9 / 0.4.0 新功能与回归
115
+
116
+ **0.4.0 桌面宿主 0.2.2-rc.1 对齐与修复**
117
+
118
+ - [ ] **底部工具行逐值对齐**(对照官方输入框):`+` 指令钮 28×28、`conversation.input.permission` / `.plan` 槽位镜像(官方权限选择器 / 计划 chip)、`input.left` / `.right` / `.model` 座位顺序、语音 activity 座位、发送钮 34×34 `translateY(-2px)`;行 padding `2px 8px 6px`、gap 12;窄容器(content-box ≤560)gap 8、模型文字 compact 收起;行内放不下时 `data-model-compact` 出现。
119
+ - [ ] **麦克风恢复**:桌面版(voice-input 插件启用)工具行右侧、模型与发送之间出现官方麦克风钮(28×28,点击进入录音/引导弹窗);`__dshCodinputErrs` 无 `usePluginNavigation is not a function` 类错误;关掉语音插件后按钮消失。
120
+ - [ ] **sending / 停止双按钮**:发送瞬间主按钮进 `[data-sending]` 旋转态,运行中 sending 阶段另出独立停止钮;运行中草稿非空按官方 steer/queue 标签显示「排队发送 / 插话发送」。
121
+ - [ ] **带图用户消息(0.4.0 修复点)**:Markdown 渲染开启时,带图消息的图片以官方缩略图显示(不是 image.png 文件卡),点缩略图打开官方「原图预览」灯箱;文件附件仍为官方文件卡;关掉用户消息 Markdown 后行为不变。
122
+ - [ ] **问答回执**:回答 `ask_user_question` 后,对话里只出现官方 `[data-question-reply]` 卡片一处(这是宿主 0.2.2 官方行为,不是插件气泡);userMarkdown 开/关对照应一致。
123
+ - [ ] **prefs 升级 / 校验 / 统计边界(B1–B4)**:旧 v2 存储迁移到 v3(坏值 `shift-enter`→`shift+enter`、旧字段删除、用户改过的快捷键不被覆盖);脏 JSON 不崩;Shift+Enter 换行生效;统计/缓存命中不出现 `NaN tok`、`1000K tok`、`99.99` 之类边界文本。
124
+ - [ ] **桌面端字面渲染(0.4.0)**:桌面版(Electron 44 / Chromium 152)输入 `//`、`->`、`x -> y` → 编辑器与预览 / 复制文本都是字面字符,不再出现「合并字形 / 只剩第二个字符」;`__dshCodinputErrs` 为 0
125
+ - [ ] **安全**:用户消息里手写 `<span data-dci-wire=...>`、`<script>`、`<img onerror>`、`javascript:` 链接 → 全部按文本/被净化,不产生伪造 chip、不执行;插件自有的 `@[label](dsh-session:)` 仍正常生成会话 chip。
126
+
127
+ **0.4.0 草稿 / 连字 / 候选菜单浮层**
128
+
129
+ - [ ] **`/codinput` 退出草稿保留**:见第 1 节「侧栏内 /codinput 退出」与第 2 节「`/codinput` 退出不丢草稿」两条(三条退出入口都要过)。
130
+ - [ ] **字体连字**:0.4.0 起已移除(见第 2 节「编辑器字面渲染」)。
131
+ - [ ] **候选菜单四向浮层**:见第 3 节第一条;重点断言 float / side 下菜单为 body 直接子节点、`position: fixed`、与 `.dci-card` rect 零交叠;normal 形态几何不回归(0.3.9 已验收值)。
132
+ - [ ] **草稿跨退出/进入**:`/codinput` 退出 → 重新 `/codinput` 进入 → 编辑器恢复退出前草稿;无 `/codinput` token 残留。
133
+
134
+ **0.3.9 菜单定位与开合(PR #5)**
135
+
136
+ - [ ] **菜单 portal 到 body**:模型 / 权限 / 设置下拉打开后 `.dci-menu` 的父节点为 `body`、`position: fixed`;祖先带 `transform` / `filter` 的布局下(可用 DevTools 给卡片加 `transform: translateY(150px)`)仍与触发器对齐,不再整体偏移。
137
+ - [ ] **锚点实时跟随**:菜单打开时滚动页面 / 让布局发生位移(≥2px)→ 菜单在 2 帧内跟随触发器,上方弹出间距恒 8px、设置下拉间距恒 4px;窗口缩放后重算并夹在视口内(margin 12),不被输入卡片或设置容器裁剪。
138
+ - [ ] **开合(0.3.9 回归点)**:连点触发器两次 = 开→收(不再「点一次收不起来」);点外部关闭、点菜单内部不关闭;模型菜单钻入子页 / 返回正常;权限菜单与设置页下拉同样开合正常。
139
+ - [ ] **0.2.2-rc.1 换通道**:按 §7 独立 prefix + `DSH_HOME` 起 `dsh@0.2.2-rc.1` → 上述菜单项与第 1、3 节冒烟全过,`__dshCodinputErrs` 为空。
112
140
 
113
141
  - [ ] **用户消息 Markdown**:发送一条含 `**加粗**`、行内代码、围栏代码块、列表的消息 → 落库后右侧气泡按 Markdown 渲染,ARIA 语义(paragraph/strong/code/list)正确;@文件 / @会话 / //技能 引用 chip 显示并可点开;复制钮与时间戳、引用摘要行、悬停显形正常;`__dshCodinputErrs` 为空。设置开关即时切换(见第 5 节)。切换宿主语言后复制钮/引用摘要文案即时更新(不刷新);连续复制后卸载/切形态无残留定时器报错。
114
- - [ ] **带图用户消息(0.3.7 P0 回归点)**:发一条带图片附件的消息(或打开历史带图消息)→ 图片经官方 `renderMessageImages` 正常显示、无 `data-slot-error` 崩溃面;此后继续发送的用户/插队消息仍是 Markdown 形态(条目未被废黜);`__dshCodinputErrs` 为空。
142
+ - [ ] **带图用户消息(0.3.7 P0 回归点 / 0.4.0 修复)**:发一条带图片附件的消息(或打开历史带图消息)→ 图片经官方 `conversation.chat.user.images` 槽位渲染(官方缩略图,可点开官方大图灯箱),不出现文件卡降级、无 `data-slot-error` 崩溃面;此后继续发送的用户/插队消息仍是 Markdown 形态(条目未被废黜);`__dshCodinputErrs` 为空。
115
143
  - [ ] **Markdown 安全基线**:用户消息里输入 `<style>body{display:none}</style>`、`<form><input></form>`、`[x](javascript:alert(1))` → 气泡只显示纯文本/安全元素,页面样式不被改写、无表单/控件、非 http(s)/mailto 链接不可点;`![x](data:image/png;base64,...)` 不渲染为图片(降级为 alt 文本)。
116
144
  - [ ] **插件页图标**:宿主插件列表/已装插件卡显示 `assets/icon.png`(与文件逐字节一致),不再落默认图;包的 `package.json` 顶层 `icon` 为相对路径。
117
145
  - [ ] **数据行三面板互斥 + portal 定位**:见第 4 节两条新增条目(互斥、滚动跟随 / resize 夹取 / Escape 与点外关闭 / 不被输入卡片裁剪)。
118
146
  - [ ] **桌面端开关样式**:桌面版(当前 `0.2.1-rc.3`)设置页开关与官方 primitives Switch 同款(ON 轨道随宿主品牌色,深色主题下为近白)——本项为 0.3.8 静态对齐项;桌面实机回归前按「待验」记录。
119
147
  - [ ] **样式热更新(patchReload live)**:替换 `lib/client.js` 后强刷(或宿主原地更新插件)→ 新增的 `dci-` 类样式生效,不出现「旧 `<style>` 仍在、新类永不注入」;无需清缓存或重装插件。
120
148
 
149
+ ## 9. 0.4.0 Markdown 渲染与正文连字
150
+
151
+ 准备一份综合 fixture(可存为草稿,预览与用户消息各发一次对照):
152
+
153
+ ```markdown
154
+ ## 标题
155
+ | 左 | 中 | 右 |
156
+ | :-- | :-: | --: |
157
+ | a | b | c |
158
+
159
+ - [x] 已完成
160
+ - [ ] 未完成
161
+ - [ ] 嵌套
162
+
163
+ ~~删除线~~ https://example.com
164
+
165
+ 正文脚注[^1]。
166
+
167
+ [^1]: 脚注内容。
168
+
169
+ > [!note] 提示
170
+ > 正文
171
+ > [!warning]- 默认收起
172
+ > 折叠内容
173
+
174
+ ==高亮== %%这是注释%% [[目标笔记|别名]] ![[图片.png]]
175
+
176
+ ![图|300](https://example.com/a.png)
177
+
178
+ ​```js
179
+ const x = 1;
180
+ ​```
181
+
182
+ 行内 $E=mc^2$ 与块级:
183
+
184
+ $$
185
+ \int_0^1 x^2\,dx
186
+ $$
187
+
188
+ ​```mermaid
189
+ flowchart LR
190
+ A[Start] --> B[End]
191
+ ​```
192
+ ```
193
+
194
+ - [ ] **管线一致性**:上述 fixture 在「预览 pane」与「用户消息(userMarkdown 开)」中渲染结构一致(脚注 id 归一化后比对);`__dshCodinputErrs` 为空、console error 0。
195
+ - [ ] **Markdown 正文等宽连字(0.4.0,默认开)**:预览与用户消息正文(段落/标题/列表/表格单元格/callout 正文/脚注正文)computed `font-family` 首选 `JetBrains Mono`、字体连字生效 → `a -> b`、`x => y`、`// note` 视觉上显示为连字箭头(DOM `textContent` 仍是原文 `->`,无 Unicode 替换);**预览与用户消息的行内代码同样显示连字箭头**;设置 → Codinput 关「Markdown 等宽连字」→ 两面立即恢复普通阅读字体/代码字体字面、箭头消失,重开恢复、刷新保持;代码块在 ON/OFF 下 computed 字体/特性一致;**编辑器输入框仍字面**;编辑器草稿、`Ctrl+A` 复制文本、实际发送内容仍是 `->`。桌面端(Electron 44.7/Chromium 152)预览/消息面应正常显示连字箭头且无「第一格空白/丢字符」。
196
+ - [ ] **GFM**:表格 3 列对齐(computed `text-align` 左/中/右 + `align` 属性)、宽表在预览里可横向滚动;任务清单勾选/未勾选/嵌套只读显示;删除线、自动链接(`target=_blank`、`rel=noopener noreferrer`)。
197
+ - [ ] **脚注**:正文上标可点 → 跳到文末脚注区对应项;重复引用正常;预览与用户消息各自独立不串 id。
198
+ - [ ] **Callouts**:`note/tip/warning/danger/info/success/question/quote` 8 类图标+配色+标题正确;`[!warning]-` 默认收起、`[!note]+` 展开;未知类型按 note 蓝族回退;普通 `>` 引用**不带** callout 样式;浅色/深色主题下标题与图标对比度足够(≥3:1)。
199
+ - [ ] **Obsidian 语法**:`==高亮==` 显示高亮底色;`%%注释%%`(单行/多行/连续)隐藏,代码块/行内代码里的 `%%` 原样保留,未闭合 `%%` 按普通文本;`[[目标]]` / `[[目标|别名]]` 为 chip,点击复制目标名(不允许导航跳转);`![[图片.png]]` 显示为样式化文本、不尝试加载文件。
200
+ - [ ] **图片**:`![图|300](http)` 宽度 300(`width` 属性保留);点击预览/用户消息中的图片打开 `.dci-lightbox[role=dialog]`,Esc 关闭;相对路径/`data:` 图片降级为 alt 文本(不破图)。
201
+ - [ ] **代码块(扩展渲染)**:46 种语言子集高亮(`code` 带 `hljs language-*`),语言标签正确,未知语言不高亮但正常显示;复制按钮点击后剪贴板内容 = 代码原文,有「已复制」反馈。
202
+ - [ ] **数学公式(扩展渲染)**:行内 `$E=mc^2$` 与块级 `$$\int$$` 渲染为 `.katex`;错误公式显示原文 + 错误样式不崩;`$5 and $10`、`\$9` 不误判为公式;**DevTools Network 无任何外联请求**(字体 data URI 内置)。
203
+ - [ ] **Mermaid(扩展渲染)**:```mermaid flowchart / sequence 渲染出 SVG;可见时才渲染(懒加载);浅色/深色主题切换后图表重新渲染且可读;语法错误回退「源码 + 错误提示」;未内置图类型(如 sankey)同样回退不崩;渲染后滚动/高度正常、不遮挡后续内容。
204
+ - [ ] **安全**:`<script>`、`<img onerror>`、`<style>`、`<iframe>`、手写 `data-dci-wire`、`[x](javascript:)`、`[x](data:text/html,...)` → 全部按文本/被拦截,不执行、不产生伪造 chip;Mermaid 恶意 label 变纯文本;KaTeX `\href` 不生成可点链接(trust:false)。
205
+ - [ ] **回归**:编辑/预览分屏比例与同步滚动、预览开关与 `defaultPreview`、三形态(normal/float/side)渲染均正常;用户消息附件行仍为官方缩略图+灯箱,question-reply 让位仍成立;10 次连续切换预览无异常、无内存/定时器报错。
206
+ - [ ] **体积/离线**:`lib/client.js` ≈ 3.4MB(发布压缩),运行时零网络请求;`npm run build` 末尾 `node --check lib/client.js` 自检通过(坏 banner/语法错误会被构建阻断)。
207
+
Binary file