fragment-tools 0.2.2 → 0.2.4
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/inputs/MIDI.js +0 -1
- package/src/client/app/modules/MidiPanel.svelte +23 -13
- package/src/client/app/modules/Params.svelte +31 -16
- package/src/client/app/state/Sketch.svelte.js +77 -73
- package/src/client/app/state/layout.svelte.js +8 -2
- package/src/client/app/state/rendering.svelte.js +23 -5
- package/src/client/app/state/utils.svelte.js +28 -4
- package/src/client/app/ui/Field.svelte +13 -11
- 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 +47 -17
- package/src/client/app/ui/fields/Select.svelte +35 -41
- package/src/client/app/ui/fields/VectorInput.svelte +63 -18
- package/src/client/app/utils/fields.utils.js +70 -48
- package/src/client/app/utils/math.utils.js +6 -0
- package/src/client/public/css/global.css +14 -0
- package/src/client/app/ui/Build.svelte +0 -91
|
@@ -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
|
|
|
@@ -8,27 +8,71 @@
|
|
|
8
8
|
min = -Infinity,
|
|
9
9
|
max = Infinity,
|
|
10
10
|
step = 0.1,
|
|
11
|
+
key,
|
|
11
12
|
locked = false,
|
|
12
13
|
disabled = false,
|
|
13
14
|
context = null,
|
|
14
|
-
key = '',
|
|
15
15
|
onchange,
|
|
16
16
|
} = $props();
|
|
17
17
|
|
|
18
|
+
const keysChecks = ['x', 'y', 'z', 'w'];
|
|
18
19
|
|
|
19
20
|
let isArray = $derived(Array.isArray(value));
|
|
20
21
|
let isObject = $derived(!isArray && typeof value === 'object');
|
|
21
|
-
let
|
|
22
|
-
|
|
22
|
+
let keys = $derived.by(() => {
|
|
23
|
+
let keys = [];
|
|
24
|
+
|
|
25
|
+
if (isArray) {
|
|
26
|
+
return value.map((_, index) => index);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!isArray && !isObject) return [0];
|
|
30
|
+
|
|
31
|
+
for (let i = 0; i < keysChecks; i++) {
|
|
32
|
+
let keyCheck = keysChecks[i];
|
|
33
|
+
|
|
34
|
+
if (keyCheck in value) {
|
|
35
|
+
keys.push(keyCheck);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (value.isVector2) {
|
|
40
|
+
return ['x', 'y'];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (value.isVector3) {
|
|
44
|
+
return ['x', 'y', 'z'];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (value.isVector4 || value.isQuaternion) {
|
|
48
|
+
return ['x', 'y', 'z', 'w'];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (isObject) {
|
|
52
|
+
return Object.keys(value);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return keys;
|
|
56
|
+
});
|
|
57
|
+
let components = $derived.by(() => {
|
|
58
|
+
if (!isObject && !isArray) {
|
|
59
|
+
return [value];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return keys.map((key) => value[key]);
|
|
63
|
+
});
|
|
64
|
+
let mins = $derived(keys.map((key) => min[key]));
|
|
65
|
+
let maxs = $derived(keys.map((key) => max[key]));
|
|
66
|
+
let steps = $derived(keys.map((key) => step[key]));
|
|
23
67
|
|
|
24
68
|
function dispatchChange() {
|
|
25
|
-
let
|
|
26
|
-
all[key] = components[index];
|
|
69
|
+
let clone = isArray ? [] : {};
|
|
27
70
|
|
|
28
|
-
|
|
29
|
-
|
|
71
|
+
keys.forEach((key, index) => {
|
|
72
|
+
clone[key] = components[index];
|
|
73
|
+
});
|
|
30
74
|
|
|
31
|
-
onchange(
|
|
75
|
+
onchange(clone);
|
|
32
76
|
}
|
|
33
77
|
|
|
34
78
|
function handleComponentChange(newValue, componentIndex) {
|
|
@@ -39,11 +83,13 @@
|
|
|
39
83
|
}
|
|
40
84
|
|
|
41
85
|
components.forEach((component, index) => {
|
|
42
|
-
components[index] =
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
86
|
+
components[index] =
|
|
87
|
+
index === componentIndex
|
|
88
|
+
? newValue
|
|
89
|
+
: locked
|
|
90
|
+
? Math.round(component * ratio * (1 / step)) /
|
|
91
|
+
(1 / step)
|
|
92
|
+
: component;
|
|
47
93
|
});
|
|
48
94
|
|
|
49
95
|
dispatchChange();
|
|
@@ -56,17 +102,16 @@
|
|
|
56
102
|
>
|
|
57
103
|
{#each components as component, index}
|
|
58
104
|
<NumberInput
|
|
59
|
-
{
|
|
60
|
-
{
|
|
61
|
-
{
|
|
105
|
+
min={mins[index]}
|
|
106
|
+
max={maxs[index]}
|
|
107
|
+
step={steps[index]}
|
|
62
108
|
{suffix}
|
|
63
109
|
{disabled}
|
|
64
110
|
{context}
|
|
65
111
|
{key}
|
|
66
112
|
label={keys[index]}
|
|
67
113
|
value={component}
|
|
68
|
-
onchange={(value) =>
|
|
69
|
-
handleComponentChange(value, index)}
|
|
114
|
+
onchange={(value) => handleComponentChange(value, index)}
|
|
70
115
|
/>
|
|
71
116
|
{/each}
|
|
72
117
|
</FieldInputRow>
|
|
@@ -10,6 +10,7 @@ export const fieldTypes = {
|
|
|
10
10
|
COLOR: 'color',
|
|
11
11
|
BUTTON: 'button',
|
|
12
12
|
DOWNLOAD: 'download',
|
|
13
|
+
IMPORT: 'import',
|
|
13
14
|
IMAGE: 'image',
|
|
14
15
|
INTERVAL: 'interval',
|
|
15
16
|
};
|
|
@@ -42,11 +43,40 @@ export function inferFieldType({ type, value, params, key }) {
|
|
|
42
43
|
|
|
43
44
|
const isArray = Array.isArray(value);
|
|
44
45
|
const isObject = !isArray && typeof value === 'object';
|
|
45
|
-
const
|
|
46
|
+
const getKeys = (value) => {
|
|
47
|
+
if (isArray) {
|
|
48
|
+
return value.map((_, index) => index);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!isArray && !isObject) return [0];
|
|
52
|
+
|
|
53
|
+
if (value.isVector3) {
|
|
54
|
+
return ['x', 'y', 'z'];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (value.isVector4 || value.isQuaternion) {
|
|
58
|
+
return ['x', 'y', 'z', 'w'];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (isObject) {
|
|
62
|
+
return Object.keys(value);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const getValues = (value, keys) => {
|
|
67
|
+
if (!isObject && !isArray) {
|
|
68
|
+
value = [value];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return keys.map((key) => value[key]);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const keys = getKeys(value);
|
|
75
|
+
const values = getValues(value, keys);
|
|
46
76
|
|
|
47
77
|
if (
|
|
48
78
|
isArray &&
|
|
49
|
-
|
|
79
|
+
values.length === 2 &&
|
|
50
80
|
typeof params.min === 'number' &&
|
|
51
81
|
typeof params.max === 'number'
|
|
52
82
|
) {
|
|
@@ -79,53 +109,45 @@ export function inferFieldType({ type, value, params, key }) {
|
|
|
79
109
|
console.warn(`Field: cannot find field type for ${key}`);
|
|
80
110
|
}
|
|
81
111
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
112
|
+
/**
|
|
113
|
+
*
|
|
114
|
+
* @param {string} folder
|
|
115
|
+
*/
|
|
116
|
+
export function parseFolder(folder) {
|
|
117
|
+
const regex = /(?<name>\w+)(?:\[(?<attributes>[^\]]+)\])?/g;
|
|
118
|
+
const matches = [...folder.matchAll(regex)];
|
|
119
|
+
|
|
120
|
+
const results = matches.map((match) => {
|
|
121
|
+
return {
|
|
122
|
+
name: match.groups.name,
|
|
123
|
+
attributes: match.groups.attributes
|
|
124
|
+
? Object.fromEntries(
|
|
125
|
+
match.groups.attributes
|
|
126
|
+
.split(', ')
|
|
127
|
+
.map((attr) => attr.split('=')),
|
|
128
|
+
)
|
|
129
|
+
: {},
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
let names = results.map((match) => match.name);
|
|
134
|
+
|
|
135
|
+
let rootId;
|
|
136
|
+
|
|
137
|
+
results.forEach((match, index) => {
|
|
138
|
+
let id = [...names].slice(0, index + 1).join('.');
|
|
139
|
+
let parentId = [...names].slice(0, index).join('.');
|
|
140
|
+
|
|
141
|
+
if (index === 0) {
|
|
142
|
+
rootId = id;
|
|
91
143
|
}
|
|
92
144
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (initialType === 'object') {
|
|
102
|
-
const keys1 = Object.keys(initialValue);
|
|
103
|
-
const keys2 = Object.keys(currentValue);
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
keys1.length !== keys2.length ||
|
|
107
|
-
!keys1.every((key) => keys2.includes(key))
|
|
108
|
-
) {
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
for (const key of keys1) {
|
|
113
|
-
const value1 = initialValue[key];
|
|
114
|
-
const value2 = currentValue[key];
|
|
115
|
-
|
|
116
|
-
if (typeof value1 === 'object' && typeof value2 === 'object') {
|
|
117
|
-
// If both values are objects, recursively compare them
|
|
118
|
-
if (hasChanged(value1, value2)) {
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
} else if (value1 !== value2) {
|
|
122
|
-
// If values are not objects, directly compare them
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
145
|
+
match.id = id;
|
|
146
|
+
match.parentId = parentId;
|
|
147
|
+
match.depth = index;
|
|
148
|
+
match.isCurrent = index === results.length - 1;
|
|
149
|
+
match.rootId = rootId;
|
|
150
|
+
});
|
|
129
151
|
|
|
130
|
-
return
|
|
152
|
+
return results;
|
|
131
153
|
}
|
|
@@ -22,6 +22,12 @@ export function clamp(value, min, max) {
|
|
|
22
22
|
return Math.max(min, Math.min(value, max));
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
*
|
|
27
|
+
* @param {number} value
|
|
28
|
+
* @param {number} step
|
|
29
|
+
* @returns {number}
|
|
30
|
+
*/
|
|
25
31
|
export function roundToStep(value, step) {
|
|
26
32
|
return Math.round(value * (1 / step)) / (1 / step);
|
|
27
33
|
}
|
|
@@ -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}
|