@adia-ai/web-components 0.4.7 → 0.4.9

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 (236) hide show
  1. package/README.md +39 -0
  2. package/USAGE.md +255 -2
  3. package/components/accordion/accordion.a2ui.json +3 -0
  4. package/components/accordion/accordion.d.ts +12 -2
  5. package/components/accordion/accordion.yaml +4 -0
  6. package/components/action-list/action-list.a2ui.json +18 -1
  7. package/components/action-list/action-list.d.ts +21 -2
  8. package/components/action-list/action-list.yaml +14 -0
  9. package/components/agent-artifact/agent-artifact.a2ui.json +11 -1
  10. package/components/agent-artifact/agent-artifact.d.ts +17 -2
  11. package/components/agent-artifact/agent-artifact.yaml +9 -0
  12. package/components/agent-feedback-bar/agent-feedback-bar.a2ui.json +10 -1
  13. package/components/agent-feedback-bar/agent-feedback-bar.d.ts +19 -2
  14. package/components/agent-feedback-bar/agent-feedback-bar.yaml +8 -0
  15. package/components/agent-questions/agent-questions.a2ui.json +14 -1
  16. package/components/agent-questions/agent-questions.d.ts +19 -2
  17. package/components/agent-questions/agent-questions.yaml +11 -0
  18. package/components/agent-reasoning/agent-reasoning.a2ui.json +29 -3
  19. package/components/agent-reasoning/agent-reasoning.d.ts +33 -2
  20. package/components/agent-reasoning/agent-reasoning.yaml +20 -0
  21. package/components/agent-suggestions/agent-suggestions.a2ui.json +18 -1
  22. package/components/agent-suggestions/agent-suggestions.d.ts +21 -2
  23. package/components/agent-suggestions/agent-suggestions.yaml +14 -0
  24. package/components/agent-trace/agent-trace.a2ui.json +8 -1
  25. package/components/agent-trace/agent-trace.d.ts +17 -2
  26. package/components/agent-trace/agent-trace.yaml +4 -0
  27. package/components/alert/alert.a2ui.json +1 -0
  28. package/components/alert/alert.d.ts +12 -2
  29. package/components/aside/aside.a2ui.json +1 -0
  30. package/components/avatar/avatar.a2ui.json +3 -0
  31. package/components/avatar/avatar.d.ts +3 -2
  32. package/components/avatar/avatar.yaml +4 -0
  33. package/components/badge/badge.a2ui.json +3 -0
  34. package/components/badge/badge.d.ts +3 -2
  35. package/components/badge/badge.yaml +4 -0
  36. package/components/block/block.a2ui.json +1 -0
  37. package/components/block/block.d.ts +3 -2
  38. package/components/breadcrumb/breadcrumb.a2ui.json +5 -0
  39. package/components/breadcrumb/breadcrumb.d.ts +3 -2
  40. package/components/breadcrumb/breadcrumb.yaml +6 -0
  41. package/components/button/button.a2ui.json +3 -0
  42. package/components/button/button.d.ts +12 -2
  43. package/components/button/button.yaml +5 -0
  44. package/components/calendar-picker/calendar-picker.a2ui.json +1 -0
  45. package/components/canvas/canvas.a2ui.json +1 -0
  46. package/components/canvas/canvas.d.ts +18 -2
  47. package/components/canvas/canvas.yaml +12 -0
  48. package/components/card/card.a2ui.json +1 -0
  49. package/components/card/card.d.ts +12 -2
  50. package/components/chart/chart.a2ui.json +4 -0
  51. package/components/chart/chart.d.ts +18 -2
  52. package/components/chart/chart.yaml +5 -0
  53. package/components/chart-legend/chart-legend.a2ui.json +19 -1
  54. package/components/chart-legend/chart-legend.d.ts +21 -2
  55. package/components/chart-legend/chart-legend.yaml +15 -0
  56. package/components/chat-thread/chat-thread.a2ui.json +12 -1
  57. package/components/chat-thread/chat-thread.d.ts +19 -2
  58. package/components/chat-thread/chat-thread.yaml +7 -0
  59. package/components/check/check.a2ui.json +1 -0
  60. package/components/code/code.a2ui.json +37 -7
  61. package/components/code/code.d.ts +30 -0
  62. package/components/code/code.yaml +29 -6
  63. package/components/col/col.a2ui.json +1 -0
  64. package/components/col/col.d.ts +3 -2
  65. package/components/color-picker/class.js +59 -1
  66. package/components/color-picker/color-picker.a2ui.json +37 -0
  67. package/components/color-picker/color-picker.d.ts +70 -8
  68. package/components/color-picker/color-picker.yaml +53 -0
  69. package/components/command/command.a2ui.json +12 -1
  70. package/components/command/command.d.ts +21 -2
  71. package/components/command/command.yaml +7 -0
  72. package/components/demo-toggle/demo-toggle.a2ui.json +8 -1
  73. package/components/demo-toggle/demo-toggle.d.ts +17 -2
  74. package/components/demo-toggle/demo-toggle.yaml +4 -0
  75. package/components/description-list/description-list.a2ui.json +1 -0
  76. package/components/description-list/description-list.d.ts +3 -2
  77. package/components/divider/divider.a2ui.json +1 -0
  78. package/components/divider/divider.d.ts +3 -2
  79. package/components/drawer/drawer.a2ui.json +1 -0
  80. package/components/drawer/drawer.d.ts +12 -2
  81. package/components/embed/embed.a2ui.json +1 -0
  82. package/components/embed/embed.d.ts +3 -2
  83. package/components/empty-state/empty-state.a2ui.json +3 -0
  84. package/components/empty-state/empty-state.d.ts +3 -2
  85. package/components/empty-state/empty-state.yaml +4 -0
  86. package/components/feed/feed.a2ui.json +9 -1
  87. package/components/feed/feed.d.ts +12 -2
  88. package/components/feed/feed.yaml +8 -1
  89. package/components/field/field.a2ui.json +1 -0
  90. package/components/field/field.d.ts +3 -2
  91. package/components/fields/fields.a2ui.json +1 -0
  92. package/components/fields/fields.d.ts +3 -2
  93. package/components/footer/footer.a2ui.json +1 -0
  94. package/components/grid/grid.a2ui.json +1 -0
  95. package/components/grid/grid.d.ts +3 -2
  96. package/components/header/header.a2ui.json +1 -0
  97. package/components/heatmap/heatmap.a2ui.json +12 -2
  98. package/components/heatmap/heatmap.d.ts +20 -2
  99. package/components/heatmap/heatmap.yaml +17 -2
  100. package/components/icon/icon.a2ui.json +1 -0
  101. package/components/icon/icon.d.ts +3 -2
  102. package/components/image/image.a2ui.json +3 -0
  103. package/components/image/image.d.ts +3 -2
  104. package/components/image/image.yaml +4 -0
  105. package/components/index.js +8 -0
  106. package/components/input/input.a2ui.json +4 -0
  107. package/components/input/input.yaml +6 -0
  108. package/components/inspector/inspector.a2ui.json +5 -0
  109. package/components/inspector/inspector.d.ts +3 -2
  110. package/components/inspector/inspector.yaml +6 -0
  111. package/components/kbd/kbd.a2ui.json +1 -0
  112. package/components/kbd/kbd.d.ts +3 -2
  113. package/components/link/link.a2ui.json +12 -1
  114. package/components/link/link.d.ts +19 -2
  115. package/components/link/link.yaml +7 -0
  116. package/components/list/list.a2ui.json +14 -1
  117. package/components/list/list.d.ts +19 -2
  118. package/components/list/list.yaml +11 -0
  119. package/components/menu/menu.a2ui.json +14 -1
  120. package/components/menu/menu.d.ts +19 -2
  121. package/components/menu/menu.yaml +11 -0
  122. package/components/modal/modal.a2ui.json +1 -0
  123. package/components/modal/modal.d.ts +12 -2
  124. package/components/nav/nav.a2ui.json +16 -1
  125. package/components/nav/nav.d.ts +21 -2
  126. package/components/nav/nav.yaml +10 -0
  127. package/components/nav-group/nav-group.a2ui.json +12 -1
  128. package/components/nav-group/nav-group.d.ts +19 -2
  129. package/components/nav-group/nav-group.yaml +7 -0
  130. package/components/nav-item/nav-item.a2ui.json +16 -1
  131. package/components/nav-item/nav-item.d.ts +21 -2
  132. package/components/nav-item/nav-item.yaml +10 -0
  133. package/components/noodles/noodles.a2ui.json +47 -2
  134. package/components/noodles/noodles.d.ts +42 -2
  135. package/components/noodles/noodles.yaml +32 -0
  136. package/components/option-card/option-card.a2ui.json +3 -0
  137. package/components/option-card/option-card.yaml +4 -0
  138. package/components/otp-input/otp-input.a2ui.json +15 -2
  139. package/components/otp-input/otp-input.d.ts +11 -0
  140. package/components/otp-input/otp-input.yaml +10 -2
  141. package/components/page/page.a2ui.json +1 -0
  142. package/components/page/page.d.ts +3 -2
  143. package/components/pagination/pagination.a2ui.json +8 -1
  144. package/components/pagination/pagination.d.ts +17 -2
  145. package/components/pagination/pagination.yaml +4 -0
  146. package/components/pane/pane.a2ui.json +8 -1
  147. package/components/pane/pane.d.ts +12 -2
  148. package/components/pane/pane.yaml +7 -1
  149. package/components/pipeline-status/pipeline-status.a2ui.json +1 -0
  150. package/components/pipeline-status/pipeline-status.d.ts +3 -2
  151. package/components/popover/popover.a2ui.json +1 -0
  152. package/components/popover/popover.d.ts +3 -2
  153. package/components/progress/progress.a2ui.json +1 -0
  154. package/components/progress/progress.d.ts +3 -2
  155. package/components/progress-row/progress-row.a2ui.json +3 -0
  156. package/components/progress-row/progress-row.d.ts +3 -2
  157. package/components/progress-row/progress-row.yaml +4 -0
  158. package/components/radio/radio.a2ui.json +1 -0
  159. package/components/range/range.a2ui.json +1 -0
  160. package/components/rating/rating.a2ui.json +1 -0
  161. package/components/richtext/richtext.a2ui.json +1 -0
  162. package/components/richtext/richtext.d.ts +3 -2
  163. package/components/row/row.a2ui.json +1 -0
  164. package/components/row/row.d.ts +12 -2
  165. package/components/search/search.a2ui.json +1 -0
  166. package/components/section/section.a2ui.json +1 -0
  167. package/components/segment/segment.a2ui.json +3 -0
  168. package/components/segment/segment.d.ts +3 -2
  169. package/components/segment/segment.yaml +4 -0
  170. package/components/segmented/segmented.a2ui.json +1 -0
  171. package/components/select/select.a2ui.json +3 -0
  172. package/components/select/select.yaml +4 -0
  173. package/components/skeleton/skeleton.a2ui.json +1 -0
  174. package/components/skeleton/skeleton.d.ts +3 -2
  175. package/components/slider/slider.a2ui.json +1 -0
  176. package/components/stack/stack.a2ui.json +1 -0
  177. package/components/stack/stack.d.ts +3 -2
  178. package/components/stat/stat.a2ui.json +1 -0
  179. package/components/step-progress/step-progress.a2ui.json +1 -0
  180. package/components/step-progress/step-progress.d.ts +3 -2
  181. package/components/stepper/stepper.a2ui.json +3 -0
  182. package/components/stepper/stepper.d.ts +3 -2
  183. package/components/stepper/stepper.yaml +4 -0
  184. package/components/stream/stream.a2ui.json +8 -1
  185. package/components/stream/stream.d.ts +21 -2
  186. package/components/stream/stream.yaml +4 -0
  187. package/components/swatch/class.js +362 -15
  188. package/components/swatch/swatch.a2ui.json +69 -1
  189. package/components/swatch/swatch.css +150 -0
  190. package/components/swatch/swatch.d.ts +46 -2
  191. package/components/swatch/swatch.yaml +67 -1
  192. package/components/swiper/swiper.a2ui.json +21 -2
  193. package/components/swiper/swiper.d.ts +28 -2
  194. package/components/swiper/swiper.yaml +15 -0
  195. package/components/switch/switch.a2ui.json +1 -0
  196. package/components/table/table.a2ui.json +87 -5
  197. package/components/table/table.d.ts +73 -2
  198. package/components/table/table.yaml +62 -2
  199. package/components/table-toolbar/table-toolbar.a2ui.json +12 -0
  200. package/components/table-toolbar/table-toolbar.d.ts +18 -2
  201. package/components/table-toolbar/table-toolbar.yaml +13 -0
  202. package/components/tabs/tabs.a2ui.json +10 -1
  203. package/components/tabs/tabs.d.ts +17 -2
  204. package/components/tabs/tabs.yaml +8 -0
  205. package/components/tag/tag.a2ui.json +12 -1
  206. package/components/tag/tag.d.ts +19 -2
  207. package/components/tag/tag.yaml +7 -0
  208. package/components/text/text.a2ui.json +1 -0
  209. package/components/text/text.d.ts +3 -2
  210. package/components/textarea/textarea.a2ui.json +1 -0
  211. package/components/timeline/timeline.a2ui.json +14 -1
  212. package/components/timeline/timeline.d.ts +17 -2
  213. package/components/timeline/timeline.yaml +11 -1
  214. package/components/toast/toast.a2ui.json +1 -0
  215. package/components/toast/toast.d.ts +12 -2
  216. package/components/toggle-group/toggle-group.a2ui.json +8 -1
  217. package/components/toggle-group/toggle-group.d.ts +17 -2
  218. package/components/toggle-group/toggle-group.yaml +4 -0
  219. package/components/toggle-scheme/toggle-scheme.a2ui.json +14 -1
  220. package/components/toggle-scheme/toggle-scheme.d.ts +19 -2
  221. package/components/toggle-scheme/toggle-scheme.yaml +11 -0
  222. package/components/toolbar/toolbar.a2ui.json +3 -0
  223. package/components/toolbar/toolbar.d.ts +3 -2
  224. package/components/toolbar/toolbar.yaml +4 -0
  225. package/components/tooltip/tooltip.a2ui.json +1 -0
  226. package/components/tooltip/tooltip.d.ts +3 -2
  227. package/components/tree/tree.a2ui.json +18 -1
  228. package/components/tree/tree.d.ts +21 -2
  229. package/components/tree/tree.yaml +14 -0
  230. package/components/upload/upload.a2ui.json +1 -0
  231. package/core/icons-phosphor.js +93 -0
  232. package/core/icons.js +92 -90
  233. package/core/index.js +5 -0
  234. package/index.d.ts +87 -79
  235. package/index.js +7 -0
  236. package/package.json +3 -2
package/README.md CHANGED
@@ -175,6 +175,45 @@ Authoring rules (enforced by `ui-audit-coherence`):
175
175
  5. **Light DOM only.** No `::part()`, `::slotted()`, shadow roots. Use
176
176
  attribute selectors on children: `:scope > [slot="icon"]`.
177
177
 
178
+ ### `static template` is a closure — import what it references
179
+
180
+ The `static template` property is a regular JavaScript closure (typically a tagged template literal — `() => html` followed by the template body). Any signal, variable, or function it references must be **lexically in scope** at the file where the class is defined. This is JavaScript scoping, not anything AdiaUI-specific — but it surprises authors coming from frameworks where templates magically receive props.
181
+
182
+ ```js
183
+ // ❌ WRONG — ReferenceError at runtime: minL is not defined
184
+ // (no import; the template function can't see minL just because
185
+ // it's exported elsewhere in the monorepo)
186
+ class MyPanel extends UIElement {
187
+ static template = () => html`<div>L: ${minL.value}</div>`;
188
+ }
189
+
190
+ // ✅ RIGHT — import the signal locally
191
+ import { minL } from './state.js';
192
+
193
+ class MyPanel extends UIElement {
194
+ static template = () => html`<div>L: ${minL.value}</div>`;
195
+ }
196
+ ```
197
+
198
+ The error surfaces in the browser console as `ReferenceError: minL is not defined` at first render — not at module load, not at `tsc --noEmit`. **The trace points at the template function body**, not at the missing import, which is the disorienting part.
199
+
200
+ If your primitive needs external reactive state that varies per-instance, **expose it as a property** rather than reaching for module-scoped signals:
201
+
202
+ ```js
203
+ // ✅ Best — per-instance reactive prop
204
+ class MyPanel extends UIElement {
205
+ static properties = {
206
+ minL: { type: Number, default: 0 },
207
+ };
208
+ static template = el => html`<div>L: ${el.minL}</div>`;
209
+ }
210
+
211
+ // Consumer:
212
+ <my-panel .minL=${minL}></my-panel> // signal binds reactively per ADR-template-binding
213
+ ```
214
+
215
+ The `el` parameter is the element instance — every signal-backed property is reactively read.
216
+
178
217
  Full authoring contract: [`docs/specs/component-token-contract.md`](../../docs/specs/component-token-contract.md).
179
218
  The `adia-ui-author` skill encodes the 20 non-negotiable rules.
180
219
 
package/USAGE.md CHANGED
@@ -166,11 +166,46 @@ function MinLSlider({ minL, onMinLChange }) {
166
166
  <slider-ui value={minL * 100} on:change={e => minL = e.detail.value / 100}></slider-ui>
167
167
  ```
168
168
 
169
+ ### Derived reactive bindings
170
+
171
+ When the value you want to render is a **transformation of a signal** — e.g. you store `0..1` but the slider displays `0..100`, or the displayed text is `${count} items` — pass a **function** as the binding. The template engine detects functions and wraps them in an `effect()`, so the binding re-evaluates whenever any signal read inside the function changes.
172
+
173
+ ```js
174
+ // ✅ Derived reactive — re-evaluates whenever minL changes
175
+ html`<slider-ui .value=${() => minL.value * 100} min="0" max="100" />`
176
+
177
+ // ✅ Direct signal — re-evaluates whenever minL changes
178
+ html`<slider-ui .value=${minL} min="0" max="1" step="0.01" />`
179
+
180
+ // ❌ Static snapshot — reads minL.value ONCE at render time; thumb never moves
181
+ html`<slider-ui .value=${minL.value * 100} />`
182
+
183
+ // ❌ Function as the value, not a binding — slider receives the function object, not its result
184
+ html`<slider-ui .value=${(v) => v * 100} />`
185
+ ```
186
+
187
+ The rule: **inside `.prop=${…}`, anything that reads `.value` directly is a snapshot. Wrap in `() => …` to get reactivity.**
188
+
189
+ For more complex derived values, `computed()` gives you a named reactive holder you can pass directly or read in templates:
190
+
191
+ ```js
192
+ import { signal, computed, html } from '@adia-ai/web-components/core';
193
+
194
+ const minL = signal(0.4);
195
+ const displayPct = computed(() => Math.round(minL.value * 100));
196
+
197
+ html`<slider-ui .value=${displayPct} />` // ✅ pass the computed signal direct
198
+ html`<text-ui>${displayPct}</text-ui>` // ✅ same in text bindings
199
+ html`<text-ui>${() => `${displayPct.value}%`}</text-ui>` // ✅ function form with template literal
200
+ ```
201
+
202
+ `computed()` memoizes (re-runs only when its dependencies change) and gives you a stable handle you can pass to multiple bindings without re-instantiating the effect at every render.
203
+
169
204
  ### The eager-evaluation trap
170
205
 
171
206
  Reported real-world bug: a consumer wrote `.value=${signal.value * 100}` and reported "the slider doesn't move on undo." The slider IS reactive; the binding pattern wasn't. `signal.value * 100` evaluates eagerly to a number; the template system never sees the signal, so it can't subscribe.
172
207
 
173
- **Fix:** pass `${signal}` (signal direct) or `${() => signal.value * 100}` (function as value). Both get effect-wrapped.
208
+ **Fix:** pass `${signal}` (signal direct) or `${() => signal.value * 100}` (function as value). Both get effect-wrapped. See the "Derived reactive bindings" section above for the full pattern.
174
209
 
175
210
  ---
176
211
 
@@ -218,7 +253,7 @@ Some primitives emit named events beyond `change`/`input`:
218
253
  - `<select-ui>` — emits `action` for option-callbacks with `detail.action`
219
254
  - `<search-ui>` — emits `search` after debounce with `detail.value`
220
255
  - `<chat-composer>` — emits `composer-submit` on Enter with `detail.value`
221
- - `<color-picker-ui>` — emits multiple typed events (`input`, `change`, `format-change`) with structured detail
256
+ - `<color-picker-ui>` — emits `input` + `change` with rich detail (`{ value, l, c, h, hex, oklch }`)
222
257
 
223
258
  Each primitive's yaml lists its complete event surface. Live demos at `https://ui-kit.exe.xyz/site/components/<name>`.
224
259
 
@@ -226,6 +261,90 @@ Each primitive's yaml lists its complete event surface. Live demos at `https://u
226
261
 
227
262
  All standard events bubble. Internal-routing collisions (e.g. a trait listening for an event then re-emitting one with the same name) are avoided by name discipline — see the `event-name-collision` memory entry for the lesson.
228
263
 
264
+ ### Authoring events in yaml (for contributors)
265
+
266
+ > *This subsection is for contributors authoring new primitives or extending an existing primitive's event surface. If you're a consumer integrating AdiaUI, skip ahead.*
267
+
268
+ Each component's `<name>.yaml` declares its event surface in a top-level `events:` block. The build pipeline (`npm run build:components`) carries that block into `<name>.a2ui.json` under `x-adiaui.events`, and v0.4.8 §80 codegen reads it from there to emit typed `addEventListener` overloads + `CustomEvent<X>` aliases in `<name>.d.ts`. **The yaml is the single source of truth**; the sidecar + `.d.ts` regenerate from it.
269
+
270
+ #### When to add an `events:` entry
271
+
272
+ Whenever your primitive's `class.js` calls `this.dispatchEvent(new CustomEvent('NAME', ...))` or `new Event('NAME', ...)`, the yaml needs a matching entry. The `check-form-bearing-dts.mjs` audit (v0.5.0-staged) catches drift between dispatch literals and `.d.ts` typings; populating yaml correctly prevents that drift.
273
+
274
+ #### The shape
275
+
276
+ ```yaml
277
+ events:
278
+ open:
279
+ description: Fired when the modal becomes visible.
280
+ detail:
281
+ trigger:
282
+ type: string
283
+ description: One of 'click' | 'keyboard' | 'programmatic'.
284
+ close:
285
+ description: Fired when the modal is dismissed.
286
+ # No detail block → CustomEvent<unknown> at the .d.ts surface.
287
+ toggle:
288
+ description: Fired when state flips.
289
+ detail:
290
+ open:
291
+ type: boolean
292
+ description: New open state after the toggle.
293
+ bubbles: false # default is true; set false only for non-bubbling events
294
+ ```
295
+
296
+ #### What ends up where
297
+
298
+ For a yaml entry like the `toggle` event above on `<my-thing-ui>`:
299
+
300
+ | Surface | Shape |
301
+ |---|---|
302
+ | `<my-thing-ui>.a2ui.json` | `x-adiaui.events.toggle = { description, detail: { open: { type, description } }, bubbles? }` |
303
+ | `<my-thing-ui>.d.ts` (codegen) | `interface MyThingToggleEventDetail { open: boolean; }` + `type MyThingToggleEvent = CustomEvent<MyThingToggleEventDetail>` + `addEventListener` overload for `'toggle'` |
304
+ | Runtime | `this.dispatchEvent(new CustomEvent('toggle', { detail: { open: this.open }, bubbles: true }))` |
305
+
306
+ #### Detail-key types
307
+
308
+ The codegen recognizes these `type:` values inside a detail key:
309
+
310
+ | yaml `type:` | TS emitted |
311
+ |---|---|
312
+ | `string` | `string` |
313
+ | `number` / `integer` | `number` |
314
+ | `boolean` | `boolean` |
315
+ | `object` | `Record<string, unknown>` |
316
+ | `array` | `unknown[]` |
317
+ | missing | `unknown` |
318
+
319
+ If you need a string-literal union, declare via `enum: [a, b, c]` and the codegen emits `'a' | 'b' | 'c'`.
320
+
321
+ #### Worked example: `<modal-ui>` close
322
+
323
+ Yaml at `packages/web-components/components/modal/modal.yaml`:
324
+
325
+ ```yaml
326
+ events:
327
+ close:
328
+ description: Fires when the modal is dismissed (backdrop click, Escape, programmatic hide()).
329
+ ```
330
+
331
+ Sidecar after `npm run build:components`:
332
+
333
+ ```json
334
+ { "x-adiaui": { "events": { "close": { "description": "Fires when the modal is dismissed..." } } } }
335
+ ```
336
+
337
+ Emitted `.d.ts` after `npm run codegen:dts`:
338
+
339
+ ```ts
340
+ export type ModalCloseEvent = CustomEvent<unknown>;
341
+ export class UIModal extends UIElement {
342
+ addEventListener(type: 'close', listener: (ev: ModalCloseEvent) => unknown, options?: ...): void;
343
+ }
344
+ ```
345
+
346
+ Reference: [`docs/conventions/component-events.md`](../../docs/conventions/component-events.md) for the 1-page convention summary; [`scripts/build/dts-codegen.mjs`](../../scripts/build/dts-codegen.mjs) for the codegen logic that reads `x-adiaui.events`; [`scripts/release/check-form-bearing-dts.mjs`](../../scripts/release/check-form-bearing-dts.mjs) for the drift audit.
347
+
229
348
  ---
230
349
 
231
350
  ## Form participation
@@ -592,6 +711,140 @@ Properties are installed by `installProps()` in `super()`, but the host's render
592
711
 
593
712
  ---
594
713
 
714
+ ## Worked examples
715
+
716
+ Migrations from common custom-helper shapes to AdiaUI primitives. Each example pairs a real consumer's pre-AdiaUI helper with the equivalent AdiaUI declaration.
717
+
718
+ ### Replace a hand-rolled color-token swatch with `<swatch-ui>` (v0.4.9+)
719
+
720
+ Pre-v0.4.9, design-token tools commonly hand-rolled a `renderSwatch()` helper composing: background color, key label, gamut badge (sRGB / P3 markers), APCA contrast badge, copy-to-clipboard button, light/dark auto-contrast label, keyboard select. v0.4.9 extends `<swatch-ui>` with all six surfaces — the helper collapses to one declarative tag.
721
+
722
+ ```diff
723
+ - // Pre — 70 LOC helper
724
+ - function renderSwatch({ color, label, detail, gamutBadge, copyValue, selected, onSelect }) {
725
+ - const el = document.createElement('div')
726
+ - el.className = 'token-swatch'
727
+ - el.style.backgroundColor = color
728
+ - el.tabIndex = 0
729
+ - el.setAttribute('role', 'button')
730
+ - el.setAttribute('aria-pressed', String(selected))
731
+ - if (selected) el.classList.add('selected')
732
+ -
733
+ - const labelEl = document.createElement('span')
734
+ - labelEl.className = 'swatch-label'
735
+ - labelEl.textContent = label
736
+ - labelEl.style.color = isLight(color) ? 'var(--fg-dark)' : 'var(--fg-light)'
737
+ - el.appendChild(labelEl)
738
+ -
739
+ - const detailEl = document.createElement('span')
740
+ - detailEl.className = 'swatch-detail'
741
+ - detailEl.textContent = detail
742
+ - el.appendChild(detailEl)
743
+ -
744
+ - if (gamutBadge) {
745
+ - const badge = document.createElement('span')
746
+ - badge.className = 'swatch-badge'
747
+ - badge.textContent = gamutBadge === 'out-of-gamut' ? '△' : '✦'
748
+ - badge.setAttribute('aria-label', gamutBadge)
749
+ - el.appendChild(badge)
750
+ - }
751
+ -
752
+ - const copyBtn = document.createElement('button')
753
+ - copyBtn.className = 'swatch-copy'
754
+ - copyBtn.textContent = '⧉'
755
+ - copyBtn.setAttribute('aria-label', `Copy ${copyValue}`)
756
+ - copyBtn.addEventListener('click', async (e) => {
757
+ - e.stopPropagation()
758
+ - try { await navigator.clipboard.writeText(copyValue); copyBtn.textContent = '✓' }
759
+ - catch { copyBtn.textContent = '⚠' }
760
+ - setTimeout(() => { copyBtn.textContent = '⧉' }, 1200)
761
+ - })
762
+ - el.appendChild(copyBtn)
763
+ -
764
+ - el.addEventListener('click', (e) => {
765
+ - if (e.target === copyBtn) return
766
+ - onSelect({ color, label })
767
+ - })
768
+ - el.addEventListener('keydown', (e) => {
769
+ - if (e.key !== 'Enter' && e.key !== ' ') return
770
+ - if (e.target === copyBtn) return
771
+ - e.preventDefault()
772
+ - onSelect({ color, label })
773
+ - })
774
+ -
775
+ - return el
776
+ - }
777
+
778
+ + // Post — one declarative tag
779
+ + html`<swatch-ui
780
+ + shape="block" size="lg"
781
+ + color=${color}
782
+ + label=${label}
783
+ + detail=${detail}
784
+ + badge=${gamutBadge}
785
+ + copyable
786
+ + copy-value=${copyValue}
787
+ + selectable
788
+ + ?selected=${selected}
789
+ + auto-contrast
790
+ + @select=${(e) => onSelect({ color: e.detail.color, label: e.detail.label })}
791
+ + ></swatch-ui>`
792
+ ```
793
+
794
+ The primitive owns: badge symbol + ARIA mapping, copy-clipboard state machine (⧉ → ✓ / ⚠ → ⧉ after 1.2s), `role="button"` + `tabindex="0"` only when `[selectable]`, focus ring on `:focus-visible` + always-on when `[selected]`, click-on-copy-doesn't-toggle-selection guard, label auto-contrast via OKLCH luminance (when `[auto-contrast]` is set).
795
+
796
+ The `select` event detail is `{ value, color, label }` — `value` is `[color]` (or `[label]` as fallback) so consumers binding to a value-shaped select handler get the canonical accessor uniformly across form-bearing primitives.
797
+
798
+ For multiple simultaneous badges (e.g. both `out-of-gamut` and `apca-fail`), pass a comma-separated list: `badge="out-of-gamut, apca-fail"` — v0.4.9 renders both stacked in the upper-right.
799
+
800
+ ### Guard global keyboard shortcuts with `isFocusInInput()` (v0.4.8+)
801
+
802
+ ```diff
803
+ - // Pre — manual cast boilerplate at every shortcut site
804
+ - document.addEventListener('keydown', (e) => {
805
+ - const active = document.activeElement as HTMLElement | null
806
+ - if (active && (
807
+ - active.tagName === 'INPUT' ||
808
+ - active.tagName === 'TEXTAREA' ||
809
+ - active.isContentEditable
810
+ - )) return
811
+ - if (e.key === '/') openCommandPalette()
812
+ - })
813
+
814
+ + // Post — one import + one call
815
+ + import { isFocusInInput } from '@adia-ai/web-modules/editor'
816
+ +
817
+ + document.addEventListener('keydown', (e) => {
818
+ + if (isFocusInInput()) return
819
+ + if (e.key === '/') openCommandPalette()
820
+ + })
821
+ ```
822
+
823
+ `isFocusInInput()` also recognizes AdiaUI text-input primitives (`<input-ui>`, `<textarea-ui>`, `<otp-input-ui>`, `<search-ui>`) by host tag-name — important because focus may land on the custom-element host before the inner native input mounts.
824
+
825
+ ### Use derived reactive bindings for unit-conversion (v0.4.6+)
826
+
827
+ ```diff
828
+ - // Pre — eager evaluation; slider doesn't move on undo
829
+ - <slider-ui .value=${minL.value * 100} min="0" max="100" step="1"></slider-ui>
830
+
831
+ + // Post — function-as-value gets effect-wrapped
832
+ + <slider-ui .value=${() => minL.value * 100} min="0" max="100" step="1"></slider-ui>
833
+ ```
834
+
835
+ The function re-runs on every dependency mutation. Same pattern works for derived computations (e.g. `() => clamp(minL.value * 100, 0, 100)`) and signal-array transforms.
836
+
837
+ ### Persist `<editor-sidebar>` width across reloads (v0.4.9+)
838
+
839
+ ```diff
840
+ - <editor-sidebar collapsible name="dts-layout">
841
+ + <editor-sidebar collapsible persist="dts-layout">
842
+ ```
843
+
844
+ `[persist]` is the preferred name in v0.4.9+. `[name]` keeps working (back-compat alias; deprecated, not scheduled for v0.5.x removal). Storage namespace + `sidebar-toggle` event detail are unchanged.
845
+
846
+ ---
847
+
595
848
  ## More
596
849
 
597
850
  - [`README.md`](./README.md) — package overview + authoring guide
@@ -29,6 +29,9 @@
29
29
  "x-adiaui": {
30
30
  "anti_patterns": [],
31
31
  "category": "container",
32
+ "composes": [
33
+ "icon-ui"
34
+ ],
32
35
  "events": {
33
36
  "change": {
34
37
  "description": "Fired when the set of open panels changes"
@@ -5,13 +5,23 @@
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
7
  * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
8
- * run `npm run components`, then `npm run codegen:dts` to regenerate;
9
- * or hand-author this file fully if rich event types are needed.
8
+ * run `npm run build:components`, then `npm run codegen:dts` to
9
+ * regenerate; or hand-author this file fully if rich event types are
10
+ * needed beyond what the yaml `events:` block can express.
10
11
  */
11
12
 
12
13
  import { UIElement } from '../../core/element.js';
13
14
 
15
+ export type AccordionChangeEvent = CustomEvent<unknown>;
16
+
14
17
  export class UIAccordion extends UIElement {
15
18
  /** Allow multiple panels to be open simultaneously */
16
19
  multiple: boolean;
20
+
21
+ addEventListener<K extends keyof HTMLElementEventMap>(
22
+ type: K,
23
+ listener: (this: UIAccordion, ev: HTMLElementEventMap[K]) => unknown,
24
+ options?: boolean | AddEventListenerOptions,
25
+ ): void;
26
+ addEventListener(type: 'change', listener: (ev: AccordionChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
17
27
  }
@@ -7,6 +7,10 @@ component: Accordion
7
7
  category: container
8
8
  version: 1
9
9
  description: Accordion container managing single/multiple open states.
10
+ # Per ADR-0027 — primitives that programmatically create other primitives
11
+ # do NOT auto-import them. Consumer (or demo shell) must explicitly import.
12
+ composes:
13
+ - icon-ui
10
14
  props:
11
15
  multiple:
12
16
  description: Allow multiple panels to be open simultaneously
@@ -24,9 +24,26 @@
24
24
  "x-adiaui": {
25
25
  "anti_patterns": [],
26
26
  "category": "navigation",
27
+ "composes": [
28
+ "icon-ui"
29
+ ],
27
30
  "events": {
28
31
  "action": {
29
- "description": "Fired on action."
32
+ "description": "Fired on action.",
33
+ "detail": {
34
+ "item": {
35
+ "description": "The triggering list-item element.",
36
+ "type": "object"
37
+ },
38
+ "text": {
39
+ "description": "Item text content or [text] attribute.",
40
+ "type": "string"
41
+ },
42
+ "value": {
43
+ "description": "Item value attribute (preferred over text for stable IDs).",
44
+ "type": "string"
45
+ }
46
+ }
30
47
  }
31
48
  },
32
49
  "examples": [
@@ -5,11 +5,30 @@
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
7
  * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
8
- * run `npm run components`, then `npm run codegen:dts` to regenerate;
9
- * or hand-author this file fully if rich event types are needed.
8
+ * run `npm run build:components`, then `npm run codegen:dts` to
9
+ * regenerate; or hand-author this file fully if rich event types are
10
+ * needed beyond what the yaml `events:` block can express.
10
11
  */
11
12
 
12
13
  import { UIElement } from '../../core/element.js';
13
14
 
15
+ export interface ActionListActionEventDetail {
16
+ /** The triggering list-item element. */
17
+ item: Record<string, unknown>;
18
+ /** Item text content or [text] attribute. */
19
+ text: string;
20
+ /** Item value attribute (preferred over text for stable IDs). */
21
+ value: string;
22
+ }
23
+
24
+ export type ActionListActionEvent = CustomEvent<ActionListActionEventDetail>;
25
+
14
26
  export class UIActionList extends UIElement {
27
+
28
+ addEventListener<K extends keyof HTMLElementEventMap>(
29
+ type: K,
30
+ listener: (this: UIActionList, ev: HTMLElementEventMap[K]) => unknown,
31
+ options?: boolean | AddEventListenerOptions,
32
+ ): void;
33
+ addEventListener(type: 'action', listener: (ev: ActionListActionEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
15
34
  }
@@ -8,10 +8,24 @@ category: navigation
8
8
  version: 1
9
9
  description: Inline list of command actions with keyboard navigation. Fires action event when an
10
10
  item is clicked or activated via keyboard.
11
+ # Per ADR-0027 — primitives that programmatically create other primitives
12
+ # do NOT auto-import them. Consumer (or demo shell) must explicitly import.
13
+ composes:
14
+ - icon-ui
11
15
  props: {}
12
16
  events:
13
17
  action:
14
18
  description: "Fired on action."
19
+ detail:
20
+ value:
21
+ type: string
22
+ description: Item value attribute (preferred over text for stable IDs).
23
+ text:
24
+ type: string
25
+ description: Item text content or [text] attribute.
26
+ item:
27
+ type: object
28
+ description: The triggering list-item element.
15
29
  slots:
16
30
  default (action-item-ui children):
17
31
  description: "Child content region for the `default (action-item-ui children)` slot."
@@ -49,9 +49,19 @@
49
49
  "x-adiaui": {
50
50
  "anti_patterns": [],
51
51
  "category": "agent",
52
+ "composes": [
53
+ "icon-ui",
54
+ "badge-ui"
55
+ ],
52
56
  "events": {
53
57
  "artifact-toggle": {
54
- "description": "Fired on artifact-toggle."
58
+ "description": "Fired on artifact-toggle.",
59
+ "detail": {
60
+ "collapsed": {
61
+ "description": "New collapsed state after the toggle.",
62
+ "type": "boolean"
63
+ }
64
+ }
55
65
  }
56
66
  },
57
67
  "examples": [
@@ -5,12 +5,20 @@
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
7
  * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
8
- * run `npm run components`, then `npm run codegen:dts` to regenerate;
9
- * or hand-author this file fully if rich event types are needed.
8
+ * run `npm run build:components`, then `npm run codegen:dts` to
9
+ * regenerate; or hand-author this file fully if rich event types are
10
+ * needed beyond what the yaml `events:` block can express.
10
11
  */
11
12
 
12
13
  import { UIElement } from '../../core/element.js';
13
14
 
15
+ export interface AgentArtifactArtifactToggleEventDetail {
16
+ /** New collapsed state after the toggle. */
17
+ collapsed: boolean;
18
+ }
19
+
20
+ export type AgentArtifactArtifactToggleEvent = CustomEvent<AgentArtifactArtifactToggleEventDetail>;
21
+
14
22
  export class UIAgentArtifact extends UIElement {
15
23
  /** Header title. */
16
24
  title: string;
@@ -22,4 +30,11 @@ export class UIAgentArtifact extends UIElement {
22
30
  kind: string;
23
31
  /** neutral | accent | warning | danger */
24
32
  tone: string;
33
+
34
+ addEventListener<K extends keyof HTMLElementEventMap>(
35
+ type: K,
36
+ listener: (this: UIAgentArtifact, ev: HTMLElementEventMap[K]) => unknown,
37
+ options?: boolean | AddEventListenerOptions,
38
+ ): void;
39
+ addEventListener(type: 'artifact-toggle', listener: (ev: AgentArtifactArtifactToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
25
40
  }
@@ -7,6 +7,11 @@ component: AgentArtifact
7
7
  category: agent
8
8
  version: 1
9
9
  description: Inline container for structured agent artifacts (A2UI, JSON, tickets).
10
+ # Per ADR-0027 — primitives that programmatically create other primitives
11
+ # do NOT auto-import them. Consumer (or demo shell) must explicitly import.
12
+ composes:
13
+ - icon-ui
14
+ - badge-ui
10
15
  props:
11
16
  kind:
12
17
  description: Badge label; value is normalized to uppercase before rendering.
@@ -31,6 +36,10 @@ props:
31
36
  events:
32
37
  artifact-toggle:
33
38
  description: "Fired on artifact-toggle."
39
+ detail:
40
+ collapsed:
41
+ type: boolean
42
+ description: New collapsed state after the toggle.
34
43
  slots:
35
44
  default:
36
45
  description: "Default slot — primary child content."
@@ -39,9 +39,18 @@
39
39
  "x-adiaui": {
40
40
  "anti_patterns": [],
41
41
  "category": "agent",
42
+ "composes": [
43
+ "button-ui"
44
+ ],
42
45
  "events": {
43
46
  "feedback-rate": {
44
- "description": "Fired on feedback-rate."
47
+ "description": "Fired on feedback-rate.",
48
+ "detail": {
49
+ "rating": {
50
+ "description": "Rating value selected by the user.",
51
+ "type": "number"
52
+ }
53
+ }
45
54
  },
46
55
  "feedback-save": {
47
56
  "description": "Fired on feedback-save."
@@ -5,12 +5,21 @@
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
7
  * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
8
- * run `npm run components`, then `npm run codegen:dts` to regenerate;
9
- * or hand-author this file fully if rich event types are needed.
8
+ * run `npm run build:components`, then `npm run codegen:dts` to
9
+ * regenerate; or hand-author this file fully if rich event types are
10
+ * needed beyond what the yaml `events:` block can express.
10
11
  */
11
12
 
12
13
  import { UIElement } from '../../core/element.js';
13
14
 
15
+ export interface AgentFeedbackBarFeedbackRateEventDetail {
16
+ /** Rating value selected by the user. */
17
+ rating: number;
18
+ }
19
+
20
+ export type AgentFeedbackBarFeedbackRateEvent = CustomEvent<AgentFeedbackBarFeedbackRateEventDetail>;
21
+ export type AgentFeedbackBarFeedbackSaveEvent = CustomEvent<unknown>;
22
+
14
23
  export class UIAgentFeedbackBar extends UIElement {
15
24
  /** Disable all actions. */
16
25
  disabled: boolean;
@@ -18,4 +27,12 @@ export class UIAgentFeedbackBar extends UIElement {
18
27
  saveIcon: string;
19
28
  /** Save button text; empty hides it. */
20
29
  saveLabel: string;
30
+
31
+ addEventListener<K extends keyof HTMLElementEventMap>(
32
+ type: K,
33
+ listener: (this: UIAgentFeedbackBar, ev: HTMLElementEventMap[K]) => unknown,
34
+ options?: boolean | AddEventListenerOptions,
35
+ ): void;
36
+ addEventListener(type: 'feedback-rate', listener: (ev: AgentFeedbackBarFeedbackRateEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
37
+ addEventListener(type: 'feedback-save', listener: (ev: AgentFeedbackBarFeedbackSaveEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
21
38
  }
@@ -7,6 +7,10 @@ component: AgentFeedbackBar
7
7
  category: agent
8
8
  version: 1
9
9
  description: Thumbs + save row under an agent response.
10
+ # Per ADR-0027 — primitives that programmatically create other primitives
11
+ # do NOT auto-import them. Consumer (or demo shell) must explicitly import.
12
+ composes:
13
+ - button-ui
10
14
  props:
11
15
  disabled:
12
16
  description: Disable all actions.
@@ -26,6 +30,10 @@ props:
26
30
  events:
27
31
  feedback-rate:
28
32
  description: "Fired on feedback-rate."
33
+ detail:
34
+ rating:
35
+ type: number
36
+ description: Rating value selected by the user.
29
37
  feedback-save:
30
38
  description: "Fired on feedback-save."
31
39
  slots:
@@ -44,9 +44,22 @@
44
44
  "x-adiaui": {
45
45
  "anti_patterns": [],
46
46
  "category": "agent",
47
+ "composes": [
48
+ "button-ui"
49
+ ],
47
50
  "events": {
48
51
  "questions-answer": {
49
- "description": "Fired on questions-answer."
52
+ "description": "Fired on questions-answer.",
53
+ "detail": {
54
+ "option": {
55
+ "description": "The full selected option object.",
56
+ "type": "object"
57
+ },
58
+ "selected": {
59
+ "description": "Array of selected option IDs.",
60
+ "type": "array"
61
+ }
62
+ }
50
63
  }
51
64
  },
52
65
  "examples": [