@pascal-app/editor 0.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 (165) hide show
  1. package/package.json +62 -0
  2. package/src/components/editor/custom-camera-controls.tsx +387 -0
  3. package/src/components/editor/editor-layout-v2.tsx +220 -0
  4. package/src/components/editor/export-manager.tsx +78 -0
  5. package/src/components/editor/first-person-controls.tsx +249 -0
  6. package/src/components/editor/floating-action-menu.tsx +231 -0
  7. package/src/components/editor/floorplan-panel.tsx +9609 -0
  8. package/src/components/editor/grid.tsx +161 -0
  9. package/src/components/editor/index.tsx +928 -0
  10. package/src/components/editor/node-action-menu.tsx +66 -0
  11. package/src/components/editor/preset-thumbnail-generator.tsx +125 -0
  12. package/src/components/editor/selection-manager.tsx +897 -0
  13. package/src/components/editor/site-edge-labels.tsx +90 -0
  14. package/src/components/editor/thumbnail-generator.tsx +166 -0
  15. package/src/components/editor/wall-measurement-label.tsx +258 -0
  16. package/src/components/feedback-dialog.tsx +265 -0
  17. package/src/components/pascal-radio.tsx +280 -0
  18. package/src/components/preview-button.tsx +16 -0
  19. package/src/components/systems/ceiling/ceiling-system.tsx +77 -0
  20. package/src/components/systems/roof/roof-edit-system.tsx +69 -0
  21. package/src/components/systems/stair/stair-edit-system.tsx +69 -0
  22. package/src/components/systems/zone/zone-label-editor-system.tsx +320 -0
  23. package/src/components/systems/zone/zone-system.tsx +87 -0
  24. package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +42 -0
  25. package/src/components/tools/ceiling/ceiling-hole-editor.tsx +47 -0
  26. package/src/components/tools/ceiling/ceiling-tool.tsx +465 -0
  27. package/src/components/tools/door/door-math.ts +110 -0
  28. package/src/components/tools/door/door-tool.tsx +293 -0
  29. package/src/components/tools/door/move-door-tool.tsx +373 -0
  30. package/src/components/tools/item/item-tool.tsx +26 -0
  31. package/src/components/tools/item/move-tool.tsx +90 -0
  32. package/src/components/tools/item/placement-math.ts +85 -0
  33. package/src/components/tools/item/placement-strategies.ts +556 -0
  34. package/src/components/tools/item/placement-types.ts +117 -0
  35. package/src/components/tools/item/use-draft-node.ts +227 -0
  36. package/src/components/tools/item/use-placement-coordinator.tsx +877 -0
  37. package/src/components/tools/roof/move-roof-tool.tsx +288 -0
  38. package/src/components/tools/roof/roof-tool.tsx +318 -0
  39. package/src/components/tools/select/box-select-tool.tsx +626 -0
  40. package/src/components/tools/shared/cursor-sphere.tsx +119 -0
  41. package/src/components/tools/shared/polygon-editor.tsx +361 -0
  42. package/src/components/tools/site/site-boundary-editor.tsx +42 -0
  43. package/src/components/tools/slab/slab-boundary-editor.tsx +42 -0
  44. package/src/components/tools/slab/slab-hole-editor.tsx +47 -0
  45. package/src/components/tools/slab/slab-tool.tsx +322 -0
  46. package/src/components/tools/stair/stair-defaults.ts +7 -0
  47. package/src/components/tools/stair/stair-tool.tsx +194 -0
  48. package/src/components/tools/tool-manager.tsx +120 -0
  49. package/src/components/tools/wall/wall-drafting.ts +140 -0
  50. package/src/components/tools/wall/wall-tool.tsx +210 -0
  51. package/src/components/tools/window/move-window-tool.tsx +410 -0
  52. package/src/components/tools/window/window-math.ts +117 -0
  53. package/src/components/tools/window/window-tool.tsx +303 -0
  54. package/src/components/tools/zone/zone-boundary-editor.tsx +39 -0
  55. package/src/components/tools/zone/zone-tool.tsx +364 -0
  56. package/src/components/ui/action-menu/action-button.tsx +59 -0
  57. package/src/components/ui/action-menu/camera-actions.tsx +74 -0
  58. package/src/components/ui/action-menu/control-modes.tsx +240 -0
  59. package/src/components/ui/action-menu/furnish-tools.tsx +102 -0
  60. package/src/components/ui/action-menu/index.tsx +152 -0
  61. package/src/components/ui/action-menu/structure-tools.tsx +100 -0
  62. package/src/components/ui/action-menu/view-toggles.tsx +397 -0
  63. package/src/components/ui/command-palette/editor-commands.tsx +396 -0
  64. package/src/components/ui/command-palette/index.tsx +730 -0
  65. package/src/components/ui/controls/action-button.tsx +33 -0
  66. package/src/components/ui/controls/material-picker.tsx +194 -0
  67. package/src/components/ui/controls/metric-control.tsx +262 -0
  68. package/src/components/ui/controls/panel-section.tsx +65 -0
  69. package/src/components/ui/controls/segmented-control.tsx +45 -0
  70. package/src/components/ui/controls/slider-control.tsx +245 -0
  71. package/src/components/ui/controls/toggle-control.tsx +38 -0
  72. package/src/components/ui/floating-level-selector.tsx +355 -0
  73. package/src/components/ui/helpers/ceiling-helper.tsx +20 -0
  74. package/src/components/ui/helpers/helper-manager.tsx +33 -0
  75. package/src/components/ui/helpers/item-helper.tsx +40 -0
  76. package/src/components/ui/helpers/roof-helper.tsx +16 -0
  77. package/src/components/ui/helpers/slab-helper.tsx +20 -0
  78. package/src/components/ui/helpers/wall-helper.tsx +20 -0
  79. package/src/components/ui/item-catalog/catalog-items.tsx +1580 -0
  80. package/src/components/ui/item-catalog/item-catalog.tsx +219 -0
  81. package/src/components/ui/panels/ceiling-panel.tsx +230 -0
  82. package/src/components/ui/panels/collections/collections-popover.tsx +356 -0
  83. package/src/components/ui/panels/door-panel.tsx +600 -0
  84. package/src/components/ui/panels/item-panel.tsx +306 -0
  85. package/src/components/ui/panels/panel-manager.tsx +59 -0
  86. package/src/components/ui/panels/panel-wrapper.tsx +80 -0
  87. package/src/components/ui/panels/presets/presets-popover.tsx +511 -0
  88. package/src/components/ui/panels/reference-panel.tsx +177 -0
  89. package/src/components/ui/panels/roof-panel.tsx +262 -0
  90. package/src/components/ui/panels/roof-segment-panel.tsx +326 -0
  91. package/src/components/ui/panels/slab-panel.tsx +228 -0
  92. package/src/components/ui/panels/stair-panel.tsx +304 -0
  93. package/src/components/ui/panels/stair-segment-panel.tsx +339 -0
  94. package/src/components/ui/panels/wall-panel.tsx +123 -0
  95. package/src/components/ui/panels/window-panel.tsx +441 -0
  96. package/src/components/ui/primitives/button.tsx +69 -0
  97. package/src/components/ui/primitives/card.tsx +75 -0
  98. package/src/components/ui/primitives/color-dot.tsx +61 -0
  99. package/src/components/ui/primitives/context-menu.tsx +227 -0
  100. package/src/components/ui/primitives/dialog.tsx +129 -0
  101. package/src/components/ui/primitives/dropdown-menu.tsx +228 -0
  102. package/src/components/ui/primitives/error-boundary.tsx +52 -0
  103. package/src/components/ui/primitives/input.tsx +21 -0
  104. package/src/components/ui/primitives/number-input.tsx +187 -0
  105. package/src/components/ui/primitives/opacity-control.tsx +79 -0
  106. package/src/components/ui/primitives/popover.tsx +42 -0
  107. package/src/components/ui/primitives/separator.tsx +28 -0
  108. package/src/components/ui/primitives/sheet.tsx +130 -0
  109. package/src/components/ui/primitives/shortcut-token.tsx +64 -0
  110. package/src/components/ui/primitives/sidebar.tsx +855 -0
  111. package/src/components/ui/primitives/skeleton.tsx +13 -0
  112. package/src/components/ui/primitives/slider.tsx +58 -0
  113. package/src/components/ui/primitives/switch.tsx +29 -0
  114. package/src/components/ui/primitives/tooltip.tsx +57 -0
  115. package/src/components/ui/scene-loader.tsx +40 -0
  116. package/src/components/ui/sidebar/app-sidebar.tsx +103 -0
  117. package/src/components/ui/sidebar/icon-rail.tsx +147 -0
  118. package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +100 -0
  119. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +438 -0
  120. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +188 -0
  121. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +80 -0
  122. package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +126 -0
  123. package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +64 -0
  124. package/src/components/ui/sidebar/panels/site-panel/index.tsx +1543 -0
  125. package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +98 -0
  126. package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +117 -0
  127. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +65 -0
  128. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +214 -0
  129. package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +96 -0
  130. package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +216 -0
  131. package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +115 -0
  132. package/src/components/ui/sidebar/panels/site-panel/tree-node-drag.tsx +342 -0
  133. package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +271 -0
  134. package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +106 -0
  135. package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +64 -0
  136. package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +87 -0
  137. package/src/components/ui/sidebar/panels/zone-panel/index.tsx +167 -0
  138. package/src/components/ui/sidebar/tab-bar.tsx +39 -0
  139. package/src/components/ui/slider-demo.tsx +36 -0
  140. package/src/components/ui/slider.tsx +81 -0
  141. package/src/components/ui/viewer-toolbar.tsx +342 -0
  142. package/src/components/viewer-overlay.tsx +499 -0
  143. package/src/components/viewer-zone-system.tsx +48 -0
  144. package/src/contexts/presets-context.tsx +121 -0
  145. package/src/hooks/use-auto-save.ts +194 -0
  146. package/src/hooks/use-contextual-tools.ts +52 -0
  147. package/src/hooks/use-grid-events.ts +106 -0
  148. package/src/hooks/use-keyboard.ts +214 -0
  149. package/src/hooks/use-mobile.ts +19 -0
  150. package/src/hooks/use-reduced-motion.ts +20 -0
  151. package/src/index.tsx +33 -0
  152. package/src/lib/constants.ts +3 -0
  153. package/src/lib/level-selection.ts +31 -0
  154. package/src/lib/scene.ts +394 -0
  155. package/src/lib/sfx/index.ts +2 -0
  156. package/src/lib/sfx-bus.ts +49 -0
  157. package/src/lib/sfx-player.ts +60 -0
  158. package/src/lib/utils.ts +43 -0
  159. package/src/store/use-audio.tsx +45 -0
  160. package/src/store/use-command-registry.ts +36 -0
  161. package/src/store/use-editor.tsx +522 -0
  162. package/src/store/use-palette-view-registry.ts +45 -0
  163. package/src/store/use-upload.ts +90 -0
  164. package/src/three-types.ts +3 -0
  165. package/tsconfig.json +9 -0
@@ -0,0 +1,227 @@
1
+ 'use client'
2
+
3
+ import * as ContextMenuPrimitive from '@radix-ui/react-context-menu'
4
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react'
5
+ import type * as React from 'react'
6
+
7
+ import { cn } from '../../../lib/utils'
8
+
9
+ function ContextMenu({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
10
+ return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
11
+ }
12
+
13
+ function ContextMenuTrigger({
14
+ ...props
15
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {
16
+ return <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} />
17
+ }
18
+
19
+ function ContextMenuGroup({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
20
+ return <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
21
+ }
22
+
23
+ function ContextMenuPortal({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
24
+ return <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
25
+ }
26
+
27
+ function ContextMenuSub({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
28
+ return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />
29
+ }
30
+
31
+ function ContextMenuRadioGroup({
32
+ ...props
33
+ }: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {
34
+ return <ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} />
35
+ }
36
+
37
+ function ContextMenuSubTrigger({
38
+ className,
39
+ inset,
40
+ children,
41
+ ...props
42
+ }: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
43
+ inset?: boolean
44
+ }) {
45
+ return (
46
+ <ContextMenuPrimitive.SubTrigger
47
+ className={cn(
48
+ "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 font-barlow text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[inset]:pl-8 data-[state=open]:text-accent-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0",
49
+ className,
50
+ )}
51
+ data-inset={inset}
52
+ data-slot="context-menu-sub-trigger"
53
+ {...props}
54
+ >
55
+ {children}
56
+ <ChevronRightIcon className="ml-auto" />
57
+ </ContextMenuPrimitive.SubTrigger>
58
+ )
59
+ }
60
+
61
+ function ContextMenuSubContent({
62
+ className,
63
+ ...props
64
+ }: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
65
+ return (
66
+ <ContextMenuPrimitive.SubContent
67
+ className={cn(
68
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=closed]:animate-out data-[state=open]:animate-in',
69
+ className,
70
+ )}
71
+ data-slot="context-menu-sub-content"
72
+ {...props}
73
+ />
74
+ )
75
+ }
76
+
77
+ function ContextMenuContent({
78
+ className,
79
+ ...props
80
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {
81
+ return (
82
+ <ContextMenuPrimitive.Portal>
83
+ <ContextMenuPrimitive.Content
84
+ className={cn(
85
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in',
86
+ className,
87
+ )}
88
+ data-slot="context-menu-content"
89
+ {...props}
90
+ />
91
+ </ContextMenuPrimitive.Portal>
92
+ )
93
+ }
94
+
95
+ function ContextMenuItem({
96
+ className,
97
+ inset,
98
+ variant = 'default',
99
+ ...props
100
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
101
+ inset?: boolean
102
+ variant?: 'default' | 'destructive'
103
+ }) {
104
+ return (
105
+ <ContextMenuPrimitive.Item
106
+ className={cn(
107
+ "data-[variant=destructive]:*:[svg]:!text-destructive relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 font-barlow text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[disabled]:opacity-50 data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0",
108
+ className,
109
+ )}
110
+ data-inset={inset}
111
+ data-slot="context-menu-item"
112
+ data-variant={variant}
113
+ {...props}
114
+ />
115
+ )
116
+ }
117
+
118
+ function ContextMenuCheckboxItem({
119
+ className,
120
+ children,
121
+ checked,
122
+ ...props
123
+ }: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {
124
+ return (
125
+ <ContextMenuPrimitive.CheckboxItem
126
+ checked={checked}
127
+ className={cn(
128
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 font-barlow text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
129
+ className,
130
+ )}
131
+ data-slot="context-menu-checkbox-item"
132
+ {...props}
133
+ >
134
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
135
+ <ContextMenuPrimitive.ItemIndicator>
136
+ <CheckIcon className="size-4" />
137
+ </ContextMenuPrimitive.ItemIndicator>
138
+ </span>
139
+ {children}
140
+ </ContextMenuPrimitive.CheckboxItem>
141
+ )
142
+ }
143
+
144
+ function ContextMenuRadioItem({
145
+ className,
146
+ children,
147
+ ...props
148
+ }: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {
149
+ return (
150
+ <ContextMenuPrimitive.RadioItem
151
+ className={cn(
152
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 font-barlow text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
153
+ className,
154
+ )}
155
+ data-slot="context-menu-radio-item"
156
+ {...props}
157
+ >
158
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
159
+ <ContextMenuPrimitive.ItemIndicator>
160
+ <CircleIcon className="size-2 fill-current" />
161
+ </ContextMenuPrimitive.ItemIndicator>
162
+ </span>
163
+ {children}
164
+ </ContextMenuPrimitive.RadioItem>
165
+ )
166
+ }
167
+
168
+ function ContextMenuLabel({
169
+ className,
170
+ inset,
171
+ ...props
172
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
173
+ inset?: boolean
174
+ }) {
175
+ return (
176
+ <ContextMenuPrimitive.Label
177
+ className={cn(
178
+ 'px-2 py-1.5 font-barlow font-medium text-foreground text-sm data-[inset]:pl-8',
179
+ className,
180
+ )}
181
+ data-inset={inset}
182
+ data-slot="context-menu-label"
183
+ {...props}
184
+ />
185
+ )
186
+ }
187
+
188
+ function ContextMenuSeparator({
189
+ className,
190
+ ...props
191
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {
192
+ return (
193
+ <ContextMenuPrimitive.Separator
194
+ className={cn('-mx-1 my-1 h-px bg-border', className)}
195
+ data-slot="context-menu-separator"
196
+ {...props}
197
+ />
198
+ )
199
+ }
200
+
201
+ function ContextMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
202
+ return (
203
+ <span
204
+ className={cn('ml-auto text-muted-foreground text-xs tracking-widest', className)}
205
+ data-slot="context-menu-shortcut"
206
+ {...props}
207
+ />
208
+ )
209
+ }
210
+
211
+ export {
212
+ ContextMenu,
213
+ ContextMenuTrigger,
214
+ ContextMenuContent,
215
+ ContextMenuItem,
216
+ ContextMenuCheckboxItem,
217
+ ContextMenuRadioItem,
218
+ ContextMenuLabel,
219
+ ContextMenuSeparator,
220
+ ContextMenuShortcut,
221
+ ContextMenuGroup,
222
+ ContextMenuPortal,
223
+ ContextMenuSub,
224
+ ContextMenuSubContent,
225
+ ContextMenuSubTrigger,
226
+ ContextMenuRadioGroup,
227
+ }
@@ -0,0 +1,129 @@
1
+ 'use client'
2
+
3
+ import * as DialogPrimitive from '@radix-ui/react-dialog'
4
+ import { XIcon } from 'lucide-react'
5
+ import type * as React from 'react'
6
+
7
+ import { cn } from '../../../lib/utils'
8
+
9
+ function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
10
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
11
+ }
12
+
13
+ function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
14
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
15
+ }
16
+
17
+ function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
18
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
19
+ }
20
+
21
+ function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
22
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
23
+ }
24
+
25
+ function DialogOverlay({
26
+ className,
27
+ ...props
28
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
29
+ return (
30
+ <DialogPrimitive.Overlay
31
+ className={cn(
32
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=open]:animate-in',
33
+ className,
34
+ )}
35
+ data-slot="dialog-overlay"
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ function DialogContent({
42
+ className,
43
+ children,
44
+ showCloseButton = true,
45
+ ...props
46
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
47
+ showCloseButton?: boolean
48
+ }) {
49
+ return (
50
+ <DialogPortal data-slot="dialog-portal">
51
+ <DialogOverlay />
52
+ <DialogPrimitive.Content
53
+ className={cn(
54
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:max-w-lg',
55
+ className,
56
+ )}
57
+ data-slot="dialog-content"
58
+ {...props}
59
+ >
60
+ {children}
61
+ {showCloseButton && (
62
+ <DialogPrimitive.Close
63
+ className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0"
64
+ data-slot="dialog-close"
65
+ >
66
+ <XIcon />
67
+ <span className="sr-only">Close</span>
68
+ </DialogPrimitive.Close>
69
+ )}
70
+ </DialogPrimitive.Content>
71
+ </DialogPortal>
72
+ )
73
+ }
74
+
75
+ function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
76
+ return (
77
+ <div
78
+ className={cn('flex flex-col gap-2 text-center sm:text-left', className)}
79
+ data-slot="dialog-header"
80
+ {...props}
81
+ />
82
+ )
83
+ }
84
+
85
+ function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
86
+ return (
87
+ <div
88
+ className={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
89
+ data-slot="dialog-footer"
90
+ {...props}
91
+ />
92
+ )
93
+ }
94
+
95
+ function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
96
+ return (
97
+ <DialogPrimitive.Title
98
+ className={cn('font-semibold text-lg leading-none', className)}
99
+ data-slot="dialog-title"
100
+ {...props}
101
+ />
102
+ )
103
+ }
104
+
105
+ function DialogDescription({
106
+ className,
107
+ ...props
108
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
109
+ return (
110
+ <DialogPrimitive.Description
111
+ className={cn('text-muted-foreground text-sm', className)}
112
+ data-slot="dialog-description"
113
+ {...props}
114
+ />
115
+ )
116
+ }
117
+
118
+ export {
119
+ Dialog,
120
+ DialogClose,
121
+ DialogContent,
122
+ DialogDescription,
123
+ DialogFooter,
124
+ DialogHeader,
125
+ DialogOverlay,
126
+ DialogPortal,
127
+ DialogTitle,
128
+ DialogTrigger,
129
+ }
@@ -0,0 +1,228 @@
1
+ 'use client'
2
+
3
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
4
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react'
5
+ import type * as React from 'react'
6
+
7
+ import { cn } from '../../../lib/utils'
8
+
9
+ function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
10
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
11
+ }
12
+
13
+ function DropdownMenuPortal({
14
+ ...props
15
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
16
+ return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
17
+ }
18
+
19
+ function DropdownMenuTrigger({
20
+ ...props
21
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
22
+ return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
23
+ }
24
+
25
+ function DropdownMenuContent({
26
+ className,
27
+ sideOffset = 4,
28
+ ...props
29
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
30
+ return (
31
+ <DropdownMenuPrimitive.Portal>
32
+ <DropdownMenuPrimitive.Content
33
+ className={cn(
34
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in',
35
+ className,
36
+ )}
37
+ data-slot="dropdown-menu-content"
38
+ sideOffset={sideOffset}
39
+ {...props}
40
+ />
41
+ </DropdownMenuPrimitive.Portal>
42
+ )
43
+ }
44
+
45
+ function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
46
+ return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
47
+ }
48
+
49
+ function DropdownMenuItem({
50
+ className,
51
+ inset,
52
+ variant = 'default',
53
+ ...props
54
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
55
+ inset?: boolean
56
+ variant?: 'default' | 'destructive'
57
+ }) {
58
+ return (
59
+ <DropdownMenuPrimitive.Item
60
+ className={cn(
61
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 font-barlow text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-inset:pl-8 data-[variant=destructive]:text-destructive data-disabled:opacity-50 data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 data-[variant=destructive]:*:[svg]:text-destructive!",
62
+ className,
63
+ )}
64
+ data-inset={inset}
65
+ data-slot="dropdown-menu-item"
66
+ data-variant={variant}
67
+ {...props}
68
+ />
69
+ )
70
+ }
71
+
72
+ function DropdownMenuCheckboxItem({
73
+ className,
74
+ children,
75
+ checked,
76
+ ...props
77
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
78
+ return (
79
+ <DropdownMenuPrimitive.CheckboxItem
80
+ checked={checked}
81
+ className={cn(
82
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 font-barlow text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
83
+ className,
84
+ )}
85
+ data-slot="dropdown-menu-checkbox-item"
86
+ {...props}
87
+ >
88
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
89
+ <DropdownMenuPrimitive.ItemIndicator>
90
+ <CheckIcon className="size-4" />
91
+ </DropdownMenuPrimitive.ItemIndicator>
92
+ </span>
93
+ {children}
94
+ </DropdownMenuPrimitive.CheckboxItem>
95
+ )
96
+ }
97
+
98
+ function DropdownMenuRadioGroup({
99
+ ...props
100
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
101
+ return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />
102
+ }
103
+
104
+ function DropdownMenuRadioItem({
105
+ className,
106
+ children,
107
+ ...props
108
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
109
+ return (
110
+ <DropdownMenuPrimitive.RadioItem
111
+ className={cn(
112
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 font-barlow text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
113
+ className,
114
+ )}
115
+ data-slot="dropdown-menu-radio-item"
116
+ {...props}
117
+ >
118
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
119
+ <DropdownMenuPrimitive.ItemIndicator>
120
+ <CircleIcon className="size-2 fill-current" />
121
+ </DropdownMenuPrimitive.ItemIndicator>
122
+ </span>
123
+ {children}
124
+ </DropdownMenuPrimitive.RadioItem>
125
+ )
126
+ }
127
+
128
+ function DropdownMenuLabel({
129
+ className,
130
+ inset,
131
+ ...props
132
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
133
+ inset?: boolean
134
+ }) {
135
+ return (
136
+ <DropdownMenuPrimitive.Label
137
+ className={cn('px-2 py-1.5 font-barlow font-medium text-sm data-inset:pl-8', className)}
138
+ data-inset={inset}
139
+ data-slot="dropdown-menu-label"
140
+ {...props}
141
+ />
142
+ )
143
+ }
144
+
145
+ function DropdownMenuSeparator({
146
+ className,
147
+ ...props
148
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
149
+ return (
150
+ <DropdownMenuPrimitive.Separator
151
+ className={cn('-mx-1 my-1 h-px bg-border', className)}
152
+ data-slot="dropdown-menu-separator"
153
+ {...props}
154
+ />
155
+ )
156
+ }
157
+
158
+ function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
159
+ return (
160
+ <span
161
+ className={cn('ml-auto text-muted-foreground text-xs tracking-widest', className)}
162
+ data-slot="dropdown-menu-shortcut"
163
+ {...props}
164
+ />
165
+ )
166
+ }
167
+
168
+ function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
169
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
170
+ }
171
+
172
+ function DropdownMenuSubTrigger({
173
+ className,
174
+ inset,
175
+ children,
176
+ ...props
177
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
178
+ inset?: boolean
179
+ }) {
180
+ return (
181
+ <DropdownMenuPrimitive.SubTrigger
182
+ className={cn(
183
+ "flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 font-barlow text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-inset:pl-8 data-[state=open]:text-accent-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0",
184
+ className,
185
+ )}
186
+ data-inset={inset}
187
+ data-slot="dropdown-menu-sub-trigger"
188
+ {...props}
189
+ >
190
+ {children}
191
+ <ChevronRightIcon className="ml-auto size-4" />
192
+ </DropdownMenuPrimitive.SubTrigger>
193
+ )
194
+ }
195
+
196
+ function DropdownMenuSubContent({
197
+ className,
198
+ ...props
199
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
200
+ return (
201
+ <DropdownMenuPrimitive.SubContent
202
+ className={cn(
203
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=closed]:animate-out data-[state=open]:animate-in',
204
+ className,
205
+ )}
206
+ data-slot="dropdown-menu-sub-content"
207
+ {...props}
208
+ />
209
+ )
210
+ }
211
+
212
+ export {
213
+ DropdownMenu,
214
+ DropdownMenuPortal,
215
+ DropdownMenuTrigger,
216
+ DropdownMenuContent,
217
+ DropdownMenuGroup,
218
+ DropdownMenuLabel,
219
+ DropdownMenuItem,
220
+ DropdownMenuCheckboxItem,
221
+ DropdownMenuRadioGroup,
222
+ DropdownMenuRadioItem,
223
+ DropdownMenuSeparator,
224
+ DropdownMenuShortcut,
225
+ DropdownMenuSub,
226
+ DropdownMenuSubTrigger,
227
+ DropdownMenuSubContent,
228
+ }
@@ -0,0 +1,52 @@
1
+ 'use client'
2
+
3
+ import React, { Component, type ErrorInfo, type ReactNode } from 'react'
4
+
5
+ interface Props {
6
+ children?: ReactNode
7
+ fallback?: ReactNode
8
+ }
9
+
10
+ interface State {
11
+ hasError: boolean
12
+ error: Error | null
13
+ }
14
+
15
+ export class ErrorBoundary extends Component<Props, State> {
16
+ public state: State = {
17
+ hasError: false,
18
+ error: null,
19
+ }
20
+
21
+ public static getDerivedStateFromError(error: Error): State {
22
+ return { hasError: true, error }
23
+ }
24
+
25
+ public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
26
+ console.error('Uncaught error:', error, errorInfo)
27
+ }
28
+
29
+ public render() {
30
+ if (this.state.hasError) {
31
+ if (this.props.fallback) {
32
+ return this.props.fallback
33
+ }
34
+ return (
35
+ <div className="flex h-screen w-screen flex-col items-center justify-center bg-[#1b1c1f] p-4 text-white">
36
+ <h2 className="mb-4 font-bold text-red-400 text-xl">Something went wrong</h2>
37
+ <pre className="max-w-full overflow-auto rounded bg-black/30 p-4 text-gray-300 text-sm">
38
+ {this.state.error?.message}
39
+ </pre>
40
+ <button
41
+ className="mt-4 rounded bg-blue-600 px-4 py-2 hover:bg-blue-700"
42
+ onClick={() => this.setState({ hasError: false, error: null })}
43
+ >
44
+ Try again
45
+ </button>
46
+ </div>
47
+ )
48
+ }
49
+
50
+ return this.props.children
51
+ }
52
+ }
@@ -0,0 +1,21 @@
1
+ import type * as React from 'react'
2
+
3
+ import { cn } from '../../../lib/utils'
4
+
5
+ function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
6
+ return (
7
+ <input
8
+ className={cn(
9
+ 'h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs outline-none transition-[color,box-shadow] selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:font-medium file:text-foreground file:text-sm placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30',
10
+ 'focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50',
11
+ 'aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40',
12
+ className,
13
+ )}
14
+ data-slot="input"
15
+ type={type}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
21
+ export { Input }