fragment-tools 0.1.19 → 0.2.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 (102) hide show
  1. package/bin/index.js +1 -0
  2. package/package.json +5 -6
  3. package/src/cli/templates/three-fragment/index.js +6 -6
  4. package/src/cli/templates/three-orthographic/index.js +3 -3
  5. package/src/cli/templates/three-perspective/index.js +3 -3
  6. package/src/client/app/actions/resize.js +14 -0
  7. package/src/client/app/components/HintLoading.svelte +94 -0
  8. package/src/client/app/components/HintPaused.svelte +88 -0
  9. package/src/client/app/components/HintRecord.svelte +62 -0
  10. package/src/client/app/components/IconLocked.svelte +51 -0
  11. package/src/client/app/components/IconTriggers.svelte +48 -0
  12. package/src/client/app/components/Init.svelte +14 -27
  13. package/src/client/app/components/KeyBinding.svelte +3 -6
  14. package/src/client/app/helpers.js +4 -40
  15. package/src/client/app/hooks.js +41 -17
  16. package/src/client/app/inputs/MIDI.js +2 -1
  17. package/src/client/app/lib/canvas-recorder/CanvasRecorder.js +6 -1
  18. package/src/client/app/lib/gl/Renderer.js +1 -0
  19. package/src/client/app/lib/svelte-json-tree/ErrorNode.svelte +28 -0
  20. package/src/client/app/lib/svelte-json-tree/ErrorStack.svelte +31 -0
  21. package/src/client/app/lib/svelte-json-tree/Expandable.svelte +25 -0
  22. package/src/client/app/lib/svelte-json-tree/JSONArrayNode.svelte +38 -0
  23. package/src/client/app/lib/svelte-json-tree/JSONArrow.svelte +47 -0
  24. package/src/client/app/lib/svelte-json-tree/JSONFunctionNode.svelte +114 -0
  25. package/src/client/app/lib/svelte-json-tree/JSONIterableArrayNode.svelte +60 -0
  26. package/src/client/app/lib/svelte-json-tree/JSONIterableMapNode.svelte +87 -0
  27. package/src/client/app/lib/svelte-json-tree/JSONNested.svelte +94 -0
  28. package/src/client/app/lib/svelte-json-tree/JSONNode.svelte +91 -0
  29. package/src/client/app/lib/svelte-json-tree/JSONObjectNode.svelte +40 -0
  30. package/src/client/app/lib/svelte-json-tree/JSONStringNode.svelte +31 -0
  31. package/src/client/app/lib/svelte-json-tree/JSONValueNode.svelte +31 -0
  32. package/src/client/app/lib/svelte-json-tree/PreviewList.svelte +38 -0
  33. package/src/client/app/lib/svelte-json-tree/RegExpNode.svelte +42 -0
  34. package/src/client/app/lib/svelte-json-tree/Root.svelte +75 -0
  35. package/src/client/app/lib/svelte-json-tree/Summary.svelte +9 -0
  36. package/src/client/app/lib/svelte-json-tree/TypedArrayNode.svelte +56 -0
  37. package/src/client/app/lib/svelte-json-tree/index.js +1 -0
  38. package/src/client/app/lib/svelte-json-tree/utils.js +57 -0
  39. package/src/client/app/modules/Console/ConsoleLine.svelte +12 -11
  40. package/src/client/app/modules/Console.svelte +82 -17
  41. package/src/client/app/modules/Exports.svelte +48 -48
  42. package/src/client/app/modules/MidiPanel.svelte +12 -19
  43. package/src/client/app/modules/Monitor.svelte +147 -55
  44. package/src/client/app/modules/Params.svelte +127 -80
  45. package/src/client/app/renderers/2DRenderer.js +1 -0
  46. package/src/client/app/renderers/FragmentRenderer.js +1 -1
  47. package/src/client/app/renderers/P5GLRenderer.js +11 -5
  48. package/src/client/app/renderers/P5Renderer.js +7 -3
  49. package/src/client/app/renderers/THREERenderer.js +42 -79
  50. package/src/client/app/state/Sketch.svelte.js +538 -0
  51. package/src/client/app/state/errors.svelte.js +17 -0
  52. package/src/client/app/state/exports.svelte.js +152 -0
  53. package/src/client/app/state/layout.svelte.js +205 -0
  54. package/src/client/app/state/monitors.svelte.js +36 -0
  55. package/src/client/app/state/renderers.svelte.js +77 -0
  56. package/src/client/app/state/rendering.svelte.js +697 -0
  57. package/src/client/app/state/sketches.svelte.js +73 -0
  58. package/src/client/app/state/utils.svelte.js +65 -0
  59. package/src/client/app/ui/Build.svelte +53 -60
  60. package/src/client/app/ui/ErrorOverlay.svelte +2 -2
  61. package/src/client/app/ui/Field.svelte +63 -189
  62. package/src/client/app/ui/FieldGroup.svelte +4 -5
  63. package/src/client/app/ui/FieldSection.svelte +14 -9
  64. package/src/client/app/ui/FieldSpace.svelte +1 -1
  65. package/src/client/app/ui/FieldTrigger.svelte +86 -84
  66. package/src/client/app/ui/FieldTriggers.svelte +25 -24
  67. package/src/client/app/ui/FloatingParams.svelte +50 -12
  68. package/src/client/app/ui/Layout.svelte +24 -13
  69. package/src/client/app/ui/LayoutColumn.svelte +2 -2
  70. package/src/client/app/ui/LayoutComponent.svelte +86 -195
  71. package/src/client/app/ui/LayoutResizer.svelte +25 -37
  72. package/src/client/app/ui/LayoutRoot.svelte +3 -5
  73. package/src/client/app/ui/LayoutRow.svelte +2 -2
  74. package/src/client/app/ui/LayoutToolbar.svelte +17 -76
  75. package/src/client/app/ui/Module.svelte +31 -35
  76. package/src/client/app/ui/ModuleHeaderAction.svelte +23 -16
  77. package/src/client/app/ui/ModuleHeaderButton.svelte +3 -3
  78. package/src/client/app/ui/ModuleHeaderSelect.svelte +4 -12
  79. package/src/client/app/ui/ModuleRenderer.svelte +84 -22
  80. package/src/client/app/ui/ParamsOutput.svelte +61 -77
  81. package/src/client/app/ui/Preview.svelte +15 -4
  82. package/src/client/app/ui/SelectChevrons.svelte +1 -2
  83. package/src/client/app/ui/SketchRenderer.svelte +89 -701
  84. package/src/client/app/ui/SketchSelect.svelte +14 -49
  85. package/src/client/app/ui/fields/ButtonInput.svelte +14 -11
  86. package/src/client/app/ui/fields/CheckboxInput.svelte +5 -12
  87. package/src/client/app/ui/fields/ColorInput.svelte +46 -121
  88. package/src/client/app/ui/fields/FieldInputRow.svelte +5 -1
  89. package/src/client/app/ui/fields/ImageInput.svelte +14 -14
  90. package/src/client/app/ui/fields/Input.svelte +19 -25
  91. package/src/client/app/ui/fields/IntervalInput.svelte +22 -22
  92. package/src/client/app/ui/fields/NumberInput.svelte +32 -38
  93. package/src/client/app/ui/fields/ProgressInput.svelte +14 -13
  94. package/src/client/app/ui/fields/Select.svelte +34 -45
  95. package/src/client/app/ui/fields/TextInput.svelte +10 -6
  96. package/src/client/app/ui/fields/VectorInput.svelte +25 -30
  97. package/src/client/app/utils/canvas.utils.js +8 -8
  98. package/src/client/app/utils/color.utils.js +46 -13
  99. package/src/client/app/utils/fields.utils.js +1 -1
  100. package/src/client/app/utils/glsl.utils.js +1 -1
  101. package/src/client/app/utils/glslErrors.js +1 -1
  102. package/src/client/main.js +2 -2
@@ -0,0 +1,538 @@
1
+ import { rendering } from './rendering.svelte';
2
+ import {
3
+ deepAssign,
4
+ deepEqual,
5
+ hydrate,
6
+ isFunction,
7
+ isObject,
8
+ persist,
9
+ } from './utils.svelte';
10
+
11
+ const noop = () => {};
12
+
13
+ class Sketch {
14
+ props = $state({});
15
+ canvas = $state(null);
16
+ backgroundColor = $state('inherit');
17
+ propsGroups = $state([]);
18
+ propsFolders = $state([]);
19
+ version = $state(0);
20
+
21
+ constructor({ key, instance, previous }) {
22
+ this.key = key;
23
+ this.instance = instance;
24
+ this.name = this.instance.name ?? this.key;
25
+ this.fps = this.instance.fps;
26
+ this.load = this.instance.load ?? noop;
27
+ this.setup = this.instance.setup ?? this.instance.init ?? noop;
28
+ this.draw = this.instance.draw ?? this.instance.update ?? noop;
29
+ this.needsUpdate = this.instance.needsUpdate ?? noop;
30
+ this.resize = this.instance.resize ?? noop;
31
+ this.duration = this.instance.duration;
32
+ this.exportDir = this.instance.exportDir;
33
+ this.filenamePattern = this.instance.filenamePattern;
34
+ this.backgroundColor = this.instance.backgroundColor ?? 'inherit';
35
+ this.buildConfig = this.instance.buildConfig ?? {};
36
+
37
+ this.recording = null;
38
+ this.params = {};
39
+ this.beforeCapture = [];
40
+ this.beforeRecord = [];
41
+ this.afterCapture = [];
42
+ this.afterRecord = [];
43
+
44
+ this.reconcile(previous);
45
+ }
46
+
47
+ reset() {
48
+ Object.keys(this.props).forEach((key) => {
49
+ this.updateProp(key, this.props[key].__initialValue);
50
+ });
51
+
52
+ this.propsFolders.forEach((fieldgroup) => {
53
+ fieldgroup.collapsed = fieldgroup.__initialCollapsed;
54
+ });
55
+ }
56
+
57
+ reconcile(previous) {
58
+ const instanceProps = this.instance.props ?? {};
59
+ const newProps = {};
60
+ const newPropsGroups = [];
61
+ const newPropsFolders = [];
62
+
63
+ Object.keys(instanceProps).forEach((key) => {
64
+ this.createProp(
65
+ newProps,
66
+ key,
67
+ instanceProps[key],
68
+ newPropsFolders,
69
+ newPropsGroups,
70
+ );
71
+ });
72
+
73
+ const restoreProps = (prevProps) => {
74
+ const prevPropKeys = Object.keys(prevProps);
75
+
76
+ if (prevPropKeys.length > 0) {
77
+ prevPropKeys.forEach((propKey) => {
78
+ let prevProp = prevProps[propKey];
79
+ let newProp = newProps[propKey];
80
+ let instanceProp = instanceProps[propKey];
81
+
82
+ if (newProp) {
83
+ if (
84
+ isObject(newProp.__initialValue) &&
85
+ deepEqual(
86
+ newProp.__initialValue,
87
+ prevProp.__initialValue,
88
+ )
89
+ ) {
90
+ deepAssign(newProp.value, prevProp.value);
91
+ deepAssign(instanceProp.value, prevProp.value);
92
+ newProp.__currentValue = newProp.value;
93
+ } else if (
94
+ newProp.__initialValue === prevProp.__initialValue
95
+ ) {
96
+ newProp.value = prevProp.value;
97
+ newProp.__currentValue = newProp.value;
98
+ instanceProp.value = prevProp.value;
99
+ }
100
+
101
+ newProp.triggers = prevProp.triggers;
102
+
103
+ if (prevProp.params) {
104
+ // reconcile locked VectorInput from UI
105
+ if (prevProp.params.locked !== undefined) {
106
+ newProp.params.locked = prevProp.params.locked;
107
+ }
108
+ }
109
+ }
110
+ });
111
+ }
112
+ };
113
+
114
+ const restorePropsFoldersState = (prevFolders) => {
115
+ const restoreFolder = (prevFolder) => {
116
+ const newFolder = newPropsFolders.find((f) => {
117
+ return prevFolder.id === f.id;
118
+ });
119
+
120
+ if (
121
+ newFolder &&
122
+ newFolder.__initialCollapsed ===
123
+ prevFolder.__initialCollapsed
124
+ ) {
125
+ newFolder.collapsed = prevFolder.collapsed;
126
+ }
127
+ };
128
+
129
+ if (prevFolders.length > 0) {
130
+ prevFolders.forEach((prevFolder) => {
131
+ restoreFolder(prevFolder);
132
+ });
133
+ }
134
+ };
135
+
136
+ if (previous) {
137
+ restoreProps(previous.props);
138
+ restorePropsFoldersState(previous.propsFolders);
139
+ } else {
140
+ const {
141
+ props: savedProps = {},
142
+ propsFolders: savedPropsFolders = [],
143
+ } = hydrate(this.key);
144
+ restoreProps(savedProps);
145
+ restorePropsFoldersState(savedPropsFolders);
146
+ }
147
+
148
+ Object.keys(newProps).forEach((key) => {
149
+ const newProp = newProps[key];
150
+
151
+ // compute hidden after restoring props values
152
+ newProp.hidden = newProp.__hidden();
153
+ });
154
+
155
+ this.props = newProps;
156
+ this.propsGroups = newPropsGroups;
157
+ this.propsFolders = newPropsFolders;
158
+ }
159
+
160
+ createProp(
161
+ target,
162
+ key,
163
+ instanceProp,
164
+ propsFoldersCollection,
165
+ propsGroupsCollection,
166
+ ) {
167
+ const duplicateInitialValue = (value) => {
168
+ if (isFunction(value)) {
169
+ return value;
170
+ }
171
+
172
+ if (isObject(value)) {
173
+ return structuredClone(value);
174
+ }
175
+
176
+ return value;
177
+ };
178
+
179
+ let {
180
+ value,
181
+ params = {},
182
+ triggers = [],
183
+ group,
184
+ folder,
185
+ displayName,
186
+ } = instanceProp;
187
+
188
+ if (value.isColor) {
189
+ value = { r: value.r, g: value.g, b: value.b };
190
+ } else if (value.isVector2) {
191
+ value = { x: value.x, y: value.y };
192
+ } else if (value.isVector3) {
193
+ value = { x: value.x, y: value.y, z: value.z };
194
+ } else if (value.isVector4) {
195
+ value = { x: value.x, y: value.y, z: value.z, w: value.w };
196
+ } else if (value.isQuaternion) {
197
+ value = { x: value.x, y: value.y, z: value.z, w: value.w };
198
+ }
199
+
200
+ let __hidden =
201
+ typeof instanceProp.hidden === 'function'
202
+ ? instanceProp.hidden
203
+ : () => instanceProp.hidden;
204
+
205
+ let initialValue = duplicateInitialValue(value);
206
+
207
+ if (group && !propsGroupsCollection.includes(group)) {
208
+ propsGroupsCollection.push(group);
209
+ }
210
+
211
+ if (folder) {
212
+ this.createPropFolder(folder, propsFoldersCollection, key);
213
+ }
214
+
215
+ let prop = {
216
+ value,
217
+ __initialValue: initialValue,
218
+ __currentValue: value,
219
+ __hidden,
220
+ params: structuredClone(params),
221
+ triggers,
222
+ group,
223
+ folder,
224
+ displayName,
225
+ };
226
+
227
+ target[key] = prop;
228
+
229
+ return prop;
230
+ }
231
+
232
+ updateProp(key, newValue) {
233
+ const prop = this.props[key];
234
+ const instanceProp = this.instance.props[key];
235
+
236
+ if (prop) {
237
+ prop.value = newValue;
238
+ prop.__currentValue = newValue;
239
+ }
240
+
241
+ if (instanceProp) {
242
+ if (isObject(instanceProp.value)) {
243
+ deepAssign(instanceProp.value, newValue);
244
+ } else {
245
+ instanceProp.value = newValue;
246
+ }
247
+
248
+ instanceProp.onChange?.(instanceProp, {
249
+ width: rendering.width,
250
+ height: rendering.height,
251
+ pixelRatio: rendering.pixelRatio,
252
+ canvas: this.canvas,
253
+ });
254
+ }
255
+
256
+ Object.keys(this.props).forEach((key) => {
257
+ this.props[key].hidden = this.props[key].__hidden();
258
+ });
259
+
260
+ this.version++;
261
+ }
262
+
263
+ createPropFolder(folder, collection, key) {
264
+ if (!folder) return undefined;
265
+
266
+ let propFolder;
267
+ let names = folder.split('.');
268
+
269
+ if (names.length > 0) {
270
+ let root;
271
+ let collapsedRegex = /^(.*?)(?:\[collapsed=(true|false)\])?$/;
272
+
273
+ for (let i = 0; i < names.length; i++) {
274
+ let name = names[i];
275
+ let match = name.match(collapsedRegex);
276
+ let displayName = match[1];
277
+ let collapsed = match[2] ? match[2] === 'true' : false;
278
+
279
+ let depth = i;
280
+ let id = [...names].slice(0, i + 1).join('.');
281
+ let parentId = [...names].slice(0, i).join('.');
282
+ let parent =
283
+ depth > 0
284
+ ? collection.find((f) => f.id === parentId)
285
+ : undefined;
286
+
287
+ let fieldgroup = collection.find((f) => f.id === id);
288
+
289
+ if (!fieldgroup) {
290
+ fieldgroup = {
291
+ id,
292
+ type: 'fieldgroup',
293
+ displayName,
294
+ collapsed,
295
+ __initialCollapsed: collapsed,
296
+ children: [],
297
+ parent,
298
+ depth,
299
+ root,
300
+ };
301
+
302
+ if (parent) {
303
+ parent.children.push(fieldgroup);
304
+ }
305
+
306
+ collection.push(fieldgroup);
307
+ }
308
+
309
+ if (i === 0) {
310
+ root = fieldgroup;
311
+ }
312
+
313
+ if (i === names.length - 1) {
314
+ fieldgroup.children.push({
315
+ type: 'field',
316
+ key,
317
+ });
318
+
319
+ propFolder = fieldgroup;
320
+ }
321
+ }
322
+ }
323
+
324
+ return propFolder;
325
+ }
326
+
327
+ updateFolder(folder, collapsed) {
328
+ this.propsFolders.forEach((f, index) => {
329
+ if (f === folder) {
330
+ this.propsFolders[index].collapsed = collapsed;
331
+ }
332
+ });
333
+ }
334
+
335
+ save() {
336
+ persist(this.key, this);
337
+ }
338
+
339
+ dispose() {
340
+ // if (this.renderer && typeof renderer.onDestroyPreview === 'function') {
341
+ // this.renderer.onDestroyPreview({
342
+ // id: this.mountID,
343
+ // container,
344
+ // canvas,
345
+ // });
346
+ // }
347
+ }
348
+
349
+ sync() {
350
+ Object.keys(this.instance.props ?? {}).forEach((key) => {
351
+ const instanceProp = this.instance.props[key];
352
+ const prop = this.props[key];
353
+
354
+ if (!prop) {
355
+ this.createProp(
356
+ this.props,
357
+ key,
358
+ instanceProp,
359
+ this.propsFolders,
360
+ this.propsGroups,
361
+ );
362
+ } else {
363
+ // sync value
364
+ if (
365
+ !isFunction(instanceProp.value) &&
366
+ !deepEqual(instanceProp.value, prop.__currentValue)
367
+ ) {
368
+ this.updateProp(key, structuredClone(instanceProp.value));
369
+ }
370
+
371
+ // sync displayName
372
+ if (instanceProp.displayName !== prop.displayName) {
373
+ prop.displayName = instanceProp.displayName;
374
+ }
375
+
376
+ if (instanceProp.folder !== prop.folder) {
377
+ // if the existing prop already had a folder
378
+ if (prop.folder) {
379
+ const fieldgroup = this.propsFolders.find(
380
+ (f) => f.id === prop.folder,
381
+ );
382
+
383
+ if (!fieldgroup) {
384
+ console.warn(
385
+ `Cannot find fieldgroup from prop.folder`,
386
+ prop.folder,
387
+ );
388
+ console.log(this.propsFolders);
389
+ return;
390
+ }
391
+
392
+ const { children } = fieldgroup;
393
+
394
+ const childIndex = children.findIndex(
395
+ (c) => c.key === key,
396
+ );
397
+ fieldgroup.children.splice(childIndex, 1);
398
+
399
+ const removeFolderIfNeeded = (fieldgroup) => {
400
+ if (fieldgroup.children.length === 0) {
401
+ const currentFolderIndex =
402
+ this.propsFolders.findIndex(
403
+ (c) => c === fieldgroup,
404
+ );
405
+ this.propsFolders.splice(currentFolderIndex, 1);
406
+
407
+ const { parent } = fieldgroup;
408
+
409
+ if (parent) {
410
+ const childIndex =
411
+ parent.children.findIndex(
412
+ (c) => c.id === fieldgroup.id,
413
+ );
414
+ parent.children.splice(childIndex, 1);
415
+ removeFolderIfNeeded(fieldgroup.parent);
416
+ }
417
+ }
418
+ };
419
+
420
+ removeFolderIfNeeded(fieldgroup);
421
+ }
422
+
423
+ if (instanceProp.folder) {
424
+ this.createPropFolder(
425
+ instanceProp.folder,
426
+ this.propsFolders,
427
+ key,
428
+ );
429
+ prop.folder = instanceProp.folder;
430
+ } else {
431
+ prop.folder = undefined;
432
+ }
433
+ }
434
+
435
+ // sync hidden
436
+ prop.hidden = prop.__hidden();
437
+
438
+ // sync params
439
+ if (instanceProp.params) {
440
+ for (const paramKey in instanceProp.params) {
441
+ const instanceParam = instanceProp.params[paramKey];
442
+ const param = prop.params[paramKey];
443
+ let needsUpdate = false;
444
+
445
+ if (isObject(instanceParam)) {
446
+ Object.keys(instanceParam).forEach((key) => {
447
+ if (isObject(instanceParam[key])) {
448
+ Object.keys(instanceParam[key]).forEach(
449
+ (k) => {
450
+ if (
451
+ instanceParam[key][k] !==
452
+ param[key][k]
453
+ ) {
454
+ needsUpdate = true;
455
+ }
456
+ },
457
+ );
458
+ } else if (instanceParam[key] !== param[key]) {
459
+ needsUpdate = true;
460
+ }
461
+ });
462
+ } else if (instanceParam !== param) {
463
+ needsUpdate = true;
464
+ }
465
+
466
+ if (needsUpdate) {
467
+ if (needsUpdate) {
468
+ prop.params[paramKey] =
469
+ structuredClone(instanceParam);
470
+ }
471
+ }
472
+ }
473
+ }
474
+
475
+ Object.keys(prop.params).forEach((paramKey) => {
476
+ if (!(paramKey in (instanceProp.params ?? {}))) {
477
+ delete prop.params[paramKey];
478
+ }
479
+ });
480
+ }
481
+ });
482
+
483
+ Object.keys(this.props).forEach((key) => {
484
+ if (!(key in (this.instance.props ?? {}))) {
485
+ delete this.props[key];
486
+ }
487
+ });
488
+ }
489
+
490
+ onBeforeCapture(fn) {
491
+ this.beforeCapture.push(fn);
492
+ }
493
+
494
+ onBeforeRecord(fn) {
495
+ this.beforeRecord.push(fn);
496
+ }
497
+
498
+ onAfterCapture(fn) {
499
+ this.afterCapture.push(fn);
500
+ }
501
+
502
+ onAfterRecord(fn) {
503
+ this.afterRecord.push(fn);
504
+ }
505
+
506
+ toJSON() {
507
+ return {
508
+ props: this.props,
509
+ propsFolders: this.propsFolders.map(
510
+ ({
511
+ id,
512
+ displayName,
513
+ depth,
514
+ parent,
515
+ collapsed,
516
+ __initialCollapsed,
517
+ }) => ({
518
+ id,
519
+ displayName,
520
+ depth,
521
+ collapsed,
522
+ __initialCollapsed,
523
+ parent: this.propsFolders.findIndex(
524
+ (f) =>
525
+ f.displayName === parent?.displayName &&
526
+ f.depth === parent?.depth,
527
+ ),
528
+ }),
529
+ ),
530
+ };
531
+ }
532
+
533
+ get backgroundColor() {
534
+ return this.instance.backgroundColor;
535
+ }
536
+ }
537
+
538
+ export default Sketch;
@@ -0,0 +1,17 @@
1
+ import { SvelteMap } from 'svelte/reactivity';
2
+
3
+ export let errors = new SvelteMap();
4
+
5
+ export function displayError(error, context) {
6
+ errors.set(context, error);
7
+ }
8
+
9
+ export function clearError(context) {
10
+ if (errors.has(context)) {
11
+ errors.delete(context);
12
+ }
13
+ }
14
+
15
+ export function clearErrors() {
16
+ errors.clear();
17
+ }
@@ -0,0 +1,152 @@
1
+ import { screenshotCanvas, recordCanvas } from '../utils/canvas.utils';
2
+ import { hydrate, persist } from './utils.svelte';
3
+
4
+ export const IMAGE_ENCODINGS = ['png', 'jpeg', 'webp'];
5
+
6
+ export const VIDEO_FORMATS = {
7
+ FRAMES: 'frames',
8
+ MP4: 'mp4',
9
+ GIF: 'gif',
10
+ WEBM: 'webm',
11
+ };
12
+
13
+ class Exports {
14
+ imageEncoding = $state(IMAGE_ENCODINGS[0]);
15
+ videoFormat = $state(Object.values(VIDEO_FORMATS)[0]);
16
+ pixelsPerInch = $state(72);
17
+ framerate = $state(60);
18
+ useDuration = $state(true);
19
+ duration = $state(1);
20
+ loopCount = $state(1);
21
+ imageQuality = $state(100);
22
+ videoQuality = $state(100);
23
+ imageCount = $state(1);
24
+ recording = $state(false);
25
+ capturing = $state(false);
26
+ imageCollapsed = $state(false);
27
+ videoCollapsed = $state(false);
28
+
29
+ constructor() {
30
+ this.key = `exports`;
31
+ $effect.root(() => {
32
+ $effect(() => {
33
+ if (!__BUILD__) {
34
+ persist(this.key, {
35
+ imageEncoding: this.imageEncoding,
36
+ videoFormat: this.videoFormat,
37
+ pixelsPerInch: this.pixelsPerInch,
38
+ framerate: this.framerate,
39
+ useDuration: this.useDuration,
40
+ duration: this.duration,
41
+ loopCount: this.loopCount,
42
+ imageQuality: this.imageQuality,
43
+ videoQuality: this.videoQuality,
44
+ imageCount: this.imageCount,
45
+ videoCollapsed: this.videoCollapsed,
46
+ imageCollapsed: this.imageCollapsed,
47
+ });
48
+ }
49
+ });
50
+ });
51
+
52
+ hydrate(this.key, this);
53
+ }
54
+
55
+ async screenshot(
56
+ canvas,
57
+ {
58
+ count = this.imageCount,
59
+ encoding = this.imageEncoding,
60
+ quality = this.imageQuality,
61
+ pixelsPerInch = this.pixelsPerInch,
62
+ filename,
63
+ pattern,
64
+ exportDir,
65
+ params = {},
66
+ onStart = () => {},
67
+ onComplete = () => {},
68
+ onBeforeCapture = () => {},
69
+ onAfterCapture = () => {},
70
+ } = {},
71
+ ) {
72
+ const captureParams = {
73
+ encoding,
74
+ quality,
75
+ pixelsPerInch,
76
+ count,
77
+ };
78
+
79
+ onStart(captureParams);
80
+
81
+ for (let i = 0; i < count; i++) {
82
+ onBeforeCapture(captureParams);
83
+
84
+ await screenshotCanvas(canvas, {
85
+ filename,
86
+ pattern,
87
+ exportDir,
88
+ index: count > 1 ? i : undefined,
89
+ params,
90
+ encoding,
91
+ quality,
92
+ pixelsPerInch,
93
+ });
94
+
95
+ onAfterCapture(captureParams);
96
+ }
97
+
98
+ onComplete(captureParams);
99
+ }
100
+
101
+ record(
102
+ canvas,
103
+ {
104
+ framerate = this.framerate,
105
+ format = this.videoFormat,
106
+ imageEncoding = this.imageEncoding,
107
+ quality = this.videoQuality,
108
+ duration,
109
+ filename,
110
+ pattern,
111
+ exportDir,
112
+ params = {},
113
+ onStart = () => {},
114
+ onTick = () => {},
115
+ onComplete = () => {},
116
+ onBeforeRecord = () => {},
117
+ onAfterRecord = () => {},
118
+ },
119
+ ) {
120
+ const recordParams = {
121
+ framerate,
122
+ format,
123
+ imageEncoding,
124
+ quality,
125
+ duration,
126
+ };
127
+
128
+ return recordCanvas(canvas, {
129
+ params,
130
+ filename,
131
+ exportDir,
132
+ pattern,
133
+ onTick,
134
+ framerate,
135
+ format,
136
+ imageEncoding,
137
+ quality,
138
+ duration: duration * this.loopCount,
139
+ onStart: () => {
140
+ onStart(recordParams);
141
+ onBeforeRecord(recordParams);
142
+ },
143
+ onComplete: () => {
144
+ this.recording = false;
145
+ onAfterRecord(recordParams);
146
+ onComplete(recordParams);
147
+ },
148
+ });
149
+ }
150
+ }
151
+
152
+ export let exports = new Exports();