paperlab 0.3.0 → 0.4.0

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/index.d.ts CHANGED
@@ -1,19 +1,19 @@
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, S as StateName, a as PaperConfigInput, b as SheetConfig, c as StockName, C as ContentConfigInput, B as BehaviorConfigInput, D as DeformerInstanceConfigInput, d as SurfaceConfigInput, e as SceneConfigInput, f as PhysicsConfigInput, g as PaperEdge, F as FieldPaperSlot, L as LightingName, h as FilmName, i as ContentConfig, j as SurfaceConfig, k as PaperStatesInput } from './slots-D0Nc_5dq.js';
5
- export { l as BackContentConfig, m as BehaviorConfig, n as ClothConfig, o as CoreStateName, p as DeformerInstanceConfig, q as FieldGroupData, r as PaperStates, s as PhysicsConfig, t as SceneConfig, u as StateDef, v as StateTransitionConfig, w as backContentSchema, x as behaviorConfigSchema, y as cardContentSchema, z as clothConfigSchema, A as contentSchema, E as coreStateNames, G as deformerInstanceSchema, H as filmNames, I as groupFieldPapers, J as lightingNames, K as paperConfigSchema, M as paperEdges, N as paperStatesSchema, O as physicsNames, Q as physicsSchema, R as receiptContentSchema, T as resolveFieldSlotConfig, U as sceneSchema, V as sheetSchema, W as stateDefSchema, X as stateTransitionSchema, Y as stockNames, Z as stockSchema, _ as surfaceSchema } from './slots-D0Nc_5dq.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, d as SceneConfigInput, e as PhysicsConfigInput, F as FieldPaperSlot, f as ContentConfig, g as PaperStatesInput, L as LightingName, h as FilmName } from './slots-BYoLxVZ4.js';
5
+ export { i as BackContentConfig, j as BehaviorConfig, k as ClothConfig, l as CoreStateName, m as DeformerInstanceConfig, n as PaperEdge, o as PaperStates, p as PhysicsConfig, q as SceneConfig, r as StateDef, s as StateName, t as StateTransitionConfig, u as SurfaceConfig, v as behaviorConfigSchema, w as clothConfigSchema, x as coreStateNames, y as lightingNames, z as paperConfigSchema, A as paperEdges, E as paperStatesSchema, G as physicsNames, H as stateDefSchema, I as stockNames } from './slots-BYoLxVZ4.js';
6
6
  import { z } from 'zod';
7
7
 
8
8
  /**
9
9
  * The interaction-state engine. A state is a set of parameter overrides on
10
- * the base preset — never a separate preset (spec M6 §1.1). The machine
10
+ * the base preset — never a separate preset. The machine
11
11
  * resolves each state to a full config (base + overrides), keeps ONE live
12
12
  * tween target of flattened numeric leaves, and always tweens FROM CURRENT
13
13
  * VALUES: a pointer that enters/leaves rapidly retargets the same tween
14
14
  * values instead of stacking or snapping.
15
15
  *
16
- * Delivery split (spec v0.2 §4: GSAP owns values, useFrame owns uploads):
16
+ * Delivery split (GSAP owns values, useFrame owns uploads):
17
17
  * GSAP animates the STABLE `flat` object in place; per-tick values are polled
18
18
  * off `liveConfig` by the consumer's frame loop, never pushed through React.
19
19
  * `onChange` fires only on STRUCTURAL boundaries (a transition's start and
@@ -22,10 +22,6 @@ import { z } from 'zod';
22
22
  */
23
23
  /** Built-in trigger events (v1 — user-defined wiring is parked for editor v2). */
24
24
  type StateEvent = 'enter' | 'leave' | 'down' | 'up' | 'pick' | 'place' | 'return';
25
- /** The fixed v1 transition table: pointer flow + pick/drop flow. */
26
- declare const stateEventTransitions: Record<string, Partial<Record<StateEvent, StateName>>>;
27
- /** The base config with `states` stripped — what a state resolves against. */
28
- declare function stripStates(config: PaperConfig): PaperConfig;
29
25
  /**
30
26
  * Resolve a state name to its full config: base + that state's overrides.
31
27
  * States without a recorded def (e.g. an untouched 'rest') are the base.
@@ -43,7 +39,6 @@ declare function resolveStateConfig(base: PaperConfig, state: string): PaperConf
43
39
  * cleanly throws here.
44
40
  */
45
41
  declare function recordStateOverride(config: PaperConfig, stateName: string, patch: Record<string, unknown>): PaperConfig;
46
- declare function flattenNumeric(value: unknown, prefix?: string, out?: Record<string, number>): Record<string, number>;
47
42
  interface PaperStateMachineOptions {
48
43
  /** Reduced motion: every transition applies instantly (duration 0). */
49
44
  instant?: boolean;
@@ -204,8 +199,6 @@ interface PaperHandle {
204
199
  /** Instant, legal return (picked → rest). */
205
200
  returnProgrammatic(): boolean;
206
201
  }
207
- /** Resolve preset + prop overrides into a validated config. */
208
- declare function resolveConfig(props: PaperMeshProps): PaperConfig;
209
202
  /**
210
203
  * The atom: one sheet of paper, hero-mode CPU path. The deformer stack (or
211
204
  * the cloth sim — never both) writes geometry positions each frame. GSAP
@@ -249,26 +242,6 @@ interface PlacedPaper {
249
242
  interface DropZoneProps extends DropZoneConfig {
250
243
  onPlace?(paper: PlacedPaper, zone: string): void;
251
244
  }
252
- interface ZoneEntry extends DropZoneConfig {
253
- onPlace?: DropZoneProps['onPlace'];
254
- }
255
- /** Shared zone state: `<DropZone>` children register; the carry loop hit-tests. */
256
- declare class DropZoneRegistry {
257
- private zones;
258
- private hoveredId;
259
- private listeners;
260
- private version;
261
- register(zone: ZoneEntry): () => void;
262
- list(): ZoneEntry[];
263
- get(id: string): ZoneEntry | undefined;
264
- get hovered(): string | null;
265
- setHovered(id: string | null): void;
266
- subscribe: (fn: () => void) => (() => void);
267
- getVersion: () => number;
268
- private notify;
269
- }
270
- /** True when `name` matches the zone's accept globs (all zones accept by default). */
271
- declare function zoneAccepts(zone: Pick<DropZoneConfig, 'accept'>, name: string): boolean;
272
245
  /**
273
246
  * A drop target inside a `<PaperField>`. While a paper is picked, its center
274
247
  * is tested against the bounds each frame; hovering applies the highlight
@@ -301,7 +274,7 @@ interface DeformerContext {
301
274
  * A deformer is a pure vertex-mapping function. The JS implementation is the
302
275
  * CPU path (hero mode) and must be allocation-free in the loop: it mutates
303
276
  * `out` in place. The mirrored GLSL implementation (GPU path, field mode)
304
- * lands in M4 — golden-vector parity tests will enforce identical results.
277
+ * lands with field mode — golden-vector parity tests will enforce identical results.
305
278
  */
306
279
  interface Deformer<O = Record<string, unknown>> {
307
280
  id: string;
@@ -310,7 +283,7 @@ interface Deformer<O = Record<string, unknown>> {
310
283
  optionsSchema: z.ZodType<O, z.ZodTypeDef, unknown>;
311
284
  /** Mutate `out` (sheet-local space; flat sheet is the XY plane facing +Z). */
312
285
  displace(out: THREE.Vector3, uv: THREE.Vector2, o: O, ctx: DeformerContext): void;
313
- /** GPU path — arrives with field mode (M4). */
286
+ /** GPU path — arrives with field mode. */
314
287
  glsl?: {
315
288
  chunk: string;
316
289
  uniforms(o: O): Record<string, unknown>;
@@ -373,37 +346,6 @@ interface DeformerInstance<O = Record<string, unknown>> {
373
346
  enabled?: boolean;
374
347
  }
375
348
 
376
- declare const cornerNames: readonly ["top-left", "top-right", "bottom-left", "bottom-right"];
377
- declare const curlOptionsSchema: z.ZodObject<{
378
- corner: z.ZodDefault<z.ZodEnum<["top-left", "top-right", "bottom-left", "bottom-right"]>>;
379
- /** How far the curl has traveled from the corner, as a fraction of the diagonal. */
380
- amount: z.ZodDefault<z.ZodNumber>;
381
- /** Cylinder radius — curl sharpness. */
382
- radius: z.ZodDefault<z.ZodNumber>;
383
- /** Skew of the fold line away from the corner diagonal, degrees. */
384
- skew: z.ZodDefault<z.ZodNumber>;
385
- }, "strip", z.ZodTypeAny, {
386
- corner: "top-left" | "top-right" | "bottom-left" | "bottom-right";
387
- amount: number;
388
- radius: number;
389
- skew: number;
390
- }, {
391
- corner?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | undefined;
392
- amount?: number | undefined;
393
- radius?: number | undefined;
394
- skew?: number | undefined;
395
- }>;
396
- type CurlOptions = z.infer<typeof curlOptionsSchema>;
397
- /**
398
- * Corner-anchored cylinder wrap — the peel/dog-ear deformer, and the
399
- * crown-jewel realism case: the mesh genuinely wraps the cylinder so content
400
- * bends with perfect continuity and the backside becomes visible.
401
- *
402
- * The fold line runs perpendicular to the corner diagonal and travels inward
403
- * with `amount`; everything cornerward of it wraps around the cylinder.
404
- */
405
- declare const curl: Deformer<CurlOptions>;
406
-
407
349
  /**
408
350
  * Pure grid math for the `sheet` layout — a block of stamps on a shared
409
351
  * backing. Shared by the layout's pose function, the backing silhouette
@@ -439,40 +381,6 @@ declare const sheetLayoutSchema: z.ZodObject<{
439
381
  backingMargin?: number | undefined;
440
382
  }>;
441
383
  type SheetLayoutOptions = z.infer<typeof sheetLayoutSchema>;
442
- /** Backing thickness + ε — papers float just above the backing sheet. */
443
- declare const SHEET_LIFT = 0.012;
444
- /**
445
- * Cell footprint = the paper's own sheet dims unless the user set an
446
- * explicit cellWidth/cellHeight — so `gutter` is literally the spacing
447
- * between stamps, whatever preset populates the grid.
448
- */
449
- declare function withSheetCellFromPaper(parsed: SheetLayoutOptions, rawOptions: Record<string, unknown> | undefined, paperDims: {
450
- width: number;
451
- height: number;
452
- } | undefined): SheetLayoutOptions;
453
- declare function sheetSlotXY(i: number, o: SheetLayoutOptions): {
454
- x: number;
455
- y: number;
456
- };
457
- /** Grid bounds + margin — the backing sheet's size. */
458
- declare function sheetBackingSize(o: SheetLayoutOptions): {
459
- width: number;
460
- height: number;
461
- };
462
- type Corner = CurlOptions['corner'];
463
- /**
464
- * The corner facing away from the sheet's center — what a thumb would find
465
- * (spec M6 §1.3). A tie (an odd grid's exact-center row/column, where the
466
- * cell straddles the midline) breaks outward-and-down: strict `<` on both
467
- * axes sends the center right and down, so a dead-center cell peels
468
- * bottom-right — the standalone peel default.
469
- */
470
- declare function outwardCorner(i: number, o: Pick<SheetLayoutOptions, 'rows' | 'columns'>): Corner;
471
- /**
472
- * Perforation auto-wiring on detach: edges that faced a neighboring slot tear
473
- * through; edges on the sheet's outer boundary keep their clean punches.
474
- */
475
- declare function tornEdgesOnDetach(i: number, o: Pick<SheetLayoutOptions, 'rows' | 'columns'>): Partial<Record<PaperEdge, 'torn' | 'intact'>>;
476
384
 
477
385
  /** Imperative field controls for the hidden a11y keyboard flow. */
478
386
  interface FieldA11yController {
@@ -483,26 +391,6 @@ interface FieldA11yController {
483
391
  slotState(slot: number): string;
484
392
  }
485
393
 
486
- /** Carry state of the hidden keyboard mirror: which paper is aloft, which zone is focused. */
487
- interface KeyboardCarry {
488
- slot: number;
489
- zoneIndex: number;
490
- }
491
- /** A keyboard step's decision: the next carry state and whether it consumed the key. */
492
- interface KeyboardStepResult {
493
- carry: KeyboardCarry | null;
494
- handled: boolean;
495
- }
496
- /**
497
- * The M6 §6 keyboard flow as a pure step (so it's testable without a DOM):
498
- * given the current carry state, the focused paper `slot`, and the pressed
499
- * `key`, it drives the field `controller` (pick → move between zones → place /
500
- * cancel) and returns the next carry state. All side effects go through
501
- * `controller`; the caller applies `carry` to its state and calls
502
- * `preventDefault()` when `handled` is true.
503
- */
504
- declare function fieldKeyboardStep(carry: KeyboardCarry | null, slot: number, key: string, controller: FieldA11yController): KeyboardStepResult;
505
-
506
394
  interface PaperFieldMeshProps {
507
395
  /** Per-paper slots; length sets the instance count. Slot presets override the shared one. */
508
396
  papers?: FieldPaperSlot[];
@@ -570,152 +458,379 @@ declare const PaperFieldMesh: react.ForwardRefExoticComponent<PaperFieldMeshProp
570
458
  /** `<PaperField />` owns its own Canvas; PaperFieldMesh drops into existing scenes. */
571
459
  declare const PaperField: react.ForwardRefExoticComponent<PaperFieldProps & react.RefAttributes<THREE.Group<THREE.Object3DEventMap>>>;
572
460
 
573
- interface UsePaperStatesResult {
574
- /** The config to render this frame animated when a machine is live. */
575
- config: PaperConfig;
576
- /** Current state name ('rest' when no machine). */
577
- state: string;
578
- /** Send triggers through this; null when states are absent or disabled. */
579
- machine: PaperStateMachine | null;
580
- }
461
+ declare function getPreset(name: string): PaperConfig;
462
+ /** Register a user preset (validated). Built-in names are reserved. */
463
+ declare function registerPreset(name: string, input: PaperConfigInput): void;
464
+ declare function unregisterPreset(name: string): void;
465
+ declare function isBuiltinPreset(name: string): boolean;
466
+ declare function listPresets(): string[];
581
467
  /**
582
- * Bind a state machine to a config with `states`. The machine survives
583
- * config edits via `rebase` (current state and live values are kept
584
- * flipping perforation to torn mid-pick must not snap the stamp to rest);
585
- * it is only rebuilt when states are toggled on/off.
468
+ * A collision-free preset name built from `base`: `base`, else `base 2`,
469
+ * `base 3`, The disambiguating suffix always grows from the SAME base a
470
+ * name derived from a synthetic base (e.g. an untitled import "imported")
471
+ * must not fall back to the original when it collides. `taken` reports whether
472
+ * a candidate is already used (built-in or user preset).
473
+ */
474
+ declare function uniquePresetName(base: string, taken: (name: string) => boolean): string;
475
+
476
+ /**
477
+ * Deep-merge preset config with prop overrides (overrides win; arrays and
478
+ * discriminated unions replace wholesale, plain objects merge).
586
479
  *
587
- * `animated` holds an IMMUTABLE structural snapshot for the React tree; it
588
- * updates only on structural boundaries (transition start/settle, state swap,
589
- * rebase), not per frame. Per-tick numeric values are read straight off
590
- * `machine.liveConfig` by the caller's frame loop — GSAP owns values, useFrame
591
- * owns uploads. `machine` is returned so the caller can poll it.
480
+ * Lives in its own module (not serialize.ts) because the schema needs it to
481
+ * validate state overrides schema serialize would be circular.
592
482
  */
593
- declare function usePaperStates(config: PaperConfig, enabled: boolean, instant: boolean, onAction?: (event: string, state: string) => void, onStateChange?: (state: string) => void): UsePaperStatesResult;
483
+ declare function mergeConfig<T>(base: T, override: unknown): T;
484
+ /**
485
+ * Like {@link mergeConfig}, but an explicit `undefined` DELETES its key instead
486
+ * of being ignored — the semantics a BASE-config write needs: structural
487
+ * setters can clear `behavior`/`deformers` or toggle a surface effect off.
488
+ * Discriminated unions (differing `type`) still replace wholesale.
489
+ */
490
+ declare function mergeWithDeletes<T>(base: T, patch: unknown): T;
491
+
492
+ /** Parse anything preset-shaped (object or JSON string) into a full, defaulted config. */
493
+ declare function parsePreset(input: PaperConfigInput | string): PaperConfig;
494
+ /** Serialize a config to `.paper` JSON. */
495
+ declare function serializePreset(config: PaperConfig): string;
594
496
 
497
+ /** The minimal PaperConfigInput that parses back to `config`. */
498
+ declare function diffConfig(config: PaperConfig): PaperConfigInput;
595
499
  /**
596
- * The backing sheet's generated content: per-slot ghost silhouettes a few
597
- * percent lighter than the backing tint. When a slot's paper detaches
598
- * (picked/placed), its silhouette lightens further and gains a faint
599
- * adhesive-sheen gradient — the visual proof of removal. Redrawn on state
600
- * change, never per-frame.
500
+ * A plain `<Paper />` snippet with only the non-default props the
501
+ * secondary export for people who read code.
601
502
  */
602
- interface SilhouetteRect {
603
- /** Canvas-UV rect (0..1, y down) of slot i on the backing sheet. */
604
- x: number;
605
- y: number;
606
- w: number;
607
- h: number;
608
- }
609
- /** Pure: where each slot's silhouette sits on the backing, in canvas UV. */
610
- declare function silhouetteRects(o: SheetLayoutOptions, count: number): SilhouetteRect[];
611
- /** Lighten a hex tint toward white by `amount` (0..1). */
612
- declare function lightenHex(hex: string, amount: number): string;
613
- interface BackingDrawSpec {
614
- options: SheetLayoutOptions;
615
- count: number;
616
- tint: string;
617
- /** Slot indices whose paper has been picked/placed away. */
618
- removed: ReadonlySet<number>;
619
- }
620
- /** Draw the backing content onto a canvas (called on state change only). */
621
- declare function drawBacking(canvas: HTMLCanvasElement, spec: BackingDrawSpec): void;
503
+ declare function buildJsxSnippet(config: PaperConfig): string;
504
+
505
+ /** One line an agent can verify against what it sees after `npm run dev`. */
506
+ declare function describeConfig(config: PaperConfig): string;
507
+ /** The self-contained integration brief — one paste into a coding agent. */
508
+ declare function buildAgentPayload(config: PaperConfig): string;
622
509
 
623
510
  /**
624
- * Lighting presets: each is a key light + ambient level + contact shadow +
625
- * optional gobo (a texture the key light projects window blinds, foliage).
626
- * Pure data here (testable in node); textures and R3F live in
627
- * PaperLighting.tsx. Serialized into presets as `scene.lighting`.
628
- *
629
- * A preset is the *starting point*, not the ceiling. `lightSchema` below is
630
- * the art-directable half: a handful of overrides that ride on top of a
631
- * named preset, so "nave, but the sun is lower and the room is dimmer" is a
632
- * thing you can say — and serialize — instead of a preset you have to fork.
511
+ * Field-mode export. Every preset a field references is INLINED as a const
512
+ * receivers don't have the sender's local preset library. Same fixed payload
513
+ * anatomy as the single-paper export, same version.
633
514
  */
634
- interface LightingPreset {
635
- id: LightingName;
636
- label: string;
637
- ambient: number;
638
- key: {
639
- color: string;
640
- intensity: number;
515
+ interface FieldExportPaper {
516
+ /** Display name of the preset this slot uses (const naming + description). */
517
+ presetName: string;
518
+ /** The RESOLVED preset config (user presets included — nothing to fetch). */
519
+ preset: PaperConfig;
520
+ content?: ContentConfig;
521
+ /** Per-slot state overrides (slot 3's hover deeper than slot 4's). */
522
+ states?: PaperStatesInput;
523
+ }
524
+ interface FieldExportZone {
525
+ id: string;
526
+ accept?: string[];
527
+ bounds: {
641
528
  position: [number, number, number];
529
+ size: [number, number];
642
530
  };
643
- contactShadowOpacity: number;
644
- /** Contact shadow blur — hard for noir, long and soft for golden hour. */
645
- contactShadowBlur: number;
646
- /** Renderer tone-mapping exposure while active. */
647
- exposure: number;
648
- /**
649
- * The tone curve the picture is printed through.
650
- *
651
- * `exposure` picks the stop; this picks the FILM, and on a subject that is
652
- * almost white the film is the louder of the two. Every preset ships
653
- * `neutral` — Khronos PBR Neutral — because it is the only one of the
654
- * three that keeps a clipping sheet BOTH bright and the colour it actually
655
- * is. See the note on `filmNames` for what the other two do to warm light.
656
- */
657
- film: FilmName;
658
- shadow: {
659
- mapSize: number;
660
- radius: number;
661
- };
662
- gobo?: {
663
- kind: 'blinds' | 'leaves';
664
- drift: number;
665
- angle: number;
666
- };
667
- /**
668
- * Distance haze. Depth in a deep space is staged almost entirely by fog —
669
- * it is what turns a row of banners into a receding colonnade instead of
670
- * a flat row of rectangles.
671
- */
672
- fog?: {
673
- color: string;
674
- near: number;
675
- far: number;
531
+ highlight?: 'none' | 'glow' | 'outline';
532
+ }
533
+ interface FieldExportInput {
534
+ layout: string;
535
+ layoutOptions?: Record<string, unknown>;
536
+ motion?: {
537
+ driver?: 'autoplay' | 'drag' | 'none';
538
+ speed?: number;
676
539
  };
677
- /**
678
- * The studio light: how strongly the room itself lights the paper.
679
- *
680
- * `<ambientLight>` adds brightness with no direction at all, which is the
681
- * single biggest reason a surface reads flat. This is the same brightness
682
- * with a SHAPE — an environment built from the sky below, so a sheet
683
- * turned toward the bright side of the room gets more light than one
684
- * turned away, and paper's sheen finally has something to reflect.
685
- */
686
- studio: number;
687
- /**
688
- * The room, as three colours. It grades zenith → horizon → floor, carries
689
- * a soft disc of the key's own colour where the key stands, and becomes
690
- * both the environment map and (in stage mode) the cyclorama, so the
691
- * light and the space it is in cannot disagree.
692
- */
693
- sky: {
694
- zenith: string;
695
- horizon: string;
696
- ground: string;
540
+ entrance?: {
541
+ type?: 'rise' | 'scatter' | 'none';
542
+ stagger?: number;
543
+ duration?: number;
697
544
  };
545
+ papers: FieldExportPaper[];
546
+ /** Drop zones — exported as `<DropZone>` children with an onPlace stub. */
547
+ zones?: FieldExportZone[];
548
+ }
549
+ /** The layout/motion/entrance props of the export, defaults stripped. */
550
+ declare function diffFieldProps(input: FieldExportInput): Record<string, unknown>;
551
+ /** The one-line visual an agent verifies after `npm run dev`. */
552
+ declare function describeFieldConfig(input: FieldExportInput): string;
553
+ /** Component source shared by the JSX snippet and the agent payload. */
554
+ declare function buildFieldComponentSource(input: FieldExportInput): string;
555
+ /** The self-contained field integration brief — one paste into a coding agent. */
556
+ declare function buildFieldAgentPayload(input: FieldExportInput): string;
557
+
558
+ /** Community deformers register here; built-ins are pre-registered. */
559
+ declare function registerDeformer(deformer: Deformer<AnyOptions>): void;
560
+ declare function getDeformer(id: string): Deformer<AnyOptions>;
561
+ declare function listDeformers(): string[];
562
+
563
+ /**
564
+ * The shared fake-aerodynamics core: velocity-linked lift +
565
+ * curated noise, per v0.2's "reads more real than a true sim" doctrine.
566
+ * Pure math, no three.js, no allocation in the per-frame paths — `carry`
567
+ * and `flight` both source their motion here.
568
+ */
569
+ interface AeroPose {
570
+ position: [number, number, number];
571
+ rotation: [number, number, number];
572
+ }
573
+
574
+ /** A draggable 3D grab point; drags write back to behavior params. */
575
+ interface HandleSpec<O = AnyOptions> {
576
+ id: string;
577
+ /** UV-space (0..1) anchor of the grab point on the flat sheet. */
578
+ anchor(o: O, sheet: SheetDims): [number, number];
579
+ /** Map a pointer position in sheet-local XY to option updates. */
580
+ drag(local: {
581
+ x: number;
582
+ y: number;
583
+ }, o: O, sheet: SheetDims): Partial<O>;
698
584
  }
699
- declare const lightingPresets: Record<LightingName, LightingPreset>;
700
- declare function getLightingPreset(name: LightingName): LightingPreset;
701
585
  /**
702
- * Overrides on top of a named preset the Blender-panel half of lighting.
703
- *
704
- * Every field is optional ON PURPOSE. An unset field means "whatever the
705
- * preset says", so a shared link carries the two sliders you actually moved
706
- * rather than a frozen copy of a rig you never touched, and re-basing onto
707
- * another preset keeps your intent instead of your numbers.
586
+ * A behavior is a named, curated bundle: a deformer stack + a parameter
587
+ * mapping + optional idle loop + handles for direct manipulation. Designers
588
+ * see 3–5 human-named params ('tightness', not 'cylinderRadius'); the stack
589
+ * underneath is an Advanced disclosure. New behaviors are the community
590
+ * on-ramp: ~50 lines over existing deformers.
708
591
  */
709
- declare const lightSchema: z.ZodObject<{
710
- /** Tone-mapping exposure — the stop the whole picture is printed at. */
711
- exposure: z.ZodOptional<z.ZodNumber>;
592
+ interface Behavior<O = AnyOptions> {
593
+ id: string;
594
+ label: string;
595
+ defaults: O;
596
+ optionsSchema: z.ZodType<O, z.ZodTypeDef, unknown>;
597
+ /** Expand human params to the underlying deformer stack. */
598
+ stack(o: O, sheet: SheetDims): DeformerInstance[];
599
+ /** Transient, time-varying option overrides (idle motion). Never persisted. */
600
+ loop?(o: O, t: number): Partial<O>;
712
601
  /**
713
- * The tone curve the film, where `exposure` is the stop.
714
- *
715
- * `filmic` is ACES, which is what every preset used to be pinned to and is
716
- * kept so a scene tuned against it can say so. On near-white paper it is
717
- * the wrong film: it desaturates and drags bright neutrals toward
718
- * yellow-green, which is the sepia cast a lit sheet used to pick up.
602
+ * Whole-sheet motion written into `pose` each frame (allocation-free),
603
+ * composed after any idle preset's transform. Must be a pure function of
604
+ * (options, t) the field applies it per instance with a time offset, so
605
+ * it has to be deterministic (flight's travel-across-the-scene).
606
+ */
607
+ transform?(o: O, t: number, pose: AeroPose): void;
608
+ handles?: HandleSpec<O>[];
609
+ /**
610
+ * The two or three options that ARE this behavior — the ones someone
611
+ * reaches for first, in the order they'd reach for them.
612
+ *
613
+ * The schema still generates a control for every option; this only says
614
+ * which ones get the big controls and which fold away behind "More". A
615
+ * behavior that nominates nothing shows all of its options flat, because
616
+ * the library must never hide a param it was not told to hide — silence
617
+ * from a community behavior is not permission to guess.
618
+ *
619
+ * Every name here has to be a field of {@link optionsSchema}; the built-in
620
+ * behaviors are checked for that, and for staying within three.
621
+ */
622
+ signature?: (keyof O & string)[];
623
+ /** The option the transport scrubber drives. */
624
+ progressParam: keyof O & string;
625
+ /** Seconds for a full 0→1 play, and how play repeats. */
626
+ duration: number;
627
+ loopMode: 'yoyo' | 'restart';
628
+ }
629
+
630
+ /** Community behaviors register here; built-ins are pre-registered. */
631
+ declare function registerBehavior(behavior: Behavior<AnyOptions>): void;
632
+ declare function getBehavior(id: string): Behavior<AnyOptions>;
633
+ declare function listBehaviors(): string[];
634
+
635
+ /**
636
+ * A layout is a pure `pose(i, n, options, phase)` function — no state, no
637
+ * three.js. `phase` is the motion driver's continuous offset in turns
638
+ * (0..1 = one full cycle); cyclic layouts use it, static ones ignore it.
639
+ * Community layouts are ~30 lines.
640
+ *
641
+ * Every built-in names a place paper actually sits — a fanned swatch deck, a
642
+ * slipped stack, a heap on a desk — because arrangement alone is what makes a
643
+ * field read as a photo carousel instead of as paper. The other half of that
644
+ * is `bias`: paper in the world does not all bend alike.
645
+ */
646
+ interface PaperPose {
647
+ position: [number, number, number];
648
+ rotation: [number, number, number];
649
+ scale: number;
650
+ /**
651
+ * How strongly this sheet takes the field's deformation: 1 = exactly as the
652
+ * preset configures it, 0 = flat. Lets one instanced draw call curl the top
653
+ * of a pile while the sheets pressed underneath stay flat. Omitted = 1.
654
+ */
655
+ bias?: number;
656
+ }
657
+ interface Layout<O = Record<string, unknown>> {
658
+ id: string;
659
+ label: string;
660
+ defaults: O;
661
+ optionsSchema: z.ZodType<O, z.ZodTypeDef, unknown>;
662
+ /**
663
+ * `sheet` is the field's paper size. Layouts that arrange by CONTACT —
664
+ * edges meeting, sheets resting on each other — cannot work without it,
665
+ * and a layout that ignores it may simply omit the parameter.
666
+ */
667
+ pose(i: number, n: number, o: O, phase: number, sheet: SheetDims): PaperPose;
668
+ /**
669
+ * Where along a walk this layout put each paper, as normalized arc length,
670
+ * in the layout's own index order.
671
+ *
672
+ * Only layouts that arrange along a PATH can answer, which is why it is
673
+ * optional. Stage mode uses it to let a viewer step from one paper to the
674
+ * next: the stops have to be where the paper actually is, and the only
675
+ * thing that knows that is the function that placed it.
676
+ */
677
+ walkStops?(n: number, o: O): number[];
678
+ }
679
+ declare function registerLayout(layout: Layout<AnyOptions>): void;
680
+ declare function getLayout(id: string): Layout<AnyOptions>;
681
+ declare function listLayouts(): string[];
682
+
683
+ /**
684
+ * A stock is a named bundle of material + geometry defaults — choosing paper
685
+ * at a print shop. Individual controls always override; schema-wise these are
686
+ * just defaults.
687
+ */
688
+ interface Stock {
689
+ id: StockName;
690
+ label: string;
691
+ /** Base tint, also used as the canvas background behind content. */
692
+ color: string;
693
+ roughness: number;
694
+ /** 0 = opaque. Vellum is translucent. */
695
+ opacity: number;
696
+ /**
697
+ * How much light passes THROUGH the sheet when something is behind it,
698
+ * 0..1. Distinct from `opacity`: newsprint is fully opaque to look at and
699
+ * still glows on a lightbox. This is what makes a backlit banner read.
700
+ */
701
+ translucency: number;
702
+ /** Ink multiply tint for content drawn on this stock (thermal prints grey-black). */
703
+ inkColor: string;
704
+ /** Thermal-printer banding intensity baked into the grain effect. */
705
+ banding: number;
706
+ /** Surface effects this stock ships with; explicit surface config overrides per key. */
707
+ defaultSurface: {
708
+ grain?: number;
709
+ aging?: number;
710
+ };
711
+ /** Reversed front-content ghost on the backside (thin stocks let ink show). */
712
+ showThrough: number;
713
+ /** Glossy near-white glue underside (stickers) — forces showThrough 0. */
714
+ adhesive: boolean;
715
+ }
716
+ declare const stocks: Record<StockName, Stock>;
717
+ declare function getStock(name: StockName): Stock;
718
+
719
+ declare const idleNames: readonly ["float", "tumble", "dangle", "taped", "breeze"];
720
+ type IdleName = (typeof idleNames)[number];
721
+ interface IdlePose {
722
+ /** Offsets added to the paper's base transform each frame. */
723
+ position: [number, number, number];
724
+ rotation: [number, number, number];
725
+ }
726
+ /**
727
+ * Curated fake physics: hand-tuned motion presets, no simulation. Cheap
728
+ * enough for both hero and field modes, and — for things like the
729
+ * falling-leaf tumble — they read *more* real than a true sim.
730
+ */
731
+ interface IdlePreset {
732
+ id: IdleName;
733
+ label: string;
734
+ /** Whole-sheet motion, written into `pose` (allocation-free). */
735
+ transform?(t: number, pose: IdlePose): void;
736
+ /** Extra deformers appended after the behavior's stack. */
737
+ stack?(): DeformerInstance[];
738
+ }
739
+
740
+ /**
741
+ * Lighting presets: each is a key light + ambient level + contact shadow +
742
+ * optional gobo (a texture the key light projects — window blinds, foliage).
743
+ * Pure data here (testable in node); textures and R3F live in
744
+ * PaperLighting.tsx. Serialized into presets as `scene.lighting`.
745
+ *
746
+ * A preset is the *starting point*, not the ceiling. `lightSchema` below is
747
+ * the art-directable half: a handful of overrides that ride on top of a
748
+ * named preset, so "nave, but the sun is lower and the room is dimmer" is a
749
+ * thing you can say — and serialize — instead of a preset you have to fork.
750
+ */
751
+ interface LightingPreset {
752
+ id: LightingName;
753
+ label: string;
754
+ ambient: number;
755
+ key: {
756
+ color: string;
757
+ intensity: number;
758
+ position: [number, number, number];
759
+ };
760
+ contactShadowOpacity: number;
761
+ /** Contact shadow blur — hard for noir, long and soft for golden hour. */
762
+ contactShadowBlur: number;
763
+ /** Renderer tone-mapping exposure while active. */
764
+ exposure: number;
765
+ /**
766
+ * The tone curve the picture is printed through.
767
+ *
768
+ * `exposure` picks the stop; this picks the FILM, and on a subject that is
769
+ * almost white the film is the louder of the two. Every preset ships
770
+ * `neutral` — Khronos PBR Neutral — because it is the only one of the
771
+ * three that keeps a clipping sheet BOTH bright and the colour it actually
772
+ * is. See the note on `filmNames` for what the other two do to warm light.
773
+ */
774
+ film: FilmName;
775
+ shadow: {
776
+ mapSize: number;
777
+ radius: number;
778
+ };
779
+ gobo?: {
780
+ kind: 'blinds' | 'leaves';
781
+ drift: number;
782
+ angle: number;
783
+ };
784
+ /**
785
+ * Distance haze. Depth in a deep space is staged almost entirely by fog —
786
+ * it is what turns a row of banners into a receding colonnade instead of
787
+ * a flat row of rectangles.
788
+ */
789
+ fog?: {
790
+ color: string;
791
+ near: number;
792
+ far: number;
793
+ };
794
+ /**
795
+ * The studio light: how strongly the room itself lights the paper.
796
+ *
797
+ * `<ambientLight>` adds brightness with no direction at all, which is the
798
+ * single biggest reason a surface reads flat. This is the same brightness
799
+ * with a SHAPE — an environment built from the sky below, so a sheet
800
+ * turned toward the bright side of the room gets more light than one
801
+ * turned away, and paper's sheen finally has something to reflect.
802
+ */
803
+ studio: number;
804
+ /**
805
+ * The room, as three colours. It grades zenith → horizon → floor, carries
806
+ * a soft disc of the key's own colour where the key stands, and becomes
807
+ * both the environment map and (in stage mode) the cyclorama, so the
808
+ * light and the space it is in cannot disagree.
809
+ */
810
+ sky: {
811
+ zenith: string;
812
+ horizon: string;
813
+ ground: string;
814
+ };
815
+ }
816
+ /**
817
+ * Overrides on top of a named preset — the Blender-panel half of lighting.
818
+ *
819
+ * Every field is optional ON PURPOSE. An unset field means "whatever the
820
+ * preset says", so a shared link carries the two sliders you actually moved
821
+ * rather than a frozen copy of a rig you never touched, and re-basing onto
822
+ * another preset keeps your intent instead of your numbers.
823
+ */
824
+ declare const lightSchema: z.ZodObject<{
825
+ /** Tone-mapping exposure — the stop the whole picture is printed at. */
826
+ exposure: z.ZodOptional<z.ZodNumber>;
827
+ /**
828
+ * The tone curve — the film, where `exposure` is the stop.
829
+ *
830
+ * `filmic` is ACES, which is what every preset used to be pinned to and is
831
+ * kept so a scene tuned against it can say so. On near-white paper it is
832
+ * the wrong film: it desaturates and drags bright neutrals toward
833
+ * yellow-green, which is the sepia cast a lit sheet used to pick up.
719
834
  */
720
835
  film: z.ZodOptional<z.ZodEnum<["agx", "neutral", "filmic"]>>;
721
836
  /** Key light strength. */
@@ -777,8 +892,6 @@ interface LightAngles {
777
892
  * they have to agree.
778
893
  */
779
894
  declare function lightAngles(position: readonly [number, number, number]): LightAngles;
780
- /** The inverse: put a light back where those angles say it stands. */
781
- declare function lightPosition(angles: LightAngles): [number, number, number];
782
895
  /**
783
896
  * A preset with the overrides applied — the rig everything else reads.
784
897
  *
@@ -791,11 +904,6 @@ declare function lightPosition(angles: LightAngles): [number, number, number];
791
904
  */
792
905
  declare function resolveLighting(base: LightingName | LightingPreset, overrides?: LightOverrides): LightingPreset;
793
906
 
794
- /**
795
- * Procedural gobo textures — no binary assets in the repo. White passes
796
- * light, dark blocks it (SpotLight.map multiplies the beam).
797
- */
798
- declare function makeGoboTexture(kind: 'blinds' | 'leaves'): THREE.CanvasTexture;
799
907
  interface PaperLightingProps {
800
908
  preset?: LightingName;
801
909
  /**
@@ -845,33 +953,56 @@ declare function LightRig({ rig, children }: {
845
953
  children: ReactNode;
846
954
  }): react.JSX.Element;
847
955
 
848
- /** Community deformers register here; built-ins are pre-registered. */
849
- declare function registerDeformer(deformer: Deformer<AnyOptions>): void;
850
- declare function getDeformer(id: string): Deformer<AnyOptions>;
851
- declare function listDeformers(): string[];
956
+ interface UsePaperStatesResult {
957
+ /** The config to render this frame — animated when a machine is live. */
958
+ config: PaperConfig;
959
+ /** Current state name ('rest' when no machine). */
960
+ state: string;
961
+ /** Send triggers through this; null when states are absent or disabled. */
962
+ machine: PaperStateMachine | null;
963
+ }
852
964
  /**
853
- * Resolve a raw `deformers` stack the Advanced fork of a behavior — into
854
- * instances safe to render.
855
- *
856
- * The escape hatch used to pass its options straight through, so a preset
857
- * naming an option that doesn't exist (`frequency` where wave wants
858
- * `wavelength`) reached the GLSL builder as `undefined` and died there with
859
- * a message about `.length`, or reached the CPU path and quietly produced
860
- * NaN vertices. Parsing through each deformer's own schema turns that into
861
- * the validation error it always was, and fills in defaults for whatever a
862
- * hand-written preset left out.
965
+ * Bind a state machine to a config with `states`. The machine survives
966
+ * config edits via `rebase` (current state and live values are kept —
967
+ * flipping perforation to torn mid-pick must not snap the stamp to rest);
968
+ * it is only rebuilt when states are toggled on/off.
863
969
  *
864
- * Disabled entries keep their slot: the GLSL uniform namespace is indexed by
865
- * position, so dropping one here would rename every uniform after it.
970
+ * `animated` holds an IMMUTABLE structural snapshot for the React tree; it
971
+ * updates only on structural boundaries (transition start/settle, state swap,
972
+ * rebase), not per frame. Per-tick numeric values are read straight off
973
+ * `machine.liveConfig` by the caller's frame loop — GSAP owns values, useFrame
974
+ * owns uploads. `machine` is returned so the caller can poll it.
866
975
  */
867
- declare function resolveDeformerStack(raw: {
868
- type: string;
869
- options?: Record<string, unknown>;
870
- enabled?: boolean;
871
- }[]): DeformerInstance[];
976
+ declare function usePaperStates(config: PaperConfig, enabled: boolean, instant: boolean, onAction?: (event: string, state: string) => void, onStateChange?: (state: string) => void): UsePaperStatesResult;
872
977
 
873
978
  /**
874
- * How finely a sheet has to be subdivided for a deformer to look like the
979
+ * Accessibility layer: reduced-motion handling, a hidden DOM mirror so the
980
+ * paper's content exists for screen readers and find-in-page, and a no-WebGL
981
+ * DOM fallback.
982
+ */
983
+ declare function usePrefersReducedMotion(override?: boolean): boolean;
984
+ declare function supportsWebGL(): boolean;
985
+
986
+ /** highp float + sin/cos implementation differences across GPUs. */
987
+ declare const PARITY_EPSILON = 0.0005;
988
+ interface ParityCase {
989
+ name: string;
990
+ stack: DeformerInstance[];
991
+ sheet: SheetDims;
992
+ t: number;
993
+ }
994
+ interface ParityResult {
995
+ name: string;
996
+ maxError: number;
997
+ pass: boolean;
998
+ }
999
+ /** One case per deformer feature worth guarding, both easy and adversarial. */
1000
+ declare const parityCases: ParityCase[];
1001
+ /** Run every parity case. Requires a WebGL2 context with float render targets. */
1002
+ declare function runParityHarness(canvas?: HTMLCanvasElement): ParityResult[];
1003
+
1004
+ /**
1005
+ * How finely a sheet has to be subdivided for a deformer to look like the
875
1006
  * surface it is approximating instead of like the polygons it is made of.
876
1007
  *
877
1008
  * A deformed mesh is a piecewise-linear stand-in for a curved surface, and the
@@ -957,16 +1088,6 @@ declare const AUTO_CEILING = 192;
957
1088
  declare const FLAT_SEGMENTS = 8;
958
1089
  /** Smallest ladder step at or above `n`, clamped to the ceiling. */
959
1090
  declare function quantizeSegments(n: number): number;
960
- /**
961
- * A resolved grid: segments along the sheet's own X and Y.
962
- *
963
- * Two numbers rather than one because a sheet is not subdivided by a single
964
- * density. A banner 1.5 wide and 8.5 tall, draped in folds that run across
965
- * its width, needs the folds resolved ACROSS and needs almost nothing down
966
- * the drop — and a single number, however it is distributed, answers one of
967
- * those questions by getting the other one wrong.
968
- */
969
- type SegmentPair = [x: number, y: number];
970
1091
  /**
971
1092
  * Extent of the sheet along a direction in its own plane, degrees. For a
972
1093
  * rectangle centered on the origin this is exactly `|w·cos| + |h·sin|` — the
@@ -991,1598 +1112,22 @@ declare function segmentsForArc(span: number, radius: number, tol?: number): num
991
1112
  declare function segmentsForSine(span: number, amplitude: number, wavelength: number, tol?: number): number;
992
1113
 
993
1114
  /**
994
- * Run an ordered deformer stack over a sheet geometry: each vertex starts
995
- * from its flat base position and flows through every enabled deformer in
996
- * order. Writes positions in place and recomputes normals.
997
- */
998
- declare function applyDeformerStack(geometry: THREE.BufferGeometry, basePositions: Float32Array, stack: DeformerInstance[], ctx: DeformerContext): void;
999
- /** A single point through the stack — used for handle anchors and tests. */
1000
- declare function displacePoint(point: THREE.Vector3, uvX: number, uvY: number, stack: DeformerInstance[], ctx: DeformerContext): THREE.Vector3;
1001
- /**
1002
- * The densest grid any deformer in the stack REQUIRES to work at all, per
1003
- * axis — the componentwise max of every entry's floor projected onto the
1004
- * sheet's own X and Y by the direction that entry curves in.
1005
- */
1006
- declare function stackMinSegments(stack: DeformerInstance[], sheet: SheetDims): SegmentPair;
1007
- /**
1008
- * The densest grid any deformer in the stack WANTS, given the options it is
1009
- * actually carrying — what `segments: 'auto'` resolves to, per axis.
1010
- *
1011
- * Disabled instances are skipped, exactly as `applyDeformerStack` skips them:
1012
- * a deformer that is not displacing anything has no opinion about the grid.
1013
- * (`stackMinSegments` does not skip them, and that difference is deliberate —
1014
- * a floor is about what the stack could do, a target about what it is doing.)
1015
- *
1016
- * A deformer with no `autoSegments` falls back to its floor, which is the
1017
- * right answer for one whose cost does not move with its options.
1018
- */
1019
- declare function stackAutoSegments(stack: DeformerInstance[], sheet: SheetDims): SegmentPair;
1020
-
1021
- declare const rollOptionsSchema: z.ZodObject<{
1022
- /** Direction of rolling in the sheet plane, degrees. 0 = +x, 90 = +y. */
1023
- angle: z.ZodDefault<z.ZodNumber>;
1024
- /** Signed distance (along the roll direction, from sheet center) where the roll begins. */
1025
- boundary: z.ZodDefault<z.ZodNumber>;
1026
- /** Cylinder radius — sharpness of the roll. */
1027
- radius: z.ZodDefault<z.ZodNumber>;
1028
- /** Radius growth per radian so multi-turn rolls spiral instead of z-fighting. */
1029
- spiral: z.ZodDefault<z.ZodNumber>;
1030
- }, "strip", z.ZodTypeAny, {
1031
- radius: number;
1032
- angle: number;
1033
- boundary: number;
1034
- spiral: number;
1035
- }, {
1036
- radius?: number | undefined;
1037
- angle?: number | undefined;
1038
- boundary?: number | undefined;
1039
- spiral?: number | undefined;
1040
- }>;
1041
- type RollOptions = z.infer<typeof rollOptionsSchema>;
1042
- /**
1043
- * Wrap the sheet around a virtual cylinder lying across the roll direction.
1044
- * Everything past `boundary` wraps; the wrap is C¹-continuous at the
1045
- * boundary and preserves arc length (content never stretches).
1046
- *
1047
- * Points arriving with z ≠ 0 (from earlier deformers in the stack) ride
1048
- * along the rolled surface's normal, so stacks compose sanely.
1049
- */
1050
- declare const roll: Deformer<RollOptions>;
1051
-
1052
- declare const bendOptionsSchema: z.ZodObject<{
1053
- /** 1/radius in world units; sign flips the arc direction. 0 = flat. */
1054
- curvature: z.ZodDefault<z.ZodNumber>;
1055
- /** Bend axis direction in the sheet plane, degrees. 0 bends across x. */
1056
- angle: z.ZodDefault<z.ZodNumber>;
1057
- }, "strip", z.ZodTypeAny, {
1058
- angle: number;
1059
- curvature: number;
1060
- }, {
1061
- angle?: number | undefined;
1062
- curvature?: number | undefined;
1063
- }>;
1064
- type BendOptions = z.infer<typeof bendOptionsSchema>;
1065
- /**
1066
- * Gentle global arc around a cylinder centered on the sheet — a standing
1067
- * paper's lean. Arc-length preserving, like roll, but symmetric about the
1068
- * center instead of one-sided.
1069
- *
1070
- * Written in its cancellation-free form throughout: `r(1 − cos θ)` is
1071
- * `2r·sin²(θ/2)`, and the in-plane shift goes through `sinMinusX`. Same arc,
1072
- * same numbers to sixteen places — it is only the float32 half that could
1073
- * tell the difference, and that is exactly the half the parity gate checks.
1074
- */
1075
- declare const bend: Deformer<BendOptions>;
1076
-
1077
- declare const foldOptionsSchema: z.ZodObject<{
1078
- /** Direction of the fold travel in the sheet plane, degrees (the crease line runs perpendicular). */
1079
- angle: z.ZodDefault<z.ZodNumber>;
1080
- /** Signed distance of the crease line from the sheet center, along the travel direction. */
1081
- offset: z.ZodDefault<z.ZodNumber>;
1082
- /** How far the flap folds over, degrees. 180 = flat against the sheet. */
1083
- foldAngle: z.ZodDefault<z.ZodNumber>;
1084
- /** Width of the soft hinge — paper never creases to a mathematical edge. */
1085
- radius: z.ZodDefault<z.ZodNumber>;
1086
- }, "strip", z.ZodTypeAny, {
1087
- radius: number;
1088
- angle: number;
1089
- offset: number;
1090
- foldAngle: number;
1091
- }, {
1092
- radius?: number | undefined;
1093
- angle?: number | undefined;
1094
- offset?: number | undefined;
1095
- foldAngle?: number | undefined;
1096
- }>;
1097
- type FoldOptions = z.infer<typeof foldOptionsSchema>;
1098
- /**
1099
- * Angular crease across a line: within the hinge width the sheet wraps a
1100
- * small cylinder (a roll), beyond it the flap continues rigid at the full
1101
- * fold angle. n folds = n instances stacked (half-fold, letter-fold,
1102
- * accordion). Arc-length preserving like every Paperlab deformer.
1103
- */
1104
- declare const fold: Deformer<FoldOptions>;
1105
-
1106
- declare const waveOptionsSchema: z.ZodObject<{
1107
- amplitude: z.ZodDefault<z.ZodNumber>;
1108
- wavelength: z.ZodDefault<z.ZodNumber>;
1109
- /** Travel speed; 0 freezes the ripple. */
1110
- speed: z.ZodDefault<z.ZodNumber>;
1111
- /** Travel direction in the sheet plane, degrees. */
1112
- angle: z.ZodDefault<z.ZodNumber>;
1113
- /** Zero the displacement at one edge (a taped/pinned edge doesn't ripple). */
1114
- pinnedEdge: z.ZodDefault<z.ZodEnum<["none", "top", "bottom", "left", "right"]>>;
1115
- }, "strip", z.ZodTypeAny, {
1116
- angle: number;
1117
- amplitude: number;
1118
- wavelength: number;
1119
- speed: number;
1120
- pinnedEdge: "left" | "right" | "top" | "none" | "bottom";
1121
- }, {
1122
- angle?: number | undefined;
1123
- amplitude?: number | undefined;
1124
- wavelength?: number | undefined;
1125
- speed?: number | undefined;
1126
- pinnedEdge?: "left" | "right" | "top" | "none" | "bottom" | undefined;
1127
- }>;
1128
- type WaveOptions = z.infer<typeof waveOptionsSchema>;
1129
- /**
1130
- * Traveling sine displacement with a quieter second harmonic — idle flutter
1131
- * and wind ripple. The only time-driven deformer so far: stacks containing
1132
- * it re-deform every frame.
1133
- */
1134
- declare const wave: Deformer<WaveOptions>;
1135
-
1136
- declare const drapeOptionsSchema: z.ZodObject<{
1137
- /** Fold depth at the free edge, world units. */
1138
- amplitude: z.ZodDefault<z.ZodNumber>;
1139
- /** How many folds run down the drop. */
1140
- folds: z.ZodDefault<z.ZodNumber>;
1141
- /**
1142
- * How fast folds deepen away from the pinned edge. 1 is linear; higher
1143
- * holds the top flat and gathers all the movement at the free end, which
1144
- * is what a sheet hung from a rod actually does.
1145
- */
1146
- falloff: z.ZodDefault<z.ZodNumber>;
1147
- /** How much a second, non-harmonic fold breaks the regularity. */
1148
- irregular: z.ZodDefault<z.ZodNumber>;
1149
- /** How much the sheet narrows as its folds deepen. */
1150
- gather: z.ZodDefault<z.ZodNumber>;
1151
- pinnedEdge: z.ZodDefault<z.ZodEnum<["top", "bottom"]>>;
1152
- }, "strip", z.ZodTypeAny, {
1153
- amplitude: number;
1154
- pinnedEdge: "top" | "bottom";
1155
- folds: number;
1156
- falloff: number;
1157
- irregular: number;
1158
- gather: number;
1159
- }, {
1160
- amplitude?: number | undefined;
1161
- pinnedEdge?: "top" | "bottom" | undefined;
1162
- folds?: number | undefined;
1163
- falloff?: number | undefined;
1164
- irregular?: number | undefined;
1165
- gather?: number | undefined;
1166
- }>;
1167
- type DrapeOptions = z.infer<typeof drapeOptionsSchema>;
1168
- /**
1169
- * Hung paper: vertical folds running the length of the drop, shallow at the
1170
- * fixed edge and deepening toward the free one.
1171
- *
1172
- * `wave` can put ripples on a sheet, but a traveling sine is a flag, not a
1173
- * curtain — it kinks the sheet ACROSS its drop and it is uniform end to end.
1174
- * Cloth hung from an edge does the opposite: the folds run WITH the drop and
1175
- * they grow as they get further from whatever is holding the sheet up. Two
1176
- * details do most of the work:
1177
- *
1178
- * - the folds are not harmonic. A pure sine reads as corrugated metal, so a
1179
- * second wave at an incommensurate frequency breaks the repeat.
1180
- * - gathered paper is narrower than flat paper. Pulling the surface toward
1181
- * its centerline in proportion to fold depth is what stops the drape from
1182
- * looking like a texture painted on a rectangle.
1183
- */
1184
- declare const drape: Deformer<DrapeOptions>;
1185
-
1186
- declare const crumpleOptionsSchema: z.ZodObject<{
1187
- /** How crushed, 0..1. Peak-to-peak height, and it drives the pull too. */
1188
- amount: z.ZodDefault<z.ZodNumber>;
1189
- /** Facets per world unit. Higher is finer, and needs more segments to resolve. */
1190
- scale: z.ZodDefault<z.ZodNumber>;
1191
- /**
1192
- * How much the sheet draws in on itself. Crumpled paper occupies a smaller
1193
- * footprint than flat paper; without this it reads as an embossed sheet
1194
- * rather than a crushed one.
1195
- */
1196
- pull: z.ZodDefault<z.ZodNumber>;
1197
- /** A different crush of the same paper. */
1198
- seed: z.ZodDefault<z.ZodNumber>;
1199
- }, "strip", z.ZodTypeAny, {
1200
- amount: number;
1201
- seed: number;
1202
- scale: number;
1203
- pull: number;
1204
- }, {
1205
- amount?: number | undefined;
1206
- seed?: number | undefined;
1207
- scale?: number | undefined;
1208
- pull?: number | undefined;
1209
- }>;
1210
- type CrumpleOptions = z.infer<typeof crumpleOptionsSchema>;
1211
- /**
1212
- * Paper that has been handled.
1213
- *
1214
- * Six deformers and not one of them crushed a sheet — `wave` and `fold` were
1215
- * the nearest and neither reads as crumpled. This is the missing primitive.
1216
- *
1217
- * The field is a jittered grid of cells, each pushed up or down, with the
1218
- * height going to zero exactly on the boundary between them: `F2 − F1`, the
1219
- * gap between the two nearest cell points, signed per cell. That vanishes on
1220
- * every boundary, so the sheet stays continuous, and its gradient flips
1221
- * across one — which is a crease. The result is an irregular polygonal
1222
- * network of facets alternating toward and away from you, which is what a
1223
- * sheet crushed in a fist actually is.
1224
- *
1225
- * **The normals matter more than the displacement here**, and getting there
1226
- * took three tries worth recording. Three summed triangle waves: periodic,
1227
- * an egg-crate. Plain distance-to-nearest (`F1`): irregular but smooth cone
1228
- * tips, so it read as hammered metal. Only creases with a sign change across
1229
- * them shade like paper.
1230
- *
1231
- * Both normal paths agree with that: the hero path averages vertex normals
1232
- * over a dense grid, the field path probes two tangents a hundredth of a
1233
- * sheet apart. Both need facets several segments wide.
1234
- *
1235
- * Cost, measured rather than assumed (`pnpm perf:field --soft`): a field of
1236
- * these runs about four times longer per frame at ×20, and six at ×60, than
1237
- * the same field of an undeformed preset. That ratio used to read 45%, and it
1238
- * grew without this deformer getting one instruction slower — `'auto'` now
1239
- * sizes the grid to the work, so the undeformed sheet it is measured against
1240
- * went from 72 segments a side to 8 and got very cheap, while crumple keeps
1241
- * the density its creases need. Which makes the point sharper than before:
1242
- * almost none of this cost is geometry. It is the nine cell lookups per
1243
- * probe, three probes deep for the normal.
1244
- */
1245
- declare const crumple: Deformer<CrumpleOptions>;
1246
-
1247
- /**
1248
- * The shared fake-aerodynamics core (spec M6 §4): velocity-linked lift +
1249
- * curated noise, per v0.2's "reads more real than a true sim" doctrine.
1250
- * Pure math, no three.js, no allocation in the per-frame paths — `carry`
1251
- * and `flight` both source their motion here.
1252
- */
1253
- interface AeroPose {
1254
- position: [number, number, number];
1255
- rotation: [number, number, number];
1256
- }
1257
- /**
1258
- * Critically-damped spring toward a target — the carry pin's cursor
1259
- * follow. Mutates `state` in place; returns nothing.
1260
- */
1261
- interface DampedValue {
1262
- value: number;
1263
- velocity: number;
1264
- }
1265
- declare function dampTo(state: DampedValue, target: number, smoothing: number, dt: number): void;
1266
- /** Coherent gust factor in [1-g, 1+g] — curated, seeded, cheap. */
1267
- declare function gust(t: number, seed: number, gustiness: number): number;
1268
- interface FlightParams {
1269
- wind: [number, number, number];
1270
- gustiness: number;
1271
- tumble: number;
1272
- path: 'drift' | 'loop';
1273
- /** Exit the scene → re-enter the opposite side (drift only). */
1274
- respawn: boolean;
1275
- /** Half-extent of the drift travel before respawn wraps it. */
1276
- range: number;
1277
- }
1278
- /**
1279
- * Free paper on the wind: the falling-leaf tumble core + a directional wind
1280
- * vector + lift, so paper travels ACROSS, not just down. A pure function of
1281
- * time and phase — instancing-safe, deterministic, loopable.
1282
- */
1283
- declare function flightPose(t: number, o: FlightParams, phase: number, pose: AeroPose): void;
1284
- /**
1285
- * Carry flutter drive: how much a held paper ripples for a given drag speed
1286
- * (world units/s). Saturates — a violent drag doesn't tear the illusion.
1287
- */
1288
- declare function carryDrive(speed: number): number;
1289
-
1290
- /** A draggable 3D grab point; drags write back to behavior params. */
1291
- interface HandleSpec<O = AnyOptions> {
1292
- id: string;
1293
- /** UV-space (0..1) anchor of the grab point on the flat sheet. */
1294
- anchor(o: O, sheet: SheetDims): [number, number];
1295
- /** Map a pointer position in sheet-local XY to option updates. */
1296
- drag(local: {
1297
- x: number;
1298
- y: number;
1299
- }, o: O, sheet: SheetDims): Partial<O>;
1300
- }
1301
- /**
1302
- * A behavior is a named, curated bundle: a deformer stack + a parameter
1303
- * mapping + optional idle loop + handles for direct manipulation. Designers
1304
- * see 3–5 human-named params ('tightness', not 'cylinderRadius'); the stack
1305
- * underneath is an Advanced disclosure. New behaviors are the community
1306
- * on-ramp: ~50 lines over existing deformers.
1307
- */
1308
- interface Behavior<O = AnyOptions> {
1309
- id: string;
1310
- label: string;
1311
- defaults: O;
1312
- optionsSchema: z.ZodType<O, z.ZodTypeDef, unknown>;
1313
- /** Expand human params to the underlying deformer stack. */
1314
- stack(o: O, sheet: SheetDims): DeformerInstance[];
1315
- /** Transient, time-varying option overrides (idle motion). Never persisted. */
1316
- loop?(o: O, t: number): Partial<O>;
1317
- /**
1318
- * Whole-sheet motion written into `pose` each frame (allocation-free),
1319
- * composed after any idle preset's transform. Must be a pure function of
1320
- * (options, t) — the field applies it per instance with a time offset, so
1321
- * it has to be deterministic (flight's travel-across-the-scene).
1322
- */
1323
- transform?(o: O, t: number, pose: AeroPose): void;
1324
- handles?: HandleSpec<O>[];
1325
- /**
1326
- * The two or three options that ARE this behavior — the ones someone
1327
- * reaches for first, in the order they'd reach for them.
1328
- *
1329
- * The schema still generates a control for every option; this only says
1330
- * which ones get the big controls and which fold away behind "More". A
1331
- * behavior that nominates nothing shows all of its options flat, because
1332
- * the library must never hide a param it was not told to hide — silence
1333
- * from a community behavior is not permission to guess.
1334
- *
1335
- * Every name here has to be a field of {@link optionsSchema}; the built-in
1336
- * behaviors are checked for that, and for staying within three.
1337
- */
1338
- signature?: (keyof O & string)[];
1339
- /** The option the transport scrubber drives. */
1340
- progressParam: keyof O & string;
1341
- /** Seconds for a full 0→1 play, and how play repeats. */
1342
- duration: number;
1343
- loopMode: 'yoyo' | 'restart';
1344
- }
1345
-
1346
- /** Community behaviors register here; built-ins are pre-registered. */
1347
- declare function registerBehavior(behavior: Behavior<AnyOptions>): void;
1348
- declare function getBehavior(id: string): Behavior<AnyOptions>;
1349
- declare function listBehaviors(): string[];
1350
-
1351
- declare const peelOptionsSchema: z.ZodObject<{
1352
- progress: z.ZodDefault<z.ZodNumber>;
1353
- /** 'auto' resolves per slot in a `sheet` field (outward-facing corner); standalone it means bottom-right. */
1354
- corner: z.ZodDefault<z.ZodEnum<["top-left", "top-right", "bottom-left", "bottom-right", "auto"]>>;
1355
- /** Curl sharpness — small is a tight dog-ear, large a soft lift. */
1356
- radius: z.ZodDefault<z.ZodNumber>;
1357
- }, "strip", z.ZodTypeAny, {
1358
- corner: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "auto";
1359
- radius: number;
1360
- progress: number;
1361
- }, {
1362
- corner?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "auto" | undefined;
1363
- radius?: number | undefined;
1364
- progress?: number | undefined;
1365
- }>;
1366
- type PeelOptions = z.infer<typeof peelOptionsSchema>;
1367
- /** A corner lifts and curls back — the hero-image hover peel. */
1368
- declare const peel: Behavior<PeelOptions>;
1369
-
1370
- declare const unrollOptionsSchema: z.ZodObject<{
1371
- /** 0 = fully rolled cylinder, 1 = flat sheet. */
1372
- progress: z.ZodDefault<z.ZodNumber>;
1373
- /** How tightly the paper is wound. */
1374
- tightness: z.ZodDefault<z.ZodNumber>;
1375
- /** Idle rocking of the rolled end. */
1376
- sway: z.ZodDefault<z.ZodNumber>;
1377
- }, "strip", z.ZodTypeAny, {
1378
- progress: number;
1379
- tightness: number;
1380
- sway: number;
1381
- }, {
1382
- progress?: number | undefined;
1383
- tightness?: number | undefined;
1384
- sway?: number | undefined;
1385
- }>;
1386
- type UnrollOptions = z.infer<typeof unrollOptionsSchema>;
1387
- /**
1388
- * A receipt unrolls from the bottom: the sheet hangs flat from its top edge
1389
- * and the remaining paper is wound in a roll at the bottom. Content bends
1390
- * true around the roll — the reference-image requirement.
1391
- */
1392
- declare const unroll: Behavior<UnrollOptions>;
1393
-
1394
- declare const flipOptionsSchema: z.ZodObject<{
1395
- /** 0 = flat, 1 = page fully turned over the spine. */
1396
- progress: z.ZodDefault<z.ZodNumber>;
1397
- /** Which edge is the spine. */
1398
- spine: z.ZodDefault<z.ZodEnum<["left", "right"]>>;
1399
- /** Softness of the turning curl. */
1400
- radius: z.ZodDefault<z.ZodNumber>;
1401
- }, "strip", z.ZodTypeAny, {
1402
- radius: number;
1403
- progress: number;
1404
- spine: "left" | "right";
1405
- }, {
1406
- radius?: number | undefined;
1407
- progress?: number | undefined;
1408
- spine?: "left" | "right" | undefined;
1409
- }>;
1410
- type FlipOptions = z.infer<typeof flipOptionsSchema>;
1411
- /**
1412
- * A page turn: the free edge curls up and rolls over toward the spine —
1413
- * the roll deformer with its boundary swept across the page.
1414
- */
1415
- declare const flip: Behavior<FlipOptions>;
1416
-
1417
- declare const letterFoldOptionsSchema: z.ZodObject<{
1418
- /** 0 = flat letter, 1 = fully tri-folded. */
1419
- progress: z.ZodDefault<z.ZodNumber>;
1420
- /** Softness of the two creases. */
1421
- crease: z.ZodDefault<z.ZodNumber>;
1422
- }, "strip", z.ZodTypeAny, {
1423
- progress: number;
1424
- crease: number;
1425
- }, {
1426
- progress?: number | undefined;
1427
- crease?: number | undefined;
1428
- }>;
1429
- type LetterFoldOptions = z.infer<typeof letterFoldOptionsSchema>;
1430
- /**
1431
- * The classic tri-fold: the bottom third folds up first, then the top third
1432
- * folds down over it. Two fold deformers stacked — order is the physics.
1433
- */
1434
- declare const letterFold: Behavior<LetterFoldOptions>;
1435
-
1436
- declare const hangOptionsSchema: z.ZodObject<{
1437
- /** Wind strength driving the ripple. */
1438
- wind: z.ZodDefault<z.ZodNumber>;
1439
- /** Gravity bulge of the hanging sheet. */
1440
- sag: z.ZodDefault<z.ZodNumber>;
1441
- }, "strip", z.ZodTypeAny, {
1442
- wind: number;
1443
- sag: number;
1444
- }, {
1445
- wind?: number | undefined;
1446
- sag?: number | undefined;
1447
- }>;
1448
- type HangOptions = z.infer<typeof hangOptionsSchema>;
1449
- /** A poster hanging from its top edge, rippling in wind. */
1450
- declare const hang: Behavior<HangOptions>;
1451
-
1452
- declare const flyOptionsSchema: z.ZodObject<{
1453
- /** Ripple energy. */
1454
- flutter: z.ZodDefault<z.ZodNumber>;
1455
- /** Aerodynamic arc of the sheet. */
1456
- curve: z.ZodDefault<z.ZodNumber>;
1457
- }, "strip", z.ZodTypeAny, {
1458
- flutter: number;
1459
- curve: number;
1460
- }, {
1461
- flutter?: number | undefined;
1462
- curve?: number | undefined;
1463
- }>;
1464
- type FlyOptions = z.infer<typeof flyOptionsSchema>;
1465
- /** A note carried on air — arched and fluttering. Pair with the `tumble` idle. */
1466
- declare const fly: Behavior<FlyOptions>;
1467
-
1468
- declare const fallOptionsSchema: z.ZodObject<{
1469
- /** Air resistance ripple while falling. */
1470
- flutter: z.ZodDefault<z.ZodNumber>;
1471
- /** A falling sheet always lifts a corner. */
1472
- curl: z.ZodDefault<z.ZodNumber>;
1473
- }, "strip", z.ZodTypeAny, {
1474
- curl: number;
1475
- flutter: number;
1476
- }, {
1477
- curl?: number | undefined;
1478
- flutter?: number | undefined;
1479
- }>;
1480
- type FallOptions = z.infer<typeof fallOptionsSchema>;
1481
- /** A dropped sheet — corner lifted, rippling. Pair with the `tumble` idle for the descent. */
1482
- declare const fall: Behavior<FallOptions>;
1483
-
1484
- declare const settleOptionsSchema: z.ZodObject<{
1485
- /**
1486
- * How long ago it landed, 0..1.
1487
- *
1488
- * 0 is the instant of arrival — still carrying the shape it fell in. 1 is
1489
- * a sheet that has been lying there, where its own weight has flattened
1490
- * out everything except what its stiffness refuses to give up.
1491
- */
1492
- relax: z.ZodDefault<z.ZodNumber>;
1493
- /**
1494
- * How hard the paper resists lying flat, 0..1.
1495
- *
1496
- * This is the stock, not the pose: tissue surrenders completely, card
1497
- * never does. It is the whole reason a settled sheet reads as PAPER and
1498
- * not as a decal — at 0 the mesh is a rectangle painted on the floor.
1499
- */
1500
- lift: z.ZodDefault<z.ZodNumber>;
1501
- /** Which corner stayed up. */
1502
- corner: z.ZodDefault<z.ZodEnum<["top-left", "top-right", "bottom-left", "bottom-right"]>>;
1503
- /**
1504
- * Slack across the middle — the low, long undulation of a sheet that is
1505
- * touching a floor in two places and bridging between them.
1506
- */
1507
- slack: z.ZodDefault<z.ZodNumber>;
1508
- }, "strip", z.ZodTypeAny, {
1509
- corner: "top-left" | "top-right" | "bottom-left" | "bottom-right";
1510
- relax: number;
1511
- lift: number;
1512
- slack: number;
1513
- }, {
1514
- corner?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | undefined;
1515
- relax?: number | undefined;
1516
- lift?: number | undefined;
1517
- slack?: number | undefined;
1518
- }>;
1519
- type SettleOptions = z.infer<typeof settleOptionsSchema>;
1520
- /**
1521
- * A sheet that has landed and relaxed.
1522
- *
1523
- * The library could drop paper (`fall`), fly it (`fly`, `flight`), heap it
1524
- * (`pile`) and catch it mid-air (`spill`) — and had no way at all to show a
1525
- * sheet that has ARRIVED. Every reference installation worth copying has
1526
- * paper on the floor: sheets settled on concrete after the fall, ribbons
1527
- * pooling where they meet the ground. It is the most beautiful detail in the
1528
- * set and it appears in it twice.
1529
- *
1530
- * The distinction from `fall` is not the shape, it is the CLOCK. `fall`
1531
- * flutters — its wave carries `speed: 1.3`, and it is a sheet still arguing
1532
- * with the air. This one is over. Everything here is static, and that is the
1533
- * point: a settled sheet that ripples is a settled sheet nobody believes.
1534
- *
1535
- * Which is also why it composes rather than deforming: a landed sheet is a
1536
- * gentle curl the stiffness held on to, plus a long slack undulation where
1537
- * it bridges the floor. Both already exist, and a deformer that can be
1538
- * spelled out of the ones we have does not earn a GLSL twin and a parity
1539
- * case.
1540
- */
1541
- declare const settle: Behavior<SettleOptions>;
1542
-
1543
- declare const ribbonOptionsSchema: z.ZodObject<{
1544
- /**
1545
- * How much of the drop is lying on the floor, as a fraction of the height.
1546
- *
1547
- * This is the whole image. A strip that stops dead at the ground reads as
1548
- * a strip that was cut to fit; one that arrives with a length to spare and
1549
- * turns over reads as paper meeting a floor, which is the thing the
1550
- * reference installations are actually about.
1551
- */
1552
- pool: z.ZodDefault<z.ZodNumber>;
1553
- /** How tightly it turns where it lands. Low is a soft slump, high is a curl. */
1554
- curl: z.ZodDefault<z.ZodNumber>;
1555
- /** Folds running down the length. A printed strip is never a flat plane. */
1556
- drape: z.ZodDefault<z.ZodNumber>;
1557
- }, "strip", z.ZodTypeAny, {
1558
- curl: number;
1559
- pool: number;
1560
- drape: number;
1561
- }, {
1562
- curl?: number | undefined;
1563
- pool?: number | undefined;
1564
- drape?: number | undefined;
1565
- }>;
1566
- type RibbonOptions = z.infer<typeof ribbonOptionsSchema>;
1567
- /**
1568
- * A strip hung from the ceiling that reaches the floor and keeps going.
1569
- *
1570
- * The single most striking image in the reference set, and the reason it
1571
- * took until now to build is that it needs three separate things that did
1572
- * not exist a week ago: a room with a ceiling to hang from, hardware to hang
1573
- * BY, and type that can be set down the length of a sheet without looking
1574
- * like a caption. It is the payoff for all of them.
1115
+ * Line breaking, shared by every content type that sets prose.
1575
1116
  *
1576
- * The mathematics is not new, which is the point of the contribution ladder.
1577
- * A ribbon is a `drape` down its length and a `fold` whose hinge sits at the
1578
- * floor line rather than at the sheet's centre `fold.offset` has always
1579
- * been able to say "crease here", and nothing had ever asked it to.
1580
- */
1581
- declare const ribbon: Behavior<RibbonOptions>;
1582
-
1583
- declare const carryOptionsSchema: z.ZodObject<{
1584
- /**
1585
- * The grab point — where the pointer was on the paper at pick time.
1586
- * 'auto' is resolved by the carry controller (usually the peeled corner:
1587
- * continuity from peel → carry is the immersion moment).
1588
- */
1589
- grab: z.ZodDefault<z.ZodEnum<["top-left", "top-right", "bottom-left", "bottom-right", "auto"]>>;
1590
- /** From stock feel: a stamp is stiff — it flutters, it doesn't flow. */
1591
- stiffness: z.ZodDefault<z.ZodNumber>;
1592
- flutter: z.ZodDefault<z.ZodNumber>;
1593
- /** How far the paper's yaw trails the drag direction (runtime transform). */
1594
- lag: z.ZodDefault<z.ZodNumber>;
1595
- /** Drag-speed drive (0..1). Written live by the carry controller. */
1596
- drive: z.ZodDefault<z.ZodNumber>;
1597
- }, "strip", z.ZodTypeAny, {
1598
- flutter: number;
1599
- grab: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "auto";
1600
- stiffness: number;
1601
- lag: number;
1602
- drive: number;
1603
- }, {
1604
- flutter?: number | undefined;
1605
- grab?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "auto" | undefined;
1606
- stiffness?: number | undefined;
1607
- lag?: number | undefined;
1608
- drive?: number | undefined;
1609
- }>;
1610
- type CarryOptions = z.infer<typeof carryOptionsSchema>;
1611
- /**
1612
- * A held paper, alive from motion (spec M6 §4.1, the field/cheap path):
1613
- * droop away from the grab point + drag-velocity flutter. The hero path —
1614
- * cloth with a single pin following the cursor — is the existing
1615
- * `physics: 'cloth'` grab; this behavior is what fields and exports run.
1616
- */
1617
- declare const carry: Behavior<CarryOptions>;
1618
-
1619
- declare const flightOptionsSchema: z.ZodObject<{
1620
- /** Directional wind vector — paper travels ACROSS the scene, not just down. */
1621
- wind: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
1622
- gustiness: z.ZodDefault<z.ZodNumber>;
1623
- tumble: z.ZodDefault<z.ZodNumber>;
1624
- /** 'loop' is a seamless idle cycle; 'drift' travels along the wind. */
1625
- path: z.ZodDefault<z.ZodEnum<["drift", "loop"]>>;
1626
- /** Drift only: exit the scene → re-enter the opposite side. */
1627
- respawn: z.ZodDefault<z.ZodBoolean>;
1628
- /** Half-extent of the travel before respawn wraps it. */
1629
- range: z.ZodDefault<z.ZodNumber>;
1630
- }, "strip", z.ZodTypeAny, {
1631
- path: "drift" | "loop";
1632
- wind: [number, number, number];
1633
- gustiness: number;
1634
- tumble: number;
1635
- respawn: boolean;
1636
- range: number;
1637
- }, {
1638
- path?: "drift" | "loop" | undefined;
1639
- wind?: [number, number, number] | undefined;
1640
- gustiness?: number | undefined;
1641
- tumble?: number | undefined;
1642
- respawn?: boolean | undefined;
1643
- range?: number | undefined;
1644
- }>;
1645
- type FlightOptions = z.infer<typeof flightOptionsSchema>;
1646
- /**
1647
- * Untethered paper carried across the scene on the wind (spec M6 §4.2) —
1648
- * the falling-leaf tumble core + directional travel + lift. Transform +
1649
- * deformer based, so it's instancing-safe: a `scatter` layout + `flight`
1650
- * idle = papers blowing through a hero section.
1117
+ * It lives on its own because `paintText` and `paintCard` were about to
1118
+ * carry two copies of it, and two copies of a line-breaker is two answers to
1119
+ * "where does this wrap" which on a sheet that CURLS is not a cosmetic
1120
+ * disagreement: the reader sees the break land on a fold.
1651
1121
  */
1652
- declare const flight: Behavior<FlightOptions>;
1653
-
1654
- declare const crumpleBehaviorOptionsSchema: z.ZodObject<{
1655
- /** 0 = flat sheet, 1 = crushed. */
1656
- progress: z.ZodDefault<z.ZodNumber>;
1657
- /** Few big facets at 0, many small ones at 1. */
1658
- coarseness: z.ZodDefault<z.ZodNumber>;
1659
- /** How far the sheet curls in on itself as it crushes. */
1660
- ball: z.ZodDefault<z.ZodNumber>;
1661
- /** A different crush of the same paper. */
1662
- seed: z.ZodDefault<z.ZodNumber>;
1663
- }, "strip", z.ZodTypeAny, {
1664
- progress: number;
1665
- coarseness: number;
1666
- ball: number;
1667
- seed: number;
1668
- }, {
1669
- progress?: number | undefined;
1670
- coarseness?: number | undefined;
1671
- ball?: number | undefined;
1672
- seed?: number | undefined;
1673
- }>;
1674
- type CrumpleBehaviorOptions = z.infer<typeof crumpleBehaviorOptionsSchema>;
1675
1122
  /**
1676
- * A sheet being screwed up in a fist.
1123
+ * Wrap a paragraph to a measure, breaking a word that cannot fit on its own.
1677
1124
  *
1678
- * Two deformers, in this order for a reason: `crumple` reads the flat sheet
1679
- * position to place its creases, so it has to run before anything that moves
1680
- * the sheet around. Crush the paper, then curl the crushed paper the other
1681
- * way round would crease a curved sheet as if it were still flat.
1682
- */
1683
- declare const crumpleBehavior: Behavior<CrumpleBehaviorOptions>;
1684
-
1685
- declare const idleNames: readonly ["float", "tumble", "dangle", "taped", "breeze"];
1686
- type IdleName = (typeof idleNames)[number];
1687
- interface IdlePose {
1688
- /** Offsets added to the paper's base transform each frame. */
1689
- position: [number, number, number];
1690
- rotation: [number, number, number];
1691
- }
1692
- /**
1693
- * Curated fake physics: hand-tuned motion presets, no simulation. Cheap
1694
- * enough for both hero and field modes, and — for things like the
1695
- * falling-leaf tumble — they read *more* real than a true sim.
1696
- */
1697
- interface IdlePreset {
1698
- id: IdleName;
1699
- label: string;
1700
- /** Whole-sheet motion, written into `pose` (allocation-free). */
1701
- transform?(t: number, pose: IdlePose): void;
1702
- /** Extra deformers appended after the behavior's stack. */
1703
- stack?(): DeformerInstance[];
1704
- }
1705
- declare const idlePresets: Record<IdleName, IdlePreset>;
1706
- declare function getIdlePreset(name: IdleName): IdlePreset;
1707
-
1708
- /**
1709
- * Compose a deformer stack into GLSL — the GPU twin of deformers/compose.ts.
1710
- * Each stack entry gets its own uniform namespace (`uRoll0_angle`,
1711
- * `uFold1_offset`, …) so the same deformer type can appear twice
1712
- * (letter-fold is two folds). Golden-vector parity with the JS path is
1713
- * enforced by the GPU harness test.
1714
- */
1715
- interface ComposedDisplacement {
1716
- /** Uniform declarations + displacement functions. */
1717
- functionsSrc: string;
1718
- /** `vec3 plDisplace(vec3 p, vec2 uv, float t)` applying the whole stack. */
1719
- displaceSrc: string;
1720
- /** Initial uniform values, keyed by their namespaced GLSL names (uSheet included). */
1721
- uniforms: Record<string, number | number[]>;
1722
- }
1723
- /**
1724
- * Just the uniform VALUES for a stack (same namespaced keys as
1725
- * buildDisplacementGLSL) — cheap enough to call every frame while a behavior
1726
- * animates. Structure (names) is stable as long as the stack's type order is.
1727
- */
1728
- declare function stackUniformValues(stack: DeformerInstance[], sheet: SheetDims): Record<string, number | number[]>;
1729
- declare function buildDisplacementGLSL(stack: DeformerInstance[], sheet: SheetDims): ComposedDisplacement;
1730
- /**
1731
- * The field-mode CSM vertex shader: displaced position + numerically
1732
- * recomputed normal (two tangent probes — exact normals of an arbitrary
1733
- * stack have no closed form).
1734
- */
1735
- declare function buildFieldVertexShader(composed: ComposedDisplacement): string;
1736
- /**
1737
- * Field fragment shader: per-instance tile from the shared content atlas on
1738
- * the FRONT face; the BACK face renders the stock with an optional reversed
1739
- * show-through ghost (per-paper back textures are a hero-mode feature).
1740
- */
1741
- declare function buildFieldFragmentShader(): string;
1742
-
1743
- /**
1744
- * A layout is a pure `pose(i, n, options, phase)` function — no state, no
1745
- * three.js. `phase` is the motion driver's continuous offset in turns
1746
- * (0..1 = one full cycle); cyclic layouts use it, static ones ignore it.
1747
- * Community layouts are ~30 lines.
1748
- *
1749
- * Every built-in names a place paper actually sits — a fanned swatch deck, a
1750
- * slipped stack, a heap on a desk — because arrangement alone is what makes a
1751
- * field read as a photo carousel instead of as paper. The other half of that
1752
- * is `bias`: paper in the world does not all bend alike.
1753
- */
1754
- interface PaperPose {
1755
- position: [number, number, number];
1756
- rotation: [number, number, number];
1757
- scale: number;
1758
- /**
1759
- * How strongly this sheet takes the field's deformation: 1 = exactly as the
1760
- * preset configures it, 0 = flat. Lets one instanced draw call curl the top
1761
- * of a pile while the sheets pressed underneath stay flat. Omitted = 1.
1762
- */
1763
- bias?: number;
1764
- }
1765
- interface Layout<O = Record<string, unknown>> {
1766
- id: string;
1767
- label: string;
1768
- defaults: O;
1769
- optionsSchema: z.ZodType<O, z.ZodTypeDef, unknown>;
1770
- /**
1771
- * `sheet` is the field's paper size. Layouts that arrange by CONTACT —
1772
- * edges meeting, sheets resting on each other — cannot work without it,
1773
- * and a layout that ignores it may simply omit the parameter.
1774
- */
1775
- pose(i: number, n: number, o: O, phase: number, sheet: SheetDims): PaperPose;
1776
- /**
1777
- * Where along a walk this layout put each paper, as normalized arc length,
1778
- * in the layout's own index order.
1779
- *
1780
- * Only layouts that arrange along a PATH can answer, which is why it is
1781
- * optional. Stage mode uses it to let a viewer step from one paper to the
1782
- * next: the stops have to be where the paper actually is, and the only
1783
- * thing that knows that is the function that placed it.
1784
- */
1785
- walkStops?(n: number, o: O): number[];
1786
- }
1787
- declare const ringSchema: z.ZodObject<{
1788
- radius: z.ZodDefault<z.ZodNumber>;
1789
- tiltDeg: z.ZodDefault<z.ZodNumber>;
1790
- }, "strip", z.ZodTypeAny, {
1791
- radius: number;
1792
- tiltDeg: number;
1793
- }, {
1794
- radius?: number | undefined;
1795
- tiltDeg?: number | undefined;
1796
- }>;
1797
- /** Prints pegged around a circle — the one carousel worth keeping. */
1798
- declare const ring: Layout<z.infer<typeof ringSchema>>;
1799
- declare const fanSchema: z.ZodObject<{
1800
- /** Total angular sweep from the first sheet to the last, degrees. */
1801
- sweep: z.ZodDefault<z.ZodNumber>;
1802
- /** Where the shared pin sits, in half-sheet-heights below center. 1 = the bottom edge. */
1803
- hinge: z.ZodDefault<z.ZodNumber>;
1804
- /** Thickness step so the sheets stack in order instead of z-fighting. */
1805
- lift: z.ZodDefault<z.ZodNumber>;
1806
- /** How much flatter the middle of the fan sits than its outer sheets. */
1807
- bow: z.ZodDefault<z.ZodNumber>;
1808
- }, "strip", z.ZodTypeAny, {
1809
- lift: number;
1810
- sweep: number;
1811
- hinge: number;
1812
- bow: number;
1813
- }, {
1814
- lift?: number | undefined;
1815
- sweep?: number | undefined;
1816
- hinge?: number | undefined;
1817
- bow?: number | undefined;
1818
- }>;
1819
- /**
1820
- * A Pantone deck, a paint-chip book, a hand of cards: every sheet pinned at
1821
- * one shared point and swung open. The sheets nearest the outside of the
1822
- * sweep carry the most curl, which is what sells the hinge as a hinge.
1823
- */
1824
- declare const fan: Layout<z.infer<typeof fanSchema>>;
1825
- declare const spreadSchema: z.ZodObject<{
1826
- /** How far each sheet slides past the one below it. */
1827
- slip: z.ZodDefault<z.ZodNumber>;
1828
- /** Direction of the slide, degrees. 0 slides right, 90 slides up. */
1829
- angle: z.ZodDefault<z.ZodNumber>;
1830
- lift: z.ZodDefault<z.ZodNumber>;
1831
- /** How much more the sheets at the far end of the slide bow. */
1832
- bow: z.ZodDefault<z.ZodNumber>;
1833
- /** Nothing hand-slid is perfectly square — a touch of per-sheet rotation. */
1834
- drift: z.ZodDefault<z.ZodNumber>;
1835
- }, "strip", z.ZodTypeAny, {
1836
- drift: number;
1837
- angle: number;
1838
- lift: number;
1839
- bow: number;
1840
- slip: number;
1841
- }, {
1842
- drift?: number | undefined;
1843
- angle?: number | undefined;
1844
- lift?: number | undefined;
1845
- bow?: number | undefined;
1846
- slip?: number | undefined;
1847
- }>;
1848
- /**
1849
- * A ream pushed sideways, or a deck dealt across a table: parallel sheets at
1850
- * a constant offset, each one bowing a little more as it comes free of the
1851
- * stack's weight.
1852
- */
1853
- declare const spread: Layout<z.infer<typeof spreadSchema>>;
1854
- declare const pileSchema: z.ZodObject<{
1855
- /** How far sheets wander from the center of the heap. */
1856
- scatter: z.ZodDefault<z.ZodNumber>;
1857
- /** Widest angle a sheet sits off square, degrees. */
1858
- turn: z.ZodDefault<z.ZodNumber>;
1859
- lift: z.ZodDefault<z.ZodNumber>;
1860
- /** How flat the sheets underneath are pressed by the ones on top. */
1861
- press: z.ZodDefault<z.ZodNumber>;
1862
- seed: z.ZodDefault<z.ZodNumber>;
1863
- }, "strip", z.ZodTypeAny, {
1864
- seed: number;
1865
- lift: number;
1866
- scatter: number;
1867
- turn: number;
1868
- press: number;
1869
- }, {
1870
- seed?: number | undefined;
1871
- lift?: number | undefined;
1872
- scatter?: number | undefined;
1873
- turn?: number | undefined;
1874
- press?: number | undefined;
1875
- }>;
1876
- /**
1877
- * The heap on a desk. The physical tell no parametric curve can fake: sheets
1878
- * rest ON each other, so only the top of the pile keeps its curl and
1879
- * everything below is pressed flat by the weight above it.
1880
- */
1881
- declare const pile: Layout<z.infer<typeof pileSchema>>;
1882
- declare const wallSchema: z.ZodObject<{
1883
- gapX: z.ZodDefault<z.ZodNumber>;
1884
- gapY: z.ZodDefault<z.ZodNumber>;
1885
- jitterAmt: z.ZodDefault<z.ZodNumber>;
1886
- /** Spread of sag across the wall — no two pinned sheets hang alike. */
1887
- sag: z.ZodDefault<z.ZodNumber>;
1888
- }, "strip", z.ZodTypeAny, {
1889
- sag: number;
1890
- gapX: number;
1891
- gapY: number;
1892
- jitterAmt: number;
1893
- }, {
1894
- sag?: number | undefined;
1895
- gapX?: number | undefined;
1896
- gapY?: number | undefined;
1897
- jitterAmt?: number | undefined;
1898
- }>;
1899
- /** A studio wall of pinned sheets: a grid, but nothing hangs quite square. */
1900
- declare const wall: Layout<z.infer<typeof wallSchema>>;
1901
- declare const spillSchema: z.ZodObject<{
1902
- spreadX: z.ZodDefault<z.ZodNumber>;
1903
- spreadY: z.ZodDefault<z.ZodNumber>;
1904
- depth: z.ZodDefault<z.ZodNumber>;
1905
- /** How far sheets pitch and roll out of the picture plane. */
1906
- tumble: z.ZodDefault<z.ZodNumber>;
1907
- /** Spread of bend across the sheets — a spill does not fold them alike. */
1908
- vary: z.ZodDefault<z.ZodNumber>;
1909
- seed: z.ZodDefault<z.ZodNumber>;
1910
- }, "strip", z.ZodTypeAny, {
1911
- tumble: number;
1912
- seed: number;
1913
- spreadX: number;
1914
- spreadY: number;
1915
- depth: number;
1916
- vary: number;
1917
- }, {
1918
- tumble?: number | undefined;
1919
- seed?: number | undefined;
1920
- spreadX?: number | undefined;
1921
- spreadY?: number | undefined;
1922
- depth?: number | undefined;
1923
- vary?: number | undefined;
1924
- }>;
1925
- /**
1926
- * A dropped folder's worth of paper, mid-air — what a `pile` looks like the
1927
- * moment before it settles. Loose in all three axes, and (the part that
1928
- * separates it from confetti) every sheet caught at its own angle AND its
1929
- * own amount of bend.
1930
- */
1931
- declare const spill: Layout<z.infer<typeof spillSchema>>;
1932
- declare const sweepSchema: z.ZodObject<{
1933
- columns: z.ZodDefault<z.ZodNumber>;
1934
- /** Breathing room around each specimen. */
1935
- gap: z.ZodDefault<z.ZodNumber>;
1936
- /** Deformation at the first specimen and at the last. */
1937
- from: z.ZodDefault<z.ZodNumber>;
1938
- to: z.ZodDefault<z.ZodNumber>;
1939
- }, "strip", z.ZodTypeAny, {
1940
- columns: number;
1941
- gap: number;
1942
- from: number;
1943
- to: number;
1944
- }, {
1945
- columns?: number | undefined;
1946
- gap?: number | undefined;
1947
- from?: number | undefined;
1948
- to?: number | undefined;
1949
- }>;
1950
- /**
1951
- * A specimen chart: the same sheet mounted in a grid, its deformation ramped
1952
- * across the series so one image shows a curl at ten stages instead of one.
1953
- * The layout the rest of this library exists to make possible — and the one
1954
- * that documents every deformer for free.
1955
- *
1956
- * Only as legible as the preset it charts: a sheet with no behavior or
1957
- * deformers has nothing for the ramp to scale, and every specimen comes out
1958
- * identical.
1959
- */
1960
- declare const sweep: Layout<z.infer<typeof sweepSchema>>;
1961
- declare const bookSchema: z.ZodObject<{
1962
- /** How far the outermost page lifts off the block, degrees. */
1963
- spread: z.ZodDefault<z.ZodNumber>;
1964
- /** Fraction of the pages bound to the left. 0 = a one-sided sample book. */
1965
- split: z.ZodDefault<z.ZodNumber>;
1966
- /** Page thickness — the gap between pages of one block. */
1967
- lift: z.ZodDefault<z.ZodNumber>;
1968
- /** How much more a lifted page arcs than one lying flat in the block. */
1969
- gutter: z.ZodDefault<z.ZodNumber>;
1970
- }, "strip", z.ZodTypeAny, {
1971
- lift: number;
1972
- split: number;
1973
- gutter: number;
1974
- spread: number;
1975
- }, {
1976
- lift?: number | undefined;
1977
- split?: number | undefined;
1978
- gutter?: number | undefined;
1979
- spread?: number | undefined;
1980
- }>;
1981
- /**
1982
- * An open codex: pages hinged on a shared spine, each block splaying away
1983
- * from the gutter. `split` slides it between the two bound forms paper takes
1984
- * — 0.5 is a book lying open, 0 is a swatch deck or sample book bound down
1985
- * one side. Pages lying flat in the block are pressed by the ones above;
1986
- * only the lifted pages keep their arc.
1987
- */
1988
- declare const book: Layout<z.infer<typeof bookSchema>>;
1989
- declare const accordionSchema: z.ZodObject<{
1990
- /** How far each panel tilts off the strip's line, degrees. 0 = flat, 90 = shut. */
1991
- angle: z.ZodDefault<z.ZodNumber>;
1992
- /** A concertina holds its creases — how much bow the panels keep. */
1993
- slack: z.ZodDefault<z.ZodNumber>;
1994
- }, "strip", z.ZodTypeAny, {
1995
- angle: number;
1996
- slack: number;
1997
- }, {
1998
- angle?: number | undefined;
1999
- slack?: number | undefined;
2000
- }>;
2001
- /**
2002
- * A concertina: panels alternating about creases they genuinely share, so
2003
- * the sheets read as ONE folded strip rather than as N separate papers —
2004
- * the only layout here where that is true. Adjacent edges are solved to
2005
- * meet, which is why it needs the sheet's real width.
2006
- */
2007
- declare const accordion: Layout<z.infer<typeof accordionSchema>>;
2008
- declare const rackSchema: z.ZodObject<{
2009
- /** Gap along the row, as a fraction of the paper's width. Under 1 they overlap. */
2010
- spacing: z.ZodDefault<z.ZodNumber>;
2011
- /** How far a sheet leans back off vertical, degrees. */
2012
- lean: z.ZodDefault<z.ZodNumber>;
2013
- /** How much that lean differs sheet to sheet — nothing propped is uniform. */
2014
- vary: z.ZodDefault<z.ZodNumber>;
2015
- /** Small rotations off square. */
2016
- sway: z.ZodDefault<z.ZodNumber>;
2017
- seed: z.ZodDefault<z.ZodNumber>;
2018
- }, "strip", z.ZodTypeAny, {
2019
- sway: number;
2020
- seed: number;
2021
- spacing: number;
2022
- vary: number;
2023
- lean: number;
2024
- }, {
2025
- sway?: number | undefined;
2026
- seed?: number | undefined;
2027
- spacing?: number | undefined;
2028
- vary?: number | undefined;
2029
- lean?: number | undefined;
2030
- }>;
2031
- /**
2032
- * Prints stood in a row and leaning back — against a wall, in a rack, propped
2033
- * along a shelf. The one arrangement here that RESTS on a surface rather than
2034
- * floating: every sheet pivots on the bottom edge it actually stands on, so
2035
- * the row shares a floor. The further a sheet has leaned, the more it bows
2036
- * under its own weight.
2037
- *
2038
- * (Stacking these front-to-back the way a letter tray really holds paper is
2039
- * physically honest and visually useless — the front sheet hides the rest.
2040
- * A row is the arrangement you can actually see.)
2041
- */
2042
- declare const rack: Layout<z.infer<typeof rackSchema>>;
2043
- declare const colonnadeSchema: z.ZodObject<{
2044
- /** The walk the colonnade is built along — see `stage/path`. */
2045
- path: z.ZodDefault<z.ZodObject<{
2046
- points: z.ZodDefault<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, "many">>;
2047
- closed: z.ZodDefault<z.ZodBoolean>;
2048
- }, "strip", z.ZodTypeAny, {
2049
- points: [number, number][];
2050
- closed: boolean;
2051
- }, {
2052
- points?: [number, number][] | undefined;
2053
- closed?: boolean | undefined;
2054
- }>>;
2055
- /** Half-width of the clear aisle: how far each banner stands off the walk line. */
2056
- aisle: z.ZodDefault<z.ZodNumber>;
2057
- /** How much that gap opens and closes along the walk. Nothing hung by hand is a corridor. */
2058
- breathe: z.ZodDefault<z.ZodNumber>;
2059
- /** Widest angle a banner turns off square to the aisle, degrees. */
2060
- twist: z.ZodDefault<z.ZodNumber>;
2061
- /** Fraction of the walk left clear at each end, so the figure has somewhere to enter from. */
2062
- margin: z.ZodDefault<z.ZodNumber>;
2063
- /** Spread of banner heights, 0..1. */
2064
- rise: z.ZodDefault<z.ZodNumber>;
2065
- /**
2066
- * How far the banners lift off the floor, as a fraction of their height.
2067
- *
2068
- * NEGATIVE is allowed, and it is what pooling actually requires. At 0 a
2069
- * banner's bottom EDGE sits on the floor — which is not the same as paper
2070
- * pooling on it, however much the old comment here claimed otherwise. A
2071
- * ribbon creases a pool-length above its bottom edge, so it has to hang
2072
- * that much lower for the crease to land on the ground and the slack to
2073
- * lie ON it rather than in mid-air parallel to it.
2074
- *
2075
- * The bound used to be 0, so the one thing this option documented itself
2076
- * as doing was the one thing it could not do.
2077
- */
2078
- hover: z.ZodDefault<z.ZodNumber>;
2079
- /** Spread of deformation — no two lengths of hung paper drape alike. */
2080
- drape: z.ZodDefault<z.ZodNumber>;
2081
- seed: z.ZodDefault<z.ZodNumber>;
2082
- }, "strip", z.ZodTypeAny, {
2083
- path: {
2084
- points: [number, number][];
2085
- closed: boolean;
2086
- };
2087
- seed: number;
2088
- drape: number;
2089
- hover: number;
2090
- margin: number;
2091
- twist: number;
2092
- aisle: number;
2093
- breathe: number;
2094
- rise: number;
2095
- }, {
2096
- path?: {
2097
- points?: [number, number][] | undefined;
2098
- closed?: boolean | undefined;
2099
- } | undefined;
2100
- seed?: number | undefined;
2101
- drape?: number | undefined;
2102
- hover?: number | undefined;
2103
- margin?: number | undefined;
2104
- twist?: number | undefined;
2105
- aisle?: number | undefined;
2106
- breathe?: number | undefined;
2107
- rise?: number | undefined;
2108
- }>;
2109
- declare const colonnade: Layout<z.infer<typeof colonnadeSchema>>;
2110
- /**
2111
- * A block of stamps: flat rows × columns grid in register, floating a hair
2112
- * above the (field-rendered) backing sheet. Standard layout contract — it
2113
- * also works standalone as a plain grid; `backing`/`backingMargin` are read
2114
- * by the field renderer, not by `pose`.
2115
- */
2116
- declare const sheet: Layout<SheetLayoutOptions>;
2117
- declare function registerLayout(layout: Layout<AnyOptions>): void;
2118
- declare function getLayout(id: string): Layout<AnyOptions>;
2119
- declare function listLayouts(): string[];
2120
-
2121
- /**
2122
- * Framing a field is arithmetic, not guesswork: layouts are pure `pose`
2123
- * functions, so we can just ask one where all its sheets are and put the
2124
- * camera where they all fit. Community layouts get framed for free.
2125
- */
2126
- interface FieldBounds {
2127
- center: [number, number, number];
2128
- /** Half-extents — the box reaches `center ± half`. */
2129
- half: [number, number, number];
2130
- }
2131
- /**
2132
- * The box `n` sheets occupy under a layout. Each sheet is treated as a ball
2133
- * of its half-diagonal, which covers every rotation the pose can apply
2134
- * without having to build the pose matrices.
2135
- */
2136
- declare function fieldBounds(layout: Layout<AnyOptions>, n: number, options: unknown, sheet: SheetDims): FieldBounds;
2137
- /**
2138
- * Where to put a perspective camera so every sheet a layout poses lands in
2139
- * frame. Solved per sheet at its own depth rather than against the field's
2140
- * bounding box: a `ring`'s widest sheets sit at mid-depth, and pretending
2141
- * that width exists at the near face would shove the camera far enough back
2142
- * to lose the gallery entirely.
2143
- */
2144
- declare function fitCamera(layout: Layout<AnyOptions>, n: number, options: unknown, sheet: SheetDims, fovDeg: number, aspect: number, margin?: number): {
2145
- position: [number, number, number];
2146
- target: [number, number, number];
2147
- };
2148
-
2149
- /** How much of the incident key light a fully translucent sheet passes on. */
2150
- declare const TRANSMISSION_GAIN = 0.5;
2151
- interface TranslucencyValues {
2152
- translucency: number;
2153
- /** Unit world direction from the scene toward the key light. */
2154
- direction: THREE.Vector3;
2155
- color: THREE.Color;
2156
- /** Light the room passes through the sheet from every direction at once. */
2157
- ambient: number;
2158
- }
2159
- /**
2160
- * Resolve the transmission uniforms from the paper and the scene's lighting
2161
- * — the key light's own position and color, so translucency can never
2162
- * disagree with the lamp casting the shadows.
2163
- *
2164
- * It takes a resolved rig as well as a name because a rig is no longer
2165
- * always a preset: once the light can be moved, the lamp this sheet is
2166
- * backlit by is the one the SCENE ended up with, not the one the paper was
2167
- * authored against.
2168
- */
2169
- declare function translucencyValues(translucency: number, lighting: LightingName | LightingPreset): TranslucencyValues;
2170
- /** Ready-to-bind uniform objects for a shader program. */
2171
- declare function translucencyUniforms(translucency: number, lighting: LightingName | LightingPreset): Record<string, {
2172
- value: unknown;
2173
- }>;
2174
-
2175
- /**
2176
- * A stock is a named bundle of material + geometry defaults — choosing paper
2177
- * at a print shop. Individual controls always override; schema-wise these are
2178
- * just defaults.
2179
- */
2180
- interface Stock {
2181
- id: StockName;
2182
- label: string;
2183
- /** Base tint, also used as the canvas background behind content. */
2184
- color: string;
2185
- roughness: number;
2186
- /** 0 = opaque. Vellum is translucent. */
2187
- opacity: number;
2188
- /**
2189
- * How much light passes THROUGH the sheet when something is behind it,
2190
- * 0..1. Distinct from `opacity`: newsprint is fully opaque to look at and
2191
- * still glows on a lightbox. This is what makes a backlit banner read.
2192
- */
2193
- translucency: number;
2194
- /** Ink multiply tint for content drawn on this stock (thermal prints grey-black). */
2195
- inkColor: string;
2196
- /** Thermal-printer banding intensity baked into the grain effect. */
2197
- banding: number;
2198
- /** Surface effects this stock ships with; explicit surface config overrides per key. */
2199
- defaultSurface: {
2200
- grain?: number;
2201
- aging?: number;
2202
- };
2203
- /** Reversed front-content ghost on the backside (thin stocks let ink show). */
2204
- showThrough: number;
2205
- /** Glossy near-white glue underside (stickers) — forces showThrough 0. */
2206
- adhesive: boolean;
2207
- }
2208
- declare const stocks: Record<StockName, Stock>;
2209
- declare function getStock(name: StockName): Stock;
2210
-
2211
- /**
2212
- * Grid shape for `count` tiles of a given aspect (height / width).
2213
- *
2214
- * Square tiles want a square grid, but a stage banner is 5.7 times taller
2215
- * than it is wide, and packing those into a square GRID makes an atlas five
2216
- * times taller than it is wide — which then has to be squashed to fit the
2217
- * texture budget, and the content with it. Choosing cols/rows ≈ aspect keeps
2218
- * the atlas itself roughly square whatever shape the paper is.
2219
- */
2220
- declare function atlasGrid(count: number, aspect?: number): {
2221
- cols: number;
2222
- rows: number;
2223
- };
2224
- interface ContentAtlas {
2225
- texture: THREE.CanvasTexture;
2226
- cols: number;
2227
- rows: number;
2228
- }
2229
- declare function useContentAtlas(contents: ContentConfig[], sheet: SheetConfig, stock: Stock): ContentAtlas | null;
2230
-
2231
- /** highp float + sin/cos implementation differences across GPUs. */
2232
- declare const PARITY_EPSILON = 0.0005;
2233
- interface ParityCase {
2234
- name: string;
2235
- stack: DeformerInstance[];
2236
- sheet: SheetDims;
2237
- t: number;
2238
- }
2239
- interface ParityResult {
2240
- name: string;
2241
- maxError: number;
2242
- pass: boolean;
2243
- }
2244
- /** One case per deformer feature worth guarding, both easy and adversarial. */
2245
- declare const parityCases: ParityCase[];
2246
- /** Run every parity case. Requires a WebGL2 context with float render targets. */
2247
- declare function runParityHarness(canvas?: HTMLCanvasElement): ParityResult[];
2248
-
2249
- type PinMode = 'top-edge' | 'top-corners' | 'corner' | 'none';
2250
- interface ClothParams {
2251
- /** Bend-spring strength: 1 = crisp paper, 0 = silk. Fabric mode falls out for free. */
2252
- stiffness: number;
2253
- gravity: number;
2254
- wind: number;
2255
- /** Local-space y of the ground plane; particles settle onto it. */
2256
- floor: number;
2257
- }
2258
- /**
2259
- * Verlet mass-spring grid on the sheet's own vertices — structural + shear +
2260
- * bend springs, pins as the interface, wind as a force field, fixed timestep
2261
- * with substeps, sleep when kinetic energy is negligible.
2262
- *
2263
- * Constraint (enforced in the schema): cloth OWNS vertex positions — a paper
2264
- * runs a behavior (deformer stack) OR cloth, never both. Pure JS, no three
2265
- * dependency: the PaperMesh adapter copies `positions` into the geometry.
2266
- */
2267
- declare class ClothSim {
2268
- readonly cols: number;
2269
- readonly rows: number;
2270
- readonly count: number;
2271
- readonly positions: Float32Array;
2272
- private readonly prev;
2273
- private readonly pinned;
2274
- private readonly pinTargets;
2275
- private readonly constraints;
2276
- private params;
2277
- private time;
2278
- private accumulator;
2279
- private stillFrames;
2280
- private grabbedIndex;
2281
- /** True when the sim has settled and steps are skipped. */
2282
- asleep: boolean;
2283
- constructor(cols: number, rows: number, width: number, height: number, pins: PinMode, params: ClothParams);
2284
- setParams(params: Partial<ClothParams>): void;
2285
- wake(): void;
2286
- /** Nearest particle to a local-space point — the grab interface. */
2287
- grabNearest(x: number, y: number, z: number): number;
2288
- moveGrab(x: number, y: number, z: number): void;
2289
- release(): void;
2290
- step(delta: number): void;
2291
- private substep;
2292
- }
2293
-
2294
- /**
2295
- * Surface effects are fragment-side chunks composed into ONE shader program
2296
- * per effect set (grain + deckle + aging = one program). Uniforms are
2297
- * namespaced per effect; shared helpers (noise) are included once.
2298
- */
2299
- interface ComposedSurface {
2300
- /** Distinguishes shader *structures* — same key ⇒ same program, only uniforms change. */
2301
- structureKey: string;
2302
- vertexShader: string;
2303
- fragmentShader: string;
2304
- uniforms: Record<string, {
2305
- value: unknown;
2306
- }>;
2307
- /** Deckle discards via alphaTest (not blending) so shadows stay correct. */
2308
- alphaTest: number;
2309
- }
2310
- /** Which content textures exist — part of the shader structure. */
2311
- interface SurfaceMaps {
2312
- hasFrontMap: boolean;
2313
- hasBackMap: boolean;
2314
- }
2315
- /**
2316
- * Compose the enabled effects into one program. The shader owns the base
2317
- * color entirely: the FRONT face samples the content texture, the BACK face
2318
- * renders the stock (or content.back) with an optional reversed show-through
2319
- * ghost — a single DoubleSide map would mirror the front content onto the
2320
- * back, which real paper doesn't do.
2321
- */
2322
- declare function composeSurface(surface: SurfaceConfig, stock: Stock, thickness: number, maps?: SurfaceMaps,
2323
- /** World dims — perforation holes are sized in world units. */
2324
- sheet?: {
2325
- width: number;
2326
- height: number;
2327
- },
2328
- /** Whose key light transmission is measured against — a preset name or the scene's resolved rig. */
2329
- lighting?: LightingName | LightingPreset): ComposedSurface;
2330
-
2331
- interface PaperMaterialProps {
2332
- stock: Stock;
2333
- texture: THREE.Texture | null;
2334
- /** content.back rendered on the reverse side (stock color otherwise). */
2335
- backTexture?: THREE.Texture | null;
2336
- surface: SurfaceConfig;
2337
- thickness: number;
2338
- /** World dims — perforation holes are sized in world units. */
2339
- sheet?: {
2340
- width: number;
2341
- height: number;
2342
- };
2343
- /**
2344
- * Scene lighting — transmission is measured against its key light. A
2345
- * `<LightRig>` above this material wins over it: in a stage the paper is
2346
- * lit by the hall, not by the preset it was authored with.
2347
- */
2348
- lighting?: LightingName;
2349
- }
2350
- /**
2351
- * The paper's skin: MeshStandardMaterial (real lighting preserved) extended
2352
- * with the composed surface-effect chunks. Content textures are sampled by
2353
- * OUR fragment (not material.map) so front and back faces can differ — real
2354
- * paper doesn't mirror its front through the sheet. Programs rebuild only
2355
- * on structure change; value edits mutate uniforms in place.
2356
- */
2357
- declare function PaperMaterial({ stock, texture, backTexture, surface, thickness, sheet, lighting, }: PaperMaterialProps): react.JSX.Element;
2358
-
2359
- type ReceiptContent = Extract<ContentConfig, {
2360
- type: 'receipt';
2361
- }>;
2362
- interface ReceiptTotals {
2363
- subtotal: number;
2364
- tax: number;
2365
- total: number;
2366
- }
2367
- declare function receiptTotals(content: ReceiptContent): ReceiptTotals;
2368
- /** Deterministic bar widths from a string — stylized Code-128 look. */
2369
- declare function barcodeBars(seed: string): number[];
2370
-
2371
- type CardContent = Extract<ContentConfig, {
2372
- type: 'card';
2373
- }>;
2374
-
2375
- /**
2376
- * Line breaking, shared by every content type that sets prose.
2377
- *
2378
- * It lives on its own because `paintText` and `paintCard` were about to
2379
- * carry two copies of it, and two copies of a line-breaker is two answers to
2380
- * "where does this wrap" — which on a sheet that CURLS is not a cosmetic
2381
- * disagreement: the reader sees the break land on a fold.
2382
- */
2383
- /**
2384
- * Wrap a paragraph to a measure, breaking a word that cannot fit on its own.
2385
- *
2386
- * The last clause is the part the old loop got wrong. It appended a word
2387
- * whenever the line was empty, on the reasonable theory that one word always
2388
- * fits — but a long URL or a compound on a narrow banner does not, and it
2389
- * ran off the edge of the sheet with nothing to stop it. A sheet is a
2390
- * physical object: type that leaves it has left it.
1125
+ * The last clause is the part the old loop got wrong. It appended a word
1126
+ * whenever the line was empty, on the reasonable theory that one word always
1127
+ * fits but a long URL or a compound on a narrow banner does not, and it
1128
+ * ran off the edge of the sheet with nothing to stop it. A sheet is a
1129
+ * physical object: type that leaves it has left it.
2391
1130
  */
2392
1131
  declare function wrapLines(ctx: CanvasRenderingContext2D, text: string, maxWidth: number, font: string): string[];
2393
1132
 
2394
- /**
2395
- * Deep-merge preset config with prop overrides (overrides win; arrays and
2396
- * discriminated unions replace wholesale, plain objects merge).
2397
- *
2398
- * Lives in its own module (not serialize.ts) because the schema needs it to
2399
- * validate state overrides — schema → serialize would be circular.
2400
- */
2401
- declare function mergeConfig<T>(base: T, override: unknown): T;
2402
- /**
2403
- * Like {@link mergeConfig}, but an explicit `undefined` DELETES its key instead
2404
- * of being ignored — the semantics a BASE-config write needs: structural
2405
- * setters can clear `behavior`/`deformers` or toggle a surface effect off.
2406
- * Discriminated unions (differing `type`) still replace wholesale.
2407
- */
2408
- declare function mergeWithDeletes<T>(base: T, patch: unknown): T;
2409
-
2410
- /** Parse anything preset-shaped (object or JSON string) into a full, defaulted config. */
2411
- declare function parsePreset(input: PaperConfigInput | string): PaperConfig;
2412
- /** Serialize a config to `.paper` JSON. */
2413
- declare function serializePreset(config: PaperConfig): string;
2414
-
2415
- /** The minimal PaperConfigInput that parses back to `config`. */
2416
- declare function diffConfig(config: PaperConfig): PaperConfigInput;
2417
- /**
2418
- * A plain `<Paper />` snippet with only the non-default props — the
2419
- * secondary export for people who read code.
2420
- */
2421
- declare function buildJsxSnippet(config: PaperConfig): string;
2422
-
2423
- /**
2424
- * The primary export consumer is a coding agent; the human is the courier.
2425
- * This template IS product surface: versioned, snapshot-tested, regenerated
2426
- * from the config. Anatomy is fixed — install → inlined code → placement
2427
- * contract → a verification step the agent can self-check → pre-empted
2428
- * failure modes.
2429
- */
2430
- /**
2431
- * v2: field exports (multi-preset galleries with inlined preset consts).
2432
- * v3: interaction states, sheet/backing fields, drop zones, carry/flight.
2433
- */
2434
- declare const AGENT_PAYLOAD_VERSION = 3;
2435
- /** One line an agent can verify against what it sees after `npm run dev`. */
2436
- declare function describeConfig(config: PaperConfig): string;
2437
- /** The self-contained integration brief — one paste into a coding agent. */
2438
- declare function buildAgentPayload(config: PaperConfig): string;
2439
-
2440
- /**
2441
- * Field-mode export. Every preset a field references is INLINED as a const —
2442
- * receivers don't have the sender's local preset library. Same fixed payload
2443
- * anatomy as the single-paper export, same version.
2444
- */
2445
- interface FieldExportPaper {
2446
- /** Display name of the preset this slot uses (const naming + description). */
2447
- presetName: string;
2448
- /** The RESOLVED preset config (user presets included — nothing to fetch). */
2449
- preset: PaperConfig;
2450
- content?: ContentConfig;
2451
- /** Per-slot state overrides (slot 3's hover deeper than slot 4's). */
2452
- states?: PaperStatesInput;
2453
- }
2454
- interface FieldExportZone {
2455
- id: string;
2456
- accept?: string[];
2457
- bounds: {
2458
- position: [number, number, number];
2459
- size: [number, number];
2460
- };
2461
- highlight?: 'none' | 'glow' | 'outline';
2462
- }
2463
- interface FieldExportInput {
2464
- layout: string;
2465
- layoutOptions?: Record<string, unknown>;
2466
- motion?: {
2467
- driver?: 'autoplay' | 'drag' | 'none';
2468
- speed?: number;
2469
- };
2470
- entrance?: {
2471
- type?: 'rise' | 'scatter' | 'none';
2472
- stagger?: number;
2473
- duration?: number;
2474
- };
2475
- papers: FieldExportPaper[];
2476
- /** Drop zones — exported as `<DropZone>` children with an onPlace stub. */
2477
- zones?: FieldExportZone[];
2478
- }
2479
- interface DistinctPreset {
2480
- varName: string;
2481
- presetName: string;
2482
- preset: PaperConfig;
2483
- }
2484
- /** Distinct presets in slot order, with collision-safe const names. */
2485
- declare function distinctFieldPresets(input: FieldExportInput): DistinctPreset[];
2486
- /** The layout/motion/entrance props of the export, defaults stripped. */
2487
- declare function diffFieldProps(input: FieldExportInput): Record<string, unknown>;
2488
- /** The one-line visual an agent verifies after `npm run dev`. */
2489
- declare function describeFieldConfig(input: FieldExportInput): string;
2490
- /** Component source shared by the JSX snippet and the agent payload. */
2491
- declare function buildFieldComponentSource(input: FieldExportInput): string;
2492
- /** The self-contained field integration brief — one paste into a coding agent. */
2493
- declare function buildFieldAgentPayload(input: FieldExportInput): string;
2494
-
2495
- /**
2496
- * Accessibility layer: reduced-motion handling, a hidden DOM mirror so the
2497
- * paper's content exists for screen readers and find-in-page, and a no-WebGL
2498
- * DOM fallback.
2499
- */
2500
- declare function usePrefersReducedMotion(override?: boolean): boolean;
2501
- declare function supportsWebGL(): boolean;
2502
- /** Human-readable text equivalent of a paper's content. */
2503
- declare function contentText(config: PaperConfig): string;
2504
- /** Hidden DOM twin of the 3D paper — screen readers read it, ctrl-F finds it. */
2505
- declare function PaperMirror({ config }: {
2506
- config: PaperConfig;
2507
- }): react.JSX.Element;
2508
- /** No WebGL: the paper renders flat — content on a stock-tinted card. */
2509
- declare function PaperFallback({ config }: {
2510
- config: PaperConfig;
2511
- }): react.JSX.Element;
2512
-
2513
- /**
2514
- * Stop-motion feel: quantize continuous time to animation "twos"
2515
- * (12 steps per second, like shooting on twos at 24fps). Applies to
2516
- * deformer time, idle motion, and behavior progress; the camera is
2517
- * excluded by default — quantized cameras read as jank, not craft.
2518
- */
2519
- declare const ON_TWOS_FPS = 12;
2520
- declare function quantizeTime(t: number, fps?: number): number;
2521
- /** Snap a 0..1 progress that plays over `duration` seconds to whole frames. */
2522
- declare function quantizeProgress(p: number, duration: number, fps?: number): number;
2523
-
2524
- declare function getPreset(name: string): PaperConfig;
2525
- /** Register a user preset (validated). Built-in names are reserved. */
2526
- declare function registerPreset(name: string, input: PaperConfigInput): void;
2527
- declare function unregisterPreset(name: string): void;
2528
- declare function isBuiltinPreset(name: string): boolean;
2529
- declare function listPresets(): string[];
2530
- /**
2531
- * A collision-free preset name built from `base`: `base`, else `base 2`,
2532
- * `base 3`, … The disambiguating suffix always grows from the SAME base — a
2533
- * name derived from a synthetic base (e.g. an untitled import → "imported")
2534
- * must not fall back to the original when it collides. `taken` reports whether
2535
- * a candidate is already used (built-in or user preset).
2536
- */
2537
- declare function uniquePresetName(base: string, taken: (name: string) => boolean): string;
2538
-
2539
- /**
2540
- * Resolve the subdivision grid for a sheet.
2541
- *
2542
- * `minSegments` is the correctness floor the active deformers require, and it
2543
- * applies however `segments` is set. `autoSegments` is what those deformers
2544
- * WANT for the options they are carrying, and it is what `'auto'` resolves
2545
- * to — see `stackAutoSegments` and `core/tessellation.ts`.
2546
- *
2547
- * Both are per axis, because a demand is a demand along a DIRECTION. A
2548
- * banner draped in folds across its width needs those folds resolved across
2549
- * and needs almost nothing down its drop; a single number spread by aspect
2550
- * ratio gives the drop the density and the folds the leftovers, which is
2551
- * both the expensive answer and the wrong-looking one. A bare number is
2552
- * still accepted and still means "this many, both ways".
2553
- *
2554
- * `'auto'` used to hand the long side a flat 72 whatever was on the sheet, so
2555
- * a blank page was tessellated exactly as finely as a crumpled one and every
2556
- * `minSegments` in the library was dead weight — nothing could ever raise a
2557
- * grid that already started at the highest value anyone asked for. It now
2558
- * sizes to the work, quantized onto a ladder so that dragging a slider does
2559
- * not rebuild the mesh.
2560
- *
2561
- * Omitting `autoSegments` keeps the old flat 72, which is what a caller with
2562
- * no deformer stack in hand should get — this helper is exported, and its
2563
- * answer to an unchanged call should not have changed.
2564
- */
2565
- declare function resolveSegments(sheet: SheetConfig, minSegments?: number | SegmentPair, autoSegments?: number | SegmentPair): [number, number];
2566
- /**
2567
- * Geometry factory. The sheet lives in its local XY plane, centered on the
2568
- * origin, facing +Z. Deformers displace these vertices; the base (flat)
2569
- * positions are kept by the caller for re-deformation each frame.
2570
- */
2571
- declare function createSheetGeometry(sheet: SheetConfig, minSegments?: number | SegmentPair, autoSegments?: number | SegmentPair): THREE.PlaneGeometry;
2572
-
2573
- type PaperMode = 'hero' | 'field';
2574
- type PaperModeRequest = PaperMode | 'auto';
2575
- interface ModeContext {
2576
- interactive: boolean;
2577
- physics: string;
2578
- count: number;
2579
- }
2580
- /**
2581
- * Hero mode: deformer stack runs in JS on the CPU, writing BufferGeometry
2582
- * positions (correct raycasting, shadows, physics, handle drags).
2583
- * Field mode: composed GLSL vertex chunks, instanced (galleries, 10+ sheets).
2584
- * The same preset JSON drives both.
2585
- */
2586
- declare function resolveMode(requested: PaperModeRequest, ctx: ModeContext): PaperMode;
2587
-
2588
- export { AGENT_PAYLOAD_VERSION, AUTO_CEILING, type AeroPose, type BackingDrawSpec, type Behavior, BehaviorConfigInput, type BendOptions, type CardContent, type CarryOptions, type ClothParams, ClothSim, type ComposedDisplacement, type ComposedSurface, type ContentAtlas, ContentConfig, ContentConfigInput, type CrumpleBehaviorOptions, type CrumpleOptions, type CurlOptions, type DampedValue, type Deformer, type DeformerContext, type DeformerInstance, DeformerInstanceConfigInput, type DrapeOptions, DropZone, type DropZoneConfig, type DropZoneProps, DropZoneRegistry, FLAT_SEGMENTS, type FallOptions, type FieldA11yController, type FieldBounds, type FieldExportInput, type FieldExportPaper, type FieldExportZone, FieldPaperSlot, FilmName, type FlightOptions, type FlightParams, type FlipOptions, type FlyOptions, type FoldOptions, type HandleSpec, type HangOptions, type IdleName, type IdlePreset, type KeyboardCarry, type KeyboardStepResult, type Layout, type LetterFoldOptions, type LightAngles, type LightOverrides, type LightOverridesInput, LightRig, LightingName, type LightingPreset, ON_TWOS_FPS, PARITY_EPSILON, Paper, PaperConfig, PaperConfigInput, PaperEdge, PaperFallback, PaperField, PaperFieldMesh, type PaperFieldMeshProps, type PaperFieldProps, type PaperHandle, PaperLighting, type PaperLightingProps, PaperMaterial, type PaperMaterialProps, PaperMesh, type PaperMeshProps, PaperMirror, type PaperMode, type PaperModeRequest, type PaperPose, type PaperProps, PaperStateMachine, type PaperStateMachineOptions, PaperStatesInput, type ParityCase, type ParityResult, type PeelOptions, PhysicsConfigInput, type PinMode, type PlacedPaper, type ReceiptContent, type RibbonOptions, type RollOptions, SAG_TOL, SHEET_LIFT, SceneConfigInput, type SettleOptions, SheetConfig, type SheetDims, type SheetLayoutOptions, type StateEvent, StateName, type Stock, StockName, SurfaceConfig, SurfaceConfigInput, type SurfaceMaps, TRANSMISSION_GAIN, type TranslucencyValues, type UnrollOptions, type UsePaperStatesResult, type WaveOptions, accordion, applyDeformerStack, atlasGrid, barcodeBars, bend, bendOptionsSchema, book, buildAgentPayload, buildDisplacementGLSL, buildFieldAgentPayload, buildFieldComponentSource, buildFieldFragmentShader, buildFieldVertexShader, buildJsxSnippet, carry, carryDrive, carryOptionsSchema, colonnade, composeSurface, contentText, cornerNames, createSheetGeometry, crumple, crumpleBehavior, crumpleBehaviorOptionsSchema, crumpleOptionsSchema, curl, curlOptionsSchema, dampTo, describeConfig, describeFieldConfig, diffConfig, diffFieldProps, displacePoint, distinctFieldPresets, drape, drapeOptionsSchema, drawBacking, fall, fallOptionsSchema, fan, fieldBounds, fieldKeyboardStep, fitCamera, flattenNumeric, flight, flightOptionsSchema, flightPose, flip, flipOptionsSchema, fly, flyOptionsSchema, fold, foldOptionsSchema, getBehavior, getDeformer, getIdlePreset, getLayout, getLightingPreset, getPreset, getStock, gust, hang, hangOptionsSchema, idleNames, idlePresets, isBuiltinPreset, letterFold, letterFoldOptionsSchema, lightAngles, lightPosition, lightSchema, lightenHex, lightingPresets, listBehaviors, listDeformers, listLayouts, listPresets, makeGoboTexture, mergeConfig, mergeWithDeletes, outwardCorner, parityCases, parsePreset, peel, peelOptionsSchema, pile, quantizeProgress, quantizeSegments, quantizeTime, rack, receiptTotals, recordStateOverride, registerBehavior, registerDeformer, registerLayout, registerPreset, resolveConfig, resolveDeformerStack, resolveLighting, resolveMode, resolveSegments, resolveStateConfig, ribbon, ribbonOptionsSchema, ring, roll, rollOptionsSchema, runParityHarness, segmentsForArc, segmentsForSine, serializePreset, settle, settleOptionsSchema, sheet, sheetBackingSize, sheetLayoutSchema, sheetSlotXY, silhouetteRects, spanAlong, spill, spread, stackAutoSegments, stackMinSegments, stackUniformValues, stateEventTransitions, stocks, stripStates, supportsWebGL, sweep, tornEdgesOnDetach, translucencyUniforms, translucencyValues, uniquePresetName, unregisterPreset, unroll, unrollOptionsSchema, useContentAtlas, usePaperStates, usePrefersReducedMotion, wall, wave, waveOptionsSchema, withSheetCellFromPaper, wrapLines, zoneAccepts };
1133
+ export { AUTO_CEILING, type Behavior, BehaviorConfigInput, ContentConfig, ContentConfigInput, type Deformer, type DeformerContext, type DeformerInstance, DeformerInstanceConfigInput, DropZone, type DropZoneConfig, type DropZoneProps, FLAT_SEGMENTS, type FieldExportInput, type FieldExportPaper, type FieldExportZone, FieldPaperSlot, FilmName, type HandleSpec, type IdleName, type IdlePreset, type Layout, type LightAngles, type LightOverrides, type LightOverridesInput, LightRig, LightingName, type LightingPreset, PARITY_EPSILON, Paper, PaperConfig, PaperConfigInput, PaperField, PaperFieldMesh, type PaperFieldMeshProps, type PaperFieldProps, type PaperHandle, PaperLighting, type PaperLightingProps, PaperMesh, type PaperMeshProps, type PaperPose, type PaperProps, PaperStatesInput, type ParityCase, type ParityResult, PhysicsConfigInput, type PlacedPaper, SAG_TOL, SceneConfigInput, SheetConfig, type SheetDims, type SheetLayoutOptions, type StateEvent, type Stock, StockName, SurfaceConfigInput, type UsePaperStatesResult, buildAgentPayload, buildFieldAgentPayload, buildFieldComponentSource, buildJsxSnippet, describeConfig, describeFieldConfig, diffConfig, diffFieldProps, getBehavior, getDeformer, getLayout, getPreset, getStock, idleNames, isBuiltinPreset, lightAngles, lightSchema, listBehaviors, listDeformers, listLayouts, listPresets, mergeConfig, mergeWithDeletes, parityCases, parsePreset, quantizeSegments, recordStateOverride, registerBehavior, registerDeformer, registerLayout, registerPreset, resolveLighting, resolveStateConfig, runParityHarness, segmentsForArc, segmentsForSine, serializePreset, sheetLayoutSchema, spanAlong, stocks, supportsWebGL, uniquePresetName, unregisterPreset, usePaperStates, usePrefersReducedMotion, wrapLines };