@adia-ai/web-components 0.7.13 → 0.7.15
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 +13 -0
- package/components/badge/badge.css +1 -1
- package/components/menu/menu-label.a2ui.json +66 -0
- package/components/menu/menu-label.yaml +55 -0
- package/components/menu/menu.class.js +37 -2
- package/components/menu/menu.css +30 -1
- package/components/menu/menu.d.ts +5 -0
- package/components/menu/menu.js +3 -2
- package/components/menu/menu.test.js +46 -0
- package/components/tag/tag.css +1 -1
- package/core/element.js +14 -0
- package/core/element.test.js +17 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog — @adia-ai/web-components
|
|
2
2
|
|
|
3
|
+
## [0.7.15] — 2026-06-08
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **New `<menu-label-ui>` — labeled group headings inside `<menu-ui>`.** A co-located child of `<menu-ui>` (registered via `defineIfFree('menu-label-ui', UIMenuLabel)` in `menu.js`; `UIMenuLabel` declared in the parent `menu.d.ts` by dts-codegen, like `menu-item`/`menu-divider`) that renders a non-interactive section heading to group `<menu-item-ui>` rows under labeled headings. Files: `components/menu/{menu-label.yaml,menu-label.a2ui.json}`. (Powers the embedded-app HCC demo's grouped / by-flow menu.)
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- **`badge-ui` + `tag-ui` horizontal padding tightened** — `--badge-px` / `--tag-px` `var(--a-space-2)` → `var(--a-space-1-5)` for a snugger chip that reads better in dense rows; vertical padding unchanged.
|
|
10
|
+
|
|
11
|
+
## [0.7.14] — 2026-06-08
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- **`input-ui`/`textarea-ui` `.value` property binding set before upgrade is no longer dropped.** A `.value=${x}` (or any reactive property) assigned during template stamp *before* the element upgrades landed as a pre-upgrade own data-property that `installProps`' `Object.defineProperty` clobbered with the signal default — so the field rendered blank while `value="${x}"` (attribute) worked, and `<select-ui>` was unaffected (inconsistent). `installProps` now captures + reifies the pre-upgrade value (WHATWG upgrade-a-property pattern), fixing it for `value` and every UIElement property. (color-app FEEDBACK-99; covered by `core/element.test.js`.)
|
|
15
|
+
|
|
3
16
|
## [0.7.13] — 2026-06-06
|
|
4
17
|
|
|
5
18
|
### Fixed
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/MenuLabel.json",
|
|
4
|
+
"title": "MenuLabel",
|
|
5
|
+
"description": "Non-interactive group heading inside a `<menu-ui>` — a small uppercase kicker that names the group of `<menu-item-ui>` rows beneath it. Renders with role=\"presentation\" so the menu's roving focus skips it.",
|
|
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
|
+
"component": {
|
|
17
|
+
"const": "MenuLabel"
|
|
18
|
+
},
|
|
19
|
+
"text": {
|
|
20
|
+
"description": "The group label. Stamped into a `<span slot=\"text\">`; consumer-provided slot content wins.",
|
|
21
|
+
"type": "string",
|
|
22
|
+
"default": ""
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"required": [
|
|
26
|
+
"component"
|
|
27
|
+
],
|
|
28
|
+
"unevaluatedProperties": false,
|
|
29
|
+
"x-adiaui": {
|
|
30
|
+
"anti_patterns": [],
|
|
31
|
+
"category": "navigation",
|
|
32
|
+
"composes": [],
|
|
33
|
+
"events": {},
|
|
34
|
+
"examples": [],
|
|
35
|
+
"keywords": [
|
|
36
|
+
"menu-label",
|
|
37
|
+
"menu-group",
|
|
38
|
+
"section-header",
|
|
39
|
+
"menu-heading"
|
|
40
|
+
],
|
|
41
|
+
"name": "UIMenuLabel",
|
|
42
|
+
"related": [
|
|
43
|
+
"Menu",
|
|
44
|
+
"MenuItem",
|
|
45
|
+
"MenuDivider"
|
|
46
|
+
],
|
|
47
|
+
"slots": {
|
|
48
|
+
"text": {
|
|
49
|
+
"description": "Custom label content; falls through to the `[text]` prop if not slotted."
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"states": [],
|
|
53
|
+
"status": "stable",
|
|
54
|
+
"synonyms": {
|
|
55
|
+
"menu-label": [
|
|
56
|
+
"menu-group-label",
|
|
57
|
+
"menu-section-header",
|
|
58
|
+
"menu-heading"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
"tag": "menu-label-ui",
|
|
62
|
+
"tokens": {},
|
|
63
|
+
"traits": [],
|
|
64
|
+
"version": 1
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Edit this file; run `npm run build:components` to regenerate a2ui.json.
|
|
2
|
+
#
|
|
3
|
+
# Authored alongside UIMenuDivider as the labeled-group counterpart: a divider
|
|
4
|
+
# SEPARATES groups, a label NAMES one. Ships as a sibling class in
|
|
5
|
+
# menu/menu.class.js + is registered alongside UIMenu + UIMenuItem.
|
|
6
|
+
|
|
7
|
+
# Child component of <menu-ui>. Surface only inside that parent.
|
|
8
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
9
|
+
name: UIMenuLabel
|
|
10
|
+
tag: menu-label-ui
|
|
11
|
+
status: stable
|
|
12
|
+
component: MenuLabel
|
|
13
|
+
category: navigation
|
|
14
|
+
version: 1
|
|
15
|
+
description: |-
|
|
16
|
+
Non-interactive group heading inside a `<menu-ui>` — a small uppercase kicker that names the group of `<menu-item-ui>` rows beneath it. Renders with role="presentation" so the menu's roving focus skips it.
|
|
17
|
+
|
|
18
|
+
props:
|
|
19
|
+
text:
|
|
20
|
+
description: The group label. Stamped into a `<span slot="text">`; consumer-provided slot content wins.
|
|
21
|
+
type: string
|
|
22
|
+
default: ""
|
|
23
|
+
slots:
|
|
24
|
+
text:
|
|
25
|
+
description: Custom label content; falls through to the `[text]` prop if not slotted.
|
|
26
|
+
|
|
27
|
+
a2ui:
|
|
28
|
+
rules:
|
|
29
|
+
- >-
|
|
30
|
+
<menu-label-ui> MUST be a direct child of <menu-ui>; like menu-item-ui
|
|
31
|
+
and menu-divider-ui it is hoisted into the top-layer popover via
|
|
32
|
+
<menu-ui>'s direct-descendant query — a label nested elsewhere renders
|
|
33
|
+
outside the popover.
|
|
34
|
+
- >-
|
|
35
|
+
Use a label to NAME a group and <menu-divider-ui> to SEPARATE groups.
|
|
36
|
+
The common pattern is one label at the top of each group with dividers
|
|
37
|
+
between groups; avoid a label with no items beneath it.
|
|
38
|
+
- >-
|
|
39
|
+
Non-interactive: role="presentation", no [value], fires no events, and
|
|
40
|
+
the menu's keyboard focus skips it. For a selectable row use
|
|
41
|
+
<menu-item-ui>; for an unavailable action use <menu-item-ui disabled>.
|
|
42
|
+
|
|
43
|
+
keywords:
|
|
44
|
+
- menu-label
|
|
45
|
+
- menu-group
|
|
46
|
+
- section-header
|
|
47
|
+
- menu-heading
|
|
48
|
+
|
|
49
|
+
related:
|
|
50
|
+
- Menu
|
|
51
|
+
- MenuItem
|
|
52
|
+
- MenuDivider
|
|
53
|
+
|
|
54
|
+
synonyms:
|
|
55
|
+
menu-label: [menu-group-label, menu-section-header, menu-heading]
|
|
@@ -93,7 +93,7 @@ export class UIMenu extends UIElement {
|
|
|
93
93
|
// → an empty popover (FEEDBACK-92). Skip anything already relocated into
|
|
94
94
|
// the popover so a re-entrant #show() (reactive re-render while open)
|
|
95
95
|
// doesn't reorder items. Mirrors the descendant query #hide() already uses.
|
|
96
|
-
const items = this.querySelectorAll('menu-item-ui, menu-divider-ui');
|
|
96
|
+
const items = this.querySelectorAll('menu-item-ui, menu-divider-ui, menu-label-ui');
|
|
97
97
|
for (const item of items) {
|
|
98
98
|
if (!pop.contains(item)) pop.appendChild(item);
|
|
99
99
|
}
|
|
@@ -130,7 +130,7 @@ export class UIMenu extends UIElement {
|
|
|
130
130
|
if (pop) {
|
|
131
131
|
if (pop.matches(':popover-open')) pop.hidePopover?.();
|
|
132
132
|
// Move items back to host (before the popover div) so next open can re-adopt them.
|
|
133
|
-
const items = pop.querySelectorAll('menu-item-ui, menu-divider-ui');
|
|
133
|
+
const items = pop.querySelectorAll('menu-item-ui, menu-divider-ui, menu-label-ui');
|
|
134
134
|
for (const item of items) this.insertBefore(item, pop);
|
|
135
135
|
}
|
|
136
136
|
|
|
@@ -352,3 +352,38 @@ export class UIMenuDivider extends UIElement {
|
|
|
352
352
|
this.setAttribute('role', 'separator');
|
|
353
353
|
}
|
|
354
354
|
}
|
|
355
|
+
|
|
356
|
+
export class UIMenuLabel extends UIElement {
|
|
357
|
+
static properties = {
|
|
358
|
+
text: { type: String, default: '', reflect: true },
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
static template = () => null;
|
|
362
|
+
|
|
363
|
+
connected() {
|
|
364
|
+
// Non-interactive group heading. role="presentation" so the menu's roving
|
|
365
|
+
// focus (which queries menu-item-ui only) skips it and a screen reader does
|
|
366
|
+
// not announce it as an actionable row — it visually labels the group below
|
|
367
|
+
// it (use a divider to separate groups; a label to name one).
|
|
368
|
+
this.setAttribute('role', 'presentation');
|
|
369
|
+
this.#stamp();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Mirror menu-item-ui: stamp a <span slot="text"> from [text]; never touch a
|
|
373
|
+
// consumer-provided [slot="text"] (ADR-0010, slot content is source of truth).
|
|
374
|
+
#stamp() {
|
|
375
|
+
if (this.querySelector(':scope > [slot="text"]')) return;
|
|
376
|
+
if (this.text) {
|
|
377
|
+
const span = document.createElement('span');
|
|
378
|
+
span.setAttribute('slot', 'text');
|
|
379
|
+
span.dataset.menuLabelStamped = '1';
|
|
380
|
+
span.textContent = this.text;
|
|
381
|
+
this.appendChild(span);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
render() {
|
|
386
|
+
const span = this.querySelector(':scope > [slot="text"]');
|
|
387
|
+
if (span?.dataset.menuLabelStamped === '1') span.textContent = this.text;
|
|
388
|
+
}
|
|
389
|
+
}
|
package/components/menu/menu.css
CHANGED
|
@@ -34,8 +34,10 @@
|
|
|
34
34
|
are neither `:scope >` children nor under a `:scope > [role]` wrapper. */
|
|
35
35
|
:scope > menu-item-ui,
|
|
36
36
|
:scope > menu-divider-ui,
|
|
37
|
+
:scope > menu-label-ui,
|
|
37
38
|
:scope > [role="presentation"] menu-item-ui,
|
|
38
|
-
:scope > [role="presentation"] menu-divider-ui
|
|
39
|
+
:scope > [role="presentation"] menu-divider-ui,
|
|
40
|
+
:scope > [role="presentation"] menu-label-ui {
|
|
39
41
|
display: none;
|
|
40
42
|
}
|
|
41
43
|
}
|
|
@@ -200,3 +202,30 @@ menu-item-ui[variant="danger"]:hover [slot="icon"] {
|
|
|
200
202
|
border: none;
|
|
201
203
|
}
|
|
202
204
|
}
|
|
205
|
+
|
|
206
|
+
@scope (menu-label-ui) {
|
|
207
|
+
:where(:scope) {
|
|
208
|
+
/* ── Layout ── */
|
|
209
|
+
--menu-label-px: var(--a-space-2);
|
|
210
|
+
--menu-label-pt: var(--a-space-2);
|
|
211
|
+
--menu-label-pb: var(--a-space-1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
:scope {
|
|
215
|
+
box-sizing: border-box;
|
|
216
|
+
display: block;
|
|
217
|
+
padding: var(--menu-label-pt) var(--menu-label-px) var(--menu-label-pb);
|
|
218
|
+
/* Section-header look — matches <text-ui variant="kicker"> (same tokens). */
|
|
219
|
+
font-family: var(--a-kicker-family);
|
|
220
|
+
font-weight: var(--a-kicker-weight);
|
|
221
|
+
font-size: var(--a-kicker-size);
|
|
222
|
+
line-height: var(--a-kicker-leading);
|
|
223
|
+
letter-spacing: var(--a-kicker-tracking);
|
|
224
|
+
text-transform: uppercase;
|
|
225
|
+
color: var(--a-kicker-color);
|
|
226
|
+
cursor: default;
|
|
227
|
+
user-select: none;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
[slot="text"] { display: block; }
|
|
231
|
+
}
|
|
@@ -52,3 +52,8 @@ export class UIMenuItem extends UIElement {
|
|
|
52
52
|
/** Semantic variant. */
|
|
53
53
|
variant: 'default' | 'danger';
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
export class UIMenuLabel extends UIElement {
|
|
57
|
+
/** The group label. Stamped into a `<span slot="text">`; consumer-provided slot content wins. */
|
|
58
|
+
text: string;
|
|
59
|
+
}
|
package/components/menu/menu.js
CHANGED
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { defineIfFree } from '../../core/register.js';
|
|
13
|
-
import { UIMenu, UIMenuItem, UIMenuDivider } from './menu.class.js';
|
|
13
|
+
import { UIMenu, UIMenuItem, UIMenuDivider, UIMenuLabel } from './menu.class.js';
|
|
14
14
|
|
|
15
15
|
defineIfFree('menu-ui', UIMenu);
|
|
16
16
|
defineIfFree('menu-item-ui', UIMenuItem);
|
|
17
17
|
defineIfFree('menu-divider-ui', UIMenuDivider);
|
|
18
|
+
defineIfFree('menu-label-ui', UIMenuLabel);
|
|
18
19
|
|
|
19
|
-
export { UIMenu, UIMenuItem, UIMenuDivider };
|
|
20
|
+
export { UIMenu, UIMenuItem, UIMenuDivider, UIMenuLabel };
|
|
@@ -128,3 +128,49 @@ describe('<menu-ui> collects dynamically-rendered items (FEEDBACK-92)', () => {
|
|
|
128
128
|
expect(order).toEqual(['1', '2']);
|
|
129
129
|
});
|
|
130
130
|
});
|
|
131
|
+
|
|
132
|
+
describe('<menu-label-ui> group heading', () => {
|
|
133
|
+
let host;
|
|
134
|
+
beforeEach(() => { host = document.createElement('div'); document.body.appendChild(host); });
|
|
135
|
+
afterEach(() => host.remove());
|
|
136
|
+
|
|
137
|
+
it('hoists labels into the popover, stamps [text], and marks them presentational', async () => {
|
|
138
|
+
const menu = document.createElement('menu-ui');
|
|
139
|
+
menu.innerHTML = `
|
|
140
|
+
<button-ui slot="trigger" text="Open"></button-ui>
|
|
141
|
+
<menu-label-ui text="Group A"></menu-label-ui>
|
|
142
|
+
<menu-item-ui text="One" value="1"></menu-item-ui>
|
|
143
|
+
<menu-divider-ui></menu-divider-ui>
|
|
144
|
+
<menu-label-ui text="Group B"></menu-label-ui>
|
|
145
|
+
<menu-item-ui text="Two" value="2"></menu-item-ui>`;
|
|
146
|
+
host.appendChild(menu);
|
|
147
|
+
await settle();
|
|
148
|
+
menu.open = true;
|
|
149
|
+
await settle();
|
|
150
|
+
|
|
151
|
+
const pop = menu.querySelector('[data-menu-popover]');
|
|
152
|
+
const labels = pop.querySelectorAll('menu-label-ui');
|
|
153
|
+
expect(labels.length).toBe(2);
|
|
154
|
+
expect(labels[0].getAttribute('role')).toBe('presentation');
|
|
155
|
+
expect(labels[0].textContent.trim()).toBe('Group A');
|
|
156
|
+
expect(labels[1].textContent.trim()).toBe('Group B');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('is non-interactive — outside the menuitem set the roving focus queries', async () => {
|
|
160
|
+
const menu = document.createElement('menu-ui');
|
|
161
|
+
menu.innerHTML = `
|
|
162
|
+
<button-ui slot="trigger" text="Open"></button-ui>
|
|
163
|
+
<menu-label-ui text="Heading"></menu-label-ui>
|
|
164
|
+
<menu-item-ui text="One" value="1"></menu-item-ui>`;
|
|
165
|
+
host.appendChild(menu);
|
|
166
|
+
await settle();
|
|
167
|
+
menu.open = true;
|
|
168
|
+
await settle();
|
|
169
|
+
|
|
170
|
+
const pop = menu.querySelector('[data-menu-popover]');
|
|
171
|
+
// A label is neither a menu-item-ui nor role="menuitem", so #enabledItems()
|
|
172
|
+
// (a `menu-item-ui` query) never lands arrow-key focus on it.
|
|
173
|
+
expect(pop.querySelector('menu-label-ui[role="menuitem"]')).toBeNull();
|
|
174
|
+
expect(pop.querySelectorAll('menu-item-ui').length).toBe(1);
|
|
175
|
+
});
|
|
176
|
+
});
|
package/components/tag/tag.css
CHANGED
|
@@ -11,7 +11,7 @@ tag-ui[removable]:not([disabled]):hover {
|
|
|
11
11
|
--tag-fg: var(--a-fg);
|
|
12
12
|
--tag-border: transparent;
|
|
13
13
|
--tag-radius: var(--a-radius-full);
|
|
14
|
-
--tag-px: var(--a-space-
|
|
14
|
+
--tag-px: var(--a-space-1-5);
|
|
15
15
|
--tag-py: 0.125rem;
|
|
16
16
|
--tag-gap: var(--a-space-1);
|
|
17
17
|
--tag-font: var(--a-ui-sm);
|
package/core/element.js
CHANGED
|
@@ -34,6 +34,19 @@ function installProps(el, props) {
|
|
|
34
34
|
for (const [key, cfg] of Object.entries(props)) {
|
|
35
35
|
const attr = cfg.attribute ?? key.toLowerCase();
|
|
36
36
|
const type = cfg.type ?? String;
|
|
37
|
+
// Lazy property upgrade: a property assigned on the instance BEFORE it
|
|
38
|
+
// upgraded (a template engine sets `.prop=${v}` during stamp — possibly
|
|
39
|
+
// before the element is defined; see the property-binding path in
|
|
40
|
+
// template.js which sets `el[name] = v` regardless of upgrade state)
|
|
41
|
+
// lives as an own data-property. The defineProperty below would clobber it
|
|
42
|
+
// with the signal default. Capture it and reify it into the signal so the
|
|
43
|
+
// value survives upgrade (WHATWG upgrade-a-property pattern). Fixes
|
|
44
|
+
// input-ui/textarea-ui `.value=${x}`. (A reflect:true prop set pre-upgrade
|
|
45
|
+
// reifies its value but won't update its attribute until the next set —
|
|
46
|
+
// setAttribute is a no-op in the upgrade constructor; a rare, fine corner.)
|
|
47
|
+
const hasPre = Object.prototype.hasOwnProperty.call(el, key);
|
|
48
|
+
const preValue = hasPre ? el[key] : undefined;
|
|
49
|
+
if (hasPre) delete el[key];
|
|
37
50
|
const sig = signal(cfg.default ?? undefined);
|
|
38
51
|
el[SIG].set(key, sig);
|
|
39
52
|
Object.defineProperty(el, key, {
|
|
@@ -47,6 +60,7 @@ function installProps(el, props) {
|
|
|
47
60
|
},
|
|
48
61
|
configurable: true,
|
|
49
62
|
});
|
|
63
|
+
if (hasPre) sig.value = preValue;
|
|
50
64
|
}
|
|
51
65
|
}
|
|
52
66
|
|
package/core/element.test.js
CHANGED
|
@@ -63,6 +63,23 @@ describe('UIElement — properties', () => {
|
|
|
63
63
|
expect(el.label).toBe('hi');
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
+
// FEEDBACK-99 (color-app, 2026-06-07): a property assigned on the instance
|
|
67
|
+
// BEFORE upgrade — template engines set `.prop=${v}` during stamp, possibly
|
|
68
|
+
// before the element is defined (see template.js property-binding path) —
|
|
69
|
+
// must survive upgrade, not be clobbered by the signal default. WHATWG
|
|
70
|
+
// "upgrade a property" pattern, here in installProps. Fixes input-ui `.value`.
|
|
71
|
+
it('reifies a property set on the instance before upgrade (lazy upgrade)', () => {
|
|
72
|
+
const tag = `test-el-${++registrationCounter}`;
|
|
73
|
+
const el = document.createElement(tag); // undefined tag → un-upgraded
|
|
74
|
+
document.body.appendChild(el);
|
|
75
|
+
el.value = 'pre-upgrade'; // own data-property, no accessor yet
|
|
76
|
+
class El extends UIElement {
|
|
77
|
+
static properties = { value: { type: String, default: '' } };
|
|
78
|
+
}
|
|
79
|
+
customElements.define(tag, El); // upgrade → installProps runs
|
|
80
|
+
expect(el.value).toBe('pre-upgrade'); // reified, not clobbered to ''
|
|
81
|
+
});
|
|
82
|
+
|
|
66
83
|
it('reflects Boolean properties to HTML attributes when reflect: true', () => {
|
|
67
84
|
class El extends UIElement {
|
|
68
85
|
static properties = { disabled: { type: Boolean, default: false, reflect: true } };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/web-components",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.15",
|
|
4
4
|
"description": "AdiaUI web components \u2014 vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui-runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./index.d.ts",
|