Repository navigation
Add Home workspace webview - #6788
lawrencecchen wants to merge 1 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds a Home webview, native handling for Home actions, UI entry points that open Home, and workspace startup settings for Home browser surfaces. ChangesHome screen and action wiring
Sequence Diagram(s)sequenceDiagram
participant HomeSurface
participant HomeWebViewBridge
participant AppDelegate
participant BrowserPanel
participant CmuxBundledWebViewURLSchemeHandler
HomeSurface->>HomeWebViewBridge: postMessage({ action })
HomeWebViewBridge->>AppDelegate: dispatch Home action
AppDelegate->>BrowserPanel: activate Home browser surface
BrowserPanel->>CmuxBundledWebViewURLSchemeHandler: load cmux-webview://home/home.html
Estimated Code Review Effort🎯 5 (Critical) | ⏱️ ~90 minutes Possibly related PRs
Poem
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (4 errors, 1 warning)
✅ Passed checks (20 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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 SummaryThis PR introduces the Home workspace webview: a pinned, index-0
Confidence Score: 4/5Safe to merge with awareness of the workspace-identity issue: all other new infrastructure (bridge trust validation, path-traversal guard, session-compat decode) looks correct. The workspace lookup in Sources/AppDelegate.swift — the Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant User
participant TitlebarButton as Titlebar Home Button
participant AppDelegate
participant TabManager
participant Workspace
participant BrowserPanel
participant SchemeHandler as CmuxBundledWebViewURLSchemeHandler
participant Bridge as HomeWebViewBridge
User->>TitlebarButton: click
TitlebarButton->>AppDelegate: showHomeInActiveMainWindow()
AppDelegate->>AppDelegate: resolve active MainWindowContext
AppDelegate->>TabManager: ensureHomeWorkspace(homeURL:)
TabManager->>TabManager: "tabs.first { customTitle == title }"
alt workspace not found
TabManager->>Workspace: addWorkspace(initialSurface: .browser, initialBrowserURL: homeURL)
Workspace->>BrowserPanel: init(initialURL: homeURL, transparentBackground: true)
end
AppDelegate->>Workspace: setPinned(true), reorderToIndex(0)
AppDelegate->>BrowserPanel: navigate(to: cmux-webview://home/home.html)
BrowserPanel->>SchemeHandler: webView(_:start:urlSchemeTask:)
SchemeHandler->>SchemeHandler: bundledFileURL → Data(contentsOf:)
SchemeHandler-->>BrowserPanel: home.html + homeSurface.mjs
BrowserPanel-->>User: Home webview rendered
User->>Bridge: "cmuxHome.postMessage({ action: newWorkspace })"
Bridge->>Bridge: isTrustedHomeFrame check
Bridge->>AppDelegate: performNewWorkspaceAction()
AppDelegate-->>User: new workspace created
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant User
participant TitlebarButton as Titlebar Home Button
participant AppDelegate
participant TabManager
participant Workspace
participant BrowserPanel
participant SchemeHandler as CmuxBundledWebViewURLSchemeHandler
participant Bridge as HomeWebViewBridge
User->>TitlebarButton: click
TitlebarButton->>AppDelegate: showHomeInActiveMainWindow()
AppDelegate->>AppDelegate: resolve active MainWindowContext
AppDelegate->>TabManager: ensureHomeWorkspace(homeURL:)
TabManager->>TabManager: "tabs.first { customTitle == title }"
alt workspace not found
TabManager->>Workspace: addWorkspace(initialSurface: .browser, initialBrowserURL: homeURL)
Workspace->>BrowserPanel: init(initialURL: homeURL, transparentBackground: true)
end
AppDelegate->>Workspace: setPinned(true), reorderToIndex(0)
AppDelegate->>BrowserPanel: navigate(to: cmux-webview://home/home.html)
BrowserPanel->>SchemeHandler: webView(_:start:urlSchemeTask:)
SchemeHandler->>SchemeHandler: bundledFileURL → Data(contentsOf:)
SchemeHandler-->>BrowserPanel: home.html + homeSurface.mjs
BrowserPanel-->>User: Home webview rendered
User->>Bridge: "cmuxHome.postMessage({ action: newWorkspace })"
Bridge->>Bridge: isTrustedHomeFrame check
Bridge->>AppDelegate: performNewWorkspaceAction()
AppDelegate-->>User: new workspace created
Reviews (4): Last reviewed commit: "Add home button dashboard" | Re-trigger Greptile |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1846d0fa29
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| AppDelegate.shared?.openPreferencesWindow(debugSource: "home.settings") | ||
| } | ||
| ) | ||
| .opacity(sidebarSelectionState.selection == .home ? 1 : 0) |
There was a problem hiding this comment.
Deactivate portal workspaces before showing Home
When Home is opened while a portal-backed panel is selected, this only fades the SwiftUI workspace container out; the WorkspaceContentView above still receives isWorkspaceVisible: presentation.isPanelVisible and isWorkspaceInputActive: isSelectedWorkspace, so terminal/browser AppKit portal views can remain visible and first-responder-capable above this dashboard. In a normal terminal or browser workspace, the Home button can therefore appear to do nothing or leave keystrokes going to the workspace; derive portal visibility/input from sidebarSelectionState.selection == .tabs (or explicitly hide/deactivate portals) before showing Home.
Useful? React with 👍 / 👎.
| return "OK" | ||
| } | ||
|
|
||
| func showHome() -> String { | ||
| var didShow = false | ||
| v2MainSync { | ||
| didShow = AppDelegate.shared?.showHomeInActiveMainWindow() == true | ||
| } |
There was a problem hiding this comment.
Debug seam left in production source
showHome() is compiled into release builds but its only caller is the case "show_home" inside #if DEBUG. Any method whose only call site is stripped from shipping builds is dead production code and a test/debug seam. The PR's own testing description confirms this was used purely as a debug verification step ("ran show_home through the tagged socket"). Either guard the method with #if DEBUG to match its call site, or promote it to a genuine production socket command and remove the #if DEBUG guard from the case.
Rule Used: Flag Swift files under a production Sources path (... (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| @@ -16122,7 +16169,270 @@ private struct ExtensionSidebarBrowserStackEndDropDelegate: DropDelegate { | |||
| } | |||
There was a problem hiding this comment.
270-line home dashboard appended to already massively oversized ContentView.swift
ContentView.swift is already well over 16 000 lines at this diff's context. The cmux file-boundary rule flags large additions to existing oversized files and independently-testable UI logic kept in the app target instead of a small package. The seven private structs here (HomeDashboardView, HomeHeaderView, HomeAction, HomeActionGridView, HomeActionCardView, HomeWorkspaceSectionView, HomeWorkspaceRowView) form a self-contained, independently testable feature that would be a natural candidate for its own file or a HomeUI mini-package.
Rule Used: Flag Swift changes that add too much unrelated res... (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
1846d0f to
163f707
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 163f707. Configure here.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 163f707b86
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| tabManager.selectedTabId = workspaceId | ||
| sidebarSelectionState.selection = .tabs |
There was a problem hiding this comment.
Clear stale multi-selection when selecting from Home
When a user has multiple workspace rows selected in the sidebar, then opens Home and chooses a workspace, this only updates tabManager.selectedTabId. The existing onChange(of: tabManager.selectedTabId) path only syncs selectedTabIds when its count is already <= 1, so the old multi-selection survives after returning to .tabs; subsequent sidebar context-menu or close actions can still operate on that stale selected set instead of the workspace just chosen. Mirror the sidebar/extension selection paths by resetting selectedTabIds and lastSidebarSelectionIndex and selecting the workspace through selectWorkspace here.
Useful? React with 👍 / 👎.
| tabManager: tabManager, | ||
| debugSource: "home.newWorkspace" | ||
| ) == true { | ||
| sidebarSelectionState.selection = .tabs |
There was a problem hiding this comment.
Move Home exit into shared workspace creation
This reset runs only for the Home card, but the same new-workspace behavior is also exposed through the titlebar button and menu/keyboard shortcuts, which call performNewWorkspaceAction without switching sidebarSelectionState back to .tabs. When Home is active, those entrypoints create/select a workspace behind the dashboard, so the action appears to do nothing; put the Home-to-tabs transition in the shared creation path or in every entrypoint that can run while Home is selected.
Useful? React with 👍 / 👎.
| .foregroundStyle(.secondary) | ||
|
|
||
| VStack(spacing: 8) { | ||
| ForEach(workspaces) { workspace in |
There was a problem hiding this comment.
Snapshot Home workspace rows before the ForEach
The root AGENTS.md snapshot-boundary rule says rows below a ForEach must receive immutable value snapshots plus closures only; this ForEach iterates live Workspace ObservableObject references and the row closure reads/captures workspace directly. That bypasses the snapshot discipline used to avoid sidebar/list invalidation churn, so build a value row model such as { id, title, directory } before this boundary and pass only that snapshot into the row/action.
Useful? React with 👍 / 👎.
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/ContentView.swift`:
- Around line 16174-16437: The Home dashboard UI has been added as a large block
inside ContentView, pushing an already oversized production file further past
the repo guideline. Move HomeDashboardView and its helper types (HomeHeaderView,
HomeAction, HomeActionGridView, HomeActionCardView, HomeWorkspaceSectionView,
and HomeWorkspaceRowView) into a dedicated Swift file under Sources, and update
ContentView to reference the extracted views without changing behavior.
- Around line 1831-1834: Update the Home workspace चयन path so it also clears
any existing sidebar multi-selection when `onSelectWorkspace` switches
`tabManager.selectedTabId` and `sidebarSelectionState.selection` back to
`.tabs`. The issue is that this closure only changes the active workspace,
leaving stale `selectedTabIds` intact; fix it by routing this entrypoint through
the same shared selection update path used elsewhere or by explicitly collapsing
the sidebar selection state in the `onSelectWorkspace` handler. Reference
`onSelectWorkspace`, `tabManager.selectedTabId`, and
`sidebarSelectionState.selection` to keep the behavior consistent across
entrypoints.
🪄 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: 325effc4-f9d1-4140-80da-4c368f653ddd
📒 Files selected for processing (9)
Resources/Localizable.xcstringsSources/AppDelegate.swiftSources/ContentView.swiftSources/SessionPersistence.swiftSources/TerminalController.swiftSources/Update/MinimalModeSidebarControls.swiftSources/Update/UpdateTitlebarAccessory.swiftSources/WindowDecorationsController.swiftSources/WindowDragHandleView.swift
| onSelectWorkspace: { workspaceId in | ||
| tabManager.selectedTabId = workspaceId | ||
| sidebarSelectionState.selection = .tabs | ||
| }, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Collapse sidebar multi-selection when Home picks a workspace.
This path only updates selectedTabId, so an existing multi-selection survives the jump back to .tabs. onChange(of: tabManager.selectedTabId) intentionally skips rewriting selectedTabIds when count > 1, which means later sidebar batch actions can still target the stale range even though the user explicitly chose one workspace from Home.
Suggested fix
onSelectWorkspace: { workspaceId in
- tabManager.selectedTabId = workspaceId
+ selectedTabIds = [workspaceId]
+ lastSidebarSelectionIndex = tabManager.tabs.firstIndex { $0.id == workspaceId }
+ if let workspace = tabManager.tabs.first(where: { $0.id == workspaceId }) {
+ tabManager.selectWorkspace(workspace)
+ } else {
+ tabManager.selectedTabId = workspaceId
+ }
sidebarSelectionState.selection = .tabs
},As per coding guidelines, "When a behavior is exposed through multiple entrypoints ... implement one shared action/model path and verify every entrypoint that should invoke it."
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onSelectWorkspace: { workspaceId in | |
| tabManager.selectedTabId = workspaceId | |
| sidebarSelectionState.selection = .tabs | |
| }, | |
| onSelectWorkspace: { workspaceId in | |
| selectedTabIds = [workspaceId] | |
| lastSidebarSelectionIndex = tabManager.tabs.firstIndex { $0.id == workspaceId } | |
| if let workspace = tabManager.tabs.first(where: { $0.id == workspaceId }) { | |
| tabManager.selectWorkspace(workspace) | |
| } else { | |
| tabManager.selectedTabId = workspaceId | |
| } | |
| sidebarSelectionState.selection = .tabs | |
| }, |
🤖 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/ContentView.swift` around lines 1831 - 1834, Update the Home
workspace चयन path so it also clears any existing sidebar multi-selection when
`onSelectWorkspace` switches `tabManager.selectedTabId` and
`sidebarSelectionState.selection` back to `.tabs`. The issue is that this
closure only changes the active workspace, leaving stale `selectedTabIds`
intact; fix it by routing this entrypoint through the same shared selection
update path used elsewhere or by explicitly collapsing the sidebar selection
state in the `onSelectWorkspace` handler. Reference `onSelectWorkspace`,
`tabManager.selectedTabId`, and `sidebarSelectionState.selection` to keep the
behavior consistent across entrypoints.
Source: Coding guidelines
| private struct HomeDashboardView: View { | ||
| let workspaces: [Workspace] | ||
| let onSelectWorkspace: (UUID) -> Void | ||
| let onNewWorkspace: () -> Void | ||
| let onNewBrowser: () -> Void | ||
| let onOpenCommandPalette: () -> Void | ||
| let onOpenSettings: () -> Void | ||
|
|
||
| private var recentWorkspaces: ArraySlice<Workspace> { | ||
| workspaces.prefix(6) | ||
| } | ||
|
|
||
| var body: some View { | ||
| ScrollView { | ||
| VStack(alignment: .leading, spacing: 24) { | ||
| HomeHeaderView( | ||
| title: String(localized: "home.title", defaultValue: "Home"), | ||
| subtitle: String(localized: "home.subtitle", defaultValue: "Graphical overview") | ||
| ) | ||
| HomeActionGridView( | ||
| actions: [ | ||
| HomeAction( | ||
| id: "newWorkspace", | ||
| title: String(localized: "home.action.newWorkspace.title", defaultValue: "New Workspace"), | ||
| subtitle: String(localized: "home.action.newWorkspace.subtitle", defaultValue: "Terminal workspace"), | ||
| systemImage: "terminal", | ||
| tint: Color(nsColor: .systemGreen), | ||
| action: onNewWorkspace | ||
| ), | ||
| HomeAction( | ||
| id: "newBrowser", | ||
| title: String(localized: "home.action.newBrowser.title", defaultValue: "Browser"), | ||
| subtitle: String(localized: "home.action.newBrowser.subtitle", defaultValue: "Browser workspace"), | ||
| systemImage: "globe", | ||
| tint: Color(nsColor: .systemBlue), | ||
| action: onNewBrowser | ||
| ), | ||
| HomeAction( | ||
| id: "commandPalette", | ||
| title: String(localized: "home.action.commandPalette.title", defaultValue: "Command Palette"), | ||
| subtitle: String(localized: "home.action.commandPalette.subtitle", defaultValue: "Commands and workspaces"), | ||
| systemImage: "command", | ||
| tint: Color(nsColor: .systemPurple), | ||
| action: onOpenCommandPalette | ||
| ), | ||
| HomeAction( | ||
| id: "settings", | ||
| title: String(localized: "home.action.settings.title", defaultValue: "Settings"), | ||
| subtitle: String(localized: "home.action.settings.subtitle", defaultValue: "Preferences"), | ||
| systemImage: "gearshape", | ||
| tint: Color(nsColor: .systemOrange), | ||
| action: onOpenSettings | ||
| ), | ||
| ] | ||
| ) | ||
|
|
||
| if !recentWorkspaces.isEmpty { | ||
| HomeWorkspaceSectionView( | ||
| title: String(localized: "home.section.workspaces", defaultValue: "Workspaces"), | ||
| workspaces: recentWorkspaces, | ||
| onSelectWorkspace: onSelectWorkspace | ||
| ) | ||
| } | ||
| } | ||
| .frame(maxWidth: 900, alignment: .leading) | ||
| .padding(.horizontal, 32) | ||
| .padding(.vertical, 34) | ||
| } | ||
| .frame(maxWidth: .infinity, maxHeight: .infinity) | ||
| .background { | ||
| LinearGradient( | ||
| colors: [ | ||
| Color(nsColor: .windowBackgroundColor), | ||
| Color(nsColor: .controlBackgroundColor).opacity(0.65), | ||
| ], | ||
| startPoint: .topLeading, | ||
| endPoint: .bottomTrailing | ||
| ) | ||
| .ignoresSafeArea() | ||
| } | ||
| .accessibilityIdentifier("HomeDashboardView") | ||
| } | ||
| } | ||
|
|
||
| private struct HomeHeaderView: View { | ||
| let title: String | ||
| let subtitle: String | ||
|
|
||
| var body: some View { | ||
| HStack(spacing: 14) { | ||
| Image(systemName: "house.fill") | ||
| .font(.system(size: 24, weight: .semibold)) | ||
| .foregroundStyle(.white) | ||
| .frame(width: 48, height: 48) | ||
| .background( | ||
| RoundedRectangle(cornerRadius: 8, style: .continuous) | ||
| .fill(Color(nsColor: .systemBlue)) | ||
| ) | ||
|
|
||
| VStack(alignment: .leading, spacing: 3) { | ||
| Text(title) | ||
| .cmuxFont(size: 28, weight: .bold) | ||
| .foregroundStyle(.primary) | ||
| Text(subtitle) | ||
| .cmuxFont(size: 13, weight: .medium) | ||
| .foregroundStyle(.secondary) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private struct HomeAction: Identifiable { | ||
| let id: String | ||
| let title: String | ||
| let subtitle: String | ||
| let systemImage: String | ||
| let tint: Color | ||
| let action: () -> Void | ||
| } | ||
|
|
||
| private struct HomeActionGridView: View { | ||
| let actions: [HomeAction] | ||
|
|
||
| private let columns = [ | ||
| GridItem(.adaptive(minimum: 190, maximum: 260), spacing: 12, alignment: .top) | ||
| ] | ||
|
|
||
| var body: some View { | ||
| LazyVGrid(columns: columns, alignment: .leading, spacing: 12) { | ||
| ForEach(actions) { action in | ||
| Button(action: action.action) { | ||
| HomeActionCardView(action: action) | ||
| } | ||
| .buttonStyle(.plain) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private struct HomeActionCardView: View { | ||
| let action: HomeAction | ||
| @State private var isHovering = false | ||
|
|
||
| var body: some View { | ||
| HStack(spacing: 12) { | ||
| Image(systemName: action.systemImage) | ||
| .font(.system(size: 17, weight: .semibold)) | ||
| .foregroundStyle(action.tint) | ||
| .frame(width: 34, height: 34) | ||
| .background( | ||
| RoundedRectangle(cornerRadius: 7, style: .continuous) | ||
| .fill(action.tint.opacity(0.14)) | ||
| ) | ||
|
|
||
| VStack(alignment: .leading, spacing: 3) { | ||
| Text(action.title) | ||
| .cmuxFont(size: 13, weight: .semibold) | ||
| .foregroundStyle(.primary) | ||
| .lineLimit(1) | ||
| Text(action.subtitle) | ||
| .cmuxFont(size: 11, weight: .regular) | ||
| .foregroundStyle(.secondary) | ||
| .lineLimit(2) | ||
| .fixedSize(horizontal: false, vertical: true) | ||
| } | ||
|
|
||
| Spacer(minLength: 4) | ||
| } | ||
| .padding(12) | ||
| .frame(minHeight: 70) | ||
| .background( | ||
| RoundedRectangle(cornerRadius: 8, style: .continuous) | ||
| .fill(Color(nsColor: .controlBackgroundColor).opacity(isHovering ? 0.88 : 0.66)) | ||
| ) | ||
| .overlay( | ||
| RoundedRectangle(cornerRadius: 8, style: .continuous) | ||
| .stroke(Color(nsColor: .separatorColor).opacity(isHovering ? 0.85 : 0.45), lineWidth: 1) | ||
| ) | ||
| .contentShape(RoundedRectangle(cornerRadius: 8, style: .continuous)) | ||
| .onHover { isHovering = $0 } | ||
| } | ||
| } | ||
|
|
||
| private struct HomeWorkspaceSectionView: View { | ||
| let title: String | ||
| let workspaces: ArraySlice<Workspace> | ||
| let onSelectWorkspace: (UUID) -> Void | ||
|
|
||
| var body: some View { | ||
| VStack(alignment: .leading, spacing: 10) { | ||
| Text(title) | ||
| .cmuxFont(size: 13, weight: .semibold) | ||
| .foregroundStyle(.secondary) | ||
|
|
||
| VStack(spacing: 8) { | ||
| ForEach(workspaces) { workspace in | ||
| Button { | ||
| onSelectWorkspace(workspace.id) | ||
| } label: { | ||
| HomeWorkspaceRowView( | ||
| title: workspace.customTitle ?? workspace.title, | ||
| directory: workspace.currentDirectory | ||
| ) | ||
| } | ||
| .buttonStyle(.plain) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private struct HomeWorkspaceRowView: View { | ||
| let title: String | ||
| let directory: String | ||
| @State private var isHovering = false | ||
|
|
||
| var body: some View { | ||
| HStack(spacing: 10) { | ||
| Image(systemName: "rectangle.stack") | ||
| .font(.system(size: 14, weight: .semibold)) | ||
| .foregroundStyle(Color(nsColor: .systemTeal)) | ||
| .frame(width: 28, height: 28) | ||
| .background( | ||
| RoundedRectangle(cornerRadius: 7, style: .continuous) | ||
| .fill(Color(nsColor: .systemTeal).opacity(0.13)) | ||
| ) | ||
|
|
||
| VStack(alignment: .leading, spacing: 2) { | ||
| Text(title) | ||
| .cmuxFont(size: 12, weight: .semibold) | ||
| .foregroundStyle(.primary) | ||
| .lineLimit(1) | ||
| if !directory.isEmpty { | ||
| Text(directory) | ||
| .cmuxFont(size: 11, weight: .regular) | ||
| .foregroundStyle(.secondary) | ||
| .lineLimit(1) | ||
| .truncationMode(.middle) | ||
| } | ||
| } | ||
|
|
||
| Spacer() | ||
|
|
||
| Image(systemName: "chevron.right") | ||
| .font(.system(size: 11, weight: .semibold)) | ||
| .foregroundStyle(.tertiary) | ||
| } | ||
| .padding(.horizontal, 12) | ||
| .padding(.vertical, 10) | ||
| .background( | ||
| RoundedRectangle(cornerRadius: 8, style: .continuous) | ||
| .fill(Color(nsColor: .controlBackgroundColor).opacity(isHovering ? 0.78 : 0.48)) | ||
| ) | ||
| .overlay( | ||
| RoundedRectangle(cornerRadius: 8, style: .continuous) | ||
| .stroke(Color(nsColor: .separatorColor).opacity(isHovering ? 0.75 : 0.32), lineWidth: 1) | ||
| ) | ||
| .contentShape(RoundedRectangle(cornerRadius: 8, style: .continuous)) | ||
| .onHover { isHovering = $0 } | ||
| } | ||
| } | ||
|
|
||
| enum SidebarSelection { | ||
| case home |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Extract the Home dashboard views into their own file.
This adds a few hundred more production lines to Sources/ContentView.swift, which is already far beyond the repo’s file-size budget. Move HomeDashboardView and its helper views into a dedicated Sources/*.swift file before merge.
As per coding guidelines, "Flag when more than 250 lines are added to an existing production Swift file already over 800 lines"; based on learnings, "avoid bloating the existing file: extract the subview into a dedicated Swift file under Sources."
🤖 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/ContentView.swift` around lines 16174 - 16437, The Home dashboard UI
has been added as a large block inside ContentView, pushing an already oversized
production file further past the repo guideline. Move HomeDashboardView and its
helper types (HomeHeaderView, HomeAction, HomeActionGridView,
HomeActionCardView, HomeWorkspaceSectionView, and HomeWorkspaceRowView) into a
dedicated Swift file under Sources, and update ContentView to reference the
extracted views without changing behavior.
Sources: Coding guidelines, Learnings
This comment has been minimized.
This comment has been minimized.
163f707 to
bdb6320
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bdb632036e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let workspace = tabManager.tabs.first { workspace in | ||
| workspace.customTitle == title || workspace.title == title | ||
| } ?? tabManager.addWorkspace( |
There was a problem hiding this comment.
Use a stable identifier for Home workspace
When any existing workspace has the localized display title “Home” (for example a user-renamed project or a browser page whose title is Home), this treats that user workspace as the dashboard and the following code then pins/reorders it and navigates or adds a browser surface. That can overwrite the user's current workspace instead of opening the Home dashboard; match a stable marker such as the bundled Home URL or dedicated metadata rather than the mutable display title.
Useful? React with 👍 / 👎.
| id: "newWorkspace", | ||
| title: "New Workspace", | ||
| subtitle: "Start a terminal workspace", | ||
| icon: "terminal", |
There was a problem hiding this comment.
Localize the Home webview copy
In Japanese or any non-English locale, the new Home webview still renders these hardcoded English labels because this React surface never reads the new Localizable.xcstrings entries or any locale payload. The root AGENTS.md localization rule requires user-facing strings to be localized for supported locales, so the Home header, action titles/subtitles, HTML title/lang, and errors need to come from an en/ja catalog or native-provided localized data before shipping.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 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`:
- Around line 6689-6694: The Home workspace lookup in ensureHomeWorkspace
currently relies on comparing the localized title, which can collide with
user-renamed workspaces and fail across locale changes. Update the workspace
identity logic to use a stable, non-user-editable identifier or persisted flag
on the Workspace created via tabManager.addWorkspace, and have the
tabManager.tabs.first lookup match that identifier instead of title/customTitle.
Keep the pin/reorder behavior unchanged, but ensure the Home workspace is
uniquely recognized regardless of localization or user edits.
- Around line 6647-6650: Remove the NSSound.beep() call from the shared helper
that guards BrowserAvailabilitySettings.isEnabled() and Self.homeWebViewURL(),
since the false return is already handled by callers like ContentView.showHome()
and the minimal-titlebar path. Keep the guard’s return value behavior unchanged,
and leave the beep responsibility to those caller methods so the failure only
alerts once.
In `@Sources/TerminalController.swift`:
- Around line 10243-10248: The showHome command in showHome() is returning a
misleading specific error when AppDelegate.shared?.showHomeInActiveMainWindow()
returns false for non-window failures. Update the failure branch in showHome()
to return a generic error message instead of "ERROR: No main window", keeping
the success path unchanged and using the showHomeInActiveMainWindow() result
only to decide OK vs failure.
In `@webviews/src/home.css`:
- Line 14: Fix the Stylelint violations in home.css by adjusting the declaration
order/spacing around the relevant rule blocks and changing the `currentColor`
value in the affected style to lowercase `currentcolor`. Update the CSS near the
`background` declaration and the rule using `currentColor` so they comply with
`declaration-empty-line-before` and `value-keyword-case`, ensuring the
stylesheet passes CI.
In `@webviews/src/surfaces/homeSurface.tsx`:
- Around line 45-66: The Home UI is currently exposing internal bridge failure
details from runHomeAction and HomeSurface, including reply.error.code and raw
Error.message text. Update HomeSurface/handleAction to translate all native or
thrown failures into stable user-facing copy, and keep the underlying
code/details only for diagnostics or logging. Preserve the bridge-specific error
handling in runHomeAction, but do not pass internal codes like invalid_request
or not_allowed through to setError or the page.
🪄 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: ba94699c-63fb-47de-ba2a-afd51a788cb0
📒 Files selected for processing (28)
Resources/Localizable.xcstringsResources/markdown-viewer/webviews-app/chunks/agentSessionSurface.mjsResources/markdown-viewer/webviews-app/chunks/diffSurface.mjsResources/markdown-viewer/webviews-app/chunks/homeSurface.mjsResources/markdown-viewer/webviews-app/chunks/installWebviewStyles.mjsResources/markdown-viewer/webviews-app/chunks/router.mjsResources/markdown-viewer/webviews-app/home.htmlResources/markdown-viewer/webviews-app/main.mjsSources/AppDelegate.swiftSources/ContentView.swiftSources/Panels/BrowserPanel.swiftSources/Panels/CmuxBundledWebViewURLSchemeHandler.swiftSources/Panels/HomeWebViewBridge.swiftSources/SessionPersistence.swiftSources/TabManager.swiftSources/TerminalController.swiftSources/Update/MinimalModeSidebarControls.swiftSources/Update/UpdateTitlebarAccessory.swiftSources/WindowDecorationsController.swiftSources/WindowDragHandleView.swiftSources/Workspace.swiftcmux.xcodeproj/project.pbxprojscripts/build-webviews-app.shwebviews/src/agent-session/shared/bridge.tswebviews/src/home.csswebviews/src/main.tsxwebviews/src/router.tsxwebviews/src/surfaces/homeSurface.tsx
| guard BrowserAvailabilitySettings.isEnabled(), | ||
| let homeURL = Self.homeWebViewURL() else { | ||
| NSSound.beep() | ||
| return false |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Remove the beep from this shared helper.
ContentView.showHome() and the minimal-titlebar callers already beep when this returns false, so this path produces two alert sounds when Home cannot open.
Suggested fix
guard BrowserAvailabilitySettings.isEnabled(),
let homeURL = Self.homeWebViewURL() else {
- NSSound.beep()
return false
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| guard BrowserAvailabilitySettings.isEnabled(), | |
| let homeURL = Self.homeWebViewURL() else { | |
| NSSound.beep() | |
| return false | |
| guard BrowserAvailabilitySettings.isEnabled(), | |
| let homeURL = Self.homeWebViewURL() else { | |
| return false | |
| } |
🤖 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/AppDelegate.swift` around lines 6647 - 6650, Remove the
NSSound.beep() call from the shared helper that guards
BrowserAvailabilitySettings.isEnabled() and Self.homeWebViewURL(), since the
false return is already handled by callers like ContentView.showHome() and the
minimal-titlebar path. Keep the guard’s return value behavior unchanged, and
leave the beep responsibility to those caller methods so the failure only alerts
once.
| func ensureHomeWorkspace(in tabManager: TabManager, homeURL: URL) -> Workspace { | ||
| let title = String(localized: "home.workspace.title", defaultValue: "Home") | ||
| let workspace = tabManager.tabs.first { workspace in | ||
| workspace.customTitle == title || workspace.title == title | ||
| } ?? tabManager.addWorkspace( | ||
| title: title, |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Use a stable Home workspace identifier instead of the localized title.
This lookup can hijack any user workspace renamed to the localized Home title, and it will also miss the existing Home workspace after a locale change, creating or mutating the wrong tab before Lines 6707-6712 pin/reorder it. Home needs a non-user-editable identifier/flag persisted with the workspace, not a title comparison.
As per path instructions, "flag deriving the value from a window/pane/terminal title, name, or process-argv heuristic" for correctness-critical detection/identity.
🤖 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/AppDelegate.swift` around lines 6689 - 6694, The Home workspace
lookup in ensureHomeWorkspace currently relies on comparing the localized title,
which can collide with user-renamed workspaces and fail across locale changes.
Update the workspace identity logic to use a stable, non-user-editable
identifier or persisted flag on the Workspace created via
tabManager.addWorkspace, and have the tabManager.tabs.first lookup match that
identifier instead of title/customTitle. Keep the pin/reorder behavior
unchanged, but ensure the Home workspace is uniquely recognized regardless of
localization or user edits.
Source: Path instructions
| func showHome() -> String { | ||
| var didShow = false | ||
| v2MainSync { | ||
| didShow = AppDelegate.shared?.showHomeInActiveMainWindow() == true | ||
| } | ||
| return didShow ? "OK" : "ERROR: No main window" |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Return a generic failure message here.
showHomeInActiveMainWindow() also returns false when Home is unavailable (for example Browser is disabled or the Home webview URL cannot be resolved), so "ERROR: No main window" is incorrect for reachable failure paths and will mislead socket debugging.
Suggested fix
func showHome() -> String {
var didShow = false
v2MainSync {
didShow = AppDelegate.shared?.showHomeInActiveMainWindow() == true
}
- return didShow ? "OK" : "ERROR: No main window"
+ return didShow ? "OK" : "ERROR: Unable to show Home"
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func showHome() -> String { | |
| var didShow = false | |
| v2MainSync { | |
| didShow = AppDelegate.shared?.showHomeInActiveMainWindow() == true | |
| } | |
| return didShow ? "OK" : "ERROR: No main window" | |
| func showHome() -> String { | |
| var didShow = false | |
| v2MainSync { | |
| didShow = AppDelegate.shared?.showHomeInActiveMainWindow() == true | |
| } | |
| return didShow ? "OK" : "ERROR: Unable to show Home" |
🤖 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/TerminalController.swift` around lines 10243 - 10248, The showHome
command in showHome() is returning a misleading specific error when
AppDelegate.shared?.showHomeInActiveMainWindow() returns false for non-window
failures. Update the failure branch in showHome() to return a generic error
message instead of "ERROR: No main window", keeping the success path unchanged
and using the showHomeInActiveMainWindow() result only to decide OK vs failure.
| --home-green: #25a244; | ||
| --home-violet: #9b5cff; | ||
| --home-orange: #ff9500; | ||
| background: transparent; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix Stylelint errors that will fail CI.
Two lint violations were flagged in this new file:
- Line 167:
value-keyword-caseexpectscurrentcolor(lowercase) instead ofcurrentColor. - Line 14:
declaration-empty-line-beforeexpects an empty line before thebackground: transparent;declaration.
🎨 Proposed fixes
--home-orange: `#ff9500`;
+
background: transparent; .home-action svg {
fill: none;
- stroke: currentColor;
+ stroke: currentcolor;Also applies to: 167-167
🧰 Tools
🪛 Stylelint (17.13.0)
[error] 14-14: Expected empty line before declaration (declaration-empty-line-before)
(declaration-empty-line-before)
🤖 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 `@webviews/src/home.css` at line 14, Fix the Stylelint violations in home.css
by adjusting the declaration order/spacing around the relevant rule blocks and
changing the `currentColor` value in the affected style to lowercase
`currentcolor`. Update the CSS near the `background` declaration and the rule
using `currentColor` so they comply with `declaration-empty-line-before` and
`value-keyword-case`, ensuring the stylesheet passes CI.
Source: Linters/SAST tools
| async function runHomeAction(action: HomeAction): Promise<void> { | ||
| const handler = nativeHandler(); | ||
| if (!handler) { | ||
| throw new Error("Home bridge is unavailable."); | ||
| } | ||
| const reply = await handler.postMessage({ action }); | ||
| if (!reply?.ok) { | ||
| throw new Error(reply?.error?.code ?? "action_failed"); | ||
| } | ||
| } | ||
|
|
||
| function HomeSurface() { | ||
| const [pendingAction, setPendingAction] = React.useState<HomeAction | null>(null); | ||
| const [error, setError] = React.useState<string | null>(null); | ||
|
|
||
| async function handleAction(action: HomeAction) { | ||
| setPendingAction(action); | ||
| setError(null); | ||
| try { | ||
| await runHomeAction(action); | ||
| } catch (err) { | ||
| setError(err instanceof Error ? err.message : "Action failed."); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Do not surface bridge error codes in the Home UI.
Lines 50-52 and Lines 65-66 can render internal bridge failures like invalid_request/not_allowed, or any thrown Error.message, directly into the page. Please map native failures to stable user-facing copy and keep the detailed code only for diagnostics.
Suggested fix
+const HOME_ACTION_ERROR = "Couldn't complete that action. Try again.";
+
async function runHomeAction(action: HomeAction): Promise<void> {
const handler = nativeHandler();
if (!handler) {
- throw new Error("Home bridge is unavailable.");
+ throw new Error("home_action_failed");
}
const reply = await handler.postMessage({ action });
if (!reply?.ok) {
- throw new Error(reply?.error?.code ?? "action_failed");
+ throw new Error("home_action_failed");
}
}
@@
- } catch (err) {
- setError(err instanceof Error ? err.message : "Action failed.");
+ } catch {
+ setError(HOME_ACTION_ERROR);
} finally {
setPendingAction(null);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async function runHomeAction(action: HomeAction): Promise<void> { | |
| const handler = nativeHandler(); | |
| if (!handler) { | |
| throw new Error("Home bridge is unavailable."); | |
| } | |
| const reply = await handler.postMessage({ action }); | |
| if (!reply?.ok) { | |
| throw new Error(reply?.error?.code ?? "action_failed"); | |
| } | |
| } | |
| function HomeSurface() { | |
| const [pendingAction, setPendingAction] = React.useState<HomeAction | null>(null); | |
| const [error, setError] = React.useState<string | null>(null); | |
| async function handleAction(action: HomeAction) { | |
| setPendingAction(action); | |
| setError(null); | |
| try { | |
| await runHomeAction(action); | |
| } catch (err) { | |
| setError(err instanceof Error ? err.message : "Action failed."); | |
| const HOME_ACTION_ERROR = "Couldn't complete that action. Try again."; | |
| async function runHomeAction(action: HomeAction): Promise<void> { | |
| const handler = nativeHandler(); | |
| if (!handler) { | |
| throw new Error("home_action_failed"); | |
| } | |
| const reply = await handler.postMessage({ action }); | |
| if (!reply?.ok) { | |
| throw new Error("home_action_failed"); | |
| } | |
| } | |
| function HomeSurface() { | |
| const [pendingAction, setPendingAction] = React.useState<HomeAction | null>(null); | |
| const [error, setError] = React.useState<string | null>(null); | |
| async function handleAction(action: HomeAction) { | |
| setPendingAction(action); | |
| setError(null); | |
| try { | |
| await runHomeAction(action); | |
| } catch { | |
| setError(HOME_ACTION_ERROR); | |
| } finally { | |
| setPendingAction(null); | |
| } | |
| } | |
| } |
🤖 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 `@webviews/src/surfaces/homeSurface.tsx` around lines 45 - 66, The Home UI is
currently exposing internal bridge failure details from runHomeAction and
HomeSurface, including reply.error.code and raw Error.message text. Update
HomeSurface/handleAction to translate all native or thrown failures into stable
user-facing copy, and keep the underlying code/details only for diagnostics or
logging. Preserve the bridge-specific error handling in runHomeAction, but do
not pass internal codes like invalid_request or not_allowed through to setError
or the page.
bdb6320 to
3f8c6d0
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3f8c6d0bfe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| _ = tabManager.reorderWorkspace(tabId: workspace.id, toIndex: 0) | ||
| ensureHomeBrowserSurface(in: workspace, homeURL: homeURL) | ||
| tabManager.selectedTabId = workspace.id |
There was a problem hiding this comment.
Collapse sidebar multi-selection when showing Home
Fresh evidence in this revision is that Home now selects its pinned workspace here by assigning selectedTabId directly. When the sidebar currently has multiple selected workspaces, ContentView's onChange(of: tabManager.selectedTabId) deliberately skips syncing selectedTabIds while the set size is greater than 1, so clicking the Home button leaves the old multi-selection active and subsequent sidebar batch/context-menu actions can still target those stale workspaces instead of Home. Route this through the shared workspace selection/collapse path or post the same collapse event used by keyboard navigation.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
Sources/TerminalController.swift (1)
10243-10249: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winMisleading failure message.
showHomeInActiveMainWindow()returnsfalsefor reachable non-window failures (Browser disabled, Home webview URL unresolvable), so"ERROR: No main window"will misattribute those cases. Return a generic message instead.🤖 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/TerminalController.swift` around lines 10243 - 10249, The failure message in showHome is too specific because showHomeInActiveMainWindow can return false for reasons other than a missing main window. Update showHome in TerminalController to return a generic ERROR message when didShow is false, and keep the current success path unchanged so the result no longer misattributes Browser-disabled or unresolved Home URL cases.
🤖 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.
Duplicate comments:
In `@Sources/TerminalController.swift`:
- Around line 10243-10249: The failure message in showHome is too specific
because showHomeInActiveMainWindow can return false for reasons other than a
missing main window. Update showHome in TerminalController to return a generic
ERROR message when didShow is false, and keep the current success path unchanged
so the result no longer misattributes Browser-disabled or unresolved Home URL
cases.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: e6c5cb7c-cb76-4250-b0d8-47436337ca72
📒 Files selected for processing (28)
Resources/Localizable.xcstringsResources/markdown-viewer/webviews-app/chunks/agentSessionSurface.mjsResources/markdown-viewer/webviews-app/chunks/diffSurface.mjsResources/markdown-viewer/webviews-app/chunks/homeSurface.mjsResources/markdown-viewer/webviews-app/chunks/installWebviewStyles.mjsResources/markdown-viewer/webviews-app/chunks/router.mjsResources/markdown-viewer/webviews-app/home.htmlResources/markdown-viewer/webviews-app/main.mjsSources/AppDelegate.swiftSources/ContentView.swiftSources/Panels/BrowserPanel.swiftSources/Panels/CmuxBundledWebViewURLSchemeHandler.swiftSources/Panels/HomeWebViewBridge.swiftSources/SessionPersistence.swiftSources/TabManager.swiftSources/TerminalController.swiftSources/Update/MinimalModeSidebarControls.swiftSources/Update/UpdateTitlebarAccessory.swiftSources/WindowDecorationsController.swiftSources/WindowDragHandleView.swiftSources/Workspace.swiftcmux.xcodeproj/project.pbxprojscripts/build-webviews-app.shwebviews/src/agent-session/shared/bridge.tswebviews/src/home.csswebviews/src/main.tsxwebviews/src/router.tsxwebviews/src/surfaces/homeSurface.tsx

Summary
cmux-webview://home/home.html, using the same webviews build pipeline as the diff/agent surfaces.cmuxHomewebview bridge for New Workspace, Browser, Command Palette, and Settings actions..homesession decode compatibility by mapping it back to tabs.Testing
git diff --check./scripts/build-webviews-app.sh --checknode -e 'JSON.parse(require("fs").readFileSync("Resources/Localizable.xcstrings", "utf8")); console.log("Localizable OK")'\n-./scripts/check-pbxproj.sh\n-./scripts/reload-cloud.sh --tag homewvpassed in https://github.com/manaflow-ai/cmux/actions/runs/28213731201.\n- Follow-up placement rebuild./scripts/reload-cloud.sh --tag homewvpassed in https://github.com/manaflow-ai/cmux/actions/runs/28214785551.\n- Launched tagged apphomewv, verified/tmp/cmux-debug-homewv.sockidentifiescom.cmuxterm.app.debug.homewv, sentshow_home, confirmed Home is selected, pinned, and index 0.\n- Verified Home DOM text and direct browser screenshot at/var/folders/rr/vmfx6xh12dz2tlvgtmyvjmf80000gn/T/cmux-browser-screenshots/surface-401255A5-1782442115517-F540CF61.png.\n- Verified titlebar order with screenshot/var/folders/rr/vmfx6xh12dz2tlvgtmyvjmf80000gn/T/cmux-screenshots/2026-06-26T03-19-20Z_16FB98FD.png: sidebar, notifications, plus, Home, back.\n- Invokedwindow.webkit.messageHandlers.cmuxHome.postMessage({ action: "newBrowser" })from the Home page and confirmed it created/focused a browser workspace.\n\n## Issues\n- Related: user task from cmuxterm-hq.Note
Low Risk
String catalog-only changes with no logic or security impact.
Overview
Adds manual English and Japanese strings in
Localizable.xcstringsfor the Home experience:titlebar.home.accessibilityLabelandtitlebar.home.tooltipfor the titlebar Home control, andhome.workspace.titlefor the pinned Home workspace label.Reviewed by Cursor Bugbot for commit 3f8c6d0. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit