@pixel-point/toolcraft 0.0.2

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 (257) hide show
  1. package/LICENSE.md +98 -0
  2. package/README.md +41 -0
  3. package/bin/create-toolcraft-app.mjs +8 -0
  4. package/bin/toolcraft.mjs +8 -0
  5. package/package.json +24 -0
  6. package/scripts/prepare-pack.mjs +29 -0
  7. package/src/cli.mjs +392 -0
  8. package/src/cli.test.mjs +284 -0
  9. package/src/copy-recursive.mjs +86 -0
  10. package/src/generate.mjs +212 -0
  11. package/src/generate.test.mjs +322 -0
  12. package/src/import-map.mjs +14 -0
  13. package/src/package-json.mjs +80 -0
  14. package/src/package-json.test.mjs +67 -0
  15. package/src/rewrite-imports.mjs +85 -0
  16. package/src/rewrite-imports.test.mjs +58 -0
  17. package/templates/runtime/contracts/component-contracts.test.ts +1165 -0
  18. package/templates/runtime/contracts/component-contracts.ts +1340 -0
  19. package/templates/runtime/contracts/decision-contracts.test.ts +206 -0
  20. package/templates/runtime/contracts/decision-contracts.ts +283 -0
  21. package/templates/runtime/contracts/index.test.ts +14 -0
  22. package/templates/runtime/contracts/index.ts +3 -0
  23. package/templates/runtime/contracts/types.ts +56 -0
  24. package/templates/runtime/export/export.test.ts +203 -0
  25. package/templates/runtime/export/export.ts +132 -0
  26. package/templates/runtime/export/index.ts +1 -0
  27. package/templates/runtime/index.ts +14 -0
  28. package/templates/runtime/react/canvas-shell.test.tsx +424 -0
  29. package/templates/runtime/react/canvas-shell.tsx +408 -0
  30. package/templates/runtime/react/control-renderers.ts +31 -0
  31. package/templates/runtime/react/controls-panel.test.tsx +3736 -0
  32. package/templates/runtime/react/controls-panel.tsx +2327 -0
  33. package/templates/runtime/react/curve-geometry.test.ts +70 -0
  34. package/templates/runtime/react/index.ts +15 -0
  35. package/templates/runtime/react/layer-tree.ts +96 -0
  36. package/templates/runtime/react/layers-panel.test.tsx +487 -0
  37. package/templates/runtime/react/layers-panel.tsx +1348 -0
  38. package/templates/runtime/react/media-file.ts +82 -0
  39. package/templates/runtime/react/panel-host-config.ts +80 -0
  40. package/templates/runtime/react/panel-host-geometry.test.ts +66 -0
  41. package/templates/runtime/react/panel-host-geometry.ts +109 -0
  42. package/templates/runtime/react/panel-host-types.ts +74 -0
  43. package/templates/runtime/react/panel-host.test.tsx +102 -0
  44. package/templates/runtime/react/panel-host.tsx +353 -0
  45. package/templates/runtime/react/runtime-public-api.test.tsx +132 -0
  46. package/templates/runtime/react/settings-transfer.test.ts +150 -0
  47. package/templates/runtime/react/settings-transfer.ts +279 -0
  48. package/templates/runtime/react/storage-key-migration.ts +48 -0
  49. package/templates/runtime/react/theme-runtime.tsx +177 -0
  50. package/templates/runtime/react/timeline-panel.test.tsx +668 -0
  51. package/templates/runtime/react/timeline-panel.tsx +2953 -0
  52. package/templates/runtime/react/toolbar-panel.test.tsx +212 -0
  53. package/templates/runtime/react/toolbar-panel.tsx +205 -0
  54. package/templates/runtime/react/toolcraft-app.integration.test.tsx +350 -0
  55. package/templates/runtime/react/toolcraft-app.test.tsx +339 -0
  56. package/templates/runtime/react/toolcraft-app.tsx +81 -0
  57. package/templates/runtime/react/toolcraft-root.test.tsx +347 -0
  58. package/templates/runtime/react/toolcraft-root.tsx +203 -0
  59. package/templates/runtime/react/use-toolcraft.ts +41 -0
  60. package/templates/runtime/schema/define-toolcraft.test.ts +1524 -0
  61. package/templates/runtime/schema/define-toolcraft.ts +1442 -0
  62. package/templates/runtime/schema/keyframe-capability.test.ts +90 -0
  63. package/templates/runtime/schema/keyframe-capability.ts +51 -0
  64. package/templates/runtime/schema/runtime-targets.ts +40 -0
  65. package/templates/runtime/schema/types.ts +370 -0
  66. package/templates/runtime/state/canvas-zoom.ts +8 -0
  67. package/templates/runtime/state/create-template-state.test.ts +242 -0
  68. package/templates/runtime/state/create-template-state.ts +95 -0
  69. package/templates/runtime/state/keyframe-evaluation.test.ts +141 -0
  70. package/templates/runtime/state/keyframe-evaluation.ts +203 -0
  71. package/templates/runtime/state/persistence.test.ts +217 -0
  72. package/templates/runtime/state/persistence.ts +511 -0
  73. package/templates/runtime/state/reducer.test.ts +937 -0
  74. package/templates/runtime/state/reducer.ts +1212 -0
  75. package/templates/runtime/state/timeline-readiness.ts +43 -0
  76. package/templates/runtime/state/types.ts +242 -0
  77. package/templates/runtime/styles.css +125 -0
  78. package/templates/runtime/testing/performance.test.ts +1058 -0
  79. package/templates/runtime/testing/performance.ts +1078 -0
  80. package/templates/starter/AGENTS.md +186 -0
  81. package/templates/starter/LICENSE.md +98 -0
  82. package/templates/starter/NOTICE.md +8 -0
  83. package/templates/starter/docs/toolcraft/README.md +41 -0
  84. package/templates/starter/docs/toolcraft/acceptance-testing.md +205 -0
  85. package/templates/starter/docs/toolcraft/agent-worklog.md +81 -0
  86. package/templates/starter/docs/toolcraft/assembly-workflow.md +206 -0
  87. package/templates/starter/docs/toolcraft/component-rules.md +299 -0
  88. package/templates/starter/docs/toolcraft/custom-controls.md +71 -0
  89. package/templates/starter/docs/toolcraft/decision-contract.md +71 -0
  90. package/templates/starter/docs/toolcraft/performance.md +112 -0
  91. package/templates/starter/docs/toolcraft/renderer-technique.md +48 -0
  92. package/templates/starter/docs/toolcraft/schema-reference.md +265 -0
  93. package/templates/starter/docs/toolcraft/workflow.md +87 -0
  94. package/templates/starter/e2e/app-browser-acceptance.spec.ts +785 -0
  95. package/templates/starter/e2e/app-controls.spec.ts +41 -0
  96. package/templates/starter/e2e/app-performance.spec.ts +326 -0
  97. package/templates/starter/e2e/canvas-handle-helpers.ts +244 -0
  98. package/templates/starter/e2e/performance-helpers.ts +612 -0
  99. package/templates/starter/e2e/product-observable-helpers.ts +170 -0
  100. package/templates/starter/index.html +12 -0
  101. package/templates/starter/package.json +52 -0
  102. package/templates/starter/playwright.config.ts +43 -0
  103. package/templates/starter/scripts/check-ai-skills.mjs +95 -0
  104. package/templates/starter/scripts/check-toolcraft-docs.mjs +159 -0
  105. package/templates/starter/scripts/check-toolcraft-integrity.mjs +232 -0
  106. package/templates/starter/scripts/run-vite-on-free-port.mjs +48 -0
  107. package/templates/starter/scripts/toolcraft-port.mjs +54 -0
  108. package/templates/starter/scripts/toolcraft-port.test.mjs +73 -0
  109. package/templates/starter/src/app/starter-acceptance.test.ts +5959 -0
  110. package/templates/starter/src/app/starter-acceptance.ts +2646 -0
  111. package/templates/starter/src/app/starter-performance.test.ts +1390 -0
  112. package/templates/starter/src/app/starter-performance.ts +12 -0
  113. package/templates/starter/src/app/starter-schema.test.ts +70 -0
  114. package/templates/starter/src/app/starter-schema.ts +15 -0
  115. package/templates/starter/src/main.tsx +18 -0
  116. package/templates/starter/src/router.tsx +16 -0
  117. package/templates/starter/src/routes/index.tsx +7 -0
  118. package/templates/starter/src/routes/root.tsx +19 -0
  119. package/templates/starter/src/styles.css +120 -0
  120. package/templates/starter/tsconfig.json +11 -0
  121. package/templates/starter/vite.config.ts +13 -0
  122. package/templates/ui/components/composites/accordion.tsx +73 -0
  123. package/templates/ui/components/composites/alert-dialog.tsx +190 -0
  124. package/templates/ui/components/composites/alert.tsx +74 -0
  125. package/templates/ui/components/composites/aspect-ratio.tsx +22 -0
  126. package/templates/ui/components/composites/avatar.tsx +98 -0
  127. package/templates/ui/components/composites/badge.tsx +69 -0
  128. package/templates/ui/components/composites/breadcrumb.tsx +106 -0
  129. package/templates/ui/components/composites/card.tsx +91 -0
  130. package/templates/ui/components/composites/combobox.tsx +486 -0
  131. package/templates/ui/components/composites/command.tsx +296 -0
  132. package/templates/ui/components/composites/context-menu.tsx +247 -0
  133. package/templates/ui/components/composites/dialog.tsx +282 -0
  134. package/templates/ui/components/composites/dropdown-menu.tsx +299 -0
  135. package/templates/ui/components/composites/empty.tsx +110 -0
  136. package/templates/ui/components/composites/hover-card.tsx +44 -0
  137. package/templates/ui/components/composites/index.ts +30 -0
  138. package/templates/ui/components/composites/menubar.tsx +214 -0
  139. package/templates/ui/components/composites/navigation-menu.tsx +167 -0
  140. package/templates/ui/components/composites/pagination.tsx +131 -0
  141. package/templates/ui/components/composites/progress.tsx +72 -0
  142. package/templates/ui/components/composites/radio-group.tsx +84 -0
  143. package/templates/ui/components/composites/resizable.tsx +42 -0
  144. package/templates/ui/components/composites/sheet.tsx +153 -0
  145. package/templates/ui/components/composites/sidebar-structural.tsx +310 -0
  146. package/templates/ui/components/composites/sidebar.tsx +431 -0
  147. package/templates/ui/components/composites/sonner.tsx +35 -0
  148. package/templates/ui/components/composites/spinner.tsx +43 -0
  149. package/templates/ui/components/composites/table.tsx +108 -0
  150. package/templates/ui/components/composites/tabs.tsx +83 -0
  151. package/templates/ui/components/control-layout/index.tsx +437 -0
  152. package/templates/ui/components/controls/actions/actions-control.tsx +139 -0
  153. package/templates/ui/components/controls/actions/index.ts +9 -0
  154. package/templates/ui/components/controls/anchor-grid/anchor-grid-control.tsx +107 -0
  155. package/templates/ui/components/controls/anchor-grid/index.ts +4 -0
  156. package/templates/ui/components/controls/boolean/boolean-controls.tsx +79 -0
  157. package/templates/ui/components/controls/boolean/index.ts +4 -0
  158. package/templates/ui/components/controls/channel-mixer/channel-mixer-control.tsx +95 -0
  159. package/templates/ui/components/controls/channel-mixer/index.ts +4 -0
  160. package/templates/ui/components/controls/channel-tabs/channel-tabs.tsx +42 -0
  161. package/templates/ui/components/controls/channel-tabs/index.ts +6 -0
  162. package/templates/ui/components/controls/code-textarea/code-textarea-control.tsx +90 -0
  163. package/templates/ui/components/controls/code-textarea/index.ts +4 -0
  164. package/templates/ui/components/controls/color/color-control.tsx +571 -0
  165. package/templates/ui/components/controls/color/color-picker-popover.tsx +104 -0
  166. package/templates/ui/components/controls/color/index.ts +41 -0
  167. package/templates/ui/components/controls/color/palette-control-data.ts +436 -0
  168. package/templates/ui/components/controls/color/palette-control.tsx +535 -0
  169. package/templates/ui/components/controls/color/style-guide-color-picker-channel-utils.ts +162 -0
  170. package/templates/ui/components/controls/color/style-guide-color-picker-interactions.ts +190 -0
  171. package/templates/ui/components/controls/color/style-guide-color-picker-logic.ts +485 -0
  172. package/templates/ui/components/controls/color/style-guide-color-picker-parts.tsx +710 -0
  173. package/templates/ui/components/controls/color/style-guide-color-picker.tsx +503 -0
  174. package/templates/ui/components/controls/control-types.ts +43 -0
  175. package/templates/ui/components/controls/curves/curve-geometry.ts +355 -0
  176. package/templates/ui/components/controls/curves/curve-graph.tsx +390 -0
  177. package/templates/ui/components/controls/curves/curves-control.tsx +445 -0
  178. package/templates/ui/components/controls/curves/index.ts +6 -0
  179. package/templates/ui/components/controls/file-drop/file-drop-control.tsx +191 -0
  180. package/templates/ui/components/controls/file-drop/index.ts +5 -0
  181. package/templates/ui/components/controls/font-picker/font-catalog.json +15360 -0
  182. package/templates/ui/components/controls/font-picker/font-catalog.ts +116 -0
  183. package/templates/ui/components/controls/font-picker/font-picker-control.tsx +1202 -0
  184. package/templates/ui/components/controls/font-picker/font-preview-loader.ts +336 -0
  185. package/templates/ui/components/controls/font-picker/index.ts +24 -0
  186. package/templates/ui/components/controls/font-picker/use-hover-intent.ts +46 -0
  187. package/templates/ui/components/controls/gradient/gradient-control-utils.ts +190 -0
  188. package/templates/ui/components/controls/gradient/gradient-control.tsx +612 -0
  189. package/templates/ui/components/controls/gradient/gradient-stop-list.tsx +400 -0
  190. package/templates/ui/components/controls/gradient/gradient-toolbar.tsx +152 -0
  191. package/templates/ui/components/controls/gradient/index.ts +4 -0
  192. package/templates/ui/components/controls/image-picker/image-picker-control.tsx +139 -0
  193. package/templates/ui/components/controls/image-picker/index.ts +7 -0
  194. package/templates/ui/components/controls/index.ts +192 -0
  195. package/templates/ui/components/controls/range-input/index.ts +4 -0
  196. package/templates/ui/components/controls/range-input/range-input-control.tsx +173 -0
  197. package/templates/ui/components/controls/range-slider/index.ts +4 -0
  198. package/templates/ui/components/controls/range-slider/range-slider-control.tsx +122 -0
  199. package/templates/ui/components/controls/range-slider/range-slider-value.ts +61 -0
  200. package/templates/ui/components/controls/segmented/index.ts +8 -0
  201. package/templates/ui/components/controls/segmented/segmented-control.tsx +94 -0
  202. package/templates/ui/components/controls/select/index.ts +4 -0
  203. package/templates/ui/components/controls/select/select-control.tsx +223 -0
  204. package/templates/ui/components/controls/slider/index.ts +4 -0
  205. package/templates/ui/components/controls/slider/slider-control.tsx +150 -0
  206. package/templates/ui/components/controls/slider/slider-value.ts +56 -0
  207. package/templates/ui/components/controls/text-input/index.ts +4 -0
  208. package/templates/ui/components/controls/text-input/text-input-control.tsx +158 -0
  209. package/templates/ui/components/controls/use-measured-element-width.ts +42 -0
  210. package/templates/ui/components/controls/vector/index.ts +8 -0
  211. package/templates/ui/components/controls/vector/vector-control.tsx +401 -0
  212. package/templates/ui/components/panel/index.ts +19 -0
  213. package/templates/ui/components/panel/panel-actions.tsx +165 -0
  214. package/templates/ui/components/panel/panel-header.tsx +61 -0
  215. package/templates/ui/components/panel/panel-icon-button.tsx +96 -0
  216. package/templates/ui/components/panel/panel-section.tsx +168 -0
  217. package/templates/ui/components/panel/panel-surface.tsx +206 -0
  218. package/templates/ui/components/panel/panel.tsx +210 -0
  219. package/templates/ui/components/primitives/animated-loader.tsx +61 -0
  220. package/templates/ui/components/primitives/button-group.tsx +134 -0
  221. package/templates/ui/components/primitives/button.tsx +429 -0
  222. package/templates/ui/components/primitives/checkbox.tsx +62 -0
  223. package/templates/ui/components/primitives/editable-slider-value-label.tsx +337 -0
  224. package/templates/ui/components/primitives/field.tsx +225 -0
  225. package/templates/ui/components/primitives/index.ts +82 -0
  226. package/templates/ui/components/primitives/input-group.tsx +298 -0
  227. package/templates/ui/components/primitives/input.tsx +61 -0
  228. package/templates/ui/components/primitives/internal/button-loading.tsx +178 -0
  229. package/templates/ui/components/primitives/label.tsx +16 -0
  230. package/templates/ui/components/primitives/popover.tsx +126 -0
  231. package/templates/ui/components/primitives/portal-layer-context.tsx +33 -0
  232. package/templates/ui/components/primitives/primitive-arrow-icon.tsx +38 -0
  233. package/templates/ui/components/primitives/scroll-fade-logic.ts +441 -0
  234. package/templates/ui/components/primitives/scroll-fade-render.tsx +75 -0
  235. package/templates/ui/components/primitives/scroll-fade-types.ts +41 -0
  236. package/templates/ui/components/primitives/scroll-fade.tsx +72 -0
  237. package/templates/ui/components/primitives/select.tsx +408 -0
  238. package/templates/ui/components/primitives/selection-state.ts +31 -0
  239. package/templates/ui/components/primitives/separator.tsx +21 -0
  240. package/templates/ui/components/primitives/slider/index.ts +4 -0
  241. package/templates/ui/components/primitives/slider/slider-interaction.tsx +96 -0
  242. package/templates/ui/components/primitives/slider/slider-parts.tsx +303 -0
  243. package/templates/ui/components/primitives/slider/slider-reset.ts +152 -0
  244. package/templates/ui/components/primitives/slider/slider-value.ts +114 -0
  245. package/templates/ui/components/primitives/slider/slider.tsx +511 -0
  246. package/templates/ui/components/primitives/switch.tsx +35 -0
  247. package/templates/ui/components/primitives/textarea.tsx +49 -0
  248. package/templates/ui/components/primitives/toggle-group.tsx +114 -0
  249. package/templates/ui/components/primitives/toggle.tsx +46 -0
  250. package/templates/ui/components/primitives/tooltip.tsx +100 -0
  251. package/templates/ui/hooks/use-mobile.ts +21 -0
  252. package/templates/ui/index.ts +31 -0
  253. package/templates/ui/lib/control-outline.ts +3 -0
  254. package/templates/ui/lib/input-control-style.ts +131 -0
  255. package/templates/ui/lib/style-guide-color-utils.ts +111 -0
  256. package/templates/ui/lib/utils.ts +6 -0
  257. package/templates/ui/styles.css +291 -0
@@ -0,0 +1,282 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
5
+
6
+ import { Button } from "../primitives";
7
+ import {
8
+ PortalLayerContainerProvider,
9
+ type PortalLayerContainer,
10
+ usePortalLayerContainer,
11
+ } from "../primitives";
12
+ import { ScrollFade } from "../primitives";
13
+ import { cn } from "../../lib/utils";
14
+ import { XIcon } from "@phosphor-icons/react";
15
+
16
+ const DIALOG_SECTION_BORDER_COLOR = "color-mix(in oklab, var(--border) 5%, transparent)";
17
+
18
+ const DialogLayoutContext = React.createContext<{
19
+ sectioned: boolean;
20
+ showCloseButton: boolean;
21
+ }>({
22
+ sectioned: false,
23
+ showCloseButton: false,
24
+ });
25
+
26
+ function Dialog({ ...props }: DialogPrimitive.Root.Props) {
27
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
28
+ }
29
+
30
+ function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
31
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
32
+ }
33
+
34
+ function DialogPortal({
35
+ children,
36
+ container,
37
+ ...props
38
+ }: DialogPrimitive.Portal.Props): React.JSX.Element {
39
+ const resolvedContainer = usePortalLayerContainer(container);
40
+ const portalNodeRef = React.useRef<HTMLDivElement | null>(null);
41
+
42
+ return (
43
+ <DialogPrimitive.Portal
44
+ data-slot="dialog-portal"
45
+ container={resolvedContainer}
46
+ ref={portalNodeRef}
47
+ {...props}
48
+ >
49
+ <PortalLayerContainerProvider container={portalNodeRef}>
50
+ {children}
51
+ </PortalLayerContainerProvider>
52
+ </DialogPrimitive.Portal>
53
+ );
54
+ }
55
+
56
+ function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
57
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
58
+ }
59
+
60
+ function DialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props) {
61
+ return (
62
+ <DialogPrimitive.Backdrop
63
+ data-slot="dialog-overlay"
64
+ className={cn(
65
+ "fixed inset-0 isolate z-50 bg-black/72 duration-100 supports-backdrop-filter:backdrop-blur-sm data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
66
+ className,
67
+ )}
68
+ {...props}
69
+ />
70
+ );
71
+ }
72
+
73
+ function DialogCloseButton({
74
+ className,
75
+ }: {
76
+ className?: string;
77
+ } = {}): React.JSX.Element {
78
+ return (
79
+ <DialogPrimitive.Close
80
+ data-slot="dialog-close"
81
+ render={
82
+ <Button
83
+ className={cn("absolute top-3 right-3", className)}
84
+ size="icon-sm"
85
+ type="button"
86
+ variant="ghost"
87
+ />
88
+ }
89
+ >
90
+ <XIcon />
91
+ <span className="sr-only">Close</span>
92
+ </DialogPrimitive.Close>
93
+ );
94
+ }
95
+
96
+ function DialogContent({
97
+ className,
98
+ children,
99
+ layout = "default",
100
+ portalContainer,
101
+ size = "default",
102
+ showCloseButton = true,
103
+ style,
104
+ ...props
105
+ }: DialogPrimitive.Popup.Props & {
106
+ layout?: "default" | "sections";
107
+ portalContainer?: PortalLayerContainer;
108
+ size?: "default" | "xl" | "2xl";
109
+ showCloseButton?: boolean;
110
+ }) {
111
+ const sectioned = layout === "sections";
112
+ const resolvedStyle =
113
+ size === "xl"
114
+ ? {
115
+ ...style,
116
+ width: "min(var(--container-xl), calc(100% - 2rem))",
117
+ }
118
+ : size === "2xl"
119
+ ? {
120
+ ...style,
121
+ width: "min(calc(var(--container-xl) + 4rem), calc(100% - 2rem))",
122
+ }
123
+ : style;
124
+
125
+ return (
126
+ <DialogPortal container={portalContainer}>
127
+ <DialogOverlay />
128
+ <DialogLayoutContext.Provider value={{ sectioned, showCloseButton }}>
129
+ <DialogPrimitive.Popup
130
+ data-slot="dialog-content"
131
+ className={cn(
132
+ "floating-popup-surface fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-2xl border p-4 popup-text-xs-plus text-[color:var(--popover-foreground)] duration-100 outline-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
133
+ size === "default" && "sm:max-w-sm",
134
+ size === "xl" && "sm:max-w-xl",
135
+ sectioned && "gap-0 p-0",
136
+ className,
137
+ )}
138
+ style={resolvedStyle}
139
+ {...props}
140
+ >
141
+ {children}
142
+ {showCloseButton ? (
143
+ <DialogCloseButton className={sectioned ? "top-3 right-3" : undefined} />
144
+ ) : null}
145
+ </DialogPrimitive.Popup>
146
+ </DialogLayoutContext.Provider>
147
+ </DialogPortal>
148
+ );
149
+ }
150
+
151
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
152
+ const { sectioned, showCloseButton } = React.useContext(DialogLayoutContext);
153
+
154
+ return (
155
+ <div
156
+ data-slot="dialog-header"
157
+ className={cn(
158
+ "flex flex-col gap-1.5 text-left",
159
+ sectioned && "px-4 pt-4",
160
+ showCloseButton && "pr-12",
161
+ className,
162
+ )}
163
+ {...props}
164
+ />
165
+ );
166
+ }
167
+
168
+ function DialogBody({ className, ...props }: React.ComponentProps<"div">) {
169
+ const { sectioned } = React.useContext(DialogLayoutContext);
170
+
171
+ return (
172
+ <div
173
+ data-slot="dialog-body"
174
+ className={cn("flex flex-col gap-4", sectioned && "px-4 pt-3 pb-6", className)}
175
+ {...props}
176
+ />
177
+ );
178
+ }
179
+
180
+ function DialogFooter({
181
+ className,
182
+ showCloseButton = false,
183
+ justify = "end",
184
+ children,
185
+ ...props
186
+ }: React.ComponentProps<"div"> & {
187
+ justify?: "between" | "end" | "start";
188
+ showCloseButton?: boolean;
189
+ }) {
190
+ const { sectioned } = React.useContext(DialogLayoutContext);
191
+
192
+ return (
193
+ <div
194
+ data-slot="dialog-footer"
195
+ className={cn(
196
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
197
+ justify === "start" && "sm:justify-start",
198
+ justify === "between" && "sm:justify-between",
199
+ sectioned && "border-t px-4 py-4",
200
+ className,
201
+ )}
202
+ style={sectioned ? { borderColor: DIALOG_SECTION_BORDER_COLOR } : undefined}
203
+ {...props}
204
+ >
205
+ {children}
206
+ {showCloseButton && (
207
+ <DialogPrimitive.Close render={<Button type="button" variant="outline" />}>
208
+ Close
209
+ </DialogPrimitive.Close>
210
+ )}
211
+ </div>
212
+ );
213
+ }
214
+
215
+ function getDialogTitleText(children: React.ReactNode): string | null {
216
+ const childNodes = React.Children.toArray(children);
217
+ if (childNodes.length === 0) {
218
+ return null;
219
+ }
220
+
221
+ if (childNodes.some((child) => typeof child !== "string" && typeof child !== "number")) {
222
+ return null;
223
+ }
224
+
225
+ const text = childNodes.join("");
226
+ return text.length > 0 ? text : null;
227
+ }
228
+
229
+ function DialogTitle({ children, className, title, ...props }: DialogPrimitive.Title.Props) {
230
+ const textContent = getDialogTitleText(children);
231
+ const resolvedTitle = title ?? textContent ?? undefined;
232
+
233
+ return (
234
+ <DialogPrimitive.Title
235
+ data-slot="dialog-title"
236
+ className={cn("min-w-0 text-sm leading-tight font-semibold tracking-tight", className)}
237
+ title={resolvedTitle}
238
+ {...props}
239
+ >
240
+ {textContent ? (
241
+ <ScrollFade
242
+ className="no-scrollbar min-w-0"
243
+ containerClassName="min-w-0"
244
+ preset="compact"
245
+ side="right"
246
+ watch={[textContent]}
247
+ >
248
+ <span className="block min-w-max whitespace-nowrap">{textContent}</span>
249
+ </ScrollFade>
250
+ ) : (
251
+ children
252
+ )}
253
+ </DialogPrimitive.Title>
254
+ );
255
+ }
256
+
257
+ function DialogDescription({ className, ...props }: DialogPrimitive.Description.Props) {
258
+ return (
259
+ <DialogPrimitive.Description
260
+ data-slot="dialog-description"
261
+ className={cn(
262
+ "popup-text-xs-plus leading-relaxed text-balance text-[color:var(--muted-foreground)] md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-[color:var(--foreground)]",
263
+ className,
264
+ )}
265
+ {...props}
266
+ />
267
+ );
268
+ }
269
+
270
+ export {
271
+ Dialog,
272
+ DialogBody,
273
+ DialogClose,
274
+ DialogContent,
275
+ DialogDescription,
276
+ DialogFooter,
277
+ DialogHeader,
278
+ DialogOverlay,
279
+ DialogPortal,
280
+ DialogTitle,
281
+ DialogTrigger,
282
+ };
@@ -0,0 +1,299 @@
1
+ import * as React from "react";
2
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu";
3
+
4
+ import { cn } from "../../lib/utils";
5
+ import { PrimitiveArrowIcon } from "../primitives";
6
+ import { CheckIcon } from "@phosphor-icons/react";
7
+
8
+ function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
9
+ return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
10
+ }
11
+
12
+ function DropdownMenuPortal({
13
+ ...props
14
+ }: MenuPrimitive.Portal.Props): React.JSX.Element {
15
+ return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
16
+ }
17
+
18
+ function DropdownMenuTrigger({
19
+ className,
20
+ ...props
21
+ }: MenuPrimitive.Trigger.Props) {
22
+ return (
23
+ <MenuPrimitive.Trigger
24
+ data-slot="dropdown-menu-trigger"
25
+ className={cn(
26
+ "[&:not(:focus):not([aria-expanded=true]):not([data-open]):not([data-popup-open]):not([data-state=open]):hover]:!border-[color:color-mix(in_oklab,var(--border)_20%,transparent)]",
27
+ className,
28
+ )}
29
+ {...props}
30
+ />
31
+ );
32
+ }
33
+
34
+ function DropdownMenuContent({
35
+ align = "start",
36
+ alignOffset = 0,
37
+ side = "bottom",
38
+ sideOffset = 4,
39
+ className,
40
+ ...props
41
+ }: MenuPrimitive.Popup.Props &
42
+ Pick<
43
+ MenuPrimitive.Positioner.Props,
44
+ "align" | "alignOffset" | "side" | "sideOffset"
45
+ >) {
46
+ return (
47
+ <MenuPrimitive.Portal>
48
+ <MenuPrimitive.Positioner
49
+ className="isolate z-50 outline-none"
50
+ align={align}
51
+ alignOffset={alignOffset}
52
+ side={side}
53
+ sideOffset={sideOffset}
54
+ >
55
+ <MenuPrimitive.Popup
56
+ data-slot="dropdown-menu-content"
57
+ className={cn(
58
+ "floating-popup-surface z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto whitespace-nowrap rounded-lg border p-1 popup-text-xs-plus text-[color:var(--popover-foreground)] duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95",
59
+ className,
60
+ )}
61
+ {...props}
62
+ />
63
+ </MenuPrimitive.Positioner>
64
+ </MenuPrimitive.Portal>
65
+ );
66
+ }
67
+
68
+ function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
69
+ return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
70
+ }
71
+
72
+ function DropdownMenuLabel({
73
+ className,
74
+ inset,
75
+ ...props
76
+ }: MenuPrimitive.GroupLabel.Props & {
77
+ inset?: boolean;
78
+ }) {
79
+ return (
80
+ <MenuPrimitive.GroupLabel
81
+ data-slot="dropdown-menu-label"
82
+ data-inset={inset}
83
+ className={cn(
84
+ "px-2 py-1.5 popup-text-xs-plus text-[color:color-mix(in_oklab,var(--foreground)_60%,transparent)] data-inset:pl-7.5",
85
+ className,
86
+ )}
87
+ {...props}
88
+ />
89
+ );
90
+ }
91
+
92
+ function DropdownMenuItem({
93
+ className,
94
+ inset,
95
+ variant = "default",
96
+ ...props
97
+ }: MenuPrimitive.Item.Props & {
98
+ inset?: boolean;
99
+ variant?: "default" | "destructive";
100
+ }) {
101
+ return (
102
+ <MenuPrimitive.Item
103
+ data-slot="dropdown-menu-item"
104
+ data-inset={inset}
105
+ data-variant={variant}
106
+ className={cn(
107
+ "group/dropdown-menu-item relative flex min-h-7 cursor-pointer items-center gap-2 rounded-md px-2 py-1 popup-text-xs-plus leading-normal tracking-tight font-medium outline-hidden select-none hover:bg-[color:color-mix(in_oklab,var(--foreground)_5%,transparent)] focus:bg-[color:color-mix(in_oklab,var(--foreground)_5%,transparent)] focus:text-[color:var(--accent-foreground)] not-data-[variant=destructive]:focus:**:text-[color:var(--accent-foreground)] data-inset:pl-7.5 data-[variant=destructive]:text-[color:var(--destructive)] data-[variant=destructive]:hover:bg-[color:color-mix(in_oklab,var(--destructive)_10%,transparent)] data-[variant=destructive]:focus:bg-[color:color-mix(in_oklab,var(--destructive)_10%,transparent)] data-[variant=destructive]:focus:text-[color:var(--destructive)] data-[variant=destructive]:[&_[data-slot=dropdown-menu-shortcut]]:text-[color:var(--destructive)] data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 data-[variant=destructive]:*:[svg]:text-[color:var(--destructive)]",
108
+ className,
109
+ )}
110
+ {...props}
111
+ />
112
+ );
113
+ }
114
+
115
+ function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
116
+ return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />;
117
+ }
118
+
119
+ function DropdownMenuSubTrigger({
120
+ className,
121
+ inset,
122
+ children,
123
+ ...props
124
+ }: MenuPrimitive.SubmenuTrigger.Props & {
125
+ inset?: boolean;
126
+ }) {
127
+ return (
128
+ <MenuPrimitive.SubmenuTrigger
129
+ data-slot="dropdown-menu-sub-trigger"
130
+ data-inset={inset}
131
+ className={cn(
132
+ "flex min-h-7 cursor-pointer items-center gap-2 rounded-md px-2 py-1 popup-text-xs-plus leading-normal tracking-tight font-medium outline-hidden select-none hover:bg-[color:color-mix(in_oklab,var(--foreground)_5%,transparent)] focus:bg-[color:color-mix(in_oklab,var(--foreground)_5%,transparent)] focus:text-[color:var(--accent-foreground)] not-data-[variant=destructive]:focus:**:text-[color:var(--accent-foreground)] data-inset:pl-7.5 data-popup-open:bg-[color:color-mix(in_oklab,var(--foreground)_5%,transparent)] data-popup-open:text-[color:var(--accent-foreground)] data-open:bg-[color:color-mix(in_oklab,var(--foreground)_5%,transparent)] data-open:text-[color:var(--accent-foreground)] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
133
+ className,
134
+ )}
135
+ {...props}
136
+ >
137
+ {children}
138
+ <PrimitiveArrowIcon className="ml-auto" direction="right" />
139
+ </MenuPrimitive.SubmenuTrigger>
140
+ );
141
+ }
142
+
143
+ function DropdownMenuSubContent({
144
+ align = "start",
145
+ alignOffset = -3,
146
+ side = "right",
147
+ sideOffset = 0,
148
+ className,
149
+ ...props
150
+ }: React.ComponentProps<typeof DropdownMenuContent>) {
151
+ return (
152
+ <DropdownMenuContent
153
+ data-slot="dropdown-menu-sub-content"
154
+ className={cn("w-auto min-w-32", className)}
155
+ align={align}
156
+ alignOffset={alignOffset}
157
+ side={side}
158
+ sideOffset={sideOffset}
159
+ {...props}
160
+ />
161
+ );
162
+ }
163
+
164
+ function DropdownMenuCheckboxItem({
165
+ className,
166
+ children,
167
+ checked,
168
+ inset,
169
+ ...props
170
+ }: MenuPrimitive.CheckboxItem.Props & {
171
+ inset?: boolean;
172
+ }) {
173
+ return (
174
+ <MenuPrimitive.CheckboxItem
175
+ data-slot="dropdown-menu-checkbox-item"
176
+ data-inset={inset}
177
+ className={cn(
178
+ "relative flex min-h-7 cursor-pointer items-center gap-2 rounded-md py-1.5 pr-8 pl-2 popup-text-xs-plus leading-normal tracking-tight font-medium outline-hidden select-none hover:bg-[color:color-mix(in_oklab,var(--foreground)_5%,transparent)] focus:bg-[color:color-mix(in_oklab,var(--foreground)_5%,transparent)] focus:text-[color:var(--accent-foreground)] focus:**:text-[color:var(--accent-foreground)] data-inset:pl-7.5 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
179
+ className,
180
+ )}
181
+ checked={checked}
182
+ {...props}
183
+ >
184
+ <span
185
+ className="pointer-events-none absolute right-2 flex items-center justify-center"
186
+ data-slot="dropdown-menu-checkbox-item-indicator"
187
+ >
188
+ <MenuPrimitive.CheckboxItemIndicator>
189
+ <CheckIcon />
190
+ </MenuPrimitive.CheckboxItemIndicator>
191
+ </span>
192
+ {children}
193
+ </MenuPrimitive.CheckboxItem>
194
+ );
195
+ }
196
+
197
+ function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
198
+ return (
199
+ <MenuPrimitive.RadioGroup
200
+ data-slot="dropdown-menu-radio-group"
201
+ {...props}
202
+ />
203
+ );
204
+ }
205
+
206
+ function DropdownMenuRadioItem({
207
+ className,
208
+ children,
209
+ inset,
210
+ ...props
211
+ }: MenuPrimitive.RadioItem.Props & {
212
+ inset?: boolean;
213
+ }) {
214
+ return (
215
+ <MenuPrimitive.RadioItem
216
+ data-slot="dropdown-menu-radio-item"
217
+ data-inset={inset}
218
+ className={cn(
219
+ "relative flex min-h-7 cursor-pointer items-center gap-2 rounded-md py-1.5 pr-8 pl-2 popup-text-xs-plus leading-normal tracking-tight font-medium outline-hidden select-none hover:bg-[color:color-mix(in_oklab,var(--foreground)_5%,transparent)] focus:bg-[color:color-mix(in_oklab,var(--foreground)_5%,transparent)] focus:text-[color:var(--accent-foreground)] focus:**:text-[color:var(--accent-foreground)] data-inset:pl-7.5 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
220
+ className,
221
+ )}
222
+ {...props}
223
+ >
224
+ <span
225
+ className="pointer-events-none absolute right-2 flex items-center justify-center"
226
+ data-slot="dropdown-menu-radio-item-indicator"
227
+ >
228
+ <MenuPrimitive.RadioItemIndicator>
229
+ <CheckIcon />
230
+ </MenuPrimitive.RadioItemIndicator>
231
+ </span>
232
+ {children}
233
+ </MenuPrimitive.RadioItem>
234
+ );
235
+ }
236
+
237
+ function DropdownMenuSeparator({
238
+ className,
239
+ ...props
240
+ }: MenuPrimitive.Separator.Props) {
241
+ return (
242
+ <MenuPrimitive.Separator
243
+ data-slot="dropdown-menu-separator"
244
+ className={cn("floating-popup-separator -mx-1 my-1 h-px", className)}
245
+ {...props}
246
+ />
247
+ );
248
+ }
249
+
250
+ function DropdownMenuShortcut({
251
+ className,
252
+ ...props
253
+ }: React.ComponentProps<"span">) {
254
+ return (
255
+ <span
256
+ data-slot="dropdown-menu-shortcut"
257
+ className={cn(
258
+ "ml-auto text-[0.625rem] tracking-widest text-[color:var(--muted-foreground)] group-focus/dropdown-menu-item:text-[color:var(--accent-foreground)]",
259
+ className,
260
+ )}
261
+ {...props}
262
+ />
263
+ );
264
+ }
265
+
266
+ function DropdownMenuSubText({
267
+ className,
268
+ ...props
269
+ }: React.ComponentProps<"span">) {
270
+ return (
271
+ <span
272
+ data-slot="dropdown-menu-subtext"
273
+ className={cn(
274
+ "text-xs font-normal !text-[color:color-mix(in_oklab,var(--foreground)_60%,transparent)]",
275
+ className,
276
+ )}
277
+ {...props}
278
+ />
279
+ );
280
+ }
281
+
282
+ export {
283
+ DropdownMenu,
284
+ DropdownMenuPortal,
285
+ DropdownMenuTrigger,
286
+ DropdownMenuContent,
287
+ DropdownMenuGroup,
288
+ DropdownMenuLabel,
289
+ DropdownMenuItem,
290
+ DropdownMenuCheckboxItem,
291
+ DropdownMenuRadioGroup,
292
+ DropdownMenuRadioItem,
293
+ DropdownMenuSeparator,
294
+ DropdownMenuShortcut,
295
+ DropdownMenuSubText,
296
+ DropdownMenuSub,
297
+ DropdownMenuSubTrigger,
298
+ DropdownMenuSubContent,
299
+ };
@@ -0,0 +1,110 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import { cn } from "../../lib/utils";
3
+
4
+ const emptyVariants = cva(
5
+ "flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-xl border-dashed p-6 text-center text-balance",
6
+ {
7
+ variants: {
8
+ variant: {
9
+ default: "",
10
+ outline: "border border-[color:color-mix(in_oklab,var(--border)_12%,transparent)]",
11
+ },
12
+ },
13
+ defaultVariants: {
14
+ variant: "default",
15
+ },
16
+ },
17
+ );
18
+
19
+ function Empty({
20
+ className,
21
+ variant = "default",
22
+ ...props
23
+ }: React.ComponentProps<"div"> & VariantProps<typeof emptyVariants>) {
24
+ return <div data-slot="empty" className={cn(emptyVariants({ variant }), className)} {...props} />;
25
+ }
26
+
27
+ function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) {
28
+ return (
29
+ <div
30
+ data-slot="empty-header"
31
+ className={cn("flex max-w-sm flex-col items-center gap-1", className)}
32
+ {...props}
33
+ />
34
+ );
35
+ }
36
+
37
+ const emptyMediaVariants = cva(
38
+ "mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
39
+ {
40
+ variants: {
41
+ variant: {
42
+ default: "bg-transparent",
43
+ icon: "flex size-8 shrink-0 items-center justify-center rounded-md bg-[color:var(--muted)] text-[color:var(--foreground)] [&_svg:not([class*='size-'])]:size-4",
44
+ },
45
+ },
46
+ defaultVariants: {
47
+ variant: "default",
48
+ },
49
+ },
50
+ );
51
+
52
+ function EmptyMedia({
53
+ className,
54
+ variant = "default",
55
+ ...props
56
+ }: React.ComponentProps<"div"> & VariantProps<typeof emptyMediaVariants>) {
57
+ return (
58
+ <div
59
+ data-slot="empty-icon"
60
+ data-variant={variant}
61
+ className={cn(emptyMediaVariants({ variant, className }))}
62
+ {...props}
63
+ />
64
+ );
65
+ }
66
+
67
+ function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
68
+ return (
69
+ <div
70
+ data-slot="empty-title"
71
+ className={cn("text-sm font-medium tracking-tight", className)}
72
+ {...props}
73
+ />
74
+ );
75
+ }
76
+
77
+ function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
78
+ return (
79
+ <p
80
+ data-slot="empty-description"
81
+ className={cn(
82
+ "text-xs/relaxed text-[color:var(--muted-foreground)] [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-[color:var(--primary)]",
83
+ className,
84
+ )}
85
+ {...props}
86
+ />
87
+ );
88
+ }
89
+
90
+ function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
91
+ return (
92
+ <div
93
+ data-slot="empty-content"
94
+ className={cn(
95
+ "flex w-full max-w-sm min-w-0 flex-col items-center gap-2 text-xs/relaxed text-balance",
96
+ className,
97
+ )}
98
+ {...props}
99
+ />
100
+ );
101
+ }
102
+
103
+ export {
104
+ Empty,
105
+ EmptyContent,
106
+ EmptyDescription,
107
+ EmptyHeader,
108
+ EmptyMedia,
109
+ EmptyTitle,
110
+ };