@adia-ai/adia-ui-factory 0.8.48 → 0.8.51
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.mcp.json +1 -1
- package/CHANGELOG.md +30 -0
- package/README.md +30 -1
- package/__init__.py +97 -0
- package/hermes-mcp.yaml +10 -0
- package/package.json +11 -2
- package/plugin.yaml +4 -0
- package/prompts/app-audit.md +14 -0
- package/prompts/app-migration.md +15 -0
- package/prompts/app-planning.md +12 -0
- package/prompts/find-unused.md +23 -0
- package/prompts/gen-ui-wiring.md +11 -0
- package/prompts/project-scaffolding.md +13 -0
- package/prompts/surface-qa.md +20 -0
- package/references/component-model.md +1 -1
- package/references/spa-architecture.md +1 -1
- package/scripts/__pycache__/record-lintcpython-314.pyc +0 -0
- package/scripts/adia-contract-check.mjs +30 -6
- package/scripts/adia-probe.mjs +54 -28
- package/skills/find-unused/references/recon-and-audits.md +1 -1
- package/skills/host-wiring/SKILL.md +1 -1
- package/skills/host-wiring/references/mode-wiring-detail.md +1 -2
- package/skills/pattern-catalog/SKILL.md +6 -1
- package/skills/pattern-catalog/references/pattern-index.md +172 -1
- package/skills/screen-composition/SKILL.md +5 -1
- package/skills/table-composition/references/base-table.md +16 -0
- package/skills/token-selection/references/a-alias-layer.md +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adia-ui-kit-factory",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.51",
|
|
4
4
|
"description": "Author and verify apps built ON the adia-ui (@adia-ai) light-DOM web-component framework \u2014 orient, scaffold, compose, wire, verify, and migrate across SPA and SSR rendering modes. Wires the a2ui MCP for catalog retrieval, UI generation, and validation.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Kim",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adia-ui-kit-factory",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.51",
|
|
4
4
|
"description": "Author and verify apps built ON the adia-ui (@adia-ai) light-DOM web-component framework — orient, scaffold, compose, wire, verify, and migrate across SPA and SSR rendering modes. Wires the a2ui MCP for catalog retrieval, UI generation, and validation.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Kim",
|
package/.mcp.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog — adia-ui-kit-factory
|
|
2
2
|
|
|
3
|
+
## [0.8.51] — 2026-08-24
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Hermes + Pi external-distribution manifests (gh#1954) — `plugin.yaml` +
|
|
7
|
+
`__init__.py` + `hermes-mcp.yaml` (Hermes) and `prompts/*.md` + a `"pi"`
|
|
8
|
+
field in `package.json` (Pi), derived from `.claude-plugin/plugin.json` +
|
|
9
|
+
`skills/` + `commands/` + `.mcp.json` via `npm run
|
|
10
|
+
build:harness-manifests` (`check:harness-manifests-fresh` gates
|
|
11
|
+
freshness). Additive: the Claude Code plugin surface is unchanged.
|
|
12
|
+
**MCP caveat, not parity:** Hermes MCP is user-level config — merge the
|
|
13
|
+
generated `hermes-mcp.yaml` into `~/.hermes/config.yaml` by hand. Pi core
|
|
14
|
+
has no native MCP — the `adia-gen-ui` server only reaches Pi through the
|
|
15
|
+
third-party `pi-mcp-adapter` reading this plugin's `.mcp.json` unmodified.
|
|
16
|
+
See the README's "Non-Claude harnesses" section before promising Pi/Hermes
|
|
17
|
+
parity for this plugin's headline MCP-wiring feature.
|
|
18
|
+
|
|
19
|
+
### Maintenance
|
|
20
|
+
- **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
|
|
21
|
+
- **`scripts/` touched in this release window** (2 file(s), e.g. `scripts/adia-contract-check.mjs`) — carried by the entries above.
|
|
22
|
+
|
|
23
|
+
## [0.8.50] — 2026-08-23
|
|
24
|
+
|
|
25
|
+
### Maintenance
|
|
26
|
+
- **Lockstep bump riding the v0.8.50 cut** (`scripts/release/check-lockstep.mjs`); the headline v0.8.50 work ships in @adia-ai/web-components (prose/verse sheet removal gh#1885 / PR #1918, input/search fill-default posture gh#1856 — see `packages/web-components/CHANGELOG.md#0850--2026-08-23`). Package-local changes are listed below.
|
|
27
|
+
- **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
|
|
28
|
+
- **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
|
|
29
|
+
|
|
30
|
+
### Docs
|
|
31
|
+
- **`references/` + `skills/` docs refreshed for the release window** — `component-model.md` and `spa-architecture.md` plus the `find-unused` (`recon-and-audits.md`), `host-wiring` (`SKILL.md`, `mode-wiring-detail.md`), and `table-composition` (`base-table.md`) skill references updated to current framework contracts (incl. the gh#1918 `[prose]`/`[verse]` removal and gh#1856 fill-default posture).
|
|
32
|
+
|
|
3
33
|
## [0.8.48] — 2026-08-23
|
|
4
34
|
|
|
5
35
|
### Added
|
package/README.md
CHANGED
|
@@ -98,7 +98,7 @@ surface-qa-agent) · `component-model.md` · the `references/contracts/` twins.
|
|
|
98
98
|
|
|
99
99
|
## MCP
|
|
100
100
|
|
|
101
|
-
`adia-gen-ui` server pinned: `@adia-ai/mcp@0.8.
|
|
101
|
+
`adia-gen-ui` server pinned: `@adia-ai/mcp@0.8.51` (tool SoT:
|
|
102
102
|
`packages/gen-ui/mcp/TOOLS.md` — the `gen-ui` section; stability rule in
|
|
103
103
|
`references/contracts/`; pin lives in `.mcp.json` — `check:plugin-count-claims`
|
|
104
104
|
guards this README copy against it). The server is `adia-mcp gen-ui`,
|
|
@@ -108,6 +108,35 @@ before either ever published; the pin name and the `mcpServers` key flipped
|
|
|
108
108
|
in the P7 release commit per the 2026-08-14 config-key ruling — new entries
|
|
109
109
|
for the other subcommands use `adia-protocol` / `adia-factory`).
|
|
110
110
|
|
|
111
|
+
## Non-Claude harnesses
|
|
112
|
+
|
|
113
|
+
External install beyond Claude Code (AGENTS.md's own "Non-Claude harnesses"
|
|
114
|
+
section carries the shared degradation table this summarizes). **The MCP
|
|
115
|
+
story is the one place neither non-Claude harness matches Claude Code's
|
|
116
|
+
plug-and-play install** — read this section before promising Pi/Hermes
|
|
117
|
+
support for this plugin specifically, since its whole value proposition is
|
|
118
|
+
wiring the `adia-gen-ui` MCP server.
|
|
119
|
+
|
|
120
|
+
- **Codex** (gh#1888): `.codex-plugin/plugin.json` + per-skill
|
|
121
|
+
`agents/openai.yaml`, derived; `mcpServers` points at the same
|
|
122
|
+
`.mcp.json` Claude Code uses. No manifest key for hooks/commands/agents —
|
|
123
|
+
a command degrades to invoking its skill directly.
|
|
124
|
+
- **Hermes** (gh#1954): `plugin.yaml` + `__init__.py` at this package's
|
|
125
|
+
root — point Hermes's plugin loading at the installed
|
|
126
|
+
`node_modules/@adia-ai/adia-ui-factory` directory. **MCP is user-level
|
|
127
|
+
config in Hermes, not a plugin field** — merge the generated
|
|
128
|
+
`hermes-mcp.yaml` into your own `~/.hermes/config.yaml` by hand; there is
|
|
129
|
+
no automatic wiring. No hooks/agents surface.
|
|
130
|
+
- **Pi** (gh#1954): discovered via the `pi-package` keyword and the `"pi"`
|
|
131
|
+
field in `package.json`; `prompts/*.md` gives every `/command` a genuine
|
|
132
|
+
Pi prompt template. **Pi core has no native MCP support at all** — the
|
|
133
|
+
`adia-gen-ui` server only reaches Pi through the third-party
|
|
134
|
+
[`pi-mcp-adapter`](https://pi.dev) package, which reads this plugin's
|
|
135
|
+
`.mcp.json` unmodified (same shape, no substitution needed). Install that
|
|
136
|
+
adapter first; without it, this plugin's headline feature — the a2ui MCP
|
|
137
|
+
wiring — silently does nothing on a stock Pi install. No hooks/agents
|
|
138
|
+
surface.
|
|
139
|
+
|
|
111
140
|
## Requirements
|
|
112
141
|
|
|
113
142
|
Python 3 (hook) + Node 22. Installs standalone in consumer repos (no monorepo
|
package/__init__.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""adia-ui-kit-factory — generated by scripts/build/harness-manifests.mjs. Do not edit."""
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def register(ctx):
|
|
8
|
+
ctx.register_skill(
|
|
9
|
+
name="app-audit",
|
|
10
|
+
path=os.path.join(_HERE, "skills", "app-audit", "SKILL.md"),
|
|
11
|
+
description="Diagnose an EXISTING AdiaUI consumer codebase before touching it — recon, classify manifest/version/spec/capability drift, root-cause a DRIFT-caused wrong-output symptom via four-layer triage (skill/codebase/substrate/spec) instead of patching the surface. Use for \"audit this UI\", \"is this up to date\", \"what should we migrate\", \"doesn't match the reference\", \"looks unstyled/off-brand\", or a suspected-drifted @adia-ai/* consumer's version health check. Produces a ranked, evidence-cited report; hands CONFIRMED sweeps to a builder. NOT greenfield planning (app-planning) or confirmed migration (app-migration); NOT a live blank-screen/console-error check (surface-qa) or chart-specific render behavior (chart-selection); NOT a duplication review (project-scaffolding).",
|
|
12
|
+
)
|
|
13
|
+
ctx.register_skill(
|
|
14
|
+
name="app-migration",
|
|
15
|
+
path=os.path.join(_HERE, "skills", "app-migration", "SKILL.md"),
|
|
16
|
+
description="Migrates a consumer adia-ui app. Use when asked to \"migrate to v0.X\", \"upgrade @adia-ai/web-components\", \"fix breaking changes\", \"sweep deprecated attributes\", port @agent-ui-kit/foreign code, or change SPA↔SSR mode — grep audit, mechanical sweep, verify gates. NOT for authoring the MIGRATION GUIDE or cutting releases (package-release, forge plugin).",
|
|
17
|
+
)
|
|
18
|
+
ctx.register_skill(
|
|
19
|
+
name="app-planning",
|
|
20
|
+
path=os.path.join(_HERE, "skills", "app-planning", "SKILL.md"),
|
|
21
|
+
description="Cold-start router for adia-ui consumer-app work. Use when the user asks to \"build/start an adia-ui app\", \"add a surface\", \"orient in this repo\" — run FIRST, also on any task whose mode/shape/shell is undecided — classifies four axes on cited signals, emits an Orientation Record, routes onward. NOT for framework-monorepo internals (adia-ui-forge).",
|
|
22
|
+
)
|
|
23
|
+
ctx.register_skill(
|
|
24
|
+
name="chart-selection",
|
|
25
|
+
path=os.path.join(_HERE, "skills", "chart-selection", "SKILL.md"),
|
|
26
|
+
description="Which adia-ui component renders a chart, graph, sparkline, gauge, or heatmap, and how to wire its data/legend/theming. Use for \"add a chart\", \"show a graph\", \"visualize this data\", \"add a sparkline/gauge/heatmap\", or \"wire a chart legend\". NOT for the surrounding screen (screen-composition), color tokens (token-selection), or data fetching (data-wiring).",
|
|
27
|
+
)
|
|
28
|
+
ctx.register_skill(
|
|
29
|
+
name="data-wiring",
|
|
30
|
+
path=os.path.join(_HERE, "skills", "data-wiring", "SKILL.md"),
|
|
31
|
+
description="Data, state & hydration in an adia-ui app. Use for \"wire up the data\" / \"manage state\" / \"hydrate the island\": pattern choice (signals · shared app store · Service/Command · DataClient · property-API · data-*), hydration per mode (SPA/SSR/hybrid). NOT for host bootstrap/registration (host-wiring) or screen markup (screen-composition).",
|
|
32
|
+
)
|
|
33
|
+
ctx.register_skill(
|
|
34
|
+
name="domain-planning",
|
|
35
|
+
path=os.path.join(_HERE, "skills", "domain-planning", "SKILL.md"),
|
|
36
|
+
description="Reasons through a PRD, spec, mockup, schema, or role/user-story BEFORE any UI is chosen — resolves intent, domain entities, roles, tasks, and decisions, then scores a semantic wireframe against the gate dimensions. Use when the input is spec-shaped (not a signed-off wireframe) and no Domain Plan exists yet for it. Preloaded by app-planning-agent; produces the Orientation Record's Domain Plan block. NOT for composing the UI itself (screen-composition) or classifying rendering mode/shape/shell (app-planning).",
|
|
37
|
+
)
|
|
38
|
+
ctx.register_skill(
|
|
39
|
+
name="find-unused",
|
|
40
|
+
path=os.path.join(_HERE, "skills", "find-unused", "SKILL.md"),
|
|
41
|
+
description="Finds what a non-breaking @adia-ai upgrade shipped that this app never picked up — the opt-in layers nothing imports, the local workarounds a fixed bug made redundant, retired enum values still sitting in stored state, and the fixes that never reached MIGRATION.md. Use after \"we bumped and nothing broke\", \"what are we missing from 0.8.x\", \"are we actually using what we ship\", \"why is [scale]/[theme] doing nothing\", or when a PATCH span needs adoption rather than repair. NOT for breaking-change repair (app-migration); NOT for a first-time install (adia-scaffold); NOT for diagnosing a broken surface (app-audit).",
|
|
42
|
+
)
|
|
43
|
+
ctx.register_skill(
|
|
44
|
+
name="gen-ui-wiring",
|
|
45
|
+
path=os.path.join(_HERE, "skills", "gen-ui-wiring", "SKILL.md"),
|
|
46
|
+
description="Authors a runtime generative-UI experience on the a2ui runtime — mounts a2ui-root/gen-root, wires generate_ui + data resolvers, grounds generation in a corpus (harvest chunks from our own pages, point retrieval at it). Use when END USERS generate UI at runtime. NOT for one-shot 'generate UI for X' screen asks (screen-composition), chat features (llm-wiring), or pipeline/MCP tool ops (adia-forge's a2ui-maintenance).",
|
|
47
|
+
)
|
|
48
|
+
ctx.register_skill(
|
|
49
|
+
name="host-wiring",
|
|
50
|
+
path=os.path.join(_HERE, "skills", "host-wiring", "SKILL.md"),
|
|
51
|
+
description="Wires an adia-ui app's host — picks SPA/SSR/hybrid, then that mode's registration, CSS cascade, routing ownership. Use for \"wire the host\", \"set up index.html\", unstyled or non-upgrading elements, \"HTMLElement is not defined\" under Next/Nuxt/SvelteKit/Astro. NOT for screen UI (screen-composition), data/state (data-wiring), app layout (project-scaffolding).",
|
|
52
|
+
)
|
|
53
|
+
ctx.register_skill(
|
|
54
|
+
name="llm-wiring",
|
|
55
|
+
path=os.path.join(_HERE, "skills", "llm-wiring", "SKILL.md"),
|
|
56
|
+
description="The @adia-ai/llm client contract — chat/streamChat streaming, StreamChunk, smart-proxy vs passthrough key security, chat-shell wiring. Use when adding a chat box or any LLM feature to an adia-ui app. NOT for runtime gen-UI (gen-ui-wiring), one-shot intent→screen composition (screen-composition), or @adia-ai/llm internals (adia-forge's llm-client-maintenance).",
|
|
57
|
+
)
|
|
58
|
+
ctx.register_skill(
|
|
59
|
+
name="pattern-catalog",
|
|
60
|
+
path=os.path.join(_HERE, "skills", "pattern-catalog", "SKILL.md"),
|
|
61
|
+
description="Index of adia-ui's pre-assembled surfaces — hundreds of patterns and template screens (auth, registration, onboarding, settings, dashboards) — see references/pattern-index.md for the current count. Use BEFORE composing any screen from primitives, or when asked \"is there an existing pattern/template for X\", \"start from a pattern\", \"what patterns exist\". Answers and points at source — NOT for composing new UI (screen-composition), shell chrome (shell-selection), or authoring new patterns (primitive-authoring).",
|
|
62
|
+
)
|
|
63
|
+
ctx.register_skill(
|
|
64
|
+
name="project-scaffolding",
|
|
65
|
+
path=os.path.join(_HERE, "skills", "project-scaffolding", "SKILL.md"),
|
|
66
|
+
description="Lays out an adia-ui app's on-disk structure — picks the project shape (single-surface / rollup / shared-foundation) and page form, scaffolds via scripts/adia-scaffold, gates on the structure rubric. Use for \"scaffold an app\", \"add a page/surface/component\" WHEN mode/shape are already decided, \"audit its structure\". If mode/shape/shell are still undecided, app-planning classifies first. NOT for the UI inside a screen (screen-composition) or host wiring (host-wiring).",
|
|
67
|
+
)
|
|
68
|
+
ctx.register_skill(
|
|
69
|
+
name="screen-composition",
|
|
70
|
+
path=os.path.join(_HERE, "skills", "screen-composition", "SKILL.md"),
|
|
71
|
+
description="Composes adia-ui screens from light-DOM catalog primitives — discovers tags/props via the a2ui MCP, themes via --a-* tokens. Use when asked to build or 'generate UI for' a screen, page, form, dashboard, or nav pattern, or when a PRD/spec/mockup needs UI. NOT for shell chrome (shell-selection), host wiring (host-wiring), runtime gen-UI (gen-ui-wiring).",
|
|
72
|
+
)
|
|
73
|
+
ctx.register_skill(
|
|
74
|
+
name="shell-selection",
|
|
75
|
+
path=os.path.join(_HERE, "skills", "shell-selection", "SKILL.md"),
|
|
76
|
+
description="Picks and composes an adia-ui page-chrome shell (@adia-ai/web-modules): admin (app frame), chat (LLM), editor (canvas+panes), simple (marketing/error), or embed. Use when asked to \"use a shell\", \"sidebar + topbar layout\", \"embed this surface\", or on shell markup debugging. NOT for screens inside it (screen-composition) or host/SSR wiring (host-wiring).",
|
|
77
|
+
)
|
|
78
|
+
ctx.register_skill(
|
|
79
|
+
name="surface-qa",
|
|
80
|
+
path=os.path.join(_HERE, "skills", "surface-qa", "SKILL.md"),
|
|
81
|
+
description="Browser-QA gate for a CONSUMER app's adia-ui surfaces — renders headless with zero console/page errors, non-zero bounding boxes, and a screenshot actually read, plus AdiaUI a11y checks. Use when shipping a surface, on \"verify/QA this page\" or a page that renders blank/empty, or when \"tests pass\" is the only evidence. NOT for composing/fixing UI (screen-composition), structural lint (adia-lint hook), or the framework repo's own dogfood/ demo-page sweep (forge's demo-audit).",
|
|
82
|
+
)
|
|
83
|
+
ctx.register_skill(
|
|
84
|
+
name="table-composition",
|
|
85
|
+
path=os.path.join(_HERE, "skills", "table-composition", "SKILL.md"),
|
|
86
|
+
description="Answers how to display/organize tabular data with table-ui: contained vs uncontained chrome, [raw] (consumer-owned body, a separate axis), opt-in striped rows, and resize/sort defaults that flip between JS .columns and declarative col-def forms, plus inline-edit-grid and tree/hierarchical-row patterns. Use for \"how do I show a table\", \"add sorting/filtering\", \"make this table striped\", \"columns aren't resizable\", \"card vs bare table\", \"editable/spreadsheet grid\", \"tree table / nested rows\", \"huge list of rows\". NOT for composing the surrounding screen (screen-composition), data wiring (data-wiring), OTHER non-table patterns (pattern-catalog), or 1000+-row virtualized lists (list-window-ui).",
|
|
87
|
+
)
|
|
88
|
+
ctx.register_skill(
|
|
89
|
+
name="token-selection",
|
|
90
|
+
path=os.path.join(_HERE, "skills", "token-selection", "SKILL.md"),
|
|
91
|
+
description="Answers which color token/role an adia-ui surface should use — role grammar, pairing laws, state families, the --a-* alias layer. Use when asked \"which token for this background/text/hover/border\" or \"--a-* or --md-sys-color-*\". ANSWERS only. NOT for composing the screen (screen-composition) or designing/verifying palettes (framework-side).",
|
|
92
|
+
)
|
|
93
|
+
ctx.register_skill(
|
|
94
|
+
name="wave-coordination",
|
|
95
|
+
path=os.path.join(_HERE, "skills", "wave-coordination", "SKILL.md"),
|
|
96
|
+
description="Team-pattern mechanics for a multi-screen build under ui-architect's GEAR 2 — who merges parallel screen-composition-agent branches and when, PR/branch mechanics, shared-file conflict rules for shared-foundation shapes, and how to size a wave. Use when planning or running a GEAR-2 dispatch: \"how do two parallel screen-composition-agent branches come back together\", \"who merges\", \"how big should a wave be\", \"what happens when two screens touch the same shared file\". NOT for picking a project's shape (project-scaffolding, which this skill's conflict rules build on) or composing one screen (screen-composition).",
|
|
97
|
+
)
|
package/hermes-mcp.yaml
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Generated from .mcp.json — Hermes MCP is user-level config,
|
|
2
|
+
# not a plugin manifest field. Merge this fragment into your own
|
|
3
|
+
# ~/.hermes/config.yaml (see this plugin's README, "Non-Claude harnesses").
|
|
4
|
+
mcp_servers:
|
|
5
|
+
adia-gen-ui:
|
|
6
|
+
command: "npx"
|
|
7
|
+
args:
|
|
8
|
+
- "-y"
|
|
9
|
+
- "@adia-ai/mcp@0.8.51"
|
|
10
|
+
- "gen-ui"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/adia-ui-factory",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.51",
|
|
4
4
|
"description": "Author and verify apps built ON the adia-ui (@adia-ai) light-DOM web-component framework — orient, scaffold, compose, wire, verify, and migrate across SPA and SSR rendering modes. Wires the a2ui MCP for catalog retrieval, UI generation, and validation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adia-ui",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"gen-ui",
|
|
9
9
|
"web-components",
|
|
10
10
|
"app-authoring",
|
|
11
|
-
"consumer"
|
|
11
|
+
"consumer",
|
|
12
|
+
"pi-package"
|
|
12
13
|
],
|
|
13
14
|
"license": "MIT",
|
|
14
15
|
"author": {
|
|
@@ -18,15 +19,23 @@
|
|
|
18
19
|
"bugs": {
|
|
19
20
|
"email": "kim.granlund@adia.ai"
|
|
20
21
|
},
|
|
22
|
+
"pi": {
|
|
23
|
+
"skills": ["./skills"],
|
|
24
|
+
"prompts": ["./prompts"]
|
|
25
|
+
},
|
|
21
26
|
"files": [
|
|
22
27
|
".claude-plugin",
|
|
23
28
|
".codex-plugin",
|
|
24
29
|
"skills",
|
|
25
30
|
"agents",
|
|
26
31
|
"commands",
|
|
32
|
+
"prompts",
|
|
27
33
|
"references",
|
|
28
34
|
"scripts",
|
|
29
35
|
"hooks",
|
|
36
|
+
"plugin.yaml",
|
|
37
|
+
"__init__.py",
|
|
38
|
+
"hermes-mcp.yaml",
|
|
30
39
|
".mcp.json",
|
|
31
40
|
"README.md",
|
|
32
41
|
"CHANGELOG.md",
|
package/plugin.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
name: "adia-ui-kit-factory"
|
|
2
|
+
version: "0.8.51"
|
|
3
|
+
description: "Author and verify apps built ON the adia-ui (@adia-ai) light-DOM web-component framework — orient, scaffold, compose, wire, verify, and migrate across SPA and SSR rendering modes. Wires the a2ui MCP for catalog retrieval, UI generation, and validation."
|
|
4
|
+
manifest_version: 1
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Probe this repo's adia-ui project context — declared vs installed versions, rendering-mode/framework signals, shells in use, theming knobs, MCP pin, monorepo misroute flag."
|
|
3
|
+
argument-hint: "[dir]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Probe the project context. **$ARGUMENTS**
|
|
7
|
+
|
|
8
|
+
Run `python3 "${CLAUDE_PLUGIN_ROOT}/scripts/adia-info" $ARGUMENTS` and present
|
|
9
|
+
the JSON as a short read: each field with the signal it derived from and
|
|
10
|
+
the decision it drives (screen-composition §Current project context carries the
|
|
11
|
+
field→decision table). An `installedVersion` that is null or mismatched vs
|
|
12
|
+
`adiaPackages` means `npm install` before trusting anything else;
|
|
13
|
+
`isFrameworkMonorepo: true` means this is framework territory — route to
|
|
14
|
+
the adia-ui-forge plugin, not this one.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Migrate consumer code across @adia-ai versions (or port a foreign codebase to adia-ui) — guide-driven audit, per-cluster consent, verify gates, Migration Report."
|
|
3
|
+
argument-hint: "[target version]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Migrate this codebase. **$ARGUMENTS**
|
|
7
|
+
|
|
8
|
+
Invoke **`app-migration`**: read the shipped guide for the span
|
|
9
|
+
(`node_modules/@adia-ai/web-components/MIGRATION.md` — install/upgrade the
|
|
10
|
+
target version first so the guide covers it; monorepo canon:
|
|
11
|
+
`.claude/docs/MIGRATION GUIDE.md`), audit every breaking item with counts
|
|
12
|
+
before any change, present the per-cluster consent options (sweep ·
|
|
13
|
+
show-diff · skip · manual — a blanket approval never covers judgment
|
|
14
|
+
items), run the verify gates, and return the Migration Report (the skill's
|
|
15
|
+
§Deliverable) with every cluster's consent decision recorded.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Orient in an existing adia-ui app — classify rendering mode, project shape, shell, and state; produce the Orientation Record."
|
|
3
|
+
argument-hint: "[path or question]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Orient in this repo. **$ARGUMENTS**
|
|
7
|
+
|
|
8
|
+
Invoke **`app-planning`** and produce the Orientation Record in the skill's
|
|
9
|
+
own contract shape (Rendering mode · Project shape · Shell · Task ·
|
|
10
|
+
Screen plan (start mode) · → Route · Verify target · Open questions), each
|
|
11
|
+
axis citing its signal. Self-check the record with
|
|
12
|
+
`python3 "${CLAUDE_PLUGIN_ROOT}/scripts/record-lint" -` before returning it.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Find what a clean @adia-ai upgrade left unused — opt-in layers nothing imports, workarounds a fixed bug made redundant, retired values in stored state, shipped advice that is wrong for this app."
|
|
3
|
+
argument-hint: "[target version]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Find what this app is not using. **$ARGUMENTS**
|
|
7
|
+
|
|
8
|
+
Invoke **`find-unused`**: enumerate every `@adia-ai/*` package each manifest
|
|
9
|
+
declares (never assume `web-components`), recon the three versions that differ
|
|
10
|
+
per package — declared range, installed, and the target: **the version in
|
|
11
|
+
`$ARGUMENTS` when one was given, registry latest only as the default**. Read
|
|
12
|
+
every `node_modules/@adia-ai/*/CHANGELOG.md` section across the
|
|
13
|
+
installed→target span, then run all four audits — inert opt-in layers, redundant local
|
|
14
|
+
workarounds, retired enum values sitting in persisted state, and shipped
|
|
15
|
+
advice that does not fit this app. Report counts and file:line before
|
|
16
|
+
proposing any edit, split mechanical items from judgment items and take
|
|
17
|
+
consent per item, verify each adoption with a check that would fail on
|
|
18
|
+
revert, and write the Adoption Record.
|
|
19
|
+
|
|
20
|
+
Reach for **`app-migration`** instead when the span carries actual breaking
|
|
21
|
+
changes — that skill is guide-driven and sweeps them with per-cluster
|
|
22
|
+
consent. This one runs when nothing broke and the question is what went
|
|
23
|
+
unused.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Author a generative-UI experience — wire the a2ui runtime, generate_ui/refine_ui via the MCP, choose core vs custom corpus."
|
|
3
|
+
argument-hint: "[experience or surface]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Build a gen-UI experience. **$ARGUMENTS**
|
|
7
|
+
|
|
8
|
+
Invoke **`gen-ui-wiring`** and run its generate → validate → render → iterate
|
|
9
|
+
loop; generated A2UI is validated as data before it is serialized, and the
|
|
10
|
+
run closes with the Generation Record (the skill's §Deliverable) — the
|
|
11
|
+
validate-before-serialize slot filled, never skipped silently.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Scaffold a new adia-ui app, page, or component skeleton (scripts/adia-scaffold emits the mechanical skeleton; the skill owns the shape decisions)."
|
|
3
|
+
argument-hint: "[spa|ssr|page|component|inventory|selftest] [name or app-root]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Scaffold an adia-ui surface. **$ARGUMENTS**
|
|
7
|
+
|
|
8
|
+
Invoke **`project-scaffolding`** to classify the project shape and target, then use
|
|
9
|
+
`${CLAUDE_PLUGIN_ROOT}/scripts/adia-scaffold` for the mechanical skeleton. Shapes
|
|
10
|
+
the bin doesn't one-shot (rollup, shared-foundation) are composed per the
|
|
11
|
+
project-shapes reference. `inventory <app-root>` scores an existing app
|
|
12
|
+
against the structure rubric (4 mechanized gates with cited paths + the 2
|
|
13
|
+
judgment rows the model completes).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Run the consumer-side exit gate on a composed surface — dispatches the read-only surface-qa-agent seat; returns the completed VerifyProof (ship | hold)."
|
|
3
|
+
argument-hint: "[url or surface] [key selectors]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Verify a composed surface. **$ARGUMENTS**
|
|
7
|
+
|
|
8
|
+
Dispatch the **surface-qa-agent** agent on the named surface/URL — the exit
|
|
9
|
+
gate runs ISOLATED from whatever context built the surface (generator ≠
|
|
10
|
+
critic). It runs the shipped probe (`scripts/adia-probe.mjs`: console/page
|
|
11
|
+
errors, bounding boxes, the deviceScaleFactor:2 capture), completes the
|
|
12
|
+
judgment half (`imageRead` carries what the pixels actually show; the a11y
|
|
13
|
+
row checked item by item), and returns the VerifyProof (surface-qa
|
|
14
|
+
§Deliverable) with verdict **ship | hold**.
|
|
15
|
+
|
|
16
|
+
Findings route back to the owning builder skill (screen-composition · data-wiring ·
|
|
17
|
+
shell-selection · host-wiring) — never fix inline from this command. No running
|
|
18
|
+
app or missing Playwright → the gate reports UNMEASURED with the reason,
|
|
19
|
+
never a silent pass. (Mid-build self-checks still invoke the `surface-qa`
|
|
20
|
+
skill inline; this command is the independent verdict at definition-of-done.)
|
|
@@ -119,7 +119,7 @@ Three levels, all `--a-*`:
|
|
|
119
119
|
|
|
120
120
|
## "Registers" — typographic treatments, opt-in
|
|
121
121
|
|
|
122
|
-
A _register_ (e.g. `
|
|
122
|
+
A _register_ (e.g. `scale="ui-sm"`, `scale="content-md"`) is a typographic treatment applied two ways together: **link the register stylesheet** (`styles/scale.css`, or wrap the surface in `<theme-provider scale="…">`, which adopts it on demand) **and** put the **attribute** on the surface. One without the other is a no-op — a common smell. Body/UI text defaults to `--a-font-family-ui`; registers opt a subtree into a different family/rhythm.
|
|
123
123
|
|
|
124
124
|
## Layout realities
|
|
125
125
|
|
|
@@ -53,7 +53,7 @@ alternatives:
|
|
|
53
53
|
- **One registration script** — the side-effecting barrel `import '@adia-ai/web-components'`. Don't piecemeal-import primitives: composites render internal `*-ui` tags (e.g. `chat-input-ui` internally renders `textarea-ui` + `select-ui`) that stay unregistered and collapse to 0px unless the barrel ran.
|
|
54
54
|
- Bespoke shell children need the **cluster barrel** (`@adia-ai/web-modules/shell`, `/chat`, `/editor`, `/simple`) — importing `admin-shell.js` alone registers only the host tag, not `admin-sidebar` / `admin-page` / the other children.
|
|
55
55
|
- Never hand-roll `:where(html,body){}` — the foundation owns the page frame; re-rolling it drifts from the system (the classic serif-leak bug).
|
|
56
|
-
- A `[scale]` register has two halves: link `scale.css` **and** put `scale="…"` (one of the six tiers — `ui-sm | ui-md | ui-lg | content-sm | content-md | content-lg`) on the surface. One without the other is a no-op.
|
|
56
|
+
- A `[scale]` register has two halves: link `scale.css` **and** put `scale="…"` (one of the six tiers — `ui-sm | ui-md | ui-lg | content-sm | content-md | content-lg`) on the surface. One without the other is a no-op.
|
|
57
57
|
- `themes.css` (named palettes) is **not** in the styles barrel — link it separately. `data-scheme` switches light/dark; `data-theme` picks the named palette.
|
|
58
58
|
- Guards: `defineIfFree(tag, ctor)` (`core/register.js`) for defines; a `#booted` flag in `connected()` — the callback re-fires on DOM moves. And `customElements.whenDefined(name)` never rejects: a `Promise.all([...whenDefined])` boot gate hangs forever on one unimported tag — chrome renders (tag-keyed CSS), the page stays "empty", zero console errors.
|
|
59
59
|
|
|
Binary file
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
// tag — see resolveA2uiDoc()).
|
|
13
13
|
//
|
|
14
14
|
// v1 scope (stated honestly, per the SPEC's Non-goals): attribute names +
|
|
15
|
-
// enum values on `*-ui` tags; class/id/slot/style/role/
|
|
16
|
-
// framework-global attributes allowlisted; one content-model
|
|
17
|
-
// <tbody> children of <table-ui> (3 of 3 occurrences
|
|
18
|
-
// and composition semantics are v2.
|
|
15
|
+
// enum values on `*-ui` tags; class/id/slot/style/role/tabindex/autocomplete/
|
|
16
|
+
// data-*/aria-*/framework-global attributes allowlisted; one content-model
|
|
17
|
+
// rule — raw <thead>/<tbody> children of <table-ui> (3 of 3 occurrences
|
|
18
|
+
// broken, gh#924). Slot and composition semantics are v2.
|
|
19
19
|
//
|
|
20
20
|
// The shipped contract mixes kebab-case and camelCase attribute names across
|
|
21
21
|
// components (an existing inconsistency in the generated manifest — e.g.
|
|
@@ -56,7 +56,20 @@ const SKIP_DIRS = new Set(['node_modules', 'dist', 'build', '.git', '.next', 'co
|
|
|
56
56
|
// `role` is a host-language global (like `aria-*`) — text.yaml itself documents
|
|
57
57
|
// role="heading" + aria-level as the sanctioned semantic-heading path (gh#1256);
|
|
58
58
|
// globals are never component contract members and never CLASS-5-UNKNOWN.
|
|
59
|
-
|
|
59
|
+
// `tabindex` and `autocomplete` are the same class of host-language global
|
|
60
|
+
// (gh#1925): `tabindex` sets DOM focus order on any element (traits like
|
|
61
|
+
// long-press/typeahead and app code use it directly on `*-ui` tags — see
|
|
62
|
+
// e.g. packages/web-components/traits/typeahead/typeahead.examples.html —
|
|
63
|
+
// and it is the sanctioned pattern for post-screen-swap heading focus);
|
|
64
|
+
// `autocomplete` is the standard form-control autofill hint, and at least
|
|
65
|
+
// one component's own yaml SoT instructs authors to set it directly
|
|
66
|
+
// (otp-input.yaml:73, `autocomplete="one-time-code"`) even though the
|
|
67
|
+
// component does not re-declare it as a reflected prop. Neither is ever a
|
|
68
|
+
// component contract member, so neither is ever CLASS-5-UNKNOWN.
|
|
69
|
+
const ALLOWLIST_EXACT = new Set([
|
|
70
|
+
'class', 'classname', 'id', 'slot', 'style', 'key', 'ref', 'part', 'role',
|
|
71
|
+
'tabindex', 'autocomplete',
|
|
72
|
+
]);
|
|
60
73
|
const ALLOWLIST_PREFIX = ['data-', 'aria-'];
|
|
61
74
|
|
|
62
75
|
function normalize(name) {
|
|
@@ -346,6 +359,17 @@ function selftest() {
|
|
|
346
359
|
const roleGlobal = checkMarkup('<text-ui variant="title" role="heading" aria-level="1">Dashboard</text-ui>', 'f.html', index);
|
|
347
360
|
if (roleGlobal.length !== 0) fails.push(`role="heading" aria-level flagged (gh#1256): ${JSON.stringify(roleGlobal)}`);
|
|
348
361
|
|
|
362
|
+
// 9a. gh#1925: `tabindex` on any *-ui tag (e.g. text-ui for post-screen-swap
|
|
363
|
+
// heading focus) is a host-language global, never CLASS-5-UNKNOWN.
|
|
364
|
+
const tabindexGlobal = checkMarkup('<text-ui variant="title" tabindex="-1">Dashboard</text-ui>', 'f.html', index);
|
|
365
|
+
if (tabindexGlobal.length !== 0) fails.push(`tabindex="-1" flagged (gh#1925): ${JSON.stringify(tabindexGlobal)}`);
|
|
366
|
+
|
|
367
|
+
// 9b. gh#1925: `autocomplete` is a host-language global too, and otp-input-ui's
|
|
368
|
+
// own yaml SoT instructs authors to set it — must never be CLASS-5-UNKNOWN
|
|
369
|
+
// even though the component doesn't re-declare it as a reflected prop.
|
|
370
|
+
const autocompleteGlobal = checkMarkup('<icon-ui name="house" autocomplete="one-time-code"></icon-ui>', 'f.html', index);
|
|
371
|
+
if (autocompleteGlobal.length !== 0) fails.push(`autocomplete flagged (gh#1925): ${JSON.stringify(autocompleteGlobal)}`);
|
|
372
|
+
|
|
349
373
|
// 10. Setup-error path (AC-4's 0/1/2 matrix): no @adia-ai/web-components in
|
|
350
374
|
// node_modules anywhere up the tree must resolve to null (drives exit 2 in
|
|
351
375
|
// main()), never a thrown exception or a silent empty-clean pass.
|
|
@@ -375,7 +399,7 @@ function selftest() {
|
|
|
375
399
|
console.error('selftest FAIL: ' + fails.join(' | '));
|
|
376
400
|
return 1;
|
|
377
401
|
}
|
|
378
|
-
console.log('selftest OK —
|
|
402
|
+
console.log('selftest OK — 13 fixtures (unknown attr, clean, enum, camelCase-normalize, table content-model, unknown tag, seed+un-seed, role/aria globals, tabindex/autocomplete globals, setup-error, -h/--help)');
|
|
379
403
|
return 0;
|
|
380
404
|
}
|
|
381
405
|
|
package/scripts/adia-probe.mjs
CHANGED
|
@@ -260,7 +260,13 @@ async function main() {
|
|
|
260
260
|
const { pathToFileURL } = await import('node:url');
|
|
261
261
|
const path = await import('node:path');
|
|
262
262
|
const req = createRequire(path.join(process.cwd(), 'package.json'));
|
|
263
|
-
|
|
263
|
+
// The plugin-cache copy resolves playwright's CJS entry; importing a
|
|
264
|
+
// CJS module via file URL exposes named exports only through
|
|
265
|
+
// `.default` (no static analysis to hoist `chromium` onto the
|
|
266
|
+
// namespace itself) — `mod.chromium` is undefined there, so fall back
|
|
267
|
+
// to `mod.default.chromium` (gh#1926).
|
|
268
|
+
const mod = await import(pathToFileURL(req.resolve('playwright')).href);
|
|
269
|
+
chromium = mod.chromium ?? mod.default.chromium;
|
|
264
270
|
} catch {
|
|
265
271
|
console.error('adia-probe: playwright not installed in this app — npm i -D playwright');
|
|
266
272
|
process.exit(2);
|
|
@@ -329,33 +335,53 @@ async function main() {
|
|
|
329
335
|
const samples = [];
|
|
330
336
|
let skipped = 0;
|
|
331
337
|
const seen = new Set();
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
338
|
+
// AdiaUI is light-DOM by convention (AGENTS.md), but a plain
|
|
339
|
+
// TreeWalker rooted at document.body never descends into a shadow
|
|
340
|
+
// tree regardless — so any *-ui element that DOES attach a shadow
|
|
341
|
+
// root (its own internal structure, a third-party element, or a
|
|
342
|
+
// future exception to the light-DOM stance) went unwalked entirely,
|
|
343
|
+
// which is why the sampler read checked:0 on custom-element-heavy
|
|
344
|
+
// pages (gh#1926). Walk every root — document, then recurse into
|
|
345
|
+
// each element's shadowRoot as it's discovered — so light-DOM slotted
|
|
346
|
+
// content and shadow-DOM internals both get sampled.
|
|
347
|
+
const visitRoot = (root) => {
|
|
348
|
+
const walker = document.createTreeWalker(
|
|
349
|
+
root,
|
|
350
|
+
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
|
|
351
|
+
);
|
|
352
|
+
let node;
|
|
353
|
+
while ((node = walker.nextNode())) {
|
|
354
|
+
if (node.nodeType === 1) {
|
|
355
|
+
if (node.shadowRoot) visitRoot(node.shadowRoot);
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (samples.length >= 120) return;
|
|
359
|
+
const text = node.textContent.trim();
|
|
360
|
+
if (!text) continue;
|
|
361
|
+
const el = node.parentElement;
|
|
362
|
+
if (!el || ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEMPLATE'].includes(el.tagName)) continue;
|
|
363
|
+
const cs = getComputedStyle(el);
|
|
364
|
+
if (cs.display === 'none' || cs.visibility === 'hidden' || Number(cs.opacity) === 0) continue;
|
|
365
|
+
const rect = el.getBoundingClientRect();
|
|
366
|
+
if (rect.width <= 0 || rect.height <= 0) continue;
|
|
367
|
+
const fgc = parse(cs.color);
|
|
368
|
+
if (!fgc) continue;
|
|
369
|
+
const bg = bgFor(el);
|
|
370
|
+
if (!bg) { skipped += 1; continue; }
|
|
371
|
+
const fg = fgc.a >= 1 ? fgc.rgb.map(Math.round) : over(fgc.rgb, fgc.a, bg);
|
|
372
|
+
const fontSizePx = parseFloat(cs.fontSize) || 16;
|
|
373
|
+
const fontWeight = cs.fontWeight === 'bold' ? 700 : Number(cs.fontWeight) || 400;
|
|
374
|
+
const key = `${fg.join()}|${bg.join()}|${fontSizePx}|${fontWeight}`;
|
|
375
|
+
if (seen.has(key)) continue;
|
|
376
|
+
seen.add(key);
|
|
377
|
+
samples.push({
|
|
378
|
+
text: text.slice(0, 40),
|
|
379
|
+
element: el.tagName.toLowerCase() + (el.id ? `#${el.id}` : ''),
|
|
380
|
+
fg, bg, fontSizePx, fontWeight,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
visitRoot(document.body);
|
|
359
385
|
return { samples, skipped };
|
|
360
386
|
}).catch(() => null);
|
|
361
387
|
await page.screenshot({ path: screenshotPath, scale: 'device', fullPage: true });
|
|
@@ -85,7 +85,7 @@ proposing any edit.
|
|
|
85
85
|
opt-in, confirm something imports it:
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
|
-
git grep -n "styles/scale.css\|styles/themes.css
|
|
88
|
+
git grep -n "styles/scale.css\|styles/themes.css"
|
|
89
89
|
|
|
90
90
|
# Attributes reach the DOM from markup AND from script — a setAttribute('scale', …)
|
|
91
91
|
# in a .js module counts. Searching templates only produces a false "unused".
|
|
@@ -90,7 +90,7 @@ Mode: SPA | SSR | hybrid — signal: <file / dep
|
|
|
90
90
|
Framework: <name, or none> — [D] documented | [G] inferred, per ssr-integration.md
|
|
91
91
|
Registration: <file>:<call site> — one side-effecting barrel import, client-only
|
|
92
92
|
Route owner: <router-ui> | <framework router> — exactly one per scope
|
|
93
|
-
CSS links: host.css + styles/index.css (+ scale.css
|
|
93
|
+
CSS links: host.css + styles/index.css (+ scale.css) — both live, or primitives render unstyled
|
|
94
94
|
State placement: cookies/localStorage/session | client signal — per mode's lifetime rule
|
|
95
95
|
Gates: registration / route-owner / CSS-links / state — pass | fail, each
|
|
96
96
|
```
|
|
@@ -43,8 +43,7 @@ Build order — snippets and the full invariant list in `spa-architecture.md`;
|
|
|
43
43
|
|
|
44
44
|
1. **Host document** — npm-consumer paths; cascade order later-wins:
|
|
45
45
|
`host.css` → `styles/index.css` → opt-in `scale.css` (the ancestor
|
|
46
|
-
`[scale]` register
|
|
47
|
-
still-working aliases) → page → component CSS. Link BOTH `host.css`
|
|
46
|
+
`[scale]` register) → page → component CSS. Link BOTH `host.css`
|
|
48
47
|
and `styles/index.css` — the styles barrel is split and `host.css` is
|
|
49
48
|
foundation-only; alone it renders primitives unstyled.
|
|
50
49
|
2. **Surface container** — self-booting custom element rendering in
|