feat(sessions): Batch 1 PR-2 — pinned zone + manual hide

Merged
GR
4mo ago
+827-13

Summary

Session-finding Batch 1 PR-2 — pins & manual hide (plan: docs/session-finding-plan.md §4.4). Follows PR #132; design confirmed with the user (pinned-section model chosen over a separate favorites view — zero mode-switch for the highest-frequency path).

📌 Pins

  • Pinned zone at the top of the Sessions list: collapsible header (▾ 📌 Pinned (N)), collapsed state remembered (localStorage). Rows fully reuse the existing session row — status dots, badges, PR links, snippets all work.
  • Dual placement: a pinned session also keeps its chronological spot in the timeline, marked with a gold ★ — the zone is a shortcut, not a move.
  • Pin/unpin: hover 📌 icon on the selected row, or ⌘D.
  • Works for any displayed row, including deep-search matches beyond the loaded ~100: the zone resolves those via the new get-sessions-by-ids IPC (reads the full session cache) and lazily enriches them.
  • While searching, the zone steps aside — results are one unified list (★ still marks pinned matches).

⊘ Manual hide (C1's missing piece)

  • Hover ⊘ icon or ⇧⌘D: forces the session into the minor-sessions fold regardless of its stats. Reversible from inside the expanded fold (the icon shows as active); hidden sessions remain searchable (search always shows everything).
  • Pin and hide are mutually exclusive: pinning unhides, hiding unpins (encoded in the pure transition helpers).

Store

~/.config/codev/session-marks.json — single cross-account file next to the accounts registry (JSON on purpose: tiny data, human-debuggable, no SQLite features needed):

{ "version": 1,
  "pins": { "<sessionId>": { "pinnedAt": "…", "cwd": "…", "accountLabel": "…", "group": null } },
  "hidden": ["<sessionId>"] }
  • fs.watch (parent-dir watch, filename-filtered — the atomic temp+rename write replaces the inode, which would detach a file watcher) → debounced push to the renderer, same pattern as the status files.
  • IPC responses also return the updated marks for instant UI feedback (watch push is the backup path).
  • sessionIds are resume-stable (verified on Claude Code 2.1.207: --resume/--continue reuse the id; only --fork-session forks), so plain sessionId keying needs no migration logic. group field reserved for v2 named groups.

Tests

  • 7 new unit tests (session-marks.test.ts): normalize (garbage/defaults/dedupe), pin↔hide mutual exclusion, idempotent removals, input immutability, tmpdir file roundtrip incl. corrupt-file recovery and no temp leftovers — 52 total pass; tsc --noEmit clean.

Out of scope

  • Named pin groups (v2; schema field reserved), drag reordering.
  • /pin in-session slash command (Batch 3, plan §6).
  • Expired-session badge for pinned-but-cleaned transcripts (#135).

🤖 On behalf of @grimmerk — generated with Claude Code


Summary by cubic

Adds session pins and manual hide with a collapsible 📌 Pinned zone. Pinned sessions live only in the zone while browsing, remain searchable, and marks sync across accounts via ~/.config/codev/session-marks.json.

  • New Features

    • Collapsible 📌 Pinned zone; collapse state remembered. New pins append to the bottom.
    • Pin/unpin via hover 📌 or Cmd+D. Hide/unhide via Shift+Cmd+D; pin and hide are mutually exclusive.
    • While searching, the zone hides; results are one list with a ★ on pinned matches.
    • Deep-search pins resolve via getSessionsByIds + lazy enrichment; unresolved pins render placeholders with '… msgs' so Pinned(N) stays stable.
    • Zone rows include Hide (unpin + fold). Hidden rows show a dim ⊘; the minor fold label adds “N hidden.”
  • Bug Fixes

    • Marks watcher closes on error, self-heals with bounded backoff, and logs retries; extracted path-based watchMarksFile; new test covers the watcher error path (53 tests total).
    • webContents.send guarded with isDestroyed(); marks listener returns an unsubscribe and cleans up; all marks IPC calls .catch errors; audit logs added for pin/unpin/hide.
    • Stale getSessionsByIds responses are dropped; collapsing the pinned zone snaps selection to the top.
    • Suppress hover re-selection during pin/hide layout shifts; selection re-anchors to the same session; Cmd+D requires an explicit selection; keyboard shortcuts keep working after clicking icons/folds.

Summary by CodeRabbit

  • New Features
    • Added a collapsible 📌 Pinned sessions zone with per-session ordering (★), remembered collapse state, and a minor-sessions hide fold.
    • Pin/hide uses keyboard shortcuts and row hover controls, enforces pin/hide mutual exclusivity, and keeps pinned results included during search (with placeholders for unresolved pins).
    • Persisted pin/hide state in ~/.config/codev/session-marks.json, shared across accounts, with live sync when the file changes.
  • Documentation
    • Updated the session switching documentation with the new pin/hide workflows and shortcuts.
  • Tests
    • Added unit coverage for marks normalization, pin/hide transitions, file roundtrips, and watcher error recovery.