@pixel-point/toolcraft 0.0.8 → 0.0.11
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 +1 -1
- package/src/generate.mjs +42 -5
- package/src/generate.test.mjs +40 -0
- package/src/package-json.mjs +15 -0
- package/src/package-json.test.mjs +14 -1
- package/templates/runtime/contracts/component-contracts.test.ts +251 -47
- package/templates/runtime/contracts/component-contracts.ts +130 -57
- package/templates/runtime/contracts/decision-contracts.test.ts +38 -1
- package/templates/runtime/contracts/decision-contracts.ts +22 -9
- 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 +916 -108
- package/templates/runtime/react/controls-panel.tsx +136 -30
- 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 +266 -170
- package/templates/runtime/schema/define-toolcraft.ts +140 -246
- package/templates/runtime/schema/runtime-targets.ts +21 -0
- package/templates/runtime/schema/types.ts +44 -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 +810 -21
- package/templates/runtime/testing/performance.ts +823 -53
- package/templates/starter/AGENTS.md +24 -18
- package/templates/starter/docs/toolcraft/README.md +8 -4
- package/templates/starter/docs/toolcraft/acceptance-testing.md +43 -10
- package/templates/starter/docs/toolcraft/agent-worklog.md +1 -0
- package/templates/starter/docs/toolcraft/assembly-workflow.md +55 -20
- package/templates/starter/docs/toolcraft/component-rules.md +75 -42
- package/templates/starter/docs/toolcraft/decision-contract.md +2 -0
- package/templates/starter/docs/toolcraft/performance.md +37 -11
- package/templates/starter/docs/toolcraft/schema-reference.md +171 -41
- 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 +55 -2
- package/templates/starter/e2e/performance-helpers.ts +45 -0
- package/templates/starter/gitignore +1 -0
- package/templates/starter/index.html +1 -0
- package/templates/starter/package.json +3 -0
- 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 +114 -13
- package/templates/starter/scripts/toolcraft-port.mjs +280 -0
- package/templates/starter/scripts/toolcraft-port.test.mjs +207 -1
- package/templates/starter/src/app/starter-acceptance.test.ts +3412 -479
- package/templates/starter/src/app/starter-acceptance.ts +1453 -97
- package/templates/starter/src/app/starter-performance.test.ts +111 -7
- 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/control-layout/index.tsx +8 -3
- package/templates/ui/components/controls/actions/actions-control.tsx +56 -5
- package/templates/ui/components/controls/code-textarea/code-textarea-control.tsx +7 -3
- package/templates/ui/components/controls/color/index.ts +4 -1
- package/templates/ui/components/controls/color/palette-control.tsx +34 -4
- package/templates/ui/components/controls/color/style-guide-color-picker-logic.ts +7 -2
- package/templates/ui/components/controls/color/style-guide-color-picker.tsx +2 -2
- 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 +4 -0
- package/templates/ui/components/controls/range-input/range-input-control.tsx +12 -4
- package/templates/ui/components/controls/range-slider/range-slider-value.ts +3 -1
- package/templates/ui/components/controls/select/select-control.tsx +8 -25
- package/templates/ui/components/controls/slider/slider-value.ts +0 -1
- package/templates/ui/components/controls/text-input/text-input-control.tsx +4 -1
- package/templates/ui/components/controls/vector/index.ts +1 -0
- package/templates/ui/components/controls/vector/vector-control.tsx +109 -12
- package/templates/ui/components/panel/panel-actions.tsx +1 -1
- package/templates/ui/components/panel/panel-section.tsx +29 -5
|
@@ -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" {
|
|
@@ -524,6 +509,10 @@ function getControlSectionLayout(
|
|
|
524
509
|
control: ToolcraftControlSchema,
|
|
525
510
|
entries: readonly [string, ToolcraftControlSchema][],
|
|
526
511
|
): "grouped" | "standalone" {
|
|
512
|
+
if (isControlGatedBySameSectionControl(control, entries)) {
|
|
513
|
+
return "grouped";
|
|
514
|
+
}
|
|
515
|
+
|
|
527
516
|
if (
|
|
528
517
|
(control.type === "color" || control.type === "colorOpacity") &&
|
|
529
518
|
entries.some(
|
|
@@ -539,6 +528,24 @@ function getControlSectionLayout(
|
|
|
539
528
|
return getControlDefaultSectionLayout(control);
|
|
540
529
|
}
|
|
541
530
|
|
|
531
|
+
function isControlGatedBySameSectionControl(
|
|
532
|
+
control: ToolcraftControlSchema,
|
|
533
|
+
entries: readonly [string, ToolcraftControlSchema][],
|
|
534
|
+
): boolean {
|
|
535
|
+
const gateTargets = [control.visibleWhen?.target, control.disabledWhen?.target].filter(
|
|
536
|
+
(target): target is string => Boolean(target),
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
if (gateTargets.length === 0) {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return entries.some(([, entryControl]) =>
|
|
544
|
+
gateTargets.includes(entryControl.target) &&
|
|
545
|
+
getControlDefaultSectionLayout(entryControl) === "grouped",
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
|
|
542
549
|
function createControlsRecord(
|
|
543
550
|
entries: readonly [string, ToolcraftControlSchema][],
|
|
544
551
|
): Record<string, ToolcraftControlSchema> {
|
|
@@ -615,116 +622,12 @@ function isPanelActionsControl(control: ToolcraftControlSchema): boolean {
|
|
|
615
622
|
return control.type === "panelActions";
|
|
616
623
|
}
|
|
617
624
|
|
|
618
|
-
function isSettingsTransferControl(control: ToolcraftControlSchema): boolean {
|
|
619
|
-
return control.type === "settingsTransfer";
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
function isRuntimeOnlyActionControl(control: ToolcraftControlSchema): boolean {
|
|
623
|
-
return isPanelActionsControl(control) || isSettingsTransferControl(control);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
function isSettingsTransferEligibilityControl(control: ToolcraftControlSchema): boolean {
|
|
627
|
-
return (
|
|
628
|
-
!isRuntimeOnlyActionControl(control) &&
|
|
629
|
-
control.target !== canvasAspectRatioTarget &&
|
|
630
|
-
control.target !== canvasSizeControlTargets.width &&
|
|
631
|
-
control.target !== canvasSizeControlTargets.height
|
|
632
|
-
);
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
function getSettingsTransferComplexityScore({
|
|
636
|
-
panels,
|
|
637
|
-
}: {
|
|
638
|
-
panels: ToolcraftAppSchema["panels"];
|
|
639
|
-
}): {
|
|
640
|
-
controlCount: number;
|
|
641
|
-
score: number;
|
|
642
|
-
sectionCount: number;
|
|
643
|
-
} {
|
|
644
|
-
const sections = panels.controls?.sections ?? [];
|
|
645
|
-
let controlCount = 0;
|
|
646
|
-
let score = 0;
|
|
647
|
-
|
|
648
|
-
for (const section of sections) {
|
|
649
|
-
for (const control of Object.values(section.controls)) {
|
|
650
|
-
if (!isSettingsTransferEligibilityControl(control)) {
|
|
651
|
-
continue;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
controlCount += 1;
|
|
655
|
-
score += settingsTransferHeavyControlTypes.has(control.type) ? 3 : 1;
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
const sectionCount = sections.filter((section) =>
|
|
660
|
-
Object.values(section.controls).some((control) =>
|
|
661
|
-
isSettingsTransferEligibilityControl(control),
|
|
662
|
-
),
|
|
663
|
-
).length;
|
|
664
|
-
|
|
665
|
-
score += Math.max(0, sectionCount - 4);
|
|
666
|
-
|
|
667
|
-
if (
|
|
668
|
-
panels.timeline === true ||
|
|
669
|
-
(typeof panels.timeline === "object" && panels.timeline.enabled !== false)
|
|
670
|
-
) {
|
|
671
|
-
score += 2;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
if (panels.layers) {
|
|
675
|
-
score += 2;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
return { controlCount, score, sectionCount };
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
export function getToolcraftSettingsTransferEligibility({
|
|
682
|
-
panels,
|
|
683
|
-
}: {
|
|
684
|
-
panels: ToolcraftAppSchema["panels"];
|
|
685
|
-
}): ToolcraftSettingsTransferEligibility {
|
|
686
|
-
const { controlCount, score, sectionCount } = getSettingsTransferComplexityScore({
|
|
687
|
-
panels,
|
|
688
|
-
});
|
|
689
|
-
const reasons: ToolcraftSettingsTransferEligibilityReason[] = [];
|
|
690
|
-
|
|
691
|
-
if (controlCount >= 12) {
|
|
692
|
-
reasons.push("control-count");
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
if (sectionCount >= 5) {
|
|
696
|
-
reasons.push("section-count");
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
if (score >= 18) {
|
|
700
|
-
reasons.push("score");
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
return {
|
|
704
|
-
controlCount,
|
|
705
|
-
eligible: reasons.length > 0,
|
|
706
|
-
reasons,
|
|
707
|
-
score,
|
|
708
|
-
sectionCount,
|
|
709
|
-
};
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
function shouldAutoEnableSettingsTransfer({
|
|
713
|
-
panels,
|
|
714
|
-
}: {
|
|
715
|
-
panels: ToolcraftAppSchema["panels"];
|
|
716
|
-
}): boolean {
|
|
717
|
-
return getToolcraftSettingsTransferEligibility({ panels }).eligible;
|
|
718
|
-
}
|
|
719
|
-
|
|
720
625
|
function resolveSettingsTransfer({
|
|
721
626
|
controls,
|
|
722
|
-
panels,
|
|
723
627
|
persistence,
|
|
724
628
|
settingsTransfer,
|
|
725
629
|
}: {
|
|
726
630
|
controls: ToolcraftControlsPanelSchema | undefined;
|
|
727
|
-
panels: ToolcraftAppSchema["panels"];
|
|
728
631
|
persistence: ResolvedToolcraftAppSchema["persistence"];
|
|
729
632
|
settingsTransfer: ToolcraftSettingsTransferSchema | undefined;
|
|
730
633
|
}): ResolvedToolcraftSettingsTransferSchema {
|
|
@@ -737,9 +640,7 @@ function resolveSettingsTransfer({
|
|
|
737
640
|
|
|
738
641
|
return {
|
|
739
642
|
appId,
|
|
740
|
-
enabled:
|
|
741
|
-
Boolean(controls) &&
|
|
742
|
-
(mode === "auto" ? shouldAutoEnableSettingsTransfer({ panels }) : mode),
|
|
643
|
+
enabled: Boolean(controls),
|
|
743
644
|
fileName: getSettingsTransferFileName({ appId, settingsTransfer }),
|
|
744
645
|
mode,
|
|
745
646
|
};
|
|
@@ -747,11 +648,7 @@ function resolveSettingsTransfer({
|
|
|
747
648
|
|
|
748
649
|
function createSettingsTransferSection(
|
|
749
650
|
settingsTransfer: ResolvedToolcraftSettingsTransferSchema,
|
|
750
|
-
): ToolcraftControlSectionSchema
|
|
751
|
-
if (!settingsTransfer.enabled) {
|
|
752
|
-
return null;
|
|
753
|
-
}
|
|
754
|
-
|
|
651
|
+
): ToolcraftControlSectionSchema {
|
|
755
652
|
return {
|
|
756
653
|
controls: {
|
|
757
654
|
settingsTransfer: {
|
|
@@ -808,51 +705,20 @@ function getCanvasAspectRatioDefaultValue(size: ToolcraftCanvasSize): {
|
|
|
808
705
|
};
|
|
809
706
|
}
|
|
810
707
|
|
|
811
|
-
function createCanvasSizeSection({
|
|
812
|
-
aspectRatioControl,
|
|
813
|
-
renderScaleControl,
|
|
814
|
-
sizeControlIds,
|
|
815
|
-
sizeControls,
|
|
816
|
-
}: {
|
|
817
|
-
aspectRatioControl: ToolcraftControlSchema;
|
|
818
|
-
renderScaleControl?: ToolcraftControlSchema;
|
|
819
|
-
sizeControlIds: readonly string[];
|
|
820
|
-
sizeControls: ToolcraftControlSectionSchema["controls"];
|
|
821
|
-
}): ToolcraftControlSectionSchema {
|
|
822
|
-
return {
|
|
823
|
-
controls: {
|
|
824
|
-
canvasAspectRatio: aspectRatioControl,
|
|
825
|
-
...sizeControls,
|
|
826
|
-
...(renderScaleControl ? { canvasRenderScale: renderScaleControl } : {}),
|
|
827
|
-
},
|
|
828
|
-
layoutGroups: getCanvasSizeLayoutGroups(sizeControlIds),
|
|
829
|
-
title: runtimeSetupSectionTitle,
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
function createCanvasRenderScaleSection(
|
|
834
|
-
renderScaleControl: ToolcraftControlSchema,
|
|
835
|
-
): ToolcraftControlSectionSchema {
|
|
836
|
-
return {
|
|
837
|
-
controls: {
|
|
838
|
-
canvasRenderScale: renderScaleControl,
|
|
839
|
-
},
|
|
840
|
-
title: runtimeSetupSectionTitle,
|
|
841
|
-
};
|
|
842
|
-
}
|
|
843
|
-
|
|
844
708
|
function mergeCanvasSizeControlsIntoSettingsTransferSection({
|
|
845
709
|
aspectRatioControl,
|
|
846
710
|
renderScaleControl,
|
|
847
711
|
settingsTransferSection,
|
|
848
712
|
sizeControlIds,
|
|
849
713
|
sizeControls,
|
|
714
|
+
timelineExtendedControl,
|
|
850
715
|
}: {
|
|
851
716
|
aspectRatioControl: ToolcraftControlSchema;
|
|
852
717
|
renderScaleControl?: ToolcraftControlSchema;
|
|
853
718
|
settingsTransferSection: ToolcraftControlSectionSchema;
|
|
854
719
|
sizeControlIds: readonly string[];
|
|
855
720
|
sizeControls: ToolcraftControlSectionSchema["controls"];
|
|
721
|
+
timelineExtendedControl?: ToolcraftControlSchema;
|
|
856
722
|
}): ToolcraftControlSectionSchema {
|
|
857
723
|
const canvasSizeLayoutGroups = getCanvasSizeLayoutGroups(sizeControlIds) ?? [];
|
|
858
724
|
|
|
@@ -863,6 +729,7 @@ function mergeCanvasSizeControlsIntoSettingsTransferSection({
|
|
|
863
729
|
canvasAspectRatio: aspectRatioControl,
|
|
864
730
|
...sizeControls,
|
|
865
731
|
...(renderScaleControl ? { canvasRenderScale: renderScaleControl } : {}),
|
|
732
|
+
...(timelineExtendedControl ? { timelineExtended: timelineExtendedControl } : {}),
|
|
866
733
|
},
|
|
867
734
|
layoutGroups:
|
|
868
735
|
canvasSizeLayoutGroups.length > 0 || settingsTransferSection.layoutGroups?.length
|
|
@@ -877,15 +744,34 @@ function mergeCanvasSizeControlsIntoSettingsTransferSection({
|
|
|
877
744
|
function mergeCanvasRenderScaleIntoSettingsTransferSection({
|
|
878
745
|
renderScaleControl,
|
|
879
746
|
settingsTransferSection,
|
|
747
|
+
timelineExtendedControl,
|
|
880
748
|
}: {
|
|
881
749
|
renderScaleControl: ToolcraftControlSchema;
|
|
882
750
|
settingsTransferSection: ToolcraftControlSectionSchema;
|
|
751
|
+
timelineExtendedControl?: ToolcraftControlSchema;
|
|
883
752
|
}): ToolcraftControlSectionSchema {
|
|
884
753
|
return {
|
|
885
754
|
...settingsTransferSection,
|
|
886
755
|
controls: {
|
|
887
756
|
...settingsTransferSection.controls,
|
|
888
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,
|
|
889
775
|
},
|
|
890
776
|
};
|
|
891
777
|
}
|
|
@@ -1410,7 +1296,7 @@ function normalizePanels({
|
|
|
1410
1296
|
const controls = { ...panels.controls };
|
|
1411
1297
|
const settingsTransferSection = createSettingsTransferSection(settingsTransfer);
|
|
1412
1298
|
const renderScaleControl: ToolcraftControlSchema | undefined =
|
|
1413
|
-
canvas.renderScale.enabled
|
|
1299
|
+
canvas.renderScale.enabled
|
|
1414
1300
|
? {
|
|
1415
1301
|
defaultValue: canvas.renderScale.defaultValue,
|
|
1416
1302
|
description:
|
|
@@ -1428,27 +1314,41 @@ function normalizePanels({
|
|
|
1428
1314
|
step: canvas.renderScale.step,
|
|
1429
1315
|
target: canvasRenderScaleTarget,
|
|
1430
1316
|
type: "slider",
|
|
1431
|
-
unit: "x",
|
|
1432
1317
|
variant: "discrete",
|
|
1433
1318
|
}
|
|
1434
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;
|
|
1435
1331
|
|
|
1436
1332
|
if (!canvas.enabled || canvas.sizing.mode !== "editable-output") {
|
|
1437
|
-
const runtimeSetupSection =
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1333
|
+
const runtimeSetupSection = renderScaleControl
|
|
1334
|
+
? mergeCanvasRenderScaleIntoSettingsTransferSection({
|
|
1335
|
+
renderScaleControl,
|
|
1336
|
+
settingsTransferSection,
|
|
1337
|
+
timelineExtendedControl,
|
|
1338
|
+
})
|
|
1339
|
+
: timelineExtendedControl
|
|
1340
|
+
? mergeTimelineExtendedIntoSettingsTransferSection({
|
|
1441
1341
|
settingsTransferSection,
|
|
1342
|
+
timelineExtendedControl,
|
|
1442
1343
|
})
|
|
1443
|
-
:
|
|
1444
|
-
(renderScaleControl ? createCanvasRenderScaleSection(renderScaleControl) : null));
|
|
1344
|
+
: settingsTransferSection;
|
|
1445
1345
|
|
|
1446
1346
|
return {
|
|
1447
1347
|
...normalizedPanels,
|
|
1448
1348
|
controls: normalizeControlsPanelLayout({
|
|
1449
1349
|
...controls,
|
|
1450
1350
|
sections: [
|
|
1451
|
-
|
|
1351
|
+
runtimeSetupSection,
|
|
1452
1352
|
...controls.sections,
|
|
1453
1353
|
],
|
|
1454
1354
|
}),
|
|
@@ -1460,63 +1360,43 @@ function normalizePanels({
|
|
|
1460
1360
|
const aspectRatioControl: ToolcraftControlSchema = {
|
|
1461
1361
|
defaultValue: getCanvasAspectRatioDefaultValue(canvas.size),
|
|
1462
1362
|
label: "Aspect ratio",
|
|
1363
|
+
orderRole: "input",
|
|
1463
1364
|
performanceReason: "Aspect ratio changes output dimensions and renderer workload.",
|
|
1464
1365
|
performanceRole: "workload",
|
|
1465
1366
|
target: canvasAspectRatioTarget,
|
|
1466
1367
|
type: "aspectRatio",
|
|
1467
1368
|
};
|
|
1468
1369
|
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
if (!hasControlTarget(panels, canvasSizeControlTargets.height)) {
|
|
1482
|
-
sizeControls.canvasHeight = {
|
|
1483
|
-
defaultValue: canvas.size.height,
|
|
1484
|
-
label: "Canvas height",
|
|
1485
|
-
performanceReason: "Canvas height changes output dimensions and renderer workload.",
|
|
1486
|
-
performanceRole: "workload",
|
|
1487
|
-
target: canvasSizeControlTargets.height,
|
|
1488
|
-
type: "text",
|
|
1489
|
-
};
|
|
1490
|
-
sizeControlIds.push("canvasHeight");
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
if (Object.keys(sizeControls).length === 0 && !renderScaleControl) {
|
|
1494
|
-
return {
|
|
1495
|
-
...normalizedPanels,
|
|
1496
|
-
controls: normalizeControlsPanelLayout({
|
|
1497
|
-
...controls,
|
|
1498
|
-
sections: [
|
|
1499
|
-
...(settingsTransferSection ? [settingsTransferSection] : []),
|
|
1500
|
-
...controls.sections,
|
|
1501
|
-
],
|
|
1502
|
-
}),
|
|
1503
|
-
};
|
|
1504
|
-
}
|
|
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");
|
|
1505
1380
|
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
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
|
+
});
|
|
1520
1400
|
|
|
1521
1401
|
return {
|
|
1522
1402
|
...normalizedPanels,
|
|
@@ -1536,7 +1416,11 @@ function resolveTimelinePanel(
|
|
|
1536
1416
|
timeline: ToolcraftTimelinePanelSchema | undefined,
|
|
1537
1417
|
): ResolvedToolcraftTimelinePanelSchema | undefined {
|
|
1538
1418
|
if (timeline === true) {
|
|
1539
|
-
return {
|
|
1419
|
+
return {
|
|
1420
|
+
defaultDurationSeconds: defaultTimelineDurationSeconds,
|
|
1421
|
+
enabled: true,
|
|
1422
|
+
mode: "keyframes",
|
|
1423
|
+
};
|
|
1540
1424
|
}
|
|
1541
1425
|
|
|
1542
1426
|
if (!timeline || timeline.enabled === false) {
|
|
@@ -1544,11 +1428,20 @@ function resolveTimelinePanel(
|
|
|
1544
1428
|
}
|
|
1545
1429
|
|
|
1546
1430
|
return {
|
|
1431
|
+
defaultDurationSeconds: normalizeTimelineDurationSeconds(timeline.defaultDurationSeconds),
|
|
1547
1432
|
enabled: true,
|
|
1548
1433
|
mode: timeline.mode ?? "keyframes",
|
|
1549
1434
|
};
|
|
1550
1435
|
}
|
|
1551
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
|
+
|
|
1552
1445
|
function hasVisibleRuntimePanel({
|
|
1553
1446
|
panels,
|
|
1554
1447
|
toolbar,
|
|
@@ -1597,7 +1490,6 @@ export function defineToolcraft(schema: ToolcraftAppSchema): ResolvedToolcraftAp
|
|
|
1597
1490
|
const persistence = resolvePersistence(schema.persistence);
|
|
1598
1491
|
const settingsTransfer = resolveSettingsTransfer({
|
|
1599
1492
|
controls: schema.panels.controls,
|
|
1600
|
-
panels: schema.panels,
|
|
1601
1493
|
persistence,
|
|
1602
1494
|
settingsTransfer: schema.settingsTransfer,
|
|
1603
1495
|
});
|
|
@@ -1622,6 +1514,7 @@ export function defineToolcraft(schema: ToolcraftAppSchema): ResolvedToolcraftAp
|
|
|
1622
1514
|
zoom: schema.toolbar?.zoom ?? canvasEnabled,
|
|
1623
1515
|
};
|
|
1624
1516
|
const exportSchema = resolveExport(schema.export);
|
|
1517
|
+
const media = resolveMedia(schema.media);
|
|
1625
1518
|
|
|
1626
1519
|
assertPanelPersistenceContract({ panels, persistence, toolbar });
|
|
1627
1520
|
|
|
@@ -1633,6 +1526,7 @@ export function defineToolcraft(schema: ToolcraftAppSchema): ResolvedToolcraftAp
|
|
|
1633
1526
|
}),
|
|
1634
1527
|
canvas,
|
|
1635
1528
|
export: exportSchema,
|
|
1529
|
+
media,
|
|
1636
1530
|
panels,
|
|
1637
1531
|
persistence,
|
|
1638
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 {
|