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,17 +1,16 @@
1
- import Input from "./Input";
1
+ import Input from './Input';
2
2
 
3
3
  const commands = {
4
- 0x8: "noteoff",
5
- 0x9: "noteon",
6
- 0xB: "controlchange",
4
+ 0x8: 'noteoff',
5
+ 0x9: 'noteon',
6
+ 0xb: 'controlchange',
7
7
  };
8
8
 
9
- const notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
9
+ const notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
10
10
 
11
- const LOCAL_STORAGE_KEY = "midi.requested";
11
+ const LOCAL_STORAGE_KEY = 'midi.requested';
12
12
 
13
13
  class MIDI extends Input {
14
-
15
14
  constructor() {
16
15
  super();
17
16
 
@@ -42,25 +41,25 @@ class MIDI extends Input {
42
41
 
43
42
  if (this.inputs.size === 1) {
44
43
  const [entry] = this.inputs.values();
45
- const { id } = entry;
44
+ const { id } = entry;
46
45
 
47
46
  this.selectedInputID = id;
48
47
  }
49
48
 
50
49
  if (this.outputs.size === 1) {
51
50
  const [entry] = this.outputs.values();
52
- const { id } = entry;
51
+ const { id } = entry;
53
52
 
54
53
  this.selectedOutputID = id;
55
54
  }
56
55
  }
57
56
 
58
57
  attachListeners() {
59
- this.inputs.forEach(entry => {
58
+ this.inputs.forEach((entry) => {
60
59
  entry.onmidimessage = (event) => {
61
60
  this.onMessage(event);
62
61
  };
63
- })
62
+ });
64
63
  }
65
64
 
66
65
  addEventListener(eventName, fn) {
@@ -76,14 +75,14 @@ class MIDI extends Input {
76
75
  let type = commands[command];
77
76
 
78
77
  let channel = (event.data[0] & 0xf) + 1;
79
- let data1 = event.data[1]
78
+ let data1 = event.data[1];
80
79
  let data2 = event.data[2];
81
80
 
82
81
  let note = {
83
82
  number: data1,
84
83
  name: notes[data1 % 12],
85
84
  };
86
-
85
+
87
86
  let velocity = data2 / 127;
88
87
  let rawVelocity = data2;
89
88
 
@@ -117,7 +116,7 @@ class MIDI extends Input {
117
116
  if (this.listeners.has(eventName)) {
118
117
  const listeners = this.listeners.get(eventName);
119
118
 
120
- listeners.forEach(listener => listener(data));
119
+ listeners.forEach((listener) => listener(data));
121
120
  }
122
121
  }
123
122
 
@@ -135,7 +134,7 @@ class MIDI extends Input {
135
134
  this.requesting = false;
136
135
  this.start();
137
136
  }
138
- } catch(error) {
137
+ } catch (error) {
139
138
  this.handleError(error);
140
139
  }
141
140
  }
@@ -1,15 +1,17 @@
1
1
  let noop = () => {};
2
2
 
3
3
  class CanvasRecorder {
4
-
5
- constructor(canvas, {
6
- duration = Infinity,
7
- framerate = 25,
8
- quality = 100,
9
- onStart = noop,
10
- onTick = noop,
11
- onComplete = noop,
12
- }) {
4
+ constructor(
5
+ canvas,
6
+ {
7
+ duration = Infinity,
8
+ framerate = 25,
9
+ quality = 100,
10
+ onStart = noop,
11
+ onTick = noop,
12
+ onComplete = noop,
13
+ },
14
+ ) {
13
15
  this.canvas = canvas;
14
16
  this.framerate = framerate;
15
17
  this.duration = duration;
@@ -19,10 +21,12 @@ class CanvasRecorder {
19
21
  this.onComplete = onComplete;
20
22
 
21
23
  this.time = 0;
22
- this.deltaTime = (1000 / this.framerate);
24
+ this.deltaTime = 1000 / this.framerate;
23
25
 
24
- this.frameDuration = (1000 / this.framerate);
25
- this.frameTotal = isFinite(duration) ? this.duration * this.framerate : Infinity;
26
+ this.frameDuration = 1000 / this.framerate;
27
+ this.frameTotal = isFinite(duration)
28
+ ? this.duration * this.framerate
29
+ : Infinity;
26
30
  this.started = false;
27
31
  this.stopped = false;
28
32
  }
@@ -39,7 +43,15 @@ class CanvasRecorder {
39
43
  return;
40
44
  }
41
45
 
42
- console.log(`CanvasRecorder - start rendering ${this.frameTotal} frames at ${this.framerate}fps for ${this.duration}s.`);
46
+ if (isFinite(this.frameTotal)) {
47
+ console.log(
48
+ `CanvasRecorder - start rendering ${this.frameTotal} frames at ${this.framerate}fps for ${this.duration}s.`,
49
+ );
50
+ } else {
51
+ console.log(
52
+ `CanvasRecorder - start rendering at ${this.framerate}fps.`,
53
+ );
54
+ }
43
55
 
44
56
  this.frameCount = 0;
45
57
  this.started = true;
@@ -61,16 +73,24 @@ class CanvasRecorder {
61
73
  frameCount: this.frameCount,
62
74
  });
63
75
 
64
- if (this.started && !this.stopped && (!isFinite(this.frameTotal) || (isFinite(this.frameTotal) && this.frameCount < this.frameTotal - 1))) {
76
+ if (
77
+ this.started &&
78
+ !this.stopped &&
79
+ (!isFinite(this.frameTotal) ||
80
+ (isFinite(this.frameTotal) &&
81
+ this.frameCount < this.frameTotal - 1))
82
+ ) {
65
83
  this.time += this.deltaTime;
66
- this.frameCount++;
67
- requestAnimationFrame(() => {
68
- this._tick()
84
+ this.frameCount++;
85
+ requestAnimationFrame(() => {
86
+ this._tick();
69
87
  });
70
- } else {
71
- console.log(`CanvasRecorder - compiling ${this.frameCount + 1} frames...`);
72
- this.end();
73
- }
88
+ } else {
89
+ console.log(
90
+ `CanvasRecorder - compiling ${this.frameCount + 1} frames...`,
91
+ );
92
+ this.end();
93
+ }
74
94
  }
75
95
 
76
96
  tick() {}
@@ -1,160 +1,148 @@
1
1
  class Renderer {
2
- constructor({
3
- canvas = document.createElement("canvas"),
4
- antialias = false,
5
- alpha = true,
6
- depth = false,
7
- stencil = false,
8
- premultipliedAlpha = false,
9
- pixelRatio = window.devicePixelRatio,
10
- webgl = 2,
11
- }) {
12
- let gl;
13
- let attributes = {
14
- depth,
15
- stencil,
16
- antialias,
17
- alpha,
18
- premultipliedAlpha,
19
- preserveDrawingBuffer: true,
20
- };
21
-
22
- this.canvas = canvas;
23
-
24
- if (webgl === 2) gl = canvas.getContext("webgl2", attributes);
25
- if (!gl) {
26
- gl =
27
- canvas.getContext("webgl", attributes) ||
28
- canvas.getContext("experimental-webgl", attributes);
29
- }
30
2
 
31
- this.gl = gl;
32
-
33
- this.state = {
34
- activeTextureUnit: 0,
35
- textureUnits: [],
36
- flipY: false,
37
- viewport: { width: 0, height: 0 },
38
- pixelRatio,
39
- width: 0,
40
- height: 0,
41
- };
42
- this.gl.state = this.state;
43
- }
44
-
45
- render({
46
- geometry,
47
- program,
48
- primitiveType = this.gl.TRIANGLES,
49
- offset = 0,
50
- count = 3,
51
- }) {
52
- if (program.needsUpdate) {
53
- program.compile();
54
- }
3
+ constructor({
4
+ canvas = document.createElement('canvas'),
5
+ antialias = false,
6
+ alpha = true,
7
+ depth = false,
8
+ stencil = false,
9
+ premultipliedAlpha = false,
10
+ pixelRatio = window.devicePixelRatio,
11
+ webgl = 2,
12
+ }) {
13
+ let gl;
14
+ let attributes = {
15
+ depth,
16
+ stencil,
17
+ antialias,
18
+ alpha,
19
+ premultipliedAlpha,
20
+ preserveDrawingBuffer: true
21
+ };
22
+
23
+ this.canvas = canvas;
24
+
25
+ if (webgl === 2) gl = canvas.getContext('webgl2', attributes);
26
+ if (!gl) {
27
+ gl = canvas.getContext('webgl', attributes) || canvas.getContext('experimental-webgl', attributes);
28
+ }
29
+
30
+ this.gl = gl;
31
+
32
+ this.state = {
33
+ activeTextureUnit: 0,
34
+ textureUnits: [],
35
+ flipY: false,
36
+ viewport: { width: 0, height: 0 },
37
+ pixelRatio,
38
+ width: 0,
39
+ height: 0,
40
+ };
41
+ this.gl.state = this.state;
42
+ }
43
+
44
+ render({
45
+ geometry,
46
+ program,
47
+ primitiveType = this.gl.TRIANGLES,
48
+ offset = 0,
49
+ count = 3
50
+ }) {
51
+ if (program.needsUpdate) {
52
+ program.compile();
53
+ }
55
54
 
56
- this.gl.clear(this.gl.COLOR_BUFFER_BIT);
57
-
58
- this.gl.useProgram(program._program);
59
-
60
- for (let attributeName in program.attributesLocations) {
61
- let location = program.attributesLocations[attributeName];
62
- let buffer = geometry.buffers[attributeName];
63
-
64
- this.gl.enableVertexAttribArray(location);
65
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
66
-
67
- const size = 2; // 2 components per iteration
68
- const type = this.gl.FLOAT; // the data is 32bit floats
69
- const normalize = false; // don't normalize the data
70
- const stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
71
- const bufferOffset = 0; // start at the beginning of the buffer
72
- this.gl.vertexAttribPointer(
73
- location,
74
- size,
75
- type,
76
- normalize,
77
- stride,
78
- bufferOffset
79
- );
80
- }
55
+ this.gl.clear(this.gl.COLOR_BUFFER_BIT);
56
+
57
+ this.gl.useProgram(program._program);
58
+
59
+ for (let attributeName in program.attributesLocations) {
60
+ let location = program.attributesLocations[attributeName];
61
+ let buffer = geometry.buffers[attributeName];
62
+
63
+ this.gl.enableVertexAttribArray(location);
64
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
65
+
66
+ const size = 2; // 2 components per iteration
67
+ const type = this.gl.FLOAT; // the data is 32bit floats
68
+ const normalize = false; // don't normalize the data
69
+ const stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
70
+ const bufferOffset = 0; // start at the beginning of the buffer
71
+ this.gl.vertexAttribPointer(
72
+ location,
73
+ size,
74
+ type,
75
+ normalize,
76
+ stride,
77
+ bufferOffset
78
+ );
79
+ }
81
80
 
82
- let textureUnit = -1;
83
-
84
- for (let uniformName in program.uniforms) {
85
- let location = program.uniformsLocations[uniformName];
86
- if (location) {
87
- let uniform = program.uniforms[uniformName];
88
-
89
- if (uniform.type === "float") {
90
- this.gl.uniform1f(location, uniform.value);
91
- } else if (uniform.type === "vec2") {
92
- this.gl.uniform2f(location, uniform.value[0], uniform.value[1]);
93
- } else if (uniform.type === "vec3") {
94
- this.gl.uniform3f(
95
- location,
96
- uniform.value[0],
97
- uniform.value[1],
98
- uniform.value[2]
99
- );
100
- } else if (uniform.type === "vec4") {
101
- this.gl.uniform4f(
102
- location,
103
- uniform.value[0],
104
- uniform.value[1],
105
- uniform.value[2],
106
- uniform.value[3]
107
- );
108
- } else if (uniform.type === "sampler2D") {
109
- if (uniform.value) {
110
- textureUnit = textureUnit + 1;
111
- uniform.value.update(textureUnit);
112
-
113
- this.gl.uniform1i(location, textureUnit);
114
- }
81
+ let textureUnit = -1;
82
+
83
+ for (let uniformName in program.uniforms) {
84
+ let location = program.uniformsLocations[uniformName];
85
+ if (location) {
86
+ let uniform = program.uniforms[uniformName];
87
+
88
+ if (uniform.type === 'float') {
89
+ this.gl.uniform1f(location, uniform.value);
90
+ } else if (uniform.type === 'vec2') {
91
+ this.gl.uniform2f(location, uniform.value[0], uniform.value[1]);
92
+ } else if (uniform.type === 'vec3') {
93
+ this.gl.uniform3f(location, uniform.value[0], uniform.value[1], uniform.value[2]);
94
+ } else if (uniform.type === 'vec4') {
95
+ this.gl.uniform4f(location, uniform.value[0], uniform.value[1], uniform.value[2], uniform.value[3]);
96
+ } else if (uniform.type === 'sampler2D') {
97
+ if (uniform.value) {
98
+ textureUnit = textureUnit + 1;
99
+ uniform.value.update(textureUnit);
100
+
101
+ this.gl.uniform1i(location, textureUnit);
102
+ }
103
+ }
104
+ }
115
105
  }
116
- }
117
- }
118
106
 
119
- this.gl.drawArrays(primitiveType, offset, count);
120
- }
107
+ this.gl.drawArrays(primitiveType, offset, count);
108
+ }
121
109
 
122
- setPixelRatio(pixelRatio = this.state.pixelRatio) {
123
- if (this.state.pixelRatio !== pixelRatio) {
124
- this.state.pixelRatio = pixelRatio;
125
- this.setSize();
110
+ setPixelRatio(pixelRatio = this.state.pixelRatio) {
111
+ if (this.state.pixelRatio !== pixelRatio) {
112
+ this.state.pixelRatio = pixelRatio;
113
+ this.setSize();
114
+ }
126
115
  }
127
- }
128
116
 
129
- setSize({ width = this.state.width, height = this.state.height } = {}) {
130
- this.state.width = width;
131
- this.state.height = height;
117
+ setSize({ width = this.state.width, height = this.state.height } = {}) {
118
+ this.state.width = width;
119
+ this.state.height = height;
132
120
 
133
- this.canvas.width = this.state.width * this.state.pixelRatio;
134
- this.canvas.height = this.state.height * this.state.pixelRatio;
121
+ this.canvas.width = this.state.width * this.state.pixelRatio;
122
+ this.canvas.height = this.state.height * this.state.pixelRatio;
135
123
 
136
- this.setViewport();
137
- }
124
+ this.setViewport();
125
+ }
138
126
 
139
- setViewport({ width = this.state.width, height = this.state.height } = {}) {
140
- let w = Math.floor(width * this.state.pixelRatio);
141
- let h = Math.floor(height * this.state.pixelRatio);
127
+ setViewport({ width = this.state.width, height = this.state.height } = {}) {
128
+ let w = Math.floor(width * this.state.pixelRatio);
129
+ let h = Math.floor(height * this.state.pixelRatio);
142
130
 
143
- if (this.state.viewport.width !== w || this.state.viewport.height !== h) {
144
- this.gl.viewport(0, 0, w, h);
131
+ if (this.state.viewport.width !== w || this.state.viewport.height !== h) {
132
+ this.gl.viewport(0, 0, w, h);
145
133
 
146
- this.state.viewport.width = w;
147
- this.state.viewport.height = h;
134
+ this.state.viewport.width = w;
135
+ this.state.viewport.height = h;
136
+ }
148
137
  }
149
- }
150
138
 
151
- destroy() {
152
- let extension = this.gl.getExtension("WEBGL_lose_context");
139
+ destroy() {
140
+ let extension = this.gl.getExtension('WEBGL_lose_context');
153
141
 
154
- if (extension) {
155
- extension.loseContext();
142
+ if (extension) {
143
+ extension.loseContext();
144
+ }
156
145
  }
157
- }
158
146
  }
159
147
 
160
148
  export default Renderer;
@@ -1,26 +1,30 @@
1
1
  <script>
2
- import Module from "../ui/Module.svelte";
3
- import Field from "../ui/Field.svelte";
4
- import FieldGroup from "../ui/FieldGroup.svelte";
5
- import { exports } from "../stores";
6
- import { recording, capturing, IMAGE_ENCODINGS, VIDEO_FORMATS } from "../stores/exports";
2
+ import Module from '../ui/Module.svelte';
3
+ import Field from '../ui/Field.svelte';
4
+ import FieldGroup from '../ui/FieldGroup.svelte';
5
+ import { exports } from '../stores';
6
+ import {
7
+ recording,
8
+ capturing,
9
+ IMAGE_ENCODINGS,
10
+ VIDEO_FORMATS,
11
+ } from '../stores/exports';
7
12
 
8
- export let mID;
9
- export let hasHeader = true;
13
+ export let mID;
14
+ export let hasHeader = true;
10
15
 
11
- const LABEL_RECORD = "start";
12
- const LABEL_RECORDING = "stop";
16
+ const LABEL_RECORD = 'start';
17
+ const LABEL_RECORDING = 'stop';
13
18
 
14
- function record () {
15
- $recording = !$recording;
16
- }
19
+ function record() {
20
+ $recording = !$recording;
21
+ }
17
22
 
18
- function capture() {
19
- $capturing = !$capturing;
20
- }
21
-
22
- $: recordLabel = $recording ? LABEL_RECORDING : LABEL_RECORD;
23
+ function capture() {
24
+ $capturing = !$capturing;
25
+ }
23
26
 
27
+ $: recordLabel = $recording ? LABEL_RECORDING : LABEL_RECORD;
24
28
  </script>
25
29
 
26
30
  <Module {mID} {hasHeader} name="exports">
@@ -29,72 +33,87 @@ $: recordLabel = $recording ? LABEL_RECORDING : LABEL_RECORD;
29
33
  key="encoding"
30
34
  value={$exports.imageEncoding}
31
35
  params={{ options: IMAGE_ENCODINGS }}
32
- on:change={((e) => {
36
+ on:change={(e) => {
33
37
  $exports.imageEncoding = e.detail;
34
- })}
38
+ }}
35
39
  />
36
40
  <Field
37
41
  key="quality"
38
42
  value={$exports.imageQuality}
39
- params={{ min: 1, max: 100, suffix: "%", triggerable: false }}
40
- on:change={((e) => {
43
+ params={{ min: 1, max: 100, suffix: '%', triggerable: false }}
44
+ on:change={(e) => {
41
45
  $exports.imageQuality = e.detail;
42
- })}
46
+ }}
43
47
  />
44
48
  <Field
45
49
  key="pixelsPerInch"
46
50
  value={$exports.pixelsPerInch}
47
51
  params={{ step: 1 }}
48
- on:change={((e) => {
52
+ on:change={(e) => {
49
53
  $exports.pixelsPerInch = e.detail;
50
- })}
54
+ }}
55
+ />
56
+ <Field
57
+ key="count"
58
+ value={$exports.imageCount || 1}
59
+ params={{ step: 1 }}
60
+ on:change={(e) => {
61
+ console.log(e.detail, $exports.imageCount);
62
+ $exports.imageCount = e.detail;
63
+ }}
51
64
  />
52
65
  <Field
53
66
  key="screenshot"
54
67
  value={capture}
55
- params={{ label: "capture", triggerable: false }}
68
+ params={{ label: 'capture', triggerable: false }}
56
69
  />
57
70
  </FieldGroup>
58
71
  <FieldGroup name="video">
59
72
  <Field
60
73
  key="framerate"
61
74
  value={$exports.framerate}
62
- on:change={((e) => {
75
+ on:change={(e) => {
63
76
  $exports.framerate = e.detail;
64
- })}
77
+ }}
65
78
  />
66
79
  <Field
67
80
  key="format"
68
81
  value={$exports.videoFormat}
69
82
  params={{ options: Object.values(VIDEO_FORMATS) }}
70
- on:change={((e) => {
83
+ on:change={(e) => {
71
84
  $exports.videoFormat = e.detail;
72
- })}
85
+ }}
73
86
  />
74
87
  <Field
75
88
  key="quality"
76
89
  value={$exports.videoQuality}
77
- params={{ min: 1, max: 100, step: 1, suffix: "%", triggerable: false }}
78
- on:change={((e) => {
90
+ params={{
91
+ min: 1,
92
+ max: 100,
93
+ step: 1,
94
+ suffix: '%',
95
+ triggerable: false,
96
+ }}
97
+ on:change={(e) => {
79
98
  $exports.videoQuality = e.detail;
80
- })}
99
+ }}
81
100
  />
82
101
  <Field
83
102
  key="useDuration"
84
103
  value={$exports.useDuration}
85
- on:change={((e) => {
104
+ on:change={(e) => {
86
105
  $exports.useDuration = e.detail;
87
- })}
88
- />
89
- {#if $exports.useDuration }
90
- <Field
91
- key="loopCount"
92
- value={$exports.loopCount}
93
- params={{ step: 1 }}
94
- on:change={((e) => {
95
- $exports.loopCount = e.detail;
96
- })}
106
+ }}
97
107
  />
108
+ {#if $exports.useDuration}
109
+ <Field
110
+ key="loopCount"
111
+ value={$exports.loopCount}
112
+ params={{ step: 1 }}
113
+ on:change={(e) => {
114
+ $exports.loopCount = e.detail;
115
+ }}
116
+ />
98
117
  {/if}
99
118
  <Field
100
119
  key="record"