agentgui 1.0.959 → 1.0.961
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/.claude/workflows/gui-claude-code-parity.js +88 -0
- package/.claude/workflows/gui-logic-predictability.js +88 -0
- package/AGENTS.md +27 -9
- package/PUNCHLIST-LOGIC.md +405 -0
- package/PUNCHLIST-PARITY.md +36 -0
- package/lib/broadcast.js +1 -0
- package/lib/ws-handlers-util.js +42 -4
- package/package.json +1 -1
- package/server.js +27 -0
- package/site/app/js/app.js +676 -137
- package/site/app/js/backend.js +25 -1
- package/site/app/vendor/anentrypoint-design/247420.css +363 -50
- package/site/app/vendor/anentrypoint-design/247420.js +15 -13
package/site/app/js/backend.js
CHANGED
|
@@ -363,8 +363,18 @@ export async function* streamChat(base, { model, messages, signal, agentId, resu
|
|
|
363
363
|
else if (block?.type === 'result') push({ type: 'result', block });
|
|
364
364
|
} else if (ev.type === 'streaming_complete') {
|
|
365
365
|
finish();
|
|
366
|
+
} else if (ev.type === 'streaming_cancelled') {
|
|
367
|
+
push({ type: 'cancelled' });
|
|
368
|
+
finish();
|
|
366
369
|
} else if (ev.type === 'streaming_error') {
|
|
367
|
-
|
|
370
|
+
// A remote stop (another tab / dashboard stop-all) is not a failure: the
|
|
371
|
+
// server marks it cancelled; surface it as a distinct event so the app
|
|
372
|
+
// can label the turn 'stopped' instead of a normal finish or error red.
|
|
373
|
+
if (ev.cancelled || ev.error === 'cancelled') {
|
|
374
|
+
push({ type: 'cancelled' });
|
|
375
|
+
} else {
|
|
376
|
+
errored = ev.error || 'streaming error';
|
|
377
|
+
}
|
|
368
378
|
finish();
|
|
369
379
|
}
|
|
370
380
|
});
|
|
@@ -381,6 +391,20 @@ export async function* streamChat(base, { model, messages, signal, agentId, resu
|
|
|
381
391
|
if (s === 'open') {
|
|
382
392
|
// Reconnected in time - the open handler already re-subscribed us.
|
|
383
393
|
if (graceTimer) { clearTimeout(graceTimer); graceTimer = null; }
|
|
394
|
+
// streaming_complete is fire-and-forget with no replay: if the turn
|
|
395
|
+
// finished while the socket was down, no terminal frame will ever
|
|
396
|
+
// arrive. Verify the session is still alive server-side; if it is
|
|
397
|
+
// gone, settle the iterator with a plain incomplete marker instead of
|
|
398
|
+
// hanging busy forever.
|
|
399
|
+
wsCall(base, 'chat.active', {}).then((d) => {
|
|
400
|
+
if (done) return;
|
|
401
|
+
const sessions = (d && d.sessions) || [];
|
|
402
|
+
const alive = Array.isArray(sessions) && sessions.some(x => x && x.sessionId === sessionId);
|
|
403
|
+
if (!alive) {
|
|
404
|
+
errored = errored || 'connection dropped mid-turn - the response may be incomplete; events were not replayed';
|
|
405
|
+
finish();
|
|
406
|
+
}
|
|
407
|
+
}).catch(() => {});
|
|
384
408
|
return;
|
|
385
409
|
}
|
|
386
410
|
if ((s === 'closed' || s === 'error') && !graceTimer) {
|
|
@@ -442,7 +442,7 @@
|
|
|
442
442
|
-webkit-font-smoothing: antialiased;
|
|
443
443
|
text-rendering: optimizeLegibility;
|
|
444
444
|
}
|
|
445
|
-
.ds-247420 ::selection { background: var(--
|
|
445
|
+
.ds-247420 ::selection { background: color-mix(in oklab, var(--accent) 24%, var(--bg-2)); color: var(--fg); }
|
|
446
446
|
|
|
447
447
|
/* Every root has a CQ container so fluid type can resolve to a meaningful inline-size. */
|
|
448
448
|
.ds-247420 .app, .ds-247420[class*="kit-"], .ds-247420 .ds-stage {
|
|
@@ -555,6 +555,9 @@
|
|
|
555
555
|
============================================================ */
|
|
556
556
|
.ds-247420 .app {
|
|
557
557
|
display: flex; flex-direction: column;
|
|
558
|
+
/* Offset parent for the sidebar drawer/scrim/toggle, which are absolute so
|
|
559
|
+
they overlay THIS shell (a thebird WM window) rather than the viewport. */
|
|
560
|
+
position: relative;
|
|
558
561
|
min-height: 100vh;
|
|
559
562
|
/* Definite height so the flex column resolves height:100% for descendants
|
|
560
563
|
(e.g. a full-height chat) at every breakpoint, not just where a desktop
|
|
@@ -932,41 +935,61 @@
|
|
|
932
935
|
grid-template-columns: minmax(80px, 12ch) minmax(0, 1fr) auto;
|
|
933
936
|
gap: var(--space-3);
|
|
934
937
|
align-items: baseline;
|
|
935
|
-
padding:
|
|
938
|
+
padding: 16px 22px;
|
|
936
939
|
background: var(--bg);
|
|
937
940
|
border-radius: var(--r-2);
|
|
938
941
|
color: var(--fg);
|
|
939
942
|
position: relative;
|
|
940
|
-
|
|
943
|
+
/* Only background + the leading rail animate; padding/size never change on
|
|
944
|
+
interaction, so they are deliberately absent (a padding transition here was
|
|
945
|
+
dead and misleading). */
|
|
946
|
+
transition: background var(--dur-snap) var(--ease);
|
|
941
947
|
}
|
|
942
|
-
.ds-247420 .row + .row { margin-top:
|
|
948
|
+
.ds-247420 .row + .row { margin-top: 2px; }
|
|
943
949
|
.ds-247420 .row::before {
|
|
944
950
|
content: '';
|
|
945
|
-
position: absolute; left:
|
|
946
|
-
|
|
947
|
-
|
|
951
|
+
position: absolute; left: 7px; top: 0; bottom: 0; width: 3px;
|
|
952
|
+
margin: auto 0;
|
|
953
|
+
height: 0; /* collapsed by default; reveals from centre */
|
|
954
|
+
background: var(--rule-strong);
|
|
955
|
+
border-radius: var(--r-pill);
|
|
956
|
+
opacity: 0;
|
|
957
|
+
transition: height var(--dur-base) var(--ease), opacity var(--dur-snap) var(--ease), background var(--dur-snap) var(--ease);
|
|
948
958
|
}
|
|
949
959
|
/* A clickable row (onClick -> role=button, or a link row) is interactive, so it
|
|
950
960
|
carries a pointer cursor; without this the click affordance is invisible. */
|
|
951
961
|
.ds-247420 .row[role="button"], .ds-247420 a.row { cursor: pointer; }
|
|
952
|
-
|
|
953
|
-
|
|
962
|
+
/* Hover reveals the rail (grows from centre) only on interactive rows — a static
|
|
963
|
+
row should not signal an affordance it does not have. */
|
|
964
|
+
.ds-247420 .row[role="button"]:hover, .ds-247420 a.row:hover { background: var(--bg-2); }
|
|
965
|
+
.ds-247420 .row[role="button"]:hover::before, .ds-247420 a.row:hover::before { height: 56%; opacity: 1; }
|
|
954
966
|
.ds-247420 .row.active { background: color-mix(in oklab, var(--accent) 10%, var(--bg-2)); }
|
|
955
|
-
.ds-247420 .row.active::before { background: var(--accent); }
|
|
967
|
+
.ds-247420 .row.active::before { height: 64%; opacity: 1; background: var(--accent); }
|
|
968
|
+
.ds-247420 .row.active .title { color: var(--accent); }
|
|
956
969
|
.ds-247420 .row.row-state-disabled { opacity: 0.5; pointer-events: none; }
|
|
957
970
|
.ds-247420 .row.row-state-error { background: color-mix(in oklab, var(--flame) 10%, var(--bg-2)); }
|
|
958
|
-
.ds-247420 .row.row-state-error::before { background: var(--flame); }
|
|
971
|
+
.ds-247420 .row.row-state-error::before { height: 64%; opacity: 1; background: var(--flame); }
|
|
959
972
|
/* rail tones — a persistent status colour on the leading rail (the ::before
|
|
960
|
-
bar).
|
|
973
|
+
bar). Always shown (a status indicator, not a hover affordance). Active/error
|
|
974
|
+
still win via their own rules below in source order. */
|
|
975
|
+
.ds-247420 .row.rail-green::before,
|
|
976
|
+
.ds-247420 .row.rail-purple::before,
|
|
977
|
+
.ds-247420 .row.rail-flame::before { height: 56%; opacity: 1; }
|
|
961
978
|
.ds-247420 .row.rail-green::before { background: var(--accent); }
|
|
962
979
|
.ds-247420 .row.rail-purple::before { background: var(--purple-2, #7F18A4); }
|
|
963
980
|
.ds-247420 .row.rail-flame::before { background: var(--flame); }
|
|
964
981
|
.ds-247420 .row-grid { /* explicit grid-template-columns set inline */ }
|
|
982
|
+
/* A row with no leading code/index drops the empty first column so the title
|
|
983
|
+
takes the full width instead of wrapping in the narrow code gutter. */
|
|
984
|
+
.ds-247420 .row.row-nocode { grid-template-columns: minmax(0, 1fr) auto; }
|
|
965
985
|
|
|
966
|
-
.ds-247420 .row .code { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--fg-3); }
|
|
967
|
-
.ds-247420 .row .title { font-family: var(--ff-body); font-weight: 600; font-size: var(--fs-lg); display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
|
|
986
|
+
.ds-247420 .row .code { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--fg-3); font-variant-numeric: tabular-nums; letter-spacing: 0.01em; }
|
|
987
|
+
.ds-247420 .row .title { font-family: var(--ff-body); font-weight: 600; font-size: var(--fs-lg); line-height: var(--lh-snug, 1.3); display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; min-width: 0; }
|
|
988
|
+
.ds-247420 .row .title .sub { font-family: var(--ff-body); font-weight: 400; font-size: var(--fs-sm); color: var(--fg-3); }
|
|
968
989
|
.ds-247420 .row .sub { font-family: var(--ff-body); font-weight: 400; font-size: var(--fs-sm); color: var(--fg-3); }
|
|
969
|
-
.ds-247420 .row .meta { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--fg-3); text-align: right; }
|
|
990
|
+
.ds-247420 .row .meta { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--fg-3); text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; align-self: center; }
|
|
991
|
+
/* WorksList meta pairs a label with a disclosure chevron, inline-aligned. */
|
|
992
|
+
.ds-247420 .ds-works-meta { display: inline-flex; align-items: center; gap: .4em; }
|
|
970
993
|
|
|
971
994
|
.ds-247420 a.row { text-decoration: none; }
|
|
972
995
|
|
|
@@ -1700,6 +1723,19 @@
|
|
|
1700
1723
|
.ds-247420 .ds-upload-item.done .ds-upload-pct { color: var(--success); }
|
|
1701
1724
|
.ds-247420 .ds-upload-item.error .ds-upload-fill { background: var(--warn); }
|
|
1702
1725
|
.ds-247420 .ds-upload-item.error .ds-upload-pct { color: var(--warn); }
|
|
1726
|
+
/* Per-row upload recovery actions (replace / dismiss) — error rows are not dead ends. */
|
|
1727
|
+
.ds-247420 .ds-upload-item { grid-template-columns: minmax(0, 1fr) 120px 44px auto; }
|
|
1728
|
+
.ds-247420 .ds-upload-actions { display: inline-flex; gap: var(--space-1); justify-content: flex-end; }
|
|
1729
|
+
.ds-247420 .ds-upload-act {
|
|
1730
|
+
padding: 2px 10px; min-height: 24px; cursor: pointer;
|
|
1731
|
+
background: var(--bg); border: var(--bw-hair) solid var(--rule); border-radius: var(--r-1);
|
|
1732
|
+
color: var(--fg-2); font-family: var(--ff-body); font-size: var(--fs-micro);
|
|
1733
|
+
}
|
|
1734
|
+
.ds-247420 .ds-upload-act:hover { background: var(--bg-3); color: var(--fg); }
|
|
1735
|
+
.ds-247420 .ds-upload-act:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
1736
|
+
@media (hover: none), (pointer: coarse) {
|
|
1737
|
+
.ds-247420 .ds-upload-act { min-height: 44px; min-width: 44px; }
|
|
1738
|
+
}
|
|
1703
1739
|
|
|
1704
1740
|
/* Empty state */
|
|
1705
1741
|
.ds-247420 .ds-file-empty {
|
|
@@ -1757,6 +1793,14 @@
|
|
|
1757
1793
|
font-family: inherit; font-size: var(--fs-sm);
|
|
1758
1794
|
}
|
|
1759
1795
|
.ds-247420 .ds-modal-input:focus { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
1796
|
+
/* In-body modal error (role=alert): mutation failures (409/403) surface INSIDE
|
|
1797
|
+
the dialog, not behind the fixed overlay. */
|
|
1798
|
+
.ds-247420 .ds-modal-error {
|
|
1799
|
+
margin: var(--space-2) 0 0; padding: 8px 10px;
|
|
1800
|
+
border: var(--bw-hair) solid var(--flame); border-radius: var(--r-2);
|
|
1801
|
+
background: color-mix(in oklab, var(--flame) 12%, var(--bg));
|
|
1802
|
+
color: var(--flame); font-size: var(--fs-xs);
|
|
1803
|
+
}
|
|
1760
1804
|
.ds-247420 .btn-primary.danger {
|
|
1761
1805
|
background: var(--warn);
|
|
1762
1806
|
border-color: var(--warn);
|
|
@@ -1886,10 +1930,12 @@
|
|
|
1886
1930
|
.ds-247420 .ds-file-row:hover .ds-file-check,
|
|
1887
1931
|
.ds-247420 .ds-file-row:focus-within .ds-file-check,
|
|
1888
1932
|
.ds-247420 .ds-file-check.on { opacity: 1; }
|
|
1889
|
-
/* Touch / coarse-pointer devices have no hover — keep checkboxes
|
|
1890
|
-
|
|
1933
|
+
/* Touch / coarse-pointer devices have no hover — keep checkboxes AND the
|
|
1934
|
+
per-row action buttons (rename/delete/download) visible so the file manager
|
|
1935
|
+
is fully reachable without a pointer (e.g. an iPad in landscape). */
|
|
1891
1936
|
@media (hover: none), (pointer: coarse) {
|
|
1892
1937
|
.ds-247420 .ds-file-check { opacity: 1; }
|
|
1938
|
+
.ds-247420 .ds-file-actions { opacity: 1; }
|
|
1893
1939
|
}
|
|
1894
1940
|
.ds-247420 .ds-file-check.on { background: var(--accent); border-color: var(--accent); }
|
|
1895
1941
|
.ds-247420 .ds-file-row.selected {
|
|
@@ -2295,36 +2341,43 @@
|
|
|
2295
2341
|
.ds-247420 .chat-msg:nth-child(n+4) { animation-delay: 0.15s; }
|
|
2296
2342
|
}
|
|
2297
2343
|
|
|
2344
|
+
/* Calm composer (claude.ai/code): ONE bordered rounded-rect shell holding the
|
|
2345
|
+
context line, a borderless textarea, the hint, and an inline send button -
|
|
2346
|
+
not a stadium pill + a floating round send. The shell carries the border and
|
|
2347
|
+
the focus ring; the textarea fills it transparently. flex-wrap lets the
|
|
2348
|
+
context line and hint occupy their own rows above/below the input row. */
|
|
2298
2349
|
.ds-247420 .chat-composer {
|
|
2299
|
-
display: flex; align-items: flex-end; gap:
|
|
2300
|
-
padding: var(--space-
|
|
2301
|
-
|
|
2350
|
+
display: flex; align-items: flex-end; flex-wrap: wrap; gap: var(--space-2);
|
|
2351
|
+
padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
|
|
2352
|
+
background: var(--bg);
|
|
2353
|
+
border: var(--bw-hair) solid var(--rule);
|
|
2354
|
+
border-radius: var(--r-2);
|
|
2302
2355
|
flex-shrink: 0;
|
|
2356
|
+
transition: border-color var(--dur-snap) var(--ease), box-shadow var(--dur-snap) var(--ease);
|
|
2357
|
+
}
|
|
2358
|
+
.ds-247420 .chat-composer:focus-within {
|
|
2359
|
+
border-color: var(--accent);
|
|
2360
|
+
box-shadow: 0 0 0 2px color-mix(in oklab, var(--accent) 18%, transparent);
|
|
2303
2361
|
}
|
|
2304
2362
|
.ds-247420 .chat-composer textarea {
|
|
2305
|
-
flex: 1; padding:
|
|
2306
|
-
background:
|
|
2307
|
-
border:
|
|
2363
|
+
flex: 1; min-width: 0; padding: 8px 4px;
|
|
2364
|
+
background: none; color: var(--fg);
|
|
2365
|
+
border: none; border-radius: 0;
|
|
2308
2366
|
font-family: inherit; font-size: var(--fs-sm);
|
|
2309
|
-
line-height: 1.
|
|
2310
|
-
min-height:
|
|
2367
|
+
line-height: 1.45; resize: none;
|
|
2368
|
+
min-height: 28px; max-height: 200px;
|
|
2311
2369
|
box-sizing: border-box; overflow-y: auto;
|
|
2312
2370
|
scrollbar-width: thin;
|
|
2313
2371
|
}
|
|
2314
2372
|
.ds-247420 .chat-composer textarea::placeholder { color: var(--fg-3); }
|
|
2315
|
-
.ds-247420 .chat-composer textarea:focus {
|
|
2316
|
-
background: var(--bg-2);
|
|
2317
|
-
border-color: var(--accent);
|
|
2318
|
-
box-shadow: 0 0 0 2px color-mix(in oklab, var(--accent) 20%, transparent);
|
|
2319
|
-
outline: none;
|
|
2320
|
-
}
|
|
2373
|
+
.ds-247420 .chat-composer textarea:focus { background: none; border: none; box-shadow: none; outline: none; }
|
|
2321
2374
|
.ds-247420 .chat-composer .send {
|
|
2322
|
-
width:
|
|
2375
|
+
width: 36px; height: 36px; border-radius: var(--r-1);
|
|
2323
2376
|
background: var(--accent); color: var(--accent-fg);
|
|
2324
2377
|
border: 0; cursor: pointer;
|
|
2325
2378
|
display: inline-flex; align-items: center; justify-content: center;
|
|
2326
2379
|
flex-shrink: 0;
|
|
2327
|
-
transition: background var(--dur-snap, .15s) var(--ease, ease)
|
|
2380
|
+
transition: background var(--dur-snap, .15s) var(--ease, ease);
|
|
2328
2381
|
}
|
|
2329
2382
|
.ds-247420 .chat-composer .send:disabled { opacity: .5; cursor: not-allowed; }
|
|
2330
2383
|
/* Ghost icon buttons in the toolbar (attach / emoji / more) — NOT the round
|
|
@@ -2338,8 +2391,8 @@
|
|
|
2338
2391
|
transition: background var(--dur-snap, .15s) var(--ease, ease), color var(--dur-snap, .15s) var(--ease, ease);
|
|
2339
2392
|
}
|
|
2340
2393
|
.ds-247420 .composer-btn:hover { background: color-mix(in oklab, var(--fg) 8%, transparent); color: var(--fg); }
|
|
2341
|
-
.ds-247420 .chat-composer .send:hover {
|
|
2342
|
-
.ds-247420 .chat-composer .send:active {
|
|
2394
|
+
.ds-247420 .chat-composer .send:hover { filter: brightness(1.08); }
|
|
2395
|
+
.ds-247420 .chat-composer .send:active { filter: brightness(0.94); }
|
|
2343
2396
|
.ds-247420 .chat-composer .send:focus-visible,
|
|
2344
2397
|
.ds-247420 .composer-btn:focus-visible {
|
|
2345
2398
|
outline: 2px solid var(--accent);
|
|
@@ -2449,6 +2502,12 @@
|
|
|
2449
2502
|
from { transform: rotate(0deg); }
|
|
2450
2503
|
to { transform: rotate(360deg); }
|
|
2451
2504
|
}
|
|
2505
|
+
/* Reusable spin utility: rotates the element (e.g. a refresh Icon used as a
|
|
2506
|
+
busy indicator) via the shared spinner-rotate keyframe. Respects reduced
|
|
2507
|
+
motion. Use instead of a decorative rotation glyph. */
|
|
2508
|
+
.ds-247420 .ds-spin { animation: spinner-rotate 0.8s linear infinite; transform-origin: center; }
|
|
2509
|
+
@media (prefers-reduced-motion: reduce) { .ds-247420 .ds-spin { animation: none; } }
|
|
2510
|
+
|
|
2452
2511
|
.ds-247420 .loading { position: relative; }
|
|
2453
2512
|
.ds-247420 .loading::after {
|
|
2454
2513
|
content: '';
|
|
@@ -3031,14 +3090,20 @@
|
|
|
3031
3090
|
============================================================ */
|
|
3032
3091
|
|
|
3033
3092
|
/* Tablet + Mobile: side rail becomes off-canvas drawer (≤900px) */
|
|
3034
|
-
|
|
3093
|
+
/* Sidebar drawer collapse keys on the SHELL width (@container on .app, which is
|
|
3094
|
+
container-type:inline-size) — NOT the viewport. Full-page (freddie) the shell
|
|
3095
|
+
== viewport so this is identical to the old @media(max-width:900px); embedded
|
|
3096
|
+
in a thebird WM window the drawer + hamburger appear when the WINDOW is narrow
|
|
3097
|
+
even on a wide desktop. Drawer/scrim/toggle are absolute (anchored to .app), so
|
|
3098
|
+
they overlay the window, not the page. */
|
|
3099
|
+
@container (max-width: 900px) {
|
|
3035
3100
|
.ds-247420 .app-body,
|
|
3036
3101
|
.ds-247420 .app-body.no-side { grid-template-columns: 1fr !important; }
|
|
3037
3102
|
.ds-247420 .app-side-shell {
|
|
3038
|
-
position:
|
|
3103
|
+
position: absolute;
|
|
3039
3104
|
left: 0; top: var(--app-topbar-h);
|
|
3040
|
-
height: calc(
|
|
3041
|
-
width: 280px; max-width:
|
|
3105
|
+
height: calc(100% - var(--app-topbar-h) - var(--app-status-h));
|
|
3106
|
+
width: 280px; max-width: 80%;
|
|
3042
3107
|
transform: translateX(-100%);
|
|
3043
3108
|
transition: transform var(--dur-base, 240ms) var(--ease, ease);
|
|
3044
3109
|
z-index: 50;
|
|
@@ -3072,21 +3137,24 @@
|
|
|
3072
3137
|
.ds-247420 .app-main { padding-left: var(--space-3); padding-right: var(--space-3); }
|
|
3073
3138
|
}
|
|
3074
3139
|
|
|
3075
|
-
/* Drawer scrim —
|
|
3140
|
+
/* Drawer scrim — absolute so it covers THIS shell, shown when drawer open and
|
|
3141
|
+
the shell is narrow (@container on .app). */
|
|
3076
3142
|
.ds-247420 .app-side-scrim {
|
|
3077
3143
|
display: none;
|
|
3078
|
-
position:
|
|
3144
|
+
position: absolute; inset: 0;
|
|
3079
3145
|
background: color-mix(in oklab, var(--bg) 60%, transparent);
|
|
3080
3146
|
z-index: 49;
|
|
3081
3147
|
}
|
|
3082
|
-
@
|
|
3148
|
+
@container (max-width: 900px) {
|
|
3083
3149
|
.ds-247420 .app-body.side-open .app-side-scrim { display: block; }
|
|
3084
3150
|
}
|
|
3085
3151
|
|
|
3086
|
-
/*
|
|
3152
|
+
/* Nav toggle (hamburger) — hidden when the shell is wide, shown when the shell
|
|
3153
|
+
itself is narrow (@container on .app). Absolute so it sits in THIS shell's
|
|
3154
|
+
topbar, not pinned to the viewport corner. */
|
|
3087
3155
|
.ds-247420 .app-side-toggle {
|
|
3088
3156
|
display: none;
|
|
3089
|
-
position:
|
|
3157
|
+
position: absolute; top: calc((var(--app-topbar-h) - 44px) / 2); left: 10px;
|
|
3090
3158
|
z-index: 51;
|
|
3091
3159
|
/* 44x44 minimum hit area (WCAG 2.5.5 / Apple HIG) - 36px is below the
|
|
3092
3160
|
reliable-tap threshold on touch. */
|
|
@@ -3099,7 +3167,7 @@
|
|
|
3099
3167
|
cursor: pointer;
|
|
3100
3168
|
}
|
|
3101
3169
|
.ds-247420 .app-side-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
3102
|
-
@
|
|
3170
|
+
@container (max-width: 900px) {
|
|
3103
3171
|
.ds-247420 .app-side-toggle { display: inline-flex; }
|
|
3104
3172
|
.ds-247420 .app-topbar .brand { margin-left: 44px; }
|
|
3105
3173
|
}
|
|
@@ -3172,6 +3240,12 @@
|
|
|
3172
3240
|
.ds-247420 .ws-shell.ws-no-pane.ws-pane-collapsed.ws-no-sessions { grid-template-columns: var(--ws-rail-w) 1fr var(--ws-pane-w); grid-template-areas: "rail content pane"; }
|
|
3173
3241
|
.ds-247420 .ws-shell.ws-rail-collapsed { --ws-rail-w: var(--ws-rail-w-collapsed); }
|
|
3174
3242
|
.ds-247420 .ws-shell.ws-pane-collapsed { --ws-pane-w: 0px; }
|
|
3243
|
+
/* Ease the rail/pane collapse. Track COUNT stays stable on collapse (only a
|
|
3244
|
+
width var flips), so grid-template-columns is animatable; reduced-motion
|
|
3245
|
+
drops it to an instant swap. */
|
|
3246
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
3247
|
+
.ds-247420 .ws-shell { transition: grid-template-columns var(--dur-base) var(--ease); }
|
|
3248
|
+
}
|
|
3175
3249
|
|
|
3176
3250
|
/* Rail (left nav) */
|
|
3177
3251
|
.ds-247420 .ws-rail {
|
|
@@ -3242,9 +3316,17 @@
|
|
|
3242
3316
|
|
|
3243
3317
|
/* Content column */
|
|
3244
3318
|
.ds-247420 .ws-content { grid-area: content; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
|
|
3245
|
-
|
|
3319
|
+
/* Crumb band is the top chrome of the content column: a stable height so the
|
|
3320
|
+
top edge aligns with the rail head, and a left gutter matching .ws-main so
|
|
3321
|
+
the trail text is not flush against the rail border. */
|
|
3322
|
+
.ds-247420 .ws-crumb { flex: 0 0 auto; min-height: 48px; padding: 0 var(--space-4); display: flex; align-items: center; gap: var(--space-1); border-bottom: var(--bw-hair) solid var(--bg-3); }
|
|
3246
3323
|
.ds-247420 .ws-crumb-main { flex: 1 1 auto; min-width: 0; }
|
|
3247
|
-
|
|
3324
|
+
/* Content column gutter. Claude-Code-class calm: file grid / dashboard / event
|
|
3325
|
+
list / history get a generous, consistent inner gutter instead of butting
|
|
3326
|
+
against the 1px column borders. The chat tab opts out via .ws-main--flush
|
|
3327
|
+
because the chat thread carries its own --measure gutter. */
|
|
3328
|
+
.ds-247420 .ws-main { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; padding: var(--space-4) var(--space-5); }
|
|
3329
|
+
.ds-247420 .ws-main.ws-main--flush { padding: 0; }
|
|
3248
3330
|
.ds-247420 .ws-main:focus { outline: none; }
|
|
3249
3331
|
|
|
3250
3332
|
/* Right context pane */
|
|
@@ -3289,8 +3371,11 @@
|
|
|
3289
3371
|
.ds-247420 .ws-rail-collapsed .ws-rail-item-label, .ds-247420 .ws-rail-collapsed .ws-rail-brand { display: none; }
|
|
3290
3372
|
/* Tap-scrim behind an open drawer; dismiss on click. Hidden unless a drawer
|
|
3291
3373
|
is open so it never blocks the content area on desktop. */
|
|
3292
|
-
|
|
3293
|
-
|
|
3374
|
+
/* Scrim is always present on mobile so it can FADE with the drawer instead of
|
|
3375
|
+
hard-appearing via a display toggle; pointer-events gate keeps it inert
|
|
3376
|
+
while closed. Reduced-motion users get the global transition kill -> instant. */
|
|
3377
|
+
.ds-247420 .ws-scrim { display: block; opacity: 0; pointer-events: none; position: fixed; inset: 0; z-index: 41; background: color-mix(in srgb, var(--fg) 38%, transparent); transition: opacity var(--dur-base) var(--ease); }
|
|
3378
|
+
.ds-247420 .ws-shell.ws-sessions-open .ws-scrim, .ds-247420 .ws-shell.ws-pane-open .ws-scrim { opacity: 1; pointer-events: auto; }
|
|
3294
3379
|
/* The mobile drawer-toggle affordances live in the crumb bar. */
|
|
3295
3380
|
.ds-247420 .ws-drawer-toggle { display: inline-flex; }
|
|
3296
3381
|
}
|
|
@@ -5337,7 +5422,10 @@
|
|
|
5337
5422
|
/* --- C5: active dashboard card (current conversation). --- */
|
|
5338
5423
|
.ds-247420 .ds-dash-card.is-active { box-shadow: inset 2px 0 0 var(--accent); }
|
|
5339
5424
|
.ds-247420 .ds-dash-card.is-selected { background: var(--accent-tint); }
|
|
5340
|
-
|
|
5425
|
+
/* Stale/idle card: a positive amber inset bar (mirrors is-active) so a stuck
|
|
5426
|
+
agent is flagged in a dense grid, not merely faded near-invisibly. The word
|
|
5427
|
+
'idle' co-carries state, so this stays colour-blind safe. */
|
|
5428
|
+
.ds-247420 .ds-dash-card.is-stale { box-shadow: inset 2px 0 0 var(--amber, #d9a93a); }
|
|
5341
5429
|
|
|
5342
5430
|
/* --- H3: dashboard live disc pulses; stale/error do not (handled by H1). --- */
|
|
5343
5431
|
|
|
@@ -5415,6 +5503,231 @@
|
|
|
5415
5503
|
.ds-247420 .ds-session-meta-copy { min-height: 44px; min-width: 44px; }
|
|
5416
5504
|
}
|
|
5417
5505
|
|
|
5506
|
+
/* ============================================================================
|
|
5507
|
+
Logic/predictability sweep (2026-06-10): composer ergonomics, turn notices,
|
|
5508
|
+
windowed thread, dashboard stopping/external states, cwd validation.
|
|
5509
|
+
============================================================================ */
|
|
5510
|
+
|
|
5511
|
+
/* Enter-to-send hint: visible while the composer is focused or carries a
|
|
5512
|
+
draft; hidden under 420px to save rows. */
|
|
5513
|
+
.ds-247420 .chat-composer-hint {
|
|
5514
|
+
display: none; width: 100%; order: 5; padding: 2px var(--space-1) 0;
|
|
5515
|
+
font-size: var(--fs-micro); color: var(--fg-3);
|
|
5516
|
+
}
|
|
5517
|
+
.ds-247420 .chat-composer:focus-within .chat-composer-hint,
|
|
5518
|
+
.ds-247420 .chat-composer.has-draft .chat-composer-hint { display: block; }
|
|
5519
|
+
@media (max-width: 420px) {
|
|
5520
|
+
.ds-247420 .chat-composer .chat-composer-hint { display: none; }
|
|
5521
|
+
}
|
|
5522
|
+
|
|
5523
|
+
/* Transient composer note (paste/drop not supported), aria-live polite. */
|
|
5524
|
+
.ds-247420 .chat-composer-note {
|
|
5525
|
+
padding: 4px var(--space-3); font-size: var(--fs-tiny); color: var(--fg-2);
|
|
5526
|
+
}
|
|
5527
|
+
|
|
5528
|
+
/* Drag-over ring: token accent, never a hex. */
|
|
5529
|
+
.ds-247420 .chat-composer.dragover { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--r-2); }
|
|
5530
|
+
|
|
5531
|
+
/* Per-bit composer context: only the bit that owns a control is a button. */
|
|
5532
|
+
.ds-247420 .chat-composer-context-bit {
|
|
5533
|
+
display: inline; padding: 0; margin: 0;
|
|
5534
|
+
background: none; border: none; cursor: pointer;
|
|
5535
|
+
font: inherit; color: inherit; text-decoration: underline dotted;
|
|
5536
|
+
}
|
|
5537
|
+
.ds-247420 .chat-composer-context-bit:hover { color: var(--fg-2); }
|
|
5538
|
+
.ds-247420 .chat-composer-context-bit:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
5539
|
+
@media (pointer: coarse) {
|
|
5540
|
+
.ds-247420 .chat-composer-context-bit { min-height: 44px; }
|
|
5541
|
+
}
|
|
5542
|
+
|
|
5543
|
+
/* Out-of-band turn notices: neutral tone (NOT error red) — a stopped or
|
|
5544
|
+
incomplete turn must not read as a finished answer. */
|
|
5545
|
+
.ds-247420 .chat-msg-notice {
|
|
5546
|
+
margin-top: var(--space-1); padding: 6px 10px;
|
|
5547
|
+
border: var(--bw-hair) solid var(--rule); border-radius: var(--r-2);
|
|
5548
|
+
background: var(--bg-2); color: var(--fg-2); font-size: var(--fs-tiny);
|
|
5549
|
+
}
|
|
5550
|
+
.ds-247420 .chat-msg-notice.is-incomplete { border-style: dashed; }
|
|
5551
|
+
|
|
5552
|
+
/* Tail-window streaming head ('streaming · N KB so far'). */
|
|
5553
|
+
.ds-247420 .chat-stream-head {
|
|
5554
|
+
padding: 2px 0 6px; font-family: var(--ff-mono);
|
|
5555
|
+
font-size: var(--fs-micro); color: var(--fg-3);
|
|
5556
|
+
}
|
|
5557
|
+
|
|
5558
|
+
/* Windowed thread: 'show N earlier turns' control at the top. */
|
|
5559
|
+
.ds-247420 .agentchat-earlier {
|
|
5560
|
+
display: flex; align-items: center; justify-content: center;
|
|
5561
|
+
gap: var(--space-3); padding: var(--space-2) var(--space-3);
|
|
5562
|
+
}
|
|
5563
|
+
.ds-247420 .agentchat-earlier-count { font-size: var(--fs-tiny); color: var(--fg-3); }
|
|
5564
|
+
.ds-247420 .agentchat-earlier-btn {
|
|
5565
|
+
padding: 4px 12px; min-height: 32px; border: var(--bw-hair) solid var(--bg-3);
|
|
5566
|
+
border-radius: var(--r-1); background: var(--bg-2); color: var(--fg-2); cursor: pointer;
|
|
5567
|
+
font-family: var(--ff-body); font-size: var(--fs-tiny);
|
|
5568
|
+
}
|
|
5569
|
+
.ds-247420 .agentchat-earlier-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
5570
|
+
@media (pointer: coarse) {
|
|
5571
|
+
.ds-247420 .agentchat-earlier-btn { min-height: 44px; }
|
|
5572
|
+
}
|
|
5573
|
+
|
|
5574
|
+
/* Inline cwd validation line (checking / error) under the cwd input. */
|
|
5575
|
+
.ds-247420 .agentchat-cwd-hint { font-size: var(--fs-tiny); color: var(--fg-3); }
|
|
5576
|
+
.ds-247420 .agentchat-cwd-hint.is-error { color: var(--flame); }
|
|
5577
|
+
|
|
5578
|
+
/* Dashboard: shared session title heading (same string as the rails). */
|
|
5579
|
+
.ds-247420 .ds-dash-title {
|
|
5580
|
+
font-size: var(--fs-body); font-weight: 600; color: var(--fg);
|
|
5581
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
5582
|
+
}
|
|
5583
|
+
|
|
5584
|
+
/* Stopping state: in-flight cancel reads distinctly from running/error. */
|
|
5585
|
+
.ds-247420 .ds-dash-status.is-stopping { color: var(--amber, #d9a93a); }
|
|
5586
|
+
|
|
5587
|
+
/* External (observed, not owned) session card: no stop control exists. */
|
|
5588
|
+
.ds-247420 .ds-dash-external {
|
|
5589
|
+
padding: 1px 6px; border: var(--bw-hair) solid var(--bg-3); border-radius: var(--r-pill);
|
|
5590
|
+
font-size: var(--fs-micro); color: var(--fg-3); text-transform: uppercase; letter-spacing: .03em;
|
|
5591
|
+
}
|
|
5592
|
+
.ds-247420 .ds-dash-card.is-external { border-style: dashed; }
|
|
5593
|
+
|
|
5594
|
+
/* One connection vocabulary: offline (is-lost kept as legacy alias). */
|
|
5595
|
+
.ds-247420 .ds-dash-stream.is-offline { color: var(--flame); }
|
|
5596
|
+
|
|
5597
|
+
/* ============================================================
|
|
5598
|
+
10th run - Claude-Code-web design-language parity
|
|
5599
|
+
============================================================ */
|
|
5600
|
+
|
|
5601
|
+
/* Flat full-width chat turns (claude.ai/code), replacing the messenger
|
|
5602
|
+
avatar-disc + colored-bubble layout. AgentChat passes flat:true; the demo
|
|
5603
|
+
Chat keeps the bubble layout. Turns are full-width to --measure; the user vs
|
|
5604
|
+
assistant distinction is a role label + a faint assistant background. */
|
|
5605
|
+
.ds-247420 .chat-msg-flat { display: block; padding: var(--space-3) var(--space-4); margin: 0; background: none; border-radius: var(--r-2); }
|
|
5606
|
+
.ds-247420 .chat-msg-flat.them { background: color-mix(in oklab, var(--fg) 3%, transparent); }
|
|
5607
|
+
.ds-247420 .chat-msg-flat.you { background: none; }
|
|
5608
|
+
.ds-247420 .chat-msg-flat .chat-stack { max-width: var(--measure); width: 100%; margin: 0; align-items: stretch; }
|
|
5609
|
+
.ds-247420 .chat-msg-flat.you .chat-stack { align-items: stretch; }
|
|
5610
|
+
.ds-247420 .chat-role { font-size: var(--fs-tiny); font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--fg-3); margin-bottom: var(--space-1); }
|
|
5611
|
+
.ds-247420 .chat-msg-flat.you .chat-role { color: var(--accent); }
|
|
5612
|
+
.ds-247420 .chat-msg-flat .chat-bubble { background: none; border: none; border-radius: 0; padding: 0; max-width: 100%; box-shadow: none; transform: none; }
|
|
5613
|
+
.ds-247420 .chat-msg-flat.you .chat-bubble { background: none; color: inherit; }
|
|
5614
|
+
.ds-247420 .chat-msg-flat:hover { background: color-mix(in oklab, var(--fg) 3%, transparent); }
|
|
5615
|
+
.ds-247420 .chat-msg-flat.you:hover { background: color-mix(in oklab, var(--fg) 2%, transparent); }
|
|
5616
|
+
.ds-247420 .chat-msg-flat:hover .chat-bubble { transform: none; box-shadow: none; background: none; }
|
|
5617
|
+
.ds-247420 .chat-msg-flat .chat-avatar { display: none; }
|
|
5618
|
+
|
|
5619
|
+
/* Tool-call card: a bordered, status-toned, scannable card (claude.ai/code),
|
|
5620
|
+
not the raw mascot-tinted details dump. The node keeps the .chat-bubble
|
|
5621
|
+
class, so these rules de-frame it and restyle. Placed after the flat-bubble
|
|
5622
|
+
reset so they win on equal specificity. */
|
|
5623
|
+
.ds-247420 .chat-msg .chat-tool {
|
|
5624
|
+
display: block; max-width: 100%; padding: 0; margin: var(--space-1) 0;
|
|
5625
|
+
background: var(--bg); color: var(--fg);
|
|
5626
|
+
border: var(--bw-hair) solid var(--rule); border-radius: var(--r-2);
|
|
5627
|
+
box-shadow: none; overflow: hidden;
|
|
5628
|
+
}
|
|
5629
|
+
.ds-247420 .chat-msg .chat-tool[open] { background: var(--bg); }
|
|
5630
|
+
.ds-247420 .chat-msg .chat-tool .chat-tool-head {
|
|
5631
|
+
display: flex; align-items: center; gap: var(--space-2);
|
|
5632
|
+
padding: var(--space-2) var(--space-3); cursor: pointer;
|
|
5633
|
+
font-size: var(--fs-sm); list-style: none;
|
|
5634
|
+
}
|
|
5635
|
+
.ds-247420 .chat-msg .chat-tool .chat-tool-head::-webkit-details-marker { display: none; }
|
|
5636
|
+
.ds-247420 .chat-msg .chat-tool .chat-tool-head:hover { background: var(--bg-2); }
|
|
5637
|
+
.ds-247420 .chat-tool-icon { display: inline-flex; color: var(--fg-3); }
|
|
5638
|
+
.ds-247420 .chat-tool-name { font-family: var(--ff-mono); font-weight: 600; color: var(--fg); }
|
|
5639
|
+
.ds-247420 .chat-tool-label { color: var(--fg-3); font-size: var(--fs-tiny); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
5640
|
+
.ds-247420 .chat-tool-status {
|
|
5641
|
+
margin-left: auto; flex: 0 0 auto;
|
|
5642
|
+
font-size: var(--fs-micro); font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
|
|
5643
|
+
color: var(--fg-3);
|
|
5644
|
+
padding: 2px var(--space-2); border-radius: var(--r-pill);
|
|
5645
|
+
background: color-mix(in oklab, var(--fg) 6%, transparent);
|
|
5646
|
+
}
|
|
5647
|
+
.ds-247420 .chat-msg .chat-tool.tool-running .chat-tool-status { color: var(--accent); background: color-mix(in oklab, var(--accent) 12%, transparent); }
|
|
5648
|
+
.ds-247420 .chat-msg .chat-tool.tool-error .chat-tool-status { color: var(--flame); background: color-mix(in oklab, var(--flame) 12%, transparent); }
|
|
5649
|
+
.ds-247420 .chat-tool-body { border-top: var(--bw-hair) solid var(--rule); padding: var(--space-2) var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); }
|
|
5650
|
+
.ds-247420 .chat-tool-section { display: flex; flex-direction: column; gap: var(--space-1); }
|
|
5651
|
+
.ds-247420 .chat-tool-section-label { font-size: var(--fs-micro); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--fg-3); }
|
|
5652
|
+
.ds-247420 .chat-tool-pre { margin: 0; padding: var(--space-2); background: var(--bg-2); border-radius: var(--r-1); font-family: var(--ff-mono); font-size: var(--fs-tiny); line-height: 1.45; overflow-x: auto; max-height: 320px; overflow-y: auto; }
|
|
5653
|
+
.ds-247420 .chat-tool-pre.is-error { color: var(--flame); }
|
|
5654
|
+
.ds-247420 .chat-tool-pre.chat-tool-empty { color: var(--fg-3); }
|
|
5655
|
+
|
|
5656
|
+
/* Fenced-code language tab on every rendered-markdown block (not just the
|
|
5657
|
+
structured CodeNode). injectCodeCopy reads the language- class. */
|
|
5658
|
+
.ds-247420 .chat-code-block { position: relative; }
|
|
5659
|
+
.ds-247420 .chat-code-lang {
|
|
5660
|
+
position: absolute; top: 0; left: 0; z-index: 2;
|
|
5661
|
+
padding: 2px var(--space-2); border-bottom-right-radius: var(--r-1);
|
|
5662
|
+
font-family: var(--ff-mono); font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: .04em;
|
|
5663
|
+
color: var(--fg-3); background: color-mix(in oklab, var(--fg) 6%, transparent);
|
|
5664
|
+
pointer-events: none;
|
|
5665
|
+
}
|
|
5666
|
+
.ds-247420 .chat-code-block pre { padding-top: calc(var(--space-3) + 4px); }
|
|
5667
|
+
|
|
5668
|
+
/* Motion + microinteraction polish (all reduced-motion guarded). */
|
|
5669
|
+
.ds-247420 .ds-session-row { transition: background var(--dur-snap) var(--ease), box-shadow var(--dur-snap) var(--ease); }
|
|
5670
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
5671
|
+
.ds-247420 .agentchat-thread { scroll-behavior: smooth; }
|
|
5672
|
+
.ds-247420 .ds-alert { animation: ds-alert-in var(--dur-base) var(--ease); }
|
|
5673
|
+
}
|
|
5674
|
+
@keyframes ds-alert-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
|
|
5675
|
+
.ds-247420 .chat-code-copy { transition: opacity var(--dur-snap, .12s) var(--ease, ease), color var(--dur-snap) var(--ease), border-color var(--dur-snap) var(--ease), background var(--dur-snap) var(--ease); }
|
|
5676
|
+
.ds-247420 .chat-msg-action { transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease); }
|
|
5677
|
+
.ds-247420 .agentchat-install-copy { transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease); }
|
|
5678
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
5679
|
+
.ds-247420 .btn.is-armed, .ds-247420 .btn-primary.is-armed { animation: ds-arm-pulse var(--dur-slow) var(--ease); }
|
|
5680
|
+
}
|
|
5681
|
+
@keyframes ds-arm-pulse { 0% { box-shadow: 0 0 0 0 color-mix(in oklab, var(--warn) 55%, transparent); } 100% { box-shadow: 0 0 0 6px color-mix(in oklab, var(--warn) 0%, transparent); } }
|
|
5682
|
+
.ds-247420 .ds-dash-card.is-new { box-shadow: inset 2px 0 0 var(--accent), 0 0 0 1px color-mix(in oklab, var(--accent) 40%, transparent); }
|
|
5683
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
5684
|
+
.ds-247420 .ds-dash-card.is-new { animation: ds-card-in var(--dur-slow) var(--ease); }
|
|
5685
|
+
}
|
|
5686
|
+
@keyframes ds-card-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
|
|
5687
|
+
|
|
5688
|
+
/* Live command-center: status-bucket groups + header breakdown + heartbeat. */
|
|
5689
|
+
.ds-247420 .ds-dash-group { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
5690
|
+
.ds-247420 .ds-dash-group-label { font-size: var(--fs-micro); font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--fg-3); padding: 0 var(--space-1); }
|
|
5691
|
+
.ds-247420 .ds-dash-breakdown { display: flex; align-items: center; gap: var(--space-1); font-size: var(--fs-sm); color: var(--fg-2); }
|
|
5692
|
+
.ds-247420 .ds-dash-breakdown .seg.is-running { color: var(--green); font-weight: 600; }
|
|
5693
|
+
.ds-247420 .ds-dash-breakdown .seg.is-error { color: var(--flame); font-weight: 600; }
|
|
5694
|
+
.ds-247420 .ds-dash-breakdown .seg.is-idle { color: var(--amber, #d9a93a); }
|
|
5695
|
+
.ds-247420 .ds-dash-stream-disc { display: inline-flex; align-items: center; gap: var(--space-1); }
|
|
5696
|
+
.ds-247420 .ds-dash-selectall { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--fs-tiny); color: var(--fg-2); cursor: pointer; background: none; border: none; padding: var(--space-1); }
|
|
5697
|
+
.ds-247420 .ds-dash-selectall:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
5698
|
+
.ds-247420 .ds-dash-clear { background: none; border: none; color: var(--fg-3); cursor: pointer; font-size: var(--fs-tiny); text-decoration: underline dotted; padding: var(--space-1); }
|
|
5699
|
+
|
|
5700
|
+
/* Conversation-rail loading skeleton (cold ccsniff walk). */
|
|
5701
|
+
.ds-247420 .ds-session-row-skeleton { display: flex; flex-direction: column; gap: 6px; padding: var(--space-2) var(--space-3); }
|
|
5702
|
+
.ds-247420 .ds-session-row-skeleton .ds-skel { background: var(--bg-3); border-radius: var(--r-1); }
|
|
5703
|
+
.ds-247420 .ds-session-row-skeleton .ds-skel-title { height: 12px; width: 70%; }
|
|
5704
|
+
.ds-247420 .ds-session-row-skeleton .ds-skel-meta { height: 9px; width: 45%; }
|
|
5705
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
5706
|
+
.ds-247420 .ds-session-row-skeleton .ds-skel { animation: ds-skel-shimmer 1.3s ease-in-out infinite; background: linear-gradient(90deg, var(--bg-3) 0%, var(--bg-2) 50%, var(--bg-3) 100%); background-size: 200% 100%; }
|
|
5707
|
+
}
|
|
5708
|
+
@keyframes ds-skel-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
|
5709
|
+
|
|
5710
|
+
/* File code preview: paint Prism tokens (the bundle scoped them to the chat
|
|
5711
|
+
block only) + an optional non-selectable line-number gutter. */
|
|
5712
|
+
.ds-247420 .ds-preview-code code { color: var(--fg); }
|
|
5713
|
+
.ds-247420 .ds-preview-code .token.comment, .ds-247420 .ds-preview-code .token.prolog, .ds-247420 .ds-preview-code .token.doctype, .ds-247420 .ds-preview-code .token.cdata { color: var(--fg-3); }
|
|
5714
|
+
.ds-247420 .ds-preview-code .token.punctuation { color: var(--fg-2); }
|
|
5715
|
+
.ds-247420 .ds-preview-code .token.property, .ds-247420 .ds-preview-code .token.tag, .ds-247420 .ds-preview-code .token.boolean, .ds-247420 .ds-preview-code .token.number, .ds-247420 .ds-preview-code .token.constant, .ds-247420 .ds-preview-code .token.symbol { color: var(--purple-2, #7F18A4); }
|
|
5716
|
+
.ds-247420 .ds-preview-code .token.selector, .ds-247420 .ds-preview-code .token.attr-name, .ds-247420 .ds-preview-code .token.string, .ds-247420 .ds-preview-code .token.char, .ds-247420 .ds-preview-code .token.builtin { color: var(--green-2, #3A9A34); }
|
|
5717
|
+
.ds-247420 .ds-preview-code .token.atrule, .ds-247420 .ds-preview-code .token.attr-value, .ds-247420 .ds-preview-code .token.keyword { color: var(--sky, #3A6EFF); }
|
|
5718
|
+
.ds-247420 .ds-preview-code .token.function, .ds-247420 .ds-preview-code .token.class-name { color: var(--flame); }
|
|
5719
|
+
.ds-247420 .ds-preview-code.has-gutter { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0 var(--space-2); }
|
|
5720
|
+
.ds-247420 .ds-preview-code.has-gutter code { display: block; min-width: 0; overflow-x: auto; }
|
|
5721
|
+
.ds-247420 .ds-preview-gutter { user-select: none; text-align: right; padding: 0 var(--space-2) 0 0; color: var(--fg-3); border-right: var(--bw-hair) solid var(--rule); font-family: var(--ff-mono); white-space: pre; line-height: inherit; }
|
|
5722
|
+
|
|
5723
|
+
/* Image preview: fit/actual control + alpha checkerboard. */
|
|
5724
|
+
.ds-247420 .ds-preview-media-controls { display: flex; gap: var(--space-2); padding: var(--space-2); justify-content: center; }
|
|
5725
|
+
.ds-247420 .ds-preview-media-alpha {
|
|
5726
|
+
background-image: linear-gradient(45deg, var(--bg-3) 25%, transparent 25%), linear-gradient(-45deg, var(--bg-3) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, var(--bg-3) 75%), linear-gradient(-45deg, transparent 75%, var(--bg-3) 75%);
|
|
5727
|
+
background-size: 16px 16px; background-position: 0 0, 0 8px, 8px -8px, -8px 0;
|
|
5728
|
+
}
|
|
5729
|
+
.ds-247420 .ds-preview-media.is-actual { max-width: none; max-height: none; }
|
|
5730
|
+
|
|
5418
5731
|
/* editor-primitives.css */
|
|
5419
5732
|
/* editor-primitives.css — chrome for in-engine editors, inspectors, IDEs,
|
|
5420
5733
|
debug HUDs. All rules under .ds-247420 scope (build prefixes). Tokens
|