fragment-tools 0.2.11 → 0.2.13
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 +2 -2
- 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/cli/templates/blank/index.ts +1 -1
- package/src/cli/templates/default/index.js +10 -2
- package/src/cli/templates/default/index.ts +5 -2
- package/src/cli/templates/fragment-gl/index.ts +1 -1
- package/src/cli/templates/p5/index.ts +1 -1
- package/src/cli/templates/p5-webgl/index.ts +1 -1
- package/src/cli/templates/three-fragment/index.js +5 -3
- package/src/cli/templates/three-fragment/index.ts +5 -4
- package/src/cli/templates/three-orthographic/index.js +6 -1
- package/src/cli/templates/three-orthographic/index.ts +6 -2
- package/src/cli/templates/three-perspective/index.js +6 -1
- package/src/cli/templates/three-perspective/index.ts +6 -2
- 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/modules/Params.svelte +1 -0
- package/src/client/app/renderers/2DRenderer.js +20 -16
- package/src/client/app/renderers/P5GLRenderer.js +13 -5
- package/src/client/app/renderers/P5Renderer.js +9 -1
- package/src/client/app/renderers/THREERenderer.js +63 -48
- package/src/client/app/state/Sketch.svelte.js +150 -10
- 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 +90 -13
- 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 +63 -16
- package/src/client/app/ui/FieldSection.svelte +4 -4
- package/src/client/app/ui/ParamsOutput.svelte +7 -5
- package/src/client/app/ui/SketchRenderer.svelte +21 -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 +93 -0
- 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 +70 -17
- 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 +41 -15
- package/tsconfig.json +1 -1
|
@@ -3,15 +3,24 @@ import { changeDpiDataUrl } from 'changedpi';
|
|
|
3
3
|
const supportedEncodings = ['image/png', 'image/jpeg', 'image/webp'];
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* @
|
|
8
|
-
* @
|
|
9
|
-
* @
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* @
|
|
14
|
-
* @
|
|
6
|
+
* @typedef {Object} ExportCanvasOptions
|
|
7
|
+
* @property {string} [encoding='image/png'] - Image MIME type (image/png, image/jpeg, image/webp)
|
|
8
|
+
* @property {number} [encodingQuality=0.92] - Image quality (0-1)
|
|
9
|
+
* @property {number} [pixelsPerInch=72] - DPI/PPI for the exported image
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {Object} ExportCanvasResult
|
|
14
|
+
* @property {string} extension - File extension (e.g., '.png', '.jpg')
|
|
15
|
+
* @property {string} dataURL - Data URL of the exported canvas
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Export a canvas to a data URL with specified encoding and DPI
|
|
20
|
+
* @param {HTMLCanvasElement} canvas - The canvas to export
|
|
21
|
+
* @param {ExportCanvasOptions} [options={}] - Export options
|
|
22
|
+
* @returns {ExportCanvasResult}
|
|
23
|
+
* @throws {Error} If encoding is not supported
|
|
15
24
|
*/
|
|
16
25
|
export function exportCanvas(
|
|
17
26
|
canvas,
|
|
@@ -5,32 +5,36 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @
|
|
9
|
-
* @
|
|
10
|
-
* @
|
|
11
|
-
* @
|
|
12
|
-
* @
|
|
13
|
-
* @
|
|
14
|
-
* @
|
|
8
|
+
* @typedef {object} PreviewParams
|
|
9
|
+
* @property {number} id
|
|
10
|
+
* @property {HTMLCanvasElement} canvas
|
|
11
|
+
* @property {HTMLElement} container
|
|
12
|
+
* @property {number} width
|
|
13
|
+
* @property {number} height
|
|
14
|
+
* @property {number} pixelRatio
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {PreviewParams} params
|
|
15
19
|
* @returns {MountParams2DRenderer}
|
|
16
20
|
*/
|
|
17
21
|
export let onMountPreview = ({ canvas }) => {
|
|
22
|
+
const context = canvas.getContext('2d');
|
|
23
|
+
|
|
24
|
+
if (!context) {
|
|
25
|
+
throw new Error(`Cannot get CanvasRenderingContext2D from canvas`);
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
return {
|
|
19
29
|
canvas,
|
|
20
|
-
context
|
|
30
|
+
context,
|
|
21
31
|
};
|
|
22
32
|
};
|
|
23
33
|
|
|
24
34
|
/**
|
|
25
|
-
* @param {MountParams2DRenderer} params
|
|
26
|
-
* @param {number} params.id
|
|
27
|
-
* @param {HTMLCanvasElement} params.canvas
|
|
28
|
-
* @param {HTMLElement} params.container
|
|
29
|
-
* @param {number} params.width
|
|
30
|
-
* @param {number} params.height
|
|
31
|
-
* @param {number} params.pixelRatio
|
|
35
|
+
* @param {MountParams2DRenderer & PreviewParams} params
|
|
32
36
|
*/
|
|
33
|
-
export let onResizePreview = ({
|
|
37
|
+
export let onResizePreview = ({ canvas, width, height, pixelRatio }) => {
|
|
34
38
|
canvas.width = width * pixelRatio;
|
|
35
39
|
canvas.height = height * pixelRatio;
|
|
36
40
|
canvas.style.width = `${width}px`;
|
|
@@ -29,8 +29,16 @@ let previews = [];
|
|
|
29
29
|
* @param {number} params.pixelRatio
|
|
30
30
|
* @returns {MountParamsP5GLRenderer}
|
|
31
31
|
*/
|
|
32
|
-
export let onMountPreview = ({
|
|
32
|
+
export let onMountPreview = ({
|
|
33
|
+
id,
|
|
34
|
+
container,
|
|
35
|
+
canvas,
|
|
36
|
+
width,
|
|
37
|
+
height,
|
|
38
|
+
pixelRatio,
|
|
39
|
+
}) => {
|
|
33
40
|
const p = new p5((sketch) => {
|
|
41
|
+
sketch.pixelDensity(pixelRatio);
|
|
34
42
|
sketch.setup = () => {
|
|
35
43
|
sketch.createCanvas(width, height, 'webgl', canvas);
|
|
36
44
|
};
|
|
@@ -49,7 +57,7 @@ export let onMountPreview = ({ id, container, canvas, width, height }) => {
|
|
|
49
57
|
};
|
|
50
58
|
|
|
51
59
|
/**
|
|
52
|
-
* @param {
|
|
60
|
+
* @param {object} params
|
|
53
61
|
* @param {number} params.id
|
|
54
62
|
* @param {HTMLCanvasElement} params.canvas
|
|
55
63
|
* @param {HTMLDivElement} params.container
|
|
@@ -65,7 +73,7 @@ export let onBeforeUpdatePreview = ({ id }) => {
|
|
|
65
73
|
};
|
|
66
74
|
|
|
67
75
|
/**
|
|
68
|
-
* @param {
|
|
76
|
+
* @param {object} params
|
|
69
77
|
* @param {number} params.id
|
|
70
78
|
* @param {HTMLCanvasElement} params.canvas
|
|
71
79
|
* @param {HTMLDivElement} params.container
|
|
@@ -86,7 +94,7 @@ export let onAfterUpdatePreview = ({ id }) => {
|
|
|
86
94
|
};
|
|
87
95
|
|
|
88
96
|
/**
|
|
89
|
-
* @param {
|
|
97
|
+
* @param {object} params
|
|
90
98
|
* @param {number} params.id
|
|
91
99
|
* @param {HTMLCanvasElement} params.canvas
|
|
92
100
|
* @param {number} params.width
|
|
@@ -103,7 +111,7 @@ export let onResizePreview = ({ id, width, height, pixelRatio }) => {
|
|
|
103
111
|
};
|
|
104
112
|
|
|
105
113
|
/**
|
|
106
|
-
* @param {
|
|
114
|
+
* @param {object} params
|
|
107
115
|
* @param {number} params.id
|
|
108
116
|
* @param {HTMLCanvasElement} params.canvas
|
|
109
117
|
* @param {HTMLElement} params.container
|
|
@@ -25,8 +25,16 @@ let previews = [];
|
|
|
25
25
|
* @param {number} params.pixelRatio
|
|
26
26
|
* @returns {MountParamsP5Renderer}
|
|
27
27
|
*/
|
|
28
|
-
export let onMountPreview = ({
|
|
28
|
+
export let onMountPreview = ({
|
|
29
|
+
id,
|
|
30
|
+
container,
|
|
31
|
+
canvas,
|
|
32
|
+
width,
|
|
33
|
+
height,
|
|
34
|
+
pixelRatio,
|
|
35
|
+
}) => {
|
|
29
36
|
const p = new p5((sketch) => {
|
|
37
|
+
sketch.pixelDensity(pixelRatio);
|
|
30
38
|
sketch.setup = () => {
|
|
31
39
|
sketch.createCanvas(width, height, canvas);
|
|
32
40
|
};
|
|
@@ -4,35 +4,53 @@ 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
|
|
35
|
-
let renderer = new WebGLRenderer({ antialias: true
|
|
52
|
+
export let onMountPreview = ({ id }) => {
|
|
53
|
+
let renderer = new WebGLRenderer({ antialias: true });
|
|
36
54
|
|
|
37
55
|
const render = renderer.render;
|
|
38
56
|
|
|
@@ -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);
|
|
@@ -125,13 +129,22 @@ export let onDestroyPreview = ({ id }) => {
|
|
|
125
129
|
const { renderer } = preview;
|
|
126
130
|
clearError(renderer.getContext().__uuid);
|
|
127
131
|
renderer.dispose();
|
|
132
|
+
renderer.forceContextLoss();
|
|
128
133
|
previews.splice(previewIndex, 1);
|
|
129
134
|
}
|
|
130
135
|
};
|
|
131
136
|
|
|
132
137
|
/* HOT SHADER RELOADING */
|
|
138
|
+
/** @type {ShaderUpdate[]} */
|
|
133
139
|
let _shaderUpdates = [];
|
|
134
140
|
|
|
141
|
+
function clearShaderUpdates() {
|
|
142
|
+
_shaderUpdates = [];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @param {Scene} scene
|
|
147
|
+
*/
|
|
135
148
|
function handleHotShaderUpdate(scene) {
|
|
136
149
|
if (_shaderUpdates.length > 0) {
|
|
137
150
|
const verifyMaterial = (material) => {
|
|
@@ -172,20 +185,22 @@ function handleHotShaderUpdate(scene) {
|
|
|
172
185
|
}
|
|
173
186
|
}
|
|
174
187
|
|
|
175
|
-
function clearShaderUpdates() {
|
|
176
|
-
_shaderUpdates = [];
|
|
177
|
-
}
|
|
178
|
-
|
|
179
188
|
if (import.meta.hot) {
|
|
180
|
-
import.meta.hot.on('sketch-update', (
|
|
189
|
+
import.meta.hot.on('sketch-update', () => {
|
|
181
190
|
clearShaderUpdates();
|
|
182
191
|
});
|
|
183
192
|
}
|
|
184
193
|
|
|
185
|
-
client.on(
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
+
});
|
|
189
203
|
|
|
190
|
-
|
|
191
|
-
}
|
|
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;
|
|
@@ -481,7 +585,7 @@ class Sketch {
|
|
|
481
585
|
fieldgroups.forEach((fieldgroup) => {
|
|
482
586
|
const hasAllFieldsHidden = fieldgroup.children
|
|
483
587
|
.filter((child) => child.type === 'field')
|
|
484
|
-
.every((child) => this.props[child.key]
|
|
588
|
+
.every((child) => this.props[child.key]?.__hidden());
|
|
485
589
|
const hasAllFieldgroupsHidden = fieldgroup.children
|
|
486
590
|
.filter((child) => child.type === 'fieldgroup')
|
|
487
591
|
.every((child) => child.hidden);
|
|
@@ -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
|
}
|