dsh-claude-style 0.13.2 → 0.14.0-dev.12
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 +182 -0
- package/LICENSE +0 -5
- package/README.en.md +1 -1
- package/README.md +2 -2
- package/THIRD_PARTY_NOTICES.md +282 -6
- package/docs/FEATURES.en.md +13 -14
- package/docs/FEATURES.md +13 -14
- package/docs/SETTINGS.en.md +21 -12
- package/docs/SETTINGS.md +21 -12
- package/docs/STYLE.md +151 -12
- package/lib/assets/1952ecd55916.js.br +0 -0
- package/lib/assets/3b91bf51db72.js.br +0 -0
- package/lib/assets/4ec527e58330.js.br +0 -0
- package/lib/assets/7323c8c86470.js.br +0 -0
- package/lib/assets/755d09170f37.js.br +0 -0
- package/lib/assets/c02a70a798bc.js.br +0 -0
- package/lib/assets/d8df7a57fbe3.js.br +0 -0
- package/lib/assets/manifest.json +28 -28
- package/lib/client.js +11 -8
- package/lib/host/brand.js +136 -0
- package/lib/host/http.js +74 -0
- package/lib/host/peakrate.js +7 -0
- package/lib/host/routes.js +26 -88
- package/lib/host/search.js +8 -1
- package/lib/host/settings.js +5 -0
- package/lib/model-descriptions.json +195 -43
- package/package.json +12 -4
- package/docs/decisions/D11-private-routes.md +0 -26
- package/docs/decisions/D12-fail-fast.md +0 -36
- package/docs/decisions/D14-account-surface.md +0 -26
- package/docs/decisions/D15-hdsl-contract.md +0 -24
- package/docs/decisions/D16-popover-baseline.md +0 -23
- package/docs/decisions/D17-permission-presets.md +0 -26
- package/docs/decisions/D19-host-contracts.md +0 -27
- package/docs/decisions/D20-sliding-pill.md +0 -26
- package/docs/decisions/D21-archived-list.md +0 -24
- package/docs/decisions/D22-search-panel.md +0 -30
- package/docs/decisions/D24-mascot.md +0 -32
- package/docs/decisions/D25-ban-screen-titlebar.md +0 -24
- package/docs/decisions/D26-motion-preference.md +0 -23
- package/docs/decisions/D27-context-stats.md +0 -35
- package/docs/decisions/D28-desktop-band.md +0 -23
- package/docs/decisions/D29-feature-switches.md +0 -24
- package/docs/decisions/D3-host-selectors.md +0 -28
- package/docs/decisions/D30-palette-and-typeface.md +0 -25
- package/docs/decisions/D31-windows-titlebar.md +0 -24
- package/docs/decisions/D32-chat-interactions.md +0 -33
- package/docs/decisions/D33-stylesheet-ownership.md +0 -26
- package/docs/decisions/D34-turn-navigator.md +0 -29
- package/docs/decisions/D35-selection-scope.md +0 -23
- package/docs/decisions/D36-modules-and-bundling.md +0 -35
- package/docs/decisions/D38-assets.md +0 -28
- package/docs/decisions/D39-on-demand-loading.md +0 -33
- package/docs/decisions/D4-composer-gate.md +0 -22
- package/docs/decisions/D40-observation-bus.md +0 -36
- package/docs/decisions/D41-scroll-owner.md +0 -40
- package/docs/decisions/D42-feature-manifest.md +0 -28
- package/docs/decisions/D43-overlay-and-upstream.md +0 -27
- package/docs/decisions/D44-contract-module.md +0 -36
- package/docs/decisions/D45-testing.md +0 -30
- package/docs/decisions/D46-monorepo.md +0 -31
- package/docs/decisions/D47-build-output.md +0 -30
- package/docs/decisions/D48-documentation.md +0 -28
- package/docs/decisions/D49-visual-yield.md +0 -30
- package/docs/decisions/D5-model-copy.md +0 -29
- package/docs/decisions/D50-brand.md +0 -24
- package/docs/decisions/D51-css-toolchain.md +0 -34
- package/docs/decisions/D52-header-band.md +0 -31
- package/docs/decisions/D53-usage-merge.md +0 -33
- package/docs/decisions/D54-peakrate-meter.md +0 -33
- package/docs/decisions/D58-redraw-retired.md +0 -29
- package/docs/decisions/D60-settings-transport.md +0 -31
- package/docs/decisions/D61-wallpaper-under-one-mask.md +0 -28
- package/docs/decisions/D62-file-row-merge.md +0 -28
- package/docs/decisions/D63-turn-status-retired.md +0 -29
- package/docs/decisions/D7-one-package.md +0 -23
- package/docs/decisions/D9-style-performance.md +0 -25
- package/docs/decisions/README.md +0 -86
- package/lib/assets/2c01384360ac.js.map.br +0 -0
- package/lib/assets/31253ed42440.js.br +0 -0
- package/lib/assets/5063c46824b6.js.br +0 -0
- package/lib/assets/55a6154f87b5.js.map.br +0 -0
- package/lib/assets/5e05b29d5005.js.br +0 -0
- package/lib/assets/668d06ca781b.js.map.br +0 -0
- package/lib/assets/930546669455.js.map.br +0 -0
- package/lib/assets/968fcbfc3e22.js.br +0 -0
- package/lib/assets/abf0547f08a4.js.br +0 -0
- package/lib/assets/ac9b0e625837.js.map.br +0 -0
- package/lib/assets/ad2905345a69.js.br +0 -0
- package/lib/assets/ba68f60ecc71.js.map.br +0 -0
- package/lib/assets/c52e41b4842b.js.map.br +0 -0
- package/lib/assets/e1291f79b7c9.js.br +0 -0
- package/lib/client.js.map +0 -7
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# D19. 宿主契约与构建编号
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:宿主边界
|
|
5
|
-
- **关联**:D3、D43、D44
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 下面这些契约的字面写在 `packages/contracts/src/dom.ts`,条目在 `packages/contracts/src/table.ts`(D44);契约测试按这份表逐条核对真实宿主(D45 的端到端环境搭好之前由冒烟检查的替代宿主页面复现)。
|
|
10
|
-
- 插槽定位点:宿主渲染器保证每个插槽外面有 `[data-slot="<key>"]` 包装层(ui-renderer 的 scoped-slots),供外部定位。例如访问模式按钮按 `[data-slot="conversation.input.permission"]` 查找,跳过插件自己插进去的按钮。
|
|
11
|
-
- 控件标记:输入框按宿主 InputBar 的结构给按钮打 `data-dsh-claude-control`——`commands`(工具行里唯一打开 listbox 的按钮)、`stop` / `send`(提交区的主按钮,按图标区分:停止画 rect,提交箭头画 path;排队与插话是同一个提交按钮换了文字)、`access`(权限插槽里的按钮)。样式写成 `button[data-dsh-claude-control="…"]`。
|
|
12
|
-
- 弹层角色随开合写撤:宿主把文档里每个 `[role="menu"]`(ui-primitives 的 `modalSelector`,与 `[role="dialog"][aria-modal="true"]` 同列)当作占据前景的菜单,快捷键派发、`closeTopModal`、Esc-Esc 停止序列与 dock 标签菜单都按它判定。皮肤为量宽高而常驻 `<body>` 的弹层卡片关闭时只是视觉上藏起来,所以 `role="menu"` 经 `setMenuPopoverOpen`(`packages/client/src/shared/popover.ts`)与 `data-open` 同写同撤,卡片开着才持有这个角色;`quickProviders` 的卡片本就开时挂载、关时摘除,不经过它。
|
|
13
|
-
- 构建编号:构建取产物内容的哈希写成 `BUILD_ID`,运行时写到 `<body data-dsh-claude-style>` 的值上;页面跑的是哪一版以它为准。热重载替换插件代码而不重新加载页面,页面的加载时间说明不了代码版本。
|
|
14
|
-
|
|
15
|
-
## 理由
|
|
16
|
-
|
|
17
|
-
- 界面文字与哈希类名随语言、版本变化,结构与契约更稳定。
|
|
18
|
-
- 关闭的卡片若持有 `role="menu"`,宿主会把整套快捷键判给一个看不见的菜单。
|
|
19
|
-
|
|
20
|
-
## 代价
|
|
21
|
-
|
|
22
|
-
- 结构判断依赖宿主 InputBar 的现状,宿主改版时需要核对。
|
|
23
|
-
- 宿主按 `modalSelector` 判定快捷键这一侧,测试只查皮肤自己的不变量:关闭的弹层卡片不持有 `role="menu"`。
|
|
24
|
-
|
|
25
|
-
## 重审条件
|
|
26
|
-
|
|
27
|
-
- 宿主给这些控件提供自己的 `data-*` 标记时,直接读宿主的标记。
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# D20. 分段控件共用滑动高亮
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:功能
|
|
5
|
-
- **关联**:D26、D40
|
|
6
|
-
- **迁移**:尺寸变化改经观察总线订阅(D40,待实施)
|
|
7
|
-
|
|
8
|
-
## 决定
|
|
9
|
-
|
|
10
|
-
- 视图标签、权限分段、进行中 / 已归档与设置页的分段控件共用 `createSlidingPill`:量出当前项相对控件内边距盒的位置与宽度,写成两个自定义属性,控件的伪元素用 transform 与 width 过渡滑过去。
|
|
11
|
-
- 属性与首次位置在同一次调用里写入,初次出现不滑入;控件隐藏或没有当前项时撤掉属性,当前项恢复自己的底色。
|
|
12
|
-
- 宽度因字体加载而变化时重新定位。
|
|
13
|
-
- 滑动不受「减少动态效果」设置影响(用户要求始终播放)。
|
|
14
|
-
- React 渲染的控件(设置页)在布局副作用里同步,赶在绘制之前。
|
|
15
|
-
|
|
16
|
-
## 理由
|
|
17
|
-
|
|
18
|
-
- 四处分段控件是同一个交互,一份实现保证一样的手感。
|
|
19
|
-
|
|
20
|
-
## 代价
|
|
21
|
-
|
|
22
|
-
- 每轮刷新读两个元素的位置;在已经排过版的帧里开销很小。
|
|
23
|
-
|
|
24
|
-
## 重审条件
|
|
25
|
-
|
|
26
|
-
- 宿主的分段控件自带滑动高亮时改用宿主的。
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# D21. 已归档列表跟随宿主的两份客户端数据
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:功能
|
|
5
|
-
- **关联**:D11
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 侧栏「已归档」是插件自己的平铺列表,订阅宿主 `workspaces.list`(归档集合)与 `sessions.list`(标题、时间、来源),两者都就绪后按宿主「仅已归档」的规则重算:只收已有会话摘要、非子代理、非空白占位的归档会话,按更新时间排序,内容不变时不重建。
|
|
10
|
-
- 宿主自带的筛选状态在 ui-workspace 私有的视图 store 里,不是客户端服务,不用它。
|
|
11
|
-
- 取消归档走 `workspaces.unarchiveSession`;删除走宿主半边的私有路由(D11)。浏览器删除成功后重拉一次会话列表基线,让已删除会话的旧摘要立刻离开列表;其余失败记警告。
|
|
12
|
-
- 点击已归档行显示宿主的同一条提示:宿主的 Toast 组件加 `workspace` 文案表里的 `toast.archivedNotOpenable`(宿主的提示通道是私有的)。
|
|
13
|
-
|
|
14
|
-
## 理由
|
|
15
|
-
|
|
16
|
-
- 宿主的筛选只能靠点它的菜单改;按宿主同一规则重算,列表内容与宿主一致。
|
|
17
|
-
|
|
18
|
-
## 代价
|
|
19
|
-
|
|
20
|
-
- 宿主「仅已归档」的规则变化时需要同步。
|
|
21
|
-
|
|
22
|
-
## 重审条件
|
|
23
|
-
|
|
24
|
-
- 宿主把视图筛选或提示通道开放为客户端服务时改用宿主的。
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# D22. 搜索面板:宿主的 Modal、宿主的数据与导航
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:功能
|
|
5
|
-
- **关联**:D11、D16
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 放置:搜索框的位置由偏好 `searchStyle` 决定,三档,选择随属性 `data-dsh-claude-search-style` 落到文档,样式表按它分流。`overlay`(默认)把搜索框插在宿主品牌行(`[data-slot="sidebar"]` 里的 `_logoRow`)的品牌旁边,这一行改成网格,品牌与搜索框同占第一格,两者的淡入淡出交给样式表的 `[data-slot="sidebar"]:hover`;`standalone` 在品牌行下方插入皮肤自己的一行,一直显示;`icon` 不画皮肤的搜索框,让宿主自己的搜索按钮留在原处,只接管它的单击。三档都只在这一行带宽版品牌时放置。
|
|
10
|
-
- 快捷键:宿主的 `session.search` 没有可替换的公开入口,它的效果是展开宿主自己的侧栏搜索并聚焦输入框。宿主的这块搜索因此保持挂载,`overlay` 与 `standalone` 下只从视觉与布局里拿掉,`icon` 下就留在原处;焦点落进它的输入框时搜索面板打开,并经宿主自己的清除按钮把它收回,快捷键改绑后也跟着走。
|
|
11
|
-
- 面板是宿主 ui-primitives 的 `Modal`:遮罩、焦点归还、Esc 与模态层都用宿主的;卡片里的行由皮肤搭。它不套 D16 的弹层外壳,但登记进弹层互斥表。`Modal` 关闭即卸载,所以关闭时先给遮罩层打标记、保持挂载淡出,淡出结束后再卸载,标记在卸载之后才撤(否则重播一帧入场动画)。
|
|
12
|
-
- 数据读宿主的客户端服务:`sessions.list` 与 `workspaces.list`(去掉已归档、子代理与空白占位)、`remote.pluginInventory` 与 `remote.pluginManager`、当前会话的 `remote.skills`、`shortcuts.catalog`。远程命名空间在面板打开时用 `ctx.get('remote.<名字>')` 读(在根上下文直接读 `remote` 的子属性会被 cordis 以「没有 inject」拒绝)。
|
|
13
|
-
- 导航用宿主自己的:`uiWorkspace.openSession` / `startSession`、`pluginNavigation.openBundle`、`layout.selectPanel`;Skill 经会话输入的 `setDraft` 与 `focus`;设置页与快捷键列表经它们在插槽登记里声明的 store(`slots.entries(key)` 条目上的 `store.create()`)打开。快捷键行打开快捷键列表,不代为执行命令(宿主的快捷键命令没有公开的执行入口)。内容命中还要落到那条消息所在的一轮:面板开完会话,等这一轮进了窗口再按命中消息的事件序号定位(按 `turnOutline` 找轮次,跳转本身走 D34 那一套,按宿主的刻度跳)。
|
|
14
|
-
- 匹配沿用宿主侧栏搜索的子串规则,排序为:标题开头、标题中的词开头、标题其他位置、第二个键(路径、包名、描述、别名)。
|
|
15
|
-
- 消息内容由宿主半边的 `/session-search` 路由搜(`packages/host/src/search.ts`):日志经共享的 `session-events.ts` 读(宿主的观察接口,按 id 读起,不先列整份存档),只收人类发出的 `user/message`(按 `data.source.kind === 'user'` 排除注入的环境快照、Skill 目录、AGENTS.md 与压缩检查点)与助手消息里的 text 块,`reasoning`、`tool-call`、`tool/result` 都不进语料;匹配照搬 `sessionQuery` 的 `text` 过滤(字面、不分大小写、空白可伸缩)。每个会话的语料落一份 `$DSH_HOME/cache/dsh-claude-style/search/<会话 id>.json`(走共享的 `cache-file.ts`,与峰谷目录、用量缓存同目录):热启动读这些文件,只重读修订号变了的存档会话;宿主开着的会话按 `seq` 只读新增的那一段。存档会话的清单每 3 秒列一次(列一次要开每个日志的头部帧,是这条路上最贵的一步);面板打开时先发一次不带查询的请求预热;每个会话只回最新的一处命中,附摘录与命中位置。
|
|
16
|
-
|
|
17
|
-
## 理由
|
|
18
|
-
|
|
19
|
-
- 面板要做的每件事宿主都已有一条路,借用这些路,行为与宿主各处一致。
|
|
20
|
-
- 位置属于读者的口味,三档的行为一致:品牌行里的搜索框会盖住品牌,有人要宿主自己的图标、有人要一个一直看得见的框,所以做成三档偏好,三档共用同一个面板。
|
|
21
|
-
- 内容搜索是唯一的例外:宿主的索引 `sessions.search`(SQLite FTS5)在出厂 Web 组合里关闭,它的 `unicode61` 分词搜不到中文句中的字;读日志与匹配规则仍用宿主的。
|
|
22
|
-
|
|
23
|
-
## 代价
|
|
24
|
-
|
|
25
|
-
- 依赖上面这些服务、store 与品牌行结构的现状;数据与导航要在真实页面核对。
|
|
26
|
-
- 内容搜索在一台还没有语料缓存的机器上,第一次搜索要读完全部会话日志(本机 260 个会话一次约 1 分钟);此后每次宿主进程启动读一遍语料文件约 1.4 秒,单次查询约 5–20 毫秒。语料按会话落盘(本机约 6 MB),内存里另有一份小写副本。存档会话的清单每 2 秒最多列一次,列一次约 1.3 秒(要逐个打开日志的头部帧),由面板打开时的预热承担,打字查询不列。
|
|
27
|
-
|
|
28
|
-
## 重审条件
|
|
29
|
-
|
|
30
|
-
- 宿主提供自己的全局搜索或命令面板时改为打开宿主的;宿主的内容索引能按子串匹配中文、并在出厂组合里打开时,内容搜索改回 `sessions.search`。
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# D24. 吉祥物:宿主的状态、一套播放器、两个角色
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:功能
|
|
5
|
-
- **关联**:D26、D38、D40、D50
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 吉祥物与品牌(D50)分开:`mascot` 偏好有 `brand`(默认:Claude 品牌下是螃蟹、DeepSeek 品牌下是 Deepy)、`crab`、`deepy`、`off` 四档,解析后的角色写成 `<body data-dsh-claude-mascot="crab|deepy|off">`;`mascotScope` 有 `home` 与 `all`(默认)两档。`packages/client/src/features/mascot/mascot.ts` 让一个角色出场、另一个释放,页面上最多一只。
|
|
10
|
-
- 站位:首页站在输入卡片上沿。对话页只认主对话区(`[data-phase="active"]` 里的 `[data-conversation-session]`),站在输入区 composerStack 的上沿;宿主的 composer 链选出替代面板时(ui-renderer 把 `[data-chain-overlay-fallback="conversation.composer"]` 内联写成 `display: none`,面板紧跟其后挂载),站到那块面板上沿。站着的宿主元素打 `data-dsh-claude-<角色>-anchor`,样式表给它 `position: relative`。
|
|
11
|
-
- 状态只读宿主的客户端数据:`uiSession.sessionStatus`、`sessions.list`(顶层会话与 `subagentCatalog`)、聊天快照(打开的轮次、正在输出的一步的最后一块、进行中的工具调用)。聊天快照在跟随会话期间订阅(只读不订阅时,轨迹视图下读不到内容)。一次性的时刻(轮次结束原因、失败的工具结果、压缩开始与结束)订阅会话的事件流 `sessions.binding(id).eventSource`,只处理订阅之后追加的事件;正在进行的压缩按事件流整个窗口算。首页读整个工作区:会话状态里新出现的 `completionUnread` 就是「后台做完」的时刻。
|
|
12
|
-
- 状态到动画按 Deepy 为 Clawd on Desk 做的主题映射(`mascot-signals.ts`),两个角色共用同一组动画名。优先级从高到低:点击与拖动的反应、出错、等待你、压缩、完成、子代理、工作、思考、空闲;工作按同时在跑的顶层会话数分三档,子代理按数目分两档。一个状态上台后一秒内不被同级或更低的换下;出错与完成各停两轮动画(4.8 秒、5.2 秒)。空闲时每二三十秒演一个空闲小节目;空闲满一分钟、其间也没有读者活动就睡着,下一次活动先演惊醒。
|
|
13
|
-
- 一套播放器,两个角色:`mascot-player.ts` 的 `createMascotPlayer` 是站位、状态机与播放的全部;角色(`whale.ts`、`crab.ts`)只给出名字、动画表、每帧时长、空闲小节目与帧图的就绪 / 失败 / 绘制 / 释放四个接口。
|
|
14
|
-
- 播放:sprite 是 overflow-hidden 的窗口,内层胶片条带整张表图、以 transform 平移对帧;循环动画整段交给 WAAPI 的 steps 关键帧(播放期间主线程无定时器、无重绘),一次性动画以 `finished` 收尾、同刻的截止定时器兜底。状态机的时钟决策汇成单个一次性定时器。减少动态效果时停在静止帧,点击与拖动的反应照演。
|
|
15
|
-
- Deepy 的画法:52×52 逻辑像素,每个逻辑像素 5 个像素(原作的原生分辨率),帧裁到能装下全部帧的最小整数逻辑像素框,八帧一行,无损调色板 PNG,由 `DEEPY_SHEETS` 登记帧数、裁切框与静止帧;页面上每个逻辑像素画成 2px。构建把每张表重建成 SVG(D38),每一格四周留 `DEEPY_GUTTER`(1 个逻辑像素)的透明边:缩小取样会越过帧格边界,没有这道边,上一格帧图底部的影子会在帧图顶端画出一条灰线。每帧 50 毫秒。
|
|
16
|
-
- 螃蟹的画法:`scripts/draw-crab.py` 按 Claude Code 螃蟹的比例逐帧画出,掏出电脑敲代码的那几帧取自 Claude Code 原版(`packages/assets/src/mascot/crab-laptop-*.png`)。52×36 格、每格 1 像素,每个动画两张表:本体(壳、侧面与眼睛)和墨色遮罩(电脑、思考泡泡、字母、音符、安全帽),八帧一行、裁到最小框,由 `CRAB_SHEETS` 登记,每帧 80 毫秒(Claude Code 的节奏)。样式表按 2 像素一格、`pixelated` 放大,遮罩填主题的三级标签墨色。
|
|
17
|
-
|
|
18
|
-
## 理由
|
|
19
|
-
|
|
20
|
-
- 一套播放器:两个角色在同样的时刻演同样的状态,换角色时行为不变;新角色只需一张动画表与一条帧图接口。
|
|
21
|
-
- 站在输入区上沿:待办、排队与目标卡片叠在输入卡片上面,站在卡片上会盖住它们右端的按钮;等待批准时站到替代面板上,摇铃正好出现在要你操作的地方。
|
|
22
|
-
- 时刻读事件流:结束原因、工具失败与压缩都原样出现在事件里,只算订阅之后追加的,打开历史会话不会误报。
|
|
23
|
-
|
|
24
|
-
## 代价
|
|
25
|
-
|
|
26
|
-
- 依赖 composer 链的替代方式、composerStack 的类名片段与会话状态、事件流的现状;订阅聊天目标让宿主在轨迹视图下也为这个会话构建聊天视图。
|
|
27
|
-
- 持续的动画每一帧改一次自己节点的样式,只牵动这一个元素的样式重算与一小块重绘。
|
|
28
|
-
- 角色的动画表必须覆盖状态机用到的全部动画名。
|
|
29
|
-
|
|
30
|
-
## 重审条件
|
|
31
|
-
|
|
32
|
-
- 宿主提供放置状态角色的插槽或统一的「会话活动」服务时改用宿主的。
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# D25. 封号彩蛋页在桌面端骑在系统标题栏那一行上
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:功能
|
|
5
|
-
- **关联**:D28
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 两个桌面平台都把窗口最上面一条交给系统层,彩蛋页不画自己的最小化 / 还原 / 关闭,顶栏骑在这条横带上:`ban-screen.ts` 打开时问一次 `desktopBand()`(D28),把横带高度与窗口控件占用的宽度写成 `--dsh-ban-caption-height` / `--dsh-ban-caption-controls`,把控件所在的一端写成 `data-dsh-ban-controls`,并给浮层打 `data-dsh-ban-titlebar`;样式表据此把字标与「退出登录」排进这一行,并在控件那一端让出宽度(Windows 在右,macOS 在左)。
|
|
10
|
-
- Windows 的横带跟着页面走:浮层打开期间把页面的 `--dsh-ban-canvas` 写到 `<body>` 的 `--dsw-specific-sidebar-fill` 上(桌面壳的探针读这个令牌,`<body>` 的 style 一变就重画横带),关闭时撤掉。只在 Windows 调用:macOS 的横带是系统的半透明材质,页面写不到。
|
|
11
|
-
- 这一行是窗口的拖拽区,浮层盖住了宿主自己的拖拽区,所以顶栏声明 `-webkit-app-region: drag`,只有「退出登录」写 `no-drag`。
|
|
12
|
-
- 这页始终画 Claude 自己的星芒加字标,与品牌偏好无关。
|
|
13
|
-
|
|
14
|
-
## 理由
|
|
15
|
-
|
|
16
|
-
- 桌面端的系统标题栏是原生层,页面既盖不住也画不上;把这一行接过来,整窗浮层才像一整个窗口。
|
|
17
|
-
|
|
18
|
-
## 代价
|
|
19
|
-
|
|
20
|
-
- 依赖 `data-windows-titlebar` 与 `data-platform="darwin"`、`--dsh-frame-top-clearance`、Windows 的 `--dsw-specific-sidebar-fill` 探针与 Window Controls Overlay API 的现状;桌面壳不再量这些时横带保持主题色,页面顶端的字标会被横带盖住。
|
|
21
|
-
|
|
22
|
-
## 重审条件
|
|
23
|
-
|
|
24
|
-
- 宿主或桌面壳给出整窗浮层的插槽,或允许浮层隐藏标题栏时,改用那个。
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# D26. 动画效果偏好:一处解析,两处读同一个值
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:运行时
|
|
5
|
-
- **关联**:D20、D24、D32
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 设置页「动画效果」有 `system` / `reduced` / `full` 三档(随系统 / 减弱 / 总是),默认随系统。解析只发生在 `packages/client/src/core/prefs.ts`:随系统时读操作系统的 `prefers-reduced-motion`,另外两档直接给出答案,结果写成 `<body data-dsh-claude-motion="reduced|full">`。偏好每次被采用时写一次;系统设置中途变化时重新解析,并让偏好监听重跑一遍。
|
|
10
|
-
- 脚本一律问 `motionReduced()`(读属性,不读媒体查询)。样式表的读者是 `theme/chrome.css` 的一条属性规则:把皮肤自己的节点(`dsh-claude-` 类名前缀与 `data-dsh-claude-` 标记)的 `animation-duration` 与 `transition-duration` 压到 0.01ms;宿主自己的过渡不在范围内。
|
|
11
|
-
- 两个例外,理由写在各自的样式表里:分段控件的滑动高亮(D20)与后台任务的转圈(`theme/sidebar.css`)在减弱时照旧播放。
|
|
12
|
-
|
|
13
|
-
## 理由
|
|
14
|
-
|
|
15
|
-
- 媒体查询是系统的事实,样式表改不了;「总是」必须在系统要求减弱时照样播放,所以答案先解析成一个文档属性,脚本与样式表读同一个值。
|
|
16
|
-
|
|
17
|
-
## 代价
|
|
18
|
-
|
|
19
|
-
- 多一条偏好与一个文档属性;系统设置变化后要等重新解析(同一事件内完成,绘制之前)。
|
|
20
|
-
|
|
21
|
-
## 重审条件
|
|
22
|
-
|
|
23
|
-
- 宿主提供统一的动效开关(例如把自己的动画也挂到一个属性上)时改接宿主的。
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# D27. 会话数字收进上下文弹层,或写在输入卡片下面自己一行
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:功能
|
|
5
|
-
- **关联**:D16、D29
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 宿主画在输入卡片下方的统计行(轮次与步数 · tok/s、总 tokens · 缓存命中)与它的两张明细面板一起隐藏(`features/composer/inline-bar.css`):统计行仍留在文档里,只为读出「性能与用量」这一档(详细 / 简洁)的结构。`context` 那一档在弹层里给出的内容按这一档分:详细档给出宿主那两组所有的行,简洁档只给四个数字——总用时(模型用时加工具调用用时,宿主没有这个合计,用皮肤自己的文案)、首 token 平均、输出速度与缓存命中,排成无标题的 2×2 网格。
|
|
10
|
-
- 数字的落点是一个选择项 `statsPosition`(设置页「对话」页的「对话统计信息位置」):`context`(默认)只把这批数字写进计量环的弹层;`inline` 把同一批数字写在输入卡片下面单独一行,末尾就是宿主画的计量环,这一行本身就是那张弹层的第二个触发口。两种取值下宿主的统计行都隐藏,弹层里的两段(宿主自己的上下文行加上这一段会话的数字明细)两种取值下都在,都由同一份数据填:这一项只决定 `inline` 那一行在不在,所以中途改选不刷新页面;取值随 `<body>` 的 `STATS_POSITION_ATTR` 上屏,隐藏统计行、把 dock 覆盖到输入行、给计量环留位置的规则都按它分档。覆盖到输入行的这一层照抄输入行自己的盒子:行高与它距容器的距离由 composer 的每次经过量出,写成 `--dsh-claude-meter-row-offset` / `--dsh-claude-meter-row-height`(`features/composer/composer.ts`),样式表读这两个值——卡片在那一行下方留的空隙与行高都属于宿主,写死会让计量环离开那一行;量在行或容器尺寸变化时做,不挂在每一帧上。给行尾留出的宽度(`--dsh-claude-meter-room`)同理按盒子算:宿主停放计量环的那个节点与它里面的触发按钮两个盒子并起来再加 8px,触发按钮比停放节点宽时行尾文字也不会被药丸底色压住。这一项不再归 `permissionsControl` 管(D29)。
|
|
11
|
-
- 那一行是宿主自己的话包住宿主自己的数(`features/context-stats/inline-stats.ts`):轮数与步数、输出速度、读入的 tokens、写回的 tokens、缓存命中率,模板取自宿主 `chat` 命名空间(`stats.counts`、`message.tokensPerSecond`、`message.turnUsage.count`、`stats.cacheHit`,分词符 `number.groupSeparator`),宿主统计行上那些图标一个都不出现;各段之间只有这一行自己的间距(10px),不插 `·` 之类的分隔符。这一行也是计量环那张弹层的第二个触发口(`stats-binding.ts` 的 `bindLine`):按一下它弹层打开、再按一下收起,回车与空格同样开合、Esc 收起,展开状态写在它自己的 `aria-expanded` 上(值取自宿主那个触发按钮)。悬停开合只归计量环那条路径:指针停在数字上不展开弹层,也不接管弹层的收起。
|
|
12
|
-
- 那一行宽过它所在的输入卡片时按台阶让位(`inline-stats.ts` 的 `fit`),顺序固定:先去掉缓存那一截的字样,再去掉轮数与步数,再去掉计量环旁的数字(环留下),然后把读入与写回两个数合成一个合计并去掉上下箭头,最后把数字折叠成 k / M / B。每上一个台阶都重新量一次「行 + 计量环」的宽度与卡片的可用宽度,宽回来时按同一顺序退回一个台阶。量宽只在数字真的变了、或视口与卡片尺寸变化时做。让位只让出宽度:第三个台阶让出计量环旁的数字时,宿主那个触发按钮保持它自己一行的高度(`features/composer/inline-bar.css`),输入区底部这一行的高度不随台阶起落变化——上方的对话按这一行排、并把末尾钉在它上面,行高一变,整屏内容就跟着上下跳。
|
|
13
|
-
- 每个数写成逐字符的 digit 组(`stats-digits.ts`):数一变就换掉它的字符 span,新 span 播 transitions.dev 的 Number pop-in(末两位延后一次;THIRD_PARTY_NOTICES.md),没变的数一个字节都不写。重画挂在投影帧上,所以每经历一个 step、流式输出的每一帧到达时动的就是那几个数。动画归皮肤自己的动效偏好管(D26),不另设开关。入场是每个字符从自己位置下方 8px 滑上来(`--dsh-claude-digit-distance`):这一行与对话共用宿主的同一个滚动容器,滑上来那几像素会越过输入区底边、把容器的可滚动高度顶大几个像素,末尾跟随随即补偿,整屏内容跟着起落;那一行因此把自己裁掉(`features/composer/inline-bar.css` 的 `overflow: clip`),入场留在行内,容器高度不受它影响。
|
|
14
|
-
- 缓存命中率与上下文占用各有一条无极色阶(`stats-ramp.ts`,取自 dsh-chat-ux 的设计):把读数落成「段标记 + 段内位置」,颜色由浏览器的 `color-mix(in oklch, …)` 在两个色标之间插出来。命中率 90%~99%(90 之前红、99 起深绿),占用 20%~40%(20 之前绿、40 起红)。计量环的填充弧(宿主那个带 `stroke-dasharray` 的圆,`composer.meter-fill`)与占用读数用同一个颜色,读数与环不会各说各话。
|
|
15
|
-
- 这是独立的功能 `contextStats`(`packages/client/src/features/context-stats/`),两种取值下都安装;宿主的两张明细对话框只在它安装期间隐藏(`<body>` 上的 `SESSION_STATS_ATTR`)。计量环按结构认:输入区底部那一行里画着圆环(`svg > circle`)的对话框触发按钮;环上的占用读数从它自己的那串文字里取(`stats-panel.ts` 的 `meterOccupancy`,composer 的留位判断读的是同一处)。
|
|
16
|
-
- 数字按数据读:`sessionStats` 与 `tokenUsage` 是宿主在整份日志上折出来的会话投影,经会话的按键读取面 `session.projections.faceOf`(宿主自己 `useProjection` 走的同一处,`features/context-stats/session-stats.ts` 经 `sessions.binding(id)` 取)。面板打开时、那一行每次重画时都按当前值填,订阅到新帧就地重写。
|
|
17
|
-
- 文字与格式与宿主一致:标签、时长模板与分词符取自宿主的 `chat` 语言命名空间(`locale.bind('chat')`);宿主的四条格式规则(`formatDuration`、`formatTokensPerSecond`、`formatExactTokens`、`formatCacheHitPercent`)在皮肤里各写一份,函数名对着来源,输出逐字符相同。
|
|
18
|
-
- 面板靠结构辨认:`[role="dialog"]`、非模态、行网格 `dl` 是它自己的直接子节点,且内部不带宿主三张统计对话框的标记(`data-session-stats-details`、`data-session-stats-usage`、`data-turn-usage-details`)。别的插件挂在同一个 `<body>` 上的弹层因此不被认成面板:dsh-better-sidebar 的子智能体节点详情小窗把 `dl` 放在画底色的卡片里面,外层那个只管定位、不画底色,数字落进那一层就会压在对话内容上。安装时清掉上一代留在别的节点上的数字块与面板标记(客户端热更新不执行上一代的拆除)。开合靠按宿主的触发按钮(`aria-expanded` 为准);悬停进出走共用的停留与宽限,并跟随「悬停打开弹层」偏好。皮肤给它打 `data-dsh-claude-context-panel`,取卡片那套一次性入场(4px、0.15 秒)。
|
|
19
|
-
- 左右位置由皮肤重写:宿主从定位点左缘放下弹层(`useStatDialog` 的 `align: 'start'`),离计量环很远。皮肤量出计量环的右缘与弹层的布局宽度(入场动画把盒子缩到 0.98,所以读 `offsetWidth`),写成 `--dsh-claude-context-panel-left` 并打上 `data-dsh-claude-context-aligned`,样式表以 `!important` 读取。盒子或视口变化时重新量。
|
|
20
|
-
- 追加的块带 `data-dsh-claude-context-stats`;投影还没应答时按真实结构占住高度(占位条按真实行的 37px 排),宿主重绘面板抹掉它时下一轮重画。占位只保留 `STATS_SKELETON_MS`(2 秒)。
|
|
21
|
-
|
|
22
|
-
## 理由
|
|
23
|
-
|
|
24
|
-
- 会话的所有数字只留一个去处;面板本体与开合仍是宿主的。读投影让插件画的是数据,既不重复渲染,也随会话实时更新。
|
|
25
|
-
- 看数字的时机因人而异:有人只在打开弹层时看,有人想一眼看到。这一行就是给后者的,用宿主自己的措辞与数值,读起来和宿主原来那一行是同一份东西。
|
|
26
|
-
- 每经历一个 step、流式输出的每一帧都有数字在动,静止地换字读起来像跳动;逐字符入场把这一次变化说成一个动作,而没变的数字保持不动。
|
|
27
|
-
|
|
28
|
-
## 代价
|
|
29
|
-
|
|
30
|
-
- 依赖两个投影单元的键名与值形状、`chat` 命名空间的键、`projections.faceOf`、统计行的结构,以及宿主的放置方式与计量环的现状。宿主不装这两个投影单元时,面板里没有这两组数字。辨认另依赖行网格挂在面板自己的直接子节点这一层结构,以及宿主三张统计对话框各自的标记;宿主把这些行包一层、或改掉那三个标记时,这段辨认要跟着核对,否则两段数字静默不出现(不抛错、不提示)。
|
|
31
|
-
- 环的颜色另依赖计量环的填充弧是那个带 `stroke-dasharray` 的圆(`composer.meter-fill`):宿主换一种画法时环不再上色,读数与那一行仍照旧。逐字符入场把行上的节点数从几段文字变成每个数字一个 span,只在数字真的变了时才重写。
|
|
32
|
-
|
|
33
|
-
## 重审条件
|
|
34
|
-
|
|
35
|
-
- 宿主把这些数字放进它自己的面板、给统计行一个可悬挂的插槽、或直接给出本地化的档位与读数色阶时,改回宿主的。
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# D28. 桌面标题带只在一处读取
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:宿主边界
|
|
5
|
-
- **关联**:D25
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- `packages/client/src/core/desktop-band.ts` 的 `desktopBand()` 是皮肤读取桌面标题带的唯一入口,一次回答三个问题:横带在不在、多高、窗口控件占哪一端,返回 `{ platform, height, controls: { side, size } }`,没有横带时返回 `null`。
|
|
10
|
-
- 判据全部取宿主的契约:标记用 `data-windows-titlebar` 与 `data-platform="darwin"`;高度用 `--dsh-frame-top-clearance`(两个平台都写;Windows 上它是 `--dsh-windows-titlebar-height` 的别名,经计算样式解开);Windows 的控件宽度以 Window Controls Overlay API 为准、取不到时回落到 140px;macOS 的控件在左端、宽度取宿主 leading seat 的起点 88px。
|
|
11
|
-
- macOS 全屏时窗口控件收起,答 `null`;`data-fullscreen` 不参与 Windows 的分支。
|
|
12
|
-
|
|
13
|
-
## 理由
|
|
14
|
-
|
|
15
|
-
- 判据写在每个读者里,两个平台的三处分歧就要在每一处重复一遍。
|
|
16
|
-
|
|
17
|
-
## 代价
|
|
18
|
-
|
|
19
|
-
- 每次调用多一次 `getComputedStyle`;`--dsh-frame-top-clearance` 缺席时回落到模块内的常数(macOS 48px、Windows 40px)。
|
|
20
|
-
|
|
21
|
-
## 重审条件
|
|
22
|
-
|
|
23
|
-
- 宿主给出统一的标题带插槽或平台无关的度量接口时改读宿主的接口。
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# D29. 功能开关:每个功能声明开关或不设开关的理由
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:运行时
|
|
5
|
-
- **关联**:D12、D42
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 每个功能在自己的清单(D42)里二选一地声明 `pref: '<偏好键>'` 或 `ungated: '<理由>'`;两个都写由类型拒绝,两个都没写或 `pref` 不是宿主半边 `PREFS_DEFAULT` 里的键由构建拒绝。
|
|
10
|
-
- 划分标准:替换或挪动宿主原有的控件与区域、或往界面上加新的可见元素的功能设开关;不设开关的是只修宿主毛病而不改变功能的(`selection`、`themeFlip`)、从属于另一个已有开关的(`copy` 与 `heroMenu` 跟随输入框改造的范围,`effort` 跟随模型选择器,`quickProviders` 是模型选择器的设置项)、只在点击时出现的(`ban`)以及设置页本身。几个功能可以共用一个开关,也可以共用一个选择项的不同取值:六个搬来的聊天区动效都声明 `chatAnimations`,`model` 与 `effort` 都声明 `modelPicker`,`contextStats`(D27)在自己的 `statsPosition` 下运行。
|
|
11
|
-
- `pref` 属于 `FEATURE_PREF_DEFAULTS` 时统一执行:启动时关着的功能不安装(选择项按清单里的 `prefValues` 判定,取值不在其中就是关);偏好每次被采用时逐个对照,关掉就运行它自己的 teardown、删掉句柄、交还接管的宿主界面,打开就重新安装,都不刷新页面。其余的 `pref`(`composerScope`、`homeLayout`、`modelPicker`、`collapseFooter`、`mascot`)由功能自己读。退役过的功能(D12)在本代内不因偏好变化装回。
|
|
12
|
-
- 设置页按分页组织(通用、外观、输入区、侧栏、对话)。布尔开关的行由拥有这个偏好的功能在清单里声明(分页、序号、标题与说明的文案键),共用一个开关的几个功能只由其中一个声明;其余的行写在 `packages/client/src/features/settings/settings-tab-*.ts`,控件在 `settings-controls.ts`,两者按序号合并;从属于另一项的行紧跟在它下面、两行之间不画分隔线,父项关闭时变灰、不能操作。
|
|
13
|
-
|
|
14
|
-
## 理由
|
|
15
|
-
|
|
16
|
-
- 新功能登记时就要回答「读者能不能关掉它」;开关的语义统一为「关掉 = 宿主原样」,每个功能的 teardown 本来就负责交还宿主界面(D12),开关只是在运行中调用它。
|
|
17
|
-
|
|
18
|
-
## 代价
|
|
19
|
-
|
|
20
|
-
- 每个功能的 teardown 必须能在运行中途执行并完整交还宿主界面,测试逐项核对开关打开与关闭两种状态。
|
|
21
|
-
|
|
22
|
-
## 重审条件
|
|
23
|
-
|
|
24
|
-
- 宿主提供插件级的功能开关或按区域停用插件的机制时,改接宿主的。
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# D3. 宿主选择器纪律
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:宿主边界
|
|
5
|
-
- **关联**:D19、D43、D44
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 选择器在 `packages/contracts/src/dom.ts` 定义一次,并在 `packages/contracts/src/table.ts` 里有条目、在功能清单的 `contracts` 里登记(D44);下面各条是挑选与写法上的纪律。
|
|
10
|
-
- 定位宿主元素按这个优先顺序:宿主的稳定契约(`[data-slot]` 插槽定位点、宿主自己的 `data-*` 属性,见 D19)→ 皮肤自己按结构写下的标记 → 带哈希类名的子串匹配。
|
|
11
|
-
- 宿主 CSS 模块的类名形如 `<哈希>_<局部名>`,子串匹配取最长的稳定片段,带上下划线:`[class*="_trailing"]`、`[class*="_row"]`。不带 `_` 的宽匹配只用于有意作用于全局的规则(全局字体、角标)。
|
|
12
|
-
- 新增子串选择器先在真实页面核对它匹配到的元素,确认没有误中。
|
|
13
|
-
- 不按界面文字匹配宿主元素;需要区分的宿主控件由皮肤按结构打标记(D19)。
|
|
14
|
-
- 不覆盖会话进行时 `[class*="viewArea"]` 的布局约定(`flex: 1 0 auto; min-height: auto`),它负责把输入框固定在滚动区底部。
|
|
15
|
-
- 全局链接颜色排除带 `_linkButton` 的链接:它们是自带文字颜色的填充或描边按钮(例如设置页的「充值」)。
|
|
16
|
-
|
|
17
|
-
## 理由
|
|
18
|
-
|
|
19
|
-
- 宿主类名里只有局部名稳定。
|
|
20
|
-
- 短片段与界面文字都出过实际问题:`row` 命中 `…_grow` 曾把输入框高度锁在 28px,`rail` 命中 `trailing`,`tag` 命中 `stage`;界面文字随语言与宿主版本变化。
|
|
21
|
-
|
|
22
|
-
## 代价
|
|
23
|
-
|
|
24
|
-
- 选择器更长;按结构写的标记要等皮肤的刷新写入(同一帧、绘制之前)。
|
|
25
|
-
|
|
26
|
-
## 重审条件
|
|
27
|
-
|
|
28
|
-
- 宿主为这些区域提供稳定的 `data-*` 契约时全面改读宿主的属性。
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# D30. 配色与字体可以交给宿主
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:宿主边界
|
|
5
|
-
- **关联**:D7、D16、D50、D51
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 「配色」与「字体」两项偏好各有 `claude`(默认)与 `host` 两档,写成 `<body data-dsh-claude-palette>` 与 `<body data-dsh-claude-typeface>`。
|
|
10
|
-
- 每条写宿主令牌的规则都挂在 Claude 一档上:写 `--dsw-` 颜色令牌的规则挂配色门,写 `--dsw-font-*` 的规则挂字体门,漏挂即构建失败。把皮肤的画布刷到宿主框架上的规则(`<html>`、`<body>`、`#root`、侧栏、对话列)同样挂在 Claude 一档上。壁纸插件在播壁纸时那几处外壳不画,整窗铺一层画布色遮罩,见 D61。
|
|
11
|
-
- 皮肤自己的节点只读私有令牌(`--dsh-claude-*`)与宿主令牌。宿主一档把每个私有令牌改成宿主令牌的别名(对照表在 `docs/STYLE.md` 的「Two palettes」),Claude 一档定义的每个私有令牌在宿主一档都必须有别名。
|
|
12
|
-
- 卡片类的面(弹层、账号弹层、搜索面板)用宿主的浮层令牌 `--dsw-alias-bg-overlay`,宿主一档下给共用弹层外壳加背景模糊(D16)。皮肤自造的字体令牌是私有的 `--dsh-claude-font-serif` / `-prose` / `-code` / `-brand`,宿主一档回落到宿主的 `--dsw-font-family` 与 `--ds-font-family-code`。
|
|
13
|
-
- 推理强度滑杆顶档的紫色、封号彩蛋页复刻的 Claude 页面在两档下都保留原色;半透明的中性悬停色与用量面板的数据配色写成字面值。
|
|
14
|
-
|
|
15
|
-
## 理由
|
|
16
|
-
|
|
17
|
-
- 「跟随宿主」时皮肤不和宿主或其他主题插件(例如 dsh-wallpaper-engine 把画布清空、把浮层改成玻璃)争同一组令牌,结果不取决于样式表的加载先后;皮肤自己的面读别名,跟着别人画的颜色走。
|
|
18
|
-
|
|
19
|
-
## 代价
|
|
20
|
-
|
|
21
|
-
- Claude 一档的令牌规则多一个属性选择器;新的私有颜色令牌在 `packages/client/src/theme/tokens.json` 里要同时写 Claude 的值与宿主一档的别名(D51);功能样式表里画面的颜色要读令牌,不能写字面值。
|
|
22
|
-
|
|
23
|
-
## 重审条件
|
|
24
|
-
|
|
25
|
-
- 宿主提供主题层级或令牌分层机制(多个主题插件按层叠加)时改用宿主的。
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# D31. Windows 桌面顶栏透明,各列延伸到窗口顶端
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:宿主边界
|
|
5
|
-
- **关联**:D3
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- Windows 桌面端(`html[data-windows-titlebar]`)的顶栏没有自己的颜色。宿主在各列上方留出的一行(框架的 `padding-top`,由框架本身与它的 `::before` 拖动条刷成侧栏底色)改为透明,侧栏与对话列各用一层向上偏移顶栏高度的外阴影(`box-shadow`)把自己的底色延伸进这一行。
|
|
10
|
-
- 侧栏右边的分隔线由侧栏的拖动把手(`[data-side="sidebar"]`)的 `::before` 向上画过这一行;宿主的 `--dsh-windows-content-radius` 设为 0,对话列与全屏文件面板左上角不再有圆角。
|
|
11
|
-
- 窗口按钮下的底色由系统绘制,颜色来自宿主桌面端 preload 放在 `<body>` 下的隐藏探针 `<span>`(读 `--dsw-specific-sidebar-fill`,交给 `setTitleBarOverlay`);皮肤把这个探针的 `background-color` 设为透明,按钮下露出页面本身,弹窗遮罩盖上来时按钮区随页面一起变暗。实测透明后按钮的悬停高亮照常,系统按图标颜色叠一层约一成浓度的颜色。
|
|
12
|
-
|
|
13
|
-
## 理由
|
|
14
|
-
|
|
15
|
-
- Claude Code 桌面端的侧栏与主界面都通到窗口顶端,一条分隔线从上到下。外阴影只画在列的盒子外面,列里的内容和宿主的布局都不动,拖动列宽与收起动画时自动跟着列走。
|
|
16
|
-
|
|
17
|
-
## 代价
|
|
18
|
-
|
|
19
|
-
- 认探针靠它内联样式里的 `--dsw-specific-sidebar-fill` 子串,属于宿主 preload 的内部写法、不是公开契约(D3 的例外,记在这里);宿主改了写法,按钮区退回宿主的底色,不会出错。
|
|
20
|
-
- 收起侧栏时宿主立即移除拖动把手,顶栏里那段分隔线比列宽动画先消失。
|
|
21
|
-
|
|
22
|
-
## 重审条件
|
|
23
|
-
|
|
24
|
-
- 宿主的桌面端提供顶栏透明的选项或按钮区底色的公开接口时改用宿主的。
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# D32. 搬来的聊天区交互:自己的前缀、自己的偏好、对上游让位
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:功能
|
|
5
|
-
- **关联**:D26、D29、D40、D41、D44、D62
|
|
6
|
-
- **迁移**:聊天区契约表并入契约模块(D44,待实施)
|
|
7
|
-
|
|
8
|
-
## 决定
|
|
9
|
-
|
|
10
|
-
- 从 dsh-chat-ux 搬入的聊天区交互(token 淡入、思考行与过程组自动开合、卷帘门过渡、增强跟随、组体跟随、文件变更行、插入符动效、聊天气泡动效、过程组标题上的计数、说明模型正在做什么的那一行)分目录放在 `chat-follow`、`chat-fold`、`chat-reveal`、`chat-files`、`chat-send`、`chat-wait`、`caret`。推理期间思考行里的回放窗口来自 transitions.dev 的「Reasoning stream」(`chat-fold` 的 `reasoning-stream`,裁剪的是宿主自己那个推理正文槽位),与状态行文字交替、计数数字滚动一样属于搬来的动效。
|
|
11
|
-
- 它们只读宿主自己写的属性;聊天区的整份宿主契约(流式容器、工具调用行、过程组、思考行的推理正文槽位、会话滚动条、输入框与回显气泡等)集中在 `packages/contracts/src/dom.ts` 一张表里。
|
|
12
|
-
- 功能自己写出的标记(DOM 标记、类名、`::highlight()` 名称、自定义属性、关键帧名称)一律用 `data-dsh-claude-` 前缀,与上游包同时装着时两套实现读不到对方的状态。
|
|
13
|
-
- 不止读标记的三处:
|
|
14
|
-
- 文件变更行接管宿主 `tool.call.toolview` 座位上的 `edit` 与 `write` 两个键位(优先级 -1,低于宿主自己那两个),改动派生与宿主的卡片模型只差「不排除子调用」这一条;同一个文件在同一个过程组里的多次改动读成一行,规则见 D62。
|
|
15
|
-
- 聊天气泡动效在 `<body>` 末尾挂一个临时替身,带 `inert` 与 `aria-hidden`,摘掉身份标记但保留样式钩子;飞行期间给真实那一行写标记把它藏起来(保留布局盒),到达后真实行先显示、替身淡出。
|
|
16
|
-
- 增强跟随在流式输出期间接管会话滚动条的跟随:宿主每次内容增长都把位置写到最底,跟随在同一帧绘制之前把这次位移撤回、交给滚动主人(D41)滑过去。宿主写这一下的地方是它挂上消息列时新建的 ResizeObserver(观察消息列、会话滚动条、输入框座位),浏览器按建立先后调用回调,所以跟随的观察者每遇到一条新的消息列就重建一次,排在宿主之后、观察同样三个元素。跟随期间把宿主自己的「回到底部」按钮临时藏起来(只写自己的标记,宿主的状态不动);读者自己发出的消息到达时跟随让开一段,宿主把新消息滚进视野的那一下照旧一步到位。
|
|
17
|
-
- 设置:这些功能共用皮肤自己的偏好条目与设置页,不动上游 `dsh-chat-ux` 的配置。对话区各项共用一个开关 `chatAnimations`(「聊天区动画效果」):开启时安装,关闭时这些功能一起卸载、各自把界面交还宿主。各功能的清单用 `pref` 声明自己挂在哪个偏好上,用 `prefValues` 声明一项选择偏好下自己运行的取值(D42);插入符动效是自己的三选一(`caretMotion`)。随偏好出现的样式规则由功能把标记写到 `<body>` 上(如 `data-dsh-claude-chat-follow`)。
|
|
18
|
-
- 对上游让位:页面装着上游时这些功能整体让位(`packages/client/src/shared/peer-plugin.ts` 读宿主启动图里的条目,也读上游留下的样式表节点);设置页把两行控件禁用、保留读者的值,下面以强调色写明由谁接管。让位由清单的 `yieldsTo` 声明(D42),入口统一执行:上游在页面上时这些功能不安装,上游中途装卸会立刻卸下或装回(`<head>` 的观察重跑偏好监听),功能内部不再自己判断。
|
|
19
|
-
- 动画效果读 `motionReduced()`(D26),样式表一侧以 `<body>` 上的动画属性为门控。
|
|
20
|
-
- 没有搬的一处:上游会把网页端「工作步骤展示」的缺省档位补成「标准」。本仓库不代写宿主 `ui-chat` 的设置;网页端默认档位(「详细」)下组体不封顶,自动开合与组体跟随在那两处不发生,读者选到「标准」或「简洁」后生效,README 写明这一点。
|
|
21
|
-
|
|
22
|
-
## 理由
|
|
23
|
-
|
|
24
|
-
- 上游的属性名原样保留时,两套实现同时装着会互相认领对方的标记;只换前缀不足以共存——两套卷帘门会互相回放同一次点击,两套自动开合各点同一个开关,文件变更行两边同优先级注册同一键位会抛错,所以必须整体让位。
|
|
25
|
-
- 跟随的观察者若在安装时建好一直沿用,就排在宿主之前:宿主那一下每帧都画出来,下一批文字到达时跟随又把已经画出来的位置拉回去,画面忽而上跳、忽而下掉。
|
|
26
|
-
|
|
27
|
-
## 代价
|
|
28
|
-
|
|
29
|
-
- 上游的更新要人工对照搬运;行为由本仓库自己的测试覆盖,包括一个上游在场的让位用例。
|
|
30
|
-
|
|
31
|
-
## 重审条件
|
|
32
|
-
|
|
33
|
-
- 上游把这几项拆成可依赖的库并公开稳定接口,或宿主自己把这些交互收成正式能力时,改为依赖而不搬运。
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# D33. 皮肤样式表带上本包自己的归属标记,兄弟包的样式表不认领
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:宿主边界
|
|
5
|
-
- **关联**:D32、D36
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 皮肤的样式表挂载时写两个标记:`data-plugin` 是本包的包名 `dsh-claude-style`,`data-plugin-css` 是本包的键 `dsh-claude-style/client.css`(`PACKAGE_NAME` 与 `STYLE_PLUGIN_CSS`)。
|
|
10
|
-
- 挂载在 `packages/client/src/core/stylesheet.ts` 的 `mountStylesheet()`:同名元素已存在时就地刷新(重写标记,内容不同才重写内容),没有才新建并挂到 `<head>`;拆卸只在自己仍是最后一代挂载者时移除。
|
|
11
|
-
- `parkForeignSheets()` 把文档中所有未打标签、又不是本包那张的 `<style>` 标成 `dsh-claude-style/foreign-sheet`(`FOREIGN_SHEET_TAG`)。它定义在 `packages/client/src/core/stylesheet.ts`,在两个时机执行:`packages/client/src/entry.ts` 的模块作用域里,以及随页面一直活着的 `<head>` 观察者回调(`packages/client/src/shared/peer-plugin.ts` 的 `checkPeerPresence`)。
|
|
12
|
-
- `data-plugin` 的值、`package.json` 的包名、产物里 `__ModuleLoader__.load` 的 id、profile 里的条目名四者必须一致;构建从 `package.json` 读取包名写成加载器的 id(D36)。
|
|
13
|
-
|
|
14
|
-
## 理由
|
|
15
|
-
|
|
16
|
-
- 宿主客户端模块系统按属性给样式表记账(`claimStyles` 与 `removeOwnedStyles`):某个包物化时,把文档里所有未打标签的 `<style>` 盖上自己的 id;这个包重载、换代或被剪除时,删掉带这个 id 的全部标签。没有标记的样式表会被下一个物化的兄弟包认领,再随它的重载被删掉,页面继续运行,规则却整份消失。
|
|
17
|
-
- 两个时机各管一段:认领扫描在本包工厂返回之后跑,模块作用域里的那次是唯一赶在它前面的动作;本包之后才出现的样式表(兄弟包在自己的 `apply()` 或懒加载块里挂,`dsh-meme` 与 `dsh-chat-import` 都这样)只有 `<head>` 观察者能在被认领之前看到——观察者回调是微任务,物化是之后的任务。
|
|
18
|
-
|
|
19
|
-
## 代价
|
|
20
|
-
|
|
21
|
-
- 本包的样式表由宿主按这个 id 管理:重载时宿主先删掉它,再由新的 `apply()` 重新挂上,一次重载失败会让页面回到宿主自己的外观。
|
|
22
|
-
- 被标成 `foreign-sheet` 的样式表此后不再被宿主收走:指望宿主在重载时替它收走旧表的插件,会留下新旧两份并存。
|
|
23
|
-
|
|
24
|
-
## 重审条件
|
|
25
|
-
|
|
26
|
-
- 宿主提供公开的客户端样式声明方式(在包清单里声明 CSS、由宿主带标记注入),或把认领扫描收窄到该次物化真正新建的标签时,改用它。
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# D34. 对话导航:皮肤画导航条,跳转按宿主自己的刻度
|
|
2
|
-
|
|
3
|
-
- **状态**:部分实施
|
|
4
|
-
- **分组**:功能
|
|
5
|
-
- **关联**:D16、D41、D44
|
|
6
|
-
- **迁移**:滚动位置的写入改经滚动唯一主人(D41,待实施)
|
|
7
|
-
|
|
8
|
-
## 决定
|
|
9
|
-
|
|
10
|
-
- 宿主自带轮次导航条(ui-chat 的 TurnNavigator):每一轮一个刻度、间距 10px,跟随阅读位置,点刻度跳到那一轮(不在已加载窗口里的先翻页再定位),`turnOutline` 投影让它一进会话就覆盖全部轮次。皮肤在同一个插槽(导航条的父元素)里放自己的导航条,刻度间距与展开后的列表行高相同(24px);宿主的导航条打上 `data-dsh-claude-turn-nav-replaced`,设为不可见但保留布局。位置照搬宿主给它导航条的规则(右侧 12px、竖直居中于对话顶部到输入区之间、最高为这一段减 64px、最多十轮),窄对话下隐藏用同一个容器查询,摆放不读布局。
|
|
11
|
-
- 指针碰到皮肤导航条立刻展开一张卡片(共用弹层外壳,D16),每一轮一行写开启这一轮的提问,每一行正好落在它的刻度上:卡片列表的盒子与导航条的盒子重合,列表滚动距离等于刻度轨道的滚动距离,行尾短横线的右端与刻度的右端在同一条竖线上。卡片只淡入淡出,不用外壳的上浮动画;打开没有停留时间,关闭沿用共用宽限。卡片挂在导航条所在的插槽里(对话栏 `centerCol` 之内),指针停在卡片上仍算停在对话栏上。
|
|
12
|
-
- 轮次列表照宿主的 `mergeTurnRailItems` 合并两个来源:聊天快照的已加载轮次(`navigation.items()`)与 `turnOutline` 投影;两边都有的一轮用已加载那边的提问,提问为空才用投影的;没有提问的一轮用宿主 `chat` 文案表里的「第 {turn} 轮」。不在已加载窗口里的轮次刻度画得更淡。
|
|
13
|
-
- 阅读位置与跳转交给宿主的导航条:当前刻度读宿主的 `aria-current` 刻度;跳转按下宿主对应位置的刻度按钮。宿主的刻度按钮只带列表位置(`data-index`)并且只渲染滚动位置附近的那些,所以只有宿主导航条的刻度数(由滚动内容高度 `数量 × 10 + 2` 读出)等于合并出的轮次数时才按,刻度没渲染时先把宿主导航条滚到那里;30 帧内仍没出现就抛错。
|
|
14
|
-
- 已加载的一轮由宿主在按下刻度的同一时刻一帧写到位:皮肤在这一帧绘制之前把位置放回原处,再经滚动主人(D41)以最高优先级滑到宿主写的位置,只滑最后 480px。别人在滑行途中改了位置时滑行立即让出;不在已加载窗口里的一轮由宿主加载完之后定位,不加滑行;减弱动效时也不滑。
|
|
15
|
-
- Alt+↑ / Alt+↓:焦点在有草稿的输入框、或有模态框与菜单占着前景时不接管;连按在 1.2 秒内从上一次去的那一轮接着走。页面上有 dsh-plugin-msg-nav(它的样式表在 `<head>` 里)时这两个键留给它。跳到的那一轮第一行出现时打上落点标记,画一条 1.4 秒淡出的强调色横线;减弱动效时横线静止显示同样的时间。
|
|
16
|
-
- 监听挂在皮肤自己的节点上,随节点去掉;滚动或窗口变化合成每帧一次去读宿主的当前刻度,刻度元素与位置都没变时不读布局。
|
|
17
|
-
|
|
18
|
-
## 理由
|
|
19
|
-
|
|
20
|
-
- 宿主的跳转(翻页、定位、阅读位置)完整可靠,按它的按钮就整套复用;展开的列表要和刻度重合,两边间距必须相同,而宿主的刻度由虚拟列表按固定 10px 摆放、改不了,所以导航条由皮肤自己画,宿主的那条留着只为被按。
|
|
21
|
-
|
|
22
|
-
## 代价
|
|
23
|
-
|
|
24
|
-
- 依赖宿主导航条的结构(插槽里唯一的 `nav`、它的 `_scroller`、`data-index` / `aria-current` 刻度、10px 间距与 6px 端距)、摆放规则与容器查询,以及 `navigation` 与 `turnOutline` 的形状;宿主改了这些,按键会在 30 帧后报错、位置会偏。
|
|
25
|
-
- 宿主的刻度按钮在宿主导航条不可见时失去键盘焦点,皮肤导航条不接受焦点,键盘读者用 Alt+↑ / Alt+↓。
|
|
26
|
-
|
|
27
|
-
## 重审条件
|
|
28
|
-
|
|
29
|
-
- 宿主开放「跳到某一轮」的接口或刻度上带轮次号时改用接口;宿主导航条的间距可配置时改回重绘宿主的导航条。
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# D35. 选区样式的作用范围
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:运行时
|
|
5
|
-
|
|
6
|
-
## 决定
|
|
7
|
-
|
|
8
|
-
- `::selection` 的两条底色规则与内联代码片的四条反相规则只作用于对话正文(`[data-chat-flow]`)与草稿编辑器(`[data-lexical-editor]`)里的块级元素(`p`、`li`、`td`、`th`、`blockquote`、`pre`、`h1`–`h6`);其余区域(侧栏、设置页、弹层等)的选区交还宿主自己的样式。
|
|
9
|
-
- 滚动条保持原样:`*` 上的 `scrollbar-width` / `scrollbar-color` 与全局四条 `::-webkit-scrollbar` 规则(8px 圆角拇指、悬停变色)。
|
|
10
|
-
|
|
11
|
-
## 理由
|
|
12
|
-
|
|
13
|
-
- 切换会话与新建会话的卡顿来自一处全文档的 `::selection`:Blink 在每次样式重算里为每个元素计算选区样式。实测一次「切到另一会话再切回」的样式重算合计:全文档作用域 1.6–2.7 s,收窄到正文块级元素 0.3–0.8 s,不装皮肤约 0.05 s;单次切换从 0.9–2.4 s 降到 0.11–0.51 s。
|
|
14
|
-
- 选区样式会继承,写在块级元素上的规则覆盖它的行内后代,正文里的选区外观不变。
|
|
15
|
-
- 滚动条那四条规则做过同样的对照测量(切换重算、滚动帧距各三组),没有可测差异。
|
|
16
|
-
|
|
17
|
-
## 代价
|
|
18
|
-
|
|
19
|
-
- 侧栏、设置页与弹层里的选区回到宿主默认的半透明洗色;以后需要自定义选区的区域,要加进这几条规则的作用范围。
|
|
20
|
-
|
|
21
|
-
## 重审条件
|
|
22
|
-
|
|
23
|
-
- 宿主提供选区颜色的公开主题接口时改用宿主的;或 Blink 不再为每个元素计算全文档 `::selection` 时放开作用范围。
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# D36. TypeScript 与 ES 模块,esbuild 打出单文件产物
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:构建与源码
|
|
5
|
-
- **关联**:取代 D1;D39、D46、D51
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 源码全部是 TypeScript 与 ES 模块,用显式的 `import` / `export` 表达依赖;`tsconfig.json` 打开 `strict`。构建先跑 `tsc`,再查打包后的导入图:漏引、循环依赖、使用尚未初始化的常量都在构建时报错,一个没有任何模块导入的源文件也报错。
|
|
10
|
-
- 宿主服务与快照里的值在契约模块(D44)给出类型之前统一写成 `HostValue`;其余类型照常写全。
|
|
11
|
-
- esbuild 打包,`react` 与宿主包标为 external,产物是满足 DSH 加载器的单文件:CommonJS 主体包进 `__ModuleLoader__.load` 的工厂,id 与包名一致(D33),压缩,并附 `lib/client.js.map`。零运行时依赖由 external 设定保证。清单写 `load: 'deferred'` 的功能不在这个单文件里,各自成块(D39)。
|
|
12
|
-
- 构建时才有的数据(样式表文本、厂商组合标、帧图的内容戳与内联地址、构建编号)经一个生成模块 `virtual:dsh-claude-style/generated` 进入源码,类型写在 `packages/client/src/generated.d.ts`。构建编号是产物内容哈希的前 12 位(D19),打包后写进等长的占位,source map 因此不受影响。
|
|
13
|
-
- 原有构建检查各有去处:
|
|
14
|
-
- 「每个源文件都在清单里」由模块导入图保证。
|
|
15
|
-
- 构建期常量直接 import,样式表不带占位;输入框门控(D4)、`:has()` 位置(D9)、配色与字体门(D30)与皮肤属性的检查由 `scripts/css.mjs` 在语法树上执行(D51)。
|
|
16
|
-
- 模型文案的结构由 `packages/client/data/model-descriptions.schema.json`(JSON Schema)声明,构建用 Ajv 校验;文档内的引用、厂商组合标是否存在与语言数由构建补查(D5)。
|
|
17
|
-
- 吉祥物帧图与登记表的对应关系、帧数、裁切框、静止帧的合法性交给 D38 的资源清单生成。
|
|
18
|
-
- 功能注册表与开关声明(D29)由功能清单的类型(D42)保证,在那之前由构建核对 `FEATURES` 表与各功能的主模块。
|
|
19
|
-
- 两半偏好默认值的逐键核对随 `packages/contracts` 的单一声明取消(D10、D46)。
|
|
20
|
-
- 产物仍检查能被解析。
|
|
21
|
-
|
|
22
|
-
## 理由
|
|
23
|
-
|
|
24
|
-
- DSH 加载器没有相对 `require`、没有资产 URL,这只限制产物的形态;构建时用打包器同样能产出一个零依赖的单文件。
|
|
25
|
-
- 共享作用域靠命名前缀与片段顺序维持依赖,片段数到几十个、源码到几万行时已经很难看清;`build.mjs` 也越长越像一个手写的打包器。
|
|
26
|
-
|
|
27
|
-
## 代价
|
|
28
|
-
|
|
29
|
-
- 开发依赖多了 TypeScript、esbuild、Ajv 与 React 的类型包。
|
|
30
|
-
- 产物里的名字被压缩,排查线上问题依赖 source map。
|
|
31
|
-
- `HostValue` 在 D44 之前不受类型检查,宿主结构的改变仍要靠冒烟与真实实例发现。
|
|
32
|
-
|
|
33
|
-
## 重审条件
|
|
34
|
-
|
|
35
|
-
- DSH 加载器原生支持 ES 模块的相对导入与资产 URL 时,产物不再需要打成单文件,改为按模块与资源分发。
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# D38. 资源只有一条路径:内容哈希清单与通用资产路由
|
|
2
|
-
|
|
3
|
-
- **状态**:已实施
|
|
4
|
-
- **分组**:构建与源码
|
|
5
|
-
- **关联**:承接 D24 的帧图、D36 的构建;D11、D12、D39、D46
|
|
6
|
-
|
|
7
|
-
## 决定
|
|
8
|
-
|
|
9
|
-
- 图片与图形资源都在 `packages/assets/src/` 下,构建(`packages/assets/assets.mjs`)为每个文件算出内容哈希(sha256 前 12 位),并由文件自己的字节数决定交付方式:不超过阈值的(16 KiB)内联进产物——图片写成数据地址,厂商组合标写成标记文本,因为选择器用 `innerHTML` 把它盖进行里,好让单色层继承那一行的颜色;超过阈值的写到 `lib/assets/<哈希>.<扩展名>`,由宿主半边一条通用路由 `/dsh-claude-style/assets/<哈希>.<扩展名>` 提供。
|
|
10
|
-
- 路由只认清单里出现过的名字:构建把清单写在 `lib/assets/manifest.json`,请求名必须命中它,因此资源目录之外什么都读不到。名字里带着内容哈希,可以长期缓存;文本资源按 brotli 预压缩存放,不接受 brotli 的客户端由路由解压后送出。延后功能的块与它的 source map(D39)也登记在这份清单里、由同一条路由提供,总是走路由,各自的名字取自身内容的哈希。
|
|
11
|
-
- 帧图与动画登记表的对应关系、帧数、裁切框、静止帧的合法性在生成清单时检查,另外核对表图自己的像素:裁切框必须整除表图,格子数必须装得下登记的帧数。没有任何登记认领的资源构建失败。
|
|
12
|
-
- Deepy 的帧图在构建期转成矢量(`vectorizeSheet`):按裁切框把每帧摆成一格、四周留 `DEEPY_GUTTER` 个逻辑像素的透明边,逐行取同色游程,把同列同宽的同色游程向下并成矩形,每种颜色一条 path。产出的 SVG 就是浏览器播放的图形;表图 PNG 只作为构建的输入,不进 `lib/`。
|
|
13
|
-
- 浏览器端的画布转换、Cache API 缓存与两处相关的 catch 随之删除(D12);运行时只剩一次图片加载。
|
|
14
|
-
|
|
15
|
-
## 理由
|
|
16
|
-
|
|
17
|
-
- 两条交付路径各自合理,但每加一种资源都要先判断走哪一条;路径的选择应该由大小自动决定。
|
|
18
|
-
- 浏览器端重建矢量的唯一目的是播放矢量图;构建时做完,运行时只剩加载。
|
|
19
|
-
- 实测 18 张表:PNG 原图共 426 KB,构建期转出的矢量未压缩 3.4 MB、brotli 后 658 KB(每张 12–66 KB)。同样格子布局的补边 PNG 是 1.27 MB,而直接送未补边的 PNG 会退回缩小取样越过帧格边界画出的灰线;矢量比补边 PNG 小一半,观感与之前播放的完全一致。
|
|
20
|
-
|
|
21
|
-
## 代价
|
|
22
|
-
|
|
23
|
-
- 宿主半边多一条通用路由;更新插件后没有重启宿主时,旧的宿主半边没有这条路由,矢量读不到(对应的动画缺席,控制台说一次)。
|
|
24
|
-
- 构建多一步矢量化与压缩;随包携带的帧图从 426 KB 增到 658 KB。
|
|
25
|
-
|
|
26
|
-
## 重审条件
|
|
27
|
-
|
|
28
|
-
- DSH 加载器支持资产 URL 时,资源改随客户端包分发;块随 D39 的重审条件改走加载器之后,删除这条路由。
|