@adia-ai/web-components 0.8.42 → 0.8.44

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 (186) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/MIGRATION.md +130 -80
  3. package/USAGE.md +1 -1
  4. package/color/index.js +1 -1
  5. package/components/action-list/action-item.a2ui.json +3 -8
  6. package/components/action-list/action-item.yaml +10 -16
  7. package/components/action-list/action-list.class.js +5 -61
  8. package/components/action-list/action-list.d.ts +1 -3
  9. package/components/avatar/avatar.class.js +12 -1
  10. package/components/button/button.d.ts +4 -0
  11. package/components/button/button.yaml +1 -1
  12. package/components/calendar-picker/calendar-picker.a2ui.json +1 -1
  13. package/components/calendar-picker/calendar-picker.class.js +2 -3
  14. package/components/calendar-picker/calendar-picker.css +16 -18
  15. package/components/calendar-picker/calendar-picker.yaml +2 -2
  16. package/components/card/card.css +3 -3
  17. package/components/chart/chart.a2ui.json +29 -18
  18. package/components/chart/chart.class.js +1308 -266
  19. package/components/chart/chart.css +151 -1
  20. package/components/chart/chart.d.ts +9 -9
  21. package/components/chart/chart.yaml +63 -31
  22. package/components/chart-legend/chart-legend.a2ui.json +20 -14
  23. package/components/chart-legend/chart-legend.class.js +79 -58
  24. package/components/chart-legend/chart-legend.css +65 -1
  25. package/components/chart-legend/chart-legend.d.ts +5 -5
  26. package/components/chart-legend/chart-legend.examples.md +4 -4
  27. package/components/chart-legend/chart-legend.yaml +50 -20
  28. package/components/check/check.css +9 -0
  29. package/components/color-area/color-area.a2ui.json +1 -1
  30. package/components/color-area/color-area.class.js +2 -2
  31. package/components/color-area/color-area.css +4 -7
  32. package/components/color-area/color-area.d.ts +1 -1
  33. package/components/color-area/color-area.yaml +3 -3
  34. package/components/color-input/color-input.a2ui.json +1 -1
  35. package/components/color-input/color-input.class.js +1 -1
  36. package/components/color-input/color-input.yaml +2 -2
  37. package/components/combobox/combobox.a2ui.json +3 -0
  38. package/components/combobox/combobox.class.js +9 -8
  39. package/components/combobox/combobox.css +25 -4
  40. package/components/combobox/combobox.yaml +6 -0
  41. package/components/command/command.css +17 -1
  42. package/components/context-menu/context-menu.d.ts +2 -0
  43. package/components/drilldown/drilldown.a2ui.json +3 -0
  44. package/components/drilldown/drilldown.css +8 -1
  45. package/components/drilldown/drilldown.yaml +2 -0
  46. package/components/field/field.class.js +5 -5
  47. package/components/heatmap/heatmap.a2ui.json +5 -0
  48. package/components/heatmap/heatmap.d.ts +2 -0
  49. package/components/heatmap/heatmap.yaml +8 -0
  50. package/components/index.js +0 -4
  51. package/components/input/input.a2ui.json +4 -4
  52. package/components/input/input.class.js +20 -7
  53. package/components/input/input.css +6 -0
  54. package/components/input/input.yaml +15 -11
  55. package/components/integration-card/integration-card.d.ts +2 -0
  56. package/components/menu/menu-item.a2ui.json +3 -8
  57. package/components/menu/menu-item.yaml +13 -21
  58. package/components/menu/menu.class.js +5 -27
  59. package/components/menu/menu.d.ts +1 -3
  60. package/components/noodles/noodles.a2ui.json +0 -15
  61. package/components/noodles/noodles.yaml +0 -12
  62. package/components/number-format/number-format.class.js +10 -0
  63. package/components/pagination/pagination.class.js +88 -4
  64. package/components/pane/pane.a2ui.json +2 -12
  65. package/components/pane/pane.class.js +5 -30
  66. package/components/pane/pane.d.ts +3 -5
  67. package/components/pane/pane.yaml +7 -19
  68. package/components/popover/popover.d.ts +2 -0
  69. package/components/progress/progress.a2ui.json +1 -1
  70. package/components/progress/progress.class.js +13 -8
  71. package/components/progress/progress.d.ts +1 -1
  72. package/components/progress/progress.yaml +2 -2
  73. package/components/radio/radio.css +7 -0
  74. package/components/range/range.class.js +9 -1
  75. package/components/search/search.css +10 -1
  76. package/components/segment/segment.a2ui.json +1 -2
  77. package/components/segment/segment.css +10 -0
  78. package/components/segment/segment.yaml +1 -2
  79. package/components/segmented/segmented.a2ui.json +1 -3
  80. package/components/segmented/segmented.class.js +2 -2
  81. package/components/segmented/segmented.yaml +2 -4
  82. package/components/select/select.a2ui.json +5 -1
  83. package/components/select/select.class.js +14 -14
  84. package/components/select/select.css +20 -1
  85. package/components/select/select.yaml +7 -1
  86. package/components/step-progress/step-progress.yaml +1 -1
  87. package/components/stream/stream.d.ts +2 -0
  88. package/components/switch/switch.css +8 -0
  89. package/components/table/table.a2ui.json +6 -1
  90. package/components/table/table.class.js +18 -1
  91. package/components/table/table.css +31 -6
  92. package/components/table/table.d.ts +3 -1
  93. package/components/table/table.examples.md +2 -2
  94. package/components/table/table.yaml +9 -1
  95. package/components/table-toolbar/table-toolbar.a2ui.json +67 -2
  96. package/components/table-toolbar/table-toolbar.class.js +492 -17
  97. package/components/table-toolbar/table-toolbar.css +311 -0
  98. package/components/table-toolbar/table-toolbar.d.ts +17 -1
  99. package/components/table-toolbar/table-toolbar.yaml +220 -15
  100. package/components/tag/tag.class.js +13 -1
  101. package/components/text/text.a2ui.json +3 -3
  102. package/components/text/text.d.ts +5 -3
  103. package/components/text/text.yaml +3 -3
  104. package/components/textarea/textarea.class.js +9 -1
  105. package/components/textarea/textarea.css +4 -0
  106. package/components/time-picker/time-picker.yaml +3 -2
  107. package/components/tour/tour.d.ts +10 -0
  108. package/components/tree/tree.yaml +1 -1
  109. package/components/upload/upload.class.js +16 -0
  110. package/components/upload/upload.d.ts +7 -0
  111. package/components/upload/upload.yaml +7 -3
  112. package/core/a11y.d.ts +22 -0
  113. package/core/a11y.js +64 -0
  114. package/core/anchor.js +9 -5
  115. package/core/element.js +1 -1
  116. package/core/icons.js +2 -5
  117. package/custom-elements.json +142 -594
  118. package/dist/host.min.css +1 -1
  119. package/dist/host.sheet.js +1 -1
  120. package/dist/theme-provider.min.js +1 -1
  121. package/dist/web-components.min.css +1 -1
  122. package/dist/web-components.min.js +122 -119
  123. package/dist/web-components.sheet.js +1 -1
  124. package/index.d.ts +0 -15
  125. package/package.json +1 -1
  126. package/patterns/access-requests/access-requests.examples.html +1 -1
  127. package/patterns/admin-keys-and-export/admin-keys-and-export.examples.html +7 -7
  128. package/patterns/agent-cost/agent-cost.examples.html +6 -6
  129. package/patterns/agent-memory/agent-memory.examples.html +3 -3
  130. package/patterns/agent-prompt-library/agent-prompt-library.examples.html +8 -8
  131. package/patterns/agent-tool-call/agent-tool-call.examples.html +1 -1
  132. package/patterns/approvals/approvals.examples.html +6 -6
  133. package/patterns/audit-log/audit-log.examples.html +1 -1
  134. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.html +4 -4
  135. package/patterns/chart-in-card/chart-in-card.examples.html +124 -10
  136. package/patterns/chart-in-card/chart-in-card.examples.js +37 -0
  137. package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +5 -5
  138. package/patterns/conversion-funnel/conversion-funnel.html +2 -2
  139. package/patterns/data-tables-inline-edit-and-tree/data-tables-inline-edit-and-tree.examples.html +2 -2
  140. package/patterns/diff-review/diff-review.examples.html +6 -6
  141. package/patterns/editor-shell/editor-shell.examples.html +3 -3
  142. package/patterns/export-flow/export-flow.examples.html +2 -2
  143. package/patterns/filter-bar/filter-bar.examples.html +2 -2
  144. package/patterns/forms-conditional-and-autocomplete/forms-conditional-and-autocomplete.examples.html +2 -2
  145. package/patterns/inline-dialog/inline-dialog.examples.html +2 -2
  146. package/patterns/marketing-engagement/marketing-engagement.examples.html +4 -4
  147. package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +1 -1
  148. package/patterns/permissions-matrix/permissions-matrix.examples.html +1 -1
  149. package/patterns/permissions-sharing/permissions-sharing.examples.html +2 -2
  150. package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +4 -4
  151. package/patterns/search-discovery/search-discovery.examples.html +2 -2
  152. package/styles/colors/semantics/features.css +8 -1
  153. package/styles/components.css +0 -4
  154. package/styles/type/roles.css +29 -9
  155. package/traits/arrow-grid-nav/arrow-grid-nav.js +1 -1
  156. package/traits/resize-observer/resize-observer.js +1 -1
  157. package/components/color-picker/color-picker.a2ui.json +0 -148
  158. package/components/color-picker/color-picker.class.js +0 -46
  159. package/components/color-picker/color-picker.css +0 -14
  160. package/components/color-picker/color-picker.d.ts +0 -99
  161. package/components/color-picker/color-picker.examples.md +0 -19
  162. package/components/color-picker/color-picker.js +0 -17
  163. package/components/color-picker/color-picker.yaml +0 -175
  164. package/components/datetime-picker/datetime-picker.a2ui.json +0 -256
  165. package/components/datetime-picker/datetime-picker.class.js +0 -58
  166. package/components/datetime-picker/datetime-picker.css +0 -11
  167. package/components/datetime-picker/datetime-picker.d.ts +0 -87
  168. package/components/datetime-picker/datetime-picker.examples.md +0 -25
  169. package/components/datetime-picker/datetime-picker.js +0 -17
  170. package/components/datetime-picker/datetime-picker.yaml +0 -254
  171. package/components/progress-row/progress-row.a2ui.json +0 -107
  172. package/components/progress-row/progress-row.class.js +0 -118
  173. package/components/progress-row/progress-row.css +0 -60
  174. package/components/progress-row/progress-row.d.ts +0 -24
  175. package/components/progress-row/progress-row.examples.md +0 -7
  176. package/components/progress-row/progress-row.js +0 -17
  177. package/components/progress-row/progress-row.yaml +0 -85
  178. package/components/toggle-group/toggle-group.a2ui.json +0 -148
  179. package/components/toggle-group/toggle-group.class.js +0 -195
  180. package/components/toggle-group/toggle-group.css +0 -127
  181. package/components/toggle-group/toggle-group.d.ts +0 -50
  182. package/components/toggle-group/toggle-group.examples.md +0 -33
  183. package/components/toggle-group/toggle-group.js +0 -18
  184. package/components/toggle-group/toggle-group.yaml +0 -135
  185. package/components/toggle-group/toggle-option.a2ui.json +0 -86
  186. package/components/toggle-group/toggle-option.yaml +0 -75
@@ -25,6 +25,14 @@ props:
25
25
  enum:
26
26
  - square
27
27
  - wide
28
+ data:
29
+ description: >-
30
+ Cell data as a JSON array of `{ r, c, v, label? }` objects, or set the
31
+ `.data` property directly with an already-parsed array. Parsed on
32
+ connect if the attribute is present and `.data` hasn't already been
33
+ set programmatically.
34
+ type: string
35
+ default: ""
28
36
  colorScheme:
29
37
  description: Color ramp
30
38
  type: string
@@ -51,7 +51,6 @@ export { UIBadge } from './badge/badge.js';
51
51
  export { UIBreadcrumb } from './breadcrumb/breadcrumb.js';
52
52
  export { UICommand } from './command/command.js';
53
53
  export { UIColorArea } from './color-area/color-area.js';
54
- export { UIColorPicker } from './color-picker/color-picker.js';
55
54
  export { UIColorInput } from './color-input/color-input.js';
56
55
  export { UINoodles } from './noodles/noodles.js';
57
56
  export { UITable } from './table/table.js';
@@ -115,18 +114,15 @@ export { UISearch } from './search/search.js';
115
114
  export { UICombobox } from './combobox/combobox.js';
116
115
  export { UITagsInput } from './tags-input/tags-input.js';
117
116
  export { UIStat } from './stat/stat.js';
118
- export { UIProgressRow } from './progress-row/progress-row.js';
119
117
  export { UIActionList, UIActionItem } from './action-list/action-list.js';
120
118
  export { UIEmptyState } from './empty-state/empty-state.js';
121
119
  export { UICalendarGrid } from './calendar-grid/calendar-grid.js';
122
120
  export { UICalendarPicker } from './calendar-picker/calendar-picker.js';
123
121
  export { UIDateRangePicker } from './date-range-picker/date-range-picker.js';
124
122
  export { UITimePicker } from './time-picker/time-picker.js';
125
- export { UIDatetimePicker } from './datetime-picker/datetime-picker.js';
126
123
  export { UIEmbed } from './embed/embed.js';
127
124
  export { UIBlock } from './block/block.js';
128
125
  export { UIText } from './text/text.js';
129
- export { UIToggleGroup, UIToggleOption } from './toggle-group/toggle-group.js';
130
126
  export { UIToggleScheme } from './toggle-scheme/toggle-scheme.js';
131
127
  export { UIDemoToggle } from './demo-toggle/demo-toggle.js';
132
128
  export { UIRichText } from './richtext/richtext.js';
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "properties": {
16
16
  "type": {
17
- "description": "Input type. `password` wraps a native `<input>` for disc masking; `number` renders a contenteditable + stepper buttons (no native input). `text`/`email`/`tel`/`url` use plain contenteditable. input-ui does NOT implement date/time/color/search affordances — those HTML5 type values render as a plain contenteditable with no calendar, clock, swatch, or search affordance (first-pass-VALID against this enum historically, but functionally broken generation output). Use the owning primitive instead — CalendarPicker for date (+ month/week, no dedicated granularity primitive exists), DatetimePicker for datetime-local, TimePicker for time, ColorInput/ColorPicker for color, Search for search. See anti_patterns.",
17
+ "description": "Input type. `password` wraps a native `<input>` for disc masking; `number` renders a contenteditable + stepper buttons (no native input). `text`/`email`/`tel`/`url` use plain contenteditable. input-ui does NOT implement date/time/color/search affordances — those HTML5 type values render as a plain contenteditable with no calendar, clock, swatch, or search affordance (first-pass-VALID against this enum historically, but functionally broken generation output). Use the owning primitive instead — CalendarPicker for date (+ month/week, no dedicated granularity primitive exists), CalendarPicker precision=\"minute\" for datetime-local, TimePicker for time, ColorInput/ColorArea for color, Search for search. See anti_patterns.",
18
18
  "type": "string",
19
19
  "enum": [
20
20
  "text",
@@ -165,8 +165,8 @@
165
165
  "wrong": "{\"component\": \"Input\", \"type\": \"date\"}\n"
166
166
  },
167
167
  {
168
- "fix": "{\"component\": \"DatetimePicker\"}\n",
169
- "why": "Same gap as \"date\" — no native datetime picker rendered. Use\nDatetimePicker for combined date + time input.\n",
168
+ "fix": "{\"component\": \"CalendarPicker\", \"precision\": \"minute\"}\n",
169
+ "why": "Same gap as \"date\" — no native datetime picker rendered. Use\nCalendarPicker with precision=\"minute\" for combined date + time\ninput (DatetimePicker was a deprecated thin alias, cut in 0.8.43,\ngh#1617 — CalendarPicker is the sole A2UI component now).\n",
170
170
  "wrong": "{\"component\": \"Input\", \"type\": \"datetime-local\"}\n"
171
171
  },
172
172
  {
@@ -186,7 +186,7 @@
186
186
  },
187
187
  {
188
188
  "fix": "{\"component\": \"ColorInput\"}\n",
189
- "why": "No native color swatch/picker rendered — just a plain text\nsurface. Use ColorInput for a compact form-bearing swatch +\npopover, or ColorPicker directly for the full OKLCH picker\nsurface.\n",
189
+ "why": "No native color swatch/picker rendered — just a plain text\nsurface. Use ColorInput for a compact form-bearing swatch +\npopover, or ColorArea directly for the full OKLCH picker surface\n(ColorPicker was a deprecated thin alias, cut in 0.8.43, gh#1617 —\nColorArea is the sole A2UI component now).\n",
190
190
  "wrong": "{\"component\": \"Input\", \"type\": \"color\"}\n"
191
191
  },
192
192
  {
@@ -53,6 +53,7 @@
53
53
  import { UIFormElement } from '../../core/form.js';
54
54
  import { isIconName, whenIconRegistryReady } from '../../core/icons.js';
55
55
  import { logicalSlotted } from '../../core/logical-children.js';
56
+ import { syncAutoAriaLabel } from '../../core/a11y.js';
56
57
 
57
58
  const renderAffix = (v) => isIconName(v)
58
59
  ? `<icon-ui name="${v}"></icon-ui>`
@@ -106,6 +107,12 @@ export class UIInput extends UIFormElement {
106
107
  #cachedGroup = '';
107
108
  #cachedSepFor = null;
108
109
  static #labelSeq = 0;
110
+ // gh#1647: tracks the aria-label VALUE this element itself last derived
111
+ // from `placeholder` (undefined when none), so re-renders refresh/clear
112
+ // OUR name without ever clobbering a consumer's own aria-label —
113
+ // value-tracking guard ported from tag.class.js/select.class.js
114
+ // (gh#1644/#1646).
115
+ #lastAutoAriaLabel = undefined;
109
116
 
110
117
  // Hold-to-repeat tuning. Initial delay before autorepeat begins, and the
111
118
  // interval between repeats. Values match the cadence of the native
@@ -272,11 +279,19 @@ export class UIInput extends UIFormElement {
272
279
  `;
273
280
  }
274
281
 
282
+ // Suppress browser-native suggestion/autofill affordances on the
283
+ // contenteditable surface (gh#1593) — a bespoke editable, not a real
284
+ // form <input>, so a browser's saved-values dropdown or spellcheck
285
+ // suggestion strip is always noise here, never a wanted feature.
286
+ // aria-autocomplete="none" documents the same intent for AT users.
275
287
  const editable = `
276
288
  <span slot="text" contenteditable="plaintext-only" tabindex="0"
277
289
  ${this.value ? '' : 'data-empty=""'}
278
290
  ${labelby}
279
291
  data-placeholder="${this.placeholder}"
292
+ autocomplete="off" aria-autocomplete="none"
293
+ autocorrect="off" autocapitalize="off" spellcheck="false"
294
+ data-lpignore="true" data-1p-ignore="true"
280
295
  ${this.#isNumberMode ? 'inputmode="decimal"' : ''}></span>`;
281
296
 
282
297
  const controls = this.#isNumberMode ? `
@@ -463,13 +478,11 @@ export class UIInput extends UIFormElement {
463
478
  // Re-resolve prefix/suffix from the live property each render (FEEDBACK-93).
464
479
  this.#syncAffixSlots();
465
480
 
466
- if (this.label) {
467
- this.removeAttribute('aria-label');
468
- } else if (this.placeholder) {
469
- this.setAttribute('aria-label', this.placeholder);
470
- } else {
471
- this.removeAttribute('aria-label');
472
- }
481
+ // A11y (gh#1647): a visible `label` already names the field; otherwise
482
+ // fall back to `placeholder` — but never clobber a consumer's own
483
+ // aria-label (value-tracking guard, gh#1644/#1646).
484
+ const ariaSource = this.label ? '' : (this.placeholder || '');
485
+ this.#lastAutoAriaLabel = syncAutoAriaLabel(this, this.#lastAutoAriaLabel, ariaSource);
473
486
 
474
487
  if (this.#isNumberMode) {
475
488
  const n = this.valueAsNumber;
@@ -51,6 +51,11 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
51
51
  /* ── State: disabled ── */
52
52
  --input-bg-disabled: var(--a-ui-bg-disabled);
53
53
  --input-fg-disabled: var(--a-ui-text-disabled);
54
+ /* gh#1595 — border kept full contrast on disabled; otp-input.css /
55
+ upload.css already alias this exact role for their own disabled
56
+ border, so this follows that sibling pattern rather than inventing
57
+ a new one. */
58
+ --input-border-disabled: var(--a-ui-border-disabled);
54
59
  text-align: start; /* §text-align-reset — blocks inheritance from centered ancestors */
55
60
  }
56
61
 
@@ -381,6 +386,7 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
381
386
  :scope[disabled] [slot="field"] {
382
387
  background: var(--input-bg-disabled);
383
388
  color: var(--input-fg-disabled);
389
+ border-color: var(--input-border-disabled);
384
390
  cursor: not-allowed;
385
391
  }
386
392
 
@@ -33,8 +33,8 @@ props:
33
33
  against this enum historically, but functionally broken generation
34
34
  output). Use the owning primitive instead — CalendarPicker for date
35
35
  (+ month/week, no dedicated granularity primitive exists),
36
- DatetimePicker for datetime-local, TimePicker for time,
37
- ColorInput/ColorPicker for color, Search for search. See
36
+ CalendarPicker precision="minute" for datetime-local, TimePicker
37
+ for time, ColorInput/ColorArea for color, Search for search. See
38
38
  anti_patterns.
39
39
  type: string
40
40
  default: text
@@ -264,9 +264,10 @@ a2ui:
264
264
  <input type="password"> for masking (per ADR-0025).
265
265
  - >-
266
266
  Wrap <input-ui> in <field-ui label="…" hint="…" error="…"> for
267
- the canonical stacked label / hint / error chrome. The inline
268
- [label] / [hint] / [error] props are also supported on the
269
- primitive for compact use.
267
+ the canonical stacked label / hint / error chrome. input-ui itself
268
+ only supports inline [label] / [error] (gh#1669 [hint] is a
269
+ field-ui-only prop; input-ui has no equivalent) — use field-ui
270
+ when a hint line is needed.
270
271
  - >-
271
272
  Form participation is implicit via UIFormElement. Set [name] for
272
273
  FormData submission; [required] / [disabled] / [readonly]
@@ -291,8 +292,8 @@ a2ui:
291
292
  — text/email/password/number/tel/url (gh#1363 B10). Never
292
293
  set [type] to an HTML5 value input-ui doesn't render — route
293
294
  date/time/color/search intents to the owning primitive instead:
294
- CalendarPicker, DatetimePicker, TimePicker, ColorInput (or
295
- ColorPicker directly), Search. See anti_patterns.
295
+ CalendarPicker (precision="minute" for datetime-local), TimePicker,
296
+ ColorInput (or ColorArea directly), Search. See anti_patterns.
296
297
  anti_patterns:
297
298
  - wrong: |
298
299
  {"component": "Input", "type": "date"}
@@ -307,9 +308,11 @@ anti_patterns:
307
308
  {"component": "Input", "type": "datetime-local"}
308
309
  why: |
309
310
  Same gap as "date" — no native datetime picker rendered. Use
310
- DatetimePicker for combined date + time input.
311
+ CalendarPicker with precision="minute" for combined date + time
312
+ input (DatetimePicker was a deprecated thin alias, cut in 0.8.43,
313
+ gh#1617 — CalendarPicker is the sole A2UI component now).
311
314
  fix: |
312
- {"component": "DatetimePicker"}
315
+ {"component": "CalendarPicker", "precision": "minute"}
313
316
  - wrong: |
314
317
  {"component": "Input", "type": "time"}
315
318
  why: |
@@ -338,8 +341,9 @@ anti_patterns:
338
341
  why: |
339
342
  No native color swatch/picker rendered — just a plain text
340
343
  surface. Use ColorInput for a compact form-bearing swatch +
341
- popover, or ColorPicker directly for the full OKLCH picker
342
- surface.
344
+ popover, or ColorArea directly for the full OKLCH picker surface
345
+ (ColorPicker was a deprecated thin alias, cut in 0.8.43, gh#1617 —
346
+ ColorArea is the sole A2UI component now).
343
347
  fix: |
344
348
  {"component": "ColorInput"}
345
349
  - wrong: |
@@ -42,6 +42,8 @@ export class UIIntegrationCard extends UIElement {
42
42
  description: string;
43
43
  /** Disables the action button. */
44
44
  disabled: boolean;
45
+ /** When `status="error"`, the message to show below the description. Ignored for other statuses. */
46
+ errorMessage: string;
45
47
  /** Logo URL (preferred — renders as <img>) or icon name (renders as <icon-ui>). URLs are sniffed by presence of `/`. */
46
48
  logo: string;
47
49
  /** Display name shown as the card title. Required. */
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://adiaui.dev/a2ui/v0_9/components/MenuItem.json",
4
4
  "title": "MenuItem",
5
- "description": "Child of `<menu-ui>`. One actionable row inside a menu, with optional leading icon + label + description + value. (`subtitle` is a deprecated alias for `description` — ADR-0063, gh#1563.)",
5
+ "description": "Child of `<menu-ui>`. One actionable row inside a menu, with optional leading icon + label + description + value. (The deprecated `subtitle` prop alias for `description` was cut in 0.8.43 — ADR-0063, gh#1563/gh#1617.)",
6
6
  "type": "object",
7
7
  "allOf": [
8
8
  {
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "properties": {
16
16
  "description": {
17
- "description": "Optional secondary description line rendered under the label. Canonical spelling (ADR-0063, gh#1563) replacing `subtitle` — converges menu-item onto the ~20-component corpus majority that already names this concept `description`.",
17
+ "description": "Optional secondary description line rendered under the label. Canonical spelling (ADR-0063, gh#1563) replacing `subtitle` — converges menu-item onto the ~20-component corpus majority that already names this concept `description`. The deprecated `subtitle` prop alias was cut in 0.8.43 (gh#1617).",
18
18
  "type": "string",
19
19
  "default": ""
20
20
  },
@@ -31,11 +31,6 @@
31
31
  "type": "string",
32
32
  "default": ""
33
33
  },
34
- "subtitle": {
35
- "description": "DEPRECATED (ADR-0063, gh#1563) — use `description` instead. Same type/default/rendered position; kept working as a dual-read compat shim (one-time console.warn when it's the name that resolves). Removed in 0.9.0. (The `subtitle` SLOT below is a separate surface, deliberately left unrenamed in this pass — see its own description.)",
36
- "type": "string",
37
- "default": ""
38
- },
39
34
  "text": {
40
35
  "description": "Item label text.",
41
36
  "type": "string",
@@ -86,7 +81,7 @@
86
81
  "description": "Custom leading element override; falls through to `[icon]` prop if not slotted."
87
82
  },
88
83
  "subtitle": {
89
- "description": "Custom secondary-line content override; falls through to the `[description]` prop (or its deprecated `[subtitle]` alias ADR-0063, gh#1563) if not slotted. The slot's own name is unchanged by that rename — this is a distinct, deliberately-unrenamed surface."
84
+ "description": "Custom secondary-line content override; falls through to the `[description]` prop if not slotted (ADR-0063, gh#1563 — the deprecated `[subtitle]` prop alias was cut in 0.8.43, gh#1617). The slot's own name is unchanged by that rename — this is a distinct, deliberately-unrenamed surface."
90
85
  },
91
86
  "text": {
92
87
  "description": "Custom label content; falls through to `[text]` prop if not slotted."
@@ -13,7 +13,7 @@ component: MenuItem
13
13
  category: navigation
14
14
  version: 1
15
15
  description: |-
16
- Child of `<menu-ui>`. One actionable row inside a menu, with optional leading icon + label + description + value. (`subtitle` is a deprecated alias for `description` — ADR-0063, gh#1563.)
16
+ Child of `<menu-ui>`. One actionable row inside a menu, with optional leading icon + label + description + value. (The deprecated `subtitle` prop alias for `description` was cut in 0.8.43 — ADR-0063, gh#1563/gh#1617.)
17
17
 
18
18
  props:
19
19
  icon:
@@ -24,21 +24,13 @@ props:
24
24
  description: Item label text.
25
25
  type: string
26
26
  default: ""
27
- subtitle:
28
- description: >-
29
- DEPRECATED (ADR-0063, gh#1563) — use `description` instead. Same
30
- type/default/rendered position; kept working as a dual-read compat
31
- shim (one-time console.warn when it's the name that resolves).
32
- Removed in 0.9.0. (The `subtitle` SLOT below is a separate surface,
33
- deliberately left unrenamed in this pass — see its own description.)
34
- type: string
35
- default: ""
36
27
  description:
37
28
  description: >-
38
29
  Optional secondary description line rendered under the label.
39
30
  Canonical spelling (ADR-0063, gh#1563) replacing `subtitle` —
40
31
  converges menu-item onto the ~20-component corpus majority that
41
- already names this concept `description`.
32
+ already names this concept `description`. The deprecated `subtitle`
33
+ prop alias was cut in 0.8.43 (gh#1617).
42
34
  type: string
43
35
  default: ""
44
36
  value:
@@ -64,9 +56,10 @@ slots:
64
56
  subtitle:
65
57
  description: >-
66
58
  Custom secondary-line content override; falls through to the
67
- `[description]` prop (or its deprecated `[subtitle]` aliasADR-0063,
68
- gh#1563) if not slotted. The slot's own name is unchanged by that
69
- rename — this is a distinct, deliberately-unrenamed surface.
59
+ `[description]` prop if not slotted (ADR-0063, gh#1563the
60
+ deprecated `[subtitle]` prop alias was cut in 0.8.43, gh#1617). The
61
+ slot's own name is unchanged by that rename — this is a distinct,
62
+ deliberately-unrenamed surface.
70
63
  trailing:
71
64
  description: Trailing element pinned to the far edge (shortcut hint, check mark).
72
65
 
@@ -98,13 +91,12 @@ a2ui:
98
91
  hint).
99
92
  - >-
100
93
  <action-item-ui> (child of <action-list-ui>) shares this row contract
101
- (icon/text/subtitle/value/variant/disabled props, icon/text/subtitle/
102
- trailing slot overrides) for inline, non-popover command rows — use
103
- it instead when the surface isn't a <menu-ui> popover (#1363 C5).
104
- NOTE (ADR-0063, gh#1563): <menu-item-ui>'s own `subtitle` PROP is now
105
- a deprecated alias for `description` — <action-item-ui> still uses
106
- `subtitle` as its own primary spelling and was NOT renamed in this
107
- pass (flagged as a discovered follow-up candidate, not fixed here).
94
+ (icon/text/description/value/variant/disabled props, icon/text/
95
+ subtitle/trailing slot overrides) for inline, non-popover command
96
+ rows — use it instead when the surface isn't a <menu-ui> popover
97
+ (#1363 C5). Both components' deprecated `subtitle` PROP aliases for
98
+ `description` were cut in 0.8.43 (ADR-0063, gh#1563/gh#1571,
99
+ gh#1617); the `subtitle` SLOT on both is unchanged.
108
100
 
109
101
  keywords:
110
102
  - menu-item
@@ -258,13 +258,11 @@ export class UIMenuItem extends UIElement {
258
258
  static properties = {
259
259
  icon: { type: String, default: '', reflect: true },
260
260
  text: { type: String, default: '', reflect: true },
261
- // ADR-0063 (gh#1563) — `subtitle` is DEPRECATED (menu-item was the sole
262
- // ~20-component-corpus outlier using `subtitle` where every sibling
263
- // uses `description`); `description` is canonical. `subtitle` stays
264
- // declared (dual-read compat, 0.8.42). The `subtitle` SLOT (a separate
265
- // surface — see menu-item.yaml) is deliberately unchanged; only the
266
- // PROP renames.
267
- subtitle: { type: String, default: '', reflect: true },
261
+ // ADR-0063 (gh#1563) — `description` is canonical (menu-item was the
262
+ // sole ~20-component-corpus outlier using `subtitle`); the deprecated
263
+ // `subtitle` PROP dual-read shim was cut in 0.8.43 (gh#1617). The
264
+ // `subtitle` SLOT (a separate surface — see menu-item.yaml) is
265
+ // deliberately unchanged.
268
266
  description: { type: String, default: '', reflect: true },
269
267
  value: { type: String, default: '', reflect: true },
270
268
  variant: { type: String, default: 'default', reflect: true },
@@ -273,32 +271,12 @@ export class UIMenuItem extends UIElement {
273
271
 
274
272
  static template = () => null;
275
273
 
276
- // ADR-0063 (gh#1563) one-shot-per-class deprecation warn for [subtitle].
277
- static #warnedSubtitle = false;
278
-
279
274
  // Set when connected() stamped role="menuitem" itself; a consumer-provided
280
275
  // role (e.g. admin-roster's role="menuitemradio", gh#616) is never touched
281
276
  // — that consumer also owns tabindex and any checked-state aria.
282
277
  #roleStamped = false;
283
278
 
284
279
  connected() {
285
- // ADR-0063 (gh#1563) compat shim — resolve BEFORE #stamp() reads
286
- // `this.description`, so there's no wrong-then-right flash. New-name
287
- // precedence: [description] wins outright when present; only fall
288
- // back to (and mirror) [subtitle] when [description] is absent.
289
- if (!this.hasAttribute('description') && this.hasAttribute('subtitle')) {
290
- this.description = this.subtitle;
291
- if (!UIMenuItem.#warnedSubtitle) {
292
- UIMenuItem.#warnedSubtitle = true;
293
- // eslint-disable-next-line no-console
294
- console.warn(
295
- '[AdiaUI] <menu-item-ui [subtitle]> is deprecated — use ' +
296
- '[description] instead (ADR-0063, gh#1563). Kept working as-is; ' +
297
- 'no behavior change.',
298
- );
299
- }
300
- }
301
-
302
280
  // role="menuitem" ONLY when actually inside a menu container. A menu-ui
303
281
  // closes by moving its items back into its own light DOM (see #hide()),
304
282
  // where there is no role="menu"/menubar ancestor — a bare role="menuitem"
@@ -41,14 +41,12 @@ export class UIMenuDivider extends UIElement {
41
41
  }
42
42
 
43
43
  export class UIMenuItem extends UIElement {
44
- /** Optional secondary description line rendered under the label. Canonical spelling (ADR-0063, gh#1563) replacing `subtitle` — converges menu-item onto the ~20-component corpus majority that already names this concept `description`. */
44
+ /** Optional secondary description line rendered under the label. Canonical spelling (ADR-0063, gh#1563) replacing `subtitle` — converges menu-item onto the ~20-component corpus majority that already names this concept `description`. The deprecated `subtitle` prop alias was cut in 0.8.43 (gh#1617). */
45
45
  description: string;
46
46
  /** Disabled state — blocks pointer + keyboard activation. */
47
47
  disabled: boolean;
48
48
  /** Optional leading Phosphor icon name. */
49
49
  icon: string;
50
- /** DEPRECATED (ADR-0063, gh#1563) — use `description` instead. Same type/default/rendered position; kept working as a dual-read compat shim (one-time console.warn when it's the name that resolves). Removed in 0.9.0. (The `subtitle` SLOT below is a separate surface, deliberately left unrenamed in this pass — see its own description.) */
51
- subtitle: string;
52
50
  /** Item label text. */
53
51
  text: string;
54
52
  /** Stable identifier emitted via the parent menu's `select` event. */
@@ -41,11 +41,6 @@
41
41
  "type": "boolean",
42
42
  "default": false
43
43
  },
44
- "port-size": {
45
- "description": "Port indicator dot size in pixels",
46
- "type": "number",
47
- "default": 10
48
- },
49
44
  "portSize": {
50
45
  "description": "Port indicator size",
51
46
  "type": "number",
@@ -56,21 +51,11 @@
56
51
  "type": "boolean",
57
52
  "default": false
58
53
  },
59
- "show-ports": {
60
- "description": "Show port indicator dots. Defaults to true when editable",
61
- "type": "boolean",
62
- "default": false
63
- },
64
54
  "showPorts": {
65
55
  "description": "Show port indicators",
66
56
  "type": "boolean",
67
57
  "default": false
68
58
  },
69
- "stroke-width": {
70
- "description": "Stroke width in pixels",
71
- "type": "number",
72
- "default": 2
73
- },
74
59
  "strokeWidth": {
75
60
  "description": "Line stroke width",
76
61
  "type": "number",
@@ -30,10 +30,6 @@ props:
30
30
  description: Allow interactive creation/deletion of connections via drag
31
31
  type: boolean
32
32
  default: false
33
- port-size:
34
- description: Port indicator dot size in pixels
35
- type: number
36
- default: 10
37
33
  portSize:
38
34
  description: Port indicator size
39
35
  type: number
@@ -44,19 +40,11 @@ props:
44
40
  type: boolean
45
41
  default: false
46
42
  reflect: true
47
- show-ports:
48
- description: Show port indicator dots. Defaults to true when editable
49
- type: boolean
50
- default: false
51
43
  showPorts:
52
44
  description: Show port indicators
53
45
  type: boolean
54
46
  default: false
55
47
  attribute: show-ports
56
- stroke-width:
57
- description: Stroke width in pixels
58
- type: number
59
- default: 2
60
48
  strokeWidth:
61
49
  description: Line stroke width
62
50
  type: number
@@ -87,6 +87,16 @@ export class UINumberFormat extends UIElement {
87
87
  this.textContent = text;
88
88
  // Mirror raw value via aria-label so screen readers can read the
89
89
  // underlying number alongside the formatted glyphs.
90
+ //
91
+ // gh#1647 judgment call (triage-corrections comment, 2026-08-18):
92
+ // this aria-label is intentional component-owned chrome, not a
93
+ // consumer-overridable name — it mirrors the RAW underlying value
94
+ // ("42% (raw: 0.42)") specifically for a11y clarity, so a consumer
95
+ // override would likely defeat that purpose. Deliberately NOT ported
96
+ // to the value-tracking `#lastAutoAriaLabel` guard (core/a11y.js)
97
+ // that the other five gh#1647 primitives got — kept unconditional,
98
+ // auto-deriving on every render. Revisit only if a stated consumer
99
+ // need for overriding it surfaces.
90
100
  if (text && Number.isFinite(Number(this.value))) {
91
101
  this.setAttribute('aria-label', `${text} (raw: ${this.value})`);
92
102
  } else {
@@ -22,6 +22,7 @@
22
22
  */
23
23
 
24
24
  import { UIElement } from '../../core/element.js';
25
+ import { KEY_MAP } from '../../core/template.js';
25
26
 
26
27
  export class UIPagination extends UIElement {
27
28
  static properties = {
@@ -48,10 +49,32 @@ export class UIPagination extends UIElement {
48
49
 
49
50
  connected() {
50
51
  if (!this.#nav) {
51
- this.#nav = document.createElement('nav');
52
- this.#nav.setAttribute('slot', 'nav');
53
- this.#nav.setAttribute('aria-label', 'Pagination');
54
- this.appendChild(this.#nav);
52
+ // Adopt a pre-existing server-rendered <nav slot="nav"> instead of
53
+ // unconditionally appending a fresh one — first-connect adoption
54
+ // (gh#1687). Steady-state reconcile is unaffected; this only guards
55
+ // the construction-time #nav field being null on upgrade.
56
+ this.#nav = this.querySelector(':scope > nav[slot="nav"]');
57
+ if (this.#nav) {
58
+ // Adopting an existing nav is only half the fix — reconcile()'s
59
+ // own keyed-diff (element.js) keys off a WeakMap-shaped
60
+ // `parent[KEY_MAP]` populated by THIS element's own prior render
61
+ // calls, which a freshly-parsed SSR fragment never has. Without
62
+ // seeding it here, the very first render treats every adopted
63
+ // child as unrecognized, stamps a full set of BRAND NEW nodes
64
+ // alongside them, and never removes the orphaned originals —
65
+ // silently doubling the button count inside the (correctly
66
+ // singular) adopted <nav>. Positionally zip the adopted
67
+ // children against the SAME key order #buildRange would
68
+ // produce right now; a shape mismatch (stale/hand-edited SSR
69
+ // markup) is left unseeded so reconcile falls back to its
70
+ // normal rebuild-from-scratch behavior instead of miskeying.
71
+ this.#seedKeyMapFromAdoptedNav();
72
+ } else {
73
+ this.#nav = document.createElement('nav');
74
+ this.#nav.setAttribute('slot', 'nav');
75
+ this.#nav.setAttribute('aria-label', 'Pagination');
76
+ this.appendChild(this.#nav);
77
+ }
55
78
  }
56
79
 
57
80
  if (!this.#bound) {
@@ -69,6 +92,67 @@ export class UIPagination extends UIElement {
69
92
  this.#bound = false;
70
93
  }
71
94
 
95
+ // Review finding (PR #1692) — the tag/marker a given item.type produces
96
+ // via #createItem(): ellipsis is a bare <span data-ellipsis>; prev/next/
97
+ // page are all <button-ui> distinguished by their own data-* marker
98
+ // (page additionally carries data-value). A same-LENGTH adopted child
99
+ // that doesn't match this shape (e.g. a plain <span> standing in for a
100
+ // page-cell <button-ui>) would still get SEEDED by a count-only check,
101
+ // then #updateItem() — which only sets attributes, never replaces the
102
+ // element — leaves it permanently the wrong tag. Checking shape here
103
+ // is what makes the fallback in #seedKeyMapFromAdoptedNav trustworthy.
104
+ #childMatchesItem(el, item) {
105
+ if (item.type === 'ellipsis') {
106
+ return el.tagName === 'SPAN' && el.hasAttribute('data-ellipsis');
107
+ }
108
+ if (el.tagName !== 'BUTTON-UI') return false;
109
+ if (item.type === 'prev') return el.hasAttribute('data-prev');
110
+ if (item.type === 'next') return el.hasAttribute('data-next');
111
+ return el.hasAttribute('data-page') && el.dataset.value === String(item.value);
112
+ }
113
+
114
+ // gh#1687 — see the call site's comment in connected(). Positionally
115
+ // zips the adopted nav's existing children against the key order the
116
+ // very next render() will compute, seeding reconcile()'s own
117
+ // `parent[KEY_MAP]` (element.js) so that render finds every item
118
+ // already present instead of stamping a duplicate full set.
119
+ #seedKeyMapFromAdoptedNav() {
120
+ const page = Math.max(1, Math.min(this.page, this.total));
121
+ const total = Math.max(1, this.total);
122
+ const siblings = Math.max(0, this.siblings);
123
+ const range = this.#buildRange(page, total, siblings);
124
+ const children = [...this.#nav.children];
125
+ const shapeMatches = children.length === range.length
126
+ && range.every((item, i) => this.#childMatchesItem(children[i], item));
127
+ if (!shapeMatches) {
128
+ // Review finding (PR #1692, two rounds) — a bare return here left
129
+ // the mismatch case worse off than doing nothing: reconcile()'s
130
+ // own cleanup loop only removes entries it finds IN
131
+ // `parent[KEY_MAP]`, so an unseeded map means every existing
132
+ // child is invisible to it — the next render() then stamps a
133
+ // full fresh set AND the stale adopted children survive alongside
134
+ // it (reproduced: 3 stale placeholder buttons + a correct fresh
135
+ // 9-item render = 12 nodes). Length alone isn't a reliable
136
+ // correlation signal either — a same-length fixture with the
137
+ // WRONG tag/marker per position (e.g. N bare <span>s standing in
138
+ // for N <button-ui> page-cells) would pass a count-only check and
139
+ // get seeded, then #updateItem() — attribute-only, never a tag
140
+ // swap — leaves it permanently wrong. Either kind of mismatch
141
+ // means the adopted markup can't be trusted to correlate with the
142
+ // next render's key order at all — abandon adoption for real:
143
+ // clear the stale children so the fallback is an actual clean
144
+ // rebuild, the same shape #nav would be in if it had just been
145
+ // freshly created.
146
+ this.#nav.replaceChildren();
147
+ return;
148
+ }
149
+ const map = new Map();
150
+ for (let i = 0; i < range.length; i++) {
151
+ map.set(range[i].key, children[i]);
152
+ }
153
+ this.#nav[KEY_MAP] = map;
154
+ }
155
+
72
156
  render() {
73
157
  if (!this.#nav) return;
74
158
 
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://adiaui.dev/a2ui/v0_9/components/Pane.json",
4
4
  "title": "Pane",
5
- "description": "Collapsible, resizable content panel — drag-resize handle on the\ninner edge when [side=\"leading\"] / [side=\"trailing\"], [resizable]\nopt-in. Used standalone for resizable two-pane layouts, or wrapped\nby <editor-sidebar> / <admin-sidebar> which DELEGATE drag to the\ninner pane-ui rather than reimplementing it. See pane.yaml's\na2ui.rules for the full composition contract.\n",
5
+ "description": "Collapsible, resizable content panel — drag-resize handle on the\ninner edge when [edge=\"leading\"] / [edge=\"trailing\"], [resizable]\nopt-in. Used standalone for resizable two-pane layouts, or wrapped\nby <editor-sidebar> / <admin-sidebar> which DELEGATE drag to the\ninner pane-ui rather than reimplementing it. See pane.yaml's\na2ui.rules for the full composition contract.\n",
6
6
  "type": "object",
7
7
  "allOf": [
8
8
  {
@@ -22,7 +22,7 @@
22
22
  "const": "Pane"
23
23
  },
24
24
  "edge": {
25
- "description": "Opts a pane into horizontal-sibling chrome: suppresses the default\nfour-sided border and moves the resize grabber to the inner edge\n(right edge for `leading`, left edge for `trailing`). Also flips the\nresize-drag direction so `trailing` panes grow when dragged leftward.\nUnset keeps the pane-intrinsic chrome (full border, right-edge\ngrabber when resizable). Canonical spelling (ADR-0063, gh#1563)\nreplacing `side`.\n",
25
+ "description": "Opts a pane into horizontal-sibling chrome: suppresses the default\nfour-sided border and moves the resize grabber to the inner edge\n(right edge for `leading`, left edge for `trailing`). Also flips the\nresize-drag direction so `trailing` panes grow when dragged leftward.\nUnset keeps the pane-intrinsic chrome (full border, right-edge\ngrabber when resizable). Canonical spelling (ADR-0063, gh#1563) — the\ndeprecated `side` dual-read alias was cut in 0.8.43 (gh#1617).\n",
26
26
  "type": "string",
27
27
  "enum": [
28
28
  "",
@@ -45,16 +45,6 @@
45
45
  "description": "Component property: resizable.",
46
46
  "type": "boolean",
47
47
  "default": false
48
- },
49
- "side": {
50
- "description": "DEPRECATED (ADR-0063, gh#1563) — use `edge` instead. Same type/default/enum; kept working as a dual-read compat shim (one-time console.warn when it's the name that resolves). Removed in 0.9.0.",
51
- "type": "string",
52
- "enum": [
53
- "",
54
- "leading",
55
- "trailing"
56
- ],
57
- "default": ""
58
48
  }
59
49
  },
60
50
  "required": [