authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-03-15 20:58:07-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-03-15 20:59:28-07:00
log86523418f2362aabae6d3729466100d9d200251b
tree488e6257b8e401446894bb08ad9d67c2aded1c09
parent75da6a34d58d3ef23ff082a16a6252a61654565a
signaturebadge-check Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

feat: reaper record flow


10 files changed, 373 insertions(+), 305 deletions(-)

config/reaper.ts+68-52
...@@ -1,58 +1,74 @@...@@ -1,58 +1,74 @@
1import * as config from "#config";
1import { Reaper } from "@clo/creative-control/Reaper.ts";2import { Reaper } from "@clo/creative-control/Reaper.ts";
2import { SpeedEditor } from "@clo/creative-control/SpeedEditor.ts";3import { SpeedEditor } from "@clo/creative-control/SpeedEditor.ts";
3import * as config from "#config";
44
5export default config.forApp("com.cockos.reaper", ({ speededitor: se, mac }) => {5export default config.forApp(
6 const reaper = new Reaper();6 "com.cockos.reaper",
7 ({ speededitor: se, mac }) => {
8 const reaper = new Reaper();
79
8 // Sync state to LEDs10 // Sync state to LEDs
9 reaper.on("transport", (transport) => {11 reaper.on("transport", (transport) => {
10 console.info(transport);12 se.leds.cut = transport.recording;
11 se.leds.audioOnly = transport.recording;13 se.leds.dissolve = transport.recording;
12 });14 se.leds.smoothCut = transport.recording;
13 mac.on("window", (window) => {15 });
14 se.leds.videoOnly = Boolean(window?.title.startsWith("FX:"));16 mac.on("window", (window) => {
15 });17 se.leds.videoOnly = Boolean(window?.title.startsWith("FX:"));
18 });
1619
17 // Keyboard Actions20 // Keyboard Actions
18 SpeedEditor.camNumbersToNumpad(se, mac);21 SpeedEditor.camNumbersToNumpad(se, mac);
19 se.onPress("stopPlay", () => {22 se.onPress("stopPlay", () => {
20 if(reaper.transport.recording) {23 if (reaper.transport.recording) {
21 reaper.runAction('transport-stop-save-all-recorded-media');24 reaper.runAction("transport-stop-save-all-recorded-media");
22 } else {25 } else {
23 reaper.runAction('transport-play-stop');26 reaper.runAction("transport-play-stop");
24 }27 }
25 });28 });
26 se.onPress("smartInsert", () => {29 se.onPress("smartInsert", () => {
27 mac.toast("Add Komplete Kontrol")30 mac.toast("Add Komplete Kontrol");
28 reaper.runScript("insert_komplete_kontrol_track");31 reaper.runScript("insert_komplete_kontrol_track");
29 });32 });
30 se.onPress("append", () => {33 se.onPress("append", () => {
31 mac.toast("Add Addictive Drums")34 mac.toast("Add Addictive Drums");
32 reaper.runScript("insert_addictive_drums_track");35 reaper.runScript("insert_addictive_drums_track");
33 });36 });
34 se.onPress("rippleOverwrite", () => {37 se.onPress("rippleOverwrite", () => {
35 mac.toast("Blank Track")38 mac.toast("Blank Track");
36 reaper.runScript("insert_blank_track");39 reaper.runScript("insert_blank_track");
37 });40 });
38 se.onPress("audioOnly", () => {41 se.onPress("cut", () => {
39 if(reaper.transport.recording) {42 if (reaper.transport.recording) {
40 reaper.runAction('transport-stop-delete-all-recorded-media');43 reaper.runAction("transport-stop-delete-all-recorded-media");
41 reaper.runAction('transport-record');44 reaper.runAction("transport-record");
42 } else {45 } else {
43 reaper.runAction('transport-record');46 reaper.runAction("transport-record");
44 }47 }
45 });48 });
46 se.onPress("videoOnly", () => {49 se.onPress("dissolve", () => {
47 if (mac.window?.title.startsWith("FX:") && mac.mainWindow) {50 reaper.runAction("transport-stop-delete-all-recorded-media");
48 mac.focusMainWindow();51 });
49 return;52 se.onPress("smoothCut", () => {
50 }53 if (reaper.transport.recording) {
51 reaper.runAction("track-view-fx-chain-for-current-last-touched-track");54 reaper.runAction("transport-stop-save-all-recorded-media");
52 });55 reaper.runAction("transport-record");
56 }
57 });
58 se.onPress("videoOnly", () => {
59 if (mac.window?.title.startsWith("FX:") && mac.mainWindow) {
60 mac.focusMainWindow();
61 return;
62 }
63 reaper.runAction("track-view-fx-chain-for-current-last-touched-track");
64 });
65 se.onPress("fullView", () => {
66 mac.pressKey("x");
67 });
5368
54 // TODO: stabilize / find a better use-case for the knob69 // TODO: stabilize / find a better use-case for the knob
55 se.on("jog", ({ value = 0 }) => {70 // se.on("jog", ({ value = 0 }) => {
56 reaper.scrub(value / 20);71 // reaper.scrub(value / 20);
57 });72 // });
58});73 },
74);
dprint.json created+17
...@@ -0,0 +1,17 @@
1{
2 "typescript": {
3 },
4 "json": {
5 },
6 "markdown": {
7 },
8 "excludes": [
9 "**/node_modules",
10 "**/*-lock.json"
11 ],
12 "plugins": [
13 "https://plugins.dprint.dev/typescript-0.95.15.wasm",
14 "https://plugins.dprint.dev/json-0.21.1.wasm",
15 "https://plugins.dprint.dev/markdown-0.21.1.wasm"
16 ]
17}
examples/event-listener.ts+1-1
...@@ -15,7 +15,7 @@ editor.on("keypress", (key) => {...@@ -15,7 +15,7 @@ editor.on("keypress", (key) => {
15 }15 }
16});16});
17editor.onDoublePress("transition", () => {17editor.onDoublePress("transition", () => {
18 console.info('(double press) "Title"');18 console.info("(double press) \"Title\"");
19});19});
2020
21console.info("init");21console.info("init");
readme.md+4-3
...@@ -10,7 +10,8 @@ These tools only work on macOS. I don't have interest in maintaining other confi...@@ -10,7 +10,8 @@ These tools only work on macOS. I don't have interest in maintaining other confi
1010
11## Hardware11## Hardware
1212
13TODO: 13TODO:
14
14- generic keyboard / karabiner elements integration?15- generic keyboard / karabiner elements integration?
15- custom trackpad integration?16- custom trackpad integration?
1617
...@@ -55,8 +56,8 @@ reaper.on("transport", (transport) => {...@@ -55,8 +56,8 @@ reaper.on("transport", (transport) => {
55 transport.recording56 transport.recording
56 ? "You are recording"57 ? "You are recording"
57 : transport.playing58 : transport.playing
58 ? "Playing"59 ? "Playing"
59 : "Stopped"60 : "Stopped",
60 );61 );
61});62});
62```63```
src/Mac.ts+142-140
...@@ -32,13 +32,13 @@ const FRONTMOST_APP_HELPER_FRAMEWORKS = [...@@ -32,13 +32,13 @@ const FRONTMOST_APP_HELPER_FRAMEWORKS = [
32 "ApplicationServices",32 "ApplicationServices",
33] as const;33] as const;
34const KEYBOARD_EVENT_SCRIPT = [34const KEYBOARD_EVENT_SCRIPT = [
35 'ObjC.import("ApplicationServices");',35 "ObjC.import(\"ApplicationServices\");",
36 "function run(argv) {",36 "function run(argv) {",
37 " const payload = JSON.parse(argv[0] ?? '{}');",37 " const payload = JSON.parse(argv[0] ?? '{}');",
38 " const actions = Array.isArray(payload.actions) ? payload.actions : [];",38 " const actions = Array.isArray(payload.actions) ? payload.actions : [];",
39 " for (const action of actions) {",39 " for (const action of actions) {",
40 " if (!Number.isInteger(action.keyCode)) {",40 " if (!Number.isInteger(action.keyCode)) {",
41 ' throw new Error(`Invalid key code: ${JSON.stringify(action)}`);',41 " throw new Error(`Invalid key code: ${JSON.stringify(action)}`);",
42 " }",42 " }",
43 " const event = $.CGEventCreateKeyboardEvent(",43 " const event = $.CGEventCreateKeyboardEvent(",
44 " null,",44 " null,",
...@@ -46,139 +46,141 @@ const KEYBOARD_EVENT_SCRIPT = [...@@ -46,139 +46,141 @@ const KEYBOARD_EVENT_SCRIPT = [
46 " Boolean(action.isDown),",46 " Boolean(action.isDown),",
47 " );",47 " );",
48 " if (!event) {",48 " if (!event) {",
49 ' throw new Error(`Failed to create keyboard event for key code ${action.keyCode}`);',49 " throw new Error(`Failed to create keyboard event for key code ${action.keyCode}`);",
50 " }",50 " }",
51 " $.CGEventPost($.kCGHIDEventTap, event);",51 " $.CGEventPost($.kCGHIDEventTap, event);",
52 " $.CFRelease(event);",52 " $.CFRelease(event);",
53 " }",53 " }",
54 ' return "";',54 " return \"\";",
55 "}",55 "}",
56].join("\n");56].join("\n");
57let frontmostAppHelperBinaryPromise: Promise<string> | null = null;57let frontmostAppHelperBinaryPromise: Promise<string> | null = null;
58let toastHelperBinaryPromise: Promise<string> | null = null;58let toastHelperBinaryPromise: Promise<string> | null = null;
5959
60// Key codes from Carbon/HIToolbox Events.h (virtual key codes on ANSI/US layouts).60// Key codes from Carbon/HIToolbox Events.h (virtual key codes on ANSI/US layouts).
61const KEY_CODES = Object.freeze({61const KEY_CODES = Object.freeze(
62 a: 0x00,62 {
63 s: 0x01,63 a: 0x00,
64 d: 0x02,64 s: 0x01,
65 f: 0x03,65 d: 0x02,
66 h: 0x04,66 f: 0x03,
67 g: 0x05,67 h: 0x04,
68 z: 0x06,68 g: 0x05,
69 x: 0x07,69 z: 0x06,
70 c: 0x08,70 x: 0x07,
71 v: 0x09,71 c: 0x08,
72 isoSection: 0x0A,72 v: 0x09,
73 b: 0x0B,73 isoSection: 0x0A,
74 q: 0x0C,74 b: 0x0B,
75 w: 0x0D,75 q: 0x0C,
76 e: 0x0E,76 w: 0x0D,
77 r: 0x0F,77 e: 0x0E,
78 y: 0x10,78 r: 0x0F,
79 t: 0x11,79 y: 0x10,
80 "1": 0x12,80 t: 0x11,
81 "2": 0x13,81 "1": 0x12,
82 "3": 0x14,82 "2": 0x13,
83 "4": 0x15,83 "3": 0x14,
84 "6": 0x16,84 "4": 0x15,
85 "5": 0x17,85 "6": 0x16,
86 equal: 0x18,86 "5": 0x17,
87 "9": 0x19,87 equal: 0x18,
88 "7": 0x1A,88 "9": 0x19,
89 minus: 0x1B,89 "7": 0x1A,
90 "8": 0x1C,90 minus: 0x1B,
91 "0": 0x1D,91 "8": 0x1C,
92 rightBracket: 0x1E,92 "0": 0x1D,
93 o: 0x1F,93 rightBracket: 0x1E,
94 u: 0x20,94 o: 0x1F,
95 leftBracket: 0x21,95 u: 0x20,
96 i: 0x22,96 leftBracket: 0x21,
97 p: 0x23,97 i: 0x22,
98 return: 0x24,98 p: 0x23,
99 l: 0x25,99 return: 0x24,
100 j: 0x26,100 l: 0x25,
101 quote: 0x27,101 j: 0x26,
102 k: 0x28,102 quote: 0x27,
103 semicolon: 0x29,103 k: 0x28,
104 backslash: 0x2A,104 semicolon: 0x29,
105 comma: 0x2B,105 backslash: 0x2A,
106 slash: 0x2C,106 comma: 0x2B,
107 n: 0x2D,107 slash: 0x2C,
108 m: 0x2E,108 n: 0x2D,
109 period: 0x2F,109 m: 0x2E,
110 tab: 0x30,110 period: 0x2F,
111 space: 0x31,111 tab: 0x30,
112 grave: 0x32,112 space: 0x31,
113 delete: 0x33,113 grave: 0x32,
114 escape: 0x35,114 delete: 0x33,
115 rightCommand: 0x36,115 escape: 0x35,
116 command: 0x37,116 rightCommand: 0x36,
117 shift: 0x38,117 command: 0x37,
118 capsLock: 0x39,118 shift: 0x38,
119 option: 0x3A,119 capsLock: 0x39,
120 control: 0x3B,120 option: 0x3A,
121 rightShift: 0x3C,121 control: 0x3B,
122 rightOption: 0x3D,122 rightShift: 0x3C,
123 rightControl: 0x3E,123 rightOption: 0x3D,
124 function: 0x3F,124 rightControl: 0x3E,
125 f17: 0x40,125 function: 0x3F,
126 numpadDecimal: 0x41,126 f17: 0x40,
127 numpadMultiply: 0x43,127 numpadDecimal: 0x41,
128 numpadPlus: 0x45,128 numpadMultiply: 0x43,
129 numpadClear: 0x47,129 numpadPlus: 0x45,
130 volumeUp: 0x48,130 numpadClear: 0x47,
131 volumeDown: 0x49,131 volumeUp: 0x48,
132 mute: 0x4A,132 volumeDown: 0x49,
133 numpadDivide: 0x4B,133 mute: 0x4A,
134 numpadEnter: 0x4C,134 numpadDivide: 0x4B,
135 numpadMinus: 0x4E,135 numpadEnter: 0x4C,
136 f18: 0x4F,136 numpadMinus: 0x4E,
137 f19: 0x50,137 f18: 0x4F,
138 numpadEquals: 0x51,138 f19: 0x50,
139 numpad0: 0x52,139 numpadEquals: 0x51,
140 numpad1: 0x53,140 numpad0: 0x52,
141 numpad2: 0x54,141 numpad1: 0x53,
142 numpad3: 0x55,142 numpad2: 0x54,
143 numpad4: 0x56,143 numpad3: 0x55,
144 numpad5: 0x57,144 numpad4: 0x56,
145 numpad6: 0x58,145 numpad5: 0x57,
146 numpad7: 0x59,146 numpad6: 0x58,
147 f20: 0x5A,147 numpad7: 0x59,
148 numpad8: 0x5B,148 f20: 0x5A,
149 numpad9: 0x5C,149 numpad8: 0x5B,
150 jisYen: 0x5D,150 numpad9: 0x5C,
151 jisUnderscore: 0x5E,151 jisYen: 0x5D,
152 jisKeypadComma: 0x5F,152 jisUnderscore: 0x5E,
153 f5: 0x60,153 jisKeypadComma: 0x5F,
154 f6: 0x61,154 f5: 0x60,
155 f7: 0x62,155 f6: 0x61,
156 f3: 0x63,156 f7: 0x62,
157 f8: 0x64,157 f3: 0x63,
158 f9: 0x65,158 f8: 0x64,
159 jisEisu: 0x66,159 f9: 0x65,
160 f11: 0x67,160 jisEisu: 0x66,
161 jisKana: 0x68,161 f11: 0x67,
162 f13: 0x69,162 jisKana: 0x68,
163 f16: 0x6A,163 f13: 0x69,
164 f14: 0x6B,164 f16: 0x6A,
165 f10: 0x6D,165 f14: 0x6B,
166 f12: 0x6F,166 f10: 0x6D,
167 f15: 0x71,167 f12: 0x6F,
168 help: 0x72,168 f15: 0x71,
169 home: 0x73,169 help: 0x72,
170 pageUp: 0x74,170 home: 0x73,
171 forwardDelete: 0x75,171 pageUp: 0x74,
172 f4: 0x76,172 forwardDelete: 0x75,
173 end: 0x77,173 f4: 0x76,
174 f2: 0x78,174 end: 0x77,
175 pageDown: 0x79,175 f2: 0x78,
176 f1: 0x7A,176 pageDown: 0x79,
177 leftArrow: 0x7B,177 f1: 0x7A,
178 rightArrow: 0x7C,178 leftArrow: 0x7B,
179 downArrow: 0x7D,179 rightArrow: 0x7C,
180 upArrow: 0x7E,180 downArrow: 0x7D,
181} as const);181 upArrow: 0x7E,
182 } as const,
183);
182type MacKeyName = keyof typeof KEY_CODES;184type MacKeyName = keyof typeof KEY_CODES;
183185
184const KEY_NAMES = Object.freeze(Object.keys(KEY_CODES) as MacKeyName[]);186const KEY_NAMES = Object.freeze(Object.keys(KEY_CODES) as MacKeyName[]);
...@@ -732,12 +734,12 @@ function parseFrontmostStateLine(line: string): FrontmostState {...@@ -732,12 +734,12 @@ function parseFrontmostStateLine(line: string): FrontmostState {
732734
733function normalizeBundleId(value: unknown) {735function normalizeBundleId(value: unknown) {
734 const directBundleId = typeof value === "string" ? value.trim() : "";736 const directBundleId = typeof value === "string" ? value.trim() : "";
735 const bundleId = BUNDLE_ID_PATTERN.exec(directBundleId)?.[0] ??737 const bundleId = BUNDLE_ID_PATTERN.exec(directBundleId)?.[0]
736 directBundleId;738 ?? directBundleId;
737 if (739 if (
738 bundleId === "" ||740 bundleId === ""
739 bundleId === "undefined" ||741 || bundleId === "undefined"
740 bundleId === "[id nil]"742 || bundleId === "[id nil]"
741 ) {743 ) {
742 return null;744 return null;
743 }745 }
...@@ -837,10 +839,10 @@ function windowEquals(left: Mac.Window | null, right: Mac.Window | null) {...@@ -837,10 +839,10 @@ function windowEquals(left: Mac.Window | null, right: Mac.Window | null) {
837 if (!left || !right) {839 if (!left || !right) {
838 return left === right;840 return left === right;
839 }841 }
840 return left.id === right.id &&842 return left.id === right.id
841 left.title === right.title &&843 && left.title === right.title
842 left.main === right.main &&844 && left.main === right.main
843 left.focused === right.focused;845 && left.focused === right.focused;
844}846}
845847
846function normalizeDelayMs(value: number, name: string) {848function normalizeDelayMs(value: number, name: string) {
...@@ -884,9 +886,9 @@ function resolveKeyCode(key: Mac.Key): number {...@@ -884,9 +886,9 @@ function resolveKeyCode(key: Mac.Key): number {
884886
885function normalizeKeyCode(keyCode: number) {887function normalizeKeyCode(keyCode: number) {
886 if (888 if (
887 !Number.isInteger(keyCode) ||889 !Number.isInteger(keyCode)
888 keyCode < 0 ||890 || keyCode < 0
889 keyCode > 0xFFFF891 || keyCode > 0xFFFF
890 ) {892 ) {
891 throw new Error(`Mac keyCode must be an integer between 0 and 65535`);893 throw new Error(`Mac keyCode must be an integer between 0 and 65535`);
892 }894 }
...@@ -946,9 +948,9 @@ function formatKeyboardDispatchError(error: unknown) {...@@ -946,9 +948,9 @@ function formatKeyboardDispatchError(error: unknown) {
946 const details = extractCommandErrorOutput(error);948 const details = extractCommandErrorOutput(error);
947 const suffix = details ? ` ${details}` : "";949 const suffix = details ? ` ${details}` : "";
948 return (950 return (
949 "Failed to send a macOS keyboard event via osascript. " +951 "Failed to send a macOS keyboard event via osascript. "
950 "Make sure this process is allowed in System Settings > Privacy & Security > Accessibility." +952 + "Make sure this process is allowed in System Settings > Privacy & Security > Accessibility."
951 suffix953 + suffix
952 );954 );
953}955}
954956
src/Reaper.ts+20-20
...@@ -1,6 +1,5 @@...@@ -1,6 +1,5 @@
1import { Events } from "@clo/lib/Events.ts";1import { Events } from "@clo/lib/Events.ts";
2import * as log from "@clo/lib/log.ts";2import * as log from "@clo/lib/log.ts";
3import { REAPER_ACTIONS, type ReaperActionId } from "./Reaper/actions.ts";
4import { execFile } from "node:child_process";3import { execFile } from "node:child_process";
5import { createSocket, type Socket } from "node:dgram";4import { createSocket, type Socket } from "node:dgram";
6import { readFileSync } from "node:fs";5import { readFileSync } from "node:fs";
...@@ -9,6 +8,7 @@ import { basename, dirname, join } from "node:path";...@@ -9,6 +8,7 @@ import { basename, dirname, join } from "node:path";
9import process from "node:process";8import process from "node:process";
10import { fileURLToPath } from "node:url";9import { fileURLToPath } from "node:url";
11import { promisify } from "node:util";10import { promisify } from "node:util";
11import { REAPER_ACTIONS, type ReaperActionId } from "./Reaper/actions.ts";
12const console = log.scoped("reaper");12const console = log.scoped("reaper");
1313
14export type { ReaperActionId } from "./Reaper/actions.ts";14export type { ReaperActionId } from "./Reaper/actions.ts";
...@@ -94,10 +94,10 @@ const DEFAULT_REAPER_OSC_TARGET_DIR = join(...@@ -94,10 +94,10 @@ const DEFAULT_REAPER_OSC_TARGET_DIR = join(
94 DEFAULT_REAPER_RESOURCE_DIR,94 DEFAULT_REAPER_RESOURCE_DIR,
95 "OSC",95 "OSC",
96);96);
97const REAPER_SCRIPT_TARGET_DIR = process.env.REAPER_SCRIPTS_DIR ??97const REAPER_SCRIPT_TARGET_DIR = process.env.REAPER_SCRIPTS_DIR
98 DEFAULT_REAPER_SCRIPT_TARGET_DIR;98 ?? DEFAULT_REAPER_SCRIPT_TARGET_DIR;
99const REAPER_OSC_TARGET_DIR = process.env.REAPER_OSC_DIR ??99const REAPER_OSC_TARGET_DIR = process.env.REAPER_OSC_DIR
100 DEFAULT_REAPER_OSC_TARGET_DIR;100 ?? DEFAULT_REAPER_OSC_TARGET_DIR;
101const OSC_PATTERN_FILE = "CloverAutomation.ReaperOSC";101const OSC_PATTERN_FILE = "CloverAutomation.ReaperOSC";
102const OSC_PATTERN_NAME = stripReaperOscExtension(OSC_PATTERN_FILE);102const OSC_PATTERN_NAME = stripReaperOscExtension(OSC_PATTERN_FILE);
103const OSC_PATTERN_CONFIG = `# OSC pattern config file for Clover Creative Control's REAPER integration.103const OSC_PATTERN_CONFIG = `# OSC pattern config file for Clover Creative Control's REAPER integration.
...@@ -174,10 +174,10 @@ export class Reaper extends Events<Reaper.EventMap> {...@@ -174,10 +174,10 @@ export class Reaper extends Events<Reaper.EventMap> {
174 }174 }
175175
176 console.info(176 console.info(
177 `${OSC_PATTERN_FILE} is not registered yet. ` +177 `${OSC_PATTERN_FILE} is not registered yet. `
178 `Add an OSC control surface manually using the "${OSC_PATTERN_NAME}" pattern, set REAPER's local listen port to ` +178 + `Add an OSC control surface manually using the "${OSC_PATTERN_NAME}" pattern, set REAPER's local listen port to `
179 `${this.#oscPort}, and send to ${this.#oscHost}:${this.#oscBindPort}. ` +179 + `${this.#oscPort}, and send to ${this.#oscHost}:${this.#oscBindPort}. `
180 "The REAPER web interface is not required for this config."180 + "The REAPER web interface is not required for this config.",
181 );181 );
182 }182 }
183183
...@@ -475,13 +475,13 @@ function sameTransportState(...@@ -475,13 +475,13 @@ function sameTransportState(
475 left: ReaperTransportState,475 left: ReaperTransportState,
476 right: ReaperTransportState,476 right: ReaperTransportState,
477) {477) {
478 return left.playing === right.playing &&478 return left.playing === right.playing
479 left.paused === right.paused &&479 && left.paused === right.paused
480 left.recording === right.recording &&480 && left.recording === right.recording
481 left.repeatOn === right.repeatOn &&481 && left.repeatOn === right.repeatOn
482 left.positionSeconds === right.positionSeconds &&482 && left.positionSeconds === right.positionSeconds
483 left.positionString === right.positionString &&483 && left.positionString === right.positionString
484 left.positionBeatsString === right.positionBeatsString;484 && left.positionBeatsString === right.positionBeatsString;
485}485}
486486
487function optimisticTransportPatchForAction(487function optimisticTransportPatchForAction(
...@@ -615,8 +615,8 @@ function hasManagedOscSurface(config: string) {...@@ -615,8 +615,8 @@ function hasManagedOscSurface(config: string) {
615615
616 const normalizedTokens = tokens.map((token) => unquoteReaperToken(token));616 const normalizedTokens = tokens.map((token) => unquoteReaperToken(token));
617 if (617 if (
618 normalizedTokens.includes(OSC_PATTERN_FILE) ||618 normalizedTokens.includes(OSC_PATTERN_FILE)
619 normalizedTokens.includes(OSC_PATTERN_NAME)619 || normalizedTokens.includes(OSC_PATTERN_NAME)
620 ) {620 ) {
621 return true;621 return true;
622 }622 }
...@@ -645,8 +645,8 @@ function formatError(error: unknown) {...@@ -645,8 +645,8 @@ function formatError(error: unknown) {
645645
646function unquoteReaperToken(token: string) {646function unquoteReaperToken(token: string) {
647 if (647 if (
648 (token.startsWith("'") && token.endsWith("'")) ||648 (token.startsWith("'") && token.endsWith("'"))
649 (token.startsWith("\"") && token.endsWith("\""))649 || (token.startsWith("\"") && token.endsWith("\""))
650 ) {650 ) {
651 return token.slice(1, -1);651 return token.slice(1, -1);
652 }652 }
src/Reaper/actions.ts+82-41
...@@ -52,7 +52,8 @@ export const REAPER_ACTIONS = {...@@ -52,7 +52,8 @@ export const REAPER_ACTIONS = {
52 "action-skip-next-action-if-cc-parameter-0-mid-2017": 2017,52 "action-skip-next-action-if-cc-parameter-0-mid-2017": 2017,
53 "action-skip-next-action-if-cc-parameter-0-mid-2018": 2018,53 "action-skip-next-action-if-cc-parameter-0-mid-2018": 2018,
54 "action-skip-next-action-set-cc-parameter-to-relative-plus-1-if-action-armed-0-otherwise": 2023,54 "action-skip-next-action-set-cc-parameter-to-relative-plus-1-if-action-armed-0-otherwise": 2023,
55 "action-skip-next-action-set-cc-parameter-to-relative-plus-1-if-action-toggle-state-enabled-1-if-disabled-0-if-toggle-state-unavailable": 2022,55 "action-skip-next-action-set-cc-parameter-to-relative-plus-1-if-action-toggle-state-enabled-1-if-disabled-0-if-toggle-state-unavailable":
56 2022,
56 "action-toggle-arm-of-next-action": 2021,57 "action-toggle-arm-of-next-action": 2021,
57 "action-wait-0-1-seconds-before-next-action": 2008,58 "action-wait-0-1-seconds-before-next-action": 2008,
58 "action-wait-0-5-seconds-before-next-action": 2009,59 "action-wait-0-5-seconds-before-next-action": 2009,
...@@ -625,7 +626,8 @@ export const REAPER_ACTIONS = {...@@ -625,7 +626,8 @@ export const REAPER_ACTIONS = {
625 "file-open-render-queue": 40929,626 "file-open-render-queue": 40929,
626 "file-open-render-statistics-charts-from-most-recent-render-in-web-browser": 42497,627 "file-open-render-statistics-charts-from-most-recent-render-in-web-browser": 42497,
627 "file-open-render-statistics-charts-from-most-recent-render-in-web-browser-hide-file-paths": 42510,628 "file-open-render-statistics-charts-from-most-recent-render-in-web-browser-hide-file-paths": 42510,
628 "file-open-render-statistics-charts-from-most-recent-render-in-web-browser-hide-file-paths-and-project-regions-markers": 43343,629 "file-open-render-statistics-charts-from-most-recent-render-in-web-browser-hide-file-paths-and-project-regions-markers":
630 43343,
629 "file-open-render-statistics-charts-from-most-recent-render-in-web-browser-hide-project-regions-markers": 43342,631 "file-open-render-statistics-charts-from-most-recent-render-in-web-browser-hide-project-regions-markers": 43342,
630 "file-project-settings": 40021,632 "file-project-settings": 40021,
631 "file-quit-reaper": 40004,633 "file-quit-reaper": 40004,
...@@ -1203,7 +1205,8 @@ export const REAPER_ACTIONS = {...@@ -1203,7 +1205,8 @@ export const REAPER_ACTIONS = {
1203 "item-open-items-in-secondary-external-editor": 40202,1205 "item-open-items-in-secondary-external-editor": 40202,
1204 "item-paste-items-tracks": 42398,1206 "item-paste-items-tracks": 42398,
1205 "item-paste-items-tracks-at-mouse-position": 41221,1207 "item-paste-items-tracks-at-mouse-position": 41221,
1206 "item-paste-items-tracks-creating-pooled-ghost-midi-items-and-automation-items-regardless-of-preferences-media-midi-and-preferences-media-automation-settings": 41072,1208 "item-paste-items-tracks-creating-pooled-ghost-midi-items-and-automation-items-regardless-of-preferences-media-midi-and-preferences-media-automation-settings":
1209 41072,
1207 "item-paste-items-tracks-old-style-handling-of-hidden-tracks": 40058,1210 "item-paste-items-tracks-old-style-handling-of-hidden-tracks": 40058,
1208 "item-propagate-to-all-similarly-named-items": 41979,1211 "item-propagate-to-all-similarly-named-items": 41979,
1209 "item-propagate-to-similarly-named-items-on-track": 41977,1212 "item-propagate-to-similarly-named-items-on-track": 41977,
...@@ -1256,9 +1259,12 @@ export const REAPER_ACTIONS = {...@@ -1256,9 +1259,12 @@ export const REAPER_ACTIONS = {
1256 "item-properties-set-item-timebase-to-beats-position-only": 40485,1259 "item-properties-set-item-timebase-to-beats-position-only": 40485,
1257 "item-properties-set-item-timebase-to-project-track-default": 40380,1260 "item-properties-set-item-timebase-to-project-track-default": 40380,
1258 "item-properties-set-item-timebase-to-time": 40433,1261 "item-properties-set-item-timebase-to-time": 40433,
1259 "item-properties-set-midi-items-to-follow-project-tempo-changes-set-item-timebase-to-beats-position-length-rate": 43095,1262 "item-properties-set-midi-items-to-follow-project-tempo-changes-set-item-timebase-to-beats-position-length-rate":
1260 "item-properties-set-midi-items-to-use-current-tempo-and-ignore-project-tempo-changes-set-item-timebase-to-beats-position-only": 43096,1263 43095,
1261 "item-properties-set-midi-items-to-use-current-tempo-and-ignore-project-tempo-changes-set-item-timebase-to-time": 43094,1264 "item-properties-set-midi-items-to-use-current-tempo-and-ignore-project-tempo-changes-set-item-timebase-to-beats-position-only":
1265 43096,
1266 "item-properties-set-midi-items-to-use-current-tempo-and-ignore-project-tempo-changes-set-item-timebase-to-time":
1267 43094,
1262 "item-properties-set-take-channel-mode-to-mono-channel-03": 41388,1268 "item-properties-set-take-channel-mode-to-mono-channel-03": 41388,
1263 "item-properties-set-take-channel-mode-to-mono-channel-04": 41389,1269 "item-properties-set-take-channel-mode-to-mono-channel-04": 41389,
1264 "item-properties-set-take-channel-mode-to-mono-channel-05": 41390,1270 "item-properties-set-take-channel-mode-to-mono-channel-05": 41390,
...@@ -1812,12 +1818,14 @@ export const REAPER_ACTIONS = {...@@ -1812,12 +1818,14 @@ export const REAPER_ACTIONS = {
1812 "options-add-edge-points-when-moving-envelope-points-with-items": 40648,1818 "options-add-edge-points-when-moving-envelope-points-with-items": 40648,
1813 "options-add-edge-points-when-moving-multiple-envelope-points": 42030,1819 "options-add-edge-points-when-moving-multiple-envelope-points": 42030,
1814 "options-add-envelope-edge-points-when-ripple-editing-or-inserting-time": 40649,1820 "options-add-envelope-edge-points-when-ripple-editing-or-inserting-time": 40649,
1815 "options-allow-drag-drop-media-import-to-target-the-top-part-of-a-track-to-insert-a-new-track-to-receive-the-media": 42477,1821 "options-allow-drag-drop-media-import-to-target-the-top-part-of-a-track-to-insert-a-new-track-to-receive-the-media":
1822 42477,
1816 "options-allow-selecting-empty-takes": 41355,1823 "options-allow-selecting-empty-takes": 41355,
1817 "options-always-record-to-automation-items": 42212,1824 "options-always-record-to-automation-items": 42212,
1818 "options-always-trim-content-behind-razor-edits-otherwise-follow-media-item-editing-preferences": 42421,1825 "options-always-trim-content-behind-razor-edits-otherwise-follow-media-item-editing-preferences": 42421,
1819 "options-auto-crossfade-media-items-when-editing": 40041,1826 "options-auto-crossfade-media-items-when-editing": 40041,
1820 "options-automatically-insert-automation-item-when-activating-envelope-that-is-bypassed-outside-of-automation-items-and-not-displayed-in-envelope-lane": 42220,1827 "options-automatically-insert-automation-item-when-activating-envelope-that-is-bypassed-outside-of-automation-items-and-not-displayed-in-envelope-lane":
1828 42220,
1821 "options-automation-item-baseline-amplitude-edits-affect-pooled-copies": 42194,1829 "options-automation-item-baseline-amplitude-edits-affect-pooled-copies": 42194,
1822 "options-automation-items-connect-to-the-underlying-envelope-on-both-sides": 42205,1830 "options-automation-items-connect-to-the-underlying-envelope-on-both-sides": 42205,
1823 "options-automation-items-connect-to-the-underlying-envelope-on-the-right-side": 42204,1831 "options-automation-items-connect-to-the-underlying-envelope-on-the-right-side": 42204,
...@@ -2307,8 +2315,10 @@ export const REAPER_ACTIONS = {...@@ -2307,8 +2315,10 @@ export const REAPER_ACTIONS = {
2307 "sws-aw-play-stop-automatically-group-simultaneously-recorded-items-deprecated": 53503,2315 "sws-aw-play-stop-automatically-group-simultaneously-recorded-items-deprecated": 53503,
2308 "sws-aw-record-automatically-group-simultaneously-recorded-items-deprecated": 53500,2316 "sws-aw-record-automatically-group-simultaneously-recorded-items-deprecated": 53500,
2309 "sws-aw-record-conditional-normal-or-time-selection-only": 53497,2317 "sws-aw-record-conditional-normal-or-time-selection-only": 53497,
2310 "sws-aw-record-conditional-normal-or-time-selection-only-automatically-group-simultaneously-recorded-items-deprecated": 53501,2318 "sws-aw-record-conditional-normal-or-time-selection-only-automatically-group-simultaneously-recorded-items-deprecated":
2311 "sws-aw-record-conditional-normal-time-selection-item-selection-automatically-group-simultaneously-recorded-items-deprecated": 53502,2319 53501,
2320 "sws-aw-record-conditional-normal-time-selection-item-selection-automatically-group-simultaneously-recorded-items-deprecated":
2321 53502,
2312 "sws-aw-record-conditional-normal-time-selection-or-item-selection": 53498,2322 "sws-aw-record-conditional-normal-time-selection-or-item-selection": 53498,
2313 "sws-aw-remove-overlaps-in-selected-items-preserving-item-starts": 53496,2323 "sws-aw-remove-overlaps-in-selected-items-preserving-item-starts": 53496,
2314 "sws-aw-remove-tracks-items-env-obeying-time-selection-and-leaving-children": 53554,2324 "sws-aw-remove-tracks-items-env-obeying-time-selection-and-leaving-children": 53554,
...@@ -2383,24 +2393,34 @@ export const REAPER_ACTIONS = {...@@ -2383,24 +2393,34 @@ export const REAPER_ACTIONS = {
2383 "sws-br-apply-next-action-to-all-visible-envelopes-in-selected-tracks": 54100,2393 "sws-br-apply-next-action-to-all-visible-envelopes-in-selected-tracks": 54100,
2384 "sws-br-apply-next-action-to-all-visible-envelopes-in-selected-tracks-if-there-is-no-track-envelope-selected": 54102,2394 "sws-br-apply-next-action-to-all-visible-envelopes-in-selected-tracks-if-there-is-no-track-envelope-selected": 54102,
2385 "sws-br-apply-next-action-to-all-visible-record-armed-envelopes-in-selected-tracks": 54101,2395 "sws-br-apply-next-action-to-all-visible-record-armed-envelopes-in-selected-tracks": 54101,
2386 "sws-br-apply-next-action-to-all-visible-record-armed-envelopes-in-selected-tracks-if-there-is-no-track-envelope-selected": 54103,2396 "sws-br-apply-next-action-to-all-visible-record-armed-envelopes-in-selected-tracks-if-there-is-no-track-envelope-selected":
2397 54103,
2387 "sws-br-check-for-new-sws-version": 55821,2398 "sws-br-check-for-new-sws-version": 55821,
2388 "sws-br-contextual-toolbars": 53938,2399 "sws-br-contextual-toolbars": 53938,
2389 "sws-br-convert-project-markers-to-tempo-markers": 54846,2400 "sws-br-convert-project-markers-to-tempo-markers": 54846,
2390 "sws-br-convert-selected-envelope-s-curve-in-time-selection-to-cc-events-in-last-clicked-cc-lane-in-last-active-midi-editor": 54051,2401 "sws-br-convert-selected-envelope-s-curve-in-time-selection-to-cc-events-in-last-clicked-cc-lane-in-last-active-midi-editor":
2391 "sws-br-convert-selected-envelope-s-curve-in-time-selection-to-cc-events-in-last-clicked-cc-lane-in-last-active-midi-editor-clear-existing-events": 54052,2402 54051,
2392 "sws-br-convert-selected-points-in-selected-envelope-to-cc-events-in-last-clicked-cc-lane-in-last-active-midi-editor": 54049,2403 "sws-br-convert-selected-envelope-s-curve-in-time-selection-to-cc-events-in-last-clicked-cc-lane-in-last-active-midi-editor-clear-existing-events":
2393 "sws-br-convert-selected-points-in-selected-envelope-to-cc-events-in-last-clicked-cc-lane-in-last-active-midi-editor-clear-existing-events": 54050,2404 54052,
2405 "sws-br-convert-selected-points-in-selected-envelope-to-cc-events-in-last-clicked-cc-lane-in-last-active-midi-editor":
2406 54049,
2407 "sws-br-convert-selected-points-in-selected-envelope-to-cc-events-in-last-clicked-cc-lane-in-last-active-midi-editor-clear-existing-events":
2408 54050,
2394 "sws-br-copy-points-in-time-selection-in-selected-envelope-to-all-visible-envelopes-in-selected-tracks": 54072,2409 "sws-br-copy-points-in-time-selection-in-selected-envelope-to-all-visible-envelopes-in-selected-tracks": 54072,
2395 "sws-br-copy-points-in-time-selection-in-selected-envelope-to-all-visible-envelopes-in-selected-tracks-paste-at-edit-cursor": 54076,2410 "sws-br-copy-points-in-time-selection-in-selected-envelope-to-all-visible-envelopes-in-selected-tracks-paste-at-edit-cursor":
2396 "sws-br-copy-points-in-time-selection-in-selected-envelope-to-all-visible-record-armed-in-envelopes-of-selected-tracks": 54073,2411 54076,
2397 "sws-br-copy-points-in-time-selection-in-selected-envelope-to-all-visible-record-armed-in-envelopes-of-selected-tracks-paste-at-edit-cursor": 54077,2412 "sws-br-copy-points-in-time-selection-in-selected-envelope-to-all-visible-record-armed-in-envelopes-of-selected-tracks":
2413 54073,
2414 "sws-br-copy-points-in-time-selection-in-selected-envelope-to-all-visible-record-armed-in-envelopes-of-selected-tracks-paste-at-edit-cursor":
2415 54077,
2398 "sws-br-copy-points-in-time-selection-in-selected-envelope-to-envelope-at-mouse-cursor": 54079,2416 "sws-br-copy-points-in-time-selection-in-selected-envelope-to-envelope-at-mouse-cursor": 54079,
2399 "sws-br-copy-points-in-time-selection-in-selected-envelope-to-envelope-at-mouse-cursor-paste-at-edit-cursor": 54081,2417 "sws-br-copy-points-in-time-selection-in-selected-envelope-to-envelope-at-mouse-cursor-paste-at-edit-cursor": 54081,
2400 "sws-br-copy-selected-points-in-selected-envelope-to-all-visible-envelopes-in-selected-tracks": 54070,2418 "sws-br-copy-selected-points-in-selected-envelope-to-all-visible-envelopes-in-selected-tracks": 54070,
2401 "sws-br-copy-selected-points-in-selected-envelope-to-all-visible-envelopes-in-selected-tracks-paste-at-edit-cursor": 54074,2419 "sws-br-copy-selected-points-in-selected-envelope-to-all-visible-envelopes-in-selected-tracks-paste-at-edit-cursor":
2420 54074,
2402 "sws-br-copy-selected-points-in-selected-envelope-to-all-visible-record-armed-envelopes-in-selected-tracks": 54071,2421 "sws-br-copy-selected-points-in-selected-envelope-to-all-visible-record-armed-envelopes-in-selected-tracks": 54071,
2403 "sws-br-copy-selected-points-in-selected-envelope-to-all-visible-record-armed-envelopes-in-selected-tracks-paste-at-edit-cursor": 54075,2422 "sws-br-copy-selected-points-in-selected-envelope-to-all-visible-record-armed-envelopes-in-selected-tracks-paste-at-edit-cursor":
2423 54075,
2404 "sws-br-copy-selected-points-in-selected-envelope-to-envelope-at-mouse-cursor": 54078,2424 "sws-br-copy-selected-points-in-selected-envelope-to-envelope-at-mouse-cursor": 54078,
2405 "sws-br-copy-selected-points-in-selected-envelope-to-to-envelope-at-mouse-cursor-paste-at-edit-cursor": 54080,2425 "sws-br-copy-selected-points-in-selected-envelope-to-to-envelope-at-mouse-cursor-paste-at-edit-cursor": 54080,
2406 "sws-br-copy-take-media-source-file-path-of-selected-items-to-clipboard": 54503,2426 "sws-br-copy-take-media-source-file-path-of-selected-items-to-clipboard": 54503,
...@@ -2437,7 +2457,8 @@ export const REAPER_ACTIONS = {...@@ -2437,7 +2457,8 @@ export const REAPER_ACTIONS = {
2437 "sws-br-delete-tempo-marker-preserve-overall-tempo-and-positions-if-possible": 54838,2457 "sws-br-delete-tempo-marker-preserve-overall-tempo-and-positions-if-possible": 54838,
2438 "sws-br-disable-ignore-project-tempo-for-selected-midi-items": 54465,2458 "sws-br-disable-ignore-project-tempo-for-selected-midi-items": 54465,
2439 "sws-br-disable-ignore-project-tempo-for-selected-midi-items-preserving-time-position-of-midi-events": 54466,2459 "sws-br-disable-ignore-project-tempo-for-selected-midi-items-preserving-time-position-of-midi-events": 54466,
2440 "sws-br-enable-ignore-project-tempo-for-selected-midi-items-preserving-time-position-of-midi-events-use-tempo-at-item-s-start": 54468,2460 "sws-br-enable-ignore-project-tempo-for-selected-midi-items-preserving-time-position-of-midi-events-use-tempo-at-item-s-start":
2461 54468,
2441 "sws-br-enable-ignore-project-tempo-for-selected-midi-items-use-tempo-at-item-s-start": 54467,2462 "sws-br-enable-ignore-project-tempo-for-selected-midi-items-use-tempo-at-item-s-start": 54467,
2442 "sws-br-exclusive-toggle-contextual-toolbar-under-mouse-cursor-preset-1": 53979,2463 "sws-br-exclusive-toggle-contextual-toolbar-under-mouse-cursor-preset-1": 53979,
2443 "sws-br-exclusive-toggle-contextual-toolbar-under-mouse-cursor-preset-2": 53984,2464 "sws-br-exclusive-toggle-contextual-toolbar-under-mouse-cursor-preset-2": 53984,
...@@ -2594,12 +2615,16 @@ export const REAPER_ACTIONS = {...@@ -2594,12 +2615,16 @@ export const REAPER_ACTIONS = {
2594 "sws-br-options-toggle-scroll-view-to-edit-cursor-on-stop": 54713,2615 "sws-br-options-toggle-scroll-view-to-edit-cursor-on-stop": 54713,
2595 "sws-br-options-toggle-send-all-notes-off-on-stop-play": 54691,2616 "sws-br-options-toggle-send-all-notes-off-on-stop-play": 54691,
2596 "sws-br-options-toggle-stop-repeat-playback-at-end-of-project": 54712,2617 "sws-br-options-toggle-stop-repeat-playback-at-end-of-project": 54712,
2597 "sws-br-play-from-edit-cursor-position-and-solo-item-and-track-under-mouse-for-the-duration-perform-until-shortcut-released": 54863,2618 "sws-br-play-from-edit-cursor-position-and-solo-item-and-track-under-mouse-for-the-duration-perform-until-shortcut-released":
2598 "sws-br-play-from-edit-cursor-position-and-solo-track-under-mouse-for-the-duration-perform-until-shortcut-released": 54862,2619 54863,
2620 "sws-br-play-from-edit-cursor-position-and-solo-track-under-mouse-for-the-duration-perform-until-shortcut-released":
2621 54862,
2599 "sws-br-play-from-edit-cursor-position-perform-until-shortcut-released": 54861,2622 "sws-br-play-from-edit-cursor-position-perform-until-shortcut-released": 54861,
2600 "sws-br-play-from-mouse-cursor-position": 54459,2623 "sws-br-play-from-mouse-cursor-position": 54459,
2601 "sws-br-play-from-mouse-cursor-position-and-solo-item-and-track-under-mouse-for-the-duration-perform-until-shortcut-released": 54860,2624 "sws-br-play-from-mouse-cursor-position-and-solo-item-and-track-under-mouse-for-the-duration-perform-until-shortcut-released":
2602 "sws-br-play-from-mouse-cursor-position-and-solo-track-under-mouse-for-the-duration-perform-until-shortcut-released": 54859,2625 54860,
2626 "sws-br-play-from-mouse-cursor-position-and-solo-track-under-mouse-for-the-duration-perform-until-shortcut-released":
2627 54859,
2603 "sws-br-play-from-mouse-cursor-position-perform-until-shortcut-released": 54858,2628 "sws-br-play-from-mouse-cursor-position-perform-until-shortcut-released": 54858,
2604 "sws-br-play-pause-from-mouse-cursor-position": 54460,2629 "sws-br-play-pause-from-mouse-cursor-position": 54460,
2605 "sws-br-play-stop-from-mouse-cursor-position": 54461,2630 "sws-br-play-stop-from-mouse-cursor-position": 54461,
...@@ -2608,14 +2633,16 @@ export const REAPER_ACTIONS = {...@@ -2608,14 +2633,16 @@ export const REAPER_ACTIONS = {
2608 "sws-br-preview-media-item-under-mouse-and-pause-during-preview-start-from-mouse-cursor-position": 54726,2633 "sws-br-preview-media-item-under-mouse-and-pause-during-preview-start-from-mouse-cursor-position": 54726,
2609 "sws-br-preview-media-item-under-mouse-at-track-fader-volume": 54727,2634 "sws-br-preview-media-item-under-mouse-at-track-fader-volume": 54727,
2610 "sws-br-preview-media-item-under-mouse-at-track-fader-volume-and-pause-during-preview": 54730,2635 "sws-br-preview-media-item-under-mouse-at-track-fader-volume-and-pause-during-preview": 54730,
2611 "sws-br-preview-media-item-under-mouse-at-track-fader-volume-and-pause-during-preview-start-from-mouse-cursor-position": 54731,2636 "sws-br-preview-media-item-under-mouse-at-track-fader-volume-and-pause-during-preview-start-from-mouse-cursor-position":
2637 54731,
2612 "sws-br-preview-media-item-under-mouse-at-track-fader-volume-start-from-mouse-position": 54728,2638 "sws-br-preview-media-item-under-mouse-at-track-fader-volume-start-from-mouse-position": 54728,
2613 "sws-br-preview-media-item-under-mouse-at-track-fader-volume-sync-with-next-measure": 54729,2639 "sws-br-preview-media-item-under-mouse-at-track-fader-volume-sync-with-next-measure": 54729,
2614 "sws-br-preview-media-item-under-mouse-start-from-mouse-cursor-position": 54723,2640 "sws-br-preview-media-item-under-mouse-start-from-mouse-cursor-position": 54723,
2615 "sws-br-preview-media-item-under-mouse-sync-with-next-measure": 54724,2641 "sws-br-preview-media-item-under-mouse-sync-with-next-measure": 54724,
2616 "sws-br-preview-media-item-under-mouse-through-track": 54732,2642 "sws-br-preview-media-item-under-mouse-through-track": 54732,
2617 "sws-br-preview-media-item-under-mouse-through-track-and-pause-during-preview": 54735,2643 "sws-br-preview-media-item-under-mouse-through-track-and-pause-during-preview": 54735,
2618 "sws-br-preview-media-item-under-mouse-through-track-and-pause-during-preview-start-from-mouse-cursor-position": 54736,2644 "sws-br-preview-media-item-under-mouse-through-track-and-pause-during-preview-start-from-mouse-cursor-position":
2645 54736,
2619 "sws-br-preview-media-item-under-mouse-through-track-start-from-mouse-position": 54733,2646 "sws-br-preview-media-item-under-mouse-through-track-start-from-mouse-position": 54733,
2620 "sws-br-preview-media-item-under-mouse-through-track-sync-with-next-measure": 54734,2647 "sws-br-preview-media-item-under-mouse-through-track-sync-with-next-measure": 54734,
2621 "sws-br-preview-take-under-mouse": 54752,2648 "sws-br-preview-take-under-mouse": 54752,
...@@ -2623,7 +2650,8 @@ export const REAPER_ACTIONS = {...@@ -2623,7 +2650,8 @@ export const REAPER_ACTIONS = {
2623 "sws-br-preview-take-under-mouse-and-pause-during-preview-start-from-mouse-cursor-position": 54756,2650 "sws-br-preview-take-under-mouse-and-pause-during-preview-start-from-mouse-cursor-position": 54756,
2624 "sws-br-preview-take-under-mouse-at-track-fader-volume": 54757,2651 "sws-br-preview-take-under-mouse-at-track-fader-volume": 54757,
2625 "sws-br-preview-take-under-mouse-at-track-fader-volume-and-pause-during-preview": 54760,2652 "sws-br-preview-take-under-mouse-at-track-fader-volume-and-pause-during-preview": 54760,
2626 "sws-br-preview-take-under-mouse-at-track-fader-volume-and-pause-during-preview-start-from-mouse-cursor-position": 54761,2653 "sws-br-preview-take-under-mouse-at-track-fader-volume-and-pause-during-preview-start-from-mouse-cursor-position":
2654 54761,
2627 "sws-br-preview-take-under-mouse-at-track-fader-volume-start-from-mouse-position": 54758,2655 "sws-br-preview-take-under-mouse-at-track-fader-volume-start-from-mouse-position": 54758,
2628 "sws-br-preview-take-under-mouse-at-track-fader-volume-sync-with-next-measure": 54759,2656 "sws-br-preview-take-under-mouse-at-track-fader-volume-sync-with-next-measure": 54759,
2629 "sws-br-preview-take-under-mouse-start-from-mouse-cursor-position": 54753,2657 "sws-br-preview-take-under-mouse-start-from-mouse-cursor-position": 54753,
...@@ -2851,9 +2879,12 @@ export const REAPER_ACTIONS = {...@@ -2851,9 +2879,12 @@ export const REAPER_ACTIONS = {
2851 "sws-br-select-dips-in-envelope": 54058,2879 "sws-br-select-dips-in-envelope": 54058,
2852 "sws-br-select-dips-in-envelope-add-to-selection": 54057,2880 "sws-br-select-dips-in-envelope-add-to-selection": 54057,
2853 "sws-br-select-envelope-at-mouse-cursor": 54094,2881 "sws-br-select-envelope-at-mouse-cursor": 54094,
2854 "sws-br-select-envelope-at-mouse-cursor-and-freehand-draw-envelope-while-snapping-points-to-left-side-grid-line-perform-until-shortcut-released": 54857,2882 "sws-br-select-envelope-at-mouse-cursor-and-freehand-draw-envelope-while-snapping-points-to-left-side-grid-line-perform-until-shortcut-released":
2855 "sws-br-select-envelope-at-mouse-cursor-and-set-closest-envelope-point-s-value-to-mouse-cursor-perform-until-shortcut-released": 54855,2883 54857,
2856 "sws-br-select-envelope-at-mouse-cursor-and-set-closest-left-side-envelope-point-s-value-to-mouse-cursor-perform-until-shortcut-released": 54856,2884 "sws-br-select-envelope-at-mouse-cursor-and-set-closest-envelope-point-s-value-to-mouse-cursor-perform-until-shortcut-released":
2885 54855,
2886 "sws-br-select-envelope-at-mouse-cursor-and-set-closest-left-side-envelope-point-s-value-to-mouse-cursor-perform-until-shortcut-released":
2887 54856,
2857 "sws-br-select-envelope-point-at-mouse-cursor": 54096,2888 "sws-br-select-envelope-point-at-mouse-cursor": 54096,
2858 "sws-br-select-envelope-point-at-mouse-cursor-selected-envelope-only": 54095,2889 "sws-br-select-envelope-point-at-mouse-cursor-selected-envelope-only": 54095,
2859 "sws-br-select-envelope-points-between-grid": 54037,2890 "sws-br-select-envelope-points-between-grid": 54037,
...@@ -2909,14 +2940,16 @@ export const REAPER_ACTIONS = {...@@ -2909,14 +2940,16 @@ export const REAPER_ACTIONS = {
2909 "sws-br-toggle-preview-media-item-under-mouse-and-pause-during-preview-start-from-mouse-cursor-position": 54741,2940 "sws-br-toggle-preview-media-item-under-mouse-and-pause-during-preview-start-from-mouse-cursor-position": 54741,
2910 "sws-br-toggle-preview-media-item-under-mouse-at-track-fader-volume": 54742,2941 "sws-br-toggle-preview-media-item-under-mouse-at-track-fader-volume": 54742,
2911 "sws-br-toggle-preview-media-item-under-mouse-at-track-fader-volume-and-pause-during-preview": 54745,2942 "sws-br-toggle-preview-media-item-under-mouse-at-track-fader-volume-and-pause-during-preview": 54745,
2912 "sws-br-toggle-preview-media-item-under-mouse-at-track-fader-volume-and-pause-during-preview-start-from-mouse-cursor-position": 54746,2943 "sws-br-toggle-preview-media-item-under-mouse-at-track-fader-volume-and-pause-during-preview-start-from-mouse-cursor-position":
2944 54746,
2913 "sws-br-toggle-preview-media-item-under-mouse-at-track-fader-volume-start-from-mouse-position": 54743,2945 "sws-br-toggle-preview-media-item-under-mouse-at-track-fader-volume-start-from-mouse-position": 54743,
2914 "sws-br-toggle-preview-media-item-under-mouse-at-track-fader-volume-sync-with-next-measure": 54744,2946 "sws-br-toggle-preview-media-item-under-mouse-at-track-fader-volume-sync-with-next-measure": 54744,
2915 "sws-br-toggle-preview-media-item-under-mouse-start-from-mouse-position": 54738,2947 "sws-br-toggle-preview-media-item-under-mouse-start-from-mouse-position": 54738,
2916 "sws-br-toggle-preview-media-item-under-mouse-sync-with-next-measure": 54739,2948 "sws-br-toggle-preview-media-item-under-mouse-sync-with-next-measure": 54739,
2917 "sws-br-toggle-preview-media-item-under-mouse-through-track": 54747,2949 "sws-br-toggle-preview-media-item-under-mouse-through-track": 54747,
2918 "sws-br-toggle-preview-media-item-under-mouse-through-track-and-pause-during-preview": 54750,2950 "sws-br-toggle-preview-media-item-under-mouse-through-track-and-pause-during-preview": 54750,
2919 "sws-br-toggle-preview-media-item-under-mouse-through-track-and-pause-during-preview-start-from-mouse-cursor-position": 54751,2951 "sws-br-toggle-preview-media-item-under-mouse-through-track-and-pause-during-preview-start-from-mouse-cursor-position":
2952 54751,
2920 "sws-br-toggle-preview-media-item-under-mouse-through-track-start-from-mouse-position": 54748,2953 "sws-br-toggle-preview-media-item-under-mouse-through-track-start-from-mouse-position": 54748,
2921 "sws-br-toggle-preview-media-item-under-mouse-through-track-sync-with-next-measure": 54749,2954 "sws-br-toggle-preview-media-item-under-mouse-through-track-sync-with-next-measure": 54749,
2922 "sws-br-toggle-preview-take-under-mouse": 54767,2955 "sws-br-toggle-preview-take-under-mouse": 54767,
...@@ -2924,14 +2957,16 @@ export const REAPER_ACTIONS = {...@@ -2924,14 +2957,16 @@ export const REAPER_ACTIONS = {
2924 "sws-br-toggle-preview-take-under-mouse-and-pause-during-preview-start-from-mouse-cursor-position": 54771,2957 "sws-br-toggle-preview-take-under-mouse-and-pause-during-preview-start-from-mouse-cursor-position": 54771,
2925 "sws-br-toggle-preview-take-under-mouse-at-track-fader-volume": 54772,2958 "sws-br-toggle-preview-take-under-mouse-at-track-fader-volume": 54772,
2926 "sws-br-toggle-preview-take-under-mouse-at-track-fader-volume-and-pause-during-preview": 54775,2959 "sws-br-toggle-preview-take-under-mouse-at-track-fader-volume-and-pause-during-preview": 54775,
2927 "sws-br-toggle-preview-take-under-mouse-at-track-fader-volume-and-pause-during-preview-start-from-mouse-cursor-position": 54776,2960 "sws-br-toggle-preview-take-under-mouse-at-track-fader-volume-and-pause-during-preview-start-from-mouse-cursor-position":
2961 54776,
2928 "sws-br-toggle-preview-take-under-mouse-at-track-fader-volume-start-from-mouse-position": 54773,2962 "sws-br-toggle-preview-take-under-mouse-at-track-fader-volume-start-from-mouse-position": 54773,
2929 "sws-br-toggle-preview-take-under-mouse-at-track-fader-volume-sync-with-next-measure": 54774,2963 "sws-br-toggle-preview-take-under-mouse-at-track-fader-volume-sync-with-next-measure": 54774,
2930 "sws-br-toggle-preview-take-under-mouse-start-from-mouse-position": 54768,2964 "sws-br-toggle-preview-take-under-mouse-start-from-mouse-position": 54768,
2931 "sws-br-toggle-preview-take-under-mouse-sync-with-next-measure": 54769,2965 "sws-br-toggle-preview-take-under-mouse-sync-with-next-measure": 54769,
2932 "sws-br-toggle-preview-take-under-mouse-through-track": 54777,2966 "sws-br-toggle-preview-take-under-mouse-through-track": 54777,
2933 "sws-br-toggle-preview-take-under-mouse-through-track-and-pause-during-preview": 54780,2967 "sws-br-toggle-preview-take-under-mouse-through-track-and-pause-during-preview": 54780,
2934 "sws-br-toggle-preview-take-under-mouse-through-track-and-pause-during-preview-start-from-mouse-cursor-position": 54781,2968 "sws-br-toggle-preview-take-under-mouse-through-track-and-pause-during-preview-start-from-mouse-cursor-position":
2969 54781,
2935 "sws-br-toggle-preview-take-under-mouse-through-track-start-from-mouse-position": 54778,2970 "sws-br-toggle-preview-take-under-mouse-through-track-start-from-mouse-position": 54778,
2936 "sws-br-toggle-preview-take-under-mouse-through-track-sync-with-next-measure": 54779,2971 "sws-br-toggle-preview-take-under-mouse-through-track-sync-with-next-measure": 54779,
2937 "sws-br-toggle-show-active-mute-send-envelopes-for-selected-tracks": 54153,2972 "sws-br-toggle-show-active-mute-send-envelopes-for-selected-tracks": 54153,
...@@ -4405,13 +4440,16 @@ export const REAPER_ACTIONS = {...@@ -4405,13 +4440,16 @@ export const REAPER_ACTIONS = {
4405 "sws-wait-for-next-beat-if-playing": 53664,4440 "sws-wait-for-next-beat-if-playing": 53664,
4406 "sws-wait-until-end-of-loop-if-playing": 53665,4441 "sws-wait-until-end-of-loop-if-playing": 53665,
4407 "sws-wol-adjust-envelope-or-track-height-under-mouse-cursor-midi-cc-relative-mousewheel": 54892,4442 "sws-wol-adjust-envelope-or-track-height-under-mouse-cursor-midi-cc-relative-mousewheel": 54892,
4408 "sws-wol-adjust-envelope-or-track-height-under-mouse-cursor-zoom-center-to-mouse-cursor-midi-cc-relative-mousewheel": 54893,4443 "sws-wol-adjust-envelope-or-track-height-under-mouse-cursor-zoom-center-to-mouse-cursor-midi-cc-relative-mousewheel":
4444 54893,
4409 "sws-wol-adjust-selected-envelope-height-midi-cc-relative-mousewheel": 54886,4445 "sws-wol-adjust-selected-envelope-height-midi-cc-relative-mousewheel": 54886,
4410 "sws-wol-adjust-selected-envelope-height-zoom-center-to-middle-arrange-midi-cc-relative-mousewheel": 54887,4446 "sws-wol-adjust-selected-envelope-height-zoom-center-to-middle-arrange-midi-cc-relative-mousewheel": 54887,
4411 "sws-wol-adjust-selected-envelope-height-zoom-center-to-mouse-cursor-midi-cc-relative-mousewheel": 54888,4447 "sws-wol-adjust-selected-envelope-height-zoom-center-to-mouse-cursor-midi-cc-relative-mousewheel": 54888,
4412 "sws-wol-adjust-selected-envelope-or-last-touched-track-height-midi-cc-relative-mousewheel": 54889,4448 "sws-wol-adjust-selected-envelope-or-last-touched-track-height-midi-cc-relative-mousewheel": 54889,
4413 "sws-wol-adjust-selected-envelope-or-last-touched-track-height-zoom-center-to-middle-arrange-midi-cc-relative-mousewheel": 54890,4449 "sws-wol-adjust-selected-envelope-or-last-touched-track-height-zoom-center-to-middle-arrange-midi-cc-relative-mousewheel":
4414 "sws-wol-adjust-selected-envelope-or-last-touched-track-height-zoom-center-to-mouse-cursor-midi-cc-relative-mousewheel": 54891,4450 54890,
4451 "sws-wol-adjust-selected-envelope-or-last-touched-track-height-zoom-center-to-mouse-cursor-midi-cc-relative-mousewheel":
4452 54891,
4415 "sws-wol-apply-height-to-selected-envelope-slot-1": 54912,4453 "sws-wol-apply-height-to-selected-envelope-slot-1": 54912,
4416 "sws-wol-apply-height-to-selected-envelope-slot-2": 54913,4454 "sws-wol-apply-height-to-selected-envelope-slot-2": 54913,
4417 "sws-wol-apply-height-to-selected-envelope-slot-3": 54914,4455 "sws-wol-apply-height-to-selected-envelope-slot-3": 54914,
...@@ -4794,8 +4832,10 @@ export const REAPER_ACTIONS = {...@@ -4794,8 +4832,10 @@ export const REAPER_ACTIONS = {
4794 "toolbars-switch-to-toolbar-9": 41948,4832 "toolbars-switch-to-toolbar-9": 41948,
4795 "track-2nd-pass-render-selected-area-of-tracks-to-mono-post-fader-stem-tracks-and-mute-originals": 42415,4833 "track-2nd-pass-render-selected-area-of-tracks-to-mono-post-fader-stem-tracks-and-mute-originals": 42415,
4796 "track-2nd-pass-render-selected-area-of-tracks-to-mono-stem-tracks-and-mute-originals": 42418,4834 "track-2nd-pass-render-selected-area-of-tracks-to-mono-stem-tracks-and-mute-originals": 42418,
4797 "track-2nd-pass-render-selected-area-of-tracks-to-multichannel-parent-send-only-post-fader-stem-tracks-and-mute-originals": 42593,4835 "track-2nd-pass-render-selected-area-of-tracks-to-multichannel-parent-send-only-post-fader-stem-tracks-and-mute-originals":
4798 "track-2nd-pass-render-selected-area-of-tracks-to-multichannel-parent-send-only-stem-tracks-and-mute-originals": 42594,4836 42593,
4837 "track-2nd-pass-render-selected-area-of-tracks-to-multichannel-parent-send-only-stem-tracks-and-mute-originals":
4838 42594,
4799 "track-2nd-pass-render-selected-area-of-tracks-to-multichannel-post-fader-stem-tracks-and-mute-originals": 42414,4839 "track-2nd-pass-render-selected-area-of-tracks-to-multichannel-post-fader-stem-tracks-and-mute-originals": 42414,
4800 "track-2nd-pass-render-selected-area-of-tracks-to-multichannel-stem-tracks-and-mute-originals": 42417,4840 "track-2nd-pass-render-selected-area-of-tracks-to-multichannel-stem-tracks-and-mute-originals": 42417,
4801 "track-2nd-pass-render-selected-area-of-tracks-to-stereo-post-fader-stem-tracks-and-mute-originals": 42413,4841 "track-2nd-pass-render-selected-area-of-tracks-to-stereo-post-fader-stem-tracks-and-mute-originals": 42413,
...@@ -5012,7 +5052,8 @@ export const REAPER_ACTIONS = {...@@ -5012,7 +5052,8 @@ export const REAPER_ACTIONS = {
5012 "track-rename-last-touched-track": 40696,5052 "track-rename-last-touched-track": 40696,
5013 "track-render-selected-area-of-tracks-to-mono-post-fader-stem-tracks-and-mute-originals": 41718,5053 "track-render-selected-area-of-tracks-to-mono-post-fader-stem-tracks-and-mute-originals": 41718,
5014 "track-render-selected-area-of-tracks-to-mono-stem-tracks-and-mute-originals": 41721,5054 "track-render-selected-area-of-tracks-to-mono-stem-tracks-and-mute-originals": 41721,
5015 "track-render-selected-area-of-tracks-to-multichannel-parent-send-only-post-fader-stem-tracks-and-mute-originals": 42591,5055 "track-render-selected-area-of-tracks-to-multichannel-parent-send-only-post-fader-stem-tracks-and-mute-originals":
5056 42591,
5016 "track-render-selected-area-of-tracks-to-multichannel-parent-send-only-stem-tracks-and-mute-originals": 42592,5057 "track-render-selected-area-of-tracks-to-multichannel-parent-send-only-stem-tracks-and-mute-originals": 42592,
5017 "track-render-selected-area-of-tracks-to-multichannel-post-fader-stem-tracks-and-mute-originals": 41717,5058 "track-render-selected-area-of-tracks-to-multichannel-post-fader-stem-tracks-and-mute-originals": 41717,
5018 "track-render-selected-area-of-tracks-to-multichannel-stem-tracks-and-mute-originals": 41720,5059 "track-render-selected-area-of-tracks-to-multichannel-stem-tracks-and-mute-originals": 41720,
src/Reaper/generate-actions.ts+6-15
...@@ -1,12 +1,5 @@...@@ -1,12 +1,5 @@
1import { execFile } from "node:child_process";1import { execFile } from "node:child_process";
2import {2import { access, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3 access,
4 mkdir,
5 mkdtemp,
6 readFile,
7 rm,
8 writeFile,
9} from "node:fs/promises";
10import { tmpdir } from "node:os";3import { tmpdir } from "node:os";
11import { dirname, join, resolve } from "node:path";4import { dirname, join, resolve } from "node:path";
12import process from "node:process";5import process from "node:process";
...@@ -76,10 +69,10 @@ function parseRawActions(value: unknown): RawReaperAction[] {...@@ -76,10 +69,10 @@ function parseRawActions(value: unknown): RawReaperAction[] {
7669
77 return value.flatMap((entry) => {70 return value.flatMap((entry) => {
78 if (71 if (
79 !entry ||72 !entry
80 typeof entry !== "object" ||73 || typeof entry !== "object"
81 !("commandId" in entry) ||74 || !("commandId" in entry)
82 !("name" in entry)75 || !("name" in entry)
83 ) {76 ) {
84 return [];77 return [];
85 }78 }
...@@ -147,9 +140,7 @@ function renderActionsFile(actions: ReadonlyArray<GeneratedReaperAction>) {...@@ -147,9 +140,7 @@ function renderActionsFile(actions: ReadonlyArray<GeneratedReaperAction>) {
147 "// Source: REAPER main action section via kbd_enumerateActions()/kbd_getTextFromCmd().",140 "// Source: REAPER main action section via kbd_enumerateActions()/kbd_getTextFromCmd().",
148 "",141 "",
149 "export const REAPER_ACTIONS = {",142 "export const REAPER_ACTIONS = {",
150 ...actions.map((action) =>143 ...actions.map((action) => ` ${JSON.stringify(action.actionId)}: ${action.commandId},`),
151 ` ${JSON.stringify(action.actionId)}: ${action.commandId},`
152 ),
153 "} as const satisfies Record<string, number>;",144 "} as const satisfies Record<string, number>;",
154 "",145 "",
155 "export type ReaperActionId = keyof typeof REAPER_ACTIONS;",146 "export type ReaperActionId = keyof typeof REAPER_ACTIONS;",
src/SpeedEditor.ts+22-22
...@@ -1,8 +1,8 @@...@@ -1,8 +1,8 @@
1import { Events } from "@clo/lib/Events.ts";1import { Events } from "@clo/lib/Events.ts";
2import * as log from "@clo/lib/log.ts";
2import { defer } from "@clo/lib/ts.ts";3import { defer } from "@clo/lib/ts.ts";
3import type { Dispose, Timer } from "@clo/lib/ts.ts";4import type { Dispose, Timer } from "@clo/lib/ts.ts";
4import type { Mac } from "./Mac";5import type { Mac } from "./Mac";
5import * as log from "@clo/lib/log.ts";
66
7const SPEED_EDITOR_VENDOR_ID = 0x1edb;7const SPEED_EDITOR_VENDOR_ID = 0x1edb;
8const SPEED_EDITOR_PRODUCT_ID = 0xda0e;8const SPEED_EDITOR_PRODUCT_ID = 0xda0e;
...@@ -215,8 +215,8 @@ export class SpeedEditor extends Events<SpeedEditor.EventMap> {...@@ -215,8 +215,8 @@ export class SpeedEditor extends Events<SpeedEditor.EventMap> {
215 }215 }
216216
217 onDoublePress(key: SpeedEditor.Key, listener: () => void): Dispose {217 onDoublePress(key: SpeedEditor.Key, listener: () => void): Dispose {
218 const listeners = this.#doublePressListeners.get(key) ??218 const listeners = this.#doublePressListeners.get(key)
219 new Set<() => void>();219 ?? new Set<() => void>();
220 listeners.add(listener);220 listeners.add(listener);
221 this.#doublePressListeners.set(key, listeners);221 this.#doublePressListeners.set(key, listeners);
222222
...@@ -236,7 +236,7 @@ export class SpeedEditor extends Events<SpeedEditor.EventMap> {...@@ -236,7 +236,7 @@ export class SpeedEditor extends Events<SpeedEditor.EventMap> {
236 this.#ready = false;236 this.#ready = false;
237 this.#refreshRequested = false;237 this.#refreshRequested = false;
238 this.#stopHotplugMonitoring?.();238 this.#stopHotplugMonitoring?.();
239 this.#stopHotplugMonitoring = null239 this.#stopHotplugMonitoring = null;
240 this.#disconnect(false);240 this.#disconnect(false);
241 this.emit("close");241 this.emit("close");
242 }242 }
...@@ -267,8 +267,8 @@ export class SpeedEditor extends Events<SpeedEditor.EventMap> {...@@ -267,8 +267,8 @@ export class SpeedEditor extends Events<SpeedEditor.EventMap> {
267267
268 #matchesHotplugDevice(device: import("usb").Device) {268 #matchesHotplugDevice(device: import("usb").Device) {
269 const descriptor = device.deviceDescriptor;269 const descriptor = device.deviceDescriptor;
270 return descriptor.idVendor === this.#options.vendorId &&270 return descriptor.idVendor === this.#options.vendorId
271 descriptor.idProduct === this.#options.productId;271 && descriptor.idProduct === this.#options.productId;
272 }272 }
273273
274 async #refreshConnection() {274 async #refreshConnection() {
...@@ -309,8 +309,8 @@ export class SpeedEditor extends Events<SpeedEditor.EventMap> {...@@ -309,8 +309,8 @@ export class SpeedEditor extends Events<SpeedEditor.EventMap> {
309 }309 }
310310
311 const currentKey = getDeviceKey(this.#deviceInfo);311 const currentKey = getDeviceKey(this.#deviceInfo);
312 const currentStillPresent = currentKey !== null &&312 const currentStillPresent = currentKey !== null
313 devices.some((device) => getDeviceKey(device) === currentKey);313 && devices.some((device) => getDeviceKey(device) === currentKey);
314314
315 if (this.#device && !currentStillPresent) {315 if (this.#device && !currentStillPresent) {
316 this.#disconnect(true);316 this.#disconnect(true);
...@@ -330,8 +330,8 @@ export class SpeedEditor extends Events<SpeedEditor.EventMap> {...@@ -330,8 +330,8 @@ export class SpeedEditor extends Events<SpeedEditor.EventMap> {
330330
331 #selectDevice(devices: ReadonlyArray<SpeedEditor.DeviceInfo>) {331 #selectDevice(devices: ReadonlyArray<SpeedEditor.DeviceInfo>) {
332 if (this.#options.path) {332 if (this.#options.path) {
333 return devices.find((device) => device.path === this.#options.path) ??333 return devices.find((device) => device.path === this.#options.path)
334 null;334 ?? null;
335 }335 }
336336
337 return devices[0] ?? null;337 return devices[0] ?? null;
...@@ -611,16 +611,16 @@ export class SpeedEditor extends Events<SpeedEditor.EventMap> {...@@ -611,16 +611,16 @@ export class SpeedEditor extends Events<SpeedEditor.EventMap> {
611 }611 }
612612
613 static camNumbersToNumpad(speededitor: Pick<SpeedEditor, "onPress">, mac: Pick<Mac, "pressKey">) {613 static camNumbersToNumpad(speededitor: Pick<SpeedEditor, "onPress">, mac: Pick<Mac, "pressKey">) {
614 speededitor.onPress("cam1", () => mac.pressKey('numpad1'));614 speededitor.onPress("cam1", () => mac.pressKey("numpad1"));
615 speededitor.onPress("cam2", () => mac.pressKey('numpad2'));615 speededitor.onPress("cam2", () => mac.pressKey("numpad2"));
616 speededitor.onPress("cam3", () => mac.pressKey('numpad3'));616 speededitor.onPress("cam3", () => mac.pressKey("numpad3"));
617 speededitor.onPress("cam4", () => mac.pressKey('numpad4'));617 speededitor.onPress("cam4", () => mac.pressKey("numpad4"));
618 speededitor.onPress("cam5", () => mac.pressKey('numpad5'));618 speededitor.onPress("cam5", () => mac.pressKey("numpad5"));
619 speededitor.onPress("cam6", () => mac.pressKey('numpad6'));619 speededitor.onPress("cam6", () => mac.pressKey("numpad6"));
620 speededitor.onPress("cam7", () => mac.pressKey('numpad7'));620 speededitor.onPress("cam7", () => mac.pressKey("numpad7"));
621 speededitor.onPress("cam8", () => mac.pressKey('numpad8'));621 speededitor.onPress("cam8", () => mac.pressKey("numpad8"));
622 speededitor.onPress("cam9", () => mac.pressKey('numpad9'));622 speededitor.onPress("cam9", () => mac.pressKey("numpad9"));
623 speededitor.onPress("liveOverwrite", () => mac.pressKey('numpad0'));623 speededitor.onPress("liveOverwrite", () => mac.pressKey("numpad0"));
624 }624 }
625}625}
626626
...@@ -909,8 +909,8 @@ function getDeviceKey(device: SpeedEditor.DeviceInfo | null) {...@@ -909,8 +909,8 @@ function getDeviceKey(device: SpeedEditor.DeviceInfo | null) {
909 return null;909 return null;
910 }910 }
911911
912 return device.path ??912 return device.path
913 [913 ?? [
914 device.vendorId,914 device.vendorId,
915 device.productId,915 device.productId,
916 device.serialNumber ?? "",916 device.serialNumber ?? "",
src/config.ts+11-11
...@@ -1,3 +1,5 @@...@@ -1,3 +1,5 @@
1import { Events } from "@clo/lib/Events.ts";
2import * as console from "@clo/lib/log.ts";
1import * as ts from "@clo/lib/ts.ts";3import * as ts from "@clo/lib/ts.ts";
2import type { Dispose } from "@clo/lib/ts.ts";4import type { Dispose } from "@clo/lib/ts.ts";
3import * as fs from "node:fs/promises";5import * as fs from "node:fs/promises";
...@@ -5,8 +7,6 @@ import * as path from "node:path";...@@ -5,8 +7,6 @@ import * as path from "node:path";
5import * as url from "node:url";7import * as url from "node:url";
6import { Mac } from "./Mac.ts";8import { Mac } from "./Mac.ts";
7import { SpeedEditor } from "./SpeedEditor.ts";9import { SpeedEditor } from "./SpeedEditor.ts";
8import { Events } from "@clo/lib/Events.ts";
9import * as console from "@clo/lib/log.ts";
1010
11export interface Configure {11export interface Configure {
12 /** Events are only active when the application is active. */12 /** Events are only active when the application is active. */
...@@ -34,9 +34,9 @@ interface AppDefinition {...@@ -34,9 +34,9 @@ interface AppDefinition {
34}34}
3535
36export type AppEvents = {36export type AppEvents = {
37 "focus": [],37 "focus": [];
38 "blur": [],38 "blur": [];
39}39};
4040
41export function forApp(41export function forApp(
42 bundle: Mac.BundleId,42 bundle: Mac.BundleId,
...@@ -151,8 +151,8 @@ export class AppSpeedEditor extends Events<SpeedEditor.EventMap> {...@@ -151,8 +151,8 @@ export class AppSpeedEditor extends Events<SpeedEditor.EventMap> {
151 onDoublePress(key: SpeedEditor.Key, listener: () => void): Dispose {151 onDoublePress(key: SpeedEditor.Key, listener: () => void): Dispose {
152 const pair = { key, listener };152 const pair = { key, listener };
153 this.#doubleKeyEvents.add(pair);153 this.#doubleKeyEvents.add(pair);
154 this.#enabled &&154 this.#enabled
155 this.#enabledStack.use(this.#source.onDoublePress(key, listener));155 && this.#enabledStack.use(this.#source.onDoublePress(key, listener));
156 return ts.defer(() => {156 return ts.defer(() => {
157 throw new Error("TODO");157 throw new Error("TODO");
158 });158 });
...@@ -199,12 +199,12 @@ export async function runConfigs(dir: string) {...@@ -199,12 +199,12 @@ export async function runConfigs(dir: string) {
199 if (!speededitor.connected) {199 if (!speededitor.connected) {
200 console.warn("Speed Editor is not connected");200 console.warn("Speed Editor is not connected");
201 }201 }
202 speededitor.on('connect', () => {202 speededitor.on("connect", () => {
203 console.info("Speed Editor connected");203 console.info("Speed Editor connected");
204 })204 });
205 speededitor.on('disconnect', () => {205 speededitor.on("disconnect", () => {
206 console.warn("Speed Editor lost connection");206 console.warn("Speed Editor lost connection");
207 })207 });
208208
209 const instances = new Map<Mac.BundleId, AppInstance>();209 const instances = new Map<Mac.BundleId, AppInstance>();
210 const pendingInstances = new Map<Mac.BundleId, Promise<AppInstance | null>>();210 const pendingInstances = new Map<Mac.BundleId, Promise<AppInstance | null>>();