Session Persistence
Relevant source files
The following files were used as context for generating this wiki page:
Session Persistence is a critical system within Dotorixel designed to automatically save and restore the user's workspace state. It ensures that pixel art progress, tab configurations, and editor settings survive page refreshes or browser restarts without requiring explicit user action. The system aims to provide a "it just remembers" experience, falling back to a default blank canvas only if no saved data is available or if data is corrupted.
Sources: issues/007-session-persistence.md:10-18, issues/008-single-tab-session-save-restore.md:12-16
The system is architected as a "deep module" that abstracts IndexedDB operations, state serialization, and debounced auto-save logic behind a unified session handle. It manages the entire lifecycle of workspace data, including multi-tab support, viewport positions, and shared tool states.
Sources: issues/012-deepen-session-persistence.md:20-30, issues/009-multi-tab-session-persistence.md:12-18
System Architecture
The session persistence system follows a layered architecture composed of four main components, unified by a facade for external consumption.
Component Overview
| Component | Responsibility |
|---|---|
Session Facade (session.ts) | The entry point for the editor. Orchestrates initialization and returns a SessionHandle. |
AutoSave (auto-save.ts) | Manages dirty tracking and debounced execution of save operations. |
SessionPersistence (session-persistence.ts) | Orchestrates the extraction of state from the Workspace and reconstruction of state during restoration. |
SessionStorage (session-storage.ts) | A typed IndexedDB wrapper providing CRUD operations for documents and workspace metadata. |
Sources: issues/012-deepen-session-persistence.md:5-15, src/lib/session/session.ts
Data Flow Diagram
The following diagram illustrates how the system initializes and handles data updates during an active session.
Storage Schema (IndexedDB)
Dotorixel uses IndexedDB (via the idb library) for persistent storage because it supports binary data (Uint8Array) natively and provides significantly higher storage limits than localStorage.
Sources: issues/007-session-persistence.md:25-30, issues/007-session-persistence.md:118-125
Object Stores
The database, named dotorixel (version 1), consists of two primary object stores:
documents: Stores individual canvas/tab data. Each record includes the pixel buffer as aUint8Array, dimensions, and metadata.workspace: A singleton store containing the global session state, such as tab order, active tab index, and viewport states.
Sources: issues/007-session-persistence.md:31-40, src/lib/session/session-storage.ts
Data Models
| Store | Field | Type | Description |
|---|---|---|---|
| documents | id | string | Primary Key (UUID). |
name | string | Display name of the tab (e.g., "Untitled 1"). | |
pixels | Uint8Array | Binary pixel data extracted from WASM. | |
| workspace | tabOrder | string[] | Ordered list of document IDs. |
activeTabIndex | number | The index of the currently viewed tab. | |
sharedState | object | Shared tool, colors, and recent color history. |
Sources: issues/007-session-persistence.md:32-38, issues/010-debounced-auto-save.md:38-42
Auto-Save and Dirty Tracking
To minimize performance overhead and prevent data loss, the system implements a debounced auto-save mechanism with selective writes.
Save Timing Logic
- Dirty Flag: When the canvas is modified (draw, resize) or workspace structure changes (tab add/close), the affected document or the workspace is marked as "dirty".
- Debounce Interval: Saves are flushed to IndexedDB after a period of inactivity (default 3–5 seconds).
- Immediate Flush: A forced save is triggered during
beforeunloadorvisibilitychange(specifically for mobile Safari) to ensure no data is lost when the app is closed.
Sources: issues/007-session-persistence.md:43-50, issues/010-debounced-auto-save.md:12-22
Selective Writes
The AutoSave class tracks specific dirtyDocIds. When a save is triggered, only documents that have actually changed are rewritten to IndexedDB. This prevents unnecessary I/O for unchanged tabs in a multi-tab session.
Sources: src/lib/session/auto-save.ts:20-25, issues/010-debounced-auto-save.md:46-50
Session Lifecycle
The session lifecycle is managed through the openSession facade, which simplifies the interaction between the UI and the persistence layer.
Initialization Sequence
The Session Handle
The openSession function returns a SessionHandle interface, which is the sole point of interaction for the UI to signal state changes.
interface SessionHandle {
markDirty(documentId: string): void;
notifyTabClosed(documentId: string): void;
flush(): Promise<void>;
dispose(): void;
}
Sources: src/lib/session/session.ts:8-13, issues/012-deepen-session-persistence.md:41-48
Implementation Details
Serialization and WASM Integration
The persistence layer interacts directly with the WASM-managed core. It uses pixelCanvas.pixels() to extract raw data and WasmPixelCanvas.from_pixels(w, h, pixels) to reconstruct it. Viewport states are similarly extracted and restored via WasmViewport constructors.
Sources: issues/007-session-persistence.md:60-68, src/lib/session/session-persistence.ts
Multi-Tab Resilience
The system uses stable UUIDs for document IDs (crypto.randomUUID()). This allows IndexedDB to perform put() operations (upserts) reliably. When a tab is closed, the system explicitly calls notifyTabClosed to remove orphaned records from the documents store during the next save cycle.
Sources: issues/010-debounced-auto-save.md:46-48, issues/009-multi-tab-session-persistence.md:28-32
Failure Handling
The system is designed to be resilient. If IndexedDB is unavailable (e.g., private browsing mode) or if saved data is corrupted, openSession catches the error and returns a fresh Workspace alongside a NO_OP_SESSION handle. This allows the editor to remain functional without persistence features, showing no errors to the user.
Sources: issues/007-session-persistence.md:52-56, src/lib/session/session.ts:40-50
Conclusion
Session Persistence in Dotorixel provides a robust, invisible safety net for users. By combining IndexedDB's binary storage capabilities with a debounced, selective-write auto-save strategy, the system balances data integrity with performance. The facade-based architecture ensures that complex persistence logic remains decoupled from the UI, facilitating easier maintenance and future extensions like cloud sync or a "past work" browser.
Sources: issues/007-session-persistence.md:145-150, issues/012-deepen-session-persistence.md:105-110