Repository navigation
feat(app): make the sidebar Recents section collapsible - #183
Merged
Merged
Conversation
Extracts the header/fold behaviour into a fork-only `SidebarSection` component so the pinned-sessions section can reuse it: it owns the toggle affordance, the aria wiring, and the growth rule that an open section is the flexible child of the column while a collapsed one shrinks to its header. Collapse state is persisted globally under `sidebar.sections`, keyed by section name, so a second section slots in without a new stored key. Projects takes the free height whenever no section below it is expanded, which keeps Settings and Help anchored to the bottom of the column. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Growing the Projects scroll area to absorb the slack an expanded Recents had been holding also pushed the collapsed header down to sit on top of Settings/Help. Pin the bottom nav with an auto margin instead, so the slack opens between the last section and the bottom nav and the header stays where it was. The layout spec only asserted that Settings did not move, which the broken version satisfied too; it now pins the section's own position as well and fails by 388px against the previous commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 11, 2026
quickbeard
pushed a commit
that referenced
this pull request
Aug 11, 2026
#173, #175 and #179 shipped with no tests; #181 and #183 already carried their own, so this fills the gap left by the other three. Unit (test-browser, since home-model reaches kobalte through the directory picker and will not import under the server condition): - buildHomeSessionRecords: directory filtering, pathKey normalisation so a Windows path and a trailing slash are one directory, sort by last update with a created-time fallback, dedupe keeping the fresher copy, sandbox and projectID resolution, and dropping a session no project claims. - descendantIDs: the subtree a delete evicts, including cycle safety. Exported for this, alongside buildHomeSessionRecords which was already exported. - session-share-request: the row-to-timeline share handoff consumes once, and a request meant for another session is not swallowed. E2E: - sidebar-persistence: the column survives opening a session, orders recents newest first, opens the session a row names, and View all and New session both work from inside a session. - sidebar-toggle-search: the titlebar toggle hides and restores the column, survives a reload and a route change, and Search lands on the recents list with its field focused and the ?focus=search param cleared. - session-actions-menu: the four actions on both lists, Share hidden when the server disables sharing, rename in place without opening the session, cancel sending nothing, and delete asking first. Two defects turned up while writing these, both left unfixed and documented where they bite: - Clicking a Recents row opens the session but never marks the row as open; the marker only appears once the tab list is read back on a fresh mount. Arriving by URL marks it immediately, so the state is right and the click path fails to re-run the memo. - The first activation of a row's actions menu is swallowed. Four clicks give closed, open, closed, open, and focus + Enter is swallowed the same way, so a user's first click on the dots lands on nothing too. 623 unit, 45 browser-condition unit and all 110 e2e regression tests pass; tsgo -b and the e2e project both typecheck. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
quickbeard
added a commit
that referenced
this pull request
Aug 11, 2026
* test(app): cover the sidebar, titlebar and session-row menu PRs #173, #175 and #179 shipped with no tests; #181 and #183 already carried their own, so this fills the gap left by the other three. Unit (test-browser, since home-model reaches kobalte through the directory picker and will not import under the server condition): - buildHomeSessionRecords: directory filtering, pathKey normalisation so a Windows path and a trailing slash are one directory, sort by last update with a created-time fallback, dedupe keeping the fresher copy, sandbox and projectID resolution, and dropping a session no project claims. - descendantIDs: the subtree a delete evicts, including cycle safety. Exported for this, alongside buildHomeSessionRecords which was already exported. - session-share-request: the row-to-timeline share handoff consumes once, and a request meant for another session is not swallowed. E2E: - sidebar-persistence: the column survives opening a session, orders recents newest first, opens the session a row names, and View all and New session both work from inside a session. - sidebar-toggle-search: the titlebar toggle hides and restores the column, survives a reload and a route change, and Search lands on the recents list with its field focused and the ?focus=search param cleared. - session-actions-menu: the four actions on both lists, Share hidden when the server disables sharing, rename in place without opening the session, cancel sending nothing, and delete asking first. Two defects turned up while writing these, both left unfixed and documented where they bite: - Clicking a Recents row opens the session but never marks the row as open; the marker only appears once the tab list is read back on a fresh mount. Arriving by URL marks it immediately, so the state is right and the click path fails to re-run the memo. - The first activation of a row's actions menu is swallowed. Four clicks give closed, open, closed, open, and focus + Enter is swallowed the same way, so a user's first click on the dots lands on nothing too. 623 unit, 45 browser-condition unit and all 110 e2e regression tests pass; tsgo -b and the e2e project both typecheck. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(app): drop the workarounds #185 makes unnecessary Both specs carried a detour around a defect, each with a comment explaining why. #185 fixes both, so the detours are now dead weight and the comments are wrong. - session-actions-menu: openRowMenu clicked a second time when the first was swallowed. One click now, and the helper no longer needs the page. - sidebar-persistence: "marks the row whose session is already open" reloaded before asserting data-selected. It asserts on the click that opened the session. The hover in openRowMenu stays. That one is not a workaround: the trigger is pointer-events-none until its row is hovered, and Playwright hit-tests before it moves the mouse. Checked both ways rather than assumed. On #185's branch the three specs pass 20/20; on main without it, both edited tests fail — the menu never reports aria-expanded=true, and no row is ever marked selected. So this must merge after #185. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Quickbeard <quickbeard@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Clicking the Recents header in the sidebar now folds the section away — the New session row, the session list and View all all hide, and a chevron beside the title shows which way it is. The choice persists across launches.
The fold behaviour lives in a new fork-only
SidebarSectioncomponent rather than inSidebarRecents, because the pinned-sessions section is coming next and needs the same three things:aria-expandedalone names the control and there is no extra string to translate),home.tsx,Collapse state is persisted under a single global
sidebar.sectionskey, a record keyed by sectionname, so the pinned section slots in without a second stored key. Sections default to open: an absent entry is an expanded section.One knock-on: an expanded Recents is the flexible child that holds the bottom nav down, so with every section collapsed nothing grows and the column has slack to place. It belongs between the last section and the bottom nav — the Recents header stays put under the last project, and Settings/Help stay on the bottom edge — so
HomeUtilityNavgets an auto margin. (Letting the Projects list grow into that slack instead is wrong: it sinks the collapsed header to the bottom of the column.)New sessionstays one click away in the titlebar while Recents is folded, so collapsing the section does not strand that action.Divergences
No upstream file changes. Both new files are fork-only, and
app-sidebar.tsxis fork-only too.DIVERGENCES.mdrow for the persistent app sidebar gets one clause:HOME_SECTION_LABEL— an export the fork added tohome.tsx— is now consumed bysidebar-section.tsxrather than directly byapp-sidebar.tsx, which is the kind of thing that row exists to tell a future syncer.Verification
bun typecheck(packages/app) — cleanbun test(packages/app) — 619 passsidebar-section-collapse.spec.ts: collapse + persistence across reload, and that collapsing moves neither the section header nor the bottom nav. The layout assertion was checked against the broken layout and fails there by 388px, so it is not passing vacuously.bun test:e2e:local— 93 pass, 2 fail inremote-session-settings.spec.ts; both fail identically on a cleanmaincheckout, so they are pre-existing and unrelatedbun lint— identical to the baseline onmain(4740 warnings, 1 pre-existing error insession-timeline.fixture.ts)🤖 Generated with Claude Code