aeon-ui-engine 1.4.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/CONSUMER.md +67 -27
- package/INTEGRATION.md +1 -1
- package/README.md +17 -8
- package/package.json +4 -1
- package/packages/cli/dist/bin.js +29 -3
- package/packages/cli/dist/{chunk-5L47SLNV.js → chunk-JMNRJCWF.js} +63 -9
- package/packages/cli/dist/index.d.ts +15 -1
- package/packages/cli/dist/index.js +3 -1
- package/packages/cli/src/bin.ts +30 -2
- package/packages/cli/src/index.ts +1 -0
- package/packages/cli/src/init.ts +10 -4
- package/packages/cli/src/link.ts +61 -0
- package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/packages/primitives/dist/{chunk-PB3LFHXU.js → chunk-VUPWYBGU.js} +14 -0
- package/packages/primitives/dist/index.js +1 -1
- package/packages/primitives/dist/machines/range-slider.d.ts +3 -3
- package/packages/primitives/dist/machines/slider.d.ts +22 -0
- package/packages/primitives/dist/machines/slider.js +1 -1
- package/packages/primitives/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/packages/primitives/src/machines/slider.ts +17 -0
- package/packages/react/dist/index.d.ts +15 -1
- package/packages/react/dist/index.js +89 -21
- package/packages/react/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/packages/react/src/floating/FloatingPositioner.tsx +8 -0
- package/packages/react/src/hooks/use-anchor-position.ts +88 -17
- package/packages/react/src/menu/index.tsx +37 -3
- package/packages/react/src/popover/index.tsx +7 -1
- package/packages/react/src/slider/index.tsx +54 -14
- package/packages/react/src/tooltip/index.tsx +7 -1
- package/packages/schemas/prompts/cursorrules.md +3 -1
- package/packages/schemas/prompts/statecharts.md +1 -1
- package/packages/schemas/src/prompts.ts +3 -1
- package/scripts/link-scoped-packages.mjs +206 -0
- package/scripts/vite-aliases.mjs +6 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.5.1] - 2026-08-18
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **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).
|
|
8
|
+
- **`aeon-ui link` CLI command** — `--cwd`, `--force`, `--quiet`; also runs at the end of `npx aeon-ui init`. Exported as `aeon-ui-engine/link`.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- CONSUMER.md, README, INTEGRATION, VITE_CONSUMER, MIGRATION_FROM_ARK, PUBLISHING: install once, link automatically, Vite plugin optional; `file:` link workaround for Next.js removed.
|
|
13
|
+
- 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.
|
|
14
|
+
|
|
15
|
+
## [1.4.1] - 2026-08-17
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **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.
|
|
20
|
+
- **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.
|
|
21
|
+
- **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).
|
|
22
|
+
- **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.
|
|
23
|
+
- **`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`.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Positioners publish `data-side`, `data-align`, `--aeon-available-height`, and `--aeon-anchor-width` for recipes that cap their own scroll area.
|
|
28
|
+
|
|
3
29
|
## [1.4.0] - 2026-08-17
|
|
4
30
|
|
|
5
31
|
### Added
|
|
@@ -13,6 +39,7 @@
|
|
|
13
39
|
### Changed
|
|
14
40
|
|
|
15
41
|
- COMPONENTS / MACHINES / PRIMITIVE_COVERAGE / API_PATTERNS updated for 1.4 primitives.
|
|
42
|
+
- **aeon-ui.com** hero, FAQ, Cursor docs, CONSUMER, and README now lead with `npx aeon-ui migrate` for existing apps.
|
|
16
43
|
|
|
17
44
|
## [1.3.6] - 2026-07-31
|
|
18
45
|
|
package/CONSUMER.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# Using `aeon-ui-engine` from npm
|
|
2
2
|
|
|
3
|
-
One package.
|
|
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
|
-
|
|
21
|
+
**Postinstall** runs `scripts/link-scoped-packages.mjs`, which symlinks:
|
|
12
22
|
|
|
13
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
38
|
+
// app/layout.tsx (Next.js) or main.tsx (Vite)
|
|
28
39
|
import 'aeon-ui-engine/aeon.css'
|
|
29
40
|
```
|
|
30
41
|
|
|
31
|
-
##
|
|
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
|
-
|
|
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
|
-
##
|
|
76
|
+
## Vite (optional plugin)
|
|
42
77
|
|
|
43
|
-
|
|
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
|
-
```
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "1.5.1",
|
|
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,6 +26,8 @@
|
|
|
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",
|
package/packages/cli/dist/bin.js
CHANGED
|
@@ -1,37 +1,63 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
initAeon,
|
|
4
|
+
linkAeon,
|
|
4
5
|
migrateAeon
|
|
5
|
-
} from "./chunk-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
103
|
-
import { join as
|
|
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 =
|
|
160
|
-
if (!
|
|
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 =
|
|
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 };
|
package/packages/cli/src/bin.ts
CHANGED
|
@@ -1,36 +1,64 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { initAeon } from './init.js'
|
|
3
|
+
import { linkAeon } from './link.js'
|
|
3
4
|
import { migrateAeon } from './migrate.js'
|
|
4
5
|
|
|
5
6
|
const args = process.argv.slice(2)
|
|
6
7
|
const cmd = args[0] ?? 'help'
|
|
7
8
|
const force = args.includes('--force') || args.includes('-f')
|
|
8
9
|
const checkOnly = args.includes('--check')
|
|
10
|
+
const quiet = args.includes('--quiet') || args.includes('-q')
|
|
11
|
+
|
|
12
|
+
function argValue(flag: string): string | undefined {
|
|
13
|
+
const i = args.indexOf(flag)
|
|
14
|
+
if (i >= 0 && args[i + 1] && !args[i + 1].startsWith('-')) return args[i + 1]
|
|
15
|
+
return undefined
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const cwdArg = argValue('--cwd')
|
|
9
19
|
|
|
10
20
|
function help() {
|
|
11
21
|
console.log(`aeon-ui — AI-optimized CLI for Aeon UI
|
|
12
22
|
|
|
13
23
|
Usage:
|
|
14
24
|
npx aeon-ui init [--force]
|
|
25
|
+
npx aeon-ui link [--quiet] [--force] [--cwd <dir>]
|
|
15
26
|
npx aeon-ui migrate [--check]
|
|
16
27
|
|
|
17
28
|
Commands:
|
|
18
29
|
init Inject Cursor rules, JSON schemas, system prompt, and a starter component
|
|
30
|
+
link Symlink node_modules/@aeon-ui/* → aeon-ui-engine/packages/* (also runs on postinstall)
|
|
19
31
|
migrate Scan for known workarounds and write AEON_MIGRATION_CHECKLIST.md
|
|
20
32
|
help Show this message
|
|
21
33
|
|
|
22
34
|
Options:
|
|
23
|
-
--force, -f Overwrite existing generated files (init)
|
|
35
|
+
--force, -f Overwrite existing generated files (init); replace existing @aeon-ui links (link)
|
|
24
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())
|
|
25
39
|
`)
|
|
26
40
|
}
|
|
27
41
|
|
|
28
42
|
if (cmd === 'init') {
|
|
29
|
-
const result = initAeon({ force })
|
|
43
|
+
const result = initAeon({ force, cwd: cwdArg })
|
|
30
44
|
console.log(`Aeon UI init → ${result.cwd}`)
|
|
31
45
|
for (const line of result.written) console.log(` ${line}`)
|
|
46
|
+
try {
|
|
47
|
+
linkAeon({ cwd: result.cwd, quiet: true })
|
|
48
|
+
console.log(' link @aeon-ui/* (scoped packages)')
|
|
49
|
+
} catch {
|
|
50
|
+
console.log(' link skipped (install aeon-ui-engine first, then: npx aeon-ui link)')
|
|
51
|
+
}
|
|
32
52
|
console.log('\nNext: npm install aeon-ui-engine react react-dom xstate @xstate/react')
|
|
33
53
|
console.log('Docs: AEON_AI.md · paste aeon.cursorrules.md into your agent system prompt')
|
|
54
|
+
} else if (cmd === 'link') {
|
|
55
|
+
try {
|
|
56
|
+
const result = linkAeon({ quiet, force, cwd: cwdArg })
|
|
57
|
+
if (quiet) console.log(`Aeon UI link → ${result.cwd}`)
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.error(err instanceof Error ? err.message : err)
|
|
60
|
+
process.exitCode = 1
|
|
61
|
+
}
|
|
34
62
|
} else if (cmd === 'migrate') {
|
|
35
63
|
const result = migrateAeon({ write: !checkOnly })
|
|
36
64
|
console.log(`Aeon UI migrate → ${result.cwd}`)
|
package/packages/cli/src/init.ts
CHANGED
|
@@ -92,10 +92,16 @@ Generated by \`npx aeon-ui init\`.
|
|
|
92
92
|
## Next steps
|
|
93
93
|
|
|
94
94
|
1. Install: \`npm install aeon-ui-engine react react-dom xstate @xstate/react\`
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
- Postinstall links \`@aeon-ui/*\` into \`node_modules\` (re-run with \`npx aeon-ui link\` if needed).
|
|
96
|
+
2. CSS: \`import 'aeon-ui-engine/aeon.css'\` (e.g. root layout).
|
|
97
|
+
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.
|
|
98
|
+
4. **Vite (optional):** \`aeonUiVitePlugin()\` from \`aeon-ui-engine/vite\` still helps with optimizeDeps; scoped imports resolve via the link step alone.
|
|
99
|
+
5. Paste \`aeon.cursorrules.md\` into Cursor rules / system prompt (already mirrored to \`.cursor/rules/aeon-ui.mdc\`)
|
|
100
|
+
6. Constrain generation with JSON under \`aeon.schemas/\`
|
|
101
|
+
|
|
102
|
+
## Frameworks
|
|
103
|
+
|
|
104
|
+
Aeon is **React-first** (full catalog + styled layer). Next.js is supported. Vue / Solid / Svelte adapters exist but are thin — not the main product.
|
|
99
105
|
|
|
100
106
|
## Principle
|
|
101
107
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
import { createRequire } from 'node:module'
|
|
3
|
+
import { existsSync } from 'node:fs'
|
|
4
|
+
import { dirname, join } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
|
|
7
|
+
export type LinkOptions = {
|
|
8
|
+
cwd?: string
|
|
9
|
+
quiet?: boolean
|
|
10
|
+
force?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function resolveLinkScript(): string {
|
|
14
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
15
|
+
const fromMonorepo = join(here, '../../../scripts/link-scoped-packages.mjs')
|
|
16
|
+
if (existsSync(fromMonorepo)) return fromMonorepo
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const require = createRequire(import.meta.url)
|
|
20
|
+
const engineRoot = dirname(require.resolve('aeon-ui-engine/package.json'))
|
|
21
|
+
return join(engineRoot, 'scripts/link-scoped-packages.mjs')
|
|
22
|
+
} catch {
|
|
23
|
+
throw new Error(
|
|
24
|
+
'Could not find scripts/link-scoped-packages.mjs (aeon-ui-engine not installed?)',
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Symlink `node_modules/@aeon-ui/*` → `aeon-ui-engine/packages/*`.
|
|
31
|
+
* Prefer the postinstall hook; this command re-runs linking manually.
|
|
32
|
+
*/
|
|
33
|
+
export function linkAeon(options: LinkOptions = {}) {
|
|
34
|
+
const script = resolveLinkScript()
|
|
35
|
+
const args = [script]
|
|
36
|
+
if (options.cwd) {
|
|
37
|
+
args.push('--cwd', options.cwd)
|
|
38
|
+
}
|
|
39
|
+
if (options.quiet) {
|
|
40
|
+
args.push('--quiet')
|
|
41
|
+
}
|
|
42
|
+
if (options.force) {
|
|
43
|
+
args.push('--force')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const result = spawnSync(process.execPath, args, {
|
|
47
|
+
stdio: options.quiet ? 'pipe' : 'inherit',
|
|
48
|
+
env: process.env,
|
|
49
|
+
cwd: options.cwd ?? process.cwd(),
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
if (result.status !== 0 && result.status !== null) {
|
|
53
|
+
const err = result.stderr?.toString() || `link exited with code ${result.status}`
|
|
54
|
+
throw new Error(err)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
cwd: options.cwd ?? process.cwd(),
|
|
59
|
+
status: result.status ?? 0,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"3.2.4","results":[[":src/machine.test.ts",{"duration":
|
|
1
|
+
{"version":"3.2.4","results":[[":src/machine.test.ts",{"duration":7.0908339999999725,"failed":false}],[":src/theme.test.ts",{"duration":3.866333999999995,"failed":false}],[":../../apps/demo/src/aeonSchemaCompiler.test.ts",{"duration":7.737041999999974,"failed":false}],[":src/identicon.test.ts",{"duration":9.875333000000012,"failed":false}]]}
|
|
@@ -29,6 +29,14 @@ var sliderMachine = setup({
|
|
|
29
29
|
}),
|
|
30
30
|
end: assign({
|
|
31
31
|
value: ({ context }) => context.max
|
|
32
|
+
}),
|
|
33
|
+
setBounds: assign(({ context, event }) => {
|
|
34
|
+
if (event.type !== "SET_BOUNDS") return context;
|
|
35
|
+
const { min, max, step } = event;
|
|
36
|
+
return { ...context, min, max, step, value: snap(context.value, min, max, step) };
|
|
37
|
+
}),
|
|
38
|
+
setDisabled: assign({
|
|
39
|
+
disabled: ({ context, event }) => event.type === "SET_DISABLED" ? event.disabled : context.disabled
|
|
32
40
|
})
|
|
33
41
|
}
|
|
34
42
|
}).createMachine({
|
|
@@ -41,6 +49,12 @@ var sliderMachine = setup({
|
|
|
41
49
|
disabled: input.disabled ?? false
|
|
42
50
|
}),
|
|
43
51
|
initial: "interaction",
|
|
52
|
+
// Bounds and disabled follow the host regardless of interaction state, so a
|
|
53
|
+
// range that widens (audio duration arriving) stops clamping to stale limits.
|
|
54
|
+
on: {
|
|
55
|
+
SET_BOUNDS: { actions: "setBounds" },
|
|
56
|
+
SET_DISABLED: { actions: "setDisabled" }
|
|
57
|
+
},
|
|
44
58
|
states: {
|
|
45
59
|
interaction: {
|
|
46
60
|
initial: "idle",
|