@oeave/bakery3 0.0.0-stage → 0.1.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.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +562 -2
  3. package/dist/animation-B1h0Ryvj.d.ts +97 -0
  4. package/dist/bake/index.d.ts +369 -0
  5. package/dist/bake/index.js +9 -0
  6. package/dist/bake/index.js.map +1 -0
  7. package/dist/bake-DaOlenyx.d.ts +1357 -0
  8. package/dist/catalog/index.d.ts +100 -0
  9. package/dist/catalog/index.js +154 -0
  10. package/dist/catalog/index.js.map +1 -0
  11. package/dist/catalog.gen-BM-aNf7n.d.ts +733 -0
  12. package/dist/chunk-25ZXSVQ3.js +1107 -0
  13. package/dist/chunk-25ZXSVQ3.js.map +1 -0
  14. package/dist/chunk-3G5QL4F4.js +145 -0
  15. package/dist/chunk-3G5QL4F4.js.map +1 -0
  16. package/dist/chunk-4E5VV4QY.js +12 -0
  17. package/dist/chunk-4E5VV4QY.js.map +1 -0
  18. package/dist/chunk-54WJDN5R.js +3286 -0
  19. package/dist/chunk-54WJDN5R.js.map +1 -0
  20. package/dist/chunk-6NYR73Y7.js +832 -0
  21. package/dist/chunk-6NYR73Y7.js.map +1 -0
  22. package/dist/chunk-6YVQANMW.js +1463 -0
  23. package/dist/chunk-6YVQANMW.js.map +1 -0
  24. package/dist/chunk-AF5BIELI.js +70 -0
  25. package/dist/chunk-AF5BIELI.js.map +1 -0
  26. package/dist/chunk-APBOWWSV.js +92 -0
  27. package/dist/chunk-APBOWWSV.js.map +1 -0
  28. package/dist/chunk-ARMTTZXZ.js +1157 -0
  29. package/dist/chunk-ARMTTZXZ.js.map +1 -0
  30. package/dist/chunk-CX4WTVGH.js +4492 -0
  31. package/dist/chunk-CX4WTVGH.js.map +1 -0
  32. package/dist/chunk-HQTUFVMM.js +1412 -0
  33. package/dist/chunk-HQTUFVMM.js.map +1 -0
  34. package/dist/chunk-MD2U6S4N.js +1952 -0
  35. package/dist/chunk-MD2U6S4N.js.map +1 -0
  36. package/dist/chunk-S4AJTLLN.js +23 -0
  37. package/dist/chunk-S4AJTLLN.js.map +1 -0
  38. package/dist/chunk-TGFNQBKC.js +551 -0
  39. package/dist/chunk-TGFNQBKC.js.map +1 -0
  40. package/dist/chunk-V3QINEEG.js +87 -0
  41. package/dist/chunk-V3QINEEG.js.map +1 -0
  42. package/dist/chunk-W6WQ5UP7.js +91 -0
  43. package/dist/chunk-W6WQ5UP7.js.map +1 -0
  44. package/dist/chunk-WBIAFIHV.js +346 -0
  45. package/dist/chunk-WBIAFIHV.js.map +1 -0
  46. package/dist/chunk-ZEBVAIJJ.js +156 -0
  47. package/dist/chunk-ZEBVAIJJ.js.map +1 -0
  48. package/dist/devtools/index.d.ts +536 -0
  49. package/dist/devtools/index.js +15 -0
  50. package/dist/devtools/index.js.map +1 -0
  51. package/dist/environments/index.d.ts +93 -0
  52. package/dist/environments/index.js +382 -0
  53. package/dist/environments/index.js.map +1 -0
  54. package/dist/hotspots/index.d.ts +111 -0
  55. package/dist/hotspots/index.js +285 -0
  56. package/dist/hotspots/index.js.map +1 -0
  57. package/dist/index.d.ts +944 -0
  58. package/dist/index.js +15 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/node/index.cjs +5115 -0
  61. package/dist/node/index.cjs.map +1 -0
  62. package/dist/node/index.d.cts +4862 -0
  63. package/dist/node/index.d.ts +576 -0
  64. package/dist/node/index.js +1279 -0
  65. package/dist/node/index.js.map +1 -0
  66. package/dist/prepare-C5qVvaw4.d.ts +112 -0
  67. package/dist/presets/index.d.ts +526 -0
  68. package/dist/presets/index.js +14 -0
  69. package/dist/presets/index.js.map +1 -0
  70. package/dist/r3f/index.d.ts +159 -0
  71. package/dist/r3f/index.js +592 -0
  72. package/dist/r3f/index.js.map +1 -0
  73. package/dist/room-S5TLCK4F.js +9 -0
  74. package/dist/room-S5TLCK4F.js.map +1 -0
  75. package/dist/rooms.gen-vhj6sFW5.d.ts +1434 -0
  76. package/dist/session-YCQS4MRH.js +9 -0
  77. package/dist/session-YCQS4MRH.js.map +1 -0
  78. package/dist/shapes/index.d.ts +222 -0
  79. package/dist/shapes/index.js +836 -0
  80. package/dist/shapes/index.js.map +1 -0
  81. package/dist/testRun-BIPXaVPb.d.ts +1130 -0
  82. package/dist/timeline-ChwgD7bT.d.ts +470 -0
  83. package/dist/tsl/index.d.ts +165 -0
  84. package/dist/tsl/index.js +310 -0
  85. package/dist/tsl/index.js.map +1 -0
  86. package/package.json +170 -4
@@ -0,0 +1,536 @@
1
+ import { Object3D, Scene, Camera } from 'three';
2
+ import { a as BakeQuality, B as BakeTextureSize, e as BakeSpec, j as BakeBundle, Q as QualityPreset } from '../bake-DaOlenyx.js';
3
+ import { CapturedScene, Bakery3, RenderOptions, RenderJob, CaptureOptions } from '../index.js';
4
+ import { T as Timeline } from '../timeline-ChwgD7bT.js';
5
+ import { BakeSession } from '../bake/index.js';
6
+ import '../animation-B1h0Ryvj.js';
7
+ import '../testRun-BIPXaVPb.js';
8
+ import '../prepare-C5qVvaw4.js';
9
+
10
+ /**
11
+ * The timeline panel: a floating strip, not an editor.
12
+ *
13
+ * The developer orbits their product with the mouse, presses +, scrubs,
14
+ * presses + again, and presses Render video, without leaving the page. It is
15
+ * vanilla DOM and imports nothing but the timeline itself, so a plain three.js
16
+ * app and, through the thin wrapper in r3f/, a React app mount exactly the
17
+ * same panel.
18
+ *
19
+ * import { mountTimelinePanel } from '@oeave/bakery3/devtools';
20
+ *
21
+ * const panel = mountTimelinePanel({
22
+ * timeline,
23
+ * targets: [{ object: hero, label: 'hero' }],
24
+ * onPlaybackChange: (active) => { controls.enabled = !active; },
25
+ * onRenderVideo: (tl) => renderVideo(tl),
26
+ * });
27
+ *
28
+ * `onPlaybackChange` is the one that matters. Orbit controls rewrite the
29
+ * camera every frame, so a timeline that owns the camera and controls that
30
+ * also own it fight, and the developer sees a stuttering preview. The host
31
+ * suspends them; capture still reads the camera the developer framed with the
32
+ * controls.
33
+ *
34
+ * `buildTimelineBody` is the same UI without the shell, because the dev panel
35
+ * (./devPanel.ts) shows it as its Video tab. `mountTimelinePanel` is the
36
+ * floating shell around that body.
37
+ */
38
+
39
+ /** An object the developer can add a lane for. The label is what the picker
40
+ * shows: a three `name` is often empty or generated, and "Mesh" three times
41
+ * in a dropdown is not a choice anybody can make. */
42
+ type TimelinePanelTarget = {
43
+ object: Object3D;
44
+ label: string;
45
+ };
46
+ /** What the host supplies. Only `timeline` is required; the Render-video
47
+ * button appears only when there is something for it to call. */
48
+ type TimelinePanelOptions = {
49
+ timeline: Timeline;
50
+ /** Objects offered in the "add track" picker. The camera is always there. */
51
+ targets?: TimelinePanelTarget[];
52
+ /** Shows the "Render video" button when given. */
53
+ onRenderVideo?: (timeline: Timeline) => void | Promise<void>;
54
+ /** Fired when the timeline takes the camera, and when it gives it back. */
55
+ onPlaybackChange?: (active: boolean) => void;
56
+ container?: HTMLElement;
57
+ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
58
+ defaultOpen?: boolean;
59
+ };
60
+ /** The handle a host keeps. Small on purpose: the panel reads everything else
61
+ * it needs off the timeline and its events, so there is no second copy of the
62
+ * state to keep in sync. */
63
+ type TimelinePanel = {
64
+ /** Rebuild the lanes, after loading a different scene, say. */
65
+ refresh(): void;
66
+ /** One line under the transport: "tracing frame 12/48". */
67
+ setStatus(message: string, tone?: 'info' | 'error'): void;
68
+ dispose(): void;
69
+ };
70
+ /**
71
+ * Attach the panel to the page.
72
+ *
73
+ * It appends one fixed-position element to `container` (the body by default)
74
+ * and owns nothing else: no globals, no stylesheet, no listener outside its
75
+ * own subtree and the timeline's events. `dispose()` puts the page back
76
+ * exactly as it was, which is what makes it safe to mount from a hot-reloading
77
+ * dev server.
78
+ */
79
+ declare function mountTimelinePanel(options: TimelinePanelOptions): TimelinePanel;
80
+
81
+ /**
82
+ * The dev panel: one floating thing that renders, films and bakes.
83
+ *
84
+ * A 48px mark in the corner of the developer's own app, which opens into a
85
+ * card with up to three tabs and a price. Four things about it are contracts
86
+ * and the rest is layout:
87
+ *
88
+ * 1. The cost is visible before the money is spent. Beside every primary
89
+ * button is a quiet line, `98% compatible ≈ $0.12`: hover the price for
90
+ * what it is made of (`96 frames × $0.039`, at most $5.00), click the
91
+ * figure for the objects that render differently. The price refreshes
92
+ * when the settings change, when the tab changes, and when the scene
93
+ * changes; never on a clock, so a price that has not moved is not fetched
94
+ * again. A key or a balance that will refuse the render is said in the
95
+ * status line then, not when Render is pressed.
96
+ * 2. Every refusal is in place. An object that cannot be baked appears in the
97
+ * list with a ⚠ and its reason and fix on hover, next to the ones that
98
+ * can. Nothing is silently missing.
99
+ * 3. Hold to compare. Pointer down shows the live scene, pointer up puts the
100
+ * bake back, both within a frame and both lossless, because the session
101
+ * keeps the patch and just switches it off.
102
+ * 4. The lock explains itself. Downloads are the one gated action, and the
103
+ * locked state says what kind of key unlocks it and where to put it.
104
+ *
105
+ * A tab only exists if its options were supplied. A vanilla three app with
106
+ * no timeline gets a two-tab panel, not a Video tab that does nothing.
107
+ *
108
+ * import { mountDevPanel } from '@oeave/bakery3/devtools';
109
+ *
110
+ * const panel = mountDevPanel({
111
+ * estimate: (request) => post('/api/estimate', request),
112
+ * render: { onRender: (settings) => runRender(settings) },
113
+ * bake: { session, onBake: (settings) => runBake(settings) },
114
+ * });
115
+ */
116
+
117
+ type DevPanelTab = 'render' | 'video' | 'bake';
118
+ /** What the panel asks the host to price. */
119
+ type DevPanelEstimateRequest = {
120
+ kind: 'render';
121
+ quality: string;
122
+ width: number;
123
+ height: number;
124
+ } | {
125
+ kind: 'video';
126
+ quality: string;
127
+ width: number;
128
+ height: number;
129
+ frames: number;
130
+ fps: number;
131
+ format: 'mp4' | 'webm';
132
+ } | {
133
+ kind: 'bake';
134
+ quality: BakeQuality;
135
+ textureSize: number;
136
+ objects: number;
137
+ texels: number;
138
+ /** The ticked objects, as objectIds. */
139
+ include: string[];
140
+ };
141
+ type DevPanelEstimate = {
142
+ /** The price beside the button. */
143
+ usd: number;
144
+ /** The most it can be charged: the breakdown's `at most` line. */
145
+ maximum?: number;
146
+ /**
147
+ * A line of your own under the breakdown, which already lists the
148
+ * settings, the count the price is made of (`96 frames × $0.039`) and the
149
+ * maximum: `on the local worker`.
150
+ */
151
+ detail?: string;
152
+ /** False when the price is exact rather than an estimate: no `≈`. */
153
+ approximate?: boolean;
154
+ };
155
+ /** One past artifact, as the host's history lists it. */
156
+ type DevPanelHistoryEntry = {
157
+ id: string;
158
+ kind: DevPanelTab;
159
+ createdAt: string;
160
+ /** When `artifact` and `thumbnail` stop working (signed URLs do). The
161
+ * panel's own history drops an entry then. */
162
+ expiresAt?: string;
163
+ cacheKey?: string;
164
+ cached?: boolean;
165
+ meta?: {
166
+ width?: number;
167
+ height?: number;
168
+ format?: string;
169
+ frames?: number;
170
+ fps?: number;
171
+ quality?: string;
172
+ textureSize?: number;
173
+ objects?: number;
174
+ skipped?: number;
175
+ /** What it was charged, in dollars. */
176
+ cost?: number;
177
+ };
178
+ thumbnail?: string;
179
+ artifact?: string;
180
+ };
181
+ /** What a finished action gives the panel to show. `entry` is what makes the
182
+ * result's Download button real: downloads go through the same gated path
183
+ * as history, never round an alternative route. */
184
+ type DevPanelActionResult = {
185
+ url?: string;
186
+ meta?: string;
187
+ isVideo?: boolean;
188
+ entry?: DevPanelHistoryEntry;
189
+ /** The job's id, so the panel's own history does not list it twice. */
190
+ id?: string;
191
+ /** The output's size: the result area holds the image's shape before the
192
+ * image has loaded, so nothing below it moves when it does. */
193
+ width?: number;
194
+ height?: number;
195
+ quality?: string;
196
+ format?: string;
197
+ frames?: number;
198
+ /** Dollars charged, for the history line. */
199
+ cost?: number;
200
+ /** Millisecond epoch at which `url` stops working. */
201
+ expiresAt?: number;
202
+ };
203
+ type DevPanelRenderSettings = {
204
+ quality: string;
205
+ width: number;
206
+ height: number;
207
+ };
208
+ type DevPanelRenderOptions = {
209
+ onRender: (settings: DevPanelRenderSettings) => void | DevPanelActionResult | Promise<void | DevPanelActionResult>;
210
+ qualities?: string[];
211
+ /** Long edges to offer. The short edge follows `aspect`. */
212
+ sizes?: number[];
213
+ /**
214
+ * Width ÷ height of the frame the host renders: 4/3 for a 640×480 room
215
+ * shot, or a function that reads the camera, so a wide viewport renders
216
+ * wide. Read each time the panel prices or renders. Defaults to 1, a
217
+ * square.
218
+ */
219
+ aspect?: number | (() => number);
220
+ defaultQuality?: string;
221
+ defaultSize?: number;
222
+ };
223
+ type DevPanelVideoSettings = DevPanelRenderSettings & {
224
+ format: 'mp4' | 'webm';
225
+ timeline: Timeline;
226
+ };
227
+ type DevPanelVideoOptions = {
228
+ timeline: Timeline;
229
+ targets?: TimelinePanelTarget[];
230
+ /**
231
+ * The live scene, read whenever `sceneVersion` reports a change.
232
+ *
233
+ * Supplying it lets the panel re-attach tracks whose objects were rebuilt
234
+ * (a variant swap, an r3f remount) instead of letting the timeline refuse at
235
+ * export. Without it the panel still works; stale tracks just survive until
236
+ * something else catches them.
237
+ */
238
+ scene?: () => Object3D | undefined;
239
+ /** Passed straight through to the timeline body: the orbit-controls
240
+ * handshake that keeps the camera from being driven twice. */
241
+ onPlaybackChange?: (active: boolean) => void;
242
+ onRenderVideo: (settings: DevPanelVideoSettings) => void | DevPanelActionResult | Promise<void | DevPanelActionResult>;
243
+ qualities?: string[];
244
+ /** Long edges to offer. The short edge follows `aspect`. */
245
+ sizes?: number[];
246
+ /** As the Render tab's. Video sizes are rounded to even numbers, which is
247
+ * what a video encoder takes. */
248
+ aspect?: number | (() => number);
249
+ formats?: Array<'mp4' | 'webm'>;
250
+ defaultQuality?: string;
251
+ defaultSize?: number;
252
+ defaultFormat?: 'mp4' | 'webm';
253
+ };
254
+ type DevPanelBakeSettings = {
255
+ quality: BakeQuality;
256
+ textureSize: BakeTextureSize;
257
+ /** The ticked boxes, as objectIds. */
258
+ include: string[];
259
+ };
260
+ type DevPanelBakeResult = {
261
+ /** Index of the generation the host added, when it added one. The panel
262
+ * reads the session for everything else. */
263
+ generation?: number;
264
+ entry?: DevPanelHistoryEntry;
265
+ };
266
+ type DevPanelBakeOptions = {
267
+ session: BakeSession;
268
+ onBake: (settings: DevPanelBakeSettings) => void | DevPanelBakeResult | Promise<void | DevPanelBakeResult>;
269
+ qualities?: BakeQuality[];
270
+ sizes?: BakeTextureSize[];
271
+ defaultQuality?: BakeQuality;
272
+ defaultSize?: BakeTextureSize;
273
+ };
274
+ /** The shape of `bakery3.check()`, structurally, so the panel does not have
275
+ * to import the compatibility analyzer to display its verdict. */
276
+ type DevPanelCompatibility = {
277
+ score: number;
278
+ issues: Array<{
279
+ severity: string;
280
+ message: string;
281
+ /** `<Car>/<Body>/<Paint>`: the panel lists the issue under `Paint`. */
282
+ path?: string;
283
+ /** Linked beside the issue. */
284
+ docs?: string;
285
+ }>;
286
+ };
287
+ type DevPanelHistoryOptions = {
288
+ list: () => Promise<DevPanelHistoryEntry[]>;
289
+ /** Omit it and no download buttons are drawn at all, which is better than
290
+ * a button that cannot work. */
291
+ download?: (entry: DevPanelHistoryEntry, part?: 'glb') => void | Promise<void>;
292
+ };
293
+ /**
294
+ * A render the panel shows in its jobs strip: what is tracing now and what
295
+ * waits behind it. A job in a terminal state is not drawn; what finished is
296
+ * the Recent list's to show. States use the protocol's RenderState names
297
+ * (`queued`, `preparing`, `rendering_preview`, `rendering`, `completed`,
298
+ * `failed`, `cancelled`) so an SDK job and a host's own queue read alike.
299
+ */
300
+ type DevPanelJob = {
301
+ id: string;
302
+ label: string;
303
+ state: string;
304
+ /** Overall, 0–1. Omit for an honest indeterminate. */
305
+ progress?: number;
306
+ /** The renderer's own words: `48% of samples`, `frame 12/48`. */
307
+ detail?: string;
308
+ /** The low-sample preview, the moment it exists: the row's thumbnail
309
+ * until `resultUrl` replaces it. */
310
+ previewUrl?: string;
311
+ resultUrl?: string;
312
+ seconds?: number;
313
+ /** Drawn as × on a job that is not finished. */
314
+ onCancel?: () => void;
315
+ };
316
+ /** What a tracked job's listeners receive: the union of RenderJob's payloads,
317
+ * loosely, so the panel can read them without importing the client. */
318
+ type TrackedJobPayload = {
319
+ percent?: number;
320
+ state?: string;
321
+ url?: string;
322
+ result?: {
323
+ url: string;
324
+ width?: number;
325
+ height?: number;
326
+ format?: string;
327
+ cost?: number;
328
+ expiresAt?: number;
329
+ };
330
+ error?: {
331
+ message?: string;
332
+ };
333
+ /** Every RenderJob payload carries the record; the strip does not read it,
334
+ * but naming it keeps this an honest structural match for all five events. */
335
+ render?: unknown;
336
+ };
337
+ /**
338
+ * What `trackJob` needs from an SDK `RenderJob`, structurally, so the panel
339
+ * never imports the client and any object with these members works.
340
+ */
341
+ type TrackableJob = {
342
+ id: string;
343
+ status: string;
344
+ progress: number;
345
+ previewUrl?: string;
346
+ on(event: 'progress' | 'preview' | 'complete' | 'failed' | 'cancelled', listener: (payload: TrackedJobPayload) => void): () => void;
347
+ cancel?(): void | Promise<void>;
348
+ };
349
+ type DevPanelOptions = {
350
+ container?: HTMLElement;
351
+ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
352
+ defaultOpen?: boolean;
353
+ defaultTab?: DevPanelTab;
354
+ /** A name beside the mark in the header, for a host with a panel of its
355
+ * own to tell apart. None by default: the mark says whose panel it is. */
356
+ title?: string;
357
+ /** Fired when the FAB is opened or the card collapsed. A host that
358
+ * remounts the panel (switching scenes, say) uses it to come back open. */
359
+ onOpenChange?: (open: boolean) => void;
360
+ /**
361
+ * A cheap fingerprint of what is in the scene: its objects, which of them
362
+ * are visible, their geometry and materials. When it changes, the price,
363
+ * the Bake tab's object list and the compatibility verdict follow, because
364
+ * a developer who just added a wall should not be looking at the price of
365
+ * the room without it.
366
+ *
367
+ * three.js has no event for most of that (a swapped geometry, a hidden
368
+ * mesh), so the panel compares this on animation frames a few times a
369
+ * second while it is open, and never while the tab is in the background.
370
+ * Keep it to a walk over the scene; it can be as approximate as you like.
371
+ */
372
+ sceneVersion?: () => string | number;
373
+ /**
374
+ * A cheap fingerprint of where things are: the camera's position, and the
375
+ * objects' if they move. How walled in the camera is moves a price, so a
376
+ * camera carried into a room is priced again, once it has come to rest. A
377
+ * view that never rests (an auto-rotating orbit, a scene that animates)
378
+ * keeps the price it had; `sceneVersion` still re-prices what is added to
379
+ * it meanwhile.
380
+ */
381
+ viewVersion?: () => string | number;
382
+ estimate?: (request: DevPanelEstimateRequest) => Promise<DevPanelEstimate | null> | DevPanelEstimate | null;
383
+ /**
384
+ * The free, local, offline compatibility check, shown beside the button as
385
+ * `98% compatible`, which opens the issues object by object. Read on open,
386
+ * when the scene changes and on every `refresh()`; returning `null` hides
387
+ * it.
388
+ */
389
+ compatibility?: () => DevPanelCompatibility | null;
390
+ history?: DevPanelHistoryOptions;
391
+ render?: DevPanelRenderOptions;
392
+ video?: DevPanelVideoOptions;
393
+ bake?: DevPanelBakeOptions;
394
+ };
395
+ type DevPanel = {
396
+ open(): void;
397
+ close(): void;
398
+ setTab(tab: DevPanelTab): void;
399
+ /** Re-read candidates, generations and history and repaint. */
400
+ refresh(): void;
401
+ /** Ask for a fresh estimate now. */
402
+ refreshCost(): void;
403
+ setStatus(message: string, tone?: 'info' | 'error' | 'ok'): void;
404
+ /** The host drives per-object progress:
405
+ * `setProgress('Baking pedestal · 3/12', 3/12)`. `null` clears it. */
406
+ setProgress(text: string | null, fraction?: number): void;
407
+ /**
408
+ * The jobs strip, replaced wholesale. A host with its own queue (a local
409
+ * worker, a batch script) reports what is tracing now and what waits
410
+ * behind it. Finished jobs are dropped from the strip; with no active job
411
+ * it hides.
412
+ */
413
+ setJobs(jobs: DevPanelJob[]): void;
414
+ /**
415
+ * Follow an SDK render job into the strip (progress, the preview the
416
+ * moment it lands) with nothing to translate on the host's side. The row
417
+ * leaves the strip when the job ends; the untrack function detaches the
418
+ * listeners.
419
+ */
420
+ trackJob(job: TrackableJob, label?: string): () => void;
421
+ /**
422
+ * A row in the jobs strip from the moment an action starts, before there
423
+ * is a job to follow: `update()` says what is happening (capturing the
424
+ * scene, uploading), `follow()` hands the same row to the job once it
425
+ * exists, and `end()` takes it away. One row from the click to the result,
426
+ * where it would otherwise be a bar that vanishes and a row that appears.
427
+ */
428
+ startJob(label: string): DevPanelJobHandle;
429
+ dispose(): void;
430
+ };
431
+ /** What `startJob` returns. See there. */
432
+ type DevPanelJobHandle = {
433
+ /** `state` names a step after the job itself is done (`applying`), which
434
+ * keeps the row up while the action finishes. */
435
+ update(detail: string, progress?: number, state?: string): void;
436
+ follow(job: TrackableJob): void;
437
+ end(): void;
438
+ };
439
+ /**
440
+ * Mount the panel. Appends one fixed-position element to `container` (the
441
+ * body by default) and owns nothing else; `dispose()` puts the page back.
442
+ */
443
+ declare function mountDevPanel(options: DevPanelOptions): DevPanel;
444
+
445
+ /**
446
+ * `mountBakery3Devtools`: the panel, wired to your client, in one call.
447
+ *
448
+ * `mountDevPanel` underneath it takes callbacks and knows nothing about an
449
+ * account. This function supplies those callbacks from a `createBakery3()`
450
+ * client, so the estimate, render, video and bake plumbing is written once
451
+ * for a vanilla three.js app and for React alike (`<Bakery3Devtools />` is a
452
+ * shell over this same function, see ../r3f/Devtools.tsx).
453
+ *
454
+ * import { createBakery3 } from '@oeave/bakery3';
455
+ * import { mountBakery3Devtools } from '@oeave/bakery3/devtools';
456
+ *
457
+ * const bakery3 = createBakery3({ token: mintToken });
458
+ * const panel = mountBakery3Devtools({ bakery3, scene, camera, renderer });
459
+ *
460
+ * By default the Bake tab goes through `bakery3.bake()`: the same capture,
461
+ * upload and `/v1/bakes` round trip a render takes, with the bundle's
462
+ * textures coming back on signed URLs. A `bakeTransport` replaces that with
463
+ * a function of your own that turns a `BakeSpec` into a `BakeBundle` (a local
464
+ * worker, or a bake farm you already run), and nothing else about the panel
465
+ * changes.
466
+ */
467
+
468
+ /** What turns a prepared bake into a bundle. See the file header. */
469
+ type BakeTransport = (spec: BakeSpec, assets: CapturedScene['assets']) => Promise<BakeBundle>;
470
+ /**
471
+ * The part of the client the panel uses.
472
+ *
473
+ * Structural rather than `Bakery3` itself so that a host wrapping the client
474
+ * (to add its own telemetry, or to swap in a fake in a test) is not shut out
475
+ * of the panel for it. A real `createBakery3()` satisfies it as-is.
476
+ */
477
+ type Bakery3DevtoolsClient = Pick<Bakery3, 'check' | 'estimate' | 'renderVideo'> & {
478
+ /** One render, one job: the panel never asks for a set of cameras. */
479
+ render(options?: RenderOptions): Promise<RenderJob>;
480
+ } &
481
+ /** Present on every real client; a host that fakes one and gives no
482
+ * `bakeTransport` is told so when the Bake button is pressed. */
483
+ Partial<Pick<Bakery3, 'bake'>>;
484
+ type Bakery3DevtoolsOptions = {
485
+ bakery3: Bakery3DevtoolsClient;
486
+ /**
487
+ * The live scene the panel checks, prices, renders and bakes. Read each
488
+ * time it is used, so a getter works; the Bake tab's session belongs to the
489
+ * scene this is when the panel mounts.
490
+ */
491
+ scene: Scene;
492
+ /** Read each time it is used, so a getter can follow a camera that is
493
+ * swapped (R3F does) without remounting the panel. Its aspect is the
494
+ * aspect of every render and video the panel starts. */
495
+ camera: Camera;
496
+ /**
497
+ * The live `WebGLRenderer`, so the capture carries your color space and
498
+ * tone mapping instead of guessing them. Optional but wanted: an app that
499
+ * tone-maps and does not say so gets a render that is faithful to the
500
+ * manifest and not to the viewport.
501
+ */
502
+ renderer?: CaptureOptions['renderer'];
503
+ /** Where the panel mounts. Defaults to `document.body`. */
504
+ container?: HTMLElement;
505
+ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
506
+ defaultQuality?: QualityPreset;
507
+ defaultOpen?: boolean;
508
+ defaultTab?: DevPanelTab;
509
+ /** A name beside the mark in the header. None by default. */
510
+ title?: string;
511
+ onOpenChange?: (open: boolean) => void;
512
+ /**
513
+ * The timeline the Video tab films. Without one the panel makes its own,
514
+ * empty, on the live scene and camera: frame a shot, press Keyframe camera,
515
+ * move the playhead and press it again.
516
+ */
517
+ timeline?: Timeline;
518
+ /** Objects offered in the timeline's "add track" picker. */
519
+ timelineTargets?: TimelinePanelTarget[];
520
+ /** Fired when the timeline takes the camera and when it gives it back.
521
+ * Suspend your orbit controls here. */
522
+ onPlaybackChange?: (active: boolean) => void;
523
+ /** Your own bake worker instead of the hosted `/v1/bakes`. */
524
+ bakeTransport?: BakeTransport;
525
+ /** Where a bundle's relative uris resolve against, when it has any. */
526
+ bakeBaseUrl?: string;
527
+ /** Zero the realtime lights when a generation is applied. Off by default:
528
+ * the session already replaces diffuse lighting with the lightmap (see
529
+ * bake/shading.ts), so muting only removes the lamps' highlights. It is a
530
+ * look to switch on from the panel, not a correction. */
531
+ muteLightsOnApply?: boolean;
532
+ history?: DevPanelHistoryOptions;
533
+ };
534
+ declare function mountBakery3Devtools(options: Bakery3DevtoolsOptions): DevPanel;
535
+
536
+ export { type BakeTransport, type Bakery3DevtoolsClient, type Bakery3DevtoolsOptions, type DevPanel, type DevPanelActionResult, type DevPanelBakeOptions, type DevPanelBakeResult, type DevPanelBakeSettings, type DevPanelCompatibility, type DevPanelEstimate, type DevPanelEstimateRequest, type DevPanelHistoryEntry, type DevPanelHistoryOptions, type DevPanelJob, type DevPanelJobHandle, type DevPanelOptions, type DevPanelRenderOptions, type DevPanelRenderSettings, type DevPanelTab, type DevPanelVideoOptions, type DevPanelVideoSettings, type TimelinePanel, type TimelinePanelOptions, type TimelinePanelTarget, type TrackableJob, type TrackedJobPayload, mountBakery3Devtools, mountDevPanel, mountTimelinePanel };
@@ -0,0 +1,15 @@
1
+ export { mountBakery3Devtools, mountDevPanel, mountTimelinePanel } from '../chunk-54WJDN5R.js';
2
+ import '../chunk-CX4WTVGH.js';
3
+ import '../chunk-V3QINEEG.js';
4
+ import '../chunk-APBOWWSV.js';
5
+ import '../chunk-S4AJTLLN.js';
6
+ import '../chunk-AF5BIELI.js';
7
+ import '../chunk-TGFNQBKC.js';
8
+ import '../chunk-HQTUFVMM.js';
9
+ import '../chunk-4E5VV4QY.js';
10
+ import '../chunk-W6WQ5UP7.js';
11
+ import '../chunk-6NYR73Y7.js';
12
+ import '../chunk-MD2U6S4N.js';
13
+ import '../chunk-3G5QL4F4.js';
14
+ //# sourceMappingURL=index.js.map
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,93 @@
1
+ import { Group, Object3D } from 'three';
2
+
3
+ /**
4
+ * The implementation behind `@oeave/bakery3/environments`. `index.ts` lists
5
+ * what is public.
6
+ */
7
+
8
+ declare const ENVIRONMENT_SHAPES: readonly ["sphere", "groundSphere", "floor"];
9
+ declare const ENVIRONMENT_SHADERS: readonly ["gradient", "bands", "blobs", "horizon"];
10
+ /**
11
+ * `dark`: an ink dome with colored light in it, over a dark, glossy ground,
12
+ * the brand's own look. `colorful`: the same ground under more of that light.
13
+ * `light`: a pale dome with soft color, over a warm matte ground.
14
+ */
15
+ declare const ENVIRONMENT_MODES: readonly ["dark", "colorful", "light"];
16
+ type EnvironmentShape = (typeof ENVIRONMENT_SHAPES)[number];
17
+ type EnvironmentShader = (typeof ENVIRONMENT_SHADERS)[number];
18
+ type EnvironmentMode = (typeof ENVIRONMENT_MODES)[number];
19
+ type EnvironmentOptions = {
20
+ /**
21
+ * `sphere`: an inward-facing dome of `radius` around the origin.
22
+ * `groundSphere`: the same dome with its lower part flattened onto y = 0,
23
+ * so shadows land on it. `floor`: a disc of `radius` at y = 0 whose color
24
+ * and opacity fade with distance, so its edge never shows.
25
+ */
26
+ shape: EnvironmentShape;
27
+ shader: EnvironmentShader;
28
+ /**
29
+ * Which palette and ground the environment defaults to. `dark` unless told
30
+ * otherwise. A `palette` overrides the colors; the ground still follows
31
+ * the mode.
32
+ */
33
+ mode?: EnvironmentMode;
34
+ /**
35
+ * Two to four CSS hex colors, darkest first. Each shader has a default
36
+ * per mode. What each slot means:
37
+ *
38
+ * - `gradient`: bottom to top (center to rim on a floor)
39
+ * - `bands`: the base, then the band colors in turn
40
+ * - `blobs`: the base, then the blob colors in turn
41
+ * - `horizon`: the dome, the horizon ring, the key glow
42
+ */
43
+ palette?: string[];
44
+ /**
45
+ * Emission strength, in the units the renderer traces with: a dome at
46
+ * `1` emits the radiance an HDRI pixel of value 1 would. Meaningless for
47
+ * the `floor` shape, which is lit, not lit from. Defaults per mode: a dark
48
+ * dome needs its few bright patches to carry the whole scene, so it emits
49
+ * harder (`3` dark, `2.5` colorful, `1.2` light).
50
+ */
51
+ intensity?: number;
52
+ /** Dome or disc radius in meters. Default `12`. */
53
+ radius?: number;
54
+ /** Seeds blob placement and the key glow's side. Default `1`. */
55
+ seed?: number;
56
+ /**
57
+ * A phase, in seconds, baked into the shader when it is built: it turns
58
+ * the bands and rotates the blobs, and is the same number in the viewport
59
+ * and in the render. Default `0`.
60
+ */
61
+ time?: number;
62
+ /**
63
+ * Add a viewport-only fill light. The dome is a real light in the render,
64
+ * but a rasterizer takes no light from an emissive mesh, so without a fill
65
+ * a product under a dark dome previews black. The fill is a hemisphere
66
+ * light in the dome's own colors (sky and ground from the palette, white
67
+ * over warm ground in light mode), marked `userData.bakery3.exclude` so it
68
+ * never reaches the manifest. The two halves differ on the product on
69
+ * purpose (a flat two-tone fill live, the dome's own light in the render).
70
+ * The dome is the same in both; the glossy ground of a dark or colorful
71
+ * dome is not, because in the render it reflects the dome (a colorful
72
+ * horizon lays a band of its color across it) and a rasterizer reflects
73
+ * no mesh. Default `false`.
74
+ */
75
+ fill?: boolean;
76
+ };
77
+ /**
78
+ * The options an environment was built with, defaults filled in. The fill is
79
+ * not part of it: it is a viewport aid, not the environment.
80
+ */
81
+ type EnvironmentSpec = Required<Omit<EnvironmentOptions, 'fill'>>;
82
+ /**
83
+ * Build an environment. Returns a `Group` holding one mesh, named
84
+ * `bakery3-environment-<shape>-<shader>`, with the resolved options on
85
+ * `userData.bakery3Preset.environment`. The product stands at the origin,
86
+ * on y = 0.
87
+ */
88
+ declare function createEnvironment(options: EnvironmentOptions): Group;
89
+ /** The spec a `createEnvironment()` group was built with, or `undefined`
90
+ * for anything else. */
91
+ declare function environmentSpecOf(object: Object3D): EnvironmentSpec | undefined;
92
+
93
+ export { ENVIRONMENT_MODES, ENVIRONMENT_SHADERS, ENVIRONMENT_SHAPES, type EnvironmentMode, type EnvironmentOptions, type EnvironmentShader, type EnvironmentShape, type EnvironmentSpec, createEnvironment, environmentSpecOf };