@displayxr/inline3d 1.28.0 → 1.29.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/CHANGELOG.md +109 -0
- package/README.md +6 -3
- package/call-full.d.ts +21 -0
- package/call.d.ts +103 -23
- package/dist/call.js +81 -0
- package/index.d.ts +27 -1
- package/js/call/capture.js +47 -2
- package/js/call/disparity.js +432 -0
- package/js/call/element.js +217 -0
- package/js/call/lift.js +9 -0
- package/js/call/signaling.js +7 -202
- package/js/call/wire.js +14 -8
- package/js/inline3d-call-full.js +48 -0
- package/js/inline3d-call.js +222 -16
- package/js/inline3d-player.js +646 -216
- package/js/inline3d-splat-playcanvas.js +46 -3
- package/js/inline3d-splat-video.js +328 -29
- package/js/inline3d.js +130 -1
- package/js/lift/index.js +15 -0
- package/package.json +21 -4
- package/player.d.ts +46 -2
- package/splat.d.ts +38 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,106 @@ 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.29.0 — 2026-09-30
|
|
9
|
+
|
|
10
|
+
Touches the **core** (additive: `sharedInline3D()`, `handle.rewoven()`) and the **preview tier**
|
|
11
|
+
(`./call` — RFC 0003 C1, the one-line path; `./player`, `./splat` `engine: 'playcanvas'`). One
|
|
12
|
+
**removal** in the preview tier: `peerjsCloud`. Otherwise additive; no change for a page that does
|
|
13
|
+
not call the new API.
|
|
14
|
+
|
|
15
|
+
### `./call` — the one-line path (RFC 0003 §1, phase C1)
|
|
16
|
+
|
|
17
|
+
- **`mountCall(el, opts?)`** — `addCall` with every argument optional: the wall is the document's
|
|
18
|
+
shared manager (`sharedInline3D()`, below), signalling defaults to the hosted server, the camera
|
|
19
|
+
to `'auto'`, the SDK chrome to on. `addCall(wall, container, opts)` stays as the explicit form.
|
|
20
|
+
A call's session recovery now re-opens through the shared manager too, so it joins a wall the
|
|
21
|
+
page re-created rather than opening a second session.
|
|
22
|
+
- **`<dxr-call>`** — the same thing as markup, registered by importing `./call` (or `./call/full`,
|
|
23
|
+
or the CDN bundle): mounts on connect, `leave()`s on disconnect (a DOM move IS a teardown —
|
|
24
|
+
woven-canvas rule 2), re-dispatches every call event as a bubbling, composed `CustomEvent`
|
|
25
|
+
named `dxr-call:<event>` with the payload in `detail`, and exposes the handle as `el.call`
|
|
26
|
+
(plus a `ready` promise). Attributes are the string-typed options: `room`, `signaling` (a
|
|
27
|
+
URL), `key`, `camera`, `layout`, `accent`, `max-peers`, `no-ui`, `auto-join`, `mono3d="off"`,
|
|
28
|
+
`no-audio`, `no-self-view`, `no-auto-converge`, `tile-aspect`, `invite-base`, `browser-url`,
|
|
29
|
+
`debug`; anything else is `el.options = { … }`, set before connect (options win). Light DOM, no
|
|
30
|
+
shadow root, **no globals** (RFC 0003 Decision 11). `auto-join` joins during the mount, so the
|
|
31
|
+
element replays `joined` and one `peer` per participant already in the room once it can
|
|
32
|
+
subscribe. Exported: `DxrCallElement`, `defineCallElement`, `attrsToOpts`, `CALL_EVENT_PREFIX`.
|
|
33
|
+
- **`@displayxr/inline3d/call/full`** — `./call` with `mono3D` defaulted to a **statically
|
|
34
|
+
imported** `lift`, the one shape a bundler can follow; importing it also points `<dxr-call>` at
|
|
35
|
+
that `mountCall`. `liftBundled` says whether this copy of the SDK carries the module. **Until
|
|
36
|
+
`./lift` lands on `main`, `js/lift/index.js` is a placeholder** (`lift === null`): the entry
|
|
37
|
+
builds and behaves like `./call`, and `liftBundled` is `false`.
|
|
38
|
+
- **CDN bundle `dist/call.js`** — one pre-bundled ESM file (core + call + element, ~124 KB min,
|
|
39
|
+
no three/PlayCanvas), built by `npm run build:dist` (esbuild) and by `prepack` into every
|
|
40
|
+
published tarball, so jsDelivr serves it as `https://cdn.jsdelivr.net/npm/@displayxr/inline3d@1/dist/call.js`.
|
|
41
|
+
`<script type="module">` only; lift is a lazy sibling chunk (`dist/lift.js`) once the module
|
|
42
|
+
is present. `dist/` is not committed.
|
|
43
|
+
- **`warning` event, code `'lift-not-bundled'`** — a 2D participant is on a 3D display, `mono3D`
|
|
44
|
+
is `'auto'`, and the lift module could not be imported from this build (a bundler that could
|
|
45
|
+
not follow the computed import, or a copy without `./lift`). Emitted once per call with one
|
|
46
|
+
`console.warn` naming the fix (`mono3D: lift` or `./call/full`); the lobby reads "2D→3D
|
|
47
|
+
unavailable in this build". Previously a silent flat tile and an info line. A display that
|
|
48
|
+
honestly cannot lift is unchanged (`handle.mono3D.reason`).
|
|
49
|
+
- **`key`** — `dxrSignaling(url, { key })` / `CallOptions.key` / `<dxr-call key>`: a publishable
|
|
50
|
+
key for the hosted service (RFC 0003 §5a), sent as a query parameter on connect. Accepted and
|
|
51
|
+
forwarded now; the server ignores it until C3.
|
|
52
|
+
- **Removed: `peerjsCloud`** (RFC 0003 Decision 12 — no known external user, so no warning
|
|
53
|
+
release). The demo adapter over the public PeerJS broker lives on as
|
|
54
|
+
`samples/call/peerjs-cloud.js`; the sample's `?signal=peerjs` still works. `dxrSignaling()`
|
|
55
|
+
(the hosted server) has been the default since 1.28.
|
|
56
|
+
- **Types:** `mountCall`, `CallOptions.wall` / `.key`, `CallEvents.warning`, `DxrCallElement`,
|
|
57
|
+
`attrsToOpts`, `defineCallElement`, `DxrCallEventMap` (+ `HTMLElementTagNameMap` /
|
|
58
|
+
`HTMLElementEventMap` augmentations), `call-full.d.ts`.
|
|
59
|
+
- **Tests:** element unit tests (attribute→option table, events, disconnect = leave); a
|
|
60
|
+
headless Chrome ↔ Chrome end-to-end run of `<dxr-call>` (source and CDN bundle) over the
|
|
61
|
+
hosted server (`npm run test:e2e`); Vite/webpack/esbuild smoke builds of `./call/full` and
|
|
62
|
+
`./call` that also run the bundles (`npm run test:bundlers`). Neither is part of `npm test`.
|
|
63
|
+
|
|
64
|
+
### Core — additive
|
|
65
|
+
|
|
66
|
+
- **`sharedInline3D(opts?)`** — the document's shared manager: the live `createInline3D()`
|
|
67
|
+
result if one exists, else a new one (concurrent callers share one creation; an unsupported
|
|
68
|
+
browser is never cached). Woven-canvas rule 1 as an API: a module that needs a wall it did not
|
|
69
|
+
create (`mountCall`, `<dxr-call>`) uses this and never opens a second session next to the
|
|
70
|
+
page's own; a page that made its wall first gets that same wall back here. Semver-covered.
|
|
71
|
+
|
|
72
|
+
- **`./splat` `setVideo` crossfade, band and on-screen rect** (#105, RFC 0001 A5.1-A5.3,
|
|
73
|
+
`engine: 'playcanvas'`). `transition: 'crossfade'` (`durationMs` 600, `easing`
|
|
74
|
+
'easeInOutSine', the player's defaults) dissolves on the GPU from the video already on the plane:
|
|
75
|
+
its quad is frozen and drawn blended over the incoming one, the buffer opaque throughout; from the
|
|
76
|
+
splat and on `setVideo(null)` it stays a cut. `band: 2.39 | '2.39:1'` fits the picture into a
|
|
77
|
+
centred letterbox slot (`cover` fills the band and crops each eye about its centre). The result
|
|
78
|
+
gains `transition`, `band`, `rect` (the picture in CSS px, canvas-relative, clipped) and
|
|
79
|
+
`onRectChange(cb)`. `attachPlayer` now crossfades and takes `band` (controls in the bars) through
|
|
80
|
+
these instead of cutting / ignoring them. **Behaviour change for `attachPlayer(…, { skin: 'call' })`:**
|
|
81
|
+
that skin defaults `band` to 2.39, which surface mode used to ignore; the video now plays in a
|
|
82
|
+
2.39 letterbox. Pass `band: null` to keep the full tile.
|
|
83
|
+
- **`handle.rewoven()`** — `firstWoven`, measured from the call, for a canvas that is already woven
|
|
84
|
+
but whose rect is about to change (fullscreen, a layout resize). Same `{ woven, confirmed,
|
|
85
|
+
reason, ms }` shape, never rejects; a change of the canvas's CSS size or devicePixelRatio while
|
|
86
|
+
pending restarts the hold (checked every frame, every window kind), and it settles anyway with
|
|
87
|
+
the new reason `'hold-capped'` four holds after the call if the size never stops changing;
|
|
88
|
+
before the first join it is `firstWoven`. Woven-canvas rule 11.
|
|
89
|
+
- **Player: `fullscreenCover`** (opt-in, default false). Entering or leaving fullscreen puts an
|
|
90
|
+
opaque cover over the canvas (on top, hard cut, below the controls) until `rewoven()` settles.
|
|
91
|
+
Off by default because a blind A/B on the Leia panel showed no raw side-by-side pair without it
|
|
92
|
+
and ~1.2 s of no picture with it; the flash that did show is browser-wide (rule 11).
|
|
93
|
+
- **Docs:** woven-canvas rules 9–11 (black goes under the canvas — previously only in the
|
|
94
|
+
authoring guide; no `backdrop-filter` over the weave; a resize is a fresh-canvas moment). The
|
|
95
|
+
authoring guide's short list now matches, 1–11.
|
|
96
|
+
- **`attachPlayer(splat, src, opts)`** — surface mode (RFC 0001 Addendum A): the player's
|
|
97
|
+
transport, playlist and events on an existing PlayCanvas splat handle's `setVideo`, with no
|
|
98
|
+
canvas of its own. `detach()` gives the slot back (`setVideo(null)`: scene, pose, lens and rig
|
|
99
|
+
as they were); `'detached'` `{ reason: 'superseded' | 'released' }` when something else takes or
|
|
100
|
+
empties the slot. Cut transitions only for now; `fullscreen` defaults off. Panel-checked on one
|
|
101
|
+
persistent canvas: attach, next/back, detach, scene state identical after.
|
|
102
|
+
- **`addPlayer` refactor:** one player core on a surface adapter. No behaviour change.
|
|
103
|
+
- **`./splat`:** read-only `handle.videoElement` (the `<video>` the plane shows, or null) and
|
|
104
|
+
`handle.canvas`.
|
|
105
|
+
- **Types:** `attachPlayer`, `AttachPlayerOptions`, `AttachedPlayerHandle`, `PlayerDetachedEvent`,
|
|
106
|
+
`'detached'` in `PlayerEvent`; `SplatHandle.videoElement` / `.canvas`.
|
|
107
|
+
|
|
8
108
|
## 1.28.0 — 2026-09-27
|
|
9
109
|
|
|
10
110
|
Touches the **preview tier** (a new subpath, `./call`; the SBS store clamp below) and two **core**
|
|
@@ -39,6 +139,15 @@ clamped to the device's GL limits.
|
|
|
39
139
|
`setRenderScale` still accepts (0, 4]. `SceneViewer` gains `effectiveRenderScale`. Nothing changes
|
|
40
140
|
on a device whose limit holds the store.
|
|
41
141
|
|
|
142
|
+
### Added — call: auto-convergence on the point between the remote person's eyes
|
|
143
|
+
|
|
144
|
+
- SBS tiles now converge by MEASUREMENT: the receiver finds the disparity of the point between the
|
|
145
|
+
remote person's eyes in the decoded frames and shifts each eye by half of it, so that person sits at
|
|
146
|
+
the display plane. No calibration, works for any SBS sender. Tracks a small template after the first
|
|
147
|
+
lock (~1 ms per measurement at 5 Hz). `autoConverge: false` restores the pair as sent; the depth
|
|
148
|
+
slider stays an offset on top; lifted (mono) tiles are untouched. `measureFocusDisparity` is
|
|
149
|
+
exported for pages that want to reuse it.
|
|
150
|
+
|
|
42
151
|
### Fixed — core: image/video buffers capped at the source's per-eye resolution
|
|
43
152
|
|
|
44
153
|
- An image or video window's backing buffer was sized box x devicePixelRatio per eye regardless of
|
package/README.md
CHANGED
|
@@ -199,7 +199,8 @@ js/
|
|
|
199
199
|
inline3d-splat-live.js setSource's live outgoing photo: its own camera, layer and target (PlayCanvas)
|
|
200
200
|
from what the asset declares (you serve the decoder files — see the guide)
|
|
201
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
|
|
202
|
+
paint, display modes and the fallback are inherited) + an SDK transport bar;
|
|
203
|
+
attachPlayer() — the same player on a PlayCanvas splat handle's setVideo
|
|
203
204
|
docs/
|
|
204
205
|
authoring-inline-3d.md the authoring guide
|
|
205
206
|
authoring-motion-and-effects.md
|
|
@@ -207,8 +208,10 @@ docs/
|
|
|
207
208
|
that is not the API
|
|
208
209
|
porting-three-js-apps.md porting an existing three.js app (WebXR or plain) to inline 3D —
|
|
209
210
|
the WebXR→inline-3d mapping table and the whole render loop
|
|
210
|
-
woven-canvas-rules.md never show a raw side-by-side frame: the join, the
|
|
211
|
-
handle.firstWoven, reading the browser's `withheld` log line
|
|
211
|
+
woven-canvas-rules.md never show a raw side-by-side frame: the join, the rules,
|
|
212
|
+
handle.firstWoven / rewoven(), reading the browser's `withheld` log line
|
|
213
|
+
call.md 3D video calls: the <dxr-call> / mountCall() one-line embed, options,
|
|
214
|
+
events, headless, theming, React, self-hosting, troubleshooting
|
|
212
215
|
proposals/ browser-side asks the SDK is waiting on
|
|
213
216
|
```
|
|
214
217
|
|
package/call-full.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Type definitions for @displayxr/inline3d/call/full — the call entry with 2D→3D bundled.
|
|
2
|
+
// PREVIEW tier — not covered by the 1.x semver promise. See docs/sdk-stability.md.
|
|
3
|
+
//
|
|
4
|
+
// Same surface as `./call` (re-exported), except `mountCall` / `addCall` default `mono3D` to a
|
|
5
|
+
// STATICALLY imported `lift` (`./lift`), which is what lets a bundler ship it. Use this entry in a
|
|
6
|
+
// bundled app that wants 2D participants lifted to 3D; use `./call` when you do not want the
|
|
7
|
+
// depth-model plumbing (it then warns `lift-not-bundled` the first time a 2D peer would have been
|
|
8
|
+
// lifted on a 3D display). Importing this entry also points `<dxr-call>` at this `mountCall`.
|
|
9
|
+
|
|
10
|
+
import type { CallHandle, CallOptions } from './call.js';
|
|
11
|
+
|
|
12
|
+
export * from './call.js';
|
|
13
|
+
|
|
14
|
+
/** True when this copy of the SDK carries the lift module; false on a build where `./lift` has not landed yet. */
|
|
15
|
+
export const liftBundled: boolean;
|
|
16
|
+
|
|
17
|
+
/** `mountCall` of `./call` with `mono3D` defaulted to the bundled `lift` (unless you pass your own). */
|
|
18
|
+
export function mountCall(el: HTMLElement, opts?: CallOptions): Promise<CallHandle>;
|
|
19
|
+
|
|
20
|
+
/** `addCall` of `./call` with `mono3D` defaulted to the bundled `lift` (unless you pass your own). */
|
|
21
|
+
export function addCall(wall: unknown, container: HTMLElement, opts?: CallOptions): Promise<CallHandle>;
|
package/call.d.ts
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
// P1 of docs/rfcs/0002-video-call.md: full-mesh WebRTC (<= 4 participants), pluggable signalling,
|
|
5
5
|
// side-by-side stereo from a stereo camera, convergence from `hello` + `hint`, SDK chrome or
|
|
6
6
|
// headless. P2a: mono peers lifted to 3D through `@displayxr/inline3d/lift` (`mono3D`).
|
|
7
|
+
// RFC 0003 C1: `mountCall(el, opts?)` (the one-line path, the wall from `sharedInline3D()`), the
|
|
8
|
+
// `warning` event (`lift-not-bundled`), `key`; `<dxr-call>` is `./call/element`, and
|
|
9
|
+
// `./call/full` is this entry with lift statically imported.
|
|
10
|
+
|
|
11
|
+
import type { Inline3D, Inline3DUnsupported, CreateInline3DOptions } from './index.js';
|
|
7
12
|
|
|
8
13
|
/**
|
|
9
14
|
* The `lift()` of `@displayxr/inline3d/lift`, as the call uses it: `lift(video, { mode: 'live',
|
|
@@ -110,32 +115,24 @@ export interface SignalingSession {
|
|
|
110
115
|
*/
|
|
111
116
|
/** The hosted DisplayXR signalling server (`dxrSignaling()` with no URL). */
|
|
112
117
|
export const DXR_SIGNAL_DEFAULT: string;
|
|
113
|
-
export function dxrSignaling(url?: string, opts?: { WebSocket?: any; pingMs?: number }): SignalingAdapter;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* DEMO ONLY — the free public PeerJS broker. No uptime guarantee and not operated by DisplayXR.
|
|
117
|
-
* `Peer` defaults to `globalThis.Peer`, else the ESM build is imported from jsDelivr.
|
|
118
|
-
*/
|
|
119
|
-
export function peerjsCloud(opts?: {
|
|
120
|
-
Peer?: any;
|
|
121
|
-
peerOptions?: object;
|
|
122
|
-
url?: string;
|
|
123
|
-
/** Upper bound on the join-time wait for slots to say hello (ms, default 5000). */
|
|
124
|
-
helloMs?: number;
|
|
125
|
-
/** A taken slot silent this long is reported unreachable (ms, default 10000). */
|
|
126
|
-
unreachableMs?: number;
|
|
127
|
-
/** Background re-dial interval for silent slots (ms, default 10000). */
|
|
128
|
-
retryMs?: number;
|
|
129
|
-
heartbeatMs?: number;
|
|
130
|
-
settleMs?: number;
|
|
131
|
-
}): SignalingAdapter;
|
|
118
|
+
export function dxrSignaling(url?: string, opts?: { WebSocket?: any; pingMs?: number; /** A publishable key for the hosted service (public by design; sent on connect). */ key?: string }): SignalingAdapter;
|
|
132
119
|
|
|
133
120
|
export type CallAccent = 'azure' | 'violet' | 'magenta' | 'sunset' | 'amber' | 'lime' | 'mint' | 'ice';
|
|
134
121
|
|
|
135
122
|
export interface CallOptions {
|
|
123
|
+
/**
|
|
124
|
+
* The inline-3D manager to put the tiles on. `mountCall` defaults it to the document's shared
|
|
125
|
+
* one (`sharedInline3D()`); `addCall` takes it as its first argument instead.
|
|
126
|
+
*/
|
|
127
|
+
wall?: Inline3D | Inline3DUnsupported | null;
|
|
136
128
|
/** Default `dxrSignaling()` (the hosted DisplayXR server). Or `dxrSignaling(url)` to self-host,
|
|
137
|
-
*
|
|
129
|
+
* or your own adapter. */
|
|
138
130
|
signaling?: SignalingAdapter;
|
|
131
|
+
/**
|
|
132
|
+
* A publishable key for the hosted signalling service (`pk_…`, RFC 0003 §5a). Public by design
|
|
133
|
+
* — it sits in page source — and only used when `signaling` is left at the default.
|
|
134
|
+
*/
|
|
135
|
+
key?: string;
|
|
139
136
|
/**
|
|
140
137
|
* `'auto'` (default): the room in this page's `#room=` fragment, else a new one on join. Or a
|
|
141
138
|
* room id / invite link. Room ids are >= 96 random bits (generated ones: 128), base64url.
|
|
@@ -163,6 +160,13 @@ export interface CallOptions {
|
|
|
163
160
|
rectify?: (stream: MediaStream, info: { width: number; height: number; deviceId: string | null; label: string }) => MediaStream | Promise<MediaStream>;
|
|
164
161
|
/** Microphone (echo cancellation + noise suppression). Default true. */
|
|
165
162
|
audio?: boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Auto-convergence (default true): measure the disparity of the point between each SBS peer's
|
|
165
|
+
* eyes on the received frames and shift the two eyes by half of it each, so the remote person sits
|
|
166
|
+
* at the display plane. No calibration needed. The depth slider stays an offset on top; a peer's
|
|
167
|
+
* `hint` is used only while no measurement has succeeded. Lifted (mono) tiles are unaffected.
|
|
168
|
+
*/
|
|
169
|
+
autoConverge?: boolean;
|
|
166
170
|
/**
|
|
167
171
|
* Lift mono peers to 3D on a 3D display (RFC §5). `'auto'` (default): import
|
|
168
172
|
* `@displayxr/inline3d/lift` lazily from this copy of the SDK, and use it when it reports a
|
|
@@ -205,8 +209,8 @@ export interface CallOptions {
|
|
|
205
209
|
/** Scroll the call block to the top of the viewport once, on join, so every tile (and the self
|
|
206
210
|
* view) is on screen and woven. Default true (only with `ui`). */
|
|
207
211
|
scrollIntoView?: boolean;
|
|
208
|
-
/** `createInline3D()` options
|
|
209
|
-
wallOptions?:
|
|
212
|
+
/** `createInline3D()` options for the wall `mountCall` creates, and for a session re-open. */
|
|
213
|
+
wallOptions?: CreateInline3DOptions;
|
|
210
214
|
debug?: boolean;
|
|
211
215
|
log?: (tag: string, detail: object) => void;
|
|
212
216
|
}
|
|
@@ -229,6 +233,8 @@ export interface CallPeer {
|
|
|
229
233
|
readonly quality: CallQuality | null;
|
|
230
234
|
/** The per-eye convergence shift currently painted, source px. */
|
|
231
235
|
readonly convergencePx: number;
|
|
236
|
+
/** Auto-convergence: measured disparity (source px; null until a lock), mean ms per measurement, last method. */
|
|
237
|
+
readonly autoConverge: { disparityPx: number | null; ms: number | null; readMs: number | null; method: 'mode' | 'focus' | 'track' | null; via: 'videoframe' | 'canvas' | null };
|
|
232
238
|
/** A lifted tile: whether lift is showing yet, native or web, and its current priority. */
|
|
233
239
|
readonly lift: { readonly live: boolean; readonly native: boolean; readonly priority: LiftStreamPriority | null; readonly state: string | null } | null;
|
|
234
240
|
}
|
|
@@ -259,6 +265,16 @@ export interface CallEvents {
|
|
|
259
265
|
speaker: { id: string | null };
|
|
260
266
|
/** Codes include `'camera-busy'`, `'no-camera'`, `'unreachable'` (error.peer = the tile id), `'room-full'`, `'session-ended'`. */
|
|
261
267
|
error: { code: string; message: string; error: Error | null };
|
|
268
|
+
/**
|
|
269
|
+
* Degraded, not broken. `'lift-not-bundled'`: a 2D participant is on a 3D display, `mono3D`
|
|
270
|
+
* is `'auto'`, and the lift module could not be imported from this build (a bundler that could
|
|
271
|
+
* not follow the computed import, or a copy of the SDK without `./lift`) — they stay flat. Fix:
|
|
272
|
+
* `import { lift } from '@displayxr/inline3d/lift'` and pass `mono3D: lift`, or import
|
|
273
|
+
* `@displayxr/inline3d/call/full`. Emitted once per call, the first time such a peer routes,
|
|
274
|
+
* with one `console.warn`. A display that honestly cannot lift is NOT this: that is
|
|
275
|
+
* `handle.mono3D.reason` (`'no-provider'` / `'no-webgpu'`) and an info line.
|
|
276
|
+
*/
|
|
277
|
+
warning: { code: 'lift-not-bundled' | string; message: string };
|
|
262
278
|
state: { id: string; state: PeerState };
|
|
263
279
|
joined: { room: string; id: string };
|
|
264
280
|
left: { room: string };
|
|
@@ -303,7 +319,64 @@ export interface CallHandle {
|
|
|
303
319
|
off<K extends keyof CallEvents>(type: K, cb: (e: CallEvents[K]) => void): void;
|
|
304
320
|
}
|
|
305
321
|
|
|
306
|
-
export function addCall(wall: unknown, container: HTMLElement, opts
|
|
322
|
+
export function addCall(wall: unknown, container: HTMLElement, opts?: CallOptions): Promise<CallHandle>;
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* The one-line path (RFC 0003 §1): `addCall` with every argument optional. The wall is
|
|
326
|
+
* `opts.wall` if given, else the document's shared manager (`sharedInline3D(opts.wallOptions)` —
|
|
327
|
+
* the page's existing session, never a second one). Hosted signalling, `camera: 'auto'` and the
|
|
328
|
+
* SDK chrome by default; resolves once the lobby shows (or, with `autoJoin` / `ui: false`, once
|
|
329
|
+
* joined). `<dxr-call>` (`@displayxr/inline3d/call/element`) is this as markup.
|
|
330
|
+
*/
|
|
331
|
+
export function mountCall(el: HTMLElement, opts?: CallOptions): Promise<CallHandle>;
|
|
332
|
+
|
|
333
|
+
// ── <dxr-call> — the one-line path as markup ───────────────────────────────────────────────
|
|
334
|
+
//
|
|
335
|
+
// Importing this entry (or `./call/full`, or the CDN bundle `dist/call.js`) registers the element.
|
|
336
|
+
// It mounts on connect (`mountCall(this, { ...attrsToOpts(this), ...this.options })`), leaves on
|
|
337
|
+
// disconnect (a DOM move IS a teardown — woven-canvas rule 2), re-dispatches every call event as
|
|
338
|
+
// a bubbling, composed `CustomEvent` named `dxr-call:<event>` with the payload in `detail`, and
|
|
339
|
+
// exposes the handle as `el.call`. Attributes (read once, at connect): `room`, `signaling` (a
|
|
340
|
+
// URL), `key`, `camera`, `layout`, `accent`, `max-peers`, `no-ui`, `auto-join`, `mono3d="off"`,
|
|
341
|
+
// `no-audio`, `no-self-view`, `no-auto-converge`, `tile-aspect`, `invite-base`, `browser-url`,
|
|
342
|
+
// `debug`. Everything else is `el.options`, set before connect; options win over attributes.
|
|
343
|
+
|
|
344
|
+
export class DxrCallElement extends HTMLElement {
|
|
345
|
+
/** The mount seam: null = `mountCall` of `./call`; `./call/full` sets its own. */
|
|
346
|
+
static mount: ((el: HTMLElement, opts?: CallOptions) => Promise<CallHandle>) | null;
|
|
347
|
+
/** Non-string options (a `MediaStream`, an adapter, `lift`, a `wall`) — set BEFORE connecting. */
|
|
348
|
+
options: CallOptions | null;
|
|
349
|
+
/** The handle once mounted; null before, and again after disconnect. */
|
|
350
|
+
readonly call: CallHandle | null;
|
|
351
|
+
/** Resolves with the handle when the mount lands (null if the element left the DOM first); rejects if it failed. */
|
|
352
|
+
readonly ready: Promise<CallHandle | null> | null;
|
|
353
|
+
connectedCallback(): void;
|
|
354
|
+
disconnectedCallback(): void;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** The attribute → option mapping the element applies at connect. Pure; unset attributes contribute nothing. */
|
|
358
|
+
export function attrsToOpts(source: { getAttribute(name: string): string | null } | ((name: string) => string | null)): Partial<CallOptions>;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Register the element (default name `dxr-call`) once. True when this call registered it; false
|
|
362
|
+
* when the name was taken or there is no `customElements` registry. The entries call it for you.
|
|
363
|
+
*/
|
|
364
|
+
export function defineCallElement(name?: string, registry?: CustomElementRegistry): boolean;
|
|
365
|
+
|
|
366
|
+
/** `'dxr-call:'` — every DOM event the element dispatches starts with it. */
|
|
367
|
+
export const CALL_EVENT_PREFIX: 'dxr-call:';
|
|
368
|
+
|
|
369
|
+
/** The DOM events: one per {@link CallEvents} key, plus `dxr-call:ready` once the handle exists. */
|
|
370
|
+
export type DxrCallEventMap = { [K in keyof CallEvents as `dxr-call:${K}`]: CustomEvent<CallEvents[K]> } & {
|
|
371
|
+
'dxr-call:ready': CustomEvent<{ call: CallHandle }>;
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
declare global {
|
|
375
|
+
interface HTMLElementTagNameMap {
|
|
376
|
+
'dxr-call': DxrCallElement;
|
|
377
|
+
}
|
|
378
|
+
interface HTMLElementEventMap extends DxrCallEventMap {}
|
|
379
|
+
}
|
|
307
380
|
|
|
308
381
|
// ── pure helpers (exported for tests and advanced pages) ──────────────────────────────────
|
|
309
382
|
export function normalizeCallOptions(opts?: Partial<CallOptions>): Record<string, unknown>;
|
|
@@ -351,6 +424,13 @@ export interface CallLiftPool {
|
|
|
351
424
|
export function createLiftPool(o: { lift: CallLiftFunction; max?: number; log?: (tag: string, o: object) => void; options?: Record<string, unknown> }): CallLiftPool;
|
|
352
425
|
export function convergenceShiftPx(p: { eyeWidthPx: number; hfovDeg?: number | null; baselineMm?: number | null; subjectZmm?: number | null }): number;
|
|
353
426
|
export function lowPass(prev: number, target: number, alpha?: number): number;
|
|
427
|
+
/** The disparity (left x − right x, input px) of the point between the eyes in a grayscale SBS frame. */
|
|
428
|
+
export function measureFocusDisparity(
|
|
429
|
+
img: ArrayLike<number>,
|
|
430
|
+
W: number,
|
|
431
|
+
H: number,
|
|
432
|
+
o?: { focus?: { x: number; y: number }; block?: number; dMin?: number; dMax?: number; dyMax?: number; minNcc?: number },
|
|
433
|
+
): { d: number; x: number; y: number; c: number; method: 'focus' | 'mode'; blocks: number } | null;
|
|
354
434
|
export function clampShift(px: number, eyeWidthPx: number, maxFraction?: number): number;
|
|
355
435
|
export function eyeCropRect(eyeW: number, eyeH: number, aspect: number, shift: number, eye: 0 | 1): { sx: number; sy: number; sw: number; sh: number };
|
|
356
436
|
export function mirrorSwapOps(W: number, H: number): Array<{ src: 'L' | 'R'; sx: number; sw: number; sy: number; sh: number; dx: number; dw: number; mirror: true }>;
|