Skip to content

feat: sidebar prompt launcher with model/target pickers and SSH status API - #4721

Closed
maucher wants to merge 4 commits into
manaflow-ai:mainfrom
maucher:feat/local-customizations
Closed

maucher wants to merge 4 commits into
manaflow-ai:mainfrom
maucher:feat/local-customizations

Conversation

@maucher

@maucher maucher commented May 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add a sidebar prompt launcher that runs ws with optional target/model selection, streams progress into the input field, and unlocks as soon as the new workspace appears in cmux.
  • Add sidebar.set_status / sidebar.clear_status socket commands plus cmuxd-remote set-status, and auto-run ws reset when a slotted workspace is closed.
  • Clean up redundant nonisolated annotations and gitignore the built remote daemon binary.
  • Isolate PromptLauncherModel on the main actor for Swift concurrency correctness.

Test plan

  • Open sidebar prompt launcher, submit a prompt with auto target — workspace appears in sidebar before ws finishes
  • Switch target to local / devbox / devbox-1 / devbox-2 and confirm correct ws invocation
  • Switch model to cursor and confirm cursor is prepended to ws args
  • From a remote SSH workspace, run cmuxd-remote set-status and verify sidebar status entry updates
  • Close a slotted workspace and confirm ws reset wkN is triggered
  • Tagged build compiles: ./scripts/reload.sh --tag local-customizations

Made with Cursor


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.


Summary by cubic

Adds a sidebar Prompt Launcher with target/model pickers that runs ws, streams progress, and opens the workspace as soon as it appears. Introduces sidebar.set_status over the v2 socket and cmuxd-remote set-status for remote status updates, plus auto ws reset when closing slotted workspaces.

  • New Features

    • Sidebar Prompt Launcher: multi-line input with target picker (auto, local, devbox, devbox-1, devbox-2) and model picker (claude, cursor); runs ws via login shell, streams output with animated dots, and unlocks on early “Waiting for …”/“✓ Workspace …” lines.
    • Status API: v2 method sidebar.set_status (key, value, icon, color, priority) and CLI cmuxd-remote set-status to update sidebar status entries from SSH/remote processes.
    • Workspace lifecycle: automatically runs ws reset wkN when closing a slotted workspace (matched via ~/.claude/workon/worktrees/*.json).
  • Refactors

    • Swift concurrency cleanup: remove unnecessary nonisolated markers; isolate PromptLauncherModel on the main actor; minor thread-safety fixes.
    • UI cleanup: remove unused Bonsplit tab bar parameters.
    • Session snapshots now exclude remote workspaces.
    • Chores: ignore built daemon/remote/cmuxd-remote binary; update Package.resolved pin for swift-asn1.

Written for commit 8d369a0. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • New Features

    • Added a sidebar prompt launcher with text input, supporting Cmd+Return to execute commands and animated loading indicator.
    • Added support for setting sidebar status entries via new command.
    • Added localization strings for prompt launcher (English and Japanese).
  • Bug Fixes

    • Improved stale workspace cleanup and session restoration filtering.
  • Chores

    • Updated dependency versions and refined internal concurrency annotations.

Review Change Stack

maucher and others added 4 commits May 22, 2026 22:13
…rkspace close

- Add SidebarPromptLauncher to sidebar footer: multiline text input that runs
  `ws [target] <prompt>` as a login shell command, streams output with animated
  dots, and supports machine selector checkboxes (local/devbox/devbox-1/devbox-2)
- Add V2 socket command `sidebar.set_status` (key, value, icon, color, priority)
  and `sidebar.clear_status` for SSH/remote processes to update sidebar status entries
- Add CLI `set-status` subcommand in cmuxd-remote mapping to sidebar.set_status
- Add `triggerWsResetIfSlotted()` in TabManager: auto-runs `ws reset wkN` when a
  slotted workspace is closed, matching against ~/.claude/workon/worktrees/*.json
- Remove `nonisolated` from CmuxTop*, WorkspaceRemoteConfiguration for compiler compat
- Remove solidSurfaceWidthAdjustment/separatorFadeWidth from tab bar (bonsplit API cleanup)
- Update vendor/bonsplit submodule and swift-asn1 version pin

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove nonisolated annotations from module-level constants, enums, and
structs that don't require isolation — these were producing warnings
under newer Swift/Xcode toolchains. Add daemon/remote/cmuxd-remote to
.gitignore so the compiled binary is never accidentally committed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add model selector (claude/cursor) to the sidebar prompt launcher
- Add "auto" as the default target option; skip target arg when auto is
  selected so ws picks the right host automatically
- Switch target UI from checkboxes to a compact Picker
- Detect workspace creation earlier by watching for "Waiting for" /
  "✓ Workspace" lines in ws output instead of waiting for full process
  exit; uses a thread-safe single-fire resume to avoid double-resuming
  the checked continuation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ensure PromptLauncherModel and NSTextView updates run on the main actor
after nonisolated cleanup; mark resolveWSBin nonisolated for pipe handlers.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel

vercel Bot commented May 25, 2026

Copy link
Copy Markdown

@maucher is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented May 25, 2026 •

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR introduces a new sidebar prompt launcher that runs external ws commands, implements the sidebar.set_status V2 command for updating workspace status, performs a systematic audit removing nonisolated modifiers across the codebase for Swift concurrency consistency, updates tab management and session persistence logic, and tunes visual backdrop effects.

Changes

Sidebar Prompt Launcher and Status Command

Layer / File(s) Summary
Sidebar prompt launcher UI components and model
Sources/ContentView.swift, Resources/Localizable.xcstrings
PromptTextEditor and PromptTextEditorContainer provide text input with placeholder rendering and newline-submit handling. PromptLauncherModel spawns external ws processes via /bin/zsh, streams stdout/stderr through pipes, strips ANSI sequences, updates prompt text with animated dots, and uses a checked continuation to resume exactly once when a workspace-created line appears or the process terminates. SidebarPromptLauncher view composes the editor, target/model pickers, and send button (Cmd+Return), disabling edits during loading. New localization strings for placeholder and send label.
Sidebar status V2 command and remote CLI
Sources/TerminalController.swift, daemon/remote/cmd/cmuxd-remote/cli.go
v2SidebarSetStatus handler validates key/value parameters, parses optional icon/color, clamps priority to [-9999, 9999], and writes a new SidebarStatusEntry with fresh timestamp. V2 dispatch routes sidebar.set_status to the handler. Remote CLI adds set-status command with key, value, icon, color, priority flags and special positional argument mapping for key and value params.

Swift Concurrency Isolation Audit

Layer / File(s) Summary
Remove nonisolated modifiers from type and global declarations
Sources/BackgroundWorkspacePrimeCoordinator.swift, Sources/CmuxEventLogWriter.swift, Sources/CmuxTopMemoryDiagnostics.swift, Sources/CmuxTopProcessCPUTracker.swift, Sources/CmuxTopProcessEnumeration.swift, Sources/CmuxTopProcessSnapshotCache.swift, Sources/CmuxTopSnapshot.swift, Sources/CmuxTopSnapshotScopeCache.swift, Sources/GhosttyCrashBreadcrumb.swift, Sources/GhosttyCrashReportMetadata.swift, Sources/Panels/BrowserHiddenWebViewDiscardPolicy.swift, Sources/Panels/BrowserPanel.swift, Sources/RestorableAgentSession.swift, Sources/RightSidebarPanelView.swift, Sources/RightSidebarRemoteCommand.swift, Sources/SessionIndexStore.swift, Sources/SessionPersistence.swift, Sources/TerminalController.swift, Sources/TerminalNotificationStore.swift, Sources/WindowChromeMetrics.swift, Sources/WorkspaceRemoteConfiguration.swift
Removes nonisolated qualifiers from nested enum/struct declarations (e.g., PrimeCompletionReason, CmuxTopResourceSummary, RightSidebarRemoteTarget), extension declarations, and global constants (e.g., cmuxEventLogLogger, sessionIndexLogger), adjusting actor isolation semantics throughout the codebase while preserving all runtime logic.

Tab Management and Session Persistence Updates

Layer / File(s) Summary
Tab manager stale cleanup, devbox reset, and display title refactoring
Sources/TabManager.swift
Stale agent PID cleanup now directly removes entries from tab.statusEntries and tab.agentPIDs instead of delegating to clearAgentPID. On workspace close, detects matching devbox slot by scanning ~/.claude/workon/worktrees for wk*.json files and triggers async ws reset <slot> in DEBUG builds. Refactors close-other-tabs confirmation title via closeOtherTabsDisplayTitle helper that collapses newlines/carriage returns and falls back to "Untitled Tab".
Session restoration workspace filtering
Sources/TabManager.swift
Session snapshot now filters restored workspaces using !$0.isRemoteWorkspace instead of isRestorableInSessionSnapshot.

Visual Effects and Configuration Updates

Layer / File(s) Summary
Backdrop effect parameter tuning
Sources/BonsplitTabBarDebug.swift, Sources/cmuxApp.swift
Debug backdrop effect initialization no longer applies solidSurfaceWidthAdjustment or separatorFadeWidth from settings; instead uses hardcoded solidWidth: 23.875 and derives fadeWidth/contentFadeWidth/solidWidth via softness-driven interpolations in the candidate effect. renderIdentity omits prior separatorFadeWidth and solidSurfaceWidthAdjustment segments.
Gitignore, localization, syntax, and dependency updates
.gitignore, Sources/ContentView.swift, cmux.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
Adds .gitignore entry for daemon/remote/cmuxd-remote built binary. Updates tmux helper parameter type from Panel to any Panel for explicit existential syntax. Updates SwiftPM lock file with new swift-asn1 revision 9f54261 / version 1.6.0.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • manaflow-ai/cmux#4225: Adds Grok integration notification logic that uses the new sidebar.set_status command to update panel status entries.

Poem

🐰 A prompt launcher hops into the sidebar bright,
With workspace creation signals dancing in the light,
Swift isolation modifiers take their final bow,
As concurrency flows through the codebase now,
Status commands relay their messages with care,
And tabs manage themselves with newfound flair! ✨


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (7 errors, 1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Cmux Swift Blocking Runtime ❌ Error PR introduces blocking sync on @MainActor: proc.waitUntilExit() in resolveWSBin() and NSLock() protecting continuation, violating swift-blocking-runtime rules. Replace waitUntilExit() with async process; use @MainActor isolation instead of NSLock for single-fire continuation.
Cmux Swift Concurrency ❌ Error New Combine usage (Timer.publish().sink() + @Published) for animation in PromptLauncherModel; replace with async/await Task.sleep and @Observable. Replace Timer.publish().autoconnect().sink with async Task loop using Task.sleep(); use @Observable instead of ObservableObject+@published.
Cmux Swift File And Package Boundaries ❌ Error PR adds 354 lines to ContentView.swift (15790 lines, exceeding 250-line threshold). Mixes UI/AppKit/subprocess/parsing. Review comment requests extraction. Extract PromptTextEditor, PromptLauncherModel, and UI helpers into dedicated file or small SwiftPM package.
Cmux Swift Logging ❌ Error TerminalController.swift adds unguarded print() calls in socket listener code (lines 1193, 1248, 1262, 1275, 1302), violating the rule against print statements in production runtime code. Guard with #if DEBUG, convert to Logger, or remove these socket listener diagnostic prints.
Cmux Full Internationalization ❌ Error sidebar.prompt_launcher entries only have en/ja translations; missing 16 of the 19 required locales. Violates full-internationalization requirement for app string catalog entries. Add translations for ar, bs, da, de, es, fr, it, ko, nb, pl, pt-BR, ru, th, tr, uk, zh-Hans, zh-Hant to sidebar.prompt_launcher entries in Resources/Localizable.xcstrings.
Cmux Swiftui State Layout ❌ Error PR introduces PromptLauncherModel as new ObservableObject with @Published properties instead of modern @Observable; violates swiftui-state-layout.md rule on modern state patterns. Replace PromptLauncherModel's ObservableObject + @Published with @Observable macro and @State for new SwiftUI state, as modern shape already used elsewhere in file.
Cmux Architecture Rethink ❌ Error ws reset matches on mutable customTitle (wrong workspace reset possible); stale cleanup skips clearAgentPID leaving incomplete state; prompt text lost on error without recovery. Use stable slot identifier for ws reset; route cleanup through clearAgentPID(); keep prompt until success confirmed; extract 250+ lines to files per size rules.
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Cmux Swift @Concurrent ❓ Inconclusive No result was produced after verification. Marking as INCONCLUSIVE. Re-run the check or adjust instructions to produce a final result.
✅ Passed checks (8 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main features: sidebar prompt launcher with model/target pickers and an SSH status API, accurately reflecting the primary changes in the changeset.
Description check ✅ Passed The PR description covers the summary of changes, test plan with checklist, and includes context. However, it lacks a dedicated Testing section and Demo Video section as specified in the template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PromptLauncherModel correctly marked @MainActor; nonisolated removals are safe—Logger is Sendable, value types lack implicit MainActor, nested types isolated in @MainActor context.
Cmux No Hacky Sleeps ✅ Passed Only in-scope file is daemon/remote/cmd/cmuxd-remote/cli.go with set-status dispatcher registration and parameter mapping; no sleep(), timers, or polling patterns detected.
Cmux User-Facing Error Privacy ✅ Passed All user-facing error messages comply with privacy rules: no vendor names, provider details, credentials, tokens, or environment variables exposed. All errors are generic.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed SidebarPromptLauncher adds embedded sidebar views, not standalone windows. No NSWindow, NSPanel, NSWindowController, Window(), or WindowGroup instances are created.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/local-customizations

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@maucher

maucher commented May 25, 2026

Copy link
Copy Markdown
Contributor Author

Closing — this PR was opened against the wrong repo by mistake. Will open on the fork instead.

@maucher maucher closed this May 25, 2026
@greptile-apps

greptile-apps Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a sidebar prompt launcher (with target/model pickers and streaming progress), a sidebar.set_status socket command plus the cmuxd-remote set-status CLI, auto-ws reset on slotted workspace close, and a broad nonisolated annotation cleanup across the Swift codebase.

  • The PromptLauncherModel drives the launcher; it spawns a Process for ws, streams output into the text field, and unlocks the UI as soon as the "Waiting for…" line appears in ws stdout.
  • v2SidebarSetStatus / cmuxd-remote set-status let remote SSH sessions push status badge updates into the sidebar, wired through the existing V2 socket protocol.
  • The stale-process cleanup in TabManager now directly mutates statusEntries/agentPIDs instead of calling clearAgentPID, bypassing the key-mapping step that converts dot-compound agent PID keys to their shorter status keys.

Confidence Score: 3/5

Two defects in the main feature path need fixing before merge: the main-actor block in ws binary resolution, and the stale-sidebar-status bug from the inlined key removal.

The resolveWSBin fallback blocks the main thread synchronously while waiting for /usr/bin/which, freezing the UI for any user whose binary isn't in the three hardcoded paths. The stale-process cleanup now removes statusEntries with the raw agent-PID key, bypassing the dot-prefix stripping that clearAgentPID performs — sidebar badges for processes that die without a clean SessionEnd won't be cleared. Both affect the primary new feature and a pre-existing maintenance path. The string-catalog gap covers 17 locales and the unused @EnvironmentObject would crash in any preview context.

Sources/ContentView.swift (blocking main-actor call, ObservableObject pattern, unused environment object), Sources/TabManager.swift (key-mapping bypass in stale-PID cleanup, hardcoded fallback string), Resources/Localizable.xcstrings (missing translations for 17 locales)

Important Files Changed

Filename Overview
Sources/ContentView.swift Adds SidebarPromptLauncher with PromptLauncherModel (ObservableObject instead of @observable), blocking proc.waitUntilExit() in resolveWSBin() on the main actor, unused @EnvironmentObject, and a DispatchQueue.main.async guard that is dead code in NSViewRepresentable.
Sources/TabManager.swift Inlines clearAgentPID without the agentStatusKey key-mapping, risking stale sidebar status entries; adds triggerWsResetIfSlotted; changes session snapshot filter; adds hardcoded 'Untitled Tab' fallback.
Resources/Localizable.xcstrings Adds two new strings with only en/ja translations; 17 other supported locales are missing entries.
Sources/TerminalController.swift Adds sidebar.set_status socket command and v2SidebarSetStatus handler; removes redundant nonisolated annotations; changes are self-contained and correct.
daemon/remote/cmd/cmuxd-remote/cli.go Adds set-status command wiring positional args to key/value; clean and consistent with existing command patterns.
Sources/BackgroundWorkspacePrimeCoordinator.swift Removes redundant nonisolated from nested enums/classes inside @mainactor type; purely cosmetic cleanup, no behavior change.

Sequence Diagram

sequenceDiagram
    participant User
    participant SidebarPromptLauncher
    participant PromptLauncherModel
    participant Process as ws Process
    participant cmux as cmux (socket)

    User->>SidebarPromptLauncher: submit prompt
    SidebarPromptLauncher->>PromptLauncherModel: launch()
    PromptLauncherModel->>PromptLauncherModel: startDotAnimation()
    PromptLauncherModel->>PromptLauncherModel: resolveWSBin() [blocks main actor if which fallback needed]
    PromptLauncherModel->>Process: proc.run()
    loop readabilityHandler (background thread)
        Process-->>PromptLauncherModel: stdout line
        PromptLauncherModel->>PromptLauncherModel: "setLastLogLine (Task @MainActor)"
        alt Waiting for... line detected
            PromptLauncherModel->>PromptLauncherModel: resume continuation (unlock UI)
        end
    end
    Process-->>PromptLauncherModel: terminationHandler resume()
    PromptLauncherModel->>SidebarPromptLauncher: "isLoading = false"

    Note over cmux: SSH remote path
    participant Remote as cmuxd-remote
    Remote->>cmux: sidebar.set_status (key, value, icon, color, priority)
    cmux->>cmux: v2SidebarSetStatus ws.statusEntries[key]
Loading

Reviews (1): Last reviewed commit: "fix: isolate sidebar prompt launcher on ..." | Re-trigger Greptile

Comment thread Sources/ContentView.swift
Comment on lines +11121 to +11135
nonisolated static func resolveWSBin() -> String? {
let home = ProcessInfo.processInfo.environment["HOME"] ?? ""
for path in ["\(home)/bin/ws", "\(home)/.local/bin/ws", "/usr/local/bin/ws"]
where FileManager.default.isExecutableFile(atPath: path) { return path }
let proc = Process()
proc.executableURL = URL(fileURLWithPath: "/usr/bin/which")
proc.arguments = ["ws"]
let pipe = Pipe()
proc.standardOutput = pipe
try? proc.run()
proc.waitUntilExit()
let out = String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines)
return out?.isEmpty == false ? out : nil
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 proc.waitUntilExit() is a synchronous blocking call. resolveWSBin() is called at the top of runWS(target:model:prompt:) async, which executes on @MainActor because PromptLauncherModel is @MainActor-isolated. If the common-case fast paths (checked paths) all miss, the /usr/bin/which ws subprocess will block the main thread until it exits, freezing the UI for the duration of the lookup.

Suggested change
nonisolated static func resolveWSBin() -> String? {
let home = ProcessInfo.processInfo.environment["HOME"] ?? ""
for path in ["\(home)/bin/ws", "\(home)/.local/bin/ws", "/usr/local/bin/ws"]
where FileManager.default.isExecutableFile(atPath: path) { return path }
let proc = Process()
proc.executableURL = URL(fileURLWithPath: "/usr/bin/which")
proc.arguments = ["ws"]
let pipe = Pipe()
proc.standardOutput = pipe
try? proc.run()
proc.waitUntilExit()
let out = String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines)
return out?.isEmpty == false ? out : nil
}
nonisolated static func resolveWSBin() async -> String? {
let home = ProcessInfo.processInfo.environment["HOME"] ?? ""
for path in ["\(home)/bin/ws", "\(home)/.local/bin/ws", "/usr/local/bin/ws"]
where FileManager.default.isExecutableFile(atPath: path) { return path }
return await withCheckedContinuation { cont in
let proc = Process()
proc.executableURL = URL(fileURLWithPath: "/usr/bin/which")
proc.arguments = ["ws"]
let pipe = Pipe()
proc.standardOutput = pipe
proc.terminationHandler = { _ in
let out = String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines)
cont.resume(returning: out?.isEmpty == false ? out : nil)
}
if (try? proc.run()) == nil { cont.resume(returning: nil) }
}
}

Rule Used: Flag new blocking or timing-based synchronization ... (source)

Comment thread Sources/TabManager.swift
Comment on lines 2199 to 2202
for key in keysToRemove {
tab.clearAgentPID(key: key, clearStatus: true, refreshPorts: false)
tab.statusEntries.removeValue(forKey: key)
tab.agentPIDs.removeValue(forKey: key)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Stale status entries from bypassed key transformation

The replaced clearAgentPID call ran keys through agentStatusKey(forAgentPIDKey:), which strips everything after the first dot when the full key has no direct match in statusEntries. The inlined removal uses the raw key from agentPIDs, so dot-compound keys miss the corresponding status entry entirely — stale badges remain visible in the sidebar. removeAgentPIDOwnership is also skipped, leaving orphaned entries in agentPIDPanelIdsByKey.

Comment on lines +3111 to +3112
"sidebar.prompt_launcher.placeholder": { "extractionState": "manual", "localizations": { "en": { "stringUnit": { "state": "translated", "value": "Prompt…" } }, "ja": { "stringUnit": { "state": "translated", "value": "プロンプト…" } } } },
"sidebar.prompt_launcher.send": { "extractionState": "manual", "localizations": { "en": { "stringUnit": { "state": "translated", "value": "Send" } }, "ja": { "stringUnit": { "state": "translated", "value": "送信" } } } },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The two new strings only carry en and ja translations, but the catalog already supports 19 locales — ar, bs, da, de, es, fr, it, km, ko, nb, ok, pl, pt-BR, ru, th, tr, uk, and more. Every other locale will fall back to the English value, violating the full-i18n rule that requires matching entries for every locale already in the touched catalog.

Rule Used: Flag production user-facing text that is not fully... (source)

Comment thread Sources/TabManager.swift
if let collapsed, !collapsed.isEmpty {
return collapsed
}
return "Untitled Tab"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 "Untitled Tab" is a hardcoded English string. It is user-visible text (shown in the "close other tabs" confirmation prompt), so it must be routed through String(localized:defaultValue:) with a matching catalog entry for every supported locale.

Suggested change
return "Untitled Tab"
return String(localized: "tab.untitled", defaultValue: "Untitled Tab")

Rule Used: Flag production user-facing text that is not fully... (source)

Comment thread Sources/ContentView.swift
Comment on lines +10982 to +10983
@MainActor
private final class PromptLauncherModel: ObservableObject {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 PromptLauncherModel is already @MainActor-isolated, so ObservableObject/@Published is the legacy pattern here. The cmux rule flags ObservableObject when @Observable is the correct shape — a single-actor model avoids whole-view invalidation on every @Published change and aligns with the codebase's modernization direction.

Suggested change
@MainActor
private final class PromptLauncherModel: ObservableObject {
@Observable @MainActor
private final class PromptLauncherModel {

Rule Used: Flag SwiftUI changes that can cause stale state, b... (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!

Comment thread Sources/ContentView.swift
Comment on lines +11173 to +11175
private struct SidebarPromptLauncher: View {
@StateObject private var model = PromptLauncherModel()
@EnvironmentObject var tabManager: TabManager

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 tabManager is declared as an @EnvironmentObject but is never referenced in SidebarPromptLauncher's body or helpers. The unused injection will crash at runtime if the view is shown in any context where TabManager is not in the environment (e.g., a preview or isolated test harness).

Suggested change
private struct SidebarPromptLauncher: View {
@StateObject private var model = PromptLauncherModel()
@EnvironmentObject var tabManager: TabManager
private struct SidebarPromptLauncher: View {
@StateObject private var model = PromptLauncherModel()

Comment thread Sources/ContentView.swift
Comment on lines +10911 to +10916
func updateNSView(_ scrollView: NSScrollView, context: Context) {
guard Thread.isMainThread else {
DispatchQueue.main.async { updateNSView(scrollView, context: context) }
return
}
guard let tv = scrollView.documentView as? NSTextView else { return }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 SwiftUI guarantees updateNSView(_:context:) is always called on the main thread, so the Thread.isMainThread guard and DispatchQueue.main.async re-dispatch are dead code. The async re-dispatch also introduces a one-frame lag and breaks SwiftUI's ordering guarantee — two rapid calls could arrive out of order after a third real update.

Suggested change
func updateNSView(_ scrollView: NSScrollView, context: Context) {
guard Thread.isMainThread else {
DispatchQueue.main.async { updateNSView(scrollView, context: context) }
return
}
guard let tv = scrollView.documentView as? NSTextView else { return }
func updateNSView(_ scrollView: NSScrollView, context: Context) {
guard let tv = scrollView.documentView as? NSTextView else { return }

Rule Used: Flag new legacy async patterns in cmux-owned Swift... (source)

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