diff --git a/docs/DESIGN-LANGUAGE.md b/docs/DESIGN-LANGUAGE.md index ac305e18..6e0c1df8 100644 --- a/docs/DESIGN-LANGUAGE.md +++ b/docs/DESIGN-LANGUAGE.md @@ -135,12 +135,19 @@ from the active terminal theme (`--bg --fg --accent --red --green --yellow layouts left `--seam-recessed` and joined them (DL-18.6), because two boundaries in one window drawn in two different greys made the quieter one read as an artefact rather than as something somebody chose. - **Amended 2026-09-11, owner:** the pane split alone left `--seam-divider` - for `--seam-split` — 20% of `--tone`, `--hair-strong`'s weight — and went - from 1px to 2px. At 12% and one pixel a grid of panes still read too faint, - while the strip's bottom edge did not, so the split is now the one line - inside the work area drawn heavier than the rest. Its hover and drag accent - is unchanged. + **Superseded for the pane split, 2026-09-18, owner: there is no split line + at all.** The 2026-09-11 amendment had taken it to `--seam-split` (20% of + `--tone`, `--hair-strong`'s weight) at 2px because a grid of panes read too + faint at 12% and one pixel. Panes are now rounded cards separated by the + stage gutter (DL-18.12), so the boundary is a background STEP — the thing the + split never had — and a drawn line beside a gap would read as three surfaces + instead of two. `--seam-split` keeps its 20% and its job — it **moved from + the line between two panes to the 1px edge around each one**, because + `--sidebar-bg` sits only a few luminance units off `--bg` and on a dark theme + the step alone left a card's corner legible and its straight runs invisible + (owner, in the dev app, same day). The divider element remains as the drag + target, transparent until its hover and drag accent, which is unchanged. The + tab strip's bottom edge keeps `--seam-divider` (DL-18.6). `--seam-raised` frames a surface that floats above chrome (popovers, dialogs). **The step must stay louder than the seam that marks it.** Before this rule a @@ -1434,13 +1441,19 @@ window's identity and its actions at the same time. absolute.** A resize seam lights its own hairline in full `--accent` while hovered, and for the whole of a drag — a target that says nothing until the column moves is found by guessing, and the app already answered this for pane - splits (`.split__divider:hover`). Full accent rather than that divider's 60% - mix: a shell seam has a chrome surface on at least one side, where the mix - reads as a lighter hairline instead of a lit line; a split divider stands - between two terminals on `--bg` and keeps its 60%. The line is 1px and sits ON the existing hairline - rather than beside it, so nothing new is drawn; only the colour of a line - that was already there changes, which keeps DL-2.3's one-boundary-one-seam - reading intact. The target itself also widened from 7px to 9px, for a + splits (`.split__divider:hover`). **Full accent, and since 2026-09-18 the + split divider takes the same value:** the 60% mix was justified by a + difference between the two — a shell seam has chrome on one side, a split + stands between two terminals on `--bg` — that DL-18.12 removed, because both + are now a 4px chrome gutter between two `--bg` cards. One idiom, one value. + **What lights is the GUTTER, not a hairline (2026-09-18, owner).** Until then + the lit line was 1px sitting ON the shell's own hairline, so that hovering + changed the colour of a line already there; once the stage stopped drawing + that hairline (DL-18.12) the same placement read as *grabbing the sidebar's + own border*, which is what the owner reported. The grip now fills + `--stage-gutter` at `--radius-flat`, so the bar the user grabs sits between + the two surfaces it moves instead of on the edge of one of them. The target + itself also widened from 7px to 9px, for a reported defect rather than a preference: along a 7px target the pointer crossed the edge repeatedly and the cursor flickered with it. Same figures on the docked column's seam (DL-19.4) — one gesture, one look, both edges. @@ -1559,6 +1572,34 @@ window's identity and its actions at the same time. exceptions until 2026-08-25, when DL-27.3's unread ripple became the third; they remain the scoped yellow exceptions recorded in DL-3.1/DL-3.2. +- **DL-18.12** **A pane is a card on the stage gutter (2026-09-18, owner).** + `--stage-gutter` is 4px and applies to all four edges of the work area AND + between panes ([`06-stage-panes.css`](../src/styles/06-stage-panes.css)); the + corner is 6px, DL-20.1's `--radius-tab` reused rather than a sixth role + opened at a use site. The gutter paints `--sidebar-bg`, the chrome the + navigation column already owns (DL-18.7), because the pane and the stage are + both `--bg` and a radius between two identical surfaces is invisible; + [`derive-colors.test.ts`](../src/lib/derive-colors.test.ts) already + guarantees `sidebarBg ≠ bg` in every theme. **That step is not enough on its + own, and the card carries a 1px `--seam-split` border as well** — the gutter + proved visible and the card's own outline did not. It is a real `border`, not + DL-1.3's permitted inset hairline: xterm fills the padding box and would + paint over an inset line. This reverses the full-bleed grid of + 2026-08-17, whose whole argument — the split line must meet the tab strip's + hairline — dissolves once the split is a gap rather than a line (DL-2.3). The + document surface takes the same gutter and radius so the stage handover does + not change the rectangle's shape; the browser tab is the one accepted + exception, since its web content is a native view the host paints over + [`.browser-panel__view`](../src/browser/browser-panel.tsx) and CSS cannot round + it. 4px and not the old 8px + because a terminal is measured in cells and the earlier gutter cost columns. + **The shell's vertical hairline went with this change.** `.window--sidebar > + .stage`'s `border-left` was the one structural line in the shell while it + separated `--sidebar-bg` from `--bg`; with the gutter painting + `--sidebar-bg` on the stage side, the same 1px ran between two identical + surfaces — a line with no step behind it, which DL-2.3 forbids. The rail and + the gutter are one chrome plane now, and the card's border is the boundary. + ## 19. Docked side panels Added 2026-08-12 for the browser panel, and written to cover the class rather @@ -1730,6 +1771,9 @@ direction token rebuild §9.4 its own. The tight role joined the earlier control/surface pair on 2026-08-16, when surface came down from 16px so the closed scale reads 8/10/12. A value chosen by feel at a use site is not part of this scale; + Amended 2026-09-18 (owner): `--radius-tab`'s membership now also covers the + stage's panes and document surface (DL-18.12), so the 6px corner is a shared + role rather than the strip's private number. `border-radius: 50%` and the 999px capsule stay shapes rather than scale values. - **DL-20.2** One motion pair for chrome state change: `--duration` (150ms) and diff --git a/src/styles/06-stage-panes.css b/src/styles/06-stage-panes.css index 55ccd6e2..c46e53c2 100644 --- a/src/styles/06-stage-panes.css +++ b/src/styles/06-stage-panes.css @@ -1,5 +1,16 @@ /* ── Stage (terminal area) ───────────────────────────────── */ +/* The gutter the pane-card geometry is derived from (owner, 2026-09-18): + 4px on every stage edge AND between panes. On `:root` and not on `.stage` + because `.pane` and `.split__divider` are also mounted outside a stage — the + gallery's seam specimens do exactly that. The matching 6px corner is + DL-20.1's `--radius-tab` spelled at each use site rather than aliased here: + `design-language.test.ts` reads the literal role names out of the + stylesheet, so an alias would read as a radius picked by feel. */ +:root { + --stage-gutter: 4px; +} + .stage { position: relative; min-height: 0; @@ -12,14 +23,20 @@ .stage__tabs { position: absolute; inset: 0; - /* No gutter (owner, 2026-08-17). The 8px this used to carry pushed the pane - grid in from every edge, so the vertical split divider started 8px BELOW - the tab strip's bottom hairline and the two lines never met — a gap nobody - noticed while the divider was 3% and impossible to miss at 12%. Panes are - full-bleed now: the split lines run into the strip's line, and the stage - reads as one ruled surface instead of a card floating in the window. */ - padding: 0; + /* A gutter on all four edges again (owner, 2026-09-18), replacing the + full-bleed grid of 2026-08-17: panes are rounded CARDS floating on the + gutter's chrome, so the stage no longer reads as one ruled surface and the + split lines no longer have to meet the strip's hairline — the whole reason + the 8px gutter was removed. 4px, not the old 8px, so the terminal keeps + its columns. */ + padding: var(--stage-gutter); display: flex; + /* The gutter has to be a DIFFERENT surface from the pane or the radius is + invisible: the pane background and `.stage` are both `--bg`. `--sidebar-bg` + is the chrome the rail already paints (DL-18.7), and + `derive-colors.test.ts` guarantees it never collapses into `--bg` in any + theme. */ + background: var(--sidebar-bg); } /* ── DL-18.6: the stage's tab strip (sidebar layout only) ───────────────── @@ -204,15 +221,34 @@ must still cover the document: the open board (30), Settings (35), the modal tier (40) and the popovers (100+). */ z-index: 20; - /* Whatever gutter `.stage__tabs` gives its panes — none, since 2026-08-17 — - so the document lands on exactly the rectangle the terminal was occupying - instead of shifting by a gutter when the stage changes hands. Both values - move together or the handover jumps. */ - padding: 0; + /* Whatever gutter `.stage__tabs` gives its panes — `--stage-gutter` since + 2026-09-18 — so the document lands on exactly the rectangle the terminal + was occupying instead of shifting by a gutter when the stage changes + hands. Both values move together or the handover jumps, and the gutter + surface has to match for the same reason. */ + padding: var(--stage-gutter); display: flex; min-width: 0; min-height: 0; - background: var(--bg); + background: var(--sidebar-bg); +} + +/* The document is a card on the gutter like a pane is, so the handover does + not change the shape of the rectangle. Named children, not `> *`: the + markdown view-mode toggle is also a direct child and it is a CONTROL, which + keeps its own DL-20.1 role. Radius only, no clipping: Monaco's suggest and + hover widgets live inside the editor and an `overflow` here would cut them + off. */ +.stage__surface > .fileview, +.stage__surface > .browser-panel, +.stage__surface > .agent-board { + border-radius: var(--radius-tab); + /* The pane's edge, for the pane's reason — see `.pane` below. This outranks + `.fileview`'s own `--hair` border (`11-settings-screen.css`) only while + the file view IS the stage's card; inside the settings screen it stays a + line within a surface, which is what `--hair` is for. */ + border: 1px solid var(--seam-split); + box-sizing: border-box; } .stage--strip .stage__surface { @@ -223,11 +259,13 @@ right: var(--dock-w); } -/* The browser variant of the surface: the native view meets the stage's - edges — a web page brings its own margins, unlike the editor the 8px - gutter was measured for. */ +/* The browser variant keeps the gutter the base rule gives it (2026-09-18): + a page floating on the chrome like every other card is worth more than the + few pixels a web page's own margins duplicate. The corners of the web + content stay SQUARE and that is accepted — the content is a native view the + host positions over `.browser-panel__view`, and CSS cannot round it. */ .stage__surface--browser { - padding: 0; + padding: var(--stage-gutter); } /* ── Browser surface: the browser tab's stage cover (DL-18.8) ──────────── @@ -243,6 +281,9 @@ min-width: 0; min-height: 0; background: var(--bg); + /* The radius comes from `.stage__surface > *`; this is what makes the URL + bar's own edges follow it instead of squaring off the top corners. */ + overflow: hidden; } .browser-panel__bar { @@ -318,7 +359,9 @@ inset: 0; display: flex; z-index: 10; - background: var(--bg); + /* The gutter's surface, not `--bg`: the zoomed pane is a card on the same + chrome as an unzoomed one, so its corners still read as corners. */ + background: var(--sidebar-bg); } .tab-stage > * { @@ -365,14 +408,19 @@ transition: none; } -/* A 2px seam between panes — no gap. `--seam-split`, not `--seam-divider`: - the owner asked for a heavier split on 2026-09-11 (DL-2.3), and the strip's - bottom edge keeps the lighter line. The grab zone is widened past the - visible line with a pseudo-element so a thin line stays easy to drag. */ +/* A `--stage-gutter` GAP between panes since 2026-09-18, replacing the 2px + `--seam-split` line of 2026-09-11: two rounded cards separated by the + gutter's chrome need no drawn seam, and a line plus a gap would read as + three surfaces. The divider element stays — it IS the gap, so it keeps + owning the drag — and paints nothing until hover. Its grab zone is still + widened past its own width with a pseudo-element. */ .split__divider { position: relative; - flex: 0 0 2px; - background: var(--seam-split); + flex: 0 0 var(--stage-gutter); + background: transparent; + /* The sidebar grip's shape, for the same reason it has it: a 4px accent + reads as a bar, and a bar takes DL-20.1's 2px detail corner. */ + border-radius: var(--radius-flat); transition: background var(--duration) var(--ease); } @@ -396,9 +444,13 @@ inset: -3px 0; } +/* Full `--accent`, up from a 60% mix (owner, 2026-09-18): the sidebar grip and + this divider are now the same object — a 4px chrome gutter between two + `--bg` cards — and the 60% was justified by a difference between them that + no longer exists (DL-18.9). One idiom, one value. */ .split__divider:hover, .split__divider.is-dragging { - background: color-mix(in srgb, var(--accent) 60%, transparent); + background: var(--accent); } /* While dragging a divider, block mouse events on terminals so text is not selected */ @@ -415,6 +467,11 @@ position: relative; display: flex; flex: 1 1 0; + /* Same corner as the pane it holds (DL-18.12): `pingPane` appends + `.pane-ping` to the SLOT, not to the pane, so the locator's 2px line would + otherwise draw square ends out over the gutter where the pane's curve has + already receded. */ + border-radius: var(--radius-tab); min-width: 0; min-height: 0; overflow: hidden; @@ -430,6 +487,24 @@ overflow: hidden; background: var(--bg); } + /* 6px (owner, 2026-09-18). The existing `overflow: hidden` is what makes it + visible: xterm's canvases fill the pane and would otherwise paint over the + rounded corners, and the same clip keeps `.is-agent-working`'s own + top-edge overlays inside the curve. `.pane-ping` is NOT one of them — it + hangs off `.pane-slot`, which carries the matching radius above. */ + border-radius: var(--radius-tab); + /* The card's own edge, added the same day the gutter was: `--sidebar-bg` is + only a few luminance units off `--bg`, so on a dark theme the gutter alone + left the corner legible and the straight runs invisible — the owner saw + exactly that in the dev app. `--seam-split` is the token the pane boundary + has always owned (DL-2.3); it moved from the line BETWEEN two panes to the + edge AROUND each one, so it is the same 20% of `--tone` doing the same + job at the new geometry. A real border and not `box-shadow: inset` + (DL-1.3's permitted hairline): xterm's canvases fill the padding box and + would paint straight over an inset line, while a border sits outside it. + The 1px costs the terminal nothing — the grid re-fits to the padding box + and a cell is never that small. */ + border: 1px solid var(--seam-split); /* The pane's two yellow signals share one visual language but not one life: a rail click appends `.pane-ping` for a single 1.5s locator; any pane whose diff --git a/src/styles/19-agent-board.css b/src/styles/19-agent-board.css index a0b5e406..95c3e255 100644 --- a/src/styles/19-agent-board.css +++ b/src/styles/19-agent-board.css @@ -11,11 +11,10 @@ are kept, unmatched, as what a revert re-mounts. */ /* The Board's rectangle on the stage is `.stage__surface` itself (spec §4.1), - and that class has carried `padding: 0` since 2026-08-17 — so unlike - `.stage__surface--browser`, which zeroed a gutter that no longer exists, the - Board's modifier needs no declaration of its own. The class stays on the - element as the surface's name in the DOM; a rule that only restated the base - would be dead the day someone tried to change it. */ + and it takes that class's `--stage-gutter` and 6px corner unchanged + (DL-18.12, 2026-09-18) — the Board is a card on the gutter like a pane or a + document. The modifier needs no declaration of its own; the class stays on + the element as the surface's name in the DOM. */ .agent-board { /* DL-34.5 / DL-4.1: the one place chrome is monospace. */