import { act, cleanup, createEvent, fireEvent, render, screen, waitFor } from "@testing-library/react"; import { FileDrop, Panel } from "@repo/ui"; import { DEFAULT_COLOR_FORMAT_MODE, getColorSurfaceModel, getColorSurfaceSliderConfig, getColorSurfaceStyle, getColorSurfaceThumbPosition, getSurfaceHsvColor, StyleGuideColorPicker, } from "@repo/ui/controls"; import * as React from "react"; import { afterEach, beforeAll, vi } from "vitest"; import { defineToolcraft } from "../../../schema/define-toolcraft"; import type { ResolvedToolcraftAppSchema } from "../../../schema/types"; import type { ToolcraftInitialState } from "../../../state/types"; import { CanvasShell } from "../../canvas/canvas-shell"; import { ControlsPanel } from "../controls-panel"; import { ToolcraftRoot } from "../../app-shell/toolcraft-root"; import { useToolcraft } from "../../app-shell/use-toolcraft"; export { act, CanvasShell, cleanup, ControlsPanel, createEvent, DEFAULT_COLOR_FORMAT_MODE, defineToolcraft, FileDrop, fireEvent, getColorSurfaceModel, getColorSurfaceSliderConfig, getColorSurfaceStyle, getColorSurfaceThumbPosition, getSurfaceHsvColor, Panel, React, render, screen, StyleGuideColorPicker, ToolcraftRoot, vi, waitFor, }; beforeAll(() => { Object.defineProperty(window, "matchMedia", { value: (query: string) => ({ addEventListener: () => undefined, addListener: () => undefined, dispatchEvent: () => false, matches: query === "(prefers-reduced-motion: reduce)", media: query, onchange: null, removeEventListener: () => undefined, removeListener: () => undefined, }), writable: true, }); window.requestAnimationFrame ??= ((callback: FrameRequestCallback) => window.setTimeout(() => callback(performance.now()), 0)) as typeof window.requestAnimationFrame; window.cancelAnimationFrame ??= ((handle: number) => window.clearTimeout(handle)) as typeof window.cancelAnimationFrame; }); afterEach(() => { cleanup(); window.localStorage.clear(); }); export function createSchema() { return defineToolcraft({ canvas: { enabled: true, size: { height: 180, unit: "px", width: 320 } }, panels: { controls: { sections: [ { controls: { prompt: { defaultValue: "Initial prompt", description: "Describe the generated result.", label: "Prompt", target: "generation.prompt", type: "text", }, opacity: { defaultValue: 75, label: "Opacity", max: 100, min: 0, target: "selectedLayer.opacity", type: "slider", unit: "%", }, staticOpacity: { defaultValue: 40, keyframeable: false, label: "Static opacity", max: 100, min: 0, target: "style.staticOpacity", type: "slider", unit: "%", }, blend: { defaultValue: "normal", label: "Blend", options: [ { label: "Normal", value: "normal" }, { label: "Screen", value: "screen" }, ], target: "style.blend", type: "segmented", }, enabled: { defaultValue: true, label: "Enabled", target: "generation.enabled", type: "switch", }, }, title: "Basic", }, { controls: { anchor: { defaultValue: "center", label: "Anchor", target: "generation.anchor", type: "anchorGrid", }, }, layout: "standalone", }, { controls: { image: { defaultValue: "image-1", items: [ { alt: "Image 1", src: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 3'/%3E", value: "image-1", }, { alt: "Image 2", src: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 3'/%3E", value: "image-2", }, ], label: "Image", target: "input.image", type: "imagePicker", }, }, layout: "standalone", }, { controls: { outputMix: { label: "Output Mix", target: "style.outputMix", type: "channelMixer", }, }, layout: "standalone", title: "Output Mix", }, { controls: { curves: { label: false, target: "style.curves", type: "curves", }, }, layout: "standalone", }, { controls: { fill: { defaultValue: { hex: "#C1FF00" }, label: "Fill", target: "style.fill", type: "color", }, stroke: { defaultValue: { hex: "#FF6A00" }, label: "Stroke", target: "style.stroke", type: "color", }, }, layout: "standalone", }, { actionGroup: "secondary", controls: { footer: { actions: [ { command: "controls.reset", label: "Reset", value: "reset", variant: "outline", }, { command: "controls.apply", label: "Apply", value: "apply", variant: "default", }, ], target: "panel.actions", type: "panelActions", }, }, layout: "standalone", }, ], title: "Generation Controls", }, timeline: true, }, }); } export function StateProbe() { const { dispatch, state } = useToolcraft(); return ( <> {String(state.values["generation.prompt"])} {String(state.values["generation.enabled"])} {String(state.values["input.image"])} {JSON.stringify(state.values["typography.font"])} {JSON.stringify(state.values["text.color"])} {JSON.stringify(state.values["style.gradient"])} {state.canvas.size.width},{state.canvas.size.height} {state.mediaAssets.length} {state.mediaAssets.map((asset) => asset.id).join(",")} {state.mediaAssets .map((asset) => asset.assetKind === "image" && asset.size ? `${asset.size.width}x${asset.size.height}` : "none", ) .join(",")} {JSON.stringify( state.mediaAssets.map((asset) => asset.assetKind === "image" ? (asset.transform ?? null) : null, ), )} {String(state.timeline.expanded)} {String(state.panels.timeline.extended)} {String(state.panels.timeline.hidden)} {JSON.stringify(state.timeline.keyframeGroups)} {JSON.stringify(state.values)} ); } export function renderControlsPanel( props: Partial> = {}, initialState?: ToolcraftInitialState, ) { return renderControlsPanelWithSchema(createSchema(), props, initialState); } export function renderControlsPanelWithSchema( schema: ResolvedToolcraftAppSchema, props: Partial> = {}, initialState?: ToolcraftInitialState, ) { return render( , ); }