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
package/src/cli/prompts.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
1
2
|
import { ConfirmPrompt, SelectPrompt, TextPrompt } from '@clack/core';
|
|
2
3
|
import isUnicodeSupported from 'is-unicode-supported';
|
|
3
4
|
import * as color from 'kleur/colors';
|
|
@@ -23,6 +24,7 @@ const S_BAR = s('│', '|');
|
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* @param {object} opts
|
|
27
|
+
* @param {string} opts.message
|
|
26
28
|
* @param {string} opts.active
|
|
27
29
|
* @param {string} opts.inactive
|
|
28
30
|
* @param {boolean} opts.initialValue
|
|
@@ -67,9 +69,9 @@ export const confirm = (opts) => {
|
|
|
67
69
|
* @param {string} [opts.placeholder]
|
|
68
70
|
* @param {string} [opts.defaultValue]
|
|
69
71
|
* @param {string} [opts.initialValue]
|
|
70
|
-
* @param {string} [opts.
|
|
71
|
-
* @param {
|
|
72
|
-
* @returns {Promise<string|symbol>}
|
|
72
|
+
* @param {string} [opts.hint]
|
|
73
|
+
* @param {(value: string | undefined) => any} [opts.validate]
|
|
74
|
+
* @returns {Promise<string | symbol | undefined>}
|
|
73
75
|
*/
|
|
74
76
|
export const text = (opts) => {
|
|
75
77
|
const { hint = '' } = opts;
|
|
@@ -82,24 +84,36 @@ export const text = (opts) => {
|
|
|
82
84
|
render() {
|
|
83
85
|
const title = `${opts.message}\n`;
|
|
84
86
|
const placeholder = opts.placeholder
|
|
85
|
-
?
|
|
86
|
-
|
|
87
|
-
:
|
|
88
|
-
const
|
|
89
|
-
? `${placeholder} ${
|
|
90
|
-
: this.
|
|
87
|
+
? styleText('inverse', opts.placeholder[0]) +
|
|
88
|
+
styleText('dim', opts.placeholder.slice(1))
|
|
89
|
+
: styleText(['inverse', 'hidden'], '_');
|
|
90
|
+
const userInput = !this.userInput
|
|
91
|
+
? `${placeholder} ${styleText('dim', hint)}`.trim()
|
|
92
|
+
: this.userInputWithCursor;
|
|
93
|
+
const value = this.value ?? '';
|
|
91
94
|
|
|
92
95
|
switch (this.state) {
|
|
93
|
-
case 'error':
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
case 'error': {
|
|
97
|
+
const errorText = this.error
|
|
98
|
+
? ` ${styleText('yellow', this.error)}`
|
|
99
|
+
: '';
|
|
100
|
+
return `${title.trim()}\n${userInput}\n${errorText}\n`;
|
|
101
|
+
}
|
|
102
|
+
case 'submit': {
|
|
103
|
+
const valueText = value
|
|
104
|
+
? `${styleText('green', value)}`
|
|
105
|
+
: '';
|
|
106
|
+
return `${styleText('dim', `${title}`)} ${valueText}\n`;
|
|
107
|
+
}
|
|
108
|
+
case 'cancel': {
|
|
109
|
+
const valueText = value
|
|
110
|
+
? ` ${styleText(['strikethrough', 'dim'], value)}`
|
|
111
|
+
: '';
|
|
112
|
+
return `${title}${valueText}${value.trim() ? `\n` : ''}`;
|
|
113
|
+
}
|
|
114
|
+
default: {
|
|
115
|
+
return `${title}${userInput}\n\n`;
|
|
116
|
+
}
|
|
103
117
|
}
|
|
104
118
|
},
|
|
105
119
|
}).prompt();
|
|
@@ -140,6 +154,22 @@ const limitOptions = (params) => {
|
|
|
140
154
|
});
|
|
141
155
|
};
|
|
142
156
|
|
|
157
|
+
/**
|
|
158
|
+
*
|
|
159
|
+
* @param {string} label
|
|
160
|
+
* @param {(text: string) => string} format
|
|
161
|
+
* @returns {string}
|
|
162
|
+
*/
|
|
163
|
+
const computeLabel = (label, format) => {
|
|
164
|
+
if (!label.includes('\n')) {
|
|
165
|
+
return format(label);
|
|
166
|
+
}
|
|
167
|
+
return label
|
|
168
|
+
.split('\n')
|
|
169
|
+
.map((line) => format(line))
|
|
170
|
+
.join('\n');
|
|
171
|
+
};
|
|
172
|
+
|
|
143
173
|
/**
|
|
144
174
|
* @typedef {string|boolean|number} Value
|
|
145
175
|
*/
|
|
@@ -149,12 +179,13 @@ const limitOptions = (params) => {
|
|
|
149
179
|
* @property {string} value
|
|
150
180
|
* @property {string} [label]
|
|
151
181
|
* @property {string} [hint]
|
|
182
|
+
* @property {boolean} [disabled]
|
|
152
183
|
*/
|
|
153
184
|
|
|
154
185
|
/**
|
|
155
186
|
* @param {object} opts
|
|
156
187
|
* @param {string} opts.message
|
|
157
|
-
* @param {
|
|
188
|
+
* @param {import('@clack/core').SelectOptions<Value>} opts.options
|
|
158
189
|
* @param {Value} [opts.initialValue]
|
|
159
190
|
* @param {number} [opts.maxItems]
|
|
160
191
|
* @returns {Promise<boolean|symbol>}
|
|
@@ -162,47 +193,69 @@ const limitOptions = (params) => {
|
|
|
162
193
|
export const select = (opts) => {
|
|
163
194
|
/**
|
|
164
195
|
*
|
|
165
|
-
* @param {Option
|
|
166
|
-
* @param {'inactive' | 'active' | 'selected' | 'cancelled'} state
|
|
196
|
+
* @param {Option} option
|
|
197
|
+
* @param {'inactive' | 'active' | 'selected' | 'cancelled' | 'disabled'} state
|
|
167
198
|
* @returns {string}
|
|
168
199
|
*/
|
|
169
200
|
const opt = (option, state) => {
|
|
170
201
|
const label = option.label ?? String(option.value);
|
|
171
202
|
switch (state) {
|
|
203
|
+
case 'disabled':
|
|
204
|
+
return `${styleText('gray', S_RADIO_INACTIVE)} ${computeLabel(label, (text) => styleText('gray', text))}${
|
|
205
|
+
option.hint
|
|
206
|
+
? ` ${styleText('dim', `(${option.hint ?? 'disabled'})`)}`
|
|
207
|
+
: ''
|
|
208
|
+
}`;
|
|
172
209
|
case 'selected':
|
|
173
|
-
return `${label}`;
|
|
210
|
+
return `${computeLabel(label, (text) => text)}`;
|
|
174
211
|
case 'active':
|
|
175
|
-
return `${
|
|
176
|
-
option.hint
|
|
212
|
+
return `${styleText('green', S_RADIO_ACTIVE)} ${label}${
|
|
213
|
+
option.hint
|
|
214
|
+
? ` ${styleText('dim', `(${option.hint})`)}`
|
|
215
|
+
: ''
|
|
177
216
|
}`;
|
|
178
217
|
case 'cancelled':
|
|
179
|
-
return `${
|
|
218
|
+
return `${computeLabel(label, (str) => styleText(['strikethrough', 'dim'], str))}`;
|
|
180
219
|
default:
|
|
181
|
-
return `${
|
|
220
|
+
return `${styleText('dim', S_RADIO_INACTIVE)} ${computeLabel(label, (text) => styleText('dim', text))}`;
|
|
182
221
|
}
|
|
183
222
|
};
|
|
184
223
|
|
|
185
224
|
return new SelectPrompt({
|
|
186
225
|
options: opts.options,
|
|
226
|
+
signal: opts.signal,
|
|
227
|
+
input: opts.input,
|
|
228
|
+
output: opts.output,
|
|
187
229
|
initialValue: opts.initialValue,
|
|
188
230
|
render() {
|
|
189
231
|
const title = `${opts.message}\n`;
|
|
190
232
|
|
|
191
233
|
switch (this.state) {
|
|
192
|
-
case 'submit':
|
|
193
|
-
return `${
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
)}\n`;
|
|
234
|
+
case 'submit': {
|
|
235
|
+
return `${styleText('dim', title)} ${styleText('green', opt(this.options[this.cursor], 'selected'))}\n`;
|
|
236
|
+
}
|
|
237
|
+
case 'cancel': {
|
|
238
|
+
return `${title}${opt(this.options[this.cursor], 'cancelled')}`;
|
|
239
|
+
}
|
|
199
240
|
default: {
|
|
200
|
-
|
|
241
|
+
const titleLineCount = title.split('\n').length;
|
|
242
|
+
const footerLineCount = 1;
|
|
243
|
+
return `${title}${limitOptions({
|
|
244
|
+
output: opts.output,
|
|
201
245
|
cursor: this.cursor,
|
|
202
246
|
options: this.options,
|
|
203
247
|
maxItems: opts.maxItems,
|
|
248
|
+
columnPadding: 0,
|
|
249
|
+
rowPadding: titleLineCount + footerLineCount,
|
|
204
250
|
style: (item, active) =>
|
|
205
|
-
opt(
|
|
251
|
+
opt(
|
|
252
|
+
item,
|
|
253
|
+
item.disabled
|
|
254
|
+
? 'disabled'
|
|
255
|
+
: active
|
|
256
|
+
? 'active'
|
|
257
|
+
: 'inactive',
|
|
258
|
+
),
|
|
206
259
|
}).join(`\n`)}\n`;
|
|
207
260
|
}
|
|
208
261
|
}
|
package/src/cli/run.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
FRAGMENT_DIRECTORY,
|
|
8
8
|
} from './createFragmentFile.js';
|
|
9
9
|
import { getEntries } from './getEntries.js';
|
|
10
|
-
import { log, magenta, bold, cyan
|
|
10
|
+
import { log, magenta, bold, cyan } from './log.js';
|
|
11
11
|
import save from './plugins/save.js';
|
|
12
12
|
import * as p from './prompts.js';
|
|
13
13
|
import { prettifyTime } from './utils.js';
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {HTMLElement} node
|
|
4
|
+
* @param {() => void} callback
|
|
5
|
+
* @returns
|
|
6
|
+
*/
|
|
1
7
|
export function resize(node, callback) {
|
|
2
8
|
if (typeof callback !== 'function') return;
|
|
3
9
|
|
|
10
|
+
/** @type {ResizeObserver | null} */
|
|
4
11
|
let observer = new ResizeObserver(callback);
|
|
5
12
|
|
|
6
13
|
observer.observe(node);
|
|
7
14
|
|
|
8
15
|
return {
|
|
9
16
|
destroy: () => {
|
|
10
|
-
observer
|
|
17
|
+
observer?.disconnect();
|
|
11
18
|
observer = null;
|
|
12
19
|
},
|
|
13
20
|
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {object} options
|
|
4
|
+
* @param {Function} options.onDragStart
|
|
5
|
+
* @param {Function} options.onDrag
|
|
6
|
+
* @param {Function} options.onDragEnd
|
|
7
|
+
* @returns {import('svelte/attachments').Attachment} */
|
|
8
|
+
export function draggable({ onDragStart, onDrag, onDragEnd } = {}) {
|
|
9
|
+
return (node) => {
|
|
10
|
+
let isDragging = false;
|
|
11
|
+
/** @type {MouseEvent | undefined} */
|
|
12
|
+
let eventStart;
|
|
13
|
+
/** @type {DOMRect | undefined} */
|
|
14
|
+
let rect;
|
|
15
|
+
let classNameDragging = 'fragment-dragging';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
*
|
|
19
|
+
* @param {MouseEvent} event
|
|
20
|
+
*/
|
|
21
|
+
function handleMouseDown(event) {
|
|
22
|
+
isDragging = true;
|
|
23
|
+
eventStart = event;
|
|
24
|
+
|
|
25
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
26
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
27
|
+
|
|
28
|
+
document.body.classList.add(classNameDragging);
|
|
29
|
+
|
|
30
|
+
rect = node.getBoundingClientRect();
|
|
31
|
+
|
|
32
|
+
const params = computeDrag(event);
|
|
33
|
+
|
|
34
|
+
onDragStart?.(event, params);
|
|
35
|
+
onDrag?.(event, params);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
* @param {MouseEvent} event
|
|
41
|
+
*/
|
|
42
|
+
function handleMouseMove(event) {
|
|
43
|
+
onDrag?.(event, computeDrag(event));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
* @param {MouseEvent} event
|
|
49
|
+
*/
|
|
50
|
+
function handleMouseUp(event) {
|
|
51
|
+
document.body.classList.remove(classNameDragging);
|
|
52
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
53
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
54
|
+
|
|
55
|
+
isDragging = false;
|
|
56
|
+
|
|
57
|
+
const params = computeDrag(event);
|
|
58
|
+
onDrag?.(event, params);
|
|
59
|
+
onDragEnd?.(event, params);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
*
|
|
64
|
+
* @param {MouseEvent} event
|
|
65
|
+
*/
|
|
66
|
+
function computeDrag(event) {
|
|
67
|
+
let distanceX = event.clientX - eventStart.clientX;
|
|
68
|
+
let distanceY = event.clientY - eventStart.clientY;
|
|
69
|
+
|
|
70
|
+
let distance = Math.sqrt(
|
|
71
|
+
distanceX * distanceX + distanceY * distanceY,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
distanceX,
|
|
76
|
+
distanceY,
|
|
77
|
+
distance,
|
|
78
|
+
isDragging,
|
|
79
|
+
rect,
|
|
80
|
+
node,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
node.addEventListener('mousedown', handleMouseDown);
|
|
85
|
+
|
|
86
|
+
return () => {
|
|
87
|
+
node.removeEventListener('mousedown', handleMouseDown);
|
|
88
|
+
|
|
89
|
+
rect = undefined;
|
|
90
|
+
eventStart = undefined;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
}
|
package/src/client/app/client.js
CHANGED
|
@@ -1,9 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} MessagePayload
|
|
3
|
+
* @property {string} event - The event name
|
|
4
|
+
* @property {any} [data] - Optional event data
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {Object} ShaderWarning
|
|
9
|
+
* @property {string} type - Warning type
|
|
10
|
+
* @property {string} importer - File that imported the shader
|
|
11
|
+
* @property {string} message - Warning message
|
|
12
|
+
* @property {Object} location - Location of the warning
|
|
13
|
+
* @property {string} location.lineText - The line of code with the warning
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {Object} ShaderUpdate
|
|
18
|
+
* @property {ShaderWarning[]} [warnings] - Array of shader warnings
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {Object} SketchUpdate
|
|
23
|
+
* @property {string} filepath
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @callback EventCallback
|
|
28
|
+
* @param {any} data - Event data
|
|
29
|
+
* @returns {void}
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @callback UnsubscribeFunction
|
|
34
|
+
* @returns {void}
|
|
35
|
+
*/
|
|
36
|
+
|
|
1
37
|
const socketProtocol = location.protocol === 'https:' ? 'wss' : 'ws';
|
|
2
38
|
const socketHost = `${location.hostname}:${__FRAGMENT_PORT__}`;
|
|
3
39
|
|
|
4
|
-
|
|
5
|
-
|
|
40
|
+
/** @type {WebSocket | undefined} */
|
|
41
|
+
let socket;
|
|
42
|
+
/** @type {Record<string, EventCallback[]>} */
|
|
43
|
+
let listeners = {};
|
|
44
|
+
let opened = false;
|
|
6
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Handle incoming WebSocket message
|
|
48
|
+
* @param {MessagePayload} payload - The message payload
|
|
49
|
+
* @returns {void}
|
|
50
|
+
*/
|
|
7
51
|
function handleMessage(payload) {
|
|
8
52
|
const { event, data = {} } = payload;
|
|
9
53
|
const callbacks = listeners[event];
|
|
@@ -13,6 +57,12 @@ function handleMessage(payload) {
|
|
|
13
57
|
}
|
|
14
58
|
}
|
|
15
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Subscribe to an event
|
|
62
|
+
* @param {string} event - The event name to listen for
|
|
63
|
+
* @param {EventCallback} cb - The callback function
|
|
64
|
+
* @returns {UnsubscribeFunction} Function to unsubscribe
|
|
65
|
+
*/
|
|
16
66
|
function on(event, cb) {
|
|
17
67
|
if (!listeners[event]) {
|
|
18
68
|
listeners[event] = [];
|
|
@@ -25,6 +75,12 @@ function on(event, cb) {
|
|
|
25
75
|
};
|
|
26
76
|
}
|
|
27
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Unsubscribe from an event
|
|
80
|
+
* @param {string} event - The event name
|
|
81
|
+
* @param {EventCallback} cb - The callback function to remove
|
|
82
|
+
* @returns {void}
|
|
83
|
+
*/
|
|
28
84
|
function off(event, cb) {
|
|
29
85
|
const callbacks = listeners[event];
|
|
30
86
|
|
|
@@ -35,9 +91,14 @@ function off(event, cb) {
|
|
|
35
91
|
}
|
|
36
92
|
}
|
|
37
93
|
|
|
38
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Emit an event to the server
|
|
96
|
+
* @param {string} event - The event name
|
|
97
|
+
* @param {any} data - The data to send
|
|
98
|
+
* @returns {void}
|
|
99
|
+
*/
|
|
39
100
|
function emit(event, data) {
|
|
40
|
-
if (opened) {
|
|
101
|
+
if (socket && opened) {
|
|
41
102
|
socket.send(
|
|
42
103
|
JSON.stringify({
|
|
43
104
|
event,
|
|
@@ -52,7 +113,7 @@ if (import.meta.hot) {
|
|
|
52
113
|
|
|
53
114
|
socket = new WebSocket(`${socketProtocol}://${socketHost}`);
|
|
54
115
|
|
|
55
|
-
socket.addEventListener('message',
|
|
116
|
+
socket.addEventListener('message', (message) => {
|
|
56
117
|
const { data } = message;
|
|
57
118
|
|
|
58
119
|
handleMessage(JSON.parse(data));
|
|
@@ -63,22 +124,33 @@ if (import.meta.hot) {
|
|
|
63
124
|
opened = true;
|
|
64
125
|
});
|
|
65
126
|
|
|
66
|
-
import.meta.hot.on(
|
|
67
|
-
|
|
68
|
-
|
|
127
|
+
import.meta.hot.on(
|
|
128
|
+
'sketch-update',
|
|
129
|
+
/** @param {SketchUpdate} sketchUpdate */
|
|
130
|
+
(sketchUpdate) => {
|
|
131
|
+
console.log(`[fragment] hmr update /${sketchUpdate.filepath}`);
|
|
132
|
+
},
|
|
133
|
+
);
|
|
69
134
|
}
|
|
70
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Client API for WebSocket communication
|
|
138
|
+
* @type {{ on: typeof on, off: typeof off, emit: typeof emit }}
|
|
139
|
+
*/
|
|
71
140
|
export const client = { on, off, emit };
|
|
72
141
|
|
|
73
142
|
client.on('shader-update', (shaderUpdates) => {
|
|
74
|
-
shaderUpdates.forEach(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
143
|
+
shaderUpdates.forEach(
|
|
144
|
+
/** @param {ShaderUpdate} shaderUpdate */
|
|
145
|
+
({ warnings = [] } = {}) => {
|
|
146
|
+
if (warnings.length > 0) {
|
|
147
|
+
warnings.forEach((warning) => {
|
|
148
|
+
const { location } = warning;
|
|
149
|
+
console.warn(
|
|
150
|
+
`[fragment-plugin-hsr] ${warning.type} ${warning.importer}\n\n ${location.lineText}\n\n${warning.message}`,
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
);
|
|
84
156
|
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
let { angle = 0 } = $props();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<svg
|
|
6
|
+
width="16"
|
|
7
|
+
height="16"
|
|
8
|
+
fill="none"
|
|
9
|
+
viewBox="0 0 24 24"
|
|
10
|
+
style="--angle: {angle}"
|
|
11
|
+
>
|
|
12
|
+
<path
|
|
13
|
+
stroke="currentColor"
|
|
14
|
+
stroke-linecap="round"
|
|
15
|
+
stroke-linejoin="round"
|
|
16
|
+
stroke-width="1.5"
|
|
17
|
+
d="M18 8H6"
|
|
18
|
+
></path>
|
|
19
|
+
<path
|
|
20
|
+
stroke="currentColor"
|
|
21
|
+
stroke-linecap="round"
|
|
22
|
+
stroke-linejoin="round"
|
|
23
|
+
stroke-width="1.5"
|
|
24
|
+
d="M10 5L6 8L10 11"
|
|
25
|
+
></path>
|
|
26
|
+
<path
|
|
27
|
+
stroke="currentColor"
|
|
28
|
+
stroke-linecap="round"
|
|
29
|
+
stroke-linejoin="round"
|
|
30
|
+
stroke-width="1.5"
|
|
31
|
+
d="M6 16H18"
|
|
32
|
+
></path>
|
|
33
|
+
<path
|
|
34
|
+
stroke="currentColor"
|
|
35
|
+
stroke-linecap="round"
|
|
36
|
+
stroke-linejoin="round"
|
|
37
|
+
stroke-width="1.5"
|
|
38
|
+
d="M14 13L18 16L14 19"
|
|
39
|
+
></path>
|
|
40
|
+
</svg>
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
svg {
|
|
44
|
+
transform: rotate(var(--angle));
|
|
45
|
+
}
|
|
46
|
+
</style>
|
package/src/client/app/hooks.js
CHANGED
|
@@ -2,18 +2,42 @@ import { rendering } from './state/rendering.svelte';
|
|
|
2
2
|
import { sketchesManager } from './state/sketches.svelte';
|
|
3
3
|
import { getContext } from './triggers/shared';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Register a callback to be called before capturing
|
|
7
|
+
* @param {Function} listener - The callback function to execute before capture
|
|
8
|
+
* @param {string} [context] - The sketch context (defaults to current context)
|
|
9
|
+
* @returns {void}
|
|
10
|
+
*/
|
|
5
11
|
export const onBeforeCapture = (listener, context = getContext()) => {
|
|
6
12
|
sketchesManager.sketches[context]?.onBeforeCapture(listener);
|
|
7
13
|
};
|
|
8
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Register a callback to be called after capturing
|
|
17
|
+
* @param {Function} listener - The callback function to execute after capture
|
|
18
|
+
* @param {string} [context] - The sketch context (defaults to current context)
|
|
19
|
+
* @returns {void}
|
|
20
|
+
*/
|
|
9
21
|
export const onAfterCapture = (listener, context = getContext()) => {
|
|
10
22
|
sketchesManager.sketches[context]?.onAfterCapture(listener);
|
|
11
23
|
};
|
|
12
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Register a callback to be called before recording
|
|
27
|
+
* @param {Function} listener - The callback function to execute before recording
|
|
28
|
+
* @param {string} [context] - The sketch context (defaults to current context)
|
|
29
|
+
* @returns {void}
|
|
30
|
+
*/
|
|
13
31
|
export const onBeforeRecord = (listener, context = getContext()) => {
|
|
14
32
|
sketchesManager.sketches[context]?.onBeforeRecord(listener);
|
|
15
33
|
};
|
|
16
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Register a callback to be called after recording
|
|
37
|
+
* @param {Function} listener - The callback function to execute after recording
|
|
38
|
+
* @param {string} [context] - The sketch context (defaults to current context)
|
|
39
|
+
* @returns {void}
|
|
40
|
+
*/
|
|
17
41
|
export const onAfterRecord = (listener, context = getContext()) => {
|
|
18
42
|
sketchesManager.sketches[context]?.onAfterRecord(listener);
|
|
19
43
|
};
|
|
@@ -23,7 +47,7 @@ export const onAfterRecord = (listener, context = getContext()) => {
|
|
|
23
47
|
* @param {object} options
|
|
24
48
|
* @param {string} [options.filename]
|
|
25
49
|
* @param {function} [options.pattern]
|
|
26
|
-
* @param {
|
|
50
|
+
* @param {string} [options.exportDir]
|
|
27
51
|
*/
|
|
28
52
|
export async function screenshot({ filename, pattern, exportDir } = {}) {
|
|
29
53
|
const context = getContext();
|