aeon-ui-engine 1.4.0 → 1.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/CONSUMER.md +67 -27
  3. package/INTEGRATION.md +1 -1
  4. package/README.md +17 -8
  5. package/package.json +5 -2
  6. package/packages/cli/dist/bin.js +29 -3
  7. package/packages/cli/dist/{chunk-5L47SLNV.js → chunk-JMNRJCWF.js} +63 -9
  8. package/packages/cli/dist/index.d.ts +15 -1
  9. package/packages/cli/dist/index.js +3 -1
  10. package/packages/cli/node_modules/.bin/vitest +2 -2
  11. package/packages/cli/src/bin.ts +30 -2
  12. package/packages/cli/src/index.ts +1 -0
  13. package/packages/cli/src/init.ts +10 -4
  14. package/packages/cli/src/link.ts +61 -0
  15. package/packages/core/node_modules/.bin/vitest +2 -2
  16. package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  17. package/packages/panda/styled-system/styles.css +0 -4
  18. package/packages/primitives/dist/{chunk-PB3LFHXU.js → chunk-VUPWYBGU.js} +14 -0
  19. package/packages/primitives/dist/index.js +1 -1
  20. package/packages/primitives/dist/machines/range-slider.d.ts +3 -3
  21. package/packages/primitives/dist/machines/slider.d.ts +22 -0
  22. package/packages/primitives/dist/machines/slider.js +1 -1
  23. package/packages/primitives/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  24. package/packages/primitives/src/machines/slider.ts +17 -0
  25. package/packages/react/dist/index.d.ts +44 -5
  26. package/packages/react/dist/index.js +173 -48
  27. package/packages/react/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  28. package/packages/react/src/dialog/dialog.test.tsx +27 -0
  29. package/packages/react/src/dialog/index.tsx +11 -3
  30. package/packages/react/src/floating/FloatingPositioner.tsx +19 -1
  31. package/packages/react/src/hooks/use-anchor-position.ts +88 -17
  32. package/packages/react/src/menu/index.tsx +45 -6
  33. package/packages/react/src/popover/index.tsx +31 -4
  34. package/packages/react/src/slider/index.tsx +66 -19
  35. package/packages/react/src/tooltip/index.tsx +7 -1
  36. package/packages/schemas/dist/index.js +3 -1
  37. package/packages/schemas/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  38. package/packages/schemas/prompts/cursorrules.md +3 -1
  39. package/packages/schemas/registry/machines/slider.json +18 -1
  40. package/packages/schemas/src/prompts.ts +3 -1
  41. package/packages/tree/README.md +29 -0
  42. package/packages/tree/dist/index.d.ts +97 -0
  43. package/packages/tree/dist/index.js +107 -0
  44. package/packages/tree/node_modules/.bin/esbuild +21 -0
  45. package/packages/tree/node_modules/.bin/jiti +21 -0
  46. package/packages/tree/node_modules/.bin/tsc +21 -0
  47. package/packages/tree/node_modules/.bin/tsserver +21 -0
  48. package/packages/tree/node_modules/.bin/tsup +21 -0
  49. package/packages/tree/node_modules/.bin/tsup-node +21 -0
  50. package/packages/tree/node_modules/.bin/tsx +21 -0
  51. package/packages/tree/node_modules/.bin/vite +21 -0
  52. package/packages/tree/node_modules/.bin/vitest +21 -0
  53. package/packages/tree/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  54. package/packages/tree/package.json +42 -0
  55. package/packages/tree/src/exclusive-action.test.ts +54 -0
  56. package/packages/tree/src/exclusive-action.ts +104 -0
  57. package/packages/tree/src/index.ts +7 -0
  58. package/packages/tree/src/interpret.tsx +45 -0
  59. package/packages/tree/src/totality.ts +20 -0
  60. package/packages/tree/src/widget.ts +31 -0
  61. package/packages/tree/tsconfig.json +8 -0
  62. package/packages/tree/vitest.config.ts +8 -0
  63. package/scripts/link-scoped-packages.mjs +206 -0
  64. package/scripts/vite-aliases.mjs +9 -8
package/CHANGELOG.md CHANGED
@@ -1,5 +1,143 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.8.0] - 2026-09-27
4
+
5
+ ### Added
6
+
7
+ - **`Popover.Positioner` / `FloatingPositioner` `anchorRef` + `matchAnchorWidth`.** Position a surface against the region it belongs to instead of the small trigger pinned to its edge, so a wide panel lands where the eye already is rather than being clamped back from the viewport edge.
8
+ - **Optional philosophy note:** [docs/ANALOGY.md](./docs/ANALOGY.md) — Aeon as metaphor for formless truth and sensory projection (not product doctrine). Linked from [PHILOSOPHY.md](./PHILOSOPHY.md) and the docs index.
9
+ - `@aeon-ui/tree` — widget trees. A widget is immutable data describing one face
10
+ of a chart, interpreted into DOM with `data-aeon-scope|part|state`. Flutter's
11
+ Widget and Element layers only: no RenderObject (layout stays CSS), no
12
+ `StatefulWidget` (the chart is the only state).
13
+ - `exclusiveActionWidget` — the shared face for "confirm, then await" regions
14
+ (`idle → confirming → busy → idle | failure`): pending label, disabled set,
15
+ failure copy and confirm slot are functions of the snapshot.
16
+ - `uncoveredStates(states, project)` — totality as a test: every state the chart
17
+ names must appear on the tree projected for it.
18
+ - `interpret(tree, hosts)` — element layer; hosts slot existing compounds
19
+ (`Prompt`, fields) into names the tree declares.
20
+ - Vite aliases, optimizeDeps exclude and tsconfig paths include `@aeon-ui/tree`.
21
+
22
+ ## [1.7.7] - 2026-08-18
23
+
24
+ ### Added
25
+
26
+ - **Three more primitives in the live section, chosen for chart complexity.** `Slider` is hierarchical — the snapshot reports `interaction.dragging`, so the chart lights the region *and* the leaf inside it while you drag, and an arrow key shows `SET_VALUE` writing context without leaving the state. `FileUpload` runs a real drag-and-drop lifecycle (`idle` / `dragging` / `rejected`), driveable by dragging a file or by sending the same events from buttons. `Toast` is the one chart that moves without you: `visible` carries a delayed `TIMEOUT` that retires it on the machine's own clock.
27
+ - **`useSliderContext`** exported from `@aeon-ui/react`, so a slider's live machine state (including the nested `interaction` value) is readable from inside `Slider.Root` — matching `useAsyncContext` / `useFieldContext` / `useStickyBarContext`.
28
+
29
+ ### Fixed
30
+
31
+ - **The live chart never highlighted a component's *initial* state.** A late SVG swap replaced the diagram after the highlight was painted, so the first snapshot silently went dark until you interacted. The explorer now repaints when the rendered chart changes.
32
+ - **`valid` also lit up `invalid`.** Node matching compared id substrings, so any state whose name contained another's was highlighted too. Matching is now segment-aware, and a region's `[*]` marker no longer inherits its parent's highlight.
33
+ - **Nested snapshot values are highlighted properly.** A dotted path such as `interaction.dragging` now lights every node on the path, since the ancestors are genuinely active.
34
+ - **Live charts are readable.** The explorer box was short enough that parallel and nested diagrams auto-fit to ~30%; it now has room, and the slider's nested region lays out left-to-right.
35
+
36
+ ## [1.7.6] - 2026-08-18
37
+
38
+ ### Changed
39
+
40
+ - **Hero video: resolved an apparent contradiction in the narration.** "Nothing else exists" (about the async machine) could read as conflicting with the later child-machine states (broadcasting, seen, mined). The closed-world line is now scoped — "that's this machine's entire world" — and the hierarchy scene opens with the bridge: "Need more states? Charts nest." Transcript re-aligned (61s).
41
+
42
+ ## [1.7.5] - 2026-08-18
43
+
44
+ ### Changed
45
+
46
+ - **Hero video narration re-recorded for clarity.** "Statechart" is now fed to TTS as "state chart" so it isn't mispronounced (the on-screen subtitle keeps the correct spelling — `speak` overrides audio only, the same mechanism the closing URL already used). The parallel-regions explanation is rebuilt around three plain questions — "Has it been edited? Does it pass? Is it sending?" — and each chart lane now prints its question next to the region name, so the words and the diagram agree. Also clearer: "no in-between states" replaces "no half states", "your CSS targets that attribute" replaces "your styles key off it", and the child machine's middle state is spoken as "seen on the network" rather than a bare "seen". Transcript re-aligned (59s).
47
+
48
+ ## [1.7.4] - 2026-08-18
49
+
50
+ ### Changed
51
+
52
+ - **Hero video intro now shows the pain, and the ending is de-branded.** The opening scene names the actual symptoms of boolean soup — a mystery spinner with no context, two buttons wired to the same job (`Retry` / `Reload`), and "state is a mess" — instead of an abstract truth count. The narration no longer says the word "dirty" (the state node still shows it, since that's the real machine state). The hierarchy scene presents the nested send flow as "a real example / wallet · send" without naming the product. Transcript re-aligned (57s).
53
+
54
+ ## [1.7.3] - 2026-08-18
55
+
56
+ ### Changed
57
+
58
+ - **Hero video: plainer intro, glossed jargon, and a real hierarchical chart from a shipping product.** The intro now shows three plain `loading` / `empty` / `error` toggles with what each means in English, instead of prop-name jargon. The parallel-regions scene explains `dirty` outright ("dirty just means the user typed") and prints a plain-English gloss under the composite value. The machine catalog scene is replaced with the send flow from HandCash Desktop: the `sendPayment` chart (`editing → confirming → handoff`) with the `txLifecycle` child machine (`broadcasting → seenInMempool → mined`) nested inside `handoff` — the wallet surface and the breadcrumb (`handoff › broadcasting`) stay in lockstep with the chart. Also fixed a renderer bug where PIL ignored text alpha, so every text fade-in had been popping at full opacity (visible as floating state labels before their nodes appeared). Transcript re-aligned (55s).
59
+
60
+ ## [1.7.2] - 2026-08-18
61
+
62
+ ### Changed
63
+
64
+ - **New hero video, built from scratch to teach statecharts — not one animated state.** Seven scenes: three booleans producing a screen with a spinner and an error at once; the async machine with every state named and every edge labelled with its event; an event with no matching edge blocked at the door; the field machine's three parallel regions all true at once beside the surface that projects them; `state → data-aeon-state → pixels`; and four components each running their own machine on their own clock. Real system typography (SF / SF Mono), a poster frame from the chart scene, faststart streaming, and re-aligned transcript cues (52s).
65
+
66
+ ## [1.7.1] - 2026-08-18
67
+
68
+ ### Changed
69
+
70
+ - **Hero video now shows the actual thesis.** Previously the statechart node swept on its own float while the phone face animated separately, so the chart and the screen never agreed. The centrepiece is now one coupled demo: the component face and the highlighted chart node are the same state, driven by a single step timeline, with the causing event (`FETCH`, `RESOLVE`, `REJECT`, `REFRESH`) labelled on the transition edge — the same idea as the live section. The closing card points at "See it live" instead of the removed builder; transcript cues re-aligned.
71
+
72
+ ## [1.7.0] - 2026-08-18
73
+
74
+ ### Added
75
+
76
+ - **Live section: real components beside the chart that tracks them.** Async, Button, Field, and Dialog run their shipped machines; the Mermaid chart highlights the state the component is actually in and names the event that caused the move. Field reports all three regions at once, because a field has no single state.
77
+ - Machine-accurate diagrams for `buttonLifecycleMachine`, `dialogMachine`, and `fieldMachine`; the async chart now shows its `REFRESH` and re-fetch edges. `aeonDiagrams.test.ts` fails if a chart misses a machine state or invents one.
78
+
79
+ ### Fixed
80
+
81
+ - **A controlled `Dialog`, `Popover`, or `Menu` could never be opened by its own trigger.** `onOpenChange` reported the resolved prop, so an owner holding `open` never heard the machine's intent — the trigger, escape key, and outside click were all dead. Each root now reports the machine's own transition (and no longer fires on mount).
82
+
83
+ ### Removed
84
+
85
+ - **The generative app builder is gone** — sandbox, chart→schema compiler, live composer, model client, cost meter, and the `/api/aeon-generate` and `/api/aeon-scrape` endpoints, plus the OpenRouter proxy plugin and its env keys (~6,000 lines). Landing nav, hero, and header CTAs point at the live section.
86
+ - The static "Determinism" diagram section, now that the live charts replace it. `expandActiveStates` lost its builder-specific token aliases.
87
+
88
+ ## [1.6.1] - 2026-08-18
89
+
90
+ ### Changed
91
+
92
+ - **Builder previews read like designs, not state dumps.** The first screen now leads with the site's own hero headline and description (scraped, never invented); other screens are titled by their state once. Actions promote one primary button and group the rest instead of stacking equal buttons. The app bar no longer repeats the state label the dock already shows.
93
+ - **Device selector is back** — phone / tablet / desktop toggle above the preview, with the desktop frame showing the scraped host in its address bar.
94
+
95
+ ## [1.6.0] - 2026-08-18
96
+
97
+ ### Added
98
+
99
+ - **Redesign any site from its URL** — paste a domain into the builder and `/api/aeon-scrape` reads the page for its name, description, nav, actions, logo, and `theme-color`. The brand becomes chrome (logo in the app bar, accent palette scoped to the preview) and the page content becomes the prompt. Private and non-HTTP hosts are refused.
100
+ - `builder/projectFaces.ts` — generic chart projection: states become screens, transitions become actions, `collect` demos add a field, `async` demos add an async region.
101
+ - Demo tests now run in CI (`pnpm test`), covering projection, scraping, palette derivation, and a server render of the builder output.
102
+
103
+ ### Changed
104
+
105
+ - **App builder rebuilt around one input.** Prompt or URL, Build, preview. The starter chips, engine bar, cost meter, inspector tabs, device picker, and build loader are gone.
106
+ - **The model returns a chart and nothing else; the UI is projected from it.** Previously ~1,900 lines of prompt keyword matching decided that "match" meant a dating app, "faq" meant an accordion, and "pong" meant paddles, then filled screens with seeded catalogs, listings, and test stimuli. All of it is deleted, along with the preset app schemas it produced.
107
+ - Empty nav tabs are dropped instead of being filled with placeholder screens, and the offline fallback chart is deliberately shapeless rather than a product guess.
108
+
109
+ ## [1.5.2] - 2026-08-18
110
+
111
+ ### Changed
112
+
113
+ - **Landing page trimmed to essentials** — removed the pillars grid, the numbered setup box, and the full rule-file dump; the Cursor section is now one command plus a copy button. Hero shows a single install line, the why section keeps only the same-prompt contrast, specs show one chart, and the FAQ is four questions.
114
+
115
+ ## [1.5.1] - 2026-08-18
116
+
117
+ ### Added
118
+
119
+ - **Scoped package linking (Next.js works without aliases)** — `postinstall` and `npx aeon-ui link` symlink `node_modules/@aeon-ui/*` → `aeon-ui-engine/packages/*`. Next.js consumers now need only `transpilePackages` plus `"use client"`; the Vite plugin is optional (optimizeDeps convenience).
120
+ - **`aeon-ui link` CLI command** — `--cwd`, `--force`, `--quiet`; also runs at the end of `npx aeon-ui init`. Exported as `aeon-ui-engine/link`.
121
+
122
+ ### Changed
123
+
124
+ - CONSUMER.md, README, INTEGRATION, VITE_CONSUMER, MIGRATION_FROM_ARK, PUBLISHING: install once, link automatically, Vite plugin optional; `file:` link workaround for Next.js removed.
125
+ - Landing FAQ and Cursor rules snippet state that Aeon is React-first with Next.js supported, and that Vue / Solid / Svelte are thin adapters rather than the main catalog.
126
+
127
+ ## [1.4.1] - 2026-08-17
128
+
129
+ ### Fixed
130
+
131
+ - **Floating surfaces are collision aware** — `Menu` / `Popover` / `Tooltip` / `Select` positioners now accept `bottom-end` / `top-end` / `*-center` placements, flip when the preferred side has no room, and clamp inside the viewport. Right-aligned triggers (avatar menus in a sticky header) no longer render offscreen.
132
+ - **Positioner stacking is configurable** — `gutter` and `zIndex` props on `Menu.Positioner`, `Popover.Positioner`, `Tooltip.Positioner` replace the hardcoded 4px gap and `z-index: 50`, so menus can sit above app chrome.
133
+ - **Menu keyboard behavior** — `Menu.Content` takes focus while open, restores focus to the trigger on dismiss, and closes on Escape from anywhere on the page (previously only when focus was already inside the surface).
134
+ - **Controlled `Slider` reports interaction** — pointer and keyboard input now call `onValueChange` even when `value` is set, so a controlled thumb (audio seek bar) follows its prop instead of Aeon's internal value. `onValueChange` no longer fires on mount or echoes the controlled prop.
135
+ - **`sliderMachine` bounds follow the host** — new `SET_BOUNDS` / `SET_DISABLED` events, sent by `Slider.Root` on prop change, so a range that grows later (track duration arriving) stops clamping to the initial `max`.
136
+
137
+ ### Added
138
+
139
+ - Positioners publish `data-side`, `data-align`, `--aeon-available-height`, and `--aeon-anchor-width` for recipes that cap their own scroll area.
140
+
3
141
  ## [1.4.0] - 2026-08-17
4
142
 
5
143
  ### Added
@@ -13,6 +151,7 @@
13
151
  ### Changed
14
152
 
15
153
  - COMPONENTS / MACHINES / PRIMITIVE_COVERAGE / API_PATTERNS updated for 1.4 primitives.
154
+ - **aeon-ui.com** hero, FAQ, Cursor docs, CONSUMER, and README now lead with `npx aeon-ui migrate` for existing apps.
16
155
 
17
156
  ## [1.3.6] - 2026-07-31
18
157
 
package/CONSUMER.md CHANGED
@@ -1,6 +1,16 @@
1
1
  # Using `aeon-ui-engine` from npm
2
2
 
3
- One package. One plugin. Done.
3
+ One package. Install once. `@aeon-ui/*` resolves automatically.
4
+
5
+ Aeon is **React-first** (Next.js included). Vue / Solid / Svelte adapters are thin — not the main product.
6
+
7
+ ## 0. Existing app? Migrate first
8
+
9
+ ```bash
10
+ npx aeon-ui migrate
11
+ ```
12
+
13
+ Scans for known local workarounds (custom dropzones, range sliders, toast bridges, hand-rolled link buttons) and writes `AEON_MIGRATION_CHECKLIST.md`. Use `--check` to print findings only. New projects can skip straight to install + `npx aeon-ui init`.
4
14
 
5
15
  ## 1. Install
6
16
 
@@ -8,51 +18,81 @@ One package. One plugin. Done.
8
18
  npm install aeon-ui-engine react react-dom xstate @xstate/react
9
19
  ```
10
20
 
11
- ## 2. Vite plugin
21
+ **Postinstall** runs `scripts/link-scoped-packages.mjs`, which symlinks:
12
22
 
13
- ```ts
14
- // vite.config.ts
15
- import react from '@vitejs/plugin-react'
16
- import { defineConfig } from 'vite'
17
- import { aeonUiVitePlugin } from 'aeon-ui-engine/vite'
23
+ `node_modules/@aeon-ui/*` → `node_modules/aeon-ui-engine/packages/*`
18
24
 
19
- export default defineConfig({
20
- plugins: [react(), aeonUiVitePlugin()],
21
- })
25
+ Re-run manually anytime:
26
+
27
+ ```bash
28
+ npx aeon-ui link
29
+ # or
30
+ node node_modules/aeon-ui-engine/scripts/link-scoped-packages.mjs
22
31
  ```
23
32
 
24
- ## 3. Import CSS (one line)
33
+ No bundler aliases are required for `@aeon-ui/ui` / `@aeon-ui/react` imports.
34
+
35
+ ## 2. Import CSS (one line)
25
36
 
26
37
  ```tsx
27
- // main.tsx or app entry
38
+ // app/layout.tsx (Next.js) or main.tsx (Vite)
28
39
  import 'aeon-ui-engine/aeon.css'
29
40
  ```
30
41
 
31
- ## 4. Use components
42
+ ## 3. Use components
32
43
 
33
44
  ```tsx
34
45
  import { Button, Dialog, Menu, Toast } from '@aeon-ui/ui'
35
46
  ```
36
47
 
37
- That's it. The plugin handles all `@aeon-ui/*` aliases, TypeScript paths, and optimizeDeps.
48
+ ---
49
+
50
+ ## Next.js
51
+
52
+ ```js
53
+ // next.config.mjs
54
+ export default {
55
+ transpilePackages: [
56
+ '@aeon-ui/ui',
57
+ '@aeon-ui/react',
58
+ '@aeon-ui/core',
59
+ '@aeon-ui/primitives',
60
+ '@aeon-ui/panda',
61
+ ],
62
+ }
63
+ ```
64
+
65
+ Mark interactive surfaces as Client Components:
66
+
67
+ ```tsx
68
+ 'use client'
69
+ import { Button } from '@aeon-ui/ui'
70
+ ```
71
+
72
+ No `resolve.alias` / webpack alias block. If Turbopack mis-resolves after install, run `npx aeon-ui link` and prefer `next dev --webpack` while evaluating.
38
73
 
39
74
  ---
40
75
 
41
- ## Optional: TypeScript
76
+ ## Vite (optional plugin)
42
77
 
43
- If your tsconfig doesn't extend the engine base:
78
+ Scoped imports already work via the link step. The Vite plugin is optional — it still helps with `optimizeDeps` and monorepo `packagesRoot`:
44
79
 
45
- ```json
46
- {
47
- "compilerOptions": {
48
- "paths": {
49
- "@aeon-ui/*": ["./node_modules/aeon-ui-engine/packages/*/src"]
50
- }
51
- }
52
- }
80
+ ```ts
81
+ // vite.config.ts
82
+ import react from '@vitejs/plugin-react'
83
+ import { defineConfig } from 'vite'
84
+ import { aeonUiVitePlugin } from 'aeon-ui-engine/vite'
85
+
86
+ export default defineConfig({
87
+ plugins: [react(), aeonUiVitePlugin()],
88
+ })
53
89
  ```
54
90
 
55
- Or extend directly:
91
+ ---
92
+
93
+ ## Optional: TypeScript
94
+
95
+ Linked packages usually typecheck via each package’s `types` field. If needed:
56
96
 
57
97
  ```json
58
98
  {
@@ -117,7 +157,7 @@ Style with any CSS system using `data-aeon-scope`, `data-aeon-part`, `data-aeon-
117
157
 
118
158
  ## Optional: Local monorepo
119
159
 
120
- Point the plugin at a local checkout:
160
+ Point the Vite plugin at a local checkout (or set `AEON_UI_ROOT` before `aeon-ui link`):
121
161
 
122
162
  ```ts
123
163
  aeonUiVitePlugin({ packagesRoot: '/path/to/aeon-ui/packages' })
@@ -125,4 +165,4 @@ aeonUiVitePlugin({ packagesRoot: '/path/to/aeon-ui/packages' })
125
165
 
126
166
  ## Reference consumer
127
167
 
128
- [Casino-games](https://github.com/GenericCPU/Casino-games) uses this exact pattern.
168
+ [Casino-games](https://github.com/GenericCPU/Casino-games) uses the engine install pattern. Next.js consumers: install + link + `transpilePackages` (see above).
package/INTEGRATION.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Agents:** [AGENTS.md](./AGENTS.md) · [docs/API_PATTERNS.md](./docs/API_PATTERNS.md)
4
4
 
5
- **npm consumers:** start with **[CONSUMER.md](./CONSUMER.md)** — install `aeon-ui-engine`, peer deps, and `aeonUiViteAliases()` before any `@aeon-ui/*` import works.
5
+ **npm consumers:** start with **[CONSUMER.md](./CONSUMER.md)** — install `aeon-ui-engine` (postinstall links `@aeon-ui/*`), peer deps, then import. Vite plugin is optional; Next.js needs `transpilePackages` only.
6
6
 
7
7
  Aeon UI is **three layers**. You can use one or all.
8
8
 
package/README.md CHANGED
@@ -16,7 +16,7 @@ Aeon UI models interactive behavior as executable [statecharts](https://www.thes
16
16
  | `@aeon-ui/primitives` | XState machines (toggle, dialog, tabs, accordion, async, button lifecycle, field) |
17
17
  | `@aeon-ui/react` | React headless bindings |
18
18
  | `@aeon-ui/schemas` | JSON Schema catalog + agent system prompts |
19
- | `@aeon-ui/cli` | `npx aeon-ui init` — Cursor rules, schemas, starter |
19
+ | `@aeon-ui/cli` | `npx aeon-ui init` · `link` · `migrate` — Cursor rules, scoped package link, migration checklist |
20
20
  | `@aeon-ui/panda` | Design tokens, `defineSlotRecipe` styles, generated `styled-system` |
21
21
  | `@aeon-ui/ui` | Styled components (`aeonButton`, `aeonAccordion`, …) |
22
22
  | `@aeon-ui/vue` | Vue 3 headless adapter (v0.1: Switch + `useAeonMachine`) |
@@ -33,13 +33,27 @@ Teach an AI agent first (optional but recommended):
33
33
  npx aeon-ui init
34
34
  ```
35
35
 
36
- The monorepo is published on npm as **`aeon-ui-engine`** (bundles all `@aeon-ui/*` workspace packages):
36
+ Migrating an existing app? Scan for known workarounds and write a checklist:
37
+
38
+ ```bash
39
+ npx aeon-ui migrate
40
+ ```
41
+
42
+ The monorepo is published on npm as **`aeon-ui-engine`** (bundles all `@aeon-ui/*` workspace packages). Postinstall links `node_modules/@aeon-ui/*` so **Next.js and Vite** resolve imports without aliases:
37
43
 
38
44
  ```bash
39
45
  npm install aeon-ui-engine react react-dom xstate @xstate/react
46
+ # re-link if needed: npx aeon-ui link
40
47
  ```
41
48
 
42
- Add the Vite plugin and import CSS:
49
+ ```tsx
50
+ // app/layout.tsx or main.tsx
51
+ import 'aeon-ui-engine/aeon.css'
52
+ ```
53
+
54
+ **Next.js** — add `transpilePackages` for `@aeon-ui/ui`, `@aeon-ui/react`, `@aeon-ui/core`, `@aeon-ui/primitives`, `@aeon-ui/panda`, and use `"use client"` for interactive components.
55
+
56
+ **Vite (optional)** — `aeonUiVitePlugin()` still helps with optimizeDeps; linking alone is enough for `@aeon-ui/*` resolution:
43
57
 
44
58
  ```ts
45
59
  // vite.config.ts
@@ -49,11 +63,6 @@ export default defineConfig({
49
63
  })
50
64
  ```
51
65
 
52
- ```tsx
53
- // main.tsx
54
- import 'aeon-ui-engine/aeon.css'
55
- ```
56
-
57
66
  Full setup guide: **[CONSUMER.md](./CONSUMER.md)**.
58
67
 
59
68
  Open [https://github.com/GenericCPU/aeon-ui](https://github.com/GenericCPU/aeon-ui) for the repo. The marketing demo lives at [https://aeon-ui.com](https://aeon-ui.com) — set `VITE_SITE_ORIGIN` to that origin in Vercel so Open Graph previews resolve the social image in `apps/demo/public/` correctly.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aeon-ui-engine",
3
- "version": "1.4.0",
3
+ "version": "1.8.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "exports": {
10
10
  "./vite": "./scripts/vite-aliases.mjs",
11
+ "./link": "./scripts/link-scoped-packages.mjs",
11
12
  "./aeon.css": "./packages/panda/aeon.css",
12
13
  "./tsconfig.base.json": "./tsconfig.base.json",
13
14
  "./CONSUMER.md": "./CONSUMER.md",
@@ -25,13 +26,15 @@
25
26
  "CHANGELOG.md"
26
27
  ],
27
28
  "scripts": {
29
+ "postinstall": "node ./scripts/link-scoped-packages.mjs",
30
+ "link:scoped": "node ./scripts/link-scoped-packages.mjs",
28
31
  "build": "pnpm --filter @aeon-ui/panda run codegen && pnpm -r run build",
29
32
  "build:headless": "pnpm --filter @aeon-ui/core --filter @aeon-ui/primitives --filter @aeon-ui/react run build",
30
33
  "codegen": "pnpm --filter @aeon-ui/panda codegen",
31
34
  "dev": "pnpm --filter @aeon-ui/demo dev",
32
35
  "examples": "pnpm --filter @aeon-ui/examples dev",
33
36
  "typecheck": "pnpm -r run typecheck",
34
- "test": "pnpm run test:headless",
37
+ "test": "pnpm run test:headless && pnpm --filter @aeon-ui/demo run test",
35
38
  "test:headless": "pnpm run build:headless && pnpm --filter @aeon-ui/core --filter @aeon-ui/primitives --filter @aeon-ui/react run test",
36
39
  "changeset": "changeset",
37
40
  "version-packages": "changeset version",
@@ -1,37 +1,63 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  initAeon,
4
+ linkAeon,
4
5
  migrateAeon
5
- } from "./chunk-5L47SLNV.js";
6
+ } from "./chunk-JMNRJCWF.js";
6
7
 
7
8
  // src/bin.ts
8
9
  var args = process.argv.slice(2);
9
10
  var cmd = args[0] ?? "help";
10
11
  var force = args.includes("--force") || args.includes("-f");
11
12
  var checkOnly = args.includes("--check");
13
+ var quiet = args.includes("--quiet") || args.includes("-q");
14
+ function argValue(flag) {
15
+ const i = args.indexOf(flag);
16
+ if (i >= 0 && args[i + 1] && !args[i + 1].startsWith("-")) return args[i + 1];
17
+ return void 0;
18
+ }
19
+ var cwdArg = argValue("--cwd");
12
20
  function help() {
13
21
  console.log(`aeon-ui \u2014 AI-optimized CLI for Aeon UI
14
22
 
15
23
  Usage:
16
24
  npx aeon-ui init [--force]
25
+ npx aeon-ui link [--quiet] [--force] [--cwd <dir>]
17
26
  npx aeon-ui migrate [--check]
18
27
 
19
28
  Commands:
20
29
  init Inject Cursor rules, JSON schemas, system prompt, and a starter component
30
+ link Symlink node_modules/@aeon-ui/* \u2192 aeon-ui-engine/packages/* (also runs on postinstall)
21
31
  migrate Scan for known workarounds and write AEON_MIGRATION_CHECKLIST.md
22
32
  help Show this message
23
33
 
24
34
  Options:
25
- --force, -f Overwrite existing generated files (init)
35
+ --force, -f Overwrite existing generated files (init); replace existing @aeon-ui links (link)
26
36
  --check Report findings only; do not write checklist (migrate)
37
+ --quiet, -q Suppress link output (link)
38
+ --cwd <dir> Project root for link (defaults to process.cwd())
27
39
  `);
28
40
  }
29
41
  if (cmd === "init") {
30
- const result = initAeon({ force });
42
+ const result = initAeon({ force, cwd: cwdArg });
31
43
  console.log(`Aeon UI init \u2192 ${result.cwd}`);
32
44
  for (const line of result.written) console.log(` ${line}`);
45
+ try {
46
+ linkAeon({ cwd: result.cwd, quiet: true });
47
+ console.log(" link @aeon-ui/* (scoped packages)");
48
+ } catch {
49
+ console.log(" link skipped (install aeon-ui-engine first, then: npx aeon-ui link)");
50
+ }
33
51
  console.log("\nNext: npm install aeon-ui-engine react react-dom xstate @xstate/react");
34
52
  console.log("Docs: AEON_AI.md \xB7 paste aeon.cursorrules.md into your agent system prompt");
53
+ } else if (cmd === "link") {
54
+ try {
55
+ const result = linkAeon({ quiet, force, cwd: cwdArg });
56
+ if (quiet) console.log(`Aeon UI link \u2192 ${result.cwd}`);
57
+ } catch (err) {
58
+ console.error(err instanceof Error ? err.message : err);
59
+ process.exitCode = 1;
60
+ }
35
61
  } else if (cmd === "migrate") {
36
62
  const result = migrateAeon({ write: !checkOnly });
37
63
  console.log(`Aeon UI migrate \u2192 ${result.cwd}`);
@@ -85,10 +85,16 @@ Generated by \`npx aeon-ui init\`.
85
85
  ## Next steps
86
86
 
87
87
  1. Install: \`npm install aeon-ui-engine react react-dom xstate @xstate/react\`
88
- 2. CSS: \`import 'aeon-ui-engine/aeon.css'\`
89
- 3. Vite: add \`aeonUiVitePlugin()\` from \`aeon-ui-engine/vite\`
90
- 4. Paste \`aeon.cursorrules.md\` into Cursor rules / system prompt (already mirrored to \`.cursor/rules/aeon-ui.mdc\`)
91
- 5. Constrain generation with JSON under \`aeon.schemas/\`
88
+ - Postinstall links \`@aeon-ui/*\` into \`node_modules\` (re-run with \`npx aeon-ui link\` if needed).
89
+ 2. CSS: \`import 'aeon-ui-engine/aeon.css'\` (e.g. root layout).
90
+ 3. **Next.js:** add \`transpilePackages\` for \`@aeon-ui/ui\`, \`@aeon-ui/react\`, \`@aeon-ui/core\`, \`@aeon-ui/primitives\`, \`@aeon-ui/panda\`. Use \`"use client"\` for interactive components. No webpack aliases required.
91
+ 4. **Vite (optional):** \`aeonUiVitePlugin()\` from \`aeon-ui-engine/vite\` still helps with optimizeDeps; scoped imports resolve via the link step alone.
92
+ 5. Paste \`aeon.cursorrules.md\` into Cursor rules / system prompt (already mirrored to \`.cursor/rules/aeon-ui.mdc\`)
93
+ 6. Constrain generation with JSON under \`aeon.schemas/\`
94
+
95
+ ## Frameworks
96
+
97
+ Aeon is **React-first** (full catalog + styled layer). Next.js is supported. Vue / Solid / Svelte adapters exist but are thin \u2014 not the main product.
92
98
 
93
99
  ## Principle
94
100
 
@@ -98,9 +104,56 @@ Generated by \`npx aeon-ui init\`.
98
104
  return { cwd, written };
99
105
  }
100
106
 
107
+ // src/link.ts
108
+ import { spawnSync } from "child_process";
109
+ import { createRequire } from "module";
110
+ import { existsSync as existsSync2 } from "fs";
111
+ import { dirname, join as join2 } from "path";
112
+ import { fileURLToPath } from "url";
113
+ function resolveLinkScript() {
114
+ const here = dirname(fileURLToPath(import.meta.url));
115
+ const fromMonorepo = join2(here, "../../../scripts/link-scoped-packages.mjs");
116
+ if (existsSync2(fromMonorepo)) return fromMonorepo;
117
+ try {
118
+ const require2 = createRequire(import.meta.url);
119
+ const engineRoot = dirname(require2.resolve("aeon-ui-engine/package.json"));
120
+ return join2(engineRoot, "scripts/link-scoped-packages.mjs");
121
+ } catch {
122
+ throw new Error(
123
+ "Could not find scripts/link-scoped-packages.mjs (aeon-ui-engine not installed?)"
124
+ );
125
+ }
126
+ }
127
+ function linkAeon(options = {}) {
128
+ const script = resolveLinkScript();
129
+ const args = [script];
130
+ if (options.cwd) {
131
+ args.push("--cwd", options.cwd);
132
+ }
133
+ if (options.quiet) {
134
+ args.push("--quiet");
135
+ }
136
+ if (options.force) {
137
+ args.push("--force");
138
+ }
139
+ const result = spawnSync(process.execPath, args, {
140
+ stdio: options.quiet ? "pipe" : "inherit",
141
+ env: process.env,
142
+ cwd: options.cwd ?? process.cwd()
143
+ });
144
+ if (result.status !== 0 && result.status !== null) {
145
+ const err = result.stderr?.toString() || `link exited with code ${result.status}`;
146
+ throw new Error(err);
147
+ }
148
+ return {
149
+ cwd: options.cwd ?? process.cwd(),
150
+ status: result.status ?? 0
151
+ };
152
+ }
153
+
101
154
  // src/migrate.ts
102
- import { existsSync as existsSync2, readFileSync, writeFileSync as writeFileSync2 } from "fs";
103
- import { join as join2 } from "path";
155
+ import { existsSync as existsSync3, readFileSync, writeFileSync as writeFileSync2 } from "fs";
156
+ import { join as join3 } from "path";
104
157
  var FINDINGS = [
105
158
  {
106
159
  id: "preview-window-machine",
@@ -156,8 +209,8 @@ function walkHints(cwd) {
156
209
  const hits = [];
157
210
  let corpus = "";
158
211
  for (const rel of CANDIDATES) {
159
- const abs = join2(cwd, rel);
160
- if (!existsSync2(abs)) continue;
212
+ const abs = join3(cwd, rel);
213
+ if (!existsSync3(abs)) continue;
161
214
  try {
162
215
  corpus += `
163
216
  ${rel}
@@ -228,7 +281,7 @@ function migrateAeon(options = {}) {
228
281
  const findings = walkHints(cwd);
229
282
  const written = [];
230
283
  if (write) {
231
- const path = join2(cwd, "AEON_MIGRATION_CHECKLIST.md");
284
+ const path = join3(cwd, "AEON_MIGRATION_CHECKLIST.md");
232
285
  const findingsBlock = findings.length === 0 ? "\n## Scan results\n\nNo known local workarounds detected in the quick scan.\n" : `
233
286
  ## Scan results
234
287
 
@@ -243,5 +296,6 @@ ${findingsBlock}`);
243
296
 
244
297
  export {
245
298
  initAeon,
299
+ linkAeon,
246
300
  migrateAeon
247
301
  };
@@ -7,6 +7,20 @@ declare function initAeon(options?: InitOptions): {
7
7
  written: string[];
8
8
  };
9
9
 
10
+ type LinkOptions = {
11
+ cwd?: string;
12
+ quiet?: boolean;
13
+ force?: boolean;
14
+ };
15
+ /**
16
+ * Symlink `node_modules/@aeon-ui/*` → `aeon-ui-engine/packages/*`.
17
+ * Prefer the postinstall hook; this command re-runs linking manually.
18
+ */
19
+ declare function linkAeon(options?: LinkOptions): {
20
+ cwd: string;
21
+ status: number;
22
+ };
23
+
10
24
  type MigrateOptions = {
11
25
  cwd?: string;
12
26
  write?: boolean;
@@ -23,4 +37,4 @@ declare function migrateAeon(options?: MigrateOptions): {
23
37
  findings: Finding[];
24
38
  };
25
39
 
26
- export { type InitOptions, type MigrateOptions, initAeon, migrateAeon };
40
+ export { type InitOptions, type LinkOptions, type MigrateOptions, initAeon, linkAeon, migrateAeon };
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  initAeon,
3
+ linkAeon,
3
4
  migrateAeon
4
- } from "./chunk-5L47SLNV.js";
5
+ } from "./chunk-JMNRJCWF.js";
5
6
  export {
6
7
  initAeon,
8
+ linkAeon,
7
9
  migrateAeon
8
10
  };
@@ -10,9 +10,9 @@ case `uname` in
10
10
  esac
11
11
 
12
12
  if [ -z "$NODE_PATH" ]; then
13
- export NODE_PATH="/Users/brandoncryderman/AeonUI/node_modules/.pnpm/vitest@4.1.8_@types+node@22.19.19_jsdom@29.1.1_vite@8.0.16_@types+node@22.19.19_esbuild@0.28.0_jiti@2.7.0_tsx@4.22.3_/node_modules/vitest/node_modules:/Users/brandoncryderman/AeonUI/node_modules/.pnpm/vitest@4.1.8_@types+node@22.19.19_jsdom@29.1.1_vite@8.0.16_@types+node@22.19.19_esbuild@0.28.0_jiti@2.7.0_tsx@4.22.3_/node_modules:/Users/brandoncryderman/AeonUI/node_modules/.pnpm/node_modules"
13
+ export NODE_PATH="/Users/brandoncryderman/AeonUI/node_modules/.pnpm/vitest@4.1.8_@types+node@22.19.19_jsdom@29.1.1_vite@8.0.16_@types+node@22.19.19_esbuild@0.27.7_jiti@2.7.0_tsx@4.22.3_/node_modules/vitest/node_modules:/Users/brandoncryderman/AeonUI/node_modules/.pnpm/vitest@4.1.8_@types+node@22.19.19_jsdom@29.1.1_vite@8.0.16_@types+node@22.19.19_esbuild@0.27.7_jiti@2.7.0_tsx@4.22.3_/node_modules:/Users/brandoncryderman/AeonUI/node_modules/.pnpm/node_modules"
14
14
  else
15
- export NODE_PATH="/Users/brandoncryderman/AeonUI/node_modules/.pnpm/vitest@4.1.8_@types+node@22.19.19_jsdom@29.1.1_vite@8.0.16_@types+node@22.19.19_esbuild@0.28.0_jiti@2.7.0_tsx@4.22.3_/node_modules/vitest/node_modules:/Users/brandoncryderman/AeonUI/node_modules/.pnpm/vitest@4.1.8_@types+node@22.19.19_jsdom@29.1.1_vite@8.0.16_@types+node@22.19.19_esbuild@0.28.0_jiti@2.7.0_tsx@4.22.3_/node_modules:/Users/brandoncryderman/AeonUI/node_modules/.pnpm/node_modules:$NODE_PATH"
15
+ export NODE_PATH="/Users/brandoncryderman/AeonUI/node_modules/.pnpm/vitest@4.1.8_@types+node@22.19.19_jsdom@29.1.1_vite@8.0.16_@types+node@22.19.19_esbuild@0.27.7_jiti@2.7.0_tsx@4.22.3_/node_modules/vitest/node_modules:/Users/brandoncryderman/AeonUI/node_modules/.pnpm/vitest@4.1.8_@types+node@22.19.19_jsdom@29.1.1_vite@8.0.16_@types+node@22.19.19_esbuild@0.27.7_jiti@2.7.0_tsx@4.22.3_/node_modules:/Users/brandoncryderman/AeonUI/node_modules/.pnpm/node_modules:$NODE_PATH"
16
16
  fi
17
17
  if [ -x "$basedir/node" ]; then
18
18
  exec "$basedir/node" "$basedir/../vitest/vitest.mjs" "$@"