From 8a0fa7388486a6da71b7154e7b6869ce030f11d1 Mon Sep 17 00:00:00 2001 From: Kieran Klukas Date: Mon, 10 Aug 2026 15:21:52 -0400 Subject: [PATCH] tweak: pane header chrome, and expand without covering the rail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expanding now takes the conversation's place rather than the window's: covering everything left the rail underneath with its toggle still on screen and doing nothing visible. Collapse the rail too and the document fills the window. Back is gone and expand takes the left slot, as one button with Maximize2 / Minimize2. The header matches the conversation header's metrics so the two line up across the seam, the documents button hides while the pane is open, and expand hides on the list, where it would mean nothing. AGENTS.md gains the [hidden] gotcha — an author display outranks the attribute's UA display:none, and it always presents as a JS bug. Three instances this week. --- AGENTS.md | 1 + src/client/app.css | 45 +++++++++++++++++++++++++++---------------- src/client/index.html | 8 +++----- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index aa6733e..c0334e5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -74,6 +74,7 @@ The built-in **`echo` model** (`createEchoModel` in `src/providers.ts`) is a det - Job params are parsed in exactly one place (`parseJobParams` in `src/store.ts`); a corrupt row is marked `failed` immediately rather than re-claimed forever. Don't re-parse `row.params` ad hoc. - Delta batching means resume granularity is the batch boundary (`BATCH_MAX_DELTAS` / `BATCH_FLUSH_MS`), not the token. The live tail between flushes is only in memory; that is intentional per the spec's write-amplification recipe. - `GET /api/conversations/:id/events` replays from seq 0 through the actor and will spin up an actor for any id, same as every other route; there is no auth yet (the `TODO(auth)` in `src/http.ts` is the only reference). +- **`[hidden]` loses to any author `display`.** The attribute's `display: none` comes from the UA stylesheet, so a rule like `.split { display: inline-flex }` silently overrides it and `el.hidden = true` does nothing visible. Every element the client toggles via `.hidden` needs a paired explicit rule (`.split[hidden] { display: none }`); `.chatshell[hidden]` documents the same trap. It has bitten this codebase several times and always looks like a JS bug. - The frontend (`src/client/`) is intentionally framework-free vanilla JS with no build step; Bun's HTML route handling does the bundling. Keep it dependency-light (the one vendored lib is `streaming-markdown`) and don't introduce a bundler. ## Style diff --git a/src/client/app.css b/src/client/app.css index f64d3db..478891a 100644 --- a/src/client/app.css +++ b/src/client/app.css @@ -109,7 +109,8 @@ a { min-height: 0; } @media (max-width: 720px) { - .app { + .app, + .app.pane-full { grid-template-columns: 1fr; } } @@ -145,7 +146,8 @@ a.icon { single column — with the rail display:none, a `0 1fr` grid would auto-place `main` into the empty 0 track and squash it to nothing. */ @media (min-width: 721px) { - .app.rail-collapsed { + .app.rail-collapsed, + .app.pane-full.rail-collapsed { grid-template-columns: 1fr; } .app.rail-collapsed .rail { @@ -1663,12 +1665,18 @@ body.selecting #selectBtn { .app.pane-open { grid-template-columns: 244px minmax(0, 1fr) var(--pane-w, 42vw); } -/* Expanded: the document takes the window. Fixed rather than a grid change, so - it doesn't have to negotiate with the rail or the thread. */ +/* Expanded: the document takes the CONVERSATION's place, not the window's. + Covering everything left the rail underneath with its toggle still on screen + and pressing it doing nothing you could see. Now the sidebar keeps working — + collapse it too and the document really does fill the window. */ +.app.pane-full { + grid-template-columns: 244px minmax(0, 1fr); +} +.app.pane-full .main { + display: none; +} .app.pane-full .pane { - position: fixed; - inset: 0; - z-index: 60; + position: static; border-left: 0; } .app.pane-full .paneresize { @@ -1704,11 +1712,14 @@ body.resizing { cursor: col-resize; user-select: none; } +/* Same metrics as the conversation header, so the two line up across the seam + rather than nearly lining up. */ .panehead { display: flex; align-items: center; - gap: 10px; - padding: 10px 12px 10px 16px; + gap: 8px; + padding: 6px 14px; + min-height: 48px; border-bottom: 1px solid var(--rule); } .panetitle { @@ -1816,17 +1827,17 @@ body.resizing { border-radius: 2px; } -.paneback { - width: 28px; - height: 28px; - flex: none; +/* `.icon` sets display, which outranks the hidden attribute's UA display:none. */ +.panefull[hidden] { + display: none; } -.paneback[hidden] { +/* One button, two states: expand while inline, back-to-panel while full. */ +.panefull .ismall, +.app.pane-full .panefull .ifull { display: none; } -.paneback svg { - width: 16px; - height: 16px; +.app.pane-full .panefull .ismall { + display: block; } /* The document list, in the same panel the documents open in. */ .doclist { diff --git a/src/client/index.html b/src/client/index.html index e1ff38a..26b2baa 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -128,14 +128,12 @@
-
- -- 2.51.2