A quick session switcher in the embedded (macOS) build. It replaces the session dropdown in the
transcript's sticky header — not the left sidebar, which was never the right fit for switching and is
removed. The full-fat view, with sentiment maps and user journeys, stays one click away as
Sessions index. Real pixels below: the shipped .badge atom, the macOS AppKit type
ladder from tokens-desktop.css, and ExportPopoverRow's exact geometry
(308pt wide, 6/12 row padding, 10pt gap, 6pt corner radius).
Decided: the real AppKit source-list capsule — which decides the implementation, not just the
styling. The capsule colour is internal to NSTableView, so having it means using the
control: the popover stays a popover (SwiftUI .popover, same as the share menu) and its
content becomes an NSTableView with style = .sourceList. The other
three below are kept switchable as the record of what was weighed. Note the specimens are SwiftUI-style
approximations — only the shipped control draws the true capsule.
The popover family has no precedent for a selected row — ExportPopoverRow has hover only
(Color.primary.opacity(0.06)), because actions have no “current one”. This adds a state the
family doesn't have, so it has to borrow from somewhere.
Assets.xcassets for, in its own words,
“any future SwiftUI selection surface”. That is this. Same 6pt rounded rect as the hover, so
hover and selection are one shape at two weights..session-entry.active text treatment but drops its
wash. Quietest; also the easiest to lose when the row is also hovered.NSMenu idiom. It costs a third column, and the check
pushes the badges out of alignment with every other row.NSTableView and matches no public UI-element colour (verified by
sampling all of them). The chip here is an imitation; a SwiftUI row cannot read the real value.
Flip the palette above and watch these three converge and diverge. Under Edo the web
nav-selection and the Lozenge are byte-identical (#e6ddc9) — they were authored as one
decision. Under Default they are not even the same hue: the web sidebar washes rows neutral grey
#efefef while the Lozenge is pale blue #eef4fc. AppKit's real capsule is a third
value under both, and unreadable under both.
So “match the project sidebar” cannot be satisfied literally on any palette. The question the side-by-side actually answers is which mismatch reads as deliberate — a popover that agrees with the web list it replaces, or one that agrees with the native list above it.
On the date: rows show duration · formatFinderDate(...) — exact parity with the Sessions
grid's Start column. Sessions ingested from media carry a real clock time
(st_birthtime, s01_ingest.py:23). Sessions imported from transcript text with a bare
YYYY-MM-DD header do not, and render 11 Feb 2026, 00:00 — the last row of each popover
is pinned to that state so the failure mode is visible rather than described.
Rows are real Fishkeeping data — genuine names and durations, genuine multi-participant sessions (#6, #8, #10) where two or three people share one session and the second and third have no name, because the pipeline only named the first speaker in each. Codes stay globally sequential the way the pipeline assigns them, so a three-person session pushes every later code along —
#11isp16, notp11. Timestamps are substituted (Fishkeeping is a transcript-only import; see the note under the swatches). Twelve sessions is the default because that's the top of the common range — switch to 6 or 34 above to see the short case and the scrolling case.p15, 16, 17in one badge, names comma-joined in the title. Uniform 42pt row height, which is what a 20-row scrolling list wants. Watch it truncate at 308pt: “Session 16 · Nina Petrović, Participan…”. That truncation is real, not a mockup artefact — it is what 308pt buys you.#Nsession badge. The p-codes are the nomenclature researchers already put on slide decks;#Nis a CLI habit that reads as an escapee in a Mac list. Nothing truncates and every code is legible; rows go 42 → 90pt.p6appears to label Session 6 — reintroducing exactly the session-number/participant-number conflation that dropping#Nwas meant to remove — and the last name is left with no badge. Aligning costs 16pt of row height. It needs no table-in-a-table: two columns sharing a row pitch is one CSS grid, or oneNSGridViewin AppKit. Flip the control to see the rejected shape.pNandpNN— the 99.9% case. Three digits widens that one badge and shifts its title; it doesn't break, and it isn't a design target before beta.Why a popover and not an
NSMenu. The thing being replaced is already a dropdown, so this isn't a sidebar being demoted into a menu — it's a dropdown moving to the titlebar and gaining the density it always wanted.NSMenucan't do a two-line row with a muted subtitle, and the duration/date line is the whole reason this beats the old selector. The list is sized for what actually happens: 5–12 sessions very commonly, occasionally 30+. At 12 nothing scrolls; past that it does, and the “full fat” answer was always Sessions index rather than a longer menu.