@adia-ai/web-components 0.8.3 → 0.8.5
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/CHANGELOG.md +33 -0
- package/USAGE.md +60 -25
- package/components/accordion/accordion.css +5 -0
- package/components/adia-mark/adia-mark.a2ui.json +123 -0
- package/components/adia-mark/adia-mark.class.js +64 -0
- package/components/adia-mark/adia-mark.css +48 -0
- package/components/adia-mark/adia-mark.d.ts +20 -0
- package/components/adia-mark/adia-mark.examples.md +26 -0
- package/components/adia-mark/adia-mark.js +17 -0
- package/components/adia-mark/adia-mark.test.js +69 -0
- package/components/adia-mark/adia-mark.yaml +121 -0
- package/components/agent-artifact/agent-artifact.css +1 -0
- package/components/agent-questions/agent-questions.a2ui.json +2 -1
- package/components/agent-questions/agent-questions.class.js +96 -53
- package/components/agent-questions/agent-questions.css +27 -106
- package/components/agent-questions/agent-questions.yaml +1 -0
- package/components/agent-reasoning/agent-reasoning.css +9 -5
- package/components/button/button.class.js +9 -2
- package/components/button/button.css +1 -1
- package/components/calendar-grid/calendar-grid.a2ui.json +3 -1
- package/components/calendar-grid/calendar-grid.class.js +11 -6
- package/components/calendar-grid/calendar-grid.css +42 -20
- package/components/calendar-grid/calendar-grid.yaml +5 -0
- package/components/calendar-picker/calendar-picker.a2ui.json +3 -1
- package/components/calendar-picker/calendar-picker.class.js +11 -6
- package/components/calendar-picker/calendar-picker.css +41 -19
- package/components/calendar-picker/calendar-picker.yaml +5 -0
- package/components/chart/chart.class.js +17 -14
- package/components/chat-thread/chat-input.a2ui.json +3 -1
- package/components/chat-thread/chat-input.css +12 -13
- package/components/chat-thread/chat-input.js +6 -2
- package/components/chat-thread/chat-input.yaml +3 -0
- package/components/check/check.css +1 -1
- package/components/color-input/color-input.class.js +8 -5
- package/components/color-picker/color-picker.class.js +6 -3
- package/components/combobox/combobox.a2ui.json +3 -1
- package/components/combobox/combobox.class.js +1 -3
- package/components/combobox/combobox.css +9 -12
- package/components/combobox/combobox.yaml +2 -0
- package/components/date-range-picker/date-range-picker.css +1 -0
- package/components/field/field.class.js +10 -7
- package/components/fields/fields.class.js +15 -12
- package/components/icon/icon.class.js +9 -5
- package/components/index.js +1 -0
- package/components/input/input.css +6 -2
- package/components/nav/nav.class.js +2 -1
- package/components/nav-group/nav-group.css +5 -6
- package/components/noodles/noodles.class.js +14 -10
- package/components/option-card/option-card.css +1 -0
- package/components/page/page.class.js +3 -2
- package/components/radio/radio.css +1 -1
- package/components/range/range.css +2 -0
- package/components/select/select.a2ui.json +2 -1
- package/components/select/select.class.js +12 -11
- package/components/select/select.css +29 -40
- package/components/select/select.test.js +6 -2
- package/components/select/select.yaml +1 -0
- package/components/swatch/swatch.a2ui.json +3 -1
- package/components/swatch/swatch.class.js +41 -32
- package/components/swatch/swatch.css +13 -18
- package/components/swatch/swatch.yaml +2 -0
- package/components/swiper/swiper.class.js +4 -1
- package/components/swiper/swiper.css +13 -4
- package/components/switch/switch.css +1 -1
- package/components/tag/tag.a2ui.json +4 -1
- package/components/tag/tag.class.js +3 -1
- package/components/tag/tag.css +13 -15
- package/components/tag/tag.yaml +3 -0
- package/components/tags-input/tags-input.css +1 -0
- package/components/textarea/textarea.css +2 -0
- package/components/time-picker/time-picker.css +2 -0
- package/components/timeline/timeline.a2ui.json +2 -1
- package/components/timeline/timeline.class.js +7 -8
- package/components/timeline/timeline.css +14 -26
- package/components/timeline/timeline.yaml +1 -0
- package/components/toggle-group/toggle-group.css +1 -0
- package/components/toolbar/toolbar.class.js +11 -6
- package/components/tree/tree.class.js +5 -2
- package/components/tree/tree.css +5 -2
- package/components/upload/upload.css +1 -0
- package/core/data-stream.js +21 -15
- package/core/element.js +36 -1
- package/core/element.test.js +67 -0
- package/core/icons-phosphor.js +86 -22
- package/core/icons.js +22 -8
- package/dist/theme-provider.min.js +2 -2
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +109 -108
- package/dist/web-components.sheet.js +1 -1
- package/package.json +1 -5
- package/styles/colors/semantics/features.css +6 -1
- package/styles/components.css +1 -0
- package/traits/error-shake/error-shake.js +23 -17
- package/traits/fade-presence/fade-presence.js +19 -15
- package/traits/success-checkmark/success-checkmark.js +21 -16
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
2
|
+
name: UIAdiaMark
|
|
3
|
+
tag: adia-mark-ui
|
|
4
|
+
status: stable
|
|
5
|
+
component: AdiaMark
|
|
6
|
+
category: display
|
|
7
|
+
version: 1
|
|
8
|
+
description: >-
|
|
9
|
+
The Adia brand mark — a rounded square with the "Adia" wordmark cut out
|
|
10
|
+
in a contrasting color. Token-driven: the background always reads as
|
|
11
|
+
the current scheme's inverse surface, and the wordmark always reads as
|
|
12
|
+
the current scheme's inverse foreground, so a single asset replaces
|
|
13
|
+
separate light/dark/transparent static exports (gh#283). Not an
|
|
14
|
+
`icon-ui` registry entry — icon-ui is the Phosphor-glyph registry only;
|
|
15
|
+
this is a fixed, one-off brand image, a separate delivery shape.
|
|
16
|
+
props:
|
|
17
|
+
size:
|
|
18
|
+
description: Diameter of the square mark. Matches avatar-ui's size ladder for visual consistency when the two sit side by side (e.g. an app switcher row).
|
|
19
|
+
type: string
|
|
20
|
+
default: md
|
|
21
|
+
enum:
|
|
22
|
+
- xs
|
|
23
|
+
- sm
|
|
24
|
+
- md
|
|
25
|
+
- lg
|
|
26
|
+
- xl
|
|
27
|
+
reflect: true
|
|
28
|
+
inline:
|
|
29
|
+
description: Renders as `display:inline-block` instead of the block-level default (ADR-0037) — set this when the mark sits beside a text run (a topbar brand slot) rather than standing alone.
|
|
30
|
+
type: boolean
|
|
31
|
+
default: false
|
|
32
|
+
reflect: true
|
|
33
|
+
events: {}
|
|
34
|
+
slots: {}
|
|
35
|
+
states:
|
|
36
|
+
- name: default
|
|
37
|
+
description: The only state — a static, non-interactive brand mark.
|
|
38
|
+
traits: []
|
|
39
|
+
tokens:
|
|
40
|
+
--adia-mark-size:
|
|
41
|
+
description: Width and height of the square mark.
|
|
42
|
+
default: 2.5rem
|
|
43
|
+
--adia-mark-radius:
|
|
44
|
+
description: Corner radius of the background square.
|
|
45
|
+
default: var(--a-radius-lg)
|
|
46
|
+
--adia-mark-bg:
|
|
47
|
+
description: Background fill — always the inverse surface token, regardless of variant or scheme.
|
|
48
|
+
default: var(--a-canvas-surface-inverse)
|
|
49
|
+
--adia-mark-fg:
|
|
50
|
+
description: Wordmark fill — always the inverse foreground token, regardless of variant or scheme.
|
|
51
|
+
default: var(--a-fg-inverse)
|
|
52
|
+
a2ui:
|
|
53
|
+
rules:
|
|
54
|
+
- Use <AdiaMark> for the Adia brand/app-identity mark — a favicon-style app-switcher tile, a splash/loading screen, an about-panel header. Do not use it as a generic icon; it has no [name] prop and always renders the same wordmark.
|
|
55
|
+
- Do not wrap <AdiaMark> in <Icon> or pass it a color/tone override — the background/foreground pairing is fixed by design (always inverse-on-inverse for contrast), not a themeable variant.
|
|
56
|
+
- Prefer <AdiaMark size="sm"> in a nav/topbar brand slot (a Row alongside the product name); reserve larger sizes (lg, xl) for splash screens or empty-state headers. Set [inline] only when the mark sits directly inside a run of prose text, not inside a Row/flex layout (block-level children compose fine as flex items already).
|
|
57
|
+
anti_patterns:
|
|
58
|
+
- wrong: |
|
|
59
|
+
<icon-ui name="adia"></icon-ui>
|
|
60
|
+
why: |
|
|
61
|
+
icon-ui only resolves Phosphor glyph names from the registry; there
|
|
62
|
+
is no "adia" Phosphor icon, and registering one would misuse a
|
|
63
|
+
third-party glyph set for a proprietary brand mark.
|
|
64
|
+
fix: |
|
|
65
|
+
<adia-mark-ui size="sm"></adia-mark-ui>
|
|
66
|
+
- wrong: |
|
|
67
|
+
<adia-mark-ui style="--adia-mark-bg: #171818; --adia-mark-fg: #E2E3E4;"></adia-mark-ui>
|
|
68
|
+
why: |
|
|
69
|
+
Hardcoding the fill colors defeats the entire point of gh#283 —
|
|
70
|
+
the mark is supposed to track the inverse-surface/inverse-foreground
|
|
71
|
+
tokens automatically across scheme changes, not be pinned to one
|
|
72
|
+
scheme's static values.
|
|
73
|
+
fix: |
|
|
74
|
+
<adia-mark-ui size="sm"></adia-mark-ui>
|
|
75
|
+
examples:
|
|
76
|
+
- name: topbar-brand-mark
|
|
77
|
+
description: Small brand mark in an admin-shell topbar, beside the product name.
|
|
78
|
+
a2ui: >-
|
|
79
|
+
[
|
|
80
|
+
{
|
|
81
|
+
"id": "brand-row",
|
|
82
|
+
"component": "Row",
|
|
83
|
+
"gap": "2",
|
|
84
|
+
"align": "center",
|
|
85
|
+
"children": ["mark", "name"]
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"id": "mark",
|
|
89
|
+
"component": "AdiaMark",
|
|
90
|
+
"size": "sm"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "name",
|
|
94
|
+
"component": "Text",
|
|
95
|
+
"variant": "heading",
|
|
96
|
+
"text": "Adia"
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
keywords:
|
|
100
|
+
- adia
|
|
101
|
+
- brand
|
|
102
|
+
- logo
|
|
103
|
+
- wordmark
|
|
104
|
+
- mark
|
|
105
|
+
- identity
|
|
106
|
+
- favicon
|
|
107
|
+
synonyms:
|
|
108
|
+
logo:
|
|
109
|
+
- adia
|
|
110
|
+
- brand
|
|
111
|
+
- mark
|
|
112
|
+
- wordmark
|
|
113
|
+
brand:
|
|
114
|
+
- adia
|
|
115
|
+
- logo
|
|
116
|
+
- mark
|
|
117
|
+
- identity
|
|
118
|
+
related:
|
|
119
|
+
- Icon
|
|
120
|
+
- Avatar
|
|
121
|
+
- Image
|
|
@@ -41,20 +41,11 @@
|
|
|
41
41
|
|
|
42
42
|
import { UIElement } from '../../core/element.js';
|
|
43
43
|
|
|
44
|
-
function escapeText(s) {
|
|
45
|
-
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function escapeAttr(s) {
|
|
49
|
-
return String(s).replace(/"/g, '"');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
44
|
export class UIAgentQuestions extends UIElement {
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
static requiredIcons = ['check'];
|
|
45
|
+
// gh issue 276 wave 4: options now render as <option-card-ui> (which
|
|
46
|
+
// composes icon-ui itself for the leading `icon` prop). This primitive
|
|
47
|
+
// no longer stamps any icon directly, so no requiredIcons entry is
|
|
48
|
+
// needed here — see check-required-icons.mjs (slot 11).
|
|
58
49
|
|
|
59
50
|
static properties = {
|
|
60
51
|
multi: { type: Boolean, default: false, reflect: true },
|
|
@@ -87,6 +78,9 @@ export class UIAgentQuestions extends UIElement {
|
|
|
87
78
|
|
|
88
79
|
disconnected() {
|
|
89
80
|
this.#submitEl?.removeEventListener('press', this.#onSubmit);
|
|
81
|
+
this.#optionsEl?.removeEventListener('change', this.#onCardChange);
|
|
82
|
+
this.#optionsEl?.removeEventListener('click', this.#onOptionsClickCapture, true);
|
|
83
|
+
this.#optionsEl?.removeEventListener('keydown', this.#onOptionsKeydownCapture, true);
|
|
90
84
|
this.#questionEl = this.#helperEl = this.#optionsEl = this.#submitEl = null;
|
|
91
85
|
}
|
|
92
86
|
|
|
@@ -121,6 +115,20 @@ export class UIAgentQuestions extends UIElement {
|
|
|
121
115
|
|
|
122
116
|
this.#optionsEl = document.createElement('div');
|
|
123
117
|
this.#optionsEl.setAttribute('data-questions-options', '');
|
|
118
|
+
// Single-select: option-card-ui's own radiogroup machinery handles
|
|
119
|
+
// click + keyboard natively and fires `change` when a card becomes
|
|
120
|
+
// selected — we just listen for it.
|
|
121
|
+
this.#optionsEl.addEventListener('change', this.#onCardChange);
|
|
122
|
+
// Multi-select: option-card-ui is radio-only (its #select() no-ops
|
|
123
|
+
// once checked, and Enter/Space call that private method directly
|
|
124
|
+
// rather than dispatching a click), so there is no toggle-off hook
|
|
125
|
+
// to attach to. Intercept at the container's CAPTURE phase — before
|
|
126
|
+
// the card's own bubble-phase listeners ever run — and drive
|
|
127
|
+
// checked/aria state ourselves. Single mode never reaches these
|
|
128
|
+
// (guarded by `if (!this.multi) return`), so its native behavior is
|
|
129
|
+
// untouched.
|
|
130
|
+
this.#optionsEl.addEventListener('click', this.#onOptionsClickCapture, true);
|
|
131
|
+
this.#optionsEl.addEventListener('keydown', this.#onOptionsKeydownCapture, true);
|
|
124
132
|
|
|
125
133
|
this.#submitEl = document.createElement('button-ui');
|
|
126
134
|
this.#submitEl.setAttribute('text', this.submitLabel || 'Continue');
|
|
@@ -139,33 +147,28 @@ export class UIAgentQuestions extends UIElement {
|
|
|
139
147
|
this.#optionsEl.innerHTML = '';
|
|
140
148
|
|
|
141
149
|
for (const opt of this.#options) {
|
|
142
|
-
const card = document.createElement('
|
|
143
|
-
card.type = 'button';
|
|
150
|
+
const card = document.createElement('option-card-ui');
|
|
144
151
|
card.setAttribute('data-questions-option', '');
|
|
145
|
-
card.setAttribute('
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
</span>`;
|
|
157
|
-
|
|
158
|
-
card.innerHTML = `
|
|
159
|
-
${iconHTML}
|
|
160
|
-
<span data-questions-option-body>
|
|
161
|
-
<span data-questions-option-label>${escapeText(opt.label || '')}</span>
|
|
162
|
-
${opt.description ? `<span data-questions-option-desc>${escapeText(opt.description)}</span>` : ''}
|
|
163
|
-
</span>
|
|
164
|
-
${checkHTML}
|
|
165
|
-
`;
|
|
166
|
-
|
|
167
|
-
card.addEventListener('click', () => this.#pick(opt));
|
|
152
|
+
card.setAttribute('data-questions-option-id', opt.id);
|
|
153
|
+
// Shared name → siblings form option-card-ui's own radiogroup
|
|
154
|
+
// (single mode). Inert in multi mode: #select() never runs there
|
|
155
|
+
// (see the capture-phase override below), so the shared name has
|
|
156
|
+
// no sibling-uncheck effect to guard against.
|
|
157
|
+
card.name = 'questions-option';
|
|
158
|
+
card.value = opt.id;
|
|
159
|
+
card.heading = opt.label || '';
|
|
160
|
+
card.description = opt.description || '';
|
|
161
|
+
card.icon = opt.icon || '';
|
|
162
|
+
|
|
168
163
|
this.#optionsEl.appendChild(card);
|
|
164
|
+
|
|
165
|
+
// Multi mode: override the radio role option-card-ui sets on
|
|
166
|
+
// connect — this is a checkbox group, and aria-checked (which the
|
|
167
|
+
// primitive keeps in sync with `.checked` regardless of role) is
|
|
168
|
+
// valid for either.
|
|
169
|
+
if (this.multi) card.setAttribute('role', 'checkbox');
|
|
170
|
+
card.checked = this.#selected.has(opt.id);
|
|
171
|
+
card.disabled = this.#answered;
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
if (this.#submitEl) {
|
|
@@ -173,22 +176,62 @@ export class UIAgentQuestions extends UIElement {
|
|
|
173
176
|
}
|
|
174
177
|
}
|
|
175
178
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
179
|
+
/** Single mode — option-card-ui's own radiogroup fired `change` because
|
|
180
|
+
* a card just became selected (it never fires on becoming unselected,
|
|
181
|
+
* so this is always a genuine new pick). */
|
|
182
|
+
#onCardChange = (e) => {
|
|
183
|
+
if (this.multi || this.#answered) return;
|
|
184
|
+
const id = e.detail?.value;
|
|
185
|
+
const opt = this.#options.find((o) => o.id === id);
|
|
186
|
+
if (!opt) return;
|
|
187
|
+
this.#selected.clear();
|
|
188
|
+
this.#selected.add(opt.id);
|
|
189
|
+
this.#answered = true;
|
|
190
|
+
this.#render();
|
|
191
|
+
this.dispatchEvent(new CustomEvent('questions-answer', {
|
|
192
|
+
bubbles: true,
|
|
193
|
+
detail: { selected: [opt.id], option: opt },
|
|
194
|
+
}));
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/** Multi mode — capture-phase click override (see #build). Stops the
|
|
198
|
+
* event before option-card-ui's own bubble-phase click handler runs,
|
|
199
|
+
* since that handler can only select, never deselect. */
|
|
200
|
+
#onOptionsClickCapture = (e) => {
|
|
201
|
+
if (!this.multi) return;
|
|
202
|
+
const card = e.target.closest?.('option-card-ui[data-questions-option]');
|
|
203
|
+
if (!card || !this.#optionsEl.contains(card)) return;
|
|
204
|
+
e.stopPropagation();
|
|
205
|
+
this.#toggleMulti(card);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/** Multi mode — capture-phase keydown override. Space/Enter toggle
|
|
209
|
+
* (mirroring click); arrow keys are swallowed rather than forwarded
|
|
210
|
+
* to option-card-ui's own forced-select navigation, since checkbox
|
|
211
|
+
* groups here never had arrow-key nav (Tab-only, as before). */
|
|
212
|
+
#onOptionsKeydownCapture = (e) => {
|
|
213
|
+
if (!this.multi) return;
|
|
214
|
+
const card = e.target.closest?.('option-card-ui[data-questions-option]');
|
|
215
|
+
if (!card || !this.#optionsEl.contains(card)) return;
|
|
216
|
+
if (e.key === ' ' || e.key === 'Enter') {
|
|
217
|
+
e.preventDefault();
|
|
218
|
+
e.stopPropagation();
|
|
219
|
+
this.#toggleMulti(card);
|
|
220
|
+
return;
|
|
191
221
|
}
|
|
222
|
+
if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
|
|
223
|
+
e.stopPropagation();
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
#toggleMulti(card) {
|
|
228
|
+
if (this.#answered) return;
|
|
229
|
+
const id = card.dataset.questionsOptionId;
|
|
230
|
+
const opt = this.#options.find((o) => o.id === id);
|
|
231
|
+
if (!opt) return;
|
|
232
|
+
if (this.#selected.has(opt.id)) this.#selected.delete(opt.id);
|
|
233
|
+
else this.#selected.add(opt.id);
|
|
234
|
+
this.#render();
|
|
192
235
|
}
|
|
193
236
|
|
|
194
237
|
#onSubmit = () => {
|
|
@@ -18,8 +18,6 @@
|
|
|
18
18
|
--agent-questions-option-bg-sel: var(--md-sys-color-primary-container);
|
|
19
19
|
--agent-questions-option-border: var(--md-sys-color-neutral-outline-variant);
|
|
20
20
|
--agent-questions-option-border-sel: var(--a-primary-bg);
|
|
21
|
-
--agent-questions-check-bg: var(--a-primary-bg);
|
|
22
|
-
--agent-questions-check-fg: var(--md-sys-color-primary-on-primary);
|
|
23
21
|
|
|
24
22
|
/* ── Layout ── */
|
|
25
23
|
--agent-questions-gap: var(--a-space-2);
|
|
@@ -27,9 +25,6 @@
|
|
|
27
25
|
--agent-questions-option-padding: var(--a-space-3);
|
|
28
26
|
--agent-questions-option-radius: var(--a-radius);
|
|
29
27
|
--agent-questions-q-padding: var(--a-space-1);
|
|
30
|
-
--agent-questions-option-body-gap: var(--a-space-0-5);
|
|
31
|
-
/* Component-intrinsic measurement; no --a-space-* equivalent */
|
|
32
|
-
--agent-questions-check-size: 20px;
|
|
33
28
|
|
|
34
29
|
/* ── Motion ── */
|
|
35
30
|
--agent-questions-duration: var(--a-duration-fast);
|
|
@@ -61,108 +56,34 @@
|
|
|
61
56
|
gap: var(--agent-questions-gap);
|
|
62
57
|
}
|
|
63
58
|
|
|
59
|
+
/* gh issue 276 wave 4: options render as <option-card-ui> now — it owns
|
|
60
|
+
its own box model, borders, hover/checked/disabled states, focus ring,
|
|
61
|
+
and heading/description/icon layout via its own @scope'd CSS. This
|
|
62
|
+
re-points its token surface to agent-questions' existing tokens so the
|
|
63
|
+
migration doesn't shift the look; it does not restyle the card itself.
|
|
64
|
+
One deliberate visual difference: the selected indicator is now
|
|
65
|
+
option-card-ui's authentic filled-radio-dot recipe (shared with
|
|
66
|
+
plan-picker-ui) rather than the bespoke check-glyph badge the native
|
|
67
|
+
buttons drew — that badge doesn't exist as an option-card-ui slot. */
|
|
64
68
|
[data-questions-option] {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
border:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
font-family: var(--agent-questions-font-family, var(--a-font-family-ui));
|
|
84
|
-
transition:
|
|
85
|
-
background var(--agent-questions-duration) var(--agent-questions-easing),
|
|
86
|
-
border-color var(--agent-questions-duration) var(--agent-questions-easing);
|
|
87
|
-
min-width: 0;
|
|
69
|
+
--option-card-padding-block: var(--agent-questions-option-padding);
|
|
70
|
+
--option-card-padding-inline: var(--agent-questions-option-padding);
|
|
71
|
+
--option-card-radius: var(--agent-questions-option-radius);
|
|
72
|
+
--option-card-gap-x: var(--agent-questions-option-gap);
|
|
73
|
+
--option-card-bg: var(--agent-questions-option-bg);
|
|
74
|
+
--option-card-border: var(--agent-questions-option-border);
|
|
75
|
+
--option-card-bg-hover: var(--agent-questions-option-bg-hover);
|
|
76
|
+
/* Match prior behavior: hover changed background only, never border. */
|
|
77
|
+
--option-card-border-hover: var(--agent-questions-option-border);
|
|
78
|
+
--option-card-bg-checked: var(--agent-questions-option-bg-sel);
|
|
79
|
+
--option-card-border-checked: var(--agent-questions-option-border-sel);
|
|
80
|
+
--option-card-heading-color: var(--agent-questions-label-fg);
|
|
81
|
+
--option-card-heading-weight: var(--agent-questions-label-weight);
|
|
82
|
+
--option-card-heading-size: var(--agent-questions-label-size);
|
|
83
|
+
--option-card-desc-color: var(--agent-questions-desc-fg);
|
|
84
|
+
--option-card-desc-size: var(--agent-questions-desc-size);
|
|
85
|
+
--option-card-duration: var(--agent-questions-duration);
|
|
86
|
+
--option-card-easing: var(--agent-questions-easing);
|
|
88
87
|
width: 100%;
|
|
89
88
|
}
|
|
90
|
-
|
|
91
|
-
[data-questions-option]:hover {
|
|
92
|
-
background: var(--agent-questions-option-bg-hover);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
[data-questions-option][data-selected] {
|
|
96
|
-
background: var(--agent-questions-option-bg-sel);
|
|
97
|
-
border-color: var(--agent-questions-option-border-sel);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
[data-questions-option][disabled] {
|
|
101
|
-
cursor: default;
|
|
102
|
-
opacity: 0.7;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
[data-questions-option]:focus-visible {
|
|
106
|
-
outline: 2px solid var(--agent-questions-option-border-sel);
|
|
107
|
-
outline-offset: 2px;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
[data-questions-option-icon] {
|
|
111
|
-
flex-shrink: 0;
|
|
112
|
-
/* Align the leading icon's center with the label's first-line
|
|
113
|
-
center. `1.5em` matches the label's effective line-height
|
|
114
|
-
(label is `font-size:` only — line-height inherits as 1.5).
|
|
115
|
-
`var(--a-icon-size)` is icon-ui's rendered height. Centering
|
|
116
|
-
the icon inside one line-box of the label's height puts its
|
|
117
|
-
midpoint on the label's first-line midpoint. We use em on the
|
|
118
|
-
icon's own font-size (which icon-ui keeps at the option's
|
|
119
|
-
font-size) so the offset scales with whatever the consumer
|
|
120
|
-
sets `--agent-questions-label-size` to. */
|
|
121
|
-
margin-top: calc((1.5em - var(--a-icon-size, 1em)) / 2);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
[data-questions-option-body] {
|
|
125
|
-
flex: 1;
|
|
126
|
-
min-width: 0;
|
|
127
|
-
display: flex;
|
|
128
|
-
flex-direction: column;
|
|
129
|
-
gap: var(--agent-questions-option-body-gap);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
[data-questions-option-label] {
|
|
133
|
-
font-size: var(--agent-questions-label-size);
|
|
134
|
-
font-weight: var(--agent-questions-label-weight);
|
|
135
|
-
color: var(--agent-questions-label-fg);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
[data-questions-option-desc] {
|
|
139
|
-
font-size: var(--agent-questions-desc-size);
|
|
140
|
-
color: var(--agent-questions-desc-fg);
|
|
141
|
-
line-height: 1.4;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
[data-questions-option-check] {
|
|
145
|
-
width: var(--agent-questions-check-size);
|
|
146
|
-
height: var(--agent-questions-check-size);
|
|
147
|
-
border-radius: 50%;
|
|
148
|
-
flex-shrink: 0;
|
|
149
|
-
display: flex;
|
|
150
|
-
align-items: center;
|
|
151
|
-
justify-content: center;
|
|
152
|
-
background: transparent;
|
|
153
|
-
opacity: 0;
|
|
154
|
-
transition: opacity var(--agent-questions-duration) var(--agent-questions-easing);
|
|
155
|
-
/* Match the leading-icon offset so a row of [icon · label · check]
|
|
156
|
-
reads as one horizontal line aligned to the label's first line. */
|
|
157
|
-
margin-top: calc((1.5em - var(--agent-questions-check-size)) / 2);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
[data-questions-option][data-selected] [data-questions-option-check] {
|
|
161
|
-
background: var(--agent-questions-check-bg);
|
|
162
|
-
opacity: 1;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
[data-questions-option][data-selected] [data-questions-option-check] icon-ui {
|
|
166
|
-
color: var(--agent-questions-check-fg);
|
|
167
|
-
}
|
|
168
89
|
}
|
|
@@ -132,14 +132,18 @@
|
|
|
132
132
|
the last, bridging plan / thought / iteration interstitials so the run
|
|
133
133
|
reads as one timeline rather than fragmented per-block stubs. Endpoints are
|
|
134
134
|
JS-measured into --reasoning-spine-top / -h (#layoutSpine) — interstitial
|
|
135
|
-
heights are dynamic, so pure CSS can't place them. The x
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
heights are dynamic, so pure CSS can't place them. The x is half the
|
|
136
|
+
component's OWN marker column (--agent-reasoning-marker-w, 1rem — matched
|
|
137
|
+
by the composed <timeline-ui size="sm">, whose dots center in that same
|
|
138
|
+
column). NOT --timeline-marker-w: that token lives inside timeline-ui's
|
|
139
|
+
scope, a DESCENDANT — unreachable here, so reading it silently took the
|
|
140
|
+
1.25rem fallback and drew the spine ~2px right of the dot centers.
|
|
141
|
+
Sits behind the dots (z-index 0 < the dots' z-index 1). The per-item
|
|
142
|
+
timeline connectors are suppressed below so they don't double-draw. */
|
|
139
143
|
[data-reasoning-body][data-spine]::before {
|
|
140
144
|
content: '';
|
|
141
145
|
position: absolute;
|
|
142
|
-
left: calc(var(--
|
|
146
|
+
left: calc(var(--agent-reasoning-marker-w, 1rem) / 2 - var(--agent-reasoning-line-w, 1.5px) / 2);
|
|
143
147
|
top: var(--reasoning-spine-top, 0);
|
|
144
148
|
height: var(--reasoning-spine-h, 0);
|
|
145
149
|
width: var(--agent-reasoning-line-w, 1.5px);
|
|
@@ -50,8 +50,15 @@ export class UIButton extends UIElement {
|
|
|
50
50
|
static template = () => null;
|
|
51
51
|
|
|
52
52
|
connected() {
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
// Respect consumer-authored ARIA — a composite may stamp button-ui as
|
|
54
|
+
// role="tab" (swiper dots), role="gridcell"-hosted day buttons
|
|
55
|
+
// (calendar), etc. Same contract as tabindex below: default only when
|
|
56
|
+
// the author said nothing (operator ruling 2026-07-17, gh#276 wave 4).
|
|
57
|
+
if (!this.hasAttribute('role')) this.setAttribute('role', 'button');
|
|
58
|
+
// Respect a consumer-authored tabindex (e.g. a mouse-only tabindex="-1"
|
|
59
|
+
// affordance like combobox's clear x, or a roving-tabindex composite) —
|
|
60
|
+
// only default the focusable state when the author said nothing.
|
|
61
|
+
if (!this.hasAttribute('tabindex')) this.setAttribute('tabindex', '0');
|
|
55
62
|
this.addEventListener('click', this.#onClick);
|
|
56
63
|
this.addEventListener('keydown', this.#onKey);
|
|
57
64
|
}
|
|
@@ -198,7 +198,7 @@ button-ui[variant="ghost"][color="warning"]:not([disabled]):hover { --button-f
|
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
/* :scope:active moved outside @scope — see Safari 17.x bug note at top. */
|
|
201
|
-
:scope:focus-visible { outline: none; box-shadow: var(--button-focus-ring); }
|
|
201
|
+
:scope:focus-visible { outline: none; box-shadow: var(--button-focus-ring); border-color: transparent; }
|
|
202
202
|
|
|
203
203
|
/* ── Variants (rest state) ──
|
|
204
204
|
Variant/state blocks re-point the component tokens (--button-bg etc.);
|
|
@@ -164,10 +164,13 @@ export class UICalendarGrid extends UIElement {
|
|
|
164
164
|
const rangeTo = (rs && re && rs <= re) ? re : null;
|
|
165
165
|
|
|
166
166
|
// Header — Prev / Title / Next
|
|
167
|
+
// <button-ui variant="ghost" icon="…"> (gh issue 276 wave 4) — button-ui
|
|
168
|
+
// respects the consumer-authored tabindex="-1", so the nav carets stay
|
|
169
|
+
// out of the roving-tabindex day-grid's tab sequence exactly as before.
|
|
167
170
|
let h = `<div data-cal-header>
|
|
168
|
-
<button data-cal-prev
|
|
171
|
+
<button-ui variant="ghost" size="xs" icon="caret-left" data-cal-prev aria-label="Previous month" tabindex="-1"></button-ui>
|
|
169
172
|
<span data-cal-title>${MONTHS_LONG[month]} ${year}</span>
|
|
170
|
-
<button data-cal-next
|
|
173
|
+
<button-ui variant="ghost" size="xs" icon="caret-right" data-cal-next aria-label="Next month" tabindex="-1"></button-ui>
|
|
171
174
|
</div>`;
|
|
172
175
|
|
|
173
176
|
// Day-of-week header
|
|
@@ -183,7 +186,7 @@ export class UICalendarGrid extends UIElement {
|
|
|
183
186
|
// Previous-month trailing days (outside, disabled)
|
|
184
187
|
for (let i = firstDay - 1; i >= 0; i--) {
|
|
185
188
|
const day = daysInPrev - i;
|
|
186
|
-
h += `<button
|
|
189
|
+
h += `<button-ui variant="ghost" data-cal-day data-outside disabled tabindex="-1" text="${day}"></button-ui>`;
|
|
187
190
|
}
|
|
188
191
|
|
|
189
192
|
// Current-month days
|
|
@@ -206,7 +209,6 @@ export class UICalendarGrid extends UIElement {
|
|
|
206
209
|
const isRangeEnd = !!(rangeTo && sameDay(date, rangeTo));
|
|
207
210
|
|
|
208
211
|
const attrs = [
|
|
209
|
-
'type="button"',
|
|
210
212
|
'data-cal-day',
|
|
211
213
|
`data-date="${iso}"`,
|
|
212
214
|
isToday ? 'data-today' : '',
|
|
@@ -219,14 +221,17 @@ export class UICalendarGrid extends UIElement {
|
|
|
219
221
|
`tabindex="${isFocused ? '0' : '-1'}"`,
|
|
220
222
|
].filter(Boolean).join(' ');
|
|
221
223
|
|
|
222
|
-
|
|
224
|
+
// <button-ui variant="ghost" text="…"> (gh issue 276 wave 4) — the day
|
|
225
|
+
// number moves from element text content to the `text` attribute,
|
|
226
|
+
// which button-ui renders via CSS attr(text) on its ::after.
|
|
227
|
+
h += `<button-ui variant="ghost" ${attrs} text="${d}"></button-ui>`;
|
|
223
228
|
}
|
|
224
229
|
|
|
225
230
|
// Next-month leading days
|
|
226
231
|
const totalCells = firstDay + daysInMonth;
|
|
227
232
|
const remaining = (7 - (totalCells % 7)) % 7;
|
|
228
233
|
for (let d = 1; d <= remaining; d++) {
|
|
229
|
-
h += `<button
|
|
234
|
+
h += `<button-ui variant="ghost" data-cal-day data-outside disabled tabindex="-1" text="${d}"></button-ui>`;
|
|
230
235
|
}
|
|
231
236
|
|
|
232
237
|
h += '</div>';
|