fragment-tools 0.1.13 → 0.1.14

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 (52) hide show
  1. package/bin/index.js +2 -2
  2. package/package.json +4 -5
  3. package/src/cli/log.js +31 -21
  4. package/src/cli/plugins/check-dependencies.js +47 -30
  5. package/src/cli/plugins/hot-shader-replacement.js +384 -0
  6. package/src/cli/plugins/hot-sketch-reload.js +3 -13
  7. package/src/cli/plugins/screenshot.js +57 -20
  8. package/src/cli/server.js +144 -133
  9. package/src/client/app/App.svelte +3 -3
  10. package/src/client/app/client.js +55 -39
  11. package/src/client/app/components/Init.svelte +12 -9
  12. package/src/client/app/helpers.js +42 -0
  13. package/src/client/app/hooks.js +20 -0
  14. package/src/client/app/inputs/Keyboard.js +13 -15
  15. package/src/client/app/inputs/MIDI.js +14 -15
  16. package/src/client/app/lib/canvas-recorder/CanvasRecorder.js +41 -21
  17. package/src/client/app/lib/gl/Renderer.js +127 -139
  18. package/src/client/app/modules/Exports.svelte +62 -43
  19. package/src/client/app/modules/MidiPanel.svelte +100 -101
  20. package/src/client/app/modules/Params.svelte +116 -103
  21. package/src/client/app/renderers/2DRenderer.js +3 -3
  22. package/src/client/app/renderers/FragmentRenderer.js +30 -23
  23. package/src/client/app/renderers/P5Renderer.js +10 -7
  24. package/src/client/app/renderers/THREERenderer.js +136 -94
  25. package/src/client/app/stores/exports.js +36 -20
  26. package/src/client/app/stores/props.js +28 -5
  27. package/src/client/app/stores/renderers.js +22 -15
  28. package/src/client/app/stores/sketches.js +7 -9
  29. package/src/client/app/stores/utils.js +95 -38
  30. package/src/client/app/triggers/Keyboard.js +88 -79
  31. package/src/client/app/triggers/MIDI.js +110 -84
  32. package/src/client/app/ui/Field.svelte +343 -240
  33. package/src/client/app/ui/FieldGroup.svelte +106 -94
  34. package/src/client/app/ui/FieldSection.svelte +125 -116
  35. package/src/client/app/ui/ParamsMultisampling.svelte +96 -95
  36. package/src/client/app/ui/ParamsOutput.svelte +113 -113
  37. package/src/client/app/ui/SelectChevrons.svelte +27 -15
  38. package/src/client/app/ui/SketchRenderer.svelte +761 -667
  39. package/src/client/app/ui/fields/ButtonInput.svelte +61 -48
  40. package/src/client/app/ui/fields/CheckboxInput.svelte +67 -61
  41. package/src/client/app/ui/fields/ColorInput.svelte +294 -238
  42. package/src/client/app/ui/fields/ImageInput.svelte +123 -121
  43. package/src/client/app/ui/fields/Input.svelte +100 -111
  44. package/src/client/app/ui/fields/ListInput.svelte +96 -96
  45. package/src/client/app/ui/fields/NumberInput.svelte +121 -116
  46. package/src/client/app/ui/fields/ProgressInput.svelte +80 -73
  47. package/src/client/app/ui/fields/Select.svelte +137 -124
  48. package/src/client/app/ui/fields/VectorInput.svelte +86 -82
  49. package/src/client/app/utils/canvas.utils.js +228 -201
  50. package/src/client/app/utils/file.utils.js +38 -34
  51. package/src/client/public/css/global.css +27 -21
  52. package/src/cli/plugins/hot-shader-reload.js +0 -86
@@ -1,106 +1,105 @@
1
1
  <script>
2
- import { onMount } from "svelte";
3
- import Module from "../ui/Module.svelte";
4
- import Field from "../ui/Field.svelte";
5
- import MIDI from "../inputs/MIDI.js";
6
-
7
- export let mID;
8
- export let hasHeader;
9
-
10
- let input, output;
11
- let inputs = [], outputs = [];
12
-
13
- function createDeviceOptions(deviceMap = new Map()) {
14
- let options = [];
15
-
16
- if (deviceMap.size !== 0) {
17
- options.push({ value: "none", label: "No device selected." });
18
- }
19
-
20
- for (let entry of deviceMap) {
21
- let device = entry[1];
22
- const { id, name, manufacturer } = device;
23
-
24
- options.push({ value: id, label: `${manufacturer} ${name} – id: ${id}` });
25
- }
26
-
27
- if (options.length === 0) {
28
- options = [
29
- { value: "none", label: "No device detected."}
30
- ];
31
- }
32
-
33
- return options;
34
- }
35
-
36
- let messages = [];
37
-
38
- $: {
39
- MIDI.selectedInputID = input;
40
- }
41
-
42
- $: {
43
- MIDI.selectedOutputID = output;
44
- }
45
-
46
- onMount(async () => {
47
- await MIDI.request();
48
-
49
- function refresh() {
50
- inputs = createDeviceOptions(MIDI.inputs);
51
- outputs = createDeviceOptions(MIDI.outputs);
52
-
53
- // if a single device is connected, select it by default
54
- input = (inputs.length === 2 ? inputs[1].value : inputs[0].value);
55
- output = (outputs.length === 2 ? outputs[1].value : outputs[0].value);
56
- }
57
-
58
- MIDI.addEventListener("connected", refresh);
59
- MIDI.addEventListener("disconnected", () => {
60
- refresh();
61
- });
62
-
63
- MIDI.addEventListener("message", (event) => {
64
- const { type, note, channel, value } = event;
65
- let date = new Date();
66
- let time = `${date.getHours()}:${String(date.getMinutes()).padStart(2, "0")}:${String(date.getSeconds()).padStart(2, "0")}`;
67
-
68
- let noteLog = ["noteon", "noteoff"].includes(type) ? ` note:${note.name}` : ``;
69
-
70
- messages = [
71
- ...messages,
72
- `${time} ${type} number:${note.number}${noteLog}`,
73
- ]
74
- });
75
-
76
- refresh();
77
- });
78
-
2
+ import { onMount } from 'svelte';
3
+ import Module from '../ui/Module.svelte';
4
+ import Field from '../ui/Field.svelte';
5
+ import MIDI from '../inputs/MIDI.js';
6
+
7
+ export let mID;
8
+ export let hasHeader;
9
+
10
+ let input, output;
11
+ let inputs = [],
12
+ outputs = [];
13
+
14
+ function createDeviceOptions(deviceMap = new Map()) {
15
+ let options = [];
16
+
17
+ if (deviceMap.size !== 0) {
18
+ options.push({ value: 'none', label: 'No device selected.' });
19
+ }
20
+
21
+ for (let entry of deviceMap) {
22
+ let device = entry[1];
23
+ const { id, name, manufacturer } = device;
24
+
25
+ options.push({
26
+ value: id,
27
+ label: `${manufacturer} ${name} id: ${id}`,
28
+ });
29
+ }
30
+
31
+ if (options.length === 0) {
32
+ options = [{ value: 'none', label: 'No device detected.' }];
33
+ }
34
+
35
+ return options;
36
+ }
37
+
38
+ let messages = [];
39
+
40
+ $: {
41
+ MIDI.selectedInputID = input;
42
+ }
43
+
44
+ $: {
45
+ MIDI.selectedOutputID = output;
46
+ }
47
+
48
+ onMount(async () => {
49
+ await MIDI.request();
50
+
51
+ function refresh() {
52
+ inputs = createDeviceOptions(MIDI.inputs);
53
+ outputs = createDeviceOptions(MIDI.outputs);
54
+
55
+ // if a single device is connected, select it by default
56
+ input = inputs.length === 2 ? inputs[1].value : inputs[0].value;
57
+ output = outputs.length === 2 ? outputs[1].value : outputs[0].value;
58
+ }
59
+
60
+ MIDI.addEventListener('connected', refresh);
61
+ MIDI.addEventListener('disconnected', () => {
62
+ refresh();
63
+ });
64
+
65
+ MIDI.addEventListener('message', (event) => {
66
+ const { type, note, channel, value } = event;
67
+ let date = new Date();
68
+ let time = `${date.getHours()}:${String(date.getMinutes()).padStart(
69
+ 2,
70
+ '0',
71
+ )}:${String(date.getSeconds()).padStart(2, '0')}`;
72
+
73
+ let noteLog = ['noteon', 'noteoff'].includes(type)
74
+ ? ` note:${note.name}`
75
+ : ``;
76
+
77
+ messages = [
78
+ ...messages,
79
+ `${time} ${type} number:${note.number}${noteLog}`,
80
+ ];
81
+ });
82
+
83
+ refresh();
84
+ });
79
85
  </script>
80
86
 
81
87
  <Module {mID} {hasHeader} name="MIDI" {...$$props} slug="midi">
82
- <Field
83
- key="inputs"
84
- value={input}
85
- on:change={(event) => input = event.detail}
86
- params={{
87
- options: inputs,
88
- }}
89
- />
90
- <Field
91
- key="outputs"
92
- value={output}
93
- on:change={(event) => output = event.detail}
94
- params={{
95
- options: outputs,
96
- }}
97
- />
98
- <Field
99
- key="messages"
100
- value={messages}
101
- type="list"
102
- params={{
103
- disabled: true
104
- }}
105
- />
88
+ <Field
89
+ key="inputs"
90
+ value={input}
91
+ on:change={(event) => (input = event.detail)}
92
+ params={{
93
+ options: inputs,
94
+ }}
95
+ />
96
+ <Field
97
+ key="outputs"
98
+ value={output}
99
+ on:change={(event) => (output = event.detail)}
100
+ params={{
101
+ options: outputs,
102
+ }}
103
+ />
104
+ <Field key="messages" value={messages} type="list" disabled />
106
105
  </Module>
@@ -1,112 +1,125 @@
1
1
  <script context="module">
2
- import { writable } from "svelte/store";
2
+ import { writable } from 'svelte/store';
3
3
 
4
- export const params = writable([]);
5
-
6
- let ID = 0;
4
+ export const params = writable([]);
7
5
 
6
+ let ID = 0;
8
7
  </script>
9
8
 
10
9
  <script>
11
- import { onMount, onDestroy } from "svelte";
12
- import { props } from "../stores";
13
- import { sketches } from "../stores/sketches.js";
14
- import { monitors } from "../stores/rendering";
15
- import Module from "../ui/Module.svelte";
16
- import Field from "../ui/Field.svelte";
17
- import OutputParams from "../ui/ParamsOutput.svelte";
18
- import ModuleHeaderAction from "../ui/ModuleHeaderAction.svelte";
19
-
20
- export let mID;
21
- export let hasHeader = true;
22
- export let output = true;
23
-
24
- let id = ID++;
25
- let selected = id;
26
- let sketch, sketchKey, sketchProps = {};
27
- let monitor, showOutputParams;
28
-
29
- onMount(() => {
30
- $params = [
31
- ...$params,
32
- {
33
- id,
34
- }
35
- ];
36
- });
37
-
38
- onDestroy(() => {
39
- $params = $params.filter((p) => p.id !== id);
40
- });
41
-
42
- $: options = [
43
- ...$monitors.map((monitor, index) => {
44
- return { value: index, label: `monitor ${index + 1}`}
45
- }),
46
- ...$params.length > 1 ? [{ value: "output", label: "output"}] : [],
47
- ];
48
-
49
- monitors.subscribe((value) => {
50
- monitor = $monitors[Math.min(selected, $monitors.length - 1)];
51
- sketchKey = monitor ? monitor.selected : undefined;
52
- });
53
-
54
- $: {
55
- sketch = $sketches[sketchKey];
56
- sketchProps = $props[sketchKey];
57
- }
58
-
59
- $: showOutputParams = (monitor && monitor.selected === "output") ||
60
- ($params.length === 1) ||
61
- (selected === "output");
62
-
10
+ import { onMount, onDestroy } from 'svelte';
11
+ import { sketches } from '../stores/sketches.js';
12
+ import { monitors } from '../stores/rendering';
13
+ import Module from '../ui/Module.svelte';
14
+ import Field from '../ui/Field.svelte';
15
+ import OutputParams from '../ui/ParamsOutput.svelte';
16
+ import ModuleHeaderAction from '../ui/ModuleHeaderAction.svelte';
17
+ import { updateProp, props } from '../stores/props';
18
+
19
+ export let mID;
20
+ export let hasHeader = true;
21
+ export let output = true;
22
+
23
+ let id = ID++;
24
+ let selected = id;
25
+ let sketch,
26
+ sketchKey,
27
+ sketchProps = {};
28
+ let monitor, showOutputParams;
29
+
30
+ onMount(() => {
31
+ $params = [
32
+ ...$params,
33
+ {
34
+ id,
35
+ },
36
+ ];
37
+ });
38
+
39
+ onDestroy(() => {
40
+ $params = $params.filter((p) => p.id !== id);
41
+ });
42
+
43
+ $: options = [
44
+ ...$monitors.map((monitor, index) => {
45
+ return { value: index, label: `monitor ${index + 1}` };
46
+ }),
47
+ ...($params.length > 1 ? [{ value: 'output', label: 'output' }] : []),
48
+ ];
49
+
50
+ monitors.subscribe((value) => {
51
+ monitor = $monitors[Math.min(selected, $monitors.length - 1)];
52
+ sketchKey = monitor ? monitor.selected : undefined;
53
+ });
54
+
55
+ $: {
56
+ sketch = $sketches[sketchKey];
57
+ sketchProps = $props[sketchKey];
58
+ }
59
+
60
+ $: showOutputParams =
61
+ (monitor && monitor.selected === 'output') ||
62
+ $params.length === 1 ||
63
+ selected === 'output';
63
64
  </script>
64
65
 
65
- <Module {mID} {hasHeader} name={`Parameters`} slug="params" >
66
- <div slot="header-right">
67
- {#if options.length > 1 }
68
- <ModuleHeaderAction
69
- value={selected}
70
- permanent
71
- border
72
- on:change={(event) => selected = event.detail}
73
- options={options}
74
- />
75
- {/if }
76
- </div>
77
- {#if showOutputParams && output }
78
- <OutputParams />
79
- {/if}
80
-
81
- {#if sketch }
82
- {#if typeof props === "object"}
83
- {#if output}
84
- <Field key="framerate" value={isFinite(sketch.fps) ? sketch.fps : 60} params={{disabled: true}}/>
85
- {/if}
86
- {#if sketch.duration && sketch.duration > 0 && output }
87
- <Field key="duration" value={sketch.duration} params={{disabled: true, suffix: "s"}}/>
88
- {/if }
89
- {#each Object.keys(sketchProps) as key, i}
90
- {#if !sketchProps[key].hidden}
91
- <Field
92
- context={sketchKey}
93
- key={key}
94
- value={sketchProps[key].value}
95
- type={sketchProps[key].type}
96
- bind:params={sketchProps[key].params}
97
- on:click={() => {
98
- $props[sketchKey][key].value._refresh = true;
99
- }}
100
- on:change={(event) => {
101
- $props[sketchKey][key].value = event.detail;
102
-
103
- if (typeof $props[sketchKey][key].onChange === 'function') {
104
- $props[sketchKey][key].onChange($props[sketchKey][key]);
105
- }
106
- }}
107
- />
108
- {/if}
109
- {/each}
110
- {/if}
111
- {/if}
66
+ <Module {mID} {hasHeader} name={`Parameters`} slug="params">
67
+ <div slot="header-right">
68
+ {#if options.length > 1}
69
+ <ModuleHeaderAction
70
+ value={selected}
71
+ permanent
72
+ border
73
+ on:change={(event) => (selected = event.detail)}
74
+ {options}
75
+ />
76
+ {/if}
77
+ </div>
78
+ {#if showOutputParams && output}
79
+ <OutputParams />
80
+ {/if}
81
+
82
+ {#if sketch}
83
+ {#if typeof props === 'object'}
84
+ {#if output}
85
+ <Field
86
+ key="framerate"
87
+ value={isFinite(sketch.fps) ? sketch.fps : 60}
88
+ disabled
89
+ />
90
+ {/if}
91
+ {#if sketch.duration && sketch.duration > 0 && output}
92
+ <Field
93
+ key="duration"
94
+ value={sketch.duration}
95
+ params={{ suffix: 's' }}
96
+ disabled
97
+ />
98
+ {/if}
99
+ {#each Object.keys(sketchProps) as key, i (key)}
100
+ {@const sketchProp = sketchProps[key]}
101
+ {@const { hidden, displayName, value, type, disabled } =
102
+ sketchProp}
103
+ {@const isDisabled =
104
+ typeof disabled === 'function' ? disabled() : disabled}
105
+ {#if typeof hidden === 'function' ? !hidden() : !hidden}
106
+ <Field
107
+ context={sketchKey}
108
+ {key}
109
+ {displayName}
110
+ {value}
111
+ {type}
112
+ disabled={isDisabled}
113
+ bind:params={sketchProps[key].params}
114
+ on:click={() => {
115
+ $props[sketchKey][key].value._refresh = true;
116
+ }}
117
+ on:change={(event) => {
118
+ updateProp(sketchKey, key, event.detail);
119
+ }}
120
+ />
121
+ {/if}
122
+ {/each}
123
+ {/if}
124
+ {/if}
112
125
  </Module>
@@ -1,5 +1,5 @@
1
1
  export let onMountPreview = ({ canvas }) => {
2
- return {
3
- context: canvas.getContext("2d"),
4
- }
2
+ return {
3
+ context: canvas.getContext('2d'),
4
+ };
5
5
  };
@@ -1,7 +1,7 @@
1
- import { fragment } from "../lib/gl";
2
- import { client } from "@fragment/client";
3
- import { getShaderPath } from "../utils/glsl.utils";
4
- import { clearError } from "../stores/errors";
1
+ import { fragment } from '../lib/gl';
2
+ import { client } from '@fragment/client';
3
+ import { getShaderPath } from '../utils/glsl.utils';
4
+ import { clearError } from '../stores/errors';
5
5
 
6
6
  let frags = [];
7
7
 
@@ -19,44 +19,51 @@ export let onMountPreview = ({ canvas, id }) => {
19
19
  };
20
20
 
21
21
  export let onResizePreview = ({ id, width, height, pixelRatio }) => {
22
- let { frag } = frags.find(f => f.id === id);
22
+ let { frag } = frags.find((f) => f.id === id);
23
23
 
24
24
  frag.resize({ width, height, pixelRatio });
25
25
  };
26
26
 
27
27
  export let onDestroyPreview = ({ canvas, id }) => {
28
- let fragIndex = frags.findIndex(f => f.id === id);
28
+ let fragIndex = frags.findIndex((f) => f.id === id);
29
29
  let { frag } = frags[fragIndex];
30
-
30
+
31
31
  clearError(frag.gl.__uuid);
32
32
 
33
33
  frag.destroy();
34
34
  frags.splice(fragIndex, 1);
35
35
  };
36
36
 
37
- client.on('shader-update', (data) => {
37
+ client.on('shader-update', (shaderUpdates) => {
38
38
  frags.forEach(({ frag }) => clearError(frag.gl.__uuid));
39
39
 
40
- const { filepath, source } = data;
40
+ shaderUpdates.forEach((shaderUpdate) => {
41
+ const { filepath, source } = shaderUpdate;
41
42
 
42
- const programs = frags.map(({ frag }) => frag.program);
43
+ const programs = frags.map(({ frag }) => frag.program);
43
44
 
44
- programs.forEach((program) => {
45
- const { fragment, vertex } = program;
45
+ programs.forEach((program) => {
46
+ const { fragment, vertex } = program;
46
47
 
47
- const shaders = {
48
- vertexShader: vertex,
49
- fragmentShader: fragment
50
- };
48
+ const shaders = {
49
+ vertexShader: vertex,
50
+ fragmentShader: fragment,
51
+ };
51
52
 
52
- Object.keys(shaders).forEach((key) => {
53
- const shaderPath = getShaderPath(shaders[key]);
53
+ Object.keys(shaders).forEach((key) => {
54
+ const shaderPath = getShaderPath(shaders[key]);
54
55
 
55
- if (shaderPath === filepath) {
56
- console.log(`[fragment] shader update ${shaderPath.replace(__CWD__, "")}`);
57
- program[key] = source;
58
- program.needsUpdate = true;
59
- }
56
+ if (shaderPath === filepath) {
57
+ console.log(
58
+ `[fragment-plugin-hsr] hsr update ${shaderPath.replace(
59
+ __CWD__,
60
+ '',
61
+ )}`,
62
+ );
63
+ program[key] = source;
64
+ program.needsUpdate = true;
65
+ }
66
+ });
60
67
  });
61
68
  });
62
69
  });
@@ -1,4 +1,4 @@
1
- import p5 from "p5";
1
+ import p5 from 'p5';
2
2
 
3
3
  let previews = [];
4
4
 
@@ -6,7 +6,7 @@ export let onMountPreview = ({ id, width, height }) => {
6
6
  const p = new p5((sketch) => {
7
7
  sketch.setup = () => {
8
8
  sketch.createCanvas(width, height);
9
- }
9
+ };
10
10
  });
11
11
 
12
12
  const preview = {
@@ -15,15 +15,15 @@ export let onMountPreview = ({ id, width, height }) => {
15
15
  };
16
16
 
17
17
  previews.push(preview);
18
-
19
- return {
18
+
19
+ return {
20
20
  canvas: p.canvas,
21
21
  p,
22
- };
22
+ };
23
23
  };
24
24
 
25
25
  export let onResizePreview = ({ id, width, height, pixelRatio }) => {
26
- const preview = previews.find(p => p.id === id);
26
+ const preview = previews.find((p) => p.id === id);
27
27
 
28
28
  if (preview) {
29
29
  preview.p.resizeCanvas(width * pixelRatio, height * pixelRatio, false);
@@ -31,9 +31,12 @@ export let onResizePreview = ({ id, width, height, pixelRatio }) => {
31
31
  };
32
32
 
33
33
  export let onDestroyPreview = ({ id }) => {
34
- const preview = previews.find(p => p.id === id);
34
+ const previewIndex = previews.find((p) => p.id === id);
35
+ const preview = previews[previewIndex];
35
36
 
36
37
  if (preview) {
37
38
  preview.p.remove();
38
39
  }
40
+
41
+ previews.splice(previewIndex, 1);
39
42
  };