dsh-claude-style 0.15.0 → 0.16.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,110 @@ All notable changes to `dsh-claude-style` are documented here, newest first.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.16.0] - 2026-10-16
8
+
9
+ [中文](#cn-0.16.0) | [English](#en-0.16.0)
10
+
11
+ <h3 id="cn-0.16.0">体验优化</h3>
12
+
13
+ - **用量记录的体积缩到约四成**:记录里一天的量按路由存放(模型,以及没有归到任何模型的余量),当天总量由这些路由相加得出,小时直方图只记非零的格子,日志已不在的会话不再存指纹。用本机上这一份记录量:146 KB 降到 57 KB,一趟只剩解析与写回;与记录一致的那一趟不再写盘。
14
+ - **界面每帧少做整份文档的查找**:查不到东西的查找此前每趟刷新都重新走一遍整份文档,而绝大多数页面上这些座位本来就不存在——会话页没有首屏根,对话框没打开时没有设置导航列。现在这类「什么都没找到」的答案在页面结构变化之前一直有效;设置页的导航格、侧栏的已归档清单清理、账户卡片的清理都收窄到它们各自所在的侧栏里。在 28568 个节点的页面上量同一个回合:插件发出的整份文档查找从 584 次降到 395 次,其中首屏根的查找从 273 次降到 26 次。
15
+ - **长对话里收走状态行的那一趟不再按对话长度平方增长**:宿主一次交还多行时,插件要把每一行还站着的状态线摘掉,此前每条站着的线都要在一份运行行清单里从头找一遍。现在这份清单改作一次集合,摘除的一趟随行数线性走完。
16
+ - **滚动动效的断言不再随机器快慢变化**:跟随的追赶与流式滑行此前在几个钟点上各读一次距离,忙碌的机器上读到的差值不同;现在探针从滑行开始的那一帧起逐帧记下距离,断言改成这条记录的不变式——开头离终点很远、每一帧只朝终点靠近、中间至少经过两个不同的值、末帧落在终点、结尾连续若干帧都停在终点。阈值与帧数住进 `packages/testing/bounds.json`,`scripts/lint.mjs` 新增一条规则:台账里每条边界都要有名字、有限的值与理由,被检查读到的名字必须在台账里,台账里没人读的条目要被拿掉。
17
+ - **账户页脚那一块不再靠 `!important` 取胜**:`packages/client/src/features/account/account-footer.css` 里那 79 处 `!important` 去掉了,主机账户行、账户菜单、头像与皮肤画布、抽屉里的行与悬停这些都改为按选择器本身的权重压过去——皮肤的选择器本来就带 `body[data-dsh-claude-style]`,比宿主那些单个类名的规则重。这些声明此前压在宿主账户菜单元素上,那个形态的走查读不到,所以从没被判定过是否必要。`importance` 场景重新走了一遍:每一处都判定过,没有一处多余,十六处仍然必要(压的是宿主的内联样式);账户菜单那四条选择器因此从「够不到」登记表里划掉。顺带把账户菜单与首页两个菜单的行样式合成一处按两个标记写(此前两处一模一样,去掉了 `!important` 才看得出重复),首页那份只留自己独有的禁用态与选中标记。页面外观不变:外观快照五十个页面状态逐项一致,账户与首页相关用例全过。
18
+ - **页脚接管那一块也不再靠 `!important`**:`packages/client/src/features/account/footer-takeover.css` 里那 43 处 `!important` 去掉了——页脚各容器归零、镜像条目塌缩、侧栏收成细轨时的账号按钮与弹层、以及细轨里隐藏设置入口的那些规则,改为按选择器本身的权重压过去(这份表排在自己的基础规则之后,且每条都点名了细轨、页脚与它要的那一行)。`importance` 场景重走:422 处声明全部判定过,没有一处多余,十六处仍然必要;台账随之写下 422 条。外观快照五十个页面状态逐项一致。
19
+ - **状态行里的说法改为放不下才截断**:模型自己写的推理从句、命令调用声明的说明、正在操作的文件名此前都按字数上限截断(分别 40、40、32 字),宽窗口里也照样在同一个字数上断开,读者看到的是一句被切掉的话。现在这三种说法整句带到这一行,放得下多少由这一行的宽度决定:宿主那一行的内容盒此前不设宽度上限,长说法会顶着计时与徽标一直伸出对话列;它现在取所在行的宽度,说法在自己的盒子里截断并画省略号,加载点、计时与徽标留在原处。
20
+ - **状态行里的从句不再被名字里的点号截断**:这一行从推理正文里取模型自己那句话时,句子在哪里断开此前由一张手写的收尾标点表决定,于是文件名、版本号与缩写里的点号都被当成句末——正在读 `app.js` 的说法写成「Reading app」,正在看 `package.json` 写成「Checking package」,缩写处更只剩下一个字母。现在句子的边界交给页面自己的句子规则,说法从分词一直带到这一句真正结束的地方;同时助动词与分词之间的一两个词(now、currently、still、just 等)与完成进行时(I have been checking)也能读出来,此前这些句子整句读不出,这一行只会退回「正在思考」。否定的句子与还没动手的打算照旧不读。
21
+ - **状态行里的从句站到下一次意图到达为止**:模型在快速输出时每秒写下好几个句子,从句与从句之间那几拍(推理读作「正在思考」,或刚起一步还没写下什么)此前会把刚说出的从句顶掉,读者看到的是一句话闪一下就退回「正在思考」,再闪一下又说下一句。现在模型自己那句从句在下一次意图到达之前一直站着——新的从句、调用自带的说法,或写回答、调用工具这类状态一到才改口;「精确状态行」关掉时不读从句,这一条不适用。一个说法锁一秒的规则不变,这条锁也因此落在说法上而不是落在行上:宿主在步骤之间重建那一行时,这一轮站着的说法与它的锁跟着这一轮走,不会重新从零开始。
22
+ - **状态行与过程组标题的扫光改用宿主自己的做法**:高光带由一层带遮罩的窗口与其中一份着色的文字副本反向平移组成,两半都是位移动画,由合成器播放,不再每帧重绘;节奏、步进、0.3 秒的起始延迟与遮罩都与宿主的行一致。回合进行中的页面上不再有运行中的绘制型动画。
23
+ - **深色下的边框改为一层高光**:`--dsw-alias-border-l1`/`-l2`/`-l3` 的深色取值从 `#242320`/`#2e2c29`/`#3a3833` 三个实色改为同一个透明白在 `.09`/`.18`/`.30` 三档,卡片、面板、代码块与小徽标的外缘因此读作顶光打出的倒角高光,而不是需要在每种底色上重新配色的轮廓线;浅色的三档仍是暖色实色。
24
+ - **弹层的边缘留在边框上,深色在外侧多一道沉降**:皮肤自建的弹层卡片与宿主自己的账户菜单、首页选择器不再把边线挪进投影——线仍是卡片自己的边框(浅色用边框色,深色用深色阶梯那格高光),排布读到的那只盒子和开启动画缩放的绘制路径因此与从前一致;深色在外侧加一道 3px 暗色沉降,把受光的边与近黑画布隔开;投影同时收成 `0 8px 24px` + `0 2px 6px`,深色加深到 `rgba(0,0,0,.65)` / `rgba(0,0,0,.45)`。
25
+ - **侧栏与对话栏之间的分割线收到发丝细边**:这条线从 1px 改为 0.5px(Windows 桌面顶栏里它的延续段同步改),在高分屏上正好是一个物理像素,与产品自身的取值一致;Markdown 表格外框与轨迹胶囊原本就是 0.5px,其余单向细线仍是 1px。
26
+ - **用户消息气泡改用写定的一对底色**:`--dsw-specific-bubble` 在 Claude 配色下不再借用按钮悬浮灰,改为浅色 `#f0f0ef`、深色 6% 白;深色此前没有取值,会回落到宿主自己的深蓝,现在这一格由本插件写定。浅色没有采用产品自己那格偏蓝的底片——它贴在这张暖色画布上会发蓝,改用皮肤已有的中性底衬(表头同值);设置页的消息气泡演示读同一个令牌,随之一起变。
27
+ - **流式输出的平滑跟随交给显卡来画**:输出期间滚动位置由宿主自己的跟随写到末尾,皮肤不再逐帧改写位置,只把新长出的内容从下缘滑上来,状态行停在原处不动。宿主的跟随在整段输出里保持开启,回到底部按钮不再需要藏起来。在本机同一段快速输出里量:最新内容落后于静止位置的距离中位数从 31 像素降到 26–28 像素,最大值从 73 像素降到 62 像素,每 4 秒的布局次数从 49 次降到 44–45 次。
28
+ - **弹层里的分隔线统一成宿主那一根,并由一处决定**:宿主菜单自己画的 `[role="separator"]` 与皮肤自己建的分隔(模型选择器的页脚分隔、供应方标题那条线)现在都是同一根 0.5px 的 `--dsw-alias-border-l2`,写成零高度盒子上的上边框而不是半个像素高盒子的底色,线因此属于排版而不是随开启缩放改变厚度;那条通用的规则落在共享外壳里,皮肤自己的卡片与它重绘的两个宿主菜单一起受它管辖(此前只有账户与首页两个菜单各写一份 1px),模型选择器的两个页脚上边框也从 1px 收到 0.5px。
29
+
30
+ ### 问题修复
31
+
32
+ - **删除对话不再带走它的用量**:面板的账此前每趟都从现存的会话日志重算,日志一删,那个会话的日子就从「全部」里消失。现在本地的这本账记在插件自己的目录里,日志已不在的会话照旧留着它结算过的日子,面板继续给出这些日子的 tokens、模型排行与小时分布;早先留在折算缓存里的日子在首次读取时迁移过来。
33
+ - **用量记录不再被覆盖掉**:记录解析不了时改名为 `usage.json.corrupt-<时间戳>` 留在插件目录里,再从会话日志重建;同一个 `$DSH_HOME` 下开着几个实例时,每次写回先合并磁盘上的那一份,另一个实例记下的会话不会被盖掉;旧的折算缓存在记录写成功之后删除。
34
+ - **侧栏页脚的插件条目全部收进账号区**:动作槽里的条目与别的插件直接挂在页脚上的卡片(用量卡片这一类)都收进账号弹层,卡片保持它自己的样子,侧栏底部只剩账号这一行,条目自带的行内样式一并让位;`importance` 台账随之记到 424 处。
35
+ - **带供应商命名空间的模型标识也认得出厂商标**:网关把模型标识写成 `cn:hy3` 这类带前缀的形状时,混元系列的模型行照常画出厂商组合标,模型列表的第一级也一并给出它的说明文案。规则的分段起点改为任何不是字母或数字的字符,`shy-mt2` 这类标识仍不被认领。
36
+ - **同一处分段起点下的其余模型也认得出厂商标**:`kimi-k3`、`minimax-m3` 这一类带服务方命名空间的标识此前也不被认领,`o3` 那一条只认冒号以外的分隔符;现在 K3、M3 与 o 系列的规则与混元那条读同一个分段起点。`nova` 那条补上分段边界,`sensenova-6.8-flash-lite` 不再被判成 Amazon Nova、标记也不再插进模型名中间。
37
+ - **Claude 品牌下的扫光不再是蓝色**:宿主在运行行上把扫光换成 DeepSeek 的蓝色,状态行的高光因此一直是蓝的;现在 Claude 品牌下状态行、过程组标题与宿主自己的调用行都用同一道暖色高光(暗色为 `#faf9f5` 的 45%,亮色为 `#141413` 的 30%),DeepSeek 品牌下仍是宿主的颜色。
38
+ - **在侧边栏或旁边的子代理转录里滚动、点击,不再让主会话停止跟随**:只有落在主会话滚动区里的滚轮、按压与拖动,以及没有焦点时按下的滚动键,才算读者离开了末尾;之前页面上任何一处的滚轮或点击都会让主会话停在原处,流式输出不再往下跟。
39
+ - **状态行不再把引用里的话当成模型在做的事**:模型在推理里引用它读到的一句话,或者在推理里讨论这一行自己那个等待词时,这一行会把那段引用连同后半句一起说成自己的动作。现在引号里(「」『』“”与英文双引号)的内容不参与从句判定,从句也不在那里断开:模型引用什么,读者不会在状态行上看到它被说成模型在做的事;模型自己那一句里带引号的部分照旧原样带出。顺带把中文的「正在」判定撤掉:它与这一行的等待词同形,没有词表能把「正在读取 app.js」与一句关于这一行的话分开,中文推理回到说等待词。
40
+
41
+ ### 其他变更
42
+
43
+ - **样式表里判断「谁包含谁」的规则改为读一处标记**:此前 16 条规则用 `:has()` 问浏览器「这个元素的子树里有没有那个部件或标记」,页面下方每次变化这些规则都要重新匹配。现在盖部件的同一趟走查顺带把答案写成一处标记,规则只读这处标记;页面上的 `:has()` 只剩进入作用域的两处与跟随焦点状态的一处。构建同时开始拒绝在 `:has()` 里读属性、核对每个标记都有声明的来源、并核对声明里的部件名。
44
+ - **每趟刷新与每一帧上的整份文档查找改走记下的答案**:界面切换的横带每趟刷新要问八次「对方的控件在不在」「侧栏的切换键在不在」「窗口菜单在不在」,聊天跟随每帧要问一次「宿主还在流式输出吗」,桌面端的页头每趟刷一次「窗口菜单在哪」——这些座位在多数页面上根本不存在,而每次问都要把整份文档走一遍。这一次改掉九处(界面切换七处、页头一处、聊天跟随一处):这些答案由宿主访问模块记住,页面结构变了才重新查找;聊天跟随改在消息列里读那个标记,与它自己的另一处读法一致。剩下的四十六处逐条登记在册并写明缘由,脚本会拒绝新增的查找,也会拒绝与代码对不上的登记。
45
+ - **检查接受的数值成了一份会被核对的记录**:一个检查失败时,最省事的回应是把表达式里那个数字放宽,而这一步此前不留痕迹。现在两个通道里每个检查所决定的数字字面量都写进 `packages/testing/check-numbers.json`(`npm run checks:numbers` 生成),闸门与云端核对这份记录:改动一个检查接受的数值,记录跟着变,放宽因此在 diff 里看得见。需要理由的边界本身继续住在 `packages/testing/bounds.json` 里,每条带 `reason`,检查读它。
46
+ - **每处保留下来的 `!important` 都记下它压过谁**:重要性走查此前只回答「去掉它画出的样子变不变」,现在把判定为需要的每一处写进 `packages/testing/importance-ledger.json`——在哪个页面形态上判定需要,以及去掉重要性之后接过这个属性的候选(元素自己的行内样式、宿主的 `!important`、宿主权重更高的规则、皮肤的另一条规则)。走查把该处的重要性单独去掉,按属性展开的每一条长属性读页面退回的值,再逐个拿走其余候选:值动了的那些就是它压过的。台账里新出现或不再需要的条目会让走查失败,`DSH_IMPORTANCE_LEDGER=write` 从一次运行重写它,差别在 diff 里读。本次十六处里三处认出了对手,其余记作未认出(走查读不到那个候选)。
47
+ - **构建要求每一处 `!important` 都有页面上的依据**:重要性走查现在把读到的每一处声明都写进 `packages/testing/importance-ledger.json`——判定为需要的连同它压过的候选,页面形态够不到的连同判定它的结果(从未匹配到元素、只在短暂标记下生效、第三方应答、伪元素读不到计算值)。样式表构建核对这份台账:产物里任何一处 `!important` 不在台账里就直接构建失败,并指出是哪些声明。
48
+ - **检查用的边界集中到一张表,每条带理由**:跟随末尾允许落后多少、长会话里一帧最长多少、长时间任务一共允许多少,这些「量出来的读数」的上限此前写在各自模块的常量里,现在住在 `packages/testing/bounds.json`,检查经 `packages/testing/bounds.cjs` 读它。放宽一个边界因此是这一个文件里的一行,理由就在旁边;余下的边界随迁移继续搬进来。
49
+ - **其余十个读数上限也搬进边界表**:门闩滚动与落定的等待、门户两侧取样的帧数、跟随末尾的贴齐余量、行与栏是否成一条线的容差、动效落定的等待、远端页面跟随本机改动的时限、设置快捷方式与设置对话框的等待、状态行换词的间隔——这些常量原来各自写在场景模块里,现在住在 `packages/testing/bounds.json`,理由照抄它们原本的注释,场景经 `packages/testing/bounds.cjs` 读。
50
+ - **宿主那张菜单卡片的查找收进一处**:账户菜单每趟刷新都要问「宿主开着的那张菜单在哪」,此前它把页面上 `[role="menu"]` 全查一遍再逐个挑出不是本插件的那张。现在这条查询由宿主访问模块记住答案(菜单是节点来了又走,答案只在页面结构变化后重查),登记表里这一条欠账随之划掉。英雄首页那一处同样的查询留给下一轮改用同一处访问器。
51
+ - **英雄首页那张选择器卡的触发点也记住答案**:输入卡片上那个工作区触发点与首页那一行是同一个选择器的两种入口,每趟刷新都要在整份文档里找一遍;现在由一处记住的查找(结构变化才重查)供着。登记表里这一条从三处降到两处,剩下的两处要的是活清单而不是单个元素,理由写在登记里。
52
+ - **首页那张宿主菜单卡也走同一条查找**:英雄首页把卡片落位前要问「宿主开着的那张菜单卡在哪」,此前它自己在整份文档里查 `[role="menu"]` 并挑出不是本插件的那张;现在改用宿主访问模块里记住答案的那一处(打开一张菜单会把外壳里别的菜单折起,所以开着的宿主卡片就是这一张)。登记表里这条从两处降到一处——剩下那一处要的是活清单(首页那一行可能同时开着多个选择器)。
53
+ - **两份决定草稿的现状跟上已落地的部分**:整份文档的查找记账现存四十三处,`importance` 台账覆盖全部五百三十九处 `!important` 且构建要求每一处都在册,边界表已有十四条由 `packages/testing/bounds.cjs` 供各场景读取,检查表达式里的其余数字由 `check-numbers.json` 记录并核对。
54
+ - **侧栏分区的标题改由总线登记表供着**:工作区视图每趟刷新都要在整份文档里查一遍分区标题,再挑出带列表区的那个;现在标题清单由观察总线按选择器登记、随页面变化增量维护(D40),每趟刷新只在清单里挑一次,标题与布局都不变时不再走文档。登记表里这一条欠账划掉,现存四十二处。
55
+ - **登记一处查不到的原因,免得下一轮重走**:首页那一行「开着的选择器」看的是宿主写在常驻节点上的 `aria-expanded`,而观察总线的登记表跟的是节点增删——改用它会把已经关掉的选择器继续算作开着(冒烟里八条检查当场这么判)。这一处的理由因此改写清楚:要的是「属性一变就作废」的答案,得由它自己的读取或当趟已经拿着的行来给。
56
+ - **对手分类按走查自己的口径补齐两处,并记下试过的死路**:判定为需要的依据里包含「去掉重要性之后有过渡开始」,分类现在也把这一条算作「这个候选正托着值」;同一条规则里与简写并排写的长属性(`border` 旁边的 `border-color`)此前被整条跳过,现在也是候选。两处补齐没有改变本次的归属数(仍是十六处里三处),台账的说明把试过的三条死路一并写下:过渡、同规则长属性、以及只看第一个匹配元素。
57
+ - **冒烟的静置判定补上「没有还在跑的有限动画」**:此前只等「连续若干帧没有新的一趟」加「一段时间没有新的趟」,进场、换词、淡入这类动画还在飞的时候读数照样被采走。现在两者之外再要求页面上没有仍在运行的有限动画(无限循环的那种不算:正在进行的会话灯与扫光本来就不落地)。整条冒烟 50/50 通过,耗时未变(93.7 秒)。
58
+ - **外观层与它的闸门落地**:冒烟走查的每个页面状态现在按部件记一份计算值快照(只收与字体无关的属性:颜色、边框、圆角、阴影、层叠与溢出),第一版覆盖五十个页面状态、十二个部件;快照进版本管理,`npm run verify` 的 `appearance` 闸门把这次走查读到的值与它逐项比对。`scripts/ci-scope.mjs` 增加一条拒绝:`packages/testing/appearance.json` 变了而 `packages/client/src` 没变的推送不通过——快照是页面的样子,只随画它的样式表移动;云端两个工作流都接上了这条。顺带把「工作树改了哪些路径」的读取收进 `scripts/shared/changed-paths.mjs`,此前它有两份。
59
+ - **方向记录改成一条方向一页**:每条方向只写它回答的背景、决定、由此而来的后果与重审条件,文件路径、函数名、数值与计数由代码注释、`ARCHITECTURE.md` 与测试承担,因此一次不改变方向的代码重构不再让方向记录过时;`docs/adr/README.md` 与作废编号的清册由 `npm run docs:index` 生成,编号作废之后不再复用。
60
+
61
+ <h3 id="en-0.16.0">Improvements</h3>
62
+
63
+ - **The usage record shrinks to about two fifths of its size**: a day is stored per route (a model, plus the remainder no model claimed), the day's own total is the sum of those routes, the hour histogram keeps its non-zero slots alone, and a session whose log is gone no longer carries a fingerprint. Measured on this machine's own record: 146 KB becomes 57 KB, and a pass pays for parsing and writing alone; a pass that matches the record writes nothing.
64
+ - **Fewer whole-document lookups per frame**: a lookup that found nothing used to walk the whole document again on every pass, while on most pages the seat it asks for is simply absent — a conversation page carries no hero root, and the settings navigation column does not exist until the dialog opens. That "nothing matched" answer now stands until the page's structure changes; the settings navigation cell, the sidebar's archived-list sweep and the account card's sweep are scoped to the sidebar they live in. Measured over one turn on a page of 28568 nodes: the skin's whole-document lookups fall from 584 to 395, and its hero-root lookups from 273 to 26.
65
+ - **The pass that takes the state lines down no longer grows with the square of the conversation**: when the host hands back several rows at once, the skin takes the status line off every row that still stands, and each standing line used to search the running-row list from the start. That list is a set now, so the pass walks it once per line.
66
+ - **The scroll motion assertions no longer move with the machine**: the follow's catch-up and the stream glide each took a distance reading at a few clock points, and those readings differ on a busy machine; the probe now records the distance frame by frame from the frame the walk starts, and the checks ask that recording's invariants instead — the run starts far off the end, every frame closes on it, at least two different values lie between the two ends, the last frame is at the end, and the frames it ended on all still read the end. The thresholds and frame counts live in `packages/testing/bounds.json`, and `scripts/lint.mjs` gained a rule: every bound in the table carries a name, a finite value and a reason, a name a check reads has to be in the table, and an entry nothing reads comes out.
67
+ - **The account footer no longer wins with `!important`**: the 79 `!important` declarations in `packages/client/src/features/account/account-footer.css` are gone, and the host account row, the account menu, the avatar and skin canvas, and the drawer's rows and hover states are now carried by the selectors' own weight — every skin selector starts with `body[data-dsh-claude-style]` and outweighs the host's single-class rules. Those declarations sat on host account-menu elements, which the walk's page states never reach, so none had ever been judged. The `importance` scenario was walked again: every declaration decided, none superfluous, the sixteen still needed beating a host inline style, and the four account-menu selectors struck out of the unreachable registry. The account menu's rows and the home page's two menus now state their row style once, for both markers together (the two copies were identical and only the dropped weight made that visible); the home sheet keeps what is its own — the disabled state and the selected mark. The pages look the same: the appearance snapshot matches across all fifty page states and the account and home cases pass.
68
+ - **The footer takeover no longer wins with `!important` either**: the 43 `!important` declarations in `packages/client/src/features/account/footer-takeover.css` are gone — the footer containers collapsing to zero, the mirrored entries folding away, the account button and popover in the collapsed rail, and the settings entry hidden there are now carried by the selectors' own weight (this sheet loads after the base rules it stands over, and every selector names the rail, the footer and the row it means). The `importance` scenario was walked again: 422 declarations, every one decided, none superfluous, the sixteen still needed unchanged, and the ledger now records those 422. The appearance snapshot matches across all fifty page states.
69
+ - **The status line cuts a wording only where it no longer fits**: the clause the model's own reasoning hands the line, the description a command call declares, and the name of the file a call works on were each cut at a character cap (40, 40 and 32 characters), so on a wide window a reader still saw a sentence broken at the same count. All three are now carried whole, and how much of one shows is the line's own width: the host's content box had no width bound, so a long wording ran past the conversation column with the clock and the badge behind it; it now takes the width of the row it stands in, and the wording is clipped with an ellipsis inside its own box while the loader, the clock and the badge keep their places.
70
+ - **The status line's clause is no longer cut at a dot inside a name**: where a sentence ended used to be decided by a hand-written table of closing punctuation, so the dot in a file name, a version number or an abbreviation read as the end of one — the clause about reading `app.js` said "Reading app" and the one about `package.json` said "Checking package", and an abbreviation left a single letter behind. Where a sentence ends is now the page's own sentence rules, so a clause runs from the participle to the place the sentence really ends. The one or two words that may stand between an auxiliary and its participle (now, currently, still, just …) and the perfect continuous (I have been checking) are read too; those sentences used to hand the line nothing and it fell back to "Thinking…". A negated clause and a plan the model has not started are still passed over.
71
+ - **The status line's clause stands until the next intent arrives**: a model writing fast ends several sentences a second, and the stretches between its clauses — the reasoning reading as the bare thinking state, or a step that has written nothing yet — used to take the clause that had just been said off the line, so the reader saw a clause flash, fall back to "Thinking…", and flash again with the next one. The model's own clause now stands until the next intent arrives: a newer clause, a call's own words, or a state such as writing or running a tool. With the Precise status line switched off no clause is read and none of this applies. The one-second lock on a wording is unchanged, and it now rests on the wording rather than on the row: when the host builds a fresh row between steps, the wording that turn stood on and its lock travel with the turn instead of starting over.
72
+ - **The status line's and the process group header's sweep now use the host's own construction**: the highlight is a masked window and a tinted copy of the words moving in opposite directions, both translations the compositor plays, so no frame is repainted for it; the period, the steps, the 0.3 s start delay and the mask match the host's rows. A page with a turn running no longer has any paint-driven animation running.
73
+ - **The dark border is now a highlight**: the dark values of `--dsw-alias-border-l1`/`-l2`/`-l3` move from the opaque `#242320`/`#2e2c29`/`#3a3833` to one translucent white at `.09`/`.18`/`.30`, so the edge of a card, a panel, a code block or a small badge reads as a chamfer catching the top light instead of a contour that has to be re-picked for every fill under it; the three light rungs stay the warm opaque ladder.
74
+ - **A popover's edge stays its own border, with a dark falloff outside it in dark**: the skin's own popover cards, the host's account menu and the hero row's pickers no longer move the line into the shadow — it is the card's border as it always was (the border token in light, the dark ladder's highlight in dark), so the box the placement reads and the paint path the opening animation scales are the ones they have always had. Dark gains a 3px dark falloff outside it that separates the lit edge from the near-black canvas, and the drop tightens to `0 8px 24px` plus `0 2px 6px`, deepening in dark to `rgba(0,0,0,.65)` / `rgba(0,0,0,.45)`.
75
+ - **The split between the sidebar and the conversation becomes a hairline**: the line drops from 1px to 0.5px, its continuation through the Windows caption row with it, which lands on a single device pixel on a retina display and is the width the product itself states; the Markdown table frame and the trajectory chip already took 0.5px, and every other single-direction rule stays 1px.
76
+ - **The reader's message bubble takes a stated pair of fills**: under the Claude palette `--dsw-specific-bubble` no longer borrows the button-hover grey and states `#f0f0ef` in light and a 6% white lift in dark, a dark cell the skin left unset before, so the bubble no longer fell through to the host's own deep blue. The light half is not the product's own cool plate, which reads blue on this warm canvas, but the skin's existing neutral (the value the table header wears); the settings page's demonstration bubble reads the same token and moves with it.
77
+ - **The smooth follow while streaming is drawn by the graphics card**: while a reply streams, the host's own follow writes the position to the end and the skin no longer rewrites it frame by frame; it only slides the newly grown content up from the bottom edge, with the status line standing still. The host's follow stays on through the whole reply, so its back-to-end button no longer has to be hidden. Measured on this machine over the same fast stream: the newest content trails its resting place by a median of 26–28 px instead of 31 px and at most 62 px instead of 73 px, and layouts per 4 seconds fall from 49 to 44–45.
78
+ - **One separator line in a popover, decided in one place**: the host menu's own `[role="separator"]` and the separators the skin builds (the model picker's footer divider and its provider-header rule) are now the same 0.5px `--dsw-alias-border-l2`, drawn as a top border on a zero-height box rather than the background of a half-pixel box, so the line belongs to the layout instead of changing thickness with the card's opening scale. The general rule lives in the shared shell and covers the skin's own cards together with the two host menus it repaints — before it, only the account and hero menus each stated a 1px line — and the two menu footers drop from a 1px to a 0.5px top border.
79
+
80
+ ### Bug Fixes
81
+
82
+ - **Deleting a conversation no longer takes its usage with it**: the panel's own account was folded again from the stored logs on every pass, so a log deleted took that session's days out of the all-time figures. The local account now lives in the plugin's own directory, and a session whose log is gone keeps the days it settled on — the panel still gives those days' tokens, its model ranking and its hour histogram. The days an earlier build kept in its fold cache are migrated on the first read.
83
+ - **The usage record is no longer overwritten**: a record that does not parse is kept in the plugin's directory as `usage.json.corrupt-<stamp>` before it is rebuilt from the session logs; with several instances open under one `$DSH_HOME`, every write first merges the copy on disk, so a session another instance recorded is not lost; the old fold cache is deleted once the record is written.
84
+ - **Every plugin entry in the sidebar footer is collected into the account area**: the action slot's entries and a card another plugin mounted into the footer itself — the usage card is one — both stand in the account popover, the card keeping its own markup, and the footer keeps the account row alone; an entry's own inline style gives way with them; the importance ledger records 424 declarations with them.
85
+ - **A model id carrying its provider's namespace resolves its vendor lockup**: an id spelled `cn:hy3` draws the Hunyuan lockup on its row and gets its description line in the picker's first level. A rule's segment start now accepts any character outside a letter or a digit, and an id such as `shy-mt2` stays unclaimed.
86
+ - **The remaining models under that same segment start resolve their vendor lockup**: an id carrying its serving provider's namespace was unclaimed for the K3 and M3 lines too, and the o-series rule read only a slash or a hyphen before `o`; the K3, M3 and o-series rules now read the segment start the Hunyuan rule reads. The `nova` rules gain a segment boundary, so `sensenova-6.8-flash-lite` is no longer claimed as Amazon Nova and its lockup no longer lands in the middle of the name.
87
+ - **The sweep under the Claude brand is no longer blue**: the host retints the sweep on its running row to DeepSeek blue, so the status line's highlight was always blue; under the Claude brand the status line, the process group header and the host's own call rows now share one warm highlight (45% of `#faf9f5` in dark, 30% of `#141413` in light), and the DeepSeek brand keeps the host's colour.
88
+ - **Scrolling or clicking in the sidebar or a subagent transcript beside the conversation no longer stops the main conversation from following**: only a wheel, press or drag inside the main conversation's scroller, or a scroll key pressed with nothing focused, counts as the reader leaving the end; before, a wheel or click anywhere on the page left the main conversation where it was and the stream stopped being followed.
89
+ - **The status line no longer reads a quotation as the model's own work**: when the model quoted a sentence it had read, or discussed this line's own waiting word while thinking, the line carried that quotation and the rest of the sentence as the model's own doing. What stands inside a quotation (「」『』“” and the English double quote) now takes no part in reading a clause, and a clause does not end inside one: a reader never sees the model's quotation stated as its work, while a quoted part of a clause the model does state is still carried as written. The Chinese 「正在」 reading is withdrawn with it — it shares its shape with this line's own waiting word, and no local word list tells 「正在读取 app.js」 from a sentence about the line — so reasoning in Chinese stands on the waiting word again.
90
+
91
+ ### Chores
92
+
93
+ - **Rules asking whether one element holds another read one mark again**: sixteen rules used to ask the browser, through `:has()`, whether an element's subtree carried a part or a mark, and every change below one of them re-matched those rules. The pass that marks the parts now writes that answer as one attribute, and the rules read it; the two `:has()` entries into scope and the one following focus state remain. The build refuses a `:has()` that reads an attribute, holds every mark read to a declared relation, and checks the part names those relations name.
94
+ - **Whole-document lookups in a pass or a frame now read a kept answer**: the interface switch's band asked eight times a pass whether the other side's controls, the sidebar's collapse toggle and the window menu were on the page, the chat follow asked once a frame whether the host was still streaming, and the desktop header band asked once a pass where the window menu stood — seats most pages do not have at all, each question walking the whole document. Nine of them go (seven in the switch, one in the band, one in the follow): the host access module keeps those answers until the page's structure changes, and the follow reads its mark inside the message column, as its own other reading already did. The forty-six that remain are recorded one by one with the reason each stays, and the lint step refuses a new lookup as well as a record the code no longer matches.
95
+ - **The numbers a check accepts are a record the gates verify**: when a check failed, the cheapest answer was to loosen whichever number stood in its expression, and that edit left no trace. Every numeric literal a check in either lane decides on is now written into `packages/testing/check-numbers.json` (`npm run checks:numbers` writes it), and the gates and the cloud verify that record: change what a check accepts and the record moves with it, so a loosened bound is visible in the diff. The bounds that need a reason keep living in `packages/testing/bounds.json`, each with its `reason`, and the checks read them from there.
96
+ - **Every `!important` that stays now records what it beats**: the importance walk used to answer only whether the page still paints the same without it; it now writes each declaration it proved necessary into `packages/testing/importance-ledger.json` — the page state that decided it, and the candidates that take the property over once its importance is gone (the element's own inline style, the host's `!important`, a host rule of higher weight, another skin rule). The walk drops that one importance, reads the value the page falls back to over every longhand the property stands for, and takes each other candidate away in turn: the ones that move the value are what it beats. A declaration that appears or stops being needed fails the walk, and `DSH_IMPORTANCE_LEDGER=write` rewrites the record from a run so the difference is read in the diff. Three of this run's sixteen attributed an opponent; the rest are recorded as unattributed, the walk finding no candidate it can scan.
97
+ - **The build requires page evidence for every `!important`**: the importance walk now writes every declaration it read into `packages/testing/importance-ledger.json` — the ones it proved necessary together with what each beats, and the ones no page state could reach together with the verdict that says so (never matched an element, only in force under a transient mark, a third-party answer, an unreadable pseudo-element). The stylesheet build verifies that record: any `!important` in the output that is not in the ledger fails the build, naming the declarations.
98
+ - **The bounds a check compares against now live in one table, each with its reason**: how far behind the tail the scroller may lag, the longest frame a long session may take, how much long-task time a turn may spend — the limits on measured readings used to sit as constants in each module, and now live in `packages/testing/bounds.json`, read through `packages/testing/bounds.cjs`. Loosening one is a line in that file with its reason beside it; the remaining bounds move in as the migration continues.
99
+ - **The other ten reading limits moved into the bounds table**: the wait for a door to roll and land, the frames sampled on each side of the doors, the slack that still counts as pinned to the row, the tolerance for a control reading as on the column's line, the wait for motion to settle, how long a remote page may take to follow a local change, the waits for the settings shortcut and dialog, and the gap the state line's wording leaves — each was a constant in a scenario module and now lives in `packages/testing/bounds.json` with the reason its own comment gave, read through `packages/testing/bounds.cjs`.
100
+ - **The lookup for the host's open menu card moved into one place**: the account menu asked every pass where the host's open menu was, scanning every `[role="menu"]` on the page and picking the one that is not the skin's own. That lookup now goes through the host access module, which keeps the answer until the page's structure changes (a menu is a node arriving and leaving), and the ledger entry recording it as debt is struck out. The hero menu's own query of the same kind moves onto the same accessor next.
101
+ - **The hero picker's card trigger keeps its answer too**: the workspace trigger on the composer card and the hero row are two views of the same picker, and the pass looked it up across the whole document every time; a kept lookup (re-asked only when the page's structure changes) serves it now. The ledger entry for that file drops from three lookups to two, and the two that remain want a live list rather than one element — the reason is written in the record.
102
+ - **The hero page reads the host's open menu card through the same lookup**: before placing its own card the hero asked where the host's open menu was, scanning the whole document for `[role="menu"]` and picking the one that is not the skin's; it now uses the accessor that keeps that answer (opening one menu folds the shell's others, so the open host card is that one). The ledger entry for the file drops from two lookups to one — the one left wants a live list, since the hero row may hold more than one open picker.
103
+ - **Both decision drafts' standing sections follow what has landed**: the whole-document lookup record stands at forty-three sites, the `!important` ledger covers all five hundred and thirty-nine declarations with the build requiring each to be in it, the bounds table holds fourteen values read through `packages/testing/bounds.cjs`, and the remaining numbers in check expressions are recorded and verified in `check-numbers.json`.
104
+ - **The sidebar's section labels come from the bus registry now**: the workspace view looked up every section label across the whole document each pass and then picked the one carrying a list area; the labels are now kept by the observation bus per selector and maintained incrementally as the page changes (D40), so a pass picks from the list without walking the document, and the ledger entry recording it as debt is struck out — forty-two sites remain recorded.
105
+ - **Why one lookup cannot move is written down, so the next pass does not walk the same road**: the hero row's open pickers turn on the host's `aria-expanded` on nodes that stay mounted, while the bus's registry follows node additions — moving it there keeps counting a picker that has closed (eight smoke checks said exactly that). The reason in the record now says what this site needs instead: an answer invalidated by an attribute change, from a reader of its own or from the row the pass already holds.
106
+ - **The opponent classification now matches the walk's own criteria in two places, and the dead ends are written down**: a declaration counts as necessary when dropping its importance starts a transition, and the classification counts that as the candidate holding the value too; a longhand written beside a shorthand in the very rule under test (`border-color` next to `border`) used to be skipped with the whole rule and is a candidate now. Neither changed today's tally (three of sixteen), and the ledger's note records the three dead ends tried: transitions, same-rule longhands, and reading only the first matching element.
107
+ - **The smoke walk's settle rule now requires that no finite animation is still running**: it used to wait only for a run of frames without a new pass plus a stretch of wall clock without one, so a reading could be taken while an entrance, a wording swap or a fade was still in flight. It now also requires that every finite animation the page runs has finished — an endless one is no such thing, since the running session's pulse and the sweeps never land. The whole smoke walk passes, 50 of 50, in the same time (93.7 s).
108
+ - **The appearance layer and its gates land**: every page state of the smoke walk now records what its parts compute to (font-independent properties only — colours, borders, radii, shadows, layering and overflow), the first version covering fifty page states and twelve parts; the snapshot is committed, and `npm run verify`'s `appearance` gate compares the walk's own reading against it item by item. `scripts/ci-scope.mjs` gained one refusal: a push where `packages/testing/appearance.json` changed and nothing under `packages/client/src` did — the snapshot is what the pages look like and only moves with the sheets that draw them; both cloud workflows carry the check. The reading of which paths the working tree changed moved into `scripts/shared/changed-paths.mjs`, which had two copies of it.
109
+ - **A direction is one page**: each record carries the background it answers, the decision, what follows from it and what would make it worth revisiting, while file paths, function names, values and counts live in the code comments, ARCHITECTURE.md and the tests, so a refactor that keeps the direction leaves no record stale; `npm run docs:index` writes `docs/adr/README.md` and the register of void numbers, and a void number is never reused.
110
+
7
111
  ## [0.15.0] - 2026-10-15
8
112
 
9
113
  [中文](#cn-0.15.0) | [English](#en-0.15.0)
@@ -167,7 +271,7 @@ All notable changes to `dsh-claude-style` are documented here, newest first.
167
271
  - 「侧栏」页多出「封号彩蛋」一行,选「关闭」后账号行只显示名字,设置项 `banLocale` 多了取值 `off`。
168
272
  - 和 [ds-bot](https://github.com/Fei2411/DeepSeek-Bot) 同时安装时,侧栏顶端多出一个聊天 / 代码切换:聊天是 ds-bot 的 Bot 界面、本主题整个让开,代码是宿主自己的界面、由本主题绘制;没装 ds-bot 时页面上没有它。
169
273
  - 「对话」页新增「状态行描述」(设置项 `statusDetail`,取值 `precise`(默认)与 `simple`),「精确」让命令调用说出模型为自己写下的描述。
170
- - 运行行现在说出具体的文件名(超过 32 字截断),推理里出现英文进行时从句时写出模型自己那句话。
274
+ - 运行行现在说出具体的文件名,推理里出现英文进行时从句时写出模型自己那句话。
171
275
  - 设置页多出「动效」分页,其中「状态行动画」用两张实时预览的卡片代替文字,一档是现在这道扫光,另一档让状态行的文字本身流动着变色;设置项 `statusMotion` 的取值为 `sweep`(默认)与 `wash`。
172
276
 
173
277
  ### 体验优化
@@ -246,7 +350,7 @@ All notable changes to `dsh-claude-style` are documented here, newest first.
246
350
  - The Sidebar tab gains an Account-hold easter egg row, with Off the account row is a plain name, and the `banLocale` setting gains the value `off`.
247
351
  - With [ds-bot](https://github.com/Fei2411/DeepSeek-Bot) installed too, the top of the sidebar carries a Chat / Code switch: Chat is ds-bot's Bot surface with the theme standing aside, Code is the host's own interface painted by the theme, and without ds-bot the page carries none.
248
352
  - The Conversation tab gains Waiting-line detail (setting `statusDetail`, `precise` (the default) or `simple`), where Precise has a command call say the description the model declared for it.
249
- - The running line names the file a call works on, cut past 32 characters, and writes the model's own words when the reasoning carries an English present-continuous clause.
353
+ - The running line names the file a call works on and writes the model's own words when the reasoning carries an English present-continuous clause.
250
354
  - The settings page gains a Motion tab whose Waiting-line animation setting picks the paint with two live previews instead of text — the highlight band the line sweeps today, or a colour wash that moves through the letters; the `statusMotion` setting is `sweep` (the default) or `wash`.
251
355
 
252
356
  ### Improvements
package/README.en.md CHANGED
@@ -111,7 +111,7 @@ Keep only one theme enabled at a time. dsh ≥ 0.1.7 is required, and a restart
111
111
  | Document | Contents |
112
112
  | --- | --- |
113
113
  | [Design tokens](docs/STYLE.md) | The palette, typography, shape, motion and layer rules, with three tables generated from the token table (in English) |
114
- | [Architecture decisions](docs/decisions/README.md) | One file per decision: build and source, the host boundary, the runtime and each feature's trade-offs, and the architecture migration in progress (in Chinese) |
114
+ | [Architecture decisions](docs/adr/README.md) | One page per direction: the background it answers, the decision, what follows from it, and what would make it worth revisiting (in Chinese) |
115
115
  | [Changelog](CHANGELOG.md) | Version history |
116
116
  | [Contributing](CONTRIBUTING.md) | Building from `packages/client/src/`, commit conventions, and the screenshot and regression tooling (in English) |
117
117
 
package/README.md CHANGED
@@ -99,7 +99,7 @@ Anthropic 字体启用(二选一):
99
99
  | 文档 | 说明 |
100
100
  | --- | --- |
101
101
  | [设计令牌](docs/STYLE.md) | 配色、字体、形状、动效与层级的规则,以及由令牌表生成的三张对照表(英文) |
102
- | [架构决策](docs/decisions/README.md) | 每条决策一个文件:构建与源码、宿主边界、运行时与各个功能的取舍,以及正在进行的架构迁移 |
102
+ | [架构决策](docs/adr/README.md) | 每条方向一页:它回答的背景、决定、由此而来的后果与重审条件 |
103
103
  | [更新日志](CHANGELOG.md) | 版本历史 |
104
104
  | [贡献指南](CONTRIBUTING.md) | 如何从 `packages/client/src/` 构建、提交规范与截图/回归工具(英文) |
105
105
  | [功能一览](docs/FEATURES.md) | 每一项功能做什么、在设置页哪里开关 |
@@ -66,13 +66,13 @@ SOFTWARE.
66
66
 
67
67
  The Number pop-in transition (`packages/client/src/features/context-stats/stats-digits.ts` and the `dsh-claude-digit-*` rules in `packages/client/src/features/composer/inline-bar.css`) is ported from transitions.dev's "Number pop-in": its keyframes, its distance, blur, stagger and easing tokens, and one span per character with the last two delayed behind the rest — renamed into this package's own classes and tokens.
68
68
 
69
- The "Text states swap" transition (`packages/client/src/features/chat-wait/chat-wait.ts` and the `dsh-claude-chat-wait-label` rules in its stylesheet, and the operation label of a shared file row in `packages/client/src/features/chat-files/` with the `dsh-claude-file-op-*` rules in its stylesheet) is ported the same way: its three-phase sequence (leave upward with a blur, change the text out of sight, enter from below), its duration, shift, blur and easing tokens as this package's own custom properties, and the reflow that releases the entry pose. The snippet's `prefers-reduced-motion` guard is answered by the animation choice this skin resolves onto `<body>` (D26), which already folds the system setting in.
69
+ The "Text states swap" transition (`packages/client/src/features/chat-wait/chat-wait.ts` and the `dsh-claude-chat-wait-label` rules in its stylesheet, and the operation label of a shared file row in `packages/client/src/features/chat-files/` with the `dsh-claude-file-op-*` rules in its stylesheet) is ported the same way: its three-phase sequence (leave upward with a blur, change the text out of sight, enter from below), its duration, shift, blur and easing tokens as this package's own custom properties, and the reflow that releases the entry pose. The snippet's `prefers-reduced-motion` guard is answered by the animation choice this skin resolves onto `<body>` (D40), which already folds the system setting in.
70
70
 
71
71
  The "Reasoning stream" transition (`packages/client/src/features/chat-fold/reasoning-stream.ts` and the `data-dsh-claude-reason-window` rules in that feature's `fold-motion.css`) is ported as well: its window (a few lines tall with a mask at each edge rather than painted gradients), its hold, step, lines, fade and easing tokens, and the stepping clock — read as live text, so there is no copy and no wrap and the steps stop at the newest line.
72
72
 
73
73
  Seven more are ported the same way, each into this package's own classes, attributes and motion tokens, with the snippet's `prefers-reduced-motion` guard answered by the skin's animation choice: "Menu dropdown" (the popover cards in `packages/client/src/shared/popover.css` and the `dsh-claude-dropdown-in` preset in `packages/client/src/shared/motion.css` — its pre-open and closing scales and its curve, the closing state carried by the separate `scale` property instead of a class), "Modal open / close" (the account-hold page, `packages/client/src/features/ban-screen/`), "Page side-by-side" (the settings tabs, `packages/client/src/features/settings/` — the page leaving is a still copy of its rows), "Notification badge" (the waiting line's overtime badge, `packages/client/src/features/chat-wait/` — the slide and the pop as `translate` and `scale` on the one pill), "Skeleton loader and reveal" (the home usage panel, `packages/client/src/features/home/` — the placeholder and the figure being one element, its fill fades while the figure comes out of the blur), "Texts reveal" (the home greeting, `packages/client/src/theme/hero.css` — the brand mark and the greeting as its two lines) and "Toggle" (the settings switches — its double-bounce keyframes, armed by the first press).
74
74
 
75
- The colour wash the waiting line can wear instead of the band (`packages/client/src/shared/motion.css`, the `dsh-claude-sweep` and `dsh-claude-wash` presets in that sheet and the `--dsh-claude-wash-*` custom properties) is the paint transitions.dev serves on its own pages as "Pro gradient text" (`.title-pro` on <https://transitions.dev/agent.html>): its seven radial washes over a grey base ramp as inline SVG, its 180% layer sizes, and its orbit and hue-rotation periods, renamed into this package's own custom properties and motion tokens, with the `prefers-reduced-motion` guard answered by the skin's animation choice (D26) as everywhere else.
75
+ The colour wash the waiting line can wear instead of the band (`packages/client/src/shared/motion.css`, the `dsh-claude-sweep` and `dsh-claude-wash` presets in that sheet and the `--dsh-claude-wash-*` custom properties) is the paint transitions.dev serves on its own pages as "Pro gradient text" (`.title-pro` on <https://transitions.dev/agent.html>): its seven radial washes over a grey base ramp as inline SVG, its 180% layer sizes, and its orbit and hue-rotation periods, renamed into this package's own custom properties and motion tokens, with the `prefers-reduced-motion` guard answered by the skin's animation choice (D40) as everywhere else.
76
76
 
77
77
  Source: <https://transitions.dev/detail.html?t=number-pop-in> · <https://transitions.dev/detail.html?t=text-states-swap> · <https://transitions.dev/detail.html?t=reasoning-stream> · <https://transitions.dev/detail.html?t=menu-dropdown> · <https://transitions.dev/detail.html?t=modal-open-close> · <https://transitions.dev/detail.html?t=page-side-by-side> · <https://transitions.dev/detail.html?t=notification-badge> · <https://transitions.dev/detail.html?t=skeleton-loader-and-reveal> · <https://transitions.dev/detail.html?t=texts-reveal> · <https://transitions.dev/detail.html?t=toggle> · <https://transitions.dev/detail.html?t=pro-gradient-text> · <https://github.com/Jakubantalik/transitions.dev>
78
78
 
@@ -22,9 +22,9 @@ What each feature does and where its switch lives; the six tabs, their rows and
22
22
  - **In progress / Archived**: The sidebar's workspace section splits into In progress and Archived; Archived is one flat list whose rows can be restored or deleted.
23
23
  - **Sidebar search**: A search box in the sidebar opens a palette that finds sessions (titles and content), projects, plugins, Skills and shortcuts, through the host's own dialog and navigation; picking a content hit lands on the turn holding that message rather than at the top of the session. Search box style places it: Cover the brand (the default) keeps it in the brand row, standing over the brand while the pointer is over the sidebar; Standalone gives it a row of its own under the brand row, shown always; Icon leaves the host's own search button in place and takes only its click over.
24
24
  - **Conversation navigator**: The column of short marks at the conversation's right edge holds one per turn and follows where you are reading. Reach it with the pointer and it opens into a list: each row carries the message that started its turn and sits exactly where that turn's mark was — the turn you are reading stays in place, and the turn under the pointer is still under it. The wheel scrolls the list and a click glides the page to the turn, loading earlier history first when it is not loaded yet; the Chat / Trajectory tabs at the top of the conversation stay up while the pointer is on the list. Alt+↑ / Alt+↓ jump to the previous or the next turn, and held or repeated they keep stepping; while the composer holds a draft the keys stay with it. The turn you land on flashes a short line over its start. With [dsh-plugin-msg-nav](https://github.com/SherUnlocked-4869/dsh-plugin-msg-nav) installed too, Alt+↑ / Alt+↓ stay with that plugin.
25
- - **Chat-area follow**: At the structural moments — a thinking row folding, a tool call row arriving — a reader sitting at the bottom is handed back to the host's own follow, instead of being left tens of pixels short by the burst of content; a process group capped in the Standard and Compact tiers (thinking and tool output kept in one scrolling body) is caught up the same way. Catching up runs on a curve: a trickle of characters settles softly and a burst glides at one steady speed before landing. While content streams, the main scroller's own follow is walked along that curve too rather than written to the end in a single frame — with a fast stream the newest lines trail just below the fold and slide into place once it stops. A message you send does not go through it: the host's scroll to bring it into view lands at once. Once the reader scrolls away from the bottom himself, the plugin stays out of it until he returns.
25
+ - **Chat-area follow**: At the structural moments — a thinking row folding, a tool call row arriving — a reader sitting at the bottom is handed back to the host's own follow, instead of being left tens of pixels short by the burst of content; a process group capped in the Standard and Compact tiers (thinking and tool output kept in one scrolling body) is caught up the same way. Catching up runs on a curve: a trickle of characters settles softly and a burst glides at one steady speed before landing. While content streams, the host's own follow keeps the position at the end and the newly grown content slides up from the bottom edge instead of the text above it jumping in a single frame, with the status line standing still; the slide runs on the graphics card and takes no layout, so several sessions streaming at once cost no more for it. A message you send does not go through it: the host's scroll to bring it into view lands at once. Once the reader scrolls away from the bottom himself, the plugin stays out of it until he returns.
26
26
  - **Automatic folding and the rolling door**: A thinking row opens while the model reasons and folds back when it stops; a running process group opens and folds back once that piece of work ends. A row or group the reader pressed himself keeps what he chose for that phase. The open reasoning stands in a window that grows with it: the window is as tall as the text until it reaches the height reasoning may take, and there it stays while the text steps up two lines at a steady beat behind a softened edge at each side, stopping at the newest line; once the reasoning stops the window comes off and the reader who opens the row reads it whole, and with Animation set to Reduced the window is never set. A process group's header says what the group holds — "2 thoughts, 1 tool call", empty figures left out — each number rolling as the work grows, and the whole line sweeps the way the host's words do while that piece of work runs; a header without the room for the sentence falls back to `Thinking×2 · Tools×1` and takes the sentence back when the room returns, with the sentence as the control's name either way. Opening or closing a row (a tool card, a thinking row, a command card) or a process group moves the height frame by frame, really pushing the content below away or pulling it back. The door only rolls the stretch the reader can see, so any length moves at the same speed, and a body holding several cards rolls as one door.
27
- - **The line saying what the model is doing**: While a turn runs, the line at the end of the conversation is no longer a fixed "Deep diving for N s ···": it names what that turn is doing now — "Thinking…", "Preparing a tool call…", "Running tools…", "Writing…" — with the time the turn has taken beside it in the host's own units. Three of those states say something more: a call in flight names the file it works on ("Reading app.js", "Editing notes.md", "Writing out.txt" — the file's own name, cut past 32 characters); a command call declares a description in its arguments (the host requires one saying what the command does), and the line writes it as the model wrote it ("Regenerate the changelog": the first line, cut past 40 characters); and reasoning carrying an English present-continuous clause hands the line the model's own words ("Checking the file is true": from the participle to that clause's end, cut past 40 characters, a negated clause and reasoning in another language passed over, and "Thinking…" where there is none). The Conversation tab's Waiting-line detail choice set to Simple reads none of these specifics and names the bare states alone; Precise (the default) reads them as written. The line follows the host's own publishes, so a call is read the moment it starts running; every wording written is locked for a second, and the pass that finds the lock run out writes the newest state of that moment — whatever came and went inside the second is passed over, so a turn that moves quickly still changes the line at most once a second. A call's own words are the one thing the lock does not pass over: a small file read inside the second is written as soon as the lock ends, ahead of the state of that moment. A wording that changes runs transitions.dev's "Text states swap": the old one blurs and fades upward, the new one rises from below, and the box takes the arriving wording's width in one move, so the clock beside it travels to its new place rather than jumping twice. Where no state can be read at all the line stands on that brand's own word — the same one it says while the model is thinking: the skin's own "Thinking…" under the Claude brand, the host's own "Deep diving" under the DeepSeek brand — and once the model has been silent for ten seconds "No response yet" appears beside the clock, sliding onto the line from below and to the left while it pops out of nothing, and shrinking away as soon as the model speaks. The line sweeps — the Motion tab can give it a colour wash that moves through the letters instead — and the host's whale, which is DeepSeek's own character, gives way under the Claude brand to the skin's own loader: three 4px dots in the accent in force, which bounce the way the snippet has them while the model thinks (each rising 8px and flattening as it leaves the floor, half a second a bounce with the second dot starting two fifths of the period behind the first and the third three fifths) and take a second motion once the turn writes or works — each dot walking its own line, three lines 120° apart with one of them run the other way, the dot largest at either end and smallest as it crosses the middle, 1.25s a round trip with as long spent going out as coming back; the two motions never cut: the lane leaving fades each of its dots out where its own motion next turns back — off the floor for a bounce, off a far end for a walk — and the lane arriving fades each of its dots in where that motion next carries it, each fade over one of its own legs, so no dot is ever moved to a pose its motion does not carry; a motion stands for 2s before the next may take it over, so a state that comes and goes inside that stretch leaves it standing, while under the other brands the host's whale tail stands; the host's screen-reader announcement stays either way; wording wider than the room this line has is cut with an ellipsis while the clock and the badge keep their places; with Animation set to Reduced the wording is written at once, with none of those three phases. As long as the conversation follows its tail the line rests above the composer, so content arriving below no longer drags it around; scrolling back yourself leaves it travelling with the content. When a plugin hides the host wording and provides visible replacement text in the same content container, the theme yields its waiting line and restores it when the takeover ends; other chat animations keep running.
27
+ - **The line saying what the model is doing**: While a turn runs, the line at the end of the conversation is no longer a fixed "Deep diving for N s ···": it names what that turn is doing now — "Thinking…", "Preparing a tool call…", "Running tools…", "Writing…" — with the time the turn has taken beside it in the host's own units. Three of those states say something more: a call in flight names the file it works on ("Reading app.js", "Editing notes.md", "Writing out.txt" — the file's own name); a command call declares a description in its arguments (the host requires one saying what the command does), and the line writes it as the model wrote it ("Regenerate the changelog": the first line); and reasoning carrying a continuous clause hands the line the model's own words ("Checking the file is true": from the participle to that clause's end, with where a sentence ends left to the page's own sentence rules, so a dot inside a file name or a version number does not cut it; a negated clause, a plan the model has not started, anything inside a quotation and reasoning carrying no clause are passed over — a model quoting a sentence it read, the line's own waiting word among them, is not saying what it is doing; and "Thinking…" where there is none). All three are carried whole: how much of one shows is the width this line itself has, and past that the words are clipped with an ellipsis while the clock and the badge keep their places. The Conversation tab's Waiting-line detail choice set to Simple reads none of these specifics and names the bare states alone; Precise (the default) reads them as written. The line follows the host's own publishes, so a call is read the moment it starts running; every wording written is locked for a second, and the pass that finds the lock run out writes the newest state of that moment — whatever came and went inside the second is passed over, so a turn that moves quickly still changes the line at most once a second. A call's own words are the one thing the lock does not pass over: a small file read inside the second is written as soon as the lock ends, ahead of the state of that moment. The model's own clause has a rule of its own: the stretches between two of them, where the reasoning reads as the bare thinking state or as nothing at all, do not take it off the line — it stands until the next intent arrives, whether that is a newer clause, a call's own words, or a state such as writing; with the Precise status line switched off no clause is read at all, so nothing stands. A wording that changes runs transitions.dev's "Text states swap": the old one blurs and fades upward, the new one rises from below, and the box takes the arriving wording's width in one move, so the clock beside it travels to its new place rather than jumping twice. Where no state can be read at all the line stands on that brand's own word — the same one it says while the model is thinking: the skin's own "Thinking…" under the Claude brand, the host's own "Deep diving" under the DeepSeek brand — and once the model has been silent for ten seconds "No response yet" appears beside the clock, sliding onto the line from below and to the left while it pops out of nothing, and shrinking away as soon as the model speaks. The line sweeps — the Motion tab can give it a colour wash that moves through the letters instead — and the host's whale, which is DeepSeek's own character, gives way under the Claude brand to the skin's own loader: three 4px dots in the accent in force, which bounce the way the snippet has them while the model thinks (each rising 8px and flattening as it leaves the floor, half a second a bounce with the second dot starting two fifths of the period behind the first and the third three fifths) and take a second motion once the turn writes or works — each dot walking its own line, three lines 120° apart with one of them run the other way, the dot largest at either end and smallest as it crosses the middle, 1.25s a round trip with as long spent going out as coming back; the two motions never cut: the lane leaving fades each of its dots out where its own motion next turns back — off the floor for a bounce, off a far end for a walk — and the lane arriving fades each of its dots in where that motion next carries it, each fade over one of its own legs, so no dot is ever moved to a pose its motion does not carry; a motion stands for 2s before the next may take it over, so a state that comes and goes inside that stretch leaves it standing, while under the other brands the host's whale tail stands; the host's screen-reader announcement stays either way; wording wider than the room this line has is cut with an ellipsis while the clock and the badge keep their places; with Animation set to Reduced the wording is written at once, with none of those three phases. As long as the conversation follows its tail the line rests above the composer, so content arriving below no longer drags it around; scrolling back yourself leaves it travelling with the content. When a plugin hides the host wording and provides visible replacement text in the same content container, the theme yields its waiting line and restores it when the takeover ends; other chat animations keep running.
28
28
  - **New text fades in**: Characters arriving in a streaming answer start faint and settle, staggered slightly by arrival order; a block arriving whole, a burst of thousands of characters and text that just reflowed from a fold stay solid. The Motion tab offers Fade in and Appears at once, each with a preview card, shipped on Appears at once.
29
29
  - **File operation rows**: A read, edit or write of a file carries the `+n -m` tail and an expandable diff card or read window, and its path opens the file; the calls on one file inside one process group stand as a single row whose icon and title move on with the file's last operation and whose tail is their change total, turning character by character as each change lands, and opening that row reads every change — or the window it was last read at when nothing changed; a failed or interrupted row keeps its verdict.
30
30
  - **Send flight**: On submission the composer card lifts and narrows into the bubble as it travels, the flying copy carrying the bubble's colour from its first frame and its words re-flowing into the shape; it meets the message row, where the real bubble shows underneath and the flying copy fades out, so the words sharpen into place.
package/docs/FEATURES.md CHANGED
@@ -22,9 +22,9 @@
22
22
  - **进行中 / 已归档**:侧栏的工作区分成「进行中」与「已归档」两档;已归档是一张平铺的列表,每一行可以恢复或删除。
23
23
  - **侧栏搜索**:侧栏里有一个搜索框,打开的面板能找会话(标题与内容)、项目、插件、Skill 与快捷键,用宿主自己的对话框与导航;点开一条内容命中会落到那条消息所在的一轮,而不只是会话开头。「搜索框样式」选它的位置:覆盖品牌(默认)把它放在品牌行、指针移到侧栏时盖过品牌,独立在品牌行下方给它自己一行并一直显示,图标则保留宿主自己的搜索按钮、只把单击接管过来。
24
24
  - **对话导航**:对话区右侧那列短横线每一轮一条,跟着你读到的位置走。鼠标碰到它,它就展开成一张列表:每一行写着开启这一轮的那条消息,正好落在它那条短横线原来的位置上——你正在读的那一轮还在原处,鼠标下面还是刚才那一轮;滚轮上下翻,点一行页面滑到那一轮,还没加载的早期对话先加载再跳;指针停在列表上时,对话区顶部的对话 / 轨迹标签照常显示。Alt+↑ / Alt+↓ 跳到上一轮或下一轮,连按会一轮一轮接着走;输入框里有草稿时这两个键留给输入框。跳到的那一轮开头会闪一条短横线。同时装着 [dsh-plugin-msg-nav](https://github.com/SherUnlocked-4869/dsh-plugin-msg-nav) 时,Alt+↑ / Alt+↓ 留给它。
25
- - **聊天区跟随**:思考行收起、工具调用行出现这些结构时刻,贴着底部的读者被交还给系统自己的跟随,内容成片到达时不再停在离底部几十像素的地方;「标准」与「简洁」档里封顶的过程组(思考与工具输出收在一个带滚动条的组体里)同样补到底部。补到底部走的是曲线:零散到达的字收得住尾巴,成片涌进的文字以一段稳速滑过去,再落到末尾。流式输出期间主滚动条的新内容也沿这条曲线推进,不再一帧写到底;输出很快时最新几行会短暂拖在屏幕下缘之外,流一停就滑到位。你自己发出的消息不走这条曲线:宿主把它滚进视野的那一下照旧一步到位。读者自己滚动离开底部之后,插件不再插手,直到他自己回到底部。
25
+ - **聊天区跟随**:思考行收起、工具调用行出现这些结构时刻,贴着底部的读者被交还给系统自己的跟随,内容成片到达时不再停在离底部几十像素的地方;「标准」与「简洁」档里封顶的过程组(思考与工具输出收在一个带滚动条的组体里)同样补到底部。补到底部走的是曲线:零散到达的字收得住尾巴,成片涌进的文字以一段稳速滑过去,再落到末尾。流式输出期间位置由宿主自己的跟随写到末尾,新长出的内容从屏幕下缘平滑滑上来,上方文字不再一帧跳上去,状态行停在原处不动;这段滑动由显卡完成,不占用页面的布局,多个会话同时输出时也不加重负担。你自己发出的消息不走这条曲线:宿主把它滚进视野的那一下照旧一步到位。读者自己滚动离开底部之后,插件不再插手,直到他自己回到底部。
26
26
  - **自动开合与卷帘门**:模型还在思考时思考行开着,思考停下就收回去;运行中的过程组自动展开,这一段结束再收起。读者自己按过的行或组在当时的阶段里不再被改动。开着的那段思考停在一个跟着它长高的窗口里:窗口先是和文本一样高,长到推理能到的最大高度就停在那里,只有到了这一刻文字才一档一档往上走两行,走到最新一行就停住,上下边缘渐隐,也不再往下推整列对话;推理一停,窗口撤下,读者打开这一行读到的是完整文本,「动画」为「减弱」时不设这个窗口。过程组的标题改写成这一组装了什么:「执行 2 次思考,调用 1 次工具」,没有的项不写,数字随内容增加逐位滚动,这一段还在进行时整行带着宿主那样的扫光;放不下整句时改用「思考×2 · 工具×1」,位置宽回来再换回整句,控件的可访问名字始终是整句。点开或收起一行(工具卡片、思考行、命令卡片)以及过程组时,高度逐帧变化,下方内容被真的推开或收回;门只走读者看得见的那一段,内容再长也是同一速度,多张卡片的展开体整扇门一起走。
27
- - **模型正在做什么的那一行**:轮次进行中,对话末尾那一行不再是固定的「深度求索中,用时 N 秒 ···」,而是写出这一轮此刻在做的事:「正在思考」「正在准备工具调用」「正在调用工具」「正在写回答」,后面跟着这一轮已用的时间(用宿主自己的单位)。三种状态说的是具体的东西:调用在跑时写出它操作的文件(「正在读取 app.js」「正在编辑 notes.md」「正在写入 out.txt」,只取文件名,名字过长截到 32 字);命令调用在参数里声明了这条命令做什么(宿主要求声明),这一行把它原样写出(「Regenerate the changelog」:只取第一行,超过 40 字截断);推理正文里出现英文进行时从句时把模型自己那句话写出来(「Checking the file is true」:从动词截到这一句的句末符号,超过 40 字截断,否定的句子与不是英文的推理都不取,取不到就回到「正在思考」)。「对话」页的「状态行描述」选「简洁」时这些具体内容都不读,只说状态本身;选「精确」(默认)照旧。这一行跟着宿主自己的发布走,调用刚开始跑就被读到;每种说法写上之后锁住 1 秒,这一秒里状态再换也不动,锁一过就直接写当时最新的状态(中间来过的那些不补演),因此一个轮次里状态换得再快,这一行也只一秒换一次;被锁跨过去的调用说法不在此列——一次读完的小文件调用会在锁一过先补上它,再轮到当时的状态。说法换掉时旧的向上淡出、新的从下方升起(来自 transitions.dev 的「Text states swap」);说法盒子换宽度时,旁边的计时随之滑到新位置,不做两次跳变;一直读不出状态时这一行说这个品牌自己的那一个词,与模型思考时说的同一个:Claude 品牌下是「正在思考」,DeepSeek 品牌下是宿主自己的「深度求索中」,不因为读不出状态而换一次说法;模型连续 10 秒没有开口时,计时后面多一枚「暂未响应」:它从左下方滑上这一行,同时从无到有弹出来,模型一开口就缩回消失。这一行带扫光(「动效」页可换成让文字本身流动着变色的那一档);宿主的小鲸鱼是 DeepSeek 自己的形象,Claude 品牌下它让位给本插件自己的加载点:三枚 4px 圆点用当时生效的主题色,模型思考时它们照旧弹跳(各自弹起 8 像素、离地时压扁,半秒一次,第二枚与第三枚分别错开五分之二与五分之三个周期),这一轮改成写回答或调用工具时换另一套走法——三颗点各沿一条直线来回,三条直线彼此相差 120°、其中一条反着走,球在两端最大、过中点最小,一个来回 1.25 秒、去程与回程一样长;两套走法之间不硬切:旧的那一列各自淡出、新的那一列各自淡入,每颗球都在自己动作里的那一拍上开始——跳球从落地回弹那一刻开始淡出、到最高点淡完,从最高点开始淡入、到落地淡完;走路从线的一端开始淡出、到中点淡完,从中点开始淡入、到另一端淡完,没有哪颗球被挪到别的动作的位置上;一个动作站满 2 秒才允许下一套接手,这段时间里来了又走的状态不打扰它;其余品牌下宿主的鲸鱼尾照旧,读屏播报也照旧;说法比这一行放得下的宽度更长时,它在这个宽度上截断并画省略号,计时与徽标留在原处;「动画」设为「减弱」时说法直接换掉,不做上面那三段。只要对话还在跟随末尾,这一行就停在输入框上方,不断追加的内容不再带着它上下移动;读者自己往回翻看时它照常随内容走。同一内容容器内,第三方插件隐藏宿主文案并提供可见替代文字时,主题等待行自动让位;接管结束后恢复,其他聊天动效继续运行。
27
+ - **模型正在做什么的那一行**:轮次进行中,对话末尾那一行不再是固定的「深度求索中,用时 N 秒 ···」,而是写出这一轮此刻在做的事:「正在思考」「正在准备工具调用」「正在调用工具」「正在写回答」,后面跟着这一轮已用的时间(用宿主自己的单位)。三种状态说的是具体的东西:调用在跑时写出它操作的文件(「正在读取 app.js」「正在编辑 notes.md」「正在写入 out.txt」,只取文件名);命令调用在参数里声明了这条命令做什么(宿主要求声明),这一行把它原样写出(「Regenerate the changelog」:只取第一行);推理正文里出现进行时从句时把模型自己那句话写出来(「Checking the file is true」:从动词截到这一句的句末;句子在哪里断开交给页面自己的句子规则,所以文件名与版本号里的点号不算句末;否定的句子、还没动手的打算、引号里的话与读不出从句的正文都不取——模型引用它读到的一句话、甚至引用这一行自己那个等待词时,那不是它在说自己正在做什么;取不到就回到「正在思考」)。这三种说法都整句带出来,不按字数截断:放得下多少由这一行自己的宽度决定,放不下时在断开处画省略号,计时与徽标留在原处。「对话」页的「状态行描述」选「简洁」时这些具体内容都不读,只说状态本身;选「精确」(默认)照旧。这一行跟着宿主自己的发布走,调用刚开始跑就被读到;每种说法写上之后锁住 1 秒,这一秒里状态再换也不动,锁一过就直接写当时最新的状态(中间来过的那些不补演),因此一个轮次里状态换得再快,这一行也只一秒换一次;被锁跨过去的调用说法不在此列——一次读完的小文件调用会在锁一过先补上它,再轮到当时的状态。模型自己那句从句另有一条:两次从句之间读不出东西的那几拍(正在思考,或刚起一步还没写下什么)不把它换掉,它一直站到下一次意图到达为止——新的从句、调用自带的说法,或写回答这类状态;「精确状态行」关掉时本来就不读从句,也就无所谓站住。说法换掉时旧的向上淡出、新的从下方升起(来自 transitions.dev 的「Text states swap」);说法盒子换宽度时,旁边的计时随之滑到新位置,不做两次跳变;一直读不出状态时这一行说这个品牌自己的那一个词,与模型思考时说的同一个:Claude 品牌下是「正在思考」,DeepSeek 品牌下是宿主自己的「深度求索中」,不因为读不出状态而换一次说法;模型连续 10 秒没有开口时,计时后面多一枚「暂未响应」:它从左下方滑上这一行,同时从无到有弹出来,模型一开口就缩回消失。这一行带扫光(「动效」页可换成让文字本身流动着变色的那一档);宿主的小鲸鱼是 DeepSeek 自己的形象,Claude 品牌下它让位给本插件自己的加载点:三枚 4px 圆点用当时生效的主题色,模型思考时它们照旧弹跳(各自弹起 8 像素、离地时压扁,半秒一次,第二枚与第三枚分别错开五分之二与五分之三个周期),这一轮改成写回答或调用工具时换另一套走法——三颗点各沿一条直线来回,三条直线彼此相差 120°、其中一条反着走,球在两端最大、过中点最小,一个来回 1.25 秒、去程与回程一样长;两套走法之间不硬切:旧的那一列各自淡出、新的那一列各自淡入,每颗球都在自己动作里的那一拍上开始——跳球从落地回弹那一刻开始淡出、到最高点淡完,从最高点开始淡入、到落地淡完;走路从线的一端开始淡出、到中点淡完,从中点开始淡入、到另一端淡完,没有哪颗球被挪到别的动作的位置上;一个动作站满 2 秒才允许下一套接手,这段时间里来了又走的状态不打扰它;其余品牌下宿主的鲸鱼尾照旧,读屏播报也照旧;说法比这一行放得下的宽度更长时,它在这个宽度上截断并画省略号,计时与徽标留在原处;「动画」设为「减弱」时说法直接换掉,不做上面那三段。只要对话还在跟随末尾,这一行就停在输入框上方,不断追加的内容不再带着它上下移动;读者自己往回翻看时它照常随内容走。同一内容容器内,第三方插件隐藏宿主文案并提供可见替代文字时,主题等待行自动让位;接管结束后恢复,其他聊天动效继续运行。
28
28
  - **新文字淡入**:流式回答里新出现的字符先淡后实,并按到达次序略作错开;整段一次到达的内容、几千字的突发与刚被折叠重排过的文字保持本色。设置页的「动效」页有「淡入」与「直接出现」两档,各带一张预览卡,默认「直接出现」。
29
29
  - **文件操作行**:一个文件的读取、编辑与写入带着行尾的 `+n -m`、可展开的改动卡片或读取窗口,路径可点开文件;同一个文件在同一个过程组里的多次调用读成一行,行首的图标与标题跟着最后一次操作换新,行尾的数字是这些改动的累计值、随每次改动到达逐位翻页,展开这一行读到全部改动(只被读取过时读到这一次读取的窗口);失败与中断的行保留裁决信息。
30
30
  - **聊天气泡动效**:提交消息时输入卡片浮起、一路收成那条气泡:飞行的这一份从第一帧就是气泡的颜色,草稿里的字跟着形状重新排,最后接上真实的消息行——真实气泡在下面先显示出来,飞行的那份淡出,文字逐渐变清晰。
@@ -37,7 +37,7 @@ In the settings dialog, this page's heading row ends with a GitHub mark; pressin
37
37
 
38
38
  **Alongside other theme plugins**: with Colours and Typefaces set to Follow the host, the skin no longer rewrites the host's colours and fonts and keeps only its layout and controls; the colours are left to DSH itself, or to another theme plugin enabled at the same time. With [dsh-wallpaper-engine](https://github.com/elysia395/dsh-wallpaper-engine), for example, the wallpaper shows through the sidebar and the conversation, and the skin's own popovers take that plugin's glass, translucent and blurring the picture behind them. **Alongside [dsh-chat-ux](https://github.com/alm-allen/dsh-chat-ux)**: that plugin implements the same chat-area interactions (the rolling door, automatic folding, the token fade, file change rows, the send flight, the drawn caret), and two copies of them intercept each other's clicks and press the same controls. So this skin stands down when it sees that plugin: the Chat-area animations row on the settings page's Conversation tab and the Composer caret motion and New-text fade rows on the Motion tab have their controls disabled while each still shows the value you set, with a line underneath in the accent colour reading "Managed by dsh-chat-ux". Your settings are not rewritten, and they take effect as they are once dsh-chat-ux is removed.
39
39
 
40
- **Chat-area follow**: at the structural moments — a thinking row folding, a tool call row arriving — a reader sitting at the bottom is handed back to the host's own follow, instead of being left tens of pixels short by the burst of content; a process group capped in the Standard and Compact tiers (thinking and tool output kept in one scrolling body) is caught up the same way. Catching up runs on a curve: a trickle of characters settles softly and a burst glides at one steady speed before landing. While content streams, the main scroller's own follow is walked along that curve too rather than written to the end in a single frame — with a fast stream the newest lines trail just below the fold and slide into place once it stops. A message you send does not go through it: the host's scroll to bring it into view lands at once. Once the reader scrolls away from the bottom himself, the plugin stays out of it until he returns.
40
+ **Chat-area follow**: at the structural moments — a thinking row folding, a tool call row arriving — a reader sitting at the bottom is handed back to the host's own follow, instead of being left tens of pixels short by the burst of content; a process group capped in the Standard and Compact tiers (thinking and tool output kept in one scrolling body) is caught up the same way. Catching up runs on a curve: a trickle of characters settles softly and a burst glides at one steady speed before landing. While content streams, the host's own follow keeps the position at the end and the newly grown content slides up from the bottom edge instead of the text above it jumping in a single frame, with the status line standing still; the slide runs on the graphics card and takes no layout, so several sessions streaming at once cost no more for it. A message you send does not go through it: the host's scroll to bring it into view lands at once. Once the reader scrolls away from the bottom himself, the plugin stays out of it until he returns.
41
41
 
42
42
  **Automatic folding**: a thinking row opens while the model reasons and folds back when it stops; a running process group opens and folds back once that piece of work ends. A row or group the reader pressed himself keeps what he chose for that phase.
43
43
 
@@ -45,7 +45,7 @@ In the settings dialog, this page's heading row ends with a GitHub mark; pressin
45
45
 
46
46
  **Counted process headers**: a process group's header says what the group holds, in place of the host's list of activities — "2 thoughts, 1 tool call". Empty figures are left out, each number rolls as the work grows, and the whole line sweeps the way the host's words do while that piece of work runs. A header without the room for the sentence (a narrow window, a narrow row) falls back to the compact figures, `Thinking×2 · Tools×1`, and takes the sentence back when the room returns; the control's accessible name is the sentence either way.
47
47
 
48
- **The line saying what the model is doing**: while a turn runs, the line at the end of the conversation is no longer a fixed "Deep diving for N s ···": it names what that turn is doing now — "Thinking…", "Preparing a tool call…", "Running tools…", "Writing…" — with the time the turn has taken beside it in the host's own units. Two of those states say something more: a call in flight names the file it works on ("Reading app.js", "Editing notes.md", "Writing out.txt" — the file's own name, cut past 32 characters), and reasoning carrying an English present-continuous clause hands the line the model's own words ("Checking the file is true": from the participle to that clause's end, cut past 40 characters, a negated clause and reasoning in another language passed over, and "Thinking…" where there is none). The line follows the host's own publishes, so a call is read the moment it starts running; every wording written is locked for a second, and the pass that finds the lock run out writes the newest state of that moment — whatever came and went inside the second is passed over, so a turn that moves quickly still changes the line at most once a second. A call's own words are the one thing the lock does not pass over: a small file read inside the second is written as soon as the lock ends, ahead of the state of that moment. A wording that changes runs transitions.dev's "Text states swap": the old one blurs and fades upward while the new one rises from below, the two travelling together and the change over in about 0.2 s, and the box takes the arriving wording's width in one move, so the clock beside it travels to its new place (about 0.24 s) rather than jumping twice. Where no state can be read at all the line stands on that brand's own word — the same one it says while the model is thinking: the skin's own "Thinking…" under the Claude brand, the host's own "Deep diving" under the DeepSeek brand — and once the model has been silent for ten seconds "No response yet" appears beside the clock. The line sweeps, and the host's whale, which is DeepSeek's own character, gives way under the Claude brand to the skin's own loader: three 4px dots in the accent in force, which bounce the way the snippet has them while the model thinks (each rising 8px and flattening as it leaves the floor, half a second a bounce with the second dot starting two fifths of the period behind the first and the third three fifths) and take a second motion once the turn writes or works — each dot walking its own line, three lines 120° apart with one of them run the other way, the dot largest at either end and smallest as it crosses the middle, 1.25s a round trip with as long spent going out as coming back; the two motions never cut: the lane leaving fades each of its dots out where its own motion next turns back — off the floor for a bounce, off a far end for a walk — and the lane arriving fades each of its dots in where that motion next carries it, each fade over one of its own legs, so no dot is ever moved to a pose its motion does not carry; a motion stands for 2s before the next may take it over, so a state that comes and goes inside that stretch leaves it standing, while under the other brands the host's whale tail stands and the host's screen-reader announcement stays either way; wording wider than the room this line has is cut with an ellipsis while the clock and the badge keep their places; with Animation set to Reduced the wording is written at once, with neither half of the swap. As long as the conversation follows its tail the line rests above the composer, so content arriving below no longer drags it around; scrolling back yourself leaves it travelling with the content.
48
+ **The line saying what the model is doing**: while a turn runs, the line at the end of the conversation is no longer a fixed "Deep diving for N s ···": it names what that turn is doing now — "Thinking…", "Preparing a tool call…", "Running tools…", "Writing…" — with the time the turn has taken beside it in the host's own units. Two of those states say something more: a call in flight names the file it works on ("Reading app.js", "Editing notes.md", "Writing out.txt" — the file's own name), and reasoning carrying a continuous clause hands the line the model's own words ("Checking the file is true": from the participle to that clause's end, with where a sentence ends left to the page's own sentence rules, so a dot inside a file name or a version number does not cut it; a negated clause, a plan the model has not started, anything inside a quotation and reasoning carrying no clause are passed over — a model quoting a sentence it read, the line's own waiting word among them, is not saying what it is doing; and "Thinking…" where there is none). Both are carried whole: how much of one shows is the width this line itself has, and past that the words are clipped with an ellipsis while the clock and the badge keep their places. The line follows the host's own publishes, so a call is read the moment it starts running; every wording written is locked for a second, and the pass that finds the lock run out writes the newest state of that moment — whatever came and went inside the second is passed over, so a turn that moves quickly still changes the line at most once a second. A call's own words are the one thing the lock does not pass over: a small file read inside the second is written as soon as the lock ends, ahead of the state of that moment. The model's own clause has a rule of its own: the stretches between two of them, where the reasoning reads as the bare thinking state or as nothing at all, do not take it off the line — it stands until the next intent arrives, whether that is a newer clause, a call's own words, or a state such as writing; with the Precise status line switched off no clause is read at all, so nothing stands. A wording that changes runs transitions.dev's "Text states swap": the old one blurs and fades upward while the new one rises from below, the two travelling together and the change over in about 0.2 s, and the box takes the arriving wording's width in one move, so the clock beside it travels to its new place (about 0.24 s) rather than jumping twice. Where no state can be read at all the line stands on that brand's own word — the same one it says while the model is thinking: the skin's own "Thinking…" under the Claude brand, the host's own "Deep diving" under the DeepSeek brand — and once the model has been silent for ten seconds "No response yet" appears beside the clock. The line sweeps, and the host's whale, which is DeepSeek's own character, gives way under the Claude brand to the skin's own loader: three 4px dots in the accent in force, which bounce the way the snippet has them while the model thinks (each rising 8px and flattening as it leaves the floor, half a second a bounce with the second dot starting two fifths of the period behind the first and the third three fifths) and take a second motion once the turn writes or works — each dot walking its own line, three lines 120° apart with one of them run the other way, the dot largest at either end and smallest as it crosses the middle, 1.25s a round trip with as long spent going out as coming back; the two motions never cut: the lane leaving fades each of its dots out where its own motion next turns back — off the floor for a bounce, off a far end for a walk — and the lane arriving fades each of its dots in where that motion next carries it, each fade over one of its own legs, so no dot is ever moved to a pose its motion does not carry; a motion stands for 2s before the next may take it over, so a state that comes and goes inside that stretch leaves it standing, while under the other brands the host's whale tail stands and the host's screen-reader announcement stays either way; wording wider than the room this line has is cut with an ellipsis while the clock and the badge keep their places; with Animation set to Reduced the wording is written at once, with neither half of the swap. As long as the conversation follows its tail the line rests above the composer, so content arriving below no longer drags it around; scrolling back yourself leaves it travelling with the content.
49
49
 
50
50
  **Send flight**: on submission the composer card lifts as it is and narrows into the bubble as it travels, its words re-flowing into the shape, landing on the message row: the real bubble shows underneath and the flying copy fades out, so the words sharpen into place.
51
51
 
package/docs/SETTINGS.md CHANGED
@@ -37,7 +37,7 @@
37
37
 
38
38
  **与其他主题插件同时使用**:「配色」与「字体」选「跟随宿主」后,本插件不再改写宿主的颜色与字体,只保留布局和控件;颜色交给 DSH 自己,或者交给同时启用的其他主题插件。例如与 [dsh-wallpaper-engine](https://github.com/elysia395/dsh-wallpaper-engine) 一起用时,壁纸透过侧栏与对话区显示出来,插件自己的弹层随壁纸插件的玻璃效果变成半透明并模糊背后的画面。**与 [dsh-chat-ux](https://github.com/alm-allen/dsh-chat-ux) 同时使用时**:那个插件实现了同一批聊天区交互(卷帘门、自动开合、文字淡入、文件变更行、聊天气泡动效、插入符动效),两套同时生效会互相拦点击、抢同一批按钮。所以本插件在检测到它时让位:设置页「对话」页的「聊天区动画效果」与「动效」页的「光标动画」「新文字淡入」三行控件禁用,但仍显示你自己设的值,下面另起一行用强调色注明「该选项由 dsh-chat-ux 管理」;你的设置不会被改写,卸载 dsh-chat-ux 之后原样生效。
39
39
 
40
- **聊天区跟随**:思考行收起、工具调用行出现这些结构时刻,贴着底部的读者被交还给系统自己的跟随,内容成片到达时不再停在离底部几十像素的地方;「标准」与「简洁」档里封顶的过程组(思考与工具输出收在一个带滚动条的组体里)同样补到底部。补到底部走的是曲线:零散到达的字收得住尾巴,成片涌进的文字以一段稳速滑过去,再落到末尾。流式输出期间主滚动条的新内容也沿这条曲线推进,不再一帧写到底;输出很快时最新几行会短暂拖在屏幕下缘之外,流一停就滑到位。你自己发出的消息不走这条曲线:宿主把它滚进视野的那一下照旧一步到位。读者自己滚动离开底部之后,插件不再插手,直到他自己回到底部。
40
+ **聊天区跟随**:思考行收起、工具调用行出现这些结构时刻,贴着底部的读者被交还给系统自己的跟随,内容成片到达时不再停在离底部几十像素的地方;「标准」与「简洁」档里封顶的过程组(思考与工具输出收在一个带滚动条的组体里)同样补到底部。补到底部走的是曲线:零散到达的字收得住尾巴,成片涌进的文字以一段稳速滑过去,再落到末尾。流式输出期间位置由宿主自己的跟随写到末尾,新长出的内容从屏幕下缘平滑滑上来,上方文字不再一帧跳上去,状态行停在原处不动;这段滑动由显卡完成,不占用页面的布局,多个会话同时输出时也不加重负担。你自己发出的消息不走这条曲线:宿主把它滚进视野的那一下照旧一步到位。读者自己滚动离开底部之后,插件不再插手,直到他自己回到底部。
41
41
 
42
42
  **思考与过程自动开合**:模型还在思考时思考行开着,思考停下就收回去;运行中的过程组自动展开,这一段结束再收起。读者自己按过的行或组在当时的阶段里不再被改动。
43
43
 
@@ -45,7 +45,7 @@
45
45
 
46
46
  **过程组标题上的计数**:过程组的标题不再只写「执行了命令,已读取文件,已调用工具等」,而是写这一组装了什么,例如「执行 2 次思考,调用 1 次工具」;没有的项不写,数字随内容增加逐位滚动,这一段还在进行时整行带着与宿主同样的扫光。这一行放不下整句时(窗口窄、过程组所在的行窄)改用简洁写法「思考×2 · 工具×1」,位置宽回来再换回整句;控件的可访问名字始终是整句。
47
47
 
48
- **模型正在做什么的那一行**:轮次进行中,对话末尾那一行不再是固定的「深度求索中,用时 N 秒 ···」,而是写出这一轮此刻在做的事:「正在思考」「正在准备工具调用」「正在调用工具」「正在写回答」,后面跟着这一轮已用的时间(用宿主自己的单位)。两种状态说的是具体的东西:调用在跑时写出它操作的文件(「正在读取 app.js」「正在编辑 notes.md」「正在写入 out.txt」,只取文件名,名字过长截到 32 字),推理正文里出现英文进行时从句时把模型自己那句话写出来(「Checking the file is true」:从动词截到这一句的句末符号,超过 40 字截断,否定的句子与不是英文的推理都不取,取不到就回到「正在思考」)。这一行跟着宿主自己的发布走,调用刚开始跑就被读到;每种说法写上之后锁住 1 秒,这一秒里状态再换也不动,锁一过就直接写当时最新的状态(中间来过的那些不补演),因此一个轮次里状态换得再快,这一行也只一秒换一次;被锁跨过去的调用说法不在此列——一次读完的小文件调用会在锁一过先补上它,再轮到当时的状态。说法换掉时旧的向上淡出、新的从下方升起,两段重叠着一同走完(整次约 0.2 秒,来自 transitions.dev 的「Text states swap」);说法盒子换宽度时,旁边的计时随之滑到新位置(约 0.24 秒),不做两次跳变;一直读不出状态时这一行说这个品牌自己的那一个词,与模型思考时说的同一个:Claude 品牌下「正在思考」,DeepSeek 品牌下宿主自己的「深度求索中」;模型连续 10 秒没有开口时,计时后面多一枚「暂未响应」。这一行带扫光;宿主的小鲸鱼是 DeepSeek 自己的形象,Claude 品牌下它让位给本插件自己的加载点:三枚 4px 圆点用当时生效的主题色,模型思考时它们照旧弹跳(各自弹起 8 像素并在离地时压扁,半秒一次,第二枚与第三枚分别错开五分之二与五分之三个周期),这一轮改成写回答或调用工具时换另一套走法——三颗点各沿一条直线来回,三条直线彼此相差 120°、其中一条反着走,球在两端最大、过中点最小,一个来回 1.25 秒、去程与回程一样长;两套走法之间不硬切:旧的那一列各自淡出、新的那一列各自淡入,每颗球都在自己动作里的那一拍上开始——跳球从落地回弹那一刻开始淡出、到最高点淡完,从最高点开始淡入、到落地淡完;走路从线的一端开始淡出、到中点淡完,从中点开始淡入、到另一端淡完,没有哪颗球被挪到别的动作的位置上;一个动作站满 2 秒才允许下一套接手,这段时间里来了又走的状态不打扰它;其余品牌下宿主的鲸鱼尾照旧,读屏播报也照旧;说法比这一行放得下的宽度更长时,它在这个宽度上截断并画省略号,计时与徽标留在原处;「动画」设为「减弱」时说法直接换掉,不做上面那两段。只要对话还在跟随末尾,这一行就停在输入框上方,不断追加的内容不再带着它上下移动;读者自己往回翻看时它照常随内容走。
48
+ **模型正在做什么的那一行**:轮次进行中,对话末尾那一行不再是固定的「深度求索中,用时 N 秒 ···」,而是写出这一轮此刻在做的事:「正在思考」「正在准备工具调用」「正在调用工具」「正在写回答」,后面跟着这一轮已用的时间(用宿主自己的单位)。两种状态说的是具体的东西:调用在跑时写出它操作的文件(「正在读取 app.js」「正在编辑 notes.md」「正在写入 out.txt」,只取文件名),推理正文里出现进行时从句时把模型自己那句话写出来(「Checking the file is true」:从动词截到这一句的句末;句子在哪里断开交给页面自己的句子规则,所以文件名与版本号里的点号不算句末;否定的句子、还没动手的打算、引号里的话与读不出从句的正文都不取——模型引用它读到的一句话、甚至引用这一行自己那个等待词时,那不是它在说自己正在做什么;取不到就回到「正在思考」)。这两种说法都整句带出来,不按字数截断:放得下多少由这一行自己的宽度决定,放不下时在断开处画省略号,计时与徽标留在原处。这一行跟着宿主自己的发布走,调用刚开始跑就被读到;每种说法写上之后锁住 1 秒,这一秒里状态再换也不动,锁一过就直接写当时最新的状态(中间来过的那些不补演),因此一个轮次里状态换得再快,这一行也只一秒换一次;被锁跨过去的调用说法不在此列——一次读完的小文件调用会在锁一过先补上它,再轮到当时的状态。模型自己那句从句另有一条:两次从句之间读不出东西的那几拍(正在思考,或刚起一步还没写下什么)不把它换掉,它一直站到下一次意图到达为止——新的从句、调用自带的说法,或写回答这类状态;「精确状态行」关掉时本来就不读从句,也就无所谓站住。说法换掉时旧的向上淡出、新的从下方升起,两段重叠着一同走完(整次约 0.2 秒,来自 transitions.dev 的「Text states swap」);说法盒子换宽度时,旁边的计时随之滑到新位置(约 0.24 秒),不做两次跳变;一直读不出状态时这一行说这个品牌自己的那一个词,与模型思考时说的同一个:Claude 品牌下「正在思考」,DeepSeek 品牌下宿主自己的「深度求索中」;模型连续 10 秒没有开口时,计时后面多一枚「暂未响应」。这一行带扫光;宿主的小鲸鱼是 DeepSeek 自己的形象,Claude 品牌下它让位给本插件自己的加载点:三枚 4px 圆点用当时生效的主题色,模型思考时它们照旧弹跳(各自弹起 8 像素并在离地时压扁,半秒一次,第二枚与第三枚分别错开五分之二与五分之三个周期),这一轮改成写回答或调用工具时换另一套走法——三颗点各沿一条直线来回,三条直线彼此相差 120°、其中一条反着走,球在两端最大、过中点最小,一个来回 1.25 秒、去程与回程一样长;两套走法之间不硬切:旧的那一列各自淡出、新的那一列各自淡入,每颗球都在自己动作里的那一拍上开始——跳球从落地回弹那一刻开始淡出、到最高点淡完,从最高点开始淡入、到落地淡完;走路从线的一端开始淡出、到中点淡完,从中点开始淡入、到另一端淡完,没有哪颗球被挪到别的动作的位置上;一个动作站满 2 秒才允许下一套接手,这段时间里来了又走的状态不打扰它;其余品牌下宿主的鲸鱼尾照旧,读屏播报也照旧;说法比这一行放得下的宽度更长时,它在这个宽度上截断并画省略号,计时与徽标留在原处;「动画」设为「减弱」时说法直接换掉,不做上面那两段。只要对话还在跟随末尾,这一行就停在输入框上方,不断追加的内容不再带着它上下移动;读者自己往回翻看时它照常随内容走。
49
49
 
50
50
  **聊天气泡动效**:提交消息时输入卡片原样浮起、一路收成那条气泡,草稿里的字跟着形状重新排,落地正好接上真实的消息行:真实气泡在下面先显示出来,飞行的那份淡出,文字逐渐变清晰。
51
51