diff --git a/packages/editor/src/editor/Canvas.tsx b/packages/editor/src/editor/Canvas.tsx index cc33eb0..70c576d 100644 --- a/packages/editor/src/editor/Canvas.tsx +++ b/packages/editor/src/editor/Canvas.tsx @@ -14,6 +14,7 @@ export interface CanvasProps { scale: number; panX: number; panY: number; + gridEnabled: boolean; guides: { xs: number[]; ys: number[] }; onSelectSingle(id?: string): void; onToggleSelect(id: string): void; @@ -276,6 +277,19 @@ export function Canvas(props: CanvasProps) { position: 'relative', }} > + {props.gridEnabled ? ( +
+ ) : null} + {/* guides (snap lines) */} {props.guides.xs.map((x) => (
+ + + @@ -446,6 +471,7 @@ export function EditorApp() { scale={state.canvas.scale} panX={state.canvas.panX} panY={state.canvas.panY} + gridEnabled={state.canvas.gridEnabled} guides={state.canvas.guides} onSelectSingle={(id) => dispatch({ type: 'selectSingle', id })} onToggleSelect={(id) => dispatch({ type: 'toggleSelect', id })} diff --git a/packages/editor/src/editor/store.ts b/packages/editor/src/editor/store.ts index 00d3471..da7bc74 100644 --- a/packages/editor/src/editor/store.ts +++ b/packages/editor/src/editor/store.ts @@ -34,6 +34,8 @@ export type EditorAction = | { type: 'setScale'; scale: number } | { type: 'panBy'; dx: number; dy: number } | { type: 'zoomAt'; scale: number; anchor: { x: number; y: number } } + | { type: 'toggleGrid' } + | { type: 'toggleSnap' } | { type: 'beginPan'; start: { screenX: number; screenY: number } } | { type: 'updatePan'; current: { screenX: number; screenY: number } } | { type: 'endPan' } @@ -271,6 +273,8 @@ export function createInitialState(): EditorRuntimeState { scale: 1, panX: 0, panY: 0, + gridEnabled: false, + snapEnabled: true, guides: { xs: [], ys: [] }, isDragging: false, isPanning: false, @@ -366,6 +370,28 @@ export function editorReducer(state: EditorRuntimeState, action: EditorAction): }; } + case 'toggleGrid': { + return { + ...state, + canvas: { + ...state.canvas, + gridEnabled: !state.canvas.gridEnabled, + }, + }; + } + + case 'toggleSnap': { + // Clearing guides prevents stale guide lines when turning snap off mid-drag. + return { + ...state, + canvas: { + ...state.canvas, + snapEnabled: !state.canvas.snapEnabled, + guides: { xs: [], ys: [] }, + }, + }; + } + case 'updateWidgetRect': { const target = state.doc.screen.nodes.find((n) => n.id === action.id); if (!target) return state; @@ -874,8 +900,8 @@ export function editorReducer(state: EditorRuntimeState, action: EditorAction): const bounds = action.bounds; - // goView-like: only snap when a single node is selected. - const canSnap = state.selection.ids.length === 1; + // goView-like: only snap when enabled and a single node is selected. + const canSnap = state.canvas.snapEnabled && state.selection.ids.length === 1; const movingId = state.selection.ids[0]; const others = canSnap @@ -1058,8 +1084,8 @@ export function editorReducer(state: EditorRuntimeState, action: EditorAction): h: Math.max(0, newH), }; - // goView-like: only snap when resizing a single selected node. - const canSnap = state.selection.ids.length === 1; + // goView-like: only snap when enabled and resizing a single selected node. + const canSnap = state.canvas.snapEnabled && state.selection.ids.length === 1; const movingId = drag.targetId; const others = canSnap diff --git a/packages/editor/src/editor/types.ts b/packages/editor/src/editor/types.ts index 649b1aa..62c76cb 100644 --- a/packages/editor/src/editor/types.ts +++ b/packages/editor/src/editor/types.ts @@ -16,6 +16,8 @@ export interface EditorCanvasState { scale: number; panX: number; panY: number; + gridEnabled: boolean; + snapEnabled: boolean; guides: { xs: number[]; ys: number[];