f1ow 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/dist/assets/elbowWorker-CUBh1uET.js +1 -0
  4. package/dist/assets/exportWorker-hU9uNZXJ.js +8 -0
  5. package/dist/assets/syncWorker.worker-CnlpNZ3k.js +6 -0
  6. package/dist/components/Canvas/CanvasElement.d.ts +42 -0
  7. package/dist/components/Canvas/ConnectionPoints.d.ts +13 -0
  8. package/dist/components/Canvas/GridLayer.d.ts +13 -0
  9. package/dist/components/Canvas/LinearElementHandles.d.ts +17 -0
  10. package/dist/components/Canvas/SelectionBox.d.ts +12 -0
  11. package/dist/components/Canvas/SelectionTransformer.d.ts +7 -0
  12. package/dist/components/ContextMenu/ContextMenu.d.ts +23 -0
  13. package/dist/components/StylePanel/StylePanel.d.ts +7 -0
  14. package/dist/components/Toolbar/Toolbar.d.ts +9 -0
  15. package/dist/components/shapes/ArrowShape.d.ts +18 -0
  16. package/dist/components/shapes/DiamondShape.d.ts +24 -0
  17. package/dist/components/shapes/EllipseShape.d.ts +24 -0
  18. package/dist/components/shapes/FreeDrawShape.d.ts +24 -0
  19. package/dist/components/shapes/ImageShape.d.ts +24 -0
  20. package/dist/components/shapes/LineShape.d.ts +18 -0
  21. package/dist/components/shapes/RectangleShape.d.ts +24 -0
  22. package/dist/components/shapes/TextShape.d.ts +23 -0
  23. package/dist/constants/index.d.ts +44 -0
  24. package/dist/f1ow-canvas.js +37725 -0
  25. package/dist/f1ow-canvas.umd.cjs +371 -0
  26. package/dist/hooks/useEfficientZoom.d.ts +14 -0
  27. package/dist/hooks/useElbowShapeFingerprint.d.ts +21 -0
  28. package/dist/hooks/useElbowWorker.d.ts +23 -0
  29. package/dist/hooks/useKeyboardShortcuts.d.ts +7 -0
  30. package/dist/hooks/useProgressiveRender.d.ts +52 -0
  31. package/dist/hooks/useSpatialIndex.d.ts +9 -0
  32. package/dist/hooks/useViewportCulling.d.ts +13 -0
  33. package/dist/lib/FlowCanvas.d.ts +4 -0
  34. package/dist/lib/FlowCanvasProps.d.ts +154 -0
  35. package/dist/lib/index.d.ts +57 -0
  36. package/dist/store/useCanvasStore.d.ts +127 -0
  37. package/dist/store/useLinearEditStore.d.ts +25 -0
  38. package/dist/types/index.d.ts +257 -0
  39. package/dist/utils/alignment.d.ts +34 -0
  40. package/dist/utils/arrowheads.d.ts +20 -0
  41. package/dist/utils/camera.d.ts +63 -0
  42. package/dist/utils/clipboard.d.ts +4 -0
  43. package/dist/utils/clone.d.ts +29 -0
  44. package/dist/utils/connection.d.ts +122 -0
  45. package/dist/utils/crdtPrep.d.ts +97 -0
  46. package/dist/utils/curve.d.ts +51 -0
  47. package/dist/utils/elbow.d.ts +110 -0
  48. package/dist/utils/elbowWorkerManager.d.ts +53 -0
  49. package/dist/utils/export.d.ts +17 -0
  50. package/dist/utils/exportWorkerManager.d.ts +25 -0
  51. package/dist/utils/fractionalIndex.d.ts +51 -0
  52. package/dist/utils/geometry.d.ts +49 -0
  53. package/dist/utils/id.d.ts +1 -0
  54. package/dist/utils/image.d.ts +52 -0
  55. package/dist/utils/performance.d.ts +65 -0
  56. package/dist/utils/roughness.d.ts +104 -0
  57. package/dist/utils/spatialIndex.d.ts +109 -0
  58. package/dist/utils/spatialSoA.d.ts +82 -0
  59. package/dist/workers/elbowWorker.d.ts +18 -0
  60. package/dist/workers/exportWorker.d.ts +18 -0
  61. package/package.json +80 -0
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Snap a continuous zoom value to the nearest efficient level.
3
+ *
4
+ * Algorithm: binary-search the midpoints between adjacent levels.
5
+ * O(log n) with n = 8 levels ≈ 3 comparisons.
6
+ */
7
+ export declare function computeEfficientZoom(zoom: number): number;
8
+ /**
9
+ * React hook wrapping `computeEfficientZoom`.
10
+ * The returned value is memoised — it only changes when the zoom
11
+ * crosses a level boundary, keeping downstream consumers (LOD,
12
+ * stroke scaling) stable during rapid zoom gestures.
13
+ */
14
+ export declare function useEfficientZoom(zoom: number): number;
@@ -0,0 +1,21 @@
1
+ import { CanvasElement } from '../types';
2
+ /**
3
+ * Compute a stable fingerprint string from the spatial properties
4
+ * (x, y, width, height, rotation) of all connectable shapes.
5
+ *
6
+ * Used as a `useMemo` dependency in ArrowShape/LineShape instead of
7
+ * the raw `allElements` array — which gets a new reference on every
8
+ * render cycle and causes unnecessary elbow recomputation.
9
+ *
10
+ * The fingerprint only changes when a connectable shape's position,
11
+ * size, rotation, or visibility changes — style changes, text content
12
+ * changes, etc. are ignored since they don't affect routing.
13
+ *
14
+ * Returns both the fingerprint string AND the elements array ref.
15
+ * The elements ref is stable (won't trigger re-renders) and is used
16
+ * to pass to `computeElbowPoints` when the fingerprint changes.
17
+ */
18
+ export declare function useElbowShapeFingerprint(allElements: CanvasElement[] | undefined): {
19
+ fingerprint: string;
20
+ elementsRef: React.MutableRefObject<CanvasElement[]>;
21
+ };
@@ -0,0 +1,23 @@
1
+ import { CanvasElement, Binding, Point } from '../types';
2
+ /**
3
+ * Hook that computes elbow route points, offloading to a Web Worker
4
+ * when beneficial.
5
+ *
6
+ * @param isElbow - whether elbow routing is active (skip if false)
7
+ * @param params - routing parameters (start/end points, bindings)
8
+ * @param allElements - all canvas elements (for obstacle detection)
9
+ * @param fingerprint - stable spatial fingerprint (for dependency tracking)
10
+ * @returns flat number[] of elbow route points (relative to startWorld)
11
+ */
12
+ export declare function useElbowWorker(isElbow: boolean, params: {
13
+ startWorld: Point;
14
+ endWorld: Point;
15
+ startBinding: Binding | null;
16
+ endBinding: Binding | null;
17
+ minStubLength?: number;
18
+ }, allElements: CanvasElement[], fingerprint: string): number[] | null;
19
+ /**
20
+ * Cleanup function to dispose the Worker.
21
+ * Call from FlowCanvas's cleanup effect.
22
+ */
23
+ export { disposeElbowWorkerManager } from '../utils/elbowWorkerManager';
@@ -0,0 +1,7 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * Keyboard shortcuts handler — canvas-standard hotkeys
4
+ * @param enabled - Whether shortcuts are active (false = hooks still called, events ignored)
5
+ * @param containerRef - Optional ref to canvas container (needed for zoomToFit/zoomToSelection)
6
+ */
7
+ export declare function useKeyboardShortcuts(enabled?: boolean, containerRef?: React.RefObject<HTMLDivElement | null>): void;
@@ -0,0 +1,52 @@
1
+ import { CanvasElement } from '../types';
2
+ export interface UseProgressiveRenderOptions {
3
+ /** Elements per batch (default: 500) */
4
+ batchSize?: number;
5
+ /** Minimum element count to activate progressive mode (default: 500) */
6
+ threshold?: number;
7
+ /** Whether progressive rendering is enabled (default: true) */
8
+ enabled?: boolean;
9
+ }
10
+ export interface ProgressiveRenderState {
11
+ /** Elements to render this frame (progressively grows) */
12
+ visibleElements: CanvasElement[];
13
+ /** Whether progressive loading is in progress */
14
+ isLoading: boolean;
15
+ /** Progress ratio (0 → 1) */
16
+ progress: number;
17
+ }
18
+ /**
19
+ * Progressive render hook for time-sliced element rendering.
20
+ *
21
+ * @param elements - Full array of elements to render
22
+ * @param options - Configuration options
23
+ * @returns Progressive render state with growing visible element list
24
+ *
25
+ * Usage:
26
+ * ```tsx
27
+ * const { visibleElements, isLoading } = useProgressiveRender(staticElements, {
28
+ * batchSize: 500,
29
+ * threshold: 500,
30
+ * });
31
+ * // Use visibleElements for rendering instead of staticElements
32
+ * ```
33
+ */
34
+ export declare function useProgressiveRender(elements: CanvasElement[], options?: UseProgressiveRenderOptions): ProgressiveRenderState;
35
+ /**
36
+ * Schedule non-critical work during browser idle time.
37
+ * Falls back to setTimeout(100ms) if requestIdleCallback is unavailable.
38
+ *
39
+ * Use cases:
40
+ * - Pre-compute spatial data
41
+ * - Prefetch image thumbnails
42
+ * - Update minimap
43
+ * - Rebuild spatial indices
44
+ */
45
+ export declare function scheduleIdleWork(work: () => void, options?: {
46
+ timeout?: number;
47
+ }): void;
48
+ /**
49
+ * Yield to browser main thread (let browser handle input events).
50
+ * Used inside long-running loops to prevent frame drops.
51
+ */
52
+ export declare function yieldToMain(): Promise<void>;
@@ -0,0 +1,9 @@
1
+ import { CanvasElement, ViewportState } from '../types';
2
+ /**
3
+ * Hook returning only elements visible in the current viewport,
4
+ * using an R-tree spatial index for large canvases.
5
+ *
6
+ * Drop-in replacement for the original `useViewportCulling` hook —
7
+ * same input/output contract.
8
+ */
9
+ export declare function useSpatialIndex(elements: CanvasElement[], viewport: ViewportState, stageWidth: number, stageHeight: number, selectedIds: string[], padding?: number): CanvasElement[];
@@ -0,0 +1,13 @@
1
+ import { CanvasElement, ViewportState } from '../types';
2
+ /**
3
+ * Returns only the elements that are visible in the current viewport
4
+ * plus any selected elements (always visible for transformer).
5
+ *
6
+ * @param elements - all resolved elements
7
+ * @param viewport - current pan/zoom state
8
+ * @param stageWidth - Stage pixel width
9
+ * @param stageHeight - Stage pixel height
10
+ * @param selectedIds - currently selected element IDs
11
+ * @param padding - extra world-space padding (default 200)
12
+ */
13
+ export declare function useViewportCulling(elements: CanvasElement[], viewport: ViewportState, stageWidth: number, stageHeight: number, selectedIds: string[], padding?: number): CanvasElement[];
@@ -0,0 +1,4 @@
1
+ import { default as React } from 'react';
2
+ import { FlowCanvasProps, FlowCanvasRef } from './FlowCanvasProps';
3
+ declare const FlowCanvas: React.ForwardRefExoticComponent<FlowCanvasProps & React.RefAttributes<FlowCanvasRef>>;
4
+ export default FlowCanvas;
@@ -0,0 +1,154 @@
1
+ import { CanvasElement, ElementStyle, ToolType } from '../types';
2
+ import { ContextMenuItem } from '../components/ContextMenu/ContextMenu';
3
+ import { CollaborationConfig } from '../collaboration/types';
4
+ export type { ContextMenuItem };
5
+ /** Context passed to custom context menu renderers */
6
+ export interface ContextMenuContext {
7
+ /** IDs of currently selected elements */
8
+ selectedIds: string[];
9
+ /** All elements on the canvas */
10
+ elements: CanvasElement[];
11
+ /** Screen-space position of the right-click */
12
+ position: {
13
+ x: number;
14
+ y: number;
15
+ };
16
+ /** Close the context menu */
17
+ close: () => void;
18
+ }
19
+ export interface FlowCanvasProps {
20
+ /** Initial elements to render on the canvas */
21
+ initialElements?: CanvasElement[];
22
+ /** Controlled elements (makes the component controlled) */
23
+ elements?: CanvasElement[];
24
+ /** Callback when elements change */
25
+ onChange?: (elements: CanvasElement[]) => void;
26
+ /** Callback when selection changes */
27
+ onSelectionChange?: (selectedIds: string[]) => void;
28
+ /** Callback when an element is created */
29
+ onElementCreate?: (element: CanvasElement) => void;
30
+ /** Callback when an element is deleted */
31
+ onElementDelete?: (ids: string[]) => void;
32
+ /**
33
+ * Callback when an element is double-clicked.
34
+ * Return `true` to prevent the default behavior (create/edit bound text).
35
+ * Return `false` or `undefined` to let the default behavior proceed.
36
+ */
37
+ onElementDoubleClick?: (elementId: string, element: CanvasElement) => boolean | void;
38
+ /** Width of the canvas (default: 100% of container) */
39
+ width?: number | string;
40
+ /** Height of the canvas (default: 100% of container) */
41
+ height?: number | string;
42
+ /** Which tools to display in toolbar */
43
+ tools?: ToolType[];
44
+ /** Default drawing style */
45
+ defaultStyle?: Partial<ElementStyle>;
46
+ /** Show/hide the toolbar */
47
+ showToolbar?: boolean;
48
+ /** Show/hide the style panel */
49
+ showStylePanel?: boolean;
50
+ /** Show/hide the status bar */
51
+ showStatusBar?: boolean;
52
+ /** Show grid by default */
53
+ showGrid?: boolean;
54
+ /** Enable keyboard shortcuts */
55
+ enableShortcuts?: boolean;
56
+ /** Theme customization */
57
+ theme?: Partial<FlowCanvasTheme>;
58
+ /** Readonly mode — disable editing */
59
+ readOnly?: boolean;
60
+ /** Additional CSS class for the root container */
61
+ className?: string;
62
+ /**
63
+ * Additional context menu items to append after the built-in items.
64
+ * Can be static items or a function that receives selection context.
65
+ */
66
+ contextMenuItems?: ContextMenuItem[] | ((ctx: ContextMenuContext) => ContextMenuItem[]);
67
+ /**
68
+ * Completely replace the built-in context menu with a custom renderer.
69
+ * When provided, the built-in context menu is NOT shown.
70
+ * Return a React element to render as the context menu.
71
+ */
72
+ renderContextMenu?: (ctx: ContextMenuContext) => React.ReactNode;
73
+ /**
74
+ * Enable real-time CRDT collaboration.
75
+ * Pass a `CollaborationConfig` to connect, or `undefined`/`null` to disable.
76
+ */
77
+ collaboration?: CollaborationConfig | null;
78
+ }
79
+ export interface FlowCanvasTheme {
80
+ /** Canvas background color */
81
+ canvasBackground: string;
82
+ /** Grid line color */
83
+ gridColor: string;
84
+ /** Selection highlight color */
85
+ selectionColor: string;
86
+ /** Toolbar background */
87
+ toolbarBg: string;
88
+ /** Toolbar border color */
89
+ toolbarBorder: string;
90
+ /** Panel background */
91
+ panelBg: string;
92
+ /** Active tool highlight */
93
+ activeToolColor: string;
94
+ /** Text color */
95
+ textColor: string;
96
+ /** Muted text color */
97
+ mutedTextColor: string;
98
+ }
99
+ export declare const DEFAULT_THEME: FlowCanvasTheme;
100
+ export interface FlowCanvasRef {
101
+ /** Get current elements */
102
+ getElements: () => CanvasElement[];
103
+ /** Set elements programmatically */
104
+ setElements: (elements: CanvasElement[]) => void;
105
+ /** Add a single element */
106
+ addElement: (element: CanvasElement) => void;
107
+ /** Delete elements by ids */
108
+ deleteElements: (ids: string[]) => void;
109
+ /** Get selected element ids */
110
+ getSelectedIds: () => string[];
111
+ /** Select elements by ids */
112
+ setSelectedIds: (ids: string[]) => void;
113
+ /** Clear selection */
114
+ clearSelection: () => void;
115
+ /** Set active tool */
116
+ setActiveTool: (tool: ToolType) => void;
117
+ /** Get active tool */
118
+ getActiveTool: () => ToolType;
119
+ /** Undo last action */
120
+ undo: () => void;
121
+ /** Redo last undone action */
122
+ redo: () => void;
123
+ /** Zoom to a specific scale */
124
+ zoomTo: (scale: number) => void;
125
+ /** Reset zoom and position */
126
+ resetView: () => void;
127
+ /**
128
+ * Scroll and zoom the viewport to center a specific element.
129
+ * Optionally specify a zoom level (defaults to current zoom, clamped to at least 1).
130
+ */
131
+ scrollToElement: (id: string, options?: {
132
+ zoom?: number;
133
+ animate?: boolean;
134
+ }) => void;
135
+ /**
136
+ * Zoom the viewport to fit all elements (or a subset) within the visible area.
137
+ * Pass element IDs to fit only those elements; omit to fit all.
138
+ */
139
+ zoomToFit: (ids?: string[], options?: {
140
+ padding?: number;
141
+ maxZoom?: number;
142
+ animate?: boolean;
143
+ }) => void;
144
+ /** Export canvas as PNG data URL */
145
+ exportPNG: () => string | null;
146
+ /** Export elements as JSON string */
147
+ exportJSON: () => string;
148
+ /** Export elements as SVG string */
149
+ exportSVG: () => string;
150
+ /** Import elements from JSON string */
151
+ importJSON: (json: string) => void;
152
+ /** Get the Konva Stage instance */
153
+ getStage: () => unknown;
154
+ }
@@ -0,0 +1,57 @@
1
+ export { default as FlowCanvas } from './FlowCanvas';
2
+ export type { FlowCanvasProps, FlowCanvasRef, FlowCanvasTheme, ContextMenuItem, ContextMenuContext, } from './FlowCanvasProps';
3
+ export { DEFAULT_THEME } from './FlowCanvasProps';
4
+ export type { CanvasElement, RectangleElement, EllipseElement, DiamondElement, LineElement, ArrowElement, FreeDrawElement, TextElement, ImageElement, BaseElement, ElementStyle, ElementType, ToolType, Point, ViewportState, ConnectionAnchor, BoundElement, Binding, SnapTarget, Arrowhead, LineType, TextAlign, VerticalAlign, ImageScaleMode, ImageCrop, ElementMeta, CanvasOperation, } from '../types';
5
+ export { useCanvasStore } from '../store/useCanvasStore';
6
+ export { DEFAULT_STYLE, STROKE_COLORS, FILL_COLORS, STROKE_WIDTHS, TOOLS, ARROWHEAD_TYPES, LINE_TYPES, ROUGHNESS_CONFIGS } from '../constants';
7
+ export { generateId } from '../utils/id';
8
+ export { distance, normalizeRect, rotatePoint, isPointInRect, getDiamondPoints, getStrokeDash } from '../utils/geometry';
9
+ export { exportToDataURL, downloadPNG, exportToJSON, downloadJSON, exportToSVG, downloadSVG } from '../utils/export';
10
+ export { drawArrowhead, arrowheadSize, flatToPoints } from '../utils/arrowheads';
11
+ export { computeCurveControlPoint, quadBezierAt, quadBezierTangent, curveArrowPrev, CURVE_RATIO } from '../utils/curve';
12
+ export { computeElbowPoints, computeElbowRoute, simplifyElbowPath, clearElbowRouteCache, directionFromFixedPoint, directionFromPoints, directionFromShapeToPoint, directionFromEdgePoint, getElbowPreferredDirection, } from '../utils/elbow';
13
+ export type { Direction } from '../utils/elbow';
14
+ export { getConnectionPoints, getEdgePoint, getEdgePointFromFixedPoint, computeFixedPoint, getAnchorPosition, findNearestSnapTarget, isConnectable, recomputeBoundPoints, findConnectorsForElement, addBoundElement, removeBoundElement, syncBoundElements, } from '../utils/connection';
15
+ export { fileToDataURL, loadImage, computeImageElementDimensions, createImageElement, getImageFilesFromDataTransfer, extractImageDataFromClipboard, clipboardHasImage, resolveImageSource, openImageFilePicker, } from '../utils/image';
16
+ export { getVisibleBounds, getElementAABB, aabbOverlaps, cullToViewport, buildElementMap, cloneElementsForHistory, rafThrottle, batchElementUpdates, } from '../utils/performance';
17
+ export type { AABB } from '../utils/performance';
18
+ export { computeEfficientZoom, useEfficientZoom } from '../hooks/useEfficientZoom';
19
+ export { SpatialIndex, getSharedSpatialIndex, elementToSpatialItem } from '../utils/spatialIndex';
20
+ export type { SpatialItem } from '../utils/spatialIndex';
21
+ export { getElbowWorkerManager, disposeElbowWorkerManager } from '../utils/elbowWorkerManager';
22
+ export type { RouteParams } from '../utils/elbowWorkerManager';
23
+ export { zoomAtPoint, getNextZoomStep, getPrevZoomStep, getElementsBounds, computeZoomToFit, animateViewport, cancelViewportAnimation, ZOOM_STEPS, DEFAULT_ANIMATION_DURATION, } from '../utils/camera';
24
+ export type { ZoomAtPointOptions, ZoomToFitOptions } from '../utils/camera';
25
+ export { getToolHandler } from '../tools';
26
+ export type { ToolHandler, ToolContext } from '../tools';
27
+ export { SpatialSoA, getSharedSpatialSoA, ELEMENT_TYPE_MAP } from '../utils/spatialSoA';
28
+ export type { SpatialSoAData } from '../utils/spatialSoA';
29
+ export { useProgressiveRender, scheduleIdleWork, yieldToMain } from '../hooks/useProgressiveRender';
30
+ export type { UseProgressiveRenderOptions, ProgressiveRenderState } from '../hooks/useProgressiveRender';
31
+ export { getExportWorkerManager, disposeExportWorkerManager, ExportWorkerManager } from '../utils/exportWorkerManager';
32
+ export { generateKeyBetween, generateNKeysBetween, isValidFractionalIndex, compareFractionalKeys, } from '../utils/fractionalIndex';
33
+ export { OperationLog, opAdd, opDelete, opMove, opResize, opStyle, opRotate, opReorder, opUpdatePoints, opSetText, opBatch, applyOperation, detectOperations, } from '../utils/crdtPrep';
34
+ export type { OperationEntry } from '../utils/crdtPrep';
35
+ export type { CollaborationUser, AwarenessState, CollaborationConfig, ConnectionStatus, CollaborationEvent, } from '../collaboration/types';
36
+ export { createCollaborationProvider, destroyCollaborationProvider, getYDoc, getYProvider, getYElements, isCollaborationActive, onStatusChange, updateAwareness, getRemoteAwareness, } from '../collaboration/yjsProvider';
37
+ export { startSync, stopSync } from '../collaboration/syncBridge';
38
+ export { elementToYMap, yMapToElement, SYNC_FIELDS, STYLE_FIELDS } from '../collaboration/syncBridgeCodec';
39
+ export { CollaborationManager } from '../collaboration/CollaborationManager';
40
+ export { SyncWorkerAdapter } from '../collaboration/syncWorker';
41
+ export type { WorkerInMessage, WorkerOutMessage, SyncWorkerCallbacks } from '../collaboration/syncWorker';
42
+ export { useCollaboration } from '../collaboration/useCollaboration';
43
+ export type { UseCollaborationReturn } from '../collaboration/useCollaboration';
44
+ export { default as CursorOverlay } from '../collaboration/CursorOverlay';
45
+ export { TileCache, tileKey } from '../rendering/tileCache';
46
+ export type { TileCoord } from '../rendering/tileCache';
47
+ export { TileRenderer, TILE_SIZE, discreteZoom, worldTileSize, getVisibleTiles, tileBounds, getElementTiles, } from '../rendering/tileRenderer';
48
+ export type { TileDrawFn, TileRendererOptions } from '../rendering/tileRenderer';
49
+ export { useTileRenderer } from '../rendering/useTileRenderer';
50
+ export type { UseTileRendererOptions, UseTileRendererReturn } from '../rendering/useTileRenderer';
51
+ export { TextureAtlas } from '../webgl/textureAtlas';
52
+ export type { AtlasRegion, AtlasEntry, ElementRasterFn } from '../webgl/textureAtlas';
53
+ export { WebGLHybridRenderer } from '../webgl/WebGLHybridRenderer';
54
+ export type { WebGLHybridRendererOptions } from '../webgl/WebGLHybridRenderer';
55
+ export { buildViewMatrix } from '../webgl/glUtils';
56
+ export { useWebGLHybrid } from '../webgl/useWebGLHybrid';
57
+ export type { UseWebGLHybridOptions, UseWebGLHybridReturn } from '../webgl/useWebGLHybrid';
@@ -0,0 +1,127 @@
1
+ import { CanvasElement, ElementStyle, ElementType, ToolType, ViewportState, Point, LineType, Arrowhead } from '../types';
2
+ /** Single element diff: tracks what changed for one element */
3
+ interface ElementDiff {
4
+ type: 'add' | 'modify' | 'delete';
5
+ elementId: string;
6
+ /** Element state before the change (for modify/delete) */
7
+ before?: CanvasElement;
8
+ /** Element state after the change (for add/modify) */
9
+ after?: CanvasElement;
10
+ }
11
+ /**
12
+ * Diff-based history entry.
13
+ * Instead of storing full snapshots, we only store what changed.
14
+ * This drastically reduces memory usage for large canvases.
15
+ */
16
+ interface HistoryEntry {
17
+ diffs: ElementDiff[];
18
+ /** Optional named mark/checkpoint for grouping */
19
+ mark?: string;
20
+ /** Timestamp for squash heuristics */
21
+ timestamp: number;
22
+ }
23
+ interface CanvasState {
24
+ elements: CanvasElement[];
25
+ selectedIds: string[];
26
+ activeTool: ToolType;
27
+ currentStyle: ElementStyle;
28
+ currentLineType: LineType;
29
+ currentStartArrowhead: Arrowhead | null;
30
+ currentEndArrowhead: Arrowhead | null;
31
+ viewport: ViewportState;
32
+ isDrawing: boolean;
33
+ drawStart: Point | null;
34
+ history: HistoryEntry[];
35
+ historyIndex: number;
36
+ /** Baseline snapshot for computing diffs against current state */
37
+ _historyBaseline: Map<string, CanvasElement>;
38
+ /** Whether history recording is temporarily paused */
39
+ _historyPaused: boolean;
40
+ showGrid: boolean;
41
+ addElement: (element: CanvasElement) => void;
42
+ updateElement: (id: string, updates: Partial<CanvasElement>) => void;
43
+ /** Batch-update multiple elements in a single store write.
44
+ * Dramatically reduces re-renders when dragging N selected elements
45
+ * (1 array allocation instead of N per frame). */
46
+ batchUpdateElements: (updates: Array<{
47
+ id: string;
48
+ updates: Partial<CanvasElement>;
49
+ }>) => void;
50
+ deleteElements: (ids: string[]) => void;
51
+ setElements: (elements: CanvasElement[]) => void;
52
+ duplicateElements: (ids: string[]) => void;
53
+ convertElementType: (ids: string[], targetType: ElementType) => void;
54
+ bringToFront: (ids: string[]) => void;
55
+ sendToBack: (ids: string[]) => void;
56
+ bringForward: (ids: string[]) => void;
57
+ sendBackward: (ids: string[]) => void;
58
+ toggleLockElements: (ids: string[]) => void;
59
+ groupElements: (ids: string[]) => void;
60
+ ungroupElements: (ids: string[]) => void;
61
+ setSelectedIds: (ids: string[]) => void;
62
+ clearSelection: () => void;
63
+ setActiveTool: (tool: ToolType) => void;
64
+ setCurrentStyle: (style: Partial<ElementStyle>) => void;
65
+ setCurrentLineType: (lineType: LineType) => void;
66
+ setCurrentStartArrowhead: (arrowhead: Arrowhead | null) => void;
67
+ setCurrentEndArrowhead: (arrowhead: Arrowhead | null) => void;
68
+ setViewport: (viewport: Partial<ViewportState>) => void;
69
+ /**
70
+ * Zoom in one step. If `center` is provided (screen-space point),
71
+ * zoom toward that point; otherwise zoom toward viewport center.
72
+ */
73
+ zoomIn: (center?: {
74
+ x: number;
75
+ y: number;
76
+ }, options?: {
77
+ animate?: boolean;
78
+ }) => void;
79
+ /**
80
+ * Zoom out one step. If `center` is provided (screen-space point),
81
+ * zoom toward that point; otherwise zoom toward viewport center.
82
+ */
83
+ zoomOut: (center?: {
84
+ x: number;
85
+ y: number;
86
+ }, options?: {
87
+ animate?: boolean;
88
+ }) => void;
89
+ resetZoom: (options?: {
90
+ animate?: boolean;
91
+ }) => void;
92
+ /**
93
+ * Zoom the viewport to fit all elements (or specific IDs).
94
+ * Requires stageWidth/stageHeight to calculate proper fit.
95
+ */
96
+ zoomToFit: (stageWidth: number, stageHeight: number, ids?: string[], options?: {
97
+ padding?: number;
98
+ maxZoom?: number;
99
+ animate?: boolean;
100
+ }) => void;
101
+ /**
102
+ * Zoom the viewport to fit currently selected elements.
103
+ */
104
+ zoomToSelection: (stageWidth: number, stageHeight: number, options?: {
105
+ padding?: number;
106
+ maxZoom?: number;
107
+ animate?: boolean;
108
+ }) => void;
109
+ setIsDrawing: (isDrawing: boolean) => void;
110
+ setDrawStart: (point: Point | null) => void;
111
+ pushHistory: (mark?: string) => void;
112
+ undo: () => void;
113
+ redo: () => void;
114
+ /** Squash the last N history entries into one (for continuous ops like drag) */
115
+ squashHistory: (count?: number) => void;
116
+ /** Pause history recording (for batch operations) */
117
+ pauseHistory: () => void;
118
+ /** Resume history recording */
119
+ resumeHistory: () => void;
120
+ /** Check if history can undo */
121
+ canUndo: () => boolean;
122
+ /** Check if history can redo */
123
+ canRedo: () => boolean;
124
+ toggleGrid: () => void;
125
+ }
126
+ export declare const useCanvasStore: import('zustand').UseBoundStore<import('zustand').StoreApi<CanvasState>>;
127
+ export {};
@@ -0,0 +1,25 @@
1
+ interface LinearEditStoreState {
2
+ /** ID of the element being edited, or null */
3
+ elementId: string | null;
4
+ /** Full editing mode (point handles visible & interactive) */
5
+ isEditing: boolean;
6
+ /** Indices of selected point(s) */
7
+ selectedPointIndices: number[];
8
+ /** Hovered point index (-1 = none) */
9
+ hoveredPointIndex: number;
10
+ /** Hovered midpoint index (null = none) */
11
+ hoveredMidpointIndex: number | null;
12
+ /** Currently dragging a point */
13
+ isDraggingPoint: boolean;
14
+ enterEditMode: (elementId: string) => void;
15
+ exitEditMode: () => void;
16
+ setSelectedPoints: (indices: number[]) => void;
17
+ togglePointSelection: (index: number) => void;
18
+ setHoveredPoint: (index: number) => void;
19
+ setHoveredMidpoint: (index: number | null) => void;
20
+ setIsDraggingPoint: (dragging: boolean) => void;
21
+ /** Reset everything */
22
+ reset: () => void;
23
+ }
24
+ export declare const useLinearEditStore: import('zustand').UseBoundStore<import('zustand').StoreApi<LinearEditStoreState>>;
25
+ export {};