create-omg 0.4.41 → 0.4.43

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 (81) hide show
  1. package/README.md +16 -6
  2. package/dist/index.mjs +71 -12
  3. package/dist/{template → templates/react-ts}/AGENTS.md +22 -0
  4. package/dist/templates/react-ts/_gitignore +24 -0
  5. package/dist/{template → templates/react-ts}/bun.lock +105 -58
  6. package/dist/templates/react-ts/components.json +25 -0
  7. package/dist/templates/react-ts/eslint.config.js +23 -0
  8. package/dist/templates/react-ts/functions/.gitkeep +0 -0
  9. package/dist/{template → templates/react-ts}/package.json +5 -0
  10. package/dist/templates/react-ts/public/favicon.svg +1 -0
  11. package/dist/templates/react-ts/public/icons/apple-touch-icon.png +0 -0
  12. package/dist/templates/react-ts/public/icons/pwa-192x192.png +0 -0
  13. package/dist/templates/react-ts/public/icons/pwa-512x512-maskable.png +0 -0
  14. package/dist/templates/react-ts/public/icons/pwa-512x512.png +0 -0
  15. package/dist/templates/react-ts/public/icons.svg +24 -0
  16. package/dist/templates/react-ts/schema.ts +5 -0
  17. package/dist/templates/react-ts/server/db.ts +103 -0
  18. package/dist/templates/react-ts/src/App.tsx +128 -0
  19. package/dist/templates/react-ts/src/components/ui/animated-number.tsx +102 -0
  20. package/dist/templates/react-ts/src/components/ui/animated-text.tsx +109 -0
  21. package/dist/templates/react-ts/src/components/ui/bottom-nav.tsx +131 -0
  22. package/dist/templates/react-ts/src/components/ui/button.tsx +84 -0
  23. package/dist/templates/react-ts/src/components/ui/card.tsx +70 -0
  24. package/dist/templates/react-ts/src/components/ui/otp-input.tsx +179 -0
  25. package/dist/templates/react-ts/src/components/ui/scroll-affordance.tsx +106 -0
  26. package/dist/templates/react-ts/src/components/ui/sound.ts +182 -0
  27. package/dist/templates/react-ts/src/components/ui/transitions.tsx +419 -0
  28. package/dist/templates/react-ts/src/components/ui/vibes-ui-styles.ts +388 -0
  29. package/dist/templates/react-ts/src/db.drizzle.ts +5 -0
  30. package/dist/templates/react-ts/src/index.css +168 -0
  31. package/dist/templates/react-ts/src/lib/utils.ts +6 -0
  32. package/dist/templates/react-ts/src/main.tsx +22 -0
  33. package/dist/templates/react-ts/src/routeTree.gen.ts +59 -0
  34. package/dist/templates/react-ts/src/routes/__root.tsx +7 -0
  35. package/dist/templates/react-ts/src/routes/index.tsx +9 -0
  36. package/dist/templates/react-ts/tsconfig.app.json +30 -0
  37. package/dist/templates/react-ts/tsconfig.json +10 -0
  38. package/dist/templates/react-ts/tsconfig.node.json +24 -0
  39. package/dist/{template → templates/react-ts}/vite.config.ts +9 -1
  40. package/dist/templates/react-ts-start/AGENTS.md +75 -0
  41. package/dist/templates/react-ts-start/README.md +58 -0
  42. package/dist/templates/react-ts-start/bun.lock +1688 -0
  43. package/dist/templates/react-ts-start/package.json +58 -0
  44. package/dist/templates/react-ts-start/src/routeTree.gen.ts +68 -0
  45. package/dist/templates/react-ts-start/src/router.tsx +18 -0
  46. package/dist/templates/react-ts-start/src/routes/__root.tsx +40 -0
  47. package/dist/templates/react-ts-start/src/routes/index.tsx +7 -0
  48. package/dist/templates/react-ts-start/vite.config.ts +29 -0
  49. package/package.json +3 -3
  50. package/dist/template/src/main.tsx +0 -10
  51. /package/dist/{template → templates/react-ts}/README.md +0 -0
  52. /package/dist/{template → templates/react-ts}/index.html +0 -0
  53. /package/dist/{template → templates/react-ts-start}/_gitignore +0 -0
  54. /package/dist/{template → templates/react-ts-start}/components.json +0 -0
  55. /package/dist/{template → templates/react-ts-start}/eslint.config.js +0 -0
  56. /package/dist/{template → templates/react-ts-start}/functions/.gitkeep +0 -0
  57. /package/dist/{template → templates/react-ts-start}/public/favicon.svg +0 -0
  58. /package/dist/{template → templates/react-ts-start}/public/icons/apple-touch-icon.png +0 -0
  59. /package/dist/{template → templates/react-ts-start}/public/icons/pwa-192x192.png +0 -0
  60. /package/dist/{template → templates/react-ts-start}/public/icons/pwa-512x512-maskable.png +0 -0
  61. /package/dist/{template → templates/react-ts-start}/public/icons/pwa-512x512.png +0 -0
  62. /package/dist/{template → templates/react-ts-start}/public/icons.svg +0 -0
  63. /package/dist/{template → templates/react-ts-start}/schema.ts +0 -0
  64. /package/dist/{template → templates/react-ts-start}/server/db.ts +0 -0
  65. /package/dist/{template → templates/react-ts-start}/src/App.tsx +0 -0
  66. /package/dist/{template → templates/react-ts-start}/src/components/ui/animated-number.tsx +0 -0
  67. /package/dist/{template → templates/react-ts-start}/src/components/ui/animated-text.tsx +0 -0
  68. /package/dist/{template → templates/react-ts-start}/src/components/ui/bottom-nav.tsx +0 -0
  69. /package/dist/{template → templates/react-ts-start}/src/components/ui/button.tsx +0 -0
  70. /package/dist/{template → templates/react-ts-start}/src/components/ui/card.tsx +0 -0
  71. /package/dist/{template → templates/react-ts-start}/src/components/ui/otp-input.tsx +0 -0
  72. /package/dist/{template → templates/react-ts-start}/src/components/ui/scroll-affordance.tsx +0 -0
  73. /package/dist/{template → templates/react-ts-start}/src/components/ui/sound.ts +0 -0
  74. /package/dist/{template → templates/react-ts-start}/src/components/ui/transitions.tsx +0 -0
  75. /package/dist/{template → templates/react-ts-start}/src/components/ui/vibes-ui-styles.ts +0 -0
  76. /package/dist/{template → templates/react-ts-start}/src/db.drizzle.ts +0 -0
  77. /package/dist/{template → templates/react-ts-start}/src/index.css +0 -0
  78. /package/dist/{template → templates/react-ts-start}/src/lib/utils.ts +0 -0
  79. /package/dist/{template → templates/react-ts-start}/tsconfig.app.json +0 -0
  80. /package/dist/{template → templates/react-ts-start}/tsconfig.json +0 -0
  81. /package/dist/{template → templates/react-ts-start}/tsconfig.node.json +0 -0
@@ -0,0 +1,419 @@
1
+ // Transitions — dependency-free, CSS-only micro-interactions.
2
+ //
3
+ // Vendored from transitions.dev (Jakub Antalík), editable source. The motion
4
+ // lives in the injected `.vui-t-*` stylesheet (see vibes-ui-styles.ts); these
5
+ // React wrappers just own the awkward bits — flipping `.is-open` AFTER first
6
+ // paint so the open transition actually runs, keeping a closing element mounted
7
+ // until its exit finishes, and the three-phase reflow dance for swapping text.
8
+ // Self-contained: no Tailwind utility classes, no motion library, no runtime
9
+ // dependency beyond React. Only transform/opacity/filter animate, and every
10
+ // family honors prefers-reduced-motion.
11
+ //
12
+ // Reach for these when the DELIGHT rule "motion should REDUCE perceived travel"
13
+ // applies: a dialog, a menu, a drawer, a wizard step, or any element/label that
14
+ // swaps. Don't pull in Framer Motion for these — and don't wrap them in
15
+ // <AnimatePresence>; the two systems fight over the same element.
16
+
17
+ import {
18
+ useEffect,
19
+ useRef,
20
+ useState,
21
+ type CSSProperties,
22
+ type ReactNode,
23
+ } from "react"
24
+ import { flushSync } from "react-dom"
25
+ import { ensureStyles } from "@/components/ui/vibes-ui-styles"
26
+
27
+ function cx(...parts: Array<string | false | null | undefined>): string {
28
+ return parts.filter(Boolean).join(" ")
29
+ }
30
+
31
+ /**
32
+ * Mount/show lifecycle for an open/close element. Returns `mounted` (keep it in
33
+ * the DOM through the exit transition) and `show` (drives `.is-open`). On open
34
+ * it waits two animation frames before flipping `show`, so the element first
35
+ * paints in its closed state and the open transition has somewhere to travel
36
+ * from; on close it holds the element mounted for `closeMs` while `.is-closing`
37
+ * plays, then unmounts it.
38
+ */
39
+ function usePresence(open: boolean, closeMs: number) {
40
+ const [mounted, setMounted] = useState(open)
41
+ const [show, setShow] = useState(open)
42
+
43
+ useEffect(() => {
44
+ if (open) {
45
+ setMounted(true)
46
+ let raf2 = 0
47
+ const raf1 = requestAnimationFrame(() => {
48
+ raf2 = requestAnimationFrame(() => setShow(true))
49
+ })
50
+ return () => {
51
+ cancelAnimationFrame(raf1)
52
+ cancelAnimationFrame(raf2)
53
+ }
54
+ }
55
+ setShow(false)
56
+ const timer = setTimeout(() => setMounted(false), closeMs)
57
+ return () => clearTimeout(timer)
58
+ }, [open, closeMs])
59
+
60
+ return { mounted, show }
61
+ }
62
+
63
+ // ── Modal ──────────────────────────────────────────────────────────────────
64
+
65
+ export type ModalProps = {
66
+ /** Whether the dialog is open. */
67
+ open: boolean
68
+ /** Called when the backdrop is clicked (wire it to your close handler). */
69
+ onClose?: () => void
70
+ children: ReactNode
71
+ /** Render a fading scrim behind the dialog. Default `true`. */
72
+ backdrop?: boolean
73
+ className?: string
74
+ style?: CSSProperties
75
+ }
76
+
77
+ /**
78
+ * Centered dialog that scales + fades in, and (faster) back out. Stays mounted
79
+ * through the close transition, then unmounts itself. Renders an optional
80
+ * click-to-close backdrop. Style the dialog surface via `className` — bring your
81
+ * own `Card` or padding/background.
82
+ */
83
+ export function Modal({
84
+ open,
85
+ onClose,
86
+ children,
87
+ backdrop = true,
88
+ className = "",
89
+ style,
90
+ }: ModalProps) {
91
+ useEffect(ensureStyles, [])
92
+ const { mounted, show } = usePresence(open, 150)
93
+ if (!mounted) return null
94
+ const state = show ? "is-open" : "is-closing"
95
+
96
+ return (
97
+ <>
98
+ {backdrop ? (
99
+ <div
100
+ className={cx("vui", "vui-t-backdrop", state)}
101
+ onClick={onClose}
102
+ aria-hidden="true"
103
+ />
104
+ ) : null}
105
+ <div
106
+ className={cx("vui", "vui-t-modal", state, className)}
107
+ style={style}
108
+ role="dialog"
109
+ aria-modal="true"
110
+ >
111
+ {children}
112
+ </div>
113
+ </>
114
+ )
115
+ }
116
+
117
+ // ── Menu ─────────────────────────────────────────────────────────────────────
118
+
119
+ export type MenuOrigin =
120
+ | "top-left"
121
+ | "top-center"
122
+ | "top-right"
123
+ | "bottom-left"
124
+ | "bottom-center"
125
+ | "bottom-right"
126
+
127
+ export type MenuProps = {
128
+ /** Whether the menu is open. */
129
+ open: boolean
130
+ children: ReactNode
131
+ /** Corner the menu scales out from — match it to the trigger. Default `top-left`. */
132
+ origin?: MenuOrigin
133
+ className?: string
134
+ style?: CSSProperties
135
+ }
136
+
137
+ /**
138
+ * Popover/dropdown that scales from its anchor corner. Position the element
139
+ * yourself (the caller owns layout — e.g. an absolutely-positioned wrapper under
140
+ * the trigger); this only animates open/close and unmounts when closed.
141
+ */
142
+ export function Menu({
143
+ open,
144
+ children,
145
+ origin = "top-left",
146
+ className = "",
147
+ style,
148
+ }: MenuProps) {
149
+ useEffect(ensureStyles, [])
150
+ const { mounted, show } = usePresence(open, 150)
151
+ if (!mounted) return null
152
+
153
+ return (
154
+ <div
155
+ className={cx("vui", "vui-t-menu", show ? "is-open" : "is-closing", className)}
156
+ data-origin={origin}
157
+ style={style}
158
+ role="menu"
159
+ >
160
+ {children}
161
+ </div>
162
+ )
163
+ }
164
+
165
+ // ── Panel ────────────────────────────────────────────────────────────────────
166
+
167
+ export type PanelProps = {
168
+ /** Whether the panel is revealed. */
169
+ open: boolean
170
+ children: ReactNode
171
+ className?: string
172
+ style?: CSSProperties
173
+ }
174
+
175
+ /**
176
+ * Inline reveal — a drawer, sheet, or expander that slides up, fades, and
177
+ * un-blurs into place. Stays mounted in both states (so it can clip inside an
178
+ * `overflow: hidden` container); set `--vui-t-panel-y` to ~half the panel
179
+ * height via `style` for a fuller travel.
180
+ */
181
+ export function Panel({ open, children, className = "", style }: PanelProps) {
182
+ useEffect(ensureStyles, [])
183
+ return (
184
+ <div
185
+ className={cx("vui", "vui-t-panel", className)}
186
+ data-open={open ? "true" : "false"}
187
+ style={style}
188
+ >
189
+ {children}
190
+ </div>
191
+ )
192
+ }
193
+
194
+ // ── Expand ───────────────────────────────────────────────────────────────────
195
+
196
+ export type ExpandProps = {
197
+ /** Whether the region is expanded. */
198
+ open: boolean
199
+ children: ReactNode
200
+ className?: string
201
+ style?: CSSProperties
202
+ }
203
+
204
+ /**
205
+ * Height-auto reveal that animates smoothly WITHOUT measuring the DOM — an
206
+ * accordion row, a disclosure, a "show more" section, a collapsible filter
207
+ * panel. Built on the `grid-template-rows: 0fr → 1fr` trick, so it interpolates
208
+ * to the content's natural height (no fixed `max-height` guesswork, no JS
209
+ * measurement, no clipping when the content grows). Stays mounted in both
210
+ * states and clips its overflow while collapsed. Reach for this instead of
211
+ * toggling `hidden`/`display:none` (which snaps) or hand-rolling a `max-height`
212
+ * hack. For an overlay drawer/sheet that floats above content use `Panel`; this
213
+ * one pushes surrounding layout as it opens.
214
+ */
215
+ export function Expand({ open, children, className = "", style }: ExpandProps) {
216
+ useEffect(ensureStyles, [])
217
+ return (
218
+ <div
219
+ className={cx("vui", "vui-t-expand", className)}
220
+ data-open={open ? "true" : "false"}
221
+ style={style}
222
+ >
223
+ <div className="vui-t-expand-inner">{children}</div>
224
+ </div>
225
+ )
226
+ }
227
+
228
+ // ── animateView (View Transitions) ─────────────────────────────────────────────
229
+
230
+ /**
231
+ * Run a state update inside a native View Transition so the browser cross-fades
232
+ * / morphs between the before and after DOM — list reorders, a filter that adds
233
+ * or removes rows, a route/tab swap, an item moving between two lists. Give the
234
+ * elements that should morph a stable `viewTransitionName` (via `style`) and the
235
+ * browser tweens their position/size for you.
236
+ *
237
+ * Uses `document.startViewTransition` with `flushSync` so React commits the
238
+ * update synchronously inside the transition callback. Degrades gracefully: if
239
+ * the API is unsupported OR the user prefers reduced motion, the update is
240
+ * applied immediately with no animation. Safe to call from any event handler.
241
+ *
242
+ * animateView(() => setItems(reorder(items)))
243
+ */
244
+ export function animateView(update: () => void): void {
245
+ const doc = document as Document & {
246
+ startViewTransition?: (cb: () => void | Promise<void>) => unknown
247
+ }
248
+ const reducedMotion =
249
+ typeof window !== "undefined" &&
250
+ typeof window.matchMedia === "function" &&
251
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches
252
+
253
+ if (typeof doc.startViewTransition !== "function" || reducedMotion) {
254
+ update()
255
+ return
256
+ }
257
+ doc.startViewTransition(() => flushSync(update))
258
+ }
259
+
260
+ // ── PageSlide ────────────────────────────────────────────────────────────────
261
+
262
+ export type PageSlideProps = {
263
+ /** Active page index (0-based). */
264
+ page: number
265
+ /** One node per page, in order. Pages before `page` exit left, after exit right. */
266
+ pages: ReactNode[]
267
+ className?: string
268
+ style?: CSSProperties
269
+ }
270
+
271
+ /**
272
+ * Side-by-side wizard / tab steps. Only the active page is interactive; the
273
+ * others sit stacked, blurred, and shifted toward the side they came from, so
274
+ * forward/back reads as horizontal travel. Keeps every page mounted.
275
+ */
276
+ export function PageSlide({ page, pages, className = "", style }: PageSlideProps) {
277
+ useEffect(ensureStyles, [])
278
+ return (
279
+ <div className={cx("vui", "vui-t-pages", className)} style={style}>
280
+ {pages.map((node, i) => (
281
+ <div
282
+ key={i}
283
+ className={cx("vui-t-page", i === page && "is-active")}
284
+ style={
285
+ {
286
+ "--vui-t-page-x":
287
+ i < page
288
+ ? "calc(var(--vui-t-page-dist, 8px) * -1)"
289
+ : "var(--vui-t-page-dist, 8px)",
290
+ } as CSSProperties
291
+ }
292
+ aria-hidden={i === page ? undefined : true}
293
+ >
294
+ {node}
295
+ </div>
296
+ ))}
297
+ </div>
298
+ )
299
+ }
300
+
301
+ // ── IconSwap ─────────────────────────────────────────────────────────────────
302
+
303
+ export type IconSwapProps = {
304
+ /** Which icon is showing. */
305
+ active: "a" | "b"
306
+ /** First icon (shown when `active === "a"`). */
307
+ a: ReactNode
308
+ /** Second icon (shown when `active === "b"`). */
309
+ b: ReactNode
310
+ className?: string
311
+ style?: CSSProperties
312
+ }
313
+
314
+ /**
315
+ * Cross-fade + scale between two stacked icons (play/pause, moon/sun,
316
+ * copy/check). Both icons render at once, sharing a grid cell; the inactive one
317
+ * fades, blurs, and shrinks away.
318
+ */
319
+ export function IconSwap({ active, a, b, className = "", style }: IconSwapProps) {
320
+ useEffect(ensureStyles, [])
321
+ return (
322
+ <span
323
+ className={cx("vui", "vui-t-icon-swap", className)}
324
+ data-state={active}
325
+ style={style}
326
+ >
327
+ <span className="vui-t-icon" data-icon="a" aria-hidden={active !== "a"}>
328
+ {a}
329
+ </span>
330
+ <span className="vui-t-icon" data-icon="b" aria-hidden={active !== "b"}>
331
+ {b}
332
+ </span>
333
+ </span>
334
+ )
335
+ }
336
+
337
+ // ── TextSwap ─────────────────────────────────────────────────────────────────
338
+
339
+ export type TextSwapProps = {
340
+ /** The current text. Changing it animates the old out (up) and the new in (from below). */
341
+ children: string
342
+ className?: string
343
+ style?: CSSProperties
344
+ }
345
+
346
+ /**
347
+ * Swap a short label/status string in place: the old text lifts up + blurs +
348
+ * fades, then the new text rises from below. Handy for state words ("Saving…" →
349
+ * "Saved", "Copy" → "Copied"). For runtime numbers reach for `AnimatedNumber`,
350
+ * and for rolling slot-text reach for `AnimatedText`; this is for arbitrary
351
+ * label replacement.
352
+ */
353
+ export function TextSwap({ children, className = "", style }: TextSwapProps) {
354
+ useEffect(ensureStyles, [])
355
+ const ref = useRef<HTMLSpanElement | null>(null)
356
+ const [text, setText] = useState(children)
357
+ const prev = useRef(children)
358
+
359
+ useEffect(() => {
360
+ const el = ref.current
361
+ if (!el || children === prev.current) return
362
+ const next = children
363
+ prev.current = next
364
+
365
+ el.classList.add("is-exit")
366
+ const dur =
367
+ parseFloat(getComputedStyle(el).getPropertyValue("--vui-t-text-dur")) || 200
368
+ const timer = setTimeout(() => {
369
+ setText(next)
370
+ el.classList.remove("is-exit")
371
+ el.classList.add("is-enter-start")
372
+ // Force reflow so the jump-below state paints before we release it.
373
+ void el.offsetWidth
374
+ el.classList.remove("is-enter-start")
375
+ }, dur)
376
+ return () => clearTimeout(timer)
377
+ }, [children])
378
+
379
+ return (
380
+ <span ref={ref} className={cx("vui", "vui-t-text", className)} style={style}>
381
+ {text}
382
+ </span>
383
+ )
384
+ }
385
+
386
+ // ── NotificationBadge ────────────────────────────────────────────────────────
387
+
388
+ export type NotificationBadgeProps = {
389
+ /** Whether the badge is shown. Toggling pops it in / out. */
390
+ show: boolean
391
+ /** Badge contents (a count, a dot). */
392
+ children?: ReactNode
393
+ className?: string
394
+ style?: CSSProperties
395
+ }
396
+
397
+ /**
398
+ * A count/dot that pops in with a slight overshoot and shrinks-blurs out. Place
399
+ * it inside a `position: relative` trigger (e.g. a bell button); it pins to the
400
+ * top-right corner and never intercepts pointer events.
401
+ */
402
+ export function NotificationBadge({
403
+ show,
404
+ children,
405
+ className = "",
406
+ style,
407
+ }: NotificationBadgeProps) {
408
+ useEffect(ensureStyles, [])
409
+ return (
410
+ <span
411
+ className={cx("vui", "vui-t-badge", className)}
412
+ data-open={show ? "true" : "false"}
413
+ style={style}
414
+ aria-hidden={!show}
415
+ >
416
+ <span className="vui-t-badge-dot">{children}</span>
417
+ </span>
418
+ )
419
+ }