paperlab 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +57 -30
  2. package/dist/{FxPostPass-V6YDK4H2.js → FxPostPass-ACFSHY3S.js} +4 -4
  3. package/dist/FxPostPass-ACFSHY3S.js.map +1 -0
  4. package/dist/{chunk-232SU3AQ.js → chunk-BIFUJCUA.js} +2 -2
  5. package/dist/{chunk-232SU3AQ.js.map → chunk-BIFUJCUA.js.map} +1 -1
  6. package/dist/{chunk-SVWWC5UL.js → chunk-LUKNPQ3S.js} +16 -17
  7. package/dist/chunk-LUKNPQ3S.js.map +1 -0
  8. package/dist/{chunk-7WOGU2D4.js → chunk-S74YZAXE.js} +2 -2
  9. package/dist/chunk-S74YZAXE.js.map +1 -0
  10. package/dist/{damageContract-nF1fgK7P.d.cts → damageContract-9RC4egJg.d.cts} +2 -2
  11. package/dist/{damageContract-nF1fgK7P.d.ts → damageContract-9RC4egJg.d.ts} +2 -2
  12. package/dist/fx.cjs +20 -20
  13. package/dist/fx.cjs.map +1 -1
  14. package/dist/fx.d.cts +42 -44
  15. package/dist/fx.d.ts +42 -44
  16. package/dist/fx.js +21 -21
  17. package/dist/fx.js.map +1 -1
  18. package/dist/index.cjs +22 -23
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +4 -4
  21. package/dist/index.d.ts +4 -4
  22. package/dist/index.js +6 -6
  23. package/dist/index.js.map +1 -1
  24. package/dist/{slots-D_EE8GXn.d.cts → slots-DbJQfwVd.d.cts} +1 -1
  25. package/dist/{slots-D_EE8GXn.d.ts → slots-DbJQfwVd.d.ts} +1 -1
  26. package/dist/stage.cjs +23 -24
  27. package/dist/stage.cjs.map +1 -1
  28. package/dist/stage.d.cts +1 -1
  29. package/dist/stage.d.ts +1 -1
  30. package/dist/stage.js +12 -12
  31. package/dist/stage.js.map +1 -1
  32. package/package.json +5 -5
  33. package/dist/FxPostPass-V6YDK4H2.js.map +0 -1
  34. package/dist/chunk-7WOGU2D4.js.map +0 -1
  35. package/dist/chunk-SVWWC5UL.js.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import * as THREE from 'three';
4
- import { P as PaperConfig, a as PaperConfigInput, S as SheetConfig, b as StockName, C as ContentConfigInput, B as BehaviorConfigInput, D as DeformerInstanceConfigInput, c as SurfaceConfigInput, M as MemoryConfigInput, d as SceneConfigInput, e as PhysicsConfigInput, f as CreaseConfig, F as FieldPaperSlot, g as ContentConfig, h as PaperStatesInput, L as LightingName, i as FilmName, j as LightOverrides, k as BackdropConfig } from './slots-D_EE8GXn.cjs';
5
- export { l as BackContentConfig, m as BehaviorConfig, n as ClothConfig, o as CoreStateName, p as CreaseConfigInput, q as DeformerInstanceConfig, r as LightOverridesInput, s as MemoryConfig, t as PaperEdge, u as PaperStates, v as PhysicsConfig, w as SceneConfig, x as StateDef, y as StateName, z as StateTransitionConfig, A as StripConfig, E as SurfaceConfig, W as WashConfig, G as backdropSchema, H as behaviorConfigSchema, I as clothConfigSchema, J as contentNames, K as contentSchemaFor, N as coreStateNames, O as creaseSchema, Q as lightSchema, R as lightingNames, T as memorySchema, U as paperConfigSchema, V as paperEdges, X as paperStatesSchema, Y as physicsNames, Z as sceneSchema, _ as stateDefSchema, $ as stockNames, a0 as stripConfigSchema, a1 as washSchema } from './slots-D_EE8GXn.cjs';
6
- import { D as DamageSource } from './damageContract-nF1fgK7P.cjs';
7
- export { a as DAMAGE_CHANNELS, b as DAMAGE_LOOK_DEFAULTS, c as DamageLook } from './damageContract-nF1fgK7P.cjs';
4
+ import { P as PaperConfig, a as PaperConfigInput, S as SheetConfig, b as StockName, C as ContentConfigInput, B as BehaviorConfigInput, D as DeformerInstanceConfigInput, c as SurfaceConfigInput, M as MemoryConfigInput, d as SceneConfigInput, e as PhysicsConfigInput, f as CreaseConfig, F as FieldPaperSlot, g as ContentConfig, h as PaperStatesInput, L as LightingName, i as FilmName, j as LightOverrides, k as BackdropConfig } from './slots-DbJQfwVd.cjs';
5
+ export { l as BackContentConfig, m as BehaviorConfig, n as ClothConfig, o as CoreStateName, p as CreaseConfigInput, q as DeformerInstanceConfig, r as LightOverridesInput, s as MemoryConfig, t as PaperEdge, u as PaperStates, v as PhysicsConfig, w as SceneConfig, x as StateDef, y as StateName, z as StateTransitionConfig, A as StripConfig, E as SurfaceConfig, W as WashConfig, G as backdropSchema, H as behaviorConfigSchema, I as clothConfigSchema, J as contentNames, K as contentSchemaFor, N as coreStateNames, O as creaseSchema, Q as lightSchema, R as lightingNames, T as memorySchema, U as paperConfigSchema, V as paperEdges, X as paperStatesSchema, Y as physicsNames, Z as sceneSchema, _ as stateDefSchema, $ as stockNames, a0 as stripConfigSchema, a1 as washSchema } from './slots-DbJQfwVd.cjs';
6
+ import { D as DamageSource } from './damageContract-9RC4egJg.cjs';
7
+ export { a as DAMAGE_CHANNELS, b as DAMAGE_LOOK_DEFAULTS, c as DamageLook } from './damageContract-9RC4egJg.cjs';
8
8
  import { z } from 'zod';
9
9
 
10
10
  /**
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import * as THREE from 'three';
4
- import { P as PaperConfig, a as PaperConfigInput, S as SheetConfig, b as StockName, C as ContentConfigInput, B as BehaviorConfigInput, D as DeformerInstanceConfigInput, c as SurfaceConfigInput, M as MemoryConfigInput, d as SceneConfigInput, e as PhysicsConfigInput, f as CreaseConfig, F as FieldPaperSlot, g as ContentConfig, h as PaperStatesInput, L as LightingName, i as FilmName, j as LightOverrides, k as BackdropConfig } from './slots-D_EE8GXn.js';
5
- export { l as BackContentConfig, m as BehaviorConfig, n as ClothConfig, o as CoreStateName, p as CreaseConfigInput, q as DeformerInstanceConfig, r as LightOverridesInput, s as MemoryConfig, t as PaperEdge, u as PaperStates, v as PhysicsConfig, w as SceneConfig, x as StateDef, y as StateName, z as StateTransitionConfig, A as StripConfig, E as SurfaceConfig, W as WashConfig, G as backdropSchema, H as behaviorConfigSchema, I as clothConfigSchema, J as contentNames, K as contentSchemaFor, N as coreStateNames, O as creaseSchema, Q as lightSchema, R as lightingNames, T as memorySchema, U as paperConfigSchema, V as paperEdges, X as paperStatesSchema, Y as physicsNames, Z as sceneSchema, _ as stateDefSchema, $ as stockNames, a0 as stripConfigSchema, a1 as washSchema } from './slots-D_EE8GXn.js';
6
- import { D as DamageSource } from './damageContract-nF1fgK7P.js';
7
- export { a as DAMAGE_CHANNELS, b as DAMAGE_LOOK_DEFAULTS, c as DamageLook } from './damageContract-nF1fgK7P.js';
4
+ import { P as PaperConfig, a as PaperConfigInput, S as SheetConfig, b as StockName, C as ContentConfigInput, B as BehaviorConfigInput, D as DeformerInstanceConfigInput, c as SurfaceConfigInput, M as MemoryConfigInput, d as SceneConfigInput, e as PhysicsConfigInput, f as CreaseConfig, F as FieldPaperSlot, g as ContentConfig, h as PaperStatesInput, L as LightingName, i as FilmName, j as LightOverrides, k as BackdropConfig } from './slots-DbJQfwVd.js';
5
+ export { l as BackContentConfig, m as BehaviorConfig, n as ClothConfig, o as CoreStateName, p as CreaseConfigInput, q as DeformerInstanceConfig, r as LightOverridesInput, s as MemoryConfig, t as PaperEdge, u as PaperStates, v as PhysicsConfig, w as SceneConfig, x as StateDef, y as StateName, z as StateTransitionConfig, A as StripConfig, E as SurfaceConfig, W as WashConfig, G as backdropSchema, H as behaviorConfigSchema, I as clothConfigSchema, J as contentNames, K as contentSchemaFor, N as coreStateNames, O as creaseSchema, Q as lightSchema, R as lightingNames, T as memorySchema, U as paperConfigSchema, V as paperEdges, X as paperStatesSchema, Y as physicsNames, Z as sceneSchema, _ as stateDefSchema, $ as stockNames, a0 as stripConfigSchema, a1 as washSchema } from './slots-DbJQfwVd.js';
6
+ import { D as DamageSource } from './damageContract-9RC4egJg.js';
7
+ export { a as DAMAGE_CHANNELS, b as DAMAGE_LOOK_DEFAULTS, c as DamageLook } from './damageContract-9RC4egJg.js';
8
8
  import { z } from 'zod';
9
9
 
10
10
  /**
package/dist/index.js CHANGED
@@ -79,11 +79,11 @@ import {
79
79
  useResolvedConfig,
80
80
  washSchema,
81
81
  wrapLines
82
- } from "./chunk-SVWWC5UL.js";
82
+ } from "./chunk-LUKNPQ3S.js";
83
83
  import {
84
84
  DAMAGE_CHANNELS,
85
85
  DAMAGE_LOOK_DEFAULTS
86
- } from "./chunk-232SU3AQ.js";
86
+ } from "./chunk-BIFUJCUA.js";
87
87
 
88
88
  // src/Paper.tsx
89
89
  import { Canvas } from "@react-three/fiber";
@@ -241,7 +241,7 @@ function buildFieldComponentSource(input) {
241
241
  id="${zone.id}"${accept}
242
242
  bounds={${JSON.stringify(zone.bounds)}}${highlight}
243
243
  onPlace={(paper, zone) => {
244
- // First-run smoke test \u2014 you should see this in the console on drop:
244
+ // First-run smoke test. You should see this in the console on drop:
245
245
  console.log(paper.presetName, '\u2192', zone.id)
246
246
  // Then replace it: stamp a postmark, advance the flow, persist the placement, \u2026
247
247
  }}
@@ -292,7 +292,7 @@ function buildFieldAgentPayload(input) {
292
292
  npm i paperlab three @react-three/fiber gsap
293
293
 
294
294
  2. Create the component below as \`components/PaperGallery.tsx\` (or the
295
- project's component convention). It is self-contained \u2014 every preset it
295
+ project's component convention). It is self-contained: every preset it
296
296
  uses is inlined, and it owns its own <Canvas>:
297
297
 
298
298
  \`\`\`tsx
@@ -303,8 +303,8 @@ ${buildFieldComponentSource(input)}
303
303
  give the parent an explicit height.
304
304
 
305
305
  4. Verify: run the dev server. You should see ${describeFieldConfig(input)}.
306
- If the canvas is blank, the parent container almost certainly has no height \u2014
307
- give it one (this is the classic React Three Fiber integration bug, not a
306
+ If the canvas is blank, the parent container almost certainly has no height.
307
+ Give it one (this is the classic React Three Fiber integration bug, not a
308
308
  paperlab bug).
309
309
 
310
310
  Constraints: don't modify the preset values; three >= 0.160 and React 19 are
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/Paper.tsx","../src/config/field-export.ts","../src/field/parity.ts"],"sourcesContent":["import { Canvas } from '@react-three/fiber'\nimport { forwardRef, useMemo } from 'react'\nimport { PaperMesh, useResolvedConfig, type PaperHandle, type PaperMeshProps } from './PaperMesh'\nimport { PaperFallback, PaperMirror, supportsWebGL } from './a11y'\nimport { PaperLighting } from './scene/PaperLighting'\nimport { PaperBackdrop } from './scene/backdrop'\nimport { ReleaseContextOnUnmount } from './scene/release'\n\nexport interface PaperProps extends PaperMeshProps {\n /** Extra children rendered inside the canvas (lights are provided). */\n children?: React.ReactNode\n className?: string\n style?: React.CSSProperties\n}\n\n/**\n * `<Paper />` owns its own `<Canvas>` and fills its parent container —\n * the parent must have a height. Use `<PaperMesh />` inside an existing\n * R3F scene instead.\n *\n * Ships with its accessibility layer: a hidden DOM mirror of the content,\n * `prefers-reduced-motion` support, and a flat DOM fallback when WebGL is\n * unavailable.\n */\nexport const Paper = forwardRef<PaperHandle, PaperProps>(function Paper(\n { children, className, style, ...meshProps },\n ref,\n) {\n // Keyed on every prop resolveConfig reads — a content/stock/behavior change\n // must refresh the fallback, mirror, and lighting, not just the mesh.\n const config = useResolvedConfig(meshProps)\n const webgl = useMemo(() => (typeof window === 'undefined' ? true : supportsWebGL()), [])\n\n return (\n <div className={className} style={{ width: '100%', height: '100%', ...style }}>\n {webgl ? (\n <Canvas shadows camera={{ position: [0, 0.35, 2.4], fov: 40 }} dpr={[1, 2]}>\n <PaperBackdrop backdrop={config.scene.backdrop} />\n <PaperLighting\n preset={config.scene.lighting}\n // The overrides, which this component was resolving without: a\n // preset whose scene said \"studio, but dimmer\" rendered as plain\n // studio here while the editor showed the dimmer one.\n light={config.scene.light}\n // One floor height, shared: the ground the shadow falls on is the\n // ground a sheet lands on.\n floor={config.scene.floor.enabled ? config.scene.floor.y : -1.05}\n scale={8}\n reducedMotion={meshProps.reducedMotion}\n // A burn bright enough to light the room says so on its source.\n damage={meshProps.damage}\n />\n {config.scene.floor.enabled && (\n // Dark, matte and large enough to leave no edge in frame: what the\n // fire's light pools on, and what the paper it cuts loose lands on.\n <mesh rotation-x={-Math.PI / 2} position={[0, config.scene.floor.y, 0]} receiveShadow>\n <planeGeometry args={[40, 40]} />\n <meshStandardMaterial\n color={config.scene.floor.color}\n roughness={config.scene.floor.roughness}\n />\n </mesh>\n )}\n <PaperMesh ref={ref} {...meshProps} />\n {children}\n <ReleaseContextOnUnmount />\n </Canvas>\n ) : (\n <PaperFallback config={config} />\n )}\n <PaperMirror config={config} />\n </div>\n )\n})\n","import {\n sceneSchema,\n type ContentConfig,\n type PaperConfig,\n type PaperStatesInput,\n type SceneConfigInput,\n} from './schema'\nimport { diffConfig } from './diff'\nimport { AGENT_PAYLOAD_VERSION } from './agent-payload'\nimport { getLayout } from '../field/layouts'\n\n/**\n * Field-mode export. Every preset a field references is INLINED as a const —\n * receivers don't have the sender's local preset library. Same fixed payload\n * anatomy as the single-paper export, same version.\n */\n\nexport interface FieldExportPaper {\n /** Display name of the preset this slot uses (const naming + description). */\n presetName: string\n /** The RESOLVED preset config (user presets included — nothing to fetch). */\n preset: PaperConfig\n content?: ContentConfig\n /** Per-slot state overrides (slot 3's hover deeper than slot 4's). */\n states?: PaperStatesInput\n}\n\nexport interface FieldExportZone {\n id: string\n accept?: string[]\n bounds: { position: [number, number, number]; size: [number, number] }\n highlight?: 'none' | 'glow' | 'outline'\n}\n\nexport interface FieldExportInput {\n layout: string\n layoutOptions?: Record<string, unknown>\n motion?: { driver?: 'autoplay' | 'drag' | 'none'; speed?: number }\n entrance?: { type?: 'rise' | 'scatter' | 'none'; stagger?: number; duration?: number }\n papers: FieldExportPaper[]\n /** How the gallery is lit. Omitted means the schema's own default rig. */\n scene?: SceneConfigInput\n /** Drop zones — exported as `<DropZone>` children with an onPlace stub. */\n zones?: FieldExportZone[]\n}\n\nconst MOTION_DEFAULTS = { driver: 'autoplay', speed: 0.5 }\nconst ENTRANCE_DEFAULTS = { type: 'rise', stagger: 0.06, duration: 0.9 }\n\nfunction camel(name: string): string {\n const id = name.replace(/[^a-zA-Z0-9]+(.)/g, (_, c: string) => c.toUpperCase()).replace(/[^a-zA-Z0-9]/g, '')\n const safe = /^[A-Za-z]/.test(id) ? id : `preset${id}`\n return safe.charAt(0).toLowerCase() + safe.slice(1)\n}\n\ninterface DistinctPreset {\n varName: string\n presetName: string\n preset: PaperConfig\n}\n\n/** Distinct presets in slot order, with collision-safe const names. */\nexport function distinctFieldPresets(input: FieldExportInput): DistinctPreset[] {\n const byKey = new Map<string, DistinctPreset>()\n const usedNames = new Set<string>()\n for (const paper of input.papers) {\n const key = JSON.stringify(paper.preset)\n if (byKey.has(key)) continue\n let varName = camel(paper.presetName)\n while (usedNames.has(varName)) varName += '2'\n usedNames.add(varName)\n byKey.set(key, { varName, presetName: paper.presetName, preset: paper.preset })\n }\n return [...byKey.values()]\n}\n\n/**\n * The entries that differ from the defaults.\n *\n * Compared STRUCTURALLY rather than by reference. Most of what passes\n * through here is numbers and strings, where the two agree — but a layout\n * option holding an array, or a scene holding a nested rig, is never\n * reference-equal to the default it was copied from, so those exported a\n * prop that said exactly what the default already said.\n */\nfunction nonDefault<T extends Record<string, unknown>>(\n value: T | undefined,\n defaults: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value ?? {})) {\n if (v === undefined) continue\n const same = v === defaults[k] || JSON.stringify(v) === JSON.stringify(defaults[k])\n if (!same) out[k] = v\n }\n return out\n}\n\n/** The layout/motion/entrance props of the export, defaults stripped. */\nexport function diffFieldProps(input: FieldExportInput): Record<string, unknown> {\n const out: Record<string, unknown> = { layout: input.layout }\n const layout = getLayout(input.layout)\n const layoutOptions = nonDefault(input.layoutOptions ?? {}, layout.defaults as Record<string, unknown>)\n if (Object.keys(layoutOptions).length > 0) out.layoutOptions = layoutOptions\n const motion = nonDefault(input.motion, MOTION_DEFAULTS)\n if (Object.keys(motion).length > 0) out.motion = motion\n const entrance = nonDefault(input.entrance, ENTRANCE_DEFAULTS)\n if (Object.keys(entrance).length > 0) out.entrance = entrance\n // Resolved through the schema before comparing, so an input naming only\n // `lighting` is judged against a whole default rig rather than a\n // half-filled one.\n const scene = nonDefault(\n sceneSchema.parse(input.scene ?? {}) as unknown as Record<string, unknown>,\n sceneSchema.parse({}) as unknown as Record<string, unknown>,\n )\n if (Object.keys(scene).length > 0) out.scene = scene\n return out\n}\n\nconst LAYOUT_PHRASES: Record<string, string> = {\n ring: 'a ring you can see around',\n fan: 'a fanned swatch deck hinged at one corner',\n spread: 'a stack slid sideways, each sheet bowing further than the last',\n pile: 'a heap on a desk, the sheets underneath pressed flat',\n wall: 'a pinned studio wall of sheets, none hanging quite square',\n spill: 'a dropped stack caught mid-air, each sheet bent its own way',\n sweep: 'a specimen chart of one sheet at every stage of its deformation',\n book: 'an open book, its pages splayed from a shared spine',\n accordion: 'one continuous strip folded into a concertina',\n rack: 'prints stood in a row and leaning back against a wall',\n sheet: 'a stamp-block grid on a shared backing sheet',\n}\n\n/** The one-line visual an agent verifies after `npm run dev`. */\nexport function describeFieldConfig(input: FieldExportInput): string {\n const n = input.papers.length\n const stateful = input.papers.some((p) => p.preset.states || p.states)\n const zones = input.zones ?? []\n const parts: string[] = []\n\n if (input.layout === 'sheet') {\n const rows = Number(input.layoutOptions?.rows ?? 2)\n const columns = Number(input.layoutOptions?.columns ?? 5)\n parts.push(`a ${rows}×${columns} sheet of papers on a shared backing`)\n } else {\n const layoutPhrase = LAYOUT_PHRASES[input.layout] ?? `a ${input.layout} layout`\n parts.push(`${n} papers arranged in ${layoutPhrase}`)\n }\n\n const driver = input.motion?.driver ?? 'autoplay'\n if (stateful) {\n // Stateful fields render static and interactive — the motion driver is moot.\n parts.push('hovering peels/reacts per paper (interaction states)')\n if (zones.length > 0) {\n parts.push(\n `dragging one past its tear threshold detaches it to carry to the ${zones\n .map((z) => z.id)\n .join(' or ')} zone (release elsewhere flutters it back)`,\n )\n }\n } else {\n if (driver === 'autoplay') parts.push('slowly orbiting on their own')\n if (driver === 'drag') parts.push('draggable sideways with the pointer')\n if (zones.length > 0) parts.push(`${zones.length} drop zone${zones.length > 1 ? 's' : ''}`)\n }\n\n const distinct = distinctFieldPresets(input)\n if (distinct.length > 1) {\n parts.push(`mixing ${distinct.length} paper presets (${distinct.map((d) => d.presetName).join(', ')})`)\n }\n return parts.join(', ')\n}\n\n/** Component source shared by the JSX snippet and the agent payload. */\nexport function buildFieldComponentSource(input: FieldExportInput): string {\n const distinct = distinctFieldPresets(input)\n const varByKey = new Map(distinct.map((d) => [JSON.stringify(d.preset), d.varName]))\n\n // `satisfies` (not `as const`) — const-asserted arrays turn readonly and\n // stop matching the library's input types.\n const consts = distinct\n .map(\n (d) =>\n `const ${d.varName} = ${JSON.stringify(diffConfig(d.preset), null, 2)} satisfies PaperConfigInput`,\n )\n .join('\\n\\n')\n\n const slots = input.papers\n .map((paper) => {\n const varName = varByKey.get(JSON.stringify(paper.preset))!\n const content = paper.content ? `, content: ${JSON.stringify(paper.content)}` : ''\n const states = paper.states ? `, states: ${JSON.stringify(paper.states)}` : ''\n return ` { preset: ${varName}${content}${states} },`\n })\n .join('\\n')\n\n const props = Object.entries(diffFieldProps(input))\n .map(([key, value]) =>\n typeof value === 'string' ? ` ${key}=\"${value}\"` : ` ${key}={${JSON.stringify(value)}}`,\n )\n .join('\\n')\n\n const zones = input.zones ?? []\n const zoneImport = zones.length > 0 ? ', DropZone' : ''\n const zoneChildren = zones\n .map((zone) => {\n const accept = zone.accept ? ` accept={${JSON.stringify(zone.accept)}}` : ''\n const highlight = zone.highlight && zone.highlight !== 'glow' ? ` highlight=\"${zone.highlight}\"` : ''\n return ` <DropZone\n id=\"${zone.id}\"${accept}\n bounds={${JSON.stringify(zone.bounds)}}${highlight}\n onPlace={(paper, zone) => {\n // First-run smoke test — you should see this in the console on drop:\n console.log(paper.presetName, '→', zone.id)\n // Then replace it: stamp a postmark, advance the flow, persist the placement, …\n }}\n />`\n })\n .join('\\n')\n\n if (zones.length === 0) {\n return `import { PaperField, type FieldPaperSlot, type PaperConfigInput${zoneImport} } from 'paperlab'\n\n${consts}\n\nconst papers: FieldPaperSlot[] = [\n${slots}\n]\n\nexport function PaperGallery() {\n return (\n <PaperField\n papers={papers}\n${props}\n />\n )\n}`\n }\n\n return `import { PaperField, type FieldPaperSlot, type PaperConfigInput${zoneImport} } from 'paperlab'\n\n${consts}\n\nconst papers: FieldPaperSlot[] = [\n${slots}\n]\n\nexport function PaperGallery() {\n return (\n <PaperField\n papers={papers}\n${props}\n >\n${zoneChildren}\n </PaperField>\n )\n}`\n}\n\n/** The self-contained field integration brief — one paste into a coding agent. */\nexport function buildFieldAgentPayload(input: FieldExportInput): string {\n return `Integrate a Paperlab paper gallery into this project. (paperlab agent-payload v${AGENT_PAYLOAD_VERSION})\n\n1. Install the dependencies:\n\n npm i paperlab three @react-three/fiber gsap\n\n2. Create the component below as \\`components/PaperGallery.tsx\\` (or the\n project's component convention). It is self-contained — every preset it\n uses is inlined, and it owns its own <Canvas>:\n\n\\`\\`\\`tsx\n${buildFieldComponentSource(input)}\n\\`\\`\\`\n\n3. Sizing: the component fills its parent container. Place it where I ask;\n give the parent an explicit height.\n\n4. Verify: run the dev server. You should see ${describeFieldConfig(input)}.\n If the canvas is blank, the parent container almost certainly has no height —\n give it one (this is the classic React Three Fiber integration bug, not a\n paperlab bug).\n\nConstraints: don't modify the preset values; three >= 0.160 and React 19 are\nrequired; the component needs no props.`\n}\n","import * as THREE from 'three'\nimport type { DeformerInstance, SheetDims } from '../deformers/types'\nimport { displacePoint } from '../deformers/compose'\nimport { getDeformer } from '../deformers/registry'\nimport { buildDisplacementGLSL } from './compose'\n\n/**\n * Golden-vector parity: the JS (hero) and GLSL (field) implementations of a\n * deformer must produce identical displacements. This harness evaluates the\n * GLSL path on the real GPU — the stack's displacement functions run in a\n * fragment shader over an N×N grid, rendered to a float target and read\n * back — then epsilon-compares against the JS path. Browser-only (WebGL2);\n * driven by tools/parity.mjs in CI.\n */\n\nconst GRID = 48\n/** highp float + sin/cos implementation differences across GPUs. */\nexport const PARITY_EPSILON = 5e-4\n\nexport interface ParityCase {\n name: string\n stack: DeformerInstance[]\n sheet: SheetDims\n t: number\n}\n\nexport interface ParityResult {\n name: string\n maxError: number\n pass: boolean\n}\n\n/** One case per deformer feature worth guarding, both easy and adversarial. */\nexport const parityCases: ParityCase[] = [\n {\n name: 'roll: defaults',\n stack: [{ type: 'roll', options: { angle: 90, boundary: 0, radius: 0.12, thickness: 0.04 } }],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n name: 'roll: tight receipt roll, rolling down',\n stack: [{ type: 'roll', options: { angle: 270, boundary: -0.4, radius: 0.07, thickness: 0.006 } }],\n sheet: { width: 1, height: 2.6 },\n t: 0,\n },\n {\n name: 'curl: bottom-right peel',\n stack: [{ type: 'curl', options: { corner: 'bottom-right', amount: 0.45, radius: 0.2, skew: 0 } }],\n sheet: { width: 1.5, height: 1 },\n t: 0,\n },\n {\n name: 'curl: skewed top-left',\n stack: [{ type: 'curl', options: { corner: 'top-left', amount: 0.7, radius: 0.3, skew: 15 } }],\n sheet: { width: 1, height: 1 },\n t: 0,\n },\n {\n name: 'bend: positive arc at an angle',\n stack: [{ type: 'bend', options: { curvature: 1.2, angle: 33 } }],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n /**\n * The gentle end, which nothing used to cover: the gate only ever tried\n * |curvature| ≥ 0.6, and `photo-print` — the field starter preset — bends\n * at 0.35. That band is where the arc's float32 evaluation loses its\n * cancellation, and where the two paths were 6e-4 apart until `bend` was\n * rewritten in cancellation-free form. Keep a case down here.\n */\n name: 'bend: barely-there arc (the band photo-print lives in)',\n stack: [{ type: 'bend', options: { curvature: 0.35, angle: 0 } }],\n sheet: { width: 1.2, height: 0.9 },\n t: 0,\n },\n {\n name: 'bend: the gentlest arc the schema allows',\n stack: [{ type: 'bend', options: { curvature: 0.02, angle: 61 } }],\n sheet: { width: 2, height: 2.6 },\n t: 0,\n },\n {\n name: 'bend: negative arc',\n stack: [{ type: 'bend', options: { curvature: -0.8, angle: 0 } }],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n name: 'fold: 90° hinge',\n stack: [{ type: 'fold', options: { angle: 90, offset: 0, foldAngle: 90, radius: 0.06 } }],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n name: 'fold: deep fold, travelling down',\n stack: [{ type: 'fold', options: { angle: 270, offset: 0.2, foldAngle: 165, radius: 0.04 } }],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n name: 'wave: free ripple at t=1.234',\n stack: [\n {\n type: 'wave',\n options: { amplitude: 0.05, wavelength: 0.5, speed: 1, angle: 20, pinnedEdge: 'none' },\n },\n ],\n sheet: { width: 1, height: 1 },\n t: 1.234,\n },\n {\n name: 'wave: pinned top at t=2.5',\n stack: [\n {\n type: 'wave',\n options: { amplitude: 0.04, wavelength: 0.4, speed: 1.5, angle: 80, pinnedEdge: 'top' },\n },\n ],\n sheet: { width: 1.2, height: 1.5 },\n t: 2.5,\n },\n {\n name: 'drape: banner hung from the top',\n stack: [\n {\n type: 'drape',\n options: { amplitude: 0.17, folds: 5, falloff: 1.6, irregular: 0.45, gather: 0.5, pinnedEdge: 'top' },\n },\n ],\n sheet: { width: 1.5, height: 8.5 },\n t: 0,\n },\n {\n name: 'drape: deep irregular folds pinned at the bottom',\n stack: [\n {\n type: 'drape',\n options: { amplitude: 0.55, folds: 11, falloff: 0.4, irregular: 1, gather: 1, pinnedEdge: 'bottom' },\n },\n ],\n sheet: { width: 2.2, height: 3 },\n t: 0,\n },\n {\n name: 'crumple: defaults',\n stack: [{ type: 'crumple', options: { amount: 0.35, scale: 3, pull: 0.4, seed: 0 } }],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n // The adversarial one. `fract` is a sawtooth, so the two halves disagree\n // hardest where a crease lands exactly on a sample — a fine scale and a\n // seed whose fold directions are near-axis puts the most creases in\n // reach of the grid.\n name: 'crumple: fully crushed, fine creases, off-axis seed',\n stack: [{ type: 'crumple', options: { amount: 1, scale: 7.5, pull: 1, seed: 5 } }],\n sheet: { width: 1.3, height: 0.9 },\n t: 0,\n },\n {\n // Crush then curl, which is the order the crumple BEHAVIOR stacks them:\n // the creases have to be placed on the flat sheet, not on a bent one.\n name: 'stacked: crumple ∘ bend (the crumple behavior)',\n stack: [\n { type: 'crumple', options: { amount: 0.62, scale: 3.3, pull: 0.5, seed: 2 } },\n { type: 'bend', options: { curvature: 0.31, angle: 35 } },\n ],\n sheet: { width: 1.1, height: 1.4 },\n t: 0,\n },\n {\n name: 'stacked: letter-fold pair (fold ∘ fold)',\n stack: [\n { type: 'fold', options: { angle: 270, offset: 0.2333, foldAngle: 120, radius: 0.05 } },\n { type: 'fold', options: { angle: 90, offset: 0.2333, foldAngle: 100, radius: 0.08 } },\n ],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n name: 'stacked: bend ∘ roll ∘ wave',\n stack: [\n { type: 'bend', options: { curvature: 0.6, angle: 0 } },\n { type: 'roll', options: { angle: 90, boundary: 0.1, radius: 0.15, thickness: 0 } },\n {\n type: 'wave',\n options: { amplitude: 0.02, wavelength: 0.6, speed: 0.7, angle: 45, pinnedEdge: 'none' },\n },\n ],\n sheet: { width: 1, height: 1.4 },\n t: 0.8,\n },\n]\n\nfunction buildParityFragment(stack: DeformerInstance[], sheet: SheetDims): string {\n const composed = buildDisplacementGLSL(stack, sheet)\n return /* glsl */ `#version 300 es\nprecision highp float;\nuniform float uPlTime;\nuniform float uPlBias;\n${composed.functionsSrc}\n${composed.displaceSrc}\nout vec4 outColor;\nvoid main() {\n vec2 uv = (gl_FragCoord.xy - 0.5) / float(${GRID - 1});\n vec3 p = vec3((uv - 0.5) * uSheet, 0.0);\n outColor = vec4(plDisplace(p, uv, uPlTime, uPlBias), 1.0);\n}\n`\n}\n\nconst VERT = `#version 300 es\nin vec2 aPos;\nvoid main() { gl_Position = vec4(aPos, 0.0, 1.0); }\n`\n\nfunction compile(gl: WebGL2RenderingContext, type: number, src: string): WebGLShader {\n const shader = gl.createShader(type)!\n gl.shaderSource(shader, src)\n gl.compileShader(shader)\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n throw new Error(`[paperlab parity] shader compile failed: ${gl.getShaderInfoLog(shader)}`)\n }\n return shader\n}\n\nfunction runCaseOnGPU(gl: WebGL2RenderingContext, c: ParityCase, bias = 1): Float32Array {\n const composed = buildDisplacementGLSL(c.stack, c.sheet)\n const program = gl.createProgram()!\n gl.attachShader(program, compile(gl, gl.VERTEX_SHADER, VERT))\n gl.attachShader(program, compile(gl, gl.FRAGMENT_SHADER, buildParityFragment(c.stack, c.sheet)))\n gl.linkProgram(program)\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n throw new Error(`[paperlab parity] link failed: ${gl.getProgramInfoLog(program)}`)\n }\n // biome-ignore lint/correctness/useHookAtTopLevel: WebGL's gl.useProgram, not a React hook\n gl.useProgram(program)\n\n for (const [name, value] of Object.entries(composed.uniforms)) {\n const loc = gl.getUniformLocation(program, name)\n if (!loc) continue\n if (typeof value === 'number') gl.uniform1f(loc, value)\n else if (value.length === 2) gl.uniform2f(loc, value[0]!, value[1]!)\n else if (value.length === 3) gl.uniform3f(loc, value[0]!, value[1]!, value[2]!)\n else gl.uniform4f(loc, value[0]!, value[1]!, value[2]!, value[3]!)\n }\n const timeLoc = gl.getUniformLocation(program, 'uPlTime')\n if (timeLoc) gl.uniform1f(timeLoc, c.t)\n const biasLoc = gl.getUniformLocation(program, 'uPlBias')\n if (biasLoc) gl.uniform1f(biasLoc, bias)\n\n const quad = gl.createBuffer()!\n gl.bindBuffer(gl.ARRAY_BUFFER, quad)\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW)\n const posLoc = gl.getAttribLocation(program, 'aPos')\n gl.enableVertexAttribArray(posLoc)\n gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0)\n\n const tex = gl.createTexture()!\n gl.bindTexture(gl.TEXTURE_2D, tex)\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, GRID, GRID, 0, gl.RGBA, gl.FLOAT, null)\n const fbo = gl.createFramebuffer()!\n gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)\n gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0)\n gl.viewport(0, 0, GRID, GRID)\n gl.drawArrays(gl.TRIANGLES, 0, 3)\n\n const out = new Float32Array(GRID * GRID * 4)\n gl.readPixels(0, 0, GRID, GRID, gl.RGBA, gl.FLOAT, out)\n gl.bindFramebuffer(gl.FRAMEBUFFER, null)\n return out\n}\n\n/**\n * The bias contract, checked on the GPU: a stack of strength-bearing\n * deformers must go completely flat at bias 0, and a stack of deformers that\n * opt out (roll) must ignore bias entirely. Parity alone can't catch this —\n * it only ever evaluates bias 1.\n */\nfunction runBiasCases(gl: WebGL2RenderingContext): ParityResult[] {\n const results: ParityResult[] = []\n for (const c of parityCases) {\n const strengths = c.stack.map((i) => getDeformer(i.type).glsl?.strength !== undefined)\n // Mixed stacks flatten only partway — nothing crisp to assert.\n if (!strengths.every((h) => h === strengths[0])) continue\n const scales = strengths[0]\n\n const at0 = runCaseOnGPU(gl, c, 0)\n const reference = scales ? null : runCaseOnGPU(gl, c, 1)\n let maxError = 0\n for (let row = 0; row < GRID; row++) {\n for (let col = 0; col < GRID; col++) {\n const i4 = (row * GRID + col) * 4\n // At bias 0 a scaling stack collapses onto the flat sheet; an opted-out\n // stack must land exactly where bias 1 put it.\n const ex = reference ? reference[i4]! : (col / (GRID - 1) - 0.5) * c.sheet.width\n const ey = reference ? reference[i4 + 1]! : (row / (GRID - 1) - 0.5) * c.sheet.height\n const ez = reference ? reference[i4 + 2]! : 0\n maxError = Math.max(\n maxError,\n Math.abs(at0[i4]! - ex),\n Math.abs(at0[i4 + 1]! - ey),\n Math.abs(at0[i4 + 2]! - ez),\n )\n }\n }\n results.push({\n name: `bias: ${c.name} → ${scales ? 'flat at 0' : 'ignores bias'}`,\n maxError,\n pass: maxError < PARITY_EPSILON,\n })\n }\n return results\n}\n\n/** Run every parity case. Requires a WebGL2 context with float render targets. */\nexport function runParityHarness(canvas?: HTMLCanvasElement): ParityResult[] {\n const cnv = canvas ?? document.createElement('canvas')\n const gl = cnv.getContext('webgl2')\n if (!gl) throw new Error('[paperlab parity] WebGL2 unavailable')\n if (!gl.getExtension('EXT_color_buffer_float')) {\n throw new Error('[paperlab parity] EXT_color_buffer_float unavailable')\n }\n\n const point = new THREE.Vector3()\n const parity = parityCases.map((c) => {\n const gpu = runCaseOnGPU(gl, c)\n let maxError = 0\n for (let row = 0; row < GRID; row++) {\n for (let col = 0; col < GRID; col++) {\n const u = col / (GRID - 1)\n const v = row / (GRID - 1)\n point.set((u - 0.5) * c.sheet.width, (v - 0.5) * c.sheet.height, 0)\n displacePoint(point, u, v, c.stack, { t: c.t, sheet: c.sheet })\n const i4 = (row * GRID + col) * 4\n maxError = Math.max(\n maxError,\n Math.abs(gpu[i4]! - point.x),\n Math.abs(gpu[i4 + 1]! - point.y),\n Math.abs(gpu[i4 + 2]! - point.z),\n )\n }\n }\n return { name: c.name, maxError, pass: maxError < PARITY_EPSILON }\n })\n return [...parity, ...runBiasCases(gl)]\n}\n"],"mappings":"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","names":["Paper"]}
1
+ {"version":3,"sources":["../src/Paper.tsx","../src/config/field-export.ts","../src/field/parity.ts"],"sourcesContent":["import { Canvas } from '@react-three/fiber'\nimport { forwardRef, useMemo } from 'react'\nimport { PaperMesh, useResolvedConfig, type PaperHandle, type PaperMeshProps } from './PaperMesh'\nimport { PaperFallback, PaperMirror, supportsWebGL } from './a11y'\nimport { PaperLighting } from './scene/PaperLighting'\nimport { PaperBackdrop } from './scene/backdrop'\nimport { ReleaseContextOnUnmount } from './scene/release'\n\nexport interface PaperProps extends PaperMeshProps {\n /** Extra children rendered inside the canvas (lights are provided). */\n children?: React.ReactNode\n className?: string\n style?: React.CSSProperties\n}\n\n/**\n * `<Paper />` owns its own `<Canvas>` and fills its parent container —\n * the parent must have a height. Use `<PaperMesh />` inside an existing\n * R3F scene instead.\n *\n * Ships with its accessibility layer: a hidden DOM mirror of the content,\n * `prefers-reduced-motion` support, and a flat DOM fallback when WebGL is\n * unavailable.\n */\nexport const Paper = forwardRef<PaperHandle, PaperProps>(function Paper(\n { children, className, style, ...meshProps },\n ref,\n) {\n // Keyed on every prop resolveConfig reads — a content/stock/behavior change\n // must refresh the fallback, mirror, and lighting, not just the mesh.\n const config = useResolvedConfig(meshProps)\n const webgl = useMemo(() => (typeof window === 'undefined' ? true : supportsWebGL()), [])\n\n return (\n <div className={className} style={{ width: '100%', height: '100%', ...style }}>\n {webgl ? (\n <Canvas shadows camera={{ position: [0, 0.35, 2.4], fov: 40 }} dpr={[1, 2]}>\n <PaperBackdrop backdrop={config.scene.backdrop} />\n <PaperLighting\n preset={config.scene.lighting}\n // The overrides, which this component was resolving without: a\n // preset whose scene said \"studio, but dimmer\" rendered as plain\n // studio here while the editor showed the dimmer one.\n light={config.scene.light}\n // One floor height, shared: the ground the shadow falls on is the\n // ground a sheet lands on.\n floor={config.scene.floor.enabled ? config.scene.floor.y : -1.05}\n scale={8}\n reducedMotion={meshProps.reducedMotion}\n // A burn bright enough to light the room says so on its source.\n damage={meshProps.damage}\n />\n {config.scene.floor.enabled && (\n // Dark, matte and large enough to leave no edge in frame: what the\n // fire's light pools on, and what the paper it cuts loose lands on.\n <mesh rotation-x={-Math.PI / 2} position={[0, config.scene.floor.y, 0]} receiveShadow>\n <planeGeometry args={[40, 40]} />\n <meshStandardMaterial\n color={config.scene.floor.color}\n roughness={config.scene.floor.roughness}\n />\n </mesh>\n )}\n <PaperMesh ref={ref} {...meshProps} />\n {children}\n <ReleaseContextOnUnmount />\n </Canvas>\n ) : (\n <PaperFallback config={config} />\n )}\n <PaperMirror config={config} />\n </div>\n )\n})\n","import {\n sceneSchema,\n type ContentConfig,\n type PaperConfig,\n type PaperStatesInput,\n type SceneConfigInput,\n} from './schema'\nimport { diffConfig } from './diff'\nimport { AGENT_PAYLOAD_VERSION } from './agent-payload'\nimport { getLayout } from '../field/layouts'\n\n/**\n * Field-mode export. Every preset a field references is INLINED as a const —\n * receivers don't have the sender's local preset library. Same fixed payload\n * anatomy as the single-paper export, same version.\n */\n\nexport interface FieldExportPaper {\n /** Display name of the preset this slot uses (const naming + description). */\n presetName: string\n /** The RESOLVED preset config (user presets included — nothing to fetch). */\n preset: PaperConfig\n content?: ContentConfig\n /** Per-slot state overrides (slot 3's hover deeper than slot 4's). */\n states?: PaperStatesInput\n}\n\nexport interface FieldExportZone {\n id: string\n accept?: string[]\n bounds: { position: [number, number, number]; size: [number, number] }\n highlight?: 'none' | 'glow' | 'outline'\n}\n\nexport interface FieldExportInput {\n layout: string\n layoutOptions?: Record<string, unknown>\n motion?: { driver?: 'autoplay' | 'drag' | 'none'; speed?: number }\n entrance?: { type?: 'rise' | 'scatter' | 'none'; stagger?: number; duration?: number }\n papers: FieldExportPaper[]\n /** How the gallery is lit. Omitted means the schema's own default rig. */\n scene?: SceneConfigInput\n /** Drop zones — exported as `<DropZone>` children with an onPlace stub. */\n zones?: FieldExportZone[]\n}\n\nconst MOTION_DEFAULTS = { driver: 'autoplay', speed: 0.5 }\nconst ENTRANCE_DEFAULTS = { type: 'rise', stagger: 0.06, duration: 0.9 }\n\nfunction camel(name: string): string {\n const id = name.replace(/[^a-zA-Z0-9]+(.)/g, (_, c: string) => c.toUpperCase()).replace(/[^a-zA-Z0-9]/g, '')\n const safe = /^[A-Za-z]/.test(id) ? id : `preset${id}`\n return safe.charAt(0).toLowerCase() + safe.slice(1)\n}\n\ninterface DistinctPreset {\n varName: string\n presetName: string\n preset: PaperConfig\n}\n\n/** Distinct presets in slot order, with collision-safe const names. */\nexport function distinctFieldPresets(input: FieldExportInput): DistinctPreset[] {\n const byKey = new Map<string, DistinctPreset>()\n const usedNames = new Set<string>()\n for (const paper of input.papers) {\n const key = JSON.stringify(paper.preset)\n if (byKey.has(key)) continue\n let varName = camel(paper.presetName)\n while (usedNames.has(varName)) varName += '2'\n usedNames.add(varName)\n byKey.set(key, { varName, presetName: paper.presetName, preset: paper.preset })\n }\n return [...byKey.values()]\n}\n\n/**\n * The entries that differ from the defaults.\n *\n * Compared STRUCTURALLY rather than by reference. Most of what passes\n * through here is numbers and strings, where the two agree — but a layout\n * option holding an array, or a scene holding a nested rig, is never\n * reference-equal to the default it was copied from, so those exported a\n * prop that said exactly what the default already said.\n */\nfunction nonDefault<T extends Record<string, unknown>>(\n value: T | undefined,\n defaults: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value ?? {})) {\n if (v === undefined) continue\n const same = v === defaults[k] || JSON.stringify(v) === JSON.stringify(defaults[k])\n if (!same) out[k] = v\n }\n return out\n}\n\n/** The layout/motion/entrance props of the export, defaults stripped. */\nexport function diffFieldProps(input: FieldExportInput): Record<string, unknown> {\n const out: Record<string, unknown> = { layout: input.layout }\n const layout = getLayout(input.layout)\n const layoutOptions = nonDefault(input.layoutOptions ?? {}, layout.defaults as Record<string, unknown>)\n if (Object.keys(layoutOptions).length > 0) out.layoutOptions = layoutOptions\n const motion = nonDefault(input.motion, MOTION_DEFAULTS)\n if (Object.keys(motion).length > 0) out.motion = motion\n const entrance = nonDefault(input.entrance, ENTRANCE_DEFAULTS)\n if (Object.keys(entrance).length > 0) out.entrance = entrance\n // Resolved through the schema before comparing, so an input naming only\n // `lighting` is judged against a whole default rig rather than a\n // half-filled one.\n const scene = nonDefault(\n sceneSchema.parse(input.scene ?? {}) as unknown as Record<string, unknown>,\n sceneSchema.parse({}) as unknown as Record<string, unknown>,\n )\n if (Object.keys(scene).length > 0) out.scene = scene\n return out\n}\n\nconst LAYOUT_PHRASES: Record<string, string> = {\n ring: 'a ring you can see around',\n fan: 'a fanned swatch deck hinged at one corner',\n spread: 'a stack slid sideways, each sheet bowing further than the last',\n pile: 'a heap on a desk, the sheets underneath pressed flat',\n wall: 'a pinned studio wall of sheets, none hanging quite square',\n spill: 'a dropped stack caught mid-air, each sheet bent its own way',\n sweep: 'a specimen chart of one sheet at every stage of its deformation',\n book: 'an open book, its pages splayed from a shared spine',\n accordion: 'one continuous strip folded into a concertina',\n rack: 'prints stood in a row and leaning back against a wall',\n sheet: 'a stamp-block grid on a shared backing sheet',\n}\n\n/** The one-line visual an agent verifies after `npm run dev`. */\nexport function describeFieldConfig(input: FieldExportInput): string {\n const n = input.papers.length\n const stateful = input.papers.some((p) => p.preset.states || p.states)\n const zones = input.zones ?? []\n const parts: string[] = []\n\n if (input.layout === 'sheet') {\n const rows = Number(input.layoutOptions?.rows ?? 2)\n const columns = Number(input.layoutOptions?.columns ?? 5)\n parts.push(`a ${rows}×${columns} sheet of papers on a shared backing`)\n } else {\n const layoutPhrase = LAYOUT_PHRASES[input.layout] ?? `a ${input.layout} layout`\n parts.push(`${n} papers arranged in ${layoutPhrase}`)\n }\n\n const driver = input.motion?.driver ?? 'autoplay'\n if (stateful) {\n // Stateful fields render static and interactive — the motion driver is moot.\n parts.push('hovering peels/reacts per paper (interaction states)')\n if (zones.length > 0) {\n parts.push(\n `dragging one past its tear threshold detaches it to carry to the ${zones\n .map((z) => z.id)\n .join(' or ')} zone (release elsewhere flutters it back)`,\n )\n }\n } else {\n if (driver === 'autoplay') parts.push('slowly orbiting on their own')\n if (driver === 'drag') parts.push('draggable sideways with the pointer')\n if (zones.length > 0) parts.push(`${zones.length} drop zone${zones.length > 1 ? 's' : ''}`)\n }\n\n const distinct = distinctFieldPresets(input)\n if (distinct.length > 1) {\n parts.push(`mixing ${distinct.length} paper presets (${distinct.map((d) => d.presetName).join(', ')})`)\n }\n return parts.join(', ')\n}\n\n/** Component source shared by the JSX snippet and the agent payload. */\nexport function buildFieldComponentSource(input: FieldExportInput): string {\n const distinct = distinctFieldPresets(input)\n const varByKey = new Map(distinct.map((d) => [JSON.stringify(d.preset), d.varName]))\n\n // `satisfies` (not `as const`) — const-asserted arrays turn readonly and\n // stop matching the library's input types.\n const consts = distinct\n .map(\n (d) =>\n `const ${d.varName} = ${JSON.stringify(diffConfig(d.preset), null, 2)} satisfies PaperConfigInput`,\n )\n .join('\\n\\n')\n\n const slots = input.papers\n .map((paper) => {\n const varName = varByKey.get(JSON.stringify(paper.preset))!\n const content = paper.content ? `, content: ${JSON.stringify(paper.content)}` : ''\n const states = paper.states ? `, states: ${JSON.stringify(paper.states)}` : ''\n return ` { preset: ${varName}${content}${states} },`\n })\n .join('\\n')\n\n const props = Object.entries(diffFieldProps(input))\n .map(([key, value]) =>\n typeof value === 'string' ? ` ${key}=\"${value}\"` : ` ${key}={${JSON.stringify(value)}}`,\n )\n .join('\\n')\n\n const zones = input.zones ?? []\n const zoneImport = zones.length > 0 ? ', DropZone' : ''\n const zoneChildren = zones\n .map((zone) => {\n const accept = zone.accept ? ` accept={${JSON.stringify(zone.accept)}}` : ''\n const highlight = zone.highlight && zone.highlight !== 'glow' ? ` highlight=\"${zone.highlight}\"` : ''\n return ` <DropZone\n id=\"${zone.id}\"${accept}\n bounds={${JSON.stringify(zone.bounds)}}${highlight}\n onPlace={(paper, zone) => {\n // First-run smoke test. You should see this in the console on drop:\n console.log(paper.presetName, '→', zone.id)\n // Then replace it: stamp a postmark, advance the flow, persist the placement, …\n }}\n />`\n })\n .join('\\n')\n\n if (zones.length === 0) {\n return `import { PaperField, type FieldPaperSlot, type PaperConfigInput${zoneImport} } from 'paperlab'\n\n${consts}\n\nconst papers: FieldPaperSlot[] = [\n${slots}\n]\n\nexport function PaperGallery() {\n return (\n <PaperField\n papers={papers}\n${props}\n />\n )\n}`\n }\n\n return `import { PaperField, type FieldPaperSlot, type PaperConfigInput${zoneImport} } from 'paperlab'\n\n${consts}\n\nconst papers: FieldPaperSlot[] = [\n${slots}\n]\n\nexport function PaperGallery() {\n return (\n <PaperField\n papers={papers}\n${props}\n >\n${zoneChildren}\n </PaperField>\n )\n}`\n}\n\n/** The self-contained field integration brief — one paste into a coding agent. */\nexport function buildFieldAgentPayload(input: FieldExportInput): string {\n return `Integrate a Paperlab paper gallery into this project. (paperlab agent-payload v${AGENT_PAYLOAD_VERSION})\n\n1. Install the dependencies:\n\n npm i paperlab three @react-three/fiber gsap\n\n2. Create the component below as \\`components/PaperGallery.tsx\\` (or the\n project's component convention). It is self-contained: every preset it\n uses is inlined, and it owns its own <Canvas>:\n\n\\`\\`\\`tsx\n${buildFieldComponentSource(input)}\n\\`\\`\\`\n\n3. Sizing: the component fills its parent container. Place it where I ask;\n give the parent an explicit height.\n\n4. Verify: run the dev server. You should see ${describeFieldConfig(input)}.\n If the canvas is blank, the parent container almost certainly has no height.\n Give it one (this is the classic React Three Fiber integration bug, not a\n paperlab bug).\n\nConstraints: don't modify the preset values; three >= 0.160 and React 19 are\nrequired; the component needs no props.`\n}\n","import * as THREE from 'three'\nimport type { DeformerInstance, SheetDims } from '../deformers/types'\nimport { displacePoint } from '../deformers/compose'\nimport { getDeformer } from '../deformers/registry'\nimport { buildDisplacementGLSL } from './compose'\n\n/**\n * Golden-vector parity: the JS (hero) and GLSL (field) implementations of a\n * deformer must produce identical displacements. This harness evaluates the\n * GLSL path on the real GPU — the stack's displacement functions run in a\n * fragment shader over an N×N grid, rendered to a float target and read\n * back — then epsilon-compares against the JS path. Browser-only (WebGL2);\n * driven by tools/parity.mjs in CI.\n */\n\nconst GRID = 48\n/** highp float + sin/cos implementation differences across GPUs. */\nexport const PARITY_EPSILON = 5e-4\n\nexport interface ParityCase {\n name: string\n stack: DeformerInstance[]\n sheet: SheetDims\n t: number\n}\n\nexport interface ParityResult {\n name: string\n maxError: number\n pass: boolean\n}\n\n/** One case per deformer feature worth guarding, both easy and adversarial. */\nexport const parityCases: ParityCase[] = [\n {\n name: 'roll: defaults',\n stack: [{ type: 'roll', options: { angle: 90, boundary: 0, radius: 0.12, thickness: 0.04 } }],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n name: 'roll: tight receipt roll, rolling down',\n stack: [{ type: 'roll', options: { angle: 270, boundary: -0.4, radius: 0.07, thickness: 0.006 } }],\n sheet: { width: 1, height: 2.6 },\n t: 0,\n },\n {\n name: 'curl: bottom-right peel',\n stack: [{ type: 'curl', options: { corner: 'bottom-right', amount: 0.45, radius: 0.2, skew: 0 } }],\n sheet: { width: 1.5, height: 1 },\n t: 0,\n },\n {\n name: 'curl: skewed top-left',\n stack: [{ type: 'curl', options: { corner: 'top-left', amount: 0.7, radius: 0.3, skew: 15 } }],\n sheet: { width: 1, height: 1 },\n t: 0,\n },\n {\n name: 'bend: positive arc at an angle',\n stack: [{ type: 'bend', options: { curvature: 1.2, angle: 33 } }],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n /**\n * The gentle end, which nothing used to cover: the gate only ever tried\n * |curvature| ≥ 0.6, and `photo-print` — the field starter preset — bends\n * at 0.35. That band is where the arc's float32 evaluation loses its\n * cancellation, and where the two paths were 6e-4 apart until `bend` was\n * rewritten in cancellation-free form. Keep a case down here.\n */\n name: 'bend: barely-there arc (the band photo-print lives in)',\n stack: [{ type: 'bend', options: { curvature: 0.35, angle: 0 } }],\n sheet: { width: 1.2, height: 0.9 },\n t: 0,\n },\n {\n name: 'bend: the gentlest arc the schema allows',\n stack: [{ type: 'bend', options: { curvature: 0.02, angle: 61 } }],\n sheet: { width: 2, height: 2.6 },\n t: 0,\n },\n {\n name: 'bend: negative arc',\n stack: [{ type: 'bend', options: { curvature: -0.8, angle: 0 } }],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n name: 'fold: 90° hinge',\n stack: [{ type: 'fold', options: { angle: 90, offset: 0, foldAngle: 90, radius: 0.06 } }],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n name: 'fold: deep fold, travelling down',\n stack: [{ type: 'fold', options: { angle: 270, offset: 0.2, foldAngle: 165, radius: 0.04 } }],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n name: 'wave: free ripple at t=1.234',\n stack: [\n {\n type: 'wave',\n options: { amplitude: 0.05, wavelength: 0.5, speed: 1, angle: 20, pinnedEdge: 'none' },\n },\n ],\n sheet: { width: 1, height: 1 },\n t: 1.234,\n },\n {\n name: 'wave: pinned top at t=2.5',\n stack: [\n {\n type: 'wave',\n options: { amplitude: 0.04, wavelength: 0.4, speed: 1.5, angle: 80, pinnedEdge: 'top' },\n },\n ],\n sheet: { width: 1.2, height: 1.5 },\n t: 2.5,\n },\n {\n name: 'drape: banner hung from the top',\n stack: [\n {\n type: 'drape',\n options: { amplitude: 0.17, folds: 5, falloff: 1.6, irregular: 0.45, gather: 0.5, pinnedEdge: 'top' },\n },\n ],\n sheet: { width: 1.5, height: 8.5 },\n t: 0,\n },\n {\n name: 'drape: deep irregular folds pinned at the bottom',\n stack: [\n {\n type: 'drape',\n options: { amplitude: 0.55, folds: 11, falloff: 0.4, irregular: 1, gather: 1, pinnedEdge: 'bottom' },\n },\n ],\n sheet: { width: 2.2, height: 3 },\n t: 0,\n },\n {\n name: 'crumple: defaults',\n stack: [{ type: 'crumple', options: { amount: 0.35, scale: 3, pull: 0.4, seed: 0 } }],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n // The adversarial one. `fract` is a sawtooth, so the two halves disagree\n // hardest where a crease lands exactly on a sample — a fine scale and a\n // seed whose fold directions are near-axis puts the most creases in\n // reach of the grid.\n name: 'crumple: fully crushed, fine creases, off-axis seed',\n stack: [{ type: 'crumple', options: { amount: 1, scale: 7.5, pull: 1, seed: 5 } }],\n sheet: { width: 1.3, height: 0.9 },\n t: 0,\n },\n {\n // Crush then curl, which is the order the crumple BEHAVIOR stacks them:\n // the creases have to be placed on the flat sheet, not on a bent one.\n name: 'stacked: crumple ∘ bend (the crumple behavior)',\n stack: [\n { type: 'crumple', options: { amount: 0.62, scale: 3.3, pull: 0.5, seed: 2 } },\n { type: 'bend', options: { curvature: 0.31, angle: 35 } },\n ],\n sheet: { width: 1.1, height: 1.4 },\n t: 0,\n },\n {\n name: 'stacked: letter-fold pair (fold ∘ fold)',\n stack: [\n { type: 'fold', options: { angle: 270, offset: 0.2333, foldAngle: 120, radius: 0.05 } },\n { type: 'fold', options: { angle: 90, offset: 0.2333, foldAngle: 100, radius: 0.08 } },\n ],\n sheet: { width: 1, height: 1.4 },\n t: 0,\n },\n {\n name: 'stacked: bend ∘ roll ∘ wave',\n stack: [\n { type: 'bend', options: { curvature: 0.6, angle: 0 } },\n { type: 'roll', options: { angle: 90, boundary: 0.1, radius: 0.15, thickness: 0 } },\n {\n type: 'wave',\n options: { amplitude: 0.02, wavelength: 0.6, speed: 0.7, angle: 45, pinnedEdge: 'none' },\n },\n ],\n sheet: { width: 1, height: 1.4 },\n t: 0.8,\n },\n]\n\nfunction buildParityFragment(stack: DeformerInstance[], sheet: SheetDims): string {\n const composed = buildDisplacementGLSL(stack, sheet)\n return /* glsl */ `#version 300 es\nprecision highp float;\nuniform float uPlTime;\nuniform float uPlBias;\n${composed.functionsSrc}\n${composed.displaceSrc}\nout vec4 outColor;\nvoid main() {\n vec2 uv = (gl_FragCoord.xy - 0.5) / float(${GRID - 1});\n vec3 p = vec3((uv - 0.5) * uSheet, 0.0);\n outColor = vec4(plDisplace(p, uv, uPlTime, uPlBias), 1.0);\n}\n`\n}\n\nconst VERT = `#version 300 es\nin vec2 aPos;\nvoid main() { gl_Position = vec4(aPos, 0.0, 1.0); }\n`\n\nfunction compile(gl: WebGL2RenderingContext, type: number, src: string): WebGLShader {\n const shader = gl.createShader(type)!\n gl.shaderSource(shader, src)\n gl.compileShader(shader)\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n throw new Error(`[paperlab parity] shader compile failed: ${gl.getShaderInfoLog(shader)}`)\n }\n return shader\n}\n\nfunction runCaseOnGPU(gl: WebGL2RenderingContext, c: ParityCase, bias = 1): Float32Array {\n const composed = buildDisplacementGLSL(c.stack, c.sheet)\n const program = gl.createProgram()!\n gl.attachShader(program, compile(gl, gl.VERTEX_SHADER, VERT))\n gl.attachShader(program, compile(gl, gl.FRAGMENT_SHADER, buildParityFragment(c.stack, c.sheet)))\n gl.linkProgram(program)\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n throw new Error(`[paperlab parity] link failed: ${gl.getProgramInfoLog(program)}`)\n }\n // biome-ignore lint/correctness/useHookAtTopLevel: WebGL's gl.useProgram, not a React hook\n gl.useProgram(program)\n\n for (const [name, value] of Object.entries(composed.uniforms)) {\n const loc = gl.getUniformLocation(program, name)\n if (!loc) continue\n if (typeof value === 'number') gl.uniform1f(loc, value)\n else if (value.length === 2) gl.uniform2f(loc, value[0]!, value[1]!)\n else if (value.length === 3) gl.uniform3f(loc, value[0]!, value[1]!, value[2]!)\n else gl.uniform4f(loc, value[0]!, value[1]!, value[2]!, value[3]!)\n }\n const timeLoc = gl.getUniformLocation(program, 'uPlTime')\n if (timeLoc) gl.uniform1f(timeLoc, c.t)\n const biasLoc = gl.getUniformLocation(program, 'uPlBias')\n if (biasLoc) gl.uniform1f(biasLoc, bias)\n\n const quad = gl.createBuffer()!\n gl.bindBuffer(gl.ARRAY_BUFFER, quad)\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW)\n const posLoc = gl.getAttribLocation(program, 'aPos')\n gl.enableVertexAttribArray(posLoc)\n gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0)\n\n const tex = gl.createTexture()!\n gl.bindTexture(gl.TEXTURE_2D, tex)\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, GRID, GRID, 0, gl.RGBA, gl.FLOAT, null)\n const fbo = gl.createFramebuffer()!\n gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)\n gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0)\n gl.viewport(0, 0, GRID, GRID)\n gl.drawArrays(gl.TRIANGLES, 0, 3)\n\n const out = new Float32Array(GRID * GRID * 4)\n gl.readPixels(0, 0, GRID, GRID, gl.RGBA, gl.FLOAT, out)\n gl.bindFramebuffer(gl.FRAMEBUFFER, null)\n return out\n}\n\n/**\n * The bias contract, checked on the GPU: a stack of strength-bearing\n * deformers must go completely flat at bias 0, and a stack of deformers that\n * opt out (roll) must ignore bias entirely. Parity alone can't catch this —\n * it only ever evaluates bias 1.\n */\nfunction runBiasCases(gl: WebGL2RenderingContext): ParityResult[] {\n const results: ParityResult[] = []\n for (const c of parityCases) {\n const strengths = c.stack.map((i) => getDeformer(i.type).glsl?.strength !== undefined)\n // Mixed stacks flatten only partway — nothing crisp to assert.\n if (!strengths.every((h) => h === strengths[0])) continue\n const scales = strengths[0]\n\n const at0 = runCaseOnGPU(gl, c, 0)\n const reference = scales ? null : runCaseOnGPU(gl, c, 1)\n let maxError = 0\n for (let row = 0; row < GRID; row++) {\n for (let col = 0; col < GRID; col++) {\n const i4 = (row * GRID + col) * 4\n // At bias 0 a scaling stack collapses onto the flat sheet; an opted-out\n // stack must land exactly where bias 1 put it.\n const ex = reference ? reference[i4]! : (col / (GRID - 1) - 0.5) * c.sheet.width\n const ey = reference ? reference[i4 + 1]! : (row / (GRID - 1) - 0.5) * c.sheet.height\n const ez = reference ? reference[i4 + 2]! : 0\n maxError = Math.max(\n maxError,\n Math.abs(at0[i4]! - ex),\n Math.abs(at0[i4 + 1]! - ey),\n Math.abs(at0[i4 + 2]! - ez),\n )\n }\n }\n results.push({\n name: `bias: ${c.name} → ${scales ? 'flat at 0' : 'ignores bias'}`,\n maxError,\n pass: maxError < PARITY_EPSILON,\n })\n }\n return results\n}\n\n/** Run every parity case. Requires a WebGL2 context with float render targets. */\nexport function runParityHarness(canvas?: HTMLCanvasElement): ParityResult[] {\n const cnv = canvas ?? document.createElement('canvas')\n const gl = cnv.getContext('webgl2')\n if (!gl) throw new Error('[paperlab parity] WebGL2 unavailable')\n if (!gl.getExtension('EXT_color_buffer_float')) {\n throw new Error('[paperlab parity] EXT_color_buffer_float unavailable')\n }\n\n const point = new THREE.Vector3()\n const parity = parityCases.map((c) => {\n const gpu = runCaseOnGPU(gl, c)\n let maxError = 0\n for (let row = 0; row < GRID; row++) {\n for (let col = 0; col < GRID; col++) {\n const u = col / (GRID - 1)\n const v = row / (GRID - 1)\n point.set((u - 0.5) * c.sheet.width, (v - 0.5) * c.sheet.height, 0)\n displacePoint(point, u, v, c.stack, { t: c.t, sheet: c.sheet })\n const i4 = (row * GRID + col) * 4\n maxError = Math.max(\n maxError,\n Math.abs(gpu[i4]! - point.x),\n Math.abs(gpu[i4 + 1]! - point.y),\n Math.abs(gpu[i4 + 2]! - point.z),\n )\n }\n }\n return { name: c.name, maxError, pass: maxError < PARITY_EPSILON }\n })\n return [...parity, ...runBiasCases(gl)]\n}\n"],"mappings":"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","names":["Paper"]}
@@ -2291,7 +2291,7 @@ declare const paperConfigSchema: z.ZodObject<{
2291
2291
  type PaperConfig = z.infer<typeof paperConfigSchema>;
2292
2292
  type PaperConfigInput = z.input<typeof paperConfigSchema>;
2293
2293
 
2294
- /** A field slot references a preset — the spec's component/instance model. */
2294
+ /** A field slot references a preset — a component/instance model. */
2295
2295
  interface FieldPaperSlot {
2296
2296
  preset?: string | PaperConfigInput;
2297
2297
  /**
@@ -2291,7 +2291,7 @@ declare const paperConfigSchema: z.ZodObject<{
2291
2291
  type PaperConfig = z.infer<typeof paperConfigSchema>;
2292
2292
  type PaperConfigInput = z.input<typeof paperConfigSchema>;
2293
2293
 
2294
- /** A field slot references a preset — the spec's component/instance model. */
2294
+ /** A field slot references a preset — a component/instance model. */
2295
2295
  interface FieldPaperSlot {
2296
2296
  preset?: string | PaperConfigInput;
2297
2297
  /**
package/dist/stage.cjs CHANGED
@@ -1877,7 +1877,7 @@ var paperConfigSchema = import_zod15.z.object({
1877
1877
  ctx.addIssue({
1878
1878
  code: import_zod15.z.ZodIssueCode.custom,
1879
1879
  path: ["physics"],
1880
- message: "the strip simulation and behavior/deformers are exclusive \u2014 the roll owns the vertices, and its rows are chain nodes rather than the sheet's own grid (pick Shape OR Strip)"
1880
+ message: "the strip simulation and behavior/deformers cannot be used together. The roll owns the vertices, and its rows are chain nodes rather than the sheet's own grid (pick Shape OR Strip)"
1881
1881
  });
1882
1882
  }
1883
1883
  if (config.states) {
@@ -1899,7 +1899,7 @@ var paperConfigSchema = import_zod15.z.object({
1899
1899
  ctx.addIssue({
1900
1900
  code: import_zod15.z.ZodIssueCode.custom,
1901
1901
  path: ["states", "states", name, "overrides"],
1902
- message: `state "${name}" overrides don't validate against the paper schema: ${first ? `${first.path.join(".")} \u2014 ${first.message}` : "invalid"}`
1902
+ message: `state "${name}" overrides don't validate against the paper schema: ${first ? `${first.path.join(".")}: ${first.message}` : "invalid"}`
1903
1903
  });
1904
1904
  }
1905
1905
  }
@@ -2223,7 +2223,7 @@ var DAMAGE_LOOK_DEFAULTS = {
2223
2223
  emberFlicker: 1.65,
2224
2224
  emberGlow: 1.25,
2225
2225
  sparkle: 0.5,
2226
- // As wide as the char band (Noor, 2026-09-13): the lip has to be seen. At
2226
+ // As wide as the char band: the lip has to be seen. At
2227
2227
  // 0.95 mm it was a hairline tracing the edge.
2228
2228
  lipWidth: 3.5,
2229
2229
  charWidth: 3.5,
@@ -4021,7 +4021,7 @@ function resolveDeformerStack(raw) {
4021
4021
  if (!parsed.success) {
4022
4022
  const issue = parsed.error.issues[0];
4023
4023
  throw new Error(
4024
- `[paperlab] deformers[${i}] ("${instance.type}"): ${issue ? `${issue.path.join(".") || "options"} \u2014 ${issue.message}` : "invalid options"}`
4024
+ `[paperlab] deformers[${i}] ("${instance.type}"): ${issue ? `${issue.path.join(".") || "options"}: ${issue.message}` : "invalid options"}`
4025
4025
  );
4026
4026
  }
4027
4027
  return {
@@ -6154,11 +6154,11 @@ var THREE11 = __toESM(require("three"), 1);
6154
6154
  var import_react10 = require("react");
6155
6155
  var import_fiber5 = require("@react-three/fiber");
6156
6156
 
6157
- // ../../node_modules/.pnpm/three-custom-shader-material@6.4.0_@react-three+fiber@9.7.0_@types+react@19.2.18_react-_9d22c69745b4efc41799679dbea453f4/node_modules/three-custom-shader-material/three-custom-shader-material.es.js
6157
+ // ../../node_modules/.pnpm/three-custom-shader-material@6.4.0_@react-three+fiber@9.7.0_@types+react@19.2.18_react-_69d6cbf74dd7fcdbd515d35271d34b04/node_modules/three-custom-shader-material/three-custom-shader-material.es.js
6158
6158
  var import_jsx_runtime4 = require("react/jsx-runtime");
6159
6159
  var r = __toESM(require("react"), 1);
6160
6160
 
6161
- // ../../node_modules/.pnpm/three-custom-shader-material@6.4.0_@react-three+fiber@9.7.0_@types+react@19.2.18_react-_9d22c69745b4efc41799679dbea453f4/node_modules/three-custom-shader-material/vanilla/three-custom-shader-material.es.js
6161
+ // ../../node_modules/.pnpm/three-custom-shader-material@6.4.0_@react-three+fiber@9.7.0_@types+react@19.2.18_react-_69d6cbf74dd7fcdbd515d35271d34b04/node_modules/three-custom-shader-material/vanilla/three-custom-shader-material.es.js
6162
6162
  var v = __toESM(require("three"), 1);
6163
6163
  var D = (
6164
6164
  /* glsl */
@@ -6749,7 +6749,7 @@ var j = class extends v.Material {
6749
6749
  }
6750
6750
  };
6751
6751
 
6752
- // ../../node_modules/.pnpm/three-custom-shader-material@6.4.0_@react-three+fiber@9.7.0_@types+react@19.2.18_react-_9d22c69745b4efc41799679dbea453f4/node_modules/three-custom-shader-material/three-custom-shader-material.es.js
6752
+ // ../../node_modules/.pnpm/three-custom-shader-material@6.4.0_@react-three+fiber@9.7.0_@types+react@19.2.18_react-_69d6cbf74dd7fcdbd515d35271d34b04/node_modules/three-custom-shader-material/three-custom-shader-material.es.js
6753
6753
  function l(u, e2) {
6754
6754
  const t = r.useRef(false);
6755
6755
  r.useEffect(() => {
@@ -7524,7 +7524,7 @@ vec3 plScorchTint(float t) {
7524
7524
  return min(c / paper, vec3(1.0));
7525
7525
  }
7526
7526
 
7527
- /** The ember ramp (\xA75 zone 3): off \u2192 #870E03 \u2192 #A12108 \u2192 #F77E14 \u2192 #FEFBE0, hottest last. */
7527
+ /** The ember ramp: off \u2192 #870E03 \u2192 #A12108 \u2192 #F77E14 \u2192 #FEFBE0, hottest last. */
7528
7528
  vec3 plEmberRamp(float t) {
7529
7529
  vec3 c0 = plLinear(vec3(0.529, 0.055, 0.012));
7530
7530
  vec3 c1 = plLinear(vec3(0.631, 0.129, 0.031));
@@ -7554,7 +7554,7 @@ void plDamage(inout vec4 color, inout float roughness) {
7554
7554
  vec2 gradC = vec2(e.r - w.r, n.r - s.r) * 0.5 * perWorld;
7555
7555
  // Scorch reaches ahead of the char, and further UP than anywhere else:
7556
7556
  // the char a centimetre below this point, and the char a few millimetres
7557
- // around it, each lent to the scorch at a discount (\xA74.2, \xA74.5).
7557
+ // around it, each lent to the scorch at a discount.
7558
7558
  vec2 mmUv = vec2(PL_MM) / uSheetSize;
7559
7559
  float charBelow = max(texture2D(uDamage, uv - upUv * uLook2.y * mmUv).r, texture2D(uDamage, uv - upUv * 0.5 * uLook2.y * mmUv).r);
7560
7560
  // A ring of eight, averaged: four diagonal maxima drew the grid back in as
@@ -7613,7 +7613,7 @@ void plDamage(inout vec4 color, inout float roughness) {
7613
7613
  float lipEdge = uLook1.y * mix(0.7, 1.1, plFbm(vec2(dot(p, tangentR) / (5.0 * PL_MM), 1.7)));
7614
7614
  float charEnd = lipEdge + uLook0.x + uLook3.w * mix(0.8, 1.2, plFbm(vec2(dot(p, tangentR) / (7.0 * PL_MM), 4.3)));
7615
7615
  // Further above the burn than below it \u2014 hot gas rises and cooks the paper
7616
- // over it (\xA74.5) \u2014 out to the scorch's full reach straight up, and about
7616
+ // over it \u2014 out to the scorch's full reach straight up, and about
7617
7617
  // half of it below and beside.
7618
7618
  vec2 upMm = normalize(upUv * uSheetSize);
7619
7619
  float above = length(toward) > 1e-3 ? max(0.0, dot(-normalize(toward), upMm)) : 0.0;
@@ -9307,7 +9307,7 @@ function buildDisplacementGLSL(stack, sheet2) {
9307
9307
  const deformer = getDeformer(instance.type);
9308
9308
  if (!deformer.glsl) {
9309
9309
  throw new Error(
9310
- `[paperlab] Deformer "${instance.type}" has no GLSL implementation \u2014 it can't run in field mode.`
9310
+ `[paperlab] Deformer "${instance.type}" has no GLSL implementation, so it can't run in field mode.`
9311
9311
  );
9312
9312
  }
9313
9313
  const ns = `u${cap(instance.type)}${i}_`;
@@ -10740,10 +10740,9 @@ function FieldKeyboardMirror({
10740
10740
  children: [
10741
10741
  paperLabel(slot, i),
10742
10742
  carrying?.slot === i && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { "aria-live": "polite", children: [
10743
- " ",
10744
- "\u2014 carrying; zone ",
10743
+ ", carrying. Zone ",
10745
10744
  controller.current?.zoneIds()[carrying.zoneIndex] ?? "none",
10746
- "; Enter places, Escape returns"
10745
+ ". Enter places it, Escape puts it back."
10747
10746
  ] })
10748
10747
  ]
10749
10748
  },
@@ -11276,7 +11275,7 @@ var ModelBoundary = class extends import_react19.Component {
11276
11275
  return { failed: true };
11277
11276
  }
11278
11277
  componentDidCatch(error) {
11279
- console.warn("[paperlab] figure.model failed to load \u2014 using the capsule figure.", error);
11278
+ console.warn("[paperlab] figure.model failed to load. Using the capsule figure.", error);
11280
11279
  }
11281
11280
  render() {
11282
11281
  return this.state.failed ? this.props.fallback : this.props.children;
@@ -11776,7 +11775,7 @@ function warnOnce(cause) {
11776
11775
  if (warned) return;
11777
11776
  warned = true;
11778
11777
  console.warn(
11779
- "[paperlab/stage] Rendering without the print pass \u2014 tone curve, bloom, vignette and grain are off.\nThey need two optional peers:\n npm i @react-three/postprocessing postprocessing\nSet `grade` to all zeros to turn the pass off deliberately and silence this.",
11778
+ "[paperlab/stage] Rendering without the print pass, so tone curve, bloom, vignette and grain are off.\nThey need two optional peers:\n npm i @react-three/postprocessing postprocessing\nSet `grade` to all zeros to turn the pass off deliberately and silence this.",
11780
11779
  ...cause === void 0 ? [] : [cause]
11781
11780
  );
11782
11781
  }
@@ -12904,7 +12903,7 @@ var stagePresets = {
12904
12903
  archive: {
12905
12904
  id: "archive",
12906
12905
  label: "Archive",
12907
- description: "Narrow strips packed tight \u2014 a corridor of records you edge through.",
12906
+ description: "Narrow strips packed tight, a corridor of records you edge through.",
12908
12907
  stage: {
12909
12908
  path: walks.bend,
12910
12909
  // Far enough back that the figure reads as small; a `low` camera
@@ -13127,7 +13126,7 @@ const text = ${JSON.stringify(input.text)}` : "";
13127
13126
  const scrolls = stageSchema.parse(input.stage).showFigure ? "walk the figure" : "move the camera";
13128
13127
  const images = input.images?.length ? exportableImages(input.images) : null;
13129
13128
  const imagesConst = images ? `
13130
- ${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." : ""}
13129
+ ${images.substituted ? "\n// Your uploaded pictures cannot travel in a snippet, so these are\n// placeholders in the same order. Point them at your own files." : ""}
13131
13130
  const images = ${JSON.stringify(images.list, null, 2)}` : "";
13132
13131
  if (!input.scroll) {
13133
13132
  return `import { PaperStage, type StageConfigInput } from 'paperlab/stage'${input.paper ? "\nimport type { PaperConfigInput } from 'paperlab'" : ""}
@@ -13184,24 +13183,24 @@ ${propLines(input, " ")}
13184
13183
  }
13185
13184
  function buildStageAgentPayload(input) {
13186
13185
  const name = input.componentName ?? "PaperNave";
13187
- const sizing = input.scroll ? `4. Sizing: the component brings its own height \u2014 it reserves ${SCROLL_HEIGHTS} viewport
13186
+ const sizing = input.scroll ? `4. Sizing: the component brings its own height. It reserves ${SCROLL_HEIGHTS} viewport
13188
13187
  heights of scroll and pins the canvas inside that. Drop it into the page
13189
13188
  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;
13190
13189
  give the parent an explicit height.`;
13191
13190
  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";
13192
- return `Integrate a Paperlab stage \u2014 ${subject} \u2014 into this project. (paperlab agent-payload v${AGENT_PAYLOAD_VERSION})
13191
+ return `Integrate a Paperlab stage (${subject}) into this project. (paperlab agent-payload v${AGENT_PAYLOAD_VERSION})
13193
13192
 
13194
13193
  1. Install the dependencies:
13195
13194
 
13196
13195
  npm i paperlab three @react-three/fiber gsap @react-three/postprocessing postprocessing
13197
13196
 
13198
- The last two are only needed by stage mode \u2014 <Paper> and <PaperField> do
13199
- not use them \u2014 and they are optional even here: <PaperStage> loads the
13197
+ The last two are only needed by stage mode (<Paper> and <PaperField> do
13198
+ not use them), and they are optional even here: <PaperStage> loads the
13200
13199
  print pass on demand, so a stage without them renders, just without the
13201
13200
  tone curve, bloom, vignette and grain. Install them to get the grade.
13202
13201
 
13203
13202
  2. Create the component below as \`components/${name}.tsx\` (or the project's
13204
- component convention). It is self-contained \u2014 it owns its own <Canvas>,
13203
+ component convention). It is self-contained: it owns its own <Canvas>,
13205
13204
  its own camera and its own lighting:
13206
13205
 
13207
13206
  \`\`\`tsx
@@ -13214,7 +13213,7 @@ ${buildStageComponentSource(input)}
13214
13213
  ${sizing}
13215
13214
 
13216
13215
  5. Verify: run the dev server. You should see ${describeStage(input)}.
13217
- If the canvas is blank, the container almost certainly has no height \u2014 give
13216
+ If the canvas is blank, the container almost certainly has no height. Give
13218
13217
  it one (this is the classic React Three Fiber integration bug, not a
13219
13218
  paperlab bug).
13220
13219