@adia-ai/web-components 0.0.1 → 0.0.3

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 (211) hide show
  1. package/a2ui/index.js +23 -17
  2. package/components/accordion/accordion.js +1 -1
  3. package/components/action-list/action-list.js +1 -1
  4. package/components/agent-artifact/agent-artifact.js +1 -1
  5. package/components/agent-feedback-bar/agent-feedback-bar.js +1 -1
  6. package/components/agent-questions/agent-questions.js +1 -1
  7. package/components/agent-reasoning/agent-reasoning.a2ui.json +14 -8
  8. package/components/agent-reasoning/agent-reasoning.css +1 -1
  9. package/components/agent-reasoning/agent-reasoning.js +9 -9
  10. package/components/agent-reasoning/agent-reasoning.yaml +9 -6
  11. package/components/agent-suggestions/agent-suggestions.js +1 -1
  12. package/components/agent-trace/agent-trace.js +1 -1
  13. package/components/alert/alert.js +1 -1
  14. package/components/avatar/avatar.a2ui.json +0 -10
  15. package/components/avatar/avatar.css +1 -1
  16. package/components/avatar/avatar.js +1 -1
  17. package/components/avatar/avatar.yaml +0 -8
  18. package/components/badge/badge.a2ui.json +6 -6
  19. package/components/badge/badge.js +22 -1
  20. package/components/badge/badge.yaml +8 -5
  21. package/components/block/block.js +1 -1
  22. package/components/breadcrumb/breadcrumb.js +1 -1
  23. package/components/button/button.a2ui.json +0 -11
  24. package/components/button/button.css +1 -1
  25. package/components/button/button.js +3 -3
  26. package/components/button/button.yaml +0 -11
  27. package/components/calendar-picker/calendar-picker.js +25 -19
  28. package/components/canvas/canvas.js +3 -3
  29. package/components/card/card.a2ui.json +0 -21
  30. package/components/card/card.js +2 -2
  31. package/components/card/card.yaml +0 -14
  32. package/components/chart/chart.a2ui.json +9 -8
  33. package/components/chart/chart.js +1 -1
  34. package/components/chart/chart.yaml +7 -5
  35. package/components/chat/chat-input.js +1 -1
  36. package/components/chat/chat.js +2 -2
  37. package/components/check/check.css +1 -1
  38. package/components/check/check.js +2 -2
  39. package/components/code/code.a2ui.json +0 -5
  40. package/components/code/code.js +1 -1
  41. package/components/code/code.yaml +0 -4
  42. package/components/col/col.a2ui.json +0 -20
  43. package/components/col/col.js +1 -1
  44. package/components/col/col.yaml +0 -20
  45. package/components/color-picker/color-picker.a2ui.json +1 -1
  46. package/components/color-picker/color-picker.js +5 -2
  47. package/components/color-picker/color-picker.yaml +1 -1
  48. package/components/command/command.js +22 -11
  49. package/components/description-list/description-list.js +1 -1
  50. package/components/divider/divider.a2ui.json +0 -19
  51. package/components/divider/divider.js +1 -1
  52. package/components/divider/divider.yaml +0 -12
  53. package/components/drawer/drawer.a2ui.json +2 -2
  54. package/components/drawer/drawer.css +1 -1
  55. package/components/drawer/drawer.js +2 -2
  56. package/components/drawer/drawer.yaml +3 -3
  57. package/components/embed/embed.js +1 -1
  58. package/components/empty-state/empty-state.a2ui.json +0 -13
  59. package/components/empty-state/empty-state.js +1 -1
  60. package/components/empty-state/empty-state.yaml +0 -5
  61. package/components/footer/footer.a2ui.json +1 -1
  62. package/components/footer/footer.yaml +1 -1
  63. package/components/grid/grid.a2ui.json +0 -5
  64. package/components/grid/grid.js +1 -1
  65. package/components/grid/grid.yaml +0 -4
  66. package/components/header/header.a2ui.json +1 -1
  67. package/components/header/header.yaml +1 -1
  68. package/components/heatmap/heatmap.a2ui.json +4 -4
  69. package/components/heatmap/heatmap.js +4 -4
  70. package/components/heatmap/heatmap.yaml +4 -4
  71. package/components/icon/icon.a2ui.json +13 -0
  72. package/components/icon/icon.js +2 -2
  73. package/components/icon/icon.yaml +6 -0
  74. package/components/image/image.a2ui.json +10 -21
  75. package/components/image/image.js +1 -1
  76. package/components/image/image.yaml +8 -16
  77. package/components/input/input.a2ui.json +0 -15
  78. package/components/input/input.js +2 -2
  79. package/components/input/input.yaml +0 -12
  80. package/components/inspector/inspector.a2ui.json +0 -5
  81. package/components/inspector/inspector.js +2 -2
  82. package/components/inspector/inspector.yaml +0 -4
  83. package/components/kbd/kbd.js +1 -1
  84. package/components/list/list.a2ui.json +0 -33
  85. package/components/list/list.js +1 -1
  86. package/components/list/list.yaml +0 -27
  87. package/components/menu/menu.a2ui.json +0 -10
  88. package/components/menu/menu.js +9 -3
  89. package/components/menu/menu.yaml +0 -8
  90. package/components/modal/modal.a2ui.json +1 -25
  91. package/components/modal/modal.js +1 -1
  92. package/components/modal/modal.yaml +1 -22
  93. package/components/noodles/noodles.a2ui.json +0 -5
  94. package/components/noodles/noodles.js +1 -1
  95. package/components/noodles/noodles.yaml +0 -5
  96. package/components/otp-input/otp-input.a2ui.json +0 -5
  97. package/components/otp-input/otp-input.js +1 -1
  98. package/components/otp-input/otp-input.yaml +0 -4
  99. package/components/pagination/pagination.a2ui.json +3 -13
  100. package/components/pagination/pagination.css +1 -1
  101. package/components/pagination/pagination.js +1 -1
  102. package/components/pagination/pagination.yaml +5 -14
  103. package/components/pane/pane.a2ui.json +0 -10
  104. package/components/pane/pane.js +1 -1
  105. package/components/pane/pane.yaml +0 -9
  106. package/components/pipeline-status/pipeline-status.js +1 -1
  107. package/components/popover/popover.a2ui.json +0 -14
  108. package/components/popover/popover.js +9 -3
  109. package/components/popover/popover.yaml +0 -11
  110. package/components/progress/progress.a2ui.json +2 -12
  111. package/components/progress/progress.js +1 -1
  112. package/components/progress/progress.yaml +2 -11
  113. package/components/progress-row/progress-row.a2ui.json +3 -13
  114. package/components/progress-row/progress-row.js +1 -1
  115. package/components/progress-row/progress-row.yaml +3 -11
  116. package/components/radio/radio.css +1 -1
  117. package/components/radio/radio.js +2 -2
  118. package/components/range/range.js +1 -1
  119. package/components/rating/rating.js +1 -1
  120. package/components/richtext/richtext.js +2 -2
  121. package/components/row/row.a2ui.json +0 -5
  122. package/components/row/row.js +2 -2
  123. package/components/row/row.yaml +0 -4
  124. package/components/search/search.a2ui.json +0 -5
  125. package/components/search/search.js +1 -1
  126. package/components/search/search.yaml +0 -4
  127. package/components/section/section.a2ui.json +1 -1
  128. package/components/section/section.yaml +1 -1
  129. package/components/segment/segment.js +1 -1
  130. package/components/segmented/segmented.css +1 -1
  131. package/components/segmented/segmented.js +1 -1
  132. package/components/select/select.a2ui.json +5 -0
  133. package/components/select/select.js +13 -3
  134. package/components/select/select.yaml +5 -0
  135. package/components/skeleton/skeleton.a2ui.json +0 -11
  136. package/components/skeleton/skeleton.js +1 -1
  137. package/components/skeleton/skeleton.yaml +0 -5
  138. package/components/slider/slider.a2ui.json +0 -5
  139. package/components/slider/slider.js +1 -1
  140. package/components/slider/slider.yaml +0 -5
  141. package/components/stack/stack.js +1 -1
  142. package/components/stat/stat.js +1 -1
  143. package/components/stepper/stepper.js +1 -1
  144. package/components/stream/stream.js +1 -1
  145. package/components/swiper/swiper.a2ui.json +7 -7
  146. package/components/swiper/swiper.js +2 -2
  147. package/components/swiper/swiper.yaml +8 -6
  148. package/components/switch/switch.css +1 -1
  149. package/components/switch/switch.js +2 -2
  150. package/components/table/table.js +1 -1
  151. package/components/tabs/tab.js +1 -1
  152. package/components/tabs/tabs.js +1 -1
  153. package/components/tag/tag.a2ui.json +0 -5
  154. package/components/tag/tag.js +1 -1
  155. package/components/tag/tag.yaml +0 -4
  156. package/components/text/text.a2ui.json +5 -0
  157. package/components/text/text.css +2 -2
  158. package/components/text/text.js +1 -1
  159. package/components/text/text.yaml +5 -0
  160. package/components/textarea/textarea.js +1 -1
  161. package/components/timeline/timeline.a2ui.json +8 -13
  162. package/components/timeline/timeline.js +1 -1
  163. package/components/timeline/timeline.yaml +6 -11
  164. package/components/toast/toast.a2ui.json +0 -15
  165. package/components/toast/toast.js +1 -1
  166. package/components/toast/toast.yaml +0 -13
  167. package/components/toggle-group/toggle-group.js +1 -1
  168. package/components/toolbar/toolbar.a2ui.json +0 -23
  169. package/components/toolbar/toolbar.js +13 -3
  170. package/components/toolbar/toolbar.yaml +0 -19
  171. package/components/tooltip/tooltip.a2ui.json +0 -10
  172. package/components/tooltip/tooltip.js +2 -2
  173. package/components/tooltip/tooltip.yaml +0 -8
  174. package/components/tree/tree.js +1 -1
  175. package/components/upload/upload.js +1 -1
  176. package/core/markdown.js +1 -1
  177. package/core/provider.js +2 -2
  178. package/package.json +7 -3
  179. package/patterns/a2ui-root/a2ui-root.a2ui.json +118 -0
  180. package/{a2ui/root.js → patterns/a2ui-root/a2ui-root.js} +9 -4
  181. package/patterns/a2ui-root/a2ui-root.yaml +76 -0
  182. package/patterns/adia-chat/adia-chat.js +3 -3
  183. package/patterns/adia-chat/css/adia-chat.streaming.css +2 -2
  184. package/patterns/adia-editor/adia-editor.js +1 -1
  185. package/patterns/app-nav/app-nav.js +1 -1
  186. package/patterns/app-nav-group/app-nav-group.js +2 -2
  187. package/patterns/app-nav-item/app-nav-item.js +1 -1
  188. package/patterns/app-shell/app-shell.js +1 -1
  189. package/patterns/gen-ui/gen-ui.css +3 -3
  190. package/patterns/gen-ui/gen-ui.js +1 -1
  191. package/patterns/index.js +1 -0
  192. package/patterns/section-nav/section-nav.js +1 -1
  193. package/patterns/section-nav-group/section-nav-group.js +1 -1
  194. package/patterns/section-nav-item/section-nav-item.js +1 -1
  195. package/traits/define.js +1 -1
  196. package/a2ui/dockables/action.js +0 -152
  197. package/a2ui/dockables/base.js +0 -30
  198. package/a2ui/dockables/controller.js +0 -97
  199. package/a2ui/dockables/data-source.js +0 -103
  200. package/a2ui/dockables/index.js +0 -6
  201. package/a2ui/dockables/lifecycle.js +0 -84
  202. package/a2ui/dockables/provider.js +0 -59
  203. package/a2ui/manifest-runtime.js +0 -226
  204. package/a2ui/registry.js +0 -200
  205. package/a2ui/renderer.js +0 -361
  206. package/a2ui/stream.js +0 -243
  207. package/a2ui/surface-manifest.js +0 -294
  208. package/a2ui/surface.js +0 -222
  209. package/a2ui/wire-factory.js +0 -134
  210. package/a2ui/wiring-engine.js +0 -209
  211. package/a2ui/wiring-registry.js +0 -342
@@ -1,209 +0,0 @@
1
- /**
2
- * Wiring Engine — Processes wireComponents messages via the Surface dock system.
3
- *
4
- * This is the bridge between the renderer (which receives raw messages)
5
- * and the Surface (which manages dockable objects). It:
6
- *
7
- * 1. Creates/retrieves a Surface for the surfaceId
8
- * 2. Resolves params from the message
9
- * 3. Translates the message sections into typed Dockables via wire-factory
10
- * 4. Docks them in dependency order on the Surface
11
- *
12
- * The external API is unchanged: process(msg), teardown(surfaceId), teardownAll().
13
- */
14
-
15
- import { Surface } from './surface.js';
16
- import { createDockables } from './wire-factory.js';
17
-
18
- export class WiringEngine {
19
- /** @type {Map<string, Surface>} */
20
- #surfaces = new Map();
21
-
22
- /** @type {(surfaceId: string, path: string, data: unknown) => void} */
23
- #updateDataModel;
24
-
25
- /** @type {(surfaceId: string, componentId: string) => HTMLElement | null} */
26
- #getElement;
27
-
28
- /**
29
- * @param {object} opts
30
- * @param {(surfaceId: string, path: string, data: unknown) => void} opts.updateDataModel
31
- * @param {(surfaceId: string, componentId: string) => HTMLElement | null} opts.getElement
32
- */
33
- constructor({ updateDataModel, getElement }) {
34
- this.#updateDataModel = updateDataModel;
35
- this.#getElement = getElement;
36
- }
37
-
38
- /**
39
- * Process a wireComponents message.
40
- * Multiple calls for the same surfaceId are additive (new dockables added,
41
- * same-id dockables hot-swapped).
42
- *
43
- * @param {object} message
44
- */
45
- async process(message) {
46
- const { surfaceId } = message;
47
- if (!surfaceId) return;
48
-
49
- let surface = this.#surfaces.get(surfaceId);
50
- if (!surface) {
51
- surface = this.#createSurface(surfaceId);
52
- this.#surfaces.set(surfaceId, surface);
53
- }
54
-
55
- try {
56
- // Resolve params (before creating dockables, they may need params)
57
- if (message.data?.params) {
58
- surface.setParams(this.#resolveParams(message.data.params));
59
- }
60
-
61
- // Translate message → dockables
62
- const { dockables, initialModel } = createDockables(message);
63
-
64
- // Set initial model state before docking
65
- if (initialModel) {
66
- surface.setInitialModel(initialModel);
67
- }
68
-
69
- // Handle explicit undocking
70
- if (message.undock) {
71
- surface.undockMany(message.undock);
72
- }
73
-
74
- // Dock all in dependency order
75
- // Some dockables have async dock() (controllers), so we dock sequentially
76
- // by kind to maintain order guarantees
77
- for (const dockable of dockables) {
78
- await surface.dock(dockable);
79
- }
80
- } catch (err) {
81
- console.warn(`Wiring: error processing wireComponents for "${surfaceId}":`, err.message);
82
- }
83
- }
84
-
85
- /**
86
- * Tear down all wiring for a surface.
87
- * @param {string} surfaceId
88
- */
89
- teardown(surfaceId) {
90
- const surface = this.#surfaces.get(surfaceId);
91
- if (!surface) return;
92
- surface.undockAll();
93
- this.#surfaces.delete(surfaceId);
94
- }
95
-
96
- /**
97
- * Tear down everything.
98
- */
99
- teardownAll() {
100
- for (const surfaceId of this.#surfaces.keys()) this.teardown(surfaceId);
101
- }
102
-
103
- /**
104
- * Get a surface for inspection/testing.
105
- * @param {string} surfaceId
106
- * @returns {Surface|null}
107
- */
108
- getSurface(surfaceId) {
109
- return this.#surfaces.get(surfaceId) || null;
110
- }
111
-
112
- /**
113
- * Get a wired surface's state (for debugging/inspection).
114
- * @param {string} surfaceId
115
- */
116
- getSurfaceState(surfaceId) {
117
- const surface = this.#surfaces.get(surfaceId);
118
- if (!surface) return null;
119
- const dockables = surface.context.listDockables();
120
- return {
121
- surfaceId,
122
- controllerCount: dockables.filter(d => d.kind === 'controller').length,
123
- sourceCount: dockables.filter(d => d.kind === 'source').length,
124
- actionCount: dockables.filter(d => d.kind === 'action').length,
125
- providerCount: dockables.filter(d => d.kind === 'provider').length,
126
- hasLifecycle: dockables.some(d => d.kind === 'lifecycle'),
127
- totalDocked: dockables.length,
128
- };
129
- }
130
-
131
- // ── Private ────────────────────────────────────────────────
132
-
133
- /**
134
- * Create a Surface wired to the renderer's element map.
135
- */
136
- #createSurface(surfaceId) {
137
- // Create an element proxy that delegates to the renderer's getElement
138
- const elementsProxy = {
139
- get: (componentId) => this.#getElement(surfaceId, componentId),
140
- has: (componentId) => !!this.#getElement(surfaceId, componentId),
141
- };
142
-
143
- // The root element is the surface container in the renderer
144
- const rootElement = this.#getElement(surfaceId, 'root') || document.createElement('div');
145
-
146
- // Build a Map-like wrapper so Surface can use elements.get()
147
- const elements = new Proxy(new Map(), {
148
- get(target, prop) {
149
- if (prop === 'get') return (id) => elementsProxy.get(id);
150
- if (prop === 'has') return (id) => elementsProxy.has(id);
151
- return target[prop];
152
- },
153
- });
154
-
155
- // Wrap updateDataModel so dockables that call ctx.setModel
156
- // also flow through to the renderer's data binding system
157
- const surface = new Surface(surfaceId, rootElement, elements);
158
-
159
- // Monkey-patch setModel on context to also notify the renderer
160
- const originalContext = surface.context;
161
- const origSetModel = originalContext.setModel;
162
- const updateDataModel = this.#updateDataModel;
163
- Object.defineProperty(surface, 'context', {
164
- get() {
165
- const ctx = originalContext;
166
- return {
167
- ...ctx,
168
- setModel(path, value) {
169
- origSetModel(path, value);
170
- updateDataModel(surfaceId, path, value);
171
- },
172
- };
173
- },
174
- });
175
-
176
- return surface;
177
- }
178
-
179
- /**
180
- * Resolve parameter declarations to values.
181
- * @param {Record<string, { from: string, key: string }>} params
182
- * @returns {Record<string, string>}
183
- */
184
- #resolveParams(params) {
185
- const resolved = {};
186
- for (const [name, spec] of Object.entries(params)) {
187
- switch (spec.from) {
188
- case 'route': {
189
- const hash = location.hash.slice(1);
190
- const match = hash.match(new RegExp(`${spec.key}/([^/]+)`));
191
- if (match) resolved[name] = match[1];
192
- break;
193
- }
194
- case 'query': {
195
- const url = new URL(location.href);
196
- const val = url.searchParams.get(spec.key);
197
- if (val) resolved[name] = val;
198
- break;
199
- }
200
- case 'literal':
201
- resolved[name] = spec.value;
202
- break;
203
- default:
204
- if (typeof spec === 'string') resolved[name] = spec;
205
- }
206
- }
207
- return resolved;
208
- }
209
- }
@@ -1,342 +0,0 @@
1
- /**
2
- * Wiring Registry — Runtime lookup for controllers, action handlers, and data resolvers.
3
- *
4
- * Parallel to standardRegistry (type → tag for components), the wiring registry
5
- * maps controller types → factories, handler names → functions, and URI schemes → resolvers.
6
- *
7
- * All entries are lazily loaded — controllers are async factory functions, only imported
8
- * when a surface actually declares them. A surface that only uses actions never loads
9
- * any controller module.
10
- *
11
- * Extensible: consumers register custom controllers, handlers, and resolvers at startup.
12
- * A healthcare app registers CheckinController; a CRM registers PipelineController.
13
- */
14
-
15
- // ═══════════════════════════════════════════════════════════════
16
- // REGISTRY
17
- // ═══════════════════════════════════════════════════════════════
18
-
19
- export const wiringRegistry = {
20
- /** Controller type → async factory function */
21
- controllers: new Map([
22
- ['FormController', async () => (await import('../controllers/form.js')).FormController],
23
- ['DataStreamController', async () => (await import('../controllers/data-stream.js')).DataStreamController],
24
- ['SelectionController', async () => (await import('../controllers/selection.js')).SelectionController],
25
- ['ToggleController', async () => (await import('../controllers/toggle.js')).ToggleController],
26
- ['AccordionController', async () => (await import('../controllers/accordion.js')).AccordionController],
27
- ['StateMachineController', async () => (await import('../controllers/state-machine.js')).StateMachineController],
28
- ]),
29
-
30
- /** Action handler name → handler function */
31
- handlers: new Map([
32
- ['submit-resource', handleSubmitResource],
33
- ['update-model', handleUpdateModel],
34
- ['navigate', handleNavigate],
35
- ['navigate-back', handleNavigateBack],
36
- ['controller-command', handleControllerCommand],
37
- ['emit-event', handleEmitEvent],
38
- ['refresh-source', handleRefreshSource],
39
- ['notify', handleNotify],
40
- ]),
41
-
42
- /** URI scheme → resolver function */
43
- resolvers: new Map([
44
- ['resource', defaultResourceResolver],
45
- ['api', defaultApiResolver],
46
- ['mock', defaultMockResolver],
47
- ]),
48
- };
49
-
50
- // ═══════════════════════════════════════════════════════════════
51
- // REGISTRATION API
52
- // ═══════════════════════════════════════════════════════════════
53
-
54
- /**
55
- * Register a custom controller type.
56
- * @param {string} name — Controller type name
57
- * @param {() => Promise<new (...args: any[]) => any>} factory — Async factory returning the constructor
58
- */
59
- export function registerController(name, factory) {
60
- wiringRegistry.controllers.set(name, factory);
61
- }
62
-
63
- /**
64
- * Register a custom action handler.
65
- * @param {string} name — Handler name
66
- * @param {(context: HandlerContext) => Promise<unknown>} fn — Handler function
67
- */
68
- export function registerHandler(name, fn) {
69
- wiringRegistry.handlers.set(name, fn);
70
- }
71
-
72
- /**
73
- * Register a custom URI resolver.
74
- * @param {string} scheme — URI scheme (e.g., 'resource', 'api', 'custom')
75
- * @param {(uri: string, params: Record<string, string>) => Promise<unknown>} fn — Resolver function
76
- */
77
- export function registerResolver(scheme, fn) {
78
- wiringRegistry.resolvers.set(scheme, fn);
79
- }
80
-
81
- // ═══════════════════════════════════════════════════════════════
82
- // RESOLUTION API
83
- // ═══════════════════════════════════════════════════════════════
84
-
85
- /**
86
- * Resolve a controller type to its constructor.
87
- * @param {string} type — Controller type name
88
- * @returns {Promise<(new (...args: any[]) => any) | null>}
89
- */
90
- export async function resolveController(type) {
91
- const factory = wiringRegistry.controllers.get(type);
92
- if (!factory) return null;
93
- return factory();
94
- }
95
-
96
- /**
97
- * Resolve a handler name to its function.
98
- * @param {string} name — Handler name
99
- * @returns {((context: HandlerContext) => Promise<unknown>) | null}
100
- */
101
- export function resolveHandler(name) {
102
- return wiringRegistry.handlers.get(name) || null;
103
- }
104
-
105
- /**
106
- * Resolve a resource URI to data.
107
- * @param {string} uri — Resource URI (e.g., 'resource://patients/123')
108
- * @param {Record<string, string>} [params] — Resolved parameters
109
- * @returns {Promise<unknown>}
110
- */
111
- export async function resolveData(uri, params = {}) {
112
- // Interpolate params into URI
113
- let resolved = uri;
114
- for (const [key, value] of Object.entries(params)) {
115
- resolved = resolved.replace(`{${key}}`, encodeURIComponent(value));
116
- }
117
-
118
- // Extract scheme
119
- const schemeEnd = resolved.indexOf('://');
120
- if (schemeEnd === -1) throw new Error(`Invalid URI: ${uri}`);
121
- const scheme = resolved.slice(0, schemeEnd);
122
-
123
- const resolver = wiringRegistry.resolvers.get(scheme);
124
- if (!resolver) throw new Error(`No resolver for scheme "${scheme}"`);
125
-
126
- return resolver(resolved, params);
127
- }
128
-
129
- // ═══════════════════════════════════════════════════════════════
130
- // HANDLER CONTEXT (passed to every action handler)
131
- // ═══════════════════════════════════════════════════════════════
132
-
133
- /**
134
- * @typedef {object} HandlerContext
135
- * @property {Event} event — The DOM event that triggered the action
136
- * @property {HTMLElement} source — The source element
137
- * @property {object} action — The action declaration from wireComponents
138
- * @property {object} dataModel — The surface's data model
139
- * @property {(path: string, value: unknown) => void} updateModel — Update the data model
140
- * @property {Record<string, any>} controllers — Map of controllerId → instance
141
- * @property {Record<string, string>} params — Resolved parameters
142
- * @property {(uri: string, params?: object) => Promise<unknown>} resolveData — Data resolver
143
- */
144
-
145
- // ═══════════════════════════════════════════════════════════════
146
- // BUILT-IN HANDLERS
147
- // ═══════════════════════════════════════════════════════════════
148
-
149
- /**
150
- * Submit to a resource URI (POST/PUT/PATCH/DELETE).
151
- */
152
- async function handleSubmitResource(ctx) {
153
- const { action, params } = ctx;
154
- const body = extractValue(action.body, ctx);
155
-
156
- let uri = action.uri;
157
- for (const [key, value] of Object.entries(params)) {
158
- uri = uri.replace(`{${key}}`, encodeURIComponent(value));
159
- }
160
-
161
- const schemeEnd = uri.indexOf('://');
162
- const scheme = schemeEnd > -1 ? uri.slice(0, schemeEnd) : 'api';
163
- const resolver = wiringRegistry.resolvers.get(scheme);
164
-
165
- // For submit, we need a POST-capable resolver
166
- // Default: convert resource:// to /api/ REST convention
167
- const apiPath = uri.replace(/^\w+:\/\//, '/api/');
168
- const response = await fetch(apiPath, {
169
- method: action.method || 'POST',
170
- headers: { 'Content-Type': 'application/json' },
171
- body: JSON.stringify(body),
172
- });
173
-
174
- if (!response.ok) {
175
- const error = await response.text().catch(() => 'Request failed');
176
- throw new Error(error);
177
- }
178
-
179
- return response.json().catch(() => ({}));
180
- }
181
-
182
- /**
183
- * Update the surface data model at a path.
184
- */
185
- async function handleUpdateModel(ctx) {
186
- const { action } = ctx;
187
- const value = extractValue(action.value, ctx);
188
- ctx.updateModel(action.path, value);
189
- }
190
-
191
- /**
192
- * Navigate to a route.
193
- */
194
- async function handleNavigate(ctx) {
195
- const { action, params } = ctx;
196
- let path = action.navigate || action.path || '';
197
- for (const [key, value] of Object.entries(params)) {
198
- path = path.replace(`{${key}}`, encodeURIComponent(value));
199
- }
200
- if (path === 'back') {
201
- history.back();
202
- } else if (path) {
203
- location.hash = path;
204
- }
205
- }
206
-
207
- /**
208
- * Navigate back in browser history.
209
- */
210
- async function handleNavigateBack() {
211
- history.back();
212
- }
213
-
214
- /**
215
- * Invoke a controller command.
216
- */
217
- async function handleControllerCommand(ctx) {
218
- const { action, controllers } = ctx;
219
- const controller = controllers[action.controllerId];
220
- if (!controller?.commands?.[action.command]) {
221
- console.warn(`Wiring: controller "${action.controllerId}" or command "${action.command}" not found`);
222
- return;
223
- }
224
- return controller.commands[action.command](action.args);
225
- }
226
-
227
- /**
228
- * Re-emit as a named custom event on the surface root.
229
- */
230
- async function handleEmitEvent(ctx) {
231
- const { action, source } = ctx;
232
- const detail = extractValue(action.detail, ctx);
233
- source.dispatchEvent(new CustomEvent(action.eventName || action.event, {
234
- bubbles: true,
235
- detail,
236
- }));
237
- }
238
-
239
- /**
240
- * Re-fetch a named data source.
241
- */
242
- async function handleRefreshSource(ctx) {
243
- const sourceId = ctx.action.sourceId;
244
- // In the dock system, the surface context has getDockable.
245
- // From the handler context, we can access it through the adapted ctx.
246
- // For now, dispatch a custom event that the DataSourceDock can listen for.
247
- document.dispatchEvent(new CustomEvent('a2ui-refresh-source', {
248
- detail: { sourceId },
249
- }));
250
- return { refreshSource: sourceId };
251
- }
252
-
253
- /**
254
- * Show a toast/alert notification.
255
- */
256
- async function handleNotify(ctx) {
257
- const { action } = ctx;
258
- const message = typeof action === 'string' ? action : action.message || action.notify;
259
- // Dispatch a notification event that a toast-ui can listen for
260
- document.dispatchEvent(new CustomEvent('a2ui-notify', {
261
- bubbles: true,
262
- detail: { message, variant: action.variant || 'info' },
263
- }));
264
- }
265
-
266
- // ═══════════════════════════════════════════════════════════════
267
- // VALUE EXTRACTION
268
- // ═══════════════════════════════════════════════════════════════
269
-
270
- /**
271
- * Extract a value based on a value descriptor.
272
- * @param {object|undefined} descriptor — { from, key, path, value }
273
- * @param {HandlerContext} ctx
274
- * @returns {unknown}
275
- */
276
- function extractValue(descriptor, ctx) {
277
- if (!descriptor) return undefined;
278
- if (typeof descriptor !== 'object') return descriptor;
279
-
280
- switch (descriptor.from) {
281
- case 'event-detail':
282
- return descriptor.key ? ctx.event?.detail?.[descriptor.key] : ctx.event?.detail;
283
- case 'event-target':
284
- return descriptor.key ? ctx.source?.[descriptor.key] : ctx.source?.value;
285
- case 'model':
286
- return getModelValue(ctx.dataModel, descriptor.path);
287
- case 'literal':
288
- return descriptor.value;
289
- case 'param':
290
- return ctx.params?.[descriptor.key];
291
- default:
292
- return descriptor;
293
- }
294
- }
295
-
296
- /**
297
- * Get a value from a data model by JSON Pointer path.
298
- * @param {object} model
299
- * @param {string} path — JSON Pointer (e.g., "/patient/name")
300
- * @returns {unknown}
301
- */
302
- function getModelValue(model, path) {
303
- if (!path || !model) return undefined;
304
- const segments = path.split('/').filter(Boolean);
305
- let current = model;
306
- for (const seg of segments) {
307
- if (current == null || typeof current !== 'object') return undefined;
308
- current = current[seg];
309
- }
310
- return current;
311
- }
312
-
313
- // ═══════════════════════════════════════════════════════════════
314
- // DEFAULT RESOLVERS
315
- // ═══════════════════════════════════════════════════════════════
316
-
317
- /**
318
- * Default resource:// resolver — maps to REST convention: /api/{path}
319
- */
320
- async function defaultResourceResolver(uri, params) {
321
- const path = uri.replace(/^resource:\/\//, '/api/');
322
- const response = await fetch(path);
323
- if (!response.ok) throw new Error(`Resource fetch failed: ${response.status}`);
324
- return response.json();
325
- }
326
-
327
- /**
328
- * Default api:// resolver — direct URL passthrough.
329
- */
330
- async function defaultApiResolver(uri, params) {
331
- const url = uri.replace(/^api:\/\//, 'https://');
332
- const response = await fetch(url);
333
- if (!response.ok) throw new Error(`API fetch failed: ${response.status}`);
334
- return response.json();
335
- }
336
-
337
- /**
338
- * Default mock:// resolver — returns empty object with the URI as _source.
339
- */
340
- async function defaultMockResolver(uri, params) {
341
- return { _source: uri, _mock: true };
342
- }