agentgui 1.0.964 → 1.0.966

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.
@@ -0,0 +1,87 @@
1
+ export const meta = {
2
+ name: 'gui-screen-realestate',
3
+ description: 'Drive a maximum-effort SCREEN-REAL-ESTATE + DENSITY sweep of the agentgui GUI toward claude.ai/code: per-lens agents hunt wasted space, low information-density, fixed-width chrome that eats the viewport, and every way the design SDK\'s appearance AS a desktop application can be tightened; an adversarial verifier confirms each gap against the live source (kept-typography guard); a synthesizer orders them into a kit-vs-app-vs-server build plan. The user verdict driving this run: maximize screen real-estate, the layout is still jank, make the SDK look and work like Claude Code\'s web GUI - fully destructive.',
4
+ whenToUse: 'Run after the Claude-Code-web parity sweep (10th run) has landed (flat chat turns, content gutters, tool cards, live command-center). Distinct from all prior workflows: this one hunts SPACE EFFICIENCY - whether the three-column shell wastes the viewport on fixed-width chrome, whether content density matches claude.ai/code (dense, not airy), whether panels resize/collapse to reclaim space, whether the reading measure and grids use freed horizontal space, whether vertical space is fully used, and whether the design SDK reads as a desktop application rather than a webpage. Reference bars: claude.ai/code (primary), cowork, Claude Desktop, c:\\dev\\fsbrowse. All visual/component work lands in c:\\dev\\anentrypoint-design; the app keeps wiring; no new index.html !important overrides; server changes stay confineToRoots-confined.',
5
+ phases: [
6
+ { title: 'Hunt', detail: 'one agent per density/space lens benchmarks the shipped source against the Claude-Code-web density bar' },
7
+ { title: 'Verify', detail: 'adversarially confirm each gap is real, unshipped, and soundly fixable (kept-typography guard)' },
8
+ { title: 'Plan', detail: 'synthesize confirmed gaps into a dependency-ordered kit-vs-app-vs-server build plan' },
9
+ ],
10
+ };
11
+
12
+ const GROUND =
13
+ ' GROUND IN REAL SOURCE (read, do not guess - TEN sweeps HAVE shipped incl the 10th-run Claude-Code-web parity: flat full-width chat turns (.chat-msg-flat), calm rounded-rect composer, bordered tool cards, .ws-main content gutter + mainFlush, folder icons, live command-center with status buckets/breakdown/heartbeat/select-all, motion easing): agentgui front-end c:\\dev\\agentgui\\site\\app\\js\\app.js + backend.js + index.html; kit c:\\dev\\anentrypoint-design\\src\\components\\{shell.js,sessions.js,agent-chat.js,chat.js,files.js,files-modals.js,context-pane.js,content.js,form-primitives.js} + colors_and_type.css + app-shell.css + chat.css; server c:\\dev\\agentgui\\lib\\http-handler.js. KEY current widths: .ws-shell --ws-rail-w 232px, --ws-sessions-w 296px, --ws-pane-w 320px (≈848px of chrome before content); .ws-main padding var(--space-4) var(--space-5); chat thread caps at --measure 68ch; --space scale 4/8/16/24/32px. AGENTS.md + PUNCHLIST-*.md record what already landed - do NOT re-report it; hunt WASTED SPACE and LOW DENSITY.' +
14
+ ' POLICY: fully-destructive run - reshape anything; ALL visual/layout/density work lands in the KIT as defaults/props/CSS; the app keeps state+callback wiring only; NEVER add new index.html !important overrides; use kit design tokens (CSS vars) not raw hex; NO decorative glyphs (ASCII words + CSS .status-dot-disc) but KEEP the typographic set middot/ellipsis/em-en-dash; any server mutation stays confineToRoots realpath-confined; preserve a11y (roles/aria/focus rings/44x44 touch where pointer is coarse/keyboard), responsive behavior (420px to ultrawide, no h-scroll), reduced-motion guards, and explicit empty/loading/error composition. Density must NOT sacrifice readability or a11y minimums.';
15
+
16
+ const LENSES = [
17
+ { key: 'space-efficiency', prompt: 'DENSITY LENS - WASTED SPACE. Hunt every place the GUI wastes pixels vs claude.ai/code (which is dense and calm, not airy). Audit: oversized paddings/margins/gaps (the --space-4/5 gutters, card padding, row heights, composer padding), the fixed-width three-column chrome (rail 232 + sessions 296 + pane 320 ≈ 848px eaten before content on every screen), empty gutters flanking the 68ch chat measure on wide monitors, half-empty panels, low text-density, excessive vertical spacing between turns/rows/sections. For each, the concrete token/CSS change that tightens it WITHOUT harming readability or a11y. Name the highest-waste offenders first.' + GROUND },
18
+ { key: 'viewport-utilization', prompt: 'DENSITY LENS - VIEWPORT UTILIZATION + COLLAPSE/RESIZE. The shell must let the user reclaim the viewport. Audit: are the rail/sessions/pane columns collapsible AND resizable (drag handles, persisted)? does the shell default to too much chrome on first load? on a wide monitor is the content area maximized or pinched between fixed panels? is there a thin-rail (icon-only) default like claude.ai/code? can the user hide sessions+pane to get a full-width thread/grid? are breakpoints right (when should a column auto-collapse)? does any column have a min-width that forces h-scroll on a small laptop? Name each missing collapse/resize affordance with the concrete shell.js prop + CSS (resize handle, persisted width, keyboard-accessible).' + GROUND },
19
+ { key: 'information-density', prompt: 'DENSITY LENS - INFORMATION PER SCREEN. Measure useful content per screen against claude.ai/code. Audit each surface: chat (how many turns visible at once; is the inter-turn spacing too generous?), files (how many rows/items visible; is the row height padded?), live dashboard (how many session cards at once; could a denser card or a compact list mode show more?), history events (event row density), settings (is it a sparse single column when a denser two-column grid fits?), context pane (wasted vertical space?). Propose a compact density tier (data-density=compact reducing the --space scale + row heights + paddings) and per-surface density wins. Name each low-density surface with the concrete fix.' + GROUND },
20
+ { key: 'adaptive-layout', prompt: 'DENSITY LENS - ADAPTIVE / RESPONSIVE LAYOUT. The layout must adapt to viewport width to use space well at every size. Audit: do files/live grids scale column COUNT with width (auto-fill minmax) or stay single-column wasting wide screens? does the chat layout adapt (wider measure / side-by-side context on ultrawide, single column on narrow)? are there density tiers by breakpoint (more columns + tighter spacing as width grows)? does the shell rearrange sensibly from phone -> laptop -> ultrawide (drawers -> columns -> multi-pane)? is there any fixed px that should be a clamp()/minmax()/fr? Name each non-adaptive layout with the concrete responsive CSS (grid auto-fill, clamp, container/media queries).' + GROUND },
21
+ { key: 'application-chrome', prompt: 'DENSITY LENS - DESIGN SDK AS A DESKTOP APPLICATION. The user wants the SDK to look/work like a desktop app (Claude Desktop), not a webpage. Audit the SDK appearance AS an application: full-bleed app shell (no body margins/scroll, 100vh/100dvh lock, content scrolls internally), a cohesive top band (crumb/titlebar) that reads like app chrome, consistent focus-visible discipline, no webpage-y default link/button styling leaking through, coherent elevation/surface layering that reads as panels in an app, a polished rail that reads as an app sidebar, and any other way the kit\'s appearance as a standalone application can be improved. Hunt webpage-tells (body scroll, full-page reflow, max-width content blocks centered like an article, default form chrome). Name each with the concrete kit fix.' + GROUND },
22
+ { key: 'finish-pending', prompt: 'DENSITY LENS - FINISH THE CARRIED-OVER FILE-MANAGER WORK (10th-run pending). Two rows are pending and directly serve density + the fsbrowse-grade file view: (1) Files MULTI-SELECT - kit FileGrid {selectable, marked:Set, onToggleMark, onRangeSelect, onSelectAll} rendering a native .ds-file-check button + a BulkBar component (N selected, clear, delete-selected, move-selected); app state.files.marked Set + shift-range + Ctrl/Cmd+A + the bulk delete loop; a confineToRoots-safe server POST /api/move-confined for drag-to-move (CSRF-guarded, validate-mutations rerun). (2) DENSITY-THUMBNAIL GRID - wire the FileGrid columns prop + a list/grid SegmentedControl, grid mode rendering image thumbnails via the confined /api/image. (3) F3 ELEVATION UNIFY - standardize app cards/panels on bg-2 + hairline var(--rule) + r-2, drop drop-shadows on .ds-dash-card/.ds-context .panel/.ds-file-grid. Read the REAL current files.js/files-modals.js/app.js/http-handler.js and emit the concrete, source-grounded implementation for each, split kit-vs-app-vs-server.' + GROUND },
23
+ ];
24
+
25
+ const GAP_SCHEMA = {
26
+ type: 'object',
27
+ required: ['lens', 'gaps'],
28
+ properties: {
29
+ lens: { type: 'string' },
30
+ gaps: {
31
+ type: 'array',
32
+ items: {
33
+ type: 'object',
34
+ required: ['title', 'where', 'fixLocation', 'fix', 'severity'],
35
+ properties: {
36
+ title: { type: 'string', description: 'the wasted-space / low-density / non-app-like gap' },
37
+ where: { type: 'string', description: 'file:line or component (or "absent")' },
38
+ fixLocation: { type: 'string', enum: ['kit', 'app', 'server', 'kit+app', 'app+server', 'kit+app+server'] },
39
+ fix: { type: 'string', description: 'the concrete change: token/css/component/prop/wiring/endpoint' },
40
+ spaceReclaimed: { type: 'string', description: 'roughly how much space/density this reclaims' },
41
+ severity: { type: 'string', enum: ['high', 'medium', 'low'] },
42
+ keptTypography: { type: 'boolean', description: 'true if this touches the middot/ellipsis/dash set - must NOT be flagged as a glyph violation' },
43
+ },
44
+ },
45
+ },
46
+ },
47
+ };
48
+
49
+ const VERDICT_SCHEMA = {
50
+ type: 'object',
51
+ required: ['title', 'real', 'reason'],
52
+ properties: {
53
+ title: { type: 'string' },
54
+ real: { type: 'boolean', description: 'true if the gap genuinely exists in shipped source, is unshipped, the fix is sound, lands in the right place, uses tokens, and preserves a11y/readability/responsive' },
55
+ reason: { type: 'string', description: 'why - cite the source you read' },
56
+ },
57
+ };
58
+
59
+ phase('Hunt');
60
+ const verified = await pipeline(
61
+ LENSES,
62
+ (l) => agent(l.prompt, { label: 'hunt:' + l.key, phase: 'Hunt', schema: GAP_SCHEMA }),
63
+ (report, l) => parallel((report?.gaps || []).map((g) => () =>
64
+ agent(
65
+ 'Adversarially verify this screen-real-estate / density / SDK-as-application finding against the REAL source (read the cited files - do not trust the claim). '
66
+ + 'KEPT-TYPOGRAPHY GUARD: middot, ellipsis, em/en-dash are intentional product typography - flagging one as a decorative glyph is NOT a real finding (real=false). '
67
+ + 'A finding is real only if the gap genuinely exists in the shipped source AND was not already fixed by a prior sweep (check the PUNCHLIST-*.md files / AGENTS.md) AND the fix is sound, lands in the right place (visual/layout in the KIT not the app), uses tokens not hex, and preserves a11y minimums + readability + responsive (no new h-scroll) + reduced-motion. '
68
+ + 'Reject density changes that would harm readability/touch-targets/contrast, and reject vague taste-only findings with no concrete source-grounded fix. '
69
+ + 'Finding: ' + JSON.stringify(g),
70
+ { label: 'verify:' + (g.title || '').slice(0, 32), phase: 'Verify', schema: VERDICT_SCHEMA }
71
+ ).then((v) => ({ ...g, lens: report.lens, verdict: v }))
72
+ ))
73
+ );
74
+
75
+ const confirmed = verified.flat().filter(Boolean).filter((g) => g.verdict && g.verdict.real);
76
+ log('confirmed ' + confirmed.length + ' real screen-real-estate / density gaps across ' + LENSES.length + ' lenses');
77
+
78
+ phase('Plan');
79
+ const plan = await agent(
80
+ 'Synthesize these adversarially-confirmed screen-real-estate / density / SDK-as-application gaps into a dependency-ordered build plan for the anentrypoint-design kit + agentgui app + server. '
81
+ + 'Order: server changes first (if any), then kit token/density foundations (the compact tier + column widths + responsive grid primitives), then kit components (each edited ONCE with all changes batched), then app wiring, then the single build->vendor->witness step. '
82
+ + 'Group by component so each kit file is touched once; call out which gaps share a file (batch), which are independent (parallelizable), which conflict, and the highest-leverage few that most reclaim screen real-estate. '
83
+ + 'Confirmed gaps: ' + JSON.stringify(confirmed),
84
+ { label: 'synthesize-plan', phase: 'Plan' }
85
+ );
86
+
87
+ return { confirmed, plan, lensCount: LENSES.length, confirmedCount: confirmed.length };
package/AGENTS.md CHANGED
@@ -35,6 +35,14 @@ Lands the 10th run's deferred batch + the janks a live witness surfaced on this
35
35
 
36
36
  **Env/tooling.** `scripts/validate-mutations.mjs` is now portable (ROOT from env/cwd, sep-aware joins, posix traversal targets, Bearer from PASSWORD; csrf case expects 401 under PASSWORD / 403 open) - 17/17 PASS on this env. Browser witnessing here: plugkit's chromium needs `--no-sandbox` (written to `/etc/chromium.d/no-sandbox`); `page.goto` waits on hung CDN subresources so use `waitUntil:'commit'` + `waitForSelector`; the witness body is shell-interpolated (NEVER use `$(...)`/backticks - use `page.locator(...)`); same-URL `goto` with only a hash change does NOT reload (use `page.reload()` after re-vendoring).
37
37
 
38
+ ## GUI screen-real-estate layer (2026-06-12) — fluid columns + drag-resize handles
39
+
40
+ An additive viewport-density layer on top of the concurrent 11th/12th-run work (the user verdict: maximize screen real-estate, the SDK must read as a desktop application). New workflow `.claude/workflows/gui-screen-realestate.js` (6 density lenses: space-efficiency, viewport-utilization, information-density, adaptive-layout, application-chrome, finish-pending). 50 agents -> 17 confirmed gaps (`PUNCHLIST-DENSITY.md`). Kit pushed `anentrypoint-design` 97dc646.
41
+
42
+ **The shell reclaims the viewport (genuinely-new, complementary to the 12th run's breakpoint column-yield).** Fixed `--ws-rail-w 232 / --ws-sessions-w 296 / --ws-pane-w 320` (≈848px chrome) became fluid clamps (`clamp(200px,16vw,260px)` etc) so chrome scales with the viewport. Kit `shell.js` renders keyboard+pointer `.ws-resizer` separators (`role=separator`, ArrowLeft/Right 16px, pointer-drag) between each track that write a clamped inline `--ws-<col>-w` (inline overrides the clamp base, pinning the width) persisted to `localStorage ds.ws.w.<col>` and seeded on mount via a `ref` (`seedWsWidths`, no app wiring). Sessions gained a desktop collapse toggle (`.ws-sessions-toggle` in the crumb; `.ws-desktop-toggle` CSS = inverse of the mobile drawer-toggle; `toggleWs` extended with `'sessions' -> ws-sessions-collapsed`). `.btn*` moved off `--r-pill` to `--r-1` (8/14 pad, 32 min-h, 44 coarse floor); `.app-status` is a thin 26px ellipsized strip (`--app-status-h:26px`); `.chat-msg-flat` centers at `--measure` (`max-width:var(--measure);margin-inline:auto`) + density-scaled spacing via the `--density` token + 84ch ultrawide tier; `.ds-dash-grid` densified (minmax 240, 220 wide tier).
43
+
44
+ **Concurrent-writer reconciliation (load-bearing — both repos have active concurrent writers).** While this run was in flight, concurrent writers shipped the ENTIRE Files multi-select + density-thumbnail + move feature on BOTH `anentrypoint-design/main` (FileGrid `selected/onToggleSelect/onSelectAll/onClearSelection/density/onDensity/thumbUrl` + `BulkBar` export) AND `agentgui/main` (the 11th/12th runs: `onMark`/`density` wiring + `B.moveEntry` -> confined `POST /api/move` + bulk move). So this run's parallel multi-select/move/F3 work was DROPPED and theirs adopted; the only thing landed on agentgui is the re-vendored kit dist (the screen-real-estate layer above) + this workflow + docs. **Rule: when origin advances mid-run with the same feature, drop yours and adopt theirs, then re-apply only your genuinely-distinct work onto their current files — never force a parallel implementation; the agentgui `/api/move-confined` endpoint built this run was redundant with their `/api/move` and discarded.** The kit ships 4 build lints (`lint-tokens/glyphs/null-children/classes`) - all must pass. Witness localhost:3009/gm/ (3000 owned by another app): the vendored bundle is served fresh only after a SERVER restart (it pre-warms statics in memory) AND a fresh browser context (the persistent witness context caches the ES module - clear it or new-session), else you read a stale bundle. Witnessed 0 console errors chat/files, btn 10px, status 26px, 3 resizers, sessions toggle, fluid rail 256@1600, no h-scroll.
45
+
38
46
  ## GUI Claude-Code-web parity sweep (2026-06-11) — tenth maximum-effort run
39
47
 
40
48
  Hunts the VISUAL + LAYOUT + MOTION design-language parity gap vs claude.ai/code (the user verdict: too primitive, layout jank). New workflow `.claude/workflows/gui-claude-code-parity.js` (6 design-language lenses: visual-language, layout-composition, chat-thread-craft, file-browser-fidelity, live-session-command, motion-microinteraction; hunt -> adversarial verify (kept-typography guard) -> plan). 66 agents -> 37 confirmed gaps (`PUNCHLIST-PARITY.md`). Pushed: kit `anentrypoint-design` 28387c3, agentgui 0a93fc6.
@@ -249,4 +257,3 @@ GUI source keeps typographic product characters — the middot separator `·`, t
249
257
 
250
258
  **`index.html` loads the DS kit from the local `./vendor/anentrypoint-design/247420.{js,css}`, NOT unpkg.** The prior "moved to unpkg @latest" note described a transient state that was reverted: the kit was re-vendored (with `AgentChat`) in `eb1eab3` and witnessed loading from `./vendor/` again (2026-06-04 and 2026-06-05 runs). The vendored copy IS the shipped UI — predictable, no upstream-publish drift. Update flow: edit the kit at `c:\dev\anentrypoint-design`, `node scripts/build.mjs`, copy `dist/247420.{js,css}` into `site/app/vendor/anentrypoint-design/`, browser-witness, then push the kit so unpkg stays in sync for other consumers. (The markdown stack marked/dompurify/prismjs still fetches from jsdelivr on first chat render.)
251
259
 
252
- @.gm/next-step.md
@@ -0,0 +1,33 @@
1
+ # PUNCHLIST-DENSITY — 11th run: screen-real-estate + density + SDK-as-application
2
+
3
+ Workflow `.claude/workflows/gui-screen-realestate.js` (6 lenses: space-efficiency, viewport-utilization, information-density, adaptive-layout, application-chrome, finish-pending). 50 agents -> 17 confirmed gaps. Verdict: the shell spends ~848px (rail 232 + sessions 296 + pane 320) of fixed chrome before content, the chat thread pins left dumping a one-sided gutter on wide screens, surfaces are airy (not Claude-Code-dense), nothing resizes, and webpage-tells remain (pill buttons, 42px status footer).
4
+
5
+ ## Highest-leverage
6
+ 1. Shell column resize handles (G2) — up to ~250px reclaimable
7
+ 2. Sessions desktop collapse toggle (G3) — full 296px reclaim
8
+ 3. Files density-thumbnail grid (G11) — ~4x files per viewport
9
+ 4. Files multi-select + bulk ops (G9) — N dialogs -> one batch pass
10
+
11
+ ## Phase 0 — server
12
+ - S1 `lib/http-handler.js`: `POST /api/move-confined {path,destDir}` mirroring /api/rename guard (confineToRoots both, refuse allow-root src, 409 on exists, EACCES/EPERM->403 ENOENT->404, global CSRF covers it)
13
+ - S2 `scripts/validate-mutations.mjs`: add move case
14
+ - S3 `backend.js`: `moveConfined(base,filePath,destDir)` stub
15
+
16
+ ## Phase 1 — kit CSS
17
+ - `app-shell.css` (one edit): G8 fluid clamps `--ws-rail-w clamp(200,16vw,260)` / sessions `clamp(260,22vw,360)` / pane `clamp(300,24vw,420)`; G2 `.ws-resizer`; G3 `.ws-sessions-collapsed{--ws-sessions-w:0}`; G6 laptop-band pane->drawer at 1100px (transform drawer, not display:none); G13 status bar `--app-status-h:24px`; G14 `.btn*` radius `--r-1` padding `8px 14px` min-height 32 (44 coarse floor kept); G15 `.panel{box-shadow:none}`
18
+ - `chat.css` (one edit): G1/G5 `.chat-msg-flat{max-width:var(--measure);margin-inline:auto}`; G7 thread spacing via `calc(token*var(--density))`; G4/G8 `.ds-dash-grid` minmax(240) gap space-2 padding space-3; G10-tier `@media(min-width:1500){minmax(220) gap space-2}`; G12 `@media(min-width:1600){.agentchat-thread{--measure:84ch}}`; G15 `.ds-dash-card`+`.ds-context .panel` border `--bg-3`->`--rule`; `.ds-file-thumb`
19
+ - `colors_and_type.css`: confirm `--density` exists (it does: compact .75 / comfortable 1 / spacious 1.35) — no edit
20
+
21
+ ## Phase 2 — kit components
22
+ - `shell.js`: G2 resizers (role=separator, ArrowLeft/Right 16px, persist ds.ws.w.<col>) + G3 sessions toggle (extend toggleWs map, persist ds.ws.sessions)
23
+ - `files.js`: G9 FileRow `{selectable,marked,onToggleMark}` native .ds-file-check + FileGrid `{marked,onSelectAll,onClearSelection}` + BulkBar export + Ctrl/Cmd+A/Escape; G10 FileRow `{draggable,onDragStartFile,onDropOnDir}` + BulkBar move…; G11 `{view,onView}` segmented + image thumb swap in grid mode
24
+
25
+ ## Phase 3 — app
26
+ - app.js: persisted widths seed; state.files.marked Set + shift-range over _sorted + bulk-delete loop; state.files.view persisted + columns/thumbSrc; onDropOnDir -> B.moveConfined
27
+ - backend.js: moveConfined
28
+
29
+ ## Phase 4 — build/vendor/witness/push
30
+ build.mjs -> vendor -> witness localhost/gm/ (0 errors, thread centered, resizer+persist, sessions-collapse, status 24px, rounded buttons, grid thumbnails, multi-select+bulk+drag, no h-scroll 420/1024/2560, glyph 0) -> validate-mutations 18 -> push kit+app.
31
+
32
+ ## Dedupes / conflicts
33
+ G1==G5; G2-dash==G4==G8-dash (one metric); G12-pane folds into G8 clamp. Conflict: G2 inline-prop drag overrides G8 clamp base (inline > stylesheet, desired) — widen JS clamps to cover ceilings. G9/G10/G11 CSS already ships as dead CSS (only .ds-file-thumb is new).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.964",
3
+ "version": "1.0.966",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
@@ -624,7 +624,7 @@
624
624
  .ds-247420 body.canvas-host { background: transparent !important; }
625
625
 
626
626
  .ds-247420 {
627
- --app-status-h: var(--size-base);
627
+ --app-status-h: 26px;
628
628
  --app-topbar-h: var(--size-lg);
629
629
  --app-crumb-h: var(--size-sm);
630
630
  }
@@ -818,14 +818,15 @@
818
818
  .ds-247420 .app-status {
819
819
  display: flex; align-items: center; gap: var(--space-3);
820
820
  width: 100%;
821
- min-height: var(--app-status-h);
822
- padding: 10px var(--pad-x);
821
+ height: var(--app-status-h); min-height: var(--app-status-h);
822
+ /* A thin desktop status strip, not a webpage footer band. */
823
+ padding: 0 var(--space-4);
823
824
  /* Status chrome is prose, not code - mono is reserved for code/paths. */
824
- font-family: var(--ff-body); font-size: var(--fs-xs);
825
+ font-family: var(--ff-body); font-size: var(--fs-xs); line-height: 1.2;
825
826
  color: var(--fg-3);
826
827
  border-top: 1px solid var(--rule);
827
828
  }
828
- .ds-247420 .app-status .item { color: inherit; white-space: nowrap; }
829
+ .ds-247420 .app-status .item { color: inherit; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
829
830
  .ds-247420 .app-status .item:first-of-type { color: var(--accent); }
830
831
  .ds-247420 .app-status .spread { flex: 1; }
831
832
  /* The status bar NEVER wraps: at narrow widths the trailing items (hints,
@@ -840,13 +841,14 @@
840
841
  Primitives
841
842
  ============================================================ */
842
843
  .ds-247420 .btn, .ds-247420 .btn-primary, .ds-247420 .btn-ghost {
843
- display: inline-flex; align-items: center; gap: 8px;
844
- padding: 13px 22px;
844
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
845
+ padding: 8px 14px; min-height: 32px;
845
846
  font-family: var(--ff-body);
846
847
  font-weight: 600; font-size: var(--fs-sm);
847
848
  text-decoration: none;
848
849
  cursor: pointer;
849
- border-radius: var(--r-pill);
850
+ /* Rounded-rect app chrome, not a marketing-page stadium CTA. */
851
+ border-radius: var(--r-1);
850
852
  border: 0;
851
853
  transition: transform var(--dur-snap) var(--ease), background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
852
854
  }
@@ -3432,10 +3434,13 @@
3432
3434
  All sizing/colour from kit tokens; no hardcoded palette.
3433
3435
  ---------------------------------------------------------------------------- */
3434
3436
  .ds-247420 .ws-shell {
3435
- --ws-rail-w: 232px;
3437
+ /* Fluid chrome columns: shrink on small laptops, breathe on ultrawide, so the
3438
+ ~848px of fixed chrome no longer eats a constant slab. An inline --ws-*-w
3439
+ written by a resize drag overrides these (inline wins), pinning the width. */
3440
+ --ws-rail-w: clamp(200px, 16vw, 260px);
3436
3441
  --ws-rail-w-collapsed: 60px;
3437
- --ws-sessions-w: 296px;
3438
- --ws-pane-w: 320px;
3442
+ --ws-sessions-w: clamp(248px, 22vw, 360px);
3443
+ --ws-pane-w: clamp(288px, 24vw, 420px);
3439
3444
  display: grid;
3440
3445
  grid-template-columns: var(--ws-rail-w) var(--ws-sessions-w) 1fr var(--ws-pane-w);
3441
3446
  grid-template-areas: "rail sessions content pane";
@@ -3456,6 +3461,21 @@
3456
3461
  .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"; }
3457
3462
  .ds-247420 .ws-shell.ws-rail-collapsed { --ws-rail-w: var(--ws-rail-w-collapsed); }
3458
3463
  .ds-247420 .ws-shell.ws-pane-collapsed { --ws-pane-w: 0px; }
3464
+ /* Sessions column desktop collapse (parity with the pane collapse), reclaiming
3465
+ its width for a full-width thread/grid. */
3466
+ .ds-247420 .ws-shell.ws-sessions-collapsed { --ws-sessions-w: 0px; }
3467
+ .ds-247420 .ws-sessions-collapsed .ws-sessions { overflow: hidden; border-right: none; }
3468
+ .ds-247420 .ws-sessions-collapsed .ws-sessions > * { display: none; }
3469
+ /* Column resize handles: a thin keyboard-accessible separator pinned to the
3470
+ right edge of each chrome track; drag (or ArrowLeft/Right) writes a clamped
3471
+ inline --ws-*-w. */
3472
+ .ds-247420 .ws-resizer { grid-row: 1 / -1; align-self: stretch; justify-self: end; width: 8px; margin-right: -4px; z-index: 5; cursor: col-resize; background: transparent; border: none; padding: 0; touch-action: none; }
3473
+ .ds-247420 .ws-resizer::after { content: ""; display: block; width: 2px; height: 100%; margin: 0 auto; background: transparent; transition: background var(--dur-snap) var(--ease); }
3474
+ .ds-247420 .ws-resizer:hover::after, .ds-247420 .ws-resizer:focus-visible::after { background: var(--accent); }
3475
+ .ds-247420 .ws-resizer:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
3476
+ .ds-247420 .ws-resizer.ws-resizer-rail { grid-column: 1; }
3477
+ .ds-247420 .ws-resizer.ws-resizer-sessions { grid-column: 2; }
3478
+ .ds-247420 .ws-resizer.ws-resizer-pane { grid-column: 3; justify-self: end; }
3459
3479
  /* Ease the rail/pane collapse. Track COUNT stays stable on collapse (only a
3460
3480
  width var flips), so grid-template-columns is animatable; reduced-motion
3461
3481
  drops it to an instant swap. */
@@ -3570,6 +3590,11 @@
3570
3590
  trailing display:none silently kills every drawer affordance (shipped bug:
3571
3591
  the mobile drawers were dead because these lines used to sit last). */
3572
3592
  .ds-247420 .ws-drawer-toggle { display: none; align-items: center; justify-content: center; width: 40px; height: 40px; border: none; background: none; color: var(--fg-2); cursor: pointer; border-radius: var(--r-1); }
3593
+ /* Desktop-only chrome toggle (sessions collapse): inverse of the mobile drawer-toggle. */
3594
+ .ds-247420 .ws-desktop-toggle { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: none; background: none; color: var(--fg-2); cursor: pointer; border-radius: var(--r-1); }
3595
+ .ds-247420 .ws-desktop-toggle:hover { background: var(--bg-2); color: var(--fg); }
3596
+ .ds-247420 .ws-desktop-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
3597
+ @media (max-width: 900px) { .ds-247420 .ws-desktop-toggle { display: none; } }
3573
3598
  .ds-247420 .ws-scrim { display: none; }
3574
3599
 
3575
3600
  /* Responsive: the columns yield to the CONTENT in stages - the main column is
@@ -5378,7 +5403,7 @@
5378
5403
  overflow-y: auto;
5379
5404
  display: flex;
5380
5405
  flex-direction: column;
5381
- gap: var(--space-2, 8px);
5406
+ gap: calc(var(--space-2, 8px) * var(--density, 1));
5382
5407
  }
5383
5408
 
5384
5409
  /* Jump-to-latest: hidden until the thread scroll listener adds .show. */
@@ -5644,7 +5669,8 @@
5644
5669
  background: color-mix(in oklab, var(--warn) 12%, transparent); color: var(--warn);
5645
5670
  }
5646
5671
  .ds-247420 .ds-dash-header .btn-primary.danger.is-armed { background: var(--warn); color: var(--paper); border-color: var(--warn); }
5647
- .ds-247420 .ds-dash-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--space-3); padding: var(--space-4); }
5672
+ .ds-247420 .ds-dash-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--space-2); padding: var(--space-3); }
5673
+ @media (min-width: 1500px) { .ds-247420 .ds-dash-grid { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); } }
5648
5674
  .ds-247420 .ds-dash-card { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-3); background: var(--bg-2); border: var(--bw-hair) solid var(--bg-3); border-radius: var(--r-2); }
5649
5675
  .ds-247420 .ds-dash-card.is-error { border-color: var(--flame); }
5650
5676
  .ds-247420 .ds-dash-card-head { display: flex; align-items: center; gap: var(--space-2); }
@@ -5939,7 +5965,10 @@
5939
5965
  avatar-disc + colored-bubble layout. AgentChat passes flat:true; the demo
5940
5966
  Chat keeps the bubble layout. Turns are full-width to --measure; the user vs
5941
5967
  assistant distinction is a role label + a faint assistant background. */
5942
- .ds-247420 .chat-msg-flat { display: block; padding: var(--space-3) var(--space-4); margin: 0; background: none; border-radius: var(--r-2); }
5968
+ /* Center the flat turn at the reading measure so a wide content column does not
5969
+ dump a one-sided dead gutter (claude.ai/code centers the thread). Padding
5970
+ scales through --density so data-density=compact tightens the thread. */
5971
+ .ds-247420 .chat-msg-flat { display: block; max-width: var(--measure); margin-inline: auto; padding: calc(var(--space-3) * var(--density, 1)) var(--space-4); margin-block: 0; background: none; border-radius: var(--r-2); }
5943
5972
  .ds-247420 .chat-msg-flat.them { background: color-mix(in oklab, var(--fg) 3%, transparent); }
5944
5973
  .ds-247420 .chat-msg-flat.you { background: none; }
5945
5974
  .ds-247420 .chat-msg-flat .chat-stack { max-width: var(--measure); width: 100%; margin: 0; align-items: stretch; }
@@ -6095,6 +6124,11 @@
6095
6124
  .ds-247420 .row-act, .ds-247420 .ds-dash-errors-toggle, .ds-247420 .agentchat-export-act, .ds-247420 .ds-file-more-btn, .ds-247420 .agentchat-install-copy, .ds-247420 .ds-upload-act, .ds-247420 .ds-session-meta-copy { min-height: 44px; }
6096
6125
  }
6097
6126
 
6127
+ /* 11th run: ultrawide widens the reading measure where there is room. */
6128
+ @media (min-width: 1600px) {
6129
+ .ds-247420 .agentchat-thread { --measure: 84ch; }
6130
+ }
6131
+
6098
6132
  /* editor-primitives.css */
6099
6133
  /* editor-primitives.css — chrome for in-engine editors, inspectors, IDEs,
6100
6134
  debug HUDs. All rules under .ds-247420 scope (build prefixes). Tokens