Skip to content

Releases: DDDMUC/dsh-edit-turn

v0.5.4 - feat: 设置卡导出给组合包

Choose a tag to compare

@DDDMUC DDDMUC released this 25 Oct 13:02

0.5.4 —— 设置卡导出给组合包(dsh-as-aistudio);会话 id 校验放宽,空会话按空状态处理。

  • 会话 id 不再只认 UUID:命名会话(例如 bili-whale,~/.dsh/storages/<profile>/sessions/bili-whale.json)是完全合法的会话,但这里的校验只接受 UUID 形式,于是对它的每个请求都被 400 拒掉 —— 面板上就是「状态加载失败,稍后重试。」。现在按 DSH 自己的语法(宿主侧 schema 就是 z.string().min(1))放宽为 /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/,仍然挡住 /、\、..、空白与控制字符。
  • 查不到会话按空状态处理:没有日志的会话(刚建的命名会话)返回空状态而不是 404 —— 面板正常加载、显示"没有可操作的内容",日志一出现下一次刷新就接上。
  • 设置卡导出:exports.ConfigCard(就是 EditorStyleOption 那个组件)现在对外导出,于是聚合包 dsh-as-aistudio 能在自己的插件页上渲染这同一张卡,而不是照着重画一份。同一份设置、同一份存储键,两个入口不会漂移;单独装本插件时那张卡仍在本插件自己的行上。

v0.5.3 - fix: 隐藏兜底不再吞第三方按钮;编辑框随内容长高

Choose a tag to compare

@DDDMUC DDDMUC released this 24 Oct 06:29

0.5.3 —— 编辑框随内容长高;隐藏兜底不再吞掉第三方插件的按钮。

  • 换页不再丢草稿:以前「正文」页写的内容只存在单块草稿里、「全部」页的子页各自另存一份,而且切页时草稿会被重置成记录原文 —— 于是「正文」页写个 1、切到「全部」页,子页里没有这个 1;切回来也没了。现在三页共用一份按块索引的草稿(正文 页与它在「全部」页里的子页是同一个块、同一个 key),任何一页里打的字,切到别的页、再切回来都还在;保存时发出的也是草稿(真页面复验:正文页写 1 → 全部页正文子页显示 1 → 在全部页改思维链子页 → 切回正文页仍是 1 → 切到思维链页是 thought draft → 再切回正文页仍是 1)。

  • 往下滚自动弹回顶部(主要问题):测量每个块的 textarea 要把它塌成 height: auto,整个文档随之变矮,浏览器就把滚动条的 scrollTop 夹到新的上限(≈0)—— 高度随后虽然恢复,位置已经丢了。宿主在流式回复时会不停重绘,所以表现为「滚到下面自动回上去」。实测:滚到底(scrollTop 847 = max 847)后触发一次布局,scrollTop 变 0。修法:新增 withScrollKept(scroller, work),测量前后保存/恢复滚动位置;「全部」页的整体重测、逐块撑高、单块模式的测量全部走它。修后同样操作 scrollTop 保持 847。

  • 最后一行滚不到:同一次「设成 auto 再读 scrollHeight」的读取永远少一行(换行在写完样式之后才落定),元素被钉在偏小高度且 overflow:hidden,外层滚动条按这个值算高度 → 最后一行既看不见也滚不到(实测:一次读 2392、二次读 2415,行高 23px)。修法:抽出 settleAreaHeight(),重置后读两次再落定。

  • 真页面复验:正文灌到 1200 / 3000 / 6000 / 12000 字符,clipped 全为 0、滚动条都能到底、最后一行完整可见且停在底栏上方 8px;滚到底后连续两次布局,位置不动。

  • 改写后的提示词气泡跟随 Markdown 渲染:装了 dsh-markdown-bubble 时,改写后那条气泡(.dshet-revision)不再固定成纯文本,而是走宿主自己的 MarkdownText(用 react-dom/client 挂载 —— 宿主自己的包也是这么用的,已实测可行)。判据是那个插件自己的存在标记 globalThis.__DSH_MARKDOWN_BUBBLE__:装了才渲染 Markdown,没装就保持纯文本,因为没装时宿主画的其它用户气泡就是纯文本,只有跟着一致才不会出现「改写过的和没改写过的长得不一样」。理由与上游建议见 deepseek-harness#10704。

  • 编辑时框跟着内容长高:以前框高在打开那一刻定死(「一轮最大」还带缓存),打字变多也不长 —— 内容超出后只能靠框内滚动条。现在:打字变多就长高,删字就变矮,但不会比「打开时」更矮(这道地板属于这一次编辑,切页也保留,保存或点叉才结束)。方向看空间:下面够就在下方长,下面不够就把底边钉在输入框卡片上沿、往上长(实测底边 680 / 输入框卡片上沿 688,留 8px 边距);整段都装不下就铺满对话区上下边界、由框内滚动。三档「高度」偏好只决定打开时多大,编辑中的跟随规则三档一致(设置页的说明文案已同步)。

  • 隐藏兜底会吞掉第三方插件的按钮(issue #1,静默失败):折叠一条被改写的消息时,那段兜底是「除我自己的三个类名外,兄弟节点一律隐藏」。dsh-rewind-pro 这类插件把图标注入到同一条用户消息里(动作条旁边 / 插槽旁边),于是被一并隐藏 —— DOM 里在、点得到、屏幕上 display:none,没有任何报错,自动化测试(querySelectorAll 数得到元素)也全绿,所以很难发现。现在只隐藏装着消息正文的那个子树:兄弟节点里不含正文的一律不动(messageContentRoot 找不到宿主形状时仍退回旧行为,免得改写后的消息留在屏幕上)。回归测试里注入两个第三方节点(动作条旁、插槽旁),断言它们保持可见、且不带本插件的折叠标记 —— 改回旧写法该测试转红。

  • 补上 error.session-empty 的中英文案:宿主半部对「还没有内容的会话」会报这个 code,客户端字典里缺它,静态校验(verify:client)一直在红。

  • 单块页(思维链 / 正文 / 用户输入)滚不动:真因不是滚轮,而是滚动位置活不过下一次布局 —— 给 textarea 赋 value 会把它的滚动位置扔回顶部(Chromium 实测:赋同一个字符串位置不变,赋另一个字符串位置归零,再赋回原值也回不来)。而「一轮最大」的高度测量正是把同一轮其它块的文字写进这个输入框来量的,于是每次缓存未命中的布局(第一次布局、切页、以及一轮里块还在变的时候——也就是模型流式输出期间)都会把读者扔回顶部。修法:测量前后保存/恢复 scrollTop。真页面复验:用户输入页灌 3000 字 → 滚到 400 → 强制重新测量(改宽度 + 输入)→ 仍是 400。

  • 思维链页保存后立即渲染(两层原因):① 宿主对一条思维链会保留多份拷贝 —— 折叠时那一行留在 DOM 里但 display:none,展开面板时再渲染一份可见的正文;而补丁逻辑只要「记住的那个元素还连在文档里」就认为已经打过补丁并 continue,于是用户正在看的那一份始终是记录原文,直到整页刷新(刷新会从日志重新折叠,宿主自己就画对了)。现在这一支会把所有拷贝一起打上(记住的元素、所有带标记的元素、所有文本等于原文/上一次文本的元素,含 findReasoningElements 这个复数版搜索),实测同一次保存后 data-dshet-patch 出现两份 335:0。② 一条回复的块列表是整轮的(宿主把同一轮里每一步的思维链和正文都挂在回答行上,每块带自己的 seq),于是同一份列表里会有多个 index 相同的块(第一步的思维链和第四步的思维链都是 index 0)。以前两处都只按 index 找块:保存后本地那条「乐观」补丁还按回答行的 seq 记,渲染时又用 find(index === …) 命中第一个同 index 的块 —— 两处都拿错了 original,于是补丁在 DOM 里匹配不到任何东西,思维链正文要等刷新才变。现在补丁按块自己的 seq 记,查块按 (seq, index) 查,并顺着块找到承载它的那条回复条目(后一步的块住在以回答行 seq 为键的条目里)。

  • 测试:npm test 254/254(新增六例:补丁只落在目标步的思维链上;保存后思维链正文立即变;后开的思考面板同样显示改写;测量后 textarea 的滚动位置不变;编辑时随内容长高、删字只矮回打开时的高度;下方没空间时向上长而不压输入框 —— 六例在改回旧写法时都会失败)。(新增五例:换页保留草稿;模拟「第一次读少一行」的 textarea 断言最终高度;模拟浏览器夹滚动条断言测量后位置不变;装了 markdown 插件时改写气泡走宿主渲染器、没装时保持纯文本 —— 把修法还原成旧写法时都会失败)。

v0.5.2 - 回复编辑不再等这一轮 / 跟随皮肤 / 思维链就地渲染

Choose a tag to compare

@DDDMUC DDDMUC released this 22 Oct 16:14

变更

  • 回复编辑不再等这一轮回复结束:就地记录(patch)只 append 一条 ignorable 事件、不改任何 surface 节点,所以 agent 还在写的时候也能保存,模型下一次折叠就读到。宿主侧只在会移动 surface 的路径上保留忙时拒绝:改写提示词(surface replace)与 replay 兜底(遮蔽窗口 + 重放尾部)。
  • 思维链改写真正落到界面:官方把 reasoning 画在它自己那一行(flow key 带 ,reasoning 分组),而按 seq 找到的行是正文行 —— 旧代码只在那一行里找,所以思维链改写永远落不到 DOM。现在思维链的搜索改为在整个会话里找「文本等于记录原文」的最深节点,并和其它块一样登记 data-dshet-patch(可回退);思考面板折叠时连头部片段一起按改写后的文本重画。
  • 编辑器跟随当前皮肤:文字/描边/主色/圆角/阴影全部读主题令牌(--dsw-alias-*、--dsw-radius-*、--dsw-shadow-lv3),取不到时退回本插件原值;悬停底色用 color-mix 从主题墨色算。皮肤的表面色只取色相、不取透明(插画皮肤把表面做成半透明靠自己的文字描边保可读,插件元素没有描边),所以「中秋」这类深色插画皮肤得到同色系深色面板,而不是插件自带的浅色面板。调色板挂在 body 上(主题令牌声明在 body,挂在 :root 上会静默取 fallback)。
  • 就地编辑器不再刷白底:pageBackground() 优先用已解析的 --dshet-panel(插画皮肤下 body 是透明的,旧代码退回 #ffffff,深色皮肤上出现白框)。
  • 改写后的提示词气泡用皮肤自己的气泡色(--dsw-specific-bubble)与圆角(--dsw-radius-xl)。
  • 同一块可以反复编辑、projection 缺失自动降级走 replay、「全部」切回单块不再残留、输入框不再被底栏压住、失败提示变醒目、保存键在忙时不再假死。

验收

  • npm test 241/241、npm run verify:contract 96 项、npm run verify:client 192 项。
  • 真页面复验:思维链改写就地可见、data-dshet-patch 落在 _text_* 思维链节点上;中秋皮肤下 --dshet-panel 解析为 rgb(20,20,25)、就地编辑器背景不再是白色。

Changes

  • Reply editing no longer waits for this turn's reply to finish: The in-place record (patch) only appends an ignorable event and does not modify any surface node, so it can be saved while the agent is still writing, and the model will read it on the next fold. The host side retains busy-time rejection only on paths that move the surface: rewriting the prompt (surface replace) and replay fallback (mask window + replay tail).
  • Chain-of-thought rewriting actually reaches the UI: The official implementation draws reasoning on its own row (the flow key includes a ,reasoning group), while the row found by seq is the body row — the old code only searched within that row, so CoT rewrites never landed in the DOM. Now the CoT search instead looks across the entire session for the deepest node whose "text equals the recorded original text," and registers data-dshet-patch like other blocks (revertible); when the thinking panel is collapsed, it redraws together with the header snippet using the rewritten text.
  • The editor follows the current skin: Text/stroke/primary color/border radius/shadow all read theme tokens (--dsw-alias-*, --dsw-radius-*, --dsw-shadow-lv3), falling back to the plugin's original values when unavailable; the hover background is calculated with color-mix from the theme ink color. The skin's surface color takes only the hue, not transparency (illustration skins make surfaces semi-transparent and rely on their own text stroke for readability; plugin elements have no stroke), so dark illustration skins such as "Mid-Autumn" get a dark panel in the same color family instead of the plugin's built-in light panel. The palette is attached to body (theme tokens are declared on body; attaching to :root would silently take the fallback).
  • The in-place editor no longer paints a white background: pageBackground() prioritizes the resolved --dshet-panel (under illustration skins body is transparent; the old code fell back to #ffffff, producing a white box on dark skins).
  • Rewritten prompt bubbles use the skin's own bubble color (--dsw-specific-bubble) and border radius (--dsw-radius-xl).
  • The same block can be edited repeatedly, missing projection automatically degrades to replay, switching from "All" back to a single block no longer leaves residue, the input box is no longer covered by the bottom bar, failure prompts are more noticeable, and the save button no longer appears dead while busy.

Acceptance

  • npm test 241/241, npm run verify:contract 96 items, npm run verify:client 192 items.
  • Real-page re-verification: CoT rewrites are visible in place, and data-dshet-patch lands on _text_* CoT nodes; under the Mid-Autumn skin, --dshet-panel resolves to rgb(20,20,25), and the in-place editor background is no longer white.

dsh-edit-turn v0.5.0

Choose a tag to compare

@DDDMUC DDDMUC released this 21 Oct 16:18

dsh-edit-turn 0.5.0

  • 整轮所有块:回答里每个 step 的 reasoning 与每个 text 都进 picker,同类自动编号;每块带自己的 seq,保存时 patch 到它所在的消息。
  • 「全部」视图:一页同时显示整轮所有块,每块自动增高 + 轻量分割线 + 标签;顶部 sticky 块导航;只有外层容器一个滚动条;宽度与 composer 卡片对齐。
  • 编辑器高度:fill(默认)/ max / content,设置页可选。
  • 编辑器位置:overlay / inline / legacy;除 inline 外默认夹紧在对话区顶部与输入框之间。
  • 输入体验:粘贴图片/文件、拖拽文件进框,与「+」同一 chip 列表。
  • 图标:取消 X / 重跑环形箭头 / 保存双勾统一 28×28、15px SVG、#81858c;「+」为官方 composer 同款。
  • 性能与自适应:滚动放置 rAF 节流 + 缓存;MutationObserver 忽略自身层;DOM 注入改 useLayoutEffect 并同步补回;resize/全屏/缩放重新放置。
  • 宿主:/apply 支持 targets 数组(一次写一条记录);/state 回复块带 seq;reasoning 字段兼容 text/reasoning/thinking。

测试:npm test 228/228、contract 96、client 184。


  • All blocks in the whole turn: the reasoning of each step and every text in the answer enter the picker, automatically numbered within the same type; each block carries its own seq, and when saving it is patched to the message it belongs to.
  • "All" view: displays all blocks of the entire turn on one page, with each block auto-expanding + light dividers + labels; sticky block navigation at the top; only the outer container has a scrollbar; width aligned with the composer card.
  • Editor height: fill (default) / max / content, selectable on the settings page.
  • Editor position: overlay / inline / legacy; except for inline, by default clamped between the top of the conversation area and the input box.
  • Input experience: paste images/files, drag files into the box, in the same chip list as "+".
  • Icons: Cancel X / rerun circular arrow / save double check are uniformly 28×28, 15px SVG, #81858c; "+" is the same as the official composer.
  • Performance and adaptivity: scroll placement uses rAF throttling + caching; MutationObserver ignores its own layer; DOM injection switched to useLayoutEffect with synchronous re-insertion; reposition on resize/fullscreen/zoom.
  • Host: /apply supports a targets array (writes one record at a time); /state reply blocks carry seq; the reasoning field is compatible with text/reasoning/thinking.

Tests: npm test 228/228, contract 96, client 184.

dsh-edit-turn 0.3.3

Choose a tag to compare

@DDDMUC DDDMUC released this 20 Oct 17:51

Fixes

  • The editor is now anchored to the pencil the user pressed, not to the assistant row. On a reply, the pencil lives in the turn-tail action strip while the anchor row can be taller than the screen; 0.3.2 kept the buttons reachable but could place the box at the far edge. It now prefers just below the button, flips just above it, and only then clamps to the viewport.
  • Long drafts still scroll inside the editor with a sticky footer, so Save/Cancel stay visible.

Verification: npm test 213/213, contract 96, client static 184. Live-checked in a headless browser: pressing the pencil of a long reply opens the editor above that pencil (top 116px), with the footer and Save inside a 700px viewport.

dsh-edit-turn 0.3.2

Choose a tag to compare

@DDDMUC DDDMUC released this 20 Oct 17:45

Fixes

  • The reply editor now stays inside the viewport: it prefers the space below the row, flips above it when needed, and pins to the bottom of the viewport when the row is taller than the screen (the previous behavior pushed the footer and its Save button off-screen).
  • The editor caps its own height (max-height + overflow-y:auto) and keeps the footer sticky, so a long draft scrolls while Save/Cancel stay reachable.
  • Width and left edge are clamped to the viewport for narrow windows.

Verification: npm test 213/213, contract 96, client static 184. Live-checked in a headless browser against a long assistant message: editor top 8px, footer and Save inside a 700px viewport, textarea scrolls internally.

dsh-edit-turn 0.3.1

Choose a tag to compare

@DDDMUC DDDMUC released this 20 Oct 10:25

Fixes

  • Reply patches now land on the active assistant row after a rerun; the turn-process row no longer steals the same seq.
  • Markdown-aware identity matching (**1** vs rendered 1。), bare-text fallback, and React re-render recovery.
  • Patched text renders without Markdown markers; <html data-dshet-version> identifies the loaded client.
  • /state.patches publishes only patches whose target is still on the surface; a prompt replacement inherits its turn.

Verification: npm test 210/210, contract 96, client static 184.

v0.3.0 — in-place reply block patches

Choose a tag to compare

@DDDMUC DDDMUC released this 20 Oct 06:59

回复编辑不再在当前宿主上重放尾部(tail)。

  • 机制:通过 appendPluginRecord 写入一条 plugin:dsh-edit-turn/patch 记录,由已注册的消息投影进行解释。界面不发生位移,后续轮次保留其编号,工具调用保留,日志每次编辑只增加一条小记录。
  • 块:输出文本和推理/思考均可编辑;编辑推理会丢弃过时的签名。
  • 回退:replyMode: auto | patch | replay;当宿主缺少任一 API 时,auto 使用 0.2.21 的 replay splice。实时重载会复用已注册的投影。
  • UI:包含多个可编辑块的回复会显示块选择器;投影文本就地绘制,折叠的推理补丁会显示一条注释。
  • 已验证:205 项测试、96 项真实校验器契约检查、184 项客户端静态检查、以 mode=patch 和 pluginRecords=true 启动沙箱配置,以及通过 /state 读取一个真实存储会话。

卸载说明:投影随宿主进程存在;卸载插件并重启宿主,即可回退到已记录的文本。


Reply edits no longer replay the tail on a current host.

  • Mechanism: one plugin:dsh-edit-turn/patch record through appendPluginRecord, interpreted by a registered message projection. The surface does not move, later turns keep their numbers, tool calls stay, and the log grows by one small record per edit.
  • Blocks: output text and reasoning/thinking are both editable; a reasoning edit drops the stale signature.
  • Fallback: replyMode: auto | patch | replay; auto uses the 0.2.21 replay splice when the host lacks either API. A live reload reuses the registered projection.
  • UI: a reply with several editable blocks gets a block picker; the projected text is drawn in place, and a collapsed reasoning patch gets a note.
  • Verified: 205 tests, 96 real-validator contract checks, 184 client static checks, sandbox profile boot with mode=patch and pluginRecords=true, plus a real stored session read through /state.

Uninstall note: the projection lives with the host process; unload the plugin and restart the host to fall back to the recorded text.

v0.2.21 - reply edits replay the turns behind them

Choose a tag to compare

@DDDMUC DDDMUC released this 19 Oct 19:08

A corrected reply no longer takes the rest of the conversation with it.

A reply cannot be a replacement carrier (the format refuses sourceEventSeqs on an
assistant/message), so 0.2.20's reply edit rolled the tail back and lost every later
turn. 0.2.21 performs the same infix splice dsh-rerun-turn uses after a rerun: shadow
from the addressed reply to the tail, append the correction in a fresh turn, then replay
every later turn back as fresh copies with new turn numbers, fresh message ids and
replayBy / replayId / originalSeq markers. The model reads prefix + corrected reply

  • the same later turns, in order.

The replay builder mirrors dsh-rerun-turn's write for write (a parity test compares the
two on one log); tool calls and results stay paired, unresolved calls are dropped along
with their advertisement, silent carriers and log-only records are skipped, and the live
loop counter moves to the highest replayed turn. A reply in the last turn writes no replay.

Also fixed: the editor placeholder/confirm text and the package description still told the
old "saving removes everything after this message" story; a partial replay now notifies the
user instead of silently hiding rows.

189 unit/host/DOM tests, 85 real-validator contract checks, 184 client static checks green.
Four real multi-turn sessions (up to 11,013 events / 130 turns) were planned and replayed
in memory and reload cleanly; a sandboxed profile boots and answers its routes.

v0.2.20 - an unchanged save announces nothing

Choose a tag to compare

@DDDMUC DDDMUC released this 06 Oct 06:07

A no-op save shows nothing now.

0.2.18 made a save that changes nothing write nothing, but announced it with a notice that
rendered over the composer placeholder text. The editor closing is the feedback; the notice
was clutter, so it is gone, along with its dictionary entries and the test that pinned it.

The unchanged flag stays and still does its real job - it is what keeps a no-op from being
reported as a failed rollback. The re-run chain is untouched: an unchanged save followed by a
re-run still re-runs the turn.

182 unit / 80 contract / 184 client checks green. Verified negatively: restoring the notify
call turns exactly the rewritten case red while the re-run case stays green.