dsh-claude-style 0.6.4 → 0.7.1
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 +74 -0
- package/README.en.md +4 -2
- package/README.md +4 -2
- package/docs/STYLE.md +169 -2
- package/docs/architecture.md +26 -2
- package/lib/client.js +5742 -3077
- package/lib/index.js +232 -1
- package/lib/model-descriptions.json +140 -4
- package/lib/usage.js +659 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,80 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `dsh-claude-style` are documented here, newest first.
|
|
4
4
|
|
|
5
|
+
## [0.7.1] - 2026-09-25
|
|
6
|
+
|
|
7
|
+
[中文](#cn-0.7.1) | [English](#en-0.7.1)
|
|
8
|
+
|
|
9
|
+
<h3 id="cn-0.7.1">体验优化</h3>
|
|
10
|
+
|
|
11
|
+
- **默认首页版面改为工作台**:没在设置里选过版面时,新会话页用工作台版面——问候在左上、输入卡片贴住窗口底边、中间是用量面板、卡片上沿站着像素螃蟹,也就是 Claude Code 自己的首页。经典版面仍可在设置页随时切回,已经选过版面的不受影响。
|
|
12
|
+
|
|
13
|
+
<h3 id="en-0.7.1">Improvements</h3>
|
|
14
|
+
|
|
15
|
+
- **Studio is the default home layout**: without a layout chosen in the settings, the new-conversation page opens in the Studio layout — the greeting at the top left, the composer on the window's bottom edge, the usage panel in between and the pixel crab on the card, Claude Code's own home. Classic stays one switch away in the settings page, and a layout already chosen is kept.
|
|
16
|
+
|
|
17
|
+
**Full Changelog**: [v0.7.0...v0.7.1](https://github.com/Nwflower/dsh-claude-style/compare/v0.7.0...v0.7.1)
|
|
18
|
+
|
|
19
|
+
## [0.7.0] - 2026-09-25
|
|
20
|
+
|
|
21
|
+
[中文](#cn-0.7.0) | [English](#en-0.7.0)
|
|
22
|
+
|
|
23
|
+
<h3 id="cn-0.7.0">新增功能</h3>
|
|
24
|
+
|
|
25
|
+
- **两套首页版面,设置页随时切换**:新增「首页版面」设置项。**经典**沿用居中大标题加输入卡片;**工作台**把标题移到左上角并固定为一句「What's up next, 用户名?」(无衬线小字,与品牌标记同一行),输入框改用对话内的单行样式贴住窗口底部、上方一行细边框上下文胶囊,标题与一块 480px 窄栏用量面板贴着输入框左缘排布——概览页签是六个数字格(会话数、消息数、Token 总量、活跃天数、高峰时段、最常用模型)与 26 周按天热力图(等分列方格、蓝色数据色阶);模型页签是每天一根按模型颜色自下而上堆叠的柱子,下面跟一份模型排行(色块、模型名、输入与输出、占比,超过六行折成「再显示 N 个」,展开后末尾是「收起」),柱与色块共用同一套按名次取色的蓝色色阶;右上角的「全部 / 30 天 / 7 天」范围切换过滤数字格(含高峰时段)、趣味行与模型排行,热力图与柱状图保持自身窗口;所选范围内的用量超过一本书时,热力图下方出现倍数趣味行:书从《动物农场》到《追忆似水年华》共十一本,每次回到新会话页随机换一本,范围内用量还不到这本书时退到已经超过的最长那本。面板只在新会话页出现,进入对话后自动消失。
|
|
26
|
+
- **用量数据在本机汇总**:新增只读路由 `GET /dsh-claude-style/usage`(与用户名路由同一同源护栏)。装了 `dsh-cost-meter` 时直接读它的账本缓存(只读),否则读本机会话日志自行汇总,并按日志文件增量缓存——冷启动首次约两秒,之后每次启动约 3 毫秒。两种来源都给出每个模型的四类 token 与每天按模型拆分的 token(`models` 与 `days[].models`),模型页签的柱状图与排行读它们;账本按「提供方:模型」记账,同一模型经不同提供方的用量合成一行。账本不记小时,读账本时其余数字先出,高峰时段随后由本机会话日志按天算出补上。汇总不可用(旧宿主半边或汇总失败)时,面板改用宿主会话列表自带的投影值出数。
|
|
27
|
+
- **面板先画骨架再填数**:数字到达前先画好外框、占位数字、空热力格与模型行的占位条,到达后只替换文字、格子颜色与条长,整个版面不跳动;取不到数据的格子画破折号,不画零。
|
|
28
|
+
- **输入卡片上的像素螃蟹**:工作台版面的新会话页上(经典版面没有),输入卡片上沿靠右站着 Claude Code 的像素螃蟹。点它一下、指针从它身上移开时、以及页面开着时每隔 25–45 秒,它照 Claude Code 的样子钓一次鱼:半转身眨眼,举竿甩到卡片边上,侧身钓一会儿,收竿转回正面,约三秒;系统要求减少动态效果时(例如 Windows 关掉了「在 Windows 中显示动画」),只有点它才播放。只有螃蟹本身接收指针,鱼竿挥动的空白处不挡点击。
|
|
29
|
+
- **热力图的日期提示**:指针停在热力图的某一天上,立刻浮出 Claude Code 样式的深色提示,写着日期和当天的消息数(如「9月9日 — 15,955」);靠两端的几列提示贴着格子外沿,不会伸出面板。会话列表兜底来源没有按天的消息数,此时提示写当天的 Token。概览的数字格同时改用 Claude Code 的叫法:消息数、Token 总量、最常用模型(模型名不加粗)。
|
|
30
|
+
- **经典首页的问候更多了**:经典版面的大标题不再每个时段只有一句,早上、午间、下午、晚上、深夜各有一组问候,另有几句不分时段;每次回到新会话页随机换一句,同一次停留里不会跳动。
|
|
31
|
+
- **接入 HDSL 启动器的账号信息**:由 HDSL 启动的实例,昵称与头像会优先取启动器里的账号。昵称按「自定义昵称 → 官方账号昵称 → HDSL 昵称 → 上次探测到的系统用户名 → 系统用户名 → `User`」回退,头像按「官方账号头像 → HDSL 头像 → Claude 徽标」回退。宿主半边新增两条只读私有路由,与用户名路由同级过宿主请求栅栏:`GET /dsh-claude-style/hdsl` 回账号元数据(不含头像文件的绝对路径),`GET /dsh-claude-style/hdsl-skin.png` 回头像图片。契约版本读不到或不认识时整组忽略;头像文件被删除时回落到徽标。
|
|
32
|
+
|
|
33
|
+
<h3 id="en-0.7.0">New Features</h3>
|
|
34
|
+
|
|
35
|
+
- **Two home layouts, switchable in the settings page**: a new Home layout preference. **Classic** keeps the centered headline over the composer card; **Studio** pins the greeting to the top left as one fixed line ("What's up next, <user>?", in the sans UI face on the brand mark's line), docks the composer in the conversation's single-line form at the window's bottom edge with a row of hairline context chips above it, and sets the greeting and a 480px usage panel against the composer's left edge — an Overview tab with six stat cells (sessions, messages, total tokens, active days, peak hour, favorite model) and a twenty-six-week per-day heat grid of square cells in equal columns, and a Models tab stacking each day's per-model tokens into one bar over a ranked model list (swatch, model name, input and output, share; past six rows it folds behind a "show more" row, and the open list ends in a "show less" row), both reading the same rank-ordered blue ramp. The All / 30d / 7d range pills filter the tiles (the peak hour included), the multiplier line and the model list while the grid and the chart keep their own windows, and once the picked range's total passes one book a multiplier line appears under the grid — eleven books from Animal Farm to In Search of Lost Time, a fresh one drawn each time the new-conversation page comes back, stepping down to the longest book the range has passed when it has not reached the drawn one. The panel appears on the new-conversation page only and steps away once a session is open.
|
|
36
|
+
- **The usage numbers are folded locally**: a new read-only route, `GET /dsh-claude-style/usage`, behind the same same-origin fence as the username route. With `dsh-cost-meter` installed it reads that plugin's ledger cache (read-only); otherwise the host half folds the local session logs itself and caches the result incrementally per log file — about two seconds on the first cold pass, about 3 ms per start afterwards. Both sources answer each model's four token buckets and each day's per-model tokens (`models` and `days[].models`), which the Models tab's chart and ranking read; the ledger books usage per provider and model, and one model served by several providers is one row. The ledger keeps no hours, so with it the other figures land first and the peak hour follows a moment later, folded per day from the local session logs. When the fold cannot answer (an older host half or a failed pass), the panel falls back to the projection block each host session row carries.
|
|
37
|
+
- **The panel draws its skeleton before its numbers**: the frame, number placeholders, an empty heat grid and stand-in share bars come first; arriving values replace only the text, the cell colours and the bar lengths, so nothing shifts. A figure no source can answer is a dash, never a zero.
|
|
38
|
+
- **A pixel crab on the composer card**: on the new-conversation page of the Studio layout (the Classic layout has none), Claude Code's pixel crab stands on the composer card's top edge near its right end. When it is clicked, when the pointer leaves it, and every 25–45 seconds while the page is in view, it plays Claude Code's fishing routine — a half turn and a wink, the rod cast down onto the card's edge, a spell of fishing side-on, and the rod put away as it turns back, about three seconds; with reduced motion requested (Windows' "Show animations in Windows" turned off, for one) only a click plays it. Only the crab itself takes the pointer, so the room the rod swings through never blocks a click.
|
|
39
|
+
- **Day tips on the heat grid**: resting the pointer on a day of the heat grid shows Claude Code's dark tip at once, with the date and that day's messages ("Sep 9 — 15,955"); over the columns at either end the tip lines up with the cell's outer edge and stays inside the panel. The session list's fallback has no per-day message count, and its tips name the day's tokens instead. The Overview tiles take Claude Code's names as well: Messages, Total tokens and Favorite model (the model name at regular weight).
|
|
40
|
+
- **More greetings on the classic home page**: the classic headline no longer has one line per time of day — morning, midday, afternoon, evening and night each have a pool, plus a few lines for any hour; a fresh line is drawn each time the new-conversation page comes back and holds still while you stay.
|
|
41
|
+
- **HDSL launcher accounts are picked up**: an instance started by HDSL prefers the launcher's account for both the nickname and the picture. The nickname falls back through the custom nickname → the signed-in account's name → the HDSL account name → the last probed system user → the system user → `User`; the picture through the account's avatar → the HDSL avatar → the Claude mark. The host half gains two read-only private routes behind the same request fence as the username route: `GET /dsh-claude-style/hdsl` answers the account metadata (never the avatar file's absolute path) and `GET /dsh-claude-style/hdsl-skin.png` answers the image. A contract version that is missing or unknown voids the whole group, and a deleted avatar file falls back to the mark.
|
|
42
|
+
|
|
43
|
+
<h3 id="cn-0.7.0">体验优化</h3>
|
|
44
|
+
|
|
45
|
+
- **同时只开一张弹层卡片**:悬停或点击打开模型选择器、推理强度、权限、账户抽屉、会话统计卡片、工作台新会话页的工作区菜单时,之前打开的那张卡片立即收起,两张卡不再叠在同一角;宿主自带的工作区菜单与账户菜单一并参与。
|
|
46
|
+
- **悬停停留由 50ms 延长到 100ms**:指针以平常速度扫过触发器不再展开卡片——工作台版面把上下文行贴在输入卡片正上方,此前指针移向输入框时几乎每次都会展开工作区菜单;停在触发器上仍然立即展开。模型选择器的收起宽限与会话统计卡片的展开停留保持各自的例外值。
|
|
47
|
+
|
|
48
|
+
<h3 id="en-0.7.0">Improvements</h3>
|
|
49
|
+
|
|
50
|
+
- **One popover card at a time**: opening the model picker, the reasoning-effort card, the permission menu, the account drawer, the session-stats card or the studio new-conversation page's workspace menu now folds whatever card was up before it, so two panels no longer stack over one corner; the host's own workspace and account menus join on the same terms.
|
|
51
|
+
- **The hover dwell goes from 50 ms to 100 ms**: a pointer crossing a trigger at an ordinary pace no longer unfolds a card — the studio layout sets the context row directly above the composer card, where a pointer on its way to the input used to unfold the workspace menu almost every time — while a pointer parked on the trigger still opens at once. The model picker's close grace and the stats card's open dwell keep their own values.
|
|
52
|
+
|
|
53
|
+
<h3 id="cn-0.7.0">问题修复</h3>
|
|
54
|
+
|
|
55
|
+
- 修复 **浅色模式下聊天记录顶部「加载更早」按钮几乎看不见**:浅色下的实色悬停底改为浅灰,按钮文字对比度从 2.6:1 回到 4.7:1,深色不变;同族令牌下的工作区重命名输入框在浅色里也不再是深色方块。
|
|
56
|
+
- 修复 **设置页「账号与余额」的「充值」按钮文字与底色几乎同色**:统一的链接色不再覆盖这类实心按钮链接,按钮恢复自身配色(浅色 3.1:1,深色 6.1:1),旁边的「查询用量」回到描边按钮样式。
|
|
57
|
+
- 修复 **实心主按钮悬停时底色跳出陶土色系**:悬停色与底色现在同族,悬停不再跳到冷灰或近白。
|
|
58
|
+
- 修复 **工作区选择器弹层打开时先跳到触发器下方再弹回**:弹层从打开的第一帧起就停在触发器旁边的正确位置,不再有可见的跳动。
|
|
59
|
+
- 修复 **切换到 Yolo 或 Auto 后权限停留在原档位**:皮肤此前通过模拟点击被隐藏的宿主访问按钮、再按文字寻找弹层里的档位行来完成这两个档位的切换,输入栏被锁定或按钮处于禁用状态时这条路径点不动,点确认后预设也不变化。现在任何档位都直接请求宿主的 `/permission` 命令完成切换(与官方弹窗确认后走的是同一条写入路径),点击后权限立即生效,皮肤控件与宿主按钮同步显示新档位;切换请求失败时控件按各功能失败隔离规则交还宿主按钮,不再无声无息。
|
|
60
|
+
- 修复 **权限弹层随重建在页面里残留**:宿主重渲染换掉控件容器后,下一次重建会往页面里追加一颗新弹层,旧弹层失去引用后永远留在文档里(长时间使用的页面上数到过 38 颗)。现在每次安装弹层先清扫文档里已有的弹层,功能卸载时一并扫除,页面上始终只有当前这一颗。
|
|
61
|
+
- 修复 **已归档行的时间被操作按钮顶离右缘**:取消归档与删除按钮此前以透明常驻占位,时间只能停在按钮预留区的左边,悬停时三样挤在一行。现在与官方会话行同一机制:静止时按钮不占位,时间与行右缘对齐;悬停(或键盘聚焦行)时时间让位隐藏,两颗按钮出现在原位置。按钮隐身期间也不再响应落在那片区域上的点击。
|
|
62
|
+
- 修复 **页面加载后第一次修改设置提示「设置存储不可用,改动不会被保存」**:设置表单现在会等宿主的命名空间登记完成后再绑定,登记晚到时也会在到达后立即绑定并读回取值,第一次修改即可保存。
|
|
63
|
+
- 修复 **新会话页把鼠标从预设模式快速移到工作文件夹时两个弹层同时打开并闪烁**:一行里的两个选择器(工作文件夹与预设模式)现在只会有一个打开,移到另一个触发器时先收起前一个,两个弹窗不再同时出现、也不再闪动;悬停离开收起的是悬停打开的那一个。
|
|
64
|
+
|
|
65
|
+
<h3 id="en-0.7.0">Bug Fixes</h3>
|
|
66
|
+
|
|
67
|
+
- Fix **the "load earlier" button at the top of the transcript being nearly invisible in light mode**: the light-mode solid hover fill is now a light gray, returning the button's contrast from 2.6:1 to 4.7:1 (dark unchanged); the workspace rename field on the same token family is no longer a dark block in light mode either.
|
|
68
|
+
- Fix **the "Top up" button in Settings → Account & balance showing its label in the same colour as its fill**: the shared link ink no longer overrides filled-button anchors, so the button keeps its own ink (3.1:1 light, 6.1:1 dark) and the neighbouring "View usage" returns to the outlined treatment.
|
|
69
|
+
- Fix **a filled primary button's hover fill jumping out of the accent family**: the hover now stays in the fill's family instead of jumping to a cold gray or near-white.
|
|
70
|
+
- Fix **the workspace picker card jumping below its trigger and back as it opens**: the card holds its correct position beside the trigger from the first frame it appears, with no visible jump.
|
|
71
|
+
- Fix **the preset staying on its old tier after switching to Yolo or Auto**: the skin drove these two tiers by synthetically clicking the hidden host access button and then locating the tier row by label in the opened menu; with the input bar locked or the trigger disabled that path pressed nothing, and the preset never changed after confirming. Every tier is now requested directly through the host's `/permission` command — the same write path the official dialog's confirmation ends in — so the switch takes effect immediately and the skin control and the host button agree on the new tier; a failed request retires the control through the per-feature failure isolation rule instead of passing silently.
|
|
72
|
+
- Fix **the permission popover stranding in the document across rebuilds**: when a host re-render replaced the control's container, the next rebuild appended a fresh popover while the previous one lost its only reference and stayed in the document forever (38 were counted on a long-lived page). Each install now sweeps every popover already in the document before appending its own, teardown sweeps the rest, and exactly one popover remains.
|
|
73
|
+
- Fix **the archived rows' time being pushed off the row's right edge by the action buttons**: the unarchive and delete buttons reserved their place while transparent, so the time stopped where their reserved space began and hover crowded all three onto one line. The rows now follow the host's own session rows' mechanism: at rest the buttons occupy nothing and the time right-aligns with the row; on hover (or while the row holds keyboard focus) the time steps aside and the two buttons take its place. The invisible buttons also stop answering clicks aimed at that area.
|
|
74
|
+
- Fix **the first settings change after a page load reporting "The settings store is unavailable, so changes will not be saved."**: the settings form now waits for the host's namespace registration before binding, binds as soon as a late registration arrives and reads the value back, so the first change saves.
|
|
75
|
+
- Fix **both popovers on the new-conversation page opening at once, and flickering, when the pointer moved quickly from the preset mode to the workspace folder**: only one of the row's two pickers (the workspace folder and the preset mode) is open at a time — moving to the other trigger folds the first, so the two cards no longer appear together or flicker, and a hover-leave folds the picker the hover opened.
|
|
76
|
+
|
|
77
|
+
**Full Changelog**: [v0.6.4...v0.7.0](https://github.com/Nwflower/dsh-claude-style/compare/v0.6.4...v0.7.0)
|
|
78
|
+
|
|
5
79
|
## [0.6.4] - 2026-09-24
|
|
6
80
|
|
|
7
81
|
[中文](#cn-0.6.4) | [English](#en-0.6.4)
|
package/README.en.md
CHANGED
|
@@ -64,11 +64,13 @@ Keep only one theme enabled at a time. After installation, **restart `dsh web`**
|
|
|
64
64
|
|
|
65
65
|
1. **Theme** — applies globally the moment it is installed, with nothing to configure. Light mode uses ivory `#FCFCFB`, dark mode warm black `#141413`, and both share the same ember orange `#D97757` action accent; light/dark follows the system color mode.
|
|
66
66
|
2. **Composer** — the input box is rebuilt from the ground up: a permission segmented control (Read / Edit / Auto / Yolo — the Auto tier appears only when the host has the built-in auto review enabled), a model trigger carrying the vendor lockup, and the toolbar and status stats laid out on a single line, with the send and stop buttons unified into 7px rounded rectangles. The stats sentence and the model trigger share the same font size and color.
|
|
67
|
-
3. **Model picker** — a new two-level popover. The first level lists the official service plus the quick providers selected in settings; "More models" opens a second level grouped by provider. Every row carries its vendor lockup and a description, and the footer holds the reasoning-effort slider (stepless — it snaps to the nearest level on release) together with the "More models" entry. The second level aligns its bottom edge with the first, providers already shown on the first level are not repeated, and models without thinking support get no slider. Hovering opens the popover after a
|
|
67
|
+
3. **Model picker** — a new two-level popover. The first level lists the official service plus the quick providers selected in settings; "More models" opens a second level grouped by provider. Every row carries its vendor lockup and a description, and the footer holds the reasoning-effort slider (stepless — it snaps to the nearest level on release) together with the "More models" entry. The second level aligns its bottom edge with the first, providers already shown on the first level are not repeated, and models without thinking support get no slider. Hovering opens the popover after a 100 ms dwell and closes it after 150 ms; the gap between the two cards does not count as leaving.
|
|
68
68
|
4. **Workspace** — the sidebar's "Workspace" heading becomes an **Active / Archived** segmented control. Active keeps the host's session tree, while Archived is the skin's own flat list (title, time, and per-row unarchive and delete icon buttons). Rows match the host's session rows measurement for measurement (row x=12 / width 251 / height 28 / title x=36).
|
|
69
69
|
5. **Sidebar** — the New session and Plugins rows take Claude's real shape: no background at rest, a background on hover, the "+" of New session set inside a circular chip, and both icons rotating 90° clockwise on hover (four-fold symmetric glyphs, so they land back on themselves). The account drawer and the ban-screen easter egg remain as they were.
|
|
70
|
-
6. **Account area** —
|
|
70
|
+
6. **Account area** — the nickname and the picture each follow one fixed order. Nickname: the custom nickname → the signed-in account's name → the HDSL launcher's account name → the last probed system user → the freshly probed system user → `User`. Picture: the signed-in account's avatar → the HDSL launcher's avatar → the Claude mark. The account profile is fetched from the server only at startup, on hot reload, and on sign-in and sign-out — never polled. The HDSL contract is read once by the host half and forwarded over two read-only routes (`GET /dsh-claude-style/hdsl` for the metadata, `GET /dsh-claude-style/hdsl-skin.png` for the avatar image; neither carries the avatar file's absolute path, and both sit behind the host's request fence). A contract version that is missing or unknown voids the whole group, and a deleted avatar file falls back to the mark. The account popover remains the host's own: the host's account row is the entry point, and the popover lists the account header, the other plugins' footer entries, and the host's own Settings / Feedback / Sign out, each keeping its original copy, order, and click behavior. On hosts without an account area (Web), the plugin builds a popover with the same look. The card uses a pure white background and spans the sidebar's width.
|
|
71
71
|
7. **Conversation views** — in the desktop Windows titlebar mode, the Conversation / Trajectory control sits centered and always visible on the titlebar row (the same row as the New session button). On wide windows with a short title it stays on the title's row; otherwise it keeps its own row below the title.
|
|
72
|
+
8. **Home layouts** — the new-conversation page comes in two layouts, Studio by default and switchable in the settings page at any time: **Classic** is the centered headline over the composer card, the headline drawn from a pool of greetings for the time of day (a few each for morning, midday, afternoon, evening and night, plus a few for any hour) and redrawn each time the new-conversation page comes back; **Studio** pins the greeting to the top left as one fixed line ("What's up next, <user>?"), docks the composer in the conversation's single-line form at the window's bottom edge with a row of hairline context chips above it, and sets the greeting and a 480px usage panel against the composer's left edge: an Overview tab with six stat cells (sessions, messages, total tokens, active days, peak hour, favorite model) and a twenty-six-week per-day heat grid of square cells in equal columns, a Models tab stacking each day's per-model tokens into one bar over a ranked model list (swatch, name, input and output, share; past six rows it folds behind a "show more" row, and the open list folds back with "show less"), and All / 30d / 7d range pills at the top right, which the peak hour and the multiplier line follow too; once the picked range's total passes one book, a multiplier line appears under the grid, naming one of eleven books (Animal Farm to In Search of Lost Time) drawn afresh each time the new-conversation page comes back. The numbers are rolled up by the plugin's host half: with `dsh-cost-meter` installed it reads that plugin's ledger cache directly (read-only — their file is never written), and without it the host half folds the local session logs itself, caching per log by size and modification time. Both sources answer each model's four buckets and each day's per-model tokens, with one model served by several providers counted as one row; the ledger keeps no hours, so the peak hour is always folded per day from the local session logs; when the roll-up cannot answer, the panel falls back to the projection block each host session row carries. The panel draws its skeleton first and does not shift when the numbers land.
|
|
73
|
+
9. **The pixel crab** — in the Studio layout, Claude Code's pixel crab stands on the new-conversation composer card's top edge, near its right end (the Classic layout has none). When it is clicked or the pointer leaves it (and on its own every half minute or so while the page is open), it plays Claude Code's fishing routine: a half turn and a wink, the rod raised and cast down onto the card's edge, a spell of fishing side-on, and the rod put away as it turns back — about three seconds. With reduced motion requested (Windows' "Show animations in Windows" turned off, for one), it moves only when clicked.
|
|
72
74
|
|
|
73
75
|
## Disabling and uninstalling
|
|
74
76
|
|
package/README.md
CHANGED
|
@@ -64,11 +64,13 @@ dsh plugin --profile web add Nwflower/dsh-claude-style # GitHub 源
|
|
|
64
64
|
|
|
65
65
|
1. **主题** —— 安装即全局生效,无需配置。亮色象牙白 `#FCFCFB`、暗色暖黑 `#141413`,两套画布共用同一个操作强调色陶烬橙 `#D97757`;亮暗跟随系统颜色模式切换。
|
|
66
66
|
2. **输入框** —— 输入框整块重做:权限分段控件(只读 / 工作区 / 自动审查 / Yolo,自动审查档位仅在宿主启用内置自动审查时出现)、带品牌锁定标的模型触发器、工具栏与状态统计同一行排版,发送键与停止键统一成 7px 圆角。统计句与模型触发器共用一套字号与颜色。
|
|
67
|
-
3. **模型选择器** —— 全新的两级弹层:一级列官方服务与设置页勾选的快捷供应商,「更多模型」二级按供应商分组;每行带厂商锁定标与说明文案,底部是推理等级推条(无极滑动,松手对齐最近档位)与「更多模型」入口。二级底边与一级对齐,一级已显示的供应商不再重复;模型不支持思考时不画推条。悬停停留
|
|
67
|
+
3. **模型选择器** —— 全新的两级弹层:一级列官方服务与设置页勾选的快捷供应商,「更多模型」二级按供应商分组;每行带厂商锁定标与说明文案,底部是推理等级推条(无极滑动,松手对齐最近档位)与「更多模型」入口。二级底边与一级对齐,一级已显示的供应商不再重复;模型不支持思考时不画推条。悬停停留 100ms 打开、离开 150ms 关闭,两卡之间的空隙不算离开。
|
|
68
68
|
4. **工作区** —— 侧栏「工作区」标题改成 **进行中 / 已归档** 分段控件:进行中沿用宿主的会话树,已归档是皮肤自己的平铺列表(标题、时间、每行「取消归档」与「删除」两个图标按钮)。行排版与宿主的会话行逐项一致(行 x=12 / 宽 251 / 高 28 / 标题起点 x=36)。
|
|
69
69
|
5. **侧栏** —— 新会话与插件两行取 Claude 真机形状:默认无底色、hover 才有底色,新会话的「+」套一枚圆形底,两行图标 hover 顺时针转 90°(四重对称图形,转回原位);底部账户抽屉、封号彩蛋一并保留。
|
|
70
|
-
6. **账号区** ——
|
|
70
|
+
6. **账号区** —— 昵称与头像各走一条固定顺序。昵称:自定义昵称 → 官方账号昵称 → HDSL 启动器昵称 → 上次探测到的系统用户名 → 本次探测到的系统用户名 → `User`;头像:官方账号头像 → HDSL 启动器头像 → Claude 徽标。官方账号资料只在启动、热重载、登录与登出时向服务器读取,不轮询;HDSL 契约由宿主半边读一次后经两条只读路由转发(`GET /dsh-claude-style/hdsl` 回账号元数据、`GET /dsh-claude-style/hdsl-skin.png` 回头像图片,都不含头像文件的绝对路径,并过宿主的请求栅栏),契约版本读不到或不认识时整组忽略,头像文件被删除时回落到徽标。账号弹层沿用宿主自带的那一个:宿主的账号行就是入口,弹层里依次是账号头部、其它插件的页脚条目与宿主自己的「设置 / 意见反馈 / 退出登录」,各行的文案、顺序与点击行为保持宿主原样;宿主没有账号区的环境(Web)由插件自建同样外观的弹层。弹层卡片为纯白背景、宽度与侧栏一致。
|
|
71
71
|
7. **对话视图** —— 对话 / 轨迹控件在桌面 Windows 标题栏模式下居中常显在标题栏那一行(与新会话按钮同一行);宽窗口且标题较短时它停在标题那一行,否则留在标题下方自己那一行。
|
|
72
|
+
8. **首页版面** —— 新会话页有两套版面,默认是工作台,在设置页随时切换:**经典**是居中大标题加输入卡片,标题按时段从一组问候里抽一句(早上、午间、下午、晚上、深夜各有几句,另有几句不分时段),每次回到新会话页换一句;**工作台**把标题移到左上并固定为一句「What's up next, 用户名?」,输入框改用对话内的单行样式贴住窗口底部、上方一行细边框上下文胶囊,标题与一块 480px 窄栏用量面板贴着输入框左缘排布:概览页签是六个数字格(会话数、消息数、Token 总量、活跃天数、高峰时段、最常用模型)与 26 周按天热力图(等分列方格),模型页签是每天一根按模型颜色堆叠的柱子加一份模型排行(色块、模型名、输入与输出、占比,超过六行折成「再显示 N 个」,展开后可「收起」),右上角的「全部 / 30 天 / 7 天」切换统计范围(高峰时段与趣味行一并跟随);所选范围内的用量超过一本书时,热力图下方出现倍数趣味行,书目共十一本(《动物农场》到《追忆似水年华》),每次回到新会话页随机换一本。数据由插件的宿主半边汇总:装了 `dsh-cost-meter` 时直接读它的账本缓存(只读,不写对方文件),没有时读本机会话日志自己算,并按日志的文件大小与修改时间增量缓存;两种来源都按模型给出四类 token、按天给出各模型的 token,同一模型经不同提供方的用量合成一行;账本不记小时,高峰时段始终由本机会话日志按天算出;汇总不可用时面板改用宿主会话列表每行自带的投影值。数字没到之前面板先画骨架,位置在填充前后不变。
|
|
73
|
+
9. **像素螃蟹** —— 工作台版面的新会话页上,输入卡片上沿、靠右的位置站着 Claude Code 的像素螃蟹(经典版面没有)。点它一下、或指针从它身上移开时(页面开着时也会每隔二三十秒自己来一次),它会照 Claude Code 的样子钓一次鱼:半转身眨眼,举起鱼竿甩到卡片边上,侧身钓一会儿,再收竿转回正面,前后约三秒。系统要求减少动态效果时(例如 Windows 关掉了「在 Windows 中显示动画」),它只在被点击时才动。
|
|
72
74
|
|
|
73
75
|
## 停用与卸载
|
|
74
76
|
|
package/docs/STYLE.md
CHANGED
|
@@ -21,6 +21,18 @@ Rules:
|
|
|
21
21
|
- Never pure white, never pure black, never cool grays.
|
|
22
22
|
- Accent usage stays under ~10% of visible elements.
|
|
23
23
|
|
|
24
|
+
**Host token bindings.** The skin supplies its palette through the host's own
|
|
25
|
+
alias tokens, so a host control that reads more than one token for one surface
|
|
26
|
+
has to be given all of them. A filled primary button is that case: the host takes
|
|
27
|
+
the fill from `--dsw-alias-brand-primary`, but the hover from a monochrome step
|
|
28
|
+
of its own scale (light `#43454a`, dark `#ebeef2`) and the label from a
|
|
29
|
+
foreground token the skin leaves alone. `--dsw-alias-button-primary-fill` and
|
|
30
|
+
`--dsw-alias-button-primary-hover` therefore state both steps in each palette.
|
|
31
|
+
Two fills are palette-specific rather than carried over from the dark base:
|
|
32
|
+
`--dsw-alias-interactive-bg-hover-solid` (a solid chip, lighter than the canvas
|
|
33
|
+
in light) and `--dsw-alias-button-elevated-fill` (a surface above the canvas in
|
|
34
|
+
light, a raised gray in dark).
|
|
35
|
+
|
|
24
36
|
## Typography
|
|
25
37
|
|
|
26
38
|
- **Serif display** — headings / editorial statements (`--dsw-font-serif`).
|
|
@@ -105,6 +117,18 @@ the account drawer, the session-stats card, and the host's own menu primitive
|
|
|
105
117
|
under the hero row's workspace and preset pickers — is meant to start from one
|
|
106
118
|
recipe. New popovers take it rather than inventing a card.
|
|
107
119
|
|
|
120
|
+
Two rules hold across all of them, both owned by `overrides/popover-utils.js`. A
|
|
121
|
+
pointer opens a card only after a **100 ms dwell** — long enough that crossing a
|
|
122
|
+
28px trigger on the way somewhere else unfolds nothing — and the card closes
|
|
123
|
+
100 ms after the pointer leaves. Two cards keep their own numbers with the reason
|
|
124
|
+
written where they are used: the model picker's levels close after 150 ms (the
|
|
125
|
+
pointer has to cross level 1 to reach level 2) and the session-stats card waits
|
|
126
|
+
300 ms to open (its sentence sits mid-row, where a passing pointer would trip
|
|
127
|
+
it). And **only one card is on screen at a time**: each popover registers its
|
|
128
|
+
close path with `registerPopover(name, close)` and calls
|
|
129
|
+
`closeOtherPopovers(name)` on the way open, so opening the model picker folds the
|
|
130
|
+
permission menu, the account drawer, the stats card and the host's hero menu.
|
|
131
|
+
|
|
108
132
|
**Card**
|
|
109
133
|
|
|
110
134
|
| Property | Value |
|
|
@@ -164,7 +188,121 @@ attribute, so it takes the same fade/scale as a one-shot `0.15s` animation; and
|
|
|
164
188
|
the host places it *below* its trigger, which is where the composer sits — so
|
|
165
189
|
`src/overrides/hero-menu.js` re-places it beside the trigger (bottom-aligned,
|
|
166
190
|
growing upward into the empty hero space, flipping left when the viewport is
|
|
167
|
-
tight)
|
|
191
|
+
tight). The host re-runs its own placement from its anchor geometry on every
|
|
192
|
+
frame while the card is open, so the position is handed over in two custom
|
|
193
|
+
properties on the card (`--dsh-claude-hero-menu-x` / `--dsh-claude-hero-menu-y`,
|
|
194
|
+
written by the same pass that stamps it) which `components/hero-menu.css` reads
|
|
195
|
+
with `!important`; that declaration outranks the host's plain inline value.
|
|
196
|
+
|
|
197
|
+
## Home layouts · 首页版面
|
|
198
|
+
|
|
199
|
+
The new-conversation page has two arrangements. `homeLayout` (settings: Home
|
|
200
|
+
layout) writes `data-dsh-claude-home-layout` onto `<body>` and
|
|
201
|
+
`src/styles/components/home-panel.css` branches on it. Both are the host's own
|
|
202
|
+
hero markup — the greeting, the workspace row, the dock and the composer card
|
|
203
|
+
inside `…_composerStack …_composerHero` — so only the arrangement differs.
|
|
204
|
+
|
|
205
|
+
| | Classic | Studio |
|
|
206
|
+
|---|---|---|
|
|
207
|
+
| greeting | centred, 44.2px serif, brand mark on its line | top left, one fixed line naming the user ("What's up next, …?") in the 20px sans UI face, a 21px brand mark on its line |
|
|
208
|
+
| composer card | vertically centred in the scroll body | the conversation's single-line inline form, resting on the window's bottom edge (16px foot) |
|
|
209
|
+
| between them | — | the usage panel |
|
|
210
|
+
| workspace / preset row | the card's footer tray | hairline chips directly above the card |
|
|
211
|
+
| column width | the hero's centred box | a 720px composer column; the greeting and the panel form a 480px block against its left edge |
|
|
212
|
+
|
|
213
|
+
The studio composer is stamped `data-composer-variant="inline"` (composer.js's
|
|
214
|
+
pass reads the same preference), so the home card is drawn by the conversation's
|
|
215
|
+
single-line stylesheet. The classic tray rules in `composer/card.css` are scoped
|
|
216
|
+
away from studio with `:not([data-dsh-claude-home-layout="studio"])`. That is
|
|
217
|
+
deliberate: they set `order: 1` / `order: 2` on the same boxes, and a competing
|
|
218
|
+
rule would have to be out-specified rather than merely reordered — scoping them
|
|
219
|
+
removes the contest.
|
|
220
|
+
|
|
221
|
+
**The usage panel.** It is a `conversation.input.dock` entry (the host's list
|
|
222
|
+
seat between the greeting and the card; a list seat keys its entries by `id`),
|
|
223
|
+
and it renders only in the hero phase — the host mounts that same seat inside a
|
|
224
|
+
conversation, where the todo, queue and goal bars hang on it.
|
|
225
|
+
|
|
226
|
+
The panel is Claude Code's dashboard shape: a flat warm-gray wash (a
|
|
227
|
+
`color-mix` of the label tone at 4% over the card tone, which flips with the
|
|
228
|
+
theme on its own), an Overview/Models tab pair on the left of its head, and the
|
|
229
|
+
All/30d/7d range pills on the right. The active tab and range pill are a gray
|
|
230
|
+
chip one step below that wash — half the radius the 20px control would round to,
|
|
231
|
+
so it reads as a rounded rectangle rather than a full pill. Overview carries six
|
|
232
|
+
stat cells in a 3×2 grid under Claude Code's names (Sessions, Messages, Total
|
|
233
|
+
tokens, Active days, Peak hour, Favorite model) whose tiles sit one clear step
|
|
234
|
+
deeper than the panel (15% of the label tone) and set a 12px label over a 13px
|
|
235
|
+
bold figure — the figure stays barely above its own label, which is also what
|
|
236
|
+
lets a long model id such as deepseek-v4.1-flash sit on one line; the favourite
|
|
237
|
+
model is a name, not a figure, and keeps the regular weight. Messages are the
|
|
238
|
+
settled calls. Once the picked range's total passes one
|
|
239
|
+
book, the yardstick line appears under the grid: eleven books from Animal Farm
|
|
240
|
+
(39k tokens) to In Search of Lost Time (1.6M), each sized at 1.3 tokens a word.
|
|
241
|
+
The book is drawn afresh each time the page comes back to the new-conversation
|
|
242
|
+
hero, and a range that has not reached it steps down to the longest book it has
|
|
243
|
+
passed, so the range pills keep the same book whenever the totals allow. The heat
|
|
244
|
+
grid takes one
|
|
245
|
+
equal column per week (twenty-six weeks), square cells from a 3px gutter, in
|
|
246
|
+
Claude Code's blue data ramp (`#3b6ecf` at 20/40/65/100 over the neutral empty
|
|
247
|
+
cell); because the columns are fractions of the panel's own width, the newest
|
|
248
|
+
week can never fall past the edge. Hovering a cell shows Claude Code's day tip at
|
|
249
|
+
once: a solid pill in the label ink with the canvas tone for text (so it inverts
|
|
250
|
+
with the theme), 13px medium, reading the day in the shell's language and its
|
|
251
|
+
messages ("Sep 9 — 15,955"); over the three columns at either end the pill lines
|
|
252
|
+
up with the cell's outer edge so it stays inside the panel. The session list's
|
|
253
|
+
fallback has no per-day message count, and its tip names the day's tokens.
|
|
254
|
+
Models is Claude Code's own shape: one column
|
|
255
|
+
per day of the chart's thirty-day window, stacked from the axis up with each
|
|
256
|
+
model's slice in its rank's colour (ranks past the ramp share its last, grey
|
|
257
|
+
step), four gridlines with their token labels in a 34px left gutter and every
|
|
258
|
+
third column's date under it in the shell's language ("Aug 26"), and beneath it
|
|
259
|
+
the ranked list — swatch, model name, the input/output split, and the share of
|
|
260
|
+
the models shown — folding past six rows behind one "show more" row, which turns
|
|
261
|
+
into "show less" once the list is open. The chart
|
|
262
|
+
and the list write counts Claude Code's way: one decimal at most, no trailing
|
|
263
|
+
".0", a lowercase k ("109M", "963.6k"). The list reads the roll-up's per-model
|
|
264
|
+
buckets; the chart reads its per-day per-model map, so a day with nothing
|
|
265
|
+
attributed to a model draws no stack. Each column is sized to its day's total
|
|
266
|
+
against the axis top and each slice to its share of the day, so the column's
|
|
267
|
+
rounding sits on the top of the stack. A range window filters the
|
|
268
|
+
tiles (the peak hour included), the yardstick line and the model list; the heat
|
|
269
|
+
grid and the chart keep their own windows.
|
|
270
|
+
|
|
271
|
+
Two sources, in this order: the host half's usage route, then the session list's
|
|
272
|
+
own projection block (`tokenUsage`, `modelSelection`, `sessionListMetadata`).
|
|
273
|
+
The second answers in a few milliseconds and carries per-model totals without the
|
|
274
|
+
four buckets, which is what the list falls back to when the first cannot answer.
|
|
275
|
+
The first reads the cost-meter ledger when one covers the newest activity — its
|
|
276
|
+
`<provider>:<model>` split becomes the per-model cells, one model across providers
|
|
277
|
+
merged into one — and otherwise the accurate per-event fold; both carry each day's
|
|
278
|
+
session ids, which a range window unions into one distinct session count. Only
|
|
279
|
+
the fold knows the settlement hours: it keeps one hour histogram per day, which a
|
|
280
|
+
range window sums into its own peak hour, and behind a cost-meter answer it still
|
|
281
|
+
runs for the histograms alone — the ledger's figures land first, and the peak
|
|
282
|
+
hour of the sessions whose logs remain lands a moment later. The panel names
|
|
283
|
+
which one it drew from, and a figure neither can answer is a dash.
|
|
284
|
+
|
|
285
|
+
The skeleton keeps the frame's geometry — six fixed-size stat cells, a heat grid
|
|
286
|
+
of a fixed cell count, and on the models tab the chart's frame with three
|
|
287
|
+
stand-in rows — and the heat grid's empty cell **is** the zero step, so "no data"
|
|
288
|
+
and "a day with no usage" stay different things: a missing value draws a
|
|
289
|
+
placeholder, a zero day draws the grid's own base tone.
|
|
290
|
+
|
|
291
|
+
## The composer crab · 输入卡片上的螃蟹
|
|
292
|
+
|
|
293
|
+
On the studio layout's new-conversation page, Claude Code's pixel crab stands on
|
|
294
|
+
the composer card's top edge: 4px cells, a nine-cell shell with two-cell arms
|
|
295
|
+
(52×32px), feet on the card's edge and the right arm 8px inside the single-line
|
|
296
|
+
card's right edge, so the feet stand where its 18px corner starts to round. The
|
|
297
|
+
classic layout keeps its centred hero without the crab. The shell is the clay accent `#d97757` in both
|
|
298
|
+
themes, the side-on back is `#b9603f`, the eyes `#141413`, and the rod takes the
|
|
299
|
+
tertiary label ink. When the crab is clicked, when the pointer leaves it, and on its own every 25–45
|
|
300
|
+
seconds while the page is in view, it plays Claude Code's fishing routine
|
|
301
|
+
(about three seconds): a half turn and a wink, the rod raised overhead and cast
|
|
302
|
+
down onto the card's edge, a hop, a spell of fishing side-on, and the rod put
|
|
303
|
+
away as it turns back to face front. With reduced motion requested, only a click
|
|
304
|
+
plays it: the pointer passing by and the idle timer leave it still. Only the crab
|
|
305
|
+
takes the pointer; the room the rod swings through does not.
|
|
168
306
|
|
|
169
307
|
## Implementation notes
|
|
170
308
|
|
|
@@ -175,7 +313,9 @@ tight) with an `important` inline write that outranks the host's own.
|
|
|
175
313
|
|
|
176
314
|
The shipped bundle `lib/client.js` is **generated** — never edit it directly.
|
|
177
315
|
Source lives in `src/` and `node scripts/build.mjs` (or `npm run build`)
|
|
178
|
-
assembles the bundle
|
|
316
|
+
assembles the bundle. The map below annotates what each source is for; the
|
|
317
|
+
assembly order itself is authoritative in `scripts/build.mjs`
|
|
318
|
+
(`FRAGMENTS` / `STYLE_FILES`), which the build enforces:
|
|
179
319
|
|
|
180
320
|
| Source | Content |
|
|
181
321
|
|---|---|
|
|
@@ -191,6 +331,7 @@ assembles the bundle:
|
|
|
191
331
|
| `src/styles/composer/inline.css` | in-conversation single-line composer (gated by composer preference) |
|
|
192
332
|
| `src/styles/composer/inline-bar.css` | the inline composer's trailing bar: model trigger and merged time/usage stats line (gated by composer preference) |
|
|
193
333
|
| `src/styles/sidebar.css` | sidebar brand, new-session row, workspace tree |
|
|
334
|
+
| `src/styles/components/workspace.css` | the workspace section's 进行中 / 已归档 segment control and the skin's own archived list |
|
|
194
335
|
| `src/styles/components/permissions.css` | permission segments and popover |
|
|
195
336
|
| `src/styles/components/account-footer.css` | account row and floating popover |
|
|
196
337
|
| `src/styles/components/ban-screen.css` | the account-hold easter egg (full-window overlay) |
|
|
@@ -201,23 +342,45 @@ assembles the bundle:
|
|
|
201
342
|
| `src/styles/components/footer-takeover.css` | host footer takeover rules |
|
|
202
343
|
| `src/styles/components/third-party.css` | agy-link repair rules |
|
|
203
344
|
| `src/styles/components/settings.css` | settings page section |
|
|
345
|
+
| `src/styles/components/home-panel.css` | the studio home layout and the usage panel's shell (keyed on the attribute `home-layout.js` writes) |
|
|
346
|
+
| `src/styles/components/home-overview.css` | the usage panel's Overview tab: stat cells, heat grid, yardstick line |
|
|
347
|
+
| `src/styles/components/home-models.css` | the usage panel's Models tab: the stacked chart and the ranked list |
|
|
348
|
+
| `src/styles/components/mascot.css` | the pixel crab: its seat on the card's top edge and its inks |
|
|
349
|
+
| `src/styles/components/theme-flip.css` | theme-flip transition suppression |
|
|
204
350
|
| `src/context/host.js` | host accessors and helpers |
|
|
205
351
|
| `src/context/prefs.js` | preference store |
|
|
206
352
|
| `src/context/model-copy.js` | model copy document store |
|
|
207
353
|
| `src/context/i18n.js` | localized copy lookups |
|
|
208
354
|
| `src/overrides/popover-utils.js` | shared anchor positioning and hover intent |
|
|
355
|
+
| `src/overrides/composer.js` | composer layout feature: each pass writes the variant, gate, attachment and context-ring attributes the composer stylesheets read |
|
|
209
356
|
| `src/overrides/copy.js` | composer/copy rewrites installer |
|
|
210
357
|
| `src/overrides/session-stats.js` | session-stats card factory (`createSessionStats`), the permissions feature's split-out half |
|
|
211
358
|
| `src/overrides/permissions.js` | permission segments/popover installer (the stats card lives in `session-stats.js`) |
|
|
359
|
+
| `src/overrides/model-brand.js` | brand lockup lookup: maps a catalog model to its vendored lockup through the `brands` bindings |
|
|
360
|
+
| `src/overrides/model-copy-lookup.js` | model copy lookup: exact entry → family rule → tier rule → the catalog's own text |
|
|
212
361
|
| `src/overrides/model/catalog.js` | model catalog factory (`createModelCatalog`): the per-session ModelDirectory store |
|
|
213
362
|
| `src/overrides/model/rows.js` | model row factory (`createModelRows`): option cells, provider rules, level-1 sections |
|
|
214
363
|
| `src/overrides/model-picker.js` | model picker installer, wiring the `model/` factories (thin orchestration) |
|
|
364
|
+
| `src/overrides/effort/matrix.js` | effort matrix factory (`createEffortMatrix`): the tier data the slider snaps to |
|
|
365
|
+
| `src/overrides/effort/control.js` | effort control factory (`createEffortControl`): the reasoning-effort slider and its card |
|
|
366
|
+
| `src/overrides/effort-picker.js` | effort picker installer, wiring the `effort/` factories (thin orchestration) |
|
|
215
367
|
| `src/overrides/hero-menu.js` | stamps the host menu card the hero row's pickers open |
|
|
368
|
+
| `src/overrides/quick-providers.js` | the settings page's quick-providers multi-select popover |
|
|
216
369
|
| `src/overrides/account/profile.js` | account profile factory (`createAccountProfile`): signed-in name/avatar reads and retries |
|
|
217
370
|
| `src/overrides/account/host-menu.js` | host account menu factory (`createHostAccountMenu`): reads and drives the host's own menu |
|
|
371
|
+
| `src/overrides/account/rows.js` | account rows factory (`createAccountRows`): the account popover's row builders |
|
|
218
372
|
| `src/overrides/account/footer-mirror.js` | footer mirror factory (`createFooterMirror`): redirects other plugins' footer entries into the drawer |
|
|
373
|
+
| `src/overrides/account/surface.js` | account surface factory (`createAccountSurface`): one row model, picks the mount point each pass (the host's account menu or the self-built popover) |
|
|
219
374
|
| `src/overrides/account-footer.js` | account drawer installer, wiring the `account/` factories (thin orchestration) |
|
|
220
375
|
| `src/overrides/ban-screen.js` | account-hold easter egg installer |
|
|
376
|
+
| `src/overrides/theme-flip.js` | suppresses transitions during a theme flip, so colours and shapes land together |
|
|
377
|
+
| `src/overrides/workspace-view.js` | workspace section feature: the 进行中 / 已归档 segments and the archived list (row delete goes through the plugin's own route) |
|
|
378
|
+
| `src/overrides/view-tabs.js` | conversation view tabs: moves the tab bar onto the title row when it fits |
|
|
379
|
+
| `src/overrides/home-layout.js` | home layout feature shell: writes the layout attribute, registers the usage panel into the host's dock seat, and draws the panel's head |
|
|
380
|
+
| `src/overrides/home/data.js` | the usage panel's data: the roll-up route, the session list's fallback, and the figures both tabs read |
|
|
381
|
+
| `src/overrides/home/overview.js` | the usage panel's Overview tab: stat cells, heat grid, yardstick line |
|
|
382
|
+
| `src/overrides/home/models.js` | the usage panel's Models tab: the stacked chart and the ranked list |
|
|
383
|
+
| `src/overrides/mascot.js` | the pixel crab on the new-conversation card: its poses as one inline SVG, and the fishing routine |
|
|
221
384
|
| `src/overrides/scheduler.js` | scheduler, observers, subscriptions, teardown |
|
|
222
385
|
| `src/overrides/selection.js` | mirrors the window's focus state onto the document for the two text-selection paints |
|
|
223
386
|
| `src/settings.js` | settings section (brand switch) |
|
|
@@ -247,6 +410,10 @@ rule should be checked for accidental hits:
|
|
|
247
410
|
- Never override the host's active-phase layout contract on
|
|
248
411
|
`[class*="viewArea"]` (`flex: 1 0 auto; min-height: auto`) — it is what
|
|
249
412
|
keeps the sticky composer seat pinned to the scrollport bottom.
|
|
413
|
+
- An anchor that carries a host button class (`_linkButton`, e.g. the settings
|
|
414
|
+
page's "充值") is a filled or outlined control, not a text link: it states its
|
|
415
|
+
own ink. The blanket anchor colour excludes that class — painting it leaves a
|
|
416
|
+
filled button's label the same colour as its fill.
|
|
250
417
|
|
|
251
418
|
`node scripts/probe.cjs --token <launch-token>` drives a headless Chrome over
|
|
252
419
|
CDP and asserts the invariants (composer pinned at bottom, single-line start,
|
package/docs/architecture.md
CHANGED
|
@@ -84,13 +84,13 @@
|
|
|
84
84
|
## D11. 插件自有路由借宿主的请求栅栏;宿主/用户来源的字符串只以文本上屏
|
|
85
85
|
|
|
86
86
|
- **背景**:`webServer.register()` 交给插件的是裸请求。宿主自己的 `/api` 挂在 Host/Origin 栅栏与浏览器会话 cookie 之后(`connection.requestRejection()`),插件路由不在其内。本插件的 `/username`(读系统用户名)因此曾经完全无鉴权:`dsh web` 绑定 `0.0.0.0` 时局域网里任何人都能直接读。读到的用户名又被客户端拼进 `innerHTML`——实测在 GUI 页面里执行了脚本,而这个页面能驱动执行 shell 命令的智能体。
|
|
87
|
-
- **决定**:宿主半边 `/username` 处理前先调 `ctx.get('connection').requestRejection(req)`,拒绝即回 401/403。宿主没有该服务时,插件自带的替身只服务回环(回环 Host、无跨站标记、Origin 与 Host 一致)。模型文案与字体等静态资产保持公开。删除会话的私有路由 `POST /dsh-claude-style/session-delete` 同样先过栅栏,另加四道:只接受 POST,id 必须匹配宿主自身的会话 id 形状,正在打开的会话拒绝,目录解析后必须留在会话根目录内(删除发生在宿主半边,浏览器半边只提交一个 id
|
|
87
|
+
- **决定**:宿主半边 `/username` 处理前先调 `ctx.get('connection').requestRejection(req)`,拒绝即回 401/403。宿主没有该服务时,插件自带的替身只服务回环(回环 Host、无跨站标记、Origin 与 Host 一致)。模型文案与字体等静态资产保持公开。删除会话的私有路由 `POST /dsh-claude-style/session-delete` 同样先过栅栏,另加四道:只接受 POST,id 必须匹配宿主自身的会话 id 形状,正在打开的会话拒绝,目录解析后必须留在会话根目录内(删除发生在宿主半边,浏览器半边只提交一个 id)。首页用量汇总路由 `GET /dsh-claude-style/usage` 与用户名路由同级:它回的是本机会话历史的聚合结果(每日 token 用量、调用次数、活跃天数),属于用户数据,因此同样先过栅栏,并且只读——第三方插件的账本(`dsh-cost-meter` 的 `ledger.json`)只读不写,自己的汇总落在 `$DSH_HOME/cache/dsh-claude-style/`。客户端:一切来自设置、账号服务、系统或第三方插件的字符串只用 `textContent` / 元素属性写入,图标复制节点而非重新解析 markup。
|
|
88
88
|
- **代价**:路由依赖宿主的 connection 服务。已核对两种壳都能通过:浏览器同源请求带会话 cookie;0.1.7 桌面壳经 `forwardWebRequest` 转发到回环 Host、剥掉页面 Origin 并自带 cookie(宿主真实的 `isTrustedApiRequest` 对这两种请求放行,对跨站与 DNS 重绑定请求拒绝)。
|
|
89
89
|
- **重审条件**:宿主为插件提供自带鉴权的路由注册(如 `connection.fetch.register`)时,迁移过去并删掉本地替身。
|
|
90
90
|
|
|
91
91
|
## D12. 特性级失败隔离:一个特性出错只关掉它自己
|
|
92
92
|
|
|
93
|
-
- **背景**:`apply()`
|
|
93
|
+
- **背景**:`apply()` 依次安装全部特性,teardown 却在最后才交给 `ctx.effect`;每轮 pass 里各 `sync()` 也没有逐个兜底——一个抛错,排在它后面的全部跳过,而且每轮如此。实测:宿主 `remote.account.getProfile()` 返回非 Promise 时 `apply()` 中途抛错,样式表与 body 属性留在页面上、调度器没装上、teardown 没注册(关掉插件也清不掉),前面装好的特性的监听器一并泄漏。effort-picker 那次「Loading plugins…」卡死是同一类问题。
|
|
94
94
|
- **决定**:
|
|
95
95
|
- teardown 最先经 `ctx.effect` 注册且幂等;每个特性单独 try/catch 安装,装不上的报一次 `console.error` 并退役。调度器本身装不上时整体回滚到宿主原样——没有调度器,其余特性都不会同步,留着只是一张半套的皮。
|
|
96
96
|
- 每轮 pass 里每个特性的 `sync()` 单独 try/catch,连续失败 3 轮即报一次并退役(`ui.retire`)。
|
|
@@ -116,3 +116,27 @@
|
|
|
116
116
|
- **理由**:宿主的菜单自带定位、动画、键盘遍历、Esc 与外部点击关闭,追加进去的行会加入同一套键盘遍历;删除驱动与镜像之后,账号区不再依赖宿主菜单的渲染时序与标签文案。
|
|
117
117
|
- **代价**:宿主菜单关闭即卸载,容器每轮重新确认并追加;宿主的列表是 React 管理的子树,追加位置固定在列表首位,宿主重渲染后由每轮同步自愈。
|
|
118
118
|
- **重审条件**:宿主为账号菜单提供追加行的插槽时,改用插槽注册,去掉 DOM 追加。
|
|
119
|
+
|
|
120
|
+
## D15. HDSL 账号契约:宿主半边转发,浏览器半边只认一条回退顺序
|
|
121
|
+
|
|
122
|
+
- **背景**:HDSL 启动器用 `HDSL_` 前缀的环境变量向实例公布「这是谁」:账号显示名、供应商、账号种类、头像来源,以及玩家头像 PNG 的绝对路径;`HDSL_ACCOUNT_CONTRACT` 是唯一必需键,读不到它整组作废。浏览器半边读不到进程环境,头像又是启动器数据目录里的一张图,两者都只能由宿主半边转发。
|
|
123
|
+
- **决定**:
|
|
124
|
+
- 宿主半边只信 `process` 与 `user-env` 两层(项目目录的 `.env` 会随仓库被克隆,没有资格声明玩家是谁),契约版本不等于 `1` 时回 `{ contract: false }`,其余字段一律不看。
|
|
125
|
+
- 新增两条私有路由,与 `/username` 同级过 `refusalOf` 栅栏:`GET /dsh-claude-style/hdsl` 回账号元数据,**不含**头像文件的绝对路径;`GET /dsh-claude-style/hdsl-skin.png` 回头像字节——路径只来自环境,永远不来自请求,读不到就回 404 让浏览器回退。
|
|
126
|
+
- 读环境走 harness 自带的 `@deepseek-ai/dsh-launch-environment`(`launchEnvironmentOf(ctx)`)。解析方式与 schemastery 相同:`createRequire(process.argv[1]).resolve()` 拿到真实路径后动态 `import`。这是 AGENTS 记录在案的第二条例外,理由与 D10 相同——`link:` 安装的插件真实路径在 profile 树之外,普通 import 解析不到,而解析不到时皮肤必须照常加载。
|
|
127
|
+
- 浏览器半边把昵称与头像各收敛成一条回退顺序:昵称 = 自定义昵称 → 官方账号昵称 → HDSL 昵称 → 探测昵称缓存 → 探测昵称 → `User`;头像 = 官方账号头像 → HDSL 头像 → Claude 徽标(样式表里的底色标记)。官方账号资料与 HDSL 契约各自推给 `src/context/host.js` 的身份存储,两个消费点(欢迎语、账号行)只读这一处。
|
|
128
|
+
- **理由**:契约在进程生命周期内不变,读一次就够,所以路由只转发、不轮询;把两条链收在一处,欢迎语、账号行、封号彩蛋不会各自漂移;缓存探测到的系统用户名,是为了刷新页面时欢迎语不再从 `User` 跳一次。
|
|
129
|
+
- **代价**:桌面端宿主的账号行由宿主自己渲染,插件只在自建账号行上画头像,所以 HDSL 头像在桌面端只作用于插件自己的表面。
|
|
130
|
+
- **重审条件**:HDSL 契约递增版本、或宿主提供「显示名 / 头像」的统一服务时,改接那一处,删掉本插件的转发路由。
|
|
131
|
+
|
|
132
|
+
## D16. 弹层共用两条基准:一个停留时长,一次只开一张卡
|
|
133
|
+
|
|
134
|
+
- **背景**:皮肤的弹层从六处各自长出来(模型选择器、推理强度、权限、账户、会话统计、hero 行的宿主菜单,另加设置页的快捷供应商),每处自己写停留时长与收起宽限。停留原本是 50ms,指针以平常速度扫过就已经超过它,于是位于指针必经之路上的触发器(工作台版面把上下文行贴在输入卡片正上方)看上去没有停留;同时「同时只开一张卡」此前只在模型与推理这一对上硬编码(两侧各自调用对方的 close),换一对弹层就会两张卡叠在一起。
|
|
135
|
+
- **决定**:
|
|
136
|
+
- 停留与宽限收到 `overrides/popover-utils.js` 的 `POPOVER_OPEN_DELAY`(100ms)与 `POPOVER_CLOSE_DELAY`(100ms)。100ms 是指针以平常速度扫过 28px 触发器所需的时间量级,路过不再展开,停在上面照常立即展开。两处例外连同理由写在使用处:模型选择器的两级卡片收起宽限 150ms(指针要横穿一级卡片才能到二级),会话统计卡片展开停留 300ms(统计句位于输入行中部,路过容易误触)。
|
|
137
|
+
- 同一文件里维护一张弹层登记表:`registerPopover(name, close)` 登记每张弹层的关闭路径,`closeOtherPopovers(name)` 在打开前关掉其余弹层。一个弹层的多层卡片按一项登记(模型选择器的一级与二级同名 `model`),宿主的菜单由驱动它的特性登记(hero 菜单、账户菜单),关闭动作各自复用既有路径——皮肤自己的卡片直接收,宿主的菜单再次点击其触发位或派发 Escape。
|
|
138
|
+
- hero 行的一项登记对应两个宿主菜单(工作文件夹与预设模式),所以 `hero-menu.js` 自己再分一次:打开前先收起行内另一个已展开的触发位,悬停离开时只收悬停打开的那一个,每轮同步里若发现行内两个触发位同时展开也收掉多余的一个。判定按「已展开的触发位」而不是「body 下的菜单卡片」——后者数得到宿主别处的菜单,前者才是这一行的选择器。
|
|
139
|
+
- 登记表只存关闭函数,不记「谁开着」:每个关闭函数在弹层未开时是空操作,所以用户用 Escape 或外部按下关掉的卡片不会留下过期状态。按名字覆盖登记,使客户端热重载后残留的旧一代闭包被新一代覆盖。
|
|
140
|
+
- **理由**:停留时长是同一个交互问题(路过还是停留),分处写必然漂移;互斥此前成对硬编码,新增弹层时要记得改 N 处,登记表把它变成一次登记。
|
|
141
|
+
- **代价**:登记表是模块级状态,特性卸载必须 `unregisterPopover`,否则旧一代的关闭函数留在表里。
|
|
142
|
+
- **重审条件**:宿主提供统一的弹层管理(全局互斥或焦点陷阱)时,改接宿主的机制,删掉本插件的登记表。
|