@solidrt/flux-types 0.0.59 → 0.0.61

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/gui/audio.d.ts CHANGED
@@ -144,9 +144,10 @@ declare module "flux:audio" {
144
144
  /**
145
145
  * Open a clip for streaming: it is decoded on demand instead of loaded fully
146
146
  * into memory, so a large track needs little RAM. Takes a `file()` from
147
- * `flux:fs` (not a path), so the source rides the `file()` proxy override - a
148
- * dev-server-proxied file streams from the server. Play the result as a single
149
- * playback; do not overlap a stream with itself. Call `unload()` when done.
147
+ * `flux:fs` (not a path), read from local storage like any `file()` (the
148
+ * app's assets in a packed app); there is no network streaming. Play the
149
+ * result as a single playback; do not overlap a stream with itself. Call
150
+ * `unload()` when done.
150
151
  */
151
152
  export function stream(source: ReturnType<typeof import("flux:fs").file>): Clip
152
153
  /**
package/gui/gpu.d.ts CHANGED
@@ -1104,6 +1104,11 @@ declare module "flux:gpu" {
1104
1104
  /** Draw the instance records in key order (see {@link InstanceOrder}). */
1105
1105
  instanceOrder?: InstanceOrder
1106
1106
  before?: DrawId
1107
+ /** Debug name reported beside the entry in the resource inventory
1108
+ * (`/gpu`, `get_gpu_resources`), so a draw is found by what it draws
1109
+ * (a mesh's geometry label, a model part's name) rather than by
1110
+ * matching counts. Free-form; the GPU never sees it. */
1111
+ label?: string
1107
1112
  } & (DrawRange | (IndexBinding & IndexRange)),
1108
1113
  ): DrawId
1109
1114
  /**
@@ -152,6 +152,34 @@ declare module "flux:rendertree" {
152
152
  * comes back on the sticky "pointerLock" bus event.
153
153
  */
154
154
  export function setPointerLock(locked: boolean): void
155
+ /** One image of a cursor frame: straight-alpha RGBA8, `width * height * 4` bytes. */
156
+ export interface CursorImage {
157
+ data: Uint8Array
158
+ width: number
159
+ height: number
160
+ }
161
+ /**
162
+ * One frame of a cursor. `images[0]` is the 1x representation; the rest
163
+ * are HiDPI alternates the platform picks by their size ratio to it (a
164
+ * 64x64 alternate of a 32x32 base serves 2x displays). `duration` is the
165
+ * frame's show time in milliseconds for an animated cursor (0 holds the
166
+ * frame, ending a one-shot animation on it); a static cursor is one frame.
167
+ */
168
+ export interface CursorFrame {
169
+ images: CursorImage[]
170
+ duration: number
171
+ }
172
+ /**
173
+ * Register an image cursor and return the handle an element's `cursor`
174
+ * property takes. `hotX`/`hotY` is the click point in the 1x image's
175
+ * pixels. Every frame must have the first frame's 1x size. Throws on an
176
+ * empty, mismatched or out-of-bounds payload; a platform that cannot
177
+ * create the cursor logs and shows the default shape for the handle.
178
+ * Handles die with the engine; drop one earlier with `dropCursor`.
179
+ */
180
+ export function createCursor(frames: CursorFrame[], hotX: number, hotY: number): number
181
+ /** Destroy a registered cursor; if it is showing, the default cursor returns. */
182
+ export function dropCursor(id: number): void
155
183
  /** Request that a frame be rendered soon (coalesced by the demand-driven loop). */
156
184
  export function requestFrame(): void
157
185
  /**
package/gui/spatial.d.ts CHANGED
@@ -161,18 +161,62 @@ declare module "flux:spatial" {
161
161
  */
162
162
  export function writeTransform(node: NodeId, transform: Float32Array): void
163
163
  export function setVisible(node: NodeId, visible: boolean): void
164
+ /** What a draw sink writes and where it sorts (bindDraw). */
165
+ export type BindDrawOptions = {
166
+ /** The entry declares `uModel`: the flush writes the node's world
167
+ * matrix there. Default true; false binds the entry for its visibility
168
+ * switch and its sort key alone (a background pinned to a root). */
169
+ params?: boolean
170
+ /** Write `uNormal`, the world matrix's inverse-transpose, too. */
171
+ normal?: boolean
172
+ /** Write the LOD cross-fade band into `uLodFade` (see setLod). */
173
+ fade?: boolean
174
+ /** The instanceCount the entry is switched on with; default 1. */
175
+ count?: number
176
+ /** The sort queue on a sorted target (setDrawSort): 0 background,
177
+ * 1 opaque (the default), 2 cutout, 3 transparent. */
178
+ queue?: number
179
+ /** Ascending above depth inside the queue; default 0. */
180
+ renderOrder?: number
181
+ }
164
182
  /**
165
183
  * Route the node's world matrix to one draw entry's `uModel` (and
166
- * `uNormal`, the inverse-transpose, when `normal`; and with `fade` the
167
- * LOD cross-fade band into `uLodFade`, see setLod). Validated like
184
+ * `uNormal` and `uLodFade` when the options ask). Validated like
168
185
  * setDrawParams: the entry must exist and declare those uniforms. The
169
186
  * entry is assumed switched off (instanceCount 0); the next flush turns it
170
187
  * on with `count` when the node is shown, and off again when hidden.
171
188
  * One draw sink PER TARGET: binding on a target the node already draws
172
189
  * into replaces that sink, binding on another target adds one - a mesh
173
190
  * drawn by a scene and by each of its views is one node with one flush.
191
+ * A node's draw sinks share one sort key: a bind keys every sink of
192
+ * the node with its `queue` and `renderOrder`.
193
+ */
194
+ export function bindDraw(node: NodeId, target: TextureId, draw: DrawId, opts?: BindDrawOptions): void
195
+ /** Re-key the node's draw sinks without a rebind (a renderOrder
196
+ * change): the queue codes of BindDrawOptions, and the renderOrder
197
+ * above depth inside the queue. Sorted targets re-sort at the next
198
+ * flush. */
199
+ export function setDrawKey(node: NodeId, queue: number, renderOrder: number): void
200
+ /**
201
+ * Whether the core orders the target's bound draw entries by queue:
202
+ * background entries first whatever their depth, opaque entries
203
+ * front-to-back by a logarithmic distance bucket of their world box
204
+ * center (four buckets per doubling, so a small camera move changes
205
+ * nothing), cutout entries (an alpha-tested fragment, no early-z) after
206
+ * them the same way, transparent entries last back-to-front by exact
207
+ * depth along the view, `renderOrder` above depth inside each queue,
208
+ * bind order breaking ties.
209
+ * Measured against the target's view (setView), so a target
210
+ * without one waits for it. Every flush that bound, unbound, re-keyed or
211
+ * moved an entry's node, or moved the view past the nearest opaque
212
+ * center's bucket margin, re-keys and issues one setDrawOrder when the
213
+ * permutation changed; entries the core does not bind draw first, before
214
+ * the background queue. Off (the default) writes no order: a shadow tile, an
215
+ * override-material view, a 2d target. Enabling a target already on
216
+ * re-issues its order at the next flush: call it after adding an
217
+ * unbound entry.
174
218
  */
175
- export function bindDraw(node: NodeId, target: TextureId, draw: DrawId, normal: boolean, count: number, fade: boolean): void
219
+ export function setDrawSort(target: TextureId, enabled: boolean): void
176
220
  /** Remove the node's draw sink on `target`, or every draw sink without
177
221
  * one. Issues no write: the entries are the caller's to remove. */
178
222
  export function unbindDraw(node: NodeId, target?: TextureId): void
@@ -212,14 +256,20 @@ declare module "flux:spatial" {
212
256
  * follows the flush; hidden nodes stay in and are skipped at query time. */
213
257
  export function setBounds(node: NodeId, bounds: Float32Array | null): void
214
258
  /**
215
- * The clip volume gating every draw sink on `target`: its view-projection
216
- * (Float32Array of 16, column-major), or null to lift it. An entry whose
217
- * node box (grown by its cull margin) falls wholly outside reads
218
- * instanceCount 0 like a hidden node, and comes back with a fresh params
219
- * write. Nodes without a box, or with culling off, are never gated. Read
220
- * at flush: set it before the flush that should see it.
259
+ * What `target` sees: its view (world to camera) and projection
260
+ * (camera to clip), two Float32Arrays of 16, column-major - or null to
261
+ * forget the target (its view, LOD bias and LOD reference). The core
262
+ * derives everything a target measures with from them: the clip volume
263
+ * gating every draw sink on it (an entry whose node box, grown by its
264
+ * cull margin, falls wholly outside reads instanceCount 0 like a hidden
265
+ * node, and comes back with a fresh params write; nodes without a box,
266
+ * or with culling off, are never gated), the LOD view (eye, forward,
267
+ * vertical focal factor, orthographic or not - setLod) and the view the
268
+ * draw sort measures depth against (setDrawSort). Read at flush: set it
269
+ * before the flush that should see it, on every camera move.
221
270
  */
222
- export function setFrustum(target: TextureId, viewProj: Float32Array | null): void
271
+ export function setView(target: TextureId, view: Float32Array, proj: Float32Array): void
272
+ export function setView(target: TextureId, view: null): void
223
273
  /** Whether frustums gate the node's draw sinks (default true), and the
224
274
  * world-unit margin the test grows its box by (default 0). */
225
275
  export function setCull(node: NodeId, enabled: boolean, margin: number): void
@@ -243,7 +293,7 @@ declare module "flux:spatial" {
243
293
  * `levels` nearest first with strictly descending sizes, all naming a
244
294
  * child or none at all (never mixed); the last level's size is the cull
245
295
  * threshold, 0 for never culled. After the walk each flush measures the
246
- * group on every target with a LOD view (setLodView) - the sphere
296
+ * group on every target with a view (setView) - the sphere
247
297
  * around its own box, else around its levels' world boxes - and gates
248
298
  * the draw sinks under each level to the level that target picked, so
249
299
  * a thousand groups cost no JS per frame. `fade` widens each threshold
@@ -257,18 +307,23 @@ declare module "flux:spatial" {
257
307
  * picked its level. A target with no LOD view draws the first level.
258
308
  */
259
309
  export function setLod(node: NodeId, levels: LodLevel[], fade: number, reference?: TextureId): void
260
- /**
261
- * What `target` measures projected size with: a Float32Array of 6 - the
262
- * eye position xyz, the projection's vertical focal factor (the
263
- * magnitude of `proj[5]`: `1 / tan(fov / 2)` for a perspective
264
- * projection, `2 / (top - bottom)` for an orthographic one - positive,
265
- * whatever clip flip the projection bakes in), 1 for orthographic else
266
- * 0, and a bias every
267
- * measured size is multiplied by (below 1 switches sooner) - or null to
268
- * lift it. Read at flush, like setFrustum; set it beside the frustum on
269
- * every camera move.
270
- */
271
- export function setLodView(target: TextureId, view: Float32Array | null): void
310
+ /** The bias every projected size measured on `target` is multiplied by
311
+ * (default 1; below 1 switches levels sooner, a quality knob). Kept
312
+ * across setView writes; a target measuring by a reference takes the
313
+ * reference's. */
314
+ export function setLodBias(target: TextureId, bias: number): void
315
+ /** The level a LOD group (setLod) draws on `target`: the index into its
316
+ * levels, the level count when culled past the last threshold, null
317
+ * before the target measured it. Throws for a node without levels. A
318
+ * population group's members pick per instance; this is the group's
319
+ * own pick. */
320
+ export function lodLevel(node: NodeId, target: TextureId): number | null
321
+ /** Make `target` measure projected size by `source`'s view instead of
322
+ * its own (null: its own again): a shadow tile culls by its light but
323
+ * draws the level the scene camera sees, so its shadow matches. Only the
324
+ * LOD measurement follows; culling and the draw sort stay the target's
325
+ * own. Set once; a source view move re-measures the target. */
326
+ export function setLodReference(target: TextureId, source: TextureId | null): void
272
327
  /**
273
328
  * A geometry's positions as the core keeps them, one copy shared by
274
329
  * every node that references it: the source of those nodes' local
package/gui/video.d.ts CHANGED
@@ -1,49 +1,162 @@
1
1
  // Video playback (gui-enabled runtime only). The imperative primitive;
2
2
  // @solidrt/core wraps it with SolidJS reactivity. There is no video element:
3
- // the player's `texture` id is displayed with <texture>/<d-texture>, and a
4
- // richer Video component composes in a higher layer.
3
+ // a texture player's `texture` id is displayed with <texture>/<d-texture>,
4
+ // and a richer Video component composes in a higher layer. A plane player
5
+ // (Android) has no texture at all: the platform composites the decoded
6
+ // picture fullscreen beneath the UI. Both players run off the frame loop
7
+ // (demux, decode, the clock, frame selection and audio on their own
8
+ // threads; a texture player's frames are shown at the UI's cadence, the
9
+ // way a browser's compositor shows a video's) and both stream: the source
10
+ // may be a URL, read as it plays.
5
11
 
6
12
  declare module "flux:video" {
13
+ import type { FluxFile } from "flux:fs"
7
14
  import type { TextureId } from "flux:gpu"
8
15
 
9
- /** An opened video: the frame texture plus controls bound to it. */
10
- type VideoPlayer = {
11
- /**
12
- * GPU texture id decoded frames are uploaded into (use as a texture
13
- * source). Holds the current frame; black until playback starts.
14
- */
15
- texture: TextureId
16
- /** Frame width in pixels. */
16
+ /**
17
+ * What went wrong with a stream, for the app to key on:
18
+ * - "network": the source could not be opened or stopped delivering (an
19
+ * unreachable or failing server, an unreadable file, a truncated body);
20
+ * - "decode": the bytes are not a stream the decoders can follow;
21
+ * - "unsupported": a stream, codec or feature this player does not play;
22
+ * - "no-plane": no video plane (not on this platform, or one is open).
23
+ * An app falling back from a plane to a texture keys on "no-plane" only:
24
+ * a network error would fail the texture the same way.
25
+ */
26
+ export type VideoErrorKind = "network" | "decode" | "unsupported" | "no-plane"
27
+
28
+ /** The Error `open` rejects with and `error()` returns. */
29
+ export interface VideoError extends Error {
30
+ kind: VideoErrorKind
31
+ }
32
+
33
+ /** Controls and facts shared by both kinds of player. */
34
+ export type VideoTransport = {
35
+ /** Frame width in pixels, at open. */
17
36
  width: number
18
- /** Frame height in pixels. */
37
+ /** Frame height in pixels, at open. */
19
38
  height: number
20
- /** Duration in seconds. */
21
- duration: number
39
+ /**
40
+ * Duration in seconds; undefined when the source has no known end (a
41
+ * live stream, a container written to a pipe).
42
+ */
43
+ duration: number | undefined
22
44
  /** Whether the file has a playable audio track. */
23
45
  hasAudio: boolean
46
+ /**
47
+ * Whether `seek` does anything. False for a source that cannot be read
48
+ * at an offset (an HTTP response without Range support); `seek` is
49
+ * then a no-op.
50
+ */
51
+ seekable: boolean
24
52
  /** Start or resume playback. */
25
53
  play(): void
26
54
  /** Pause playback (the current frame stays displayed). */
27
55
  pause(): void
56
+ /**
57
+ * Seek to a time in seconds. Lands on the frame at that time (decoding
58
+ * from the preceding keyframe); while paused the target frame is shown.
59
+ * A no-op when `seekable` is false.
60
+ */
61
+ seek(seconds: number): void
28
62
  /** Whether playback is running. */
29
63
  playing(): boolean
30
- /** Presentation time of the displayed frame, in seconds. */
64
+ /**
65
+ * Presentation time of the displayed frame, in seconds: for a texture
66
+ * player the frame on screen (the one the compositor last latched),
67
+ * for a plane the frame last released to the surface.
68
+ */
31
69
  currentTime(): number
32
- /** Whether the last frame has been displayed. */
70
+ /**
71
+ * Whether the last frame has been displayed. A failure does not set
72
+ * it: see `error`.
73
+ */
33
74
  finished(): boolean
34
- /** Stop playback and release the decoder, texture, and audio sink. */
75
+ /**
76
+ * Whether playback is held for the source to catch up: playing, with
77
+ * the picture and the sound paused until enough is read ahead.
78
+ */
79
+ buffering(): boolean
80
+ /**
81
+ * The failure that stopped playback, if one did: the reader could not
82
+ * read on, or the decoder gave up. Playback stops where it was
83
+ * (`playing()` goes false, the last frame stays up); a `seek` retries
84
+ * the source. The same object `failed` resolves with.
85
+ */
86
+ error(): VideoError | undefined
87
+ /**
88
+ * Resolves with the failure that stops playback, once, or with
89
+ * undefined when the player closes without one. What a reactive layer
90
+ * watches instead of polling `error()`.
91
+ */
92
+ failed: Promise<VideoError | undefined>
93
+ /** Stop playback and release everything the player holds. */
35
94
  close(): void
36
95
  }
37
96
 
38
97
  /**
39
- * Open a video file (MP4 with 8-bit 4:2:0 VP9 video, i.e. profile 0;
40
- * AAC audio plays, other audio tracks are ignored). The path resolves
41
- * like file() paths (through the app's assets in a packed app). Playback
42
- * starts paused; call `play()`. Rejects when the file is unreadable or
43
- * its codec unsupported.
98
+ * A texture player: frames are decoded off the frame loop and shown in
99
+ * a GPU texture at the UI's cadence, each at the frame closest to its
100
+ * time.
101
+ */
102
+ export type VideoPlayer = VideoTransport & {
103
+ /**
104
+ * GPU texture id decoded frames land in (use as a texture source).
105
+ * Holds the current frame; black until playback starts.
106
+ */
107
+ texture: TextureId
108
+ }
109
+
110
+ /**
111
+ * A plane player: the decoder renders into the platform's own video
112
+ * surface, fullscreen beneath the UI, on the video's clock. Exists from
113
+ * open until close; one at a time. The UI draws over it wherever it
114
+ * paints, and the window's uncovered pixels show the video. Its audio
115
+ * track plays on the default output, in sync with the picture.
116
+ */
117
+ export type VideoPlane = VideoTransport
118
+
119
+ /**
120
+ * A video source: a path resolved like file() paths (through the app's
121
+ * assets in a packed app), an http: or https: URL, or a file() from
122
+ * flux:fs (a packed asset is read out of the exe). Any other scheme
123
+ * throws. A URL is read by the runtime directly: no disk cache, no
124
+ * dev-server proxying, so the device must reach the host itself.
125
+ */
126
+ export type VideoSource = string | FluxFile
127
+
128
+ export type VideoOpenOptions = {
129
+ /** Where the picture goes: a texture (default) or the platform's video plane. */
130
+ present?: "texture"
131
+ /**
132
+ * Aborting it abandons the open: the promise rejects with the signal's
133
+ * reason and the source is closed.
134
+ */
135
+ signal?: AbortSignal
136
+ }
137
+
138
+ export type VideoPlaneOptions = {
139
+ present: "plane"
140
+ /** How the picture fits the window: letterboxed (default) or cropped to fill. */
141
+ fit?: "contain" | "cover"
142
+ /** Aborting it abandons the open (see VideoOpenOptions). */
143
+ signal?: AbortSignal
144
+ }
145
+
146
+ /**
147
+ * Open a video (WebM with 8-bit 4:2:0 VP9 video, i.e. profile 0; Opus
148
+ * audio plays, other audio tracks are ignored). Playback starts paused;
149
+ * call `play()`. Rejects with a VideoError: "network" when the source
150
+ * cannot be opened (or does not answer within the open timeout),
151
+ * "unsupported" when its codec is not played, "no-plane" for
152
+ * `present: "plane"` on a platform without a video plane (Android only)
153
+ * or while another plane is open or opening. The header is read off the
154
+ * JS thread; the promise resolves once the player (and, for a plane, the
155
+ * plane) exists.
44
156
  *
45
- * Encode with `ffmpeg -c:v libvpx-vp9 -c:a aac out.mp4`; VP9 is
46
- * royalty-free, so the decoder ships on every platform.
157
+ * Encode with `ffmpeg -c:v libvpx-vp9 -c:a libopus out.webm`; VP9 and
158
+ * Opus are royalty-free, so both decoders ship on every platform.
47
159
  */
48
- export function open(path: string): Promise<VideoPlayer>
160
+ export function open(source: VideoSource, options?: VideoOpenOptions): Promise<VideoPlayer>
161
+ export function open(source: VideoSource, options: VideoPlaneOptions): Promise<VideoPlane>
49
162
  }
package/modules/fs.d.ts CHANGED
@@ -36,6 +36,15 @@ declare module "flux:fs" {
36
36
  append(data: string | Uint8Array): Promise<void>
37
37
  /** Remove the file. A missing file is not an error. */
38
38
  remove(): Promise<void>
39
+ /**
40
+ * Move the file to `to` (the OS rename: an existing target is replaced,
41
+ * a missing source rejects, a move across filesystems rejects rather
42
+ * than copying, and `to`'s parent directories are not created). The
43
+ * atomic-write step: write a temporary file, then rename it over the
44
+ * real one. This handle keeps its old `path`; open the new one with
45
+ * `file(to)`.
46
+ */
47
+ rename(to: string): Promise<void>
39
48
  }
40
49
 
41
50
  /**
@@ -77,9 +86,11 @@ declare module "flux:fs" {
77
86
  /**
78
87
  * Reference a file by path. Lazy: no I/O happens until a method is called.
79
88
  *
80
- * Relative paths resolve against the process cwd. Exception: in a SolidRT
81
- * app running an installed version, paths under `assets/` resolve read-only
82
- * into that version's immutable assets tree (writes there error).
89
+ * Relative paths resolve against the process cwd, which in a SolidRT app
90
+ * is the app's per-app persistent storage folder (kept across updates), so
91
+ * `flux.file("notes.json")` is the app's own durable file. Exception: in a
92
+ * SolidRT app running an installed version, paths under `assets/` resolve
93
+ * read-only into that version's immutable assets tree (writes there error).
83
94
  *
84
95
  * @param path Path to the file.
85
96
  */
@@ -38,7 +38,10 @@ declare module "flux:sqlite" {
38
38
  /**
39
39
  * Open a connection to the database at `path`.
40
40
  *
41
- * @param path Database file path.
41
+ * @param path Database file path. A relative path resolves against the
42
+ * process cwd, which in a SolidRT app is the app's per-app persistent
43
+ * storage folder (kept across updates), so `"app.db"` is the app's own
44
+ * durable database.
42
45
  * @param mode Open mode; defaults to "ro".
43
46
  */
44
47
  static open(path: string, mode?: OpenMode): Promise<Database>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidrt/flux-types",
3
- "version": "0.0.59",
3
+ "version": "0.0.61",
4
4
  "license": "MIT",
5
5
  "funding": "https://github.com/sponsors/wellawaretech",
6
6
  "author": "Antoine van Wel",
@@ -1,6 +1,5 @@
1
1
  // Timers, microtask scheduling, and the monotonic clock. flux's timers differ
2
- // from the browser in two ways: the delay is required, and no extra callback
3
- // arguments are forwarded.
2
+ // from the browser in one way: no extra callback arguments are forwarded.
4
3
  //
5
4
  // In a GUI runtime the timers are FRAME-QUANTIZED but WALL-ACCURATE: a
6
5
  // deadline is measured against the real clock from the moment of
@@ -18,17 +17,18 @@
18
17
  // servers) keeps ordinary wall-clock timers.
19
18
 
20
19
  /**
21
- * Run `callback` after at least `ms` milliseconds. Returns a timer id for
20
+ * Run `callback` after at least `ms` milliseconds (default 0, as on the
21
+ * web: the next frame in a GUI runtime). Returns a timer id for
22
22
  * {@link clearTimeout}.
23
23
  */
24
- declare function setTimeout(callback: () => void, ms: number): number
24
+ declare function setTimeout(callback: () => void, ms?: number): number
25
25
  /** Cancel a pending timeout. Unknown or missing ids are ignored. */
26
26
  declare function clearTimeout(id?: number): void
27
27
  /**
28
- * Run `callback` every `ms` milliseconds. Returns a timer id for
29
- * {@link clearInterval}.
28
+ * Run `callback` every `ms` milliseconds (default 0: once per frame in a
29
+ * GUI runtime). Returns a timer id for {@link clearInterval}.
30
30
  */
31
- declare function setInterval(callback: () => void, ms: number): number
31
+ declare function setInterval(callback: () => void, ms?: number): number
32
32
  /** Cancel a running interval. Unknown or missing ids are ignored. */
33
33
  declare function clearInterval(id?: number): void
34
34
  /**