Repository navigation
Expose stable window title templates - #6059
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a WindowTitleTemplate type and Defaults key, registers and parses ChangesWindow Title Template Feature
🎯 3 (Moderate) | ⏱️ ~25 minutes
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (2 errors, 1 warning)
✅ Passed checks (18 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryAdds an opt-in
Confidence Score: 5/5Safe to merge; the change is purely additive, default behavior is unchanged, and all prior review findings are resolved. The feature is opt-in and fully isolated behind the blank-default guard in WindowTitleTemplate.configured(). The three issues flagged in the previous review round are all corrected. The only remaining item is a formatting inconsistency on one line in the settings parser, which has no behavioral impact. Sources/KeyboardShortcutSettingsFileStore.swift — the windowTitleTemplate parsing block is compressed to a single long line, unlike every other parser in that function. Important Files Changed
Sequence DiagramsequenceDiagram
participant WS as Workspace
participant NC as NotificationCenter
participant TM as TabManager
participant WTT as WindowTitleTemplate
participant UD as UserDefaults
participant WIN as NSWindow
WS->>NC: post(.workspaceCurrentDirectoryDidChange, userInfo:[workspaceId:])
NC->>TM: closure (queue: .main)
TM->>TM: workspaceCurrentDirectoryDidChange(workspaceId:)
TM->>TM: "guard workspaceId == selectedTabId"
TM->>WTT: configured() → reads UserDefaults
UD-->>WTT: rawValue string
WTT-->>TM: WindowTitleTemplate?
TM->>WTT: resolved(context: WindowTitleTemplateContext)
WTT-->>TM: final title string
TM->>WIN: "title = trimmedResolvedTitle"
Note over TM,UD: On settings reload, AppDelegate calls refreshWindowTitlesAcrossMainWindows() which fans out to each unique TabManager
Reviews (8): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Sources/TabManager.swift`:
- Around line 6811-6812: The code trims resolvedTitle into trimmedResolvedTitle
but still returns resolvedTitle, allowing leading/trailing whitespace to leak
into NSWindow.title; update the return in the method that computes the window
title to return trimmedResolvedTitle.isEmpty ? defaultTitle :
trimmedResolvedTitle (use trimmedResolvedTitle in the non-empty case instead of
resolvedTitle) so the trimmed result is applied to NSWindow.title.
In `@web/data/cmux.schema.json`:
- Around line 269-274: Update the fallback wording for the windowTitleTemplate
property so both schema and locale match the actual behavior: in
web/data/cmux.schema.json (lines 269-274) revise the "description" for
"windowTitleTemplate" to state that a blank value preserves the existing default
title (which may include the current directory) rather than only the
"active-workspace title", and keep the list of supported placeholders unchanged;
in web/messages/en.json (lines 866-869) make the mirror change to the English
help text so it also says a blank value preserves the existing default
title/current-directory fallback instead of implying it only uses the
active-workspace title.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: a09b546a-bfc6-46bc-9851-56f75222a83c
📒 Files selected for processing (14)
Packages/CmuxSettings/Sources/CmuxSettings/Keys/AppCatalogSection.swiftSources/App/WindowTitleTemplate.swiftSources/AppDelegate.swiftSources/CmuxSettingsJSONPathSupport.swiftSources/KeyboardShortcutSettingsFileStore+Template.swiftSources/KeyboardShortcutSettingsFileStore.swiftSources/TabManager.swiftcmux.xcodeproj/project.pbxprojcmuxTests/WindowTitleTemplateTests.swiftdocs/configuration.mdweb/app/[locale]/docs/configuration/page.tsxweb/data/cmux.schema.jsonweb/messages/en.jsonweb/messages/ja.json
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
Sources/TabManager.swift (2)
3786-3803:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMirror PR-tracker cleanup in every workspace-removal path.
closeWorkspace(_:)tears down bothsidebarGitMetadataServiceandpullRequestProbing, butdetachWorkspace(tabId:)andrestoreSessionSnapshot(_:)now only clear the git-probe side. After a window move or full session restore, thisTabManagercan keep polling orphaned workspace/panel IDs until deinit, which is the kind of entrypoint drift the repo explicitly tries to avoid. As per coding guidelines, shared behavior exposed through multiple entrypoints should use one shared action/model path and verify every entrypoint that invokes it.Also applies to: 8098-8102
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/TabManager.swift` around lines 3786 - 3803, detachWorkspace(tabId:) and restoreSessionSnapshot(_:) only clear sidebarGitMetadataService but not pullRequestProbing, causing orphaned polling; extract or reuse a shared teardown action (e.g. teardownProbesForWorkspace(workspaceId: UUID) or reuse the same helper closeWorkspaceDidTearDownProbes) and call it from detachWorkspace(tabId:) and restoreSessionSnapshot(_:) so both sidebarGitMetadataService.clearWorkspaceGitProbes(workspaceId:) and pullRequestProbing.clearProbes(forWorkspaceId:) (or the existing method used by closeWorkspace(_:)) are invoked for the removed workspace, leaving other teardown steps (groupId=nil, unwireClosedBrowserTracking, recentlyClosedBrowsers.removeSnapshots, owningTabManager=nil) unchanged.Source: Coding guidelines
1149-1202: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy liftExtract the new sidebar-git / welcome lifecycle code instead of growing
TabManager.swiftfurther.These additions push even more unrelated behavior into an already 8k+ line file that is simultaneously doing workspace lifecycle, window integration, session restore, UI-test harnessing, and now sidebar-git composition plus welcome-command readiness. Please move the new subsystem wiring/helpers into dedicated
Sources/files before adding more here. As per coding guidelines, production Swift files over 800 lines and mixed-responsibility files should be split rather than expanded further.Also applies to: 1792-1850
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/TabManager.swift` around lines 1149 - 1202, This large TabManager.swift init block that constructs SidebarGitMetadataService, PullRequestProbeService/PullRequestPollService, sharedWorkspaceGitProbeLimiter, and wires attach(host:) (symbols: SidebarGitMetadataService, PullRequestProbeService, PullRequestPollService, sharedWorkspaceGitProbeLimiter, sidebarGitMetadataService, pullRequestProbing, attach(host:)) should be extracted into a dedicated small composition/coordinator type (e.g. SidebarGitCoordinator) in its own source file: move the creation of pullRequestProbeService, pullRequestPollService, the SidebarGitMetadataService instantiation, and the probeLimiter defaulting logic into that coordinator, implement a simple API (init(...) and attachHost(_ host: SidebarGitHosting) or similar) and have TabManager only create the coordinator and call its attachHost(self) and store only the minimal seam(s) (or forward calls). Replace the large init wiring in TabManager with a 1–2 line creation/forward to the new coordinator, keeping all behavior identical (preserve debugLog lambda, defaulting to Self.sharedWorkspaceGitProbeLimiter, and types used), and move any welcome/ready-to-run helper logic related to sidebar-git into the same new file or a separate small helper to keep TabManager under the 800-line, single-responsibility limit.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Sources/AppDelegate.swift`:
- Line 1053: startupSessionSnapshot is mutable outside its intended owner;
restrict mutation to AppDelegate by changing its declaration in AppDelegate from
"var startupSessionSnapshot: AppSessionSnapshot?" to "private(set) var
startupSessionSnapshot: AppSessionSnapshot?" (or make it private and add a
public read-only accessor property) so only AppDelegate can write it while
AppDelegate+WindowIdentity.swift can continue to read it; update any call sites
that attempt to mutate it outside AppDelegate (if any) to use an AppDelegate
method instead.
In `@web/messages/uk.json`:
- Line 767: Update the Ukrainian string for the "windowTitleTemplate" key to
replace the awkward phrase "відступом до поточного каталогу" with wording that
clearly means “fallback to the current directory” (for example, "поверненням до
поточного каталогу" or "з використанням поточного каталогу як запасного
варіанта"); keep the rest of the sentence unchanged so the full value still
lists supported placeholders and context.
---
Outside diff comments:
In `@Sources/TabManager.swift`:
- Around line 3786-3803: detachWorkspace(tabId:) and restoreSessionSnapshot(_:)
only clear sidebarGitMetadataService but not pullRequestProbing, causing
orphaned polling; extract or reuse a shared teardown action (e.g.
teardownProbesForWorkspace(workspaceId: UUID) or reuse the same helper
closeWorkspaceDidTearDownProbes) and call it from detachWorkspace(tabId:) and
restoreSessionSnapshot(_:) so both
sidebarGitMetadataService.clearWorkspaceGitProbes(workspaceId:) and
pullRequestProbing.clearProbes(forWorkspaceId:) (or the existing method used by
closeWorkspace(_:)) are invoked for the removed workspace, leaving other
teardown steps (groupId=nil, unwireClosedBrowserTracking,
recentlyClosedBrowsers.removeSnapshots, owningTabManager=nil) unchanged.
- Around line 1149-1202: This large TabManager.swift init block that constructs
SidebarGitMetadataService, PullRequestProbeService/PullRequestPollService,
sharedWorkspaceGitProbeLimiter, and wires attach(host:) (symbols:
SidebarGitMetadataService, PullRequestProbeService, PullRequestPollService,
sharedWorkspaceGitProbeLimiter, sidebarGitMetadataService, pullRequestProbing,
attach(host:)) should be extracted into a dedicated small
composition/coordinator type (e.g. SidebarGitCoordinator) in its own source
file: move the creation of pullRequestProbeService, pullRequestPollService, the
SidebarGitMetadataService instantiation, and the probeLimiter defaulting logic
into that coordinator, implement a simple API (init(...) and attachHost(_ host:
SidebarGitHosting) or similar) and have TabManager only create the coordinator
and call its attachHost(self) and store only the minimal seam(s) (or forward
calls). Replace the large init wiring in TabManager with a 1–2 line
creation/forward to the new coordinator, keeping all behavior identical
(preserve debugLog lambda, defaulting to Self.sharedWorkspaceGitProbeLimiter,
and types used), and move any welcome/ready-to-run helper logic related to
sidebar-git into the same new file or a separate small helper to keep TabManager
under the 800-line, single-responsibility limit.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 68df73fa-a7a4-40cd-92a8-2513c07ad07e
📒 Files selected for processing (27)
Sources/AppDelegate+WindowIdentity.swiftSources/AppDelegate.swiftSources/KeyboardShortcutSettingsFileStore.swiftSources/TabManager+WindowTitle.swiftSources/TabManager.swiftcmux.xcodeproj/project.pbxprojweb/data/cmux.schema.jsonweb/messages/ar.jsonweb/messages/bs.jsonweb/messages/da.jsonweb/messages/de.jsonweb/messages/en.jsonweb/messages/es.jsonweb/messages/fr.jsonweb/messages/it.jsonweb/messages/ja.jsonweb/messages/km.jsonweb/messages/ko.jsonweb/messages/no.jsonweb/messages/pl.jsonweb/messages/pt-BR.jsonweb/messages/ru.jsonweb/messages/th.jsonweb/messages/tr.jsonweb/messages/uk.jsonweb/messages/zh-CN.jsonweb/messages/zh-TW.json
PRs included: - AppDelegate decomposition: CmuxSession session-snapshot repository (manaflow-ai#6030) - Fix Cmd+T cwd after session restore (manaflow-ai#6055) - Speed up iOS terminal scroll rendering (manaflow-ai#6035) - Preserve Pi sessions across workspace restore (manaflow-ai#5607) - Scope Biome checks to maintained JS sources (manaflow-ai#6008) - Fix manaflow-ai#5917: restore OSC 11 pane-local backgrounds (manaflow-ai#5997) - Expose stable window title templates (manaflow-ai#6059) - Honor macos-option-as-alt left/right - Fix macOS 27 SF Symbol rasterization crash (manaflow-ai#5999) - CmuxRemote* family: extract Workspace remote/cloud-VM connectivity - Fix iOS workspace swipe-delete confirmation crash (manaflow-ai#6051) - TabManager decomposition Wave 3+4 sub-models - Sidebar row cleanups: branchless frame anchor - CmuxIPCService: extract AppDelegate multi-window CLI routing - CmuxSidebarGit: extract TabManager git-metadata + PR-polling subsystem - CmuxTerminalCore: extract terminal core leaf Fork-side adjustments: - ghostty submodule: cherry-pick mouse-modifier-state fix onto our renderer-realized branch - Workspace.swift: take theirs (upstream extracted ~7700 lines into CmuxCore.Remote/CmuxRemoteSession packages); restore fork's renameTopLevelLayoutTabContaining/closeTopLevelLayoutTabContaining + surfaceTmuxClientTTYNames + WorkspaceLayoutTab integration - TabManager.swift: take theirs; re-add static allocatePortOrdinal() - BrowserPanelView, RenderableSystemSymbol: keep fork's cmuxSymbolPixelSize extension on top of upstream's cmuxSymbolRasterSize - Add CmuxWorkspaces / CMUXSessionDaemon / CmuxCommandPalette imports to TerminalController, Workspace, SessionPersistence - Sources/Workspace+P43Stubs.swift: thin shims for SplitEqualizer, WorkspaceRemoteSessionController.PortScanKickReason, WorkspaceGroupNewWorkspacePlacementSettings (legacy types fork TC still calls; replace with package APIs in P44+) - Sources/GhosttySurfaceSizeDeferralReason.swift: restore fork-only enum (deleted by upstream) - Sources/StableLayout/SessionBlueprintExportAction.swift: parked debug action (depends on legacy SessionPersistenceStore, gone) - Sources/GhosttyTerminalView.swift: stub ghostty_surface_select_cursor_line_compat (needs zig 0.15.2 xcframework rebuild) - pbxproj: keep-both, drop stale ProcessPipeReader/SplitEqualizer/Panels/BrowserProxyEndpoint refs, fix SurfaceHibernationPolicy UUID collision - Drop fork's WorkspaceRemoteConfiguration.swift + WorkspaceRemoteSSHBatchCommandBuilder.swift (extracted to CmuxCore package)
Summary
Closes #6045.
This adds an opt-in
app.windowTitleTemplatesetting for the macOSNSWindow.title, so users can expose a stable per-window token for AX/title-based tiling window managers without changing the default title behavior.Default behavior is unchanged: when
app.windowTitleTemplateis unset or blank, cmux keeps using the active workspace/current-directory title exactly as before.Configuration
Example
~/.config/cmux/cmux.json:{ "app": { "windowTitleTemplate": "[cmux:{windowToken}] {activeWorkspace}" } }Supported placeholders:
{windowId}: full persisted per-window UUID, lowercased{windowToken}: first 8 characters of the persisted window UUID{activeWorkspace}: active workspace title, falling back to the default cmux title when blank{activeDirectory}: active workspace current directory{defaultTitle}: title cmux would have used without a template{appName}:cmuxUnknown placeholders remain literal, and replacement is non-recursive.
Tiling WM Usage
With the template above, a restored window title includes a stable token such as
[cmux:abcd1234]. AeroSpace/yabai users can match the title with a regex substring like:The token comes from the existing persisted
SessionWindowSnapshot.windowId, so restored macOS windows keep the same matchable token across relaunches.Testing
No local tests, local builds,
xcodebuild, orreload.shwere run per instruction.Static/no-build validation run before push:
scripts/normalize-pbxproj.pyscripts/check-pbxproj.shscripts/lint-pbxproj-test-wiring.shjq empty web/messages/*.json web/data/cmux.schema.jsondocs.configuration.schemaDescriptions.app.windowTitleTemplateacross all 20web/messages/*.jsonfilesgit diff --checkgit diff --cached --checkfor merge resolutionsCI is being used as the required build/test gate.
Demo Video
Not recorded yet. The local dev build and launch are intentionally deferred until all required CI checks are green and the user explicitly asks to launch the tagged app.
Summary by CodeRabbit
New Features
Documentation
Tests