@adia-ai/a2ui-retrieval 0.7.18 → 0.7.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/CHANGELOG.md +16 -0
- package/anti-patterns.js +56 -15
- package/catalog.js +32 -2
- package/component-catalog.js +36 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog — @adia-ai/a2ui-retrieval
|
|
2
2
|
|
|
3
|
+
## [0.7.20] — 2026-06-10
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Catalog loaders work on statically-served sites.** `component-catalog.js` + `catalog.js` gain the same static-fetch branch as the compose loaders: one consolidated `catalog-a2ui_0_9.json` request (181 component schemas) replaces the vite-only glob over 138 sidecars.
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- **3 anti-pattern rules from the gen-UI contact-form incident** (these feed BOTH the generation prompt and post-validation): `noCssPropAttrs` (`display=`/`visibility=` as markup attributes — components silently ignore them, so "hidden" content renders visibly), `inputTypeTextarea` (`input-ui[type="textarea"]` → `textarea-ui`), `fieldOwnsLabelHintError` (a sibling `text-ui` label / raw `<label>` / `<small>` error inside `field-ui` → use `label=`/`hint=`/`error=`).
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **`noInventedComponents` known-tags list regenerated from the yaml `tag:` SoT (145 tags).** The hand-rolled list was stale — it was missing `field-ui` (a core primitive), so every canonical labeled form false-positived as an invented component.
|
|
13
|
+
|
|
14
|
+
## [0.7.19] — 2026-06-10
|
|
15
|
+
|
|
16
|
+
### Maintenance
|
|
17
|
+
- **Lockstep version bump only.** No source changes in this package; bumped to maintain the 9-package version coherence enforced by `scripts/release/check-lockstep.mjs`. Substantive v0.7.19 work shipped in icon-ui standalone-import paint fix + late-connect retry (web-components); full 308-chunk embedding vectors (a2ui-corpus). See `packages/web-components/CHANGELOG.md#0719--2026-06-10` for details.
|
|
18
|
+
|
|
3
19
|
## [0.7.18] — 2026-06-09
|
|
4
20
|
|
|
5
21
|
### Maintenance
|
package/anti-patterns.js
CHANGED
|
@@ -67,21 +67,36 @@ const antiPatterns = [
|
|
|
67
67
|
check: (html) => {
|
|
68
68
|
const tags = html.match(/<([a-z]+-(?:n|ui))\b/gi) || [];
|
|
69
69
|
const known = new Set([
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
'
|
|
76
|
-
'
|
|
77
|
-
'
|
|
78
|
-
'
|
|
79
|
-
'
|
|
80
|
-
'
|
|
81
|
-
'
|
|
82
|
-
'
|
|
83
|
-
'
|
|
84
|
-
'
|
|
70
|
+
// Regenerated 2026-06-10 from the per-component yaml `tag:` SoT
|
|
71
|
+
// (node: scan packages/web-components/components/*/*.yaml) + the
|
|
72
|
+
// runtime/infra extras below. The previous hand-rolled list was
|
|
73
|
+
// stale — it was MISSING field-ui (a core primitive), so every
|
|
74
|
+
// canonical labeled form flagged noInventedComponents.
|
|
75
|
+
'accordion-item-ui', 'accordion-ui', 'action-item-ui', 'action-list-ui', 'adia-root', 'agent-artifact-ui',
|
|
76
|
+
'agent-feedback-bar-ui', 'agent-questions-ui', 'agent-reasoning-ui', 'agent-suggestions-ui', 'agent-trace-ui', 'alert-ui',
|
|
77
|
+
'aside-ui', 'avatar-group-ui', 'avatar-ui', 'badge-ui', 'block-ui', 'blockquote-ui',
|
|
78
|
+
'breadcrumb-ui', 'button-ui', 'calendar-grid-ui', 'calendar-picker-ui', 'canvas-ui', 'card-ui',
|
|
79
|
+
'chart-legend-ui', 'chart-ui', 'chat-input-ui', 'chat-thread-ui', 'check-ui', 'code-ui',
|
|
80
|
+
'col-ui', 'color-input-ui', 'color-picker-ui', 'combobox-ui', 'command-ui', 'context-menu-ui',
|
|
81
|
+
'date-range-picker-ui', 'datetime-picker-ui', 'datetime-ui', 'demo-toggle-ui', 'description-list-ui', 'divider-ui',
|
|
82
|
+
'drawer-ui', 'embed-ui', 'empty-state-ui', 'feed-item-ui', 'feed-ui', 'field-ui',
|
|
83
|
+
'fields-ui', 'footer-ui', 'form-container-ui', 'frame-ui', 'grid-ui', 'header-ui',
|
|
84
|
+
'heatmap-ui', 'icon-provider', 'icon-ui', 'image-ui', 'inline-edit-ui', 'inline-message-ui',
|
|
85
|
+
'input-ui', 'inspector-ui', 'integration-card-ui', 'kbd-ui', 'link-ui', 'list-item-ui',
|
|
86
|
+
'list-ui', 'list-window-ui', 'listbox-ui', 'loading-overlay-ui', 'mark-ui', 'menu-divider-ui',
|
|
87
|
+
'menu-item-ui', 'menu-label-ui', 'menu-ui', 'modal-ui', 'nav-group-ui', 'nav-item-ui',
|
|
88
|
+
'nav-ui', 'noodles-ui', 'number-format-ui', 'option-card-ui', 'otp-input-ui', 'page-ui',
|
|
89
|
+
'pagination-ui', 'pane-ui', 'password-strength-ui', 'pipeline-status-ui', 'popover-ui', 'preview-ui',
|
|
90
|
+
'progress-row-ui', 'progress-ui', 'qr-code-ui', 'radio-ui', 'range-ui', 'rating-ui',
|
|
91
|
+
'relative-time-ui', 'richtext-ui', 'router-ui', 'row-ui', 'search-ui', 'section-ui',
|
|
92
|
+
'segment-ui', 'segmented-ui', 'select-ui', 'skeleton-ui', 'skip-nav-ui', 'slider-ui',
|
|
93
|
+
'spinner-ui', 'stack-ui', 'stat-ui', 'step-progress-ui', 'stepper-item-ui', 'stepper-ui',
|
|
94
|
+
'stream-ui', 'swatch-ui', 'swiper-ui', 'switch-ui', 'tab-ui', 'table-toolbar-ui',
|
|
95
|
+
'table-ui', 'tabs-ui', 'tag-ui', 'tags-input-ui', 'text-ui', 'textarea-ui',
|
|
96
|
+
'theme-provider', 'theme-ui', 'time-picker-ui', 'timeline-item-ui', 'timeline-ui', 'toast-ui',
|
|
97
|
+
'toc-ui', 'toggle-group-ui', 'toggle-option-ui', 'toggle-scheme-ui', 'toolbar-group-ui', 'toolbar-ui',
|
|
98
|
+
'tooltip-ui', 'tour-step-ui', 'tour-ui', 'tree-item-ui', 'tree-ui', 'upload-ui',
|
|
99
|
+
'visually-hidden-ui',
|
|
85
100
|
]);
|
|
86
101
|
for (const match of tags) {
|
|
87
102
|
const tag = match.slice(1).toLowerCase();
|
|
@@ -91,6 +106,32 @@ const antiPatterns = [
|
|
|
91
106
|
},
|
|
92
107
|
fix: 'Only use registered AdiaUI types. Check the registry for available components.',
|
|
93
108
|
},
|
|
109
|
+
{
|
|
110
|
+
name: 'noCssPropAttrs',
|
|
111
|
+
description: 'CSS properties (display, visibility) are not markup attributes. Components silently ignore unknown attributes, so display="none" content renders VISIBLY — overlapping its siblings.',
|
|
112
|
+
check: /\s(?:display|visibility)\s*=\s*"/i,
|
|
113
|
+
fix: 'Remove the attribute. To hide content conditionally, omit the element from the tree (or use the component\'s documented state attribute, e.g. [hidden] on plain containers).',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'inputTypeTextarea',
|
|
117
|
+
description: 'input-ui has no type="textarea" — multi-line input is its own component.',
|
|
118
|
+
check: /<input-ui[^>]*\btype\s*=\s*"textarea"/i,
|
|
119
|
+
fix: 'Use <textarea-ui rows="…"> for multi-line text input.',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'fieldOwnsLabelHintError',
|
|
123
|
+
description: 'field-ui OWNS the label / hint / error chrome via its label= / hint= / error= attributes. Emitting a sibling text-ui "label", a raw <label>, or a <small> error inside field-ui double-renders and breaks the label-to-control binding (field-ui binds the first slot-less child as the control — a text-ui label steals that binding).',
|
|
124
|
+
check: (html) => {
|
|
125
|
+
// LLM-emitted label-ish children inside field-ui. The stamped internal
|
|
126
|
+
// label carries data-field-label — exclude it so post-render HTML
|
|
127
|
+
// doesn't false-positive.
|
|
128
|
+
if (/<field-ui[^>]*>\s*(?:<text-ui[^>]*variant\s*=\s*"label"|<label(?![^>]*data-field-label))/i.test(html)) return true;
|
|
129
|
+
// <small> is not an AdiaUI component anywhere; inside field-ui it is
|
|
130
|
+
// always a hand-rolled error line.
|
|
131
|
+
return /<field-ui[^>]*>[\s\S]*?<small[\s>]/i.test(html);
|
|
132
|
+
},
|
|
133
|
+
fix: 'Use <field-ui label="Full Name" hint="…" error="…"><input-ui …/></field-ui> — exactly ONE slot-less child (the control); field-ui renders the label, hint, and error itself.',
|
|
134
|
+
},
|
|
94
135
|
{
|
|
95
136
|
name: 'slotOnContainer',
|
|
96
137
|
description: 'Slot attributes (slot="heading", slot="description", slot="action") must go on the content children inside Header/Footer, not on Header/Footer elements themselves.',
|
package/catalog.js
CHANGED
|
@@ -39,8 +39,19 @@ async function loadSchemas() {
|
|
|
39
39
|
}
|
|
40
40
|
} catch { /* components dir missing */ }
|
|
41
41
|
} else {
|
|
42
|
+
// Browser. Vite rewrites the glob CALL at transform time; on the static
|
|
43
|
+
// deploy that call throws (no Vite — `import.meta.glob` is a compile-time
|
|
44
|
+
// macro, undefined at runtime) → we fall through to fetching the
|
|
45
|
+
// CONSOLIDATED catalog. `typeof import.meta.glob` is undefined in BOTH
|
|
46
|
+
// contexts, so the only reliable discriminator is whether the CALL
|
|
47
|
+
// produced loaders. See composition-library.js for the full rationale.
|
|
48
|
+
let modules = null;
|
|
42
49
|
try {
|
|
43
|
-
|
|
50
|
+
modules = import.meta.glob('../../web-components/components/*/*.a2ui.json', { query: '?raw', import: 'default' });
|
|
51
|
+
} catch { /* no Vite transform — static deploy */ }
|
|
52
|
+
|
|
53
|
+
if (modules && Object.keys(modules).length > 0) {
|
|
54
|
+
// Vite dev — iterate the glob loaders.
|
|
44
55
|
for (const [, loader] of Object.entries(modules)) {
|
|
45
56
|
try {
|
|
46
57
|
const raw = await loader();
|
|
@@ -49,7 +60,26 @@ async function loadSchemas() {
|
|
|
49
60
|
if (tag) schemaByTag.set(tag, schema);
|
|
50
61
|
} catch { /* skip invalid */ }
|
|
51
62
|
}
|
|
52
|
-
}
|
|
63
|
+
} else {
|
|
64
|
+
// Static deploy — fetch the consolidated catalog once. Its `components`
|
|
65
|
+
// values carry the same `x-adiaui.tag` shape as the per-component
|
|
66
|
+
// sidecars (2026-06-10). Relative-to-module so it resolves under any
|
|
67
|
+
// mount point + the dist symlink layout.
|
|
68
|
+
try {
|
|
69
|
+
const url = new URL('../corpus/catalog-a2ui_0_9.json', import.meta.url);
|
|
70
|
+
const res = await fetch(url);
|
|
71
|
+
if (res.ok) {
|
|
72
|
+
const catalog = await res.json();
|
|
73
|
+
const comps = catalog?.components;
|
|
74
|
+
if (comps && typeof comps === 'object') {
|
|
75
|
+
for (const schema of Object.values(comps)) {
|
|
76
|
+
const tag = tagFromSchema(schema);
|
|
77
|
+
if (tag) schemaByTag.set(tag, schema);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch { /* consolidated catalog not present */ }
|
|
82
|
+
}
|
|
53
83
|
}
|
|
54
84
|
}
|
|
55
85
|
|
package/component-catalog.js
CHANGED
|
@@ -24,7 +24,12 @@ const _componentData = new Map();
|
|
|
24
24
|
let _loaded = false;
|
|
25
25
|
let _loadPromise = null;
|
|
26
26
|
|
|
27
|
-
// Vite
|
|
27
|
+
// Vite rewrites this CALL at transform time. On the static deploy the call
|
|
28
|
+
// throws (no Vite) → `_globA2UIModules` null → fetch the CONSOLIDATED
|
|
29
|
+
// catalog (one request) instead of 138 per-component sidecars.
|
|
30
|
+
// `import.meta.glob` is a compile-time macro (undefined at runtime in both
|
|
31
|
+
// Vite and static) — the only reliable discriminator is whether the CALL
|
|
32
|
+
// produced loaders. See composition-library.js for the full rationale.
|
|
28
33
|
let _globA2UIModules = null;
|
|
29
34
|
if (!IS_NODE) {
|
|
30
35
|
try {
|
|
@@ -33,9 +38,20 @@ if (!IS_NODE) {
|
|
|
33
38
|
import: 'default',
|
|
34
39
|
});
|
|
35
40
|
} catch {
|
|
36
|
-
//
|
|
41
|
+
// No Vite transform here (static deploy) — fall through to the fetch branch.
|
|
37
42
|
}
|
|
38
43
|
}
|
|
44
|
+
const HAS_VITE_GLOB =
|
|
45
|
+
!IS_NODE && !!_globA2UIModules && Object.keys(_globA2UIModules).length > 0;
|
|
46
|
+
|
|
47
|
+
// Static-deploy source: the consolidated catalog co-located in the corpus.
|
|
48
|
+
// Its `components` map values are byte-shape-identical to the standalone
|
|
49
|
+
// `.a2ui.json` sidecars (verified 2026-06-10), so one fetch replaces 138.
|
|
50
|
+
// Relative-to-module from retrieval/ → ../a2ui-corpus is `../corpus/`.
|
|
51
|
+
const _CONSOLIDATED_CATALOG_URL =
|
|
52
|
+
!IS_NODE && !HAS_VITE_GLOB
|
|
53
|
+
? new URL('../corpus/catalog-a2ui_0_9.json', import.meta.url)
|
|
54
|
+
: null;
|
|
39
55
|
|
|
40
56
|
async function _loadNode() {
|
|
41
57
|
const fs = await import(/* @vite-ignore */ 'node:fs/promises');
|
|
@@ -103,12 +119,29 @@ async function _loadBrowser() {
|
|
|
103
119
|
}
|
|
104
120
|
}
|
|
105
121
|
|
|
122
|
+
async function _loadStatic() {
|
|
123
|
+
if (!_CONSOLIDATED_CATALOG_URL) return;
|
|
124
|
+
try {
|
|
125
|
+
const res = await fetch(_CONSOLIDATED_CATALOG_URL);
|
|
126
|
+
if (!res.ok) return;
|
|
127
|
+
const catalog = await res.json();
|
|
128
|
+
const comps = catalog?.components;
|
|
129
|
+
if (!comps || typeof comps !== 'object') return;
|
|
130
|
+
for (const data of Object.values(comps)) {
|
|
131
|
+
if (data?.title) _componentData.set(data.title, data);
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
// Consolidated catalog not present / unparseable — leave empty.
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
106
138
|
export async function loadCatalog() {
|
|
107
139
|
if (_loaded) return;
|
|
108
140
|
if (_loadPromise) return _loadPromise;
|
|
109
141
|
_loadPromise = (async () => {
|
|
110
142
|
if (IS_NODE) await _loadNode();
|
|
111
|
-
else await _loadBrowser();
|
|
143
|
+
else if (HAS_VITE_GLOB) await _loadBrowser();
|
|
144
|
+
else await _loadStatic();
|
|
112
145
|
})();
|
|
113
146
|
await _loadPromise;
|
|
114
147
|
_loaded = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/a2ui-retrieval",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.20",
|
|
4
4
|
"description": "AdiaUI A2UI retrieval layer \u2014 catalog lookup, intent classification, domain routing, pattern + anti-pattern matching, clarity + context assembly. Consumed by the compose engine and any A2UI-protocol tooling that needs to reason about user intent against the catalog.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|