fragment-tools 0.2.12 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +12 -11
- package/src/cli/build.js +1 -0
- package/src/cli/create.js +22 -4
- package/src/cli/createConfig.js +8 -3
- package/src/cli/getEntries.js +10 -1
- package/src/cli/plugins/hot-shader-replacement.js +54 -16
- package/src/cli/plugins/save.js +97 -38
- package/src/cli/prompts.js +89 -36
- package/src/cli/run.js +1 -1
- package/src/client/app/actions/resize.js +8 -1
- package/src/client/app/attachments/draggable.js +93 -0
- package/src/client/app/client.js +90 -18
- package/src/client/app/components/IconFlip.svelte +46 -0
- package/src/client/app/hooks.js +25 -1
- package/src/client/app/lib/canvas-recorder/CanvasRecorder.js +95 -3
- package/src/client/app/lib/canvas-recorder/FrameRecorder.js +45 -3
- package/src/client/app/lib/canvas-recorder/GIFRecorder.js +72 -13
- package/src/client/app/lib/canvas-recorder/MediaBunnyRecorder.js +43 -9
- package/src/client/app/lib/canvas-recorder/utils.js +18 -9
- package/src/client/app/renderers/2DRenderer.js +20 -16
- package/src/client/app/renderers/FragmentRenderer.js +1 -1
- package/src/client/app/renderers/P5GLRenderer.js +14 -6
- package/src/client/app/renderers/P5Renderer.js +9 -1
- package/src/client/app/renderers/THREERenderer.js +62 -48
- package/src/client/app/state/Sketch.svelte.js +149 -9
- package/src/client/app/state/errors.svelte.js +19 -0
- package/src/client/app/state/exports.svelte.js +14 -1
- package/src/client/app/state/rendering.svelte.js +47 -0
- package/src/client/app/state/sketches.svelte.js +43 -7
- package/src/client/app/state/utils.svelte.js +49 -0
- package/src/client/app/ui/Field.svelte +6 -1
- package/src/client/app/ui/FieldSection.svelte +4 -4
- package/src/client/app/ui/ParamsOutput.svelte +1 -1
- package/src/client/app/ui/SketchRenderer.svelte +16 -0
- package/src/client/app/ui/fields/ButtonInput.svelte +2 -0
- package/src/client/app/ui/fields/CheckboxInput.svelte +13 -11
- package/src/client/app/ui/fields/ColorInput.svelte +16 -11
- package/src/client/app/ui/fields/GradientInput.svelte +607 -0
- package/src/client/app/ui/fields/Input.svelte +10 -6
- package/src/client/app/ui/fields/IntervalInput.svelte +27 -35
- package/src/client/app/ui/fields/NumberInput.svelte +51 -13
- package/src/client/app/ui/fields/PaletteInput.svelte +181 -0
- package/src/client/app/ui/fields/ProgressInput.svelte +44 -16
- package/src/client/app/ui/fields/TextareaInput.svelte +10 -10
- package/src/client/app/utils/canvas.utils.js +105 -28
- package/src/client/app/utils/color.utils.js +74 -17
- package/src/client/app/utils/fields.utils.js +68 -26
- package/src/client/app/utils/file.utils.js +86 -31
- package/src/client/app/utils/glsl.utils.js +11 -2
- package/src/client/app/utils/glslErrors.js +31 -21
- package/src/client/app/utils/index.js +28 -12
- package/src/client/main.js +7 -1
- package/src/types/global.d.ts +143 -0
- package/src/types/props.d.ts +40 -15
- package/tsconfig.json +1 -1
|
@@ -1,37 +1,55 @@
|
|
|
1
1
|
import { WebGLRenderer, Scene } from 'three';
|
|
2
|
-
import { client } from '
|
|
2
|
+
import { client } from '../client';
|
|
3
3
|
import { getShaderPath } from '../utils/glsl.utils';
|
|
4
4
|
import { clearError } from '../state/errors.svelte';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* @typedef {object}
|
|
7
|
+
* @typedef {object} MountParamsTHREERenderer
|
|
8
8
|
* @property {HTMLCanvasElement} canvas
|
|
9
|
-
* @property {
|
|
10
|
-
* @property {
|
|
9
|
+
* @property {Scene} scene
|
|
10
|
+
* @property {WebGLRenderer} renderer
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* @typedef {object}
|
|
14
|
+
* @typedef {object} PreviewTHREERenderer
|
|
15
15
|
* @property {number} id
|
|
16
|
-
* @property {
|
|
17
|
-
* @property {
|
|
18
|
-
* @property {
|
|
16
|
+
* @property {Scene} scene
|
|
17
|
+
* @property {WebGLRenderer} renderer
|
|
18
|
+
* @property {boolean} rendered
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {object} PreviewParams
|
|
23
|
+
* @property {number} id
|
|
24
|
+
* @property {HTMLCanvasElement} canvas
|
|
25
|
+
* @property {HTMLDivElement} container
|
|
26
|
+
* @property {number} width
|
|
27
|
+
* @property {number} height
|
|
28
|
+
* @property {number} pixelRatio
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Object} ShaderWarning
|
|
33
|
+
* @property {string} type - Warning type
|
|
34
|
+
* @property {string} importer - File that imported the shader
|
|
35
|
+
* @property {string} message - Warning message
|
|
36
|
+
* @property {Object} location - Location of the warning
|
|
37
|
+
* @property {string} location.lineText - The line of code with the warning
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {Object} ShaderUpdate
|
|
42
|
+
* @property {ShaderWarning[]} [warnings] - Array of shader warnings
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/** @type {PreviewTHREERenderer[]} */
|
|
22
46
|
let previews = [];
|
|
23
47
|
|
|
24
48
|
/**
|
|
25
|
-
* @param {
|
|
26
|
-
* @
|
|
27
|
-
* @param {HTMLCanvasElement} params.canvas
|
|
28
|
-
* @param {HTMLDivElement} params.container
|
|
29
|
-
* @param {number} params.width
|
|
30
|
-
* @param {number} params.height
|
|
31
|
-
* @param {number} params.pixelRatio
|
|
32
|
-
* @returns {MountParamsThreeRenderer}
|
|
49
|
+
* @param {PreviewParams} params
|
|
50
|
+
* @returns {MountParamsTHREERenderer}
|
|
33
51
|
*/
|
|
34
|
-
export let onMountPreview = ({ id
|
|
52
|
+
export let onMountPreview = ({ id }) => {
|
|
35
53
|
let renderer = new WebGLRenderer({ antialias: true });
|
|
36
54
|
|
|
37
55
|
const render = renderer.render;
|
|
@@ -59,10 +77,7 @@ export let onMountPreview = ({ id, canvas }) => {
|
|
|
59
77
|
};
|
|
60
78
|
|
|
61
79
|
/**
|
|
62
|
-
* @param {
|
|
63
|
-
* @param {number} params.id
|
|
64
|
-
* @param {HTMLCanvasElement} params.canvas
|
|
65
|
-
* @param {HTMLDivElement} params.container
|
|
80
|
+
* @param {PreviewParams} params
|
|
66
81
|
*/
|
|
67
82
|
export let onBeforeUpdatePreview = ({ id }) => {
|
|
68
83
|
const preview = previews.find((p) => p.id === id);
|
|
@@ -73,10 +88,7 @@ export let onBeforeUpdatePreview = ({ id }) => {
|
|
|
73
88
|
};
|
|
74
89
|
|
|
75
90
|
/**
|
|
76
|
-
* @param {
|
|
77
|
-
* @param {number} params.id
|
|
78
|
-
* @param {HTMLCanvasElement} params.canvas
|
|
79
|
-
* @param {HTMLDivElement} params.container
|
|
91
|
+
* @param {PreviewParams} params
|
|
80
92
|
*/
|
|
81
93
|
export let onAfterUpdatePreview = ({ id }) => {
|
|
82
94
|
const preview = previews.find((p) => p.id === id);
|
|
@@ -94,14 +106,9 @@ export let onAfterUpdatePreview = ({ id }) => {
|
|
|
94
106
|
};
|
|
95
107
|
|
|
96
108
|
/**
|
|
97
|
-
* @param {
|
|
98
|
-
* @param {number} params.id
|
|
99
|
-
* @param {HTMLCanvasElement} params.canvas
|
|
100
|
-
* @param {number} params.width
|
|
101
|
-
* @param {number} params.height
|
|
102
|
-
* @param {number} params.pixelRatio
|
|
109
|
+
* @param {PreviewParams} params
|
|
103
110
|
*/
|
|
104
|
-
export let onResizePreview = ({ id, width, height, pixelRatio }) => {
|
|
111
|
+
export let onResizePreview = ({ id, width, height, pixelRatio, canvas }) => {
|
|
105
112
|
const preview = previews.find((p) => p.id === id);
|
|
106
113
|
|
|
107
114
|
if (preview) {
|
|
@@ -112,10 +119,7 @@ export let onResizePreview = ({ id, width, height, pixelRatio }) => {
|
|
|
112
119
|
};
|
|
113
120
|
|
|
114
121
|
/**
|
|
115
|
-
* @param {
|
|
116
|
-
* @param {number} params.id
|
|
117
|
-
* @param {HTMLCanvasElement} params.canvas
|
|
118
|
-
* @param {HTMLElement} params.container
|
|
122
|
+
* @param {PreviewParams} params
|
|
119
123
|
*/
|
|
120
124
|
export let onDestroyPreview = ({ id }) => {
|
|
121
125
|
const previewIndex = previews.findIndex((p) => p.id === id);
|
|
@@ -131,8 +135,16 @@ export let onDestroyPreview = ({ id }) => {
|
|
|
131
135
|
};
|
|
132
136
|
|
|
133
137
|
/* HOT SHADER RELOADING */
|
|
138
|
+
/** @type {ShaderUpdate[]} */
|
|
134
139
|
let _shaderUpdates = [];
|
|
135
140
|
|
|
141
|
+
function clearShaderUpdates() {
|
|
142
|
+
_shaderUpdates = [];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @param {Scene} scene
|
|
147
|
+
*/
|
|
136
148
|
function handleHotShaderUpdate(scene) {
|
|
137
149
|
if (_shaderUpdates.length > 0) {
|
|
138
150
|
const verifyMaterial = (material) => {
|
|
@@ -173,20 +185,22 @@ function handleHotShaderUpdate(scene) {
|
|
|
173
185
|
}
|
|
174
186
|
}
|
|
175
187
|
|
|
176
|
-
function clearShaderUpdates() {
|
|
177
|
-
_shaderUpdates = [];
|
|
178
|
-
}
|
|
179
|
-
|
|
180
188
|
if (import.meta.hot) {
|
|
181
|
-
import.meta.hot.on('sketch-update', (
|
|
189
|
+
import.meta.hot.on('sketch-update', () => {
|
|
182
190
|
clearShaderUpdates();
|
|
183
191
|
});
|
|
184
192
|
}
|
|
185
193
|
|
|
186
|
-
client.on(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
194
|
+
client.on(
|
|
195
|
+
'shader-update',
|
|
196
|
+
/**
|
|
197
|
+
* @param {ShaderUpdate[]} shaderUpdates
|
|
198
|
+
*/
|
|
199
|
+
(shaderUpdates) => {
|
|
200
|
+
previews.forEach((preview) => {
|
|
201
|
+
clearError(preview.renderer.getContext().__uuid);
|
|
202
|
+
});
|
|
190
203
|
|
|
191
|
-
|
|
192
|
-
}
|
|
204
|
+
_shaderUpdates = shaderUpdates;
|
|
205
|
+
},
|
|
206
|
+
);
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
deepClone,
|
|
6
6
|
deepEqual,
|
|
7
7
|
hydrate,
|
|
8
|
+
isDataURL,
|
|
8
9
|
isFunction,
|
|
9
10
|
isObject,
|
|
10
11
|
persist,
|
|
@@ -12,11 +13,58 @@ import {
|
|
|
12
13
|
|
|
13
14
|
const noop = () => {};
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* @typedef InstanceProp
|
|
18
|
+
* @property {any} value
|
|
19
|
+
* @property {Record<string, any>} params
|
|
20
|
+
* @property {any[]} triggers
|
|
21
|
+
* @property {string|undefined} group
|
|
22
|
+
* @property {string|undefined} type
|
|
23
|
+
* @property {string|undefined} folder
|
|
24
|
+
* @property {string|null|undefined} displayName
|
|
25
|
+
* @property {boolean|(() => boolean)} hidden
|
|
26
|
+
* @property {boolean|(() => boolean)} disabled
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef SketchProp
|
|
31
|
+
* @property {any} value
|
|
32
|
+
* @property {any} __initialValue
|
|
33
|
+
* @property {any} __currentValue
|
|
34
|
+
* @property {Record<string, any>} params
|
|
35
|
+
* @property {any[]} triggers
|
|
36
|
+
* @property {string|undefined} group
|
|
37
|
+
* @property {string|undefined} type
|
|
38
|
+
* @property {string|undefined} folder
|
|
39
|
+
* @property {string|null|undefined} displayName
|
|
40
|
+
* @property {boolean} hidden
|
|
41
|
+
* @property {(() => boolean)} __hidden
|
|
42
|
+
* @property {boolean} disabled
|
|
43
|
+
* @property {(() => boolean)} __disabled
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @typedef SketchPropFolder
|
|
48
|
+
* @property {string} id
|
|
49
|
+
* @property {string} displayName
|
|
50
|
+
* @property {string} displayName
|
|
51
|
+
* @property {string} rootId
|
|
52
|
+
* @property {(SketchPropFolder | { type: 'field', key: string })[]} children
|
|
53
|
+
* @property {SketchPropFolder|undefined} parent
|
|
54
|
+
* @property {'fieldgroup'} type
|
|
55
|
+
* @property {number} depth
|
|
56
|
+
* @property {boolean} collapsed
|
|
57
|
+
* @property {boolean} hidden
|
|
58
|
+
* @property {boolean} __initialCollapsed
|
|
59
|
+
*/
|
|
60
|
+
|
|
15
61
|
class Sketch {
|
|
62
|
+
/** @type {Record<string, SketchProp} */
|
|
16
63
|
props = $state({});
|
|
17
64
|
canvas = $state(null);
|
|
18
65
|
backgroundColor = $state('inherit');
|
|
19
66
|
propsGroups = $state([]);
|
|
67
|
+
/** @type {SketchPropFolder[]} */
|
|
20
68
|
propsFolders = $state([]);
|
|
21
69
|
version = $state(0);
|
|
22
70
|
|
|
@@ -38,9 +86,13 @@ class Sketch {
|
|
|
38
86
|
|
|
39
87
|
this.recording = null;
|
|
40
88
|
this.params = {};
|
|
89
|
+
/** @type {(() => {})[]} */
|
|
41
90
|
this.beforeCapture = [];
|
|
91
|
+
/** @type {(() => {})[]} */
|
|
42
92
|
this.beforeRecord = [];
|
|
93
|
+
/** @type {(() => {})[]} */
|
|
43
94
|
this.afterCapture = [];
|
|
95
|
+
/** @type {(() => {})[]} */
|
|
44
96
|
this.afterRecord = [];
|
|
45
97
|
|
|
46
98
|
this.reconcile(previous);
|
|
@@ -58,8 +110,10 @@ class Sketch {
|
|
|
58
110
|
|
|
59
111
|
reconcile(previous) {
|
|
60
112
|
const instanceProps = this.instance.props ?? {};
|
|
113
|
+
/** @type {Record<string, SketchProp>} */
|
|
61
114
|
const newProps = {};
|
|
62
115
|
const newPropsGroups = [];
|
|
116
|
+
/** @type {SketchPropFolder[]} */
|
|
63
117
|
const newPropsFolders = [];
|
|
64
118
|
|
|
65
119
|
Object.keys(instanceProps).forEach((key) => {
|
|
@@ -117,7 +171,15 @@ class Sketch {
|
|
|
117
171
|
}
|
|
118
172
|
};
|
|
119
173
|
|
|
174
|
+
/**
|
|
175
|
+
*
|
|
176
|
+
* @param {SketchPropFolder[]} prevFolders
|
|
177
|
+
*/
|
|
120
178
|
const restorePropsFoldersState = (prevFolders) => {
|
|
179
|
+
/**
|
|
180
|
+
*
|
|
181
|
+
* @param {SketchPropFolder} prevFolder
|
|
182
|
+
*/
|
|
121
183
|
const restoreFolder = (prevFolder) => {
|
|
122
184
|
const newFolder = newPropsFolders.find((f) => {
|
|
123
185
|
return prevFolder.id === f.id;
|
|
@@ -163,6 +225,15 @@ class Sketch {
|
|
|
163
225
|
this.propsFolders = newPropsFolders;
|
|
164
226
|
}
|
|
165
227
|
|
|
228
|
+
/**
|
|
229
|
+
*
|
|
230
|
+
* @param {Record<string, any>} target
|
|
231
|
+
* @param {string} key
|
|
232
|
+
* @param {InstanceProp} instanceProp
|
|
233
|
+
* @param {*} propsFoldersCollection
|
|
234
|
+
* @param {*} propsGroupsCollection
|
|
235
|
+
* @returns
|
|
236
|
+
*/
|
|
166
237
|
createProp(
|
|
167
238
|
target,
|
|
168
239
|
key,
|
|
@@ -208,7 +279,11 @@ class Sketch {
|
|
|
208
279
|
}
|
|
209
280
|
|
|
210
281
|
if (folder) {
|
|
211
|
-
this
|
|
282
|
+
// this prevent references from breaking when using Proxies
|
|
283
|
+
const propsFoldersCollectionCopy = [...propsFoldersCollection];
|
|
284
|
+
this.createPropFolder(folder, propsFoldersCollectionCopy, key);
|
|
285
|
+
propsFoldersCollection.length = 0;
|
|
286
|
+
propsFoldersCollection.push(...propsFoldersCollectionCopy);
|
|
212
287
|
}
|
|
213
288
|
|
|
214
289
|
let prop = {
|
|
@@ -232,6 +307,11 @@ class Sketch {
|
|
|
232
307
|
return prop;
|
|
233
308
|
}
|
|
234
309
|
|
|
310
|
+
/**
|
|
311
|
+
*
|
|
312
|
+
* @param {string} key
|
|
313
|
+
* @param {any} newValue
|
|
314
|
+
*/
|
|
235
315
|
updateProp(key, newValue) {
|
|
236
316
|
const prop = this.props[key];
|
|
237
317
|
const instanceProp = this.instance.props[key];
|
|
@@ -243,7 +323,13 @@ class Sketch {
|
|
|
243
323
|
|
|
244
324
|
if (instanceProp) {
|
|
245
325
|
if (!deepEqual(instanceProp.value, newValue)) {
|
|
246
|
-
if (
|
|
326
|
+
if (
|
|
327
|
+
Array.isArray(instanceProp.value) &&
|
|
328
|
+
Array.isArray(newValue)
|
|
329
|
+
) {
|
|
330
|
+
instanceProp.value.length = 0;
|
|
331
|
+
instanceProp.value.push(...newValue);
|
|
332
|
+
} else if (isObject(instanceProp.value) && isObject(newValue)) {
|
|
247
333
|
deepAssign(instanceProp.value, newValue);
|
|
248
334
|
} else {
|
|
249
335
|
instanceProp.value = newValue;
|
|
@@ -266,6 +352,13 @@ class Sketch {
|
|
|
266
352
|
this.version++;
|
|
267
353
|
}
|
|
268
354
|
|
|
355
|
+
/**
|
|
356
|
+
*
|
|
357
|
+
* @param {string} folder
|
|
358
|
+
* @param {SketchPropFolder[]} collection
|
|
359
|
+
* @param {string} key
|
|
360
|
+
* @returns {SketchPropFolder | undefined}
|
|
361
|
+
*/
|
|
269
362
|
createPropFolder(folder, collection, key) {
|
|
270
363
|
if (!folder) return undefined;
|
|
271
364
|
|
|
@@ -316,7 +409,7 @@ class Sketch {
|
|
|
316
409
|
collection.push(fieldgroup);
|
|
317
410
|
}
|
|
318
411
|
|
|
319
|
-
if (isCurrent) {
|
|
412
|
+
if (fieldgroup && isCurrent) {
|
|
320
413
|
fieldgroup.children.push({
|
|
321
414
|
type: 'field',
|
|
322
415
|
key,
|
|
@@ -330,6 +423,11 @@ class Sketch {
|
|
|
330
423
|
return propFolder;
|
|
331
424
|
}
|
|
332
425
|
|
|
426
|
+
/**
|
|
427
|
+
*
|
|
428
|
+
* @param {SketchPropFolder} folder
|
|
429
|
+
* @param {boolean} collapsed
|
|
430
|
+
*/
|
|
333
431
|
updateFolder(folder, collapsed) {
|
|
334
432
|
this.propsFolders.forEach((f, index) => {
|
|
335
433
|
if (f.id === folder.id) {
|
|
@@ -405,17 +503,20 @@ class Sketch {
|
|
|
405
503
|
);
|
|
406
504
|
fieldgroup.children.splice(childIndex, 1);
|
|
407
505
|
|
|
506
|
+
/**
|
|
507
|
+
*
|
|
508
|
+
* @param {SketchPropFolder} fieldgroup
|
|
509
|
+
*/
|
|
408
510
|
const removeFolderIfNeeded = (fieldgroup) => {
|
|
409
511
|
if (fieldgroup.children.length === 0) {
|
|
410
512
|
const currentFolderIndex =
|
|
411
513
|
this.propsFolders.findIndex(
|
|
412
|
-
(c) => c === fieldgroup,
|
|
514
|
+
(c) => c.id === fieldgroup.id,
|
|
413
515
|
);
|
|
414
516
|
this.propsFolders.splice(currentFolderIndex, 1);
|
|
415
517
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (parent) {
|
|
518
|
+
if (fieldgroup && fieldgroup.parent) {
|
|
519
|
+
const { parent } = fieldgroup;
|
|
419
520
|
const childIndex =
|
|
420
521
|
parent.children.findIndex(
|
|
421
522
|
(c) => c.id === fieldgroup.id,
|
|
@@ -430,11 +531,14 @@ class Sketch {
|
|
|
430
531
|
}
|
|
431
532
|
|
|
432
533
|
if (instanceProp.folder) {
|
|
534
|
+
// this prevent references from breaking when using Proxies
|
|
535
|
+
const propsFoldersCopy = [...this.propsFolders];
|
|
433
536
|
this.createPropFolder(
|
|
434
537
|
instanceProp.folder,
|
|
435
|
-
|
|
538
|
+
propsFoldersCopy,
|
|
436
539
|
key,
|
|
437
540
|
);
|
|
541
|
+
this.propsFolders = propsFoldersCopy;
|
|
438
542
|
prop.folder = instanceProp.folder;
|
|
439
543
|
} else {
|
|
440
544
|
prop.folder = undefined;
|
|
@@ -498,25 +602,61 @@ class Sketch {
|
|
|
498
602
|
});
|
|
499
603
|
}
|
|
500
604
|
|
|
605
|
+
/**
|
|
606
|
+
* @param {() => void} fn
|
|
607
|
+
*/
|
|
501
608
|
onBeforeCapture(fn) {
|
|
502
609
|
this.beforeCapture.push(fn);
|
|
503
610
|
}
|
|
504
611
|
|
|
612
|
+
/**
|
|
613
|
+
*
|
|
614
|
+
* @param {() => void} fn
|
|
615
|
+
*/
|
|
505
616
|
onBeforeRecord(fn) {
|
|
506
617
|
this.beforeRecord.push(fn);
|
|
507
618
|
}
|
|
508
619
|
|
|
620
|
+
/**
|
|
621
|
+
* @param {() => void} fn
|
|
622
|
+
*/
|
|
509
623
|
onAfterCapture(fn) {
|
|
510
624
|
this.afterCapture.push(fn);
|
|
511
625
|
}
|
|
512
626
|
|
|
627
|
+
/**
|
|
628
|
+
* @param {() => void} fn
|
|
629
|
+
*/
|
|
513
630
|
onAfterRecord(fn) {
|
|
514
631
|
this.afterRecord.push(fn);
|
|
515
632
|
}
|
|
516
633
|
|
|
517
634
|
toJSON() {
|
|
635
|
+
/**
|
|
636
|
+
* @typedef SketchPropJSON
|
|
637
|
+
* @property {any} value
|
|
638
|
+
* @property {Record<string, any>} params
|
|
639
|
+
* @property {any[]} triggers
|
|
640
|
+
* @property {any} __initialValue
|
|
641
|
+
* @property {any} __currentValue
|
|
642
|
+
*/
|
|
643
|
+
/** @type {Record<string, SketchPropJSON>} */
|
|
644
|
+
const props = {};
|
|
645
|
+
|
|
646
|
+
for (const key in this.props) {
|
|
647
|
+
const prop = this.props[key];
|
|
648
|
+
|
|
649
|
+
props[key] = {
|
|
650
|
+
value: isDataURL(prop.value) ? prop.__initialValue : prop.value,
|
|
651
|
+
params: prop.params,
|
|
652
|
+
triggers: prop.triggers,
|
|
653
|
+
__initialValue: prop.__initialValue,
|
|
654
|
+
__currentValue: prop.__currentValue,
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
|
|
518
658
|
return {
|
|
519
|
-
props
|
|
659
|
+
props,
|
|
520
660
|
propsFolders: this.propsFolders.map(
|
|
521
661
|
({
|
|
522
662
|
id,
|
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
import { SvelteMap } from 'svelte/reactivity';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Map storing errors by context
|
|
5
|
+
* @type {SvelteMap<string, Error>}
|
|
6
|
+
*/
|
|
3
7
|
export let errors = new SvelteMap();
|
|
4
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Display an error for a specific context
|
|
11
|
+
* @param {Error | any} error - The error to display
|
|
12
|
+
* @param {string} context - The context identifier for the error
|
|
13
|
+
* @returns {void}
|
|
14
|
+
*/
|
|
5
15
|
export function displayError(error, context) {
|
|
6
16
|
errors.set(context, error);
|
|
7
17
|
}
|
|
8
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Clear the error for a specific context
|
|
21
|
+
* @param {string} context - The context identifier
|
|
22
|
+
* @returns {void}
|
|
23
|
+
*/
|
|
9
24
|
export function clearError(context) {
|
|
10
25
|
if (errors.has(context)) {
|
|
11
26
|
errors.delete(context);
|
|
12
27
|
}
|
|
13
28
|
}
|
|
14
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Clear all errors
|
|
32
|
+
* @returns {void}
|
|
33
|
+
*/
|
|
15
34
|
export function clearErrors() {
|
|
16
35
|
errors.clear();
|
|
17
36
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { saveFiles } from '@fragment/utils/file.utils';
|
|
1
2
|
import { screenshotCanvas, recordCanvas } from '../utils/canvas.utils';
|
|
2
3
|
import { hydrate, persist } from './utils.svelte';
|
|
3
4
|
|
|
@@ -58,6 +59,7 @@ class Exports {
|
|
|
58
59
|
imageCount = $state(1);
|
|
59
60
|
recording = $state(false);
|
|
60
61
|
capturing = $state(false);
|
|
62
|
+
committing = $state(false);
|
|
61
63
|
imageCollapsed = $state(false);
|
|
62
64
|
videoCollapsed = $state(false);
|
|
63
65
|
|
|
@@ -96,6 +98,8 @@ class Exports {
|
|
|
96
98
|
quality = this.imageQuality,
|
|
97
99
|
pixelsPerInch = this.pixelsPerInch,
|
|
98
100
|
filename,
|
|
101
|
+
files = [],
|
|
102
|
+
commit = false,
|
|
99
103
|
pattern,
|
|
100
104
|
exportDir,
|
|
101
105
|
params = {},
|
|
@@ -117,7 +121,7 @@ class Exports {
|
|
|
117
121
|
for (let i = 0; i < count; i++) {
|
|
118
122
|
onBeforeCapture(captureParams);
|
|
119
123
|
|
|
120
|
-
|
|
124
|
+
const file = screenshotCanvas(canvas, {
|
|
121
125
|
filename,
|
|
122
126
|
pattern,
|
|
123
127
|
exportDir,
|
|
@@ -128,10 +132,19 @@ class Exports {
|
|
|
128
132
|
pixelsPerInch,
|
|
129
133
|
});
|
|
130
134
|
|
|
135
|
+
files.push(file);
|
|
136
|
+
|
|
131
137
|
onAfterCapture(captureParams);
|
|
132
138
|
}
|
|
133
139
|
|
|
134
140
|
onComplete(captureParams);
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
await saveFiles(files, [], { commit });
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error(`[fragment] Error while saving screenshot.`);
|
|
146
|
+
console.log(error);
|
|
147
|
+
}
|
|
135
148
|
}
|
|
136
149
|
|
|
137
150
|
record(
|
|
@@ -12,6 +12,11 @@ import { layout } from './layout.svelte.js';
|
|
|
12
12
|
import { persist, hydrate } from './utils.svelte';
|
|
13
13
|
import presets from '../lib/presets';
|
|
14
14
|
import { client } from '../client.js';
|
|
15
|
+
import { saveFiles } from '@fragment/utils/file.utils.js';
|
|
16
|
+
import {
|
|
17
|
+
defaultFilenamePattern,
|
|
18
|
+
getFilenameParams,
|
|
19
|
+
} from '@fragment/utils/canvas.utils.js';
|
|
15
20
|
|
|
16
21
|
export const SIZES = {
|
|
17
22
|
FIXED: 'fixed',
|
|
@@ -581,6 +586,8 @@ export class Render {
|
|
|
581
586
|
filename = this.sketch.key,
|
|
582
587
|
pattern = this.sketch.filenamePattern,
|
|
583
588
|
exportDir = this.sketch.exportDir,
|
|
589
|
+
files = [],
|
|
590
|
+
commit = false,
|
|
584
591
|
} = {}) {
|
|
585
592
|
const { sketch } = this;
|
|
586
593
|
|
|
@@ -591,6 +598,8 @@ export class Render {
|
|
|
591
598
|
params: {
|
|
592
599
|
props: sketch.props,
|
|
593
600
|
},
|
|
601
|
+
files,
|
|
602
|
+
commit,
|
|
594
603
|
onBeforeCapture: (params) => {
|
|
595
604
|
sketch.beforeCapture.forEach((fn) => fn(params));
|
|
596
605
|
this.renderSketch();
|
|
@@ -602,6 +611,44 @@ export class Render {
|
|
|
602
611
|
});
|
|
603
612
|
}
|
|
604
613
|
|
|
614
|
+
async commit({
|
|
615
|
+
filename = this.sketch.key,
|
|
616
|
+
pattern = this.sketch.filenamePattern ?? defaultFilenamePattern,
|
|
617
|
+
exportDir = this.sketch.exportDir,
|
|
618
|
+
} = {}) {
|
|
619
|
+
const { sketch } = this;
|
|
620
|
+
const { props } = sketch;
|
|
621
|
+
|
|
622
|
+
const data = {};
|
|
623
|
+
|
|
624
|
+
for (const key in props) {
|
|
625
|
+
data[key] = { value: props[key].value };
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
let patternParams = getFilenameParams();
|
|
629
|
+
let name = pattern({
|
|
630
|
+
filename,
|
|
631
|
+
params: { props },
|
|
632
|
+
...patternParams,
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
let files = [
|
|
636
|
+
{
|
|
637
|
+
filename: `${name}.props.json`,
|
|
638
|
+
exportDir,
|
|
639
|
+
data: JSON.stringify(data),
|
|
640
|
+
},
|
|
641
|
+
];
|
|
642
|
+
|
|
643
|
+
await this.screenshot({
|
|
644
|
+
filename,
|
|
645
|
+
pattern,
|
|
646
|
+
exportDir,
|
|
647
|
+
files,
|
|
648
|
+
commit: true,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
|
|
605
652
|
get params() {
|
|
606
653
|
return {
|
|
607
654
|
...this.mountParams,
|
|
@@ -6,11 +6,28 @@ import Sketch from './Sketch.svelte.js';
|
|
|
6
6
|
import { rendering } from './rendering.svelte.js';
|
|
7
7
|
import { removeHotListeners } from '../triggers/index.js';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} SketchInstance
|
|
11
|
+
* @property {string} [rendering]
|
|
12
|
+
* @property {any} [renderer]
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Record<string, () => Promise<SketchInstance>>} SketchCollection
|
|
17
|
+
*/
|
|
18
|
+
|
|
9
19
|
class SketchesManager {
|
|
20
|
+
/** @type {Record<string, Sketch>} */
|
|
10
21
|
sketches = $state({});
|
|
11
22
|
keys = $derived(Object.keys(this.sketches));
|
|
12
23
|
count = $derived(this.keys.length);
|
|
13
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Load a single sketch from a collection
|
|
27
|
+
* @param {SketchCollection} collection - The collection of sketches
|
|
28
|
+
* @param {string} key - The key of the sketch to load
|
|
29
|
+
* @returns {Promise<SketchInstance | undefined>}
|
|
30
|
+
*/
|
|
14
31
|
async loadSketch(collection, key) {
|
|
15
32
|
try {
|
|
16
33
|
let sketch = await collection[key]();
|
|
@@ -27,6 +44,11 @@ class SketchesManager {
|
|
|
27
44
|
}
|
|
28
45
|
}
|
|
29
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Load all sketches from a collection
|
|
49
|
+
* @param {SketchCollection} collection - The collection of sketches to load
|
|
50
|
+
* @returns {Promise<void>}
|
|
51
|
+
*/
|
|
30
52
|
async loadAll(collection) {
|
|
31
53
|
const keys = [...Object.keys(collection)];
|
|
32
54
|
|
|
@@ -42,16 +64,30 @@ class SketchesManager {
|
|
|
42
64
|
keys.map((key) => this.loadSketch(collection, key)),
|
|
43
65
|
);
|
|
44
66
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
/** @type {Record<string, SketchInstance>} */
|
|
68
|
+
const newSketches = keys.reduce(
|
|
69
|
+
/**
|
|
70
|
+
* @param {Record<string, SketchInstance>} all
|
|
71
|
+
* @param {string} key
|
|
72
|
+
* @param {number} index
|
|
73
|
+
*/
|
|
74
|
+
(all, key, index) => {
|
|
75
|
+
if (loadedSketches[index]) {
|
|
76
|
+
all[key] = loadedSketches[index];
|
|
77
|
+
}
|
|
49
78
|
|
|
50
|
-
|
|
51
|
-
|
|
79
|
+
return all;
|
|
80
|
+
},
|
|
81
|
+
{},
|
|
82
|
+
);
|
|
52
83
|
|
|
84
|
+
/** @type {Record<string, Sketch>} */
|
|
53
85
|
const newInstancedSketches = Object.keys(newSketches).reduce(
|
|
54
|
-
|
|
86
|
+
/**
|
|
87
|
+
* @param {Record<string, Sketch>} all
|
|
88
|
+
* @param {string} key
|
|
89
|
+
*/
|
|
90
|
+
(all, key) => {
|
|
55
91
|
const prevSketch = this.sketches[key];
|
|
56
92
|
|
|
57
93
|
const instanced = new Sketch({
|