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(
,
);
}