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.
Files changed (67) hide show
  1. package/package.json +12 -11
  2. package/src/cli/build.js +1 -0
  3. package/src/cli/create.js +22 -4
  4. package/src/cli/createConfig.js +2 -2
  5. package/src/cli/getEntries.js +10 -1
  6. package/src/cli/plugins/hot-shader-replacement.js +54 -16
  7. package/src/cli/plugins/save.js +97 -38
  8. package/src/cli/prompts.js +89 -36
  9. package/src/cli/run.js +1 -1
  10. package/src/cli/templates/blank/index.ts +1 -1
  11. package/src/cli/templates/default/index.js +10 -2
  12. package/src/cli/templates/default/index.ts +5 -2
  13. package/src/cli/templates/fragment-gl/index.ts +1 -1
  14. package/src/cli/templates/p5/index.ts +1 -1
  15. package/src/cli/templates/p5-webgl/index.ts +1 -1
  16. package/src/cli/templates/three-fragment/index.js +5 -3
  17. package/src/cli/templates/three-fragment/index.ts +5 -4
  18. package/src/cli/templates/three-orthographic/index.js +6 -1
  19. package/src/cli/templates/three-orthographic/index.ts +6 -2
  20. package/src/cli/templates/three-perspective/index.js +6 -1
  21. package/src/cli/templates/three-perspective/index.ts +6 -2
  22. package/src/client/app/actions/resize.js +8 -1
  23. package/src/client/app/attachments/draggable.js +93 -0
  24. package/src/client/app/client.js +90 -18
  25. package/src/client/app/components/IconFlip.svelte +46 -0
  26. package/src/client/app/hooks.js +25 -1
  27. package/src/client/app/lib/canvas-recorder/CanvasRecorder.js +95 -3
  28. package/src/client/app/lib/canvas-recorder/FrameRecorder.js +45 -3
  29. package/src/client/app/lib/canvas-recorder/GIFRecorder.js +72 -13
  30. package/src/client/app/lib/canvas-recorder/MediaBunnyRecorder.js +43 -9
  31. package/src/client/app/lib/canvas-recorder/utils.js +18 -9
  32. package/src/client/app/modules/Params.svelte +1 -0
  33. package/src/client/app/renderers/2DRenderer.js +20 -16
  34. package/src/client/app/renderers/P5GLRenderer.js +13 -5
  35. package/src/client/app/renderers/P5Renderer.js +9 -1
  36. package/src/client/app/renderers/THREERenderer.js +63 -48
  37. package/src/client/app/state/Sketch.svelte.js +150 -10
  38. package/src/client/app/state/errors.svelte.js +19 -0
  39. package/src/client/app/state/exports.svelte.js +14 -1
  40. package/src/client/app/state/rendering.svelte.js +90 -13
  41. package/src/client/app/state/sketches.svelte.js +43 -7
  42. package/src/client/app/state/utils.svelte.js +49 -0
  43. package/src/client/app/ui/Field.svelte +63 -16
  44. package/src/client/app/ui/FieldSection.svelte +4 -4
  45. package/src/client/app/ui/ParamsOutput.svelte +7 -5
  46. package/src/client/app/ui/SketchRenderer.svelte +21 -0
  47. package/src/client/app/ui/fields/ButtonInput.svelte +2 -0
  48. package/src/client/app/ui/fields/CheckboxInput.svelte +13 -11
  49. package/src/client/app/ui/fields/ColorInput.svelte +16 -11
  50. package/src/client/app/ui/fields/GradientInput.svelte +607 -0
  51. package/src/client/app/ui/fields/Input.svelte +10 -6
  52. package/src/client/app/ui/fields/IntervalInput.svelte +27 -35
  53. package/src/client/app/ui/fields/NumberInput.svelte +51 -13
  54. package/src/client/app/ui/fields/PaletteInput.svelte +181 -0
  55. package/src/client/app/ui/fields/ProgressInput.svelte +44 -16
  56. package/src/client/app/ui/fields/TextareaInput.svelte +93 -0
  57. package/src/client/app/utils/canvas.utils.js +105 -28
  58. package/src/client/app/utils/color.utils.js +74 -17
  59. package/src/client/app/utils/fields.utils.js +70 -17
  60. package/src/client/app/utils/file.utils.js +86 -31
  61. package/src/client/app/utils/glsl.utils.js +11 -2
  62. package/src/client/app/utils/glslErrors.js +31 -21
  63. package/src/client/app/utils/index.js +28 -12
  64. package/src/client/main.js +7 -1
  65. package/src/types/global.d.ts +143 -0
  66. package/src/types/props.d.ts +41 -15
  67. package/tsconfig.json +1 -1
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * @typedef {Object} File
3
- * @property {string} filepath
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
- resolve(e.target.result);
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
- if (typeof data === 'object' && ['json', 'txt'].includes(extension)) {
67
- data = JSON.stringify(data, undefined, 4);
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([data], { type });
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(blob);
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
- await downloadBlob(blob, { filename });
182
+ downloadBlob(blob, { filename });
156
183
  }
157
184
 
158
185
  if (Array.isArray(files)) {
159
- return Promise.all(files.map((file) => saveFile(file)));
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
- * @returns {Promise<string[]>}
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
- file.size = estimateFileSize(file.data);
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
- if (size < limitInMb) {
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
- const { filepaths, error } = await response.json();
241
+ /** @type {{ filepaths: string[], warnings: string[], errors: string[]}} */
242
+ const { filepaths, warnings, errors } = await response.json();
204
243
 
205
- if (response.ok && filepaths?.length) {
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
- * @returns {Promise<string[]>}
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
- const regex = /<filepath:\/\/(.*)>/;
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 { source, lineNumber, message } = getShaderError(
116
- gl,
117
- shader,
118
- shader.__type,
119
- );
120
-
121
- const error = new ShaderCompileError({
122
- source,
123
- message,
124
- filename,
125
- lineNumber,
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).trim();
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
- * @param {Map} map
4
- * @param {string} key
5
- * @param {any} find
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
- if (map.has(key)) {
9
- map.set(key, [...map.get(key), item]);
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
- * @param {Map} map
18
- * @param {string} key
19
- * @param {function} findIndex
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
- if (map.has(key)) {
23
- const items = map.get(key);
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) {
@@ -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: document.getElementById('app'),
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
+ }
@@ -2,6 +2,7 @@ type BaseProp<Value, Params, Type> = {
2
2
  value: Value;
3
3
  params?: Params;
4
4
  type?: Type;
5
+ disabled?: boolean;
5
6
  hidden?: boolean;
6
7
  displayName?: string | null;
7
8
  folder?: string;
@@ -10,19 +11,23 @@ type BaseProp<Value, Params, Type> = {
10
11
  };
11
12
 
12
13
  type SelectProp = BaseProp<
13
- number | string,
14
+ number | string | undefined,
14
15
  {
15
16
  options?:
16
17
  | number[]
17
18
  | string[]
19
+ | undefined[]
18
20
  | Array<{ label?: string; value: number }>
19
- | Array<{ label?: string; value: string }>;
21
+ | Array<{ label?: string; value: string | undefined }>;
20
22
  },
21
23
  'select'
22
24
  >;
23
25
  type NumberProp = BaseProp<
24
26
  number,
25
- { disabled?: boolean; step?: number } | { min: number; max: number },
27
+ ({ min?: never; max?: never } | { min: number; max: number }) & {
28
+ step?: number;
29
+ suffix?: string;
30
+ },
26
31
  'number'
27
32
  >;
28
33
 
@@ -55,24 +60,40 @@ type VecParams<V extends VecValue> = V extends readonly number[]
55
60
 
56
61
  type VecProp<V extends VecValue = VecValue> = BaseProp<
57
62
  V,
58
- { locked?: boolean } | VecParams<V>,
63
+ { locked?: boolean; suffix?: string } & VecParams<V>,
59
64
  'vec'
60
65
  >;
61
66
  type CheckboxProp = BaseProp<boolean, never, 'checkbox'>;
62
- type TextProp = BaseProp<string, { disabled?: boolean }, 'text'>;
63
- type ListProp = BaseProp<any[], { disabled?: boolean }, 'list'>;
64
- type ColorProp = BaseProp<
65
- string | THREE.Color | { r: number; g: number; b: string; a?: number },
66
- never,
67
- 'color'
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'
68
85
  >;
69
- type ButtonProp = BaseProp<
70
- () => void,
71
- { disabled?: boolean; label?: string },
72
- 'button' | 'download'
86
+ type DownloadProp = BaseProp<
87
+ () => [any, string],
88
+ { label?: string },
89
+ 'download'
73
90
  >;
74
91
  type ImageProp = BaseProp<string, never, 'image'>;
75
92
 
93
+ type GradientStop = { color: ColorRepresentation; position: number };
94
+
95
+ type GradientProp = BaseProp<GradientStop[], never, 'gradient'>;
96
+
76
97
  type Prop =
77
98
  | SelectProp
78
99
  | NumberProp
@@ -80,10 +101,15 @@ type Prop =
80
101
  | VecProp<VecArray>
81
102
  | CheckboxProp
82
103
  | TextProp
104
+ | TextareaProp
83
105
  | ListProp
84
106
  | ColorProp
107
+ | PaletteProp
108
+ | ImportProp
109
+ | DownloadProp
85
110
  | ButtonProp
86
- | ImageProp;
111
+ | ImageProp
112
+ | GradientProp;
87
113
 
88
114
  export type Props = Record<string, Prop>;
89
115
 
package/tsconfig.json CHANGED
@@ -20,7 +20,7 @@
20
20
  /* Linting */
21
21
  "strict": true,
22
22
  "allowJs": true,
23
- "checkJs": false,
23
+ "checkJs": true,
24
24
  "noUnusedLocals": true,
25
25
  "noUnusedParameters": true,
26
26
  "noFallthroughCasesInSwitch": true,