@adia-ai/web-components 0.7.6 → 0.7.7
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 +12 -0
- package/components/agent-artifact/agent-artifact.class.js +28 -12
- package/components/agent-artifact/agent-artifact.test.js +61 -0
- package/components/drawer/drawer.css +1 -1
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +1 -1
- package/package.json +1 -1
- package/styles/verse.css +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog — @adia-ai/web-components
|
|
2
2
|
|
|
3
|
+
## [0.7.7] — 2026-06-02
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **`[verse]` register now scales `--a-toggle-size` (check / radio / switch) down with its type + controls.** Companion to the 0.7.6 `[verse]` icon/caret fix (`91460aae2`). `--a-toggle-size` (the check / radio / switch control size) is declared at `:root` (`styles/foundation/size.css` = `--a-size-sm − --a-space-1` = 20px @md) and shifted by the global `[size]` rules (`styles/api/sizing.css` = 16/24 at sm/lg), but was **absent from `styles/verse.css`** — so in a verse context check / radio / switch chrome stayed the `:root`-computed 20px against verse's smaller (18/20/24) controls and 11/12/13px type. Added `--a-toggle-size` to all three verse sites (base `[verse]` + `[verse][size="lg"]` + `[verse][size="sm"]`) on clean `--a-space` rungs, one tier down: verse toggle is now **12 / 16 / 20px** at sm/md/lg (was a flat 20px); the regular register is unchanged (16/20/24). The explicit `[size]` tier rules are **required** — verse's `context` layer shadows the lower-layer global `[size]` `--a-toggle-size` rules, the same reason `--a-size` and the icon/caret tokens need them. `--a-space` rungs (not literal px like icon/caret) because the toggle targets land exactly on rungs, keeping the values density-aware and consistent with verse's inset/gap. Verified by `scripts/qa/register-scale-probe.mjs` (extended with a toggle axis): verse 12/16/20, regular 16/20/24, all other registers/axes unchanged. Showcased on the `check` / `radio` / `switch` demos (a "Typography registers" section, mirroring the card / button / text / field / badge / stat pages). File: `styles/verse.css`.
|
|
8
|
+
- **`<agent-artifact-ui>` no longer strands `.map()`-rendered action buttons in the body.** `#build()` grabbed the author's `slot="primary"` / `slot="secondary"` buttons with a `:scope > [slot]` direct-child query, then `this.innerHTML = ''`. Buttons supplied via interpolation (`${actions.map(…)}`) arrive nested in the template engine's `display:contents` / `role="presentation"` wrapper spans, so the direct-child grab missed them and the wipe left them rendered in the artifact *body* instead of the header actions cluster. `#build()` now partitions children with a wrapper-piercing logical walk (the FB-92/96/98 pattern, mirroring `select-ui` `#logicalOptionChildren`), so interpolated AND static action buttons both reach the actions cluster. +2 regression tests. Surfaced by `audit-wrapper-trap` arm 2. File: `components/agent-artifact/agent-artifact.class.js`.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **`<drawer-ui>` body inset is now register / size-aware (`--drawer-inset` → `var(--a-inset)`).** `--drawer-inset` hardcoded `var(--a-space-4)` (a literal 16px rung); it now reads `var(--a-inset)`, matching its sibling `card-ui` (`--card-inset: var(--a-inset)`). **Behavior-neutral at the default tier** (`--a-inset-md` == `--a-space-4` == 16px); the change is that the drawer body / header / footer inset (header & footer padding derive from `--drawer-inset`) now scales with `[size]` (sm 14 / lg 18), `[verse]` (12), and `[prose]` (40). drawer's `[padding]` / `[bleed]` are layout *modes* (canvas-bg / no-pad), not a value scale — unchanged. Part of the 0.7.7 inset-alignment audit, which left `page` / `canvas` / `table` filter / `richtext` `pre` as intentional fixed rungs (their literals aren't 16px, so a swap would change behavior). Files: `components/drawer/drawer.css`, `components/drawer/drawer.examples.html`.
|
|
13
|
+
- **CDN bundle rebuilt** — `dist/web-components.min.css` regenerated so the `[verse]` `--a-toggle-size` fix + the drawer inset reach `@adia-ai/web-components@0.7` CDN consumers.
|
|
14
|
+
|
|
3
15
|
## [0.7.6] — 2026-06-02
|
|
4
16
|
|
|
5
17
|
### Added
|
|
@@ -139,19 +139,35 @@ export class UIAgentArtifact extends UIElement {
|
|
|
139
139
|
// To keep the Light-DOM approach consistent, we wrap existing children
|
|
140
140
|
// into a body container if one isn't already present.
|
|
141
141
|
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
// Partition the author's children by LOGICAL slot, piercing the template
|
|
143
|
+
// engine's display:contents / role="presentation" wrapper spans. A consumer
|
|
144
|
+
// that interpolates the action buttons — `${actions.map(a => html`<button-ui
|
|
145
|
+
// slot="primary">…`)}` — gets them nested in wrapper spans (core/template.js
|
|
146
|
+
// wrap()), so the old `:scope > [slot="primary"]` direct-child grab returned
|
|
147
|
+
// nothing and `innerHTML = ''` stranded them in the body. This is the
|
|
148
|
+
// wrapper-trap class (FB-92/96/98); walk + pierce so wrapped action buttons
|
|
149
|
+
// reach the header actions cluster. Wrappers are flattened (display:contents
|
|
150
|
+
// is layout-transparent); everything non-action becomes body.
|
|
151
|
+
const primaryBtns = [];
|
|
152
|
+
const secondaryBtns = [];
|
|
153
|
+
const bodyNodes = [];
|
|
154
|
+
const isWrapper = (el) =>
|
|
155
|
+
el.getAttribute('role') === 'presentation' || el.style?.display === 'contents';
|
|
156
|
+
const partition = (nodes) => {
|
|
157
|
+
for (const n of nodes) {
|
|
158
|
+
if (n.nodeType === 1) {
|
|
159
|
+
const el = /** @type {Element} */ (n);
|
|
160
|
+
if (isWrapper(el)) { partition(el.childNodes); continue; } // pierce; drop the wrapper
|
|
161
|
+
const slot = el.getAttribute('slot') || '';
|
|
162
|
+
if (slot === 'primary') { primaryBtns.push(el); continue; }
|
|
163
|
+
if (slot === 'secondary') { secondaryBtns.push(el); continue; }
|
|
164
|
+
}
|
|
165
|
+
bodyNodes.push(n);
|
|
147
166
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
// Clear — we'll rebuild, preserving slotted action buttons
|
|
152
|
-
const primaryBtns = this.querySelectorAll(':scope > [slot="primary"]');
|
|
153
|
-
const secondaryBtns = this.querySelectorAll(':scope > [slot="secondary"]');
|
|
167
|
+
};
|
|
168
|
+
partition(Array.from(this.childNodes));
|
|
154
169
|
|
|
170
|
+
// Clear — we'll rebuild around the captured (now-detached) children.
|
|
155
171
|
this.innerHTML = '';
|
|
156
172
|
|
|
157
173
|
// Header — keyboard-focusable button-style row that toggles collapsed.
|
|
@@ -197,7 +213,7 @@ export class UIAgentArtifact extends UIElement {
|
|
|
197
213
|
// Body
|
|
198
214
|
this.#bodyEl = document.createElement('div');
|
|
199
215
|
this.#bodyEl.setAttribute('data-artifact-body', '');
|
|
200
|
-
for (const n of
|
|
216
|
+
for (const n of bodyNodes) this.#bodyEl.appendChild(n);
|
|
201
217
|
if (this.collapsed) this.#bodyEl.hidden = true;
|
|
202
218
|
|
|
203
219
|
this.append(this.#headerEl, this.#bodyEl);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import '../../core/element.js';
|
|
3
|
+
import './agent-artifact.js';
|
|
4
|
+
|
|
5
|
+
const tick = () => new Promise((r) => queueMicrotask(r));
|
|
6
|
+
|
|
7
|
+
function mount(html) {
|
|
8
|
+
const wrap = document.createElement('div');
|
|
9
|
+
wrap.innerHTML = html;
|
|
10
|
+
document.body.appendChild(wrap);
|
|
11
|
+
return wrap.firstElementChild;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('agent-artifact-ui', () => {
|
|
15
|
+
beforeEach(() => { document.body.innerHTML = ''; });
|
|
16
|
+
|
|
17
|
+
// Baseline — static slot="primary"/"secondary" buttons route into the header
|
|
18
|
+
// actions cluster, and the default-slot content lands in the body.
|
|
19
|
+
it('routes static action buttons into the header actions cluster (not the body)', async () => {
|
|
20
|
+
const a = mount(`
|
|
21
|
+
<agent-artifact-ui title="Build">
|
|
22
|
+
<button-ui slot="primary">Run</button-ui>
|
|
23
|
+
<button-ui slot="secondary">Cancel</button-ui>
|
|
24
|
+
<p>Body content</p>
|
|
25
|
+
</agent-artifact-ui>
|
|
26
|
+
`);
|
|
27
|
+
await tick();
|
|
28
|
+
const actions = a.querySelector('[data-artifact-actions]');
|
|
29
|
+
const body = a.querySelector('[data-artifact-body]');
|
|
30
|
+
expect(actions).toBeTruthy();
|
|
31
|
+
expect(actions.querySelectorAll('[slot="primary"], [slot="secondary"]').length).toBe(2);
|
|
32
|
+
expect(body.querySelectorAll('[slot="primary"], [slot="secondary"]').length).toBe(0);
|
|
33
|
+
expect(body.textContent).toContain('Body content');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Wrapper-trap regression (audit-wrapper-trap arm 2): action buttons rendered
|
|
37
|
+
// via `.map()`/`repeat()` arrive nested in the template engine's
|
|
38
|
+
// display:contents / role="presentation" wrapper spans. The old `:scope >
|
|
39
|
+
// [slot="primary"]` direct-child grab missed them, so `innerHTML = ''`
|
|
40
|
+
// stranded them in the body. They must reach the actions cluster.
|
|
41
|
+
it('routes WRAPPED (interpolated) action buttons into the actions cluster, not the body', async () => {
|
|
42
|
+
const a = mount(`
|
|
43
|
+
<agent-artifact-ui title="Build">
|
|
44
|
+
<span style="display:contents" role="presentation">
|
|
45
|
+
<span style="display:contents" role="presentation"><button-ui slot="primary">Run</button-ui></span>
|
|
46
|
+
<span style="display:contents" role="presentation"><button-ui slot="secondary">Cancel</button-ui></span>
|
|
47
|
+
</span>
|
|
48
|
+
<p>Body content</p>
|
|
49
|
+
</agent-artifact-ui>
|
|
50
|
+
`);
|
|
51
|
+
await tick();
|
|
52
|
+
const actions = a.querySelector('[data-artifact-actions]');
|
|
53
|
+
const body = a.querySelector('[data-artifact-body]');
|
|
54
|
+
expect(actions.querySelectorAll('[slot="primary"], [slot="secondary"]').length).toBe(2);
|
|
55
|
+
// The regression: before the wrapper-pierce fix these landed in the body.
|
|
56
|
+
expect(body.querySelectorAll('[slot="primary"], [slot="secondary"]').length).toBe(0);
|
|
57
|
+
expect(body.textContent).toContain('Body content');
|
|
58
|
+
// No empty wrapper spans left dangling — they're flattened, not re-appended.
|
|
59
|
+
expect(a.querySelectorAll('[role="presentation"]').length).toBe(0);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
/* ── Geometry ── */
|
|
20
20
|
--drawer-width: 24rem;
|
|
21
21
|
--drawer-height: 24rem;
|
|
22
|
-
--drawer-inset: var(--a-
|
|
22
|
+
--drawer-inset: var(--a-inset);
|
|
23
23
|
--drawer-header-pad: var(--drawer-inset);
|
|
24
24
|
--drawer-footer-pad: var(--drawer-inset);
|
|
25
25
|
--drawer-header-gap: var(--a-space-2);
|