@@ -249,56 +249,78 @@ settings and the picker established.
249249
250250### The app shell
251251
252- ` layout.go ` (` renderFrame ` + ` shell.ctml ` ) closes the migration at the
253- outermost layer: the permanent frame every primary panel renders inside
254- — tab strip, session strip, the active panel/overlay body, footer key
255- hints, all inside one rounded border.
256-
257- - ** A ` <layout variant="HF"> ` band-split, not a full HLCRF page.** The
258- header (tab strip + session strip, already joined and each already
259- fitted to the frame's inner width) and the footer (status + key hints,
260- likewise pre-fitted) ride ` <verbatim> ` in the H and F slots;
261- ` renderBandFrame ` (the same HF idiom overlays and the Data list's live
262- filter input already use) splits the render at the H slot's own
263- recorded box height, and the host composes the region — the active main
264- panel, plus the wide/toggled inspector, ` renderWorkspaceRegion ` —
265- between the two bands, exactly as a widget-carrying overlay composes a
266- live Bubbles widget between its own bands.
267- - ** The Content slot is a dead end for full-width verbatim.** Unlike
268- Header/Footer/Sidebar/Aside, ` renderTermContent ` hardcodes a ` (0,1) `
269- padding with no ` TermTheme ` override; content already fitted to the
270- slot's own width arrives one column too wide once that padding lands,
271- and the slot's own ` Width() ` enforcement word-wraps the overflow onto a
272- spurious extra row instead of leaving it byte-exact. ` renderTermBox `
273- (Sidebar/Aside — L and R) carries the same defect through its own
274- hardcoded ` -4 ` /` -2 ` offsets. H and F have no such tax — both route
275- through nothing but the (fully themeable, here stripped-to-blank)
276- ` theme.Header ` /` theme.Footer ` style — so the region stays host-composed
277- and rides between the bands rather than through any middle-band slot.
278- ` wideInspectorWidth ` , ` measureFrame ` 's per-` layoutKind ` maths, and
279- ` renderWorkspaceRegion ` 's wide/compact/narrow arrangements are therefore
280- unchanged: none of it is a rendering-composition concern .ctml can take
281- over, since the one slot that could hold it corrupts pre-fitted content,
282- and the arithmetic itself has nowhere to live in a language with no
283- operators (docs/ctml.md S:8.4).
284- - ** Mouse hit-testing needed no re-plumbing.** ` onMouse ` resolves against
285- its own independent ` renderPanelBarBoxes ` call, keyed off
286- ` frameInsetRows ` /` frameInsetCols ` and the tab strip's row-0 position —
287- both unchanged by this slice, since the tab strip's own render path
288- (` renderPanelBar ` , ` tabs.ctml ` ) is untouched; it now merely arrives at
289- the shell as a pre-rendered value instead of a ` JoinVertical ` argument.
290- - ** The chat transcript stays hand-composed.** ` renderTranscript ` 's
291- turn-by-turn Glamour composition is a list whose row COUNT is unbounded
292- and whose per-row body is pre-styled ANSI — exactly the ` <each> ` +
293- per-row ` <verbatim> ` shape a transcript would need — but `<verbatim
294- value="key">` resolves ` key` once against the document-wide
295- ` Bindings.Values ` at PARSE time (` ctml/parse.go ` 's ` parseVerbatim ` ), not
296- per row at render time the way a ` {{path}} ` bind does; every row inside
297- an ` <each> ` would render the identical fixed content. Converting the
298- transcript would need either an unbounded flat list of named verbatim
299- slots (impractical) or row-scoped verbatim resolution go-html does not
300- have yet (mirroring how ` id="row-{{row.id}}" ` already resolves per row
301- for box identification). Left unconverted, not silently skipped.
252+ ` layout.go ` (` renderFrame ` + ` shell.ctml ` /` shellregion.ctml ` /` shellwide.ctml ` )
253+ closes the migration at the outermost layer: the permanent frame every
254+ primary panel renders inside — tab strip, session strip, the active
255+ panel/overlay body, footer key hints, all inside one rounded border.
256+
257+ - ** The header/footer bands are a ` <layout variant="HF"> ` band-split** in
258+ all three shell files alike. The header (tab strip + session strip,
259+ already joined and each already fitted to the frame's inner width) and
260+ the footer (status + key hints, likewise pre-fitted) ride ` <verbatim> ` in
261+ the H and F slots.
262+ - ** The region joins the same declarative surface as H/F for every shape
263+ but one** , since go-html v0.14.0 shipped a themeable ` Content ` style
264+ (` TermTheme.Content ` , matching the pre-existing themeable ` Aside ` ) and
265+ row-scoped verbatim: a zero-chrome C (and R) now passes pre-fitted
266+ content through byte-exact at the slot's full width, closing the gap the
267+ previous round left open (docs/ctml.md S:15.2/S:15.5). Wide layouts
268+ (` shellwide.ctml ` , ` <layout variant="HCRF"> ` ) bind the main panel to C
269+ and the inspector to R, one ` RenderTerm ` call (` renderBandLayout ` ) in
270+ place of ` lipgloss.JoinHorizontal ` and a manual ` │ ` separator column.
271+ Every single-pane shape — Narrow either way, Overlay with the inspector
272+ closed — binds the one active pane to C (` shellregion.ctml ` ,
273+ ` <layout variant="HCF"> ` ), replacing a bare ` fitPane ` call. The ONE shape
274+ that still cannot join them is Overlay with the inspector open: two
275+ independently-sized panes stacked vertically with a rule between, which
276+ HLCRF's five-letter slot vocabulary has no pair for (H/F are whole-page
277+ top/bottom bands, not a reusable mid-page pair, and go-html's own
278+ automatic L/C/R vertical stacking triggers only below 80 columns —
279+ S:15.1 — above this layout kind's own floor). That one shape stays
280+ exactly the pre-.ctml host composition: ` shell.ctml ` 's H/F-only layout,
281+ split by ` renderBandFrame ` , with ` renderInspectorStack ` composing the
282+ region between the bands — the same HF+host-composition idiom a
283+ widget-carrying overlay uses for a live Bubbles widget between its own
284+ bands.
285+ - ** The Wide inspector is now 28 columns, not 32.** go-html's non-FitSlots
286+ middle band gives R a fixed outer-width budget (the unexported
287+ ` termAsideWidth ` , S:15.1) whenever R is present at ≥80 columns — there is
288+ no ` TermOptions ` /` Layout ` lever to request a different width, and a
289+ zero-chrome C does not collapse the C/R junction either: go-html always
290+ inserts its own single blank-space gutter before R, so the old visible
291+ ` │ ` rule is gone with it (the gutter is blank; there is no theme lever to
292+ paint a glyph into it). ` regionAsideWidth ` (` layout.go ` ) mirrors the
293+ fixed budget as a documented local constant — go-html exports no
294+ accessor for it (S:15.5) — and ` TestRegionAsideWidth_MatchesGoHTML ` pins
295+ it against a live ` RenderTermBoxes ` call so upstream drift fails loudly
296+ rather than silently reflowing the frame. The main panel gains the 4
297+ columns back (` measureFrame ` 's Wide case); this is a real, visible width
298+ delta, tied entirely to that one doctrine clause.
299+ - ** Mouse hit-testing needed no re-plumbing, this slice included.**
300+ ` onMouse ` resolves against its own independent ` renderPanelBarBoxes `
301+ call, keyed off ` frameInsetRows ` /` frameInsetCols ` and the tab strip's
302+ row-0 position — both unchanged since the tab strip's own render path
303+ (` renderPanelBar ` , ` tabs.ctml ` ) is untouched. No region content (chat
304+ transcript, Work/Data/Models/Service panels, the inspector) resolves a
305+ mouse coordinate against its own composition today, so there was no
306+ "old composition maths" to re-plumb onto boxes for this slice either.
307+ - ** The chat transcript's row LIST is declarative; each row's own
308+ STRUCTURE stays host-decided.** ` transcript.ctml ` is a zero-or-one-row
309+ "notice" sequence (the session status banner) followed by one row per
310+ turn, each row's verbatim ` body ` the turn's complete pre-formatted
311+ output — row-scoped verbatim (` <verbatim value="{{turn.body}}"/> ` ,
312+ materialised per row against the enclosing ` <each> ` , S:6.5) replaced the
313+ old hand-grown ` core.Builder ` accumulation loop. What did NOT convert:
314+ a turn's SHAPE (an inline "you "+text line for ` user ` , a label+result
315+ pair for ` tool ` , or an optional thinking line + label + Glamour/streaming
316+ body + tool calls + a live spinner for ` assistant ` ) still branches in Go
317+ (` renderTranscriptTurn ` ), because ` <switch> ` /` <if> ` /` <unless> ` compile to
318+ a ` func(*Context) bool/string ` closure resolved once per RENDER against
319+ ` Context.Data ` (S:7) — the same context for every row an ` <each> `
320+ produces, not a per-row scope the way a ` {{path}} ` bind is. A row can
321+ vary its VALUES; it cannot vary its STRUCTURE. This is the documented
322+ closed-vocabulary boundary (S:1.1: a named lookup, never an expression),
323+ not a workaround.
302324
303325### The command palette
304326
0 commit comments