Files
paint-ui/tools/move-tool.ts
Sebastian Krüger 6e35849f4e feat: implement MoveCommand for undoable layer movement
Critical Fix - Move Tool Undo/Redo:
- Create MoveCommand class to track layer position changes
- Capture initial and final positions during move operation
- Only add to history if position actually changed
- Real-time visual feedback during drag (via updateLayer)
- Single undo point per move operation (not per pixel)

Command Pattern:
- MoveCommand extends BaseCommand
- Implements execute() and undo() methods
- captureAfterPosition() called on pointer up
- hasChanged() prevents no-op entries in history

Files:
- core/commands/move-command.ts - New command class
- tools/move-tool.ts - Updated to use MoveCommand
- core/commands/index.ts - Export MoveCommand

This fixes the critical issue where moving layers had no undo support.
Users can now undo/redo layer movements as expected.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 09:19:09 +01:00

80 lines
2.0 KiB
TypeScript

import { BaseTool } from './base-tool';
import type { PointerState } from '@/types';
import { useLayerStore } from '@/store/layer-store';
import { useHistoryStore } from '@/store/history-store';
import { MoveCommand } from '@/core/commands';
export class MoveTool extends BaseTool {
private startX = 0;
private startY = 0;
private layerStartX = 0;
private layerStartY = 0;
private moveCommand: MoveCommand | null = null;
constructor() {
super('Move');
}
onPointerDown(pointer: PointerState): void {
this.isActive = true;
this.isDrawing = true;
const layer = this.getActiveLayer();
if (!layer) return;
this.startX = pointer.x;
this.startY = pointer.y;
this.layerStartX = layer.x;
this.layerStartY = layer.y;
// Create move command with initial position
this.moveCommand = new MoveCommand(layer.id, { x: layer.x, y: layer.y });
}
onPointerMove(pointer: PointerState): void {
if (!this.isDrawing) return;
const layer = this.getActiveLayer();
if (!layer) return;
const dx = pointer.x - this.startX;
const dy = pointer.y - this.startY;
// Update position in real-time (for visual feedback)
const { updateLayer } = useLayerStore.getState();
updateLayer(layer.id, {
x: this.layerStartX + dx,
y: this.layerStartY + dy,
});
}
onPointerUp(): void {
if (this.moveCommand) {
const layer = this.getActiveLayer();
if (layer) {
// Capture final position
this.moveCommand.captureAfterPosition(layer.x, layer.y);
// Only add to history if position actually changed
if (this.moveCommand.hasChanged()) {
const { executeCommand } = useHistoryStore.getState();
executeCommand(this.moveCommand);
}
}
this.moveCommand = null;
}
this.isDrawing = false;
this.isActive = false;
}
getCursor(): string {
return 'move';
}
private getActiveLayer() {
const { activeLayerId, layers } = useLayerStore.getState();
return layers.find((l) => l.id === activeLayerId);
}
}