From 65bf72537f38e11637a14d40d34f32185b03dc73 Mon Sep 17 00:00:00 2001 From: Jeff-Emmett Date: Wed, 26 Feb 2025 09:48:17 -0500 Subject: [PATCH] camera initialization fixed --- src/hooks/useCameraControls.ts | 87 ++-------------------------------- src/routes/Board.tsx | 2 + src/ui/cameraUtils.ts | 68 +++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 84 deletions(-) diff --git a/src/hooks/useCameraControls.ts b/src/hooks/useCameraControls.ts index f3e4d0d..8fbb1ea 100644 --- a/src/hooks/useCameraControls.ts +++ b/src/hooks/useCameraControls.ts @@ -1,6 +1,6 @@ import { useEffect } from "react" -import { Editor, TLEventMap, TLFrameShape, TLParentId, TLShapeId } from "tldraw" -import { useSearchParams } from "react-router-dom" +import { Editor, TLEventMap, TLFrameShape, TLParentId } from "tldraw" +import { cameraHistory } from "@/ui/cameraUtils" // Define camera state interface interface CameraState { @@ -10,16 +10,12 @@ interface CameraState { } const MAX_HISTORY = 10 -let cameraHistory: CameraState[] = [] -// TODO: use this - -// Improved camera change tracking with debouncing +// Track camera changes const trackCameraChange = (editor: Editor) => { const currentCamera = editor.getCamera() const lastPosition = cameraHistory[cameraHistory.length - 1] - // Store any viewport change that's not from a revert operation if ( !lastPosition || currentCamera.x !== lastPosition.x || @@ -34,80 +30,6 @@ const trackCameraChange = (editor: Editor) => { } export function useCameraControls(editor: Editor | null) { - const [searchParams] = useSearchParams() - - // Handle URL-based camera positioning - useEffect(() => { - if (!editor || !editor.store || !editor.getInstanceState().isFocused) { - console.log("Editor not ready:", { - editor: !!editor, - store: !!editor?.store, - isFocused: editor?.getInstanceState().isFocused, - }) - return - } - - const x = searchParams.get("x") - const y = searchParams.get("y") - const zoom = searchParams.get("zoom") - const frameId = searchParams.get("frameId") - const isLocked = searchParams.get("isLocked") === "true" - - console.log("Setting camera:", { x, y, zoom, frameId, isLocked }) - - // Set camera position if coordinates exist - if (x && y && zoom) { - const position = { - x: Math.round(parseFloat(x)), - y: Math.round(parseFloat(y)), - z: Math.round(parseFloat(zoom)), - } - console.log("Camera position:", position) - - requestAnimationFrame(() => { - editor.setCamera(position, { animation: { duration: 0 } }) - - // Apply camera lock immediately after setting position if needed - if (isLocked) { - editor.setCameraOptions({ isLocked: true }) - } - - console.log("Current camera:", editor.getCamera()) - }) - } - - // Handle frame-specific logic - if (frameId) { - const frame = editor.getShape(frameId as TLShapeId) - if (frame) { - editor.select(frameId as TLShapeId) - - // If x/y/zoom are not provided in URL, zoom to frame bounds - if (!x || !y || !zoom) { - const bounds = editor.getShapePageBounds(frame)! - const viewportPageBounds = editor.getViewportPageBounds() - const targetZoom = Math.min( - viewportPageBounds.width / bounds.width, - viewportPageBounds.height / bounds.height, - 1, // Cap at 1x zoom, matching lockCameraToFrame - ) - - editor.zoomToBounds(bounds, { - animation: { duration: 0 }, - targetZoom, - }) - } - - // Apply camera lock after camera is positioned - if (isLocked) { - requestAnimationFrame(() => { - editor.setCameraOptions({ isLocked: true }) - }) - } - } - } - }, [editor, searchParams]) - // Track camera changes useEffect(() => { if (!editor) return @@ -116,7 +38,6 @@ export function useCameraControls(editor: Editor | null) { trackCameraChange(editor) } - // Track both viewport changes and user interaction end editor.on("viewportChange" as keyof TLEventMap, handler) editor.on("userChangeEnd" as keyof TLEventMap, handler) @@ -126,7 +47,7 @@ export function useCameraControls(editor: Editor | null) { } }, [editor]) - // Enhanced camera control functions + // Camera control functions return { zoomToFrame: (frameId: string) => { if (!editor) return diff --git a/src/routes/Board.tsx b/src/routes/Board.tsx index 1600a9f..38e5e69 100644 --- a/src/routes/Board.tsx +++ b/src/routes/Board.tsx @@ -30,6 +30,7 @@ import { makeRealSettings, applySettingsMigrations } from "@/lib/settings" import { PromptShapeTool } from "@/tools/PromptShapeTool" import { PromptShape } from "@/shapes/PromptShapeUtil" import { llm } from "@/utils/llmUtils" +import { setInitialCameraFromUrl } from "@/ui/cameraUtils" // Default to production URL if env var isn't available export const WORKER_URL = "https://jeffemmett-canvas.jeffemmett.workers.dev" @@ -115,6 +116,7 @@ export function Board() { setEditor(editor) editor.registerExternalAssetHandler("url", unfurlBookmarkUrl) editor.setCurrentTool("hand") + setInitialCameraFromUrl(editor) handleInitialPageLoad(editor) registerPropagators(editor, [ TickPropagator, diff --git a/src/ui/cameraUtils.ts b/src/ui/cameraUtils.ts index 20b116b..6bb7704 100644 --- a/src/ui/cameraUtils.ts +++ b/src/ui/cameraUtils.ts @@ -1,4 +1,4 @@ -import { Editor } from "tldraw" +import { Editor, TLFrameShape, TLParentId, TLShape, TLShapeId } from "tldraw" export const cameraHistory: { x: number; y: number; z: number }[] = [] const MAX_HISTORY = 10 // Keep last 10 camera positions @@ -222,3 +222,69 @@ export const lockCameraToFrame = async (editor: Editor) => { alert("Failed to copy frame link. Please check clipboard permissions.") } } + +export const setInitialCameraFromUrl = (editor: Editor) => { + const url = new URL(window.location.href) + const x = url.searchParams.get("x") + const y = url.searchParams.get("y") + const zoom = url.searchParams.get("zoom") + const shapeId = url.searchParams.get("shapeId") + const frameId = url.searchParams.get("frameId") + //const isLocked = url.searchParams.get("isLocked") === "true" + + console.log('Setting initial camera from URL:', { x, y, zoom, shapeId, frameId }) + + if (x && y && zoom) { + editor.stopCameraAnimation() + editor.setCamera( + { + x: parseFloat(x), + y: parseFloat(y), + z: parseFloat(zoom) + }, + { animation: { duration: 0 } } + ) + } + + // Handle shape/frame selection and zoom + if (shapeId) { + editor.select(shapeId as TLShapeId) + const bounds = editor.getSelectionPageBounds() + if (bounds && !x && !y && !zoom) { + zoomToSelection(editor) + } + } else if (frameId) { + editor.select(frameId as TLShapeId) + const frame = editor.getShape(frameId as TLShapeId) + if (frame && !x && !y && !zoom) { + const bounds = editor.getShapePageBounds(frame as TLShape) + if (bounds) { + editor.zoomToBounds(bounds, { + targetZoom: 1, + animation: { duration: 0 }, + }) + } + } + } + + // if (isLocked) { + // editor.setCameraOptions({ isLocked: true }) + // } +} + +export const zoomToFrame = (editor: Editor, frameId: string) => { + if (!editor) return + const frame = editor.getShape(frameId as TLParentId) as TLFrameShape + if (!frame) return + + editor.zoomToBounds(editor.getShapePageBounds(frame)!, { + inset: 32, + animation: { duration: 500 }, + }) +} + +export const copyFrameLink = (_editor: Editor, frameId: string) => { + const url = new URL(window.location.href) + url.searchParams.set("frameId", frameId) + navigator.clipboard.writeText(url.toString()) +}