@pixel-point/toolcraft 0.0.9 → 0.0.12
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.
- package/README.md +42 -9
- package/package.json +2 -1
- package/src/cli.mjs +4 -2
- package/src/cli.test.mjs +5 -2
- package/src/generate.mjs +14 -6
- package/src/generate.test.mjs +33 -2
- package/templates/runtime/contracts/component-contracts.test.ts +175 -36
- package/templates/runtime/contracts/component-contracts.ts +90 -45
- package/templates/runtime/contracts/decision-contracts.test.ts +33 -1
- package/templates/runtime/contracts/decision-contracts.ts +19 -6
- package/templates/runtime/export/export.test.ts +63 -0
- package/templates/runtime/export/export.ts +55 -0
- package/templates/runtime/index.ts +1 -0
- package/templates/runtime/react/canvas-shell.test.tsx +58 -4
- package/templates/runtime/react/canvas-shell.tsx +31 -9
- package/templates/runtime/react/controls-panel.test.tsx +478 -27
- package/templates/runtime/react/controls-panel.tsx +71 -26
- package/templates/runtime/react/runtime-public-api.test.tsx +1 -1
- package/templates/runtime/react/settings-transfer.test.ts +4 -0
- package/templates/runtime/react/settings-transfer.ts +6 -1
- package/templates/runtime/react/timeline-panel.test.tsx +14 -0
- package/templates/runtime/react/timeline-panel.tsx +44 -7
- package/templates/runtime/react/toolcraft-app.integration.test.tsx +9 -1
- package/templates/runtime/react/toolcraft-app.test.tsx +112 -3
- package/templates/runtime/react/toolcraft-app.tsx +56 -37
- package/templates/runtime/schema/define-toolcraft.test.ts +225 -173
- package/templates/runtime/schema/define-toolcraft.ts +117 -247
- package/templates/runtime/schema/runtime-targets.ts +21 -0
- package/templates/runtime/schema/types.ts +41 -0
- package/templates/runtime/state/create-template-state.test.ts +156 -0
- package/templates/runtime/state/create-template-state.ts +38 -8
- package/templates/runtime/state/media-defaults.ts +105 -0
- package/templates/runtime/state/persistence.test.ts +58 -0
- package/templates/runtime/state/persistence.ts +105 -1
- package/templates/runtime/state/reducer.test.ts +280 -4
- package/templates/runtime/state/reducer.ts +195 -9
- package/templates/runtime/state/timeline-loop.test.ts +71 -0
- package/templates/runtime/state/timeline-loop.ts +35 -0
- package/templates/runtime/state/types.ts +27 -0
- package/templates/runtime/testing/performance.test.ts +657 -2
- package/templates/runtime/testing/performance.ts +789 -49
- package/templates/starter/AGENTS.md +22 -16
- package/templates/starter/docs/toolcraft/README.md +8 -4
- package/templates/starter/docs/toolcraft/acceptance-testing.md +41 -8
- package/templates/starter/docs/toolcraft/agent-worklog.md +1 -0
- package/templates/starter/docs/toolcraft/assembly-workflow.md +51 -20
- package/templates/starter/docs/toolcraft/component-rules.md +58 -37
- package/templates/starter/docs/toolcraft/decision-contract.md +2 -0
- package/templates/starter/docs/toolcraft/performance.md +30 -10
- package/templates/starter/docs/toolcraft/schema-reference.md +134 -33
- package/templates/starter/docs/toolcraft/workflow.md +5 -2
- package/templates/starter/e2e/app-browser-acceptance.spec.ts +3 -3
- package/templates/starter/e2e/app-performance.spec.ts +57 -4
- package/templates/starter/e2e/performance-helpers.ts +45 -0
- package/templates/starter/index.html +1 -0
- package/templates/starter/package.json +3 -2
- package/templates/starter/playwright.config.ts +1 -1
- package/templates/starter/scripts/check-toolcraft-docs.mjs +1 -0
- package/templates/starter/scripts/run-vite-on-free-port.mjs +82 -16
- package/templates/starter/scripts/toolcraft-port.mjs +178 -0
- package/templates/starter/scripts/toolcraft-port.test.mjs +147 -0
- package/templates/starter/src/app/starter-acceptance.test.ts +2573 -313
- package/templates/starter/src/app/starter-acceptance.ts +978 -81
- package/templates/starter/src/app/starter-performance.test.ts +130 -8
- package/templates/starter/src/app/starter-performance.ts +5 -0
- package/templates/starter/src/app/starter-schema.test.ts +32 -7
- package/templates/starter/src/app/starter-schema.ts +6 -2
- package/templates/starter/vite.config.ts +58 -2
- package/templates/ui/components/controls/actions/actions-control.tsx +46 -3
- package/templates/ui/components/controls/color/palette-control.tsx +34 -4
- package/templates/ui/components/controls/file-drop/file-drop-control.tsx +186 -14
- package/templates/ui/components/controls/file-drop/index.ts +6 -1
- package/templates/ui/components/controls/index.ts +2 -0
- package/templates/ui/components/controls/range-slider/range-slider-value.ts +19 -5
- package/templates/ui/components/controls/select/select-control.tsx +4 -26
- package/templates/ui/components/controls/vector/vector-control.tsx +25 -4
- package/templates/ui/components/panel/panel-actions.tsx +1 -1
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
getToolcraftCanvasAspectRatioPreset,
|
|
4
4
|
getToolcraftCanvasAspectRatioPresetBySize,
|
|
5
5
|
} from "./canvas-aspect-ratio-presets";
|
|
6
|
+
import { toolcraftTimelinePanelExtendedTarget } from "./runtime-targets";
|
|
6
7
|
import type {
|
|
7
8
|
ToolcraftAssemblyCapability,
|
|
8
9
|
ToolcraftAssemblyCommand,
|
|
@@ -33,6 +34,7 @@ const defaultCanvasSize = {
|
|
|
33
34
|
|
|
34
35
|
type ResolvedCanvas = ResolvedToolcraftAppSchema["canvas"];
|
|
35
36
|
type ResolvedExport = ResolvedToolcraftAppSchema["export"];
|
|
37
|
+
type ResolvedMedia = ResolvedToolcraftAppSchema["media"];
|
|
36
38
|
type ResolvedToolbar = Required<ToolcraftToolbarSchema>;
|
|
37
39
|
type PanelContract = {
|
|
38
40
|
capabilities?: readonly string[];
|
|
@@ -47,6 +49,9 @@ const canvasSizeControlTargets = {
|
|
|
47
49
|
} as const;
|
|
48
50
|
const canvasAspectRatioTarget = "canvas.aspectRatio";
|
|
49
51
|
const canvasRenderScaleTarget = "canvas.renderScale";
|
|
52
|
+
const defaultTimelineDurationSeconds = 8;
|
|
53
|
+
const minTimelineDurationSeconds = 1;
|
|
54
|
+
const maxTimelineDurationSeconds = 60;
|
|
50
55
|
const defaultCanvasRenderScale = {
|
|
51
56
|
defaultValue: 2,
|
|
52
57
|
enabled: false,
|
|
@@ -57,33 +62,6 @@ const defaultCanvasRenderScale = {
|
|
|
57
62
|
const maxAutoInlineControlLabelLength = 18;
|
|
58
63
|
const settingsTransferTarget = "runtime.settingsTransfer";
|
|
59
64
|
const runtimeSetupSectionTitle = "Setup";
|
|
60
|
-
const settingsTransferHeavyControlTypes = new Set([
|
|
61
|
-
"channelMixer",
|
|
62
|
-
"code",
|
|
63
|
-
"collectionActions",
|
|
64
|
-
"colorOpacity",
|
|
65
|
-
"curves",
|
|
66
|
-
"fileDrop",
|
|
67
|
-
"fontPicker",
|
|
68
|
-
"gradient",
|
|
69
|
-
"imagePicker",
|
|
70
|
-
"palette",
|
|
71
|
-
"rangeSlider",
|
|
72
|
-
"vector",
|
|
73
|
-
]);
|
|
74
|
-
|
|
75
|
-
export type ToolcraftSettingsTransferEligibilityReason =
|
|
76
|
-
| "control-count"
|
|
77
|
-
| "score"
|
|
78
|
-
| "section-count";
|
|
79
|
-
|
|
80
|
-
export type ToolcraftSettingsTransferEligibility = {
|
|
81
|
-
controlCount: number;
|
|
82
|
-
eligible: boolean;
|
|
83
|
-
reasons: ToolcraftSettingsTransferEligibilityReason[];
|
|
84
|
-
score: number;
|
|
85
|
-
sectionCount: number;
|
|
86
|
-
};
|
|
87
65
|
|
|
88
66
|
type ToolcraftControlActionSchema = NonNullable<
|
|
89
67
|
ToolcraftControlSchema["actions"]
|
|
@@ -193,7 +171,7 @@ function resolveCanvasSizing(
|
|
|
193
171
|
}
|
|
194
172
|
|
|
195
173
|
if (canvas.upload) {
|
|
196
|
-
return { mode: "
|
|
174
|
+
return { mode: "editable-output" };
|
|
197
175
|
}
|
|
198
176
|
|
|
199
177
|
return { mode: "intrinsic-media" };
|
|
@@ -257,6 +235,12 @@ function resolveExport(
|
|
|
257
235
|
};
|
|
258
236
|
}
|
|
259
237
|
|
|
238
|
+
function resolveMedia(mediaSchema: ToolcraftAppSchema["media"]): ResolvedMedia {
|
|
239
|
+
return {
|
|
240
|
+
defaultAssets: mediaSchema?.defaultAssets ?? [],
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
260
244
|
function getPanelDragMode(
|
|
261
245
|
contract: { capabilities?: readonly string[] },
|
|
262
246
|
): ToolcraftAssemblyPanelContract["dragMode"] {
|
|
@@ -283,7 +267,12 @@ function createPanelAssemblyContract({
|
|
|
283
267
|
])
|
|
284
268
|
: [];
|
|
285
269
|
const panelCommands = enabled
|
|
286
|
-
? unique<ToolcraftAssemblyCommand>([
|
|
270
|
+
? unique<ToolcraftAssemblyCommand>([
|
|
271
|
+
"panels.setOffset",
|
|
272
|
+
"panels.setHidden",
|
|
273
|
+
"panels.resetOffset",
|
|
274
|
+
...commands,
|
|
275
|
+
])
|
|
287
276
|
: [];
|
|
288
277
|
|
|
289
278
|
return {
|
|
@@ -332,7 +321,7 @@ function createToolcraftAssembly({
|
|
|
332
321
|
|
|
333
322
|
if (canvas.upload) {
|
|
334
323
|
capabilities.push("canvas.upload");
|
|
335
|
-
commands.push("media.delete", "media.import", "media.reorder");
|
|
324
|
+
commands.push("media.delete", "media.import", "media.reorder", "media.transform");
|
|
336
325
|
}
|
|
337
326
|
}
|
|
338
327
|
|
|
@@ -479,7 +468,12 @@ function createToolcraftAssembly({
|
|
|
479
468
|
? (["canvas.panBy", "canvas.setOffset", "canvas.setViewport"] as const)
|
|
480
469
|
: []),
|
|
481
470
|
...(canvas.upload
|
|
482
|
-
? ([
|
|
471
|
+
? ([
|
|
472
|
+
"media.delete",
|
|
473
|
+
"media.import",
|
|
474
|
+
"media.reorder",
|
|
475
|
+
"media.transform",
|
|
476
|
+
] as const)
|
|
483
477
|
: []),
|
|
484
478
|
])
|
|
485
479
|
: [],
|
|
@@ -496,15 +490,6 @@ function createToolcraftAssembly({
|
|
|
496
490
|
};
|
|
497
491
|
}
|
|
498
492
|
|
|
499
|
-
function hasControlTarget(
|
|
500
|
-
panels: ToolcraftAppSchema["panels"],
|
|
501
|
-
target: string,
|
|
502
|
-
): boolean {
|
|
503
|
-
return (panels.controls?.sections ?? []).some((section) =>
|
|
504
|
-
Object.values(section.controls).some((control) => control.target === target),
|
|
505
|
-
);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
493
|
function getControlDefaultSectionLayout(
|
|
509
494
|
control: ToolcraftControlSchema,
|
|
510
495
|
): "grouped" | "standalone" {
|
|
@@ -637,116 +622,12 @@ function isPanelActionsControl(control: ToolcraftControlSchema): boolean {
|
|
|
637
622
|
return control.type === "panelActions";
|
|
638
623
|
}
|
|
639
624
|
|
|
640
|
-
function isSettingsTransferControl(control: ToolcraftControlSchema): boolean {
|
|
641
|
-
return control.type === "settingsTransfer";
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
function isRuntimeOnlyActionControl(control: ToolcraftControlSchema): boolean {
|
|
645
|
-
return isPanelActionsControl(control) || isSettingsTransferControl(control);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
function isSettingsTransferEligibilityControl(control: ToolcraftControlSchema): boolean {
|
|
649
|
-
return (
|
|
650
|
-
!isRuntimeOnlyActionControl(control) &&
|
|
651
|
-
control.target !== canvasAspectRatioTarget &&
|
|
652
|
-
control.target !== canvasSizeControlTargets.width &&
|
|
653
|
-
control.target !== canvasSizeControlTargets.height
|
|
654
|
-
);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
function getSettingsTransferComplexityScore({
|
|
658
|
-
panels,
|
|
659
|
-
}: {
|
|
660
|
-
panels: ToolcraftAppSchema["panels"];
|
|
661
|
-
}): {
|
|
662
|
-
controlCount: number;
|
|
663
|
-
score: number;
|
|
664
|
-
sectionCount: number;
|
|
665
|
-
} {
|
|
666
|
-
const sections = panels.controls?.sections ?? [];
|
|
667
|
-
let controlCount = 0;
|
|
668
|
-
let score = 0;
|
|
669
|
-
|
|
670
|
-
for (const section of sections) {
|
|
671
|
-
for (const control of Object.values(section.controls)) {
|
|
672
|
-
if (!isSettingsTransferEligibilityControl(control)) {
|
|
673
|
-
continue;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
controlCount += 1;
|
|
677
|
-
score += settingsTransferHeavyControlTypes.has(control.type) ? 3 : 1;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
const sectionCount = sections.filter((section) =>
|
|
682
|
-
Object.values(section.controls).some((control) =>
|
|
683
|
-
isSettingsTransferEligibilityControl(control),
|
|
684
|
-
),
|
|
685
|
-
).length;
|
|
686
|
-
|
|
687
|
-
score += Math.max(0, sectionCount - 4);
|
|
688
|
-
|
|
689
|
-
if (
|
|
690
|
-
panels.timeline === true ||
|
|
691
|
-
(typeof panels.timeline === "object" && panels.timeline.enabled !== false)
|
|
692
|
-
) {
|
|
693
|
-
score += 2;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
if (panels.layers) {
|
|
697
|
-
score += 2;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
return { controlCount, score, sectionCount };
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
export function getToolcraftSettingsTransferEligibility({
|
|
704
|
-
panels,
|
|
705
|
-
}: {
|
|
706
|
-
panels: ToolcraftAppSchema["panels"];
|
|
707
|
-
}): ToolcraftSettingsTransferEligibility {
|
|
708
|
-
const { controlCount, score, sectionCount } = getSettingsTransferComplexityScore({
|
|
709
|
-
panels,
|
|
710
|
-
});
|
|
711
|
-
const reasons: ToolcraftSettingsTransferEligibilityReason[] = [];
|
|
712
|
-
|
|
713
|
-
if (controlCount >= 12) {
|
|
714
|
-
reasons.push("control-count");
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
if (sectionCount >= 5) {
|
|
718
|
-
reasons.push("section-count");
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
if (score >= 18) {
|
|
722
|
-
reasons.push("score");
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
return {
|
|
726
|
-
controlCount,
|
|
727
|
-
eligible: reasons.length > 0,
|
|
728
|
-
reasons,
|
|
729
|
-
score,
|
|
730
|
-
sectionCount,
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
function shouldAutoEnableSettingsTransfer({
|
|
735
|
-
panels,
|
|
736
|
-
}: {
|
|
737
|
-
panels: ToolcraftAppSchema["panels"];
|
|
738
|
-
}): boolean {
|
|
739
|
-
return getToolcraftSettingsTransferEligibility({ panels }).eligible;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
625
|
function resolveSettingsTransfer({
|
|
743
626
|
controls,
|
|
744
|
-
panels,
|
|
745
627
|
persistence,
|
|
746
628
|
settingsTransfer,
|
|
747
629
|
}: {
|
|
748
630
|
controls: ToolcraftControlsPanelSchema | undefined;
|
|
749
|
-
panels: ToolcraftAppSchema["panels"];
|
|
750
631
|
persistence: ResolvedToolcraftAppSchema["persistence"];
|
|
751
632
|
settingsTransfer: ToolcraftSettingsTransferSchema | undefined;
|
|
752
633
|
}): ResolvedToolcraftSettingsTransferSchema {
|
|
@@ -759,9 +640,7 @@ function resolveSettingsTransfer({
|
|
|
759
640
|
|
|
760
641
|
return {
|
|
761
642
|
appId,
|
|
762
|
-
enabled:
|
|
763
|
-
Boolean(controls) &&
|
|
764
|
-
(mode === "auto" ? shouldAutoEnableSettingsTransfer({ panels }) : mode),
|
|
643
|
+
enabled: Boolean(controls),
|
|
765
644
|
fileName: getSettingsTransferFileName({ appId, settingsTransfer }),
|
|
766
645
|
mode,
|
|
767
646
|
};
|
|
@@ -769,11 +648,7 @@ function resolveSettingsTransfer({
|
|
|
769
648
|
|
|
770
649
|
function createSettingsTransferSection(
|
|
771
650
|
settingsTransfer: ResolvedToolcraftSettingsTransferSchema,
|
|
772
|
-
): ToolcraftControlSectionSchema
|
|
773
|
-
if (!settingsTransfer.enabled) {
|
|
774
|
-
return null;
|
|
775
|
-
}
|
|
776
|
-
|
|
651
|
+
): ToolcraftControlSectionSchema {
|
|
777
652
|
return {
|
|
778
653
|
controls: {
|
|
779
654
|
settingsTransfer: {
|
|
@@ -830,51 +705,20 @@ function getCanvasAspectRatioDefaultValue(size: ToolcraftCanvasSize): {
|
|
|
830
705
|
};
|
|
831
706
|
}
|
|
832
707
|
|
|
833
|
-
function createCanvasSizeSection({
|
|
834
|
-
aspectRatioControl,
|
|
835
|
-
renderScaleControl,
|
|
836
|
-
sizeControlIds,
|
|
837
|
-
sizeControls,
|
|
838
|
-
}: {
|
|
839
|
-
aspectRatioControl: ToolcraftControlSchema;
|
|
840
|
-
renderScaleControl?: ToolcraftControlSchema;
|
|
841
|
-
sizeControlIds: readonly string[];
|
|
842
|
-
sizeControls: ToolcraftControlSectionSchema["controls"];
|
|
843
|
-
}): ToolcraftControlSectionSchema {
|
|
844
|
-
return {
|
|
845
|
-
controls: {
|
|
846
|
-
canvasAspectRatio: aspectRatioControl,
|
|
847
|
-
...sizeControls,
|
|
848
|
-
...(renderScaleControl ? { canvasRenderScale: renderScaleControl } : {}),
|
|
849
|
-
},
|
|
850
|
-
layoutGroups: getCanvasSizeLayoutGroups(sizeControlIds),
|
|
851
|
-
title: runtimeSetupSectionTitle,
|
|
852
|
-
};
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
function createCanvasRenderScaleSection(
|
|
856
|
-
renderScaleControl: ToolcraftControlSchema,
|
|
857
|
-
): ToolcraftControlSectionSchema {
|
|
858
|
-
return {
|
|
859
|
-
controls: {
|
|
860
|
-
canvasRenderScale: renderScaleControl,
|
|
861
|
-
},
|
|
862
|
-
title: runtimeSetupSectionTitle,
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
|
|
866
708
|
function mergeCanvasSizeControlsIntoSettingsTransferSection({
|
|
867
709
|
aspectRatioControl,
|
|
868
710
|
renderScaleControl,
|
|
869
711
|
settingsTransferSection,
|
|
870
712
|
sizeControlIds,
|
|
871
713
|
sizeControls,
|
|
714
|
+
timelineExtendedControl,
|
|
872
715
|
}: {
|
|
873
716
|
aspectRatioControl: ToolcraftControlSchema;
|
|
874
717
|
renderScaleControl?: ToolcraftControlSchema;
|
|
875
718
|
settingsTransferSection: ToolcraftControlSectionSchema;
|
|
876
719
|
sizeControlIds: readonly string[];
|
|
877
720
|
sizeControls: ToolcraftControlSectionSchema["controls"];
|
|
721
|
+
timelineExtendedControl?: ToolcraftControlSchema;
|
|
878
722
|
}): ToolcraftControlSectionSchema {
|
|
879
723
|
const canvasSizeLayoutGroups = getCanvasSizeLayoutGroups(sizeControlIds) ?? [];
|
|
880
724
|
|
|
@@ -885,6 +729,7 @@ function mergeCanvasSizeControlsIntoSettingsTransferSection({
|
|
|
885
729
|
canvasAspectRatio: aspectRatioControl,
|
|
886
730
|
...sizeControls,
|
|
887
731
|
...(renderScaleControl ? { canvasRenderScale: renderScaleControl } : {}),
|
|
732
|
+
...(timelineExtendedControl ? { timelineExtended: timelineExtendedControl } : {}),
|
|
888
733
|
},
|
|
889
734
|
layoutGroups:
|
|
890
735
|
canvasSizeLayoutGroups.length > 0 || settingsTransferSection.layoutGroups?.length
|
|
@@ -899,15 +744,34 @@ function mergeCanvasSizeControlsIntoSettingsTransferSection({
|
|
|
899
744
|
function mergeCanvasRenderScaleIntoSettingsTransferSection({
|
|
900
745
|
renderScaleControl,
|
|
901
746
|
settingsTransferSection,
|
|
747
|
+
timelineExtendedControl,
|
|
902
748
|
}: {
|
|
903
749
|
renderScaleControl: ToolcraftControlSchema;
|
|
904
750
|
settingsTransferSection: ToolcraftControlSectionSchema;
|
|
751
|
+
timelineExtendedControl?: ToolcraftControlSchema;
|
|
905
752
|
}): ToolcraftControlSectionSchema {
|
|
906
753
|
return {
|
|
907
754
|
...settingsTransferSection,
|
|
908
755
|
controls: {
|
|
909
756
|
...settingsTransferSection.controls,
|
|
910
757
|
canvasRenderScale: renderScaleControl,
|
|
758
|
+
...(timelineExtendedControl ? { timelineExtended: timelineExtendedControl } : {}),
|
|
759
|
+
},
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
function mergeTimelineExtendedIntoSettingsTransferSection({
|
|
764
|
+
settingsTransferSection,
|
|
765
|
+
timelineExtendedControl,
|
|
766
|
+
}: {
|
|
767
|
+
settingsTransferSection: ToolcraftControlSectionSchema;
|
|
768
|
+
timelineExtendedControl: ToolcraftControlSchema;
|
|
769
|
+
}): ToolcraftControlSectionSchema {
|
|
770
|
+
return {
|
|
771
|
+
...settingsTransferSection,
|
|
772
|
+
controls: {
|
|
773
|
+
...settingsTransferSection.controls,
|
|
774
|
+
timelineExtended: timelineExtendedControl,
|
|
911
775
|
},
|
|
912
776
|
};
|
|
913
777
|
}
|
|
@@ -1432,7 +1296,7 @@ function normalizePanels({
|
|
|
1432
1296
|
const controls = { ...panels.controls };
|
|
1433
1297
|
const settingsTransferSection = createSettingsTransferSection(settingsTransfer);
|
|
1434
1298
|
const renderScaleControl: ToolcraftControlSchema | undefined =
|
|
1435
|
-
canvas.renderScale.enabled
|
|
1299
|
+
canvas.renderScale.enabled
|
|
1436
1300
|
? {
|
|
1437
1301
|
defaultValue: canvas.renderScale.defaultValue,
|
|
1438
1302
|
description:
|
|
@@ -1453,23 +1317,38 @@ function normalizePanels({
|
|
|
1453
1317
|
variant: "discrete",
|
|
1454
1318
|
}
|
|
1455
1319
|
: undefined;
|
|
1320
|
+
const timelineExtendedControl: ToolcraftControlSchema | undefined =
|
|
1321
|
+
normalizedTimeline?.enabled
|
|
1322
|
+
? {
|
|
1323
|
+
defaultValue: false,
|
|
1324
|
+
description:
|
|
1325
|
+
"Shows the extended runtime timeline with scrubber, duration, loop, and keyframe controls; compact mode keeps only Play visible.",
|
|
1326
|
+
label: "Timeline",
|
|
1327
|
+
target: toolcraftTimelinePanelExtendedTarget,
|
|
1328
|
+
type: "switch",
|
|
1329
|
+
}
|
|
1330
|
+
: undefined;
|
|
1456
1331
|
|
|
1457
1332
|
if (!canvas.enabled || canvas.sizing.mode !== "editable-output") {
|
|
1458
|
-
const runtimeSetupSection =
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1333
|
+
const runtimeSetupSection = renderScaleControl
|
|
1334
|
+
? mergeCanvasRenderScaleIntoSettingsTransferSection({
|
|
1335
|
+
renderScaleControl,
|
|
1336
|
+
settingsTransferSection,
|
|
1337
|
+
timelineExtendedControl,
|
|
1338
|
+
})
|
|
1339
|
+
: timelineExtendedControl
|
|
1340
|
+
? mergeTimelineExtendedIntoSettingsTransferSection({
|
|
1462
1341
|
settingsTransferSection,
|
|
1342
|
+
timelineExtendedControl,
|
|
1463
1343
|
})
|
|
1464
|
-
:
|
|
1465
|
-
(renderScaleControl ? createCanvasRenderScaleSection(renderScaleControl) : null));
|
|
1344
|
+
: settingsTransferSection;
|
|
1466
1345
|
|
|
1467
1346
|
return {
|
|
1468
1347
|
...normalizedPanels,
|
|
1469
1348
|
controls: normalizeControlsPanelLayout({
|
|
1470
1349
|
...controls,
|
|
1471
1350
|
sections: [
|
|
1472
|
-
|
|
1351
|
+
runtimeSetupSection,
|
|
1473
1352
|
...controls.sections,
|
|
1474
1353
|
],
|
|
1475
1354
|
}),
|
|
@@ -1488,59 +1367,36 @@ function normalizePanels({
|
|
|
1488
1367
|
type: "aspectRatio",
|
|
1489
1368
|
};
|
|
1490
1369
|
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
sizeControlIds.push("canvasWidth");
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
if (!hasControlTarget(panels, canvasSizeControlTargets.height)) {
|
|
1505
|
-
sizeControls.canvasHeight = {
|
|
1506
|
-
defaultValue: canvas.size.height,
|
|
1507
|
-
label: "Canvas height",
|
|
1508
|
-
orderRole: "input",
|
|
1509
|
-
performanceReason: "Canvas height changes output dimensions and renderer workload.",
|
|
1510
|
-
performanceRole: "workload",
|
|
1511
|
-
target: canvasSizeControlTargets.height,
|
|
1512
|
-
type: "text",
|
|
1513
|
-
};
|
|
1514
|
-
sizeControlIds.push("canvasHeight");
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
if (Object.keys(sizeControls).length === 0 && !renderScaleControl) {
|
|
1518
|
-
return {
|
|
1519
|
-
...normalizedPanels,
|
|
1520
|
-
controls: normalizeControlsPanelLayout({
|
|
1521
|
-
...controls,
|
|
1522
|
-
sections: [
|
|
1523
|
-
...(settingsTransferSection ? [settingsTransferSection] : []),
|
|
1524
|
-
...controls.sections,
|
|
1525
|
-
],
|
|
1526
|
-
}),
|
|
1527
|
-
};
|
|
1528
|
-
}
|
|
1370
|
+
sizeControls.canvasWidth = {
|
|
1371
|
+
defaultValue: canvas.size.width,
|
|
1372
|
+
label: "Canvas width",
|
|
1373
|
+
orderRole: "input",
|
|
1374
|
+
performanceReason: "Canvas width changes output dimensions and renderer workload.",
|
|
1375
|
+
performanceRole: "workload",
|
|
1376
|
+
target: canvasSizeControlTargets.width,
|
|
1377
|
+
type: "text",
|
|
1378
|
+
};
|
|
1379
|
+
sizeControlIds.push("canvasWidth");
|
|
1529
1380
|
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1381
|
+
sizeControls.canvasHeight = {
|
|
1382
|
+
defaultValue: canvas.size.height,
|
|
1383
|
+
label: "Canvas height",
|
|
1384
|
+
orderRole: "input",
|
|
1385
|
+
performanceReason: "Canvas height changes output dimensions and renderer workload.",
|
|
1386
|
+
performanceRole: "workload",
|
|
1387
|
+
target: canvasSizeControlTargets.height,
|
|
1388
|
+
type: "text",
|
|
1389
|
+
};
|
|
1390
|
+
sizeControlIds.push("canvasHeight");
|
|
1391
|
+
|
|
1392
|
+
const runtimeSettingsSection = mergeCanvasSizeControlsIntoSettingsTransferSection({
|
|
1393
|
+
aspectRatioControl,
|
|
1394
|
+
renderScaleControl,
|
|
1395
|
+
settingsTransferSection,
|
|
1396
|
+
sizeControlIds,
|
|
1397
|
+
sizeControls,
|
|
1398
|
+
timelineExtendedControl,
|
|
1399
|
+
});
|
|
1544
1400
|
|
|
1545
1401
|
return {
|
|
1546
1402
|
...normalizedPanels,
|
|
@@ -1560,7 +1416,11 @@ function resolveTimelinePanel(
|
|
|
1560
1416
|
timeline: ToolcraftTimelinePanelSchema | undefined,
|
|
1561
1417
|
): ResolvedToolcraftTimelinePanelSchema | undefined {
|
|
1562
1418
|
if (timeline === true) {
|
|
1563
|
-
return {
|
|
1419
|
+
return {
|
|
1420
|
+
defaultDurationSeconds: defaultTimelineDurationSeconds,
|
|
1421
|
+
enabled: true,
|
|
1422
|
+
mode: "keyframes",
|
|
1423
|
+
};
|
|
1564
1424
|
}
|
|
1565
1425
|
|
|
1566
1426
|
if (!timeline || timeline.enabled === false) {
|
|
@@ -1568,11 +1428,20 @@ function resolveTimelinePanel(
|
|
|
1568
1428
|
}
|
|
1569
1429
|
|
|
1570
1430
|
return {
|
|
1431
|
+
defaultDurationSeconds: normalizeTimelineDurationSeconds(timeline.defaultDurationSeconds),
|
|
1571
1432
|
enabled: true,
|
|
1572
1433
|
mode: timeline.mode ?? "keyframes",
|
|
1573
1434
|
};
|
|
1574
1435
|
}
|
|
1575
1436
|
|
|
1437
|
+
function normalizeTimelineDurationSeconds(value: unknown): number {
|
|
1438
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1439
|
+
return defaultTimelineDurationSeconds;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
return Math.min(maxTimelineDurationSeconds, Math.max(minTimelineDurationSeconds, value));
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1576
1445
|
function hasVisibleRuntimePanel({
|
|
1577
1446
|
panels,
|
|
1578
1447
|
toolbar,
|
|
@@ -1621,7 +1490,6 @@ export function defineToolcraft(schema: ToolcraftAppSchema): ResolvedToolcraftAp
|
|
|
1621
1490
|
const persistence = resolvePersistence(schema.persistence);
|
|
1622
1491
|
const settingsTransfer = resolveSettingsTransfer({
|
|
1623
1492
|
controls: schema.panels.controls,
|
|
1624
|
-
panels: schema.panels,
|
|
1625
1493
|
persistence,
|
|
1626
1494
|
settingsTransfer: schema.settingsTransfer,
|
|
1627
1495
|
});
|
|
@@ -1646,6 +1514,7 @@ export function defineToolcraft(schema: ToolcraftAppSchema): ResolvedToolcraftAp
|
|
|
1646
1514
|
zoom: schema.toolbar?.zoom ?? canvasEnabled,
|
|
1647
1515
|
};
|
|
1648
1516
|
const exportSchema = resolveExport(schema.export);
|
|
1517
|
+
const media = resolveMedia(schema.media);
|
|
1649
1518
|
|
|
1650
1519
|
assertPanelPersistenceContract({ panels, persistence, toolbar });
|
|
1651
1520
|
|
|
@@ -1657,6 +1526,7 @@ export function defineToolcraft(schema: ToolcraftAppSchema): ResolvedToolcraftAp
|
|
|
1657
1526
|
}),
|
|
1658
1527
|
canvas,
|
|
1659
1528
|
export: exportSchema,
|
|
1529
|
+
media,
|
|
1660
1530
|
panels,
|
|
1661
1531
|
persistence,
|
|
1662
1532
|
settingsTransfer,
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
export const toolcraftTimelinePanelVisibleTarget = "panels.timeline.visible";
|
|
2
|
+
export const toolcraftTimelinePanelExtendedTarget = "panels.timeline.extended";
|
|
3
|
+
|
|
1
4
|
export const toolcraftRuntimeOwnedTargets = [
|
|
2
5
|
"canvas.aspectRatio",
|
|
3
6
|
"canvas.renderScale",
|
|
4
7
|
"canvas.size.width",
|
|
5
8
|
"canvas.size.height",
|
|
9
|
+
"runtime.settingsTransfer",
|
|
10
|
+
toolcraftTimelinePanelExtendedTarget,
|
|
11
|
+
toolcraftTimelinePanelVisibleTarget,
|
|
6
12
|
] as const;
|
|
7
13
|
|
|
8
14
|
export const toolcraftReservedTargets = [
|
|
@@ -33,6 +39,21 @@ export function isToolcraftCanvasAspectRatioTarget(target: string): boolean {
|
|
|
33
39
|
return target === "canvas.aspectRatio";
|
|
34
40
|
}
|
|
35
41
|
|
|
42
|
+
export function isToolcraftTimelinePanelVisibleTarget(target: string): boolean {
|
|
43
|
+
return target === toolcraftTimelinePanelVisibleTarget;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function isToolcraftTimelinePanelExtendedTarget(target: string): boolean {
|
|
47
|
+
return target === toolcraftTimelinePanelExtendedTarget;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function isToolcraftTimelinePanelRuntimeTarget(target: string): boolean {
|
|
51
|
+
return (
|
|
52
|
+
isToolcraftTimelinePanelExtendedTarget(target) ||
|
|
53
|
+
isToolcraftTimelinePanelVisibleTarget(target)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
36
57
|
export function isToolcraftReservedTarget(
|
|
37
58
|
target: string,
|
|
38
59
|
): target is ToolcraftReservedTarget {
|
|
@@ -110,7 +110,9 @@ export type ToolcraftAssemblyCommand =
|
|
|
110
110
|
| "media.delete"
|
|
111
111
|
| "media.import"
|
|
112
112
|
| "media.reorder"
|
|
113
|
+
| "media.transform"
|
|
113
114
|
| "panels.resetOffset"
|
|
115
|
+
| "panels.setHidden"
|
|
114
116
|
| "panels.setOffset"
|
|
115
117
|
| "timeline.changeKeyframeEasing"
|
|
116
118
|
| "timeline.deleteControlKeyframes"
|
|
@@ -180,11 +182,13 @@ export type ToolcraftTimelineMode = "keyframes" | "playback";
|
|
|
180
182
|
export type ToolcraftTimelinePanelSchema =
|
|
181
183
|
| boolean
|
|
182
184
|
| {
|
|
185
|
+
defaultDurationSeconds?: number;
|
|
183
186
|
enabled?: boolean;
|
|
184
187
|
mode?: ToolcraftTimelineMode;
|
|
185
188
|
};
|
|
186
189
|
|
|
187
190
|
export type ResolvedToolcraftTimelinePanelSchema = {
|
|
191
|
+
defaultDurationSeconds: number;
|
|
188
192
|
enabled: boolean;
|
|
189
193
|
mode: ToolcraftTimelineMode;
|
|
190
194
|
};
|
|
@@ -192,6 +196,7 @@ export type ResolvedToolcraftTimelinePanelSchema = {
|
|
|
192
196
|
export type ToolcraftPersistableStateSlice =
|
|
193
197
|
| "canvas"
|
|
194
198
|
| "layers"
|
|
199
|
+
| "media"
|
|
195
200
|
| "panels"
|
|
196
201
|
| "timeline"
|
|
197
202
|
| "values";
|
|
@@ -247,6 +252,7 @@ export type ToolcraftActionSchema = {
|
|
|
247
252
|
| "export"
|
|
248
253
|
| "rotate-ccw"
|
|
249
254
|
| "shuffle"
|
|
255
|
+
| "upload-simple"
|
|
250
256
|
| "wand-sparkles";
|
|
251
257
|
label?: string;
|
|
252
258
|
value: string;
|
|
@@ -276,6 +282,39 @@ export type ToolcraftControlPerformanceRole =
|
|
|
276
282
|
|
|
277
283
|
export type ToolcraftFileDropAssetKind = "file" | "image";
|
|
278
284
|
|
|
285
|
+
export type ToolcraftMediaPositionSchema = {
|
|
286
|
+
x: number;
|
|
287
|
+
y: number;
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
export type ToolcraftMediaTransformSchema = {
|
|
291
|
+
flipHorizontal?: boolean;
|
|
292
|
+
flipVertical?: boolean;
|
|
293
|
+
rotationDeg?: 0 | 90 | 180 | 270;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
export type ToolcraftDefaultMediaAssetSchema = {
|
|
297
|
+
assetKind?: ToolcraftFileDropAssetKind;
|
|
298
|
+
dataUrl: string;
|
|
299
|
+
fileName: string;
|
|
300
|
+
id?: string;
|
|
301
|
+
layerId?: string;
|
|
302
|
+
layerName?: string;
|
|
303
|
+
mimeType?: string;
|
|
304
|
+
position?: ToolcraftMediaPositionSchema;
|
|
305
|
+
size?: ToolcraftCanvasSize;
|
|
306
|
+
sourceTarget?: string;
|
|
307
|
+
transform?: ToolcraftMediaTransformSchema;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
export type ToolcraftMediaSchema = {
|
|
311
|
+
defaultAssets?: readonly ToolcraftDefaultMediaAssetSchema[];
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
export type ResolvedToolcraftMediaSchema = {
|
|
315
|
+
defaultAssets: readonly ToolcraftDefaultMediaAssetSchema[];
|
|
316
|
+
};
|
|
317
|
+
|
|
279
318
|
export type ToolcraftControlConditionSchema = {
|
|
280
319
|
equals?: unknown;
|
|
281
320
|
greaterThan?: number;
|
|
@@ -401,6 +440,7 @@ export type ResolvedToolcraftPanelsSchema = {
|
|
|
401
440
|
export type ToolcraftAppSchema = {
|
|
402
441
|
canvas: ToolcraftCanvasSchema;
|
|
403
442
|
export?: ToolcraftExportSchema;
|
|
443
|
+
media?: ToolcraftMediaSchema;
|
|
404
444
|
panels: ToolcraftPanelsSchema;
|
|
405
445
|
persistence?: ToolcraftPersistenceSchema;
|
|
406
446
|
settingsTransfer?: ToolcraftSettingsTransferSchema;
|
|
@@ -415,6 +455,7 @@ export type ResolvedToolcraftAppSchema = {
|
|
|
415
455
|
sizeSource: ToolcraftCanvasSizeSource;
|
|
416
456
|
};
|
|
417
457
|
export: ResolvedToolcraftExportSchema;
|
|
458
|
+
media: ResolvedToolcraftMediaSchema;
|
|
418
459
|
panels: ResolvedToolcraftPanelsSchema;
|
|
419
460
|
persistence: ResolvedToolcraftPersistenceSchema;
|
|
420
461
|
settingsTransfer: ResolvedToolcraftSettingsTransferSchema;
|