@adia-ai/web-components 0.0.2 → 0.0.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 (127) hide show
  1. package/README.md +29 -22
  2. package/a2ui/index.js +23 -17
  3. package/components/accordion/accordion.js +1 -1
  4. package/components/action-list/action-list.js +1 -1
  5. package/components/agent-artifact/agent-artifact.js +1 -1
  6. package/components/agent-feedback-bar/agent-feedback-bar.js +1 -1
  7. package/components/agent-questions/agent-questions.js +1 -1
  8. package/components/agent-reasoning/agent-reasoning.js +1 -1
  9. package/components/agent-suggestions/agent-suggestions.js +1 -1
  10. package/components/agent-trace/agent-trace.js +1 -1
  11. package/components/alert/alert.js +1 -1
  12. package/components/avatar/avatar.js +1 -1
  13. package/components/badge/badge.js +1 -1
  14. package/components/block/block.js +1 -1
  15. package/components/breadcrumb/breadcrumb.js +1 -1
  16. package/components/button/button.js +2 -2
  17. package/components/calendar-picker/calendar-picker.js +2 -2
  18. package/components/canvas/canvas.js +2 -2
  19. package/components/card/card.js +2 -2
  20. package/components/chart/chart.js +1 -1
  21. package/components/chat/chat-input.js +1 -1
  22. package/components/chat/chat.css +1 -2
  23. package/components/chat/chat.js +2 -2
  24. package/components/check/check.js +2 -2
  25. package/components/code/code.css +2 -0
  26. package/components/code/code.js +1 -1
  27. package/components/col/col.js +1 -1
  28. package/components/color-picker/color-picker.js +1 -1
  29. package/components/command/command.js +1 -1
  30. package/components/description-list/description-list.js +1 -1
  31. package/components/divider/divider.js +1 -1
  32. package/components/drawer/drawer.js +1 -1
  33. package/components/embed/embed.js +1 -1
  34. package/components/empty-state/empty-state.js +1 -1
  35. package/components/grid/grid.js +1 -1
  36. package/components/heatmap/heatmap.js +1 -1
  37. package/components/icon/icon.js +2 -2
  38. package/components/image/image.js +1 -1
  39. package/components/input/input.js +2 -2
  40. package/components/inspector/inspector.js +2 -2
  41. package/components/kbd/kbd.js +1 -1
  42. package/components/list/list.js +1 -1
  43. package/components/menu/menu.js +2 -2
  44. package/components/modal/modal.js +1 -1
  45. package/components/noodles/noodles.js +1 -1
  46. package/components/otp-input/otp-input.js +1 -1
  47. package/components/pagination/pagination.js +1 -1
  48. package/components/pane/pane.css +2 -2
  49. package/components/pane/pane.js +1 -1
  50. package/components/pipeline-status/pipeline-status.js +1 -1
  51. package/components/popover/popover.js +2 -2
  52. package/components/progress/progress.js +1 -1
  53. package/components/progress-row/progress-row.js +1 -1
  54. package/components/radio/radio.js +2 -2
  55. package/components/range/range.js +1 -1
  56. package/components/rating/rating.js +1 -1
  57. package/components/richtext/richtext.js +2 -2
  58. package/components/row/row.js +2 -2
  59. package/components/search/search.js +1 -1
  60. package/components/segment/segment.js +1 -1
  61. package/components/segmented/segmented.js +1 -1
  62. package/components/select/select.js +2 -2
  63. package/components/skeleton/skeleton.js +1 -1
  64. package/components/slider/slider.js +1 -1
  65. package/components/stack/stack.js +1 -1
  66. package/components/stat/stat.js +1 -1
  67. package/components/stepper/stepper.js +1 -1
  68. package/components/stream/stream.js +1 -1
  69. package/components/swiper/swiper.js +1 -1
  70. package/components/switch/switch.js +2 -2
  71. package/components/table/table.js +1 -1
  72. package/components/tabs/tab.js +1 -1
  73. package/components/tabs/tabs.js +1 -1
  74. package/components/tag/tag.js +1 -1
  75. package/components/text/text.js +1 -1
  76. package/components/textarea/textarea.js +1 -1
  77. package/components/timeline/timeline.js +1 -1
  78. package/components/toast/toast.js +1 -1
  79. package/components/toggle-group/toggle-group.js +1 -1
  80. package/components/toolbar/toolbar.js +2 -2
  81. package/components/tooltip/tooltip.js +2 -2
  82. package/components/tree/tree.js +1 -1
  83. package/components/upload/upload.js +1 -1
  84. package/core/markdown.js +1 -1
  85. package/core/provider.js +2 -2
  86. package/package.json +7 -3
  87. package/patterns/a2ui-root/a2ui-root.a2ui.json +118 -0
  88. package/{a2ui/root.js → patterns/a2ui-root/a2ui-root.js} +9 -4
  89. package/patterns/a2ui-root/a2ui-root.yaml +76 -0
  90. package/patterns/adia-chat/adia-chat.js +3 -3
  91. package/patterns/adia-chat/css/adia-chat.tokens.css +1 -1
  92. package/patterns/adia-editor/adia-editor.a2ui.json +12 -0
  93. package/patterns/adia-editor/adia-editor.js +1 -1
  94. package/patterns/adia-editor/adia-editor.yaml +17 -0
  95. package/patterns/adia-editor/css/adia-editor.layout.css +70 -3
  96. package/patterns/adia-editor/css/adia-editor.tokens.css +1 -1
  97. package/patterns/app-nav/app-nav.js +1 -1
  98. package/patterns/app-nav-group/app-nav-group.js +2 -2
  99. package/patterns/app-nav-item/app-nav-item.js +1 -1
  100. package/patterns/app-shell/app-shell.js +1 -1
  101. package/patterns/app-shell/css/app-shell.tokens.css +1 -1
  102. package/patterns/gen-ui/gen-ui.js +1 -1
  103. package/patterns/index.js +1 -0
  104. package/patterns/section-nav/section-nav.js +1 -1
  105. package/patterns/section-nav-group/section-nav-group.js +1 -1
  106. package/patterns/section-nav-item/section-nav-item.js +1 -1
  107. package/styles/tokens.css +12 -0
  108. package/traits/define.js +1 -1
  109. package/a2ui/dockables/action.js +0 -152
  110. package/a2ui/dockables/base.js +0 -30
  111. package/a2ui/dockables/controller.js +0 -97
  112. package/a2ui/dockables/data-source.js +0 -103
  113. package/a2ui/dockables/index.js +0 -6
  114. package/a2ui/dockables/lifecycle.js +0 -84
  115. package/a2ui/dockables/provider.js +0 -59
  116. package/a2ui/manifest-runtime.js +0 -226
  117. package/a2ui/registry.js +0 -200
  118. package/a2ui/renderer.js +0 -361
  119. package/a2ui/stream.js +0 -243
  120. package/a2ui/surface-manifest.js +0 -294
  121. package/a2ui/surface.js +0 -222
  122. package/a2ui/wire-factory.js +0 -134
  123. package/a2ui/wiring-engine.js +0 -209
  124. package/a2ui/wiring-registry.js +0 -342
  125. package/patterns/adia-chat/index.html +0 -93
  126. package/patterns/adia-editor/index.html +0 -179
  127. package/patterns/app-shell/index.html +0 -112
@@ -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
- }
@@ -1,93 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>AdiaUI — Chat</title>
7
- <link rel="stylesheet" href="../../styles/tokens.css" />
8
- <link rel="stylesheet" href="../../styles/styles.css" />
9
- <link rel="stylesheet" href="adia-chat.css" />
10
- <style>
11
- body {
12
- font-family: var(--a-font-family);
13
- margin: 0;
14
- height: 100dvh;
15
- display: flex;
16
- flex-direction: column;
17
- }
18
- </style>
19
- </head>
20
- <body>
21
-
22
- <adia-chat-ui id="chat" proxy-url="/api/chat" model="claude-haiku-4-5-20251001"
23
- style="flex:1; border:none; border-radius:0;">
24
- <header>
25
- <span data-chat-name>Factory Chat</span>
26
- <span data-chat-status></span>
27
- </header>
28
- <section data-chat-messages>
29
- <empty-state-ui data-chat-empty icon="chat-circle"
30
- heading="AdiaUI Chat"
31
- description="Type a message to start a conversation. Connects to LLM model via proxy if available.">
32
- </empty-state-ui>
33
- </section>
34
- <footer>
35
- <chat-input-ui data-chat-input placeholder="Message Claude..."></chat-input-ui>
36
- </footer>
37
- </adia-chat-ui>
38
-
39
- <script type="module">
40
- import '../../components/index.js';
41
- import '../../patterns/index.js';
42
-
43
- const chat = document.getElementById('chat');
44
-
45
- // Wire the model picker inside chat-input-ui. Same list as
46
- // /site/playground/gen-ui — extract to a shared constant when a third
47
- // consumer appears.
48
- customElements.whenDefined('chat-input-ui').then(() => {
49
- const chatInput = chat.querySelector('[data-chat-input]');
50
- if (!chatInput) return;
51
- chatInput.models = [
52
- { label: 'Anthropic', options: [
53
- { value: 'claude-haiku-4-5-20251001', label: 'Haiku 4.5' },
54
- { value: 'claude-sonnet-4-6', label: 'Sonnet 4.6' },
55
- ]},
56
- { label: 'OpenAI', options: [
57
- { value: 'gpt-4o-mini', label: 'GPT-4o Mini' },
58
- ]},
59
- { label: 'Google', options: [
60
- { value: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash' },
61
- ]},
62
- ];
63
- chatInput.model = 'claude-haiku-4-5-20251001';
64
- // Keep <adia-chat-ui>'s `model` attribute in lockstep with the chat-input
65
- // picker so every send uses the user's current selection.
66
- chatInput.addEventListener('change', () => {
67
- if (chatInput.model) chat.setAttribute('model', chatInput.model);
68
- });
69
- });
70
-
71
- // Test proxy availability — fallback to simulated if not running
72
- fetch('/api/chat', { method: 'OPTIONS' }).catch(() => {
73
- chat.removeAttribute('proxy-url');
74
- const empty = chat.querySelector('[data-chat-empty]');
75
- if (empty) empty.setAttribute('description', 'No API proxy detected. Run `node server.js` with API keys. Responses are simulated.');
76
-
77
- chat.addEventListener('submit', async (e) => {
78
- const { text } = e.detail;
79
- chat.appendMessage({ role: 'user', content: text });
80
- chat.appendMessage({ role: 'assistant', content: '' });
81
- chat.startStreaming();
82
-
83
- const response = `You said: "${text}"\n\nThis is a **simulated** response.\n\n\`\`\`js\nconsole.log("Hello from AdiaUI!");\n\`\`\`\n\nStart the proxy with \`node server.js\` for real LLM responses.`;
84
- for (const char of response) {
85
- await new Promise(r => setTimeout(r, 12));
86
- chat.appendChunk(char);
87
- }
88
- chat.stopStreaming();
89
- });
90
- });
91
- </script>
92
- </body>
93
- </html>