@adia-ai/web-components 0.6.14 → 0.6.16

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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog — @adia-ai/web-components
2
2
 
3
+ ## [0.6.16] — 2026-05-21
4
+
5
+ ### Maintenance
6
+ - **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.6.16 work shipped in `@adia-ai/web-modules`: (1) `<admin-page>` selector relaxed to descendant combinator so router-wrapped layouts no longer collapse to `display: inline` at narrow viewports (was the root of the page-header/KPI overlap regression); (2) `--page-content-header-bg` rebased from `--a-canvas-2` to `--a-canvas-1` so the page-header reads as shell-chrome continuation. See `packages/web-modules/CHANGELOG.md#0616--2026-05-21` for details.
7
+
8
+ ## [0.6.15] — 2026-05-21
9
+
10
+ ### Fixed
11
+ - **`installIconLoaders` / `installIconLoadersForRegistered` no longer silently accept a malformed shape (FB-07).** The previous behavior was `weightModules = { ...EMPTY_WEIGHTS, ...modules }` which spread any top-level keys verbatim. A flat path→svg glob (the documented quick-start shape; see FB-09) had its `/path/foo.svg` keys spread at the top level of `weightModules`, leaving every per-weight slot empty. Every `<icon-ui>` then rendered the icon NAME as visible text in its slot — confusing to debug because the per-icon `warnMissingIcon` console message points at name-resolution issues (Phosphor alias, `registerIcon`, etc.), not at loader-shape issues. The fix shape-detects three cases: (1) per-weight map keyed by `regular`/`thin`/`light`/`bold`/`fill`/`duotone` installs verbatim; (2) flat path→svg map auto-promotes to `{ regular: <flat> }` with a one-time `console.warn` so the caller knows to wrap explicitly (and so bold/fill/etc. aren't silently lost); (3) anything else logs `console.error` with the received key sample and aborts the install (registry stays unchanged). Net: the documented flat shape from `docs/guides/02-quick-start.md` continues to work but emits one diagnostic warn per install; consumers on prior versions writing the flat shape needed to wrap manually to get icons at all. Files: `packages/web-components/core/icons.js` (+59 LOC: `WEIGHT_KEYS` const, `isFlatGlobShape()`, `warnedShapes` WeakSet, three-branch dispatch inside `installIconLoaders`).
12
+ - **`core/icons-phosphor.js` no longer silently swallows `import.meta.glob` failures (FB-08).** Two silent-fail paths fixed. (a) **Empty-glob path** — `import.meta.glob('/node_modules/@phosphor-icons/core/assets/<weight>/*.svg', …)` returns `{}` (not an error) when zero files match. This happens when the consumer's Vite root sits below `node_modules`, or with pnpm/yarn-berry virtual paths, or when this module is loaded from a published package directory (the absolute-style glob is resolved against the Vite root, not this file's directory). Previously `hasViteGlob` was set true and `installIconLoaders({ regular: {}, … })` ran anyway, leaving the registry empty with no diagnostic. The fix sums per-weight entry counts after the `try` block; if all six weights have zero entries, emits a `console.warn` with the canonical manual-install snippet. (b) **Caught-error path** — the bare `catch {}` was scoped to handle "no Vite at all" (`ReferenceError` because `import.meta.glob` doesn't exist in static serving), but it also swallowed every OTHER error type (plugin order issues, Vite-config misconfigs, etc.). The catch now logs non-`ReferenceError` failures via `console.warn` before falling back to the manifest fetch. Files: `packages/web-components/core/icons-phosphor.js` (+38 LOC, -2 LOC).
13
+
14
+ ### Docs
15
+ - **Quick-start + migration guide examples corrected (FB-09).** `docs/guides/02-quick-start.md` lines ~76-81 (boot sequence) and ~126-131 (the dedicated `installIconLoadersForRegistered` section) both showed the flat-glob form passed directly to `installIconLoadersForRegistered`. Both now wrap the glob in `{ regular: … }` to match the canonical contract. New `### Pitfall: flat glob silently auto-promoted (v0.6.15+)` section explains the FB-07 auto-promote behavior and when the per-weight form is strictly required (bold/fill/etc.). `docs/MIGRATION GUIDE.md` lines ~279-282 (the v0.5.3 §154 recommended upgrade example) had the same flat-glob bug — corrected. The skill `adia-ui-kit` v2.9.0 ships the same scaffold-template correction.
16
+
17
+ ### Verification
18
+ - `verify:traits` 56/56 clean.
19
+ - `components.mjs --verify` clean (142 files up-to-date).
20
+ - `smoke:engines` green; `smoke:register-engine` 11/11.
21
+ - `npm run check` omnibus PASSES (LightningCSS minify, link check, primitive composes coverage, demo shell imports, phosphor wiring, MCP server smoke 3/3, register-engine 11/11, traits 56/56).
22
+ - `eval:diff --engine zettel` cov=49% / avg=90 (baseline-identical).
23
+ - Browser-verified four cases against `installIconLoaders`: flat glob → 1 warn + install; garbage shape → 1 error + abort; null → 1 error + abort; per-weight map → silent. Live `/site/examples/admin-dashboard` shows 406/412 icon-ui elements with rendered SVGs — same baseline as pre-fix; substrate change non-regressing.
24
+
3
25
  ## [0.6.14] — 2026-05-21
4
26
 
5
27
  ### Maintenance
@@ -65,11 +65,50 @@ try {
65
65
  duotone: import.meta.glob('/node_modules/@phosphor-icons/core/assets/duotone/*.svg', { query: '?raw', import: 'default' }),
66
66
  };
67
67
  hasViteGlob = true;
68
- } catch {
69
- /* non-Vite static serving manifest fetch below handles it */
68
+ } catch (err) {
69
+ // FB-08: distinguish "no Vite at all" (expected, ReferenceError) from
70
+ // every other error (unexpected, worth logging). ReferenceError is the
71
+ // signal that `import.meta.glob` doesn't exist — i.e. non-Vite static
72
+ // serving, which is what the manifest-fetch fallback below was designed
73
+ // for. Anything else got swallowed silently by the previous bare catch.
74
+ if (err && !(err instanceof ReferenceError)) {
75
+ console.warn(
76
+ `[icons-phosphor] import.meta.glob threw an unexpected error ` +
77
+ `(not a vanilla ReferenceError): ${err.message}. ` +
78
+ `Falling back to manifest fetch. If you're using Vite, this likely ` +
79
+ `means a misconfigured plugin order or glob root.`
80
+ );
81
+ }
70
82
  }
71
83
 
72
84
  if (hasViteGlob) {
85
+ // FB-08: detect the silent-empty-glob case — `import.meta.glob` returns
86
+ // `{}` when zero files match (it does NOT throw). The previous behavior
87
+ // installed empty per-weight maps and the consumer saw an empty icon
88
+ // registry with no diagnostic. This happens when the consumer's Vite
89
+ // root sits below `node_modules`, or with pnpm/yarn-berry virtual paths,
90
+ // or when this file runs from inside a published package directory
91
+ // (its absolute-style glob is resolved relative to the Vite root, not
92
+ // this file's directory).
93
+ const totalIcons = Object.values(weightModules).reduce(
94
+ (sum, weight) => sum + Object.keys(weight).length, 0
95
+ );
96
+ if (totalIcons === 0) {
97
+ console.warn(
98
+ `[icons-phosphor] No Phosphor icons matched the auto-install glob ` +
99
+ `\`/node_modules/@phosphor-icons/core/assets/<weight>/*.svg\`. ` +
100
+ `This usually means your Vite root sits below node_modules, you use ` +
101
+ `pnpm/yarn-berry virtual paths, or this module is loaded from a ` +
102
+ `published package directory. Install loaders manually from your app entry:\n` +
103
+ ` import { installIconLoaders } from '@adia-ai/web-components/core/icons';\n` +
104
+ ` installIconLoaders({\n` +
105
+ ` regular: import.meta.glob(\n` +
106
+ ` '../node_modules/@phosphor-icons/core/assets/regular/*.svg',\n` +
107
+ ` { query: '?raw', import: 'default', eager: true }\n` +
108
+ ` ),\n` +
109
+ ` });`
110
+ );
111
+ }
73
112
  installIconLoaders(weightModules);
74
113
  } else {
75
114
  // Non-Vite static serving: fetch the build-time manifest in the
package/core/icons.js CHANGED
@@ -103,8 +103,27 @@ export function listIcons() {
103
103
  // `installIconLoaders` (called by `icons-phosphor.js` in the zero-config path).
104
104
 
105
105
  const EMPTY_WEIGHTS = { regular: {}, thin: {}, light: {}, bold: {}, fill: {}, duotone: {} };
106
+ const WEIGHT_KEYS = new Set(['regular', 'thin', 'light', 'bold', 'fill', 'duotone']);
106
107
  let weightModules = EMPTY_WEIGHTS;
107
108
 
109
+ /* Shape-detect a flat `import.meta.glob` result: zero recognized weight
110
+ keys (`regular`/`thin`/`light`/`bold`/`fill`/`duotone`) AND at least one
111
+ key that looks like an SVG path (`.svg` suffix). Used to auto-promote
112
+ the documented flat-glob form (FB-09) to `{ regular: <flat> }` so the
113
+ substrate stops silently failing on the most common malformed shape
114
+ (FB-07). */
115
+ function isFlatGlobShape(modules) {
116
+ const keys = Object.keys(modules);
117
+ if (keys.length === 0) return false;
118
+ for (const k of keys) if (WEIGHT_KEYS.has(k)) return false;
119
+ for (const k of keys) if (k.endsWith('.svg')) return true;
120
+ return false;
121
+ }
122
+
123
+ /* Warn-once cache so the FB-07 auto-promote message doesn't spam in HMR
124
+ environments where `installIconLoaders` re-runs on every hot reload. */
125
+ const warnedShapes = new WeakSet();
126
+
108
127
  /* `whenIconRegistryReady` lets a component defer icon-name decisions until
109
128
  the registry is sync-checkable. <icon-ui> doesn't need it — it owns the
110
129
  <name → svg> render and re-stamps elements when the loader map arrives.
@@ -128,7 +147,51 @@ export const whenIconRegistryReady = new Promise((r) => { resolveRegistryReady =
128
147
  * before loaders arrived. Idempotent — calling it twice replaces the map.
129
148
  */
130
149
  export function installIconLoaders(modules) {
131
- weightModules = { ...EMPTY_WEIGHTS, ...modules };
150
+ // FB-07: validate shape before installing. The previous behavior was
151
+ // `weightModules = { ...EMPTY_WEIGHTS, ...modules }` which silently
152
+ // accepted any object — including the documented-but-broken flat
153
+ // path→svg glob (FB-09), which spread path keys at the top level
154
+ // and left every weight slot empty. Three shapes are now recognized:
155
+ //
156
+ // 1. Per-weight map { regular: { path: svg }, bold: { ... }, ... }
157
+ // — canonical, installs verbatim.
158
+ // 2. Flat glob { '/path/foo.svg': '<svg>', ... } — auto-promoted to
159
+ // { regular: <flat> } with a one-time warn so the caller knows
160
+ // to wrap explicitly (and so bold/fill/etc. aren't silently lost).
161
+ // 3. Anything else — logged via console.error, install aborted.
162
+ if (!modules || typeof modules !== 'object') {
163
+ console.error(
164
+ `[icons] installIconLoaders: expected per-weight map { regular: { ... }, bold: { ... }, ... }, ` +
165
+ `got ${modules === null ? 'null' : typeof modules}. Install aborted; registry unchanged.`
166
+ );
167
+ return;
168
+ }
169
+ let installable = modules;
170
+ if (isFlatGlobShape(modules)) {
171
+ if (!warnedShapes.has(modules)) {
172
+ warnedShapes.add(modules);
173
+ console.warn(
174
+ `[icons] installIconLoaders received a flat path→svg map. ` +
175
+ `Auto-promoting to { regular: <flat> }. Wrap explicitly to silence ` +
176
+ `this warning and to load bold/fill/etc. weights:\n` +
177
+ ` installIconLoaders({ regular: import.meta.glob('.../regular/*.svg', { ... }) });`
178
+ );
179
+ }
180
+ installable = { regular: modules };
181
+ } else {
182
+ const hasAnyWeight = Object.keys(modules).some(k => WEIGHT_KEYS.has(k));
183
+ if (!hasAnyWeight) {
184
+ const sampleKeys = Object.keys(modules).slice(0, 5).join(', ');
185
+ console.error(
186
+ `[icons] installIconLoaders: shape unrecognized. ` +
187
+ `Expected per-weight map keyed by regular/thin/light/bold/fill/duotone, ` +
188
+ `or a flat path→svg map. Received keys: ${sampleKeys || '(empty)'}. ` +
189
+ `Install aborted; registry unchanged.`
190
+ );
191
+ return;
192
+ }
193
+ }
194
+ weightModules = { ...EMPTY_WEIGHTS, ...installable };
132
195
  registryReady = true;
133
196
  if (typeof document !== 'undefined') {
134
197
  for (const el of document.querySelectorAll('icon-ui[name]')) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/web-components",
3
- "version": "0.6.14",
3
+ "version": "0.6.16",
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",