import { describe, expect, it } from "vitest"; import { defineToolcraft } from "./define-toolcraft"; import { getProductSections } from "./define-toolcraft-test-utils"; describe("defineToolcraft control layout normalization", () => { it("adds runtime layout groups only for compact numeric text pairs", () => { const app = defineToolcraft({ canvas: { enabled: false }, panels: { controls: { sections: [ { controls: { format: { defaultValue: "png", label: "Format", options: [ { label: "PNG", value: "png" }, { label: "JPEG", value: "jpeg" }, ], target: "export.format", type: "select", }, quality: { defaultValue: "balanced", label: "Quality", options: [ { label: "Balanced", value: "balanced" }, { label: "High", value: "high" }, ], target: "export.quality", type: "select", }, width: { defaultValue: "1024", label: "Width", target: "export.width", type: "text", }, height: { defaultValue: "768", label: "Height", target: "export.height", type: "text", }, prompt: { defaultValue: "Describe the generated asset", label: "Prompt", target: "generation.prompt", type: "text", }, seed: { defaultValue: "42", label: "Seed", target: "generation.seed", type: "text", }, }, title: "Output", }, ], title: "Controls", }, }, }); const [outputSection] = getProductSections(app); expect(outputSection?.layoutGroups).toEqual([ { columns: 2, controls: ["width", "height"], layout: "inline", }, ]); }); it("adds runtime layout groups for compact numeric and color opacity pairs", () => { const app = defineToolcraft({ canvas: { enabled: false }, panels: { controls: { sections: [ { controls: { maskSize: { defaultValue: "180", label: "Mask size", target: "mask.size", type: "text", }, maskColor: { defaultValue: { hex: "#0EA5E9", opacity: 82 }, label: "Color", target: "mask.color", type: "colorOpacity", }, }, title: "Mask", }, ], title: "Controls", }, }, }); const [maskSection] = getProductSections(app); expect(maskSection?.layoutGroups).toEqual([ { columns: 2, controls: ["maskSize", "maskColor"], layout: "inline", }, ]); }); it("does not auto-pair mixed compact fields without visible labels", () => { const app = defineToolcraft({ canvas: { enabled: false }, panels: { controls: { sections: [ { controls: { maskSize: { defaultValue: "180", label: "Mask size", target: "mask.size", type: "text", }, maskColor: { defaultValue: { hex: "#0EA5E9", opacity: 82 }, target: "mask.color", type: "colorOpacity", }, }, title: "Mask", }, ], title: "Controls", }, }, }); expect(app.panels.controls?.sections[0]?.layoutGroups).toBeUndefined(); }); it("preserves explicit inline select combinations without auto-pairing ordinary selects", () => { const app = defineToolcraft({ canvas: { enabled: false }, panels: { controls: { sections: [ { controls: { colorSpace: { defaultValue: "srgb", label: "Color space", options: [ { label: "sRGB", value: "srgb" }, { label: "Display P3", value: "display-p3" }, ], target: "export.colorSpace", type: "select", }, bitDepth: { defaultValue: "8", label: "Bit depth", options: [ { label: "8-bit", value: "8" }, { label: "16-bit", value: "16" }, ], target: "export.bitDepth", type: "select", }, }, layoutGroups: [ { columns: 2, controls: ["colorSpace", "bitDepth"], layout: "inline", }, ], title: "Format Pair", }, { controls: { format: { defaultValue: "png", label: "Format", options: [ { label: "PNG", value: "png" }, { label: "JPEG", value: "jpeg" }, ], target: "export.format", type: "select", }, quality: { defaultValue: "balanced", label: "Quality", options: [ { label: "Balanced", value: "balanced" }, { label: "High", value: "high" }, ], target: "export.quality", type: "select", }, }, title: "Output", }, ], title: "Controls", }, }, }); const [formatPairSection, outputSection] = getProductSections(app); expect(formatPairSection?.layoutGroups).toEqual([ { columns: 2, controls: ["colorSpace", "bitDepth"], layout: "inline", }, ]); expect(outputSection?.layoutGroups).toBeUndefined(); }); it("splits standalone controls out of grouped control sections", () => { const app = defineToolcraft({ canvas: { enabled: false }, panels: { controls: { sections: [ { controls: { opacity: { defaultValue: 75, label: "Opacity", target: "selectedLayer.opacity", type: "slider", }, threshold: { defaultValue: 50, label: "Threshold", target: "selectedLayer.threshold", type: "slider", }, anchor: { defaultValue: "center", label: "Anchor", target: "selectedLayer.anchor", type: "anchorGrid", }, }, title: "Basic", }, ], title: "Controls", }, }, }); const [basicSection, anchorSection] = getProductSections(app); expect(Object.keys(basicSection?.controls ?? {})).toEqual(["opacity", "threshold"]); expect(basicSection?.title).toBe("Basic"); expect(anchorSection?.layout).toBe("standalone"); expect(anchorSection?.title).toBe("Anchor"); expect(Object.keys(anchorSection?.controls ?? {})).toEqual(["anchor"]); }); it("keeps stepped continuous sliders plain and normalizes explicit discrete markers", () => { const app = defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { speed: { defaultValue: 118, label: "Reveal speed", markerCount: 6, max: 150, min: 0, step: 1, target: "ascii.speed", type: "slider", }, toneSteps: { defaultValue: 2, label: "Tone steps", markerCount: 4, max: 4, min: 0, step: 1, target: "ascii.toneSteps", type: "slider", variant: "discrete", }, duration: { defaultValue: 0.6, label: "Flip duration", markerCount: 6, max: 5, min: 0, step: 0.1, target: "ascii.flipDurationSec", type: "slider", variant: "continuous", }, range: { defaultValue: [2, 8], label: "Range", markerCount: 3, max: 10, min: 0, step: 0.5, target: "ascii.range", type: "rangeSlider", }, }, title: "Sliders", }, ], title: "Controls", }, }, }); const controls = getProductSections(app)[0]?.controls; expect(controls?.speed?.variant).toBeUndefined(); expect(controls?.speed?.markerCount).toBe(6); expect(controls?.toneSteps?.variant).toBe("discrete"); expect(controls?.toneSteps?.markerCount).toBe(5); expect(controls?.duration?.variant).toBe("continuous"); expect(controls?.duration?.markerCount).toBe(6); expect(controls?.range?.variant).toBeUndefined(); expect(controls?.range?.markerCount).toBe(3); }); });