refactor(canvas): host A2UI via gateway
This commit is contained in:
@@ -99,10 +99,12 @@ public struct BridgeHello: Codable, Sendable {
|
||||
public struct BridgeHelloOk: Codable, Sendable {
|
||||
public let type: String
|
||||
public let serverName: String
|
||||
public let canvasHostUrl: String?
|
||||
|
||||
public init(type: String = "hello-ok", serverName: String) {
|
||||
public init(type: String = "hello-ok", serverName: String, canvasHostUrl: String? = nil) {
|
||||
self.type = type
|
||||
self.serverName = serverName
|
||||
self.canvasHostUrl = canvasHostUrl
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,23 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Canvas</title>
|
||||
<style>
|
||||
:root { color-scheme: light dark; }
|
||||
html, body { height: 100%; margin: 0; }
|
||||
body {
|
||||
font: 14px system-ui, -apple-system, BlinkMacSystemFont, "Roboto", sans-serif;
|
||||
background: #0b1020;
|
||||
color: #e5e7eb;
|
||||
overflow: hidden;
|
||||
}
|
||||
clawdis-a2ui-host { display: block; height: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<clawdis-a2ui-host></clawdis-a2ui-host>
|
||||
<script src="a2ui.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -85,16 +85,6 @@
|
||||
pointer-events: none;
|
||||
z-index: 3;
|
||||
}
|
||||
#clawdis-a2ui-wrap {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: none;
|
||||
z-index: 2;
|
||||
}
|
||||
#clawdis-a2ui-wrap clawdis-a2ui-host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
#clawdis-status .card {
|
||||
text-align: center;
|
||||
padding: 16px 18px;
|
||||
@@ -126,9 +116,6 @@
|
||||
<div class="subtitle" id="clawdis-status-subtitle">Waiting for agent</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="clawdis-a2ui-wrap">
|
||||
<clawdis-a2ui-host></clawdis-a2ui-host>
|
||||
</div>
|
||||
<script>
|
||||
(() => {
|
||||
const canvas = document.getElementById('clawdis-canvas');
|
||||
@@ -170,92 +157,6 @@
|
||||
};
|
||||
})();
|
||||
|
||||
(() => {
|
||||
const wrap = document.getElementById('clawdis-a2ui-wrap');
|
||||
if (!wrap) return;
|
||||
|
||||
const candidates = [
|
||||
// iOS (SwiftPM resources flattened)
|
||||
"a2ui.bundle.js",
|
||||
// Android (assets keep directory structure)
|
||||
"../CanvasA2UI/a2ui.bundle.js",
|
||||
"CanvasA2UI/a2ui.bundle.js",
|
||||
];
|
||||
|
||||
const loadScript = (src) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const el = document.createElement("script");
|
||||
el.src = src;
|
||||
el.async = true;
|
||||
el.onload = () => resolve();
|
||||
el.onerror = () => reject(new Error(`failed to load ${src}`));
|
||||
document.head.appendChild(el);
|
||||
});
|
||||
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
const installVisibilityHooks = () => {
|
||||
const api = globalThis.clawdisA2UI;
|
||||
if (!api || typeof api.applyMessages !== "function") return false;
|
||||
if (globalThis.__clawdisA2UIVisibilityHooksInstalled) return true;
|
||||
globalThis.__clawdisA2UIVisibilityHooksInstalled = true;
|
||||
|
||||
const show = () => { wrap.style.display = "block"; };
|
||||
const hide = () => { wrap.style.display = "none"; };
|
||||
|
||||
const sync = () => {
|
||||
try {
|
||||
const surfaces =
|
||||
typeof api.getSurfaces === "function" ? api.getSurfaces() : [];
|
||||
if (Array.isArray(surfaces) && surfaces.length > 0) show();
|
||||
else hide();
|
||||
} catch {
|
||||
hide();
|
||||
}
|
||||
};
|
||||
|
||||
const origApply = api.applyMessages.bind(api);
|
||||
api.applyMessages = (messages) => {
|
||||
const res = origApply(messages);
|
||||
sync();
|
||||
return res;
|
||||
};
|
||||
const origReset = api.reset.bind(api);
|
||||
api.reset = () => {
|
||||
const res = origReset();
|
||||
hide();
|
||||
return res;
|
||||
};
|
||||
|
||||
hide();
|
||||
return true;
|
||||
};
|
||||
|
||||
(async () => {
|
||||
if (globalThis.clawdisA2UI) {
|
||||
installVisibilityHooks();
|
||||
return;
|
||||
}
|
||||
|
||||
let loaded = false;
|
||||
for (const src of candidates) {
|
||||
try {
|
||||
await loadScript(src);
|
||||
loaded = true;
|
||||
break;
|
||||
} catch {
|
||||
// try next
|
||||
}
|
||||
}
|
||||
if (!loaded) return;
|
||||
|
||||
// Wait for custom element upgrade + connectedCallback to publish globalThis.clawdisA2UI.
|
||||
for (let i = 0; i < 60; i += 1) {
|
||||
if (installVisibilityHooks()) return;
|
||||
await sleep(50);
|
||||
}
|
||||
})().catch(() => {});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -28,40 +28,4 @@ import Testing
|
||||
#expect(msg.contains("instance=ipad16_6 ctx={\"city\":\"Vienna\"}"))
|
||||
#expect(msg.hasSuffix(" default=update_canvas"))
|
||||
}
|
||||
|
||||
@Test func a2uiBundleSupportsAndroidBridgeFallback() throws {
|
||||
guard let url = ClawdisKitResources.bundle.url(forResource: "a2ui.bundle", withExtension: "js")
|
||||
else {
|
||||
throw NSError(domain: "Tests", code: 1, userInfo: [
|
||||
NSLocalizedDescriptionKey: "Missing resource a2ui.bundle.js",
|
||||
])
|
||||
}
|
||||
let js = try String(contentsOf: url, encoding: .utf8)
|
||||
#expect(js.contains("clawdisCanvasA2UIAction"))
|
||||
#expect(js.contains("globalThis.clawdisCanvasA2UIAction"))
|
||||
}
|
||||
|
||||
@Test func a2uiBundleWrapsDynamicCssValues() throws {
|
||||
guard let url = ClawdisKitResources.bundle.url(forResource: "a2ui.bundle", withExtension: "js")
|
||||
else {
|
||||
throw NSError(domain: "Tests", code: 1, userInfo: [
|
||||
NSLocalizedDescriptionKey: "Missing resource a2ui.bundle.js",
|
||||
])
|
||||
}
|
||||
let js = try String(contentsOf: url, encoding: .utf8)
|
||||
#expect(js.contains("blur(${r(statusBlur)})"))
|
||||
#expect(js.contains("box-shadow: ${r(statusShadow)}"))
|
||||
}
|
||||
|
||||
@Test func a2uiBundleStylesModalBackdrop() throws {
|
||||
guard let url = ClawdisKitResources.bundle.url(forResource: "a2ui.bundle", withExtension: "js")
|
||||
else {
|
||||
throw NSError(domain: "Tests", code: 1, userInfo: [
|
||||
NSLocalizedDescriptionKey: "Missing resource a2ui.bundle.js",
|
||||
])
|
||||
}
|
||||
let js = try String(contentsOf: url, encoding: .utf8)
|
||||
#expect(js.contains("::backdrop"))
|
||||
#expect(js.contains("backdrop-filter: blur(6px)"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,14 +8,17 @@ import { themeContext } from "@clawdis/a2ui-theme-context";
|
||||
|
||||
const modalStyles = css`
|
||||
dialog {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 24px;
|
||||
border: none;
|
||||
background: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
max-width: calc(100vw - 32px);
|
||||
max-height: calc(100vh - 32px);
|
||||
background: rgba(5, 8, 16, 0.65);
|
||||
backdrop-filter: blur(6px);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
dialog::backdrop {
|
||||
|
||||
@@ -5,7 +5,14 @@ import { defineConfig } from "rolldown";
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(here, "../../../../..");
|
||||
const fromHere = (p) => path.resolve(here, p);
|
||||
const outputFile = path.resolve(here, "../../Sources/ClawdisKit/Resources/CanvasA2UI/a2ui.bundle.js");
|
||||
const outputFile = path.resolve(
|
||||
here,
|
||||
"../../../../..",
|
||||
"src",
|
||||
"canvas-host",
|
||||
"a2ui",
|
||||
"a2ui.bundle.js",
|
||||
);
|
||||
|
||||
const a2uiLitDist = path.resolve(repoRoot, "vendor/a2ui/renderers/lit/dist/src");
|
||||
const a2uiThemeContext = path.resolve(a2uiLitDist, "0.8/ui/context/theme.js");
|
||||
|
||||
Reference in New Issue
Block a user