import { describe, expect, it } from "vitest"; import { timelineModule } from "../modules/built-ins/timeline/timeline-module"; import { defineToolcraft } from "./define-toolcraft"; import { createSliderControls, getProductSections, } from "./define-toolcraft-test-utils"; describe("defineToolcraft runtime setup settings transfer", () => { it("uses canonical identity for settings transfer independently of controls title", () => { const createApp = (controlsTitle: string) => defineToolcraft({ base: { canvas: { enabled: true }, identity: { id: "legacy-settings-id", title: "My Visual Tool", }, panels: { controls: { sections: [], title: controlsTitle, }, }, persistence: { storage: "localStorage", }, settingsTransfer: {}, }, modules: [], }); const first = createApp("First Controls Title"); const renamed = createApp("Renamed Controls Title"); expect(first.identity).toEqual({ id: "legacy-settings-id", title: "My Visual Tool", }); expect(first.settingsTransfer.appId).toBe(first.identity.id); expect(first.settingsTransfer.appId).not.toBe("persistence-slug"); expect(first.settingsTransfer.appId).not.toBe("first-controls-title"); expect(renamed.identity).toEqual(first.identity); expect(renamed.settingsTransfer.appId).toBe(first.settingsTransfer.appId); }); it("keeps settings transfer visible for small auto schemas", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true, size: { height: 320, unit: "px", width: 320 }, }, panels: { controls: { sections: [ { id: "test-section-1", controls: { opacity: { applicability: { mode: "always" as const }, defaultValue: 75, label: "Opacity", target: "style.opacity", type: "slider", }, }, title: "Style", }, ], title: "Mini App", }, }, }, modules: [], }); expect(app.settingsTransfer).toMatchObject({ appId: "runtime-test", enabled: true, fileName: "runtime-test-settings.json", mode: "auto", }); expect(app.panels.controls?.sections[1]?.title).toBe("Settings"); expect( app.panels.controls?.sections[0]?.controls.settingsTransfer, ).toMatchObject({ applicability: { mode: "always" as const }, target: "runtime.settingsTransfer", type: "settingsTransfer", }); expect( app.panels.controls?.sections[1]?.controls.canvasAspectRatio, ).toMatchObject({ applicability: { mode: "conditional" as const }, target: "canvas.aspectRatio", type: "aspectRatio", }); expect(app.panels.controls?.sections[1]?.controls.canvasWidth).toBeTruthy(); }); it("keeps runtime setup controls outside product sections", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true, size: { height: 720, unit: "px", width: 1280 }, }, panels: { controls: { sections: [ { id: "test-section-2", controls: createSliderControls(10), title: "Product Controls", }, ], title: "Runtime Size App", }, }, settingsTransfer: false, }, modules: [], }); expect(app.settingsTransfer.enabled).toBe(true); expect( app.panels.controls?.sections[0]?.controls.settingsTransfer, ).toMatchObject({ applicability: { mode: "always" as const }, target: "runtime.settingsTransfer", type: "settingsTransfer", }); expect( app.panels.controls?.sections[1]?.controls.canvasAspectRatio, ).toBeTruthy(); expect(app.panels.controls?.sections[1]?.controls.canvasWidth).toBeTruthy(); expect( app.panels.controls?.sections[1]?.controls.canvasHeight, ).toBeTruthy(); expect(getProductSections(app)).toHaveLength(1); expect(getProductSections(app)[0]?.title).toBe("Product Controls"); }); it("injects settings transfer as the first section for complex auto schemas", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true, size: { height: 720, unit: "px", width: 1280 }, }, panels: { controls: { sections: [ { id: "test-section-3", controls: { prompt: { applicability: { mode: "always" as const }, defaultValue: "Prompt", label: "Prompt", target: "generation.prompt", type: "text", }, mask: { applicability: { mode: "always" as const }, defaultValue: "Mask", label: "Mask", target: "generation.mask", type: "code", }, font: { applicability: { mode: "always" as const }, defaultValue: { fontId: "inter" }, label: "Font", target: "typography.font", type: "fontPicker", }, gradient: { applicability: { mode: "always" as const }, label: "Gradient", target: "style.gradient", type: "gradient", }, }, title: "Text", }, { id: "test-section-4", controls: { opacity: { applicability: { mode: "always" as const }, defaultValue: 80, target: "style.opacity", type: "slider", }, blur: { applicability: { mode: "always" as const }, defaultValue: 4, target: "style.blur", type: "slider", }, threshold: { applicability: { mode: "always" as const }, defaultValue: 50, target: "style.threshold", type: "slider", }, seed: { applicability: { mode: "always" as const }, defaultValue: "123", target: "style.seed", type: "text", }, spacing: { applicability: { mode: "always" as const }, defaultValue: 8, target: "style.spacing", type: "slider", }, density: { applicability: { mode: "always" as const }, defaultValue: 16, target: "style.density", type: "slider", }, speed: { applicability: { mode: "always" as const }, defaultValue: 1, target: "style.speed", type: "slider", }, }, title: "Style", }, { id: "test-section-5", controls: { anchor: { applicability: { mode: "always" as const }, defaultValue: "center", target: "layout.anchor", type: "anchorGrid", }, }, layout: "standalone", title: "Layout", }, ], title: "Complex App", }, }, }, modules: [ timelineModule({ mode: "playback", }), ], }); const [defaultsSection, runtimeSettingsSection, firstProductSection] = app.panels.controls?.sections ?? []; expect(app.settingsTransfer.enabled).toBe(true); expect(runtimeSettingsSection?.title).toBe("Settings"); expect(Object.keys(runtimeSettingsSection?.controls ?? {})).toEqual([ "infinityCanvas", "workspaceBackground", "canvasAspectRatio", "canvasWidth", "canvasHeight", "timelineExtended", ]); expect(runtimeSettingsSection?.controls.canvasAspectRatio).toMatchObject({ applicability: { mode: "conditional" as const }, defaultValue: { height: 9, mode: "preset", value: "16:9", width: 16, }, target: "canvas.aspectRatio", type: "aspectRatio", }); expect(runtimeSettingsSection?.controls.canvasWidth).toBeTruthy(); expect(runtimeSettingsSection?.controls.canvasHeight).toBeTruthy(); expect(defaultsSection?.controls.settingsTransfer?.type).toBe("settingsTransfer"); expect(runtimeSettingsSection?.layout).toBe("standalone"); expect(runtimeSettingsSection?.layoutGroups).toEqual([ { columns: 2, controls: ["canvasWidth", "canvasHeight"], layout: "inline", }, ]); expect(firstProductSection?.title).toBe("Text"); }); it("uses legacy settings transfer naming as an identity seed while keeping setup mandatory", () => { const forced = defineToolcraft({ base: { identity: { id: "Tiny Tool Presets", title: "Toolcraft App" }, canvas: { enabled: false }, panels: { controls: { sections: [], title: "Tiny Tool", }, }, settingsTransfer: { additionalValueTargets: [ " composition.layout ", "composition.layout", "", "composition.selection", ], enabled: true, fileName: "tiny-presets", }, }, modules: [], }); const disabled = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true, size: { height: 720, unit: "px", width: 1280 }, }, panels: { controls: { sections: [ { id: "test-section-6", controls: Object.fromEntries( Array.from({ length: 12 }, (_, index) => [ `control${index}`, { applicability: { mode: "always" as const }, defaultValue: index, target: `values.control${index}`, type: "slider", }, ]), ), title: "Many Controls", }, ], title: "Heavy Tool", }, }, settingsTransfer: false, }, modules: [], }); expect(forced.settingsTransfer).toMatchObject({ additionalValueTargets: ["composition.layout", "composition.selection"], appId: "tiny-tool-presets", enabled: true, fileName: "tiny-presets.json", }); expect( Object.isFrozen(forced.settingsTransfer.additionalValueTargets), ).toBe(true); expect( forced.panels.controls?.sections[0]?.controls.settingsTransfer?.type, ).toBe("settingsTransfer"); expect(disabled.settingsTransfer).toMatchObject({ additionalValueTargets: [], enabled: true, mode: false, }); expect( disabled.panels.controls?.sections[0]?.controls.settingsTransfer?.type, ).toBe("settingsTransfer"); }); it("does not let app-authored runtime targets suppress mandatory setup controls", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true, renderScale: true, size: { height: 1080, unit: "px", width: 1920 }, sizing: { mode: "editable-output" }, }, panels: { controls: { sections: [ { id: "test-section-7", controls: { manualWidth: { applicability: { mode: "always" as const }, defaultValue: 1280, label: "Width", target: "canvas.size.width", type: "text", }, manualRenderScale: { applicability: { mode: "always" as const }, defaultValue: 1, label: "Scale", max: 2, min: 1, target: "canvas.renderScale", type: "slider", }, manualTimeline: { applicability: { mode: "always" as const }, defaultValue: true, label: "Timeline", target: "panels.timeline.extended", type: "switch", }, }, title: "Manual Runtime Duplicates", }, ], title: "Controls", }, }, }, modules: [ timelineModule({ mode: "playback", }), ], }); const [, setupSection, duplicateSection] = app.panels.controls?.sections ?? []; expect(Object.keys(setupSection?.controls ?? {})).toEqual([ "infinityCanvas", "workspaceBackground", "canvasAspectRatio", "canvasWidth", "canvasHeight", "canvasRenderScale", "timelineExtended", ]); expect(setupSection?.layoutGroups?.[0]).toEqual({ columns: 2, controls: ["canvasWidth", "canvasHeight"], layout: "inline", }); expect(duplicateSection?.controls.manualWidth?.target).toBe( "canvas.size.width", ); expect(duplicateSection?.controls.manualRenderScale?.target).toBe( "canvas.renderScale", ); expect(duplicateSection?.controls.manualTimeline?.target).toBe( "panels.timeline.extended", ); }); });