mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 07:29:42 +02:00
feat(tabs): grouped vertical rail from owner tab layouts
The vertical tab rail now reads the owner's tab layout (GET /api/tab-layout) and draws its groups as collapsible sections. This is the first frontend consumer of the tab-layout backend and it is read-only: nothing in the browser writes the layout yet. - tab-layout-browser.js (new, pure, loaded before app.js): projects the layout onto the live sessions and open web tabs, renders the grouped markup, stores collapse per device, and sequences loads newest-wins with a bounded retry on failure. - app.js: loads the layout on init and on tab:layoutChanged, renders the grouped rail from the same per-row markup the flat rail uses, falls through to a full render whenever the grouping structure changes, and withholds drag-reorder in the grouped rail. - Grouping is opt-in by construction. With no layout, a failed read, a layout without groups, or a horizontal strip, the rail renders exactly as before (byte-identical markup). - Grouping is a render layer only: sessionOrder, Alt+N, Ctrl+Tab and the palette keep reading the server-projected order, and row badges keep their Alt+N slot. - A collapsed group still shows the active row; lineage arcs to a hidden session anchor to its group header. - webview-tabs.js: renderWebviewTab() extracted so a single web tab can be placed into its group with unchanged markup.
This commit is contained in:
@@ -634,6 +634,78 @@ html[data-tab-orientation='vertical'] .tab-rail .session-tab .tab-info {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* --- Grouped rail (owner tab layout) ---------------------------------- */
|
||||
/* Only rendered when the owner has at least one group (tab-layout-browser.js).
|
||||
Each group's rows sit in their OWN flex column, so the activity sort's inline
|
||||
`order` (and web tabs' `order: 9999`) rank rows within a group, never across. */
|
||||
html[data-tab-orientation='vertical'] .tab-rail .tab-layout-group,
|
||||
html[data-tab-orientation='vertical'] .tab-rail .tab-layout-group-refs {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
html[data-tab-orientation='vertical'] .tab-rail .tab-layout-group + .tab-layout-group {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
html[data-tab-orientation='vertical'] .tab-rail .tab-layout-group-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
padding: 4px 6px 2px;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
color: var(--text-muted);
|
||||
background: transparent;
|
||||
font: inherit;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
html[data-tab-orientation='vertical'] .tab-rail .tab-layout-group-toggle {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
html[data-tab-orientation='vertical'] .tab-rail .tab-layout-group-toggle:hover {
|
||||
color: var(--text);
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
html[data-tab-orientation='vertical'] .tab-rail .tab-layout-group-toggle:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.tab-layout-group-name {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tab-layout-group-count {
|
||||
flex: 0 0 auto;
|
||||
font-variant-numeric: tabular-nums;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.tab-layout-group-chevron::before {
|
||||
content: '\25BE';
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tab-layout-group-toggle[aria-expanded='false'] .tab-layout-group-chevron::before {
|
||||
content: '\25B8';
|
||||
}
|
||||
|
||||
html[data-tab-orientation='vertical'] .header-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
@@ -10510,6 +10582,21 @@ kbd {
|
||||
drop-shadow(0 0 9px var(--lineage-color, var(--session-blue, #2b8fd9)));
|
||||
}
|
||||
|
||||
/* An arc re-anchored to a collapsed group's header (its row is hidden): quieter
|
||||
and dotted, so it reads as "somewhere in there" rather than a real row. */
|
||||
.connection-line.lineage-line--proxied {
|
||||
stroke-dasharray: 2 7;
|
||||
opacity: 0.46;
|
||||
}
|
||||
|
||||
.connection-line.lineage-line--proxied.lineage-line--working {
|
||||
opacity: 0.62;
|
||||
}
|
||||
|
||||
.lineage-line-dot.lineage-line-dot--proxied {
|
||||
opacity: 0.46;
|
||||
}
|
||||
|
||||
/* The child end marches while that worker is actually working, so the line
|
||||
itself carries the signal. Motion is opt-out-able at the OS level. */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
|
||||
Reference in New Issue
Block a user