@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,192 @@
1
+ "use client";
2
+
3
+ export { ActionsControl, ActionsControl as Actions } from "./actions";
4
+ export type {
5
+ ActionControlIconName,
6
+ ActionControlObjectOption,
7
+ ActionControlOption,
8
+ ActionsControlProps as ActionsProps,
9
+ ActionsControlProps,
10
+ } from "./actions";
11
+ export {
12
+ AnchorGridControl,
13
+ AnchorGridControl as AnchorGrid,
14
+ } from "./anchor-grid";
15
+ export type {
16
+ AnchorGridControlProps,
17
+ AnchorGridControlProps as AnchorGridProps,
18
+ AnchorGridValue,
19
+ } from "./anchor-grid";
20
+ export {
21
+ CheckboxControl,
22
+ CheckboxControl as Checkbox,
23
+ SwitchControl,
24
+ SwitchControl as Switch,
25
+ } from "./boolean";
26
+ export type {
27
+ CheckboxControlProps,
28
+ CheckboxControlProps as CheckboxProps,
29
+ SwitchControlProps,
30
+ SwitchControlProps as SwitchProps,
31
+ } from "./boolean";
32
+ export {
33
+ CodeTextareaControl,
34
+ CodeTextareaControl as CodeTextarea,
35
+ } from "./code-textarea";
36
+ export type {
37
+ CodeTextareaControlProps,
38
+ CodeTextareaControlProps as CodeTextareaProps,
39
+ } from "./code-textarea";
40
+ export {
41
+ ColorControl,
42
+ ColorControl as Color,
43
+ ColorOpacity,
44
+ ColorOpacityControl,
45
+ ColorValueControl,
46
+ ColorValueControl as ColorValue,
47
+ DEFAULT_COLOR_FORMAT_MODE,
48
+ getColorSurfaceModel,
49
+ getColorSurfaceSliderConfig,
50
+ getColorSurfaceStyle,
51
+ getColorSurfaceThumbPosition,
52
+ getSurfaceHsvColor,
53
+ PaletteControl,
54
+ PaletteControl as Palette,
55
+ } from "./color";
56
+ export type {
57
+ ColorFormatMode,
58
+ ColorControlInput,
59
+ ColorControlInputPair,
60
+ ColorControlProps,
61
+ ColorControlProps as ColorProps,
62
+ ColorOpacityControlProps,
63
+ ColorOpacityValue,
64
+ ColorSurfaceModel,
65
+ PaletteColorFamily,
66
+ PaletteControlChangeMeta,
67
+ PaletteControlProps,
68
+ PaletteControlProps as PaletteProps,
69
+ PaletteControlValue,
70
+ PaletteShadeStep,
71
+ } from "./color";
72
+ export {
73
+ PALETTE_SHADE_STEPS,
74
+ STYLE_GUIDE_PRIMARY_FAMILY_OPTIONS,
75
+ TAILWIND_COLOR_PALETTE,
76
+ getPaletteHex,
77
+ } from "./color";
78
+ export {
79
+ ChannelMixerControl,
80
+ ChannelMixerControl as ChannelMixer,
81
+ } from "./channel-mixer";
82
+ export type {
83
+ ChannelMixerControlProps,
84
+ ChannelMixerControlProps as ChannelMixerProps,
85
+ ChannelMixerValues,
86
+ } from "./channel-mixer";
87
+ export { createControlHistoryGroupId } from "./control-types";
88
+ export type {
89
+ ControlChangeHistoryMode,
90
+ ControlChangeMeta,
91
+ ControlOption,
92
+ ControlValueChangeHandler,
93
+ } from "./control-types";
94
+ export type {
95
+ CurveChannel,
96
+ CurvePoint,
97
+ GradientStop,
98
+ GradientType,
99
+ MixerChannel,
100
+ } from "./control-types";
101
+ export { CurvesControl, CurvesControl as Curves, getCurvePointAtX } from "./curves";
102
+ export type {
103
+ CurveInterpolation,
104
+ CurvesControlProps,
105
+ CurvesControlProps as CurvesProps,
106
+ CurvesControlVariant,
107
+ } from "./curves";
108
+ export { FileDropControl, FileDropControl as FileDrop } from "./file-drop";
109
+ export type {
110
+ FileDropControlProps,
111
+ FileDropControlProps as FileDropProps,
112
+ FileDropPreview,
113
+ } from "./file-drop";
114
+ export {
115
+ FontPickerControl,
116
+ FontPickerControl as FontPicker,
117
+ getDefaultFontPickerFontId,
118
+ getFontPickerCatalog,
119
+ getFontPickerFontById,
120
+ resolveFontPickerFontId,
121
+ } from "./font-picker";
122
+ export type {
123
+ FontPickerControlProps,
124
+ FontPickerControlProps as FontPickerProps,
125
+ FontPickerFontCatalogEntry,
126
+ FontPickerFontCategory,
127
+ FontPickerFontFilterValue,
128
+ FontPickerLetterSpacingPreset,
129
+ FontPickerLineHeightPreset,
130
+ FontPickerTextCasePreset,
131
+ FontPickerValue,
132
+ } from "./font-picker";
133
+ export { GradientControl, GradientControl as Gradient } from "./gradient";
134
+ export type {
135
+ GradientControlProps,
136
+ GradientControlProps as GradientProps,
137
+ } from "./gradient";
138
+ export {
139
+ ImagePickerControl,
140
+ ImagePickerControl as ImagePicker,
141
+ } from "./image-picker";
142
+ export type {
143
+ ImagePickerControlProps,
144
+ ImagePickerControlProps as ImagePickerProps,
145
+ ImagePickerItem,
146
+ } from "./image-picker";
147
+ export {
148
+ RangeSliderControl,
149
+ RangeSliderControl as RangeSlider,
150
+ } from "./range-slider";
151
+ export type {
152
+ RangeSliderControlProps,
153
+ RangeSliderControlProps as RangeSliderProps,
154
+ } from "./range-slider";
155
+ export {
156
+ RangeInputControl,
157
+ RangeInputControl as RangeInput,
158
+ } from "./range-input";
159
+ export type {
160
+ RangeInputControlProps,
161
+ RangeInputControlProps as RangeInputProps,
162
+ } from "./range-input";
163
+ export { SegmentedControl, SegmentedControl as Segmented } from "./segmented";
164
+ export type {
165
+ SegmentedControlOption,
166
+ SegmentedControlProps,
167
+ SegmentedControlProps as SegmentedProps,
168
+ SegmentedControlVariant,
169
+ } from "./segmented";
170
+ export { SelectControl, SelectControl as Select, StaticSelect } from "./select";
171
+ export type {
172
+ SelectControlProps,
173
+ SelectControlProps as SelectProps,
174
+ } from "./select";
175
+ export { SliderControl, SliderControl as Slider } from "./slider";
176
+ export type {
177
+ SliderControlProps,
178
+ SliderControlProps as SliderProps,
179
+ } from "./slider";
180
+ export { TextInputControl, TextInputControl as TextInput } from "./text-input";
181
+ export type {
182
+ TextInputControlProps,
183
+ TextInputControlProps as TextInputProps,
184
+ } from "./text-input";
185
+ export { VectorControl, VectorControl as Vector } from "./vector";
186
+ export type {
187
+ VectorControlProps,
188
+ VectorControlProps as VectorProps,
189
+ VectorControlValue,
190
+ VectorControlValue as VectorValue,
191
+ VectorPadVariant,
192
+ } from "./vector";
@@ -0,0 +1,4 @@
1
+ "use client";
2
+
3
+ export { RangeInputControl } from "./range-input-control";
4
+ export type { RangeInputControlProps } from "./range-input-control";
@@ -0,0 +1,173 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ import { ControlFieldLabel } from "../../control-layout";
6
+ import { Field, Input } from "../../primitives";
7
+ import { type ControlValueChangeHandler } from "../control-types";
8
+ import { cn } from "../../../lib/utils";
9
+
10
+ export type RangeInputControlProps = {
11
+ defaultValue?: { end: string; start: string };
12
+ end: string;
13
+ name: string;
14
+ onValueChange?: ControlValueChangeHandler<{ start: string; end: string }>;
15
+ start: string;
16
+ };
17
+
18
+ const RANGE_FULL_WIDTH_VALUE_LENGTH = 7;
19
+
20
+ function shouldUseFullWidthRangeInputs(start: string, end: string): boolean {
21
+ return Math.max(start.length, end.length) > RANGE_FULL_WIDTH_VALUE_LENGTH;
22
+ }
23
+
24
+ function RangeInputs({
25
+ className,
26
+ end,
27
+ name,
28
+ onCancel,
29
+ onCommit,
30
+ onDraftChange,
31
+ start,
32
+ }: RangeInputControlProps & {
33
+ className?: string;
34
+ onCancel: () => void;
35
+ onCommit: () => void;
36
+ onDraftChange: (nextValue: { start: string; end: string }) => void;
37
+ }): React.JSX.Element {
38
+ function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
39
+ if (event.key === "Enter") {
40
+ event.preventDefault();
41
+ onCommit();
42
+ event.currentTarget.blur();
43
+ return;
44
+ }
45
+
46
+ if (event.key === "Escape") {
47
+ event.preventDefault();
48
+ onCancel();
49
+ event.currentTarget.blur();
50
+ }
51
+ }
52
+
53
+ return (
54
+ <div
55
+ className={cn(
56
+ "grid min-w-0 grid-cols-[minmax(0,1fr)_minmax(0,1fr)] gap-1.5",
57
+ className,
58
+ )}
59
+ >
60
+ <Input
61
+ aria-label={`${name} start`}
62
+ className="font-mono"
63
+ onBlur={onCommit}
64
+ onChange={(event) =>
65
+ onDraftChange({ end, start: event.target.value })
66
+ }
67
+ onKeyDown={handleKeyDown}
68
+ size="default"
69
+ value={start}
70
+ />
71
+ <Input
72
+ aria-label={`${name} end`}
73
+ className="font-mono"
74
+ onBlur={onCommit}
75
+ onChange={(event) =>
76
+ onDraftChange({ end: event.target.value, start })
77
+ }
78
+ onKeyDown={handleKeyDown}
79
+ size="default"
80
+ value={end}
81
+ />
82
+ </div>
83
+ );
84
+ }
85
+
86
+ export function RangeInputControl({
87
+ defaultValue,
88
+ end,
89
+ name,
90
+ onValueChange,
91
+ start,
92
+ }: RangeInputControlProps): React.JSX.Element {
93
+ const [currentValue, setCurrentValue] = React.useState({ end, start });
94
+ const committedValueRef = React.useRef({ end, start });
95
+ const defaultValueRef = React.useRef(defaultValue ?? { end, start });
96
+
97
+ React.useEffect(() => {
98
+ committedValueRef.current = { end, start };
99
+ setCurrentValue({ end, start });
100
+ }, [end, start]);
101
+
102
+ React.useEffect(() => {
103
+ defaultValueRef.current = defaultValue ?? { end, start };
104
+ }, [defaultValue, end, start]);
105
+
106
+ function updateDraft(nextValue: { start: string; end: string }): void {
107
+ setCurrentValue(nextValue);
108
+ }
109
+
110
+ function getCommittedValue(): { start: string; end: string } {
111
+ const nextValue = {
112
+ end:
113
+ currentValue.end.trim() === ""
114
+ ? defaultValueRef.current.end
115
+ : currentValue.end,
116
+ start:
117
+ currentValue.start.trim() === ""
118
+ ? defaultValueRef.current.start
119
+ : currentValue.start,
120
+ };
121
+
122
+ return nextValue;
123
+ }
124
+
125
+ function commitValue(): void {
126
+ const nextValue = getCommittedValue();
127
+
128
+ setCurrentValue(nextValue);
129
+
130
+ if (
131
+ nextValue.start !== committedValueRef.current.start ||
132
+ nextValue.end !== committedValueRef.current.end
133
+ ) {
134
+ onValueChange?.(nextValue);
135
+ }
136
+ }
137
+
138
+ function cancelDraft(): void {
139
+ setCurrentValue(committedValueRef.current);
140
+ }
141
+
142
+ if (shouldUseFullWidthRangeInputs(start, end)) {
143
+ return (
144
+ <Field className="h-fit min-w-0 gap-2">
145
+ <ControlFieldLabel>{name}</ControlFieldLabel>
146
+ <RangeInputs
147
+ className="w-full"
148
+ end={currentValue.end}
149
+ name={name}
150
+ onCancel={cancelDraft}
151
+ onCommit={commitValue}
152
+ onDraftChange={updateDraft}
153
+ start={currentValue.start}
154
+ />
155
+ </Field>
156
+ );
157
+ }
158
+
159
+ return (
160
+ <Field className="h-fit min-w-0 items-center justify-between gap-3" orientation="horizontal">
161
+ <ControlFieldLabel>{name}</ControlFieldLabel>
162
+ <RangeInputs
163
+ className="w-1/2 shrink-0"
164
+ end={currentValue.end}
165
+ name={name}
166
+ onCancel={cancelDraft}
167
+ onCommit={commitValue}
168
+ onDraftChange={updateDraft}
169
+ start={currentValue.start}
170
+ />
171
+ </Field>
172
+ );
173
+ }
@@ -0,0 +1,4 @@
1
+ "use client";
2
+
3
+ export { RangeSliderControl } from "./range-slider-control";
4
+ export type { RangeSliderControlProps } from "./range-slider-control";
@@ -0,0 +1,122 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ import {
6
+ EditableSliderValueLabel,
7
+ Field,
8
+ Slider,
9
+ } from "../../primitives";
10
+ import { ControlFieldLabel } from "../../control-layout";
11
+ import {
12
+ formatRangeSliderValue,
13
+ parseRangeSliderDraft,
14
+ } from "./range-slider-value";
15
+ import { applySliderValueLabelUnit } from "../slider/slider-value";
16
+ import {
17
+ createControlHistoryGroupId,
18
+ type ControlChangeMeta,
19
+ type ControlValueChangeHandler,
20
+ } from "../control-types";
21
+
22
+ export type RangeSliderControlProps = {
23
+ baseValue?: readonly [number, number] | readonly number[];
24
+ disabled?: boolean;
25
+ markerCount?: number;
26
+ max?: number;
27
+ min?: number;
28
+ name: string;
29
+ onValueChange?: ControlValueChangeHandler<readonly number[]>;
30
+ step?: number;
31
+ unit?: string;
32
+ value: readonly [number, number] | readonly number[];
33
+ valueLabel?: string;
34
+ variant?: "continuous" | "discrete";
35
+ };
36
+
37
+ export function RangeSliderControl({
38
+ baseValue,
39
+ disabled = false,
40
+ markerCount,
41
+ max = 100,
42
+ min = 0,
43
+ name,
44
+ onValueChange,
45
+ step = 0.1,
46
+ unit,
47
+ value,
48
+ valueLabel,
49
+ variant = "continuous",
50
+ }: RangeSliderControlProps): React.JSX.Element {
51
+ const [rangeValue, setRangeValue] = React.useState<readonly number[]>(value);
52
+ const liveHistoryGroupRef = React.useRef<string | null>(null);
53
+ const resolvedValueLabel = valueLabel
54
+ ? applySliderValueLabelUnit(valueLabel, unit)
55
+ : formatRangeSliderValue(rangeValue, unit);
56
+
57
+ React.useEffect(() => {
58
+ setRangeValue(value);
59
+ }, [value]);
60
+
61
+ function getLiveHistoryMeta(): ControlChangeMeta {
62
+ liveHistoryGroupRef.current ??= createControlHistoryGroupId(`range-slider:${name}`);
63
+
64
+ return {
65
+ history: "merge",
66
+ historyGroup: liveHistoryGroupRef.current,
67
+ };
68
+ }
69
+
70
+ function finishLiveHistoryGroup(): void {
71
+ liveHistoryGroupRef.current = null;
72
+ }
73
+
74
+ function updateValue(
75
+ nextValue: number | readonly number[],
76
+ meta?: ControlChangeMeta,
77
+ ): void {
78
+ const resolvedValue =
79
+ typeof nextValue === "number" ? [nextValue, nextValue] : [...nextValue];
80
+
81
+ setRangeValue(resolvedValue);
82
+ onValueChange?.(resolvedValue, meta);
83
+ }
84
+
85
+ function commitValueLabel(nextValueLabel: string): void {
86
+ const nextValue = parseRangeSliderDraft(nextValueLabel, { max, min, step });
87
+
88
+ if (!nextValue) {
89
+ return;
90
+ }
91
+
92
+ setRangeValue(nextValue);
93
+ onValueChange?.(nextValue);
94
+ }
95
+
96
+ return (
97
+ <Field className="min-w-0 gap-1!" data-disabled={disabled}>
98
+ <div className="flex w-full min-w-0 items-center justify-between gap-3">
99
+ <ControlFieldLabel>{name}</ControlFieldLabel>
100
+ <EditableSliderValueLabel
101
+ ariaLabel={`${name} value`}
102
+ disabled={disabled}
103
+ onCommit={commitValueLabel}
104
+ valueLabel={resolvedValueLabel}
105
+ />
106
+ </div>
107
+ <Slider
108
+ className="w-full"
109
+ markerCount={markerCount}
110
+ max={max}
111
+ min={min}
112
+ disabled={disabled}
113
+ onValueChange={(nextValue) => updateValue(nextValue, getLiveHistoryMeta())}
114
+ onValueCommitted={finishLiveHistoryGroup}
115
+ resetValue={baseValue ?? value}
116
+ step={step}
117
+ value={rangeValue}
118
+ variant={variant}
119
+ />
120
+ </Field>
121
+ );
122
+ }
@@ -0,0 +1,61 @@
1
+ export function formatRangeSliderValue(
2
+ value: readonly number[],
3
+ unit?: string,
4
+ ): string {
5
+ const formatValue = (item: number): string => `${Math.round(item)}${unit ?? ""}`;
6
+
7
+ if (value.length >= 2 && value[0] === value[1]) {
8
+ return formatValue(value[0] ?? 0);
9
+ }
10
+
11
+ return value.map(formatValue).join(" – ");
12
+ }
13
+
14
+ export function snapRangeSliderValue(
15
+ value: number,
16
+ {
17
+ max,
18
+ min,
19
+ step,
20
+ }: {
21
+ max: number;
22
+ min: number;
23
+ step: number;
24
+ },
25
+ ): number {
26
+ const clampedValue = Math.min(max, Math.max(min, value));
27
+ const steppedValue = Math.round((clampedValue - min) / step) * step + min;
28
+
29
+ return Number(steppedValue.toFixed(4));
30
+ }
31
+
32
+ export function parseRangeSliderDraft(
33
+ draftValue: string,
34
+ {
35
+ max,
36
+ min,
37
+ step,
38
+ }: {
39
+ max: number;
40
+ min: number;
41
+ step: number;
42
+ },
43
+ ): [number, number] | null {
44
+ const normalizedDraftValue = draftValue.replace(/(\d)\s*-\s*(?=\d)/g, "$1 ");
45
+ const values = Array.from(
46
+ normalizedDraftValue.matchAll(/-?\d+(?:[.,]\d+)?/g),
47
+ ([match]) => Number(match.replace(",", ".")),
48
+ ).filter(Number.isFinite);
49
+
50
+ if (values.length === 0) {
51
+ return null;
52
+ }
53
+
54
+ const [first = min, second = first] = values;
55
+ const sortedValues = [
56
+ snapRangeSliderValue(first, { max, min, step }),
57
+ snapRangeSliderValue(second, { max, min, step }),
58
+ ].sort((left, right) => left - right) as [number, number];
59
+
60
+ return sortedValues;
61
+ }
@@ -0,0 +1,8 @@
1
+ "use client";
2
+
3
+ export { SegmentedControl } from "./segmented-control";
4
+ export type {
5
+ SegmentedControlOption,
6
+ SegmentedControlProps,
7
+ SegmentedControlVariant,
8
+ } from "./segmented-control";
@@ -0,0 +1,94 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ import { ControlFieldLabel } from "../../control-layout";
6
+ import { Field, ToggleGroup, ToggleGroupItem } from "../../primitives";
7
+ import type { ControlOption } from "../control-types";
8
+
9
+ export type SegmentedControlVariant = "default" | "dots";
10
+
11
+ export type SegmentedControlOption = ControlOption & {
12
+ indicatorColor?: string;
13
+ };
14
+
15
+ export type SegmentedControlProps = {
16
+ ariaLabel?: string;
17
+ name: string;
18
+ onValueChange?: (value: string) => void;
19
+ options: readonly SegmentedControlOption[];
20
+ value?: string;
21
+ variant?: SegmentedControlVariant;
22
+ };
23
+
24
+ export function SegmentedControl({
25
+ ariaLabel,
26
+ name,
27
+ onValueChange,
28
+ options,
29
+ value,
30
+ variant = "default",
31
+ }: SegmentedControlProps): React.JSX.Element {
32
+ const [currentValue, setCurrentValue] = React.useState(
33
+ () => value ?? options[0]?.value ?? "",
34
+ );
35
+ const selectedValue = value ?? currentValue;
36
+
37
+ React.useEffect(() => {
38
+ if (typeof value !== "undefined") {
39
+ setCurrentValue(value);
40
+ return;
41
+ }
42
+
43
+ if (!options.some((option) => option.value === currentValue)) {
44
+ setCurrentValue(options[0]?.value ?? "");
45
+ }
46
+ }, [currentValue, options, value]);
47
+
48
+ function updateValue(nextValue: string): void {
49
+ setCurrentValue(nextValue);
50
+ onValueChange?.(nextValue);
51
+ }
52
+
53
+ return (
54
+ <Field className="min-w-0 gap-2">
55
+ <ControlFieldLabel>{name}</ControlFieldLabel>
56
+ <ToggleGroup
57
+ aria-label={ariaLabel ?? name}
58
+ className="w-full"
59
+ onValueChange={(nextValue) => {
60
+ const [selectedValue] = nextValue;
61
+
62
+ if (selectedValue) {
63
+ updateValue(selectedValue);
64
+ }
65
+ }}
66
+ size="default"
67
+ value={selectedValue ? [selectedValue] : []}
68
+ variant="outline"
69
+ >
70
+ {options.map((option) => (
71
+ <ToggleGroupItem
72
+ aria-label={option.label}
73
+ className={
74
+ variant === "dots" ? "min-w-0 flex-1 gap-[7px]" : "min-w-0 flex-1"
75
+ }
76
+ key={option.value}
77
+ value={option.value}
78
+ >
79
+ {variant === "dots" ? (
80
+ <span
81
+ aria-hidden="true"
82
+ className="size-2 shrink-0 rounded-full"
83
+ style={{
84
+ backgroundColor: option.indicatorColor ?? "currentColor",
85
+ }}
86
+ />
87
+ ) : null}
88
+ {option.label}
89
+ </ToggleGroupItem>
90
+ ))}
91
+ </ToggleGroup>
92
+ </Field>
93
+ );
94
+ }
@@ -0,0 +1,4 @@
1
+ "use client";
2
+
3
+ export { SelectControl, StaticSelect } from "./select-control";
4
+ export type { SelectControlProps } from "./select-control";