Multi-Tab Workspace
Relevant source files
The following files were used as context for generating this wiki page:
The Multi-Tab Workspace is a core architectural feature of DOTORIXEL that enables users to work on multiple related pixel art assets simultaneously. It replaces the single-image workflow with a system that manages multiple independent images, each with its own canvas, viewport, and undo history, while maintaining shared global states such as tools and colors.
This system is built using a Workspace + SharedState pattern. The Workspace class acts as the orchestrator, managing a collection of EditorState instances (tabs). This allows for seamless switching between different project files without losing progress or state information.
Architecture and Data Flow
The architecture centralizes tab management in the Workspace class, which manages an array of EditorState objects. A key innovation is the SharedState object, which ensures that tool selections and color palettes remain consistent across all open tabs, preventing the need for re-selection when switching tasks.
Core Component Relationships
The following diagram illustrates the relationship between the Workspace, the individual tabs (EditorStates), and the shared global state.
Component Responsibilities
| Component | Responsibility | Key Data Held |
|---|---|---|
| Workspace | Tab orchestration, creation, and destruction. | List of tabs, index of the active tab, tab name counter. |
| EditorState | Management of a single image's local state. | Pixel data, undo/redo history, zoom level, pan position. |
| SharedState | Global tool and color configuration. | Active tool, FG/BG colors, recent colors list. |
| TabStrip UI | Visual representation and interaction layer for tabs. | Tab labels, active indicators, close/add buttons. |
Sources: issues/002-tab-system.md, issues/004-workspace-class.md
Tab Management Logic
The Workspace class handles the lifecycle of tabs through several key methods. It ensures that the application always has at least one active tab and manages the naming convention for new images.
Key Lifecycle Methods
addTab(): Creates a new 16x16EditorStateusing the sharedSharedStatereference. It assigns a sequential name (e.g., "Untitled 2") and automatically sets the new tab as active.closeTab(index): Removes a tab from the array. If the tab being closed is the active one, the system automatically activates the right neighbor (or the left if it was the rightmost tab).setActiveTab(index): Updates theactiveTabIndex. This triggers reactivity in the UI, causing thePixelCanvasViewand panels to update their data source to the newly selected tab.
Sources: src/lib/canvas/workspace.svelte.ts:50-110, issues/004-workspace-class.md
Tab Activation Flow
When a user interacts with the TabStrip or closes a tab, the workspace follows a specific logic to determine which tab should become active next.
User Interface: TabStrip
The TabStrip component provides the visual interface for the workspace. It is integrated into both the Docked (desktop) and Tab (mobile) layout paradigms.
Visual Design and Interaction
- Responsive Sizing: The TabStrip adapts its height based on the device: 36px for desktop, 32px for tablet, and 28px for mobile.
- Active State: The active tab is visually distinguished by an elevated background and a 2px accent indicator at the bottom.
- Overflow Management: The strip supports horizontal scrolling (touch swipe or trackpad) with a hidden scrollbar to accommodate many open tabs.
- Automatic Scrolling: When a new tab is created or an inactive tab is selected, the
TabStripensures it is scrolled into the visible area.
Sources: src/lib/ui-editor/TabStrip.svelte:10-50, issues/002-tab-system.md, src/routes/editor/+page.svelte:182-192
Integration in Page Shell
The editor page shell (+page.svelte) instantiates the Workspace and uses a $derived rune to provide the activeEditor to child components. This indirection allows the entire UI to switch context instantly when workspace.activeTabIndex changes.
// src/routes/editor/+page.svelte:31-37
let workspace = $state(
new Workspace({
foregroundColor: { r: 45, g: 45, b: 45, a: 255 },
gridColor: '#ECE5D9'
})
);
const editor = $derived(workspace.activeEditor);
State Independence and Persistence
Each tab maintains strict independence for data that belongs to the specific image:
- Pixel Canvas: The raw pixel data and dimensions.
- History: An independent
WasmHistoryManagerfor undo/redo. - Viewport: Zoom levels and pan coordinates are unique to each tab, allowing users to focus on different parts of different images.
While this system currently operates in-memory, the architecture supports future session persistence by assigning unique documentId values to each tab, which can be used to map to IndexedDB records.
Sources: issues/002-tab-system.md, src/lib/canvas/workspace.svelte.ts:25-30, src/routes/editor/+page.svelte:92-101
Conclusion
The Multi-Tab Workspace transforms DOTORIXEL into a professional-grade editor capable of handling complex multi-asset workflows. By separating shared tool state from independent image state, it provides a predictable and efficient environment for pixel artists. The system's robust tab lifecycle logic and responsive UI ensure that workspace management remains intuitive across all device form factors.