aeon-ui-engine 1.3.5 → 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 +52 -0
- package/CONSUMER.md +67 -27
- package/INTEGRATION.md +1 -1
- package/README.md +17 -8
- package/package.json +4 -1
- package/packages/cli/README.md +13 -1
- package/packages/cli/dist/bin.js +50 -6
- package/packages/cli/dist/chunk-JMNRJCWF.js +301 -0
- package/packages/cli/dist/index.d.ts +31 -1
- package/packages/cli/dist/index.js +7 -3
- package/packages/cli/src/bin.ts +50 -4
- package/packages/cli/src/index.ts +2 -0
- package/packages/cli/src/init.ts +10 -4
- package/packages/cli/src/link.ts +61 -0
- package/packages/cli/src/migrate.ts +170 -0
- package/packages/core/dist/index.d.ts +35 -7
- package/packages/core/dist/index.js +102 -2
- package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/packages/core/src/anatomy.ts +39 -3
- package/packages/core/src/identicon.test.ts +39 -0
- package/packages/core/src/identicon.ts +79 -0
- package/packages/core/src/index.ts +1 -0
- package/packages/panda/panda.config.ts +4 -0
- package/packages/panda/src/recipes/composer.ts +58 -4
- package/packages/panda/src/recipes/file-upload.ts +100 -0
- package/packages/panda/src/recipes/index.ts +2 -0
- package/packages/panda/src/recipes/range-slider.ts +59 -0
- package/packages/panda/src/recipes/thread.ts +77 -2
- package/packages/panda/styled-system/recipes/aeon-composer.d.ts +1 -1
- package/packages/panda/styled-system/recipes/aeon-composer.mjs +12 -0
- package/packages/panda/styled-system/recipes/aeon-file-upload.d.ts +31 -0
- package/packages/panda/styled-system/recipes/aeon-file-upload.mjs +61 -0
- package/packages/panda/styled-system/recipes/aeon-range-slider.d.ts +31 -0
- package/packages/panda/styled-system/recipes/aeon-range-slider.mjs +49 -0
- package/packages/panda/styled-system/recipes/aeon-thread.d.ts +1 -1
- package/packages/panda/styled-system/recipes/aeon-thread.mjs +20 -0
- package/packages/panda/styled-system/recipes/index.d.ts +2 -0
- package/packages/panda/styled-system/recipes/index.mjs +2 -0
- package/packages/panda/styled-system/styles.css +292 -0
- package/packages/primitives/dist/chunk-CV4UXXZF.js +44 -0
- package/packages/primitives/dist/chunk-IZKQSCYU.js +164 -0
- package/packages/primitives/dist/{chunk-PB3LFHXU.js → chunk-VUPWYBGU.js} +14 -0
- package/packages/primitives/dist/index.d.ts +2 -0
- package/packages/primitives/dist/index.js +23 -7
- package/packages/primitives/dist/machines/file-upload.d.ts +39 -0
- package/packages/primitives/dist/machines/file-upload.js +8 -0
- package/packages/primitives/dist/machines/range-slider.d.ts +129 -0
- package/packages/primitives/dist/machines/range-slider.js +12 -0
- 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/__tests__/file-upload.test.ts +25 -0
- package/packages/primitives/src/__tests__/range-slider.test.ts +39 -0
- package/packages/primitives/src/index.ts +2 -0
- package/packages/primitives/src/machines/file-upload.ts +55 -0
- package/packages/primitives/src/machines/range-slider.ts +209 -0
- package/packages/primitives/src/machines/slider.ts +17 -0
- package/packages/react/dist/index.d.ts +132 -26
- package/packages/react/dist/index.js +1822 -1029
- package/packages/react/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/packages/react/src/button/index.tsx +50 -15
- package/packages/react/src/composer/index.tsx +58 -3
- package/packages/react/src/file-upload/index.tsx +322 -0
- package/packages/react/src/floating/FloatingPositioner.tsx +8 -0
- package/packages/react/src/hooks/use-anchor-position.ts +88 -17
- package/packages/react/src/index.ts +3 -0
- package/packages/react/src/menu/index.tsx +37 -3
- package/packages/react/src/popover/index.tsx +7 -1
- package/packages/react/src/prompt/index.tsx +45 -0
- package/packages/react/src/range-slider/index.tsx +322 -0
- package/packages/react/src/slider/index.tsx +54 -14
- package/packages/react/src/thread/index.tsx +74 -3
- package/packages/react/src/tooltip/index.tsx +7 -1
- package/packages/react/src/utils/slot.tsx +47 -0
- package/packages/schemas/dist/index.d.ts +4 -0
- package/packages/schemas/dist/index.js +67 -1
- package/packages/schemas/json/button.json +6 -0
- package/packages/schemas/json/catalog.json +333 -0
- package/packages/schemas/json/fileUpload.json +168 -0
- package/packages/schemas/json/rangeSlider.json +157 -0
- package/packages/schemas/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/packages/schemas/prompts/cursorrules.md +3 -1
- package/packages/schemas/prompts/statecharts.md +1 -1
- package/packages/schemas/registry/catalog.json +18 -0
- package/packages/schemas/registry/components/button.json +6 -0
- package/packages/schemas/registry/components/fileUpload.json +168 -0
- package/packages/schemas/registry/components/rangeSlider.json +157 -0
- package/packages/schemas/registry/machines/fileUpload.json +182 -0
- package/packages/schemas/registry/machines/rangeSlider.json +201 -0
- package/packages/schemas/scripts/emit.ts +4 -0
- package/packages/schemas/src/components.ts +70 -0
- package/packages/schemas/src/prompts.ts +3 -1
- package/packages/schemas/tsconfig.json +1 -1
- package/packages/ui/dist/index.d.ts +100 -46
- package/packages/ui/dist/index.js +463 -341
- package/packages/ui/src/composer.tsx +37 -11
- package/packages/ui/src/file-upload.tsx +36 -0
- package/packages/ui/src/index.ts +2 -0
- package/packages/ui/src/prompt.tsx +3 -0
- package/packages/ui/src/range-slider.tsx +24 -0
- package/packages/ui/src/thread.tsx +40 -0
- package/scripts/link-scoped-packages.mjs +206 -0
- package/scripts/vite-aliases.mjs +6 -8
- package/packages/cli/dist/chunk-IW5NT2Z2.js +0 -103
|
@@ -5,23 +5,49 @@ import { cn } from './cn.js'
|
|
|
5
5
|
|
|
6
6
|
type RootProps = ComponentProps<typeof Headless.Root>
|
|
7
7
|
type InputProps = ComponentProps<typeof Headless.Input>
|
|
8
|
-
type
|
|
8
|
+
type ActionsProps = ComponentProps<typeof Headless.Actions>
|
|
9
|
+
type SendProps = ComponentProps<typeof Headless.Send>
|
|
10
|
+
type ToolbarProps = ComponentProps<typeof Headless.Toolbar>
|
|
11
|
+
type SuggestionsProps = ComponentProps<typeof Headless.Suggestions>
|
|
12
|
+
type SuggestionProps = ComponentProps<typeof Headless.Suggestion>
|
|
13
|
+
|
|
14
|
+
type Styles = ReturnType<typeof aeonComposer> & {
|
|
15
|
+
toolbar?: string
|
|
16
|
+
suggestions?: string
|
|
17
|
+
suggestion?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function styles(): Styles {
|
|
21
|
+
return aeonComposer() as Styles
|
|
22
|
+
}
|
|
9
23
|
|
|
10
24
|
export const Composer = {
|
|
11
25
|
Root: ({ className, ...props }: RootProps) => {
|
|
12
|
-
const
|
|
13
|
-
return <Headless.Root className={cn(
|
|
26
|
+
const s = styles()
|
|
27
|
+
return <Headless.Root className={cn(s.root, className)} {...props} />
|
|
14
28
|
},
|
|
15
29
|
Input: ({ className, ...props }: InputProps) => {
|
|
16
|
-
const
|
|
17
|
-
return <Headless.Input className={cn(
|
|
30
|
+
const s = styles()
|
|
31
|
+
return <Headless.Input className={cn(s.input, className)} {...props} />
|
|
32
|
+
},
|
|
33
|
+
Actions: ({ className, ...props }: ActionsProps) => {
|
|
34
|
+
const s = styles()
|
|
35
|
+
return <Headless.Actions className={cn(s.actions, className)} {...props} />
|
|
36
|
+
},
|
|
37
|
+
Send: ({ className, ...props }: SendProps) => {
|
|
38
|
+
const s = styles()
|
|
39
|
+
return <Headless.Send className={cn(s.send, className)} {...props} />
|
|
40
|
+
},
|
|
41
|
+
Toolbar: ({ className, ...props }: ToolbarProps) => {
|
|
42
|
+
const s = styles()
|
|
43
|
+
return <Headless.Toolbar className={cn(s.toolbar ?? s.actions, className)} {...props} />
|
|
18
44
|
},
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
return <Headless.
|
|
45
|
+
Suggestions: ({ className, ...props }: SuggestionsProps) => {
|
|
46
|
+
const s = styles()
|
|
47
|
+
return <Headless.Suggestions className={cn(s.suggestions, className)} {...props} />
|
|
22
48
|
},
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
return <Headless.
|
|
49
|
+
Suggestion: ({ className, ...props }: SuggestionProps) => {
|
|
50
|
+
const s = styles()
|
|
51
|
+
return <Headless.Suggestion className={cn(s.suggestion, className)} {...props} />
|
|
26
52
|
},
|
|
27
53
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { FileUpload as Headless } from '@aeon-ui/react'
|
|
2
|
+
import { aeonFileUpload } from '@aeon-ui/panda/styled-system/recipes'
|
|
3
|
+
import type { ComponentProps } from 'react'
|
|
4
|
+
import { cn } from './cn.js'
|
|
5
|
+
|
|
6
|
+
const recipe = aeonFileUpload()
|
|
7
|
+
|
|
8
|
+
export const FileUpload = {
|
|
9
|
+
Root: ({ className, ...props }: ComponentProps<typeof Headless.Root>) => (
|
|
10
|
+
<Headless.Root className={cn(recipe.root, className)} {...props} />
|
|
11
|
+
),
|
|
12
|
+
Label: ({ className, ...props }: ComponentProps<typeof Headless.Label>) => (
|
|
13
|
+
<Headless.Label className={cn(recipe.label, className)} {...props} />
|
|
14
|
+
),
|
|
15
|
+
Dropzone: ({ className, ...props }: ComponentProps<typeof Headless.Dropzone>) => (
|
|
16
|
+
<Headless.Dropzone className={cn(recipe.dropzone, className)} {...props} />
|
|
17
|
+
),
|
|
18
|
+
Input: ({ className, ...props }: ComponentProps<typeof Headless.Input>) => (
|
|
19
|
+
<Headless.Input className={cn(recipe.input, className)} {...props} />
|
|
20
|
+
),
|
|
21
|
+
Hint: ({ className, ...props }: ComponentProps<typeof Headless.Hint>) => (
|
|
22
|
+
<Headless.Hint className={cn(recipe.hint, className)} {...props} />
|
|
23
|
+
),
|
|
24
|
+
Preview: ({ className, ...props }: ComponentProps<typeof Headless.Preview>) => (
|
|
25
|
+
<Headless.Preview className={cn(recipe.preview, className)} {...props} />
|
|
26
|
+
),
|
|
27
|
+
ClearTrigger: ({ className, ...props }: ComponentProps<typeof Headless.ClearTrigger>) => (
|
|
28
|
+
<Headless.ClearTrigger className={cn(recipe.clearTrigger, className)} {...props} />
|
|
29
|
+
),
|
|
30
|
+
RejectionMessage: ({
|
|
31
|
+
className,
|
|
32
|
+
...props
|
|
33
|
+
}: ComponentProps<typeof Headless.RejectionMessage>) => (
|
|
34
|
+
<Headless.RejectionMessage className={cn(recipe.rejectionMessage, className)} {...props} />
|
|
35
|
+
),
|
|
36
|
+
}
|
package/packages/ui/src/index.ts
CHANGED
|
@@ -22,6 +22,8 @@ export { Separator } from './separator.js'
|
|
|
22
22
|
export { Progress } from './progress.js'
|
|
23
23
|
export { RadioGroup } from './radio-group.js'
|
|
24
24
|
export { Slider } from './slider.js'
|
|
25
|
+
export { RangeSlider } from './range-slider.js'
|
|
26
|
+
export { FileUpload } from './file-upload.js'
|
|
25
27
|
export { PinInput } from './pin-input.js'
|
|
26
28
|
export { Accordion } from './accordion.js'
|
|
27
29
|
export { Badge } from './badge.js'
|
|
@@ -19,6 +19,9 @@ export const Prompt = {
|
|
|
19
19
|
),
|
|
20
20
|
Meta: Headless.Meta,
|
|
21
21
|
Amount: Headless.Amount,
|
|
22
|
+
Verb: Headless.Verb,
|
|
23
|
+
Recipient: Headless.Recipient,
|
|
24
|
+
Effect: Headless.Effect,
|
|
22
25
|
Actions: Headless.Actions,
|
|
23
26
|
Primary: ({ className, ...props }: ComponentProps<typeof Headless.Primary>) => (
|
|
24
27
|
<Headless.Primary className={cn(className)} {...props} />
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RangeSlider as Headless } from '@aeon-ui/react'
|
|
2
|
+
import { aeonRangeSlider } from '@aeon-ui/panda/styled-system/recipes'
|
|
3
|
+
import type { ComponentProps } from 'react'
|
|
4
|
+
import { cn } from './cn.js'
|
|
5
|
+
|
|
6
|
+
const recipe = aeonRangeSlider()
|
|
7
|
+
|
|
8
|
+
export const RangeSlider = {
|
|
9
|
+
Root: ({ className, ...props }: ComponentProps<typeof Headless.Root>) => (
|
|
10
|
+
<Headless.Root className={cn(recipe.root, className)} {...props} />
|
|
11
|
+
),
|
|
12
|
+
Track: ({ className, ...props }: ComponentProps<typeof Headless.Track>) => (
|
|
13
|
+
<Headless.Track className={cn(recipe.track, className)} {...props} />
|
|
14
|
+
),
|
|
15
|
+
Range: ({ className, ...props }: ComponentProps<typeof Headless.Range>) => (
|
|
16
|
+
<Headless.Range className={cn(recipe.range, className)} {...props} />
|
|
17
|
+
),
|
|
18
|
+
Thumb: ({ className, ...props }: ComponentProps<typeof Headless.Thumb>) => (
|
|
19
|
+
<Headless.Thumb className={cn(recipe.thumb, className)} {...props} />
|
|
20
|
+
),
|
|
21
|
+
ValueText: ({ className, ...props }: ComponentProps<typeof Headless.ValueText>) => (
|
|
22
|
+
<Headless.ValueText className={cn(recipe.valueText, className)} {...props} />
|
|
23
|
+
),
|
|
24
|
+
}
|
|
@@ -6,6 +6,7 @@ import { cn } from './cn.js'
|
|
|
6
6
|
type RootProps = ComponentProps<typeof Headless.Root>
|
|
7
7
|
type ItemProps = ComponentProps<typeof Headless.Item>
|
|
8
8
|
type PartProps = ComponentProps<typeof Headless.Bubble>
|
|
9
|
+
type BindProps = ComponentProps<typeof Headless.Bind>
|
|
9
10
|
|
|
10
11
|
export const Thread = {
|
|
11
12
|
Root: ({ className, ...props }: RootProps) => {
|
|
@@ -32,4 +33,43 @@ export const Thread = {
|
|
|
32
33
|
const styles = aeonThread()
|
|
33
34
|
return <Headless.Day className={cn(styles.day, className)} {...props} />
|
|
34
35
|
},
|
|
36
|
+
Bind: ({ className, ...props }: BindProps) => {
|
|
37
|
+
const styles = aeonThread()
|
|
38
|
+
return (
|
|
39
|
+
<Headless.Bind className={cn((styles as { bind?: string }).bind, className)} {...props} />
|
|
40
|
+
)
|
|
41
|
+
},
|
|
42
|
+
Card: ({ className, ...props }: PartProps) => {
|
|
43
|
+
const styles = aeonThread()
|
|
44
|
+
return (
|
|
45
|
+
<Headless.Card className={cn((styles as { card?: string }).card ?? styles.bubble, className)} {...props} />
|
|
46
|
+
)
|
|
47
|
+
},
|
|
48
|
+
CardTitle: ({ className, ...props }: PartProps) => {
|
|
49
|
+
const styles = aeonThread()
|
|
50
|
+
return (
|
|
51
|
+
<Headless.CardTitle
|
|
52
|
+
className={cn((styles as { cardTitle?: string }).cardTitle, className)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
},
|
|
57
|
+
CardBody: ({ className, ...props }: PartProps) => {
|
|
58
|
+
const styles = aeonThread()
|
|
59
|
+
return (
|
|
60
|
+
<Headless.CardBody
|
|
61
|
+
className={cn((styles as { cardBody?: string }).cardBody, className)}
|
|
62
|
+
{...props}
|
|
63
|
+
/>
|
|
64
|
+
)
|
|
65
|
+
},
|
|
66
|
+
CardActions: ({ className, ...props }: PartProps) => {
|
|
67
|
+
const styles = aeonThread()
|
|
68
|
+
return (
|
|
69
|
+
<Headless.CardActions
|
|
70
|
+
className={cn((styles as { cardActions?: string }).cardActions, className)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
},
|
|
35
75
|
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Materialize `node_modules/@aeon-ui/*` → `aeon-ui-engine/packages/*`
|
|
3
|
+
* so Next.js / Vite / Node can resolve scoped imports without bundler aliases.
|
|
4
|
+
*
|
|
5
|
+
* Runs as `aeon-ui-engine` postinstall (uses INIT_CWD) or via `npx aeon-ui link`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* node ./scripts/link-scoped-packages.mjs
|
|
9
|
+
* node ./scripts/link-scoped-packages.mjs --cwd /path/to/app
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { createRequire } from 'node:module'
|
|
13
|
+
import {
|
|
14
|
+
existsSync,
|
|
15
|
+
lstatSync,
|
|
16
|
+
mkdirSync,
|
|
17
|
+
readlinkSync,
|
|
18
|
+
realpathSync,
|
|
19
|
+
rmSync,
|
|
20
|
+
symlinkSync,
|
|
21
|
+
} from 'node:fs'
|
|
22
|
+
import path from 'node:path'
|
|
23
|
+
import { fileURLToPath } from 'node:url'
|
|
24
|
+
|
|
25
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
26
|
+
|
|
27
|
+
/** Packages exposed under `@aeon-ui/<name>` for consumers. */
|
|
28
|
+
export const SCOPED_PACKAGES = [
|
|
29
|
+
'core',
|
|
30
|
+
'primitives',
|
|
31
|
+
'react',
|
|
32
|
+
'ui',
|
|
33
|
+
'panda',
|
|
34
|
+
'schemas',
|
|
35
|
+
'surface',
|
|
36
|
+
'vue',
|
|
37
|
+
'solid',
|
|
38
|
+
'svelte',
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Absolute path to the `aeon-ui-engine` package root (npm install or monorepo).
|
|
43
|
+
*/
|
|
44
|
+
export function resolveEngineRoot(opts = {}) {
|
|
45
|
+
if (opts.engineRoot) return path.resolve(opts.engineRoot)
|
|
46
|
+
if (process.env.AEON_UI_ROOT) return path.resolve(process.env.AEON_UI_ROOT)
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const require = createRequire(import.meta.url)
|
|
50
|
+
return path.dirname(require.resolve('aeon-ui-engine/package.json'))
|
|
51
|
+
} catch {
|
|
52
|
+
// Running from the monorepo checkout (this file lives in scripts/).
|
|
53
|
+
return path.resolve(scriptDir, '..')
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Consumer `node_modules` where `@aeon-ui/*` should appear.
|
|
59
|
+
* Prefer INIT_CWD (npm/pnpm/yarn set this to the install project).
|
|
60
|
+
*/
|
|
61
|
+
export function resolveTargetNodeModules(opts = {}) {
|
|
62
|
+
if (opts.nodeModules) return path.resolve(opts.nodeModules)
|
|
63
|
+
if (opts.cwd) return path.join(path.resolve(opts.cwd), 'node_modules')
|
|
64
|
+
|
|
65
|
+
// Prefer INIT_CWD (npm/pnpm/yarn set this to the install project).
|
|
66
|
+
// Skip when postinstall runs inside the engine package itself during a
|
|
67
|
+
// monorepo install — workspace already provides @aeon-ui/*.
|
|
68
|
+
if (process.env.INIT_CWD) {
|
|
69
|
+
const initCwd = path.resolve(process.env.INIT_CWD)
|
|
70
|
+
const engineRoot = resolveEngineRoot(opts)
|
|
71
|
+
if (initCwd === engineRoot) {
|
|
72
|
+
return path.join(engineRoot, 'node_modules')
|
|
73
|
+
}
|
|
74
|
+
return path.join(initCwd, 'node_modules')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const engineRoot = resolveEngineRoot(opts)
|
|
78
|
+
// Installed as dependency: …/node_modules/aeon-ui-engine
|
|
79
|
+
const parent = path.dirname(engineRoot)
|
|
80
|
+
if (path.basename(parent) === 'node_modules') {
|
|
81
|
+
return parent
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Monorepo / direct run: link into engine's own node_modules
|
|
85
|
+
return path.join(engineRoot, 'node_modules')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isSymlink(p) {
|
|
89
|
+
try {
|
|
90
|
+
return lstatSync(p).isSymbolicLink()
|
|
91
|
+
} catch {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function sameTarget(linkPath, absTarget) {
|
|
97
|
+
try {
|
|
98
|
+
const current = readlinkSync(linkPath)
|
|
99
|
+
const resolved = path.isAbsolute(current)
|
|
100
|
+
? path.normalize(current)
|
|
101
|
+
: path.resolve(path.dirname(linkPath), current)
|
|
102
|
+
if (resolved === absTarget) return true
|
|
103
|
+
} catch {
|
|
104
|
+
/* not a symlink or unreadable */
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
return realpathSync(linkPath) === realpathSync(absTarget)
|
|
108
|
+
} catch {
|
|
109
|
+
return false
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {{ cwd?: string, engineRoot?: string, nodeModules?: string, quiet?: boolean, force?: boolean }} [opts]
|
|
115
|
+
* @returns {{ linked: string[], skipped: string[], missing: string[], targetNodeModules: string, engineRoot: string }}
|
|
116
|
+
*/
|
|
117
|
+
export function linkScopedPackages(opts = {}) {
|
|
118
|
+
const quiet = opts.quiet ?? false
|
|
119
|
+
const force = opts.force ?? false
|
|
120
|
+
const engineRoot = resolveEngineRoot(opts)
|
|
121
|
+
const targetNodeModules = resolveTargetNodeModules({ ...opts, engineRoot })
|
|
122
|
+
const scopeDir = path.join(targetNodeModules, '@aeon-ui')
|
|
123
|
+
const packagesRoot = path.join(engineRoot, 'packages')
|
|
124
|
+
|
|
125
|
+
const linked = []
|
|
126
|
+
const skipped = []
|
|
127
|
+
const missing = []
|
|
128
|
+
|
|
129
|
+
mkdirSync(scopeDir, { recursive: true })
|
|
130
|
+
|
|
131
|
+
for (const name of SCOPED_PACKAGES) {
|
|
132
|
+
const absTarget = path.join(packagesRoot, name)
|
|
133
|
+
if (!existsSync(absTarget)) {
|
|
134
|
+
missing.push(name)
|
|
135
|
+
continue
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const linkPath = path.join(scopeDir, name)
|
|
139
|
+
|
|
140
|
+
if (existsSync(linkPath) || isSymlink(linkPath)) {
|
|
141
|
+
if (sameTarget(linkPath, absTarget)) {
|
|
142
|
+
skipped.push(name)
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
// Do not clobber pnpm/npm workspace links unless --force
|
|
146
|
+
if (!force) {
|
|
147
|
+
skipped.push(name)
|
|
148
|
+
continue
|
|
149
|
+
}
|
|
150
|
+
rmSync(linkPath, { recursive: true, force: true })
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const type = process.platform === 'win32' ? 'junction' : 'dir'
|
|
154
|
+
symlinkSync(absTarget, linkPath, type)
|
|
155
|
+
linked.push(name)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!quiet) {
|
|
159
|
+
const rel = (n) => `@aeon-ui/${n}`
|
|
160
|
+
for (const n of linked) console.log(` link ${rel(n)} → packages/${n}`)
|
|
161
|
+
for (const n of skipped) console.log(` skip ${rel(n)} (already present)`)
|
|
162
|
+
for (const n of missing) console.log(` miss ${rel(n)} (not in engine)`)
|
|
163
|
+
if (linked.length === 0 && skipped.length > 0) {
|
|
164
|
+
console.log(`aeon-ui link: ${skipped.length} package(s) already present in ${targetNodeModules}`)
|
|
165
|
+
} else if (linked.length > 0) {
|
|
166
|
+
console.log(`aeon-ui link: linked ${linked.length} package(s) into ${targetNodeModules}`)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { linked, skipped, missing, targetNodeModules, engineRoot }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function parseArgs(argv) {
|
|
174
|
+
const opts = {}
|
|
175
|
+
for (let i = 0; i < argv.length; i++) {
|
|
176
|
+
const a = argv[i]
|
|
177
|
+
if (a === '--cwd' && argv[i + 1]) {
|
|
178
|
+
opts.cwd = argv[++i]
|
|
179
|
+
} else if (a === '--engine-root' && argv[i + 1]) {
|
|
180
|
+
opts.engineRoot = argv[++i]
|
|
181
|
+
} else if (a === '--quiet' || a === '-q') {
|
|
182
|
+
opts.quiet = true
|
|
183
|
+
} else if (a === '--force' || a === '-f') {
|
|
184
|
+
opts.force = true
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return opts
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const isMain = (() => {
|
|
191
|
+
if (!process.argv[1]) return false
|
|
192
|
+
try {
|
|
193
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])
|
|
194
|
+
} catch {
|
|
195
|
+
return path.resolve(fileURLToPath(import.meta.url)) === path.resolve(process.argv[1])
|
|
196
|
+
}
|
|
197
|
+
})()
|
|
198
|
+
|
|
199
|
+
if (isMain) {
|
|
200
|
+
try {
|
|
201
|
+
linkScopedPackages(parseArgs(process.argv.slice(2)))
|
|
202
|
+
} catch (err) {
|
|
203
|
+
console.error('aeon-ui link failed:', err instanceof Error ? err.message : err)
|
|
204
|
+
process.exitCode = 1
|
|
205
|
+
}
|
|
206
|
+
}
|
package/scripts/vite-aliases.mjs
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Vite
|
|
2
|
+
* Vite helpers for apps that install `aeon-ui-engine` from npm.
|
|
3
|
+
*
|
|
4
|
+
* Scoped `@aeon-ui/*` imports resolve via postinstall linking
|
|
5
|
+
* (`aeon-ui-engine/link` / `npx aeon-ui link`). This plugin is optional —
|
|
6
|
+
* it still helps with optimizeDeps and monorepo `packagesRoot`.
|
|
3
7
|
*
|
|
4
8
|
* @example
|
|
5
|
-
* // vite.config.ts —
|
|
9
|
+
* // vite.config.ts — optional
|
|
6
10
|
* import { aeonUiVitePlugin } from 'aeon-ui-engine/vite'
|
|
7
11
|
*
|
|
8
12
|
* export default defineConfig({
|
|
9
13
|
* plugins: [react(), aeonUiVitePlugin()],
|
|
10
14
|
* })
|
|
11
|
-
*
|
|
12
|
-
* // tsconfig.json — spread the base config
|
|
13
|
-
* {
|
|
14
|
-
* "extends": "aeon-ui-engine/tsconfig",
|
|
15
|
-
* "compilerOptions": { "jsx": "react-jsx" }
|
|
16
|
-
* }
|
|
17
15
|
*/
|
|
18
16
|
import { createRequire } from 'node:module'
|
|
19
17
|
import path from 'node:path'
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
// src/init.ts
|
|
2
|
-
import { mkdirSync, writeFileSync, existsSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
import {
|
|
5
|
-
CURSOR_RULES_SNIPPET,
|
|
6
|
-
AEON_SYSTEM_PROMPT,
|
|
7
|
-
catalog,
|
|
8
|
-
componentSchemas
|
|
9
|
-
} from "@aeon-ui/schemas";
|
|
10
|
-
var STARTER = `/**
|
|
11
|
-
* Aeon UI starter \u2014 generated by \`npx aeon-ui init\`
|
|
12
|
-
* Headless: @aeon-ui/react \xB7 Styled: @aeon-ui/ui \xB7 Schemas: @aeon-ui/schemas
|
|
13
|
-
*/
|
|
14
|
-
import { Button, Field } from '@aeon-ui/ui'
|
|
15
|
-
|
|
16
|
-
export function AeonStarter() {
|
|
17
|
-
return (
|
|
18
|
-
<div>
|
|
19
|
-
<Field.Root>
|
|
20
|
-
<Field.Label>Email</Field.Label>
|
|
21
|
-
<Field.Control type="email" placeholder="you@example.com" />
|
|
22
|
-
<Field.Message />
|
|
23
|
-
</Field.Root>
|
|
24
|
-
<Button.Root status="idle">Continue</Button.Root>
|
|
25
|
-
</div>
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
`;
|
|
29
|
-
function initAeon(options = {}) {
|
|
30
|
-
const cwd = options.cwd ?? process.cwd();
|
|
31
|
-
const force = options.force ?? false;
|
|
32
|
-
const written = [];
|
|
33
|
-
const write = (rel, body) => {
|
|
34
|
-
const path = join(cwd, rel);
|
|
35
|
-
if (existsSync(path) && !force) {
|
|
36
|
-
written.push(`skip ${rel} (exists)`);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
mkdirSync(join(path, ".."), { recursive: true });
|
|
40
|
-
writeFileSync(path, body.endsWith("\n") ? body : `${body}
|
|
41
|
-
`);
|
|
42
|
-
written.push(`write ${rel}`);
|
|
43
|
-
};
|
|
44
|
-
write(".cursor/rules/aeon-ui.mdc", `---
|
|
45
|
-
description: Aeon UI \u2014 statechart-driven components for AI agents
|
|
46
|
-
globs: **/*.{tsx,ts,jsx,js}
|
|
47
|
-
alwaysApply: true
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
${CURSOR_RULES_SNIPPET.trim()}
|
|
51
|
-
`);
|
|
52
|
-
write("aeon.cursorrules.md", `${CURSOR_RULES_SNIPPET.trim()}
|
|
53
|
-
`);
|
|
54
|
-
write("aeon.system-prompt.md", `${AEON_SYSTEM_PROMPT.trim()}
|
|
55
|
-
`);
|
|
56
|
-
write(
|
|
57
|
-
"aeon.schemas/catalog.json",
|
|
58
|
-
`${JSON.stringify(
|
|
59
|
-
{
|
|
60
|
-
$id: catalog.$id,
|
|
61
|
-
title: catalog.title,
|
|
62
|
-
description: catalog.description,
|
|
63
|
-
version: catalog.version,
|
|
64
|
-
principle: catalog.principle,
|
|
65
|
-
attributes: catalog.attributes,
|
|
66
|
-
components: catalog.components,
|
|
67
|
-
note: "Full schemas live under aeon.schemas/components/*.json"
|
|
68
|
-
},
|
|
69
|
-
null,
|
|
70
|
-
2
|
|
71
|
-
)}
|
|
72
|
-
`
|
|
73
|
-
);
|
|
74
|
-
for (const [id, schema] of Object.entries(componentSchemas)) {
|
|
75
|
-
write(`aeon.schemas/components/${id}.json`, `${JSON.stringify(schema, null, 2)}
|
|
76
|
-
`);
|
|
77
|
-
}
|
|
78
|
-
write("src/components/AeonStarter.tsx", STARTER);
|
|
79
|
-
write(
|
|
80
|
-
"AEON_AI.md",
|
|
81
|
-
`# Aeon UI \u2014 AI agent setup
|
|
82
|
-
|
|
83
|
-
Generated by \`npx aeon-ui init\`.
|
|
84
|
-
|
|
85
|
-
## Next steps
|
|
86
|
-
|
|
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/\`
|
|
92
|
-
|
|
93
|
-
## Principle
|
|
94
|
-
|
|
95
|
-
**UI = f(state)** \u2014 never invent boolean combinations for states the schema does not list.
|
|
96
|
-
`
|
|
97
|
-
);
|
|
98
|
-
return { cwd, written };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export {
|
|
102
|
-
initAeon
|
|
103
|
-
};
|