@jamesyong42/infinite-canvas 0.0.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 James Yong
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,308 @@
1
+ # Infinite Canvas
2
+
3
+ [![CI](https://github.com/jamesyong-42/infinite-canvas/actions/workflows/ci.yml/badge.svg)](https://github.com/jamesyong-42/infinite-canvas/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/@jamesyong42/infinite-canvas)](https://www.npmjs.com/package/@jamesyong42/infinite-canvas)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
6
+
7
+ **[Live Demo](https://jamesyong-42.github.io/infinite-canvas/)** | **[npm](https://www.npmjs.com/org/jamesyong42)**
8
+
9
+ A high-performance infinite canvas library for React, built on an Entity Component System (ECS) architecture with WebGL-accelerated rendering.
10
+
11
+ ## Features
12
+
13
+ - **ECS-powered** — Decoupled entity/component architecture with spatial indexing (RBush), change detection, and topologically-sorted system scheduling
14
+ - **Dual rendering surfaces** — DOM widgets and WebGL (R3F) widgets on the same canvas
15
+ - **GPU-rendered chrome** — Dot grid, selection outlines, resize handles, hover highlights, and snap guides all rendered via SDF shaders in a single draw call
16
+ - **Figma-style interactions** — Snap alignment (edge/center), equal spacing detection, multi-select group bounding box
17
+ - **Mobile-first gestures** — iOS Freeform-style touch: single-finger pan, pinch-to-zoom, tap-to-select, double-tap to enter containers
18
+ - **Responsive widgets** — Breakpoint system adapts widget rendering based on screen-space size (micro/compact/normal/expanded/detailed)
19
+ - **Undo/redo** — Command buffer with grouped operations (entire drag = one undo step)
20
+ - **Hierarchical navigation** — Enter/exit nested containers with camera state preservation
21
+ - **Performance profiling** — Built-in profiler with User Timing API integration, per-system timing, percentile stats
22
+ - **Dark mode** — Full dark mode support across canvas, widgets, and UI panels
23
+ - **Configurable** — Grid, selection, snap, zoom, and breakpoint parameters all exposed
24
+
25
+ ## Package
26
+
27
+ Everything ships in a single package: **`@jamesyong42/infinite-canvas`**. It exposes three entry points:
28
+
29
+ | Import | Purpose |
30
+ |--------|---------|
31
+ | `@jamesyong42/infinite-canvas` | Main API — `<InfiniteCanvas>`, `createLayoutEngine`, hooks, built-in components |
32
+ | `@jamesyong42/infinite-canvas/ecs` | ECS primitives for advanced users (`defineComponent`, `defineSystem`, `World`) |
33
+ | `@jamesyong42/infinite-canvas/advanced` | WebGL renderers, serialization, profiler, spatial index |
34
+
35
+ ## Quick Start
36
+
37
+ ```bash
38
+ npm install @jamesyong42/infinite-canvas react react-dom
39
+ # For WebGL widgets (optional):
40
+ npm install three @react-three/fiber
41
+ ```
42
+
43
+ ```tsx
44
+ import { createLayoutEngine, InfiniteCanvas, useWidgetData } from '@jamesyong42/infinite-canvas';
45
+
46
+ // 1. Define your widget component
47
+ function MyCard({ entityId }) {
48
+ const data = useWidgetData(entityId);
49
+ return <div className="p-4 bg-white rounded shadow">{data.title}</div>;
50
+ }
51
+
52
+ // 2. Create the layout engine
53
+ const engine = createLayoutEngine({ zoom: { min: 0.05, max: 8 } });
54
+
55
+ // 3. Add widgets
56
+ engine.addWidget({
57
+ type: 'card',
58
+ position: { x: 100, y: 100 },
59
+ size: { width: 250, height: 180 },
60
+ data: { title: 'Hello World' },
61
+ });
62
+
63
+ // 4. Render — widgets prop wires up the widget types
64
+ function App() {
65
+ return (
66
+ <InfiniteCanvas
67
+ engine={engine}
68
+ widgets={[
69
+ { type: 'card', component: MyCard, defaultSize: { width: 250, height: 180 } },
70
+ ]}
71
+ className="h-screen w-screen"
72
+ />
73
+ );
74
+ }
75
+ ```
76
+
77
+ ## WebGL Widgets (R3F)
78
+
79
+ Register widgets with `surface: 'webgl'` to render 3D content via React Three Fiber:
80
+
81
+ ```tsx
82
+ import { useFrame } from '@react-three/fiber';
83
+ import { useRef } from 'react';
84
+
85
+ function My3DWidget({ entityId, width, height }) {
86
+ const meshRef = useRef();
87
+ useFrame((_, delta) => { meshRef.current.rotation.y += delta; });
88
+ return (
89
+ <mesh ref={meshRef}>
90
+ <boxGeometry args={[width * 0.5, height * 0.5, 50]} />
91
+ <meshBasicMaterial color="hotpink" wireframe />
92
+ </mesh>
93
+ );
94
+ }
95
+
96
+ <InfiniteCanvas
97
+ engine={engine}
98
+ widgets={[
99
+ { type: 'my-3d', surface: 'webgl', component: My3DWidget, defaultSize: { width: 250, height: 250 } },
100
+ ]}
101
+ />
102
+ ```
103
+
104
+ WebGL widgets get a transparent R3F canvas layered between the grid and DOM layers. The R3F camera is synced with the engine camera every frame. Widget components receive `entityId`, `width`, and `height` props and work in centered local coordinates.
105
+
106
+ ## Widget Development
107
+
108
+ Widgets are React components that receive `entityId` and use hooks to read/write ECS data:
109
+
110
+ ```tsx
111
+ import {
112
+ Transform2D,
113
+ useBreakpoint,
114
+ useComponent,
115
+ useIsSelected,
116
+ useUpdateWidget,
117
+ useWidgetData,
118
+ } from '@jamesyong42/infinite-canvas';
119
+
120
+ function MyWidget({ entityId }) {
121
+ const data = useWidgetData(entityId); // custom widget data
122
+ const breakpoint = useBreakpoint(entityId); // 'micro' | 'compact' | 'normal' | 'expanded' | 'detailed'
123
+ const isSelected = useIsSelected(entityId); // selection state
124
+ const transform = useComponent(entityId, Transform2D); // position/size
125
+ const updateWidget = useUpdateWidget(entityId); // update widget data
126
+
127
+ if (breakpoint === 'micro') return <div>...</div>; // minimal view
128
+ if (breakpoint === 'compact') return <div>...</div>; // condensed view
129
+ return <div>...</div>; // full view
130
+ }
131
+ ```
132
+
133
+ ## Configuration
134
+
135
+ ### Grid
136
+
137
+ ```tsx
138
+ <InfiniteCanvas
139
+ engine={engine}
140
+ grid={{
141
+ spacings: [8, 64, 512], // world-px grid levels [fine, medium, coarse]
142
+ dotColor: [0, 0, 0], // RGB 0-1
143
+ dotAlpha: 0.18, // base opacity
144
+ fadeIn: [4, 12], // CSS-px fade in range
145
+ fadeOut: [250, 500], // CSS-px fade out range
146
+ dotRadius: [0.5, 1.4], // CSS-px dot size range
147
+ levelWeight: [1.0, 0.4], // opacity weight per grid level
148
+ }}
149
+ />
150
+
151
+ // Disable grid
152
+ <InfiniteCanvas engine={engine} grid={false} />
153
+ ```
154
+
155
+ ### Selection
156
+
157
+ ```tsx
158
+ <InfiniteCanvas
159
+ engine={engine}
160
+ selection={{
161
+ outlineColor: [0.051, 0.6, 1.0], // Figma blue
162
+ outlineWidth: 1.5,
163
+ handleSize: 8,
164
+ handleFill: [1, 1, 1],
165
+ handleBorder: [0.051, 0.6, 1.0],
166
+ handleBorderWidth: 1.5,
167
+ hoverColor: [0.051, 0.6, 1.0],
168
+ hoverWidth: 1.0,
169
+ groupDash: 4,
170
+ }}
171
+ />
172
+ ```
173
+
174
+ ### Engine
175
+
176
+ ```tsx
177
+ const engine = createLayoutEngine({
178
+ zoom: { min: 0.05, max: 8 },
179
+ breakpoints: { micro: 40, compact: 120, normal: 500, expanded: 1200 },
180
+ });
181
+
182
+ // Snap guides
183
+ engine.setSnapEnabled(true);
184
+ engine.setSnapThreshold(5); // world pixels
185
+ ```
186
+
187
+ ## Architecture
188
+
189
+ ```
190
+ @jamesyong42/infinite-canvas
191
+ ├── Main API (InfiniteCanvas, createLayoutEngine, hooks, components)
192
+ ├── /ecs (ECS primitives: defineComponent, defineSystem, World)
193
+ └── /advanced (WebGL renderers, serialization, profiler, spatial index)
194
+ ```
195
+
196
+ ### Rendering Stack
197
+
198
+ ```
199
+ z:0 WebGL canvas (Three.js)
200
+ ├── GridRenderer — multi-level dot grid (SDF shader)
201
+ └── SelectionRenderer — outlines, handles, hover, snap guides (SDF shader)
202
+
203
+ z:1 R3F canvas (React Three Fiber, lazy)
204
+ └── WebGL widgets — 3D content with synced orthographic camera
205
+
206
+ z:2 DOM layer
207
+ ├── WidgetSlots — DOM widget content + pointer events
208
+ └── SelectionOverlays — invisible pointer event layer for WebGL widgets
209
+
210
+ z:3 UI chrome
211
+ └── Panels, buttons, toggles
212
+ ```
213
+
214
+ ### ECS Components
215
+
216
+ | Component | Description |
217
+ |-----------|-------------|
218
+ | `Transform2D` | Position, size, rotation |
219
+ | `WorldBounds` | Computed world-space bounds (propagated from parent) |
220
+ | `Widget` | Surface (`'dom'`/`'webgl'`) and type identifier |
221
+ | `WidgetData` | Arbitrary widget-specific data |
222
+ | `WidgetBreakpoint` | Computed responsive breakpoint |
223
+ | `ZIndex` | Rendering order |
224
+ | `Parent` / `Children` | Hierarchy |
225
+ | `Container` | Marks entity as enterable |
226
+
227
+ ### ECS Tags
228
+
229
+ `Selectable` `Draggable` `Resizable` `Locked` `Selected` `Active` `Visible`
230
+
231
+ ### Systems (execution order)
232
+
233
+ 1. `transformPropagate` — Propagate transforms down hierarchy, compute WorldBounds
234
+ 2. `spatialIndex` — Update RBush spatial index
235
+ 3. `navigationFilter` — Filter entities to active navigation layer
236
+ 4. `cull` — Mark viewport-visible entities
237
+ 5. `breakpoint` — Compute responsive breakpoints
238
+ 6. `sort` — Z-index ordering
239
+
240
+ ## Keyboard Shortcuts (Playground)
241
+
242
+ | Shortcut | Action |
243
+ |----------|--------|
244
+ | `Cmd/Ctrl+Z` | Undo |
245
+ | `Cmd/Ctrl+Shift+Z` | Redo |
246
+ | `Escape` | Exit container |
247
+ | `Delete` / `Backspace` | Delete selected |
248
+ | Double-click | Enter container |
249
+
250
+ ## Performance Profiling
251
+
252
+ Enable the built-in profiler via the Inspector panel or programmatically:
253
+
254
+ ```tsx
255
+ engine.profiler.setEnabled(true);
256
+
257
+ // After some frames:
258
+ const stats = engine.profiler.getStats();
259
+ console.log(stats.fps); // frames per second
260
+ console.log(stats.frameTime.p95); // 95th percentile frame time (ms)
261
+ console.log(stats.systemAvg); // per-system average timing
262
+ console.log(stats.budgetUsed); // % of 16.67ms budget used
263
+ ```
264
+
265
+ All timing data integrates with Chrome DevTools Performance tab via the User Timing API (`performance.mark`/`performance.measure`).
266
+
267
+ ## Development
268
+
269
+ ```bash
270
+ # Clone and install
271
+ git clone https://github.com/jamesyong-42/infinite-canvas.git
272
+ cd infinite-canvas
273
+ pnpm install
274
+
275
+ # Build the library
276
+ pnpm build
277
+
278
+ # Run the playground demo
279
+ pnpm dev
280
+
281
+ # Run tests
282
+ pnpm test
283
+
284
+ # Typecheck
285
+ pnpm exec tsc --noEmit -p packages/infinite-canvas/tsconfig.json
286
+ ```
287
+
288
+ ## Tech Stack
289
+
290
+ - **TypeScript** — Strict mode, fully typed public API
291
+ - **React 18 / 19** — Compatible with both versions
292
+ - **Three.js** — WebGL rendering (grid, selection chrome)
293
+ - **React Three Fiber** — WebGL widget rendering (optional peer dependency)
294
+ - **RBush** — Spatial indexing for hit testing and viewport culling
295
+ - **tsup** — Library bundling (ESM + CJS + DTS)
296
+ - **Vite** — Playground bundling
297
+ - **Tailwind CSS v4** — Playground styling
298
+ - **Biome** — Linting and formatting
299
+ - **Vitest** — Testing
300
+ - **pnpm** — Workspace management
301
+
302
+ ## Contributing
303
+
304
+ Contributions are welcome! See the [live demo](https://jamesyong-42.github.io/infinite-canvas/) for an overview of the features, then check out the playground at `apps/playground/` to experiment with changes locally.
305
+
306
+ ## License
307
+
308
+ [MIT](./LICENSE)