@workerdeck/ui 2.7.2 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -30
- package/build/{SessionPanel-B5FfiBxE.mjs → SessionPanel-BClL7zUS.mjs} +694 -413
- package/build/SessionPanel-BClL7zUS.mjs.map +1 -0
- package/build/{SessionPanel-_lQLhWOw.d.mts → SessionPanel-BVbau2eR.d.mts} +68 -4
- package/build/format.mjs +1 -1
- package/build/index.d.mts +22 -15
- package/build/index.mjs +6 -6
- package/build/index.mjs.map +1 -1
- package/build/scoped.css +26 -2
- package/build/{status-BO9wJloK.mjs → status-kmsBhuFG.mjs} +3 -3
- package/build/status-kmsBhuFG.mjs.map +1 -0
- package/build/workspace.d.mts +3 -2
- package/build/workspace.mjs +7 -8
- package/build/workspace.mjs.map +1 -1
- package/package.json +18 -18
- package/src/components/agent/Composer.tsx +86 -84
- package/src/components/agent/ContextDialog.tsx +1 -1
- package/src/components/agent/EntryIcon.tsx +1 -1
- package/src/components/agent/FileTree.tsx +1 -1
- package/src/components/agent/FileViewer.tsx +1 -1
- package/src/components/agent/HostFilesDialog.tsx +2 -2
- package/src/components/agent/PermissionModeSelect.tsx +2 -2
- package/src/components/agent/PermissionPrompt.tsx +2 -2
- package/src/components/agent/SessionBrowser.tsx +1 -1
- package/src/components/agent/SessionItem.tsx +4 -2
- package/src/components/agent/SessionPanel.tsx +306 -226
- package/src/components/agent/SessionWorkspace.tsx +3 -0
- package/src/components/agent/SkillsDialog.tsx +3 -3
- package/src/components/agent/StatusBar.tsx +1 -1
- package/src/components/agent/TaskList.tsx +2 -2
- package/src/components/agent/TranscriptRows.tsx +1 -1
- package/src/components/agent/UsageDialog.tsx +1 -1
- package/src/components/agent/UsageMeters.tsx +17 -4
- package/src/components/agent/composer-commands.ts +142 -0
- package/src/components/agent/use-path-links.ts +2 -7
- package/src/components/agent/use-subagent-frame.ts +2 -2
- package/src/components/agent/use-transcript-jumps.ts +2 -2
- package/src/components/prompt-area/clipboard-helpers.ts +2 -2
- package/src/components/prompt-area/cursor-helpers.ts +7 -7
- package/src/components/prompt-area/dom-helpers.ts +8 -8
- package/src/components/prompt-area/html-to-markdown.ts +2 -2
- package/src/components/prompt-area/prompt-area-engine.ts +4 -4
- package/src/components/prompt-area/prompt-area-list-ops.ts +10 -10
- package/src/components/prompt-area/prompt-area.tsx +2 -2
- package/src/components/prompt-area/trigger-presets.ts +6 -6
- package/src/components/prompt-area/types.ts +6 -6
- package/src/components/prompt-area/use-chip-editing.ts +8 -8
- package/src/components/prompt-area/use-markdown-mode.ts +3 -3
- package/src/components/prompt-area/use-prompt-area-events.ts +3 -3
- package/src/components/prompt-area/use-prompt-area-keydown.ts +3 -3
- package/src/components/prompt-area/use-prompt-area-state.ts +4 -4
- package/src/components/prompt-area/use-prompt-area.ts +5 -5
- package/src/components/prompt-area/use-trigger-search.ts +1 -1
- package/src/components/terminal/PermissionPrompt.tsx +4 -4
- package/src/components/terminal/affordances.tsx +1 -1
- package/src/components/terminal/file-link.tsx +20 -0
- package/src/components/terminal/height.ts +2 -2
- package/src/components/terminal/items.tsx +2 -2
- package/src/components/terminal/markdown.tsx +31 -5
- package/src/components/terminal/scrubber.tsx +1 -1
- package/src/components/terminal/todos.ts +1 -1
- package/src/components/ui/Splitter.tsx +1 -1
- package/src/index.ts +11 -1
- package/src/lib/context-note.ts +3 -3
- package/src/lib/file-link.ts +70 -0
- package/src/lib/format.ts +2 -2
- package/src/styles/scoped.entry.css +2 -2
- package/src/styles/terminal.css +94 -79
- package/src/styles/theme.css +39 -39
- package/build/SessionPanel-B5FfiBxE.mjs.map +0 -1
- package/build/status-BO9wJloK.mjs.map +0 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Build entry for the SCOPED stylesheet (`@workerdeck/ui/scoped.css`)
|
|
2
|
+
* Build entry for the SCOPED stylesheet (`@workerdeck/ui/scoped.css`) - see
|
|
3
3
|
* `scripts/build-scoped-css.mjs`, which compiles this with the Tailwind CLI and
|
|
4
4
|
* then rewrites every selector under `.wd-root`. Not shipped as-is and not an
|
|
5
5
|
* export: embedders import the compiled `scoped.css` artifact.
|
|
6
6
|
*
|
|
7
7
|
* `source(none)` + explicit `@source`: the utilities compiled here are exactly
|
|
8
8
|
* the ones this package's own components (and streamdown, whose markdown
|
|
9
|
-
* renderer ships Tailwind-classed markup) use
|
|
9
|
+
* renderer ships Tailwind-classed markup) use - hermetic, independent of any
|
|
10
10
|
* host app's Tailwind build.
|
|
11
11
|
*/
|
|
12
12
|
@import 'tailwindcss' source(none);
|
package/src/styles/terminal.css
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* *renderer*: every row it draws lands on the same character cell, and a grid
|
|
6
6
|
* that has to be reassembled from `gap-2 pl-[calc(...)] -mx-1` at each call site
|
|
7
7
|
* is a grid that drifts the first time someone edits one row. The components in
|
|
8
|
-
* `components/terminal/` are therefore thin
|
|
8
|
+
* `components/terminal/` are therefore thin - they choose a class and a tone,
|
|
9
9
|
* and the geometry lives here in one place where it can be read as a whole.
|
|
10
10
|
*
|
|
11
11
|
* It also means an embedder gets the geometry without configuring Tailwind's
|
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
* face is exactly one cell, so a gutter is `2ch` and an indent level is
|
|
19
19
|
* `2ch`, and text lands on a column rather than near one. A wrapped line
|
|
20
20
|
* hangs under its own first character because the body is its own grid
|
|
21
|
-
* column
|
|
21
|
+
* column - not because a padding and a negative text-indent were tuned
|
|
22
22
|
* against each other.
|
|
23
23
|
* 2. **Vertical measures are whole multiples of `--term-line`, never padding.**
|
|
24
24
|
* Blank space between blocks is a *blank line* (an empty row), the way it is
|
|
25
25
|
* in a terminal. Nothing has margins, so no two blocks can disagree about
|
|
26
|
-
* the rhythm, and a row's height is always an integer number of lines
|
|
26
|
+
* the rhythm, and a row's height is always an integer number of lines - the
|
|
27
27
|
* thing that keeps text off half-pixels and out of the blur.
|
|
28
28
|
*
|
|
29
29
|
* `--term-font-size` and `--term-line` must both be whole pixels. A line height
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
[data-terminal] {
|
|
36
|
-
/* Metrics. Overridable per host
|
|
37
|
-
phone wants a larger cell
|
|
36
|
+
/* Metrics. Overridable per host - VS Code hands its editor font down, and a
|
|
37
|
+
phone wants a larger cell - but always in whole pixels. */
|
|
38
38
|
--term-font-size: 13px;
|
|
39
39
|
--term-line: 18px;
|
|
40
40
|
/* One indent level, and the width of a gutter. The same value on purpose:
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
font-feature-settings:
|
|
52
52
|
'liga' 0,
|
|
53
53
|
'calt' 0;
|
|
54
|
-
/* `ch` is the advance of `0`, so tabular figures keep digit columns
|
|
55
|
-
diff's line numbers
|
|
54
|
+
/* `ch` is the advance of `0`, so tabular figures keep digit columns - the
|
|
55
|
+
diff's line numbers - genuinely aligned. */
|
|
56
56
|
font-variant-numeric: tabular-nums;
|
|
57
57
|
-webkit-font-smoothing: antialiased;
|
|
58
58
|
tab-size: 2;
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
--term-diff-fg: #9a9a9a;
|
|
97
97
|
--term-diff-num: #5a5a5a;
|
|
98
98
|
|
|
99
|
-
/* A band of code or output. Barely there
|
|
99
|
+
/* A band of code or output. Barely there - one step off the canvas. */
|
|
100
100
|
--term-band-bg: rgb(255 255 255 / 0.04);
|
|
101
101
|
/* The user's own prompt row. */
|
|
102
102
|
--term-user-bg: rgb(255 255 255 / 0.05);
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
/* Behind an OPEN block. Yellow, not neutral, and the theme's one deliberate
|
|
105
105
|
reuse of that tone for something other than "waiting on you": an open block
|
|
106
106
|
is a state the reader put the transcript into, and on the phone it wants
|
|
107
|
-
the same colour as the rail mark that says so. Kept very low
|
|
107
|
+
the same colour as the rail mark that says so. Kept very low - this washes
|
|
108
108
|
whole regions, and at band strength an opened run would shout louder than
|
|
109
109
|
anything inside it. */
|
|
110
110
|
--term-open-wash: rgb(215 186 125 / 0.1);
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
min-height: var(--term-line);
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
/* Indent levels. A nested row steps one cell per level
|
|
153
|
+
/* Indent levels. A nested row steps one cell per level - the same 2 columns a
|
|
154
154
|
marker occupies, so a child's marker sits under its parent's first letter. */
|
|
155
155
|
.term-row[data-indent='1'] {
|
|
156
156
|
padding-left: var(--term-cell);
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
.term-gutter {
|
|
166
|
-
/* `pre` so a space-only gutter keeps its cell
|
|
166
|
+
/* `pre` so a space-only gutter keeps its cell - an unmarked row must still
|
|
167
167
|
start its text on the same column as a marked one. */
|
|
168
168
|
white-space: pre;
|
|
169
169
|
user-select: none;
|
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
drag that *begins* on an unselectable node starts no selection at all, so
|
|
173
173
|
without this the most natural way to select transcript text did nothing.
|
|
174
174
|
Letting the press fall through to the row keeps `user-select: none` doing
|
|
175
|
-
its real job
|
|
175
|
+
its real job - keeping the glyph out of the copied text - without owning
|
|
176
176
|
the pointer. The row below is still pressable from here, since the press
|
|
177
177
|
handler lives on the ancestor. */
|
|
178
178
|
pointer-events: none;
|
|
@@ -188,7 +188,7 @@
|
|
|
188
188
|
overflow-wrap: break-word;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
/* A blank line
|
|
191
|
+
/* A blank line - the only vertical spacing in the theme. Exactly one row tall
|
|
192
192
|
and holding nothing, which is what a terminal's blank line is. */
|
|
193
193
|
.term-blank {
|
|
194
194
|
height: var(--term-line);
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
/* A row `scrollIntoView`ed lands one line below the top edge rather than flush
|
|
198
198
|
against it, so the target reads as the start of something rather than as
|
|
199
199
|
content cut off by the frame. Only the recap's `'center'` jump still goes
|
|
200
|
-
through scrollIntoView
|
|
200
|
+
through scrollIntoView - the scrubber's `'start'` jumps compute their own
|
|
201
201
|
offset (a sticky row's rect is wherever it is pinned) and land the line at
|
|
202
202
|
exactly the top edge. */
|
|
203
203
|
[data-terminal] [data-slot='transcript-rows'] [data-index] {
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
/* ── Bands ───────────────────────────────────────────────────────────────────
|
|
216
216
|
*
|
|
217
217
|
* A run of rows carrying a background: a code block, a command's output, a diff
|
|
218
|
-
* hunk. Full-bleed
|
|
218
|
+
* hunk. Full-bleed - the wash reaches the scroller's edges rather than stopping
|
|
219
219
|
* at a content gutter, because in a terminal the line *is* the full width.
|
|
220
220
|
* `--term-bleed` is how far out that is; the transcript sets it to its own
|
|
221
221
|
* padding and the band cancels it with matched negative margin and padding.
|
|
@@ -264,13 +264,13 @@
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
/* Weight is the only other axis. A terminal has one type size, so emphasis has
|
|
267
|
-
to be carried by weight and colour
|
|
267
|
+
to be carried by weight and colour - never by a larger glyph. */
|
|
268
268
|
[data-terminal] [data-weight='bold'] {
|
|
269
269
|
font-weight: 600;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
/* The scroller. Its horizontal padding IS `--term-bleed`
|
|
273
|
-
contract a full-bleed band relies on
|
|
272
|
+
/* The scroller. Its horizontal padding IS `--term-bleed` - that is the whole
|
|
273
|
+
contract a full-bleed band relies on - and its vertical padding is a line, so
|
|
274
274
|
the first row starts one blank line down like every other block. */
|
|
275
275
|
[data-terminal].term-transcript {
|
|
276
276
|
padding-inline: var(--term-bleed, 0px);
|
|
@@ -280,7 +280,7 @@
|
|
|
280
280
|
/* ── Interaction ─────────────────────────────────────────────────────────────
|
|
281
281
|
*
|
|
282
282
|
* A terminal has no buttons, but a transcript does have things you can open. The
|
|
283
|
-
* affordance is the hover fill on the row itself
|
|
283
|
+
* affordance is the hover fill on the row itself - nothing gains a border, a
|
|
284
284
|
* radius or a focus box that would take it off the grid.
|
|
285
285
|
*/
|
|
286
286
|
[data-terminal] .term-press {
|
|
@@ -299,7 +299,7 @@
|
|
|
299
299
|
this is in `press.tsx`: the click that ends such a drag is refused. */
|
|
300
300
|
user-select: text;
|
|
301
301
|
}
|
|
302
|
-
/* The hover fill lives with the other affordances below
|
|
302
|
+
/* The hover fill lives with the other affordances below - it is one, and it is
|
|
303
303
|
switchable for the same reason. */
|
|
304
304
|
[data-terminal] .term-press:focus-visible {
|
|
305
305
|
outline: none;
|
|
@@ -307,9 +307,9 @@
|
|
|
307
307
|
|
|
308
308
|
/* ── Affordances ─────────────────────────────────────────────────────────────
|
|
309
309
|
*
|
|
310
|
-
* What a real terminal cannot do. Every one of these costs **no layout**
|
|
310
|
+
* What a real terminal cannot do. Every one of these costs **no layout** - a
|
|
311
311
|
* fill is a background, an action is an absolutely positioned overlay one line
|
|
312
|
-
* tall
|
|
312
|
+
* tall - so switching them off leaves every glyph on the cell it was already on.
|
|
313
313
|
* See `components/terminal/affordances.tsx`.
|
|
314
314
|
*/
|
|
315
315
|
|
|
@@ -326,7 +326,7 @@
|
|
|
326
326
|
/* An opened block keeps the fill the pointer gave it, for the whole of what it
|
|
327
327
|
opened. Eighty lines of output pushes its own first line off the top of the
|
|
328
328
|
screen, and without a wash there is nothing to say where the thing you
|
|
329
|
-
expanded began
|
|
329
|
+
expanded began - or that these rows are one block at all. Full-bleed, like
|
|
330
330
|
every other wash; the hover fill inside it reaches the same edge, so the two
|
|
331
331
|
stack without a step. (The other half is `useRevealOnOpen`, which brings that
|
|
332
332
|
first line back when it has gone above the fold.) */
|
|
@@ -339,7 +339,7 @@
|
|
|
339
339
|
/* Another frame of reference: the rows a subagent produced, inside the `Task`
|
|
340
340
|
that spawned them.
|
|
341
341
|
|
|
342
|
-
Two cells of indent, whole
|
|
342
|
+
Two cells of indent, whole - the theme's rule is that horizontal measures are
|
|
343
343
|
`ch`, and these rows carry markers in a gutter that has to keep landing on the
|
|
344
344
|
grid the rows above it use. The rule itself is drawn *inside* that padding
|
|
345
345
|
with an inset shadow rather than as a border, which is what makes the 2ch
|
|
@@ -349,7 +349,7 @@
|
|
|
349
349
|
`--term-faint` rather than the cards `--border` token, and that is the whole
|
|
350
350
|
reason this class exists: the nested block sits on the *open* block's wash,
|
|
351
351
|
and a border colour tuned for the base ground resolved to rgb(31,31,31) on it
|
|
352
|
-
|
|
352
|
+
- a rule nobody could see. The same trap `row-hover` is alpha to avoid. */
|
|
353
353
|
[data-terminal] .term-nested {
|
|
354
354
|
padding-left: 2ch;
|
|
355
355
|
box-shadow: inset 1px 0 0 var(--term-faint);
|
|
@@ -396,14 +396,14 @@
|
|
|
396
396
|
color: var(--term-fg);
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
-
/* What you typed, on a band of its own
|
|
399
|
+
/* What you typed, on a band of its own - the CLI's own treatment. */
|
|
400
400
|
[data-terminal] .term-user {
|
|
401
401
|
background: var(--term-user-bg);
|
|
402
402
|
margin-inline: calc(-1 * var(--term-bleed, 0px));
|
|
403
403
|
padding-inline: var(--term-bleed, 0px);
|
|
404
404
|
/* Declared, never inherited. This band is the one transcript block that is
|
|
405
405
|
neither a `.term-press` nor plain flow text, so without saying so it takes
|
|
406
|
-
whatever the host gives it
|
|
406
|
+
whatever the host gives it - which differs between the browser and the VS
|
|
407
407
|
Code webview, and a block that cannot host a selection endpoint does not
|
|
408
408
|
merely refuse the drag: the selection jumps to the next valid position
|
|
409
409
|
above it, swallowing everything in between. */
|
|
@@ -416,10 +416,25 @@
|
|
|
416
416
|
text-underline-offset: 2px;
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
+
/* A link to a file opens through the host rather than the address bar, so it is a button.
|
|
420
|
+
* Outside `[data-terminal]` too: a cards transcript renders the same markdown. */
|
|
421
|
+
button.term-link {
|
|
422
|
+
appearance: none;
|
|
423
|
+
background: none;
|
|
424
|
+
border: 0;
|
|
425
|
+
padding: 0;
|
|
426
|
+
font: inherit;
|
|
427
|
+
color: inherit;
|
|
428
|
+
cursor: pointer;
|
|
429
|
+
text-align: inherit;
|
|
430
|
+
text-decoration: underline;
|
|
431
|
+
text-underline-offset: 2px;
|
|
432
|
+
}
|
|
433
|
+
|
|
419
434
|
/* A file path, while the modifier that would open it is held.
|
|
420
435
|
*
|
|
421
436
|
* Only then, and that is the point: a path *is* clickable but only under
|
|
422
|
-
* Cmd/Ctrl, so it should only look clickable then
|
|
437
|
+
* Cmd/Ctrl, so it should only look clickable then - an always-underlined path
|
|
423
438
|
* promises a plain click that does nothing. Applied by `usePathLinks` using the
|
|
424
439
|
* same test the click itself makes, so nothing lights up that a click ignores.
|
|
425
440
|
* Outside `[data-terminal]` too, since a cards transcript names files as well. */
|
|
@@ -430,7 +445,7 @@
|
|
|
430
445
|
}
|
|
431
446
|
|
|
432
447
|
/* A status-line reading that leads somewhere. Inline and unstyled until hovered
|
|
433
|
-
|
|
448
|
+
- the line is meant to be quiet, and five underlined segments would make it
|
|
434
449
|
the loudest row on screen. */
|
|
435
450
|
[data-terminal] .term-reading {
|
|
436
451
|
font: inherit;
|
|
@@ -452,7 +467,7 @@
|
|
|
452
467
|
* row is a fill, and the only "control" is a row you can press a number on.
|
|
453
468
|
*/
|
|
454
469
|
|
|
455
|
-
/* One line tall with the stroke through its middle, full-bleed
|
|
470
|
+
/* One line tall with the stroke through its middle, full-bleed - the boundary
|
|
456
471
|
between the run and the decision. */
|
|
457
472
|
[data-terminal] .term-rule-row {
|
|
458
473
|
height: var(--term-line);
|
|
@@ -474,7 +489,7 @@
|
|
|
474
489
|
}
|
|
475
490
|
|
|
476
491
|
/* An option's expanded detail (a preview, a reason field) lines up with the
|
|
477
|
-
option's own text
|
|
492
|
+
option's own text - one gutter in, so it reads as belonging to that row. */
|
|
478
493
|
[data-terminal] .term-detail {
|
|
479
494
|
padding-left: 5ch;
|
|
480
495
|
}
|
|
@@ -511,7 +526,7 @@
|
|
|
511
526
|
color: var(--bg, #000);
|
|
512
527
|
}
|
|
513
528
|
|
|
514
|
-
/* The bare text field
|
|
529
|
+
/* The bare text field - a caret and a rule, no box. */
|
|
515
530
|
[data-terminal] .term-input {
|
|
516
531
|
width: 100%;
|
|
517
532
|
min-width: 0;
|
|
@@ -524,7 +539,7 @@
|
|
|
524
539
|
/* The terminal's own caret: a filled cell, not a hairline. It is the last
|
|
525
540
|
thing in the theme still drawn the way the web draws it, and it is the one
|
|
526
541
|
the eye is always on. `caret-animation: manual` hands the blink to the
|
|
527
|
-
author and we then decline to animate it, so the block sits steady
|
|
542
|
+
author and we then decline to animate it, so the block sits steady - a
|
|
528
543
|
blinking block is a strobe at this size, where a blinking hairline was
|
|
529
544
|
merely a caret. Both are ignored where unsupported, which is exactly the
|
|
530
545
|
right failure: a thin blinking caret is a plain caret, not a broken one. */
|
|
@@ -536,7 +551,7 @@
|
|
|
536
551
|
no padding and `line-height` is `--term-line`, so the content box is always a
|
|
537
552
|
whole number of lines and the rule under it lands on a cell boundary. The cap is
|
|
538
553
|
lines too, not a pixel height. Where `field-sizing` is unsupported this is a
|
|
539
|
-
one-line box that scrolls
|
|
554
|
+
one-line box that scrolls - the old single-line behaviour, minus the sideways
|
|
540
555
|
crawl, so the degradation costs nothing that was there before. */
|
|
541
556
|
[data-terminal] textarea.term-input {
|
|
542
557
|
display: block;
|
|
@@ -556,8 +571,8 @@
|
|
|
556
571
|
*
|
|
557
572
|
* The pinned prompt is the prompt's **first line**, and the pin is the
|
|
558
573
|
* browser's, not script's. `TranscriptRows` renders each prompt inside a lane
|
|
559
|
-
* spanning its turn; a one-line `sticky` **head**
|
|
560
|
-
* again, clipped by height
|
|
574
|
+
* spanning its turn; a one-line `sticky` **head** - the same row rendered
|
|
575
|
+
* again, clipped by height - leads the lane with its flow footprint cancelled
|
|
561
576
|
* (the negative bottom margin), so in flow it lies exactly over the real
|
|
562
577
|
* row's first line and is invisible. The compositor pins it (no per-scroll
|
|
563
578
|
* JS, no frame lag) and the lane's bottom edge pushes it off when the next
|
|
@@ -567,18 +582,18 @@
|
|
|
567
582
|
* The head's visible line docks flush to the top edge of the scroller. Its
|
|
568
583
|
* inter-row gap is padding on the head (matching the row it overlays), so a
|
|
569
584
|
* gap-carrying head is one line taller and pins one line negative: the
|
|
570
|
-
* padding parks above the viewport edge and the line starts at zero
|
|
585
|
+
* padding parks above the viewport edge and the line starts at zero - which
|
|
571
586
|
* is also what makes the pin engage exactly when the line itself reaches the
|
|
572
587
|
* top, not a line early.
|
|
573
588
|
*
|
|
574
589
|
* The lane spans the whole turn and the head is an overlay, so both are
|
|
575
|
-
* transparent to the pointer
|
|
576
|
-
* head is `aria-hidden` in the markup. The z-index is on the lane
|
|
590
|
+
* transparent to the pointer - the rows it covers own interaction, and the
|
|
591
|
+
* head is `aria-hidden` in the markup. The z-index is on the lane -
|
|
577
592
|
* every row wrapper is its own stacking context (they are transformed), so
|
|
578
593
|
* the head's own z-index could never beat a later sibling.
|
|
579
594
|
*
|
|
580
595
|
* `--bg` is the panel's own ground, the same token the hover-revealed row
|
|
581
|
-
* actions cover themselves with
|
|
596
|
+
* actions cover themselves with - the user band is translucent by design, so
|
|
582
597
|
* without a ground the rows passing beneath would show straight through the
|
|
583
598
|
* pinned head. Opaque always, which is invisible in flow: the ground behind
|
|
584
599
|
* it is the same `--bg`, and the row it covers is its own content.
|
|
@@ -591,14 +606,14 @@
|
|
|
591
606
|
/* The prompt itself is *inside* the lane, not beneath it, so the lane's `none`
|
|
592
607
|
reached it too and made a user message the one block in the transcript the
|
|
593
608
|
pointer could not touch: a drag started on it landed on the lane instead and
|
|
594
|
-
selected nothing. Only the real row is given back
|
|
609
|
+
selected nothing. Only the real row is given back - the head overlay above
|
|
595
610
|
stays transparent, which is the whole point of the lane. */
|
|
596
611
|
[data-terminal] [data-sticky-lane] > [data-index] {
|
|
597
612
|
pointer-events: auto;
|
|
598
613
|
}
|
|
599
614
|
|
|
600
615
|
/* The head's own strip: the full lane, out of flow, so the head needs no
|
|
601
|
-
footprint-cancelling margin
|
|
616
|
+
footprint-cancelling margin - sticky confinement clamps the margin box, and
|
|
602
617
|
a negative margin shrinks it to nothing, letting the head overshoot the
|
|
603
618
|
lane's end by its own height (two pinned prompts on screen at once). */
|
|
604
619
|
[data-terminal] [data-sticky-lane] > [data-sticky-headlane] {
|
|
@@ -609,7 +624,7 @@
|
|
|
609
624
|
|
|
610
625
|
/* Hidden until actually stuck (`data-stuck`, set by the lane's sentinel
|
|
611
626
|
observer): an overlay visible in flow sits on the real row's first line and
|
|
612
|
-
swallows its selection highlight
|
|
627
|
+
swallows its selection highlight - the line reads as unselectable. */
|
|
613
628
|
[data-terminal] [data-sticky-headlane] > [data-sticky-head] {
|
|
614
629
|
position: sticky;
|
|
615
630
|
top: 0;
|
|
@@ -632,19 +647,19 @@
|
|
|
632
647
|
/* ── Composer ────────────────────────────────────────────────────────────────
|
|
633
648
|
*
|
|
634
649
|
* The foot of the panel, and a *row* like every other: `>` in the gutter cell,
|
|
635
|
-
* the caret on the body column. That is the whole design
|
|
650
|
+
* the caret on the body column. That is the whole design - a typed line and a
|
|
636
651
|
* rendered one start on the same character, which no chat composer does and
|
|
637
652
|
* every terminal does for free.
|
|
638
653
|
*
|
|
639
654
|
* Docked rather than floating: no radius, no shadow, and a rule along the top
|
|
640
655
|
* *and* the bottom, both turning accent while anything inside has focus. Those
|
|
641
656
|
* two rules are the CLI's own frame for its prompt, and they are the only
|
|
642
|
-
* affordance
|
|
657
|
+
* affordance - which is what the transcript above (no boxes anywhere) asks for.
|
|
643
658
|
* They also do the work a box would: the field reads as its own strip of the
|
|
644
659
|
* panel rather than as the last row of the transcript, without a border on the
|
|
645
660
|
* sides that would take the `❯` off the column the rows start on.
|
|
646
661
|
*
|
|
647
|
-
* The bottom rule is why the status bar under it draws none of its own
|
|
662
|
+
* The bottom rule is why the status bar under it draws none of its own - see
|
|
648
663
|
* `StatusBar.tsx`. Two adjacent 1px rules is a 2px rule with a seam in it.
|
|
649
664
|
*/
|
|
650
665
|
[data-terminal].term-composer {
|
|
@@ -654,7 +669,7 @@
|
|
|
654
669
|
}
|
|
655
670
|
/* The bar spans the panel; its contents sit in the transcript's own column, so
|
|
656
671
|
the `>` lands on the column the rows above it start on. Two elements because
|
|
657
|
-
those are two different widths
|
|
672
|
+
those are two different widths - a rule that stopped at the content column
|
|
658
673
|
would read as a card, which is the one thing this theme has none of. */
|
|
659
674
|
[data-terminal] .term-composer-body {
|
|
660
675
|
margin-inline: auto;
|
|
@@ -663,7 +678,7 @@
|
|
|
663
678
|
padding-inline: var(--term-bleed, 0px);
|
|
664
679
|
/* Air on each side of the prompt, and a plain pixel value rather than a
|
|
665
680
|
fraction of `--term-line`: this is chrome between two rules, not a
|
|
666
|
-
transcript row, and the whole-multiple rule governs rows
|
|
681
|
+
transcript row, and the whole-multiple rule governs rows - a computed
|
|
667
682
|
half-line would be exactly the fractional metric that rule exists to
|
|
668
683
|
forbid. */
|
|
669
684
|
padding-block: 8px;
|
|
@@ -674,7 +689,7 @@
|
|
|
674
689
|
}
|
|
675
690
|
|
|
676
691
|
/* Shell mode. The frame and the gutter glyph move together and both go magenta, because the thing
|
|
677
|
-
the mode changes is where the text goes
|
|
692
|
+
the mode changes is where the text goes - a subtle cue would be the wrong kind of quiet. Listed
|
|
678
693
|
after the focus rule so it wins while the field is focused, which is the whole time it is on. */
|
|
679
694
|
[data-terminal].term-composer-shell,
|
|
680
695
|
[data-terminal].term-composer-shell:focus-within {
|
|
@@ -684,7 +699,7 @@
|
|
|
684
699
|
color: var(--term-dim);
|
|
685
700
|
}
|
|
686
701
|
|
|
687
|
-
/* The contentEditable field. `font: inherit` is what makes it the cell
|
|
702
|
+
/* The contentEditable field. `font: inherit` is what makes it the cell - the
|
|
688
703
|
surface above already carries the size and the line height, and anything that
|
|
689
704
|
restates them here can disagree with the rows. */
|
|
690
705
|
[data-terminal] .term-composer-field {
|
|
@@ -702,7 +717,7 @@
|
|
|
702
717
|
* badge outside its top-right corner, over a strip with its own bottom rule.
|
|
703
718
|
* None of that survives here: this theme has no rounded corners and no badges
|
|
704
719
|
* floating off an edge, and the strip sits *inside* the composer's frame, which
|
|
705
|
-
* already draws the only rule there is
|
|
720
|
+
* already draws the only rule there is - a second one directly above the field
|
|
706
721
|
* reads as the box this theme exists not to have.
|
|
707
722
|
*
|
|
708
723
|
* So the thumbnail is a plain 1px cell, and the remove control is tucked into
|
|
@@ -711,7 +726,7 @@
|
|
|
711
726
|
* puts it just inside the thumbnail's border instead of straddling it.
|
|
712
727
|
*
|
|
713
728
|
* The strip's only spacing is the 8px the composer already uses above and below
|
|
714
|
-
* its prompt
|
|
729
|
+
* its prompt - a plain pixel value, and deliberately, for the reason given
|
|
715
730
|
* there: this is chrome between rules, not a transcript row, so the
|
|
716
731
|
* whole-multiple-of-`--term-line` rule does not govern it. No side padding: the
|
|
717
732
|
* strip is already inside the body column, so padding here would step it in
|
|
@@ -724,7 +739,7 @@
|
|
|
724
739
|
}
|
|
725
740
|
/* A refused upload, in the terminal's own red. This needs to be said here
|
|
726
741
|
rather than left to the card form's `border-danger/50`: the rule above sets
|
|
727
|
-
the `border` shorthand, colour included, and outranks a utility class
|
|
742
|
+
the `border` shorthand, colour included, and outranks a utility class - so
|
|
728
743
|
without this the one state that must stand out is the one that looks
|
|
729
744
|
ordinary. */
|
|
730
745
|
[data-terminal] .term-attachment[data-failed] {
|
|
@@ -739,7 +754,7 @@
|
|
|
739
754
|
|
|
740
755
|
/* A composer action as a character: one cell wide, one line tall, so pressing
|
|
741
756
|
it costs no width and removing it would move nothing. Dim until you reach for
|
|
742
|
-
it
|
|
757
|
+
it - the same restraint the row actions show. */
|
|
743
758
|
[data-terminal] .term-glyph {
|
|
744
759
|
flex: none;
|
|
745
760
|
width: var(--term-cell);
|
|
@@ -755,8 +770,8 @@
|
|
|
755
770
|
cursor: pointer;
|
|
756
771
|
}
|
|
757
772
|
/* A glyph standing in the **gutter cell** is a marker, so it aligns like one.
|
|
758
|
-
The cell is 2ch and a character is 1ch, so `text-align: center`
|
|
759
|
-
standalone control at the row's trailing edge
|
|
773
|
+
The cell is 2ch and a character is 1ch, so `text-align: center` - right for a
|
|
774
|
+
standalone control at the row's trailing edge - lands the glyph half a cell
|
|
760
775
|
(≈3.9px at 13px/JetBrains Mono) to the right of the column every `❯` and `●`
|
|
761
776
|
in the transcript starts on. `start` is what `.term-gutter` uses, and on this
|
|
762
777
|
grid "nearly the same column" is just the wrong column. */
|
|
@@ -784,7 +799,7 @@
|
|
|
784
799
|
* An added or removed line is a **band**, not coloured text: the wash runs the
|
|
785
800
|
* full width of the screen, the way it does in a terminal, so the shape of a
|
|
786
801
|
* change is readable from the scrollbar without reading a word of it. The line
|
|
787
|
-
* numbers ride inside the wash
|
|
802
|
+
* numbers ride inside the wash - they are part of the changed line.
|
|
788
803
|
*/
|
|
789
804
|
/* A diff belongs to the call above it, so it steps in one cell like any other
|
|
790
805
|
tool output. */
|
|
@@ -813,7 +828,7 @@
|
|
|
813
828
|
[data-terminal] .term-diff .term-row[data-diff='context'] {
|
|
814
829
|
color: var(--term-diff-fg);
|
|
815
830
|
}
|
|
816
|
-
/* The number column is dimmer than the line it belongs to in every case
|
|
831
|
+
/* The number column is dimmer than the line it belongs to in every case - it is
|
|
817
832
|
an address, not content. `.term-gutter`'s own colour would flatten all three
|
|
818
833
|
to one grey, so each state dims its own. */
|
|
819
834
|
[data-terminal] .term-diff .term-row > .term-gutter {
|
|
@@ -829,13 +844,13 @@
|
|
|
829
844
|
/* ── Markdown ────────────────────────────────────────────────────────────────
|
|
830
845
|
*
|
|
831
846
|
* The element map is in `components/terminal/markdown.tsx`; this is the little
|
|
832
|
-
* CSS it needs. Nothing here is an override
|
|
847
|
+
* CSS it needs. Nothing here is an override - the renderer is told what to emit,
|
|
833
848
|
* so there is no default to undo and no `!important` anywhere in the theme.
|
|
834
849
|
*/
|
|
835
850
|
|
|
836
851
|
/* react-markdown emits the source's newlines as text nodes between blocks. The
|
|
837
852
|
surrounding rows are `pre-wrap` (a plain line of output must keep its own
|
|
838
|
-
spacing), which would render every one of those as a visible blank line
|
|
853
|
+
spacing), which would render every one of those as a visible blank line - so
|
|
839
854
|
markdown bodies opt back out, and only a fenced block opts back in. */
|
|
840
855
|
.term-md {
|
|
841
856
|
white-space: normal;
|
|
@@ -844,7 +859,7 @@
|
|
|
844
859
|
/* And it has to reach `.term-body` explicitly, because a list item *is* a Row
|
|
845
860
|
and Rows carry `pre-wrap` (a plain line of tool output must keep its own
|
|
846
861
|
spacing). Inherited, that turns every newline react-markdown emits between an
|
|
847
|
-
item's block children into a visible empty line
|
|
862
|
+
item's block children into a visible empty line - a nested list drew k+2
|
|
848
863
|
phantom blanks, so a two-line nested item was 72px of 18px rows. Models nest
|
|
849
864
|
lists constantly, and the grid audit cannot see it: the phantoms are whole
|
|
850
865
|
lines, so every row still lands on the grid. Fenced code opts back in for
|
|
@@ -856,7 +871,7 @@
|
|
|
856
871
|
|
|
857
872
|
/* The one vertical rule: consecutive blocks are one blank line apart. A margin
|
|
858
873
|
rather than an inserted row, because this row list belongs to the markdown
|
|
859
|
-
renderer
|
|
874
|
+
renderer - but it is exactly one line, so the grid holds. */
|
|
860
875
|
.term-md .term-block + .term-block {
|
|
861
876
|
margin-top: var(--term-line);
|
|
862
877
|
}
|
|
@@ -889,7 +904,7 @@
|
|
|
889
904
|
background: linear-gradient(var(--term-faint), var(--term-faint)) left / 1px 100% no-repeat;
|
|
890
905
|
}
|
|
891
906
|
|
|
892
|
-
/* A horizontal rule is one line tall with the rule through its middle
|
|
907
|
+
/* A horizontal rule is one line tall with the rule through its middle - the row
|
|
893
908
|
it occupies is a whole line either way. */
|
|
894
909
|
.term-md .term-rule {
|
|
895
910
|
height: var(--term-line);
|
|
@@ -921,7 +936,7 @@
|
|
|
921
936
|
border-collapse: collapse;
|
|
922
937
|
/* `max-content` and **no** `max-width`. A cap of `100%` looks like the
|
|
923
938
|
defensive choice and is the opposite: it clamps a wide table below its
|
|
924
|
-
natural width, so the cells wrap and a 14-row table becomes 19 lines tall
|
|
939
|
+
natural width, so the cells wrap and a 14-row table becomes 19 lines tall -
|
|
925
940
|
which breaks the theme's premise (a row is a line) and with it the height
|
|
926
941
|
calculator, whose table model counts rows. It also silently disables the
|
|
927
942
|
wrapper's own `overflow-x`, since a table that shrinks never overflows.
|
|
@@ -943,15 +958,15 @@
|
|
|
943
958
|
/* ── Scrubber ────────────────────────────────────────────────────────────────
|
|
944
959
|
*
|
|
945
960
|
* The overview ruler (`components/terminal/scrubber.tsx`): a 12px rail of marks
|
|
946
|
-
* over the scroller's right edge
|
|
961
|
+
* over the scroller's right edge - VS Code's strip beside the minimap, with
|
|
947
962
|
* this transcript's semantics. It is *chrome*, not rows: absolutely positioned
|
|
948
963
|
* over the scroll container so the transcript's grid is untouched, and drawn
|
|
949
964
|
* entirely from --term-* tokens so it follows the theme wherever the panel is
|
|
950
965
|
* embedded. Its width is the one deliberate exception to the theme's `ch` rule
|
|
951
|
-
*
|
|
966
|
+
* - see the note on `.term-scrubber` below.
|
|
952
967
|
*
|
|
953
968
|
* Interactivity is an affordance. Without `data-interactive` the rail is
|
|
954
|
-
* passive paint with pointer events off
|
|
969
|
+
* passive paint with pointer events off - and the native scrollbar, which
|
|
955
970
|
* stays in that mode, keeps working straight through it.
|
|
956
971
|
*/
|
|
957
972
|
[data-terminal].term-scrubber {
|
|
@@ -977,7 +992,7 @@
|
|
|
977
992
|
touch-action: none;
|
|
978
993
|
}
|
|
979
994
|
|
|
980
|
-
/* The viewport band
|
|
995
|
+
/* The viewport band - where you are: a faint wash with a hairline frame,
|
|
981
996
|
floated over the marks (z-index) so the framed region reads as a window
|
|
982
997
|
onto the rail rather than a stripe behind it. `--term-faint` at 25% rather
|
|
983
998
|
than a literal, so the light palette re-tints it. */
|
|
@@ -992,7 +1007,7 @@
|
|
|
992
1007
|
}
|
|
993
1008
|
|
|
994
1009
|
/* There is no separate cursor line. The band above answers "where am I" on its
|
|
995
|
-
own
|
|
1010
|
+
own - it is outlined, so its top edge already reads as an edge - and a 2px
|
|
996
1011
|
blue rule laid over it was a second indicator of the same fact, in the
|
|
997
1012
|
loudest colour the rail has. */
|
|
998
1013
|
|
|
@@ -1007,7 +1022,7 @@
|
|
|
1007
1022
|
outline: 1px solid color-mix(in srgb, var(--term-bright) 40%, transparent);
|
|
1008
1023
|
}
|
|
1009
1024
|
|
|
1010
|
-
/* Two lanes of 8px
|
|
1025
|
+
/* Two lanes of 8px - what you asked, and what came back - and 'f', the
|
|
1011
1026
|
full-width lane for annotations *about* the run (an error, an approval, a
|
|
1012
1027
|
bookmark, the catch-up seam) rather than steps through it. Pixels, not
|
|
1013
1028
|
percentages: the whole point of the 16px rail is that a lane is a reliable
|
|
@@ -1026,7 +1041,7 @@
|
|
|
1026
1041
|
width: 12px;
|
|
1027
1042
|
}
|
|
1028
1043
|
|
|
1029
|
-
/* Lane marks are two-tone: the first 2px
|
|
1044
|
+
/* Lane marks are two-tone: the first 2px - the mark's own anchor - at full
|
|
1030
1045
|
strength, the rest of the row's extent a 25% tail, so a long answer reads
|
|
1031
1046
|
as long without a tall solid bar shouting over the rail. A minimum-height
|
|
1032
1047
|
mark shows only the solid segment, so short and long marks need no separate
|
|
@@ -1038,7 +1053,7 @@
|
|
|
1038
1053
|
}
|
|
1039
1054
|
/* A sub-agent's stretch of the transcript, in the input lane: green, because
|
|
1040
1055
|
every other colour on this rail is already spoken for and none of them means
|
|
1041
|
-
"somebody else's working"
|
|
1056
|
+
"somebody else's working" - blue is you, white is the answer, red is an
|
|
1042
1057
|
alarm, magenta is your bookmark, yellow is the session waiting on you. Drawn
|
|
1043
1058
|
as an extent (2px head, 25% tail) like the two marks it shares its geometry
|
|
1044
1059
|
with: collapsed it is a tick, expanded it is the band the sub-agent covers. */
|
|
@@ -1054,13 +1069,13 @@
|
|
|
1054
1069
|
.term-scrubber .term-scrub-mark[data-kind='error'] {
|
|
1055
1070
|
background: var(--term-red);
|
|
1056
1071
|
}
|
|
1057
|
-
/* A failed tool call is an alarm, so it is solid like the rest of them
|
|
1058
|
-
response lane, since it is something the run produced
|
|
1072
|
+
/* A failed tool call is an alarm, so it is solid like the rest of them - in the
|
|
1073
|
+
response lane, since it is something the run produced - but at 55%, which is the one thing keeping the rail readable. A session
|
|
1059
1074
|
error is rare and a turn failure rarer; a tool that failed and was recovered
|
|
1060
1075
|
from is routine (a grep that matched nothing, a build fixed on the second go),
|
|
1061
1076
|
and at full strength a normal working session paints the rail solid red and
|
|
1062
1077
|
the two errors that actually ended something stop standing out. Dimmer reads
|
|
1063
|
-
as the same class of thing, one rank quieter
|
|
1078
|
+
as the same class of thing, one rank quieter - which is exactly its rank in
|
|
1064
1079
|
LOUDNESS. */
|
|
1065
1080
|
.term-scrubber .term-scrub-mark[data-kind='toolFailed'] {
|
|
1066
1081
|
background: color-mix(in srgb, var(--term-red) 55%, transparent);
|
|
@@ -1087,8 +1102,8 @@
|
|
|
1087
1102
|
}
|
|
1088
1103
|
}
|
|
1089
1104
|
|
|
1090
|
-
/* The peek. Rendered from transcript state
|
|
1091
|
-
unmounted
|
|
1105
|
+
/* The peek. Rendered from transcript state - the row it describes is usually
|
|
1106
|
+
unmounted - and framed the theme's way: strokes as backgrounds, no border.
|
|
1092
1107
|
pointer-events: none, so it can never trap the pointer that revealed it. */
|
|
1093
1108
|
.term-scrubber .term-scrub-peek {
|
|
1094
1109
|
position: absolute;
|
|
@@ -1124,7 +1139,7 @@
|
|
|
1124
1139
|
|
|
1125
1140
|
/* While an interactive scrubber is mounted it IS the scrollbar; the transcript
|
|
1126
1141
|
stamps this attribute on the scroll element. A passive rail leaves the
|
|
1127
|
-
native scrollbar alone
|
|
1142
|
+
native scrollbar alone - pointer-events: none above is what keeps that
|
|
1128
1143
|
scrollbar reachable through the paint. */
|
|
1129
1144
|
[data-term-scrubber-host] {
|
|
1130
1145
|
scrollbar-width: none;
|
|
@@ -1138,7 +1153,7 @@
|
|
|
1138
1153
|
* A picture a tool returned, drawn in a box of whole lines (`IMAGE_BOX_LINES`,
|
|
1139
1154
|
* set inline by `TerminalImage` so the constant has one spelling across the
|
|
1140
1155
|
* renderer and the height calculator). The height is on the element and nothing
|
|
1141
|
-
* here may add to it: no margin, no border, no padding
|
|
1156
|
+
* here may add to it: no margin, no border, no padding - a box that is not
|
|
1142
1157
|
* exactly N lines is a row the virtualizer was told the wrong size for.
|
|
1143
1158
|
*
|
|
1144
1159
|
* `object-fit: contain` with a top-left origin rather than a centred one: the
|