Skip to content

feat(app): SessionDetail polish + library-feed home#128

Merged
graydawnc merged 16 commits intomainfrom
feat/session-detail
Apr 29, 2026
Merged

feat(app): SessionDetail polish + library-feed home#128
graydawnc merged 16 commits intomainfrom
feat/session-detail

Conversation

@graydawnc
Copy link
Copy Markdown
Collaborator

@graydawnc graydawnc commented Apr 29, 2026

Why

Once the rest of the stack landed and the team started dogfooding, three pain points emerged together:

  1. SessionDetail had no first-class actions. Resuming, copying the session ID, or copying the resume command all required leaving the page or hunting through context menus.
  2. The library shell felt unfinished. The home feed (project-card grid from the search-migration PR), the section headers, the search results scope chip, alignment between sidebar wordmark and pane headers, and the Cmd+K overlay all needed a coordinated design pass.
  3. in:project scope was a real bug. The chip was plumbed through the UI in the search-migration PR, but the IPC handler ignored the identityKey argument — search returned global results regardless of what the user picked.

Bundling them keeps each diff small while letting reviewers see the design pass as one cohesive change.

What this PR does

SessionDetail — header + actions

  • Title row: back arrow + truncating title
  • Meta line: path · N messages · time · ● source (source dot+label moved here from a separate chip; saves vertical space and groups all metadata)
  • Actions row (right-aligned to meta, self-end): Pin · Resume in Terminal · ⋯
  • Overflow menu (⋯): Copy session ID, Copy resume command (these are secondary actions — promoting them to the toolbar would crowd it without payoff)
  • Back arrow aligned to the title's first line so it doesn't drift toward the meta row
  • All MessageBubble rows pad to px-6 so message avatars line up with the back arrow

Library home — global feed

  • Replaces the project-card grid with: Pinned segment (across all projects) → date-bucketed Recent (Today / Yesterday / Earlier This Week / Earlier)
  • Hides empty (0-message) sessions because the watcher creates a session row when a CLI process starts, before any user input — those rows would otherwise pollute the feed
  • Section headers are collapsible (chevron reveals on hover) — same component used in ProjectView

Sidebar shell

  • Permanent search trigger (full-width pill) at the top
  • PROJECTS list collapsible
  • Settings gear pinned to bottom-right (icon-only, aligned with the project session-count column above)
  • Sync status text reformatted to <count> sessions · <relative> (e.g. 420 sessions · 5m)
  • Hides scrollbar (scrollbar-none) — Linear/Notion-style; kept overlay scroll behavior for trackpads
  • New Settings → General section: Sidebar density toggles (Show source dots / Show session count) — for users who want a cleaner sidebar
  • Refetches project_groups_v whenever sync phase changes, so newly-synced projects appear without a manual reload

Cmd+K overlay polish

  • Recents in empty state: when query is empty, show recent sessions (scope-aware: scoped to the active project if in:project is selected) bucketed by date — Notion-style
  • Stable height: min-h-[220px] max-h-[min(420px,55vh)] body so the overlay doesn't jitter when toggling Fast/Agent or scope, and doesn't get absurdly tall on small windows
  • Mode-aware placeholder: "Search your sessions…" vs "Ask anything about your sessions…" so the user knows what the active mode does
  • Refined SegmentedPill: drops the border/shadow look for an accent-tinted active state; adds role="tablist" + aria-selected
  • Tab hint on scope: anchored to the scope chips so users discover the keyboard shortcut for in:project ↔ All toggle
  • Agent selector (Agent mode only): rendered on the scope row, right-aligned with an "Asking:" label. Trigger uses mousedown.preventDefault so opening the menu doesn't steal focus from the input — Tab keyboard nav keeps working after closing the menu
  • in:project IPC fix — the missing piece. searchFragments and the IPC handler now accept and apply the identityKey arg; doSearch passes it through when committing from the overlay; the results page header displays the active scope so the user can verify

Pin + ProjectView refinements

  • PinButton redrawn (Tabler-style angled pushpin) with two sizes — md (16px in detail) and sm (11px in rows) so it doesn't out-weigh adjacent ⋯ menus
  • ProjectView source filter: replaced separate pill row with inline opacity toggle on the meta source dots — clicking a source dims it (filters out); much less visual weight
  • ProjectView sort uses the same Menu component as FragmentResults, for consistency

Alignment + small polish

  • Unified pt-3 px-6 across panes so the sidebar wordmark, project header, library feed title, and session detail all start at the same vertical position
  • ⋯ buttons everywhere use mousedown.preventDefault so a click doesn't keep focus and leave the action group stuck visible
  • Folder icon prefix on each Sidebar project row; chevron reveals on hover
  • New shared Menu component (reusable dropdown with click-outside + escape handling)

Test plan

  • pnpm --filter @spool/app exec playwright test — all suites pass
  • Manual: pin/unpin from session detail; copy resume command from menu; Cmd+K with project scope → "View all results" → verify scope label and results both reflect the project
  • Manual: Cmd+K empty query → recents render (scope-aware); toggle Fast/Agent → no height jitter; open agent menu → Tab keyboard still works after closing
  • Manual: Settings → General → toggle source dots / session count → sidebar updates immediately
  • Manual: collapse/expand sidebar PROJECTS and home feed sections; verify alignment between sidebar wordmark, settings gear, and pane headers

@graydawnc graydawnc force-pushed the feat/search-migration branch from b7da818 to 26a7f6e Compare April 29, 2026 16:29
Base automatically changed from feat/search-migration to main April 29, 2026 16:31
…ta quality

- Sidebar: full-width search trigger, collapsible PROJECTS, sync+settings at bottom, hide 0-session projects
- LibraryLanding: replace project card grid with global Pinned + Recent feed (Today/Yesterday/Earlier This Week/Earlier buckets); rows show project name
- SessionRow: optional showProject prop for cross-project context
- ProjectView: drop redundant SearchTrigger from header (sidebar owns it)
- Hide compact-bar SearchBar when ProjectView active (was showing both)
- Window size 1180x780 (was 960x620)
- Filter empty (0-message) sessions from listRecentSessions / listSessionsByIdentity / listPinnedSessions / listPinnedSessionsByIdentity
- New listPinnedSessions core query + spool:list-pinned-sessions IPC for global pinned feed
- in:project scope actually filters: thread identityKey through searchFragments + spool:search IPC
- Search results header shows scope label (in <project> / all projects)
- Cmd+K overlay: agent selector slot appears when Agent mode active
- Cmd+K overlay: clickable "View all results" footer with ⇧↵ shortcut hint
- LibraryLanding + ProjectView: section headers (PINNED / TODAY / RECENT / etc.) collapsible; hover reveals chevron
- Sidebar PROJECTS toggle: chevron next to label, hover-only
- ProjectView header: project path below title in subdued mono
- SessionRow: action group aligned to first line; Menu adds "Copy resume command"
- New Menu component (reusable dropdown with click-outside + esc handling)
- e2e: helpers use search-trigger click; agent / project-view sort / fast-search updated for new UI
Live FTS in the overlay correctly filtered by identityKey, but doSearch
(invoked by "View all results" / Shift+Enter) called window.spool.search
without the scope arg. Result: scope chip said "in <project>" but the
results page showed sessions from every project.

Now doSearch reads searchScope + activeProjectKey and passes scopedKey
through, matching the overlay's behavior.
Replaces two-row stack (path / title+meta+actions) with a clean single
row: back · source chip (● claude) · title · pin · Resume · ⋯, plus a
subdued meta line (path · N messages · time) below. Right-side actions
now vertical-center with the title instead of bottom-aligning under a
stacked block.

- Source chip uses short label + color dot; model dropped (noisy)
- Copy ID / Copy resume command moved into ⋯ menu
- MessageBubble x-padding bumped to px-6 so avatars align with back arrow
- e2e updated for the new actions menu
- FragmentResults: replace native <select> sort with Menu (matches ProjectView)
- SearchOverlay: render Tab as a <kbd> chip ("Tab to switch")
- Sidebar: outline folder icon prefix on each project row
- PinButton (sm): shrink button to w-6 h-6, icon to 11px (less heavy next to ⋯)
- SessionRow ⋯: preventDefault on mousedown so click doesn't keep focus —
  fixes pin/⋯ staying visible after clicking the menu and moving the cursor
- LibraryLanding: tighten header (pt-5, text-xl, smaller subtitle) so it
  aligns with the sidebar wordmark and fills less empty space
Sidebar wordmark, LibraryLanding header, ProjectView header, and
SessionDetail header now all start at the same vertical position.
Previous mix of pt-5 / pt-6 left noticeable empty bands at the top
of each pane and made the sidebar and main pane look misaligned.
… actions

Sidebar listProjectGroups was only fetched at mount, so newly-indexed
sessions never showed up in project counts until the user reopened the
window. Now re-fetches on every sync phase change — cheap (single SQL
query) and lets the user watch counts climb during indexing.

SessionDetail header: source chip moves from row 1 (next to title) to
the end of the meta line. Right-side action group (pin / Resume / ⋯)
pulled out of the title row and pinned with self-end so its baseline
aligns with the meta line, not the title — gives the title room to
breathe and groups the actions visually with the second-row metadata.
…inline

- PinButton: replace tilted clipboard-pin glyph with Tabler-style angled
  pushpin (head + diagonal shaft + tip). Cleaner at small sizes.
- Standardize icon sizes onto a 9 / 11 / 13 / 14 / 16 scale: bump the
  10px overlay arrow to 9 (matches sort caret), trim the 12px overlay
  mode toggles + session-detail back to 11 (matches small actions).
- Sidebar status text: drop the verbose 'Synced N ago · X sessions'
  template — green dot already conveys synced state. Now reads
  'X sessions · 5m' (count first, short relative time, no 'ago'),
  fits comfortably even with 6-digit counts.
- ProjectView source filter: collapse the separate pill row into the
  meta line. Source labels themselves become toggle buttons; non-active
  sources dim to 40% opacity, default state (no filter) shows all at
  full opacity. Saves one row of vertical space and removes redundant
  display of source names twice.
Settings → General → Sidebar gains two toggles, both default on:
- Show source dots (the colored CLI indicators on each project row)
- Show session count (the trailing number on each project row)

Both flags persist on AgentsConfig (the existing JSON-backed user prefs
on disk), so they roam across app restarts. Avoid a flicker on Settings
open: GeneralTab now waits for getAgentsConfig before rendering instead
of seeding from default values and then animating the toggles to their
real state.

Sidebar bottom is restructured: settings moves from a full-width row
to a compact icon button right of the sync status line. The whole
status footer is now one row: green dot + 'X sessions · 5m' on the
left, gear icon on the right. Gear sized down to 12px with warm-faint
color so it visually matches the status text instead of competing with
it.
…count column

Hide the sidebar list scrollbar entirely (scroll-by-wheel/trackpad still
works) — same pattern as Linear/Notion/Slack nav. Reserved scrollbar
gutter looked like an unsightly empty band; auto-toggling gutter caused
content to shift when the list grew or shrank. With no gutter at all,
the right edge is stable and clean.

Settings gear now sits at pr-1.5 in the bottom row. Math: project row
count's right edge is 16px from the container right (px-2 of scrollable
+ px-2 of button). With pr-1.5 (6px) + the gear button's centering
(half of (24-12)=6) + the SVG's internal whitespace, the gear's visual
right edge lands roughly at the count column's right edge — slightly
right of perfectly mathematical alignment, which reads better given the
icon's optical weight.
…ht, refined pill

- show recent sessions (scope-aware) in empty query state, Notion-style
- stabilize body height with min-h/max-h range so mode/scope toggles don't jitter
- mode-aware placeholder ('Search…' vs 'Ask anything…') to guide users
- move agent selector to scope row; Tab hint anchored to scope chips
- keep input focus when opening agent menu (mousedown.preventDefault)
- redesign SegmentedPill: drop border/shadow, accent-tinted active, a11y roles
- export bucketSessionsByDate helper for overlay reuse
@graydawnc graydawnc force-pushed the feat/session-detail branch from 05a324b to 7463b74 Compare April 29, 2026 16:32
@graydawnc graydawnc marked this pull request as ready for review April 29, 2026 16:35
@graydawnc graydawnc merged commit de1717e into main Apr 29, 2026
4 checks passed
@graydawnc graydawnc deleted the feat/session-detail branch April 29, 2026 16:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant