@adia-ai/web-components 0.0.18 → 0.0.20
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.
- package/components/accordion/accordion.css +101 -102
- package/components/agent-feedback-bar/agent-feedback-bar.js +8 -8
- package/components/agent-questions/agent-questions.css +2 -1
- package/components/agent-questions/agent-questions.js +6 -6
- package/components/agent-reasoning/agent-reasoning.js +20 -5
- package/components/agent-trace/agent-trace.a2ui.json +5 -5
- package/components/agent-trace/agent-trace.js +7 -5
- package/components/agent-trace/agent-trace.yaml +2 -2
- package/components/alert/alert.a2ui.json +1 -2
- package/components/alert/alert.css +4 -4
- package/components/alert/alert.yaml +1 -2
- package/components/avatar/avatar.a2ui.json +3 -3
- package/components/avatar/avatar.js +10 -0
- package/components/avatar/avatar.yaml +6 -6
- package/components/button/button.a2ui.json +14 -2
- package/components/button/button.css +19 -2
- package/components/button/button.js +1 -0
- package/components/button/button.yaml +20 -2
- package/components/calendar-picker/calendar-picker.css +2 -1
- package/components/calendar-picker/calendar-picker.js +12 -1
- package/components/chart/chart.css +11 -11
- package/components/chart/chart.js +26 -18
- package/components/chart-legend/chart-legend.a2ui.json +2 -2
- package/components/chart-legend/chart-legend.js +4 -1
- package/components/chart-legend/chart-legend.yaml +2 -2
- package/components/chat/chat-input.js +13 -5
- package/components/chat/chat.a2ui.json +2 -2
- package/components/chat/chat.js +14 -3
- package/components/chat/chat.yaml +2 -2
- package/components/code/code.css +16 -6
- package/components/command/command.js +9 -1
- package/components/field/field.a2ui.json +0 -5
- package/components/field/field.css +2 -2
- package/components/field/field.js +53 -5
- package/components/field/field.yaml +5 -8
- package/components/heatmap/heatmap.css +32 -23
- package/components/input/input.js +30 -1
- package/components/kbd/kbd.a2ui.json +5 -1
- package/components/kbd/kbd.yaml +5 -1
- package/components/menu/menu.css +20 -8
- package/components/menu/menu.js +9 -1
- package/components/modal/modal.css +101 -108
- package/components/noodles/noodles.js +25 -8
- package/components/pipeline-status/pipeline-status.css +4 -4
- package/components/pipeline-status/pipeline-status.js +6 -4
- package/components/popover/popover.js +4 -0
- package/components/progress-row/progress-row.a2ui.json +3 -2
- package/components/progress-row/progress-row.yaml +2 -1
- package/components/range/range.js +7 -0
- package/components/richtext/richtext.css +2 -2
- package/components/richtext/richtext.js +4 -1
- package/components/segment/segment.css +1 -1
- package/components/segmented/segmented.js +7 -1
- package/components/select/select.css +7 -4
- package/components/slider/slider.js +15 -8
- package/components/stepper/stepper.css +181 -144
- package/components/stepper/stepper.js +5 -2
- package/components/swiper/swiper.a2ui.json +3 -3
- package/components/swiper/swiper.css +11 -77
- package/components/swiper/swiper.js +6 -5
- package/components/swiper/swiper.yaml +3 -3
- package/components/switch/switch.a2ui.json +8 -1
- package/components/switch/switch.yaml +8 -1
- package/components/table/table.js +9 -1
- package/components/table-toolbar/table-toolbar.a2ui.json +21 -21
- package/components/table-toolbar/table-toolbar.css +32 -91
- package/components/table-toolbar/table-toolbar.js +219 -86
- package/components/table-toolbar/table-toolbar.yaml +21 -12
- package/components/tabs/tabs.css +3 -2
- package/components/tabs/tabs.js +7 -1
- package/components/tag/tag.a2ui.json +2 -2
- package/components/tag/tag.yaml +2 -2
- package/components/timeline/timeline.css +244 -204
- package/components/timeline/timeline.js +1 -3
- package/components/toast/toast.a2ui.json +2 -3
- package/components/toast/toast.yaml +5 -3
- package/components/toolbar/toolbar.css +6 -1
- package/components/toolbar/toolbar.js +10 -2
- package/components/tooltip/tooltip.css +8 -2
- package/components/tooltip/tooltip.js +12 -14
- package/components/tree/tree.css +21 -0
- package/core/icons.js +14 -0
- package/core/polyfills.js +17 -7
- package/package.json +1 -1
- package/patterns/a2ui-root/a2ui-root.js +21 -14
- package/patterns/app-shell/css/app-shell.main.css +30 -1
- package/patterns/app-shell/css/app-shell.tokens.css +1 -0
- package/patterns/gen-ui/gen-ui.js +1 -1
- package/styles/colors/semantics.css +59 -2
- package/styles/tokens.css +16 -12
|
@@ -114,10 +114,27 @@
|
|
|
114
114
|
--button-border: var(--button-border-ghost);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
/* ── Color (semantic axis) ──
|
|
118
|
+
`[color]` carries semantic intent; `variant` carries visual style.
|
|
119
|
+
Together they compose: `<button-ui variant="solid" color="danger">`
|
|
120
|
+
vs `<button-ui variant="outline" color="danger">`.
|
|
121
|
+
`variant="danger"` is a deprecated alias migrated to color via JS. */
|
|
122
|
+
:scope[color="danger"] {
|
|
118
123
|
--button-bg: var(--button-bg-danger);
|
|
119
124
|
--button-fg: var(--button-fg-danger);
|
|
120
125
|
}
|
|
126
|
+
:scope[color="success"] {
|
|
127
|
+
--button-bg: var(--a-success-strong);
|
|
128
|
+
--button-fg: var(--a-success-fg);
|
|
129
|
+
}
|
|
130
|
+
:scope[color="info"] {
|
|
131
|
+
--button-bg: var(--a-info-strong);
|
|
132
|
+
--button-fg: var(--a-info-fg);
|
|
133
|
+
}
|
|
134
|
+
:scope[color="warning"] {
|
|
135
|
+
--button-bg: var(--a-warning-strong);
|
|
136
|
+
--button-fg: var(--a-warning-fg);
|
|
137
|
+
}
|
|
121
138
|
|
|
122
139
|
/* ── Hover (after variants so it wins on specificity via :hover) ── */
|
|
123
140
|
:scope:not([disabled]):hover,
|
|
@@ -138,7 +155,7 @@
|
|
|
138
155
|
--button-border: var(--button-border-hover);
|
|
139
156
|
}
|
|
140
157
|
|
|
141
|
-
:scope[
|
|
158
|
+
:scope[color="danger"]:not([disabled]):hover {
|
|
142
159
|
--button-bg: var(--button-bg-hover);
|
|
143
160
|
--button-fg: var(--button-fg-hover);
|
|
144
161
|
--button-border: var(--button-border-hover);
|
|
@@ -5,6 +5,7 @@ class AdiaButton extends AdiaElement {
|
|
|
5
5
|
static properties = {
|
|
6
6
|
text: { type: String, default: '', reflect: true },
|
|
7
7
|
variant: { type: String, default: 'solid', reflect: true },
|
|
8
|
+
color: { type: String, default: '', reflect: true },
|
|
8
9
|
size: { type: String, default: 'md', reflect: true },
|
|
9
10
|
disabled: { type: Boolean, default: false, reflect: true },
|
|
10
11
|
stretch: { type: Boolean, default: false, reflect: true },
|
|
@@ -41,7 +41,11 @@ props:
|
|
|
41
41
|
type: string
|
|
42
42
|
default: ""
|
|
43
43
|
variant:
|
|
44
|
-
description:
|
|
44
|
+
description: >-
|
|
45
|
+
Visual style — `solid` (default fill), `outline`, `ghost`, `link`.
|
|
46
|
+
`default` / `primary` are aliases of `solid`. Style is independent
|
|
47
|
+
of semantic intent — to express destructive / success / info /
|
|
48
|
+
warning intent, set [color="…"] alongside.
|
|
45
49
|
type: string
|
|
46
50
|
default: solid
|
|
47
51
|
enum:
|
|
@@ -49,12 +53,26 @@ props:
|
|
|
49
53
|
- solid
|
|
50
54
|
- outline
|
|
51
55
|
- ghost
|
|
52
|
-
- danger
|
|
53
56
|
- primary
|
|
54
57
|
- secondary
|
|
55
58
|
- soft
|
|
56
59
|
- current
|
|
57
60
|
- link
|
|
61
|
+
color:
|
|
62
|
+
description: >-
|
|
63
|
+
Semantic intent — composes with [variant]. `<button-ui variant="solid" color="danger">`
|
|
64
|
+
= filled destructive action; `<button-ui variant="outline" color="success">`
|
|
65
|
+
= outlined success affordance.
|
|
66
|
+
type: string
|
|
67
|
+
default: ""
|
|
68
|
+
enum:
|
|
69
|
+
- default
|
|
70
|
+
- accent
|
|
71
|
+
- info
|
|
72
|
+
- success
|
|
73
|
+
- warning
|
|
74
|
+
- danger
|
|
75
|
+
reflect: true
|
|
58
76
|
events:
|
|
59
77
|
press:
|
|
60
78
|
description: Fired on complete press (pointer up or Enter/Space)
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
--calendar-picker-day-today-border: var(--a-accent);
|
|
73
73
|
--calendar-picker-day-today-dot-size: var(--a-space-0-5);
|
|
74
74
|
--calendar-picker-day-today-dot-offset: var(--a-space-1);
|
|
75
|
+
--calendar-picker-day-grid-gap: var(--a-space-px); /* 1px hairline between cells */
|
|
75
76
|
--calendar-picker-day-focus-ring: var(--a-focus-ring);
|
|
76
77
|
|
|
77
78
|
/* Transitions */
|
|
@@ -244,7 +245,7 @@ calendar-picker-ui [data-cal-weekdays] span {
|
|
|
244
245
|
calendar-picker-ui [data-cal-grid] {
|
|
245
246
|
display: grid;
|
|
246
247
|
grid-template-columns: repeat(7, 1fr);
|
|
247
|
-
gap:
|
|
248
|
+
gap: var(--calendar-picker-day-grid-gap);
|
|
248
249
|
}
|
|
249
250
|
|
|
250
251
|
/* Day buttons */
|
|
@@ -59,6 +59,7 @@ class AdiaCalendarPicker extends AdiaFormElement {
|
|
|
59
59
|
#viewMonth = new Date().getMonth();
|
|
60
60
|
#focusedDay = null;
|
|
61
61
|
#popoverClickBound = false;
|
|
62
|
+
#openRaf = null;
|
|
62
63
|
|
|
63
64
|
#onPopoverClick = (e) => {
|
|
64
65
|
const target = e.target instanceof Element ? e.target : null;
|
|
@@ -121,12 +122,18 @@ class AdiaCalendarPicker extends AdiaFormElement {
|
|
|
121
122
|
this.#anchorCleanup = anchorPopover(this.#trigger, this.#popover, {
|
|
122
123
|
placement: 'bottom-start', gap: 4,
|
|
123
124
|
});
|
|
124
|
-
requestAnimationFrame(() => {
|
|
125
|
+
this.#openRaf = requestAnimationFrame(() => {
|
|
126
|
+
this.#openRaf = null;
|
|
127
|
+
if (!this.isConnected || !this.open) return;
|
|
125
128
|
document.addEventListener('pointerdown', this.#onOutside, { once: true });
|
|
126
129
|
});
|
|
127
130
|
} else {
|
|
128
131
|
this.#anchorCleanup?.();
|
|
129
132
|
this.#anchorCleanup = null;
|
|
133
|
+
if (this.#openRaf != null) {
|
|
134
|
+
cancelAnimationFrame(this.#openRaf);
|
|
135
|
+
this.#openRaf = null;
|
|
136
|
+
}
|
|
130
137
|
this.#popover?.hidePopover?.();
|
|
131
138
|
document.removeEventListener('pointerdown', this.#onOutside);
|
|
132
139
|
}
|
|
@@ -311,6 +318,10 @@ class AdiaCalendarPicker extends AdiaFormElement {
|
|
|
311
318
|
|
|
312
319
|
disconnected() {
|
|
313
320
|
super.disconnected();
|
|
321
|
+
if (this.#openRaf != null) {
|
|
322
|
+
cancelAnimationFrame(this.#openRaf);
|
|
323
|
+
this.#openRaf = null;
|
|
324
|
+
}
|
|
314
325
|
this.removeEventListener('click', this.#onClick);
|
|
315
326
|
this.removeEventListener('keydown', this.#onKey);
|
|
316
327
|
document.removeEventListener('pointerdown', this.#onOutside);
|
|
@@ -630,24 +630,24 @@
|
|
|
630
630
|
/* ═══════ Color variants ═══════ */
|
|
631
631
|
|
|
632
632
|
:scope[color="success"] {
|
|
633
|
-
--chart-bar: var(--a-success);
|
|
633
|
+
--chart-bar: var(--a-success-strong);
|
|
634
634
|
--chart-bar-hover: var(--a-success-bg-hover);
|
|
635
|
-
--chart-line: var(--a-success);
|
|
636
|
-
--chart-dot: var(--a-success);
|
|
635
|
+
--chart-line: var(--a-success-strong);
|
|
636
|
+
--chart-dot: var(--a-success-strong);
|
|
637
637
|
}
|
|
638
638
|
|
|
639
639
|
:scope[color="warning"] {
|
|
640
|
-
--chart-bar: var(--a-warning);
|
|
640
|
+
--chart-bar: var(--a-warning-strong);
|
|
641
641
|
--chart-bar-hover: var(--a-warning-bg-hover);
|
|
642
|
-
--chart-line: var(--a-warning);
|
|
643
|
-
--chart-dot: var(--a-warning);
|
|
642
|
+
--chart-line: var(--a-warning-strong);
|
|
643
|
+
--chart-dot: var(--a-warning-strong);
|
|
644
644
|
}
|
|
645
645
|
|
|
646
646
|
:scope[color="danger"] {
|
|
647
|
-
--chart-bar: var(--a-danger);
|
|
647
|
+
--chart-bar: var(--a-danger-strong);
|
|
648
648
|
--chart-bar-hover: var(--a-danger-bg-hover);
|
|
649
|
-
--chart-line: var(--a-danger);
|
|
650
|
-
--chart-dot: var(--a-danger);
|
|
649
|
+
--chart-line: var(--a-danger-strong);
|
|
650
|
+
--chart-dot: var(--a-danger-strong);
|
|
651
651
|
}
|
|
652
652
|
|
|
653
653
|
:scope[color="info"] {
|
|
@@ -682,8 +682,8 @@
|
|
|
682
682
|
───────────────────────────────────────────────────────────────── */
|
|
683
683
|
|
|
684
684
|
.chart-tooltip-popup {
|
|
685
|
-
--chart-tooltip-bg: var(--a-
|
|
686
|
-
--chart-tooltip-fg: var(--a-
|
|
685
|
+
--chart-tooltip-bg: var(--a-fg);
|
|
686
|
+
--chart-tooltip-fg: var(--a-bg);
|
|
687
687
|
--chart-tooltip-px: var(--a-space-2-5);
|
|
688
688
|
--chart-tooltip-py: var(--a-space-1-5);
|
|
689
689
|
--chart-tooltip-radius: var(--a-radius-sm);
|
|
@@ -196,9 +196,9 @@ class AdiaChart extends AdiaElement {
|
|
|
196
196
|
hideValues: { type: Boolean, default: false, reflect: true, attribute: 'hide-values' },
|
|
197
197
|
radius: { type: Number, default: null, reflect: true },
|
|
198
198
|
smooth: { type: Number, default: 0.4, reflect: true },
|
|
199
|
-
aspect: { type: String, default: 'std', reflect: true },
|
|
200
|
-
size: { type: String, default: '', reflect: true },
|
|
201
|
-
format: { type: String, default: 'abbr', reflect: true },
|
|
199
|
+
aspect: { type: String, default: 'std', reflect: true },
|
|
200
|
+
size: { type: String, default: '', reflect: true },
|
|
201
|
+
format: { type: String, default: 'abbr', reflect: true },
|
|
202
202
|
};
|
|
203
203
|
|
|
204
204
|
static template = () => null;
|
|
@@ -263,10 +263,11 @@ class AdiaChart extends AdiaElement {
|
|
|
263
263
|
if (!this.hasAttribute('role')) this.setAttribute('role', 'img');
|
|
264
264
|
if (!this.hasAttribute('aria-label')) this.setAttribute('aria-label', this.heading || `${this.type} chart`);
|
|
265
265
|
|
|
266
|
-
/*
|
|
267
|
-
chart-legend-ui[for=self]
|
|
268
|
-
|
|
269
|
-
|
|
266
|
+
/* Listen for canonical `toggle` events bubbled from external
|
|
267
|
+
chart-legend-ui[for=self] descendants. The handler filters by
|
|
268
|
+
target so other components dispatching `toggle` (accordion-ui,
|
|
269
|
+
agent-trace-ui, etc.) don't interfere. */
|
|
270
|
+
document.addEventListener('toggle', this.#onLegendToggle);
|
|
270
271
|
|
|
271
272
|
/* OD-CHART-06 — keyboard a11y. Chart becomes focusable; arrow keys
|
|
272
273
|
move a virtual focus across datums in DOM order, Enter/Space fires
|
|
@@ -279,6 +280,14 @@ class AdiaChart extends AdiaElement {
|
|
|
279
280
|
this.addEventListener('keydown', this.#onKeydown);
|
|
280
281
|
this.addEventListener('focus', this.#onFocus);
|
|
281
282
|
this.addEventListener('blur', this.#onBlur);
|
|
283
|
+
/* Pointer/click handlers attached to the host (not per-render SVG) so
|
|
284
|
+
render() stays listener-graph-idempotent. Handlers use
|
|
285
|
+
e.target.closest('[data-tip-*]') so they only fire on real datums. */
|
|
286
|
+
this.addEventListener('pointerover', this.#onPointerOver);
|
|
287
|
+
this.addEventListener('pointermove', this.#onPointerMove);
|
|
288
|
+
this.addEventListener('pointerleave', this.#onPointerLeave);
|
|
289
|
+
this.addEventListener('pointerdown', this.#onPointerDown);
|
|
290
|
+
this.addEventListener('click', this.#onClick);
|
|
282
291
|
this.#warnDeprecatedAttrs();
|
|
283
292
|
|
|
284
293
|
this.#resizeObs = new ResizeObserver((entries) => {
|
|
@@ -443,16 +452,10 @@ class AdiaChart extends AdiaElement {
|
|
|
443
452
|
/* Notify external legend/tooltip consumers that legendData has refreshed. */
|
|
444
453
|
this.dispatchEvent(new CustomEvent('legend-update', { bubbles: true }));
|
|
445
454
|
|
|
446
|
-
/*
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
move still handle mouse + pen (`pointerType` gating inside each). */
|
|
451
|
-
svgEl.addEventListener('pointerover', this.#onPointerOver);
|
|
452
|
-
svgEl.addEventListener('pointermove', this.#onPointerMove);
|
|
453
|
-
svgEl.addEventListener('pointerleave', this.#onPointerLeave);
|
|
454
|
-
svgEl.addEventListener('pointerdown', this.#onPointerDown);
|
|
455
|
-
svgEl.addEventListener('click', this.#onClick);
|
|
455
|
+
/* Hover tooltip + custom events are wired in connected() — host-level
|
|
456
|
+
so they survive the innerHTML wipe at render. Internal tooltip
|
|
457
|
+
(#tipEl) remains for back-compat; Phase 2 retires it when
|
|
458
|
+
tooltip-ui[follows=pointer] lands. */
|
|
456
459
|
}
|
|
457
460
|
|
|
458
461
|
/* ── Per-series --color-{key} injection ──
|
|
@@ -491,11 +494,16 @@ class AdiaChart extends AdiaElement {
|
|
|
491
494
|
this.#resizeObs?.disconnect();
|
|
492
495
|
this.#resizeObs = null;
|
|
493
496
|
if (this.#resizeRaf) { cancelAnimationFrame(this.#resizeRaf); this.#resizeRaf = null; }
|
|
494
|
-
document.removeEventListener('
|
|
497
|
+
document.removeEventListener('toggle', this.#onLegendToggle);
|
|
495
498
|
document.removeEventListener('pointerdown', this.#pinnedTouchDismiss);
|
|
496
499
|
this.removeEventListener('keydown', this.#onKeydown);
|
|
497
500
|
this.removeEventListener('focus', this.#onFocus);
|
|
498
501
|
this.removeEventListener('blur', this.#onBlur);
|
|
502
|
+
this.removeEventListener('pointerover', this.#onPointerOver);
|
|
503
|
+
this.removeEventListener('pointermove', this.#onPointerMove);
|
|
504
|
+
this.removeEventListener('pointerleave', this.#onPointerLeave);
|
|
505
|
+
this.removeEventListener('pointerdown', this.#onPointerDown);
|
|
506
|
+
this.removeEventListener('click', this.#onClick);
|
|
499
507
|
this.#hideTooltip();
|
|
500
508
|
}
|
|
501
509
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"default": ""
|
|
28
28
|
},
|
|
29
29
|
"onToggle": {
|
|
30
|
-
"description": "Series-toggle mode emitted via
|
|
30
|
+
"description": "Series-toggle mode emitted via the `toggle` event. `hide` removes the series from the render; `opacity` fades it. Wired via [for] on chart-ui.",
|
|
31
31
|
"type": "string",
|
|
32
32
|
"enum": [
|
|
33
33
|
"hide",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"anti_patterns": [],
|
|
72
72
|
"category": "agent",
|
|
73
73
|
"events": {
|
|
74
|
-
"
|
|
74
|
+
"toggle": {
|
|
75
75
|
"description": "Fires on row click (non-static). Detail: {key, active, mode}. `active` is the new state (true=visible). Consumers (or chart-ui via [for]) wire this to series visibility."
|
|
76
76
|
}
|
|
77
77
|
},
|
|
@@ -36,6 +36,9 @@ import { AdiaElement } from '../../core/element.js';
|
|
|
36
36
|
class AdiaChartLegend extends AdiaElement {
|
|
37
37
|
static properties = {
|
|
38
38
|
for: { type: String, default: '', reflect: true },
|
|
39
|
+
// Items is a JSON-encoded array; reflecting to the attribute would
|
|
40
|
+
// serialize a potentially long array on every change. The property is
|
|
41
|
+
// the source of truth; the attribute is intentionally write-only.
|
|
39
42
|
items: { type: String, default: '', reflect: false },
|
|
40
43
|
shape: { type: String, default: 'dot', reflect: true },
|
|
41
44
|
position: { type: String, default: 'bottom', reflect: true },
|
|
@@ -173,7 +176,7 @@ class AdiaChartLegend extends AdiaElement {
|
|
|
173
176
|
if (newActive) row.setAttribute('data-active', '');
|
|
174
177
|
else row.removeAttribute('data-active');
|
|
175
178
|
|
|
176
|
-
this.dispatchEvent(new CustomEvent('
|
|
179
|
+
this.dispatchEvent(new CustomEvent('toggle', {
|
|
177
180
|
bubbles: true,
|
|
178
181
|
detail: { key, active: newActive, mode: this.onToggle || 'hide' },
|
|
179
182
|
}));
|
|
@@ -48,7 +48,7 @@ props:
|
|
|
48
48
|
reflect: true
|
|
49
49
|
onToggle:
|
|
50
50
|
description: >-
|
|
51
|
-
Series-toggle mode emitted via
|
|
51
|
+
Series-toggle mode emitted via the `toggle` event. `hide` removes the
|
|
52
52
|
series from the render; `opacity` fades it. Wired via [for] on chart-ui.
|
|
53
53
|
type: string
|
|
54
54
|
default: hide
|
|
@@ -58,7 +58,7 @@ props:
|
|
|
58
58
|
reflect: true
|
|
59
59
|
attribute: on-toggle
|
|
60
60
|
events:
|
|
61
|
-
|
|
61
|
+
toggle:
|
|
62
62
|
description: >-
|
|
63
63
|
Fires on row click (non-static). Detail: {key, active, mode}. `active` is
|
|
64
64
|
the new state (true=visible). Consumers (or chart-ui via [for]) wire
|
|
@@ -24,7 +24,7 @@ import { AdiaElement } from '../../core/element.js';
|
|
|
24
24
|
* model — currently selected model value (reflected, two-way with select)
|
|
25
25
|
* placeholder — textarea placeholder
|
|
26
26
|
* disabled — disable entire input (textarea becomes contenteditable=false)
|
|
27
|
-
*
|
|
27
|
+
* loading — in-flight / streaming state: send button disabled, submit
|
|
28
28
|
* events suppressed, but textarea stays editable so the user
|
|
29
29
|
* can draft a follow-up while the model is still responding.
|
|
30
30
|
*
|
|
@@ -40,7 +40,7 @@ import { AdiaElement } from '../../core/element.js';
|
|
|
40
40
|
class AdiaChatInput extends AdiaElement {
|
|
41
41
|
static properties = {
|
|
42
42
|
disabled: { type: Boolean, default: false, reflect: true },
|
|
43
|
-
|
|
43
|
+
loading: { type: Boolean, default: false, reflect: true },
|
|
44
44
|
placeholder: { type: String, default: 'Type a message...', reflect: true },
|
|
45
45
|
model: { type: String, default: '', reflect: true },
|
|
46
46
|
};
|
|
@@ -56,6 +56,7 @@ class AdiaChatInput extends AdiaElement {
|
|
|
56
56
|
#attachments = []; // [{ type: 'image', dataUrl, name }]
|
|
57
57
|
#previewEl = null;
|
|
58
58
|
#fileInput = null;
|
|
59
|
+
#focusRaf = null;
|
|
59
60
|
|
|
60
61
|
#onAttachPress = () => this.#fileInput?.click();
|
|
61
62
|
#onFileInputChange = () => {
|
|
@@ -154,7 +155,7 @@ class AdiaChatInput extends AdiaElement {
|
|
|
154
155
|
this.#textareaEl.placeholder = this.placeholder;
|
|
155
156
|
}
|
|
156
157
|
if (this.#sendEl) {
|
|
157
|
-
this.#sendEl.disabled = this.disabled || this.
|
|
158
|
+
this.#sendEl.disabled = this.disabled || this.loading;
|
|
158
159
|
}
|
|
159
160
|
// Sync model value to select (handles late upgrades)
|
|
160
161
|
if (this.#modelEl && this.model && this.#modelEl.value !== this.model) {
|
|
@@ -164,7 +165,10 @@ class AdiaChatInput extends AdiaElement {
|
|
|
164
165
|
|
|
165
166
|
#onPointerDown = (e) => {
|
|
166
167
|
if (e.target.closest('button-ui, select-ui, input-ui, a')) return;
|
|
167
|
-
requestAnimationFrame(() =>
|
|
168
|
+
this.#focusRaf = requestAnimationFrame(() => {
|
|
169
|
+
this.#focusRaf = null;
|
|
170
|
+
this.#textareaEl?.focus();
|
|
171
|
+
});
|
|
168
172
|
};
|
|
169
173
|
|
|
170
174
|
#onModelChange = () => {
|
|
@@ -172,7 +176,7 @@ class AdiaChatInput extends AdiaElement {
|
|
|
172
176
|
};
|
|
173
177
|
|
|
174
178
|
#onSubmit = () => {
|
|
175
|
-
if (this.disabled || this.
|
|
179
|
+
if (this.disabled || this.loading) return;
|
|
176
180
|
const text = this.value;
|
|
177
181
|
if (!text && !this.#attachments.length) return;
|
|
178
182
|
this.dispatchEvent(new CustomEvent('submit', {
|
|
@@ -240,6 +244,10 @@ class AdiaChatInput extends AdiaElement {
|
|
|
240
244
|
}
|
|
241
245
|
|
|
242
246
|
disconnected() {
|
|
247
|
+
if (this.#focusRaf != null) {
|
|
248
|
+
cancelAnimationFrame(this.#focusRaf);
|
|
249
|
+
this.#focusRaf = null;
|
|
250
|
+
}
|
|
243
251
|
this.removeEventListener('paste', this.#onPaste);
|
|
244
252
|
this.#sendEl?.removeEventListener('press', this.#onSubmit);
|
|
245
253
|
this.#textareaEl?.removeEventListener('submit', this.#onSubmit);
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"anti_patterns": [],
|
|
31
31
|
"category": "agent",
|
|
32
32
|
"events": {
|
|
33
|
-
"
|
|
34
|
-
"description": "Fired
|
|
33
|
+
"submit": {
|
|
34
|
+
"description": "Fired when the user submits a chat message via Enter or send button. Detail: { text, model }."
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"examples": [
|
package/components/chat/chat.js
CHANGED
|
@@ -32,7 +32,7 @@ function escapeHTML(s) {
|
|
|
32
32
|
* chat.messages // read all messages
|
|
33
33
|
*
|
|
34
34
|
* Events:
|
|
35
|
-
*
|
|
35
|
+
* submit — user pressed send (detail: { text, model })
|
|
36
36
|
*/
|
|
37
37
|
class AdiaChat extends AdiaElement {
|
|
38
38
|
static properties = {
|
|
@@ -44,6 +44,7 @@ class AdiaChat extends AdiaElement {
|
|
|
44
44
|
#messages = [];
|
|
45
45
|
#messagesEl = null;
|
|
46
46
|
#inputEl = null;
|
|
47
|
+
#scrollRaf = null;
|
|
47
48
|
|
|
48
49
|
get messages() { return [...this.#messages]; }
|
|
49
50
|
|
|
@@ -69,6 +70,10 @@ class AdiaChat extends AdiaElement {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
disconnected() {
|
|
73
|
+
if (this.#scrollRaf != null) {
|
|
74
|
+
cancelAnimationFrame(this.#scrollRaf);
|
|
75
|
+
this.#scrollRaf = null;
|
|
76
|
+
}
|
|
72
77
|
this.#inputEl?.removeEventListener('submit', this.#onSubmit);
|
|
73
78
|
this.#messagesEl = null;
|
|
74
79
|
this.#inputEl = null;
|
|
@@ -79,7 +84,7 @@ class AdiaChat extends AdiaElement {
|
|
|
79
84
|
const { text, model } = e.detail || {};
|
|
80
85
|
if (!text) return;
|
|
81
86
|
this.#inputEl.clear();
|
|
82
|
-
this.dispatchEvent(new CustomEvent('
|
|
87
|
+
this.dispatchEvent(new CustomEvent('submit', { bubbles: true, detail: { text, model } }));
|
|
83
88
|
};
|
|
84
89
|
|
|
85
90
|
appendMessage({ role, content = '', render = false }) {
|
|
@@ -147,7 +152,13 @@ class AdiaChat extends AdiaElement {
|
|
|
147
152
|
|
|
148
153
|
#scrollToBottom() {
|
|
149
154
|
const el = this.#messagesEl;
|
|
150
|
-
if (el)
|
|
155
|
+
if (!el) return;
|
|
156
|
+
if (this.#scrollRaf != null) cancelAnimationFrame(this.#scrollRaf);
|
|
157
|
+
this.#scrollRaf = requestAnimationFrame(() => {
|
|
158
|
+
this.#scrollRaf = null;
|
|
159
|
+
if (!this.isConnected) return;
|
|
160
|
+
el.scrollTop = el.scrollHeight;
|
|
161
|
+
});
|
|
151
162
|
}
|
|
152
163
|
}
|
|
153
164
|
customElements.define('chat-ui', AdiaChat);
|
|
@@ -13,8 +13,8 @@ props:
|
|
|
13
13
|
type: boolean
|
|
14
14
|
default: false
|
|
15
15
|
events:
|
|
16
|
-
|
|
17
|
-
description: "Fired
|
|
16
|
+
submit:
|
|
17
|
+
description: "Fired when the user submits a chat message via Enter or send button. Detail: { text, model }."
|
|
18
18
|
slots:
|
|
19
19
|
default:
|
|
20
20
|
description: "Default slot — primary child content."
|
package/components/code/code.css
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
--code-radius: var(--a-radius-md);
|
|
7
7
|
--code-radius-sm: var(--a-radius-sm);
|
|
8
8
|
--code-copy-px: var(--a-space-1);
|
|
9
|
+
--code-copy-py: var(--a-space-0-5);
|
|
10
|
+
--code-lint-marker-w: 3px; /* Vertical lint-marker rule; intrinsic measurement; no --a-space-* equivalent at this width. */
|
|
9
11
|
|
|
10
12
|
/* ── Colors ── */
|
|
11
13
|
--code-bg: var(--a-bg);
|
|
@@ -31,9 +33,12 @@
|
|
|
31
33
|
DOM. See docs/specs/code-editor.md §6. */
|
|
32
34
|
--code-gutter-bg: var(--a-bg-subtle);
|
|
33
35
|
--code-gutter-fg: var(--a-fg-muted);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
/* Transparency-mixing in oklab — Safari < 18 has an OKLCH bug where
|
|
37
|
+
the transparent operand's hue resolves to 0, red-shifting the mix.
|
|
38
|
+
oklab is hue-agnostic and visually identical in fixed engines. */
|
|
39
|
+
--code-active-line-bg: color-mix(in oklab, var(--a-accent-muted) 40%, transparent);
|
|
40
|
+
--code-selection-bg: color-mix(in oklab, var(--a-accent-muted) 60%, transparent);
|
|
41
|
+
--code-selection-match: color-mix(in oklab, var(--a-accent-muted) 30%, transparent);
|
|
37
42
|
--code-cursor: var(--a-accent-strong);
|
|
38
43
|
--code-focus-ring: var(--a-focus-ring);
|
|
39
44
|
|
|
@@ -110,7 +115,7 @@
|
|
|
110
115
|
cursor: pointer;
|
|
111
116
|
font-size: var(--code-header-font);
|
|
112
117
|
color: var(--code-header-fg);
|
|
113
|
-
padding:
|
|
118
|
+
padding: var(--code-copy-py) var(--code-copy-px);
|
|
114
119
|
border-radius: var(--code-radius-sm);
|
|
115
120
|
transition:
|
|
116
121
|
background var(--code-duration) var(--code-easing),
|
|
@@ -258,7 +263,12 @@
|
|
|
258
263
|
:scope .cm-scroller {
|
|
259
264
|
font-family: inherit;
|
|
260
265
|
line-height: inherit;
|
|
261
|
-
padding
|
|
266
|
+
/* No padding — the gutter band ([data-cm-mount] > .cm-gutters has its
|
|
267
|
+
own --code-gutter-bg) reads as the chrome it is when pinned to the
|
|
268
|
+
left edge, and the active-line highlight and selection block get
|
|
269
|
+
full bleed. The header / footer bands frame the editor; this content
|
|
270
|
+
region should not double-inset. */
|
|
271
|
+
padding: 0;
|
|
262
272
|
}
|
|
263
273
|
|
|
264
274
|
:scope .cm-content {
|
|
@@ -347,7 +357,7 @@
|
|
|
347
357
|
}
|
|
348
358
|
:scope .cm-diagnostic {
|
|
349
359
|
padding: 0;
|
|
350
|
-
border-inline-start:
|
|
360
|
+
border-inline-start: var(--code-lint-marker-w) solid transparent;
|
|
351
361
|
padding-inline-start: var(--a-space-2);
|
|
352
362
|
}
|
|
353
363
|
:scope .cm-diagnostic-error { border-inline-start-color: var(--code-lint-error); }
|
|
@@ -32,6 +32,7 @@ class AdiaCommand extends AdiaElement {
|
|
|
32
32
|
#activeIdx = -1;
|
|
33
33
|
#inputEl = null;
|
|
34
34
|
#listEl = null;
|
|
35
|
+
#focusRaf = null;
|
|
35
36
|
#footerEl = null;
|
|
36
37
|
#bound = false;
|
|
37
38
|
#itemByEl = new WeakMap();
|
|
@@ -88,7 +89,10 @@ class AdiaCommand extends AdiaElement {
|
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
if (this.open) {
|
|
91
|
-
requestAnimationFrame(() =>
|
|
92
|
+
this.#focusRaf = requestAnimationFrame(() => {
|
|
93
|
+
this.#focusRaf = null;
|
|
94
|
+
this.#inputEl?.focus();
|
|
95
|
+
});
|
|
92
96
|
}
|
|
93
97
|
}
|
|
94
98
|
|
|
@@ -280,6 +284,10 @@ class AdiaCommand extends AdiaElement {
|
|
|
280
284
|
};
|
|
281
285
|
|
|
282
286
|
disconnected() {
|
|
287
|
+
if (this.#focusRaf != null) {
|
|
288
|
+
cancelAnimationFrame(this.#focusRaf);
|
|
289
|
+
this.#focusRaf = null;
|
|
290
|
+
}
|
|
283
291
|
this.removeEventListener('keydown', this.#onKeydown);
|
|
284
292
|
this.#inputEl?.removeEventListener('input', this.#onInput);
|
|
285
293
|
this.#listEl?.removeEventListener('click', this.#onListClick);
|
|
@@ -21,11 +21,6 @@
|
|
|
21
21
|
"component": {
|
|
22
22
|
"const": "Field"
|
|
23
23
|
},
|
|
24
|
-
"error": {
|
|
25
|
-
"description": "Validation error message rendered below the control in danger style. Takes precedence over `hint` in the same row, and carries role=\"alert\" so screen readers announce changes.",
|
|
26
|
-
"type": "string",
|
|
27
|
-
"default": ""
|
|
28
|
-
},
|
|
29
24
|
"hint": {
|
|
30
25
|
"description": "Help text rendered below the control in caption style. Wired into the slotted control's aria-describedby so screen readers announce it. Suppressed when `error` is set.",
|
|
31
26
|
"type": "string",
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
--field-label-color: var(--a-fg);
|
|
6
6
|
--field-label-size: var(--a-ui-sm);
|
|
7
7
|
--field-label-weight: var(--a-weight-medium);
|
|
8
|
-
--field-required-color: var(--a-danger);
|
|
8
|
+
--field-required-color: var(--a-danger-strong);
|
|
9
9
|
--field-trailing-color: var(--a-fg-subtle);
|
|
10
10
|
--field-trailing-size: var(--a-ui-sm);
|
|
11
11
|
--field-hint-color: var(--a-fg-muted);
|
|
12
12
|
--field-hint-size: var(--a-ui-sm);
|
|
13
|
-
--field-error-color: var(--a-danger);
|
|
13
|
+
--field-error-color: var(--a-danger-strong);
|
|
14
14
|
--field-error-size: var(--a-ui-sm);
|
|
15
15
|
|
|
16
16
|
/* In inline mode, the label column auto-sizes by default (each
|