Canvas: venstreklikk-dra for pan + kameraposisjon lagres
- Klikk-hold-dra på tom bakgrunn = pan (erstatter midtre museknapp) - Shift+dra = lasso-seleksjon (som før) - Kameraposisjon (x, y, zoom) lagres i workspace-metadata og gjenopprettes ved neste besøk - Senket ZOOM_MIN til 5% for å tillate zoom ut på store canvas Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6101bf00b1
commit
79371c20ac
3 changed files with 30 additions and 16 deletions
|
|
@ -11,6 +11,7 @@
|
|||
clampZoom,
|
||||
ZOOM_MIN,
|
||||
ZOOM_MAX,
|
||||
ZOOM_FIT_MIN,
|
||||
} from './types.js';
|
||||
|
||||
/**
|
||||
|
|
@ -211,21 +212,23 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// Left button on empty space = lasso or deselect
|
||||
if (!spaceHeld) {
|
||||
if (e.shiftKey) {
|
||||
// Start lasso
|
||||
isLassoing = true;
|
||||
lassoStart = { x: sx, y: sy };
|
||||
lassoEnd = { x: sx, y: sy };
|
||||
} else {
|
||||
// Deselect all
|
||||
selectedIds = new Set();
|
||||
onSelectionChange?.([]);
|
||||
}
|
||||
containerEl.setPointerCapture(e.pointerId);
|
||||
e.preventDefault();
|
||||
// Left button on empty space = pan (or lasso with shift)
|
||||
if (e.shiftKey) {
|
||||
// Shift+drag = lasso selection
|
||||
isLassoing = true;
|
||||
lassoStart = { x: sx, y: sy };
|
||||
lassoEnd = { x: sx, y: sy };
|
||||
} else {
|
||||
// Normal drag on empty space = pan
|
||||
isPanning = true;
|
||||
panStart = { x: e.clientX, y: e.clientY };
|
||||
cameraStart = { x: camera.x, y: camera.y };
|
||||
// Deselect all
|
||||
selectedIds = new Set();
|
||||
onSelectionChange?.([]);
|
||||
}
|
||||
containerEl.setPointerCapture(e.pointerId);
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -113,8 +113,10 @@ export function visibleObjects<T extends CanvasObject>(
|
|||
}
|
||||
|
||||
/** Clamp zoom to allowed range */
|
||||
export const ZOOM_MIN = 0.1;
|
||||
export const ZOOM_MIN = 0.05;
|
||||
export const ZOOM_MAX = 3.0;
|
||||
/** Lower bound for zoomToFit — allows fitting very spread-out layouts */
|
||||
export const ZOOM_FIT_MIN = 0.02;
|
||||
|
||||
export function clampZoom(zoom: number): number {
|
||||
return Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, zoom));
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
// Canvas + BlockShell
|
||||
import Canvas from '$lib/components/canvas/Canvas.svelte';
|
||||
import type { CanvasObject } from '$lib/components/canvas/types.js';
|
||||
import type { CanvasObject, Camera } from '$lib/components/canvas/types.js';
|
||||
import BlockShell from '$lib/components/blockshell/BlockShell.svelte';
|
||||
|
||||
// Workspace layout
|
||||
|
|
@ -76,6 +76,11 @@
|
|||
// Load theme preferences
|
||||
if (res.metadata) {
|
||||
loadThemeFromMetadata(res.metadata as Record<string, unknown>);
|
||||
// Load saved camera position
|
||||
const cam = (res.metadata as Record<string, unknown>).camera as Camera | undefined;
|
||||
if (cam && typeof cam.x === 'number') {
|
||||
savedCamera = cam;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
|
|
@ -97,6 +102,7 @@
|
|||
|
||||
let layout = $state<WorkspaceLayout>({ panels: [] });
|
||||
let layoutInitialized = $state(false);
|
||||
let savedCamera = $state<Camera>({ x: 0, y: 0, zoom: 1.0 });
|
||||
|
||||
// When workspace node appears in store (after creation), load its layout
|
||||
$effect(() => {
|
||||
|
|
@ -141,6 +147,7 @@
|
|||
metadata: {
|
||||
...currentMeta,
|
||||
workspace_layout: layout,
|
||||
camera: savedCamera,
|
||||
preferences: {
|
||||
...(currentMeta.preferences ?? {}),
|
||||
theme: { hueBg: themeHueBg, hueSurface: themeHueSurface, hueAccent: themeHueAccent },
|
||||
|
|
@ -778,6 +785,8 @@
|
|||
objects={canvasObjects}
|
||||
onObjectMove={handleObjectMove}
|
||||
grid={{ enabled: false, size: 20 }}
|
||||
initialCamera={savedCamera}
|
||||
onCameraChange={(cam) => { savedCamera = cam; persistMetadata(); }}
|
||||
>
|
||||
{#snippet renderObject(obj)}
|
||||
{@const trait = obj.id}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue