diff --git a/apps/macos/Sources/Clawdis/DebugActions.swift b/apps/macos/Sources/Clawdis/DebugActions.swift index 4581e1ed9..0b7a98bd8 100644 --- a/apps/macos/Sources/Clawdis/DebugActions.swift +++ b/apps/macos/Sources/Clawdis/DebugActions.swift @@ -240,6 +240,12 @@ enum DebugActions { typealias PortListener = PortGuardian.ReportListener typealias PortReport = PortGuardian.PortReport + @MainActor + static func openChatInBrowser() async { + let session = WebChatManager.shared.preferredSessionKey() + await WebChatManager.shared.openInBrowser(sessionKey: session) + } + static func checkGatewayPorts() async -> [PortReport] { let mode = CommandResolver.connectionSettings().mode return await PortGuardian.shared.diagnose(mode: mode) diff --git a/apps/macos/Sources/Clawdis/MenuContentView.swift b/apps/macos/Sources/Clawdis/MenuContentView.swift index 0a2cd1c1b..961041c65 100644 --- a/apps/macos/Sources/Clawdis/MenuContentView.swift +++ b/apps/macos/Sources/Clawdis/MenuContentView.swift @@ -141,6 +141,11 @@ struct MenuContent: View { } label: { Label("Send Test Notification", systemImage: "bell") } + Button { + Task { await DebugActions.openChatInBrowser() } + } label: { + Label("Open Chat in Browser…", systemImage: "safari") + } Divider() Button { DebugActions.restartGateway() diff --git a/apps/macos/Sources/Clawdis/WebChatWindow.swift b/apps/macos/Sources/Clawdis/WebChatWindow.swift index 0edef9bbe..2a91d4b1f 100644 --- a/apps/macos/Sources/Clawdis/WebChatWindow.swift +++ b/apps/macos/Sources/Clawdis/WebChatWindow.swift @@ -447,6 +447,7 @@ final class WebChatManager { static let shared = WebChatManager() private var windowController: WebChatWindowController? private var panelController: WebChatWindowController? + private var browserTunnel: WebChatTunnel? var onPanelVisibilityChanged: ((Bool) -> Void)? func show(sessionKey: String) { @@ -513,6 +514,37 @@ final class WebChatManager { return "+1003" } + @MainActor + func openInBrowser(sessionKey: String) async { + let port = AppStateStore.webChatPort + let base: URL + if CommandResolver.connectionModeIsRemote() { + do { + // Prefer the configured port; fall back if busy. + let tunnel = try await WebChatTunnel.create( + remotePort: port, + preferredLocalPort: UInt16(port)) + self.browserTunnel?.terminate() + self.browserTunnel = tunnel + guard let local = tunnel.localPort else { + throw NSError(domain: "WebChat", code: 7, userInfo: [NSLocalizedDescriptionKey: "Tunnel missing local port"]) + } + base = URL(string: "http://127.0.0.1:\(local)/")! + } catch { + NSAlert(error: error).runModal() + return + } + } else { + base = URL(string: "http://127.0.0.1:\(port)/")! + } + + var comps = URLComponents(url: base, resolvingAgainstBaseURL: false) + comps?.path = "/webchat/" + comps?.queryItems = [URLQueryItem(name: "session", value: sessionKey)] + guard let url = comps?.url else { return } + NSWorkspace.shared.open(url) + } + func close() { self.windowController?.shutdown() self.windowController?.close()