@yam8d/m8-sdk 0.5.3 → 0.5.4

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.
package/dist/index.js CHANGED
@@ -521,7 +521,7 @@ var D = (e) => {
521
521
  serial: "Web Serial",
522
522
  usb: "WebUSB",
523
523
  midi: "Web MIDI"
524
- }, A = () => {
524
+ }, ee = () => {
525
525
  let e = "requestMIDIAccess" in navigator, t = "serial" in navigator, n = "usb" in navigator;
526
526
  if (!e && !t && !n) return { browserSupport: !1 };
527
527
  let r = async (e) => {
@@ -624,26 +624,26 @@ var D = (e) => {
624
624
  };
625
625
  }
626
626
  };
627
- }, ee = async (e, { maxAttempts: t = 3, retryDelayMs: n = 1500 } = {}) => {
627
+ }, te = async (e, { maxAttempts: t = 3, retryDelayMs: n = 1500 } = {}) => {
628
628
  for (let r = 1; r <= t; r++) try {
629
629
  return await e();
630
630
  } catch (e) {
631
631
  console.warn(`Connection attempt ${r}/${t} failed:`, e), r < t ? await new Promise((e) => setTimeout(e, n)) : console.error("Could not connect to M8 after all attempts:", e);
632
632
  }
633
- }, j = {
633
+ }, A = {
634
634
  autoGainControl: !1,
635
635
  channelCount: { ideal: 2 },
636
636
  echoCancellation: !1,
637
637
  noiseSuppression: !1
638
- }, M = async () => {
638
+ }, j = async () => {
639
639
  let e = (await navigator.mediaDevices.enumerateDevices()).filter((e) => e.kind === "audioinput" && e.label.includes("M8") && e.deviceId !== "default" && e.deviceId !== "communications");
640
640
  return e.length > 1 && console.warn("Suspicious: More than one M8 audio input found"), e[0] ?? null;
641
- }, N = async () => navigator.mediaDevices?.enumerateDevices ? M() : null, P = async ({ requireM8: e = !1 } = {}) => {
641
+ }, M = async () => navigator.mediaDevices?.enumerateDevices ? j() : null, N = async ({ requireM8: e = !1 } = {}) => {
642
642
  if (!navigator.mediaDevices?.getUserMedia) throw Error("Audio capture is not supported in this browser");
643
- let t = await N();
643
+ let t = await M();
644
644
  if (!t) {
645
- let n = await navigator.mediaDevices.getUserMedia({ audio: j });
646
- if (t = await N(), !t) {
645
+ let n = await navigator.mediaDevices.getUserMedia({ audio: A });
646
+ if (t = await M(), !t) {
647
647
  if (!e) return n;
648
648
  for (let e of n.getTracks()) e.stop();
649
649
  throw Error("No M8 audio input found (is the M8 connected over USB?)");
@@ -651,10 +651,10 @@ var D = (e) => {
651
651
  for (let e of n.getTracks()) e.stop();
652
652
  }
653
653
  return navigator.mediaDevices.getUserMedia({ audio: {
654
- ...j,
654
+ ...A,
655
655
  deviceId: { exact: t.deviceId }
656
656
  } });
657
- }, F = () => {
657
+ }, P = () => {
658
658
  let e = null, t = null, n = null, r = null, i = (e) => {
659
659
  if (e.state !== "suspended" || typeof window > "u") return;
660
660
  let t = () => {
@@ -666,7 +666,7 @@ var D = (e) => {
666
666
  let r = e;
667
667
  e = null, t = null, n = null, await r?.close().catch(() => void 0);
668
668
  }, o = async (r) => {
669
- let o = await P({ requireM8: r }), s = new AudioContext(), c = s.createMediaStreamSource(o), l = s.createMediaStreamDestination();
669
+ let o = await N({ requireM8: r }), s = new AudioContext(), c = s.createMediaStreamSource(o), l = s.createMediaStreamDestination();
670
670
  c.connect(s.destination), c.connect(l), e = s, t = o, n = l.stream;
671
671
  for (let e of o.getAudioTracks()) e.addEventListener("ended", () => {
672
672
  t === o && a();
@@ -688,7 +688,437 @@ var D = (e) => {
688
688
  return e.length <= 0 ? null : new MediaStream(e.map((e) => e.clone()));
689
689
  }
690
690
  };
691
+ }, ne = [
692
+ "up",
693
+ "down",
694
+ "left",
695
+ "right"
696
+ ], re = 64, F = (e) => `${e.x},${e.y},${e.w},${e.h}`, ie = (e) => {
697
+ let [t, n, r, i] = e.split(",").map(Number);
698
+ return [
699
+ t,
700
+ n,
701
+ r,
702
+ i
703
+ ].every(Number.isFinite) ? {
704
+ x: t,
705
+ y: n,
706
+ w: r,
707
+ h: i
708
+ } : null;
709
+ }, ae = () => ({
710
+ cells: /* @__PURE__ */ new Map(),
711
+ moves: /* @__PURE__ */ new Map(),
712
+ unreachable: /* @__PURE__ */ new Set(),
713
+ suspects: /* @__PURE__ */ new Set()
714
+ }), oe = (e = {}) => {
715
+ let t = /* @__PURE__ */ new Map(), n = () => e.onChange?.(), r = (e) => {
716
+ let n = t.get(e);
717
+ for (n ? t.delete(e) : n = ae(), t.set(e, n); t.size > re;) {
718
+ let e = t.keys().next().value;
719
+ if (e === void 0) break;
720
+ t.delete(e);
721
+ }
722
+ return n;
723
+ }, i = (e, t) => {
724
+ let n = F(t);
725
+ return !e.cells.has(n) && (e.cells.set(n, {
726
+ x: t.x,
727
+ y: t.y,
728
+ w: t.w,
729
+ h: t.h
730
+ }), !0);
731
+ };
732
+ if (e.data?.version === 1) for (let t of e.data.layouts ?? []) {
733
+ let e = r(t.key);
734
+ for (let n of t.cells ?? []) {
735
+ let t = ie(n);
736
+ t && i(e, t);
737
+ }
738
+ for (let [n, r, i] of t.moves ?? []) {
739
+ if (!ne.includes(r)) continue;
740
+ let t = e.moves.get(n) ?? {};
741
+ t[r] = i, e.moves.set(n, t);
742
+ }
743
+ for (let n of t.unreachable ?? []) e.unreachable.add(n);
744
+ for (let n of t.suspects ?? []) e.suspects.add(n);
745
+ }
746
+ return {
747
+ get: (e) => t.get(e),
748
+ recordCell: (e, t) => {
749
+ i(r(e), t) && n();
750
+ },
751
+ recordMove: (e, t, a, o) => {
752
+ let s = r(e);
753
+ i(s, t), o && i(s, o);
754
+ let c = F(t), l = o ? F(o) : null, u = s.moves.get(c) ?? {};
755
+ u[a] !== l && (u[a] = l, s.moves.set(c, u), n());
756
+ },
757
+ markUnreachable: (e, t) => {
758
+ let i = r(e);
759
+ i.unreachable.has(t) || (i.suspects.delete(t) ? i.unreachable.add(t) : i.suspects.add(t), n());
760
+ },
761
+ toJSON: () => ({
762
+ version: 1,
763
+ layouts: [...t.entries()].map(([e, t]) => ({
764
+ key: e,
765
+ cells: [...t.cells.keys()],
766
+ moves: [...t.moves.entries()].flatMap(([e, t]) => ne.filter((e) => t[e] !== void 0).map((n) => [
767
+ e,
768
+ n,
769
+ t[n] ?? null
770
+ ])),
771
+ unreachable: [...t.unreachable],
772
+ suspects: [...t.suspects]
773
+ }))
774
+ })
775
+ };
776
+ }, se = (e, t, n) => t === "up" ? n.y > e.y : t === "down" ? n.y < e.y : t === "left" ? n.x > e.x : n.x < e.x, ce = 1.01, le = (e, t, n, r) => Math.min(t, r) - Math.max(e, n), ue = (e, t, n) => {
777
+ let r = t.x + t.w / 2, i = t.y + t.h / 2, a = null;
778
+ for (let [o, s] of e.cells) {
779
+ let e = s.x + s.w / 2 - r, c = s.y + s.h / 2 - i, l, u;
780
+ if (n === "up" || n === "down") {
781
+ if (l = n === "down" ? c : -c, u = Math.abs(e), le(t.x, t.x + t.w, s.x, s.x + s.w) <= 0) continue;
782
+ } else if (l = n === "right" ? e : -e, u = Math.abs(c), le(t.y, t.y + t.h, s.y, s.y + s.h) < Math.min(t.h, s.h) / 2) continue;
783
+ l < Math.min(t.h, s.h) / 2 || (!a || l < a.along - .5 || Math.abs(l - a.along) <= .5 && u < a.across) && (a = {
784
+ id: o,
785
+ along: l,
786
+ across: u
787
+ });
788
+ }
789
+ return a?.id ?? null;
790
+ }, de = (e, t, n, r = /* @__PURE__ */ new Set(), i = () => !1) => {
791
+ if (n(t)) return [];
792
+ let a = /* @__PURE__ */ new Map([[t, 0]]), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Set(), c = /* @__PURE__ */ new Set([t]);
793
+ for (; c.size > 0;) {
794
+ let l = "", u = Infinity;
795
+ for (let e of c) {
796
+ let t = a.get(e) ?? Infinity;
797
+ t < u && (u = t, l = e);
798
+ }
799
+ if (c.delete(l), s.has(l)) continue;
800
+ if (s.add(l), n(l)) {
801
+ let e = [];
802
+ for (let n = l; n !== t;) {
803
+ let t = o.get(n);
804
+ e.unshift({
805
+ dir: t.dir,
806
+ to: n,
807
+ guessed: t.guessed
808
+ }), n = t.from;
809
+ }
810
+ return e;
811
+ }
812
+ let d = e.cells.get(l), f = e.moves.get(l) ?? {};
813
+ for (let t of ne) {
814
+ if (r.has(`${l}:${t}`) || d && i(d, t)) continue;
815
+ let n = f[t];
816
+ if (n === null) continue;
817
+ let p = n === void 0, m = p ? d ? ue(e, d, t) : null : n;
818
+ if (!m || s.has(m)) continue;
819
+ let h = e.cells.get(m);
820
+ if (!p && d && h && se(d, t, h)) continue;
821
+ let g = u + (p ? ce : 1);
822
+ g < (a.get(m) ?? Infinity) && (a.set(m, g), o.set(m, {
823
+ from: l,
824
+ dir: t,
825
+ guessed: p
826
+ }), c.add(m));
827
+ }
828
+ }
829
+ return null;
691
830
  }, I = {
831
+ version: "1.0",
832
+ description: "Semantic mapping of M8 tracker views: defines the structure of each grid view so cursor position + line text can be interpreted as typed field values.",
833
+ notes: {
834
+ fontMode: "All x/width values assume font mode 0 (standard, 12×14 px cells, 40 cols × ~22 rows). Font modes 1 (bold uppercase) and 2 (large) may shift column positions.",
835
+ playbackIndicator: "The M8 renders a '>' character one column to the left of the first data column when that step/row/track is currently playing. This character is part of the text stream; the editing cursor is a separate rect overlay.",
836
+ screenGeometry: "The screen is 40 columns x 24 rows. Every row has a one-column left margin (the playback marker is drawn in it), so x positions in this schema count from column 1 of the raw screen and cursorPos.x is the schema x + 1. Titles are on row 3, content starts on row 5. The text stream is lower case ('tempo', 'b-4', 'ff').",
837
+ emptyCell: "An empty or unused cell displays '--' (2 chars) or '---' (3 chars) or '---00' (command+value). Parsed hexValue will be null for these.",
838
+ relativeHex: "Transpose and some FX values use relative hex: 00 = no change, 01-7F = +1-127, FF-80 = -1-128. See 'Relative and Absolute FX Commands' in the manual.",
839
+ rowIndex: "Row/step indices are parsed from the leftmost characters of currentLine. They are hex for all grid views.",
840
+ titleAsterisk: "A '*' next to the item ID in the screen title (e.g. 'PHRASE 03*') indicates the item is used elsewhere in the chain or song (phrase view); on other views it marks unsaved changes. See 'An asterisk next to the phrase number' in the manual."
841
+ },
842
+ fieldTypes: {
843
+ chainRef: "Reference to a chain number (hex 00-FF). '--' means empty/unused.",
844
+ phraseRef: "Reference to a phrase number (hex 00-FF). '--' means empty/unused.",
845
+ note: "Musical note in M8 format: [A-G][#|-][0-9] e.g. 'C-4', 'A#3'. '---' = empty. 'OFF' = note-off.",
846
+ velocity: "Note velocity 00-7F (hex), from 00 (silence) to 7F (maximum). Default is 64 (hex). '--' = inherit.",
847
+ instrumentRef: "Instrument number 00-7F (hex). '--' = no retrigger (keep current instrument).",
848
+ fxCommand: "3-character sequencer or instrument FX command name, e.g. 'KIL', 'ARP', 'DEL'. '---' = none.",
849
+ fxValue: "2-char hex value 00-FF accompanying an FX command. Interpretation depends on the command.",
850
+ transpose: "Semitone or tick transpose. Relative: 00=none, 01-7F=+1-+127, FF-80=-1--128.",
851
+ ticks: "Number of ticks this step consumes in a groove (hex 01-FF). '00' skips the step. '--' = empty (groove loops at this point).",
852
+ volume: "Volume multiplier 00-FF (hex). Multiplied with the phrase-row velocity. '--' = no override.",
853
+ ppq: "Pulses Per Quarter note: resolution of the groove clock (decimal). One of 24 (default), 48, 96 or 192. Only present on groove row 0; absent on all other rows.",
854
+ instrumentName: "Instrument name string (up to 12 characters). Text field, no hex value.",
855
+ eqSlot: "EQ slot assignment (hex). '--' = no EQ applied to this instrument.",
856
+ rowIndex: "The positional index of this row in the current view (song row, chain position, phrase step, etc.).",
857
+ noteInterval: "Scale row label: one of the 12 note intervals C, C#, D, D#, E, F, F#, G, G#, A, A#, B (sharps use '#', naturals may render a dim '-' pad, e.g. 'C-').",
858
+ onOff: "Toggle field displayed as 'ON' or 'OFF'; the scale view 'EN' column shows '--' for a note out of the scale. Used by the scale view 'EN' column.",
859
+ semitoneOffset: "Signed semitone offset in decimal notation with two decimals (-24.00 to +24.00), e.g. '00.00', '-00.50', '12.00'. Used by the scale view 'OFFSET' column.",
860
+ swing: "Groove swing amount as a percentage with one decimal, e.g. '50.0%' = straight timing, higher = swung. Shown on every second groove row (0, 2, 4, …): the swing of that pair of steps (ticks 07,05 → 58.3%).",
861
+ instrumentType: "Instrument engine chosen with the TYPE field of the Instrument view: WAVSYNTH, MACROSYN, SAMPLER, FMSYNTH, HYPERSYNTH, EXTERNAL INST, MIDI OUT (NONE for an unused slot). Text value; changes which parameters the view shows.",
862
+ name: "Free-text name field (instrument, song, sample, directory). Up to 12 characters; a row of dashes means unnamed.",
863
+ parameter: "Editable value of a named parameter (CUTOFF, TEMPO, GAIN, …). Its 'format' says how to read it: hex byte, 'XX:YY' pair, decimal, decimal with a fraction, or free text.",
864
+ option: "Choice from a list, shown as its name, optionally preceded by its index in hex (e.g. '00CHORUS', 'STEREO', 'BELL'). hexValue holds the index when there is one, text holds the name.",
865
+ action: "Button-like text that does something when [EDIT] is pressed on it (LOAD, SAVE, RENDER, SETTINGS, VIEW INST. POOL, EQ, …). The text is the caption.",
866
+ fileEntry: "Row of a file browser: '/name' is a directory, '/..' the parent directory, anything else a file (e.g. 'Bass.m8i')."
867
+ },
868
+ views: /* @__PURE__ */ JSON.parse("{\"song\":{\"title\":\"Song View\",\"type\":\"grid\",\"description\":\"Arranges chains across 8 independent tracks to form the song structure. Each row is a song position (up to 256 rows); each column is a track. Each track advances vertically until it reaches an empty column ('--'), then loops back to row 00. Chains containing no notes are grayed out; chains '00' or 'FE' are commonly used as silent spacers. On row '00', [UP][UP] enters track reordering mode.\",\"rowField\":{\"x\":0,\"width\":2,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"songRow\",\"description\":\"Song row 00-FF. Each track loops back to row 00 when it reaches '--'.\"},\"columns\":[{\"x\":3,\"width\":2,\"key\":\"track1\",\"type\":\"chainRef\",\"label\":\"Track 1\",\"meta\":{\"trackIndex\":1}},{\"x\":6,\"width\":2,\"key\":\"track2\",\"type\":\"chainRef\",\"label\":\"Track 2\",\"meta\":{\"trackIndex\":2}},{\"x\":9,\"width\":2,\"key\":\"track3\",\"type\":\"chainRef\",\"label\":\"Track 3\",\"meta\":{\"trackIndex\":3}},{\"x\":12,\"width\":2,\"key\":\"track4\",\"type\":\"chainRef\",\"label\":\"Track 4\",\"meta\":{\"trackIndex\":4}},{\"x\":15,\"width\":2,\"key\":\"track5\",\"type\":\"chainRef\",\"label\":\"Track 5\",\"meta\":{\"trackIndex\":5}},{\"x\":18,\"width\":2,\"key\":\"track6\",\"type\":\"chainRef\",\"label\":\"Track 6\",\"meta\":{\"trackIndex\":6}},{\"x\":21,\"width\":2,\"key\":\"track7\",\"type\":\"chainRef\",\"label\":\"Track 7\",\"meta\":{\"trackIndex\":7}},{\"x\":24,\"width\":2,\"key\":\"track8\",\"type\":\"chainRef\",\"label\":\"Track 8\",\"meta\":{\"trackIndex\":8}}]},\"chain\":{\"title\":\"Chain View\",\"type\":\"grid\",\"description\":\"An ordered list of up to 16 phrases (256 steps). The first '--' phrase ends the chain and the song continues on to the next chain in the song view. The 'TSP' column optionally transposes every note in the phrase in semitones (relative hex).\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"chainPos\",\"description\":\"Position within the chain (0-F, max 16 entries).\"},\"columns\":[{\"x\":2,\"width\":2,\"key\":\"phrase\",\"type\":\"phraseRef\",\"label\":\"Phrase (PH)\",\"description\":\"Phrase number to play at this chain position.\"},{\"x\":5,\"width\":2,\"key\":\"transpose\",\"type\":\"transpose\",\"label\":\"Transpose (TSP)\",\"description\":\"Optional note transpose in semitones applied to every note in the phrase (relative hex).\"}]},\"phrase\":{\"title\":\"Phrase View\",\"type\":\"grid\",\"description\":\"A 16-step sequence. Each step holds a note, velocity, instrument assignment, and up to 3 FX command+value pairs. Notes cover 11 octaves (the full 128 MIDI note range); velocity defaults to 64 (hex); an empty instrument column plays the note without retriggering the instrument. A '*' next to the phrase number indicates the phrase is used elsewhere in the chain or song.\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"step\",\"description\":\"Step index 0-F (16th note by default; altered by groove).\"},\"columns\":[{\"x\":2,\"width\":3,\"key\":\"note\",\"type\":\"note\",\"label\":\"Note (N)\"},{\"x\":6,\"width\":2,\"key\":\"vel\",\"type\":\"velocity\",\"label\":\"Velocity (V)\"},{\"x\":9,\"width\":2,\"key\":\"inst\",\"type\":\"instrumentRef\",\"label\":\"Instrument (I)\"},{\"x\":12,\"width\":3,\"key\":\"fx1cmd\",\"type\":\"fxCommand\",\"label\":\"FX1 Command\"},{\"x\":15,\"width\":2,\"key\":\"fx1val\",\"type\":\"fxValue\",\"label\":\"FX1 Value\"},{\"x\":18,\"width\":3,\"key\":\"fx2cmd\",\"type\":\"fxCommand\",\"label\":\"FX2 Command\"},{\"x\":21,\"width\":2,\"key\":\"fx2val\",\"type\":\"fxValue\",\"label\":\"FX2 Value\"},{\"x\":24,\"width\":3,\"key\":\"fx3cmd\",\"type\":\"fxCommand\",\"label\":\"FX3 Command\"},{\"x\":27,\"width\":2,\"key\":\"fx3val\",\"type\":\"fxValue\",\"label\":\"FX3 Value\"}]},\"table\":{\"title\":\"Table View\",\"type\":\"grid\",\"description\":\"A 16-step mini-sequencer that runs alongside an instrument. Used for arpeggios, multi-stage envelopes, and automated FX. The 'N' column transposes the instrument for the step (relative hex); 'V' multiplies with the phrase-row velocity. Each instrument has a dedicated table plus 128 assignable tables (TBL command). FX columns can run at independent speeds via TIC; a TIC on the last table row affects all rows. HOP shortens or loops the table.\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"step\",\"description\":\"Table step 0-F. Loops from bottom unless a HOP command redirects.\"},\"columns\":[{\"x\":2,\"width\":2,\"key\":\"transpose\",\"type\":\"transpose\",\"label\":\"Transpose (N)\",\"description\":\"Transposes the instrument for this step (relative hex). '--' = no transpose.\"},{\"x\":5,\"width\":2,\"key\":\"volume\",\"type\":\"volume\",\"label\":\"Volume (V)\",\"description\":\"Volume multiplier applied on top of the phrase velocity (hex). '--' = no override.\"},{\"x\":8,\"width\":3,\"key\":\"fx1cmd\",\"type\":\"fxCommand\",\"label\":\"FX1 Command\",\"description\":\"3-char command name for table FX slot 1.\"},{\"x\":11,\"width\":2,\"key\":\"fx1val\",\"type\":\"fxValue\",\"label\":\"FX1 Value\",\"description\":\"Value for table FX1.\"},{\"x\":14,\"width\":3,\"key\":\"fx2cmd\",\"type\":\"fxCommand\",\"label\":\"FX2 Command\",\"description\":\"3-char command name for table FX slot 2.\"},{\"x\":17,\"width\":2,\"key\":\"fx2val\",\"type\":\"fxValue\",\"label\":\"FX2 Value\",\"description\":\"Value for table FX2.\"},{\"x\":20,\"width\":3,\"key\":\"fx3cmd\",\"type\":\"fxCommand\",\"label\":\"FX3 Command\",\"description\":\"3-char command name for table FX slot 3.\"},{\"x\":23,\"width\":2,\"key\":\"fx3val\",\"type\":\"fxValue\",\"label\":\"FX3 Value\",\"description\":\"Value for table FX3.\"}]},\"groove\":{\"title\":\"Groove View\",\"type\":\"grid\",\"description\":\"Defines the timing of each phrase step in ticks. Used to create swing, shuffle, triplets, and other rhythmic feels. At the default 24 PPQ a step is 6 ticks and a full phrase totals 96 ticks. The groove loops when a '--' entry is reached; a '00' entry skips the phrase step. Row 0 also holds the PPQ resolution (24/48/96/192) and the SWING percentage; editing the row 0 tick value alters rows 0 and 1 together (a common swing setting is 07,05 or 08,04).\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"step\",\"description\":\"Groove step 0-F. The groove loops when a '--' entry is reached.\"},\"columns\":[{\"x\":2,\"width\":2,\"key\":\"ticks\",\"type\":\"ticks\",\"label\":\"Ticks (TIC)\",\"description\":\"Number of ticks this step takes. Default is 06 (at 24 PPQN = 1 sixteenth note). '00' skips the step.\"},{\"x\":6,\"width\":2,\"key\":\"ppq\",\"type\":\"ppq\",\"label\":\"PPQ\",\"description\":\"Pulses Per Quarter note: 24, 48, 96 or 192. Only present on groove row 0; absent on all other rows.\"},{\"x\":10,\"width\":5,\"key\":\"swing\",\"type\":\"swing\",\"label\":\"Swing\",\"description\":\"Swing amount as a percentage (e.g. '50.0%' = straight). Shown on every second row (0, 2, 4, …) for that pair of steps; empty on the others.\"}]},\"scale\":{\"title\":\"Scale View\",\"type\":\"grid\",\"description\":\"Defines and quantizes note-related events (project/chain transpose, note entry, ARP, PIT, and the MIDIOUT instrument CHD and ADD commands). Up to 16 user-defined scales per project; scale '00' is the default for all 8 tracks and can be overridden per track with SCA/SCG FX commands. Each of the 12 note intervals can be enabled ('EN' = ON) with an optional detuning offset (-24.00 to +24.00 semitones); a note out of the scale reads '-- -- --'. Rows that are not column-mapped (Model:02, fw 6.5): KEY under the title (global root note for the default scale, the same on every scale), then after the intervals TUNE (A4 reference in Hz, e.g. '440.00'), NAME (up to 16 characters, 92 factory scales load from /Scales/Factory), and the LOAD/SAVE actions.\",\"columns\":[{\"x\":0,\"width\":2,\"key\":\"interval\",\"type\":\"noteInterval\",\"label\":\"Note Interval\",\"description\":\"Note interval row label: C, C#, D, D#, E, F, F#, G, G#, A, A#, B.\"},{\"x\":3,\"width\":3,\"key\":\"en\",\"type\":\"onOff\",\"label\":\"Enabled (EN)\",\"description\":\"Enables this note interval in the scale: 'ON', or '--' when the note is out of the scale (the device draws the row '-- -- --').\"},{\"x\":6,\"width\":6,\"key\":\"offset\",\"type\":\"semitoneOffset\",\"label\":\"Offset (OFFSET)\",\"description\":\"Detune offset in semitones, decimal with two decimals (-24.00 to +24.00), e.g. '00.00'.\"}]},\"instrumentpool\":{\"title\":\"Instrument Pool\",\"type\":\"grid\",\"description\":\"Lists all 128 instrument slots with their mix routing. Each row shows the instrument name (up to 12 chars, '------------' = empty slot) and its dry/FX send levels. Mixer-related settings can be adjusted per instrument here; [EDIT] on an empty slot loads an instrument or sample.\",\"rowField\":{\"x\":0,\"width\":2,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"instIndex\",\"description\":\"Instrument slot index 00-7F.\"},\"columns\":[{\"x\":3,\"width\":12,\"key\":\"name\",\"type\":\"instrumentName\",\"label\":\"Instrument Name\",\"description\":\"Name of the instrument in this slot (up to 12 chars). '------------' = empty slot.\"},{\"x\":16,\"width\":2,\"key\":\"dry\",\"type\":\"volume\",\"label\":\"Dry (DRY)\",\"description\":\"Direct output volume for this instrument (hex 00-FF).\"},{\"x\":19,\"width\":2,\"key\":\"mx\",\"type\":\"volume\",\"label\":\"ModFX (MX)\",\"description\":\"ModFX send level (hex 00-FF).\"},{\"x\":22,\"width\":2,\"key\":\"de\",\"type\":\"volume\",\"label\":\"Delay (DE)\",\"description\":\"Delay send level (hex 00-FF).\"},{\"x\":25,\"width\":2,\"key\":\"rv\",\"type\":\"volume\",\"label\":\"Reverb (RV)\",\"description\":\"Reverb send level (hex 00-FF).\"},{\"x\":28,\"width\":2,\"key\":\"eq\",\"type\":\"eqSlot\",\"label\":\"EQ Slot (EQ)\",\"description\":\"EQ slot assignment. '--' = no EQ.\"}]},\"inst\":{\"title\":\"Instrument View\",\"type\":\"parameter\",\"description\":\"Per-instrument parameters for the selected slot: TYPE (Wavsynth, Macrosynth, Sampler, FM Synth, Hypersynth, External Instrument, MIDI Out), LOAD/SAVE presets, NAME, TRANSP. (enable/disable transposition from scales and transpose columns), TABLE TIC (default table speed), and EQ slot, plus the synth engine parameters. Almost every parameter has an associated FX command; highlighting a parameter and navigating to the phrase or table view sets that parameter's FX command as default.\"},\"instmods\":{\"title\":\"Instrument Modulation View\",\"type\":\"parameter\",\"description\":\"Four configurable modulation slots per instrument: AHD envelope, ADSR envelope, DRUM envelope, LFO, TRIG envelope, or TRACKING, each with a destination parameter (DEST) and optional source (SRC).\"},\"mixer\":{\"title\":\"Mixer View\",\"type\":\"parameter\",\"description\":\"Mix levels for the 8 tracks, the 3 send effects (MX/DE/RE), the analog input (INPUT, with dual-mono mode) and USB input, the DJ filter (DJF), OTT compression, main EQ, limiter (LIM) and main volume (MIX). All signals are summed through OTT, main EQ, limiter, DJ filter and main volume. Most values are also exposed as FX commands (VMV, VMX, VDE, VRE, VT1-8, DJC/DJR/DJT, IVO/IMX/IDE/IRV, USB, ...).\"},\"project\":{\"title\":\"Project View\",\"type\":\"parameter\",\"description\":\"Song-wide settings such as tempo, project transpose (identical to the TSP FX command), and LIVE QUANTIZE behavior for cueing tracks in live mode. [SHIFT]+[OPTION] stores a song snapshot; [SHIFT]+[EDIT] recalls it.\"},\"effectsettings\":{\"title\":\"Effect Settings View\",\"type\":\"parameter\",\"description\":\"Settings for the 3 send effects: ModFX type/depth/frequency/width, Delay time/feedback/width, and Reverb size/decay/modulation/width/freeze — all addressable via the XMT-XMW, XDT-XDR and XRS-XRZ FX commands.\"},\"mixeq\":{\"title\":\"Mix EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the main mix EQ slot (assign with the EQM FX command). Selecting EQ in the Mixer view and pressing [EDIT] or [SHIFT]+[RIGHT] opens this view.\"},\"modfxeq\":{\"title\":\"ModFX EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the ModFX send path.\"},\"delayeq\":{\"title\":\"Delay EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the Delay send path.\"},\"reverbeq\":{\"title\":\"Reverb EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the Reverb send path.\"},\"mixscope\":{\"title\":\"Mix Scope View\",\"type\":\"parameter\",\"description\":\"Graphical scope of the main mix activity. Opened from the Mixer view with MIX selected via [EDIT] or [SHIFT]+[RIGHT].\"},\"limiterscope\":{\"title\":\"Limiter Scope View\",\"type\":\"parameter\",\"description\":\"Graphical scope of the limiter's compression activity (the white line on the MIX indicator). Opened from the Mixer view with LIM selected via [EDIT] or [SHIFT]+[RIGHT].\"},\"systemsettings\":{\"title\":\"System Settings View\",\"type\":\"parameter\",\"description\":\"System-level settings for the device.\"},\"loadinstrument\":{\"title\":\"Load Instrument\",\"type\":\"parameter\",\"description\":\"File browser for loading an instrument preset from the SD card ([EDIT] on an empty instrument slot, or LOAD in the Instrument view).\"},\"selectsavedirectory\":{\"title\":\"Select Save Directory\",\"type\":\"parameter\",\"description\":\"File browser directory-selection step when saving a preset or project.\"},\"createdirectory\":{\"title\":\"Create Directory\",\"type\":\"parameter\",\"description\":\"File browser dialog for creating a new directory on the SD card.\"}}"),
869
+ fxCommands: /* @__PURE__ */ JSON.parse("{\"sequencer\":{\"ARP\":{\"description\":\"Arpeggio: plays 3 notes rapidly. X=first interval semitones, Y=second interval semitones.\"},\"ARC\":{\"description\":\"Arpeggio Config: X=direction and number of octaves, Y=speed in ticks.\"},\"CHA\":{\"description\":\"Chance: X=probability left of command (0=never, F=always), Y=probability right. In a table: probability for everything to the left of CHA from 00 (never) to FF (always).\"},\"DEL\":{\"description\":\"Delay: in phrase delays entire row by XX ticks; in table delays table playhead.\"},\"GRV\":{\"description\":\"Groove: sets groove number XX for the current track.\"},\"GGR\":{\"description\":\"Global Groove: sets groove XX for all tracks.\"},\"HOP\":{\"description\":\"Hop: in phrase jumps to row Y of next phrase; HOPFF stops track. In table loops to row Y X times.\"},\"INS\":{\"description\":\"Instrument: changes active instrument to XX as an FX command.\"},\"KIL\":{\"description\":\"Kill: stops the playing instrument after XX ticks.\"},\"RND\":{\"description\":\"Random: randomises the preceding FX command. X=left range, Y=right range (0=none, F=full).\"},\"RNL\":{\"description\":\"Randomise Left: randomises the FX command to the left (X=left range, Y=right range). In the first column RNL randomises the note and instrument number in phrase, or note and velocity in table.\"},\"RET\":{\"description\":\"Retrig: retriggers current row. Y≠0: retrig every Y ticks with X volume ramp. Y=0: single retrig after X ticks.\"},\"REP\":{\"description\":\"Repeat: repeats and increments the last FX command by XX each step. Active until a new command or REP00 stops it; it does not need to appear on every row. In a table: loops the last row XX times. A double-caret '^^' appears next to an FX lane while a REP from a previous phrase is active.\"},\"RTO\":{\"description\":\"Repeat To: use after REP to hold at the given value (the min/max REP stops at). In a table, used alone: loops until the relative value is reached.\"},\"RMX\":{\"description\":\"Remix: repositions the phrase playhead (Y) on tracks to the left (selected by X).\"},\"NTH\":{\"description\":\"Nth Trigger: conditional trigger on the loop count. X=affects what is left of the command, Y=what is right (in a table: what is left).\"},\"PSL\":{\"description\":\"Pitch Slide (portamento): slides to the given note over XX ticks.\"},\"PBN\":{\"description\":\"Pitch Bend: continuous pitch slide up (00-7F) or down (80-FF).\"},\"PVB\":{\"description\":\"Vibrato: X=speed, Y=depth.\"},\"PVX\":{\"description\":\"Extreme Vibrato: like PVB but with higher range.\"},\"SCA\":{\"description\":\"Track Scale: X=key signature, Y=scale number for this track.\"},\"SCG\":{\"description\":\"Global Scale: X=key, Y=scale number for the whole song.\"},\"SNG\":{\"description\":\"Song Hop: moves this track's song position by XX rows relative to current. Ignored if the resulting song position is not playable.\"},\"SED\":{\"description\":\"Random Seed: sets the random seed XX for this track.\"},\"TBL\":{\"description\":\"Table: assigns table number XX to the current instrument.\"},\"THO\":{\"description\":\"Table Hop: jumps all table columns to position 0X.\"},\"TIC\":{\"description\":\"Table Tick: sets the table tick rate. 00 = increment row on each instrument trigger; 01-FB = ticks per row; FC = Octave Map (playing octave maps to table row); FD = Velocity Map; FE = Note Map (pair with HOP00 on row 0C to limit to 12 notes/octave); FF = increment at 200 Hz. In a table each FX column can run independently; a TIC on the last table row affects all rows.\"},\"TBX\":{\"description\":\"Aux Table: runs table XX in parallel with the instrument table. 00=stop aux table. A TIC in the same row as a TBX affects the auxiliary table's tick rate.\"},\"TPO\":{\"description\":\"Tempo: sets song BPM to XX. In a table the tempo is relative.\"},\"TSP\":{\"description\":\"Global Song Transpose: transposes the entire song by XX semitones. Identical to the transpose value in the Project View.\"},\"NXT\":{\"description\":\"Next Track Trigger: plays instrument XX on the track to the right using the current note/velocity. Does not work on track 8.\"},\"OFF\":{\"description\":\"Note Off: stops the playing instrument after XX ticks. Triggers ADSR release stage.\"},\"MTT\":{\"description\":\"Micro Time: adjusts row playback timing in positive or negative sub-tick (1/8 tick) amounts. Negative amounts do not work on the first phrase row. Currently has no action in tables.\"}},\"instrument\":{\"VOL\":{\"description\":\"Volume (relative).\",\"relative\":true},\"PIT\":{\"description\":\"Pitch in semitones (relative).\",\"relative\":true},\"FIN\":{\"description\":\"Fine tune: pitch shift of up to about one semitone (relative).\",\"relative\":true},\"PLY\":{\"description\":\"Sampler play mode (FWD, REV, loop modes…).\"},\"STA\":{\"description\":\"Sampler start position.\"},\"LOP\":{\"description\":\"Sampler loop start (relative).\",\"relative\":true},\"LEN\":{\"description\":\"Sampler length (relative).\",\"relative\":true},\"DEG\":{\"description\":\"Degrade amount (relative).\",\"relative\":true},\"SLI\":{\"description\":\"Sampler slice to play.\"},\"ERR\":{\"description\":\"Error: randomizes pitch and parameters by the given amount.\"},\"FLT\":{\"description\":\"Filter type.\"},\"FIL\":{\"description\":\"Filter type.\"},\"ALG\":{\"description\":\"FM algorithm.\"},\"FM1\":{\"description\":\"FM modulation amount 1 (relative).\",\"relative\":true},\"FM2\":{\"description\":\"FM modulation amount 2 (relative).\",\"relative\":true},\"FM3\":{\"description\":\"FM modulation amount 3 (relative).\",\"relative\":true},\"FM4\":{\"description\":\"FM modulation amount 4 (relative).\",\"relative\":true},\"SNC\":{\"description\":\"Sync: on an FM synth the FM sync phase position; on a Hypersynth it offsets the sync of the oscillators by the given amount.\"},\"CRD\":{\"description\":\"Hypersynth chord set.\"},\"CVO\":{\"description\":\"Hypersynth chord voice offset: X=voice, Y=offset.\"},\"SWM\":{\"description\":\"Hypersynth oscillator swarm (relative).\",\"relative\":true},\"WID\":{\"description\":\"Hypersynth stereo width (relative).\",\"relative\":true},\"SUB\":{\"description\":\"Hypersynth sub-oscillator amount (relative).\",\"relative\":true},\"OSC\":{\"description\":\"Macrosynth oscillator shape.\"},\"TBR\":{\"description\":\"Macrosynth timbre (relative).\",\"relative\":true},\"COL\":{\"description\":\"Macrosynth color (relative).\",\"relative\":true},\"RED\":{\"description\":\"Macrosynth bit reduction (relative).\",\"relative\":true},\"TRG\":{\"description\":\"Macrosynth trigger/strike.\"},\"CUT\":{\"description\":\"Filter cutoff (relative).\",\"relative\":true},\"RES\":{\"description\":\"Filter resonance (relative).\",\"relative\":true},\"AMP\":{\"description\":\"Amp level (relative).\",\"relative\":true},\"LIM\":{\"description\":\"Limit mode: how the amp stage clips.\"},\"PAN\":{\"description\":\"Pan (relative).\",\"relative\":true},\"DRY\":{\"description\":\"Dry volume: the instrument's level in the main mix.\"},\"SMX\":{\"description\":\"Send to ModFX.\"},\"SDL\":{\"description\":\"Send to Delay.\"},\"SRV\":{\"description\":\"Send to Reverb.\"},\"CHD\":{\"description\":\"Chord (MIDIOUT instrument): triggers a chord. Quantized by the active scale.\"},\"ADD\":{\"description\":\"Add Note (MIDIOUT instrument): adds a note to the chord. Quantized by the active scale.\"}},\"modulation\":{\"EA\":{\"description\":\"Envelope amount of modulator N (relative).\",\"relative\":true},\"AT\":{\"description\":\"Envelope attack of modulator N.\"},\"HO\":{\"description\":\"Envelope hold of modulator N.\"},\"DE\":{\"description\":\"Envelope decay of modulator N.\"},\"ET\":{\"description\":\"Envelope trigger of modulator N: retriggers the envelope.\"},\"LA\":{\"description\":\"LFO amount of modulator N (relative).\",\"relative\":true},\"LO\":{\"description\":\"LFO oscillator shape of modulator N.\"},\"LS\":{\"description\":\"LFO trigger settings of modulator N.\"},\"LF\":{\"description\":\"LFO frequency of modulator N.\"},\"LT\":{\"description\":\"LFO trigger of modulator N: at the given offset, resets the LFO to that position.\"}},\"mixer\":{\"VMV\":{\"description\":\"Main Volume: sets the main song output volume.\"},\"VMX\":{\"description\":\"ModFX Volume.\"},\"VDE\":{\"description\":\"Delay Volume.\"},\"VRE\":{\"description\":\"Reverb Volume.\"},\"VT1\":{\"description\":\"Track 1 Volume.\"},\"VT2\":{\"description\":\"Track 2 Volume.\"},\"VT3\":{\"description\":\"Track 3 Volume.\"},\"VT4\":{\"description\":\"Track 4 Volume.\"},\"VT5\":{\"description\":\"Track 5 Volume.\"},\"VT6\":{\"description\":\"Track 6 Volume.\"},\"VT7\":{\"description\":\"Track 7 Volume.\"},\"VT8\":{\"description\":\"Track 8 Volume.\"},\"DJC\":{\"description\":\"DJ Filter Cutoff.\"},\"DJR\":{\"description\":\"DJ Filter Resonance.\"},\"DJT\":{\"description\":\"DJ Filter Type.\"},\"EQM\":{\"description\":\"Assign main mix EQ slot.\"},\"EQI\":{\"description\":\"Assign current instrument EQ slot.\"},\"XMT\":{\"description\":\"Send Effect: ModFX mod type and phase position (Effect Settings view).\"},\"XMM\":{\"description\":\"Send Effect: ModFX mod depth (Effect Settings view).\"},\"XMF\":{\"description\":\"Send Effect: ModFX mod frequency (Effect Settings view).\"},\"XMW\":{\"description\":\"Send Effect: ModFX stereo width (Effect Settings view).\"},\"XMR\":{\"description\":\"Send Effect: ModFX to reverb mix (Effect Settings view).\"},\"XDT\":{\"description\":\"Send Effect: Delay time — X = left, Y = right, in large increments (Effect Settings view).\"},\"XDF\":{\"description\":\"Send Effect: Delay feedback (Effect Settings view).\"},\"XDW\":{\"description\":\"Send Effect: Delay stereo width (Effect Settings view).\"},\"XDR\":{\"description\":\"Send Effect: Delay to reverb mix (Effect Settings view).\"},\"XRS\":{\"description\":\"Send Effect: Reverb room size (Effect Settings view).\"},\"XRD\":{\"description\":\"Send Effect: Reverb decay (Effect Settings view).\"},\"XRM\":{\"description\":\"Send Effect: Reverb mod depth (Effect Settings view).\"},\"XRF\":{\"description\":\"Send Effect: Reverb mod frequency (Effect Settings view).\"},\"XRW\":{\"description\":\"Send Effect: Reverb stereo width (Effect Settings view).\"},\"XRZ\":{\"description\":\"Send Effect: Reverb freeze — enabled with a value greater than 00, disabled at 00.\"},\"VIN\":{\"description\":\"Line Input Mixer: analog input volume (Mixer view).\"},\"XRH\":{\"description\":\"Send Effect: Reverb shimmer (Effect Settings view).\"},\"OTT\":{\"description\":\"OTT mix amount (Mixer OTT settings; OTC and OTI default to 80).\"},\"OTC\":{\"description\":\"OTT color (default 80).\"},\"OTI\":{\"description\":\"OTT time (default 80).\"},\"IVO\":{\"description\":\"Alias of VIN as named in the manual: analog input volume (Mixer view).\"},\"IMX\":{\"description\":\"Line Input Mixer: analog input ModFX send (Mixer view).\"},\"IDE\":{\"description\":\"Line Input Mixer: analog input delay send (Mixer view).\"},\"IRV\":{\"description\":\"Line Input Mixer: analog input reverb send (Mixer view). Also referenced as 'IRE' in the manual text.\"},\"IRE\":{\"description\":\"Alias of IRV: analog input reverb send (Mixer view).\"},\"IV2\":{\"description\":\"Line Input Mixer: volume for the 2nd input (right channel in dual-mono input mode, set in the Mixer view).\"},\"IM2\":{\"description\":\"Line Input Mixer: ModFX send for the 2nd input (right channel in dual-mono input mode).\"},\"ID2\":{\"description\":\"Line Input Mixer: delay send for the 2nd input (right channel in dual-mono input mode).\"},\"IR2\":{\"description\":\"Line Input Mixer: reverb send for the 2nd input (right channel in dual-mono input mode).\"},\"USB\":{\"description\":\"USB input volume (Mixer view).\"}}}"),
870
+ instrumentFx: {
871
+ _note: "Instrument commands offered by [EDIT]+[UP/DOWN] (Effect Command Help view) in the Current Instrument section depend on the instrument type; the Instrument Mods section depends on the modulators of that instrument, and Sequencer FX and Mixer/Effects are the same for every type. Lists marked verified were read from a real M8 (Model:02). Wavsynth, External and MIDI Out were not available on that device: their lists are not known here.",
872
+ types: {
873
+ sampler: {
874
+ verified: !0,
875
+ commands: [
876
+ "VOL",
877
+ "PIT",
878
+ "FIN",
879
+ "PLY",
880
+ "STA",
881
+ "LOP",
882
+ "LEN",
883
+ "DEG",
884
+ "FLT",
885
+ "CUT",
886
+ "RES",
887
+ "AMP",
888
+ "LIM",
889
+ "PAN",
890
+ "SLI",
891
+ "ERR",
892
+ "DRY",
893
+ "SMX",
894
+ "SDL",
895
+ "SRV"
896
+ ]
897
+ },
898
+ macrosynth: {
899
+ verified: !0,
900
+ commands: [
901
+ "VOL",
902
+ "PIT",
903
+ "FIN",
904
+ "OSC",
905
+ "TBR",
906
+ "COL",
907
+ "DEG",
908
+ "RED",
909
+ "FIL",
910
+ "CUT",
911
+ "RES",
912
+ "AMP",
913
+ "LIM",
914
+ "PAN",
915
+ "TRG",
916
+ "ERR",
917
+ "DRY",
918
+ "SMX",
919
+ "SDL",
920
+ "SRV"
921
+ ]
922
+ },
923
+ fmsynth: {
924
+ verified: !0,
925
+ commands: [
926
+ "VOL",
927
+ "PIT",
928
+ "FIN",
929
+ "ALG",
930
+ "FM1",
931
+ "FM2",
932
+ "FM3",
933
+ "FM4",
934
+ "FIL",
935
+ "CUT",
936
+ "RES",
937
+ "AMP",
938
+ "LIM",
939
+ "PAN",
940
+ "SNC",
941
+ "ERR",
942
+ "DRY",
943
+ "SMX",
944
+ "SDL",
945
+ "SRV"
946
+ ]
947
+ },
948
+ hypersynth: {
949
+ verified: !0,
950
+ commands: [
951
+ "VOL",
952
+ "PIT",
953
+ "FIN",
954
+ "CRD",
955
+ "CVO",
956
+ "SWM",
957
+ "WID",
958
+ "SUB",
959
+ "FIL",
960
+ "CUT",
961
+ "RES",
962
+ "AMP",
963
+ "LIM",
964
+ "PAN",
965
+ "SNC",
966
+ "ERR",
967
+ "DRY",
968
+ "SMX",
969
+ "SDL",
970
+ "SRV"
971
+ ]
972
+ },
973
+ midiout: {
974
+ verified: !1,
975
+ commands: ["CHD", "ADD"]
976
+ }
977
+ },
978
+ modulation: {
979
+ _note: "Modulator slot N (1-4) offers the commands of its kind. Seen: an envelope slot (AHD) offers EA AT HO DE ET, an LFO slot offers LA LO LS LF LT. Slot kinds vary per instrument (e.g. Sampler 00: env, env, LFO, LFO; Hypersynth 01: env, LFO, LFO, LFO). Other modulator kinds were not captured.",
980
+ envelope: [
981
+ "EA",
982
+ "AT",
983
+ "HO",
984
+ "DE",
985
+ "ET"
986
+ ],
987
+ lfo: [
988
+ "LA",
989
+ "LO",
990
+ "LS",
991
+ "LF",
992
+ "LT"
993
+ ]
994
+ },
995
+ relativeCommands: { description: "Relative commands add to the instrument parameter's current value (01-7F add, 80-FF subtract) and keep accumulating until a note is triggered with a RET command or with an instrument number in the I column; that resets every parameter to the instrument's own value." }
996
+ }
997
+ }, fe = 3, pe = 1.5, me = 12, he = 34, ge = Object.fromEntries(Object.entries(I.views).flatMap(([e, t]) => t.rowField ? [[e, t.rowField.width]] : [])), _e = (e, t) => {
998
+ let n = e ? ge[e] : void 0;
999
+ if (!n) return null;
1000
+ let r = -1, i = -1;
1001
+ return t.forEach((e, t) => {
1002
+ /^[0-9a-f]+$/i.test(e.slice(1, 1 + n)) && (e[1 + n] ?? " ") === " " && (r < 0 && (r = t), i = t);
1003
+ }), r < 0 ? null : {
1004
+ first: r,
1005
+ last: i,
1006
+ numberWidth: n
1007
+ };
1008
+ }, ve = (e, t) => {
1009
+ let n = Math.round((e.x - t.offX) / t.cellW), r = Math.round((e.y - t.offY) / t.cellH), i = Math.max(1, Math.round(e.w / t.cellW));
1010
+ return {
1011
+ x: n * t.cellW + t.offX,
1012
+ y: r * t.cellH + t.offY,
1013
+ w: i * t.cellW,
1014
+ h: t.cellH
1015
+ };
1016
+ }, ye = (e, t) => Math.round((e.y - t.offY) / t.cellH), be = (e, t) => Math.round(e / t.cellW) >= 40 ? he : null, xe = (e, t) => e.map((e, n) => {
1017
+ let r = (t === null ? e : e.slice(0, t)).trim().split(/\s+/)[0] ?? "";
1018
+ return r.length >= 2 && /[g-z]/i.test(r) ? `${n}${r.toLowerCase()}` : "";
1019
+ }).filter(Boolean).join(" "), Se = (e, t, n) => ({
1020
+ x: t * n.cellW + n.offX,
1021
+ y: e * n.cellH + n.offY,
1022
+ w: n.cellW,
1023
+ h: n.cellH
1024
+ }), Ce = (e, t, n, r = () => !1) => {
1025
+ let i = [];
1026
+ return e.forEach((e, a) => {
1027
+ let o = [...` ${(n === null ? e : e.slice(0, n)).slice(1)}`].map((e, t) => e !== " " && r(a, t) ? " " : e).join("");
1028
+ for (let e of o.matchAll(/\S+/g)) {
1029
+ let n = e.index ?? 0, r = e[0];
1030
+ i.push({
1031
+ key: `${a}:${n}:${r.toLowerCase()}`,
1032
+ rect: {
1033
+ x: n * t.cellW + t.offX,
1034
+ y: a * t.cellH + t.offY,
1035
+ w: r.length * t.cellW,
1036
+ h: t.cellH
1037
+ },
1038
+ row: a,
1039
+ col: n,
1040
+ text: r
1041
+ });
1042
+ }
1043
+ }), i;
1044
+ }, we = (e, t, n) => {
1045
+ let r = ye(e, n), i = Math.round((e.x - n.offX) / n.cellW), a = Math.floor((e.x + e.w - 1 - n.offX) / n.cellW);
1046
+ return (t[r] ?? "").slice(Math.max(0, i), a + 1).trim().length > 0;
1047
+ }, Te = (e, t, n, r) => Math.max(0, Math.min(t, r) - Math.max(e, n)), Ee = (e, t, n) => Te(e.x, e.x + e.w, t.x, t.x + t.w) >= Math.min(n.cellW, t.w) / 2, De = (e, t, n) => Te(e.y, e.y + e.h, t.y, t.y + t.h) >= Math.min(n.cellH, t.h) / 2, Oe = (e, t, n) => Ee(e, t, n) && De(e, t, n), ke = (e) => ({
1048
+ x: e.x + e.w / 2,
1049
+ y: e.y + e.h / 2
1050
+ }), L = (e, t) => {
1051
+ let n = ke(e), r = ke(t), i = Math.max(0, Math.abs(n.x - r.x) - (e.w + t.w) / 2 + fe), a = Math.max(0, Math.abs(n.y - r.y) - (e.h + t.h) / 2 + fe);
1052
+ return Math.hypot(i, a) + .01 * Math.hypot(n.x - r.x, n.y - r.y);
1053
+ }, Ae = (e, t) => ({
1054
+ dx: Math.max(0, t.x - e.x, e.x - (t.x + t.w)),
1055
+ dy: Math.max(0, t.y - e.y, e.y - (t.y + t.h))
1056
+ }), je = (e) => ({
1057
+ kind: "point",
1058
+ rect: {
1059
+ x: e.x,
1060
+ y: e.y,
1061
+ w: 1,
1062
+ h: 1
1063
+ }
1064
+ }), Me = (e, t, n) => e.kind === "cell" && F(t) === e.id || Oe(t, e.rect, n), Ne = (e, t, n, r, i = null) => {
1065
+ let a = (e) => !i || e >= i.first && e <= i.last, o = [];
1066
+ for (let [r, i] of e?.cells ?? []) a(ye(i, n)) && we(i, t, n) && o.push({
1067
+ id: r,
1068
+ rect: i
1069
+ });
1070
+ let s = (e, t) => {
1071
+ let r = Se(e, t, n);
1072
+ return o.some((e) => Oe(e.rect, r, n));
1073
+ }, c = (e) => !!i && e.col === 1 && e.text.length <= i.numberWidth;
1074
+ return {
1075
+ cells: o,
1076
+ runs: Ce(t, n, r, s).filter((e) => a(e.row) && !c(e))
1077
+ };
1078
+ }, Pe = (e, t, n, r, i, a = null, o = /* @__PURE__ */ new Set(), s = !1) => {
1079
+ let { cells: c, runs: l } = Ne(t, n, r, i, a), u = c.map((e) => ({
1080
+ goal: {
1081
+ kind: "cell",
1082
+ ...e
1083
+ },
1084
+ area: e.rect
1085
+ }));
1086
+ for (let n of l) {
1087
+ if (t?.unreachable.has(n.key) || o.has(n.key)) continue;
1088
+ let i = Math.min(n.col + n.text.length - 1, Math.max(n.col, Math.floor((e.x - r.offX) / r.cellW)));
1089
+ u.push({
1090
+ goal: {
1091
+ kind: "text",
1092
+ key: n.key,
1093
+ rect: Se(n.row, i, r),
1094
+ run: n.rect,
1095
+ row: n.row
1096
+ },
1097
+ area: n.rect
1098
+ });
1099
+ }
1100
+ let d = n[Math.floor((e.y - r.offY) / r.cellH)] ?? "", f = (e) => e > 0 && (d[e] ?? " ") !== " ", p = Math.floor((e.x - r.offX) / r.cellW);
1101
+ for (; f(p) && (f(p + 1) || f(p + 2));) p += f(p + 1) ? 1 : 2;
1102
+ let m = (t) => {
1103
+ let { dx: n, dy: i } = Ae(e, t);
1104
+ if (i > 0 || n > me * r.cellW) return !1;
1105
+ let a = Math.round((t.x - r.offX) / r.cellW);
1106
+ if (a <= p) return !0;
1107
+ for (let e = p + 1; e < a; e++) if (f(e)) return !1;
1108
+ return !0;
1109
+ }, h = u.some(({ area: e }) => m(e)), g = u.some(({ goal: e }) => e.kind === "cell"), _ = null;
1110
+ for (let { goal: t, area: n } of u) {
1111
+ let { dx: r, dy: i } = Ae(e, n);
1112
+ if (h && !m(n) || s && !h && g && t.kind !== "cell") continue;
1113
+ let a = n.x > e.x, o = Math.hypot(r, i * pe) - (t.kind === "cell" ? .001 : 0) - (a ? .5 : 0), c = ke(n), l = Math.hypot(c.x - e.x, c.y - e.y);
1114
+ (!_ || o < _.score - 1e-6 || Math.abs(o - _.score) <= 1e-6 && l < _.tie) && (_ = {
1115
+ goal: t,
1116
+ score: o,
1117
+ tie: l
1118
+ });
1119
+ }
1120
+ return _?.goal ?? je(e);
1121
+ }, R = {
692
1122
  Left: 128,
693
1123
  Up: 64,
694
1124
  Down: 32,
@@ -697,160 +1127,256 @@ var D = (e) => {
697
1127
  Right: 4,
698
1128
  Opt: 2,
699
1129
  Edit: 1
700
- }, te = ({ left: e, right: t, up: n, down: r, shift: i, play: a, opt: o, edit: s }) => {
1130
+ }, Fe = ({ left: e, right: t, up: n, down: r, shift: i, play: a, opt: o, edit: s }) => {
701
1131
  let c = 0;
702
- return e && (c |= I.Left), t && (c |= I.Right), n && (c |= I.Up), r && (c |= I.Down), i && (c |= I.Shift), a && (c |= I.Play), o && (c |= I.Opt), s && (c |= I.Edit), c;
703
- }, ne = {
704
- up: I.Up,
705
- down: I.Down,
706
- left: I.Left,
707
- right: I.Right
708
- }, re = I.Up | I.Down | I.Left | I.Right | I.Edit | I.Opt | I.Shift, ie = 24, ae = 150, L = .1, oe = (e) => e === "up" || e === "down" ? "vertical" : "horizontal", se = (e) => {
709
- switch (e) {
710
- case "up": return "down";
711
- case "down": return "up";
712
- case "left": return "right";
713
- case "right": return "left";
714
- }
715
- }, R = (e, t) => e ? e.rx === t.rx && e.ry === t.ry && e.rw === t.rw && e.rh === t.rh : !1, ce = (e) => ({
716
- x: e.rx + e.rw / 2,
717
- y: e.ry + e.rh / 2
718
- }), le = (e, t) => t.x >= e.rx - L && t.x <= e.rx + e.rw + L, ue = (e, t) => t.y >= e.ry - L && t.y <= e.ry + e.rh + L, de = (e, t) => {
719
- let n = ce(e), r = n.x - t.x, i = n.y - t.y;
720
- return Math.sqrt(r * r + i * i);
721
- }, fe = (e, t, n) => e === "vertical" ? n.y < t.ry - L ? "up" : n.y > t.ry + t.rh + L ? "down" : null : n.x < t.rx - L ? "left" : n.x > t.rx + t.rw + L ? "right" : null, pe = ({ bus: e, store: t }) => {
722
- let n = null, r = null, i = 0, a = 0, o = !1, s = "vertical", c = {
723
- vertical: !1,
724
- horizontal: !1
725
- }, l = null, u = null, d = !1, f = null, p = [], m = [], h = [], g = () => {
726
- f &&= (clearTimeout(f), null);
727
- }, _ = () => {
728
- g(), f = setTimeout(() => {
729
- O("timeout");
730
- }, ae);
731
- }, v = () => {
732
- r = null, s = "vertical", c = {
733
- vertical: !1,
734
- horizontal: !1
735
- }, l = null, u = null, d = !1, p = [], m = [], h = [], o = !1, a = i & ~re, g();
736
- }, y = () => {
737
- g(), n &&= (n(), null), v();
738
- }, b = () => {
1132
+ return e && (c |= R.Left), t && (c |= R.Right), n && (c |= R.Up), r && (c |= R.Down), i && (c |= R.Shift), a && (c |= R.Play), o && (c |= R.Opt), s && (c |= R.Edit), c;
1133
+ }, Ie = {
1134
+ up: R.Up,
1135
+ down: R.Down,
1136
+ left: R.Left,
1137
+ right: R.Right
1138
+ }, Le = R.Up | R.Down | R.Left | R.Right, Re = R.Edit | R.Opt | R.Shift | R.Play, ze = Le | R.Edit | R.Opt | R.Shift, Be = 48, Ve = 40, He = 16, Ue = 4, We = 150, Ge = 300, Ke = 200, qe = (e, t) => !!e && !!t && e.x === t.x && e.y === t.y && e.w === t.w && e.h === t.h, Je = (e) => {
1139
+ if (e & Re) return null;
1140
+ let t = Object.keys(Ie).filter((t) => e & Ie[t]);
1141
+ return t.length === 1 ? t[0] : null;
1142
+ }, Ye = ({ bus: e, store: t, getScreenLines: n = () => [], cursorMap: r = oe() }) => {
1143
+ let i = null, a = 0, o = 0, s = !1, c = null, l = null, u = () => t.get("cellMetrics"), d = () => be(t.get("systemInfo")?.screenWidth ?? 480, u()), f = () => {
739
1144
  let e = t.get("cursorRect");
740
- return e ? {
741
- rx: e.x,
742
- ry: e.y,
743
- rw: e.w,
744
- rh: e.h
745
- } : null;
1145
+ return e ? ve(e, u()) : null;
1146
+ }, p = () => {
1147
+ let e = t.get("viewName");
1148
+ if (!e || t.get("selectionMode")) return null;
1149
+ let r = t.get("systemInfo");
1150
+ return [
1151
+ e,
1152
+ r?.model ?? t.get("deviceModel") ?? "?",
1153
+ r?.fontMode ?? t.get("fontMode") ?? "?",
1154
+ xe(n(), d())
1155
+ ].join("|");
1156
+ }, m = () => {
1157
+ c &&= (clearTimeout(c), null);
1158
+ }, h = (e = We) => {
1159
+ m(), c = setTimeout(() => {
1160
+ c = null, E("timeout");
1161
+ }, e);
1162
+ }, g = (e) => {
1163
+ if (!i) return;
1164
+ let { resolve: t } = i;
1165
+ i.goal, f(), i = null, m(), s = !1, o = a & ~ze, t(e);
1166
+ }, _ = (t) => {
1167
+ e.commands.sendKeys(o | Ie[t]), e.commands.sendKeys(o);
1168
+ }, v = (e, t, n) => e.goal.kind === "cell" ? t === e.goal.id : !!n && Me(e.goal, n, u()), y = (e, t) => {
1169
+ let n = [], r = e.y + e.h / 2, i = e.x + e.w / 2;
1170
+ return De(e, t.rect, u()) || n.push(t.rect.y + t.rect.h / 2 < r ? "up" : "down"), Ee(e, t.rect, u()) || n.push(t.rect.x + t.rect.w / 2 < i ? "left" : "right"), n;
1171
+ }, b = (e, t, n) => {
1172
+ if (!e.grid) return !1;
1173
+ let r = ye(t, u());
1174
+ return n === "up" && r <= e.grid.first || n === "down" && r >= e.grid.last;
746
1175
  }, x = (e, t) => {
747
- let n = de(e, t), r = le(e, t) || ue(e, t);
748
- m.push({
749
- rect: e,
750
- index: p.length,
751
- dist: n,
752
- axisAligned: r
753
- }), h.push(e), h.length > 4 && h.shift();
754
- }, S = (e) => {
755
- let t = null;
756
- for (let n of m) (!e || n.axisAligned) && (!t || n.dist < t.dist) && (t = n);
757
- if (!t && e) for (let e of m) (!t || e.dist < t.dist) && (t = e);
758
- return t?.index ?? p.length;
759
- }, C = (t) => {
760
- let n = a, r = n | ne[t];
761
- e.commands.sendKeys(r), e.commands.sendKeys(n);
762
- }, w = (e) => {
763
- for (let t = p.length - 1; t >= e; --t) C(se(p[t]));
764
- }, T = () => {
765
- if (h.length < 3 || p.length < 2) return !1;
766
- let e = h[h.length - 1], t = h[h.length - 2], n = h[h.length - 3], r = p[p.length - 1], i = p[p.length - 2];
767
- if (R(e, n) && !R(e, t) && se(i) === r) return !0;
768
- if (h.length >= 4) {
769
- let e = h[h.length - 4], t = h[h.length - 3], n = h[h.length - 2], r = h[h.length - 1];
770
- if (R(e, n) && R(t, r) && !R(e, t)) return !0;
1176
+ let n = F(t), i = e.layoutKey ? r.get(e.layoutKey) : void 0;
1177
+ if (i) {
1178
+ let t = de(i, n, (t) => v(e, t, i.cells.get(t)), e.distrust, (t, n) => b(e, t, n))?.[0];
1179
+ if (t && !(t.guessed && (e.explored >= Ve || e.tried.has(`${n}:${t.dir}`)))) return {
1180
+ dir: t.dir,
1181
+ expected: t.to,
1182
+ guessed: t.guessed
1183
+ };
771
1184
  }
772
- return !1;
773
- }, E = (e, t) => {
774
- let n = le(e, t), r = ue(e, t);
775
- if (n && r) return null;
776
- if (n && !r) return c.vertical ? null : fe("vertical", e, t);
777
- if (r && !n) return c.horizontal ? null : fe("horizontal", e, t);
778
- let i = s, a = i === "vertical" ? "horizontal" : "vertical";
779
- return (c[i] ? null : fe(i, e, t)) || (c[a] ? null : fe(a, e, t));
780
- }, D = (e, t) => {
781
- C(e), p.push(e), l = e, u = t, d = !0, o = !0;
1185
+ let a = i?.moves.get(n) ?? {}, o = L(t, e.goal.rect), s = null;
1186
+ for (let r of y(t, e.goal)) {
1187
+ let c = `${n}:${r}`;
1188
+ if (e.tried.has(c) || b(e, t, r)) continue;
1189
+ let l = e.distrust.has(c) ? void 0 : a[r];
1190
+ if (l === null) s ??= {
1191
+ dir: r,
1192
+ expected: null,
1193
+ guessed: !1
1194
+ };
1195
+ else if (l !== void 0) {
1196
+ let n = i?.cells.get(l);
1197
+ if (n && !se(t, r, n) && L(n, e.goal.rect) < o) return {
1198
+ dir: r,
1199
+ expected: l,
1200
+ guessed: !1
1201
+ };
1202
+ } else return e.explored >= Ve ? null : {
1203
+ dir: r,
1204
+ expected: void 0,
1205
+ guessed: !0
1206
+ };
1207
+ }
1208
+ return s;
1209
+ }, S = (e, t, n) => {
1210
+ _(n.dir), e.steps += 1, n.guessed && (e.explored += 1), e.tried.add(`${F(t)}:${n.dir}`), e.pending = {
1211
+ from: t,
1212
+ dir: n.dir,
1213
+ expected: n.expected,
1214
+ guessed: n.guessed
1215
+ }, s = !0, n.dir, n.expected, h();
1216
+ }, C = (e, t, i) => {
1217
+ if (e.phase === "seek") {
1218
+ if (e.goal.kind === "text" && Oe(t, e.goal.run, u())) {
1219
+ g(!0);
1220
+ return;
1221
+ }
1222
+ if (i && e.goal.kind === "text" && e.layoutKey) {
1223
+ let { row: i, run: a } = e.goal, o = De(t, a, u()), s = a.x + a.w / 2 < t.x + t.w / 2, c = ye(t, u()), l = i < c ? "up" : "down", f = Math.abs(i - c) === 1 && r.get(e.layoutKey)?.moves.get(F(t))?.[l] === null, p = o || f ? Ne(r.get(e.layoutKey), n(), u(), d(), e.grid).runs.filter((e) => e.row === i).filter((e) => !o || (s ? e.rect.x + e.rect.w <= t.x + 3 : e.rect.x >= t.x + t.w - 3)).map((e) => e.key) : [];
1224
+ p.includes(e.goal.key) || p.push(e.goal.key);
1225
+ for (let t of p) e.failedTexts.add(t), r.markUnreachable(e.layoutKey, t);
1226
+ let m = Pe(e.point, r.get(e.layoutKey), n(), u(), d(), e.grid, e.failedTexts, !0);
1227
+ if (e.goals < Ue && m.kind !== "point" && m.rect !== e.goal.rect) {
1228
+ e.goal, e.goal = m, e.goals += 1, e.explored = 0, e.tried.clear(), w(e);
1229
+ return;
1230
+ }
1231
+ }
1232
+ let a = {
1233
+ x: e.point.x,
1234
+ y: e.point.y,
1235
+ w: 1,
1236
+ h: 1
1237
+ }, o = Ne(e.layoutKey ? r.get(e.layoutKey) : void 0, n(), u(), d(), e.grid).cells, s = t;
1238
+ if (o.length > 0) for (let e of o) L(e.rect, a) < L(s, a) - 1e-6 && (s = e.rect);
1239
+ else for (let t of e.visited.values()) L(t, e.goal.rect) < L(s, e.goal.rect) - 1e-6 && (s = t);
1240
+ if (!qe(s, t)) {
1241
+ e.goal, e.phase = "return", e.goal = {
1242
+ kind: "cell",
1243
+ id: F(s),
1244
+ rect: s
1245
+ }, e.steps = 0, e.explored = 0, e.tried.clear(), w(e);
1246
+ return;
1247
+ }
1248
+ }
1249
+ g(!1);
782
1250
  };
783
- function O(e) {
784
- if (!r) return;
785
- let t = b();
786
- if (!t) return;
787
- let n = r;
788
- if ((m.length === 0 || !R(m[m.length - 1].rect, t)) && x(t, n), T()) {
789
- w(S(!0)), y();
1251
+ function w(e) {
1252
+ let t = f();
1253
+ if (!t) {
1254
+ g(!1);
790
1255
  return;
791
1256
  }
792
- if (le(t, n) && ue(t, n)) {
793
- y();
1257
+ if (Me(e.goal, t, u())) {
1258
+ g(e.phase === "seek");
794
1259
  return;
795
1260
  }
796
- if (p.length >= ie) {
797
- w(S(!0)), y();
1261
+ if (e.steps >= (e.phase === "seek" ? Be : He)) {
1262
+ C(e, t, !1);
798
1263
  return;
799
1264
  }
800
- if (d && l && u) {
801
- if (!R(u, t)) d = !1, o = !1, c[oe(l)] = !1;
802
- else if (e === "timeout") {
803
- let e = oe(l);
804
- c[e] = !0, s = e === "vertical" ? "horizontal" : "vertical", d = !1, o = !1;
805
- } else return;
806
- }
807
- let i = E(t, n);
808
- if (i) s = oe(i), D(i, t), _();
1265
+ let n = x(e, t);
1266
+ n ? S(e, t, n) : C(e, t, e.explored < Ve);
1267
+ }
1268
+ let T = (e, t, n) => {
1269
+ e.pending = null, m(), s = !1;
1270
+ let i = p();
1271
+ e.layoutKey && i === e.layoutKey && r.recordMove(e.layoutKey, t.from, t.dir, n);
1272
+ let a = n ? F(n) : null;
1273
+ !t.guessed && t.expected !== void 0 && a !== t.expected && e.distrust.add(`${F(t.from)}:${t.dir}`), n && e.visited.set(F(n), n);
1274
+ };
1275
+ function E(e) {
1276
+ let n = i;
1277
+ if (!n) return;
1278
+ let r = f();
1279
+ if (!r) g(!1);
809
1280
  else {
810
- if (e === "timeout") {
811
- y();
812
- return;
1281
+ if (n.pending) {
1282
+ if (!qe(n.pending.from, r)) T(n, n.pending, r);
1283
+ else if (e === "timeout" && !n.pending.graced) {
1284
+ n.pending.graced = !0, h(Ge);
1285
+ return;
1286
+ } else if (e === "timeout") T(n, n.pending, null);
1287
+ else return;
813
1288
  }
814
- w(S(!0)), y();
1289
+ t.get("viewName") === n.viewName ? w(n) : g(!1);
815
1290
  }
816
1291
  }
817
- let k = (e) => {
818
- i = e.keys ?? 0, r || (a = i & ~re), r && o && (o = !1, e.keys, d && _());
1292
+ let D = (e) => {
1293
+ if (a = e.keys ?? 0, i) {
1294
+ l = null, s && (s = !1, i.pending && h());
1295
+ return;
1296
+ }
1297
+ o = a & ~ze;
1298
+ let t = Je(a);
1299
+ t ? (!l || l.dir !== t || l.releasedAt !== null) && (l = {
1300
+ dir: t,
1301
+ from: f(),
1302
+ layoutKey: p(),
1303
+ releasedAt: null
1304
+ }) : a === 0 && l ? l.releasedAt ??= Date.now() : l = null;
1305
+ }, O = () => {
1306
+ if (i) {
1307
+ E("rect");
1308
+ return;
1309
+ }
1310
+ let e = f(), t = p();
1311
+ if (!e || !t) {
1312
+ l = null;
1313
+ return;
1314
+ }
1315
+ let a = l;
1316
+ if (a?.from && a.layoutKey === t && (a.releasedAt === null || Date.now() - a.releasedAt <= Ke)) {
1317
+ let i = n();
1318
+ !qe(a.from, e) && we(a.from, i, u()) && we(e, i, u()) && r.recordMove(t, a.from, a.dir, e), a.from = e;
1319
+ } else l = null;
819
1320
  };
820
- e.protocol.eventBus.on("key", k);
821
- let A = t.sub("cursorRect", () => {
822
- r && O("rect");
823
- });
1321
+ e.protocol.eventBus.on("key", D);
1322
+ let k = t.sub("cursorRect", O);
824
1323
  return {
825
- navigateTo: (e) => new Promise((i) => {
826
- if (!t.get("cursorPos")) {
827
- i();
1324
+ navigateTo: (e) => new Promise((s) => {
1325
+ let m = f();
1326
+ if (!t.get("cursorPos") || !m) {
1327
+ s(!1);
828
1328
  return;
829
1329
  }
830
- n &&= (n(), null), v(), n = i, r = e;
831
- let a = b();
832
- a ? (x(a, e), O("start")) : y();
1330
+ let g = i?.pending ?? null;
1331
+ if (i) {
1332
+ let e = i;
1333
+ i = null, e.resolve(!1);
1334
+ }
1335
+ let _ = p(), v = _ ? r.get(_) : void 0, y = n(), b = _e(t.get("viewName"), y), x = t.get("selectionMode") ? je(e) : Pe(e, v, y, u(), d(), b);
1336
+ _ && we(m, y, u()) && r.recordCell(_, m), g || (o = a & ~ze), l = null;
1337
+ let S = {
1338
+ point: e,
1339
+ grid: b,
1340
+ goal: x,
1341
+ goals: 1,
1342
+ phase: "seek",
1343
+ viewName: t.get("viewName"),
1344
+ layoutKey: _,
1345
+ steps: 0,
1346
+ explored: 0,
1347
+ tried: /* @__PURE__ */ new Set(),
1348
+ distrust: /* @__PURE__ */ new Set(),
1349
+ visited: /* @__PURE__ */ new Map([[F(m), m]]),
1350
+ failedTexts: /* @__PURE__ */ new Set(),
1351
+ pending: g ? {
1352
+ ...g,
1353
+ expected: void 0,
1354
+ guessed: !0
1355
+ } : null,
1356
+ resolve: s
1357
+ };
1358
+ i = S, g ? c || h() : w(S);
833
1359
  }),
834
1360
  dispose: () => {
835
- y(), e.protocol.eventBus.off("key", k), A();
1361
+ g(!1), e.protocol.eventBus.off("key", D), k();
836
1362
  }
837
1363
  };
838
- }, z = (e) => e?.trim().toLowerCase() ?? "", me = /\.m[a-z0-9]{2}$/i, he = (e) => [
1364
+ }, z = (e) => e?.trim().toLowerCase() ?? "", Xe = /\.m[a-z0-9]{2}$/i, Ze = (e) => [
839
1365
  e.viewName ?? "",
840
1366
  e.viewTitle ?? "",
841
1367
  e.cursorPos?.x ?? "",
842
1368
  e.cursorPos?.y ?? "",
843
1369
  e.textUnderCursor ?? "",
844
1370
  e.currentLine ?? ""
845
- ].join("|"), ge = (e) => e.textUnderCursor?.trim() || e.currentLine?.trim() || null, _e = (e) => {
1371
+ ].join("|"), Qe = (e) => e.textUnderCursor?.trim() || e.currentLine?.trim() || null, $e = (e) => {
846
1372
  let t = z(e.viewName), n = z(e.viewTitle);
847
1373
  return t.includes("load") || t.includes("save") || n.includes("load") || n.includes("save");
848
- }, ve = (e) => z(e) === "/..", ye = (e) => !!e?.trim().startsWith("/"), be = (e) => !!e && !ye(e) && me.test(e.trim()), xe = (e) => {
1374
+ }, et = (e) => z(e) === "/..", tt = (e) => !!e?.trim().startsWith("/"), nt = (e) => !!e && !tt(e) && Xe.test(e.trim()), rt = (e) => {
849
1375
  let t = e?.trim() ?? "";
850
1376
  if (!t) return [];
851
- let n = z(t), r = z(t.replace(me, ""));
1377
+ let n = z(t), r = z(t.replace(Xe, ""));
852
1378
  return Array.from(new Set([n, r].filter(Boolean)));
853
- }, Se = (e) => ve(e) ? "parent" : ye(e) ? /^\/[^/]+$/.test(e.trim()) ? "dir" : "other" : be(e) ? "file" : "other", Ce = (e, t) => {
1379
+ }, it = (e) => et(e) ? "parent" : tt(e) ? /^\/[^/]+$/.test(e.trim()) ? "dir" : "other" : nt(e) ? "file" : "other", at = (e, t) => {
854
1380
  let n = (t) => e[t]?.trim() ?? "", r = t.cursorPos?.y ?? null, i = z(t.viewTitle), a = 0, o = e.length - 1;
855
1381
  if (r !== null && n(r)) {
856
1382
  for (a = r; a > 0 && n(a - 1);) --a;
@@ -863,7 +1389,7 @@ var D = (e) => {
863
1389
  row: e,
864
1390
  text: t,
865
1391
  key: z(t),
866
- kind: Se(t)
1392
+ kind: it(t)
867
1393
  });
868
1394
  }
869
1395
  let c = t.textUnderCursor?.trim();
@@ -874,7 +1400,7 @@ var D = (e) => {
874
1400
  hasParent: s.some((e) => e.kind === "parent"),
875
1401
  consistent: !c || r === null || n(r).includes(c)
876
1402
  };
877
- }, we = (e, t) => {
1403
+ }, ot = (e, t) => {
878
1404
  if (t.length === 0) return {
879
1405
  order: e,
880
1406
  consistent: !0
@@ -908,22 +1434,22 @@ var D = (e) => {
908
1434
  order: i,
909
1435
  consistent: !0
910
1436
  };
911
- }, Te = (e) => e.split(/[^a-z0-9]+/).filter((e) => e.length >= 3), Ee = /* @__PURE__ */ new Set([
1437
+ }, st = (e) => e.split(/[^a-z0-9]+/).filter((e) => e.length >= 3), ct = /* @__PURE__ */ new Set([
912
1438
  "load",
913
1439
  "save",
914
1440
  "select",
915
1441
  "create",
916
1442
  "directory",
917
1443
  "new"
918
- ]), De = (e, t) => ({
919
- target: Te(z(e).replace(me, "")),
920
- title: Te(z(t)).filter((e) => !Ee.has(e))
921
- }), Oe = (e, t) => {
1444
+ ]), lt = (e, t) => ({
1445
+ target: st(z(e).replace(Xe, "")),
1446
+ title: st(z(t)).filter((e) => !ct.has(e))
1447
+ }), ut = (e, t) => {
922
1448
  let n = e.replace(/^\//, "");
923
1449
  if (n.length < 3) return 0;
924
1450
  let r = (e) => e.includes(n) || n.includes(e);
925
1451
  return t.target.some(r) ? 2 : +!!t.title.some(r);
926
- }, B = (e) => new Promise((t) => setTimeout(t, e)), ke = ({ getKeySink: e, store: t, debugLog: n, getScreenLines: r }) => {
1452
+ }, B = (e) => new Promise((t) => setTimeout(t, e)), dt = ({ getKeySink: e, store: t, debugLog: n, getScreenLines: r }) => {
927
1453
  let i = () => !!e(), a = (t) => {
928
1454
  e()?.sendKeys(t);
929
1455
  }, o = async (e, t = 50) => {
@@ -965,13 +1491,13 @@ var D = (e) => {
965
1491
  }),
966
1492
  getFileBrowserSnapshot: c,
967
1493
  waitForFileBrowserSnapshotChange: (e, n = 500) => new Promise((r) => {
968
- let i = !1, a = null, o = null, s = null, l = null, u = null, d = null, f = he(e), p = (e) => {
1494
+ let i = !1, a = null, o = null, s = null, l = null, u = null, d = null, f = Ze(e), p = (e) => {
969
1495
  i || (i = !0, d !== null && clearTimeout(d), a?.(), o?.(), s?.(), l?.(), u?.(), r(e));
970
1496
  }, m = () => {
971
1497
  let e = c();
972
- he(e) !== f && p(e);
1498
+ Ze(e) !== f && p(e);
973
1499
  }, h = c();
974
- he(h) === f ? (a = t.sub("textUnderCursor", m), o = t.sub("currentLine", m), s = t.sub("cursorPos", m), l = t.sub("viewName", m), u = t.sub("viewTitle", m), d = setTimeout(() => {
1500
+ Ze(h) === f ? (a = t.sub("textUnderCursor", m), o = t.sub("currentLine", m), s = t.sub("cursorPos", m), l = t.sub("viewName", m), u = t.sub("viewTitle", m), d = setTimeout(() => {
975
1501
  p(c());
976
1502
  }, n)) : p(h);
977
1503
  }),
@@ -979,7 +1505,7 @@ var D = (e) => {
979
1505
  };
980
1506
  }, V = (e, t) => {
981
1507
  t === void 0 ? console.log("[M8SDK browseFile]", e) : console.log("[M8SDK browseFile]", e, t);
982
- }, Ae = 8, je = 8, Me = 64, Ne = 512, Pe = () => ({
1508
+ }, ft = 8, pt = 8, mt = 64, ht = 512, gt = () => ({
983
1509
  order: [],
984
1510
  kinds: /* @__PURE__ */ new Map(),
985
1511
  names: /* @__PURE__ */ new Map(),
@@ -989,40 +1515,40 @@ var D = (e) => {
989
1515
  sorted: !1,
990
1516
  visited: /* @__PURE__ */ new Set(),
991
1517
  rejected: /* @__PURE__ */ new Set()
992
- }), Fe = (e) => e === "up" ? "down" : "up", Ie = (e) => {
1518
+ }), _t = (e) => e === "up" ? "down" : "up", vt = (e) => {
993
1519
  let { hasBus: t, pressAndRelease: n, getFileBrowserSnapshot: r, waitForFileBrowserSnapshotChange: i, getScreenLines: a } = e, o = /* @__PURE__ */ new Map(), s = null, c = null;
994
1520
  return async (e, l = !0) => {
995
1521
  if (!t()) return !1;
996
1522
  let u = z(e);
997
1523
  if (!u) return !1;
998
- let d = me.test(u), f = De(e, r().viewTitle);
1524
+ let d = Xe.test(u), f = lt(e, r().viewTitle);
999
1525
  V("start", {
1000
1526
  targetText: e,
1001
1527
  normalizedTarget: u,
1002
1528
  exact: l
1003
1529
  });
1004
- let p = () => _e(r()), m = async (e, t = 350) => {
1530
+ let p = () => $e(r()), m = async (e, t = 350) => {
1005
1531
  let a = r();
1006
1532
  await n(e, 35);
1007
1533
  let o = await i(a, t);
1008
1534
  return {
1009
- changed: he(o) !== he(a),
1535
+ changed: Ze(o) !== Ze(a),
1010
1536
  snapshot: o
1011
1537
  };
1012
- }, h = (e) => e === "up" ? I.Up : I.Down, g = (e) => I.Opt | h(e), _ = async () => {
1538
+ }, h = (e) => e === "up" ? R.Up : R.Down, g = (e) => R.Opt | h(e), _ = async () => {
1013
1539
  let e = r();
1014
- V("apply alphabetical sort"), await n(I.Shift | I.Opt, 35), await B(180), await i(e, 300);
1015
- }, v = (e) => be(e) ? xe(e).some((e) => l ? e === u : e.includes(u)) : !1, y = (e) => {
1540
+ V("apply alphabetical sort"), await n(R.Shift | R.Opt, 35), await B(180), await i(e, 300);
1541
+ }, v = (e) => nt(e) ? rt(e).some((e) => l ? e === u : e.includes(u)) : !1, y = (e) => {
1016
1542
  let t = e.trim();
1017
- return d ? t : t.replace(me, "");
1543
+ return d ? t : t.replace(Xe, "");
1018
1544
  }, b = (e) => z(y(e)), x = y(e), S = async () => {
1019
1545
  for (let e = 0;; e += 1) {
1020
- let t = Ce(a(), r());
1546
+ let t = at(a(), r());
1021
1547
  if (t.consistent || e >= 2) return t;
1022
1548
  await B(40);
1023
1549
  }
1024
1550
  }, C = (e, t) => {
1025
- let n = t.entries.map((e) => e.key), i = we(e.order, n);
1551
+ let n = t.entries.map((e) => e.key), i = ot(e.order, n);
1026
1552
  e.order = i.order, i.consistent || (e.top = !1, e.bottom = !1);
1027
1553
  for (let n of t.entries) e.kinds.set(n.key, n.kind), e.names.set(n.key, n.text);
1028
1554
  let a = z(r().viewName);
@@ -1031,10 +1557,10 @@ var D = (e) => {
1031
1557
  let e = o.get(a);
1032
1558
  (e === void 0 || n.length < e) && o.set(a, n.length);
1033
1559
  }
1034
- n.length < Math.max(je, o.get(a) ?? 0) && (e.top = !0, e.bottom = !0);
1560
+ n.length < Math.max(pt, o.get(a) ?? 0) && (e.top = !0, e.bottom = !0);
1035
1561
  }, w = async (e, t, n) => {
1036
1562
  let r = (await m(g(n), 280)).changed, i = r, a = !1;
1037
- if (!r && s !== !0) for (let e = 0; e < Ae; e += 1) {
1563
+ if (!r && s !== !0) for (let e = 0; e < ft; e += 1) {
1038
1564
  if (!(await m(h(n), 250)).changed) {
1039
1565
  a = !0;
1040
1566
  break;
@@ -1046,7 +1572,7 @@ var D = (e) => {
1046
1572
  let o = await S();
1047
1573
  C(e, o);
1048
1574
  let c = t.cursorKey ? e.order.indexOf(t.cursorKey) : -1, l = o.cursorKey ? e.order.indexOf(o.cursorKey) : -1, u = Math.abs(l - c);
1049
- return r && c >= 0 && l >= 0 && u > 0 && u < Ae && (s = !0), o;
1575
+ return r && c >= 0 && l >= 0 && u > 0 && u < ft && (s = !0), o;
1050
1576
  }, T = (e, t, n) => {
1051
1577
  if (n === "/..") return "up";
1052
1578
  let r = e.order.indexOf(n), i = t.cursorKey ? e.order.indexOf(t.cursorKey) : -1;
@@ -1060,7 +1586,7 @@ var D = (e) => {
1060
1586
  let t = n.row - r.cursorRow;
1061
1587
  if (t === 0) return r;
1062
1588
  let i = t < 0 ? "up" : "down";
1063
- if (!(await m(Math.abs(t) >= Ae ? g(i) : h(i), 250)).changed) return null;
1589
+ if (!(await m(Math.abs(t) >= ft ? g(i) : h(i), 250)).changed) return null;
1064
1590
  r = await S(), C(e, r);
1065
1591
  continue;
1066
1592
  }
@@ -1068,17 +1594,17 @@ var D = (e) => {
1068
1594
  let o = await w(e, r, i);
1069
1595
  if (o) r = o;
1070
1596
  else if (a) return null;
1071
- else a = !0, i = Fe(i);
1597
+ else a = !0, i = _t(i);
1072
1598
  }
1073
1599
  return null;
1074
1600
  }, D = async (e, t) => {
1075
1601
  let n = t.cursorRow ?? 0, i = t.entries.filter((t) => t.kind === "file" && !e.rejected.has(t.key) && v(t.text)).sort((e, t) => Math.abs(e.row - n) - Math.abs(t.row - n))[0];
1076
1602
  if (!i) return "none";
1077
- let a = await E(e, i.key, t), o = ge(r());
1603
+ let a = await E(e, i.key, t), o = Qe(r());
1078
1604
  return a && v(o) ? (V("match found", {
1079
1605
  entry: o,
1080
1606
  y: a.cursorRow
1081
- }), await m(I.Edit, 500), "found") : (V("match not reachable", { candidate: i.text }), e.rejected.add(i.key), "failed");
1607
+ }), await m(R.Edit, 500), "found") : (V("match not reachable", { candidate: i.text }), e.rejected.add(i.key), "failed");
1082
1608
  }, O = (e) => e.order.filter((t) => e.kinds.get(t) === "file").map((t) => {
1083
1609
  let n = e.names.get(t) ?? "";
1084
1610
  return {
@@ -1101,27 +1627,27 @@ var D = (e) => {
1101
1627
  };
1102
1628
  }), r = n.findIndex((e) => e.kind === "file");
1103
1629
  r < 0 || (n.slice(r).some((e) => e.kind === "dir") ? c = !1 : n.some((e, t) => e.kind === "dir" && n.slice(t + 1).some((t) => t.kind === "file" && e.ci > t.ci && e.raw > t.raw)) && c === null && (c = !0));
1104
- }, A = (e) => !l || !e.sorted || c !== !0 || !e.top || e.bottom ? !1 : O(e).some((e) => /^[a-z0-9]/.test(e.ci) && e.ci > u && e.raw > x && e.raw > u), ee = (e, t) => {
1630
+ }, ee = (e) => !l || !e.sorted || c !== !0 || !e.top || e.bottom ? !1 : O(e).some((e) => /^[a-z0-9]/.test(e.ci) && e.ci > u && e.raw > x && e.raw > u), te = (e, t) => {
1105
1631
  if (!l || !e.sorted) return null;
1106
1632
  let n = t.entries.filter((e) => e.kind === "file").map((e) => b(e.text)), r = n[0], i = n[n.length - 1];
1107
1633
  return r === void 0 || i === void 0 ? null : u < r ? "up" : u > i ? "down" : null;
1108
- }, j = (e, t) => e.top === e.bottom ? ee(e, t) ?? "up" : e.top ? "down" : "up", M = (e, t) => {
1634
+ }, A = (e, t) => e.top === e.bottom ? te(e, t) ?? "up" : e.top ? "down" : "up", j = (e, t) => {
1109
1635
  let n = t.cursorKey ? e.order.indexOf(t.cursorKey) : -1, r = null;
1110
1636
  for (let [i, a] of e.kinds) {
1111
1637
  if (a !== "dir" || e.visited.has(i)) continue;
1112
- let o = t.entries.find((e) => e.key === i), s = e.order.indexOf(i), c = o && t.cursorRow !== null ? Math.abs(o.row - t.cursorRow) : 100 + (n >= 0 && s >= 0 ? Math.abs(s - n) : 500), l = Oe(i, f) * 1e4 - c;
1638
+ let o = t.entries.find((e) => e.key === i), s = e.order.indexOf(i), c = o && t.cursorRow !== null ? Math.abs(o.row - t.cursorRow) : 100 + (n >= 0 && s >= 0 ? Math.abs(s - n) : 500), l = ut(i, f) * 1e4 - c;
1113
1639
  (!r || l > r.rank) && (r = {
1114
1640
  key: i,
1115
1641
  rank: l
1116
1642
  });
1117
1643
  }
1118
1644
  return r?.key ?? null;
1119
- }, N = async (e, t) => await E(e, "/..", t) ? (V("navigate to parent directory"), (await m(I.Edit, 500)).changed && p()) : (V("parent directory entry unreachable"), !1), P = async (e) => {
1120
- if (e > Me || !p()) return V("search aborted", {
1645
+ }, M = async (e, t) => await E(e, "/..", t) ? (V("navigate to parent directory"), (await m(R.Edit, 500)).changed && p()) : (V("parent directory entry unreachable"), !1), N = async (e) => {
1646
+ if (e > mt || !p()) return V("search aborted", {
1121
1647
  depth: e,
1122
1648
  active: p()
1123
1649
  }), "aborted";
1124
- let t = Pe(), n = await S();
1650
+ let t = gt(), n = await S();
1125
1651
  C(t, n), V("search directory", {
1126
1652
  depth: e,
1127
1653
  entries: n.entries.length,
@@ -1130,12 +1656,12 @@ var D = (e) => {
1130
1656
  for (let r = 0;; r += 1) {
1131
1657
  let i = await D(t, n);
1132
1658
  if (i === "found") return "found";
1133
- if (i === "failed" && (n = await S(), C(t, n)), k(t), A(t) && (V("rest of listing is past the target", { depth: e }), t.bottom = !0), t.top && t.bottom || r >= Ne) break;
1134
- let a = await w(t, n, j(t, n));
1659
+ if (i === "failed" && (n = await S(), C(t, n)), k(t), ee(t) && (V("rest of listing is past the target", { depth: e }), t.bottom = !0), t.top && t.bottom || r >= ht) break;
1660
+ let a = await w(t, n, A(t, n));
1135
1661
  a && (n = a);
1136
1662
  }
1137
1663
  for (;;) {
1138
- let r = M(t, n);
1664
+ let r = j(t, n);
1139
1665
  if (!r) break;
1140
1666
  if (t.visited.add(r), !await E(t, r, n)) {
1141
1667
  V("directory unreachable", { directory: r }), n = await S(), C(t, n);
@@ -1145,27 +1671,27 @@ var D = (e) => {
1145
1671
  directory: r,
1146
1672
  depth: e
1147
1673
  });
1148
- let i = await m(I.Edit, 500);
1674
+ let i = await m(R.Edit, 500);
1149
1675
  if (!p()) return "aborted";
1150
1676
  if (!i.changed) {
1151
1677
  n = await S(), C(t, n);
1152
1678
  continue;
1153
1679
  }
1154
- let a = await P(e + 1);
1680
+ let a = await N(e + 1);
1155
1681
  if (a !== "missed") return a;
1156
1682
  n = await S(), C(t, n);
1157
1683
  }
1158
- return e === 0 || await N(t, n) ? "missed" : "aborted";
1684
+ return e === 0 || await M(t, n) ? "missed" : "aborted";
1159
1685
  };
1160
1686
  if (!p()) return console.warn("[M8SDK] browseFile requires a file browser view (load/save)"), !1;
1161
1687
  if (a().length === 0) return console.warn("[M8SDK] browseFile needs the screen reader, which is not available"), !1;
1162
- let F = await P(0);
1163
- return F !== "found" && V("search failed", {
1688
+ let P = await N(0);
1689
+ return P !== "found" && V("search failed", {
1164
1690
  targetText: e,
1165
- outcome: F
1166
- }), F === "found";
1691
+ outcome: P
1692
+ }), P === "found";
1167
1693
  };
1168
- }, Le = (e) => {
1694
+ }, H = (e) => {
1169
1695
  if (!e) return null;
1170
1696
  let t = e.trim();
1171
1697
  if (t === "--") return 0;
@@ -1173,7 +1699,7 @@ var D = (e) => {
1173
1699
  if (!n) return null;
1174
1700
  let r = parseInt(n[0], 16);
1175
1701
  return Number.isNaN(r) ? null : r;
1176
- }, Re = (e) => {
1702
+ }, yt = (e) => {
1177
1703
  if (!e) return null;
1178
1704
  let t = e.trim();
1179
1705
  if (t === "--") return 0;
@@ -1181,11 +1707,11 @@ var D = (e) => {
1181
1707
  if (!n) return null;
1182
1708
  let r = Number.parseInt(n[0], 10);
1183
1709
  return Number.isNaN(r) ? null : r;
1184
- }, ze = /[+-]?\d+\.\d+/g, Be = (e) => {
1710
+ }, bt = /[+-]?\d+\.\d+/g, xt = (e) => {
1185
1711
  if (!e) return [];
1186
1712
  let t = [];
1187
- ze.lastIndex = 0;
1188
- let n = ze.exec(e);
1713
+ bt.lastIndex = 0;
1714
+ let n = bt.exec(e);
1189
1715
  for (; n !== null;) {
1190
1716
  let r = n[0], i = r.startsWith("-"), a = i || r.startsWith("+") ? r.slice(1) : r, o = a.indexOf("."), s = a.slice(0, o), c = a.slice(o + 1), l = Number.parseInt(s || "0", 10), u = Number.parseInt(c || "0", 10), d = 10 ** c.length, f = d > 0 ? 100 / d : 1, p = l * 100 + u * 100 / d;
1191
1717
  !Number.isNaN(l) && !Number.isNaN(u) && t.push({
@@ -1196,16 +1722,16 @@ var D = (e) => {
1196
1722
  decScale: d,
1197
1723
  tickHundredths: f,
1198
1724
  ticks: (i ? -1 : 1) * Math.round(p / f)
1199
- }), n = ze.exec(e);
1725
+ }), n = bt.exec(e);
1200
1726
  }
1201
1727
  return t;
1202
- }, Ve = (e, t) => {
1728
+ }, St = (e, t) => {
1203
1729
  if (e.length <= 1) return 0;
1204
1730
  let n = t?.trim() ?? "";
1205
1731
  if (!n) return 0;
1206
1732
  let r = e.findIndex((e) => e.raw === n || e.raw.replace(/^[+-]/, "") === n || `${e.negative ? "-" : ""}${e.intDigits}` === n || e.intDigits === n || e.decDigits === n);
1207
1733
  return r >= 0 ? r : 0;
1208
- }, He = (e, t) => {
1734
+ }, Ct = (e, t) => {
1209
1735
  if (!e) return null;
1210
1736
  let n = t?.trim() ?? "";
1211
1737
  if (!n) return null;
@@ -1219,13 +1745,13 @@ var D = (e) => {
1219
1745
  if (r && !t) return "dec";
1220
1746
  }
1221
1747
  return null;
1222
- }, Ue = (e, t) => t.length === 1 ? e.raw === t || e.raw.replace(/^[+-]/, "") === t : e.raw === t || e.raw.replace(/^[+-]/, "") === t || `${e.negative ? "-" : ""}${e.intDigits}` === t || e.intDigits === t || e.decDigits === t, We = (e, t, n) => {
1748
+ }, wt = (e, t) => t.length === 1 ? e.raw === t || e.raw.replace(/^[+-]/, "") === t : e.raw === t || e.raw.replace(/^[+-]/, "") === t || `${e.negative ? "-" : ""}${e.intDigits}` === t || e.intDigits === t || e.decDigits === t, Tt = (e, t, n) => {
1223
1749
  let r = n?.trim() ?? "";
1224
- return !r || !e[t] || Ue(e[t], r) ? !1 : e.some((e, n) => n !== t && Ue(e, r));
1225
- }, H = (e, t) => {
1750
+ return !r || !e[t] || wt(e[t], r) ? !1 : e.some((e, n) => n !== t && wt(e, r));
1751
+ }, U = (e, t) => {
1226
1752
  let n = Math.round(e * t), r = n < 0 ? "-" : "", i = Math.abs(n), a = Math.floor(i / 100);
1227
1753
  return `${r}${a}.${(i - a * 100).toString().padStart(2, "0")}`;
1228
- }, Ge = [
1754
+ }, Et = [
1229
1755
  "C-",
1230
1756
  "C#",
1231
1757
  "D-",
@@ -1238,35 +1764,35 @@ var D = (e) => {
1238
1764
  "A-",
1239
1765
  "A#",
1240
1766
  "B-"
1241
- ], U = (e) => {
1767
+ ], W = (e) => {
1242
1768
  if (!e) return null;
1243
1769
  let t = e.trim().toUpperCase().match(/([A-G][#-][0-9A-F])/);
1244
1770
  if (!t) return null;
1245
- let n = t[1], r = n.slice(0, 2), i = Number.parseInt(n.slice(2, 3), 16), a = Ge.indexOf(r);
1771
+ let n = t[1], r = n.slice(0, 2), i = Number.parseInt(n.slice(2, 3), 16), a = Et.indexOf(r);
1246
1772
  return a < 0 || Number.isNaN(i) ? null : {
1247
1773
  label: n,
1248
1774
  noteName: r,
1249
1775
  octave: i,
1250
1776
  semitoneIndex: i * 12 + a
1251
1777
  };
1252
- }, Ke = (e) => {
1778
+ }, Dt = (e) => {
1253
1779
  let { store: t, debugLog: n, hasBus: r, sendKeys: i, pressAndRelease: a, waitForTextUnderCursorChange: o, waitForCurrentLineChange: s } = e;
1254
1780
  return async (e) => {
1255
1781
  if (!r() || !Number.isFinite(e)) return !1;
1256
1782
  let c = 0, l = 1, u = [], d = () => {
1257
- let e = Be(t.get("currentLine"));
1783
+ let e = xt(t.get("currentLine"));
1258
1784
  if (u = e, !e.length) return null;
1259
1785
  c = Math.min(c, e.length - 1);
1260
1786
  let n = e[c];
1261
1787
  return n.tickHundredths > 0 && (l = n.tickHundredths), n;
1262
- }, f = Be(t.get("currentLine"));
1788
+ }, f = xt(t.get("currentLine"));
1263
1789
  if (!f.length) return console.warn("[M8SDK] Could not find a float value like \"440.00\" or \"-10.69\" on the current line:", t.get("currentLine")), !1;
1264
- c = Ve(f, t.get("textUnderCursor")), f.length > 1 && n("[M8SDK] Multiple float values on line, editing:", f[c]?.raw);
1790
+ c = St(f, t.get("textUnderCursor")), f.length > 1 && n("[M8SDK] Multiple float values on line, editing:", f[c]?.raw);
1265
1791
  let p = f[c];
1266
1792
  l = p.tickHundredths;
1267
1793
  let m = Math.round(Math.round(e * 100) / l);
1268
- if (p.ticks === m) return n("[M8SDK] Already at target float value:", H(m, l)), !0;
1269
- n("[M8SDK] Setting float value from", H(p.ticks, l), "to", H(m, l)), await a(I.Edit), await B(80);
1794
+ if (p.ticks === m) return n("[M8SDK] Already at target float value:", U(m, l)), !0;
1795
+ n("[M8SDK] Setting float value from", U(p.ticks, l), "to", U(m, l)), await a(R.Edit), await B(80);
1270
1796
  let h = () => ({
1271
1797
  upDown: {
1272
1798
  mag: null,
@@ -1280,19 +1806,19 @@ var D = (e) => {
1280
1806
  full: h(),
1281
1807
  int: h(),
1282
1808
  dec: h()
1283
- }, _ = p.decScale, v = Date.now(), y = He(p, t.get("textUnderCursor")) ?? "int";
1809
+ }, _ = p.decScale, v = Date.now(), y = Ct(p, t.get("textUnderCursor")) ?? "int";
1284
1810
  n("[M8SDK] Float cursor focus:", y);
1285
1811
  let b = !1, x = !1, S = p.ticks, C = null, w = p.ticks, T = Math.abs(p.ticks - m), E = 0, D = !1, O = !1, k = (e, t) => {
1286
- let n = e === "upDown" ? I.Up : I.Right, r = e === "upDown" ? I.Down : I.Left;
1287
- return I.Edit | (t >= 0 ? n : r);
1288
- }, A = async (e, n, r) => {
1812
+ let n = e === "upDown" ? R.Up : R.Right, r = e === "upDown" ? R.Down : R.Left;
1813
+ return R.Edit | (t >= 0 ? n : r);
1814
+ }, ee = async (e, n, r) => {
1289
1815
  let a = g[y][e], o = k(e, n * a.bias), c = t.get("currentLine");
1290
1816
  for (let e = 0; e < r; e++) i(o), await B(22);
1291
1817
  await s(c, 350);
1292
- }, ee = async (e) => {
1293
- let n = () => He(d(), t.get("textUnderCursor"));
1818
+ }, te = async (e) => {
1819
+ let n = () => Ct(d(), t.get("textUnderCursor"));
1294
1820
  if (n() === e) return y = e, !0;
1295
- let r = t.get("textUnderCursor"), i = e === "dec" ? I.Right : I.Left, s = e === "dec" ? I.Left : I.Right;
1821
+ let r = t.get("textUnderCursor"), i = e === "dec" ? R.Right : R.Left, s = e === "dec" ? R.Left : R.Right;
1296
1822
  if (await a(i, 35), await o(r, 250), n() === e) return y = e, !0;
1297
1823
  await a(s, 35), await o(r, 250);
1298
1824
  let c = n() === e;
@@ -1304,7 +1830,7 @@ var D = (e) => {
1304
1830
  console.warn("[M8SDK] Float value no longer parseable while editing:", t.get("currentLine")), O = !0;
1305
1831
  break;
1306
1832
  }
1307
- if (We(u, c, t.get("textUnderCursor"))) {
1833
+ if (Tt(u, c, t.get("textUnderCursor"))) {
1308
1834
  console.warn("[M8SDK] Float cursor drifted to another field, aborting to avoid editing the wrong value"), x = !0, O = !0;
1309
1835
  break;
1310
1836
  }
@@ -1322,8 +1848,8 @@ var D = (e) => {
1322
1848
  }
1323
1849
  if (C = S, S = e.ticks, y !== "full" && !b) {
1324
1850
  let e = i >= _ ? "int" : "dec";
1325
- if (e !== y && !await ee(e)) {
1326
- if (We(u, c, t.get("textUnderCursor"))) {
1851
+ if (e !== y && !await te(e)) {
1852
+ if (Tt(u, c, t.get("textUnderCursor"))) {
1327
1853
  console.warn("[M8SDK] Float cursor drifted to another field, aborting to avoid editing the wrong value"), x = !0, O = !0;
1328
1854
  break;
1329
1855
  }
@@ -1332,21 +1858,21 @@ var D = (e) => {
1332
1858
  }
1333
1859
  let s = g[y], f = y === "int" ? Math.max(32, 3 * _) : y === "full" ? _ : 8, p, h = 1;
1334
1860
  s.upDown.mag !== null && s.upDown.mag > 0 && i >= s.upDown.mag ? (p = "upDown", h = Math.min(Math.max(1, Math.floor(i / s.upDown.mag)), 96)) : s.upDown.mag === null && i >= f ? (p = "upDown", h = 1) : s.leftRight.mag !== null && s.leftRight.mag > 0 ? (p = "leftRight", h = Math.min(Math.max(1, Math.floor(i / s.leftRight.mag)), 96)) : (p = "leftRight", h = 1);
1335
- let v = r > 0 ? 1 : -1, k = e.ticks, j = y, M = p;
1336
- await A(p, v, h);
1337
- let N = d();
1338
- if (!N) {
1861
+ let v = r > 0 ? 1 : -1, k = e.ticks, A = y, j = p;
1862
+ await ee(p, v, h);
1863
+ let M = d();
1864
+ if (!M) {
1339
1865
  O = !0;
1340
1866
  break;
1341
1867
  }
1342
- let P = N.ticks - k;
1343
- if (P === 0) {
1344
- E += 1, E >= 4 && (console.warn(`[M8SDK] Float value stopped reacting at ${H(N.ticks, l)} (target ${H(m, l)} may be out of range)`), O = !0);
1868
+ let N = M.ticks - k;
1869
+ if (N === 0) {
1870
+ E += 1, E >= 4 && (console.warn(`[M8SDK] Float value stopped reacting at ${U(M.ticks, l)} (target ${U(m, l)} may be out of range)`), O = !0);
1345
1871
  continue;
1346
1872
  }
1347
1873
  E = 0;
1348
- let F = g[j][M];
1349
- F.mag = Math.max(1, Math.round(Math.abs(P) / h)), Math.sign(P) !== Math.sign(r) && (n(`[M8SDK] Float ${M} arrows move oppositely in '${j}' focus, inverting mapping`), F.bias *= -1, C = null);
1874
+ let P = g[A][j];
1875
+ P.mag = Math.max(1, Math.round(Math.abs(N) / h)), Math.sign(N) !== Math.sign(r) && (n(`[M8SDK] Float ${j} arrows move oppositely in '${A}' focus, inverting mapping`), P.bias *= -1, C = null);
1350
1876
  }
1351
1877
  if (!D && !x) {
1352
1878
  let e = Date.now();
@@ -1354,20 +1880,20 @@ var D = (e) => {
1354
1880
  let e = d();
1355
1881
  if (!e || e.ticks === w) break;
1356
1882
  let t = w - e.ticks, n = Math.abs(t);
1357
- y !== "full" && !b && n >= _ != (y === "int") && (await ee(n >= _ ? "int" : "dec") || (b = !0));
1883
+ y !== "full" && !b && n >= _ != (y === "int") && (await te(n >= _ ? "int" : "dec") || (b = !0));
1358
1884
  let r = g[y], i, a = 1;
1359
1885
  r.upDown.mag !== null && r.upDown.mag > 0 && n >= r.upDown.mag ? (i = "upDown", a = Math.min(Math.max(1, Math.floor(n / r.upDown.mag)), 16)) : r.leftRight.mag !== null && r.leftRight.mag > 0 ? (i = "leftRight", a = Math.min(Math.max(1, Math.floor(n / r.leftRight.mag)), 16)) : i = "leftRight";
1360
1886
  let o = e.ticks;
1361
- await A(i, t > 0 ? 1 : -1, a);
1887
+ await ee(i, t > 0 ? 1 : -1, a);
1362
1888
  let s = d();
1363
1889
  if (!s || s.ticks === o) break;
1364
1890
  }
1365
1891
  }
1366
- x || await a(I.Edit);
1367
- let j = d(), M = !!j && j.ticks === m;
1368
- return n(`[M8SDK] Float setting ${M ? "succeeded" : "failed"}: "${j?.raw ?? "N/A"}" (${j ? H(j.ticks, l) : "N/A"})`), M;
1892
+ x || await a(R.Edit);
1893
+ let A = d(), j = !!A && A.ticks === m;
1894
+ return n(`[M8SDK] Float setting ${j ? "succeeded" : "failed"}: "${A?.raw ?? "N/A"}" (${A ? U(A.ticks, l) : "N/A"})`), j;
1369
1895
  };
1370
- }, qe = {
1896
+ }, Ot = {
1371
1897
  song: [
1372
1898
  {
1373
1899
  to: "project",
@@ -1820,7 +2346,7 @@ var D = (e) => {
1820
2346
  via: "exit",
1821
2347
  keys: [2, 0]
1822
2348
  }]
1823
- }, Je = { views: /* @__PURE__ */ "chain.effectsettings.groove.instmods.inst.instrumentpool.mixer.phrase.project.scale.song.table.mixeq.limiterscope.mixscope.modfxeq.delayeq.reverbeq.loadinstrument.selectsavedirectory.createdirectory.systemsettings.midisettings.midimapping.renderaudio.timestats.themesettings.loadproject".split(".") }, Ye = (e) => {
2349
+ }, kt = { views: /* @__PURE__ */ "chain.effectsettings.groove.instmods.inst.instrumentpool.mixer.phrase.project.scale.song.table.mixeq.limiterscope.mixscope.modfxeq.delayeq.reverbeq.loadinstrument.selectsavedirectory.createdirectory.systemsettings.midisettings.midimapping.renderaudio.timestats.themesettings.loadproject".split(".") }, At = (e) => {
1824
2350
  let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
1825
2351
  for (let [r, i] of Object.entries(e)) {
1826
2352
  let e = r.toLowerCase(), a = [], o = /* @__PURE__ */ new Map();
@@ -1850,20 +2376,20 @@ var D = (e) => {
1850
2376
  graph: t,
1851
2377
  edgeKeys: n
1852
2378
  };
1853
- }, Xe = null, Ze = null, Qe = () => (Xe ??= Ye(qe), Xe), $e = () => {
1854
- if (!Ze) {
1855
- let e = Array.isArray(Je.views) ? Je.views : [];
1856
- Ze = new Set(e.map((e) => String(e).trim().toLowerCase()).filter((e) => !!e));
2379
+ }, jt = null, Mt = null, Nt = () => (jt ??= At(Ot), jt), Pt = () => {
2380
+ if (!Mt) {
2381
+ let e = Array.isArray(kt.views) ? kt.views : [];
2382
+ Mt = new Set(e.map((e) => String(e).trim().toLowerCase()).filter((e) => !!e));
1857
2383
  }
1858
- return Ze;
1859
- }, et = 50, tt = {
2384
+ return Mt;
2385
+ }, Ft = 50, It = {
1860
2386
  x: 0,
1861
2387
  y: 0,
1862
2388
  w: 1,
1863
2389
  h: 1
1864
- }, nt = class {
2390
+ }, Lt = class {
1865
2391
  constructor() {
1866
- this.assemblies = [], this.selectionRectSamples = [], this.lastCursor = tt, this.lastSelectionCursor = null, this.lastCursorColor = null, this.lastSelectionMode = !1, this.lastSelectionSeenAt = 0, this.ASSEMBLY_TIMEOUT = 100, this.SELECTION_SAMPLE_WINDOW = 50, this.SELECTION_HOLD_DURATION = 150;
2392
+ this.assemblies = [], this.selectionRectSamples = [], this.lastCursor = It, this.lastSelectionCursor = null, this.lastCursorColor = null, this.lastSelectionMode = !1, this.lastSelectionSeenAt = 0, this.ASSEMBLY_TIMEOUT = 100, this.SELECTION_SAMPLE_WINDOW = 50, this.SELECTION_HOLD_DURATION = 150;
1867
2393
  }
1868
2394
  processRect(e) {
1869
2395
  if (!e) return {
@@ -2055,7 +2581,7 @@ var D = (e) => {
2055
2581
  ].filter((e) => e.foundVert || e.foundTip).length >= 2;
2056
2582
  }
2057
2583
  buildCursorFromAssembly(e) {
2058
- if (!e.tr || !e.bl || !e.br) return tt;
2584
+ if (!e.tr || !e.bl || !e.br) return It;
2059
2585
  let t = e.tr.x - e.tl.x - 1, n = e.bl.y - e.tl.y - 1;
2060
2586
  return {
2061
2587
  x: e.tl.x + 1,
@@ -2072,19 +2598,19 @@ var D = (e) => {
2072
2598
  h: 1
2073
2599
  }, this.lastSelectionCursor = null, this.lastSelectionMode = !1, this.lastSelectionSeenAt = 0;
2074
2600
  }
2075
- }, rt = (e) => e ? `#${[
2601
+ }, Rt = (e) => e ? `#${[
2076
2602
  e.r,
2077
2603
  e.g,
2078
2604
  e.b
2079
2605
  ].map((e) => e.toString(16).padStart(2, "0")).join("")}` : null;
2080
- function it(e, t) {
2081
- let n = new nt();
2606
+ function zt(e, t) {
2607
+ let n = new Lt();
2082
2608
  class r {
2083
2609
  constructor() {
2084
2610
  this.rowChars = /* @__PURE__ */ new Map(), this.timer = null, this.knownViews = [], this.earlyAccepted = !1;
2085
2611
  }
2086
2612
  init() {
2087
- this.knownViews = Array.from($e());
2613
+ this.knownViews = Array.from(Pt());
2088
2614
  }
2089
2615
  normForCompare(e) {
2090
2616
  return e = e.includes("live") ? "song" : e, e.toLowerCase().trim().replace(/[{}]/g, "0");
@@ -2128,7 +2654,7 @@ function it(e, t) {
2128
2654
  bg: e.background
2129
2655
  }), this.tryEarlyAccept(o), this.timer && clearTimeout(this.timer), this.timer = setTimeout(() => {
2130
2656
  this.emitStable();
2131
- }, et)));
2657
+ }, Ft)));
2132
2658
  }
2133
2659
  reset() {
2134
2660
  this.rowChars.clear(), this.timer &&= (clearTimeout(this.timer), null), this.earlyAccepted = !1;
@@ -2207,7 +2733,7 @@ function it(e, t) {
2207
2733
  let e = this.grid.get(i), a = [], o = [];
2208
2734
  for (let n = 0; n < t; n += 1) {
2209
2735
  let t = e?.get(n);
2210
- a.push(rt(t?.fg)), o.push(rt(t?.bg));
2736
+ a.push(Rt(t?.fg)), o.push(Rt(t?.bg));
2211
2737
  }
2212
2738
  n.push(a), r.push(o);
2213
2739
  }
@@ -2234,7 +2760,7 @@ function it(e, t) {
2234
2760
  if (!e) return;
2235
2761
  Date.now() < s && console.log(`[rect] type=${e.type} pos=(${e.pos.x},${e.pos.y}) size=${e.size.width}x${e.size.height}`);
2236
2762
  let { cellW: r, cellH: i, offX: a, offY: o } = t.get("cellMetrics"), { cursor: c, cursorColor: l, selectionMode: u } = n.processRect(e);
2237
- l && rt(l) !== rt(t.get("cursorColor")) && t.set("cursorColor", l);
2763
+ l && Rt(l) !== Rt(t.get("cursorColor")) && t.set("cursorColor", l);
2238
2764
  let { x: d, y: f, w: p, h: m } = c, h = Math.round((d - a) / r), g = Math.round((f - o) / i), _ = t.get("cursorPos"), v = t.get("selectionMode");
2239
2765
  if (u) {
2240
2766
  let e = t.get("cursorRect");
@@ -2290,7 +2816,7 @@ function it(e, t) {
2290
2816
  }
2291
2817
  //#endregion
2292
2818
  //#region src/engine/host/hostState.ts
2293
- var at = (e) => {
2819
+ var Bt = (e) => {
2294
2820
  let t = e.get("macroStatus");
2295
2821
  return {
2296
2822
  viewName: e.get("viewName"),
@@ -2312,13 +2838,13 @@ var at = (e) => {
2312
2838
  macroCurrentStep: t.currentStep,
2313
2839
  macroSequenceLength: t.sequenceLength
2314
2840
  };
2315
- }, ot = (e, t, n) => {
2841
+ }, Vt = (e, t, n) => {
2316
2842
  let r = e.get("cellMetrics");
2317
2843
  return {
2318
2844
  x: t * r.cellW + r.offX + r.cellW / 2,
2319
2845
  y: n * r.cellH + r.offY + r.cellH / 2
2320
2846
  };
2321
- }, st = (e) => te({
2847
+ }, Ht = (e) => Fe({
2322
2848
  left: e.includes("left"),
2323
2849
  right: e.includes("right"),
2324
2850
  up: e.includes("up"),
@@ -2327,13 +2853,11 @@ var at = (e) => {
2327
2853
  play: e.includes("play"),
2328
2854
  opt: e.includes("opt"),
2329
2855
  edit: e.includes("edit")
2330
- }), ct = ({ io: e, editors: t, setValueFloat: n, browseFile: r, navigateToView: i, navigateTo: a, enableMidiTransport: o, getScreenCellColors: s }) => {
2856
+ }), Ut = ({ io: e, editors: t, setValueFloat: n, browseFile: r, navigateToView: i, navigateTo: a, enableMidiTransport: o, getScreenCellColors: s }) => {
2331
2857
  let { debugLog: c, hasBus: l, sendKeys: u, pressAndRelease: d, store: f, getScreenLines: p } = e, m = (e, t, n) => async (...r) => l() ? n(...r) : (console.warn(`[M8SDK] ${e}: no bus connection`), t);
2332
2858
  return {
2333
2859
  navigateToView: m("navigateToView", !1, async (e) => (c("[M8SDK] Executing navigateToView:", e), i(e))),
2334
- navigateTo: m("navigateTo", void 0, async (e, t) => {
2335
- await a(ot(f, e, t));
2336
- }),
2860
+ navigateTo: m("navigateTo", !1, async (e, t) => a(Vt(f, e, t))),
2337
2861
  setValueToHex: m("setValueToHex", !1, async (e) => (c("[M8SDK] Executing setValueToHex:", e), t.setValueToHex(e))),
2338
2862
  setValueToInt: m("setValueToInt", !1, async (e) => (c("[M8SDK] Executing setValueToInt:", e), t.setValueToInt(e))),
2339
2863
  setValueFloat: m("setValueFloat", !1, async (e) => (c("[M8SDK] Executing setValueFloat:", e), n(e))),
@@ -2348,15 +2872,15 @@ var at = (e) => {
2348
2872
  exact: t
2349
2873
  }), r(e, t))),
2350
2874
  sendKeyDown: m("sendKeyDown", void 0, async (e) => {
2351
- c("[M8SDK] Executing sendKeyDown:", e), u(st(e));
2875
+ c("[M8SDK] Executing sendKeyDown:", e), u(Ht(e));
2352
2876
  }),
2353
2877
  sendKeyUp: m("sendKeyUp", void 0, async () => {
2354
2878
  c("[M8SDK] Executing sendKeyUp"), u(0);
2355
2879
  }),
2356
2880
  sendKeyPress: m("sendKeyPress", void 0, async (e) => {
2357
- c("[M8SDK] Executing sendKeyPress:", e), await d(st(e));
2881
+ c("[M8SDK] Executing sendKeyPress:", e), await d(Ht(e));
2358
2882
  }),
2359
- getState: async () => at(f),
2883
+ getState: async () => Bt(f),
2360
2884
  getScreen: async () => ({
2361
2885
  width: 40,
2362
2886
  height: 24,
@@ -2370,7 +2894,7 @@ var at = (e) => {
2370
2894
  }),
2371
2895
  enableMidiTransport: async () => o()
2372
2896
  };
2373
- }, lt = 6, ut = (e) => {
2897
+ }, Wt = 6, Gt = (e) => {
2374
2898
  let t = null, n = 0, r = null, i = null, a = (e) => performance.timeOrigin + e, o = (t) => {
2375
2899
  let o = t.data;
2376
2900
  if (!o || o.length === 0) return;
@@ -2381,7 +2905,7 @@ var at = (e) => {
2381
2905
  let e = s - r;
2382
2906
  e > 0 && e < 200 && (i = i == null ? e : i * .9 + e * .1);
2383
2907
  }
2384
- r = s, n += 1, n % lt === 0 && e({
2908
+ r = s, n += 1, n % Wt === 0 && e({
2385
2909
  kind: "clock",
2386
2910
  ticks: n,
2387
2911
  at: s,
@@ -2424,65 +2948,75 @@ var at = (e) => {
2424
2948
  return;
2425
2949
  }
2426
2950
  }
2427
- };
2428
- return {
2429
- enable: async () => {
2430
- if (t) return { ok: !0 };
2431
- if (!("requestMIDIAccess" in navigator)) return {
2951
+ }, s = null, c = 0, l = async () => {
2952
+ let e = c;
2953
+ if (!("requestMIDIAccess" in navigator)) return {
2954
+ ok: !1,
2955
+ error: "Web MIDI is not available in this browser"
2956
+ };
2957
+ try {
2958
+ let n = [...(await navigator.requestMIDIAccess()).inputs.values()].find((e) => e.name === "M8");
2959
+ return n ? (await n.open(), c === e ? (n.addEventListener("midimessage", o), t = n, { ok: !0 }) : {
2960
+ ok: !1,
2961
+ error: "disabled"
2962
+ }) : {
2432
2963
  ok: !1,
2433
- error: "Web MIDI is not available in this browser"
2964
+ error: "no MIDI input named M8"
2434
2965
  };
2435
- try {
2436
- let e = [...(await navigator.requestMIDIAccess()).inputs.values()].find((e) => e.name === "M8");
2437
- return e ? (await e.open(), e.addEventListener("midimessage", o), t = e, { ok: !0 }) : {
2438
- ok: !1,
2439
- error: "no MIDI input named M8"
2440
- };
2441
- } catch (e) {
2442
- return {
2443
- ok: !1,
2444
- error: e instanceof Error ? e.message : String(e)
2445
- };
2446
- }
2966
+ } catch (e) {
2967
+ return {
2968
+ ok: !1,
2969
+ error: e instanceof Error ? e.message : String(e)
2970
+ };
2971
+ }
2972
+ };
2973
+ return {
2974
+ enable: () => {
2975
+ if (t) return Promise.resolve({ ok: !0 });
2976
+ if (s) return s;
2977
+ let e = l();
2978
+ return s = e, e.then((t) => {
2979
+ s === e && !t.ok && (s = null);
2980
+ }), e;
2447
2981
  },
2448
2982
  disable: () => {
2449
- t?.removeEventListener("midimessage", o), t = null;
2983
+ c += 1, s = null, t?.removeEventListener("midimessage", o), t = null;
2450
2984
  }
2451
2985
  };
2452
- }, dt = (e, t) => {
2986
+ }, Kt = (e, t) => {
2453
2987
  let n = [], r = e, i = t;
2454
2988
  for (; r !== i;) {
2455
2989
  let e = i - r, t = Math.abs(e), a = e > 0 ? 1 : -1;
2456
2990
  if (t >= 16) {
2457
- let e = Math.floor(t / 16), o = a > 0 ? I.Up : I.Down, s = I.Edit | o, c = Math.min(e, 10);
2991
+ let e = Math.floor(t / 16), o = a > 0 ? R.Up : R.Down, s = R.Edit | o, c = Math.min(e, 10);
2458
2992
  for (let e = 0; e < c && (n.push(s), n.push(0), r += a * 16, r !== i); e++);
2459
2993
  } else if (t > 0) {
2460
- let e = a > 0 ? I.Right : I.Left, t = I.Edit | e;
2994
+ let e = a > 0 ? R.Right : R.Left, t = R.Edit | e;
2461
2995
  n.push(t), n.push(0), r += a * 1;
2462
2996
  } else break;
2463
2997
  }
2464
2998
  return n;
2465
- }, ft = (e, t) => {
2999
+ }, qt = (e, t) => {
2466
3000
  let n = [], r = e;
2467
3001
  for (; r !== t;) {
2468
3002
  let e = t - r, i = e > 0 ? 1 : -1;
2469
3003
  if (Math.abs(e) >= 12) {
2470
- let e = i > 0 ? I.Up : I.Down;
2471
- n.push(I.Edit | e), n.push(0), r += i * 12;
3004
+ let e = i > 0 ? R.Up : R.Down;
3005
+ n.push(R.Edit | e), n.push(0), r += i * 12;
2472
3006
  } else {
2473
- let e = i > 0 ? I.Right : I.Left;
2474
- n.push(I.Edit | e), n.push(0), r += i;
3007
+ let e = i > 0 ? R.Right : R.Left;
3008
+ n.push(R.Edit | e), n.push(0), r += i;
2475
3009
  }
2476
3010
  if (n.length > 2048) break;
2477
3011
  }
2478
3012
  return n;
2479
- }, pt = (e) => {
3013
+ }, Jt = (e) => {
2480
3014
  let { store: t, debugLog: n, hasBus: r, sendKeys: i, pressAndRelease: a, waitForTextUnderCursorChange: o } = e;
2481
3015
  return {
2482
3016
  setValueToHex: async (e) => {
2483
3017
  if (!r()) return !1;
2484
3018
  e = Math.max(0, Math.min(255, e));
2485
- let s = t.get("textUnderCursor"), c = Le(s), l = s?.trim() === "--";
3019
+ let s = t.get("textUnderCursor"), c = H(s), l = s?.trim() === "--";
2486
3020
  if (c === null) return console.warn("[M8SDK] Could not parse current value under cursor:", s), !1;
2487
3021
  if (c === e && !l) return n("[M8SDK] Already at target value:", e.toString(16).padStart(2, "0").toUpperCase()), !0;
2488
3022
  n(`[M8SDK] Setting value from "${s?.trim() ?? "N/A"}" (${c.toString(16).padStart(2, "0").toUpperCase()}) to ${e.toString(16).padStart(2, "0").toUpperCase()}`);
@@ -2490,60 +3024,60 @@ var at = (e) => {
2490
3024
  if (l) {
2491
3025
  n("[M8SDK] Initial value is \"--\", pressing edit to recall last value");
2492
3026
  let e = s;
2493
- await a(I.Edit), await o(e, 250);
2494
- let r = t.get("textUnderCursor"), i = Le(r);
2495
- if (n(`[M8SDK] After edit press, value is now: "${r?.trim() ?? "N/A"}" (${i?.toString(16).padStart(2, "0").toUpperCase() ?? "N/A"})`), i === null) return console.warn("[M8SDK] Failed to read value after edit press on \"--\""), await a(I.Edit), !1;
3027
+ await a(R.Edit), await o(e, 250);
3028
+ let r = t.get("textUnderCursor"), i = H(r);
3029
+ if (n(`[M8SDK] After edit press, value is now: "${r?.trim() ?? "N/A"}" (${i?.toString(16).padStart(2, "0").toUpperCase() ?? "N/A"})`), i === null) return console.warn("[M8SDK] Failed to read value after edit press on \"--\""), await a(R.Edit), !1;
2496
3030
  u = i;
2497
3031
  } else {
2498
3032
  let e = s;
2499
- await a(I.Edit), await o(e, 250);
3033
+ await a(R.Edit), await o(e, 250);
2500
3034
  }
2501
- let d = dt(u, e);
3035
+ let d = Kt(u, e);
2502
3036
  n(`[M8SDK] Precalculated ${d.length / 2} key presses from ${u.toString(16).padStart(2, "0").toUpperCase()} to ${e.toString(16).padStart(2, "0").toUpperCase()}`);
2503
3037
  for (let e of d) i(e), await B(30);
2504
3038
  await o(s, 250);
2505
- let f = t.get("textUnderCursor"), p = Le(f);
3039
+ let f = t.get("textUnderCursor"), p = H(f);
2506
3040
  if (p !== e) {
2507
3041
  n("[M8SDK] Precomputation missed target, falling back to iterative mode");
2508
3042
  let r = p ?? u, i = Date.now();
2509
3043
  for (; r !== e && Date.now() - i < 5e3;) {
2510
3044
  let i = e - r, s = Math.abs(i), c = i > 0 ? 1 : -1, l = t.get("textUnderCursor");
2511
3045
  if (s >= 16) {
2512
- let e = c > 0 ? I.Up : I.Down, t = I.Edit | e;
3046
+ let e = c > 0 ? R.Up : R.Down, t = R.Edit | e;
2513
3047
  await a(t);
2514
3048
  } else if (s > 0) {
2515
- let e = c > 0 ? I.Right : I.Left, t = I.Edit | e;
3049
+ let e = c > 0 ? R.Right : R.Left, t = R.Edit | e;
2516
3050
  await a(t);
2517
3051
  }
2518
3052
  await o(l, 250);
2519
- let u = Le(t.get("textUnderCursor"));
3053
+ let u = H(t.get("textUnderCursor"));
2520
3054
  u !== null && (r = u, n(`[M8SDK] Iterative: current value: ${r.toString(16).padStart(2, "0").toUpperCase()}`));
2521
3055
  }
2522
- p = Le(t.get("textUnderCursor"));
3056
+ p = H(t.get("textUnderCursor"));
2523
3057
  }
2524
- await a(I.Edit);
3058
+ await a(R.Edit);
2525
3059
  let m = p === e;
2526
3060
  return n(`[M8SDK] Value setting ${m ? "succeeded" : "failed"}. Final value: "${f?.trim() ?? "N/A"}" (${p?.toString(16).padStart(2, "0").toUpperCase() ?? "N/A"})`), m;
2527
3061
  },
2528
3062
  setValueToInt: async (e) => {
2529
3063
  if (!r()) return !1;
2530
3064
  e = Math.floor(e);
2531
- let n = t.get("textUnderCursor"), i = Re(n);
3065
+ let n = t.get("textUnderCursor"), i = yt(n);
2532
3066
  if (i === null) return console.warn("[M8SDK] Could not parse current integer value under cursor:", n), !1;
2533
3067
  if (i === e) return !0;
2534
- await a(I.Edit), await o(n, 250);
3068
+ await a(R.Edit), await o(n, 250);
2535
3069
  let s = i, c = i, l = Math.abs(i - e), u = null, d = Date.now();
2536
3070
  for (; s !== e && Date.now() - d < 5e3;) {
2537
3071
  let n = e - s, r = Math.abs(n), i = n > 0 ? 1 : -1, d = t.get("textUnderCursor"), f = s;
2538
3072
  if (r >= 16) {
2539
- let e = i > 0 ? I.Up : I.Down;
2540
- await a(I.Edit | e, 35);
3073
+ let e = i > 0 ? R.Up : R.Down;
3074
+ await a(R.Edit | e, 35);
2541
3075
  } else {
2542
- let e = i > 0 ? I.Right : I.Left;
2543
- await a(I.Edit | e, 35);
3076
+ let e = i > 0 ? R.Right : R.Left;
3077
+ await a(R.Edit | e, 35);
2544
3078
  }
2545
3079
  await o(d, 250);
2546
- let p = Re(t.get("textUnderCursor"));
3080
+ let p = yt(t.get("textUnderCursor"));
2547
3081
  if (p === null || p === s) break;
2548
3082
  let m = Math.abs(p - e);
2549
3083
  m < l && (l = m, c = p);
@@ -2556,19 +3090,19 @@ var at = (e) => {
2556
3090
  for (; s !== c && Date.now() - e < 1500;) {
2557
3091
  let e = c - s, n = Math.abs(e), r = e > 0 ? 1 : -1, i = t.get("textUnderCursor");
2558
3092
  if (n >= 16) {
2559
- let e = r > 0 ? I.Up : I.Down;
2560
- await a(I.Edit | e, 35);
3093
+ let e = r > 0 ? R.Up : R.Down;
3094
+ await a(R.Edit | e, 35);
2561
3095
  } else {
2562
- let e = r > 0 ? I.Right : I.Left;
2563
- await a(I.Edit | e, 35);
3096
+ let e = r > 0 ? R.Right : R.Left;
3097
+ await a(R.Edit | e, 35);
2564
3098
  }
2565
3099
  await o(i, 250);
2566
- let l = Re(t.get("textUnderCursor"));
3100
+ let l = yt(t.get("textUnderCursor"));
2567
3101
  if (l === null || l === s) break;
2568
3102
  s = l;
2569
3103
  }
2570
3104
  }
2571
- return await a(I.Edit), Re(t.get("textUnderCursor")) === e;
3105
+ return await a(R.Edit), yt(t.get("textUnderCursor")) === e;
2572
3106
  },
2573
3107
  setNote: async (e) => {
2574
3108
  if (!r()) return !1;
@@ -2576,27 +3110,27 @@ var at = (e) => {
2576
3110
  if (s === "---" || s === "OFF") {
2577
3111
  let e = t.get("textUnderCursor"), r = e?.trim().toUpperCase() ?? "";
2578
3112
  if (r === s) return !0;
2579
- if (await a(I.Opt | I.Edit), await o(e, 250), s === "OFF" && r !== "---") {
3113
+ if (await a(R.Opt | R.Edit), await o(e, 250), s === "OFF" && r !== "---") {
2580
3114
  let e = t.get("textUnderCursor");
2581
- e?.trim().toUpperCase() === "---" && (await a(I.Opt | I.Edit), await o(e, 250));
3115
+ e?.trim().toUpperCase() === "---" && (await a(R.Opt | R.Edit), await o(e, 250));
2582
3116
  }
2583
3117
  let i = t.get("textUnderCursor")?.trim().toUpperCase();
2584
3118
  return n("[M8SDK] setNote special value result:", i), i === s;
2585
3119
  }
2586
- let c = U(e);
3120
+ let c = W(e);
2587
3121
  if (!c) return console.warn("[M8SDK] Invalid note format. Expected like C-1, C#1, D#A, ---, OFF:", e), !1;
2588
3122
  let l = t.get("textUnderCursor");
2589
- (l?.trim() === "---" || l?.trim().toUpperCase() === "OFF") && (n("[M8SDK] Initial note is \"---\" or \"OFF\", priming with edit key"), await a(I.Edit), await o(l, 250), l = t.get("textUnderCursor"));
2590
- let u = U(l);
3123
+ (l?.trim() === "---" || l?.trim().toUpperCase() === "OFF") && (n("[M8SDK] Initial note is \"---\" or \"OFF\", priming with edit key"), await a(R.Edit), await o(l, 250), l = t.get("textUnderCursor"));
3124
+ let u = W(l);
2591
3125
  if (!u) return console.warn("[M8SDK] Could not parse current note under cursor:", l), !1;
2592
3126
  if (u.semitoneIndex === c.semitoneIndex) return !0;
2593
- await a(I.Edit), await o(l, 250);
2594
- let d = ft(u.semitoneIndex, c.semitoneIndex);
3127
+ await a(R.Edit), await o(l, 250);
3128
+ let d = qt(u.semitoneIndex, c.semitoneIndex);
2595
3129
  if (d.length > 0) {
2596
3130
  for (let e of d) i(e), await B(25);
2597
3131
  await o(l, 280);
2598
- let e = U(t.get("textUnderCursor"));
2599
- if (e?.semitoneIndex === c.semitoneIndex) return await a(I.Edit), !0;
3132
+ let e = W(t.get("textUnderCursor"));
3133
+ if (e?.semitoneIndex === c.semitoneIndex) return await a(R.Edit), !0;
2600
3134
  u = e ?? u;
2601
3135
  }
2602
3136
  let f = /* @__PURE__ */ new Set();
@@ -2605,21 +3139,21 @@ var at = (e) => {
2605
3139
  for (; Date.now() - p < 7e3;) {
2606
3140
  let e = c.semitoneIndex - u.semitoneIndex;
2607
3141
  if (e === 0) break;
2608
- let n = Math.abs(e) >= 12, r = e > 0, i = n ? r ? I.Up : I.Down : r ? I.Right : I.Left, s = n ? r ? I.Down : I.Up : r ? I.Left : I.Right, l = u, d = Math.abs(l.semitoneIndex - c.semitoneIndex), p = t.get("textUnderCursor");
2609
- await a(I.Edit | i, 35), await o(p, 250);
2610
- let m = U(t.get("textUnderCursor"));
3142
+ let n = Math.abs(e) >= 12, r = e > 0, i = n ? r ? R.Up : R.Down : r ? R.Right : R.Left, s = n ? r ? R.Down : R.Up : r ? R.Left : R.Right, l = u, d = Math.abs(l.semitoneIndex - c.semitoneIndex), p = t.get("textUnderCursor");
3143
+ await a(R.Edit | i, 35), await o(p, 250);
3144
+ let m = W(t.get("textUnderCursor"));
2611
3145
  if (!m || m.label === l.label) break;
2612
3146
  if (u = m, Math.abs(u.semitoneIndex - c.semitoneIndex) > d) {
2613
3147
  let e = t.get("textUnderCursor");
2614
- await a(I.Edit | s, 35), await o(e, 250);
2615
- let n = U(t.get("textUnderCursor"));
3148
+ await a(R.Edit | s, 35), await o(e, 250);
3149
+ let n = W(t.get("textUnderCursor"));
2616
3150
  n && (u = n);
2617
3151
  break;
2618
3152
  }
2619
3153
  if (f.has(u.label)) break;
2620
3154
  f.add(u.label);
2621
3155
  }
2622
- return await a(I.Edit), U(t.get("textUnderCursor"))?.semitoneIndex === c.semitoneIndex;
3156
+ return await a(R.Edit), W(t.get("textUnderCursor"))?.semitoneIndex === c.semitoneIndex;
2623
3157
  },
2624
3158
  setValueToString: async (e, i = !0, s = !1) => {
2625
3159
  if (!r()) return !1;
@@ -2631,9 +3165,9 @@ var at = (e) => {
2631
3165
  };
2632
3166
  if (u(l())) return !0;
2633
3167
  let d = t.get("textUnderCursor");
2634
- d?.trim() === "---" && (n("[M8SDK] Initial string value is \"---\", priming with edit key"), await a(I.Edit), await o(d, 250));
3168
+ d?.trim() === "---" && (n("[M8SDK] Initial string value is \"---\", priming with edit key"), await a(R.Edit), await o(d, 250));
2635
3169
  let f = t.get("textUnderCursor");
2636
- if (await a(I.Edit), await o(f, 250), u(l())) return await a(I.Edit), !0;
3170
+ if (await a(R.Edit), await o(f, 250), u(l())) return await a(R.Edit), !0;
2637
3171
  let p = t.get("textUnderCursor"), m = 0, h = Date.now(), g = [];
2638
3172
  for (let e = 0; e < 512; e++) {
2639
3173
  if (Date.now() - h > 2e3) {
@@ -2649,26 +3183,26 @@ var at = (e) => {
2649
3183
  }
2650
3184
  }
2651
3185
  let r = p;
2652
- await a(I.Edit | I.Down, 35);
3186
+ await a(R.Edit | R.Down, 35);
2653
3187
  let i = await o(r, 250);
2654
3188
  if (z(i) === z(p) ? m += 1 : (m = 0, p = i), m > 1) break;
2655
3189
  }
2656
3190
  let _ = /* @__PURE__ */ new Set();
2657
3191
  for (let e = 0; e < 1024; e++) {
2658
3192
  let e = l();
2659
- if (u(e)) return await a(I.Edit), !0;
3193
+ if (u(e)) return await a(R.Edit), !0;
2660
3194
  let n = z(e);
2661
3195
  if (n) {
2662
3196
  if (_.has(n)) break;
2663
3197
  _.add(n);
2664
3198
  }
2665
3199
  let r = t.get("textUnderCursor");
2666
- if (await a(I.Edit | I.Right, 35), z(await o(r, 250)) === n) break;
3200
+ if (await a(R.Edit | R.Right, 35), z(await o(r, 250)) === n) break;
2667
3201
  }
2668
- return await a(I.Edit), !1;
3202
+ return await a(R.Edit), !1;
2669
3203
  }
2670
3204
  };
2671
- }, mt = ({ bus: e, store: t, cancelOnKeydown: n = !1 }) => {
3205
+ }, Yt = ({ bus: e, store: t, cancelOnKeydown: n = !1 }) => {
2672
3206
  let r = [], i = null, a = !1, o = () => {
2673
3207
  a && u("preempted by keyboard");
2674
3208
  }, s = () => {
@@ -2716,7 +3250,7 @@ var at = (e) => {
2716
3250
  r = [], i = null, c();
2717
3251
  }
2718
3252
  };
2719
- }, ht = () => ({
3253
+ }, Xt = () => ({
2720
3254
  viewName: null,
2721
3255
  viewTitle: null,
2722
3256
  minimapKey: null,
@@ -2739,9 +3273,9 @@ var at = (e) => {
2739
3273
  },
2740
3274
  systemInfo: null,
2741
3275
  macroStatus: { running: !1 }
2742
- }), gt = (e = {}) => {
3276
+ }), Zt = (e = {}) => {
2743
3277
  let t = {
2744
- ...ht(),
3278
+ ...Xt(),
2745
3279
  ...e
2746
3280
  }, n = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Set();
2747
3281
  return {
@@ -2764,8 +3298,8 @@ var at = (e) => {
2764
3298
  }),
2765
3299
  snapshot: () => ({ ...t })
2766
3300
  };
2767
- }, _t = () => 0, vt = () => 1;
2768
- function yt(e, t, n, r = _t, i = vt) {
3301
+ }, Qt = () => 0, $t = () => 1;
3302
+ function en(e, t, n, r = Qt, i = $t) {
2769
3303
  let a = t.trim(), o = n.trim();
2770
3304
  if (!e.has(a) || !e.has(o)) return null;
2771
3305
  let s = /* @__PURE__ */ new Set([a]), c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
@@ -2810,29 +3344,29 @@ function yt(e, t, n, r = _t, i = vt) {
2810
3344
  }
2811
3345
  //#endregion
2812
3346
  //#region src/engine/viewNavigation.ts
2813
- var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
2814
- let n = !!t?.shift, r = I.Shift, i = {
2815
- up: (n ? r : 0) | I.Up,
2816
- down: (n ? r : 0) | I.Down,
2817
- left: (n ? r : 0) | I.Left,
2818
- right: (n ? r : 0) | I.Right
3347
+ var tn = 20, nn = 700, rn = 80, an = 4e3, on = 120, sn = (e, t) => {
3348
+ let n = !!t?.shift, r = R.Shift, i = {
3349
+ up: (n ? r : 0) | R.Up,
3350
+ down: (n ? r : 0) | R.Down,
3351
+ left: (n ? r : 0) | R.Left,
3352
+ right: (n ? r : 0) | R.Right
2819
3353
  }, a = [];
2820
3354
  n && a.push(r);
2821
3355
  for (let t of e) a.push(i[t], n ? r : 0);
2822
3356
  return a.push(0), a;
2823
- }, Et = (e) => [{
3357
+ }, cn = (e) => [{
2824
3358
  from: e,
2825
3359
  to: "",
2826
3360
  kind: "exit",
2827
- keys: [I.Opt, 0]
3361
+ keys: [R.Opt, 0]
2828
3362
  }, {
2829
3363
  from: e,
2830
3364
  to: "",
2831
3365
  kind: "shift",
2832
3366
  dir: "up",
2833
- keys: [I.Shift | I.Up, 0]
2834
- }], Dt = (e) => `${e.kind}:${e.dir ?? ""}:${e.keys.join(",")}:${e.cursor ? `${e.cursor.x},${e.cursor.y}` : ""}`, Ot = (e) => e[e.length - 1] === 0 ? e.slice() : [...e, 0], kt = "#goal", At = (e, t) => `${e}<${t}`, jt = (e) => new Promise((t) => setTimeout(t, e)), Mt = ({ store: e, runner: t, getScreenLines: n, moveCursor: r }) => {
2835
- let i = $e(), a = null, o = 0, s = null, c = 0, l = /* @__PURE__ */ new Map(), u = "", d = e.get("viewName") || "", f = e.sub("viewName", () => {
3367
+ keys: [R.Shift | R.Up, 0]
3368
+ }], ln = (e) => `${e.kind}:${e.dir ?? ""}:${e.keys.join(",")}:${e.cursor ? `${e.cursor.x},${e.cursor.y}` : ""}`, un = (e) => e[e.length - 1] === 0 ? e.slice() : [...e, 0], dn = "#goal", fn = (e, t) => `${e}<${t}`, pn = (e) => new Promise((t) => setTimeout(t, e)), mn = ({ store: e, runner: t, getScreenLines: n, moveCursor: r }) => {
3369
+ let i = Pt(), a = null, o = 0, s = null, c = 0, l = /* @__PURE__ */ new Map(), u = "", d = e.get("viewName") || "", f = e.sub("viewName", () => {
2836
3370
  let t = e.get("viewName") || "";
2837
3371
  t && t !== d && (d && (u = d), d = t);
2838
3372
  }), p = null, m = null, h = () => {
@@ -2840,11 +3374,11 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
2840
3374
  }, g = (e) => {
2841
3375
  let t = (e || "").toLowerCase().trim();
2842
3376
  return i.has(t) || console.warn("Target not in viewlist", { goal: t }), t;
2843
- }, _ = (e, t) => `${e.from}<${t}|${Dt(e)}`, v = (e, t) => {
3377
+ }, _ = (e, t) => `${e.from}<${t}|${ln(e)}`, v = (e, t) => {
2844
3378
  let n = /* @__PURE__ */ new Map();
2845
- for (let t of Qe().graph.get(e) ?? []) {
3379
+ for (let t of Nt().graph.get(e) ?? []) {
2846
3380
  if (t.kind === "button" && !r) continue;
2847
- let e = Dt(t);
3381
+ let e = ln(t);
2848
3382
  n.set(e, [...n.get(e) ?? [], t]);
2849
3383
  }
2850
3384
  let i = [];
@@ -2868,11 +3402,11 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
2868
3402
  }
2869
3403
  return i;
2870
3404
  }, y = (e, t, n) => {
2871
- let r = /* @__PURE__ */ new Map([[kt, []]]), i = [[e, t]];
2872
- for (r.set(At(e, t), []); i.length;) {
2873
- let [e, t] = i.shift(), a = r.get(At(e, t));
3405
+ let r = /* @__PURE__ */ new Map([[dn, []]]), i = [[e, t]];
3406
+ for (r.set(fn(e, t), []); i.length;) {
3407
+ let [e, t] = i.shift(), a = r.get(fn(e, t));
2874
3408
  if (e === n) a.push({
2875
- to: kt,
3409
+ to: dn,
2876
3410
  weight: 0,
2877
3411
  nav: {
2878
3412
  from: e,
@@ -2882,7 +3416,7 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
2882
3416
  }
2883
3417
  });
2884
3418
  else for (let n of v(e, t)) {
2885
- let t = At(n.to, e);
3419
+ let t = fn(n.to, e);
2886
3420
  a.push({
2887
3421
  to: t,
2888
3422
  weight: n.weight,
@@ -2892,10 +3426,10 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
2892
3426
  }
2893
3427
  return r;
2894
3428
  }, b = (e, t) => {
2895
- let n = yt(y(e, u, t), At(e, u), kt);
3429
+ let n = en(y(e, u, t), fn(e, u), dn);
2896
3430
  if (n?.edges.length) return n.edges[0].nav;
2897
- let r = Qe().graph.get(e) ?? [];
2898
- return r.length && !r.some((e) => e.kind === "exit") ? null : Et(e).find((e) => !l.has(_(e, u)) && !l.has(_(e, "*"))) ?? null;
3431
+ let r = Nt().graph.get(e) ?? [];
3432
+ return r.length && !r.some((e) => e.kind === "exit") ? null : cn(e).find((e) => !l.has(_(e, u)) && !l.has(_(e, "*"))) ?? null;
2899
3433
  }, x = (e) => {
2900
3434
  let t = e.cursor;
2901
3435
  if (!e.label || !n) return t;
@@ -2940,12 +3474,12 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
2940
3474
  }), E();
2941
3475
  }, T = async (n, i) => {
2942
3476
  let a = x(n.edge);
2943
- S(a) || (await Promise.race([r(a.x, a.y), jt(Ct)]), await jt(wt)), i === c && p === n && (e.get("viewName") !== n.from || !S(a) ? (console.warn("Navigation: button not reached", {
3477
+ S(a) || (await Promise.race([r(a.x, a.y), pn(an)]), await pn(on)), i === c && p === n && (e.get("viewName") !== n.from || !S(a) ? (console.warn("Navigation: button not reached", {
2944
3478
  from: n.from,
2945
3479
  to: n.edge.to,
2946
3480
  cell: a,
2947
3481
  cursor: e.get("cursorPos")
2948
- }), w(e.get("viewName") === n.from ? n.from : e.get("viewName") || "")) : (n.phase = "keys", t.cancel("navigation button"), t.start(Ot(n.edge.keys))));
3482
+ }), w(e.get("viewName") === n.from ? n.from : e.get("viewName") || "")) : (n.phase = "keys", t.cancel("navigation button"), t.start(un(n.edge.keys))));
2949
3483
  }, E = () => {
2950
3484
  let n = a, r = e.get("viewName") || "";
2951
3485
  if (!n) return;
@@ -2960,7 +3494,7 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
2960
3494
  }), C(!0);
2961
3495
  return;
2962
3496
  }
2963
- if (o >= bt) {
3497
+ if (o >= tn) {
2964
3498
  console.warn("Navigation aborted: max steps reached", {
2965
3499
  startTitle: r,
2966
3500
  goalTitle: n,
@@ -2995,14 +3529,14 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
2995
3529
  T(s, c);
2996
3530
  return;
2997
3531
  }
2998
- let l = i.keys.length ? Ot(i.keys) : Tt([i.dir], { shift: !0 });
3532
+ let l = i.keys.length ? un(i.keys) : sn([i.dir], { shift: !0 });
2999
3533
  t.cancel("iterative navigation step"), t.start(l);
3000
3534
  }, D = () => {
3001
3535
  if (p?.phase !== "keys") return;
3002
3536
  let n = e.get("macroStatus").running, r = e.get("viewName") || "", i = !!r && r !== p.from;
3003
3537
  (!n || i) && (h(), m = n ? setTimeout(() => {
3004
3538
  t.cancel("navigation: no release echo"), t.start([0]), w(e.get("viewName") || "");
3005
- }, xt) : setTimeout(() => w(e.get("viewName") || ""), i ? St : xt));
3539
+ }, nn) : setTimeout(() => w(e.get("viewName") || ""), i ? rn : nn));
3006
3540
  }, O = e.sub("viewName", D), k = e.sub("macroStatus", D);
3007
3541
  return {
3008
3542
  navigateToView: (t) => new Promise((n) => {
@@ -3016,7 +3550,7 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
3016
3550
  f(), O(), k(), c += 1, C(!1);
3017
3551
  }
3018
3552
  };
3019
- }, Nt = /* @__PURE__ */ new Set([
3553
+ }, hn = /* @__PURE__ */ new Set([
3020
3554
  "macroStatus",
3021
3555
  "deviceModel",
3022
3556
  "fontMode",
@@ -3024,33 +3558,35 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
3024
3558
  "titleColor",
3025
3559
  "backgroundColor",
3026
3560
  "minimapKey"
3027
- ]), Pt = (e, t = {}) => {
3028
- let n = t.store ?? gt(), r = (...e) => {
3561
+ ]), gn = (e, t = {}) => {
3562
+ let n = t.store ?? Zt(), r = (...e) => {
3029
3563
  t.debug && console.log(...e);
3030
3564
  }, i = /* @__PURE__ */ new Map(), a = (e, t) => {
3031
3565
  for (let n of [...i.get(e) ?? []]) n(t);
3032
- }, o = it(e.protocol.eventBus, n), s = mt({
3566
+ }, o = zt(e.protocol.eventBus, n), s = Yt({
3033
3567
  bus: e,
3034
3568
  store: n,
3035
3569
  cancelOnKeydown: t.cancelMacroOnKeydown
3036
- }), c = pe({
3570
+ }), c = Ye({
3037
3571
  bus: e,
3038
- store: n
3039
- }), l = Mt({
3572
+ store: n,
3573
+ getScreenLines: o.getScreenLines,
3574
+ cursorMap: t.cursorMap ?? oe()
3575
+ }), l = mn({
3040
3576
  store: n,
3041
3577
  runner: s,
3042
3578
  getScreenLines: o.getScreenLines,
3043
- moveCursor: (e, t) => c.navigateTo(ot(n, e, t))
3044
- }), u = ut((e) => a("transport", e)), d = ke({
3579
+ moveCursor: (e, t) => c.navigateTo(Vt(n, e, t))
3580
+ }), u = Gt((e) => a("transport", e)), d = dt({
3045
3581
  getKeySink: () => e.commands,
3046
3582
  store: n,
3047
3583
  debugLog: r,
3048
3584
  getScreenLines: o.getScreenLines
3049
- }), f = ct({
3585
+ }), f = Ut({
3050
3586
  io: d,
3051
- editors: pt(d),
3052
- setValueFloat: Ke(d),
3053
- browseFile: Ie(d),
3587
+ editors: Jt(d),
3588
+ setValueFloat: Dt(d),
3589
+ browseFile: vt(d),
3054
3590
  navigateToView: (e) => l.navigateToView(e),
3055
3591
  navigateTo: (e) => c.navigateTo(e),
3056
3592
  enableMidiTransport: () => u.enable(),
@@ -3080,7 +3616,7 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
3080
3616
  };
3081
3617
  (!_ || _.textUnderCursor !== e.textUnderCursor || _.currentLine !== e.currentLine) && (_ = e, r("[M8SDK] Text updated:", e.textUnderCursor, e.currentLine), a("textUpdated", e));
3082
3618
  }
3083
- [...e].some((e) => Nt.has(e)) && a("stateChanged", at(n));
3619
+ [...e].some((e) => hn.has(e)) && a("stateChanged", Bt(n));
3084
3620
  }, y = n.subAll((e) => {
3085
3621
  p.add(e), m || (m = !0, queueMicrotask(v));
3086
3622
  }), b = (e) => {
@@ -3093,7 +3629,7 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
3093
3629
  macro: s,
3094
3630
  navigateTo: (e) => c.navigateTo(e),
3095
3631
  navigateToView: (e) => l.navigateToView(e),
3096
- getState: () => at(n),
3632
+ getState: () => Bt(n),
3097
3633
  getScreenLines: o.getScreenLines,
3098
3634
  getScreenCellColors: o.getScreenCellColors,
3099
3635
  on: (e, t) => {
@@ -3106,174 +3642,7 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
3106
3642
  e.protocol.eventBus.off("key", b), y(), l.dispose(), c.dispose(), s.dispose(), u.disable(), o.dispose(), i.clear();
3107
3643
  }
3108
3644
  };
3109
- }, W = {
3110
- version: "1.0",
3111
- description: "Semantic mapping of M8 tracker views: defines the structure of each grid view so cursor position + line text can be interpreted as typed field values.",
3112
- notes: {
3113
- fontMode: "All x/width values assume font mode 0 (standard, 12×14 px cells, 40 cols × ~22 rows). Font modes 1 (bold uppercase) and 2 (large) may shift column positions.",
3114
- playbackIndicator: "The M8 renders a '>' character one column to the left of the first data column when that step/row/track is currently playing. This character is part of the text stream; the editing cursor is a separate rect overlay.",
3115
- screenGeometry: "The screen is 40 columns x 24 rows. Every row has a one-column left margin (the playback marker is drawn in it), so x positions in this schema count from column 1 of the raw screen and cursorPos.x is the schema x + 1. Titles are on row 3, content starts on row 5. The text stream is lower case ('tempo', 'b-4', 'ff').",
3116
- emptyCell: "An empty or unused cell displays '--' (2 chars) or '---' (3 chars) or '---00' (command+value). Parsed hexValue will be null for these.",
3117
- relativeHex: "Transpose and some FX values use relative hex: 00 = no change, 01-7F = +1-127, FF-80 = -1-128. See 'Relative and Absolute FX Commands' in the manual.",
3118
- rowIndex: "Row/step indices are parsed from the leftmost characters of currentLine. They are hex for all grid views.",
3119
- titleAsterisk: "A '*' next to the item ID in the screen title (e.g. 'PHRASE 03*') indicates the item is used elsewhere in the chain or song (phrase view); on other views it marks unsaved changes. See 'An asterisk next to the phrase number' in the manual."
3120
- },
3121
- fieldTypes: {
3122
- chainRef: "Reference to a chain number (hex 00-FF). '--' means empty/unused.",
3123
- phraseRef: "Reference to a phrase number (hex 00-FF). '--' means empty/unused.",
3124
- note: "Musical note in M8 format: [A-G][#|-][0-9] e.g. 'C-4', 'A#3'. '---' = empty. 'OFF' = note-off.",
3125
- velocity: "Note velocity 00-7F (hex), from 00 (silence) to 7F (maximum). Default is 64 (hex). '--' = inherit.",
3126
- instrumentRef: "Instrument number 00-7F (hex). '--' = no retrigger (keep current instrument).",
3127
- fxCommand: "3-character sequencer or instrument FX command name, e.g. 'KIL', 'ARP', 'DEL'. '---' = none.",
3128
- fxValue: "2-char hex value 00-FF accompanying an FX command. Interpretation depends on the command.",
3129
- transpose: "Semitone or tick transpose. Relative: 00=none, 01-7F=+1-+127, FF-80=-1--128.",
3130
- ticks: "Number of ticks this step consumes in a groove (hex 01-FF). '00' skips the step. '--' = empty (groove loops at this point).",
3131
- volume: "Volume multiplier 00-FF (hex). Multiplied with the phrase-row velocity. '--' = no override.",
3132
- ppq: "Pulses Per Quarter note: resolution of the groove clock (decimal). One of 24 (default), 48, 96 or 192. Only present on groove row 0; absent on all other rows.",
3133
- instrumentName: "Instrument name string (up to 12 characters). Text field, no hex value.",
3134
- eqSlot: "EQ slot assignment (hex). '--' = no EQ applied to this instrument.",
3135
- rowIndex: "The positional index of this row in the current view (song row, chain position, phrase step, etc.).",
3136
- noteInterval: "Scale row label: one of the 12 note intervals C, C#, D, D#, E, F, F#, G, G#, A, A#, B (sharps use '#', naturals may render a dim '-' pad, e.g. 'C-').",
3137
- onOff: "Toggle field displayed as 'ON' or 'OFF'; the scale view 'EN' column shows '--' for a note out of the scale. Used by the scale view 'EN' column.",
3138
- semitoneOffset: "Signed semitone offset in decimal notation with two decimals (-24.00 to +24.00), e.g. '00.00', '-00.50', '12.00'. Used by the scale view 'OFFSET' column.",
3139
- swing: "Groove swing amount as a percentage with one decimal, e.g. '50.0%' = straight timing, higher = swung. Shown on every second groove row (0, 2, 4, …): the swing of that pair of steps (ticks 07,05 → 58.3%).",
3140
- instrumentType: "Instrument engine chosen with the TYPE field of the Instrument view: WAVSYNTH, MACROSYN, SAMPLER, FMSYNTH, HYPERSYNTH, EXTERNAL INST, MIDI OUT (NONE for an unused slot). Text value; changes which parameters the view shows.",
3141
- name: "Free-text name field (instrument, song, sample, directory). Up to 12 characters; a row of dashes means unnamed.",
3142
- parameter: "Editable value of a named parameter (CUTOFF, TEMPO, GAIN, …). Its 'format' says how to read it: hex byte, 'XX:YY' pair, decimal, decimal with a fraction, or free text.",
3143
- option: "Choice from a list, shown as its name, optionally preceded by its index in hex (e.g. '00CHORUS', 'STEREO', 'BELL'). hexValue holds the index when there is one, text holds the name.",
3144
- action: "Button-like text that does something when [EDIT] is pressed on it (LOAD, SAVE, RENDER, SETTINGS, VIEW INST. POOL, EQ, …). The text is the caption.",
3145
- fileEntry: "Row of a file browser: '/name' is a directory, '/..' the parent directory, anything else a file (e.g. 'Bass.m8i')."
3146
- },
3147
- views: /* @__PURE__ */ JSON.parse("{\"song\":{\"title\":\"Song View\",\"type\":\"grid\",\"description\":\"Arranges chains across 8 independent tracks to form the song structure. Each row is a song position (up to 256 rows); each column is a track. Each track advances vertically until it reaches an empty column ('--'), then loops back to row 00. Chains containing no notes are grayed out; chains '00' or 'FE' are commonly used as silent spacers. On row '00', [UP][UP] enters track reordering mode.\",\"rowField\":{\"x\":0,\"width\":2,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"songRow\",\"description\":\"Song row 00-FF. Each track loops back to row 00 when it reaches '--'.\"},\"columns\":[{\"x\":3,\"width\":2,\"key\":\"track1\",\"type\":\"chainRef\",\"label\":\"Track 1\",\"meta\":{\"trackIndex\":1}},{\"x\":6,\"width\":2,\"key\":\"track2\",\"type\":\"chainRef\",\"label\":\"Track 2\",\"meta\":{\"trackIndex\":2}},{\"x\":9,\"width\":2,\"key\":\"track3\",\"type\":\"chainRef\",\"label\":\"Track 3\",\"meta\":{\"trackIndex\":3}},{\"x\":12,\"width\":2,\"key\":\"track4\",\"type\":\"chainRef\",\"label\":\"Track 4\",\"meta\":{\"trackIndex\":4}},{\"x\":15,\"width\":2,\"key\":\"track5\",\"type\":\"chainRef\",\"label\":\"Track 5\",\"meta\":{\"trackIndex\":5}},{\"x\":18,\"width\":2,\"key\":\"track6\",\"type\":\"chainRef\",\"label\":\"Track 6\",\"meta\":{\"trackIndex\":6}},{\"x\":21,\"width\":2,\"key\":\"track7\",\"type\":\"chainRef\",\"label\":\"Track 7\",\"meta\":{\"trackIndex\":7}},{\"x\":24,\"width\":2,\"key\":\"track8\",\"type\":\"chainRef\",\"label\":\"Track 8\",\"meta\":{\"trackIndex\":8}}]},\"chain\":{\"title\":\"Chain View\",\"type\":\"grid\",\"description\":\"An ordered list of up to 16 phrases (256 steps). The first '--' phrase ends the chain and the song continues on to the next chain in the song view. The 'TSP' column optionally transposes every note in the phrase in semitones (relative hex).\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"chainPos\",\"description\":\"Position within the chain (0-F, max 16 entries).\"},\"columns\":[{\"x\":2,\"width\":2,\"key\":\"phrase\",\"type\":\"phraseRef\",\"label\":\"Phrase (PH)\",\"description\":\"Phrase number to play at this chain position.\"},{\"x\":5,\"width\":2,\"key\":\"transpose\",\"type\":\"transpose\",\"label\":\"Transpose (TSP)\",\"description\":\"Optional note transpose in semitones applied to every note in the phrase (relative hex).\"}]},\"phrase\":{\"title\":\"Phrase View\",\"type\":\"grid\",\"description\":\"A 16-step sequence. Each step holds a note, velocity, instrument assignment, and up to 3 FX command+value pairs. Notes cover 11 octaves (the full 128 MIDI note range); velocity defaults to 64 (hex); an empty instrument column plays the note without retriggering the instrument. A '*' next to the phrase number indicates the phrase is used elsewhere in the chain or song.\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"step\",\"description\":\"Step index 0-F (16th note by default; altered by groove).\"},\"columns\":[{\"x\":2,\"width\":3,\"key\":\"note\",\"type\":\"note\",\"label\":\"Note (N)\"},{\"x\":6,\"width\":2,\"key\":\"vel\",\"type\":\"velocity\",\"label\":\"Velocity (V)\"},{\"x\":9,\"width\":2,\"key\":\"inst\",\"type\":\"instrumentRef\",\"label\":\"Instrument (I)\"},{\"x\":12,\"width\":3,\"key\":\"fx1cmd\",\"type\":\"fxCommand\",\"label\":\"FX1 Command\"},{\"x\":15,\"width\":2,\"key\":\"fx1val\",\"type\":\"fxValue\",\"label\":\"FX1 Value\"},{\"x\":18,\"width\":3,\"key\":\"fx2cmd\",\"type\":\"fxCommand\",\"label\":\"FX2 Command\"},{\"x\":21,\"width\":2,\"key\":\"fx2val\",\"type\":\"fxValue\",\"label\":\"FX2 Value\"},{\"x\":24,\"width\":3,\"key\":\"fx3cmd\",\"type\":\"fxCommand\",\"label\":\"FX3 Command\"},{\"x\":27,\"width\":2,\"key\":\"fx3val\",\"type\":\"fxValue\",\"label\":\"FX3 Value\"}]},\"table\":{\"title\":\"Table View\",\"type\":\"grid\",\"description\":\"A 16-step mini-sequencer that runs alongside an instrument. Used for arpeggios, multi-stage envelopes, and automated FX. The 'N' column transposes the instrument for the step (relative hex); 'V' multiplies with the phrase-row velocity. Each instrument has a dedicated table plus 128 assignable tables (TBL command). FX columns can run at independent speeds via TIC; a TIC on the last table row affects all rows. HOP shortens or loops the table.\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"step\",\"description\":\"Table step 0-F. Loops from bottom unless a HOP command redirects.\"},\"columns\":[{\"x\":2,\"width\":2,\"key\":\"transpose\",\"type\":\"transpose\",\"label\":\"Transpose (N)\",\"description\":\"Transposes the instrument for this step (relative hex). '--' = no transpose.\"},{\"x\":5,\"width\":2,\"key\":\"volume\",\"type\":\"volume\",\"label\":\"Volume (V)\",\"description\":\"Volume multiplier applied on top of the phrase velocity (hex). '--' = no override.\"},{\"x\":8,\"width\":3,\"key\":\"fx1cmd\",\"type\":\"fxCommand\",\"label\":\"FX1 Command\",\"description\":\"3-char command name for table FX slot 1.\"},{\"x\":11,\"width\":2,\"key\":\"fx1val\",\"type\":\"fxValue\",\"label\":\"FX1 Value\",\"description\":\"Value for table FX1.\"},{\"x\":14,\"width\":3,\"key\":\"fx2cmd\",\"type\":\"fxCommand\",\"label\":\"FX2 Command\",\"description\":\"3-char command name for table FX slot 2.\"},{\"x\":17,\"width\":2,\"key\":\"fx2val\",\"type\":\"fxValue\",\"label\":\"FX2 Value\",\"description\":\"Value for table FX2.\"},{\"x\":20,\"width\":3,\"key\":\"fx3cmd\",\"type\":\"fxCommand\",\"label\":\"FX3 Command\",\"description\":\"3-char command name for table FX slot 3.\"},{\"x\":23,\"width\":2,\"key\":\"fx3val\",\"type\":\"fxValue\",\"label\":\"FX3 Value\",\"description\":\"Value for table FX3.\"}]},\"groove\":{\"title\":\"Groove View\",\"type\":\"grid\",\"description\":\"Defines the timing of each phrase step in ticks. Used to create swing, shuffle, triplets, and other rhythmic feels. At the default 24 PPQ a step is 6 ticks and a full phrase totals 96 ticks. The groove loops when a '--' entry is reached; a '00' entry skips the phrase step. Row 0 also holds the PPQ resolution (24/48/96/192) and the SWING percentage; editing the row 0 tick value alters rows 0 and 1 together (a common swing setting is 07,05 or 08,04).\",\"rowField\":{\"x\":0,\"width\":1,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"step\",\"description\":\"Groove step 0-F. The groove loops when a '--' entry is reached.\"},\"columns\":[{\"x\":2,\"width\":2,\"key\":\"ticks\",\"type\":\"ticks\",\"label\":\"Ticks (TIC)\",\"description\":\"Number of ticks this step takes. Default is 06 (at 24 PPQN = 1 sixteenth note). '00' skips the step.\"},{\"x\":6,\"width\":2,\"key\":\"ppq\",\"type\":\"ppq\",\"label\":\"PPQ\",\"description\":\"Pulses Per Quarter note: 24, 48, 96 or 192. Only present on groove row 0; absent on all other rows.\"},{\"x\":10,\"width\":5,\"key\":\"swing\",\"type\":\"swing\",\"label\":\"Swing\",\"description\":\"Swing amount as a percentage (e.g. '50.0%' = straight). Shown on every second row (0, 2, 4, …) for that pair of steps; empty on the others.\"}]},\"scale\":{\"title\":\"Scale View\",\"type\":\"grid\",\"description\":\"Defines and quantizes note-related events (project/chain transpose, note entry, ARP, PIT, and the MIDIOUT instrument CHD and ADD commands). Up to 16 user-defined scales per project; scale '00' is the default for all 8 tracks and can be overridden per track with SCA/SCG FX commands. Each of the 12 note intervals can be enabled ('EN' = ON) with an optional detuning offset (-24.00 to +24.00 semitones); a note out of the scale reads '-- -- --'. Rows that are not column-mapped (Model:02, fw 6.5): KEY under the title (global root note for the default scale, the same on every scale), then after the intervals TUNE (A4 reference in Hz, e.g. '440.00'), NAME (up to 16 characters, 92 factory scales load from /Scales/Factory), and the LOAD/SAVE actions.\",\"columns\":[{\"x\":0,\"width\":2,\"key\":\"interval\",\"type\":\"noteInterval\",\"label\":\"Note Interval\",\"description\":\"Note interval row label: C, C#, D, D#, E, F, F#, G, G#, A, A#, B.\"},{\"x\":3,\"width\":3,\"key\":\"en\",\"type\":\"onOff\",\"label\":\"Enabled (EN)\",\"description\":\"Enables this note interval in the scale: 'ON', or '--' when the note is out of the scale (the device draws the row '-- -- --').\"},{\"x\":6,\"width\":6,\"key\":\"offset\",\"type\":\"semitoneOffset\",\"label\":\"Offset (OFFSET)\",\"description\":\"Detune offset in semitones, decimal with two decimals (-24.00 to +24.00), e.g. '00.00'.\"}]},\"instrumentpool\":{\"title\":\"Instrument Pool\",\"type\":\"grid\",\"description\":\"Lists all 128 instrument slots with their mix routing. Each row shows the instrument name (up to 12 chars, '------------' = empty slot) and its dry/FX send levels. Mixer-related settings can be adjusted per instrument here; [EDIT] on an empty slot loads an instrument or sample.\",\"rowField\":{\"x\":0,\"width\":2,\"parse\":\"hex\",\"type\":\"rowIndex\",\"key\":\"instIndex\",\"description\":\"Instrument slot index 00-7F.\"},\"columns\":[{\"x\":3,\"width\":12,\"key\":\"name\",\"type\":\"instrumentName\",\"label\":\"Instrument Name\",\"description\":\"Name of the instrument in this slot (up to 12 chars). '------------' = empty slot.\"},{\"x\":16,\"width\":2,\"key\":\"dry\",\"type\":\"volume\",\"label\":\"Dry (DRY)\",\"description\":\"Direct output volume for this instrument (hex 00-FF).\"},{\"x\":19,\"width\":2,\"key\":\"mx\",\"type\":\"volume\",\"label\":\"ModFX (MX)\",\"description\":\"ModFX send level (hex 00-FF).\"},{\"x\":22,\"width\":2,\"key\":\"de\",\"type\":\"volume\",\"label\":\"Delay (DE)\",\"description\":\"Delay send level (hex 00-FF).\"},{\"x\":25,\"width\":2,\"key\":\"rv\",\"type\":\"volume\",\"label\":\"Reverb (RV)\",\"description\":\"Reverb send level (hex 00-FF).\"},{\"x\":28,\"width\":2,\"key\":\"eq\",\"type\":\"eqSlot\",\"label\":\"EQ Slot (EQ)\",\"description\":\"EQ slot assignment. '--' = no EQ.\"}]},\"inst\":{\"title\":\"Instrument View\",\"type\":\"parameter\",\"description\":\"Per-instrument parameters for the selected slot: TYPE (Wavsynth, Macrosynth, Sampler, FM Synth, Hypersynth, External Instrument, MIDI Out), LOAD/SAVE presets, NAME, TRANSP. (enable/disable transposition from scales and transpose columns), TABLE TIC (default table speed), and EQ slot, plus the synth engine parameters. Almost every parameter has an associated FX command; highlighting a parameter and navigating to the phrase or table view sets that parameter's FX command as default.\"},\"instmods\":{\"title\":\"Instrument Modulation View\",\"type\":\"parameter\",\"description\":\"Four configurable modulation slots per instrument: AHD envelope, ADSR envelope, DRUM envelope, LFO, TRIG envelope, or TRACKING, each with a destination parameter (DEST) and optional source (SRC).\"},\"mixer\":{\"title\":\"Mixer View\",\"type\":\"parameter\",\"description\":\"Mix levels for the 8 tracks, the 3 send effects (MX/DE/RE), the analog input (INPUT, with dual-mono mode) and USB input, the DJ filter (DJF), OTT compression, main EQ, limiter (LIM) and main volume (MIX). All signals are summed through OTT, main EQ, limiter, DJ filter and main volume. Most values are also exposed as FX commands (VMV, VMX, VDE, VRE, VT1-8, DJC/DJR/DJT, IVO/IMX/IDE/IRV, USB, ...).\"},\"project\":{\"title\":\"Project View\",\"type\":\"parameter\",\"description\":\"Song-wide settings such as tempo, project transpose (identical to the TSP FX command), and LIVE QUANTIZE behavior for cueing tracks in live mode. [SHIFT]+[OPTION] stores a song snapshot; [SHIFT]+[EDIT] recalls it.\"},\"effectsettings\":{\"title\":\"Effect Settings View\",\"type\":\"parameter\",\"description\":\"Settings for the 3 send effects: ModFX type/depth/frequency/width, Delay time/feedback/width, and Reverb size/decay/modulation/width/freeze — all addressable via the XMT-XMW, XDT-XDR and XRS-XRZ FX commands.\"},\"mixeq\":{\"title\":\"Mix EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the main mix EQ slot (assign with the EQM FX command). Selecting EQ in the Mixer view and pressing [EDIT] or [SHIFT]+[RIGHT] opens this view.\"},\"modfxeq\":{\"title\":\"ModFX EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the ModFX send path.\"},\"delayeq\":{\"title\":\"Delay EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the Delay send path.\"},\"reverbeq\":{\"title\":\"Reverb EQ View\",\"type\":\"parameter\",\"description\":\"EQ editor for the Reverb send path.\"},\"mixscope\":{\"title\":\"Mix Scope View\",\"type\":\"parameter\",\"description\":\"Graphical scope of the main mix activity. Opened from the Mixer view with MIX selected via [EDIT] or [SHIFT]+[RIGHT].\"},\"limiterscope\":{\"title\":\"Limiter Scope View\",\"type\":\"parameter\",\"description\":\"Graphical scope of the limiter's compression activity (the white line on the MIX indicator). Opened from the Mixer view with LIM selected via [EDIT] or [SHIFT]+[RIGHT].\"},\"systemsettings\":{\"title\":\"System Settings View\",\"type\":\"parameter\",\"description\":\"System-level settings for the device.\"},\"loadinstrument\":{\"title\":\"Load Instrument\",\"type\":\"parameter\",\"description\":\"File browser for loading an instrument preset from the SD card ([EDIT] on an empty instrument slot, or LOAD in the Instrument view).\"},\"selectsavedirectory\":{\"title\":\"Select Save Directory\",\"type\":\"parameter\",\"description\":\"File browser directory-selection step when saving a preset or project.\"},\"createdirectory\":{\"title\":\"Create Directory\",\"type\":\"parameter\",\"description\":\"File browser dialog for creating a new directory on the SD card.\"}}"),
3148
- fxCommands: /* @__PURE__ */ JSON.parse("{\"sequencer\":{\"ARP\":{\"description\":\"Arpeggio: plays 3 notes rapidly. X=first interval semitones, Y=second interval semitones.\"},\"ARC\":{\"description\":\"Arpeggio Config: X=direction and number of octaves, Y=speed in ticks.\"},\"CHA\":{\"description\":\"Chance: X=probability left of command (0=never, F=always), Y=probability right. In a table: probability for everything to the left of CHA from 00 (never) to FF (always).\"},\"DEL\":{\"description\":\"Delay: in phrase delays entire row by XX ticks; in table delays table playhead.\"},\"GRV\":{\"description\":\"Groove: sets groove number XX for the current track.\"},\"GGR\":{\"description\":\"Global Groove: sets groove XX for all tracks.\"},\"HOP\":{\"description\":\"Hop: in phrase jumps to row Y of next phrase; HOPFF stops track. In table loops to row Y X times.\"},\"INS\":{\"description\":\"Instrument: changes active instrument to XX as an FX command.\"},\"KIL\":{\"description\":\"Kill: stops the playing instrument after XX ticks.\"},\"RND\":{\"description\":\"Random: randomises the preceding FX command. X=left range, Y=right range (0=none, F=full).\"},\"RNL\":{\"description\":\"Randomise Left: randomises the FX command to the left (X=left range, Y=right range). In the first column RNL randomises the note and instrument number in phrase, or note and velocity in table.\"},\"RET\":{\"description\":\"Retrig: retriggers current row. Y≠0: retrig every Y ticks with X volume ramp. Y=0: single retrig after X ticks.\"},\"REP\":{\"description\":\"Repeat: repeats and increments the last FX command by XX each step. Active until a new command or REP00 stops it; it does not need to appear on every row. In a table: loops the last row XX times. A double-caret '^^' appears next to an FX lane while a REP from a previous phrase is active.\"},\"RTO\":{\"description\":\"Repeat To: use after REP to hold at the given value (the min/max REP stops at). In a table, used alone: loops until the relative value is reached.\"},\"RMX\":{\"description\":\"Remix: repositions the phrase playhead (Y) on tracks to the left (selected by X).\"},\"NTH\":{\"description\":\"Nth Trigger: conditional trigger on the loop count. X=affects what is left of the command, Y=what is right (in a table: what is left).\"},\"PSL\":{\"description\":\"Pitch Slide (portamento): slides to the given note over XX ticks.\"},\"PBN\":{\"description\":\"Pitch Bend: continuous pitch slide up (00-7F) or down (80-FF).\"},\"PVB\":{\"description\":\"Vibrato: X=speed, Y=depth.\"},\"PVX\":{\"description\":\"Extreme Vibrato: like PVB but with higher range.\"},\"SCA\":{\"description\":\"Track Scale: X=key signature, Y=scale number for this track.\"},\"SCG\":{\"description\":\"Global Scale: X=key, Y=scale number for the whole song.\"},\"SNG\":{\"description\":\"Song Hop: moves this track's song position by XX rows relative to current. Ignored if the resulting song position is not playable.\"},\"SED\":{\"description\":\"Random Seed: sets the random seed XX for this track.\"},\"TBL\":{\"description\":\"Table: assigns table number XX to the current instrument.\"},\"THO\":{\"description\":\"Table Hop: jumps all table columns to position 0X.\"},\"TIC\":{\"description\":\"Table Tick: sets the table tick rate. 00 = increment row on each instrument trigger; 01-FB = ticks per row; FC = Octave Map (playing octave maps to table row); FD = Velocity Map; FE = Note Map (pair with HOP00 on row 0C to limit to 12 notes/octave); FF = increment at 200 Hz. In a table each FX column can run independently; a TIC on the last table row affects all rows.\"},\"TBX\":{\"description\":\"Aux Table: runs table XX in parallel with the instrument table. 00=stop aux table. A TIC in the same row as a TBX affects the auxiliary table's tick rate.\"},\"TPO\":{\"description\":\"Tempo: sets song BPM to XX. In a table the tempo is relative.\"},\"TSP\":{\"description\":\"Global Song Transpose: transposes the entire song by XX semitones. Identical to the transpose value in the Project View.\"},\"NXT\":{\"description\":\"Next Track Trigger: plays instrument XX on the track to the right using the current note/velocity. Does not work on track 8.\"},\"OFF\":{\"description\":\"Note Off: stops the playing instrument after XX ticks. Triggers ADSR release stage.\"},\"MTT\":{\"description\":\"Micro Time: adjusts row playback timing in positive or negative sub-tick (1/8 tick) amounts. Negative amounts do not work on the first phrase row. Currently has no action in tables.\"}},\"instrument\":{\"VOL\":{\"description\":\"Volume (relative).\",\"relative\":true},\"PIT\":{\"description\":\"Pitch in semitones (relative).\",\"relative\":true},\"FIN\":{\"description\":\"Fine tune: pitch shift of up to about one semitone (relative).\",\"relative\":true},\"PLY\":{\"description\":\"Sampler play mode (FWD, REV, loop modes…).\"},\"STA\":{\"description\":\"Sampler start position.\"},\"LOP\":{\"description\":\"Sampler loop start (relative).\",\"relative\":true},\"LEN\":{\"description\":\"Sampler length (relative).\",\"relative\":true},\"DEG\":{\"description\":\"Degrade amount (relative).\",\"relative\":true},\"SLI\":{\"description\":\"Sampler slice to play.\"},\"ERR\":{\"description\":\"Error: randomizes pitch and parameters by the given amount.\"},\"FLT\":{\"description\":\"Filter type.\"},\"FIL\":{\"description\":\"Filter type.\"},\"ALG\":{\"description\":\"FM algorithm.\"},\"FM1\":{\"description\":\"FM modulation amount 1 (relative).\",\"relative\":true},\"FM2\":{\"description\":\"FM modulation amount 2 (relative).\",\"relative\":true},\"FM3\":{\"description\":\"FM modulation amount 3 (relative).\",\"relative\":true},\"FM4\":{\"description\":\"FM modulation amount 4 (relative).\",\"relative\":true},\"SNC\":{\"description\":\"Sync: on an FM synth the FM sync phase position; on a Hypersynth it offsets the sync of the oscillators by the given amount.\"},\"CRD\":{\"description\":\"Hypersynth chord set.\"},\"CVO\":{\"description\":\"Hypersynth chord voice offset: X=voice, Y=offset.\"},\"SWM\":{\"description\":\"Hypersynth oscillator swarm (relative).\",\"relative\":true},\"WID\":{\"description\":\"Hypersynth stereo width (relative).\",\"relative\":true},\"SUB\":{\"description\":\"Hypersynth sub-oscillator amount (relative).\",\"relative\":true},\"OSC\":{\"description\":\"Macrosynth oscillator shape.\"},\"TBR\":{\"description\":\"Macrosynth timbre (relative).\",\"relative\":true},\"COL\":{\"description\":\"Macrosynth color (relative).\",\"relative\":true},\"RED\":{\"description\":\"Macrosynth bit reduction (relative).\",\"relative\":true},\"TRG\":{\"description\":\"Macrosynth trigger/strike.\"},\"CUT\":{\"description\":\"Filter cutoff (relative).\",\"relative\":true},\"RES\":{\"description\":\"Filter resonance (relative).\",\"relative\":true},\"AMP\":{\"description\":\"Amp level (relative).\",\"relative\":true},\"LIM\":{\"description\":\"Limit mode: how the amp stage clips.\"},\"PAN\":{\"description\":\"Pan (relative).\",\"relative\":true},\"DRY\":{\"description\":\"Dry volume: the instrument's level in the main mix.\"},\"SMX\":{\"description\":\"Send to ModFX.\"},\"SDL\":{\"description\":\"Send to Delay.\"},\"SRV\":{\"description\":\"Send to Reverb.\"},\"CHD\":{\"description\":\"Chord (MIDIOUT instrument): triggers a chord. Quantized by the active scale.\"},\"ADD\":{\"description\":\"Add Note (MIDIOUT instrument): adds a note to the chord. Quantized by the active scale.\"}},\"modulation\":{\"EA\":{\"description\":\"Envelope amount of modulator N (relative).\",\"relative\":true},\"AT\":{\"description\":\"Envelope attack of modulator N.\"},\"HO\":{\"description\":\"Envelope hold of modulator N.\"},\"DE\":{\"description\":\"Envelope decay of modulator N.\"},\"ET\":{\"description\":\"Envelope trigger of modulator N: retriggers the envelope.\"},\"LA\":{\"description\":\"LFO amount of modulator N (relative).\",\"relative\":true},\"LO\":{\"description\":\"LFO oscillator shape of modulator N.\"},\"LS\":{\"description\":\"LFO trigger settings of modulator N.\"},\"LF\":{\"description\":\"LFO frequency of modulator N.\"},\"LT\":{\"description\":\"LFO trigger of modulator N: at the given offset, resets the LFO to that position.\"}},\"mixer\":{\"VMV\":{\"description\":\"Main Volume: sets the main song output volume.\"},\"VMX\":{\"description\":\"ModFX Volume.\"},\"VDE\":{\"description\":\"Delay Volume.\"},\"VRE\":{\"description\":\"Reverb Volume.\"},\"VT1\":{\"description\":\"Track 1 Volume.\"},\"VT2\":{\"description\":\"Track 2 Volume.\"},\"VT3\":{\"description\":\"Track 3 Volume.\"},\"VT4\":{\"description\":\"Track 4 Volume.\"},\"VT5\":{\"description\":\"Track 5 Volume.\"},\"VT6\":{\"description\":\"Track 6 Volume.\"},\"VT7\":{\"description\":\"Track 7 Volume.\"},\"VT8\":{\"description\":\"Track 8 Volume.\"},\"DJC\":{\"description\":\"DJ Filter Cutoff.\"},\"DJR\":{\"description\":\"DJ Filter Resonance.\"},\"DJT\":{\"description\":\"DJ Filter Type.\"},\"EQM\":{\"description\":\"Assign main mix EQ slot.\"},\"EQI\":{\"description\":\"Assign current instrument EQ slot.\"},\"XMT\":{\"description\":\"Send Effect: ModFX mod type and phase position (Effect Settings view).\"},\"XMM\":{\"description\":\"Send Effect: ModFX mod depth (Effect Settings view).\"},\"XMF\":{\"description\":\"Send Effect: ModFX mod frequency (Effect Settings view).\"},\"XMW\":{\"description\":\"Send Effect: ModFX stereo width (Effect Settings view).\"},\"XMR\":{\"description\":\"Send Effect: ModFX to reverb mix (Effect Settings view).\"},\"XDT\":{\"description\":\"Send Effect: Delay time — X = left, Y = right, in large increments (Effect Settings view).\"},\"XDF\":{\"description\":\"Send Effect: Delay feedback (Effect Settings view).\"},\"XDW\":{\"description\":\"Send Effect: Delay stereo width (Effect Settings view).\"},\"XDR\":{\"description\":\"Send Effect: Delay to reverb mix (Effect Settings view).\"},\"XRS\":{\"description\":\"Send Effect: Reverb room size (Effect Settings view).\"},\"XRD\":{\"description\":\"Send Effect: Reverb decay (Effect Settings view).\"},\"XRM\":{\"description\":\"Send Effect: Reverb mod depth (Effect Settings view).\"},\"XRF\":{\"description\":\"Send Effect: Reverb mod frequency (Effect Settings view).\"},\"XRW\":{\"description\":\"Send Effect: Reverb stereo width (Effect Settings view).\"},\"XRZ\":{\"description\":\"Send Effect: Reverb freeze — enabled with a value greater than 00, disabled at 00.\"},\"VIN\":{\"description\":\"Line Input Mixer: analog input volume (Mixer view).\"},\"XRH\":{\"description\":\"Send Effect: Reverb shimmer (Effect Settings view).\"},\"OTT\":{\"description\":\"OTT mix amount (Mixer OTT settings; OTC and OTI default to 80).\"},\"OTC\":{\"description\":\"OTT color (default 80).\"},\"OTI\":{\"description\":\"OTT time (default 80).\"},\"IVO\":{\"description\":\"Alias of VIN as named in the manual: analog input volume (Mixer view).\"},\"IMX\":{\"description\":\"Line Input Mixer: analog input ModFX send (Mixer view).\"},\"IDE\":{\"description\":\"Line Input Mixer: analog input delay send (Mixer view).\"},\"IRV\":{\"description\":\"Line Input Mixer: analog input reverb send (Mixer view). Also referenced as 'IRE' in the manual text.\"},\"IRE\":{\"description\":\"Alias of IRV: analog input reverb send (Mixer view).\"},\"IV2\":{\"description\":\"Line Input Mixer: volume for the 2nd input (right channel in dual-mono input mode, set in the Mixer view).\"},\"IM2\":{\"description\":\"Line Input Mixer: ModFX send for the 2nd input (right channel in dual-mono input mode).\"},\"ID2\":{\"description\":\"Line Input Mixer: delay send for the 2nd input (right channel in dual-mono input mode).\"},\"IR2\":{\"description\":\"Line Input Mixer: reverb send for the 2nd input (right channel in dual-mono input mode).\"},\"USB\":{\"description\":\"USB input volume (Mixer view).\"}}}"),
3149
- instrumentFx: {
3150
- _note: "Instrument commands offered by [EDIT]+[UP/DOWN] (Effect Command Help view) in the Current Instrument section depend on the instrument type; the Instrument Mods section depends on the modulators of that instrument, and Sequencer FX and Mixer/Effects are the same for every type. Lists marked verified were read from a real M8 (Model:02). Wavsynth, External and MIDI Out were not available on that device: their lists are not known here.",
3151
- types: {
3152
- sampler: {
3153
- verified: !0,
3154
- commands: [
3155
- "VOL",
3156
- "PIT",
3157
- "FIN",
3158
- "PLY",
3159
- "STA",
3160
- "LOP",
3161
- "LEN",
3162
- "DEG",
3163
- "FLT",
3164
- "CUT",
3165
- "RES",
3166
- "AMP",
3167
- "LIM",
3168
- "PAN",
3169
- "SLI",
3170
- "ERR",
3171
- "DRY",
3172
- "SMX",
3173
- "SDL",
3174
- "SRV"
3175
- ]
3176
- },
3177
- macrosynth: {
3178
- verified: !0,
3179
- commands: [
3180
- "VOL",
3181
- "PIT",
3182
- "FIN",
3183
- "OSC",
3184
- "TBR",
3185
- "COL",
3186
- "DEG",
3187
- "RED",
3188
- "FIL",
3189
- "CUT",
3190
- "RES",
3191
- "AMP",
3192
- "LIM",
3193
- "PAN",
3194
- "TRG",
3195
- "ERR",
3196
- "DRY",
3197
- "SMX",
3198
- "SDL",
3199
- "SRV"
3200
- ]
3201
- },
3202
- fmsynth: {
3203
- verified: !0,
3204
- commands: [
3205
- "VOL",
3206
- "PIT",
3207
- "FIN",
3208
- "ALG",
3209
- "FM1",
3210
- "FM2",
3211
- "FM3",
3212
- "FM4",
3213
- "FIL",
3214
- "CUT",
3215
- "RES",
3216
- "AMP",
3217
- "LIM",
3218
- "PAN",
3219
- "SNC",
3220
- "ERR",
3221
- "DRY",
3222
- "SMX",
3223
- "SDL",
3224
- "SRV"
3225
- ]
3226
- },
3227
- hypersynth: {
3228
- verified: !0,
3229
- commands: [
3230
- "VOL",
3231
- "PIT",
3232
- "FIN",
3233
- "CRD",
3234
- "CVO",
3235
- "SWM",
3236
- "WID",
3237
- "SUB",
3238
- "FIL",
3239
- "CUT",
3240
- "RES",
3241
- "AMP",
3242
- "LIM",
3243
- "PAN",
3244
- "SNC",
3245
- "ERR",
3246
- "DRY",
3247
- "SMX",
3248
- "SDL",
3249
- "SRV"
3250
- ]
3251
- },
3252
- midiout: {
3253
- verified: !1,
3254
- commands: ["CHD", "ADD"]
3255
- }
3256
- },
3257
- modulation: {
3258
- _note: "Modulator slot N (1-4) offers the commands of its kind. Seen: an envelope slot (AHD) offers EA AT HO DE ET, an LFO slot offers LA LO LS LF LT. Slot kinds vary per instrument (e.g. Sampler 00: env, env, LFO, LFO; Hypersynth 01: env, LFO, LFO, LFO). Other modulator kinds were not captured.",
3259
- envelope: [
3260
- "EA",
3261
- "AT",
3262
- "HO",
3263
- "DE",
3264
- "ET"
3265
- ],
3266
- lfo: [
3267
- "LA",
3268
- "LO",
3269
- "LS",
3270
- "LF",
3271
- "LT"
3272
- ]
3273
- },
3274
- relativeCommands: { description: "Relative commands add to the instrument parameter's current value (01-7F add, 80-FF subtract) and keep accumulating until a note is triggered with a RET command or with an instrument number in the I column; that resets every parameter to the instrument's own value." }
3275
- }
3276
- }, Ft = {
3645
+ }, _n = {
3277
3646
  version: "1.0",
3278
3647
  description: "Layouts of the non-grid ('parameter') views: which text at which column is an instrument type, name, parameter, option or action. Column positions were measured on the manual's screenshots (font mode 0, 40 columns) and cross-checked with the phrase view grid; descriptions come from the manual.",
3279
3648
  notes: {
@@ -3283,7 +3652,7 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
3283
3652
  unverified: "Positions are read off the manual's screenshots, not from a device capture. Instrument-type specific layouts other than the Wavsynth header/parameter grid (Sampler SAMPLE row, FM operators, Hypersynth chord, MIDI CC rows) are only reached through the generic label/value zones."
3284
3653
  },
3285
3654
  views: /* @__PURE__ */ JSON.parse("{\"project\":{\"rows\":[{\"id\":\"tempo\",\"match\":[{\"x\":0,\"text\":\"TEMPO\"}],\"cells\":[{\"x\":13,\"width\":6,\"key\":\"tempo\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" BPM\",\"label\":\"Tempo (TEMPO)\",\"description\":\"Song speed in beats per minute. [EDIT] pressed rhythmically sets the tempo by tapping.\"}]},{\"id\":\"transpose\",\"match\":[{\"x\":0,\"text\":\"TRANSPOSE\"}],\"cells\":[{\"x\":13,\"width\":2,\"key\":\"transpose\",\"type\":\"transpose\",\"format\":\"hex\",\"label\":\"Project Transpose (TRANSPOSE)\",\"description\":\"Transposes the whole song in semitones (same as the TSP command). Only affects instruments with TRANSP. enabled; applied when [EDIT] is released.\"}]},{\"id\":\"groove\",\"match\":[{\"x\":0,\"text\":\"GROOVE\"}],\"cells\":[{\"x\":13,\"width\":14,\"key\":\"groove\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Default Groove (GROOVE)\",\"description\":\"Groove used when the song plays (number and name); applied when [EDIT] is released. See Groove View.\"}]},{\"id\":\"scale\",\"match\":[{\"x\":0,\"text\":\"SCALE\"}],\"cells\":[{\"x\":13,\"width\":16,\"key\":\"scale\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Default Scale (SCALE)\",\"description\":\"Scale (number, key and name) used when the song plays; applied when [EDIT] is released. See Scale View.\"}]},{\"id\":\"liveQuantize\",\"match\":[{\"x\":0,\"text\":\"LIVE QUANTIZ\"}],\"cells\":[{\"x\":13,\"width\":14,\"key\":\"liveQuantize\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Live Quantize (LIVE QUANTIZE)\",\"description\":\"How long a cued chain waits before starting when playing in live mode.\"}]},{\"id\":\"midi\",\"match\":[{\"x\":0,\"text\":\"MIDI\"},{\"x\":13,\"text\":\"SETTINGS\"}],\"cells\":[{\"x\":13,\"width\":8,\"key\":\"midiSettings\",\"type\":\"action\",\"format\":\"text\",\"label\":\"MIDI Settings\",\"description\":\"Open the MIDI Settings View.\"},{\"x\":22,\"width\":8,\"key\":\"midiMappings\",\"type\":\"action\",\"format\":\"text\",\"label\":\"MIDI Mappings\",\"description\":\"Open the MIDI Mappings View.\"}]},{\"id\":\"name\",\"match\":[{\"x\":0,\"text\":\"NAME\"}],\"cells\":[{\"x\":13,\"width\":12,\"key\":\"name\",\"type\":\"name\",\"format\":\"text\",\"label\":\"Song Name (NAME)\",\"description\":\"Name of the current song, entered before saving.\"}]},{\"id\":\"file\",\"match\":[{\"x\":0,\"text\":\"PROJECT\"},{\"x\":13,\"text\":\"LOAD\"}],\"cells\":[{\"x\":13,\"width\":4,\"key\":\"load\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Load Song\",\"description\":\"Load a song from the SD card.\"},{\"x\":18,\"width\":4,\"key\":\"save\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Save Song\",\"description\":\"Save the current song.\"},{\"x\":23,\"width\":3,\"key\":\"new\",\"type\":\"action\",\"format\":\"text\",\"label\":\"New Song\",\"description\":\"Create a new song.\"}]},{\"id\":\"export\",\"match\":[{\"x\":0,\"text\":\"EXPORT/SHARE\"}],\"cells\":[{\"x\":13,\"width\":6,\"key\":\"render\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Render\",\"description\":\"Open the Render View to render audio.\"},{\"x\":20,\"width\":6,\"key\":\"bundle\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Bundle\",\"description\":\"Bundle the song with the instruments and samples it uses.\"}]},{\"id\":\"clearUnused\",\"match\":[{\"x\":0,\"text\":\"CLEAR UNUSED\"}],\"cells\":[{\"x\":13,\"width\":7,\"key\":\"clearPhrases\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Clear Unused Phrases\",\"description\":\"Frees chains and phrases that are not used in the song.\"},{\"x\":21,\"width\":8,\"key\":\"clearInstTbl\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Clear Unused Instruments/Tables\",\"description\":\"Frees instruments, EQs and tables that are not used in the song.\"}]},{\"id\":\"instPool\",\"match\":[{\"x\":0,\"text\":\"INST. POOL\"}],\"cells\":[{\"x\":13,\"width\":15,\"key\":\"viewInstPool\",\"type\":\"action\",\"format\":\"text\",\"label\":\"View Instrument Pool\",\"description\":\"Go to the Instrument Pool View.\"}]},{\"id\":\"timeStats\",\"match\":[{\"x\":0,\"text\":\"TIME STATS\"}],\"cells\":[{\"x\":13,\"width\":15,\"key\":\"viewTimeStats\",\"type\":\"action\",\"format\":\"text\",\"label\":\"View Time Stats\",\"description\":\"Open the Time Stats View (time spent on the song).\"}]},{\"id\":\"system\",\"match\":[{\"x\":0,\"text\":\"SYSTEM\"},{\"x\":13,\"text\":\"SETTINGS\"}],\"cells\":[{\"x\":13,\"width\":8,\"key\":\"systemSettings\",\"type\":\"action\",\"format\":\"text\",\"label\":\"System Settings\",\"description\":\"Go to the System Settings View.\"},{\"x\":22,\"width\":9,\"key\":\"usbDrive\",\"type\":\"action\",\"format\":\"text\",\"label\":\"USB Drive\",\"description\":\"Puts the M8 in USB drive mode so SD card files can be transferred to a computer.\"}]}]},\"systemsettings\":{\"rows\":[{\"id\":\"backlight\",\"match\":[{\"x\":0,\"text\":\"BACKLIGHT\"}],\"cells\":[{\"x\":15,\"width\":2,\"key\":\"backlight\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Backlight (BACKLIGHT)\",\"description\":\"Backlight LED brightness of the display.\"}]},{\"id\":\"fontOptions\",\"match\":[{\"x\":0,\"text\":\"FONT OPTIONS\"}],\"cells\":[{\"x\":15,\"width\":15,\"key\":\"fontOptions\",\"type\":\"option\",\"format\":\"text\",\"label\":\"Font Options (FONT OPTIONS)\",\"description\":\"Font settings, including a large font mode for readability.\"}]},{\"id\":\"theme\",\"match\":[{\"x\":0,\"text\":\"THEME\"}],\"cells\":[{\"x\":15,\"width\":10,\"key\":\"editTheme\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Theme (EDIT THEME)\",\"description\":\"Open the Theme View.\"}]},{\"id\":\"notePreview\",\"match\":[{\"x\":0,\"text\":\"NOTE PREVIEW\"}],\"cells\":[{\"x\":15,\"width\":3,\"key\":\"notePreview\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Note Preview (NOTE PREVIEW)\",\"description\":\"Plays the note when editing notes in the phrase view.\"}]},{\"id\":\"recCountIn\",\"match\":[{\"x\":0,\"regex\":\"^REC\\\\.\\\\s?(COUNT IN|METRONOME)\\\\s*$\",\"width\":13}],\"cells\":[{\"x\":15,\"width\":6,\"key\":\"recCountIn\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Recording Count-in (REC. COUNT IN)\",\"description\":\"Number of phrases/measures of metronome count-in before recording audio in the Sample Editor or Selection to Sample.\"}]},{\"id\":\"metronomeVol\",\"match\":[{\"x\":0,\"text\":\"METRONOME VOL\"}],\"cells\":[{\"x\":15,\"width\":2,\"key\":\"metronomeVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Metronome Volume (METRONOME VOL)\",\"description\":\"Volume of the metronome sound (edit /System/Metronome.m8i to change the sound).\"}]},{\"id\":\"songTemplate\",\"match\":[{\"x\":0,\"text\":\"SONG TEMPLATE\"}],\"cells\":[{\"x\":15,\"width\":4,\"key\":\"songTemplateSave\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Song Template (SAVE)\",\"description\":\"Use the currently opened song as the template for new songs.\"},{\"x\":20,\"width\":5,\"key\":\"songTemplateClear\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Song Template (CLEAR)\",\"description\":\"Clear the song template.\"}]},{\"id\":\"usbAudioMode\",\"match\":[{\"x\":0,\"text\":\"USB AUDIO MODE\"}],\"cells\":[{\"x\":15,\"width\":14,\"key\":\"usbAudioMode\",\"type\":\"option\",\"format\":\"text\",\"label\":\"USB Audio Mode (USB AUDIO MODE)\",\"description\":\"USB output as stereo or multi-channel (multi-channel sends all 8 tracks and the effects individually, 12 stereo channels).\",\"options\":{\"STEREO OUT\":\"Stereo mix over USB.\"}}]},{\"id\":\"usbMainOut\",\"match\":[{\"x\":0,\"text\":\"USB MAIN OUT\"}],\"cells\":[{\"x\":15,\"width\":6,\"key\":\"usbMainOut\",\"type\":\"option\",\"format\":\"text\",\"label\":\"USB Main Out (USB MAIN OUT)\",\"description\":\"Routing behaviour of USB audio (see the USB audio routing options).\"}]},{\"id\":\"lineInGate\",\"match\":[{\"x\":0,\"text\":\"LINE-IN GATE\"}],\"cells\":[{\"x\":15,\"width\":6,\"key\":\"lineInGate\",\"type\":\"option\",\"format\":\"text\",\"label\":\"Line-in Gate (LINE-IN GATE)\",\"description\":\"Noise gate threshold for the line input, adjustable from -70 dB to -10 dB (OFF disables it).\"}]},{\"id\":\"splashScreen\",\"match\":[{\"x\":0,\"text\":\"SPLASH SCREEN\"}],\"cells\":[{\"x\":15,\"width\":3,\"key\":\"splashScreen\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Splash Screen (SPLASH SCREEN)\",\"description\":\"Show the M8 logo and play the start-up sound when turning on the device.\"}]},{\"id\":\"hpProtection\",\"match\":[{\"x\":0,\"text\":\"HP PROTECTION\"}],\"cells\":[{\"x\":15,\"width\":3,\"key\":\"hpProtection\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Headphone Protection (HP PROTECTION)\",\"description\":\"Limits the headphone output volume to F0 when a pack is unplugged and re-inserted.\"}]},{\"id\":\"keyDelayRep\",\"match\":[{\"x\":0,\"text\":\"KEY DELAY:REP\"}],\"cells\":[{\"x\":15,\"width\":7,\"key\":\"keyDelayRep\",\"type\":\"parameter\",\"format\":\"decimalPair\",\"unit\":\" ms\",\"parts\":[\"delay\",\"repeat\"],\"label\":\"Key Delay:Repeat (KEY DELAY:REP)\",\"description\":\"Key repeat timing in milliseconds: delay before a held key starts repeating, then the time between repeats.\"}]},{\"id\":\"battCharging\",\"match\":[{\"x\":0,\"regex\":\"^BATT\\\\.\\\\s?(CHARGING|STATUS)\\\\s*$\",\"width\":14}],\"cells\":[{\"x\":15,\"width\":14,\"key\":\"battCharging\",\"type\":\"option\",\"format\":\"text\",\"label\":\"Battery Charging (BATT. CHARGING / STATUS)\",\"description\":\"Whether the battery charges while the M8 is on and USB is connected (turn off to reduce power drawn from the host); also shows the current charging status.\"}]}]},\"effectsettings\":{\"sections\":[\"MODFX\",\"DELAY\",\"REVERB\"],\"rows\":[{\"id\":\"modfxType\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"MOD TYPE\"}],\"cells\":[{\"x\":21,\"width\":10,\"key\":\"modfxType\",\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"ModFX Type (MOD TYPE)\",\"description\":\"ModFX effect: Chorus, Phaser or Flanger (also sets the phase position).\",\"options\":{\"CHORUS\":\"Stereo chorus.\",\"PHASER\":\"Stereo phaser.\",\"FLANGER\":\"Stereo flanger.\"}}]},{\"id\":\"modfxInputEq\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"INPUT EQ\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"modfxInputEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Input EQ (INPUT EQ)\",\"description\":\"Open the EQ editor for this effect's input.\"}]},{\"id\":\"modfxDepthFreq\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"MOD DEPTH:FRQ\"}],\"cells\":[{\"x\":21,\"width\":5,\"key\":\"modfxDepthFreq\",\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"mod depth\",\"mod frequency\"],\"label\":\"ModFX Mod Depth:Frequency (MOD DEPTH:FRQ)\",\"description\":\"Amount of modulation in the stereo signal (first value) and speed of the modulation (second value).\"}]},{\"id\":\"modfxWidth\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"STEREO WIDTH\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"modfxWidth\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (STEREO WIDTH)\",\"description\":\"Stereo width: 00 is mono, FF is stereo.\"}]},{\"id\":\"modfxReverbSend\",\"section\":\"MODFX\",\"match\":[{\"x\":7,\"text\":\"REVERB SEND\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"modfxReverbSend\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Reverb Send (REVERB SEND)\",\"description\":\"Amount of this effect sent to the reverb.\"}]},{\"id\":\"delayInputEq\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"INPUT EQ\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"delayInputEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Input EQ (INPUT EQ)\",\"description\":\"Open the EQ editor for this effect's input.\"}]},{\"id\":\"delayTime\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"TIME L:R\"}],\"cells\":[{\"x\":21,\"width\":5,\"key\":\"delayTime\",\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"left time\",\"right time\"],\"label\":\"Delay Time L:R (TIME L:R)\",\"description\":\"Delay time for the left and right channels: first digit in beats, second digit in fractions of a beat (40 is 4 beats).\"}]},{\"id\":\"delayFeedback\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"FEEDBACK\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"delayFeedback\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Feedback (FEEDBACK)\",\"description\":\"Amount of the delayed signal fed back into the delay.\"}]},{\"id\":\"delayWidth\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"STEREO WIDTH\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"delayWidth\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (STEREO WIDTH)\",\"description\":\"Stereo width: 00 is mono, FF is stereo.\"}]},{\"id\":\"delayReverbSend\",\"section\":\"DELAY\",\"match\":[{\"x\":7,\"text\":\"REVERB SEND\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"delayReverbSend\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Reverb Send (REVERB SEND)\",\"description\":\"Amount of this effect sent to the reverb.\"}]},{\"id\":\"reverbInputEq\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"INPUT EQ\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"reverbInputEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Input EQ (INPUT EQ)\",\"description\":\"Open the EQ editor for this effect's input.\"}]},{\"id\":\"reverbRoomSize\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"ROOM SIZE\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"reverbRoomSize\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Room Size (ROOM SIZE)\",\"description\":\"Size of the reverb room.\"}]},{\"id\":\"reverbDecayShimmer\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"DECAY:SHIMMER\"}],\"cells\":[{\"x\":21,\"width\":5,\"key\":\"reverbDecayShimmer\",\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"decay\",\"shimmer\"],\"label\":\"Reverb Decay:Shimmer (DECAY:SHIMMER)\",\"description\":\"Reverb decay time (first value) and shimmer amount (second value).\"}]},{\"id\":\"reverbDepthFreq\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"MOD DEPTH:FRQ\"}],\"cells\":[{\"x\":21,\"width\":5,\"key\":\"reverbDepthFreq\",\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"mod depth\",\"mod frequency\"],\"label\":\"Reverb Mod Depth:Frequency (MOD DEPTH:FRQ)\",\"description\":\"Reverb modulation depth (first value) and frequency (second value).\"}]},{\"id\":\"reverbWidth\",\"section\":\"REVERB\",\"match\":[{\"x\":7,\"text\":\"STEREO WIDTH\"}],\"cells\":[{\"x\":21,\"width\":2,\"key\":\"reverbWidth\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (STEREO WIDTH)\",\"description\":\"Stereo width: 00 is mono, FF is stereo.\"}]}]},\"mixer\":{\"rows\":[{\"id\":\"speaker\",\"match\":[{\"x\":0,\"text\":\"SPEAKER\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"speakerVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Speaker Volume (SPEAKER VOL)\",\"description\":\"Volume of the built-in speaker.\"}]},{\"id\":\"outputVol\",\"match\":[{\"x\":0,\"text\":\"OUTPUT VOL\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"outputVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Output Volume (OUTPUT VOL)\",\"description\":\"Volume of the audio output (headphones / line out).\"}]},{\"id\":\"tracks\",\"match\":[{\"x\":0,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2},{\"x\":21,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2}],\"cells\":[{\"x\":0,\"width\":2,\"key\":\"track1\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 1 Volume\",\"meta\":{\"trackIndex\":1},\"description\":\"Volume of track 1. Instrument volumes are applied on top; the track's send levels are set in the instruments.\"},{\"x\":3,\"width\":2,\"key\":\"track2\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 2 Volume\",\"meta\":{\"trackIndex\":2},\"description\":\"Volume of track 2.\"},{\"x\":6,\"width\":2,\"key\":\"track3\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 3 Volume\",\"meta\":{\"trackIndex\":3},\"description\":\"Volume of track 3.\"},{\"x\":9,\"width\":2,\"key\":\"track4\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 4 Volume\",\"meta\":{\"trackIndex\":4},\"description\":\"Volume of track 4.\"},{\"x\":12,\"width\":2,\"key\":\"track5\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 5 Volume\",\"meta\":{\"trackIndex\":5},\"description\":\"Volume of track 5.\"},{\"x\":15,\"width\":2,\"key\":\"track6\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 6 Volume\",\"meta\":{\"trackIndex\":6},\"description\":\"Volume of track 6.\"},{\"x\":18,\"width\":2,\"key\":\"track7\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 7 Volume\",\"meta\":{\"trackIndex\":7},\"description\":\"Volume of track 7.\"},{\"x\":21,\"width\":2,\"key\":\"track8\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Track 8 Volume\",\"meta\":{\"trackIndex\":8},\"description\":\"Volume of track 8.\"}]},{\"id\":\"eq\",\"match\":[{\"x\":27,\"text\":\"EQ\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"mixEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Main Mix EQ (EQ)\",\"description\":\"Open the main mix EQ editor ([EDIT] or [SHIFT]+[RIGHT]).\"}]},{\"id\":\"levels\",\"match\":[{\"x\":0,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2},{\"x\":3,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2},{\"x\":6,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2},{\"x\":12,\"regex\":\"^[0-9A-F]{2}$\",\"width\":2}],\"cells\":[{\"x\":0,\"width\":2,\"key\":\"modfxVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"ModFX Volume (MX)\",\"description\":\"Output level of the ModFX send effect.\"},{\"x\":3,\"width\":2,\"key\":\"delayVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Delay Volume (DE)\",\"description\":\"Output level of the delay send effect.\"},{\"x\":6,\"width\":2,\"key\":\"reverbVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Reverb Volume (RE)\",\"description\":\"Output level of the reverb send effect.\"},{\"x\":12,\"width\":2,\"key\":\"inputVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input Volume (INPUT)\",\"description\":\"Volume of the analog input for monitoring. In dual-mono mode this is the left channel.\"},{\"x\":15,\"width\":2,\"key\":\"inputVol2\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input Volume, 2nd channel\",\"description\":\"Enables dual-mono input mode when set (right channel volume); clear with [EDIT]+[OPTION] to disable.\"},{\"x\":18,\"width\":2,\"key\":\"usbVol\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"USB Input Volume (USB)\",\"description\":\"Volume of anything the USB host computer sends to the M8.\"},{\"x\":27,\"width\":2,\"key\":\"mainVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Main Volume (MIX)\",\"description\":\"Main song volume, applied after the limiter, DJ filter and OTT. [EDIT] or [SHIFT]+[RIGHT] opens the Mix Scope.\"}]},{\"id\":\"limiter\",\"match\":[{\"x\":0,\"text\":\"MX\"},{\"x\":3,\"text\":\"DE\"},{\"x\":6,\"text\":\"RE\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"limiter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter (LIM)\",\"description\":\"Main mix limiter amount; the limiter is engaged when the value is above 00. [EDIT] or [SHIFT]+[RIGHT] opens the Limiter Scope.\"}]},{\"id\":\"sendModfx\",\"match\":[{\"x\":9,\"text\":\"MX\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"inputModfx\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input ModFX Send (INPUT MX)\",\"description\":\"How much of the analog input goes to the ModFX effect.\"},{\"x\":18,\"width\":2,\"key\":\"usbModfx\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"USB Input ModFX Send (USB MX)\",\"description\":\"How much of the USB input goes to the ModFX effect.\"},{\"x\":27,\"width\":2,\"key\":\"djFilter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"DJ Filter (DJF)\",\"description\":\"DJ filter cutoff: 80 is off, below 80 low-pass (or band-stop), above 80 high-pass. Mode and options are in the Scope View.\"}]},{\"id\":\"sendDelay\",\"match\":[{\"x\":9,\"text\":\"DE\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"inputDelay\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input Delay Send (INPUT DE)\",\"description\":\"How much of the analog input goes to the delay effect.\"},{\"x\":18,\"width\":2,\"key\":\"usbDelay\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"USB Input Delay Send (USB DE)\",\"description\":\"How much of the USB input goes to the delay effect.\"},{\"x\":27,\"width\":2,\"key\":\"ott\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"OTT (OTT)\",\"description\":\"Over The Top multi-band compression mixed in parallel: 00 to 80 mixes it in; from 80 and above the main mix is faded out.\"}]},{\"id\":\"sendReverb\",\"match\":[{\"x\":9,\"text\":\"RE\"}],\"cells\":[{\"x\":12,\"width\":2,\"key\":\"inputReverb\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"Analog Input Reverb Send (INPUT RE)\",\"description\":\"How much of the analog input goes to the reverb effect.\"},{\"x\":18,\"width\":2,\"key\":\"usbReverb\",\"type\":\"volume\",\"format\":\"hex\",\"label\":\"USB Input Reverb Send (USB RE)\",\"description\":\"How much of the USB input goes to the reverb effect.\"}]}]},\"mixeq\":{\"rows\":[{\"id\":\"gain\",\"match\":[{\"x\":0,\"text\":\"GAIN\"}],\"cells\":[{\"x\":5,\"width\":6,\"key\":\"lowGain\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" dB\",\"label\":\"LOW Gain\",\"description\":\"Boost (positive) or cut (negative) applied by the LOW band, in decibels.\"},{\"x\":16,\"width\":6,\"key\":\"midGain\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" dB\",\"label\":\"MID Gain\",\"description\":\"Boost (positive) or cut (negative) applied by the MID band, in decibels.\"},{\"x\":27,\"width\":6,\"key\":\"highGain\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" dB\",\"label\":\"HIGH Gain\",\"description\":\"Boost (positive) or cut (negative) applied by the HIGH band, in decibels.\"}]},{\"id\":\"freq\",\"match\":[{\"x\":0,\"text\":\"FREQ\"}],\"cells\":[{\"x\":6,\"width\":5,\"key\":\"lowFreq\",\"type\":\"parameter\",\"format\":\"decimal\",\"unit\":\" Hz\",\"label\":\"LOW Frequency\",\"description\":\"Centre (or corner) frequency of the LOW band.\"},{\"x\":17,\"width\":5,\"key\":\"midFreq\",\"type\":\"parameter\",\"format\":\"decimal\",\"unit\":\" Hz\",\"label\":\"MID Frequency\",\"description\":\"Centre (or corner) frequency of the MID band.\"},{\"x\":28,\"width\":5,\"key\":\"highFreq\",\"type\":\"parameter\",\"format\":\"decimal\",\"unit\":\" Hz\",\"label\":\"HIGH Frequency\",\"description\":\"Centre (or corner) frequency of the HIGH band.\"}]},{\"id\":\"q\",\"match\":[{\"x\":0,\"text\":\"Q\"},{\"x\":1,\"regex\":\"^\\\\s?$\",\"width\":1}],\"cells\":[{\"x\":6,\"width\":3,\"key\":\"lowQ\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"LOW Q\",\"description\":\"Quality factor (bandwidth) of the LOW band: higher values narrow it.\"},{\"x\":17,\"width\":3,\"key\":\"midQ\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"MID Q\",\"description\":\"Quality factor (bandwidth) of the MID band: higher values narrow it.\"},{\"x\":28,\"width\":3,\"key\":\"highQ\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"HIGH Q\",\"description\":\"Quality factor (bandwidth) of the HIGH band: higher values narrow it.\"}]},{\"id\":\"type\",\"match\":[{\"x\":0,\"text\":\"TYPE\"}],\"cells\":[{\"x\":6,\"width\":9,\"key\":\"lowType\",\"type\":\"option\",\"format\":\"text\",\"label\":\"LOW Type\",\"description\":\"Filter shape of the LOW band.\",\"options\":{\"LOWCUT\":\"Removes frequencies below the cutoff.\",\"LOWSHELF\":\"Boosts or cuts frequencies below the shelf frequency.\",\"BELL\":\"Peaking filter: boosts or cuts around the centre frequency.\",\"BANDPASS\":\"Passes frequencies around the centre frequency, attenuating both sides.\",\"HI.SHELF\":\"Boosts or cuts frequencies above the shelf frequency.\",\"HI.CUT\":\"Attenuates or removes frequencies above the cutoff.\",\"ALLPASS\":\"Passes all frequencies at equal gain while shifting their phase.\"}},{\"x\":17,\"width\":9,\"key\":\"midType\",\"type\":\"option\",\"format\":\"text\",\"label\":\"MID Type\",\"description\":\"Filter shape of the MID band.\",\"options\":{\"LOWCUT\":\"Removes frequencies below the cutoff.\",\"LOWSHELF\":\"Boosts or cuts frequencies below the shelf frequency.\",\"BELL\":\"Peaking filter: boosts or cuts around the centre frequency.\",\"BANDPASS\":\"Passes frequencies around the centre frequency, attenuating both sides.\",\"HI.SHELF\":\"Boosts or cuts frequencies above the shelf frequency.\",\"HI.CUT\":\"Attenuates or removes frequencies above the cutoff.\",\"ALLPASS\":\"Passes all frequencies at equal gain while shifting their phase.\"}},{\"x\":28,\"width\":9,\"key\":\"highType\",\"type\":\"option\",\"format\":\"text\",\"label\":\"HIGH Type\",\"description\":\"Filter shape of the HIGH band.\",\"options\":{\"LOWCUT\":\"Removes frequencies below the cutoff.\",\"LOWSHELF\":\"Boosts or cuts frequencies below the shelf frequency.\",\"BELL\":\"Peaking filter: boosts or cuts around the centre frequency.\",\"BANDPASS\":\"Passes frequencies around the centre frequency, attenuating both sides.\",\"HI.SHELF\":\"Boosts or cuts frequencies above the shelf frequency.\",\"HI.CUT\":\"Attenuates or removes frequencies above the cutoff.\",\"ALLPASS\":\"Passes all frequencies at equal gain while shifting their phase.\"}}]},{\"id\":\"mode\",\"match\":[{\"x\":0,\"text\":\"MODE\"}],\"cells\":[{\"x\":6,\"width\":6,\"key\":\"lowMode\",\"type\":\"option\",\"format\":\"text\",\"label\":\"LOW Mode\",\"description\":\"How the LOW band is applied to the stereo signal.\",\"options\":{\"STEREO\":\"Processes left and right identically.\",\"MID\":\"Affects only the mid (centre) information.\",\"SIDE\":\"Affects only the side information (difference between left and right).\",\"LEFT\":\"Affects only the left channel.\",\"RIGHT\":\"Affects only the right channel.\"}},{\"x\":17,\"width\":6,\"key\":\"midMode\",\"type\":\"option\",\"format\":\"text\",\"label\":\"MID Mode\",\"description\":\"How the MID band is applied to the stereo signal.\",\"options\":{\"STEREO\":\"Processes left and right identically.\",\"MID\":\"Affects only the mid (centre) information.\",\"SIDE\":\"Affects only the side information (difference between left and right).\",\"LEFT\":\"Affects only the left channel.\",\"RIGHT\":\"Affects only the right channel.\"}},{\"x\":28,\"width\":6,\"key\":\"highMode\",\"type\":\"option\",\"format\":\"text\",\"label\":\"HIGH Mode\",\"description\":\"How the HIGH band is applied to the stereo signal.\",\"options\":{\"STEREO\":\"Processes left and right identically.\",\"MID\":\"Affects only the mid (centre) information.\",\"SIDE\":\"Affects only the side information (difference between left and right).\",\"LEFT\":\"Affects only the left channel.\",\"RIGHT\":\"Affects only the right channel.\"}}]}]},\"modfxeq\":{\"extends\":\"mixeq\"},\"delayeq\":{\"extends\":\"mixeq\"},\"reverbeq\":{\"extends\":\"mixeq\"},\"mixscope\":{\"rows\":[{\"id\":\"limiterAtkRel\",\"match\":[{\"x\":9,\"text\":\"ATK\"},{\"x\":16,\"text\":\"REL\"},{\"x\":23,\"text\":\"LIM\"}],\"cells\":[{\"x\":13,\"width\":2,\"key\":\"attack\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter Attack (ATK)\",\"description\":\"Limiter attack time from 0 to 100 ms (see the on-screen help text for the conversion to milliseconds).\"},{\"x\":20,\"width\":2,\"key\":\"release\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter Release (REL)\",\"description\":\"Limiter release time from 4 to 1000 ms; 00 activates AUTO mode (100-900 ms depending on the compression amount).\"},{\"x\":27,\"width\":2,\"key\":\"limiter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter (LIM)\",\"description\":\"Limiter amount.\"}]},{\"id\":\"zoomPeak\",\"match\":[{\"x\":0,\"text\":\"ZOOM\"}],\"cells\":[{\"x\":5,\"width\":5,\"key\":\"zoom\",\"type\":\"parameter\",\"format\":\"text\",\"label\":\"Zoom (ZOOM)\",\"description\":\"Lower limit of the scope histogram, in decibels.\"},{\"x\":20,\"width\":6,\"key\":\"peak\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" dB\",\"label\":\"Peak (PEAK)\",\"description\":\"Current peak level of the main mix in dB. Resets when MIX or LIM change; clear it with [OPT]+[EDIT].\"}]},{\"id\":\"eq\",\"match\":[{\"x\":27,\"text\":\"EQ\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"mixEq\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Main Mix EQ (EQ)\",\"description\":\"Open the main mix EQ editor.\"}]},{\"id\":\"softClip\",\"match\":[{\"x\":9,\"text\":\"SOFT CLIP\"}],\"cells\":[{\"x\":19,\"width\":3,\"key\":\"softClip\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Soft Clip (SOFT CLIP)\",\"description\":\"Gentle saturation after the limiter to prevent harsh clipping; raise MIX to exaggerate it.\"},{\"x\":27,\"width\":2,\"key\":\"mainVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Main Volume (MIX)\",\"description\":\"Main mix / song volume, applied after the limiter stage.\"}]},{\"id\":\"mix\",\"match\":[{\"x\":23,\"text\":\"MIX\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"mainVol\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Main Volume (MIX)\",\"description\":\"Main mix / song volume, applied after the limiter stage.\"}]},{\"id\":\"lim\",\"match\":[{\"x\":23,\"text\":\"LIM\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"limiter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter (LIM)\",\"description\":\"Limiter amount.\"}]},{\"id\":\"djf\",\"match\":[{\"x\":23,\"text\":\"DJF\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"djFilter\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"DJ Filter (DJF)\",\"description\":\"DJ filter cutoff: 80 is off, 00-80 low-pass, 80-FF high-pass (mode set with TYPE).\"}]},{\"id\":\"ott\",\"match\":[{\"x\":23,\"text\":\"OTT\"}],\"cells\":[{\"x\":27,\"width\":2,\"key\":\"ott\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"OTT (OTT)\",\"description\":\"OTT mix amount; from 80 and above the main mix volume is faded out.\"}]}],\"zones\":[{\"id\":\"right\",\"labelX\":23,\"labelWidth\":4,\"x\":27,\"width\":6}],\"vocabulary\":{\"ATK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter Attack (ATK)\",\"description\":\"Limiter attack time from 0 to 100 ms (see the on-screen help text for the conversion).\"},\"REL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Limiter Release (REL)\",\"description\":\"Limiter release time from 4 to 1000 ms; 00 activates AUTO mode (100-900 ms depending on the compression amount).\"},\"TYPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"DJ Filter Type (TYPE)\",\"description\":\"DJ filter type: low-pass/high-pass, low-pass/band-stop or band-stop/high-pass.\"},\"RES\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"DJ Filter Resonance (RES)\",\"description\":\"Resonance of the DJ filter.\"},\"TIME\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"OTT Time (TIME)\",\"description\":\"OTT compressor envelope time as a percentage from 10% to 1000%.\"},\"COLOR\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"OTT Color (COLOR)\",\"description\":\"OTT compressor bands and low/high gains: above 80 shifts toward treble, below 80 toward bass.\"}}},\"limiterscope\":{\"extends\":\"mixscope\"},\"inst\":{\"rows\":[{\"id\":\"type\",\"match\":[{\"x\":0,\"text\":\"TYPE\"}],\"cells\":[{\"x\":8,\"width\":13,\"key\":\"instrumentType\",\"type\":\"instrumentType\",\"format\":\"text\",\"label\":\"Instrument Type (TYPE)\",\"description\":\"Kind of instrument in this slot: Wavsynth, Macrosynth, Sampler, FM Synth, Hypersynth, External Instrument, MIDI Out (or NONE for an unused slot). Changing it changes the parameters below.\"},{\"x\":22,\"width\":4,\"key\":\"load\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Load Preset (LOAD)\",\"description\":\"Load an instrument preset from the SD card.\"},{\"x\":27,\"width\":4,\"key\":\"save\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Save Preset (SAVE)\",\"description\":\"Save this instrument as a preset on the SD card.\"}]},{\"id\":\"name\",\"match\":[{\"x\":0,\"text\":\"NAME\"}],\"cells\":[{\"x\":8,\"width\":12,\"key\":\"name\",\"type\":\"name\",\"format\":\"text\",\"label\":\"Instrument Name (NAME)\",\"description\":\"Name of the instrument: helps keep the song organised when browsing, saving or bundling instruments.\"}]},{\"id\":\"transp\",\"match\":[{\"x\":0,\"text\":\"TRANSP.\"}],\"cells\":[{\"x\":8,\"width\":3,\"key\":\"transpose\",\"type\":\"onOff\",\"format\":\"text\",\"label\":\"Transpose Enabled (TRANSP.)\",\"description\":\"Enable or disable note transposition from scales, chain transpose columns and the project transpose for this instrument (turn off for percussive or effect instruments).\"},{\"x\":21,\"width\":2,\"key\":\"tableTic\",\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Table Tick (TBL. TIC)\",\"description\":\"Default speed at which this instrument's table advances: 01 is one tick per step, 02 two ticks per step. Can be overridden by the TIC command.\"},{\"x\":29,\"width\":2,\"key\":\"eq\",\"type\":\"eqSlot\",\"format\":\"hex\",\"label\":\"EQ Slot (EQ)\",\"description\":\"EQ bank assigned to this instrument (-- for none). [EDIT] or [SHIFT]+[RIGHT] opens the EQ editor.\"}]}],\"zones\":[{\"id\":\"left\",\"labelX\":0,\"labelWidth\":8,\"x\":8,\"width\":24},{\"id\":\"right\",\"labelX\":18,\"labelWidth\":4,\"x\":22,\"width\":11}],\"operators\":{\"instrument\":\"fmsynth\",\"x\":8,\"step\":6,\"width\":5,\"letters\":[\"A\",\"B\",\"C\",\"D\"]},\"vocabularyFrom\":\"instrumentTypes\"},\"instmods\":{\"slotLabel\":\"MOD\\\\d\",\"zones\":[{\"id\":\"left\",\"labelX\":0,\"labelWidth\":4,\"x\":5,\"width\":10},{\"id\":\"right\",\"labelX\":16,\"labelWidth\":4,\"x\":21,\"width\":10}],\"vocabulary\":{\"MOD#\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Modulator Type\",\"description\":\"Type of this modulation slot: AHD envelope, ADSR envelope, DRUM envelope, LFO, TRIG envelope or TRACKING. Slots can modulate their neighbour (Mod 1 affects Mod 2, 2→3, 3→4, 4→1).\",\"options\":{\"AHD ENV\":\"Attack Hold Decay envelope based on tempo.\",\"ADSR ENV\":\"Traditional Attack Decay Sustain Release envelope.\",\"DRUM ENV\":\"Sharp peak and swelling hold envelope for percussive elements.\",\"LFO\":\"Tempo-synced low frequency oscillator.\",\"TRIG ENV\":\"AHD envelope triggered by another playing track or instrument.\",\"TRACKING\":\"Modulates an instrument parameter from notes or velocities.\",\"OFF\":\"Slot unused.\"}},\"DEST\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Destination (DEST)\",\"description\":\"Instrument parameter this modulator controls. MOD AMT, MOD RATE or MOD BOTH modulate the neighbouring modulator's amount, rate or both.\"},\"SRC\":{\"type\":\"parameter\",\"format\":\"hexOption\",\"label\":\"Source (SRC)\",\"description\":\"Modulation source. Trig envelope: an instrument number (00-7F) or a whole track (80-87 = tracks 1-8). Tracking: NOTE, VELOCITY or VELOCITY TAKEOVER (velocity no longer affects instrument volume).\"},\"AMT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Amount (AMT)\",\"description\":\"How strongly the modulator is applied to the destination.\"},\"ATK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Attack (ATK)\",\"description\":\"Time to reach the amount (in ticks, relative to tempo).\"},\"HOLD\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Hold (HOLD)\",\"description\":\"Time to wait after the attack before decaying (in ticks).\"},\"DEC\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Decay (DEC)\",\"description\":\"Time to fall back (AHD/Drum: to zero; ADSR: to the sustain level), in ticks.\"},\"SUS\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Sustain (SUS)\",\"description\":\"Level held while the note is playing (ADSR).\"},\"REL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Release (REL)\",\"description\":\"Time to fall back to zero after the note is released (ADSR); needs an OFF note or FX command.\"},\"PEAK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Peak (PEAK)\",\"description\":\"Shape of the initial transient (drum envelope); modifies the duck amount and peak time.\"},\"BODY\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Body (BODY)\",\"description\":\"Time to hold the modulation before decaying (drum envelope).\"},\"OSC\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"LFO Shape (OSC)\",\"description\":\"LFO shape: triangle, sine, ramp down/up, exponential down/up, square down/up, random, drunk. The 'T' variants run in ticks (much faster).\",\"options\":{\"TRI\":\"Triangle.\",\"SIN\":\"Sine.\",\"RAMP DN\":\"Ramp down.\",\"RAMP UP\":\"Ramp up.\",\"EXP DN\":\"Exponential down.\",\"EXP UP\":\"Exponential up.\",\"SQU DN\":\"Square down.\",\"SQU UP\":\"Square up.\",\"RANDOM\":\"Random values.\",\"DRUNK\":\"Random walk.\"}},\"TRIG\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"LFO Trigger (TRIG)\",\"description\":\"How the LFO reacts when the instrument triggers and when the shape ends.\",\"options\":{\"FREE\":\"Loop the shape and don't reset on new notes.\",\"RETRIG\":\"Loop the shape and reset on each instrument trigger.\",\"HOLD\":\"Hold the last value of the shape.\",\"ONCE\":\"Play the shape once and reset to the start value.\"}},\"FREQ\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"LFO Frequency (FREQ)\",\"description\":\"Rate of the LFO in steps (16th notes), or in ticks for the 'T' shapes.\"},\"LVAL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Low Value (LVAL)\",\"description\":\"Lowest value of the source range scaled to the destination (tracking). Can be higher than HVAL to invert the range.\"},\"HVAL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"High Value (HVAL)\",\"description\":\"Highest value of the source range scaled to the destination (tracking).\"}}},\"scale\":{\"notes\":\"Only the rows below the 12 interval rows; the intervals themselves are grid columns in m8-view-context.json.\",\"rows\":[{\"id\":\"key\",\"match\":[{\"x\":0,\"text\":\"KEY\"}],\"cells\":[{\"x\":6,\"width\":2,\"key\":\"key\",\"type\":\"option\",\"format\":\"text\",\"label\":\"Key (KEY)\",\"description\":\"Global key: the root note of the default scale (can be overridden per track with SCA / SCG).\"}]},{\"id\":\"tune\",\"match\":[{\"x\":0,\"text\":\"TUNE\"}],\"cells\":[{\"x\":6,\"width\":6,\"key\":\"tune\",\"type\":\"parameter\",\"format\":\"float\",\"unit\":\" Hz\",\"label\":\"Tuning (TUNE)\",\"description\":\"Reference pitch of A4 in Hz (440.00 is standard).\"}]},{\"id\":\"name\",\"match\":[{\"x\":0,\"text\":\"NAME\"}],\"cells\":[{\"x\":6,\"width\":16,\"key\":\"name\",\"type\":\"name\",\"format\":\"text\",\"label\":\"Scale Name (NAME)\",\"description\":\"Name of this scale (up to 16 characters); 92 factory scales load from /Scales/Factory.\"}]},{\"id\":\"file\",\"match\":[{\"x\":6,\"text\":\"LOAD\"},{\"x\":11,\"text\":\"SAVE\"}],\"cells\":[{\"x\":6,\"width\":4,\"key\":\"load\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Load Scale (LOAD)\",\"description\":\"Load a scale from the SD card.\"},{\"x\":11,\"width\":4,\"key\":\"save\",\"type\":\"action\",\"format\":\"text\",\"label\":\"Save Scale (SAVE)\",\"description\":\"Save this scale to the SD card.\"}]}]},\"loadinstrument\":{\"kind\":\"browser\",\"browserFor\":\"instrument preset\"},\"selectsavedirectory\":{\"kind\":\"browser\",\"browserFor\":\"save location\"},\"createdirectory\":{\"kind\":\"text\",\"fieldLabel\":\"Directory Name\",\"description\":\"Name of the directory to create on the SD card.\"}}"),
3286
- instrumentTypes: /* @__PURE__ */ JSON.parse("{\"common\":{\"name\":\"Common\",\"description\":\"Parameters shared by most instrument types.\",\"params\":{\"FILTER\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Filter Type (FILTER)\",\"description\":\"Multi-mode filter type: low-pass, high-pass, band-pass, band-stop, LP>HP (low-pass with RES controlling a 1-pole high-pass), ZDF low-pass, ZDF high-pass.\"},\"CUTOFF\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Filter Cutoff (CUTOFF)\",\"description\":\"Filter cutoff frequency.\"},\"RES\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Filter Resonance (RES)\",\"description\":\"Filter resonance amount.\"},\"AMP\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Amplification (AMP)\",\"description\":\"Amplifies the signal; values above the maximum level are handled according to LIM.\"},\"LIM\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Limit Mode (LIM)\",\"description\":\"Limit / clipping behaviour of the amplified signal.\",\"options\":{\"CLIP\":\"Values above the maximum level are clipped.\",\"SIN\":\"The waveform is fed through a sine function.\",\"FOLD\":\"Values above the maximum level are folded back into themselves.\",\"WRAP\":\"Values above the maximum level are wrapped vertically.\",\"POST\":\"Amplification applied with hard-clipping after the filter stage.\",\"POST:AD\":\"Post filter amplification with soft-clipping.\",\"POST:W1\":\"Post filter folding distortion.\",\"POST:W2\":\"Post filter folding distortion.\",\"POST:W3\":\"Post filter folding distortion.\"}},\"PAN\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Pan (PAN)\",\"description\":\"Stereo position of the instrument (80 is centre).\"},\"DRY\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Dry Volume (DRY)\",\"description\":\"Dry volume sent to the mixer channel.\"},\"MFX\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"ModFX Send (MFX)\",\"description\":\"ModFX send effect volume.\"},\"DEL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Delay Send (DEL)\",\"description\":\"Delay send effect volume.\"},\"REV\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Reverb Send (REV)\",\"description\":\"Reverb send effect volume.\"}}},\"wavsynth\":{\"name\":\"Wavsynth\",\"description\":\"Wave table synthesizer and noise generator: an 8-bit sample buffer generated in real time from one of 9 basic shapes (repeated, skewed, mirrored, filtered, overdriven) or one of 61 built-in wave tables of 64 waveforms each.\",\"params\":{\"SHAPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Shape (SHAPE)\",\"description\":\"Base waveform: PULSE 12% to 75%, SAW, TRIANGLE, SINE, NOISE PITCHED, NOISE, or WT wave tables. For PWM set PULSE 50% and adjust MIRROR (smoothness set by SIZE).\"},\"SIZE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Size (SIZE)\",\"description\":\"Horizontal size of the waveform (number of samples).\"},\"MULT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Multiply (MULT)\",\"description\":\"Multiplies the waveform by adding repeats of the shape; can sound like hard sync.\"},\"WARP\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Warp (WARP)\",\"description\":\"Pushes the shape to one side of the wave table.\"},\"SCAN\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Scan / Mirror (SCAN)\",\"description\":\"Shapes 0-8: mirrors the waveform at the given position (0-200%). Wave tables: scans through the 64 waveforms.\"},\"FILTER\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Filter Type (FILTER)\",\"description\":\"Multi-mode filter type, plus 4 modes that apply the filter into the waveform: WAV LP, WAV HP, WAV BP, WAV BS.\"}}},\"macrosynth\":{\"name\":\"Macrosynth\",\"description\":\"44 engines ('shapes') from basic analog to 2-op FM, physical modelling and percussion; TIMBRE and COLOR change meaning per shape.\",\"params\":{\"SHAPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Shape (SHAPE)\",\"description\":\"One of the 44 Macrosynth engines (see the Macrosynth Models appendix).\"},\"TIMBRE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Timbre (TIMBRE)\",\"description\":\"First parameter of the selected shape.\"},\"COLOR\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Color (COLOR)\",\"description\":\"Second parameter of the selected shape.\"},\"DEGRADE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Degrade (DEGRADE)\",\"description\":\"Sample rate reduction effect.\"},\"REDUX\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Redux (REDUX)\",\"description\":\"Bit rate reduction effect.\"}}},\"sampler\":{\"name\":\"Sampler\",\"description\":\"Sample player, editor and recorder for 1 or 2 channel PCM .wav files.\",\"params\":{\"SAMPLE\":{\"type\":\"name\",\"format\":\"text\",\"label\":\"Sample (SAMPLE)\",\"description\":\"The loaded sample; use LOAD to browse for one, REC. to record or EDIT to open the Sample Editor.\"},\"SLICE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Slice (SLICE)\",\"description\":\"Slices the sample into equal sections, mapped from the lowest octave (C-1); 01 FILE uses the markers stored in the file.\"},\"PLAY\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Play Mode (PLAY)\",\"description\":\"Play direction and loop mode.\",\"options\":{\"FWD\":\"Forward, no loop.\",\"REV\":\"Reverse, no loop.\",\"FWDLOOP\":\"Looping forward.\",\"REVLOOP\":\"Looping reverse.\",\"FWD PP\":\"Ping-pong looping, forward first.\",\"REV PP\":\"Ping-pong looping, reverse first.\",\"OSC\":\"Oscillator mode: ignores the start position and doesn't restart on retrigger.\",\"OSC REV\":\"Oscillator mode, reverse.\",\"OSC PP\":\"Oscillator mode, ping-pong.\",\"REPITCH\":\"Pitches the sample from the song tempo (adjust with STEPS/BPM).\"}},\"START\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Start (START)\",\"description\":\"Sample start position (ignored in OSC play modes).\"},\"LOOP ST.\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Loop Start (LOOP ST.)\",\"description\":\"Where the sample loops back to when it reaches the end in a looping play mode.\"},\"LOOP ST\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Loop Start (LOOP ST)\",\"description\":\"Where the sample loops back to when it reaches the end in a looping play mode.\"},\"LEN\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Length (LEN)\",\"description\":\"Sample length.\"},\"LENGTH\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Length (LENGTH)\",\"description\":\"Sample length.\"},\"DETUNE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Detune (DETUNE)\",\"description\":\"Detune with 80 as the centre frequency: first digit steps semitones, second digit 1/16 semitone.\"},\"DEGRADE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Degrade (DEGRADE)\",\"description\":\"Sample rate reduction; any value above 00 disables sample interpolation.\"}}},\"fmsynth\":{\"name\":\"FM Synth\",\"description\":\"Four-operator, 12-algorithm FM synthesizer. Each operator has a base shape, ratio, level, feedback and two modulation slots.\",\"operatorDestinations\":{\"LEV\":\"controls the level from 00 to the currently set level.\",\"RAT\":\"adds 0.00 to +16.00 to the current ratio.\",\"PIT\":\"adds to the note pitch, in semitones, for this operator.\",\"FBK\":\"controls the feedback from 00 to the currently set feedback.\"},\"params\":{\"ALGO\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Algorithm (ALGO)\",\"description\":\"One of 12 algorithms (oscillator routing); the last one is additive mode.\"},\"A\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator A (A)\",\"description\":\"Operator letter with optional shape (SIN, SW2-SW6, SAW, SQU, PUL, IMP, TRI, NOISE, then wave tables).\"},\"B\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator B (B)\",\"description\":\"Operator letter with optional shape.\"},\"C\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator C (C)\",\"description\":\"Operator letter with optional shape.\"},\"D\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator D (D)\",\"description\":\"Operator letter with optional shape.\"},\"RATIO\":{\"type\":\"parameter\",\"format\":\"float\",\"label\":\"Ratio (RATIO)\",\"description\":\"Operator frequency ratio relative to the played note (2.00 is an octave up, 0.50 an octave down).\"},\"LEV/FB\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"level\",\"feedback\"],\"label\":\"Level / Feedback (LEV/FB)\",\"description\":\"Operator volume and feedback.\"},\"MOD\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Modulation Slot (MOD)\",\"description\":\"Assigns one of MOD1-MOD4 to control a destination on the operator.\"},\"LEV\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Level Mod (LEV)\",\"description\":\"Controls the level from 00 to the currently set level.\"},\"RAT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Ratio Mod (RAT)\",\"description\":\"Adds 0.00 to +16.00 to the current ratio.\"},\"PIT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Pitch Mod (PIT)\",\"description\":\"Adds to the note pitch, in semitones, for the operator.\"},\"FBK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Feedback Mod (FBK)\",\"description\":\"Controls feedback from 00 to the currently set feedback.\"},\"MOD#\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Modulation Source\",\"description\":\"Modulation source value freely assigned to operators through the MOD slots.\"}}},\"hypersynth\":{\"name\":\"Hypersynth\",\"description\":\"Six-note saw wave chord synthesizer with 16 chord banks of up to 6 intervals, detune (swarm), stereo width and a square sub-oscillator.\",\"params\":{\"SCALE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Scale (SCALE)\",\"description\":\"One of the song's scales, or 00 to use the song/track scale settings.\"},\"CHORD\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Chord (CHORD)\",\"description\":\"Chord bank and its intervals (copy with [SHIFT]+[OPTION], paste with [SHIFT]+[EDIT]).\"},\"SHIFT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Shift (SHIFT)\",\"description\":\"Cross-fades volume between the first 3 intervals and the second 3.\"},\"SHAPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Shape (SHAPE)\",\"description\":\"Waveform of the chord oscillators (SAW by default; not in the manual, seen on a Model:02, firmware 6.5).\"},\"SWARM\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Swarm (SWARM)\",\"description\":\"Detunes the oscillators relative to their interval.\"},\"WIDTH\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (WIDTH)\",\"description\":\"Stereo width of the oscillators, from mono 00 to full stereo FF.\"},\"SUBOSC\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Sub Oscillator (SUBOSC)\",\"description\":\"Mixes in a sub-oscillator 2 octaves below the root (below 80) or 1 octave below (above 80).\"}}},\"external\":{\"name\":\"External Instrument\",\"description\":\"Hybrid MIDI output and audio input device: notes and MIDI FX are sent to the chosen port and channel, audio returns through its filter and amp stage.\",\"params\":{\"INPUT\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Audio Input (INPUT)\",\"description\":\"Audio input: line-in (stereo, left, right), USB (stereo, left, right), or line-in and USB.\"},\"PORT\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"MIDI Port (PORT)\",\"description\":\"MIDI output port: MIDI and USB, MIDI only, USB only, or Internal (track MIDI input / global MIDI CCs from the MIDI Settings View).\"},\"CHANNEL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"MIDI Channel (CHANNEL)\",\"description\":\"MIDI channel, 1 to 16.\"},\"BANK:PG\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"bank\",\"program\"],\"label\":\"Bank / Program (BANK:PG)\",\"description\":\"Optional MIDI bank select and program change (program change can also be sent as an FX command).\"},\"CC#\":{\"type\":\"parameter\",\"format\":\"text\",\"label\":\"Custom CC (CC:VAL)\",\"description\":\"A custom MIDI CC number, then an optional default value (-- for none), sent with the note-on after program change messages.\"}}},\"midiout\":{\"name\":\"MIDI Out\",\"description\":\"Controls external devices over USB MIDI or the TRS (type A) MIDI output.\",\"params\":{\"PORT\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"MIDI Port (PORT)\",\"description\":\"MIDI output port: MIDI and USB, MIDI only, USB only, or Internal (track MIDI input / global MIDI CCs from the MIDI Settings View).\"},\"CHANNEL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"MIDI Channel (CHANNEL)\",\"description\":\"MIDI channel, 1 to 16.\"},\"BANK:PG\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"bank\",\"program\"],\"label\":\"Bank / Program (BANK:PG)\",\"description\":\"Optional MIDI bank select and program change (program change can also be sent as an FX command).\"},\"CC#\":{\"type\":\"parameter\",\"format\":\"text\",\"label\":\"Custom CC (CC:VAL)\",\"description\":\"One of up to 10 custom MIDI CC numbers per instrument, then an optional default value (-- for none), sent with the note-on after program change messages.\"}}}}"),
3655
+ instrumentTypes: /* @__PURE__ */ JSON.parse("{\"common\":{\"name\":\"Common\",\"description\":\"Parameters shared by most instrument types.\",\"params\":{\"FILTER\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Filter Type (FILTER)\",\"description\":\"Multi-mode filter type: low-pass, high-pass, band-pass, band-stop, LP>HP (low-pass with RES controlling a 1-pole high-pass), ZDF low-pass, ZDF high-pass.\"},\"CUTOFF\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Filter Cutoff (CUTOFF)\",\"description\":\"Filter cutoff frequency.\"},\"RES\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Filter Resonance (RES)\",\"description\":\"Filter resonance amount.\"},\"AMP\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Amplification (AMP)\",\"description\":\"Amplifies the signal; values above the maximum level are handled according to LIM.\"},\"LIM\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Limit Mode (LIM)\",\"description\":\"Limit / clipping behaviour of the amplified signal.\",\"options\":{\"CLIP\":\"Values above the maximum level are clipped.\",\"SIN\":\"The waveform is fed through a sine function.\",\"FOLD\":\"Values above the maximum level are folded back into themselves.\",\"WRAP\":\"Values above the maximum level are wrapped vertically.\",\"POST\":\"Amplification applied with hard-clipping after the filter stage.\",\"POST:AD\":\"Post filter amplification with soft-clipping.\",\"POST:W1\":\"Post filter folding distortion.\",\"POST:W2\":\"Post filter folding distortion.\",\"POST:W3\":\"Post filter folding distortion.\"}},\"PAN\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Pan (PAN)\",\"description\":\"Stereo position of the instrument (80 is centre).\"},\"DRY\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Dry Volume (DRY)\",\"description\":\"Dry volume sent to the mixer channel.\"},\"MFX\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"ModFX Send (MFX)\",\"description\":\"ModFX send effect volume.\"},\"DEL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Delay Send (DEL)\",\"description\":\"Delay send effect volume.\"},\"REV\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Reverb Send (REV)\",\"description\":\"Reverb send effect volume.\"}}},\"wavsynth\":{\"name\":\"Wavsynth\",\"description\":\"Wave table synthesizer and noise generator: an 8-bit sample buffer generated in real time from one of 9 basic shapes (repeated, skewed, mirrored, filtered, overdriven) or one of 61 built-in wave tables of 64 waveforms each.\",\"params\":{\"SHAPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Shape (SHAPE)\",\"description\":\"Base waveform: PULSE 12% to 75%, SAW, TRIANGLE, SINE, NOISE PITCHED, NOISE, or WT wave tables. For PWM set PULSE 50% and adjust MIRROR (smoothness set by SIZE).\"},\"SIZE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Size (SIZE)\",\"description\":\"Horizontal size of the waveform (number of samples).\"},\"MULT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Multiply (MULT)\",\"description\":\"Multiplies the waveform by adding repeats of the shape; can sound like hard sync.\"},\"WARP\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Warp (WARP)\",\"description\":\"Pushes the shape to one side of the wave table.\"},\"SCAN\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Scan / Mirror (SCAN)\",\"description\":\"Shapes 0-8: mirrors the waveform at the given position (0-200%). Wave tables: scans through the 64 waveforms.\"},\"FILTER\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Filter Type (FILTER)\",\"description\":\"Multi-mode filter type, plus 4 modes that apply the filter into the waveform: WAV LP, WAV HP, WAV BP, WAV BS.\"}}},\"macrosynth\":{\"name\":\"Macrosynth\",\"description\":\"44 engines ('shapes') from basic analog to 2-op FM, physical modelling and percussion; TIMBRE and COLOR change meaning per shape.\",\"params\":{\"SHAPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Shape (SHAPE)\",\"description\":\"One of the 44 Macrosynth engines (see the Macrosynth Models appendix).\"},\"TIMBRE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Timbre (TIMBRE)\",\"description\":\"First parameter of the selected shape.\"},\"COLOR\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Color (COLOR)\",\"description\":\"Second parameter of the selected shape.\"},\"DEGRADE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Degrade (DEGRADE)\",\"description\":\"Sample rate reduction effect.\"},\"REDUX\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Redux (REDUX)\",\"description\":\"Bit rate reduction effect.\"}}},\"sampler\":{\"name\":\"Sampler\",\"description\":\"Sample player, editor and recorder for 1 or 2 channel PCM .wav files.\",\"params\":{\"SAMPLE\":{\"type\":\"name\",\"format\":\"text\",\"label\":\"Sample (SAMPLE)\",\"description\":\"The loaded sample; use LOAD to browse for one, REC. to record or EDIT to open the Sample Editor.\"},\"SLICE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Slice (SLICE)\",\"description\":\"Slices the sample into equal sections, mapped from the lowest octave (C-1); 01 FILE uses the markers stored in the file.\"},\"PLAY\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Play Mode (PLAY)\",\"description\":\"Play direction and loop mode.\",\"options\":{\"FWD\":\"Forward, no loop.\",\"REV\":\"Reverse, no loop.\",\"FWDLOOP\":\"Looping forward.\",\"REVLOOP\":\"Looping reverse.\",\"FWD PP\":\"Ping-pong looping, forward first.\",\"REV PP\":\"Ping-pong looping, reverse first.\",\"OSC\":\"Oscillator mode: ignores the start position and doesn't restart on retrigger.\",\"OSC REV\":\"Oscillator mode, reverse.\",\"OSC PP\":\"Oscillator mode, ping-pong.\",\"REPITCH\":\"Pitches the sample from the song tempo (adjust with STEPS/BPM).\"}},\"START\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Start (START)\",\"description\":\"Sample start position (ignored in OSC play modes).\"},\"LOOP ST.\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Loop Start (LOOP ST.)\",\"description\":\"Where the sample loops back to when it reaches the end in a looping play mode.\"},\"LOOP ST\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Loop Start (LOOP ST)\",\"description\":\"Where the sample loops back to when it reaches the end in a looping play mode.\"},\"LEN\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Length (LEN)\",\"description\":\"Sample length.\"},\"LENGTH\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Length (LENGTH)\",\"description\":\"Sample length.\"},\"DETUNE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Detune (DETUNE)\",\"description\":\"Detune with 80 as the centre frequency: first digit steps semitones, second digit 1/16 semitone.\"},\"DEGRADE\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Degrade (DEGRADE)\",\"description\":\"Sample rate reduction; any value above 00 disables sample interpolation.\"}}},\"fmsynth\":{\"name\":\"FM Synth\",\"description\":\"Four-operator, 12-algorithm FM synthesizer. Each operator has a base shape, ratio, level, feedback and two modulation slots.\",\"operatorDestinations\":{\"LEV\":\"controls the level from 00 to the currently set level.\",\"RAT\":\"adds 0.00 to +16.00 to the current ratio.\",\"PIT\":\"adds to the note pitch, in semitones, for this operator.\",\"FBK\":\"controls the feedback from 00 to the currently set feedback.\"},\"params\":{\"ALGO\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Algorithm (ALGO)\",\"description\":\"One of 12 algorithms (oscillator routing); the last one is additive mode.\"},\"A\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator A (A)\",\"description\":\"Operator letter with optional shape (SIN, SW2-SW6, SAW, SQU, PUL, IMP, TRI, NOISE, then wave tables).\"},\"B\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator B (B)\",\"description\":\"Operator letter with optional shape (SIN, SW2-SW6, SAW, SQU, PUL, IMP, TRI, NOISE, then wave tables).\"},\"C\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator C (C)\",\"description\":\"Operator letter with optional shape (SIN, SW2-SW6, SAW, SQU, PUL, IMP, TRI, NOISE, then wave tables).\"},\"D\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Operator D (D)\",\"description\":\"Operator letter with optional shape (SIN, SW2-SW6, SAW, SQU, PUL, IMP, TRI, NOISE, then wave tables).\"},\"RATIO\":{\"type\":\"parameter\",\"format\":\"float\",\"label\":\"Ratio (RATIO)\",\"description\":\"Operator frequency ratio relative to the played note (2.00 is an octave up, 0.50 an octave down).\"},\"LEV/FB\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"level\",\"feedback\"],\"label\":\"Level / Feedback (LEV/FB)\",\"description\":\"Operator volume and feedback.\"},\"MOD\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Modulation Slot (MOD)\",\"description\":\"Assigns one of MOD1-MOD4 to control a destination on the operator.\"},\"LEV\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Level Mod (LEV)\",\"description\":\"Controls the level from 00 to the currently set level.\"},\"RAT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Ratio Mod (RAT)\",\"description\":\"Adds 0.00 to +16.00 to the current ratio.\"},\"PIT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Pitch Mod (PIT)\",\"description\":\"Adds to the note pitch, in semitones, for the operator.\"},\"FBK\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Feedback Mod (FBK)\",\"description\":\"Controls feedback from 00 to the currently set feedback.\"},\"MOD#\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Modulation Source\",\"description\":\"Modulation source value freely assigned to operators through the MOD slots.\"}}},\"hypersynth\":{\"name\":\"Hypersynth\",\"description\":\"Six-note saw wave chord synthesizer with 16 chord banks of up to 6 intervals, detune (swarm), stereo width and a square sub-oscillator.\",\"params\":{\"SCALE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Scale (SCALE)\",\"description\":\"One of the song's scales, or 00 to use the song/track scale settings.\"},\"CHORD\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Chord (CHORD)\",\"description\":\"Chord bank and its intervals (copy with [SHIFT]+[OPTION], paste with [SHIFT]+[EDIT]).\"},\"SHIFT\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Shift (SHIFT)\",\"description\":\"Cross-fades volume between the first 3 intervals and the second 3.\"},\"SHAPE\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Shape (SHAPE)\",\"description\":\"Waveform of the chord oscillators (SAW by default; not in the manual, seen on a Model:02, firmware 6.5).\"},\"SWARM\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Swarm (SWARM)\",\"description\":\"Detunes the oscillators relative to their interval.\"},\"WIDTH\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Stereo Width (WIDTH)\",\"description\":\"Stereo width of the oscillators, from mono 00 to full stereo FF.\"},\"SUBOSC\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"Sub Oscillator (SUBOSC)\",\"description\":\"Mixes in a sub-oscillator 2 octaves below the root (below 80) or 1 octave below (above 80).\"}}},\"external\":{\"name\":\"External Instrument\",\"description\":\"Hybrid MIDI output and audio input device: notes and MIDI FX are sent to the chosen port and channel, audio returns through its filter and amp stage.\",\"params\":{\"INPUT\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"Audio Input (INPUT)\",\"description\":\"Audio input: line-in (stereo, left, right), USB (stereo, left, right), or line-in and USB.\"},\"PORT\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"MIDI Port (PORT)\",\"description\":\"MIDI output port: MIDI and USB, MIDI only, USB only, or Internal (track MIDI input / global MIDI CCs from the MIDI Settings View).\"},\"CHANNEL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"MIDI Channel (CHANNEL)\",\"description\":\"MIDI channel, 1 to 16.\"},\"BANK:PG\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"bank\",\"program\"],\"label\":\"Bank / Program (BANK:PG)\",\"description\":\"Optional MIDI bank select and program change (program change can also be sent as an FX command).\"},\"CC#\":{\"type\":\"parameter\",\"format\":\"text\",\"label\":\"Custom CC (CC:VAL)\",\"description\":\"A custom MIDI CC number, then an optional default value (-- for none), sent with the note-on after program change messages.\"}}},\"midiout\":{\"name\":\"MIDI Out\",\"description\":\"Controls external devices over USB MIDI or the TRS (type A) MIDI output.\",\"params\":{\"PORT\":{\"type\":\"option\",\"format\":\"hexOption\",\"label\":\"MIDI Port (PORT)\",\"description\":\"MIDI output port: MIDI and USB, MIDI only, USB only, or Internal (track MIDI input / global MIDI CCs from the MIDI Settings View).\"},\"CHANNEL\":{\"type\":\"parameter\",\"format\":\"hex\",\"label\":\"MIDI Channel (CHANNEL)\",\"description\":\"MIDI channel, 1 to 16.\"},\"BANK:PG\":{\"type\":\"parameter\",\"format\":\"hexPair\",\"parts\":[\"bank\",\"program\"],\"label\":\"Bank / Program (BANK:PG)\",\"description\":\"Optional MIDI bank select and program change (program change can also be sent as an FX command).\"},\"CC#\":{\"type\":\"parameter\",\"format\":\"text\",\"label\":\"Custom CC (CC:VAL)\",\"description\":\"One of up to 10 custom MIDI CC numbers per instrument, then an optional default value (-- for none), sent with the note-on after program change messages.\"}}}}"),
3287
3656
  instrumentTypeNames: {
3288
3657
  WAVS: "wavsynth",
3289
3658
  MACRO: "macrosynth",
@@ -3293,23 +3662,23 @@ var bt = 20, xt = 700, St = 80, Ct = 4e3, wt = 120, Tt = (e, t) => {
3293
3662
  EXTERN: "external",
3294
3663
  MIDI: "midiout"
3295
3664
  }
3296
- }, It = Ft.views, Lt = Ft.instrumentTypes, Rt = Ft.instrumentTypeNames, zt = {};
3297
- for (let [e, t] of Object.entries(It)) {
3665
+ }, vn = _n.views, yn = _n.instrumentTypes, bn = _n.instrumentTypeNames, xn = {};
3666
+ for (let [e, t] of Object.entries(vn)) {
3298
3667
  let { extends: n, ...r } = t;
3299
- zt[e] = n ? {
3300
- ...It[n],
3668
+ xn[e] = n ? {
3669
+ ...vn[n],
3301
3670
  ...r
3302
3671
  } : r;
3303
3672
  }
3304
- function Bt(e) {
3305
- return zt[e] ?? null;
3673
+ function Sn(e) {
3674
+ return xn[e] ?? null;
3306
3675
  }
3307
3676
  var G = (e, t, n) => t >= e.length ? "" : e.slice(t, t + n);
3308
- function Vt(e, t) {
3677
+ function Cn(e, t) {
3309
3678
  let n = e.width ?? e.text?.length ?? 1, r = G(t, e.x, n);
3310
3679
  return e.text == null ? e.regex != null && new RegExp(e.regex, "i").test(r) : r.toUpperCase() === e.text.toUpperCase();
3311
3680
  }
3312
- function Ht(e, t) {
3681
+ function wn(e, t) {
3313
3682
  let n = e.sections;
3314
3683
  if (!n) return null;
3315
3684
  for (let e of t) {
@@ -3318,7 +3687,7 @@ function Ht(e, t) {
3318
3687
  }
3319
3688
  return null;
3320
3689
  }
3321
- function Ut(e, t, n) {
3690
+ function Tn(e, t, n) {
3322
3691
  let r = e.zones?.find((e) => e.id === t);
3323
3692
  if (!e.slotLabel || !r) return null;
3324
3693
  let i = RegExp(`^${e.slotLabel}$`, "i");
@@ -3328,46 +3697,46 @@ function Ut(e, t, n) {
3328
3697
  }
3329
3698
  return null;
3330
3699
  }
3331
- function Wt(e) {
3700
+ function En(e) {
3332
3701
  let t = e.toUpperCase().replace(/[^A-Z]/g, "");
3333
3702
  if (!t) return null;
3334
- let n = Object.keys(Rt).find((e) => t.startsWith(e));
3335
- return n ? Rt[n] : null;
3703
+ let n = Object.keys(bn).find((e) => t.startsWith(e));
3704
+ return n ? bn[n] : null;
3336
3705
  }
3337
- function Gt(e) {
3706
+ function Dn(e) {
3338
3707
  for (let t of e) if (t.toUpperCase().startsWith("TYPE")) {
3339
3708
  let e = G(t, 8, 13).trim();
3340
3709
  return e ? {
3341
3710
  text: e,
3342
- key: Wt(e)
3711
+ key: En(e)
3343
3712
  } : null;
3344
3713
  }
3345
3714
  return null;
3346
3715
  }
3347
- function Kt(e) {
3348
- let t = Lt[e];
3716
+ function On(e) {
3717
+ let t = yn[e];
3349
3718
  return t ? {
3350
3719
  name: t.name,
3351
3720
  description: t.description
3352
3721
  } : null;
3353
3722
  }
3354
- function qt(e, t, n) {
3723
+ function kn(e, t, n) {
3355
3724
  let r = t.replace(/^MOD\d$/, "MOD#").replace(/^CC[A-J]$/, "CC#");
3356
3725
  if (e.vocabulary) return e.vocabulary[r] ?? null;
3357
3726
  if (e.vocabularyFrom !== "instrumentTypes") return null;
3358
- let i = n ? [n, "common"] : ["common", ...Object.keys(Lt).filter((e) => e !== "common")];
3727
+ let i = n ? [n, "common"] : ["common", ...Object.keys(yn).filter((e) => e !== "common")];
3359
3728
  for (let e of i) {
3360
- let t = Lt[e]?.params[r];
3729
+ let t = yn[e]?.params[r];
3361
3730
  if (t) return t;
3362
3731
  }
3363
3732
  return null;
3364
3733
  }
3365
- var Jt = (e) => e.toLowerCase().replace(/[^a-z0-9]+(.)?/g, (e, t) => t ? t.toUpperCase() : "").replace(/^[^a-z]+/, ""), Yt = /^[A-Z][A-Z0-9.:#/ -]*$/, Xt = {
3734
+ var An = (e) => e.toLowerCase().replace(/[^a-z0-9]+(.)?/g, (e, t) => t ? t.toUpperCase() : "").replace(/^[^a-z]+/, ""), jn = /^[A-Z][A-Z0-9.:#/ -]*$/, Mn = {
3366
3735
  RATIO: "ratio",
3367
3736
  "LEV/FB": "levFb",
3368
3737
  MOD: "mod"
3369
- }, Zt = /^(?:-----|[1-4]>[A-Z]{3})$/i;
3370
- function Qt(e, t, n) {
3738
+ }, Nn = /^(?:-----|[1-4]>[A-Z]{3})$/i;
3739
+ function Pn(e, t, n) {
3371
3740
  let r = G(t, 0, e.x).trim().toUpperCase(), i = e.letters.map((t, n) => ({
3372
3741
  letter: t,
3373
3742
  x: e.x + n * e.step
@@ -3381,10 +3750,10 @@ function Qt(e, t, n) {
3381
3750
  paramLabel: r || e,
3382
3751
  operator: a
3383
3752
  }
3384
- }), o = Xt[r];
3753
+ }), o = Mn[r];
3385
3754
  if (o) {
3386
3755
  let t = n.params[r];
3387
- return t ? i.map(({ letter: i, x: s }) => a(o, s, e.width, r === "MOD" ? $t(t, 1, n) : t, i)) : null;
3756
+ return t ? i.map(({ letter: i, x: s }) => a(o, s, e.width, r === "MOD" ? Fn(t, 1, n) : t, i)) : null;
3388
3757
  }
3389
3758
  if (r) return null;
3390
3759
  if (/^\s+[A-D] \S/i.test(t)) {
@@ -3408,9 +3777,9 @@ function Qt(e, t, n) {
3408
3777
  return r.length ? r : null;
3409
3778
  }
3410
3779
  let s = i.map(({ x: n }) => G(t, n, e.width).trim()), c = n.params.MOD;
3411
- return c && s.every((e) => Zt.test(e)) && !G(t, 0, e.x).trim() ? i.map(({ letter: t, x: r }) => a("mod2", r, e.width, $t(c, 2, n), t)) : null;
3780
+ return c && s.every((e) => Nn.test(e)) && !G(t, 0, e.x).trim() ? i.map(({ letter: t, x: r }) => a("mod2", r, e.width, Fn(c, 2, n), t)) : null;
3412
3781
  }
3413
- function $t(e, t, n) {
3782
+ function Fn(e, t, n) {
3414
3783
  let r = {};
3415
3784
  for (let [e, t] of Object.entries(n.operatorDestinations ?? {})) for (let n = 1; n <= 4; n += 1) r[`${n}>${e}`] = `MOD${n} ${e}: ${t}`;
3416
3785
  return {
@@ -3421,8 +3790,8 @@ function $t(e, t, n) {
3421
3790
  options: r
3422
3791
  };
3423
3792
  }
3424
- var en = /^(CC[A-J])(?: CC:VAL)? +(?=\S)/i, tn = 6;
3425
- function nn(e, t, n) {
3793
+ var In = /^(CC[A-J])(?: CC:VAL)? +(?=\S)/i, Ln = 6;
3794
+ function Rn(e, t, n) {
3426
3795
  if (e.kind === "browser") {
3427
3796
  let r = n.entryText?.trim() || t.trim();
3428
3797
  return r ? {
@@ -3460,12 +3829,12 @@ function nn(e, t, n) {
3460
3829
  }]
3461
3830
  };
3462
3831
  }
3463
- for (let r of e.rows ?? []) if (!(r.section && n.section && r.section !== n.section) && r.match.every((e) => Vt(e, t))) return {
3832
+ for (let r of e.rows ?? []) if (!(r.section && n.section && r.section !== n.section) && r.match.every((e) => Cn(e, t))) return {
3464
3833
  rowId: r.id,
3465
3834
  cells: r.cells
3466
3835
  };
3467
3836
  if (e.operators && n.instrumentKey === e.operators.instrument) {
3468
- let n = Qt(e.operators, t, Lt[e.operators.instrument]);
3837
+ let n = Pn(e.operators, t, yn[e.operators.instrument]);
3469
3838
  if (n) return {
3470
3839
  rowId: "operators",
3471
3840
  cells: n
@@ -3473,11 +3842,11 @@ function nn(e, t, n) {
3473
3842
  }
3474
3843
  let r = e.zones ?? [], i = [];
3475
3844
  return r.forEach((a, o) => {
3476
- let s = G(t, a.labelX, a.labelWidth).trim().toUpperCase(), c = a.x, l = null, u = o === 0 ? en.exec(t) : null;
3477
- if (u && (s = u[1].toUpperCase(), c = u[0].length, l = tn), !Yt.test(s)) return;
3478
- let d = r[o + 1], f = d != null && G(t, d.labelX - 1, 1).trim() === "" && Yt.test(G(t, d.labelX, d.labelWidth).trim().toUpperCase()), p = l ?? (d && f ? Math.max(1, d.labelX - 1 - c) : a.width);
3845
+ let s = G(t, a.labelX, a.labelWidth).trim().toUpperCase(), c = a.x, l = null, u = o === 0 ? In.exec(t) : null;
3846
+ if (u && (s = u[1].toUpperCase(), c = u[0].length, l = Ln), !jn.test(s)) return;
3847
+ let d = r[o + 1], f = d != null && G(t, d.labelX - 1, 1).trim() === "" && jn.test(G(t, d.labelX, d.labelWidth).trim().toUpperCase()), p = l ?? (d && f ? Math.max(1, d.labelX - 1 - c) : a.width);
3479
3848
  if (!G(t, c, p).trim()) return;
3480
- let m = qt(e, s, n.instrumentKey), h = Jt(s) || a.id, g = i.some((e) => e.key === h) ? `${h}${a.id[0].toUpperCase()}${a.id.slice(1)}` : h;
3849
+ let m = kn(e, s, n.instrumentKey), h = An(s) || a.id, g = i.some((e) => e.key === h) ? `${h}${a.id[0].toUpperCase()}${a.id.slice(1)}` : h;
3481
3850
  i.push({
3482
3851
  x: c,
3483
3852
  width: p,
@@ -3505,13 +3874,13 @@ var K = {
3505
3874
  values: null,
3506
3875
  text: null
3507
3876
  };
3508
- function rn(e) {
3877
+ function zn(e) {
3509
3878
  return /^[0-9A-F]{2}$/i.test(e) ? "hex" : /^[0-9A-F]{2}:[0-9A-F]{2}$/i.test(e) ? "hexPair" : /^[+-]?\d+([.,]\d+)?$/.test(e) ? "float" : /^[0-9A-F]{2}\S/i.test(e) ? "hexOption" : "text";
3510
3879
  }
3511
- function an(e, t) {
3880
+ function Bn(e, t) {
3512
3881
  let n = e.trim();
3513
3882
  if (!n) return K;
3514
- switch (t === "auto" ? rn(n) : t) {
3883
+ switch (t === "auto" ? zn(n) : t) {
3515
3884
  case "hex": {
3516
3885
  let e = /^([0-9A-F]{2})/i.exec(n), t = e ? parseInt(e[1], 16) : null;
3517
3886
  return {
@@ -3593,7 +3962,7 @@ function an(e, t) {
3593
3962
  };
3594
3963
  }
3595
3964
  }
3596
- function on(e, t) {
3965
+ function Vn(e, t) {
3597
3966
  if (!e || !t) return null;
3598
3967
  let n = t.toUpperCase().trim();
3599
3968
  if (e[n]) return e[n];
@@ -3602,28 +3971,28 @@ function on(e, t) {
3602
3971
  }
3603
3972
  //#endregion
3604
3973
  //#region src/fxCommands.ts
3605
- var sn = W.fxCommands, cn = W.instrumentFx.types, ln = Object.entries(cn).filter(([, e]) => e.verified).map(([e]) => e);
3606
- function un(e) {
3607
- let t = ln.filter((t) => cn[t].commands.includes(e));
3608
- return t.length === 0 || t.length === ln.length ? null : t;
3974
+ var Hn = I.fxCommands, Un = I.instrumentFx.types, Wn = Object.entries(Un).filter(([, e]) => e.verified).map(([e]) => e);
3975
+ function Gn(e) {
3976
+ let t = Wn.filter((t) => Un[t].commands.includes(e));
3977
+ return t.length === 0 || t.length === Wn.length ? null : t;
3609
3978
  }
3610
- function dn(e) {
3979
+ function Kn(e) {
3611
3980
  let t = e.toUpperCase();
3612
3981
  for (let e of [
3613
3982
  "sequencer",
3614
3983
  "instrument",
3615
3984
  "mixer"
3616
3985
  ]) {
3617
- let n = sn[e][t];
3986
+ let n = Hn[e][t];
3618
3987
  if (n) return {
3619
3988
  description: n.description,
3620
3989
  category: e,
3621
3990
  relative: n.relative === !0,
3622
- instrumentTypes: e === "instrument" ? un(t) : null,
3991
+ instrumentTypes: e === "instrument" ? Gn(t) : null,
3623
3992
  slot: null
3624
3993
  };
3625
3994
  }
3626
- let n = /^([A-Z]{2})([1-4])$/.exec(t), r = n ? sn.modulation[n[1]] : void 0;
3995
+ let n = /^([A-Z]{2})([1-4])$/.exec(t), r = n ? Hn.modulation[n[1]] : void 0;
3627
3996
  if (n && r) return {
3628
3997
  description: r.description.replace("modulator N", `modulator ${n[2]}`),
3629
3998
  category: "modulation",
@@ -3633,24 +4002,24 @@ function dn(e) {
3633
4002
  };
3634
4003
  }
3635
4004
  function q(e) {
3636
- return dn(e)?.relative === !0;
4005
+ return Kn(e)?.relative === !0;
3637
4006
  }
3638
- function fn(e) {
3639
- let t = cn[e.toLowerCase()];
4007
+ function qn(e) {
4008
+ let t = Un[e.toLowerCase()];
3640
4009
  return t ? {
3641
4010
  type: e.toLowerCase(),
3642
4011
  verified: t.verified,
3643
4012
  commands: [...t.commands]
3644
4013
  } : null;
3645
4014
  }
3646
- var pn = W.instrumentFx.relativeCommands.description, J = (e) => e.toString(16).toUpperCase().padStart(2, "0"), Y = (e) => e.toString(16).toUpperCase(), mn = (e) => e === 1 ? "once" : e === 2 ? "twice" : `${e} times`, X = (e) => e > 127 ? e - 256 : e, hn = (e) => `${e > 0 ? "+" : e < 0 ? "−" : "±"}${Math.abs(e)}`;
3647
- function gn(e, t) {
4015
+ var Jn = I.instrumentFx.relativeCommands.description, J = (e) => e.toString(16).toUpperCase().padStart(2, "0"), Y = (e) => e.toString(16).toUpperCase(), Yn = (e) => e === 1 ? "once" : e === 2 ? "twice" : `${e} times`, X = (e) => e > 127 ? e - 256 : e, Xn = (e) => `${e > 0 ? "+" : e < 0 ? "−" : "±"}${Math.abs(e)}`;
4016
+ function Zn(e, t) {
3648
4017
  return q(e) && t > 127 ? `${J(t)} (${X(t)})` : J(t);
3649
4018
  }
3650
- function _n(e) {
4019
+ function Qn(e) {
3651
4020
  return e > 127 ? `−${J(256 - e)}` : `+${J(e)}`;
3652
4021
  }
3653
- var vn = [
4022
+ var $n = [
3654
4023
  "C-",
3655
4024
  "C#",
3656
4025
  "D-",
@@ -3664,16 +4033,16 @@ var vn = [
3664
4033
  "A#",
3665
4034
  "B-"
3666
4035
  ];
3667
- function yn(e) {
4036
+ function er(e) {
3668
4037
  let t = /^([A-G])([#-])(\d)$/.exec((e ?? "").trim().toUpperCase());
3669
4038
  if (!t) return null;
3670
- let n = vn.indexOf(`${t[1]}${t[2]}`);
4039
+ let n = $n.indexOf(`${t[1]}${t[2]}`);
3671
4040
  return n < 0 ? null : Number(t[3]) * 12 + n;
3672
4041
  }
3673
- function bn(e) {
3674
- return `${vn[(e % 12 + 12) % 12]}${Math.floor(e / 12)}`;
4042
+ function tr(e) {
4043
+ return `${$n[(e % 12 + 12) % 12]}${Math.floor(e / 12)}`;
3675
4044
  }
3676
- var xn = [
4045
+ var nr = [
3677
4046
  "C",
3678
4047
  "C#",
3679
4048
  "D",
@@ -3686,14 +4055,14 @@ var xn = [
3686
4055
  "A",
3687
4056
  "A#",
3688
4057
  "B"
3689
- ], Sn = 6;
3690
- function Cn(e) {
3691
- return e === Sn ? "one row per step" : Sn % e === 0 ? `${Sn / e} rows per step` : e % Sn === 0 ? `one row every ${e / Sn} steps` : "";
4058
+ ], rr = 6;
4059
+ function ir(e) {
4060
+ return e === rr ? "one row per step" : rr % e === 0 ? `${rr / e} rows per step` : e % rr === 0 ? `one row every ${e / rr} steps` : "";
3692
4061
  }
3693
- function wn(e, t) {
4062
+ function ar(e, t) {
3694
4063
  if (e === 0) return `${t} moves one row each time the instrument is triggered`;
3695
4064
  if (e <= 251) {
3696
- let n = Cn(e);
4065
+ let n = ir(e);
3697
4066
  return `${t} moves one row every ${e} tick${e === 1 ? "" : "s"}${n ? ` (${n})` : ""}`;
3698
4067
  }
3699
4068
  return [
@@ -3703,12 +4072,12 @@ function wn(e, t) {
3703
4072
  `${t} moves one row every 5 ms (200 Hz)`
3704
4073
  ][e - 252] ?? null;
3705
4074
  }
3706
- function Tn(e, t, n = {}) {
4075
+ function or(e, t, n = {}) {
3707
4076
  let r = e.toUpperCase(), i = t >> 4, a = t & 15, o = (e) => `${e} tick${e === 1 ? "" : "s"}`;
3708
4077
  switch (r) {
3709
4078
  case "ARP": {
3710
- let e = yn(n.note), t = `+${i} and +${a} semitones`;
3711
- return e == null ? `plays the note, then ${t}` : `${t}: ${bn(e)} ${bn(e + i)} ${bn(e + a)}`;
4079
+ let e = er(n.note), t = `+${i} and +${a} semitones`;
4080
+ return e == null ? `plays the note, then ${t}` : `${t}: ${tr(e)} ${tr(e + i)} ${tr(e + a)}`;
3712
4081
  }
3713
4082
  case "ARC": return `arp mode ${i}, speed ${o(a)}`;
3714
4083
  case "CHA": return n.inTable ? `everything left of it plays with probability ${t}/255 (00 never, FF always)` : `${i}/15 chance for what is left of it, ${a}/15 for what is right (0 never, F always)`;
@@ -3722,7 +4091,7 @@ function Tn(e, t, n = {}) {
3722
4091
  case "TBL": return `instrument uses table ${J(t)}`;
3723
4092
  case "TBX": return t === 0 ? "stops the auxiliary table" : `plays table ${J(t)} alongside the instrument table`;
3724
4093
  case "THO": return n.inTable ? `every column of the table jumps to row ${Y(a)}` : `the instrument's table jumps to row ${Y(a)} (the aux table when a TBX is on this step)`;
3725
- case "HOP": return n.inTable ? a === n.row ? i === 0 ? `this column stops on row ${Y(a)}` : `holds this column on row ${Y(a)} ${mn(i)}` : i === 0 ? `always jumps back to row ${Y(a)}: the rows in between loop` : `jumps to row ${Y(a)} ${mn(i)}, then the column runs on past it` : t === 255 ? "stops this track" : `jumps to row ${Y(a)} of the next phrase`;
4094
+ case "HOP": return n.inTable ? a === n.row ? i === 0 ? `this column stops on row ${Y(a)}` : `holds this column on row ${Y(a)} ${Yn(i)}` : i === 0 ? `always jumps back to row ${Y(a)}: the rows in between loop` : `jumps to row ${Y(a)} ${Yn(i)}, then the column runs on past it` : t === 255 ? "stops this track" : `jumps to row ${Y(a)} of the next phrase`;
3726
4095
  case "REP": return !n.inTable || t === 0 ? null : `plays the command above it ${t} more time${t === 1 ? "" : "s"}, one row each`;
3727
4096
  case "RTO": return n.inTable ? `repeats the relative command above it until it has added ${t > 127 ? `${J(t)} (${X(t)})` : J(t)} in total` : null;
3728
4097
  case "RMX": return `tracks to its left (selection ${Y(i)}) jump to phrase row ${Y(a)}`;
@@ -3734,47 +4103,47 @@ function Tn(e, t, n = {}) {
3734
4103
  case "PBN": return t < 128 ? `pitch slides up by ${t}` : `pitch slides down by ${256 - t}`;
3735
4104
  case "SCA":
3736
4105
  case "SCG": {
3737
- let e = xn[i];
4106
+ let e = nr[i];
3738
4107
  return `${r === "SCA" ? "track" : "song"} scale ${a.toString(16).toUpperCase()}${e ? `, key ${e}` : ""}`;
3739
4108
  }
3740
- case "SNG": return `moves the song position by ${hn(X(t))} row(s)`;
4109
+ case "SNG": return `moves the song position by ${Xn(X(t))} row(s)`;
3741
4110
  case "SED": return `random seed ${J(t)}`;
3742
4111
  case "NXT": return `plays instrument ${J(t)} on the next track`;
3743
4112
  case "MTT": {
3744
4113
  if (n.inTable) return "no effect in a table";
3745
4114
  let e = X(t);
3746
- return e < 0 && n.row === 0 ? "no effect: a row cannot be moved earlier on the first step" : e === 0 ? "no timing shift" : `shifts the row by ${hn(e)}/8 tick`;
4115
+ return e < 0 && n.row === 0 ? "no effect: a row cannot be moved earlier on the first step" : e === 0 ? "no timing shift" : `shifts the row by ${Xn(e)}/8 tick`;
3747
4116
  }
3748
4117
  case "TPO": return `${t} BPM`;
3749
- case "TSP": return `transposes the song by ${hn(X(t))} semitones`;
3750
- case "TIC": return wn(t, n.inTable ? "this column" : "the instrument's table");
4118
+ case "TSP": return `transposes the song by ${Xn(X(t))} semitones`;
4119
+ case "TIC": return ar(t, n.inTable ? "this column" : "the instrument's table");
3751
4120
  case "XRZ": return t > 0 ? "reverb freeze on" : "reverb freeze off";
3752
4121
  }
3753
4122
  if (q(r)) {
3754
4123
  let e = X(t);
3755
- return r === "PIT" ? e === 0 ? "no change" : `${hn(e)} semitones` : e === 0 ? "no change" : hn(e);
4124
+ return r === "PIT" ? e === 0 ? "no change" : `${Xn(e)} semitones` : e === 0 ? "no change" : Xn(e);
3756
4125
  }
3757
4126
  return null;
3758
4127
  }
3759
- function En(e, t, n) {
4128
+ function sr(e, t, n) {
3760
4129
  let r = e + t & 255;
3761
4130
  return n != null && (t < 128 ? r < e || r > n : r > e || r < n) && (r = n), {
3762
4131
  value: r,
3763
4132
  atLimit: n != null && r === n
3764
4133
  };
3765
4134
  }
3766
- function Dn(e, t, n, r = !1) {
4135
+ function cr(e, t, n, r = !1) {
3767
4136
  let i = null, a = null, o = null, s = null, c = r, l = !1, u = null, d = "none", f = Math.min(n, e.length - 1);
3768
4137
  for (let n = 0; n <= f; n += 1) {
3769
4138
  let f = e[n].fields[`fx${t}cmd`], p = e[n].fields[`fx${t}val`];
3770
4139
  if (d = "none", f && !f.isEmpty) {
3771
4140
  let e = f.rawValue.toUpperCase(), t = p && !p.isEmpty ? p.hexValue : null;
3772
- e === "REP" ? t ? !i && !r ? d = "orphan" : (d = c ? "repChange" : "rep", c || (u = n), c = !0, o = t, a != null && ({value: a, atLimit: l} = En(a, o, s))) : (d = "repStop", c = !1, o = null, l = !1) : e === "RTO" ? (d = "rto", s = t) : (d = c ? "replace" : "set", i = {
4141
+ e === "REP" ? t ? !i && !r ? d = "orphan" : (d = c ? "repChange" : "rep", c || (u = n), c = !0, o = t, a != null && ({value: a, atLimit: l} = sr(a, o, s))) : (d = "repStop", c = !1, o = null, l = !1) : e === "RTO" ? (d = "rto", s = t) : (d = c ? "replace" : "set", i = {
3773
4142
  cmd: e,
3774
4143
  value: t,
3775
4144
  step: n
3776
4145
  }, a = t, c = !1, o = null, s = null, l = !1, u = null);
3777
- } else c && (d = "continue", a != null && o != null && ({value: a, atLimit: l} = En(a, o, s)));
4146
+ } else c && (d = "continue", a != null && o != null && ({value: a, atLimit: l} = sr(a, o, s)));
3778
4147
  }
3779
4148
  return {
3780
4149
  lane: t,
@@ -3790,8 +4159,8 @@ function Dn(e, t, n, r = !1) {
3790
4159
  carried: c && !i
3791
4160
  };
3792
4161
  }
3793
- function On(e) {
3794
- let t = e.source, n = (e) => e == null ? "??" : t ? gn(t.cmd, e) : J(e), r = e.increment == null ? "" : _n(e.increment), i = e.atLimit && e.limit != null ? `, held at the RTO limit ${J(e.limit)}` : "";
4162
+ function lr(e) {
4163
+ let t = e.source, n = (e) => e == null ? "??" : t ? Zn(t.cmd, e) : J(e), r = e.increment == null ? "" : Qn(e.increment), i = e.atLimit && e.limit != null ? `, held at the RTO limit ${J(e.limit)}` : "";
3795
4164
  switch (e.event) {
3796
4165
  case "rep":
3797
4166
  case "repChange": return e.carried ? `REP ${r} per step continues the repeat from the previous phrase` : `${e.event === "rep" ? "repeats" : "now repeats"} ${t?.cmd} from ${n(t?.value ?? null)} (step ${t?.step.toString(16).toUpperCase()}), ${r} per step → ${n(e.value)} on this step${i}`;
@@ -3803,7 +4172,7 @@ function On(e) {
3803
4172
  default: return null;
3804
4173
  }
3805
4174
  }
3806
- function kn(e) {
4175
+ function ur(e) {
3807
4176
  let t = e.fields.inst;
3808
4177
  return t && !t.isEmpty ? "instrument" : [
3809
4178
  1,
@@ -3814,10 +4183,10 @@ function kn(e) {
3814
4183
  return n != null && !n.isEmpty && n.rawValue.toUpperCase() === "RET";
3815
4184
  }) ? "ret" : null;
3816
4185
  }
3817
- function An(e, t, n = null) {
4186
+ function dr(e, t, n = null) {
3818
4187
  let r = {}, i = null, a = null, o = Math.min(t, e.length - 1);
3819
4188
  for (let t = 0; t <= o; t += 1) {
3820
- let o = kn(e[t]);
4189
+ let o = ur(e[t]);
3821
4190
  if (o) {
3822
4191
  for (let e of Object.keys(r)) delete r[e];
3823
4192
  i = t, a = o;
@@ -3827,7 +4196,7 @@ function An(e, t, n = null) {
3827
4196
  2,
3828
4197
  3
3829
4198
  ]) {
3830
- let s = Dn(e, o, t, n === o), c = s.source;
4199
+ let s = cr(e, o, t, n === o), c = s.source;
3831
4200
  if (!c || s.carried || !q(c.cmd)) continue;
3832
4201
  let l = null, u = !1;
3833
4202
  if (s.event === "set" || s.event === "replace" ? l = c.value : (s.event === "rep" || s.event === "repChange" || s.event === "continue") && (l = s.value, u = !0), l == null) continue;
@@ -3844,21 +4213,21 @@ function An(e, t, n = null) {
3844
4213
  }
3845
4214
  return r;
3846
4215
  }
3847
- var jn = (e) => e === 0 ? "±00" : `${e > 0 ? "+" : "−"}${J(Math.abs(e))}`;
3848
- function Mn(e) {
4216
+ var fr = (e) => e === 0 ? "±00" : `${e > 0 ? "+" : "−"}${J(Math.abs(e))}`;
4217
+ function pr(e) {
3849
4218
  if (e.resetStep == null) return "the start of this phrase (earlier state unknown)";
3850
4219
  let t = e.resetStep.toString(16).toUpperCase();
3851
4220
  return e.resetBy === "ret" ? `the RET on step ${t}` : `the instrument number on step ${t}`;
3852
4221
  }
3853
- var Nn = "stays until a note with an instrument number (I) or a RET resets it";
3854
- function Pn(e) {
3855
- if (!e || e.count <= 1) return `relative: ${Nn}`;
3856
- let t = e.viaRep ? ", including REP repeats" : "", n = e.total === 0 ? "has had no net effect" : `has moved by ${jn(e.total)}`;
3857
- return `relative: ${e.cmd} ${n} over ${e.count} steps since ${Mn(e)}${t}; ${Nn}`;
4222
+ var mr = "stays until a note with an instrument number (I) or a RET resets it";
4223
+ function hr(e) {
4224
+ if (!e || e.count <= 1) return `relative: ${mr}`;
4225
+ let t = e.viaRep ? ", including REP repeats" : "", n = e.total === 0 ? "has had no net effect" : `has moved by ${fr(e.total)}`;
4226
+ return `relative: ${e.cmd} ${n} over ${e.count} steps since ${pr(e)}${t}; ${mr}`;
3858
4227
  }
3859
- function Fn(e) {
4228
+ function gr(e) {
3860
4229
  let t = Object.values(e).filter((e) => e.total !== 0);
3861
- return t.length ? `triggers the instrument again: resets the relative changes still in force (${t.map((e) => `${e.cmd} ${jn(e.total)}`).join(", ")})` : null;
4230
+ return t.length ? `triggers the instrument again: resets the relative changes still in force (${t.map((e) => `${e.cmd} ${fr(e.total)}`).join(", ")})` : null;
3862
4231
  }
3863
4232
  //#endregion
3864
4233
  //#region src/scaleInsight.ts
@@ -3875,7 +4244,7 @@ var Z = [
3875
4244
  "A",
3876
4245
  "A#",
3877
4246
  "B"
3878
- ], In = [
4247
+ ], _r = [
3879
4248
  {
3880
4249
  name: "Major (Ionian)",
3881
4250
  steps: [
@@ -4093,7 +4462,7 @@ var Z = [
4093
4462
  11
4094
4463
  ]
4095
4464
  }
4096
- ], Ln = [
4465
+ ], vr = [
4097
4466
  "the root",
4098
4467
  "a minor 2nd",
4099
4468
  "a major 2nd",
@@ -4107,26 +4476,26 @@ var Z = [
4107
4476
  "a minor 7th",
4108
4477
  "a major 7th"
4109
4478
  ];
4110
- function Rn(e) {
4479
+ function yr(e) {
4111
4480
  let t = e.replace(/[-\s]/g, "").toUpperCase();
4112
4481
  return Z.includes(t) ? t : null;
4113
4482
  }
4114
- function zn(e) {
4483
+ function br(e) {
4115
4484
  let t = Z.indexOf(e);
4116
- return t <= 0 ? t === 0 ? "the first row (the scale root)" : e : `${Ln[t]} (+${t} semitones above C)`;
4485
+ return t <= 0 ? t === 0 ? "the first row (the scale root)" : e : `${vr[t]} (+${t} semitones above C)`;
4117
4486
  }
4118
- function Bn(e) {
4487
+ function xr(e) {
4119
4488
  let t = new Set(e);
4120
4489
  if (t.size === 0) return [];
4121
4490
  let n = [];
4122
- for (let e of In) if (e.steps.length === t.size) for (let r = 0; r < 12; r += 1) e.steps.every((e) => t.has((e + r) % 12)) && n.push({
4491
+ for (let e of _r) if (e.steps.length === t.size) for (let r = 0; r < 12; r += 1) e.steps.every((e) => t.has((e + r) % 12)) && n.push({
4123
4492
  root: r,
4124
4493
  name: e.name
4125
4494
  });
4126
4495
  return n.sort((e, t) => e.root - t.root), n.map((e) => e.root === 0 ? e.name : `${e.name} from ${Z[e.root]}`);
4127
4496
  }
4128
- function Vn(e) {
4129
- let t = e.filter((e) => e.enabled === !0), n = t.map((e) => e.interval), r = Bn(t.map((e) => e.semitone)), i = e.filter((e) => e.enabled === !0 && e.offset != null && e.offset !== 0).map((e) => ({
4497
+ function Sr(e) {
4498
+ let t = e.filter((e) => e.enabled === !0), n = t.map((e) => e.interval), r = xr(t.map((e) => e.semitone)), i = e.filter((e) => e.enabled === !0 && e.offset != null && e.offset !== 0).map((e) => ({
4130
4499
  interval: e.interval,
4131
4500
  offset: e.offset
4132
4501
  }));
@@ -4135,47 +4504,47 @@ function Vn(e) {
4135
4504
  enabled: n,
4136
4505
  shapes: r,
4137
4506
  detuned: i,
4138
- summary: Hn({
4507
+ summary: Cr({
4139
4508
  enabled: n,
4140
4509
  shapes: r,
4141
4510
  detuned: i
4142
4511
  }).join(" — ")
4143
4512
  };
4144
4513
  }
4145
- function Hn(e) {
4514
+ function Cr(e) {
4146
4515
  let { enabled: t, shapes: n, detuned: r } = e;
4147
4516
  if (!t.length) return ["no notes enabled"];
4148
4517
  let i = n[0] ?? `${t.length} notes, custom scale`;
4149
- return r.length && (i += `; detuned: ${r.map((e) => `${e.interval} ${Un(e.offset)}`).join(", ")}`), [t.join(" "), i];
4518
+ return r.length && (i += `; detuned: ${r.map((e) => `${e.interval} ${wr(e.offset)}`).join(", ")}`), [t.join(" "), i];
4150
4519
  }
4151
- function Un(e) {
4520
+ function wr(e) {
4152
4521
  return `${e > 0 ? "+" : e < 0 ? "−" : ""}${Math.abs(e).toFixed(2)} st`;
4153
4522
  }
4154
4523
  //#endregion
4155
4524
  //#region src/viewContext.ts
4156
- var Wn = /* @__PURE__ */ new Set([
4525
+ var Tr = /* @__PURE__ */ new Set([
4157
4526
  "--",
4158
4527
  "---",
4159
4528
  "---00",
4160
4529
  "----",
4161
4530
  "-"
4162
4531
  ]);
4163
- function Gn(e) {
4164
- return !e || Wn.has(e) || /^-+$/.test(e);
4532
+ function Er(e) {
4533
+ return !e || Tr.has(e) || /^-+$/.test(e);
4165
4534
  }
4166
- function Kn(e) {
4535
+ function Dr(e) {
4167
4536
  let t = e.trim();
4168
- if (!t || Gn(t)) return null;
4537
+ if (!t || Er(t)) return null;
4169
4538
  let n = parseInt(t, 16);
4170
4539
  return Number.isNaN(n) ? null : n;
4171
4540
  }
4172
- function qn(e, t, n) {
4541
+ function Or(e, t, n) {
4173
4542
  return t >= e.length ? "" : e.slice(t, Math.min(t + n, e.length));
4174
4543
  }
4175
- function Jn(e) {
4176
- return Yn(e);
4544
+ function kr(e) {
4545
+ return Ar(e);
4177
4546
  }
4178
- function Yn(e) {
4547
+ function Ar(e) {
4179
4548
  if (!e) return {
4180
4549
  viewId: null,
4181
4550
  isUnsaved: !1,
@@ -4188,7 +4557,7 @@ function Yn(e) {
4188
4557
  marker: r
4189
4558
  };
4190
4559
  }
4191
- function Xn(e) {
4560
+ function jr(e) {
4192
4561
  return e.length > 0 && /^[^a-zA-Z0-9 ]/.test(e[0]) ? {
4193
4562
  line: e.slice(1),
4194
4563
  offset: 1
@@ -4197,12 +4566,12 @@ function Xn(e) {
4197
4566
  offset: 0
4198
4567
  };
4199
4568
  }
4200
- function Zn(e, t) {
4201
- let n = qn(t, e.x, e.width).trim();
4569
+ function Mr(e, t) {
4570
+ let n = Or(t, e.x, e.width).trim();
4202
4571
  n.includes("^") && (e.type === "fxCommand" || e.type === "fxValue") && (n = "");
4203
- let r = Gn(n);
4572
+ let r = Er(n);
4204
4573
  if (e.format) {
4205
- let t = r ? null : an(n, e.format), i = e.type === "name" || e.type === "fileEntry", a = t?.text != null && !i ? t.text.toUpperCase() : t?.text ?? null;
4574
+ let t = r ? null : Bn(n, e.format), i = e.type === "name" || e.type === "fileEntry", a = t?.text != null && !i ? t.text.toUpperCase() : t?.text ?? null;
4206
4575
  return {
4207
4576
  key: e.key,
4208
4577
  label: e.label,
@@ -4217,8 +4586,8 @@ function Zn(e, t) {
4217
4586
  values: t?.values ?? null,
4218
4587
  parts: e.parts,
4219
4588
  unit: e.unit,
4220
- valueDescription: !r && on(e.options, a ?? n) || void 0,
4221
- fileKind: e.type === "fileEntry" ? Qn(n) : void 0
4589
+ valueDescription: !r && Vn(e.options, a ?? n) || void 0,
4590
+ fileKind: e.type === "fileEntry" ? Nr(n) : void 0
4222
4591
  };
4223
4592
  }
4224
4593
  let i = [
@@ -4238,7 +4607,7 @@ function Zn(e, t) {
4238
4607
  if (a.includes(e.type)) {
4239
4608
  let e = parseInt(n, 10);
4240
4609
  o = Number.isNaN(e) ? null : e;
4241
- } else o = Kn(n);
4610
+ } else o = Dr(n);
4242
4611
  }
4243
4612
  return {
4244
4613
  key: e.key,
@@ -4250,20 +4619,20 @@ function Zn(e, t) {
4250
4619
  meta: e.meta
4251
4620
  };
4252
4621
  }
4253
- function Qn(e) {
4622
+ function Nr(e) {
4254
4623
  if (e === "/..") return "parent";
4255
4624
  if (e.startsWith("/")) return "directory";
4256
4625
  let t = /\.([a-z0-9]+)$/i.exec(e)?.[1]?.toLowerCase();
4257
4626
  return t === "m8i" ? "instrumentPreset" : t === "wav" ? "sample" : t?.startsWith("m8") ? "m8file" : "file";
4258
4627
  }
4259
- function $n(e, t) {
4260
- let n = qn(e, t.x, t.width).trim();
4628
+ function Pr(e, t) {
4629
+ let n = Or(e, t.x, t.width).trim();
4261
4630
  if (!n) return null;
4262
- if (t.parse === "hex") return Kn(n);
4631
+ if (t.parse === "hex") return Dr(n);
4263
4632
  let r = parseInt(n, 10);
4264
4633
  return Number.isNaN(r) ? null : r;
4265
4634
  }
4266
- function er(e, t) {
4635
+ function Fr(e, t) {
4267
4636
  for (let n of e) if (t >= n.x && t < n.x + n.width) return n;
4268
4637
  let n = null, r = 2;
4269
4638
  for (let i of e) {
@@ -4272,9 +4641,9 @@ function er(e, t) {
4272
4641
  }
4273
4642
  return n;
4274
4643
  }
4275
- function tr(e, t) {
4644
+ function Ir(e, t) {
4276
4645
  let n = {};
4277
- for (let r of e.columns ?? []) n[r.key] = Zn(r, t);
4646
+ for (let r of e.columns ?? []) n[r.key] = Mr(r, t);
4278
4647
  for (let e of Object.keys(n)) {
4279
4648
  let t = /^fx(\d)cmd$/.exec(e)?.[1], r = t ? n[`fx${t}val`] : void 0;
4280
4649
  r && n[e].isEmpty && (n[`fx${t}val`] = {
@@ -4283,27 +4652,27 @@ function tr(e, t) {
4283
4652
  });
4284
4653
  }
4285
4654
  return {
4286
- rowIndex: e.rowField ? $n(t, e.rowField) : null,
4655
+ rowIndex: e.rowField ? Pr(t, e.rowField) : null,
4287
4656
  rowKey: e.rowField?.key ?? "row",
4288
4657
  rowId: null,
4289
4658
  fields: n
4290
4659
  };
4291
4660
  }
4292
- var nr = (e) => e.replace(/\s+/g, ""), rr = 1, Q = (e) => e.slice(1);
4293
- function ir(e, t) {
4661
+ var Lr = (e) => e.replace(/\s+/g, ""), Rr = 1, Q = (e) => e.slice(1);
4662
+ function zr(e, t) {
4294
4663
  if (!t || !e.cursorPos || !e.currentLine) return !1;
4295
4664
  let n = t.lines[e.cursorPos.y];
4296
- return n != null && nr(n) === nr(e.currentLine);
4665
+ return n != null && Lr(n) === Lr(e.currentLine);
4297
4666
  }
4298
- var ar = 16, or = 4, sr = 21;
4299
- function cr(e, t) {
4300
- let n = W.views[e];
4667
+ var Br = 16, Vr = 4, Hr = 21;
4668
+ function Ur(e, t) {
4669
+ let n = I.views[e];
4301
4670
  if (n?.type !== "grid" || !n.rowField) return [];
4302
- let r = [], i = Math.min(sr, t.lines.length - 1);
4303
- for (let e = or; e <= i; e += 1) {
4671
+ let r = [], i = Math.min(Hr, t.lines.length - 1);
4672
+ for (let e = Vr; e <= i; e += 1) {
4304
4673
  let i = Q(t.lines[e] ?? ""), a = n.rowField;
4305
- if (i.length <= a.x + a.width || !lr(i[a.x + a.width])) continue;
4306
- let o = tr(n, i);
4674
+ if (i.length <= a.x + a.width || !Wr(i[a.x + a.width])) continue;
4675
+ let o = Ir(n, i);
4307
4676
  o.rowIndex != null && r.push({
4308
4677
  ...o,
4309
4678
  screenRow: e
@@ -4311,11 +4680,11 @@ function cr(e, t) {
4311
4680
  }
4312
4681
  return r;
4313
4682
  }
4314
- var lr = (e) => e === " " || /^[^a-zA-Z0-9-]$/.test(e);
4315
- function ur(e, t) {
4316
- let n = W.views[e];
4683
+ var Wr = (e) => e === " " || /^[^a-zA-Z0-9-]$/.test(e);
4684
+ function Gr(e, t) {
4685
+ let n = I.views[e];
4317
4686
  if (n?.type !== "grid") return null;
4318
- let r = cr(e, t), i = (e) => !!e && e !== " " && lr(e);
4687
+ let r = Ur(e, t), i = (e) => !!e && e !== " " && Wr(e);
4319
4688
  if (e === "song") return {
4320
4689
  tracks: (n.columns ?? []).filter((e) => e.type === "chainRef").map((e) => r.find((n) => i(Q(t.lines[n.screenRow] ?? "")[e.x - 1]))?.rowIndex ?? null),
4321
4690
  row: null
@@ -4329,25 +4698,25 @@ function ur(e, t) {
4329
4698
  row: null
4330
4699
  };
4331
4700
  }
4332
- function dr(e, t, n, r) {
4701
+ function Kr(e, t, n, r) {
4333
4702
  let i = (t.cursorPos?.y ?? -1) - r;
4334
4703
  if (i < 0) return null;
4335
4704
  let a = [], o = null;
4336
- for (let t = 0; t < ar; t += 1) {
4705
+ for (let t = 0; t < Br; t += 1) {
4337
4706
  let r = n.lines[i + t];
4338
4707
  if (r == null) return null;
4339
- let s = Q(r), c = tr(e, s);
4708
+ let s = Q(r), c = Ir(e, s);
4340
4709
  if (c.rowIndex !== t) return null;
4341
4710
  a.push(c);
4342
4711
  let l = s.indexOf("^^");
4343
- o == null && l >= 0 && (o = fr(e, l));
4712
+ o == null && l >= 0 && (o = qr(e, l));
4344
4713
  }
4345
4714
  return {
4346
4715
  steps: a,
4347
4716
  carriedRepLane: o
4348
4717
  };
4349
4718
  }
4350
- function fr(e, t) {
4719
+ function qr(e, t) {
4351
4720
  let n = [];
4352
4721
  for (let r = 1; r <= 3; r += 1) {
4353
4722
  let i = e.columns?.find((e) => e.key === `fx${r}cmd`), a = e.columns?.find((e) => e.key === `fx${r}val`);
@@ -4361,8 +4730,8 @@ function fr(e, t) {
4361
4730
  let r = Math.min(...n.map((e) => e.dist)), i = n.filter((e) => e.dist === r);
4362
4731
  return r <= 1 && i.length === 1 ? i[0].lane : null;
4363
4732
  }
4364
- function pr(e) {
4365
- let t = Rn(e.fields.interval?.rawValue ?? "");
4733
+ function Jr(e) {
4734
+ let t = yr(e.fields.interval?.rawValue ?? "");
4366
4735
  if (!t) return null;
4367
4736
  let n = e.fields.en?.rawValue.toUpperCase(), r = parseFloat(e.fields.offset?.rawValue ?? "");
4368
4737
  return {
@@ -4372,15 +4741,15 @@ function pr(e) {
4372
4741
  offset: Number.isNaN(r) ? null : r
4373
4742
  };
4374
4743
  }
4375
- function mr(e, t) {
4744
+ function Yr(e, t) {
4376
4745
  let n = /* @__PURE__ */ new Map();
4377
4746
  for (let r of t.lines) {
4378
- let t = tr(e, Q(r)), i = pr(t), a = t.fields.en?.rawValue.toUpperCase();
4747
+ let t = Ir(e, Q(r)), i = Jr(t), a = t.fields.en?.rawValue.toUpperCase();
4379
4748
  !i || a !== "ON" && a !== "OFF" && a !== "--" || n.has(i.interval) || n.set(i.interval, i);
4380
4749
  }
4381
4750
  return n.size === Z.length ? Z.map((e) => n.get(e)) : null;
4382
4751
  }
4383
- function hr(e, t, n, r) {
4752
+ function Xr(e, t, n, r) {
4384
4753
  let i = {
4385
4754
  ...r,
4386
4755
  isGridView: !1,
@@ -4397,40 +4766,40 @@ function hr(e, t, n, r) {
4397
4766
  resetsRelative: null,
4398
4767
  scaleNote: null,
4399
4768
  scale: null
4400
- }, a = e.cursorPos, o = a ? a.x - 1 : 0, s = a && ir(e, t) ? t : null, c = s && a ? Q(s.lines[a.y] ?? "") : e.currentLine ?? "";
4769
+ }, a = e.cursorPos, o = a ? a.x - 1 : 0, s = a && zr(e, t) ? t : null, c = s && a ? Q(s.lines[a.y] ?? "") : e.currentLine ?? "";
4401
4770
  if (!c.trim()) return i;
4402
- let l = s && a ? s.lines.slice(0, a.y + 1).reverse().map(Q) : [c], u = r.viewName === "inst" ? Gt(s ? s.lines.map(Q) : [c]) : null, d = {
4771
+ let l = s && a ? s.lines.slice(0, a.y + 1).reverse().map(Q) : [c], u = r.viewName === "inst" ? Dn(s ? s.lines.map(Q) : [c]) : null, d = {
4403
4772
  instrumentKey: u?.key ?? null,
4404
4773
  entryText: e.textUnderCursor
4405
4774
  };
4406
4775
  if (!s && a && n.kind == null) for (let e = 0; e <= Math.min(o, 24); e += 1) {
4407
4776
  let t = " ".repeat(e) + c;
4408
- if (nn(n, t, {
4777
+ if (Rn(n, t, {
4409
4778
  ...d,
4410
- section: Ht(n, [t])
4779
+ section: wn(n, [t])
4411
4780
  }).cells.some((e) => o >= e.x && o < e.x + e.width)) {
4412
4781
  c = t;
4413
4782
  break;
4414
4783
  }
4415
4784
  }
4416
- let f = Ht(n, s ? l : [c]), { rowId: p, cells: m } = nn(n, c, {
4785
+ let f = wn(n, s ? l : [c]), { rowId: p, cells: m } = Rn(n, c, {
4417
4786
  ...d,
4418
4787
  section: f
4419
4788
  }), h = {};
4420
- for (let e of m) h[e.key] = Zn(e, c);
4789
+ for (let e of m) h[e.key] = Mr(e, c);
4421
4790
  let g = {
4422
4791
  rowIndex: null,
4423
4792
  rowKey: "param",
4424
4793
  rowId: p,
4425
4794
  fields: h
4426
- }, _ = null, v = a ? er(m, o) : null;
4795
+ }, _ = null, v = a ? Fr(m, o) : null;
4427
4796
  v && (_ = {
4428
4797
  ...h[v.key],
4429
4798
  viewName: r.viewName,
4430
4799
  rowIndex: null
4431
4800
  });
4432
4801
  let y = v?.meta?.zone;
4433
- return s && typeof y == "string" && (f = Ut(n, y, l) ?? f), {
4802
+ return s && typeof y == "string" && (f = Tn(n, y, l) ?? f), {
4434
4803
  ...i,
4435
4804
  section: f,
4436
4805
  instrumentType: u,
@@ -4439,10 +4808,10 @@ function hr(e, t, n, r) {
4439
4808
  lineText: c.trim()
4440
4809
  };
4441
4810
  }
4442
- function gr(e, t) {
4811
+ function Zr(e, t) {
4443
4812
  let n = e.viewName;
4444
4813
  if (!n) return null;
4445
- let r = W.views[n] ?? null, i = r?.type === "grid", a = r?.title ?? null, o = r?.description ?? null, { viewId: s, isUnsaved: c } = Yn(e.viewTitle), l = {
4814
+ let r = I.views[n] ?? null, i = r?.type === "grid", a = r?.title ?? null, o = r?.description ?? null, { viewId: s, isUnsaved: c } = Ar(e.viewTitle), l = {
4446
4815
  viewName: n,
4447
4816
  viewTitle: a,
4448
4817
  viewDescription: o,
@@ -4461,8 +4830,8 @@ function gr(e, t) {
4461
4830
  resetsRelative: null,
4462
4831
  scaleNote: null,
4463
4832
  scale: null
4464
- }, d = r?.type === "parameter" ? Bt(n) : null;
4465
- if (d) return hr(e, t, d, l);
4833
+ }, d = r?.type === "parameter" ? Sn(n) : null;
4834
+ if (d) return Xr(e, t, d, l);
4466
4835
  if (!i || !r) return {
4467
4836
  ...u,
4468
4837
  isGridView: !1,
@@ -4476,11 +4845,11 @@ function gr(e, t) {
4476
4845
  row: null,
4477
4846
  activeField: null
4478
4847
  };
4479
- let { line: p } = Xn(f), m = e.cursorPos ? e.cursorPos.x - 1 : 0, h = tr(r, p), g = n === "scale" && !Rn(h.fields.interval?.rawValue ?? "") ? Bt(n) : null;
4480
- if (g) return hr(e, t, g, l);
4848
+ let { line: p } = jr(f), m = e.cursorPos ? e.cursorPos.x - 1 : 0, h = Ir(r, p), g = n === "scale" && !yr(h.fields.interval?.rawValue ?? "") ? Sn(n) : null;
4849
+ if (g) return Xr(e, t, g, l);
4481
4850
  let _ = null;
4482
4851
  if (e.cursorPos) {
4483
- let e = er(r.columns ?? [], m);
4852
+ let e = Fr(r.columns ?? [], m);
4484
4853
  if (e) {
4485
4854
  let t = h.fields[e.key];
4486
4855
  t && (_ = {
@@ -4491,20 +4860,20 @@ function gr(e, t) {
4491
4860
  }
4492
4861
  }
4493
4862
  let v = null, y = null, b = null, x = null, S = null, C = null;
4494
- if (ir(e, t)) {
4863
+ if (zr(e, t)) {
4495
4864
  if (n === "phrase" && h.rowIndex != null) {
4496
- let n = dr(r, e, t, h.rowIndex);
4865
+ let n = Kr(r, e, t, h.rowIndex);
4497
4866
  if (n) {
4498
4867
  v = n.steps, y = n.carriedRepLane;
4499
4868
  let e = _ ? /^fx(\d)(cmd|val)$/.exec(_.key)?.[1] : void 0;
4500
- e && (b = Dn(v, Number(e), h.rowIndex, y === Number(e))), x = An(v, h.rowIndex, y), h.rowIndex > 0 && kn(h) && (S = An(v, h.rowIndex - 1, y));
4869
+ e && (b = cr(v, Number(e), h.rowIndex, y === Number(e))), x = dr(v, h.rowIndex, y), h.rowIndex > 0 && ur(h) && (S = dr(v, h.rowIndex - 1, y));
4501
4870
  }
4502
4871
  } else if (n === "scale") {
4503
- let e = mr(r, t);
4504
- e && (C = Vn(e));
4872
+ let e = Yr(r, t);
4873
+ e && (C = Sr(e));
4505
4874
  }
4506
4875
  }
4507
- let w = n === "scale" ? pr(h) : null;
4876
+ let w = n === "scale" ? Jr(h) : null;
4508
4877
  return {
4509
4878
  ...u,
4510
4879
  isGridView: !0,
@@ -4520,25 +4889,25 @@ function gr(e, t) {
4520
4889
  scale: C
4521
4890
  };
4522
4891
  }
4523
- function _r(e) {
4524
- if (e.isParameterView) return Er(e);
4892
+ function Qr(e) {
4893
+ if (e.isParameterView) return si(e);
4525
4894
  if (e.viewName === "scale") {
4526
- let t = Dr(e);
4895
+ let t = ci(e);
4527
4896
  if (t) return t;
4528
4897
  }
4529
4898
  if (!e.activeField) return $(["location", e.viewTitle ?? e.viewName]);
4530
- let { activeField: t, row: n } = e, r = $(["field", `${t.label}: ${kr(t)}`]), i = (t.type === "fxCommand" || t.type === "fxValue") && n ? yr(e, t, n) : [...Sr(e, t), ...Cr(e, t)], a = i.length > 0;
4899
+ let { activeField: t, row: n } = e, r = $(["field", `${t.label}: ${ui(t)}`]), i = (t.type === "fxCommand" || t.type === "fxValue") && n ? ei(e, t, n) : [...ri(e, t), ...ii(e, t)], a = i.length > 0;
4531
4900
  if (t.type === "fxCommand" && !t.isEmpty) {
4532
- let e = a && /^(REP|RTO)$/i.test(t.rawValue) ? void 0 : dn(t.rawValue);
4901
+ let e = a && /^(REP|RTO)$/i.test(t.rawValue) ? void 0 : Kn(t.rawValue);
4533
4902
  e && r.push({
4534
4903
  kind: "fxInfo",
4535
- text: `(${br(e)})`
4904
+ text: `(${ti(e)})`
4536
4905
  });
4537
4906
  }
4538
4907
  if (t.type === "fxValue" && n) {
4539
4908
  let e = t.key.replace("val", "cmd"), i = n.fields[e];
4540
4909
  if (i && !i.isEmpty) {
4541
- let e = a && /^(REP|RTO)$/i.test(i.rawValue) ? void 0 : dn(i.rawValue), t = e ? `${i.rawValue} (${br(e)})` : i.rawValue;
4910
+ let e = a && /^(REP|RTO)$/i.test(i.rawValue) ? void 0 : Kn(i.rawValue), t = e ? `${i.rawValue} (${ti(e)})` : i.rawValue;
4542
4911
  r.push({
4543
4912
  kind: "fxTarget",
4544
4913
  text: `for ${t}`
@@ -4557,8 +4926,8 @@ function _r(e) {
4557
4926
  }
4558
4927
  return r;
4559
4928
  }
4560
- function vr(e) {
4561
- return _r(e).map((e) => e.text).join(" — ");
4929
+ function $r(e) {
4930
+ return Qr(e).map((e) => e.text).join(" — ");
4562
4931
  }
4563
4932
  function $(...e) {
4564
4933
  return e.flatMap(([e, t]) => t ? [{
@@ -4566,33 +4935,33 @@ function $(...e) {
4566
4935
  text: t
4567
4936
  }] : []);
4568
4937
  }
4569
- function yr(e, t, n) {
4938
+ function ei(e, t, n) {
4570
4939
  let r = /^fx(\d)/.exec(t.key)?.[1];
4571
4940
  if (!r) return [];
4572
4941
  let i = n.fields[`fx${r}cmd`], a = n.fields[`fx${r}val`];
4573
4942
  if (!i || i.isEmpty) {
4574
4943
  if (e.fxLane?.event !== "continue") return [];
4575
- let t = e.fxLane.source?.cmd, n = t && q(t) ? Pn(e.relativeFx?.[t]) : null;
4576
- return $(["fxLane", On(e.fxLane)], ["relative", n]);
4944
+ let t = e.fxLane.source?.cmd, n = t && q(t) ? hr(e.relativeFx?.[t]) : null;
4945
+ return $(["fxLane", lr(e.fxLane)], ["relative", n]);
4577
4946
  }
4578
- let o = i.rawValue.toUpperCase(), s = e.fxLane && e.fxLane.event !== "none" && e.fxLane.event !== "set" ? On(e.fxLane) : null;
4947
+ let o = i.rawValue.toUpperCase(), s = e.fxLane && e.fxLane.event !== "none" && e.fxLane.event !== "set" ? lr(e.fxLane) : null;
4579
4948
  if ((o === "REP" || o === "RTO") && e.viewName !== "table") {
4580
- let t = e.fxLane?.source?.cmd, n = o === "REP" && e.fxLane?.active && t && q(t) ? Pn(e.relativeFx?.[t]) : null;
4949
+ let t = e.fxLane?.source?.cmd, n = o === "REP" && e.fxLane?.active && t && q(t) ? hr(e.relativeFx?.[t]) : null;
4581
4950
  return $(["fxLane", s], ["relative", n]);
4582
4951
  }
4583
4952
  if (!a || a.isEmpty || a.hexValue == null) return $(["fxLane", s]);
4584
- let c = t.type === "fxCommand" ? `value ${gn(o, a.hexValue)}: ` : "", l = Tn(o, a.hexValue, {
4953
+ let c = t.type === "fxCommand" ? `value ${Zn(o, a.hexValue)}: ` : "", l = or(o, a.hexValue, {
4585
4954
  note: n.fields.note?.isEmpty ? null : n.fields.note?.rawValue,
4586
4955
  inTable: e.viewName === "table",
4587
4956
  row: n.rowIndex
4588
4957
  }), u = null;
4589
- return q(o) ? u = Pn(e.relativeFx?.[o]) : o === "RET" && e.resetsRelative && (u = Fn(e.resetsRelative)), $(["meaning", l ? `${c}${l}` : null], ["fxLane", s], ["relative", u]);
4958
+ return q(o) ? u = hr(e.relativeFx?.[o]) : o === "RET" && e.resetsRelative && (u = gr(e.resetsRelative)), $(["meaning", l ? `${c}${l}` : null], ["fxLane", s], ["relative", u]);
4590
4959
  }
4591
- function br(e) {
4960
+ function ti(e) {
4592
4961
  let t = e.description.replace(/\.$/, "");
4593
- return e.instrumentTypes ? `${t}; offered by ${e.instrumentTypes.map((e) => xr[e] ?? e).join(", ")} instruments` : t;
4962
+ return e.instrumentTypes ? `${t}; offered by ${e.instrumentTypes.map((e) => ni[e] ?? e).join(", ")} instruments` : t;
4594
4963
  }
4595
- var xr = {
4964
+ var ni = {
4596
4965
  sampler: "Sampler",
4597
4966
  macrosynth: "Macrosynth",
4598
4967
  fmsynth: "FM Synth",
@@ -4601,17 +4970,17 @@ var xr = {
4601
4970
  external: "External",
4602
4971
  midiout: "MIDI Out"
4603
4972
  };
4604
- function Sr(e, t) {
4605
- return t.type !== "instrumentRef" || t.isEmpty || !e.resetsRelative ? [] : $(["relative", Fn(e.resetsRelative)]);
4973
+ function ri(e, t) {
4974
+ return t.type !== "instrumentRef" || t.isEmpty || !e.resetsRelative ? [] : $(["relative", gr(e.resetsRelative)]);
4606
4975
  }
4607
- function Cr(e, t) {
4976
+ function ii(e, t) {
4608
4977
  return e.viewName !== "table" || t.key !== "volume" || t.isEmpty || t.hexValue == null || t.hexValue > 127 ? [] : $(["meaning", `plays at ${Math.round(t.hexValue / 127 * 100)}% of the step's velocity`]);
4609
4978
  }
4610
- var wr = {
4979
+ var ai = {
4611
4980
  MODFX: "ModFX",
4612
4981
  DELAY: "Delay",
4613
4982
  REVERB: "Reverb"
4614
- }, Tr = {
4983
+ }, oi = {
4615
4984
  parent: "goes up to the parent directory",
4616
4985
  directory: "directory: [EDIT] opens it",
4617
4986
  instrumentPreset: "instrument preset: [EDIT] selects it",
@@ -4619,30 +4988,30 @@ var wr = {
4619
4988
  m8file: "M8 file: [EDIT] selects it",
4620
4989
  file: "file: [EDIT] selects it"
4621
4990
  };
4622
- function Er(e) {
4991
+ function si(e) {
4623
4992
  let t = `${e.viewTitle ?? e.viewName}${e.viewId ? ` ${e.viewId}` : ""}`, n = e.activeField;
4624
4993
  if (!n) return $(["location", t], ["line", e.lineText]);
4625
- let r = e.instrumentType?.key ? Kt(e.instrumentType.key) : null;
4626
- return $(["field", `${n.label}: ${kr(n)}`], ["optionInfo", n.valueDescription], ["fieldInfo", n.description], ["fileInfo", n.fileKind && Tr[n.fileKind]], ["instrument", e.instrumentType && `${r?.name ?? e.instrumentType.text} instrument`], ["section", e.section && (wr[e.section] ?? e.section)], ["location", t]);
4994
+ let r = e.instrumentType?.key ? On(e.instrumentType.key) : null;
4995
+ return $(["field", `${n.label}: ${ui(n)}`], ["optionInfo", n.valueDescription], ["fieldInfo", n.description], ["fileInfo", n.fileKind && oi[n.fileKind]], ["instrument", e.instrumentType && `${r?.name ?? e.instrumentType.text} instrument`], ["section", e.section && (ai[e.section] ?? e.section)], ["location", t]);
4627
4996
  }
4628
- function Dr(e) {
4997
+ function ci(e) {
4629
4998
  let t = `${e.viewTitle ?? e.viewName}${e.viewId ? ` ${e.viewId}` : ""}`, n = e.activeField, r = e.scaleNote;
4630
4999
  if (!n || !r) return e.lineText ? $(["location", t], ["line", e.lineText]) : null;
4631
5000
  let { interval: i, enabled: a, offset: o } = r, s;
4632
5001
  if (n.key === "offset") {
4633
- let e = o == null ? `detune of ${i}` : o === 0 ? `${i} is not detuned` : `${i} is detuned by ${Un(o)}`;
5002
+ let e = o == null ? `detune of ${i}` : o === 0 ? `${i} is not detuned` : `${i} is detuned by ${wr(o)}`;
4634
5003
  s = $(["field", `${n.label}: ${n.rawValue || "(empty)"}`], ["meaning", e], ["meaning", a === !1 ? "(note is OFF)" : null]);
4635
5004
  } else {
4636
5005
  let e = a === !0 ? "is in" : a === !1 ? "is not in" : "is not yet set in";
4637
- s = $(["field", `${n.label}: ${n.key === "en" ? n.rawValue.toUpperCase() || "(unset)" : i}`], ["meaning", i], ["meaning", zn(i)], ["meaning", `${e} the scale`]);
5006
+ s = $(["field", `${n.label}: ${n.key === "en" ? n.rawValue.toUpperCase() || "(unset)" : i}`], ["meaning", i], ["meaning", br(i)], ["meaning", `${e} the scale`]);
4638
5007
  }
4639
5008
  if (e.scale) {
4640
- let [t, n] = Hn(e.scale);
5009
+ let [t, n] = Cr(e.scale);
4641
5010
  s.push(...$(["scale", `Scale: ${t}`], ["scale", n]));
4642
5011
  }
4643
5012
  return [...s, ...$(["location", `${t} row ${i}`])];
4644
5013
  }
4645
- function Or(e) {
5014
+ function li(e) {
4646
5015
  let t = e.unit ?? "";
4647
5016
  if (e.values && e.parts) {
4648
5017
  let n = e.hexValue != null, r = e.values.map((e) => n ? e.toString(16).toUpperCase().padStart(2, "0") : String(e)), i = e.parts.map((e, n) => `${e} ${r[n] ?? "?"}${t}`).join(", ");
@@ -4650,7 +5019,7 @@ function Or(e) {
4650
5019
  }
4651
5020
  return e.hexValue == null ? e.numericValue == null ? e.text ?? e.rawValue : `${e.rawValue}${t}` : `${e.rawValue.slice(0, 2)}h (dec ${e.hexValue})${e.text ? ` ${e.text}` : ""}`;
4652
5021
  }
4653
- function kr(e) {
5022
+ function ui(e) {
4654
5023
  if (e.isEmpty) return "(empty)";
4655
5024
  let t = e.type === "name" || e.type === "fileEntry" ? e : {
4656
5025
  ...e,
@@ -4677,26 +5046,26 @@ function kr(e) {
4677
5046
  case "fileEntry": return t.text ?? t.rawValue;
4678
5047
  case "name": return `"${t.rawValue}"`;
4679
5048
  case "option": return t.hexValue != null && t.text ? `${t.rawValue.slice(0, 2)} ${t.text}` : t.text ?? t.rawValue;
4680
- case "parameter": return Or(t);
4681
- case "noteInterval": return `Interval ${Rn(t.rawValue) ?? t.rawValue}`;
5049
+ case "parameter": return li(t);
5050
+ case "noteInterval": return `Interval ${yr(t.rawValue) ?? t.rawValue}`;
4682
5051
  case "semitoneOffset": return t.isEmpty ? "(empty)" : `${t.rawValue} st`;
4683
5052
  default: return t.rawValue;
4684
5053
  }
4685
5054
  }
4686
5055
  //#endregion
4687
5056
  //#region src/client.ts
4688
- var Ar = "yam8d-external-app", jr = 3e3, Mr = (e) => {
5057
+ var di = "yam8d-external-app", fi = 3e3, pi = (e) => {
4689
5058
  let t = e.remoteHandle();
4690
5059
  return {
4691
5060
  call: (e, ...n) => t.call(e, ...n),
4692
5061
  on: (e, n) => (t.addEventListener(e, n), () => t.removeEventListener(e, n)),
4693
5062
  close: () => e.close()
4694
5063
  };
4695
- }, Nr = (e) => ({
5064
+ }, mi = (e) => ({
4696
5065
  call: (t, ...n) => e.methods[t](...n),
4697
5066
  on: (t, n) => e.on(t, n),
4698
5067
  close: () => e.dispose()
4699
- }), Pr = (e) => {
5068
+ }), hi = (e) => {
4700
5069
  let t = [];
4701
5070
  return {
4702
5071
  messenger: {
@@ -4710,21 +5079,21 @@ var Ar = "yam8d-external-app", jr = 3e3, Mr = (e) => {
4710
5079
  for (let e of t.splice(0)) e();
4711
5080
  }
4712
5081
  };
4713
- }, Fr = () => {
5082
+ }, gi = () => {
4714
5083
  if (typeof window > "u") return !1;
4715
5084
  try {
4716
5085
  return window.parent !== window;
4717
5086
  } catch {
4718
5087
  return !0;
4719
5088
  }
4720
- }, Ir = (e) => e instanceof DOMException && (e.name === "NotFoundError" || e.name === "AbortError"), Lr = () => {
5089
+ }, _i = (e) => e instanceof DOMException && (e.name === "NotFoundError" || e.name === "AbortError"), vi = () => {
4721
5090
  let e = typeof navigator > "u" ? null : navigator;
4722
5091
  return {
4723
5092
  serial: !!e && "serial" in e,
4724
5093
  usb: !!e && "usb" in e,
4725
5094
  midi: !!e && "requestMIDIAccess" in e
4726
5095
  };
4727
- }, Rr = () => ({
5096
+ }, yi = () => ({
4728
5097
  viewName: null,
4729
5098
  viewTitle: null,
4730
5099
  minimapKey: null,
@@ -4741,9 +5110,9 @@ var Ar = "yam8d-external-app", jr = 3e3, Mr = (e) => {
4741
5110
  fontMode: null,
4742
5111
  systemInfo: null,
4743
5112
  macroRunning: !1
4744
- }), zr = class {
5113
+ }), bi = class {
4745
5114
  constructor(e = {}) {
4746
- this.backend = null, this.backendUnsubscribers = [], this.device = null, this._mode = null, this.audio = null, this.connecting = null, this.stateCallbacks = /* @__PURE__ */ new Set(), this.viewCallbacks = /* @__PURE__ */ new Set(), this.cursorCallbacks = /* @__PURE__ */ new Set(), this.textCallbacks = /* @__PURE__ */ new Set(), this.keyCallbacks = /* @__PURE__ */ new Set(), this.transportCallbacks = /* @__PURE__ */ new Set(), this.connectionCallbacks = /* @__PURE__ */ new Set(), this._state = Rr(), this._screen = null, this.screenRefreshing = !1, this.screenDirty = !1, this.config = e;
5115
+ this.backend = null, this.backendUnsubscribers = [], this.device = null, this._mode = null, this.audio = null, this.connecting = null, this.stateCallbacks = /* @__PURE__ */ new Set(), this.viewCallbacks = /* @__PURE__ */ new Set(), this.cursorCallbacks = /* @__PURE__ */ new Set(), this.textCallbacks = /* @__PURE__ */ new Set(), this.keyCallbacks = /* @__PURE__ */ new Set(), this.transportCallbacks = /* @__PURE__ */ new Set(), this.connectionCallbacks = /* @__PURE__ */ new Set(), this._state = yi(), this._screen = null, this.screenRefreshing = !1, this.screenDirty = !1, this.config = e;
4747
5116
  }
4748
5117
  get state() {
4749
5118
  return this._state;
@@ -4765,11 +5134,11 @@ var Ar = "yam8d-external-app", jr = 3e3, Mr = (e) => {
4765
5134
  }
4766
5135
  async resolveMode() {
4767
5136
  let e = this.config.mode ?? "auto";
4768
- if (e === "standalone" || e === "auto" && !Fr()) {
5137
+ if (e === "standalone" || e === "auto" && !gi()) {
4769
5138
  await this.startStandalone();
4770
5139
  return;
4771
5140
  }
4772
- let t = typeof window < "u" && window.name === "yam8d-external-app", n = e === "host" || t ? null : this.config.handshakeTimeoutMs ?? jr;
5141
+ let t = typeof window < "u" && window.name === "yam8d-external-app", n = e === "host" || t ? null : this.config.handshakeTimeoutMs ?? fi;
4773
5142
  await this.handshake(n) || (this.config.debug && console.log("[M8 SDK] No yam8d host answered, switching to standalone mode"), await this.startStandalone());
4774
5143
  }
4775
5144
  async handshake(r) {
@@ -4781,10 +5150,10 @@ var Ar = "yam8d-external-app", jr = 3e3, Mr = (e) => {
4781
5150
  this.config.debug && (i = t(i, (e, ...t) => {
4782
5151
  console.log("[M8 SDK]", e, ...t);
4783
5152
  }));
4784
- let a = Pr(i), o = e(a.messenger, { ping: async () => "pong" }), s, c = new Promise((e) => {
5153
+ let a = hi(i), o = e(a.messenger, { ping: async () => "pong" }), s, c = new Promise((e) => {
4785
5154
  r !== null && (s = setTimeout(() => e(null), r));
4786
5155
  }), l = await Promise.race([o, c]);
4787
- return clearTimeout(s), l ? (this._mode = "host", await this.attach(Mr(l)), l.localHandle().emit("ready", void 0), !0) : (a.dispose(), !1);
5156
+ return clearTimeout(s), l ? (this._mode = "host", await this.attach(pi(l)), l.localHandle().emit("ready", void 0), !0) : (a.dispose(), !1);
4788
5157
  }
4789
5158
  async startStandalone() {
4790
5159
  if (this._mode = "standalone", this.config.autoConnect !== !1) try {
@@ -4794,7 +5163,7 @@ var Ar = "yam8d-external-app", jr = 3e3, Mr = (e) => {
4794
5163
  }
4795
5164
  }
4796
5165
  async openDevice(e, t) {
4797
- let n = A();
5166
+ let n = ee();
4798
5167
  if (!n.browserSupport) throw Error("This browser has no Web Serial, WebUSB or Web MIDI: the M8 cannot be reached from this page");
4799
5168
  let r = e === "auto" && !t ? [
4800
5169
  "serial",
@@ -4810,14 +5179,14 @@ var Ar = "yam8d-external-app", jr = 3e3, Mr = (e) => {
4810
5179
  });
4811
5180
  break;
4812
5181
  } catch (r) {
4813
- if (a = r, t && !Ir(r) && (i = await ee(() => n.connect({
5182
+ if (a = r, t && !_i(r) && (i = await te(() => n.connect({
4814
5183
  transport: e,
4815
5184
  prompt: !1
4816
5185
  }), { maxAttempts: 2 }), i)) break;
4817
5186
  }
4818
5187
  if (!i) throw a;
4819
- let o = Pt(i, { debug: this.config.debug });
4820
- this.device = i, await this.attach(Nr(o)), i.commands.resetScreen();
5188
+ let o = gn(i, { debug: this.config.debug });
5189
+ this.device = i, await this.attach(mi(o)), i.commands.resetScreen();
4821
5190
  let s = i;
4822
5191
  i.closed.then(() => {
4823
5192
  this.device === s && (this.config.debug && console.log("[M8 SDK] Device connection ended"), this.device = null, this.detach());
@@ -4898,7 +5267,7 @@ var Ar = "yam8d-external-app", jr = 3e3, Mr = (e) => {
4898
5267
  await this.disconnectAudio(), await this.closeDevice();
4899
5268
  }
4900
5269
  async connectAudio() {
4901
- await this.connect(), this._mode !== "host" && (this.audio ??= F(), await this.audio.connect({ requireM8: !0 }));
5270
+ await this.connect(), this._mode !== "host" && (this.audio ??= P(), await this.audio.connect({ requireM8: !0 }));
4902
5271
  }
4903
5272
  async disconnectAudio() {
4904
5273
  await this.audio?.disconnect();
@@ -4967,7 +5336,7 @@ var Ar = "yam8d-external-app", jr = 3e3, Mr = (e) => {
4967
5336
  }, e;
4968
5337
  }
4969
5338
  async captureSnapshot() {
4970
- let [e, t] = await Promise.all([this.fetchState(), this.getScreen()]), n = gr(e, t);
5339
+ let [e, t] = await Promise.all([this.fetchState(), this.getScreen()]), n = Zr(e, t);
4971
5340
  return {
4972
5341
  capturedAt: Date.now(),
4973
5342
  state: {
@@ -4980,8 +5349,8 @@ var Ar = "yam8d-external-app", jr = 3e3, Mr = (e) => {
4980
5349
  deviceModel: e.deviceModel
4981
5350
  },
4982
5351
  semantic: n,
4983
- description: n ? vr(n) : null,
4984
- descriptionParts: n ? _r(n) : null,
5352
+ description: n ? $r(n) : null,
5353
+ descriptionParts: n ? Qr(n) : null,
4985
5354
  screen: t
4986
5355
  };
4987
5356
  }
@@ -5010,23 +5379,23 @@ var Ar = "yam8d-external-app", jr = 3e3, Mr = (e) => {
5010
5379
  this.audio?.disconnect(), this.closeDevice(), this.detach(), this.stateCallbacks.clear(), this.viewCallbacks.clear(), this.cursorCallbacks.clear(), this.textCallbacks.clear(), this.keyCallbacks.clear(), this.transportCallbacks.clear(), this.connectionCallbacks.clear();
5011
5380
  }
5012
5381
  getSemanticContext() {
5013
- return gr(this._state, this._screen);
5382
+ return Zr(this._state, this._screen);
5014
5383
  }
5015
5384
  describeContext() {
5016
- let e = gr(this._state, this._screen);
5017
- return e ? vr(e) : null;
5385
+ let e = Zr(this._state, this._screen);
5386
+ return e ? $r(e) : null;
5018
5387
  }
5019
5388
  describeContextParts() {
5020
- let e = gr(this._state, this._screen);
5021
- return e ? _r(e) : null;
5389
+ let e = Zr(this._state, this._screen);
5390
+ return e ? Qr(e) : null;
5022
5391
  }
5023
5392
  };
5024
- async function Br(e = {}) {
5025
- let t = new zr(e);
5393
+ async function xi(e = {}) {
5394
+ let t = new bi(e);
5026
5395
  return await t.connect(), t;
5027
5396
  }
5028
- function Vr(e = {}) {
5029
- let t = new zr(e);
5397
+ function Si(e = {}) {
5398
+ let t = new bi(e);
5030
5399
  return {
5031
5400
  client: t,
5032
5401
  connect: async () => {
@@ -5034,10 +5403,10 @@ function Vr(e = {}) {
5034
5403
  }
5035
5404
  };
5036
5405
  }
5037
- var Hr = {
5038
- createM8Client: Br,
5039
- createM8ClientSync: Vr
5040
- }, Ur = [
5406
+ var Ci = {
5407
+ createM8Client: xi,
5408
+ createM8ClientSync: Si
5409
+ }, wi = [
5041
5410
  {
5042
5411
  role: "background",
5043
5412
  label: "BACKGROUND"
@@ -5090,7 +5459,7 @@ var Hr = {
5090
5459
  role: "meterPeak",
5091
5460
  label: "METER PEAK"
5092
5461
  }
5093
- ], Wr = "themesettings", Gr = (e) => e.replace(/\s+/g, "").toUpperCase(), Kr = (e) => Math.max(0, Math.min(255, Math.round(e))).toString(16).padStart(2, "0"), qr = (e, t, n) => `#${Kr(e)}${Kr(t)}${Kr(n)}`, Jr = (e, t, n) => {
5462
+ ], Ti = "themesettings", Ei = (e) => e.replace(/\s+/g, "").toUpperCase(), Di = (e) => Math.max(0, Math.min(255, Math.round(e))).toString(16).padStart(2, "0"), Oi = (e, t, n) => `#${Di(e)}${Di(t)}${Di(n)}`, ki = (e, t, n) => {
5094
5463
  let r = e / 256 * 6, i = t / 255, a = n / 255, o = Math.floor(r) % 6, s = r - Math.floor(r), c = a * (1 - i), l = a * (1 - s * i), u = a * (1 - (1 - s) * i), [d, f, p] = [
5095
5464
  [
5096
5465
  a,
@@ -5123,14 +5492,14 @@ var Hr = {
5123
5492
  l
5124
5493
  ]
5125
5494
  ][o];
5126
- return qr(d * 255, f * 255, p * 255);
5127
- }, Yr = new Map(Ur.map(({ role: e, label: t }) => [Gr(t), e]));
5128
- function Xr(e) {
5495
+ return Oi(d * 255, f * 255, p * 255);
5496
+ }, Ai = new Map(wi.map(({ role: e, label: t }) => [Ei(t), e]));
5497
+ function ji(e) {
5129
5498
  let t = "rgb", n = null, r = {};
5130
5499
  for (let i of e.lines) {
5131
5500
  let e = i.replace(/[{}]/g, "0"), a = /^\s*MODE\s+([A-Z])\s+([A-Z])\s+([A-Z])\b/i.exec(e);
5132
5501
  if (a) {
5133
- t = Gr(a.slice(1).join("")) === "HSV" ? "hsv" : "rgb";
5502
+ t = Ei(a.slice(1).join("")) === "HSV" ? "hsv" : "rgb";
5134
5503
  continue;
5135
5504
  }
5136
5505
  let o = /^\s*THEME\s+NAME\s+(\S.*?)\s*$/i.exec(e);
@@ -5141,23 +5510,23 @@ function Xr(e) {
5141
5510
  }
5142
5511
  let s = /^\s*([A-Z][A-Z:/. ]*?)\s+([0-9A-F]{2})\s+([0-9A-F]{2})\s+([0-9A-F]{2})(?:\s|$)/i.exec(e);
5143
5512
  if (!s) continue;
5144
- let c = Yr.get(Gr(s[1]));
5513
+ let c = Ai.get(Ei(s[1]));
5145
5514
  if (!c || r[c]) continue;
5146
5515
  let [l, u, d] = s.slice(2, 5).map((e) => parseInt(e, 16));
5147
- r[c] = t === "hsv" ? Jr(l, u, d) : qr(l, u, d);
5516
+ r[c] = t === "hsv" ? ki(l, u, d) : Oi(l, u, d);
5148
5517
  }
5149
- return Ur.some(({ role: e }) => !r[e]) ? null : {
5518
+ return wi.some(({ role: e }) => !r[e]) ? null : {
5150
5519
  colors: r,
5151
5520
  mode: t,
5152
5521
  name: n
5153
5522
  };
5154
5523
  }
5155
- var Zr = (e) => e ? qr(e.r, e.g, e.b) : void 0;
5156
- function Qr(e) {
5157
- let t = {}, n = Zr(e.backgroundColor), r = Zr(e.titleColor);
5524
+ var Mi = (e) => e ? Oi(e.r, e.g, e.b) : void 0;
5525
+ function Ni(e) {
5526
+ let t = {}, n = Mi(e.backgroundColor), r = Mi(e.titleColor);
5158
5527
  return n && (t.background = n), r && (t.textTitles = r), t;
5159
5528
  }
5160
5529
  //#endregion
5161
- export { I as M8KeyMask, j as M8_AUDIO_CAPTURE_CONSTRAINTS, Ur as M8_THEME_ROLES, Wr as M8_THEME_VIEW, pn as RELATIVE_FX_RULE, rr as SCREEN_MARGIN, Ar as YAM8D_FRAME_NAME, F as createM8Audio, Br as createM8Client, Vr as createM8ClientSync, A as createM8Connection, Pt as createM8Engine, Hr as default, vr as describeContext, _r as describeContextParts, kr as formatFieldValue, Lr as getDeviceSupport, fn as getInstrumentFxCommands, P as getM8InputStream, gr as getSemanticContext, dn as lookupFxCommand, Qr as observeThemeColors, cr as parseGridScreen, Xr as parseThemeScreen, Jn as parseViewTitle, te as pressKeys, ur as readPlayPositions };
5530
+ export { R as M8KeyMask, A as M8_AUDIO_CAPTURE_CONSTRAINTS, wi as M8_THEME_ROLES, Ti as M8_THEME_VIEW, Jn as RELATIVE_FX_RULE, Rr as SCREEN_MARGIN, di as YAM8D_FRAME_NAME, P as createM8Audio, xi as createM8Client, Si as createM8ClientSync, ee as createM8Connection, gn as createM8Engine, Ci as default, $r as describeContext, Qr as describeContextParts, ui as formatFieldValue, vi as getDeviceSupport, qn as getInstrumentFxCommands, N as getM8InputStream, Zr as getSemanticContext, Kn as lookupFxCommand, Ni as observeThemeColors, Ur as parseGridScreen, ji as parseThemeScreen, kr as parseViewTitle, Fe as pressKeys, Gr as readPlayPositions };
5162
5531
 
5163
5532
  //# sourceMappingURL=index.js.map