Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ __pycache__/
.zig-cache/
zig-out/

# CEFWebView vendored package: CEF binary distribution + built frameworks
vendor/CEFWebView/CEF/
vendor/CEFWebView/Frameworks/
vendor/CEFWebView/.build/

# Node
node_modules/
.next/
Expand Down
31 changes: 31 additions & 0 deletions GhosttyTabs.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
FE001101 /* FileExplorerStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE001001 /* FileExplorerStore.swift */; };
CEF00200 /* CefDebugWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF00201 /* CefDebugWindow.swift */; };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -i 'cefdebugwindow.swift' -x sed -n '1,40p' {}
echo "---"
fd -i 'cefdebugwindow.swift' -x rg -n '#if DEBUG|#endif|import CEFWebView' {}

Repository: manaflow-ai/cmux

Length of output: 1417


🏁 Script executed:

fd -i 'cefdebugwindow.swift' -x cat -n {}

Repository: manaflow-ai/cmux

Length of output: 5200


🏁 Script executed:

rg -n '#if DEBUG|#endif' --glob '**/CefDebugWindow.swift'

Repository: manaflow-ai/cmux

Length of output: 42


🏁 Script executed:

rg -n 'CEF' GhosttyTabs.xcodeproj/project.pbxproj | head -20

Repository: manaflow-ai/cmux

Length of output: 1025


Wrap CefDebugWindow.swift entirely in #if DEBUG / #endif.

The file imports CEFWebView and is added unconditionally to the build sources (line 872). Since CEFWebView is not embedded in Release builds, the file must be guarded with #if DEBUG at the top and #endif at the end to prevent link failures.

Also applies to: 872-872

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GhosttyTabs.xcodeproj/project.pbxproj` at line 11, Wrap the entire contents
of CefDebugWindow.swift with a DEBUG-only compile guard: add `#if` DEBUG at the
very top of the file and `#endif` at the very bottom so the file (referenced as
CEF00201 / PBXBuildFile CEF00200) is excluded from Release builds; ensure the
import of CEFWebView and all types/extension definitions remain inside the guard
so no symbols from CefDebugWindow.swift are referenced in non-DEBUG builds.

FE001102 /* FileExplorerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE001002 /* FileExplorerView.swift */; };
FE002101 /* FileExplorerRootResolverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE002001 /* FileExplorerRootResolverTests.swift */; };
FE002102 /* FileExplorerStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE002002 /* FileExplorerStoreTests.swift */; };
Expand Down Expand Up @@ -212,6 +213,7 @@

/* Begin PBXFileReference section */
FE001001 /* FileExplorerStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileExplorerStore.swift; sourceTree = "<group>"; };
CEF00201 /* CefDebugWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CefDebugWindow.swift; sourceTree = "<group>"; };
FE001002 /* FileExplorerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileExplorerView.swift; sourceTree = "<group>"; };
FE002001 /* FileExplorerRootResolverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileExplorerRootResolverTests.swift; sourceTree = "<group>"; };
FE002002 /* FileExplorerStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileExplorerStoreTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -533,6 +535,7 @@
A5001655 /* CmuxDirectoryTrust.swift */,
FE001001 /* FileExplorerStore.swift */,
FE001002 /* FileExplorerView.swift */,
CEF00201 /* CefDebugWindow.swift */,
);
path = Sources;
sourceTree = "<group>";
Expand Down Expand Up @@ -661,6 +664,7 @@
A5001271 /* PostHog */,
A5001261 /* Bonsplit */,
A5001291 /* MarkdownUI */,
CEF00101 /* CEFWebView */,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep CEFWebView out of default app dependency graph

Adding CEFWebView as an unconditional GhosttyTabs package dependency makes every cmux/cmux-unit build compile and link the CEF wrapper, which requires binaries under vendor/CEFWebView/Frameworks. Those artifacts are not in the repo (the directory is generated by scripts/setup-cefwebview.sh), and existing setup/CI flows build the app directly, so fresh checkouts will fail before tests can run. This dependency should be gated to the debug-only path or bootstrap CEF artifacts as part of standard setup/build workflows.

Useful? React with 👍 / 👎.

@cubic-dev-ai cubic-dev-ai Bot Apr 16, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: CEFWebView is added as a package dependency but not linked in the target’s Frameworks build phase.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At GhosttyTabs.xcodeproj/project.pbxproj, line 667:

<comment>CEFWebView is added as a package dependency but not linked in the target’s Frameworks build phase.</comment>

<file context>
@@ -661,6 +664,7 @@
 					A5001271 /* PostHog */,
 					A5001261 /* Bonsplit */,
 					A5001291 /* MarkdownUI */,
+					CEF00101 /* CEFWebView */,
 				);
 			name = GhosttyTabs;
</file context>
Fix with Cubic

);
name = GhosttyTabs;
productName = GhosttyTabs;
Expand Down Expand Up @@ -781,6 +785,7 @@
A5001272 /* XCRemoteSwiftPackageReference "posthog-ios" */,
A5001292 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */,
A5001260 /* XCLocalSwiftPackageReference "bonsplit" */,
CEF00100 /* XCLocalSwiftPackageReference "CEFWebView" */,
);
productRefGroup = A5001042 /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -864,6 +869,7 @@
A5001654 /* CmuxDirectoryTrust.swift in Sources */,
FE001101 /* FileExplorerStore.swift in Sources */,
FE001102 /* FileExplorerView.swift in Sources */,
CEF00200 /* CefDebugWindow.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -1047,11 +1053,19 @@
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = NO;
GENERATE_INFOPLIST_FILE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/vendor/CEFWebView/Frameworks",
);
INFOPLIST_FILE = Resources/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/vendor/CEFWebView/Frameworks",
);
Comment on lines +1056 to +1068

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

CEFWebView is wired into Release builds but no Embed Frameworks step exists for Chromium Embedded Framework.framework.

The Release config (A5001083) gains the same FRAMEWORK_SEARCH_PATHS / LIBRARY_SEARCH_PATHS entries as Debug, and the CEFWebView product is listed in packageProductDependencies for the GhosttyTabs target unconditionally. The Embed Frameworks build phase (A5001020) is still empty, so a Release build produced via plain xcodebuild (without running scripts/embed-cefwebview.sh) will link CEFWrapper against Chromium Embedded Framework but ship a cmux.app missing that framework, yielding a dyld load failure at launch.

Per the PR description this is a Phase-2 spike not intended for dogfooding, but it's worth either (a) gating the package product dependency / search paths behind a Debug-only xcconfig, or (b) adding a Run Script phase to Release that invokes scripts/embed-cefwebview.sh "${TARGET_BUILD_DIR}/${WRAPPER_NAME}" so Release builds remain self-contained. Otherwise Release TestFlight/notarized archives will ship broken the moment anything in the app imports CEFWebView.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GhosttyTabs.xcodeproj/project.pbxproj` around lines 1056 - 1068, The Release
config (A5001083) currently includes CEFWebView search paths and GhosttyTabs
unconditionally lists the CEFWebView package product, but the Embed Frameworks
phase (A5001020) is empty so Release builds will omit Chromium Embedded
Framework.framework; fix by either (a) gating the
packageProductDependencies/search paths to Debug-only via an xcconfig or
conditional project setting so GhosttyTabs does not depend on CEFWebView in
Release, or (b) add a Run Script build phase (or populate A5001020) that calls
scripts/embed-cefwebview.sh "${TARGET_BUILD_DIR}/${WRAPPER_NAME}" in Release
builds so the Chromium Embedded Framework is embedded for distribution; update
the project’s packageProductDependencies and build phase settings accordingly
and ensure CEFWrapper links remain consistent.

MARKETING_VERSION = 0.63.2;
OTHER_LDFLAGS = (
"-lc++",
Expand Down Expand Up @@ -1086,11 +1100,19 @@
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = NO;
GENERATE_INFOPLIST_FILE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/vendor/CEFWebView/Frameworks",
);
INFOPLIST_FILE = Resources/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/vendor/CEFWebView/Frameworks",
);
MARKETING_VERSION = 0.63.2;
OTHER_LDFLAGS = (
"-lc++",
Expand Down Expand Up @@ -1312,6 +1334,10 @@
isa = XCLocalSwiftPackageReference;
relativePath = vendor/bonsplit;
};
CEF00100 /* XCLocalSwiftPackageReference "CEFWebView" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = vendor/CEFWebView;
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
Expand Down Expand Up @@ -1340,6 +1366,11 @@
package = A5001292 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */;
productName = MarkdownUI;
};
CEF00101 /* CEFWebView */ = {
isa = XCSwiftPackageProductDependency;
package = CEF00100 /* XCLocalSwiftPackageReference "CEFWebView" */;
productName = CEFWebView;
};
/* End XCSwiftPackageProductDependency section */

/* Begin XCConfigurationList section */
Expand Down
128 changes: 128 additions & 0 deletions Sources/CefDebugWindow.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import AppKit
import CEFWebView
import SwiftUI

/// Debug-only window hosting a CEFWebView (Chromium) so we can dogfood the
/// engine before plumbing it into BrowserPanel. Open via Debug > Debug Windows >
/// Chromium (CEF)…
@MainActor
final class CefDebugWindowController: NSWindowController, NSWindowDelegate {
static let shared = CefDebugWindowController()

private init() {
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 1024, height: 720),
styleMask: [.titled, .closable, .miniaturizable, .resizable],
backing: .buffered,
defer: false
)
window.title = "Chromium (CEF)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

User-facing strings are not localized.

"Chromium (CEF)" window title, the "URL" TextField placeholder, and "CEF initialization failed" headline are bare literals. Per coding guidelines, all user-facing strings must go through String(localized: "key", defaultValue: "...") with entries in Resources/Localizable.xcstrings. "Chromium" / "CEF" are brand names and should stay untranslated inside the default value, but the surrounding UI chrome ("URL", "… initialization failed") should be localized.

As per coding guidelines: "All user-facing strings must be localized. Use String(localized: "key.name", defaultValue: "English text") for every string shown in the UI".

Also applies to: 66-66, 86-86

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/CefDebugWindow.swift` at line 19, Replace bare UI string literals
with localized variants using String(localized: "key", defaultValue: "...") and
add corresponding keys to Resources/Localizable.xcstrings: update the
window.title assignment in CefDebugWindow (window.title = ...) to use
String(localized: "cef.windowTitle", defaultValue: "Chromium (CEF)"), replace
the TextField placeholder "URL" with String(localized: "cef.urlPlaceholder",
defaultValue: "URL"), and change the error headline "CEF initialization failed"
to String(localized: "cef.initFailedHeadline", defaultValue: "CEF initialization
failed"); keep the brand tokens "Chromium" and "CEF" verbatim inside the
defaultValue strings and add the three keys to Localizable.xcstrings.

window.identifier = NSUserInterfaceItemIdentifier("cmux.cefDebug")
window.isReleasedWhenClosed = false
window.center()
window.contentView = NSHostingView(rootView: CefDebugView())
super.init(window: window)
window.delegate = self
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func show() {
window?.makeKeyAndOrderFront(nil)
}
}

private struct CefDebugView: View {
@State private var url: URL? = URL(string: "https://www.google.com")
@State private var urlText: String = "https://www.google.com"

@cubic-dev-ai cubic-dev-ai Bot Apr 16, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: urlText is only written by user input; after navigation, redirects, or back/forward, state.currentURL updates but the address bar keeps showing whatever was last typed. Add .onChange(of: state.currentURL) to mirror the actual URL into urlText (skip updates while the field is focused to avoid clobbering in-progress typing).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Sources/CefDebugWindow.swift, line 40:

<comment>`urlText` is only written by user input; after navigation, redirects, or back/forward, `state.currentURL` updates but the address bar keeps showing whatever was last typed. Add `.onChange(of: state.currentURL)` to mirror the actual URL into `urlText` (skip updates while the field is focused to avoid clobbering in-progress typing).</comment>

<file context>
@@ -0,0 +1,128 @@
+
+private struct CefDebugView: View {
+    @State private var url: URL? = URL(string: "https://www.google.com")
+    @State private var urlText: String = "https://www.google.com"
+    @State private var state = CEFWebViewState()
+
</file context>
Fix with Cubic

@State private var state = CEFWebViewState()

var body: some View {
VStack(spacing: 0) {
HStack(spacing: 6) {
Button {
state.goBack()
} label: {
Image(systemName: "chevron.left")
}
.disabled(!state.canGoBack)

Button {
state.goForward()
} label: {
Image(systemName: "chevron.right")
}
.disabled(!state.canGoForward)

Button {
state.reload()
} label: {
Image(systemName: "arrow.clockwise")
}

TextField("URL", text: $urlText)
.textFieldStyle(.roundedBorder)
.onSubmit {
if let parsed = parseURL(urlText) {
url = parsed
}
}
Comment on lines +66 to +72

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

urlText drifts out of sync with the actual page URL.

urlText is only written by the user; after navigation, redirects, back/forward, or clicks inside the page, state.currentURL updates but the address bar keeps showing whatever was last typed. Consider mirroring state.currentURL into urlText via .onChange(of: state.currentURL) (skipping updates while the field is focused, to avoid clobbering in-progress typing).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/CefDebugWindow.swift` around lines 66 - 72, The address bar TextField
(binding urlText → url via parseURL in the onSubmit) drifts because urlText is
never updated from navigation; add an .onChange(of: state.currentURL) handler
near the TextField to set urlText = state.currentURL (or its string) when
navigation changes, but avoid clobbering in-progress edits by gating updates
with a FocusState/Bool (e.g., addressBarFocused) so you only mirror
state.currentURL into urlText when the field is not focused; keep references to
urlText, url, parseURL, state.currentURL and the TextField block so the change
is localized.

}
.padding(8)

Divider()

ZStack {
CEFWebView(url: $url, state: $state)

if let err = state.initializationError {
VStack(spacing: 12) {
Image(systemName: "exclamationmark.triangle.fill")
.font(.largeTitle)
.foregroundStyle(.yellow)
Text("CEF initialization failed")
.font(.headline)
ScrollView {
Text(err)
.font(.system(.caption, design: .monospaced))
.textSelection(.enabled)
.padding()
}
}
.padding()
.background(.regularMaterial)
}
}

HStack {
if state.isLoading {
ProgressView().controlSize(.small)
}
Text(state.title ?? state.currentURL?.absoluteString ?? "")
.font(.caption)
.lineLimit(1)
.truncationMode(.middle)
Spacer()
if state.rendererHelperFailed {
Text(state.rendererFailureStatusLine)
.font(.caption)
.foregroundStyle(.red)
}
}
.padding(.horizontal, 8)
.padding(.vertical, 4)
}
}

private func parseURL(_ text: String) -> URL? {
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return nil }
if trimmed.contains("://") {
return URL(string: trimmed)
}
return URL(string: "https://\(trimmed)")
}
}
3 changes: 3 additions & 0 deletions Sources/cmuxApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,9 @@ struct cmuxApp: App {
Button("Background Debug…") {
BackgroundDebugWindowController.shared.show()
}
Button("Chromium (CEF)…") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Localize the new Debug menu label.

Line 491 adds a user-facing bare string literal ("Chromium (CEF)…"). Please localize it with String(localized:..., defaultValue:...).

Suggested patch
-                    Button("Chromium (CEF)…") {
+                    Button(
+                        String(
+                            localized: "debug.menu.chromiumCef",
+                            defaultValue: "Chromium (CEF)…"
+                        )
+                    ) {
                         CefDebugWindowController.shared.show()
                     }

As per coding guidelines, “All user-facing strings must be localized. Use String(localized: "key.name", defaultValue: "English text") for every string shown in the UI.”

📝 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.

Suggested change
Button("Chromium (CEF)…") {
Button(
String(
localized: "debug.menu.chromiumCef",
defaultValue: "Chromium (CEF)…"
)
) {
CefDebugWindowController.shared.show()
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/cmuxApp.swift` at line 491, Replace the hard-coded button label
"Chromium (CEF)…" with a localized string; update the Button initializer to use
String(localized: "debug.menu.chromium_cef", defaultValue: "Chromium (CEF)…")
(or another appropriate localization key) so the user-facing label in the
Button("Chromium (CEF)…") call is localized; ensure you apply this change where
the Button(...) for the Debug menu is created.

CefDebugWindowController.shared.show()
}
Button("Browser Import Hint Debug…") {
BrowserImportHintDebugWindowController.shared.show()
}
Expand Down
Loading
Loading