From d4a0250c202e0cd1274f8064e3d18faeba9147f2 Mon Sep 17 00:00:00 2001 From: twotalesanimation <80506065+twotalesanimation@users.noreply.github.com> Date: Sun, 12 Jul 2026 12:36:18 +0200 Subject: [PATCH] Shoot sketches templates --- components/shoot-log/SketchPad.tsx | 320 ----------------------------- 1 file changed, 320 deletions(-) diff --git a/components/shoot-log/SketchPad.tsx b/components/shoot-log/SketchPad.tsx index d05a02c..e29c6f0 100644 --- a/components/shoot-log/SketchPad.tsx +++ b/components/shoot-log/SketchPad.tsx @@ -570,324 +570,4 @@ export function SketchPad({ takeId, onSaved, onClose }: Props) { ); } - if (!canvas) return; - const ctx = canvas.getContext("2d"); - if (!ctx) return; - ctx.fillStyle = BG_COLOR; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - for (const stroke of strokesRef.current) { - ctx.beginPath(); - ctx.strokeStyle = stroke.color; - ctx.fillStyle = stroke.color; - ctx.lineWidth = stroke.width; - ctx.lineCap = "round"; - ctx.lineJoin = "round"; - - if (stroke.points.length === 1) { - // Single tap → dot - ctx.arc(stroke.points[0].x, stroke.points[0].y, stroke.width / 2, 0, Math.PI * 2); - ctx.fill(); - } else { - ctx.moveTo(stroke.points[0].x, stroke.points[0].y); - for (let i = 1; i < stroke.points.length; i++) { - ctx.lineTo(stroke.points[i].x, stroke.points[i].y); - } - ctx.stroke(); - } - } - }, []); - - // ── Size canvas to fill container on mount + window resize ──────────────── - useEffect(() => { - const canvas = canvasRef.current; - if (!canvas) return; - - const fit = () => { - const parent = canvas.parentElement; - if (!parent) return; - const { width, height } = parent.getBoundingClientRect(); - canvas.width = Math.floor(width); - canvas.height = Math.floor(height); - redraw(); - }; - - fit(); - window.addEventListener("resize", fit); - return () => window.removeEventListener("resize", fit); - }, [redraw]); - - // ── Keyboard shortcuts ──────────────────────────────────────────────────── - useEffect(() => { - const onKey = (e: KeyboardEvent) => { - if (e.key === "Escape") onClose(); - if (e.key === "p" && !e.metaKey && !e.ctrlKey) setIsEraser(false); - if (e.key === "e" && !e.metaKey && !e.ctrlKey) setIsEraser(true); - if ((e.metaKey || e.ctrlKey) && e.key === "z") { - e.preventDefault(); - strokesRef.current = strokesRef.current.slice(0, -1); - setStrokeCount(strokesRef.current.length); - redraw(); - } - }; - window.addEventListener("keydown", onKey); - return () => window.removeEventListener("keydown", onKey); - }, [onClose, redraw]); - - // ── Coordinate helper — CSS px → canvas buffer px ──────────────────────── - const getPos = (e: React.PointerEvent) => { - const canvas = canvasRef.current!; - const rect = canvas.getBoundingClientRect(); - return { - x: (e.clientX - rect.left) * (canvas.width / rect.width), - y: (e.clientY - rect.top) * (canvas.height / rect.height), - }; - }; - - // ── Pointer handlers ────────────────────────────────────────────────────── - const onPointerDown = (e: React.PointerEvent) => { - e.preventDefault(); - (e.target as HTMLCanvasElement).setPointerCapture(e.pointerId); - isDrawingRef.current = true; - - const pos = getPos(e); - const stroke: Stroke = { - color: isEraser ? BG_COLOR : color, - width: isEraser ? brushSize * 4 : brushSize, - points: [pos], - }; - currentStrokeRef.current = stroke; - - // Immediate dot feedback - const ctx = canvasRef.current!.getContext("2d")!; - ctx.beginPath(); - ctx.fillStyle = stroke.color; - ctx.arc(pos.x, pos.y, stroke.width / 2, 0, Math.PI * 2); - ctx.fill(); - }; - - const onPointerMove = (e: React.PointerEvent) => { - if (!isDrawingRef.current || !currentStrokeRef.current) return; - e.preventDefault(); - - const pos = getPos(e); - const stroke = currentStrokeRef.current; - const prev = stroke.points[stroke.points.length - 1]; - stroke.points.push(pos); - - // Incremental draw — no full redraw needed during stroke - const ctx = canvasRef.current!.getContext("2d")!; - ctx.beginPath(); - ctx.strokeStyle = stroke.color; - ctx.lineWidth = stroke.width; - ctx.lineCap = "round"; - ctx.lineJoin = "round"; - ctx.moveTo(prev.x, prev.y); - ctx.lineTo(pos.x, pos.y); - ctx.stroke(); - }; - - const onPointerUp = () => { - if (!isDrawingRef.current) return; - isDrawingRef.current = false; - if (currentStrokeRef.current) { - strokesRef.current = [...strokesRef.current, currentStrokeRef.current]; - setStrokeCount(strokesRef.current.length); - currentStrokeRef.current = null; - } - }; - - const undo = () => { - strokesRef.current = strokesRef.current.slice(0, -1); - setStrokeCount(strokesRef.current.length); - redraw(); - }; - - const clear = () => { - strokesRef.current = []; - setStrokeCount(0); - redraw(); - }; - - // ── Save ────────────────────────────────────────────────────────────────── - const save = async () => { - const canvas = canvasRef.current; - if (!canvas) return; - setSaving(true); - setError(null); - try { - const blob = await new Promise((resolve) => - canvas.toBlob(resolve, "image/png") - ); - if (!blob) throw new Error("Canvas export failed"); - - const fileName = `sketch_${Date.now()}.png`; - const fd = new FormData(); - fd.append("file", new File([blob], fileName, { type: "image/png" })); - fd.append("type", "image"); - - const uploadRes = await fetch("/api/upload", { method: "POST", body: fd }); - if (!uploadRes.ok) throw new Error("Upload failed"); - const { url, key } = await uploadRes.json(); - - const attRes = await fetch(`/api/shoot-log/takes/${takeId}/attachments`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - fileUrl: url, - fileKey: key ?? "", - fileName, - fileSize: blob.size, - fileType: "IMAGE", - category: "MISCELLANEOUS", - caption: "Sketch", - }), - }); - if (!attRes.ok) throw new Error("Failed to save attachment"); - const { attachment } = await attRes.json(); - onSaved(attachment as TakeAttachmentData); - onClose(); - } catch (err) { - setError(err instanceof Error ? err.message : "Save failed"); - } finally { - setSaving(false); - } - }; - - // ─── Render ─────────────────────────────────────────────────────────────── - const activeColor = isEraser ? BG_COLOR : color; - - return ( -
- - {/* ── Toolbar ── */} -
- - {/* Pen / Eraser toggle */} -
- - -
- -
- - {/* Colour swatches */} -
- {COLORS.map((c) => ( -
- -
- - {/* Brush sizes */} -
- {BRUSH_SIZES.map((s) => ( - - ))} -
- -
- - {error && {error}} - - {/* Action buttons */} -
- - - - -
-
- - {/* ── Canvas ── */} -
- {strokeCount === 0 && ( -
-

Draw your sketch…

-
- )} - -
-
- ); -}