@displayxr/inline3d 1.24.0 → 1.25.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/CHANGELOG.md +128 -0
- package/README.md +15 -0
- package/index.d.ts +57 -0
- package/js/inline3d-player.js +2219 -0
- package/js/inline3d-splat-layer-rig.js +341 -125
- package/js/inline3d-splat-playcanvas.js +52 -18
- package/js/inline3d-splat.js +8 -1
- package/js/inline3d.js +208 -12
- package/package.json +7 -1
- package/player.d.ts +273 -0
- package/splat.d.ts +46 -7
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,134 @@ entry points (`.`, `./three`) are frozen for 1.x, while the **scene subpaths** (
|
|
|
5
5
|
`./splat`, `./model`) are a preview tier whose options may change in any release. Entries below say
|
|
6
6
|
which tier they touch, because that is what tells you whether an upgrade can move your pixels.
|
|
7
7
|
|
|
8
|
+
## 1.25.1 — 2026-09-25
|
|
9
|
+
|
|
10
|
+
Touches the **preview tier** (`./splat`, `engine: 'playcanvas'` only). Additive; nothing changes
|
|
11
|
+
for a page that does not call it.
|
|
12
|
+
|
|
13
|
+
- **`handle.setRenderScale(s)`** changes the per-eye buffer scale (the `renderScale` option) live,
|
|
14
|
+
in (0, 4], and resizes the backing store on the next animation frame; **`handle.renderScale`**
|
|
15
|
+
reads it back. For an app whose screens differ: full resolution where the splat is hidden and
|
|
16
|
+
only stage quads draw, a lower scale where a large splat is on screen. Replaces poking the
|
|
17
|
+
viewer's internal field.
|
|
18
|
+
|
|
19
|
+
## 1.25.0 — 2026-09-25
|
|
20
|
+
|
|
21
|
+
Touches the **preview tier** (a new subpath, `./player`) and the **core** (`.`, frozen tier): new
|
|
22
|
+
tracking-state API and an opt-in `untrackedFallback`. No pixel change for a page that does not opt
|
|
23
|
+
in, and none on a browser without the tracking-state surface.
|
|
24
|
+
|
|
25
|
+
### Fixed — `./splat` (`engine: 'playcanvas'`), `setLayerRig`
|
|
26
|
+
|
|
27
|
+
Reported on the panel: layers set to the display rig still looked flat, with no way to tell why. 1.23
|
|
28
|
+
could fail silently; now it cannot:
|
|
29
|
+
|
|
30
|
+
- **Works on both view paths.** The N-camera fallback (one camera per view) used to leave the layer
|
|
31
|
+
on the photo rig with a single warning at the call. It now gets one display camera per view, the
|
|
32
|
+
shear carried in its projection, and measures identical to the RenderView path.
|
|
33
|
+
- **`layerRigState()` says what happened**: `path` ('renderviews' | 'ncamera' | 'mono'), `engaged`
|
|
34
|
+
(the display-rig views were applied on the last frame), `reason` when not (e.g. a layer drawn by
|
|
35
|
+
another camera, a layer not in the composition), `viewerDistance`, `gain`, `planeM`,
|
|
36
|
+
`photoConvergenceM`, `planeOffset`, `located`. The same line is WARNed on the first 3D frame and on
|
|
37
|
+
every change.
|
|
38
|
+
- A display layer that no camera draws is now drawn by the display camera. A layer another camera
|
|
39
|
+
draws is named, not taken.
|
|
40
|
+
- **`planeOffset` / `planeDistance`** choose which stage plane lands on the glass (a stage whose
|
|
41
|
+
"z = 0" is not the photo's convergence plane read as recessed). The change is exact, and the 2D
|
|
42
|
+
picture does not move. **`handle.setLayerRigOptions(opts)`** changes these options live, and
|
|
43
|
+
options now merge across calls (`null` clears a key).
|
|
44
|
+
|
|
45
|
+
### Added — core
|
|
46
|
+
|
|
47
|
+
- **Tracking state: `wall.trackingState` and `on('trackingstatechange', (state, ev) => …)`**, on
|
|
48
|
+
every tile handle too (`'tracking' | 'searching' | 'unknown'`), mirroring the browser's
|
|
49
|
+
`session.trackingState` (DisplayXR Browser patch 0195; browser-pvt#161). Read off the session at
|
|
50
|
+
construction and on every frame, emitted on change only, released to `'unknown'` when the
|
|
51
|
+
session ends. The unsupported shape carries `trackingState: 'unknown'`. A browser without the
|
|
52
|
+
surface (1.0.5 and earlier) reports `'unknown'`, silently. Pages use it for "step back into
|
|
53
|
+
view" hints. (API and tests from #35.)
|
|
54
|
+
- **`createInline3D({ untrackedFallback: 'none' | 'mono' })`**, default `'none'`. `'mono'` eases
|
|
55
|
+
every image/video window to its left eye in both halves of its SBS buffer on `'searching'`, and
|
|
56
|
+
back on `'tracking'`, over the mode switch's duration; `'unknown'` leaves it where it is. No
|
|
57
|
+
buffer reallocation and no layer close, so there is no squeezed-pair flash. Scene windows are
|
|
58
|
+
never touched. It is for **MANUAL** eye-tracking displays, where the app handles tracking loss;
|
|
59
|
+
on **MANAGED** displays (the default, and Leia's) the vendor already goes 2D before it reports
|
|
60
|
+
`'searching'`, so leave it off. Docs: authoring guide § Knowing when nobody is tracked. RFC
|
|
61
|
+
0001 and the player header say where `untrackedFallback` lives again.
|
|
62
|
+
|
|
63
|
+
### Added — player
|
|
64
|
+
|
|
65
|
+
- **`addPlayer(wall, canvas, src, opts)` — a media player module (preview tier).** Built on
|
|
66
|
+
`addVideo`: creates and owns a hidden `<video>`, and for `format:'sbs'` (default) hands it
|
|
67
|
+
straight to `wall.addVideo()`, so the stereo paint, the SBS buffer contract and the
|
|
68
|
+
display-mode fallback are inherited rather than re-implemented. Adds real transport
|
|
69
|
+
(`play/pause/seek/currentTime/duration/volume/muted/setSource/remove`), an `on/off` event pair
|
|
70
|
+
(`play|pause|ended|timeupdate|ready|error`), an SDK-drawn partial-region transport bar
|
|
71
|
+
(`data-inline3d-overlay`, auto-hiding, keyboard-accessible), and a keyboard map
|
|
72
|
+
(Space/K/←/→/J/L/M) scoped to the canvas/transport. `format:'mono'` paints genuinely flat
|
|
73
|
+
content full-frame on a small paint loop of its own — see `js/inline3d-player.js`'s module doc
|
|
74
|
+
comment for why this is a different code path from the unsupported-`wall` fallback, and why
|
|
75
|
+
neither one is the RFC's `untrackedFallback`/`trackingState` mechanism, which does not exist in
|
|
76
|
+
this codebase. v1 is narrower than `docs/rfcs/0001-media-player.md`: no `'tb'`/`'auto'` format
|
|
77
|
+
detection, no `opts.group` one-active-player policy, no `opts.fit`/letterboxing. Sample:
|
|
78
|
+
`samples/player/`.
|
|
79
|
+
- **`setSource()` transitions in `./splat`'s vocabulary.** `transition: 'cut' | 'crossfade'`,
|
|
80
|
+
`durationMs` (600), `easing` (the same named curves, `'easeInOutSine'`), `outgoing: 'frozen'`,
|
|
81
|
+
and `fadeMs` as the legacy alias. Per-call options override the player's own field by field.
|
|
82
|
+
The crossfade dissolves from the outgoing title's last frame through an opt-in mixer canvas (a
|
|
83
|
+
player built without one keeps the plain `addVideo` path). `./splat`'s other transitions,
|
|
84
|
+
unknown easings and `outgoing: 'live'` throw by name.
|
|
85
|
+
- **Transport: title line, ±10 s skip, fullscreen.** `title` (a now-playing line in a bounded top
|
|
86
|
+
band, its own overlay, fading with the bar; `setSource(src, { title })` changes it),
|
|
87
|
+
`skipButtons` (default on, plain rewind / fast-forward glyphs, hidden under 420 px) and `fullscreen` (default on, plus `F`): the
|
|
88
|
+
canvas's container goes fullscreen so the transport comes along, and the canvas is sized to fit
|
|
89
|
+
at its own aspect — never `object-fit`, which would letterbox inside the woven rect. The sample
|
|
90
|
+
gains chapter chips (real `setSource()` crossfades) and an honest caption for the mono tile.
|
|
91
|
+
- **`skin: 'dock'`** — a floating rounded dock, lit round buttons, an accent orb for play, a glowing
|
|
92
|
+
scrub fill and knob, a title pill. `'classic'` stays the default. Every glow is inside its
|
|
93
|
+
overlay's own box: a shadow outside the excluded rect would be woven with the canvas. The sample
|
|
94
|
+
uses `'dock'` (`?skin=classic` for the band) and stacks its tiles at 90% of the window.
|
|
95
|
+
- **Developer customisation: `size: 's' | 'm' | 'l'` and `handle.setAppearance({ accent, size, skin })`**
|
|
96
|
+
(live re-skin). Size is CSS `zoom` on each overlay's content, so icons, fonts and hit targets
|
|
97
|
+
scale together and the centre badge stays centred (measured at all three). Round controls draw
|
|
98
|
+
their own feathered edge (a 1.6 px mask) instead of relying on the border-radius clip, which can
|
|
99
|
+
stair-step where an overlay is re-composited on its own. The sample gains a developer bar
|
|
100
|
+
(accent picker, S/M/L, skin) showing the exact `setAppearance` call.
|
|
101
|
+
- **Docs: the overlay no-glow rule and three media traps** (codec: VP9/AV1 + Opus WebM only, no
|
|
102
|
+
H.264/AAC; non-square-pixel sources play squashed, rewrap with square pixels; black picture with
|
|
103
|
+
working sound = an opaque page background over the woven canvas), and a note that `addPlayer`
|
|
104
|
+
shares `./splat` `setVideo`'s option names (`format`, later `fit`).
|
|
105
|
+
- **`group`: one decoding player at a time.** Players sharing a group take turns: a `play` pauses
|
|
106
|
+
every other member, so a shelf of muted, looping previews decodes one video at a time. Leaving
|
|
107
|
+
on `remove()`.
|
|
108
|
+
- **Controls inside the black bars, and skins `'bars'` / `'call'`** (the Show Spatial team's
|
|
109
|
+
layouts). With a `band`, when the bottom bar is at least 44 CSS px (times `size`), every skin
|
|
110
|
+
puts its controls in it and the title in the top bar; the centre button and key pip stand down,
|
|
111
|
+
so nothing sits over the picture; a thinner bar falls back to the overlay. Re-measured on
|
|
112
|
+
resize. `'bars'`: back / play / elapsed / scrub / remaining in one row (a solid bottom strip
|
|
113
|
+
without a band). `'call'`: name + timer bar on top, controls at the bottom, band 2.39 by default.
|
|
114
|
+
- **Playlist API** (RFC 0001 Addendum A4, for the Show Spatial team's voice commands): `titles`
|
|
115
|
+
(`[{ id, src, title?, poster? }]`, bare sources accepted), `loopList`, `autoAdvance`; on the
|
|
116
|
+
handle `titles`, `current`, `setTitles()`, `play(id)`, `toggle()`, `next()`, `back()` (restart
|
|
117
|
+
past 3 s, else the previous title), and a `'titlechange'` event. Switches go through
|
|
118
|
+
`setSource`, so transitions, the title line and posters follow. `normalizeTitles`,
|
|
119
|
+
`nextIndex` and `backTarget` are pure and tested.
|
|
120
|
+
- **For the Show Spatial team** (quick wins from their list): `format: 'tb'` (top/bottom, repacked
|
|
121
|
+
into the woven pair; `setVideo`'s vocabulary); `band` — a letterbox slot of a target aspect
|
|
122
|
+
(`2.39`, `'2.39:1'`) the picture is fitted into; `posterFormat: 'sbs' | 'tb'` — stereo poster
|
|
123
|
+
stills painted eye by eye; `on('ended')` also fires once if the clip had already ended when the
|
|
124
|
+
listener was attached; `src` / `setSource` take candidates best first and `pickSource()` plays
|
|
125
|
+
the first the browser supports by full `canPlayType` string. Docs: woven-canvas rule 9 (nothing
|
|
126
|
+
with a background above the woven canvas) and a Hosting video section (codec, range requests,
|
|
127
|
+
cache headers, CORS, square pixels).
|
|
128
|
+
- **`fit: 'contain' | 'cover'`** — `./splat` `setVideo`'s names and meaning: contain letterboxes each
|
|
129
|
+
eye (transparent bars), cover fills the tile and crops. Unset keeps the stretch-to-tile pixels.
|
|
130
|
+
On the woven path a small per-eye fit canvas sits after the crossfade mixer (one extra draw per
|
|
131
|
+
frame, only when set); the flat paths fit directly. `fitRect()` is exported and tested.
|
|
132
|
+
- **Named accents** — `accent: 'azure' | 'violet' | 'magenta' | 'sunset' | 'amber' | 'lime' | 'mint' |
|
|
133
|
+
'ice'` (or any CSS colour), exported as `PLAYER_ACCENTS`. Each is light enough for the dock's dark
|
|
134
|
+
play glyph. The sample shows them as swatches.
|
|
135
|
+
|
|
8
136
|
## 1.24.0 — 2026-09-25
|
|
9
137
|
|
|
10
138
|
Touches the **preview tier** (`./splat`, `engine: 'playcanvas'` only). No API change. Pixels change
|
package/README.md
CHANGED
|
@@ -27,6 +27,8 @@ import { addSplat } from '@displayxr/inline3d/splat'; // experimen
|
|
|
27
27
|
import { addModel } from '@displayxr/inline3d/model'; // experimental: glTF/GLB in a tile
|
|
28
28
|
// (Draco / meshopt / KTX2 too —
|
|
29
29
|
// you serve the decoder files)
|
|
30
|
+
import { addPlayer } from '@displayxr/inline3d/player'; // preview: a media player, built
|
|
31
|
+
// on addVideo, with transport
|
|
30
32
|
import { SceneViewer } from '@displayxr/inline3d/viewer'; // experimental: framing + orbit
|
|
31
33
|
```
|
|
32
34
|
|
|
@@ -147,6 +149,15 @@ No projection math lands in your page or in the SDK — the off-axis frustum sta
|
|
|
147
149
|
> OS weave service has bound (typically at page load), a false-negative that silently drops you to 2D.
|
|
148
150
|
> `createInline3D()` detects by actually acquiring a session, which is authoritative.
|
|
149
151
|
|
|
152
|
+
> **Nobody in front of the display?** `wall.trackingState` is `'tracking'`, `'searching'` or
|
|
153
|
+
> `'unknown'`, and `wall.on('trackingstatechange', state => …)` tells you when it moves (tile
|
|
154
|
+
> handles carry both too). `'searching'` means nobody is being tracked: the viewer left the
|
|
155
|
+
> display's 3D zone, or the panel is in a 2D mode. On a MANAGED display (the default, and Leia's)
|
|
156
|
+
> the vendor has already gone 2D by then, so use it for hints only. On a MANUAL display,
|
|
157
|
+
> `createInline3D({ untrackedFallback: 'mono' })` eases `addImage` / `addVideo` windows flat for you.
|
|
158
|
+
> `'unknown'` is "no opinion" (an older browser, or a closed session): render normally. See
|
|
159
|
+
> [knowing when nobody is tracked](docs/authoring-inline-3d.md#knowing-when-nobody-is-tracked).
|
|
160
|
+
|
|
150
161
|
Full API + authoring guidance: [`docs/authoring-inline-3d.md`](docs/authoring-inline-3d.md).
|
|
151
162
|
Before you ship a page that navigates or remounts, read
|
|
152
163
|
[`docs/woven-canvas-rules.md`](docs/woven-canvas-rules.md): how to avoid a raw side-by-side
|
|
@@ -164,6 +175,8 @@ samples/
|
|
|
164
175
|
each woven with one SDK call, all on one session
|
|
165
176
|
splat/ a 3D Gaussian splat in a tile (PlayCanvas; ?engine=spark), with a 2D plate over it
|
|
166
177
|
model/ a glTF mesh, a mesh+splat scene, and a Draco-COMPRESSED glTF (PlayCanvas; ?engine=three)
|
|
178
|
+
player/ a media player — SDK transport (play/pause/seek/mute), one call, built on
|
|
179
|
+
addVideo; default (SBS) and format:'mono' side by side
|
|
167
180
|
composition/ the 14-case 2D/3D overlap matrix — demo AND standing hardware regression
|
|
168
181
|
surface; red cases ship red (see samples/README.md)
|
|
169
182
|
vendor/draco/ three's Draco decoder, served for samples/model (compressed glTF needs it)
|
|
@@ -185,6 +198,8 @@ js/
|
|
|
185
198
|
inline3d-splat-effects.js splat shader effects: the runner, the effect registry (PlayCanvas)
|
|
186
199
|
inline3d-splat-live.js setSource's live outgoing photo: its own camera, layer and target (PlayCanvas)
|
|
187
200
|
from what the asset declares (you serve the decoder files — see the guide)
|
|
201
|
+
inline3d-player.js preview: addPlayer() — a media player window built on addVideo (stereo
|
|
202
|
+
paint, display modes and the fallback are inherited) + an SDK transport bar
|
|
188
203
|
docs/
|
|
189
204
|
authoring-inline-3d.md the authoring guide
|
|
190
205
|
authoring-motion-and-effects.md
|
package/index.d.ts
CHANGED
|
@@ -157,6 +157,24 @@ export interface HardwareDisplayStateChange {
|
|
|
157
157
|
|
|
158
158
|
export type DisplayModeChange = RenderingModeChange | HardwareDisplayStateChange;
|
|
159
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Is anyone being tracked in front of the display? From the DisplayXR runtime (which may hand the
|
|
162
|
+
* decision to the vendor plug-in), via the browser's `session.trackingState` (patch 0195).
|
|
163
|
+
*
|
|
164
|
+
* - `'tracking'` — a viewer is in the display's 3D zone.
|
|
165
|
+
* - `'searching'` — the runtime's derived isTracking is FALSE: nobody is in the zone, **or** the
|
|
166
|
+
* display is in an untracked / 2D mode. It does **not** necessarily mean the tracker lost lock
|
|
167
|
+
* on a viewer who is still there, so do not word a page's UI as if it did.
|
|
168
|
+
* - `'unknown'` — no opinion: this browser has no tracking-state surface, or the session ended.
|
|
169
|
+
*/
|
|
170
|
+
export type XRTrackingState = 'unknown' | 'tracking' | 'searching';
|
|
171
|
+
|
|
172
|
+
/** The tracking state changed. The callback is handed the STATE first, this object second. */
|
|
173
|
+
export interface TrackingStateChange {
|
|
174
|
+
type: 'trackingstatechange';
|
|
175
|
+
state: XRTrackingState;
|
|
176
|
+
}
|
|
177
|
+
|
|
160
178
|
/** What a page may lift into the floating native viewer. `null` = no `XRDisplayLayer.undock`. */
|
|
161
179
|
export interface UndockCapabilities {
|
|
162
180
|
model: boolean;
|
|
@@ -314,9 +332,17 @@ export interface TileHandle {
|
|
|
314
332
|
/** Listen for one display event, re-emitted on this handle. Returns an unsubscribe function. */
|
|
315
333
|
on(type: 'renderingmodechange', cb: (e: RenderingModeChange) => void): () => void;
|
|
316
334
|
on(type: 'hardwaredisplaystatechange', cb: (e: HardwareDisplayStateChange) => void): () => void;
|
|
335
|
+
/**
|
|
336
|
+
* Nobody-is-tracked changes, with the STATE as the first argument. The same subscription as the
|
|
337
|
+
* manager's; silent forever on a browser without the surface. See {@link Inline3D.trackingState}.
|
|
338
|
+
*/
|
|
339
|
+
on(type: 'trackingstatechange', cb: (state: XRTrackingState, e: TrackingStateChange) => void): () => void;
|
|
317
340
|
/** Drop a listener registered with {@link TileHandle.on}. */
|
|
318
341
|
off(type: 'renderingmodechange', cb: (e: RenderingModeChange) => void): void;
|
|
319
342
|
off(type: 'hardwaredisplaystatechange', cb: (e: HardwareDisplayStateChange) => void): void;
|
|
343
|
+
off(type: 'trackingstatechange', cb: (state: XRTrackingState, e: TrackingStateChange) => void): void;
|
|
344
|
+
/** The manager's {@link Inline3D.trackingState}, read live. */
|
|
345
|
+
readonly trackingState: XRTrackingState;
|
|
320
346
|
/**
|
|
321
347
|
* Both display events through one callback — the older shape, still supported. Returns an
|
|
322
348
|
* unsubscribe function; inert (a no-op unsubscribe) on a browser without the API.
|
|
@@ -367,10 +393,19 @@ export interface Inline3D {
|
|
|
367
393
|
setStereoEnabled(enabled: boolean): Promise<boolean>;
|
|
368
394
|
on(type: 'renderingmodechange', cb: (e: RenderingModeChange) => void): () => void;
|
|
369
395
|
on(type: 'hardwaredisplaystatechange', cb: (e: HardwareDisplayStateChange) => void): () => void;
|
|
396
|
+
on(type: 'trackingstatechange', cb: (state: XRTrackingState, e: TrackingStateChange) => void): () => void;
|
|
370
397
|
off(type: 'renderingmodechange', cb: (e: RenderingModeChange) => void): void;
|
|
371
398
|
off(type: 'hardwaredisplaystatechange', cb: (e: HardwareDisplayStateChange) => void): void;
|
|
399
|
+
off(type: 'trackingstatechange', cb: (state: XRTrackingState, e: TrackingStateChange) => void): void;
|
|
372
400
|
/** As last REPORTED by `hardwaredisplaystatechange` — never what was last requested. */
|
|
373
401
|
readonly hardwareDisplayState: XRHardwareDisplayMode | null;
|
|
402
|
+
/**
|
|
403
|
+
* Is anyone being tracked in front of the display right now? Mirrors `session.trackingState` as
|
|
404
|
+
* last read — `'unknown'` where the browser has no such surface (silently), and again once the
|
|
405
|
+
* session ends. Pages use it for "step back into view" hints; see
|
|
406
|
+
* {@link CreateInline3DOptions.untrackedFallback} for having the SDK flatten image/video tiles.
|
|
407
|
+
*/
|
|
408
|
+
readonly trackingState: XRTrackingState;
|
|
374
409
|
/** The active mode as last read/reported. `viewCount: 0` means "not read yet". */
|
|
375
410
|
readonly activeMode: { modeIndex: number; viewCount: number };
|
|
376
411
|
/** True while the SDK is holding every window's rig flat because a 1-view mode is active. */
|
|
@@ -441,6 +476,8 @@ export interface Inline3D {
|
|
|
441
476
|
/** The shape {@link createInline3D} resolves to when inline-3D is unavailable. */
|
|
442
477
|
export interface Inline3DUnsupported {
|
|
443
478
|
supported: false;
|
|
479
|
+
/** Always `'unknown'`: no session, nobody to track. Present so a page need not branch first. */
|
|
480
|
+
trackingState: 'unknown';
|
|
444
481
|
error?: unknown;
|
|
445
482
|
}
|
|
446
483
|
|
|
@@ -470,6 +507,26 @@ export interface CreateInline3DOptions {
|
|
|
470
507
|
autoChrome?: boolean;
|
|
471
508
|
/** The eased 2D<->3D transition. On by default; see {@link ModeSwitchOptions}. */
|
|
472
509
|
modeSwitch?: ModeSwitchOptions;
|
|
510
|
+
/**
|
|
511
|
+
* What the SDK does to the image and video windows it owns the pixels of while **nobody is
|
|
512
|
+
* tracked** (`trackingState === 'searching'`). Default `'none'`.
|
|
513
|
+
*
|
|
514
|
+
* Whose job this is depends on the display's eye-tracking mode:
|
|
515
|
+
* - **MANAGED** (the default, and Leia's): the vendor owns tracking loss. It eases the eyes
|
|
516
|
+
* together and reports `'searching'` only once the display is already 2D. Leave this at
|
|
517
|
+
* `'none'`: a flatten here would be a second transition.
|
|
518
|
+
* - **MANUAL**: the vendor does nothing and the app handles tracking loss. Use `'mono'`.
|
|
519
|
+
*
|
|
520
|
+
* - `'none'` — nothing changes.
|
|
521
|
+
* - `'mono'` — each `addImage` / `addVideo` window eases to its LEFT eye in both halves of its
|
|
522
|
+
* side-by-side buffer on `'searching'`, and back on `'tracking'`, over the mode switch's
|
|
523
|
+
* duration. The buffer is never reallocated and the layer never closed; `'unknown'` leaves it
|
|
524
|
+
* where it is.
|
|
525
|
+
*
|
|
526
|
+
* **Scene windows are never touched** — the page owns those pixels. Listen for
|
|
527
|
+
* `trackingstatechange` and do the same for a scene.
|
|
528
|
+
*/
|
|
529
|
+
untrackedFallback?: 'none' | 'mono';
|
|
473
530
|
}
|
|
474
531
|
|
|
475
532
|
/**
|