fragment-tools 0.2.12 → 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/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/P5GLRenderer.js +13 -5
- package/src/client/app/renderers/P5Renderer.js +9 -1
- package/src/client/app/renderers/THREERenderer.js +61 -47
- 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,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {Object} File
|
|
3
|
-
* @property {string}
|
|
4
|
-
* @property {string} exportDir
|
|
3
|
+
* @property {string} filename
|
|
5
4
|
* @property {string} data
|
|
5
|
+
* @property {string} [exportDir]
|
|
6
6
|
* @property {string} [encoding]
|
|
7
|
+
* @property {Blob} [blob]
|
|
8
|
+
* @property {number} [size]
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
/**
|
|
@@ -20,7 +22,12 @@ export async function createDataURLFromBlob(blob) {
|
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
reader.onload = (e) => {
|
|
23
|
-
|
|
25
|
+
const result = e.target?.result;
|
|
26
|
+
if (typeof result === 'string') {
|
|
27
|
+
resolve(result);
|
|
28
|
+
} else {
|
|
29
|
+
reject(new Error('Failed to read blob as data URL'));
|
|
30
|
+
}
|
|
24
31
|
};
|
|
25
32
|
|
|
26
33
|
reader.readAsDataURL(blob);
|
|
@@ -30,7 +37,7 @@ export async function createDataURLFromBlob(blob) {
|
|
|
30
37
|
/**
|
|
31
38
|
* Transform a Data URL into a blob
|
|
32
39
|
* @param {string} dataURL
|
|
33
|
-
* @returns {Blob}
|
|
40
|
+
* @returns {Promise<Blob>}
|
|
34
41
|
*/
|
|
35
42
|
export function createBlobFromDataURL(dataURL) {
|
|
36
43
|
return new Promise((resolve, reject) => {
|
|
@@ -60,15 +67,24 @@ export function createBlobFromDataURL(dataURL) {
|
|
|
60
67
|
});
|
|
61
68
|
}
|
|
62
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Download data as a file
|
|
72
|
+
* @param {string | object} data - The data to download
|
|
73
|
+
* @param {string} filename - The filename for the download
|
|
74
|
+
* @returns {void}
|
|
75
|
+
*/
|
|
63
76
|
export function download(data, filename) {
|
|
64
77
|
let extension = getFileExtension(filename);
|
|
65
78
|
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
let content;
|
|
80
|
+
if (typeof data === 'object') {
|
|
81
|
+
content = JSON.stringify(data, undefined, 4);
|
|
82
|
+
} else {
|
|
83
|
+
content = data;
|
|
68
84
|
}
|
|
69
85
|
|
|
70
86
|
let type = getMimeType(extension);
|
|
71
|
-
let blob = new Blob([
|
|
87
|
+
let blob = new Blob([content], { type });
|
|
72
88
|
|
|
73
89
|
downloadBlob(blob, { filename });
|
|
74
90
|
}
|
|
@@ -78,6 +94,7 @@ export function download(data, filename) {
|
|
|
78
94
|
* @param {Blob} blob
|
|
79
95
|
* @param {object} [options]
|
|
80
96
|
* @param {string} [options.filename="untitled"]
|
|
97
|
+
* @returns {void}
|
|
81
98
|
*/
|
|
82
99
|
export function downloadBlob(blob, { filename = 'untitled' } = {}) {
|
|
83
100
|
let a = document.createElement('a');
|
|
@@ -91,7 +108,7 @@ export function downloadBlob(blob, { filename = 'untitled' } = {}) {
|
|
|
91
108
|
a.onclick = () => {
|
|
92
109
|
a.onclick = () => {};
|
|
93
110
|
setTimeout(() => {
|
|
94
|
-
window.URL.revokeObjectURL(
|
|
111
|
+
window.URL.revokeObjectURL(a.href);
|
|
95
112
|
if (a.parentElement) a.parentElement.removeChild(a);
|
|
96
113
|
a.removeAttribute('href');
|
|
97
114
|
});
|
|
@@ -106,7 +123,7 @@ export function downloadBlob(blob, { filename = 'untitled' } = {}) {
|
|
|
106
123
|
* @returns {string} filename
|
|
107
124
|
*/
|
|
108
125
|
export function getFilename(path) {
|
|
109
|
-
return path.split(/[\\/]/).pop();
|
|
126
|
+
return path.split(/[\\/]/).pop() || path;
|
|
110
127
|
}
|
|
111
128
|
|
|
112
129
|
/**
|
|
@@ -114,34 +131,44 @@ export function getFilename(path) {
|
|
|
114
131
|
* @param {string} data
|
|
115
132
|
* @returns {number}
|
|
116
133
|
*/
|
|
117
|
-
export function estimateFileSize(data) {
|
|
134
|
+
export function estimateFileSize(data, errorMargin = 1) {
|
|
118
135
|
const base64Length = data.length - (data.indexOf(',') + 1);
|
|
119
|
-
return (base64Length * (3 / 4) - 2) / 1024 / 1024;
|
|
136
|
+
return ((base64Length * (3 / 4) - 2) / 1024 / 1024) * errorMargin;
|
|
120
137
|
}
|
|
121
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Extract file extension from a path
|
|
141
|
+
* @param {string} path - The file path
|
|
142
|
+
* @returns {string} The file extension or undefined if not found
|
|
143
|
+
*/
|
|
122
144
|
export function getFileExtension(path) {
|
|
123
145
|
const match = path.match(/[^\\/]\.([^.\\/]+)$/);
|
|
124
146
|
|
|
125
147
|
if (match && match.length > 1) {
|
|
126
148
|
return match[1];
|
|
127
149
|
}
|
|
150
|
+
|
|
151
|
+
return '';
|
|
128
152
|
}
|
|
129
153
|
|
|
130
154
|
/**
|
|
131
|
-
*
|
|
132
|
-
* @param {string} extension
|
|
133
|
-
* @returns {string}
|
|
155
|
+
* Get MIME type from file extension
|
|
156
|
+
* @param {string} extension - The file extension
|
|
157
|
+
* @returns {string} The MIME type or 'application/octet-stream' if not recognized
|
|
134
158
|
*/
|
|
135
159
|
export function getMimeType(extension) {
|
|
136
160
|
if (extension === 'json') return 'application/json';
|
|
137
161
|
if (extension === 'txt') return 'text';
|
|
138
162
|
if (extension === 'png') return 'image/png';
|
|
139
163
|
if (extension === 'jpeg' || extension === 'jpg') return 'image/jpeg';
|
|
164
|
+
|
|
165
|
+
return 'application/octet-stream';
|
|
140
166
|
}
|
|
141
167
|
|
|
142
168
|
/**
|
|
143
|
-
*
|
|
144
|
-
* @param {File|File[]} files
|
|
169
|
+
* Save file(s) in the browser using download
|
|
170
|
+
* @param {File | File[]} files - Single file or array of files to save
|
|
171
|
+
* @returns {Promise<void>}
|
|
145
172
|
*/
|
|
146
173
|
export async function saveInBrowser(files) {
|
|
147
174
|
/**
|
|
@@ -152,11 +179,11 @@ export async function saveInBrowser(files) {
|
|
|
152
179
|
blob = await createBlobFromDataURL(data);
|
|
153
180
|
}
|
|
154
181
|
|
|
155
|
-
|
|
182
|
+
downloadBlob(blob, { filename });
|
|
156
183
|
}
|
|
157
184
|
|
|
158
185
|
if (Array.isArray(files)) {
|
|
159
|
-
|
|
186
|
+
await Promise.all(files.map((file) => saveFile(file)));
|
|
160
187
|
} else {
|
|
161
188
|
await saveFile(files);
|
|
162
189
|
}
|
|
@@ -164,34 +191,45 @@ export async function saveInBrowser(files) {
|
|
|
164
191
|
|
|
165
192
|
/**
|
|
166
193
|
* Save files to disk by sending them to Fragment save plugin. Fallback to saveInBrowser if fails
|
|
167
|
-
* @param {File[]} files
|
|
168
|
-
* @
|
|
194
|
+
* @param {File[]} [files=[]]
|
|
195
|
+
* @param {string[]} [out=[]]
|
|
196
|
+
* @returns {Promise<string[] | void>}
|
|
169
197
|
*/
|
|
170
|
-
export async function saveFiles(files = [], out = []) {
|
|
198
|
+
export async function saveFiles(files = [], out = [], { commit = false } = {}) {
|
|
171
199
|
if (__DEV__) {
|
|
172
200
|
files.forEach((file) => {
|
|
173
201
|
if (!file.size) {
|
|
174
|
-
|
|
202
|
+
const errorMargin = 1.3;
|
|
203
|
+
file.size = estimateFileSize(file.data, errorMargin);
|
|
175
204
|
}
|
|
176
205
|
});
|
|
177
206
|
|
|
178
207
|
const limitInMb = 100;
|
|
208
|
+
/** @type {{ files: File[], commit: boolean }} */
|
|
179
209
|
const body = {
|
|
180
210
|
files: [],
|
|
211
|
+
commit: false,
|
|
181
212
|
};
|
|
182
213
|
|
|
183
214
|
let size = 0;
|
|
184
215
|
|
|
185
216
|
for (let i = 0; i < files.length; i++) {
|
|
186
217
|
const file = files[i];
|
|
187
|
-
|
|
218
|
+
|
|
219
|
+
if (size + (file.size ?? 0) < limitInMb) {
|
|
188
220
|
body.files.push(file);
|
|
189
|
-
size += file.size;
|
|
221
|
+
size += file.size || 0;
|
|
190
222
|
} else {
|
|
191
223
|
break;
|
|
192
224
|
}
|
|
193
225
|
}
|
|
194
226
|
|
|
227
|
+
const isLastBatch = body.files.length - files.length === 0;
|
|
228
|
+
|
|
229
|
+
if (isLastBatch) {
|
|
230
|
+
body.commit = commit;
|
|
231
|
+
}
|
|
232
|
+
|
|
195
233
|
const response = await fetch('/save', {
|
|
196
234
|
method: 'POST',
|
|
197
235
|
body: JSON.stringify(body),
|
|
@@ -200,13 +238,21 @@ export async function saveFiles(files = [], out = []) {
|
|
|
200
238
|
'Content-Type': 'application/json',
|
|
201
239
|
},
|
|
202
240
|
});
|
|
203
|
-
|
|
241
|
+
/** @type {{ filepaths: string[], warnings: string[], errors: string[]}} */
|
|
242
|
+
const { filepaths, warnings, errors } = await response.json();
|
|
204
243
|
|
|
205
|
-
if (
|
|
244
|
+
if (errors?.length > 0) {
|
|
245
|
+
errors.forEach((error) => {
|
|
246
|
+
console.error(`[fragment] ${error}`);
|
|
247
|
+
});
|
|
248
|
+
await saveInBrowser(files);
|
|
249
|
+
} else if (response.ok && filepaths?.length) {
|
|
206
250
|
out.push(...filepaths);
|
|
207
251
|
|
|
208
252
|
if (body.files.length < files.length) {
|
|
209
|
-
return saveFiles(files.slice(body.files.length), out
|
|
253
|
+
return saveFiles(files.slice(body.files.length), out, {
|
|
254
|
+
commit,
|
|
255
|
+
});
|
|
210
256
|
}
|
|
211
257
|
|
|
212
258
|
if (out.length < 15) {
|
|
@@ -219,10 +265,17 @@ export async function saveFiles(files = [], out = []) {
|
|
|
219
265
|
});
|
|
220
266
|
}
|
|
221
267
|
|
|
268
|
+
if (warnings?.length > 0) {
|
|
269
|
+
warnings.forEach((warning) => {
|
|
270
|
+
console.warn(`[fragment] ${warning}`);
|
|
271
|
+
});
|
|
272
|
+
} else {
|
|
273
|
+
if (commit) {
|
|
274
|
+
console.log(`[fragment] Committed latest changes.`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
222
278
|
return out;
|
|
223
|
-
} else {
|
|
224
|
-
console.error(`[fragment] Error while saving files on disk.`);
|
|
225
|
-
await saveInBrowser(files);
|
|
226
279
|
}
|
|
227
280
|
} else {
|
|
228
281
|
await saveInBrowser(files);
|
|
@@ -233,7 +286,9 @@ export async function saveFiles(files = [], out = []) {
|
|
|
233
286
|
* Save a blob on disk
|
|
234
287
|
* @param {Blob} blob
|
|
235
288
|
* @param {object} options
|
|
236
|
-
* @
|
|
289
|
+
* @param {string} options.filename
|
|
290
|
+
* @param {string} options.exportDir
|
|
291
|
+
* @returns {Promise<string[] | void>}
|
|
237
292
|
*/
|
|
238
293
|
export async function saveBlob(blob, { filename, exportDir }) {
|
|
239
294
|
const data = await createDataURLFromBlob(blob);
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Extract the file path from a shader string
|
|
3
|
+
* @param {string} [shader=''] - The shader source code
|
|
4
|
+
* @returns {string | null} The extracted file path, or null if not found
|
|
5
|
+
*/
|
|
3
6
|
export function getShaderPath(shader = '') {
|
|
4
7
|
const match = shader.match(/<filepath:\/\/(.*)>/);
|
|
5
8
|
return match && match[1];
|
|
6
9
|
}
|
|
7
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Remove the file path comment from a shader string
|
|
13
|
+
* @param {string} shader - The shader source code
|
|
14
|
+
* @param {string | null} [filepath] - Optional filepath to remove (defaults to extracted path)
|
|
15
|
+
* @returns {string} The shader with the filepath comment removed
|
|
16
|
+
*/
|
|
8
17
|
export function removeShaderPath(shader, filepath = getShaderPath(shader)) {
|
|
9
18
|
if (filepath) {
|
|
10
19
|
return shader.replace(`// <filepath://${filepath}>`, '');
|
|
@@ -4,6 +4,7 @@ import { getShaderPath, removeShaderPath } from './glsl.utils';
|
|
|
4
4
|
const methods = ['attachShader'];
|
|
5
5
|
|
|
6
6
|
const contexts = [WebGLRenderingContext, WebGL2RenderingContext];
|
|
7
|
+
/** @type {Record<string, any>} */
|
|
7
8
|
const references = {};
|
|
8
9
|
|
|
9
10
|
for (let i = 0; i < methods.length; i++) {
|
|
@@ -50,6 +51,14 @@ const FRAGMENT_SHADER = 35632;
|
|
|
50
51
|
const VERTEX_SHADER = 35633;
|
|
51
52
|
|
|
52
53
|
class ShaderCompileError extends Error {
|
|
54
|
+
/**
|
|
55
|
+
*
|
|
56
|
+
* @param {Object} params
|
|
57
|
+
* @param {string} [params.source]
|
|
58
|
+
* @param {string} [params.filename]
|
|
59
|
+
* @param {string} [params.message]
|
|
60
|
+
* @param {number} [params.lineNumber]
|
|
61
|
+
*/
|
|
53
62
|
constructor({
|
|
54
63
|
source,
|
|
55
64
|
filename = '',
|
|
@@ -112,30 +121,33 @@ e('compileShader', function (res, args) {
|
|
|
112
121
|
const filename = shader.__filename;
|
|
113
122
|
|
|
114
123
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
displayError(error, this.__uuid);
|
|
124
|
+
const shaderError = getShaderError(gl, shader, shader.__type);
|
|
125
|
+
|
|
126
|
+
if (shaderError) {
|
|
127
|
+
const { source, message, lineNumber } = shaderError;
|
|
128
|
+
const shaderCompileError = new ShaderCompileError({
|
|
129
|
+
source,
|
|
130
|
+
message,
|
|
131
|
+
filename,
|
|
132
|
+
lineNumber,
|
|
133
|
+
});
|
|
134
|
+
displayError(shaderCompileError, this.__uuid);
|
|
135
|
+
}
|
|
129
136
|
}
|
|
130
137
|
});
|
|
131
138
|
|
|
139
|
+
/**
|
|
140
|
+
*
|
|
141
|
+
* @param {WebGL2RenderingContext} gl
|
|
142
|
+
* @param {WebGLShader} shader
|
|
143
|
+
* @param {number} type
|
|
144
|
+
* @returns {{ message: string, source: string, lineNumber: number} | undefined}
|
|
145
|
+
*/
|
|
132
146
|
function getShaderError(gl, shader, type) {
|
|
147
|
+
console.log(`getShaderError`, gl, shader, type);
|
|
133
148
|
const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
|
|
134
|
-
const errors = gl.getShaderInfoLog(shader)
|
|
135
|
-
|
|
136
|
-
if (status && errors === '') return '';
|
|
137
|
-
|
|
138
|
-
const errorMatches = errors.match(/ERROR: 0:(\d+):([\s\S]*$)/);
|
|
149
|
+
const errors = gl.getShaderInfoLog(shader)?.trim() ?? '';
|
|
150
|
+
const errorMatches = errors && errors.match(/ERROR: 0:(\d+):([\s\S]*$)/);
|
|
139
151
|
|
|
140
152
|
if (errorMatches) {
|
|
141
153
|
const lineNumber = parseInt(errorMatches[1]);
|
|
@@ -148,8 +160,6 @@ function getShaderError(gl, shader, type) {
|
|
|
148
160
|
source: source,
|
|
149
161
|
lineNumber,
|
|
150
162
|
};
|
|
151
|
-
} else {
|
|
152
|
-
return errors;
|
|
153
163
|
}
|
|
154
164
|
}
|
|
155
165
|
|
|
@@ -1,26 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @
|
|
4
|
-
* @param {
|
|
5
|
-
* @param {
|
|
2
|
+
* @template K, V
|
|
3
|
+
* @callback FindIndexCallback
|
|
4
|
+
* @param {V} item - The item to test
|
|
5
|
+
* @param {number} index - The index of the item
|
|
6
|
+
* @param {V[]} array - The array being searched
|
|
7
|
+
* @returns {boolean}
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Add an item to an array stored in a Map
|
|
12
|
+
* @template K, V
|
|
13
|
+
* @param {Map<K, V[]>} map - The Map containing arrays as values
|
|
14
|
+
* @param {K} key - The key to store the item under
|
|
15
|
+
* @param {V} item - The item to add to the array
|
|
16
|
+
* @returns {void}
|
|
6
17
|
*/
|
|
7
18
|
export const addToMapArray = (map, key, item) => {
|
|
8
|
-
|
|
9
|
-
|
|
19
|
+
const previous = map.get(key);
|
|
20
|
+
|
|
21
|
+
if (Array.isArray(previous)) {
|
|
22
|
+
map.set(key, [...previous, item]);
|
|
10
23
|
} else {
|
|
11
24
|
map.set(key, [item]);
|
|
12
25
|
}
|
|
13
26
|
};
|
|
14
27
|
|
|
15
28
|
/**
|
|
16
|
-
*
|
|
17
|
-
* @
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {
|
|
29
|
+
* Remove an item from an array stored in a Map
|
|
30
|
+
* @template K, V
|
|
31
|
+
* @param {Map<K, V[]>} map - The Map containing arrays as values
|
|
32
|
+
* @param {K} key - The key where the array is stored
|
|
33
|
+
* @param {(item: V, index: number, array: V[]) => boolean} findIndex - Callback to find the item to remove
|
|
34
|
+
* @returns {void}
|
|
20
35
|
*/
|
|
21
36
|
export const removeFromMapArray = (map, key, findIndex) => {
|
|
22
|
-
|
|
23
|
-
|
|
37
|
+
const items = map.get(key);
|
|
38
|
+
|
|
39
|
+
if (Array.isArray(items)) {
|
|
24
40
|
const index = items.findIndex(findIndex);
|
|
25
41
|
|
|
26
42
|
if (index >= 0) {
|
package/src/client/main.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { mount } from 'svelte';
|
|
2
2
|
import App from './app/App.svelte';
|
|
3
3
|
|
|
4
|
+
const target = document.getElementById('app');
|
|
5
|
+
|
|
6
|
+
if (!target) {
|
|
7
|
+
throw new Error('App mount target not found');
|
|
8
|
+
}
|
|
9
|
+
|
|
4
10
|
const app = mount(App, {
|
|
5
|
-
target
|
|
11
|
+
target,
|
|
6
12
|
props: {},
|
|
7
13
|
});
|
|
8
14
|
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
|
|
3
|
+
declare const __DEV__: boolean;
|
|
4
|
+
declare const __CWD__: string;
|
|
5
|
+
declare const __FRAGMENT_PORT__: number | undefined;
|
|
6
|
+
declare const __START_TIME__: number;
|
|
7
|
+
declare const __SEED__: number;
|
|
8
|
+
declare const __BUILD__: boolean;
|
|
9
|
+
|
|
10
|
+
declare module 'virtual:sketches' {
|
|
11
|
+
/**
|
|
12
|
+
* Collection of sketch modules
|
|
13
|
+
*/
|
|
14
|
+
export const sketches: Record<string, () => Promise<any>>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
declare module 'changedpi' {
|
|
18
|
+
/**
|
|
19
|
+
* Collection of sketch modules
|
|
20
|
+
*/
|
|
21
|
+
export function changeDpiDataUrl(data: string, ppi: number);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare module 'gifenc' {
|
|
25
|
+
/**
|
|
26
|
+
* RGB color palette entry
|
|
27
|
+
*/
|
|
28
|
+
export type PaletteEntry = [number, number, number];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* RGBA color
|
|
32
|
+
*/
|
|
33
|
+
export type RGBAColor = [number, number, number, number];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Quantization options
|
|
37
|
+
*/
|
|
38
|
+
export interface QuantizeOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Enable oneBitTransparency
|
|
41
|
+
*/
|
|
42
|
+
oneBitTransparency?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Format of input data
|
|
45
|
+
*/
|
|
46
|
+
format?: 'rgb565' | 'rgba4444' | 'rgba5551';
|
|
47
|
+
/**
|
|
48
|
+
* Clear hash between frames
|
|
49
|
+
*/
|
|
50
|
+
clearHash?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Clear hash modulo
|
|
53
|
+
*/
|
|
54
|
+
clearHashModulo?: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Frame write options
|
|
59
|
+
*/
|
|
60
|
+
export interface WriteFrameOptions {
|
|
61
|
+
/**
|
|
62
|
+
* Color palette for the frame
|
|
63
|
+
*/
|
|
64
|
+
palette?: PaletteEntry[];
|
|
65
|
+
/**
|
|
66
|
+
* Delay in milliseconds
|
|
67
|
+
*/
|
|
68
|
+
delay?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Transparent color index
|
|
71
|
+
*/
|
|
72
|
+
transparent?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Disposal method
|
|
75
|
+
*/
|
|
76
|
+
dispose?: number;
|
|
77
|
+
/**
|
|
78
|
+
* Enable repeat
|
|
79
|
+
*/
|
|
80
|
+
repeat?: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* GIF Encoder instance
|
|
85
|
+
*/
|
|
86
|
+
export interface GIFEncoderInstance {
|
|
87
|
+
/**
|
|
88
|
+
* Write a frame to the GIF
|
|
89
|
+
*/
|
|
90
|
+
writeFrame(
|
|
91
|
+
index: Uint8Array,
|
|
92
|
+
width: number,
|
|
93
|
+
height: number,
|
|
94
|
+
options?: WriteFrameOptions,
|
|
95
|
+
): void;
|
|
96
|
+
/**
|
|
97
|
+
* Finish encoding and finalize the GIF
|
|
98
|
+
*/
|
|
99
|
+
finish(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Get the encoded bytes
|
|
102
|
+
*/
|
|
103
|
+
bytes(): Uint8Array<ArrayBuffer>;
|
|
104
|
+
/**
|
|
105
|
+
* Get the byte length
|
|
106
|
+
*/
|
|
107
|
+
bytesView(): Uint8Array;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Create a new GIF encoder
|
|
112
|
+
*/
|
|
113
|
+
export function GIFEncoder(): GIFEncoderInstance;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Quantize RGBA pixel data to a color palette
|
|
117
|
+
*/
|
|
118
|
+
export function quantize(
|
|
119
|
+
rgba: Uint8Array | Uint8ClampedArray,
|
|
120
|
+
maxColors: number,
|
|
121
|
+
options?: QuantizeOptions,
|
|
122
|
+
): PaletteEntry[];
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Apply a palette to RGBA pixel data
|
|
126
|
+
*/
|
|
127
|
+
export function applyPalette(
|
|
128
|
+
rgba: Uint8Array | Uint8ClampedArray,
|
|
129
|
+
palette: PaletteEntry[],
|
|
130
|
+
format?: 'rgb565' | 'rgba4444' | 'rgba5551',
|
|
131
|
+
): Uint8Array;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Prequantize RGBA data
|
|
135
|
+
*/
|
|
136
|
+
export function prequantize(
|
|
137
|
+
rgba: Uint8Array | Uint8ClampedArray,
|
|
138
|
+
options?: QuantizeOptions,
|
|
139
|
+
): {
|
|
140
|
+
rgba: Uint8Array;
|
|
141
|
+
palette: PaletteEntry[];
|
|
142
|
+
};
|
|
143
|
+
}
|
package/src/types/props.d.ts
CHANGED
|
@@ -11,19 +11,23 @@ type BaseProp<Value, Params, Type> = {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
type SelectProp = BaseProp<
|
|
14
|
-
number | string,
|
|
14
|
+
number | string | undefined,
|
|
15
15
|
{
|
|
16
16
|
options?:
|
|
17
17
|
| number[]
|
|
18
18
|
| string[]
|
|
19
|
+
| undefined[]
|
|
19
20
|
| Array<{ label?: string; value: number }>
|
|
20
|
-
| Array<{ label?: string; value: string }>;
|
|
21
|
+
| Array<{ label?: string; value: string | undefined }>;
|
|
21
22
|
},
|
|
22
23
|
'select'
|
|
23
24
|
>;
|
|
24
25
|
type NumberProp = BaseProp<
|
|
25
26
|
number,
|
|
26
|
-
{
|
|
27
|
+
({ min?: never; max?: never } | { min: number; max: number }) & {
|
|
28
|
+
step?: number;
|
|
29
|
+
suffix?: string;
|
|
30
|
+
},
|
|
27
31
|
'number'
|
|
28
32
|
>;
|
|
29
33
|
|
|
@@ -56,24 +60,40 @@ type VecParams<V extends VecValue> = V extends readonly number[]
|
|
|
56
60
|
|
|
57
61
|
type VecProp<V extends VecValue = VecValue> = BaseProp<
|
|
58
62
|
V,
|
|
59
|
-
{ locked?: boolean }
|
|
63
|
+
{ locked?: boolean; suffix?: string } & VecParams<V>,
|
|
60
64
|
'vec'
|
|
61
65
|
>;
|
|
62
66
|
type CheckboxProp = BaseProp<boolean, never, 'checkbox'>;
|
|
63
|
-
type TextProp = BaseProp<string, {
|
|
64
|
-
type
|
|
65
|
-
type
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
type TextProp = BaseProp<string, { label?: string }, 'text'>;
|
|
68
|
+
type TextareaProp = BaseProp<string, { height?: string }, 'textarea'>;
|
|
69
|
+
type ListProp = BaseProp<any[], never, 'list'>;
|
|
70
|
+
type ColorRepresentation =
|
|
71
|
+
| string
|
|
72
|
+
| THREE.Color
|
|
73
|
+
| { r: number; g: number; b: string; a?: number };
|
|
74
|
+
type ColorProp = BaseProp<ColorRepresentation, never, 'color'>;
|
|
75
|
+
type PaletteProp = BaseProp<
|
|
76
|
+
ColorRepresentation[],
|
|
77
|
+
{ extensible?: boolean; editable?: boolean },
|
|
78
|
+
'palette'
|
|
79
|
+
>;
|
|
80
|
+
type ButtonProp = BaseProp<() => void, { label?: string }, 'button'>;
|
|
81
|
+
type ImportProp = BaseProp<
|
|
82
|
+
(event: ProgressEvent) => void,
|
|
83
|
+
{ label?: string; accept?: string },
|
|
84
|
+
'import'
|
|
69
85
|
>;
|
|
70
|
-
type
|
|
71
|
-
() =>
|
|
72
|
-
{
|
|
73
|
-
'
|
|
86
|
+
type DownloadProp = BaseProp<
|
|
87
|
+
() => [any, string],
|
|
88
|
+
{ label?: string },
|
|
89
|
+
'download'
|
|
74
90
|
>;
|
|
75
91
|
type ImageProp = BaseProp<string, never, 'image'>;
|
|
76
92
|
|
|
93
|
+
type GradientStop = { color: ColorRepresentation; position: number };
|
|
94
|
+
|
|
95
|
+
type GradientProp = BaseProp<GradientStop[], never, 'gradient'>;
|
|
96
|
+
|
|
77
97
|
type Prop =
|
|
78
98
|
| SelectProp
|
|
79
99
|
| NumberProp
|
|
@@ -81,10 +101,15 @@ type Prop =
|
|
|
81
101
|
| VecProp<VecArray>
|
|
82
102
|
| CheckboxProp
|
|
83
103
|
| TextProp
|
|
104
|
+
| TextareaProp
|
|
84
105
|
| ListProp
|
|
85
106
|
| ColorProp
|
|
107
|
+
| PaletteProp
|
|
108
|
+
| ImportProp
|
|
109
|
+
| DownloadProp
|
|
86
110
|
| ButtonProp
|
|
87
|
-
| ImageProp
|
|
111
|
+
| ImageProp
|
|
112
|
+
| GradientProp;
|
|
88
113
|
|
|
89
114
|
export type Props = Record<string, Prop>;
|
|
90
115
|
|