@cplieger/web-terminal-ui 3.5.0 → 4.1.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 +60 -27
- package/css/00-tokens.css +18 -2
- package/css/01-scope.css +76 -0
- package/css/{02-app.css → 02-terminal.css} +113 -239
- package/css/10-primitives.css +98 -0
- package/css/20-connection-banner.css +42 -0
- package/css/21-scroll-to-bottom.css +65 -0
- package/css/22-context-menu.css +45 -0
- package/css/23-toolbar.css +181 -0
- package/css/{05-tabs.css → 30-tabs.css} +127 -66
- package/css/{06-mobile.css → 31-switcher.css} +85 -80
- package/css/{07-animations.css → 40-animations.css} +15 -15
- package/css/MANIFEST +19 -11
- package/css/MANIFEST.single +12 -0
- package/css/MANIFEST.tabbed +16 -0
- package/css/MANIFEST.touch +12 -0
- package/css/{01-reset.css → page.css} +18 -19
- package/package.json +19 -6
- package/scaffold/index.html +5 -0
- package/src/features/activity-monitor.ts +32 -2
- package/src/features/connection-banner.ts +1 -0
- package/src/features/context-menu.ts +6 -33
- package/src/features/menu-position.ts +50 -0
- package/src/features/mobile-toolbar.ts +5 -4
- package/src/features/{tabs.ts → tabs/index.ts} +310 -721
- package/src/features/tabs/model.ts +342 -0
- package/src/features/tabs/strip.ts +101 -0
- package/src/features/tabs/switcher.ts +129 -0
- package/src/index.ts +1 -0
- package/src/kernel/a11y.ts +30 -5
- package/src/kernel/conn-state.ts +16 -6
- package/src/kernel/kernel.ts +170 -59
- package/src/kernel/types.ts +68 -15
- package/src/presets/agent-tabbed.ts +15 -0
- package/src/presets/single.ts +27 -0
- package/src/presets/tabbed.ts +42 -0
- package/src/presets/touch.ts +13 -0
- package/src/presets.ts +12 -76
- package/src/viewport.ts +28 -15
- package/css/03-primitives.css +0 -83
- package/css/04-feedback.css +0 -180
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
/* ===== Tabs: desktop strip + activity vocabulary (tabs feature) =====
|
|
2
2
|
The desktop top-bar tab strip, its per-tab chrome (label, close, add, the
|
|
3
|
-
right-click menu), the surface offset that clears the
|
|
3
|
+
right-click menu), the surface offset that clears the docked strip, and the
|
|
4
4
|
shared activity-dot status vocabulary. The mobile tab chrome (switcher bar,
|
|
5
|
-
overview sheet, external key grid) lives in
|
|
6
|
-
|
|
5
|
+
overview sheet, external key grid) lives in 31-switcher.css; all motion lives
|
|
6
|
+
in 40-animations.css. */
|
|
7
7
|
|
|
8
|
-
.wt-region-top-bar {
|
|
8
|
+
:where(.wt-root) .wt-region-top-bar {
|
|
9
9
|
/* On desktop the tab strip docks to the top; the region is transparent, so
|
|
10
10
|
the bar element below owns its own fixed placement. */
|
|
11
11
|
display: contents;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
.wt-tab-bar {
|
|
15
|
-
|
|
14
|
+
:where(.wt-root) .wt-tab-bar {
|
|
15
|
+
/* Absolute against .wt-root, like all chrome (identical geometry in
|
|
16
|
+
wt-viewport mode; see 01-scope.css). The bar itself never scrolls: the
|
|
17
|
+
tabs + "+" scroll inside .wt-tab-scroll (below), so the keyboard button —
|
|
18
|
+
the bar's last flex item, outside the scroller — stays pinned at the right
|
|
19
|
+
edge however many tabs are open. */
|
|
20
|
+
position: absolute;
|
|
16
21
|
bottom: var(--safe-bottom);
|
|
17
22
|
left: 0;
|
|
18
23
|
right: 0;
|
|
@@ -24,17 +29,34 @@
|
|
|
24
29
|
background: var(--surface);
|
|
25
30
|
backdrop-filter: var(--blur);
|
|
26
31
|
border-top: 1px solid var(--border);
|
|
27
|
-
overflow-x: auto;
|
|
28
32
|
z-index: 15;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
/* The scrolling tab list inside the bar: the tabs plus the "+", which floats
|
|
36
|
+
right of the last tab and scrolls with the list. Horizontal scrolling lives
|
|
37
|
+
HERE, not on the bar, so the strip-end keyboard button never scrolls away.
|
|
38
|
+
flex: 1 + min-width: 0 make it shrink to leave room for the keyboard button
|
|
39
|
+
when that is shown (a wide touchscreen) and stretch to the full bar width
|
|
40
|
+
when it is hidden (a fine-pointer desktop), so the tabs reclaim the space. */
|
|
41
|
+
:where(.wt-root) .wt-tab-scroll {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
gap: var(--sp-1);
|
|
45
|
+
flex: 1 1 auto;
|
|
46
|
+
min-width: 0;
|
|
47
|
+
overflow-x: auto;
|
|
48
|
+
/* A wheel/trackpad/touch scroll that runs out of tabs must not chain into
|
|
49
|
+
an embedding page (wt-container mode). */
|
|
50
|
+
overscroll-behavior-x: contain;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
:where(.wt-root) .wt-tab {
|
|
32
54
|
display: inline-flex;
|
|
33
55
|
align-items: center;
|
|
34
56
|
gap: var(--sp-1);
|
|
35
57
|
/* Browser-style tabs: up to 300px per tab when there is room, shrinking toward
|
|
36
|
-
a 100px floor as the strip fills; past the floor the
|
|
37
|
-
(.wt-tab-
|
|
58
|
+
a 100px floor as the strip fills; past the floor the list scrolls horizontally
|
|
59
|
+
(.wt-tab-scroll has overflow-x: auto). flex-grow is 0, so the 300px basis is a
|
|
38
60
|
hard cap — every tab is the same size and never stretches past it, whatever
|
|
39
61
|
its title; the label truncates instead. box-sizing keeps the basis a true
|
|
40
62
|
total. */
|
|
@@ -56,12 +78,12 @@
|
|
|
56
78
|
|
|
57
79
|
/* Inactive tabs read as outlined chips with a subtle dark fill (so adjacent tabs
|
|
58
80
|
stay separated from the bar and from each other) and lift on hover. */
|
|
59
|
-
.wt-tab:not(.wt-tab-active) {
|
|
81
|
+
:where(.wt-root) .wt-tab:not(.wt-tab-active) {
|
|
60
82
|
border-color: var(--border);
|
|
61
83
|
background: var(--tab-bg);
|
|
62
84
|
}
|
|
63
85
|
|
|
64
|
-
.wt-tab:not(.wt-tab-active):hover {
|
|
86
|
+
:where(.wt-root) .wt-tab:not(.wt-tab-active):hover {
|
|
65
87
|
/* A solid lift over the chip. --tab-hover-bg is a translucent wash meant for a
|
|
66
88
|
transparent-rest control; replacing the --tab-bg chip with it would read as
|
|
67
89
|
"more transparent", not "lighter", so bake the same 15% --text lift into the
|
|
@@ -73,7 +95,7 @@
|
|
|
73
95
|
/* The active tab reads clearly above the inactive chips: a stronger fill plus an
|
|
74
96
|
accent-coloured edge (inactive chips carry a faint white --border; the active
|
|
75
97
|
one gets the accent equivalent). Themeable via --accent (web-terminal-kiro -> purple). */
|
|
76
|
-
.wt-tab-active {
|
|
98
|
+
:where(.wt-root) .wt-tab-active {
|
|
77
99
|
background: var(--tab-active-bg);
|
|
78
100
|
border-color: var(--tab-active-border);
|
|
79
101
|
color: var(--tab-active-fg);
|
|
@@ -81,7 +103,7 @@
|
|
|
81
103
|
|
|
82
104
|
/* The label flexes to fill the fixed-width tab and truncates with an ellipsis
|
|
83
105
|
(min-width: 0 lets a flex item shrink below its content width). */
|
|
84
|
-
.wt-tab-label {
|
|
106
|
+
:where(.wt-root) .wt-tab-label {
|
|
85
107
|
flex: 1 1 auto;
|
|
86
108
|
min-width: 0;
|
|
87
109
|
overflow: hidden;
|
|
@@ -90,14 +112,14 @@
|
|
|
90
112
|
}
|
|
91
113
|
|
|
92
114
|
/* A little breathing room after the activity dot, before the label. */
|
|
93
|
-
.wt-tab-dot {
|
|
115
|
+
:where(.wt-root) .wt-tab-dot {
|
|
94
116
|
margin-right: 2px;
|
|
95
117
|
}
|
|
96
118
|
|
|
97
119
|
/* The tab close (x): a centered, muted square icon button with a box on hover.
|
|
98
120
|
Its glyph is an inline SVG (styled below), so flex-centering the button
|
|
99
121
|
centers it exactly. */
|
|
100
|
-
.wt-tab-close {
|
|
122
|
+
:where(.wt-root) .wt-tab-close {
|
|
101
123
|
display: inline-flex;
|
|
102
124
|
align-items: center;
|
|
103
125
|
justify-content: center;
|
|
@@ -116,27 +138,28 @@
|
|
|
116
138
|
cursor: pointer;
|
|
117
139
|
}
|
|
118
140
|
|
|
119
|
-
.wt-tab-close:hover {
|
|
141
|
+
:where(.wt-root) .wt-tab-close:hover {
|
|
120
142
|
background: var(--text-active);
|
|
121
143
|
color: var(--text);
|
|
122
144
|
}
|
|
123
145
|
|
|
124
146
|
/* On an inactive tab the close appears only on hover, kept in the layout via
|
|
125
147
|
visibility so the tab width never jumps; on the active tab it is always shown. */
|
|
126
|
-
.wt-tab:not(.wt-tab-active) .wt-tab-close {
|
|
148
|
+
:where(.wt-root) .wt-tab:not(.wt-tab-active) .wt-tab-close {
|
|
127
149
|
visibility: hidden;
|
|
128
150
|
}
|
|
129
151
|
|
|
130
|
-
.wt-tab:not(.wt-tab-active):hover .wt-tab-close {
|
|
152
|
+
:where(.wt-root) .wt-tab:not(.wt-tab-active):hover .wt-tab-close {
|
|
131
153
|
visibility: visible;
|
|
132
154
|
}
|
|
133
155
|
|
|
134
156
|
/* Coarse pointer (an iPad using the strip): no hover fires, so an inactive tab's close
|
|
135
157
|
would be unreachable without first activating the tab. Reveal every tab's close there,
|
|
136
|
-
matching the always-visible mobile switcher closes.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
158
|
+
matching the always-visible mobile switcher closes. No narrow gate needed: on the
|
|
159
|
+
narrow-coarse phone the whole strip is display:none (31-switcher.css), so this rule
|
|
160
|
+
is moot there. */
|
|
161
|
+
@media (pointer: coarse) {
|
|
162
|
+
:where(.wt-root) .wt-tab:not(.wt-tab-active) .wt-tab-close {
|
|
140
163
|
visibility: visible;
|
|
141
164
|
}
|
|
142
165
|
}
|
|
@@ -144,7 +167,7 @@
|
|
|
144
167
|
/* The add-tab (+) button: its hover box matches an active tab's fill and
|
|
145
168
|
stretches to the strip height; the 2px strip gap (not a custom margin) spaces
|
|
146
169
|
it from the last tab. Its glyph is an inline SVG. */
|
|
147
|
-
.wt-tab-new {
|
|
170
|
+
:where(.wt-root) .wt-tab-new {
|
|
148
171
|
display: inline-flex;
|
|
149
172
|
align-items: center;
|
|
150
173
|
justify-content: center;
|
|
@@ -166,7 +189,7 @@
|
|
|
166
189
|
cursor: pointer;
|
|
167
190
|
}
|
|
168
191
|
|
|
169
|
-
.wt-tab-new:hover {
|
|
192
|
+
:where(.wt-root) .wt-tab-new:hover {
|
|
170
193
|
/* Keep the accent glyph on hover (no colour override) so the "+" stays the
|
|
171
194
|
accent colour like the mobile "+"; hover only adds the fill. */
|
|
172
195
|
background: var(--tab-active-bg);
|
|
@@ -175,8 +198,8 @@
|
|
|
175
198
|
/* The +/x icons are inline SVG so they center exactly in their flex-centered
|
|
176
199
|
button and stay symmetric regardless of the UI font (fixes the off-center
|
|
177
200
|
glyph and the lopsided "+"). The + is a touch larger and bolder than the x. */
|
|
178
|
-
.wt-tab-close svg,
|
|
179
|
-
.wt-tab-new svg {
|
|
201
|
+
:where(.wt-root) .wt-tab-close svg,
|
|
202
|
+
:where(.wt-root) .wt-tab-new svg {
|
|
180
203
|
stroke: currentcolor;
|
|
181
204
|
fill: none;
|
|
182
205
|
stroke-width: 2.5;
|
|
@@ -184,12 +207,12 @@
|
|
|
184
207
|
stroke-linejoin: round;
|
|
185
208
|
}
|
|
186
209
|
|
|
187
|
-
.wt-tab-close svg {
|
|
210
|
+
:where(.wt-root) .wt-tab-close svg {
|
|
188
211
|
width: 13px;
|
|
189
212
|
height: 13px;
|
|
190
213
|
}
|
|
191
214
|
|
|
192
|
-
.wt-tab-new svg {
|
|
215
|
+
:where(.wt-root) .wt-tab-new svg {
|
|
193
216
|
width: 16px;
|
|
194
217
|
height: 16px;
|
|
195
218
|
}
|
|
@@ -199,29 +222,31 @@
|
|
|
199
222
|
landscape), where there is no mobile switcher to carry one. Built + wired by
|
|
200
223
|
the SAME factory as the mobile switcher's keyboard button (see makeKbButton
|
|
201
224
|
in tabs.ts). Uses the shared .wt-btn primitive for its 44px box + centered
|
|
202
|
-
icon, borderless like the switcher's
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
.wt-
|
|
225
|
+
icon, borderless like the switcher's. The bar's LAST flex item, OUTSIDE the
|
|
226
|
+
tab scroller: pinned at the bar's right edge in the same column as the
|
|
227
|
+
scroll-to-bottom button floating above it (both 44px boxes whose right edge
|
|
228
|
+
sits --sp-2 from the root's right edge), where an overflowing tab list can
|
|
229
|
+
never push or scroll it away; the "+" stays with the tabs inside the
|
|
230
|
+
scroller. Hidden on a fine-pointer desktop (a real keyboard is present; the
|
|
231
|
+
scroller then stretches to the full bar width) and until a keyboardToggle
|
|
232
|
+
feature is wired ([hidden]); shown only on a coarse pointer at strip widths
|
|
233
|
+
(the .wt-narrow complement below; the breakpoint itself is the kernel's,
|
|
234
|
+
mirrored as the root class). */
|
|
235
|
+
:where(.wt-root) .wt-tab-kb {
|
|
211
236
|
display: none;
|
|
212
237
|
flex: none;
|
|
213
238
|
}
|
|
214
239
|
|
|
215
240
|
/* Grid open: filled so the toggled state reads at a glance (mirrors the
|
|
216
241
|
switcher keyboard button). */
|
|
217
|
-
.wt-tab-kb.wt-active {
|
|
242
|
+
:where(.wt-root) .wt-tab-kb.wt-active {
|
|
218
243
|
background: var(--tab-hover-bg);
|
|
219
244
|
color: var(--tab-active-fg);
|
|
220
245
|
}
|
|
221
246
|
|
|
222
247
|
/* Sticky-Ctrl armed: invert (matches the switcher keyboard button + the armed
|
|
223
248
|
Ctrl key) so a pending Ctrl is visible with the grid closed. */
|
|
224
|
-
.wt-tab-kb.wt-armed {
|
|
249
|
+
:where(.wt-root) .wt-tab-kb.wt-armed {
|
|
225
250
|
background: var(--text);
|
|
226
251
|
color: var(--bg);
|
|
227
252
|
}
|
|
@@ -229,14 +254,14 @@
|
|
|
229
254
|
/* Show only on a WIDE touchscreen (where the desktop strip is used AND a
|
|
230
255
|
keyboardToggle is wired, hence :not([hidden])). In-flow now, so the strip
|
|
231
256
|
needs no reserved right padding for it. */
|
|
232
|
-
@media (pointer: coarse)
|
|
233
|
-
.wt-tab-kb:not([hidden]) {
|
|
257
|
+
@media (pointer: coarse) {
|
|
258
|
+
:where(.wt-root:not(.wt-narrow)) .wt-tab-kb:not([hidden]) {
|
|
234
259
|
display: inline-flex;
|
|
235
260
|
}
|
|
236
261
|
}
|
|
237
262
|
|
|
238
263
|
/* The tab being dragged (reorder) dims while it moves. */
|
|
239
|
-
.wt-tab-dragging {
|
|
264
|
+
:where(.wt-root) .wt-tab-dragging {
|
|
240
265
|
opacity: 0.5;
|
|
241
266
|
}
|
|
242
267
|
|
|
@@ -244,8 +269,10 @@
|
|
|
244
269
|
Close to the right / Close to the left / Close all. Replaces the old bar
|
|
245
270
|
"Close all" button. Mirrors .wt-ctx-menu (a fixed surface popover, clamped
|
|
246
271
|
into the viewport by the feature); disabled items are dimmed and inert. */
|
|
247
|
-
.wt-tab-menu {
|
|
248
|
-
|
|
272
|
+
:where(.wt-root) .wt-tab-menu {
|
|
273
|
+
/* Absolute against .wt-root; placed by the shared point-anchored positioner
|
|
274
|
+
(menu-position.ts), which converts to root-relative offsets. */
|
|
275
|
+
position: absolute;
|
|
249
276
|
display: none;
|
|
250
277
|
flex-direction: column;
|
|
251
278
|
background: var(--surface);
|
|
@@ -256,11 +283,11 @@
|
|
|
256
283
|
z-index: 30;
|
|
257
284
|
}
|
|
258
285
|
|
|
259
|
-
.wt-tab-menu.visible {
|
|
286
|
+
:where(.wt-root) .wt-tab-menu.visible {
|
|
260
287
|
display: flex;
|
|
261
288
|
}
|
|
262
289
|
|
|
263
|
-
.wt-tab-menu button {
|
|
290
|
+
:where(.wt-root) .wt-tab-menu button {
|
|
264
291
|
background: transparent;
|
|
265
292
|
color: var(--text);
|
|
266
293
|
border: none;
|
|
@@ -274,30 +301,37 @@
|
|
|
274
301
|
white-space: nowrap;
|
|
275
302
|
}
|
|
276
303
|
|
|
277
|
-
|
|
304
|
+
/* :enabled (not :not(:disabled)) keeps the pseudo-class count within the
|
|
305
|
+
stylelint budget alongside the zero-specificity scope. */
|
|
306
|
+
:where(.wt-root) .wt-tab-menu button:enabled:hover {
|
|
278
307
|
background: var(--text-active);
|
|
279
308
|
}
|
|
280
309
|
|
|
281
|
-
.wt-tab-menu button:disabled {
|
|
310
|
+
:where(.wt-root) .wt-tab-menu button:disabled {
|
|
282
311
|
color: color-mix(in oklch, var(--text) 35%, transparent);
|
|
283
312
|
cursor: default;
|
|
284
313
|
}
|
|
285
314
|
|
|
286
|
-
/* Pull the terminal surface up off the
|
|
315
|
+
/* Pull the terminal surface up off the docked BOTTOM tab strip so it does not
|
|
287
316
|
overlap the last rows (a mirror of the old top offset). The tabs feature
|
|
288
317
|
marks the surface (.wt-with-tabbar) and keeps the measured strip height in
|
|
289
|
-
--wt-tabbar-h; the surface is
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
css/
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
318
|
+
--wt-tabbar-h; the surface is absolute inset:0, so a bottom offset clears it.
|
|
319
|
+
Applies whenever the desktop strip is shown — a fine pointer OR a root that
|
|
320
|
+
is not narrow (an iPad) — the complement of the narrow-coarse switcher gate
|
|
321
|
+
in css/31-switcher.css. On the narrow-coarse phone the strip is hidden and
|
|
322
|
+
the mobile switcher's inset is applied through viewport.ts
|
|
323
|
+
(--wt-reserve-bottom) instead. Two rules because the class half cannot mix
|
|
324
|
+
into a media condition; they set the same declaration. */
|
|
325
|
+
@media (pointer: fine) {
|
|
326
|
+
:where(.wt-root) .term.wt-with-tabbar {
|
|
297
327
|
bottom: var(--wt-tabbar-h, 44px);
|
|
298
328
|
}
|
|
299
329
|
}
|
|
300
330
|
|
|
331
|
+
:where(.wt-root:not(.wt-narrow)) .term.wt-with-tabbar {
|
|
332
|
+
bottom: var(--wt-tabbar-h, 44px);
|
|
333
|
+
}
|
|
334
|
+
|
|
301
335
|
/* ===== Activity dots (activityMonitor via tabs) =====
|
|
302
336
|
Four-state, distinguishable without color (shape + motion, not hue alone),
|
|
303
337
|
per section 12 / WCAG 1.4.1. The one .wt-status-dot vocabulary is shared by
|
|
@@ -310,7 +344,7 @@
|
|
|
310
344
|
stay clean, label-only; a progress-reporting agent (kiro-cli, Claude Code)
|
|
311
345
|
lights up. The element stays in the DOM either way (JS sets data-status
|
|
312
346
|
regardless), so revealing it is a pure CSS switch. */
|
|
313
|
-
.wt-status-dot {
|
|
347
|
+
:where(.wt-root) .wt-status-dot {
|
|
314
348
|
display: none;
|
|
315
349
|
width: 9px;
|
|
316
350
|
height: 9px;
|
|
@@ -327,11 +361,11 @@
|
|
|
327
361
|
/* Reveal a dot only once its session has reported activity (the tabs feature
|
|
328
362
|
adds .wt-reports per dot; see paintStatusDot). Without it the dot — and the
|
|
329
363
|
whole activity vocabulary for that tab — stays invisible. */
|
|
330
|
-
.wt-status-dot.wt-reports {
|
|
364
|
+
:where(.wt-root) .wt-status-dot.wt-reports {
|
|
331
365
|
display: block;
|
|
332
366
|
}
|
|
333
367
|
|
|
334
|
-
.wt-status-dot[data-status="idle"] {
|
|
368
|
+
:where(.wt-root) .wt-status-dot[data-status="idle"] {
|
|
335
369
|
background: transparent;
|
|
336
370
|
border: 1.5px solid color-mix(in oklch, var(--text) 45%, transparent);
|
|
337
371
|
}
|
|
@@ -341,7 +375,7 @@
|
|
|
341
375
|
on every tab state.) Motion is the primary non-hue cue. Server-side this is
|
|
342
376
|
driven by the OSC 9;4 progress signal, so it tracks agent activity, not the
|
|
343
377
|
user typing at the prompt. */
|
|
344
|
-
.wt-status-dot[data-status="working"] {
|
|
378
|
+
:where(.wt-root) .wt-status-dot[data-status="working"] {
|
|
345
379
|
background: #fff;
|
|
346
380
|
border-color: #fff;
|
|
347
381
|
animation: wt-pulse 1.2s ease-in-out infinite;
|
|
@@ -349,7 +383,7 @@
|
|
|
349
383
|
|
|
350
384
|
/* Agent done / ready: solid green, matching Kiro IDE's "Ready" (#22c55e), no
|
|
351
385
|
motion. The filled dot distinguishes it from the hollow idle without hue. */
|
|
352
|
-
.wt-status-dot[data-status="done"] {
|
|
386
|
+
:where(.wt-root) .wt-status-dot[data-status="done"] {
|
|
353
387
|
background: #22c55e;
|
|
354
388
|
border-color: #22c55e;
|
|
355
389
|
}
|
|
@@ -357,14 +391,14 @@
|
|
|
357
391
|
/* User action required (permission / approval): solid amber matching Kiro IDE's
|
|
358
392
|
"waiting-approval" (#fbbf24), plus a ring so it reads as "needs you" via shape
|
|
359
393
|
as well as hue. It persists until you act on or switch into the tab. */
|
|
360
|
-
.wt-status-dot[data-status="input"] {
|
|
394
|
+
:where(.wt-root) .wt-status-dot[data-status="input"] {
|
|
361
395
|
background: #fbbf24;
|
|
362
396
|
border-color: #fbbf24;
|
|
363
397
|
box-shadow: 0 0 0 2px oklch(85% 0.16 85deg / 40%);
|
|
364
398
|
}
|
|
365
399
|
|
|
366
400
|
/* Exited: a dim hollow dot, static; distinct from idle by its lower opacity. */
|
|
367
|
-
.wt-status-dot[data-status="exited"] {
|
|
401
|
+
:where(.wt-root) .wt-status-dot[data-status="exited"] {
|
|
368
402
|
background: transparent;
|
|
369
403
|
border: 1.5px solid color-mix(in oklch, var(--text) 30%, transparent);
|
|
370
404
|
opacity: 0.5;
|
|
@@ -378,7 +412,7 @@
|
|
|
378
412
|
|
|
379
413
|
/* Reduced-motion (or the animations feature off): drop the pulse, keep color. */
|
|
380
414
|
@media (prefers-reduced-motion: reduce) {
|
|
381
|
-
.wt-status-dot[data-status="working"] {
|
|
415
|
+
:where(.wt-root) .wt-status-dot[data-status="working"] {
|
|
382
416
|
animation: none;
|
|
383
417
|
/* The pulse is working's ONLY non-hue cue; with motion removed,
|
|
384
418
|
add a static shape cue (an inner hole -> a ring) so working differs
|
|
@@ -386,3 +420,30 @@
|
|
|
386
420
|
box-shadow: inset 0 0 0 2px var(--bg);
|
|
387
421
|
}
|
|
388
422
|
}
|
|
423
|
+
|
|
424
|
+
/* ===== Catching-up cue (tabs feature, sections 12/13) =====
|
|
425
|
+
A switched-into tab's cached screen is stale until its resume delta lands, so
|
|
426
|
+
it must not read as live. This unobtrusive cue shows only if the delta has not
|
|
427
|
+
arrived shortly after the switch, and clears on the first screen frame.
|
|
428
|
+
Absolute against .wt-root, like all chrome. */
|
|
429
|
+
:where(.wt-root) .wt-catchup {
|
|
430
|
+
position: absolute;
|
|
431
|
+
top: calc(var(--safe-top, 0px) + var(--vv-top, 0px) + 44px);
|
|
432
|
+
left: 50%;
|
|
433
|
+
transform: translateX(-50%);
|
|
434
|
+
padding: 2px var(--sp-2);
|
|
435
|
+
background: var(--surface);
|
|
436
|
+
backdrop-filter: var(--blur);
|
|
437
|
+
border: 1px solid var(--border);
|
|
438
|
+
border-radius: var(--radius);
|
|
439
|
+
color: color-mix(in oklch, var(--text) 75%, transparent);
|
|
440
|
+
font-family: var(--font-ui);
|
|
441
|
+
font-size: 0.75rem;
|
|
442
|
+
z-index: 24;
|
|
443
|
+
pointer-events: none; /* passive status surface: never steal taps from the terminal beneath */
|
|
444
|
+
display: none;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
:where(.wt-root) .wt-catchup.visible {
|
|
448
|
+
display: block;
|
|
449
|
+
}
|