mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-10 01:09:43 +02:00
Merge #538: Tab Layout (by state, by case, ledger, classic) and three header stats styles (Discussion #426)
This commit is contained in:
@@ -897,6 +897,30 @@ Tests: `test/terminal-touch-tap.test.ts`.
|
||||
|
||||
⚠️ Mobile no longer hoists the active session to the front of the strip: that reordering ran on full renders only, so tab order flipped depending on which render path fired, and it renumbered the Alt+N badges. Scroll-into-view replaces it; do not reintroduce it.
|
||||
|
||||
### Tab layouts (by state, by case, ledger)
|
||||
|
||||
**Tab layouts** (`tabArrangement`, per-device, default `state`; Discussion #426): App Settings → Appearance → Tabs → **Tab Layout**. Four values: `state` (option C), `case` (option A), `ledger` (option B) and `classic`, the strip as before. `<html data-tab-arrangement>` is stamped pre-paint and by `applyTabOrientation()`; the render paths read it through `isTabTriage()`, `isTabClusters()` and `isTabLedger()`. Named groups in the vertical rail (owner tab layouts, `_projectTabGroups()` non-null) win over both groupings, and the grouped tree renders as before.
|
||||
|
||||
**By state.** The list is split into four groups: **needs you** (a permission or question dialog; a failed session joins it), **waiting** (idle prompt pending), **working**, **idle** (also ended sessions, an agent that exited inside a live pane, and web tabs). Idle is `quiet` (`TAB_TRIAGE_GROUPS`): its heading element still exists, because it anchors the row's `order` band and, as the lead, holds the row's place beside the brand, but it draws no label or count (`.tab-triage-head--quiet`). `tabStateOrder` (`urgent-first` default, `urgent-last`) decides which end they start from; only the group order flips, never the rows inside a group. The desktop header strip draws a row per group with its label left-aligned in a column that `_sizeTabTriageGutter()` measures to the widest label on screen (re-measured only when the label text changes, and once on `document.fonts.ready`), so no row carries a fixed gutter's worth of empty space. The brand leaves the flow and sits over the strip's top-left corner (`.header:has(...)`), so the first row's heading (`.tab-triage-head--lead`, natural width) is pushed past it by `--tab-triage-brand` (a ResizeObserver keeps that width, so no render pass reads layout for it) and every later row starts at the edge, under "Codeman"; the flat rail and the sidebar draw a section per group; the tablet strip (600-767px) keeps its scrolling row with the headings as inline dividers; phones keep their chip row in group order with the headings hidden (mobile.css). Classification is `_mobileOverviewState()` + `_mobileOverviewExit()`, the home screens' own; the fold into four groups and the order bands are pure in `CodemanTabTriage` (constants.js, `computeTabTriageLayout()`).
|
||||
|
||||
⚠️ **By state is the flex `order` property, never a DOM reorder**, exactly like the sorted rail: `#sessionTabs` stays in `sessionOrder`, so the Alt+N badges, drag, the keyboard walk (which sorts by COMPUTED order) and the sidebar filter keep reading the list they always read. Each group owns a band of `TAB_TRIAGE_STRIDE` order values: its heading, its rows, its web tabs, then the row break that ends the header line. The headings and breaks are `aria-hidden` direct children of `#sessionTabs`, reconciled in place by `_syncTabTriageChrome()` after BOTH render paths, because a state change is an incremental pass and can still empty a group or fill a new one. Inside a header row tabs keep TAB order; a sorted rail ranks each section the way it ranks the flat rail. The header strip only wraps into rows on desktop, where `updateTabOverflowMode()` forces `tabs-auto-wrap`; the breaks only display in a wrapping strip, since a `flex-basis: 100%` break in a nowrap scroller would steal width.
|
||||
|
||||
**By case.** Each case's tabs sit in one `.tab-cluster` box (`_renderTabClusters()`), in the order the case first appears in the tab order; membership is `_mobileOverviewCaseFor()` (longest case path that prefixes the working directory), else the directory, else the session alone. A box with two or more tabs is labelled (swatch, name, count) and its generated `w<n>-<case>` names drop the `-<case>`, which stays in the DOM in a `.tab-name-case` span only `.tabs-clusters` hides; a one-tab case gets only the swatch in the header strip; a web tab gets a box of its own. The colour is `--session-<colour>` by a stable hash of the case key (`CodemanTabClusters.colorFor()`), so nothing is stored. The rail and the sidebar draw every case as a labelled section; phones dissolve the boxes (`display: contents`) into the one chip row.
|
||||
|
||||
⚠️ **The boxes ARE a DOM grouping**, unlike by state, which is safe only because a tab changes case far less often than it changes state: `_tabClusterLayout().key` is the whole structure, and the incremental path rebuilds whenever it differs from the last full render's. The keyboard walk keeps boxes together (box index first, then computed order, since a sorted rail orders rows inside each box). Drag only reorders inside one box, as it only reorders inside one state group (`_isTabDropAcrossGroups()`).
|
||||
|
||||
**Ledger.** CSS only, on `.tabs-ledger` inside `@media (min-width: 768px)` and the header host: the flat list on an auto-fill column grid with equal cells, mono type and a 3px status bar (`--ledger-bar`: yellow for a waiting alert, red for an action alert or an error, muted for stopped, exited and web tabs) instead of the dot. ⚠️ It must not change the markup (pinned: the ledger's innerHTML equals classic's); the rail, the sidebar and narrower strips show the plain list.
|
||||
|
||||
With `classic` nothing is left behind: no headings, no breaks, no inline order, no boxes, no name split, no arrangement class. A stale cached mobile-overview.js degrades every grouping to the flat strip. Tests: `test/tab-triage.test.ts`, `test/tab-clusters.test.ts`.
|
||||
|
||||
### Header stats styles
|
||||
|
||||
**Header stats styles** (`headerStatsStyle`, per-device, desktop only, default `tiles`; Discussion #426 option G): how the WS readout, CPU, MEM and the plan-usage windows are drawn. `tiles` gives each its own label-over-value tile with a bar along the bottom edge; `compact` is two pills, WS / CPU / MEM and the plan windows, with a ring beside every value (stat rings accent, red past 80%; plan rings green, yellow or red); `classic` is the bars and `5H · 7D` chip as before. `data-header-stats` on `<html>` (pre-paint, then `applyHeaderStatsStyle()` from `applyHeaderVisibilitySettings()`) drives every rule; narrower than 768px and in a solo window it resolves to `classic`, because the stats are hidden there anyway and the moves below would otherwise reach the phone header.
|
||||
|
||||
⚠️ The clustered styles need the three elements contiguous, and the template keeps the classic order, so `applyHeaderStatsStyle()` MOVES `#connectionIndicator` into `#headerSystemStats` (first child) and `#planUsageChip` right after it, and `classic` moves them back to comment anchors it left at the template positions. Ids are unchanged, so every writer still finds them. ⚠️ The indicator joins the pill only while System Stats is shown: the pill is hidden with `display: none`, and the WS readout must not disappear with it.
|
||||
|
||||
⚠️ The extra parts (`.stat-ring`, `.connection-tile`, `.pu-ring`, `.pu-meter`) are always rendered and hidden by default in styles.css, which is what keeps `classic` looking exactly as before. ⚠️ A tile is a three-row grid (9px label, 14px value, 2px bar) with pixel line-heights, 36px tall like the header, in the bundled JetBrains Mono. The first version laid the bar over the bottom of a fixed 28px tile, and a taller system mono (SF Mono) pushed the value into it; the bar is now a real row, so the height comes from the layout, not the font. The WS tile keeps its grid on the inner `.connection-tile` span, because `_updateConnectionIndicator()` writes the indicator's own `display` inline. The tile words come from `_connectionTileWords()`, DERIVED from the connection descriptor rather than added to it, because `test/connection-indicator.test.ts` pins the descriptor's exact shape. Plan rings and meters clamp their fill to 0-100 while the label keeps the real number. Tests: `test/header-stats-style.test.ts`.
|
||||
|
||||
### Phone overview home screen
|
||||
|
||||
**Phone overview home screen** (`mobile-overview.js`, phones only, per-device `mobileOverviewEnabled`, default ON): under 600px the "C" logo shows a session overview (NEEDS YOU / CURRENT SESSIONS / PAST SESSIONS) instead of the welcome overlay; tablet and desktop are unchanged. The branch lives in `showWelcome()`/`hideWelcome()` (terminal-ui.js) behind `shouldUseMobileOverview()`, which is **width-driven** (`getDeviceType() === 'mobile'`) because this is a layout decision, unlike the settings namespace which stays handheld-based.
|
||||
|
||||
Reference in New Issue
Block a user