feat(app): nest forked sessions under their parent in the session list - #1423
feat(app): nest forked sessions under their parent in the session list#1423chphch wants to merge 2 commits into
Conversation
fb101ed to
7442723
Compare
28bac10 to
4fd3ca7
Compare
4fd3ca7 to
ce6b6d2
Compare
|
Rebased onto current Why it stopped workingThe session list moved to project cards. What changed
ProofThree sessions in one project card, created via the daemon's Setting on — the child is reordered directly under its parent, indented with a Setting off — original newest-first order, all three rows at the same offset, no connector: |
Forked sessions already persist `parentSessionId` in their encrypted metadata (set on every fork path — Fork action, duplicate-from-message, and the MCP open_session tool), but the session list ignored it: a fork appeared as an unrelated row sorted only by recency, so the lineage was invisible. Render forked children directly under their parent within each list section, indented by fork depth with a "└" tree connector. A child whose parent is not in the same section falls back to depth 0, so nesting never crosses section boundaries. Both the compact active-sessions rows and the full inactive (by-date) rows are covered. The reorder + indent math lives in a pure, unit-tested util (utils/forkLineage.ts); storage.ts nests within each date group and the active group nests per project group. Visual indent caps at a max depth so deep chains never march off-screen (forkDepth itself stays accurate). Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
Put the fork-lineage nesting behind a new `expForkNesting` setting (off by default) so it ships as an opt-in experiment rather than changing every user's session list. When off, rows keep their original order and depth 0, so the renderer shows no indentation or connectors (upstream layout). - settings: add `expForkNesting` (default false) - storage: thread the flag into buildSessionListViewData; rebuild the list on settings change so the toggle applies immediately - ActiveSessionsGroupCompact: gate the per-project fork reorder on the setting - settings/features: add the toggle + i18n strings for all languages Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
ce6b6d2 to
7bb06c5
Compare
|
Fixed a visibility bug in the connector itself, folded into the first commit. The connector is drawn with
Measured rather than eyeballed, since this is a contrast bug — sampling pixels from the rendered app at 1280:
Forcing the old colour back on the same element reproduces the bug exactly, and a whole-image diff between the two is 46 pixels — all inside the connector rect, nothing else moved. The unselected rendering is unchanged. Scope of the measurement: light web theme and the compact row path. The |


Forked sessions already persist
parentSessionIdin their (encrypted) metadata — it's set on every fork path (the Fork quick-action, duplicate-from-message, and the MCPopen_sessiontool) and already surfaced as the "Forked From" link on the session info screen — but the session list ignored it, so a fork showed up as an unrelated row sorted only by recency and you couldn't tell at a glance what was branched from what.This renders forked children directly under their parent within each list section (the active project group, and each by-date group), indented by fork depth with a
└tree connector. A child whose parent isn't in the same section falls back to depth 0 so nesting never crosses section boundaries, and the visual indent caps at a max depth so deep chains never march off-screen (the underlying depth stays accurate).Gated behind an experimental
expForkNestingsetting, off by default. With it off the session list is byte-for-byte the upstream layout — rows keep their original order and stay at depth 0, so the renderer draws no indentation and no connectors. Users opt in from Settings → Features.Shape of the change
utils/forkLineage.ts(new) — the pure reorder + indent math, dependency-free and unit-tested.sync/storage.ts—SessionRowDatagainsparentSessionIdandforkDepth; a newbuildOrderedSessionRowsapplies the lineage ordering when the flag is on and returns rows untouched at depth 0 when it's off, so the flag check lives in exactly one place.forkDepthfor their indent padding.Proof
Synthetic parent → two children → grandchild sessions rendered through the real
SessionsListon Expo web (standalone server,dev_tokenURL bypass, driven with Playwright). Left = flag off (forks scattered by recency, lineage invisible); right = flag on (children nested under their parent with a└connector; the grandchild sits one level deeper). Both render paths are shown — the compact active group (top) and the full by-date "Today" group (bottom).Verification
pnpm typecheckclean; app suite green.utils/forkLineage.test.tscovers the ordering — multi-level chains, multiple children of one parent, the parent-not-in-section fallback, a parent-cycle guard, and deep-equal reference stability.