dsh-claude-style 0.19.4-dev.49 → 0.19.5-dev.50
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/README.en.md +5 -5
- package/README.md +5 -5
- package/docs/CHANGELOG.md +44 -0
- package/docs/STYLE.md +487 -633
- package/lib/assets/8a720f5f7014.js.br +0 -0
- package/lib/assets/manifest.json +4 -4
- package/lib/client.js +10 -10
- package/lib/host/settings.js +14 -2
- package/package.json +1 -1
- package/lib/assets/d1be2553f5b3.js.br +0 -0
package/docs/STYLE.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Design tokens for `dsh-claude-style`, recreating the Claude Code Desktop aesthetic.
|
|
4
4
|
|
|
5
|
+
## Where the values live
|
|
6
|
+
|
|
7
|
+
This page states the rule each look follows and the reason behind it. A value
|
|
8
|
+
lives where it is used: colours, durations, easings and layers in
|
|
9
|
+
`packages/client/src/theme/tokens.json`, a component's lengths in the stylesheet
|
|
10
|
+
beside its feature, the mascots' frames in
|
|
11
|
+
`packages/client/src/features/mascot/sheets.ts`, the page-scale arithmetic in
|
|
12
|
+
`packages/client/src/shared/page-zoom.ts` and `page-scale.ts`. A rule that turns
|
|
13
|
+
on one value quotes its token name; everything else is read from the file the
|
|
14
|
+
rule names (D48). The three tables marked generated are written from
|
|
15
|
+
`tokens.json` by `npm run build`; edit the JSON, not the tables.
|
|
16
|
+
|
|
5
17
|
## Palette
|
|
6
18
|
|
|
7
19
|
Every look is a **colour card**: an accent, a canvas, a sidebar and the reader's
|
|
@@ -10,47 +22,50 @@ their own, a link per scheme. The first card is the default look. A card's dark
|
|
|
10
22
|
outlines are drawn either as a lit highlight or as a flat surface step, and its
|
|
11
23
|
brand art takes its colour from the ink or the accent (D30).
|
|
12
24
|
|
|
13
|
-
The colour every token paints is a **tone** of one of those seeds
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
The ladder is shared by every card; a card states only its seeds, its sidebar,
|
|
18
|
-
its bubble and its traits.
|
|
25
|
+
The colour every token paints is a **tone** of one of those seeds; the build
|
|
26
|
+
resolves the tones into plain colours, so the page ships literals and nothing
|
|
27
|
+
derives at runtime. The ladder is shared by every card; a card states only its
|
|
28
|
+
seeds, its sidebar, its bubble and its traits.
|
|
19
29
|
|
|
20
|
-
The reader authors a card of their own: four colours, shown and edited in
|
|
21
|
-
|
|
30
|
+
The reader authors a card of their own: four colours, shown and edited in the
|
|
31
|
+
settings page beside the two brands. Every card can be picked; a brand card
|
|
22
32
|
paints its own colours whatever the brand mark is, and a picked card offers
|
|
23
33
|
「恢复跟随品牌」 to hand the colours back to the theme style's brand. The
|
|
24
|
-
reader's palette is the same ladder written as relative colours
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
the brand art from the same table (D30).
|
|
34
|
+
reader's palette is the same ladder written as relative colours, one block per
|
|
35
|
+
brand and scheme, so the colours resolve in the page while the brand cards keep
|
|
36
|
+
their literals; the image-colour choice (`Default`, `Accent`, `Text colour`)
|
|
37
|
+
repaints the brand art from the same table (D30).
|
|
29
38
|
|
|
30
39
|
Rules:
|
|
31
40
|
|
|
32
|
-
- Never pure black
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
The reader's own message plate is the card's `bubble` fill: Claude's neutral
|
|
38
|
-
plate, DeepSeek's light blue `#ECF0FF`; the custom card derives it from its
|
|
39
|
-
own canvas, so a conversation bubble reads as the card that painted it.
|
|
41
|
+
- Never pure black, never a cold gray. Each canvas pair — the Claude card's and
|
|
42
|
+
the DeepSeek card's — is read as one family: the canvas, its sidebar, its
|
|
43
|
+
borders and every ink step around them derive from that card's seed. The
|
|
44
|
+
reader's own message plate is the card's `bubble` fill, so a conversation
|
|
45
|
+
bubble reads as the card that painted it.
|
|
40
46
|
- Accent usage stays under ~10% of visible elements.
|
|
41
47
|
- Literal colours stay outside the ladder: the selection ring, the ripple's
|
|
42
48
|
front, the peak and off-peak badges and the popover shadow.
|
|
43
49
|
|
|
44
50
|
**The DeepSeek card** turns both schemes blue. Its accent is DeepSeek's brand
|
|
45
|
-
blue
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
blue, the colour of its whale logo and of Deepy; the sidebar's whale and
|
|
52
|
+
wordmark and the hero's fish take it as their ink. Its light canvas is a faint
|
|
53
|
+
blue-white with a light blue reader's bubble, its dark canvas a blue-black, and
|
|
54
|
+
its dark outlines are surface steps rather than highlights. The skin's Claude
|
|
55
|
+
marks (the account row's picture without an avatar) become DeepSeek's whale
|
|
56
|
+
(`packages/assets/src/brand/deepseek-mark.svg`, the host's `FISH_LOGO_PATH`);
|
|
57
|
+
the reasoning slider's accent follows whichever palette is in force.
|
|
58
|
+
|
|
59
|
+
**Host token bindings.** The skin supplies its palette through the host's own
|
|
60
|
+
alias tokens, so a host control that reads more than one token for one surface
|
|
61
|
+
has to be given all of them. A filled primary button is that case: the host takes
|
|
62
|
+
the fill from `--dsw-alias-brand-primary`, but the hover from a monochrome step
|
|
63
|
+
of its own scale and the label from a foreground token the skin leaves alone.
|
|
64
|
+
`--dsw-alias-button-primary-fill` and `--dsw-alias-button-primary-hover`
|
|
65
|
+
therefore state both steps in each card. Two fills are palette-specific rather
|
|
66
|
+
than carried over from the dark base: a solid chip, lighter than the canvas in
|
|
67
|
+
light, and an elevated fill, a surface above the canvas in light and a raised
|
|
68
|
+
gray in dark.
|
|
54
69
|
|
|
55
70
|
The tables below are generated from `packages/client/src/theme/tokens.json` by `npm run build`;
|
|
56
71
|
edit the JSON, not the tables. A token's row names the tone it reads and the
|
|
@@ -174,26 +189,12 @@ card leaves unset.
|
|
|
174
189
|
| `--dsh-claude-meter-valley` | literal | `#3d7550` | `#8cbf96` | `#3d7550` | `#8cbf96` | `var(--dsw-alias-state-success-primary)` | the off-peak rate badge, and the dated promotions priced below it |
|
|
175
190
|
<!-- /generated:tokens -->
|
|
176
191
|
|
|
177
|
-
**Host token bindings.** The skin supplies its palette through the host's own
|
|
178
|
-
alias tokens, so a host control that reads more than one token for one surface
|
|
179
|
-
has to be given all of them. A filled primary button is that case: the host takes
|
|
180
|
-
the fill from `--dsw-alias-brand-primary`, but the hover from a monochrome step
|
|
181
|
-
of its own scale (light `#43454a`, dark `#ebeef2`) and the label from a
|
|
182
|
-
foreground token the skin leaves alone. `--dsw-alias-button-primary-fill` and
|
|
183
|
-
`--dsw-alias-button-primary-hover` therefore state both steps in each card.
|
|
184
|
-
Two fills are palette-specific rather than carried over from the dark base:
|
|
185
|
-
`--dsw-alias-interactive-bg-hover-solid` (a solid chip, lighter than the canvas
|
|
186
|
-
in light) and `--dsw-alias-button-elevated-fill` (a surface above the canvas in
|
|
187
|
-
light, a raised gray in dark).
|
|
188
|
-
|
|
189
192
|
## Two palettes · 两套配色归属
|
|
190
193
|
|
|
191
194
|
The Colours and Typefaces rows each choose who paints: **Claude** (the default)
|
|
192
195
|
or **Follow the host**. Every rule that writes a host token carries the Claude
|
|
193
|
-
gate in its selector
|
|
194
|
-
|
|
195
|
-
rule that paints the skin's canvas onto the host's frame (`<html>`, `<body>`,
|
|
196
|
-
`#root`, the sidebar and conversation
|
|
196
|
+
gate in its selector, and so does every rule that paints the skin's canvas onto
|
|
197
|
+
the host's frame (`<html>`, `<body>`, `#root`, the sidebar and conversation
|
|
197
198
|
columns); the build refuses an ungated one. Under "follow the host" those rules
|
|
198
199
|
drop out: the host's own tokens stand, or another theme plugin's, such as a
|
|
199
200
|
wallpaper plugin that clears the canvas and turns the overlays to glass.
|
|
@@ -202,91 +203,82 @@ The skin's own surfaces read only its private tokens and host tokens. Under the
|
|
|
202
203
|
host palette each private token is an alias of a host token (the token table's
|
|
203
204
|
Host alias column), so those surfaces follow whoever paints the host's. A new
|
|
204
205
|
private colour token gets its tone and its host alias in
|
|
205
|
-
`packages/client/src/theme/tokens.json`; the build refuses a private token
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
206
|
+
`packages/client/src/theme/tokens.json`; the build refuses a private token
|
|
207
|
+
without an alias.
|
|
208
|
+
|
|
209
|
+
Cards take the host's overlay layer rather than its canvas: a plugin that clears
|
|
210
|
+
the canvas still gives its overlays a readable fill, and the shared popover card
|
|
211
|
+
blurs what lies behind it so its rows stay readable over a picture.
|
|
209
212
|
|
|
210
213
|
Under the host typeface the skin's own faces fall back to the host's:
|
|
211
|
-
`--dsh-claude-font-serif`, `-prose` and `-brand` to `--dsw-font-family` (the
|
|
212
|
-
carries no serif), `--dsh-claude-font-code` to `--ds-font-family-code`.
|
|
214
|
+
`--dsh-claude-font-serif`, `-prose` and `-brand` to `--dsw-font-family` (the
|
|
215
|
+
host carries no serif), `--dsh-claude-font-code` to `--ds-font-family-code`.
|
|
213
216
|
|
|
214
|
-
The account-hold page keeps its own colours, reproducing a Claude screen.
|
|
215
|
-
|
|
216
|
-
|
|
217
|
+
The account-hold page keeps its own colours, reproducing a Claude screen. The
|
|
218
|
+
reasoning slider and its trigger take the theme accent: the dot matrix and knob
|
|
219
|
+
glow read `--dsh-claude-apex*`, and the level ramp reads
|
|
217
220
|
`--dsh-claude-effort-*`. Translucent neutral hover tints and the usage panel's
|
|
218
221
|
data colours stay literal; they read on any canvas. Every other colour that
|
|
219
222
|
paints a surface, a text, a border or an accent reads a token.
|
|
220
223
|
|
|
221
224
|
**The effort trigger's tone.** While the Composer tab's effort-accent row is on,
|
|
222
|
-
the effort level's text in the composer's seat box and the card's level name
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
keeps the saturation and lightness of the ramp step.
|
|
233
|
-
The switch-off level and the model's own default level have no strength the catalog
|
|
234
|
-
names, so both keep the resting ink (`--dsw-alias-label-secondary`). The row off
|
|
235
|
-
leaves the resting ink on every rung.
|
|
225
|
+
the effort level's text in the composer's seat box and the card's level name take
|
|
226
|
+
their colour from the rung in force; the caption and the scale ends keep their
|
|
227
|
+
own caption inks. Every thinking rung has one place on a single ramp between two
|
|
228
|
+
ends mixed from the theme accent: the low end toward the raised surface (less
|
|
229
|
+
saturated, lower contrast), the high end toward the label ink (more contrast),
|
|
230
|
+
and the rung's index over the ladder's span places it. The hue starts at the
|
|
231
|
+
theme accent and travels slightly toward red as the rung climbs. The switch-off
|
|
232
|
+
level and the model's own default level have no strength the catalog names, so
|
|
233
|
+
both keep the resting ink (`--dsw-alias-label-secondary`). The row off leaves the
|
|
234
|
+
resting ink on every rung.
|
|
236
235
|
|
|
237
236
|
**The composer's seat.** The model's name and the effort level share one box in
|
|
238
237
|
the row, the way the host's own seat carries both texts: the box is the row's one
|
|
239
238
|
item, it stands against the row's right end and grows leftward, and both of its
|
|
240
|
-
cards hang from that right end. The box paints the hover plate — the two texts
|
|
241
|
-
two hit areas of one control, not two chips side by side — and the text under
|
|
242
|
-
pointer takes the stronger ink, except a level wearing the accent ramp, which
|
|
243
|
-
its rung and lets the plate answer the pointer. Each text is as wide as the
|
|
244
|
-
it shows, so a level's name changing width moves the box's left end and
|
|
245
|
-
else.
|
|
239
|
+
cards hang from that right end. The box paints the hover plate — the two texts
|
|
240
|
+
are two hit areas of one control, not two chips side by side — and the text under
|
|
241
|
+
the pointer takes the stronger ink, except a level wearing the accent ramp, which
|
|
242
|
+
keeps its rung and lets the plate answer the pointer. Each text is as wide as the
|
|
243
|
+
words it shows, so a level's name changing width moves the box's left end and
|
|
244
|
+
nothing else.
|
|
246
245
|
|
|
247
246
|
## Windows titlebar · 桌面顶栏
|
|
248
247
|
|
|
249
|
-
On the Windows desktop the
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
tint of the button glyph's colour.
|
|
248
|
+
On the Windows desktop the caption row has no colour of its own: the sidebar and
|
|
249
|
+
the conversation run to the window's top edge, each in its own fill, and the
|
|
250
|
+
sidebar's hairline runs with them from the top edge to the bottom. The
|
|
251
|
+
conversation column and the fullscreen file panel lose the host's top-left
|
|
252
|
+
corner, so the hairline meets them straight. The three window buttons sit on the
|
|
253
|
+
page itself: no block of their own behind them, and they dim with a modal's mask
|
|
254
|
+
like everything else. Their hover plate is the system's, a tint of the button
|
|
255
|
+
glyph's colour.
|
|
258
256
|
|
|
259
257
|
## Right sidebar · 右侧栏
|
|
260
258
|
|
|
261
|
-
A panel docked in the right column is a card:
|
|
262
|
-
side,
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
**Right sidebar card layout** (the Sidebar tab) chooses it: on is the card
|
|
277
|
-
above; off drops every rule that draws one, so the pane stands in the host's
|
|
278
|
-
own fill, flush with the column on all four sides, and a split's two panes meet
|
|
279
|
-
at the dock's own resting line. Every card rule is gated on
|
|
259
|
+
A panel docked in the right column is a card: inset from the column on every
|
|
260
|
+
side, its radius from the shape scale, the skin's hairline and the host's own
|
|
261
|
+
panel elevation. The frame's canvas shows through the gap, and the host's left
|
|
262
|
+
border on the first dock column gives way to the hairline.
|
|
263
|
+
|
|
264
|
+
The card hangs on what paints the column: a docked tab's pane or the column's own
|
|
265
|
+
empty host, which the pane inside it leaves transparent. A panel pulled out of
|
|
266
|
+
the sidebar becomes the host's own floating window and keeps that window's frame;
|
|
267
|
+
a split column gives each pane its own card, with the drag divider left on the
|
|
268
|
+
seam between them.
|
|
269
|
+
|
|
270
|
+
**Right sidebar card layout** (the Sidebar tab) chooses it: on is the card above;
|
|
271
|
+
off drops every rule that draws one, so the pane stands in the host's own fill,
|
|
272
|
+
flush with the column on all four sides, and a split's two panes meet at the
|
|
273
|
+
dock's own resting line. Every card rule is gated on
|
|
280
274
|
`<body data-dsh-claude-dock-look="card">`, so off is the host's own panel look
|
|
281
275
|
with nothing added.
|
|
282
276
|
|
|
283
277
|
**The start page.** The host centers its stack of entries in a box that never
|
|
284
278
|
grows, so a list taller than the pane was clipped at both ends, its first rows
|
|
285
|
-
out of reach. On the card the stack scrolls:
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
list fits, and fall to zero once it does not, so the stack starts at the top
|
|
289
|
-
and scrolls to its end.
|
|
279
|
+
out of reach. On the card the stack scrolls: the first entry's top margin and the
|
|
280
|
+
last entry's bottom margin take the free space while the stack fits, and fall to
|
|
281
|
+
zero once it does not, so the list starts at the top and scrolls to its end.
|
|
290
282
|
|
|
291
283
|
## Typography
|
|
292
284
|
|
|
@@ -294,49 +286,40 @@ and scrolls to its end.
|
|
|
294
286
|
- **Sans UI** — chrome, body (`--dsw-font-family`).
|
|
295
287
|
- **Mono** — code, technical labels (`--dsh-claude-font-code`).
|
|
296
288
|
|
|
297
|
-
|
|
298
289
|
## Shapes
|
|
299
290
|
|
|
300
|
-
- Radius: 4 / 8 / 16 px
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
11px, the permission and workspace segments' 7px, the view tabs' 5px, the
|
|
305
|
-
peak-rate and file pills' 999px.
|
|
291
|
+
- Radius: a ladder of 4 / 8 / 16 px, a 12px popover card with 6px rows, and
|
|
292
|
+
pills for CTAs. A shape copied from a host control keeps that control's own
|
|
293
|
+
geometry rather than the ladder, and says so in the stylesheet beside its
|
|
294
|
+
feature.
|
|
306
295
|
- Borders: 1px, and in dark not a colour but a highlight. The three border
|
|
307
|
-
tokens are one translucent white at
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
host control keeps that control's own stroke: the inline composer's 1.6px stop
|
|
312
|
-
square.
|
|
296
|
+
tokens are one translucent white at three strengths, so a card, a panel, a
|
|
297
|
+
code block or a keycap reads as a chamfer catching the top light over whatever
|
|
298
|
+
fill it sits on rather than as a contour whose colour has to be re-picked per
|
|
299
|
+
surface. The light rungs stay the warm opaque ladder.
|
|
313
300
|
- Separator: `--dsh-claude-divider`, a rung below the lightest border, drawn at
|
|
314
301
|
0.5px on every line that only separates — the sidebar's foot area, the search
|
|
315
302
|
panel's foot, the context stats, the settings rows, a menu's separators, the
|
|
316
|
-
file block's IN/OUT line and the transcript's `hr
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
heavier than a separator inside it.
|
|
320
|
-
conversation stays 0.5px of l1 (`theme/chrome.css`); the Markdown table frame
|
|
321
|
-
and the trajectory chip stay 0.5px of l2 (`theme/typography.css`,
|
|
322
|
-
`features/chat-files`).
|
|
303
|
+
file block's IN/OUT line and the transcript's `hr`. The line belongs to the
|
|
304
|
+
layout rather than to a surface's opening animation, so one thickness covers
|
|
305
|
+
every separator a card holds. A card, a field and a popover keep their 1px
|
|
306
|
+
border in l1, so an outline always reads heavier than a separator inside it.
|
|
323
307
|
- Spacing: 4 px rhythm.
|
|
324
308
|
- Bottom edge: the transcript never fades behind the composer by shadow. The
|
|
325
309
|
composer seat paints an opaque `var(--dsw-alias-bg-base)` bar across its own
|
|
326
310
|
box (so the bar's height *is* the composer's height) and one
|
|
327
|
-
`--dsh-composer-fade-h`
|
|
328
|
-
|
|
311
|
+
`--dsh-composer-fade-h` gradient band above it, running to transparent. The
|
|
312
|
+
same token is the transcript's bottom clearance, so the last turn rests
|
|
329
313
|
exactly at the band's top edge. Never re-add background-coloured halo shadows
|
|
330
314
|
to the card to hide the transcript: they have to be restated in every
|
|
331
315
|
light / dark / focus / attachment rule, and every rule that resets the card's
|
|
332
316
|
`box-shadow` inherits the job.
|
|
333
317
|
- Focus: the composer field lights up — its hairline takes the input box's own
|
|
334
|
-
shadow colour
|
|
335
|
-
|
|
336
|
-
black edge
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
used for focus strokes. Every other control's keyboard ring is the host's own
|
|
318
|
+
shadow colour with a halo in the same tone and a deeper drop shadow; dark
|
|
319
|
+
inverts the face, so the hairline and halo become bright ivory rather than a
|
|
320
|
+
black edge on a near-black canvas. The footer tray below follows the same
|
|
321
|
+
hairline so the two tiers stay one outline. Accent is never used for focus
|
|
322
|
+
strokes. Every other control's keyboard ring is the host's own
|
|
340
323
|
`:focus-visible` rule painting `--dsw-focus-ring-color`, which the skin states
|
|
341
324
|
as the same ink; the skin's ring is a `:where()` fallback of no weight, so the
|
|
342
325
|
rule of whichever layer owns the control — this skin's, the host's, or another
|
|
@@ -350,7 +333,7 @@ value (D9). One value per role, so a faster feedback or a calmer swap is one
|
|
|
350
333
|
edit; the build refuses a time or a curve written anywhere else.
|
|
351
334
|
|
|
352
335
|
- Feedback: a control answering hover, press or focus.
|
|
353
|
-
- Reveal: content entering the chat area at the host's own
|
|
336
|
+
- Reveal: content entering the chat area at the host's own pace.
|
|
354
337
|
- Surface: an entrance — a menu, a popover, a panel, a dialog, a toast.
|
|
355
338
|
- Exit: a dismissal, deliberately quicker than the entrance.
|
|
356
339
|
- Travel: something moving along its own track (a fold's door, the reasoning
|
|
@@ -364,10 +347,9 @@ edit; the build refuses a time or a curve written anywhere else.
|
|
|
364
347
|
- The presets these tokens feed live in `packages/client/src/shared/motion.css`:
|
|
365
348
|
a surface arriving or leaving, a menu growing out of its trigger, a plain
|
|
366
349
|
fade, one half of a wording swap, the pulse a placeholder and the sidebar's
|
|
367
|
-
ongoing light share, and the two paints
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
keyframes.
|
|
350
|
+
ongoing light share, and the two paints the waiting line's words can wear (a
|
|
351
|
+
highlight band and a colour wash). A feature that needs its own effect reads
|
|
352
|
+
the tokens and keeps its own keyframes.
|
|
371
353
|
|
|
372
354
|
The table below is generated from `packages/client/src/theme/tokens.json` by `npm run build`;
|
|
373
355
|
edit the JSON, not the table.
|
|
@@ -455,12 +437,12 @@ edit the JSON, not the table.
|
|
|
455
437
|
|
|
456
438
|
## Layers
|
|
457
439
|
|
|
458
|
-
A z-index the skin writes is one of two kinds (D9). Inside one box, siblings
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
440
|
+
A z-index the skin writes is one of two kinds (D9). Inside one box, siblings are
|
|
441
|
+
ordered with a literal from -1 to 9: a fill painted under its own content, a
|
|
442
|
+
label over its sliding pill, the composer's rows over one another. Anything that
|
|
443
|
+
has to clear what the host draws around it — the composer's own controls, the
|
|
444
|
+
window's title row, a floating card, a surface over the whole window — stands on
|
|
445
|
+
a layer token, and the build refuses any other number.
|
|
464
446
|
|
|
465
447
|
The table below is generated from `packages/client/src/theme/tokens.json` by `npm run build`;
|
|
466
448
|
edit the JSON, not the table.
|
|
@@ -479,21 +461,16 @@ edit the JSON, not the table.
|
|
|
479
461
|
## Text selection
|
|
480
462
|
|
|
481
463
|
Selection is the one surface that leaves the palette on purpose — it is a
|
|
482
|
-
transient gesture, not part of the interface, so it uses the platform's two
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
| State | Background | Text |
|
|
486
|
-
|---|---|---|
|
|
487
|
-
| Window focused | `#3366D0` | `#FFFFFF` |
|
|
488
|
-
| Window unfocused | `#C7C7C6` | `#000000` |
|
|
464
|
+
transient gesture, not part of the interface, so it uses the platform's two solid
|
|
465
|
+
paints and looks the same on both canvases.
|
|
489
466
|
|
|
490
467
|
Both are solid (never translucent) and `!important`, so they override whatever
|
|
491
468
|
colour the text underneath carries — links, inline code, syntax tokens. CSS
|
|
492
469
|
cannot read window focus: Chromium reaches the inactive paint through its own
|
|
493
470
|
internal `-internal-inactive-selection-*` properties, which a stylesheet cannot
|
|
494
|
-
address. `packages/client/src/features/composer/selection-mirror.ts` therefore
|
|
495
|
-
onto `data-dsh-window-blur` and the stylesheet
|
|
496
|
-
selection itself survives the blur.
|
|
471
|
+
address. `packages/client/src/features/composer/selection-mirror.ts` therefore
|
|
472
|
+
mirrors `document.hasFocus()` onto `data-dsh-window-blur` and the stylesheet
|
|
473
|
+
switches on that attribute; the selection itself survives the blur.
|
|
497
474
|
|
|
498
475
|
The window's chrome is not text. The sidebar column and the conversation header
|
|
499
476
|
take `user-select: none`: a press on a row or on the session title is a gesture
|
|
@@ -509,199 +486,140 @@ host's own crumb already carries the padding and the radius — with a tip under
|
|
|
509
486
|
the segment naming the press, in the label ink on the canvas tone the usage
|
|
510
487
|
panel's day tip uses. The segment carries the tip rather than the words because
|
|
511
488
|
the host clips a crumb's overflow for its ellipsis. The press opens a field over
|
|
512
|
-
the words' own line
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
title; a rename the host refuses keeps the field, ringed in the host's error
|
|
517
|
-
tone.
|
|
489
|
+
the words' own line, in the selection's blue and in the page's own space so the
|
|
490
|
+
reader's zoom keeps it on the title; the field hugs the name the way Claude's own
|
|
491
|
+
field does rather than laying a card over it. A rename the host refuses keeps the
|
|
492
|
+
field, ringed in the host's error tone.
|
|
518
493
|
|
|
519
494
|
## Markdown material
|
|
520
495
|
|
|
521
496
|
A quote is a **container**, not a link: it keeps the prose face on a neutral bar
|
|
522
|
-
and wash
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
497
|
+
and wash, and whatever sits inside it keeps its own material — links stay blue,
|
|
498
|
+
inline-code chips stay warm red, file mentions stay link-blue. Never paint the
|
|
499
|
+
quote itself with the link colour: the colour inherits into the block's inline
|
|
500
|
+
code and mentions, which is exactly the leak that made every path inside a quote
|
|
501
|
+
read as a link.
|
|
527
502
|
|
|
528
503
|
**Inline file mentions are links, not code.** The host resolves a file path
|
|
529
504
|
inside an inline code span to a button (`.fileMention`, hashed — match it with
|
|
530
505
|
`[class*="_fileMention"]`) and paints it with its link alias. The generic
|
|
531
|
-
inline-code chip rule is more specific than that class, so a mention inherits
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
opaque on hover, same thickness and offset
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
506
|
+
inline-code chip rule is more specific than that class, so a mention inherits the
|
|
507
|
+
chip's warm red unless a rule names it. A mention takes the link blue, the link's
|
|
508
|
+
weight and the link's underline — solid in the link tone at rest, solid and fully
|
|
509
|
+
opaque on hover, same thickness and offset as an anchor; the chip itself is left
|
|
510
|
+
alone, with the same fill, hairline, radius and padding as any other inline code,
|
|
511
|
+
and plain inline code keeps its warm text. Note the hover rule must set only
|
|
512
|
+
`text-decoration-color`: the `text-decoration` shorthand resets
|
|
513
|
+
`text-decoration-thickness` to `auto` and thins the line mid-hover.
|
|
539
514
|
|
|
540
515
|
## Inline code
|
|
541
516
|
|
|
542
|
-
The chip hugs its glyphs. The host builds it as an `inline-flex` box that
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
517
|
+
The chip hugs its glyphs. The host builds it as an `inline-flex` box that inherits
|
|
518
|
+
the prose line box, which left empty wash above and below the text; the skin
|
|
519
|
+
tightens the code's own line box so the wash sits on the text, stopping short of
|
|
520
|
+
the value that starts clipping descenders. The fill, hairline, radius and size
|
|
521
|
+
stay the host's.
|
|
547
522
|
|
|
548
523
|
## Popovers · 多选一弹层
|
|
549
524
|
|
|
550
525
|
Every single-choice popover in the skin — the permission menu, the model picker,
|
|
551
526
|
the account drawer, and the host's own menu primitive under the hero row's
|
|
552
|
-
workspace and preset pickers —
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
pointer
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
menu
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
527
|
+
workspace and preset pickers — starts from one recipe. New popovers take it
|
|
528
|
+
rather than inventing a card. The shell, its rows and the separator every card
|
|
529
|
+
shares are written once in `shared/popover.css`; a card's heading and footer are
|
|
530
|
+
its own.
|
|
531
|
+
|
|
532
|
+
Two rules hold across all of them, both owned by `shared/popover.ts`. A pointer
|
|
533
|
+
opens a card only after a short dwell — long enough that crossing a trigger on
|
|
534
|
+
the way somewhere else unfolds nothing — and the card closes a short grace after
|
|
535
|
+
the pointer leaves. A card that keeps its own numbers writes them where they are
|
|
536
|
+
used, with the reason beside them: the model picker's levels close later,
|
|
537
|
+
because the pointer has to cross level 1 to reach level 2. And **only one card is
|
|
538
|
+
on screen at a time**: opening one closes the others through the shared registry,
|
|
539
|
+
so opening the model picker folds the permission menu, the account drawer and the
|
|
540
|
+
host's hero menu.
|
|
541
|
+
|
|
542
|
+
Every card the skin places stays inside the window: the shared placement holds
|
|
543
|
+
the resolved position inside the window's own pixels, so a card taller than the
|
|
544
|
+
room beside its trigger begins at the window's top edge rather than drawing its
|
|
545
|
+
lower rows past the bottom of the page.
|
|
570
546
|
|
|
571
547
|
The context meter's popover is the host's own panel (it portals it, places it,
|
|
572
548
|
and dismisses it), so the skin drives its trigger on hover and paints nothing of
|
|
573
549
|
the shell itself. What it carries is described under "Context popover" below.
|
|
574
550
|
|
|
575
|
-
**
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
| radius | 12px |
|
|
582
|
-
| shadow | `0 8px 24px rgba(20,20,19,.12), 0 2px 6px rgba(20,20,19,.08)`; dark `0 8px 28px rgba(0,0,0,.65), 0 2px 8px rgba(0,0,0,.45)` |
|
|
583
|
-
| padding | 6px |
|
|
584
|
-
| layout | flex column, `gap: 6px`; a list body stacks its rows 3px apart |
|
|
585
|
-
| z-index | 99999, above the host's own menus (1100) |
|
|
586
|
-
| open | `opacity 0 → 1`, `translateY(4px) scale(.98) → none`, `.15s ease`, origin on the anchor's side |
|
|
587
|
-
|
|
588
|
-
**Row**
|
|
589
|
-
|
|
590
|
-
| Property | Value |
|
|
591
|
-
|---|---|
|
|
592
|
-
| min-height | 32px — a floor, not a cap: a two-line row grows |
|
|
593
|
-
| padding | `2px 7px` |
|
|
594
|
-
| radius | 6px |
|
|
595
|
-
| text | 13px / 20px, `var(--dsw-alias-label-primary)` |
|
|
596
|
-
| hover | `var(--dsh-claude-hover-bg, rgba(0, 0, 0, .08))` |
|
|
597
|
-
| icon | 16px, `var(--dsw-alias-label-secondary)` |
|
|
598
|
-
| two-line row | name 13px / 16px at 500; description 11px / 14px in `label-tertiary` |
|
|
599
|
-
| current row | trailing `IconCheckOutline16` in `var(--dsw-alias-brand-primary)` — the accent is what marks the choice |
|
|
600
|
-
| disabled | `opacity: .4`, `cursor: not-allowed` |
|
|
601
|
-
|
|
602
|
-
**Heading, separator, footer**
|
|
603
|
-
|
|
604
|
-
- heading row: 11px / 16px at 600, `letter-spacing: .04em`, uppercase, `label-tertiary`, `padding: 6px 7px 2px`
|
|
605
|
-
- separator: `0.5px` `var(--dsh-claude-divider)` — one rung below the lightest
|
|
606
|
-
border — `margin: 2px 4px`, drawn as a top border on a zero-height box. One
|
|
607
|
-
rule in the shared shell covers every separator in a card, the host's own menu
|
|
608
|
-
separators included, so the line is one thickness wherever it appears and does
|
|
609
|
-
not follow the card's opening scale
|
|
610
|
-
- pinned footer: `margin-top` / `padding-top` 4px, and the same `0.5px` divider
|
|
611
|
-
line above it
|
|
612
|
-
|
|
613
|
-
**Host surfaces**
|
|
614
|
-
|
|
615
|
-
The host's dropdown menus are one shared primitive (primitives' `Menu`), and its
|
|
616
|
-
two class-name families hash in opposite directions: the primitive ships inside
|
|
617
|
-
the web shell as `_<local>_<hash>_<n>` (`_itemWrap_1nxmc_92`,
|
|
618
|
-
`_itemLabel_1nxmc_174`), while the client-ui packages hash as `<hash>_<local>`
|
|
619
|
-
(`daogkW_itemName`, `p_FcLG_cardWorkspaceTrigger`). A substring matcher therefore
|
|
620
|
-
takes the longest stable piece of whichever family it targets — `_itemWrap_`,
|
|
621
|
-
`_itemLabel_`, `_viewport_` on one side, `_itemName`, `_itemDesc` on the other —
|
|
622
|
-
never the bare local name.
|
|
551
|
+
**Host surfaces.** The host's dropdown menus are one shared primitive
|
|
552
|
+
(primitives' `Menu`), and its two class-name families hash in opposite
|
|
553
|
+
directions: the primitive ships inside the web shell as `_<local>_<hash>_<n>`,
|
|
554
|
+
while the client-ui packages hash as `<hash>_<local>`. A substring matcher
|
|
555
|
+
therefore takes the longest stable piece of whichever family it targets — the
|
|
556
|
+
local name with its underscores — never the bare local name.
|
|
623
557
|
|
|
624
558
|
The hero row's two pickers are that primitive, portaled to `<body>` with no
|
|
625
|
-
marker of their own. `packages/client/src/features/hero-menu/hero-menu.ts` stamps
|
|
626
|
-
`data-dsh-claude-hero-menu` and
|
|
627
|
-
host's other menus (sidebar
|
|
628
|
-
keep the host's own design on
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
`data-dsh-claude-account-ready` once the skin's rows are inside it and its
|
|
646
|
-
placement has been read twice with the same value. The entrance animation hangs
|
|
647
|
-
on that stamp, so it plays on the card the reader will actually see.
|
|
648
|
-
|
|
649
|
-
One footer control stays in the sidebar rather than becoming a row of that
|
|
650
|
-
card: the update plugin's own button, which names itself with
|
|
559
|
+
marker of their own. `packages/client/src/features/hero-menu/hero-menu.ts` stamps
|
|
560
|
+
the open card with `data-dsh-claude-hero-menu` and
|
|
561
|
+
`features/hero-menu/hero-menu.css` restyles it; the host's other menus (sidebar
|
|
562
|
+
row menus, the settings permission row, submenus) keep the host's own design on
|
|
563
|
+
purpose. The stamp's value names the picker (`workspace` or `preset`), because
|
|
564
|
+
the two cards differ: the preset card keeps the recipe's rows for its two-line
|
|
565
|
+
entries, and the workspace card is Claude Code's folder menu — a plain-text row
|
|
566
|
+
per folder, no glyph in front of each, and the accent check on the current one.
|
|
567
|
+
|
|
568
|
+
**The account card** is that primitive too, and it carries the skin's rows inside
|
|
569
|
+
it. The host mounts it with its own three entries and places it from that
|
|
570
|
+
geometry; the skin's rows arrive a frame later and the card grows, so anything
|
|
571
|
+
painted in between would fade in at the height and the place the card is about to
|
|
572
|
+
leave — low, then jumping up mid-fade. The card therefore stays unpainted while
|
|
573
|
+
the account row is armed, and the feature stamps it ready once the skin's rows
|
|
574
|
+
are inside it and its placement has settled; the entrance animation hangs on that
|
|
575
|
+
stamp, so it plays on the card the reader will actually see.
|
|
576
|
+
|
|
577
|
+
One footer control stays in the sidebar rather than becoming a row of that card:
|
|
578
|
+
the update plugin's own button, which names itself with
|
|
651
579
|
`data-dsh-plugin="update"` and `data-dsh-part="entry"`. The takeover leaves the
|
|
652
580
|
plugin's node hidden in its slot and stands a copy of it at the right end of the
|
|
653
|
-
account row — the plugin's words, mark, progress and press
|
|
654
|
-
|
|
655
|
-
the
|
|
581
|
+
account row — the plugin's words, mark, progress and press — so a reader reaching
|
|
582
|
+
for an update does not have to open the menu first. The rail has no room beside
|
|
583
|
+
the icon and folds the copy away.
|
|
656
584
|
|
|
657
585
|
## Context popover · 上下文弹层
|
|
658
586
|
|
|
659
587
|
The context meter's popover is the host's own panel: the skin opens it by
|
|
660
|
-
pressing the host's trigger (hover,
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
the
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
panel's own layout width — `offsetWidth`, not its rect: the entrance scales the
|
|
674
|
-
box, and a transformed rect is two per cent narrower than the one that settles —
|
|
675
|
-
and writes `--dsh-claude-context-panel-left` with the mark that turns it on;
|
|
676
|
-
`features/composer/inline-bar.css` reads it with `!important`, which outranks the
|
|
677
|
-
host's inline value. The reading is re-taken when the panel's box changes (a size
|
|
678
|
-
subscription on the observation bus) and when the viewport moves (`reposition('viewport')`).
|
|
588
|
+
pressing the host's trigger (hover, under the "open popovers on hover"
|
|
589
|
+
preference; a click still works), and paints nothing of its shell or its
|
|
590
|
+
dismissal. A surface the skin opens arrives the way every skin surface does — the
|
|
591
|
+
cards' own short rise, as a one-shot animation.
|
|
592
|
+
|
|
593
|
+
Its horizontal place is the one thing the skin takes over. The host hangs the
|
|
594
|
+
panel from the anchor's left edge and only then clamps it into the viewport, so a
|
|
595
|
+
trigger at the end of the composer row would leave the panel against the window's
|
|
596
|
+
right margin instead of under the meter. `features/context-stats/` reads the
|
|
597
|
+
meter's edge and the panel's layout width — its box width, not its scaled rect,
|
|
598
|
+
since the entrance scales it — hands them to `features/composer/inline-bar.css`
|
|
599
|
+
through a custom property that the sheet reads over the host's inline value, and
|
|
600
|
+
re-reads when the panel's box or the viewport moves.
|
|
679
601
|
|
|
680
602
|
What the skin appends to it is the session's numbers
|
|
681
|
-
(`features/context-stats/session-stats.ts`), read from the host's
|
|
682
|
-
and `tokenUsage` projections — the same durable whole-log values
|
|
683
|
-
pills render — never by opening the host's stat dialogs. The
|
|
684
|
-
duration
|
|
685
|
-
two surfaces read the same words
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
of the line. On the line it
|
|
702
|
-
stands between the written-back tokens and the cache share, and it is among the
|
|
703
|
-
figures the ladder gives up when the line needs the room for the total; in the
|
|
704
|
-
block it is the usage section's first row.
|
|
603
|
+
(`features/context-stats/session-stats.ts`), read from the host's own
|
|
604
|
+
`sessionStats` and `tokenUsage` projections — the same durable whole-log values
|
|
605
|
+
the host's own pills render — never by opening the host's stat dialogs. The
|
|
606
|
+
labels and the duration and token templates come from the host's `chat` locale
|
|
607
|
+
namespace, so the two surfaces read the same words, and the host's formatting
|
|
608
|
+
rules are mirrored so the figures match character for character.
|
|
609
|
+
|
|
610
|
+
The cost is the session's own. The host half prices every settlement at its own
|
|
611
|
+
instant and route and answers the settled total; the browser adds the tokens the
|
|
612
|
+
streaming answer has grown by at the output rate in force, so the figure moves
|
|
613
|
+
with the written-back tokens and settles with them. It writes the shorter of four
|
|
614
|
+
significant digits and four decimals with the symbol of the `billingCurrency`
|
|
615
|
+
setting: the official CNY and USD tables are stored apart, so the setting picks a
|
|
616
|
+
table rather than converting one. A model the official table does not carry is
|
|
617
|
+
priced from the snapshot's catalogue — its published CNY line when the vendor has
|
|
618
|
+
one, its USD list price converted at the snapshot's fixed rate otherwise; a model
|
|
619
|
+
neither carries keeps the segment out of the line. On the line it stands between
|
|
620
|
+
the written-back tokens and the cache share, and it is among the figures the
|
|
621
|
+
ladder gives up when the line needs the room for the total; in the block it is
|
|
622
|
+
the usage section's first row.
|
|
705
623
|
|
|
706
624
|
In the toolbar's placement the segment keeps its width across a session switch:
|
|
707
625
|
the session's figure is replaced while the next one is being priced, so the
|
|
@@ -711,86 +629,68 @@ extends left, and the box stands as the currency symbol and a dash in
|
|
|
711
629
|
of the line and back, and the whole bottom line re-lays around it. The box leads
|
|
712
630
|
that line, so the room an amount does not need is the line's leading edge rather
|
|
713
631
|
than a gap between two figures. On the line of its own the segment hugs its
|
|
714
|
-
amount like every other figure:
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
nothing is drawn, and the browser asks the host for neither the price table nor
|
|
719
|
-
the session's total.
|
|
632
|
+
amount like every other figure: the line fits its figures again for the width the
|
|
633
|
+
arriving amount needs. The `billingCurrency` choice's Off takes the segment out
|
|
634
|
+
of the line altogether: nothing is drawn, and the browser asks the host for
|
|
635
|
+
neither the price table nor the session's total.
|
|
720
636
|
|
|
721
637
|
The settings page's custom price table (the row under the currency) writes a row
|
|
722
|
-
per model
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
editor's card is the modal's own: a 720px card on the window, and a page under
|
|
728
|
-
760px folds each row into a card with the model above three labelled rates.
|
|
638
|
+
per model, per million tokens in that currency; a filled bucket overrides the
|
|
639
|
+
tables' rate while a blank one leaves it alone. The rows travel to the host with
|
|
640
|
+
the session's cost ask, so a settlement and the answer still streaming are priced
|
|
641
|
+
from one set of rates; a model neither the tables nor a row prices leaves the
|
|
642
|
+
segment out of the line as before.
|
|
729
643
|
|
|
730
644
|
Which figures appear follows the host's statistics row. Its detailed form gets
|
|
731
|
-
the whole list
|
|
732
|
-
share on the composer line, so the block keeps the four that row leaves out —
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
The block
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
the
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
the host places it *below* its trigger, which is where the composer sits — so
|
|
758
|
-
`packages/client/src/features/hero-menu/hero-menu.ts` re-places it on the trigger the way the
|
|
759
|
-
skin's own composer pickers sit: right-aligned with the trigger and opening
|
|
760
|
-
upward by the same 6px air, flipping below only when the viewport leaves no room
|
|
761
|
-
above, and clamped to the 8px viewport margin (`POPOVER_MARGIN`). The host
|
|
762
|
-
re-runs its own placement from its anchor geometry on every frame while the card
|
|
763
|
-
is open, so the position is handed over in two custom properties on the card
|
|
764
|
-
(`--dsh-claude-hero-menu-x` / `--dsh-claude-hero-menu-y`, written by the same
|
|
765
|
-
pass that stamps it) which `features/hero-menu/hero-menu.css` reads with
|
|
766
|
-
`!important`; that declaration outranks the host's plain inline value.
|
|
645
|
+
the whole list; its compact form carries only the output speed and the cache-hit
|
|
646
|
+
share on the composer line, so the block keeps the four that row leaves out — the
|
|
647
|
+
total time, the first-token average, the output speed and the cache-hit share.
|
|
648
|
+
The total is the one figure the host has no word for (its dialog names the
|
|
649
|
+
model's time and the tool calls' separately): it is their sum, labelled from the
|
|
650
|
+
skin's own copy.
|
|
651
|
+
|
|
652
|
+
The block is a rule under the figures and a two-column grid of sections, each
|
|
653
|
+
with a heading and rows that stack a label over a value; values sit in tabular
|
|
654
|
+
figures so numbers do not dance as they change, and a row appears only when its
|
|
655
|
+
input exists. The block is rewritten on every projection frame while the panel is
|
|
656
|
+
open. While the projections have answered nothing, the block holds the numbers'
|
|
657
|
+
place under the two real headings with pulsing bars (still under the reduced
|
|
658
|
+
animation choice), and gives the place up rather than keeping placeholders for a
|
|
659
|
+
host that serves no such projection.
|
|
660
|
+
|
|
661
|
+
**The hero row's pickers.** They follow the recipe above rather than numbers of
|
|
662
|
+
their own, and they are the host's own menu primitive, which differs in two ways
|
|
663
|
+
CSS cannot change: it mounts instead of toggling an open attribute, so it takes
|
|
664
|
+
the same fade and scale as a one-shot animation; and the host places it below its
|
|
665
|
+
trigger, which is where the composer sits. `features/hero-menu/hero-menu.ts`
|
|
666
|
+
therefore re-places it on the trigger the way the skin's own composer pickers sit
|
|
667
|
+
— right-aligned with the trigger and opening upward, flipping below only when the
|
|
668
|
+
viewport leaves no room above — and hands the position to
|
|
669
|
+
`features/hero-menu/hero-menu.css` through custom properties, which the sheet
|
|
670
|
+
reads over the host's inline value.
|
|
767
671
|
|
|
768
672
|
## Context meter · 上下文计量
|
|
769
673
|
|
|
770
674
|
**The trigger is a pie.** Where the host draws a ring of two circles, the meter
|
|
771
675
|
draws two sectors on one centre: the share taken in the colour its occupancy fell
|
|
772
676
|
on, the rest of the plate in the quiet tone beside it. The smaller share keeps
|
|
773
|
-
the box's radius
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
the
|
|
777
|
-
|
|
778
|
-
the
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
reading
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
and the room the composer keeps for it are the host's. The occupied sector takes
|
|
787
|
-
the reading's own colour (the occupancy ramp, below), the plate the quiet tone
|
|
788
|
-
beside it.
|
|
677
|
+
the box's radius and the larger one steps in a little, so a lopsided split reads
|
|
678
|
+
as the smaller part standing out to the edge, and the step eases to nothing as
|
|
679
|
+
the split comes even. The seam is one width shared between two strokes by the
|
|
680
|
+
split: the occupied sector's stroke runs from the whole width at nothing taken to
|
|
681
|
+
nothing at everything taken, the plate's is the mirror, and each is half the
|
|
682
|
+
width at the even split. Each sector is cut by the stroke around the other's
|
|
683
|
+
path, so the seam follows the whole boundary — both radii, the outer arc and the
|
|
684
|
+
apex — and the two edges move with the split as the reading crosses the middle.
|
|
685
|
+
The part that stands out is painted last: the plate above the even split, the
|
|
686
|
+
reading's colour below it. The pie is one pixel wider than the ring's cell on
|
|
687
|
+
each side and concentric with it; the ring keeps its cell and is hidden while the
|
|
688
|
+
pie stands, so the trigger's layout and the room the composer keeps for it are
|
|
689
|
+
the host's.
|
|
789
690
|
|
|
790
691
|
**The line of numbers.** On its own line under the card, each figure is plain
|
|
791
|
-
text in the line's type
|
|
792
|
-
|
|
793
|
-
thirds of the distance every 73ms and the rest within 220ms, a reading that lands
|
|
692
|
+
text in the line's type with tabular figures, not a run of per-character spans: a
|
|
693
|
+
reading that moves is chased from the value on show, a reading that lands
|
|
794
694
|
mid-count becomes the new destination, and another session's numbers are chased
|
|
795
695
|
from the ones on show too. Only the cache share's figure and the percent sign
|
|
796
696
|
after it wear the band; the words before them keep the line's tone. A card too
|
|
@@ -800,139 +700,146 @@ figure into k / M / B.
|
|
|
800
700
|
## Search · 搜索
|
|
801
701
|
|
|
802
702
|
**Sidebar box.** The box takes the brand's place in the logo row while the
|
|
803
|
-
pointer is over the sidebar:
|
|
804
|
-
the
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
the
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
after the title and an optional 13px tertiary excerpt line under it, the match
|
|
831
|
-
inside it in the primary label at weight 500 with no fill); a hairline-topped footer
|
|
832
|
-
of hints with 20px keycaps. The input text, the chips' text, the captions and
|
|
833
|
-
the row glyphs all start on one vertical line 24px in from the card edge. The
|
|
834
|
-
highlighted row takes `rgba(20, 20, 19, 0.05)` (dark `rgba(250, 249, 245, 0.07)`)
|
|
835
|
-
and shows the Enter glyph; the others hide it. The card fades in and settles
|
|
836
|
-
from 6px above at 98.5% scale over 0.18s; on close card and mask fade out
|
|
837
|
-
together over 0.14s before the modal unmounts.
|
|
703
|
+
pointer is over the sidebar: a short field the height of the row, a hairline on
|
|
704
|
+
the canvas fill, a search glyph in the secondary label, the "Search" label in the
|
|
705
|
+
tertiary label, and the host's search shortcut as keycaps at the right end. The
|
|
706
|
+
New session row's own shortcut wears that same keycap, and the `+` the host
|
|
707
|
+
writes between the two keys goes, the way the box leaves it out. It is a button
|
|
708
|
+
like the New session and Plugins rows below it: the pointer cursor, and on hover
|
|
709
|
+
the same plate those rows take laid over its opaque fill. Box and brand share one
|
|
710
|
+
grid cell and cross-fade on the timing the workspace heading and its segmented
|
|
711
|
+
control trade places with; the box is excluded from the window drag region.
|
|
712
|
+
Standalone gives the box a row of its own under the brand row, shown without a
|
|
713
|
+
hover; that row adds no inline padding, so the box's hairline stands on the two
|
|
714
|
+
edges of the column's own inset and is exactly as wide as the New session plate
|
|
715
|
+
below it. In the desktop titlebar mode the brand row rises, as far as it goes with
|
|
716
|
+
the box still wholly below the titlebar edge, which clips the column. A box
|
|
717
|
+
narrower than its keycaps need keeps its glyph and label and leaves them out.
|
|
718
|
+
|
|
719
|
+
**Palette.** A modal card set down the window from the top, the host's mask under
|
|
720
|
+
it, on the same canvas fill. From the top: a borderless input with a close
|
|
721
|
+
button; the category chips sharing one sliding highlight; the list, capped
|
|
722
|
+
against the page's own height token rather than `vh` (D81), with section captions
|
|
723
|
+
and rows carrying a title, a glyph, a detail and an optional excerpt line, the
|
|
724
|
+
match in the primary label with no fill; a hairline-topped footer of hints with
|
|
725
|
+
keycaps. The input text, the chips' text, the captions and the row glyphs all
|
|
726
|
+
start on one vertical line inside the card edge. The highlighted row takes a
|
|
727
|
+
plate and shows the Enter glyph; the others hide it. The card fades in and settles
|
|
728
|
+
from above at less than full scale; on close card and mask fade out together
|
|
729
|
+
before the modal unmounts.
|
|
838
730
|
|
|
839
731
|
## Chat / Code switch · 聊天 / 代码切换
|
|
840
732
|
|
|
841
|
-
With ds-bot installed, the switch is two
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
the
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
the controls at the row's left end; on the web it is a cell of the sidebar's
|
|
853
|
-
first row, before the collapse toggle. It moves nothing: there is no
|
|
854
|
-
transition, since the press hands the whole page to the other side.
|
|
733
|
+
With ds-bot installed, the switch is two options on a padded plate, each carrying
|
|
734
|
+
its glyph in the stroke the sidebar's glyphs use. It is painted on both sides of
|
|
735
|
+
the switch by whoever paints the host's tokens — the theme on the Code side,
|
|
736
|
+
ds-bot on the Chat side — so the plate is a translucent tint, the options take
|
|
737
|
+
the tertiary label at rest and the primary on hover, and the checked one is cut
|
|
738
|
+
from the conversation canvas with a hairline of its own. On the desktop it stands
|
|
739
|
+
in the caption row, its right edge inside the sidebar column's, centred in the
|
|
740
|
+
row, and leaves the screen while the column is too narrow to keep clear of the
|
|
741
|
+
controls at the row's left end; on the web it is a cell of the sidebar's first
|
|
742
|
+
row, before the collapse toggle. It moves nothing: there is no transition, since
|
|
743
|
+
the press hands the whole page to the other side.
|
|
855
744
|
|
|
856
745
|
## The waiting line · 模型正在做什么的那一行
|
|
857
746
|
|
|
858
747
|
The host's running row, its divider and its live region stay as the host draws
|
|
859
748
|
them. Its whale tail is DeepSeek's own character, so under the Claude brand the
|
|
860
|
-
skin hides it and leads the line with its own loader:
|
|
861
|
-
|
|
862
|
-
(`features/chat-wait/wait-loader.ts`). Three dots bounce
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
the box never empties: each dot of the motion leaving waits for its own still
|
|
873
|
-
moment — the top of a hop, an end of a walk, a corner between two swaps — and the
|
|
874
|
-
dot it is paired with in the motion arriving takes its place there, at its size,
|
|
875
|
-
then travels into its own path over 480ms on the harmonic curve. The dots are
|
|
749
|
+
skin hides it and leads the line with its own loader: dots of the accent in force
|
|
750
|
+
in a small box, playing one of three motions
|
|
751
|
+
(`features/chat-wait/wait-loader.ts`). Three dots bounce and flatten against the
|
|
752
|
+
floor as they leave; three dots walk three lines through one point, each a simple
|
|
753
|
+
harmonic along its own line, shrinking as it crosses the middle so no frame of
|
|
754
|
+
the line motion draws a dot larger than the bouncing one; four dots swap the
|
|
755
|
+
corners of a rectangle, one pair and then the other. The loader opens on one of
|
|
756
|
+
the three at random and lets another take over on a slow clock. A change never
|
|
757
|
+
cuts and the box never empties: each dot of the motion leaving waits for its own
|
|
758
|
+
still moment — the top of a hop, an end of a walk, a corner between two swaps —
|
|
759
|
+
and the dot it is paired with in the motion arriving takes its place there, at
|
|
760
|
+
its size, then travels into its own path on the harmonic curve. The dots are
|
|
876
761
|
paired so they travel least; from three dots to four one splits in two, and from
|
|
877
762
|
four to three the spare one glides onto its partner and is gone where the two
|
|
878
|
-
meet. Under the
|
|
879
|
-
|
|
880
|
-
shimmering words step aside for the skin's line beside it: the
|
|
881
|
-
secondary label
|
|
882
|
-
the
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
and the wording is written at once. The Motion tab's preview cards
|
|
899
|
-
wear these same two paints (`packages/client/src/features/settings/settings.css`)
|
|
900
|
-
and take turns between a state word and the host's own word for a wait, so a card
|
|
901
|
-
shows the wording swap as well as the paint.
|
|
763
|
+
meet. Under the other brands the host's whale stands.
|
|
764
|
+
|
|
765
|
+
The host's own shimmering words step aside for the skin's line beside it: the
|
|
766
|
+
wording in the secondary label on a line of its own, the turn's clock after it in
|
|
767
|
+
the caption with tabular figures, and the "No response yet" badge after that. The
|
|
768
|
+
line takes the row's own width and gives way inside it: the words are carried
|
|
769
|
+
whole, so a clause the row has no room for is clipped where the room ends and the
|
|
770
|
+
copy draws an ellipsis, with the loader, the clock and the badge keeping their
|
|
771
|
+
places. The wording wears one of two paints, whichever the Motion tab's
|
|
772
|
+
Waiting-line animation holds: a highlight band swept across it the way the host's
|
|
773
|
+
own rows sweep, or a colour wash — the radial washes of
|
|
774
|
+
`packages/client/src/shared/motion.css` over a grey ramp, orbiting the glyph box,
|
|
775
|
+
the effect transitions.dev ships as its Pro gradient text. A wording that changes
|
|
776
|
+
runs both halves of the swap at once: the old one leaves upward with a slight
|
|
777
|
+
blur while the new one enters from below; under Reduced motion the band is left
|
|
778
|
+
out, the wash holds its still frame, and the wording is written at once. The
|
|
779
|
+
Motion tab's preview cards wear these same two paints
|
|
780
|
+
(`packages/client/src/features/settings/settings.css`) and take turns between a
|
|
781
|
+
state word and the host's own word for a wait, so a card shows the wording swap
|
|
782
|
+
as well as the paint.
|
|
902
783
|
|
|
903
784
|
With the chat area's own follow on, the row is pinned while the conversation
|
|
904
785
|
follows its tail, at the place that follow's glide comes to rest — the
|
|
905
|
-
composer's own height, the fade band above it and the host's
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
786
|
+
composer's own height, the fade band above it and the host's transcript padding —
|
|
787
|
+
with the canvas carried on down to the composer's top, since the host's clip
|
|
788
|
+
leaves a strip under the row where the line a stream slide brings up would
|
|
789
|
+
otherwise show below the line. The pin stands only while the host reports the
|
|
790
|
+
tail (or the stream glide holds the position) and steps aside for the reader's
|
|
791
|
+
own scrolling.
|
|
792
|
+
|
|
793
|
+
## Process rows · 过程行
|
|
794
|
+
|
|
795
|
+
An open process group lays its rows out one of two ways, whichever the Main
|
|
796
|
+
window tab's Process rows card holds. Flat is the host's own list, an icon
|
|
797
|
+
leading each row. Column is Claude Code's: the group's body becomes one card —
|
|
798
|
+
the 1px card border and the ladder's 8px radius — and each row is one line of it,
|
|
799
|
+
36px tall, inset 12px, with the 0.5px divider between rows and none above the
|
|
800
|
+
first. No icon leads a row; a row that opens has a chevron trailing its words in
|
|
801
|
+
the same ink, turning down while the row stands open, and its details open
|
|
802
|
+
inside the card under it. The group's body has no maximum height or scrollbar;
|
|
803
|
+
an opened thought keeps its own `min(400px, 50vh)` scrolling area. A trailing
|
|
804
|
+
thought adds 6px below its line or details, leaving room above the card's bottom
|
|
805
|
+
edge. A thought reads as the thought itself and a generic call as its tool and
|
|
806
|
+
purpose, the kind word and its separator set aside; a named call keeps its verb
|
|
807
|
+
(查找文件 · \*.md), and a row with no description keeps its title. The header
|
|
808
|
+
with its counted line stays above the card, outside it. The Main window tab's
|
|
809
|
+
two cards draw a small group in each layout with these same
|
|
810
|
+
rules (`features/process-column/process-column.css`).
|
|
911
811
|
|
|
912
812
|
## Home layouts · 首页版面
|
|
913
813
|
|
|
914
814
|
The new-conversation page has two arrangements. `homeLayout` (settings: Home
|
|
915
815
|
layout) writes `data-dsh-claude-home-layout` onto `<body>` and
|
|
916
|
-
`packages/client/src/features/home/home-panel.css` branches on it. Both are the
|
|
917
|
-
hero markup — the greeting, the workspace row, the dock and the
|
|
918
|
-
inside
|
|
816
|
+
`packages/client/src/features/home/home-panel.css` branches on it. Both are the
|
|
817
|
+
host's own hero markup — the greeting, the workspace row, the dock and the
|
|
818
|
+
composer card inside the composer stack — so only the arrangement differs.
|
|
919
819
|
|
|
920
820
|
| | Classic | Studio |
|
|
921
821
|
|---|---|---|
|
|
922
|
-
| greeting | centred,
|
|
923
|
-
| composer card | vertically centred in the scroll body | the conversation's single-line inline form, resting on the window's bottom edge
|
|
822
|
+
| greeting | centred, in the display serif, brand mark on its line | top left, one fixed line naming the user, in the sans UI face, brand mark on its line |
|
|
823
|
+
| composer card | vertically centred in the scroll body | the conversation's single-line inline form, resting on the window's bottom edge |
|
|
924
824
|
| between them | — | the usage panel |
|
|
925
825
|
| permission control | the segment group | the conversation's trigger and its card |
|
|
926
826
|
| workspace / preset row | the card's footer tray | hairline chips directly above the card |
|
|
927
|
-
| column width | the hero's centred box | a
|
|
827
|
+
| column width | the hero's centred box | a narrower composer column, with the greeting and the panel forming one block against its left edge |
|
|
828
|
+
|
|
829
|
+
The Appearance tab's cards draw these arrangements as miniature pages rather
|
|
830
|
+
than placeholder bars, exaggerating the content column and type for legibility
|
|
831
|
+
at that size. The studio greeting and dashboard stand slightly narrower than
|
|
832
|
+
the context row and composer. The dashboard uses the real panel, stat and
|
|
833
|
+
heat-cell classes, including all six stats and the seven-day-row grid; the
|
|
834
|
+
classic card keeps the centred brand greeting and the composer's footer tray
|
|
835
|
+
(`features/home/home-preview.css`).
|
|
928
836
|
|
|
929
|
-
The studio composer is stamped `data-composer-variant="inline"` (composer
|
|
837
|
+
The studio composer is stamped `data-composer-variant="inline"` (the composer's
|
|
930
838
|
pass reads the same preference), so the home card is drawn by the conversation's
|
|
931
|
-
single-line stylesheet. The classic tray rules in `features/composer/card.css`
|
|
932
|
-
away from studio
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
removes the contest.
|
|
839
|
+
single-line stylesheet. The classic tray rules in `features/composer/card.css`
|
|
840
|
+
are scoped away from studio on purpose: they set `order` on the same boxes, and a
|
|
841
|
+
competing rule would have to be out-specified rather than merely reordered —
|
|
842
|
+
scoping them removes the contest.
|
|
936
843
|
|
|
937
844
|
**The usage panel.** It is a `conversation.input.dock` entry (the host's list
|
|
938
845
|
seat between the greeting and the card; a list seat keys its entries by `id`),
|
|
@@ -943,89 +850,67 @@ workspace) has none; there the skin mounts the same component on a
|
|
|
943
850
|
`display: contents` element of its own (`.dsh-claude-home-seat`) and drops it
|
|
944
851
|
once the host's dock arrives. A window too short for the whole panel shrinks the
|
|
945
852
|
panel, never the page: the greeting keeps the top edge, the chips and the card
|
|
946
|
-
keep the bottom, and the panel scrolls between them
|
|
947
|
-
|
|
853
|
+
keep the bottom, and the panel scrolls between them under a fade over its bottom
|
|
854
|
+
edge.
|
|
948
855
|
|
|
949
856
|
The cold start card waits for a workspace: the host marks it with a dashed ring
|
|
950
857
|
and makes every control on it inert, so a press anywhere opens the workspace
|
|
951
|
-
picker. In the studio form the ring sits on the single-line input box itself
|
|
952
|
-
|
|
953
|
-
trigger fills the card's empty mode strip on the preset a new session starts in
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
The panel is Claude Code's dashboard shape: a flat warm-gray wash
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
third column's date under it in the shell's language ("Aug 26"), and beneath it
|
|
1002
|
-
the ranked list — swatch, model name, the input/output split, and the share of
|
|
1003
|
-
the models shown — folding past six rows behind one "show more" row, which turns
|
|
1004
|
-
into "show less" once the list is open. The chart
|
|
1005
|
-
and the list write counts Claude Code's way: one decimal at most, no trailing
|
|
1006
|
-
".0", a lowercase k ("109M", "963.6k"). The list reads the roll-up's per-model
|
|
1007
|
-
buckets; the chart reads its per-day per-model map, so a day with nothing
|
|
1008
|
-
attributed to a model draws no stack. Each column is sized to its day's total
|
|
1009
|
-
against the axis top and each slice to its share of the day, so the column's
|
|
1010
|
-
rounding sits on the top of the stack. A range window filters the
|
|
1011
|
-
tiles (the peak hour included), the yardstick line and the model list; the heat
|
|
1012
|
-
grid and the chart keep their own windows.
|
|
858
|
+
picker. In the studio form the ring sits on the single-line input box itself (the
|
|
859
|
+
host's dashed stroke and colours, flat, no drop shadow), and the permission
|
|
860
|
+
trigger fills the card's empty mode strip on the preset a new session starts in —
|
|
861
|
+
disabled, like the rest of the card's controls.
|
|
862
|
+
|
|
863
|
+
The panel is Claude Code's dashboard shape: a flat warm-gray wash that flips with
|
|
864
|
+
the theme on its own, an Overview/Models tab pair on the left of its head, and the
|
|
865
|
+
All/30d/7d range pills on the right. The active tab and range pill are a gray chip
|
|
866
|
+
one step below that wash — half the radius the control would round to, so it
|
|
867
|
+
reads as a rounded rectangle rather than a full pill. Overview carries six stat
|
|
868
|
+
cells in a 3×2 grid under Claude Code's names (Sessions, Messages, Total tokens,
|
|
869
|
+
Active days, Peak hour, Favorite model), whose tiles sit one clear step deeper
|
|
870
|
+
than the panel, a label over a bold figure that stays barely above its own label
|
|
871
|
+
— which is also what lets a long model id sit on one line; the favourite model is
|
|
872
|
+
a name, not a figure, and keeps the regular weight. Messages are the settled
|
|
873
|
+
calls.
|
|
874
|
+
|
|
875
|
+
Once the picked range's total passes one book, the yardstick line appears under
|
|
876
|
+
the grid. The books it measures and their counts are in
|
|
877
|
+
`packages/client/src/features/home/home-figures.ts`; the line states the rounded
|
|
878
|
+
multiple as a share of the book ("~2× the tokens in Moby-Dick"), never as an
|
|
879
|
+
excess over it. The book is drawn afresh each time the page comes back to the
|
|
880
|
+
new-conversation hero, and a range that has not reached it steps down to the
|
|
881
|
+
longest book it has passed, so the range pills keep the same book whenever the
|
|
882
|
+
totals allow.
|
|
883
|
+
|
|
884
|
+
The heat grid takes one equal column per week, square cells from a gutter, in
|
|
885
|
+
Claude Code's blue data ramp over the neutral empty cell; because the columns are
|
|
886
|
+
fractions of the panel's own width, the newest week can never fall past the edge.
|
|
887
|
+
Hovering a cell raises Claude Code's day tip after a short rest: a solid pill in
|
|
888
|
+
the label ink with the canvas tone for text (so it inverts with the theme),
|
|
889
|
+
reading the day in the shell's language and the tokens that day spent, the same
|
|
890
|
+
figure the cell's heat is bucketed from. The grid carries one pill and moves it
|
|
891
|
+
onto the day the pointer names, so a crossing between two cells changes its words
|
|
892
|
+
in place and leaves it standing over the gutters between them; it is centred on
|
|
893
|
+
its day except where its own width would carry it past the panel's edge, and
|
|
894
|
+
stands above its cell except where the cell holds more room below it.
|
|
895
|
+
|
|
896
|
+
Models is Claude Code's own shape: one column per day of the chart's window,
|
|
897
|
+
stacked from the axis up with each model's slice in its rank's colour (ranks past
|
|
898
|
+
the ramp share its last, grey step), gridlines with their token labels, and every
|
|
899
|
+
third column's date under it in the shell's language, and beneath it the ranked
|
|
900
|
+
list — swatch, model name, the input/output split, and the share of the models
|
|
901
|
+
shown — folding past a short list behind one "show more" row, which turns into
|
|
902
|
+
"show less" once the list is open. The chart and the list write counts Claude
|
|
903
|
+
Code's way: one decimal at most, no trailing ".0", a lowercase k. Each column is
|
|
904
|
+
sized to its day's total against the axis top and each slice to its share of the
|
|
905
|
+
day, so the column's rounding sits on the top of the stack. A range window filters
|
|
906
|
+
the tiles (the peak hour included), the yardstick line and the model list; the
|
|
907
|
+
heat grid and the chart keep their own windows.
|
|
1013
908
|
|
|
1014
909
|
Two sources, in this order: the host half's usage route, then the session list's
|
|
1015
|
-
own projection block (`tokenUsage`, `modelSelection`, `sessionListMetadata`).
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
`<provider>:<model>` split becomes the per-model cells, one model across providers
|
|
1020
|
-
merged into one — takes the dates past the ledger's newest day from the accurate
|
|
1021
|
-
per-event fold, and keeps the two apart by date rather than adding them; the
|
|
1022
|
-
sessions behind the ledger's days and the logs written on the folded ones are one
|
|
1023
|
-
union. Both carry each day's session ids, which a range window unions into one
|
|
1024
|
-
distinct session count. Only the fold knows the settlement hours: it keeps one
|
|
1025
|
-
hour histogram per day, which a range window sums into its own peak hour, and the
|
|
1026
|
-
ledger's own days carry none — the ledger's figures land first, and the peak hour
|
|
1027
|
-
of the folded days lands a moment later. The panel names
|
|
1028
|
-
which one it drew from, and a figure neither can answer is a dash.
|
|
910
|
+
own projection block (`tokenUsage`, `modelSelection`, `sessionListMetadata`). The
|
|
911
|
+
session list carries per-model totals without the four buckets, which is what the
|
|
912
|
+
list falls back to when the route cannot answer. The panel names which one it
|
|
913
|
+
drew from, and a figure neither can answer is a dash.
|
|
1029
914
|
|
|
1030
915
|
The skeleton keeps the frame's geometry — six fixed-size stat cells, a heat grid
|
|
1031
916
|
of a fixed cell count, and on the models tab the chart's frame with three
|
|
@@ -1038,69 +923,39 @@ placeholder, a zero day draws the grid's own base tone.
|
|
|
1038
923
|
Claude Code's pixel crab is the mascot under the Claude brand (or when picked in
|
|
1039
924
|
the Mascot row). It stands where Deepy does — the composer card's top edge on
|
|
1040
925
|
both home layouts, the input area or the panel that replaces its card in a
|
|
1041
|
-
conversation (unless Where it appears keeps it to the home page) — and follows
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
the
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
by `scripts/draw-crab.py` after the same character. With the animation choice
|
|
1054
|
-
resolved to "reduced" each state holds its still frame; a click or a pull still
|
|
1055
|
-
plays its reaction.
|
|
1056
|
-
|
|
1057
|
-
| State | Animation | Still frame |
|
|
1058
|
-
|---|---|---|
|
|
1059
|
-
| idle, every 20–40 s a look around, a claw wave or Claude Code's whole laptop routine | `idle`, `idle-look`, `idle-wave`, `idle-laptop` | 0 |
|
|
1060
|
-
| the model reasons or has not answered: eyes up to a thought bubble | `thinking` | 18 |
|
|
1061
|
-
| the model writes or tools run: 1 / 2 / 3+ sessions at work (typing / headphones / typing in a hard hat) | `typing` / `music` / `building` | 0 / 0 / 0 |
|
|
1062
|
-
| subagents running: 1 / 2+ (headphones / conducting) | `music` / `conducting` | 0 / 0 |
|
|
1063
|
-
| a compaction runs: squashed | `compacting` | 3 |
|
|
1064
|
-
| an approval, a question or a plan review waits: an exclamation mark | `notification` | 0 |
|
|
1065
|
-
| a tool call or a turn failed (4.8 s): crossed eyes, a shake | `error` | 4 |
|
|
1066
|
-
| a turn or a compaction finished (5.2 s): hops, claws up | `happy` | 3 |
|
|
1067
|
-
| a quiet minute / the next pointer move or key | `sleeping` / `waking` | 0 / 11 |
|
|
1068
|
-
| a click on the left / right half, four quick clicks, a pull | `poke-left` / `poke-right`, `tickle`, `drag` | 0 |
|
|
926
|
+
conversation (unless Where it appears keeps it to the home page) — and follows the
|
|
927
|
+
same states with the same keys. Its frames, its crop boxes and the frame each
|
|
928
|
+
state holds under reduced motion are in
|
|
929
|
+
`packages/client/src/features/mascot/sheets.ts`, the table the build cuts the
|
|
930
|
+
sheets from; the art is drawn by `scripts/draw-crab.py` after Claude Code's
|
|
931
|
+
character. The shell keeps the clay accent in both themes and the props (the
|
|
932
|
+
laptop, the thought bubble, letters, notes, the hard hat) are an ink mask filled
|
|
933
|
+
with the tertiary label ink, so the mascot reads as part of the card it stands on.
|
|
934
|
+
|
|
935
|
+
Only the resting crab takes the pointer. With the animation choice resolved to
|
|
936
|
+
"reduced" each state holds its still frame; a click or a pull still plays its
|
|
937
|
+
reaction.
|
|
1069
938
|
|
|
1070
939
|
## Deepy · 小鲸鱼
|
|
1071
940
|
|
|
1072
|
-
Under the DeepSeek brand (or when picked in the Mascot row) the mascot is
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
plays its reaction.
|
|
1091
|
-
|
|
1092
|
-
| State | Animation | Still frame |
|
|
1093
|
-
|---|---|---|
|
|
1094
|
-
| idle, every 20–40 s a look around or a spout | `idle`, `idle-look`, `idle-spout` | 0 |
|
|
1095
|
-
| the model reasons or has not answered | `thinking` | 20 |
|
|
1096
|
-
| the model writes or tools run: 1 / 2 / 3+ sessions at work | `typing` / `music` / `building` | 16 / 0 / 0 |
|
|
1097
|
-
| subagents running: 1 / 2+ | `music` / `conducting` | 0 / 6 |
|
|
1098
|
-
| a compaction runs | `compacting` | 20 |
|
|
1099
|
-
| an approval, a question or a plan review waits | `notification` | 12 |
|
|
1100
|
-
| a tool call or a turn failed (4.8 s) | `error` | 24 |
|
|
1101
|
-
| a turn or a compaction finished (5.2 s) | `happy` | 44 |
|
|
1102
|
-
| a quiet minute (no work, no pointer move or key) / the next pointer move or key | `sleeping` / `waking` | 10 / 29 |
|
|
1103
|
-
| a click on the face / the tail, four quick clicks, a pull | `poke-left` / `poke-right`, `tickle`, `drag` | 0 |
|
|
941
|
+
Under the DeepSeek brand (or when picked in the Mascot row) the mascot is Deepy,
|
|
942
|
+
the pixel whale of the Deepy theme pack. Its sheets, crop boxes and still frames
|
|
943
|
+
are in `features/mascot/sheets.ts`, like the crab's, and the two share one state
|
|
944
|
+
machine, so a state added for one is a state for both. Its body is Deepy's blue
|
|
945
|
+
with a navy outline and a white belly in both themes; on the dark canvas the
|
|
946
|
+
outline and the soft shadow recede and the body carries the shape.
|
|
947
|
+
|
|
948
|
+
Its ground line sits on the top edge of what it stands on, and its crop box
|
|
949
|
+
reaches past that line, so the box is lifted by that margin: the sprite's last
|
|
950
|
+
row lands on the card's top edge and the shadow's tail stays off the card instead
|
|
951
|
+
of being stamped across it. Its box is inset from the right end of the card it
|
|
952
|
+
stands over — on the home page the composer card under both layouts, in a
|
|
953
|
+
conversation the whole input area, and the panel that replaces the card while the
|
|
954
|
+
reader is asked for something. The sheets are scaled down smoothly so the whale
|
|
955
|
+
stays crisp from 1× to 2× screens without `image-rendering: pixelated`, which
|
|
956
|
+
would drop rows on a downscale. Only the resting body takes the pointer. With the
|
|
957
|
+
animation choice resolved to "reduced" each state holds its still frame; a click
|
|
958
|
+
or a pull still plays its reaction.
|
|
1104
959
|
|
|
1105
960
|
## Implementation notes
|
|
1106
961
|
|
|
@@ -1118,4 +973,3 @@ plays its reaction.
|
|
|
1118
973
|
whole: `packages/client/src/shared/page-scale.ts` gives each one a space one pixel to one
|
|
1119
974
|
pixel with the window (D81), and a rule that caps a card against the window reads
|
|
1120
975
|
`--dsh-claude-page-height` / `--dsh-claude-page-width` rather than `vh` / `vw`.
|
|
1121
|
-
|