248 lines
6.1 KiB
TypeScript
248 lines
6.1 KiB
TypeScript
import { BaseCommand } from './base-command';
|
|
import { useLayerStore } from '@/store';
|
|
import type { Layer, LayerUpdate, CreateLayerParams, Command } from '@/types';
|
|
|
|
/**
|
|
* Command to create a new layer
|
|
*/
|
|
export class CreateLayerCommand extends BaseCommand {
|
|
private layerId: string | null = null;
|
|
private layer: Layer | null = null;
|
|
|
|
constructor(private params: CreateLayerParams) {
|
|
super('Create Layer');
|
|
}
|
|
|
|
execute(): void {
|
|
const store = useLayerStore.getState();
|
|
this.layer = store.createLayer(this.params);
|
|
this.layerId = this.layer.id;
|
|
}
|
|
|
|
undo(): void {
|
|
if (!this.layerId) return;
|
|
const store = useLayerStore.getState();
|
|
store.deleteLayer(this.layerId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Command to delete a layer
|
|
*/
|
|
export class DeleteLayerCommand extends BaseCommand {
|
|
private layer: Layer | null = null;
|
|
private wasActive: boolean = false;
|
|
|
|
constructor(private layerId: string) {
|
|
super('Delete Layer');
|
|
}
|
|
|
|
execute(): void {
|
|
const store = useLayerStore.getState();
|
|
this.layer = store.getLayer(this.layerId) || null;
|
|
this.wasActive = store.activeLayerId === this.layerId;
|
|
|
|
if (this.layer) {
|
|
store.deleteLayer(this.layerId);
|
|
}
|
|
}
|
|
|
|
undo(): void {
|
|
if (!this.layer) return;
|
|
|
|
const store = useLayerStore.getState();
|
|
const layers = [...store.layers];
|
|
|
|
// Re-insert layer at its original position
|
|
layers.splice(this.layer.order, 0, this.layer);
|
|
|
|
// Update order for all layers
|
|
const reorderedLayers = layers.map((l, index) => ({
|
|
...l,
|
|
order: index,
|
|
}));
|
|
|
|
// Manually update the store
|
|
useLayerStore.setState({
|
|
layers: reorderedLayers,
|
|
activeLayerId: this.wasActive ? this.layerId : store.activeLayerId,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Command to update layer properties
|
|
*/
|
|
export class UpdateLayerCommand extends BaseCommand {
|
|
private oldValues: LayerUpdate;
|
|
private newValues: LayerUpdate;
|
|
|
|
constructor(
|
|
private layerId: string,
|
|
updates: LayerUpdate,
|
|
commandName?: string
|
|
) {
|
|
super(commandName || 'Update Layer');
|
|
|
|
const store = useLayerStore.getState();
|
|
const layer = store.getLayer(layerId);
|
|
|
|
if (!layer) {
|
|
throw new Error(`Layer ${layerId} not found`);
|
|
}
|
|
|
|
// Store old values
|
|
this.oldValues = {};
|
|
this.newValues = updates;
|
|
|
|
Object.keys(updates).forEach((key) => {
|
|
this.oldValues[key as keyof LayerUpdate] = layer[key as keyof Layer] as any;
|
|
});
|
|
}
|
|
|
|
execute(): void {
|
|
const store = useLayerStore.getState();
|
|
store.updateLayer(this.layerId, this.newValues);
|
|
}
|
|
|
|
undo(): void {
|
|
const store = useLayerStore.getState();
|
|
store.updateLayer(this.layerId, this.oldValues);
|
|
}
|
|
|
|
/**
|
|
* Merge consecutive updates to the same layer
|
|
*/
|
|
merge(other: Command): boolean {
|
|
if (!(other instanceof UpdateLayerCommand)) return false;
|
|
if (other.layerId !== this.layerId) return false;
|
|
|
|
// Merge within 1 second
|
|
if (other.timestamp - this.timestamp > 1000) return false;
|
|
|
|
// Update new values with the latest changes
|
|
Object.assign(this.newValues, other.newValues);
|
|
this.timestamp = other.timestamp;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Command to duplicate a layer
|
|
*/
|
|
export class DuplicateLayerCommand extends BaseCommand {
|
|
private newLayerId: string | null = null;
|
|
|
|
constructor(private sourceLayerId: string) {
|
|
super('Duplicate Layer');
|
|
}
|
|
|
|
execute(): void {
|
|
const store = useLayerStore.getState();
|
|
const newLayer = store.duplicateLayer(this.sourceLayerId);
|
|
this.newLayerId = newLayer?.id || null;
|
|
}
|
|
|
|
undo(): void {
|
|
if (!this.newLayerId) return;
|
|
const store = useLayerStore.getState();
|
|
store.deleteLayer(this.newLayerId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Command to reorder a layer
|
|
*/
|
|
export class ReorderLayerCommand extends BaseCommand {
|
|
private oldOrder: number;
|
|
private newOrder: number;
|
|
|
|
constructor(private layerId: string, newOrder: number) {
|
|
super('Reorder Layer');
|
|
|
|
const store = useLayerStore.getState();
|
|
const layer = store.getLayer(layerId);
|
|
|
|
if (!layer) {
|
|
throw new Error(`Layer ${layerId} not found`);
|
|
}
|
|
|
|
this.oldOrder = layer.order;
|
|
this.newOrder = newOrder;
|
|
}
|
|
|
|
execute(): void {
|
|
const store = useLayerStore.getState();
|
|
store.reorderLayer(this.layerId, this.newOrder);
|
|
}
|
|
|
|
undo(): void {
|
|
const store = useLayerStore.getState();
|
|
store.reorderLayer(this.layerId, this.oldOrder);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Command to merge layer down
|
|
*/
|
|
export class MergeLayerDownCommand extends BaseCommand {
|
|
private topLayer: Layer | null = null;
|
|
private bottomLayerBefore: HTMLCanvasElement | null = null;
|
|
private bottomLayerId: string | null = null;
|
|
|
|
constructor(private layerId: string) {
|
|
super('Merge Down');
|
|
}
|
|
|
|
execute(): void {
|
|
const store = useLayerStore.getState();
|
|
const layers = store.layers;
|
|
const layerIndex = layers.findIndex((l) => l.id === this.layerId);
|
|
|
|
if (layerIndex === -1 || layerIndex === 0) return;
|
|
|
|
this.topLayer = { ...layers[layerIndex] };
|
|
const bottomLayer = layers[layerIndex - 1];
|
|
this.bottomLayerId = bottomLayer.id;
|
|
|
|
// Clone bottom layer canvas before merge
|
|
if (bottomLayer.canvas) {
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = bottomLayer.canvas.width;
|
|
canvas.height = bottomLayer.canvas.height;
|
|
const ctx = canvas.getContext('2d');
|
|
if (ctx) {
|
|
ctx.drawImage(bottomLayer.canvas, 0, 0);
|
|
}
|
|
this.bottomLayerBefore = canvas;
|
|
}
|
|
|
|
store.mergeDown(this.layerId);
|
|
}
|
|
|
|
undo(): void {
|
|
if (!this.topLayer || !this.bottomLayerId || !this.bottomLayerBefore) return;
|
|
|
|
const store = useLayerStore.getState();
|
|
|
|
// Restore bottom layer canvas
|
|
const bottomLayer = store.getLayer(this.bottomLayerId);
|
|
if (bottomLayer && bottomLayer.canvas) {
|
|
const ctx = bottomLayer.canvas.getContext('2d');
|
|
if (ctx) {
|
|
ctx.clearRect(0, 0, bottomLayer.canvas.width, bottomLayer.canvas.height);
|
|
ctx.drawImage(this.bottomLayerBefore, 0, 0);
|
|
}
|
|
}
|
|
|
|
// Re-create top layer
|
|
const layers = [...store.layers];
|
|
layers.splice(this.topLayer.order, 0, this.topLayer);
|
|
|
|
useLayerStore.setState({
|
|
layers: layers.map((l, index) => ({ ...l, order: index })),
|
|
});
|
|
}
|
|
}
|