Editor API Reference
ElucimEditor
Section titled “ElucimEditor”The main editor component — all-in-one canvas, toolbar, inspector, and timeline.
interface ElucimEditorProps { initialDocument?: ElucimDocument; initialFrame?: number | 'last'; theme?: ElucimTheme; // unified content theme editorTheme?: Record<string, string>; // explicit chrome overrides onDocumentChange?: (document: ElucimDocument) => void; onBrowseImage?: BrowseImageFn; // custom image picker callback imageResolver?: ImageResolverFn; // resolve image refs to URLs className?: string; style?: React.CSSProperties;}| Prop | Type | Default | Description |
|---|---|---|---|
initialDocument | ElucimDocument | Empty 800×600 scene | Starting scene document |
initialFrame | number | 'last' | 0 | Starting frame. Use 'last' to start at final frame. |
theme | ElucimTheme | — | Unified content theme — same type used by DslRenderer. Editor chrome is auto-derived from content tokens. |
editorTheme | Record<string, string> | — | Explicit chrome overrides — accepts hex, named colors, or var() refs. Overrides auto-derived values. |
onDocumentChange | (doc: ElucimDocument) => void | — | Called on every document change (skips initial mount) |
onBrowseImage | BrowseImageFn | — | Custom image picker callback. When set, shows a ”…” browse button next to image fields. See Image Assets. |
imageResolver | ImageResolverFn | — | Resolves opaque image ref values to renderable URLs. See Image Assets. |
className | string | — | CSS class on root element |
style | CSSProperties | — | Inline styles on root element |
Observing document changes
Section titled “Observing document changes”function MyApp() { const handleChange = (doc: ElucimDocument) => { // Called on every edit — debounce if doing expensive work console.log('Document changed:', doc); };
return ( <ElucimEditor initialDocument={myDoc} onDocumentChange={handleChange} /> );}ElucimEditorLayout
Section titled “ElucimEditorLayout”The internal layout component used by ElucimEditor. Must be rendered inside an EditorProvider.
Use this when you need custom composition (e.g. adding your own panels) while keeping the
standard floating-panel layout, scrollbar styles, and theme injection.
import { EditorProvider, ElucimEditorLayout, useEditorDocument } from '@elucim/editor';
function SaveButton() { const doc = useEditorDocument(); return <button onClick={() => save(doc)}>Save</button>;}
function MyEditor({ doc }: { doc: ElucimDocument }) { return ( <EditorProvider initialDocument={doc}> <ElucimEditorLayout theme={myTheme} /> <SaveButton /> </EditorProvider> );}Components
Section titled “Components”| Export | Description |
|---|---|
ElucimEditor | Main editor — canvas + toolbar + inspector + timeline |
ElucimEditorLayout | Internal layout for custom composition inside EditorProvider |
ElucimCanvas | Canvas with zoom/pan and selection overlays |
Toolbar | Element template palette |
Inspector | Property editing panel |
Timeline | Animation playback controls |
FloatingPanel | Draggable floating container |
SelectionOverlay | Selection boxes, resize/rotate handles |
DotGrid | Zoom-adaptive dot grid background |
Minimap | Canvas overview minimap |
ZoomControls | Zoom buttons and zoom-to-fit |
useEditorState()
Section titled “useEditorState()”Returns the full editor state and dispatch function.
function useEditorState(): [EditorState, React.Dispatch<EditorAction>];const [state, dispatch] = useEditorState();
// Read stateconsole.log(state.selectedIds);console.log(state.viewport.zoom);
// Dispatch actionsdispatch({ type: 'SELECT', ids: ['element-1'] });dispatch({ type: 'UNDO' });useEditorDocument()
Section titled “useEditorDocument()”Returns the current ElucimDocument.
function useEditorDocument(): ElucimDocument;useEditorSelection()
Section titled “useEditorSelection()”Returns the array of selected element IDs.
function useEditorSelection(): string[];Other Hooks
Section titled “Other Hooks”| Hook | Description |
|---|---|
useViewport() | Zoom/pan state and handlers |
useDrag() | Element drag, resize, and rotate interactions |
useMarquee() | Marquee (lasso) selection interactions |
useMeasuredBounds() | DOM-based element bounds measurement |
State Types
Section titled “State Types”EditorState
Section titled “EditorState”interface EditorState { document: ElucimDocument; selectedIds: string[]; viewport: { x: number; y: number; zoom: number }; past: ElucimDocument[]; future: ElucimDocument[]; currentFrame: number; isPlaying: boolean; activeTool: EditorTool; isPanning: boolean; toolbarPosition: { x: number; y: number }; inspectorPosition: { x: number; y: number } | null; inspectorPinned: boolean; toolbarCollapsed: boolean;}EditorTool
Section titled “EditorTool”type EditorTool = 'select' | 'rect' | 'circle' | 'line' | 'arrow' | 'text' | 'latex';EditorAction
Section titled “EditorAction”All available dispatch actions:
| Action | Payload | Description |
|---|---|---|
SELECT | ids: string[] | Replace selection |
SELECT_ADD | id: string | Add to selection |
SELECT_TOGGLE | id: string | Toggle element in/out of selection |
DESELECT_ALL | — | Clear selection |
SET_DOCUMENT | document: ElucimDocument | Replace entire document |
UPDATE_ELEMENT | id: string, changes: object | Update element properties |
UPDATE_CANVAS | changes: object | Update scene root properties |
ADD_ELEMENT | element: object | Add element to scene |
DELETE_ELEMENTS | ids: string[] | Remove elements |
DUPLICATE_ELEMENTS | ids: string[], offset?: {dx, dy} | Clone elements with offset (default +20,+20) |
MOVE_ELEMENT | id: string, dx: number, dy: number | Translate element (moves all selected if multi-selected) |
RESIZE_ELEMENT | id: string, handle: string, dx, dy, constrain?: boolean | Resize element. constrain forces uniform scaling. |
ROTATE_ELEMENT | id: string, angleDeg: number | Rotate element |
GROUP_ELEMENTS | ids: string[] | Group 2+ elements |
UNGROUP | id: string | Ungroup a group element |
RENAME_ELEMENT | id: string, newId: string | Rename element ID |
REORDER_ELEMENT | id: string, newIndex: number | Change element z-order by index |
BRING_FORWARD | ids: string[] | Move elements one step forward |
SEND_BACKWARD | ids: string[] | Move elements one step backward |
BRING_TO_FRONT | ids: string[] | Move elements to front |
SEND_TO_BACK | ids: string[] | Move elements to back |
ALIGN_ELEMENTS | ids: string[], direction: AlignDirection | Align elements (left, right, top, bottom, center-h, center-v) |
DISTRIBUTE_ELEMENTS | ids: string[], direction: DistributeDirection | Distribute 3+ elements evenly (horizontal, vertical) |
SET_VIEWPORT | viewport: Partial<Viewport> | Update zoom/pan |
SET_FRAME | frame: number | Seek to frame |
SET_PLAYING | playing: boolean | Play or pause |
SET_TOOL | tool: EditorTool | Switch active tool |
ZOOM_TO_FIT | — | Auto-fit canvas to viewport |
UNDO | — | Undo last action |
REDO | — | Redo last undone action |
CANVAS_ID
Section titled “CANVAS_ID”Sentinel value used when the canvas itself is selected:
import { CANVAS_ID } from '@elucim/editor';// CANVAS_ID === '__canvas__'
const isCanvasSelected = selectedIds.includes(CANVAS_ID);Keyboard Shortcuts
Section titled “Keyboard Shortcuts”All shortcuts work when the editor canvas is focused. They are disabled when typing in input fields.
Editing
Section titled “Editing”| Shortcut | Action |
|---|---|
Delete / Backspace | Delete selected elements |
Ctrl+Z | Undo |
Ctrl+Y / Ctrl+Shift+Z | Redo |
Ctrl+A | Select all elements |
Ctrl+D | Duplicate selected elements |
Ctrl+C | Copy selected elements |
Ctrl+V | Paste copied elements |
Ctrl+G | Group selected elements (2+) |
Ctrl+Shift+G | Ungroup selected group |
Escape | Deselect all, reset to select tool |
Z-Order
Section titled “Z-Order”| Shortcut | Action |
|---|---|
Ctrl+] | Bring forward one step |
Ctrl+[ | Send backward one step |
Ctrl+Shift+] | Bring to front |
Ctrl+Shift+[ | Send to back |
Navigation
Section titled “Navigation”| Shortcut | Action |
|---|---|
Arrow Keys | Nudge selected elements by 1px |
Shift+Arrow Keys | Nudge by 10px |
Ctrl+= / Ctrl+- | Zoom in / out |
Ctrl+0 | Zoom to fit |
Ctrl+1 | Zoom to 100% |
Space (hold) | Pan mode |
Ctrl/Cmd+Scroll | Zoom at cursor |
Mouse Modifiers
Section titled “Mouse Modifiers”| Interaction | Behavior |
|---|---|
| Click element | Select (replaces selection) |
| Shift/Ctrl/Cmd+Click element | Toggle in/out of selection |
| Drag element | Move selected elements |
| Alt+Drag element | Duplicate in place, then drag copy |
| Shift+Drag resize handle | Constrained (uniform) resize |
| Drag on empty canvas | Marquee selection |
| Shift+Drag on empty canvas | Add to existing selection |
| Middle-click drag | Pan |
| Right-click | Context menu |
Context Menu
Section titled “Context Menu”Right-click on elements or the canvas to access:
- Group / Ungroup — combine or split element groups
- Duplicate / Copy / Paste / Delete — clipboard and element operations
- Bring Forward / Send Backward / Bring to Front / Send to Back — z-order controls
- Align (when 2+ selected) — Left, Right, Top, Bottom, Center ↔, Center ↕
- Distribute (when 3+ selected) — Horizontal, Vertical (even spacing)
- Select All / Deselect All
Utilities
Section titled “Utilities”Import / Export
Section titled “Import / Export”// Serialize document to JSON stringfunction exportToJson(document: ElucimDocument): string;
// Parse JSON string to documentfunction importFromJson(json: string): ElucimDocument;
// Trigger browser file downloadfunction downloadAsJson(document: ElucimDocument, filename?: string): void;Bounds & Hit Testing
Section titled “Bounds & Hit Testing”// Compute bounding box for a DSL elementfunction getElementBounds(element: object): { x: number; y: number; width: number; height: number } | null;
// Merge multiple bounding boxesfunction mergeBounds(boxes: Array<{ x: number; y: number; width: number; height: number }>): { x: number; y: number; width: number; height: number };
// Hit-test a point against a bounding boxfunction isPointInBounds( point: { x: number; y: number }, bounds: { x: number; y: number; width: number; height: number }): boolean;
// Snap a value to the nearest grid incrementfunction computeSnap(value: number, gridSize: number): number;Unified Theming
Section titled “Unified Theming”Pass the same ElucimTheme to both DslRenderer and ElucimEditor — the editor automatically derives its chrome tokens from the content theme:
import type { ElucimTheme } from '@elucim/core';
const myTheme: ElucimTheme = { foreground: '#e0e0e0', background: '#1a1a2e', primary: '#4fc3f7', muted: '#64748b', surface: '#1e293b', border: '#334155',};
// Same theme for both — no duplicate token maps needed<DslRenderer dsl={doc} theme={myTheme} colorScheme="dark" /><ElucimEditor initialDocument={doc} theme={myTheme} />Use editorTheme to override specific chrome tokens while keeping the auto-derivation:
<ElucimEditor initialDocument={doc} theme={myTheme} editorTheme={{ 'color-scheme': 'light', accent: '#ff6b35' }}/>Theming
Section titled “Theming”// Get CSS var() reference with fallbackfunction v(token: string): string;
// Merge theme overrides with defaultsfunction buildThemeVars(overrides?: Record<string, string>): Record<string, string>;
// Derive editor chrome from an ElucimTheme content theme.// Uses CSS color-mix() for panel/chrome/input-bg, so var() surface values work correctly.function deriveEditorTheme(contentTheme: ElucimTheme, colorScheme: 'light' | 'dark'): Record<string, string>;
// Generate SVG rotation cursor with custom colorfunction makeRotateCursor(color?: string): string;
// Pre-built rotation cursor with default accentconst ROTATE_CURSOR: string;
// Full token registry with defaultsconst EDITOR_TOKENS: Record<string, string>;
// Light-mode token defaults (used when color-scheme: 'light')const EDITOR_TOKENS_LIGHT: Record<string, string>;deriveEditorTheme in detail
Section titled “deriveEditorTheme in detail”Automatically maps content tokens → editor chrome tokens:
import { deriveEditorTheme } from '@elucim/editor';import type { ElucimTheme } from '@elucim/core';
const myTheme: ElucimTheme = { foreground: '#e0e0e0', background: '#1a1a2e', surface: 'var(--my-surface)', // CSS var() values work primary: '#4fc3f7',};
const chrome = deriveEditorTheme(myTheme, 'dark');// chrome.panel → 'color-mix(in srgb, var(--my-surface) 95%, transparent)'// chrome.chrome → 'color-mix(in srgb, var(--my-surface) 85%, transparent)'// chrome.fg → '#e0e0e0'// chrome.accent → '#4fc3f7'Panel, chrome, and input-bg use CSS color-mix() to derive semi-transparent variants from the surface color. This works with both hex values and var() references — no JavaScript color parsing needed.
Templates
Section titled “Templates”// All available element templatesconst ELEMENT_TEMPLATES: Array<{ type: string; category: string; label: string; defaults: object;}>;
// Templates grouped by categoryfunction getTemplatesByCategory(): Record<string, typeof ELEMENT_TEMPLATES>;
// Display labels for categoriesconst CATEGORY_LABELS: Record<string, string>;