aeon-ui-engine 1.3.5 → 1.4.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 (84) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/package.json +1 -1
  3. package/packages/cli/README.md +13 -1
  4. package/packages/cli/dist/bin.js +23 -5
  5. package/packages/cli/dist/chunk-5L47SLNV.js +247 -0
  6. package/packages/cli/dist/index.d.ts +17 -1
  7. package/packages/cli/dist/index.js +5 -3
  8. package/packages/cli/src/bin.ts +21 -3
  9. package/packages/cli/src/index.ts +1 -0
  10. package/packages/cli/src/migrate.ts +170 -0
  11. package/packages/core/dist/index.d.ts +35 -7
  12. package/packages/core/dist/index.js +102 -2
  13. package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  14. package/packages/core/src/anatomy.ts +39 -3
  15. package/packages/core/src/identicon.test.ts +39 -0
  16. package/packages/core/src/identicon.ts +79 -0
  17. package/packages/core/src/index.ts +1 -0
  18. package/packages/panda/panda.config.ts +4 -0
  19. package/packages/panda/src/recipes/composer.ts +58 -4
  20. package/packages/panda/src/recipes/file-upload.ts +100 -0
  21. package/packages/panda/src/recipes/index.ts +2 -0
  22. package/packages/panda/src/recipes/range-slider.ts +59 -0
  23. package/packages/panda/src/recipes/thread.ts +77 -2
  24. package/packages/panda/styled-system/recipes/aeon-composer.d.ts +1 -1
  25. package/packages/panda/styled-system/recipes/aeon-composer.mjs +12 -0
  26. package/packages/panda/styled-system/recipes/aeon-file-upload.d.ts +31 -0
  27. package/packages/panda/styled-system/recipes/aeon-file-upload.mjs +61 -0
  28. package/packages/panda/styled-system/recipes/aeon-range-slider.d.ts +31 -0
  29. package/packages/panda/styled-system/recipes/aeon-range-slider.mjs +49 -0
  30. package/packages/panda/styled-system/recipes/aeon-thread.d.ts +1 -1
  31. package/packages/panda/styled-system/recipes/aeon-thread.mjs +20 -0
  32. package/packages/panda/styled-system/recipes/index.d.ts +2 -0
  33. package/packages/panda/styled-system/recipes/index.mjs +2 -0
  34. package/packages/panda/styled-system/styles.css +292 -0
  35. package/packages/primitives/dist/chunk-CV4UXXZF.js +44 -0
  36. package/packages/primitives/dist/chunk-IZKQSCYU.js +164 -0
  37. package/packages/primitives/dist/index.d.ts +2 -0
  38. package/packages/primitives/dist/index.js +22 -6
  39. package/packages/primitives/dist/machines/file-upload.d.ts +39 -0
  40. package/packages/primitives/dist/machines/file-upload.js +8 -0
  41. package/packages/primitives/dist/machines/range-slider.d.ts +129 -0
  42. package/packages/primitives/dist/machines/range-slider.js +12 -0
  43. package/packages/primitives/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  44. package/packages/primitives/src/__tests__/file-upload.test.ts +25 -0
  45. package/packages/primitives/src/__tests__/range-slider.test.ts +39 -0
  46. package/packages/primitives/src/index.ts +2 -0
  47. package/packages/primitives/src/machines/file-upload.ts +55 -0
  48. package/packages/primitives/src/machines/range-slider.ts +209 -0
  49. package/packages/react/dist/index.d.ts +117 -25
  50. package/packages/react/dist/index.js +1731 -1006
  51. package/packages/react/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  52. package/packages/react/src/button/index.tsx +50 -15
  53. package/packages/react/src/composer/index.tsx +58 -3
  54. package/packages/react/src/file-upload/index.tsx +322 -0
  55. package/packages/react/src/index.ts +3 -0
  56. package/packages/react/src/prompt/index.tsx +45 -0
  57. package/packages/react/src/range-slider/index.tsx +322 -0
  58. package/packages/react/src/thread/index.tsx +74 -3
  59. package/packages/react/src/utils/slot.tsx +47 -0
  60. package/packages/schemas/dist/index.d.ts +4 -0
  61. package/packages/schemas/dist/index.js +67 -1
  62. package/packages/schemas/json/button.json +6 -0
  63. package/packages/schemas/json/catalog.json +333 -0
  64. package/packages/schemas/json/fileUpload.json +168 -0
  65. package/packages/schemas/json/rangeSlider.json +157 -0
  66. package/packages/schemas/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  67. package/packages/schemas/registry/catalog.json +18 -0
  68. package/packages/schemas/registry/components/button.json +6 -0
  69. package/packages/schemas/registry/components/fileUpload.json +168 -0
  70. package/packages/schemas/registry/components/rangeSlider.json +157 -0
  71. package/packages/schemas/registry/machines/fileUpload.json +182 -0
  72. package/packages/schemas/registry/machines/rangeSlider.json +201 -0
  73. package/packages/schemas/scripts/emit.ts +4 -0
  74. package/packages/schemas/src/components.ts +70 -0
  75. package/packages/schemas/tsconfig.json +1 -1
  76. package/packages/ui/dist/index.d.ts +100 -46
  77. package/packages/ui/dist/index.js +463 -341
  78. package/packages/ui/src/composer.tsx +37 -11
  79. package/packages/ui/src/file-upload.tsx +36 -0
  80. package/packages/ui/src/index.ts +2 -0
  81. package/packages/ui/src/prompt.tsx +3 -0
  82. package/packages/ui/src/range-slider.tsx +24 -0
  83. package/packages/ui/src/thread.tsx +40 -0
  84. package/packages/cli/dist/chunk-IW5NT2Z2.js +0 -103
@@ -0,0 +1,322 @@
1
+ import { partAttrs, partOnlyAttrs, rangeSliderAnatomy } from '@aeon-ui/core'
2
+ import {
3
+ rangeSliderMachine,
4
+ snapRangeSliderValue,
5
+ } from '@aeon-ui/primitives'
6
+ import {
7
+ createContext,
8
+ forwardRef,
9
+ useCallback,
10
+ useContext,
11
+ useEffect,
12
+ useMemo,
13
+ useRef,
14
+ type HTMLAttributes,
15
+ type KeyboardEvent,
16
+ type PointerEvent,
17
+ type ReactNode,
18
+ } from 'react'
19
+ import { useAeonMachine } from '../hooks/use-aeon-machine.js'
20
+ import { mergeProps } from '../utils/merge-props.js'
21
+
22
+ type ThumbIndex = 0 | 1
23
+
24
+ interface RangeSliderContextValue {
25
+ values: [number, number]
26
+ min: number
27
+ max: number
28
+ step: number
29
+ disabled: boolean
30
+ dragging: boolean
31
+ activeThumb: ThumbIndex | null
32
+ send: ReturnType<typeof useAeonMachine<typeof rangeSliderMachine>>[1]
33
+ setThumbFromClientX: (thumb: ThumbIndex, clientX: number) => void
34
+ trackRef: React.RefObject<HTMLDivElement | null>
35
+ }
36
+
37
+ const RangeSliderCtx = createContext<RangeSliderContextValue | null>(null)
38
+
39
+ function useRangeSliderCtx() {
40
+ const ctx = useContext(RangeSliderCtx)
41
+ if (!ctx) throw new Error('RangeSlider parts must be used within RangeSlider.Root')
42
+ return ctx
43
+ }
44
+
45
+ function percent(value: number, min: number, max: number) {
46
+ if (max <= min) return 0
47
+ return ((value - min) / (max - min)) * 100
48
+ }
49
+
50
+ export interface RangeSliderRootProps {
51
+ value?: [number, number]
52
+ defaultValue?: [number, number]
53
+ min?: number
54
+ max?: number
55
+ step?: number
56
+ minStepsBetween?: number
57
+ disabled?: boolean
58
+ onValueChange?: (value: [number, number]) => void
59
+ children?: ReactNode
60
+ className?: string
61
+ }
62
+
63
+ const Root = forwardRef<HTMLDivElement, RangeSliderRootProps>(function RangeSliderRoot(
64
+ {
65
+ value,
66
+ defaultValue,
67
+ min = 0,
68
+ max = 100,
69
+ step = 1,
70
+ minStepsBetween = 0,
71
+ disabled = false,
72
+ onValueChange,
73
+ children,
74
+ className,
75
+ ...rest
76
+ },
77
+ ref,
78
+ ) {
79
+ const trackRef = useRef<HTMLDivElement>(null)
80
+ const [snapshot, send] = useAeonMachine(rangeSliderMachine, {
81
+ input: {
82
+ values: defaultValue ?? [min, max],
83
+ min,
84
+ max,
85
+ step,
86
+ minStepsBetween,
87
+ disabled,
88
+ },
89
+ })
90
+
91
+ const resolvedValues = value ?? snapshot.context.values
92
+ const dragging = snapshot.matches({ interaction: 'dragging' })
93
+ const activeThumb = snapshot.context.activeThumb
94
+
95
+ useEffect(() => {
96
+ if (value !== undefined) send({ type: 'SET_VALUES', values: value })
97
+ }, [value, send])
98
+
99
+ useEffect(() => {
100
+ send({ type: 'SET_BOUNDS', min, max, step, minStepsBetween })
101
+ }, [min, max, step, minStepsBetween, send])
102
+
103
+ useEffect(() => {
104
+ send({ type: 'SET_DISABLED', disabled })
105
+ }, [disabled, send])
106
+
107
+ const setThumbFromClientX = useCallback(
108
+ (thumb: ThumbIndex, clientX: number) => {
109
+ const track = trackRef.current
110
+ if (!track || disabled) return
111
+ const rect = track.getBoundingClientRect()
112
+ if (rect.width <= 0) return
113
+ const ratio = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width))
114
+ const next = snapRangeSliderValue(min + ratio * (max - min), min, max, step)
115
+ send({ type: 'POINTER_MOVE', value: next })
116
+ if (value === undefined) {
117
+ // Machine updates async via snapshot; notify after POINTER_MOVE by reading next tick
118
+ // Consumers in uncontrolled mode get onValueChange from effect below.
119
+ }
120
+ void thumb
121
+ },
122
+ [disabled, min, max, step, send, value],
123
+ )
124
+
125
+ const lastNotified = useRef<string>('')
126
+ useEffect(() => {
127
+ const key = resolvedValues.join(',')
128
+ if (key === lastNotified.current) return
129
+ lastNotified.current = key
130
+ onValueChange?.(resolvedValues)
131
+ }, [resolvedValues, onValueChange])
132
+
133
+ useEffect(() => {
134
+ if (!dragging) return
135
+ const onMove = (e: globalThis.PointerEvent) => {
136
+ if (activeThumb == null) return
137
+ setThumbFromClientX(activeThumb, e.clientX)
138
+ }
139
+ const onUp = () => send({ type: 'POINTER_UP' })
140
+ window.addEventListener('pointermove', onMove)
141
+ window.addEventListener('pointerup', onUp)
142
+ return () => {
143
+ window.removeEventListener('pointermove', onMove)
144
+ window.removeEventListener('pointerup', onUp)
145
+ }
146
+ }, [dragging, activeThumb, setThumbFromClientX, send])
147
+
148
+ const ctx = useMemo(
149
+ () => ({
150
+ values: resolvedValues,
151
+ min,
152
+ max,
153
+ step,
154
+ disabled,
155
+ dragging,
156
+ activeThumb,
157
+ send,
158
+ setThumbFromClientX,
159
+ trackRef,
160
+ }),
161
+ [
162
+ resolvedValues,
163
+ min,
164
+ max,
165
+ step,
166
+ disabled,
167
+ dragging,
168
+ activeThumb,
169
+ send,
170
+ setThumbFromClientX,
171
+ ],
172
+ )
173
+
174
+ return (
175
+ <RangeSliderCtx.Provider value={ctx}>
176
+ <div
177
+ ref={ref}
178
+ className={className}
179
+ {...mergeProps(
180
+ partAttrs(rangeSliderAnatomy.scope, rangeSliderAnatomy.root, {
181
+ state: dragging ? 'dragging' : 'idle',
182
+ disabled,
183
+ }),
184
+ rest as HTMLAttributes<HTMLDivElement>,
185
+ )}
186
+ >
187
+ {children}
188
+ </div>
189
+ </RangeSliderCtx.Provider>
190
+ )
191
+ })
192
+
193
+ const Track = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(function RangeSliderTrack(
194
+ { onPointerDown, ...rest },
195
+ ref,
196
+ ) {
197
+ const { disabled, values, send, setThumbFromClientX, trackRef, min, max } = useRangeSliderCtx()
198
+
199
+ return (
200
+ <div
201
+ ref={(node) => {
202
+ trackRef.current = node
203
+ if (typeof ref === 'function') ref(node)
204
+ else if (ref) ref.current = node
205
+ }}
206
+ {...mergeProps(
207
+ partOnlyAttrs(rangeSliderAnatomy.track),
208
+ {
209
+ onPointerDown: (e: PointerEvent<HTMLDivElement>) => {
210
+ onPointerDown?.(e)
211
+ if (disabled || e.button !== 0) return
212
+ e.currentTarget.setPointerCapture(e.pointerId)
213
+ const rect = e.currentTarget.getBoundingClientRect()
214
+ const ratio = Math.min(1, Math.max(0, (e.clientX - rect.left) / rect.width))
215
+ const pointerValue = min + ratio * (max - min)
216
+ const dist0 = Math.abs(pointerValue - values[0])
217
+ const dist1 = Math.abs(pointerValue - values[1])
218
+ const thumb: ThumbIndex = dist0 <= dist1 ? 0 : 1
219
+ send({ type: 'POINTER_DOWN', thumb })
220
+ setThumbFromClientX(thumb, e.clientX)
221
+ },
222
+ },
223
+ rest,
224
+ )}
225
+ />
226
+ )
227
+ })
228
+
229
+ const Range = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(function RangeSliderRange(
230
+ { style, ...rest },
231
+ ref,
232
+ ) {
233
+ const { values, min, max } = useRangeSliderCtx()
234
+ const left = percent(values[0], min, max)
235
+ const right = percent(values[1], min, max)
236
+ return (
237
+ <div
238
+ ref={ref}
239
+ aria-hidden
240
+ {...mergeProps(partOnlyAttrs(rangeSliderAnatomy.range), rest, {
241
+ style: { ...style, left: `${left}%`, width: `${Math.max(0, right - left)}%` },
242
+ })}
243
+ />
244
+ )
245
+ })
246
+
247
+ export interface RangeSliderThumbProps extends HTMLAttributes<HTMLSpanElement> {
248
+ index: ThumbIndex
249
+ }
250
+
251
+ const Thumb = forwardRef<HTMLSpanElement, RangeSliderThumbProps>(function RangeSliderThumb(
252
+ { index, onKeyDown, onPointerDown, style, ...rest },
253
+ ref,
254
+ ) {
255
+ const { values, min, max, step, disabled, dragging, activeThumb, send, setThumbFromClientX } =
256
+ useRangeSliderCtx()
257
+ const value = values[index]
258
+ const isActive = dragging && activeThumb === index
259
+
260
+ return (
261
+ <span
262
+ ref={ref}
263
+ role="slider"
264
+ tabIndex={disabled ? -1 : 0}
265
+ aria-valuemin={min}
266
+ aria-valuemax={max}
267
+ aria-valuenow={value}
268
+ aria-disabled={disabled || undefined}
269
+ data-aeon-index={index}
270
+ {...mergeProps(
271
+ partOnlyAttrs(rangeSliderAnatomy.thumb, {
272
+ state: isActive ? 'dragging' : disabled ? 'disabled' : 'idle',
273
+ }),
274
+ {
275
+ style: { ...style, left: `${percent(value, min, max)}%` },
276
+ onPointerDown: (e: PointerEvent<HTMLSpanElement>) => {
277
+ onPointerDown?.(e)
278
+ if (disabled || e.button !== 0) return
279
+ e.stopPropagation()
280
+ e.currentTarget.setPointerCapture(e.pointerId)
281
+ send({ type: 'POINTER_DOWN', thumb: index })
282
+ setThumbFromClientX(index, e.clientX)
283
+ },
284
+ onKeyDown: (e: KeyboardEvent<HTMLSpanElement>) => {
285
+ onKeyDown?.(e)
286
+ if (disabled || e.defaultPrevented) return
287
+ if (e.key === 'ArrowRight' || e.key === 'ArrowUp') {
288
+ e.preventDefault()
289
+ send({ type: 'STEP', thumb: index, delta: step })
290
+ }
291
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') {
292
+ e.preventDefault()
293
+ send({ type: 'STEP', thumb: index, delta: -step })
294
+ }
295
+ if (e.key === 'Home') {
296
+ e.preventDefault()
297
+ send({ type: 'HOME', thumb: index })
298
+ }
299
+ if (e.key === 'End') {
300
+ e.preventDefault()
301
+ send({ type: 'END', thumb: index })
302
+ }
303
+ },
304
+ },
305
+ rest,
306
+ )}
307
+ />
308
+ )
309
+ })
310
+
311
+ const ValueText = forwardRef<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>>(
312
+ function RangeSliderValueText(props, ref) {
313
+ const { values } = useRangeSliderCtx()
314
+ return (
315
+ <span ref={ref} {...mergeProps(partOnlyAttrs(rangeSliderAnatomy.valueText), props)}>
316
+ {props.children ?? `${values[0]} – ${values[1]}`}
317
+ </span>
318
+ )
319
+ },
320
+ )
321
+
322
+ export const RangeSlider = { Root, Track, Range, Thumb, ValueText }
@@ -1,15 +1,26 @@
1
1
  import { partAttrs, scopeAttrs, threadAnatomy } from '@aeon-ui/core'
2
- import { forwardRef, type HTMLAttributes, type ReactNode } from 'react'
2
+ import {
3
+ forwardRef,
4
+ type ButtonHTMLAttributes,
5
+ type HTMLAttributes,
6
+ type ReactNode,
7
+ } from 'react'
3
8
  import { mergeProps } from '../utils/merge-props.js'
4
9
 
10
+ /** Base alignment / delivery states. */
5
11
  export type ThreadMessageState = 'mine' | 'theirs' | 'pending' | 'failed'
6
12
 
13
+ /** Structured in-thread faces (space-combinable with mine/theirs). */
14
+ export type ThreadItemFace = 'command-result' | 'payment-card' | 'request-card'
15
+
7
16
  export interface ThreadRootProps extends HTMLAttributes<HTMLElement> {
8
17
  children?: ReactNode
9
18
  }
10
19
 
11
20
  /**
12
- * Thread — message stream. Item `state` projects mine | theirs | pending | failed.
21
+ * Thread — message stream.
22
+ * Item `state` projects mine | theirs | pending | failed, optionally plus
23
+ * command-result | payment-card | request-card (e.g. `mine payment-card`).
13
24
  */
14
25
  const Root = forwardRef<HTMLElement, ThreadRootProps>(function ThreadRoot(
15
26
  { children, ...rest },
@@ -40,7 +51,7 @@ const List = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(function
40
51
  })
41
52
 
42
53
  export interface ThreadItemProps extends HTMLAttributes<HTMLDivElement> {
43
- state?: ThreadMessageState | `${ThreadMessageState} ${string}`
54
+ state?: ThreadMessageState | ThreadItemFace | `${ThreadMessageState} ${ThreadItemFace}` | string
44
55
  }
45
56
 
46
57
  const Item = forwardRef<HTMLDivElement, ThreadItemProps>(function ThreadItem(
@@ -85,6 +96,61 @@ const Day = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(function
85
96
  )
86
97
  })
87
98
 
99
+ /** Reply / message-binding control — must be touch-reachable (BRC-218 §4.10). */
100
+ const Bind = forwardRef<HTMLButtonElement, ButtonHTMLAttributes<HTMLButtonElement>>(
101
+ function ThreadBind(props, ref) {
102
+ return (
103
+ <button
104
+ ref={ref}
105
+ type="button"
106
+ {...mergeProps(partAttrs(threadAnatomy.scope, threadAnatomy.bind), props)}
107
+ />
108
+ )
109
+ },
110
+ )
111
+
112
+ const Card = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(function ThreadCard(
113
+ props,
114
+ ref,
115
+ ) {
116
+ return (
117
+ <div ref={ref} {...mergeProps(partAttrs(threadAnatomy.scope, threadAnatomy.card), props)} />
118
+ )
119
+ })
120
+
121
+ const CardTitle = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
122
+ function ThreadCardTitle(props, ref) {
123
+ return (
124
+ <div
125
+ ref={ref}
126
+ {...mergeProps(partAttrs(threadAnatomy.scope, threadAnatomy.cardTitle), props)}
127
+ />
128
+ )
129
+ },
130
+ )
131
+
132
+ const CardBody = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
133
+ function ThreadCardBody(props, ref) {
134
+ return (
135
+ <div
136
+ ref={ref}
137
+ {...mergeProps(partAttrs(threadAnatomy.scope, threadAnatomy.cardBody), props)}
138
+ />
139
+ )
140
+ },
141
+ )
142
+
143
+ const CardActions = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
144
+ function ThreadCardActions(props, ref) {
145
+ return (
146
+ <div
147
+ ref={ref}
148
+ {...mergeProps(partAttrs(threadAnatomy.scope, threadAnatomy.cardActions), props)}
149
+ />
150
+ )
151
+ },
152
+ )
153
+
88
154
  export const Thread = {
89
155
  Root,
90
156
  List,
@@ -92,4 +158,9 @@ export const Thread = {
92
158
  Bubble,
93
159
  Meta,
94
160
  Day,
161
+ Bind,
162
+ Card,
163
+ CardTitle,
164
+ CardBody,
165
+ CardActions,
95
166
  }
@@ -0,0 +1,47 @@
1
+ import {
2
+ Children,
3
+ cloneElement,
4
+ forwardRef,
5
+ isValidElement,
6
+ type HTMLAttributes,
7
+ type ReactElement,
8
+ type ReactNode,
9
+ type Ref,
10
+ } from 'react'
11
+ import { mergeProps } from './merge-props.js'
12
+
13
+ function mergeRefs<T>(...refs: Array<Ref<T> | undefined>) {
14
+ return (node: T) => {
15
+ for (const ref of refs) {
16
+ if (!ref) continue
17
+ if (typeof ref === 'function') ref(node)
18
+ else (ref as { current: T }).current = node
19
+ }
20
+ }
21
+ }
22
+
23
+ export type SlotProps = HTMLAttributes<HTMLElement> & {
24
+ children?: ReactNode
25
+ }
26
+
27
+ /**
28
+ * Radix-style Slot — merges Aeon attrs onto a single child element
29
+ * (e.g. Next.js Link) instead of rendering a wrapper.
30
+ */
31
+ export const Slot = forwardRef<HTMLElement, SlotProps>(function Slot(
32
+ { children, ...props },
33
+ forwardedRef,
34
+ ) {
35
+ const child = Children.only(children)
36
+ if (!isValidElement(child)) {
37
+ throw new Error('Slot expects a single React element child')
38
+ }
39
+
40
+ const childElement = child as ReactElement<HTMLAttributes<HTMLElement> & { ref?: Ref<HTMLElement> }>
41
+ const childProps = childElement.props
42
+
43
+ return cloneElement(childElement, {
44
+ ...mergeProps(props, childProps),
45
+ ref: mergeRefs(forwardedRef, childProps.ref),
46
+ } as HTMLAttributes<HTMLElement>)
47
+ })
@@ -37,6 +37,8 @@ declare const componentSchemas: {
37
37
  readonly progress: JsonSchema;
38
38
  readonly radioGroup: JsonSchema;
39
39
  readonly slider: JsonSchema;
40
+ readonly rangeSlider: JsonSchema;
41
+ readonly fileUpload: JsonSchema;
40
42
  readonly pinInput: JsonSchema;
41
43
  readonly avatar: JsonSchema;
42
44
  readonly content: JsonSchema;
@@ -87,6 +89,8 @@ declare const catalog: {
87
89
  readonly progress: JsonSchema;
88
90
  readonly radioGroup: JsonSchema;
89
91
  readonly slider: JsonSchema;
92
+ readonly rangeSlider: JsonSchema;
93
+ readonly fileUpload: JsonSchema;
90
94
  readonly pinInput: JsonSchema;
91
95
  readonly avatar: JsonSchema;
92
96
  readonly content: JsonSchema;
@@ -89,11 +89,16 @@ var componentSchemas = {
89
89
  },
90
90
  variant: { type: "string", enum: ["solid", "outline", "ghost", "link"] },
91
91
  size: { type: "string", enum: ["sm", "md", "lg"] },
92
- disabled: { type: "boolean" }
92
+ disabled: { type: "boolean" },
93
+ asChild: {
94
+ type: "boolean",
95
+ description: "Merge button attrs onto a single child element (Link, a)."
96
+ }
93
97
  },
94
98
  slots: { Root: "button.root", Group: "button.group", Label: "button.label", Icon: "button.icon" },
95
99
  examples: [
96
100
  '<Button.Root status="pending" aria-busy>Saving\u2026</Button.Root>',
101
+ '<Button.Root asChild variant="solid"><a href="/upload">Upload</a></Button.Root>',
97
102
  "const [snap, send] = useAeonMachine(buttonLifecycleMachine)\n<Button.Root status={snap.value as ButtonStatus} onClick={() => send({ type: 'PRESS' })} />"
98
103
  ]
99
104
  }),
@@ -378,6 +383,67 @@ var componentSchemas = {
378
383
  ValueText: "slider.valueText"
379
384
  }
380
385
  }),
386
+ rangeSlider: componentSchema({
387
+ id: "https://aeon-ui.com/schemas/rangeSlider.json",
388
+ title: "RangeSlider",
389
+ description: "Two-thumb range via rangeSliderMachine \u2014 idle | dragging with values pair and minStepsBetween gap.",
390
+ scope: "rangeSlider",
391
+ parts: ["root", "track", "range", "thumb", "valueText"],
392
+ states: ["idle", "dragging"],
393
+ machine: "rangeSliderMachine",
394
+ runtime: "useAeonMachine",
395
+ events: [
396
+ "SET_VALUES",
397
+ "SET_BOUNDS",
398
+ "SET_DISABLED",
399
+ "POINTER_DOWN",
400
+ "POINTER_MOVE",
401
+ "POINTER_UP",
402
+ "STEP",
403
+ "HOME",
404
+ "END"
405
+ ],
406
+ slots: {
407
+ Root: "rangeSlider.root",
408
+ Track: "rangeSlider.track",
409
+ Range: "rangeSlider.range",
410
+ Thumb: "rangeSlider.thumb",
411
+ ValueText: "rangeSlider.valueText"
412
+ },
413
+ examples: [
414
+ "<RangeSlider.Root defaultValue={[20, 80]} minStepsBetween={1}>\n <RangeSlider.Track /><RangeSlider.Range />\n <RangeSlider.Thumb index={0} /><RangeSlider.Thumb index={1} />\n</RangeSlider.Root>"
415
+ ]
416
+ }),
417
+ fileUpload: componentSchema({
418
+ id: "https://aeon-ui.com/schemas/fileUpload.json",
419
+ title: "FileUpload",
420
+ description: "Drag-drop surface via fileUploadMachine \u2014 idle | dragging | rejected. File bytes stay in React callbacks.",
421
+ scope: "fileUpload",
422
+ parts: [
423
+ "root",
424
+ "dropzone",
425
+ "input",
426
+ "label",
427
+ "hint",
428
+ "preview",
429
+ "clearTrigger",
430
+ "rejectionMessage"
431
+ ],
432
+ states: ["idle", "dragging", "rejected"],
433
+ machine: "fileUploadMachine",
434
+ runtime: "useAeonMachine",
435
+ events: ["DRAG_OVER", "DRAG_LEAVE", "DROP_ACCEPT", "DROP_REJECT", "RESET"],
436
+ slots: {
437
+ Root: "fileUpload.root",
438
+ Dropzone: "fileUpload.dropzone",
439
+ Input: "fileUpload.input",
440
+ Label: "fileUpload.label",
441
+ Hint: "fileUpload.hint",
442
+ Preview: "fileUpload.preview",
443
+ ClearTrigger: "fileUpload.clearTrigger",
444
+ RejectionMessage: "fileUpload.rejectionMessage"
445
+ }
446
+ }),
381
447
  pinInput: componentSchema({
382
448
  id: "https://aeon-ui.com/schemas/pinInput.json",
383
449
  title: "PinInput",
@@ -79,6 +79,10 @@
79
79
  },
80
80
  "disabled": {
81
81
  "type": "boolean"
82
+ },
83
+ "asChild": {
84
+ "type": "boolean",
85
+ "description": "Merge button attrs onto a single child element (Link, a)."
82
86
  }
83
87
  }
84
88
  },
@@ -134,6 +138,7 @@
134
138
  },
135
139
  "default": [
136
140
  "<Button.Root status=\"pending\" aria-busy>Saving…</Button.Root>",
141
+ "<Button.Root asChild variant=\"solid\"><a href=\"/upload\">Upload</a></Button.Root>",
137
142
  "const [snap, send] = useAeonMachine(buttonLifecycleMachine)\n<Button.Root status={snap.value as ButtonStatus} onClick={() => send({ type: 'PRESS' })} />"
138
143
  ]
139
144
  }
@@ -164,6 +169,7 @@
164
169
  },
165
170
  "examples": [
166
171
  "<Button.Root status=\"pending\" aria-busy>Saving…</Button.Root>",
172
+ "<Button.Root asChild variant=\"solid\"><a href=\"/upload\">Upload</a></Button.Root>",
167
173
  "const [snap, send] = useAeonMachine(buttonLifecycleMachine)\n<Button.Root status={snap.value as ButtonStatus} onClick={() => send({ type: 'PRESS' })} />"
168
174
  ]
169
175
  }