paperlab 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -45
- package/dist/{chunk-3IMUEESH.js → chunk-XE4E43MD.js} +4 -4
- package/dist/chunk-XE4E43MD.js.map +1 -0
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -12
- package/dist/index.d.ts +12 -12
- package/dist/index.js +1 -1
- package/dist/{slots-D0Nc_5dq.d.cts → slots-DUMR64HB.d.cts} +2 -2
- package/dist/{slots-D0Nc_5dq.d.ts → slots-DUMR64HB.d.ts} +2 -2
- package/dist/stage.cjs +3 -3
- package/dist/stage.cjs.map +1 -1
- package/dist/stage.d.cts +1 -1
- package/dist/stage.d.ts +1 -1
- package/dist/stage.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-3IMUEESH.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,31 +1,39 @@
|
|
|
1
1
|
# Paperlab
|
|
2
2
|
|
|
3
|
-
**Physical, realistic paper as a React component.**
|
|
3
|
+
**Physical, realistic paper as a React component.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/paperlab)
|
|
6
|
+
[](https://github.com/NourMtir0722/Paperlab/actions/workflows/ci.yml)
|
|
7
|
+
[](https://github.com/NourMtir0722/Paperlab/blob/main/LICENSE)
|
|
8
|
+
|
|
9
|
+
A hero image that peels, a receipt that unrolls, a letter that folds, a poster rippling in wind, a gallery ring of prints. A sheet is real 3D geometry, not a CSS trick and not a video — content is a texture on a mesh that genuinely bends, so text and imagery curl with perfect continuity.
|
|
10
|
+
|
|
11
|
+
**[Try it →](https://nourmtir0722.github.io/Paperlab/)** · [the editor](https://nourmtir0722.github.io/Paperlab/editor/) (desktop) · [the reference](https://nourmtir0722.github.io/Paperlab/docs/) · [for coding agents](https://github.com/NourMtir0722/Paperlab/blob/main/AGENTS.md)
|
|
6
12
|
|
|
7
13
|
| | |
|
|
8
14
|
|---|---|
|
|
9
|
-
|  |  |  |
|
|
10
16
|
|  |  |
|
|
11
17
|
|
|
12
|
-
Every frame above is real geometry — no video, no sprite sheet.
|
|
13
|
-
|
|
14
|
-

|
|
18
|
+
Every frame above is real geometry — no video, no sprite sheet.
|
|
15
19
|
|
|
16
|
-
##
|
|
20
|
+
## Install
|
|
17
21
|
|
|
18
22
|
```sh
|
|
19
23
|
npm i paperlab three @react-three/fiber gsap
|
|
20
24
|
```
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
Requires React ≥ 19 and three ≥ 0.162. TypeScript types ship with the package; both ESM and CJS builds are published.
|
|
27
|
+
|
|
28
|
+
Stage mode lives at the `paperlab/stage` subpath and needs two more peers:
|
|
23
29
|
|
|
24
30
|
```sh
|
|
25
31
|
npm i @react-three/postprocessing postprocessing
|
|
26
32
|
```
|
|
27
33
|
|
|
28
|
-
They are optional
|
|
34
|
+
They are optional. `<Paper>` and `<PaperField>` never reach for them, so a bundle that does not import `paperlab/stage` contains none of it — and the subpath is what keeps the import specifier itself out of your build graph.
|
|
35
|
+
|
|
36
|
+
## Quick start
|
|
29
37
|
|
|
30
38
|
```tsx
|
|
31
39
|
import { Paper } from 'paperlab'
|
|
@@ -34,7 +42,7 @@ import { Paper } from 'paperlab'
|
|
|
34
42
|
<Paper preset="receipt-unroll" autoplay />
|
|
35
43
|
```
|
|
36
44
|
|
|
37
|
-
Or
|
|
45
|
+
Or configure it yourself:
|
|
38
46
|
|
|
39
47
|
```tsx
|
|
40
48
|
<Paper
|
|
@@ -48,7 +56,16 @@ Or sculpt your own:
|
|
|
48
56
|
/>
|
|
49
57
|
```
|
|
50
58
|
|
|
51
|
-
|
|
59
|
+
## The four components
|
|
60
|
+
|
|
61
|
+
| | |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `<Paper>` | one sheet, owns its own `<Canvas>`, fills its parent |
|
|
64
|
+
| `<PaperMesh>` | the same sheet without a canvas, for an existing React Three Fiber scene |
|
|
65
|
+
| `<PaperField>` | many sheets in **one instanced draw call**, arranged by a layout |
|
|
66
|
+
| `<PaperStage>` | paper as architecture — a room of hanging banners you can walk through (`paperlab/stage`) |
|
|
67
|
+
|
|
68
|
+
A gallery is one component:
|
|
52
69
|
|
|
53
70
|
```tsx
|
|
54
71
|
import { PaperField } from 'paperlab'
|
|
@@ -56,7 +73,7 @@ import { PaperField } from 'paperlab'
|
|
|
56
73
|
<PaperField images={photos} preset="photo-print" layout="ring" />
|
|
57
74
|
```
|
|
58
75
|
|
|
59
|
-
|
|
76
|
+
And a space built out of a sentence is one component:
|
|
60
77
|
|
|
61
78
|
```tsx
|
|
62
79
|
import { PaperStage } from 'paperlab/stage'
|
|
@@ -64,39 +81,84 @@ import { PaperStage } from 'paperlab/stage'
|
|
|
64
81
|
<PaperStage text="the paper remembers every hand that folded it" progress={scroll} />
|
|
65
82
|
```
|
|
66
83
|
|
|
67
|
-
##
|
|
84
|
+
## The catalogue
|
|
68
85
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
- **Physics** — curated idle motion (`float`, `tumble`, `breeze`…) that composes with behaviors, and a verlet **cloth** mode: pin the top edge, add wind, grab the sheet and pull.
|
|
72
|
-
- **Field mode** — 10+ papers render as *one instanced draw call* with the deformers running on the GPU (parity-tested against the CPU path), arranged by pure layout functions. Every layout names somewhere paper actually sits: `book` (pages splayed from a spine — `split: 0` makes it a swatch deck), `accordion` (one continuous concertina strip), `fan` (a hand of cards), `spread` (a stack slid sideways), `pile` (a heap on a desk), `rack` (prints stood in a row, leaning back), `wall` (a pinned studio wall), `spill` (a dropped stack mid-air), `colonnade` (banners arranged along a walk, for stage mode), plus `ring`, `sheet`, and `sweep` — a specimen chart of one sheet at ten stages of the same curl. Each pose carries a **bias** — how strongly that one sheet takes the deformation — so the top of a pile curls while the sheets pressed underneath lie flat, in the same draw call. The camera frames itself from the layout's own poses, so a wide `wall` and a deep `ring` both land without hand-tuning.
|
|
73
|
-
- **Stage mode** — paper as *architecture*: banners hung the height of a room along a walk you travel, with light coming through the paper from an opening at the far end. `<PaperStage text="…" />` builds the whole space out of a sentence — split a word to a banner and set down its drop — and binding `progress` to scroll makes the page scroll the walk. The room is real: a ceiling, poured floor slabs, columns with base plates, and a doorway the source shines through, all of it there because **architecture carries scale better than a figure does** (there is a walking figure, and it is off by default — the stage is navigable, so the person in the hall is the viewer). Every part of the scene — the arrangement, the camera, the light source — reads the same walk, so they cannot drift apart. Quality adapts to the machine on its own.
|
|
74
|
-
- **A stage you can walk** — stage mode is navigable, not a video. It drifts on its own until you touch it, then drag it (with inertia), wheel it, step banner to banner with the arrow keys, or click the paper you want to stand in front of. The stops come from the layout, so a step lands *on* a banner. `motion={{ capture: false }}` for a stage inside a scrolling page, so it never eats a reader's scroll; binding `progress` to page scroll still wins over all of it.
|
|
75
|
-
- **Lighting you can actually light with** — a preset is the starting point, not the ceiling. `light={{ exposure, film, key, color, direction, height, ambient, studio, haze }}` moves the lamp in the terms a person would say it in (degrees around the room, degrees above the horizon), and **studio** is the room itself as an environment map, which is what gives paper directional fill and something for its sheen to reflect. Overrides serialize as overrides, so a shared scene carries the two sliders you moved rather than a frozen copy of a rig you never touched.
|
|
76
|
-
- **Hardware that holds the paper up** — thread to the ceiling or a rod across the top edge, gripped by a clip or a peg. Every reference installation shows what suspends it, and a hung thing that shows what holds it stops reading as a rectangle that happens to float.
|
|
77
|
-
- **Presets** — everything serializes to `.paper` JSON validated by a zod schema. Diffable, forkable, shareable.
|
|
78
|
-
- **Agent-first export** — the editor's **Copy for AI** button produces a self-contained brief you paste into Claude Code (or any coding agent): install line, inlined component, placement contract, and a verification step the agent can self-check. See [AGENTS.md](https://github.com/NourMtir0722/Paperlab/blob/main/AGENTS.md).
|
|
79
|
-
- **Accessible by default** — `prefers-reduced-motion` freezes behaviors at their pose, a hidden DOM mirror carries the content for screen readers, and a flat DOM fallback renders when WebGL isn't available.
|
|
86
|
+
Everything below is rendered by the library itself and regenerated from the
|
|
87
|
+
registries — `pnpm media`, `pnpm shot:catalogue`, `pnpm sheet`. No mockups.
|
|
80
88
|
|
|
81
|
-
|
|
89
|
+
### Behaviors — 12
|
|
82
90
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
- **Behaviors** — `peel`, `unroll`, `flip`, `letter-fold`, `hang`, `fly`, `fall`, `carry`, `flight`, `crumple`, `settle`, `ribbon`: human-named params ("tightness", not "cylinderRadius") over a stack of pure geometry deformers. Each behavior nominates the two or three params that *are* it, so tools can lead with those. Draggable handles when `interactive`.
|
|
92
|
+
|
|
93
|
+
Underneath them are seven **deformers** — `roll`, `curl`, `bend`, `fold`, `wave`, `drape`, `crumple` — each a pure vertex mapping written twice: a JS implementation for the CPU/hero path and a GLSL twin for the GPU/field path. A 37-case golden-vector gate holds the two identical, and a separate test asserts each one actually draws a surface. All arc-length preserving, because paper does not stretch.
|
|
94
|
+
|
|
95
|
+
### Stocks — 7
|
|
96
|
+
|
|
97
|
+

|
|
98
|
+
|
|
99
|
+
One sheet of words, seven papers. Stock is not a colour swap: thermal takes on banding, newsprint takes grain, vellum goes translucent and lets the light through it. On top of stock sit composable surface effects — grain, torn deckle edges, crease lines, perforation, aging — as shader chunks. Alpha-affecting effects use `alphaTest` rather than blending, so shadows stay correct.
|
|
100
|
+
|
|
101
|
+
### Layouts — 12
|
|
102
|
+
|
|
103
|
+

|
|
104
|
+
|
|
105
|
+
Every layout names somewhere paper actually sits: `book` (pages splayed from a spine — `split: 0` makes it a swatch deck), `accordion` (one continuous concertina strip), `fan` (a hand of cards), `spread` (a stack slid sideways), `pile` (a heap on a desk), `rack` (prints stood in a row, leaning back), `wall` (a pinned studio wall), `spill` (a dropped stack mid-air), `colonnade` (banners along a walk, for stage mode), `ring`, `sheet`, and `sweep` — a specimen chart of one sheet at ten stages of the same curl.
|
|
106
|
+
|
|
107
|
+
Each pose also carries a **bias**: how strongly that one sheet takes the deformation. So the top of a pile curls while the sheets pressed underneath lie flat — in the same draw call. A whole field is **one instanced draw call** with the deformers running on the GPU, and the camera frames itself from the layout's own poses, so a wide `wall` and a deep `ring` both land without hand-tuning.
|
|
108
|
+
|
|
109
|
+

|
|
110
|
+
|
|
111
|
+
Twelve sheets, twelve peeled corners, **one draw call** — the deformers run on the GPU and the text curls with the mesh.
|
|
112
|
+
|
|
113
|
+
### Lighting — 8 rigs
|
|
114
|
+
|
|
115
|
+

|
|
116
|
+
|
|
117
|
+
A rig is the starting point, not the ceiling. `light={{ exposure, film, key, color, direction, height, ambient, studio, haze }}` moves the lamp in the terms a person would say it in — degrees around the room, degrees above the horizon — and **studio** is the room itself as an environment map, which is what gives paper directional fill and something for its sheen to reflect. `window` and `leaves` carry a gobo; `lightbox` puts the source behind the sheet and lets you read it through the paper.
|
|
118
|
+
|
|
119
|
+
Overrides serialize *as* overrides, so a shared scene carries the two sliders you moved rather than a frozen copy of a rig you never touched.
|
|
120
|
+
|
|
121
|
+
### Stage — paper as architecture
|
|
122
|
+
|
|
123
|
+

|
|
86
124
|
|
|
87
|
-
|
|
125
|
+
Banners hung the height of a room along a walk you travel, with light coming through the paper from an opening at the far end. `<PaperStage text="…" />` builds the whole space out of a sentence, and binding `progress` to scroll makes the page scroll the walk.
|
|
88
126
|
|
|
89
|
-
|
|
127
|
+
The room is real — a ceiling, poured floor slabs, columns with base plates, and a doorway the source shines through — because **architecture carries scale better than a figure does**. There is a walking figure, and it is off by default: the stage is navigable, so the person in the hall is the viewer.
|
|
128
|
+
|
|
129
|
+
**Six rooms ship**, and they are not variations on a theme — the walk changes shape, the paper changes proportion, the light changes where it comes from:
|
|
130
|
+
|
|
131
|
+

|
|
132
|
+
|
|
133
|
+
Every one of those is built from the same two things: sheets of paper, and words printed on them. No imagery, no textures from anywhere else.
|
|
134
|
+
|
|
135
|
+
**Four camera shots** frame any of them, each reading the same walk as the arrangement and the light, so they cannot drift apart:
|
|
136
|
+
|
|
137
|
+

|
|
138
|
+
|
|
139
|
+
And it is navigable rather than a video. It drifts on its own until you touch it, then drag it (with inertia), wheel it, step banner to banner with the arrow keys, or click the paper you want to stand in front of. The stops come from the layout, so a step lands *on* a banner. `motion={{ capture: false }}` for a stage inside a scrolling page, so it never eats a reader's scroll. Quality adapts to the machine on its own across four tiers.
|
|
140
|
+
|
|
141
|
+
### The rest
|
|
142
|
+
|
|
143
|
+
- **Content** — `blank`, `image`, `text`, `card` and `receipt`, any of which can also sit on the reverse of the sheet via `content.back`. Text is measured and wrapped with real tracking applied *before* measurement, so the painted line matches the line it was broken to.
|
|
144
|
+
- **Physics** — curated idle motion (`float`, `tumble`, `dangle`, `taped`, `breeze`) that composes with behaviors, and a verlet **cloth** mode: pin the top edge, add wind, grab the sheet and pull. Cloth and behaviors are mutually exclusive by schema — cloth owns the vertices.
|
|
145
|
+
- **Interaction states** — a preset can carry `states`: overrides-on-base diffs keyed `rest` / `hover` / `pressed` / `picked` / `placed`, with the triggers built in. Drag a stamp past its threshold and it tears off its sheet (the perforation edges facing its neighbours flip to torn), release it over a `<DropZone>` and it settles, release it anywhere else and it flutters home. The whole flow is reachable from the keyboard: focus a paper, Enter picks, arrows move between zones, Enter places, Escape returns it.
|
|
146
|
+
- **Hardware that holds the paper up** — thread to the ceiling or a rod across the top edge, gripped by a clip or a peg. A hung thing that shows what holds it stops reading as a rectangle that happens to float.
|
|
147
|
+
- **Presets** — 15 paper presets and 6 stage presets, and everything serializes to `.paper` JSON validated by a zod schema. Diffable, forkable, shareable.
|
|
148
|
+
- **Agent-first export** — the editor's **Copy for AI** button produces a self-contained brief you paste into a coding agent: install line, inlined component, placement contract, and a verification step the agent can self-check. See [AGENTS.md](https://github.com/NourMtir0722/Paperlab/blob/main/AGENTS.md) and [docs/llms.txt](https://github.com/NourMtir0722/Paperlab/blob/main/docs/llms.txt).
|
|
149
|
+
- **Accessible by default** — `prefers-reduced-motion` freezes behaviors at their pose and disables physics and entrances, a hidden DOM mirror carries the content for screen readers and find-in-page, and a flat DOM fallback renders when WebGL isn't available.
|
|
150
|
+
|
|
151
|
+
The zod schema in `config/schema.ts` is the single source of truth: it validates the API, generates the editor's controls, defines the file format and feeds the docs. If a feature can't serialize into a preset, it doesn't ship.
|
|
90
152
|
|
|
91
153
|
## Papers are made to be passed around
|
|
92
154
|
|
|
93
155
|
A paper is data — a `.paper` JSON object validated by a zod schema — so it travels without asking anyone's permission. **You do not need to fork this repo to share one.**
|
|
94
156
|
|
|
95
|
-
**Sending one.** Sculpt a paper in the [editor](https://nourmtir0722.github.io/Paperlab/editor/) and hit **Share**: you get a link with the whole paper packed into it. Anyone who opens that link lands in their own editor with your paper loaded and *editable* — a fork, not a read-only view.
|
|
157
|
+
**Sending one.** Sculpt a paper in the [editor](https://nourmtir0722.github.io/Paperlab/editor/) and hit **Share**: you get a link with the whole paper packed into it. Anyone who opens that link lands in their own editor with your paper loaded and *editable* — a fork, not a read-only view. (Uploaded images are too big for a URL; use the ⬇ download and send the `.paper` file instead.)
|
|
96
158
|
|
|
97
|
-
**Receiving one.** Open the link, or drag a `.paper` file onto the preset panel. Either way it lands in your library next to the built-ins
|
|
159
|
+
**Receiving one.** Open the link, or drag a `.paper` file onto the preset panel. Either way it lands in your library next to the built-ins.
|
|
98
160
|
|
|
99
|
-
**Using one in your project.** A `.paper` file is a preset object, so it goes straight in:
|
|
161
|
+
**Using one in your project.** A `.paper` file *is* a preset object, so it goes straight in:
|
|
100
162
|
|
|
101
163
|
```tsx
|
|
102
164
|
import alice from './alice-note.paper.json'
|
|
@@ -115,27 +177,74 @@ registerPreset('alice-note', alice)
|
|
|
115
177
|
|
|
116
178
|
That's the whole loop: **make → send → remix → ship.** If you'd rather your paper shipped *with* the library so everyone gets it by name, that's the first rung of [CONTRIBUTING.md](https://github.com/NourMtir0722/Paperlab/blob/main/CONTRIBUTING.md) — a preset PR is JSON and no code.
|
|
117
179
|
|
|
118
|
-
##
|
|
180
|
+
## The apps
|
|
181
|
+
|
|
182
|
+
Three surfaces ship alongside the library, all built on its public API only.
|
|
183
|
+
|
|
184
|
+
**[The playground](https://nourmtir0722.github.io/Paperlab/)** — one input, one scene, shareable by link. Type a sentence and it builds you a room out of it. Built for a phone.
|
|
185
|
+
|
|
186
|
+
**[The editor](https://nourmtir0722.github.io/Paperlab/editor/)** — a three-rail canvas tool: presets on the left, sculpt on canvas, inspector on the right, transport at the bottom (space = play/pause), undo and redo on ⌘Z.
|
|
187
|
+
|
|
188
|
+

|
|
189
|
+
|
|
190
|
+
The inspector is generated from the zod schema, so it can never drift from the API. Each behavior nominates the two or three params that *are* it — `unroll` opens on progress and tightness, and folds sheet, stock, content, surface, physics and scene away behind their own headings. Labels drag to scrub: full range in ~300px, shift for a 4× finer pass, click the readout to type an exact value.
|
|
191
|
+
|
|
192
|
+

|
|
193
|
+
|
|
194
|
+
Field mode composes galleries against the same panel — swap the layout, watch fourteen papers rearrange in one draw call. **Export code** ends the session in your codebase, and **Copy for AI** ends it in a coding agent's. It wants a real screen: under about 900px it says so and points you at the playground.
|
|
195
|
+
|
|
196
|
+
**[The reference](https://nourmtir0722.github.io/Paperlab/docs/)** — the whole catalogue with every behavior, deformer, layout, stock and surface rendering live. The catalogue is generated from the registries, so it cannot advertise something the library doesn't have.
|
|
197
|
+
|
|
198
|
+
## Development
|
|
199
|
+
|
|
200
|
+
pnpm + Turborepo, Node 22, [Biome](https://biomejs.dev) for lint and format.
|
|
201
|
+
|
|
202
|
+
```sh
|
|
203
|
+
pnpm install
|
|
204
|
+
pnpm dev # the editor at localhost:5173
|
|
205
|
+
```
|
|
119
206
|
|
|
120
207
|
| | |
|
|
121
208
|
|---|---|
|
|
122
|
-
| [`packages/paperlab`](https://github.com/NourMtir0722/Paperlab/blob/main/packages/paperlab/) | the npm library |
|
|
209
|
+
| [`packages/paperlab`](https://github.com/NourMtir0722/Paperlab/blob/main/packages/paperlab/) | the npm library — the only published artifact |
|
|
123
210
|
| [`apps/editor`](https://github.com/NourMtir0722/Paperlab/blob/main/apps/editor/) | the editor — every knob, and the export |
|
|
124
211
|
| [`apps/playground`](https://github.com/NourMtir0722/Paperlab/blob/main/apps/playground/) | the playground — one input, one scene, shareable by link |
|
|
125
|
-
| [`apps/docs`](https://github.com/NourMtir0722/Paperlab/blob/main/apps/docs/) | the
|
|
126
|
-
| [`tools/`](https://github.com/NourMtir0722/Paperlab/blob/main/tools/) |
|
|
127
|
-
| [`docs/llms.txt`](https://github.com/NourMtir0722/Paperlab/blob/main/docs/llms.txt) | the agent-readable API reference |
|
|
128
|
-
| [`docs/design.md`](https://github.com/NourMtir0722/Paperlab/blob/main/docs/design.md) | the design
|
|
129
|
-
|
|
212
|
+
| [`apps/docs`](https://github.com/NourMtir0722/Paperlab/blob/main/apps/docs/) | the reference site, with every behavior running live |
|
|
213
|
+
| [`tools/`](https://github.com/NourMtir0722/Paperlab/blob/main/tools/) | browser harnesses — parity, perf, screenshots, the README's motion |
|
|
214
|
+
| [`AGENTS.md`](https://github.com/NourMtir0722/Paperlab/blob/main/AGENTS.md) · [`docs/llms.txt`](https://github.com/NourMtir0722/Paperlab/blob/main/docs/llms.txt) | the agent-readable API reference |
|
|
215
|
+
| [`docs/design.md`](https://github.com/NourMtir0722/Paperlab/blob/main/docs/design.md) | the design language the three apps share, enforced by a test |
|
|
216
|
+
|
|
217
|
+
### Checks
|
|
130
218
|
|
|
131
219
|
```sh
|
|
132
|
-
pnpm test # unit
|
|
133
|
-
pnpm test:parity #
|
|
134
|
-
pnpm test:drive # the stage really walks when you drag it
|
|
220
|
+
pnpm test # 700+ unit tests — deformer math, schema, cloth, layouts, exports
|
|
221
|
+
pnpm test:parity # 37 golden-vector cases: every deformer's GLSL twin vs its JS twin
|
|
222
|
+
pnpm test:drive # the stage really walks when you drag, wheel or arrow it
|
|
135
223
|
pnpm test:share # sculpt → link → a browser that has never seen the paper
|
|
224
|
+
pnpm typecheck
|
|
225
|
+
pnpm lint
|
|
226
|
+
pnpm knip # dead code and unused exports
|
|
136
227
|
pnpm build
|
|
137
228
|
```
|
|
138
229
|
|
|
139
|
-
|
|
230
|
+
Anything that needs a real GPU, real pointer events or a second browser profile is a browser harness in `tools/` rather than a unit test. All four test commands are CI gates, along with `publint` and `are-the-types-wrong` on the published package.
|
|
231
|
+
|
|
232
|
+
### Measurement
|
|
233
|
+
|
|
234
|
+
```sh
|
|
235
|
+
pnpm perf # stage frame cost (--gpu for the platform GPU, --soft for the SwiftShader floor)
|
|
236
|
+
pnpm perf:field # field frame cost
|
|
237
|
+
pnpm shot # stage PNGs into .shots/ (also shot:ui, shot:play)
|
|
238
|
+
pnpm shot:light # every lighting rig, one frame each
|
|
239
|
+
pnpm shot:catalogue # sweep any axis — --vary=layout|stock|lighting --all
|
|
240
|
+
pnpm sheet # compose those frames into a labelled contact sheet
|
|
241
|
+
pnpm media # the README's GIFs and MP4s, stepped frame-exact (needs ffmpeg)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Contributing
|
|
245
|
+
|
|
246
|
+
Contributions climb a ladder, easiest first: **presets** (JSON only, zero code) → **behaviors** (~50 lines over existing deformers) → **layouts** (a ~30-line pure function) → **deformers** (dual JS + GLSL implementation with parity cases) → **surface effects** (GLSL chunks). Merged work ships in the editor's library with attribution. See [CONTRIBUTING.md](https://github.com/NourMtir0722/Paperlab/blob/main/CONTRIBUTING.md).
|
|
247
|
+
|
|
248
|
+
## License
|
|
140
249
|
|
|
141
|
-
Apache 2.0 © Noor Mtir
|
|
250
|
+
Apache 2.0 © Noor Mtir. Attribution travels with the code: redistributions must reproduce the [NOTICE](https://github.com/NourMtir0722/Paperlab/blob/main/NOTICE) file. If Paperlab made it into something you shipped, a visible credit or link back is warmly appreciated.
|
|
@@ -1150,7 +1150,7 @@ var paperConfigSchema = z14.object({
|
|
|
1150
1150
|
physics: physicsSchema.default("none"),
|
|
1151
1151
|
scene: sceneSchema.default({}),
|
|
1152
1152
|
onTwos: z14.boolean().default(false),
|
|
1153
|
-
/** Interaction state machine — overrides-on-base diffs
|
|
1153
|
+
/** Interaction state machine — overrides-on-base diffs. */
|
|
1154
1154
|
states: paperStatesSchema.optional()
|
|
1155
1155
|
}).superRefine((config, ctx) => {
|
|
1156
1156
|
if (typeof config.physics === "object" && (config.behavior || config.deformers)) {
|
|
@@ -1438,7 +1438,7 @@ var builtins = {
|
|
|
1438
1438
|
meta: { name: "Blank sheet", tags: ["starter"] },
|
|
1439
1439
|
stock: "printer"
|
|
1440
1440
|
},
|
|
1441
|
-
// The
|
|
1441
|
+
// The driving use case: one stamp of the 2×5 block. Hover peels the
|
|
1442
1442
|
// outward-facing corner ('auto' resolves per sheet slot), pressing deepens
|
|
1443
1443
|
// the peel; the perforation tears when it detaches (field auto-wiring).
|
|
1444
1444
|
"postage-stamp": {
|
|
@@ -3163,7 +3163,7 @@ void plPerforation(inout vec4 color) {
|
|
|
3163
3163
|
float cell = mod(a + uPerfSpacing * 0.5, uPerfSpacing) - uPerfSpacing * 0.5;
|
|
3164
3164
|
if (uPerfTorn[e] < 0.5) {
|
|
3165
3165
|
// Intact: clean semicircular punches on the edge line (alphaTest, not
|
|
3166
|
-
// blending \u2014 shadow correctness
|
|
3166
|
+
// blending \u2014 shadow correctness).
|
|
3167
3167
|
if (length(vec2(cell, d)) < uPerfRadius) color.a = 0.0;
|
|
3168
3168
|
} else {
|
|
3169
3169
|
// Torn: ripped profile following the hole rhythm \u2014 alternating tabs and
|
|
@@ -7032,4 +7032,4 @@ export {
|
|
|
7032
7032
|
describeConfig,
|
|
7033
7033
|
buildAgentPayload
|
|
7034
7034
|
};
|
|
7035
|
-
//# sourceMappingURL=chunk-
|
|
7035
|
+
//# sourceMappingURL=chunk-XE4E43MD.js.map
|