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,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persists data to localStorage with a "fragment." prefix
|
|
3
|
+
* @param {string} key - The storage key (will be prefixed with "fragment.")
|
|
4
|
+
* @param {any} data - The data to store (will be JSON stringified)
|
|
5
|
+
* @throws {Error} If localStorage operation fails
|
|
6
|
+
*/
|
|
1
7
|
export function persist(key, data) {
|
|
2
8
|
try {
|
|
3
9
|
window.localStorage.setItem(`fragment.${key}`, JSON.stringify(data));
|
|
@@ -6,6 +12,13 @@ export function persist(key, data) {
|
|
|
6
12
|
}
|
|
7
13
|
}
|
|
8
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Retrieves and optionally merges data from localStorage
|
|
17
|
+
* @param {string} key - The storage key (will be prefixed with "fragment.")
|
|
18
|
+
* @param {Record<string, any>} [target={}] - Optional target object to merge data into
|
|
19
|
+
* @param {any} [defaultValue={}] - Default value to return if no data found
|
|
20
|
+
* @returns {any} The retrieved data, defaultValue if not found, or undefined on error
|
|
21
|
+
*/
|
|
9
22
|
export function hydrate(key, target = {}, defaultValue = {}) {
|
|
10
23
|
try {
|
|
11
24
|
const storageKey = `fragment.${key}`;
|
|
@@ -31,14 +44,39 @@ export function hydrate(key, target = {}, defaultValue = {}) {
|
|
|
31
44
|
}
|
|
32
45
|
}
|
|
33
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Checks if a value is an object
|
|
49
|
+
* @param {any} item - The value to check
|
|
50
|
+
* @returns {boolean} True if the value is an object
|
|
51
|
+
*/
|
|
34
52
|
export function isObject(item) {
|
|
35
53
|
return item && typeof item === 'object';
|
|
36
54
|
}
|
|
37
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Checks if a value is a function
|
|
58
|
+
* @param {any} item - The value to check
|
|
59
|
+
* @returns {boolean} True if the value is a function
|
|
60
|
+
*/
|
|
38
61
|
export function isFunction(item) {
|
|
39
62
|
return item && typeof item === 'function';
|
|
40
63
|
}
|
|
41
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Returns true if the given cache key contains the data:image scheme.
|
|
67
|
+
* @param {any} value
|
|
68
|
+
* @return {boolean} Whether the given cache url contains the blob: scheme or not.
|
|
69
|
+
*/
|
|
70
|
+
export function isDataURL(value) {
|
|
71
|
+
return typeof value === 'string' && value.startsWith('data:image/');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Recursively assigns properties from source to target
|
|
76
|
+
* @param {Record<string, any>} target - The target object to assign to
|
|
77
|
+
* @param {Record<string, any>} source - The source object to assign from
|
|
78
|
+
* @returns {void}
|
|
79
|
+
*/
|
|
42
80
|
export function deepAssign(target, source) {
|
|
43
81
|
for (const key in source) {
|
|
44
82
|
if (isObject(source[key]) && isObject(target[key])) {
|
|
@@ -49,6 +87,12 @@ export function deepAssign(target, source) {
|
|
|
49
87
|
}
|
|
50
88
|
}
|
|
51
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Recursively compares two values for deep equality
|
|
92
|
+
* @param {any} target - The first value to compare
|
|
93
|
+
* @param {any} source - The second value to compare
|
|
94
|
+
* @returns {boolean} True if the values are deeply equal
|
|
95
|
+
*/
|
|
52
96
|
export function deepEqual(target, source) {
|
|
53
97
|
if (isObject(target) && isObject(source)) {
|
|
54
98
|
let isEqual = true;
|
|
@@ -75,6 +119,11 @@ export function deepEqual(target, source) {
|
|
|
75
119
|
return target === source;
|
|
76
120
|
}
|
|
77
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Creates a deep clone of a value
|
|
124
|
+
* @param {any} value - The value to clone
|
|
125
|
+
* @returns {any} A deep clone of the value, or the original value if cloning fails
|
|
126
|
+
*/
|
|
78
127
|
export function deepClone(value) {
|
|
79
128
|
if (isFunction(value)) {
|
|
80
129
|
return value;
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
import ButtonInput from './fields/ButtonInput.svelte';
|
|
11
11
|
import ImageInput from './fields/ImageInput.svelte';
|
|
12
12
|
import IntervalInput from './fields/IntervalInput.svelte';
|
|
13
|
+
import PaletteInput from './fields/PaletteInput.svelte';
|
|
14
|
+
import GradientInput from './fields/GradientInput.svelte';
|
|
13
15
|
import { fieldTypes } from '../utils/fields.utils.js';
|
|
14
16
|
|
|
15
17
|
const fields = {
|
|
@@ -21,11 +23,14 @@
|
|
|
21
23
|
[`${fieldTypes.TEXTAREA}`]: TextareaInput,
|
|
22
24
|
[`${fieldTypes.LIST}`]: ListInput,
|
|
23
25
|
[`${fieldTypes.COLOR}`]: ColorInput,
|
|
26
|
+
[`${fieldTypes.PALETTE}`]: PaletteInput,
|
|
24
27
|
[`${fieldTypes.BUTTON}`]: ButtonInput,
|
|
25
28
|
[`${fieldTypes.DOWNLOAD}`]: ButtonInput,
|
|
26
29
|
[`${fieldTypes.IMPORT}`]: ImportInput,
|
|
27
30
|
[`${fieldTypes.IMAGE}`]: ImageInput,
|
|
28
31
|
[`${fieldTypes.INTERVAL}`]: IntervalInput,
|
|
32
|
+
[`${fieldTypes.GRADIENT}`]: GradientInput,
|
|
33
|
+
[`${fieldTypes.WRAPPER}`]: null,
|
|
29
34
|
};
|
|
30
35
|
</script>
|
|
31
36
|
|
|
@@ -140,7 +145,7 @@
|
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
function restoreInitialValue() {
|
|
143
|
-
onchange(initialValue);
|
|
148
|
+
onchange($state.snapshot(initialValue));
|
|
144
149
|
}
|
|
145
150
|
</script>
|
|
146
151
|
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
let {
|
|
3
3
|
key,
|
|
4
4
|
visible = true,
|
|
5
|
-
secondary,
|
|
6
|
-
interactive,
|
|
5
|
+
secondary = false,
|
|
6
|
+
interactive = false,
|
|
7
7
|
displayName = undefined,
|
|
8
8
|
disabled = false,
|
|
9
9
|
children,
|
|
10
|
-
infos,
|
|
11
|
-
onclick,
|
|
10
|
+
infos = undefined,
|
|
11
|
+
onclick = () => {},
|
|
12
12
|
} = $props();
|
|
13
13
|
</script>
|
|
14
14
|
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
/>
|
|
103
103
|
{/if}
|
|
104
104
|
{#if rendering.resizing === SIZES.PRESET}
|
|
105
|
-
<Field key="preset">
|
|
105
|
+
<Field key="preset" type="wrapper" value={`${rendering.preset}-${rendering.presetOrientation}`}>
|
|
106
106
|
<FieldInputRow --grid-template-columns="1fr 1fr">
|
|
107
107
|
<Select
|
|
108
108
|
value={rendering.preset}
|
|
@@ -74,6 +74,11 @@
|
|
|
74
74
|
render.screenshot();
|
|
75
75
|
exports.capturing = false;
|
|
76
76
|
}
|
|
77
|
+
|
|
78
|
+
if (exports.committing) {
|
|
79
|
+
render.commit();
|
|
80
|
+
exports.committing = false;
|
|
81
|
+
}
|
|
77
82
|
});
|
|
78
83
|
|
|
79
84
|
function checkForRefresh(event) {
|
|
@@ -113,6 +118,16 @@
|
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
120
|
|
|
121
|
+
function checkForCommit(event) {
|
|
122
|
+
if (event.metaKey || event.ctrlKey) {
|
|
123
|
+
event.preventDefault();
|
|
124
|
+
|
|
125
|
+
if (!exports.committing) {
|
|
126
|
+
render.commit();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
116
131
|
let backgroundColor = $derived.by(() => {
|
|
117
132
|
if (layout.previewing) {
|
|
118
133
|
return (
|
|
@@ -154,6 +169,7 @@
|
|
|
154
169
|
<KeyBinding type="down" key=" " onTrigger={checkForPause} />
|
|
155
170
|
<KeyBinding type="down" key="s" onTrigger={checkForScreenshot} />
|
|
156
171
|
<KeyBinding type="down" key="S" onTrigger={checkForRecord} />
|
|
172
|
+
<KeyBinding type="down" key="k" onTrigger={checkForCommit} />
|
|
157
173
|
|
|
158
174
|
<style>
|
|
159
175
|
.sketch-renderer {
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} = $props();
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {Object} Props
|
|
4
|
+
* @property {boolean} value
|
|
5
|
+
* @property {boolean} disabled
|
|
6
|
+
* @property {(value: boolean) => void|undefined} onchange
|
|
7
|
+
*/
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
/** @type {Props} */
|
|
10
|
+
let { value = $bindable(), disabled = false, onchange } = $props();
|
|
11
|
+
|
|
12
|
+
function handleChange() {
|
|
13
|
+
onchange?.(value);
|
|
14
|
+
}
|
|
13
15
|
</script>
|
|
14
16
|
|
|
15
17
|
<div class="checkbox" class:disabled>
|
|
@@ -18,7 +20,7 @@
|
|
|
18
20
|
bind:checked={value}
|
|
19
21
|
type="checkbox"
|
|
20
22
|
onchange={handleChange}
|
|
21
|
-
|
|
23
|
+
{disabled}
|
|
22
24
|
/>
|
|
23
25
|
</div>
|
|
24
26
|
|
|
@@ -16,18 +16,21 @@
|
|
|
16
16
|
let textValue = $state();
|
|
17
17
|
let alpha = $state(1);
|
|
18
18
|
let hasAlpha = $derived(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
format &&
|
|
20
|
+
[
|
|
21
|
+
color.FORMATS.RGBA_STRING,
|
|
22
|
+
color.FORMATS.VEC4_STRING,
|
|
23
|
+
color.FORMATS.VEC4_ARRAY,
|
|
24
|
+
color.FORMATS.RGBA_OBJECT,
|
|
25
|
+
color.FORMATS.RGBA_OBJECT_STRING,
|
|
26
|
+
color.FORMATS.HSLA_STRING,
|
|
27
|
+
].includes(format),
|
|
26
28
|
);
|
|
27
29
|
|
|
28
30
|
$effect(() => {
|
|
29
31
|
if (hasAlpha) {
|
|
30
|
-
const
|
|
32
|
+
const components = color.toComponents(value);
|
|
33
|
+
const a = components[3] ?? 1;
|
|
31
34
|
alpha = a;
|
|
32
35
|
} else {
|
|
33
36
|
alpha = 1;
|
|
@@ -143,7 +146,7 @@
|
|
|
143
146
|
<input
|
|
144
147
|
class="input"
|
|
145
148
|
type="color"
|
|
146
|
-
|
|
149
|
+
{disabled}
|
|
147
150
|
value={hexValue}
|
|
148
151
|
onblur={handleBlur}
|
|
149
152
|
oninput={onInput}
|
|
@@ -212,7 +215,7 @@
|
|
|
212
215
|
bottom: var(--gap);
|
|
213
216
|
|
|
214
217
|
background-color: var(--currentColor);
|
|
215
|
-
border-radius: calc(var(--fragment-input-border-radius)
|
|
218
|
+
border-radius: calc(var(--fragment-input-border-radius) - var(--gap));
|
|
216
219
|
opacity: var(--opacity, 1);
|
|
217
220
|
pointer-events: none;
|
|
218
221
|
}
|
|
@@ -224,7 +227,9 @@
|
|
|
224
227
|
var(--box-shadow-color, var(--fragment-accent-color));
|
|
225
228
|
}
|
|
226
229
|
|
|
227
|
-
.
|
|
230
|
+
:global(body:not(.fragment-dragging))
|
|
231
|
+
.color-input:not(.disabled)
|
|
232
|
+
.mirror:focus-within {
|
|
228
233
|
box-shadow: 0 0 0 2px
|
|
229
234
|
var(--box-shadow-color, var(--fragment-accent-color));
|
|
230
235
|
}
|