paperlab 0.5.0 → 0.5.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.
package/dist/stage.cjs CHANGED
@@ -52,8 +52,8 @@ module.exports = __toCommonJS(stage_exports);
52
52
 
53
53
  // src/stage/PaperStage.tsx
54
54
  var THREE22 = __toESM(require("three"), 1);
55
- var import_fiber10 = require("@react-three/fiber");
56
- var import_react23 = require("react");
55
+ var import_fiber11 = require("@react-three/fiber");
56
+ var import_react24 = require("react");
57
57
  var import_zod29 = require("zod");
58
58
 
59
59
  // src/a11y/index.tsx
@@ -275,8 +275,8 @@ function contentText(config) {
275
275
  // src/PaperField.tsx
276
276
  var THREE16 = __toESM(require("three"), 1);
277
277
  var import_gsap5 = require("gsap");
278
- var import_fiber6 = require("@react-three/fiber");
279
- var import_react16 = require("react");
278
+ var import_fiber7 = require("@react-three/fiber");
279
+ var import_react17 = require("react");
280
280
 
281
281
  // src/config/schema.ts
282
282
  var import_zod14 = require("zod");
@@ -2110,9 +2110,24 @@ function paintBackdrop(backdrop, width, height, image) {
2110
2110
  return texture;
2111
2111
  }
2112
2112
 
2113
+ // src/scene/release.tsx
2114
+ var import_fiber3 = require("@react-three/fiber");
2115
+ var import_react4 = require("react");
2116
+ function ReleaseContextOnUnmount() {
2117
+ const gl = (0, import_fiber3.useThree)((s) => s.gl);
2118
+ (0, import_react4.useEffect)(
2119
+ () => () => {
2120
+ gl.forceContextLoss();
2121
+ gl.dispose();
2122
+ },
2123
+ [gl]
2124
+ );
2125
+ return null;
2126
+ }
2127
+
2113
2128
  // src/field/dropZones.tsx
2114
2129
  var THREE4 = __toESM(require("three"), 1);
2115
- var import_react4 = require("react");
2130
+ var import_react5 = require("react");
2116
2131
  var import_jsx_runtime3 = require("react/jsx-runtime");
2117
2132
  var DropZoneRegistry = class {
2118
2133
  zones = /* @__PURE__ */ new Map();
@@ -2151,7 +2166,7 @@ var DropZoneRegistry = class {
2151
2166
  for (const fn of this.listeners) fn();
2152
2167
  }
2153
2168
  };
2154
- var DropZoneContext = (0, import_react4.createContext)(null);
2169
+ var DropZoneContext = (0, import_react5.createContext)(null);
2155
2170
  var globCache = /* @__PURE__ */ new Map();
2156
2171
  var globRegExp = (glob) => {
2157
2172
  let re = globCache.get(glob);
@@ -2167,17 +2182,17 @@ function zoneAccepts(zone, name) {
2167
2182
  }
2168
2183
  var zoneContains = (zone, x, y) => Math.abs(x - zone.bounds.position[0]) <= zone.bounds.size[0] / 2 && Math.abs(y - zone.bounds.position[1]) <= zone.bounds.size[1] / 2;
2169
2184
  function DropZoneVisual({ registry: registry4, config }) {
2170
- (0, import_react4.useSyncExternalStore)(registry4.subscribe, registry4.getVersion, registry4.getVersion);
2185
+ (0, import_react5.useSyncExternalStore)(registry4.subscribe, registry4.getVersion, registry4.getVersion);
2171
2186
  const hovered = registry4.hovered === config.id;
2172
2187
  const style = config.highlight ?? "glow";
2173
2188
  const [w, h] = config.bounds.size;
2174
- const edges = (0, import_react4.useMemo)(() => {
2189
+ const edges = (0, import_react5.useMemo)(() => {
2175
2190
  const plane = new THREE4.PlaneGeometry(w, h);
2176
2191
  const geo = new THREE4.EdgesGeometry(plane);
2177
2192
  plane.dispose();
2178
2193
  return geo;
2179
2194
  }, [w, h]);
2180
- (0, import_react4.useEffect)(() => () => edges.dispose(), [edges]);
2195
+ (0, import_react5.useEffect)(() => () => edges.dispose(), [edges]);
2181
2196
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("group", { position: config.bounds.position, children: [
2182
2197
  style !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("mesh", { children: [
2183
2198
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("planeGeometry", { args: [w, h] }),
@@ -2198,8 +2213,8 @@ function DropZoneVisual({ registry: registry4, config }) {
2198
2213
  // src/PaperMesh.tsx
2199
2214
  var THREE11 = __toESM(require("three"), 1);
2200
2215
  var import_gsap2 = require("gsap");
2201
- var import_fiber3 = require("@react-three/fiber");
2202
- var import_react10 = require("react");
2216
+ var import_fiber4 = require("@react-three/fiber");
2217
+ var import_react11 = require("react");
2203
2218
 
2204
2219
  // src/config/serialize.ts
2205
2220
  function parsePreset(input) {
@@ -2262,9 +2277,9 @@ function computeSheetNormals(geometry) {
2262
2277
  }
2263
2278
 
2264
2279
  // src/core/stable.ts
2265
- var import_react5 = require("react");
2280
+ var import_react6 = require("react");
2266
2281
  function useStable(value) {
2267
- const held = (0, import_react5.useRef)(value);
2282
+ const held = (0, import_react6.useRef)(value);
2268
2283
  if (!deepEqual(held.current, value)) held.current = value;
2269
2284
  return held.current;
2270
2285
  }
@@ -2580,7 +2595,7 @@ function listPresets() {
2580
2595
 
2581
2596
  // src/content/texture.ts
2582
2597
  var THREE6 = __toESM(require("three"), 1);
2583
- var import_react6 = require("react");
2598
+ var import_react7 = require("react");
2584
2599
 
2585
2600
  // src/content/type.ts
2586
2601
  function wrapLines(ctx, text, maxWidth, font) {
@@ -2869,9 +2884,9 @@ function makeTexture(canvas) {
2869
2884
  return tex;
2870
2885
  }
2871
2886
  function useContentTexture(content, sheet2, stock) {
2872
- const [texture, setTexture] = (0, import_react6.useState)(null);
2887
+ const [texture, setTexture] = (0, import_react7.useState)(null);
2873
2888
  const key = JSON.stringify({ content: content ?? null, w: sheet2.width, h: sheet2.height, stock: stock.id });
2874
- (0, import_react6.useEffect)(() => {
2889
+ (0, import_react7.useEffect)(() => {
2875
2890
  let disposed = false;
2876
2891
  let tex = null;
2877
2892
  if (!content) {
@@ -3840,7 +3855,7 @@ var ClothSim = class {
3840
3855
 
3841
3856
  // src/surface/PaperMaterial.tsx
3842
3857
  var THREE10 = __toESM(require("three"), 1);
3843
- var import_react8 = require("react");
3858
+ var import_react9 = require("react");
3844
3859
  var import_three_custom_shader_material = __toESM(require("three-custom-shader-material"), 1);
3845
3860
 
3846
3861
  // src/surface/compose.ts
@@ -4213,14 +4228,14 @@ function padPositions(positions) {
4213
4228
  }
4214
4229
 
4215
4230
  // src/scene/rig.tsx
4216
- var import_react7 = require("react");
4231
+ var import_react8 = require("react");
4217
4232
  var import_jsx_runtime4 = require("react/jsx-runtime");
4218
- var LightRigContext = (0, import_react7.createContext)(null);
4233
+ var LightRigContext = (0, import_react8.createContext)(null);
4219
4234
  function LightRig({ rig, children }) {
4220
4235
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LightRigContext.Provider, { value: rig, children });
4221
4236
  }
4222
4237
  function useLightRig(own) {
4223
- return (0, import_react7.useContext)(LightRigContext) ?? getLightingPreset(own);
4238
+ return (0, import_react8.useContext)(LightRigContext) ?? getLightingPreset(own);
4224
4239
  }
4225
4240
 
4226
4241
  // src/surface/PaperMaterial.tsx
@@ -4246,8 +4261,8 @@ function PaperMaterial({
4246
4261
  sheet2,
4247
4262
  rig
4248
4263
  );
4249
- const bound = (0, import_react8.useMemo)(() => composed.uniforms, [composed.structureKey]);
4250
- (0, import_react8.useEffect)(() => {
4264
+ const bound = (0, import_react9.useMemo)(() => composed.uniforms, [composed.structureKey]);
4265
+ (0, import_react9.useEffect)(() => {
4251
4266
  for (const [key, uniform] of Object.entries(composed.uniforms)) {
4252
4267
  if (!bound[key] || key === "uFrontMap" || key === "uBackMap") continue;
4253
4268
  if (bound[key].value instanceof THREE10.Color && uniform.value instanceof THREE10.Color) {
@@ -4258,7 +4273,7 @@ function PaperMaterial({
4258
4273
  }
4259
4274
  }
4260
4275
  });
4261
- (0, import_react8.useEffect)(() => {
4276
+ (0, import_react9.useEffect)(() => {
4262
4277
  if (bound.uFrontMap) bound.uFrontMap.value = texture;
4263
4278
  if (bound.uBackMap) bound.uBackMap.value = backTexture ?? null;
4264
4279
  }, [bound, texture, backTexture]);
@@ -4292,7 +4307,7 @@ function quantizeProgress(p, duration, fps = ON_TWOS_FPS) {
4292
4307
  }
4293
4308
 
4294
4309
  // src/states/usePaperStates.ts
4295
- var import_react9 = require("react");
4310
+ var import_react10 = require("react");
4296
4311
 
4297
4312
  // src/states/machine.ts
4298
4313
  var import_gsap = require("gsap");
@@ -4519,15 +4534,15 @@ var PaperStateMachine = class {
4519
4534
  // src/states/usePaperStates.ts
4520
4535
  function usePaperStates(config, enabled, instant, onAction, onStateChange) {
4521
4536
  const live = enabled && Boolean(config.states);
4522
- const key = (0, import_react9.useMemo)(() => JSON.stringify(config), [config]);
4523
- const onActionRef = (0, import_react9.useRef)(onAction);
4537
+ const key = (0, import_react10.useMemo)(() => JSON.stringify(config), [config]);
4538
+ const onActionRef = (0, import_react10.useRef)(onAction);
4524
4539
  onActionRef.current = onAction;
4525
- const onStateChangeRef = (0, import_react9.useRef)(onStateChange);
4540
+ const onStateChangeRef = (0, import_react10.useRef)(onStateChange);
4526
4541
  onStateChangeRef.current = onStateChange;
4527
- const machineRef = (0, import_react9.useRef)(null);
4528
- const lastStateRef = (0, import_react9.useRef)("rest");
4529
- const [animated, setAnimated] = (0, import_react9.useState)(null);
4530
- (0, import_react9.useEffect)(() => {
4542
+ const machineRef = (0, import_react10.useRef)(null);
4543
+ const lastStateRef = (0, import_react10.useRef)("rest");
4544
+ const [animated, setAnimated] = (0, import_react10.useState)(null);
4545
+ (0, import_react10.useEffect)(() => {
4531
4546
  if (!live) {
4532
4547
  machineRef.current?.dispose();
4533
4548
  machineRef.current = null;
@@ -4554,7 +4569,7 @@ function usePaperStates(config, enabled, instant, onAction, onStateChange) {
4554
4569
  lastStateRef.current = machine.state;
4555
4570
  setAnimated({ config: machine.structuralConfig(), state: machine.state });
4556
4571
  }, [key, live, instant]);
4557
- (0, import_react9.useEffect)(
4572
+ (0, import_react10.useEffect)(
4558
4573
  () => () => {
4559
4574
  machineRef.current?.dispose();
4560
4575
  machineRef.current = null;
@@ -4586,7 +4601,7 @@ function configInputs(props) {
4586
4601
  }
4587
4602
  function useResolvedConfig(props) {
4588
4603
  const inputs = useStable(configInputs(props));
4589
- return (0, import_react10.useMemo)(() => resolveConfig(props), [inputs]);
4604
+ return (0, import_react11.useMemo)(() => resolveConfig(props), [inputs]);
4590
4605
  }
4591
4606
  function resolveConfig(props) {
4592
4607
  const base = props.preset ? typeof props.preset === "string" ? getPreset(props.preset) : parsePreset(props.preset) : paperConfigSchema.parse({});
@@ -4610,7 +4625,7 @@ var planeNormal = new THREE11.Vector3();
4610
4625
  var anchorScratch = new THREE11.Vector3();
4611
4626
  var worldScratch = new THREE11.Vector3();
4612
4627
  var quatScratch = new THREE11.Quaternion();
4613
- var PaperMesh = (0, import_react10.forwardRef)(function PaperMesh2(props, ref) {
4628
+ var PaperMesh = (0, import_react11.forwardRef)(function PaperMesh2(props, ref) {
4614
4629
  const resolved = useResolvedConfig(props);
4615
4630
  const reduced = usePrefersReducedMotion(props.reducedMotion);
4616
4631
  const statesLive = Boolean(resolved.states) && props.stateTriggers !== false;
@@ -4620,33 +4635,33 @@ var PaperMesh = (0, import_react10.forwardRef)(function PaperMesh2(props, ref) {
4620
4635
  state: machineState
4621
4636
  } = usePaperStates(resolved, statesLive, reduced, props.onStateAction, props.onStateChange);
4622
4637
  const behavior = config.behavior ? getBehavior(config.behavior.type) : null;
4623
- const machineRef = (0, import_react10.useRef)(null);
4638
+ const machineRef = (0, import_react11.useRef)(null);
4624
4639
  machineRef.current = machine;
4625
- const resolvedRef = (0, import_react10.useRef)(resolved);
4640
+ const resolvedRef = (0, import_react11.useRef)(resolved);
4626
4641
  resolvedRef.current = resolved;
4627
4642
  const isCloth = !reduced && typeof config.physics === "object";
4628
4643
  const idle = !reduced && typeof config.physics === "string" && config.physics !== "none" ? getIdlePreset(config.physics) : null;
4629
- const meshRef = (0, import_react10.useRef)(null);
4630
- const groupRef = (0, import_react10.useRef)(null);
4631
- const handleRefs = (0, import_react10.useRef)([]);
4632
- const overridesRef = (0, import_react10.useRef)({});
4633
- const dirtyRef = (0, import_react10.useRef)(true);
4634
- const playingRef = (0, import_react10.useRef)(false);
4635
- const tweenRef = (0, import_react10.useRef)(null);
4636
- const draggingRef = (0, import_react10.useRef)(null);
4637
- const configRef = (0, import_react10.useRef)(config);
4644
+ const meshRef = (0, import_react11.useRef)(null);
4645
+ const groupRef = (0, import_react11.useRef)(null);
4646
+ const handleRefs = (0, import_react11.useRef)([]);
4647
+ const overridesRef = (0, import_react11.useRef)({});
4648
+ const dirtyRef = (0, import_react11.useRef)(true);
4649
+ const playingRef = (0, import_react11.useRef)(false);
4650
+ const tweenRef = (0, import_react11.useRef)(null);
4651
+ const draggingRef = (0, import_react11.useRef)(null);
4652
+ const configRef = (0, import_react11.useRef)(config);
4638
4653
  configRef.current = config;
4639
- const controls = (0, import_fiber3.useThree)((s) => s.controls);
4640
- const camera = (0, import_fiber3.useThree)((s) => s.camera);
4654
+ const controls = (0, import_fiber4.useThree)((s) => s.controls);
4655
+ const camera = (0, import_fiber4.useThree)((s) => s.camera);
4641
4656
  const behaviorKey = JSON.stringify(config.behavior ?? null);
4642
4657
  const deformersKey = JSON.stringify(config.deformers ?? null);
4643
4658
  const sheetKey = JSON.stringify(config.sheet);
4644
4659
  const physicsKey = JSON.stringify(config.physics);
4645
- (0, import_react10.useEffect)(() => {
4660
+ (0, import_react11.useEffect)(() => {
4646
4661
  if (!draggingRef.current && !playingRef.current) overridesRef.current = {};
4647
4662
  dirtyRef.current = true;
4648
4663
  }, [behaviorKey, deformersKey, sheetKey, physicsKey]);
4649
- const { minSegments, autoSegments, animatedStack } = (0, import_react10.useMemo)(() => {
4664
+ const { minSegments, autoSegments, animatedStack } = (0, import_react11.useMemo)(() => {
4650
4665
  const cfg = configRef.current;
4651
4666
  const probe2 = buildStack(cfg, {});
4652
4667
  if (!probe2) {
@@ -4675,18 +4690,18 @@ var PaperMesh = (0, import_react10.forwardRef)(function PaperMesh2(props, ref) {
4675
4690
  animatedStack: animated
4676
4691
  };
4677
4692
  }, [behaviorKey, deformersKey, physicsKey]);
4678
- const geometry = (0, import_react10.useMemo)(() => {
4693
+ const geometry = (0, import_react11.useMemo)(() => {
4679
4694
  if (!isCloth) return createSheetGeometry(config.sheet, minSegments, autoSegments);
4680
4695
  const [sx, sy] = resolveSegments(config.sheet, 2);
4681
4696
  const capped = Math.min(Math.max(sx, sy), CLOTH_MAX_SEGMENTS);
4682
4697
  return new THREE11.PlaneGeometry(config.sheet.width, config.sheet.height, capped, capped);
4683
4698
  }, [sheetKey, minSegments, autoSegments, isCloth]);
4684
- (0, import_react10.useEffect)(() => () => geometry.dispose(), [geometry]);
4685
- const basePositions = (0, import_react10.useMemo)(
4699
+ (0, import_react11.useEffect)(() => () => geometry.dispose(), [geometry]);
4700
+ const basePositions = (0, import_react11.useMemo)(
4686
4701
  () => Float32Array.from(geometry.attributes.position.array),
4687
4702
  [geometry]
4688
4703
  );
4689
- const sim = (0, import_react10.useMemo)(() => {
4704
+ const sim = (0, import_react11.useMemo)(() => {
4690
4705
  if (!isCloth) return null;
4691
4706
  const cloth = configRef.current.physics;
4692
4707
  const cols = geometry.parameters.widthSegments + 1;
@@ -4739,7 +4754,7 @@ var PaperMesh = (0, import_react10.forwardRef)(function PaperMesh2(props, ref) {
4739
4754
  playingRef.current = false;
4740
4755
  tweenRef.current?.pause();
4741
4756
  };
4742
- (0, import_react10.useEffect)(() => {
4757
+ (0, import_react11.useEffect)(() => {
4743
4758
  if (props.autoplay && !reduced) play();
4744
4759
  return () => {
4745
4760
  tweenRef.current?.kill();
@@ -4755,7 +4770,7 @@ var PaperMesh = (0, import_react10.forwardRef)(function PaperMesh2(props, ref) {
4755
4770
  behavior: { ...cfg.behavior, ...overridesRef.current }
4756
4771
  });
4757
4772
  };
4758
- (0, import_react10.useImperativeHandle)(ref, () => ({
4773
+ (0, import_react11.useImperativeHandle)(ref, () => ({
4759
4774
  play,
4760
4775
  pause,
4761
4776
  get playing() {
@@ -4792,8 +4807,8 @@ var PaperMesh = (0, import_react10.forwardRef)(function PaperMesh2(props, ref) {
4792
4807
  placeProgrammatic: () => machineRef.current?.placeProgrammatic() ?? false,
4793
4808
  returnProgrammatic: () => machineRef.current?.returnProgrammatic() ?? false
4794
4809
  }));
4795
- const idlePose = (0, import_react10.useRef)({ position: [0, 0, 0], rotation: [0, 0, 0] });
4796
- (0, import_fiber3.useFrame)(({ clock }, delta) => {
4810
+ const idlePose = (0, import_react11.useRef)({ position: [0, 0, 0], rotation: [0, 0, 0] });
4811
+ (0, import_fiber4.useFrame)(({ clock }, delta) => {
4797
4812
  const cfg = liveConfig();
4798
4813
  const now = reduced ? 0 : cfg.onTwos ? quantizeTime(clock.elapsedTime) : clock.elapsedTime;
4799
4814
  const hasBehaviorTransform = Boolean(behavior?.transform && cfg.behavior);
@@ -4878,7 +4893,7 @@ var PaperMesh = (0, import_react10.forwardRef)(function PaperMesh2(props, ref) {
4878
4893
  const p = overridesRef.current[behavior.progressParam];
4879
4894
  if (typeof p === "number") props.onProgress?.(p);
4880
4895
  };
4881
- const grabAnchor = (0, import_react10.useRef)(new THREE11.Vector3());
4896
+ const grabAnchor = (0, import_react11.useRef)(new THREE11.Vector3());
4882
4897
  const clothDown = (e) => {
4883
4898
  if (!isCloth || !sim || !props.interactive || !groupRef.current) return;
4884
4899
  e.stopPropagation();
@@ -5094,22 +5109,22 @@ function resolveFieldSlotConfig(slot, fallback, index, layoutId, layoutOptions)
5094
5109
  // src/field/fieldGroup.tsx
5095
5110
  var THREE13 = __toESM(require("three"), 1);
5096
5111
  var import_gsap3 = require("gsap");
5097
- var import_fiber4 = require("@react-three/fiber");
5098
- var import_react12 = require("react");
5112
+ var import_fiber5 = require("@react-three/fiber");
5113
+ var import_react13 = require("react");
5099
5114
  var import_three_custom_shader_material2 = __toESM(require("three-custom-shader-material"), 1);
5100
5115
 
5101
5116
  // src/content/atlas.ts
5102
5117
  var THREE12 = __toESM(require("three"), 1);
5103
- var import_react11 = require("react");
5118
+ var import_react12 = require("react");
5104
5119
  var MAX_ATLAS = 4096;
5105
5120
  function atlasGrid(count, aspect = 1) {
5106
5121
  const cols = Math.max(1, Math.min(count, Math.ceil(Math.sqrt(count * Math.max(aspect, 0.01)))));
5107
5122
  return { cols, rows: Math.max(1, Math.ceil(count / cols)) };
5108
5123
  }
5109
5124
  function useContentAtlas(contents, sheet2, stock) {
5110
- const [atlas, setAtlas] = (0, import_react11.useState)(null);
5125
+ const [atlas, setAtlas] = (0, import_react12.useState)(null);
5111
5126
  const stableContents = useStable(contents);
5112
- (0, import_react11.useEffect)(() => {
5127
+ (0, import_react12.useEffect)(() => {
5113
5128
  let disposed = false;
5114
5129
  const aspect = sheet2.height / sheet2.width;
5115
5130
  const { cols, rows } = atlasGrid(contents.length, aspect);
@@ -5839,11 +5854,11 @@ function FieldGroup({
5839
5854
  const stock = getStock(config.stock);
5840
5855
  const rig = useLightRig(config.scene.lighting);
5841
5856
  const behavior = config.behavior && !config.deformers ? getBehavior(config.behavior.type) : null;
5842
- const progressRef = (0, import_react12.useRef)(
5857
+ const progressRef = (0, import_react13.useRef)(
5843
5858
  behavior ? config.behavior[behavior.progressParam] : 0
5844
5859
  );
5845
5860
  const buildStackAt = (progress) => fieldShapeStack(config, progress);
5846
- const initialStack = (0, import_react12.useMemo)(
5861
+ const initialStack = (0, import_react13.useMemo)(
5847
5862
  () => buildStackAt(progressRef.current),
5848
5863
  [
5849
5864
  JSON.stringify(config.behavior ?? null),
@@ -5852,7 +5867,7 @@ function FieldGroup({
5852
5867
  ]
5853
5868
  );
5854
5869
  const structureKey = initialStack.map((i) => i.type).join("|");
5855
- const geometry = (0, import_react12.useMemo)(() => {
5870
+ const geometry = (0, import_react13.useMemo)(() => {
5856
5871
  const floor = stackMinSegments(initialStack, config.sheet);
5857
5872
  const want = [0, 0];
5858
5873
  for (const p of PROGRESS_SAMPLES2) {
@@ -5886,9 +5901,9 @@ function FieldGroup({
5886
5901
  geo.setAttribute("aBias", new THREE13.InstancedBufferAttribute(bias, 1));
5887
5902
  return geo;
5888
5903
  }, [JSON.stringify(config.sheet), structureKey, count, autoCeiling]);
5889
- (0, import_react12.useEffect)(() => () => geometry.dispose(), [geometry]);
5904
+ (0, import_react13.useEffect)(() => () => geometry.dispose(), [geometry]);
5890
5905
  const atlas = useContentAtlas(contents, config.sheet, stock);
5891
- const shader = (0, import_react12.useMemo)(() => {
5906
+ const shader = (0, import_react13.useMemo)(() => {
5892
5907
  const composed = buildDisplacementGLSL(initialStack, config.sheet);
5893
5908
  const uniforms = {};
5894
5909
  for (const [name, value] of Object.entries(composed.uniforms)) {
@@ -5917,19 +5932,19 @@ function FieldGroup({
5917
5932
  config.surface.showThrough,
5918
5933
  config.surface.translucency
5919
5934
  ]);
5920
- (0, import_react12.useEffect)(() => {
5935
+ (0, import_react13.useEffect)(() => {
5921
5936
  const values = translucencyValues(config.surface.translucency ?? stock.translucency, rig);
5922
5937
  shader.uniforms.uTranslucency.value = values.translucency;
5923
5938
  shader.uniforms.uBackLightDir.value.copy(values.direction);
5924
5939
  shader.uniforms.uBackLightColor.value.copy(values.color);
5925
5940
  shader.uniforms.uAmbientTransmission.value = values.ambient;
5926
5941
  }, [shader, rig, config.surface.translucency, stock.translucency]);
5927
- (0, import_react12.useEffect)(() => {
5942
+ (0, import_react13.useEffect)(() => {
5928
5943
  if (!atlas) return;
5929
5944
  shader.uniforms.uAtlas.value = atlas.texture;
5930
5945
  shader.uniforms.uAtlasGrid.value.set(atlas.cols, atlas.rows);
5931
5946
  }, [atlas, shader]);
5932
- (0, import_react12.useEffect)(() => {
5947
+ (0, import_react13.useEffect)(() => {
5933
5948
  if (!behavior || shared.reduced) return;
5934
5949
  const state = { p: progressRef.current };
5935
5950
  const tween = import_gsap3.gsap.to(state, {
@@ -5946,8 +5961,8 @@ function FieldGroup({
5946
5961
  tween.kill();
5947
5962
  };
5948
5963
  }, [structureKey, shared.reduced]);
5949
- const meshRef = (0, import_react12.useRef)(null);
5950
- (0, import_fiber4.useFrame)(({ clock }) => {
5964
+ const meshRef = (0, import_react13.useRef)(null);
5965
+ (0, import_fiber5.useFrame)(({ clock }) => {
5951
5966
  const mesh = meshRef.current;
5952
5967
  if (!mesh) return;
5953
5968
  const elapsed = clock.elapsedTime - Math.max(shared.mountTimeRef.current, 0);
@@ -6094,7 +6109,7 @@ var easeInOut = (t) => t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;
6094
6109
 
6095
6110
  // src/field/backingSheet.tsx
6096
6111
  var THREE14 = __toESM(require("three"), 1);
6097
- var import_react13 = require("react");
6112
+ var import_react14 = require("react");
6098
6113
 
6099
6114
  // src/content/backing.ts
6100
6115
  function silhouetteRects(o, count) {
@@ -6167,7 +6182,7 @@ function BackingSheet({
6167
6182
  removed
6168
6183
  }) {
6169
6184
  const { width, height } = sheetBackingSize(options);
6170
- const canvas = (0, import_react13.useMemo)(() => {
6185
+ const canvas = (0, import_react14.useMemo)(() => {
6171
6186
  if (typeof document === "undefined") return null;
6172
6187
  const c = document.createElement("canvas");
6173
6188
  const scale = Math.min(1024, Math.round(360 * Math.max(width, height))) / Math.max(width, height);
@@ -6175,14 +6190,14 @@ function BackingSheet({
6175
6190
  c.height = Math.max(2, Math.round(height * scale));
6176
6191
  return c;
6177
6192
  }, [width, height]);
6178
- const texture = (0, import_react13.useMemo)(() => canvas ? new THREE14.CanvasTexture(canvas) : null, [canvas]);
6193
+ const texture = (0, import_react14.useMemo)(() => canvas ? new THREE14.CanvasTexture(canvas) : null, [canvas]);
6179
6194
  const removedKey = [...removed].sort((a, b) => a - b).join(",");
6180
- (0, import_react13.useEffect)(() => {
6195
+ (0, import_react14.useEffect)(() => {
6181
6196
  if (!canvas || !texture) return;
6182
6197
  drawBacking(canvas, { options, count, tint: BACKING_TINT, removed });
6183
6198
  texture.needsUpdate = true;
6184
6199
  }, [canvas, texture, JSON.stringify(options), count, removedKey]);
6185
- (0, import_react13.useEffect)(() => () => texture?.dispose(), [texture]);
6200
+ (0, import_react14.useEffect)(() => () => texture?.dispose(), [texture]);
6186
6201
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("mesh", { receiveShadow: true, children: [
6187
6202
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("planeGeometry", { args: [width, height] }),
6188
6203
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("meshStandardMaterial", { map: texture, color: "#ffffff", roughness: 0.92, metalness: 0 })
@@ -6192,8 +6207,8 @@ function BackingSheet({
6192
6207
  // src/field/interactiveField.tsx
6193
6208
  var THREE15 = __toESM(require("three"), 1);
6194
6209
  var import_gsap4 = require("gsap");
6195
- var import_fiber5 = require("@react-three/fiber");
6196
- var import_react14 = require("react");
6210
+ var import_fiber6 = require("@react-three/fiber");
6211
+ var import_react15 = require("react");
6197
6212
  var import_jsx_runtime9 = require("react/jsx-runtime");
6198
6213
  var PICK_BEHAVIORS = /* @__PURE__ */ new Set(["peel", "carry"]);
6199
6214
  function InteractiveField(props) {
@@ -6201,15 +6216,15 @@ function InteractiveField(props) {
6201
6216
  const total = papers.length;
6202
6217
  const layout = getLayout(layoutId);
6203
6218
  const reduced = usePrefersReducedMotion(props.reducedMotion);
6204
- const contextRegistry = (0, import_react14.useContext)(DropZoneContext);
6205
- const registry4 = (0, import_react14.useMemo)(() => contextRegistry ?? new DropZoneRegistry(), [contextRegistry]);
6206
- const camera = (0, import_fiber5.useThree)((s) => s.camera);
6207
- const gl = (0, import_fiber5.useThree)((s) => s.gl);
6208
- const controls = (0, import_fiber5.useThree)((s) => s.controls);
6209
- const [removed, setRemoved] = (0, import_react14.useState)(EMPTY_SET);
6210
- const [slotPatches, setSlotPatches] = (0, import_react14.useState)({});
6211
- const [slotStates, setSlotStates] = (0, import_react14.useState)({});
6212
- const slotConfigs = (0, import_react14.useMemo)(
6219
+ const contextRegistry = (0, import_react15.useContext)(DropZoneContext);
6220
+ const registry4 = (0, import_react15.useMemo)(() => contextRegistry ?? new DropZoneRegistry(), [contextRegistry]);
6221
+ const camera = (0, import_fiber6.useThree)((s) => s.camera);
6222
+ const gl = (0, import_fiber6.useThree)((s) => s.gl);
6223
+ const controls = (0, import_fiber6.useThree)((s) => s.controls);
6224
+ const [removed, setRemoved] = (0, import_react15.useState)(EMPTY_SET);
6225
+ const [slotPatches, setSlotPatches] = (0, import_react15.useState)({});
6226
+ const [slotStates, setSlotStates] = (0, import_react15.useState)({});
6227
+ const slotConfigs = (0, import_react15.useMemo)(
6213
6228
  () => papers.map((slot, i) => {
6214
6229
  const config = resolveFieldSlotConfig(slot, fallback, i, layoutId, layoutOptions);
6215
6230
  const patch = slotPatches[i];
@@ -6223,14 +6238,14 @@ function InteractiveField(props) {
6223
6238
  slotPatches
6224
6239
  ]
6225
6240
  );
6226
- const poses = (0, import_react14.useMemo)(
6241
+ const poses = (0, import_react15.useMemo)(
6227
6242
  () => slotConfigs.map((_, i) => layout.pose(i, total, layoutOptions, 0, props.sheet)),
6228
6243
  [layoutId, JSON.stringify(layoutOptions), total, props.sheet.width, props.sheet.height]
6229
6244
  );
6230
- const groupRefs = (0, import_react14.useRef)([]);
6231
- const handleRefs = (0, import_react14.useRef)([]);
6232
- const pressRef = (0, import_react14.useRef)(null);
6233
- const carriedRef = (0, import_react14.useRef)(null);
6245
+ const groupRefs = (0, import_react15.useRef)([]);
6246
+ const handleRefs = (0, import_react15.useRef)([]);
6247
+ const pressRef = (0, import_react15.useRef)(null);
6248
+ const carriedRef = (0, import_react15.useRef)(null);
6234
6249
  const zonesLive = () => [...props.zones ?? [], ...registry4.list()];
6235
6250
  const slotName = (i) => {
6236
6251
  const preset = papers[i]?.preset ?? fallback;
@@ -6243,10 +6258,10 @@ function InteractiveField(props) {
6243
6258
  }
6244
6259
  props.onSlotStateChange?.(i, state);
6245
6260
  };
6246
- const raycaster = (0, import_react14.useMemo)(() => new THREE15.Raycaster(), []);
6247
- const planeScratch = (0, import_react14.useMemo)(() => new THREE15.Plane(new THREE15.Vector3(0, 0, 1), 0), []);
6248
- const pointScratch = (0, import_react14.useMemo)(() => new THREE15.Vector3(), []);
6249
- const ndcScratch = (0, import_react14.useMemo)(() => new THREE15.Vector2(), []);
6261
+ const raycaster = (0, import_react15.useMemo)(() => new THREE15.Raycaster(), []);
6262
+ const planeScratch = (0, import_react15.useMemo)(() => new THREE15.Plane(new THREE15.Vector3(0, 0, 1), 0), []);
6263
+ const pointScratch = (0, import_react15.useMemo)(() => new THREE15.Vector3(), []);
6264
+ const ndcScratch = (0, import_react15.useMemo)(() => new THREE15.Vector2(), []);
6250
6265
  const planePoint = (clientX, clientY, planeZ) => {
6251
6266
  const rect = gl.domElement.getBoundingClientRect();
6252
6267
  ndcScratch.set(
@@ -6376,7 +6391,7 @@ function InteractiveField(props) {
6376
6391
  import_gsap4.gsap.to(group.rotation, { x: home.rotation[0], y: home.rotation[1], z: home.rotation[2], duration });
6377
6392
  import_gsap4.gsap.to(group.scale, { x: home.scale, y: home.scale, z: home.scale, duration: duration * 0.5 });
6378
6393
  };
6379
- const onPointerMoveRef = (0, import_react14.useRef)(() => {
6394
+ const onPointerMoveRef = (0, import_react15.useRef)(() => {
6380
6395
  });
6381
6396
  onPointerMoveRef.current = (e) => {
6382
6397
  const press = pressRef.current;
@@ -6400,7 +6415,7 @@ function InteractiveField(props) {
6400
6415
  pick(slot, hit[0], hit[1], pointerId);
6401
6416
  }
6402
6417
  };
6403
- const onPointerUpRef = (0, import_react14.useRef)(() => {
6418
+ const onPointerUpRef = (0, import_react15.useRef)(() => {
6404
6419
  });
6405
6420
  onPointerUpRef.current = (e) => {
6406
6421
  pressRef.current = null;
@@ -6414,7 +6429,7 @@ function InteractiveField(props) {
6414
6429
  if (zone) settleInto(carried, zone);
6415
6430
  else returnHome(carried);
6416
6431
  };
6417
- (0, import_react14.useEffect)(() => {
6432
+ (0, import_react15.useEffect)(() => {
6418
6433
  const move = (e) => onPointerMoveRef.current(e);
6419
6434
  const up = (e) => onPointerUpRef.current(e);
6420
6435
  window.addEventListener("pointermove", move);
@@ -6424,7 +6439,7 @@ function InteractiveField(props) {
6424
6439
  window.removeEventListener("pointerup", up);
6425
6440
  };
6426
6441
  }, []);
6427
- (0, import_fiber5.useFrame)((_, delta) => {
6442
+ (0, import_fiber6.useFrame)((_, delta) => {
6428
6443
  const carried = carriedRef.current;
6429
6444
  if (!carried || carried.settling) return;
6430
6445
  const group = groupRefs.current[carried.slot];
@@ -6462,7 +6477,7 @@ function InteractiveField(props) {
6462
6477
  group.scale.x += (targetScale - group.scale.x) * 0.15;
6463
6478
  group.scale.y = group.scale.z = group.scale.x;
6464
6479
  });
6465
- const a11yImplRef = (0, import_react14.useRef)(null);
6480
+ const a11yImplRef = (0, import_react15.useRef)(null);
6466
6481
  a11yImplRef.current = {
6467
6482
  pick: (slot) => {
6468
6483
  const home = poses[slot];
@@ -6487,7 +6502,7 @@ function InteractiveField(props) {
6487
6502
  zoneIds: () => zonesLive().map((z30) => z30.id),
6488
6503
  slotState: (slot) => slotStates[slot] ?? "rest"
6489
6504
  };
6490
- (0, import_react14.useEffect)(() => {
6505
+ (0, import_react15.useEffect)(() => {
6491
6506
  const ref = props.a11yRef;
6492
6507
  if (!ref) return;
6493
6508
  ref.current = {
@@ -6540,7 +6555,7 @@ function InteractiveField(props) {
6540
6555
  }
6541
6556
 
6542
6557
  // src/field/keyboardMirror.tsx
6543
- var import_react15 = require("react");
6558
+ var import_react16 = require("react");
6544
6559
  var import_jsx_runtime10 = require("react/jsx-runtime");
6545
6560
  function fieldKeyboardStep(carry2, slot, key, controller) {
6546
6561
  if (!carry2) {
@@ -6586,7 +6601,7 @@ function FieldKeyboardMirror({
6586
6601
  papers,
6587
6602
  controller
6588
6603
  }) {
6589
- const [carrying, setCarrying] = (0, import_react15.useState)(null);
6604
+ const [carrying, setCarrying] = (0, import_react16.useState)(null);
6590
6605
  const paperLabel = (slot, i) => {
6591
6606
  try {
6592
6607
  const config = resolveConfig({ preset: slot.preset });
@@ -6689,16 +6704,16 @@ function fitCamera(layout, n, options, sheet2, fovDeg, aspect, margin = 1.06) {
6689
6704
 
6690
6705
  // src/PaperField.tsx
6691
6706
  var import_jsx_runtime11 = require("react/jsx-runtime");
6692
- var PaperFieldMesh = (0, import_react16.forwardRef)(
6707
+ var PaperFieldMesh = (0, import_react17.forwardRef)(
6693
6708
  function PaperFieldMesh2(props, ref) {
6694
6709
  const reduced = usePrefersReducedMotion(props.reducedMotion);
6695
- const papers = (0, import_react16.useMemo)(
6710
+ const papers = (0, import_react17.useMemo)(
6696
6711
  () => effectiveFieldPapers(props.papers, props.images),
6697
6712
  [props.papers, props.images]
6698
6713
  );
6699
6714
  const stablePapers = useStable(papers);
6700
6715
  const stablePreset = useStable(props.preset ?? null);
6701
- const groups = (0, import_react16.useMemo)(
6716
+ const groups = (0, import_react17.useMemo)(
6702
6717
  () => groupFieldPapers(stablePapers, stablePreset ?? void 0),
6703
6718
  [stablePapers, stablePreset]
6704
6719
  );
@@ -6707,22 +6722,22 @@ var PaperFieldMesh = (0, import_react16.forwardRef)(
6707
6722
  const layout = getLayout(layoutId);
6708
6723
  const firstSheet = groups[0]?.config.sheet;
6709
6724
  const stableLayoutOptions = useStable(props.layoutOptions ?? {});
6710
- const layoutOptions = (0, import_react16.useMemo)(
6725
+ const layoutOptions = (0, import_react17.useMemo)(
6711
6726
  // Sheet grids size their cells from the papers themselves — gutter is
6712
6727
  // then literally the spacing between stamps (explicit cell dims win).
6713
6728
  () => resolveLayoutOptions(layoutId, layout, stableLayoutOptions, firstSheet),
6714
6729
  [layoutId, stableLayoutOptions, firstSheet?.width, firstSheet?.height]
6715
6730
  );
6716
- const phaseRef = (0, import_react16.useRef)(0);
6717
- const dragVelRef = (0, import_react16.useRef)(0);
6718
- const mountTimeRef = (0, import_react16.useRef)(-1);
6719
- const morphRef = (0, import_react16.useRef)({ from: null, t: 1 });
6720
- const prevLayout = (0, import_react16.useRef)({ id: layoutId, options: layoutOptions });
6721
- const gl = (0, import_fiber6.useThree)((s) => s.gl);
6731
+ const phaseRef = (0, import_react17.useRef)(0);
6732
+ const dragVelRef = (0, import_react17.useRef)(0);
6733
+ const mountTimeRef = (0, import_react17.useRef)(-1);
6734
+ const morphRef = (0, import_react17.useRef)({ from: null, t: 1 });
6735
+ const prevLayout = (0, import_react17.useRef)({ id: layoutId, options: layoutOptions });
6736
+ const gl = (0, import_fiber7.useThree)((s) => s.gl);
6722
6737
  const driver = reduced ? "none" : props.motion?.driver ?? "autoplay";
6723
6738
  const speed = props.motion?.speed ?? 0.5;
6724
6739
  const entranceType = reduced ? "none" : props.entrance?.type ?? "rise";
6725
- (0, import_react16.useEffect)(() => {
6740
+ (0, import_react17.useEffect)(() => {
6726
6741
  const prev = prevLayout.current;
6727
6742
  if (prev.id !== layoutId || JSON.stringify(prev.options) !== JSON.stringify(layoutOptions)) {
6728
6743
  morphRef.current = { from: prev, t: 0 };
@@ -6730,7 +6745,7 @@ var PaperFieldMesh = (0, import_react16.forwardRef)(
6730
6745
  prevLayout.current = { id: layoutId, options: layoutOptions };
6731
6746
  }
6732
6747
  }, [layoutId, layoutOptions]);
6733
- (0, import_react16.useEffect)(() => {
6748
+ (0, import_react17.useEffect)(() => {
6734
6749
  if (driver !== "drag") return;
6735
6750
  const el = gl.domElement;
6736
6751
  let lastX = null;
@@ -6756,7 +6771,7 @@ var PaperFieldMesh = (0, import_react16.forwardRef)(
6756
6771
  window.removeEventListener("pointerup", up);
6757
6772
  };
6758
6773
  }, [driver, gl]);
6759
- (0, import_fiber6.useFrame)(({ clock }, delta) => {
6774
+ (0, import_fiber7.useFrame)(({ clock }, delta) => {
6760
6775
  if (mountTimeRef.current < 0) mountTimeRef.current = clock.elapsedTime;
6761
6776
  if (driver === "autoplay") phaseRef.current += delta * speed * 0.02;
6762
6777
  if (driver === "drag") {
@@ -6814,14 +6829,14 @@ var PaperFieldMesh = (0, import_react16.forwardRef)(
6814
6829
  }
6815
6830
  );
6816
6831
  function FitCamera(meshProps) {
6817
- const camera = (0, import_fiber6.useThree)((s) => s.camera);
6818
- const width = (0, import_fiber6.useThree)((s) => s.size.width);
6819
- const height = (0, import_fiber6.useThree)((s) => s.size.height);
6832
+ const camera = (0, import_fiber7.useThree)((s) => s.camera);
6833
+ const width = (0, import_fiber7.useThree)((s) => s.size.width);
6834
+ const height = (0, import_fiber7.useThree)((s) => s.size.height);
6820
6835
  const papersProp = useStable(meshProps.papers ?? null);
6821
6836
  const imagesProp = useStable(meshProps.images ?? null);
6822
6837
  const presetProp = useStable(meshProps.preset ?? null);
6823
6838
  const optionsProp = useStable(meshProps.layoutOptions ?? {});
6824
- const field = (0, import_react16.useMemo)(() => {
6839
+ const field = (0, import_react17.useMemo)(() => {
6825
6840
  const papers = effectiveFieldPapers(papersProp ?? void 0, imagesProp ?? void 0);
6826
6841
  const layoutId = meshProps.layout ?? "ring";
6827
6842
  const layout = getLayout(layoutId);
@@ -6829,7 +6844,7 @@ function FitCamera(meshProps) {
6829
6844
  const options = resolveLayoutOptions(layoutId, layout, optionsProp, sheet2);
6830
6845
  return { layout, n: papers.length, options, sheet: sheet2 ?? DEFAULT_SHEET };
6831
6846
  }, [papersProp, imagesProp, presetProp, meshProps.layout, optionsProp]);
6832
- (0, import_react16.useEffect)(() => {
6847
+ (0, import_react17.useEffect)(() => {
6833
6848
  if (!(camera instanceof THREE16.PerspectiveCamera)) return;
6834
6849
  const { position, target } = fitCamera(
6835
6850
  field.layout,
@@ -6845,27 +6860,28 @@ function FitCamera(meshProps) {
6845
6860
  }, [camera, width, height, field]);
6846
6861
  return null;
6847
6862
  }
6848
- var PaperField = (0, import_react16.forwardRef)(function PaperField2({ children, className, style, scene, ...meshProps }, ref) {
6863
+ var PaperField = (0, import_react17.forwardRef)(function PaperField2({ children, className, style, scene, ...meshProps }, ref) {
6849
6864
  const rig = sceneSchema.parse(scene ?? {});
6850
- const registry4 = (0, import_react16.useMemo)(() => new DropZoneRegistry(), []);
6851
- const a11yRef = (0, import_react16.useRef)(null);
6852
- const papers = (0, import_react16.useMemo)(
6865
+ const registry4 = (0, import_react17.useMemo)(() => new DropZoneRegistry(), []);
6866
+ const a11yRef = (0, import_react17.useRef)(null);
6867
+ const papers = (0, import_react17.useMemo)(
6853
6868
  () => effectiveFieldPapers(meshProps.papers, meshProps.images),
6854
6869
  [meshProps.papers, meshProps.images]
6855
6870
  );
6856
6871
  const stablePapers = useStable(papers);
6857
6872
  const stablePreset = useStable(meshProps.preset ?? null);
6858
- const interactive = (0, import_react16.useMemo)(
6873
+ const interactive = (0, import_react17.useMemo)(
6859
6874
  () => fieldIsInteractive(stablePapers, stablePreset ?? void 0, meshProps.interactive),
6860
6875
  [stablePapers, stablePreset, meshProps.interactive]
6861
6876
  );
6862
6877
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className, style: { width: "100%", height: "100%", ...style }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(DropZoneContext.Provider, { value: registry4, children: [
6863
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_fiber6.Canvas, { shadows: true, camera: { position: [0, 0.6, 5.2], fov: 45 }, dpr: [1, 2], children: [
6878
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_fiber7.Canvas, { shadows: true, camera: { position: [0, 0.6, 5.2], fov: 45 }, dpr: [1, 2], children: [
6864
6879
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(FitCamera, { ...meshProps }),
6865
6880
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PaperBackdrop, { backdrop: rig.backdrop }),
6866
6881
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PaperLighting, { preset: rig.lighting, light: rig.light, floor: -2.4, scale: 14 }),
6867
6882
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PaperFieldMesh, { ref, a11yControllerRef: a11yRef, ...meshProps }),
6868
- children
6883
+ children,
6884
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ReleaseContextOnUnmount, {})
6869
6885
  ] }),
6870
6886
  interactive && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(FieldKeyboardMirror, { papers, controller: a11yRef })
6871
6887
  ] }) });
@@ -6962,13 +6978,13 @@ function stageCamera(path, walked, scale, options) {
6962
6978
 
6963
6979
  // src/stage/Figure.tsx
6964
6980
  var THREE18 = __toESM(require("three"), 1);
6965
- var import_react18 = require("react");
6966
- var import_fiber8 = require("@react-three/fiber");
6981
+ var import_react19 = require("react");
6982
+ var import_fiber9 = require("@react-three/fiber");
6967
6983
 
6968
6984
  // src/stage/RiggedFigure.tsx
6969
6985
  var import_drei2 = require("@react-three/drei");
6970
- var import_fiber7 = require("@react-three/fiber");
6971
- var import_react17 = require("react");
6986
+ var import_fiber8 = require("@react-three/fiber");
6987
+ var import_react18 = require("react");
6972
6988
  var THREE17 = __toESM(require("three"), 1);
6973
6989
  var import_SkeletonUtils = require("three/examples/jsm/utils/SkeletonUtils.js");
6974
6990
 
@@ -7144,7 +7160,7 @@ function placeFigure(path, distance, o) {
7144
7160
 
7145
7161
  // src/stage/RiggedFigure.tsx
7146
7162
  var import_jsx_runtime12 = require("react/jsx-runtime");
7147
- var ModelBoundary = class extends import_react17.Component {
7163
+ var ModelBoundary = class extends import_react18.Component {
7148
7164
  state = { failed: false };
7149
7165
  static getDerivedStateFromError() {
7150
7166
  return { failed: true };
@@ -7158,14 +7174,14 @@ var ModelBoundary = class extends import_react17.Component {
7158
7174
  };
7159
7175
  function Rigged({ url, options, distance, frozen }) {
7160
7176
  const gltf = (0, import_drei2.useGLTF)(url);
7161
- const scene = (0, import_react17.useMemo)(() => (0, import_SkeletonUtils.clone)(gltf.scene), [gltf.scene]);
7177
+ const scene = (0, import_react18.useMemo)(() => (0, import_SkeletonUtils.clone)(gltf.scene), [gltf.scene]);
7162
7178
  const silhouette = options.finish === "silhouette";
7163
- const material = (0, import_react17.useMemo)(
7179
+ const material = (0, import_react18.useMemo)(
7164
7180
  () => silhouette ? new THREE17.MeshBasicMaterial({ color: options.color, toneMapped: false }) : null,
7165
7181
  [silhouette, options.color]
7166
7182
  );
7167
- (0, import_react17.useEffect)(() => () => material?.dispose(), [material]);
7168
- const scale = (0, import_react17.useMemo)(() => {
7183
+ (0, import_react18.useEffect)(() => () => material?.dispose(), [material]);
7184
+ const scale = (0, import_react18.useMemo)(() => {
7169
7185
  scene.traverse((child) => {
7170
7186
  const mesh = child;
7171
7187
  if (!mesh.isMesh) return;
@@ -7179,13 +7195,13 @@ function Rigged({ url, options, distance, frozen }) {
7179
7195
  const height = box.max.y - box.min.y;
7180
7196
  return height > 0 ? options.height / height : 1;
7181
7197
  }, [scene, material, silhouette, options.height]);
7182
- const mixer = (0, import_react17.useMemo)(() => new THREE17.AnimationMixer(scene), [scene]);
7183
- const clip = (0, import_react17.useMemo)(() => {
7198
+ const mixer = (0, import_react18.useMemo)(() => new THREE17.AnimationMixer(scene), [scene]);
7199
+ const clip = (0, import_react18.useMemo)(() => {
7184
7200
  const names = gltf.animations.map((a) => a.name);
7185
7201
  const wanted = frozen ? pickStillClip(names) : pickClip(names, isRunning(options));
7186
7202
  return gltf.animations.find((a) => a.name === wanted) ?? gltf.animations[0];
7187
7203
  }, [gltf.animations, options, frozen]);
7188
- (0, import_react17.useEffect)(() => {
7204
+ (0, import_react18.useEffect)(() => {
7189
7205
  if (!clip) return;
7190
7206
  mixer.clipAction(clip).play();
7191
7207
  return () => {
@@ -7193,7 +7209,7 @@ function Rigged({ url, options, distance, frozen }) {
7193
7209
  mixer.uncacheClip(clip);
7194
7210
  };
7195
7211
  }, [mixer, clip]);
7196
- (0, import_fiber7.useFrame)(() => {
7212
+ (0, import_fiber8.useFrame)(() => {
7197
7213
  if (!clip) return;
7198
7214
  mixer.setTime(frozen ? 0 : clipTimeFor(distance.current, options, clip.duration));
7199
7215
  });
@@ -7212,29 +7228,29 @@ function Segment({ length, radius, material }) {
7212
7228
  function Figure({ path, figure, distance, distanceRef, walkLength, frozen }) {
7213
7229
  const reducedMotion = usePrefersReducedMotion();
7214
7230
  const still = frozen ?? reducedMotion;
7215
- const options = (0, import_react18.useMemo)(() => figureSchema.parse(figure ?? {}), [figure]);
7216
- const walk = (0, import_react18.useMemo)(() => getWalkPath(walkPathSchema.parse(path ?? {})), [path]);
7217
- const root = (0, import_react18.useRef)(null);
7218
- const walkedRef = (0, import_react18.useRef)(0);
7219
- const hips = (0, import_react18.useRef)(null);
7220
- const chest = (0, import_react18.useRef)(null);
7221
- const legL = (0, import_react18.useRef)(null);
7222
- const legR = (0, import_react18.useRef)(null);
7223
- const kneeL = (0, import_react18.useRef)(null);
7224
- const kneeR = (0, import_react18.useRef)(null);
7225
- const armL = (0, import_react18.useRef)(null);
7226
- const armR = (0, import_react18.useRef)(null);
7227
- const elbowL = (0, import_react18.useRef)(null);
7228
- const elbowR = (0, import_react18.useRef)(null);
7229
- const material = (0, import_react18.useMemo)(
7231
+ const options = (0, import_react19.useMemo)(() => figureSchema.parse(figure ?? {}), [figure]);
7232
+ const walk = (0, import_react19.useMemo)(() => getWalkPath(walkPathSchema.parse(path ?? {})), [path]);
7233
+ const root = (0, import_react19.useRef)(null);
7234
+ const walkedRef = (0, import_react19.useRef)(0);
7235
+ const hips = (0, import_react19.useRef)(null);
7236
+ const chest = (0, import_react19.useRef)(null);
7237
+ const legL = (0, import_react19.useRef)(null);
7238
+ const legR = (0, import_react19.useRef)(null);
7239
+ const kneeL = (0, import_react19.useRef)(null);
7240
+ const kneeR = (0, import_react19.useRef)(null);
7241
+ const armL = (0, import_react19.useRef)(null);
7242
+ const armR = (0, import_react19.useRef)(null);
7243
+ const elbowL = (0, import_react19.useRef)(null);
7244
+ const elbowR = (0, import_react19.useRef)(null);
7245
+ const material = (0, import_react19.useMemo)(
7230
7246
  () => new THREE18.MeshBasicMaterial({ color: options.color, toneMapped: false }),
7231
7247
  [options.color]
7232
7248
  );
7233
- (0, import_react18.useEffect)(() => () => material.dispose(), [material]);
7249
+ (0, import_react19.useEffect)(() => () => material.dispose(), [material]);
7234
7250
  const h = options.height;
7235
7251
  const p = PROPORTIONS;
7236
7252
  const torso = (p.shoulder - p.hip) * h;
7237
- (0, import_fiber8.useFrame)((state) => {
7253
+ (0, import_fiber9.useFrame)((state) => {
7238
7254
  const walked = distanceRef !== void 0 ? distanceRef.current * (walkLength ?? walk.length) : distance ?? (still ? 0 : state.clock.elapsedTime * options.speed);
7239
7255
  walkedRef.current = walked;
7240
7256
  const { position, yaw, pose } = placeFigure(walk, walked, options);
@@ -7289,7 +7305,7 @@ function Figure({ path, figure, distance, distanceRef, walkLength, frozen }) {
7289
7305
  // The capsules are both the fallback and the thing being replaced, so
7290
7306
  // a model that is still downloading shows a walking figure rather than
7291
7307
  // a hole, and one that never arrives leaves the stage as it was.
7292
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react18.Suspense, { fallback: capsules, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7308
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react19.Suspense, { fallback: capsules, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7293
7309
  RiggedFigure,
7294
7310
  {
7295
7311
  url: options.model,
@@ -7304,7 +7320,7 @@ function Figure({ path, figure, distance, distanceRef, walkLength, frozen }) {
7304
7320
 
7305
7321
  // src/stage/Surround.tsx
7306
7322
  var THREE19 = __toESM(require("three"), 1);
7307
- var import_react19 = require("react");
7323
+ var import_react20 = require("react");
7308
7324
  var import_jsx_runtime14 = require("react/jsx-runtime");
7309
7325
  function makeSkyTexture(sky) {
7310
7326
  const canvas = document.createElement("canvas");
@@ -7333,8 +7349,7 @@ function makeGlowTexture(color) {
7333
7349
  const ctx = canvas.getContext("2d");
7334
7350
  const glow = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
7335
7351
  const c = new THREE19.Color(color);
7336
- const rgb = `${c.r * 255 | 0}, ${c.g * 255 | 0}, ${c.b * 255 | 0}`;
7337
- for (const [stop, alpha] of [
7352
+ for (const [stop, level] of [
7338
7353
  [0, 1],
7339
7354
  [0.5, 1],
7340
7355
  [0.62, 0.66],
@@ -7343,7 +7358,8 @@ function makeGlowTexture(color) {
7343
7358
  [0.94, 0.03],
7344
7359
  [1, 0]
7345
7360
  ]) {
7346
- glow.addColorStop(stop, `rgba(${rgb}, ${alpha})`);
7361
+ const scaled = `${c.r * 255 * level | 0}, ${c.g * 255 * level | 0}, ${c.b * 255 * level | 0}`;
7362
+ glow.addColorStop(stop, `rgb(${scaled})`);
7347
7363
  }
7348
7364
  ctx.fillStyle = glow;
7349
7365
  ctx.fillRect(0, 0, size, size);
@@ -7359,8 +7375,8 @@ function Source({
7359
7375
  color,
7360
7376
  intensity = SOURCE_INTENSITY
7361
7377
  }) {
7362
- const texture = (0, import_react19.useMemo)(() => makeGlowTexture(color), [color]);
7363
- (0, import_react19.useEffect)(() => () => texture.dispose(), [texture]);
7378
+ const texture = (0, import_react20.useMemo)(() => makeGlowTexture(color), [color]);
7379
+ (0, import_react20.useEffect)(() => () => texture.dispose(), [texture]);
7364
7380
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("mesh", { position, rotation: [0, yaw, 0], children: [
7365
7381
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("planeGeometry", { args: [size * 2.4, size * 1.8] }),
7366
7382
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
@@ -7368,6 +7384,7 @@ function Source({
7368
7384
  {
7369
7385
  map: texture,
7370
7386
  transparent: true,
7387
+ blending: THREE19.AdditiveBlending,
7371
7388
  color: new THREE19.Color(intensity, intensity, intensity),
7372
7389
  depthWrite: false,
7373
7390
  fog: false
@@ -7377,8 +7394,8 @@ function Source({
7377
7394
  }
7378
7395
  function Surround({ radius, sky }) {
7379
7396
  const { zenith, horizon, ground } = sky;
7380
- const texture = (0, import_react19.useMemo)(() => makeSkyTexture({ zenith, horizon, ground }), [zenith, horizon, ground]);
7381
- (0, import_react19.useEffect)(() => () => texture.dispose(), [texture]);
7397
+ const texture = (0, import_react20.useMemo)(() => makeSkyTexture({ zenith, horizon, ground }), [zenith, horizon, ground]);
7398
+ (0, import_react20.useEffect)(() => () => texture.dispose(), [texture]);
7382
7399
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("mesh", { children: [
7383
7400
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("sphereGeometry", { args: [radius, 32, 24] }),
7384
7401
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("meshBasicMaterial", { map: texture, side: THREE19.BackSide, fog: false })
@@ -7387,7 +7404,7 @@ function Surround({ radius, sky }) {
7387
7404
 
7388
7405
  // src/stage/Room.tsx
7389
7406
  var THREE20 = __toESM(require("three"), 1);
7390
- var import_react20 = require("react");
7407
+ var import_react21 = require("react");
7391
7408
  var import_jsx_runtime15 = require("react/jsx-runtime");
7392
7409
  function makeFloorTexture(color, repeats) {
7393
7410
  const size = 256;
@@ -7419,8 +7436,8 @@ function Floor({
7419
7436
  slab
7420
7437
  }) {
7421
7438
  const repeats = slab > 0 ? Math.max(1, Math.round(size / slab)) : 0;
7422
- const texture = (0, import_react20.useMemo)(() => repeats > 0 ? makeFloorTexture(color, repeats) : null, [color, repeats]);
7423
- (0, import_react20.useEffect)(() => () => texture?.dispose(), [texture]);
7439
+ const texture = (0, import_react21.useMemo)(() => repeats > 0 ? makeFloorTexture(color, repeats) : null, [color, repeats]);
7440
+ (0, import_react21.useEffect)(() => () => texture?.dispose(), [texture]);
7424
7441
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("mesh", { rotation: [-Math.PI / 2, 0, 0], receiveShadow: true, children: [
7425
7442
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("planeGeometry", { args: [size, size] }),
7426
7443
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("meshStandardMaterial", { map: texture, color: texture ? "#ffffff" : color, roughness: 1 })
@@ -7440,7 +7457,7 @@ function Columns({
7440
7457
  offset,
7441
7458
  color
7442
7459
  }) {
7443
- const placements = (0, import_react20.useMemo)(() => {
7460
+ const placements = (0, import_react21.useMemo)(() => {
7444
7461
  if (!(spacing > 0) || !(path.length > 0)) return [];
7445
7462
  const bays = Math.max(1, Math.round(path.length / spacing));
7446
7463
  const out = [];
@@ -7456,12 +7473,12 @@ function Columns({
7456
7473
  }
7457
7474
  return out;
7458
7475
  }, [path, spacing, offset]);
7459
- const shaft = (0, import_react20.useRef)(null);
7460
- const base = (0, import_react20.useRef)(null);
7461
- const capital = (0, import_react20.useRef)(null);
7476
+ const shaft = (0, import_react21.useRef)(null);
7477
+ const base = (0, import_react21.useRef)(null);
7478
+ const capital = (0, import_react21.useRef)(null);
7462
7479
  const plate = width * 1.45;
7463
7480
  const plateHeight = width * 0.24;
7464
- (0, import_react20.useEffect)(() => {
7481
+ (0, import_react21.useEffect)(() => {
7465
7482
  const m = new THREE20.Matrix4();
7466
7483
  const q = new THREE20.Quaternion();
7467
7484
  const scale = new THREE20.Vector3(1, 1, 1);
@@ -7505,7 +7522,7 @@ function Doorway({
7505
7522
  color,
7506
7523
  extent
7507
7524
  }) {
7508
- const geometry = (0, import_react20.useMemo)(() => {
7525
+ const geometry = (0, import_react21.useMemo)(() => {
7509
7526
  const w = size * 2.4 * opening;
7510
7527
  const h = size * 1.8 * opening;
7511
7528
  const shape = new THREE20.Shape();
@@ -7523,8 +7540,8 @@ function Doorway({
7523
7540
  shape.holes.push(hole);
7524
7541
  return new THREE20.ShapeGeometry(shape);
7525
7542
  }, [size, opening, extent]);
7526
- (0, import_react20.useEffect)(() => () => geometry.dispose(), [geometry]);
7527
- const stood = (0, import_react20.useMemo)(
7543
+ (0, import_react21.useEffect)(() => () => geometry.dispose(), [geometry]);
7544
+ const stood = (0, import_react21.useMemo)(
7528
7545
  () => [position[0] + Math.sin(yaw) * NUDGE, position[1], position[2] + Math.cos(yaw) * NUDGE],
7529
7546
  [position, yaw]
7530
7547
  );
@@ -7533,7 +7550,7 @@ function Doorway({
7533
7550
 
7534
7551
  // src/stage/Suspension.tsx
7535
7552
  var THREE21 = __toESM(require("three"), 1);
7536
- var import_react21 = require("react");
7553
+ var import_react22 = require("react");
7537
7554
  var import_jsx_runtime16 = require("react/jsx-runtime");
7538
7555
  var euler = (pose) => new THREE21.Euler(pose.rotation[0], pose.rotation[1], pose.rotation[2]);
7539
7556
  function rodLength(sheet2, pose) {
@@ -7557,13 +7574,13 @@ function Suspension({
7557
7574
  type,
7558
7575
  hardware
7559
7576
  }) {
7560
- const poses = (0, import_react21.useMemo)(() => {
7577
+ const poses = (0, import_react22.useMemo)(() => {
7561
7578
  const entry = getLayout(layout);
7562
7579
  if (!entry) return [];
7563
7580
  const options = entry.optionsSchema.parse(layoutOptions);
7564
7581
  return Array.from({ length: count }, (_, i) => entry.pose(i, count, options, 0, sheet2));
7565
7582
  }, [layout, layoutOptions, count, sheet2]);
7566
- const geometry = (0, import_react21.useMemo)(() => {
7583
+ const geometry = (0, import_react22.useMemo)(() => {
7567
7584
  const points = [];
7568
7585
  const end = new THREE21.Vector3();
7569
7586
  for (const pose of poses) {
@@ -7583,17 +7600,17 @@ function Suspension({
7583
7600
  g.setAttribute("position", new THREE21.Float32BufferAttribute(points, 3));
7584
7601
  return g;
7585
7602
  }, [poses, paperHeight, ceiling, type, sheet2]);
7586
- (0, import_react21.useEffect)(() => () => geometry.dispose(), [geometry]);
7587
- const clipRef = (0, import_react21.useRef)(null);
7588
- const rodRef = (0, import_react21.useRef)(null);
7589
- const rodGeometry = (0, import_react21.useMemo)(() => {
7603
+ (0, import_react22.useEffect)(() => () => geometry.dispose(), [geometry]);
7604
+ const clipRef = (0, import_react22.useRef)(null);
7605
+ const rodRef = (0, import_react22.useRef)(null);
7606
+ const rodGeometry = (0, import_react22.useMemo)(() => {
7590
7607
  const r = sheet2.width * 0.018;
7591
7608
  const g = new THREE21.CylinderGeometry(r, r, sheet2.width * 1.22, 8);
7592
7609
  g.rotateZ(Math.PI / 2);
7593
7610
  return g;
7594
7611
  }, [sheet2.width]);
7595
- (0, import_react21.useEffect)(() => () => rodGeometry.dispose(), [rodGeometry]);
7596
- (0, import_react21.useEffect)(() => {
7612
+ (0, import_react22.useEffect)(() => () => rodGeometry.dispose(), [rodGeometry]);
7613
+ (0, import_react22.useEffect)(() => {
7597
7614
  const m = new THREE21.Matrix4();
7598
7615
  const q = new THREE21.Quaternion();
7599
7616
  const at = new THREE21.Vector3();
@@ -7984,8 +8001,8 @@ function travelBetween(from, to, t, closed) {
7984
8001
  }
7985
8002
 
7986
8003
  // src/stage/useWalk.ts
7987
- var import_react22 = require("react");
7988
- var import_fiber9 = require("@react-three/fiber");
8004
+ var import_react23 = require("react");
8005
+ var import_fiber10 = require("@react-three/fiber");
7989
8006
  function useWalk({
7990
8007
  path,
7991
8008
  motion,
@@ -7995,15 +8012,15 @@ function useWalk({
7995
8012
  reduced,
7996
8013
  onProgress
7997
8014
  }) {
7998
- const gl = (0, import_fiber9.useThree)((s) => s.gl);
7999
- const walk = (0, import_react22.useRef)(progress ?? 0);
8000
- const velocity = (0, import_react22.useRef)(0);
8001
- const dragged = (0, import_react22.useRef)(false);
8002
- const travel = (0, import_react22.useRef)(null);
8015
+ const gl = (0, import_fiber10.useThree)((s) => s.gl);
8016
+ const walk = (0, import_react23.useRef)(progress ?? 0);
8017
+ const velocity = (0, import_react23.useRef)(0);
8018
+ const dragged = (0, import_react23.useRef)(false);
8019
+ const travel = (0, import_react23.useRef)(null);
8003
8020
  const controlled = progress !== void 0;
8004
8021
  const interactive = !controlled && motion.driver === "drag";
8005
- const engaged = (0, import_react22.useRef)(false);
8006
- const travelTo = (0, import_react22.useCallback)(
8022
+ const engaged = (0, import_react23.useRef)(false);
8023
+ const travelTo = (0, import_react23.useCallback)(
8007
8024
  (target) => {
8008
8025
  engaged.current = true;
8009
8026
  velocity.current = 0;
@@ -8017,14 +8034,14 @@ function useWalk({
8017
8034
  },
8018
8035
  [path.closed, reduced]
8019
8036
  );
8020
- const step = (0, import_react22.useCallback)(
8037
+ const step = (0, import_react23.useCallback)(
8021
8038
  (direction) => {
8022
8039
  const from = travel.current?.to ?? walk.current;
8023
8040
  travelTo(nextStop(stops, from, direction, path.closed));
8024
8041
  },
8025
8042
  [stops, path.closed, travelTo]
8026
8043
  );
8027
- (0, import_react22.useEffect)(() => {
8044
+ (0, import_react23.useEffect)(() => {
8028
8045
  if (controlled) {
8029
8046
  walk.current = progress;
8030
8047
  travel.current = null;
@@ -8032,7 +8049,7 @@ function useWalk({
8032
8049
  }
8033
8050
  }, [controlled, progress]);
8034
8051
  const canvas = gl.domElement;
8035
- (0, import_react22.useEffect)(() => {
8052
+ (0, import_react23.useEffect)(() => {
8036
8053
  if (!interactive) return;
8037
8054
  const hadTabIndex = canvas.hasAttribute("tabindex");
8038
8055
  if (!hadTabIndex) canvas.tabIndex = 0;
@@ -8123,8 +8140,8 @@ function useWalk({
8123
8140
  if (!hadLabel) canvas.removeAttribute("aria-label");
8124
8141
  };
8125
8142
  }, [canvas, interactive, motion.speed, motion.capture, path.closed, step, travelTo, stops]);
8126
- const reported = (0, import_react22.useRef)(-1);
8127
- (0, import_fiber9.useFrame)((_, delta) => {
8143
+ const reported = (0, import_react23.useRef)(-1);
8144
+ (0, import_fiber10.useFrame)((_, delta) => {
8128
8145
  if (onProgress && Math.abs(walk.current - reported.current) > 1e-4) {
8129
8146
  reported.current = walk.current;
8130
8147
  onProgress(walk.current);
@@ -8150,7 +8167,7 @@ function useWalk({
8150
8167
  if (!path.closed && (walk.current <= 0 || walk.current >= 1)) velocity.current = 0;
8151
8168
  }
8152
8169
  });
8153
- return (0, import_react22.useMemo)(() => ({ walk, travelTo, step, dragged }), [travelTo, step]);
8170
+ return (0, import_react23.useMemo)(() => ({ walk, travelTo, step, dragged }), [travelTo, step]);
8154
8171
  }
8155
8172
 
8156
8173
  // src/stage/quality.ts
@@ -8270,13 +8287,13 @@ function ShotRig({
8270
8287
  paperHeight,
8271
8288
  walk
8272
8289
  }) {
8273
- const camera = (0, import_fiber10.useThree)((s) => s.camera);
8274
- const path = (0, import_react23.useMemo)(() => getWalkPath(stage.path), [stage.path]);
8275
- const scale = (0, import_react23.useMemo)(
8290
+ const camera = (0, import_fiber11.useThree)((s) => s.camera);
8291
+ const path = (0, import_react24.useMemo)(() => getWalkPath(stage.path), [stage.path]);
8292
+ const scale = (0, import_react24.useMemo)(
8276
8293
  () => ({ figure: stage.figure.height, paper: paperHeight }),
8277
8294
  [stage.figure.height, paperHeight]
8278
8295
  );
8279
- (0, import_fiber10.useFrame)(() => {
8296
+ (0, import_fiber11.useFrame)(() => {
8280
8297
  const { position, target } = stageCamera(path, walk.current * path.length, scale, stage.shot);
8281
8298
  camera.position.set(position[0], position[1], position[2]);
8282
8299
  camera.lookAt(target[0], target[1], target[2]);
@@ -8284,17 +8301,17 @@ function ShotRig({
8284
8301
  return null;
8285
8302
  }
8286
8303
  function QualityWatch({ tier, onChange }) {
8287
- const samples = (0, import_react23.useRef)([]);
8288
- const settle2 = (0, import_react23.useRef)(SETTLE_FRAMES);
8289
- const window2 = (0, import_react23.useRef)(FIRST_WINDOW);
8290
- const failed = (0, import_react23.useRef)(null);
8291
- const settled = (0, import_react23.useCallback)((next) => {
8304
+ const samples = (0, import_react24.useRef)([]);
8305
+ const settle2 = (0, import_react24.useRef)(SETTLE_FRAMES);
8306
+ const window2 = (0, import_react24.useRef)(FIRST_WINDOW);
8307
+ const failed = (0, import_react24.useRef)(null);
8308
+ const settled = (0, import_react24.useCallback)((next) => {
8292
8309
  samples.current = [];
8293
8310
  settle2.current = SETTLE_FRAMES;
8294
8311
  window2.current = STEADY_WINDOW;
8295
8312
  return next;
8296
8313
  }, []);
8297
- (0, import_fiber10.useFrame)((_, delta) => {
8314
+ (0, import_fiber11.useFrame)((_, delta) => {
8298
8315
  if (settle2.current > 0) {
8299
8316
  settle2.current -= 1;
8300
8317
  return;
@@ -8331,41 +8348,41 @@ function PaperStageScene({
8331
8348
  reducedMotion
8332
8349
  }) {
8333
8350
  const still = usePrefersReducedMotion(reducedMotion);
8334
- const [tier, setTier] = (0, import_react23.useState)(quality === "auto" ? INITIAL_TIER : quality);
8335
- (0, import_react23.useEffect)(() => {
8351
+ const [tier, setTier] = (0, import_react24.useState)(quality === "auto" ? INITIAL_TIER : quality);
8352
+ (0, import_react24.useEffect)(() => {
8336
8353
  if (quality !== "auto") setTier(quality);
8337
8354
  }, [quality]);
8338
- const reportQuality = (0, import_react23.useRef)(onQualityChange);
8339
- (0, import_react23.useEffect)(() => {
8355
+ const reportQuality = (0, import_react24.useRef)(onQualityChange);
8356
+ (0, import_react24.useEffect)(() => {
8340
8357
  reportQuality.current = onQualityChange;
8341
8358
  });
8342
- (0, import_react23.useEffect)(() => {
8359
+ (0, import_react24.useEffect)(() => {
8343
8360
  reportQuality.current?.(tier);
8344
8361
  }, [tier]);
8345
8362
  const settings = quality === "auto" ? qualityTiers[tier] : qualityFor(quality);
8346
8363
  const stageKey = JSON.stringify(stageInput ?? {});
8347
- const stage = (0, import_react23.useMemo)(() => stageSchema.parse(stageInput ?? {}), [stageKey]);
8348
- const path = (0, import_react23.useMemo)(() => getWalkPath(stage.path), [stage.path]);
8349
- const rig = (0, import_react23.useMemo)(() => {
8364
+ const stage = (0, import_react24.useMemo)(() => stageSchema.parse(stageInput ?? {}), [stageKey]);
8365
+ const path = (0, import_react24.useMemo)(() => getWalkPath(stage.path), [stage.path]);
8366
+ const rig = (0, import_react24.useMemo)(() => {
8350
8367
  const resolved = resolveLighting(stage.lighting, stage.light);
8351
8368
  return {
8352
8369
  ...resolved,
8353
8370
  sky: { zenith: stage.source.zenith, horizon: stage.source.color, ground: stage.ground.color }
8354
8371
  };
8355
8372
  }, [stage.lighting, stage.light, stage.source.zenith, stage.source.color, stage.ground.color]);
8356
- const sheetDims = (0, import_react23.useMemo)(() => {
8373
+ const sheetDims = (0, import_react24.useMemo)(() => {
8357
8374
  const { width, height } = resolveConfig({ preset: preset ?? BANNER }).sheet;
8358
8375
  return { width, height };
8359
8376
  }, [preset]);
8360
8377
  const paperHeight = sheetDims.height;
8361
8378
  const paperWidth = sheetDims.width;
8362
8379
  const paper = preset ?? BANNER;
8363
- const resolvedLayoutOptions = (0, import_react23.useMemo)(() => {
8380
+ const resolvedLayoutOptions = (0, import_react24.useMemo)(() => {
8364
8381
  const schema = getLayout(layout).optionsSchema;
8365
8382
  const takesPath = schema instanceof import_zod29.z.ZodObject && "path" in schema.shape;
8366
8383
  return takesPath ? { ...layoutOptions, path: stage.path } : layoutOptions;
8367
8384
  }, [layout, layoutOptions, stage.path]);
8368
- const slots = (0, import_react23.useMemo)(() => {
8385
+ const slots = (0, import_react24.useMemo)(() => {
8369
8386
  if (papers) return papers;
8370
8387
  if (images) return void 0;
8371
8388
  if (text !== void 0) {
@@ -8403,7 +8420,7 @@ function PaperStageScene({
8403
8420
  }, [papers, images, text, count, sheetDims]);
8404
8421
  const drive = stageMotionSchema.parse(motion ?? {});
8405
8422
  const slotCount = slots?.length ?? images?.length ?? count;
8406
- const stops = (0, import_react23.useMemo)(() => {
8423
+ const stops = (0, import_react24.useMemo)(() => {
8407
8424
  const spec = getLayout(layout);
8408
8425
  const placed = spec.walkStops?.(slotCount, spec.optionsSchema.parse(resolvedLayoutOptions ?? {}));
8409
8426
  if (placed && placed.length > 0) return placed;
@@ -8418,11 +8435,11 @@ function PaperStageScene({
8418
8435
  reduced: still,
8419
8436
  onProgress
8420
8437
  });
8421
- const surroundRadius = (0, import_react23.useMemo)(
8438
+ const surroundRadius = (0, import_react24.useMemo)(
8422
8439
  () => Math.max(path.length * 1.6, paperHeight * 9),
8423
8440
  [path.length, paperHeight]
8424
8441
  );
8425
- const source = (0, import_react23.useMemo)(() => {
8442
+ const source = (0, import_react24.useMemo)(() => {
8426
8443
  const [x, z30] = walkPoint(path, path.length + stage.source.beyond);
8427
8444
  const [tx, tz] = path.tangentAt(1);
8428
8445
  const size = paperHeight * stage.source.spread;
@@ -8525,7 +8542,7 @@ function PaperStageScene({
8525
8542
  function PaperStage({ children, className, style, ...sceneProps }) {
8526
8543
  const dpr = qualityFor(sceneProps.quality ?? "auto").dpr;
8527
8544
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className, style: { width: "100%", height: "100%", ...style }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
8528
- import_fiber10.Canvas,
8545
+ import_fiber11.Canvas,
8529
8546
  {
8530
8547
  shadows: true,
8531
8548
  dpr: [1, dpr],
@@ -8535,7 +8552,8 @@ function PaperStage({ children, className, style, ...sceneProps }) {
8535
8552
  },
8536
8553
  children: [
8537
8554
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PaperStageScene, { ...sceneProps }),
8538
- children
8555
+ children,
8556
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ReleaseContextOnUnmount, {})
8539
8557
  ]
8540
8558
  }
8541
8559
  ) });
@@ -8964,7 +8982,11 @@ function describeStage(input) {
8964
8982
  );
8965
8983
  const moved = Object.entries(stage.light).filter(([, value]) => value !== void 0).map(([key]) => key);
8966
8984
  if (moved.length > 0) parts.push(`with its ${moved.join(", ")} set by hand`);
8967
- if (input.scroll) parts.push("and scrolling the page walks the figure deeper into it");
8985
+ if (input.scroll) {
8986
+ parts.push(
8987
+ stage.showFigure ? "and scrolling the page walks the figure deeper into it" : "and scrolling the page carries the camera deeper into it"
8988
+ );
8989
+ }
8968
8990
  return parts.join(", ");
8969
8991
  }
8970
8992
  function exportableImages(images) {
@@ -9005,6 +9027,7 @@ const banner = ${stringifyStage(diffConfig(paperConfigSchema.parse(input.paper))
9005
9027
  const textConst = input.text?.trim() ? `
9006
9028
 
9007
9029
  const text = ${JSON.stringify(input.text)}` : "";
9030
+ const scrolls = stageSchema.parse(input.stage).showFigure ? "walk the figure" : "move the camera";
9008
9031
  const images = input.images?.length ? exportableImages(input.images) : null;
9009
9032
  const imagesConst = images ? `
9010
9033
  ${images.substituted ? "\n// Your uploaded pictures cannot travel in a snippet \u2014 these are\n// placeholders in the same order. Point them at your own files." : ""}
@@ -9031,7 +9054,7 @@ export function ${name}() {
9031
9054
  const ref = useRef<HTMLDivElement>(null)
9032
9055
  const [progress, setProgress] = useState(0)
9033
9056
 
9034
- // Scroll the section, walk the figure. The stage is pinned for the height
9057
+ // Scroll the section, ${scrolls}. The stage is pinned for the height
9035
9058
  // of the section, so the page scrolling past it IS the walk.
9036
9059
  useEffect(() => {
9037
9060
  const el = ref.current
@@ -9068,7 +9091,8 @@ function buildStageAgentPayload(input) {
9068
9091
  heights of scroll and pins the canvas inside that. Drop it into the page
9069
9092
  flow as a section; do NOT wrap it in a fixed-height container.` : `4. Sizing: the component fills its parent container. Place it where I ask;
9070
9093
  give the parent an explicit height.`;
9071
- return `Integrate a Paperlab stage \u2014 paper as architecture, with a figure walking through it \u2014 into this project. (paperlab agent-payload v${AGENT_PAYLOAD_VERSION})
9094
+ const subject = stageSchema.parse(input.stage).showFigure ? "paper as architecture, with a figure walking through it" : "paper as architecture, banners hung along a walk you move through";
9095
+ return `Integrate a Paperlab stage \u2014 ${subject} \u2014 into this project. (paperlab agent-payload v${AGENT_PAYLOAD_VERSION})
9072
9096
 
9073
9097
  1. Install the dependencies:
9074
9098