@pixel-point/toolcraft 0.0.3 → 0.0.6

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 (47) hide show
  1. package/package.json +1 -1
  2. package/scripts/prepare-pack.mjs +5 -0
  3. package/src/generate.mjs +13 -0
  4. package/src/generate.test.mjs +6 -0
  5. package/templates/runtime/contracts/component-contracts.test.ts +86 -8
  6. package/templates/runtime/contracts/component-contracts.ts +36 -8
  7. package/templates/runtime/contracts/decision-contracts.ts +2 -2
  8. package/templates/runtime/export/export.test.ts +65 -0
  9. package/templates/runtime/export/export.ts +54 -1
  10. package/templates/runtime/react/canvas-shell.test.tsx +7 -7
  11. package/templates/runtime/react/controls-panel.test.tsx +323 -6
  12. package/templates/runtime/react/controls-panel.tsx +349 -24
  13. package/templates/runtime/react/settings-transfer.test.ts +6 -0
  14. package/templates/runtime/react/settings-transfer.ts +28 -2
  15. package/templates/runtime/react/timeline-panel.test.tsx +69 -0
  16. package/templates/runtime/react/timeline-panel.tsx +98 -10
  17. package/templates/runtime/react/toolbar-panel.test.tsx +6 -6
  18. package/templates/runtime/react/toolcraft-app.integration.test.tsx +2 -2
  19. package/templates/runtime/schema/canvas-aspect-ratio-presets.ts +50 -0
  20. package/templates/runtime/schema/define-toolcraft.test.ts +122 -2
  21. package/templates/runtime/schema/define-toolcraft.ts +197 -6
  22. package/templates/runtime/schema/keyframe-capability.test.ts +7 -0
  23. package/templates/runtime/schema/keyframe-capability.ts +2 -2
  24. package/templates/runtime/schema/runtime-targets.ts +6 -0
  25. package/templates/runtime/schema/types.ts +23 -1
  26. package/templates/runtime/state/canvas-zoom.ts +1 -1
  27. package/templates/runtime/state/create-template-state.test.ts +9 -3
  28. package/templates/runtime/state/reducer.test.ts +135 -2
  29. package/templates/runtime/state/reducer.ts +236 -12
  30. package/templates/runtime/state/types.ts +1 -0
  31. package/templates/starter/AGENTS.md +6 -4
  32. package/templates/starter/docs/toolcraft/README.md +1 -1
  33. package/templates/starter/docs/toolcraft/acceptance-testing.md +4 -2
  34. package/templates/starter/docs/toolcraft/assembly-workflow.md +13 -4
  35. package/templates/starter/docs/toolcraft/component-rules.md +24 -5
  36. package/templates/starter/docs/toolcraft/performance.md +5 -0
  37. package/templates/starter/docs/toolcraft/renderer-technique.md +1 -1
  38. package/templates/starter/docs/toolcraft/schema-reference.md +53 -9
  39. package/templates/starter/gitignore +36 -0
  40. package/templates/starter/src/app/starter-acceptance.test.ts +678 -21
  41. package/templates/starter/src/app/starter-acceptance.ts +357 -4
  42. package/templates/ui/components/control-layout/index.tsx +4 -4
  43. package/templates/ui/components/controls/file-drop/file-drop-control.tsx +101 -18
  44. package/templates/ui/components/controls/font-picker/font-picker-control.tsx +1 -1
  45. package/templates/ui/components/controls/range-slider/range-slider-value.ts +4 -1
  46. package/templates/ui/components/controls/slider/slider-value.ts +48 -5
  47. package/templates/ui/components/primitives/editable-slider-value-label.tsx +6 -1
@@ -17,19 +17,62 @@ export function formatSliderValue(value: number, step: number): string {
17
17
  return String(rounded);
18
18
  }
19
19
 
20
+ const compactSliderValueUnits = new Set([
21
+ "%",
22
+ "°",
23
+ "px",
24
+ "em",
25
+ "rem",
26
+ "vw",
27
+ "vh",
28
+ "vmin",
29
+ "vmax",
30
+ "x",
31
+ "s",
32
+ "ms",
33
+ ]);
34
+
35
+ function escapeRegExp(value: string): string {
36
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
37
+ }
38
+
39
+ export function getSliderValueUnitSeparator(unit: string): "" | " " {
40
+ const normalizedUnit = unit.trim();
41
+
42
+ if (
43
+ normalizedUnit === "" ||
44
+ compactSliderValueUnits.has(normalizedUnit) ||
45
+ /^[^\p{Letter}\p{Number}]+$/u.test(normalizedUnit)
46
+ ) {
47
+ return "";
48
+ }
49
+
50
+ return " ";
51
+ }
52
+
20
53
  export function applySliderValueLabelUnit(
21
54
  valueLabel: string,
22
55
  unit?: string,
23
56
  ): string {
24
- if (!unit || typeof parseSliderValueLabel(valueLabel) !== "number") {
57
+ const normalizedUnit = unit?.trim();
58
+
59
+ if (
60
+ !normalizedUnit ||
61
+ typeof parseSliderValueLabel(valueLabel) !== "number"
62
+ ) {
25
63
  return valueLabel;
26
64
  }
27
65
 
28
- return valueLabel.replaceAll(/-?\d+(?:\.\d+)?/g, (match, offset) => {
29
- const textAfterMatch = valueLabel.slice(offset + match.length).trimStart();
66
+ const separator = getSliderValueUnitSeparator(normalizedUnit);
67
+ const unitPattern = new RegExp(
68
+ `(-?\\d+(?:\\.\\d+)?)(?:\\s*${escapeRegExp(normalizedUnit)})?`,
69
+ "g",
70
+ );
30
71
 
31
- return textAfterMatch.startsWith(unit) ? match : `${match}${unit}`;
32
- });
72
+ return valueLabel.replaceAll(
73
+ unitPattern,
74
+ (_match, value: string) => `${value}${separator}${normalizedUnit}`,
75
+ );
33
76
  }
34
77
 
35
78
  export function formatSliderValueWithUnit(
@@ -40,6 +40,7 @@ export function EditableSliderValueLabel({
40
40
  const [editing, setEditing] = useState(false);
41
41
  const editorRef = useRef<HTMLSpanElement>(null);
42
42
  const valueLabelRef = useRef(valueLabel);
43
+ const isEditableValueLabel = hasEditableNumericValueLabel(valueLabel);
43
44
  const valueTextClassName = getEditableValueTextClassName({ layout, textAlign });
44
45
  const widestValueLabel = getWidestValueLabel(valueLabel, maxValueLabel);
45
46
 
@@ -61,7 +62,7 @@ export function EditableSliderValueLabel({
61
62
  }
62
63
  }, [editing]);
63
64
 
64
- if (disabled || !onCommit) {
65
+ if (disabled || !onCommit || !isEditableValueLabel) {
65
66
  const valueTextToneClassName = disabled
66
67
  ? "text-[color:color-mix(in_oklab,var(--foreground)_60%,transparent)] opacity-60"
67
68
  : "text-[color:var(--muted-foreground)]";
@@ -323,6 +324,10 @@ function getWidestValueLabel(valueLabel: string, maxValueLabel?: string): string
323
324
  return maxValueLabel;
324
325
  }
325
326
 
327
+ function hasEditableNumericValueLabel(valueLabel: string): boolean {
328
+ return /-?\d+(?:\.\d+)?/.test(valueLabel);
329
+ }
330
+
326
331
  function selectEditableText(node: HTMLElement): void {
327
332
  const selection = window.getSelection();
328
333