import type { ToolcraftControlSchema, ToolcraftDefaultMediaAssetSchema, } from "../../schema/types"; import type { ToolcraftProductBase } from "../../schema/product-base"; export type ProjectBBaseOptions = Readonly<{ canvasEnabled?: boolean; canvasUpload?: boolean; defaultImage?: boolean; defaultModel?: boolean; imageSource?: boolean; layers?: boolean; modelSource?: boolean; orientation?: boolean; persistence?: "localStorage" | "none"; }>; export function createProjectBBaseSchema({ canvasEnabled = true, canvasUpload = true, defaultImage = false, defaultModel = false, imageSource = true, layers = false, modelSource = true, orientation = true, persistence = "localStorage", }: ProjectBBaseOptions = {}): ToolcraftProductBase { const defaultAssets: ToolcraftDefaultMediaAssetSchema[] = []; if (defaultImage) { defaultAssets.push({ ingressPolicy: "prepared-source", position: { x: 0, y: 0 }, sourceSize: { height: 512, unit: "px", width: 512 }, dataUrl: "data:image/png;base64,AA==", fileName: "reference.png", sourceTarget: "source.image", }); } if (defaultModel) { defaultAssets.push({ assetKind: "model", fileName: "fixture.glb", sourceFiles: [ { dataUrl: "data:model/gltf-binary;base64,AA==", mimeType: "model/gltf-binary", path: "fixture.glb", }, ], }); } const controls: Record = { intensity: { applicability: { mode: "always" as const }, defaultValue: 50, description: "Controls the product treatment strength.", label: "Intensity", max: 100, min: 0, target: "product.intensity", type: "slider", }, }; if (imageSource) { controls.image = { applicability: { mode: "always" as const }, description: "Primary image source for the scene.", label: "Reference image", target: "source.image", type: "fileDrop", }; controls.files = { applicability: { mode: "always" as const }, assetKind: "file", description: "Supporting files used by the scene.", label: "Reference files", multiple: true, target: "source.files", type: "fileDrop", }; } if (modelSource) { controls.model = { applicability: { mode: "always" as const }, assetKind: "model", description: "Three-dimensional source model.", label: "Source model", target: "source.model", type: "fileDrop", }; } if (orientation) { controls.orientation = { applicability: { mode: "always" as const }, defaultValue: { position: [3, 3, 3], up: [0, 1, 0], }, description: "Controls the runtime model orbit.", keyframeable: false, label: false, target: "view.orbit", type: "orientationGizmo", }; } const base: ToolcraftProductBase = { canvas: { draggable: true, enabled: canvasEnabled, size: { height: 240, unit: "px", width: 360 }, sizing: { mode: "editable-output" }, upload: canvasUpload, }, identity: { id: "project-b-scene", title: "Project B Scene" }, ...(defaultAssets.length > 0 ? { media: { defaultAssets } } : {}), panels: { controls: { sections: [ { controls, id: "scene", title: "Scene", }, ], title: "Project B Scene", }, }, persistence: persistence === "none" ? { storage: "none" } : { additionalValueTargets: ["product.seed"], storage: "localStorage", }, }; if (layers) { (base.panels as Record).layers = true; } return base; } export function freezeProjectBFixtureRecursively(value: unknown): void { if (value === null || typeof value !== "object" || Object.isFrozen(value)) { return; } for (const child of Object.values(value)) { freezeProjectBFixtureRecursively(child); } Object.freeze(value); }