@workerdeck/ui 2.7.2 → 2.8.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-Dg4xf-ql.mjs} +46 -46
- package/build/SessionPanel-Dg4xf-ql.mjs.map +1 -0
- package/build/format.mjs +1 -1
- package/build/index.d.mts +11 -11
- package/build/index.mjs +4 -4
- package/build/index.mjs.map +1 -1
- package/build/scoped.css +2 -2
- package/build/{status-BO9wJloK.mjs → status-kmsBhuFG.mjs} +3 -3
- package/build/status-kmsBhuFG.mjs.map +1 -0
- package/build/workspace.mjs +4 -4
- package/build/workspace.mjs.map +1 -1
- package/package.json +18 -18
- package/src/components/agent/Composer.tsx +2 -2
- 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 +1 -1
- package/src/components/agent/SessionPanel.tsx +2 -2
- 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/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/height.ts +2 -2
- package/src/components/terminal/items.tsx +2 -2
- 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/lib/context-note.ts +3 -3
- package/src/lib/format.ts +2 -2
- package/src/styles/scoped.entry.css +2 -2
- package/src/styles/terminal.css +79 -79
- package/src/styles/theme.css +39 -39
- package/build/SessionPanel-B5FfiBxE.mjs.map +0 -1
- package/build/status-BO9wJloK.mjs.map +0 -1
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. */
|
|
@@ -419,7 +419,7 @@
|
|
|
419
419
|
/* A file path, while the modifier that would open it is held.
|
|
420
420
|
*
|
|
421
421
|
* Only then, and that is the point: a path *is* clickable but only under
|
|
422
|
-
* Cmd/Ctrl, so it should only look clickable then
|
|
422
|
+
* Cmd/Ctrl, so it should only look clickable then - an always-underlined path
|
|
423
423
|
* promises a plain click that does nothing. Applied by `usePathLinks` using the
|
|
424
424
|
* same test the click itself makes, so nothing lights up that a click ignores.
|
|
425
425
|
* Outside `[data-terminal]` too, since a cards transcript names files as well. */
|
|
@@ -430,7 +430,7 @@
|
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
/* A status-line reading that leads somewhere. Inline and unstyled until hovered
|
|
433
|
-
|
|
433
|
+
- the line is meant to be quiet, and five underlined segments would make it
|
|
434
434
|
the loudest row on screen. */
|
|
435
435
|
[data-terminal] .term-reading {
|
|
436
436
|
font: inherit;
|
|
@@ -452,7 +452,7 @@
|
|
|
452
452
|
* row is a fill, and the only "control" is a row you can press a number on.
|
|
453
453
|
*/
|
|
454
454
|
|
|
455
|
-
/* One line tall with the stroke through its middle, full-bleed
|
|
455
|
+
/* One line tall with the stroke through its middle, full-bleed - the boundary
|
|
456
456
|
between the run and the decision. */
|
|
457
457
|
[data-terminal] .term-rule-row {
|
|
458
458
|
height: var(--term-line);
|
|
@@ -474,7 +474,7 @@
|
|
|
474
474
|
}
|
|
475
475
|
|
|
476
476
|
/* An option's expanded detail (a preview, a reason field) lines up with the
|
|
477
|
-
option's own text
|
|
477
|
+
option's own text - one gutter in, so it reads as belonging to that row. */
|
|
478
478
|
[data-terminal] .term-detail {
|
|
479
479
|
padding-left: 5ch;
|
|
480
480
|
}
|
|
@@ -511,7 +511,7 @@
|
|
|
511
511
|
color: var(--bg, #000);
|
|
512
512
|
}
|
|
513
513
|
|
|
514
|
-
/* The bare text field
|
|
514
|
+
/* The bare text field - a caret and a rule, no box. */
|
|
515
515
|
[data-terminal] .term-input {
|
|
516
516
|
width: 100%;
|
|
517
517
|
min-width: 0;
|
|
@@ -524,7 +524,7 @@
|
|
|
524
524
|
/* The terminal's own caret: a filled cell, not a hairline. It is the last
|
|
525
525
|
thing in the theme still drawn the way the web draws it, and it is the one
|
|
526
526
|
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
|
|
527
|
+
author and we then decline to animate it, so the block sits steady - a
|
|
528
528
|
blinking block is a strobe at this size, where a blinking hairline was
|
|
529
529
|
merely a caret. Both are ignored where unsupported, which is exactly the
|
|
530
530
|
right failure: a thin blinking caret is a plain caret, not a broken one. */
|
|
@@ -536,7 +536,7 @@
|
|
|
536
536
|
no padding and `line-height` is `--term-line`, so the content box is always a
|
|
537
537
|
whole number of lines and the rule under it lands on a cell boundary. The cap is
|
|
538
538
|
lines too, not a pixel height. Where `field-sizing` is unsupported this is a
|
|
539
|
-
one-line box that scrolls
|
|
539
|
+
one-line box that scrolls - the old single-line behaviour, minus the sideways
|
|
540
540
|
crawl, so the degradation costs nothing that was there before. */
|
|
541
541
|
[data-terminal] textarea.term-input {
|
|
542
542
|
display: block;
|
|
@@ -556,8 +556,8 @@
|
|
|
556
556
|
*
|
|
557
557
|
* The pinned prompt is the prompt's **first line**, and the pin is the
|
|
558
558
|
* 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
|
|
559
|
+
* spanning its turn; a one-line `sticky` **head** - the same row rendered
|
|
560
|
+
* again, clipped by height - leads the lane with its flow footprint cancelled
|
|
561
561
|
* (the negative bottom margin), so in flow it lies exactly over the real
|
|
562
562
|
* row's first line and is invisible. The compositor pins it (no per-scroll
|
|
563
563
|
* JS, no frame lag) and the lane's bottom edge pushes it off when the next
|
|
@@ -567,18 +567,18 @@
|
|
|
567
567
|
* The head's visible line docks flush to the top edge of the scroller. Its
|
|
568
568
|
* inter-row gap is padding on the head (matching the row it overlays), so a
|
|
569
569
|
* 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
|
|
570
|
+
* padding parks above the viewport edge and the line starts at zero - which
|
|
571
571
|
* is also what makes the pin engage exactly when the line itself reaches the
|
|
572
572
|
* top, not a line early.
|
|
573
573
|
*
|
|
574
574
|
* 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
|
|
575
|
+
* transparent to the pointer - the rows it covers own interaction, and the
|
|
576
|
+
* head is `aria-hidden` in the markup. The z-index is on the lane -
|
|
577
577
|
* every row wrapper is its own stacking context (they are transformed), so
|
|
578
578
|
* the head's own z-index could never beat a later sibling.
|
|
579
579
|
*
|
|
580
580
|
* `--bg` is the panel's own ground, the same token the hover-revealed row
|
|
581
|
-
* actions cover themselves with
|
|
581
|
+
* actions cover themselves with - the user band is translucent by design, so
|
|
582
582
|
* without a ground the rows passing beneath would show straight through the
|
|
583
583
|
* pinned head. Opaque always, which is invisible in flow: the ground behind
|
|
584
584
|
* it is the same `--bg`, and the row it covers is its own content.
|
|
@@ -591,14 +591,14 @@
|
|
|
591
591
|
/* The prompt itself is *inside* the lane, not beneath it, so the lane's `none`
|
|
592
592
|
reached it too and made a user message the one block in the transcript the
|
|
593
593
|
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
|
|
594
|
+
selected nothing. Only the real row is given back - the head overlay above
|
|
595
595
|
stays transparent, which is the whole point of the lane. */
|
|
596
596
|
[data-terminal] [data-sticky-lane] > [data-index] {
|
|
597
597
|
pointer-events: auto;
|
|
598
598
|
}
|
|
599
599
|
|
|
600
600
|
/* The head's own strip: the full lane, out of flow, so the head needs no
|
|
601
|
-
footprint-cancelling margin
|
|
601
|
+
footprint-cancelling margin - sticky confinement clamps the margin box, and
|
|
602
602
|
a negative margin shrinks it to nothing, letting the head overshoot the
|
|
603
603
|
lane's end by its own height (two pinned prompts on screen at once). */
|
|
604
604
|
[data-terminal] [data-sticky-lane] > [data-sticky-headlane] {
|
|
@@ -609,7 +609,7 @@
|
|
|
609
609
|
|
|
610
610
|
/* Hidden until actually stuck (`data-stuck`, set by the lane's sentinel
|
|
611
611
|
observer): an overlay visible in flow sits on the real row's first line and
|
|
612
|
-
swallows its selection highlight
|
|
612
|
+
swallows its selection highlight - the line reads as unselectable. */
|
|
613
613
|
[data-terminal] [data-sticky-headlane] > [data-sticky-head] {
|
|
614
614
|
position: sticky;
|
|
615
615
|
top: 0;
|
|
@@ -632,19 +632,19 @@
|
|
|
632
632
|
/* ── Composer ────────────────────────────────────────────────────────────────
|
|
633
633
|
*
|
|
634
634
|
* 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
|
|
635
|
+
* the caret on the body column. That is the whole design - a typed line and a
|
|
636
636
|
* rendered one start on the same character, which no chat composer does and
|
|
637
637
|
* every terminal does for free.
|
|
638
638
|
*
|
|
639
639
|
* Docked rather than floating: no radius, no shadow, and a rule along the top
|
|
640
640
|
* *and* the bottom, both turning accent while anything inside has focus. Those
|
|
641
641
|
* two rules are the CLI's own frame for its prompt, and they are the only
|
|
642
|
-
* affordance
|
|
642
|
+
* affordance - which is what the transcript above (no boxes anywhere) asks for.
|
|
643
643
|
* They also do the work a box would: the field reads as its own strip of the
|
|
644
644
|
* panel rather than as the last row of the transcript, without a border on the
|
|
645
645
|
* sides that would take the `❯` off the column the rows start on.
|
|
646
646
|
*
|
|
647
|
-
* The bottom rule is why the status bar under it draws none of its own
|
|
647
|
+
* The bottom rule is why the status bar under it draws none of its own - see
|
|
648
648
|
* `StatusBar.tsx`. Two adjacent 1px rules is a 2px rule with a seam in it.
|
|
649
649
|
*/
|
|
650
650
|
[data-terminal].term-composer {
|
|
@@ -654,7 +654,7 @@
|
|
|
654
654
|
}
|
|
655
655
|
/* The bar spans the panel; its contents sit in the transcript's own column, so
|
|
656
656
|
the `>` lands on the column the rows above it start on. Two elements because
|
|
657
|
-
those are two different widths
|
|
657
|
+
those are two different widths - a rule that stopped at the content column
|
|
658
658
|
would read as a card, which is the one thing this theme has none of. */
|
|
659
659
|
[data-terminal] .term-composer-body {
|
|
660
660
|
margin-inline: auto;
|
|
@@ -663,7 +663,7 @@
|
|
|
663
663
|
padding-inline: var(--term-bleed, 0px);
|
|
664
664
|
/* Air on each side of the prompt, and a plain pixel value rather than a
|
|
665
665
|
fraction of `--term-line`: this is chrome between two rules, not a
|
|
666
|
-
transcript row, and the whole-multiple rule governs rows
|
|
666
|
+
transcript row, and the whole-multiple rule governs rows - a computed
|
|
667
667
|
half-line would be exactly the fractional metric that rule exists to
|
|
668
668
|
forbid. */
|
|
669
669
|
padding-block: 8px;
|
|
@@ -674,7 +674,7 @@
|
|
|
674
674
|
}
|
|
675
675
|
|
|
676
676
|
/* 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
|
|
677
|
+
the mode changes is where the text goes - a subtle cue would be the wrong kind of quiet. Listed
|
|
678
678
|
after the focus rule so it wins while the field is focused, which is the whole time it is on. */
|
|
679
679
|
[data-terminal].term-composer-shell,
|
|
680
680
|
[data-terminal].term-composer-shell:focus-within {
|
|
@@ -684,7 +684,7 @@
|
|
|
684
684
|
color: var(--term-dim);
|
|
685
685
|
}
|
|
686
686
|
|
|
687
|
-
/* The contentEditable field. `font: inherit` is what makes it the cell
|
|
687
|
+
/* The contentEditable field. `font: inherit` is what makes it the cell - the
|
|
688
688
|
surface above already carries the size and the line height, and anything that
|
|
689
689
|
restates them here can disagree with the rows. */
|
|
690
690
|
[data-terminal] .term-composer-field {
|
|
@@ -702,7 +702,7 @@
|
|
|
702
702
|
* badge outside its top-right corner, over a strip with its own bottom rule.
|
|
703
703
|
* None of that survives here: this theme has no rounded corners and no badges
|
|
704
704
|
* floating off an edge, and the strip sits *inside* the composer's frame, which
|
|
705
|
-
* already draws the only rule there is
|
|
705
|
+
* already draws the only rule there is - a second one directly above the field
|
|
706
706
|
* reads as the box this theme exists not to have.
|
|
707
707
|
*
|
|
708
708
|
* So the thumbnail is a plain 1px cell, and the remove control is tucked into
|
|
@@ -711,7 +711,7 @@
|
|
|
711
711
|
* puts it just inside the thumbnail's border instead of straddling it.
|
|
712
712
|
*
|
|
713
713
|
* The strip's only spacing is the 8px the composer already uses above and below
|
|
714
|
-
* its prompt
|
|
714
|
+
* its prompt - a plain pixel value, and deliberately, for the reason given
|
|
715
715
|
* there: this is chrome between rules, not a transcript row, so the
|
|
716
716
|
* whole-multiple-of-`--term-line` rule does not govern it. No side padding: the
|
|
717
717
|
* strip is already inside the body column, so padding here would step it in
|
|
@@ -724,7 +724,7 @@
|
|
|
724
724
|
}
|
|
725
725
|
/* A refused upload, in the terminal's own red. This needs to be said here
|
|
726
726
|
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
|
|
727
|
+
the `border` shorthand, colour included, and outranks a utility class - so
|
|
728
728
|
without this the one state that must stand out is the one that looks
|
|
729
729
|
ordinary. */
|
|
730
730
|
[data-terminal] .term-attachment[data-failed] {
|
|
@@ -739,7 +739,7 @@
|
|
|
739
739
|
|
|
740
740
|
/* A composer action as a character: one cell wide, one line tall, so pressing
|
|
741
741
|
it costs no width and removing it would move nothing. Dim until you reach for
|
|
742
|
-
it
|
|
742
|
+
it - the same restraint the row actions show. */
|
|
743
743
|
[data-terminal] .term-glyph {
|
|
744
744
|
flex: none;
|
|
745
745
|
width: var(--term-cell);
|
|
@@ -755,8 +755,8 @@
|
|
|
755
755
|
cursor: pointer;
|
|
756
756
|
}
|
|
757
757
|
/* 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
|
|
758
|
+
The cell is 2ch and a character is 1ch, so `text-align: center` - right for a
|
|
759
|
+
standalone control at the row's trailing edge - lands the glyph half a cell
|
|
760
760
|
(≈3.9px at 13px/JetBrains Mono) to the right of the column every `❯` and `●`
|
|
761
761
|
in the transcript starts on. `start` is what `.term-gutter` uses, and on this
|
|
762
762
|
grid "nearly the same column" is just the wrong column. */
|
|
@@ -784,7 +784,7 @@
|
|
|
784
784
|
* An added or removed line is a **band**, not coloured text: the wash runs the
|
|
785
785
|
* full width of the screen, the way it does in a terminal, so the shape of a
|
|
786
786
|
* change is readable from the scrollbar without reading a word of it. The line
|
|
787
|
-
* numbers ride inside the wash
|
|
787
|
+
* numbers ride inside the wash - they are part of the changed line.
|
|
788
788
|
*/
|
|
789
789
|
/* A diff belongs to the call above it, so it steps in one cell like any other
|
|
790
790
|
tool output. */
|
|
@@ -813,7 +813,7 @@
|
|
|
813
813
|
[data-terminal] .term-diff .term-row[data-diff='context'] {
|
|
814
814
|
color: var(--term-diff-fg);
|
|
815
815
|
}
|
|
816
|
-
/* The number column is dimmer than the line it belongs to in every case
|
|
816
|
+
/* The number column is dimmer than the line it belongs to in every case - it is
|
|
817
817
|
an address, not content. `.term-gutter`'s own colour would flatten all three
|
|
818
818
|
to one grey, so each state dims its own. */
|
|
819
819
|
[data-terminal] .term-diff .term-row > .term-gutter {
|
|
@@ -829,13 +829,13 @@
|
|
|
829
829
|
/* ── Markdown ────────────────────────────────────────────────────────────────
|
|
830
830
|
*
|
|
831
831
|
* The element map is in `components/terminal/markdown.tsx`; this is the little
|
|
832
|
-
* CSS it needs. Nothing here is an override
|
|
832
|
+
* CSS it needs. Nothing here is an override - the renderer is told what to emit,
|
|
833
833
|
* so there is no default to undo and no `!important` anywhere in the theme.
|
|
834
834
|
*/
|
|
835
835
|
|
|
836
836
|
/* react-markdown emits the source's newlines as text nodes between blocks. The
|
|
837
837
|
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
|
|
838
|
+
spacing), which would render every one of those as a visible blank line - so
|
|
839
839
|
markdown bodies opt back out, and only a fenced block opts back in. */
|
|
840
840
|
.term-md {
|
|
841
841
|
white-space: normal;
|
|
@@ -844,7 +844,7 @@
|
|
|
844
844
|
/* And it has to reach `.term-body` explicitly, because a list item *is* a Row
|
|
845
845
|
and Rows carry `pre-wrap` (a plain line of tool output must keep its own
|
|
846
846
|
spacing). Inherited, that turns every newline react-markdown emits between an
|
|
847
|
-
item's block children into a visible empty line
|
|
847
|
+
item's block children into a visible empty line - a nested list drew k+2
|
|
848
848
|
phantom blanks, so a two-line nested item was 72px of 18px rows. Models nest
|
|
849
849
|
lists constantly, and the grid audit cannot see it: the phantoms are whole
|
|
850
850
|
lines, so every row still lands on the grid. Fenced code opts back in for
|
|
@@ -856,7 +856,7 @@
|
|
|
856
856
|
|
|
857
857
|
/* The one vertical rule: consecutive blocks are one blank line apart. A margin
|
|
858
858
|
rather than an inserted row, because this row list belongs to the markdown
|
|
859
|
-
renderer
|
|
859
|
+
renderer - but it is exactly one line, so the grid holds. */
|
|
860
860
|
.term-md .term-block + .term-block {
|
|
861
861
|
margin-top: var(--term-line);
|
|
862
862
|
}
|
|
@@ -889,7 +889,7 @@
|
|
|
889
889
|
background: linear-gradient(var(--term-faint), var(--term-faint)) left / 1px 100% no-repeat;
|
|
890
890
|
}
|
|
891
891
|
|
|
892
|
-
/* A horizontal rule is one line tall with the rule through its middle
|
|
892
|
+
/* A horizontal rule is one line tall with the rule through its middle - the row
|
|
893
893
|
it occupies is a whole line either way. */
|
|
894
894
|
.term-md .term-rule {
|
|
895
895
|
height: var(--term-line);
|
|
@@ -921,7 +921,7 @@
|
|
|
921
921
|
border-collapse: collapse;
|
|
922
922
|
/* `max-content` and **no** `max-width`. A cap of `100%` looks like the
|
|
923
923
|
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
|
|
924
|
+
natural width, so the cells wrap and a 14-row table becomes 19 lines tall -
|
|
925
925
|
which breaks the theme's premise (a row is a line) and with it the height
|
|
926
926
|
calculator, whose table model counts rows. It also silently disables the
|
|
927
927
|
wrapper's own `overflow-x`, since a table that shrinks never overflows.
|
|
@@ -943,15 +943,15 @@
|
|
|
943
943
|
/* ── Scrubber ────────────────────────────────────────────────────────────────
|
|
944
944
|
*
|
|
945
945
|
* The overview ruler (`components/terminal/scrubber.tsx`): a 12px rail of marks
|
|
946
|
-
* over the scroller's right edge
|
|
946
|
+
* over the scroller's right edge - VS Code's strip beside the minimap, with
|
|
947
947
|
* this transcript's semantics. It is *chrome*, not rows: absolutely positioned
|
|
948
948
|
* over the scroll container so the transcript's grid is untouched, and drawn
|
|
949
949
|
* entirely from --term-* tokens so it follows the theme wherever the panel is
|
|
950
950
|
* embedded. Its width is the one deliberate exception to the theme's `ch` rule
|
|
951
|
-
*
|
|
951
|
+
* - see the note on `.term-scrubber` below.
|
|
952
952
|
*
|
|
953
953
|
* Interactivity is an affordance. Without `data-interactive` the rail is
|
|
954
|
-
* passive paint with pointer events off
|
|
954
|
+
* passive paint with pointer events off - and the native scrollbar, which
|
|
955
955
|
* stays in that mode, keeps working straight through it.
|
|
956
956
|
*/
|
|
957
957
|
[data-terminal].term-scrubber {
|
|
@@ -977,7 +977,7 @@
|
|
|
977
977
|
touch-action: none;
|
|
978
978
|
}
|
|
979
979
|
|
|
980
|
-
/* The viewport band
|
|
980
|
+
/* The viewport band - where you are: a faint wash with a hairline frame,
|
|
981
981
|
floated over the marks (z-index) so the framed region reads as a window
|
|
982
982
|
onto the rail rather than a stripe behind it. `--term-faint` at 25% rather
|
|
983
983
|
than a literal, so the light palette re-tints it. */
|
|
@@ -992,7 +992,7 @@
|
|
|
992
992
|
}
|
|
993
993
|
|
|
994
994
|
/* There is no separate cursor line. The band above answers "where am I" on its
|
|
995
|
-
own
|
|
995
|
+
own - it is outlined, so its top edge already reads as an edge - and a 2px
|
|
996
996
|
blue rule laid over it was a second indicator of the same fact, in the
|
|
997
997
|
loudest colour the rail has. */
|
|
998
998
|
|
|
@@ -1007,7 +1007,7 @@
|
|
|
1007
1007
|
outline: 1px solid color-mix(in srgb, var(--term-bright) 40%, transparent);
|
|
1008
1008
|
}
|
|
1009
1009
|
|
|
1010
|
-
/* Two lanes of 8px
|
|
1010
|
+
/* Two lanes of 8px - what you asked, and what came back - and 'f', the
|
|
1011
1011
|
full-width lane for annotations *about* the run (an error, an approval, a
|
|
1012
1012
|
bookmark, the catch-up seam) rather than steps through it. Pixels, not
|
|
1013
1013
|
percentages: the whole point of the 16px rail is that a lane is a reliable
|
|
@@ -1026,7 +1026,7 @@
|
|
|
1026
1026
|
width: 12px;
|
|
1027
1027
|
}
|
|
1028
1028
|
|
|
1029
|
-
/* Lane marks are two-tone: the first 2px
|
|
1029
|
+
/* Lane marks are two-tone: the first 2px - the mark's own anchor - at full
|
|
1030
1030
|
strength, the rest of the row's extent a 25% tail, so a long answer reads
|
|
1031
1031
|
as long without a tall solid bar shouting over the rail. A minimum-height
|
|
1032
1032
|
mark shows only the solid segment, so short and long marks need no separate
|
|
@@ -1038,7 +1038,7 @@
|
|
|
1038
1038
|
}
|
|
1039
1039
|
/* A sub-agent's stretch of the transcript, in the input lane: green, because
|
|
1040
1040
|
every other colour on this rail is already spoken for and none of them means
|
|
1041
|
-
"somebody else's working"
|
|
1041
|
+
"somebody else's working" - blue is you, white is the answer, red is an
|
|
1042
1042
|
alarm, magenta is your bookmark, yellow is the session waiting on you. Drawn
|
|
1043
1043
|
as an extent (2px head, 25% tail) like the two marks it shares its geometry
|
|
1044
1044
|
with: collapsed it is a tick, expanded it is the band the sub-agent covers. */
|
|
@@ -1054,13 +1054,13 @@
|
|
|
1054
1054
|
.term-scrubber .term-scrub-mark[data-kind='error'] {
|
|
1055
1055
|
background: var(--term-red);
|
|
1056
1056
|
}
|
|
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
|
|
1057
|
+
/* A failed tool call is an alarm, so it is solid like the rest of them - in the
|
|
1058
|
+
response lane, since it is something the run produced - but at 55%, which is the one thing keeping the rail readable. A session
|
|
1059
1059
|
error is rare and a turn failure rarer; a tool that failed and was recovered
|
|
1060
1060
|
from is routine (a grep that matched nothing, a build fixed on the second go),
|
|
1061
1061
|
and at full strength a normal working session paints the rail solid red and
|
|
1062
1062
|
the two errors that actually ended something stop standing out. Dimmer reads
|
|
1063
|
-
as the same class of thing, one rank quieter
|
|
1063
|
+
as the same class of thing, one rank quieter - which is exactly its rank in
|
|
1064
1064
|
LOUDNESS. */
|
|
1065
1065
|
.term-scrubber .term-scrub-mark[data-kind='toolFailed'] {
|
|
1066
1066
|
background: color-mix(in srgb, var(--term-red) 55%, transparent);
|
|
@@ -1087,8 +1087,8 @@
|
|
|
1087
1087
|
}
|
|
1088
1088
|
}
|
|
1089
1089
|
|
|
1090
|
-
/* The peek. Rendered from transcript state
|
|
1091
|
-
unmounted
|
|
1090
|
+
/* The peek. Rendered from transcript state - the row it describes is usually
|
|
1091
|
+
unmounted - and framed the theme's way: strokes as backgrounds, no border.
|
|
1092
1092
|
pointer-events: none, so it can never trap the pointer that revealed it. */
|
|
1093
1093
|
.term-scrubber .term-scrub-peek {
|
|
1094
1094
|
position: absolute;
|
|
@@ -1124,7 +1124,7 @@
|
|
|
1124
1124
|
|
|
1125
1125
|
/* While an interactive scrubber is mounted it IS the scrollbar; the transcript
|
|
1126
1126
|
stamps this attribute on the scroll element. A passive rail leaves the
|
|
1127
|
-
native scrollbar alone
|
|
1127
|
+
native scrollbar alone - pointer-events: none above is what keeps that
|
|
1128
1128
|
scrollbar reachable through the paint. */
|
|
1129
1129
|
[data-term-scrubber-host] {
|
|
1130
1130
|
scrollbar-width: none;
|
|
@@ -1138,7 +1138,7 @@
|
|
|
1138
1138
|
* A picture a tool returned, drawn in a box of whole lines (`IMAGE_BOX_LINES`,
|
|
1139
1139
|
* set inline by `TerminalImage` so the constant has one spelling across the
|
|
1140
1140
|
* 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
|
|
1141
|
+
* here may add to it: no margin, no border, no padding - a box that is not
|
|
1142
1142
|
* exactly N lines is a row the virtualizer was told the wrong size for.
|
|
1143
1143
|
*
|
|
1144
1144
|
* `object-fit: contain` with a top-left origin rather than a centred one: the
|