Repository navigation
feat(app): one New session tab, at the front of the strip - #192
Merged
Merged
Conversation
Draft tabs persist until they are promoted into a session or closed, so every click on New session pushed another identical empty draft into the tab bar. Focus the one already open for that server and directory instead, and only create a draft when there is none to go to. Matched on server and directory because a draft belongs to a project; jumping to one pointing elsewhere would be a worse answer than opening a new draft. LaunchNewSession already reused a draft at boot for the same reason — this extends it to every deliberate New session. Covers the three labelled affordances, all of which route through createHomeModel: the sidebar row, the recent-sessions screen button and a project menu's New session. The tab bar's + and mod+t are left alone — they are `tab.new`, and always producing a new tab is the point. Both e2e assertions were run against the unfixed code and fail there, stacking two and three drafts respectively. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both share `openNewTab`, which resolved a project from whatever was on screen and then opened a draft unconditionally. All five of its resolution branches now go through a local `openDraftFor`, which selects the draft already open for that server and directory and only creates one when there is none. They are labelled "New session" and reached for with the same intent as the sidebar row, so splitting the behaviour on which control you happened to use was the wrong line to draw. Home (mod+b) already reused a draft; this makes the three consistent. The + gains data-action="titlebar-new-tab". Without it the e2e selector matched by accessible name, which the sidebar row also carries — the first version of that test silently re-tested the sidebar path instead. DIVERGENCES row 120 said Home's reuse was deliberately unlike the New session button, "which is expected to always make a new one". That is no longer true, so the rationale is corrected rather than left contradicting the shipped behaviour. Both new e2e assertions fail against the unfixed titlebar. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`newDraft` appended, so with several sessions open the draft landed at the far right and could scroll out of view. It unshifts now. Session tabs still append; nothing else about draft handling changes. Knock-on worth knowing: `promoteDraft` replaces the draft at its own index, so the session keeps the head position once the first message is sent and sessions accumulate newest-first. Recorded in the DIVERGENCES row, including where to change it if that is ever unwanted — moving the tab in promoteDraft, not reverting the insert. The ordering test fails against the appending version. 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.
Two changes to how the New session tab behaves in the tab bar.
1. It goes to the draft you already have open
Starting a new session added another empty draft tab every time, so a few clicks left a row of identical ones. It now goes to the draft already open for that project, and only starts one when there isn't a draft to go to.
Every way in is covered:
createHomeModel.openProjectNewSessiontitlebar.openNewTabmod+t/mod+n(tab.new)Home (
mod+b) already reused a draft; this makes them consistent.openNewTabresolves a project through five fallback branches depending on what's on screen, and all five now go through a localopenDraftFor, so the rule holds however the project was worked out.Matched on server and directory — a draft belongs to a project, so starting a session in project B never lands you in project A's draft. A draft with text in it is reused, not replaced, so you land back on what you were writing; the trade is no one-click route to a second blank draft without closing the first. The model carried from the active tab still seeds a new draft only — a reused one keeps its own, as clicking its tab would.
2. It opens at the front of the strip
newDraftappended, so with several sessions open the draft sat at the far right and could scroll out of view. It unshifts now. Session tabs still append.Knock-on worth knowing:
promoteDraftreplaces the draft at its own index, so once you send the first message that session keeps the head position and sessions accumulate newest-first. I took that as the better default — the tab you're working in shouldn't jump across the strip mid-flow — but it is a visible change to session order, and it's easy to reverse: move the tab inpromoteDraft, which the DIVERGENCES row spells out. Reverting the insert instead would just put New session back at the end.Tests
findOpenDraftis a pure helper with 6 unit tests: finds the project's draft, ignores session tabs, misses on a different directory, misses on a different server, takes the first of several, empty list. Typed structurally —directoryoptional, since session tabs carry none — so it needs nothing from the tabs context at build or run time.Five e2e tests cover the real flow, and all five fail against the unfixed code — stacking two and three drafts, and rendering the draft last. They navigate away between clicks so each is a real navigation rather than a no-op on the page already showing.
The
+gainsdata-action="titlebar-new-tab". Without it the selector matched by accessible name — which the sidebar row also carries — so the first version of that test silently re-tested the sidebar path instead of the button it named.Divergences
Two upstream files, both recorded:
titlebar.tsx— row 120 grows a fourth change, and needed a correction: it recorded Home's reuse as deliberately unlike the New session button, "which is expected to always make a new one". That is no longer the fork's position, and leaving it would have told a future syncer to restore the stacking.context/tabs.tsx— a new row for theunshift, including thepromoteDraftknock-on and where to change it.home-model.tsxis fork-only and the other files are new.Verification
bun test:e2e:local— 139 passed, 0 failedbun test(packages/app) — 644 passbun typecheck(packages/app) — cleanbun lint— 4742 warnings + 1 error, four belowmain's 4746: collapsing the fivenewDraftcall sites in the titlebar removed them.🤖 Generated with Claude Code