fragment-tools 0.2.2 → 0.2.3
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 +1 -1
- package/src/client/app/components/Build.svelte +64 -0
- package/src/client/app/{ui → components}/Preview.svelte +1 -2
- package/src/client/app/modules/Params.svelte +3 -0
- package/src/client/app/state/Sketch.svelte.js +12 -35
- package/src/client/app/state/layout.svelte.js +8 -2
- package/src/client/app/state/rendering.svelte.js +5 -2
- package/src/client/app/state/utils.svelte.js +15 -4
- package/src/client/app/ui/Field.svelte +3 -1
- package/src/client/app/ui/FieldGroup.svelte +5 -2
- package/src/client/app/ui/FieldSection.svelte +4 -4
- package/src/client/app/ui/Layout.svelte +1 -1
- package/src/client/app/ui/LayoutBuild.svelte +54 -0
- package/src/client/app/ui/LayoutColumn.svelte +2 -2
- package/src/client/app/ui/LayoutComponent.svelte +14 -4
- package/src/client/app/ui/LayoutResizer.svelte +11 -2
- package/src/client/app/ui/LayoutRow.svelte +2 -2
- package/src/client/app/ui/fields/ButtonInput.svelte +3 -3
- package/src/client/app/ui/fields/ColorInput.svelte +23 -13
- package/src/client/app/ui/fields/ImportInput.svelte +52 -0
- package/src/client/app/ui/fields/Input.svelte +4 -2
- package/src/client/app/ui/fields/IntervalInput.svelte +8 -6
- package/src/client/app/ui/fields/ProgressInput.svelte +6 -3
- package/src/client/app/ui/fields/Select.svelte +35 -41
- package/src/client/app/utils/fields.utils.js +1 -0
- package/src/client/public/css/global.css +14 -0
- package/src/client/app/ui/Build.svelte +0 -91
package/package.json
CHANGED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { onDestroy } from 'svelte';
|
|
3
|
+
import { sketchesManager } from '../state/sketches.svelte';
|
|
4
|
+
import { rendering } from '../state/rendering.svelte';
|
|
5
|
+
import { layout } from '../state/layout.svelte';
|
|
6
|
+
import LayoutBuild from '../ui/LayoutBuild.svelte';
|
|
7
|
+
|
|
8
|
+
console.log(`Made with Fragment. https://fragment.tools`);
|
|
9
|
+
|
|
10
|
+
let sketchKey = $derived(sketchesManager.keys[0]);
|
|
11
|
+
let sketch = $derived(sketchesManager.sketches[sketchKey]);
|
|
12
|
+
let buildConfig = $derived(sketch?.buildConfig ?? {});
|
|
13
|
+
let persistent = $derived(buildConfig.layout?.persistent ?? false);
|
|
14
|
+
let styles = $derived(buildConfig?.styles ?? '');
|
|
15
|
+
|
|
16
|
+
/** @type {HTMLHeadElement} */
|
|
17
|
+
let head;
|
|
18
|
+
/** @type {HTMLStyleElement} */
|
|
19
|
+
let style;
|
|
20
|
+
|
|
21
|
+
$effect(() => {
|
|
22
|
+
if (__BUILD__) {
|
|
23
|
+
layout.persistent = persistent;
|
|
24
|
+
} else if (persistent && layout.previewing) {
|
|
25
|
+
console.warn(`Layout is not preserved while previewing`);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
$effect(() => {
|
|
30
|
+
rendering.override(sketch?.buildConfig);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
$effect(() => {
|
|
34
|
+
if (styles !== '') {
|
|
35
|
+
head = document.getElementsByTagName('head')[0];
|
|
36
|
+
|
|
37
|
+
if (style) {
|
|
38
|
+
head.removeChild(style);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
style = document.createElement('style');
|
|
42
|
+
style.setAttribute('type', 'text/css');
|
|
43
|
+
style.appendChild(document.createTextNode(styles));
|
|
44
|
+
head.appendChild(style);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
onDestroy(() => {
|
|
49
|
+
if (style && head) {
|
|
50
|
+
head.removeChild(style);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
{#if sketch}
|
|
56
|
+
{#if buildConfig.layout?.component}
|
|
57
|
+
{#await buildConfig.layout.component() then layoutModule}
|
|
58
|
+
{@const LayoutBuildCustom = layoutModule.default}
|
|
59
|
+
<LayoutBuildCustom {sketchKey} {buildConfig} {sketch} />
|
|
60
|
+
{/await}
|
|
61
|
+
{:else}
|
|
62
|
+
<LayoutBuild {sketchKey} {sketch} {buildConfig} />
|
|
63
|
+
{/if}
|
|
64
|
+
{/if}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { onDestroy, onMount } from 'svelte';
|
|
3
|
-
|
|
4
2
|
import { rendering } from '../state/rendering.svelte';
|
|
3
|
+
import { onDestroy } from 'svelte';
|
|
5
4
|
import Build from './Build.svelte';
|
|
6
5
|
|
|
7
6
|
const { resizing, width, height, pixelRatio, aspectRatio, scale, preset } =
|
|
@@ -181,19 +181,20 @@ class Sketch {
|
|
|
181
181
|
params = {},
|
|
182
182
|
triggers = [],
|
|
183
183
|
group,
|
|
184
|
+
type,
|
|
184
185
|
folder,
|
|
185
186
|
displayName,
|
|
186
187
|
} = instanceProp;
|
|
187
188
|
|
|
188
|
-
if (value
|
|
189
|
+
if (value?.isColor) {
|
|
189
190
|
value = { r: value.r, g: value.g, b: value.b };
|
|
190
|
-
} else if (value
|
|
191
|
+
} else if (value?.isVector2) {
|
|
191
192
|
value = { x: value.x, y: value.y };
|
|
192
|
-
} else if (value
|
|
193
|
+
} else if (value?.isVector3) {
|
|
193
194
|
value = { x: value.x, y: value.y, z: value.z };
|
|
194
|
-
} else if (value
|
|
195
|
+
} else if (value?.isVector4) {
|
|
195
196
|
value = { x: value.x, y: value.y, z: value.z, w: value.w };
|
|
196
|
-
} else if (value
|
|
197
|
+
} else if (value?.isQuaternion) {
|
|
197
198
|
value = { x: value.x, y: value.y, z: value.z, w: value.w };
|
|
198
199
|
}
|
|
199
200
|
|
|
@@ -217,6 +218,7 @@ class Sketch {
|
|
|
217
218
|
__initialValue: initialValue,
|
|
218
219
|
__currentValue: value,
|
|
219
220
|
__hidden,
|
|
221
|
+
type,
|
|
220
222
|
params: structuredClone(params),
|
|
221
223
|
triggers,
|
|
222
224
|
group,
|
|
@@ -239,9 +241,7 @@ class Sketch {
|
|
|
239
241
|
}
|
|
240
242
|
|
|
241
243
|
if (instanceProp) {
|
|
242
|
-
if (
|
|
243
|
-
deepAssign(instanceProp.value, newValue);
|
|
244
|
-
} else {
|
|
244
|
+
if (!deepEqual(instanceProp.value, newValue)) {
|
|
245
245
|
instanceProp.value = newValue;
|
|
246
246
|
}
|
|
247
247
|
|
|
@@ -365,7 +365,7 @@ class Sketch {
|
|
|
365
365
|
!isFunction(instanceProp.value) &&
|
|
366
366
|
!deepEqual(instanceProp.value, prop.__currentValue)
|
|
367
367
|
) {
|
|
368
|
-
this.updateProp(key,
|
|
368
|
+
this.updateProp(key, instanceProp.value);
|
|
369
369
|
}
|
|
370
370
|
|
|
371
371
|
// sync displayName
|
|
@@ -440,34 +440,11 @@ class Sketch {
|
|
|
440
440
|
for (const paramKey in instanceProp.params) {
|
|
441
441
|
const instanceParam = instanceProp.params[paramKey];
|
|
442
442
|
const param = prop.params[paramKey];
|
|
443
|
-
let needsUpdate =
|
|
444
|
-
|
|
445
|
-
if (isObject(instanceParam)) {
|
|
446
|
-
Object.keys(instanceParam).forEach((key) => {
|
|
447
|
-
if (isObject(instanceParam[key])) {
|
|
448
|
-
Object.keys(instanceParam[key]).forEach(
|
|
449
|
-
(k) => {
|
|
450
|
-
if (
|
|
451
|
-
instanceParam[key][k] !==
|
|
452
|
-
param[key][k]
|
|
453
|
-
) {
|
|
454
|
-
needsUpdate = true;
|
|
455
|
-
}
|
|
456
|
-
},
|
|
457
|
-
);
|
|
458
|
-
} else if (instanceParam[key] !== param[key]) {
|
|
459
|
-
needsUpdate = true;
|
|
460
|
-
}
|
|
461
|
-
});
|
|
462
|
-
} else if (instanceParam !== param) {
|
|
463
|
-
needsUpdate = true;
|
|
464
|
-
}
|
|
443
|
+
let needsUpdate = !deepEqual(instanceParam, param);
|
|
465
444
|
|
|
466
445
|
if (needsUpdate) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
structuredClone(instanceParam);
|
|
470
|
-
}
|
|
446
|
+
prop.params[paramKey] =
|
|
447
|
+
structuredClone(instanceParam);
|
|
471
448
|
}
|
|
472
449
|
}
|
|
473
450
|
}
|
|
@@ -6,17 +6,22 @@ class Layout {
|
|
|
6
6
|
components = $state([]);
|
|
7
7
|
editing = $state(false);
|
|
8
8
|
previewing = $state(false);
|
|
9
|
+
persistent = $state(true);
|
|
9
10
|
|
|
10
11
|
getID() {
|
|
11
12
|
return COMPONENT_ID++;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
constructor() {
|
|
15
|
-
this.key =
|
|
16
|
+
this.key = __BUILD__ ? `layout${__START_TIME__}` : `layout`;
|
|
16
17
|
|
|
17
18
|
$effect.root(() => {
|
|
18
19
|
$effect(() => {
|
|
19
|
-
|
|
20
|
+
const isPersistent = __BUILD__
|
|
21
|
+
? this.persistent
|
|
22
|
+
: !this.previewing;
|
|
23
|
+
|
|
24
|
+
if (isPersistent) {
|
|
20
25
|
this.persist($state.snapshot(this.components));
|
|
21
26
|
}
|
|
22
27
|
});
|
|
@@ -156,6 +161,7 @@ class Layout {
|
|
|
156
161
|
type: source.type,
|
|
157
162
|
name: source.name,
|
|
158
163
|
minimized: source.minimized,
|
|
164
|
+
headless: source.headless,
|
|
159
165
|
params: source.params,
|
|
160
166
|
children: [...source.children],
|
|
161
167
|
}));
|
|
@@ -361,7 +361,7 @@ export class Render {
|
|
|
361
361
|
|
|
362
362
|
if (needsUpdate) {
|
|
363
363
|
console.warn(
|
|
364
|
-
`Canvas ${attributeName} was changed from sketch
|
|
364
|
+
`Canvas ${attributeName} was changed from sketch from ${rendering[attributeName]}px to ${dimension}px.`,
|
|
365
365
|
);
|
|
366
366
|
rendering[attributeName] = dimension;
|
|
367
367
|
|
|
@@ -460,6 +460,8 @@ export class Render {
|
|
|
460
460
|
};
|
|
461
461
|
|
|
462
462
|
this.init = async () => {
|
|
463
|
+
if (this.errored) return;
|
|
464
|
+
|
|
463
465
|
clearError(this.sketch.key);
|
|
464
466
|
this.mountParams = this.renderer?.onMountPreview?.(this.params);
|
|
465
467
|
if (this.mountParams && this.mountParams.canvas !== this.canvas) {
|
|
@@ -599,7 +601,8 @@ export class Render {
|
|
|
599
601
|
sketch.beforeRecord.forEach((fn) => fn(params));
|
|
600
602
|
},
|
|
601
603
|
onTick: ({ time, deltaTime }) => {
|
|
602
|
-
this.
|
|
604
|
+
this.time += deltaTime;
|
|
605
|
+
this.loop(this.time);
|
|
603
606
|
},
|
|
604
607
|
onComplete: (params) => {
|
|
605
608
|
sketch.afterRecord.forEach((fn) => fn(params));
|
|
@@ -50,11 +50,22 @@ export function deepAssign(target, source) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export function deepEqual(target, source) {
|
|
53
|
-
if (isObject(target) && isObject(
|
|
53
|
+
if (isObject(target) && isObject(source)) {
|
|
54
54
|
let isEqual = true;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
|
|
56
|
+
if (
|
|
57
|
+
Array.isArray(target) &&
|
|
58
|
+
Array.isArray(source) &&
|
|
59
|
+
target.length !== source.length
|
|
60
|
+
) {
|
|
61
|
+
isEqual = false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (isEqual) {
|
|
65
|
+
for (const key in source) {
|
|
66
|
+
if (isEqual) {
|
|
67
|
+
isEqual = deepEqual(target[key], source[key]);
|
|
68
|
+
}
|
|
58
69
|
}
|
|
59
70
|
}
|
|
60
71
|
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
[`${fieldTypes.COLOR}`]: ColorInput,
|
|
22
22
|
[`${fieldTypes.BUTTON}`]: ButtonInput,
|
|
23
23
|
[`${fieldTypes.DOWNLOAD}`]: ButtonInput,
|
|
24
|
+
[`${fieldTypes.IMPORT}`]: ImportInput,
|
|
24
25
|
[`${fieldTypes.IMAGE}`]: ImageInput,
|
|
25
26
|
[`${fieldTypes.INTERVAL}`]: IntervalInput,
|
|
26
27
|
};
|
|
@@ -35,10 +36,11 @@
|
|
|
35
36
|
import { inferFieldType } from '../utils/fields.utils.js';
|
|
36
37
|
import IconTriggers from '../components/IconTriggers.svelte';
|
|
37
38
|
import IconLocked from '../components/IconLocked.svelte';
|
|
39
|
+
import ImportInput from './fields/ImportInput.svelte';
|
|
38
40
|
|
|
39
41
|
let {
|
|
40
42
|
key,
|
|
41
|
-
value
|
|
43
|
+
value,
|
|
42
44
|
initialValue = value,
|
|
43
45
|
context = null,
|
|
44
46
|
params = $bindable({}),
|
|
@@ -79,7 +79,8 @@
|
|
|
79
79
|
transition: opacity 0.1s ease;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
.header__action:hover .header__icon
|
|
82
|
+
:global(body:not(.fragment-dragging)) .header__action:hover .header__icon,
|
|
83
|
+
.header__action:focus-visible .header__icon {
|
|
83
84
|
opacity: 1;
|
|
84
85
|
}
|
|
85
86
|
|
|
@@ -98,7 +99,9 @@
|
|
|
98
99
|
transition: opacity 0.1s ease;
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
:global(body:not(.fragment-dragging))
|
|
103
|
+
.header__action:hover
|
|
104
|
+
.field-group__name,
|
|
102
105
|
.header__action:focus-visible .field-group__name {
|
|
103
106
|
opacity: 1;
|
|
104
107
|
}
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
grid-template-columns: 1fr;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
.field__section:hover .field__label,
|
|
51
|
+
:global(body:not(.fragment-dragging)) .field__section:hover .field__label,
|
|
52
52
|
.field__section:focus-within .field__label {
|
|
53
53
|
opacity: 1;
|
|
54
54
|
}
|
|
@@ -102,9 +102,9 @@
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
.field__label:focus-visible {
|
|
105
|
-
outline:
|
|
106
|
-
|
|
107
|
-
border-radius:
|
|
105
|
+
outline: 2px var(--color-active) solid;
|
|
106
|
+
outline-offset: 2px;
|
|
107
|
+
border-radius: 1px;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
.field__section.secondary {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import Root from './LayoutRoot.svelte';
|
|
3
3
|
import Column from './LayoutColumn.svelte';
|
|
4
|
-
import Build from '
|
|
4
|
+
import Build from '../components/Build.svelte';
|
|
5
5
|
import Row from './LayoutRow.svelte';
|
|
6
6
|
import ModuleRenderer from './ModuleRenderer.svelte';
|
|
7
7
|
import { layout } from '../state/layout.svelte.js';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Monitor from '../modules/Monitor.svelte';
|
|
3
|
+
import Params from '../modules/Params.svelte';
|
|
4
|
+
import FloatingParams from './FloatingParams.svelte';
|
|
5
|
+
import Column from './LayoutColumn.svelte';
|
|
6
|
+
import Row from './LayoutRow.svelte';
|
|
7
|
+
|
|
8
|
+
let { buildConfig = {}, sketch, sketchKey } = $props();
|
|
9
|
+
|
|
10
|
+
let gui = $derived(buildConfig.gui ?? {});
|
|
11
|
+
let layout = $derived(buildConfig.layout ?? {});
|
|
12
|
+
let headless = $derived(layout.headless ?? false);
|
|
13
|
+
let resizable = $derived(layout.resizable ?? false);
|
|
14
|
+
|
|
15
|
+
let guiOutput = $derived(gui.output ?? true);
|
|
16
|
+
let guiAlign = $derived(gui.align ?? 'right');
|
|
17
|
+
let guiHidden = $derived(gui?.hidden);
|
|
18
|
+
let guiSize = $derived(gui?.size ?? 0.25);
|
|
19
|
+
let guiMinimize = $derived(gui?.minimize);
|
|
20
|
+
let guiPosition = $derived(gui?.position);
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
{#if guiPosition === 'fixed'}
|
|
24
|
+
<Row>
|
|
25
|
+
{#if guiAlign === 'left'}
|
|
26
|
+
<Column size={guiSize} {resizable}>
|
|
27
|
+
<Params {headless} />
|
|
28
|
+
</Column>
|
|
29
|
+
<Column size={1 - guiSize} {resizable}>
|
|
30
|
+
<Monitor {headless} params={{ selected: sketchKey }} />
|
|
31
|
+
</Column>
|
|
32
|
+
{:else}
|
|
33
|
+
<Column size={1 - guiSize} {resizable}>
|
|
34
|
+
<Monitor {headless} params={{ selected: sketchKey }} />
|
|
35
|
+
</Column>
|
|
36
|
+
<Column size={guiSize} {resizable}>
|
|
37
|
+
<Params {headless} />
|
|
38
|
+
</Column>
|
|
39
|
+
{/if}
|
|
40
|
+
</Row>
|
|
41
|
+
{:else}
|
|
42
|
+
<Row>
|
|
43
|
+
<Monitor {headless} {sketchKey} params={{ selected: sketchKey }} />
|
|
44
|
+
{#if gui}
|
|
45
|
+
<FloatingParams
|
|
46
|
+
output={guiOutput}
|
|
47
|
+
align={guiAlign}
|
|
48
|
+
size={guiSize}
|
|
49
|
+
hidden={guiHidden}
|
|
50
|
+
minimize={guiMinimize}
|
|
51
|
+
/>
|
|
52
|
+
{/if}
|
|
53
|
+
</Row>
|
|
54
|
+
{/if}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import LayoutComponent from './LayoutComponent.svelte';
|
|
3
3
|
|
|
4
|
-
let { size = 1, children } = $props();
|
|
4
|
+
let { size = 1, children, resizable = true } = $props();
|
|
5
5
|
</script>
|
|
6
6
|
|
|
7
|
-
<LayoutComponent
|
|
7
|
+
<LayoutComponent type="column" {size} {resizable}>
|
|
8
8
|
{@render children?.()}
|
|
9
9
|
</LayoutComponent>
|
|
@@ -4,10 +4,16 @@
|
|
|
4
4
|
import Toolbar from './LayoutToolbar.svelte';
|
|
5
5
|
import Resizer from './LayoutResizer.svelte';
|
|
6
6
|
import ModuleRenderer from './ModuleRenderer.svelte';
|
|
7
|
-
import Preview from '
|
|
7
|
+
import Preview from '../components/Preview.svelte';
|
|
8
8
|
import LayoutComponent from './LayoutComponent.svelte';
|
|
9
9
|
|
|
10
|
-
let {
|
|
10
|
+
let {
|
|
11
|
+
id = layout.getID(),
|
|
12
|
+
size = 1,
|
|
13
|
+
type = 'column',
|
|
14
|
+
children,
|
|
15
|
+
resizable = true,
|
|
16
|
+
} = $props();
|
|
11
17
|
|
|
12
18
|
let parent = getContext('parent');
|
|
13
19
|
let isColumn = $derived(type === 'column');
|
|
@@ -120,7 +126,7 @@
|
|
|
120
126
|
{/if}
|
|
121
127
|
{/each}
|
|
122
128
|
{:else}
|
|
123
|
-
{@render children()}
|
|
129
|
+
{@render children?.()}
|
|
124
130
|
{/if}
|
|
125
131
|
{#if layout.editing && (isRoot || (childComponents.length === 1 && childComponents[0].type === 'module') || childComponents.length === 0)}
|
|
126
132
|
<Toolbar
|
|
@@ -134,7 +140,11 @@
|
|
|
134
140
|
{/if}
|
|
135
141
|
</div>
|
|
136
142
|
{#if !isRoot}
|
|
137
|
-
<Resizer
|
|
143
|
+
<Resizer
|
|
144
|
+
direction={isColumn ? 'vertical' : 'horizontal'}
|
|
145
|
+
{current}
|
|
146
|
+
disabled={!resizable}
|
|
147
|
+
/>
|
|
138
148
|
{/if}
|
|
139
149
|
|
|
140
150
|
<style>
|
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
import { layout } from '../state/layout.svelte.js';
|
|
12
12
|
import { clamp, map } from '../utils/math.utils.js';
|
|
13
13
|
|
|
14
|
-
let {
|
|
14
|
+
let {
|
|
15
|
+
direction = DIRECTIONS.HORIZONTAL,
|
|
16
|
+
current,
|
|
17
|
+
disabled = false,
|
|
18
|
+
} = $props();
|
|
15
19
|
|
|
16
20
|
let visible = $state(false);
|
|
17
21
|
let isDragging = $state(false);
|
|
@@ -133,6 +137,7 @@
|
|
|
133
137
|
class="resizer resizer--{direction}"
|
|
134
138
|
class:dragging={isDragging}
|
|
135
139
|
class:editing={layout.editing}
|
|
140
|
+
class:disabled
|
|
136
141
|
>
|
|
137
142
|
<div
|
|
138
143
|
class="resizer-hover"
|
|
@@ -150,6 +155,10 @@
|
|
|
150
155
|
position: relative;
|
|
151
156
|
}
|
|
152
157
|
|
|
158
|
+
.resizer.disabled {
|
|
159
|
+
pointer-events: none;
|
|
160
|
+
}
|
|
161
|
+
|
|
153
162
|
[class~='resizer']:last-of-type {
|
|
154
163
|
display: none;
|
|
155
164
|
}
|
|
@@ -177,7 +186,7 @@
|
|
|
177
186
|
opacity: 0.1;
|
|
178
187
|
}
|
|
179
188
|
|
|
180
|
-
.resizer .resizer-hover:hover:before {
|
|
189
|
+
:global(body:not(.fragment-dragging)) .resizer .resizer-hover:hover:before {
|
|
181
190
|
opacity: 0.25;
|
|
182
191
|
}
|
|
183
192
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import LayoutComponent from './LayoutComponent.svelte';
|
|
3
3
|
|
|
4
|
-
let { size = 1, children } = $props();
|
|
4
|
+
let { size = 1, children, resizable = true } = $props();
|
|
5
5
|
</script>
|
|
6
6
|
|
|
7
|
-
<LayoutComponent
|
|
7
|
+
<LayoutComponent type="row" {size} {resizable}>
|
|
8
8
|
{@render children?.()}
|
|
9
9
|
</LayoutComponent>
|
|
@@ -57,15 +57,15 @@
|
|
|
57
57
|
color: var(--color-text-input-disabled);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
.button:hover {
|
|
60
|
+
:global(body:not(.fragment-dragging)) .button:hover {
|
|
61
61
|
color: var(--color-text);
|
|
62
62
|
|
|
63
63
|
box-shadow: inset 0 0 0 1px
|
|
64
64
|
var(--box-shadow-color-active, var(--color-active));
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
.button:active,
|
|
68
|
-
.button:focus-visible {
|
|
67
|
+
:global(body:not(.fragment-dragging)) .button:active,
|
|
68
|
+
:global(body:not(.fragment-dragging)) .button:focus-visible {
|
|
69
69
|
box-shadow: 0 0 0 2px
|
|
70
70
|
var(--box-shadow-color-active, var(--color-active));
|
|
71
71
|
}
|
|
@@ -3,19 +3,27 @@
|
|
|
3
3
|
import TextInput from './TextInput.svelte';
|
|
4
4
|
import Field from '../Field.svelte';
|
|
5
5
|
|
|
6
|
-
let {
|
|
6
|
+
let {
|
|
7
|
+
value,
|
|
8
|
+
context = null,
|
|
9
|
+
key = '',
|
|
10
|
+
disabled = false,
|
|
11
|
+
onchange,
|
|
12
|
+
} = $props();
|
|
7
13
|
|
|
8
14
|
let format = $derived(color.getColorFormat(value));
|
|
9
15
|
let hexValue = $derived(color.toHex(value, format));
|
|
10
16
|
let textValue = $state();
|
|
11
17
|
let alpha = $state(1);
|
|
12
|
-
let hasAlpha = $derived(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
let hasAlpha = $derived(
|
|
19
|
+
[
|
|
20
|
+
color.FORMATS.RGBA_STRING,
|
|
21
|
+
color.FORMATS.VEC4_STRING,
|
|
22
|
+
color.FORMATS.VEC4_ARRAY,
|
|
23
|
+
color.FORMATS.RGBA_OBJECT,
|
|
24
|
+
color.FORMATS.HSLA_STRING,
|
|
25
|
+
].includes(format),
|
|
26
|
+
);
|
|
19
27
|
|
|
20
28
|
$effect(() => {
|
|
21
29
|
if (hasAlpha) {
|
|
@@ -24,7 +32,7 @@
|
|
|
24
32
|
} else {
|
|
25
33
|
alpha = 1;
|
|
26
34
|
}
|
|
27
|
-
})
|
|
35
|
+
});
|
|
28
36
|
|
|
29
37
|
$effect(() => {
|
|
30
38
|
textValue = color.toString(value, format)?.toLowerCase();
|
|
@@ -36,10 +44,10 @@
|
|
|
36
44
|
if (format === newFormat) {
|
|
37
45
|
onchange(newColor);
|
|
38
46
|
} else {
|
|
39
|
-
const components = color.toComponents(newColor);
|
|
47
|
+
const components = color.toComponents(newColor);
|
|
40
48
|
const [r, g, b] = components;
|
|
41
49
|
|
|
42
|
-
switch(format) {
|
|
50
|
+
switch (format) {
|
|
43
51
|
case color.FORMATS.RGB_OBJECT:
|
|
44
52
|
onchange({ r, g, b });
|
|
45
53
|
break;
|
|
@@ -47,7 +55,9 @@
|
|
|
47
55
|
onchange({ r, g, b, a: alpha });
|
|
48
56
|
break;
|
|
49
57
|
default:
|
|
50
|
-
onchange(
|
|
58
|
+
onchange(
|
|
59
|
+
color.componentsToFormat([r, g, b, alpha], format),
|
|
60
|
+
);
|
|
51
61
|
}
|
|
52
62
|
}
|
|
53
63
|
}
|
|
@@ -207,7 +217,7 @@
|
|
|
207
217
|
pointer-events: none;
|
|
208
218
|
}
|
|
209
219
|
|
|
210
|
-
.mirror:hover {
|
|
220
|
+
:global(body:not(.fragment-dragging)) .mirror:hover {
|
|
211
221
|
box-shadow: inset 0 0 0 1px var(--box-shadow-color, var(--color-active));
|
|
212
222
|
}
|
|
213
223
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import ButtonInput from './ButtonInput.svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
value,
|
|
6
|
+
label = 'import',
|
|
7
|
+
disabled = false,
|
|
8
|
+
title = '',
|
|
9
|
+
accept,
|
|
10
|
+
readAs = 'readAsText',
|
|
11
|
+
children,
|
|
12
|
+
} = $props();
|
|
13
|
+
|
|
14
|
+
/** @type {HTMLInputElement}*/
|
|
15
|
+
let input;
|
|
16
|
+
|
|
17
|
+
let fileReader = new FileReader();
|
|
18
|
+
fileReader.onload = (event) => {
|
|
19
|
+
value(event);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function handleClick(event) {
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
input.click();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function handleChange(event) {
|
|
28
|
+
if (event.target.files.length > 0) {
|
|
29
|
+
const readAsFn = fileReader[readAs];
|
|
30
|
+
|
|
31
|
+
if (!readAsFn) {
|
|
32
|
+
console.error(
|
|
33
|
+
`readAs: '${readAs}' is not a function of FileReader.`,
|
|
34
|
+
);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
readAsFn.call(fileReader, event.target.files[0]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<ButtonInput onclick={handleClick} {disabled} {label} {title}>
|
|
44
|
+
{@render children?.()}
|
|
45
|
+
</ButtonInput>
|
|
46
|
+
<input
|
|
47
|
+
class="visually-hidden"
|
|
48
|
+
onchange={handleChange}
|
|
49
|
+
type="file"
|
|
50
|
+
bind:this={input}
|
|
51
|
+
{accept}
|
|
52
|
+
/>
|
|
@@ -53,11 +53,13 @@
|
|
|
53
53
|
box-shadow: inset 0 0 0 1px var(--color-border-input);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
:global(body:not(.fragment-dragging))
|
|
57
|
+
.input-container:not(.disabled):hover {
|
|
57
58
|
box-shadow: inset 0 0 0 1px var(--color-active);
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
:global(body:not(.fragment-dragging))
|
|
62
|
+
.input-container:not(.disabled):focus-within {
|
|
61
63
|
box-shadow: 0 0 0 2px var(--color-active);
|
|
62
64
|
}
|
|
63
65
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
/** @type {DOMRect}*/
|
|
22
22
|
let rect;
|
|
23
23
|
/** @type {boolean}*/
|
|
24
|
-
let isDragging = false;
|
|
24
|
+
let isDragging = $state(false);
|
|
25
25
|
|
|
26
26
|
let proximityIndex = -1;
|
|
27
27
|
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
* @param {MouseEvent} event
|
|
31
31
|
*/
|
|
32
32
|
function handleMouseDown(event) {
|
|
33
|
-
document.body.
|
|
33
|
+
document.body.classList.add('fragment-dragging');
|
|
34
|
+
|
|
34
35
|
document.addEventListener('mousemove', handleMouseMove);
|
|
35
36
|
document.addEventListener('mouseup', handleMouseUp);
|
|
36
37
|
|
|
@@ -85,7 +86,7 @@
|
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
function handleMouseUp() {
|
|
88
|
-
document.body.
|
|
89
|
+
document.body.classList.remove('fragment-dragging');
|
|
89
90
|
document.removeEventListener('mousemove', handleMouseMove);
|
|
90
91
|
document.removeEventListener('mouseup', handleMouseUp);
|
|
91
92
|
|
|
@@ -119,9 +120,10 @@
|
|
|
119
120
|
<div class="interval-input" class:disabled>
|
|
120
121
|
<FieldInputRow --grid-template-columns="1fr 0.5fr">
|
|
121
122
|
<div
|
|
122
|
-
class="range
|
|
123
|
+
class="range"
|
|
124
|
+
class:dragging={isDragging}
|
|
123
125
|
bind:this={node}
|
|
124
|
-
|
|
126
|
+
onmousedown={handleMouseDown}
|
|
125
127
|
>
|
|
126
128
|
<div class="handler" style="--position: {p1};" />
|
|
127
129
|
<div class="filler" style="--p1: {p1}; --p2: {p2};"></div>
|
|
@@ -189,7 +191,7 @@
|
|
|
189
191
|
container-type: size;
|
|
190
192
|
}
|
|
191
193
|
|
|
192
|
-
.range:hover {
|
|
194
|
+
:global(body:not(.fragment-dragging)) .range:hover {
|
|
193
195
|
box-shadow: inset 0 0 0 1px var(--color-active);
|
|
194
196
|
}
|
|
195
197
|
|
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
let node;
|
|
17
17
|
let rect;
|
|
18
18
|
|
|
19
|
-
let isDragging = false;
|
|
19
|
+
let isDragging = $state(false);
|
|
20
20
|
|
|
21
21
|
// handlers
|
|
22
22
|
function handleMouseDown(event) {
|
|
23
|
+
document.body.classList.add('fragment-dragging');
|
|
23
24
|
document.addEventListener('mousemove', handleMouseMove);
|
|
24
25
|
document.addEventListener('mouseup', handleMouseUp);
|
|
25
26
|
|
|
@@ -48,6 +49,7 @@
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
function handleMouseUp() {
|
|
52
|
+
document.body.classList.remove('fragment-dragging');
|
|
51
53
|
document.removeEventListener('mousemove', handleMouseMove);
|
|
52
54
|
document.removeEventListener('mouseup', handleMouseUp);
|
|
53
55
|
|
|
@@ -59,10 +61,11 @@
|
|
|
59
61
|
</script>
|
|
60
62
|
|
|
61
63
|
<div
|
|
62
|
-
class="progress
|
|
64
|
+
class="progress"
|
|
63
65
|
bind:this={node}
|
|
64
66
|
onmousedown={handleMouseDown}
|
|
65
67
|
class:disabled
|
|
68
|
+
class:dragging={isDragging}
|
|
66
69
|
>
|
|
67
70
|
<div class="fill" style="--progress: {progress}; --opacity: {opacity};" />
|
|
68
71
|
</div>
|
|
@@ -80,7 +83,7 @@
|
|
|
80
83
|
container-type: size;
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
.progress:hover {
|
|
86
|
+
:global(body:not(.fragment-dragging)) .progress:hover {
|
|
84
87
|
box-shadow: inset 0 0 0 1px var(--color-active);
|
|
85
88
|
}
|
|
86
89
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
import { isObject } from '../../state/utils.svelte';
|
|
2
3
|
import SelectChevrons from '../SelectChevrons.svelte';
|
|
3
4
|
|
|
4
5
|
let {
|
|
@@ -10,55 +11,48 @@
|
|
|
10
11
|
onchange = () => {},
|
|
11
12
|
} = $props();
|
|
12
13
|
|
|
13
|
-
function toStringifiedValue(
|
|
14
|
-
if (
|
|
15
|
-
return
|
|
16
|
-
} else if (option === undefined) {
|
|
17
|
-
return undefined;
|
|
18
|
-
} else if (optionType === 'object') {
|
|
19
|
-
return toStringifiedValue(option.value);
|
|
20
|
-
} else if (optionType === 'function') {
|
|
21
|
-
return option.name;
|
|
14
|
+
function toStringifiedValue(value) {
|
|
15
|
+
if (typeof value === 'function') {
|
|
16
|
+
return `${value.name}()`;
|
|
22
17
|
}
|
|
23
18
|
|
|
24
|
-
return
|
|
19
|
+
return String(value);
|
|
25
20
|
}
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
22
|
+
function createOption(option) {
|
|
23
|
+
let value =
|
|
24
|
+
isObject(option) && 'value' in option ? option.value : option;
|
|
25
|
+
let label = option?.label ?? toStringifiedValue(value);
|
|
26
|
+
let disabled = option?.disabled ?? false;
|
|
27
|
+
let stringValue = toStringifiedValue(value);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
label,
|
|
31
|
+
value,
|
|
32
|
+
stringValue,
|
|
33
|
+
disabled,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let sanitizedOptions = $derived.by(() => {
|
|
38
|
+
let opts = options.map((option, optionIndex) => {
|
|
39
|
+
return createOption(option, optionIndex);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return opts;
|
|
43
|
+
});
|
|
48
44
|
|
|
49
45
|
let sanitizedValue = $derived(
|
|
50
|
-
sanitizedOptions.find((opt) => opt.value === value)
|
|
46
|
+
sanitizedOptions.find((opt) => opt.value === value) ??
|
|
47
|
+
sanitizedOptions[0],
|
|
51
48
|
);
|
|
52
49
|
|
|
53
50
|
function handleChange(event) {
|
|
54
|
-
const
|
|
51
|
+
const sanitizedOption = sanitizedOptions.find(
|
|
55
52
|
(opt) => opt.stringValue === event.currentTarget.value,
|
|
56
53
|
);
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
const newValue = typeof option === 'object' ? option.value : option;
|
|
60
|
-
|
|
61
|
-
onchange(newValue);
|
|
55
|
+
onchange(sanitizedOption.value);
|
|
62
56
|
}
|
|
63
57
|
</script>
|
|
64
58
|
|
|
@@ -84,9 +78,7 @@
|
|
|
84
78
|
>
|
|
85
79
|
{/each}
|
|
86
80
|
</select>
|
|
87
|
-
|
|
88
|
-
<SelectChevrons />
|
|
89
|
-
{/if}
|
|
81
|
+
<SelectChevrons />
|
|
90
82
|
</div>
|
|
91
83
|
</div>
|
|
92
84
|
|
|
@@ -113,7 +105,9 @@
|
|
|
113
105
|
background-color: var(--color-background-input);
|
|
114
106
|
}
|
|
115
107
|
|
|
116
|
-
|
|
108
|
+
:global(body:not(.fragment-dragging))
|
|
109
|
+
.select-input:not(.disabled)
|
|
110
|
+
.container:hover {
|
|
117
111
|
box-shadow: inset 0 0 0 1px var(--color-active);
|
|
118
112
|
}
|
|
119
113
|
|
|
@@ -54,6 +54,20 @@ body {
|
|
|
54
54
|
overscroll-behavior: none;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
body.fragment-dragging {
|
|
58
|
+
-webkit-user-select: none;
|
|
59
|
+
user-select: none;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
body.fragment-dragging input {
|
|
63
|
+
-webkit-user-select: none;
|
|
64
|
+
user-select: none;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
body.fragment-dragging * {
|
|
68
|
+
cursor: ew-resize !important;
|
|
69
|
+
}
|
|
70
|
+
|
|
57
71
|
#app {
|
|
58
72
|
position: fixed;
|
|
59
73
|
top: 0;
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { onDestroy } from 'svelte';
|
|
3
|
-
|
|
4
|
-
import Monitor from '../modules/Monitor.svelte';
|
|
5
|
-
import Params from '../modules/Params.svelte';
|
|
6
|
-
import FloatingParams from './FloatingParams.svelte';
|
|
7
|
-
import Column from './LayoutColumn.svelte';
|
|
8
|
-
import Row from './LayoutRow.svelte';
|
|
9
|
-
import { sketchesManager } from '../state/sketches.svelte';
|
|
10
|
-
import { rendering } from '../state/rendering.svelte';
|
|
11
|
-
|
|
12
|
-
console.log(`Made with Fragment. https://fragment.tools`);
|
|
13
|
-
|
|
14
|
-
let sketchKey = $derived(sketchesManager.keys[0]);
|
|
15
|
-
|
|
16
|
-
let sketch = $derived(sketchesManager.sketches[sketchKey]);
|
|
17
|
-
|
|
18
|
-
let gui = $derived(sketch?.buildConfig?.gui);
|
|
19
|
-
let guiOutput = $derived(gui?.output);
|
|
20
|
-
let guiAlign = $derived(gui?.align ?? 'right');
|
|
21
|
-
let guiHidden = $derived(gui?.hidden);
|
|
22
|
-
let guiSize = $derived(gui?.size ?? 0.25);
|
|
23
|
-
let guiMinimize = $derived(gui?.minimize);
|
|
24
|
-
let guiPosition = $derived(gui?.position);
|
|
25
|
-
let styles = $derived(sketch?.buildConfig?.styles ?? '');
|
|
26
|
-
|
|
27
|
-
/** @type {HTMLHeadElement} */
|
|
28
|
-
let head;
|
|
29
|
-
/** @type {HTMLStyleElement} */
|
|
30
|
-
let style;
|
|
31
|
-
|
|
32
|
-
$effect(() => {
|
|
33
|
-
rendering.override(sketch?.buildConfig);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
$effect(() => {
|
|
37
|
-
if (styles !== '') {
|
|
38
|
-
head = document.getElementsByTagName('head')[0];
|
|
39
|
-
|
|
40
|
-
if (style) {
|
|
41
|
-
head.removeChild(style);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
style = document.createElement('style');
|
|
45
|
-
style.setAttribute('type', 'text/css');
|
|
46
|
-
style.appendChild(document.createTextNode(styles));
|
|
47
|
-
head.appendChild(style);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
onDestroy(() => {
|
|
52
|
-
if (style && head) {
|
|
53
|
-
head.removeChild(style);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
</script>
|
|
57
|
-
|
|
58
|
-
{#if sketch}
|
|
59
|
-
{#if guiPosition === 'fixed'}
|
|
60
|
-
<Row>
|
|
61
|
-
{#if guiAlign === 'left'}
|
|
62
|
-
<Column size={guiSize}>
|
|
63
|
-
<Params />
|
|
64
|
-
</Column>
|
|
65
|
-
<Column size={1 - guiSize}>
|
|
66
|
-
<Monitor params={{ selected: sketchKey }} />
|
|
67
|
-
</Column>
|
|
68
|
-
{:else}
|
|
69
|
-
<Column size={1 - guiSize}>
|
|
70
|
-
<Monitor params={{ selected: sketchKey }} />
|
|
71
|
-
</Column>
|
|
72
|
-
<Column size={guiSize}>
|
|
73
|
-
<Params />
|
|
74
|
-
</Column>
|
|
75
|
-
{/if}
|
|
76
|
-
</Row>
|
|
77
|
-
{:else}
|
|
78
|
-
<Row>
|
|
79
|
-
<Monitor headless {sketchKey} params={{ selected: sketchKey }} />
|
|
80
|
-
{#if gui}
|
|
81
|
-
<FloatingParams
|
|
82
|
-
output={guiOutput}
|
|
83
|
-
align={guiAlign}
|
|
84
|
-
size={guiSize}
|
|
85
|
-
hidden={guiHidden}
|
|
86
|
-
minimize={guiMinimize}
|
|
87
|
-
/>
|
|
88
|
-
{/if}
|
|
89
|
-
</Row>
|
|
90
|
-
{/if}
|
|
91
|
-
{/if}
|