fix: stop repeating the project name in the title bar - #1474
Merged
Conversation
The multi-project switcher (#1470) put the project name in the toolbar while `navigationTitle` printed the same string a few points away, so a plain project read "pine ⌄ pine" across one strip. `WindowChromePresentation` now resolves both surfaces together. The title carries the active file — the switcher answers "which project", the title answers "which file", as Xcode splits them. With no editor tab open there is nothing file-shaped to show, so the title falls back to the repository name and the switcher drops its now-duplicate text, keeping its icon. That state is not an edge case: a project with no restored session opens straight into a terminal (#1251), which is the first screen of every newly opened project and where the duplicate was most visible. Suppression compares the resolved strings rather than "is a file open", so an agent worktree keeps its `pine — feat/branch` label beside a `pine` title — the branch is the only thing telling two windows of one repository apart, and it must never be dropped as a duplicate. The title falls back to the repository rather than the workspace root for the same reason: a worktree root is a hashed service directory. Fixes a second defect found while checking this on screen: the switcher asked for `folder.stack`, which is not an SF Symbol and rendered as nothing. Invisible while a label sat beside it, it would have left a bare chevron once the label could be suppressed. Switcher symbols move into `MenuIcons` so `MenuIconTests` proves they resolve, and the control keeps an explicit accessibility label so an icon-only button is never nameless.
Contributor
✅ Code Coverage: 79.1745%Threshold: 70% Logic-only lines: 78534/99191 Coverage is at or above the required threshold. Generated by CI — see job summary for detailed file-level breakdown. |
This was referenced Aug 18, 2026
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.
Summary
pine — feat/branchlabel beside apinetitle — the branch is the only thing telling two windows of one repository apart.folder.stack, which is not an SF Symbol and rendered as nothing. Invisible while a label sat beside it; a bare chevron once the label could be suppressed.Why the empty-tab state mattered
A project with no restored session opens straight into a terminal (#1251) — no editor tab, so the title fell back to the project name while the switcher already showed it. That is the first screen of every newly opened project, which is exactly where the duplicate was most visible. The screenshots below are that state, before and after.
⌄ TitleFixture— switcher label suppressed nothing, and the missingfolder.stackleft a bare chevron[▣] ⌄+ titleTitleFixture[▣] TitleFixture ⌄+ titleContentView.swiftWhere the logic lives
WindowChromePresentation(new,nonisolated, no UI dependencies) resolves title and switcher label together. It also guards the degenerate inputs: blank names, a/repository (URL.lastPathComponentof the filesystem root), and a final"Pine"fallback — a window with an empty title disappears from the Window menu and Mission Control.Switcher SF Symbols moved into
MenuIconssoMenuIconTestsproves they resolve, and the switcher keeps an explicitaccessibilityLabelso the icon-only form never reaches VoiceOver as an unnamed button.Related issue
N/A — reported directly during review of the 2.4.0 release PR (#1471).
Test plan
Unit — 20 tests in
WindowChromePresentationTests, including the invariant "no string is ever printed twice" over eight input shapes, plus the negative and hostile paths: blank/whitespace-only names, filesystem-root/, both inputs degenerate at once, canonically-equivalent Unicode spellings (precomposed vs decomposed) treated as duplicates, embedded newlines preserved rather than rewritten, a 4096-character name left untruncated, and a file named exactly like its project.Unit — 10 new cases in
MenuIconTestscovering every switcher and worktree-state symbol, so a non-existent name fails a test instead of silently rendering blank.UI —
EditorWindowTests.testWindowTitleShowsActiveFileInsteadOfRepeatingProjectName: asserts the window is addressable asTitleFixturewith no tab open, becomesmain.swiftonce a file is opened, thatTitleFixtureno longer exists as a window title at that point, retitles on tab switch, and retitles again on tab close.Local runs (macOS 27):
WindowChromePresentationTests+MenuIconTests— 24 tests, passPineTests— 754 tests; 4AgentInboxToolbarButtonSnapshotTestsfailures and intermittentTabTransferSafetyTests/ApplicationLifecycleProcessTeststimeouts. Both reproduce on unmodifiedmain— the snapshot baselines are recorded on macOS 26 CI, and the timeouts are load-sensitive. Re-run of those suites with this change, isolated: 71 tests, pass.--reset-state, which clears saved sessions, and a working Pine with live agent sessions was running on the machine. macOS 26 CI is the execution gate.check_ui_test_shards.py --max-delta 3— 40 classes, 232 tests, delta 2. Stale shard counts inci.ymlcomments updated to match.Compatibility matrix
Complete environment output:
Terminal renderer coverage
--disable-metal): N/AManual testing checklist
Verified by running the built app against a fixture project and reading the title bar in both states — with a restored editor tab (
TitleFixture ⌄+ContentView.swift) and with none (⌄icon only +TitleFixture). The missingfolder.stacksymbol was found this way and not from the diff.Not verified by hand: light appearance (no colors changed, only which text renders), the Window menu entry for a retitled window, and switching between two projects in one window — all covered by CI or unchanged code paths.