fragment-tools 0.1.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 (192) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +101 -0
  3. package/bin/index.js +19 -0
  4. package/docs/README.md +18 -0
  5. package/docs/api/CLI.md +44 -0
  6. package/docs/api/renderers.md +80 -0
  7. package/docs/api/sketch.md +216 -0
  8. package/docs/api/triggers.md +101 -0
  9. package/docs/guide/about.md +16 -0
  10. package/docs/guide/exports.md +86 -0
  11. package/docs/guide/external-dependencies.md +22 -0
  12. package/docs/guide/getting-started.md +113 -0
  13. package/docs/guide/hot-shader-reloading.md +20 -0
  14. package/docs/guide/shortcuts.md +12 -0
  15. package/docs/guide/triggers.png +0 -0
  16. package/docs/guide/using-triggers.md +39 -0
  17. package/examples/cube-three.js +34 -0
  18. package/examples/ellipse-p5.js +26 -0
  19. package/examples/icon.fs +96 -0
  20. package/examples/icon.js +63 -0
  21. package/examples/icon.png +0 -0
  22. package/examples/icon.transparent.png +0 -0
  23. package/examples/package-lock.json +40 -0
  24. package/examples/package.json +15 -0
  25. package/examples/shape-2d.js +45 -0
  26. package/examples/shape-three.js +49 -0
  27. package/examples/shape-tree.fs +3 -0
  28. package/package.json +37 -0
  29. package/screenshot.png +0 -0
  30. package/src/cli/db.js +17 -0
  31. package/src/cli/index.js +198 -0
  32. package/src/cli/log.js +26 -0
  33. package/src/cli/plugins/check-dependencies.js +77 -0
  34. package/src/cli/plugins/db.js +12 -0
  35. package/src/cli/plugins/hot-shader-reload.js +86 -0
  36. package/src/cli/plugins/hot-sketch-reload.js +39 -0
  37. package/src/cli/plugins/screenshot.js +31 -0
  38. package/src/cli/server.js +140 -0
  39. package/src/cli/templates/2d.js +15 -0
  40. package/src/cli/templates/fragment.fs +10 -0
  41. package/src/cli/templates/fragment.js +18 -0
  42. package/src/cli/templates/index.js +24 -0
  43. package/src/cli/templates/p5.js +13 -0
  44. package/src/cli/templates/three-fragment.js +53 -0
  45. package/src/cli/templates/three-orthographic.js +23 -0
  46. package/src/cli/templates/three-perspective.js +20 -0
  47. package/src/cli/ws.js +92 -0
  48. package/src/client/app/App.svelte +8 -0
  49. package/src/client/app/client.js +68 -0
  50. package/src/client/app/components/IconCross.svelte +29 -0
  51. package/src/client/app/components/Init.svelte +13 -0
  52. package/src/client/app/components/KeyBinding.svelte +32 -0
  53. package/src/client/app/inputs/Input.js +15 -0
  54. package/src/client/app/inputs/Keyboard.js +21 -0
  55. package/src/client/app/inputs/MIDI.js +144 -0
  56. package/src/client/app/inputs/Mouse.js +5 -0
  57. package/src/client/app/inputs/Webcam.js +98 -0
  58. package/src/client/app/lib/canvas-recorder/CanvasRecorder.js +88 -0
  59. package/src/client/app/lib/canvas-recorder/FFMPEGRecorder.js +56 -0
  60. package/src/client/app/lib/canvas-recorder/FrameRecorder.js +40 -0
  61. package/src/client/app/lib/canvas-recorder/GIFRecorder.js +52 -0
  62. package/src/client/app/lib/canvas-recorder/MP4Recorder.js +46 -0
  63. package/src/client/app/lib/canvas-recorder/WebMRecorder.js +30 -0
  64. package/src/client/app/lib/canvas-recorder/mp4.js +20 -0
  65. package/src/client/app/lib/canvas-recorder/mp4.wasm +0 -0
  66. package/src/client/app/lib/canvas-recorder/utils.js +22 -0
  67. package/src/client/app/lib/gl/Geometry.js +39 -0
  68. package/src/client/app/lib/gl/Program.js +130 -0
  69. package/src/client/app/lib/gl/Renderer.js +148 -0
  70. package/src/client/app/lib/gl/Texture.js +114 -0
  71. package/src/client/app/lib/gl/index.js +109 -0
  72. package/src/client/app/lib/gl/utils.js +5 -0
  73. package/src/client/app/lib/helpers/frameDebounce.js +40 -0
  74. package/src/client/app/lib/loader/index.js +20 -0
  75. package/src/client/app/lib/loader/loadImage.js +19 -0
  76. package/src/client/app/lib/loader/loadScript.js +14 -0
  77. package/src/client/app/lib/paper-sizes.js +104 -0
  78. package/src/client/app/lib/presets.js +12 -0
  79. package/src/client/app/lib/tempo/Analyser.js +165 -0
  80. package/src/client/app/lib/tempo/Range.js +97 -0
  81. package/src/client/app/lib/tempo/index.js +138 -0
  82. package/src/client/app/modules/AudioAnalyser/Range.svelte +93 -0
  83. package/src/client/app/modules/AudioAnalyser/Spectrum.svelte +31 -0
  84. package/src/client/app/modules/AudioAnalyser.svelte +70 -0
  85. package/src/client/app/modules/Console/ConsoleLine.svelte +254 -0
  86. package/src/client/app/modules/Console.svelte +82 -0
  87. package/src/client/app/modules/Exports.svelte +105 -0
  88. package/src/client/app/modules/MidiPanel.svelte +106 -0
  89. package/src/client/app/modules/Monitor.svelte +62 -0
  90. package/src/client/app/modules/Params.svelte +112 -0
  91. package/src/client/app/renderers/2DRenderer.js +5 -0
  92. package/src/client/app/renderers/FragmentRenderer.js +62 -0
  93. package/src/client/app/renderers/OGLRenderer.js +0 -0
  94. package/src/client/app/renderers/P5Renderer.js +39 -0
  95. package/src/client/app/renderers/THREERenderer.js +128 -0
  96. package/src/client/app/stores/audioAnalysis.js +10 -0
  97. package/src/client/app/stores/console.js +76 -0
  98. package/src/client/app/stores/errors.js +25 -0
  99. package/src/client/app/stores/exports.js +28 -0
  100. package/src/client/app/stores/index.js +2 -0
  101. package/src/client/app/stores/layout.js +187 -0
  102. package/src/client/app/stores/multisampling.js +16 -0
  103. package/src/client/app/stores/props.js +44 -0
  104. package/src/client/app/stores/renderers.js +60 -0
  105. package/src/client/app/stores/rendering.js +111 -0
  106. package/src/client/app/stores/sketches.js +40 -0
  107. package/src/client/app/stores/time.js +27 -0
  108. package/src/client/app/stores/utils.js +66 -0
  109. package/src/client/app/transitions/fade.js +17 -0
  110. package/src/client/app/transitions/index.js +12 -0
  111. package/src/client/app/transitions/splitX.js +16 -0
  112. package/src/client/app/transitions/splitY.js +16 -0
  113. package/src/client/app/triggers/Keyboard.js +95 -0
  114. package/src/client/app/triggers/MIDI.js +122 -0
  115. package/src/client/app/triggers/Mouse.js +96 -0
  116. package/src/client/app/triggers/Trigger.js +71 -0
  117. package/src/client/app/triggers/index.js +19 -0
  118. package/src/client/app/triggers/shared.js +37 -0
  119. package/src/client/app/ui/Build.svelte +96 -0
  120. package/src/client/app/ui/ErrorOverlay.svelte +130 -0
  121. package/src/client/app/ui/Field.svelte +262 -0
  122. package/src/client/app/ui/FieldGroup.svelte +103 -0
  123. package/src/client/app/ui/FieldSection.svelte +123 -0
  124. package/src/client/app/ui/FieldSpace.svelte +37 -0
  125. package/src/client/app/ui/FieldTrigger.svelte +263 -0
  126. package/src/client/app/ui/FieldTriggers.svelte +58 -0
  127. package/src/client/app/ui/FloatingParams.svelte +49 -0
  128. package/src/client/app/ui/Layout.svelte +50 -0
  129. package/src/client/app/ui/LayoutColumn.svelte +9 -0
  130. package/src/client/app/ui/LayoutComponent.svelte +279 -0
  131. package/src/client/app/ui/LayoutResizer.svelte +218 -0
  132. package/src/client/app/ui/LayoutRoot.svelte +11 -0
  133. package/src/client/app/ui/LayoutRow.svelte +9 -0
  134. package/src/client/app/ui/LayoutToolbar.svelte +264 -0
  135. package/src/client/app/ui/Module.svelte +154 -0
  136. package/src/client/app/ui/ModuleHeaderAction.svelte +87 -0
  137. package/src/client/app/ui/ModuleHeaderButton.svelte +21 -0
  138. package/src/client/app/ui/ModuleHeaderSelect.svelte +50 -0
  139. package/src/client/app/ui/ModuleRenderer.svelte +38 -0
  140. package/src/client/app/ui/OutputRenderer.svelte +149 -0
  141. package/src/client/app/ui/ParamsMultisampling.svelte +109 -0
  142. package/src/client/app/ui/ParamsOutput.svelte +139 -0
  143. package/src/client/app/ui/Preview.svelte +15 -0
  144. package/src/client/app/ui/SelectChevrons.svelte +25 -0
  145. package/src/client/app/ui/SketchRenderer.svelte +672 -0
  146. package/src/client/app/ui/SketchSelect.svelte +49 -0
  147. package/src/client/app/ui/fields/ButtonInput.svelte +54 -0
  148. package/src/client/app/ui/fields/CheckboxInput.svelte +70 -0
  149. package/src/client/app/ui/fields/ColorInput.svelte +187 -0
  150. package/src/client/app/ui/fields/FieldInputRow.svelte +13 -0
  151. package/src/client/app/ui/fields/ImageInput.svelte +145 -0
  152. package/src/client/app/ui/fields/Input.svelte +120 -0
  153. package/src/client/app/ui/fields/ListInput.svelte +106 -0
  154. package/src/client/app/ui/fields/NumberInput.svelte +114 -0
  155. package/src/client/app/ui/fields/ProgressInput.svelte +90 -0
  156. package/src/client/app/ui/fields/Select.svelte +116 -0
  157. package/src/client/app/ui/fields/TextInput.svelte +18 -0
  158. package/src/client/app/ui/fields/Vec2Input.svelte +5 -0
  159. package/src/client/app/ui/fields/Vec3Input.svelte +6 -0
  160. package/src/client/app/ui/fields/VectorInput.svelte +102 -0
  161. package/src/client/app/utils/canvas.utils.js +229 -0
  162. package/src/client/app/utils/color.utils.js +427 -0
  163. package/src/client/app/utils/file.utils.js +77 -0
  164. package/src/client/app/utils/glsl.utils.js +14 -0
  165. package/src/client/app/utils/glslErrors.js +154 -0
  166. package/src/client/app/utils/index.js +39 -0
  167. package/src/client/app/utils/math.utils.js +23 -0
  168. package/src/client/app/utils/props.utils.js +53 -0
  169. package/src/client/index.html +18 -0
  170. package/src/client/main.js +9 -0
  171. package/src/client/public/css/global.css +115 -0
  172. package/src/client/public/favicon.ico +0 -0
  173. package/src/client/public/fonts/Inter-Bold.woff2 +0 -0
  174. package/src/client/public/fonts/Inter-Italic.woff2 +0 -0
  175. package/src/client/public/fonts/Inter-Regular.woff2 +0 -0
  176. package/src/client/public/fonts/Inter-SemiBold.woff2 +0 -0
  177. package/src/client/public/fonts/JetBrainsMono-Regular.woff2 +0 -0
  178. package/src/client/public/icons/chevron-bottom.svg +3 -0
  179. package/src/client/public/icons/chevron-right.svg +3 -0
  180. package/src/client/public/icons/chevron-top.svg +3 -0
  181. package/src/client/public/icons/columns-horizontal.svg +4 -0
  182. package/src/client/public/icons/columns-vertical.svg +4 -0
  183. package/src/client/public/icons/folder-plus.svg +6 -0
  184. package/src/client/public/icons/lock.svg +4 -0
  185. package/src/client/public/icons/picture-in-picture.svg +4 -0
  186. package/src/client/public/icons/trash.svg +5 -0
  187. package/src/client/public/icons/trigger.svg +8 -0
  188. package/src/client/public/icons/unlock.svg +4 -0
  189. package/src/client/public/js/ffmpeg.min.js +2 -0
  190. package/src/client/public/js/ffmpeg.min.js.map +1 -0
  191. package/src/client/public/js/gif.js +2 -0
  192. package/src/client/public/js/gif.worker.js +2 -0
@@ -0,0 +1,263 @@
1
+ <script context="module">
2
+ const inputs = {
3
+ "Mouse": {
4
+ events: [
5
+ { name: "onMouseDown", triggerable: true, controllable: false },
6
+ { name: "onMouseUp", triggerable: true, controllable: false },
7
+ { name: "onMouseMove", triggerable: true, controllable: false },
8
+ { name: "onClick", triggerable: true, controllable: false },
9
+ ]
10
+ },
11
+ "Keyboard": {
12
+ events: [
13
+ { name: "onKeyDown", triggerable: true, controllable: false },
14
+ { name: "onKeyPress", triggerable: true, controllable: false },
15
+ { name: "onKeyUp", triggerable: true, controllable: false },
16
+ ]
17
+ },
18
+ "MIDI": {
19
+ events: [
20
+ { name: "onNoteOn", triggerable: true, controllable: false },
21
+ { name: "onNoteOff", triggerable: true, controllable: false },
22
+ { name: "onNumberOn", triggerable: true, controllable: false },
23
+ { name: "onNumberOff", triggerable: true, controllable: false },
24
+ { name: "onControlChange", triggerable: false, controllable: true },
25
+ ],
26
+ }
27
+ };
28
+ </script>
29
+
30
+ <script>
31
+ import IconCross from "../components/IconCross.svelte";
32
+ import ButtonInput from "./fields/ButtonInput.svelte";
33
+ import FieldInputRow from "./fields/FieldInputRow.svelte";
34
+ import Select from "./fields/Select.svelte";
35
+ import TextInput from "./fields/TextInput.svelte";
36
+ import * as triggersMap from "../triggers/index.js";
37
+ import { createEventDispatcher, onDestroy, onMount } from "svelte";
38
+
39
+ export let index;
40
+ export let inputType = undefined;
41
+ export let eventName = undefined;
42
+ export let enabled = true;
43
+ export let controllable = false;
44
+ export let triggerable = false;
45
+ export let context;
46
+ export let onTrigger = () => {};
47
+ export let params = { key: []};
48
+
49
+ let trigger;
50
+ let dispatch = createEventDispatcher();
51
+
52
+ function registerTrigger() {
53
+ if (trigger) {
54
+ enabled = trigger.enabled;
55
+
56
+ trigger.destroy();
57
+ trigger = null;
58
+ }
59
+
60
+ const createTrigger = triggersMap[eventName];
61
+
62
+ trigger = createTrigger(onTrigger, {
63
+ ...params,
64
+ context,
65
+ hot: false,
66
+ enabled,
67
+ });
68
+ }
69
+
70
+ function onTypeChange(event) {
71
+ inputType = event.detail;
72
+
73
+ if (!eventOptions.includes(eventName)) {
74
+ eventName = undefined;
75
+ key = null;
76
+ }
77
+
78
+ if (trigger) {
79
+ trigger.destroy();
80
+ trigger = null;
81
+ }
82
+ }
83
+
84
+ function onEventChange(event) {
85
+ eventName = event.detail;
86
+
87
+ if (inputType === "Mouse") {
88
+ registerTrigger();
89
+ }
90
+ }
91
+
92
+ function onTextChange(e) {
93
+ const castToNumber = ["onControlChange", "onNumberOn", "onNumberOff"].includes(eventName);
94
+
95
+ params.key = e.detail.split(',').map((value) => {
96
+ return castToNumber ? Number(value) : value;
97
+ });
98
+
99
+ registerTrigger();
100
+ }
101
+
102
+ function handleClickDelete() {
103
+ dispatch('delete', index);
104
+ }
105
+
106
+ function toggleTrigger() {
107
+ if (trigger) {
108
+ trigger.enabled = !trigger.enabled;
109
+ }
110
+ }
111
+
112
+ onMount(() => {
113
+ if (
114
+ (inputType === "Mouse" && eventName) ||
115
+ (inputType && eventName && params.key)
116
+ ) {
117
+ registerTrigger();
118
+ }
119
+ });
120
+
121
+ onDestroy(() => {
122
+ if (trigger) {
123
+ trigger.destroy();
124
+ trigger = null;
125
+ }
126
+ });
127
+
128
+ $: validInputs = [...Object.keys(inputs)].reduce((all, inputName) => {
129
+ const input = inputs[inputName];
130
+ const { disabled, events } = input;
131
+ const filteredEvents = events.filter((event) => {
132
+ return event.triggerable === triggerable && event.controllable === controllable;
133
+ });
134
+
135
+ if (filteredEvents.length > 0) {
136
+ all[inputName] = { events: filteredEvents, disabled };
137
+ };
138
+
139
+ return all;
140
+ }, {});
141
+
142
+ $: inputOptions = [
143
+ { label: "Select input", value: undefined, disabled: true },
144
+ ...Object.keys(validInputs).map((inputName) => ({
145
+ value: inputName,
146
+ disabled: validInputs[inputName].disabled,
147
+ }))
148
+ ];
149
+
150
+ $: eventOptions = inputType ? [
151
+ { label: "-", value: undefined, disabled: true },
152
+ ...validInputs[inputType].events.map(event => ({ value: event.name }))
153
+ ] : [];
154
+
155
+ $: isValid = inputType && eventName;
156
+ $: key = params.key && params.key.length ? params.key.join(',') : "";
157
+
158
+ </script>
159
+
160
+ <div class="field-trigger {inputType ? inputType.toLowerCase() : ""}">
161
+ <FieldInputRow --grid-template-columns="var(--width-activity) var(--width-cols) var(--width-delete)">
162
+ <button
163
+ class="activity"
164
+ class:valid={isValid}
165
+ class:enabled={trigger && trigger.enabled}
166
+ class:disabled={!trigger || !trigger.enabled}
167
+ on:click={toggleTrigger}
168
+ ></button>
169
+ <Select
170
+ name="trigger-input"
171
+ value={inputType}
172
+ options={inputOptions}
173
+ on:change={onTypeChange}
174
+ />
175
+ {#if inputType }
176
+ <Select
177
+ options={eventOptions}
178
+ bind:value={eventName}
179
+ disabled={inputType === undefined}
180
+ on:change={onEventChange}
181
+ />
182
+ {/if}
183
+ {#if inputType === 'Keyboard'}
184
+ <TextInput
185
+ bind:value={key}
186
+ label="key"
187
+ on:input={onTextChange}
188
+ />
189
+ {/if}
190
+ {#if inputType === 'MIDI'}
191
+ <TextInput
192
+ bind:value={key}
193
+ label={["onNoteOn", "onNoteOff"].includes(eventName) ? "note" : "number"}
194
+ on:input={onTextChange}
195
+ />
196
+ {/if}
197
+ <ButtonInput
198
+ label="delete"
199
+ showLabel={false}
200
+ on:click={handleClickDelete}
201
+ --color-text="white"
202
+ --background-color="var(--color-red)"
203
+ --box-shadow-color-active="var(--color-lightred)"
204
+ >
205
+ <IconCross />
206
+ </ButtonInput>
207
+ </FieldInputRow>
208
+ </div>
209
+
210
+ <style>
211
+
212
+ .field-trigger {
213
+ --width-delete: var(--height-input);
214
+ --width-input: 90px;
215
+ --width-activity: 16px;
216
+ --width-cols: 1fr;
217
+
218
+ width: 100%;
219
+ }
220
+
221
+ .activity {
222
+ --background-color: rgba(255, 255, 255, 0.5);
223
+
224
+ position: relative;
225
+
226
+ width: var(--width-activity);
227
+ height: 100%;
228
+
229
+ background-color: transparent;
230
+ }
231
+
232
+ .activity:before {
233
+ --size: 4px;
234
+
235
+ content: '';
236
+
237
+ position: absolute;
238
+ top: calc(50% - var(--size) * 0.5);
239
+ left: calc(50% - var(--size) * 0.5);
240
+
241
+ width: var(--size);
242
+ height: var(--size);
243
+ border-radius: 2px;
244
+
245
+ background-color: var(--background-color);
246
+ }
247
+
248
+ .activity.valid.enabled {
249
+ --background-color: var(--color-green);
250
+ }
251
+
252
+ .activity.valid.disabled {
253
+ --background-color: var(--color-red);
254
+ }
255
+
256
+ .field-trigger.mouse {
257
+ --width-cols: var(--width-input) 1fr;
258
+ }
259
+
260
+ .field-trigger.keyboard, .field-trigger.midi {
261
+ --width-cols: var(--width-input) 1fr 0.75fr;
262
+ }
263
+ </style>
@@ -0,0 +1,58 @@
1
+ <script>
2
+ import FieldTrigger from "./FieldTrigger.svelte";
3
+ import ButtonInput from "./fields/ButtonInput.svelte";
4
+
5
+ export let context;
6
+ export let onTrigger;
7
+ export let triggers;
8
+ export let triggerable = false;
9
+ export let controllable = false;
10
+
11
+ function onTriggerDelete(e) {
12
+ const triggerIndex = e.detail;
13
+
14
+ $triggers = $triggers.filter((t, i) => i !== triggerIndex);
15
+ }
16
+
17
+ function handleClickAdd() {
18
+ triggers.update((current) => {
19
+ return [
20
+ ...current,
21
+ {
22
+ inputType: undefined,
23
+ eventName: undefined,
24
+ },
25
+ ]
26
+ });
27
+ }
28
+ </script>
29
+
30
+ {#if onTrigger }
31
+ <ButtonInput label="add trigger" on:click={handleClickAdd} />
32
+ <div class="field-triggers">
33
+ {#each $triggers as trigger, index}
34
+ <FieldTrigger
35
+ {index}
36
+ bind:inputType={trigger.inputType}
37
+ bind:eventName={trigger.eventName}
38
+ bind:params={trigger.params}
39
+ bind:enabled={trigger.enabled}
40
+ {onTrigger}
41
+ {context}
42
+ {controllable}
43
+ {triggerable}
44
+ on:delete={onTriggerDelete}
45
+ />
46
+ {/each}
47
+ </div>
48
+ {/if}
49
+
50
+ <style>
51
+ .field-triggers {
52
+ width: 100%;
53
+ }
54
+
55
+ .field-triggers:not(:empty) {
56
+ margin-top: var(--column-gap);
57
+ }
58
+ </style>
@@ -0,0 +1,49 @@
1
+ <script>
2
+ import KeyBinding from "../components/KeyBinding.svelte";
3
+ import Params from "../modules/Params.svelte";
4
+
5
+ export let size = 0.3;
6
+ export let align = "right";
7
+ export let output = false;
8
+ export let hidden = false;
9
+
10
+ $: visible = !hidden;
11
+ $: width = typeof size === "number" ? `${size * 100}%` : size;
12
+
13
+ </script>
14
+
15
+ <div
16
+ class="container"
17
+ class:hidden={!visible}
18
+ class:align-left={align === "left"}
19
+ class:align-right={align === "right"}
20
+ style={`width: ${width};`}
21
+ >
22
+ <Params {output}/>
23
+ </div>
24
+ <KeyBinding key="h" on:trigger={() => visible = !visible} />
25
+
26
+ <style>
27
+ .container {
28
+ --padding: 16px;
29
+ position: absolute;
30
+ top: var(--padding);
31
+
32
+ height: auto;
33
+
34
+ border-radius: calc(var(--border-radius-input) * 2);
35
+ overflow: hidden;
36
+ }
37
+
38
+ .container.hidden {
39
+ display: none;
40
+ }
41
+
42
+ .container.align-left {
43
+ left: var(--padding);
44
+ }
45
+
46
+ .container.align-right {
47
+ right: var(--padding);
48
+ }
49
+ </style>
@@ -0,0 +1,50 @@
1
+ <script>
2
+ import Root from "./LayoutRoot.svelte";
3
+ import Column from "./LayoutColumn.svelte";
4
+ import Build from "./Build.svelte";
5
+ import Row from "./LayoutRow.svelte";
6
+ import ModuleRenderer from "./ModuleRenderer.svelte";
7
+ import { layout } from "../stores/layout.js";
8
+ import KeyBinding from "../components/KeyBinding.svelte";
9
+ import { monitors, preview } from "../stores/rendering";
10
+
11
+ function toggleEdition() {
12
+ $layout.editing = !$layout.editing;
13
+ }
14
+
15
+ function togglePreview() {
16
+ if ($monitors.length === 1 && !$layout.previewing) {
17
+ $preview = $monitors[0].selected;
18
+ }
19
+
20
+ $layout.previewing = !$layout.previewing;
21
+ }
22
+
23
+ </script>
24
+
25
+ <Root>
26
+ {#if __PRODUCTION__ || $layout.previewing }
27
+ <Build />
28
+ {:else}
29
+ <Row size={1}>
30
+ <Column size={0.65}>
31
+ <ModuleRenderer name="monitor" />
32
+ </Column>
33
+ <Column size={0.35}>
34
+ <Row size={1}>
35
+ <ModuleRenderer name="exports" />
36
+ </Row>
37
+ <Row size={1}>
38
+ <ModuleRenderer name="params" />
39
+ </Row>
40
+ </Column>
41
+ </Row>
42
+ {/if}
43
+ </Root>
44
+ {#if !__PRODUCTION__}
45
+ <KeyBinding key="w" on:trigger={toggleEdition} />
46
+ <KeyBinding key="p" on:trigger={togglePreview} />
47
+ {/if}
48
+ {#if $layout.editing}
49
+ <KeyBinding key="Escape" type="down" on:trigger={toggleEdition} />
50
+ {/if}
@@ -0,0 +1,9 @@
1
+ <script>
2
+ import LayoutComponent from "./LayoutComponent.svelte";
3
+
4
+ export let size = 1;
5
+ </script>
6
+
7
+ <LayoutComponent size={size} type="column">
8
+ <slot></slot>
9
+ </LayoutComponent>
@@ -0,0 +1,279 @@
1
+ <script context="module">
2
+ let ID = 0;
3
+ </script>
4
+
5
+ <script>
6
+ import { getContext, hasContext, onDestroy, setContext } from "svelte";
7
+ import { writable } from "svelte/store";
8
+ import { addChildren, addSibling, layout, remove, replaceChildren, swapRoot, updateModule } from "../stores/layout";
9
+ import Toolbar from "./LayoutToolbar.svelte";
10
+ import Resizer from "./LayoutResizer.svelte";
11
+ import { getModuleID } from "./Module.svelte";
12
+ import ModuleRenderer from "./ModuleRenderer.svelte";
13
+ import Preview from "./Preview.svelte";
14
+
15
+ export let size = 1;
16
+ export let type = "column";
17
+ export let tree = { children: [] };
18
+
19
+ let parent = hasContext('parent') ? getContext('parent') : null;
20
+ let depth = hasContext('depth') ? (getContext('depth') + 1) : 0;
21
+ let module = writable({});
22
+ setContext('depth', depth);
23
+ setContext('module', module);
24
+
25
+ let isRoot = parent === null;
26
+ let children = writable([]);
27
+ let style = "";
28
+
29
+ function createComponent({
30
+ id,
31
+ parent = null,
32
+ root = false,
33
+ node = null,
34
+ depth,
35
+ size = 1,
36
+ minimized = false,
37
+ type,
38
+ children = [],
39
+ }) {
40
+ return {
41
+ id,
42
+ root,
43
+ node,
44
+ depth,
45
+ size,
46
+ minimized,
47
+ parent: parent ? parent.id : null,
48
+ type,
49
+ children,
50
+ };
51
+ }
52
+
53
+ let current = createComponent({
54
+ id: !isNaN(tree.id) ? tree.id : ID++,
55
+ root: isRoot,
56
+ depth,
57
+ size,
58
+ parent,
59
+ type,
60
+ });
61
+
62
+ ID = Math.max(ID, !isNaN(current.id) ? current.id + 1 : 0);
63
+
64
+ $: isColumn = type === "column";
65
+ $: isRow = !isColumn;
66
+
67
+ const context = {
68
+ id: current.id,
69
+ children,
70
+ registerChild: (child) => {
71
+ $children = [...$children, child];
72
+
73
+ onDestroy(() => {
74
+ $children = $children.filter((c) => c !== child);
75
+ });
76
+ }
77
+ };
78
+
79
+ setContext('parent', context);
80
+
81
+ if (parent) {
82
+ parent.registerChild(current);
83
+ }
84
+
85
+ if (!__PRODUCTION__) {
86
+ $layout.registerChild(current, () => $children);
87
+ }
88
+
89
+ $: {
90
+ let property = ``, value = ``;
91
+
92
+ const nodes = tree.children;
93
+
94
+ if (Array.isArray(nodes) && nodes.length > 1) {
95
+ if (isColumn) {
96
+ property = `grid-template-rows`;
97
+ value = nodes.map((row, i) => {
98
+ let size = `${row.size}fr`;
99
+
100
+ return `minmax(25px, ${size}) 0px`;
101
+ }).join(' ');
102
+ } else {
103
+ property = `grid-template-columns`;
104
+ value = nodes.map((col, i) => {
105
+ let size = `${col.size}fr`;
106
+
107
+ return `minmax(25px, ${size}) 0px`;
108
+ }).join(' ');
109
+ }
110
+
111
+ style = `${property}:${value}`;
112
+ } else {
113
+ style = "";
114
+ }
115
+ }
116
+
117
+ function addComponent(newType) {
118
+ const isSibling = newType === type;
119
+
120
+ const newborn = createComponent({
121
+ id: ID++,
122
+ parent: isSibling ? parent : context,
123
+ depth: isSibling ? depth : depth + 1,
124
+ type: newType,
125
+ children: [
126
+ { mID: getModuleID(), type: "module" },
127
+ ]
128
+ });
129
+
130
+ if (isSibling) {
131
+ if (isRoot) {
132
+ const newSibling = createComponent({
133
+ id: ID++,
134
+ depth: newborn.depth,
135
+ type: newborn.type,
136
+ children: current.children,
137
+ });
138
+
139
+ // switch type
140
+ current.children = [
141
+ newSibling,
142
+ newborn,
143
+ ];
144
+ current.type = current.type === "column" ? "row" : "column";
145
+
146
+ swapRoot(current);
147
+ } else {
148
+ addSibling(current, newborn);
149
+ }
150
+ } else {
151
+ if ($children.length === 1 && $children[0].type === "module") {
152
+ replaceChildren(current, [
153
+ ...$children.map((c, i) => createComponent({
154
+ id: ID++,
155
+ type: type === "row" ? "column" : "row",
156
+ depth: depth + 1,
157
+ children: [
158
+ c,
159
+ ]
160
+ })),
161
+ newborn
162
+ ]);
163
+
164
+ module.set({});
165
+ } else {
166
+ addChildren(current, newborn);
167
+ }
168
+ }
169
+ }
170
+
171
+ function addColumn() {
172
+ addComponent('column');
173
+ }
174
+
175
+ function addRow() {
176
+ addComponent("row");
177
+ }
178
+
179
+ function deleteCurrent() {
180
+ remove(current);
181
+
182
+ $children = current.children;
183
+ }
184
+
185
+ function handleModuleChange(event) {
186
+ const moduleName = event.detail;
187
+ $children[0].name = moduleName; // keep state when replacingChildren
188
+
189
+ updateModule($module, {
190
+ name: moduleName,
191
+ });
192
+ }
193
+
194
+ let offsetWidth;
195
+
196
+ $: minimized = current.minimized;
197
+
198
+ </script>
199
+
200
+ <div
201
+ style={style}
202
+ class:column={isColumn}
203
+ class:root={isRoot}
204
+ class:row={isRow}
205
+ class:minimized={minimized}
206
+ bind:this={current.node}
207
+ bind:offsetWidth={offsetWidth}
208
+ >
209
+ {#if isRoot && $layout.previewing}
210
+ <Preview />
211
+ {:else if tree && Array.isArray(tree.children) && tree.children.length > 0 }
212
+ {#each tree.children as child (child.id) }
213
+ {#if child.type === "column" || child.type === "row"}
214
+ <svelte:self type={child.type} size={child.size} tree={child} />
215
+ {:else if child.type === "module"}
216
+ <ModuleRenderer name={child.name} mID={child.mID} hasHeader={child.hasHeader} />
217
+ {/if}
218
+ {/each}
219
+ {:else}
220
+ <slot></slot>
221
+ {/if}
222
+ {#if $layout.editing && (($children.length === 1 && $children[0].type === "module") || isRoot) }
223
+ <Toolbar
224
+ {isRoot}
225
+ moduleName={$children[0].name}
226
+ on:change={handleModuleChange}
227
+ on:add-row={addRow}
228
+ on:add-column={addColumn}
229
+ on:delete={deleteCurrent}
230
+ vertical={offsetWidth < 300}
231
+ />
232
+ {/if}
233
+ </div>
234
+ {#if !isRoot }
235
+ <Resizer direction={isColumn ? "vertical" : "horizontal"} {current} {parent} />
236
+ {/if}
237
+
238
+
239
+ <style>
240
+ .root {
241
+ align-content: stretch;
242
+ width: 100%;
243
+ height: 100%;
244
+ }
245
+
246
+ .column {
247
+ position: relative;
248
+ display: grid;
249
+ grid-template-columns: 1fr;
250
+ grid-template-rows: minmax(25px, 1fr);
251
+ }
252
+
253
+ .column:not(:last-child) {
254
+ border-right: 0.5px solid var(--color-lightblack);
255
+ }
256
+
257
+ .column:not(:first-child) {
258
+ border-left: 0.5px solid var(--color-lightblack);
259
+ }
260
+
261
+ .row {
262
+ position: relative;
263
+ display: grid;
264
+ grid-template-columns: 1fr;
265
+ grid-template-rows: minmax(25px, 1fr);
266
+ width: 100%;
267
+ height: 100%;
268
+
269
+ background-color: var(--color-background);
270
+ }
271
+
272
+ .row:not(:first-child) {
273
+ border-top: 0.5px solid var(--color-lightblack);
274
+ }
275
+
276
+ .row:not(:last-child) {
277
+ border-bottom: 0.5px solid var(--color-lightblack);
278
+ }
279
+ </style>