proteus-sdk 0.0.0-stage → 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.
- package/README.md +69 -2
- package/dist/convert.d.ts +24 -0
- package/dist/convert.js +76 -0
- package/dist/index.d.ts +586 -0
- package/dist/index.js +750 -0
- package/dist/mount.d.ts +57 -0
- package/dist/mount.js +32 -0
- package/dist/types.d.ts +586 -0
- package/dist/types.js +9 -0
- package/package.json +53 -4
- package/pkg/README.md +11 -0
- package/pkg/package.json +34 -0
- package/pkg/proteus_sdk_web.d.ts +309 -0
- package/pkg/proteus_sdk_web.js +9 -0
- package/pkg/proteus_sdk_web_bg.js +1671 -0
- package/pkg/proteus_sdk_web_bg.wasm +0 -0
- package/pkg/proteus_sdk_web_bg.wasm.d.ts +76 -0
- package/pkg-host/README.md +11 -0
- package/pkg-host/package.json +34 -0
- package/pkg-host/proteus_host_web.d.ts +323 -0
- package/pkg-host/proteus_host_web.js +9 -0
- package/pkg-host/proteus_host_web_bg.js +3428 -0
- package/pkg-host/proteus_host_web_bg.wasm +0 -0
- package/pkg-host/proteus_host_web_bg.wasm.d.ts +84 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Proteus TypeScript SDK: create components, connect them, and transition
|
|
3
|
+
* between them.
|
|
4
|
+
*
|
|
5
|
+
* Start with {@link mount}, which runs an app on a `<canvas>`. Each handle
|
|
6
|
+
* ({@link Handle}, {@link TransitionChannel}, {@link TextureHandle}) keeps a
|
|
7
|
+
* reference to the {@link ProteusApp} it came from, so its methods are called
|
|
8
|
+
* directly: `button.onClick(() => ...)`.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
import { Handle as WasmHandle, ProteusApp as WasmApp, TransitionChannel as WasmTransitionChannel, TextureHandle as WasmTextureHandle, VideoHandle as WasmVideoHandle } from "../pkg/proteus_sdk_web.js";
|
|
13
|
+
import type { ImageCrop, ImageSpec, ChildBehavior, ComponentData, ComponentSpec, Geometry, MergeLayout, SplitStrategy, TextSpec, TextureRequest, TextureState, TransitionInteractionConfig, TransitionConfig, TransitionDropped, Size } from "./types.js";
|
|
14
|
+
export * from "./types.js";
|
|
15
|
+
export * from "./convert.js";
|
|
16
|
+
export * from "./mount.js";
|
|
17
|
+
/** A callback with no arguments, such as {@link Handle.onClick}'s. */
|
|
18
|
+
export type PlainCallback = () => void;
|
|
19
|
+
/**
|
|
20
|
+
* {@link Handle.onDrag}'s callback: how far the pointer moved since the last
|
|
21
|
+
* frame, in world units (x right, y up).
|
|
22
|
+
*/
|
|
23
|
+
export type DragCallback = (delta: {
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
}) => void;
|
|
27
|
+
/** {@link TransitionChannel.onDropped}'s callback: the request that couldn't run, and why. */
|
|
28
|
+
export type DroppedCallback = (dropped: TransitionDropped) => void;
|
|
29
|
+
/**
|
|
30
|
+
* A handle to one component.
|
|
31
|
+
*
|
|
32
|
+
* Callbacks registered with the `on*` methods run every time their event
|
|
33
|
+
* happens, until the component is destroyed. They run after the app's update
|
|
34
|
+
* for that tick, so anything they start takes effect on the next tick.
|
|
35
|
+
*
|
|
36
|
+
* Once the component is destroyed, methods that change it throw, and the
|
|
37
|
+
* failure is also logged. Methods that read it return `undefined`. A method
|
|
38
|
+
* that returns `boolean` uses `false` for "there was nothing to do", such as
|
|
39
|
+
* an image that hasn't been baked yet; that is not an error and doesn't throw.
|
|
40
|
+
*/
|
|
41
|
+
export declare class Handle {
|
|
42
|
+
private readonly app;
|
|
43
|
+
/** @internal */
|
|
44
|
+
readonly wasmHandle: WasmHandle;
|
|
45
|
+
/**
|
|
46
|
+
* Prefer {@link ProteusApp.component} or {@link ProteusApp.handleFromId}
|
|
47
|
+
* over calling this directly.
|
|
48
|
+
*/
|
|
49
|
+
constructor(app: ProteusApp,
|
|
50
|
+
/** @internal */
|
|
51
|
+
wasmHandle: WasmHandle);
|
|
52
|
+
/** The component's ID, for {@link ProteusApp.handleFromId} and {@link ComponentSpec.children}. */
|
|
53
|
+
id(): number;
|
|
54
|
+
/** A snapshot of the component's current state, or `undefined` if it has been destroyed. */
|
|
55
|
+
get(): ComponentData | undefined;
|
|
56
|
+
/** Calls `cb` each time the pointer is pressed on this component. A click also gives it focus. */
|
|
57
|
+
onClick(cb: PlainCallback): void;
|
|
58
|
+
/**
|
|
59
|
+
* Calls `cb` each time a transition finishes on this component.
|
|
60
|
+
*
|
|
61
|
+
* Every transition reports its completion once, on one component:
|
|
62
|
+
*
|
|
63
|
+
* - {@link Handle.animateTo}: this component.
|
|
64
|
+
* - {@link TransitionChannel.set}: the `to` component.
|
|
65
|
+
* - {@link Handle.splitTo} or {@link Handle.splitToWithStates} with
|
|
66
|
+
* `"row"`, `"column"` or `"grid"`: the source, once every target has arrived.
|
|
67
|
+
* - {@link Handle.splitTo} or {@link Handle.splitToWithStates} with
|
|
68
|
+
* `"perTarget"`: each target, separately. The source has no transition of
|
|
69
|
+
* its own; it is hidden as soon as the split starts.
|
|
70
|
+
* - {@link Handle.mergeFrom}: the destination, once every source has
|
|
71
|
+
* arrived.
|
|
72
|
+
*
|
|
73
|
+
* Changes of interaction style, such as a hover effect, don't count.
|
|
74
|
+
*/
|
|
75
|
+
onTransitionComplete(cb: PlainCallback): void;
|
|
76
|
+
/** Calls `cb` each time the pointer moves onto this component. */
|
|
77
|
+
onHoverEnter(cb: PlainCallback): void;
|
|
78
|
+
/** Calls `cb` each time the pointer moves off this component. */
|
|
79
|
+
onHoverExit(cb: PlainCallback): void;
|
|
80
|
+
/**
|
|
81
|
+
* Calls `cb` each time the pointer is pressed on this component. This fires
|
|
82
|
+
* at the same moment as {@link Handle.onClick}; use it with
|
|
83
|
+
* {@link Handle.onRelease} to follow a press from start to end.
|
|
84
|
+
*/
|
|
85
|
+
onPress(cb: PlainCallback): void;
|
|
86
|
+
/**
|
|
87
|
+
* Calls `cb` each time the pointer is released after a press on this
|
|
88
|
+
* component, even if the pointer has moved off it.
|
|
89
|
+
*/
|
|
90
|
+
onRelease(cb: PlainCallback): void;
|
|
91
|
+
/** Calls `cb` each time this component gains focus. */
|
|
92
|
+
onFocus(cb: PlainCallback): void;
|
|
93
|
+
/** Calls `cb` each time this component loses focus. */
|
|
94
|
+
onBlur(cb: PlainCallback): void;
|
|
95
|
+
/**
|
|
96
|
+
* Calls `cb` every tick while this component is pressed, with the distance
|
|
97
|
+
* the pointer moved since the previous tick, in world units (y points up).
|
|
98
|
+
*/
|
|
99
|
+
onDrag(cb: DragCallback): void;
|
|
100
|
+
/**
|
|
101
|
+
* Makes `child` a child of this component. Its geometry becomes relative to
|
|
102
|
+
* this component's.
|
|
103
|
+
*/
|
|
104
|
+
addChild(child: Handle): void;
|
|
105
|
+
/**
|
|
106
|
+
* Detaches `child` from this component. The child is kept, as a top-level
|
|
107
|
+
* component; to destroy it instead, call {@link Handle.destroy} on it.
|
|
108
|
+
*
|
|
109
|
+
* A detached child may move on screen: its geometry was relative to this
|
|
110
|
+
* component, and is now relative to the world.
|
|
111
|
+
*
|
|
112
|
+
* Throws if either component no longer exists, or if `child` isn't a child
|
|
113
|
+
* of this component.
|
|
114
|
+
*/
|
|
115
|
+
removeChild(child: Handle): void;
|
|
116
|
+
/**
|
|
117
|
+
* Splits this component into `targets`: a 1→N transition.
|
|
118
|
+
*
|
|
119
|
+
* Each target ends at its own declared geometry. This component is hidden
|
|
120
|
+
* as soon as the split starts. With `"row"`, `"column"` and `"grid"`, slices of
|
|
121
|
+
* this component move into place and the targets appear when they arrive;
|
|
122
|
+
* with `"perTarget"`, the targets themselves move. The transition starts on
|
|
123
|
+
* the next tick.
|
|
124
|
+
*
|
|
125
|
+
* `childBehavior`, if given, sets each target's timing separately. It is
|
|
126
|
+
* called once per target with its index and the total; see
|
|
127
|
+
* {@link ChildBehavior}.
|
|
128
|
+
*/
|
|
129
|
+
splitTo(targets: Handle[], config: TransitionConfig, strategy: SplitStrategy, childBehavior?: ChildBehavior): void;
|
|
130
|
+
/**
|
|
131
|
+
* Merges `sources` into this component: an N→1 transition.
|
|
132
|
+
*
|
|
133
|
+
* The sources are hidden as soon as the merge starts. `layout` decides
|
|
134
|
+
* which part of this component each source moves toward. The transition
|
|
135
|
+
* starts on the next tick. `childBehavior`, if given, sets each source's
|
|
136
|
+
* timing separately; see {@link ChildBehavior}.
|
|
137
|
+
*/
|
|
138
|
+
mergeFrom(sources: Handle[], config: TransitionConfig, layout: MergeLayout, childBehavior?: ChildBehavior): void;
|
|
139
|
+
/**
|
|
140
|
+
* Destroys this component and its children, along with their callbacks and
|
|
141
|
+
* the channels they own.
|
|
142
|
+
*
|
|
143
|
+
* @throws if it was already destroyed. Safe to ignore, but reported so that
|
|
144
|
+
* destroying twice is visible.
|
|
145
|
+
*/
|
|
146
|
+
destroy(): void;
|
|
147
|
+
/**
|
|
148
|
+
* Removes this component's text, image and baked content, and releases
|
|
149
|
+
* their textures. The component itself remains, drawn as a plain quad in
|
|
150
|
+
* its color. Nothing is baked again; to show new content, use
|
|
151
|
+
* {@link Handle.setText} or {@link Handle.setImage}.
|
|
152
|
+
*
|
|
153
|
+
* A component made with {@link ComponentSpec.bake} lost its children and
|
|
154
|
+
* its own color when it was baked, so afterwards it is a plain white quad.
|
|
155
|
+
*
|
|
156
|
+
* Releasing a texture doesn't free atlas space immediately. A texture that
|
|
157
|
+
* no component references becomes available for reuse, and the atlas
|
|
158
|
+
* reclaims its space when it needs room for another texture. Textures
|
|
159
|
+
* marked `eternal` are never reclaimed.
|
|
160
|
+
*
|
|
161
|
+
* @throws if this component no longer exists.
|
|
162
|
+
*/
|
|
163
|
+
freeResources(): void;
|
|
164
|
+
/**
|
|
165
|
+
* Replaces this component's text, or adds text to a component that has
|
|
166
|
+
* none. The host bakes the new text before it next draws, and the old
|
|
167
|
+
* text's texture is released (see {@link Handle.freeResources}).
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* score.setText({ content: `Score: ${points}`, sizePx: 24 });
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
174
|
+
* @throws if this component no longer exists, or `text` isn't a valid
|
|
175
|
+
* {@link TextSpec}.
|
|
176
|
+
*/
|
|
177
|
+
setText(text: TextSpec): void;
|
|
178
|
+
/**
|
|
179
|
+
* Replaces this component's image, or adds an image to a component that
|
|
180
|
+
* has none. The host decodes and bakes the new image before it next draws,
|
|
181
|
+
* and the old image's texture is released (see
|
|
182
|
+
* {@link Handle.freeResources}). A crop set with {@link Handle.cropImage}
|
|
183
|
+
* applied to the old image, so it is cleared.
|
|
184
|
+
*
|
|
185
|
+
* @throws if this component no longer exists, or `image` isn't a valid
|
|
186
|
+
* {@link ImageSpec}.
|
|
187
|
+
*/
|
|
188
|
+
setImage(image: ImageSpec): void;
|
|
189
|
+
/**
|
|
190
|
+
* Like {@link Handle.splitTo}, with each target's end geometry given
|
|
191
|
+
* explicitly instead of taken from its declared geometry.
|
|
192
|
+
*
|
|
193
|
+
* Use this when a target should end somewhere other than its declared
|
|
194
|
+
* geometry, or when this component is also one of the targets. In the
|
|
195
|
+
* second case, pass its end geometry here rather than calling
|
|
196
|
+
* {@link Handle.setDeclaredGeometry} first, which would move it before the
|
|
197
|
+
* split begins.
|
|
198
|
+
*/
|
|
199
|
+
splitToWithStates(targets: {
|
|
200
|
+
handle: Handle;
|
|
201
|
+
state: Geometry;
|
|
202
|
+
}[], config: TransitionConfig, strategy: SplitStrategy): void;
|
|
203
|
+
/**
|
|
204
|
+
* Sets this component's declared geometry and moves it there immediately.
|
|
205
|
+
*
|
|
206
|
+
* Use this when a component's layout can only be worked out after it is
|
|
207
|
+
* created, such as a cell sized to fit its text
|
|
208
|
+
* ({@link Handle.bakedTextSize}). Transitions into the component, and its
|
|
209
|
+
* interaction styles, use the new geometry.
|
|
210
|
+
*/
|
|
211
|
+
setDeclaredGeometry(state: Geometry): void;
|
|
212
|
+
/**
|
|
213
|
+
* Transitions this component from its current geometry to `to`, where it
|
|
214
|
+
* then rests: `to` becomes its declared geometry, as with
|
|
215
|
+
* {@link Handle.setDeclaredGeometry}, so a later transition into it, or an
|
|
216
|
+
* interaction style, starts from there.
|
|
217
|
+
*
|
|
218
|
+
* Unlike {@link TransitionChannel.set}, only this component is involved, which
|
|
219
|
+
* makes this a good fit for moving a component around repeatedly. Calling
|
|
220
|
+
* it during a transition starts a new one from wherever the component is.
|
|
221
|
+
* The transition starts on the next tick.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```ts
|
|
225
|
+
* card.animateTo(
|
|
226
|
+
* { ...card.get()!.geometry, position: { x: 300, y: 0, z: 0 } },
|
|
227
|
+
* { duration: 0.3, easing: "easeOutCubic" },
|
|
228
|
+
* );
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
animateTo(to: Geometry, config: TransitionConfig): void;
|
|
232
|
+
/**
|
|
233
|
+
* The size in pixels of this component's baked text, or `undefined` if the
|
|
234
|
+
* text hasn't been baked yet or the component has none. The host bakes text
|
|
235
|
+
* the next time it renders a frame.
|
|
236
|
+
*/
|
|
237
|
+
bakedTextSize(): Size | undefined;
|
|
238
|
+
/**
|
|
239
|
+
* The size in pixels of this component's baked image, or `undefined` if the
|
|
240
|
+
* image hasn't been baked yet or the component has none. This is the full
|
|
241
|
+
* image size, even after {@link Handle.cropImage}.
|
|
242
|
+
*/
|
|
243
|
+
bakedImageSize(): Size | undefined;
|
|
244
|
+
/**
|
|
245
|
+
* Shows `source`'s baked image on this component, replacing its current
|
|
246
|
+
* image. Returns `false` if `source` has no baked image yet.
|
|
247
|
+
*
|
|
248
|
+
* The two components share the texture, which stays in the atlas as long as
|
|
249
|
+
* either one references it. Use this to prepare a hidden component before a
|
|
250
|
+
* transition reveals it: for example, before merging grid tiles into a
|
|
251
|
+
* detail view, copy the selected tile's image onto the detail view.
|
|
252
|
+
*/
|
|
253
|
+
copyBakedImageFrom(source: Handle): boolean;
|
|
254
|
+
/**
|
|
255
|
+
* Shows only the part of this component's image that `crop` selects: for
|
|
256
|
+
* example `{ kind: "centeredSquare" }` to fill a square grid tile with an
|
|
257
|
+
* image of any shape.
|
|
258
|
+
*
|
|
259
|
+
* The crop is always measured from the whole image, so calling it again
|
|
260
|
+
* replaces the crop rather than cropping the crop, and `{ kind: "none" }`
|
|
261
|
+
* shows the whole image again. Only the visible region changes: no pixels
|
|
262
|
+
* are copied. Returns `false`, and changes nothing, if the image hasn't been
|
|
263
|
+
* baked yet. Throws if `crop` isn't a valid {@link ImageCrop}.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```ts
|
|
267
|
+
* // A 16:9 view of the image, kept to its top edge.
|
|
268
|
+
* tile.cropImage({ kind: "aspect", ratio: 16 / 9, anchor: { x: 0.5, y: 0 } });
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
cropImage(crop: ImageCrop): boolean;
|
|
272
|
+
/**
|
|
273
|
+
* Sets whether this component responds to input.
|
|
274
|
+
*
|
|
275
|
+
* A non-interactive component is not there for input: it is never hovered,
|
|
276
|
+
* pressed, dragged or focused, and input goes to whatever is behind it.
|
|
277
|
+
* `false` has the same effect as {@link ComponentSpec.nonInteractive},
|
|
278
|
+
* applied after creation. Use it for things that are never controls, such
|
|
279
|
+
* as backgrounds and labels. For a control that is temporarily unavailable,
|
|
280
|
+
* use {@link Handle.setDisabled}, which still blocks input.
|
|
281
|
+
*
|
|
282
|
+
* Proteus currently handles pointer input only (on the web, that includes
|
|
283
|
+
* touch and pen). Other kinds of input will follow the same rule.
|
|
284
|
+
*/
|
|
285
|
+
setInteractive(interactive: boolean): void;
|
|
286
|
+
/**
|
|
287
|
+
* Shows or hides this component and its children.
|
|
288
|
+
*
|
|
289
|
+
* A hidden component is neither drawn nor hit-tested.
|
|
290
|
+
* {@link TransitionChannel.set} already hides the component it transitions from
|
|
291
|
+
* and shows the one it transitions to; use this for everything else.
|
|
292
|
+
*
|
|
293
|
+
* A hidden component stops being drawn on the next frame and stops receiving
|
|
294
|
+
* input one tick later. Input is matched against what was last drawn, so a
|
|
295
|
+
* click in the same tick as the hide still reaches it.
|
|
296
|
+
*/
|
|
297
|
+
setVisible(visible: boolean): void;
|
|
298
|
+
/**
|
|
299
|
+
* Sets this component's opacity, clamped to `0`–`1`.
|
|
300
|
+
*
|
|
301
|
+
* Opacity multiplies down the hierarchy: a child at `0.6` under a parent at
|
|
302
|
+
* `0.6` is drawn at `0.36`. It only affects drawing, so a component at `0`
|
|
303
|
+
* still receives pointer input. To take a component out of input as well,
|
|
304
|
+
* use {@link Handle.setVisible}.
|
|
305
|
+
*/
|
|
306
|
+
setOpacity(opacity: number): void;
|
|
307
|
+
/**
|
|
308
|
+
* Disables or re-enables this component.
|
|
309
|
+
*
|
|
310
|
+
* A disabled component is still drawn and still blocks input from reaching
|
|
311
|
+
* what is behind it, but fires no events itself, and shows its
|
|
312
|
+
* {@link ComponentSpec.disabled} style, as a disabled control does on the
|
|
313
|
+
* web. Use it for a control that isn't available yet, such as a submit
|
|
314
|
+
* button. For something that is never a control, use
|
|
315
|
+
* {@link Handle.setInteractive}, which lets input through.
|
|
316
|
+
*/
|
|
317
|
+
setDisabled(disabled: boolean): void;
|
|
318
|
+
/**
|
|
319
|
+
* Sets whether this component accepts input while transitioning.
|
|
320
|
+
* `undefined` restores the default, where a transitioning component ignores
|
|
321
|
+
* input.
|
|
322
|
+
*/
|
|
323
|
+
setTransitionInteraction(config: TransitionInteractionConfig | undefined): void;
|
|
324
|
+
/**
|
|
325
|
+
* Sets this component's image to `texture`, replacing its current image.
|
|
326
|
+
* Returns `false` if the texture has been evicted.
|
|
327
|
+
*
|
|
328
|
+
* Only the image changes: text on the component is still drawn on top, and
|
|
329
|
+
* video, if the component is showing it, still plays. Swapping textures is
|
|
330
|
+
* cheap, which makes this suitable for frame-by-frame animation from
|
|
331
|
+
* textures loaded in advance.
|
|
332
|
+
*/
|
|
333
|
+
setTexture(texture: TextureHandle): boolean;
|
|
334
|
+
/**
|
|
335
|
+
* Shows `video` on this component, in place of its image or color.
|
|
336
|
+
* **Experimental**: see {@link VideoHandle}.
|
|
337
|
+
*
|
|
338
|
+
* If the component also has an image, {@link Handle.setVideoCrossfade}
|
|
339
|
+
* blends between the two; this starts fully on the video. Returns `false`,
|
|
340
|
+
* changing nothing, if `video` was released or replaced.
|
|
341
|
+
*
|
|
342
|
+
* @throws if this component no longer exists.
|
|
343
|
+
*/
|
|
344
|
+
showVideo(video: VideoHandle): boolean;
|
|
345
|
+
/**
|
|
346
|
+
* Stops showing video on this component, which returns to its image or
|
|
347
|
+
* color.
|
|
348
|
+
*
|
|
349
|
+
* @throws if this component no longer exists.
|
|
350
|
+
*/
|
|
351
|
+
hideVideo(): void;
|
|
352
|
+
/**
|
|
353
|
+
* Sets the blend between this component's image (`0`) and its video (`1`).
|
|
354
|
+
* To fade the video in, call it with `0` right after
|
|
355
|
+
* {@link Handle.showVideo}, then raise it over time. Returns `false` if the
|
|
356
|
+
* component isn't showing video.
|
|
357
|
+
*
|
|
358
|
+
* @throws if this component no longer exists.
|
|
359
|
+
*/
|
|
360
|
+
setVideoCrossfade(videoT: number): boolean;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* A transition channel, which transitions one component into another: see
|
|
364
|
+
* {@link TransitionChannel.set}.
|
|
365
|
+
*/
|
|
366
|
+
export declare class TransitionChannel {
|
|
367
|
+
private readonly app;
|
|
368
|
+
/** @internal */
|
|
369
|
+
readonly wasmHandle: WasmTransitionChannel;
|
|
370
|
+
/** Prefer {@link ProteusApp.transitionChannel} over calling this directly. */
|
|
371
|
+
constructor(app: ProteusApp,
|
|
372
|
+
/** @internal */
|
|
373
|
+
wasmHandle: WasmTransitionChannel);
|
|
374
|
+
/**
|
|
375
|
+
* Transitions `from` into `to`: a 1→1 transition.
|
|
376
|
+
*
|
|
377
|
+
* `from` is hidden, and `to` is shown and moves from `from`'s current
|
|
378
|
+
* geometry to its own declared geometry. If `to` is already transitioning,
|
|
379
|
+
* the request is dropped unless `interruptible` is set; then a new
|
|
380
|
+
* transition starts from wherever `to` is. The transition starts on the
|
|
381
|
+
* next tick. A request that can't run is reported to
|
|
382
|
+
* {@link TransitionChannel.onDropped}. On a destroyed channel, the call is
|
|
383
|
+
* ignored with a warning.
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* ```ts
|
|
387
|
+
* const open = app.transitionChannel();
|
|
388
|
+
* button.onClick(() => open.set(panel, button, { duration: 0.4 }));
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
set(to: Handle, from: Handle, config: TransitionConfig, interruptible?: boolean): void;
|
|
392
|
+
/**
|
|
393
|
+
* Calls `cb` with the reason each time a {@link TransitionChannel.set} request on
|
|
394
|
+
* this channel can't run. See {@link DropReason}.
|
|
395
|
+
*/
|
|
396
|
+
onDropped(cb: DroppedCallback): void;
|
|
397
|
+
/** Destroys this channel and its `onDropped` handlers. Later `set` calls are ignored, with a warning. */
|
|
398
|
+
destroy(): void;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* A texture in the atlas. Show it on a component with
|
|
402
|
+
* {@link Handle.setTexture}.
|
|
403
|
+
*
|
|
404
|
+
* A texture can't be freed through its handle: once no component references
|
|
405
|
+
* it, the atlas can reclaim its space when it needs room. See
|
|
406
|
+
* {@link Handle.freeResources}.
|
|
407
|
+
*/
|
|
408
|
+
export declare class TextureHandle {
|
|
409
|
+
private readonly app;
|
|
410
|
+
/** @internal */
|
|
411
|
+
readonly wasmHandle: WasmTextureHandle;
|
|
412
|
+
/** Prefer {@link ProteusApp.texture} over calling this directly. */
|
|
413
|
+
constructor(app: ProteusApp,
|
|
414
|
+
/** @internal */
|
|
415
|
+
wasmHandle: WasmTextureHandle);
|
|
416
|
+
/** The texture's ID, for {@link ProteusApp.textureFromId}. */
|
|
417
|
+
id(): number;
|
|
418
|
+
/** The texture's kind and size, or `undefined` if it has been evicted or never existed. */
|
|
419
|
+
state(): TextureState | undefined;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* A video whose frames your own player supplies. **Experimental.**
|
|
423
|
+
*
|
|
424
|
+
* Proteus shows video; it doesn't play it. Play the video with your own
|
|
425
|
+
* player, usually a `<video>` element, and upload each frame: with
|
|
426
|
+
* {@link VideoHandle.uploadFrom} for a `<video>` element, or
|
|
427
|
+
* {@link VideoHandle.uploadFrame} for pixels you decoded yourself. Show it on
|
|
428
|
+
* components with {@link Handle.showVideo}. Playback controls are the
|
|
429
|
+
* player's own.
|
|
430
|
+
*
|
|
431
|
+
* There is one video at a time: {@link ProteusApp.createVideo} replaces any
|
|
432
|
+
* video that already exists, and its handle stops working.
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* ```ts
|
|
436
|
+
* const video = app.createVideo();
|
|
437
|
+
* tile.showVideo(video);
|
|
438
|
+
* const element = document.querySelector("video")!;
|
|
439
|
+
* const upload = () => {
|
|
440
|
+
* video.uploadFrom(element);
|
|
441
|
+
* element.requestVideoFrameCallback(upload);
|
|
442
|
+
* };
|
|
443
|
+
* element.requestVideoFrameCallback(upload);
|
|
444
|
+
* ```
|
|
445
|
+
*/
|
|
446
|
+
export declare class VideoHandle {
|
|
447
|
+
private readonly app;
|
|
448
|
+
/** @internal */
|
|
449
|
+
readonly wasmHandle: WasmVideoHandle;
|
|
450
|
+
private canvas?;
|
|
451
|
+
private context?;
|
|
452
|
+
/** Prefer {@link ProteusApp.createVideo} over calling this directly. */
|
|
453
|
+
constructor(app: ProteusApp,
|
|
454
|
+
/** @internal */
|
|
455
|
+
wasmHandle: WasmVideoHandle);
|
|
456
|
+
/**
|
|
457
|
+
* Uploads one frame of `width × height` RGBA pixels, `width * height * 4`
|
|
458
|
+
* bytes, which every component showing this video draws from the next
|
|
459
|
+
* frame. The video's texture follows the frames' size.
|
|
460
|
+
*
|
|
461
|
+
* Returns `false`, uploading nothing, if the video was released or
|
|
462
|
+
* replaced, or if `rgba` has the wrong length.
|
|
463
|
+
*/
|
|
464
|
+
uploadFrame(width: number, height: number, rgba: Uint8Array | Uint8ClampedArray): boolean;
|
|
465
|
+
/**
|
|
466
|
+
* Uploads the frame `element` is showing now. Call it for each new frame,
|
|
467
|
+
* for example from `requestVideoFrameCallback`.
|
|
468
|
+
*
|
|
469
|
+
* The frame is drawn to an offscreen canvas and read back, which works in
|
|
470
|
+
* every browser. Returns `false` if the element has no frame yet, or as
|
|
471
|
+
* {@link VideoHandle.uploadFrame} does.
|
|
472
|
+
*
|
|
473
|
+
* @throws a `SecurityError` if the video comes from another origin without
|
|
474
|
+
* CORS, since the browser then won't let its pixels be read.
|
|
475
|
+
*/
|
|
476
|
+
uploadFrom(element: HTMLVideoElement): boolean;
|
|
477
|
+
/**
|
|
478
|
+
* Releases this video and its GPU texture. Components showing video show
|
|
479
|
+
* nothing in its place until {@link Handle.hideVideo} or another video.
|
|
480
|
+
*/
|
|
481
|
+
release(): void;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* An app's components, channels and callbacks.
|
|
485
|
+
*
|
|
486
|
+
* {@link mount} creates one and passes it to your `setup` function; that is the
|
|
487
|
+
* usual way to get one. `new ProteusApp()` creates a standalone app that draws
|
|
488
|
+
* nothing, which is useful for tests and other headless use.
|
|
489
|
+
*/
|
|
490
|
+
export declare class ProteusApp {
|
|
491
|
+
/** @internal */
|
|
492
|
+
readonly wasmApp: WasmApp;
|
|
493
|
+
/**
|
|
494
|
+
* Creates a standalone app that draws nothing.
|
|
495
|
+
*
|
|
496
|
+
* @param wasmApp For internal use by {@link mount}; omit it.
|
|
497
|
+
*/
|
|
498
|
+
constructor(wasmApp?: WasmApp);
|
|
499
|
+
/** Creates a component from `spec` and returns its handle. */
|
|
500
|
+
component(spec: ComponentSpec): Handle;
|
|
501
|
+
/**
|
|
502
|
+
* Returns a {@link Handle} for a component ID, from {@link Handle.id} or
|
|
503
|
+
* {@link ComponentData.children}.
|
|
504
|
+
*
|
|
505
|
+
* The ID isn't checked. If its component has been destroyed, the handle
|
|
506
|
+
* behaves like any handle to a destroyed component.
|
|
507
|
+
*/
|
|
508
|
+
handleFromId(id: number): Handle;
|
|
509
|
+
/**
|
|
510
|
+
* Creates a transition channel, which transitions one component into
|
|
511
|
+
* another with {@link TransitionChannel.set}. If `owner` is given, the
|
|
512
|
+
* channel is destroyed along with it.
|
|
513
|
+
*/
|
|
514
|
+
transitionChannel(owner?: Handle): TransitionChannel;
|
|
515
|
+
/** Returns a {@link TextureHandle} for a texture ID. The same as {@link ProteusApp.textureFromId}. */
|
|
516
|
+
texture(id: number): TextureHandle;
|
|
517
|
+
/**
|
|
518
|
+
* Decodes a PNG or JPEG image and adds it to the atlas, returning a handle
|
|
519
|
+
* to show with {@link Handle.setTexture}.
|
|
520
|
+
*
|
|
521
|
+
* The pixels are on the GPU when this returns, so there is nothing to wait
|
|
522
|
+
* for. You supply the bytes:
|
|
523
|
+
*
|
|
524
|
+
* ```ts
|
|
525
|
+
* const bytes = new Uint8Array(await (await fetch(url)).arrayBuffer());
|
|
526
|
+
* const tex = app.loadTexture(bytes, { maxSide: 512 });
|
|
527
|
+
* if (tex) tile.setTexture(tex);
|
|
528
|
+
* ```
|
|
529
|
+
*
|
|
530
|
+
* Show the texture on a component in the same frame. From the next frame
|
|
531
|
+
* on, a texture that no component uses may be evicted to make room.
|
|
532
|
+
* Several textures can be loaded and then shown: none is evicted in the
|
|
533
|
+
* frame it was loaded.
|
|
534
|
+
*
|
|
535
|
+
* Returns `undefined` if the bytes can't be decoded. An image that decodes
|
|
536
|
+
* but doesn't fit the atlas returns a handle to no texture, which draws
|
|
537
|
+
* nothing.
|
|
538
|
+
*/
|
|
539
|
+
loadTexture(bytes: Uint8Array, request?: TextureRequest): TextureHandle | undefined;
|
|
540
|
+
/**
|
|
541
|
+
* Adds RGBA pixels to the atlas and returns a handle to them. `rgba` holds
|
|
542
|
+
* `width * height * 4` bytes. For PNG or JPEG data, use
|
|
543
|
+
* {@link ProteusApp.loadTexture}.
|
|
544
|
+
*
|
|
545
|
+
* Show the texture on a component in the same frame: from the next frame
|
|
546
|
+
* on, a texture that no component uses may be evicted to make room. If the
|
|
547
|
+
* atlas is full, the handle refers to no texture and draws nothing.
|
|
548
|
+
*/
|
|
549
|
+
bakeTexture(width: number, height: number, rgba: Uint8Array, request?: TextureRequest): TextureHandle;
|
|
550
|
+
/**
|
|
551
|
+
* Creates a video, whose frames your own player supplies.
|
|
552
|
+
* **Experimental**: see {@link VideoHandle}. There is one video at a time:
|
|
553
|
+
* this replaces any video that already exists.
|
|
554
|
+
*/
|
|
555
|
+
createVideo(): VideoHandle;
|
|
556
|
+
/** Returns a {@link TextureHandle} for an ID from {@link TextureHandle.id}. */
|
|
557
|
+
textureFromId(id: number): TextureHandle;
|
|
558
|
+
/** A snapshot of a component's current state, or `undefined` if it has been destroyed. */
|
|
559
|
+
get(handle: Handle): ComponentData | undefined;
|
|
560
|
+
/**
|
|
561
|
+
* Advances the app by `deltaSeconds`, then runs callbacks for the events
|
|
562
|
+
* that occurred. {@link mount} calls this for you every frame.
|
|
563
|
+
*/
|
|
564
|
+
tick(deltaSeconds: number): void;
|
|
565
|
+
/**
|
|
566
|
+
* Updates the pointer position, in world units: the origin is the center of
|
|
567
|
+
* the viewport and y points up. Convert from page coordinates with
|
|
568
|
+
* {@link topLeftToWorld}. {@link mount} reports the pointer for you.
|
|
569
|
+
*/
|
|
570
|
+
pointerMoved(x: number, y: number): void;
|
|
571
|
+
/** Records that the pointer left the viewport. */
|
|
572
|
+
pointerLeft(): void;
|
|
573
|
+
/** Records that the pointer was pressed. */
|
|
574
|
+
pointerPressed(): void;
|
|
575
|
+
/** Records that the pointer was released. */
|
|
576
|
+
pointerReleased(): void;
|
|
577
|
+
/**
|
|
578
|
+
* Calls {@link ProteusApp.tick} on every animation frame, and returns a
|
|
579
|
+
* function that stops the loop.
|
|
580
|
+
*
|
|
581
|
+
* Only for a standalone app created with `new ProteusApp()`. An app from
|
|
582
|
+
* {@link mount} is already ticked every frame; starting this loop as well
|
|
583
|
+
* would tick it twice.
|
|
584
|
+
*/
|
|
585
|
+
startAnimationLoop(): () => void;
|
|
586
|
+
}
|