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(); this.requestUpdate(); return { ok: true, surfaces: this.surfaces.map(([id]) => id) }; } reset() { this.#processor.clearSurfaces(); this.#syncSurfaces(); this.requestUpdate(); return { ok: true }; } #syncSurfaces() { this.surfaces = Array.from(this.#processor.getSurfaces().entries()); } render() { if (this.surfaces.length === 0) { return html`