Repository navigation
fix(app): open session row menus on the first click, and stop clipping View all - #185
Merged
Merged
Conversation
…ping View all
Two defects found while writing the sidebar tests.
The row menu ignored its first click. `createHomeModel`'s records memo
mints a fresh `{session, project}` wrapper per session on every recompute
— the projects memo alone returns a new array on a pointerdown — and the
list rendered those through a reference-keyed `<For>`, so the row was
torn down and rebuilt mid-gesture. With no surviving common ancestor the
browser fires no `click` at all, so the gesture vanished and only a
second click landed. Iterating session ids instead keeps the row DOM
across a rebuild and feeds it the new record reactively, which also stops
a background refresh from closing an open menu or dropping an in-progress
rename.
Below roughly a 380px window the flexible area falls under one row, and
"View all" was drawn into it anyway and clipped by overflow-hidden.
`fitsRecentsRow` withholds it at those sizes: whole or absent, never cut.
Both regressions are pinned. The three new or extended assertions were
each run against the unfixed code and fail there.
The same first-click defect remains on the home "View all" list, whose
rows live in upstream's home.tsx. A stable-identity cache in the
fork-only home-model would have covered both, but it does not fix that
list — its rows come from different arrays — so it is not included here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…irst click
Carries the sidebar fix to the other surface. `groupSessions` mints new
group objects on every call and the records memo mints new record
wrappers, so both `<For>` levels on the View all screen were keyed on
references that churned — tearing down the rows on recomputes that
changed nothing, including between a pointerdown and its mouseup, which
is why the ⋮ menu there ignored its first click too.
Both levels now iterate ids. DIVERGENCES gains a row: home.tsx is
upstream, and the on-conflict note spells out that reverting to
`<For each={groups()}>` reintroduces the bug.
Both new assertions were run against the unfixed home.tsx and fail there.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
quickbeard
pushed a commit
that referenced
this pull request
Aug 11, 2026
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>
quickbeard
pushed a commit
that referenced
this pull request
Aug 11, 2026
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>
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 was referenced Aug 11, 2026
quickbeard
added a commit
that referenced
this pull request
Aug 11, 2026
* docs: fold the seven sidebar entries into one The Unreleased section had grown to seven bullets that are all one piece of work: the persistent sidebar, its Recents section, the titlebar buttons, the folding header, the row menus, and two follow-ups to those. Read from a release, that is one feature, so it is now one entry carrying all six PR numbers. Written as the end state rather than as a history. #185 fixed a row menu that ignored its first click and a View all row that could be drawn clipped, and #184 restyled Delete — all of them defects in, or refinements to, work that has not shipped yet, so a reader of the released changelog never saw the behavior being corrected. What survives from them is the behavior itself: View all is left out rather than clipped in a window too short for it, and Delete is red with no ellipsis. No detail was dropped on the way: the Projects/Settings/Help move, Recents sizing and its View all row, the fold and its persistence, the narrow-window fallback, both titlebar buttons and the absence of keybinds for them, View → Toggle Sidebar, and every item in the row menu with its rename/delete/share behavior are all still stated. Released sections are untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * minor changes --------- 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.
The two defects the sidebar tests turned up, fixed on every surface that has them.
1. Session row ⋮ menus ignored their first click
Root cause, traced with a DOM-mutation and pointer-event trace.
createHomeModel's records memo mints a fresh{session, project, projectName}wrapper per session every time it runs, and it runs on recomputes that changed nothing it reads — theprojectsmemo alone hands back a new array on a pointerdown, three times over. The lists rendered those wrappers through a reference-keyed<For>, so every row's DOM was destroyed and rebuilt between pointerdown and mouseup. With no surviving common ancestor the browser fires noclickevent at all — the trace showspointerdownon the icon, the row subtree removed and re-added, thenmouseupon a different node and noclickanywhere. The second click landed because the rebuild was over.Fix: iterate stable ids, not object references, so a rebuilt record set updates rows in place.
app-sidebar.tsx, fork-only) — keyed by session id.home.tsx, upstream) — keyed at both levels:groups()by group id, and each group's sessions by session id.groupSessionsmints new group objects too, so keying only the inner list would still have rebuilt everything.This was never only about the first click. The same rebuild could close an already-open menu, or drop a rename part-way through typing, whenever anything refreshed in the background.
The ⋯ menu beside a session title was never affected — it has no such list above it.
2. View all was drawn into space too small to hold it
Below roughly a 380px-tall window the sidebar's flexible area drops under one row (12px at 380, none at 360).
recentsRowCapacityclamps the session rows to zero, but nothing withheld the View all row, and the list clips withoverflow-hidden— so it was drawn cut in half, or invisibly. NewfitsRecentsRowgates it on one whole row of space: whole or absent, never cut.Tests
Every new or extended assertion was run against the unfixed code and fails there — checked, not assumed:
sidebar-session-menu-first-click.spec.tsandhome-session-menu-first-click.spec.ts— one click opens the menu; and a forced record rebuild leaves both the open menu and the row's DOM node identity intact.sidebar-section-collapse.spec.tsnow runs past the old 400px floor down to 320.fitsRecentsRowunit tests, including a cross-check that it never disagrees withrecentsRowCapacity.Divergences
home.tsxis upstream, so it gets aDIVERGENCES.mdrow. The on-conflict note spells out that reverting to<For each={groups()}>/<For each={group.sessions}>reintroduces the bug, and points at the regression spec to use if upstream ever makes those references stable.One approach I tried and dropped: a stable-identity cache in the fork-only
home-model.tsxwould have fixed both surfaces with no upstream edit, but it does not fix the View all list — those rows come fromgroupSessions' own arrays — so I reverted it rather than leave unproven machinery in place.Verification
bun typecheck(packages/app) — cleanbun test(packages/app) — 626 pass (up 7)bun test:e2e:local— 101 pass (up 4), 2 fail inremote-session-settings.spec.ts; both fail identically on cleanmain, pre-existing and unrelatedbun lint— identical to themainbaseline (4740 warnings, 1 pre-existing error)🤖 Generated with Claude Code