@adia-ai/web-components 0.0.26 → 0.0.28

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 (60) hide show
  1. package/components/agent-artifact/agent-artifact.a2ui.json +1 -1
  2. package/components/agent-artifact/agent-artifact.css +11 -0
  3. package/components/agent-artifact/agent-artifact.js +23 -2
  4. package/components/agent-artifact/agent-artifact.yaml +1 -1
  5. package/components/agent-questions/agent-questions.css +20 -1
  6. package/components/agent-reasoning/agent-reasoning.css +11 -0
  7. package/components/agent-reasoning/agent-reasoning.js +16 -0
  8. package/components/agent-trace/agent-trace.css +36 -12
  9. package/components/alert/alert.a2ui.json +10 -4
  10. package/components/alert/alert.css +13 -0
  11. package/components/alert/alert.js +1 -1
  12. package/components/alert/alert.yaml +21 -4
  13. package/components/badge/badge.a2ui.json +0 -2
  14. package/components/badge/badge.css +20 -0
  15. package/components/badge/badge.js +10 -2
  16. package/components/badge/badge.yaml +0 -2
  17. package/components/breadcrumb/breadcrumb.a2ui.json +16 -1
  18. package/components/breadcrumb/breadcrumb.css +27 -0
  19. package/components/breadcrumb/breadcrumb.js +95 -17
  20. package/components/breadcrumb/breadcrumb.yaml +15 -1
  21. package/components/calendar-picker/calendar-picker.css +17 -0
  22. package/components/chart/chart.css +20 -13
  23. package/components/chart/chart.js +49 -17
  24. package/components/chart-legend/chart-legend.css +30 -54
  25. package/components/chart-legend/chart-legend.js +48 -30
  26. package/components/code/code.css +41 -0
  27. package/components/code/code.js +44 -3
  28. package/components/command/command.js +52 -1
  29. package/components/empty-state/empty-state.js +32 -21
  30. package/components/feed/feed-item.yaml +50 -0
  31. package/components/feed/feed.a2ui.json +59 -0
  32. package/components/feed/feed.css +141 -0
  33. package/components/feed/feed.js +276 -0
  34. package/components/feed/feed.yaml +33 -0
  35. package/components/index.js +2 -0
  36. package/components/list/list.js +20 -16
  37. package/components/menu/menu.css +18 -0
  38. package/components/menu/menu.js +24 -10
  39. package/components/pane/pane.css +5 -0
  40. package/components/pipeline-status/pipeline-status.css +15 -1
  41. package/components/popover/popover.css +17 -0
  42. package/components/select/select.css +18 -0
  43. package/components/swatch/swatch.a2ui.json +116 -0
  44. package/components/swatch/swatch.css +141 -0
  45. package/components/swatch/swatch.js +121 -0
  46. package/components/swatch/swatch.yaml +101 -0
  47. package/components/swiper/swiper.css +9 -0
  48. package/components/table/table.css +5 -0
  49. package/components/table/table.js +45 -1
  50. package/components/table-toolbar/table-toolbar.css +13 -0
  51. package/components/tag/tag.css +10 -0
  52. package/components/timeline/timeline.css +15 -4
  53. package/components/toast/toast.css +93 -48
  54. package/components/toast/toast.js +101 -22
  55. package/components/toolbar/toolbar.css +13 -0
  56. package/components/tooltip/tooltip.css +11 -3
  57. package/core/provider.js +1 -0
  58. package/package.json +1 -1
  59. package/styles/colors/semantics.css +1 -1
  60. package/styles/components.css +1 -0
@@ -0,0 +1,116 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/Swatch.json",
4
+ "title": "Swatch",
5
+ "description": "Color/style swatch primitive — a small inline tile representing a single\nvisual sample. Renders as block, dot, square, line, or dashed depending on\nshape; pairs with an optional label slot or attribute. Composed by chart-\nlegend-ui's per-row swatch and consumed directly by the token-colors /\nspacing demo pages.\n",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "color": {
17
+ "description": "Swatch color. Accepts any CSS color value or var() reference. Sets the internal --swatch-color custom property; consumers can also set --swatch-color via inline style.",
18
+ "type": "string",
19
+ "default": ""
20
+ },
21
+ "component": {
22
+ "const": "Swatch"
23
+ },
24
+ "label": {
25
+ "description": "Optional label rendered next to (or below, for shape=\"block\") the swatch. Use the default slot for richer content.",
26
+ "type": "string",
27
+ "default": ""
28
+ },
29
+ "shape": {
30
+ "description": "Visual shape — block (filled tile), dot (small circle), square (small filled square), line (solid hairline), dashed (dashed hairline).",
31
+ "type": "string",
32
+ "enum": [
33
+ "block",
34
+ "dot",
35
+ "square",
36
+ "line",
37
+ "dashed"
38
+ ],
39
+ "default": "square"
40
+ },
41
+ "size": {
42
+ "description": "Size preset — sm / md / lg. Drives the swatch's intrinsic dimensions; lg is reserved for the token-scale demo (40 px tall).",
43
+ "type": "string",
44
+ "enum": [
45
+ "sm",
46
+ "md",
47
+ "lg"
48
+ ],
49
+ "default": "md"
50
+ }
51
+ },
52
+ "required": [
53
+ "component"
54
+ ],
55
+ "unevaluatedProperties": false,
56
+ "x-adiaui": {
57
+ "anti_patterns": [],
58
+ "category": "display",
59
+ "events": {},
60
+ "examples": [
61
+ {
62
+ "description": "A single dot swatch with text label, e.g. as a chart-legend row.",
63
+ "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Swatch\",\n \"shape\": \"dot\",\n \"color\": \"var(--a-data-0)\",\n \"label\": \"Revenue\"\n }\n]",
64
+ "name": "basic-swatch"
65
+ },
66
+ {
67
+ "description": "Block swatch for a token-scale step.",
68
+ "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Swatch\",\n \"shape\": \"block\",\n \"size\": \"lg\",\n \"color\": \"var(--a-neutral-50)\",\n \"label\": \"50\"\n }\n]",
69
+ "name": "token-block"
70
+ }
71
+ ],
72
+ "keywords": [
73
+ "swatch",
74
+ "color",
75
+ "chip",
76
+ "sample",
77
+ "palette",
78
+ "legend",
79
+ "step",
80
+ "token"
81
+ ],
82
+ "name": "AdiaSwatch",
83
+ "related": [
84
+ "chart-legend-ui",
85
+ "tag-ui"
86
+ ],
87
+ "slots": {
88
+ "default": {
89
+ "description": "Optional rich label content. When present, replaces the [label] attribute's text."
90
+ }
91
+ },
92
+ "states": [
93
+ {
94
+ "description": "Default, ready for display.",
95
+ "name": "idle"
96
+ }
97
+ ],
98
+ "synonyms": {
99
+ "color-chip": [
100
+ "swatch",
101
+ "chip"
102
+ ],
103
+ "color-sample": [
104
+ "swatch"
105
+ ]
106
+ },
107
+ "tag": "swatch-ui",
108
+ "tokens": {
109
+ "--swatch-color": {
110
+ "description": "Resolved color of the swatch fill / line. Wins over the [color] attr if set inline."
111
+ }
112
+ },
113
+ "traits": [],
114
+ "version": 1
115
+ }
116
+ }
@@ -0,0 +1,141 @@
1
+ @scope (swatch-ui) {
2
+ :where(:scope) {
3
+ /* ── Layout ── */
4
+ --swatch-gap: var(--a-space-2);
5
+
6
+ /* ── Sizes ──
7
+ The "tile" is the coloured shape; the label is plain text rendered
8
+ beside (or below, for shape="block") the tile. Tile dimensions are
9
+ size-driven; line shapes use a fixed thickness regardless of size. */
10
+ --swatch-tile-sm: var(--a-space-2); /* 8px ~ */
11
+ --swatch-tile-md: var(--a-space-2-5); /* 10px ~ */
12
+ --swatch-tile-lg: 40px; /* token-scale block */
13
+ --swatch-line-thickness: 2px;
14
+ --swatch-line-length: calc(var(--swatch-tile-md) * 1.75);
15
+
16
+ /* ── Colors ──
17
+ Default colour falls back through the data palette so a bare
18
+ <swatch-ui> still renders something visible; consumers usually set
19
+ --swatch-color via [color] attr or inline style. */
20
+ --swatch-color: var(--a-data-0);
21
+ --swatch-label-fg: var(--a-fg-subtle);
22
+
23
+ /* ── Shape ── */
24
+ --swatch-radius-block: var(--a-radius-sm);
25
+ --swatch-radius-square: var(--a-radius-sm);
26
+
27
+ /* ── Block-shape extras (token-scale) ── */
28
+ --swatch-block-shadow: inset 0 0 0 1px color-mix(in oklab, currentColor 8%, transparent);
29
+
30
+ /* ── Typography ── */
31
+ --swatch-label-size: var(--a-ui-size);
32
+ --swatch-label-step-size: 10px;
33
+ --swatch-label-step-mt: 4px;
34
+ }
35
+
36
+ :scope {
37
+ /* Default: inline-flex row — tile + label side-by-side, vertically
38
+ centered. shape="block" overrides to flex-column (label sits below
39
+ the tile, mimicking the token-scale step layout). */
40
+ box-sizing: border-box;
41
+ display: inline-flex;
42
+ align-items: center;
43
+ gap: var(--swatch-gap);
44
+ min-width: 0;
45
+ color: var(--swatch-label-fg);
46
+ font-size: var(--swatch-label-size);
47
+ line-height: 1.2;
48
+ vertical-align: baseline;
49
+ }
50
+
51
+ /* The colored tile — sized by [size] + [shape] combinations below. */
52
+ :scope > [data-tile] {
53
+ box-sizing: border-box;
54
+ display: block;
55
+ flex-shrink: 0;
56
+ background: var(--swatch-color);
57
+ line-height: 0;
58
+ }
59
+
60
+ :scope > [data-label] {
61
+ min-width: 0;
62
+ overflow: hidden;
63
+ text-overflow: ellipsis;
64
+ white-space: nowrap;
65
+ }
66
+
67
+ :scope > [data-label][hidden] { display: none; }
68
+
69
+ /* ═══════ Shape: dot ═══════ */
70
+ :scope[shape="dot"] > [data-tile] {
71
+ width: var(--_size, var(--swatch-tile-md));
72
+ height: var(--_size, var(--swatch-tile-md));
73
+ border-radius: 50%;
74
+ }
75
+
76
+ /* ═══════ Shape: square ═══════ */
77
+ :scope[shape="square"] > [data-tile] {
78
+ width: var(--_size, var(--swatch-tile-md));
79
+ height: var(--_size, var(--swatch-tile-md));
80
+ border-radius: var(--swatch-radius-square);
81
+ }
82
+
83
+ /* ═══════ Shape: line (solid hairline) ═══════ */
84
+ :scope[shape="line"] > [data-tile] {
85
+ width: var(--swatch-line-length);
86
+ height: var(--swatch-line-thickness);
87
+ border-radius: var(--swatch-line-thickness);
88
+ }
89
+
90
+ /* ═══════ Shape: dashed (dashed hairline) ═══════ */
91
+ :scope[shape="dashed"] > [data-tile] {
92
+ width: var(--swatch-line-length);
93
+ height: var(--swatch-line-thickness);
94
+ background: transparent;
95
+ border-top: var(--swatch-line-thickness) dashed var(--swatch-color);
96
+ }
97
+
98
+ /* ═══════ Shape: block (token-scale tile) ═══════
99
+ Filled rectangle whose height grows with [size]; label sits BELOW
100
+ the tile (flex-column) so we can use it as a token-step swatch
101
+ without any extra wrapper. */
102
+ :scope[shape="block"] {
103
+ flex-direction: column;
104
+ align-items: stretch;
105
+ gap: 0;
106
+ }
107
+
108
+ :scope[shape="block"] > [data-tile] {
109
+ width: 100%;
110
+ height: var(--_block-h, var(--swatch-tile-lg));
111
+ border-radius: var(--swatch-radius-block);
112
+ box-shadow: var(--swatch-block-shadow);
113
+ }
114
+
115
+ :scope[shape="block"] > [data-label] {
116
+ text-align: center;
117
+ font-family: var(--a-font-family-code);
118
+ font-size: var(--swatch-label-step-size);
119
+ font-variant-numeric: tabular-nums;
120
+ color: var(--a-fg-muted);
121
+ margin-block-start: var(--swatch-label-step-mt);
122
+ white-space: nowrap;
123
+ }
124
+
125
+ /* ═══════ Sizes ═══════
126
+ Override the dot/square diameter (`--_size`) and the block-shape
127
+ height (`--_block-h`) for each size preset. Line/dashed widths use
128
+ --swatch-line-length so they stay consistent at all sizes. */
129
+ :scope[size="sm"] {
130
+ --_size: var(--swatch-tile-sm);
131
+ --_block-h: calc(var(--swatch-tile-lg) * 0.6);
132
+ }
133
+ :scope[size="md"] {
134
+ --_size: var(--swatch-tile-md);
135
+ --_block-h: calc(var(--swatch-tile-lg) * 0.75);
136
+ }
137
+ :scope[size="lg"] {
138
+ --_size: calc(var(--swatch-tile-md) * 1.4);
139
+ --_block-h: var(--swatch-tile-lg);
140
+ }
141
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * <swatch-ui> — color / style sample primitive.
3
+ *
4
+ * A small inline tile representing one visual sample — a chart series's
5
+ * legend marker, a design-token step in a color or spacing scale, a
6
+ * theme-preview pill. Replaces docs-only `<span data-swatch>` usage and
7
+ * the per-shape CSS that used to live inside chart-legend-ui.
8
+ *
9
+ * Usage:
10
+ * <swatch-ui shape="dot" color="var(--a-data-0)" label="Revenue"></swatch-ui>
11
+ * <swatch-ui shape="block" size="lg" color="var(--a-neutral-50)" label="50"></swatch-ui>
12
+ * <swatch-ui shape="line" color="#0ea5e9">Forecast</swatch-ui>
13
+ *
14
+ * Attributes:
15
+ * shape — block | dot | square | line | dashed (default square)
16
+ * size — sm | md | lg (default md). lg is the 40px-tall token-scale block.
17
+ * color — any CSS color (or var() ref). Sets --swatch-color internally.
18
+ * label — optional text label rendered beside / below the swatch
19
+ *
20
+ * Slots:
21
+ * default — rich label content; takes precedence over [label] attr
22
+ *
23
+ * Stamping is light-DOM with two children:
24
+ * <span data-tile></span> — the colored shape
25
+ * <span data-label></span> — the label text (omitted when no label/slot)
26
+ */
27
+
28
+ import { AdiaElement } from '../../core/element.js';
29
+
30
+ const SHAPES = new Set(['block', 'dot', 'square', 'line', 'dashed']);
31
+ const SIZES = new Set(['sm', 'md', 'lg']);
32
+
33
+ class AdiaSwatch extends AdiaElement {
34
+ static properties = {
35
+ shape: { type: String, default: 'square', reflect: true },
36
+ size: { type: String, default: 'md', reflect: true },
37
+ color: { type: String, default: '', reflect: true },
38
+ label: { type: String, default: '', reflect: true },
39
+ };
40
+
41
+ static template = () => null;
42
+
43
+ #tileEl = null;
44
+ #labelEl = null;
45
+ #stamped = false;
46
+
47
+ connected() {
48
+ this.#stamp();
49
+ }
50
+
51
+ render() {
52
+ this.#stamp();
53
+ this.#sync();
54
+ }
55
+
56
+ #stamp() {
57
+ if (this.#stamped) return;
58
+ // Capture pre-existing default-slot content so consumer-authored
59
+ // children (e.g. <swatch-ui>Forecast</swatch-ui>) survive stamping.
60
+ const slotted = Array.from(this.childNodes).filter(n =>
61
+ !(n.nodeType === 1 && (n.dataset?.tile !== undefined || n.dataset?.label !== undefined))
62
+ );
63
+ this.innerHTML = '';
64
+
65
+ this.#tileEl = document.createElement('span');
66
+ this.#tileEl.setAttribute('data-tile', '');
67
+ this.#tileEl.setAttribute('aria-hidden', 'true');
68
+ this.appendChild(this.#tileEl);
69
+
70
+ this.#labelEl = document.createElement('span');
71
+ this.#labelEl.setAttribute('data-label', '');
72
+ if (slotted.length) {
73
+ for (const n of slotted) this.#labelEl.appendChild(n);
74
+ }
75
+ this.appendChild(this.#labelEl);
76
+
77
+ this.#stamped = true;
78
+ }
79
+
80
+ #sync() {
81
+ if (!this.#tileEl || !this.#labelEl) return;
82
+
83
+ // Normalize enum values; fall back silently when a consumer sets a typo.
84
+ const shape = SHAPES.has(this.shape) ? this.shape : 'square';
85
+ const size = SIZES.has(this.size) ? this.size : 'md';
86
+ if (shape !== this.shape) this.setAttribute('shape', shape);
87
+ if (size !== this.size) this.setAttribute('size', size);
88
+
89
+ // Color: only write the inline custom property when [color] is set
90
+ // explicitly; otherwise leave whatever the consumer wrote via
91
+ // style="--swatch-color: …" alone.
92
+ if (this.color) {
93
+ this.style.setProperty('--swatch-color', this.color);
94
+ }
95
+
96
+ // Label: prefer slotted content (already in #labelEl from #stamp);
97
+ // when label-attr is set and the slot is empty, write the attribute.
98
+ if (!this.#labelEl.firstChild && this.label) {
99
+ this.#labelEl.textContent = this.label;
100
+ } else if (this.#labelEl.firstChild?.nodeType === 3 && this.label) {
101
+ // Pure-text node from a previous label-attr render — keep it
102
+ // synced to the current attr value.
103
+ this.#labelEl.textContent = this.label;
104
+ }
105
+
106
+ // Hide the label box when there's no content. Empty-but-stamped span
107
+ // would otherwise eat the layout gap.
108
+ const hasLabel = this.#labelEl.textContent.trim().length > 0
109
+ || (this.#labelEl.firstElementChild != null);
110
+ this.#labelEl.toggleAttribute('hidden', !hasLabel);
111
+ }
112
+
113
+ disconnected() {
114
+ this.#tileEl = null;
115
+ this.#labelEl = null;
116
+ this.#stamped = false;
117
+ }
118
+ }
119
+
120
+ customElements.define('swatch-ui', AdiaSwatch);
121
+ export { AdiaSwatch };
@@ -0,0 +1,101 @@
1
+ $schema: ../../../../scripts/schemas/component.yaml.schema.json
2
+ name: AdiaSwatch
3
+ tag: swatch-ui
4
+ component: Swatch
5
+ category: display
6
+ version: 1
7
+ description: |
8
+ Color/style swatch primitive — a small inline tile representing a single
9
+ visual sample. Renders as block, dot, square, line, or dashed depending on
10
+ shape; pairs with an optional label slot or attribute. Composed by chart-
11
+ legend-ui's per-row swatch and consumed directly by the token-colors /
12
+ spacing demo pages.
13
+ props:
14
+ shape:
15
+ description: Visual shape — block (filled tile), dot (small circle), square (small filled square), line (solid hairline), dashed (dashed hairline).
16
+ type: string
17
+ default: square
18
+ reflect: true
19
+ enum:
20
+ - block
21
+ - dot
22
+ - square
23
+ - line
24
+ - dashed
25
+ size:
26
+ description: Size preset — sm / md / lg. Drives the swatch's intrinsic dimensions; lg is reserved for the token-scale demo (40 px tall).
27
+ type: string
28
+ default: md
29
+ reflect: true
30
+ enum:
31
+ - sm
32
+ - md
33
+ - lg
34
+ color:
35
+ description: Swatch color. Accepts any CSS color value or var() reference. Sets the internal --swatch-color custom property; consumers can also set --swatch-color via inline style.
36
+ type: string
37
+ default: ""
38
+ reflect: true
39
+ label:
40
+ description: Optional label rendered next to (or below, for shape="block") the swatch. Use the default slot for richer content.
41
+ type: string
42
+ default: ""
43
+ reflect: true
44
+ events: {}
45
+ slots:
46
+ default:
47
+ description: Optional rich label content. When present, replaces the [label] attribute's text.
48
+ states:
49
+ - name: idle
50
+ description: Default, ready for display.
51
+ traits: []
52
+ tokens:
53
+ --swatch-color:
54
+ description: Resolved color of the swatch fill / line. Wins over the [color] attr if set inline.
55
+ a2ui:
56
+ rules: []
57
+ anti_patterns: []
58
+ examples:
59
+ - name: basic-swatch
60
+ description: A single dot swatch with text label, e.g. as a chart-legend row.
61
+ a2ui: >-
62
+ [
63
+ {
64
+ "id": "root",
65
+ "component": "Swatch",
66
+ "shape": "dot",
67
+ "color": "var(--a-data-0)",
68
+ "label": "Revenue"
69
+ }
70
+ ]
71
+ - name: token-block
72
+ description: Block swatch for a token-scale step.
73
+ a2ui: >-
74
+ [
75
+ {
76
+ "id": "root",
77
+ "component": "Swatch",
78
+ "shape": "block",
79
+ "size": "lg",
80
+ "color": "var(--a-neutral-50)",
81
+ "label": "50"
82
+ }
83
+ ]
84
+ keywords:
85
+ - swatch
86
+ - color
87
+ - chip
88
+ - sample
89
+ - palette
90
+ - legend
91
+ - step
92
+ - token
93
+ synonyms:
94
+ color-chip:
95
+ - swatch
96
+ - chip
97
+ color-sample:
98
+ - swatch
99
+ related:
100
+ - chart-legend-ui
101
+ - tag-ui
@@ -188,6 +188,15 @@
188
188
  transform var(--swiper-duration) var(--swiper-easing);
189
189
  }
190
190
 
191
+ :scope > [data-swiper-dots] > button:hover {
192
+ background: var(--swiper-dot-bg-active);
193
+ }
194
+
195
+ :scope > [data-swiper-dots] > button:focus-visible {
196
+ outline: none;
197
+ box-shadow: var(--a-focus-ring);
198
+ }
199
+
191
200
  :scope > [data-swiper-dots] > button[aria-current="true"] {
192
201
  background: var(--swiper-dot-bg-active);
193
202
  }
@@ -210,6 +210,9 @@
210
210
 
211
211
  [data-body] [role="gridcell"] {
212
212
  box-sizing: border-box;
213
+ display: flex;
214
+ align-items: center;
215
+ align-self: stretch;
213
216
  padding: var(--table-py) var(--table-px);
214
217
  border-bottom: 1px solid var(--table-border);
215
218
  min-width: 0;
@@ -218,10 +221,12 @@
218
221
 
219
222
  [data-align="right"] {
220
223
  text-align: right;
224
+ justify-content: flex-end;
221
225
  }
222
226
 
223
227
  [data-align="center"] {
224
228
  text-align: center;
229
+ justify-content: center;
225
230
  }
226
231
 
227
232
  /* ═══════ Row states ═══════ */
@@ -129,10 +129,54 @@ class AdiaTable extends AdiaElement {
129
129
 
130
130
  get data() { return this.#data; }
131
131
 
132
- // ── Public API: read-only getters ──
132
+ // ── Public API: selection ──
133
133
 
134
+ /**
135
+ * Indices of currently-selected rows, ascending.
136
+ * @returns {number[]}
137
+ */
134
138
  get selected() { return [...this.#selected].sort((a, b) => a - b); }
135
139
 
140
+ /**
141
+ * Replace the selection set programmatically. Out-of-range indices are
142
+ * silently dropped. Pair with `selectable` mode; on a non-selectable
143
+ * table the indices are stored but not rendered as checked rows.
144
+ * @param {Iterable<number>} indices
145
+ */
146
+ set selected(indices) {
147
+ this.#selected.clear();
148
+ if (indices) {
149
+ for (const i of indices) {
150
+ if (Number.isInteger(i) && i >= 0 && i < this.#data.length) {
151
+ this.#selected.add(i);
152
+ }
153
+ }
154
+ }
155
+ this.#requestRender();
156
+ this.dispatchEvent(new CustomEvent('select', {
157
+ detail: { selected: this.selected },
158
+ bubbles: true,
159
+ }));
160
+ }
161
+
162
+ /**
163
+ * Empty the selection set. Equivalent to `el.selected = []` but doesn't
164
+ * require constructing an empty array; the most common selection-write
165
+ * call by far (after a "delete" or "archive" bulk action), so worth
166
+ * having a one-token verb form.
167
+ */
168
+ clearSelection() {
169
+ if (this.#selected.size === 0) return;
170
+ this.#selected.clear();
171
+ this.#requestRender();
172
+ this.dispatchEvent(new CustomEvent('select', {
173
+ detail: { selected: [] },
174
+ bubbles: true,
175
+ }));
176
+ }
177
+
178
+ // ── Public API: read-only getters ──
179
+
136
180
  get sortState() { return this.#sortState.map(s => ({ ...s })); }
137
181
 
138
182
  // ── Public API: filters ──
@@ -156,6 +156,19 @@
156
156
  display: flex;
157
157
  flex-direction: column;
158
158
  gap: var(--a-space-1);
159
+ /* Fade + lift in on first paint. */
160
+ opacity: 1;
161
+ translate: 0 0;
162
+ transition: opacity var(--a-duration-fast) var(--a-easing-out),
163
+ translate var(--a-duration-fast) var(--a-easing-out);
164
+ @starting-style {
165
+ opacity: 0;
166
+ translate: 0 -4px;
167
+ }
168
+ }
169
+
170
+ @media (prefers-reduced-motion: reduce) {
171
+ [data-toolbar-popover]:popover-open { transition: none; }
159
172
  }
160
173
 
161
174
  /* The popover head + empty hint are <text-ui variant="kicker"|caption">,
@@ -79,6 +79,16 @@ tag-ui[removable]:not([disabled]):hover {
79
79
  --tag-fg: var(--a-danger-bg);
80
80
  }
81
81
 
82
+ /* `default` is a semantic alias of the base — same tokens as the
83
+ unstyled `:scope`, declared explicitly so the yaml enum and the
84
+ CSS contract agree. Most consumers omit `variant` and inherit the
85
+ base; the explicit selector lets `<tag-ui variant="default">`
86
+ render identically without falling through to base. */
87
+ :scope[variant="default"] {
88
+ --tag-bg: var(--a-bg-muted);
89
+ --tag-fg: var(--a-fg);
90
+ }
91
+
82
92
  /* Size handled by universal [size] attribute system. */
83
93
 
84
94
  /* hover rule moved outside @scope — see Safari 17.x bug note at top. */
@@ -347,17 +347,29 @@ agent-reasoning-ui timeline-ui:not([orientation="horizontal"]),
347
347
  :scope > [data-timeline-toggle] {
348
348
  position: absolute;
349
349
  inset-inline-end: 0;
350
- top: calc((1.4em - 1em) / 2);
350
+ /* Place the chevron's icon-center on row 1's optical center.
351
+ The button has --a-space-0-5 vertical padding, so the icon
352
+ sits that much lower inside the box; we subtract that so the
353
+ icon (not the box) lines up with the time-slot text center. */
354
+ top: calc((1.4em - 1em) / 2 - var(--a-space-0-5));
351
355
  background: none;
352
356
  border: none;
353
- padding: 0 var(--timeline-item-toggle-px);
357
+ padding: var(--a-space-0-5) var(--timeline-item-toggle-px);
354
358
  margin: 0;
355
359
  cursor: pointer;
356
- color: inherit;
360
+ color: var(--timeline-item-label-fg-muted, var(--a-fg-muted));
361
+ border-radius: var(--a-radius-sm);
357
362
  display: inline-flex;
358
363
  align-items: center;
359
364
  justify-content: center;
360
365
  line-height: 0;
366
+ transition: background var(--timeline-item-duration) var(--timeline-item-easing),
367
+ color var(--timeline-item-duration) var(--timeline-item-easing);
368
+ }
369
+
370
+ :scope > [data-timeline-toggle]:hover {
371
+ background: var(--a-bg-subtle);
372
+ color: var(--timeline-item-label-fg, var(--a-fg));
361
373
  }
362
374
 
363
375
  /* Reserve room so the time isn't overlapped by the chevron */
@@ -368,7 +380,6 @@ agent-reasoning-ui timeline-ui:not([orientation="horizontal"]),
368
380
  :scope > [data-timeline-toggle]:focus-visible {
369
381
  outline: none;
370
382
  box-shadow: var(--a-focus-ring);
371
- border-radius: var(--a-radius-sm);
372
383
  }
373
384
  }
374
385