@betterstart/cli 0.1.3 → 0.1.4

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 (234) hide show
  1. package/README.md +133 -0
  2. package/dist/cli.d.ts +1 -9
  3. package/dist/cli.js +13484 -367
  4. package/dist/cli.js.map +1 -1
  5. package/dist/index.d.ts +24 -266
  6. package/dist/index.js +4 -11378
  7. package/dist/index.js.map +1 -1
  8. package/package.json +29 -42
  9. package/templates/schema.json +959 -0
  10. package/templates/tiptap/hooks/use-composed-ref.ts +43 -0
  11. package/templates/tiptap/hooks/use-cursor-visibility.ts +68 -0
  12. package/templates/tiptap/hooks/use-element-rect.ts +166 -0
  13. package/templates/tiptap/hooks/use-is-breakpoint.ts +32 -0
  14. package/templates/tiptap/hooks/use-menu-navigation.ts +182 -0
  15. package/templates/tiptap/hooks/use-scrolling.ts +64 -0
  16. package/templates/tiptap/hooks/use-throttled-callback.ts +146 -0
  17. package/templates/tiptap/hooks/use-tiptap-editor.ts +46 -0
  18. package/templates/tiptap/hooks/use-unmount.ts +21 -0
  19. package/templates/tiptap/hooks/use-window-size.ts +87 -0
  20. package/templates/tiptap/lib/tiptap-utils.ts +587 -0
  21. package/templates/tiptap/styles/_keyframe-animations.scss +91 -0
  22. package/templates/tiptap/styles/_variables.scss +296 -0
  23. package/templates/tiptap/tiptap-extension/node-background-extension.ts +138 -0
  24. package/templates/tiptap/tiptap-icons/align-center-icon.tsx +38 -0
  25. package/templates/tiptap/tiptap-icons/align-justify-icon.tsx +38 -0
  26. package/templates/tiptap/tiptap-icons/align-left-icon.tsx +38 -0
  27. package/templates/tiptap/tiptap-icons/align-right-icon.tsx +38 -0
  28. package/templates/tiptap/tiptap-icons/arrow-left-icon.tsx +24 -0
  29. package/templates/tiptap/tiptap-icons/ban-icon.tsx +26 -0
  30. package/templates/tiptap/tiptap-icons/blockquote-icon.tsx +44 -0
  31. package/templates/tiptap/tiptap-icons/bold-icon.tsx +26 -0
  32. package/templates/tiptap/tiptap-icons/chevron-down-icon.tsx +26 -0
  33. package/templates/tiptap/tiptap-icons/close-icon.tsx +24 -0
  34. package/templates/tiptap/tiptap-icons/code-block-icon.tsx +38 -0
  35. package/templates/tiptap/tiptap-icons/code2-icon.tsx +32 -0
  36. package/templates/tiptap/tiptap-icons/corner-down-left-icon.tsx +26 -0
  37. package/templates/tiptap/tiptap-icons/external-link-icon.tsx +28 -0
  38. package/templates/tiptap/tiptap-icons/heading-five-icon.tsx +28 -0
  39. package/templates/tiptap/tiptap-icons/heading-four-icon.tsx +28 -0
  40. package/templates/tiptap/tiptap-icons/heading-icon.tsx +24 -0
  41. package/templates/tiptap/tiptap-icons/heading-one-icon.tsx +28 -0
  42. package/templates/tiptap/tiptap-icons/heading-six-icon.tsx +30 -0
  43. package/templates/tiptap/tiptap-icons/heading-three-icon.tsx +36 -0
  44. package/templates/tiptap/tiptap-icons/heading-two-icon.tsx +28 -0
  45. package/templates/tiptap/tiptap-icons/highlighter-icon.tsx +26 -0
  46. package/templates/tiptap/tiptap-icons/image-plus-icon.tsx +26 -0
  47. package/templates/tiptap/tiptap-icons/italic-icon.tsx +24 -0
  48. package/templates/tiptap/tiptap-icons/link-icon.tsx +28 -0
  49. package/templates/tiptap/tiptap-icons/list-icon.tsx +56 -0
  50. package/templates/tiptap/tiptap-icons/list-ordered-icon.tsx +56 -0
  51. package/templates/tiptap/tiptap-icons/list-todo-icon.tsx +50 -0
  52. package/templates/tiptap/tiptap-icons/moon-star-icon.tsx +30 -0
  53. package/templates/tiptap/tiptap-icons/redo2-icon.tsx +26 -0
  54. package/templates/tiptap/tiptap-icons/strike-icon.tsx +28 -0
  55. package/templates/tiptap/tiptap-icons/subscript-icon.tsx +38 -0
  56. package/templates/tiptap/tiptap-icons/sun-icon.tsx +58 -0
  57. package/templates/tiptap/tiptap-icons/superscript-icon.tsx +38 -0
  58. package/templates/tiptap/tiptap-icons/trash-icon.tsx +26 -0
  59. package/templates/tiptap/tiptap-icons/underline-icon.tsx +26 -0
  60. package/templates/tiptap/tiptap-icons/undo2-icon.tsx +26 -0
  61. package/templates/tiptap/tiptap-node/blockquote-node/blockquote-node.scss +37 -0
  62. package/templates/tiptap/tiptap-node/code-block-node/code-block-node.scss +54 -0
  63. package/templates/tiptap/tiptap-node/heading-node/heading-node.scss +45 -0
  64. package/templates/tiptap/tiptap-node/horizontal-rule-node/horizontal-rule-node-extension.ts +10 -0
  65. package/templates/tiptap/tiptap-node/horizontal-rule-node/horizontal-rule-node.scss +25 -0
  66. package/templates/tiptap/tiptap-node/image-node/image-node.scss +35 -0
  67. package/templates/tiptap/tiptap-node/image-upload-node/image-upload-node-extension.ts +154 -0
  68. package/templates/tiptap/tiptap-node/image-upload-node/image-upload-node.scss +249 -0
  69. package/templates/tiptap/tiptap-node/image-upload-node/image-upload-node.tsx +522 -0
  70. package/templates/tiptap/tiptap-node/image-upload-node/index.tsx +1 -0
  71. package/templates/tiptap/tiptap-node/list-node/list-node.scss +208 -0
  72. package/templates/tiptap/tiptap-node/paragraph-node/paragraph-node.scss +273 -0
  73. package/templates/tiptap/tiptap-ui/blockquote-button/blockquote-button.tsx +104 -0
  74. package/templates/tiptap/tiptap-ui/blockquote-button/index.tsx +2 -0
  75. package/templates/tiptap/tiptap-ui/blockquote-button/use-blockquote.ts +252 -0
  76. package/templates/tiptap/tiptap-ui/code-block-button/code-block-button.tsx +106 -0
  77. package/templates/tiptap/tiptap-ui/code-block-button/index.tsx +2 -0
  78. package/templates/tiptap/tiptap-ui/code-block-button/use-code-block.ts +261 -0
  79. package/templates/tiptap/tiptap-ui/color-highlight-button/color-highlight-button.scss +49 -0
  80. package/templates/tiptap/tiptap-ui/color-highlight-button/color-highlight-button.tsx +153 -0
  81. package/templates/tiptap/tiptap-ui/color-highlight-button/index.tsx +2 -0
  82. package/templates/tiptap/tiptap-ui/color-highlight-button/use-color-highlight.ts +345 -0
  83. package/templates/tiptap/tiptap-ui/color-highlight-popover/color-highlight-popover.tsx +207 -0
  84. package/templates/tiptap/tiptap-ui/color-highlight-popover/index.tsx +1 -0
  85. package/templates/tiptap/tiptap-ui/heading-button/heading-button.tsx +107 -0
  86. package/templates/tiptap/tiptap-ui/heading-button/index.tsx +2 -0
  87. package/templates/tiptap/tiptap-ui/heading-button/use-heading.ts +314 -0
  88. package/templates/tiptap/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu.tsx +131 -0
  89. package/templates/tiptap/tiptap-ui/heading-dropdown-menu/index.tsx +2 -0
  90. package/templates/tiptap/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts +130 -0
  91. package/templates/tiptap/tiptap-ui/image-upload-button/image-upload-button.tsx +114 -0
  92. package/templates/tiptap/tiptap-ui/image-upload-button/index.tsx +2 -0
  93. package/templates/tiptap/tiptap-ui/image-upload-button/use-image-upload.ts +192 -0
  94. package/templates/tiptap/tiptap-ui/link-popover/index.tsx +2 -0
  95. package/templates/tiptap/tiptap-ui/link-popover/link-popover.tsx +285 -0
  96. package/templates/tiptap/tiptap-ui/link-popover/use-link-popover.ts +286 -0
  97. package/templates/tiptap/tiptap-ui/list-button/index.tsx +2 -0
  98. package/templates/tiptap/tiptap-ui/list-button/list-button.tsx +108 -0
  99. package/templates/tiptap/tiptap-ui/list-button/use-list.ts +329 -0
  100. package/templates/tiptap/tiptap-ui/list-dropdown-menu/index.tsx +1 -0
  101. package/templates/tiptap/tiptap-ui/list-dropdown-menu/list-dropdown-menu.tsx +123 -0
  102. package/templates/tiptap/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts +203 -0
  103. package/templates/tiptap/tiptap-ui/mark-button/index.tsx +2 -0
  104. package/templates/tiptap/tiptap-ui/mark-button/mark-button.tsx +107 -0
  105. package/templates/tiptap/tiptap-ui/mark-button/use-mark.ts +206 -0
  106. package/templates/tiptap/tiptap-ui/text-align-button/index.tsx +2 -0
  107. package/templates/tiptap/tiptap-ui/text-align-button/text-align-button.tsx +118 -0
  108. package/templates/tiptap/tiptap-ui/text-align-button/use-text-align.ts +212 -0
  109. package/templates/tiptap/tiptap-ui/undo-redo-button/index.tsx +2 -0
  110. package/templates/tiptap/tiptap-ui/undo-redo-button/undo-redo-button.tsx +105 -0
  111. package/templates/tiptap/tiptap-ui/undo-redo-button/use-undo-redo.ts +173 -0
  112. package/templates/tiptap/tiptap-ui-primitive/badge/badge-colors.scss +395 -0
  113. package/templates/tiptap/tiptap-ui-primitive/badge/badge-group.scss +16 -0
  114. package/templates/tiptap/tiptap-ui-primitive/badge/badge.scss +99 -0
  115. package/templates/tiptap/tiptap-ui-primitive/badge/badge.tsx +46 -0
  116. package/templates/tiptap/tiptap-ui-primitive/badge/index.tsx +1 -0
  117. package/templates/tiptap/tiptap-ui-primitive/button/button-colors.scss +429 -0
  118. package/templates/tiptap/tiptap-ui-primitive/button/button-group.scss +22 -0
  119. package/templates/tiptap/tiptap-ui-primitive/button/button.scss +314 -0
  120. package/templates/tiptap/tiptap-ui-primitive/button/button.tsx +102 -0
  121. package/templates/tiptap/tiptap-ui-primitive/button/index.tsx +1 -0
  122. package/templates/tiptap/tiptap-ui-primitive/card/card.scss +77 -0
  123. package/templates/tiptap/tiptap-ui-primitive/card/card.tsx +59 -0
  124. package/templates/tiptap/tiptap-ui-primitive/card/index.tsx +1 -0
  125. package/templates/tiptap/tiptap-ui-primitive/dropdown-menu/dropdown-menu.scss +63 -0
  126. package/templates/tiptap/tiptap-ui-primitive/dropdown-menu/dropdown-menu.tsx +95 -0
  127. package/templates/tiptap/tiptap-ui-primitive/dropdown-menu/index.tsx +1 -0
  128. package/templates/tiptap/tiptap-ui-primitive/input/index.tsx +1 -0
  129. package/templates/tiptap/tiptap-ui-primitive/input/input.scss +45 -0
  130. package/templates/tiptap/tiptap-ui-primitive/input/input.tsx +18 -0
  131. package/templates/tiptap/tiptap-ui-primitive/popover/index.tsx +1 -0
  132. package/templates/tiptap/tiptap-ui-primitive/popover/popover.scss +63 -0
  133. package/templates/tiptap/tiptap-ui-primitive/popover/popover.tsx +33 -0
  134. package/templates/tiptap/tiptap-ui-primitive/separator/index.tsx +1 -0
  135. package/templates/tiptap/tiptap-ui-primitive/separator/separator.scss +23 -0
  136. package/templates/tiptap/tiptap-ui-primitive/separator/separator.tsx +33 -0
  137. package/templates/tiptap/tiptap-ui-primitive/spacer/index.tsx +1 -0
  138. package/templates/tiptap/tiptap-ui-primitive/spacer/spacer.tsx +21 -0
  139. package/templates/tiptap/tiptap-ui-primitive/toolbar/index.tsx +1 -0
  140. package/templates/tiptap/tiptap-ui-primitive/toolbar/toolbar.scss +98 -0
  141. package/templates/tiptap/tiptap-ui-primitive/toolbar/toolbar.tsx +113 -0
  142. package/templates/tiptap/tiptap-ui-primitive/tooltip/index.tsx +1 -0
  143. package/templates/tiptap/tiptap-ui-primitive/tooltip/tooltip.scss +43 -0
  144. package/templates/tiptap/tiptap-ui-primitive/tooltip/tooltip.tsx +223 -0
  145. package/templates/ui/accordion.tsx +52 -0
  146. package/templates/ui/alert-dialog.tsx +116 -0
  147. package/templates/ui/alert.tsx +48 -0
  148. package/templates/ui/aspect-ratio.tsx +7 -0
  149. package/templates/ui/avatar.tsx +46 -0
  150. package/templates/ui/badge.tsx +32 -0
  151. package/templates/ui/breadcrumb.tsx +98 -0
  152. package/templates/ui/button-group.tsx +77 -0
  153. package/templates/ui/button.tsx +48 -0
  154. package/templates/ui/calendar.tsx +176 -0
  155. package/templates/ui/card.tsx +54 -0
  156. package/templates/ui/carousel.tsx +234 -0
  157. package/templates/ui/chart.tsx +349 -0
  158. package/templates/ui/checkbox.tsx +27 -0
  159. package/templates/ui/collapsible.tsx +11 -0
  160. package/templates/ui/command.tsx +142 -0
  161. package/templates/ui/context-menu.tsx +188 -0
  162. package/templates/ui/curriculum-editor.tsx +601 -0
  163. package/templates/ui/date-picker.tsx +70 -0
  164. package/templates/ui/dialog.tsx +103 -0
  165. package/templates/ui/drawer.tsx +99 -0
  166. package/templates/ui/dropdown-menu.tsx +185 -0
  167. package/templates/ui/dynamic-list-field.tsx +95 -0
  168. package/templates/ui/empty.tsx +90 -0
  169. package/templates/ui/field.tsx +231 -0
  170. package/templates/ui/file-upload-example.tsx +113 -0
  171. package/templates/ui/form.tsx +172 -0
  172. package/templates/ui/hover-card.tsx +28 -0
  173. package/templates/ui/icon-picker.tsx +435 -0
  174. package/templates/ui/icons-data.ts +6 -0
  175. package/templates/ui/image-upload-field.tsx +360 -0
  176. package/templates/ui/input-group.tsx +160 -0
  177. package/templates/ui/input-otp.tsx +70 -0
  178. package/templates/ui/input.tsx +21 -0
  179. package/templates/ui/item.tsx +171 -0
  180. package/templates/ui/kbd.tsx +28 -0
  181. package/templates/ui/label.tsx +20 -0
  182. package/templates/ui/logo.tsx +113 -0
  183. package/templates/ui/markdown-editor.tsx +303 -0
  184. package/templates/ui/markdown-utils.ts +128 -0
  185. package/templates/ui/media-upload-field.tsx +255 -0
  186. package/templates/ui/menubar.tsx +230 -0
  187. package/templates/ui/navigation-menu.tsx +119 -0
  188. package/templates/ui/pagination.tsx +96 -0
  189. package/templates/ui/placeholder.tsx +25 -0
  190. package/templates/ui/popover.tsx +32 -0
  191. package/templates/ui/progress.tsx +24 -0
  192. package/templates/ui/radio-group.tsx +37 -0
  193. package/templates/ui/resizable.tsx +41 -0
  194. package/templates/ui/rich-text-editor.tsx +374 -0
  195. package/templates/ui/scroll-area.tsx +45 -0
  196. package/templates/ui/select.tsx +151 -0
  197. package/templates/ui/separator.tsx +25 -0
  198. package/templates/ui/sheet.tsx +120 -0
  199. package/templates/ui/sidebar.tsx +684 -0
  200. package/templates/ui/skeleton.tsx +7 -0
  201. package/templates/ui/slider.tsx +24 -0
  202. package/templates/ui/sonner.tsx +29 -0
  203. package/templates/ui/spinner.tsx +15 -0
  204. package/templates/ui/switch.tsx +28 -0
  205. package/templates/ui/table.tsx +93 -0
  206. package/templates/ui/tabs.tsx +54 -0
  207. package/templates/ui/textarea.tsx +20 -0
  208. package/templates/ui/toast.tsx +127 -0
  209. package/templates/ui/toggle-group.tsx +56 -0
  210. package/templates/ui/toggle.tsx +43 -0
  211. package/templates/ui/tooltip.tsx +31 -0
  212. package/templates/ui/use-mobile.tsx +19 -0
  213. package/templates/ui/video-upload-field.tsx +368 -0
  214. package/dist/chunk-EIH4RRIJ.js +0 -183
  215. package/dist/chunk-EIH4RRIJ.js.map +0 -1
  216. package/dist/chunk-NKRQYAS6.js +0 -260
  217. package/dist/chunk-NKRQYAS6.js.map +0 -1
  218. package/dist/chunk-QLVSHP7X.js +0 -235
  219. package/dist/chunk-QLVSHP7X.js.map +0 -1
  220. package/dist/chunk-WY6BC55D.js +0 -357
  221. package/dist/chunk-WY6BC55D.js.map +0 -1
  222. package/dist/config/index.d.ts +0 -93
  223. package/dist/config/index.js +0 -58
  224. package/dist/config/index.js.map +0 -1
  225. package/dist/core/index.d.ts +0 -415
  226. package/dist/core/index.js +0 -906
  227. package/dist/core/index.js.map +0 -1
  228. package/dist/import-resolver-BaZ-rzkH.d.ts +0 -123
  229. package/dist/logger-awLb347n.d.ts +0 -81
  230. package/dist/plugins/index.d.ts +0 -213
  231. package/dist/plugins/index.js +0 -365
  232. package/dist/plugins/index.js.map +0 -1
  233. package/dist/types-ByX_gl6y.d.ts +0 -232
  234. package/dist/types-eI549DEG.d.ts +0 -331
@@ -0,0 +1,368 @@
1
+ 'use client'
2
+
3
+ import { useUpload } from '@cms/hooks/use-upload'
4
+ import { cn } from '@cms/utils/cn'
5
+ import { Check, Loader2, Upload, X } from 'lucide-react'
6
+ import * as React from 'react'
7
+ import { toast } from 'sonner'
8
+ import { Button } from './button'
9
+ import { Input } from './input'
10
+ import { Progress } from './progress'
11
+
12
+ function isValidUrl(url: string): boolean {
13
+ try {
14
+ new URL(url)
15
+ return true
16
+ } catch {
17
+ return false
18
+ }
19
+ }
20
+
21
+ function createPreviewUrl(file: File): string {
22
+ return URL.createObjectURL(file)
23
+ }
24
+
25
+ function revokePreviewUrl(url: string): void {
26
+ if (url.startsWith('blob:')) URL.revokeObjectURL(url)
27
+ }
28
+
29
+ function useFetchVideo({ maxSizeInMB }: { accept: string; maxSizeInMB: number }) {
30
+ const [isFetching, setIsFetching] = React.useState(false)
31
+
32
+ const fetchVideoFromUrl = React.useCallback(
33
+ async (url: string): Promise<File | null> => {
34
+ setIsFetching(true)
35
+ try {
36
+ const response = await fetch(url)
37
+ if (!response.ok) {
38
+ toast.error('Failed to fetch video from URL')
39
+ return null
40
+ }
41
+ const blob = await response.blob()
42
+ if (blob.size > maxSizeInMB * 1024 * 1024) {
43
+ toast.error(`Video exceeds ${maxSizeInMB}MB limit`)
44
+ return null
45
+ }
46
+ const filename = url.split('/').pop() || 'video'
47
+ return new File([blob], filename, { type: blob.type })
48
+ } catch {
49
+ toast.error('Failed to fetch video from URL')
50
+ return null
51
+ } finally {
52
+ setIsFetching(false)
53
+ }
54
+ },
55
+ [maxSizeInMB]
56
+ )
57
+
58
+ return { fetchVideoFromUrl, isFetching }
59
+ }
60
+
61
+ export interface VideoUploadFieldProps {
62
+ value?: string
63
+ onChange: (value: string) => void
64
+ onBlur?: () => void
65
+ disabled?: boolean
66
+ accept?: string
67
+ maxSizeInMB?: number
68
+ className?: string
69
+ label?: string
70
+ description?: string
71
+ }
72
+
73
+ interface UrlInputState {
74
+ value: string
75
+ preview: string | null
76
+ }
77
+
78
+ /**
79
+ * Video upload field component with R2 integration
80
+ * Designed for use in forms with react-hook-form
81
+ */
82
+ export function VideoUploadField({
83
+ value,
84
+ onChange,
85
+ onBlur,
86
+ disabled = false,
87
+ accept = 'video/*',
88
+ maxSizeInMB = 100,
89
+ className,
90
+ label,
91
+ description
92
+ }: VideoUploadFieldProps) {
93
+ const fileInputRef = React.useRef<HTMLInputElement>(null)
94
+ const [previewUrl, setPreviewUrl] = React.useState<string | null>(value || null)
95
+ const [urlState, setUrlState] = React.useState<UrlInputState>({
96
+ value: '',
97
+ preview: null
98
+ })
99
+
100
+ const { fetchVideoFromUrl, isFetching: isFetchingUrl } = useFetchVideo({
101
+ accept,
102
+ maxSizeInMB
103
+ })
104
+
105
+ const { upload, mutation, progress } = useUpload({
106
+ validationConfig: {
107
+ maxSizeInBytes: maxSizeInMB * 1024 * 1024,
108
+ allowedTypes:
109
+ accept === 'video/*'
110
+ ? ['video/mp4', 'video/webm', 'video/ogg', 'video/quicktime', 'video/x-msvideo']
111
+ : accept.split(',').map((t) => t.trim()),
112
+ maxFiles: 1
113
+ },
114
+ onSuccess: (result) => {
115
+ if (result.success && result.files?.[0]) {
116
+ const uploadedUrl = result.files[0].url
117
+ onChange(uploadedUrl)
118
+ setPreviewUrl(uploadedUrl)
119
+ toast.success('Video uploaded successfully')
120
+ } else {
121
+ const errorMsg = result.error || 'Upload failed'
122
+ toast.error(errorMsg)
123
+ console.error('Upload failed:', errorMsg)
124
+ setPreviewUrl(value || null)
125
+ }
126
+ },
127
+ onError: (error) => {
128
+ const errorMsg = error.message || 'Network error during upload'
129
+ toast.error(errorMsg)
130
+ console.error('Upload error:', error)
131
+ setPreviewUrl(value || null)
132
+ },
133
+ prefix: 'videos'
134
+ })
135
+
136
+ // Update preview when value changes externally
137
+ React.useEffect(() => {
138
+ if (value) {
139
+ setPreviewUrl(value)
140
+ }
141
+ }, [value])
142
+
143
+ // Cleanup object URLs on unmount
144
+ React.useEffect(() => {
145
+ return () => {
146
+ if (previewUrl) {
147
+ revokePreviewUrl(previewUrl)
148
+ }
149
+ }
150
+ }, [previewUrl])
151
+
152
+ const handleFileSelect = React.useCallback(
153
+ (e: React.ChangeEvent<HTMLInputElement>) => {
154
+ const file = e.target.files?.[0]
155
+ if (!file) return
156
+
157
+ if (file.size === 0) {
158
+ toast.error('Cannot upload empty file')
159
+ if (fileInputRef.current) {
160
+ fileInputRef.current.value = ''
161
+ }
162
+ return
163
+ }
164
+
165
+ // Create local preview
166
+ try {
167
+ const localPreview = createPreviewUrl(file)
168
+ setPreviewUrl(localPreview)
169
+ } catch (error) {
170
+ console.error('[VideoUploadField] Failed to create preview:', error)
171
+ toast.error('Failed to preview video')
172
+ return
173
+ }
174
+
175
+ upload([file])
176
+ },
177
+ [upload]
178
+ )
179
+
180
+ const handleUrlChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
181
+ const url = e.target.value.trim()
182
+ const preview = url && isValidUrl(url) ? url : null
183
+ setUrlState({ value: url, preview })
184
+ }, [])
185
+
186
+ const handleUrlConfirm = React.useCallback(async () => {
187
+ if (!urlState.value.trim()) {
188
+ toast.error('Please enter a valid video URL')
189
+ return
190
+ }
191
+
192
+ const file = await fetchVideoFromUrl(urlState.value.trim())
193
+ if (!file) return
194
+
195
+ // Create local preview
196
+ try {
197
+ const localPreview = createPreviewUrl(file)
198
+ setPreviewUrl(localPreview)
199
+ setUrlState({ value: '', preview: null })
200
+ } catch (error) {
201
+ console.error('[VideoUploadField] Failed to create preview:', error)
202
+ toast.error('Failed to preview video')
203
+ return
204
+ }
205
+
206
+ upload([file])
207
+ }, [urlState.value, fetchVideoFromUrl, upload])
208
+
209
+ const handleRemove = React.useCallback(() => {
210
+ if (previewUrl) {
211
+ revokePreviewUrl(previewUrl)
212
+ }
213
+ setPreviewUrl(null)
214
+ setUrlState({ value: '', preview: null })
215
+ onChange('')
216
+ mutation.reset()
217
+ if (fileInputRef.current) {
218
+ fileInputRef.current.value = ''
219
+ }
220
+ }, [previewUrl, onChange, mutation])
221
+
222
+ const handleClick = React.useCallback(() => {
223
+ fileInputRef.current?.click()
224
+ }, [])
225
+
226
+ const isUploading = mutation.isPending
227
+ const uploadProgress = progress[0]?.progress || 0
228
+ const displayPreview = urlState.preview || previewUrl
229
+
230
+ return (
231
+ <div className={cn('space-y-2', className)}>
232
+ {label && (
233
+ <label htmlFor="video-input" className="text-sm font-medium">
234
+ {label}
235
+ </label>
236
+ )}
237
+ {description && <p className="text-sm text-muted-foreground">{description}</p>}
238
+
239
+ <div className="space-y-4">
240
+ <input
241
+ id="video-input"
242
+ ref={fileInputRef}
243
+ type="file"
244
+ accept={accept}
245
+ onChange={handleFileSelect}
246
+ onBlur={onBlur}
247
+ disabled={disabled || isUploading || isFetchingUrl}
248
+ className="hidden"
249
+ />
250
+
251
+ {/* URL Input Section */}
252
+ <div className="space-y-2">
253
+ <div className="flex gap-2">
254
+ <Input
255
+ type="url"
256
+ placeholder="Paste video URL here or Click below to upload"
257
+ value={urlState.value}
258
+ onChange={handleUrlChange}
259
+ onBlur={onBlur}
260
+ disabled={disabled || isUploading || isFetchingUrl}
261
+ className="flex-1"
262
+ />
263
+ {urlState.preview && (
264
+ <Button
265
+ type="button"
266
+ onClick={handleUrlConfirm}
267
+ disabled={disabled || isUploading || isFetchingUrl}
268
+ className="shrink-0"
269
+ size="lg"
270
+ >
271
+ {isFetchingUrl ? (
272
+ <Loader2 className="size-4 animate-spin" />
273
+ ) : (
274
+ <>
275
+ <Check className="size-4" />
276
+ Confirm
277
+ </>
278
+ )}
279
+ </Button>
280
+ )}
281
+ </div>
282
+ </div>
283
+
284
+ {/* Preview or Upload Area */}
285
+ {displayPreview ? (
286
+ <div className="relative w-full rounded-lg corner-squircle border border-dashed border-border bg-secondary h-50 flex items-center justify-center p-10 group overflow-hidden">
287
+ <video
288
+ src={displayPreview}
289
+ controls
290
+ className="w-full h-auto max-h-60 object-contain rounded-lg corner-squircle aspect-[3/4]"
291
+ >
292
+ <track kind="captions" />
293
+ </video>
294
+
295
+ {/* Upload Progress Overlay */}
296
+ {isUploading && (
297
+ <div className="absolute inset-0 bg-black/50 flex flex-col items-center justify-center gap-2 rounded-lg corner-squircle">
298
+ <Loader2 className="size-8 text-white animate-spin" />
299
+ <Progress value={uploadProgress} className="w-3/4" />
300
+ <span className="text-white text-sm">{uploadProgress}%</span>
301
+ </div>
302
+ )}
303
+
304
+ {/* Remove Button */}
305
+ {!isUploading && !isFetchingUrl && (
306
+ <Button
307
+ type="button"
308
+ variant="destructive"
309
+ size="icon"
310
+ className="absolute top-2 right-2 hidden group-hover:flex"
311
+ onClick={handleRemove}
312
+ disabled={disabled}
313
+ >
314
+ <X className="size-4" />
315
+ </Button>
316
+ )}
317
+
318
+ {/* URL Preview Indicator */}
319
+ {urlState.preview && !previewUrl && (
320
+ <div className="absolute bottom-2 left-2 bg-primary/90 text-primary-foreground text-xs px-2 py-1 rounded">
321
+ Click Confirm to upload
322
+ </div>
323
+ )}
324
+ </div>
325
+ ) : (
326
+ <button
327
+ type="button"
328
+ onClick={handleClick}
329
+ disabled={disabled || isUploading}
330
+ className={cn(
331
+ 'w-full rounded-lg corner-squircle border border-dashed border-border bg-secondary/50',
332
+ 'hover:border-primary/50 transition-colors',
333
+ 'flex flex-col items-center justify-center gap-2 p-8',
334
+ 'disabled:opacity-50 disabled:cursor-not-allowed'
335
+ )}
336
+ >
337
+ {isUploading ? (
338
+ <>
339
+ <Loader2 className="size-10 text-muted-foreground animate-spin" />
340
+ <Progress value={uploadProgress} className="w-3/4" />
341
+ <span className="text-sm text-muted-foreground">{uploadProgress}%</span>
342
+ </>
343
+ ) : (
344
+ <>
345
+ <div className="rounded-full corner-squircle bg-primary/10 p-3">
346
+ <Upload className="size-6 text-primary" />
347
+ </div>
348
+ <div className="text-center">
349
+ <p className="text-sm font-medium">Click to upload video</p>
350
+ <p className="text-xs text-muted-foreground mt-1">Max size: {maxSizeInMB}MB</p>
351
+ </div>
352
+ </>
353
+ )}
354
+ </button>
355
+ )}
356
+
357
+ {/* Error Message */}
358
+ {mutation.isError && (
359
+ <div className="text-sm text-destructive">Upload failed: {mutation.error.message}</div>
360
+ )}
361
+
362
+ {mutation.data && !mutation.data.success && (
363
+ <div className="text-sm text-destructive">{mutation.data.error}</div>
364
+ )}
365
+ </div>
366
+ </div>
367
+ )
368
+ }
@@ -1,183 +0,0 @@
1
- import {
2
- ImportResolver,
3
- MONOREPO_IMPORT_PATHS
4
- } from "./chunk-NKRQYAS6.js";
5
-
6
- // src/utils.ts
7
- import fs from "fs";
8
- import path from "path";
9
- var _paths = null;
10
- function initPaths(startDir) {
11
- const cwd = startDir || process.cwd();
12
- let root = cwd;
13
- while (root !== "/") {
14
- if (fs.existsSync(path.join(root, "turbo.json")) || fs.existsSync(path.join(root, "pnpm-workspace.yaml"))) {
15
- break;
16
- }
17
- root = path.dirname(root);
18
- }
19
- if (root === "/") {
20
- throw new Error("Could not find monorepo root (no turbo.json or pnpm-workspace.yaml found)");
21
- }
22
- _paths = {
23
- root,
24
- app: path.join(root, "apps/web"),
25
- database: path.join(root, "packages/database"),
26
- lib: path.join(root, "packages/lib"),
27
- hooks: path.join(root, "packages/hooks"),
28
- schemas: path.join(root, "packages/codegen/src/schemas"),
29
- data: path.join(root, "packages/data")
30
- };
31
- return _paths;
32
- }
33
- function setPaths(paths) {
34
- _paths = paths;
35
- }
36
- function getPaths() {
37
- if (!_paths) {
38
- return initPaths();
39
- }
40
- return _paths;
41
- }
42
- function getProjectRoot() {
43
- return getPaths().app;
44
- }
45
- var _importResolver = null;
46
- function initImportResolver(config) {
47
- _importResolver = new ImportResolver(config.imports);
48
- return _importResolver;
49
- }
50
- function getImportResolver() {
51
- if (!_importResolver) {
52
- _importResolver = new ImportResolver(MONOREPO_IMPORT_PATHS);
53
- }
54
- return _importResolver;
55
- }
56
- function toPascalCase(str) {
57
- return str.split(/[-_\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
58
- }
59
- function toCamelCase(str) {
60
- const pascal = toPascalCase(str);
61
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
62
- }
63
- function toKebabCase(str) {
64
- return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
65
- }
66
- function toSnakeCase(str) {
67
- return str.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
68
- }
69
- function toScreamingSnakeCase(str) {
70
- return str.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toUpperCase();
71
- }
72
- function ensureDir(dirPath) {
73
- if (!fs.existsSync(dirPath)) {
74
- fs.mkdirSync(dirPath, { recursive: true });
75
- }
76
- }
77
- function getMigrationTimestamp() {
78
- const now = /* @__PURE__ */ new Date();
79
- const year = now.getFullYear();
80
- const month = String(now.getMonth() + 1).padStart(2, "0");
81
- const day = String(now.getDate()).padStart(2, "0");
82
- const hours = String(now.getHours()).padStart(2, "0");
83
- const minutes = String(now.getMinutes()).padStart(2, "0");
84
- const seconds = String(now.getSeconds()).padStart(2, "0");
85
- return `${year}${month}${day}${hours}${minutes}${seconds}`;
86
- }
87
- function isPlural(str) {
88
- const pluralPatterns = [
89
- /ies$/,
90
- /ves$/,
91
- /ses$/,
92
- /xes$/,
93
- /zes$/,
94
- /ches$/,
95
- /shes$/,
96
- /men$/,
97
- /people$/
98
- ];
99
- for (const pattern of pluralPatterns) {
100
- if (pattern.test(str)) {
101
- return true;
102
- }
103
- }
104
- if (str.endsWith("s") && !str.endsWith("ss")) {
105
- return true;
106
- }
107
- return false;
108
- }
109
- function pluralize(str) {
110
- if (isPlural(str)) {
111
- return str;
112
- }
113
- if (str.endsWith("y") && !["ay", "ey", "iy", "oy", "uy"].some((v) => str.endsWith(v))) {
114
- return `${str.slice(0, -1)}ies`;
115
- }
116
- if (str.endsWith("s") || str.endsWith("x") || str.endsWith("ch") || str.endsWith("sh")) {
117
- return `${str}es`;
118
- }
119
- if (str.endsWith("f")) {
120
- return `${str.slice(0, -1)}ves`;
121
- }
122
- if (str.endsWith("fe")) {
123
- return `${str.slice(0, -2)}ves`;
124
- }
125
- return `${str}s`;
126
- }
127
- function singularize(str) {
128
- if (!isPlural(str)) {
129
- return str;
130
- }
131
- if (str.endsWith("ies")) {
132
- return `${str.slice(0, -3)}y`;
133
- }
134
- if (str.endsWith("ves")) {
135
- return `${str.slice(0, -3)}f`;
136
- }
137
- if (str.endsWith("sses") || str.endsWith("xes") || str.endsWith("ches") || str.endsWith("shes") || str.endsWith("zes")) {
138
- return str.slice(0, -2);
139
- }
140
- if (str.endsWith("s") && !str.endsWith("ss")) {
141
- return str.slice(0, -1);
142
- }
143
- return str;
144
- }
145
- function quotePropertyName(name) {
146
- const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
147
- if (validIdentifier.test(name)) {
148
- return name;
149
- }
150
- return `'${name}'`;
151
- }
152
- function singularizeLabel(label) {
153
- const words = label.split(" ");
154
- if (words.length === 0) {
155
- return label;
156
- }
157
- const lastWord = words[words.length - 1];
158
- const lastWordLower = lastWord.toLowerCase();
159
- const singularLastWordLower = singularize(lastWordLower);
160
- const capitalizedSingular = lastWord[0] === lastWord[0].toUpperCase() ? singularLastWordLower.charAt(0).toUpperCase() + singularLastWordLower.slice(1) : singularLastWordLower;
161
- return [...words.slice(0, -1), capitalizedSingular].join(" ");
162
- }
163
-
164
- export {
165
- initPaths,
166
- setPaths,
167
- getPaths,
168
- getProjectRoot,
169
- initImportResolver,
170
- getImportResolver,
171
- toPascalCase,
172
- toCamelCase,
173
- toKebabCase,
174
- toSnakeCase,
175
- toScreamingSnakeCase,
176
- ensureDir,
177
- getMigrationTimestamp,
178
- pluralize,
179
- singularize,
180
- quotePropertyName,
181
- singularizeLabel
182
- };
183
- //# sourceMappingURL=chunk-EIH4RRIJ.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils.ts"],"sourcesContent":["// Utility functions for the codemod system\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { ImportResolver } from './config/import-resolver'\nimport { MONOREPO_IMPORT_PATHS } from './config/presets'\nimport type { BetterstartConfig } from './config/types'\nimport type { MonorepoPaths } from './types'\n\n// Store monorepo paths globally (set during CLI initialization)\nlet _paths: MonorepoPaths | null = null\n\n/**\n * Initialize paths for the monorepo\n * Detects monorepo root and sets up all package paths\n */\nexport function initPaths(startDir?: string): MonorepoPaths {\n const cwd = startDir || process.cwd()\n\n // Find monorepo root by looking for turbo.json or pnpm-workspace.yaml\n let root = cwd\n while (root !== '/') {\n if (\n fs.existsSync(path.join(root, 'turbo.json')) ||\n fs.existsSync(path.join(root, 'pnpm-workspace.yaml'))\n ) {\n break\n }\n root = path.dirname(root)\n }\n\n if (root === '/') {\n throw new Error('Could not find monorepo root (no turbo.json or pnpm-workspace.yaml found)')\n }\n\n _paths = {\n root,\n app: path.join(root, 'apps/web'),\n database: path.join(root, 'packages/database'),\n lib: path.join(root, 'packages/lib'),\n hooks: path.join(root, 'packages/hooks'),\n schemas: path.join(root, 'packages/codegen/src/schemas'),\n data: path.join(root, 'packages/data')\n }\n\n return _paths\n}\n\n/**\n * Set pre-resolved paths (e.g., from config-based resolution)\n * This allows the CLI to seed paths from the config system\n * so that generators work in both monorepo and standalone projects.\n */\nexport function setPaths(paths: MonorepoPaths): void {\n _paths = paths\n}\n\n/**\n * Get monorepo paths (must call initPaths or setPaths first)\n */\nexport function getPaths(): MonorepoPaths {\n if (!_paths) {\n return initPaths()\n }\n return _paths\n}\n\n/**\n * Get the monorepo root directory\n * @deprecated Use getPaths().root instead\n */\nexport function getProjectRoot(): string {\n return getPaths().app\n}\n\n// Store import resolver globally (set during CLI initialization)\nlet _importResolver: ImportResolver | null = null\n\n/**\n * Initialize the import resolver from config\n */\nexport function initImportResolver(config: BetterstartConfig): ImportResolver {\n _importResolver = new ImportResolver(config.imports)\n return _importResolver\n}\n\n/**\n * Get the import resolver (must call initImportResolver first, or falls back to monorepo defaults)\n */\nexport function getImportResolver(): ImportResolver {\n if (!_importResolver) {\n _importResolver = new ImportResolver(MONOREPO_IMPORT_PATHS)\n }\n return _importResolver\n}\n\n/**\n * Convert a string to PascalCase\n */\nexport function toPascalCase(str: string): string {\n return str\n .split(/[-_\\s]+/)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join('')\n}\n\n/**\n * Convert a string to camelCase\n */\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str)\n return pascal.charAt(0).toLowerCase() + pascal.slice(1)\n}\n\n/**\n * Convert a string to kebab-case\n */\nexport function toKebabCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase()\n}\n\n/**\n * Convert a string to snake_case\n */\nexport function toSnakeCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n .replace(/[\\s-]+/g, '_')\n .toLowerCase()\n}\n\n/**\n * Convert a string to SCREAMING_SNAKE_CASE\n */\nexport function toScreamingSnakeCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n .replace(/[\\s-]+/g, '_')\n .toUpperCase()\n}\n\n/**\n * Ensure a directory exists, create it if it doesn't\n */\nexport function ensureDir(dirPath: string): void {\n if (!fs.existsSync(dirPath)) {\n fs.mkdirSync(dirPath, { recursive: true })\n }\n}\n\n/**\n * Format a timestamp for migration files\n */\nexport function getMigrationTimestamp(): string {\n const now = new Date()\n const year = now.getFullYear()\n const month = String(now.getMonth() + 1).padStart(2, '0')\n const day = String(now.getDate()).padStart(2, '0')\n const hours = String(now.getHours()).padStart(2, '0')\n const minutes = String(now.getMinutes()).padStart(2, '0')\n const seconds = String(now.getSeconds()).padStart(2, '0')\n return `${year}${month}${day}${hours}${minutes}${seconds}`\n}\n\n/**\n * Check if a word is already plural\n */\nfunction isPlural(str: string): boolean {\n const pluralPatterns = [\n /ies$/,\n /ves$/,\n /ses$/,\n /xes$/,\n /zes$/,\n /ches$/,\n /shes$/,\n /men$/,\n /people$/\n ]\n\n for (const pattern of pluralPatterns) {\n if (pattern.test(str)) {\n return true\n }\n }\n\n if (str.endsWith('s') && !str.endsWith('ss')) {\n return true\n }\n\n return false\n}\n\n/**\n * Pluralize a string\n */\nexport function pluralize(str: string): string {\n if (isPlural(str)) {\n return str\n }\n\n if (str.endsWith('y') && !['ay', 'ey', 'iy', 'oy', 'uy'].some((v) => str.endsWith(v))) {\n return `${str.slice(0, -1)}ies`\n }\n if (str.endsWith('s') || str.endsWith('x') || str.endsWith('ch') || str.endsWith('sh')) {\n return `${str}es`\n }\n if (str.endsWith('f')) {\n return `${str.slice(0, -1)}ves`\n }\n if (str.endsWith('fe')) {\n return `${str.slice(0, -2)}ves`\n }\n return `${str}s`\n}\n\n/**\n * Singularize a string\n */\nexport function singularize(str: string): string {\n if (!isPlural(str)) {\n return str\n }\n\n if (str.endsWith('ies')) {\n return `${str.slice(0, -3)}y`\n }\n if (str.endsWith('ves')) {\n return `${str.slice(0, -3)}f`\n }\n if (\n str.endsWith('sses') ||\n str.endsWith('xes') ||\n str.endsWith('ches') ||\n str.endsWith('shes') ||\n str.endsWith('zes')\n ) {\n return str.slice(0, -2)\n }\n if (str.endsWith('s') && !str.endsWith('ss')) {\n return str.slice(0, -1)\n }\n return str\n}\n\n/**\n * Quote a property name if it's not a valid JavaScript identifier.\n * Properties with hyphens, spaces, or starting with numbers need quotes.\n */\nexport function quotePropertyName(name: string): string {\n // Valid JS identifier: starts with letter/underscore/$, contains only letters/numbers/underscore/$\n const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/\n if (validIdentifier.test(name)) {\n return name\n }\n return `'${name}'`\n}\n\n/**\n * Singularize a label (e.g., \"Trusted Companies\" -> \"Trusted Company\")\n * Handles display labels with proper capitalization\n */\nexport function singularizeLabel(label: string): string {\n const words = label.split(' ')\n if (words.length === 0) {\n return label\n }\n\n // Singularize the last word\n const lastWord = words[words.length - 1]\n const lastWordLower = lastWord.toLowerCase()\n const singularLastWordLower = singularize(lastWordLower)\n\n // Preserve capitalization of the last word\n const capitalizedSingular =\n lastWord[0] === lastWord[0].toUpperCase()\n ? singularLastWordLower.charAt(0).toUpperCase() + singularLastWordLower.slice(1)\n : singularLastWordLower\n\n return [...words.slice(0, -1), capitalizedSingular].join(' ')\n}\n"],"mappings":"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","names":[]}