@supersoniks/concorde 4.9.97-visual-stack.0 → 4.9.98-visual-stack.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 (171) hide show
  1. package/build-infos.json +1 -1
  2. package/concorde-3d.bundle.js +201 -152
  3. package/concorde-3d.es.js +14621 -13823
  4. package/concorde-core.bundle.js +1 -1
  5. package/concorde-core.es.js +1 -1
  6. package/concorde-hugging-face-infer.bundle.js +1 -1
  7. package/concorde-hugging-face-infer.es.js +1 -1
  8. package/concorde-shader.bundle.js +31 -20
  9. package/concorde-shader.es.js +3236 -2865
  10. package/concorde-webgpu.bundle.js +43 -32
  11. package/concorde-webgpu.es.js +2977 -2618
  12. package/dist/concorde-3d.bundle.js +201 -152
  13. package/dist/concorde-3d.es.js +14621 -13823
  14. package/dist/concorde-core.bundle.js +1 -1
  15. package/dist/concorde-core.es.js +1 -1
  16. package/dist/concorde-hugging-face-infer.bundle.js +1 -1
  17. package/dist/concorde-hugging-face-infer.es.js +1 -1
  18. package/dist/concorde-shader.bundle.js +31 -20
  19. package/dist/concorde-shader.es.js +3236 -2865
  20. package/dist/concorde-webgpu.bundle.js +43 -32
  21. package/dist/concorde-webgpu.es.js +2977 -2618
  22. package/dist/js/core/components/functional/3d/3d.js +100 -2
  23. package/dist/js/core/components/functional/3d/3d.js.map +1 -1
  24. package/dist/js/core/components/functional/3d/constants.js +3 -0
  25. package/dist/js/core/components/functional/3d/constants.js.map +1 -1
  26. package/dist/js/core/components/functional/3d/ensure.js +6 -3
  27. package/dist/js/core/components/functional/3d/ensure.js.map +1 -1
  28. package/dist/js/core/components/functional/3d/form-params.js +2 -0
  29. package/dist/js/core/components/functional/3d/form-params.js.map +1 -1
  30. package/dist/js/core/components/functional/3d/frame-source.js +22 -0
  31. package/dist/js/core/components/functional/3d/frame-source.js.map +1 -0
  32. package/dist/js/core/components/functional/3d/life.js +2 -1
  33. package/dist/js/core/components/functional/3d/life.js.map +1 -1
  34. package/dist/js/core/components/functional/3d/out-publish.js +3 -2
  35. package/dist/js/core/components/functional/3d/out-publish.js.map +1 -1
  36. package/dist/js/core/components/functional/3d/playback.js +6 -1
  37. package/dist/js/core/components/functional/3d/playback.js.map +1 -1
  38. package/dist/js/core/components/functional/3d/post-bind.js +207 -0
  39. package/dist/js/core/components/functional/3d/post-bind.js.map +1 -0
  40. package/dist/js/core/components/functional/3d/post-channels.js +39 -0
  41. package/dist/js/core/components/functional/3d/post-channels.js.map +1 -0
  42. package/dist/js/core/components/functional/3d/post-fx.js +495 -0
  43. package/dist/js/core/components/functional/3d/post-fx.js.map +1 -0
  44. package/dist/js/core/components/functional/3d/props.js +142 -0
  45. package/dist/js/core/components/functional/3d/props.js.map +1 -1
  46. package/dist/js/core/components/functional/3d/reload.js +5 -0
  47. package/dist/js/core/components/functional/3d/reload.js.map +1 -1
  48. package/dist/js/core/components/functional/3d/runtime.js +80 -3
  49. package/dist/js/core/components/functional/3d/runtime.js.map +1 -1
  50. package/dist/js/core/components/functional/3d/scene-runtime.js +1 -1
  51. package/dist/js/core/components/functional/3d/scene-runtime.js.map +1 -1
  52. package/dist/js/core/components/functional/3d/tune.js +21 -0
  53. package/dist/js/core/components/functional/3d/tune.js.map +1 -1
  54. package/dist/js/core/components/functional/3d/updated.js +13 -1
  55. package/dist/js/core/components/functional/3d/updated.js.map +1 -1
  56. package/dist/js/core/components/functional/shader/gl-runtime.js +280 -31
  57. package/dist/js/core/components/functional/shader/gl-runtime.js.map +1 -1
  58. package/dist/js/core/components/functional/shader/shader.js +207 -61
  59. package/dist/js/core/components/functional/shader/shader.js.map +1 -1
  60. package/dist/js/core/components/functional/shader/shadertoy-prelude.js +39 -5
  61. package/dist/js/core/components/functional/shader/shadertoy-prelude.js.map +1 -1
  62. package/dist/js/core/components/functional/shader/types.js +58 -7
  63. package/dist/js/core/components/functional/shader/types.js.map +1 -1
  64. package/dist/js/core/components/functional/webgpu/gl-fallback.js +27 -6
  65. package/dist/js/core/components/functional/webgpu/gl-fallback.js.map +1 -1
  66. package/dist/js/core/components/functional/webgpu/gpu-runtime.js +115 -13
  67. package/dist/js/core/components/functional/webgpu/gpu-runtime.js.map +1 -1
  68. package/dist/js/core/components/functional/webgpu/helpers.js +8 -2
  69. package/dist/js/core/components/functional/webgpu/helpers.js.map +1 -1
  70. package/dist/js/core/components/functional/webgpu/webgpu.js +43 -1
  71. package/dist/js/core/components/functional/webgpu/webgpu.js.map +1 -1
  72. package/dist/js/core/utils/mediaRef.js +86 -0
  73. package/dist/js/core/utils/mediaRef.js.map +1 -1
  74. package/dist/types/core/components/functional/3d/3d.d.ts +31 -3
  75. package/dist/types/core/components/functional/3d/3d.d.ts.map +1 -1
  76. package/dist/types/core/components/functional/3d/constants.d.ts +1 -1
  77. package/dist/types/core/components/functional/3d/constants.d.ts.map +1 -1
  78. package/dist/types/core/components/functional/3d/ensure.d.ts +3 -0
  79. package/dist/types/core/components/functional/3d/ensure.d.ts.map +1 -1
  80. package/dist/types/core/components/functional/3d/form-params.d.ts.map +1 -1
  81. package/dist/types/core/components/functional/3d/frame-source.d.ts +23 -0
  82. package/dist/types/core/components/functional/3d/frame-source.d.ts.map +1 -0
  83. package/dist/types/core/components/functional/3d/life.d.ts +2 -0
  84. package/dist/types/core/components/functional/3d/life.d.ts.map +1 -1
  85. package/dist/types/core/components/functional/3d/out-publish.d.ts +1 -0
  86. package/dist/types/core/components/functional/3d/out-publish.d.ts.map +1 -1
  87. package/dist/types/core/components/functional/3d/playback.d.ts +9 -0
  88. package/dist/types/core/components/functional/3d/playback.d.ts.map +1 -1
  89. package/dist/types/core/components/functional/3d/post-bind.d.ts +52 -0
  90. package/dist/types/core/components/functional/3d/post-bind.d.ts.map +1 -0
  91. package/dist/types/core/components/functional/3d/post-channels.d.ts +40 -0
  92. package/dist/types/core/components/functional/3d/post-channels.d.ts.map +1 -0
  93. package/dist/types/core/components/functional/3d/post-fx.d.ts +73 -0
  94. package/dist/types/core/components/functional/3d/post-fx.d.ts.map +1 -0
  95. package/dist/types/core/components/functional/3d/props.d.ts +42 -0
  96. package/dist/types/core/components/functional/3d/props.d.ts.map +1 -1
  97. package/dist/types/core/components/functional/3d/reload.d.ts +3 -0
  98. package/dist/types/core/components/functional/3d/reload.d.ts.map +1 -1
  99. package/dist/types/core/components/functional/3d/runtime.d.ts +16 -1
  100. package/dist/types/core/components/functional/3d/runtime.d.ts.map +1 -1
  101. package/dist/types/core/components/functional/3d/scene-runtime.d.ts +1 -1
  102. package/dist/types/core/components/functional/3d/scene-runtime.d.ts.map +1 -1
  103. package/dist/types/core/components/functional/3d/tune.d.ts +5 -0
  104. package/dist/types/core/components/functional/3d/tune.d.ts.map +1 -1
  105. package/dist/types/core/components/functional/3d/types.d.ts +16 -0
  106. package/dist/types/core/components/functional/3d/types.d.ts.map +1 -1
  107. package/dist/types/core/components/functional/3d/updated.d.ts.map +1 -1
  108. package/dist/types/core/components/functional/shader/gl-runtime.d.ts +53 -2
  109. package/dist/types/core/components/functional/shader/gl-runtime.d.ts.map +1 -1
  110. package/dist/types/core/components/functional/shader/shader.d.ts +41 -9
  111. package/dist/types/core/components/functional/shader/shader.d.ts.map +1 -1
  112. package/dist/types/core/components/functional/shader/shadertoy-prelude.d.ts +16 -2
  113. package/dist/types/core/components/functional/shader/shadertoy-prelude.d.ts.map +1 -1
  114. package/dist/types/core/components/functional/shader/types.d.ts +35 -5
  115. package/dist/types/core/components/functional/shader/types.d.ts.map +1 -1
  116. package/dist/types/core/components/functional/webgpu/gl-fallback.d.ts +4 -0
  117. package/dist/types/core/components/functional/webgpu/gl-fallback.d.ts.map +1 -1
  118. package/dist/types/core/components/functional/webgpu/gpu-runtime.d.ts +14 -0
  119. package/dist/types/core/components/functional/webgpu/gpu-runtime.d.ts.map +1 -1
  120. package/dist/types/core/components/functional/webgpu/helpers.d.ts +1 -0
  121. package/dist/types/core/components/functional/webgpu/helpers.d.ts.map +1 -1
  122. package/dist/types/core/components/functional/webgpu/webgpu.d.ts +11 -1
  123. package/dist/types/core/components/functional/webgpu/webgpu.d.ts.map +1 -1
  124. package/dist/types/core/utils/mediaRef.d.ts +34 -0
  125. package/dist/types/core/utils/mediaRef.d.ts.map +1 -1
  126. package/package.json +49 -1
  127. package/src/core/components/functional/3d/3d.md +46 -3
  128. package/src/core/components/functional/3d/3d.spec.ts +42 -0
  129. package/src/core/components/functional/3d/3d.ts +112 -3
  130. package/src/core/components/functional/3d/constants.ts +3 -0
  131. package/src/core/components/functional/3d/ensure.ts +11 -3
  132. package/src/core/components/functional/3d/form-params.ts +2 -0
  133. package/src/core/components/functional/3d/frame-source.ts +30 -0
  134. package/src/core/components/functional/3d/life.ts +4 -1
  135. package/src/core/components/functional/3d/out-publish.ts +4 -3
  136. package/src/core/components/functional/3d/playback.ts +13 -1
  137. package/src/core/components/functional/3d/post-bind.ts +262 -0
  138. package/src/core/components/functional/3d/post-channels.spec.ts +36 -0
  139. package/src/core/components/functional/3d/post-channels.ts +69 -0
  140. package/src/core/components/functional/3d/post-fx.ts +594 -0
  141. package/src/core/components/functional/3d/props.ts +45 -0
  142. package/src/core/components/functional/3d/reload.ts +8 -0
  143. package/src/core/components/functional/3d/runtime.ts +93 -4
  144. package/src/core/components/functional/3d/scene-runtime.ts +3 -0
  145. package/src/core/components/functional/3d/tune.ts +21 -0
  146. package/src/core/components/functional/3d/types.ts +14 -0
  147. package/src/core/components/functional/3d/updated.ts +17 -1
  148. package/src/core/components/functional/shader/gl-runtime.ts +374 -36
  149. package/src/core/components/functional/shader/shader.md +36 -11
  150. package/src/core/components/functional/shader/shader.spec.ts +350 -3
  151. package/src/core/components/functional/shader/shader.ts +236 -59
  152. package/src/core/components/functional/shader/shadertoy-prelude.ts +49 -5
  153. package/src/core/components/functional/shader/types.ts +92 -13
  154. package/src/core/components/functional/webgpu/gl-fallback.ts +28 -4
  155. package/src/core/components/functional/webgpu/gpu-runtime.ts +154 -18
  156. package/src/core/components/functional/webgpu/helpers.ts +8 -2
  157. package/src/core/components/functional/webgpu/webgpu.md +13 -2
  158. package/src/core/components/functional/webgpu/webgpu.spec.ts +37 -0
  159. package/src/core/components/functional/webgpu/webgpu.ts +48 -1
  160. package/src/core/utils/mediaRef.spec.ts +61 -0
  161. package/src/core/utils/mediaRef.ts +108 -0
  162. package/src/docs/_misc/visual-stack.md +37 -3
  163. package/src/docs/components/docs-demo-sources.ts +42 -0
  164. package/src/docs/docs.ts +3 -0
  165. package/src/docs/example/docs-3d-post-depth-demo.ts +68 -0
  166. package/src/docs/example/docs-3d-shader-live-demo.ts +98 -0
  167. package/src/docs/example/docs-3d-webgpu-live-demo.ts +83 -0
  168. package/src/docs/example/docs-harris-webgpu-demo.ts +53 -55
  169. package/src/docs/search/docs-search.json +2583 -863
  170. package/src/tsconfig.json +36 -0
  171. package/src/tsconfig.tsbuildinfo +1 -1
@@ -4,6 +4,7 @@
4
4
  * - `url` : http(s) | blob: | data: (préféré pour les canaux shader)
5
5
  * - `blob` : binaire (HF, upload) — le producteur publie aussi `url` quand possible
6
6
  * - `blobUrl` : alias historique (extract shader)
7
+ * - `element` : sélecteur `#id` ou Element (pont live → canaux shader, ignoré par toMediaUrl)
7
8
  */
8
9
  export type SonicMediaRef = {
9
10
  url?: string;
@@ -12,7 +13,40 @@ export type SonicMediaRef = {
12
13
  width?: number;
13
14
  height?: number;
14
15
  mime?: string;
16
+ /** Sélecteur `#id` ou référence Element (upload live, pas d’URL). */
17
+ element?: string | Element;
15
18
  };
19
+ /**
20
+ * Contrat minimal pour qu’un composant Concorde alimente un canal shader
21
+ * sans JPEG / blob (`channel0="#plateau"` ou `{ element }`).
22
+ */
23
+ export type SonicFrameSource = {
24
+ /** Canvas lisible (preserveDrawingBuffer ou 2D). `null` tant que pas prêt. */
25
+ getFrameCanvas(): HTMLCanvasElement | null;
26
+ /** Incrémenté après chaque rendu effectif. */
27
+ readonly frameSeq: number;
28
+ };
29
+ /**
30
+ * Producteur de frames live qui peut rester actif hors viewport
31
+ * tant qu’un consommateur est enregistré (`sonic-3d` Lot 9).
32
+ */
33
+ export type SonicFrameConsumerHost = {
34
+ registerFrameConsumer(token?: object): void;
35
+ unregisterFrameConsumer(token?: object): void;
36
+ };
37
+ export declare function isFrameSource(el: unknown): el is SonicFrameSource;
38
+ export declare function isFrameConsumerHost(el: unknown): el is SonicFrameConsumerHost;
39
+ /** Suit les `#id` résolus → register/unregister auprès du producteur. */
40
+ export declare class FrameConsumerRegistry {
41
+ private tokens;
42
+ sync(elements: Iterable<Element | null | undefined>): void;
43
+ clear(): void;
44
+ }
45
+ /** Enregistre un Element sans id → clé `#__sonic_frame_N` (réutilisée). */
46
+ export declare function registerLiveElement(el: Element): string;
47
+ export declare function lookupLiveElement(key: string): Element | null;
48
+ /** Normalise `element` DP → clé canal (`#id` ou live). */
49
+ export declare function mediaElementKey(value: unknown): string | null;
16
50
  /** Objet image sérialisé (sortie HF / extract) : Blob + dimensions. */
17
51
  export declare function isMediaBlobObject(value: unknown): value is SonicMediaRef & {
18
52
  blob: Blob;
@@ -1 +1 @@
1
- {"version":3,"file":"mediaRef.d.ts","sourceRoot":"","sources":["../../../../src/core/utils/mediaRef.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,uEAAuE;AACvE,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,aAAa,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAWzC;AAaD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAMxD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAqBT;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAQ5D;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA6B/B"}
1
+ {"version":3,"file":"mediaRef.d.ts","sourceRoot":"","sources":["../../../../src/core/utils/mediaRef.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,8EAA8E;IAC9E,cAAc,IAAI,iBAAiB,GAAG,IAAI,CAAC;IAC3C,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,qBAAqB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,uBAAuB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/C,CAAC;AAEF,wBAAgB,aAAa,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,gBAAgB,CAMjE;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,sBAAsB,CAO7E;AAED,yEAAyE;AACzE,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAA8B;IAE5C,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAkBnD,KAAK;CAGN;AAQD,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,OAAO,GAAG,MAAM,CAOvD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAQ7D;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAY7D;AAED,uEAAuE;AACvE,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,aAAa,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAWzC;AAaD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAMxD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAqBT;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAQ5D;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA6B/B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supersoniks/concorde",
3
- "version": "4.9.97-visual-stack.0",
3
+ "version": "4.9.98-visual-stack.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "",
@@ -178,6 +178,18 @@
178
178
  "types": "./dist/types/core/components/functional/3d/frame-capture.d.ts",
179
179
  "default": "./dist/js/core/components/functional/3d/frame-capture.js"
180
180
  },
181
+ "./frame-source": {
182
+ "types": "./dist/types/core/components/functional/3d/frame-source.d.ts",
183
+ "default": "./dist/js/core/components/functional/3d/frame-source.js"
184
+ },
185
+ "./functional/frame-source": {
186
+ "types": "./dist/types/core/components/functional/3d/frame-source.d.ts",
187
+ "default": "./dist/js/core/components/functional/3d/frame-source.js"
188
+ },
189
+ "./functional/3d/frame-source": {
190
+ "types": "./dist/types/core/components/functional/3d/frame-source.d.ts",
191
+ "default": "./dist/js/core/components/functional/3d/frame-source.js"
192
+ },
181
193
  "./gl": {
182
194
  "types": "./dist/types/core/components/functional/3d/gl.d.ts",
183
195
  "default": "./dist/js/core/components/functional/3d/gl.js"
@@ -310,6 +322,42 @@
310
322
  "types": "./dist/types/core/components/functional/3d/pointer.d.ts",
311
323
  "default": "./dist/js/core/components/functional/3d/pointer.js"
312
324
  },
325
+ "./post-bind": {
326
+ "types": "./dist/types/core/components/functional/3d/post-bind.d.ts",
327
+ "default": "./dist/js/core/components/functional/3d/post-bind.js"
328
+ },
329
+ "./functional/post-bind": {
330
+ "types": "./dist/types/core/components/functional/3d/post-bind.d.ts",
331
+ "default": "./dist/js/core/components/functional/3d/post-bind.js"
332
+ },
333
+ "./functional/3d/post-bind": {
334
+ "types": "./dist/types/core/components/functional/3d/post-bind.d.ts",
335
+ "default": "./dist/js/core/components/functional/3d/post-bind.js"
336
+ },
337
+ "./post-channels": {
338
+ "types": "./dist/types/core/components/functional/3d/post-channels.d.ts",
339
+ "default": "./dist/js/core/components/functional/3d/post-channels.js"
340
+ },
341
+ "./functional/post-channels": {
342
+ "types": "./dist/types/core/components/functional/3d/post-channels.d.ts",
343
+ "default": "./dist/js/core/components/functional/3d/post-channels.js"
344
+ },
345
+ "./functional/3d/post-channels": {
346
+ "types": "./dist/types/core/components/functional/3d/post-channels.d.ts",
347
+ "default": "./dist/js/core/components/functional/3d/post-channels.js"
348
+ },
349
+ "./post-fx": {
350
+ "types": "./dist/types/core/components/functional/3d/post-fx.d.ts",
351
+ "default": "./dist/js/core/components/functional/3d/post-fx.js"
352
+ },
353
+ "./functional/post-fx": {
354
+ "types": "./dist/types/core/components/functional/3d/post-fx.d.ts",
355
+ "default": "./dist/js/core/components/functional/3d/post-fx.js"
356
+ },
357
+ "./functional/3d/post-fx": {
358
+ "types": "./dist/types/core/components/functional/3d/post-fx.d.ts",
359
+ "default": "./dist/js/core/components/functional/3d/post-fx.js"
360
+ },
313
361
  "./props": {
314
362
  "types": "./dist/types/core/components/functional/3d/props.d.ts",
315
363
  "default": "./dist/js/core/components/functional/3d/props.js"
@@ -85,7 +85,9 @@ Le viewer lit `asset` ou `src` depuis le DP — la source (fetch, upload, HF) n
85
85
  | `fit` | Fit bbox après load (défaut true) |
86
86
  | `pick` | Survol highlight + clic → event `pick` + `hit*` ; double-clic = fit |
87
87
  | `play` / `play-on-hover` | Boucle RAF |
88
- | `release-offscreen` | Dispose renderer hors viewport (défaut true) |
88
+ | `play-offscreen` | Garde RAF / runtime hors viewport (pont live `#id`) |
89
+ | `release-offscreen` | Dispose renderer hors viewport (défaut true) sauf si `play-offscreen` / consommateur live |
90
+ | `damping` | Inertie OrbitControls (défaut `0.08`, `0` = off) |
89
91
  | `dpr-max` | Cap devicePixelRatio (défaut 2) |
90
92
  | `dataProvider` / `out-data-provider` | Entrée / sortie |
91
93
  | `out-interval` | Throttle publication ms (défaut 80) |
@@ -109,10 +111,51 @@ Le viewer lit `asset` ou `src` depuis le DP — la source (fetch, upload, HF) n
109
111
 
110
112
  ## Shader bridging
111
113
 
112
- No shared WebGL context between `sonic-3d` and `sonic-shader`. Bridge: `frame-out` publishes `frameUrl` + `snapshot` (`SonicMediaRef`) to the DP → shader channels.
114
+ No shared WebGL context between `sonic-3d` and `sonic-shader`.
113
115
 
114
- **Note:** each new snapshot **revokes** the previous ObjectURL. To freeze a frame (HF / shader), clone with `cloneMediaRef` — otherwise `Failed to load texture: blob:…`.
116
+ | Need | Bridge |
117
+ |------|--------|
118
+ | **Live post** | `channel0="#plateau"` on `sonic-shader` (or DP `{ element }`) — canvas copy each frame via `SonicFrameSource` |
119
+ | **Export / HF / freeze** | `frame-out` → `frameUrl` + `snapshot` (`SonicMediaRef` JPEG). Clone with `cloneMediaRef` before holding a frame |
115
120
 
116
121
  ```html
122
+ <!-- Live -->
123
+ <sonic-3d id="plateau" play auto-rotate …></sonic-3d>
124
+ <sonic-shader channel0="#plateau" image="…" play></sonic-shader>
125
+
126
+ <!-- Export / freeze -->
117
127
  <sonic-3d frame-out out-data-provider="view3d" snapshot-interval="250" …></sonic-3d>
118
128
  ```
129
+
130
+ Demo: [Visual stack — 3D → shader live](#docs/_misc/visual-stack.md/visual-stack).
131
+
132
+ Un `sonic-shader` / `sonic-webgpu` qui pointe `#id` s’enregistre via `registerFrameConsumer` : le 3D **continue hors viewport** (pas de dispose). Option explicite : `play-offscreen`.
133
+
134
+ ### Arrêt net
135
+
136
+ `el.stopMotion()` coupe `autoRotate` et l’inertie OrbitControls. Quand la caméra est immobile depuis ~8 frames : event `rest` + champ out `moving: 0|1`. Attribut `damping` (défaut `0.08`, `0` = off).
137
+
138
+ ## Post FX (in-viewer)
139
+
140
+ When `post-image` is set, the scene renders to an offscreen target with a **depth texture**, then ShaderToy-style passes run inside the same Three context (`post-buffer-a`…`post-image`). Same GLSL as `sonic-shader` (`mainImage`, `iChannel0–3`, `uParam0–3`, `sonicLinearDepth`).
141
+
142
+ | Channel token | Source |
143
+ |---------------|--------|
144
+ | `scene` (default ch0) | Color render target |
145
+ | `depth` | Depth texture from the scene pass |
146
+ | `buffer-a`…`d` | Ping-pong buffers |
147
+ | URL / `#id` | External texture (same as shader live bridge) |
148
+
149
+ ```html
150
+ <sonic-3d
151
+ post-image="…GLSL…"
152
+ post-image-ch0="scene"
153
+ post-image-ch1="depth"
154
+ param0="0.5"
155
+ post-scale="1"
156
+ ></sonic-3d>
157
+ ```
158
+
159
+ Demo: [Visual stack — post depth edges](#docs/_misc/visual-stack.md/visual-stack).
160
+
161
+ Pick / orbit still use scene geometry; `frame-out` captures the **final** canvas after post.
@@ -169,3 +169,45 @@ describe("orbit.wheelAffectsZoom", () => {
169
169
  expect(orbit.wheelAffectsZoom(atMaxZoom, 1)).toBe(true);
170
170
  });
171
171
  });
172
+
173
+ describe("wantsOffscreenPlay / frame consumers", () => {
174
+ it("playOffscreen ou consumers → true", async () => {
175
+ const { wantsOffscreenPlay } = await import("./playback");
176
+ expect(wantsOffscreenPlay({ playOffscreen: false, frameConsumerCount: 0 })).toBe(
177
+ false,
178
+ );
179
+ expect(wantsOffscreenPlay({ playOffscreen: true, frameConsumerCount: 0 })).toBe(
180
+ true,
181
+ );
182
+ expect(wantsOffscreenPlay({ playOffscreen: false, frameConsumerCount: 1 })).toBe(
183
+ true,
184
+ );
185
+ });
186
+
187
+ it("registerFrameConsumer incrémente le compteur", async () => {
188
+ const el = TestUtils.bootstrap(
189
+ `<sonic-3d style="width:64px;height:64px"></sonic-3d>`,
190
+ true,
191
+ )[0] as Sonic3d;
192
+ await el.updateComplete;
193
+ expect(el.frameConsumerCount).toBe(0);
194
+ const token = {};
195
+ el.registerFrameConsumer(token);
196
+ expect(el.frameConsumerCount).toBe(1);
197
+ expect(el.playOffscreen).toBe(false);
198
+ el.unregisterFrameConsumer(token);
199
+ expect(el.frameConsumerCount).toBe(0);
200
+ });
201
+
202
+ it("stopMotion coupe autoRotate", async () => {
203
+ const el = TestUtils.bootstrap(
204
+ `<sonic-3d auto-rotate style="width:64px;height:64px"></sonic-3d>`,
205
+ true,
206
+ )[0] as Sonic3d;
207
+ await el.updateComplete;
208
+ expect(el.autoRotate).toBe(true);
209
+ el.stopMotion();
210
+ expect(el.autoRotate).toBe(false);
211
+ expect(el.moving).toBe(0);
212
+ });
213
+ });
@@ -1,9 +1,11 @@
1
1
  import { html } from "lit";
2
2
  import { customElement, state } from "lit/decorators.js";
3
3
  import { PublisherManager } from "@supersoniks/concorde/utils";
4
+ import { ensure } from "./ensure";
4
5
  import { formBind, type FormOrbit } from "./form-bind";
5
6
  import { life } from "./life";
6
7
  import { outPublish } from "./out-publish";
8
+ import { postBind } from "./post-bind";
7
9
  import type { SceneHit } from "./pick";
8
10
  import { playback } from "./playback";
9
11
  import { pointer } from "./pointer";
@@ -13,6 +15,8 @@ import { sonic3dStyles } from "./styles";
13
15
  import { propUpdate } from "./updated";
14
16
  import { wire } from "./wire";
15
17
 
18
+ /** Frames sans mouvement avant `rest` / `moving=0`. */
19
+ const REST_STILL_FRAMES = 8;
16
20
  /**
17
21
  * Viewer 3D Concorde (Three.js) — glTF/GLB, caméra orbit/perspective/ortho,
18
22
  * outputs dataProvider paresseux. Opt-in : `@supersoniks/concorde/3d`.
@@ -21,7 +25,7 @@ import { wire } from "./wire";
21
25
  export class Sonic3d extends Sonic3dProps {
22
26
  static styles = sonic3dStyles;
23
27
 
24
- @state() private errorMessage = "";
28
+ @state() errorMessage = "";
25
29
 
26
30
  /** État runtime partagé avec les helpers (lu ici pour noUnusedLocals). */
27
31
  runtime: SceneRuntime | null = null;
@@ -46,7 +50,22 @@ export class Sonic3d extends Sonic3dProps {
46
50
  snapshotBusy = false;
47
51
  frameUrls: string[] = [];
48
52
  snapshotSeq = 0;
53
+ /** Contrat SonicFrameSource — incrémenté après chaque render. */
54
+ frameSeq = 0;
55
+ lastDrawAt = 0;
49
56
  lastFormOrbit: FormOrbit | null = null;
57
+ /** Consommateurs live (`registerFrameConsumer`). */
58
+ private frameConsumers = new Set<object>();
59
+ /** 1 si caméra en mouvement, 0 sinon (publié en out). */
60
+ moving = 0;
61
+ private stillFrames = 0;
62
+ private lastMotionYaw = NaN;
63
+ private lastMotionPitch = NaN;
64
+ private lastMotionDist = NaN;
65
+
66
+ get frameConsumerCount() {
67
+ return this.frameConsumers.size;
68
+ }
50
69
 
51
70
  onFormMutation = () => {
52
71
  const data = (this.formPublisher?.get() ?? {}) as Record<string, unknown>;
@@ -135,8 +154,93 @@ export class Sonic3d extends Sonic3dProps {
135
154
  private drawFrame(now: number) {
136
155
  if (!this.runtime || !this.ready) return;
137
156
  this.frame++;
157
+ const time = (now - this.startTime) / 1000;
158
+ const timeDelta = Math.max(0, (now - this.lastDrawAt) / 1000);
159
+ this.lastDrawAt = now;
160
+ postBind.syncFrame(this as never, time, timeDelta, this.frame);
138
161
  this.runtime.render();
139
- outPublish.maybePublish(this as never, now, (now - this.startTime) / 1000);
162
+ this.frameSeq++;
163
+ this.trackMotion(time);
164
+ outPublish.maybePublish(this as never, now, time);
165
+ }
166
+
167
+ /**
168
+ * Enregistre un consommateur live (`sonic-shader` / `sonic-webgpu` via `#id`).
169
+ * Garde le RAF hors viewport tant qu’il reste au moins un token.
170
+ */
171
+ registerFrameConsumer(token: object = {}) {
172
+ this.frameConsumers.add(token);
173
+ ensure.syncVisibility(this as never);
174
+ }
175
+
176
+ unregisterFrameConsumer(token: object = {}) {
177
+ this.frameConsumers.delete(token);
178
+ ensure.syncVisibility(this as never);
179
+ }
180
+
181
+ /**
182
+ * Coupe `autoRotate` + inertie OrbitControls.
183
+ * Émet `rest` si la caméra était en mouvement.
184
+ */
185
+ stopMotion() {
186
+ this.autoRotate = false;
187
+ this.runtime?.stopMotion();
188
+ this.stillFrames = REST_STILL_FRAMES;
189
+ if (this.moving !== 0) {
190
+ this.moving = 0;
191
+ this.dispatchEvent(
192
+ new CustomEvent("rest", {
193
+ detail: { moving: 0 },
194
+ bubbles: true,
195
+ composed: true,
196
+ }),
197
+ );
198
+ }
199
+ this.syncPlayback();
200
+ }
201
+
202
+ private trackMotion(time: number) {
203
+ if (!this.runtime) return;
204
+ const snap = this.runtime.snapshot({
205
+ time,
206
+ frame: this.frame,
207
+ hovering: this.hovering ? 1 : 0,
208
+ pointerX: this.pointerX,
209
+ pointerY: this.pointerY,
210
+ hit: this.lastHit,
211
+ });
212
+ const eps = 1e-5;
213
+ const still =
214
+ Number.isFinite(this.lastMotionYaw) &&
215
+ Math.abs(snap.yaw - this.lastMotionYaw) < eps &&
216
+ Math.abs(snap.pitch - this.lastMotionPitch) < eps &&
217
+ Math.abs(snap.distance - this.lastMotionDist) < eps &&
218
+ !this.autoRotate;
219
+ this.lastMotionYaw = snap.yaw;
220
+ this.lastMotionPitch = snap.pitch;
221
+ this.lastMotionDist = snap.distance;
222
+ if (still) {
223
+ this.stillFrames++;
224
+ if (this.stillFrames >= REST_STILL_FRAMES && this.moving !== 0) {
225
+ this.moving = 0;
226
+ this.dispatchEvent(
227
+ new CustomEvent("rest", {
228
+ detail: { moving: 0, yaw: snap.yaw, pitch: snap.pitch },
229
+ bubbles: true,
230
+ composed: true,
231
+ }),
232
+ );
233
+ }
234
+ } else {
235
+ this.stillFrames = 0;
236
+ this.moving = 1;
237
+ }
238
+ }
239
+
240
+ /** Contrat SonicFrameSource — canvas Three (preserveDrawingBuffer). */
241
+ getFrameCanvas(): HTMLCanvasElement | null {
242
+ if (!this.runtime || !this.ready) return null;
243
+ return this.runtime.renderer.domElement;
140
244
  }
141
245
 
142
246
  onPointerMove = (e: PointerEvent) => pointer.onMove(this as never, e);
@@ -161,12 +265,17 @@ export type {
161
265
  Sonic3dConfig,
162
266
  Sonic3dOutput,
163
267
  } from "./types";
164
- export type { SonicMediaRef } from "@supersoniks/concorde/core/utils/mediaRef";
268
+ export type { SonicMediaRef, SonicFrameSource, SonicFrameConsumerHost } from "@supersoniks/concorde/core/utils/mediaRef";
165
269
  export {
166
270
  toMediaUrl,
167
271
  cloneMediaRef,
272
+ isFrameSource,
273
+ isFrameConsumerHost,
274
+ FrameConsumerRegistry,
168
275
  } from "@supersoniks/concorde/core/utils/mediaRef";
276
+ export { wantsOffscreenPlay } from "./playback";
169
277
  export { frameCapture } from "./frame-capture";
278
+ export { frameSource } from "./frame-source";
170
279
 
171
280
  declare global {
172
281
  interface HTMLElementTagNameMap {
@@ -93,6 +93,9 @@ export const OUT_CONSUMER_KEYS = [
93
93
  /** Canvas → ObjectURL (SonicMediaRef) pour chaînage shader. */
94
94
  "frameUrl",
95
95
  "snapshot",
96
+ /** Pont live → sonic-shader (`{ element }`), publié une fois au ready. */
97
+ "frameSource",
98
+ "moving",
96
99
  ] as const;
97
100
 
98
101
  export type Sonic3dFormControlKey = (typeof FORM_CONTROL_KEYS)[number];
@@ -1,6 +1,6 @@
1
1
  import type { Sonic3dCameraMode } from "./constants";
2
2
  import { parse } from "./parse";
3
- import { playback } from "./playback";
3
+ import { playback, wantsOffscreenPlay } from "./playback";
4
4
  import { reload } from "./reload";
5
5
  import { SceneRuntime } from "./scene-runtime";
6
6
 
@@ -16,10 +16,13 @@ export type EnsureHost = {
16
16
  maxDistance: number;
17
17
  minPolar: number;
18
18
  maxPolar: number;
19
+ damping: number;
19
20
  loadToken: number;
20
21
  ready: boolean;
21
22
  inView: boolean;
22
23
  releaseOffscreen: boolean;
24
+ playOffscreen: boolean;
25
+ frameConsumerCount: number;
23
26
  getBoundingClientRect: () => DOMRect;
24
27
  clientWidth: number;
25
28
  clientHeight: number;
@@ -52,6 +55,7 @@ async function ensureRuntime(host: EnsureHost) {
52
55
  minPolar: reload.degToRad(host.minPolar),
53
56
  maxPolar: reload.degToRad(host.maxPolar),
54
57
  });
58
+ host.runtime.setDamping(host.damping);
55
59
  reload.applyOrbitLimits(host as never);
56
60
  syncSize(host);
57
61
  await reload.run(host as never);
@@ -71,11 +75,15 @@ function releaseRuntime(host: EnsureHost) {
71
75
  }
72
76
 
73
77
  function syncVisibility(host: EnsureHost) {
74
- if (!host.inView && host.releaseOffscreen) {
78
+ const keepOffscreen = wantsOffscreenPlay(host);
79
+ if (!host.inView && host.releaseOffscreen && !keepOffscreen) {
75
80
  releaseRuntime(host);
76
81
  return;
77
82
  }
78
- if (host.inView && (!host.runtime || host.runtime.isLost)) {
83
+ if (
84
+ (!host.runtime || host.runtime.isLost) &&
85
+ (host.inView || keepOffscreen || !host.releaseOffscreen)
86
+ ) {
79
87
  void ensureRuntime(host);
80
88
  return;
81
89
  }
@@ -2,6 +2,7 @@ import { ORBIT_CHANGE_EPSILON } from "./constants";
2
2
  import { formFields } from "./form-fields";
3
3
  import { parse } from "./parse";
4
4
  import { playback } from "./playback";
5
+ import { postBind } from "./post-bind";
5
6
  import { reload } from "./reload";
6
7
  import type { SceneRuntime } from "./runtime";
7
8
  import type { Sonic3dAsset, Sonic3dCameraMode } from "./types";
@@ -91,6 +92,7 @@ function applyFormParams(host: FormBindHost, data: Record<string, unknown>) {
91
92
 
92
93
  formFields.pushToRuntime(host, data);
93
94
  applyFormOrbit(host, data);
95
+ postBind.applyFormPost(host as never, data);
94
96
  if (needReload) void reload.run(host as never);
95
97
  }
96
98
 
@@ -0,0 +1,30 @@
1
+ import { publisherFieldHasListener } from "./publisher-listen";
2
+
3
+ type OutPublisherLike = {
4
+ _proxies_?: Map<string, { hasListener?: () => boolean }>;
5
+ frameSource?: { set: (v: unknown) => void };
6
+ };
7
+
8
+ export type FrameSourceHost = {
9
+ outPublisher: OutPublisherLike | null;
10
+ bindOutPublisher: () => void;
11
+ ready: boolean;
12
+ };
13
+
14
+ /**
15
+ * Publie `{ element: host }` une fois au ready si un lecteur écoute `frameSource`.
16
+ * Aucun coût pendant l’animation.
17
+ */
18
+ function maybePublish(host: FrameSourceHost & Element) {
19
+ if (!host.ready) return;
20
+ if (!host.outPublisher) host.bindOutPublisher();
21
+ const pub = host.outPublisher;
22
+ if (!pub || !publisherFieldHasListener(pub, "frameSource")) return;
23
+ try {
24
+ pub.frameSource?.set({ element: host });
25
+ } catch {
26
+ /* leaf manquant */
27
+ }
28
+ }
29
+
30
+ export const frameSource = { maybePublish } as const;
@@ -1,6 +1,7 @@
1
1
  import { PublisherManager } from "@supersoniks/concorde/utils";
2
2
  import { ensure } from "./ensure";
3
3
  import { formBind } from "./form-bind";
4
+ import { wantsOffscreenPlay } from "./playback";
4
5
  import type { SceneRuntime } from "./runtime";
5
6
 
6
7
  /** Host Lit pour cycle de vie / observers / bind DP. */
@@ -9,6 +10,8 @@ export type LifeHost = {
9
10
  dataProvider: string;
10
11
  outDataProvider: string;
11
12
  releaseOffscreen: boolean;
13
+ playOffscreen: boolean;
14
+ frameConsumerCount: number;
12
15
  inView: boolean;
13
16
  runtime: SceneRuntime | null;
14
17
  camera: string;
@@ -86,7 +89,7 @@ function firstUpdated(host: LifeHost) {
86
89
  bindFormProvider(host);
87
90
  bindOutPublisher(host);
88
91
  requestAnimationFrame(() => {
89
- if (host.inView || !host.releaseOffscreen) {
92
+ if (host.inView || !host.releaseOffscreen || wantsOffscreenPlay(host)) {
90
93
  void ensure.ensureRuntime(host as never);
91
94
  }
92
95
  });
@@ -33,6 +33,7 @@ export type OutPublishHost = {
33
33
  pointerY: number;
34
34
  lastHit: SceneHit | null;
35
35
  fov: number;
36
+ moving: number;
36
37
  formPublisher: OutPublisherLike | null;
37
38
  bindOutPublisher: () => void;
38
39
  dispatchEvent: (event: Event) => boolean;
@@ -64,7 +65,7 @@ function shouldSkipFormControlOutWrite(
64
65
  }
65
66
 
66
67
  function buildOutput(host: OutPublishHost, time: number): Sonic3dOutput {
67
- return (
68
+ const base =
68
69
  host.runtime?.snapshot({
69
70
  time,
70
71
  frame: host.frame,
@@ -91,8 +92,8 @@ function buildOutput(host: OutPublishHost, time: number): Sonic3dOutput {
91
92
  distance: 0,
92
93
  yaw: 0,
93
94
  pitch: 0,
94
- }
95
- );
95
+ };
96
+ return { ...base, moving: host.moving };
96
97
  }
97
98
 
98
99
  function writePayload(host: OutPublishHost, payload: Sonic3dOutput) {
@@ -8,10 +8,21 @@ export type PlaybackHost = {
8
8
  playOnHover: boolean;
9
9
  hovering: boolean;
10
10
  autoRotate: boolean;
11
+ playOffscreen: boolean;
12
+ /** Nombre de consommateurs live (`registerFrameConsumer`). */
13
+ frameConsumerCount: number;
11
14
  raf: number;
12
15
  drawFrame: (now: number) => void;
13
16
  };
14
17
 
18
+ /** Hors écran : jouer si `play-offscreen` ou au moins un consommateur live. */
19
+ export function wantsOffscreenPlay(host: {
20
+ playOffscreen: boolean;
21
+ frameConsumerCount: number;
22
+ }): boolean {
23
+ return !!host.playOffscreen || host.frameConsumerCount > 0;
24
+ }
25
+
15
26
  function prefersReducedMotion(): boolean {
16
27
  return (
17
28
  typeof matchMedia === "function" &&
@@ -22,7 +33,7 @@ function prefersReducedMotion(): boolean {
22
33
  function shouldAnimate(host: PlaybackHost): boolean {
23
34
  if (!host.play) return false;
24
35
  if (!host.ready) return false;
25
- if (!host.inView) return false;
36
+ if (!host.inView && !wantsOffscreenPlay(host)) return false;
26
37
  if (!host.runtime) return false;
27
38
  if (prefersReducedMotion()) return false;
28
39
  if (host.playOnHover) return host.hovering;
@@ -64,6 +75,7 @@ function syncPlayback(host: PlaybackHost, loop: () => void) {
64
75
  export const playback = {
65
76
  prefersReducedMotion,
66
77
  shouldAnimate,
78
+ wantsOffscreenPlay,
67
79
  syncAutoRotate,
68
80
  syncPlayback,
69
81
  stopLoop,