"use client"; import * as React from "react"; import type { ResolvedToolcraftAppSchema } from "../../schema/types"; import type { AnyToolcraftRendererPipelineRegistration } from "../../rendering"; import { createToolcraftRuntimeSceneVisibility, type ToolcraftProductSceneBoundsProvider, } from "../../scene"; import type { ToolcraftState } from "../../state/types"; import { getToolcraftExportRendererCoverageErrors, } from "../../export/export-renderer-coverage"; import type { ToolcraftProductExportRenderer } from "../../export/product-export-renderer"; import type { ToolcraftProductSvgExportRenderer } from "../../export/product-svg-export-renderer"; import { CanvasShell } from "../canvas/canvas-shell"; import { ToolcraftProductSceneBoundsBoundary } from "../canvas/product-scene-surface"; import { ControlsPanel, type ToolcraftPanelActionHandler, } from "../controls-panel/controls-panel"; import type { ToolcraftControlRendererMap } from "../controls-panel/control-renderers"; import type { ToolcraftControlsSceneExport } from "../controls-panel/actions/controls-panel-actions"; import { ToolcraftRoot } from "./toolcraft-root"; import { LayersPanel } from "../layers/layers-panel"; import { useToolcraftModelRenderPreparationStatus } from "../model-rendering/model-render-provider"; import { TimelinePanel } from "../timeline/timeline-panel"; import { ToolbarPanel } from "./toolbar-panel"; import { useToolcraftCommittedSelector } from "./toolcraft-selectors"; import type { ToolcraftModelPresentationMode } from "../model-rendering/model-render-binding"; import { useToolcraftPersistenceStatus } from "./use-toolcraft-persistence"; export type ToolcraftAppComposition = { canvasContent?: React.ReactNode; controlRenderers?: ToolcraftControlRendererMap; exportRenderer?: ToolcraftProductExportRenderer; infiniteCanvasContent?: React.ReactNode; modelPresentation?: ToolcraftModelPresentationMode; onPanelAction?: ToolcraftPanelActionHandler; renderDefaultCanvasMedia?: boolean; rendererPipelineRegistration?: AnyToolcraftRendererPipelineRegistration; sceneBoundsProvider?: ToolcraftProductSceneBoundsProvider; schema: ResolvedToolcraftAppSchema; svgExportRenderer?: ToolcraftProductSvgExportRenderer; }; export type ToolcraftAppProps = ToolcraftAppComposition & { className?: string; style?: React.CSSProperties; }; const toolcraftMinAppWidthPx = 1024; const selectAppSurfaces = (state: ToolcraftState) => state.schema.assembly.surfaces; const selectTimelinePanelExtended = (state: ToolcraftState) => state.panels.timeline.extended === true; function cn(...classNames: Array): string { return classNames.filter(Boolean).join(" "); } function ToolcraftAppContent({ canvasContent, className, controlRenderers, infiniteCanvasContent, onPanelAction, renderDefaultCanvasMedia = true, sceneExport, style, }: Omit< ToolcraftAppProps, | "modelPresentation" | "rendererPipelineRegistration" | "sceneBoundsProvider" | "schema" > & Readonly<{ sceneExport: ToolcraftControlsSceneExport }>): React.JSX.Element { const surfaces = useToolcraftCommittedSelector(selectAppSurfaces); const timelinePanelVariant = useToolcraftCommittedSelector(selectTimelinePanelExtended) ? "extended" : "compact"; const modelRendererStatus = useToolcraftModelRenderPreparationStatus(); const persistenceStatus = useToolcraftPersistenceStatus(); return (
{surfaces.canvas.enabled ? ( {canvasContent} ) : null} {surfaces.panels.layers?.enabled ? ( ) : null} {surfaces.panels.controls?.enabled ? ( ) : null} {surfaces.panels.timeline?.enabled ? ( ) : null} {surfaces.panels.toolbar.enabled ? ( ) : null}
); } export function ToolcraftApp({ canvasContent, exportRenderer, modelPresentation, renderDefaultCanvasMedia = true, rendererPipelineRegistration, sceneBoundsProvider, schema, svgExportRenderer, ...props }: ToolcraftAppProps): React.JSX.Element { const suppressedModelTargets = modelPresentation?.mode === "custom" ? modelPresentation.consumers.map(({ sourceTarget }) => sourceTarget) : []; const productSceneRequired = React.Children.count(canvasContent) > 0 || rendererPipelineRegistration !== undefined || suppressedModelTargets.length > 0; const exportRendererErrors = getToolcraftExportRendererCoverageErrors({ exportRenderer, productSceneRequired, schema, svgExportRenderer, }); if (exportRendererErrors.length > 0) { throw new Error( `Toolcraft export renderer configuration is invalid:\n- ${exportRendererErrors.join("\n- ")}`, ); } const sceneExport: ToolcraftControlsSceneExport = Object.freeze({ ...(sceneBoundsProvider ? { boundsProvider: sceneBoundsProvider } : {}), ...(exportRenderer ? { exportRenderer } : {}), productSceneRequired, ...(svgExportRenderer ? { svgExportRenderer } : {}), visibility: createToolcraftRuntimeSceneVisibility({ renderDefaultImages: renderDefaultCanvasMedia, suppressedModelTargets, }), }); return ( ); }