@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.2

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 (218) hide show
  1. package/CLAUDE.md +87 -0
  2. package/LICENSE.md +664 -0
  3. package/README.md +84 -2
  4. package/dist/dist-manifest.json +218 -0
  5. package/dist/eyes/index.js +109 -0
  6. package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
  7. package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
  8. package/dist/fat/pkg-node/package.json +12 -0
  9. package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
  10. package/dist/frqtl/compat/boot-node.js +34 -0
  11. package/dist/frqtl/compat/engine.js +127 -0
  12. package/dist/frqtl/compat/package-root.js +24 -0
  13. package/dist/frqtl/compat/providers/wasm-engine.js +2499 -0
  14. package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
  15. package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
  16. package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
  17. package/dist/frqtl/compat/renderer/Filters.js +163 -0
  18. package/dist/frqtl/compat/renderer/RenderFamily.js +84 -0
  19. package/dist/frqtl/compat/renderer/RenderMesh.js +99 -0
  20. package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
  21. package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
  22. package/dist/frqtl/compat/renderer/captureStill-common.js +258 -0
  23. package/dist/frqtl/compat/renderer/display-slot.js +96 -0
  24. package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
  25. package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
  26. package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
  27. package/dist/frqtl/compat/renderer/providers/index.js +22 -0
  28. package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
  29. package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
  30. package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
  31. package/dist/frqtl/compat/renderer/providers/three-provider.js +2470 -0
  32. package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
  33. package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
  34. package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
  35. package/dist/frqtl/compat/vendor.js +196 -0
  36. package/dist/frqtl/node-entry.js +17 -0
  37. package/dist/frqtl/objects/FrqtlApp.js +2920 -0
  38. package/dist/frqtl/objects/ObjectFactory.js +296 -0
  39. package/dist/frqtl/objects/RecipeContext.js +322 -0
  40. package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
  41. package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
  42. package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
  43. package/dist/frqtl/objects/instruments/capture-host.js +453 -0
  44. package/dist/frqtl/objects/instruments/capture.js +710 -0
  45. package/dist/frqtl/objects/instruments/hosted-eyes.js +984 -0
  46. package/dist/frqtl/objects/kinds-entry.js +14 -0
  47. package/dist/frqtl/objects/kinds.js +190 -0
  48. package/dist/frqtl/objects/objects/Box.js +273 -0
  49. package/dist/frqtl/objects/objects/Bullet.js +155 -0
  50. package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
  51. package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
  52. package/dist/frqtl/objects/objects/DirectedTunnel.js +411 -0
  53. package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
  54. package/dist/frqtl/objects/objects/LightSource.js +351 -0
  55. package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
  56. package/dist/frqtl/objects/objects/Tube.js +471 -0
  57. package/dist/frqtl/objects/objects/absorbRadiation.js +309 -0
  58. package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
  59. package/dist/frqtl/objects/objects/controlAction.js +89 -0
  60. package/dist/frqtl/objects/objects/controlBind.js +278 -0
  61. package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
  62. package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
  63. package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
  64. package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
  65. package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
  66. package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
  67. package/dist/frqtl/objects/objects/makeControlsForObject.js +820 -0
  68. package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
  69. package/dist/frqtl/objects/objects/slitLinerScatter.js +261 -0
  70. package/dist/frqtl/objects/objects/sustainedSource.js +298 -0
  71. package/dist/frqtl/objects/objects/tagger.js +228 -0
  72. package/dist/frqtl/objects/schema/engine-scopes.js +468 -0
  73. package/dist/frqtl/objects/schema/index.js +256 -0
  74. package/dist/frqtl/objects/schema/scopes.js +457 -0
  75. package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
  76. package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
  77. package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
  78. package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
  79. package/dist/frqtl/objects/setup/_worldInit.js +162 -0
  80. package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
  81. package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
  82. package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
  83. package/dist/frqtl/objects/setup/setupConeEmitter3D.js +250 -0
  84. package/dist/frqtl/objects/setup/setupDoubleSlit.js +326 -0
  85. package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
  86. package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
  87. package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
  88. package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
  89. package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
  90. package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
  91. package/dist/frqtl/objects/setup/setupLightSourceLab.js +277 -0
  92. package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
  93. package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
  94. package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
  95. package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1258 -0
  96. package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
  97. package/dist/frqtl/objects/setup/setupTunnelLab.js +240 -0
  98. package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
  99. package/dist/frqtl/objects/setup/setupWaveInterference.js +177 -0
  100. package/dist/frqtl/objects/v1-replacements.js +29 -0
  101. package/dist/frqtl/package-core.js +23 -0
  102. package/dist/frqtl/parts/CameraControls.js +643 -0
  103. package/dist/frqtl/parts/CameraPath.js +575 -0
  104. package/dist/frqtl/parts/CollisionActions.js +34 -0
  105. package/dist/frqtl/parts/CollisionTypes.js +30 -0
  106. package/dist/frqtl/parts/DetectorWall.js +496 -0
  107. package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
  108. package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
  109. package/dist/frqtl/parts/FrqtlVision.js +818 -0
  110. package/dist/frqtl/parts/GraphingDetectorWall.js +489 -0
  111. package/dist/frqtl/parts/LargeObject.js +405 -0
  112. package/dist/frqtl/parts/LightColor.js +255 -0
  113. package/dist/frqtl/parts/LiveChart.js +285 -0
  114. package/dist/frqtl/parts/LogicGate.js +717 -0
  115. package/dist/frqtl/parts/OvenBox.js +611 -0
  116. package/dist/frqtl/parts/PointSource.js +762 -0
  117. package/dist/frqtl/parts/Rectangle.js +200 -0
  118. package/dist/frqtl/parts/SlitLightSource.js +234 -0
  119. package/dist/frqtl/parts/SlitScreen.js +628 -0
  120. package/dist/frqtl/parts/SphereEmitter.js +946 -0
  121. package/dist/frqtl/parts/Trace.js +508 -0
  122. package/dist/frqtl/parts/Tunnel.js +1057 -0
  123. package/dist/frqtl/parts/Wall.js +1211 -0
  124. package/dist/frqtl/parts/index.js +33 -0
  125. package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
  126. package/dist/frqtl/scenes/absorbing-wall.js +32 -0
  127. package/dist/frqtl/scenes/atomic-model.js +88 -0
  128. package/dist/frqtl/scenes/big-bang.js +46 -0
  129. package/dist/frqtl/scenes/blackbody.js +81 -0
  130. package/dist/frqtl/scenes/circuit-sat.js +127 -0
  131. package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
  132. package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
  133. package/dist/frqtl/scenes/counting-wall.js +38 -0
  134. package/dist/frqtl/scenes/double-slit.js +31 -0
  135. package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
  136. package/dist/frqtl/scenes/dse-one-slit.js +114 -0
  137. package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
  138. package/dist/frqtl/scenes/dse-tunnel-rebuild.js +243 -0
  139. package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
  140. package/dist/frqtl/scenes/dse-two-slits.js +132 -0
  141. package/dist/frqtl/scenes/empty.js +14 -0
  142. package/dist/frqtl/scenes/laser-buildup.js +49 -0
  143. package/dist/frqtl/scenes/mirror-3d.js +56 -0
  144. package/dist/frqtl/scenes/mirror-route.js +140 -0
  145. package/dist/frqtl/scenes/mirror.js +38 -0
  146. package/dist/frqtl/scenes/omni-3d.js +27 -0
  147. package/dist/frqtl/scenes/omni-source.js +22 -0
  148. package/dist/frqtl/scenes/self-interference.js +39 -0
  149. package/dist/frqtl/scenes/smoking-gun.js +122 -0
  150. package/dist/frqtl/scenes/straight-walk.js +22 -0
  151. package/dist/frqtl/scenes/sun.js +39 -0
  152. package/dist/frqtl/scenes/two-source.js +37 -0
  153. package/dist/frqtl/scenes/wave.js +26 -0
  154. package/dist/shim/bundle/frqnb.js +112 -0
  155. package/dist/shim/bundle/index.js +121 -0
  156. package/dist/shim/bundle/indexHtml.js +107 -0
  157. package/dist/shim/bundle/manifest.js +95 -0
  158. package/dist/shim/bundle/zip.js +243 -0
  159. package/dist/shim/bus.js +67 -0
  160. package/dist/shim/controls/index.js +627 -0
  161. package/dist/shim/controls/registry.js +274 -0
  162. package/dist/shim/engine/artifact-shell.js +211 -0
  163. package/dist/shim/engine/boot-phases.js +44 -0
  164. package/dist/shim/engine/engine-stack-boot.js +59 -0
  165. package/dist/shim/engine/router.js +690 -0
  166. package/dist/shim/experiment-frame/index.js +296 -0
  167. package/dist/shim/expr/ast-schema.js +369 -0
  168. package/dist/shim/field-descriptor/index.js +325 -0
  169. package/dist/shim/frame-data/index.js +224 -0
  170. package/dist/shim/index.js +67 -0
  171. package/dist/shim/notebook/canvas-guard.js +82 -0
  172. package/dist/shim/notebook/catalog.js +35 -0
  173. package/dist/shim/notebook/format.js +736 -0
  174. package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
  175. package/dist/shim/notebook/kernel.js +671 -0
  176. package/dist/shim/notebook/knob.js +150 -0
  177. package/dist/shim/notebook/module-resolver.js +260 -0
  178. package/dist/shim/notebook/notebook-knobs.js +65 -0
  179. package/dist/shim/notebook/produced-bindings.js +67 -0
  180. package/dist/shim/notebook/realm-registry.js +121 -0
  181. package/dist/shim/notebook/realm-transport.js +73 -0
  182. package/dist/shim/notebook/setup-builder.js +507 -0
  183. package/dist/shim/notebook/store.js +159 -0
  184. package/dist/shim/notebook/vendor-importmap.js +134 -0
  185. package/dist/shim/notebook/view-builder.js +189 -0
  186. package/dist/shim/notebook/view-ctx.js +100 -0
  187. package/dist/shim/notebook/view-handle.js +202 -0
  188. package/dist/shim/notebook/warmup.js +102 -0
  189. package/dist/shim/package.json +4 -0
  190. package/dist/shim/page-params/index.js +213 -0
  191. package/dist/shim/project/identity.js +112 -0
  192. package/dist/shim/project/index.js +19 -0
  193. package/dist/shim/project/link.js +402 -0
  194. package/dist/shim/project/node-specifier-hook.mjs +35 -0
  195. package/dist/shim/project/register-node.mjs +17 -0
  196. package/dist/shim/project/specifiers.js +106 -0
  197. package/dist/shim/registry/copy-gate.js +110 -0
  198. package/dist/shim/registry/exhibits.js +455 -0
  199. package/dist/shim/registry/experiments.js +215 -0
  200. package/dist/shim/registry/index.js +19 -0
  201. package/dist/shim/registry/scene-source.js +157 -0
  202. package/dist/shim/registry/walkthrough.js +804 -0
  203. package/dist/shim/render/capture-envelope.js +279 -0
  204. package/dist/shim/render/heat-colormap.js +92 -0
  205. package/dist/shim/render/render-stages.js +855 -0
  206. package/dist/shim/render/sun-look.js +33 -0
  207. package/dist/shim/render-state/apply.js +262 -0
  208. package/dist/shim/render-state/index.js +74 -0
  209. package/dist/shim/rpc/index.js +207 -0
  210. package/dist/shim/runtime/index.js +1023 -0
  211. package/dist/shim/storage/boot.js +69 -0
  212. package/dist/shim/storage/index.js +169 -0
  213. package/dist/shim/storage/local-storage-provider.js +230 -0
  214. package/dist/shim/storage/make-namespace-store.js +132 -0
  215. package/dist/shim/storage/modules.js +249 -0
  216. package/dist/shim/storage/projects.js +150 -0
  217. package/llms.txt +12 -0
  218. package/package.json +63 -4
@@ -0,0 +1,160 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * three-camera-contract.js — the three.js camera's DECLARATIVE contract: the
5
+ * view PRESETS and the POSE AXES its `getCameraControl()` handle publishes.
6
+ *
7
+ * ## Why this is its own file
8
+ *
9
+ * It is the pure, declarative half of the camera authority: two functions of
10
+ * `(width, height)` returning plain data, with no THREE, no WebGL, and no live
11
+ * provider state. `three-provider.js` (the live half) resolves it and publishes
12
+ * the results on the handle as `ctl.presets` / `ctl.poseFields`; every host —
13
+ * STUDIO's camera strip, the GYM's camera row, an experiment page's own preset
14
+ * buttons — FERRIES those and re-lists nothing. One declaration, N consumers.
15
+ *
16
+ * Keeping it separate also holds `three-provider.js` under the per-file size
17
+ * ceiling that `sdk/_build/verify-publish-surface.mjs` enforces on
18
+ * the copied-verbatim dist directories — that gate's own instruction is to
19
+ * SPLIT at a natural seam rather than raise the ceiling, and "declarative
20
+ * contract vs live renderer" is that seam.
21
+ *
22
+ * ## Node
23
+ *
24
+ * Named ES-module exports, imported by `three-provider.js`; both halves of the contract are testable
25
+ * in plain Node with no THREE instance — see
26
+ * `lib/frqtl/compat/renderer/providers/test/camera-handle-single-source.test.mjs`,
27
+ * which drives every assertion off these two functions.
28
+ */
29
+
30
+ import * as FieldDescriptorSchema from '../../../../shim/field-descriptor/index.js';
31
+
32
+
33
+
34
+
35
+ /**
36
+ * The ONE camera-preset table — the SINGLE SOURCE for both the preset NAMES
37
+ * and the orbit pose each name resolves to. `getCameraControl()` publishes
38
+ * `Object.keys()` of it as `ctl.presets` and resolves `setPreset(name)`
39
+ * through it, so a preset name is written in exactly one place in the repo.
40
+ *
41
+ * Three pre-computed perspectives on the canonical world frame:
42
+ * X = down the barrel (+X = downrange, out the muzzle) · Y = vertical (grip −Y) ·
43
+ * Z = lateral. Azimuth is measured around the vertical so az=+90 puts the camera
44
+ * downrange (+X) looking back INTO the muzzle; az=0 puts it on +Z (side).
45
+ * down-barrel — DEFAULT: straight down the barrel, the bullet staring at you
46
+ * (zero-momentum air is invisible). Tight distance → big bullet.
47
+ * table-top — down-barrel rotated 90°: eye-level SIDE view (barrel horizontal).
48
+ * rooftop — looking down ~45° in both Y and Z: aerial 3/4.
49
+ * `distance` is relative to the scene size, so a preset frames any scene.
50
+ *
51
+ * @param {number} width scene width in engine pixels
52
+ * @param {number} height scene height in engine pixels
53
+ * @returns {Object<string,{azimuth:number,elevation:number,distance:number}>}
54
+ */
55
+ function cameraPresets(width, height) {
56
+ var base = Math.max(width, height);
57
+ return {
58
+ 'down-barrel': { azimuth: 90, elevation: 0, distance: base * 0.95 },
59
+ 'table-top': { azimuth: 0, elevation: 0, distance: base * 1.30 },
60
+ 'rooftop': { azimuth: 45, elevation: 45, distance: base * 1.50 }
61
+ };
62
+ }
63
+
64
+ /**
65
+ * The ONE declaration of the camera's POSE AXES — the sliders a host builds.
66
+ *
67
+ * Each entry is a `lib/shim/field-descriptor` descriptor (REUSED, never
68
+ * re-derived). `name` is the `getOrbit()` / `setOrbit()` key, so a host builds
69
+ * one slider per entry and writes back through the key it was handed: it
70
+ * enumerates no axis and invents no range, and a newly declared axis appears
71
+ * in every host with zero host edits.
72
+ *
73
+ * Bounds live HERE because they are the PROVIDER's knowledge, not a host's:
74
+ * azimuth [-180, 180]° — the full turn.
75
+ * elevation [-90, 90]° — pole to pole. (The hosts' old ±85 was a pole
76
+ * guard hand-copied into two files.)
77
+ * distance SCENE-SCALED off `base = max(width,height)` — the same base the
78
+ * preset table frames with: [base/3, base*4], step base/30. A host
79
+ * cannot compute this (it does not know the scene size), which is
80
+ * why the old fixed 200–2400 was a guess that happened to be right
81
+ * only for a 600×400 scene, where this formula reproduces it exactly.
82
+ * roll [-180, 180]° — the full turn. Roll has NO pole, so its bound is a
83
+ * WRAP point, not a clamp: `setOrbit` wraps a roll write mod 360.
84
+ * `default` is the factory's own start pose (azimuth 0, elevation 0 —
85
+ * straight-on-z; distance = base*1.6, what `_init` derives from canvas size;
86
+ * roll 0 — the horizon level).
87
+ *
88
+ * @param {number} width scene width in engine pixels
89
+ * @param {number} height scene height in engine pixels
90
+ * @returns {Array<object>} normalized field descriptors, one per pose axis.
91
+ */
92
+ function cameraPoseFields(width, height) {
93
+ var normalizeFieldDescriptor = FieldDescriptorSchema.normalizeFieldDescriptor;
94
+ var base = Math.max(width, height);
95
+ return [
96
+ normalizeFieldDescriptor({
97
+ name: 'azimuth', label: 'az', type: 'number', unit: 'deg', default: 0,
98
+ // `wrap: 360` DECLARES the value space is periodic with that period — the
99
+ // axis is a circle, not a segment. Consumers that must interpolate (CameraPath)
100
+ // FERRY this instead of hand-listing which axes are angular; a new wrapping
101
+ // axis therefore gets shortest-arc interpolation with zero consumer edits.
102
+ // It describes the VALUE SPACE, not a storage normalisation (setOrbit wraps
103
+ // roll's stored value; azimuth is periodic without being renormalised).
104
+ constraint: { min: -180, max: 180, integer: false, wrap: 360 },
105
+ widgetConfig: { step: 1 },
106
+ description: 'Orbit angle around the vertical axis, in degrees. az=0 looks straight ' +
107
+ 'at the x/y plane from +z; az=+90 puts the camera on +x looking back along it.'
108
+ }),
109
+ normalizeFieldDescriptor({
110
+ name: 'elevation', label: 'el', type: 'number', unit: 'deg', default: 0,
111
+ constraint: { min: -90, max: 90, integer: false },
112
+ widgetConfig: { step: 1 },
113
+ description: 'Orbit angle above (+) / below (−) the horizontal, in degrees. ' +
114
+ '±90 is straight down / straight up the vertical axis.'
115
+ }),
116
+ normalizeFieldDescriptor({
117
+ name: 'distance', label: 'dist', type: 'number', unit: 'px', default: base * 1.6,
118
+ constraint: { min: Math.round(base / 3), max: base * 4, integer: false },
119
+ widgetConfig: { step: Math.max(1, Math.round(base / 30)) },
120
+ description: 'Camera distance from the orbit centre, in engine pixels. Bounds are ' +
121
+ 'relative to the scene size (base = max(width,height)) so the slider frames any scene.'
122
+ }),
123
+ normalizeFieldDescriptor({
124
+ name: 'roll', label: 'roll', type: 'number', unit: 'deg', default: 0,
125
+ constraint: { min: -180, max: 180, integer: false, wrap: 360 },
126
+ widgetConfig: { step: 1 },
127
+ description: 'Rotation about the view axis — the horizon tilt, in degrees. It spins ' +
128
+ 'the picture in the screen plane without moving the eye: roll=90 puts what was ' +
129
+ 'screen-vertical onto the screen horizontal. Unlike elevation it has NO pole, so ' +
130
+ 'the range is a WRAP point (mod 360), never a clamp.'
131
+ })
132
+ ,
133
+ normalizeFieldDescriptor({
134
+ name: 'target', label: 'centre', type: 'object', unit: 'px', default: null,
135
+ // DECLARED (2026-07-30): `target` is a fully legal settable axis of this
136
+ // camera — `setOrbit({target})` applies it, `getOrbit()` returns it, and
137
+ // `setPreset` pans with it. Omitting it from this list made the DECLARATION
138
+ // disagree with the SURFACE, and anything that validates a pose against the
139
+ // declared axes (CameraPath) therefore rejected `target` waypoints: a 3D
140
+ // camera path could orbit but never dolly/track, which kills any path that follows
141
+ // a moving region.
142
+ //
143
+ // Deliberately carries NO numeric constraint: it is a {x,y,z} POINT, not a
144
+ // scalar. The shared `poseSliderBounds` helper returns null for a descriptor
145
+ // with no range and both slider hosts already skip it ("incomplete range ⇒
146
+ // hide, never a 0–100 default"), so declaring it adds an axis to the CONTRACT
147
+ // without inventing a slider that could not work. Same shape as the 2D
148
+ // camera's own `target` descriptor — one vocabulary, both modes.
149
+ description: 'The world point {x,y,z} the orbit revolves around; null = the ' +
150
+ 'scene centre. Pan/track IS target — the same key the 2D camera uses.'
151
+ })
152
+ ];
153
+ }
154
+
155
+ // ── Export ──────────────────────────────────────────────────────────────
156
+
157
+ export {
158
+ cameraPresets,
159
+ cameraPoseFields
160
+ };