mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 15:39:41 +02:00
feat(sidebar): add a rich session sidebar that carries the home screen's row detail
Session List Layout gains a third option. The old "Left sidebar" becomes "Left sidebar simple" and is unchanged down to the byte; the new "Left sidebar" puts on each row what the desktop home rail and the phone overview already show: when the session was first created, how long it has been in the state it is in, and a status pill naming that state. A docked column is not a tab strip. It has width to spare and a row per session either way, and "name + folder" is the whole story a TAB can tell, not the whole story there is. This is the information that was missing, and it already existed one surface over. Both sidebar values are the same layout, and both set data-session-list="sidebar"; the row detail rides on a separate data-sidebar-detail attribute. That split is the load-bearing decision here: every one of the ~25 isSessionSidebarActive() call sites and every html[data-session-list="sidebar"] rule in styles.css and mobile.css keeps matching both variants without being touched. A third data-session-list value would have meant auditing and editing all of them. - Stored values: 'header', 'sidebar' (simple), 'sidebar-rich'. Anyone already on 'sidebar' keeps exactly the layout they picked — the rename is label-only. - State classification and the "how long has it been like this" anchor come from _mobileOverviewState() / _mobileOverviewSince(), not re-derived, so the three surfaces cannot disagree about what "working" means. A working pane repaints ~1/s, so its duration is measured from the turn's last Enter: a running turn reads "working 12m", not "0m". - Stamps refresh in place on a 20s clock rather than by re-rendering — a rebuild would restart every load spinner and alert animation in the list, twice a minute. The clock runs only while rich rows are on screen, and is stopped from both render paths and from applySessionListLayout(). - The incremental render path updates the pill, the accent class and the since anchor; a tick alone cannot see a state change, and a new turn re-stamps lastSubmitAt without changing state. - applySessionListLayout() now re-renders on a DETAIL change too. simple <-> rich leaves data-session-list on 'sidebar' both times, and the meta line is emitted by the row template rather than toggled by CSS, so the old layout-only test would have flipped the setting and repainted nothing. - Width: 300px for the extra line. The collapsed 44px rail and the handheld drawer are both explicitly held back from it — the desktop rule is (0,3,1) and would otherwise out-specify mobile.css's (0,2,1) drawer base and pin a 320px phone's drawer to 300px. - Missing/stale mobile-overview.js degrades to a row with no meta line rather than throwing and taking the whole tab strip down. 15 new tests cover the attribute split, the solo-window override, the detail-change re-render, the row model, both render paths, the clock lifecycle and the mobile width guard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,7 @@
|
||||
--header-height: 36px;
|
||||
--toolbar-height: 42px;
|
||||
--sidebar-width: 260px;
|
||||
--sidebar-width-rich: 300px; /* detailed rows carry a stamps line as well */
|
||||
--sidebar-width-collapsed: 44px; /* == --touch-target-min */
|
||||
--sidebar-transition: 0.18s ease;
|
||||
--glass-bg: rgba(31, 38, 48, 0.85);
|
||||
@@ -16675,6 +16676,14 @@ html[data-session-list="sidebar"] .session-sidebar {
|
||||
dropdowns and the inline rename input paintable in place. */
|
||||
}
|
||||
|
||||
/* Rich rows carry a stamps line the simple rows do not, and at 260px
|
||||
"created 3d ago · working 12m" ellipsizes before it is finished. Overridden
|
||||
by the collapsed rule below, which is more specific and comes after. */
|
||||
html[data-session-list="sidebar"][data-sidebar-detail="rich"] .session-sidebar {
|
||||
flex-basis: var(--sidebar-width-rich);
|
||||
width: var(--sidebar-width-rich);
|
||||
}
|
||||
|
||||
html[data-session-list="sidebar"][data-sidebar="collapsed"] .session-sidebar {
|
||||
flex-basis: var(--sidebar-width-collapsed);
|
||||
width: var(--sidebar-width-collapsed);
|
||||
@@ -16797,6 +16806,131 @@ html[data-session-list="sidebar"] .session-tab.tab-filtered-out {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* --- Rich rows (sessionListLayout 'sidebar-rich') ----------------------- */
|
||||
/* The detailed variant of the SAME sidebar: identical column, identical
|
||||
re-parented #sessionTabs, identical filter and Alt+B toggle. The only
|
||||
difference is that each row also carries the line the desktop home rail and
|
||||
the phone overview carry — when the session was first created, how long it
|
||||
has been in the state it is in, and a status pill.
|
||||
|
||||
Everything here is scoped to html[data-sidebar-detail="rich"], which
|
||||
applySessionListLayout() only ever sets to 'rich' while data-session-list is
|
||||
'sidebar'. `.tab-meta` is emitted by the row template exclusively in that
|
||||
mode, so these rules have nothing to match anywhere else — the display:none
|
||||
below is the second lock, not the mechanism. */
|
||||
html[data-sidebar-detail="rich"] .session-sidebar .tab-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35em;
|
||||
min-width: 0;
|
||||
margin-top: 0.15em;
|
||||
font-size: 0.62rem;
|
||||
font-family: monospace;
|
||||
line-height: 1.3;
|
||||
color: var(--text-muted);
|
||||
opacity: 0.8;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* A meta line can only be produced by the rich row template, but if one ever
|
||||
survives into another layout (a render that lost a race with a settings flip)
|
||||
it must not paint: the header strip has no room for it. */
|
||||
.session-tab .tab-meta {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html[data-sidebar-detail="rich"] .session-sidebar .tab-meta-item {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
html[data-sidebar-detail="rich"] .session-sidebar .tab-meta-key {
|
||||
margin-right: 0.35em;
|
||||
opacity: 0.7;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
html[data-sidebar-detail="rich"] .session-sidebar .tab-meta-sep {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
/* While a session is actually doing something, how long it has been doing it is
|
||||
what the eye should land on — same emphasis the home rail gives it. */
|
||||
html[data-sidebar-detail="rich"] .session-sidebar .session-tab.tab-state-working .tab-meta-since {
|
||||
color: var(--green);
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
/* Pushed hard right and never shrinking, so the stamps ellipsize before the
|
||||
status word does. */
|
||||
html[data-sidebar-detail="rich"] .session-sidebar .tab-pill {
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
padding: 0.1em 0.5em;
|
||||
border-radius: 999px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95em;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Same three colors as every other session surface: red means a question is
|
||||
pending, yellow means it wants input, green means work is happening. */
|
||||
html[data-sidebar-detail="rich"] .session-sidebar .tab-pill--needs,
|
||||
html[data-sidebar-detail="rich"] .session-sidebar .tab-pill--error {
|
||||
background: color-mix(in srgb, var(--red) 18%, transparent);
|
||||
border-color: color-mix(in srgb, var(--red) 45%, transparent);
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
html[data-sidebar-detail="rich"] .session-sidebar .tab-pill--waiting {
|
||||
background: color-mix(in srgb, var(--yellow) 18%, transparent);
|
||||
border-color: color-mix(in srgb, var(--yellow) 45%, transparent);
|
||||
color: var(--yellow);
|
||||
}
|
||||
|
||||
html[data-sidebar-detail="rich"] .session-sidebar .tab-pill--working {
|
||||
background: color-mix(in srgb, var(--green) 15%, transparent);
|
||||
border-color: color-mix(in srgb, var(--green) 40%, transparent);
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
/* Muted one step further than the idle dot: the pill is a block of color, so it
|
||||
reads louder than a 9px dot at the same mix. */
|
||||
html[data-sidebar-detail="rich"] .session-sidebar .tab-pill--idle {
|
||||
background: color-mix(in srgb, var(--green) 7%, transparent);
|
||||
border-color: color-mix(in srgb, var(--green) 18%, var(--border));
|
||||
color: color-mix(in srgb, var(--green) 45%, var(--text-muted));
|
||||
}
|
||||
|
||||
/* Three lines of content per row instead of two, so give them room to breathe
|
||||
and stop the row actions crowding the pill.
|
||||
|
||||
:not([data-sidebar="collapsed"]) is load-bearing, not decoration: the two
|
||||
rules below are the only ones in this block that move geometry rather than
|
||||
paint the meta line, and the collapsed 44px rail centres a row that is by
|
||||
then just a status dot and its badges. Without the guard, `align-items:
|
||||
flex-start` and a 0.15rem top margin on .tab-status would push that dot off
|
||||
the centre line of every row in the rail. */
|
||||
html[data-sidebar-detail="rich"]:not([data-sidebar="collapsed"]) .session-sidebar .session-tab {
|
||||
align-items: flex-start;
|
||||
padding: 0.45rem 0.5rem;
|
||||
}
|
||||
|
||||
/* The gear/detach/close column is centred against a two-line row; against a
|
||||
three-line one it drifts low, so pin it to the name it acts on. */
|
||||
html[data-sidebar-detail="rich"]:not([data-sidebar="collapsed"]) .session-sidebar .session-tab .tab-actions,
|
||||
html[data-sidebar-detail="rich"]:not([data-sidebar="collapsed"]) .session-sidebar .session-tab .tab-number,
|
||||
html[data-sidebar-detail="rich"]:not([data-sidebar="collapsed"]) .session-sidebar .session-tab .tab-status {
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
|
||||
/* --- Collapsed rail ---------------------------------------------------- */
|
||||
/* Collapsed is a 44px icon rail, not "hidden": the ambient signal (status dot,
|
||||
task/subagent/ultracode badges) is the whole point of mission control and
|
||||
|
||||
Reference in New Issue
Block a user