Files
clawdbot/apps/macos/Sources/Clawdis/Resources/CanvasA2UI/bootstrap.js
2025-12-17 11:35:06 +01:00

198 lines
5.4 KiB
JavaScript

import { html, css, LitElement } from "lit";
import { repeat } from "lit/directives/repeat.js";
import { ContextProvider } from "@lit/context";
import { v0_8 } from "@a2ui/lit";
import "@a2ui/lit/ui";
import { themeContext } from "@clawdis/a2ui-theme-context";
const empty = Object.freeze({});
const emptyClasses = () => ({});
const textHintStyles = () => ({ h1: {}, h2: {}, h3: {}, h4: {}, h5: {}, body: {}, caption: {} });
const clawdisTheme = {
components: {
AudioPlayer: emptyClasses(),
Button: emptyClasses(),
Card: emptyClasses(),
Column: emptyClasses(),
CheckBox: { container: emptyClasses(), element: emptyClasses(), label: emptyClasses() },
DateTimeInput: { container: emptyClasses(), element: emptyClasses(), label: emptyClasses() },
Divider: emptyClasses(),
Image: {
all: emptyClasses(),
icon: emptyClasses(),
avatar: emptyClasses(),
smallFeature: emptyClasses(),
mediumFeature: emptyClasses(),
largeFeature: emptyClasses(),
header: emptyClasses(),
},
Icon: emptyClasses(),
List: emptyClasses(),
Modal: { backdrop: emptyClasses(), element: emptyClasses() },
MultipleChoice: { container: emptyClasses(), element: emptyClasses(), label: emptyClasses() },
Row: emptyClasses(),
Slider: { container: emptyClasses(), element: emptyClasses(), label: emptyClasses() },
Tabs: { container: emptyClasses(), element: emptyClasses(), controls: { all: emptyClasses(), selected: emptyClasses() } },
Text: {
all: emptyClasses(),
h1: emptyClasses(),
h2: emptyClasses(),
h3: emptyClasses(),
h4: emptyClasses(),
h5: emptyClasses(),
caption: emptyClasses(),
body: emptyClasses(),
},
TextField: { container: emptyClasses(), element: emptyClasses(), label: emptyClasses() },
Video: emptyClasses(),
},
elements: {
a: emptyClasses(),
audio: emptyClasses(),
body: emptyClasses(),
button: emptyClasses(),
h1: emptyClasses(),
h2: emptyClasses(),
h3: emptyClasses(),
h4: emptyClasses(),
h5: emptyClasses(),
iframe: emptyClasses(),
input: emptyClasses(),
p: emptyClasses(),
pre: emptyClasses(),
textarea: emptyClasses(),
video: emptyClasses(),
},
markdown: {
p: [],
h1: [],
h2: [],
h3: [],
h4: [],
h5: [],
ul: [],
ol: [],
li: [],
a: [],
strong: [],
em: [],
},
additionalStyles: {
Card: {
background: "linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.03))",
border: "1px solid rgba(255,255,255,.09)",
borderRadius: "14px",
padding: "14px",
boxShadow: "0 10px 30px rgba(0,0,0,.35)",
},
Column: { gap: "10px" },
Row: { gap: "10px", alignItems: "center" },
Divider: { opacity: "0.25" },
Button: {
background: "linear-gradient(135deg, #22c55e 0%, #06b6d4 100%)",
border: "0",
borderRadius: "12px",
padding: "10px 14px",
color: "#071016",
fontWeight: "650",
cursor: "pointer",
boxShadow: "0 10px 25px rgba(6, 182, 212, 0.18)",
},
Text: {
...textHintStyles(),
h1: { fontSize: "20px", fontWeight: "750", margin: "0 0 6px 0" },
h2: { fontSize: "16px", fontWeight: "700", margin: "0 0 6px 0" },
body: { fontSize: "13px", lineHeight: "1.4" },
caption: { opacity: "0.8" },
},
TextField: { display: "grid", gap: "6px" },
Image: { borderRadius: "12px" },
},
};
class ClawdisA2UIHost extends LitElement {
static properties = {
surfaces: { state: true },
};
#processor = v0_8.Data.createSignalA2uiMessageProcessor();
#themeProvider = new ContextProvider(this, {
context: themeContext,
initialValue: clawdisTheme,
});
surfaces = [];
static styles = css`
:host {
display: block;
height: 100%;
box-sizing: border-box;
padding: 12px;
}
#surfaces {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
height: 100%;
overflow: auto;
padding-bottom: 24px;
}
`;
connectedCallback() {
super.connectedCallback();
globalThis.clawdisA2UI = {
applyMessages: (messages) => this.applyMessages(messages),
reset: () => this.reset(),
getSurfaces: () => Array.from(this.#processor.getSurfaces().keys()),
};
this.#syncSurfaces();
}
applyMessages(messages) {
if (!Array.isArray(messages)) {
throw new Error("A2UI: expected messages array");
}
this.#processor.processMessages(messages);
this.#syncSurfaces();
return { ok: true, surfaces: this.surfaces.map(([id]) => id) };
}
reset() {
this.#processor.clearSurfaces();
this.#syncSurfaces();
return { ok: true };
}
#syncSurfaces() {
this.surfaces = Array.from(this.#processor.getSurfaces().entries());
}
render() {
if (this.surfaces.length === 0) {
return html`<div style="opacity:.8; padding: 10px;">
<div style="font-weight: 700; margin-bottom: 6px;">Canvas (A2UI)</div>
<div>Waiting for A2UI messages…</div>
</div>`;
}
return html`<section id="surfaces">
${repeat(
this.surfaces,
([surfaceId]) => surfaceId,
([surfaceId, surface]) => html`<a2ui-surface
.surfaceId=${surfaceId}
.surface=${surface}
.processor=${this.#processor}
></a2ui-surface>`
)}
</section>`;
}
}
customElements.define("clawdis-a2ui-host", ClawdisA2UIHost);