Summary
The UI already inserts day separators into the timeline (maybeInsertDateDivider in static/chat.js: "Today", "Yesterday", full date), which works great while a day fits on screen. But in a busy channel a single day can be hundreds of messages long, so once you scroll into history the separator that tells you which day you're reading is often hundreds of messages above the viewport. Reading back through a long working session, there's no way to tell what day the messages in front of you belong to without scrolling all the way up to find the divider.
Proposal
A transient floating day pill, the way WhatsApp Web does it: while you scroll, a small capsule appears top-centre showing the day of the messages currently at the top of the view, and it fades out about a second after you stop scrolling. It floats over the content (no layout shift, nothing permanently covered, clicks pass straight through it) and only ever paints its own small capsule; the in-flow day separators are untouched.
The floating indicator is a frontend-only additive UI change with no server code or settings; the same PR also includes the targeted per-channel Clear fix below. Outside scrolling, the indicator itself is hidden.
A bug found along the way (fixed in the same PR)
While building this I found that per-channel Clear leaves orphaned day separators behind: the clear loop removes only elements with dataset.id, but dividers are created with only dataset.channel (static/chat.js), and filterMessagesByChannel (static/channels.js) only toggles visibility. So after clearing a channel its separators linger in the DOM, and the next message duplicates one. Reproduce on current main: clear a channel that has messages from a previous day, and the old day's separator is still there. The PR fixes the clear loop to remove matching dividers too; the indicator needs that anyway so it can never show a date from cleared history.
Implementation notes
- Host is a zero-height
position: sticky first child of #timeline: no flow height, stays centred when side panels resize, pointer-events: none and aria-hidden="true" so it can't intercept clicks or reach screen readers, and there is no full-width surface that could cover content.
- The label scan is cheap and stays cheap on big rooms: it iterates a cached live
HTMLCollection of .date-divider elements (follows divider add/remove automatically) at most once per requestAnimationFrame, driven from the existing timeline scroll listener; cost scales with the number of day dividers (tens), not messages (thousands).
- Stale-state hooks: the label is recomputed (without triggering the show/fade cycle) after channel switch restore and after both clear paths, and the pill hides immediately when no divider is above the viewport.
- Static asset
?v= cache-busts bumped for the touched files.
Implementation
Implemented in PR #78 (4 static files, 109 additions / 5 deletions; no server changes). Tested locally on a real multi-agent workspace (3000+ messages, 8 channels): node --check passes on both touched JS files; live checks covered correct day while scrolling both directions, fade-out on idle, day handoff at boundaries, channel switch and per-channel clear showing no stale day, click-through, and narrow-window layout.
Summary
The UI already inserts day separators into the timeline (
maybeInsertDateDividerinstatic/chat.js: "Today", "Yesterday", full date), which works great while a day fits on screen. But in a busy channel a single day can be hundreds of messages long, so once you scroll into history the separator that tells you which day you're reading is often hundreds of messages above the viewport. Reading back through a long working session, there's no way to tell what day the messages in front of you belong to without scrolling all the way up to find the divider.Proposal
A transient floating day pill, the way WhatsApp Web does it: while you scroll, a small capsule appears top-centre showing the day of the messages currently at the top of the view, and it fades out about a second after you stop scrolling. It floats over the content (no layout shift, nothing permanently covered, clicks pass straight through it) and only ever paints its own small capsule; the in-flow day separators are untouched.
The floating indicator is a frontend-only additive UI change with no server code or settings; the same PR also includes the targeted per-channel Clear fix below. Outside scrolling, the indicator itself is hidden.
A bug found along the way (fixed in the same PR)
While building this I found that per-channel Clear leaves orphaned day separators behind: the clear loop removes only elements with
dataset.id, but dividers are created with onlydataset.channel(static/chat.js), andfilterMessagesByChannel(static/channels.js) only toggles visibility. So after clearing a channel its separators linger in the DOM, and the next message duplicates one. Reproduce on currentmain: clear a channel that has messages from a previous day, and the old day's separator is still there. The PR fixes the clear loop to remove matching dividers too; the indicator needs that anyway so it can never show a date from cleared history.Implementation notes
position: stickyfirst child of#timeline: no flow height, stays centred when side panels resize,pointer-events: noneandaria-hidden="true"so it can't intercept clicks or reach screen readers, and there is no full-width surface that could cover content.HTMLCollectionof.date-dividerelements (follows divider add/remove automatically) at most once perrequestAnimationFrame, driven from the existing timeline scroll listener; cost scales with the number of day dividers (tens), not messages (thousands).?v=cache-busts bumped for the touched files.Implementation
Implemented in PR #78 (4 static files, 109 additions / 5 deletions; no server changes). Tested locally on a real multi-agent workspace (3000+ messages, 8 channels):
node --checkpasses on both touched JS files; live checks covered correct day while scrolling both directions, fade-out on idle, day handoff at boundaries, channel switch and per-channel clear showing no stale day, click-through, and narrow-window layout.