@snaptrude/plugin-core 0.9.15 → 0.9.16

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.
@@ -0,0 +1,1201 @@
1
+ import * as z from "zod"
2
+ import { PluginApiReturn } from "../../types"
3
+ import { ComponentHandle, Vec3Components } from "../../handles"
4
+
5
+ /**
6
+ * Walkthroughs: camera paths through the model, played in the viewport and
7
+ * exported as videos.
8
+ *
9
+ * A walkthrough is a list of keyframes (camera positions and where each looks)
10
+ * joined by a smooth path that the camera travels at a steady speed. Make one
11
+ * from full keyframes (`create`), from points on the floor (`createFromPoints`)
12
+ * or from a camera move around what is selected (`createFromPreset`); inspect
13
+ * it (`sample`, `listIssues`, `capture`); play it (`start`/`stop`/`getState`);
14
+ * and export it as a video onto the Present canvas (`export` plus the job
15
+ * methods). Edits are write-gated and each is one step of the walkthrough undo
16
+ * history, which is separate from the model's. Walkthroughs are not saved with
17
+ * the project yet: they last until the project is closed or reloaded, while an
18
+ * exported video stays on the Present canvas.
19
+ *
20
+ * Positions are world coordinates in internal `babylon` units (1 unit =
21
+ * 0.254 m, Y up); yaw is degrees clockwise from +z seen from above, pitch is
22
+ * degrees above level. Accessed via `snaptrude.presentation.walkthroughs`.
23
+ */
24
+ export abstract class PluginPresentationWalkthroughsApi {
25
+ constructor() {}
26
+
27
+ /**
28
+ * List the open project's walkthroughs, oldest first.
29
+ *
30
+ * @returns `{ walkthroughs }` — every {@linkcode PluginWalkthrough} (`[]` when there are none).
31
+ *
32
+ * @examplePrompt Which walkthroughs are there?
33
+ * @examplePrompt List the camera paths in this project
34
+ *
35
+ * # Example
36
+ * ```ts
37
+ * const { walkthroughs } = await snaptrude.presentation.walkthroughs.list()
38
+ * ```
39
+ */
40
+ public abstract list(): PluginApiReturn<PluginWalkthroughsListResult>
41
+
42
+ /**
43
+ * Get one walkthrough by id.
44
+ *
45
+ * @param walkthroughId - The walkthrough's id
46
+ * @returns The {@linkcode PluginWalkthrough}, or `null` when there is none with that id.
47
+ *
48
+ * @examplePrompt Show me the keyframes of the orbit walkthrough
49
+ * @examplePrompt How long does this walkthrough take?
50
+ *
51
+ * # Example
52
+ * ```ts
53
+ * const walkthrough = await snaptrude.presentation.walkthroughs.get(walkthroughId)
54
+ * console.log(walkthrough?.durationInSeconds)
55
+ * ```
56
+ */
57
+ public abstract get(
58
+ walkthroughId: string,
59
+ ): PluginApiReturn<PluginWalkthrough | null>
60
+
61
+ /**
62
+ * Create a walkthrough from full keyframes: where the camera stands at each
63
+ * and how it looks there (ahead along the path, at a target, or in a fixed
64
+ * direction). A keyframe that follows the floor stands at the walkthrough's
65
+ * eye height above the floor, stair, terrain or ground at or below its `eye`,
66
+ * and the path between two such keyframes climbs stairs and ramps. The new walkthrough opens in the
67
+ * walkthrough dock. Write-gated; one undo step.
68
+ *
69
+ * @param keyframes - At least one {@linkcode PluginWalkthroughKeyframeInput}, in order
70
+ * @param options - Name, loop, repeat, and speed or duration ({@linkcode PluginWalkthroughCreateOptions})
71
+ * @returns The new {@linkcode PluginWalkthrough}.
72
+ * @throws METHOD_NOT_PERMITTED when plugin writes are disabled. VALIDATION when
73
+ * `isLoop` is set with fewer than three keyframes. PRECONDITION_FAILED when a
74
+ * floor-following keyframe lies below the ground with nothing under it.
75
+ *
76
+ * @examplePrompt Make a walkthrough through these three viewpoints
77
+ * @examplePrompt Create a camera path that looks at the atrium the whole way
78
+ *
79
+ * # Example
80
+ * ```ts
81
+ * const walkthrough = await snaptrude.presentation.walkthroughs.create(
82
+ * [
83
+ * { eye: { x: 0, y: 30, z: 80 }, look: { mode: "target", target: { x: 0, y: 10, z: 0 } } },
84
+ * { eye: { x: 60, y: 30, z: 0 }, look: { mode: "target", target: { x: 0, y: 10, z: 0 } } },
85
+ * ],
86
+ * { name: "Approach", durationInSeconds: 10 },
87
+ * )
88
+ * ```
89
+ */
90
+ public abstract create(
91
+ keyframes: PluginWalkthroughKeyframeInput[],
92
+ options?: PluginWalkthroughCreateOptions,
93
+ ): PluginApiReturn<PluginWalkthrough>
94
+
95
+ /**
96
+ * Create a walkthrough that walks through points on the floor, the way the
97
+ * Walkthrough tool draws a route: the camera stands at eye height above the
98
+ * floor, stair, terrain or ground at each point, climbs stairs and ramps between them, and looks ahead
99
+ * along the path, or at `options.lookAt` the whole way. Write-gated; one undo
100
+ * step.
101
+ *
102
+ * @param points - 2 to 200 points on or above the floor, in order
103
+ * @param options - Name, loop, repeat, speed or duration, and `lookAt` ({@linkcode PluginWalkthroughCreateFromPointsOptions})
104
+ * @returns The new {@linkcode PluginWalkthrough}.
105
+ * @throws METHOD_NOT_PERMITTED when plugin writes are disabled. VALIDATION when
106
+ * `isLoop` is set with fewer than three points. PRECONDITION_FAILED when a point
107
+ * lies below the ground with nothing under it.
108
+ *
109
+ * @examplePrompt Walk from the entrance through the lobby to the stairs
110
+ * @examplePrompt Make a walkthrough that visits every room on this floor
111
+ * @examplePrompt Create a loop around the courtyard
112
+ *
113
+ * # Example
114
+ * ```ts
115
+ * await snaptrude.presentation.walkthroughs.createFromPoints(
116
+ * [{ x: 0, y: 0, z: 0 }, { x: 40, y: 0, z: 0 }, { x: 40, y: 0, z: 30 }],
117
+ * { name: "Ground floor tour" },
118
+ * )
119
+ * ```
120
+ */
121
+ public abstract createFromPoints(
122
+ points: Vec3Components[],
123
+ options?: PluginWalkthroughCreateFromPointsOptions,
124
+ ): PluginApiReturn<PluginWalkthrough>
125
+
126
+ /**
127
+ * Create a walkthrough from a camera move around a subject, as the
128
+ * Walkthrough button's menu does: `orbit` circles it facing its middle,
129
+ * `flyOver` passes above it looking ahead and down, `pushIn` and `pullBack`
130
+ * move toward and away from it, and `rise` cranes up from eye level. The
131
+ * subject is `options.subject`, else the selection, else the visible model
132
+ * without its terrain; the move is sized to fit it in view and starts on the
133
+ * side the camera is on. While the walkthrough is still a preset,
134
+ * {@linkcode PluginPresentationWalkthroughsApi.update} can change its
135
+ * distance, height and direction; editing a keyframe makes it ordinary.
136
+ * Write-gated; one undo step.
137
+ *
138
+ * @param kind - The camera move ({@linkcode PluginWalkthroughPresetKind})
139
+ * @param options - Subject, distance, height, direction, duration, name, repeat ({@linkcode PluginWalkthroughCreateFromPresetOptions})
140
+ * @returns The new {@linkcode PluginWalkthrough}.
141
+ * @throws METHOD_NOT_PERMITTED when plugin writes are disabled. HANDLE_INVALID when a
142
+ * subject handle does not resolve. PRECONDITION_FAILED when there is nothing to frame.
143
+ *
144
+ * @examplePrompt Orbit around the selected building
145
+ * @examplePrompt Make a drone fly-over of the site
146
+ * @examplePrompt Push in slowly toward the entrance
147
+ * @examplePrompt Crane up to reveal the whole campus
148
+ *
149
+ * # Example
150
+ * ```ts
151
+ * await snaptrude.presentation.walkthroughs.createFromPreset("orbit", {
152
+ * subject: [towerId],
153
+ * heightInDegrees: 35,
154
+ * durationInSeconds: 20,
155
+ * })
156
+ * ```
157
+ */
158
+ public abstract createFromPreset(
159
+ kind: PluginWalkthroughPresetKind,
160
+ options?: PluginWalkthroughCreateFromPresetOptions,
161
+ ): PluginApiReturn<PluginWalkthrough>
162
+
163
+ /**
164
+ * Change a walkthrough: only the fields given change. Speed and duration are
165
+ * two ways to set one thing, so give at most one of them. `preset` changes a
166
+ * preset walkthrough's camera move and makes its keyframes again, keeping its
167
+ * duration; changing `isLoop` of a preset walkthrough makes it ordinary.
168
+ * Write-gated; one undo step.
169
+ *
170
+ * @param walkthroughId - The walkthrough's id
171
+ * @param patch - The fields to change ({@linkcode PluginWalkthroughPatch})
172
+ * @returns The updated {@linkcode PluginWalkthrough}.
173
+ * @throws METHOD_NOT_PERMITTED when plugin writes are disabled. HANDLE_INVALID when
174
+ * there is no walkthrough with that id. VALIDATION when `isLoop` is set on a
175
+ * walkthrough of fewer than three keyframes. PRECONDITION_FAILED when `preset`
176
+ * is given for a walkthrough that is not a preset.
177
+ *
178
+ * @examplePrompt Slow the walkthrough down to 30 seconds
179
+ * @examplePrompt Rename it to "Evening approach"
180
+ * @examplePrompt Make the orbit go the other way round and higher up
181
+ *
182
+ * # Example
183
+ * ```ts
184
+ * await snaptrude.presentation.walkthroughs.update(walkthroughId, {
185
+ * durationInSeconds: 30,
186
+ * isRepeating: true,
187
+ * })
188
+ * ```
189
+ */
190
+ public abstract update(
191
+ walkthroughId: string,
192
+ patch: PluginWalkthroughPatch,
193
+ ): PluginApiReturn<PluginWalkthrough>
194
+
195
+ /**
196
+ * Delete a walkthrough. Write-gated; one undo step.
197
+ *
198
+ * @param walkthroughId - The walkthrough's id
199
+ * @returns `true` once it has been deleted.
200
+ * @throws METHOD_NOT_PERMITTED when plugin writes are disabled. HANDLE_INVALID when
201
+ * there is no walkthrough with that id.
202
+ *
203
+ * @examplePrompt Delete the old walkthrough
204
+ *
205
+ * # Example
206
+ * ```ts
207
+ * await snaptrude.presentation.walkthroughs.delete(walkthroughId)
208
+ * ```
209
+ */
210
+ public abstract delete(walkthroughId: string): PluginApiReturn<boolean>
211
+
212
+ /**
213
+ * Add a keyframe to a walkthrough at `index` (0 to the keyframe count;
214
+ * default: the end). A preset walkthrough becomes ordinary. Write-gated; one
215
+ * undo step.
216
+ *
217
+ * @param walkthroughId - The walkthrough's id
218
+ * @param keyframe - The {@linkcode PluginWalkthroughKeyframeInput} to add
219
+ * @param index - Where to insert it (default: after the last keyframe)
220
+ * @returns The updated {@linkcode PluginWalkthrough}; the new keyframe is at `index`.
221
+ * @throws METHOD_NOT_PERMITTED when plugin writes are disabled. HANDLE_INVALID when
222
+ * there is no walkthrough with that id. VALIDATION when `index` is out of range.
223
+ *
224
+ * @examplePrompt Add a stop in the kitchen after the second point
225
+ *
226
+ * # Example
227
+ * ```ts
228
+ * await snaptrude.presentation.walkthroughs.createKeyframe(
229
+ * walkthroughId,
230
+ * { eye: { x: 12, y: 0, z: 30 }, followsFloor: true, holdInSeconds: 2 },
231
+ * 2,
232
+ * )
233
+ * ```
234
+ */
235
+ public abstract createKeyframe(
236
+ walkthroughId: string,
237
+ keyframe: PluginWalkthroughKeyframeInput,
238
+ index?: number,
239
+ ): PluginApiReturn<PluginWalkthrough>
240
+
241
+ /**
242
+ * Change a keyframe: only the fields given change. A preset walkthrough
243
+ * becomes ordinary. Write-gated; one undo step.
244
+ *
245
+ * @param walkthroughId - The walkthrough's id
246
+ * @param keyframeId - The keyframe's id
247
+ * @param patch - The fields to change ({@linkcode PluginWalkthroughKeyframePatch})
248
+ * @returns The updated {@linkcode PluginWalkthrough}.
249
+ * @throws METHOD_NOT_PERMITTED when plugin writes are disabled. HANDLE_INVALID when
250
+ * the walkthrough or keyframe does not exist.
251
+ *
252
+ * @examplePrompt Pause for three seconds at the second point
253
+ * @examplePrompt Make the last point look at the fireplace
254
+ *
255
+ * # Example
256
+ * ```ts
257
+ * await snaptrude.presentation.walkthroughs.updateKeyframe(walkthroughId, keyframeId, {
258
+ * look: { mode: "target", target: { x: 5, y: 4, z: 10 } },
259
+ * })
260
+ * ```
261
+ */
262
+ public abstract updateKeyframe(
263
+ walkthroughId: string,
264
+ keyframeId: string,
265
+ patch: PluginWalkthroughKeyframePatch,
266
+ ): PluginApiReturn<PluginWalkthrough>
267
+
268
+ /**
269
+ * Remove a keyframe; the path then runs straight from the keyframe before it
270
+ * to the one after. Removing the last keyframe deletes the walkthrough. A
271
+ * preset walkthrough becomes ordinary. Write-gated; one undo step.
272
+ *
273
+ * @param walkthroughId - The walkthrough's id
274
+ * @param keyframeId - The keyframe's id
275
+ * @returns The updated {@linkcode PluginWalkthrough}, or `null` when removing the last keyframe deleted it.
276
+ * @throws METHOD_NOT_PERMITTED when plugin writes are disabled. HANDLE_INVALID when
277
+ * the walkthrough or keyframe does not exist.
278
+ *
279
+ * @examplePrompt Remove the third stop from the walkthrough
280
+ *
281
+ * # Example
282
+ * ```ts
283
+ * await snaptrude.presentation.walkthroughs.deleteKeyframe(walkthroughId, keyframeId)
284
+ * ```
285
+ */
286
+ public abstract deleteKeyframe(
287
+ walkthroughId: string,
288
+ keyframeId: string,
289
+ ): PluginApiReturn<PluginWalkthrough | null>
290
+
291
+ /**
292
+ * Move a keyframe to position `index` in the walkthrough's order (the
293
+ * `presentation.sheets.reorder` convention). A preset walkthrough becomes
294
+ * ordinary. Write-gated; one undo step.
295
+ *
296
+ * @param walkthroughId - The walkthrough's id
297
+ * @param keyframeId - The keyframe's id
298
+ * @param index - Its new 0-based position (0 to the keyframe count - 1)
299
+ * @returns The updated {@linkcode PluginWalkthrough}.
300
+ * @throws METHOD_NOT_PERMITTED when plugin writes are disabled. HANDLE_INVALID when
301
+ * the walkthrough or keyframe does not exist. VALIDATION when `index` is out of range.
302
+ *
303
+ * @examplePrompt Visit the terrace before the living room
304
+ *
305
+ * # Example
306
+ * ```ts
307
+ * await snaptrude.presentation.walkthroughs.reorderKeyframe(walkthroughId, keyframeId, 0)
308
+ * ```
309
+ */
310
+ public abstract reorderKeyframe(
311
+ walkthroughId: string,
312
+ keyframeId: string,
313
+ index: number,
314
+ ): PluginApiReturn<PluginWalkthrough>
315
+
316
+ /**
317
+ * Sample where the camera is and which way it looks at given moments of a
318
+ * walkthrough's playback, without moving the camera.
319
+ *
320
+ * @param walkthroughId - The walkthrough's id
321
+ * @param timesInSeconds - 1 to 500 moments, each clamped to the walkthrough's duration
322
+ * @returns `{ samples }` — one {@linkcode PluginWalkthroughSample} per time, in order.
323
+ * @throws HANDLE_INVALID when there is no walkthrough with that id.
324
+ *
325
+ * @examplePrompt Where is the camera halfway through the walkthrough?
326
+ * @examplePrompt Check how high the camera flies over the roof
327
+ *
328
+ * # Example
329
+ * ```ts
330
+ * const walkthrough = await snaptrude.presentation.walkthroughs.get(walkthroughId)
331
+ * const { samples } = await snaptrude.presentation.walkthroughs.sample(walkthroughId, [
332
+ * 0,
333
+ * walkthrough!.durationInSeconds / 2,
334
+ * ])
335
+ * ```
336
+ */
337
+ public abstract sample(
338
+ walkthroughId: string,
339
+ timesInSeconds: number[],
340
+ ): PluginApiReturn<PluginWalkthroughSampleResult>
341
+
342
+ /**
343
+ * Find places where a walkthrough's camera passes through the model: through
344
+ * anything walking would stop at, such as a wall, glazing, a column, a slab or
345
+ * a roof, which would show the inside of the geometry on screen. Doors,
346
+ * furniture and room masses don't count, as walking passes through them. Each
347
+ * issue gives the first moment and point of one pass. The camera itself does
348
+ * not move.
349
+ *
350
+ * @param walkthroughId - The walkthrough's id
351
+ * @returns `{ issues }` — every {@linkcode PluginWalkthroughIssue} in playback order (`[]` when the path is clear).
352
+ * @throws HANDLE_INVALID when there is no walkthrough with that id.
353
+ *
354
+ * @examplePrompt Does the walkthrough go through any walls?
355
+ * @examplePrompt Check the camera path for clashes and fix them
356
+ *
357
+ * # Example
358
+ * ```ts
359
+ * const { issues } = await snaptrude.presentation.walkthroughs.listIssues(walkthroughId)
360
+ * for (const issue of issues) console.log(issue.timeInSeconds, issue.componentId)
361
+ * ```
362
+ */
363
+ public abstract listIssues(
364
+ walkthroughId: string,
365
+ ): PluginApiReturn<PluginWalkthroughListIssuesResult>
366
+
367
+ /**
368
+ * Capture frames of a walkthrough as one contact-sheet image: a grid of
369
+ * views at evenly spaced moments (or at `options.timesInSeconds`), read left
370
+ * to right and top to bottom. The camera goes back to where it was
371
+ * afterwards. Switches from 2D plan view to 3D first.
372
+ *
373
+ * @param walkthroughId - The walkthrough's id
374
+ * @param options - How many frames or which moments, image width and format ({@linkcode PluginWalkthroughCaptureOptions})
375
+ * @returns `{ image, frames }` — the contact sheet as a base64 data URL and the moment of each frame.
376
+ * @throws HANDLE_INVALID when there is no walkthrough with that id. PRECONDITION_FAILED
377
+ * when a walkthrough is playing or being exported.
378
+ *
379
+ * @examplePrompt Show me what the walkthrough looks like
380
+ * @examplePrompt Capture six frames of the fly-over
381
+ *
382
+ * # Example
383
+ * ```ts
384
+ * const { image } = await snaptrude.presentation.walkthroughs.capture(walkthroughId, {
385
+ * count: 6,
386
+ * })
387
+ * ```
388
+ */
389
+ public abstract capture(
390
+ walkthroughId: string,
391
+ options?: PluginWalkthroughCaptureOptions,
392
+ ): PluginApiReturn<PluginWalkthroughCaptureResult>
393
+
394
+ /**
395
+ * Play a walkthrough in the viewport, from the start or `options.fromTimeInSeconds`.
396
+ * Arrow-key navigation pauses while it plays; {@linkcode PluginPresentationWalkthroughsApi.stop}
397
+ * puts the camera back where it was. Switches from 2D plan view to 3D first.
398
+ * Resolves once playback has started; a walkthrough that is already playing
399
+ * keeps playing.
400
+ *
401
+ * @param walkthroughId - The walkthrough's id
402
+ * @param options - `fromTimeInSeconds` (default 0)
403
+ * @returns The {@linkcode PluginWalkthroughPlaybackState} after starting.
404
+ * @throws HANDLE_INVALID when there is no walkthrough with that id. PRECONDITION_FAILED
405
+ * when a walkthrough is being exported.
406
+ *
407
+ * @examplePrompt Play the walkthrough
408
+ * @examplePrompt Show me the orbit
409
+ *
410
+ * # Example
411
+ * ```ts
412
+ * await snaptrude.presentation.walkthroughs.start(walkthroughId)
413
+ * ```
414
+ */
415
+ public abstract start(
416
+ walkthroughId: string,
417
+ options?: PluginWalkthroughStartOptions,
418
+ ): PluginApiReturn<PluginWalkthroughPlaybackState>
419
+
420
+ /**
421
+ * Stop playback and put the camera back where it was before it started. A
422
+ * no-op when nothing is playing.
423
+ *
424
+ * @returns The {@linkcode PluginWalkthroughPlaybackState} after stopping.
425
+ *
426
+ * @examplePrompt Stop the walkthrough
427
+ *
428
+ * # Example
429
+ * ```ts
430
+ * await snaptrude.presentation.walkthroughs.stop()
431
+ * ```
432
+ */
433
+ public abstract stop(): PluginApiReturn<PluginWalkthroughPlaybackState>
434
+
435
+ /**
436
+ * Read the playback state: which walkthrough is playing or paused, and how
437
+ * far through it.
438
+ *
439
+ * @returns The current {@linkcode PluginWalkthroughPlaybackState}.
440
+ *
441
+ * @examplePrompt Is the walkthrough still playing?
442
+ *
443
+ * # Example
444
+ * ```ts
445
+ * const { status, timeInSeconds } = await snaptrude.presentation.walkthroughs.getState()
446
+ * ```
447
+ */
448
+ public abstract getState(): PluginApiReturn<PluginWalkthroughPlaybackState>
449
+
450
+ /**
451
+ * Record a walkthrough as a video and place it on the Present canvas, as an
452
+ * asynchronous job: this resolves at once with the queued job, and
453
+ * {@linkcode PluginPresentationWalkthroughsApi.getJob} reports it until it
454
+ * completes, fails or is cancelled. Recording plays the walkthrough in real
455
+ * time at the chosen size, so it takes about as long as the walkthrough plus
456
+ * the upload; the editor switches to Present mode to place the video, which
457
+ * keeps playing muted in a loop there. Only one export runs at a time.
458
+ * Write-gated.
459
+ *
460
+ * @param walkthroughId - The walkthrough's id
461
+ * @param options - Video quality and frame rate ({@linkcode PluginWalkthroughExportOptions})
462
+ * @returns The queued {@linkcode PluginWalkthroughExportJob}.
463
+ * @throws METHOD_NOT_PERMITTED when plugin writes are disabled. HANDLE_INVALID when
464
+ * there is no walkthrough with that id. PRECONDITION_FAILED when another export
465
+ * is running or this browser cannot record video.
466
+ *
467
+ * @examplePrompt Export the walkthrough as a video
468
+ * @examplePrompt Put a 1080p video of the orbit on a sheet
469
+ *
470
+ * # Example
471
+ * ```ts
472
+ * const walkthroughs = snaptrude.presentation.walkthroughs
473
+ * let job = await walkthroughs.export(walkthroughId, { quality: "1080p" })
474
+ * while (job.status === "queued" || job.status === "running") {
475
+ * await new Promise((resolve) => setTimeout(resolve, 2000))
476
+ * job = (await walkthroughs.getJob(job.jobId))!
477
+ * }
478
+ * console.log(job.result?.shapeId)
479
+ * ```
480
+ */
481
+ public abstract export(
482
+ walkthroughId: string,
483
+ options?: PluginWalkthroughExportOptions,
484
+ ): PluginApiReturn<PluginWalkthroughExportJob>
485
+
486
+ /**
487
+ * Get an export job by id.
488
+ *
489
+ * @param jobId - The job's id, from {@linkcode PluginPresentationWalkthroughsApi.export}
490
+ * @returns The {@linkcode PluginWalkthroughExportJob}, or `null` when there is none
491
+ * with that id (finished jobs are forgotten after 30 minutes).
492
+ *
493
+ * @examplePrompt Is the video export done yet?
494
+ *
495
+ * # Example
496
+ * ```ts
497
+ * const job = await snaptrude.presentation.walkthroughs.getJob(jobId)
498
+ * ```
499
+ */
500
+ public abstract getJob(
501
+ jobId: string,
502
+ ): PluginApiReturn<PluginWalkthroughExportJob | null>
503
+
504
+ /**
505
+ * Cancel an export job. The recording stops and nothing is placed; a job that
506
+ * has already finished is returned unchanged. Write-gated.
507
+ *
508
+ * @param jobId - The job's id
509
+ * @returns The {@linkcode PluginWalkthroughExportJob} after cancelling.
510
+ * @throws METHOD_NOT_PERMITTED when plugin writes are disabled. HANDLE_INVALID when
511
+ * there is no job with that id.
512
+ *
513
+ * @examplePrompt Cancel the video export
514
+ *
515
+ * # Example
516
+ * ```ts
517
+ * await snaptrude.presentation.walkthroughs.cancelJob(jobId)
518
+ * ```
519
+ */
520
+ public abstract cancelJob(
521
+ jobId: string,
522
+ ): PluginApiReturn<PluginWalkthroughExportJob>
523
+
524
+ /**
525
+ * List export jobs, newest first: running ones and those that finished in the
526
+ * last 30 minutes.
527
+ *
528
+ * @returns `{ jobs }` — each {@linkcode PluginWalkthroughExportJob} (`[]` when there are none).
529
+ *
530
+ * @examplePrompt Which video exports are running?
531
+ *
532
+ * # Example
533
+ * ```ts
534
+ * const { jobs } = await snaptrude.presentation.walkthroughs.listJobs()
535
+ * ```
536
+ */
537
+ public abstract listJobs(): PluginApiReturn<PluginWalkthroughListJobsResult>
538
+ }
539
+
540
+ // Plain {x, y, z} record with finite components (the brepFromRevolution `axisOrigin` pattern).
541
+ const FiniteVec3Components = z.object({
542
+ x: z.number().finite(),
543
+ y: z.number().finite(),
544
+ z: z.number().finite(),
545
+ })
546
+
547
+ const PositiveFinite = z.number().finite().positive()
548
+
549
+ /**
550
+ * How a keyframe's camera looks — exactly one of:
551
+ *
552
+ * | Shape | Meaning |
553
+ * |---|---|
554
+ * | `{ mode: "path" }` | Ahead along the path, level |
555
+ * | `{ mode: "target", target }` | At a point in the model |
556
+ * | `{ mode: "fixed", yawInDegrees, pitchInDegrees }` | In a fixed direction (pitch within ±89°) |
557
+ */
558
+ export const PluginWalkthroughLook = z.discriminatedUnion("mode", [
559
+ z.object({ mode: z.literal("path") }).strict(),
560
+ z
561
+ .object({ mode: z.literal("target"), target: FiniteVec3Components })
562
+ .strict(),
563
+ z
564
+ .object({
565
+ mode: z.literal("fixed"),
566
+ yawInDegrees: z.number().finite(),
567
+ pitchInDegrees: z.number().finite().min(-89).max(89),
568
+ })
569
+ .strict(),
570
+ ])
571
+ export type PluginWalkthroughLook = z.infer<typeof PluginWalkthroughLook>
572
+
573
+ /**
574
+ * A keyframe of a {@linkcode PluginWalkthrough}.
575
+ *
576
+ * | Property | Type | Description |
577
+ * |---|---|---|
578
+ * | `keyframeId` | `string` | The keyframe's id |
579
+ * | `eye` | `{ x, y, z }` | Camera position (world, `babylon` units) |
580
+ * | `look` | {@linkcode PluginWalkthroughLook} | How the camera looks here |
581
+ * | `followsFloor` | `boolean` | Stands at eye height above the floor; paths between two such keyframes follow the floor |
582
+ * | `holdInSeconds` | `number` | Pause here before moving on |
583
+ */
584
+ export const PluginWalkthroughKeyframe = z.object({
585
+ keyframeId: z.string(),
586
+ eye: Vec3Components,
587
+ look: PluginWalkthroughLook,
588
+ followsFloor: z.boolean(),
589
+ holdInSeconds: z.number(),
590
+ })
591
+ export type PluginWalkthroughKeyframe = z.infer<
592
+ typeof PluginWalkthroughKeyframe
593
+ >
594
+
595
+ /**
596
+ * A keyframe to add, for {@linkcode PluginPresentationWalkthroughsApi.create} and
597
+ * {@linkcode PluginPresentationWalkthroughsApi.createKeyframe}.
598
+ *
599
+ * | Property | Type | Description |
600
+ * |---|---|---|
601
+ * | `eye` | `{ x, y, z }` | Camera position; with `followsFloor`, a point on or above the floor |
602
+ * | `look` | {@linkcode PluginWalkthroughLook} `\| undefined` | How the camera looks here (default `{ mode: "path" }`) |
603
+ * | `followsFloor` | `boolean \| undefined` | Stand at eye height above the floor at or below `eye` (default `false`: `eye` as given) |
604
+ * | `holdInSeconds` | `number \| undefined` | Pause here, 0 to 60 (default 0) |
605
+ */
606
+ export const PluginWalkthroughKeyframeInput = z
607
+ .object({
608
+ eye: FiniteVec3Components,
609
+ look: PluginWalkthroughLook.optional(),
610
+ followsFloor: z.boolean().optional(),
611
+ holdInSeconds: z.number().finite().min(0).max(60).optional(),
612
+ })
613
+ .strict()
614
+ export type PluginWalkthroughKeyframeInput = z.infer<
615
+ typeof PluginWalkthroughKeyframeInput
616
+ >
617
+
618
+ /**
619
+ * The fields to change in {@linkcode PluginPresentationWalkthroughsApi.updateKeyframe};
620
+ * omitted fields keep their value.
621
+ */
622
+ export const PluginWalkthroughKeyframePatch = z
623
+ .object({
624
+ eye: FiniteVec3Components.optional(),
625
+ look: PluginWalkthroughLook.optional(),
626
+ followsFloor: z.boolean().optional(),
627
+ holdInSeconds: z.number().finite().min(0).max(60).optional(),
628
+ })
629
+ .strict()
630
+ export type PluginWalkthroughKeyframePatch = z.infer<
631
+ typeof PluginWalkthroughKeyframePatch
632
+ >
633
+
634
+ /**
635
+ * A camera move for {@linkcode PluginPresentationWalkthroughsApi.createFromPreset}:
636
+ * `orbit` (circle the subject facing its middle), `flyOver` (pass above it
637
+ * looking ahead and down), `pushIn` / `pullBack` (move toward / away from it),
638
+ * `rise` (crane up from eye level).
639
+ */
640
+ export const PluginWalkthroughPresetKind = z.enum([
641
+ "orbit",
642
+ "flyOver",
643
+ "pushIn",
644
+ "pullBack",
645
+ "rise",
646
+ ])
647
+ export type PluginWalkthroughPresetKind = z.infer<
648
+ typeof PluginWalkthroughPresetKind
649
+ >
650
+
651
+ /**
652
+ * The camera move a preset walkthrough was made from.
653
+ *
654
+ * | Property | Type | Description |
655
+ * |---|---|---|
656
+ * | `kind` | {@linkcode PluginWalkthroughPresetKind} | The move |
657
+ * | `distance` | `number` | Camera distance, as a multiple of the distance at which the subject just fits in view |
658
+ * | `heightInDegrees` | `number` | Camera height, in degrees above level seen from the subject's middle |
659
+ * | `isClockwise` | `boolean` | Orbits only: goes round clockwise seen from above |
660
+ */
661
+ export const PluginWalkthroughPreset = z.object({
662
+ kind: PluginWalkthroughPresetKind,
663
+ distance: z.number(),
664
+ heightInDegrees: z.number(),
665
+ isClockwise: z.boolean(),
666
+ })
667
+ export type PluginWalkthroughPreset = z.infer<typeof PluginWalkthroughPreset>
668
+
669
+ /** The Distance setting: 0.8 to 3 times the distance at which the subject just fits. */
670
+ const PresetDistance = z.number().finite().min(0.8).max(3)
671
+ /** The Height setting, in degrees above level. */
672
+ const PresetHeightInDegrees = z.number().finite().min(0).max(75)
673
+
674
+ /**
675
+ * A walkthrough.
676
+ *
677
+ * | Property | Type | Description |
678
+ * |---|---|---|
679
+ * | `walkthroughId` | `string` | The walkthrough's id |
680
+ * | `name` | `string` | Its name |
681
+ * | `keyframes` | {@linkcode PluginWalkthroughKeyframe}`[]` | Its keyframes, in order |
682
+ * | `isLoop` | `boolean` | The path returns from the last keyframe to the first |
683
+ * | `isRepeating` | `boolean` | Playback starts again when it reaches the end |
684
+ * | `speed` | `number` | Travel speed, `babylon` units per second |
685
+ * | `durationInSeconds` | `number` | How long playback takes, including easing and holds |
686
+ * | `pathLength` | `number` | Length of the path, `babylon` units |
687
+ * | `eyeHeight` | `number` | Eye height above the floor for floor-following keyframes, `babylon` units |
688
+ * | `preset` | {@linkcode PluginWalkthroughPreset} `\| null` | The camera move it was made from, while it is still a preset |
689
+ */
690
+ export const PluginWalkthrough = z.object({
691
+ walkthroughId: z.string(),
692
+ name: z.string(),
693
+ keyframes: z.array(PluginWalkthroughKeyframe),
694
+ isLoop: z.boolean(),
695
+ isRepeating: z.boolean(),
696
+ speed: z.number(),
697
+ durationInSeconds: z.number(),
698
+ pathLength: z.number(),
699
+ eyeHeight: z.number(),
700
+ preset: PluginWalkthroughPreset.nullable(),
701
+ })
702
+ export type PluginWalkthrough = z.infer<typeof PluginWalkthrough>
703
+
704
+ /**
705
+ * Result of {@linkcode PluginPresentationWalkthroughsApi.list}.
706
+ */
707
+ export const PluginWalkthroughsListResult = z.object({
708
+ walkthroughs: z.array(PluginWalkthrough),
709
+ })
710
+ export type PluginWalkthroughsListResult = z.infer<
711
+ typeof PluginWalkthroughsListResult
712
+ >
713
+
714
+ const CreateOptionFields = {
715
+ name: z.string().trim().min(1).max(120).optional(),
716
+ isLoop: z.boolean().optional(),
717
+ isRepeating: z.boolean().optional(),
718
+ speed: PositiveFinite.optional(),
719
+ durationInSeconds: PositiveFinite.optional(),
720
+ }
721
+
722
+ const atMostOneOfSpeedAndDuration = (value: {
723
+ speed?: number
724
+ durationInSeconds?: number
725
+ }) => value.speed === undefined || value.durationInSeconds === undefined
726
+ const SPEED_AND_DURATION_MESSAGE = "Give speed or durationInSeconds, not both"
727
+
728
+ /**
729
+ * Options for {@linkcode PluginPresentationWalkthroughsApi.create}.
730
+ *
731
+ * | Property | Type | Description |
732
+ * |---|---|---|
733
+ * | `name` | `string \| undefined` | Its name (default "Walkthrough N") |
734
+ * | `isLoop` | `boolean \| undefined` | Return from the last keyframe to the first (default `false`; needs 3 keyframes) |
735
+ * | `isRepeating` | `boolean \| undefined` | Play again from the start at the end (default `false`) |
736
+ * | `speed` | `number \| undefined` | Travel speed, `babylon` units per second (default 1 m/s) |
737
+ * | `durationInSeconds` | `number \| undefined` | Take this long instead; sets the speed. Give `speed` or this, not both |
738
+ */
739
+ export const PluginWalkthroughCreateOptions = z
740
+ .object(CreateOptionFields)
741
+ .strict()
742
+ .refine(atMostOneOfSpeedAndDuration, SPEED_AND_DURATION_MESSAGE)
743
+ export type PluginWalkthroughCreateOptions = z.infer<
744
+ typeof PluginWalkthroughCreateOptions
745
+ >
746
+
747
+ /**
748
+ * Arguments for {@linkcode PluginPresentationWalkthroughsApi.create}.
749
+ */
750
+ export const PluginWalkthroughCreateArgs = z.object({
751
+ keyframes: z.array(PluginWalkthroughKeyframeInput).min(1).max(200),
752
+ options: PluginWalkthroughCreateOptions.optional(),
753
+ })
754
+ export type PluginWalkthroughCreateArgs = z.infer<
755
+ typeof PluginWalkthroughCreateArgs
756
+ >
757
+
758
+ /**
759
+ * Options for {@linkcode PluginPresentationWalkthroughsApi.createFromPoints}: those of
760
+ * {@linkcode PluginWalkthroughCreateOptions}, plus:
761
+ *
762
+ * | Property | Type | Description |
763
+ * |---|---|---|
764
+ * | `lookAt` | `{ x, y, z } \| undefined` | Look at this point the whole way (default: ahead along the path) |
765
+ */
766
+ export const PluginWalkthroughCreateFromPointsOptions = z
767
+ .object({ ...CreateOptionFields, lookAt: FiniteVec3Components.optional() })
768
+ .strict()
769
+ .refine(atMostOneOfSpeedAndDuration, SPEED_AND_DURATION_MESSAGE)
770
+ export type PluginWalkthroughCreateFromPointsOptions = z.infer<
771
+ typeof PluginWalkthroughCreateFromPointsOptions
772
+ >
773
+
774
+ /**
775
+ * Arguments for {@linkcode PluginPresentationWalkthroughsApi.createFromPoints}.
776
+ */
777
+ export const PluginWalkthroughCreateFromPointsArgs = z.object({
778
+ points: z.array(FiniteVec3Components).min(2).max(200),
779
+ options: PluginWalkthroughCreateFromPointsOptions.optional(),
780
+ })
781
+ export type PluginWalkthroughCreateFromPointsArgs = z.infer<
782
+ typeof PluginWalkthroughCreateFromPointsArgs
783
+ >
784
+
785
+ /**
786
+ * Options for {@linkcode PluginPresentationWalkthroughsApi.createFromPreset}.
787
+ *
788
+ * | Property | Type | Description |
789
+ * |---|---|---|
790
+ * | `subject` | {@linkcode ComponentHandle}`[] \| undefined` | What to frame (default: the selection, else the visible model without terrain) |
791
+ * | `distance` | `number \| undefined` | 0.8 to 3 times the distance at which the subject just fits (default per move) |
792
+ * | `heightInDegrees` | `number \| undefined` | 0 to 75 degrees above level (default per move) |
793
+ * | `isClockwise` | `boolean \| undefined` | Orbits only: go round clockwise seen from above (default `false`) |
794
+ * | `durationInSeconds` | `number \| undefined` | How long the move takes (default per move: orbit 24, fly over 12, others 8) |
795
+ * | `name` | `string \| undefined` | Its name (default the move's name and a number) |
796
+ * | `isRepeating` | `boolean \| undefined` | Play again from the start at the end (default `false`) |
797
+ */
798
+ export const PluginWalkthroughCreateFromPresetOptions = z
799
+ .object({
800
+ subject: z.array(ComponentHandle).min(1).max(1000).optional(),
801
+ distance: PresetDistance.optional(),
802
+ heightInDegrees: PresetHeightInDegrees.optional(),
803
+ isClockwise: z.boolean().optional(),
804
+ durationInSeconds: PositiveFinite.optional(),
805
+ name: z.string().trim().min(1).max(120).optional(),
806
+ isRepeating: z.boolean().optional(),
807
+ })
808
+ .strict()
809
+ export type PluginWalkthroughCreateFromPresetOptions = z.infer<
810
+ typeof PluginWalkthroughCreateFromPresetOptions
811
+ >
812
+
813
+ /**
814
+ * Arguments for {@linkcode PluginPresentationWalkthroughsApi.createFromPreset}.
815
+ */
816
+ export const PluginWalkthroughCreateFromPresetArgs = z.object({
817
+ kind: PluginWalkthroughPresetKind,
818
+ options: PluginWalkthroughCreateFromPresetOptions.optional(),
819
+ })
820
+ export type PluginWalkthroughCreateFromPresetArgs = z.infer<
821
+ typeof PluginWalkthroughCreateFromPresetArgs
822
+ >
823
+
824
+ /**
825
+ * The fields to change in {@linkcode PluginPresentationWalkthroughsApi.update};
826
+ * omitted fields keep their value.
827
+ *
828
+ * | Property | Type | Description |
829
+ * |---|---|---|
830
+ * | `name` | `string \| undefined` | New name |
831
+ * | `isLoop` | `boolean \| undefined` | Return from the last keyframe to the first (needs 3 keyframes); makes a preset ordinary |
832
+ * | `isRepeating` | `boolean \| undefined` | Play again from the start at the end |
833
+ * | `speed` | `number \| undefined` | Travel speed, `babylon` units per second |
834
+ * | `durationInSeconds` | `number \| undefined` | Take this long; sets the speed. Give `speed` or this, not both |
835
+ * | `preset` | `{ distance?, heightInDegrees?, isClockwise? } \| undefined` | A preset walkthrough's move settings; its keyframes are made again |
836
+ */
837
+ export const PluginWalkthroughPatch = z
838
+ .object({
839
+ name: z.string().trim().min(1).max(120).optional(),
840
+ isLoop: z.boolean().optional(),
841
+ isRepeating: z.boolean().optional(),
842
+ speed: PositiveFinite.optional(),
843
+ durationInSeconds: PositiveFinite.optional(),
844
+ preset: z
845
+ .object({
846
+ distance: PresetDistance.optional(),
847
+ heightInDegrees: PresetHeightInDegrees.optional(),
848
+ isClockwise: z.boolean().optional(),
849
+ })
850
+ .strict()
851
+ .optional(),
852
+ })
853
+ .strict()
854
+ .refine(atMostOneOfSpeedAndDuration, SPEED_AND_DURATION_MESSAGE)
855
+ export type PluginWalkthroughPatch = z.infer<typeof PluginWalkthroughPatch>
856
+
857
+ /**
858
+ * Arguments for {@linkcode PluginPresentationWalkthroughsApi.update}.
859
+ */
860
+ export const PluginWalkthroughUpdateArgs = z.object({
861
+ walkthroughId: z.string().min(1),
862
+ patch: PluginWalkthroughPatch,
863
+ })
864
+ export type PluginWalkthroughUpdateArgs = z.infer<
865
+ typeof PluginWalkthroughUpdateArgs
866
+ >
867
+
868
+ /**
869
+ * Arguments for {@linkcode PluginPresentationWalkthroughsApi.createKeyframe}.
870
+ */
871
+ export const PluginWalkthroughCreateKeyframeArgs = z.object({
872
+ walkthroughId: z.string().min(1),
873
+ keyframe: PluginWalkthroughKeyframeInput,
874
+ index: z.number().int().min(0).optional(),
875
+ })
876
+ export type PluginWalkthroughCreateKeyframeArgs = z.infer<
877
+ typeof PluginWalkthroughCreateKeyframeArgs
878
+ >
879
+
880
+ /**
881
+ * Arguments for {@linkcode PluginPresentationWalkthroughsApi.updateKeyframe}.
882
+ */
883
+ export const PluginWalkthroughUpdateKeyframeArgs = z.object({
884
+ walkthroughId: z.string().min(1),
885
+ keyframeId: z.string().min(1),
886
+ patch: PluginWalkthroughKeyframePatch,
887
+ })
888
+ export type PluginWalkthroughUpdateKeyframeArgs = z.infer<
889
+ typeof PluginWalkthroughUpdateKeyframeArgs
890
+ >
891
+
892
+ /**
893
+ * Arguments for {@linkcode PluginPresentationWalkthroughsApi.deleteKeyframe}.
894
+ */
895
+ export const PluginWalkthroughDeleteKeyframeArgs = z.object({
896
+ walkthroughId: z.string().min(1),
897
+ keyframeId: z.string().min(1),
898
+ })
899
+ export type PluginWalkthroughDeleteKeyframeArgs = z.infer<
900
+ typeof PluginWalkthroughDeleteKeyframeArgs
901
+ >
902
+
903
+ /**
904
+ * Arguments for {@linkcode PluginPresentationWalkthroughsApi.reorderKeyframe}.
905
+ */
906
+ export const PluginWalkthroughReorderKeyframeArgs = z.object({
907
+ walkthroughId: z.string().min(1),
908
+ keyframeId: z.string().min(1),
909
+ index: z.number().int().min(0),
910
+ })
911
+ export type PluginWalkthroughReorderKeyframeArgs = z.infer<
912
+ typeof PluginWalkthroughReorderKeyframeArgs
913
+ >
914
+
915
+ /**
916
+ * Where the camera is at one moment — an item of
917
+ * {@linkcode PluginWalkthroughSampleResult}.
918
+ *
919
+ * | Property | Type | Description |
920
+ * |---|---|---|
921
+ * | `timeInSeconds` | `number` | The moment (clamped to the walkthrough's duration) |
922
+ * | `eye` | `{ x, y, z }` | Camera position |
923
+ * | `direction` | `{ x, y, z }` | Unit view direction |
924
+ * | `yawInDegrees` | `number` | View direction, degrees clockwise from +z seen from above |
925
+ * | `pitchInDegrees` | `number` | View direction, degrees above level |
926
+ */
927
+ export const PluginWalkthroughSample = z.object({
928
+ timeInSeconds: z.number(),
929
+ eye: Vec3Components,
930
+ direction: Vec3Components,
931
+ yawInDegrees: z.number(),
932
+ pitchInDegrees: z.number(),
933
+ })
934
+ export type PluginWalkthroughSample = z.infer<typeof PluginWalkthroughSample>
935
+
936
+ /**
937
+ * Arguments for {@linkcode PluginPresentationWalkthroughsApi.sample}.
938
+ */
939
+ export const PluginWalkthroughSampleArgs = z.object({
940
+ walkthroughId: z.string().min(1),
941
+ timesInSeconds: z.array(z.number().finite()).min(1).max(500),
942
+ })
943
+ export type PluginWalkthroughSampleArgs = z.infer<
944
+ typeof PluginWalkthroughSampleArgs
945
+ >
946
+
947
+ /**
948
+ * Result of {@linkcode PluginPresentationWalkthroughsApi.sample}.
949
+ */
950
+ export const PluginWalkthroughSampleResult = z.object({
951
+ samples: z.array(PluginWalkthroughSample),
952
+ })
953
+ export type PluginWalkthroughSampleResult = z.infer<
954
+ typeof PluginWalkthroughSampleResult
955
+ >
956
+
957
+ /**
958
+ * What is wrong at a walkthrough issue: `throughGeometry` — the camera passes
959
+ * through the model (a wall, glazing, a column, a slab, a roof or other solid
960
+ * that walking would stop at).
961
+ */
962
+ export const PluginWalkthroughIssueKind = z.enum(["throughGeometry"])
963
+ export type PluginWalkthroughIssueKind = z.infer<
964
+ typeof PluginWalkthroughIssueKind
965
+ >
966
+
967
+ /**
968
+ * A place where a walkthrough's camera passes through the model — an item of
969
+ * {@linkcode PluginWalkthroughListIssuesResult}.
970
+ *
971
+ * | Property | Type | Description |
972
+ * |---|---|---|
973
+ * | `kind` | {@linkcode PluginWalkthroughIssueKind} | What is wrong |
974
+ * | `timeInSeconds` | `number` | When the camera reaches it during playback |
975
+ * | `point` | `{ x, y, z }` | Where the path enters the geometry |
976
+ * | `componentId` | `string \| null` | The component passed through, when it is one |
977
+ */
978
+ export const PluginWalkthroughIssue = z.object({
979
+ kind: PluginWalkthroughIssueKind,
980
+ timeInSeconds: z.number(),
981
+ point: Vec3Components,
982
+ componentId: z.string().nullable(),
983
+ })
984
+ export type PluginWalkthroughIssue = z.infer<typeof PluginWalkthroughIssue>
985
+
986
+ /**
987
+ * Result of {@linkcode PluginPresentationWalkthroughsApi.listIssues}.
988
+ */
989
+ export const PluginWalkthroughListIssuesResult = z.object({
990
+ issues: z.array(PluginWalkthroughIssue),
991
+ })
992
+ export type PluginWalkthroughListIssuesResult = z.infer<
993
+ typeof PluginWalkthroughListIssuesResult
994
+ >
995
+
996
+ /**
997
+ * Options for {@linkcode PluginPresentationWalkthroughsApi.capture}.
998
+ *
999
+ * | Property | Type | Description |
1000
+ * |---|---|---|
1001
+ * | `count` | `number \| undefined` | Frames at evenly spaced moments, first to last, 1 to 12 (default 6) |
1002
+ * | `timesInSeconds` | `number[] \| undefined` | Capture these 1 to 12 moments instead. Give `count` or this, not both |
1003
+ * | `width` | `number \| undefined` | Width of the whole image in pixels, 256 to 2048 (default 1536) |
1004
+ * | `format` | `"png" \| "jpeg" \| undefined` | Image format (default `"jpeg"`) |
1005
+ */
1006
+ export const PluginWalkthroughCaptureOptions = z
1007
+ .object({
1008
+ count: z.number().int().min(1).max(12).optional(),
1009
+ timesInSeconds: z.array(z.number().finite()).min(1).max(12).optional(),
1010
+ width: z.number().int().min(256).max(2048).optional(),
1011
+ format: z.enum(["png", "jpeg"]).optional(),
1012
+ })
1013
+ .strict()
1014
+ .refine(
1015
+ (value) => value.count === undefined || value.timesInSeconds === undefined,
1016
+ "Give count or timesInSeconds, not both",
1017
+ )
1018
+ export type PluginWalkthroughCaptureOptions = z.infer<
1019
+ typeof PluginWalkthroughCaptureOptions
1020
+ >
1021
+
1022
+ /**
1023
+ * Arguments for {@linkcode PluginPresentationWalkthroughsApi.capture}.
1024
+ */
1025
+ export const PluginWalkthroughCaptureArgs = z.object({
1026
+ walkthroughId: z.string().min(1),
1027
+ options: PluginWalkthroughCaptureOptions.optional(),
1028
+ })
1029
+ export type PluginWalkthroughCaptureArgs = z.infer<
1030
+ typeof PluginWalkthroughCaptureArgs
1031
+ >
1032
+
1033
+ /**
1034
+ * Result of {@linkcode PluginPresentationWalkthroughsApi.capture}.
1035
+ *
1036
+ * | Property | Type | Description |
1037
+ * |---|---|---|
1038
+ * | `image` | `string` | The contact sheet as a base64 data URL |
1039
+ * | `frames` | `{ timeInSeconds }[]` | The moment of each frame, left to right and top to bottom |
1040
+ */
1041
+ export const PluginWalkthroughCaptureResult = z.object({
1042
+ image: z.string(),
1043
+ frames: z.array(z.object({ timeInSeconds: z.number() })),
1044
+ })
1045
+ export type PluginWalkthroughCaptureResult = z.infer<
1046
+ typeof PluginWalkthroughCaptureResult
1047
+ >
1048
+
1049
+ /**
1050
+ * Options for {@linkcode PluginPresentationWalkthroughsApi.start}.
1051
+ *
1052
+ * | Property | Type | Description |
1053
+ * |---|---|---|
1054
+ * | `fromTimeInSeconds` | `number \| undefined` | Start this far in (default 0) |
1055
+ */
1056
+ export const PluginWalkthroughStartOptions = z
1057
+ .object({ fromTimeInSeconds: z.number().finite().min(0).optional() })
1058
+ .strict()
1059
+ export type PluginWalkthroughStartOptions = z.infer<
1060
+ typeof PluginWalkthroughStartOptions
1061
+ >
1062
+
1063
+ /**
1064
+ * Arguments for {@linkcode PluginPresentationWalkthroughsApi.start}.
1065
+ */
1066
+ export const PluginWalkthroughStartArgs = z.object({
1067
+ walkthroughId: z.string().min(1),
1068
+ options: PluginWalkthroughStartOptions.optional(),
1069
+ })
1070
+ export type PluginWalkthroughStartArgs = z.infer<
1071
+ typeof PluginWalkthroughStartArgs
1072
+ >
1073
+
1074
+ /**
1075
+ * The playback state — result of {@linkcode PluginPresentationWalkthroughsApi.start},
1076
+ * {@linkcode PluginPresentationWalkthroughsApi.stop} and
1077
+ * {@linkcode PluginPresentationWalkthroughsApi.getState}.
1078
+ *
1079
+ * | Property | Type | Description |
1080
+ * |---|---|---|
1081
+ * | `status` | `"stopped" \| "playing" \| "paused"` | Playback status |
1082
+ * | `walkthroughId` | `string \| null` | The walkthrough playing or paused (`null` when stopped) |
1083
+ * | `timeInSeconds` | `number` | How far through it |
1084
+ * | `durationInSeconds` | `number` | Its duration (0 when stopped) |
1085
+ * | `isRecording` | `boolean` | Whether playback is being recorded by an export |
1086
+ */
1087
+ export const PluginWalkthroughPlaybackState = z.object({
1088
+ status: z.enum(["stopped", "playing", "paused"]),
1089
+ walkthroughId: z.string().nullable(),
1090
+ timeInSeconds: z.number(),
1091
+ durationInSeconds: z.number(),
1092
+ isRecording: z.boolean(),
1093
+ })
1094
+ export type PluginWalkthroughPlaybackState = z.infer<
1095
+ typeof PluginWalkthroughPlaybackState
1096
+ >
1097
+
1098
+ /**
1099
+ * Options for {@linkcode PluginPresentationWalkthroughsApi.export}.
1100
+ *
1101
+ * | Property | Type | Description |
1102
+ * |---|---|---|
1103
+ * | `quality` | `"720p" \| "1080p" \| "4k" \| undefined` | Video size (default `"1080p"`) |
1104
+ * | `framesPerSecond` | `30 \| 60 \| undefined` | Frame rate (default 30) |
1105
+ */
1106
+ export const PluginWalkthroughExportOptions = z
1107
+ .object({
1108
+ quality: z.enum(["720p", "1080p", "4k"]).optional(),
1109
+ framesPerSecond: z.union([z.literal(30), z.literal(60)]).optional(),
1110
+ })
1111
+ .strict()
1112
+ export type PluginWalkthroughExportOptions = z.infer<
1113
+ typeof PluginWalkthroughExportOptions
1114
+ >
1115
+
1116
+ /**
1117
+ * Arguments for {@linkcode PluginPresentationWalkthroughsApi.export}.
1118
+ */
1119
+ export const PluginWalkthroughExportArgs = z.object({
1120
+ walkthroughId: z.string().min(1),
1121
+ options: PluginWalkthroughExportOptions.optional(),
1122
+ })
1123
+ export type PluginWalkthroughExportArgs = z.infer<
1124
+ typeof PluginWalkthroughExportArgs
1125
+ >
1126
+
1127
+ /**
1128
+ * The status of a {@linkcode PluginWalkthroughExportJob}.
1129
+ */
1130
+ export const PluginWalkthroughExportJobStatus = z.enum([
1131
+ "queued",
1132
+ "running",
1133
+ "completed",
1134
+ "failed",
1135
+ "cancelled",
1136
+ ])
1137
+ export type PluginWalkthroughExportJobStatus = z.infer<
1138
+ typeof PluginWalkthroughExportJobStatus
1139
+ >
1140
+
1141
+ /**
1142
+ * What a completed export made.
1143
+ *
1144
+ * | Property | Type | Description |
1145
+ * |---|---|---|
1146
+ * | `shapeId` | `string` | The video shape on the Present canvas |
1147
+ * | `videoUrl` | `string \| null` | Where the uploaded video is stored |
1148
+ * | `width` / `height` | `number` | Video size in pixels |
1149
+ * | `mimeType` | `string` | Video type, e.g. `video/mp4` |
1150
+ * | `durationInSeconds` | `number` | Video length |
1151
+ */
1152
+ export const PluginWalkthroughExportResult = z.object({
1153
+ shapeId: z.string(),
1154
+ videoUrl: z.string().nullable(),
1155
+ width: z.number(),
1156
+ height: z.number(),
1157
+ mimeType: z.string(),
1158
+ durationInSeconds: z.number(),
1159
+ })
1160
+ export type PluginWalkthroughExportResult = z.infer<
1161
+ typeof PluginWalkthroughExportResult
1162
+ >
1163
+
1164
+ /**
1165
+ * A walkthrough video export job.
1166
+ *
1167
+ * | Property | Type | Description |
1168
+ * |---|---|---|
1169
+ * | `jobId` | `string` | The job's id |
1170
+ * | `walkthroughId` | `string` | The walkthrough being exported |
1171
+ * | `status` | {@linkcode PluginWalkthroughExportJobStatus} | Where the job is |
1172
+ * | `createdAt` / `updatedAt` | `number` | Epoch milliseconds |
1173
+ * | `completedAt` | `number \| undefined` | When it completed, failed or was cancelled |
1174
+ * | `progress` | `number \| undefined` | 0 to 1 while running |
1175
+ * | `result` | {@linkcode PluginWalkthroughExportResult} `\| undefined` | What it made, once completed |
1176
+ * | `error` | `string \| undefined` | Why it failed |
1177
+ */
1178
+ export const PluginWalkthroughExportJob = z.object({
1179
+ jobId: z.string(),
1180
+ walkthroughId: z.string(),
1181
+ status: PluginWalkthroughExportJobStatus,
1182
+ createdAt: z.number(),
1183
+ updatedAt: z.number(),
1184
+ completedAt: z.number().optional(),
1185
+ progress: z.number().min(0).max(1).optional(),
1186
+ result: PluginWalkthroughExportResult.optional(),
1187
+ error: z.string().optional(),
1188
+ })
1189
+ export type PluginWalkthroughExportJob = z.infer<
1190
+ typeof PluginWalkthroughExportJob
1191
+ >
1192
+
1193
+ /**
1194
+ * Result of {@linkcode PluginPresentationWalkthroughsApi.listJobs}.
1195
+ */
1196
+ export const PluginWalkthroughListJobsResult = z.object({
1197
+ jobs: z.array(PluginWalkthroughExportJob),
1198
+ })
1199
+ export type PluginWalkthroughListJobsResult = z.infer<
1200
+ typeof PluginWalkthroughListJobsResult
1201
+ >