import type { Deck, Slide, Element, ShapeType } from "./api"; import { resolveDeckBlobs } from "./api"; // editor state export const editorState = $state({ deck: null as Deck | null, currentSlideIndex: 0, selectedElements: new Set(), isPresenting: false, showNotes: false, presentationStartTime: null as number | null, presenterPanelHeight: 80, // default height in pixels }); export const getCurrentSlide = (): Slide | null => { if (!editorState.deck) return null; return editorState.deck.slides[editorState.currentSlideIndex] || null; }; export const addSlide = () => { if (!editorState.deck) return; const newSlide: Slide = { elements: [], notes: "", createdAt: new Date().toISOString() }; editorState.deck.slides.push(newSlide); editorState.currentSlideIndex = editorState.deck.slides.length - 1; }; export const deleteSlide = (index: number) => { if (!editorState.deck || editorState.deck.slides.length <= 1) return; editorState.deck.slides.splice(index, 1); if (editorState.currentSlideIndex >= editorState.deck.slides.length) { editorState.currentSlideIndex = editorState.deck.slides.length - 1; } }; export const addElement = (type: Element["type"], shapeType?: ShapeType) => { const slide = getCurrentSlide(); if (!slide) return; const element: Element = { type, x: 100, y: 100, width: type === "text" ? 400 : 200, height: type === "text" ? 100 : 200, content: type === "text" ? "new text" : "", fontSize: type === "text" ? 32 : undefined, color: type === "text" ? "#ffffff" : "#3b82f6", shapeType: type === "shape" ? (shapeType || "rectangle") : undefined, }; slide.elements.push(element); editorState.selectedElements = new Set([slide.elements.length - 1]); }; export const deleteSelectedElements = () => { const slide = getCurrentSlide(); if (!slide || editorState.selectedElements.size === 0) return; // Delete in reverse order to preserve indices const indices = Array.from(editorState.selectedElements).sort((a, b) => b - a); for (const index of indices) { slide.elements.splice(index, 1); } editorState.selectedElements = new Set(); }; export const nextSlide = () => { if (!editorState.deck) return; if (editorState.currentSlideIndex < editorState.deck.slides.length - 1) { editorState.currentSlideIndex++; editorState.selectedElements = new Set(); } }; export const prevSlide = () => { if (editorState.currentSlideIndex > 0) { editorState.currentSlideIndex--; editorState.selectedElements = new Set(); } }; export const goToSlide = (index: number) => { if (!editorState.deck) return; if (index >= 0 && index < editorState.deck.slides.length) { editorState.currentSlideIndex = index; editorState.selectedElements = new Set(); } }; export const startPresenting = () => { editorState.isPresenting = true; editorState.presentationStartTime = Date.now(); editorState.selectedElements = new Set(); }; export const stopPresenting = () => { editorState.isPresenting = false; editorState.presentationStartTime = null; }; export const newDeck = (name: string = "untitled") => { const now = new Date().toISOString(); editorState.deck = { name, slides: [{ elements: [], notes: "", createdAt: now }], createdAt: now, }; editorState.currentSlideIndex = 0; editorState.selectedElements = new Set(); }; export const loadDeck = (deck: Deck) => { // Resolve blob refs to displayable URLs if the deck has a repo (DID) const resolvedDeck = deck.repo ? resolveDeckBlobs(deck, deck.repo) : deck; editorState.deck = resolvedDeck; editorState.currentSlideIndex = 0; editorState.selectedElements = new Set(); }; export const closeDeck = () => { editorState.deck = null; editorState.currentSlideIndex = 0; editorState.selectedElements = new Set(); editorState.isPresenting = false; }; // Helper for selection export const selectElement = (index: number, addToSelection: boolean = false) => { if (addToSelection) { const newSet = new Set(editorState.selectedElements); if (newSet.has(index)) { newSet.delete(index); } else { newSet.add(index); } editorState.selectedElements = newSet; } else { editorState.selectedElements = new Set([index]); } }; export const clearSelection = () => { editorState.selectedElements = new Set(); }; // Get single selected element (null if none or multiple) export const getSelectedElement = () => { const slide = getCurrentSlide(); if (!slide || editorState.selectedElements.size !== 1) return null; const index = Array.from(editorState.selectedElements)[0]; return slide.elements[index] || null; };