@lumencast/runtime 0.11.0 → 0.12.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.
Files changed (54) hide show
  1. package/README.md +39 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/{broadcast-DtHoU_fS.js → broadcast-Dg77Br5j.js} +3 -3
  4. package/dist/{broadcast-DtHoU_fS.js.map → broadcast-Dg77Br5j.js.map} +1 -1
  5. package/dist/{control-B9frEbNG.js → control-iPqpFp80.js} +4 -4
  6. package/dist/{control-B9frEbNG.js.map → control-iPqpFp80.js.map} +1 -1
  7. package/dist/{index-Dz27r92m.js → index-C4r-fG1q.js} +361 -326
  8. package/dist/index-C4r-fG1q.js.map +1 -0
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.html +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/lumencast.js +1 -1
  14. package/dist/mount.d.ts.map +1 -1
  15. package/dist/mount.js +52 -0
  16. package/dist/mount.js.map +1 -1
  17. package/dist/render/primitives/capture-stream-cache.d.ts +26 -0
  18. package/dist/render/primitives/capture-stream-cache.d.ts.map +1 -0
  19. package/dist/render/primitives/capture-stream-cache.js +141 -0
  20. package/dist/render/primitives/capture-stream-cache.js.map +1 -0
  21. package/dist/render/primitives/capture.d.ts +1 -1
  22. package/dist/render/primitives/capture.d.ts.map +1 -1
  23. package/dist/render/primitives/capture.js +20 -87
  24. package/dist/render/primitives/capture.js.map +1 -1
  25. package/dist/{status-pill-B2vBTwRC.js → status-pill-nls_2xkN.js} +2 -2
  26. package/dist/{status-pill-B2vBTwRC.js.map → status-pill-nls_2xkN.js.map} +1 -1
  27. package/dist/{test-DD2SBDku.js → test-d8HW2LcF.js} +4 -4
  28. package/dist/{test-DD2SBDku.js.map → test-d8HW2LcF.js.map} +1 -1
  29. package/dist/transport/ws.d.ts +4 -1
  30. package/dist/transport/ws.d.ts.map +1 -1
  31. package/dist/transport/ws.js +7 -0
  32. package/dist/transport/ws.js.map +1 -1
  33. package/dist/{tree-CgU_sUwI.js → tree-BME9ba3m.js} +589 -567
  34. package/dist/tree-BME9ba3m.js.map +1 -0
  35. package/dist/types.d.ts +18 -2
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/webrtc/index.d.ts +1 -1
  38. package/dist/webrtc/index.d.ts.map +1 -1
  39. package/dist/webrtc/index.js.map +1 -1
  40. package/dist/webrtc/peer-stream-registry.d.ts +17 -0
  41. package/dist/webrtc/peer-stream-registry.d.ts.map +1 -1
  42. package/dist/webrtc/peer-stream-registry.js +22 -0
  43. package/dist/webrtc/peer-stream-registry.js.map +1 -1
  44. package/package.json +4 -4
  45. package/src/index.ts +1 -0
  46. package/src/mount.ts +54 -0
  47. package/src/render/primitives/capture-stream-cache.ts +164 -0
  48. package/src/render/primitives/capture.tsx +19 -93
  49. package/src/transport/ws.ts +11 -0
  50. package/src/types.ts +18 -2
  51. package/src/webrtc/index.ts +1 -0
  52. package/src/webrtc/peer-stream-registry.ts +38 -0
  53. package/dist/index-Dz27r92m.js.map +0 -1
  54. package/dist/tree-CgU_sUwI.js.map +0 -1
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ErrorCode } from "@lumencast/protocol";
1
+ import type { ErrorCode, SceneRosterEntry } from "@lumencast/protocol";
2
2
  import type { ResolveCaptureDevice } from "./render/primitives/capture";
3
3
  import type { ResolvePeerStream, SubscribePeerStream } from "./render/primitives/media";
4
4
  import type { ReservedCamLeaves } from "./state/reserved-leaves";
@@ -14,7 +14,12 @@ export interface LumencastError {
14
14
  recoverable: boolean;
15
15
  }
16
16
  export interface LumencastMetric {
17
- name: "delta_received" | "delta_applied" | "frame_dropped" | "reconnect" | "snapshot_received" | "scene_changed";
17
+ name: "delta_received" | "delta_applied" | "frame_dropped" | "reconnect" | "snapshot_received" | "scene_changed"
18
+ /** A render bundle was warmed ahead of time from a roster entry (either a
19
+ * `scene_roster` frame or the `preloadRoster` mount option). Emitted once
20
+ * the warm fetch resolves (or from cache). Carries `scene_id` +
21
+ * `scene_version` + `source` ("frame" | "option"). */
22
+ | "roster_preloaded";
18
23
  [key: string]: unknown;
19
24
  }
20
25
  /** Anti-silent-drop render diagnostic (ADR 001 §3.4, issue #34).
@@ -88,6 +93,17 @@ export interface MountOptions {
88
93
  * reads it. Omit it and the reserved leaves are simply not surfaced (the
89
94
  * preview/headless paths are unaffected). */
90
95
  onReservedLeaves?: (leaves: ReservedCamLeaves) => void;
96
+ /** Preload the render bundles of a known scene roster so the FIRST switch to
97
+ * each scene is instant (a warm cache hit instead of a blocking fetch).
98
+ * Each entry is `{ scene_id, scene_version }`. Warmed in the background right
99
+ * after mount — best-effort: a failed warm is swallowed (the scene still
100
+ * fetches on demand at switch time) and never blocks or errors the mount.
101
+ *
102
+ * This is the PUBLIC preload surface (lumencast-js #87b) for hosts that
103
+ * already know the roster at mount time. When the server also emits
104
+ * `scene_roster` frames the runtime warms from those too — both paths feed
105
+ * the same cache and are idempotent (a version is warmed at most once). */
106
+ preloadRoster?: readonly SceneRosterEntry[];
91
107
  }
92
108
  export interface LumencastHandle {
93
109
  /** Tear down the WS, unmount the React tree, release timers. Idempotent. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,CAAC;AAE7D,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,YAAY,GAAG,MAAM,CAAC;AAErE,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,sBAAsB,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EACA,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,WAAW,GACX,mBAAmB,GACnB,eAAe,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;+BAE+B;AAC/B,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,cAAc,CAAC;IACtB,IAAI,EAAE,aAAa,CAAC;IACpB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IACrE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C;;;yDAGqD;IACrD,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACzD;;;;;;;yEAOqE;IACrE,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C;;;;;gFAK4E;IAC5E,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;qEAGiE;IACjE,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C;;;;;;;;;kDAS8C;IAC9C,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;CACxD;AAED,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CAC3C;AAED,YAAY,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,CAAC;AAE7D,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,YAAY,GAAG,MAAM,CAAC;AAErE,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,sBAAsB,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EACA,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,WAAW,GACX,mBAAmB,GACnB,eAAe;IACjB;;;2DAGuD;OACrD,kBAAkB,CAAC;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;+BAE+B;AAC/B,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,cAAc,CAAC;IACtB,IAAI,EAAE,aAAa,CAAC;IACpB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IACrE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C;;;yDAGqD;IACrD,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACzD;;;;;;;yEAOqE;IACrE,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C;;;;;gFAK4E;IAC5E,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;qEAGiE;IACjE,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C;;;;;;;;;kDAS8C;IAC9C,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACvD;;;;;;;;;gFAS4E;IAC5E,aAAa,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CAC3C;AAED,YAAY,EAAE,SAAS,EAAE,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { MeetViewer, type MeetViewerOptions } from "./meet-viewer.js";
2
2
  import { type PeerStreamListener, type PeerStreamRegistry } from "./peer-stream-registry.js";
3
3
  export { MeetViewer, type MeetViewerOptions, type MeetViewerDeps, type PeerInfo, type RemoteTrackEvent, } from "./meet-viewer.js";
4
- export { createPeerStreamRegistry, type PeerStreamRegistry, type PeerStreamListener, } from "./peer-stream-registry.js";
4
+ export { createPeerStreamRegistry, type PeerStreamRegistry, type PeerStreamListener, type RosterListener, } from "./peer-stream-registry.js";
5
5
  export interface PeerViewer {
6
6
  /** Join the room(s) (viewer role, no capture). */
7
7
  join: () => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webrtc/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAyB,MAAM,kBAAkB,CAAC;AAC7F,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,UAAU;IACzB,kDAAkD;IAClD,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,8EAA8E;IAC9E,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,yEAAyE;IACzE,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,WAAW,GAAG,IAAI,CAAC;IAC7D,8EAA8E;IAC9E,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,KAAK,MAAM,IAAI,CAAC;IACrF,mDAAmD;IACnD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B;gDAC4C;IAC5C,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED;;;;mFAImF;AACnF;;;;;;;8BAO8B;AAC9B,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK1C;AA+BD;8EAC8E;AAC9E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAevE;AAED,8DAA8D;AAC9D,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAAG;IAC1D,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB;oFACgF;IAChF,IAAI,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,mBAAoB,SAAQ,UAAU;IACrD;;oEAEgE;IAChE,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAED;;;;;;;;;;2EAU2E;AAC3E,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,0BAA0B,GAClC,mBAAmB,CA8ErB;AAED;;oEAEoE;AACpE,MAAM,MAAM,mBAAmB,GAC3B,0BAA0B,GAC1B,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE1D;;oCAEoC;AACpC,wBAAgB,6BAA6B,CAAC,SAAS,EAAE,mBAAmB,GAAG,mBAAmB,CAYjG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webrtc/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAyB,MAAM,kBAAkB,CAAC;AAC7F,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACpB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,UAAU;IACzB,kDAAkD;IAClD,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,8EAA8E;IAC9E,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,yEAAyE;IACzE,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,WAAW,GAAG,IAAI,CAAC;IAC7D,8EAA8E;IAC9E,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,KAAK,MAAM,IAAI,CAAC;IACrF,mDAAmD;IACnD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B;gDAC4C;IAC5C,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED;;;;mFAImF;AACnF;;;;;;;8BAO8B;AAC9B,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK1C;AA+BD;8EAC8E;AAC9E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAevE;AAED,8DAA8D;AAC9D,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAAG;IAC1D,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB;oFACgF;IAChF,IAAI,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,mBAAoB,SAAQ,UAAU;IACrD;;oEAEgE;IAChE,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAED;;;;;;;;;;2EAU2E;AAC3E,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,0BAA0B,GAClC,mBAAmB,CA8ErB;AAED;;oEAEoE;AACpE,MAAM,MAAM,mBAAmB,GAC3B,0BAA0B,GAC1B,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE1D;;oCAEoC;AACpC,wBAAgB,6BAA6B,CAAC,SAAS,EAAE,mBAAmB,GAAG,mBAAmB,CAYjG"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webrtc/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webrtc/index.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,29 @@
1
1
  export type PeerStreamListener = (stream: MediaStream | null) => void;
2
+ /** Notified whenever the live roster changes ORDER (a peer connects for the first
3
+ * time, or a connected peer drops). A pure re-`set` of an already-present label's
4
+ * stream is NOT a roster change (same arrival order) — it reaches per-label
5
+ * `subscribe` listeners only. Carries no payload : the listener re-reads
6
+ * `orderedLabels()`. */
7
+ export type RosterListener = () => void;
2
8
  export interface PeerStreamRegistry {
3
9
  /** #4 contract — the current stream for a label, or `null` if the peer is not
4
10
  * connected (yet / any more). Synchronous, side-effect free. */
5
11
  resolve(peerLabel: string): MediaStream | null;
12
+ /** The live `peer_label`s in ARRIVAL ORDER (insertion order of the backing Map,
13
+ * restricted to labels that currently hold a stream). Drives positional slot
14
+ * resolution (`@<n>` → `orderedLabels()[n]`, ADR Blue 009 axe 1 positional
15
+ * variant). Returns a fresh array — safe for the caller to keep / index. */
16
+ orderedLabels(): string[];
6
17
  /** Push channel for the LIVE `media` primitive : invoked immediately with the
7
18
  * current value, then on every change for `peerLabel`. Returns an
8
19
  * unsubscribe. */
9
20
  subscribe(peerLabel: string, listener: PeerStreamListener): () => void;
21
+ /** Roster-change channel : invoked whenever a peer connects (new label) or
22
+ * leaves (label dropped) — i.e. whenever `orderedLabels()` could shift. Lets a
23
+ * positional consumer re-resolve `@<n>` when arrivals/departures shuffle the
24
+ * order. NOT invoked on a pure stream replacement of an existing label. Returns
25
+ * an unsubscribe. */
26
+ subscribeRoster(listener: RosterListener): () => void;
10
27
  /** Viewer-side : publish / replace a peer's stream (peer connected). */
11
28
  set(peerLabel: string, stream: MediaStream): void;
12
29
  /** Viewer-side : drop a peer's stream (peer left / connection failed). The
@@ -1 +1 @@
1
- {"version":3,"file":"peer-stream-registry.d.ts","sourceRoot":"","sources":["../../src/webrtc/peer-stream-registry.ts"],"names":[],"mappings":"AAuBA,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;AAEtE,MAAM,WAAW,kBAAkB;IACjC;qEACiE;IACjE,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IAC/C;;uBAEmB;IACnB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI,CAAC;IACvE,wEAAwE;IACxE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAClD;;gDAE4C;IAC5C,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;iBACa;IACb,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,wBAAwB,IAAI,kBAAkB,CAgD7D"}
1
+ {"version":3,"file":"peer-stream-registry.d.ts","sourceRoot":"","sources":["../../src/webrtc/peer-stream-registry.ts"],"names":[],"mappings":"AAuBA,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;AAEtE;;;;yBAIyB;AACzB,MAAM,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC;AAExC,MAAM,WAAW,kBAAkB;IACjC;qEACiE;IACjE,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IAC/C;;;iFAG6E;IAC7E,aAAa,IAAI,MAAM,EAAE,CAAC;IAC1B;;uBAEmB;IACnB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI,CAAC;IACvE;;;;0BAIsB;IACtB,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI,CAAC;IACtD,wEAAwE;IACxE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAClD;;gDAE4C;IAC5C,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;iBACa;IACb,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,wBAAwB,IAAI,kBAAkB,CAoE7D"}
@@ -23,6 +23,7 @@
23
23
  export function createPeerStreamRegistry() {
24
24
  const streams = new Map();
25
25
  const listeners = new Map();
26
+ const rosterListeners = new Set();
26
27
  function notify(peerLabel) {
27
28
  const set = listeners.get(peerLabel);
28
29
  if (set === undefined)
@@ -31,10 +32,25 @@ export function createPeerStreamRegistry() {
31
32
  for (const listener of set)
32
33
  listener(value);
33
34
  }
35
+ function notifyRoster() {
36
+ for (const listener of [...rosterListeners])
37
+ listener();
38
+ }
34
39
  return {
35
40
  resolve(peerLabel) {
36
41
  return streams.get(peerLabel) ?? null;
37
42
  },
43
+ orderedLabels() {
44
+ // Map preserves insertion order = arrival order ; every key holds a stream
45
+ // (a dropped peer is `delete`d in remove()).
46
+ return [...streams.keys()];
47
+ },
48
+ subscribeRoster(listener) {
49
+ rosterListeners.add(listener);
50
+ return () => {
51
+ rosterListeners.delete(listener);
52
+ };
53
+ },
38
54
  subscribe(peerLabel, listener) {
39
55
  let set = listeners.get(peerLabel);
40
56
  if (set === undefined) {
@@ -57,20 +73,26 @@ export function createPeerStreamRegistry() {
57
73
  set(peerLabel, stream) {
58
74
  if (streams.get(peerLabel) === stream)
59
75
  return; // idempotent re-emit guard
76
+ const isNew = !streams.has(peerLabel); // a brand-new arrival shifts the roster
60
77
  streams.set(peerLabel, stream);
61
78
  notify(peerLabel);
79
+ if (isNew)
80
+ notifyRoster();
62
81
  },
63
82
  remove(peerLabel) {
64
83
  if (!streams.has(peerLabel))
65
84
  return;
66
85
  streams.delete(peerLabel);
67
86
  notify(peerLabel);
87
+ notifyRoster(); // a departure shifts every later position
68
88
  },
69
89
  clear() {
70
90
  const labels = [...streams.keys()];
71
91
  streams.clear();
72
92
  for (const label of labels)
73
93
  notify(label);
94
+ if (labels.length > 0)
95
+ notifyRoster();
74
96
  },
75
97
  };
76
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"peer-stream-registry.js","sourceRoot":"","sources":["../../src/webrtc/peer-stream-registry.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,sCAAsC;AACtC,EAAE;AACF,4EAA4E;AAC5E,6EAA6E;AAC7E,8EAA8E;AAC9E,uEAAuE;AACvE,EAAE;AACF,gFAAgF;AAChF,4EAA4E;AAC5E,wEAAwE;AACxE,oEAAoE;AACpE,2EAA2E;AAC3E,2EAA2E;AAC3E,+EAA+E;AAC/E,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,8EAA8E;AAC9E,+EAA+E;AAC/E,+EAA+E;AAC/E,4DAA4D;AAuB5D,MAAM,UAAU,wBAAwB;IACtC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC/C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAmC,CAAC;IAE7D,SAAS,MAAM,CAAC,SAAiB;QAC/B,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACrC,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO;QAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC;QAC7C,KAAK,MAAM,QAAQ,IAAI,GAAG;YAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO;QACL,OAAO,CAAC,SAAS;YACf,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC;QACxC,CAAC;QACD,SAAS,CAAC,SAAS,EAAE,QAAQ;YAC3B,IAAI,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACnC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;gBAChB,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;YAChC,CAAC;YACD,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAClB,oEAAoE;YACpE,8DAA8D;YAC9D,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,CAAC;YACzC,OAAO,GAAG,EAAE;gBACV,MAAM,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBACnC,IAAI,CAAC,KAAK,SAAS;oBAAE,OAAO;gBAC5B,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACnB,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC;oBAAE,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAChD,CAAC,CAAC;QACJ,CAAC;QACD,GAAG,CAAC,SAAS,EAAE,MAAM;YACnB,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,MAAM;gBAAE,OAAO,CAAC,2BAA2B;YAC1E,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YAC/B,MAAM,CAAC,SAAS,CAAC,CAAC;QACpB,CAAC;QACD,MAAM,CAAC,SAAS;YACd,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;gBAAE,OAAO;YACpC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC1B,MAAM,CAAC,SAAS,CAAC,CAAC;QACpB,CAAC;QACD,KAAK;YACH,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YACnC,OAAO,CAAC,KAAK,EAAE,CAAC;YAChB,KAAK,MAAM,KAAK,IAAI,MAAM;gBAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5C,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"peer-stream-registry.js","sourceRoot":"","sources":["../../src/webrtc/peer-stream-registry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumencast/runtime",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "description": "Browser runtime for Lumencast — mount(), LSDP/1 transport, leaf-grain store, LSML render, animations, overlays.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -36,7 +36,7 @@
36
36
  "framer-motion": "^12.0.0",
37
37
  "react": "^19.0.0",
38
38
  "react-dom": "^19.0.0",
39
- "@lumencast/protocol": "0.11.0"
39
+ "@lumencast/protocol": "0.12.1"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@playwright/test": "^1.49.1",
@@ -50,8 +50,8 @@
50
50
  "vite-plugin-dts": "^4.5.0",
51
51
  "vitest": "^4.1.5",
52
52
  "ws": "^8.18.0",
53
- "@lumencast/server": "0.11.0",
54
- "@lumencast/dev-server": "0.11.0"
53
+ "@lumencast/server": "0.12.1",
54
+ "@lumencast/dev-server": "0.12.1"
55
55
  },
56
56
  "scripts": {
57
57
  "dev": "vite",
package/src/index.ts CHANGED
@@ -76,6 +76,7 @@ export {
76
76
  type RemoteTrackEvent,
77
77
  type PeerStreamRegistry,
78
78
  type PeerStreamListener,
79
+ type RosterListener,
79
80
  } from "./webrtc/index.js";
80
81
 
81
82
  // Bundle types are useful for hosts that want to typecheck pre-compiled scenes.
package/src/mount.ts CHANGED
@@ -46,6 +46,11 @@ export function mount(options: MountOptions): LumencastHandle {
46
46
 
47
47
  let active = true;
48
48
 
49
+ // Render-bundle versions already warmed (or warming) by the preload path.
50
+ // Keeps both roster sources (the `scene_roster` frame and the `preloadRoster`
51
+ // mount option) idempotent — a version is fetched by the warmer at most once.
52
+ const warmedVersions = new Set<string>();
53
+
49
54
  // ADR Blue 009 §3.2–3.3 — surface the reserved `__cam.*` LSDP leaves (the
50
55
  // slot→peer assignments + the receive-only viewer creds) to the host so its
51
56
  // WebRTC viewer (Solar) can drive room joins + `x-zab.meet-peer` slot re-keying.
@@ -107,6 +112,13 @@ export function mount(options: MountOptions): LumencastHandle {
107
112
  to: frame.scene_version,
108
113
  });
109
114
  },
115
+ onSceneRoster: (frame) => {
116
+ if (!active) return;
117
+ // The server advertised the show roster (LSDP/1.1 `scene_roster`).
118
+ // Warm every scene's render bundle in the background so the first switch
119
+ // to each is a cache hit, not a blocking fetch.
120
+ warmRoster(frame.entries, "frame");
121
+ },
110
122
  onServerError: (frame) => {
111
123
  reportError({
112
124
  code: frame.code,
@@ -121,6 +133,12 @@ export function mount(options: MountOptions): LumencastHandle {
121
133
 
122
134
  ws.start();
123
135
 
136
+ // Public preload surface (#87b) — warm a host-supplied roster right after
137
+ // mount, before any switch. Same warmer + cache as the `scene_roster` frame.
138
+ if (options.preloadRoster !== undefined && options.preloadRoster.length > 0) {
139
+ warmRoster(options.preloadRoster, "option");
140
+ }
141
+
124
142
  const root: Root = createRoot(options.target);
125
143
  root.render(
126
144
  createElement(LumencastApp, {
@@ -165,6 +183,42 @@ export function mount(options: MountOptions): LumencastHandle {
165
183
 
166
184
  // --- helpers ----------------------------------------------------------
167
185
 
186
+ /**
187
+ * Warm the render bundles for a set of roster entries in the background.
188
+ * Best-effort and non-blocking: each `get()` populates the fetcher's cache
189
+ * keyed by `scene_version`, so the eventual `onSnapshot` fetch for that scene
190
+ * is an instant cache hit. Idempotent via `warmedVersions`; the already-active
191
+ * scene is skipped (its bundle is already loaded or in flight). A failed warm
192
+ * is swallowed and its version released so a later roster can retry — the
193
+ * scene still fetches on demand at switch time.
194
+ */
195
+ function warmRoster(
196
+ entries: readonly { scene_id: string; scene_version: string }[],
197
+ source: "frame" | "option",
198
+ ): void {
199
+ const activeVersion = bundleSignal.value?.scene_version;
200
+ for (const { scene_id, scene_version } of entries) {
201
+ if (scene_version === activeVersion) continue;
202
+ if (warmedVersions.has(scene_version)) continue;
203
+ warmedVersions.add(scene_version);
204
+ void bundleFetcher
205
+ .get(scene_id, scene_version)
206
+ .then(() => {
207
+ if (!active) return;
208
+ options.onMetric?.({
209
+ name: "roster_preloaded",
210
+ scene_id,
211
+ scene_version,
212
+ source,
213
+ });
214
+ })
215
+ .catch(() => {
216
+ // Release so a subsequent roster advertisement can retry the warm.
217
+ warmedVersions.delete(scene_version);
218
+ });
219
+ }
220
+ }
221
+
168
222
  async function onSnapshot(
169
223
  fetcher: BundleFetcher,
170
224
  bSignal: typeof bundleSignal,
@@ -0,0 +1,164 @@
1
+ // Per-device stream cache for `x-zab.capture` ACQUIRE mode.
2
+ //
3
+ // Why this exists: a scene switch remounts the WHOLE render tree
4
+ // (`AnimatePresence` keyed on the scene id in app.tsx / crossfade.tsx), so every
5
+ // `Capture` node unmounts and a fresh one mounts. Without sharing, each switch
6
+ // stops the old node's tracks (RC11) and re-acquires the same physical device in
7
+ // the new node. A real USB webcam reopens near-instantly, but a synthetic
8
+ // DirectShow filter (OBS Virtual Camera) renegotiates slowly → a visible blink
9
+ // on every switch, because — by design (ADR 007 Prism) — every scene references
10
+ // the SAME shared vcam device.
11
+ //
12
+ // The fix mirrors the editor's `use-live-source.ts`: a ref-counted cache keyed by
13
+ // the resolved PHYSICAL device. Two `Capture` nodes on the same device share one
14
+ // `getUserMedia`; tracks stop (RC11) only when the LAST consumer releases. During
15
+ // a crossfade both the exiting and entering scenes are mounted at once
16
+ // (`AnimatePresence mode="sync"`), so the ref-count never reaches 0 across a
17
+ // switch and the device is never renegotiated — no blink.
18
+ //
19
+ // Cache key stability: the key is the resolved `deviceId` / `captureSourceId`, not
20
+ // the raw logical `deviceRef`. Physical ids are salted per origin/partition (see
21
+ // capture.tsx §A1.3), but the salt is constant WITHIN one runtime origin, so the
22
+ // resolved id is stable across scene switches inside a single mount/session — the
23
+ // exact scope over which sharing must hold. Keying on the physical id also
24
+ // correctly de-dupes two distinct `deviceRef`s that resolve to the same device.
25
+
26
+ import type { ResolveCaptureDevice } from "./capture";
27
+
28
+ interface CacheEntry {
29
+ promise: Promise<MediaStream>;
30
+ stream: MediaStream | null;
31
+ refs: number;
32
+ }
33
+
34
+ const cache = new Map<string, CacheEntry>();
35
+
36
+ /** Outcome of a claim: either a shared stream promise (with the key to release
37
+ * later) or PLACEHOLDER — the unknown-kind / declared-but-unresolved-ref path
38
+ * that acquires nothing, exactly as the old inline `acquireStream` returning
39
+ * `null` did. A PLACEHOLDER claim holds NO ref (nothing to release). */
40
+ export type CaptureClaim =
41
+ | { kind: "stream"; key: string; promise: Promise<MediaStream> }
42
+ | { kind: "placeholder" };
43
+
44
+ /** Resolve the device, then claim a shared stream for it (incrementing the
45
+ * ref-count), or return PLACEHOLDER. Awaits the resolver first — physical ids
46
+ * are salted per origin/partition, so the host may re-resolve a portable key
47
+ * (label) against THIS context (capture.tsx §A1.3). A throw from the underlying
48
+ * `getUserMedia` surfaces via the returned promise (caller → PLACEHOLDER). */
49
+ export async function claimCaptureStream(
50
+ sourceKind: string,
51
+ deviceRef: string,
52
+ resolveCaptureDevice: ResolveCaptureDevice | undefined,
53
+ ): Promise<CaptureClaim> {
54
+ const md = navigator.mediaDevices;
55
+ const resolved = (await resolveCaptureDevice?.(deviceRef, sourceKind)) ?? null;
56
+ const deviceId = resolved?.deviceId;
57
+ const captureSourceId = resolved?.captureSourceId;
58
+ const declaredRef = deviceRef.length > 0;
59
+
60
+ // Compute a stable physical key + a lazy acquisition thunk, or bail to
61
+ // PLACEHOLDER for the same reasons the inline switch used to return `null`.
62
+ let physicalId: string;
63
+ let acquire: () => Promise<MediaStream>;
64
+ switch (sourceKind) {
65
+ case "media.webcam":
66
+ case "media.mic":
67
+ case "media.app_audio": {
68
+ // §A1.3 (amended) — NO default-device fallback for a DECLARED deviceRef
69
+ // that did not resolve. Acquiring the host default here is the silent
70
+ // "automatic allocation" of the WRONG camera. → PLACEHOLDER. The bare
71
+ // default constraint stays ONLY when no deviceRef is declared at all.
72
+ if (declaredRef && (typeof deviceId !== "string" || deviceId.length === 0)) {
73
+ return { kind: "placeholder" };
74
+ }
75
+ const channel = sourceKind === "media.webcam" ? "video" : "audio";
76
+ physicalId = typeof deviceId === "string" && deviceId.length > 0 ? deviceId : "default";
77
+ acquire = () => md.getUserMedia({ [channel]: deviceConstraint(deviceId) });
78
+ break;
79
+ }
80
+ case "media.screen":
81
+ case "media.window": {
82
+ // DIRECT capture of the picked desktopCapturer surface (no system picker)
83
+ // via Electron's legacy `chromeMediaSource:desktop` + resolved id.
84
+ if (typeof captureSourceId === "string" && captureSourceId.length > 0) {
85
+ physicalId = captureSourceId;
86
+ acquire = () =>
87
+ md.getUserMedia({
88
+ video: {
89
+ mandatory: {
90
+ chromeMediaSource: "desktop",
91
+ chromeMediaSourceId: captureSourceId,
92
+ },
93
+ } as unknown as MediaTrackConstraints,
94
+ });
95
+ break;
96
+ }
97
+ // A declared surface ref that didn't resolve → PLACEHOLDER, never a
98
+ // default picker. The picker stays only when no ref is declared.
99
+ if (declaredRef) return { kind: "placeholder" };
100
+ physicalId = "display";
101
+ acquire = () => md.getDisplayMedia({ video: true });
102
+ break;
103
+ }
104
+ default:
105
+ return { kind: "placeholder" };
106
+ }
107
+
108
+ const key = `${sourceKind}:${physicalId}`;
109
+ const existing = cache.get(key);
110
+ if (existing) {
111
+ existing.refs += 1;
112
+ return { kind: "stream", key, promise: existing.promise };
113
+ }
114
+ const promise = acquire();
115
+ const entry: CacheEntry = { promise, stream: null, refs: 1 };
116
+ cache.set(key, entry);
117
+ promise
118
+ .then((s) => {
119
+ entry.stream = s;
120
+ })
121
+ .catch(() => {
122
+ // Acquisition failed — evict so a later mount retries instead of sharing a
123
+ // rejected promise. Consumers of this promise all fall back to PLACEHOLDER.
124
+ cache.delete(key);
125
+ });
126
+ return { kind: "stream", key, promise };
127
+ }
128
+
129
+ /** Drop one consumer's claim. Stops every track (RC11 — kill the device light)
130
+ * ONLY when the last consumer releases. A no-op for an already-evicted key
131
+ * (e.g. an acquisition that rejected and self-evicted). */
132
+ export function releaseCaptureStream(key: string): void {
133
+ const entry = cache.get(key);
134
+ if (entry === undefined) return;
135
+ entry.refs -= 1;
136
+ if (entry.refs > 0) return;
137
+ cache.delete(key);
138
+ if (entry.stream !== null) stopStream(entry.stream);
139
+ }
140
+
141
+ /** A `getUserMedia` track constraint. A resolved deviceId is pinned with
142
+ * `exact`, NOT a bare (ideal) deviceId: an *ideal* constraint SILENTLY falls
143
+ * back to the host default camera when the requested device can't start (e.g.
144
+ * an INACTIVE virtual cam enumerated but producing no stream). `exact` yields
145
+ * the requested device (its placeholder frame if idle), or an
146
+ * OverconstrainedError the caller catches into PLACEHOLDER — never the wrong
147
+ * cam. No deviceId → `true` (host default) applies ONLY when no deviceRef was
148
+ * declared. */
149
+ function deviceConstraint(deviceId: string | undefined): MediaTrackConstraints | boolean {
150
+ return typeof deviceId === "string" && deviceId.length > 0
151
+ ? { deviceId: { exact: deviceId } }
152
+ : true;
153
+ }
154
+
155
+ /** Stop every track of a stream (RC11 — release the camera/mic, kill the light). */
156
+ function stopStream(stream: MediaStream): void {
157
+ for (const track of stream.getTracks()) track.stop();
158
+ }
159
+
160
+ /** Test-only: drop all cached entries WITHOUT stopping tracks. Lets a test file
161
+ * start from a clean ref-count without leaking state between cases. */
162
+ export function __resetCaptureStreamCache(): void {
163
+ cache.clear();
164
+ }
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
2
  import type { PrimitiveProps } from "./index";
3
3
  import { useOptionalLumencastRuntime } from "../../overlay/runtime-context";
4
+ import { claimCaptureStream, releaseCaptureStream } from "./capture-stream-cache";
4
5
 
5
6
  /** `x-zab.capture` — context-aware capture primitive (Zab vendor primitive,
6
7
  * RFC-0001 / ADR 004 §Amendment 1).
@@ -62,32 +63,38 @@ export function Capture({ resolved }: PrimitiveProps) {
62
63
  if (!isCaptureCapable()) return;
63
64
 
64
65
  let cancelled = false;
65
- let acquired: MediaStream | null = null;
66
+ // The cache key of the claim we hold, so cleanup releases exactly one ref.
67
+ let claimedKey: string | null = null;
66
68
 
67
69
  void (async () => {
68
70
  try {
69
- const media = await acquireStream(sourceKind, deviceRef, resolveCaptureDevice);
70
- if (media === null) return; // unknown/unsupported kind PLACEHOLDER
71
+ // Claim a per-device shared stream (ref-counted): a scene switch that
72
+ // remounts a Capture on the SAME device reuses the live stream instead
73
+ // of stopping and re-acquiring it — the vcam-blink fix.
74
+ const claim = await claimCaptureStream(sourceKind, deviceRef, resolveCaptureDevice);
75
+ if (claim.kind === "placeholder") return; // unknown/unresolved → PLACEHOLDER
76
+ const media = await claim.promise;
71
77
  if (cancelled) {
72
- // Unmounted (or scene-changed) during acquisition — stop immediately
73
- // so we never leave a camera light on (RC11).
74
- stopStream(media);
78
+ // Unmounted (or scene-changed) during acquisition — drop our claim.
79
+ // The shared stream stays alive if another consumer still holds it,
80
+ // and stops (RC11) only when the last one releases.
81
+ releaseCaptureStream(claim.key);
75
82
  return;
76
83
  }
77
- acquired = media;
84
+ claimedKey = claim.key;
78
85
  setStream(media);
79
86
  } catch {
80
87
  // §A1.2(2)(a) — any acquisition failure (permission denied, no device,
81
88
  // getUserMedia rejected) falls back to PLACEHOLDER, no throw, no
82
- // diagnostic.
89
+ // diagnostic. The rejected cache entry self-evicts; our ref went with it.
83
90
  }
84
91
  })();
85
92
 
86
93
  return () => {
87
94
  cancelled = true;
88
- // RC11 — stop the tracks at unmount / scene change. Clearing state is
89
- // unnecessary (the component is gone) but stopping the device is not.
90
- if (acquired !== null) stopStream(acquired);
95
+ // RC11 — release our claim at unmount / scene change. Tracks stop only
96
+ // when this was the last consumer of the shared stream.
97
+ if (claimedKey !== null) releaseCaptureStream(claimedKey);
91
98
  };
92
99
  // Re-acquire when the logical source identity changes (a scene switch can
93
100
  // reuse the node with a new sourceKind/deviceRef).
@@ -142,7 +149,7 @@ export type ResolvedCaptureDevice = {
142
149
  * hash. MAY be async: physical ids (e.g. getUserMedia `deviceId`) are salted
143
150
  * per origin/partition, so the host often must re-resolve a portable key
144
151
  * (label) against THIS context's devices — an inherently asynchronous step
145
- * (`enumerateDevices`). `acquireStream` awaits it, so the device is bound
152
+ * (`enumerateDevices`). `claimCaptureStream` awaits it, so the device is bound
146
153
  * before acquisition rather than racing a late global mutation. */
147
154
  export type ResolveCaptureDevice = (
148
155
  deviceRef: string,
@@ -165,87 +172,6 @@ function isVisualKind(sourceKind: string): boolean {
165
172
  );
166
173
  }
167
174
 
168
- /** Acquire a live stream for `sourceKind`, applying a host-resolved `deviceId`
169
- * when available. Returns `null` for an unsupported/unknown kind (→
170
- * PLACEHOLDER) ; throws are caught by the caller (→ PLACEHOLDER fallback). */
171
- async function acquireStream(
172
- sourceKind: string,
173
- deviceRef: string,
174
- resolveCaptureDevice: ResolveCaptureDevice | undefined,
175
- ): Promise<MediaStream | null> {
176
- const md = navigator.mediaDevices;
177
-
178
- // §A1.3 (amended 2026-06-27) — AWAIT the resolver: physical ids are salted
179
- // per origin/partition, so the host may need an async re-resolution by a
180
- // portable key (label) in THIS context. Awaiting binds the device before
181
- // acquisition instead of racing a late global update (the previous sync call
182
- // let the node acquire with a stale/absent id first).
183
- const resolved = (await resolveCaptureDevice?.(deviceRef, sourceKind)) ?? null;
184
- const deviceId = resolved?.deviceId;
185
- const declaredRef = deviceRef.length > 0;
186
-
187
- switch (sourceKind) {
188
- case "media.webcam":
189
- case "media.mic":
190
- case "media.app_audio": {
191
- // §A1.3 (amended) — NO default-device fallback for a DECLARED deviceRef
192
- // that did not resolve to a real device. Acquiring the host default cam
193
- // here is the silent "automatic allocation" of the WRONG camera the
194
- // consuming app must never get. → PLACEHOLDER (return null). The bare
195
- // default constraint stays ONLY when no deviceRef is declared at all.
196
- if (declaredRef && (typeof deviceId !== "string" || deviceId.length === 0)) {
197
- return null;
198
- }
199
- const channel = sourceKind === "media.webcam" ? "video" : "audio";
200
- return md.getUserMedia({ [channel]: deviceConstraint(deviceId) });
201
- }
202
- case "media.screen":
203
- case "media.window": {
204
- // DIRECT capture of the picked desktopCapturer surface (no system picker)
205
- // via Electron's legacy `chromeMediaSource:desktop` + the resolved
206
- // `captureSourceId`.
207
- const captureSourceId = resolved?.captureSourceId;
208
- if (typeof captureSourceId === "string" && captureSourceId.length > 0) {
209
- return md.getUserMedia({
210
- video: {
211
- mandatory: {
212
- chromeMediaSource: "desktop",
213
- chromeMediaSourceId: captureSourceId,
214
- },
215
- } as unknown as MediaTrackConstraints,
216
- });
217
- }
218
- // A declared surface ref that didn't resolve → PLACEHOLDER, never a
219
- // default `getDisplayMedia` picker. The picker stays only when no ref is
220
- // declared (a bare capture node on a non-Electron host).
221
- if (declaredRef) return null;
222
- return md.getDisplayMedia({ video: true });
223
- }
224
- default:
225
- return null;
226
- }
227
- }
228
-
229
- /** A `getUserMedia` track constraint. A resolved deviceId is pinned with
230
- * `exact`, NOT a bare (ideal) deviceId: an *ideal* constraint SILENTLY falls
231
- * back to the host default camera when the requested device can't start (e.g.
232
- * an INACTIVE virtual cam that's enumerated but produces no stream) — the
233
- * "automatic allocation" of the WRONG camera. `exact` yields the requested
234
- * device (its placeholder frame if idle), or an OverconstrainedError the
235
- * caller catches into PLACEHOLDER — never the wrong cam. No deviceId → `true`
236
- * (host default) applies ONLY when no deviceRef was declared. */
237
- function deviceConstraint(deviceId: string | undefined): MediaTrackConstraints | boolean {
238
- return typeof deviceId === "string" && deviceId.length > 0
239
- ? { deviceId: { exact: deviceId } }
240
- : true;
241
- }
242
-
243
- /** Stop every track of a stream (RC11 — release the camera/mic, kill the
244
- * device light). */
245
- function stopStream(stream: MediaStream): void {
246
- for (const track of stream.getTracks()) track.stop();
247
- }
248
-
249
175
  /** A render dimension: a finite number → px, a non-empty string → verbatim,
250
176
  * anything else → the fallback (matches the `image` primitive's helper). */
251
177
  function dimOr(v: unknown, fallback: string): string {