gpucanvas 0.2.0 → 0.3.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/README.md +150 -10
- package/dist/{chunk-VOFDBN5J.js → chunk-RBXGH44N.js} +1500 -91
- package/dist/chunk-RBXGH44N.js.map +1 -0
- package/dist/editor-BSRqjhN8.d.ts +267 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +11 -1
- package/dist/react.d.ts +37 -3
- package/dist/react.js +92 -2
- package/dist/react.js.map +1 -1
- package/package.json +6 -3
- package/dist/chunk-VOFDBN5J.js.map +0 -1
- package/dist/types-D8v10v6m.d.ts +0 -101
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
interface Camera {
|
|
2
|
+
/** World coordinate shown at the viewport's top-left corner. */
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
zoom: number;
|
|
6
|
+
}
|
|
7
|
+
interface Rect {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}
|
|
13
|
+
interface Point {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
}
|
|
17
|
+
/** A point in surface pixels (from the surface's top left) as world units. */
|
|
18
|
+
declare const screenToWorld: (camera: Camera, point: Point) => Point;
|
|
19
|
+
/** A world point as surface pixels. */
|
|
20
|
+
declare const worldToScreen: (camera: Camera, point: Point) => Point;
|
|
21
|
+
/** Zooms by `factor` while keeping the world point under (screenX, screenY) fixed. */
|
|
22
|
+
declare function zoomAt(camera: Camera, screenX: number, screenY: number, factor: number): Camera;
|
|
23
|
+
/** Fits `bounds` into the viewport with `padding` CSS pixels on each side. */
|
|
24
|
+
declare function fitCamera(bounds: Rect, viewportWidth: number, viewportHeight: number, padding?: number): Camera;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Shader effects on live nodes. An effect is a WGSL function the GPU runs for every pixel of a
|
|
28
|
+
* node's snapshot, so the component stays live DOM while it ripples, glows or dissolves.
|
|
29
|
+
*
|
|
30
|
+
* The function is `fn effect(input: EffectInput) -> vec4f`, returning a premultiplied color:
|
|
31
|
+
*
|
|
32
|
+
* ```wgsl
|
|
33
|
+
* struct EffectInput {
|
|
34
|
+
* uv: vec2f, // 0 to 1 across the node; outside that range in its padding
|
|
35
|
+
* size: vec2f, // the node's size in CSS pixels
|
|
36
|
+
* time: f32, // seconds since the canvas started
|
|
37
|
+
* zoom: f32, // the camera's zoom
|
|
38
|
+
* params: vec4f, // the node's parameters, from setEffect
|
|
39
|
+
* pointer: vec2f, // the pointer, in the node's uv; far away when it is off the canvas
|
|
40
|
+
* press: vec2f, // where the last press landed, in the node's uv
|
|
41
|
+
* sincePress: f32, // seconds since that press
|
|
42
|
+
* }
|
|
43
|
+
* fn source(uv: vec2f) -> vec4f // the node's own pixels, transparent outside 0 to 1
|
|
44
|
+
* fn hash(p: vec2f) -> f32 // a pseudo-random value from 0 to 1
|
|
45
|
+
* fn noise(p: vec2f) -> f32 // smooth value noise from 0 to 1
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
interface Effect {
|
|
49
|
+
readonly name: string;
|
|
50
|
+
readonly wgsl: string;
|
|
51
|
+
/** Runs every frame while a node wearing it is in view, for effects that use `time`. */
|
|
52
|
+
readonly animated: boolean;
|
|
53
|
+
/** Parameters a node gets when `setEffect` leaves them out, up to four. */
|
|
54
|
+
readonly params: readonly number[];
|
|
55
|
+
/** How far past its node the effect draws, in CSS pixels: a glow's reach, a blur's spread. */
|
|
56
|
+
readonly pad: number | ((params: readonly number[]) => number);
|
|
57
|
+
/**
|
|
58
|
+
* The same look as a CSS `filter`, for the DOM backend. Effects without one show their node
|
|
59
|
+
* unchanged there.
|
|
60
|
+
*/
|
|
61
|
+
readonly css?: (params: readonly number[], time: number) => string;
|
|
62
|
+
/** Seconds an animated CSS look takes to repeat. Default 4. */
|
|
63
|
+
readonly period?: number;
|
|
64
|
+
}
|
|
65
|
+
interface EffectDefinition {
|
|
66
|
+
name: string;
|
|
67
|
+
wgsl: string;
|
|
68
|
+
animated?: boolean;
|
|
69
|
+
params?: readonly number[];
|
|
70
|
+
pad?: Effect['pad'];
|
|
71
|
+
css?: Effect['css'];
|
|
72
|
+
period?: number;
|
|
73
|
+
}
|
|
74
|
+
declare const defineEffect: (definition: EffectDefinition) => Effect;
|
|
75
|
+
declare const EFFECTS: {
|
|
76
|
+
readonly glass: Effect;
|
|
77
|
+
readonly shockwave: Effect;
|
|
78
|
+
readonly glitch: Effect;
|
|
79
|
+
readonly vhs: Effect;
|
|
80
|
+
readonly frost: Effect;
|
|
81
|
+
readonly dither: Effect;
|
|
82
|
+
readonly glow: Effect;
|
|
83
|
+
readonly hue: Effect;
|
|
84
|
+
readonly ripple: Effect;
|
|
85
|
+
readonly blur: Effect;
|
|
86
|
+
readonly pixelate: Effect;
|
|
87
|
+
readonly dissolve: Effect;
|
|
88
|
+
readonly grayscale: Effect;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
type GpuCanvasMode = 'gpu' | 'dom';
|
|
92
|
+
interface GpuCanvasOptions {
|
|
93
|
+
/** `auto` uses the GPU path when WebGPU and HTML-in-Canvas are available, otherwise DOM. */
|
|
94
|
+
mode?: 'auto' | GpuCanvasMode;
|
|
95
|
+
/** Canvas background, any CSS color. */
|
|
96
|
+
background?: string;
|
|
97
|
+
/** Outline of selected elements, any CSS color. Default `#4f8cff`. */
|
|
98
|
+
selectionColor?: string;
|
|
99
|
+
/** Outline of the hovered element, any CSS color. Default: the selection color. */
|
|
100
|
+
hoverColor?: string;
|
|
101
|
+
camera?: Camera;
|
|
102
|
+
/** Side of each atlas layer in texels. Default 4096, capped by the device. */
|
|
103
|
+
atlasSize?: number;
|
|
104
|
+
/** Atlas layers; each 4096 layer costs 64 MB of GPU memory. One holds thumbnails. Default 5. */
|
|
105
|
+
atlasLayers?: number;
|
|
106
|
+
/** Most snapshot pixels rasterized per frame; the rest wait for later frames. Default 24M. */
|
|
107
|
+
rasterPixelBudget?: number;
|
|
108
|
+
/** Most raster calls per frame; one call covers a whole page or one node. Default 12. */
|
|
109
|
+
maxRastersPerFrame?: number;
|
|
110
|
+
/** Main-thread milliseconds a frame may spend issuing rasters. Default 6. */
|
|
111
|
+
rasterTimeBudgetMs?: number;
|
|
112
|
+
/** Side of a page, the unit nodes are batched into for rasterizing, in CSS pixels. Default 2048. */
|
|
113
|
+
pageSize?: number;
|
|
114
|
+
onCameraChange?: (camera: Camera) => void;
|
|
115
|
+
}
|
|
116
|
+
interface GpuCanvasStats {
|
|
117
|
+
mode: GpuCanvasMode;
|
|
118
|
+
nodes: number;
|
|
119
|
+
visible: number;
|
|
120
|
+
/** Visible nodes still waiting for a snapshot at the current zoom. */
|
|
121
|
+
pending: number;
|
|
122
|
+
/** Nodes that got fresh pixels in the last frame. */
|
|
123
|
+
rasterizedLastFrame: number;
|
|
124
|
+
/** Raster calls in the last frame (gpu only). */
|
|
125
|
+
rasterCallsLastFrame?: number;
|
|
126
|
+
/** Main-thread milliseconds the last frame spent issuing rasters. */
|
|
127
|
+
rasterMs: number;
|
|
128
|
+
/** Pages nodes are batched into, and how many are drawable right now (gpu only). */
|
|
129
|
+
pages?: number;
|
|
130
|
+
drawablePages?: number;
|
|
131
|
+
atlasLayersUsed: number;
|
|
132
|
+
/** Main-thread milliseconds the last composite took. */
|
|
133
|
+
frameMs: number;
|
|
134
|
+
/** Frames composited since creation (gpu only). An idle canvas draws none. */
|
|
135
|
+
frames?: number;
|
|
136
|
+
/** Times a visible node that had pixels showed a placeholder instead: visible flicker. */
|
|
137
|
+
lostPixels?: number;
|
|
138
|
+
/** Times a sharp zoomed-in node fell back to its blurrier whole snapshot. */
|
|
139
|
+
lostDetail?: number;
|
|
140
|
+
/** Share of the thumbnail layer in use, 0 to 1 (gpu only). */
|
|
141
|
+
thumbnailFill?: number;
|
|
142
|
+
/** Visible nodes showing only their thumbnail while a sharper snapshot is drawn (gpu only). */
|
|
143
|
+
onThumbnail?: number;
|
|
144
|
+
/** Visible nodes the last frame planned to rasterize: missing, dirty, blurry, oversampled. */
|
|
145
|
+
needs?: number[];
|
|
146
|
+
}
|
|
147
|
+
interface GpuCanvas {
|
|
148
|
+
readonly mode: GpuCanvasMode;
|
|
149
|
+
/** The element that receives camera input; the host's only child. */
|
|
150
|
+
readonly surface: HTMLElement;
|
|
151
|
+
/** Places `element` on the canvas at `rect` (world units). The element keeps its own DOM and events. */
|
|
152
|
+
addNode(element: HTMLElement, rect: Rect): void;
|
|
153
|
+
updateNode(element: HTMLElement, rect: Rect): void;
|
|
154
|
+
removeNode(element: HTMLElement): void;
|
|
155
|
+
/** Where `element` sits on the canvas, in world units, or null when it is not a node. */
|
|
156
|
+
getNodeRect(element: HTMLElement): Rect | null;
|
|
157
|
+
/** Nodes that overlap `rect` (world units), such as the ones under a selection box. */
|
|
158
|
+
getNodesIn(rect: Rect): HTMLElement[];
|
|
159
|
+
/** The node that holds `element`: the element itself when it is a node, or null. */
|
|
160
|
+
getNodeOf(element: Element): HTMLElement | null;
|
|
161
|
+
/** Where any element on the canvas sits, a node or anything inside one, in world units. */
|
|
162
|
+
getElementRect(element: Element): Rect | null;
|
|
163
|
+
/** The deepest element under the world point `point`, in the topmost node there. */
|
|
164
|
+
getElementAt(point: Point): Element | null;
|
|
165
|
+
/**
|
|
166
|
+
* Outlines these elements as selected, replacing the previous selection. They can be nodes or
|
|
167
|
+
* anything inside them, and the outlines follow their layout. Pass `[]` to clear.
|
|
168
|
+
*/
|
|
169
|
+
setSelection(elements: Iterable<Element>): void;
|
|
170
|
+
/** Outlines one element more lightly, such as the one under the pointer. Pass null to clear. */
|
|
171
|
+
setHover(element: Element | null): void;
|
|
172
|
+
/**
|
|
173
|
+
* Draws a node through a shader effect, such as \`ripple\` or one from \`defineEffect\`, with up to
|
|
174
|
+
* four parameters (the effect's defaults otherwise). Pass null to remove it. The DOM backend
|
|
175
|
+
* shows effects that have a CSS look as a \`filter\` and ignores the rest.
|
|
176
|
+
*/
|
|
177
|
+
setEffect(node: HTMLElement, effect: Effect | null, params?: ArrayLike<number>): void;
|
|
178
|
+
getCamera(): Camera;
|
|
179
|
+
setCamera(camera: Camera): void;
|
|
180
|
+
/** A point in surface pixels as world units, under the current camera. */
|
|
181
|
+
screenToWorld(point: Point): Point;
|
|
182
|
+
/** A world point as surface pixels, under the current camera. */
|
|
183
|
+
worldToScreen(point: Point): Point;
|
|
184
|
+
/**
|
|
185
|
+
* Redraws every node. Call it after a change the nodes' own DOM does not show, such as a theme
|
|
186
|
+
* class on `<html>` or a web font finishing loading.
|
|
187
|
+
*/
|
|
188
|
+
invalidate(): void;
|
|
189
|
+
/** Changes the canvas background, any CSS color. */
|
|
190
|
+
setBackground(color: string): void;
|
|
191
|
+
/** Fits all nodes in view. */
|
|
192
|
+
fit(padding?: number): void;
|
|
193
|
+
getStats(): GpuCanvasStats;
|
|
194
|
+
destroy(): void;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* A layer is what one click selects: one element, or several that act as one, such as a React
|
|
199
|
+
* component that renders a fragment. Its first element is its identity.
|
|
200
|
+
*/
|
|
201
|
+
type Layer = Element[];
|
|
202
|
+
/**
|
|
203
|
+
* What a click can select, from the element under the pointer. `'node'`: whole nodes only.
|
|
204
|
+
* `'element'`: every element down to the one under the pointer. A function returns the layers
|
|
205
|
+
* inside the node that hold `element`, outermost first; the node itself always comes before them.
|
|
206
|
+
*/
|
|
207
|
+
type EditorPick = 'node' | 'element' | ((element: Element, canvas: GpuCanvas) => Array<Element | Element[]>);
|
|
208
|
+
|
|
209
|
+
interface TransformEvent {
|
|
210
|
+
phase: 'start' | 'move' | 'end' | 'cancel';
|
|
211
|
+
kind: 'move' | 'resize';
|
|
212
|
+
/** What is being moved or resized. */
|
|
213
|
+
layers: Layer[];
|
|
214
|
+
/** Each layer's rect when the gesture started, world units. */
|
|
215
|
+
from: Rect[];
|
|
216
|
+
/** Where each layer should be now. On `cancel` it equals `from`. */
|
|
217
|
+
to: Rect[];
|
|
218
|
+
}
|
|
219
|
+
interface EditorOptions {
|
|
220
|
+
/** What a click can select, and how deep a double-click goes. Default `'node'`. */
|
|
221
|
+
pick?: EditorPick;
|
|
222
|
+
/**
|
|
223
|
+
* `'shift-click'` (default): clicks still reach the components. Shift-click selects,
|
|
224
|
+
* shift-cmd-click (shift-ctrl-click) adds or removes, shift-drag draws a selection box.
|
|
225
|
+
* `'click'`: an edit mode. Every click selects, shift-click adds or removes, drags move, and
|
|
226
|
+
* shift-drag on the background draws a selection box.
|
|
227
|
+
* Either way, a double-click goes one layer deeper, and a drag on the open area of the layer
|
|
228
|
+
* you are inside draws a box over its children.
|
|
229
|
+
*/
|
|
230
|
+
trigger?: 'shift-click' | 'click';
|
|
231
|
+
/** Outlines what a click would select. With `'shift-click'`, only while shift is held. Default true. */
|
|
232
|
+
hover?: boolean;
|
|
233
|
+
/** Shift-drag draws a box that selects the layers it touches. Default true. */
|
|
234
|
+
marquee?: boolean;
|
|
235
|
+
/** Names and sizes next to the selection and the hovered layer. Default true. */
|
|
236
|
+
labels?: boolean;
|
|
237
|
+
/** A layer's name for its label. Default: the element's tag inside nodes, nothing for nodes. */
|
|
238
|
+
label?: (layer: Layer) => string | null;
|
|
239
|
+
/** Moves and resizes snap to the edges and centers of their neighbors. Default true. */
|
|
240
|
+
snap?: boolean;
|
|
241
|
+
/** Called with the new selection after every change. */
|
|
242
|
+
onChange?: (selected: Layer[]) => void;
|
|
243
|
+
/**
|
|
244
|
+
* Called through every move and resize. The editor never moves anything itself: apply `to`
|
|
245
|
+
* (`updateNode`, state, styles) to make it real. Handles and drag-to-move need this and the
|
|
246
|
+
* `'click'` trigger.
|
|
247
|
+
*/
|
|
248
|
+
onTransform?: (event: TransformEvent) => void;
|
|
249
|
+
/** Labels, frame and handles. Default `#4f8cff`. */
|
|
250
|
+
color?: string;
|
|
251
|
+
}
|
|
252
|
+
interface Editor {
|
|
253
|
+
get(): Layer[];
|
|
254
|
+
/** Selects these layers at the top level. */
|
|
255
|
+
set(layers: Iterable<Element | Element[]>): void;
|
|
256
|
+
clear(): void;
|
|
257
|
+
/** Removes the listeners, the chrome and the outlines. */
|
|
258
|
+
destroy(): void;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* A canvas editor's selection: click, double-click to go deeper, hover, box selection,
|
|
262
|
+
* keyboard navigation, labels, and moving and resizing with snapping. The canvas draws the
|
|
263
|
+
* outlines; the editor draws the rest above it.
|
|
264
|
+
*/
|
|
265
|
+
declare function bindEditor(canvas: GpuCanvas, options?: EditorOptions): Editor;
|
|
266
|
+
|
|
267
|
+
export { type Camera as C, type Editor as E, type GpuCanvasOptions as G, type Layer as L, type Point as P, type Rect as R, type TransformEvent as T, type GpuCanvas as a, type EditorOptions as b, type EditorPick as c, type Effect as d, type EffectDefinition as e, type GpuCanvasMode as f, type GpuCanvasStats as g, bindEditor as h, defineEffect as i, EFFECTS as j, fitCamera as k, screenToWorld as s, worldToScreen as w, zoomAt as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { G as GpuCanvasOptions, a as GpuCanvas } from './
|
|
2
|
-
export { C as Camera, b as
|
|
1
|
+
import { G as GpuCanvasOptions, a as GpuCanvas } from './editor-BSRqjhN8.js';
|
|
2
|
+
export { C as Camera, E as Editor, b as EditorOptions, c as EditorPick, d as Effect, e as EffectDefinition, f as GpuCanvasMode, g as GpuCanvasStats, L as Layer, P as Point, R as Rect, T as TransformEvent, h as bindEditor, i as defineEffect, j as effects, k as fitCamera, s as screenToWorld, w as worldToScreen, z as zoomAt } from './editor-BSRqjhN8.js';
|
|
3
3
|
|
|
4
4
|
declare function supportsGpuCanvas(): boolean;
|
|
5
5
|
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
+
EFFECTS,
|
|
3
|
+
bindEditor,
|
|
2
4
|
createGpuCanvas,
|
|
5
|
+
defineEffect,
|
|
3
6
|
fitCamera,
|
|
7
|
+
screenToWorld,
|
|
4
8
|
supportsGpuCanvas,
|
|
9
|
+
worldToScreen,
|
|
5
10
|
zoomAt
|
|
6
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-RBXGH44N.js";
|
|
7
12
|
export {
|
|
13
|
+
bindEditor,
|
|
8
14
|
createGpuCanvas,
|
|
15
|
+
defineEffect,
|
|
16
|
+
EFFECTS as effects,
|
|
9
17
|
fitCamera,
|
|
18
|
+
screenToWorld,
|
|
10
19
|
supportsGpuCanvas,
|
|
20
|
+
worldToScreen,
|
|
11
21
|
zoomAt
|
|
12
22
|
};
|
|
13
23
|
//# sourceMappingURL=index.js.map
|
package/dist/react.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, CSSProperties } from 'react';
|
|
3
|
-
import { G as GpuCanvasOptions, a as GpuCanvas$1 } from './
|
|
3
|
+
import { d as Effect, G as GpuCanvasOptions, a as GpuCanvas$1, L as Layer, b as EditorOptions } from './editor-BSRqjhN8.js';
|
|
4
4
|
|
|
5
5
|
interface GpuCanvasProps extends GpuCanvasOptions {
|
|
6
6
|
children?: ReactNode;
|
|
@@ -18,9 +18,43 @@ interface CanvasNodeProps {
|
|
|
18
18
|
children?: ReactNode;
|
|
19
19
|
className?: string;
|
|
20
20
|
style?: CSSProperties;
|
|
21
|
+
/** A shader the node is drawn through, such as `effects.glow`. */
|
|
22
|
+
effect?: Effect | null;
|
|
23
|
+
/** Up to four parameters for `effect`; its defaults otherwise. */
|
|
24
|
+
effectParams?: readonly number[];
|
|
21
25
|
}
|
|
22
26
|
/** A live DOM node placed on the canvas in world units. Events, focus and state work as usual. */
|
|
23
|
-
declare function CanvasNode({ x, y, width, height, children, className, style }: CanvasNodeProps): react.ReactPortal;
|
|
27
|
+
declare function CanvasNode({ x, y, width, height, children, className, style, effect, effectParams, }: CanvasNodeProps): react.ReactPortal;
|
|
24
28
|
declare const useGpuCanvas: () => GpuCanvas$1 | null;
|
|
29
|
+
/**
|
|
30
|
+
* The editor (`bindEditor`) on the canvas around it, with its selection as React state. Use it
|
|
31
|
+
* inside `<GpuCanvas>`; `onChange` lifts the selection out.
|
|
32
|
+
*/
|
|
33
|
+
declare function useEditor(options?: EditorOptions): {
|
|
34
|
+
selected: Layer[];
|
|
35
|
+
set: (layers: Iterable<Element | Element[]>) => void | undefined;
|
|
36
|
+
clear: () => void | undefined;
|
|
37
|
+
};
|
|
38
|
+
interface ComponentInfo {
|
|
39
|
+
/** The component's display name. Production builds usually minify it. */
|
|
40
|
+
name: string;
|
|
41
|
+
/** The DOM elements it renders at its top level: one, or several for a fragment. */
|
|
42
|
+
elements: Element[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The React components that rendered `element`, nearest first, up to the portal into its node
|
|
46
|
+
* (`<CanvasNode>` renders one). It reads React's internal fibers, as click-to-component tools do,
|
|
47
|
+
* so it can break with a React release.
|
|
48
|
+
*/
|
|
49
|
+
declare function componentsOf(element: Element): ComponentInfo[];
|
|
50
|
+
/** The nearest React component that rendered `element`, or null. */
|
|
51
|
+
declare const componentOf: (element: Element) => ComponentInfo | null;
|
|
52
|
+
/**
|
|
53
|
+
* A pick for `bindEditor` and `useEditor` that selects React components: a click lands on the
|
|
54
|
+
* outermost component in the node, and each double-click goes one component deeper.
|
|
55
|
+
*/
|
|
56
|
+
declare const pickComponents: (element: Element) => Element[][];
|
|
57
|
+
/** A label for components: the name of the outermost component a layer is the top of. */
|
|
58
|
+
declare const labelComponent: (layer: Layer) => string | null;
|
|
25
59
|
|
|
26
|
-
export { CanvasNode, type CanvasNodeProps, GpuCanvas, type GpuCanvasProps, useGpuCanvas };
|
|
60
|
+
export { CanvasNode, type CanvasNodeProps, type ComponentInfo, GpuCanvas, type GpuCanvasProps, componentOf, componentsOf, labelComponent, pickComponents, useEditor, useGpuCanvas };
|
package/dist/react.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import {
|
|
3
|
+
bindEditor,
|
|
3
4
|
createGpuCanvas
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-RBXGH44N.js";
|
|
5
6
|
|
|
6
7
|
// src/react.tsx
|
|
7
8
|
import {
|
|
@@ -37,7 +38,17 @@ function GpuCanvas({ children, className, style, onReady, ...options }) {
|
|
|
37
38
|
}, []);
|
|
38
39
|
return /* @__PURE__ */ jsx("div", { ref: hostRef, className, style: { position: "relative", ...style }, children: /* @__PURE__ */ jsx(CanvasContext.Provider, { value: canvas, children: canvas && children }) });
|
|
39
40
|
}
|
|
40
|
-
function CanvasNode({
|
|
41
|
+
function CanvasNode({
|
|
42
|
+
x,
|
|
43
|
+
y,
|
|
44
|
+
width,
|
|
45
|
+
height,
|
|
46
|
+
children,
|
|
47
|
+
className,
|
|
48
|
+
style,
|
|
49
|
+
effect = null,
|
|
50
|
+
effectParams
|
|
51
|
+
}) {
|
|
41
52
|
const canvas = useContext(CanvasContext);
|
|
42
53
|
if (!canvas) throw new Error("<CanvasNode> must be inside <GpuCanvas>");
|
|
43
54
|
const [element] = useState(() => document.createElement("div"));
|
|
@@ -53,15 +64,94 @@ function CanvasNode({ x, y, width, height, children, className, style }) {
|
|
|
53
64
|
useLayoutEffect(() => {
|
|
54
65
|
element.className = className ?? "";
|
|
55
66
|
}, [element, className]);
|
|
67
|
+
const paramsKey = effectParams?.join(",");
|
|
68
|
+
useLayoutEffect(() => {
|
|
69
|
+
canvas.setEffect(element, effect, effectParams);
|
|
70
|
+
}, [canvas, element, effect, paramsKey]);
|
|
56
71
|
return createPortal(
|
|
57
72
|
/* @__PURE__ */ jsx("div", { style: { width: "100%", height: "100%", ...style }, children }),
|
|
58
73
|
element
|
|
59
74
|
);
|
|
60
75
|
}
|
|
61
76
|
var useGpuCanvas = () => useContext(CanvasContext);
|
|
77
|
+
function useEditor(options = {}) {
|
|
78
|
+
const canvas = useGpuCanvas();
|
|
79
|
+
const [selected, setSelected] = useState([]);
|
|
80
|
+
const editor = useRef(null);
|
|
81
|
+
const latest = useRef(options);
|
|
82
|
+
latest.current = options;
|
|
83
|
+
const pick = typeof options.pick === "function" ? "function" : options.pick ?? "node";
|
|
84
|
+
const transforms = !!options.onTransform;
|
|
85
|
+
const { trigger, hover, marquee, labels, snap, color } = options;
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!canvas) return;
|
|
88
|
+
const bound = bindEditor(canvas, {
|
|
89
|
+
pick: pick === "function" ? (element, at) => latest.current.pick(element, at) : pick,
|
|
90
|
+
trigger,
|
|
91
|
+
hover,
|
|
92
|
+
marquee,
|
|
93
|
+
labels,
|
|
94
|
+
snap,
|
|
95
|
+
color,
|
|
96
|
+
label: (layer) => latest.current.label ? latest.current.label(layer) : defaultLabel(canvas, layer),
|
|
97
|
+
onChange(next) {
|
|
98
|
+
setSelected(next);
|
|
99
|
+
latest.current.onChange?.(next);
|
|
100
|
+
},
|
|
101
|
+
onTransform: transforms ? (event) => latest.current.onTransform?.(event) : void 0
|
|
102
|
+
});
|
|
103
|
+
editor.current = bound;
|
|
104
|
+
return () => {
|
|
105
|
+
bound.destroy();
|
|
106
|
+
editor.current = null;
|
|
107
|
+
setSelected([]);
|
|
108
|
+
};
|
|
109
|
+
}, [canvas, pick, trigger, hover, marquee, labels, snap, color, transforms]);
|
|
110
|
+
return {
|
|
111
|
+
selected,
|
|
112
|
+
set: (layers) => editor.current?.set(layers),
|
|
113
|
+
clear: () => editor.current?.clear()
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
var defaultLabel = (canvas, layer) => canvas.getNodeOf(layer[0]) === layer[0] ? null : layer[0].localName;
|
|
117
|
+
var COMPONENT_TAGS = /* @__PURE__ */ new Set([0, 1, 11, 15]);
|
|
118
|
+
var PORTAL_TAG = 4;
|
|
119
|
+
var fiberOf = (element) => {
|
|
120
|
+
const key = Object.keys(element).find((name) => name.startsWith("__reactFiber$"));
|
|
121
|
+
return key ? element[key] : null;
|
|
122
|
+
};
|
|
123
|
+
var nameOf = (type) => {
|
|
124
|
+
const t = type;
|
|
125
|
+
return t.displayName || t.name || t.render?.displayName || t.render?.name || "Anonymous";
|
|
126
|
+
};
|
|
127
|
+
var topElements = (fiber, out = []) => {
|
|
128
|
+
for (let child = fiber.child; child; child = child.sibling) {
|
|
129
|
+
if (child.stateNode instanceof Element) out.push(child.stateNode);
|
|
130
|
+
else if (child.tag !== PORTAL_TAG) topElements(child, out);
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
};
|
|
134
|
+
function componentsOf(element) {
|
|
135
|
+
const found = [];
|
|
136
|
+
for (let fiber = fiberOf(element); fiber; fiber = fiber.return) {
|
|
137
|
+
if (fiber.tag === PORTAL_TAG) break;
|
|
138
|
+
if (!COMPONENT_TAGS.has(fiber.tag)) continue;
|
|
139
|
+
const elements = topElements(fiber);
|
|
140
|
+
if (elements.length) found.push({ name: nameOf(fiber.type), elements });
|
|
141
|
+
}
|
|
142
|
+
return found;
|
|
143
|
+
}
|
|
144
|
+
var componentOf = (element) => componentsOf(element)[0] ?? null;
|
|
145
|
+
var pickComponents = (element) => componentsOf(element).reverse().map((component) => component.elements);
|
|
146
|
+
var labelComponent = (layer) => componentsOf(layer[0]).filter((component) => component.elements[0] === layer[0]).at(-1)?.name ?? null;
|
|
62
147
|
export {
|
|
63
148
|
CanvasNode,
|
|
64
149
|
GpuCanvas,
|
|
150
|
+
componentOf,
|
|
151
|
+
componentsOf,
|
|
152
|
+
labelComponent,
|
|
153
|
+
pickComponents,
|
|
154
|
+
useEditor,
|
|
65
155
|
useGpuCanvas
|
|
66
156
|
};
|
|
67
157
|
//# sourceMappingURL=react.js.map
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.tsx"],"sourcesContent":["import {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n createGpuCanvas,\n type GpuCanvas as GpuCanvasInstance,\n type GpuCanvasOptions,\n} from './index';\n\nconst CanvasContext = createContext<GpuCanvasInstance | null>(null);\n\nexport interface GpuCanvasProps extends GpuCanvasOptions {\n children?: ReactNode;\n className?: string;\n style?: CSSProperties;\n onReady?: (canvas: GpuCanvasInstance) => void;\n}\n\n/** An infinite canvas. Children are `<CanvasNode>`s; they mount once the canvas is ready. */\nexport function GpuCanvas({ children, className, style, onReady, ...options }: GpuCanvasProps) {\n const hostRef = useRef<HTMLDivElement>(null);\n const [canvas, setCanvas] = useState<GpuCanvasInstance | null>(null);\n const latest = useRef({ options, onReady });\n latest.current = { options, onReady };\n\n useEffect(() => {\n let disposed = false;\n let created: GpuCanvasInstance | null = null;\n createGpuCanvas(hostRef.current!, latest.current.options).then((instance) => {\n if (disposed) return instance.destroy();\n created = instance;\n setCanvas(instance);\n latest.current.onReady?.(instance);\n });\n return () => {\n disposed = true;\n created?.destroy();\n setCanvas(null);\n };\n // The mode and GPU resources are fixed for the canvas's lifetime; remount to change them.\n }, []);\n\n return (\n <div ref={hostRef} className={className} style={{ position: 'relative', ...style }}>\n <CanvasContext.Provider value={canvas}>{canvas && children}</CanvasContext.Provider>\n </div>\n );\n}\n\nexport interface CanvasNodeProps {\n x: number;\n y: number;\n width: number;\n height: number;\n children?: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\n/** A live DOM node placed on the canvas in world units. Events, focus and state work as usual. */\nexport function CanvasNode({ x, y, width, height, children, className, style }: CanvasNodeProps) {\n const canvas = useContext(CanvasContext);\n if (!canvas) throw new Error('<CanvasNode> must be inside <GpuCanvas>');\n const [element] = useState(() => document.createElement('div'));\n\n useLayoutEffect(() => {\n canvas.addNode(element, { x, y, width, height });\n return () => canvas.removeNode(element);\n // Position changes go through updateNode below without remounting.\n }, [canvas, element]);\n\n const mounted = useRef(false);\n useLayoutEffect(() => {\n if (mounted.current) canvas.updateNode(element, { x, y, width, height });\n mounted.current = true;\n }, [canvas, element, x, y, width, height]);\n\n useLayoutEffect(() => {\n element.className = className ?? '';\n }, [element, className]);\n\n return createPortal(\n <div style={{ width: '100%', height: '100%', ...style }}>{children}</div>,\n element,\n );\n}\n\nexport const useGpuCanvas = () => useContext(CanvasContext);\n"],"mappings":";;;;;;AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,oBAAoB;AA0CvB;AAnCN,IAAM,gBAAgB,cAAwC,IAAI;AAU3D,SAAS,UAAU,EAAE,UAAU,WAAW,OAAO,SAAS,GAAG,QAAQ,GAAmB;AAC7F,QAAM,UAAU,OAAuB,IAAI;AAC3C,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAmC,IAAI;AACnE,QAAM,SAAS,OAAO,EAAE,SAAS,QAAQ,CAAC;AAC1C,SAAO,UAAU,EAAE,SAAS,QAAQ;AAEpC,YAAU,MAAM;AACd,QAAI,WAAW;AACf,QAAI,UAAoC;AACxC,oBAAgB,QAAQ,SAAU,OAAO,QAAQ,OAAO,EAAE,KAAK,CAAC,aAAa;AAC3E,UAAI,SAAU,QAAO,SAAS,QAAQ;AACtC,gBAAU;AACV,gBAAU,QAAQ;AAClB,aAAO,QAAQ,UAAU,QAAQ;AAAA,IACnC,CAAC;AACD,WAAO,MAAM;AACX,iBAAW;AACX,eAAS,QAAQ;AACjB,gBAAU,IAAI;AAAA,IAChB;AAAA,EAEF,GAAG,CAAC,CAAC;AAEL,SACE,oBAAC,SAAI,KAAK,SAAS,WAAsB,OAAO,EAAE,UAAU,YAAY,GAAG,MAAM,GAC/E,8BAAC,cAAc,UAAd,EAAuB,OAAO,QAAS,oBAAU,UAAS,GAC7D;AAEJ;AAaO,SAAS,WAAW,EAAE,GAAG,GAAG,OAAO,QAAQ,UAAU,WAAW,MAAM,GAAoB;AAC/F,QAAM,SAAS,WAAW,aAAa;AACvC,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,yCAAyC;AACtE,QAAM,CAAC,OAAO,IAAI,SAAS,MAAM,SAAS,cAAc,KAAK,CAAC;AAE9D,kBAAgB,MAAM;AACpB,WAAO,QAAQ,SAAS,EAAE,GAAG,GAAG,OAAO,OAAO,CAAC;AAC/C,WAAO,MAAM,OAAO,WAAW,OAAO;AAAA,EAExC,GAAG,CAAC,QAAQ,OAAO,CAAC;AAEpB,QAAM,UAAU,OAAO,KAAK;AAC5B,kBAAgB,MAAM;AACpB,QAAI,QAAQ,QAAS,QAAO,WAAW,SAAS,EAAE,GAAG,GAAG,OAAO,OAAO,CAAC;AACvE,YAAQ,UAAU;AAAA,EACpB,GAAG,CAAC,QAAQ,SAAS,GAAG,GAAG,OAAO,MAAM,CAAC;AAEzC,kBAAgB,MAAM;AACpB,YAAQ,YAAY,aAAa;AAAA,EACnC,GAAG,CAAC,SAAS,SAAS,CAAC;AAEvB,SAAO;AAAA,IACL,oBAAC,SAAI,OAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,GAAG,MAAM,GAAI,UAAS;AAAA,IACnE;AAAA,EACF;AACF;AAEO,IAAM,eAAe,MAAM,WAAW,aAAa;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx"],"sourcesContent":["import {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n bindEditor,\n createGpuCanvas,\n type Editor,\n type EditorOptions,\n type EditorPick,\n type Effect,\n type GpuCanvas as GpuCanvasInstance,\n type GpuCanvasOptions,\n type Layer,\n} from './index';\n\nconst CanvasContext = createContext<GpuCanvasInstance | null>(null);\n\nexport interface GpuCanvasProps extends GpuCanvasOptions {\n children?: ReactNode;\n className?: string;\n style?: CSSProperties;\n onReady?: (canvas: GpuCanvasInstance) => void;\n}\n\n/** An infinite canvas. Children are `<CanvasNode>`s; they mount once the canvas is ready. */\nexport function GpuCanvas({ children, className, style, onReady, ...options }: GpuCanvasProps) {\n const hostRef = useRef<HTMLDivElement>(null);\n const [canvas, setCanvas] = useState<GpuCanvasInstance | null>(null);\n const latest = useRef({ options, onReady });\n latest.current = { options, onReady };\n\n useEffect(() => {\n let disposed = false;\n let created: GpuCanvasInstance | null = null;\n createGpuCanvas(hostRef.current!, latest.current.options).then((instance) => {\n if (disposed) return instance.destroy();\n created = instance;\n setCanvas(instance);\n latest.current.onReady?.(instance);\n });\n return () => {\n disposed = true;\n created?.destroy();\n setCanvas(null);\n };\n // The mode and GPU resources are fixed for the canvas's lifetime; remount to change them.\n }, []);\n\n return (\n <div ref={hostRef} className={className} style={{ position: 'relative', ...style }}>\n <CanvasContext.Provider value={canvas}>{canvas && children}</CanvasContext.Provider>\n </div>\n );\n}\n\nexport interface CanvasNodeProps {\n x: number;\n y: number;\n width: number;\n height: number;\n children?: ReactNode;\n className?: string;\n style?: CSSProperties;\n /** A shader the node is drawn through, such as `effects.glow`. */\n effect?: Effect | null;\n /** Up to four parameters for `effect`; its defaults otherwise. */\n effectParams?: readonly number[];\n}\n\n/** A live DOM node placed on the canvas in world units. Events, focus and state work as usual. */\nexport function CanvasNode({\n x,\n y,\n width,\n height,\n children,\n className,\n style,\n effect = null,\n effectParams,\n}: CanvasNodeProps) {\n const canvas = useContext(CanvasContext);\n if (!canvas) throw new Error('<CanvasNode> must be inside <GpuCanvas>');\n const [element] = useState(() => document.createElement('div'));\n\n useLayoutEffect(() => {\n canvas.addNode(element, { x, y, width, height });\n return () => canvas.removeNode(element);\n // Position changes go through updateNode below without remounting.\n }, [canvas, element]);\n\n const mounted = useRef(false);\n useLayoutEffect(() => {\n if (mounted.current) canvas.updateNode(element, { x, y, width, height });\n mounted.current = true;\n }, [canvas, element, x, y, width, height]);\n\n useLayoutEffect(() => {\n element.className = className ?? '';\n }, [element, className]);\n\n const paramsKey = effectParams?.join(',');\n useLayoutEffect(() => {\n canvas.setEffect(element, effect, effectParams);\n // The parameters' values, not the array's identity, decide a redraw.\n }, [canvas, element, effect, paramsKey]);\n\n return createPortal(\n <div style={{ width: '100%', height: '100%', ...style }}>{children}</div>,\n element,\n );\n}\n\nexport const useGpuCanvas = () => useContext(CanvasContext);\n\n/**\n * The editor (`bindEditor`) on the canvas around it, with its selection as React state. Use it\n * inside `<GpuCanvas>`; `onChange` lifts the selection out.\n */\nexport function useEditor(options: EditorOptions = {}) {\n const canvas = useGpuCanvas();\n const [selected, setSelected] = useState<Layer[]>([]);\n const editor = useRef<Editor | null>(null);\n const latest = useRef(options);\n latest.current = options;\n // Functions are read when they are called, so a new one each render does not rebind.\n const pick = typeof options.pick === 'function' ? 'function' : (options.pick ?? 'node');\n const transforms = !!options.onTransform;\n const { trigger, hover, marquee, labels, snap, color } = options;\n\n useEffect(() => {\n if (!canvas) return;\n const bound = bindEditor(canvas, {\n pick:\n pick === 'function'\n ? (element, at) => (latest.current.pick as PickFunction)(element, at)\n : pick,\n trigger,\n hover,\n marquee,\n labels,\n snap,\n color,\n label: (layer) =>\n latest.current.label ? latest.current.label(layer) : defaultLabel(canvas, layer),\n onChange(next) {\n setSelected(next);\n latest.current.onChange?.(next);\n },\n onTransform: transforms ? (event) => latest.current.onTransform?.(event) : undefined,\n });\n editor.current = bound;\n return () => {\n bound.destroy();\n editor.current = null;\n setSelected([]);\n };\n }, [canvas, pick, trigger, hover, marquee, labels, snap, color, transforms]);\n\n return {\n selected,\n set: (layers: Iterable<Element | Element[]>) => editor.current?.set(layers),\n clear: () => editor.current?.clear(),\n };\n}\n\ntype PickFunction = Extract<EditorPick, (...args: never[]) => unknown>;\n\n/** The editor's own label: the tag name inside nodes, nothing for nodes. */\nconst defaultLabel = (canvas: GpuCanvasInstance, layer: Layer) =>\n canvas.getNodeOf(layer[0]) === layer[0] ? null : layer[0].localName;\n\nexport interface ComponentInfo {\n /** The component's display name. Production builds usually minify it. */\n name: string;\n /** The DOM elements it renders at its top level: one, or several for a fragment. */\n elements: Element[];\n}\n\ninterface Fiber {\n tag: number;\n type: unknown;\n stateNode: unknown;\n return: Fiber | null;\n child: Fiber | null;\n sibling: Fiber | null;\n}\n\n// React's fiber tags for components: function, class, forwardRef, memo.\nconst COMPONENT_TAGS = new Set([0, 1, 11, 15]);\nconst PORTAL_TAG = 4;\n\nconst fiberOf = (element: Element): Fiber | null => {\n const key = Object.keys(element).find((name) => name.startsWith('__reactFiber$'));\n return key ? (element as unknown as Record<string, Fiber>)[key] : null;\n};\n\nconst nameOf = (type: unknown): string => {\n const t = type as {\n displayName?: string;\n name?: string;\n render?: { displayName?: string; name?: string };\n };\n return t.displayName || t.name || t.render?.displayName || t.render?.name || 'Anonymous';\n};\n\nconst topElements = (fiber: Fiber, out: Element[] = []) => {\n for (let child = fiber.child; child; child = child.sibling) {\n if (child.stateNode instanceof Element) out.push(child.stateNode);\n else if (child.tag !== PORTAL_TAG) topElements(child, out);\n }\n return out;\n};\n\n/**\n * The React components that rendered `element`, nearest first, up to the portal into its node\n * (`<CanvasNode>` renders one). It reads React's internal fibers, as click-to-component tools do,\n * so it can break with a React release.\n */\nexport function componentsOf(element: Element): ComponentInfo[] {\n const found: ComponentInfo[] = [];\n for (let fiber = fiberOf(element); fiber; fiber = fiber.return) {\n // Node content reaches its node through a portal; above it is the app around the canvas.\n if (fiber.tag === PORTAL_TAG) break;\n if (!COMPONENT_TAGS.has(fiber.tag)) continue;\n const elements = topElements(fiber);\n if (elements.length) found.push({ name: nameOf(fiber.type), elements });\n }\n return found;\n}\n\n/** The nearest React component that rendered `element`, or null. */\nexport const componentOf = (element: Element): ComponentInfo | null =>\n componentsOf(element)[0] ?? null;\n\n/**\n * A pick for `bindEditor` and `useEditor` that selects React components: a click lands on the\n * outermost component in the node, and each double-click goes one component deeper.\n */\nexport const pickComponents = (element: Element): Element[][] =>\n componentsOf(element)\n .reverse()\n .map((component) => component.elements);\n\n/** A label for components: the name of the outermost component a layer is the top of. */\nexport const labelComponent = (layer: Layer): string | null =>\n componentsOf(layer[0])\n .filter((component) => component.elements[0] === layer[0])\n .at(-1)?.name ?? null;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gpucanvas",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Put thousands of live DOM nodes on an infinite canvas
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Put thousands of live DOM nodes on an infinite canvas: the GPU draws them, an editor selects, moves and resizes them, and shaders run on them.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"canvas",
|
|
7
7
|
"infinite-canvas",
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
"html-in-canvas",
|
|
10
10
|
"react",
|
|
11
11
|
"dom",
|
|
12
|
-
"design-tools"
|
|
12
|
+
"design-tools",
|
|
13
|
+
"editor",
|
|
14
|
+
"shaders",
|
|
15
|
+
"wgsl"
|
|
13
16
|
],
|
|
14
17
|
"author": "Berkant Ay",
|
|
15
18
|
"license": "MIT",
|