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 1 commit 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: |
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 |
5341c3c to
e87c747
Compare
|
Rebased onto current The home rework ( Worth saying explicitly: the rework grouped the list by filesystem location (project → worktree), not by lineage. That narrows where a fork can hide — a child usually shares its parent's card now — but it never expresses the relation.
|
f8b35da to
731c2cf
Compare
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 group, indented by fork depth with a "└" tree connector. A child whose parent is not in the same group falls back to depth 0, so nesting never crosses group boundaries. The reorder + indent math lives in a pure, unit-tested util (utils/forkLineage.ts). Ordering runs at render time rather than while the list data is built: the list is filtered afterwards (archive toggle, search box), and a depth stamped before the filter would leave a child indented under a parent that is no longer on screen. Behind the `expForkNesting` setting, off by default. Rebased onto current main, where the home list is assembled per project group — the two original commits are squashed so the gate and the behavior it gates land together.
731c2cf to
e123b03
Compare


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.