@adia-ai/adia-ui-factory 0.2.4 → 0.8.0
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 +5 -7
- package/.mcp.json +1 -1
- package/CHANGELOG.md +27 -67
- package/README.md +32 -53
- package/agents/app-architect.md +28 -0
- package/agents/routing-corpus.json +202 -0
- package/agents/screen-composer.md +27 -0
- package/bin/adia-lint +17 -2
- package/commands/adia-genui.md +5 -8
- package/commands/adia-migrate.md +5 -6
- package/commands/adia-orient.md +5 -10
- package/commands/adia-scaffold.md +7 -13
- package/commands/adia-verify.md +5 -6
- package/package.json +5 -6
- package/references/a2ui-mcp-tools.md +11 -8
- package/references/authoring-components.md +25 -1
- package/references/component-model.md +52 -4
- package/references/contracts/a2ui-mcp-surface.md +35 -0
- package/references/contracts/migration-guide-format.md +34 -0
- package/references/data-and-hydration.md +8 -5
- package/references/genui-a2ui.md +12 -6
- package/references/llm.md +22 -39
- package/references/migration.md +110 -36
- package/references/project-shapes.md +11 -13
- package/references/shell-admin.md +5 -2
- package/references/shell-chat.md +5 -3
- package/references/shell-editor.md +1 -1
- package/references/shell-embed.md +40 -26
- package/references/shell-simple.md +5 -2
- package/references/spa-architecture.md +28 -73
- package/references/ssr-integration.md +1 -1
- package/references/verification.md +8 -12
- package/skills/adia-compose/SKILL.md +52 -0
- package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/Guidelines.md +3 -3
- package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/components.md +1 -1
- package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/styles.md +1 -1
- package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/tokens.md +3 -3
- package/skills/{adia-ui-kit → adia-compose}/assets/templates/FEEDBACK.template.md +7 -7
- package/skills/{adia-ui-kit → adia-compose}/assets/templates/RESPONSE.template.md +6 -7
- package/skills/adia-compose/references/composition-traps.md +58 -0
- package/skills/adia-compose/references/feedback-discipline.md +20 -0
- package/skills/adia-compose/references/spec-to-ui-reasoning.md +158 -0
- package/skills/adia-data/SKILL.md +78 -0
- package/skills/adia-genui/SKILL.md +83 -0
- package/skills/adia-host/SKILL.md +119 -0
- package/skills/adia-llm/SKILL.md +36 -0
- package/skills/adia-migrate/SKILL.md +76 -0
- package/skills/adia-orient/SKILL.md +123 -0
- package/skills/adia-project/SKILL.md +83 -0
- package/skills/adia-shells/SKILL.md +74 -0
- package/skills/adia-verify/SKILL.md +43 -0
- package/skills/adia-verify/references/verification.md +42 -0
- package/commands/adia-compose.md +0 -10
- package/commands/adia-wire.md +0 -13
- package/skills/adia-ui-compose/SKILL.md +0 -57
- package/skills/adia-ui-data/SKILL.md +0 -62
- package/skills/adia-ui-factory/SKILL.md +0 -113
- package/skills/adia-ui-genui/SKILL.md +0 -74
- package/skills/adia-ui-kit/CHANGELOG.md +0 -41
- package/skills/adia-ui-kit/SKILL.md +0 -442
- package/skills/adia-ui-kit/assets/team-scaffold/archive-README.md +0 -30
- package/skills/adia-ui-kit/assets/team-scaffold/drafts-README.md +0 -57
- package/skills/adia-ui-kit/assets/team-scaffold/inbox-README.md +0 -76
- package/skills/adia-ui-kit/assets/team-scaffold/outbox-README.md +0 -46
- package/skills/adia-ui-kit/assets/team-scaffold/team-root.README.md +0 -51
- package/skills/adia-ui-kit/assets/team-scaffold/tickets-README.md +0 -208
- package/skills/adia-ui-kit/assets/team-scaffold/tickets-REGISTRY.md +0 -81
- package/skills/adia-ui-kit/assets/templates/AGENTS.template.md +0 -128
- package/skills/adia-ui-kit/assets/templates/CLAUDE.template.md +0 -1
- package/skills/adia-ui-kit/assets/templates/figma-make-guidelines.md +0 -90
- package/skills/adia-ui-kit/evals/adversarial-content-trust.json +0 -83
- package/skills/adia-ui-kit/evals/evals.json +0 -55
- package/skills/adia-ui-kit/evals/routing-corpus.json +0 -121
- package/skills/adia-ui-kit/evals/teach-routing-cases.json +0 -92
- package/skills/adia-ui-kit/references/capabilities.md +0 -245
- package/skills/adia-ui-kit/references/catalog-lookup.md +0 -186
- package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v1.md +0 -8
- package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v2.md +0 -3585
- package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v3.md +0 -1899
- package/skills/adia-ui-kit/references/common-gotchas-consumer.md +0 -522
- package/skills/adia-ui-kit/references/component-literacy.md +0 -291
- package/skills/adia-ui-kit/references/composition.md +0 -245
- package/skills/adia-ui-kit/references/correction-loop.md +0 -462
- package/skills/adia-ui-kit/references/css-policy.md +0 -343
- package/skills/adia-ui-kit/references/decision-model.md +0 -158
- package/skills/adia-ui-kit/references/diagnostic-output.md +0 -57
- package/skills/adia-ui-kit/references/docs-catalog.md +0 -239
- package/skills/adia-ui-kit/references/exclusions.md +0 -225
- package/skills/adia-ui-kit/references/feedback-authoring.md +0 -466
- package/skills/adia-ui-kit/references/figma-make.md +0 -244
- package/skills/adia-ui-kit/references/flows-auth.md +0 -307
- package/skills/adia-ui-kit/references/flows-content.md +0 -206
- package/skills/adia-ui-kit/references/flows-data.md +0 -348
- package/skills/adia-ui-kit/references/flows-interaction.md +0 -181
- package/skills/adia-ui-kit/references/flows-shells.md +0 -336
- package/skills/adia-ui-kit/references/gap-detection.md +0 -266
- package/skills/adia-ui-kit/references/harness.md +0 -284
- package/skills/adia-ui-kit/references/loading-protocol.md +0 -406
- package/skills/adia-ui-kit/references/local-link.md +0 -198
- package/skills/adia-ui-kit/references/mcp.md +0 -401
- package/skills/adia-ui-kit/references/migration.md +0 -116
- package/skills/adia-ui-kit/references/mission.md +0 -194
- package/skills/adia-ui-kit/references/mode-routing.md +0 -298
- package/skills/adia-ui-kit/references/patterns-agent.md +0 -223
- package/skills/adia-ui-kit/references/patterns-data.md +0 -1065
- package/skills/adia-ui-kit/references/patterns-forms.md +0 -223
- package/skills/adia-ui-kit/references/patterns-layout.md +0 -326
- package/skills/adia-ui-kit/references/patterns-recipes.md +0 -1416
- package/skills/adia-ui-kit/references/patterns-shells.md +0 -1097
- package/skills/adia-ui-kit/references/patterns-traits.md +0 -259
- package/skills/adia-ui-kit/references/patterns-wiring.md +0 -339
- package/skills/adia-ui-kit/references/pev-rationale.md +0 -137
- package/skills/adia-ui-kit/references/reasoning-gate.md +0 -146
- package/skills/adia-ui-kit/references/reasoning-methodologies.md +0 -112
- package/skills/adia-ui-kit/references/recon.md +0 -192
- package/skills/adia-ui-kit/references/rendering-model.md +0 -431
- package/skills/adia-ui-kit/references/response-authoring.md +0 -398
- package/skills/adia-ui-kit/references/session-log-schema.md +0 -203
- package/skills/adia-ui-kit/references/session-log-template.md +0 -118
- package/skills/adia-ui-kit/references/setup.md +0 -652
- package/skills/adia-ui-kit/references/skill-hygiene.md +0 -135
- package/skills/adia-ui-kit/references/spec-to-ui-reasoning.md +0 -394
- package/skills/adia-ui-kit/references/state.md +0 -229
- package/skills/adia-ui-kit/references/teach-protocol.md +0 -421
- package/skills/adia-ui-kit/references/theme.md +0 -142
- package/skills/adia-ui-kit/references/version-check.md +0 -69
- package/skills/adia-ui-kit/scripts/audit-decision-trace.mjs +0 -198
- package/skills/adia-ui-kit/scripts/audit-kit-roster.mjs +0 -366
- package/skills/adia-ui-kit/scripts/changelog-archive.mjs +0 -126
- package/skills/adia-ui-kit/scripts/check-adia-versions.py +0 -404
- package/skills/adia-ui-kit/scripts/run-skill-evals.mjs +0 -358
- package/skills/adia-ui-kit/scripts/validate-session-log.mjs +0 -242
- package/skills/adia-ui-kit/skill.json +0 -102
- package/skills/adia-ui-llm/SKILL.md +0 -51
- package/skills/adia-ui-migrate/SKILL.md +0 -64
- package/skills/adia-ui-migration/CHANGELOG.md +0 -47
- package/skills/adia-ui-migration/SKILL.md +0 -381
- package/skills/adia-ui-migration/skill.json +0 -29
- package/skills/adia-ui-project/SKILL.md +0 -77
- package/skills/adia-ui-shells/SKILL.md +0 -62
- package/skills/adia-ui-spa/SKILL.md +0 -52
- package/skills/adia-ui-ssr/SKILL.md +0 -52
- package/skills/adia-ui-verify/SKILL.md +0 -44
- /package/skills/{adia-ui-kit → adia-compose}/assets/templates/FEEDBACK-OMISSIONS-DEFENSE.template.md +0 -0
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Scaffold a new adia-ui app
|
|
3
|
-
argument-hint: "[
|
|
2
|
+
description: Scaffold a new adia-ui app, page, or component skeleton (bin/adia-scaffold emits the mechanical skeleton; the skill owns the shape decisions).
|
|
3
|
+
argument-hint: "[app|page|component] [name]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Scaffold
|
|
6
|
+
Scaffold an adia-ui surface. **$ARGUMENTS**
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- SPA → `python3 "${CLAUDE_PLUGIN_ROOT}/bin/adia-scaffold" spa <name>`
|
|
13
|
-
- SSR → `python3 "${CLAUDE_PLUGIN_ROOT}/bin/adia-scaffold" ssr <name> --framework <next|nuxt|sveltekit|astro>`
|
|
14
|
-
- add surfaces with the `page` (`--duo` for declarative) / `component` sub-commands.
|
|
15
|
-
3. **Hand off:** `adia-ui-project` for the shape/layout decisions the bin doesn't make, `adia-ui-spa` / `adia-ui-ssr` for host wiring, then `adia-ui-compose` for the first real screen.
|
|
16
|
-
|
|
17
|
-
The bin owns the byte-stable layout; the skills own the methodology. Don't restate either here.
|
|
8
|
+
Invoke **`adia-project`** to classify the project shape and target, then use
|
|
9
|
+
`${CLAUDE_PLUGIN_ROOT}/bin/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.
|
package/commands/adia-verify.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
3
|
-
argument-hint: "[
|
|
2
|
+
description: Run the consumer-side exit gate on a composed surface — browser render, console, a11y, and composition audits.
|
|
3
|
+
argument-hint: "[page or surface]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Verify
|
|
6
|
+
Verify a composed surface. **$ARGUMENTS**
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
The skill owns the depth (`${CLAUDE_PLUGIN_ROOT}/references/verification.md`); don't restate it here.
|
|
8
|
+
Invoke **`adia-verify`** and run its gate against the real rendered surface;
|
|
9
|
+
findings come back file:line with severity.
|
package/package.json
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/adia-ui-factory",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Author and verify apps built
|
|
3
|
+
"version": "0.8.0",
|
|
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
|
"keywords": [
|
|
6
6
|
"adia-ui",
|
|
7
7
|
"a2ui",
|
|
8
|
+
"gen-ui",
|
|
8
9
|
"web-components",
|
|
9
10
|
"app-authoring",
|
|
10
|
-
"
|
|
11
|
-
"spa",
|
|
12
|
-
"scaffolding",
|
|
13
|
-
"llm-ui"
|
|
11
|
+
"consumer"
|
|
14
12
|
],
|
|
15
13
|
"license": "MIT",
|
|
16
14
|
"author": {
|
|
@@ -20,6 +18,7 @@
|
|
|
20
18
|
"files": [
|
|
21
19
|
".claude-plugin",
|
|
22
20
|
"skills",
|
|
21
|
+
"agents",
|
|
23
22
|
"commands",
|
|
24
23
|
"references",
|
|
25
24
|
"bin",
|
|
@@ -20,8 +20,8 @@ This plugin wires `@adia-ai/a2ui-mcp` (declared in `.mcp.json`, run via `npx`).
|
|
|
20
20
|
|
|
21
21
|
**Retrieve patterns & knowledge:**
|
|
22
22
|
|
|
23
|
-
- `search_chunks` — semantic/keyword search over the
|
|
24
|
-
- `search_patterns` / `
|
|
23
|
+
- `search_chunks` — semantic/keyword search over the corpus's 394 chunks.
|
|
24
|
+
- `search_patterns` / `list_patterns` / `get_composition` — reusable composition patterns.
|
|
25
25
|
- `get_chunk` / `get_graph` / `resolve_composition` / `zettel_stats` — chunk graph navigation.
|
|
26
26
|
|
|
27
27
|
**Classify & assemble (the generation pre-step):**
|
|
@@ -32,8 +32,9 @@ This plugin wires `@adia-ai/a2ui-mcp` (declared in `.mcp.json`, run via `npx`).
|
|
|
32
32
|
**Generate** (host LLM in stdio):
|
|
33
33
|
|
|
34
34
|
- `generate_ui` — produce A2UI/markup for a described surface.
|
|
35
|
-
- `refine_ui` —
|
|
36
|
-
- `plan_app_state` —
|
|
35
|
+
- `refine_ui` — correct a `generate_ui` result whose validation failed (params: `intent`, `previousMessages`, `validationErrors`; monolithic only — zettel refines via `refine_composition`).
|
|
36
|
+
- `plan_app_state` — extract the intent/domain/tasks ontology from a prompt *before* generating.
|
|
37
|
+
- `compose_from_chunks` / `refine_composition` / `get_state` — the zettel two-call chunk workflow (compose, then refine against the kept `state_id`).
|
|
37
38
|
|
|
38
39
|
**Validate** (offline):
|
|
39
40
|
|
|
@@ -43,7 +44,9 @@ This plugin wires `@adia-ai/a2ui-mcp` (declared in `.mcp.json`, run via `npx`).
|
|
|
43
44
|
|
|
44
45
|
**Feedback & authoring loop:**
|
|
45
46
|
|
|
46
|
-
- `submit_feedback` · `get_quality_metrics` · `get_training_gaps` · `
|
|
47
|
+
- `submit_feedback` · `get_quality_metrics` · `get_training_gaps` · `run_eval` · `report_issue` (files a session ticket under `qa/findings/issues/`).
|
|
48
|
+
|
|
49
|
+
**Probe:** `server_status` — connectivity/version check (the `adia-verify` wiring probe).
|
|
47
50
|
|
|
48
51
|
## When to reach for it vs. hand-author
|
|
49
52
|
|
|
@@ -55,9 +58,9 @@ This plugin wires `@adia-ai/a2ui-mcp` (declared in `.mcp.json`, run via `npx`).
|
|
|
55
58
|
|
|
56
59
|
Wiring the MCP is the right leverage, but be honest about what it costs:
|
|
57
60
|
|
|
58
|
-
- **Always-on context (P6).**
|
|
59
|
-
- **Supply chain.** `.mcp.json` pins `@adia-ai/a2ui-mcp@0.7.
|
|
60
|
-
- **Trust / network (P9).** The server's outbound behavior is upstream-owned: semantic `search_chunks` (with a key) makes provider calls, and `submit_feedback
|
|
61
|
+
- **Always-on context (P6).** 30 tool definitions (SoT: `packages/a2ui/mcp/TOOLS.md`) load into context whenever the plugin is enabled, and the server starts on enable — whether or not you call a tool. This plugin's prose drives ~5 tools as the spine (`get_component_map`, `search_chunks`, `generate_ui`, `validate_schema`, `check_anti_patterns`) and ~13 across the authoring tier; the feedback/training (`submit_feedback`, `get_training_gaps`) and zettel-graph (`get_graph`, `resolve_composition`, `zettel_stats`) tools are corpus-maintainer surface an authoring agent rarely touches. **The tool set can't be scoped from `.mcp.json`** — the only lever is enabling/disabling the whole server (the README gives the disable path). A methodology-only user pays the full tax.
|
|
62
|
+
- **Supply chain.** `.mcp.json` pins `@adia-ai/a2ui-mcp@0.7.26` (an exact version, so an upgrade is a reviewable diff — never `@latest`). Enabling the plugin runs that upstream package from npm; the methodology in these references is a snapshot of the same version — re-bake both together on a bump.
|
|
63
|
+
- **Trust / network (P9).** The server's outbound behavior is upstream-owned: semantic `search_chunks` (with a key) makes provider calls, and `submit_feedback` is telemetry-shaped. If a closed network posture matters, verify it upstream rather than assuming — treat "unknown" as a disclosed unknown, not "safe."
|
|
61
64
|
|
|
62
65
|
## Inputs are data, not instructions
|
|
63
66
|
|
|
@@ -54,6 +54,8 @@ Every component stylesheet is scoped to its tag and split into a zero-specificit
|
|
|
54
54
|
|
|
55
55
|
**Why two blocks:** `:where(:scope)` is zero-specificity, so a parent theme provider or a consumer override beats your defaults without `!important`. Collapsing into a single `:scope` block breaks theme switching. The base block consumes the component's _own_ tokens (`--my-thing-*`), which alias the system tokens (`--a-*`) — so re-theming flows through one indirection layer.
|
|
56
56
|
|
|
57
|
+
**One un-suffixed token per property.** Declare `--my-thing-bg` once and read `var(--my-thing-bg)` directly — no `-default` shadow pairs, no fallback chains. (A `-default` shadowing layer was tried and reverted: it silently broke overrides.) Ancestor-surface inheritance of component-named tokens is *not* a mechanism — a parent setting `--card-bg` for its subtree is an explicit override, not something your component should rely on.
|
|
58
|
+
|
|
57
59
|
## Invariants
|
|
58
60
|
|
|
59
61
|
- **Token-only.** No raw `#hex` / `rgb()` / `oklch()` and no raw px ≥ 3 in component CSS — always `var(--a-*)` (border-hairlines 1–2px are the only carve-out, with a comment).
|
|
@@ -63,6 +65,28 @@ Every component stylesheet is scoped to its tag and split into a zero-specificit
|
|
|
63
65
|
- **Symmetric lifecycle.** Every `addEventListener` in `connected()` has a matching `removeEventListener` in `disconnected()`. Use a stable handler reference (private field `#onClick = (e) => …`), not an inline arrow — `removeEventListener` needs reference equality.
|
|
64
66
|
- **Data down, events up.** Sub-components receive state via properties (`.rec = …`) and emit `CustomEvent`s; they never reach into a parent's internals.
|
|
65
67
|
- **Font-family floor.** A text-bearing component floors its family to a token: `font-family: var(--my-thing-family, var(--a-font-family-ui))` — otherwise it inherits a broken host font (the dead-`--a-font` trap resolves to UA serif).
|
|
68
|
+
- **Real props, not `data-*` hooks.** A component's public boolean/enum API is declared `static properties` reflected as clean attributes — never `data-*` styling hooks.
|
|
69
|
+
- **Container queries are opt-in** — `[provide-container-inline-size]` is the attribute utility that makes an element a size-query provider; it's not a per-component default.
|
|
70
|
+
|
|
71
|
+
## CSS mechanics that bite
|
|
72
|
+
|
|
73
|
+
- **`background: <color>` shorthand silently resets `background-clip`/`origin`/`position`/`size`** — a hover/active state that only recolors must use the `background-color` longhand, or a base `background-clip: content-box` flips to `border-box` and the fill balloons.
|
|
74
|
+
- **`translate`/`scale`/`rotate` are independent properties, not `transform` aliases** — writing `style.translate` and reading the computed `transform` matrix (or vice versa) silently no-ops; animation loops that mix them read 0 and never move.
|
|
75
|
+
- **An offsetting ancestor transform (`translate(-50%,-50%)`) breaks CSS anchor positioning** for top-layer popovers; an identity transform (`translateX(0)`) doesn't.
|
|
76
|
+
- **An equal-specificity `@media` override must follow its base rule in source order** — placed earlier it silently loses.
|
|
77
|
+
- **Never author `<thead>/<tbody>/<tr>/<td>` inside a custom element** — the HTML parser foster-parents them out of any non-`<table>` ancestor before your JS ever runs.
|
|
78
|
+
|
|
79
|
+
## Lifecycle & reactivity traps (UIElement)
|
|
80
|
+
|
|
81
|
+
- **Never declare a native DOM prop** (`textContent`, `innerHTML`, `className`, `id`) **in `static properties`** — the prop installer replaces the native accessor with a signal, so `el.textContent = ''` becomes a signal write instead of a child wipe.
|
|
82
|
+
- **Wrap custom-setter bodies in `untracked()`** when they read reactive props — otherwise the read leaks a subscription into whichever effect called the setter.
|
|
83
|
+
- **A MutationObserver that watches its own host and mutates it in the callback loops forever** — drain with `this.#observer.takeRecords()` after every absorb pass.
|
|
84
|
+
- **A document-listening trait must not re-emit the same-named event on its host with `bubbles: true`** — the bubble re-reaches `document` and recurses; use distinct names for the incoming signal and the outgoing host event.
|
|
85
|
+
- **A parent `render()` that filters children by an attr the child sets in its own `connected()` races the child upgrade** — match by tag name as well.
|
|
86
|
+
- **Sync icon-registry checks (`isIconName`/`hasIcon`) at connect race the async manifest** on static (non-Vite) hosts — pair them with `whenIconRegistryReady` deferred re-stamping.
|
|
87
|
+
- **FLIP/layout-animation preserves node identity, not content** — it fires on `appendChild` moves but not `replaceChildren` rebuilds, so diff children in place; and any post-drop effect must target the *final* DOM node by stable id (cross-container moves rebuild the source in a new parent).
|
|
88
|
+
- **Drag affordances: one strong layer.** Keep the drop indicator (most precise) strong; ghost border, column outline, and bg tint stay subtle — everything at full strength reads as competing chrome.
|
|
89
|
+
- **Drop targets: walk descendants, index post-removal.** `[droppable]` matches items via descendants + a closest-droppable filter (not direct children), so nested containers (cards inside a `.col-list`) still register; and a drop-indicator index is column-relative *after* the lifted item leaves — filter out the `[data-draggable-list-item-lifting]` node before counting, or the indicator lands one slot off.
|
|
66
90
|
|
|
67
91
|
## Anti-patterns (the lint seeds)
|
|
68
92
|
|
|
@@ -85,4 +109,4 @@ The mechanizable smells the framework audits — these seed the phase-(c) hook a
|
|
|
85
109
|
| Hardcoded `open` on `<modal-ui>`/`<drawer-ui>` | drive via `.open = true` |
|
|
86
110
|
| `new URL('…', import.meta.url)` literal for a corpus path | hold the path in a variable first |
|
|
87
111
|
|
|
88
|
-
Run the framework's
|
|
112
|
+
Run the plugin's `bin/adia-lint` on changed files, the framework's `audit:*` gates when you have the repo, and `mcp__a2ui__check_anti_patterns` / `validate_schema` on generated markup.
|
|
@@ -13,8 +13,12 @@ How to _think_ about the catalog. The **live** catalog is the a2ui MCP (`get_com
|
|
|
13
13
|
|
|
14
14
|
**Naming:** kebab-case with a `-ui` **suffix** (`<button-ui>`, not `<a-button>`). Shells/composites carry a domain prefix (`<admin-*>`, `<chat-*>`).
|
|
15
15
|
|
|
16
|
+
**Light DOM is the substrate.** Components render in the light DOM — a `slot="X"` attribute is *inert* metadata unless CSS targets `[slot="X"]`; positioning is CSS by tag + ancestor + DOM order, never slot routing. Grep before believing any "the slot routes content" claim.
|
|
17
|
+
|
|
16
18
|
**Rule of first resort:** reach for a catalog primitive before authoring anything. Never use a raw `<button>`/`<input>`/`<div>` where a `*-ui` primitive exists — raw elements skip focus rings, theming, density, and form association. Author a project component (see [authoring-components.md](authoring-components.md)) only when no primitive composes to the need.
|
|
17
19
|
|
|
20
|
+
**Browser baseline:** Chromium 125+ · Safari 18.0+ · Firefox 129+ — the floor that makes `light-dark()`, `@scope`, popovers, and OKLCH usable without fallbacks.
|
|
21
|
+
|
|
18
22
|
## Registration is a side effect of import
|
|
19
23
|
|
|
20
24
|
Importing a component module **registers its tag**. Two forms:
|
|
@@ -26,6 +30,13 @@ import '@adia-ai/web-components/components/button/button.js'; // one component
|
|
|
26
30
|
|
|
27
31
|
Registration uses `defineIfFree('button-ui', UIButton)` internally — idempotent, so double-import is safe. The non-registering class import (`.../button/class`) exists for tests/subclassing. **In SSR this import must be client-only** — see [ssr-integration.md](ssr-integration.md).
|
|
28
32
|
|
|
33
|
+
Registration corollaries that bite:
|
|
34
|
+
|
|
35
|
+
- **Composites stamp primitives your HTML never mentions** — `chat-input-ui` internally renders `textarea-ui` + `select-ui`; unless those primitives are registered too, they stay undefined and collapse to 0×0.
|
|
36
|
+
- **Shells register via the cluster barrel** (`@adia-ai/web-modules/shell|chat|editor`) — importing one shell's `.js` registers only the host; its bespoke children (`admin-sidebar`, `chat-thread`, …) only register when their sibling JS loads.
|
|
37
|
+
- **Some tags are defined in a parent component's `.js`**, and some components are deliberately CSS-only (stylesheet + metadata, no `.js`) — when auditing whether a tag is real, grep for its `customElements.define`, don't `ls` folders.
|
|
38
|
+
- **JS registers, CSS loads separately** — component CSS arrives via `<link>`/the CSS barrel, not the module graph; a JS-only side-effect import yields a registered but unstyled element.
|
|
39
|
+
|
|
29
40
|
## Signals, not a virtual DOM
|
|
30
41
|
|
|
31
42
|
Components extend `UIElement` (or `UIFormElement` for form-participating controls) and use fine-grained signals:
|
|
@@ -40,6 +51,28 @@ import { UIElement, signal, computed, effect } from '@adia-ai/web-components/cor
|
|
|
40
51
|
|
|
41
52
|
Declared `static properties` are wrapped as signals automatically, so setting `el.disabled = true` re-renders. Don't run a parallel `CustomEvent`-only state path that competes with signals.
|
|
42
53
|
|
|
54
|
+
## Choosing components — the recurring calls
|
|
55
|
+
|
|
56
|
+
Selection mistakes that keep recurring (verify props with `lookup_component` when unsure):
|
|
57
|
+
|
|
58
|
+
- **`<stack-ui>` is a z-axis overlay** — all children share one grid cell. Vertical stacking is `<col-ui>`.
|
|
59
|
+
- **High-frequency, low-cardinality view pickers** (Day/Week/Month, Kanban/List) are `<segmented-ui>`, not `<select-ui>` — all options visible, active one highlighted; reserve `select-ui` for lower-frequency, many-option knobs.
|
|
60
|
+
- **Sidebar navigation is `<nav-ui>` + `<nav-item-ui>`**; `<menu-ui>`/`<menu-item-ui>` are for Popover-API dropdowns, never persistent nav.
|
|
61
|
+
- **A list of rows** (leading control + title + subtext + trailing badge) is `<list-ui> > <list-item-ui>`, not bespoke flex divs.
|
|
62
|
+
- **A standalone checkbox is `<check-ui name label="…">`** — never one `check-ui` wrapped in `<field-ui inline>`. `<field-ui>` is for form contexts only; toolbar knobs use the bare control + `aria-label` (the trigger already shows the value).
|
|
63
|
+
- **Multiple radio/check siblings inside one `<field-ui label>`** need a `<col-ui gap="1">` wrapper — field-ui stacks a single input; without it they overlap.
|
|
64
|
+
- **Card/drawer body content wraps in `<section>`** (canonical order: void media → `<header>` → `<section>`+ → `<footer>`) — direct flow children bypass the body slot and lose the `--card-inset` margin; `<section bleed>` zeros the inset but keeps inline padding.
|
|
65
|
+
- **Clickable grid cards:** wrap the card in `<a href style="display:contents">` — link semantics + keyboard focus without a layout box, so the grid still sees the card as the cell; hover rides `a:hover card-ui`.
|
|
66
|
+
- **No `stretch` on a `<button-ui>` inside a `<col-ui>` action stack** (col-ui already stretches children); `size="lg"` on an action stack applies only when it contains a `variant="primary"` button.
|
|
67
|
+
- **`<toast-ui>` is a facade over `<feed-ui>`** — `AdiaToast.show()` routes to `AdiaFeed.post()`; the top-layer/queue/focus logic lives in the feed component.
|
|
68
|
+
|
|
69
|
+
## Attribute honesty — silent-failure class
|
|
70
|
+
|
|
71
|
+
- **Components silently accept any made-up attribute** — `text-ui muted`, `card-ui hover-elevate` are no-ops with zero warnings. Check the real prop list (`lookup_component`) before authoring; a rendered-but-unstyled state usually means an invented attr.
|
|
72
|
+
- **`empty-state-ui` takes `[heading]`, not `[title]`** — `title=` becomes the native tooltip and the message silently doesn't render.
|
|
73
|
+
- **Primary content often rides the default slot, not `text=`/`label=`** — `kbd-ui` and `card-ui` are the canonical traps. `button-ui` accepts both `text=` and child text; prefer `text=` for generated UI.
|
|
74
|
+
- **Shell-tier bespoke children reflect state as attributes** (`admin-sidebar[collapsed]`) — read and style via attribute selectors; coordinate via `querySelector`, not a central store.
|
|
75
|
+
|
|
43
76
|
## Traits — behavior by declaration
|
|
44
77
|
|
|
45
78
|
```html
|
|
@@ -48,6 +81,9 @@ Declared `static properties` are wrapped as signals automatically, so setting `e
|
|
|
48
81
|
|
|
49
82
|
or programmatically: `static traits = [pressable, scalePress]`. A trait is a factory (`defineTrait({ name, setup({host}) { …; return cleanup } })`) that manages attributes/events and tears itself down on disconnect. Query the live set with the MCP's `get_traits`.
|
|
50
83
|
|
|
84
|
+
- **Traits require `UIElement`** — `traits="…"` on an HTMLElement-based or native element is silently ignored.
|
|
85
|
+
- **`data-stream-*` attributes** are a universal ingestion trait: any element with a settable `.data` can subscribe to a shared, refcounted transport.
|
|
86
|
+
|
|
51
87
|
## Tokens — the only styling currency
|
|
52
88
|
|
|
53
89
|
Three levels, all `--a-*`:
|
|
@@ -55,17 +91,29 @@ Three levels, all `--a-*`:
|
|
|
55
91
|
| Level | Role | Examples |
|
|
56
92
|
| --- | --- | --- |
|
|
57
93
|
| **L1 primitive scales** | dimensionless base values | `--a-space-2` `--a-size-md` `--a-radius-sm` `--a-duration-fast` `--a-font-family-ui` |
|
|
58
|
-
| **L2 semantic families** | role vocabulary | `--a-primary` `--a-danger` `--a-
|
|
59
|
-
| **L3 state × role** | what components consume | `--a-
|
|
94
|
+
| **L2 semantic families** | role vocabulary | `--a-primary` `--a-danger` `--a-primary-bg` |
|
|
95
|
+
| **L3 state × role** | what components consume | `--a-primary-bg-hover` `--a-danger-fg-active` `--a-ui-bg-disabled` |
|
|
60
96
|
|
|
61
|
-
- **Color scheme** uses `light-dark()` at the token layer — never swap tokens by hand. Toggle with `<toggle-scheme-ui scheme="auto" target=":root" persist>`.
|
|
62
|
-
- **Density
|
|
97
|
+
- **Color scheme** uses `light-dark()` at the token layer — never swap tokens by hand. Toggle with `<toggle-scheme-ui scheme="auto" target=":root" persist>`. Theming is **two independent axes**: `data-scheme` (light/dark) vs `data-theme` (named palette) — and `themes.css` loads separately from the styles barrel.
|
|
98
|
+
- **Density / spacing / radius are `@property` multipliers** — three knobs rescale the whole system (the parametric spatial system); e.g. `--a-density: 0.8` at a provider boundary.
|
|
63
99
|
- **Zero raw colors, zero raw px ≥ 3** in component CSS — always a token. This is mechanized by the `adia-lint` hook and by the MCP's `check_anti_patterns`.
|
|
100
|
+
- **Use a token for its named role, never by coincidence** — `--a-bg` is never a foreground, `--a-fg` never a background; a primary fill is `--a-primary` + `--a-chrome-light` text.
|
|
101
|
+
- **Text/icons on a filled primary disc** (radio dots, step circles, badge counters) use `--a-chrome-light` — theme-stable against any fill. (`--a-primary-fg` also resolves light post-Material-adoption; `--a-chrome-light` stays correct on arbitrary fills.)
|
|
102
|
+
- **Series/group identity colors are `--a-data-0..9`**; semantic tones (info/success/danger/accent) mark *state*, never identity.
|
|
103
|
+
- **Never re-ladder a foundation surface token** — don't override `--card-bg` with a bespoke elevation ladder; divergent ladders reintroduce the light/dark contrast inconsistency the symmetric canvas ramp prevents. (Per-element status tints are the legitimate exception.)
|
|
104
|
+
- **Global `[color]`/`[weight]` utilities override component colors by `@layer` order** — a filled control that repurposes `color=` must opt out explicitly; conversely `[weight]` does *not* beat component-scoped `font-weight` (use a variant).
|
|
105
|
+
- **Square/1:1 cells inheriting `--a-radius-md` render as circles** — small square cells take `--a-radius-sm`.
|
|
106
|
+
- **Subtle structure chrome** (gridlines, weekend tints, today-column) shares one "barely-there" contrast budget — calibrate against the canvas surfaces in both schemes; too low is invisible, too high is chrome riot.
|
|
64
107
|
|
|
65
108
|
## "Registers" — typographic treatments, opt-in
|
|
66
109
|
|
|
67
110
|
A _register_ (e.g. `verse`, `prose`) is a typographic treatment applied two ways together: **link the register stylesheet** (`styles/verse.css`) **and** put the **attribute** on the surface (`<my-surface verse>`). 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.
|
|
68
111
|
|
|
112
|
+
## Layout realities
|
|
113
|
+
|
|
114
|
+
- **A grid `auto`/`max-content` track collapses to ~1px around a flex wrapper** whose explicit width lives on an inner child — intrinsic max-content doesn't propagate through the wrapper; set the width on the wrapper itself (CSS, or JS-mirrored via ResizeObserver).
|
|
115
|
+
- **Full-height shells need an unbroken flex chain** — see the mount gotcha in [shell-admin.md](shell-admin.md); it applies to every shell.
|
|
116
|
+
|
|
69
117
|
## Where this is incomplete
|
|
70
118
|
|
|
71
119
|
The deep internals — the `html` template engine's escaping contract, `BaseController` delegation, the icon loader (`installIconLoadersForRegistered` + Vite `import.meta.glob`), `@bp` responsive notation, and the A2UI runtime/`<a2ui-root>` resolver — are framework-owned and version-specific. Treat the MCP (`lookup_component`, `search_chunks`) as the source of truth for them rather than encoding them here.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Contract: a2ui MCP tool surface (consumer side)
|
|
2
|
+
|
|
3
|
+
<!-- VENDORED TWIN: adia-forge ships references/contracts/a2ui-mcp-surface.md
|
|
4
|
+
(producer side). Changes here must be reconciled in BOTH copies. -->
|
|
5
|
+
|
|
6
|
+
The forge SHIPS `@adia-ai/a2ui-mcp` (source: `packages/a2ui/mcp/`); the factory
|
|
7
|
+
PINS it in `.mcp.json` and consumer skills drive it. Source of truth for the
|
|
8
|
+
full tool surface: `packages/a2ui/mcp/TOOLS.md` (30 tools, generated from
|
|
9
|
+
`server.js` — update both together).
|
|
10
|
+
|
|
11
|
+
## Stability rule (the load-bearing clause)
|
|
12
|
+
|
|
13
|
+
- **Changing an existing tool's input/output contract is a breaking change for
|
|
14
|
+
every external MCP client** (Claude Desktop, Cursor, the factory plugin).
|
|
15
|
+
It requires: a dry-run diff of the schema, an explicit operator proceed, a
|
|
16
|
+
version bump of `@adia-ai/a2ui-mcp`, and a factory-side pin update.
|
|
17
|
+
- **Adding tools is additive and safe.** Removing or renaming is breaking.
|
|
18
|
+
- The factory pins an exact version (`npx -y @adia-ai/a2ui-mcp@<exact>`);
|
|
19
|
+
producers must not assume consumers float.
|
|
20
|
+
|
|
21
|
+
## The consumer-load-bearing subset
|
|
22
|
+
|
|
23
|
+
Tools the factory's skills depend on by name — treat their contracts as
|
|
24
|
+
frozen-unless-versioned:
|
|
25
|
+
|
|
26
|
+
| Tool | Consumer use |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `generate_ui`, `refine_ui` | gen-UI generation loop (adia-genui) |
|
|
29
|
+
| `validate_schema`, `check_anti_patterns` | the trust gate on LLM-emitted A2UI (screen-composer) |
|
|
30
|
+
| `search_chunks`, `lookup_component`, `get_component_map`, `get_traits` | catalog literacy (adia-compose — "the MCP is the live catalog, don't memorize names") |
|
|
31
|
+
| `convert_html` | migration aid (adia-migrate) |
|
|
32
|
+
| `server_status` | connectivity probe (adia-verify) |
|
|
33
|
+
|
|
34
|
+
`refine_ui` note: present in the pinned server; keep TOOLS.md's entry in sync
|
|
35
|
+
with `server.js` when either changes (the 2026-06 red-team caught it missing).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Contract: MIGRATION GUIDE format (consumer side)
|
|
2
|
+
|
|
3
|
+
<!-- VENDORED TWIN: adia-forge ships references/contracts/migration-guide-format.md
|
|
4
|
+
(producer side). Cross-plugin imports are forbidden, so changes here must be
|
|
5
|
+
reconciled in BOTH copies. -->
|
|
6
|
+
|
|
7
|
+
`{repo}/.claude/docs/MIGRATION GUIDE.md` is the artifact the forge PRODUCES
|
|
8
|
+
(via `adia-release`, on every breaking cut) and the factory CONSUMES (via
|
|
9
|
+
`adia-migrate`, to drive consumer sweeps). This is the shared shape.
|
|
10
|
+
|
|
11
|
+
## Producer obligations (adia-release, per breaking cut)
|
|
12
|
+
|
|
13
|
+
1. **Version-scope bullet** in the top index: `**\`X.Y.Z\`** — <MINOR/PATCH>
|
|
14
|
+
lockstep cut. <BREAKING: one-line summary | No breaking changes.> See
|
|
15
|
+
[§ vX.Y.Z section below](#anchor).` Every cut gets a bullet, breaking or not —
|
|
16
|
+
the consumer sweep walks this index to find its version span.
|
|
17
|
+
2. **Per-cut section** for breaking cuts, containing, in order:
|
|
18
|
+
- the breaking-change list, each item with: the old form, the new form, and
|
|
19
|
+
a **greppable pattern** (`git grep -n '<old>'`) a consumer can sweep;
|
|
20
|
+
- mechanical vs **judgment items** separated — judgment items (semantic
|
|
21
|
+
flips, behavioral defaults) are explicitly marked *never auto-sweep*;
|
|
22
|
+
- the verify command(s) that prove the sweep landed.
|
|
23
|
+
3. **Anchors are stable** — section headings are linked from release notes and
|
|
24
|
+
consumer tooling; never retitle an existing section.
|
|
25
|
+
4. Soft-API graduations (`_debug.*` → top-level) are documented even though
|
|
26
|
+
non-breaking — consumers key on them.
|
|
27
|
+
|
|
28
|
+
## Consumer expectations (adia-migrate)
|
|
29
|
+
|
|
30
|
+
The sweep discipline reads: index bullet(s) for the span → per-cut sections →
|
|
31
|
+
grep audit with the given patterns → mechanical sweeps → judgment items
|
|
32
|
+
surfaced to the operator → verify gates. A guide entry missing a greppable
|
|
33
|
+
pattern or a verify command is a producer defect — report it upstream, do not
|
|
34
|
+
improvise a sweep.
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
name: data-and-hydration
|
|
3
3
|
load-when: wiring an adia-ui app's data-flow, state ownership, content hydration, or section registration
|
|
4
4
|
load-size: ~2.5k tokens
|
|
5
|
-
required-for: [adia-
|
|
5
|
+
required-for: [adia-data — all modes]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Data, state & hydration — patterns
|
|
9
9
|
|
|
10
|
-
Code shapes for the five data-flow patterns, the three hydration paths, and section wiring. The ownership rules (single-owner · projections-only · attribution) are the `adia-
|
|
10
|
+
Code shapes for the five data-flow patterns, the three hydration paths, and section wiring. The ownership rules (single-owner · projections-only · attribution) are the `adia-data` rubric gates.
|
|
11
11
|
|
|
12
12
|
## The five patterns
|
|
13
13
|
|
|
@@ -41,7 +41,9 @@ await client.mutate({ type: 'order', payload }, { action_source: btn.dataset.act
|
|
|
41
41
|
```js
|
|
42
42
|
table.columns = [{ key:'name', label:'Name', sortable:true }];
|
|
43
43
|
table.data = rows; // NOT: append <option>/<tr> children post-connect —
|
|
44
|
-
select.options = opts; // the element auto-stamps its slots at connected()
|
|
44
|
+
select.options = opts; // the element auto-stamps its slots at connected(); later-
|
|
45
|
+
// appended children land OUTSIDE the stamped popover/listbox
|
|
46
|
+
// as visible flow content and break the parent's layout
|
|
45
47
|
```
|
|
46
48
|
|
|
47
49
|
**5 · Declarative `data-*`** — static flows; state is CSS.
|
|
@@ -59,7 +61,7 @@ select.options = opts; // the element auto-stamps its slots at connected
|
|
|
59
61
|
connected() { if (this.#booted) return; this.#booted = true; this.#load(); }
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
**SSR — server → props → client.** The framework fetches on the server, seeds components as initial props, and the client refreshes via property binding (React `ref`+`useEffect`, Vue `:prop`, Svelte `bind:`). See `adia-
|
|
64
|
+
**SSR — server → props → client.** The framework fetches on the server, seeds components as initial props, and the client refreshes via property binding (React `ref`+`useEffect`, Vue `:prop`, Svelte `bind:`). See `adia-host` / [ssr-integration.md](ssr-integration.md).
|
|
63
65
|
|
|
64
66
|
**Hybrid — SPA island in an SSR page.** The server renders the page and emits the island's seed as a prop/attribute; the island **registers + boots on the client** and owns its own state and in-island routing. The framework owns the page and top-level routing; the island is a self-contained SPA surface inside it.
|
|
65
67
|
|
|
@@ -81,4 +83,5 @@ Rule: exactly one route owner _per scope_ — the framework routes the page; a c
|
|
|
81
83
|
|
|
82
84
|
- **Content-less `<router-ui>`** for in-DOM/in-island tabs: routes _without_ `content`; CSS shows the active view. A content-mode route fetches + `innerHTML`-replaces — wrong for stamped views.
|
|
83
85
|
- **Own the URL** when you need query params: `history.replaceState(...)` and reflect `data-route-path` yourself; don't set `router.routes` (it path-routes and clobbers query params).
|
|
84
|
-
- **Single owner** per piece of state — the
|
|
86
|
+
- **Single owner** per piece of state — the ownership assignments are the `adia-data` rubric gates; the mechanic: a control mutates the route, an observer/CSS reflects it back — never a second source of truth.
|
|
87
|
+
- **Never reset user-set state from a sibling control** — changing one selector (engine, tab) must not auto-reset an unrelated user-controlled one (mode, theme), even if the new selection ignores that setting.
|
package/references/genui-a2ui.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: genui-a2ui
|
|
3
3
|
load-when: authoring a generative-UI experience — mounting the a2ui runtime, feeding generated A2UI, wiring resolvers, or using the corpus
|
|
4
4
|
load-size: ~2.5k tokens
|
|
5
|
-
required-for: [adia-
|
|
5
|
+
required-for: [adia-genui — all modes]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# a2ui runtime & corpus — consumer surface
|
|
@@ -14,8 +14,8 @@ The consumer-facing a2ui runtime (mount + feed + resolve) and corpus (core vs cu
|
|
|
14
14
|
**`<a2ui-root>`** — renders an A2UI surface.
|
|
15
15
|
|
|
16
16
|
- **Author mode:** set `.doc = A2UIMessage[]` (a JS property; setting it re-renders). Use for editors/previews/tests.
|
|
17
|
-
- **Stream mode:** set `src` + `transport` (`sse` | `ws` | `jsonl` | `mcp`); the root opens the stream and reconciles messages as they arrive. (`doc`
|
|
18
|
-
- **Events:** `a2ui-message` (per message) · `a2ui-connected` / `a2ui-error` / `a2ui-closed` (stream) · `a2ui-action` (a child `[data-action]` fired).
|
|
17
|
+
- **Stream mode:** set `src` + `transport` (`sse` | `ws` | `jsonl` | `mcp`); the root opens the stream and reconciles messages as they arrive. (Don't set both — a `doc` reset does not abort an open stream; call `disconnect()` before switching to author mode.)
|
|
18
|
+
- **Events:** `a2ui-message` (per message) · `a2ui-connected` / `a2ui-error` / `a2ui-closed` (stream) · `a2ui-action` (a child `[data-action]` fired).
|
|
19
19
|
|
|
20
20
|
**`<gen-root mode="chat|split|canvas">`** — a layout shell unifying chat + canvas (+ `inspector` to debug the generated tree). Switch layout via `mode`.
|
|
21
21
|
|
|
@@ -30,10 +30,14 @@ The consumer-facing a2ui runtime (mount + feed + resolve) and corpus (core vs cu
|
|
|
30
30
|
|
|
31
31
|
## A2UI message format
|
|
32
32
|
|
|
33
|
+
A2UI describes **layout, not behavior**: component trees + props + data/slot
|
|
34
|
+
bindings + primitive refs — never JS behavior or per-canvas CSS. Behavior comes from the
|
|
35
|
+
substrate's traits or pre-built apps; "make the generator emit JS/CSS" is a won't-fix.
|
|
36
|
+
|
|
33
37
|
A discriminated union the runtime reconciles (set `.doc` to an array of these):
|
|
34
38
|
|
|
35
39
|
- `createSurface` — `{ type, surfaceId, root? }` — start a surface.
|
|
36
|
-
- `updateComponents` — `{ type, surfaceId, components: [{ id, component, children?, ...props }] }` — upsert the tree; `component` is an A2UI type name (`Card`, `Text`, `Stat`, …); other keys are props. A prop can be a **data binding** `{ path: 'kpi/value' }`.
|
|
40
|
+
- `updateComponents` — `{ type, surfaceId, components: [{ id, component, children?, ...props }] }` — upsert the tree; `component` is an A2UI type name (`Card`, `Text`, `Stat`, …); other keys are props. A prop can be a **data binding** `{ path: 'kpi/value' }`. A `textContent` prop only hard-sets text on pure-text leaves (the runtime whitelists them) — on container components it routes through the `text=` attribute instead, because `el.textContent = …` would wipe the slotted children.
|
|
37
41
|
- `updateDataModel` — `{ type, surfaceId, path, value }` — set data (JSON-Pointer path) that bindings read.
|
|
38
42
|
- `wireComponents` — `{ type, surfaceId, controllers?, dataSources?, … }` — declare controllers/handlers/data sources/actions.
|
|
39
43
|
- `meta` — `{ type, feedback? }` — pipeline metadata/reasoning (traces, feedback).
|
|
@@ -49,13 +53,15 @@ Built-in schemes: `resource:` (→ a `/api/...` REST convention), `api:` (direct
|
|
|
49
53
|
|
|
50
54
|
## The loop, end to end
|
|
51
55
|
|
|
52
|
-
`classify_intent` → `assemble_context` (or `search_chunks` to ground) → `generate_ui` → **`validate_schema` + `check_anti_patterns`** → set `root.doc` → `refine_ui
|
|
56
|
+
`classify_intent` → `assemble_context` (or `search_chunks` to ground) → `generate_ui` → **`validate_schema` + `check_anti_patterns`** → set `root.doc` → on failed validation, `refine_ui(intent, previousMessages, validationErrors)` (monolithic; zettel multi-turn refinement uses `refine_composition` with the `state_id` from the prior `compose_from_chunks`). **Validate before every render** — the runtime renders what you give it; garbage in, garbage rendered.
|
|
53
57
|
|
|
54
58
|
## Corpus — core vs roll-your-own
|
|
55
59
|
|
|
56
60
|
- **Shape:** chunks are JSON (`{ name, kind: block|panel|page, component tree, metadata }`), with an `_index`, a `catalog`, and pre-computed `chunk-embeddings`. Harvested from real pages' `data-chunk` markers.
|
|
57
61
|
- **Core:** the MCP ships and retrieves over it — `search_chunks` (keyword always; semantic when `VOYAGE_API_KEY`/OpenAI is set) / `lookup_chunk`. Offline-capable (embeddings are committed).
|
|
58
|
-
- **Roll your own** _(pattern, lightly documented):_ author demo pages → mark regions with `data-chunk` + `data-chunk-kind`/`-domain`/`-description`/`-keywords` →
|
|
62
|
+
- **Roll your own** _(pattern, lightly documented):_ author demo pages → mark regions with `data-chunk` + `data-chunk-kind`/`-slot`/`-domain`/`-description`/`-keywords` (the harvester also reads `-related`/`-tags`) → `npm run harvest:chunks` → point retrieval at your set → (optional) build embeddings (needs a key). **Grounding rule:** every chunk must trace to a real page — no synthetic chunks.
|
|
63
|
+
- **Keep chunks fresh:** after any layout/`@bp` change to `data-chunk`-annotated HTML, re-run `npm run harvest:chunks` in the same session — harvested chunks silently hold the stale values otherwise.
|
|
64
|
+
- **Dev-overlay caveat:** the `?chunks` overlay (`site/dev-chunks.js`) prepends a `<span data-chunk-marker>` into every `[data-chunk]` element — `:first-child`/`:nth-child` rules on those children break during dev only.
|
|
59
65
|
|
|
60
66
|
## Consumer vs maintainer
|
|
61
67
|
|
package/references/llm.md
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
name: llm
|
|
3
|
+
load-when: wiring @adia-ai/llm — chat/streamChat signatures, StreamChunk handling, the proxy security model
|
|
4
|
+
load-size: ~1.2k tokens
|
|
5
|
+
required-for: [adia-llm]
|
|
6
|
+
---
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
# `@adia-ai/llm` — the app-side LLM client
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
Chat / streaming / AI features for a consumer app. Snapshot verified against **@adia-ai/llm v0.7.26**. LLM client surfaces drift — confirm precise field names against the installed version and lean on the **smart-proxy contract** (stable) rather than memorized fields.
|
|
6
11
|
|
|
7
|
-
## Import &
|
|
12
|
+
## Import & core API
|
|
8
13
|
|
|
9
14
|
```js
|
|
10
15
|
import { chat, streamChat, createClient } from '@adia-ai/llm';
|
|
@@ -15,16 +20,16 @@ import { MODELS, DEFAULT_MODEL } from '@adia-ai/llm/models';
|
|
|
15
20
|
- `streamChat(opts) → AsyncGenerator<StreamChunk>` — streaming; `for await` the chunks.
|
|
16
21
|
- `createClient(defaults) → { chat, stream }` — a client with baked-in defaults.
|
|
17
22
|
|
|
18
|
-
`ChatOpts` (the fields
|
|
23
|
+
`ChatOpts` (the fields that matter): `model`, `messages: [{role, content}]`, `provider?` (auto-detected from the model name), `system?`, `maxTokens?`, `temperature?`, `signal?` (AbortSignal), `proxyUrl?`, plus Anthropic extras `thinking?` / `cache?`.
|
|
19
24
|
|
|
20
|
-
`StreamChunk` is a tagged union — branch on `chunk.type
|
|
25
|
+
`StreamChunk` is a tagged union — branch on `chunk.type`, handle every branch:
|
|
21
26
|
|
|
22
27
|
```js
|
|
23
28
|
for await (const chunk of streamChat(opts)) {
|
|
24
|
-
if
|
|
25
|
-
else if (chunk.type === 'thinking') showThinking(chunk.text);
|
|
29
|
+
if (chunk.type === 'text') append(chunk.text); // also .snapshot (full text so far)
|
|
30
|
+
else if (chunk.type === 'thinking') showThinking(chunk.text); // Anthropic extended thinking
|
|
26
31
|
else if (chunk.type === 'done') finalize(chunk.usage, chunk.stopReason);
|
|
27
|
-
else if (chunk.type === 'error') fail(chunk.error);
|
|
32
|
+
else if (chunk.type === 'error') fail(chunk.error); // render it — never drop
|
|
28
33
|
}
|
|
29
34
|
```
|
|
30
35
|
|
|
@@ -34,46 +39,24 @@ Anthropic, OpenAI, and Gemini, **auto-detected from the model name** (`claude*`
|
|
|
34
39
|
|
|
35
40
|
## The proxy / security model — read this first
|
|
36
41
|
|
|
37
|
-
**Never ship a provider API key to the browser in production.**
|
|
42
|
+
**Never ship a provider API key to the browser in production.** Two proxy shapes, auto-selected from the `proxyUrl` (a URL matching `/api/llm/<provider>/…` ⇒ passthrough; anything else ⇒ smart):
|
|
38
43
|
|
|
39
|
-
- **Smart proxy (production):** point `proxyUrl` at your own same-origin endpoint (e.g. `/api/chat`). The browser sends a provider-neutral body (`{provider, model, messages,
|
|
44
|
+
- **Smart proxy (production):** point `proxyUrl` at your own same-origin endpoint (e.g. `/api/chat`). The browser sends a provider-neutral body (`{provider, model, messages, system?, maxTokens?, temperature?}`); **your server** holds the real key, reformats per provider, and pipes the SSE bytes back. The browser never sees a key.
|
|
40
45
|
|
|
41
46
|
```js
|
|
42
47
|
for await (const c of streamChat({ proxyUrl: '/api/chat', model, messages })) { … }
|
|
43
48
|
```
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
Reference implementation (monorepo only — `server.js` is not in the npm-published files): `packages/llm/server.js` (`POST /api/chat`).
|
|
46
51
|
|
|
47
|
-
- **Passthrough proxy (
|
|
52
|
+
- **Passthrough proxy (Vite dev ONLY):** a `proxyUrl` matching `/api/llm/<provider>/…` (e.g. a Vite dev proxy). The browser sends the real upstream body **and the real key in headers** — anyone with DevTools can read it. **Never deploy this shape.**
|
|
48
53
|
|
|
49
|
-
When authoring: default to the smart-proxy pattern;
|
|
54
|
+
When authoring: default to the smart-proxy pattern; use passthrough only behind a Vite dev proxy, and say so explicitly.
|
|
50
55
|
|
|
51
|
-
##
|
|
56
|
+
## Chat surface wiring
|
|
52
57
|
|
|
53
|
-
`<chat-shell-
|
|
54
|
-
|
|
55
|
-
```html
|
|
56
|
-
<chat-shell-ui proxy-url="/api/chat" model="claude-sonnet-4-6">
|
|
57
|
-
<chat-header><span slot="name">Assistant</span><chat-status slot="status"></chat-status></chat-header>
|
|
58
|
-
<chat-thread><chat-empty><empty-state-ui heading="Hello!"></empty-state-ui></chat-empty></chat-thread>
|
|
59
|
-
<chat-composer><chat-input-ui placeholder="Message…"></chat-input-ui></chat-composer>
|
|
60
|
-
</chat-shell-ui>
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
- **Properties:** `model`, `provider`, `proxyUrl`, `system`, `thinking`, `streaming`.
|
|
64
|
-
- **Events:** `submit`, `chunk`, `thinking`, `done`, `error`, `abort`, `message`, `clear`.
|
|
65
|
-
- **Methods:** `send(text, {model})`, `appendMessage(...)`, `clear()`, `abort()`; accessors `messages` / `conversation` / `export` / `import(...)`.
|
|
66
|
-
|
|
67
|
-
Set `proxy-url` (or, dev-only, an `apiKey`) and it auto-sends on submit; otherwise it just emits `submit` for you to handle. For SSR, register it client-side like any other component (see `ssr-integration.md`).
|
|
68
|
-
|
|
69
|
-
## Boundary: app LLM vs. UI generation
|
|
70
|
-
|
|
71
|
-
Two separate paths — don't conflate them:
|
|
72
|
-
|
|
73
|
-
- **`@adia-ai/llm`** — a generic chat/streaming client for _your app's_ AI features (a chat box, a summarize button). Knows nothing about A2UI.
|
|
74
|
-
- **a2ui MCP `generate_ui`** — turns an intent into A2UI component markup (retrieval-first, LLM fallback). Use it to _build UI_, then `validate_schema` / `check_anti_patterns` (see `a2ui-mcp-tools.md`).
|
|
75
|
-
- The bridge (`createAdapter` from `@adia-ai/llm/bridge`) is what the A2UI generation pipeline uses internally to call an LLM for that fallback — you rarely call it directly.
|
|
58
|
+
`<chat-shell proxy-url="/api/chat" model="…">` auto-sends on submit via `streamChat` and renders the stream for you; without `proxy-url` (or a dev-only `apiKey` property) it only emits `submit` for you to handle. The full surface — cluster roster, props/events/methods, registration gotchas — lives in [`shell-chat.md`](shell-chat.md); this file owns only the client/proxy contract.
|
|
76
59
|
|
|
77
60
|
## Not in the package (don't assume)
|
|
78
61
|
|
|
79
|
-
Tool/function-calling chunks, structured-output/JSON-schema modes, built-in retry/backoff, and client-side token estimation are **not** surfaced by `@adia-ai/llm`
|
|
62
|
+
Tool/function-calling chunks, structured-output/JSON-schema modes, built-in retry/backoff, and client-side token estimation are **not** surfaced by `@adia-ai/llm` v0.7.26 — handle them in your own server layer. `createAdapter` from `@adia-ai/llm/bridge` is the A2UI generation pipeline's internal LLM adapter; an app rarely calls it directly.
|