import { create } from "zustand"; import type { ClockBlock, ClockRow, Signal } from "../assets/ClockTimeline/model"; export const defaultClockSignal: Signal = { type: "virtual-signal", name: "signal-clock", subgroup: "pictographs", icon: "virtual-signal/signal-clock.png", order: "p[clock]", }; interface ClockState { // Global Settings duration: number; pixelsPerTick: number; setDuration: (duration: number) => void; setZoom: (val: number) => void; clockSignal: Signal; setClockSignal: (signal: Signal | null) => void; // Normalized Data Models rows: Record; rowOrder: string[]; // Maintains vertical rendering order blocks: Record; // UI State selectedBlockIds: Set; alignmentTick: number | null; setAlignmentTick: (tick: number | null) => void; // --- Actions --- loadState: (state: Partial) => void; // Row Management addRow: (row: ClockRow) => void; updateRow: (id: string, patch: Partial) => void; removeRow: (id: string) => void; // Block Management addBlocks: (newBlocks: ClockBlock[]) => void; updateBlock: (id: string, patch: Partial) => void; // High-performance batch update for dragging multiple blocks at once moveBlocks: (updates: Record) => void; removeBlocks: (ids: string[]) => void; // Selection selectBlocks: (ids: Set | ((prev: Set) => Set)) => void; clearSelection: () => void; } export const useClockStore = create((set) => ({ duration: 256, pixelsPerTick: 4, clockSignal: defaultClockSignal, // Set your default signal here rows: {}, rowOrder: [], blocks: {}, selectedBlockIds: new Set(), alignmentTick: null, loadState: (state: Partial) => set(() => ({ ...state })), setDuration: (duration) => set({ duration }), setZoom: (val) => set({ pixelsPerTick: val }), setClockSignal: (clockSignal) => { if (clockSignal) set({ clockSignal }); }, setAlignmentTick: (tick) => set({ alignmentTick: tick }), addRow: (row) => set((state) => ({ rows: { ...state.rows, [row.id]: row }, rowOrder: [...state.rowOrder, row.id], })), updateRow: (id, patch) => set((state) => ({ rows: { ...state.rows, [id]: { ...state.rows[id], ...patch }, }, })), removeRow: (id) => set((state) => { const newRows = { ...state.rows }; delete newRows[id]; // Also clean up orphan blocks const newBlocks = Object.fromEntries(Object.entries(state.blocks).filter(([_, block]) => block.rowId !== id)); return { rows: newRows, rowOrder: state.rowOrder.filter((rowId) => rowId !== id), blocks: newBlocks, }; }), addBlocks: (newBlocks) => set((state) => { const blockMap = { ...state.blocks }; newBlocks.forEach((b) => { blockMap[b.id] = b; }); return { blocks: blockMap }; }), updateBlock: (id, patch) => set((state) => ({ blocks: { ...state.blocks, [id]: { ...state.blocks[id], ...patch }, }, })), moveBlocks: (updates) => set((state) => { const nextBlocks = { ...state.blocks }; for (const [id, patch] of Object.entries(updates)) { if (nextBlocks[id]) { nextBlocks[id] = { ...nextBlocks[id], ...patch }; } } return { blocks: nextBlocks }; }), removeBlocks: (ids) => set((state) => { const newBlocks = { ...state.blocks }; ids.forEach((id) => delete newBlocks[id]); const newSelection = new Set(state.selectedBlockIds); ids.forEach((id) => newSelection.delete(id)); return { blocks: newBlocks, selectedBlockIds: newSelection }; }), selectBlocks: (idsOrUpdater) => set((state) => ({ selectedBlockIds: typeof idsOrUpdater === "function" ? idsOrUpdater(state.selectedBlockIds) : idsOrUpdater, })), clearSelection: () => set({ selectedBlockIds: new Set() }), }));