@adia-ai/web-components 0.5.4 → 0.5.6

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.
Files changed (46) hide show
  1. package/components/accordion/accordion-item.a2ui.json +50 -0
  2. package/components/accordion/accordion-item.yaml +27 -0
  3. package/components/action-list/action-item.a2ui.json +63 -0
  4. package/components/action-list/action-item.yaml +37 -0
  5. package/components/agent-feedback-bar/class.js +9 -3
  6. package/components/avatar/avatar-group.a2ui.json +50 -0
  7. package/components/avatar/avatar-group.yaml +26 -0
  8. package/components/avatar/avatar.a2ui.json +4 -1
  9. package/components/avatar/avatar.yaml +7 -0
  10. package/components/button/class.js +39 -0
  11. package/components/chart/chart.a2ui.json +4 -2
  12. package/components/list/list-item.a2ui.json +53 -0
  13. package/components/list/list-item.yaml +29 -0
  14. package/components/segmented/segmented.d.ts +3 -3
  15. package/components/select/class.js +14 -0
  16. package/components/select/select.a2ui.json +5 -0
  17. package/components/select/select.css +10 -0
  18. package/components/select/select.d.ts +3 -3
  19. package/components/select/select.yaml +5 -0
  20. package/components/slider/class.js +58 -0
  21. package/components/slider/slider.a2ui.json +10 -0
  22. package/components/slider/slider.css +13 -0
  23. package/components/slider/slider.yaml +10 -0
  24. package/components/switch/class.js +18 -4
  25. package/components/switch/switch.css +10 -0
  26. package/components/switch/switch.d.ts +3 -3
  27. package/components/tabs/tab.a2ui.json +58 -0
  28. package/components/tabs/tab.yaml +33 -0
  29. package/components/timeline/timeline-item.a2ui.json +76 -0
  30. package/components/timeline/timeline-item.yaml +47 -0
  31. package/components/toast/toast.d.ts +35 -0
  32. package/components/tree/class.js +91 -0
  33. package/components/tree/tree-item.a2ui.json +65 -0
  34. package/components/tree/tree-item.yaml +41 -0
  35. package/components/tree/tree.a2ui.json +15 -0
  36. package/components/tree/tree.css +18 -0
  37. package/components/tree/tree.yaml +10 -0
  38. package/core/anchor.d.ts +71 -0
  39. package/core/controller.d.ts +171 -0
  40. package/core/markdown.d.ts +26 -0
  41. package/core/polyfills.d.ts +31 -0
  42. package/core/provider.d.ts +82 -0
  43. package/core/streams-bridge.d.ts +78 -0
  44. package/core/template.js +21 -3
  45. package/core/transport.d.ts +78 -0
  46. package/package.json +2 -2
@@ -0,0 +1,65 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/TreeItem.json",
4
+ "title": "TreeItem",
5
+ "description": "Child of <tree-ui>. One tree row with optional icon + text + trailing badge, plus nested tree-item-ui children for the collapsible hierarchy. Use inside <tree-ui> only.",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "badge": {
17
+ "description": "Optional trailing badge text (count, label, etc.). Renders muted + small + right-aligned alongside the row, mirroring the nav-item-ui badge API. When empty, the badge slot stays empty.\n\nAdded in §184 (v0.5.5, FEEDBACK-08 §1).",
18
+ "type": "string"
19
+ },
20
+ "component": {
21
+ "const": "TreeItem"
22
+ },
23
+ "icon": {
24
+ "description": "Optional leading icon name (Phosphor).",
25
+ "type": "string"
26
+ },
27
+ "open": {
28
+ "description": "When true, the item's children render expanded. Single-instance state; the parent `<tree-ui>` toggles this on click + Enter/Space + ArrowRight/ArrowLeft per tree-view APG.",
29
+ "type": "boolean"
30
+ },
31
+ "selected": {
32
+ "description": "Reflects the currently-selected item. Exactly one tree-item is selected per `<tree-ui>` parent (managed by the parent).",
33
+ "type": "boolean"
34
+ },
35
+ "text": {
36
+ "description": "Primary text rendered in the row.",
37
+ "type": "string"
38
+ },
39
+ "value": {
40
+ "description": "Stable value emitted in `tree-select` event detail.",
41
+ "type": "string"
42
+ }
43
+ },
44
+ "required": [
45
+ "component"
46
+ ],
47
+ "unevaluatedProperties": false,
48
+ "x-adiaui": {
49
+ "anti_patterns": [],
50
+ "category": "data",
51
+ "composes": [],
52
+ "events": {},
53
+ "examples": [],
54
+ "keywords": [],
55
+ "name": "UITreeItem",
56
+ "related": [],
57
+ "slots": {},
58
+ "states": [],
59
+ "synonyms": {},
60
+ "tag": "tree-item-ui",
61
+ "tokens": {},
62
+ "traits": [],
63
+ "version": 1
64
+ }
65
+ }
@@ -0,0 +1,41 @@
1
+ # Edit this file; run `npm run build:components` to regenerate a2ui.json.
2
+ #
3
+ # §184 (v0.5.5): authored to close FEEDBACK-08 §1 — tree-item-ui badge
4
+ # attribute + general orphan-class catalog gap. tree-item-ui has shipped
5
+ # since v0.0.x co-located in tree/class.js (UITreeItem) but never had its
6
+ # own catalog entry. With §172 sibling-yaml scanner, this file is picked
7
+ # up next to the parent yaml. Mirrors the v0.5.5 §176 sibling-yaml
8
+ # baseline-orphan closure pattern.
9
+
10
+ # Child component of <tree-ui>. Surface only inside that parent.
11
+ $schema: ../../../../scripts/schemas/component.yaml.schema.json
12
+ name: UITreeItem
13
+ tag: tree-item-ui
14
+ component: TreeItem
15
+ category: data
16
+ version: 1
17
+ description: |-
18
+ Child of <tree-ui>. One tree row with optional icon + text + trailing badge, plus nested tree-item-ui children for the collapsible hierarchy. Use inside <tree-ui> only.
19
+
20
+ props:
21
+ text:
22
+ description: Primary text rendered in the row.
23
+ type: string
24
+ icon:
25
+ description: Optional leading icon name (Phosphor).
26
+ type: string
27
+ value:
28
+ description: Stable value emitted in `tree-select` event detail.
29
+ type: string
30
+ open:
31
+ description: When true, the item's children render expanded. Single-instance state; the parent `<tree-ui>` toggles this on click + Enter/Space + ArrowRight/ArrowLeft per tree-view APG.
32
+ type: boolean
33
+ selected:
34
+ description: Reflects the currently-selected item. Exactly one tree-item is selected per `<tree-ui>` parent (managed by the parent).
35
+ type: boolean
36
+ badge:
37
+ description: |-
38
+ Optional trailing badge text (count, label, etc.). Renders muted + small + right-aligned alongside the row, mirroring the nav-item-ui badge API. When empty, the badge slot stays empty.
39
+
40
+ Added in §184 (v0.5.5, FEEDBACK-08 §1).
41
+ type: string
@@ -84,6 +84,21 @@
84
84
  "--tree-actions-gap": {
85
85
  "description": "Gap between action icons"
86
86
  },
87
+ "--tree-badge-bg": {
88
+ "description": "Background color for the trailing badge (§184)."
89
+ },
90
+ "--tree-badge-fg": {
91
+ "description": "Foreground color for the trailing badge (§184)."
92
+ },
93
+ "--tree-badge-px": {
94
+ "description": "Inline padding for the trailing badge (§184)."
95
+ },
96
+ "--tree-badge-radius": {
97
+ "description": "Border radius for the trailing badge (§184)."
98
+ },
99
+ "--tree-badge-size": {
100
+ "description": "Font size for the trailing badge (§184)."
101
+ },
87
102
  "--tree-bg-hover": {
88
103
  "description": "Background color on hover"
89
104
  },
@@ -127,6 +127,24 @@
127
127
  text-overflow: ellipsis;
128
128
  }
129
129
 
130
+ /* ── Badge (§184, v0.5.5, FEEDBACK-08 §1) ──
131
+ Optional trailing badge for counts/labels (e.g. "Colors (7)" →
132
+ text="Colors" badge="7"). Empty span renders nothing (no padding,
133
+ no chrome). Sits before the [slot="actions"] hover-revealed slot. */
134
+ [slot="badge"] {
135
+ flex-shrink: 0;
136
+ font-size: var(--tree-badge-size, var(--a-fine-size));
137
+ color: var(--tree-badge-fg, var(--tree-fg-muted));
138
+ background: var(--tree-badge-bg, transparent);
139
+ padding: 0 var(--tree-badge-px, var(--a-space-1));
140
+ border-radius: var(--tree-badge-radius, var(--a-radius-sm));
141
+ line-height: 1;
142
+ font-variant-numeric: tabular-nums;
143
+ }
144
+ [slot="badge"]:empty {
145
+ display: none;
146
+ }
147
+
130
148
  /* ── Actions (right side) ── */
131
149
  [slot="actions"] {
132
150
  display: flex;
@@ -66,6 +66,16 @@ tokens:
66
66
  description: Inline-end padding of each row
67
67
  --tree-row-radius:
68
68
  description: Border radius of each row
69
+ --tree-badge-bg:
70
+ description: Background color for the trailing badge (§184).
71
+ --tree-badge-fg:
72
+ description: Foreground color for the trailing badge (§184).
73
+ --tree-badge-px:
74
+ description: Inline padding for the trailing badge (§184).
75
+ --tree-badge-radius:
76
+ description: Border radius for the trailing badge (§184).
77
+ --tree-badge-size:
78
+ description: Font size for the trailing badge (§184).
69
79
  requiredIcons:
70
80
  - caret-right
71
81
  a2ui:
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Anchor positioning — positions a popover/panel relative to an anchor
3
+ * element. Uses native CSS Anchor Positioning when supported (Chrome
4
+ * 125+), falls back to a JS implementation (`getBoundingClientRect()` +
5
+ * `position: fixed`) otherwise.
6
+ *
7
+ * Works with the Popover API: the popover renders in the top layer via
8
+ * `showPopover()` on a `popover="manual"` or `popover="auto"` element,
9
+ * and the position is wired with either `anchor()` + `position-area` or
10
+ * manual coords.
11
+ *
12
+ * Runtime exports mirror `core/anchor.js`. `.d.ts` authored in v0.5.6
13
+ * §191 to close the §178 audit-script baseline missing-sibling gap.
14
+ *
15
+ * @see ./anchor.js (runtime SoT)
16
+ * @see ../USAGE.md (consumer guide)
17
+ */
18
+
19
+ /**
20
+ * Placement vocabulary for {@link anchorPopover}. Mirrors a subset of
21
+ * the floating-ui / Popper conventions. `-start` aligns the popover's
22
+ * leading edge to the anchor's leading edge along the perpendicular
23
+ * axis; `-end` aligns the trailing edges; bare (no suffix) centers.
24
+ */
25
+ export type Placement =
26
+ | 'bottom'
27
+ | 'bottom-start'
28
+ | 'bottom-end'
29
+ | 'top'
30
+ | 'top-start'
31
+ | 'top-end'
32
+ | 'left'
33
+ | 'left-start'
34
+ | 'left-end'
35
+ | 'right'
36
+ | 'right-start'
37
+ | 'right-end';
38
+
39
+ /**
40
+ * Options for {@link anchorPopover}.
41
+ */
42
+ export interface AnchorOptions {
43
+ /** Where to place the popover relative to its anchor. Default: `bottom-start`. */
44
+ placement?: Placement;
45
+ /** Pixel gap on the main (anchor-adjacent) axis. Default: 4. */
46
+ gap?: number;
47
+ /**
48
+ * When true, the popover's `width` is constrained to match the
49
+ * anchor's width — useful for combobox-style listboxes.
50
+ */
51
+ matchWidth?: boolean;
52
+ }
53
+
54
+ /**
55
+ * `true` when the current browser supports the CSS Anchor Positioning
56
+ * primitives the native path uses (`anchor-name`, `position-area`).
57
+ * `false` otherwise (browser falls back to the JS positioning path).
58
+ */
59
+ export const supportsAnchorCSS: boolean;
60
+
61
+ /**
62
+ * Position `popover` relative to `anchor`. Picks the native CSS Anchor
63
+ * Positioning path when available, otherwise the JS fallback. Returns a
64
+ * cleanup function — call it to detach listeners / restore inline
65
+ * styles.
66
+ */
67
+ export function anchorPopover(
68
+ anchor: Element,
69
+ popover: HTMLElement,
70
+ options?: AnchorOptions,
71
+ ): () => void;
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Controller pattern — host-attached state objects that reflect into
3
+ * the DOM + notify subscribers on change. `BaseController` is the
4
+ * minimal contract; `RouteController` is the canonical routing-state
5
+ * implementation used by `<router-ui>` (see `core/provider.js`).
6
+ *
7
+ * Runtime exports mirror `core/controller.js`. `.d.ts` authored in
8
+ * v0.5.6 §191 to close the §178 audit-script baseline missing-sibling
9
+ * gap.
10
+ *
11
+ * @see ./controller.js (runtime SoT)
12
+ * @see ./provider.js (`<router-ui>` consumer of `RouteController`)
13
+ */
14
+
15
+ /**
16
+ * Static schema each {@link BaseController} subclass should declare so
17
+ * the dev-warn at `connect()` time can identify the controller in
18
+ * diagnostics. Optional but conventional.
19
+ */
20
+ export interface ControllerSchema {
21
+ /** Stable lowercase controller identifier (e.g. `"route"`). */
22
+ name: string;
23
+ /** Map of public state field names → human-readable type label. */
24
+ state?: Record<string, string>;
25
+ /** List of canonical command names available on the controller. */
26
+ commands?: ReadonlyArray<string>;
27
+ /** Host attributes the controller writes via `reflect()`. */
28
+ attributes?: ReadonlyArray<string>;
29
+ }
30
+
31
+ /**
32
+ * Base class for host-attached controllers.
33
+ *
34
+ * Subclasses MUST implement `getState()` and SHOULD declare a static
35
+ * `schema`; the constructor warns when these are missing the first
36
+ * time the controller is connected.
37
+ */
38
+ export class BaseController {
39
+ /** Optional schema (subclasses declare via `static schema = …`). */
40
+ static schema?: ControllerSchema;
41
+
42
+ /** The host element this controller is attached to, or `null`. */
43
+ readonly host: Element | null;
44
+
45
+ /**
46
+ * Wire the controller to a host element. Calls
47
+ * {@link onConnect} + {@link reflect} once.
48
+ */
49
+ connect(host: Element): void;
50
+
51
+ /**
52
+ * Detach from `host` (or the currently-connected host if omitted).
53
+ * Calls {@link onDisconnect}.
54
+ */
55
+ disconnect(host?: Element): void;
56
+
57
+ /**
58
+ * Subscribe to state-change notifications. Returns an unsubscribe
59
+ * function.
60
+ */
61
+ subscribe(fn: () => void): () => void;
62
+
63
+ /**
64
+ * Run subscriber callbacks + `reflect()` after a state mutation.
65
+ * Subclasses call this from their command implementations.
66
+ */
67
+ notify(): void;
68
+
69
+ /** Lifecycle hook — override to wire listeners at connect time. */
70
+ onConnect(host: Element): void;
71
+
72
+ /** Lifecycle hook — override to release listeners at disconnect time. */
73
+ onDisconnect(host: Element): void;
74
+
75
+ /**
76
+ * Override to write controller state into the host (attributes,
77
+ * data-* properties, etc.). Called on every {@link notify} + at
78
+ * `connect()`.
79
+ */
80
+ reflect(): void;
81
+
82
+ /**
83
+ * Return the controller's current public state. Subclasses MUST
84
+ * override. The default implementation throws.
85
+ */
86
+ getState(): unknown;
87
+ }
88
+
89
+ /**
90
+ * A single route declaration consumed by {@link RouteController}.
91
+ */
92
+ export interface Route {
93
+ /**
94
+ * Path pattern. Static segments match literally; segments prefixed
95
+ * with `:` are captured into `params`. e.g. `/users/:id`.
96
+ */
97
+ path: string;
98
+ /** Optional content URL for `<router-ui>` to fetch + inject. */
99
+ content?: string;
100
+ /** Optional document title to set when the route matches. */
101
+ title?: string;
102
+ /** Optional section identifier (consumer-defined). */
103
+ section?: string;
104
+ /** Open-ended — route definitions can carry arbitrary metadata. */
105
+ [key: string]: unknown;
106
+ }
107
+
108
+ /**
109
+ * Public state shape returned by {@link RouteController.getState}.
110
+ */
111
+ export interface RouteState {
112
+ /** Current pathname being matched. */
113
+ path: string;
114
+ /** Captured `:param` values from the matched route. */
115
+ params: Record<string, string>;
116
+ /** The matched route declaration, or `null` if no match. */
117
+ route: Route | null;
118
+ /** Previous pathname (before the most recent navigation). */
119
+ previous: string;
120
+ }
121
+
122
+ /**
123
+ * Imperative commands exposed by {@link RouteController}. Wire-up
124
+ * stable across both code-driven navigation + DOM-driven link clicks
125
+ * (the latter via `<router-ui>`'s click delegation).
126
+ */
127
+ export interface RouteCommands {
128
+ /** Push a new pathname onto the history stack + re-match routes. */
129
+ navigate(path: string): void;
130
+ /** Replace the current history entry's pathname + re-match. */
131
+ replace(path: string): void;
132
+ /** Equivalent to `history.back()` when `historySync` is on. */
133
+ back(): void;
134
+ /** Equivalent to `history.forward()` when `historySync` is on. */
135
+ forward(): void;
136
+ /** Swap the routes table at runtime (re-matches the current path). */
137
+ setRoutes(routes: ReadonlyArray<Route>): void;
138
+ }
139
+
140
+ /**
141
+ * Constructor options for {@link RouteController}.
142
+ */
143
+ export interface RouteControllerOptions {
144
+ /** Initial routes table. May be empty + updated later via `setRoutes`. */
145
+ routes?: ReadonlyArray<Route>;
146
+ /** Initial pathname (defaults to `location.pathname`). */
147
+ initial?: string;
148
+ /**
149
+ * When `true` (default), navigate/replace push to History API +
150
+ * `popstate` syncs state on browser back/forward.
151
+ */
152
+ historySync?: boolean;
153
+ }
154
+
155
+ /**
156
+ * Routing-state controller. Used by `<router-ui>` (see
157
+ * `core/provider.js`) as the canonical history-syncing route matcher.
158
+ * Can also be used standalone — `notify()` fires whenever the path
159
+ * changes; `getState()` returns the current match.
160
+ */
161
+ export class RouteController extends BaseController {
162
+ static schema: ControllerSchema;
163
+
164
+ constructor(options?: RouteControllerOptions);
165
+
166
+ /** Public state — path, params, matched route, previous path. */
167
+ getState(): RouteState;
168
+
169
+ /** Imperative API; wire from command palette / link handlers. */
170
+ commands: RouteCommands;
171
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Minimal markdown → HTML renderer for LLM output. Handles fenced code
3
+ * blocks, inline code, bold, italic, links, lists, headings, and
4
+ * paragraphs. No external dependencies.
5
+ *
6
+ * Fenced code blocks with a language identifier emit `<code-ui
7
+ * language="…">…</code-ui>` so the syntax-highlight upgrade picks them
8
+ * up automatically (see SPEC-CODE-EDITOR-001). Unlabeled fences keep
9
+ * the bare `<pre><code>…</code></pre>` form.
10
+ *
11
+ * Runtime exports mirror `core/markdown.js`. `.d.ts` authored in v0.5.6
12
+ * §191 to close the §178 audit-script baseline missing-sibling gap.
13
+ *
14
+ * @see ./markdown.js (runtime SoT)
15
+ */
16
+
17
+ /**
18
+ * Render a markdown source string to HTML.
19
+ *
20
+ * The output is a sanitized HTML string suitable for direct
21
+ * `element.innerHTML` assignment — text content is HTML-escaped before
22
+ * inline formatting is applied. Consumer is responsible for any
23
+ * additional context-specific sanitization (e.g. URL-scheme allowlisting
24
+ * for the link targets).
25
+ */
26
+ export function renderMarkdown(src: string): string;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Optional polyfills — for consumers extending BELOW the library's
3
+ * stated baseline (Chromium 125+, Safari 17.4+, Firefox 129+).
4
+ *
5
+ * **Side-effect-only module.** Importing this file runs feature checks
6
+ * at module-evaluation time and dynamically loads polyfills only when
7
+ * the runtime lacks support:
8
+ *
9
+ * - Popover API (consumers extending to Firefox < 125 / Safari < 17)
10
+ * - CSS Anchor Positioning (consumers extending to Safari < 26 /
11
+ * Firefox < 147)
12
+ *
13
+ * Browsers with native support skip the download. The polyfill
14
+ * packages (`@oddbird/popover-polyfill`, `@oddbird/css-anchor-positioning`)
15
+ * are optional — if not installed, the import silently fails.
16
+ *
17
+ * Consumers on the stated baseline should NOT import this module — the
18
+ * runtime checks resolve to no-op, but skipping the file avoids the
19
+ * conditional `import()` setup entirely.
20
+ *
21
+ * Runtime mirror: `core/polyfills.js`. `.d.ts` authored in v0.5.6 §191
22
+ * to close the §178 audit-script baseline missing-sibling gap.
23
+ *
24
+ * @see ./polyfills.js (runtime SoT)
25
+ */
26
+
27
+ // Side-effect-only module — no exports. The empty `export {}` marks
28
+ // the file as an ES module so TypeScript treats `import '...polyfills.js'`
29
+ // as a valid module side-effect import without complaining about a
30
+ // missing module shape.
31
+ export {};
@@ -0,0 +1,82 @@
1
+ /**
2
+ * `<router-ui>` provider — declarative + imperative client-side
3
+ * routing built on {@link RouteController} (re-declared here for
4
+ * historical reasons; the canonical export lives in `core/controller.js`).
5
+ *
6
+ * Two consumer paths:
7
+ *
8
+ * 1. **Declarative** — assign `router.routes = [...]` and the provider
9
+ * instantiates its own controller, wires `popstate`, intercepts
10
+ * same-origin link clicks, fetches each matched route's `content`
11
+ * URL, and injects into the host element.
12
+ *
13
+ * 2. **Controller-driven** — construct a {@link RouteController}
14
+ * yourself + assign to `router.controller`. Useful when multiple
15
+ * UI surfaces need to share routing state.
16
+ *
17
+ * Side-effect: importing this module calls
18
+ * `customElements.define('router-ui', UIRouter)`.
19
+ *
20
+ * Runtime exports mirror `core/provider.js`. `.d.ts` authored in v0.5.6
21
+ * §191 to close the §178 audit-script baseline missing-sibling gap.
22
+ *
23
+ * @see ./provider.js (runtime SoT — also defines `customElements`)
24
+ * @see ./controller.d.ts (canonical `RouteController` typing)
25
+ */
26
+
27
+ import { UIElement } from './element.js';
28
+ import type { Route, RouteCommands, RouteState, ControllerSchema, RouteControllerOptions } from './controller.js';
29
+ import { BaseController } from './controller.js';
30
+
31
+ /**
32
+ * Routing-state controller. Re-declared from `core/controller.js` for
33
+ * historical compatibility — both files define the same class shape;
34
+ * consumers can import either. New code should prefer
35
+ * `import { RouteController } from '@adia-ai/web-components/core/controller'`
36
+ * for the canonical path.
37
+ */
38
+ export class RouteController extends BaseController {
39
+ static schema: ControllerSchema;
40
+ constructor(options?: RouteControllerOptions);
41
+ getState(): RouteState;
42
+ commands: RouteCommands;
43
+ }
44
+
45
+ /**
46
+ * Optional async transform applied to fetched route content before
47
+ * injection. Useful for wrapping content in a layout shell.
48
+ */
49
+ export type TemplateResolver = (html: string, route: Route) => string | Promise<string>;
50
+
51
+ /**
52
+ * `<router-ui>` custom element. Side-effect-registered on module load.
53
+ */
54
+ export class UIRouter extends UIElement {
55
+ /** Currently-bound controller (declarative-mode controller is auto-created). */
56
+ controller?: RouteController;
57
+
58
+ /** Optional template wrapper hook — see {@link TemplateResolver}. */
59
+ templateResolver?: TemplateResolver;
60
+
61
+ /**
62
+ * Declarative-mode setter — assigning routes auto-creates an
63
+ * internal {@link RouteController} on first assignment, or
64
+ * `setRoutes`-replaces the table on subsequent ones.
65
+ */
66
+ set routes(list: ReadonlyArray<Route>);
67
+
68
+ /** Shortcut for `controller.commands.navigate(path)`. */
69
+ navigate(path: string): void;
70
+
71
+ /** Shortcut for `controller.commands.replace(path)`. */
72
+ replace(path: string): void;
73
+ }
74
+
75
+ /**
76
+ * `router-ui` is registered as a side-effect of module load.
77
+ */
78
+ declare global {
79
+ interface HTMLElementTagNameMap {
80
+ 'router-ui': UIRouter;
81
+ }
82
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * streams-bridge — proxy data-stream signals into A2UI surface data
3
+ * models (per OD-CHART-16 option b).
4
+ *
5
+ * The bridge duck-types the renderer (it needs `.process()`, nothing
6
+ * else), so it works with any A2UI-protocol consumer — the actual
7
+ * `A2UIRenderer` from `@adia-ai/a2ui-runtime`, a custom renderer, or
8
+ * a test stub.
9
+ *
10
+ * Contract:
11
+ * - One-way only: stream → data model. A2UI writes back to its model
12
+ * (e.g. via `update-data-model` wiring actions) do NOT propagate
13
+ * into the stream signal.
14
+ * - Tear down with the returned dispose function. Dispose detaches
15
+ * the effect; it does NOT release the stream's refcount.
16
+ * - `select` uses dot-separated path syntax (matches
17
+ * `data-stream-path` on the trait). `path` uses slash-separated
18
+ * syntax (matches A2UI bindings).
19
+ *
20
+ * Runtime exports mirror `core/streams-bridge.js`. `.d.ts` authored in
21
+ * v0.5.6 §191 to close the §178 audit-script baseline missing-sibling
22
+ * gap.
23
+ *
24
+ * @see ./streams-bridge.js (runtime SoT)
25
+ * @see ./data-stream.js (the stream registry the bridge reads from)
26
+ */
27
+
28
+ /**
29
+ * Minimal renderer contract the bridge requires. The actual
30
+ * `A2UIRenderer` from `@adia-ai/a2ui-runtime` satisfies this surface,
31
+ * as does any custom renderer that handles
32
+ * `{ type: 'updateDataModel', ... }` actions.
33
+ */
34
+ export interface BridgeRenderer {
35
+ process(action: {
36
+ type: 'updateDataModel';
37
+ surfaceId: string;
38
+ path: string;
39
+ value: unknown;
40
+ }): void;
41
+ }
42
+
43
+ /**
44
+ * Bridge options shared by {@link bridgeStream} and
45
+ * {@link bridgeStreamAsync}.
46
+ */
47
+ export interface BridgeOptions {
48
+ /** Target surface ID inside the renderer's data model. */
49
+ surfaceId: string;
50
+ /** Stream registry ID (the same string passed to `acquireStream`). */
51
+ streamId: string;
52
+ /** Slash-separated A2UI data-model path inside the surface. */
53
+ path: string;
54
+ /** Optional dot-separated path to a sub-value within the stream's signal value. */
55
+ select?: string;
56
+ }
57
+
58
+ /**
59
+ * Bridge a registered stream into a renderer's surface data model.
60
+ *
61
+ * If the stream is not yet registered, logs a warning and returns a
62
+ * no-op dispose. Use {@link bridgeStreamAsync} when the stream might
63
+ * register after this call (e.g. DOM source connecting on the same
64
+ * tick as the bridge wiring).
65
+ *
66
+ * Returns a dispose function — call it to detach the effect.
67
+ */
68
+ export function bridgeStream(renderer: BridgeRenderer, opts: BridgeOptions): () => void;
69
+
70
+ /**
71
+ * Async variant — awaits the stream's registration before attaching
72
+ * the effect. Resolves once the bridge is wired (the resolved value
73
+ * is the dispose function).
74
+ */
75
+ export function bridgeStreamAsync(
76
+ renderer: BridgeRenderer,
77
+ opts: BridgeOptions,
78
+ ): Promise<() => void>;
package/core/template.js CHANGED
@@ -137,14 +137,32 @@ function scan(fragment, count) {
137
137
  // bug. Hot template paths fire many times, so warn (not throw):
138
138
  // a thrown error would crash render; warn surfaces the bug class.
139
139
  if (attr.value.includes('{{p:')) {
140
+ // §184 (v0.5.5, FEEDBACK-08 §5): `class` is special-cased
141
+ // in the hint because `.class=${expr}` sets a JS expando
142
+ // property (`element.class = "foo"`) — NOT the `class`
143
+ // attribute / className. Consumers following the original
144
+ // §152 generic hint silently lost CSS classes. For class,
145
+ // suggest `.className=${expr}`; for style suggest the
146
+ // canonical style-object path; for everything else keep
147
+ // the generic property-assignment recipe.
148
+ const a = attr.name;
149
+ const specific =
150
+ a === 'class'
151
+ ? ` .className=\${expression} ← write to the className property (NOT .class, which is an expando)\n` +
152
+ ` class="\${expression}" ← full replacement (whole class string is the expression)\n` +
153
+ ` .classList=\${{foo: true, bar: false}} ← if/when implemented; verify in USAGE.md\n`
154
+ : a === 'style'
155
+ ? ` .style.cssText=\${expression} ← write CSS text to .style.cssText\n` +
156
+ ` style="\${expression}" ← full replacement of the style string\n`
157
+ : ` ${a}="\${expression}" ← full replacement (whole attr is the placeholder)\n` +
158
+ ` .${a}=\${expression} ← property assignment (preferred for objects/functions)\n`;
140
159
  // eslint-disable-next-line no-console
141
160
  console.warn(
142
161
  `[template] Partial attribute interpolation is not supported.\n` +
143
162
  ` Element: <${n.tagName.toLowerCase()}>\n` +
144
- ` Attribute: ${attr.name}="${attr.value.slice(0, 80)}${attr.value.length > 80 ? '…' : ''}"\n` +
163
+ ` Attribute: ${a}="${attr.value.slice(0, 80)}${attr.value.length > 80 ? '…' : ''}"\n` +
145
164
  ` Use one of:\n` +
146
- ` ${attr.name}="\${expression}" ← full replacement (whole attr is the placeholder)\n` +
147
- ` .${attr.name}=\${expression} ← property assignment (preferred for objects/functions)\n` +
165
+ specific +
148
166
  ` Compute the full attr value as a single expression and interpolate the whole.`
149
167
  );
150
168
  }