@threadplane/chat 0.0.57 → 0.0.59

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.
@@ -10,8 +10,8 @@ import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
10
10
  import { Router, NavigationEnd } from '@angular/router';
11
11
  import { filter, map, startWith } from 'rxjs/operators';
12
12
  import { toJSONSchema } from 'zod/v4';
13
- import { resolveDynamic, getByPointer, isPathRef, setByPointer, createA2uiMessageParser } from '@threadplane/a2ui';
14
- export { isLiteralBoolean, isLiteralNumber, isLiteralString, isPathRef } from '@threadplane/a2ui';
13
+ import { createA2uiFunctionRegistry, isPathRef, resolveDynamic, getByPointer, isFunctionCall, A2UI_WIRE_VERSION, deleteByPointer, setByPointer, createA2uiMessageParser, A2UI_BASIC_CATALOG_ID } from '@threadplane/a2ui';
14
+ export { A2UI_BASIC_CATALOG_ID, A2UI_MIME_TYPE, A2UI_WIRE_VERSION, isFunctionCall, isPathRef } from '@threadplane/a2ui';
15
15
  import { materialize as materialize$1, createPartialJsonParser } from '@cacheplane/partial-json';
16
16
  import { fromEvent, EMPTY } from 'rxjs';
17
17
 
@@ -9106,23 +9106,49 @@ function createClientToolsCoordinator(registry, options = {}) {
9106
9106
  };
9107
9107
  }
9108
9108
 
9109
- const RESERVED_PROP_KEYS = new Set(['child', 'children', 'action', 'tabItems', 'entryPointChild', 'contentChild']);
9110
- /** Pull the (single) component-type key + its props from a v1 ComponentDef wrapper. */
9111
- function unwrapComponentDef(def) {
9112
- const entries = Object.entries(def);
9113
- if (entries.length !== 1) {
9114
- return { type: 'Text', props: {} };
9115
- }
9116
- const [type, props] = entries[0];
9117
- return { type, props: (props ?? {}) };
9118
- }
9109
+ /** Shared standard-function registry (formatString, formatters, logic). */
9110
+ const A2UI_FUNCTIONS = createA2uiFunctionRegistry();
9111
+ /** Keys that are protocol structure (base fields + child/action wiring),
9112
+ * not renderable props. */
9113
+ const RESERVED_PROP_KEYS = new Set([
9114
+ 'id', 'component', 'catalogId', 'weight', 'accessibility', 'checks',
9115
+ 'child', 'children', 'action', 'tabs', 'trigger', 'content',
9116
+ ]);
9119
9117
  function resolveAction(action, surface, sourceComponentId) {
9120
- if (!action)
9118
+ if (!action || typeof action !== 'object')
9119
+ return undefined;
9120
+ if (!('event' in action)) {
9121
+ // Local client-side function action — routed to the surface component's
9122
+ // built-in `a2ui:localAction` handler (openUrl et al.).
9123
+ if ('functionCall' in action && action.functionCall
9124
+ && typeof action.functionCall.call === 'string') {
9125
+ return {
9126
+ click: {
9127
+ action: 'a2ui:localAction',
9128
+ params: {
9129
+ call: action.functionCall.call,
9130
+ args: action.functionCall.args ?? {},
9131
+ },
9132
+ },
9133
+ };
9134
+ }
9135
+ return undefined;
9136
+ }
9137
+ const event = action.event;
9138
+ if (!event || typeof event.name !== 'string')
9121
9139
  return undefined;
9122
9140
  const resolvedContext = {};
9123
- if (Array.isArray(action.context)) {
9124
- for (const entry of action.context) {
9125
- resolvedContext[entry.key] = resolveDynamic(entry.value, surface.dataModel);
9141
+ if (event.context && typeof event.context === 'object') {
9142
+ for (const [key, value] of Object.entries(event.context)) {
9143
+ if (isPathRef(value)) {
9144
+ // Live marker: the surface component substitutes the CURRENT value
9145
+ // (user edits included) from its state store at dispatch time —
9146
+ // build-time resolution would freeze the agent-seeded snapshot.
9147
+ resolvedContext[key] = { $bindState: value.path };
9148
+ }
9149
+ else {
9150
+ resolvedContext[key] = resolveDynamic(value, surface.dataModel, undefined, A2UI_FUNCTIONS);
9151
+ }
9126
9152
  }
9127
9153
  }
9128
9154
  return {
@@ -9131,7 +9157,7 @@ function resolveAction(action, surface, sourceComponentId) {
9131
9157
  params: {
9132
9158
  surfaceId: surface.surfaceId,
9133
9159
  sourceComponentId,
9134
- name: action.name,
9160
+ name: event.name,
9135
9161
  context: resolvedContext,
9136
9162
  },
9137
9163
  },
@@ -9140,16 +9166,15 @@ function resolveAction(action, surface, sourceComponentId) {
9140
9166
  function childrenToList(children, surface) {
9141
9167
  if (!children)
9142
9168
  return undefined;
9143
- if ('explicitList' in children) {
9144
- return { ids: children.explicitList };
9169
+ if (Array.isArray(children)) {
9170
+ return { ids: children };
9145
9171
  }
9146
- if ('template' in children) {
9147
- const t = children.template;
9148
- const arr = getByPointer(surface.dataModel, t.dataBinding);
9172
+ if (typeof children === 'object' && 'componentId' in children && 'path' in children) {
9173
+ const arr = getByPointer(surface.dataModel, children.path);
9149
9174
  if (!Array.isArray(arr))
9150
9175
  return { ids: [] };
9151
- const ids = arr.map((_, i) => `${t.componentId}__${i}`);
9152
- return { ids, templateExpand: { componentId: t.componentId, arrPath: t.dataBinding, arr } };
9176
+ const ids = arr.map((_, i) => `${children.componentId}__${i}`);
9177
+ return { ids, templateExpand: { componentId: children.componentId, arrPath: children.path, arr } };
9153
9178
  }
9154
9179
  return undefined;
9155
9180
  }
@@ -9158,7 +9183,8 @@ function surfaceToSpec(surface) {
9158
9183
  return null;
9159
9184
  const elements = {};
9160
9185
  for (const [id, comp] of surface.components) {
9161
- const { type, props: rawProps } = unwrapComponentDef(comp.component);
9186
+ const type = typeof comp.component === 'string' ? comp.component : 'Text';
9187
+ const rawProps = comp;
9162
9188
  const resolvedProps = {};
9163
9189
  const bindings = {};
9164
9190
  for (const [key, value] of Object.entries(rawProps)) {
@@ -9177,62 +9203,73 @@ function surfaceToSpec(surface) {
9177
9203
  bindings[key] = path;
9178
9204
  resolvedProps[key] = { $bindState: path };
9179
9205
  }
9206
+ else if (isFunctionCall(value)) {
9207
+ const resolved = resolveDynamic(value, surface.dataModel, undefined, A2UI_FUNCTIONS);
9208
+ if (resolved !== undefined)
9209
+ resolvedProps[key] = resolved;
9210
+ }
9180
9211
  else {
9181
- resolvedProps[key] = resolveDynamic(value, surface.dataModel);
9212
+ resolvedProps[key] = resolveDynamic(value, surface.dataModel, undefined, A2UI_FUNCTIONS);
9182
9213
  }
9183
9214
  }
9184
9215
  if (Object.keys(bindings).length > 0) {
9185
9216
  resolvedProps['_bindings'] = bindings;
9186
9217
  }
9218
+ // Checkable components surface their live validation message through a
9219
+ // reserved store path the surface component writes on failed submits.
9220
+ if (componentHasChecks(rawProps)) {
9221
+ resolvedProps['errorText'] = { $bindState: `/_a2uiChecks/${id}` };
9222
+ }
9187
9223
  const action = rawProps.action;
9188
9224
  const on = resolveAction(action, surface, id);
9189
- // Map children — handle Card single child / Button single child / Modal entryPointChild+contentChild / Tabs tabItems
9225
+ // Map children — Card/Button single child, Modal trigger+content, Tabs tabs[].
9190
9226
  let children;
9191
- if (type === 'Card' && typeof rawProps.child === 'string') {
9192
- children = [rawProps.child];
9193
- }
9194
- else if (type === 'Button' && typeof rawProps.child === 'string') {
9195
- children = [rawProps.child];
9227
+ if ((type === 'Card' || type === 'Button') && typeof rawProps['child'] === 'string') {
9228
+ children = [rawProps['child']];
9196
9229
  }
9197
9230
  else if (type === 'Modal') {
9198
- const m = rawProps;
9199
9231
  const ids = [];
9200
- if (m.entryPointChild)
9201
- ids.push(m.entryPointChild);
9202
- if (m.contentChild)
9203
- ids.push(m.contentChild);
9232
+ if (typeof rawProps['trigger'] === 'string')
9233
+ ids.push(rawProps['trigger']);
9234
+ if (typeof rawProps['content'] === 'string')
9235
+ ids.push(rawProps['content']);
9204
9236
  children = ids;
9205
9237
  }
9206
9238
  else if (type === 'Tabs') {
9207
- const items = rawProps.tabItems ?? [];
9239
+ const items = rawProps.tabs ?? [];
9208
9240
  children = items.map(t => t.child);
9209
9241
  // Resolve tab titles and pass them as a plain string array for the Tabs component's tab bar.
9210
- resolvedProps['tabTitles'] = items.map(t => t.title !== undefined ? String(resolveDynamic(t.title, surface.dataModel)) : '');
9242
+ resolvedProps['tabTitles'] = items.map(t => t.title !== undefined
9243
+ ? String(resolveDynamic(t.title, surface.dataModel, undefined, A2UI_FUNCTIONS))
9244
+ : '');
9211
9245
  }
9212
- else if (type === 'MultipleChoice') {
9246
+ else if (type === 'ChoicePicker') {
9213
9247
  // Resolve options[*].label (DynamicString) so the component receives plain strings.
9214
9248
  const opts = rawProps.options ?? [];
9215
9249
  resolvedProps['options'] = opts.map(o => ({
9216
- label: o.label !== undefined ? String(resolveDynamic(o.label, surface.dataModel)) : '',
9250
+ label: o.label !== undefined
9251
+ ? String(resolveDynamic(o.label, surface.dataModel, undefined, A2UI_FUNCTIONS))
9252
+ : '',
9217
9253
  value: o.value,
9218
9254
  }));
9219
9255
  }
9220
9256
  else {
9221
- const childInfo = childrenToList(rawProps.children, surface);
9257
+ const childInfo = childrenToList(rawProps['children'], surface);
9222
9258
  if (childInfo) {
9223
9259
  children = childInfo.ids;
9224
9260
  if (childInfo.templateExpand) {
9225
9261
  const t = childInfo.templateExpand;
9226
9262
  const templateComp = surface.components.get(t.componentId);
9227
9263
  if (templateComp) {
9228
- const { type: tType, props: tRaw } = unwrapComponentDef(templateComp.component);
9264
+ const tType = typeof templateComp.component === 'string' ? templateComp.component : 'Text';
9265
+ const tRaw = templateComp;
9229
9266
  for (let i = 0; i < t.arr.length; i++) {
9230
9267
  const scope = { basePath: `${t.arrPath}/${i}`, item: t.arr[i] };
9231
9268
  const itemProps = {};
9232
9269
  for (const [k, v] of Object.entries(tRaw)) {
9233
9270
  if (RESERVED_PROP_KEYS.has(k))
9234
9271
  continue;
9235
- itemProps[k] = resolveDynamic(v, surface.dataModel, scope);
9272
+ itemProps[k] = resolveDynamic(v, surface.dataModel, scope, A2UI_FUNCTIONS);
9236
9273
  }
9237
9274
  elements[`${t.componentId}__${i}`] = { type: tType, props: itemProps };
9238
9275
  }
@@ -9251,24 +9288,35 @@ function surfaceToSpec(surface) {
9251
9288
  const root = surface.components.has('root')
9252
9289
  ? 'root'
9253
9290
  : surface.components.keys().next().value;
9254
- return { root, elements, state: surface.dataModel };
9291
+ // Seed empty check messages so errorText $bindState bindings resolve
9292
+ // (render-element defers mounting while any bound prop is undefined).
9293
+ const checkSeeds = {};
9294
+ for (const [id, comp] of surface.components) {
9295
+ if (componentHasChecks(comp))
9296
+ checkSeeds[id] = '';
9297
+ }
9298
+ const state = Object.keys(checkSeeds).length > 0
9299
+ ? { ...surface.dataModel, _a2uiChecks: { ...checkSeeds, ...(surface.dataModel['_a2uiChecks'] ?? {}) } }
9300
+ : surface.dataModel;
9301
+ return { root, elements, state };
9302
+ }
9303
+ /** True when the component carries validation rules the renderer enforces:
9304
+ * explicit `checks`, or a TextField `validationRegexp` with a bound value. */
9305
+ function componentHasChecks(raw) {
9306
+ if (Array.isArray(raw['checks']) && raw['checks'].length > 0)
9307
+ return true;
9308
+ return typeof raw['validationRegexp'] === 'string'
9309
+ && raw['validationRegexp'].length > 0
9310
+ && isPathRef(raw['value']);
9255
9311
  }
9256
9312
 
9257
- function toDynamicValue(v) {
9258
- if (typeof v === 'string')
9259
- return { literalString: v };
9260
- if (typeof v === 'number')
9261
- return { literalNumber: v };
9262
- if (typeof v === 'boolean')
9263
- return { literalBoolean: v };
9264
- return { literalString: String(v) };
9265
- }
9313
+ // SPDX-License-Identifier: MIT
9266
9314
  /**
9267
9315
  * Derive a human-readable label for an outgoing action by walking from
9268
9316
  * the source component to its authored visible text. Today supported:
9269
- * Button → child Text → literalString. Returns null for other component
9270
- * types or when the linkage isn't well-formed; callers fall back to a
9271
- * camelCase humanization of `action.name`.
9317
+ * Button → child Text → text. Returns null for other component types or
9318
+ * when the linkage isn't well-formed; callers fall back to a camelCase
9319
+ * humanization of `action.name`.
9272
9320
  *
9273
9321
  * Why: the chat-lib used to ship a hardcoded `KNOWN_LABELS` map
9274
9322
  * (bookingSubmit → 'Search flights') that embedded app-specific
@@ -9278,49 +9326,38 @@ function toDynamicValue(v) {
9278
9326
  */
9279
9327
  function deriveActionLabel(surface, sourceId) {
9280
9328
  const source = surface.components.get(sourceId);
9281
- if (!source)
9329
+ if (!source || source.component !== 'Button')
9282
9330
  return null;
9283
- const buttonProps = source.component.Button;
9284
- if (!buttonProps?.child)
9331
+ const childId = source.child;
9332
+ if (typeof childId !== 'string')
9285
9333
  return null;
9286
- const labelText = surface.components.get(buttonProps.child);
9287
- if (!labelText)
9334
+ const labelText = surface.components.get(childId);
9335
+ if (!labelText || labelText.component !== 'Text')
9288
9336
  return null;
9289
- const textProps = labelText.component.Text;
9290
- if (!textProps)
9291
- return null;
9292
- // `text` may be either a raw string (LLM-author ergonomic shorthand) or
9293
- // a wrapped DynamicString `{ literalString: "..." }` (canonical v1 shape).
9294
- // Accept both so the label survives whichever form the LLM happens to emit.
9295
- const text = textProps.text;
9337
+ // v0.9 dynamic strings are bare literals or `{ path }` bindings; only a
9338
+ // bare literal is a usable authored label.
9339
+ const text = labelText.text;
9296
9340
  if (typeof text === 'string') {
9297
9341
  return text.length > 0 ? text : null;
9298
9342
  }
9299
- if (text && typeof text === 'object' && typeof text.literalString === 'string') {
9300
- const literal = text.literalString;
9301
- return literal.length > 0 ? literal : null;
9302
- }
9303
9343
  return null;
9304
9344
  }
9305
- /** Builds an A2uiActionMessage from handler params and the current surface.
9306
- * The action.context is serialized as v1 DynamicValue-wrapped entries.
9307
- * Sets action.label when the source component is a Button with a Text
9308
- * child whose literalString is non-empty. */
9345
+ /** Builds a v0.9 A2uiActionMessage from handler params and the current
9346
+ * surface. The action.context is the resolved plain object the renderer
9347
+ * produced from the component's `action.event.context`. Sets action.label
9348
+ * when the source component is a Button with a Text child whose text is a
9349
+ * non-empty bare literal. */
9309
9350
  function buildA2uiActionMessage(params, surface) {
9310
- const rawContext = params['context'] ?? {};
9311
- const wrappedContext = {};
9312
- for (const [k, v] of Object.entries(rawContext)) {
9313
- wrappedContext[k] = toDynamicValue(v);
9314
- }
9351
+ const context = params['context'] ?? {};
9315
9352
  const sourceComponentId = params['sourceComponentId'];
9316
9353
  const message = {
9317
- version: 'v1',
9354
+ version: A2UI_WIRE_VERSION,
9318
9355
  action: {
9319
9356
  name: params['name'],
9320
9357
  surfaceId: surface.surfaceId,
9321
9358
  sourceComponentId,
9322
9359
  timestamp: new Date().toISOString(),
9323
- context: wrappedContext,
9360
+ context,
9324
9361
  },
9325
9362
  };
9326
9363
  const label = deriveActionLabel(surface, sourceComponentId);
@@ -9329,7 +9366,6 @@ function buildA2uiActionMessage(params, surface) {
9329
9366
  if (surface.sendDataModel) {
9330
9367
  message.metadata = {
9331
9368
  a2uiClientDataModel: {
9332
- version: 'v1',
9333
9369
  surfaces: { [surface.surfaceId]: surface.dataModel },
9334
9370
  },
9335
9371
  };
@@ -9396,22 +9432,55 @@ class A2uiSurfaceComponent {
9396
9432
  surfaceFallback = input(undefined, ...(ngDevMode ? [{ debugName: "surfaceFallback" }] : []));
9397
9433
  events = output();
9398
9434
  action = output();
9399
- /** Agent-set primary color from `beginRendering.styles.primaryColor`.
9435
+ /** Emitted when a submit is blocked by failing validation checks —
9436
+ * the spec client → agent error message (code VALIDATION_FAILED). */
9437
+ validationError = output();
9438
+ /** Surface-owned live state store: `$bindState` props read it and input
9439
+ * components write user edits into it, so event-time logic (checks,
9440
+ * action context) sees CURRENT values instead of the agent-seeded
9441
+ * snapshot. Seeded from spec.state with user edits preserved. Public so
9442
+ * hosts (and tests) can read the live values of a rendered surface. */
9443
+ liveStore = signalStateStore({});
9444
+ /** Last value this component seeded per state path (see chat-generative-ui:
9445
+ * distinguishes "still our seed — safe to overwrite" from "user edited"). */
9446
+ seeded = new Map();
9447
+ constructor() {
9448
+ effect(() => {
9449
+ const s = this.spec();
9450
+ const state = s?.state;
9451
+ if (!state)
9452
+ return;
9453
+ untracked(() => {
9454
+ for (const [key, value] of Object.entries(state)) {
9455
+ const path = key.startsWith('/') ? key : `/${key}`;
9456
+ const current = this.liveStore.get(path);
9457
+ const untouched = current === undefined ||
9458
+ (this.seeded.has(path) && current === this.seeded.get(path));
9459
+ if (untouched) {
9460
+ if (current !== value)
9461
+ this.liveStore.set(path, value);
9462
+ this.seeded.set(path, value);
9463
+ }
9464
+ }
9465
+ });
9466
+ });
9467
+ }
9468
+ /** Agent-set primary color from `createSurface.theme.primaryColor`.
9400
9469
  * Returns null when unset so the host binding doesn't override the
9401
9470
  * consumer's `:root`-level `--a2ui-primary` default. */
9402
- primaryColor = computed(() => (this.state()?.surface ?? this.surface())?.styles?.primaryColor ?? null, ...(ngDevMode ? [{ debugName: "primaryColor" }] : []));
9403
- /** Agent-set font family from `beginRendering.styles.font`. Returns
9404
- * null when unset so the host doesn't override consumer fonts. */
9405
- fontFamily = computed(() => (this.state()?.surface ?? this.surface())?.styles?.font ?? null, ...(ngDevMode ? [{ debugName: "fontFamily" }] : []));
9406
- /** Roots from the surface state components whose ids appear as
9407
- * children of no other component. The wire spec includes
9408
- * `beginRendering.root` as the single root; that path stays usable
9409
- * but we keep the renderer permissive in case future surfaces emit
9410
- * multiple top-level components.
9471
+ primaryColor = computed(() => (this.state()?.surface ?? this.surface())?.theme?.primaryColor ?? null, ...(ngDevMode ? [{ debugName: "primaryColor" }] : []));
9472
+ /** Agent identity chrome from `createSurface.theme`. When neither
9473
+ * `agentDisplayName` nor `iconUrl` is set, no header renders at all
9474
+ * (zero layout impact for themeless surfaces the common case). */
9475
+ agentDisplayName = computed(() => (this.state()?.surface ?? this.surface())?.theme?.agentDisplayName ?? null, ...(ngDevMode ? [{ debugName: "agentDisplayName" }] : []));
9476
+ iconUrl = computed(() => (this.state()?.surface ?? this.surface())?.theme?.iconUrl ?? null, ...(ngDevMode ? [{ debugName: "iconUrl" }] : []));
9477
+ /** Roots from the surface state. The v0.9 wire contract reserves the
9478
+ * component id `root` as the single tree root; we keep the renderer
9479
+ * permissive in case future surfaces emit multiple top-level
9480
+ * components.
9411
9481
  *
9412
9482
  * Conservative: returns only the first key from componentViews
9413
- * insertion order. The wire format's beginRendering.root carries the
9414
- * true root id; plumbing it through A2uiSurfaceState is a follow-up. */
9483
+ * insertion order. */
9415
9484
  rootIds = computed(() => {
9416
9485
  const st = this.state();
9417
9486
  if (!st)
@@ -9421,7 +9490,7 @@ class A2uiSurfaceComponent {
9421
9490
  /** Convert the A2UI surface to a json-render Spec for rendering.
9422
9491
  * Prefers `state().surface` (the progressively-built wire surface)
9423
9492
  * over the legacy `surface` input. surfaceToSpec handles
9424
- * children.explicitList → spec.children translation + reserved-key
9493
+ * children-id-list → spec.children translation + reserved-key
9425
9494
  * filtering + path-ref → $bindState rewriting; the rendered tree
9426
9495
  * then uses render-element's standard input-mapping
9427
9496
  * (`childKeys: el.children`) so catalog components receive the
@@ -9447,7 +9516,50 @@ class A2uiSurfaceComponent {
9447
9516
  const surf = this.state()?.surface ?? this.surface();
9448
9517
  if (!surf)
9449
9518
  return undefined;
9450
- const message = buildA2uiActionMessage(params, surf);
9519
+ // Live model: user edits in the store overlay the agent-seeded model.
9520
+ const liveModel = this.mergedLiveModel(surf);
9521
+ // Validation gate: every check rule on the surface must pass before
9522
+ // an event action dispatches (spec CheckRule semantics).
9523
+ const failures = evaluateSurfaceChecks(surf, liveModel);
9524
+ if (failures.length > 0) {
9525
+ for (const f of failures) {
9526
+ this.liveStore.set(`/_a2uiChecks/${f.componentId}`, f.message);
9527
+ }
9528
+ const first = failures[0];
9529
+ this.validationError.emit({
9530
+ version: A2UI_WIRE_VERSION,
9531
+ error: {
9532
+ code: 'VALIDATION_FAILED',
9533
+ surfaceId: surf.surfaceId,
9534
+ ...(first.path ? { path: first.path } : {}),
9535
+ message: first.message,
9536
+ },
9537
+ });
9538
+ return undefined;
9539
+ }
9540
+ // Clear any stale messages from a previous failed submit.
9541
+ for (const [id, comp] of surf.components) {
9542
+ if (componentHasChecks(comp)) {
9543
+ this.liveStore.set(`/_a2uiChecks/${id}`, '');
9544
+ }
9545
+ }
9546
+ // Substitute live-context markers with current values.
9547
+ const rawContext = params['context'] ?? {};
9548
+ const context = {};
9549
+ for (const [k, v] of Object.entries(rawContext)) {
9550
+ if (v != null && typeof v === 'object' && '$bindState' in v) {
9551
+ const path = String(v['$bindState']);
9552
+ context[k] = getByPointer(liveModel, path);
9553
+ }
9554
+ else {
9555
+ context[k] = v;
9556
+ }
9557
+ }
9558
+ // sendDataModel metadata must carry the LIVE model (user edits
9559
+ // included), minus renderer-internal keys.
9560
+ const { _a2uiChecks, ...publicModel } = liveModel;
9561
+ void _a2uiChecks;
9562
+ const message = buildA2uiActionMessage({ ...params, context }, { ...surf, dataModel: publicModel });
9451
9563
  this.action.emit(message);
9452
9564
  return message;
9453
9565
  },
@@ -9460,7 +9572,7 @@ class A2uiSurfaceComponent {
9460
9572
  }
9461
9573
  // Built-in fallback
9462
9574
  if (call === 'openUrl' && typeof globalThis.window !== 'undefined') {
9463
- globalThis.window.open(String(args['url'] ?? ''), '_blank');
9575
+ globalThis.window.open(String(args['url'] ?? ''), '_blank', 'noopener');
9464
9576
  }
9465
9577
  return undefined;
9466
9578
  },
@@ -9469,12 +9581,30 @@ class A2uiSurfaceComponent {
9469
9581
  onRenderEvent(event) {
9470
9582
  this.events.emit(event);
9471
9583
  }
9584
+ /** Agent-seeded data model overlaid with the store's current state
9585
+ * (user edits + check messages). Shallow per-key merge is sufficient:
9586
+ * store snapshots hold whole top-level values written via pointers. */
9587
+ mergedLiveModel(surf) {
9588
+ const snapshot = this.liveStore.getSnapshot();
9589
+ return deepOverlay(surf.dataModel, snapshot);
9590
+ }
9472
9591
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiSurfaceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9473
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiSurfaceComponent, isStandalone: true, selector: "a2ui-surface", inputs: { surface: { classPropertyName: "surface", publicName: "surface", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, catalog: { classPropertyName: "catalog", publicName: "catalog", isSignal: true, isRequired: true, transformFunction: null }, handlers: { classPropertyName: "handlers", publicName: "handlers", isSignal: true, isRequired: false, transformFunction: null }, surfaceFallback: { classPropertyName: "surfaceFallback", publicName: "surfaceFallback", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { events: "events", action: "action" }, host: { properties: { "style.--a2ui-primary": "primaryColor()", "style.font-family": "fontFamily()" } }, ngImport: i0, template: `
9592
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiSurfaceComponent, isStandalone: true, selector: "a2ui-surface", inputs: { surface: { classPropertyName: "surface", publicName: "surface", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, catalog: { classPropertyName: "catalog", publicName: "catalog", isSignal: true, isRequired: true, transformFunction: null }, handlers: { classPropertyName: "handlers", publicName: "handlers", isSignal: true, isRequired: false, transformFunction: null }, surfaceFallback: { classPropertyName: "surfaceFallback", publicName: "surfaceFallback", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { events: "events", action: "action", validationError: "validationError" }, host: { properties: { "style.--a2ui-primary": "primaryColor()" } }, ngImport: i0, template: `
9593
+ @if (agentDisplayName() || iconUrl()) {
9594
+ <div class="a2ui-surface-chrome">
9595
+ @if (iconUrl(); as icon) {
9596
+ <img [src]="icon" alt="" referrerpolicy="no-referrer" />
9597
+ }
9598
+ @if (agentDisplayName(); as name) {
9599
+ <span>{{ name }}</span>
9600
+ }
9601
+ </div>
9602
+ }
9474
9603
  @if (spec(); as s) {
9475
9604
  <render-spec
9476
9605
  [spec]="s"
9477
9606
  [registry]="registry()"
9607
+ [store]="liveStore"
9478
9608
  [handlers]="internalHandlers()"
9479
9609
  (events)="onRenderEvent($event)"
9480
9610
  />
@@ -9485,32 +9615,32 @@ class A2uiSurfaceComponent {
9485
9615
  <a2ui-default-fallback />
9486
9616
  }
9487
9617
  }
9488
- `, isInline: true, dependencies: [{ kind: "component", type: RenderSpecComponent, selector: "render-spec", inputs: ["spec", "registry", "store", "functions", "handlers", "loading"], outputs: ["events"] }, { kind: "component", type: A2uiDefaultFallbackComponent, selector: "a2ui-default-fallback" }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9618
+ `, isInline: true, styles: [".a2ui-surface-chrome{display:flex;align-items:center;gap:var(--a2ui-spacing-2);margin-bottom:var(--a2ui-spacing-2);color:var(--a2ui-label);font-size:var(--a2ui-typography-label-size)}.a2ui-surface-chrome img{width:16px;height:16px;border-radius:50%;object-fit:cover}\n"], dependencies: [{ kind: "component", type: RenderSpecComponent, selector: "render-spec", inputs: ["spec", "registry", "store", "functions", "handlers", "loading"], outputs: ["events"] }, { kind: "component", type: A2uiDefaultFallbackComponent, selector: "a2ui-default-fallback" }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9489
9619
  }
9490
9620
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiSurfaceComponent, decorators: [{
9491
9621
  type: Component,
9492
- args: [{
9493
- selector: 'a2ui-surface',
9494
- standalone: true,
9495
- imports: [
9622
+ args: [{ selector: 'a2ui-surface', standalone: true, imports: [
9496
9623
  RenderSpecComponent,
9497
9624
  A2uiDefaultFallbackComponent,
9498
9625
  NgComponentOutlet,
9499
- ],
9500
- changeDetection: ChangeDetectionStrategy.OnPush,
9501
- // The host applies the agent-set v1 styles (`beginRendering.styles`)
9502
- // as inline CSS custom properties + font-family. Catalog components
9503
- // consume `--a2ui-primary` for accents (buttons, sliders, focus,
9504
- // etc.); `font-family` cascades naturally from the host.
9505
- host: {
9626
+ ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
9506
9627
  '[style.--a2ui-primary]': 'primaryColor()',
9507
- '[style.font-family]': 'fontFamily()',
9508
- },
9509
- template: `
9628
+ }, template: `
9629
+ @if (agentDisplayName() || iconUrl()) {
9630
+ <div class="a2ui-surface-chrome">
9631
+ @if (iconUrl(); as icon) {
9632
+ <img [src]="icon" alt="" referrerpolicy="no-referrer" />
9633
+ }
9634
+ @if (agentDisplayName(); as name) {
9635
+ <span>{{ name }}</span>
9636
+ }
9637
+ </div>
9638
+ }
9510
9639
  @if (spec(); as s) {
9511
9640
  <render-spec
9512
9641
  [spec]="s"
9513
9642
  [registry]="registry()"
9643
+ [store]="liveStore"
9514
9644
  [handlers]="internalHandlers()"
9515
9645
  (events)="onRenderEvent($event)"
9516
9646
  />
@@ -9521,9 +9651,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
9521
9651
  <a2ui-default-fallback />
9522
9652
  }
9523
9653
  }
9524
- `,
9525
- }]
9526
- }], propDecorators: { surface: [{ type: i0.Input, args: [{ isSignal: true, alias: "surface", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], catalog: [{ type: i0.Input, args: [{ isSignal: true, alias: "catalog", required: true }] }], handlers: [{ type: i0.Input, args: [{ isSignal: true, alias: "handlers", required: false }] }], surfaceFallback: [{ type: i0.Input, args: [{ isSignal: true, alias: "surfaceFallback", required: false }] }], events: [{ type: i0.Output, args: ["events"] }], action: [{ type: i0.Output, args: ["action"] }] } });
9654
+ `, styles: [".a2ui-surface-chrome{display:flex;align-items:center;gap:var(--a2ui-spacing-2);margin-bottom:var(--a2ui-spacing-2);color:var(--a2ui-label);font-size:var(--a2ui-typography-label-size)}.a2ui-surface-chrome img{width:16px;height:16px;border-radius:50%;object-fit:cover}\n"] }]
9655
+ }], ctorParameters: () => [], propDecorators: { surface: [{ type: i0.Input, args: [{ isSignal: true, alias: "surface", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], catalog: [{ type: i0.Input, args: [{ isSignal: true, alias: "catalog", required: true }] }], handlers: [{ type: i0.Input, args: [{ isSignal: true, alias: "handlers", required: false }] }], surfaceFallback: [{ type: i0.Input, args: [{ isSignal: true, alias: "surfaceFallback", required: false }] }], events: [{ type: i0.Output, args: ["events"] }], action: [{ type: i0.Output, args: ["action"] }], validationError: [{ type: i0.Output, args: ["validationError"] }] } });
9656
+ /** Recursively overlay `top` onto `base` (plain objects merge; anything
9657
+ * else in `top` wins). */
9658
+ function deepOverlay(base, top) {
9659
+ const out = { ...base };
9660
+ for (const [k, v] of Object.entries(top)) {
9661
+ const prev = out[k];
9662
+ if (v != null && typeof v === 'object' && !Array.isArray(v)
9663
+ && prev != null && typeof prev === 'object' && !Array.isArray(prev)) {
9664
+ out[k] = deepOverlay(prev, v);
9665
+ }
9666
+ else {
9667
+ out[k] = v;
9668
+ }
9669
+ }
9670
+ return out;
9671
+ }
9672
+ const CHECK_FUNCTIONS = createA2uiFunctionRegistry();
9673
+ /** Evaluate every check rule on the surface against the live model.
9674
+ * A rule passes when its condition resolves to exactly `true`. TextField
9675
+ * `validationRegexp` (with a bound value) contributes an implicit rule. */
9676
+ function evaluateSurfaceChecks(surf, liveModel) {
9677
+ const failures = [];
9678
+ for (const [id, comp] of surf.components) {
9679
+ const raw = comp;
9680
+ const boundPath = isPathRef(raw['value']) ? raw['value'].path : undefined;
9681
+ const rules = Array.isArray(raw['checks']) ? [...raw['checks']] : [];
9682
+ if (typeof raw['validationRegexp'] === 'string' && raw['validationRegexp'].length > 0 && boundPath) {
9683
+ rules.push({
9684
+ condition: { call: 'regex', args: { value: { path: boundPath }, pattern: raw['validationRegexp'] } },
9685
+ message: 'Invalid format',
9686
+ });
9687
+ }
9688
+ for (const rule of rules) {
9689
+ if (!rule || typeof rule.message !== 'string')
9690
+ continue;
9691
+ const passed = resolveDynamic(rule.condition, liveModel, undefined, CHECK_FUNCTIONS) === true;
9692
+ if (!passed) {
9693
+ failures.push({ componentId: id, message: rule.message, ...(boundPath ? { path: boundPath } : {}) });
9694
+ break; // first failing rule per component
9695
+ }
9696
+ }
9697
+ }
9698
+ return failures;
9699
+ }
9527
9700
 
9528
9701
  // SPDX-License-Identifier: MIT
9529
9702
  /**
@@ -9586,7 +9759,9 @@ function createParseTreeStore(parser) {
9586
9759
  };
9587
9760
  }
9588
9761
 
9589
- const REF_PATTERN = /\{(\$\.[^}]+)\}/g;
9762
+ // `[^{}]` (no nested braces) + a length bound keep the scan linear on
9763
+ // adversarial LLM-authored strings (CodeQL js/polynomial-redos).
9764
+ const REF_PATTERN = /\{(\$\.[^{}]{1,512})\}/g;
9590
9765
  function walk(value, into) {
9591
9766
  if (typeof value === 'string') {
9592
9767
  let m;
@@ -9606,29 +9781,19 @@ function walk(value, into) {
9606
9781
  }
9607
9782
  }
9608
9783
  /** Extracts the set of data-model paths (e.g. `$.form.name`) referenced
9609
- * by `{$.path}` expressions inside a component's prop bag. Result is
9784
+ * by `{$.path}` expressions inside a component's props. Result is
9610
9785
  * deduplicated and sorted for stable signal identity. */
9611
- function extractBindings(def) {
9786
+ function extractBindings(component) {
9612
9787
  const out = new Set();
9613
- walk(def, out);
9788
+ walk(component, out);
9614
9789
  return [...out].sort();
9615
9790
  }
9616
9791
 
9617
9792
  // SPDX-License-Identifier: MIT
9618
- function entriesToObject(entries) {
9619
- const out = {};
9620
- for (const e of entries) {
9621
- if ('valueString' in e && e.valueString !== undefined)
9622
- out[e.key] = e.valueString;
9623
- else if ('valueNumber' in e && e.valueNumber !== undefined)
9624
- out[e.key] = e.valueNumber;
9625
- else if ('valueBoolean' in e && e.valueBoolean !== undefined)
9626
- out[e.key] = e.valueBoolean;
9627
- else if ('valueMap' in e && Array.isArray(e.valueMap))
9628
- out[e.key] = entriesToObject(e.valueMap);
9629
- }
9630
- return out;
9631
- }
9793
+ /** Component-envelope keys that are protocol structure, not renderable props. */
9794
+ const RESERVED_VIEW_PROP_KEYS = new Set([
9795
+ 'id', 'component', 'catalogId', 'weight', 'accessibility', 'checks',
9796
+ ]);
9632
9797
  /** Returns true if `path` (in `$.a.b.c` form) resolves to a defined,
9633
9798
  * non-null value inside `dataModel`. Used to decide per-component
9634
9799
  * readiness. */
@@ -9648,7 +9813,7 @@ function isResolved(dataModel, path) {
9648
9813
  * nested objects/arrays are recursed. */
9649
9814
  function resolveProps(value, dataModel) {
9650
9815
  if (typeof value === 'string') {
9651
- const full = value.match(/^\{(\$\.[^}]+)\}$/);
9816
+ const full = value.match(/^\{(\$\.[^{}]{1,512})\}$/);
9652
9817
  if (full) {
9653
9818
  const segs = full[1].slice(2).split('.');
9654
9819
  let cur = dataModel;
@@ -9659,7 +9824,9 @@ function resolveProps(value, dataModel) {
9659
9824
  }
9660
9825
  return cur;
9661
9826
  }
9662
- return value.replace(/\{(\$\.[^}]+)\}/g, (_, path) => {
9827
+ // Bounded, brace-free class keeps the scan linear on adversarial
9828
+ // LLM-authored strings (CodeQL js/polynomial-redos).
9829
+ return value.replace(/\{(\$\.[^{}]{1,512})\}/g, (_, path) => {
9663
9830
  const segs = path.slice(2).split('.');
9664
9831
  let cur = dataModel;
9665
9832
  for (const s of segs) {
@@ -9681,12 +9848,50 @@ function resolveProps(value, dataModel) {
9681
9848
  }
9682
9849
  return value;
9683
9850
  }
9851
+ /** Resolve a flat v0.9 component into the renderable prop bag: reserved
9852
+ * protocol keys stripped, `{$.path}` references substituted. */
9853
+ function resolveViewProps(component, dataModel) {
9854
+ const out = {};
9855
+ for (const [k, v] of Object.entries(component)) {
9856
+ if (RESERVED_VIEW_PROP_KEYS.has(k))
9857
+ continue;
9858
+ out[k] = resolveProps(v, dataModel);
9859
+ }
9860
+ return out;
9861
+ }
9862
+ function projectView(component) {
9863
+ return {
9864
+ id: component.id,
9865
+ type: typeof component.component === 'string' ? component.component : 'Unknown',
9866
+ bindings: extractBindings(component),
9867
+ ready: false,
9868
+ props: {},
9869
+ def: component,
9870
+ };
9871
+ }
9872
+ /** Apply one v0.9 data-model mutation (set at path, whole-model replace,
9873
+ * or delete-at-path when `value` is omitted). */
9874
+ function applyDataModelDelta(dataModel, delta) {
9875
+ const path = delta.path && delta.path !== '/' ? delta.path : undefined;
9876
+ if (delta.del) {
9877
+ return path ? deleteByPointer(dataModel, path) : {};
9878
+ }
9879
+ if (!path) {
9880
+ return (delta.value ?? {});
9881
+ }
9882
+ return setByPointer(dataModel, path, delta.value);
9883
+ }
9684
9884
  /**
9685
9885
  * Create an {@link A2uiSurfaceStore} — the per-conversation store that buffers
9686
- * streamed A2UI surface updates, tracks each surface's data model + lifecycle
9886
+ * streamed A2UI v0.9 envelopes, tracks each surface's data model + lifecycle
9687
9887
  * state, and exposes them as signals for rendering. One store backs a chat
9688
9888
  * thread's A2UI surfaces.
9689
9889
  *
9890
+ * A surface becomes visible once its `createSurface` envelope has arrived AND
9891
+ * a component with id `root` has been defined (the v0.9 progressive-rendering
9892
+ * rule). Everything received earlier is buffered; afterwards, components merge
9893
+ * incrementally by id and data-model updates apply immediately.
9894
+ *
9690
9895
  * @returns A fresh, empty {@link A2uiSurfaceStore}.
9691
9896
  * @example
9692
9897
  * ```ts
@@ -9701,145 +9906,115 @@ function createA2uiSurfaceStore() {
9701
9906
  function bufferOf(surfaceId) {
9702
9907
  let b = buffers.get(surfaceId);
9703
9908
  if (!b) {
9704
- b = { dataModelDeltas: [] };
9909
+ b = { components: new Map(), componentViews: new Map(), dataModelDeltas: [] };
9705
9910
  buffers.set(surfaceId, b);
9706
9911
  }
9707
9912
  return b;
9708
9913
  }
9914
+ function publish(surface, views) {
9915
+ const nextSurfaces = new Map(surfacesSignal());
9916
+ nextSurfaces.set(surface.surfaceId, surface);
9917
+ surfacesSignal.set(nextSurfaces);
9918
+ const nextStates = new Map(surfaceStatesSignal());
9919
+ nextStates.set(surface.surfaceId, { surface, componentViews: views });
9920
+ surfaceStatesSignal.set(nextStates);
9921
+ }
9922
+ /** Recompute readiness/props for every view against `dataModel`,
9923
+ * honoring the monotonic ready rule. */
9924
+ function refreshViews(views, dataModel) {
9925
+ const next = new Map();
9926
+ for (const [id, v] of views) {
9927
+ const allResolved = v.bindings.every((p) => isResolved(dataModel, p));
9928
+ // Monotonic: once ready=true, stays true even if a later update
9929
+ // clears a referenced path.
9930
+ const nextReady = v.ready || allResolved;
9931
+ next.set(id, {
9932
+ ...v,
9933
+ ready: nextReady,
9934
+ props: nextReady ? resolveViewProps(v.def, dataModel) : v.props,
9935
+ });
9936
+ }
9937
+ return next;
9938
+ }
9939
+ /** Commit the buffer to a live surface if the v0.9 render condition holds:
9940
+ * createSurface seen AND a `root` component defined. */
9941
+ function tryCommit(surfaceId) {
9942
+ const b = buffers.get(surfaceId);
9943
+ if (!b || !b.create || !b.components.has('root'))
9944
+ return;
9945
+ let dataModel = {};
9946
+ for (const d of b.dataModelDeltas) {
9947
+ dataModel = applyDataModelDelta(dataModel, d);
9948
+ }
9949
+ const surface = {
9950
+ surfaceId,
9951
+ catalogId: b.create.catalogId,
9952
+ ...(b.create.theme ? { theme: b.create.theme } : {}),
9953
+ ...(b.create.sendDataModel !== undefined ? { sendDataModel: b.create.sendDataModel } : {}),
9954
+ components: new Map(b.components),
9955
+ dataModel,
9956
+ };
9957
+ publish(surface, refreshViews(b.componentViews, dataModel));
9958
+ buffers.delete(surfaceId);
9959
+ }
9709
9960
  function apply(message) {
9710
- if ('surfaceUpdate' in message) {
9711
- const upd = message.surfaceUpdate;
9712
- const b = bufferOf(upd.surfaceId);
9713
- const map = new Map();
9714
- for (const c of upd.components)
9715
- map.set(c.id, c);
9716
- b.components = map;
9717
- // Project per-component views with bindings extracted from prop
9718
- // expressions. ready starts false; props starts empty.
9719
- const views = new Map();
9720
- for (const c of upd.components) {
9721
- const def = c.component;
9722
- const typeKey = (def && typeof def === 'object')
9723
- ? (Object.keys(def)[0] ?? 'Unknown')
9724
- : 'Unknown';
9725
- views.set(c.id, {
9726
- id: c.id,
9727
- type: typeKey,
9728
- bindings: extractBindings(def),
9729
- ready: false,
9730
- props: {},
9731
- def,
9732
- });
9961
+ if ('createSurface' in message) {
9962
+ const create = message.createSurface;
9963
+ const live = surfacesSignal().get(create.surfaceId);
9964
+ if (live) {
9965
+ // v0.9 calls createSurface-on-existing an agent error; tolerate it
9966
+ // as an idempotent refresh of the surface's create-time fields.
9967
+ const state = surfaceStatesSignal().get(create.surfaceId);
9968
+ const surface = {
9969
+ ...live,
9970
+ catalogId: create.catalogId,
9971
+ ...(create.theme !== undefined ? { theme: create.theme } : {}),
9972
+ ...(create.sendDataModel !== undefined ? { sendDataModel: create.sendDataModel } : {}),
9973
+ };
9974
+ publish(surface, new Map(state?.componentViews ?? []));
9975
+ return;
9733
9976
  }
9734
- b.componentViews = views;
9977
+ bufferOf(create.surfaceId).create = create;
9978
+ tryCommit(create.surfaceId);
9735
9979
  return;
9736
9980
  }
9737
- if ('dataModelUpdate' in message) {
9738
- const upd = message.dataModelUpdate;
9739
- const surface = surfacesSignal().get(upd.surfaceId);
9740
- if (surface) {
9741
- // Already-rendered surface: apply incrementally.
9742
- let dataModel = surface.dataModel;
9743
- const obj = entriesToObject(upd.contents);
9744
- if (upd.path && upd.path !== '/') {
9745
- for (const [k, v] of Object.entries(obj)) {
9746
- dataModel = setByPointer(dataModel, `${upd.path}/${k}`, v);
9747
- }
9748
- }
9749
- else {
9750
- dataModel = { ...dataModel, ...obj };
9751
- }
9752
- const next = new Map(surfacesSignal());
9753
- const nextSurface = { ...surface, dataModel };
9754
- next.set(upd.surfaceId, nextSurface);
9755
- surfacesSignal.set(next);
9756
- // Recompute per-component readiness with the monotonic rule.
9757
- const prevState = surfaceStatesSignal().get(upd.surfaceId);
9758
- if (prevState) {
9759
- const nextViews = new Map();
9760
- for (const [id, v] of prevState.componentViews) {
9761
- const allResolved = v.bindings.every((p) => isResolved(dataModel, p));
9762
- // Monotonic: once ready=true, stays true even if a later
9763
- // update clears a referenced path.
9764
- const nextReady = v.ready || allResolved;
9765
- nextViews.set(id, {
9766
- ...v,
9767
- ready: nextReady,
9768
- props: nextReady
9769
- ? resolveProps(v.def, dataModel)
9770
- : v.props,
9771
- });
9772
- }
9773
- const nextStatesMap = new Map(surfaceStatesSignal());
9774
- nextStatesMap.set(upd.surfaceId, { surface: nextSurface, componentViews: nextViews });
9775
- surfaceStatesSignal.set(nextStatesMap);
9981
+ if ('updateComponents' in message) {
9982
+ const upd = message.updateComponents;
9983
+ const live = surfacesSignal().get(upd.surfaceId);
9984
+ if (live) {
9985
+ // Incremental merge by id into the live surface.
9986
+ const components = new Map(live.components);
9987
+ const state = surfaceStatesSignal().get(upd.surfaceId);
9988
+ const views = new Map(state?.componentViews ?? []);
9989
+ for (const c of upd.components) {
9990
+ components.set(c.id, c);
9991
+ views.set(c.id, projectView(c));
9776
9992
  }
9993
+ const surface = { ...live, components };
9994
+ publish(surface, refreshViews(views, surface.dataModel));
9995
+ return;
9777
9996
  }
9778
- else {
9779
- // Pre-render: buffer the delta.
9780
- const b = bufferOf(upd.surfaceId);
9781
- b.dataModelDeltas.push({ path: upd.path, contents: upd.contents });
9997
+ const b = bufferOf(upd.surfaceId);
9998
+ for (const c of upd.components) {
9999
+ b.components.set(c.id, c);
10000
+ b.componentViews.set(c.id, projectView(c));
9782
10001
  }
10002
+ tryCommit(upd.surfaceId);
9783
10003
  return;
9784
10004
  }
9785
- if ('beginRendering' in message) {
9786
- const begin = message.beginRendering;
9787
- const b = buffers.get(begin.surfaceId);
9788
- if (!b || !b.components)
9789
- return; // no surfaceUpdate yet — no-op
9790
- // Build initial data model from buffered deltas.
9791
- let dataModel = {};
9792
- for (const d of b.dataModelDeltas) {
9793
- const obj = entriesToObject(d.contents);
9794
- if (d.path && d.path !== '/') {
9795
- for (const [k, v] of Object.entries(obj)) {
9796
- dataModel = setByPointer(dataModel, `${d.path}/${k}`, v);
9797
- }
9798
- }
9799
- else {
9800
- dataModel = { ...dataModel, ...obj };
9801
- }
10005
+ if ('updateDataModel' in message) {
10006
+ const upd = message.updateDataModel;
10007
+ const delta = { path: upd.path, value: upd.value, del: !('value' in upd) || upd.value === undefined };
10008
+ const live = surfacesSignal().get(upd.surfaceId);
10009
+ if (live) {
10010
+ const dataModel = applyDataModelDelta(live.dataModel, delta);
10011
+ const surface = { ...live, dataModel };
10012
+ const state = surfaceStatesSignal().get(upd.surfaceId);
10013
+ publish(surface, refreshViews(state?.componentViews ?? new Map(), dataModel));
9802
10014
  }
9803
- // Merge with any existing surface's dataModel if this is a re-render.
9804
- const existing = surfacesSignal().get(begin.surfaceId);
9805
- if (existing) {
9806
- dataModel = { ...existing.dataModel, ...dataModel };
9807
- }
9808
- // Capture v1 styles (font, primaryColor) from beginRendering. A
9809
- // re-render keeps any prior styles unless the new beginRendering
9810
- // explicitly overrides them — this matches the agent's likely
9811
- // intent ("change the data, keep the look").
9812
- const nextStyles = begin.styles
9813
- ?? existing?.styles;
9814
- const surface = {
9815
- surfaceId: begin.surfaceId,
9816
- catalogId: 'basic',
9817
- components: b.components,
9818
- dataModel,
9819
- ...(nextStyles ? { styles: nextStyles } : {}),
9820
- };
9821
- const next = new Map(surfacesSignal());
9822
- next.set(begin.surfaceId, surface);
9823
- surfacesSignal.set(next);
9824
- // Project per-component views with initial readiness based on the
9825
- // accumulated data model.
9826
- const baseViews = b.componentViews ?? new Map();
9827
- const initialViews = new Map();
9828
- for (const [id, v] of baseViews) {
9829
- const allResolved = v.bindings.every((p) => isResolved(dataModel, p));
9830
- initialViews.set(id, {
9831
- ...v,
9832
- ready: allResolved,
9833
- props: allResolved
9834
- ? resolveProps(v.def, dataModel)
9835
- : {},
9836
- });
10015
+ else {
10016
+ bufferOf(upd.surfaceId).dataModelDeltas.push(delta);
9837
10017
  }
9838
- const nextStates = new Map(surfaceStatesSignal());
9839
- nextStates.set(begin.surfaceId, { surface, componentViews: initialViews });
9840
- surfaceStatesSignal.set(nextStates);
9841
- // Reset buffer so subsequent surfaceUpdate is the next round.
9842
- buffers.set(begin.surfaceId, { dataModelDeltas: [] });
9843
10018
  return;
9844
10019
  }
9845
10020
  if ('deleteSurface' in message) {
@@ -10151,7 +10326,7 @@ function createContentClassifier() {
10151
10326
 
10152
10327
  // libs/chat/src/lib/a2ui/envelope-normalizer.ts
10153
10328
  // SPDX-License-Identifier: MIT
10154
- const ENVELOPE_KEYS = ['surfaceUpdate', 'beginRendering', 'dataModelUpdate', 'deleteSurface'];
10329
+ const ENVELOPE_KEYS = ['createSurface', 'updateComponents', 'updateDataModel', 'deleteSurface'];
10155
10330
  /**
10156
10331
  * The parent LLM may emit envelope-tool arguments in four shapes (observed in
10157
10332
  * the spike across gpt-5-mini and gpt-5): the canonical {envelopes: [...]},
@@ -10183,7 +10358,7 @@ function normalizeEnvelopeArgs(args) {
10183
10358
  .sort((a, b) => a - b)
10184
10359
  .map((k) => args[String(k)]);
10185
10360
  }
10186
- // (d) flat single envelope: { surfaceUpdate: {...} } | { beginRendering: ... } | etc
10361
+ // (d) flat single envelope: { createSurface: {...} } | { updateComponents: ... } | etc
10187
10362
  if (ENVELOPE_KEYS.some((k) => k in args)) {
10188
10363
  return [args];
10189
10364
  }
@@ -10374,17 +10549,15 @@ function isValidJsonPrefix(s) {
10374
10549
  * tool_call.arguments JSON. Uses @cacheplane/partial-json to extract
10375
10550
  * structurally-complete envelope objects from the growing args string.
10376
10551
  *
10377
- * Synthesis safety net: if the first complete surfaceUpdate arrives and
10378
- * no beginRendering has been extracted yet, the bridge synthesises one
10379
- * targeted at the surfaceUpdate's first component (preferring id='root'
10380
- * if present). This makes the surface mount IMMEDIATELY after the first
10381
- * surfaceUpdate parses without waiting for the LLM to emit beginRendering
10382
- * at the end of its envelope list so the render-element fallback gate
10383
- * (PR #252) actually fires while dataModelUpdates flow in.
10552
+ * Synthesis safety net: v0.9 requires a `createSurface` envelope before
10553
+ * any `updateComponents`. If a complete `updateComponents` arrives for a
10554
+ * surface with no `createSurface` seen yet this turn, the bridge
10555
+ * synthesises one (basic catalog) so the surface can mount as soon as its
10556
+ * `root` component is defined the store gates rendering on
10557
+ * createSurface + root, and fills the tree in progressively after that.
10384
10558
  *
10385
- * The store's apply() already treats repeated beginRendering for the same
10386
- * surfaceId as idempotent (just re-applies styles), so the LLM's eventual
10387
- * beginRendering (if any) is a no-op rather than a conflict.
10559
+ * The store treats a later "real" createSurface for the same surface as an
10560
+ * idempotent refresh, so LLMs that emit one out of order are harmless.
10388
10561
  */
10389
10562
  function createPartialArgsBridge(store) {
10390
10563
  const states = new Map();
@@ -10394,20 +10567,13 @@ function createPartialArgsBridge(store) {
10394
10567
  s = {
10395
10568
  parser: createPartialJsonParser(),
10396
10569
  dispatchedCount: 0,
10397
- surfacePairDispatched: false,
10398
- synthesisedSurfaceId: null,
10570
+ createDispatched: new Set(),
10399
10571
  poisoned: false,
10400
10572
  };
10401
10573
  states.set(toolCallId, s);
10402
10574
  }
10403
10575
  return s;
10404
10576
  }
10405
- function pickRoot(components) {
10406
- if (components.length === 0)
10407
- return null;
10408
- const explicitRoot = components.find((c) => c.id === 'root');
10409
- return explicitRoot ? explicitRoot.id : components[0].id;
10410
- }
10411
10577
  function push(toolCallId, argsSoFar) {
10412
10578
  const state = stateOf(toolCallId);
10413
10579
  if (state.poisoned)
@@ -10437,32 +10603,8 @@ function createPartialArgsBridge(store) {
10437
10603
  const envelopes = normalizeEnvelopeArgs(materialised);
10438
10604
  if (!envelopes)
10439
10605
  return;
10440
- // Phase 1: defer initial dispatch until the first envelope is a complete
10441
- // surfaceUpdate whose components include at least one entry with an `id`
10442
- // — otherwise pickRoot returns null and synthesis silently no-ops, leaving
10443
- // the surface unmounted forever. Once we have a pickable root, dispatch
10444
- // the surfaceUpdate AND a synthesised beginRendering as an atomic pair.
10445
- if (!state.surfacePairDispatched) {
10446
- const firstEnv = envelopes[0];
10447
- if (!firstEnv || !('surfaceUpdate' in firstEnv))
10448
- return;
10449
- if (!isStructurallyComplete(firstEnv))
10450
- return;
10451
- const upd = firstEnv.surfaceUpdate;
10452
- if (upd.components.length === 0)
10453
- return;
10454
- const root = pickRoot(upd.components);
10455
- if (!root)
10456
- return;
10457
- state.surfacePairDispatched = true;
10458
- state.dispatchedCount = 1; // index 0 = the surfaceUpdate we just sent
10459
- state.synthesisedSurfaceId = upd.surfaceId;
10460
- store.applyPartialArgs(toolCallId, [
10461
- firstEnv,
10462
- { beginRendering: { surfaceId: upd.surfaceId, root } },
10463
- ]);
10464
- }
10465
- // Phase 2: dispatch any newly-complete envelopes beyond the initial pair.
10606
+ // Dispatch newly-complete envelopes in order, synthesising the missing
10607
+ // createSurface when the stream leads with components.
10466
10608
  const newEnvelopes = [];
10467
10609
  for (let i = state.dispatchedCount; i < envelopes.length; i++) {
10468
10610
  const env = envelopes[i];
@@ -10471,14 +10613,18 @@ function createPartialArgsBridge(store) {
10471
10613
  // exist before earlier ones complete (envelopes are an ordered list).
10472
10614
  break;
10473
10615
  }
10474
- // Skip a "real" beginRendering for the synthesised surface — the store
10475
- // already treats it as idempotent (just re-applies styles), but we
10476
- // advance past it so dispatchedCount stays in sync with the index.
10477
- if ('beginRendering' in env &&
10478
- env.beginRendering.surfaceId ===
10479
- state.synthesisedSurfaceId) {
10480
- state.dispatchedCount = i + 1;
10481
- continue;
10616
+ if ('createSurface' in env) {
10617
+ state.createDispatched.add(env.createSurface.surfaceId);
10618
+ }
10619
+ else if ('updateComponents' in env) {
10620
+ const surfaceId = env.updateComponents.surfaceId;
10621
+ if (!state.createDispatched.has(surfaceId)) {
10622
+ state.createDispatched.add(surfaceId);
10623
+ newEnvelopes.push({
10624
+ version: A2UI_WIRE_VERSION,
10625
+ createSurface: { surfaceId, catalogId: A2UI_BASIC_CATALOG_ID },
10626
+ });
10627
+ }
10482
10628
  }
10483
10629
  newEnvelopes.push(env);
10484
10630
  state.dispatchedCount = i + 1;
@@ -10497,12 +10643,27 @@ function isStructurallyComplete(env) {
10497
10643
  if (!env || typeof env !== 'object' || Array.isArray(env))
10498
10644
  return false;
10499
10645
  const obj = env;
10500
- for (const k of ['surfaceUpdate', 'beginRendering', 'dataModelUpdate', 'deleteSurface']) {
10646
+ for (const k of ['createSurface', 'updateComponents', 'updateDataModel', 'deleteSurface']) {
10501
10647
  if (k in obj && typeof obj[k] === 'object' && obj[k] !== null) {
10502
- // For surfaceUpdate, also require non-undefined surfaceId + components.
10503
- if (k === 'surfaceUpdate') {
10504
- const su = obj[k];
10505
- return typeof su.surfaceId === 'string' && Array.isArray(su.components);
10648
+ // For updateComponents, require surfaceId + components where every
10649
+ // component has at least parsed its `id` and `component` fields —
10650
+ // a half-streamed component object materialises as `{}` and must not
10651
+ // dispatch (it would consume the envelope index and drop the real
10652
+ // components forever, since re-parses skip dispatched indices).
10653
+ if (k === 'updateComponents') {
10654
+ const uc = obj[k];
10655
+ return typeof uc.surfaceId === 'string'
10656
+ && Array.isArray(uc.components)
10657
+ && uc.components.length > 0
10658
+ && uc.components.every((c) => c != null && typeof c === 'object'
10659
+ && typeof c.id === 'string'
10660
+ && typeof c.component === 'string');
10661
+ }
10662
+ // For createSurface, require both ids so a half-streamed envelope
10663
+ // doesn't commit with an undefined catalogId.
10664
+ if (k === 'createSurface') {
10665
+ const cs = obj[k];
10666
+ return typeof cs.surfaceId === 'string' && typeof cs.catalogId === 'string';
10506
10667
  }
10507
10668
  return true;
10508
10669
  }
@@ -10514,7 +10675,7 @@ function isStructurallyComplete(env) {
10514
10675
  /**
10515
10676
  * Synthesize a short human-readable label for a serialized A2UI action
10516
10677
  * message, so the chat composition can render "Search flights" instead
10517
- * of a raw `{"version":"v1","action":...}` JSON dump as a user bubble.
10678
+ * of a raw `{"version":"v0.9","action":...}` JSON dump as a user bubble.
10518
10679
  *
10519
10680
  * Per the A2UI spec, action messages flow on the client → agent
10520
10681
  * return channel and are framed as typed events (closer to tool calls
@@ -10531,7 +10692,7 @@ function isStructurallyComplete(env) {
10531
10692
  * "Booking submit"). Used when no label was stamped — typically
10532
10693
  * because the source component isn't a Button-with-Text-child.
10533
10694
  *
10534
- * Returns null for any content that isn't a v1 A2UI action message;
10695
+ * Returns null for any content that isn't an A2UI action message;
10535
10696
  * callers should fall back to the original content in that case.
10536
10697
  *
10537
10698
  * Design context: a previous iteration shipped a hardcoded
@@ -10560,7 +10721,8 @@ function a2uiActionLabel(content) {
10560
10721
  }
10561
10722
  if (!isRecord$1(parsed))
10562
10723
  return null;
10563
- if (parsed['version'] !== 'v1')
10724
+ const version = parsed['version'];
10725
+ if (typeof version !== 'string' || !version.startsWith('v'))
10564
10726
  return null;
10565
10727
  const action = parsed['action'];
10566
10728
  if (!isRecord$1(action))
@@ -11256,10 +11418,10 @@ class ChatComponent {
11256
11418
  // A2UI/json-render markers in the content string.
11257
11419
  const projectedContent = m.content;
11258
11420
  if (typeof projectedContent === 'string' && projectedContent.length > 0) {
11259
- // A2UI v1 envelope keys (canonical Google shape).
11260
- if (projectedContent.includes('"surfaceUpdate"')
11261
- || projectedContent.includes('"beginRendering"')
11262
- || projectedContent.includes('"dataModelUpdate"')) {
11421
+ // A2UI v0.9 envelope keys (canonical Google shape).
11422
+ if (projectedContent.includes('"createSurface"')
11423
+ || projectedContent.includes('"updateComponents"')
11424
+ || projectedContent.includes('"updateDataModel"')) {
11263
11425
  return true;
11264
11426
  }
11265
11427
  // json-render spec shape — looks like `{ "root": "...", "elements": ... }`.
@@ -11548,7 +11710,7 @@ class ChatComponent {
11548
11710
  </div>
11549
11711
  </div>
11550
11712
  }
11551
- `, isInline: true, styles: [":host{font-family:var(--tplane-chat-font-family);color:var(--tplane-chat-text)}\n", ":host{display:flex;flex-direction:column;flex:1 1 auto;height:100%;min-height:0;max-height:100%;overflow:hidden;background:var(--tplane-chat-bg)}:host>chat-welcome{display:flex;flex:1 1 auto;width:100%}.chat-shell{display:flex;flex:1;min-height:0;overflow:hidden}.chat-shell__sidebar{width:240px;flex-shrink:0;border-right:1px solid var(--tplane-chat-separator);background:var(--tplane-chat-surface-alt);overflow-y:auto;display:none}@media(min-width:768px){.chat-shell__sidebar{display:block}}.chat-shell__main{flex:1;min-width:0;display:flex;flex-direction:column;min-height:0}.chat-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:60px 20px;color:var(--tplane-chat-text-muted);text-align:center;flex:1;min-height:0}.chat-empty[hidden]{display:none}.chat-empty__title{font-size:1.125rem;font-weight:500;color:var(--tplane-chat-text);margin:0}.chat-empty__sub{margin:0;font-size:var(--tplane-chat-font-size-sm)}.chat-scroll{flex:1;min-height:0;overflow-y:auto;padding-top:var(--tplane-chat-edge-pad)}.chat-scroll::-webkit-scrollbar{width:6px}.chat-scroll::-webkit-scrollbar-thumb{background:var(--tplane-chat-separator);border-radius:10px}[chatFooter]{padding-bottom:var(--tplane-chat-edge-pad)}.chat-footer-wrap{position:relative}\n"], dependencies: [{ kind: "component", type: ChatWindowComponent, selector: "chat-window" }, { kind: "component", type: ChatMessageListComponent, selector: "chat-message-list", inputs: ["agent"] }, { kind: "directive", type: MessageTemplateDirective, selector: "ng-template[chatMessageTemplate]", inputs: ["chatMessageTemplate"] }, { kind: "component", type: ChatMessageComponent, selector: "chat-message", inputs: ["role", "current", "streaming", "prevRole", "message"] }, { kind: "component", type: ChatInputComponent, selector: "chat-input", inputs: ["agent", "submitOnEnter", "placeholder", "showStopButton"], outputs: ["submitted", "stopped"] }, { kind: "component", type: ChatTypingIndicatorComponent, selector: "chat-typing-indicator", inputs: ["agent"] }, { kind: "component", type: ChatErrorComponent, selector: "chat-error", inputs: ["agent"] }, { kind: "component", type: ChatThreadListComponent, selector: "chat-thread-list", inputs: ["threads", "activeThreadId", "showNewThreadButton", "actions", "mode", "projects"], outputs: ["threadSelected", "newThreadRequested"] }, { kind: "component", type: ChatGenerativeUiComponent, selector: "chat-generative-ui", inputs: ["spec", "registry", "store", "handlers", "loading"], outputs: ["events"] }, { kind: "component", type: ChatStreamingMdComponent, selector: "chat-streaming-md", inputs: ["document", "viewRegistry"] }, { kind: "component", type: ChatToolCallsComponent, selector: "chat-tool-calls", inputs: ["agent", "message", "grouping", "groupSummary", "excludeToolNames"] }, { kind: "component", type: ChatToolViewsComponent, selector: "chat-tool-views", inputs: ["agent", "message", "views", "store", "handlers"], outputs: ["events"] }, { kind: "component", type: A2uiSurfaceComponent, selector: "a2ui-surface", inputs: ["surface", "state", "catalog", "handlers", "surfaceFallback"], outputs: ["events", "action"] }, { kind: "component", type: ChatMessageActionsComponent, selector: "chat-message-actions", inputs: ["content", "disabled"], outputs: ["regenerate", "rate", "contentCopied"] }, { kind: "component", type: ChatWelcomeComponent, selector: "chat-welcome" }, { kind: "component", type: ChatSelectComponent, selector: "chat-select", inputs: ["options", "value", "placeholder", "disabled", "menuLabel", "panelClass"], outputs: ["valueChange"] }, { kind: "component", type: ChatReasoningComponent, selector: "chat-reasoning", inputs: ["content", "delivery", "durationMs", "label", "defaultExpanded"] }, { kind: "component", type: ChatScrollBubbleComponent, selector: "chat-scroll-bubble", inputs: ["mode"], outputs: ["clicked"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11713
+ `, isInline: true, styles: [":host{font-family:var(--tplane-chat-font-family);color:var(--tplane-chat-text)}\n", ":host{display:flex;flex-direction:column;flex:1 1 auto;height:100%;min-height:0;max-height:100%;overflow:hidden;background:var(--tplane-chat-bg)}:host>chat-welcome{display:flex;flex:1 1 auto;width:100%}.chat-shell{display:flex;flex:1;min-height:0;overflow:hidden}.chat-shell__sidebar{width:240px;flex-shrink:0;border-right:1px solid var(--tplane-chat-separator);background:var(--tplane-chat-surface-alt);overflow-y:auto;display:none}@media(min-width:768px){.chat-shell__sidebar{display:block}}.chat-shell__main{flex:1;min-width:0;display:flex;flex-direction:column;min-height:0}.chat-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:60px 20px;color:var(--tplane-chat-text-muted);text-align:center;flex:1;min-height:0}.chat-empty[hidden]{display:none}.chat-empty__title{font-size:1.125rem;font-weight:500;color:var(--tplane-chat-text);margin:0}.chat-empty__sub{margin:0;font-size:var(--tplane-chat-font-size-sm)}.chat-scroll{flex:1;min-height:0;overflow-y:auto;padding-top:var(--tplane-chat-edge-pad)}.chat-scroll::-webkit-scrollbar{width:6px}.chat-scroll::-webkit-scrollbar-thumb{background:var(--tplane-chat-separator);border-radius:10px}[chatFooter]{padding-bottom:var(--tplane-chat-edge-pad)}.chat-footer-wrap{position:relative}\n"], dependencies: [{ kind: "component", type: ChatWindowComponent, selector: "chat-window" }, { kind: "component", type: ChatMessageListComponent, selector: "chat-message-list", inputs: ["agent"] }, { kind: "directive", type: MessageTemplateDirective, selector: "ng-template[chatMessageTemplate]", inputs: ["chatMessageTemplate"] }, { kind: "component", type: ChatMessageComponent, selector: "chat-message", inputs: ["role", "current", "streaming", "prevRole", "message"] }, { kind: "component", type: ChatInputComponent, selector: "chat-input", inputs: ["agent", "submitOnEnter", "placeholder", "showStopButton"], outputs: ["submitted", "stopped"] }, { kind: "component", type: ChatTypingIndicatorComponent, selector: "chat-typing-indicator", inputs: ["agent"] }, { kind: "component", type: ChatErrorComponent, selector: "chat-error", inputs: ["agent"] }, { kind: "component", type: ChatThreadListComponent, selector: "chat-thread-list", inputs: ["threads", "activeThreadId", "showNewThreadButton", "actions", "mode", "projects"], outputs: ["threadSelected", "newThreadRequested"] }, { kind: "component", type: ChatGenerativeUiComponent, selector: "chat-generative-ui", inputs: ["spec", "registry", "store", "handlers", "loading"], outputs: ["events"] }, { kind: "component", type: ChatStreamingMdComponent, selector: "chat-streaming-md", inputs: ["document", "viewRegistry"] }, { kind: "component", type: ChatToolCallsComponent, selector: "chat-tool-calls", inputs: ["agent", "message", "grouping", "groupSummary", "excludeToolNames"] }, { kind: "component", type: ChatToolViewsComponent, selector: "chat-tool-views", inputs: ["agent", "message", "views", "store", "handlers"], outputs: ["events"] }, { kind: "component", type: A2uiSurfaceComponent, selector: "a2ui-surface", inputs: ["surface", "state", "catalog", "handlers", "surfaceFallback"], outputs: ["events", "action", "validationError"] }, { kind: "component", type: ChatMessageActionsComponent, selector: "chat-message-actions", inputs: ["content", "disabled"], outputs: ["regenerate", "rate", "contentCopied"] }, { kind: "component", type: ChatWelcomeComponent, selector: "chat-welcome" }, { kind: "component", type: ChatSelectComponent, selector: "chat-select", inputs: ["options", "value", "placeholder", "disabled", "menuLabel", "panelClass"], outputs: ["valueChange"] }, { kind: "component", type: ChatReasoningComponent, selector: "chat-reasoning", inputs: ["content", "delivery", "durationMs", "label", "defaultExpanded"] }, { kind: "component", type: ChatScrollBubbleComponent, selector: "chat-scroll-bubble", inputs: ["mode"], outputs: ["clicked"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11552
11714
  }
11553
11715
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ChatComponent, decorators: [{
11554
11716
  type: Component,
@@ -12493,6 +12655,37 @@ const CHAT_DEBUG_INCLUDED = ngDevMode ||
12493
12655
 
12494
12656
  // libs/chat/src/lib/compositions/chat-sidenav/chat-sidenav.component.ts
12495
12657
  // SPDX-License-Identifier: MIT
12658
+ /**
12659
+ * The conversation sidebar: thread list, projects, search, and the new-chat
12660
+ * action. Pair it with a runtime's thread store (e.g. `LangGraphThreadsAdapter`)
12661
+ * and a {@link ThreadActionAdapter} for the per-row rename/delete/archive menu.
12662
+ *
12663
+ * **This component renders the sidebar only — it is not a layout wrapper.** Its
12664
+ * `<ng-content>` slots are all named (`sidenavHeader`, `sidenavPrimary`,
12665
+ * `sidenavSections`, `sidenavFooterLeft`, `sidenavFooterRight`,
12666
+ * `sidenavAccount`) and target regions *inside* the sidebar. There is no default
12667
+ * slot, so a `<chat>` placed between the tags is silently dropped. Render the
12668
+ * chat as a sibling and lay the two out yourself:
12669
+ *
12670
+ * @example
12671
+ * ```html
12672
+ * <chat-sidenav
12673
+ * [threads]="threads.threads()"
12674
+ * [activeThreadId]="activeThread()"
12675
+ * [actions]="threadActions"
12676
+ * [agent]="agent"
12677
+ * (newChat)="activeThread.set(null)"
12678
+ * (threadSelected)="activeThread.set($event)"
12679
+ * />
12680
+ * <main class="chat-pane">
12681
+ * <chat [agent]="agent" />
12682
+ * </main>
12683
+ * ```
12684
+ * ```css
12685
+ * :host { display: flex; height: 100dvh; }
12686
+ * .chat-pane { flex: 1; min-width: 0; }
12687
+ * ```
12688
+ */
12496
12689
  class ChatSidenavComponent {
12497
12690
  mode = input('expanded', ...(ngDevMode ? [{ debugName: "mode" }] : []));
12498
12691
  open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
@@ -13430,11 +13623,8 @@ function normalizeViewEntry(entry) {
13430
13623
  // SPDX-License-Identifier: MIT
13431
13624
  class A2uiAudioPlayerComponent {
13432
13625
  url = input('', ...(ngDevMode ? [{ debugName: "url" }] : []));
13433
- /** v1 canonical prop: short description / title rendered above the player. */
13626
+ /** v0.9 prop: short description / title rendered above the player. */
13434
13627
  description = input('', ...(ngDevMode ? [{ debugName: "description" }] : []));
13435
- /** v1 prop name: autoPlay (camelCase). */
13436
- autoPlay = input(false, ...(ngDevMode ? [{ debugName: "autoPlay" }] : []));
13437
- controls = input(true, ...(ngDevMode ? [{ debugName: "controls" }] : []));
13438
13628
  // Framework inputs required by the render harness.
13439
13629
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
13440
13630
  emit = input(() => { }, ...(ngDevMode ? [{ debugName: "emit" }] : []));
@@ -13442,7 +13632,7 @@ class A2uiAudioPlayerComponent {
13442
13632
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
13443
13633
  spec = input(undefined, ...(ngDevMode ? [{ debugName: "spec" }] : []));
13444
13634
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiAudioPlayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13445
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiAudioPlayerComponent, isStandalone: true, selector: "a2ui-audio-player", inputs: { url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, autoPlay: { classPropertyName: "autoPlay", publicName: "autoPlay", isSignal: true, isRequired: false, transformFunction: null }, controls: { classPropertyName: "controls", publicName: "controls", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13635
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiAudioPlayerComponent, isStandalone: true, selector: "a2ui-audio-player", inputs: { url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13446
13636
  <div class="a2ui-audio-wrap">
13447
13637
  @if (description()) {
13448
13638
  <span class="a2ui-audio-description">{{ description() }}</span>
@@ -13450,8 +13640,7 @@ class A2uiAudioPlayerComponent {
13450
13640
  <audio
13451
13641
  class="a2ui-audio"
13452
13642
  [src]="url()"
13453
- [autoplay]="autoPlay()"
13454
- [controls]="controls()"
13643
+ controls
13455
13644
  ></audio>
13456
13645
  </div>
13457
13646
  `, isInline: true, styles: [".a2ui-audio-wrap{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-audio-description{font-size:var(--a2ui-typography-caption-size);color:var(--a2ui-on-surface-variant)}.a2ui-audio{display:block;width:100%}\n"] });
@@ -13466,31 +13655,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
13466
13655
  <audio
13467
13656
  class="a2ui-audio"
13468
13657
  [src]="url()"
13469
- [autoplay]="autoPlay()"
13470
- [controls]="controls()"
13658
+ controls
13471
13659
  ></audio>
13472
13660
  </div>
13473
13661
  `, styles: [".a2ui-audio-wrap{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-audio-description{font-size:var(--a2ui-typography-caption-size);color:var(--a2ui-on-surface-variant)}.a2ui-audio{display:block;width:100%}\n"] }]
13474
- }], propDecorators: { url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], autoPlay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoPlay", required: false }] }], controls: [{ type: i0.Input, args: [{ isSignal: true, alias: "controls", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
13662
+ }], propDecorators: { url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
13475
13663
 
13476
13664
  // SPDX-License-Identifier: MIT
13665
+ const VARIANT_CLASS = {
13666
+ default: 'a2ui-btn a2ui-btn--default',
13667
+ primary: 'a2ui-btn a2ui-btn--primary',
13668
+ borderless: 'a2ui-btn a2ui-btn--borderless',
13669
+ };
13477
13670
  class A2uiButtonComponent {
13478
- /** v1: child Text component is rendered inside the button via childKeys. */
13671
+ /** v0.9: child Text component is rendered inside the button via childKeys. */
13479
13672
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
13480
13673
  spec = input.required(...(ngDevMode ? [{ debugName: "spec" }] : []));
13481
- primary = input(true, ...(ngDevMode ? [{ debugName: "primary" }] : []));
13674
+ /** v0.9 prop: visual style (default 'default'). */
13675
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : []));
13482
13676
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
13483
13677
  emit = input(() => { }, ...(ngDevMode ? [{ debugName: "emit" }] : []));
13484
13678
  // Framework inputs required by the render harness.
13485
13679
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
13486
13680
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
13681
+ cssClass() {
13682
+ return VARIANT_CLASS[this.variant()] ?? VARIANT_CLASS['default'];
13683
+ }
13487
13684
  handleClick() {
13488
13685
  this.emit()('click');
13489
13686
  }
13490
13687
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13491
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiButtonComponent, isStandalone: true, selector: "a2ui-button", inputs: { childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: true, transformFunction: null }, primary: { classPropertyName: "primary", publicName: "primary", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13688
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiButtonComponent, isStandalone: true, selector: "a2ui-button", inputs: { childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: true, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13492
13689
  <button
13493
- [class]="primary() ? 'a2ui-btn a2ui-btn--primary' : 'a2ui-btn a2ui-btn--secondary'"
13690
+ [class]="cssClass()"
13494
13691
  [disabled]="disabled()"
13495
13692
  (click)="handleClick()"
13496
13693
  >
@@ -13498,13 +13695,13 @@ class A2uiButtonComponent {
13498
13695
  <render-element [elementKey]="key" [spec]="spec()" />
13499
13696
  }
13500
13697
  </button>
13501
- `, isInline: true, styles: [".a2ui-btn{display:inline-flex;align-items:center;justify-content:center;padding:var(--a2ui-spacing-2) var(--a2ui-spacing-4);border-radius:var(--a2ui-shape-small);font-size:var(--a2ui-typography-body-size);font-weight:500;cursor:pointer;transition:background var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard),opacity var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard);border:none}.a2ui-btn:disabled{opacity:.5;cursor:not-allowed}.a2ui-btn--primary{background:var(--a2ui-primary);color:var(--a2ui-on-primary)}.a2ui-btn--primary:hover:not(:disabled){background:var(--a2ui-primary-hover)}.a2ui-btn--secondary{background:var(--a2ui-surface-variant);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline)}.a2ui-btn--secondary:hover:not(:disabled){background:var(--a2ui-outline)}\n"], dependencies: [{ kind: "component", type: RenderElementComponent, selector: "render-element", inputs: ["elementKey", "spec"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13698
+ `, isInline: true, styles: [".a2ui-btn{display:inline-flex;align-items:center;justify-content:center;padding:var(--a2ui-spacing-2) var(--a2ui-spacing-4);border-radius:var(--a2ui-shape-small);font-size:var(--a2ui-typography-body-size);font-weight:500;cursor:pointer;transition:background var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard),opacity var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard);border:none}.a2ui-btn:disabled{opacity:.5;cursor:not-allowed}.a2ui-btn--primary{background:var(--a2ui-primary);color:var(--a2ui-on-primary)}.a2ui-btn--primary:hover:not(:disabled){background:var(--a2ui-primary-hover)}.a2ui-btn--default{background:var(--a2ui-surface-variant);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline)}.a2ui-btn--default:hover:not(:disabled){background:var(--a2ui-outline)}.a2ui-btn--borderless{background:transparent;color:var(--a2ui-on-surface);border:none}.a2ui-btn--borderless:hover:not(:disabled){background:var(--a2ui-surface-variant)}\n"], dependencies: [{ kind: "component", type: RenderElementComponent, selector: "render-element", inputs: ["elementKey", "spec"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13502
13699
  }
13503
13700
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiButtonComponent, decorators: [{
13504
13701
  type: Component,
13505
13702
  args: [{ selector: 'a2ui-button', standalone: true, imports: [RenderElementComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
13506
13703
  <button
13507
- [class]="primary() ? 'a2ui-btn a2ui-btn--primary' : 'a2ui-btn a2ui-btn--secondary'"
13704
+ [class]="cssClass()"
13508
13705
  [disabled]="disabled()"
13509
13706
  (click)="handleClick()"
13510
13707
  >
@@ -13512,8 +13709,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
13512
13709
  <render-element [elementKey]="key" [spec]="spec()" />
13513
13710
  }
13514
13711
  </button>
13515
- `, styles: [".a2ui-btn{display:inline-flex;align-items:center;justify-content:center;padding:var(--a2ui-spacing-2) var(--a2ui-spacing-4);border-radius:var(--a2ui-shape-small);font-size:var(--a2ui-typography-body-size);font-weight:500;cursor:pointer;transition:background var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard),opacity var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard);border:none}.a2ui-btn:disabled{opacity:.5;cursor:not-allowed}.a2ui-btn--primary{background:var(--a2ui-primary);color:var(--a2ui-on-primary)}.a2ui-btn--primary:hover:not(:disabled){background:var(--a2ui-primary-hover)}.a2ui-btn--secondary{background:var(--a2ui-surface-variant);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline)}.a2ui-btn--secondary:hover:not(:disabled){background:var(--a2ui-outline)}\n"] }]
13516
- }], propDecorators: { childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: true }] }], primary: [{ type: i0.Input, args: [{ isSignal: true, alias: "primary", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
13712
+ `, styles: [".a2ui-btn{display:inline-flex;align-items:center;justify-content:center;padding:var(--a2ui-spacing-2) var(--a2ui-spacing-4);border-radius:var(--a2ui-shape-small);font-size:var(--a2ui-typography-body-size);font-weight:500;cursor:pointer;transition:background var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard),opacity var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard);border:none}.a2ui-btn:disabled{opacity:.5;cursor:not-allowed}.a2ui-btn--primary{background:var(--a2ui-primary);color:var(--a2ui-on-primary)}.a2ui-btn--primary:hover:not(:disabled){background:var(--a2ui-primary-hover)}.a2ui-btn--default{background:var(--a2ui-surface-variant);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline)}.a2ui-btn--default:hover:not(:disabled){background:var(--a2ui-outline)}.a2ui-btn--borderless{background:transparent;color:var(--a2ui-on-surface);border:none}.a2ui-btn--borderless:hover:not(:disabled){background:var(--a2ui-surface-variant)}\n"] }]
13713
+ }], propDecorators: { childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
13517
13714
 
13518
13715
  // SPDX-License-Identifier: MIT
13519
13716
  class A2uiCardComponent {
@@ -13556,83 +13753,302 @@ function emitBinding(host, bindings, prop, value) {
13556
13753
  class A2uiCheckBoxComponent {
13557
13754
  host = injectRenderHost();
13558
13755
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
13559
- /** v1 canonical prop: boolean checked state. */
13560
- value = input(undefined, ...(ngDevMode ? [{ debugName: "value" }] : []));
13561
- /** Pre-v1 alias retained for back-compat. */
13562
- checked = input(false, ...(ngDevMode ? [{ debugName: "checked" }] : []));
13756
+ /** v0.9 prop: boolean checked state. */
13757
+ value = input(false, ...(ngDevMode ? [{ debugName: "value" }] : []));
13758
+ /** Live validation message written by the surface's check gate
13759
+ * (bound to /_a2uiChecks/<id>); empty when valid. */
13760
+ errorText = input('', ...(ngDevMode ? [{ debugName: "errorText" }] : []));
13563
13761
  _bindings = input({}, ...(ngDevMode ? [{ debugName: "_bindings" }] : []));
13564
13762
  // Framework inputs required by the render harness.
13565
13763
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
13566
13764
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
13567
13765
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
13568
13766
  spec = input(undefined, ...(ngDevMode ? [{ debugName: "spec" }] : []));
13569
- effectiveValue = computed(() => this.value() ?? this.checked(), ...(ngDevMode ? [{ debugName: "effectiveValue" }] : []));
13570
13767
  onChange(event) {
13571
13768
  const val = event.target.checked;
13572
- // Emit on whichever binding the surface declared. v1 surfaces use
13573
- // `value`; pre-v1 used `checked`.
13574
- const bound = this._bindings();
13575
- if (bound['value']) {
13576
- emitBinding(this.host, bound, 'value', val);
13577
- }
13578
- else {
13579
- emitBinding(this.host, bound, 'checked', val);
13580
- }
13769
+ emitBinding(this.host, this._bindings(), 'value', val);
13581
13770
  }
13582
13771
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiCheckBoxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13583
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.6", type: A2uiCheckBoxComponent, isStandalone: true, selector: "a2ui-check-box", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, _bindings: { classPropertyName: "_bindings", publicName: "_bindings", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13772
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiCheckBoxComponent, isStandalone: true, selector: "a2ui-check-box", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, errorText: { classPropertyName: "errorText", publicName: "errorText", isSignal: true, isRequired: false, transformFunction: null }, _bindings: { classPropertyName: "_bindings", publicName: "_bindings", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13584
13773
  <label class="a2ui-cb">
13585
- <input type="checkbox" [checked]="effectiveValue()" (change)="onChange($event)" class="a2ui-cb__input" />
13774
+ <input type="checkbox" [checked]="value()" (change)="onChange($event)" class="a2ui-cb__input" />
13586
13775
  {{ label() }}
13587
13776
  </label>
13588
- `, isInline: true, styles: [".a2ui-cb{display:flex;align-items:center;gap:var(--a2ui-spacing-2);font-size:var(--a2ui-typography-body-size);cursor:pointer}.a2ui-cb__input{width:16px;height:16px;border-radius:var(--a2ui-shape-extra-small);cursor:pointer;accent-color:var(--a2ui-primary)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13777
+ @if (errorText()) {
13778
+ <div class="a2ui-check-error" role="alert">{{ errorText() }}</div>
13779
+ }
13780
+ `, isInline: true, styles: [".a2ui-cb{display:flex;align-items:center;gap:var(--a2ui-spacing-2);font-size:var(--a2ui-typography-body-size);cursor:pointer}.a2ui-cb__input{width:16px;height:16px;border-radius:var(--a2ui-shape-extra-small);cursor:pointer;accent-color:var(--a2ui-primary)}.a2ui-check-error{font-size:var(--a2ui-typography-label-size);color:var(--a2ui-error, #d33d55)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13589
13781
  }
13590
13782
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiCheckBoxComponent, decorators: [{
13591
13783
  type: Component,
13592
13784
  args: [{ selector: 'a2ui-check-box', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
13593
13785
  <label class="a2ui-cb">
13594
- <input type="checkbox" [checked]="effectiveValue()" (change)="onChange($event)" class="a2ui-cb__input" />
13786
+ <input type="checkbox" [checked]="value()" (change)="onChange($event)" class="a2ui-cb__input" />
13595
13787
  {{ label() }}
13596
13788
  </label>
13597
- `, styles: [".a2ui-cb{display:flex;align-items:center;gap:var(--a2ui-spacing-2);font-size:var(--a2ui-typography-body-size);cursor:pointer}.a2ui-cb__input{width:16px;height:16px;border-radius:var(--a2ui-shape-extra-small);cursor:pointer;accent-color:var(--a2ui-primary)}\n"] }]
13598
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }], _bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "_bindings", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
13789
+ @if (errorText()) {
13790
+ <div class="a2ui-check-error" role="alert">{{ errorText() }}</div>
13791
+ }
13792
+ `, styles: [".a2ui-cb{display:flex;align-items:center;gap:var(--a2ui-spacing-2);font-size:var(--a2ui-typography-body-size);cursor:pointer}.a2ui-cb__input{width:16px;height:16px;border-radius:var(--a2ui-shape-extra-small);cursor:pointer;accent-color:var(--a2ui-primary)}.a2ui-check-error{font-size:var(--a2ui-typography-label-size);color:var(--a2ui-error, #d33d55)}\n"] }]
13793
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], errorText: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorText", required: false }] }], _bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "_bindings", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
13599
13794
 
13600
13795
  // SPDX-License-Identifier: MIT
13601
- const ALIGN_MAP = {
13796
+ class A2uiChoicePickerComponent {
13797
+ static _idCounter = 0;
13798
+ /** Groups the radio inputs of this instance (mutuallyExclusive mode). */
13799
+ _groupName = `a2ui-choice-picker-${++A2uiChoicePickerComponent._idCounter}`;
13800
+ host = injectRenderHost();
13801
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
13802
+ /** v0.9 prop: current selection (string[]). Normalized in `valueArray`
13803
+ * because LLMs sometimes seed the data model with a scalar (e.g. `"5"`)
13804
+ * instead of an array (`["5"]`); we coerce so .includes() works either way. */
13805
+ value = input(undefined, ...(ngDevMode ? [{ debugName: "value" }] : []));
13806
+ /** Resolved options with plain string labels (surface-to-spec resolves DynamicString). */
13807
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
13808
+ /** v0.9 prop: 'mutuallyExclusive' (single-select, default) or 'multipleSelection'. */
13809
+ variant = input('mutuallyExclusive', ...(ngDevMode ? [{ debugName: "variant" }] : []));
13810
+ /** v0.9 prop: render as 'checkbox' rows (default) or 'chips'. */
13811
+ displayStyle = input('checkbox', ...(ngDevMode ? [{ debugName: "displayStyle" }] : []));
13812
+ /** v0.9 prop: when true, show a client-side option filter input. */
13813
+ filterable = input(false, ...(ngDevMode ? [{ debugName: "filterable" }] : []));
13814
+ /** Live validation message written by the surface's check gate
13815
+ * (bound to /_a2uiChecks/<id>); empty when valid. */
13816
+ errorText = input('', ...(ngDevMode ? [{ debugName: "errorText" }] : []));
13817
+ _bindings = input({}, ...(ngDevMode ? [{ debugName: "_bindings" }] : []));
13818
+ // Framework inputs required by the render harness.
13819
+ bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
13820
+ loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
13821
+ childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
13822
+ spec = input(undefined, ...(ngDevMode ? [{ debugName: "spec" }] : []));
13823
+ valueArray = computed(() => {
13824
+ const v = this.value();
13825
+ if (Array.isArray(v))
13826
+ return v;
13827
+ if (v == null || v === '')
13828
+ return [];
13829
+ return [String(v)];
13830
+ }, ...(ngDevMode ? [{ debugName: "valueArray" }] : []));
13831
+ isSingleSelect = computed(() => this.variant() !== 'multipleSelection', ...(ngDevMode ? [{ debugName: "isSingleSelect" }] : []));
13832
+ /** Local, client-side option filter (only rendered when filterable). */
13833
+ filterText = signal('', ...(ngDevMode ? [{ debugName: "filterText" }] : []));
13834
+ visibleOptions = computed(() => {
13835
+ const f = this.filterText().trim().toLowerCase();
13836
+ const opts = this.options();
13837
+ return f ? opts.filter(o => o.label.toLowerCase().includes(f)) : opts;
13838
+ }, ...(ngDevMode ? [{ debugName: "visibleOptions" }] : []));
13839
+ isSelected(value) {
13840
+ return this.valueArray().includes(value);
13841
+ }
13842
+ onFilterInput(event) {
13843
+ this.filterText.set(event.target.value);
13844
+ }
13845
+ onCheckChange(value, event) {
13846
+ const checked = event.target.checked;
13847
+ if (this.isSingleSelect()) {
13848
+ // Radio semantics: the chosen option replaces the selection. `value` is
13849
+ // a string list on the wire, so write a one-element array.
13850
+ if (checked)
13851
+ emitBinding(this.host, this._bindings(), 'value', [value]);
13852
+ return;
13853
+ }
13854
+ emitBinding(this.host, this._bindings(), 'value', this.toggled(value, checked));
13855
+ }
13856
+ onChipToggle(value) {
13857
+ if (this.isSingleSelect()) {
13858
+ emitBinding(this.host, this._bindings(), 'value', [value]);
13859
+ return;
13860
+ }
13861
+ const checked = !this.isSelected(value);
13862
+ emitBinding(this.host, this._bindings(), 'value', this.toggled(value, checked));
13863
+ }
13864
+ toggled(value, checked) {
13865
+ const current = [...this.valueArray()];
13866
+ const idx = current.indexOf(value);
13867
+ if (checked && idx === -1) {
13868
+ current.push(value);
13869
+ }
13870
+ else if (!checked && idx !== -1) {
13871
+ current.splice(idx, 1);
13872
+ }
13873
+ // Pass the updated array directly (typed value, no JSON stringification needed).
13874
+ return current;
13875
+ }
13876
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiChoicePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13877
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiChoicePickerComponent, isStandalone: true, selector: "a2ui-choice-picker", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, displayStyle: { classPropertyName: "displayStyle", publicName: "displayStyle", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, errorText: { classPropertyName: "errorText", publicName: "errorText", isSignal: true, isRequired: false, transformFunction: null }, _bindings: { classPropertyName: "_bindings", publicName: "_bindings", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13878
+ <div class="a2ui-cp">
13879
+ @if (label()) {
13880
+ <span class="a2ui-cp__label">{{ label() }}</span>
13881
+ }
13882
+
13883
+ @if (filterable()) {
13884
+ <input
13885
+ type="text"
13886
+ class="a2ui-cp__filter"
13887
+ placeholder="Filter options"
13888
+ [value]="filterText()"
13889
+ (input)="onFilterInput($event)"
13890
+ />
13891
+ }
13892
+
13893
+ @if (displayStyle() === 'chips') {
13894
+ <!-- Chips: toggle buttons. -->
13895
+ <div class="a2ui-cp__chips">
13896
+ @for (opt of visibleOptions(); track opt.value) {
13897
+ <button
13898
+ type="button"
13899
+ [class]="isSelected(opt.value) ? 'a2ui-cp__chip a2ui-cp__chip--selected' : 'a2ui-cp__chip'"
13900
+ [attr.aria-pressed]="isSelected(opt.value)"
13901
+ (click)="onChipToggle(opt.value)"
13902
+ >{{ opt.label }}</button>
13903
+ }
13904
+ </div>
13905
+ } @else {
13906
+ <!-- Checkbox style: radio rows (mutuallyExclusive) or checkbox rows (multipleSelection). -->
13907
+ <div class="a2ui-cp__checks">
13908
+ @for (opt of visibleOptions(); track opt.value) {
13909
+ <label class="a2ui-cp__check-row">
13910
+ <input
13911
+ [type]="isSingleSelect() ? 'radio' : 'checkbox'"
13912
+ class="a2ui-cp__checkbox"
13913
+ [attr.name]="isSingleSelect() ? _groupName : null"
13914
+ [checked]="isSelected(opt.value)"
13915
+ (change)="onCheckChange(opt.value, $event)"
13916
+ />
13917
+ {{ opt.label }}
13918
+ </label>
13919
+ }
13920
+ </div>
13921
+ }
13922
+ @if (errorText()) {
13923
+ <div class="a2ui-check-error" role="alert">{{ errorText() }}</div>
13924
+ }
13925
+ </div>
13926
+ `, isInline: true, styles: [".a2ui-cp{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-cp__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-cp__filter{padding:var(--a2ui-spacing-1) var(--a2ui-spacing-2);font-size:var(--a2ui-typography-caption-size);border-radius:var(--a2ui-shape-small);background:var(--a2ui-input-bg);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);outline:none;transition:border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard)}.a2ui-cp__filter:focus{outline:var(--a2ui-focus-ring-width) solid var(--a2ui-focus-ring-color);outline-offset:2px;border-color:var(--a2ui-primary)}.a2ui-cp__checks{display:flex;flex-direction:column;gap:var(--a2ui-spacing-2)}.a2ui-cp__check-row{display:flex;align-items:center;gap:var(--a2ui-spacing-2);font-size:var(--a2ui-typography-body-size);cursor:pointer}.a2ui-cp__checkbox{width:16px;height:16px;border-radius:var(--a2ui-shape-extra-small);cursor:pointer;accent-color:var(--a2ui-primary)}.a2ui-cp__chips{display:flex;flex-wrap:wrap;gap:var(--a2ui-spacing-2)}.a2ui-cp__chip{padding:var(--a2ui-spacing-1) var(--a2ui-spacing-3);font-size:var(--a2ui-typography-body-size);border-radius:var(--a2ui-shape-large, 9999px);background:var(--a2ui-surface-variant);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);cursor:pointer;transition:background var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard),border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard)}.a2ui-cp__chip--selected{background:var(--a2ui-primary);color:var(--a2ui-on-primary);border-color:var(--a2ui-primary)}.a2ui-check-error{font-size:var(--a2ui-typography-label-size);color:var(--a2ui-error, #d33d55)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13927
+ }
13928
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiChoicePickerComponent, decorators: [{
13929
+ type: Component,
13930
+ args: [{ selector: 'a2ui-choice-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
13931
+ <div class="a2ui-cp">
13932
+ @if (label()) {
13933
+ <span class="a2ui-cp__label">{{ label() }}</span>
13934
+ }
13935
+
13936
+ @if (filterable()) {
13937
+ <input
13938
+ type="text"
13939
+ class="a2ui-cp__filter"
13940
+ placeholder="Filter options"
13941
+ [value]="filterText()"
13942
+ (input)="onFilterInput($event)"
13943
+ />
13944
+ }
13945
+
13946
+ @if (displayStyle() === 'chips') {
13947
+ <!-- Chips: toggle buttons. -->
13948
+ <div class="a2ui-cp__chips">
13949
+ @for (opt of visibleOptions(); track opt.value) {
13950
+ <button
13951
+ type="button"
13952
+ [class]="isSelected(opt.value) ? 'a2ui-cp__chip a2ui-cp__chip--selected' : 'a2ui-cp__chip'"
13953
+ [attr.aria-pressed]="isSelected(opt.value)"
13954
+ (click)="onChipToggle(opt.value)"
13955
+ >{{ opt.label }}</button>
13956
+ }
13957
+ </div>
13958
+ } @else {
13959
+ <!-- Checkbox style: radio rows (mutuallyExclusive) or checkbox rows (multipleSelection). -->
13960
+ <div class="a2ui-cp__checks">
13961
+ @for (opt of visibleOptions(); track opt.value) {
13962
+ <label class="a2ui-cp__check-row">
13963
+ <input
13964
+ [type]="isSingleSelect() ? 'radio' : 'checkbox'"
13965
+ class="a2ui-cp__checkbox"
13966
+ [attr.name]="isSingleSelect() ? _groupName : null"
13967
+ [checked]="isSelected(opt.value)"
13968
+ (change)="onCheckChange(opt.value, $event)"
13969
+ />
13970
+ {{ opt.label }}
13971
+ </label>
13972
+ }
13973
+ </div>
13974
+ }
13975
+ @if (errorText()) {
13976
+ <div class="a2ui-check-error" role="alert">{{ errorText() }}</div>
13977
+ }
13978
+ </div>
13979
+ `, styles: [".a2ui-cp{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-cp__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-cp__filter{padding:var(--a2ui-spacing-1) var(--a2ui-spacing-2);font-size:var(--a2ui-typography-caption-size);border-radius:var(--a2ui-shape-small);background:var(--a2ui-input-bg);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);outline:none;transition:border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard)}.a2ui-cp__filter:focus{outline:var(--a2ui-focus-ring-width) solid var(--a2ui-focus-ring-color);outline-offset:2px;border-color:var(--a2ui-primary)}.a2ui-cp__checks{display:flex;flex-direction:column;gap:var(--a2ui-spacing-2)}.a2ui-cp__check-row{display:flex;align-items:center;gap:var(--a2ui-spacing-2);font-size:var(--a2ui-typography-body-size);cursor:pointer}.a2ui-cp__checkbox{width:16px;height:16px;border-radius:var(--a2ui-shape-extra-small);cursor:pointer;accent-color:var(--a2ui-primary)}.a2ui-cp__chips{display:flex;flex-wrap:wrap;gap:var(--a2ui-spacing-2)}.a2ui-cp__chip{padding:var(--a2ui-spacing-1) var(--a2ui-spacing-3);font-size:var(--a2ui-typography-body-size);border-radius:var(--a2ui-shape-large, 9999px);background:var(--a2ui-surface-variant);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);cursor:pointer;transition:background var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard),border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard)}.a2ui-cp__chip--selected{background:var(--a2ui-primary);color:var(--a2ui-on-primary);border-color:var(--a2ui-primary)}.a2ui-check-error{font-size:var(--a2ui-typography-label-size);color:var(--a2ui-error, #d33d55)}\n"] }]
13980
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], displayStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayStyle", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], errorText: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorText", required: false }] }], _bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "_bindings", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
13981
+
13982
+ // SPDX-License-Identifier: MIT
13983
+ const ALIGN_MAP$1 = {
13602
13984
  start: 'flex-start', center: 'center', end: 'flex-end', stretch: 'stretch',
13603
13985
  };
13986
+ /** justify 'stretch' has no justify-content equivalent — children grow instead
13987
+ * (see the --justify-stretch class below). */
13988
+ const JUSTIFY_MAP$1 = {
13989
+ start: 'flex-start', center: 'center', end: 'flex-end',
13990
+ spaceAround: 'space-around', spaceBetween: 'space-between',
13991
+ spaceEvenly: 'space-evenly', stretch: 'normal',
13992
+ };
13604
13993
  class A2uiColumnComponent {
13605
13994
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
13606
13995
  spec = input.required(...(ngDevMode ? [{ debugName: "spec" }] : []));
13607
- gap = input(3, ...(ngDevMode ? [{ debugName: "gap" }] : []));
13608
- alignment = input('start', ...(ngDevMode ? [{ debugName: "alignment" }] : []));
13609
- distribution = input('start', ...(ngDevMode ? [{ debugName: "distribution" }] : []));
13996
+ /** v0.9 prop: cross-axis alignment (default 'stretch'). */
13997
+ align = input('stretch', ...(ngDevMode ? [{ debugName: "align" }] : []));
13998
+ /** v0.9 prop: main-axis distribution (default 'start'). */
13999
+ justify = input('start', ...(ngDevMode ? [{ debugName: "justify" }] : []));
14000
+ /** Not part of the v0.9 catalog — kept for json-render generative-ui
14001
+ * specs, which may set a numeric spacing unit (multiples of 4px) or a
14002
+ * named size. Unset falls back to the CSS default gap. */
14003
+ gap = input(undefined, ...(ngDevMode ? [{ debugName: "gap" }] : []));
13610
14004
  // Framework inputs required by the render harness.
13611
14005
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
13612
14006
  emit = input(() => { }, ...(ngDevMode ? [{ debugName: "emit" }] : []));
13613
14007
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
13614
- alignItems = computed(() => ALIGN_MAP[this.alignment()] ?? 'flex-start', ...(ngDevMode ? [{ debugName: "alignItems" }] : []));
13615
- /** Convert the Tailwind gap unit (multiples of 4px) to pixels. */
13616
- gapPx = computed(() => this.gap() * 4, ...(ngDevMode ? [{ debugName: "gapPx" }] : []));
14008
+ alignItems = computed(() => ALIGN_MAP$1[this.align()] ?? 'stretch', ...(ngDevMode ? [{ debugName: "alignItems" }] : []));
14009
+ justifyContent = computed(() => JUSTIFY_MAP$1[this.justify()] ?? 'flex-start', ...(ngDevMode ? [{ debugName: "justifyContent" }] : []));
14010
+ cssClass = computed(() => this.justify() === 'stretch' ? 'a2ui-col a2ui-col--justify-stretch' : 'a2ui-col', ...(ngDevMode ? [{ debugName: "cssClass" }] : []));
14011
+ gapPx = computed(() => {
14012
+ const g = this.gap();
14013
+ if (typeof g === 'number' && Number.isFinite(g))
14014
+ return g * 4;
14015
+ if (g === 'small')
14016
+ return 8;
14017
+ if (g === 'medium')
14018
+ return 12;
14019
+ if (g === 'large')
14020
+ return 16;
14021
+ return null;
14022
+ }, ...(ngDevMode ? [{ debugName: "gapPx" }] : []));
13617
14023
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiColumnComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13618
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiColumnComponent, isStandalone: true, selector: "a2ui-column", inputs: { childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: true, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, alignment: { classPropertyName: "alignment", publicName: "alignment", isSignal: true, isRequired: false, transformFunction: null }, distribution: { classPropertyName: "distribution", publicName: "distribution", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13619
- <div class="a2ui-col" [style.align-items]="alignItems()" [style.gap.px]="gapPx()">
14024
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiColumnComponent, isStandalone: true, selector: "a2ui-column", inputs: { childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: true, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, justify: { classPropertyName: "justify", publicName: "justify", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14025
+ <div
14026
+ [class]="cssClass()"
14027
+ [style.align-items]="alignItems()"
14028
+ [style.justify-content]="justifyContent()"
14029
+ [style.gap.px]="gapPx()"
14030
+ >
13620
14031
  @for (key of childKeys(); track key) {
13621
14032
  <render-element [elementKey]="key" [spec]="spec()" />
13622
14033
  }
13623
14034
  </div>
13624
- `, isInline: true, styles: [".a2ui-col{display:flex;flex-direction:column}\n"], dependencies: [{ kind: "component", type: RenderElementComponent, selector: "render-element", inputs: ["elementKey", "spec"] }] });
14035
+ `, isInline: true, styles: [".a2ui-col{display:flex;flex-direction:column;gap:var(--a2ui-spacing-3)}.a2ui-col--justify-stretch>render-element{flex:1}\n"], dependencies: [{ kind: "component", type: RenderElementComponent, selector: "render-element", inputs: ["elementKey", "spec"] }] });
13625
14036
  }
13626
14037
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiColumnComponent, decorators: [{
13627
14038
  type: Component,
13628
14039
  args: [{ selector: 'a2ui-column', standalone: true, imports: [RenderElementComponent], template: `
13629
- <div class="a2ui-col" [style.align-items]="alignItems()" [style.gap.px]="gapPx()">
14040
+ <div
14041
+ [class]="cssClass()"
14042
+ [style.align-items]="alignItems()"
14043
+ [style.justify-content]="justifyContent()"
14044
+ [style.gap.px]="gapPx()"
14045
+ >
13630
14046
  @for (key of childKeys(); track key) {
13631
14047
  <render-element [elementKey]="key" [spec]="spec()" />
13632
14048
  }
13633
14049
  </div>
13634
- `, styles: [".a2ui-col{display:flex;flex-direction:column}\n"] }]
13635
- }], propDecorators: { childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: true }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], alignment: [{ type: i0.Input, args: [{ isSignal: true, alias: "alignment", required: false }] }], distribution: [{ type: i0.Input, args: [{ isSignal: true, alias: "distribution", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
14050
+ `, styles: [".a2ui-col{display:flex;flex-direction:column;gap:var(--a2ui-spacing-3)}.a2ui-col--justify-stretch>render-element{flex:1}\n"] }]
14051
+ }], propDecorators: { childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: true }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], justify: [{ type: i0.Input, args: [{ isSignal: true, alias: "justify", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
13636
14052
 
13637
14053
  // SPDX-License-Identifier: MIT
13638
14054
  class A2uiDateTimeInputComponent {
@@ -13640,12 +14056,19 @@ class A2uiDateTimeInputComponent {
13640
14056
  _inputId = `a2ui-date-time-input-${++A2uiDateTimeInputComponent._idCounter}`;
13641
14057
  host = injectRenderHost();
13642
14058
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
13643
- /** v1 prop: value (resolved DynamicString). */
14059
+ /** v0.9 prop: ISO 8601 value (resolved DynamicString). Still renders when absent. */
13644
14060
  value = input('', ...(ngDevMode ? [{ debugName: "value" }] : []));
13645
- /** v1 prop: enableDate — include date portion. */
14061
+ /** v0.9 prop: enableDate — include date portion. */
13646
14062
  enableDate = input(true, ...(ngDevMode ? [{ debugName: "enableDate" }] : []));
13647
- /** v1 prop: enableTime — include time portion. */
14063
+ /** v0.9 prop: enableTime — include time portion. */
13648
14064
  enableTime = input(false, ...(ngDevMode ? [{ debugName: "enableTime" }] : []));
14065
+ /** v0.9 prop: ISO lower bound mapped to the native input's min. */
14066
+ min = input(undefined, ...(ngDevMode ? [{ debugName: "min" }] : []));
14067
+ /** v0.9 prop: ISO upper bound mapped to the native input's max. */
14068
+ max = input(undefined, ...(ngDevMode ? [{ debugName: "max" }] : []));
14069
+ /** Live validation message written by the surface's check gate
14070
+ * (bound to /_a2uiChecks/<id>); empty when valid. */
14071
+ errorText = input('', ...(ngDevMode ? [{ debugName: "errorText" }] : []));
13649
14072
  _bindings = input({}, ...(ngDevMode ? [{ debugName: "_bindings" }] : []));
13650
14073
  // Framework inputs required by the render harness.
13651
14074
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
@@ -13667,7 +14090,7 @@ class A2uiDateTimeInputComponent {
13667
14090
  emitBinding(this.host, this._bindings(), 'value', val);
13668
14091
  }
13669
14092
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiDateTimeInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13670
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiDateTimeInputComponent, isStandalone: true, selector: "a2ui-date-time-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, enableDate: { classPropertyName: "enableDate", publicName: "enableDate", isSignal: true, isRequired: false, transformFunction: null }, enableTime: { classPropertyName: "enableTime", publicName: "enableTime", isSignal: true, isRequired: false, transformFunction: null }, _bindings: { classPropertyName: "_bindings", publicName: "_bindings", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14093
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiDateTimeInputComponent, isStandalone: true, selector: "a2ui-date-time-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, enableDate: { classPropertyName: "enableDate", publicName: "enableDate", isSignal: true, isRequired: false, transformFunction: null }, enableTime: { classPropertyName: "enableTime", publicName: "enableTime", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, errorText: { classPropertyName: "errorText", publicName: "errorText", isSignal: true, isRequired: false, transformFunction: null }, _bindings: { classPropertyName: "_bindings", publicName: "_bindings", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13671
14094
  <div class="a2ui-dti">
13672
14095
  @if (label()) {
13673
14096
  <label [htmlFor]="_inputId" class="a2ui-dti__label">{{ label() }}</label>
@@ -13676,11 +14099,16 @@ class A2uiDateTimeInputComponent {
13676
14099
  [id]="_inputId"
13677
14100
  [type]="htmlInputType()"
13678
14101
  [value]="value()"
14102
+ [attr.min]="min() || null"
14103
+ [attr.max]="max() || null"
13679
14104
  class="a2ui-dti__input"
13680
14105
  (change)="onChange($event)"
13681
14106
  />
14107
+ @if (errorText()) {
14108
+ <div class="a2ui-check-error" role="alert">{{ errorText() }}</div>
14109
+ }
13682
14110
  </div>
13683
- `, isInline: true, styles: [".a2ui-dti{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-dti__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-dti__input{padding:var(--a2ui-spacing-2) var(--a2ui-spacing-3);font-size:var(--a2ui-typography-body-size);border-radius:var(--a2ui-shape-small);background:var(--a2ui-input-bg);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);outline:none;transition:border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard)}.a2ui-dti__input:focus{outline:var(--a2ui-focus-ring-width) solid var(--a2ui-focus-ring-color);outline-offset:2px;border-color:var(--a2ui-primary)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14111
+ `, isInline: true, styles: [".a2ui-dti{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-dti__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-dti__input{padding:var(--a2ui-spacing-2) var(--a2ui-spacing-3);font-size:var(--a2ui-typography-body-size);border-radius:var(--a2ui-shape-small);background:var(--a2ui-input-bg);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);outline:none;transition:border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard)}.a2ui-dti__input:focus{outline:var(--a2ui-focus-ring-width) solid var(--a2ui-focus-ring-color);outline-offset:2px;border-color:var(--a2ui-primary)}.a2ui-check-error{font-size:var(--a2ui-typography-label-size);color:var(--a2ui-error, #d33d55)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13684
14112
  }
13685
14113
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiDateTimeInputComponent, decorators: [{
13686
14114
  type: Component,
@@ -13693,21 +14121,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
13693
14121
  [id]="_inputId"
13694
14122
  [type]="htmlInputType()"
13695
14123
  [value]="value()"
14124
+ [attr.min]="min() || null"
14125
+ [attr.max]="max() || null"
13696
14126
  class="a2ui-dti__input"
13697
14127
  (change)="onChange($event)"
13698
14128
  />
14129
+ @if (errorText()) {
14130
+ <div class="a2ui-check-error" role="alert">{{ errorText() }}</div>
14131
+ }
13699
14132
  </div>
13700
- `, styles: [".a2ui-dti{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-dti__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-dti__input{padding:var(--a2ui-spacing-2) var(--a2ui-spacing-3);font-size:var(--a2ui-typography-body-size);border-radius:var(--a2ui-shape-small);background:var(--a2ui-input-bg);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);outline:none;transition:border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard)}.a2ui-dti__input:focus{outline:var(--a2ui-focus-ring-width) solid var(--a2ui-focus-ring-color);outline-offset:2px;border-color:var(--a2ui-primary)}\n"] }]
13701
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], enableDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableDate", required: false }] }], enableTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableTime", required: false }] }], _bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "_bindings", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14133
+ `, styles: [".a2ui-dti{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-dti__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-dti__input{padding:var(--a2ui-spacing-2) var(--a2ui-spacing-3);font-size:var(--a2ui-typography-body-size);border-radius:var(--a2ui-shape-small);background:var(--a2ui-input-bg);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);outline:none;transition:border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard)}.a2ui-dti__input:focus{outline:var(--a2ui-focus-ring-width) solid var(--a2ui-focus-ring-color);outline-offset:2px;border-color:var(--a2ui-primary)}.a2ui-check-error{font-size:var(--a2ui-typography-label-size);color:var(--a2ui-error, #d33d55)}\n"] }]
14134
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], enableDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableDate", required: false }] }], enableTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableTime", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], errorText: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorText", required: false }] }], _bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "_bindings", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
13702
14135
 
13703
14136
  // SPDX-License-Identifier: MIT
13704
14137
  class A2uiDividerComponent {
13705
- /** Canonical v1 spec name. The LLM emits this. */
13706
- axis = input(undefined, ...(ngDevMode ? [{ debugName: "axis" }] : []));
13707
- /** Older alias retained for json-render usage and back-compat. */
13708
- direction = input('horizontal', ...(ngDevMode ? [{ debugName: "direction" }] : []));
13709
- /** Effective axis — `axis` wins if provided, otherwise fall back to `direction`. */
13710
- orientation = computed(() => this.axis() ?? this.direction(), ...(ngDevMode ? [{ debugName: "orientation" }] : []));
14138
+ /** v0.9 prop: divider axis (default 'horizontal'). */
14139
+ axis = input('horizontal', ...(ngDevMode ? [{ debugName: "axis" }] : []));
14140
+ orientation = computed(() => this.axis(), ...(ngDevMode ? [{ debugName: "orientation" }] : []));
13711
14141
  // Framework inputs required by the render harness.
13712
14142
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
13713
14143
  emit = input(() => { }, ...(ngDevMode ? [{ debugName: "emit" }] : []));
@@ -13715,7 +14145,7 @@ class A2uiDividerComponent {
13715
14145
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
13716
14146
  spec = input(undefined, ...(ngDevMode ? [{ debugName: "spec" }] : []));
13717
14147
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiDividerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13718
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiDividerComponent, isStandalone: true, selector: "a2ui-divider", inputs: { axis: { classPropertyName: "axis", publicName: "axis", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14148
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiDividerComponent, isStandalone: true, selector: "a2ui-divider", inputs: { axis: { classPropertyName: "axis", publicName: "axis", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13719
14149
  @if (orientation() === 'vertical') {
13720
14150
  <div class="a2ui-divider a2ui-divider--vertical"></div>
13721
14151
  } @else {
@@ -13732,7 +14162,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
13732
14162
  <hr class="a2ui-divider a2ui-divider--horizontal" />
13733
14163
  }
13734
14164
  `, styles: [".a2ui-divider--horizontal{display:block;width:100%;border:none;border-top:1px solid var(--a2ui-outline);margin:var(--a2ui-spacing-2) 0}.a2ui-divider--vertical{display:inline-block;align-self:stretch;width:1px;background:var(--a2ui-outline);margin:0 var(--a2ui-spacing-2)}\n"] }]
13735
- }], propDecorators: { axis: [{ type: i0.Input, args: [{ isSignal: true, alias: "axis", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14165
+ }], propDecorators: { axis: [{ type: i0.Input, args: [{ isSignal: true, alias: "axis", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
13736
14166
 
13737
14167
  // SPDX-License-Identifier: MIT
13738
14168
  /**
@@ -13749,119 +14179,118 @@ function toMaterialSymbolName(name) {
13749
14179
  return name.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();
13750
14180
  }
13751
14181
  class A2uiIconComponent {
13752
- /** v1 canonical prop. */
14182
+ /** v0.9 prop: a Material Symbols name (string) or an inline `{ svgPath }`. */
13753
14183
  name = input(undefined, ...(ngDevMode ? [{ debugName: "name" }] : []));
13754
- /** Pre-v1 alias retained for back-compat. */
13755
- icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : []));
13756
- size = input(null, ...(ngDevMode ? [{ debugName: "size" }] : []));
13757
14184
  // Framework inputs required by the render harness.
13758
14185
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
13759
14186
  emit = input(() => { }, ...(ngDevMode ? [{ debugName: "emit" }] : []));
13760
14187
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
13761
14188
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
13762
14189
  spec = input(undefined, ...(ngDevMode ? [{ debugName: "spec" }] : []));
13763
- effectiveName = computed(() => this.name() ?? this.icon(), ...(ngDevMode ? [{ debugName: "effectiveName" }] : []));
14190
+ /** Inline SVG path when `name` is the `{ svgPath }` object form. */
14191
+ svgPath = computed(() => {
14192
+ const n = this.name();
14193
+ return typeof n === 'object' && n !== null && typeof n.svgPath === 'string'
14194
+ ? n.svgPath
14195
+ : null;
14196
+ }, ...(ngDevMode ? [{ debugName: "svgPath" }] : []));
14197
+ /** The string ligature name when `name` is a string. */
14198
+ ligatureName = computed(() => typeof this.name() === 'string' ? this.name() : '', ...(ngDevMode ? [{ debugName: "ligatureName" }] : []));
13764
14199
  /** The effective name as a Material Symbols ligature (camelCase → snake_case). */
13765
- glyphName = computed(() => toMaterialSymbolName(this.effectiveName()), ...(ngDevMode ? [{ debugName: "glyphName" }] : []));
14200
+ glyphName = computed(() => toMaterialSymbolName(this.ligatureName()), ...(ngDevMode ? [{ debugName: "glyphName" }] : []));
13766
14201
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13767
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiIconComponent, isStandalone: true, selector: "a2ui-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13768
- @if (effectiveName(); as name) {
14202
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiIconComponent, isStandalone: true, selector: "a2ui-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14203
+ @if (svgPath(); as path) {
14204
+ <svg
14205
+ class="a2ui-icon a2ui-icon--svg"
14206
+ viewBox="0 -960 960 960"
14207
+ fill="currentColor"
14208
+ role="img"
14209
+ aria-hidden="true"
14210
+ ><path [attr.d]="path" /></svg>
14211
+ } @else if (ligatureName(); as name) {
13769
14212
  <span
13770
14213
  class="a2ui-icon material-symbols-outlined"
13771
- [style.font-size]="size() ? size() + 'px' : '1.125rem'"
13772
14214
  [attr.aria-label]="name"
13773
14215
  role="img"
13774
14216
  >{{ glyphName() }}</span>
13775
14217
  }
13776
- `, isInline: true, styles: [".a2ui-icon{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;line-height:1;letter-spacing:normal;text-transform:none;white-space:nowrap;word-wrap:normal;direction:ltr;font-feature-settings:\"liga\";-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24;color:currentColor;display:inline-flex;align-items:center;justify-content:center;-webkit-user-select:none;user-select:none}\n"] });
14218
+ `, isInline: true, styles: [".a2ui-icon{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;font-size:1.125rem;line-height:1;letter-spacing:normal;text-transform:none;white-space:nowrap;word-wrap:normal;direction:ltr;font-feature-settings:\"liga\";-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24;color:currentColor;display:inline-flex;align-items:center;justify-content:center;-webkit-user-select:none;user-select:none}.a2ui-icon--svg{width:1.125rem;height:1.125rem}\n"] });
13777
14219
  }
13778
14220
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiIconComponent, decorators: [{
13779
14221
  type: Component,
13780
14222
  args: [{ selector: 'a2ui-icon', standalone: true, template: `
13781
- @if (effectiveName(); as name) {
14223
+ @if (svgPath(); as path) {
14224
+ <svg
14225
+ class="a2ui-icon a2ui-icon--svg"
14226
+ viewBox="0 -960 960 960"
14227
+ fill="currentColor"
14228
+ role="img"
14229
+ aria-hidden="true"
14230
+ ><path [attr.d]="path" /></svg>
14231
+ } @else if (ligatureName(); as name) {
13782
14232
  <span
13783
14233
  class="a2ui-icon material-symbols-outlined"
13784
- [style.font-size]="size() ? size() + 'px' : '1.125rem'"
13785
14234
  [attr.aria-label]="name"
13786
14235
  role="img"
13787
14236
  >{{ glyphName() }}</span>
13788
14237
  }
13789
- `, styles: [".a2ui-icon{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;line-height:1;letter-spacing:normal;text-transform:none;white-space:nowrap;word-wrap:normal;direction:ltr;font-feature-settings:\"liga\";-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24;color:currentColor;display:inline-flex;align-items:center;justify-content:center;-webkit-user-select:none;user-select:none}\n"] }]
13790
- }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14238
+ `, styles: [".a2ui-icon{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;font-size:1.125rem;line-height:1;letter-spacing:normal;text-transform:none;white-space:nowrap;word-wrap:normal;direction:ltr;font-feature-settings:\"liga\";-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24;color:currentColor;display:inline-flex;align-items:center;justify-content:center;-webkit-user-select:none;user-select:none}.a2ui-icon--svg{width:1.125rem;height:1.125rem}\n"] }]
14239
+ }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
13791
14240
 
13792
14241
  // SPDX-License-Identifier: MIT
13793
- const USAGE_HINT_STYLE = {
13794
- icon: { maxWidth: '24px', aspectRatio: '1 / 1' },
13795
- avatar: { maxWidth: '48px', aspectRatio: '1 / 1', borderRadius: '50%' },
13796
- smallFeature: { maxWidth: '160px' },
13797
- mediumFeature: { maxWidth: '320px' },
13798
- largeFeature: { maxWidth: '480px' },
13799
- header: { maxWidth: '100%', aspectRatio: '16 / 5' },
14242
+ const FIT_MAP = {
14243
+ contain: 'contain',
14244
+ cover: 'cover',
14245
+ fill: 'fill',
14246
+ none: 'none',
14247
+ scaleDown: 'scale-down',
13800
14248
  };
13801
14249
  class A2uiImageComponent {
13802
14250
  url = input('', ...(ngDevMode ? [{ debugName: "url" }] : []));
13803
- alt = input('', ...(ngDevMode ? [{ debugName: "alt" }] : []));
13804
- width = input(null, ...(ngDevMode ? [{ debugName: "width" }] : []));
13805
- height = input(null, ...(ngDevMode ? [{ debugName: "height" }] : []));
13806
- /** v1 prop: CSS object-fit equivalent. */
13807
- fit = input(undefined, ...(ngDevMode ? [{ debugName: "fit" }] : []));
13808
- /** v1 prop: sizing preset. */
13809
- usageHint = input(undefined, ...(ngDevMode ? [{ debugName: "usageHint" }] : []));
14251
+ /** v0.9 prop: alt text / accessible description. */
14252
+ description = input('', ...(ngDevMode ? [{ debugName: "description" }] : []));
14253
+ /** v0.9 prop: CSS object-fit equivalent ('scaleDown' 'scale-down'). */
14254
+ fit = input('fill', ...(ngDevMode ? [{ debugName: "fit" }] : []));
14255
+ /** v0.9 prop: sizing preset. */
14256
+ variant = input('mediumFeature', ...(ngDevMode ? [{ debugName: "variant" }] : []));
13810
14257
  // Framework inputs required by the render harness.
13811
14258
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
13812
14259
  emit = input(() => { }, ...(ngDevMode ? [{ debugName: "emit" }] : []));
13813
14260
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
13814
14261
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
13815
14262
  spec = input(undefined, ...(ngDevMode ? [{ debugName: "spec" }] : []));
13816
- explicitWidth() {
13817
- return this.width() != null ? this.width() + 'px' : null;
13818
- }
13819
- explicitHeight() {
13820
- return this.height() != null ? this.height() + 'px' : null;
13821
- }
13822
- hintStyle() {
13823
- const h = this.usageHint();
13824
- return h ? USAGE_HINT_STYLE[h] : null;
13825
- }
14263
+ objectFit = computed(() => FIT_MAP[this.fit()] ?? 'fill', ...(ngDevMode ? [{ debugName: "objectFit" }] : []));
14264
+ cssClass = computed(() => `a2ui-img a2ui-img--${this.variant()}`, ...(ngDevMode ? [{ debugName: "cssClass" }] : []));
13826
14265
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiImageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13827
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.6", type: A2uiImageComponent, isStandalone: true, selector: "a2ui-image", inputs: { url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, fit: { classPropertyName: "fit", publicName: "fit", isSignal: true, isRequired: false, transformFunction: null }, usageHint: { classPropertyName: "usageHint", publicName: "usageHint", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14266
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.6", type: A2uiImageComponent, isStandalone: true, selector: "a2ui-image", inputs: { url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, fit: { classPropertyName: "fit", publicName: "fit", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13828
14267
  <img
13829
- class="a2ui-img"
14268
+ [class]="cssClass()"
13830
14269
  [src]="url()"
13831
- [alt]="alt()"
13832
- [style.width]="explicitWidth()"
13833
- [style.height]="explicitHeight()"
13834
- [style.object-fit]="fit()"
13835
- [style.max-width]="hintStyle()?.maxWidth"
13836
- [style.aspect-ratio]="hintStyle()?.aspectRatio || null"
13837
- [style.border-radius]="hintStyle()?.borderRadius || null"
14270
+ [alt]="description()"
14271
+ [style.object-fit]="objectFit()"
13838
14272
  />
13839
- `, isInline: true, styles: [".a2ui-img{display:block;max-width:100%;border-radius:var(--a2ui-shape-extra-small)}\n"] });
14273
+ `, isInline: true, styles: [".a2ui-img{display:block;max-width:100%;border-radius:var(--a2ui-shape-extra-small)}.a2ui-img--icon{width:24px;height:24px}.a2ui-img--avatar{width:40px;height:40px;border-radius:50%}.a2ui-img--smallFeature{width:120px}.a2ui-img--mediumFeature{width:240px}.a2ui-img--largeFeature{width:400px}.a2ui-img--header{width:100%;aspect-ratio:16 / 5}\n"] });
13840
14274
  }
13841
14275
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiImageComponent, decorators: [{
13842
14276
  type: Component,
13843
14277
  args: [{ selector: 'a2ui-image', standalone: true, template: `
13844
14278
  <img
13845
- class="a2ui-img"
14279
+ [class]="cssClass()"
13846
14280
  [src]="url()"
13847
- [alt]="alt()"
13848
- [style.width]="explicitWidth()"
13849
- [style.height]="explicitHeight()"
13850
- [style.object-fit]="fit()"
13851
- [style.max-width]="hintStyle()?.maxWidth"
13852
- [style.aspect-ratio]="hintStyle()?.aspectRatio || null"
13853
- [style.border-radius]="hintStyle()?.borderRadius || null"
14281
+ [alt]="description()"
14282
+ [style.object-fit]="objectFit()"
13854
14283
  />
13855
- `, styles: [".a2ui-img{display:block;max-width:100%;border-radius:var(--a2ui-shape-extra-small)}\n"] }]
13856
- }], propDecorators: { url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], fit: [{ type: i0.Input, args: [{ isSignal: true, alias: "fit", required: false }] }], usageHint: [{ type: i0.Input, args: [{ isSignal: true, alias: "usageHint", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14284
+ `, styles: [".a2ui-img{display:block;max-width:100%;border-radius:var(--a2ui-shape-extra-small)}.a2ui-img--icon{width:24px;height:24px}.a2ui-img--avatar{width:40px;height:40px;border-radius:50%}.a2ui-img--smallFeature{width:120px}.a2ui-img--mediumFeature{width:240px}.a2ui-img--largeFeature{width:400px}.a2ui-img--header{width:100%;aspect-ratio:16 / 5}\n"] }]
14285
+ }], propDecorators: { url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], fit: [{ type: i0.Input, args: [{ isSignal: true, alias: "fit", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
13857
14286
 
13858
14287
  // SPDX-License-Identifier: MIT
13859
14288
  class A2uiListComponent {
13860
14289
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
13861
14290
  spec = input.required(...(ngDevMode ? [{ debugName: "spec" }] : []));
13862
14291
  direction = input('vertical', ...(ngDevMode ? [{ debugName: "direction" }] : []));
13863
- /** v1 canonical prop: cross-axis alignment. */
13864
- alignment = input(undefined, ...(ngDevMode ? [{ debugName: "alignment" }] : []));
14292
+ /** v0.9 prop: cross-axis alignment (default 'stretch'). */
14293
+ align = input('stretch', ...(ngDevMode ? [{ debugName: "align" }] : []));
13865
14294
  // Framework inputs required by the render harness.
13866
14295
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
13867
14296
  emit = input(() => { }, ...(ngDevMode ? [{ debugName: "emit" }] : []));
@@ -13872,15 +14301,13 @@ class A2uiListComponent {
13872
14301
  : 'a2ui-list--vertical';
13873
14302
  }, ...(ngDevMode ? [{ debugName: "listClass" }] : []));
13874
14303
  alignmentCss = computed(() => {
13875
- const a = this.alignment();
13876
- if (!a)
13877
- return null;
14304
+ const a = this.align();
13878
14305
  return a === 'start' ? 'flex-start'
13879
14306
  : a === 'end' ? 'flex-end'
13880
14307
  : a; // center / stretch are valid CSS values as-is
13881
14308
  }, ...(ngDevMode ? [{ debugName: "alignmentCss" }] : []));
13882
14309
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13883
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiListComponent, isStandalone: true, selector: "a2ui-list", inputs: { childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: true, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, alignment: { classPropertyName: "alignment", publicName: "alignment", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14310
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiListComponent, isStandalone: true, selector: "a2ui-list", inputs: { childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: true, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13884
14311
  <div [class]="listClass()" [style.align-items]="alignmentCss()">
13885
14312
  @for (key of childKeys(); track key) {
13886
14313
  <render-element [elementKey]="key" [spec]="spec()" />
@@ -13897,18 +14324,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
13897
14324
  }
13898
14325
  </div>
13899
14326
  `, styles: [".a2ui-list--vertical{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1);overflow-y:auto;max-height:384px}.a2ui-list--horizontal{display:flex;flex-direction:row;gap:var(--a2ui-spacing-1);overflow-x:auto}\n"] }]
13900
- }], propDecorators: { childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: true }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], alignment: [{ type: i0.Input, args: [{ isSignal: true, alias: "alignment", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
14327
+ }], propDecorators: { childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: true }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
13901
14328
 
13902
14329
  // SPDX-License-Identifier: MIT
13903
14330
  class A2uiModalComponent {
13904
14331
  /**
13905
- * v1: childKeys[0] = entryPointChild (inline trigger),
13906
- * childKeys[1] = contentChild (modal body).
14332
+ * v0.9: childKeys[0] = trigger (inline entry point),
14333
+ * childKeys[1] = content (modal body).
13907
14334
  */
13908
14335
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
13909
14336
  spec = input.required(...(ngDevMode ? [{ debugName: "spec" }] : []));
13910
- /** Resolved title string (from optional title DynamicString). */
13911
- title = input('', ...(ngDevMode ? [{ debugName: "title" }] : []));
13912
14337
  // Framework inputs required by the render harness.
13913
14338
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
13914
14339
  emit = input(() => { }, ...(ngDevMode ? [{ debugName: "emit" }] : []));
@@ -13917,7 +14342,7 @@ class A2uiModalComponent {
13917
14342
  entryPointKey = computed(() => this.childKeys()[0] ?? null, ...(ngDevMode ? [{ debugName: "entryPointKey" }] : []));
13918
14343
  contentKey = computed(() => this.childKeys()[1] ?? null, ...(ngDevMode ? [{ debugName: "contentKey" }] : []));
13919
14344
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13920
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiModalComponent, isStandalone: true, selector: "a2ui-modal", inputs: { childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14345
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiModalComponent, isStandalone: true, selector: "a2ui-modal", inputs: { childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: true, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
13921
14346
  <!-- Entry point (trigger): always rendered inline, e.g. a button. -->
13922
14347
  @if (entryPointKey(); as epKey) {
13923
14348
  <div
@@ -13950,16 +14375,13 @@ class A2uiModalComponent {
13950
14375
  (keydown.space)="open.set(false)"
13951
14376
  ></div>
13952
14377
  <div class="a2ui-modal__panel">
13953
- @if (title()) {
13954
- <h2 class="a2ui-modal__title">{{ title() }}</h2>
13955
- }
13956
14378
  @if (contentKey(); as cKey) {
13957
14379
  <render-element [elementKey]="cKey" [spec]="spec()" />
13958
14380
  }
13959
14381
  </div>
13960
14382
  </div>
13961
14383
  }
13962
- `, isInline: true, styles: [".a2ui-modal__trigger{display:contents}.a2ui-modal__overlay{position:fixed;inset:0;z-index:50;display:flex;align-items:center;justify-content:center}.a2ui-modal__backdrop{position:absolute;inset:0;background:var(--a2ui-scrim);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.a2ui-modal__panel{position:relative;background:var(--a2ui-surface);border:1px solid var(--a2ui-outline);border-radius:var(--a2ui-shape-medium);padding:var(--a2ui-spacing-5);max-width:512px;width:100%;margin:0 var(--a2ui-spacing-4);box-shadow:var(--a2ui-elevation-4)}.a2ui-modal__title{font-size:var(--a2ui-typography-h4-size);font-weight:var(--a2ui-typography-h4-weight);margin:0 0 var(--a2ui-spacing-4)}\n"], dependencies: [{ kind: "component", type: RenderElementComponent, selector: "render-element", inputs: ["elementKey", "spec"] }] });
14384
+ `, isInline: true, styles: [".a2ui-modal__trigger{display:contents}.a2ui-modal__overlay{position:fixed;inset:0;z-index:50;display:flex;align-items:center;justify-content:center}.a2ui-modal__backdrop{position:absolute;inset:0;background:var(--a2ui-scrim);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.a2ui-modal__panel{position:relative;background:var(--a2ui-surface);border:1px solid var(--a2ui-outline);border-radius:var(--a2ui-shape-medium);padding:var(--a2ui-spacing-5);max-width:512px;width:100%;margin:0 var(--a2ui-spacing-4);box-shadow:var(--a2ui-elevation-4)}\n"], dependencies: [{ kind: "component", type: RenderElementComponent, selector: "render-element", inputs: ["elementKey", "spec"] }] });
13963
14385
  }
13964
14386
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiModalComponent, decorators: [{
13965
14387
  type: Component,
@@ -13996,160 +14418,60 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
13996
14418
  (keydown.space)="open.set(false)"
13997
14419
  ></div>
13998
14420
  <div class="a2ui-modal__panel">
13999
- @if (title()) {
14000
- <h2 class="a2ui-modal__title">{{ title() }}</h2>
14001
- }
14002
14421
  @if (contentKey(); as cKey) {
14003
14422
  <render-element [elementKey]="cKey" [spec]="spec()" />
14004
14423
  }
14005
14424
  </div>
14006
14425
  </div>
14007
14426
  }
14008
- `, styles: [".a2ui-modal__trigger{display:contents}.a2ui-modal__overlay{position:fixed;inset:0;z-index:50;display:flex;align-items:center;justify-content:center}.a2ui-modal__backdrop{position:absolute;inset:0;background:var(--a2ui-scrim);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.a2ui-modal__panel{position:relative;background:var(--a2ui-surface);border:1px solid var(--a2ui-outline);border-radius:var(--a2ui-shape-medium);padding:var(--a2ui-spacing-5);max-width:512px;width:100%;margin:0 var(--a2ui-spacing-4);box-shadow:var(--a2ui-elevation-4)}.a2ui-modal__title{font-size:var(--a2ui-typography-h4-size);font-weight:var(--a2ui-typography-h4-weight);margin:0 0 var(--a2ui-spacing-4)}\n"] }]
14009
- }], propDecorators: { childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
14010
-
14011
- // SPDX-License-Identifier: MIT
14012
- class A2uiMultipleChoiceComponent {
14013
- host = injectRenderHost();
14014
- label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
14015
- /** Resolved current selections from surface-to-spec. Normalized in
14016
- * `selectionsArray` because LLMs sometimes seed the data model with a
14017
- * scalar (e.g. `"5"`) instead of an array (`["5"]`); we coerce so
14018
- * .includes() works either way. */
14019
- selections = input(undefined, ...(ngDevMode ? [{ debugName: "selections" }] : []));
14020
- selectionsArray = computed(() => {
14021
- const v = this.selections();
14022
- if (Array.isArray(v))
14023
- return v;
14024
- if (v == null || v === '')
14025
- return [];
14026
- return [String(v)];
14027
- }, ...(ngDevMode ? [{ debugName: "selectionsArray" }] : []));
14028
- /** Resolved options with plain string labels (surface-to-spec resolves DynamicString). */
14029
- options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
14030
- /** When ≤ 1 — render as single-select <select>; otherwise multi-select checkboxes. */
14031
- maxAllowedSelections = input(1, ...(ngDevMode ? [{ debugName: "maxAllowedSelections" }] : []));
14032
- _bindings = input({}, ...(ngDevMode ? [{ debugName: "_bindings" }] : []));
14033
- // Framework inputs required by the render harness.
14034
- bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
14035
- loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
14036
- childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
14037
- spec = input(undefined, ...(ngDevMode ? [{ debugName: "spec" }] : []));
14038
- isSingleSelect = computed(() => this.maxAllowedSelections() <= 1, ...(ngDevMode ? [{ debugName: "isSingleSelect" }] : []));
14039
- isSelected(value) {
14040
- return this.selectionsArray().includes(value);
14041
- }
14042
- onSelectChange(event) {
14043
- const val = event.target.value;
14044
- emitBinding(this.host, this._bindings(), 'selections', val);
14045
- }
14046
- onCheckChange(value, event) {
14047
- const checked = event.target.checked;
14048
- const current = [...this.selectionsArray()];
14049
- const idx = current.indexOf(value);
14050
- if (checked && idx === -1) {
14051
- current.push(value);
14052
- }
14053
- else if (!checked && idx !== -1) {
14054
- current.splice(idx, 1);
14055
- }
14056
- // Pass the updated selections array directly (typed value, no JSON stringification needed).
14057
- emitBinding(this.host, this._bindings(), 'selections', current);
14058
- }
14059
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiMultipleChoiceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14060
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiMultipleChoiceComponent, isStandalone: true, selector: "a2ui-multiple-choice", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, selections: { classPropertyName: "selections", publicName: "selections", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, maxAllowedSelections: { classPropertyName: "maxAllowedSelections", publicName: "maxAllowedSelections", isSignal: true, isRequired: false, transformFunction: null }, _bindings: { classPropertyName: "_bindings", publicName: "_bindings", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14061
- <div class="a2ui-mc">
14062
- @if (label()) {
14063
- <span class="a2ui-mc__label">{{ label() }}</span>
14064
- }
14065
-
14066
- @if (isSingleSelect()) {
14067
- <!-- Single-select: HTML <select> -->
14068
- <select class="a2ui-mc__select" (change)="onSelectChange($event)">
14069
- @for (opt of options(); track opt.value) {
14070
- <option [value]="opt.value" [selected]="isSelected(opt.value)">{{ opt.label }}</option>
14071
- }
14072
- </select>
14073
- } @else {
14074
- <!-- Multi-select: checkbox list -->
14075
- <div class="a2ui-mc__checks">
14076
- @for (opt of options(); track opt.value) {
14077
- <label class="a2ui-mc__check-row">
14078
- <input
14079
- type="checkbox"
14080
- class="a2ui-mc__checkbox"
14081
- [checked]="isSelected(opt.value)"
14082
- (change)="onCheckChange(opt.value, $event)"
14083
- />
14084
- {{ opt.label }}
14085
- </label>
14086
- }
14087
- </div>
14088
- }
14089
- </div>
14090
- `, isInline: true, styles: [".a2ui-mc{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-mc__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-mc__select{padding:var(--a2ui-spacing-2) var(--a2ui-spacing-3);font-size:var(--a2ui-typography-body-size);border-radius:var(--a2ui-shape-small);background:var(--a2ui-input-bg);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);outline:none;transition:border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard)}.a2ui-mc__select:focus{outline:var(--a2ui-focus-ring-width) solid var(--a2ui-focus-ring-color);outline-offset:2px;border-color:var(--a2ui-primary)}.a2ui-mc__checks{display:flex;flex-direction:column;gap:var(--a2ui-spacing-2)}.a2ui-mc__check-row{display:flex;align-items:center;gap:var(--a2ui-spacing-2);font-size:var(--a2ui-typography-body-size);cursor:pointer}.a2ui-mc__checkbox{width:16px;height:16px;border-radius:var(--a2ui-shape-extra-small);cursor:pointer;accent-color:var(--a2ui-primary)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14091
- }
14092
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiMultipleChoiceComponent, decorators: [{
14093
- type: Component,
14094
- args: [{ selector: 'a2ui-multiple-choice', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
14095
- <div class="a2ui-mc">
14096
- @if (label()) {
14097
- <span class="a2ui-mc__label">{{ label() }}</span>
14098
- }
14099
-
14100
- @if (isSingleSelect()) {
14101
- <!-- Single-select: HTML <select> -->
14102
- <select class="a2ui-mc__select" (change)="onSelectChange($event)">
14103
- @for (opt of options(); track opt.value) {
14104
- <option [value]="opt.value" [selected]="isSelected(opt.value)">{{ opt.label }}</option>
14105
- }
14106
- </select>
14107
- } @else {
14108
- <!-- Multi-select: checkbox list -->
14109
- <div class="a2ui-mc__checks">
14110
- @for (opt of options(); track opt.value) {
14111
- <label class="a2ui-mc__check-row">
14112
- <input
14113
- type="checkbox"
14114
- class="a2ui-mc__checkbox"
14115
- [checked]="isSelected(opt.value)"
14116
- (change)="onCheckChange(opt.value, $event)"
14117
- />
14118
- {{ opt.label }}
14119
- </label>
14120
- }
14121
- </div>
14122
- }
14123
- </div>
14124
- `, styles: [".a2ui-mc{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-mc__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-mc__select{padding:var(--a2ui-spacing-2) var(--a2ui-spacing-3);font-size:var(--a2ui-typography-body-size);border-radius:var(--a2ui-shape-small);background:var(--a2ui-input-bg);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);outline:none;transition:border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard)}.a2ui-mc__select:focus{outline:var(--a2ui-focus-ring-width) solid var(--a2ui-focus-ring-color);outline-offset:2px;border-color:var(--a2ui-primary)}.a2ui-mc__checks{display:flex;flex-direction:column;gap:var(--a2ui-spacing-2)}.a2ui-mc__check-row{display:flex;align-items:center;gap:var(--a2ui-spacing-2);font-size:var(--a2ui-typography-body-size);cursor:pointer}.a2ui-mc__checkbox{width:16px;height:16px;border-radius:var(--a2ui-shape-extra-small);cursor:pointer;accent-color:var(--a2ui-primary)}\n"] }]
14125
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], selections: [{ type: i0.Input, args: [{ isSignal: true, alias: "selections", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], maxAllowedSelections: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxAllowedSelections", required: false }] }], _bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "_bindings", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14427
+ `, styles: [".a2ui-modal__trigger{display:contents}.a2ui-modal__overlay{position:fixed;inset:0;z-index:50;display:flex;align-items:center;justify-content:center}.a2ui-modal__backdrop{position:absolute;inset:0;background:var(--a2ui-scrim);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.a2ui-modal__panel{position:relative;background:var(--a2ui-surface);border:1px solid var(--a2ui-outline);border-radius:var(--a2ui-shape-medium);padding:var(--a2ui-spacing-5);max-width:512px;width:100%;margin:0 var(--a2ui-spacing-4);box-shadow:var(--a2ui-elevation-4)}\n"] }]
14428
+ }], propDecorators: { childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: true }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
14126
14429
 
14127
14430
  // SPDX-License-Identifier: MIT
14128
- const ROW_ALIGN_MAP = {
14431
+ const ALIGN_MAP = {
14129
14432
  start: 'flex-start', center: 'center', end: 'flex-end', stretch: 'stretch',
14130
14433
  };
14131
- const ROW_JUSTIFY_MAP = {
14434
+ /** justify 'stretch' has no justify-content equivalent — children grow instead
14435
+ * (see the --justify-stretch class below). */
14436
+ const JUSTIFY_MAP = {
14132
14437
  start: 'flex-start', center: 'center', end: 'flex-end',
14133
- 'space-between': 'space-between', 'space-around': 'space-around',
14438
+ spaceAround: 'space-around', spaceBetween: 'space-between',
14439
+ spaceEvenly: 'space-evenly', stretch: 'normal',
14134
14440
  };
14135
14441
  class A2uiRowComponent {
14136
14442
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
14137
14443
  spec = input.required(...(ngDevMode ? [{ debugName: "spec" }] : []));
14138
- gap = input(3, ...(ngDevMode ? [{ debugName: "gap" }] : []));
14139
- alignment = input('start', ...(ngDevMode ? [{ debugName: "alignment" }] : []));
14140
- distribution = input('start', ...(ngDevMode ? [{ debugName: "distribution" }] : []));
14444
+ /** v0.9 prop: cross-axis alignment (default 'stretch'). */
14445
+ align = input('stretch', ...(ngDevMode ? [{ debugName: "align" }] : []));
14446
+ /** v0.9 prop: main-axis distribution (default 'start'). */
14447
+ justify = input('start', ...(ngDevMode ? [{ debugName: "justify" }] : []));
14448
+ /** Not part of the v0.9 catalog — kept for json-render generative-ui
14449
+ * specs, which may set a numeric spacing unit (multiples of 4px) or a
14450
+ * named size. Unset falls back to the CSS default gap. */
14451
+ gap = input(undefined, ...(ngDevMode ? [{ debugName: "gap" }] : []));
14141
14452
  // Framework inputs required by the render harness.
14142
14453
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
14143
14454
  emit = input(() => { }, ...(ngDevMode ? [{ debugName: "emit" }] : []));
14144
14455
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
14145
- alignItems = computed(() => ROW_ALIGN_MAP[this.alignment()] ?? 'flex-start', ...(ngDevMode ? [{ debugName: "alignItems" }] : []));
14146
- justifyContent = computed(() => ROW_JUSTIFY_MAP[this.distribution()] ?? 'flex-start', ...(ngDevMode ? [{ debugName: "justifyContent" }] : []));
14147
- /** Convert the gap unit (multiples of 4px) to pixels. */
14148
- gapPx = computed(() => this.gap() * 4, ...(ngDevMode ? [{ debugName: "gapPx" }] : []));
14456
+ alignItems = computed(() => ALIGN_MAP[this.align()] ?? 'stretch', ...(ngDevMode ? [{ debugName: "alignItems" }] : []));
14457
+ justifyContent = computed(() => JUSTIFY_MAP[this.justify()] ?? 'flex-start', ...(ngDevMode ? [{ debugName: "justifyContent" }] : []));
14458
+ cssClass = computed(() => this.justify() === 'stretch' ? 'a2ui-row a2ui-row--justify-stretch' : 'a2ui-row', ...(ngDevMode ? [{ debugName: "cssClass" }] : []));
14459
+ gapPx = computed(() => {
14460
+ const g = this.gap();
14461
+ if (typeof g === 'number' && Number.isFinite(g))
14462
+ return g * 4;
14463
+ if (g === 'small')
14464
+ return 8;
14465
+ if (g === 'medium')
14466
+ return 12;
14467
+ if (g === 'large')
14468
+ return 16;
14469
+ return null;
14470
+ }, ...(ngDevMode ? [{ debugName: "gapPx" }] : []));
14149
14471
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiRowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14150
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiRowComponent, isStandalone: true, selector: "a2ui-row", inputs: { childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: true, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, alignment: { classPropertyName: "alignment", publicName: "alignment", isSignal: true, isRequired: false, transformFunction: null }, distribution: { classPropertyName: "distribution", publicName: "distribution", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14472
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiRowComponent, isStandalone: true, selector: "a2ui-row", inputs: { childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: true, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, justify: { classPropertyName: "justify", publicName: "justify", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14151
14473
  <div
14152
- class="a2ui-row"
14474
+ [class]="cssClass()"
14153
14475
  [style.align-items]="alignItems()"
14154
14476
  [style.justify-content]="justifyContent()"
14155
14477
  [style.gap.px]="gapPx()"
@@ -14158,13 +14480,13 @@ class A2uiRowComponent {
14158
14480
  <render-element [elementKey]="key" [spec]="spec()" />
14159
14481
  }
14160
14482
  </div>
14161
- `, isInline: true, styles: [".a2ui-row{display:flex;flex-direction:row;flex-wrap:wrap}\n"], dependencies: [{ kind: "component", type: RenderElementComponent, selector: "render-element", inputs: ["elementKey", "spec"] }] });
14483
+ `, isInline: true, styles: [".a2ui-row{display:flex;flex-direction:row;flex-wrap:wrap;gap:var(--a2ui-spacing-3)}.a2ui-row--justify-stretch>render-element{flex:1}\n"], dependencies: [{ kind: "component", type: RenderElementComponent, selector: "render-element", inputs: ["elementKey", "spec"] }] });
14162
14484
  }
14163
14485
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiRowComponent, decorators: [{
14164
14486
  type: Component,
14165
14487
  args: [{ selector: 'a2ui-row', standalone: true, imports: [RenderElementComponent], template: `
14166
14488
  <div
14167
- class="a2ui-row"
14489
+ [class]="cssClass()"
14168
14490
  [style.align-items]="alignItems()"
14169
14491
  [style.justify-content]="justifyContent()"
14170
14492
  [style.gap.px]="gapPx()"
@@ -14173,8 +14495,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
14173
14495
  <render-element [elementKey]="key" [spec]="spec()" />
14174
14496
  }
14175
14497
  </div>
14176
- `, styles: [".a2ui-row{display:flex;flex-direction:row;flex-wrap:wrap}\n"] }]
14177
- }], propDecorators: { childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: true }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], alignment: [{ type: i0.Input, args: [{ isSignal: true, alias: "alignment", required: false }] }], distribution: [{ type: i0.Input, args: [{ isSignal: true, alias: "distribution", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
14498
+ `, styles: [".a2ui-row{display:flex;flex-direction:row;flex-wrap:wrap;gap:var(--a2ui-spacing-3)}.a2ui-row--justify-stretch>render-element{flex:1}\n"] }]
14499
+ }], propDecorators: { childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: true }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], justify: [{ type: i0.Input, args: [{ isSignal: true, alias: "justify", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
14178
14500
 
14179
14501
  // SPDX-License-Identifier: MIT
14180
14502
  class A2uiSliderComponent {
@@ -14182,13 +14504,15 @@ class A2uiSliderComponent {
14182
14504
  _inputId = `a2ui-slider-${++A2uiSliderComponent._idCounter}`;
14183
14505
  host = injectRenderHost();
14184
14506
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
14185
- /** v1 prop: value (resolved DynamicNumber). */
14507
+ /** v0.9 prop: value (resolved DynamicNumber). */
14186
14508
  value = input(0, ...(ngDevMode ? [{ debugName: "value" }] : []));
14187
- /** v1 prop: minValue. */
14188
- minValue = input(0, ...(ngDevMode ? [{ debugName: "minValue" }] : []));
14189
- /** v1 prop: maxValue. */
14190
- maxValue = input(100, ...(ngDevMode ? [{ debugName: "maxValue" }] : []));
14191
- step = input(1, ...(ngDevMode ? [{ debugName: "step" }] : []));
14509
+ /** v0.9 prop: lower bound (default 0). */
14510
+ min = input(0, ...(ngDevMode ? [{ debugName: "min" }] : []));
14511
+ /** v0.9 prop: upper bound. */
14512
+ max = input(100, ...(ngDevMode ? [{ debugName: "max" }] : []));
14513
+ /** Live validation message written by the surface's check gate
14514
+ * (bound to /_a2uiChecks/<id>); empty when valid. */
14515
+ errorText = input('', ...(ngDevMode ? [{ debugName: "errorText" }] : []));
14192
14516
  _bindings = input({}, ...(ngDevMode ? [{ debugName: "_bindings" }] : []));
14193
14517
  // Framework inputs required by the render harness.
14194
14518
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
@@ -14200,7 +14524,7 @@ class A2uiSliderComponent {
14200
14524
  emitBinding(this.host, this._bindings(), 'value', val);
14201
14525
  }
14202
14526
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiSliderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14203
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiSliderComponent, isStandalone: true, selector: "a2ui-slider", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, minValue: { classPropertyName: "minValue", publicName: "minValue", isSignal: true, isRequired: false, transformFunction: null }, maxValue: { classPropertyName: "maxValue", publicName: "maxValue", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, _bindings: { classPropertyName: "_bindings", publicName: "_bindings", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14527
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiSliderComponent, isStandalone: true, selector: "a2ui-slider", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, errorText: { classPropertyName: "errorText", publicName: "errorText", isSignal: true, isRequired: false, transformFunction: null }, _bindings: { classPropertyName: "_bindings", publicName: "_bindings", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14204
14528
  <div class="a2ui-slider">
14205
14529
  @if (label()) {
14206
14530
  <label [htmlFor]="_inputId" class="a2ui-slider__label">{{ label() }}: {{ value() }}</label>
@@ -14209,14 +14533,16 @@ class A2uiSliderComponent {
14209
14533
  [id]="_inputId"
14210
14534
  type="range"
14211
14535
  class="a2ui-slider__input"
14212
- [min]="minValue()"
14213
- [max]="maxValue()"
14214
- [step]="step()"
14536
+ [min]="min()"
14537
+ [max]="max()"
14215
14538
  [value]="value()"
14216
14539
  (input)="onInput($event)"
14217
14540
  />
14541
+ @if (errorText()) {
14542
+ <div class="a2ui-check-error" role="alert">{{ errorText() }}</div>
14543
+ }
14218
14544
  </div>
14219
- `, isInline: true, styles: [".a2ui-slider{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-slider__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-slider__input{width:100%;cursor:pointer;accent-color:var(--a2ui-primary)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14545
+ `, isInline: true, styles: [".a2ui-slider{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-slider__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-slider__input{width:100%;cursor:pointer;accent-color:var(--a2ui-primary)}.a2ui-check-error{font-size:var(--a2ui-typography-label-size);color:var(--a2ui-error, #d33d55)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14220
14546
  }
14221
14547
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiSliderComponent, decorators: [{
14222
14548
  type: Component,
@@ -14229,21 +14555,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
14229
14555
  [id]="_inputId"
14230
14556
  type="range"
14231
14557
  class="a2ui-slider__input"
14232
- [min]="minValue()"
14233
- [max]="maxValue()"
14234
- [step]="step()"
14558
+ [min]="min()"
14559
+ [max]="max()"
14235
14560
  [value]="value()"
14236
14561
  (input)="onInput($event)"
14237
14562
  />
14563
+ @if (errorText()) {
14564
+ <div class="a2ui-check-error" role="alert">{{ errorText() }}</div>
14565
+ }
14238
14566
  </div>
14239
- `, styles: [".a2ui-slider{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-slider__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-slider__input{width:100%;cursor:pointer;accent-color:var(--a2ui-primary)}\n"] }]
14240
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], minValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "minValue", required: false }] }], maxValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxValue", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], _bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "_bindings", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14567
+ `, styles: [".a2ui-slider{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-slider__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-slider__input{width:100%;cursor:pointer;accent-color:var(--a2ui-primary)}.a2ui-check-error{font-size:var(--a2ui-typography-label-size);color:var(--a2ui-error, #d33d55)}\n"] }]
14568
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], errorText: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorText", required: false }] }], _bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "_bindings", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14241
14569
 
14242
14570
  // SPDX-License-Identifier: MIT
14243
14571
  class A2uiTabsComponent {
14244
- /** Resolved tab titles from tabItems[*].title — produced by surface-to-spec. */
14572
+ /** Resolved tab titles from tabs[*].title — produced by surface-to-spec. */
14245
14573
  tabTitles = input([], ...(ngDevMode ? [{ debugName: "tabTitles" }] : []));
14246
- /** v1: each child key corresponds to a tab's contentChild (childKeys[i] ↔ tabTitles[i]). */
14574
+ /** v0.9: each child key corresponds to a tab's child (childKeys[i] ↔ tabTitles[i]). */
14247
14575
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
14248
14576
  spec = input.required(...(ngDevMode ? [{ debugName: "spec" }] : []));
14249
14577
  // Framework inputs required by the render harness.
@@ -14314,7 +14642,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
14314
14642
  // SPDX-License-Identifier: MIT
14315
14643
  class A2uiTextComponent {
14316
14644
  text = input('', ...(ngDevMode ? [{ debugName: "text" }] : []));
14317
- usageHint = input('body', ...(ngDevMode ? [{ debugName: "usageHint" }] : []));
14645
+ /** v0.9 prop: typography variant. */
14646
+ variant = input('body', ...(ngDevMode ? [{ debugName: "variant" }] : []));
14318
14647
  // Framework-mandated inputs the render harness passes to every element.
14319
14648
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
14320
14649
  emit = input(() => { }, ...(ngDevMode ? [{ debugName: "emit" }] : []));
@@ -14322,62 +14651,57 @@ class A2uiTextComponent {
14322
14651
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
14323
14652
  spec = input(undefined, ...(ngDevMode ? [{ debugName: "spec" }] : []));
14324
14653
  cssClass() {
14325
- return `a2ui-text-${this.usageHint()}`;
14654
+ return `a2ui-text-${this.variant()}`;
14326
14655
  }
14327
14656
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14328
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.6", type: A2uiTextComponent, isStandalone: true, selector: "a2ui-text", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, usageHint: { classPropertyName: "usageHint", publicName: "usageHint", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `<span [class]="cssClass()">{{ text() }}</span>`, isInline: true, styles: [".a2ui-text-h1{display:block;font-size:var(--a2ui-typography-h1-size);font-weight:var(--a2ui-typography-h1-weight);line-height:var(--a2ui-typography-h1-line-height);margin:0}.a2ui-text-h2{display:block;font-size:var(--a2ui-typography-h2-size);font-weight:var(--a2ui-typography-h2-weight);line-height:var(--a2ui-typography-h2-line-height);margin:0}.a2ui-text-h3{display:block;font-size:var(--a2ui-typography-h3-size);font-weight:var(--a2ui-typography-h3-weight);line-height:var(--a2ui-typography-h3-line-height);margin:0}.a2ui-text-h4{display:block;font-size:var(--a2ui-typography-h4-size);font-weight:var(--a2ui-typography-h4-weight);line-height:var(--a2ui-typography-h4-line-height);margin:0}.a2ui-text-h5{display:block;font-size:var(--a2ui-typography-h5-size);font-weight:var(--a2ui-typography-h5-weight);line-height:var(--a2ui-typography-h5-line-height);margin:0}.a2ui-text-caption{display:block;font-size:var(--a2ui-typography-caption-size);font-weight:var(--a2ui-typography-caption-weight);color:var(--a2ui-caption);line-height:var(--a2ui-typography-caption-line-height)}.a2ui-text-body{display:block;font-size:var(--a2ui-typography-body-size);font-weight:var(--a2ui-typography-body-weight);line-height:var(--a2ui-typography-body-line-height)}\n"] });
14657
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.6", type: A2uiTextComponent, isStandalone: true, selector: "a2ui-text", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `<span [class]="cssClass()">{{ text() }}</span>`, isInline: true, styles: [".a2ui-text-h1{display:block;font-size:var(--a2ui-typography-h1-size);font-weight:var(--a2ui-typography-h1-weight);line-height:var(--a2ui-typography-h1-line-height);margin:0}.a2ui-text-h2{display:block;font-size:var(--a2ui-typography-h2-size);font-weight:var(--a2ui-typography-h2-weight);line-height:var(--a2ui-typography-h2-line-height);margin:0}.a2ui-text-h3{display:block;font-size:var(--a2ui-typography-h3-size);font-weight:var(--a2ui-typography-h3-weight);line-height:var(--a2ui-typography-h3-line-height);margin:0}.a2ui-text-h4{display:block;font-size:var(--a2ui-typography-h4-size);font-weight:var(--a2ui-typography-h4-weight);line-height:var(--a2ui-typography-h4-line-height);margin:0}.a2ui-text-h5{display:block;font-size:var(--a2ui-typography-h5-size);font-weight:var(--a2ui-typography-h5-weight);line-height:var(--a2ui-typography-h5-line-height);margin:0}.a2ui-text-caption{display:block;font-size:var(--a2ui-typography-caption-size);font-weight:var(--a2ui-typography-caption-weight);color:var(--a2ui-caption);line-height:var(--a2ui-typography-caption-line-height)}.a2ui-text-body{display:block;font-size:var(--a2ui-typography-body-size);font-weight:var(--a2ui-typography-body-weight);line-height:var(--a2ui-typography-body-line-height)}\n"] });
14329
14658
  }
14330
14659
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiTextComponent, decorators: [{
14331
14660
  type: Component,
14332
14661
  args: [{ selector: 'a2ui-text', standalone: true, template: `<span [class]="cssClass()">{{ text() }}</span>`, styles: [".a2ui-text-h1{display:block;font-size:var(--a2ui-typography-h1-size);font-weight:var(--a2ui-typography-h1-weight);line-height:var(--a2ui-typography-h1-line-height);margin:0}.a2ui-text-h2{display:block;font-size:var(--a2ui-typography-h2-size);font-weight:var(--a2ui-typography-h2-weight);line-height:var(--a2ui-typography-h2-line-height);margin:0}.a2ui-text-h3{display:block;font-size:var(--a2ui-typography-h3-size);font-weight:var(--a2ui-typography-h3-weight);line-height:var(--a2ui-typography-h3-line-height);margin:0}.a2ui-text-h4{display:block;font-size:var(--a2ui-typography-h4-size);font-weight:var(--a2ui-typography-h4-weight);line-height:var(--a2ui-typography-h4-line-height);margin:0}.a2ui-text-h5{display:block;font-size:var(--a2ui-typography-h5-size);font-weight:var(--a2ui-typography-h5-weight);line-height:var(--a2ui-typography-h5-line-height);margin:0}.a2ui-text-caption{display:block;font-size:var(--a2ui-typography-caption-size);font-weight:var(--a2ui-typography-caption-weight);color:var(--a2ui-caption);line-height:var(--a2ui-typography-caption-line-height)}.a2ui-text-body{display:block;font-size:var(--a2ui-typography-body-size);font-weight:var(--a2ui-typography-body-weight);line-height:var(--a2ui-typography-body-line-height)}\n"] }]
14333
- }], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], usageHint: [{ type: i0.Input, args: [{ isSignal: true, alias: "usageHint", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14662
+ }], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14334
14663
 
14335
14664
  // SPDX-License-Identifier: MIT
14336
- /** Maps v1 textFieldType to HTML input[type] or textarea. */
14665
+ /** Maps v0.9 variant to HTML input[type] or textarea. */
14337
14666
  const TYPE_MAP = {
14338
14667
  shortText: 'text',
14339
14668
  longText: 'text', // handled by textarea below
14340
14669
  number: 'number',
14341
14670
  obscured: 'password',
14342
- date: 'date',
14343
14671
  };
14344
14672
  class A2uiTextFieldComponent {
14345
14673
  static _idCounter = 0;
14346
14674
  _inputId = `a2ui-text-field-${++A2uiTextFieldComponent._idCounter}`;
14347
14675
  host = injectRenderHost();
14348
14676
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
14349
- /** v1 prop: text (resolved string value). */
14350
- text = input('', ...(ngDevMode ? [{ debugName: "text" }] : []));
14351
- /** Back-compat alias: value. surface-to-spec resolves DynamicString → plain string. */
14352
- value = computed(() => this.text() ?? '', ...(ngDevMode ? [{ debugName: "value" }] : []));
14677
+ /** v0.9 prop: resolved string value. */
14678
+ value = input('', ...(ngDevMode ? [{ debugName: "value" }] : []));
14353
14679
  placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
14354
- textFieldType = input('shortText', ...(ngDevMode ? [{ debugName: "textFieldType" }] : []));
14680
+ /** v0.9 prop: input variant (default 'shortText'). */
14681
+ variant = input('shortText', ...(ngDevMode ? [{ debugName: "variant" }] : []));
14682
+ /** Enforced by the surface's check gate as an implicit regex rule (plus the native pattern attribute). */
14355
14683
  validationRegexp = input('', ...(ngDevMode ? [{ debugName: "validationRegexp" }] : []));
14684
+ /** Live validation message written by the surface's check gate
14685
+ * (bound to /_a2uiChecks/<id>); empty when valid. */
14686
+ errorText = input('', ...(ngDevMode ? [{ debugName: "errorText" }] : []));
14356
14687
  _bindings = input({}, ...(ngDevMode ? [{ debugName: "_bindings" }] : []));
14357
14688
  // Framework inputs required by the render harness.
14358
14689
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
14359
14690
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
14360
14691
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
14361
14692
  spec = input(undefined, ...(ngDevMode ? [{ debugName: "spec" }] : []));
14362
- htmlInputType = computed(() => TYPE_MAP[this.textFieldType()] ?? 'text', ...(ngDevMode ? [{ debugName: "htmlInputType" }] : []));
14693
+ htmlInputType = computed(() => TYPE_MAP[this.variant()] ?? 'text', ...(ngDevMode ? [{ debugName: "htmlInputType" }] : []));
14363
14694
  onInput(event) {
14364
14695
  const val = event.target.value;
14365
- // Emit on 'text' binding (v1 prop name); also try 'value' for compat.
14366
- const bound = this._bindings();
14367
- if (bound['text']) {
14368
- emitBinding(this.host, bound, 'text', val);
14369
- }
14370
- else {
14371
- emitBinding(this.host, bound, 'value', val);
14372
- }
14696
+ emitBinding(this.host, this._bindings(), 'value', val);
14373
14697
  }
14374
14698
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiTextFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14375
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiTextFieldComponent, isStandalone: true, selector: "a2ui-text-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, textFieldType: { classPropertyName: "textFieldType", publicName: "textFieldType", isSignal: true, isRequired: false, transformFunction: null }, validationRegexp: { classPropertyName: "validationRegexp", publicName: "validationRegexp", isSignal: true, isRequired: false, transformFunction: null }, _bindings: { classPropertyName: "_bindings", publicName: "_bindings", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14699
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: A2uiTextFieldComponent, isStandalone: true, selector: "a2ui-text-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, validationRegexp: { classPropertyName: "validationRegexp", publicName: "validationRegexp", isSignal: true, isRequired: false, transformFunction: null }, errorText: { classPropertyName: "errorText", publicName: "errorText", isSignal: true, isRequired: false, transformFunction: null }, _bindings: { classPropertyName: "_bindings", publicName: "_bindings", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14376
14700
  <div class="a2ui-tf">
14377
14701
  @if (label()) {
14378
14702
  <label [htmlFor]="_inputId" class="a2ui-tf__label">{{ label() }}</label>
14379
14703
  }
14380
- @if (textFieldType() === 'longText') {
14704
+ @if (variant() === 'longText') {
14381
14705
  <textarea
14382
14706
  [id]="_inputId"
14383
14707
  [value]="value()"
@@ -14397,8 +14721,11 @@ class A2uiTextFieldComponent {
14397
14721
  (input)="onInput($event)"
14398
14722
  />
14399
14723
  }
14724
+ @if (errorText()) {
14725
+ <div class="a2ui-check-error" role="alert">{{ errorText() }}</div>
14726
+ }
14400
14727
  </div>
14401
- `, isInline: true, styles: [".a2ui-tf{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-tf__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-tf__input{padding:var(--a2ui-spacing-2) var(--a2ui-spacing-3);font-size:var(--a2ui-typography-body-size);border-radius:var(--a2ui-shape-small);background:var(--a2ui-input-bg);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);outline:none;transition:border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard);resize:vertical}.a2ui-tf__input:focus{outline:var(--a2ui-focus-ring-width) solid var(--a2ui-focus-ring-color);outline-offset:2px;border-color:var(--a2ui-primary)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14728
+ `, isInline: true, styles: [".a2ui-tf{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-tf__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-tf__input{padding:var(--a2ui-spacing-2) var(--a2ui-spacing-3);font-size:var(--a2ui-typography-body-size);border-radius:var(--a2ui-shape-small);background:var(--a2ui-input-bg);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);outline:none;transition:border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard);resize:vertical}.a2ui-tf__input:focus{outline:var(--a2ui-focus-ring-width) solid var(--a2ui-focus-ring-color);outline-offset:2px;border-color:var(--a2ui-primary)}.a2ui-check-error{font-size:var(--a2ui-typography-label-size);color:var(--a2ui-error, #d33d55)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14402
14729
  }
14403
14730
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiTextFieldComponent, decorators: [{
14404
14731
  type: Component,
@@ -14407,7 +14734,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
14407
14734
  @if (label()) {
14408
14735
  <label [htmlFor]="_inputId" class="a2ui-tf__label">{{ label() }}</label>
14409
14736
  }
14410
- @if (textFieldType() === 'longText') {
14737
+ @if (variant() === 'longText') {
14411
14738
  <textarea
14412
14739
  [id]="_inputId"
14413
14740
  [value]="value()"
@@ -14427,16 +14754,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
14427
14754
  (input)="onInput($event)"
14428
14755
  />
14429
14756
  }
14757
+ @if (errorText()) {
14758
+ <div class="a2ui-check-error" role="alert">{{ errorText() }}</div>
14759
+ }
14430
14760
  </div>
14431
- `, styles: [".a2ui-tf{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-tf__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-tf__input{padding:var(--a2ui-spacing-2) var(--a2ui-spacing-3);font-size:var(--a2ui-typography-body-size);border-radius:var(--a2ui-shape-small);background:var(--a2ui-input-bg);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);outline:none;transition:border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard);resize:vertical}.a2ui-tf__input:focus{outline:var(--a2ui-focus-ring-width) solid var(--a2ui-focus-ring-color);outline-offset:2px;border-color:var(--a2ui-primary)}\n"] }]
14432
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], textFieldType: [{ type: i0.Input, args: [{ isSignal: true, alias: "textFieldType", required: false }] }], validationRegexp: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationRegexp", required: false }] }], _bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "_bindings", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14761
+ `, styles: [".a2ui-tf{display:flex;flex-direction:column;gap:var(--a2ui-spacing-1)}.a2ui-tf__label{font-size:var(--a2ui-typography-label-size);font-weight:var(--a2ui-typography-label-weight);color:var(--a2ui-label)}.a2ui-tf__input{padding:var(--a2ui-spacing-2) var(--a2ui-spacing-3);font-size:var(--a2ui-typography-body-size);border-radius:var(--a2ui-shape-small);background:var(--a2ui-input-bg);color:var(--a2ui-on-surface);border:1px solid var(--a2ui-outline);outline:none;transition:border-color var(--a2ui-motion-duration-short) var(--a2ui-motion-easing-standard);resize:vertical}.a2ui-tf__input:focus{outline:var(--a2ui-focus-ring-width) solid var(--a2ui-focus-ring-color);outline-offset:2px;border-color:var(--a2ui-primary)}.a2ui-check-error{font-size:var(--a2ui-typography-label-size);color:var(--a2ui-error, #d33d55)}\n"] }]
14762
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], validationRegexp: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationRegexp", required: false }] }], errorText: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorText", required: false }] }], _bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "_bindings", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14433
14763
 
14434
14764
  // SPDX-License-Identifier: MIT
14435
14765
  class A2uiVideoComponent {
14436
14766
  url = input('', ...(ngDevMode ? [{ debugName: "url" }] : []));
14437
- /** v1 prop name: autoPlay (camelCase). */
14438
- autoPlay = input(false, ...(ngDevMode ? [{ debugName: "autoPlay" }] : []));
14439
- controls = input(true, ...(ngDevMode ? [{ debugName: "controls" }] : []));
14440
14767
  // Framework inputs required by the render harness.
14441
14768
  bindings = input({}, ...(ngDevMode ? [{ debugName: "bindings" }] : []));
14442
14769
  emit = input(() => { }, ...(ngDevMode ? [{ debugName: "emit" }] : []));
@@ -14444,12 +14771,11 @@ class A2uiVideoComponent {
14444
14771
  childKeys = input([], ...(ngDevMode ? [{ debugName: "childKeys" }] : []));
14445
14772
  spec = input(undefined, ...(ngDevMode ? [{ debugName: "spec" }] : []));
14446
14773
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: A2uiVideoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14447
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.6", type: A2uiVideoComponent, isStandalone: true, selector: "a2ui-video", inputs: { url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null }, autoPlay: { classPropertyName: "autoPlay", publicName: "autoPlay", isSignal: true, isRequired: false, transformFunction: null }, controls: { classPropertyName: "controls", publicName: "controls", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14774
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.6", type: A2uiVideoComponent, isStandalone: true, selector: "a2ui-video", inputs: { url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null }, bindings: { classPropertyName: "bindings", publicName: "bindings", isSignal: true, isRequired: false, transformFunction: null }, emit: { classPropertyName: "emit", publicName: "emit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, childKeys: { classPropertyName: "childKeys", publicName: "childKeys", isSignal: true, isRequired: false, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14448
14775
  <video
14449
14776
  class="a2ui-video"
14450
14777
  [src]="url()"
14451
- [autoplay]="autoPlay()"
14452
- [controls]="controls()"
14778
+ controls
14453
14779
  ></video>
14454
14780
  `, isInline: true, styles: [".a2ui-video{display:block;width:100%;border-radius:var(--a2ui-shape-small)}\n"] });
14455
14781
  }
@@ -14459,11 +14785,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
14459
14785
  <video
14460
14786
  class="a2ui-video"
14461
14787
  [src]="url()"
14462
- [autoplay]="autoPlay()"
14463
- [controls]="controls()"
14788
+ controls
14464
14789
  ></video>
14465
14790
  `, styles: [".a2ui-video{display:block;width:100%;border-radius:var(--a2ui-shape-small)}\n"] }]
14466
- }], propDecorators: { url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: false }] }], autoPlay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoPlay", required: false }] }], controls: [{ type: i0.Input, args: [{ isSignal: true, alias: "controls", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14791
+ }], propDecorators: { url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: false }] }], bindings: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindings", required: false }] }], emit: [{ type: i0.Input, args: [{ isSignal: true, alias: "emit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], childKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "childKeys", required: false }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }] } });
14467
14792
 
14468
14793
  // SPDX-License-Identifier: MIT
14469
14794
  /**
@@ -14484,6 +14809,7 @@ function a2uiBasicCatalog() {
14484
14809
  Button: A2uiButtonComponent,
14485
14810
  Card: A2uiCardComponent,
14486
14811
  CheckBox: A2uiCheckBoxComponent,
14812
+ ChoicePicker: A2uiChoicePickerComponent,
14487
14813
  Column: A2uiColumnComponent,
14488
14814
  DateTimeInput: A2uiDateTimeInputComponent,
14489
14815
  Divider: A2uiDividerComponent,
@@ -14491,7 +14817,6 @@ function a2uiBasicCatalog() {
14491
14817
  Image: A2uiImageComponent,
14492
14818
  List: A2uiListComponent,
14493
14819
  Modal: A2uiModalComponent,
14494
- MultipleChoice: A2uiMultipleChoiceComponent,
14495
14820
  Row: A2uiRowComponent,
14496
14821
  Slider: A2uiSliderComponent,
14497
14822
  Tabs: A2uiTabsComponent,
@@ -14501,6 +14826,22 @@ function a2uiBasicCatalog() {
14501
14826
  });
14502
14827
  }
14503
14828
 
14829
+ // SPDX-License-Identifier: MIT
14830
+ /**
14831
+ * The A2UI client capabilities this renderer supports — the typed
14832
+ * `a2uiClientCapabilities` metadata a host attaches to agent requests so
14833
+ * the agent knows which component catalogs it may target
14834
+ * (catalog negotiation, A2UI v0.9 transport metadata).
14835
+ *
14836
+ * @example
14837
+ * ```ts
14838
+ * const metadata = { a2uiClientCapabilities: a2uiClientCapabilities() };
14839
+ * ```
14840
+ */
14841
+ function a2uiClientCapabilities() {
14842
+ return { supportedCatalogIds: [A2UI_BASIC_CATALOG_ID] };
14843
+ }
14844
+
14504
14845
  /**
14505
14846
  * Declare an async function tool the model can call; its resolved return value
14506
14847
  * becomes the tool result shipped back to the model.
@@ -14695,5 +15036,5 @@ function mockAgent(opts = {}) {
14695
15036
  * Generated bundle index. Do not edit.
14696
15037
  */
14697
15038
 
14698
- export { A2uiAudioPlayerComponent, A2uiButtonComponent, A2uiCardComponent, A2uiCheckBoxComponent, A2uiColumnComponent, A2uiDateTimeInputComponent, A2uiDividerComponent, A2uiIconComponent, A2uiImageComponent, A2uiListComponent, A2uiModalComponent, A2uiMultipleChoiceComponent, A2uiRowComponent, A2uiSliderComponent, A2uiSurfaceComponent, A2uiTabsComponent, A2uiTextComponent, A2uiTextFieldComponent, A2uiVideoComponent, AGENT_ERROR_MESSAGES, AgentError, CHAT_CONFIG, CHAT_LIFECYCLE, ChatApprovalCardComponent, ChatCitationCardTemplateDirective, ChatCitationPreviewComponent, ChatCitationsCardComponent, ChatCitationsComponent, ChatComponent, ChatConfirmDialogComponent, ChatConnectedOverlayDirective, ChatErrorComponent, ChatGenerativeUiComponent, ChatGenuiSkeletonComponent, ChatHistorySearchPaletteComponent, ChatInputComponent, ChatInterruptComponent, ChatInterruptPanelComponent, ChatLauncherButtonComponent, ChatMessageActionsComponent, ChatMessageComponent, ChatMessageListComponent, ChatOverflowMenuComponent, ChatOverlayOriginDirective, ChatPopupComponent, ChatProjectListComponent, ChatReasoningComponent, ChatScrollBubbleComponent, ChatSelectComponent, ChatSidebarComponent, ChatSidenavComponent, ChatSidenavScrimComponent, ChatStreamingMdComponent, ChatSubagentCardComponent, ChatSubagentsComponent, ChatSuggestionsComponent, ChatThreadListComponent, ChatTimelineComponent, ChatTimelineSliderComponent, ChatToolCallCardComponent, ChatToolCallTemplateDirective, ChatToolCallsComponent, ChatToolViewsComponent, ChatTraceComponent, ChatTypingIndicatorComponent, ChatWelcomeComponent, ChatWelcomeSuggestionComponent, ChatWindowComponent, CitationsResolverService, IS_HEADER_ROW, MARKDOWN_VIEW_REGISTRY, MarkdownAutolinkComponent, MarkdownBlockquoteComponent, MarkdownChildrenComponent, MarkdownCitationReferenceComponent, MarkdownCodeBlockComponent, MarkdownDocumentComponent, MarkdownEmphasisComponent, MarkdownHardBreakComponent, MarkdownHeadingComponent, MarkdownHtmlComponent, MarkdownImageComponent, MarkdownInlineCodeComponent, MarkdownLinkComponent, MarkdownListComponent, MarkdownListItemComponent, MarkdownMathComponent, MarkdownParagraphComponent, MarkdownSoftBreakComponent, MarkdownStrikethroughComponent, MarkdownStrongComponent, MarkdownTableCellComponent, MarkdownTableComponent, MarkdownTableRowComponent, MarkdownTextComponent, MarkdownThematicBreakComponent, MessageTemplateDirective, STREAMING_MARKDOWN_CONTRACT_VIOLATION_POLICY, a2uiBasicCatalog, action, ask, buildA2uiActionMessage, cacheplaneMarkdownViews, cancelledClientToolResult, citationSourceVisual, citationTypeLabel, citationTypeMeta, clientToolGuardFailureResult, completeDelivery, createA2uiSurfaceStore, createAgentRef, createContentClassifier, createParseTreeStore, createPartialArgsBridge, defaultInterruptedClientToolResult, deriveDomain, deriveJsonSchema, deriveMonogram, deriveSourceType, emitBinding, executeFunctionTool, extractErrorMessage, formatDuration, formatPublished, getInterrupt, getMessageType, injectThreadRouting, isAbortError, isAssistantMessage, isSystemMessage, isToolMessage, isTyping, isUserMessage, markdownDocument, messageContent, mockAgent, monogramColor, monogramHue, normalizeEnvelopeArgs, normalizeViewEntry, provideChat, renderMarkdown, selectPendingClientToolCalls, shouldClaimBeforeExecute, startClientToolExecutor, staticDelivery, statusColor, streamingDelivery, submitMessage, toAgentError, toClientToolSpecs, tools, validateArgs, view };
15039
+ export { A2uiAudioPlayerComponent, A2uiButtonComponent, A2uiCardComponent, A2uiCheckBoxComponent, A2uiChoicePickerComponent, A2uiColumnComponent, A2uiDateTimeInputComponent, A2uiDividerComponent, A2uiIconComponent, A2uiImageComponent, A2uiListComponent, A2uiModalComponent, A2uiRowComponent, A2uiSliderComponent, A2uiSurfaceComponent, A2uiTabsComponent, A2uiTextComponent, A2uiTextFieldComponent, A2uiVideoComponent, AGENT_ERROR_MESSAGES, AgentError, CHAT_CONFIG, CHAT_LIFECYCLE, ChatApprovalCardComponent, ChatCitationCardTemplateDirective, ChatCitationPreviewComponent, ChatCitationsCardComponent, ChatCitationsComponent, ChatComponent, ChatConfirmDialogComponent, ChatConnectedOverlayDirective, ChatErrorComponent, ChatGenerativeUiComponent, ChatGenuiSkeletonComponent, ChatHistorySearchPaletteComponent, ChatInputComponent, ChatInterruptComponent, ChatInterruptPanelComponent, ChatLauncherButtonComponent, ChatMessageActionsComponent, ChatMessageComponent, ChatMessageListComponent, ChatOverflowMenuComponent, ChatOverlayOriginDirective, ChatPopupComponent, ChatProjectListComponent, ChatReasoningComponent, ChatScrollBubbleComponent, ChatSelectComponent, ChatSidebarComponent, ChatSidenavComponent, ChatSidenavScrimComponent, ChatStreamingMdComponent, ChatSubagentCardComponent, ChatSubagentsComponent, ChatSuggestionsComponent, ChatThreadListComponent, ChatTimelineComponent, ChatTimelineSliderComponent, ChatToolCallCardComponent, ChatToolCallTemplateDirective, ChatToolCallsComponent, ChatToolViewsComponent, ChatTraceComponent, ChatTypingIndicatorComponent, ChatWelcomeComponent, ChatWelcomeSuggestionComponent, ChatWindowComponent, CitationsResolverService, IS_HEADER_ROW, MARKDOWN_VIEW_REGISTRY, MarkdownAutolinkComponent, MarkdownBlockquoteComponent, MarkdownChildrenComponent, MarkdownCitationReferenceComponent, MarkdownCodeBlockComponent, MarkdownDocumentComponent, MarkdownEmphasisComponent, MarkdownHardBreakComponent, MarkdownHeadingComponent, MarkdownHtmlComponent, MarkdownImageComponent, MarkdownInlineCodeComponent, MarkdownLinkComponent, MarkdownListComponent, MarkdownListItemComponent, MarkdownMathComponent, MarkdownParagraphComponent, MarkdownSoftBreakComponent, MarkdownStrikethroughComponent, MarkdownStrongComponent, MarkdownTableCellComponent, MarkdownTableComponent, MarkdownTableRowComponent, MarkdownTextComponent, MarkdownThematicBreakComponent, MessageTemplateDirective, STREAMING_MARKDOWN_CONTRACT_VIOLATION_POLICY, a2uiBasicCatalog, a2uiClientCapabilities, action, ask, buildA2uiActionMessage, cacheplaneMarkdownViews, cancelledClientToolResult, citationSourceVisual, citationTypeLabel, citationTypeMeta, clientToolGuardFailureResult, completeDelivery, createA2uiSurfaceStore, createAgentRef, createContentClassifier, createParseTreeStore, createPartialArgsBridge, defaultInterruptedClientToolResult, deriveDomain, deriveJsonSchema, deriveMonogram, deriveSourceType, emitBinding, executeFunctionTool, extractErrorMessage, formatDuration, formatPublished, getInterrupt, getMessageType, injectThreadRouting, isAbortError, isAssistantMessage, isSystemMessage, isToolMessage, isTyping, isUserMessage, markdownDocument, messageContent, mockAgent, monogramColor, monogramHue, normalizeEnvelopeArgs, normalizeViewEntry, provideChat, renderMarkdown, selectPendingClientToolCalls, shouldClaimBeforeExecute, startClientToolExecutor, staticDelivery, statusColor, streamingDelivery, submitMessage, toAgentError, toClientToolSpecs, tools, validateArgs, view };
14699
15040
  //# sourceMappingURL=threadplane-chat.mjs.map