Canvas: fix A2UI push rendering
This commit is contained in:
@@ -64,11 +64,29 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
|
|||||||
self.webView = WKWebView(frame: .zero, configuration: config)
|
self.webView = WKWebView(frame: .zero, configuration: config)
|
||||||
self.webView.setValue(false, forKey: "drawsBackground")
|
self.webView.setValue(false, forKey: "drawsBackground")
|
||||||
|
|
||||||
self.watcher = CanvasFileWatcher(url: self.sessionDir) { [weak webView] in
|
let sessionDir = self.sessionDir
|
||||||
|
let webView = self.webView
|
||||||
|
self.watcher = CanvasFileWatcher(url: sessionDir) { [weak webView] in
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
|
guard let webView else { return }
|
||||||
|
|
||||||
// Only auto-reload when we are showing local canvas content.
|
// Only auto-reload when we are showing local canvas content.
|
||||||
guard webView?.url?.scheme == CanvasScheme.scheme else { return }
|
guard webView.url?.scheme == CanvasScheme.scheme else { return }
|
||||||
webView?.reload()
|
|
||||||
|
// Avoid reloading the built-in A2UI shell due to filesystem noise (it does not depend on session files).
|
||||||
|
let path = webView.url?.path ?? ""
|
||||||
|
if path.hasPrefix("/__clawdis__/a2ui") { return }
|
||||||
|
if path == "/" || path.isEmpty {
|
||||||
|
let indexA = sessionDir.appendingPathComponent("index.html", isDirectory: false)
|
||||||
|
let indexB = sessionDir.appendingPathComponent("index.htm", isDirectory: false)
|
||||||
|
if !FileManager.default.fileExists(atPath: indexA.path),
|
||||||
|
!FileManager.default.fileExists(atPath: indexB.path)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webView.reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -234,12 +234,20 @@ enum ControlRequestHandler {
|
|||||||
private static func handleCanvasA2UI(session: String, command: CanvasA2UICommand, jsonl: String?) async -> Response {
|
private static func handleCanvasA2UI(session: String, command: CanvasA2UICommand, jsonl: String?) async -> Response {
|
||||||
guard self.canvasEnabled() else { return Response(ok: false, message: "Canvas disabled by user") }
|
guard self.canvasEnabled() else { return Response(ok: false, message: "Canvas disabled by user") }
|
||||||
do {
|
do {
|
||||||
// Ensure the Canvas is visible and the default page is loaded.
|
// Ensure the Canvas is visible without forcing a navigation/reload.
|
||||||
_ = try await MainActor.run {
|
_ = try await MainActor.run {
|
||||||
try CanvasManager.shared.show(sessionKey: session, path: "/")
|
try CanvasManager.shared.show(sessionKey: session, path: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the in-page A2UI bridge. If it doesn't appear, force-load the bundled A2UI shell once.
|
||||||
|
var ready = await Self.waitForCanvasA2UI(session: session, requireBuiltinPath: false, timeoutMs: 2_000)
|
||||||
|
if !ready {
|
||||||
|
_ = try await MainActor.run {
|
||||||
|
try CanvasManager.shared.show(sessionKey: session, path: "/__clawdis__/a2ui/")
|
||||||
|
}
|
||||||
|
ready = await Self.waitForCanvasA2UI(session: session, requireBuiltinPath: true, timeoutMs: 5_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
let ready = await Self.waitForCanvasA2UI(session: session, timeoutMs: 2_000)
|
|
||||||
guard ready else { return Response(ok: false, message: "A2UI not ready") }
|
guard ready else { return Response(ok: false, message: "A2UI not ready") }
|
||||||
|
|
||||||
let js: String
|
let js: String
|
||||||
@@ -354,14 +362,29 @@ enum ControlRequestHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func waitForCanvasA2UI(session: String, timeoutMs: Int) async -> Bool {
|
private static func waitForCanvasA2UI(session: String, requireBuiltinPath: Bool, timeoutMs: Int) async -> Bool {
|
||||||
let clock = ContinuousClock()
|
let clock = ContinuousClock()
|
||||||
let deadline = clock.now.advanced(by: .milliseconds(timeoutMs))
|
let deadline = clock.now.advanced(by: .milliseconds(timeoutMs))
|
||||||
while clock.now < deadline {
|
while clock.now < deadline {
|
||||||
do {
|
do {
|
||||||
let res = try await CanvasManager.shared.eval(
|
let res = try await CanvasManager.shared.eval(
|
||||||
sessionKey: session,
|
sessionKey: session,
|
||||||
javaScript: "(() => globalThis.clawdisA2UI ? 'ready' : '')()")
|
javaScript: """
|
||||||
|
(() => {
|
||||||
|
try {
|
||||||
|
if (document?.readyState !== 'complete') { return ''; }
|
||||||
|
if (!globalThis.clawdisA2UI) { return ''; }
|
||||||
|
if (typeof globalThis.clawdisA2UI.applyMessages !== 'function') { return ''; }
|
||||||
|
if (\(requireBuiltinPath ? "true" : "false")) {
|
||||||
|
const p = String(location?.pathname ?? '');
|
||||||
|
if (!p.startsWith('/__clawdis__/a2ui')) { return ''; }
|
||||||
|
}
|
||||||
|
return 'ready';
|
||||||
|
} catch {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
""")
|
||||||
if res == "ready" { return true }
|
if res == "ready" { return true }
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore; keep waiting.
|
// Ignore; keep waiting.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -159,12 +159,14 @@ class ClawdisA2UIHost extends LitElement {
|
|||||||
}
|
}
|
||||||
this.#processor.processMessages(messages);
|
this.#processor.processMessages(messages);
|
||||||
this.#syncSurfaces();
|
this.#syncSurfaces();
|
||||||
|
this.requestUpdate();
|
||||||
return { ok: true, surfaces: this.surfaces.map(([id]) => id) };
|
return { ok: true, surfaces: this.surfaces.map(([id]) => id) };
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.#processor.clearSurfaces();
|
this.#processor.clearSurfaces();
|
||||||
this.#syncSurfaces();
|
this.#syncSurfaces();
|
||||||
|
this.requestUpdate();
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ stop_launch_agent
|
|||||||
|
|
||||||
# 1.5) Bundle web chat assets (single-file JS to avoid import-map issues).
|
# 1.5) Bundle web chat assets (single-file JS to avoid import-map issues).
|
||||||
run_step "bundle webchat" bash -lc "cd '${ROOT_DIR}' && pnpm webchat:bundle"
|
run_step "bundle webchat" bash -lc "cd '${ROOT_DIR}' && pnpm webchat:bundle"
|
||||||
|
# Bundle built-in Canvas A2UI shell (single-file JS, shipped in the app bundle).
|
||||||
|
run_step "bundle canvas a2ui" bash -lc "cd '${ROOT_DIR}' && pnpm canvas:a2ui:bundle"
|
||||||
|
|
||||||
# 2) Rebuild into the same path the packager consumes (.build).
|
# 2) Rebuild into the same path the packager consumes (.build).
|
||||||
run_step "clean build cache" bash -lc "cd '${ROOT_DIR}/apps/macos' && rm -rf .build .build-swift .swiftpm 2>/dev/null || true"
|
run_step "clean build cache" bash -lc "cd '${ROOT_DIR}/apps/macos' && rm -rf .build .build-swift .swiftpm 2>/dev/null || true"
|
||||||
|
|||||||
Reference in New Issue
Block a user