@urbicon-ui/blocks 6.40.4 → 6.43.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/dist/components/Chat/A2UIView/A2UINode.svelte +20 -39
- package/dist/components/Chat/A2UIView/A2UIView.svelte +47 -50
- package/dist/components/Chat/A2UIView/a2ui-basic-catalog.d.ts +17 -0
- package/dist/components/Chat/A2UIView/a2ui-basic-catalog.js +69 -0
- package/dist/components/Chat/A2UIView/a2ui-catalog.d.ts +95 -0
- package/dist/components/Chat/A2UIView/a2ui-catalog.js +87 -0
- package/dist/components/Chat/A2UIView/a2ui-node-common.d.ts +40 -0
- package/dist/components/Chat/A2UIView/a2ui-node-common.js +72 -0
- package/dist/components/Chat/A2UIView/a2ui-prompt.d.ts +21 -10
- package/dist/components/Chat/A2UIView/a2ui-prompt.js +170 -24
- package/dist/components/Chat/A2UIView/a2ui-registry.d.ts +1 -1
- package/dist/components/Chat/A2UIView/a2ui-registry.js +2 -1
- package/dist/components/Chat/A2UIView/a2ui-render.d.ts +30 -0
- package/dist/components/Chat/A2UIView/a2ui-render.js +61 -23
- package/dist/components/Chat/A2UIView/a2ui-router.d.ts +136 -0
- package/dist/components/Chat/A2UIView/a2ui-router.js +264 -0
- package/dist/components/Chat/A2UIView/a2ui-schema.d.ts +45 -0
- package/dist/components/Chat/A2UIView/a2ui-schema.js +133 -0
- package/dist/components/Chat/A2UIView/a2ui-stream.d.ts +120 -0
- package/dist/components/Chat/A2UIView/a2ui-stream.js +406 -0
- package/dist/components/Chat/A2UIView/a2ui-transcript.d.ts +65 -0
- package/dist/components/Chat/A2UIView/a2ui-transcript.js +101 -0
- package/dist/components/Chat/A2UIView/a2ui-validate.d.ts +33 -1
- package/dist/components/Chat/A2UIView/a2ui-validate.js +167 -63
- package/dist/components/Chat/A2UIView/a2ui.types.d.ts +50 -0
- package/dist/components/Chat/A2UIView/a2ui.types.js +38 -1
- package/dist/components/Chat/A2UIView/index.d.ts +35 -2
- package/dist/components/Chat/A2UIView/index.js +13 -0
- package/dist/components/Chat/A2UIView/urbicon/UrbiconA2UINode.svelte +831 -0
- package/dist/components/Chat/A2UIView/urbicon/UrbiconA2UINode.svelte.d.ts +4 -0
- package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon-catalog.d.ts +14 -0
- package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon-catalog.js +87 -0
- package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon-registry.d.ts +78 -0
- package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon-registry.js +783 -0
- package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon.variants.d.ts +43 -0
- package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon.variants.js +33 -0
- package/dist/components/Chat/A2UIView/urbicon/urbicon-axes.gen.d.ts +7 -0
- package/dist/components/Chat/A2UIView/urbicon/urbicon-axes.gen.js +86 -0
- package/dist/components/Chat/ChatMessage/chat-message.variants.d.ts +19 -19
- package/dist/components/FileUpload/fileUpload.variants.js +15 -4
- package/dist/components/PinInput/PinInput.svelte +1 -0
- package/dist/components/PinInput/pin-input.variants.js +14 -1
- package/dist/components/TimeInput/time-input.variants.js +16 -1
- package/dist/internal/field-chrome.d.ts +18 -1
- package/dist/internal/field-chrome.js +18 -1
- package/dist/primitives/Avatar/avatar.variants.js +4 -1
- package/dist/primitives/Badge/badge.variants.js +11 -2
- package/dist/primitives/Button/button.variants.js +4 -1
- package/dist/primitives/Card/card.variants.js +4 -1
- package/dist/primitives/Checkbox/checkbox.variants.js +7 -1
- package/dist/primitives/Combobox/Combobox.svelte +9 -1
- package/dist/primitives/Combobox/combobox.variants.d.ts +25 -0
- package/dist/primitives/Combobox/combobox.variants.js +32 -3
- package/dist/primitives/Combobox/index.d.ts +8 -2
- package/dist/primitives/Dialog/dialog.variants.js +4 -1
- package/dist/primitives/Drawer/drawer.variants.js +4 -1
- package/dist/primitives/Input/input.variants.js +17 -1
- package/dist/primitives/Menu/menu.variants.js +5 -1
- package/dist/primitives/Progress/progress.variants.js +5 -2
- package/dist/primitives/RadioGroup/radioGroup.variants.js +68 -9
- package/dist/primitives/Select/select.variants.js +13 -4
- package/dist/primitives/Slider/Slider.svelte +4 -1
- package/dist/primitives/Slider/slider.variants.js +9 -2
- package/dist/primitives/Textarea/textarea.variants.js +15 -1
- package/dist/primitives/Toast/toast.variants.js +5 -1
- package/dist/primitives/Toggle/toggle.variants.js +92 -18
- package/dist/primitives/Tooltip/tooltip.variants.d.ts +3 -3
- package/dist/primitives/Tooltip/tooltip.variants.js +5 -1
- package/dist/utils/persistent-state.svelte.d.ts +25 -1
- package/dist/utils/persistent-state.svelte.js +98 -10
- package/docs/MIGRATION-v5.md +29 -0
- package/package.json +3 -3
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import { checkImageUrl, checkLinkUrl } from '../markdown/url-policy.js';
|
|
22
22
|
import type { A2uiActionEvent } from './a2ui.types';
|
|
23
23
|
import { A2UI_REGISTRY, A2UI_SVG_PATH_RE } from './a2ui-registry';
|
|
24
|
+
import { dedupeOptions, splitDateTime } from './a2ui-node-common';
|
|
24
25
|
import type { A2uiRenderContext, A2uiRenderNode } from './a2ui-render';
|
|
25
26
|
import { bindingPointer, toInputString, toStringArray } from './a2ui-render';
|
|
26
27
|
|
|
@@ -246,7 +247,11 @@
|
|
|
246
247
|
).value;
|
|
247
248
|
}
|
|
248
249
|
}
|
|
250
|
+
// `sendDataModel` surfaces ship the whole model alongside the context, so a
|
|
251
|
+
// context the agent under-specified still tells it what the user entered.
|
|
252
|
+
const dataModel = context.actionDataModel?.();
|
|
249
253
|
const payload: A2uiActionEvent = {
|
|
254
|
+
...(dataModel === undefined ? {} : { dataModel }),
|
|
250
255
|
name: event.name as string,
|
|
251
256
|
surfaceId: context.surfaceId,
|
|
252
257
|
sourceComponentId: instance.id,
|
|
@@ -306,27 +311,18 @@
|
|
|
306
311
|
if (pointer) return toStringArray(resolved('value'));
|
|
307
312
|
return localList ?? toStringArray(raw('value'));
|
|
308
313
|
});
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
// `
|
|
315
|
-
//
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (seen.has(option.value)) continue;
|
|
322
|
-
seen.add(option.value);
|
|
323
|
-
out.push({
|
|
324
|
-
value: option.value,
|
|
325
|
-
label: text(context.resolve(option.label, node.scopePrefix).value)
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
return out;
|
|
329
|
-
});
|
|
314
|
+
// Dedupe by value (first wins): the validator warns on duplicates, but the
|
|
315
|
+
// keyed `{#each}` below is on option.value — two equal keys throw Svelte's
|
|
316
|
+
// `each_key_duplicate` (a hard crash that would break the "never throw"
|
|
317
|
+
// contract for an untrusted payload). Shared with the Urbicon dispatcher.
|
|
318
|
+
const choiceOptions = $derived(
|
|
319
|
+
// `options` may itself be a { path } binding — that is how an agent shows a
|
|
320
|
+
// list it fetched mid-conversation (free slots, search hits) without
|
|
321
|
+
// rewriting the component. Resolve it first, then the per-option labels.
|
|
322
|
+
dedupeOptions(resolved('options'), (label) =>
|
|
323
|
+
text(context.resolve(label, node.scopePrefix).value)
|
|
324
|
+
)
|
|
325
|
+
);
|
|
330
326
|
const choiceLabel = $derived(text(resolved('label')));
|
|
331
327
|
function writeChoice(next: string[]) {
|
|
332
328
|
const pointer = valuePointer();
|
|
@@ -359,24 +355,6 @@
|
|
|
359
355
|
return 'date';
|
|
360
356
|
});
|
|
361
357
|
|
|
362
|
-
function normalizeTimePart(value: string): string {
|
|
363
|
-
const match = /^(\d{2}:\d{2}(?::\d{2})?)/.exec(value);
|
|
364
|
-
return match ? match[1] : '';
|
|
365
|
-
}
|
|
366
|
-
function splitDateTime(value: string): { date: string; time: string } {
|
|
367
|
-
const trimmed = value.trim();
|
|
368
|
-
if (trimmed === '') return { date: '', time: '' };
|
|
369
|
-
const tIndex = trimmed.indexOf('T');
|
|
370
|
-
if (tIndex !== -1) {
|
|
371
|
-
return {
|
|
372
|
-
date: trimmed.slice(0, tIndex),
|
|
373
|
-
time: normalizeTimePart(trimmed.slice(tIndex + 1))
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
if (trimmed.includes(':')) return { date: '', time: normalizeTimePart(trimmed) };
|
|
377
|
-
return { date: trimmed, time: '' };
|
|
378
|
-
}
|
|
379
|
-
|
|
380
358
|
const dtParts = $derived(splitDateTime(textValue));
|
|
381
359
|
const dtMinParts = $derived(splitDateTime(text(resolved('min'))));
|
|
382
360
|
const dtMaxParts = $derived(splitDateTime(text(resolved('max'))));
|
|
@@ -638,11 +616,14 @@
|
|
|
638
616
|
</RadioGroup>
|
|
639
617
|
{/if}
|
|
640
618
|
{:else if component === 'Slider'}
|
|
619
|
+
<!-- showValue is forced on: a generated surface has no other place to state
|
|
620
|
+
the current number, and the agent cannot ask for it (not a catalog prop). -->
|
|
641
621
|
<Slider
|
|
642
622
|
label={text(resolved('label')) || undefined}
|
|
643
623
|
value={sliderValue}
|
|
644
624
|
min={sliderMin}
|
|
645
625
|
max={sliderMax}
|
|
626
|
+
showValue
|
|
646
627
|
onValueChange={onSliderChange}
|
|
647
628
|
style={weightStyle}
|
|
648
629
|
aria-label={ariaLabel}
|
|
@@ -2,26 +2,9 @@
|
|
|
2
2
|
import { Alert } from '../../../primitives';
|
|
3
3
|
import { untrack } from 'svelte';
|
|
4
4
|
import { getBlocksConfig, resolveSlotClasses } from '../../../provider';
|
|
5
|
-
import { type IconComponent, resolveIcon } from '../../../icons';
|
|
6
|
-
import PlusIconDefault from '../../../icons/PlusIcon.svelte';
|
|
7
|
-
import ArrowLeftIconDefault from '../../../icons/ArrowLeftIcon.svelte';
|
|
8
|
-
import CheckIconDefault from '../../../icons/CheckIcon.svelte';
|
|
9
|
-
import CloseIconDefault from '../../../icons/CloseIcon.svelte';
|
|
10
|
-
import TrashIconDefault from '../../../icons/TrashIcon.svelte';
|
|
11
|
-
import EditIconDefault from '../../../icons/EditIcon.svelte';
|
|
12
|
-
import DangerCircleIconDefault from '../../../icons/DangerCircleIcon.svelte';
|
|
13
|
-
import HomeIconDefault from '../../../icons/HomeIcon.svelte';
|
|
14
|
-
import InfoCircleIconDefault from '../../../icons/InfoCircleIcon.svelte';
|
|
15
|
-
import MailIconDefault from '../../../icons/MailIcon.svelte';
|
|
16
|
-
import MenuIconDefault from '../../../icons/MenuIcon.svelte';
|
|
17
|
-
import SearchIconDefault from '../../../icons/SearchIcon.svelte';
|
|
18
|
-
import SendIconDefault from '../../../icons/SendIcon.svelte';
|
|
19
|
-
import SettingsIconDefault from '../../../icons/SettingsIcon.svelte';
|
|
20
|
-
import StarIconDefault from '../../../icons/StarIcon.svelte';
|
|
21
|
-
import WarningTriangleIconDefault from '../../../icons/WarningTriangleIcon.svelte';
|
|
22
|
-
import CircleHelpIconDefault from '../../../icons/CircleHelpIcon.svelte';
|
|
23
|
-
import A2UINode from './A2UINode.svelte';
|
|
24
5
|
import type { A2uiValidationIssue } from './a2ui.types';
|
|
6
|
+
import { basicA2uiCatalog } from './a2ui-basic-catalog';
|
|
7
|
+
import type { A2uiCatalog } from './a2ui-catalog';
|
|
25
8
|
import { deleteAtPointer, resolveDynamic, setAtPointer } from './a2ui-data';
|
|
26
9
|
import {
|
|
27
10
|
type A2uiProcessor,
|
|
@@ -43,6 +26,8 @@
|
|
|
43
26
|
unsupportedLabel = 'Unsupported component',
|
|
44
27
|
blockedImageLabel = 'Image blocked',
|
|
45
28
|
pendingLabel = 'Loading UI',
|
|
29
|
+
catalogs: catalogsProp,
|
|
30
|
+
dataSchema: dataSchemaProp,
|
|
46
31
|
class: className,
|
|
47
32
|
unstyled: unstyledProp = false,
|
|
48
33
|
slotClasses: slotClassesProp = {},
|
|
@@ -53,29 +38,30 @@
|
|
|
53
38
|
const blocksConfig = getBlocksConfig();
|
|
54
39
|
const unstyled = $derived(unstyledProp || blocksConfig?.unstyled || false);
|
|
55
40
|
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
41
|
+
// Effective catalogs: the Basic catalog is always available and FIRST (the
|
|
42
|
+
// default/fallback), consumer catalogs follow. Resolved ONCE at init —
|
|
43
|
+
// `createIcons()` reads the IconProvider context, so it must run during
|
|
44
|
+
// component init and cannot be reactive. Keep `catalogs` referentially stable.
|
|
45
|
+
// `untrack` captures the initial prop value without a reactive subscription
|
|
46
|
+
// (the init-only capture is deliberate — silences `state_referenced_locally`).
|
|
47
|
+
const catalogs: readonly A2uiCatalog[] = [
|
|
48
|
+
basicA2uiCatalog,
|
|
49
|
+
...(untrack(() => catalogsProp) ?? [])
|
|
50
|
+
];
|
|
51
|
+
const catalogById = new Map<string, A2uiCatalog>();
|
|
52
|
+
const iconsByCatalog = new Map<string, ReturnType<A2uiCatalog['createIcons']>>();
|
|
53
|
+
for (const catalog of catalogs) {
|
|
54
|
+
if (catalogById.has(catalog.catalogId)) continue;
|
|
55
|
+
catalogById.set(catalog.catalogId, catalog);
|
|
56
|
+
for (const alias of catalog.catalogIdAliases ?? []) {
|
|
57
|
+
if (!catalogById.has(alias)) catalogById.set(alias, catalog);
|
|
58
|
+
}
|
|
59
|
+
iconsByCatalog.set(catalog.catalogId, catalog.createIcons());
|
|
60
|
+
}
|
|
61
|
+
const defaultCatalog = catalogs[0];
|
|
62
|
+
const defaultIcons = iconsByCatalog.get(defaultCatalog.catalogId) ?? defaultCatalog.createIcons();
|
|
63
|
+
// Init-fixed like the catalogs (validation config, not reactive UI state).
|
|
64
|
+
const dataSchema = untrack(() => dataSchemaProp);
|
|
79
65
|
|
|
80
66
|
// ── Processor (plain, non-reactive) + version counter ────────────────────
|
|
81
67
|
// The processor mutates plain Maps in place (the streaming-markdown engine
|
|
@@ -83,7 +69,7 @@
|
|
|
83
69
|
// render tree derives from it, so reading it is a real subscription (unlike a
|
|
84
70
|
// `void messages` proxy read). `processor`/`consumed` are plain lets: their
|
|
85
71
|
// reassignment must NOT itself trigger reactivity — the bump does.
|
|
86
|
-
let processor: A2uiProcessor = createA2uiProcessor();
|
|
72
|
+
let processor: A2uiProcessor = createA2uiProcessor({ catalogs, dataSchema });
|
|
87
73
|
let consumed: unknown[] = [];
|
|
88
74
|
let normalizeIssue = $state<A2uiValidationIssue | undefined>(undefined);
|
|
89
75
|
let version = $state(0);
|
|
@@ -110,7 +96,7 @@
|
|
|
110
96
|
const prefixMatches =
|
|
111
97
|
envelopes.length >= consumed.length && consumed.every((env, i) => env === envelopes[i]);
|
|
112
98
|
if (!prefixMatches) {
|
|
113
|
-
processor = createA2uiProcessor();
|
|
99
|
+
processor = createA2uiProcessor({ catalogs, dataSchema });
|
|
114
100
|
consumed = [];
|
|
115
101
|
generation++;
|
|
116
102
|
}
|
|
@@ -195,6 +181,11 @@
|
|
|
195
181
|
allIssues.push(graphIssue);
|
|
196
182
|
}
|
|
197
183
|
const root = buildRenderTree(surface);
|
|
184
|
+
// Resolve the surface's catalog to its Svelte wiring: which dispatcher
|
|
185
|
+
// renders it and which icon set it draws from. surface.catalog is one of
|
|
186
|
+
// `catalogs`, so both lookups always hit; the `??` are belt-and-braces.
|
|
187
|
+
const surfaceCatalog = catalogById.get(surface.catalog.catalogId) ?? defaultCatalog;
|
|
188
|
+
const surfaceIcons = iconsByCatalog.get(surfaceCatalog.catalogId) ?? defaultIcons;
|
|
198
189
|
const context: A2uiRenderContext = {
|
|
199
190
|
classes: currentClasses,
|
|
200
191
|
urlPolicy,
|
|
@@ -202,11 +193,14 @@
|
|
|
202
193
|
inline: false,
|
|
203
194
|
surfaceId: surface.surfaceId,
|
|
204
195
|
onAction,
|
|
196
|
+
// Read lazily: the click, not the render, decides what the agent sees.
|
|
197
|
+
actionDataModel: surface.sendDataModel ? () => surface.dataModel : undefined,
|
|
205
198
|
resolve: (value, scope) => resolveDynamic(value, surface.dataModel, scope),
|
|
206
199
|
write: (pointer, value) => writeBinding(surface.surfaceId, pointer, value),
|
|
207
200
|
remove: (pointer) => removeBinding(surface.surfaceId, pointer),
|
|
208
|
-
icons,
|
|
209
|
-
fallbackIcon,
|
|
201
|
+
icons: surfaceIcons.icons,
|
|
202
|
+
fallbackIcon: surfaceIcons.fallbackIcon,
|
|
203
|
+
nodeComponent: surfaceCatalog.Node,
|
|
210
204
|
labels: {
|
|
211
205
|
unsupported: unsupportedLabel,
|
|
212
206
|
blockedImage: blockedImageLabel,
|
|
@@ -241,12 +235,15 @@
|
|
|
241
235
|
|
|
242
236
|
<!--
|
|
243
237
|
The render tree recurses through this self-referencing snippet rather than a
|
|
244
|
-
self-import inside
|
|
245
|
-
down as `renderChild` gives clean recursion without the
|
|
246
|
-
resolution that self-imports trip in svelte-check.
|
|
238
|
+
self-import inside the node dispatcher: a snippet that renders the dispatcher
|
|
239
|
+
and passes ITSELF down as `renderChild` gives clean recursion without the
|
|
240
|
+
circular component-type resolution that self-imports trip in svelte-check. The
|
|
241
|
+
dispatcher is the surface catalog's own node component (Basic → A2UINode; a
|
|
242
|
+
custom catalog → its own), read off the render context per surface.
|
|
247
243
|
-->
|
|
248
244
|
{#snippet renderNode(node: A2uiRenderNode, context: A2uiRenderContext)}
|
|
249
|
-
|
|
245
|
+
{@const Node = context.nodeComponent}
|
|
246
|
+
<Node {node} {context} renderChild={renderNode} />
|
|
250
247
|
{/snippet}
|
|
251
248
|
|
|
252
249
|
<!--
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The v0.9.1 `basic`-subset catalog, fully wired: the pure `basicA2uiCatalogSpec`
|
|
3
|
+
* plus its Svelte half — the `A2UINode` dispatcher and the `createIcons` factory.
|
|
4
|
+
*
|
|
5
|
+
* The icon map lives here (not at A2UIView module scope) so it is built ONCE per
|
|
6
|
+
* catalog and threaded through the render context, and so a custom catalog can
|
|
7
|
+
* ship its own icon set from its own module without dragging Basic's icons into
|
|
8
|
+
* every bundle. `createIcons` is a FACTORY, not a constant: `resolveIcon` reads
|
|
9
|
+
* the IconProvider context, so it must run during component initialisation —
|
|
10
|
+
* A2UIView calls `catalog.createIcons()` from its script top level.
|
|
11
|
+
*
|
|
12
|
+
* This module imports `.svelte` components, so — unlike `a2ui-catalog.ts` — it is
|
|
13
|
+
* NOT server-importable. Keep the Svelte-free spec in `a2ui-catalog.ts`.
|
|
14
|
+
*/
|
|
15
|
+
import { type A2uiCatalog } from './a2ui-catalog.js';
|
|
16
|
+
/** The renderable Basic catalog: spec + `A2UINode` dispatcher + icon factory. */
|
|
17
|
+
export declare const basicA2uiCatalog: A2uiCatalog;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The v0.9.1 `basic`-subset catalog, fully wired: the pure `basicA2uiCatalogSpec`
|
|
3
|
+
* plus its Svelte half — the `A2UINode` dispatcher and the `createIcons` factory.
|
|
4
|
+
*
|
|
5
|
+
* The icon map lives here (not at A2UIView module scope) so it is built ONCE per
|
|
6
|
+
* catalog and threaded through the render context, and so a custom catalog can
|
|
7
|
+
* ship its own icon set from its own module without dragging Basic's icons into
|
|
8
|
+
* every bundle. `createIcons` is a FACTORY, not a constant: `resolveIcon` reads
|
|
9
|
+
* the IconProvider context, so it must run during component initialisation —
|
|
10
|
+
* A2UIView calls `catalog.createIcons()` from its script top level.
|
|
11
|
+
*
|
|
12
|
+
* This module imports `.svelte` components, so — unlike `a2ui-catalog.ts` — it is
|
|
13
|
+
* NOT server-importable. Keep the Svelte-free spec in `a2ui-catalog.ts`.
|
|
14
|
+
*/
|
|
15
|
+
import { resolveIcon } from '../../../icons/index.js';
|
|
16
|
+
import ArrowLeftIconDefault from '../../../icons/ArrowLeftIcon.svelte';
|
|
17
|
+
import CheckIconDefault from '../../../icons/CheckIcon.svelte';
|
|
18
|
+
import CircleHelpIconDefault from '../../../icons/CircleHelpIcon.svelte';
|
|
19
|
+
import CloseIconDefault from '../../../icons/CloseIcon.svelte';
|
|
20
|
+
import DangerCircleIconDefault from '../../../icons/DangerCircleIcon.svelte';
|
|
21
|
+
import EditIconDefault from '../../../icons/EditIcon.svelte';
|
|
22
|
+
import HomeIconDefault from '../../../icons/HomeIcon.svelte';
|
|
23
|
+
import InfoCircleIconDefault from '../../../icons/InfoCircleIcon.svelte';
|
|
24
|
+
import MailIconDefault from '../../../icons/MailIcon.svelte';
|
|
25
|
+
import MenuIconDefault from '../../../icons/MenuIcon.svelte';
|
|
26
|
+
import PlusIconDefault from '../../../icons/PlusIcon.svelte';
|
|
27
|
+
import SearchIconDefault from '../../../icons/SearchIcon.svelte';
|
|
28
|
+
import SendIconDefault from '../../../icons/SendIcon.svelte';
|
|
29
|
+
import SettingsIconDefault from '../../../icons/SettingsIcon.svelte';
|
|
30
|
+
import StarIconDefault from '../../../icons/StarIcon.svelte';
|
|
31
|
+
import TrashIconDefault from '../../../icons/TrashIcon.svelte';
|
|
32
|
+
import WarningTriangleIconDefault from '../../../icons/WarningTriangleIcon.svelte';
|
|
33
|
+
import A2UINode from './A2UINode.svelte';
|
|
34
|
+
import { basicA2uiCatalogSpec } from './a2ui-catalog.js';
|
|
35
|
+
/**
|
|
36
|
+
* Build the Basic catalog's A2UI icon-enum name → resolved Urbicon icon map.
|
|
37
|
+
* Resolved via DIRECT imports (tree-shakeable — never `getIcon()`); an
|
|
38
|
+
* IconProvider override still wins, the direct import is the fallback. Must run
|
|
39
|
+
* during component init (`resolveIcon` reads context).
|
|
40
|
+
*/
|
|
41
|
+
function createBasicIcons() {
|
|
42
|
+
return {
|
|
43
|
+
icons: {
|
|
44
|
+
add: resolveIcon('plus', PlusIconDefault),
|
|
45
|
+
arrowBack: resolveIcon('arrowLeft', ArrowLeftIconDefault),
|
|
46
|
+
check: resolveIcon('check', CheckIconDefault),
|
|
47
|
+
close: resolveIcon('close', CloseIconDefault),
|
|
48
|
+
delete: resolveIcon('trash', TrashIconDefault),
|
|
49
|
+
edit: resolveIcon('edit', EditIconDefault),
|
|
50
|
+
error: resolveIcon('danger', DangerCircleIconDefault),
|
|
51
|
+
home: resolveIcon('home', HomeIconDefault),
|
|
52
|
+
info: resolveIcon('info', InfoCircleIconDefault),
|
|
53
|
+
mail: resolveIcon('mail', MailIconDefault),
|
|
54
|
+
menu: resolveIcon('menu', MenuIconDefault),
|
|
55
|
+
search: resolveIcon('search', SearchIconDefault),
|
|
56
|
+
send: resolveIcon('send', SendIconDefault),
|
|
57
|
+
settings: resolveIcon('settings', SettingsIconDefault),
|
|
58
|
+
star: resolveIcon('star', StarIconDefault),
|
|
59
|
+
warning: resolveIcon('warning', WarningTriangleIconDefault)
|
|
60
|
+
},
|
|
61
|
+
fallbackIcon: resolveIcon('circleHelp', CircleHelpIconDefault)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** The renderable Basic catalog: spec + `A2UINode` dispatcher + icon factory. */
|
|
65
|
+
export const basicA2uiCatalog = {
|
|
66
|
+
...basicA2uiCatalogSpec,
|
|
67
|
+
Node: A2UINode,
|
|
68
|
+
createIcons: createBasicIcons
|
|
69
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catalog abstraction for the A2UI engine. A *catalog* bundles everything the
|
|
3
|
+
* pure-TS engine (validate/render/prompt) needs to be catalog-agnostic: the
|
|
4
|
+
* component registry, the icon-name set, the unsupported/ignored lists, the
|
|
5
|
+
* flex-container set (which parents honour a child `weight`), and per-component
|
|
6
|
+
* post-validation checks. This is the seam that lets a second, Urbicon-native
|
|
7
|
+
* catalog (`urbicon/…`) sit alongside the v0.9.1 `basic` catalog without
|
|
8
|
+
* touching the engine.
|
|
9
|
+
*
|
|
10
|
+
* Two layers:
|
|
11
|
+
* - `A2uiCatalogSpec` — pure TS, Svelte-free, server-importable (a server
|
|
12
|
+
* building the system prompt or validating a payload has no DOM). This is
|
|
13
|
+
* what the engine threads.
|
|
14
|
+
* - `A2uiCatalog` — adds the Svelte wiring (`Node` dispatcher, `createIcons`
|
|
15
|
+
* factory) via TYPE-ONLY imports, so importing this module still pulls no
|
|
16
|
+
* Svelte runtime. The `.svelte`-bearing catalog VALUE is assembled in a
|
|
17
|
+
* separate module (`a2ui-basic-catalog.ts`) so the pure spec stays isolated.
|
|
18
|
+
*
|
|
19
|
+
* The spec-level prop/component types live in `a2ui-registry.ts` (the Basic
|
|
20
|
+
* data); they are re-exported here so a downstream catalog imports one place.
|
|
21
|
+
*/
|
|
22
|
+
import type { Component } from 'svelte';
|
|
23
|
+
import type { IconComponent } from '../../../icons/index.js';
|
|
24
|
+
import { type A2uiValidationIssue } from './a2ui.types.js';
|
|
25
|
+
import { type A2uiComponentSpec, type A2uiPropKind, type A2uiPropSpec } from './a2ui-registry.js';
|
|
26
|
+
import type { A2uiNodeProps } from './a2ui-render.js';
|
|
27
|
+
export type { A2uiComponentSpec, A2uiPropKind, A2uiPropSpec };
|
|
28
|
+
/**
|
|
29
|
+
* Context passed to a per-component post-validation check. `props` is the
|
|
30
|
+
* already-validated (registry-declared, stored) prop map for the component;
|
|
31
|
+
* `base` is its payload path (`/messages/<i>/updateComponents/components/<j>`).
|
|
32
|
+
*/
|
|
33
|
+
export interface A2uiComponentCheckContext {
|
|
34
|
+
id: string;
|
|
35
|
+
props: ReadonlyMap<string, unknown>;
|
|
36
|
+
surfaceId: string;
|
|
37
|
+
base: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A per-component check the engine runs after generic prop validation, keyed by
|
|
41
|
+
* component name. Returns any extra issues (e.g. ChoicePicker chips-fallback,
|
|
42
|
+
* DateTimeInput missing-mode). Kept as catalog DATA rather than hardcoded
|
|
43
|
+
* branches so each catalog owns its own semantic warnings.
|
|
44
|
+
*/
|
|
45
|
+
export type A2uiComponentCheck = (ctx: A2uiComponentCheckContext) => A2uiValidationIssue[];
|
|
46
|
+
/**
|
|
47
|
+
* The Svelte-free half of a catalog — everything the engine (validate / render
|
|
48
|
+
* graph / prompt) needs. Structurally satisfied by the richer `A2uiCatalog`.
|
|
49
|
+
*/
|
|
50
|
+
export interface A2uiCatalogSpec {
|
|
51
|
+
/** Catalog id advertised in `createSurface` and matched against incoming surfaces. */
|
|
52
|
+
catalogId: string;
|
|
53
|
+
/** Additional ids that resolve to this catalog (version/back-compat aliases). */
|
|
54
|
+
catalogIdAliases?: readonly string[];
|
|
55
|
+
/** Component name → spec (the single source of truth for validation AND the prompt). */
|
|
56
|
+
registry: Readonly<Record<string, A2uiComponentSpec>>;
|
|
57
|
+
/** Mapped `Icon.name` values; an unmapped name degrades to a fallback glyph. */
|
|
58
|
+
iconNames: readonly string[];
|
|
59
|
+
/** Real catalog components deliberately not rendered (error chip, not "unknown"). */
|
|
60
|
+
unsupportedComponents: ReadonlySet<string>;
|
|
61
|
+
/** Props recognised anywhere but dropped before render (validation warning only). */
|
|
62
|
+
ignoredProps: ReadonlySet<string>;
|
|
63
|
+
/** Component names whose DIRECT children may carry a `weight` (flex-grow). */
|
|
64
|
+
flexContainers: ReadonlySet<string>;
|
|
65
|
+
/** Optional per-component post-validation checks, keyed by component name. */
|
|
66
|
+
componentChecks?: Readonly<Record<string, A2uiComponentCheck>>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A full, renderable catalog — the spec plus its Svelte wiring. `Node` is the
|
|
70
|
+
* recursive dispatcher A2UIView renders per surface; `createIcons` builds the
|
|
71
|
+
* icon map (must run during component init — `resolveIcon` reads the
|
|
72
|
+
* IconProvider context). The `Component`/`IconComponent` references are
|
|
73
|
+
* TYPE-ONLY, so this module stays runtime-Svelte-free.
|
|
74
|
+
*/
|
|
75
|
+
export interface A2uiCatalog extends A2uiCatalogSpec {
|
|
76
|
+
Node: Component<A2uiNodeProps>;
|
|
77
|
+
createIcons: () => {
|
|
78
|
+
icons: Readonly<Record<string, IconComponent>>;
|
|
79
|
+
fallbackIcon: IconComponent;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Resolve a `createSurface.catalogId` to a configured catalog: exact id match
|
|
84
|
+
* first, then an alias match. No prefix/semver matching (an id must round-trip
|
|
85
|
+
* unchanged). Returns `undefined` when nothing matches — the caller falls back
|
|
86
|
+
* to the default catalog.
|
|
87
|
+
*/
|
|
88
|
+
export declare function resolveCatalog<T extends A2uiCatalogSpec>(catalogs: readonly T[], catalogId: string): T | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* The v0.9.1 `basic`-subset catalog, spec half. The Basic-specific ChoicePicker
|
|
91
|
+
* and DateTimeInput warnings live here as `componentChecks` (previously
|
|
92
|
+
* hardcoded in the validator) so the engine carries no catalog-specific
|
|
93
|
+
* branches. Message strings are byte-identical to the pre-refactor validator.
|
|
94
|
+
*/
|
|
95
|
+
export declare const basicA2uiCatalogSpec: A2uiCatalogSpec;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catalog abstraction for the A2UI engine. A *catalog* bundles everything the
|
|
3
|
+
* pure-TS engine (validate/render/prompt) needs to be catalog-agnostic: the
|
|
4
|
+
* component registry, the icon-name set, the unsupported/ignored lists, the
|
|
5
|
+
* flex-container set (which parents honour a child `weight`), and per-component
|
|
6
|
+
* post-validation checks. This is the seam that lets a second, Urbicon-native
|
|
7
|
+
* catalog (`urbicon/…`) sit alongside the v0.9.1 `basic` catalog without
|
|
8
|
+
* touching the engine.
|
|
9
|
+
*
|
|
10
|
+
* Two layers:
|
|
11
|
+
* - `A2uiCatalogSpec` — pure TS, Svelte-free, server-importable (a server
|
|
12
|
+
* building the system prompt or validating a payload has no DOM). This is
|
|
13
|
+
* what the engine threads.
|
|
14
|
+
* - `A2uiCatalog` — adds the Svelte wiring (`Node` dispatcher, `createIcons`
|
|
15
|
+
* factory) via TYPE-ONLY imports, so importing this module still pulls no
|
|
16
|
+
* Svelte runtime. The `.svelte`-bearing catalog VALUE is assembled in a
|
|
17
|
+
* separate module (`a2ui-basic-catalog.ts`) so the pure spec stays isolated.
|
|
18
|
+
*
|
|
19
|
+
* The spec-level prop/component types live in `a2ui-registry.ts` (the Basic
|
|
20
|
+
* data); they are re-exported here so a downstream catalog imports one place.
|
|
21
|
+
*/
|
|
22
|
+
import { A2UI_ISSUE_CODES } from './a2ui.types.js';
|
|
23
|
+
import { A2UI_CATALOG_ID, A2UI_ICON_NAMES, A2UI_IGNORED_PROPS, A2UI_REGISTRY, UNSUPPORTED_A2UI_COMPONENTS } from './a2ui-registry.js';
|
|
24
|
+
/**
|
|
25
|
+
* Resolve a `createSurface.catalogId` to a configured catalog: exact id match
|
|
26
|
+
* first, then an alias match. No prefix/semver matching (an id must round-trip
|
|
27
|
+
* unchanged). Returns `undefined` when nothing matches — the caller falls back
|
|
28
|
+
* to the default catalog.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveCatalog(catalogs, catalogId) {
|
|
31
|
+
for (const catalog of catalogs) {
|
|
32
|
+
if (catalog.catalogId === catalogId)
|
|
33
|
+
return catalog;
|
|
34
|
+
}
|
|
35
|
+
for (const catalog of catalogs) {
|
|
36
|
+
if (catalog.catalogIdAliases?.includes(catalogId))
|
|
37
|
+
return catalog;
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The v0.9.1 `basic`-subset catalog, spec half. The Basic-specific ChoicePicker
|
|
43
|
+
* and DateTimeInput warnings live here as `componentChecks` (previously
|
|
44
|
+
* hardcoded in the validator) so the engine carries no catalog-specific
|
|
45
|
+
* branches. Message strings are byte-identical to the pre-refactor validator.
|
|
46
|
+
*/
|
|
47
|
+
export const basicA2uiCatalogSpec = {
|
|
48
|
+
catalogId: A2UI_CATALOG_ID,
|
|
49
|
+
registry: A2UI_REGISTRY,
|
|
50
|
+
iconNames: A2UI_ICON_NAMES,
|
|
51
|
+
unsupportedComponents: UNSUPPORTED_A2UI_COMPONENTS,
|
|
52
|
+
ignoredProps: A2UI_IGNORED_PROPS,
|
|
53
|
+
flexContainers: new Set(['Row', 'Column']),
|
|
54
|
+
componentChecks: {
|
|
55
|
+
ChoicePicker: ({ id, props, surfaceId, base }) => {
|
|
56
|
+
if (props.get('displayStyle') === 'chips' || props.get('filterable') === true) {
|
|
57
|
+
return [
|
|
58
|
+
{
|
|
59
|
+
severity: 'warning',
|
|
60
|
+
code: A2UI_ISSUE_CODES.CHOICEPICKER_FALLBACK,
|
|
61
|
+
message: `ChoicePicker "${id}" chips/filterable are rendered with a fallback`,
|
|
62
|
+
surfaceId,
|
|
63
|
+
path: base
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
return [];
|
|
68
|
+
},
|
|
69
|
+
DateTimeInput: ({ id, props, surfaceId, base }) => {
|
|
70
|
+
// Both flags default to false in the spec — a DateTimeInput without either
|
|
71
|
+
// would have no input UI at all. We read tolerantly (render a date input)
|
|
72
|
+
// and report loudly so the agent fixes the payload.
|
|
73
|
+
if (props.get('enableDate') !== true && props.get('enableTime') !== true) {
|
|
74
|
+
return [
|
|
75
|
+
{
|
|
76
|
+
severity: 'warning',
|
|
77
|
+
code: A2UI_ISSUE_CODES.DATETIME_NO_MODE,
|
|
78
|
+
message: `DateTimeInput "${id}" sets neither enableDate nor enableTime; rendering a date input`,
|
|
79
|
+
surfaceId,
|
|
80
|
+
path: base
|
|
81
|
+
}
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers shared by the catalog node dispatchers (`A2UINode`,
|
|
3
|
+
* `urbicon/UrbiconA2UINode`). No Svelte — just the payload-shaping logic both
|
|
4
|
+
* renderers need identically. The reactive plumbing (the `$derived`/`$state`
|
|
5
|
+
* two-way-binding wiring) is deliberately NOT extracted: it is duplicated per
|
|
6
|
+
* dispatcher so each stays a self-contained, readable component.
|
|
7
|
+
*
|
|
8
|
+
* `dedupeOptions` is load-bearing for the "never throw" contract: EVERY
|
|
9
|
+
* payload-driven keyed `{#each}` over option values must dedupe first, or two
|
|
10
|
+
* equal keys throw Svelte's `each_key_duplicate` — a hard render crash on an
|
|
11
|
+
* untrusted payload. The validator only WARNS on duplicates; the renderer must
|
|
12
|
+
* drop them.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Coerce an unknown resolved value to display text: strings pass through, finite
|
|
16
|
+
* numbers stringify, everything else (null/undefined/object/NaN) becomes `''`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function coerceText(value: unknown): string;
|
|
19
|
+
/**
|
|
20
|
+
* Normalise a raw options array into a deduped `{ value, label }` list (first
|
|
21
|
+
* occurrence of each value wins). `resolveLabel` turns a raw label value (a
|
|
22
|
+
* literal or a `{ path }` binding) into display text — the caller injects its
|
|
23
|
+
* data-binding resolver. Malformed options (non-object, non-string value) are
|
|
24
|
+
* skipped. Dedup is mandatory: the rendered list is keyed on `value`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function dedupeOptions(rawOptions: unknown, resolveLabel: (rawLabel: unknown) => string): Array<{
|
|
27
|
+
value: string;
|
|
28
|
+
label: string;
|
|
29
|
+
}>;
|
|
30
|
+
/** Keep only the leading `HH:MM(:SS)?` of a time string; `''` if there is none. */
|
|
31
|
+
export declare function normalizeTimePart(value: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Split one ISO 8601 date/time literal into its date and time parts, timezone-
|
|
34
|
+
* naively (a trailing offset/`Z` is dropped, never re-attached — timezone
|
|
35
|
+
* semantics belong to the agent, not a UI renderer without timezone context).
|
|
36
|
+
*/
|
|
37
|
+
export declare function splitDateTime(value: string): {
|
|
38
|
+
date: string;
|
|
39
|
+
time: string;
|
|
40
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers shared by the catalog node dispatchers (`A2UINode`,
|
|
3
|
+
* `urbicon/UrbiconA2UINode`). No Svelte — just the payload-shaping logic both
|
|
4
|
+
* renderers need identically. The reactive plumbing (the `$derived`/`$state`
|
|
5
|
+
* two-way-binding wiring) is deliberately NOT extracted: it is duplicated per
|
|
6
|
+
* dispatcher so each stays a self-contained, readable component.
|
|
7
|
+
*
|
|
8
|
+
* `dedupeOptions` is load-bearing for the "never throw" contract: EVERY
|
|
9
|
+
* payload-driven keyed `{#each}` over option values must dedupe first, or two
|
|
10
|
+
* equal keys throw Svelte's `each_key_duplicate` — a hard render crash on an
|
|
11
|
+
* untrusted payload. The validator only WARNS on duplicates; the renderer must
|
|
12
|
+
* drop them.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Coerce an unknown resolved value to display text: strings pass through, finite
|
|
16
|
+
* numbers stringify, everything else (null/undefined/object/NaN) becomes `''`.
|
|
17
|
+
*/
|
|
18
|
+
export function coerceText(value) {
|
|
19
|
+
if (typeof value === 'string')
|
|
20
|
+
return value;
|
|
21
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
22
|
+
return String(value);
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Normalise a raw options array into a deduped `{ value, label }` list (first
|
|
27
|
+
* occurrence of each value wins). `resolveLabel` turns a raw label value (a
|
|
28
|
+
* literal or a `{ path }` binding) into display text — the caller injects its
|
|
29
|
+
* data-binding resolver. Malformed options (non-object, non-string value) are
|
|
30
|
+
* skipped. Dedup is mandatory: the rendered list is keyed on `value`.
|
|
31
|
+
*/
|
|
32
|
+
export function dedupeOptions(rawOptions, resolveLabel) {
|
|
33
|
+
if (!Array.isArray(rawOptions))
|
|
34
|
+
return [];
|
|
35
|
+
const seen = new Set();
|
|
36
|
+
const out = [];
|
|
37
|
+
for (const option of rawOptions) {
|
|
38
|
+
if (option === null || typeof option !== 'object')
|
|
39
|
+
continue;
|
|
40
|
+
const value = option.value;
|
|
41
|
+
if (typeof value !== 'string' || seen.has(value))
|
|
42
|
+
continue;
|
|
43
|
+
seen.add(value);
|
|
44
|
+
out.push({ value, label: resolveLabel(option.label) });
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
/** Keep only the leading `HH:MM(:SS)?` of a time string; `''` if there is none. */
|
|
49
|
+
export function normalizeTimePart(value) {
|
|
50
|
+
const match = /^(\d{2}:\d{2}(?::\d{2})?)/.exec(value);
|
|
51
|
+
return match ? match[1] : '';
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Split one ISO 8601 date/time literal into its date and time parts, timezone-
|
|
55
|
+
* naively (a trailing offset/`Z` is dropped, never re-attached — timezone
|
|
56
|
+
* semantics belong to the agent, not a UI renderer without timezone context).
|
|
57
|
+
*/
|
|
58
|
+
export function splitDateTime(value) {
|
|
59
|
+
const trimmed = value.trim();
|
|
60
|
+
if (trimmed === '')
|
|
61
|
+
return { date: '', time: '' };
|
|
62
|
+
const tIndex = trimmed.indexOf('T');
|
|
63
|
+
if (tIndex !== -1) {
|
|
64
|
+
return {
|
|
65
|
+
date: trimmed.slice(0, tIndex),
|
|
66
|
+
time: normalizeTimePart(trimmed.slice(tIndex + 1))
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (trimmed.includes(':'))
|
|
70
|
+
return { date: '', time: normalizeTimePart(trimmed) };
|
|
71
|
+
return { date: trimmed, time: '' };
|
|
72
|
+
}
|