fragment-tools 0.1.19 → 0.2.0

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 (102) hide show
  1. package/bin/index.js +1 -0
  2. package/package.json +5 -6
  3. package/src/cli/templates/three-fragment/index.js +6 -6
  4. package/src/cli/templates/three-orthographic/index.js +3 -3
  5. package/src/cli/templates/three-perspective/index.js +3 -3
  6. package/src/client/app/actions/resize.js +14 -0
  7. package/src/client/app/components/HintLoading.svelte +94 -0
  8. package/src/client/app/components/HintPaused.svelte +88 -0
  9. package/src/client/app/components/HintRecord.svelte +62 -0
  10. package/src/client/app/components/IconLocked.svelte +51 -0
  11. package/src/client/app/components/IconTriggers.svelte +48 -0
  12. package/src/client/app/components/Init.svelte +14 -27
  13. package/src/client/app/components/KeyBinding.svelte +3 -6
  14. package/src/client/app/helpers.js +4 -40
  15. package/src/client/app/hooks.js +41 -17
  16. package/src/client/app/inputs/MIDI.js +2 -1
  17. package/src/client/app/lib/canvas-recorder/CanvasRecorder.js +6 -1
  18. package/src/client/app/lib/gl/Renderer.js +1 -0
  19. package/src/client/app/lib/svelte-json-tree/ErrorNode.svelte +28 -0
  20. package/src/client/app/lib/svelte-json-tree/ErrorStack.svelte +31 -0
  21. package/src/client/app/lib/svelte-json-tree/Expandable.svelte +25 -0
  22. package/src/client/app/lib/svelte-json-tree/JSONArrayNode.svelte +38 -0
  23. package/src/client/app/lib/svelte-json-tree/JSONArrow.svelte +47 -0
  24. package/src/client/app/lib/svelte-json-tree/JSONFunctionNode.svelte +114 -0
  25. package/src/client/app/lib/svelte-json-tree/JSONIterableArrayNode.svelte +60 -0
  26. package/src/client/app/lib/svelte-json-tree/JSONIterableMapNode.svelte +87 -0
  27. package/src/client/app/lib/svelte-json-tree/JSONNested.svelte +94 -0
  28. package/src/client/app/lib/svelte-json-tree/JSONNode.svelte +91 -0
  29. package/src/client/app/lib/svelte-json-tree/JSONObjectNode.svelte +40 -0
  30. package/src/client/app/lib/svelte-json-tree/JSONStringNode.svelte +31 -0
  31. package/src/client/app/lib/svelte-json-tree/JSONValueNode.svelte +31 -0
  32. package/src/client/app/lib/svelte-json-tree/PreviewList.svelte +38 -0
  33. package/src/client/app/lib/svelte-json-tree/RegExpNode.svelte +42 -0
  34. package/src/client/app/lib/svelte-json-tree/Root.svelte +75 -0
  35. package/src/client/app/lib/svelte-json-tree/Summary.svelte +9 -0
  36. package/src/client/app/lib/svelte-json-tree/TypedArrayNode.svelte +56 -0
  37. package/src/client/app/lib/svelte-json-tree/index.js +1 -0
  38. package/src/client/app/lib/svelte-json-tree/utils.js +57 -0
  39. package/src/client/app/modules/Console/ConsoleLine.svelte +12 -11
  40. package/src/client/app/modules/Console.svelte +82 -17
  41. package/src/client/app/modules/Exports.svelte +48 -48
  42. package/src/client/app/modules/MidiPanel.svelte +12 -19
  43. package/src/client/app/modules/Monitor.svelte +147 -55
  44. package/src/client/app/modules/Params.svelte +127 -80
  45. package/src/client/app/renderers/2DRenderer.js +1 -0
  46. package/src/client/app/renderers/FragmentRenderer.js +1 -1
  47. package/src/client/app/renderers/P5GLRenderer.js +11 -5
  48. package/src/client/app/renderers/P5Renderer.js +7 -3
  49. package/src/client/app/renderers/THREERenderer.js +42 -79
  50. package/src/client/app/state/Sketch.svelte.js +538 -0
  51. package/src/client/app/state/errors.svelte.js +17 -0
  52. package/src/client/app/state/exports.svelte.js +152 -0
  53. package/src/client/app/state/layout.svelte.js +205 -0
  54. package/src/client/app/state/monitors.svelte.js +36 -0
  55. package/src/client/app/state/renderers.svelte.js +77 -0
  56. package/src/client/app/state/rendering.svelte.js +697 -0
  57. package/src/client/app/state/sketches.svelte.js +73 -0
  58. package/src/client/app/state/utils.svelte.js +65 -0
  59. package/src/client/app/ui/Build.svelte +53 -60
  60. package/src/client/app/ui/ErrorOverlay.svelte +2 -2
  61. package/src/client/app/ui/Field.svelte +63 -189
  62. package/src/client/app/ui/FieldGroup.svelte +4 -5
  63. package/src/client/app/ui/FieldSection.svelte +14 -9
  64. package/src/client/app/ui/FieldSpace.svelte +1 -1
  65. package/src/client/app/ui/FieldTrigger.svelte +86 -84
  66. package/src/client/app/ui/FieldTriggers.svelte +25 -24
  67. package/src/client/app/ui/FloatingParams.svelte +50 -12
  68. package/src/client/app/ui/Layout.svelte +24 -13
  69. package/src/client/app/ui/LayoutColumn.svelte +2 -2
  70. package/src/client/app/ui/LayoutComponent.svelte +86 -195
  71. package/src/client/app/ui/LayoutResizer.svelte +25 -37
  72. package/src/client/app/ui/LayoutRoot.svelte +3 -5
  73. package/src/client/app/ui/LayoutRow.svelte +2 -2
  74. package/src/client/app/ui/LayoutToolbar.svelte +17 -76
  75. package/src/client/app/ui/Module.svelte +31 -35
  76. package/src/client/app/ui/ModuleHeaderAction.svelte +23 -16
  77. package/src/client/app/ui/ModuleHeaderButton.svelte +3 -3
  78. package/src/client/app/ui/ModuleHeaderSelect.svelte +4 -12
  79. package/src/client/app/ui/ModuleRenderer.svelte +84 -22
  80. package/src/client/app/ui/ParamsOutput.svelte +61 -77
  81. package/src/client/app/ui/Preview.svelte +15 -4
  82. package/src/client/app/ui/SelectChevrons.svelte +1 -2
  83. package/src/client/app/ui/SketchRenderer.svelte +89 -701
  84. package/src/client/app/ui/SketchSelect.svelte +14 -49
  85. package/src/client/app/ui/fields/ButtonInput.svelte +14 -11
  86. package/src/client/app/ui/fields/CheckboxInput.svelte +5 -12
  87. package/src/client/app/ui/fields/ColorInput.svelte +46 -121
  88. package/src/client/app/ui/fields/FieldInputRow.svelte +5 -1
  89. package/src/client/app/ui/fields/ImageInput.svelte +14 -14
  90. package/src/client/app/ui/fields/Input.svelte +19 -25
  91. package/src/client/app/ui/fields/IntervalInput.svelte +22 -22
  92. package/src/client/app/ui/fields/NumberInput.svelte +32 -38
  93. package/src/client/app/ui/fields/ProgressInput.svelte +14 -13
  94. package/src/client/app/ui/fields/Select.svelte +34 -45
  95. package/src/client/app/ui/fields/TextInput.svelte +10 -6
  96. package/src/client/app/ui/fields/VectorInput.svelte +25 -30
  97. package/src/client/app/utils/canvas.utils.js +8 -8
  98. package/src/client/app/utils/color.utils.js +46 -13
  99. package/src/client/app/utils/fields.utils.js +1 -1
  100. package/src/client/app/utils/glsl.utils.js +1 -1
  101. package/src/client/app/utils/glslErrors.js +1 -1
  102. package/src/client/main.js +2 -2
@@ -2,90 +2,90 @@
2
2
  import Module from '../ui/Module.svelte';
3
3
  import Field from '../ui/Field.svelte';
4
4
  import FieldGroup from '../ui/FieldGroup.svelte';
5
- import { exports } from '../stores';
5
+ import { layout } from '../state/layout.svelte';
6
6
  import {
7
- recording,
8
- capturing,
9
7
  IMAGE_ENCODINGS,
10
8
  VIDEO_FORMATS,
11
- } from '../stores/exports';
9
+ exports,
10
+ } from '../state/exports.svelte';
12
11
 
13
- export let mID;
14
- export let hasHeader = true;
12
+ let { id = layout.getID(), headless = false } = $props();
15
13
 
16
14
  const LABEL_RECORD = 'start';
17
15
  const LABEL_RECORDING = 'stop';
18
16
 
19
- function record() {
20
- $recording = !$recording;
21
- }
22
-
23
- function capture() {
24
- $capturing = !$capturing;
25
- }
26
-
27
- $: recordLabel = $recording ? LABEL_RECORDING : LABEL_RECORD;
17
+ let recordLabel = $derived(
18
+ exports.recording ? LABEL_RECORDING : LABEL_RECORD,
19
+ );
28
20
  </script>
29
21
 
30
- <Module {mID} {hasHeader} name="exports">
31
- <FieldGroup name="image">
22
+ <Module {id} {headless} name="exports">
23
+ <FieldGroup
24
+ name="image"
25
+ collapsed={exports.imageCollapsed}
26
+ onchange={(collapsed) => (exports.imageCollapsed = collapsed)}
27
+ >
32
28
  <Field
33
29
  key="encoding"
34
- value={$exports.imageEncoding}
30
+ value={exports.imageEncoding}
35
31
  params={{ options: IMAGE_ENCODINGS }}
36
- on:change={(e) => {
37
- $exports.imageEncoding = e.detail;
32
+ onchange={(value) => {
33
+ exports.imageEncoding = value;
38
34
  }}
39
35
  />
40
36
  <Field
41
37
  key="quality"
42
- value={$exports.imageQuality}
38
+ value={exports.imageQuality}
43
39
  params={{ min: 1, max: 100, suffix: '%', triggerable: false }}
44
- on:change={(e) => {
45
- $exports.imageQuality = e.detail;
40
+ onchange={(value) => {
41
+ exports.imageQuality = value;
46
42
  }}
47
43
  />
48
44
  <Field
49
45
  key="pixelsPerInch"
50
- value={$exports.pixelsPerInch}
46
+ value={exports.pixelsPerInch}
51
47
  params={{ step: 1 }}
52
- on:change={(e) => {
53
- $exports.pixelsPerInch = e.detail;
48
+ onchange={(value) => {
49
+ exports.pixelsPerInch = value;
54
50
  }}
55
51
  />
56
52
  <Field
57
53
  key="count"
58
- value={$exports.imageCount || 1}
54
+ value={exports.imageCount || 1}
59
55
  params={{ step: 1 }}
60
- on:change={(e) => {
61
- $exports.imageCount = e.detail;
56
+ onchange={(value) => {
57
+ exports.imageCount = value;
62
58
  }}
63
59
  />
64
60
  <Field
65
61
  key="screenshot"
66
- value={capture}
62
+ value={() => (exports.capturing = !exports.capturing)}
67
63
  params={{ label: 'capture', triggerable: false }}
68
64
  />
69
65
  </FieldGroup>
70
- <FieldGroup name="video">
66
+ <FieldGroup
67
+ name="video"
68
+ collapsed={exports.videoCollapsed}
69
+ onchange={(collapsed) => (exports.videoCollapsed = collapsed)}
70
+ >
71
71
  <Field
72
72
  key="framerate"
73
- value={$exports.framerate}
74
- on:change={(e) => {
75
- $exports.framerate = e.detail;
73
+ value={exports.framerate}
74
+ onchange={(value) => {
75
+ exports.framerate = value;
76
76
  }}
77
77
  />
78
78
  <Field
79
79
  key="format"
80
- value={$exports.videoFormat}
80
+ value={exports.videoFormat}
81
81
  params={{ options: Object.values(VIDEO_FORMATS) }}
82
- on:change={(e) => {
83
- $exports.videoFormat = e.detail;
82
+ onchange={(value) => {
83
+ exports.videoFormat = value;
84
84
  }}
85
85
  />
86
86
  <Field
87
87
  key="quality"
88
- value={$exports.videoQuality}
88
+ value={exports.videoQuality}
89
89
  params={{
90
90
  min: 1,
91
91
  max: 100,
@@ -93,30 +93,30 @@
93
93
  suffix: '%',
94
94
  triggerable: false,
95
95
  }}
96
- on:change={(e) => {
97
- $exports.videoQuality = e.detail;
96
+ onchange={(value) => {
97
+ exports.videoQuality = value;
98
98
  }}
99
99
  />
100
100
  <Field
101
101
  key="useDuration"
102
- value={$exports.useDuration}
103
- on:change={(e) => {
104
- $exports.useDuration = e.detail;
102
+ value={exports.useDuration}
103
+ onchange={(value) => {
104
+ exports.useDuration = value;
105
105
  }}
106
106
  />
107
- {#if $exports.useDuration}
107
+ {#if exports.useDuration}
108
108
  <Field
109
109
  key="loopCount"
110
- value={$exports.loopCount}
110
+ value={exports.loopCount}
111
111
  params={{ step: 1 }}
112
- on:change={(e) => {
113
- $exports.loopCount = e.detail;
112
+ onchange={(value) => {
113
+ exports.loopCount = value;
114
114
  }}
115
115
  />
116
116
  {/if}
117
117
  <Field
118
118
  key="record"
119
- value={record}
119
+ value={() => (exports.recording = !exports.recording)}
120
120
  params={{ label: recordLabel, triggerable: false }}
121
121
  />
122
122
  </FieldGroup>
@@ -4,12 +4,13 @@
4
4
  import Field from '../ui/Field.svelte';
5
5
  import MIDI from '../inputs/MIDI.js';
6
6
 
7
- export let mID;
8
- export let hasHeader;
7
+ let { mID, headless = false, ...restProps } = $props();
9
8
 
10
- let input, output;
11
- let inputs = [],
12
- outputs = [];
9
+ let input = $state(null);
10
+ let output = $state(null);
11
+ let inputs = $state([]);
12
+ let outputs = $state([]);
13
+ let messages = $state([]);
13
14
 
14
15
  function createDeviceOptions(deviceMap = new Map()) {
15
16
  let options = [];
@@ -35,15 +36,10 @@
35
36
  return options;
36
37
  }
37
38
 
38
- let messages = [];
39
-
40
- $: {
39
+ $effect(() => {
41
40
  MIDI.selectedInputID = input;
42
- }
43
-
44
- $: {
45
41
  MIDI.selectedOutputID = output;
46
- }
42
+ });
47
43
 
48
44
  onMount(async () => {
49
45
  await MIDI.request();
@@ -74,21 +70,18 @@
74
70
  ? ` note:${note.name}`
75
71
  : ``;
76
72
 
77
- messages = [
78
- ...messages,
79
- `${time} ${type} number:${note.number}${noteLog}`,
80
- ];
73
+ messages.push(`${time} ${type} number:${note.number}${noteLog}`);
81
74
  });
82
75
 
83
76
  refresh();
84
77
  });
85
78
  </script>
86
79
 
87
- <Module {mID} {hasHeader} name="MIDI" {...$$props} slug="midi">
80
+ <Module {headless} name="MIDI" {...restProps} slug="midi">
88
81
  <Field
89
82
  key="inputs"
90
83
  value={input}
91
- on:change={(event) => (input = event.detail)}
84
+ onchange={(value) => (input = value)}
92
85
  params={{
93
86
  options: inputs,
94
87
  }}
@@ -96,7 +89,7 @@
96
89
  <Field
97
90
  key="outputs"
98
91
  value={output}
99
- on:change={(event) => (output = event.detail)}
92
+ onchange={(value) => (output = value)}
100
93
  params={{
101
94
  options: outputs,
102
95
  }}
@@ -1,74 +1,166 @@
1
- <script context="module">
2
- let ID = 0;
3
- </script>
4
-
5
1
  <script>
6
- import { onMount, onDestroy } from 'svelte';
7
2
  import Module from '../ui/Module.svelte';
8
3
  import SketchRenderer from '../ui/SketchRenderer.svelte';
9
- import OutputRenderer from '../ui/OutputRenderer.svelte';
10
4
  import SketchSelect from '../ui/SketchSelect.svelte';
11
- import { sketchesKeys } from '../stores/sketches.js';
12
- import { monitors, preview } from '../stores/rendering';
13
-
14
- export let mID;
15
- export let hasHeader = true;
16
- export let sketchKey = null;
17
-
18
- let id = ID++;
19
- let name = 'monitor';
20
- let selected = sketchKey
21
- ? sketchKey
22
- : $preview
23
- ? $preview
24
- : $sketchesKeys[Math.min(id, $sketchesKeys.length - 1)];
25
-
26
- sketchesKeys.subscribe((keys) => {
27
- if (!selected && keys.length > 0) {
28
- $monitors = $monitors.map((monitor) => {
29
- if (monitor.id === id) {
30
- monitor.selected =
31
- $sketchesKeys[Math.min(id, $sketchesKeys.length - 1)];
32
- }
33
5
 
34
- return monitor;
35
- });
36
- }
6
+ import ErrorOverlay from '../ui/ErrorOverlay.svelte';
7
+ import { errors } from '../state/errors.svelte.js';
8
+ import { onMount } from 'svelte';
9
+ import { layout } from '../state/layout.svelte';
10
+ import { rendering, SIZES } from '../state/rendering.svelte.js';
11
+ import ModuleHeaderAction from '../ui/ModuleHeaderAction.svelte';
12
+ import { sketchesManager } from '../state/sketches.svelte';
13
+
14
+ let { id = layout.getID(), headless = false, params } = $props();
15
+
16
+ let key = $derived(
17
+ sketchesManager.keys.includes(params.selected) ||
18
+ (params.selected === 'output' && sketchesManager.keys.length > 1)
19
+ ? params.selected
20
+ : sketchesManager.keys[0],
21
+ );
22
+
23
+ let monitors = $derived(rendering.monitors);
24
+ let monitorID = rendering.getMonitorID();
25
+ let index = $derived(monitors.findIndex((m) => m.id === monitorID));
26
+ let name = $derived(
27
+ `Monitor ${monitors.length > 1 ? index + 1 : ''}`.trim(),
28
+ );
29
+ let monitor = $derived(monitors[index]);
30
+
31
+ // display error if error context match current key
32
+ // or if an error doesn't match any params from monitor
33
+ let error = $derived(
34
+ errors.has(key)
35
+ ? errors.get(key)
36
+ : monitors.length === 1 ||
37
+ !monitors.some(
38
+ (m) => m.selected === errors.keys().next().value,
39
+ )
40
+ ? errors.values().next().value
41
+ : null,
42
+ );
43
+
44
+ let dimensions = $state({
45
+ width: undefined,
46
+ height: undefined,
37
47
  });
48
+ let node;
49
+ let zoomLevel = $derived.by(() => {
50
+ const zx = dimensions.width / rendering.width;
51
+ const zy = dimensions.height / rendering.height;
38
52
 
39
- onMount(() => {
40
- $monitors = [
41
- ...$monitors,
42
- {
43
- id,
44
- selected,
45
- },
46
- ];
53
+ if (zx >= 1 && zy >= 1) {
54
+ return 100;
55
+ } else {
56
+ return Math.round(Math.min(zx, zy) * 100);
57
+ }
47
58
  });
48
59
 
49
- monitors.subscribe((all) => {
50
- const current = all.find((monitor) => monitor.id === id);
60
+ function checkForResize(resizing, { width, height }) {
61
+ if (isFinite(width) && isFinite(height)) {
62
+ let isWindowResize = resizing === SIZES.WINDOW;
63
+ let isAspectResize = resizing === SIZES.ASPECT_RATIO;
64
+ let canUpdate = isWindowResize || isAspectResize;
65
+
66
+ if (canUpdate) {
67
+ let newWidth, newHeight;
68
+
69
+ if (isWindowResize) {
70
+ newWidth = Math.round(width);
71
+ newHeight = Math.round(height);
72
+ } else if (isAspectResize) {
73
+ const { offsetWidth, offsetHeight } = node;
74
+ const aspectRatio = rendering.aspectRatio;
75
+ const monitorRatio = offsetWidth / offsetHeight;
51
76
 
52
- if (current && current.selected !== selected) {
53
- selected = current.selected;
77
+ if (aspectRatio < monitorRatio) {
78
+ newHeight = offsetHeight;
79
+ newWidth = Math.round(newHeight * aspectRatio);
80
+ } else {
81
+ newWidth = offsetWidth;
82
+ newHeight = Math.round(newWidth / aspectRatio);
83
+ }
84
+ }
85
+
86
+ let needsUpdate =
87
+ newWidth !== rendering.width ||
88
+ newHeight !== rendering.height;
89
+
90
+ if (needsUpdate) {
91
+ rendering.width = newWidth;
92
+ rendering.height = newHeight;
93
+ }
94
+ }
54
95
  }
96
+ }
97
+
98
+ onMount(() => {
99
+ rendering.monitors.push({
100
+ id: monitorID,
101
+ dimensions,
102
+ });
103
+
104
+ return () => {
105
+ const monitorIndex = rendering.monitors.findIndex(
106
+ (m) => m.id === monitorID,
107
+ );
108
+ rendering.monitors.splice(monitorIndex, 1);
109
+ };
55
110
  });
56
111
 
57
- onDestroy(() => {
58
- $monitors = $monitors.filter((m) => m.id !== id);
112
+ $effect(() => {
113
+ if (index === 0) {
114
+ checkForResize(rendering.resizing, dimensions);
115
+ }
59
116
  });
60
117
 
61
- $: index = $monitors.findIndex((monitor) => monitor.id === id);
62
- $: moduleName = `${name} ${$monitors.length > 1 ? index + 1 : ''}`;
118
+ /**
119
+ *
120
+ * @param {ResizeObserverEntry[]} event
121
+ */
122
+ function onresize(event) {
123
+ const { contentRect, target } = event[0];
124
+
125
+ if (!node) node = target;
126
+
127
+ dimensions.width = contentRect.width;
128
+ dimensions.height = contentRect.height;
129
+ }
63
130
  </script>
64
131
 
65
- <Module {hasHeader} {mID} slug="monitor" name={moduleName} scrollable={false}>
66
- <svelte:fragment slot="header-left">
67
- <SketchSelect monitorID={id} {selected} />
68
- </svelte:fragment>
69
- {#if selected && selected !== 'output'}
70
- <SketchRenderer key={selected} {id} />
71
- {:else if selected}
72
- <OutputRenderer />
132
+ {#snippet headerLeft()}
133
+ <SketchSelect
134
+ monitorID={id}
135
+ sketchKey={key}
136
+ onchange={(event) => {
137
+ params.selected = event.target.value;
138
+ }}
139
+ />
140
+ {/snippet}
141
+
142
+ {#snippet headerRight()}
143
+ <ModuleHeaderAction
144
+ value={zoomLevel}
145
+ permanent
146
+ border
147
+ options={[{ value: zoomLevel, label: `${zoomLevel}%` }]}
148
+ />
149
+ {/snippet}
150
+
151
+ <Module
152
+ {id}
153
+ {headless}
154
+ slug="monitor"
155
+ {name}
156
+ scrollable={false}
157
+ {headerLeft}
158
+ {headerRight}
159
+ {onresize}
160
+ >
161
+ <!-- {#if selected && selected !== 'output'} -->
162
+ <SketchRenderer {key} {id} />
163
+ {#if error}
164
+ <ErrorOverlay {error} />
73
165
  {/if}
74
166
  </Module>