@vessel-dsp/core 0.6.3 → 0.6.5

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 (182) hide show
  1. package/README.md +11 -3
  2. package/dist/editor/commands.d.ts +13 -13
  3. package/dist/editor/commands.d.ts.map +1 -1
  4. package/dist/editor/commands.js +44 -29
  5. package/dist/editor/commands.js.map +1 -1
  6. package/dist/editor/factory.d.ts +1 -1
  7. package/dist/editor/factory.d.ts.map +1 -1
  8. package/dist/editor/factory.js +51 -51
  9. package/dist/editor/factory.js.map +1 -1
  10. package/dist/editor/history.d.ts +7 -7
  11. package/dist/editor/history.d.ts.map +1 -1
  12. package/dist/editor/history.js +20 -12
  13. package/dist/editor/history.js.map +1 -1
  14. package/dist/editor/index.d.ts +8 -8
  15. package/dist/editor/index.d.ts.map +1 -1
  16. package/dist/editor/index.js +4 -4
  17. package/dist/editor/index.js.map +1 -1
  18. package/dist/editor/layout.d.ts +1 -1
  19. package/dist/editor/layout.d.ts.map +1 -1
  20. package/dist/editor/layout.js +11 -6
  21. package/dist/editor/layout.js.map +1 -1
  22. package/dist/formats/circuit-json/serializer.d.ts +15 -15
  23. package/dist/formats/circuit-json/serializer.d.ts.map +1 -1
  24. package/dist/formats/circuit-json/serializer.js +486 -394
  25. package/dist/formats/circuit-json/serializer.js.map +1 -1
  26. package/dist/formats/document.d.ts +6 -6
  27. package/dist/formats/document.d.ts.map +1 -1
  28. package/dist/formats/document.js +112 -92
  29. package/dist/formats/document.js.map +1 -1
  30. package/dist/formats/interchange/parser.d.ts +1 -1
  31. package/dist/formats/interchange/parser.d.ts.map +1 -1
  32. package/dist/formats/interchange/parser.js +483 -286
  33. package/dist/formats/interchange/parser.js.map +1 -1
  34. package/dist/formats/interchange/serializer.d.ts +1 -1
  35. package/dist/formats/interchange/serializer.d.ts.map +1 -1
  36. package/dist/formats/interchange/serializer.js +59 -28
  37. package/dist/formats/interchange/serializer.js.map +1 -1
  38. package/dist/formats/ltspice/catalog.d.ts +1 -1
  39. package/dist/formats/ltspice/catalog.d.ts.map +1 -1
  40. package/dist/formats/ltspice/catalog.js +150 -48
  41. package/dist/formats/ltspice/catalog.js.map +1 -1
  42. package/dist/formats/ltspice/encoding.js +12 -40
  43. package/dist/formats/ltspice/encoding.js.map +1 -1
  44. package/dist/formats/ltspice/parser.d.ts +1 -1
  45. package/dist/formats/ltspice/parser.d.ts.map +1 -1
  46. package/dist/formats/ltspice/parser.js +122 -75
  47. package/dist/formats/ltspice/parser.js.map +1 -1
  48. package/dist/formats/ltspice/serializer.d.ts +1 -1
  49. package/dist/formats/ltspice/serializer.d.ts.map +1 -1
  50. package/dist/formats/ltspice/serializer.js +69 -47
  51. package/dist/formats/ltspice/serializer.js.map +1 -1
  52. package/dist/formats/schx/catalog.d.ts +1 -1
  53. package/dist/formats/schx/catalog.d.ts.map +1 -1
  54. package/dist/formats/schx/catalog.js +499 -254
  55. package/dist/formats/schx/catalog.js.map +1 -1
  56. package/dist/formats/schx/parser.d.ts +1 -1
  57. package/dist/formats/schx/parser.d.ts.map +1 -1
  58. package/dist/formats/schx/parser.js +40 -38
  59. package/dist/formats/schx/parser.js.map +1 -1
  60. package/dist/formats/schx/runtime-descriptors.d.ts +1 -1
  61. package/dist/formats/schx/runtime-descriptors.d.ts.map +1 -1
  62. package/dist/formats/schx/runtime-descriptors.js +239 -201
  63. package/dist/formats/schx/runtime-descriptors.js.map +1 -1
  64. package/dist/formats/schx/serializer.d.ts +2 -2
  65. package/dist/formats/schx/serializer.d.ts.map +1 -1
  66. package/dist/formats/schx/serializer.js +106 -106
  67. package/dist/formats/schx/serializer.js.map +1 -1
  68. package/dist/formats/schx/transforms.d.ts +1 -1
  69. package/dist/formats/schx/transforms.d.ts.map +1 -1
  70. package/dist/formats/schx/transforms.js +16 -8
  71. package/dist/formats/schx/transforms.js.map +1 -1
  72. package/dist/formats/spice/parser.d.ts +1 -1
  73. package/dist/formats/spice/parser.d.ts.map +1 -1
  74. package/dist/formats/spice/parser.js +105 -56
  75. package/dist/formats/spice/parser.js.map +1 -1
  76. package/dist/formats/spice/serializer.d.ts +1 -1
  77. package/dist/formats/spice/serializer.js +14 -12
  78. package/dist/formats/spice/serializer.js.map +1 -1
  79. package/dist/index.d.ts +47 -46
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +32 -31
  82. package/dist/index.js.map +1 -1
  83. package/dist/model/connectivity.d.ts +1 -1
  84. package/dist/model/connectivity.d.ts.map +1 -1
  85. package/dist/model/connectivity.js +22 -7
  86. package/dist/model/connectivity.js.map +1 -1
  87. package/dist/model/netlist.d.ts +3 -3
  88. package/dist/model/netlist.d.ts.map +1 -1
  89. package/dist/model/netlist.js +117 -100
  90. package/dist/model/netlist.js.map +1 -1
  91. package/dist/model/properties.d.ts +1 -1
  92. package/dist/model/properties.d.ts.map +1 -1
  93. package/dist/model/properties.js +16 -16
  94. package/dist/model/properties.js.map +1 -1
  95. package/dist/model/quantity.d.ts +1 -1
  96. package/dist/model/quantity.d.ts.map +1 -1
  97. package/dist/model/quantity.js +35 -35
  98. package/dist/model/quantity.js.map +1 -1
  99. package/dist/model/types.d.ts +72 -37
  100. package/dist/model/types.d.ts.map +1 -1
  101. package/dist/model/types.js +1 -1
  102. package/dist/model/types.js.map +1 -1
  103. package/dist/model/validation.d.ts +5 -5
  104. package/dist/model/validation.d.ts.map +1 -1
  105. package/dist/model/validation.js +763 -315
  106. package/dist/model/validation.js.map +1 -1
  107. package/dist/model/wires.d.ts +1 -1
  108. package/dist/model/wires.d.ts.map +1 -1
  109. package/dist/model/wires.js +4 -1
  110. package/dist/model/wires.js.map +1 -1
  111. package/dist/panel/extract.d.ts +2 -2
  112. package/dist/panel/extract.d.ts.map +1 -1
  113. package/dist/panel/extract.js +376 -216
  114. package/dist/panel/extract.js.map +1 -1
  115. package/dist/panel/index.d.ts +7 -5
  116. package/dist/panel/index.d.ts.map +1 -1
  117. package/dist/panel/index.js +5 -4
  118. package/dist/panel/index.js.map +1 -1
  119. package/dist/panel/knobs.d.ts +4 -4
  120. package/dist/panel/knobs.d.ts.map +1 -1
  121. package/dist/panel/knobs.js +1 -1
  122. package/dist/panel/knobs.js.map +1 -1
  123. package/dist/panel/placement.d.ts +27 -0
  124. package/dist/panel/placement.d.ts.map +1 -0
  125. package/dist/panel/placement.js +91 -0
  126. package/dist/panel/placement.js.map +1 -0
  127. package/dist/panel/protocol.d.ts +1 -1
  128. package/dist/panel/protocol.d.ts.map +1 -1
  129. package/dist/panel/protocol.js +32 -23
  130. package/dist/panel/protocol.js.map +1 -1
  131. package/dist/panel/types.d.ts +26 -18
  132. package/dist/panel/types.d.ts.map +1 -1
  133. package/dist/panel/types.js.map +1 -1
  134. package/dist/preview/bounds.d.ts +1 -1
  135. package/dist/preview/bounds.d.ts.map +1 -1
  136. package/dist/preview/bounds.js +3 -3
  137. package/dist/preview/bounds.js.map +1 -1
  138. package/dist/preview/box-layout.d.ts +2 -2
  139. package/dist/preview/box-layout.js.map +1 -1
  140. package/dist/preview/colors.d.ts +1 -1
  141. package/dist/preview/colors.js +35 -35
  142. package/dist/preview/colors.js.map +1 -1
  143. package/dist/preview/hanging.d.ts +1 -1
  144. package/dist/preview/hanging.d.ts.map +1 -1
  145. package/dist/preview/hanging.js +4 -1
  146. package/dist/preview/hanging.js.map +1 -1
  147. package/dist/preview/junctions.d.ts +1 -1
  148. package/dist/preview/junctions.d.ts.map +1 -1
  149. package/dist/preview/junctions.js.map +1 -1
  150. package/dist/preview/label-layout.d.ts.map +1 -1
  151. package/dist/preview/label-layout.js +4 -4
  152. package/dist/preview/label-layout.js.map +1 -1
  153. package/dist/preview/ports.d.ts +1 -1
  154. package/dist/preview/ports.d.ts.map +1 -1
  155. package/dist/preview/ports.js +2 -1
  156. package/dist/preview/ports.js.map +1 -1
  157. package/dist/preview/renderable-wires.d.ts +1 -1
  158. package/dist/preview/renderable-wires.d.ts.map +1 -1
  159. package/dist/preview/renderable-wires.js +3 -1
  160. package/dist/preview/renderable-wires.js.map +1 -1
  161. package/dist/preview/routing.d.ts +1 -1
  162. package/dist/preview/routing.js +1 -1
  163. package/dist/preview/routing.js.map +1 -1
  164. package/dist/preview/snap.d.ts +1 -1
  165. package/dist/preview/snap.d.ts.map +1 -1
  166. package/dist/preview/snap.js +11 -3
  167. package/dist/preview/snap.js.map +1 -1
  168. package/dist/preview/symbols/svg-content.d.ts.map +1 -1
  169. package/dist/preview/symbols/svg-content.js +200 -50
  170. package/dist/preview/symbols/svg-content.js.map +1 -1
  171. package/dist/preview/symbols.d.ts +2 -2
  172. package/dist/preview/symbols.d.ts.map +1 -1
  173. package/dist/preview/symbols.js +100 -97
  174. package/dist/preview/symbols.js.map +1 -1
  175. package/dist/preview/wire-chains.d.ts +1 -1
  176. package/dist/preview/wire-chains.d.ts.map +1 -1
  177. package/dist/preview/wire-chains.js.map +1 -1
  178. package/dist/profiles.d.ts +600 -0
  179. package/dist/profiles.d.ts.map +1 -0
  180. package/dist/profiles.js +118 -0
  181. package/dist/profiles.js.map +1 -0
  182. package/package.json +54 -54
@@ -1,13 +1,48 @@
1
- import { isParsedQuantity, propertyNumericValue, propertyStringValue } from '../model/properties.js';
2
- import { buildKnobSteps, snapKnobPosition } from './knobs.js';
1
+ import { isParsedQuantity, propertyNumericValue, propertyStringValue, } from "../model/properties.js";
2
+ import { buildKnobSteps, snapKnobPosition } from "./knobs.js";
3
3
  const RUNTIME_CONTINUOUS_CONTROL_SPECS = [
4
- { key: 'time', controlProperty: 'TimeControl', wipeProperty: 'TimeControlWipe', sweepProperty: 'TimeControlSweep' },
5
- { key: 'feedback', controlProperty: 'FeedbackControl', wipeProperty: 'FeedbackControlWipe', sweepProperty: 'FeedbackControlSweep' },
6
- { key: 'mix', controlProperty: 'MixControl', wipeProperty: 'MixControlWipe', sweepProperty: 'MixControlSweep' },
7
- { key: 'level', controlProperty: 'LevelControl', wipeProperty: 'LevelControlWipe', sweepProperty: 'LevelControlSweep' },
8
- { key: 'tone', controlProperty: 'ToneControl', wipeProperty: 'ToneControlWipe', sweepProperty: 'ToneControlSweep' },
9
- { key: 'mod-rate', controlProperty: 'ModRateControl', wipeProperty: 'ModRateControlWipe', sweepProperty: 'ModRateControlSweep' },
10
- { key: 'mod-depth', controlProperty: 'ModDepthControl', wipeProperty: 'ModDepthControlWipe', sweepProperty: 'ModDepthControlSweep' },
4
+ {
5
+ key: "time",
6
+ controlProperty: "TimeControl",
7
+ wipeProperty: "TimeControlWipe",
8
+ sweepProperty: "TimeControlSweep",
9
+ },
10
+ {
11
+ key: "feedback",
12
+ controlProperty: "FeedbackControl",
13
+ wipeProperty: "FeedbackControlWipe",
14
+ sweepProperty: "FeedbackControlSweep",
15
+ },
16
+ {
17
+ key: "mix",
18
+ controlProperty: "MixControl",
19
+ wipeProperty: "MixControlWipe",
20
+ sweepProperty: "MixControlSweep",
21
+ },
22
+ {
23
+ key: "level",
24
+ controlProperty: "LevelControl",
25
+ wipeProperty: "LevelControlWipe",
26
+ sweepProperty: "LevelControlSweep",
27
+ },
28
+ {
29
+ key: "tone",
30
+ controlProperty: "ToneControl",
31
+ wipeProperty: "ToneControlWipe",
32
+ sweepProperty: "ToneControlSweep",
33
+ },
34
+ {
35
+ key: "mod-rate",
36
+ controlProperty: "ModRateControl",
37
+ wipeProperty: "ModRateControlWipe",
38
+ sweepProperty: "ModRateControlSweep",
39
+ },
40
+ {
41
+ key: "mod-depth",
42
+ controlProperty: "ModDepthControl",
43
+ wipeProperty: "ModDepthControlWipe",
44
+ sweepProperty: "ModDepthControlSweep",
45
+ },
11
46
  ];
12
47
  // extractPanel inspects a CircuitDocument and emits the typed Panel descriptor
13
48
  // that drives the runtime control surface. It's a pure read over the existing
@@ -20,7 +55,19 @@ export function extractPanel(doc) {
20
55
  const jacks = [];
21
56
  for (const component of doc.components) {
22
57
  switch (component.kind) {
23
- case 'potentiometer': {
58
+ case "variable-resistor": {
59
+ if (!isVariableResistorControl(component)) {
60
+ break;
61
+ }
62
+ if (isSliderControl(component)) {
63
+ sliders.push(toSlider(component));
64
+ }
65
+ else {
66
+ knobs.push(toKnob(component));
67
+ }
68
+ break;
69
+ }
70
+ case "potentiometer": {
24
71
  if (isSliderControl(component)) {
25
72
  sliders.push(toSlider(component));
26
73
  }
@@ -29,15 +76,15 @@ export function extractPanel(doc) {
29
76
  }
30
77
  break;
31
78
  }
32
- case 'switch': {
79
+ case "switch": {
33
80
  switches.push(toSwitch(component));
34
81
  break;
35
82
  }
36
- case 'led': {
83
+ case "led": {
37
84
  leds.push(toLed(component));
38
85
  break;
39
86
  }
40
- case 'jack': {
87
+ case "jack": {
41
88
  jacks.push(toJack(component));
42
89
  break;
43
90
  }
@@ -74,7 +121,7 @@ export function extractDeviceInterface(doc) {
74
121
  for (const control of doc.deviceInterface?.controls ?? []) {
75
122
  controls.set(control.id, {
76
123
  ...control,
77
- provenance: 'vdsp-declared',
124
+ provenance: "vdsp-declared",
78
125
  });
79
126
  }
80
127
  for (const inferred of inferredControls) {
@@ -90,24 +137,73 @@ export function extractDeviceInterface(doc) {
90
137
  });
91
138
  continue;
92
139
  }
93
- if (declared.binding !== undefined
94
- && inferred.binding !== undefined
95
- && bindingSignature(declared.binding) !== bindingSignature(inferred.binding)) {
140
+ if (declared.binding !== undefined &&
141
+ inferred.binding !== undefined &&
142
+ bindingSignature(declared.binding) !== bindingSignature(inferred.binding)) {
96
143
  diagnostics.push({
97
- code: 'device-interface-inferred-binding-conflict',
144
+ code: "device-interface-inferred-binding-conflict",
98
145
  message: `Declared device interface control "${declared.id}" conflicts with inferred binding`,
99
146
  componentId: declared.id,
100
147
  });
101
148
  }
102
149
  }
150
+ const resolvedControls = Array.from(controls.values());
103
151
  return {
104
152
  groups: doc.controlGroups ?? [],
105
153
  contexts: doc.controlContexts ?? [],
106
- controls: Array.from(controls.values()),
154
+ controls: resolvedControls,
155
+ groupMemberships: resolveControlGroupMemberships(doc.controlGroups ?? [], controls),
107
156
  ...(panel.placement === undefined ? {} : { placement: panel.placement }),
108
157
  diagnostics,
109
158
  };
110
159
  }
160
+ function resolveControlGroupMemberships(groups, controls) {
161
+ const memberships = [];
162
+ const groupsById = new Map(groups.map((group) => [group.id, group]));
163
+ const explicitMemberships = new Set();
164
+ for (const group of groups) {
165
+ for (const member of group.members ?? []) {
166
+ const control = controls.get(member.controlId);
167
+ if (control === undefined) {
168
+ continue;
169
+ }
170
+ explicitMemberships.add(`${group.id}:${control.id}`);
171
+ memberships.push({
172
+ group,
173
+ control,
174
+ ...(member.order === undefined ? {} : { order: member.order }),
175
+ ...(member.appliesWhen === undefined
176
+ ? {}
177
+ : { appliesWhen: member.appliesWhen }),
178
+ ...(member.description === undefined
179
+ ? {}
180
+ : { description: member.description }),
181
+ });
182
+ }
183
+ }
184
+ for (const control of controls.values()) {
185
+ if (control.groupId === undefined) {
186
+ continue;
187
+ }
188
+ const group = groupsById.get(control.groupId);
189
+ if (group === undefined ||
190
+ explicitMemberships.has(`${group.id}:${control.id}`)) {
191
+ continue;
192
+ }
193
+ memberships.push({
194
+ group,
195
+ control,
196
+ ...(control.order === undefined ? {} : { order: control.order }),
197
+ ...(control.appliesWhen === undefined
198
+ ? {}
199
+ : { appliesWhen: control.appliesWhen }),
200
+ ...(control.description === undefined
201
+ ? {}
202
+ : { description: control.description }),
203
+ });
204
+ }
205
+ return memberships;
206
+ }
111
207
  function inferDeviceInterfaceControls(doc, panel) {
112
208
  const controls = [];
113
209
  const controlInterfaceIds = new Set();
@@ -123,7 +219,7 @@ function inferDeviceInterfaceControls(doc, panel) {
123
219
  controls.push({
124
220
  id: knob.id,
125
221
  label: knob.name,
126
- kind: 'knob',
222
+ kind: "knob",
127
223
  role: roleFromControlId(knob.id),
128
224
  binding: {
129
225
  componentId,
@@ -138,21 +234,21 @@ function inferDeviceInterfaceControls(doc, panel) {
138
234
  controls.push({
139
235
  id: slider.id,
140
236
  label: slider.name,
141
- kind: 'slider',
237
+ kind: "slider",
142
238
  role: roleFromControlId(slider.id),
143
239
  binding: {
144
240
  componentId: componentIdFromControlId(slider.id),
145
241
  controlId: slider.id,
146
242
  controlName: slider.name,
147
243
  },
148
- provenance: 'source-inferred',
244
+ provenance: "source-inferred",
149
245
  });
150
246
  }
151
247
  for (const switchControl of panel.switches) {
152
248
  controls.push({
153
249
  id: switchControl.id,
154
250
  label: switchControl.name,
155
- kind: 'switch',
251
+ kind: "switch",
156
252
  role: roleFromControlId(switchControl.id),
157
253
  binding: {
158
254
  componentId: componentIdFromControlId(switchControl.id),
@@ -166,14 +262,14 @@ function inferDeviceInterfaceControls(doc, panel) {
166
262
  controls.push({
167
263
  id: led.id,
168
264
  label: led.name,
169
- kind: 'led',
170
- role: 'indicator',
265
+ kind: "led",
266
+ role: "indicator",
171
267
  binding: {
172
268
  componentId: componentIdFromControlId(led.id),
173
269
  controlId: led.id,
174
270
  controlName: led.name,
175
271
  },
176
- provenance: 'source-inferred',
272
+ provenance: "source-inferred",
177
273
  });
178
274
  }
179
275
  for (const jack of panel.jacks) {
@@ -182,11 +278,11 @@ function inferDeviceInterfaceControls(doc, panel) {
182
278
  controls.push({
183
279
  id: jack.id,
184
280
  label: jack.name,
185
- kind: 'jack',
281
+ kind: "jack",
186
282
  role: jack.controlRole ?? jack.role,
187
283
  ...(binding === undefined ? {} : { binding }),
188
284
  provenance: controlInterfaceIds.has(jack.id)
189
- ? 'control-interface-declared'
285
+ ? "control-interface-declared"
190
286
  : provenanceForComponentControl(doc, componentId),
191
287
  });
192
288
  }
@@ -196,12 +292,12 @@ function deviceBindingForJack(jack, componentId) {
196
292
  if (jack.binding !== undefined) {
197
293
  return deviceBindingFromControlInterfaceBinding(jack.binding, componentId);
198
294
  }
199
- if (jack.id.endsWith(':tempo-tap')) {
295
+ if (jack.id.endsWith(":tempo-tap")) {
200
296
  return {
201
297
  componentId,
202
298
  controlId: jack.id,
203
299
  controlName: jack.name,
204
- property: 'TempoTapControl',
300
+ property: "TempoTapControl",
205
301
  };
206
302
  }
207
303
  if (jack.sourceComponentId !== undefined || jack.id === componentId) {
@@ -220,23 +316,27 @@ function deviceBindingFromControlInterfaceBinding(binding, fallbackComponentId)
220
316
  const componentId = binding.sourceComponentId ?? fallbackComponentId;
221
317
  return {
222
318
  componentId,
223
- ...(binding.controlId === undefined ? {} : { controlId: binding.controlId }),
224
- ...(binding.controlName === undefined ? {} : { controlName: binding.controlName }),
319
+ ...(binding.controlId === undefined
320
+ ? {}
321
+ : { controlId: binding.controlId }),
322
+ ...(binding.controlName === undefined
323
+ ? {}
324
+ : { controlName: binding.controlName }),
225
325
  ...(binding.property === undefined ? {} : { property: binding.property }),
226
326
  };
227
327
  }
228
328
  function provenanceForComponentControl(doc, componentId) {
229
329
  const component = doc.components.find((candidate) => candidate.id === componentId);
230
330
  return component !== undefined && isRuntimeDescriptor(component)
231
- ? 'runtime-descriptor-inferred'
232
- : 'source-inferred';
331
+ ? "runtime-descriptor-inferred"
332
+ : "source-inferred";
233
333
  }
234
334
  function componentIdFromControlId(id) {
235
- const separator = id.indexOf(':');
335
+ const separator = id.indexOf(":");
236
336
  return separator <= 0 ? id : id.slice(0, separator);
237
337
  }
238
338
  function roleFromControlId(id) {
239
- const separator = id.indexOf(':');
339
+ const separator = id.indexOf(":");
240
340
  const raw = separator >= 0 ? id.slice(separator + 1) : id;
241
341
  return normalizeToken(raw);
242
342
  }
@@ -247,25 +347,25 @@ function runtimeControlProperty(id) {
247
347
  return spec.controlProperty;
248
348
  }
249
349
  }
250
- if (key === 'mode') {
251
- return 'ModeControl';
350
+ if (key === "mode") {
351
+ return "ModeControl";
252
352
  }
253
- if (key === 'tempo-tap') {
254
- return 'TempoTapControl';
353
+ if (key === "tempo-tap") {
354
+ return "TempoTapControl";
255
355
  }
256
- if (key === 'direct-out') {
257
- return 'DirectOutputJack';
356
+ if (key === "direct-out") {
357
+ return "DirectOutputJack";
258
358
  }
259
359
  return undefined;
260
360
  }
261
361
  function bindingSignature(binding) {
262
362
  return [
263
363
  binding.componentId,
264
- binding.controlId ?? '',
265
- binding.controlName ?? '',
266
- binding.property ?? '',
267
- binding.externalInterfaceId ?? '',
268
- ].join(':');
364
+ binding.controlId ?? "",
365
+ binding.controlName ?? "",
366
+ binding.property ?? "",
367
+ binding.externalInterfaceId ?? "",
368
+ ].join(":");
269
369
  }
270
370
  function applyControlInterfaces(controlInterfaces, jacks) {
271
371
  for (const controlInterface of controlInterfaces ?? []) {
@@ -295,70 +395,90 @@ function toControlInterfaceJack(controlInterface) {
295
395
  ...(sourceComponentId === undefined ? {} : { sourceComponentId }),
296
396
  ...(controlRole === undefined ? {} : { controlRole }),
297
397
  ...(interfaceName === undefined ? {} : { interface: interfaceName }),
298
- ...(controlInterface.connector === undefined ? {} : { connector: controlInterface.connector }),
299
- ...(controlInterface.assignmentHint === undefined ? {} : { assignmentHint: controlInterface.assignmentHint }),
300
- ...(controlInterface.polarity === undefined ? {} : { polarity: controlInterface.polarity }),
301
- ...(controlInterface.binding === undefined ? {} : { binding: controlInterface.binding }),
302
- ...(controlInterface.description === undefined ? {} : { description: controlInterface.description }),
398
+ ...(controlInterface.connector === undefined
399
+ ? {}
400
+ : { connector: controlInterface.connector }),
401
+ ...(controlInterface.assignmentHint === undefined
402
+ ? {}
403
+ : { assignmentHint: controlInterface.assignmentHint }),
404
+ ...(controlInterface.polarity === undefined
405
+ ? {}
406
+ : { polarity: controlInterface.polarity }),
407
+ ...(controlInterface.binding === undefined
408
+ ? {}
409
+ : { binding: controlInterface.binding }),
410
+ ...(controlInterface.description === undefined
411
+ ? {}
412
+ : { description: controlInterface.description }),
303
413
  };
304
414
  }
305
415
  function jackRoleForControlInterface(controlInterface) {
306
416
  switch (controlInterface.role) {
307
- case 'tempo-tap':
308
- return 'tempo-tap';
309
- case 'expression':
310
- return 'expression';
311
- case 'external-control':
312
- case 'trigger':
313
- case 'reset':
314
- case 'sampler-trigger':
315
- return 'external-control';
316
- case 'unknown':
317
- return 'unknown';
417
+ case "tempo-tap":
418
+ return "tempo-tap";
419
+ case "expression":
420
+ return "expression";
421
+ case "external-control":
422
+ case "trigger":
423
+ case "reset":
424
+ case "sampler-trigger":
425
+ return "external-control";
426
+ case "unknown":
427
+ return "unknown";
318
428
  }
319
429
  }
320
430
  function defaultControlRole(controlInterface) {
321
- return controlInterface.role === 'unknown' || controlInterface.role === 'external-control'
431
+ return controlInterface.role === "unknown" ||
432
+ controlInterface.role === "external-control"
322
433
  ? undefined
323
434
  : controlInterface.role;
324
435
  }
325
436
  function defaultInterfaceName(controlInterface) {
326
- if (controlInterface.role === 'tempo-tap') {
327
- return 'tap-tempo';
437
+ if (controlInterface.role === "tempo-tap") {
438
+ return "tap-tempo";
328
439
  }
329
- if (controlInterface.role === 'unknown') {
440
+ if (controlInterface.role === "unknown") {
330
441
  return undefined;
331
442
  }
332
- return 'external-control-input';
443
+ return "external-control-input";
333
444
  }
334
445
  function toKnob(component) {
335
- const taper = resolveTaper(propertyString(component, 'Sweep') ?? propertyString(component, 'Taper'));
336
- const stepLabels = parseStepLabels(propertyStringAny(component, ['StepLabels', 'Steps']));
337
- const explicitStepCount = parseStepCount(propertyStringAny(component, ['StepCount', 'Detents', 'Positions', 'Steps']));
338
- const steps = buildKnobSteps(stepLabels.length >= 2 ? stepLabels.length : explicitStepCount ?? 0, stepLabels);
446
+ const taper = resolveTaper(propertyString(component, "Sweep") ?? propertyString(component, "Taper"));
447
+ const stepLabels = parseStepLabels(propertyStringAny(component, ["StepLabels", "Steps"]));
448
+ const explicitStepCount = parseStepCount(propertyStringAny(component, [
449
+ "StepCount",
450
+ "Detents",
451
+ "Positions",
452
+ "Steps",
453
+ ]));
454
+ const steps = buildKnobSteps(stepLabels.length >= 2 ? stepLabels.length : (explicitStepCount ?? 0), stepLabels);
339
455
  const rawDefaultPosition = clamp01(parseNumeric(component.properties.Wipe) ?? 0.5);
340
- const defaultPosition = steps === undefined ? rawDefaultPosition : snapKnobPosition({ steps }, rawDefaultPosition);
341
- const resistance = quantityProperty(component, 'Resistance');
342
- const gangGroup = propertyString(component, 'Group') ?? undefined;
343
- const description = propertyString(component, 'Description') ?? undefined;
456
+ const defaultPosition = steps === undefined
457
+ ? rawDefaultPosition
458
+ : snapKnobPosition({ steps }, rawDefaultPosition);
459
+ const resistance = quantityProperty(component, "Resistance");
460
+ const gangGroup = propertyString(component, "Group") ?? undefined;
461
+ const description = propertyString(component, "Description") ?? undefined;
344
462
  return {
345
463
  id: component.id,
346
464
  name: component.name,
347
465
  taper,
348
- controlMode: steps === undefined ? 'continuous' : 'stepped',
466
+ controlMode: steps === undefined ? "continuous" : "stepped",
349
467
  defaultPosition,
350
468
  ...(steps !== undefined ? { steps } : {}),
351
469
  ...(resistance !== undefined ? { resistance } : {}),
352
470
  ...(gangGroup !== undefined && gangGroup.length > 0 ? { gangGroup } : {}),
353
- ...(description !== undefined && description.length > 0 ? { description } : {}),
471
+ ...(description !== undefined && description.length > 0
472
+ ? { description }
473
+ : {}),
354
474
  };
355
475
  }
356
476
  function toSlider(component) {
357
477
  const defaultPosition = clamp01(parseNumeric(component.properties.Wipe) ?? 0.5);
358
- const orientation = resolveSliderOrientation(propertyStringAny(component, ['Orientation', 'SliderOrientation']));
478
+ const orientation = resolveSliderOrientation(propertyStringAny(component, ["Orientation", "SliderOrientation"]));
359
479
  const range = sliderRange(component);
360
- const gangGroup = propertyString(component, 'Group') ?? undefined;
361
- const description = propertyString(component, 'Description') ?? undefined;
480
+ const gangGroup = propertyString(component, "Group") ?? undefined;
481
+ const description = propertyString(component, "Description") ?? undefined;
362
482
  return {
363
483
  id: component.id,
364
484
  name: component.name,
@@ -366,16 +486,18 @@ function toSlider(component) {
366
486
  orientation,
367
487
  ...(range !== undefined ? { range } : {}),
368
488
  ...(gangGroup !== undefined && gangGroup.length > 0 ? { gangGroup } : {}),
369
- ...(description !== undefined && description.length > 0 ? { description } : {}),
489
+ ...(description !== undefined && description.length > 0
490
+ ? { description }
491
+ : {}),
370
492
  };
371
493
  }
372
494
  function toSwitch(component) {
373
495
  const switchKind = resolveSwitchKind(component);
374
496
  const { poles, positions } = switchGeometry(switchKind);
375
497
  const defaultPosition = clampInt(parseNumeric(component.properties.Position) ?? 0, 0, positions - 1);
376
- const gangGroup = propertyString(component, 'Group') ?? undefined;
377
- const partNumber = propertyString(component, 'PartNumber') ?? undefined;
378
- const description = propertyString(component, 'Description') ?? undefined;
498
+ const gangGroup = propertyString(component, "Group") ?? undefined;
499
+ const partNumber = propertyString(component, "PartNumber") ?? undefined;
500
+ const description = propertyString(component, "Description") ?? undefined;
379
501
  return {
380
502
  id: component.id,
381
503
  name: component.name,
@@ -384,30 +506,39 @@ function toSwitch(component) {
384
506
  positions,
385
507
  defaultPosition,
386
508
  ...(gangGroup !== undefined && gangGroup.length > 0 ? { gangGroup } : {}),
387
- ...(partNumber !== undefined && partNumber.length > 0 ? { partNumber } : {}),
388
- ...(description !== undefined && description.length > 0 ? { description } : {}),
509
+ ...(partNumber !== undefined && partNumber.length > 0
510
+ ? { partNumber }
511
+ : {}),
512
+ ...(description !== undefined && description.length > 0
513
+ ? { description }
514
+ : {}),
389
515
  };
390
516
  }
391
517
  function toLed(component) {
392
- const color = propertyString(component, 'Color') ?? inferLedColor(component);
393
- const partNumber = propertyString(component, 'PartNumber') ?? undefined;
394
- const description = propertyString(component, 'Description') ?? undefined;
518
+ const color = propertyString(component, "Color") ?? inferLedColor(component);
519
+ const partNumber = propertyString(component, "PartNumber") ?? undefined;
520
+ const description = propertyString(component, "Description") ?? undefined;
395
521
  return {
396
522
  id: component.id,
397
523
  name: component.name,
398
524
  ...(color !== undefined ? { color } : {}),
399
- ...(partNumber !== undefined && partNumber.length > 0 ? { partNumber } : {}),
400
- ...(description !== undefined && description.length > 0 ? { description } : {}),
525
+ ...(partNumber !== undefined && partNumber.length > 0
526
+ ? { partNumber }
527
+ : {}),
528
+ ...(description !== undefined && description.length > 0
529
+ ? { description }
530
+ : {}),
401
531
  };
402
532
  }
403
533
  function toJack(component) {
404
534
  const role = resolveJackRole(component);
405
- const name = nonEmptyString(propertyStringAny(component, ['JackLabel', 'Label'])) ?? component.name;
406
- const audioRole = nonEmptyString(propertyString(component, 'AudioRole'));
407
- const impedance = quantityProperty(component, 'Impedance');
408
- const controlRole = nonEmptyString(propertyString(component, 'ControlRole'));
409
- const interfaceName = nonEmptyString(propertyString(component, 'Interface'));
410
- const description = propertyString(component, 'Description') ?? undefined;
535
+ const name = nonEmptyString(propertyStringAny(component, ["JackLabel", "Label"])) ??
536
+ component.name;
537
+ const audioRole = nonEmptyString(propertyString(component, "AudioRole"));
538
+ const impedance = quantityProperty(component, "Impedance");
539
+ const controlRole = nonEmptyString(propertyString(component, "ControlRole"));
540
+ const interfaceName = nonEmptyString(propertyString(component, "Interface"));
541
+ const description = propertyString(component, "Description") ?? undefined;
411
542
  const sourceTypeName = component.sourceTypeName ?? undefined;
412
543
  return {
413
544
  id: component.id,
@@ -418,7 +549,9 @@ function toJack(component) {
418
549
  ...(sourceTypeName !== undefined ? { sourceTypeName } : {}),
419
550
  ...(controlRole !== undefined ? { controlRole } : {}),
420
551
  ...(interfaceName !== undefined ? { interface: interfaceName } : {}),
421
- ...(description !== undefined && description.length > 0 ? { description } : {}),
552
+ ...(description !== undefined && description.length > 0
553
+ ? { description }
554
+ : {}),
422
555
  };
423
556
  }
424
557
  function runtimeDescriptorKnobs(component) {
@@ -432,7 +565,7 @@ function runtimeDescriptorKnobs(component) {
432
565
  id: `${component.id}:${spec.key}`,
433
566
  name,
434
567
  taper: resolveTaper(propertyString(component, spec.sweepProperty)),
435
- controlMode: 'continuous',
568
+ controlMode: "continuous",
436
569
  defaultPosition: clamp01(parseNumeric(component.properties[spec.wipeProperty]) ?? 0.5),
437
570
  });
438
571
  }
@@ -443,96 +576,120 @@ function runtimeDescriptorKnobs(component) {
443
576
  return knobs;
444
577
  }
445
578
  function runtimeDescriptorMode(component) {
446
- const name = nonEmptyString(propertyString(component, 'ModeControl'));
447
- const labels = parseStepLabels(propertyStringAny(component, ['ModeLabels', 'ModeOptions']));
448
- const explicitStepCount = parseStepCount(propertyStringAny(component, ['ModeStepCount', 'ModeSteps', 'ModeCount']));
449
- const steps = buildKnobSteps(labels.length >= 2 ? labels.length : explicitStepCount ?? 0, labels);
579
+ const name = nonEmptyString(propertyString(component, "ModeControl"));
580
+ const labels = parseStepLabels(propertyStringAny(component, ["ModeLabels", "ModeOptions"]));
581
+ const explicitStepCount = parseStepCount(propertyStringAny(component, ["ModeStepCount", "ModeSteps", "ModeCount"]));
582
+ const steps = buildKnobSteps(labels.length >= 2 ? labels.length : (explicitStepCount ?? 0), labels);
450
583
  if (name === undefined || steps === undefined) {
451
584
  return undefined;
452
585
  }
453
586
  return {
454
587
  id: `${component.id}:mode`,
455
588
  name,
456
- taper: 'unknown',
457
- controlMode: 'stepped',
458
- defaultPosition: runtimeModeDefaultPosition(steps, parseNumericAny(component, ['ModeControlWipe', 'ModeDefaultIndex', 'ModeIndex'])),
589
+ taper: "unknown",
590
+ controlMode: "stepped",
591
+ defaultPosition: runtimeModeDefaultPosition(steps, parseNumericAny(component, [
592
+ "ModeControlWipe",
593
+ "ModeDefaultIndex",
594
+ "ModeIndex",
595
+ ])),
459
596
  steps,
460
597
  };
461
598
  }
462
599
  function runtimeDescriptorTempoTap(component) {
463
- const name = nonEmptyString(propertyStringAny(component, ['TempoTapControl', 'TapTempoControl', 'TempoControl']));
600
+ const name = nonEmptyString(propertyStringAny(component, [
601
+ "TempoTapControl",
602
+ "TapTempoControl",
603
+ "TempoControl",
604
+ ]));
464
605
  if (name === undefined) {
465
606
  return undefined;
466
607
  }
467
608
  const sourceTypeName = component.sourceTypeName ?? undefined;
468
- const assignmentHint = 'momentary';
609
+ const assignmentHint = "momentary";
469
610
  return {
470
611
  id: `${component.id}:tempo-tap`,
471
612
  name,
472
- role: 'tempo-tap',
613
+ role: "tempo-tap",
473
614
  sourceComponentId: component.id,
474
- controlRole: 'tempo-tap',
475
- interface: 'tap-tempo',
615
+ controlRole: "tempo-tap",
616
+ interface: "tap-tempo",
476
617
  assignmentHint,
477
618
  ...(sourceTypeName !== undefined ? { sourceTypeName } : {}),
478
619
  };
479
620
  }
480
621
  function runtimeDescriptorDirectOut(component) {
481
622
  const name = nonEmptyString(propertyStringAny(component, [
482
- 'DirectOutputJack',
483
- 'DirectOutJack',
484
- 'DirectOutputControl',
485
- 'DirectOutControl',
623
+ "DirectOutputJack",
624
+ "DirectOutJack",
625
+ "DirectOutputControl",
626
+ "DirectOutControl",
486
627
  ]));
487
628
  if (name === undefined) {
488
629
  return undefined;
489
630
  }
490
631
  const sourceTypeName = component.sourceTypeName ?? undefined;
491
632
  const description = nonEmptyString(propertyStringAny(component, [
492
- 'DirectOutputRuntimeBoundary',
493
- 'DirectOutputDescription',
494
- 'DirectOutDescription',
633
+ "DirectOutputRuntimeBoundary",
634
+ "DirectOutputDescription",
635
+ "DirectOutDescription",
495
636
  ]));
496
637
  const controlId = `${component.id}:direct-out`;
497
638
  return {
498
639
  id: controlId,
499
640
  name,
500
- role: 'direct-output',
641
+ role: "direct-output",
501
642
  sourceComponentId: component.id,
502
- controlRole: 'direct-output',
503
- interface: 'audio-output',
643
+ controlRole: "direct-output",
644
+ interface: "audio-output",
504
645
  binding: {
505
646
  sourceComponentId: component.id,
506
647
  controlId,
507
648
  controlName: name,
508
- property: 'DirectOutputJack',
649
+ property: "DirectOutputJack",
509
650
  },
510
651
  ...(sourceTypeName !== undefined ? { sourceTypeName } : {}),
511
652
  ...(description === undefined ? {} : { description }),
512
653
  };
513
654
  }
514
655
  function isSliderControl(component) {
515
- const style = propertyStringAny(component, ['ControlStyle', 'ControlType', 'PanelControl', 'UiControl', 'Style']);
656
+ const style = propertyStringAny(component, [
657
+ "ControlStyle",
658
+ "ControlType",
659
+ "PanelControl",
660
+ "UiControl",
661
+ "Style",
662
+ ]);
516
663
  if (style === null) {
517
664
  return false;
518
665
  }
519
666
  const lower = style.toLowerCase();
520
- return lower.includes('slider') || lower.includes('fader');
667
+ return lower.includes("slider") || lower.includes("fader");
668
+ }
669
+ function isVariableResistorControl(component) {
670
+ return (component.properties.Wipe !== undefined ||
671
+ component.properties.Sweep !== undefined ||
672
+ component.properties.Taper !== undefined ||
673
+ isSliderControl(component));
521
674
  }
522
675
  function resolveSliderOrientation(value) {
523
- if (value?.toLowerCase().includes('horizontal')) {
524
- return 'horizontal';
676
+ if (value?.toLowerCase().includes("horizontal")) {
677
+ return "horizontal";
525
678
  }
526
- return 'vertical';
679
+ return "vertical";
527
680
  }
528
681
  function sliderRange(component) {
529
- const min = parseNumericAny(component, ['RangeMin', 'Min', 'Minimum']);
530
- const max = parseNumericAny(component, ['RangeMax', 'Max', 'Maximum']);
682
+ const min = parseNumericAny(component, ["RangeMin", "Min", "Minimum"]);
683
+ const max = parseNumericAny(component, ["RangeMax", "Max", "Maximum"]);
531
684
  if (min === undefined || max === undefined || min >= max) {
532
685
  return undefined;
533
686
  }
534
- const unit = propertyStringAny(component, ['Unit', 'RangeUnit']) ?? undefined;
535
- const center = parseNumericAny(component, ['Center', 'CenterValue', 'RangeCenter']);
687
+ const unit = propertyStringAny(component, ["Unit", "RangeUnit"]) ?? undefined;
688
+ const center = parseNumericAny(component, [
689
+ "Center",
690
+ "CenterValue",
691
+ "RangeCenter",
692
+ ]);
536
693
  return {
537
694
  min,
538
695
  max,
@@ -542,19 +699,19 @@ function sliderRange(component) {
542
699
  }
543
700
  function resolveTaper(value) {
544
701
  if (value === null || value === undefined) {
545
- return 'unknown';
702
+ return "unknown";
546
703
  }
547
704
  const lower = value.toLowerCase();
548
- if (lower.includes('log') && lower.includes('rev')) {
549
- return 'reverse-log';
705
+ if (lower.includes("log") && lower.includes("rev")) {
706
+ return "reverse-log";
550
707
  }
551
- if (lower.includes('log') || lower.includes('audio')) {
552
- return 'log';
708
+ if (lower.includes("log") || lower.includes("audio")) {
709
+ return "log";
553
710
  }
554
- if (lower.includes('lin')) {
555
- return 'linear';
711
+ if (lower.includes("lin")) {
712
+ return "linear";
556
713
  }
557
- return 'unknown';
714
+ return "unknown";
558
715
  }
559
716
  function resolveSwitchKind(component) {
560
717
  const short = shortType(component.sourceTypeName);
@@ -562,47 +719,47 @@ function resolveSwitchKind(component) {
562
719
  return inferFromTerminals(component.terminals.length);
563
720
  }
564
721
  const upper = short.toUpperCase();
565
- if (upper === 'SPDT')
566
- return 'spdt';
567
- if (upper === 'SP3T')
568
- return 'sp3t';
569
- if (upper === 'SP4T')
570
- return 'sp4t';
571
- if (upper === '3PDT')
572
- return '3pdt';
573
- if (upper === 'TOGGLE')
574
- return 'toggle';
575
- if (upper === 'ROTARY')
576
- return 'rotary';
577
- if (upper === 'SWITCH')
578
- return 'spst';
722
+ if (upper === "SPDT")
723
+ return "spdt";
724
+ if (upper === "SP3T")
725
+ return "sp3t";
726
+ if (upper === "SP4T")
727
+ return "sp4t";
728
+ if (upper === "3PDT")
729
+ return "3pdt";
730
+ if (upper === "TOGGLE")
731
+ return "toggle";
732
+ if (upper === "ROTARY")
733
+ return "rotary";
734
+ if (upper === "SWITCH")
735
+ return "spst";
579
736
  return inferFromTerminals(component.terminals.length);
580
737
  }
581
738
  function inferFromTerminals(count) {
582
739
  if (count <= 2)
583
- return 'spst';
740
+ return "spst";
584
741
  if (count === 3)
585
- return 'spdt';
742
+ return "spdt";
586
743
  if (count === 9)
587
- return '3pdt';
588
- return 'unknown';
744
+ return "3pdt";
745
+ return "unknown";
589
746
  }
590
747
  function switchGeometry(kind) {
591
748
  switch (kind) {
592
- case 'spst':
593
- case 'toggle':
749
+ case "spst":
750
+ case "toggle":
594
751
  return { poles: 1, positions: 2 };
595
- case 'spdt':
752
+ case "spdt":
596
753
  return { poles: 1, positions: 2 };
597
- case 'sp3t':
754
+ case "sp3t":
598
755
  return { poles: 1, positions: 3 };
599
- case 'sp4t':
756
+ case "sp4t":
600
757
  return { poles: 1, positions: 4 };
601
- case '3pdt':
758
+ case "3pdt":
602
759
  return { poles: 3, positions: 2 };
603
- case 'rotary':
760
+ case "rotary":
604
761
  return { poles: 1, positions: 6 };
605
- case 'unknown':
762
+ case "unknown":
606
763
  return { poles: 1, positions: 2 };
607
764
  }
608
765
  }
@@ -613,23 +770,23 @@ function resolveJackRole(component) {
613
770
  }
614
771
  const short = shortType(component.sourceTypeName);
615
772
  if (short === null) {
616
- return 'unknown';
773
+ return "unknown";
617
774
  }
618
775
  const upper = short.toUpperCase();
619
- if (upper === 'INPUT' || upper === 'INPUTJACK')
620
- return 'input';
621
- if (upper === 'SPEAKER' || upper === 'OUTPUTJACK')
622
- return 'output';
623
- if (upper === 'SEND')
624
- return 'send';
625
- if (upper === 'RETURN')
626
- return 'return';
627
- if (upper === 'EXPRESSION' || upper === 'EXP')
628
- return 'expression';
629
- return 'unknown';
776
+ if (upper === "INPUT" || upper === "INPUTJACK")
777
+ return "input";
778
+ if (upper === "SPEAKER" || upper === "OUTPUTJACK")
779
+ return "output";
780
+ if (upper === "SEND")
781
+ return "send";
782
+ if (upper === "RETURN")
783
+ return "return";
784
+ if (upper === "EXPRESSION" || upper === "EXP")
785
+ return "expression";
786
+ return "unknown";
630
787
  }
631
788
  function resolveSemanticJackRole(component) {
632
- const semanticProperties = ['Role', 'ControlRole', 'Interface'];
789
+ const semanticProperties = ["Role", "ControlRole", "Interface"];
633
790
  for (const name of semanticProperties) {
634
791
  const value = propertyString(component, name);
635
792
  if (value === null) {
@@ -644,48 +801,48 @@ function resolveSemanticJackRole(component) {
644
801
  }
645
802
  function normalizeJackRole(value) {
646
803
  const normalized = normalizeToken(value);
647
- if (['input', 'audio-input', 'in'].includes(normalized))
648
- return 'input';
649
- if (['direct-output', 'direct-out', 'dry-output', 'dry-out'].includes(normalized))
650
- return 'direct-output';
651
- if (['output', 'audio-output', 'out'].includes(normalized))
652
- return 'output';
653
- if (normalized === 'send')
654
- return 'send';
655
- if (normalized === 'return')
656
- return 'return';
657
- if (['expression', 'exp', 'expression-pedal'].includes(normalized))
658
- return 'expression';
659
- if (['tempo-tap', 'tap-tempo', 'tempo-in', 'tap', 'tempo'].includes(normalized))
660
- return 'tempo-tap';
804
+ if (["input", "audio-input", "in"].includes(normalized))
805
+ return "input";
806
+ if (["direct-output", "direct-out", "dry-output", "dry-out"].includes(normalized))
807
+ return "direct-output";
808
+ if (["output", "audio-output", "out"].includes(normalized))
809
+ return "output";
810
+ if (normalized === "send")
811
+ return "send";
812
+ if (normalized === "return")
813
+ return "return";
814
+ if (["expression", "exp", "expression-pedal"].includes(normalized))
815
+ return "expression";
816
+ if (["tempo-tap", "tap-tempo", "tempo-in", "tap", "tempo"].includes(normalized))
817
+ return "tempo-tap";
661
818
  if ([
662
- 'external-control',
663
- 'external-control-input',
664
- 'control-input',
665
- 'remote',
666
- 'footswitch',
667
- 'trigger',
668
- 'reset',
819
+ "external-control",
820
+ "external-control-input",
821
+ "control-input",
822
+ "remote",
823
+ "footswitch",
824
+ "trigger",
825
+ "reset",
669
826
  ].includes(normalized)) {
670
- return 'external-control';
827
+ return "external-control";
671
828
  }
672
829
  return null;
673
830
  }
674
831
  function inferLedColor(component) {
675
832
  // Common pedal LED colors are usually red / amber / green. Try the part number.
676
- const part = propertyString(component, 'PartNumber')?.toLowerCase() ?? '';
677
- if (part.includes('red'))
678
- return 'red';
679
- if (part.includes('green'))
680
- return 'green';
681
- if (part.includes('amber'))
682
- return 'amber';
683
- if (part.includes('blue'))
684
- return 'blue';
685
- if (part.includes('yellow'))
686
- return 'yellow';
687
- if (part.includes('white'))
688
- return 'white';
833
+ const part = propertyString(component, "PartNumber")?.toLowerCase() ?? "";
834
+ if (part.includes("red"))
835
+ return "red";
836
+ if (part.includes("green"))
837
+ return "green";
838
+ if (part.includes("amber"))
839
+ return "amber";
840
+ if (part.includes("blue"))
841
+ return "blue";
842
+ if (part.includes("yellow"))
843
+ return "yellow";
844
+ if (part.includes("white"))
845
+ return "white";
689
846
  return undefined;
690
847
  }
691
848
  function shortType(sourceTypeName) {
@@ -766,10 +923,13 @@ function runtimeModeDefaultPosition(steps, rawValue) {
766
923
  return snapKnobPosition({ steps }, clamp01(rawValue));
767
924
  }
768
925
  function isRuntimeDescriptor(component) {
769
- return component.kind === 'ic' && component.properties.RuntimeDescriptor === 'true';
926
+ return (component.kind === "ic" && component.properties.RuntimeDescriptor === "true");
770
927
  }
771
928
  function normalizeToken(value) {
772
- return value.trim().toLowerCase().replace(/[\s_]+/g, '-');
929
+ return value
930
+ .trim()
931
+ .toLowerCase()
932
+ .replace(/[\s_]+/g, "-");
773
933
  }
774
934
  function clamp01(v) {
775
935
  if (v < 0)