mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 15:09:42 +02:00
feat(tabs): full-row activation and tree semantics for the grouped rail
The grouped vertical rail is now an ARIA tree with a tree keyboard model, and tab rows are pinned as full-row activation targets whose controls keep their own actions and stable hit targets. Tree semantics (grouped vertical rail only): - #sessionTabs becomes role=tree while grouped and returns to its shipped role=tablist and label when grouping ends. The header strip, sidebar and flat rail keep role=tablist / role=tab exactly as before (the flat rail's markup is unchanged byte for byte). - A named group's header is a level-1 treeitem with aria-expanded that aria-owns its rows' role=group (rows are level 2). Ungrouped rows and the row a collapsed group keeps showing are level-1 items; a collapsed header owns nothing, and the "Ungrouped" heading is a visual divider hidden from assistive tech. aria-level, aria-setsize and aria-posinset are set on every item, and aria-selected follows the selection without a rebuild. - Exactly one treeitem carries tabindex=0 (roving). Controls inside rows leave the tab order, so Shift+F10 / ContextMenu open a row's actions (session action menu, web tab settings). - Up/Down walk visible items, Home/End jump, Right expands a header or enters it, Left collapses a header or climbs from a row to its header, Enter/Space select a row or toggle a header. With the activity sort on, the walk follows painted order within each group; the flat list keeps its whole-list walk. - Focus survives a full re-render by identity (a row a collapse just hid hands focus to its header), but a render never pulls focus into the rail. - The group header is the treeitem itself (no nested button), still toggled by click through the same onclick and still the lineage proxy anchor. Full-row activation: - Clicking a row's status dot, mode chip, name or padding already selected it upstream; that is now pinned in real Chromium for the strip, the flat rail and the grouped rail, together with every control (gear, detach, close, overflow, web tab gear and close) running only its own action. - The close control now shows a pointer like its siblings instead of the default arrow. - Enter/Space on a focused web tab in the flat list opens it; it used to call selectSession(undefined). - The action controls are pinned to stay under the pointer when a row is hovered (no reflow-on-hover moving the gear out from under a click). New Chromium suite test/tab-activation.browser.test.ts is listed in BROWSER_TEST_GLOBS (run with npm run test:browser).
This commit is contained in:
@@ -303,6 +303,8 @@ So: `_confirmIdle()` (session.ts) requires the pane to go quiet, and then asks t
|
||||
- **Collapse is per-device** (`codeman:tab-groups-collapsed` in localStorage, ids of deleted groups garbage-collected on adoption, any storage failure means all-expanded). A collapsed group still SHOWS the active row, and `_updateActiveTabImmediate` falls through to a full render whenever the structure key changes, since a class toggle cannot reveal a hidden row.
|
||||
- **Lineage arcs to a collapse-hidden session anchor to its group header** (`lineage-line--proxied`); two endpoints proxied to one header draw nothing.
|
||||
- **Drag-reorder is off in the grouped rail** until grouped editing lands: a flat-order drop cannot express a group move, and the server re-ranks within the old group.
|
||||
- **Only the grouped rail is a tree.** `#sessionTabs` ships as `role=tablist` with `role=tab` rows, and the header strip, sidebar and flat rail keep exactly that. While grouped, `_applyTabListRole` makes it `role=tree` (and restores `tablist` + its label when grouping ends), named-group headers are level-1 `treeitem`s that `aria-owns` their rows' `role=group` (rows sit beside the header, not inside it), and ungrouped rows plus a collapsed group's kept selection are level-1 items. A collapsed header owns nothing, and the "Ungrouped" heading is `aria-hidden`. Rows are re-roled in the DOM by `_applyTabTreeSemantics` after render, never by rewriting their markup, so a grouped row's content stays the flat row's.
|
||||
- **One tab stop in the tree.** Exactly one treeitem carries `tabindex=0` (the focused or selected item); every control inside a row drops to `-1`, which is why Shift+F10 / ContextMenu open a row's actions from the keyboard. Focus survives a full re-render by identity (`group:`/`session:`/`webview:`; a row a collapse just hid hands focus to its header), but only when focus was already inside the rail. The tree walk (`_tabTreeItems`) follows painted order WITHIN each group when the rail is sorted; the flat list keeps its own whole-list computed-order walk.
|
||||
|
||||
|
||||
⚠️ **`TabLayoutService` is the single mutation boundary** and every lifecycle caller (session created/removed, webview created/deleted, a legacy order PUT) describes ONE completed server action and gets AT MOST ONE versioned write; writing layout state from a route or a manager directly is what the service exists to prevent.
|
||||
|
||||
Reference in New Issue
Block a user