@adia-ai/adia-ui-factory 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.claude-plugin/plugin.json +12 -3
  2. package/.mcp.json +1 -1
  3. package/CHANGELOG.md +12 -0
  4. package/README.md +24 -1
  5. package/bin/adia-scaffold +93 -13
  6. package/package.json +1 -1
  7. package/references/shell-embed.md +10 -0
  8. package/references/spa-architecture.md +7 -0
  9. package/skills/adia-ui-kit/CHANGELOG.md +41 -0
  10. package/skills/adia-ui-kit/SKILL.md +442 -0
  11. package/skills/adia-ui-kit/assets/figma-make/guidelines/Guidelines.md +87 -0
  12. package/skills/adia-ui-kit/assets/figma-make/guidelines/components.md +142 -0
  13. package/skills/adia-ui-kit/assets/figma-make/guidelines/styles.md +118 -0
  14. package/skills/adia-ui-kit/assets/figma-make/guidelines/tokens.md +120 -0
  15. package/skills/adia-ui-kit/assets/team-scaffold/archive-README.md +30 -0
  16. package/skills/adia-ui-kit/assets/team-scaffold/drafts-README.md +57 -0
  17. package/skills/adia-ui-kit/assets/team-scaffold/inbox-README.md +76 -0
  18. package/skills/adia-ui-kit/assets/team-scaffold/outbox-README.md +46 -0
  19. package/skills/adia-ui-kit/assets/team-scaffold/team-root.README.md +51 -0
  20. package/skills/adia-ui-kit/assets/team-scaffold/tickets-README.md +208 -0
  21. package/skills/adia-ui-kit/assets/team-scaffold/tickets-REGISTRY.md +81 -0
  22. package/skills/adia-ui-kit/assets/templates/AGENTS.template.md +128 -0
  23. package/skills/adia-ui-kit/assets/templates/CLAUDE.template.md +1 -0
  24. package/skills/adia-ui-kit/assets/templates/FEEDBACK-OMISSIONS-DEFENSE.template.md +150 -0
  25. package/skills/adia-ui-kit/assets/templates/FEEDBACK.template.md +213 -0
  26. package/skills/adia-ui-kit/assets/templates/RESPONSE.template.md +114 -0
  27. package/skills/adia-ui-kit/assets/templates/figma-make-guidelines.md +90 -0
  28. package/skills/adia-ui-kit/evals/adversarial-content-trust.json +83 -0
  29. package/skills/adia-ui-kit/evals/evals.json +55 -0
  30. package/skills/adia-ui-kit/evals/routing-corpus.json +121 -0
  31. package/skills/adia-ui-kit/evals/teach-routing-cases.json +92 -0
  32. package/skills/adia-ui-kit/references/capabilities.md +245 -0
  33. package/skills/adia-ui-kit/references/catalog-lookup.md +186 -0
  34. package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v1.md +8 -0
  35. package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v2.md +3585 -0
  36. package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v3.md +1899 -0
  37. package/skills/adia-ui-kit/references/common-gotchas-consumer.md +522 -0
  38. package/skills/adia-ui-kit/references/component-literacy.md +291 -0
  39. package/skills/adia-ui-kit/references/composition.md +245 -0
  40. package/skills/adia-ui-kit/references/correction-loop.md +462 -0
  41. package/skills/adia-ui-kit/references/css-policy.md +343 -0
  42. package/skills/adia-ui-kit/references/decision-model.md +158 -0
  43. package/skills/adia-ui-kit/references/diagnostic-output.md +57 -0
  44. package/skills/adia-ui-kit/references/docs-catalog.md +239 -0
  45. package/skills/adia-ui-kit/references/exclusions.md +225 -0
  46. package/skills/adia-ui-kit/references/feedback-authoring.md +466 -0
  47. package/skills/adia-ui-kit/references/figma-make.md +244 -0
  48. package/skills/adia-ui-kit/references/flows-auth.md +307 -0
  49. package/skills/adia-ui-kit/references/flows-content.md +206 -0
  50. package/skills/adia-ui-kit/references/flows-data.md +348 -0
  51. package/skills/adia-ui-kit/references/flows-interaction.md +181 -0
  52. package/skills/adia-ui-kit/references/flows-shells.md +336 -0
  53. package/skills/adia-ui-kit/references/gap-detection.md +266 -0
  54. package/skills/adia-ui-kit/references/harness.md +284 -0
  55. package/skills/adia-ui-kit/references/loading-protocol.md +406 -0
  56. package/skills/adia-ui-kit/references/local-link.md +198 -0
  57. package/skills/adia-ui-kit/references/mcp.md +401 -0
  58. package/skills/adia-ui-kit/references/migration.md +116 -0
  59. package/skills/adia-ui-kit/references/mission.md +194 -0
  60. package/skills/adia-ui-kit/references/mode-routing.md +298 -0
  61. package/skills/adia-ui-kit/references/patterns-agent.md +223 -0
  62. package/skills/adia-ui-kit/references/patterns-data.md +1065 -0
  63. package/skills/adia-ui-kit/references/patterns-forms.md +223 -0
  64. package/skills/adia-ui-kit/references/patterns-layout.md +326 -0
  65. package/skills/adia-ui-kit/references/patterns-recipes.md +1416 -0
  66. package/skills/adia-ui-kit/references/patterns-shells.md +1097 -0
  67. package/skills/adia-ui-kit/references/patterns-traits.md +259 -0
  68. package/skills/adia-ui-kit/references/patterns-wiring.md +339 -0
  69. package/skills/adia-ui-kit/references/pev-rationale.md +137 -0
  70. package/skills/adia-ui-kit/references/reasoning-gate.md +146 -0
  71. package/skills/adia-ui-kit/references/reasoning-methodologies.md +112 -0
  72. package/skills/adia-ui-kit/references/recon.md +192 -0
  73. package/skills/adia-ui-kit/references/rendering-model.md +431 -0
  74. package/skills/adia-ui-kit/references/response-authoring.md +398 -0
  75. package/skills/adia-ui-kit/references/session-log-schema.md +203 -0
  76. package/skills/adia-ui-kit/references/session-log-template.md +118 -0
  77. package/skills/adia-ui-kit/references/setup.md +652 -0
  78. package/skills/adia-ui-kit/references/skill-hygiene.md +135 -0
  79. package/skills/adia-ui-kit/references/spec-to-ui-reasoning.md +394 -0
  80. package/skills/adia-ui-kit/references/state.md +229 -0
  81. package/skills/adia-ui-kit/references/teach-protocol.md +421 -0
  82. package/skills/adia-ui-kit/references/theme.md +142 -0
  83. package/skills/adia-ui-kit/references/version-check.md +69 -0
  84. package/skills/adia-ui-kit/scripts/audit-decision-trace.mjs +198 -0
  85. package/skills/adia-ui-kit/scripts/audit-kit-roster.mjs +366 -0
  86. package/skills/adia-ui-kit/scripts/changelog-archive.mjs +126 -0
  87. package/skills/adia-ui-kit/scripts/check-adia-versions.py +404 -0
  88. package/skills/adia-ui-kit/scripts/run-skill-evals.mjs +358 -0
  89. package/skills/adia-ui-kit/scripts/validate-session-log.mjs +242 -0
  90. package/skills/adia-ui-kit/skill.json +102 -0
  91. package/skills/adia-ui-migration/CHANGELOG.md +47 -0
  92. package/skills/adia-ui-migration/SKILL.md +381 -0
  93. package/skills/adia-ui-migration/skill.json +29 -0
  94. package/skills/adia-ui-spa/SKILL.md +1 -1
  95. package/skills/adia-ui-verify/SKILL.md +2 -2
@@ -0,0 +1,259 @@
1
+ # Trait Patterns
2
+
3
+ ## What Traits Are
4
+
5
+ Traits are composable behaviors that attach to any HTML element via `defineTrait()`.
6
+ They follow a factory pattern: call the trait function to get an instance, connect it
7
+ to an element, disconnect when done.
8
+
9
+ ```js
10
+ import { pressable, focusable } from '@adiahealth/web-components/traits';
11
+
12
+ const btn = document.querySelector('#my-btn');
13
+ const inst = pressable();
14
+ inst.connect(btn);
15
+ btn.addEventListener('press', () => console.log('pressed!'));
16
+ // Later: inst.disconnect(btn);
17
+ ```
18
+
19
+ On AdiaElement subclasses:
20
+ ```js
21
+ class MyWidget extends AdiaElement {
22
+ static traits = [pressable, focusable]; // auto-connect on mount
23
+ }
24
+ // Or at runtime:
25
+ myElement.addTrait(draggable);
26
+ ```
27
+
28
+ ### Conventions
29
+
30
+ - Every trait sets/removes `data-{trait-name}-{state}` attributes
31
+ - Cleanup on `disconnect()` removes all managed attributes and listeners
32
+ - Traits compose — multiple traits on the same element
33
+ - Traits never modify innerHTML — attributes and listeners only
34
+ - Schema available via `trait.schema`
35
+
36
+ ---
37
+
38
+ ## Interaction Traits
39
+
40
+ ### pressable
41
+ Dispatches `press` event. Sets `data-pressable-pressed` during press.
42
+ ```js
43
+ inst.connect(el);
44
+ el.addEventListener('press', () => { /* handle */ });
45
+ ```
46
+
47
+ ### hoverable
48
+ Sets `data-hoverable-hovered`. Fires `hover-start` / `hover-end`.
49
+
50
+ ### focusable
51
+ Focus ring management. Sets `data-focusable-focused`.
52
+
53
+ ### draggable
54
+ Makes element draggable. Sets `data-draggable-dragging`.
55
+ Events: `drag-start`, `drag-move` (with `dx`, `dy`), `drag-end`.
56
+ ```js
57
+ el.addEventListener('drag-move', (e) => console.log(e.detail.dx, e.detail.dy));
58
+ ```
59
+
60
+ ### inertia-drag
61
+ Like draggable but with momentum/velocity after release. Element continues moving with deceleration.
62
+
63
+ ### resizable
64
+ Adds resize handles. Sets `data-resizable-resizing` during resize.
65
+
66
+ ### tossable
67
+ Fling/throw physics — element can be tossed with velocity.
68
+
69
+ ### scale-press
70
+ Scales element down on press (tactile feedback). Sets `data-scale-press-active`.
71
+
72
+ ---
73
+
74
+ ## Focus & Keyboard Traits
75
+
76
+ ### focus-trap
77
+ Traps Tab focus within a container (modals, dialogs):
78
+ ```js
79
+ focusTrap().connect(modalElement);
80
+ ```
81
+
82
+ ### keyboard-nav
83
+ Roving focus with arrow keys inside a container:
84
+ ```js
85
+ keyboardNav().connect(listElement);
86
+ ```
87
+
88
+ ### hotkey
89
+ Bind keyboard shortcuts:
90
+ ```js
91
+ const inst = hotkey();
92
+ inst.connect(el, { key: 'mod+k' });
93
+ el.addEventListener('hotkey', () => openPalette());
94
+ ```
95
+ `mod` = Cmd on Mac, Ctrl on Windows/Linux.
96
+
97
+ ### roving-tabindex
98
+ Manages tabindex across a group of focusable elements. Only one active at a time.
99
+
100
+ ### typeahead
101
+ Type-ahead search within a list — focuses matching item as user types.
102
+
103
+ ---
104
+
105
+ ## Form Traits
106
+
107
+ ### form-associated
108
+ Makes non-form element participate in `<form>` submission.
109
+
110
+ ### dirty-state
111
+ Tracks if value changed from initial. Sets `data-dirty-state-dirty`.
112
+
113
+ ### disabled-state
114
+ Manages `data-disabled-state-disabled` with pointer/keyboard blocking.
115
+
116
+ ### resettable
117
+ Restores initial value on form reset.
118
+
119
+ ### validation
120
+ Reads rules from `data-validate="required,minlength:3,pattern:..."`.
121
+ Sets `data-validation-invalid`, `data-validation-valid`, `data-validation-message`.
122
+ Dispatches `validated` event with `{ valid, errors }`.
123
+
124
+ ### value-sync
125
+ Syncs attribute `value`, property `value`, and controller state.
126
+ Dispatches `value-change` with `{ value, oldValue, source }`.
127
+
128
+ ---
129
+
130
+ ## Animation Traits
131
+
132
+ ### fade-presence
133
+ Animate in/out with opacity transition. Set `data-fade-presence-visible` to trigger.
134
+
135
+ ### count-up
136
+ Animate number from 0 to target:
137
+ ```js
138
+ el.setAttribute('data-count-up-target', '1000');
139
+ el.setAttribute('data-count-up-active', '');
140
+ ```
141
+
142
+ ### attention-pulse
143
+ Pulsing glow to draw attention.
144
+
145
+ ### glow-focus
146
+ Glowing focus ring effect (alternative to default outline).
147
+
148
+ ### gradient-shift
149
+ Animated gradient background shift.
150
+
151
+ ### confetti / confetti-burst
152
+ Particle celebration effects:
153
+ ```js
154
+ confetti().connect(el);
155
+ el.setAttribute('data-confetti-active', '');
156
+ ```
157
+
158
+ ### spring-animate
159
+ Spring physics animation (bounce, overshoot).
160
+
161
+ ### shimmer-loading
162
+ Shimmer/skeleton loading effect overlay.
163
+
164
+ ### ripple
165
+ Material-style ripple on click/press.
166
+
167
+ ---
168
+
169
+ ## Layout & Positioning Traits
170
+
171
+ ### anchor-positioning
172
+ Position element relative to an anchor (popovers, tooltips):
173
+ ```js
174
+ anchorPositioning().connect(popover, { anchor: triggerEl });
175
+ ```
176
+
177
+ ### portal
178
+ Teleport element to a different DOM location (overlays):
179
+ ```js
180
+ portal().connect(el, { target: document.body });
181
+ ```
182
+
183
+ ### parallax
184
+ Scroll-linked parallax movement.
185
+
186
+ ### magnetic-hover
187
+ Element subtly follows cursor on hover.
188
+
189
+ ### snap-to-grid
190
+ Snaps drag position to a grid:
191
+ ```js
192
+ snapToGrid().connect(el, { gridSize: 16 });
193
+ ```
194
+
195
+ ### scroll-lock
196
+ Prevents body scroll (used with modals/drawers).
197
+
198
+ ---
199
+
200
+ ## Observer Traits
201
+
202
+ ### intersection-observer-trait
203
+ Element visibility tracking:
204
+ ```js
205
+ intersectionObserverTrait().connect(el);
206
+ el.addEventListener('intersect', (e) => console.log(e.detail.isIntersecting));
207
+ ```
208
+
209
+ ### resize-observer-trait
210
+ Element resize tracking:
211
+ ```js
212
+ resizeObserverTrait().connect(el);
213
+ el.addEventListener('resize', (e) => console.log(e.detail.width, e.detail.height));
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Visual Traits
219
+
220
+ ### noise-texture
221
+ Subtle noise overlay on element background.
222
+
223
+ ### drag-ghost
224
+ Custom drag ghost image during drag operations.
225
+
226
+ ### haptic-feedback
227
+ Vibration feedback on interaction (mobile).
228
+
229
+ ### tilt-hover
230
+ 3D tilt effect following cursor position on hover.
231
+
232
+ ### sound-feedback
233
+ Play audio on interaction events.
234
+
235
+ ---
236
+
237
+ ## State Traits
238
+
239
+ ### active-state
240
+ Toggles `data-active-state-active`. Useful for selection:
241
+ ```js
242
+ activeState().connect(el);
243
+ el.addEventListener('click', () => el.toggleAttribute('data-active-state-active'));
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Full Trait Inventory (44)
249
+
250
+ | Category | Traits |
251
+ |----------|--------|
252
+ | Interaction | pressable, hoverable, focusable, draggable, resizable, inertia-drag, tossable, scale-press |
253
+ | Focus/Keyboard | focus-trap, keyboard-nav, hotkey, roving-tabindex, typeahead |
254
+ | Form | form-associated, dirty-state, disabled-state, resettable, validation, value-sync |
255
+ | Animation | fade-presence, count-up, attention-pulse, glow-focus, gradient-shift, confetti, confetti-burst, spring-animate, shimmer-loading, ripple |
256
+ | Layout | anchor-positioning, portal, parallax, magnetic-hover, snap-to-grid, scroll-lock |
257
+ | Observer | intersection-observer-trait, resize-observer-trait |
258
+ | Visual | noise-texture, drag-ghost, haptic-feedback, tilt-hover, sound-feedback |
259
+ | State | active-state |
@@ -0,0 +1,339 @@
1
+ # Wiring, Manifests & Evolution Patterns
2
+
3
+ ## Wiring Contract (A007)
4
+
5
+ Surfaces connect to live data, state, and actions via `wireComponents` messages.
6
+ Always sent AFTER `updateComponents` — hosts must exist before wiring attaches.
7
+
8
+ ### Complete Wired Form
9
+
10
+ ```json
11
+ [
12
+ { "type": "createSurface", "surfaceId": "profile" },
13
+ {
14
+ "type": "updateComponents",
15
+ "surfaceId": "profile",
16
+ "components": [
17
+ { "id": "root", "component": "Card", "size": "lg", "children": ["hdr", "form", "ftr"] },
18
+ { "id": "hdr", "component": "Header", "children": ["title"] },
19
+ { "id": "title", "component": "Text", "variant": "h3", "textContent": "Edit Profile" },
20
+ { "id": "form", "component": "FormContainer", "children": ["fields"] },
21
+ { "id": "fields", "component": "Column", "gap": "md", "children": ["name", "email"] },
22
+ { "id": "name", "component": "TextField", "label": "Name", "name": "name", "value": { "path": "/user/name" }, "required": true },
23
+ { "id": "email", "component": "TextField", "label": "Email", "name": "email", "type": "email", "value": { "path": "/user/email" } },
24
+ { "id": "ftr", "component": "Footer", "children": ["cancel", "save"] },
25
+ { "id": "cancel", "component": "Button", "text": "Cancel", "variant": "outline" },
26
+ { "id": "save", "component": "Button", "text": "Save", "variant": "primary" }
27
+ ]
28
+ },
29
+ {
30
+ "type": "wireComponents",
31
+ "surfaceId": "profile",
32
+ "data": {
33
+ "sources": [
34
+ { "id": "user", "path": "/user", "uri": "resource://users/{userId}", "refresh": "once" }
35
+ ],
36
+ "params": { "userId": { "from": "route", "key": "id" } }
37
+ },
38
+ "state": {
39
+ "controllers": [
40
+ { "id": "form-ctrl", "type": "FormController", "host": "form", "config": { "validateOn": "blur" } }
41
+ ]
42
+ },
43
+ "actions": [
44
+ {
45
+ "event": "form-submit", "source": "form",
46
+ "handler": "submit-resource", "method": "PUT", "uri": "resource://users/{userId}",
47
+ "body": { "from": "event-detail", "key": "data" },
48
+ "onSuccess": { "notify": "Profile saved", "refresh-source": "user" },
49
+ "onError": { "notify": "Save failed. Please try again." }
50
+ },
51
+ { "event": "click", "source": "cancel", "handler": "navigate-back" }
52
+ ]
53
+ }
54
+ ]
55
+ ```
56
+
57
+ ### Four Wiring Blocks (all optional)
58
+
59
+ **data** — Where content comes from:
60
+ ```json
61
+ "data": {
62
+ "sources": [
63
+ { "id": "patient", "path": "/patient", "uri": "resource://patients/{id}", "refresh": "on-focus" },
64
+ { "id": "types", "path": "/visitTypes", "uri": "resource://visit-types", "refresh": "once" }
65
+ ],
66
+ "params": { "id": { "from": "route", "key": "patientId" } }
67
+ }
68
+ ```
69
+
70
+ **state** — Controllers for lifecycle management:
71
+ ```json
72
+ "state": {
73
+ "controllers": [
74
+ { "id": "form", "type": "FormController", "host": "form-root", "config": { "validateOn": "blur" } },
75
+ { "id": "stream", "type": "DataStreamController", "host": "chart", "config": { "max": 60, "throttle": 500 } }
76
+ ]
77
+ }
78
+ ```
79
+
80
+ **actions** — Event -> handler bindings:
81
+ ```json
82
+ "actions": [
83
+ {
84
+ "event": "form-submit", "source": "form-root",
85
+ "handler": "submit-resource", "method": "POST", "uri": "resource://checkins",
86
+ "body": { "from": "event-detail", "key": "data" },
87
+ "onSuccess": { "notify": "Done", "navigate": "/patients/{id}/visits" },
88
+ "onError": { "notify": "Failed" }
89
+ }
90
+ ]
91
+ ```
92
+
93
+ **provides** — Context for child surfaces:
94
+ ```json
95
+ "provides": {
96
+ "context": "patient-ctx",
97
+ "host": "root",
98
+ "shape": { "patient": { "path": "/patient" }, "appointments": { "path": "/appointments" } }
99
+ }
100
+ ```
101
+
102
+ ### Child Context Consumption
103
+
104
+ Children consume from a provider without fetching:
105
+ ```json
106
+ {
107
+ "type": "wireComponents",
108
+ "surfaceId": "insurance-card",
109
+ "data": {
110
+ "sources": [
111
+ { "id": "insurance", "path": "/insurance", "from-context": "patient-ctx", "key": "patient.insurance" }
112
+ ]
113
+ }
114
+ }
115
+ ```
116
+
117
+ No fetch — reads from the nearest ancestor provider with that context name.
118
+
119
+ ### Built-in Handlers
120
+
121
+ | Handler | Purpose | Required fields |
122
+ |---------|---------|-----------------|
123
+ | `submit-resource` | POST/PUT/DELETE to resource URI | `uri`, `method` |
124
+ | `update-model` | Update data model at a path | `path` |
125
+ | `navigate` | Route navigation (`{param}` templates) | `navigate` value |
126
+ | `navigate-back` | Browser history back | — |
127
+ | `controller-command` | Invoke controller command | `controllerId`, `command` |
128
+ | `emit-event` | Re-emit as named CustomEvent | `eventName` |
129
+ | `refresh-source` | Re-fetch a data source | `sourceId` |
130
+ | `notify` | Show toast notification | `message` |
131
+
132
+ ### Built-in Controllers
133
+
134
+ | Type | Manages | Commands |
135
+ |------|---------|----------|
136
+ | `FormController` | Validation, dirty/pristine | `validate`, `reset`, `setFieldError` |
137
+ | `DataStreamController` | Live data buffer | `push`, `connect`, `poll`, `stop`, `clear` |
138
+ | `SelectionController` | Selected items | `select`, `deselect`, `toggle`, `selectAll`, `clear` |
139
+ | `ToggleController` | Boolean on/off | `toggle`, `set` |
140
+ | `AccordionController` | Panel expand/collapse | `open`, `close`, `toggle` |
141
+
142
+ ### Value Extraction
143
+
144
+ ```json
145
+ { "from": "event-detail", "key": "data" } // e.detail.data
146
+ { "from": "event-target", "key": "value" } // e.target.value
147
+ { "from": "model", "path": "/patient/name" } // data model lookup
148
+ { "from": "literal", "value": "scheduled" } // static value
149
+ { "from": "param", "key": "patientId" } // resolved route param
150
+ ```
151
+
152
+ ### Refresh Strategies
153
+
154
+ | Strategy | Behavior |
155
+ |----------|----------|
156
+ | `once` | Fetch on initial wire |
157
+ | `on-focus` | Re-fetch on window focus |
158
+ | `interval:{ms}` | Poll at interval |
159
+ | `stream` | Live via DataStreamController |
160
+ | `on-action:{name}` | Re-fetch after action completes |
161
+
162
+ ### Custom Registration
163
+
164
+ ```js
165
+ import { registerController, registerHandler, registerResolver } from '@adiahealth/a2ui';
166
+
167
+ registerController('CheckinController', async () => (await import('./checkin-ctrl.js')).CheckinController);
168
+ registerHandler('show-modal', async (ctx) => { ctx.source.closest('modal-ui').show = true; });
169
+ registerResolver('fhir', async (uri) => (await fetch(uri.replace('fhir://', 'https://fhir.api/'))).json());
170
+ ```
171
+
172
+ ---
173
+
174
+ ## Surface Manifests (A008)
175
+
176
+ Multiple surfaces declare relationships via a typed manifest.
177
+
178
+ ### Building a Manifest
179
+
180
+ ```js
181
+ import { SurfaceManifest, ManifestRuntime, sharedContextStore } from '@adiahealth/a2ui';
182
+
183
+ const manifest = new SurfaceManifest({ id: 'checkin-flow', name: 'Patient Check-in' });
184
+
185
+ // Add surfaces
186
+ manifest.addSurface('schedule', {
187
+ name: "Today's Schedule",
188
+ route: '/schedule',
189
+ entryPoint: true,
190
+ consumes: { 'patient-ctx': { keys: ['appointments'] } },
191
+ });
192
+
193
+ manifest.addSurface('checkin', {
194
+ name: 'Patient Check-in',
195
+ route: '/patients/:patientId/checkin',
196
+ requiredParams: ['patientId'],
197
+ produces: { 'checkin-result': { type: 'object' } },
198
+ consumes: { 'patient-ctx': { keys: ['patient'] } },
199
+ });
200
+
201
+ // Define shared context
202
+ manifest.defineSharedContext('patient-ctx', {
203
+ shape: { patient: { type: 'object' }, appointments: { type: 'array' } },
204
+ source: { uri: 'resource://patients/{patientId}', refresh: 'on-focus' },
205
+ });
206
+
207
+ // Add associations
208
+ manifest.addAssociation({ type: 'routes-to', from: 'schedule', to: 'checkin', trigger: 'action:begin-checkin' });
209
+ manifest.addAssociation({ type: 'shares-context', from: 'schedule', to: 'checkin', context: 'patient-ctx' });
210
+ manifest.addAssociation({ type: 'feeds', from: 'checkin', to: 'summary', trigger: 'action:submit-checkin',
211
+ mapping: { '/visit': { from: 'action-result' } }
212
+ });
213
+ manifest.addAssociation({ type: 'triggers', from: 'checkin', to: 'schedule', trigger: 'action:submit-checkin',
214
+ effect: 'refresh-source:appointments'
215
+ });
216
+
217
+ // Validate
218
+ const { valid, issues } = manifest.validate();
219
+
220
+ // Serialize / restore
221
+ const json = manifest.toJSON();
222
+ const restored = SurfaceManifest.fromJSON(json);
223
+ ```
224
+
225
+ ### ManifestRuntime
226
+
227
+ ```js
228
+ const runtime = new ManifestRuntime(manifest);
229
+
230
+ await runtime.onSurfaceMount('schedule', { patientId: '123' });
231
+ runtime.onSurfaceUnmount('schedule');
232
+
233
+ // Action fires cross-surface triggers
234
+ await runtime.onActionComplete('checkin', 'submit-checkin', { id: 'visit-456' });
235
+ // -> navigates to summary, feeds data, refreshes schedule
236
+
237
+ const targets = runtime.getNavigationTargets('schedule');
238
+ // -> [{ surfaceId: 'checkin', route: '/patients/:id/checkin', trigger: 'action:begin-checkin' }]
239
+ ```
240
+
241
+ ### ContextStore
242
+
243
+ ```js
244
+ sharedContextStore.define('patient-ctx', { shape: { patient: {} } });
245
+ sharedContextStore.update('patient-ctx', { patient: { name: 'Jane Doe' } });
246
+ sharedContextStore.read('patient-ctx', 'patient.name'); // -> 'Jane Doe'
247
+
248
+ const unsub = sharedContextStore.subscribe('patient-ctx', (data) => console.log(data));
249
+ await sharedContextStore.join('patient-ctx', 'checkin-surface', fetchFn, params);
250
+ sharedContextStore.leave('patient-ctx', 'checkin-surface');
251
+ ```
252
+
253
+ ### Association Types
254
+
255
+ | Type | Direction | When to use |
256
+ |------|-----------|-------------|
257
+ | `routes-to` | A -> B | User navigates from A to B |
258
+ | `feeds` | A -> B | A's output becomes B's input |
259
+ | `shares-context` | A <-> B | Both read the same store |
260
+ | `depends-on` | A -> B | A can't render until B is ready |
261
+ | `triggers` | A -> B | A's action causes side effect on B |
262
+ | `contains` | A -> B | B renders inside A |
263
+ | `slots-into` | A -> B | A declares placement preference in B |
264
+
265
+ ---
266
+
267
+ ## Evolution Engine
268
+
269
+ ### Pattern Export & Import
270
+
271
+ ```js
272
+ import { savePattern, importPattern, buildPatternJSON } from '@adia-ai/a2ui-compose';
273
+
274
+ // Build pattern from generation
275
+ const pattern = buildPatternJSON('login-form', {
276
+ components: result.messages[0].components,
277
+ intent: 'login form with email and password',
278
+ domain: 'forms',
279
+ });
280
+
281
+ // Save (downloads .json + .html in browser)
282
+ savePattern('login-form', {
283
+ messages: result.messages,
284
+ validation: result.validation,
285
+ intent: 'login form',
286
+ canvasHTML: canvas.getHTML(),
287
+ });
288
+
289
+ // Import at runtime
290
+ importPattern(patternJSON); // -> { success: true, name: 'login-form' }
291
+ ```
292
+
293
+ ### Feedback Collection
294
+
295
+ ```js
296
+ import { FeedbackCollector } from '@adia-ai/a2ui-compose';
297
+
298
+ const fb = new FeedbackCollector();
299
+ fb.initFromGeneration(execId, { intent, domain, mode, messages, validation });
300
+ fb.collectFeedback(execId, {
301
+ rating: 5, intentAlignment: 5, visualQuality: 4, componentChoice: 5, userEdited: false, notes: 'Perfect',
302
+ });
303
+ fb.collectPatternFeedback(execId, { shouldBePattern: true, suggestedName: 'login-basic' });
304
+ fb.exportFeedback(); // downloads training JSON
305
+ ```
306
+
307
+ ### Pattern Promotion
308
+
309
+ ```js
310
+ import { evaluatePromotion } from '@adia-ai/a2ui-compose';
311
+
312
+ const result = evaluatePromotion({
313
+ score: 98, feedback: { rating: 5, shouldBePattern: true },
314
+ alignment: { score: 0.9 }, componentTypes: ['Card', 'TextField', 'Button'], intent: 'login form',
315
+ });
316
+ // -> { shouldPromote: true, confidence: 0.97, suggestedName: 'login-form' }
317
+ ```
318
+
319
+ Promotion criteria: `score >= 95 AND rating >= 4 AND alignment >= 0.8`.
320
+
321
+ ### Intent Alignment
322
+
323
+ ```js
324
+ import { checkIntentAlignment } from '@adia-ai/a2ui-compose';
325
+
326
+ const alignment = checkIntentAlignment('login form with email, password, submit', components);
327
+ // -> { score: 0.83, checks: [...], gaps: ['Missing action: "submit"'] }
328
+ ```
329
+
330
+ ### Intent Decomposition
331
+
332
+ ```js
333
+ import { decomposeIntent, composeSubtasks } from '@adia-ai/a2ui-compose';
334
+
335
+ const decomp = decomposeIntent('settings page with profile, notifications, security, billing');
336
+ // -> { shouldDecompose: true, subtasks: [{ intent: '...', label: 'Profile' }, ...], layout: { component: 'Tabs', child: 'Tab' } }
337
+
338
+ const composed = composeSubtasks(decomp.layout, subtaskResults);
339
+ ```