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,103 +1,115 @@
1
1
  <script>
2
- export let name;
2
+ export let name;
3
3
 
4
- export let collapsed = false;
5
-
6
- function handleClick() {
7
- collapsed = !collapsed;
8
- }
4
+ export let collapsed = false;
9
5
 
6
+ function handleClick() {
7
+ collapsed = !collapsed;
8
+ }
10
9
  </script>
11
10
 
12
- <div class="field-group {collapsed ? "collapsed" : ""}">
13
- <header class="header">
14
- <button class="header__action" on:click={handleClick}>
15
- <svg class="header__icon" width="24" height="24" fill="none" viewBox="0 0 24 24">
16
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M10.75 8.75L14.25 12L10.75 15.25"/>
17
- </svg>
18
- <span class="field-group__name">{name}</span>
19
- </button>
20
- </header>
21
- <div class="content">
22
- <slot></slot>
23
- </div>
11
+ <div class="field-group {collapsed ? 'collapsed' : ''}">
12
+ <header class="header">
13
+ <button class="header__action" on:click={handleClick}>
14
+ <svg
15
+ class="header__icon"
16
+ width="24"
17
+ height="24"
18
+ fill="none"
19
+ viewBox="0 0 24 24"
20
+ >
21
+ <path
22
+ stroke="currentColor"
23
+ stroke-linecap="round"
24
+ stroke-linejoin="round"
25
+ stroke-width="1.5"
26
+ d="M10.75 8.75L14.25 12L10.75 15.25"
27
+ />
28
+ </svg>
29
+ <span class="field-group__name">{name}</span>
30
+ </button>
31
+ </header>
32
+ <div class="content">
33
+ <slot />
34
+ </div>
24
35
  </div>
25
36
 
26
37
  <style>
27
- .field-group {
28
- position: relative;
29
-
30
- display: grid;
31
- width: 100%;
32
-
33
- }
34
-
35
- .field-group:after {
36
- content: "";
37
-
38
- position: absolute;
39
- left: 0;
40
- bottom: 0px;
41
-
42
- width: 12px;
43
- height: 1px;
44
-
45
- background-color: #323233;
46
- }
47
-
48
- .header {
49
- padding: 3px 6px;
50
- border-bottom: 1px solid #323233;
51
- }
52
-
53
- .header__action {
54
- display: flex;
55
- align-items: center;
56
- width: 100%;
57
- text-align: left;
58
-
59
- background: transparent;
60
- cursor: pointer;
61
- }
62
-
63
- .header__icon {
64
- padding-bottom: 1px;
65
-
66
- color: #f0f0f0;
67
- transform: rotate(90deg);
68
- opacity: 0.5;
69
- transition: opacity 0.1s ease;
70
- }
71
-
72
- .header__action:hover .header__icon {
73
- opacity: 1;
74
- }
75
-
76
- .field-group.collapsed .header__icon {
77
- transform: rotate(0deg);
78
- }
79
-
80
- .field-group__name {
81
- color: #f0f0f0;
82
-
83
- font-size: 11px;
84
- font-weight: 700;
85
- /* text-transform: uppercase; */
86
-
87
- opacity: 0.75;
88
- transition: opacity 0.1s ease;
89
- }
90
-
91
- .header__action:hover .field-group__name {
92
- opacity: 1;
93
- }
94
-
95
- .content {
96
- margin-left: 12px;
97
- border-left: 1px solid #323233;
98
- }
99
-
100
- .field-group.collapsed .content {
101
- display: none;
102
- }
38
+ .field-group {
39
+ position: relative;
40
+
41
+ display: grid;
42
+ width: 100%;
43
+ }
44
+
45
+ .field-group:after {
46
+ content: '';
47
+
48
+ position: absolute;
49
+ left: 0;
50
+ bottom: 0px;
51
+
52
+ width: 12px;
53
+ height: 1px;
54
+
55
+ background-color: #323233;
56
+ }
57
+
58
+ .header {
59
+ padding: 3px 6px;
60
+ border-bottom: 1px solid #323233;
61
+ }
62
+
63
+ .header__action {
64
+ display: flex;
65
+ align-items: center;
66
+ width: 100%;
67
+ text-align: left;
68
+
69
+ background: transparent;
70
+ cursor: pointer;
71
+ outline: 0;
72
+ }
73
+
74
+ .header__icon {
75
+ padding-bottom: 1px;
76
+
77
+ color: #f0f0f0;
78
+ transform: rotate(90deg);
79
+ opacity: 0.5;
80
+ transition: opacity 0.1s ease;
81
+ }
82
+
83
+ .header__action:hover .header__icon {
84
+ opacity: 1;
85
+ }
86
+
87
+ .field-group.collapsed .header__icon {
88
+ transform: rotate(0deg);
89
+ }
90
+
91
+ .field-group__name {
92
+ color: #f0f0f0;
93
+
94
+ font-size: 11px;
95
+ font-weight: 700;
96
+ /* text-transform: uppercase; */
97
+
98
+ opacity: 0.75;
99
+ transition: opacity 0.1s ease;
100
+ }
101
+
102
+ .header__action:hover .field-group__name,
103
+ .header__action:focus-visible .field-group__name {
104
+ opacity: 1;
105
+ }
106
+
107
+ .content {
108
+ margin-left: 12px;
109
+ border-left: 1px solid #323233;
110
+ }
111
+
112
+ .field-group.collapsed .content {
113
+ display: none;
114
+ }
103
115
  </style>
@@ -1,123 +1,132 @@
1
1
  <script>
2
- export let visible = true;
3
- export let secondary = false;
4
- export let interactive = false;
5
- export let label = "";
6
- export let name = "";
2
+ export let key;
3
+ export let visible = true;
4
+ export let secondary = false;
5
+ export let interactive = false;
6
+ export let displayName = key;
7
+ export let disabled = false;
7
8
  </script>
8
9
 
9
- <div class="field__section" class:visible={visible} class:secondary={secondary} class:no-label={label === null}>
10
- <div class="field__infos">
11
- {#if label !== null}
12
- {#if interactive}
13
- <button class="field__label" on:click>{label}</button>
14
- {:else}
15
- <span class="field__label">{label}</span>
16
- {/if}
17
- {/if}
18
- <slot name="infos"></slot>
19
- </div>
20
- <div class="field__input">
21
- <slot></slot>
22
- </div>
10
+ <div
11
+ class="field__section"
12
+ class:visible
13
+ class:secondary
14
+ class:nameless={displayName === null}
15
+ >
16
+ <div class="field__infos">
17
+ {#if displayName !== null}
18
+ {#if interactive}
19
+ <button class="field__label" {disabled} on:click
20
+ >{displayName}</button
21
+ >
22
+ {:else}
23
+ <span class="field__label">{displayName}</span>
24
+ {/if}
25
+ {/if}
26
+ <slot name="infos" />
27
+ </div>
28
+ <div class="field__input">
29
+ <slot />
30
+ </div>
23
31
  </div>
24
32
 
25
33
  <style>
26
- .field__section {
27
- position: relative;
28
-
29
- display: grid;
30
- grid-template-columns: 0.5fr 1fr;
31
- column-gap: 10px;
32
- }
33
-
34
- .field__section.no-label {
35
- grid-template-columns: 1fr;
36
- }
37
-
38
- .field__section:hover .field__label, .field__section:focus-within .field__label {
39
- opacity: 1;
40
- }
41
-
42
- .field__section:not(.visible) {
43
- display: none;
44
- }
45
-
46
- .field__section.secondary {
47
- --margin: 15px;
48
-
49
- grid-template-columns: 0.25fr 1fr;
50
- margin-top: var(--margin);
51
- }
52
-
53
- .field__section.secondary:before {
54
- content: '';
55
-
56
- position: absolute;
57
- left: 10px;
58
- top: calc(var(--margin) * -1);
59
-
60
- width: 1px;
61
- height: var(--margin);
62
-
63
- background-color: var(--color-spacing);
64
- }
65
-
66
- .field__infos {
67
- position: relative;
68
-
69
- display: flex;
70
- align-items: center;
71
- justify-content: space-between;
72
-
73
- color: var(--color-text);
74
- }
75
-
76
- .field__label {
77
- color: inherit;
78
- font-size: var(--font-size-input);
79
- user-select: none;
80
-
81
- opacity: 0.6;
82
- background-color: transparent;
83
- transition: opacity 0.1s ease;
84
- }
85
-
86
- button.field__label {
87
- cursor: pointer;
88
- }
89
-
90
- .field__label:focus-visible {
91
- outline: 0;
92
- box-shadow: 0 0 0 2px var(--color-text);
93
- border-radius: 2px;
94
- }
95
-
96
- .field__section.secondary {
97
- grid-template-columns: 1fr;
98
- }
99
-
100
- .field__section.secondary .field__infos {
101
- display: none;
102
- }
103
-
104
- .field__section.secondary .field__label {
105
- position: relative;
106
-
107
- padding-left: 5px;
108
-
109
- background-color: #242425;
110
- }
111
-
112
- .field__input {
113
- display: flex;
114
- flex-direction: column;
115
- justify-content: center;
116
- align-items: flex-start;
117
- }
118
-
119
- .field__section.secondary .field__input {
120
- padding: var(--column-gap);
121
- border: 1px solid var(--color-spacing);
122
- }
34
+ .field__section {
35
+ position: relative;
36
+
37
+ display: grid;
38
+ grid-template-columns: 0.5fr 1fr;
39
+ column-gap: 10px;
40
+ }
41
+
42
+ .field__section.nameless {
43
+ grid-template-columns: 1fr;
44
+ }
45
+
46
+ .field__section:hover .field__label,
47
+ .field__section:focus-within .field__label {
48
+ opacity: 1;
49
+ }
50
+
51
+ .field__section:not(.visible) {
52
+ display: none;
53
+ }
54
+
55
+ .field__section.secondary {
56
+ --margin: 15px;
57
+
58
+ grid-template-columns: 0.25fr 1fr;
59
+ margin-top: var(--margin);
60
+ }
61
+
62
+ .field__section.secondary:before {
63
+ content: '';
64
+
65
+ position: absolute;
66
+ left: 10px;
67
+ top: calc(var(--margin) * -1);
68
+
69
+ width: 1px;
70
+ height: var(--margin);
71
+
72
+ background-color: var(--color-spacing);
73
+ }
74
+
75
+ .field__infos {
76
+ position: relative;
77
+
78
+ display: flex;
79
+ align-items: center;
80
+ justify-content: space-between;
81
+
82
+ color: var(--color-text);
83
+ }
84
+
85
+ .field__label {
86
+ color: inherit;
87
+ font-size: var(--font-size-input);
88
+ user-select: none;
89
+
90
+ opacity: 0.6;
91
+ background-color: transparent;
92
+ transition: opacity 0.1s ease;
93
+ }
94
+
95
+ button.field__label {
96
+ cursor: pointer;
97
+ }
98
+
99
+ .field__label:focus-visible {
100
+ outline: 0;
101
+ box-shadow: 0 0 0 2px var(--color-text);
102
+ border-radius: 2px;
103
+ }
104
+
105
+ .field__section.secondary {
106
+ grid-template-columns: 1fr;
107
+ }
108
+
109
+ .field__section.secondary .field__infos {
110
+ display: none;
111
+ }
112
+
113
+ .field__section.secondary .field__label {
114
+ position: relative;
115
+
116
+ padding-left: 5px;
117
+
118
+ background-color: #242425;
119
+ }
120
+
121
+ .field__input {
122
+ display: flex;
123
+ flex-direction: column;
124
+ justify-content: center;
125
+ align-items: flex-start;
126
+ }
127
+
128
+ .field__section.secondary .field__input {
129
+ padding: var(--column-gap);
130
+ border: 1px solid var(--color-spacing);
131
+ }
123
132
  </style>
@@ -1,109 +1,110 @@
1
1
  <script>
2
- import { multisampling, threshold, transition } from "../stores/multisampling.js";
3
- import { monitors } from "../stores/rendering.js";
4
- import { transitions } from "../transitions/index.js";
5
- import Field from "./Field.svelte";
6
- import FieldGroup from "./FieldGroup.svelte";
2
+ import {
3
+ multisampling,
4
+ threshold,
5
+ transition,
6
+ } from '../stores/multisampling.js';
7
+ import { monitors } from '../stores/rendering.js';
8
+ import { transitions } from '../transitions/index.js';
9
+ import Field from './Field.svelte';
10
+ import FieldGroup from './FieldGroup.svelte';
7
11
 
8
- let monitorDisabled = false;
9
- let sampler0 = -1;
10
- let sampler1 = -1;
12
+ let monitorDisabled = false;
13
+ let sampler0 = -1;
14
+ let sampler1 = -1;
11
15
 
12
- const transitionOptions = Object.keys(transitions).map((key) => {
13
- const transition = transitions[key];
14
- const label = transition.name ? transition.name : key;
15
- return { value: key, label };
16
- });
17
-
18
- if (!$transition) {
19
- $transition = transitionOptions[0].value;
20
- }
16
+ const transitionOptions = Object.keys(transitions).map((key) => {
17
+ const transition = transitions[key];
18
+ const label = transition.name ? transition.name : key;
19
+ return { value: key, label };
20
+ });
21
21
 
22
- $: samplerOptions = $monitors
23
- .map((monitor, index) => ({ label: `monitor ${index+1}`, value: monitor.id })) // set index first so it matches <Monitor>
24
- .filter((option) => {
25
- const monitor = $monitors.find((m) => m.id === option.value);
22
+ if (!$transition) {
23
+ $transition = transitionOptions[0].value;
24
+ }
26
25
 
27
- return monitor.selected !== "output";
28
- });
29
- $: {
30
- monitorDisabled = samplerOptions.length === 0;
26
+ $: samplerOptions = $monitors
27
+ .map((monitor, index) => ({
28
+ label: `monitor ${index + 1}`,
29
+ value: monitor.id,
30
+ })) // set index first so it matches <Monitor>
31
+ .filter((option) => {
32
+ const monitor = $monitors.find((m) => m.id === option.value);
31
33
 
32
- if (samplerOptions.length === 0) {
33
- samplerOptions = [
34
- { label: `No monitor available.`, value: -1 }
35
- ];
36
- sampler0 = samplerOptions[0].value;
37
- sampler1 = samplerOptions[0].value;
38
- } else {
39
- if (sampler0 < 0) {
40
- sampler0 = samplerOptions[0].value;
41
- }
34
+ return monitor.selected !== 'output';
35
+ });
36
+ $: {
37
+ monitorDisabled = samplerOptions.length === 0;
42
38
 
43
- if (sampler1 < 0) {
44
- sampler1 = samplerOptions.length > 1 ? samplerOptions[1].value : -1;
45
- }
46
- }
39
+ if (samplerOptions.length === 0) {
40
+ samplerOptions = [{ label: `No monitor available.`, value: -1 }];
41
+ sampler0 = samplerOptions[0].value;
42
+ sampler1 = samplerOptions[0].value;
43
+ } else {
44
+ if (sampler0 < 0) {
45
+ sampler0 = samplerOptions[0].value;
46
+ }
47
47
 
48
- $multisampling = [sampler0, sampler1];
49
- }
48
+ if (sampler1 < 0) {
49
+ sampler1 =
50
+ samplerOptions.length > 1 ? samplerOptions[1].value : -1;
51
+ }
52
+ }
50
53
 
54
+ $multisampling = [sampler0, sampler1];
55
+ }
51
56
  </script>
57
+
52
58
  <FieldGroup name="monitors">
53
- <Field
54
- key="sampler0"
55
- value={sampler0}
56
- on:change={event => {
57
- sampler0 = Number(event.detail);
58
- }}
59
- params={
60
- {
61
- options: samplerOptions,
62
- disabled: monitorDisabled
63
- }
64
- }
65
- />
66
- <Field
67
- key="sampler1"
68
- value={sampler1}
69
- on:change={event => {
70
- sampler1 = Number(event.detail);
71
- }}
72
- params={
73
- {
74
- options: samplerOptions,
75
- disabled: monitorDisabled
76
- }
77
- }
78
- />
59
+ <Field
60
+ key="sampler0"
61
+ value={sampler0}
62
+ on:change={(event) => {
63
+ sampler0 = Number(event.detail);
64
+ }}
65
+ params={{
66
+ options: samplerOptions,
67
+ }}
68
+ disabled={monitorDisabled}
69
+ />
70
+ <Field
71
+ key="sampler1"
72
+ value={sampler1}
73
+ on:change={(event) => {
74
+ sampler1 = Number(event.detail);
75
+ }}
76
+ params={{
77
+ options: samplerOptions,
78
+ }}
79
+ disabled={monitorDisabled}
80
+ />
79
81
  </FieldGroup>
80
82
  <FieldGroup name="transition">
81
- <Field
82
- key="type"
83
- value={$transition}
84
- on:change={(event) => {
85
- $transition = event.detail;
86
- }}
87
- params={{
88
- options: transitionOptions,
89
- }}
90
- />
91
- <Field
92
- key="threshold"
93
- value={$threshold}
94
- on:change={(event) => $threshold = event.detail }
95
- params={{
96
- step: 0.01,
97
- min: 0,
98
- max: 1,
99
- }}
100
- />
101
- <Field
102
- key="switch"
103
- value={() => $threshold = 1 - Math.round($threshold)}
104
- params={{
105
- label: `switch ${Math.round($threshold) > 0 ? `<` : `>`}`
106
- }}
107
- />
108
-
83
+ <Field
84
+ key="type"
85
+ value={$transition}
86
+ on:change={(event) => {
87
+ $transition = event.detail;
88
+ }}
89
+ params={{
90
+ options: transitionOptions,
91
+ }}
92
+ />
93
+ <Field
94
+ key="threshold"
95
+ value={$threshold}
96
+ on:change={(event) => ($threshold = event.detail)}
97
+ params={{
98
+ step: 0.01,
99
+ min: 0,
100
+ max: 1,
101
+ }}
102
+ />
103
+ <Field
104
+ key="switch"
105
+ value={() => ($threshold = 1 - Math.round($threshold))}
106
+ params={{
107
+ label: `switch ${Math.round($threshold) > 0 ? `<` : `>`}`,
108
+ }}
109
+ />
109
110
  </FieldGroup>