@adia-ai/web-components 0.5.8 → 0.5.10

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/USAGE.md +253 -0
  2. package/components/accordion/accordion.d.ts +22 -1
  3. package/components/action-list/action-list.d.ts +14 -1
  4. package/components/agent-artifact/agent-artifact.d.ts +1 -1
  5. package/components/agent-feedback-bar/agent-feedback-bar.d.ts +1 -1
  6. package/components/agent-questions/agent-questions.d.ts +1 -1
  7. package/components/agent-reasoning/agent-reasoning.d.ts +1 -1
  8. package/components/agent-suggestions/agent-suggestions.d.ts +1 -1
  9. package/components/agent-trace/agent-trace.d.ts +1 -1
  10. package/components/alert/alert.d.ts +1 -1
  11. package/components/avatar/avatar.d.ts +8 -1
  12. package/components/badge/badge.d.ts +1 -1
  13. package/components/block/block.d.ts +1 -1
  14. package/components/breadcrumb/breadcrumb.d.ts +1 -1
  15. package/components/button/button.d.ts +1 -1
  16. package/components/canvas/canvas.d.ts +1 -1
  17. package/components/card/card.d.ts +1 -1
  18. package/components/chart/chart.d.ts +1 -1
  19. package/components/chart-legend/chart-legend.d.ts +1 -1
  20. package/components/chat-thread/chat-thread.d.ts +40 -1
  21. package/components/code/code.css +1 -1
  22. package/components/col/col.a2ui.json +4 -4
  23. package/components/col/col.d.ts +2 -2
  24. package/components/col/col.yaml +2 -2
  25. package/components/command/command.d.ts +1 -1
  26. package/components/demo-toggle/demo-toggle.d.ts +1 -1
  27. package/components/description-list/description-list.d.ts +1 -1
  28. package/components/divider/divider.d.ts +1 -1
  29. package/components/drawer/drawer.d.ts +1 -1
  30. package/components/embed/embed.d.ts +1 -1
  31. package/components/empty-state/class.js +10 -4
  32. package/components/empty-state/empty-state.a2ui.json +5 -0
  33. package/components/empty-state/empty-state.css +36 -0
  34. package/components/empty-state/empty-state.d.ts +6 -1
  35. package/components/empty-state/empty-state.test.js +77 -0
  36. package/components/empty-state/empty-state.yaml +9 -0
  37. package/components/feed/feed.css +1 -1
  38. package/components/feed/feed.d.ts +106 -13
  39. package/components/field/field.d.ts +1 -1
  40. package/components/fields/fields.d.ts +1 -1
  41. package/components/grid/grid.d.ts +1 -1
  42. package/components/heatmap/heatmap.d.ts +1 -1
  43. package/components/icon/icon.d.ts +1 -1
  44. package/components/image/image.d.ts +1 -1
  45. package/components/input/class.js +2 -1
  46. package/components/input/input.a2ui.json +5 -0
  47. package/components/input/input.d.ts +14 -0
  48. package/components/input/input.test.js +99 -0
  49. package/components/input/input.yaml +14 -0
  50. package/components/inspector/inspector.d.ts +1 -1
  51. package/components/kbd/kbd.d.ts +1 -1
  52. package/components/link/link.d.ts +1 -1
  53. package/components/list/list.d.ts +10 -1
  54. package/components/menu/menu-divider.a2ui.json +41 -0
  55. package/components/menu/menu-divider.yaml +15 -0
  56. package/components/menu/menu-item.a2ui.json +77 -0
  57. package/components/menu/menu-item.yaml +45 -0
  58. package/components/menu/menu.d.ts +17 -1
  59. package/components/modal/modal.d.ts +1 -1
  60. package/components/nav/nav.css +1 -1
  61. package/components/nav/nav.d.ts +1 -1
  62. package/components/nav-group/nav-group.d.ts +1 -1
  63. package/components/nav-item/nav-item.d.ts +1 -1
  64. package/components/noodles/noodles.d.ts +1 -1
  65. package/components/page/page.d.ts +1 -1
  66. package/components/pagination/pagination.d.ts +1 -1
  67. package/components/pane/pane.d.ts +1 -1
  68. package/components/pipeline-status/pipeline-status.d.ts +1 -1
  69. package/components/popover/popover.d.ts +1 -1
  70. package/components/progress/progress.d.ts +1 -1
  71. package/components/progress-row/progress-row.d.ts +1 -1
  72. package/components/range/range.css +1 -1
  73. package/components/rating/rating.d.ts +8 -0
  74. package/components/richtext/richtext.a2ui.json +4 -4
  75. package/components/richtext/richtext.d.ts +2 -2
  76. package/components/richtext/richtext.yaml +2 -2
  77. package/components/row/row.d.ts +1 -1
  78. package/components/segment/segment.d.ts +1 -1
  79. package/components/select/class.js +28 -0
  80. package/components/select/select.css +1 -1
  81. package/components/select/select.d.ts +14 -0
  82. package/components/select/select.yaml +28 -0
  83. package/components/skeleton/skeleton.d.ts +1 -1
  84. package/components/slider/class.js +20 -50
  85. package/components/slider/slider.css +1 -1
  86. package/components/stack/stack.d.ts +1 -1
  87. package/components/step-progress/step-progress.d.ts +1 -1
  88. package/components/stepper/stepper-item.a2ui.json +74 -0
  89. package/components/stepper/stepper-item.yaml +45 -0
  90. package/components/stepper/stepper.d.ts +13 -1
  91. package/components/stream/stream.d.ts +1 -1
  92. package/components/swatch/swatch.css +1 -1
  93. package/components/swatch/swatch.d.ts +1 -1
  94. package/components/swiper/swiper.d.ts +1 -1
  95. package/components/switch/switch.css +1 -1
  96. package/components/table/table.d.ts +1 -1
  97. package/components/table-toolbar/table-toolbar.d.ts +1 -1
  98. package/components/tabs/tabs.d.ts +12 -1
  99. package/components/tag/tag.d.ts +1 -1
  100. package/components/text/text.d.ts +64 -13
  101. package/components/text/text.test.js +8 -2
  102. package/components/textarea/class.js +7 -1
  103. package/components/textarea/textarea.a2ui.json +5 -0
  104. package/components/textarea/textarea.yaml +14 -0
  105. package/components/timeline/timeline.d.ts +20 -1
  106. package/components/toast/class.js +9 -0
  107. package/components/toast/toast.d.ts +9 -5
  108. package/components/toast/toast.yaml +16 -0
  109. package/components/toggle-group/toggle-group.d.ts +12 -1
  110. package/components/toggle-group/toggle-option.a2ui.json +61 -0
  111. package/components/toggle-group/toggle-option.yaml +33 -0
  112. package/components/toggle-scheme/toggle-scheme.d.ts +1 -1
  113. package/components/toolbar/toolbar-group.a2ui.json +41 -0
  114. package/components/toolbar/toolbar-group.yaml +16 -0
  115. package/components/toolbar/toolbar.d.ts +4 -1
  116. package/components/tooltip/tooltip.css +2 -2
  117. package/components/tooltip/tooltip.d.ts +1 -1
  118. package/components/tree/tree.css +1 -1
  119. package/components/tree/tree.d.ts +18 -1
  120. package/core/form.d.ts +35 -0
  121. package/core/form.js +93 -0
  122. package/core/signals.d.ts +28 -0
  123. package/index.d.ts +2 -2
  124. package/package.json +1 -1
  125. package/styles/colors/semantics.css +6 -0
  126. package/styles/tokens.css +3 -0
  127. package/styles/typography.css +2 -0
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/description-list
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/divider
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/drawer
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/embed
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -26,9 +26,13 @@ import { UIElement } from '../../core/element.js';
26
26
 
27
27
  export class UIEmptyState extends UIElement {
28
28
  static properties = {
29
- icon: { type: String, default: '', reflect: true },
30
- heading: { type: String, default: '', reflect: true },
31
- description: { type: String, default: '', reflect: true },
29
+ icon: { type: String, default: '', reflect: true },
30
+ heading: { type: String, default: '', reflect: true },
31
+ description: { type: String, default: '', reflect: true },
32
+ // §223 (v0.5.9): minimal layout — single-line muted (no centered column,
33
+ // no padding bump, no icon-size lg). Use for inline empty-table-row /
34
+ // inline placeholder cells where the canvas placeholder is too loud.
35
+ minimal: { type: Boolean, default: false, reflect: true },
32
36
  };
33
37
 
34
38
  static template = () => null;
@@ -47,7 +51,9 @@ export class UIEmptyState extends UIElement {
47
51
  if (!this.#iconEl) {
48
52
  this.#iconEl = this.#stampMark(document.createElement('icon-ui'));
49
53
  this.#iconEl.setAttribute('slot', 'icon');
50
- this.#iconEl.setAttribute('size', 'lg');
54
+ // §223 (v0.5.9): minimal layout sizes the icon inline; non-minimal
55
+ // keeps the canonical lg badge.
56
+ if (!this.minimal) this.#iconEl.setAttribute('size', 'lg');
51
57
  this.insertBefore(this.#iconEl, this.firstChild);
52
58
  }
53
59
 
@@ -30,6 +30,11 @@
30
30
  "description": "Icon name displayed above the heading",
31
31
  "type": "string",
32
32
  "default": ""
33
+ },
34
+ "minimal": {
35
+ "description": "§223 (v0.5.9). Single-line muted layout — drops centered-column chrome\n(no vertical padding, no row-stack, no icon-size bump). Useful for\ninline empty-table-row / inline empty-list / placeholder cells where\nthe full-canvas placeholder is too prominent.",
36
+ "type": "boolean",
37
+ "default": false
33
38
  }
34
39
  },
35
40
  "required": [
@@ -55,4 +55,40 @@
55
55
  [slot="action"] {
56
56
  margin-top: var(--empty-state-action-mt);
57
57
  }
58
+
59
+ /* ── §223 (v0.5.9): [minimal] — single-line muted layout ──
60
+ Drops centered-column chrome (no canvas-style padding, no flex-column
61
+ stack). Renders as `[icon] heading [description]` inline in muted-fg.
62
+ Useful for inline placeholder rows / cells where the full placeholder
63
+ would be too loud. */
64
+ :scope[minimal] {
65
+ flex-direction: row;
66
+ align-items: baseline;
67
+ text-align: start;
68
+ padding: var(--a-space-2) var(--a-space-3);
69
+ gap: var(--a-space-2);
70
+ color: var(--a-fg-muted);
71
+ font-size: var(--a-ui-size);
72
+ }
73
+ :scope[minimal] [slot="icon"] {
74
+ font-size: 1em;
75
+ color: inherit;
76
+ flex-shrink: 0;
77
+ }
78
+ :scope[minimal] [slot="heading"] {
79
+ font-size: inherit;
80
+ font-weight: var(--a-weight-normal);
81
+ color: inherit;
82
+ line-height: inherit;
83
+ }
84
+ :scope[minimal] [slot="description"] {
85
+ font-size: inherit;
86
+ color: inherit;
87
+ line-height: inherit;
88
+ max-width: none;
89
+ }
90
+ :scope[minimal] [slot="action"] {
91
+ margin-top: 0;
92
+ margin-inline-start: auto;
93
+ }
58
94
  }
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/empty-state
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -19,4 +19,9 @@ export class UIEmptyState extends UIElement {
19
19
  heading: string;
20
20
  /** Icon name displayed above the heading */
21
21
  icon: string;
22
+ /** §223 (v0.5.9). Single-line muted layout — drops centered-column chrome
23
+ (no vertical padding, no row-stack, no icon-size bump). Useful for
24
+ inline empty-table-row / inline empty-list / placeholder cells where
25
+ the full-canvas placeholder is too prominent. */
26
+ minimal: boolean;
22
27
  }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * empty-state-ui — focused unit tests for the §223 (v0.5.9) `minimal`
3
+ * boolean prop.
4
+ *
5
+ * Pre-§223 only the centered-column canvas layout existed. §223 adds
6
+ * an inline single-row muted layout for placeholder cells / table rows
7
+ * where the full-canvas placeholder is too prominent (R13 §4).
8
+ */
9
+
10
+ import { describe, it, expect, beforeEach } from 'vitest';
11
+ import '../../core/element.js';
12
+ import './empty-state.js';
13
+
14
+ const tick = () => new Promise((r) => queueMicrotask(r));
15
+
16
+ function mount(html) {
17
+ const wrap = document.createElement('div');
18
+ wrap.innerHTML = html;
19
+ document.body.appendChild(wrap);
20
+ return wrap.firstElementChild;
21
+ }
22
+
23
+ describe('empty-state-ui — §223 (v0.5.9) minimal mode', () => {
24
+ beforeEach(() => { document.body.innerHTML = ''; });
25
+
26
+ it('defaults to non-minimal (canvas-style placeholder)', () => {
27
+ const el = mount('<empty-state-ui heading="No results"></empty-state-ui>');
28
+ expect(el.minimal).toBe(false);
29
+ expect(el.hasAttribute('minimal')).toBe(false);
30
+ });
31
+
32
+ it('reflects [minimal] attribute to the property', () => {
33
+ const el = mount('<empty-state-ui minimal heading="No matches"></empty-state-ui>');
34
+ expect(el.minimal).toBe(true);
35
+ expect(el.hasAttribute('minimal')).toBe(true);
36
+ });
37
+
38
+ it('stamps icon WITHOUT size="lg" when minimal', async () => {
39
+ const el = mount('<empty-state-ui minimal icon="search" heading="No matches"></empty-state-ui>');
40
+ await tick();
41
+ const icon = el.querySelector(':scope > [slot="icon"]');
42
+ expect(icon).not.toBeNull();
43
+ // Minimal layout uses 1em inherited font-size; non-minimal stamps lg.
44
+ expect(icon.hasAttribute('size')).toBe(false);
45
+ });
46
+
47
+ it('stamps icon WITH size="lg" when not minimal (no regression)', async () => {
48
+ const el = mount('<empty-state-ui icon="search" heading="No matches"></empty-state-ui>');
49
+ await tick();
50
+ const icon = el.querySelector(':scope > [slot="icon"]');
51
+ expect(icon).not.toBeNull();
52
+ expect(icon.getAttribute('size')).toBe('lg');
53
+ });
54
+
55
+ it('stamps heading + description slots in both modes', async () => {
56
+ const minimal = mount('<empty-state-ui minimal heading="Empty" description="No rows"></empty-state-ui>');
57
+ const canvas = mount('<empty-state-ui heading="Empty" description="No rows"></empty-state-ui>');
58
+ await tick();
59
+
60
+ for (const el of [minimal, canvas]) {
61
+ const h = el.querySelector(':scope > [slot="heading"]');
62
+ const d = el.querySelector(':scope > [slot="description"]');
63
+ expect(h?.textContent).toBe('Empty');
64
+ expect(d?.textContent).toBe('No rows');
65
+ }
66
+ });
67
+
68
+ it('honors a consumer-provided [slot="action"] child in either mode', () => {
69
+ const el = mount(`
70
+ <empty-state-ui minimal heading="Empty">
71
+ <button slot="action" data-test="reset">Reset</button>
72
+ </empty-state-ui>
73
+ `);
74
+ const action = el.querySelector(':scope > [slot="action"][data-test="reset"]');
75
+ expect(action).not.toBeNull();
76
+ });
77
+ });
@@ -24,6 +24,15 @@ props:
24
24
  description: Icon name displayed above the heading
25
25
  type: string
26
26
  default: ""
27
+ minimal:
28
+ description: |-
29
+ §223 (v0.5.9). Single-line muted layout — drops centered-column chrome
30
+ (no vertical padding, no row-stack, no icon-size bump). Useful for
31
+ inline empty-table-row / inline empty-list / placeholder cells where
32
+ the full-canvas placeholder is too prominent.
33
+ type: boolean
34
+ default: false
35
+ reflect: true
27
36
  events: {}
28
37
  slots:
29
38
  action:
@@ -102,7 +102,7 @@ feed-item-ui[data-closing] {
102
102
  --feed-item-duration: var(--a-duration);
103
103
  --feed-item-easing: var(--a-easing-out);
104
104
  --feed-item-gap: var(--a-space-3);
105
- --feed-item-max-width: var(--a-feed-max-width, 22rem);
105
+ --feed-item-max-width: 22rem; /* §230-bundle (v0.5.9): component-local. Was orphaned --a-feed-max-width — hoisted per RESPONSE-21 guidance. */
106
106
  }
107
107
  :scope[variant="info"] { --feed-item-fg: var(--a-info-fg); --feed-item-bg: var(--a-info-muted); }
108
108
  :scope[variant="success"] { --feed-item-fg: var(--a-success-fg); --feed-item-bg: var(--a-success-muted); }
@@ -1,29 +1,122 @@
1
1
  /**
2
- * `<feed-ui>` — Shared top-layer feed channel. Per docs/specs/feed-channel.md (SPEC-FEED-CHANNEL-001). Per-position singletons mounted lazily into document.body via Popover API; consumers post via the static API (`UIFeed.post()`) or the global 'feed' CustomEvent.
2
+ * `<feed-ui>` — Shared top-layer feed channel. Per docs/specs/feed-channel.md
3
+ * (SPEC-FEED-CHANNEL-001). Per-position singletons mounted lazily into
4
+ * document.body via Popover API; consumers post via the static API
5
+ * (`UIFeed.post()`) or the global 'feed' CustomEvent.
3
6
  *
4
7
  * @see https://ui-kit.exe.xyz/site/components/feed
5
8
  *
6
- * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
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.
9
+ * HAND-AUTHORED (not codegen'd) per §247 (v0.5.10, FB-24) — the
10
+ * sibling-yaml codegen walk (v0.5.9 §228) catches the two element classes
11
+ * (`UIFeedContainer` + `UIFeedItem`) but misses the ambient `UIFeed`
12
+ * static-API class because it has no tag-registered yaml. Hand-authored
13
+ * .d.ts adds the static-API + supporting types. `dts-codegen.mjs` keeps
14
+ * this file in its `HAND_AUTHORED_DTS` skip list.
11
15
  */
12
16
 
13
17
  import { UIElement } from '../../core/element.js';
14
18
 
15
- export type FeedCloseEvent = CustomEvent<unknown>;
19
+ /** Lane positions where `<feed-ui>` containers mount. */
20
+ export type FeedPosition =
21
+ | 'top-left' | 'top-center' | 'top-right'
22
+ | 'bottom-left' | 'bottom-center' | 'bottom-right'
23
+ | 'inline';
16
24
 
17
- export class UIFeed extends UIElement {
18
- /** Cap on simultaneously visible items per lane */
25
+ /**
26
+ * Options for `UIFeed.post()` the imperative one-shot path. Mirrors
27
+ * `UIFeedItem`'s reflected props plus `position` (which container lane).
28
+ */
29
+ export interface UIFeedPostOptions {
30
+ /** Body copy for the feed item. */
31
+ text: string;
32
+ /** Optional emphasis line above text. */
33
+ heading?: string;
34
+ /** Optional leading icon name (Phosphor). */
35
+ icon?: string;
36
+ /** Semantic variant. */
37
+ variant?: 'default' | 'info' | 'success' | 'warning' | 'danger';
38
+ /** Auto-fade timer in ms; `null` / `0` = sticky (requires user input). */
39
+ duration?: number;
40
+ /** Render an `x` close button (default `true` for sticky, `false` for auto-fade). */
41
+ dismissible?: boolean;
42
+ /** Lane to render into. Defaults to `'bottom-right'`. */
43
+ position?: FeedPosition;
44
+ /**
45
+ * Phase 2 action-required policy: when set, the item carries an action
46
+ * button. Per `feed-channel.md` §2.3, sets `role="alertdialog"` + focus
47
+ * is trapped until the action fires (or the item dismisses).
48
+ */
49
+ action?: string;
50
+ }
51
+
52
+ /** Imperative handle returned by `UIFeed.post()` — dismiss / update. */
53
+ export interface FeedHandle {
54
+ /** Stable id assigned by `<feed-ui>`. `null` if the post failed. */
55
+ id: string | null;
56
+ /** Dismiss the item programmatically (triggers exit animation). */
57
+ dismiss(): void;
58
+ /** Mutate the item's content in-place (e.g. promote loading→done). */
59
+ update(patch: Partial<UIFeedPostOptions>): void;
60
+ }
61
+
62
+ export type FeedContainerCloseEvent = CustomEvent<unknown>;
63
+
64
+ export class UIFeedContainer extends UIElement {
65
+ /** Cap on simultaneously visible items per lane. */
19
66
  max: number;
20
- /** Lane the feed renders into */
21
- position: 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' | 'inline';
67
+ /** Lane the feed renders into. */
68
+ position: FeedPosition;
69
+
70
+ addEventListener<K extends keyof HTMLElementEventMap>(
71
+ type: K,
72
+ listener: (this: UIFeedContainer, ev: HTMLElementEventMap[K]) => unknown,
73
+ options?: boolean | AddEventListenerOptions,
74
+ ): void;
75
+ addEventListener(type: 'close', listener: (ev: FeedContainerCloseEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
76
+ }
77
+
78
+ export type FeedItemCloseEvent = CustomEvent<unknown>;
79
+
80
+ export class UIFeedItem extends UIElement {
81
+ /** Render an `x` close button (default `true` for sticky, `false` for auto-fade). */
82
+ dismissible: boolean;
83
+ /** Auto-fade timer in ms; `null` / `0` = sticky (requires user input). */
84
+ duration: number;
85
+ /** Optional emphasis line above text. */
86
+ heading: string;
87
+ /** Optional leading icon name. */
88
+ icon: string;
89
+ /** Body copy. */
90
+ text: string;
91
+ /** Semantic variant. */
92
+ variant: 'default' | 'info' | 'success' | 'warning' | 'danger';
22
93
 
23
94
  addEventListener<K extends keyof HTMLElementEventMap>(
24
95
  type: K,
25
- listener: (this: UIFeed, ev: HTMLElementEventMap[K]) => unknown,
96
+ listener: (this: UIFeedItem, ev: HTMLElementEventMap[K]) => unknown,
26
97
  options?: boolean | AddEventListenerOptions,
27
98
  ): void;
28
- addEventListener(type: 'close', listener: (ev: FeedCloseEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
99
+ addEventListener(type: 'close', listener: (ev: FeedItemCloseEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
100
+ }
101
+
102
+ /**
103
+ * Static API for posting `<feed-item-ui>` rows without instantiating
104
+ * `<feed-ui>` declaratively. Per-position lanes auto-mount into
105
+ * `document.body` on first call. Use this in place of declarative
106
+ * `<feed-ui>` for one-shot programmatic messages (toast-style API).
107
+ *
108
+ * The class is NOT a custom element — it's a singleton namespace. Don't
109
+ * try to `new UIFeed()` or extend it; use the static methods only.
110
+ */
111
+ export class UIFeed {
112
+ /** Get (or lazily create + mount) the per-position lane container. */
113
+ static get(position?: FeedPosition): UIFeedContainer;
114
+ /** Post a feed item. Returns a handle for programmatic dismiss / update. */
115
+ static post(opts: UIFeedPostOptions): FeedHandle;
116
+ /** Clear all items in the lane at the given position. */
117
+ static clear(position?: FeedPosition): void;
118
+ /** Purge all lanes; tear down DOM containers. */
119
+ static purge(): void;
120
+ /** Internal: drop empty container after the last item dismisses. */
121
+ static releaseContainerIfEmpty(container: UIFeedContainer): void;
29
122
  }
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/field
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/fields
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/grid
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/heatmap
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/icon
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/image
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -601,7 +601,8 @@ export class UIInput extends UIFormElement {
601
601
  this.value = text;
602
602
  if (!this.#isNativePassword) this.#textEl.toggleAttribute('data-empty', !text);
603
603
  this.syncValue(text);
604
- this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
604
+ // §220 (v0.5.9, FEEDBACK-14 §3): trailing-debounce when `throttle > 0`.
605
+ this.scheduleThrottledInput();
605
606
  };
606
607
 
607
608
  #onBeforeInput = (e) => {
@@ -146,6 +146,11 @@
146
146
  "type": "string",
147
147
  "default": ""
148
148
  },
149
+ "throttle": {
150
+ "description": "§220 (v0.5.9, FEEDBACK-14 §3). Trailing-debounce on the `input`\nevent in milliseconds. When > 0, value mutates immediately + the UI\nstays responsive, but `input` dispatch is collapsed so only the\nfinal value in the throttle window emits. Useful for expensive\n`input`-driven computation (palette regen, large list filter,\nserver-side autocomplete). `change` fires unthrottled on blur /\nEnter / stepper commit; any pending `input` flushes before `change`\nso consumers see input→input→…→input→change ordering. Default 0\npreserves synchronous emission.",
151
+ "type": "number",
152
+ "default": 0
153
+ },
149
154
  "value": {
150
155
  "description": "Current input value, synced with contenteditable text surface. For `type=\"number\"`, this is the formatted numeric string; read `el.valueAsNumber` for the parsed Number.",
151
156
  "type": "string",
@@ -30,6 +30,20 @@ export class UIInput extends UIFormElement {
30
30
  suffix: string;
31
31
  /** Raw mode — drops chrome (border, padding) for inline composition. */
32
32
  raw: boolean;
33
+ /**
34
+ * §247 (v0.5.10, FB-26 §1): mobile keyboard hint per HTML `inputmode`
35
+ * spec — `numeric` / `decimal` / `email` / `tel` / `url` / `search` /
36
+ * `none`. Forwards to the underlying contenteditable surface. Empty
37
+ * string = no hint (browser default).
38
+ */
39
+ inputmode: string;
40
+ /**
41
+ * §247 (v0.5.10, FB-26 §1): HTML `autocomplete` attribute (`off` /
42
+ * `name` / `email` / `current-password` / `new-password` etc).
43
+ * Forwards to the underlying contenteditable surface for browser
44
+ * autofill behavior.
45
+ */
46
+ autocomplete: string;
33
47
 
34
48
  // ── Number-mode properties (active when type="number") ──
35
49
  /** Minimum value. `null` to disable. */
@@ -121,3 +121,102 @@ describe('input-ui — §199 leading/trailing affordance slots', () => {
121
121
  expect(text.textContent).toBe('Theme 1');
122
122
  });
123
123
  });
124
+
125
+ describe('input-ui — §220 (v0.5.9) throttle parity', () => {
126
+ beforeEach(() => { document.body.innerHTML = ''; });
127
+
128
+ it('inherits `throttle` property default 0 from UIFormElement', () => {
129
+ const el = mount('<input-ui></input-ui>');
130
+ expect(el.throttle).toBe(0);
131
+ });
132
+
133
+ it('reflects [throttle] attribute to the property', () => {
134
+ const el = mount('<input-ui throttle="150"></input-ui>');
135
+ expect(el.throttle).toBe(150);
136
+ });
137
+
138
+ it('dispatches `input` synchronously when throttle=0 (default)', async () => {
139
+ const el = mount('<input-ui></input-ui>');
140
+ await tick();
141
+ let count = 0;
142
+ // Filter for host-emitted CustomEvents (have detail.value); surface
143
+ // InputEvents bubble up too but have no detail property.
144
+ el.addEventListener('input', (e) => {
145
+ if (e.detail?.value !== undefined) count++;
146
+ });
147
+
148
+ // Simulate user typing — set value + dispatch DOM input on the
149
+ // contenteditable surface so the host's #onInput handler fires.
150
+ const surface = el.querySelector('[slot="text"]');
151
+ surface.textContent = 'a';
152
+ surface.dispatchEvent(new InputEvent('input', { bubbles: true }));
153
+ surface.textContent = 'ab';
154
+ surface.dispatchEvent(new InputEvent('input', { bubbles: true }));
155
+ surface.textContent = 'abc';
156
+ surface.dispatchEvent(new InputEvent('input', { bubbles: true }));
157
+
158
+ expect(count).toBe(3);
159
+ });
160
+
161
+ it('collapses burst dispatches to one trailing `input` when throttle>0', async () => {
162
+ const el = mount('<input-ui throttle="20"></input-ui>');
163
+ await tick();
164
+ const events = [];
165
+ // Filter for host-emitted CustomEvents (have detail.value); surface
166
+ // InputEvents bubble up too but have no detail property.
167
+ el.addEventListener('input', (e) => {
168
+ if (e.detail?.value !== undefined) events.push(e.detail.value);
169
+ });
170
+
171
+ const surface = el.querySelector('[slot="text"]');
172
+ surface.textContent = 'a';
173
+ surface.dispatchEvent(new InputEvent('input', { bubbles: true }));
174
+ surface.textContent = 'ab';
175
+ surface.dispatchEvent(new InputEvent('input', { bubbles: true }));
176
+ surface.textContent = 'abc';
177
+ surface.dispatchEvent(new InputEvent('input', { bubbles: true }));
178
+
179
+ expect(events).toEqual([]);
180
+
181
+ await new Promise((r) => setTimeout(r, 35));
182
+ expect(events).toEqual(['abc']);
183
+ });
184
+
185
+ it('flushPendingInput() fires the pending dispatch synchronously', async () => {
186
+ const el = mount('<input-ui throttle="50"></input-ui>');
187
+ await tick();
188
+ const events = [];
189
+ // Filter for host-emitted CustomEvents (have detail.value); surface
190
+ // InputEvents bubble up too but have no detail property.
191
+ el.addEventListener('input', (e) => {
192
+ if (e.detail?.value !== undefined) events.push(e.detail.value);
193
+ });
194
+
195
+ const surface = el.querySelector('[slot="text"]');
196
+ surface.textContent = 'pending';
197
+ surface.dispatchEvent(new InputEvent('input', { bubbles: true }));
198
+
199
+ expect(events).toEqual([]);
200
+ el.flushPendingInput();
201
+ expect(events).toEqual(['pending']);
202
+ });
203
+
204
+ it('dropPendingInput() prevents the dispatch (used by disconnected)', async () => {
205
+ const el = mount('<input-ui throttle="20"></input-ui>');
206
+ await tick();
207
+ const events = [];
208
+ // Filter for host-emitted CustomEvents (have detail.value); surface
209
+ // InputEvents bubble up too but have no detail property.
210
+ el.addEventListener('input', (e) => {
211
+ if (e.detail?.value !== undefined) events.push(e.detail.value);
212
+ });
213
+
214
+ const surface = el.querySelector('[slot="text"]');
215
+ surface.textContent = 'gone';
216
+ surface.dispatchEvent(new InputEvent('input', { bubbles: true }));
217
+
218
+ el.dropPendingInput();
219
+ await new Promise((r) => setTimeout(r, 30));
220
+ expect(events).toEqual([]);
221
+ });
222
+ });
@@ -139,6 +139,20 @@ props:
139
139
  description: Suffix text rendered after the text surface (e.g., unit like 'kg')
140
140
  type: string
141
141
  default: ''
142
+ throttle:
143
+ description: |-
144
+ §220 (v0.5.9, FEEDBACK-14 §3). Trailing-debounce on the `input`
145
+ event in milliseconds. When > 0, value mutates immediately + the UI
146
+ stays responsive, but `input` dispatch is collapsed so only the
147
+ final value in the throttle window emits. Useful for expensive
148
+ `input`-driven computation (palette regen, large list filter,
149
+ server-side autocomplete). `change` fires unthrottled on blur /
150
+ Enter / stepper commit; any pending `input` flushes before `change`
151
+ so consumers see input→input→…→input→change ordering. Default 0
152
+ preserves synchronous emission.
153
+ type: number
154
+ default: 0
155
+ reflect: true
142
156
  value:
143
157
  description: Current input value, synced with contenteditable text surface. For `type="number"`, this is the formatted
144
158
  numeric string; read `el.valueAsNumber` for the parsed Number.
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/inspector
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -4,7 +4,7 @@
4
4
  * @see https://ui-kit.exe.xyz/site/components/kbd
5
5
  *
6
6
  * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
8
  * run `npm run build:components`, then `npm run codegen:dts` to
9
9
  * regenerate; or hand-author this file fully if rich event types are
10
10
  * needed beyond what the yaml `events:` block can express.
@@ -28,7 +28,7 @@ wiring. ARIA role is "link" (set automatically by `<a>` element).
28
28
  * @see https://ui-kit.exe.xyz/site/components/link
29
29
  *
30
30
  * Type declarations generated by scripts/build/dts-codegen.mjs from
31
- * the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
31
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
32
32
  * run `npm run build:components`, then `npm run codegen:dts` to
33
33
  * regenerate; or hand-author this file fully if rich event types are
34
34
  * needed beyond what the yaml `events:` block can express.