Skip to content

Commit 58a0b07

Browse files
authored
fix(macos): keep A2UI canvas content visible (#75039)
1 parent eecd758 commit 58a0b07

4 files changed

Lines changed: 43 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ Docs: https://docs.openclaw.ai
6868
- Feishu: skip empty-text messages (e.g. `{"text":""}`) that carry no media, so no blank user turn is written to the session and downstream LLM providers cannot reject the request with "messages must not be empty". (#74634) Thanks @xdengli and @hclsys.
6969
- Feishu/Bitable: clean up newly created placeholder rows whose fields contain only default empty values while preserving meaningful link, attachment, user, number, boolean, and location values during create-app cleanup. (#73920) Carries forward #40602. Thanks @boat2moon.
7070
- macOS app: keep attach-only mode and the Debug Settings launchd toggle marker-only, so launching with `--attach-only`/`--no-launchd` no longer uninstalls the Gateway LaunchAgent or drops active sessions. (#72174) Thanks @DolencLuka.
71+
- macOS Canvas: stop auto-reloading the current A2UI host during push/eval/snapshot flows, so pushed A2UI content remains visible instead of returning to the empty Canvas shell. Fixes #73337. Thanks @Gr4via.
7172
- Plugin SDK: restore the deprecated `plugin-sdk/zalouser` command-auth facade so published Lark/Zalo plugins that import it load on current hosts. Fixes #74702. Thanks @Goron01.
7273
- Plugins/runtime-deps: include bundled provider plugins when `models.providers`, auth profiles, agent defaults, or subagent model refs configure that provider, while keeping inactive default-enabled provider plugins out of doctor repair. Refs #74307. Thanks @Skeptomenos.
7374
- Plugins/runtime: resolve relative plugin `api.resolvePath` inputs against the plugin root instead of the host working directory, while keeping absolute and home paths user-resolved. Fixes #74718. Thanks @jimdawdy-hub.

apps/macos/Sources/OpenClaw/CanvasManager.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,9 @@ final class CanvasManager {
184184

185185
private func maybeAutoNavigateToA2UI(controller: CanvasWindowController, a2uiUrl: String?) {
186186
guard let a2uiUrl else { return }
187-
let shouldNavigate = controller.shouldAutoNavigateToA2UI(lastAutoTarget: self.lastAutoA2UIUrl)
187+
let shouldNavigate = controller.shouldAutoNavigateToA2UI(
188+
lastAutoTarget: self.lastAutoA2UIUrl,
189+
candidateTarget: a2uiUrl)
188190
guard shouldNavigate else {
189191
Self.logger.debug("canvas auto-nav skipped; target unchanged")
190192
return

apps/macos/Sources/OpenClaw/CanvasWindowController.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,12 +319,14 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
319319
self.sessionDir.path
320320
}
321321

322-
func shouldAutoNavigateToA2UI(lastAutoTarget: String?) -> Bool {
323-
let trimmed = (self.currentTarget ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
324-
if trimmed.isEmpty || trimmed == "/" { return true }
322+
func shouldAutoNavigateToA2UI(lastAutoTarget: String?, candidateTarget: String) -> Bool {
323+
let current = (self.currentTarget ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
324+
let candidate = candidateTarget.trimmingCharacters(in: .whitespacesAndNewlines)
325+
if current.isEmpty || current == "/" { return true }
326+
if !candidate.isEmpty, current == candidate { return false }
325327
if let lastAuto = lastAutoTarget?.trimmingCharacters(in: .whitespacesAndNewlines),
326328
!lastAuto.isEmpty,
327-
trimmed == lastAuto
329+
current == lastAuto
328330
{
329331
return true
330332
}

apps/macos/Tests/OpenClawIPCTests/CanvasWindowSmokeTests.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,37 @@ struct CanvasWindowSmokeTests {
4646
controller.hideCanvas()
4747
controller.close()
4848
}
49+
50+
@Test func `A2UI auto navigation is idempotent for current host target`() throws {
51+
let root = FileManager().temporaryDirectory
52+
.appendingPathComponent("openclaw-canvas-test-\(UUID().uuidString)")
53+
try FileManager().createDirectory(at: root, withIntermediateDirectories: true)
54+
defer { try? FileManager().removeItem(at: root) }
55+
56+
let controller = try CanvasWindowController(
57+
sessionKey: "main",
58+
root: root,
59+
presentation: .window)
60+
defer { controller.close() }
61+
62+
let oldTarget = "http://127.0.0.1:18789/__openclaw__/a2ui/?platform=macos"
63+
let currentTarget = "http://127.0.0.1:18790/__openclaw__/a2ui/?platform=macos"
64+
let userTarget = "https://github.com/openclaw/openclaw"
65+
66+
#expect(controller.shouldAutoNavigateToA2UI(lastAutoTarget: nil, candidateTarget: currentTarget) == true)
67+
68+
controller.load(target: "/")
69+
#expect(controller.shouldAutoNavigateToA2UI(lastAutoTarget: nil, candidateTarget: currentTarget) == true)
70+
71+
controller.load(target: currentTarget)
72+
#expect(controller
73+
.shouldAutoNavigateToA2UI(lastAutoTarget: currentTarget, candidateTarget: currentTarget) == false)
74+
75+
controller.load(target: oldTarget)
76+
#expect(controller.shouldAutoNavigateToA2UI(lastAutoTarget: oldTarget, candidateTarget: currentTarget) == true)
77+
78+
controller.load(target: userTarget)
79+
#expect(controller
80+
.shouldAutoNavigateToA2UI(lastAutoTarget: currentTarget, candidateTarget: currentTarget) == false)
81+
}
4982
}

0 commit comments

Comments
 (0)