diff --git a/src/App.tsx b/src/App.tsx index acbef460..6913be05 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -64,6 +64,7 @@ const UnifiedStudio = lazyWithRetry(() => import("./pages/UnifiedStudio")); const CADStudio = lazyWithRetry(() => import("./pages/CADStudio")); const CADToCatalog = lazyWithRetry(() => import("./pages/CADToCatalog")); const TextToCAD = lazyWithRetry(() => import("./pages/TextToCAD")); +const SketchToCAD = lazyWithRetry(() => import("./pages/SketchToCAD")); const Generations = lazyWithRetry(() => import("./pages/Generations")); const Credits = lazyWithRetry(() => import("./pages/Credits")); const Pricing = lazyWithRetry(() => import("./pages/Pricing")); @@ -230,6 +231,7 @@ const App = () => ( } /> } /> } /> + } /> {/* Admin routes */} }> diff --git a/src/assets/examples/sketch-allowed-1.webp b/src/assets/examples/sketch-allowed-1.webp new file mode 100644 index 00000000..ef046f7f Binary files /dev/null and b/src/assets/examples/sketch-allowed-1.webp differ diff --git a/src/assets/examples/sketch-allowed-2.webp b/src/assets/examples/sketch-allowed-2.webp new file mode 100644 index 00000000..2de543fc Binary files /dev/null and b/src/assets/examples/sketch-allowed-2.webp differ diff --git a/src/assets/examples/sketch-notallowed-1.webp b/src/assets/examples/sketch-notallowed-1.webp new file mode 100644 index 00000000..65767794 Binary files /dev/null and b/src/assets/examples/sketch-notallowed-1.webp differ diff --git a/src/assets/examples/sketch-notallowed-2.webp b/src/assets/examples/sketch-notallowed-2.webp new file mode 100644 index 00000000..efe5e729 Binary files /dev/null and b/src/assets/examples/sketch-notallowed-2.webp differ diff --git a/src/components/sketch-to-cad/SketchLeftPanel.tsx b/src/components/sketch-to-cad/SketchLeftPanel.tsx new file mode 100644 index 00000000..6b17aa0d --- /dev/null +++ b/src/components/sketch-to-cad/SketchLeftPanel.tsx @@ -0,0 +1,168 @@ +import { useRef, useCallback } from "react"; +import { ImagePlus } from "lucide-react"; +import creditCoinIcon from "@/assets/icons/credit-coin.png"; +import { useEstimatedCost } from "@/hooks/use-estimated-cost"; +import { SKETCH_TO_CAD_WORKFLOW } from "@/lib/sketch-to-cad-workflows"; + +interface SketchLeftPanelProps { + previewUrl: string | null; + description: string; + onDescriptionChange: (d: string) => void; + isGenerating: boolean; + hasModel: boolean; + onRegenerate: () => void; + onNewSketch: (file: File) => void; + onGlbUpload: (file: File) => void; + onReset?: () => void; + creditBlock?: React.ReactNode; +} + +export default function SketchLeftPanel({ + previewUrl, + description, + onDescriptionChange, + isGenerating, + hasModel, + onRegenerate, + onNewSketch, + onGlbUpload, + onReset, + creditBlock, +}: SketchLeftPanelProps) { + const sketchInputRef = useRef(null); + const glbInputRef = useRef(null); + const { cost: estimatedCost, loading: costLoading } = useEstimatedCost({ workflowName: SKETCH_TO_CAD_WORKFLOW }); + + const handleSketchInput = useCallback((e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file && file.type.startsWith('image/')) onNewSketch(file); + e.target.value = ''; + }, [onNewSketch]); + + const handleGlbInput = useCallback((e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) onGlbUpload(file); + e.target.value = ''; + }, [onGlbUpload]); + + return ( +
+ {/* Header */} +
+

+ Sketch to 3D +

+
+ + {/* Body */} +
+ {/* Sketch thumbnail */} +
+

+ Sketch +

+
+ {previewUrl ? ( + Your sketch + ) : ( +
+ +
+ )} +
+ + +
+ + {/* Description */} +
+

+ Description +

+