Skip to content

fix: stop repeating the project name in the title bar - #1474

Merged
batonogov merged 1 commit into
mainfrom
worktree-fix-title-duplication
Aug 18, 2026
Merged

fix: stop repeating the project name in the title bar#1474
batonogov merged 1 commit into
mainfrom
worktree-fix-title-duplication

Conversation

@batonogov

Copy link
Copy Markdown
Owner

Summary

  • The project name stopped appearing twice in the title bar. The window title now carries the active file; the toolbar switcher keeps the project. With no editor tab open the title falls back to the project and the switcher drops its duplicate text, keeping its icon.
  • Duplicate detection compares the resolved strings, not "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.
  • Fixed a second defect found 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; 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.

Title bar
Before ⌄ TitleFixture — switcher label suppressed nothing, and the missing folder.stack left a bare chevron
After (no tab) [▣] ⌄ + title TitleFixture
After (file open) [▣] TitleFixture ⌄ + title ContentView.swift

Where 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.lastPathComponent of 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 MenuIcons so MenuIconTests proves they resolve, and the switcher keeps an explicit accessibilityLabel so 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 tests added/updated
  • UI tests added/updated (if applicable)

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 MenuIconTests covering 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 as TitleFixture with no tab open, becomes main.swift once a file is opened, that TitleFixture no 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, pass
  • Full PineTests — 754 tests; 4 AgentInboxToolbarButtonSnapshotTests failures and intermittent TabTransferSafetyTests / ApplicationLifecycleProcessTests timeouts. Both reproduce on unmodified main — 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.
  • UI tests were not run locally: they launch with --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.
  • SwiftLint — clean on all changed files.
  • check_ui_test_shards.py --max-delta 3 — 40 classes, 232 tests, delta 2. Stale shard counts in ci.yml comments updated to match.

Compatibility matrix

  • macOS 26: Not tested locally — macOS 26 GitHub CI is required before merge
  • macOS 27 beta: Tested — macOS 27.0 build 26A5406e
  • Xcode: Xcode 27.0 build 27A5218g
  • macOS SDK: 27.0 build 26A5406c

Complete environment output:

ProductName:		macOS
ProductVersion:		27.0
BuildVersion:		26A5406e
Xcode 27.0
Build version 27A5218g
27.0
26A5406c

Terminal renderer coverage

  • Default path (Metal when available): N/A — no terminal code touched; the change is title-bar text
  • CoreGraphics (--disable-metal): N/A

Manual testing checklist

  • Verified the change works as expected
  • No regressions in related features

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 missing folder.stack symbol 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.

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.
@github-actions

Copy link
Copy Markdown
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.

@batonogov
batonogov merged commit a621921 into main Aug 18, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant