@adia-ai/web-modules 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +292 -0
- package/README.md +119 -0
- package/chat/chat-shell/chat-shell.a2ui.json +149 -0
- package/chat/chat-shell/chat-shell.css +10 -0
- package/chat/chat-shell/chat-shell.js +297 -0
- package/chat/chat-shell/chat-shell.yaml +119 -0
- package/chat/chat-shell/css/chat-shell.empty.css +12 -0
- package/chat/chat-shell/css/chat-shell.layout.css +60 -0
- package/chat/chat-shell/css/chat-shell.markdown.css +74 -0
- package/chat/chat-shell/css/chat-shell.messages.css +87 -0
- package/chat/chat-shell/css/chat-shell.streaming.css +30 -0
- package/chat/chat-shell/css/chat-shell.tokens.css +95 -0
- package/chat/index.js +1 -0
- package/editor/editor-shell/css/editor-shell.layout.css +171 -0
- package/editor/editor-shell/css/editor-shell.tokens.css +28 -0
- package/editor/editor-shell/editor-shell.a2ui.json +73 -0
- package/editor/editor-shell/editor-shell.css +6 -0
- package/editor/editor-shell/editor-shell.js +56 -0
- package/editor/editor-shell/editor-shell.yaml +59 -0
- package/editor/index.js +1 -0
- package/index.js +14 -0
- package/package.json +48 -0
- package/runtime/a2ui-root/a2ui-root.a2ui.json +125 -0
- package/runtime/a2ui-root/a2ui-root.js +191 -0
- package/runtime/a2ui-root/a2ui-root.yaml +87 -0
- package/runtime/gen-root/gen-root.a2ui.json +72 -0
- package/runtime/gen-root/gen-root.css +83 -0
- package/runtime/gen-root/gen-root.js +136 -0
- package/runtime/gen-root/gen-root.yaml +43 -0
- package/runtime/index.js +2 -0
- package/shell/admin-shell/admin-shell.a2ui.json +129 -0
- package/shell/admin-shell/admin-shell.css +14 -0
- package/shell/admin-shell/admin-shell.js +261 -0
- package/shell/admin-shell/admin-shell.yaml +89 -0
- package/shell/admin-shell/css/admin-shell.collapsed.css +86 -0
- package/shell/admin-shell/css/admin-shell.helpers.css +42 -0
- package/shell/admin-shell/css/admin-shell.main.css +182 -0
- package/shell/admin-shell/css/admin-shell.shell.css +48 -0
- package/shell/admin-shell/css/admin-shell.sidebar.css +165 -0
- package/shell/admin-shell/css/admin-shell.templates.css +215 -0
- package/shell/admin-shell/css/admin-shell.tokens.css +119 -0
- package/shell/index.js +1 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <a2ui-root> — A2UI protocol surface.
|
|
3
|
+
* Connects to a stream source and renders A2UI messages as AdiaUI components.
|
|
4
|
+
*
|
|
5
|
+
* <a2ui-root src="/api/agent" transport="sse"></a2ui-root>
|
|
6
|
+
* <a2ui-root src="ws://localhost:8080" transport="ws"></a2ui-root>
|
|
7
|
+
*
|
|
8
|
+
* Static / author-driven mode — set the `doc` property (array of A2UI messages)
|
|
9
|
+
* and the renderer resets + replays them. Editors and previews can drive the
|
|
10
|
+
* surface without opening a transport. Setting `doc` to a new array re-renders
|
|
11
|
+
* from scratch (reset() + processAll()).
|
|
12
|
+
*
|
|
13
|
+
* const root = document.querySelector('a2ui-root');
|
|
14
|
+
* root.doc = [
|
|
15
|
+
* { type: 'createSurface', surfaceId: 'root', root: 'c-1' },
|
|
16
|
+
* { type: 'updateComponents', components: [{ id: 'c-1', component: 'Heading', text: 'Hi' }] },
|
|
17
|
+
* ];
|
|
18
|
+
*
|
|
19
|
+
* Events:
|
|
20
|
+
* a2ui-connected — stream connected
|
|
21
|
+
* a2ui-message — each message received (detail: { message })
|
|
22
|
+
* a2ui-error — stream error (detail: { error })
|
|
23
|
+
* a2ui-closed — stream ended
|
|
24
|
+
* a2ui-action — user interaction (detail: { name, sourceComponentId, context })
|
|
25
|
+
* doc-replaced — fired after a full doc reset + replay (author-driven mode)
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { UIElement } from '../../../web-components/core/element.js';
|
|
29
|
+
import {
|
|
30
|
+
A2UIRenderer,
|
|
31
|
+
registry,
|
|
32
|
+
sseStream,
|
|
33
|
+
wsStream,
|
|
34
|
+
jsonlStream,
|
|
35
|
+
mcpStream,
|
|
36
|
+
} from '@adia-ai/a2ui-utils';
|
|
37
|
+
|
|
38
|
+
class A2UIRoot extends UIElement {
|
|
39
|
+
static properties = {
|
|
40
|
+
src: { type: String, default: '', reflect: true },
|
|
41
|
+
transport: { type: String, default: 'sse', reflect: true },
|
|
42
|
+
loading: { type: Boolean, default: false, reflect: true },
|
|
43
|
+
active: { type: Boolean, default: false, reflect: true },
|
|
44
|
+
batch: { type: Boolean, default: false, reflect: true },
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
static template = () => null;
|
|
48
|
+
|
|
49
|
+
#renderer = null;
|
|
50
|
+
#abortCtrl = null;
|
|
51
|
+
#doc = null;
|
|
52
|
+
#bound = false;
|
|
53
|
+
|
|
54
|
+
#onClick = (e) => {
|
|
55
|
+
const actionEl = e.target.closest('[data-action]');
|
|
56
|
+
if (!actionEl) return;
|
|
57
|
+
this.dispatchEvent(new CustomEvent('a2ui-action', {
|
|
58
|
+
bubbles: true,
|
|
59
|
+
detail: {
|
|
60
|
+
name: actionEl.getAttribute('data-action'),
|
|
61
|
+
sourceComponentId: actionEl.getAttribute('data-a2ui-id') || '',
|
|
62
|
+
timestamp: Date.now(),
|
|
63
|
+
context: {},
|
|
64
|
+
},
|
|
65
|
+
}));
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
connected() {
|
|
69
|
+
this.#renderer = new A2UIRenderer(this, registry, { batch: this.batch });
|
|
70
|
+
|
|
71
|
+
if (!this.#bound) {
|
|
72
|
+
this.#bound = true;
|
|
73
|
+
this.addEventListener('click', this.#onClick);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
render() {
|
|
78
|
+
if (this.src && !this.active) this.#connect();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async #connect() {
|
|
82
|
+
this.#abortCtrl?.abort();
|
|
83
|
+
this.#abortCtrl = new AbortController();
|
|
84
|
+
const signal = this.#abortCtrl.signal;
|
|
85
|
+
|
|
86
|
+
this.loading = true;
|
|
87
|
+
this.active = false;
|
|
88
|
+
|
|
89
|
+
let stream;
|
|
90
|
+
try {
|
|
91
|
+
switch (this.transport) {
|
|
92
|
+
case 'sse':
|
|
93
|
+
stream = sseStream(this.src, { signal, catalog: registry });
|
|
94
|
+
break;
|
|
95
|
+
case 'ws':
|
|
96
|
+
case 'websocket':
|
|
97
|
+
stream = wsStream(this.src, { signal, catalog: registry });
|
|
98
|
+
break;
|
|
99
|
+
case 'jsonl':
|
|
100
|
+
stream = jsonlStream(this.src, { signal, catalog: registry });
|
|
101
|
+
break;
|
|
102
|
+
case 'mcp':
|
|
103
|
+
stream = mcpStream(this.src, {
|
|
104
|
+
signal,
|
|
105
|
+
catalog: registry,
|
|
106
|
+
onAction: (name, params) => {
|
|
107
|
+
this.dispatchEvent(new CustomEvent('a2ui-action', {
|
|
108
|
+
bubbles: true,
|
|
109
|
+
detail: { name, params, timestamp: Date.now() },
|
|
110
|
+
}));
|
|
111
|
+
return Promise.resolve({ status: 'dispatched' });
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
break;
|
|
115
|
+
default:
|
|
116
|
+
console.warn(`a2ui-root: unknown transport "${this.transport}"`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.active = true;
|
|
121
|
+
this.loading = false;
|
|
122
|
+
this.dispatchEvent(new Event('a2ui-connected', { bubbles: true }));
|
|
123
|
+
|
|
124
|
+
for await (const message of stream) {
|
|
125
|
+
if (signal.aborted) break;
|
|
126
|
+
this.#renderer.process(message);
|
|
127
|
+
this.dispatchEvent(new CustomEvent('a2ui-message', {
|
|
128
|
+
bubbles: true,
|
|
129
|
+
detail: { message },
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
if (!signal.aborted) {
|
|
134
|
+
this.dispatchEvent(new CustomEvent('a2ui-error', {
|
|
135
|
+
bubbles: true,
|
|
136
|
+
detail: { error: err },
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this.active = false;
|
|
142
|
+
this.loading = false;
|
|
143
|
+
this.dispatchEvent(new Event('a2ui-closed', { bubbles: true }));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
process(message) {
|
|
147
|
+
if (!this.#renderer) {
|
|
148
|
+
this.#renderer = new A2UIRenderer(this, registry);
|
|
149
|
+
}
|
|
150
|
+
this.#renderer.process(message);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
processAll(messages) {
|
|
154
|
+
for (const msg of messages) this.process(msg);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
get doc() { return this.#doc; }
|
|
158
|
+
set doc(messages) {
|
|
159
|
+
this.#doc = Array.isArray(messages) ? messages : [];
|
|
160
|
+
if (!this.#renderer) {
|
|
161
|
+
this.#renderer = new A2UIRenderer(this, registry);
|
|
162
|
+
}
|
|
163
|
+
this.#renderer.reset();
|
|
164
|
+
for (const msg of this.#doc) this.#renderer.process(msg);
|
|
165
|
+
this.dispatchEvent(new CustomEvent('doc-replaced', {
|
|
166
|
+
bubbles: true,
|
|
167
|
+
detail: { count: this.#doc.length },
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
reset() {
|
|
172
|
+
this.#renderer?.reset();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
disconnect() {
|
|
176
|
+
this.#abortCtrl?.abort();
|
|
177
|
+
this.active = false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
disconnected() {
|
|
181
|
+
this.#abortCtrl?.abort();
|
|
182
|
+
this.removeEventListener('click', this.#onClick);
|
|
183
|
+
this.#bound = false;
|
|
184
|
+
this.active = false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
get renderer() { return this.#renderer; }
|
|
188
|
+
}
|
|
189
|
+
customElements.define('a2ui-root', A2UIRoot);
|
|
190
|
+
|
|
191
|
+
export { A2UIRoot };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Edit this file; run `npm run build:components` to regenerate a2ui.json.
|
|
2
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
3
|
+
name: A2UIRoot
|
|
4
|
+
tag: a2ui-root
|
|
5
|
+
component: A2UIRoot
|
|
6
|
+
category: container
|
|
7
|
+
version: 1
|
|
8
|
+
description: A2UI protocol surface. Connects to a stream source (SSE, WebSocket, JSONL, MCP) and renders A2UI messages as AdiaUI components via the `@adia-ai/a2ui-utils` renderer.
|
|
9
|
+
props:
|
|
10
|
+
src:
|
|
11
|
+
description: Stream source URL (endpoint for SSE/WebSocket, file path for JSONL, tool-call target for MCP).
|
|
12
|
+
type: string
|
|
13
|
+
default: ""
|
|
14
|
+
transport:
|
|
15
|
+
description: Stream transport to use.
|
|
16
|
+
type: string
|
|
17
|
+
default: sse
|
|
18
|
+
enum:
|
|
19
|
+
- sse
|
|
20
|
+
- ws
|
|
21
|
+
- websocket
|
|
22
|
+
- jsonl
|
|
23
|
+
- mcp
|
|
24
|
+
loading:
|
|
25
|
+
description: True while the stream is connecting.
|
|
26
|
+
type: boolean
|
|
27
|
+
default: false
|
|
28
|
+
reflect: true
|
|
29
|
+
active:
|
|
30
|
+
description: True while the stream is connected and receiving messages.
|
|
31
|
+
type: boolean
|
|
32
|
+
default: false
|
|
33
|
+
reflect: true
|
|
34
|
+
batch:
|
|
35
|
+
description: Batch renderer updates via requestAnimationFrame for large fan-in.
|
|
36
|
+
type: boolean
|
|
37
|
+
default: false
|
|
38
|
+
doc:
|
|
39
|
+
description: >-
|
|
40
|
+
Author-driven mode — set to an array of A2UI messages and the renderer
|
|
41
|
+
resets + replays them. No network/transport involvement. Setting to a
|
|
42
|
+
new array triggers a full re-render. Use this for editors, previews,
|
|
43
|
+
tests, and any static-doc authoring loop. When both `src` and `doc` are
|
|
44
|
+
set, `doc` wins (the stream is not opened). Pass as a JS property; not
|
|
45
|
+
reflected to an attribute.
|
|
46
|
+
type: array
|
|
47
|
+
events:
|
|
48
|
+
a2ui-connected:
|
|
49
|
+
description: Fired when the stream is established.
|
|
50
|
+
a2ui-message:
|
|
51
|
+
description: "Fired for each A2UI message received. detail: { message }"
|
|
52
|
+
a2ui-action:
|
|
53
|
+
description: "Fired when a child element with [data-action] is clicked. detail: { name, sourceComponentId, context }"
|
|
54
|
+
a2ui-error:
|
|
55
|
+
description: "Fired when the stream errors. detail: { error }"
|
|
56
|
+
a2ui-closed:
|
|
57
|
+
description: Fired when the stream ends.
|
|
58
|
+
doc-replaced:
|
|
59
|
+
description: "Fired after a full doc reset + replay in author-driven mode. detail: { count }"
|
|
60
|
+
slots:
|
|
61
|
+
default:
|
|
62
|
+
description: The rendered surface. Children are stamped by the A2UI renderer.
|
|
63
|
+
states:
|
|
64
|
+
- name: idle
|
|
65
|
+
description: Default, ready to connect.
|
|
66
|
+
- name: loading
|
|
67
|
+
description: Stream is connecting.
|
|
68
|
+
- name: active
|
|
69
|
+
description: Stream is connected and receiving messages.
|
|
70
|
+
traits: []
|
|
71
|
+
tokens: {}
|
|
72
|
+
a2ui:
|
|
73
|
+
rules: []
|
|
74
|
+
anti_patterns: []
|
|
75
|
+
keywords:
|
|
76
|
+
- a2ui
|
|
77
|
+
- protocol
|
|
78
|
+
- renderer
|
|
79
|
+
- stream
|
|
80
|
+
- surface
|
|
81
|
+
- sse
|
|
82
|
+
- websocket
|
|
83
|
+
- jsonl
|
|
84
|
+
- mcp
|
|
85
|
+
related:
|
|
86
|
+
- canvas
|
|
87
|
+
- inspector
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/GenRoot.json",
|
|
4
|
+
"title": "GenRoot",
|
|
5
|
+
"description": "Composition shell for chat + canvas generative UI. Manages layout modes\n(chat-only / split / canvas-only) and delegates a unified API across\nchild chat-ui, canvas-ui, and inspector-ui elements.\n",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"allOf": [
|
|
8
|
+
{
|
|
9
|
+
"$ref": "common_types.json#/$defs/ComponentCommon"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"$ref": "common_types.json#/$defs/CatalogComponentCommon"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"component": {
|
|
17
|
+
"const": "GenRoot"
|
|
18
|
+
},
|
|
19
|
+
"inspector": {
|
|
20
|
+
"description": "Show the inspector pane (for debugging generated A2UI).",
|
|
21
|
+
"type": "boolean",
|
|
22
|
+
"default": false
|
|
23
|
+
},
|
|
24
|
+
"mode": {
|
|
25
|
+
"description": "Layout mode — chat-only, 50/50 split, or canvas-only.",
|
|
26
|
+
"type": "string",
|
|
27
|
+
"enum": [
|
|
28
|
+
"chat",
|
|
29
|
+
"split",
|
|
30
|
+
"canvas"
|
|
31
|
+
],
|
|
32
|
+
"default": "chat"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"required": [
|
|
36
|
+
"component"
|
|
37
|
+
],
|
|
38
|
+
"unevaluatedProperties": false,
|
|
39
|
+
"x-adiaui": {
|
|
40
|
+
"anti_patterns": [],
|
|
41
|
+
"category": "layout",
|
|
42
|
+
"events": {},
|
|
43
|
+
"examples": [],
|
|
44
|
+
"keywords": [
|
|
45
|
+
"gen-ui",
|
|
46
|
+
"generative-ui",
|
|
47
|
+
"chat-canvas",
|
|
48
|
+
"agent-shell"
|
|
49
|
+
],
|
|
50
|
+
"name": "GenRoot",
|
|
51
|
+
"related": [
|
|
52
|
+
"AppShell",
|
|
53
|
+
"Chat"
|
|
54
|
+
],
|
|
55
|
+
"slots": {
|
|
56
|
+
"default": {
|
|
57
|
+
"description": "Author supplies chat-ui, canvas-ui, and optional inspector-ui children."
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"states": [
|
|
61
|
+
{
|
|
62
|
+
"description": "Default mode=chat.",
|
|
63
|
+
"name": "idle"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"synonyms": {},
|
|
67
|
+
"tag": "gen-root",
|
|
68
|
+
"tokens": {},
|
|
69
|
+
"traits": [],
|
|
70
|
+
"version": 1
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
@scope (gen-root) {
|
|
2
|
+
|
|
3
|
+
/* ═══════════════════════════════════════════════════════
|
|
4
|
+
SHELL HOST — full bleed, no card chrome
|
|
5
|
+
═══════════════════════════════════════════════════════ */
|
|
6
|
+
|
|
7
|
+
:scope {
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
display: grid;
|
|
10
|
+
height: 100%;
|
|
11
|
+
min-height: 0;
|
|
12
|
+
background: var(--chat-bg, var(--a-canvas-0));
|
|
13
|
+
font-family: var(--a-font-family);
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
|
|
16
|
+
/* Default: chat mode — single column */
|
|
17
|
+
grid-template-columns: 1fr;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* ═══════════════════════════════════════════════════════
|
|
21
|
+
MODE: CHAT (initial screen — thread only, full width)
|
|
22
|
+
═══════════════════════════════════════════════════════ */
|
|
23
|
+
|
|
24
|
+
:scope[mode="chat"] {
|
|
25
|
+
grid-template-columns: 1fr;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
:scope[mode="chat"] > canvas-ui {
|
|
29
|
+
display: none;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* ═══════════════════════════════════════════════════════
|
|
33
|
+
MODE: SPLIT (dialog left + canvas right)
|
|
34
|
+
═══════════════════════════════════════════════════════ */
|
|
35
|
+
|
|
36
|
+
:scope[mode="split"] {
|
|
37
|
+
grid-template-columns: minmax(320px, 1fr) 1.5fr;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
:scope[mode="split"] > chat-ui {
|
|
41
|
+
border-right: var(--a-border-thin) solid var(--a-canvas-border-subtle);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* ═══════════════════════════════════════════════════════
|
|
45
|
+
MODE: CANVAS (canvas only)
|
|
46
|
+
═══════════════════════════════════════════════════════ */
|
|
47
|
+
|
|
48
|
+
:scope[mode="canvas"] {
|
|
49
|
+
grid-template-columns: 1fr;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
:scope[mode="canvas"] > chat-ui {
|
|
53
|
+
display: none;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ═══════════════════════════════════════════════════════
|
|
57
|
+
INSPECTOR
|
|
58
|
+
═══════════════════════════════════════════════════════ */
|
|
59
|
+
|
|
60
|
+
:scope > inspector-ui {
|
|
61
|
+
display: none;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
:scope[inspector] > inspector-ui {
|
|
65
|
+
display: flex;
|
|
66
|
+
border-left: var(--a-border-thin) solid var(--a-canvas-border-subtle);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
:scope[mode="split"][inspector] {
|
|
70
|
+
grid-template-columns: minmax(320px, 1fr) 1.5fr 1fr;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* ═══════════════════════════════════════════════════════
|
|
74
|
+
CHILDREN — fill grid cells
|
|
75
|
+
═══════════════════════════════════════════════════════ */
|
|
76
|
+
|
|
77
|
+
:scope > chat-ui,
|
|
78
|
+
:scope > canvas-ui,
|
|
79
|
+
:scope > inspector-ui {
|
|
80
|
+
min-height: 0;
|
|
81
|
+
min-width: 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { UIElement } from '../../../web-components/core/element.js';
|
|
2
|
+
import '../../../web-components/components/chat-thread/chat-thread.js';
|
|
3
|
+
import '../../../web-components/components/canvas/canvas.js';
|
|
4
|
+
import '../../../web-components/components/inspector/inspector.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* <gen-root> — Composition shell for chat + canvas generative UI.
|
|
8
|
+
*
|
|
9
|
+
* Modes:
|
|
10
|
+
* chat — thread only, full width
|
|
11
|
+
* split — thread left + canvas right (50/50)
|
|
12
|
+
* canvas — canvas only, thread hidden
|
|
13
|
+
*
|
|
14
|
+
* Consumer-owns-LLM: forwards all events from children, never calls fetch.
|
|
15
|
+
*
|
|
16
|
+
* Events (forwarded):
|
|
17
|
+
* submit — from thread (chat input)
|
|
18
|
+
* suggestion-select — from thread
|
|
19
|
+
* canvas-interaction — from canvas
|
|
20
|
+
*/
|
|
21
|
+
export class GenRoot extends UIElement {
|
|
22
|
+
static properties = {
|
|
23
|
+
mode: { type: String, default: 'chat', reflect: true },
|
|
24
|
+
inspector: { type: Boolean, default: false, reflect: true },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
static template = () => null;
|
|
28
|
+
|
|
29
|
+
#bound = false;
|
|
30
|
+
#threadEl = null;
|
|
31
|
+
#canvasEl = null;
|
|
32
|
+
#inspectorEl = null;
|
|
33
|
+
|
|
34
|
+
connected() {
|
|
35
|
+
// Forward events from children — they bubble, so just re-dispatch isn't needed.
|
|
36
|
+
// Events already bubble from thread/canvas through this host.
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
render() {
|
|
40
|
+
if (this.#bound) return;
|
|
41
|
+
this.#bound = true;
|
|
42
|
+
|
|
43
|
+
this.#threadEl = document.createElement('chat-thread-ui');
|
|
44
|
+
this.#canvasEl = document.createElement('canvas-ui');
|
|
45
|
+
|
|
46
|
+
this.appendChild(this.#threadEl);
|
|
47
|
+
this.appendChild(this.#canvasEl);
|
|
48
|
+
|
|
49
|
+
if (this.inspector) {
|
|
50
|
+
this.#inspectorEl = document.createElement('inspector-ui');
|
|
51
|
+
this.appendChild(this.#inspectorEl);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── Public API ──
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Append a chat message. Delegates to thread.
|
|
59
|
+
* @param {{ role: string, content: string, avatar?: string }} msg
|
|
60
|
+
*/
|
|
61
|
+
appendMessage(msg) {
|
|
62
|
+
this.#threadEl?.appendMessage(msg);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Append chain-of-thought. Delegates to thread.
|
|
67
|
+
* @param {{ text: string }} msg
|
|
68
|
+
*/
|
|
69
|
+
appendCoT(msg) {
|
|
70
|
+
this.#threadEl?.appendCoT(msg);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Push an artifact to the canvas. Auto-switches to split mode if in chat mode.
|
|
75
|
+
* @param {object[]} messages — A2UI messages
|
|
76
|
+
*/
|
|
77
|
+
pushArtifact(messages) {
|
|
78
|
+
if (this.mode === 'chat') {
|
|
79
|
+
this.mode = 'split';
|
|
80
|
+
}
|
|
81
|
+
this.#canvasEl?.processAll(messages);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Set suggestion buttons. Delegates to thread.
|
|
86
|
+
* @param {{ label: string, prompt: string }[]} items
|
|
87
|
+
*/
|
|
88
|
+
setSuggestions(items) {
|
|
89
|
+
this.#threadEl?.setSuggestions(items);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Append a result message with action buttons. Delegates to thread. */
|
|
93
|
+
appendResultMessage(opts) { this.#threadEl?.appendResultMessage(opts); }
|
|
94
|
+
|
|
95
|
+
/** Show save-pattern bar. Delegates to thread. */
|
|
96
|
+
showSavePatternBar(opts) { this.#threadEl?.showSavePatternBar(opts); }
|
|
97
|
+
|
|
98
|
+
/** Show feedback widget. Delegates to thread. */
|
|
99
|
+
showFeedbackWidget(opts) { this.#threadEl?.showFeedbackWidget(opts); }
|
|
100
|
+
|
|
101
|
+
/** Show typing indicator. Delegates to thread. */
|
|
102
|
+
showTyping() { this.#threadEl?.showTyping(); }
|
|
103
|
+
|
|
104
|
+
/** Hide typing indicator. Delegates to thread. */
|
|
105
|
+
hideTyping() { this.#threadEl?.hideTyping(); }
|
|
106
|
+
|
|
107
|
+
/** Append an error message. Delegates to thread. */
|
|
108
|
+
appendError(content) { this.#threadEl?.appendError(content); }
|
|
109
|
+
|
|
110
|
+
/** Start a pipeline status indicator. Delegates to thread. */
|
|
111
|
+
startPipelineStatus() { return this.#threadEl?.startPipelineStatus(); }
|
|
112
|
+
|
|
113
|
+
/** Update the active pipeline status. Delegates to thread. */
|
|
114
|
+
updatePipelineStatus(opts) { this.#threadEl?.updatePipelineStatus(opts); }
|
|
115
|
+
|
|
116
|
+
/** Mark the active pipeline status as complete. Delegates to thread. */
|
|
117
|
+
completePipelineStatus() { this.#threadEl?.completePipelineStatus(); }
|
|
118
|
+
|
|
119
|
+
/** Reset both thread and canvas. */
|
|
120
|
+
reset() {
|
|
121
|
+
this.#threadEl?.clear();
|
|
122
|
+
this.#canvasEl?.reset();
|
|
123
|
+
this.mode = 'chat';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Access the thread element. */
|
|
127
|
+
get thread() { return this.#threadEl; }
|
|
128
|
+
|
|
129
|
+
/** Access the canvas element. */
|
|
130
|
+
get canvas() { return this.#canvasEl; }
|
|
131
|
+
|
|
132
|
+
/** Access the inspector element (null if inspector not enabled). */
|
|
133
|
+
get inspect() { return this.#inspectorEl; }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
customElements.define('gen-root', GenRoot);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
2
|
+
name: GenRoot
|
|
3
|
+
tag: gen-root
|
|
4
|
+
component: GenRoot
|
|
5
|
+
category: layout
|
|
6
|
+
version: 1
|
|
7
|
+
description: |
|
|
8
|
+
Composition shell for chat + canvas generative UI. Manages layout modes
|
|
9
|
+
(chat-only / split / canvas-only) and delegates a unified API across
|
|
10
|
+
child chat-ui, canvas-ui, and inspector-ui elements.
|
|
11
|
+
|
|
12
|
+
props:
|
|
13
|
+
mode:
|
|
14
|
+
type: string
|
|
15
|
+
default: chat
|
|
16
|
+
enum: [chat, split, canvas]
|
|
17
|
+
reflect: true
|
|
18
|
+
description: Layout mode — chat-only, 50/50 split, or canvas-only.
|
|
19
|
+
|
|
20
|
+
inspector:
|
|
21
|
+
type: boolean
|
|
22
|
+
default: false
|
|
23
|
+
reflect: true
|
|
24
|
+
description: Show the inspector pane (for debugging generated A2UI).
|
|
25
|
+
|
|
26
|
+
events: {}
|
|
27
|
+
|
|
28
|
+
slots:
|
|
29
|
+
default:
|
|
30
|
+
description: Author supplies chat-ui, canvas-ui, and optional inspector-ui children.
|
|
31
|
+
|
|
32
|
+
states:
|
|
33
|
+
- name: idle
|
|
34
|
+
description: Default mode=chat.
|
|
35
|
+
|
|
36
|
+
traits: []
|
|
37
|
+
|
|
38
|
+
a2ui:
|
|
39
|
+
rules:
|
|
40
|
+
- gen-root is an integration shell. Prefer admin-shell for admin UIs; use gen-root only for chat+canvas tooling.
|
|
41
|
+
|
|
42
|
+
keywords: [gen-ui, generative-ui, chat-canvas, agent-shell]
|
|
43
|
+
related: [AppShell, Chat]
|
package/runtime/index.js
ADDED