cortena-ui 1.3.0 → 1.4.0
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/README.md +74 -3
- package/dist/a2ui/catalogue-doc.d.ts +44 -0
- package/dist/a2ui/catalogue-doc.js +169 -0
- package/dist/a2ui/catalogue-doc.js.map +1 -0
- package/dist/a2ui/catalogue.d.ts +44 -0
- package/dist/a2ui/catalogue.js +942 -0
- package/dist/a2ui/catalogue.js.map +1 -0
- package/dist/a2ui/index.d.ts +7 -0
- package/dist/a2ui/message.d.ts +232 -0
- package/dist/a2ui/message.js +365 -0
- package/dist/a2ui/message.js.map +1 -0
- package/dist/a2ui/renderer.d.ts +46 -0
- package/dist/a2ui/renderer.js +252 -0
- package/dist/a2ui/renderer.js.map +1 -0
- package/dist/a2ui/views.d.ts +80 -0
- package/dist/a2ui/views.js +596 -0
- package/dist/a2ui/views.js.map +1 -0
- package/dist/a2ui.d.ts +8 -0
- package/dist/a2ui.js +7 -0
- package/dist/chart.d.ts +9 -0
- package/dist/chart.js +9 -0
- package/dist/components/accordion.d.ts +22 -0
- package/dist/components/accordion.js +63 -0
- package/dist/components/accordion.js.map +1 -0
- package/dist/components/alert.d.ts +15 -0
- package/dist/components/alert.js +64 -0
- package/dist/components/alert.js.map +1 -0
- package/dist/components/avatar.d.ts +18 -0
- package/dist/components/avatar.js +40 -0
- package/dist/components/avatar.js.map +1 -0
- package/dist/components/badge.d.ts +12 -0
- package/dist/components/badge.js +47 -0
- package/dist/components/badge.js.map +1 -0
- package/dist/components/breadcrumb.d.ts +71 -0
- package/dist/components/breadcrumb.js +123 -0
- package/dist/components/breadcrumb.js.map +1 -0
- package/dist/components/button-link.d.ts +28 -0
- package/dist/components/button-link.js +24 -0
- package/dist/components/button-link.js.map +1 -0
- package/dist/components/button.d.ts +24 -0
- package/dist/components/button.js +65 -0
- package/dist/components/button.js.map +1 -0
- package/dist/components/calendar.d.ts +28 -0
- package/dist/components/calendar.js +123 -0
- package/dist/components/calendar.js.map +1 -0
- package/dist/components/card.d.ts +32 -0
- package/dist/components/card.js +57 -0
- package/dist/components/card.js.map +1 -0
- package/dist/components/chart/chart.d.ts +71 -0
- package/dist/components/chart/chart.js +167 -0
- package/dist/components/chart/chart.js.map +1 -0
- package/dist/components/chart/container.d.ts +88 -0
- package/dist/components/chart/container.js +189 -0
- package/dist/components/chart/container.js.map +1 -0
- package/dist/components/chart/data.js +128 -0
- package/dist/components/chart/data.js.map +1 -0
- package/dist/components/chart/index.d.ts +9 -0
- package/dist/components/chart/index.js +10 -0
- package/dist/components/chart/nivo-charts.js +225 -0
- package/dist/components/chart/nivo-charts.js.map +1 -0
- package/dist/components/chart/nivo-lazy.js +50 -0
- package/dist/components/chart/nivo-lazy.js.map +1 -0
- package/dist/components/chart/nivo-theme.d.ts +156 -0
- package/dist/components/chart/nivo-theme.js +161 -0
- package/dist/components/chart/nivo-theme.js.map +1 -0
- package/dist/components/chart/recharts-charts.js +276 -0
- package/dist/components/chart/recharts-charts.js.map +1 -0
- package/dist/components/chart/recharts-lazy.js +58 -0
- package/dist/components/chart/recharts-lazy.js.map +1 -0
- package/dist/components/chart/recharts-parts.d.ts +10 -0
- package/dist/components/chart/recharts-parts.js +25 -0
- package/dist/components/chart/recharts-parts.js.map +1 -0
- package/dist/components/chart/tokens.d.ts +62 -0
- package/dist/components/chart/tokens.js +192 -0
- package/dist/components/chart/tokens.js.map +1 -0
- package/dist/components/chart/types.d.ts +114 -0
- package/dist/components/chart/types.js +27 -0
- package/dist/components/chart/types.js.map +1 -0
- package/dist/components/checkbox.d.ts +20 -0
- package/dist/components/checkbox.js +24 -0
- package/dist/components/checkbox.js.map +1 -0
- package/dist/components/chip.d.ts +17 -0
- package/dist/components/chip.js +47 -0
- package/dist/components/chip.js.map +1 -0
- package/dist/components/combobox.d.ts +76 -0
- package/dist/components/combobox.js +201 -0
- package/dist/components/combobox.js.map +1 -0
- package/dist/components/command.d.ts +50 -0
- package/dist/components/command.js +117 -0
- package/dist/components/command.js.map +1 -0
- package/dist/components/data-table/data-table.d.ts +65 -0
- package/dist/components/data-table/data-table.js +588 -0
- package/dist/components/data-table/data-table.js.map +1 -0
- package/dist/components/data-table/export.d.ts +23 -0
- package/dist/components/data-table/export.js +95 -0
- package/dist/components/data-table/export.js.map +1 -0
- package/dist/components/data-table/features.d.ts +66 -0
- package/dist/components/data-table/features.js +103 -0
- package/dist/components/data-table/features.js.map +1 -0
- package/dist/components/data-table/index.d.ts +8 -0
- package/dist/components/data-table/parts.d.ts +79 -0
- package/dist/components/data-table/parts.js +400 -0
- package/dist/components/data-table/parts.js.map +1 -0
- package/dist/components/data-table/system-columns.js +84 -0
- package/dist/components/data-table/system-columns.js.map +1 -0
- package/dist/components/data-table/types.d.ts +172 -0
- package/dist/components/data-table/use-data-table.d.ts +19 -0
- package/dist/components/data-table/use-data-table.js +171 -0
- package/dist/components/data-table/use-data-table.js.map +1 -0
- package/dist/components/data-table/use-server-source.js +200 -0
- package/dist/components/data-table/use-server-source.js.map +1 -0
- package/dist/components/date-field.d.ts +54 -0
- package/dist/components/date-field.js +235 -0
- package/dist/components/date-field.js.map +1 -0
- package/dist/components/date-picker.d.ts +42 -0
- package/dist/components/date-picker.js +125 -0
- package/dist/components/date-picker.js.map +1 -0
- package/dist/components/dialog.d.ts +36 -0
- package/dist/components/dialog.js +97 -0
- package/dist/components/dialog.js.map +1 -0
- package/dist/components/dropdown-menu.d.ts +54 -0
- package/dist/components/dropdown-menu.js +191 -0
- package/dist/components/dropdown-menu.js.map +1 -0
- package/dist/components/dropzone.d.ts +48 -0
- package/dist/components/dropzone.js +185 -0
- package/dist/components/dropzone.js.map +1 -0
- package/dist/components/empty-state.d.ts +22 -0
- package/dist/components/empty-state.js +46 -0
- package/dist/components/empty-state.js.map +1 -0
- package/dist/components/error-banner.d.ts +17 -0
- package/dist/components/error-banner.js +52 -0
- package/dist/components/error-banner.js.map +1 -0
- package/dist/components/field.d.ts +64 -0
- package/dist/components/field.js +133 -0
- package/dist/components/field.js.map +1 -0
- package/dist/components/form.d.ts +37 -0
- package/dist/components/form.js +103 -0
- package/dist/components/form.js.map +1 -0
- package/dist/components/input.d.ts +17 -0
- package/dist/components/input.js +26 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/kbd.d.ts +11 -0
- package/dist/components/kbd.js +20 -0
- package/dist/components/kbd.js.map +1 -0
- package/dist/components/label.d.ts +14 -0
- package/dist/components/label.js +28 -0
- package/dist/components/label.js.map +1 -0
- package/dist/components/loading-skeleton.d.ts +22 -0
- package/dist/components/loading-skeleton.js +123 -0
- package/dist/components/loading-skeleton.js.map +1 -0
- package/dist/components/markdown.d.ts +44 -0
- package/dist/components/markdown.js +231 -0
- package/dist/components/markdown.js.map +1 -0
- package/dist/components/page-header.d.ts +26 -0
- package/dist/components/page-header.js +53 -0
- package/dist/components/page-header.js.map +1 -0
- package/dist/components/popover.d.ts +29 -0
- package/dist/components/popover.js +74 -0
- package/dist/components/popover.js.map +1 -0
- package/dist/components/progress.d.ts +22 -0
- package/dist/components/progress.js +49 -0
- package/dist/components/progress.js.map +1 -0
- package/dist/components/radio-group.d.ts +20 -0
- package/dist/components/radio-group.js +29 -0
- package/dist/components/radio-group.js.map +1 -0
- package/dist/components/scroll-area.d.ts +25 -0
- package/dist/components/scroll-area.js +39 -0
- package/dist/components/scroll-area.js.map +1 -0
- package/dist/components/section-card.d.ts +29 -0
- package/dist/components/section-card.js +55 -0
- package/dist/components/section-card.js.map +1 -0
- package/dist/components/segmented.d.ts +27 -0
- package/dist/components/segmented.js +32 -0
- package/dist/components/segmented.js.map +1 -0
- package/dist/components/select.d.ts +62 -0
- package/dist/components/select.js +97 -0
- package/dist/components/select.js.map +1 -0
- package/dist/components/separator.d.ts +19 -0
- package/dist/components/separator.js +23 -0
- package/dist/components/separator.js.map +1 -0
- package/dist/components/sheet.d.ts +35 -0
- package/dist/components/sheet.js +131 -0
- package/dist/components/sheet.js.map +1 -0
- package/dist/components/skeleton.d.ts +12 -0
- package/dist/components/skeleton.js +22 -0
- package/dist/components/skeleton.js.map +1 -0
- package/dist/components/sortable-list.d.ts +67 -0
- package/dist/components/sortable-list.js +206 -0
- package/dist/components/sortable-list.js.map +1 -0
- package/dist/components/spinner.d.ts +29 -0
- package/dist/components/spinner.js +34 -0
- package/dist/components/spinner.js.map +1 -0
- package/dist/components/status-dot.d.ts +26 -0
- package/dist/components/status-dot.js +47 -0
- package/dist/components/status-dot.js.map +1 -0
- package/dist/components/switch.d.ts +21 -0
- package/dist/components/switch.js +21 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/tabs.d.ts +24 -0
- package/dist/components/tabs.js +60 -0
- package/dist/components/tabs.js.map +1 -0
- package/dist/components/textarea.d.ts +19 -0
- package/dist/components/textarea.js +47 -0
- package/dist/components/textarea.js.map +1 -0
- package/dist/components/toast.d.ts +47 -0
- package/dist/components/toast.js +106 -0
- package/dist/components/toast.js.map +1 -0
- package/dist/components/toolbar.d.ts +19 -0
- package/dist/components/toolbar.js +45 -0
- package/dist/components/toolbar.js.map +1 -0
- package/dist/components/tooltip.d.ts +28 -0
- package/dist/components/tooltip.js +59 -0
- package/dist/components/tooltip.js.map +1 -0
- package/dist/core.d.ts +44 -0
- package/dist/core.js +44 -0
- package/dist/data-table.d.ts +9 -0
- package/dist/data-table.js +7 -0
- package/dist/form.d.ts +7 -0
- package/dist/form.js +7 -0
- package/dist/hooks/use-cortena-theme.d.ts +98 -0
- package/dist/hooks/use-cortena-theme.js +145 -0
- package/dist/hooks/use-cortena-theme.js.map +1 -0
- package/dist/index.d.ts +75 -2678
- package/dist/index.js +66 -8598
- package/dist/lib/cn.d.ts +10 -0
- package/dist/lib/cn.js +15 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/render.d.ts +23 -0
- package/dist/lib/render.js +20 -0
- package/dist/lib/render.js.map +1 -0
- package/dist/lib/shakeable.js +39 -0
- package/dist/lib/shakeable.js.map +1 -0
- package/dist/markdown.d.ts +3 -0
- package/dist/markdown.js +3 -0
- package/dist/sortable-list.d.ts +3 -0
- package/dist/sortable-list.js +3 -0
- package/package.json +37 -14
- package/src/a2ui/catalogue.ts +140 -103
- package/src/a2ui/views.tsx +5 -2
- package/src/components/alert.tsx +1 -1
- package/src/components/badge.tsx +1 -1
- package/src/components/button.tsx +1 -1
- package/src/components/chart/chart.tsx +72 -25
- package/src/components/chart/container.tsx +24 -39
- package/src/components/chart/index.tsx +4 -2
- package/src/components/chart/nivo-charts.tsx +41 -13
- package/src/components/chart/nivo-lazy.tsx +52 -0
- package/src/components/chart/recharts-charts.tsx +98 -3
- package/src/components/chart/recharts-lazy.tsx +72 -0
- package/src/components/chart/recharts-parts.tsx +34 -0
- package/src/components/chart/tokens.ts +1 -1
- package/src/components/chart/types.ts +14 -0
- package/src/components/chip.tsx +1 -1
- package/src/components/combobox.tsx +2 -2
- package/src/components/data-table/export.ts +18 -2
- package/src/components/data-table/features.ts +77 -45
- package/src/components/data-table/parts.tsx +15 -0
- package/src/components/data-table/use-data-table.ts +7 -2
- package/src/components/data-table/use-server-source.ts +17 -4
- package/src/components/date-field.tsx +1 -1
- package/src/components/error-banner.tsx +1 -1
- package/src/components/field.tsx +2 -2
- package/src/components/form.tsx +2 -2
- package/src/components/input.tsx +1 -1
- package/src/components/label.tsx +1 -1
- package/src/components/markdown.tsx +5 -4
- package/src/components/sheet.tsx +2 -2
- package/src/components/sortable-list.tsx +2 -2
- package/src/components/textarea.tsx +1 -1
- package/src/components/toast.tsx +1 -1
- package/src/entries/a2ui.ts +11 -0
- package/src/entries/chart.ts +10 -0
- package/src/entries/core.ts +91 -0
- package/src/entries/data-table.ts +9 -0
- package/src/entries/form.ts +24 -0
- package/src/entries/markdown.ts +9 -0
- package/src/entries/sortable-list.ts +13 -0
- package/src/index.ts +32 -98
- package/src/lib/shakeable.ts +33 -0
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
//#region src/a2ui/message.ts
|
|
3
|
+
/**
|
|
4
|
+
* A2UI message layer.
|
|
5
|
+
*
|
|
6
|
+
* The wire format cortena-ui accepts, and the fold from a stream of messages
|
|
7
|
+
* onto a set of surfaces. Deliberately free of React so it can be reused by a
|
|
8
|
+
* canvas host, a test fixture, or the catalogue doc generator.
|
|
9
|
+
*
|
|
10
|
+
* ## Two dialects, one fold
|
|
11
|
+
*
|
|
12
|
+
* **v0.9** is the current A2UI protocol (`createSurface`, `updateComponents`,
|
|
13
|
+
* `updateDataModel`, `deleteSurface`) and is what an agent should emit.
|
|
14
|
+
*
|
|
15
|
+
* **v0.8** is what Cortena already speaks: core bundles Google's A2UI Lit
|
|
16
|
+
* renderer at `src/canvas-host/a2ui/` and its `A2uiMessageProcessor` handles
|
|
17
|
+
* `beginRendering`, `surfaceUpdate`, `dataModelUpdate` and `deleteSurface`.
|
|
18
|
+
* The `canvas` agent tool pushes exactly those, as JSON lines, through
|
|
19
|
+
* `canvas.a2ui.pushJSONL`. Accepting both means one renderer serves the chat,
|
|
20
|
+
* the web canvas panel and every payload already written against the native
|
|
21
|
+
* nodes.
|
|
22
|
+
*
|
|
23
|
+
* ## Transport
|
|
24
|
+
*
|
|
25
|
+
* Per `05-agent-ui.md`, an A2UI block travels as an AG-UI `CUSTOM` event. The
|
|
26
|
+
* event value is a message, an array of messages, or the JSONL text the canvas
|
|
27
|
+
* tool sends; {@link parseA2UIMessages} takes all three.
|
|
28
|
+
*/
|
|
29
|
+
/** Surface id used when a message omits one, matching core's Lit processor. */
|
|
30
|
+
const A2UI_DEFAULT_SURFACE_ID = "@default";
|
|
31
|
+
/** Protocol version this package speaks. */
|
|
32
|
+
const A2UI_VERSION = "v0.9";
|
|
33
|
+
function isRecord(value) {
|
|
34
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
35
|
+
}
|
|
36
|
+
/** True when a value is a `{ path }` data binding rather than a literal object. */
|
|
37
|
+
function isDataBinding(value) {
|
|
38
|
+
return isRecord(value) && typeof value.path === "string" && Object.keys(value).length === 1;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Parse whatever arrived into a message list.
|
|
42
|
+
*
|
|
43
|
+
* A string is tried as JSON first (an array or a single message) and then as
|
|
44
|
+
* JSONL, which is the form `canvas.a2ui.pushJSONL` sends. A line that does not
|
|
45
|
+
* parse is reported rather than dropped: silently rendering three of four
|
|
46
|
+
* pushed messages is the failure mode this returns errors for.
|
|
47
|
+
*/
|
|
48
|
+
function parseA2UIMessages(input) {
|
|
49
|
+
if (input === null || input === void 0) return {
|
|
50
|
+
messages: [],
|
|
51
|
+
errors: []
|
|
52
|
+
};
|
|
53
|
+
if (typeof input === "string") {
|
|
54
|
+
const trimmed = input.trim();
|
|
55
|
+
if (!trimmed) return {
|
|
56
|
+
messages: [],
|
|
57
|
+
errors: []
|
|
58
|
+
};
|
|
59
|
+
if (trimmed.startsWith("[")) try {
|
|
60
|
+
const parsed = JSON.parse(trimmed);
|
|
61
|
+
if (Array.isArray(parsed)) return collect(parsed);
|
|
62
|
+
return {
|
|
63
|
+
messages: [],
|
|
64
|
+
errors: ["Expected an array of A2UI messages."]
|
|
65
|
+
};
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return {
|
|
68
|
+
messages: [],
|
|
69
|
+
errors: [`Malformed JSON: ${errorText(error)}`]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const messages = [];
|
|
73
|
+
const errors = [];
|
|
74
|
+
const lines = trimmed.split("\n");
|
|
75
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
76
|
+
const line = (lines[index] ?? "").trim();
|
|
77
|
+
if (!line) continue;
|
|
78
|
+
try {
|
|
79
|
+
const one = collect([JSON.parse(line)]);
|
|
80
|
+
messages.push(...one.messages);
|
|
81
|
+
errors.push(...one.errors.map((text) => `Line ${index + 1}: ${text}`));
|
|
82
|
+
} catch (error) {
|
|
83
|
+
errors.push(`Line ${index + 1}: malformed JSON (${errorText(error)}).`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
messages,
|
|
88
|
+
errors
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return collect(Array.isArray(input) ? input : [input]);
|
|
92
|
+
}
|
|
93
|
+
function collect(values) {
|
|
94
|
+
const messages = [];
|
|
95
|
+
const errors = [];
|
|
96
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
97
|
+
const value = values[index];
|
|
98
|
+
if (!isRecord(value)) {
|
|
99
|
+
errors.push(`Message ${index + 1} is not an object.`);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
messages.push(value);
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
messages,
|
|
106
|
+
errors
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function errorText(error) {
|
|
110
|
+
return error instanceof Error ? error.message : String(error);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Fold a message stream onto surfaces.
|
|
114
|
+
*
|
|
115
|
+
* Updates accumulate: a later `updateComponents` replaces the components it
|
|
116
|
+
* names and leaves the rest, and a later `updateDataModel` writes at its path
|
|
117
|
+
* only. That is what makes the JSONL push form work — the agent sends the
|
|
118
|
+
* shell, then the rows, then a correction, and each one folds onto the same
|
|
119
|
+
* surface.
|
|
120
|
+
*/
|
|
121
|
+
function foldMessages(input) {
|
|
122
|
+
const { messages, errors } = parseA2UIMessages(input);
|
|
123
|
+
const surfaces = /* @__PURE__ */ new Map();
|
|
124
|
+
const order = [];
|
|
125
|
+
const surfaceFor = (id) => {
|
|
126
|
+
const key = id && id.trim() ? id : A2UI_DEFAULT_SURFACE_ID;
|
|
127
|
+
let surface = surfaces.get(key);
|
|
128
|
+
if (!surface) {
|
|
129
|
+
surface = {
|
|
130
|
+
id: key,
|
|
131
|
+
root: void 0,
|
|
132
|
+
components: /* @__PURE__ */ new Map(),
|
|
133
|
+
dataModel: {},
|
|
134
|
+
styles: {}
|
|
135
|
+
};
|
|
136
|
+
surfaces.set(key, surface);
|
|
137
|
+
order.push(key);
|
|
138
|
+
}
|
|
139
|
+
return surface;
|
|
140
|
+
};
|
|
141
|
+
const applyComponents = (payload, index) => {
|
|
142
|
+
if (!payload) return;
|
|
143
|
+
const surface = surfaceFor(payload.surfaceId);
|
|
144
|
+
if (!Array.isArray(payload.components)) {
|
|
145
|
+
errors.push(`Message ${index + 1}: components must be an array.`);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
for (const spec of payload.components) {
|
|
149
|
+
if (!isRecord(spec) || typeof spec.id !== "string" || !spec.id) {
|
|
150
|
+
errors.push(`Message ${index + 1}: a component is missing a string id.`);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
surface.components.set(spec.id, spec);
|
|
154
|
+
}
|
|
155
|
+
if (typeof payload.root === "string" && payload.root) surface.root = payload.root;
|
|
156
|
+
};
|
|
157
|
+
const applyData = (payload) => {
|
|
158
|
+
if (!payload) return;
|
|
159
|
+
const surface = surfaceFor(payload.surfaceId);
|
|
160
|
+
const value = payload.value !== void 0 ? payload.value : payload.contents;
|
|
161
|
+
const path = payload.path && payload.path.trim() ? payload.path : "/";
|
|
162
|
+
surface.dataModel = setAtPointer(surface.dataModel, path, value);
|
|
163
|
+
};
|
|
164
|
+
for (let index = 0; index < messages.length; index += 1) {
|
|
165
|
+
const message = messages[index];
|
|
166
|
+
if (!message) continue;
|
|
167
|
+
if (message.createSurface) {
|
|
168
|
+
const surface = surfaceFor(message.createSurface.surfaceId);
|
|
169
|
+
if (typeof message.createSurface.root === "string" && message.createSurface.root) surface.root = message.createSurface.root;
|
|
170
|
+
}
|
|
171
|
+
if (message.beginRendering) {
|
|
172
|
+
const surface = surfaceFor(message.beginRendering.surfaceId);
|
|
173
|
+
if (typeof message.beginRendering.root === "string" && message.beginRendering.root) surface.root = message.beginRendering.root;
|
|
174
|
+
if (isRecord(message.beginRendering.styles)) surface.styles = {
|
|
175
|
+
...surface.styles,
|
|
176
|
+
...message.beginRendering.styles
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
applyComponents(message.updateComponents, index);
|
|
180
|
+
applyComponents(message.surfaceUpdate, index);
|
|
181
|
+
applyData(message.updateDataModel);
|
|
182
|
+
applyData(message.dataModelUpdate);
|
|
183
|
+
if (message.deleteSurface) {
|
|
184
|
+
const key = message.deleteSurface.surfaceId ?? "@default";
|
|
185
|
+
surfaces.delete(key);
|
|
186
|
+
const at = order.indexOf(key);
|
|
187
|
+
if (at !== -1) order.splice(at, 1);
|
|
188
|
+
}
|
|
189
|
+
if (!(message.createSurface ?? message.updateComponents ?? message.updateDataModel ?? message.deleteSurface ?? message.beginRendering ?? message.surfaceUpdate ?? message.dataModelUpdate)) errors.push(`Message ${index + 1}: no known operation (createSurface, updateComponents, updateDataModel, deleteSurface, beginRendering, surfaceUpdate, dataModelUpdate).`);
|
|
190
|
+
}
|
|
191
|
+
const result = [];
|
|
192
|
+
for (const key of order) {
|
|
193
|
+
const surface = surfaces.get(key);
|
|
194
|
+
if (!surface) continue;
|
|
195
|
+
result.push({
|
|
196
|
+
id: surface.id,
|
|
197
|
+
root: surface.root ?? defaultRoot(surface.components),
|
|
198
|
+
components: surface.components,
|
|
199
|
+
dataModel: surface.dataModel,
|
|
200
|
+
styles: surface.styles
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
surfaces: result,
|
|
205
|
+
errors
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/** No explicit root: prefer the conventional `root` id, else the first component. */
|
|
209
|
+
function defaultRoot(components) {
|
|
210
|
+
if (components.has("root")) return "root";
|
|
211
|
+
for (const id of components.keys()) return id;
|
|
212
|
+
}
|
|
213
|
+
/** Split a JSON pointer into its unescaped segments. */
|
|
214
|
+
function pointerSegments(pointer) {
|
|
215
|
+
const trimmed = pointer.trim();
|
|
216
|
+
if (!trimmed || trimmed === "/" || trimmed === ".") return [];
|
|
217
|
+
return (trimmed.startsWith("/") ? trimmed.slice(1) : trimmed).split("/").filter((segment) => segment !== "").map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
218
|
+
}
|
|
219
|
+
/** Resolve a possibly relative pointer against a data context. */
|
|
220
|
+
function resolvePointer(path, contextPath) {
|
|
221
|
+
if (path.startsWith("/")) return path;
|
|
222
|
+
const relative = path === "." || path === "" ? "" : path.replace(/^\.?\//, "");
|
|
223
|
+
const base = contextPath === "/" || !contextPath ? "" : contextPath.replace(/\/$/, "");
|
|
224
|
+
if (!relative) return base || "/";
|
|
225
|
+
return `${base}/${relative}`;
|
|
226
|
+
}
|
|
227
|
+
/** Read a JSON pointer out of the data model. */
|
|
228
|
+
function getAtPointer(model, pointer) {
|
|
229
|
+
let current = model;
|
|
230
|
+
for (const segment of pointerSegments(pointer)) {
|
|
231
|
+
if (Array.isArray(current)) {
|
|
232
|
+
const index = Number(segment);
|
|
233
|
+
current = Number.isInteger(index) ? current[index] : void 0;
|
|
234
|
+
} else if (isRecord(current)) current = current[segment];
|
|
235
|
+
else return;
|
|
236
|
+
if (current === void 0) return;
|
|
237
|
+
}
|
|
238
|
+
return current;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Write a JSON pointer into the data model, returning a new model.
|
|
242
|
+
*
|
|
243
|
+
* Immutable because the renderer keeps the model in React state: a mutation in
|
|
244
|
+
* place would not re-render, which is the kind of silent nothing-happens bug
|
|
245
|
+
* that is hard to see in a browser test.
|
|
246
|
+
*/
|
|
247
|
+
function setAtPointer(model, pointer, value) {
|
|
248
|
+
const segments = pointerSegments(pointer);
|
|
249
|
+
if (segments.length === 0) return isRecord(value) ? { ...value } : {};
|
|
250
|
+
const clone = { ...model };
|
|
251
|
+
let cursor = clone;
|
|
252
|
+
for (let index = 0; index < segments.length - 1; index += 1) {
|
|
253
|
+
const segment = segments[index] ?? "";
|
|
254
|
+
const nextSegment = segments[index + 1] ?? "";
|
|
255
|
+
const wantsArray = /^\d+$/.test(nextSegment);
|
|
256
|
+
const existing = Array.isArray(cursor) ? cursor[Number(segment)] : cursor[segment];
|
|
257
|
+
let next;
|
|
258
|
+
if (Array.isArray(existing)) next = [...existing];
|
|
259
|
+
else if (isRecord(existing)) next = { ...existing };
|
|
260
|
+
else next = wantsArray ? [] : {};
|
|
261
|
+
if (Array.isArray(cursor)) cursor[Number(segment)] = next;
|
|
262
|
+
else cursor[segment] = next;
|
|
263
|
+
cursor = next;
|
|
264
|
+
}
|
|
265
|
+
const last = segments[segments.length - 1] ?? "";
|
|
266
|
+
if (Array.isArray(cursor)) cursor[Number(last)] = value;
|
|
267
|
+
else cursor[last] = value;
|
|
268
|
+
return clone;
|
|
269
|
+
}
|
|
270
|
+
function readChildren(spec, properties) {
|
|
271
|
+
const raw = spec.children ?? properties.children;
|
|
272
|
+
if (!raw) return { kind: "none" };
|
|
273
|
+
if (Array.isArray(raw)) return {
|
|
274
|
+
kind: "list",
|
|
275
|
+
ids: raw.filter((id) => typeof id === "string")
|
|
276
|
+
};
|
|
277
|
+
if (isRecord(raw)) {
|
|
278
|
+
const record = raw;
|
|
279
|
+
const explicitList = record.explicitList;
|
|
280
|
+
if (Array.isArray(explicitList)) return {
|
|
281
|
+
kind: "list",
|
|
282
|
+
ids: explicitList.filter((id) => typeof id === "string")
|
|
283
|
+
};
|
|
284
|
+
const template = record.template;
|
|
285
|
+
if (isRecord(template)) {
|
|
286
|
+
const { componentId, dataBinding } = template;
|
|
287
|
+
if (typeof componentId === "string" && typeof dataBinding === "string") return {
|
|
288
|
+
kind: "template",
|
|
289
|
+
componentId,
|
|
290
|
+
path: dataBinding
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (typeof record.componentId === "string" && typeof record.path === "string") return {
|
|
294
|
+
kind: "template",
|
|
295
|
+
componentId: record.componentId,
|
|
296
|
+
path: record.path
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
return { kind: "none" };
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Split a component spec into its type name and its raw (unresolved) properties,
|
|
303
|
+
* across both dialects. Returns `undefined` when the spec names no type.
|
|
304
|
+
*/
|
|
305
|
+
function readComponent(spec) {
|
|
306
|
+
if (typeof spec.component === "string") {
|
|
307
|
+
const { id: _id, component: _component, children: _children, weight: _weight, ...rest } = spec;
|
|
308
|
+
return {
|
|
309
|
+
name: spec.component,
|
|
310
|
+
properties: rest
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
if (isRecord(spec.component)) {
|
|
314
|
+
const name = Object.keys(spec.component)[0];
|
|
315
|
+
if (!name) return;
|
|
316
|
+
const properties = spec.component[name];
|
|
317
|
+
return {
|
|
318
|
+
name,
|
|
319
|
+
properties: isRecord(properties) ? properties : {}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Resolve one property value against the data model.
|
|
325
|
+
*
|
|
326
|
+
* `{ path }` reads the model, `{ literal }` / `{ literalString }` unwrap, and
|
|
327
|
+
* everything else is returned as written, recursing through arrays and objects
|
|
328
|
+
* so a binding nested inside a prop object still resolves.
|
|
329
|
+
*/
|
|
330
|
+
function resolveValue(value, model, contextPath) {
|
|
331
|
+
if (Array.isArray(value)) return value.map((item) => resolveValue(item, model, contextPath));
|
|
332
|
+
if (isRecord(value)) {
|
|
333
|
+
if (isDataBinding(value)) return getAtPointer(model, resolvePointer(value.path, contextPath));
|
|
334
|
+
if ("literalString" in value && typeof value.literalString === "string") return value.literalString;
|
|
335
|
+
if ("literal" in value && Object.keys(value).length === 1) return value.literal;
|
|
336
|
+
const out = {};
|
|
337
|
+
for (const [key, item] of Object.entries(value)) out[key] = resolveValue(item, model, contextPath);
|
|
338
|
+
return out;
|
|
339
|
+
}
|
|
340
|
+
return value;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Normalise an action property into `{ name, context }`.
|
|
344
|
+
*
|
|
345
|
+
* Accepts v0.9's `{ event: { name, context } }`, the bare `{ name, context }`
|
|
346
|
+
* shape, and a plain string, which is what an agent writes most often.
|
|
347
|
+
*/
|
|
348
|
+
function readAction(value) {
|
|
349
|
+
if (typeof value === "string" && value.trim()) return {
|
|
350
|
+
name: value,
|
|
351
|
+
context: {}
|
|
352
|
+
};
|
|
353
|
+
if (!isRecord(value)) return;
|
|
354
|
+
const event = isRecord(value.event) ? value.event : value;
|
|
355
|
+
const name = event.name;
|
|
356
|
+
if (typeof name !== "string" || !name) return;
|
|
357
|
+
return {
|
|
358
|
+
name,
|
|
359
|
+
context: isRecord(event.context) ? event.context : {}
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
//#endregion
|
|
363
|
+
export { A2UI_DEFAULT_SURFACE_ID, A2UI_VERSION, foldMessages, getAtPointer, isDataBinding, parseA2UIMessages, pointerSegments, readAction, readChildren, readComponent, resolvePointer, resolveValue, setAtPointer };
|
|
364
|
+
|
|
365
|
+
//# sourceMappingURL=message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message.js","names":[],"sources":["../../src/a2ui/message.ts"],"sourcesContent":["/**\n * A2UI message layer.\n *\n * The wire format cortena-ui accepts, and the fold from a stream of messages\n * onto a set of surfaces. Deliberately free of React so it can be reused by a\n * canvas host, a test fixture, or the catalogue doc generator.\n *\n * ## Two dialects, one fold\n *\n * **v0.9** is the current A2UI protocol (`createSurface`, `updateComponents`,\n * `updateDataModel`, `deleteSurface`) and is what an agent should emit.\n *\n * **v0.8** is what Cortena already speaks: core bundles Google's A2UI Lit\n * renderer at `src/canvas-host/a2ui/` and its `A2uiMessageProcessor` handles\n * `beginRendering`, `surfaceUpdate`, `dataModelUpdate` and `deleteSurface`.\n * The `canvas` agent tool pushes exactly those, as JSON lines, through\n * `canvas.a2ui.pushJSONL`. Accepting both means one renderer serves the chat,\n * the web canvas panel and every payload already written against the native\n * nodes.\n *\n * ## Transport\n *\n * Per `05-agent-ui.md`, an A2UI block travels as an AG-UI `CUSTOM` event. The\n * event value is a message, an array of messages, or the JSONL text the canvas\n * tool sends; {@link parseA2UIMessages} takes all three.\n */\n\n/** Surface id used when a message omits one, matching core's Lit processor. */\nexport const A2UI_DEFAULT_SURFACE_ID = \"@default\";\n\n/** Protocol version this package speaks. */\nexport const A2UI_VERSION = \"v0.9\";\n\n/** A reference to a value in the surface's data model, as a JSON pointer. */\nexport interface A2UIDataBinding {\n path: string;\n}\n\n/**\n * A property value as an agent writes it: a literal, a data binding, or one of\n * the v0.8 literal wrappers the Lit renderer accepts.\n */\nexport type A2UIPropertyValue =\n | string\n | number\n | boolean\n | null\n | A2UIDataBinding\n | { literal: unknown }\n | { literalString: string }\n | readonly A2UIPropertyValue[]\n | { readonly [key: string]: unknown };\n\n/**\n * A component's children. v0.9 uses a plain id array or a `{ componentId, path }`\n * template; v0.8 wraps the same two in `explicitList` / `template`.\n */\nexport type A2UIChildList =\n | readonly string[]\n | { componentId: string; path: string }\n | { explicitList: readonly string[] }\n | { template: { componentId: string; dataBinding: string } };\n\n/** An action an agent attaches to an interactive property. */\nexport type A2UIActionSpec =\n | string\n | { event: { name: string; context?: Record<string, unknown> } }\n | { name: string; context?: Record<string, unknown> };\n\n/**\n * One component in a surface.\n *\n * v0.9 is flat — `{ id, component: \"Text\", text: \"hi\" }` — and v0.8 wraps the\n * properties under the type name — `{ id, component: { Text: { text: \"hi\" } } }`.\n */\nexport interface A2UIComponentSpec {\n id: string;\n component: string | Record<string, Record<string, unknown>>;\n children?: A2UIChildList;\n /** v0.8 flex weight; carried through but not used for layout here. */\n weight?: number | string;\n [property: string]: unknown;\n}\n\nexport interface A2UICreateSurface {\n surfaceId?: string;\n catalogId?: string;\n /** Not in the v0.9 schema; accepted so a single message can seed a surface. */\n root?: string;\n sendDataModel?: boolean;\n}\n\nexport interface A2UIUpdateComponents {\n surfaceId?: string;\n components: A2UIComponentSpec[];\n root?: string;\n}\n\nexport interface A2UIUpdateDataModel {\n surfaceId?: string;\n path?: string;\n value?: unknown;\n /** v0.8 spelling of `value`. */\n contents?: unknown;\n}\n\nexport interface A2UIBeginRendering {\n surfaceId?: string;\n root?: string;\n styles?: Record<string, unknown>;\n}\n\nexport interface A2UIDeleteSurface {\n surfaceId?: string;\n}\n\n/** One message on the wire. Exactly one operation key is read per message. */\nexport interface A2UIMessage {\n version?: string;\n createSurface?: A2UICreateSurface;\n updateComponents?: A2UIUpdateComponents;\n updateDataModel?: A2UIUpdateDataModel;\n deleteSurface?: A2UIDeleteSurface;\n /** v0.8 */\n beginRendering?: A2UIBeginRendering;\n /** v0.8 */\n surfaceUpdate?: A2UIUpdateComponents;\n /** v0.8 */\n dataModelUpdate?: A2UIUpdateDataModel;\n}\n\n/**\n * Everything the renderer's `message` prop takes: one message, a list of them\n * (the AG-UI `CUSTOM` payload), or JSONL / a JSON array as text (the canvas\n * tool's `a2ui_push`).\n */\nexport type A2UIMessageInput = A2UIMessage | readonly A2UIMessage[] | string | null | undefined;\n\n/** A folded surface: what the renderer draws. */\nexport interface A2UISurface {\n id: string;\n /** Id of the root component; `undefined` until a message names one. */\n root: string | undefined;\n components: ReadonlyMap<string, A2UIComponentSpec>;\n dataModel: Record<string, unknown>;\n styles: Record<string, unknown>;\n}\n\nexport interface A2UIFoldResult {\n surfaces: A2UISurface[];\n /** One line per malformed message; the renderer shows these in an error card. */\n errors: string[];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** True when a value is a `{ path }` data binding rather than a literal object. */\nexport function isDataBinding(value: unknown): value is A2UIDataBinding {\n return isRecord(value) && typeof value.path === \"string\" && Object.keys(value).length === 1;\n}\n\n/**\n * Parse whatever arrived into a message list.\n *\n * A string is tried as JSON first (an array or a single message) and then as\n * JSONL, which is the form `canvas.a2ui.pushJSONL` sends. A line that does not\n * parse is reported rather than dropped: silently rendering three of four\n * pushed messages is the failure mode this returns errors for.\n */\nexport function parseA2UIMessages(input: A2UIMessageInput): {\n messages: A2UIMessage[];\n errors: string[];\n} {\n if (input === null || input === undefined) {\n return { messages: [], errors: [] };\n }\n if (typeof input === \"string\") {\n const trimmed = input.trim();\n if (!trimmed) {\n return { messages: [], errors: [] };\n }\n if (trimmed.startsWith(\"[\")) {\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (Array.isArray(parsed)) {\n return collect(parsed);\n }\n return { messages: [], errors: [\"Expected an array of A2UI messages.\"] };\n } catch (error) {\n return { messages: [], errors: [`Malformed JSON: ${errorText(error)}`] };\n }\n }\n const messages: A2UIMessage[] = [];\n const errors: string[] = [];\n const lines = trimmed.split(\"\\n\");\n for (let index = 0; index < lines.length; index += 1) {\n const line = (lines[index] ?? \"\").trim();\n if (!line) {\n continue;\n }\n try {\n const parsed: unknown = JSON.parse(line);\n const one = collect([parsed]);\n messages.push(...one.messages);\n errors.push(...one.errors.map((text) => `Line ${index + 1}: ${text}`));\n } catch (error) {\n errors.push(`Line ${index + 1}: malformed JSON (${errorText(error)}).`);\n }\n }\n return { messages, errors };\n }\n return collect(Array.isArray(input) ? input : [input]);\n}\n\nfunction collect(values: readonly unknown[]): { messages: A2UIMessage[]; errors: string[] } {\n const messages: A2UIMessage[] = [];\n const errors: string[] = [];\n for (let index = 0; index < values.length; index += 1) {\n const value = values[index];\n if (!isRecord(value)) {\n errors.push(`Message ${index + 1} is not an object.`);\n continue;\n }\n messages.push(value as A2UIMessage);\n }\n return { messages, errors };\n}\n\nfunction errorText(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\ninterface MutableSurface {\n id: string;\n root: string | undefined;\n components: Map<string, A2UIComponentSpec>;\n dataModel: Record<string, unknown>;\n styles: Record<string, unknown>;\n}\n\n/**\n * Fold a message stream onto surfaces.\n *\n * Updates accumulate: a later `updateComponents` replaces the components it\n * names and leaves the rest, and a later `updateDataModel` writes at its path\n * only. That is what makes the JSONL push form work — the agent sends the\n * shell, then the rows, then a correction, and each one folds onto the same\n * surface.\n */\nexport function foldMessages(input: A2UIMessageInput): A2UIFoldResult {\n const { messages, errors } = parseA2UIMessages(input);\n const surfaces = new Map<string, MutableSurface>();\n const order: string[] = [];\n\n const surfaceFor = (id: string | undefined): MutableSurface => {\n const key = id && id.trim() ? id : A2UI_DEFAULT_SURFACE_ID;\n let surface = surfaces.get(key);\n if (!surface) {\n surface = { id: key, root: undefined, components: new Map(), dataModel: {}, styles: {} };\n surfaces.set(key, surface);\n order.push(key);\n }\n return surface;\n };\n\n const applyComponents = (payload: A2UIUpdateComponents | undefined, index: number): void => {\n if (!payload) {\n return;\n }\n const surface = surfaceFor(payload.surfaceId);\n if (!Array.isArray(payload.components)) {\n errors.push(`Message ${index + 1}: components must be an array.`);\n return;\n }\n for (const spec of payload.components) {\n if (!isRecord(spec) || typeof spec.id !== \"string\" || !spec.id) {\n errors.push(`Message ${index + 1}: a component is missing a string id.`);\n continue;\n }\n surface.components.set(spec.id, spec as A2UIComponentSpec);\n }\n if (typeof payload.root === \"string\" && payload.root) {\n surface.root = payload.root;\n }\n };\n\n const applyData = (payload: A2UIUpdateDataModel | undefined): void => {\n if (!payload) {\n return;\n }\n const surface = surfaceFor(payload.surfaceId);\n const value = payload.value !== undefined ? payload.value : payload.contents;\n const path = payload.path && payload.path.trim() ? payload.path : \"/\";\n surface.dataModel = setAtPointer(surface.dataModel, path, value);\n };\n\n for (let index = 0; index < messages.length; index += 1) {\n const message = messages[index];\n if (!message) {\n continue;\n }\n if (message.createSurface) {\n const surface = surfaceFor(message.createSurface.surfaceId);\n if (typeof message.createSurface.root === \"string\" && message.createSurface.root) {\n surface.root = message.createSurface.root;\n }\n }\n if (message.beginRendering) {\n const surface = surfaceFor(message.beginRendering.surfaceId);\n if (typeof message.beginRendering.root === \"string\" && message.beginRendering.root) {\n surface.root = message.beginRendering.root;\n }\n if (isRecord(message.beginRendering.styles)) {\n surface.styles = { ...surface.styles, ...message.beginRendering.styles };\n }\n }\n applyComponents(message.updateComponents, index);\n applyComponents(message.surfaceUpdate, index);\n applyData(message.updateDataModel);\n applyData(message.dataModelUpdate);\n if (message.deleteSurface) {\n const key = message.deleteSurface.surfaceId ?? A2UI_DEFAULT_SURFACE_ID;\n surfaces.delete(key);\n const at = order.indexOf(key);\n if (at !== -1) {\n order.splice(at, 1);\n }\n }\n const known =\n message.createSurface ??\n message.updateComponents ??\n message.updateDataModel ??\n message.deleteSurface ??\n message.beginRendering ??\n message.surfaceUpdate ??\n message.dataModelUpdate;\n if (!known) {\n errors.push(\n `Message ${index + 1}: no known operation (createSurface, updateComponents, updateDataModel, deleteSurface, beginRendering, surfaceUpdate, dataModelUpdate).`,\n );\n }\n }\n\n const result: A2UISurface[] = [];\n for (const key of order) {\n const surface = surfaces.get(key);\n if (!surface) {\n continue;\n }\n result.push({\n id: surface.id,\n root: surface.root ?? defaultRoot(surface.components),\n components: surface.components,\n dataModel: surface.dataModel,\n styles: surface.styles,\n });\n }\n return { surfaces: result, errors };\n}\n\n/** No explicit root: prefer the conventional `root` id, else the first component. */\nfunction defaultRoot(components: ReadonlyMap<string, A2UIComponentSpec>): string | undefined {\n if (components.has(\"root\")) {\n return \"root\";\n }\n for (const id of components.keys()) {\n return id;\n }\n return undefined;\n}\n\n/** Split a JSON pointer into its unescaped segments. */\nexport function pointerSegments(pointer: string): string[] {\n const trimmed = pointer.trim();\n if (!trimmed || trimmed === \"/\" || trimmed === \".\") {\n return [];\n }\n const body = trimmed.startsWith(\"/\") ? trimmed.slice(1) : trimmed;\n return body\n .split(\"/\")\n .filter((segment) => segment !== \"\")\n .map((segment) => segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n}\n\n/** Resolve a possibly relative pointer against a data context. */\nexport function resolvePointer(path: string, contextPath: string): string {\n if (path.startsWith(\"/\")) {\n return path;\n }\n const relative = path === \".\" || path === \"\" ? \"\" : path.replace(/^\\.?\\//, \"\");\n const base = contextPath === \"/\" || !contextPath ? \"\" : contextPath.replace(/\\/$/, \"\");\n if (!relative) {\n return base || \"/\";\n }\n return `${base}/${relative}`;\n}\n\n/** Read a JSON pointer out of the data model. */\nexport function getAtPointer(model: unknown, pointer: string): unknown {\n let current: unknown = model;\n for (const segment of pointerSegments(pointer)) {\n if (Array.isArray(current)) {\n const index = Number(segment);\n current = Number.isInteger(index) ? current[index] : undefined;\n } else if (isRecord(current)) {\n current = current[segment];\n } else {\n return undefined;\n }\n if (current === undefined) {\n return undefined;\n }\n }\n return current;\n}\n\n/**\n * Write a JSON pointer into the data model, returning a new model.\n *\n * Immutable because the renderer keeps the model in React state: a mutation in\n * place would not re-render, which is the kind of silent nothing-happens bug\n * that is hard to see in a browser test.\n */\nexport function setAtPointer(\n model: Record<string, unknown>,\n pointer: string,\n value: unknown,\n): Record<string, unknown> {\n const segments = pointerSegments(pointer);\n if (segments.length === 0) {\n return isRecord(value) ? { ...value } : {};\n }\n const clone: Record<string, unknown> = { ...model };\n let cursor: Record<string, unknown> | unknown[] = clone;\n for (let index = 0; index < segments.length - 1; index += 1) {\n const segment = segments[index] ?? \"\";\n const nextSegment = segments[index + 1] ?? \"\";\n const wantsArray = /^\\d+$/.test(nextSegment);\n const existing: unknown = Array.isArray(cursor)\n ? cursor[Number(segment)]\n : (cursor as Record<string, unknown>)[segment];\n let next: Record<string, unknown> | unknown[];\n if (Array.isArray(existing)) {\n next = [...existing];\n } else if (isRecord(existing)) {\n next = { ...existing };\n } else {\n next = wantsArray ? [] : {};\n }\n if (Array.isArray(cursor)) {\n cursor[Number(segment)] = next;\n } else {\n (cursor as Record<string, unknown>)[segment] = next;\n }\n cursor = next;\n }\n const last = segments[segments.length - 1] ?? \"\";\n if (Array.isArray(cursor)) {\n cursor[Number(last)] = value;\n } else {\n (cursor as Record<string, unknown>)[last] = value;\n }\n return clone;\n}\n\n/** Normalise the children of a component spec into a single description. */\nexport type A2UIChildren =\n | { kind: \"list\"; ids: readonly string[] }\n | { kind: \"template\"; componentId: string; path: string }\n | { kind: \"none\" };\n\nexport function readChildren(spec: A2UIComponentSpec, properties: Record<string, unknown>): A2UIChildren {\n const raw = (spec.children ?? properties.children) as A2UIChildList | undefined;\n if (!raw) {\n return { kind: \"none\" };\n }\n if (Array.isArray(raw)) {\n return { kind: \"list\", ids: raw.filter((id): id is string => typeof id === \"string\") };\n }\n if (isRecord(raw)) {\n const record: Record<string, unknown> = raw;\n const explicitList = record.explicitList;\n if (Array.isArray(explicitList)) {\n return {\n kind: \"list\",\n ids: explicitList.filter((id): id is string => typeof id === \"string\"),\n };\n }\n const template = record.template;\n if (isRecord(template)) {\n const { componentId, dataBinding } = template;\n if (typeof componentId === \"string\" && typeof dataBinding === \"string\") {\n return { kind: \"template\", componentId, path: dataBinding };\n }\n }\n if (typeof record.componentId === \"string\" && typeof record.path === \"string\") {\n return { kind: \"template\", componentId: record.componentId, path: record.path };\n }\n }\n return { kind: \"none\" };\n}\n\n/**\n * Split a component spec into its type name and its raw (unresolved) properties,\n * across both dialects. Returns `undefined` when the spec names no type.\n */\nexport function readComponent(\n spec: A2UIComponentSpec,\n): { name: string; properties: Record<string, unknown> } | undefined {\n if (typeof spec.component === \"string\") {\n const { id: _id, component: _component, children: _children, weight: _weight, ...rest } = spec;\n return { name: spec.component, properties: rest };\n }\n if (isRecord(spec.component)) {\n const name = Object.keys(spec.component)[0];\n if (!name) {\n return undefined;\n }\n const properties = (spec.component as Record<string, unknown>)[name];\n return { name, properties: isRecord(properties) ? properties : {} };\n }\n return undefined;\n}\n\n/**\n * Resolve one property value against the data model.\n *\n * `{ path }` reads the model, `{ literal }` / `{ literalString }` unwrap, and\n * everything else is returned as written, recursing through arrays and objects\n * so a binding nested inside a prop object still resolves.\n */\nexport function resolveValue(\n value: unknown,\n model: Record<string, unknown>,\n contextPath: string,\n): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => resolveValue(item, model, contextPath));\n }\n if (isRecord(value)) {\n if (isDataBinding(value)) {\n return getAtPointer(model, resolvePointer(value.path, contextPath));\n }\n if (\"literalString\" in value && typeof value.literalString === \"string\") {\n return value.literalString;\n }\n if (\"literal\" in value && Object.keys(value).length === 1) {\n return value.literal;\n }\n const out: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n out[key] = resolveValue(item, model, contextPath);\n }\n return out;\n }\n return value;\n}\n\n/** A user action leaving the renderer, shaped like A2UI's client-to-server action. */\nexport interface A2UIAction {\n surfaceId: string;\n componentId: string;\n name: string;\n payload: Record<string, unknown>;\n}\n\n/**\n * Normalise an action property into `{ name, context }`.\n *\n * Accepts v0.9's `{ event: { name, context } }`, the bare `{ name, context }`\n * shape, and a plain string, which is what an agent writes most often.\n */\nexport function readAction(\n value: unknown,\n): { name: string; context: Record<string, unknown> } | undefined {\n if (typeof value === \"string\" && value.trim()) {\n return { name: value, context: {} };\n }\n if (!isRecord(value)) {\n return undefined;\n }\n const event = isRecord(value.event) ? value.event : value;\n const name = event.name;\n if (typeof name !== \"string\" || !name) {\n return undefined;\n }\n return { name, context: isRecord(event.context) ? event.context : {} };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { A2UIAction, A2UIMessageInput, A2UI_DEFAULT_SURFACE_ID } from "./message.js";
|
|
3
|
+
import { DataSource } from "../components/data-table/types.js";
|
|
4
|
+
import "../components/data-table/index.js";
|
|
5
|
+
import { A2UIRow } from "./views.js";
|
|
6
|
+
import { A2UICatalogue } from "./catalogue.js";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
//#region src/a2ui/renderer.d.ts
|
|
9
|
+
export interface A2UIRendererProps {
|
|
10
|
+
/**
|
|
11
|
+
* The payload. One message, the list an AG-UI `CUSTOM` event carries, or the
|
|
12
|
+
* JSONL text `canvas.a2ui.pushJSONL` sends. Later messages fold onto earlier
|
|
13
|
+
* ones, so a push stream can be accumulated by the host and handed here whole.
|
|
14
|
+
*/
|
|
15
|
+
message: A2UIMessageInput;
|
|
16
|
+
/** The component vocabulary. Defaults to the whole cortena-ui catalogue. */
|
|
17
|
+
catalogue?: A2UICatalogue;
|
|
18
|
+
/** Receives every user event, shaped like A2UI's client-to-server action. */
|
|
19
|
+
onAction?: (action: A2UIAction) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Resolves a `{ kind: "server", sourceId }` table handle into a real data
|
|
22
|
+
* source. Without it, a server table renders an error card rather than an
|
|
23
|
+
* empty one, because a silently empty table reads as "no results".
|
|
24
|
+
*/
|
|
25
|
+
resolveDataSource?: (sourceId: string) => DataSource<A2UIRow> | undefined;
|
|
26
|
+
/** Render only this surface. Default: every surface, in arrival order. */
|
|
27
|
+
surfaceId?: string;
|
|
28
|
+
/** Debounce before a continuous input becomes an action. @default 300 */
|
|
29
|
+
debounceMs?: number;
|
|
30
|
+
className?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A2UIRenderer — draws A2UI messages through cortena-ui.
|
|
34
|
+
*
|
|
35
|
+
* ```tsx
|
|
36
|
+
* <A2UIRenderer
|
|
37
|
+
* message={customEvent.value}
|
|
38
|
+
* catalogue={defaultCatalogue}
|
|
39
|
+
* onAction={(action) => agent.send(action)}
|
|
40
|
+
* resolveDataSource={(id) => sources[id]}
|
|
41
|
+
* />
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function A2UIRenderer({ message, catalogue, onAction, resolveDataSource, surfaceId, debounceMs, className }: A2UIRendererProps): React.JSX.Element;
|
|
45
|
+
//#endregion
|
|
46
|
+
//# sourceMappingURL=renderer.d.ts.map
|