@adia-ai/a2ui-compose 0.0.1 → 0.2.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.
Files changed (31) hide show
  1. package/CHANGELOG.md +143 -1
  2. package/README.md +17 -14
  3. package/{engine → core}/generator.js +13 -13
  4. package/{engine → core}/state.js +2 -2
  5. package/index.js +4 -4
  6. package/package.json +17 -14
  7. package/{engines → strategies}/monolithic/_shared.js +9 -9
  8. package/{engines → strategies}/monolithic/generate-instant.js +8 -8
  9. package/{engines → strategies}/monolithic/generate-pro.js +10 -10
  10. package/{engines → strategies}/monolithic/generate-thinking.js +8 -8
  11. package/{engines → strategies}/registry.js +6 -6
  12. package/strategies/zettel/chunk-composer.js +182 -0
  13. package/strategies/zettel/chunk-refiner.js +514 -0
  14. package/strategies/zettel/chunk-synthesizer.js +235 -0
  15. package/{engines → strategies}/zettel/generate.js +1 -1
  16. package/strategies/zettel/issue-reporter.js +380 -0
  17. package/{engines → strategies}/zettel/session-store.js +1 -1
  18. package/strategies/zettel/state-cache.js +153 -0
  19. package/transpiler/transpiler.js +1 -1
  20. package/engine/constitution.md +0 -78
  21. /package/{engine → core}/artifacts.js +0 -0
  22. /package/{engine → core}/context-store.js +0 -0
  23. /package/{engine → core}/pattern-export.js +0 -0
  24. /package/{engine → core}/pipeline/engine.js +0 -0
  25. /package/{engine → core}/pipeline/types.js +0 -0
  26. /package/{engine → core}/reference.js +0 -0
  27. /package/{engines → strategies}/zettel/_smoke.js +0 -0
  28. /package/{engines → strategies}/zettel/composer.js +0 -0
  29. /package/{engines → strategies}/zettel/fragment-library.js +0 -0
  30. /package/{engines → strategies}/zettel/generator-adapter.js +0 -0
  31. /package/{engines → strategies}/zettel/synthesizer.js +0 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * State-cache — bounded LRU for multi-turn gen-UI compositions.
3
+ *
4
+ * Keyed by `state_id`. Bounded by `maxSize` (default 64; configurable via the
5
+ * `A2UI_STATE_CACHE_SIZE` env var). When the cache is at capacity, the
6
+ * least-recently-touched entry is evicted on the next `set`. Entries are
7
+ * touched on every `get` and on every overwriting `set`. `peek` reads without
8
+ * touching recency — used by the issue-reporter when attaching traces.
9
+ *
10
+ * Pure data structure: no I/O, no LLM, no async. Safe to instantiate as a
11
+ * server-process singleton; survives only as long as the MCP process.
12
+ *
13
+ * Recommended entry shape (caller-enforced — the cache itself stores any object):
14
+ * {
15
+ * state_id, // mirrors the key
16
+ * intent, // user's request that produced this state
17
+ * html, // materialized output
18
+ * plan, // chunk-binding plan (or null for refinements)
19
+ * ops_history, // chronological A2UI message list
20
+ * parent_state_id, // chain-back for refinements (null for created roots)
21
+ * created_at, // ISO timestamp
22
+ * }
23
+ *
24
+ * Spec: docs/specs/genui-multiturn-architecture.md §2.2 + §4.4.
25
+ */
26
+
27
+ const DEFAULT_MAX_SIZE = 64;
28
+
29
+ function envMaxSize() {
30
+ const v = process.env.A2UI_STATE_CACHE_SIZE;
31
+ if (!v) return null;
32
+ const n = parseInt(v, 10);
33
+ return Number.isFinite(n) && n > 0 ? n : null;
34
+ }
35
+
36
+ export class StateCache {
37
+ constructor({ maxSize } = {}) {
38
+ this.maxSize = maxSize ?? envMaxSize() ?? DEFAULT_MAX_SIZE;
39
+ this._map = new Map();
40
+ }
41
+
42
+ /** Insert or update; touches recency. Returns the state_id. */
43
+ set(state_id, state) {
44
+ if (this._map.has(state_id)) {
45
+ this._map.delete(state_id);
46
+ } else if (this._map.size >= this.maxSize) {
47
+ const firstKey = this._map.keys().next().value;
48
+ this._map.delete(firstKey);
49
+ }
50
+ this._map.set(state_id, state);
51
+ return state_id;
52
+ }
53
+
54
+ /** Read; touches recency on hit. Returns null on miss. */
55
+ get(state_id) {
56
+ if (!this._map.has(state_id)) return null;
57
+ const entry = this._map.get(state_id);
58
+ this._map.delete(state_id);
59
+ this._map.set(state_id, entry);
60
+ return entry;
61
+ }
62
+
63
+ /** Read without touching recency. Returns null on miss. */
64
+ peek(state_id) {
65
+ return this._map.get(state_id) ?? null;
66
+ }
67
+
68
+ has(state_id) {
69
+ return this._map.has(state_id);
70
+ }
71
+
72
+ /** Returns true if removed, false if absent. */
73
+ evict(state_id) {
74
+ return this._map.delete(state_id);
75
+ }
76
+
77
+ /** Insertion-ordered list of keys (oldest → newest). */
78
+ list() {
79
+ return Array.from(this._map.keys());
80
+ }
81
+
82
+ size() {
83
+ return this._map.size;
84
+ }
85
+
86
+ clear() {
87
+ this._map.clear();
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Mint a state_id for a freshly created composition.
93
+ *
94
+ * Format: `<intent-prefix>-<rand4>-v<N>-<unix-min>`
95
+ * - intent-prefix — first whitespace-delimited word of the intent, lowercased,
96
+ * non-alphanumeric stripped, capped at 12 chars; falls back to `state` when empty.
97
+ * - rand4 — 4 hex chars; prevents collisions on identical intents.
98
+ * - v<N> — 1-based version (v1 for created, v2+ for refinements).
99
+ * - unix-min — Math.floor(Date.now() / 60000), giving a compact minute-resolution
100
+ * timestamp that humans can scan for chronological order.
101
+ *
102
+ * Spec: docs/specs/genui-multiturn-architecture.md §2.2.
103
+ *
104
+ * @param {string} intent
105
+ * @param {number} [version=1]
106
+ * @returns {string}
107
+ */
108
+ export function mintStateId(intent, version = 1) {
109
+ const prefix = (intent ?? '')
110
+ .toLowerCase()
111
+ .split(/\s+/)[0]
112
+ ?.replace(/[^a-z0-9]/g, '')
113
+ ?.slice(0, 12) || 'state';
114
+ const rand4 = Math.floor(Math.random() * 0xffff).toString(16).padStart(4, '0');
115
+ const unixMin = Math.floor(Date.now() / 60000);
116
+ const v = Math.max(1, Math.floor(version));
117
+ return `${prefix}-${rand4}-v${v}-${unixMin}`;
118
+ }
119
+
120
+ /**
121
+ * Mint the next state_id in a refinement chain.
122
+ *
123
+ * Preserves the parent's prefix + rand4 stem so the lineage is visible at a
124
+ * glance (`dash-3f9a-v1-…` → `dash-3f9a-v2-…`). Bumps the version and
125
+ * timestamp. Falls back to a fresh `mintStateId` if the parent id doesn't
126
+ * match the expected format.
127
+ *
128
+ * @param {string} parentStateId
129
+ * @param {number} version — should be parent's version + 1
130
+ * @returns {string}
131
+ */
132
+ export function mintNextStateId(parentStateId, version) {
133
+ const m = parentStateId?.match(/^([a-z0-9]+)-([a-f0-9]{4})-v\d+-\d+$/);
134
+ if (!m) return mintStateId(parentStateId ?? '', version);
135
+ const [, prefix, rand4] = m;
136
+ const unixMin = Math.floor(Date.now() / 60000);
137
+ const v = Math.max(1, Math.floor(version));
138
+ return `${prefix}-${rand4}-v${v}-${unixMin}`;
139
+ }
140
+
141
+ let _cache = null;
142
+
143
+ /** Module-level singleton. Lazily instantiated. */
144
+ export function getStateCache(opts) {
145
+ if (!_cache) _cache = new StateCache(opts);
146
+ return _cache;
147
+ }
148
+
149
+ /** Replace the singleton; useful for tests + server boot. */
150
+ export function resetStateCache(opts) {
151
+ _cache = new StateCache(opts);
152
+ return _cache;
153
+ }
@@ -15,7 +15,7 @@ import {
15
15
  SKIP_TAGS, extractProps, inferGap,
16
16
  } from './transpiler-maps.js';
17
17
  import { validateSchema } from '../../validator/validator.js';
18
- import { getContext } from '../engine/reference.js';
18
+ import { getContext } from '../core/reference.js';
19
19
 
20
20
  // ═══════════════════════════════════════════════════════════════
21
21
  // PUBLIC API
@@ -1,78 +0,0 @@
1
- # AdiaUI Generation Constitution
2
-
3
- Rules the generator MUST satisfy. Used by the RLAIF critique-revise stage
4
- to evaluate and improve generated output. Each rule maps to a validator check.
5
-
6
- ---
7
-
8
- ## Card Content Model
9
-
10
- - Every `Card` MUST have at least one of: `Header`, `Section`, `Footer`.
11
- - `Section` content MUST be wrapped in a `Column` component, not placed directly.
12
- - Headings (`h1`-`h6`) belong in `Header`, not `Section`.
13
- - Action buttons belong in `Footer`, not `Section` or `Header` (unless using `slot="action"`).
14
- - `Header` supports 4 slots: `slot="icon"` (leading), `slot="heading"` (title), `slot="description"` (subtitle), `slot="action"` (trailing badge/button).
15
-
16
- ## Typography & Variant System
17
-
18
- - Use semantic HTML elements with `variant` attribute: `<h1 variant="title">`, `<h3 variant="section">`, `<p variant="body">`.
19
- - Never use `data-heading` — use `variant` instead.
20
- - Text hierarchy: display > title > heading > section > subsection > body > deck > caption > kicker > label.
21
- - Use `color="subtle"` or `color="muted"` for secondary text, not inline styles.
22
-
23
- ## Layout Primitives
24
-
25
- - `Column` (col-ui): vertical stack. Use numeric `gap` values: `gap="2"`, `gap="4"`, `gap="6"`.
26
- - `Row` (row-ui): horizontal. Use `gap`, `justify`, `align` attributes.
27
- - `Grid` (grid-ui): CSS grid. Use `columns="2|3|4"`.
28
- - Never use named gaps (`gap="sm"`, `gap="md"`, `gap="lg"`) — always numeric.
29
- - Never use inline `style` attributes for layout.
30
- - Never use CSS class names.
31
-
32
- ## Component Types
33
-
34
- - Use `Input` for text inputs (not `TextField` or `TextInput`).
35
- - Use `CheckBox` for checkboxes (not `Checkbox`).
36
- - Use `Select` for dropdowns (not `ChoicePicker`).
37
- - Use `Toggle` for switch controls.
38
- - Use `Button` with `variant="primary"` for main CTA, `variant="outline"` for secondary.
39
- - Button text via `text` prop, icon via `icon` prop (Phosphor icon name).
40
-
41
- ## Flat Adjacency Protocol
42
-
43
- - Every component has a unique `id`.
44
- - Root component MUST have `id: "root"`.
45
- - Children referenced by ID array: `children: ["child-1", "child-2"]`.
46
- - IDs should be short and descriptive: `"hdr"`, `"email-field"`, `"submit-btn"`.
47
- - No circular references.
48
- - No orphaned components (every non-root must be referenced by a parent).
49
-
50
- ## Anti-Patterns (DO NOT)
51
-
52
- 1. **No bare divs** — never use `<div>` or HTML-only elements. Use AdiaUI components.
53
- 2. **No flat adjacency violations** — every Text inside Section must be inside a Column wrapper.
54
- 3. **No heading hierarchy skips** — don't jump from h1 to h3.
55
- 4. **No duplicate IDs** — every component must have a unique ID.
56
- 5. **No content directly in Tab** — Tab is a button strip item, not a content container.
57
- 6. **No invented components** — only use types registered in the A2UI registry.
58
- 7. **No slot on containers** — `slot="heading"` goes on the heading element, not on Header.
59
- 8. **No inline styles** — use component props and tokens, not `style="..."`.
60
-
61
- ## Prose & Content Context
62
-
63
- - For marketing/content pages, wrap in `<section prose>` to shift to content-optimized typography.
64
- - Centered page headers use: `<header align="center" size="lg">` → `<col-ui gap="4">` → kicker/display/deck.
65
- - Use `nomargin` on typography elements inside cards to prevent double-spacing.
66
-
67
- ## Icon Names
68
-
69
- - Use Phosphor icon names: `arrow-right`, `check-circle`, `warning-circle`, etc.
70
- - Brand icons need `-logo` suffix: `google-logo`, `github-logo`, `twitter-logo`.
71
- - Common aliases are resolved automatically: `send`→`paper-plane-right`, `settings`→`gear`, `mail`→`envelope`.
72
-
73
- ## Accessibility
74
-
75
- - Interactive elements must have `aria-label` if no visible text.
76
- - Icon-only buttons must have `aria-label`.
77
- - Images must have `alt` text.
78
- - Form inputs must have `label` prop.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes