authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-07-05 13:00:12-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-07-05 13:01:04-07:00
logdf4a98ec1ad2493896b0c50e5abf5f95402fa8e1
treefd028a49f6d9625c2b9290db794b8d51fecb5e10
parentefa2473ccc8ba2de27d5d8a7a0f9e62c3968deb5
signaturebadge-check Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

fix recording button


2 files changed, 25 insertions(+), 28 deletions(-)

control/config/reaper.ts+5-4
...@@ -30,12 +30,13 @@ export default config.forApp(...@@ -30,12 +30,13 @@ export default config.forApp(
30 const recording = keypad.overlay((overlay) => {30 const recording = keypad.overlay((overlay) => {
31 overlay.key("down-left", lucide("Save").fg("green"), () => {31 overlay.key("down-left", lucide("Save").fg("green"), () => {
32 reaper.runAction("transport-stop-save-all-recorded-media");32 reaper.runAction("transport-stop-save-all-recorded-media");
33 reaper.runAction("transport-record");
33 });34 });
34 overlay.key("down", lucide("Redo").fg("green"), () => {35 overlay.key("down", lucide("Undo").fg("red"), () => {
35 reaper.runAction("transport-stop-save-all-recorded-media");36 reaper.runAction("transport-stop-delete-all-recorded-media");
36 reaper.runAction("transport-record");37 reaper.runAction("transport-record");
37 });38 });
38 overlay.key("down-right", lucide("Trash").fg("#ff6b6b"), () => {39 overlay.key("down-right", lucide("Trash").fg("red"), () => {
39 reaper.runAction("transport-stop-delete-all-recorded-media");40 reaper.runAction("transport-stop-delete-all-recorded-media");
40 });41 });
41 });42 });
...@@ -67,7 +68,7 @@ export default config.forApp(...@@ -67,7 +68,7 @@ export default config.forApp(
67 });68 });
6869
69 dialpad.on("rotate", (delta) => {70 dialpad.on("rotate", (delta) => {
70 console.log({delta})71 console.log({ delta });
71 reaper.runAction(72 reaper.runAction(
72 delta > 073 delta > 0
73 ? "tempo-increase-current-project-tempo-0-1-bpm"74 ? "tempo-increase-current-project-tempo-0-1-bpm"
control/src/icons.ts+20-24
...@@ -9,8 +9,8 @@...@@ -9,8 +9,8 @@
9//9//
10// The result's `.svg` is what KeypadUI rasterizes to JPEG. Everything here is10// The result's `.svg` is what KeypadUI rasterizes to JPEG. Everything here is
11// pure string building — no build step, no runtime SVG parsing beyond a trim.11// pure string building — no build step, no runtime SVG parsing beyond a trim.
12import type { MdiIcon } from "mdi-ts";
13import * as lucideIcons from "lucide-static";12import * as lucideIcons from "lucide-static";
13import type { MdiIcon } from "mdi-ts";
14import { readFileSync } from "node:fs";14import { readFileSync } from "node:fs";
15import { createRequire } from "node:module";15import { createRequire } from "node:module";
16import { dirname, join } from "node:path";16import { dirname, join } from "node:path";
...@@ -33,7 +33,7 @@ const colorMap = {...@@ -33,7 +33,7 @@ const colorMap = {
33 white: "#ffffff",33 white: "#ffffff",
34 grey: "#8a8a8a",34 grey: "#8a8a8a",
35 gray: "#8a8a8a",35 gray: "#8a8a8a",
36 red: "#ff5a36",36 red: "#ff6b6b",
37 orange: "#ff9f43",37 orange: "#ff9f43",
38 amber: "#ffbf47",38 amber: "#ffbf47",
39 yellow: "#ffd23f",39 yellow: "#ffd23f",
...@@ -137,8 +137,7 @@ export class Icon implements Svg {...@@ -137,8 +137,7 @@ export class Icon implements Svg {
137 return glyph(lucideInner(this.#source.name), this.#glyphSize(), {137 return glyph(lucideInner(this.#source.name), this.#glyphSize(), {
138 fill: "none",138 fill: "none",
139 stroke: fg,139 stroke: fg,
140 extra:140 extra: `stroke-width="2" stroke-linecap="round" stroke-linejoin="round"`,
141 `stroke-width="2" stroke-linecap="round" stroke-linejoin="round"`,
142 });141 });
143 case "mdi":142 case "mdi":
144 return glyph(mdiInner(this.#source.name), this.#glyphSize(), {143 return glyph(mdiInner(this.#source.name), this.#glyphSize(), {
...@@ -156,8 +155,8 @@ export class Icon implements Svg {...@@ -156,8 +155,8 @@ export class Icon implements Svg {
156 fg,155 fg,
157 );156 );
158 case "blank":157 case "blank":
159 return `<circle cx="${KEY_SIZE / 2}" cy="${KEY_SIZE / 2}" r="7" ` +158 return `<circle cx="${KEY_SIZE / 2}" cy="${KEY_SIZE / 2}" r="7" `
160 `fill="${BLANK_DOT}"/>`;159 + `fill="${BLANK_DOT}"/>`;
161 }160 }
162 }161 }
163162
...@@ -232,8 +231,8 @@ export const blank: Icon = new Icon({ kind: "blank" }, BLANK_BG);...@@ -232,8 +231,8 @@ export const blank: Icon = new Icon({ kind: "blank" }, BLANK_BG);
232231
233/** Wrap inner markup in a full key-sized document with a solid background. */232/** Wrap inner markup in a full key-sized document with a solid background. */
234function wrapSvg(inner: string, bg: string): string {233function wrapSvg(inner: string, bg: string): string {
235 return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${KEY_SIZE} ${KEY_SIZE}">` +234 return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${KEY_SIZE} ${KEY_SIZE}">`
236 `<rect width="${KEY_SIZE}" height="${KEY_SIZE}" fill="${bg}"/>${inner}</svg>`;235 + `<rect width="${KEY_SIZE}" height="${KEY_SIZE}" fill="${bg}"/>${inner}</svg>`;
237}236}
238237
239interface GlyphStyle {238interface GlyphStyle {
...@@ -247,8 +246,8 @@ function glyph(inner: string, size: number, style: GlyphStyle): string {...@@ -247,8 +246,8 @@ function glyph(inner: string, size: number, style: GlyphStyle): string {
247 const offset = (KEY_SIZE - size) / 2;246 const offset = (KEY_SIZE - size) / 2;
248 const scale = size / 24;247 const scale = size / 24;
249 const extra = style.extra ? ` ${style.extra}` : "";248 const extra = style.extra ? ` ${style.extra}` : "";
250 return `<g transform="translate(${offset} ${offset}) scale(${scale})" ` +249 return `<g transform="translate(${offset} ${offset}) scale(${scale})" `
251 `fill="${style.fill}" stroke="${style.stroke}"${extra}>${inner}</g>`;250 + `fill="${style.fill}" stroke="${style.stroke}"${extra}>${inner}</g>`;
252}251}
253252
254const SANS_FONT = "Helvetica, Arial, sans-serif";253const SANS_FONT = "Helvetica, Arial, sans-serif";
...@@ -276,9 +275,9 @@ function textAt(...@@ -276,9 +275,9 @@ function textAt(
276): string {275): string {
277 const family = style.family ?? SANS_FONT;276 const family = style.family ?? SANS_FONT;
278 const weight = style.weight ?? 600;277 const weight = style.weight ?? 600;
279 return `<text x="${x}" y="${y}" fill="${fg}" font-family="${family}" ` +278 return `<text x="${x}" y="${y}" fill="${fg}" font-family="${family}" `
280 `font-size="${size}" font-weight="${weight}" text-anchor="middle" ` +279 + `font-size="${size}" font-weight="${weight}" text-anchor="middle" `
281 `dominant-baseline="central">${escapeXml(label)}</text>`;280 + `dominant-baseline="central">${escapeXml(label)}</text>`;
282}281}
283282
284function text(label: string, fg: string, size: number): string {283function text(label: string, fg: string, size: number): string {
...@@ -291,11 +290,9 @@ const STACK_GAP = 6;...@@ -291,11 +290,9 @@ const STACK_GAP = 6;
291290
292/** Lay text lines out vertically, centered as a group. */291/** Lay text lines out vertically, centered as a group. */
293function stackInner(lines: readonly StackSpec[], fg: string): string {292function stackInner(lines: readonly StackSpec[], fg: string): string {
294 const sizes = lines.map((line, index) =>293 const sizes = lines.map((line, index) => line.size ?? (index === 0 ? STACK_PRIMARY : STACK_SECONDARY));
295 line.size ?? (index === 0 ? STACK_PRIMARY : STACK_SECONDARY)294 const height = sizes.reduce((sum, size) => sum + size, 0)
296 );295 + STACK_GAP * Math.max(0, lines.length - 1);
297 const height = sizes.reduce((sum, size) => sum + size, 0) +
298 STACK_GAP * Math.max(0, lines.length - 1);
299 let top = (KEY_SIZE - height) / 2;296 let top = (KEY_SIZE - height) / 2;
300297
301 return lines298 return lines
...@@ -330,15 +327,14 @@ function timeSignatureInner(...@@ -330,15 +327,14 @@ function timeSignatureInner(
330 let staff = "";327 let staff = "";
331 for (let i = 0; i < STAFF_LINES; i += 1) {328 for (let i = 0; i < STAFF_LINES; i += 1) {
332 const y = top + i * STAFF_GAP;329 const y = top + i * STAFF_GAP;
333 staff += `<line x1="${STAFF_INSET}" y1="${y}" x2="${KEY_SIZE - STAFF_INSET}" ` +330 staff += `<line x1="${STAFF_INSET}" y1="${y}" x2="${KEY_SIZE - STAFF_INSET}" `
334 `y2="${y}" stroke="${fg}" stroke-width="1.5" opacity="0.4"/>`;331 + `y2="${y}" stroke="${fg}" stroke-width="1.5" opacity="0.4"/>`;
335 }332 }
336 const style: TextStyle = { family: SERIF_FONT, weight: 700 };333 const style: TextStyle = { family: SERIF_FONT, weight: 700 };
337 const numeral = TIMESIG_GLYPH;334 const numeral = TIMESIG_GLYPH;
338 const middle = KEY_SIZE / 2;335 const middle = KEY_SIZE / 2;
339 const glyphs =336 const glyphs = textAt(KEY_SIZE / 2, middle - TIMESIG_STACK_OFFSET, numerator, fg, numeral, style)
340 textAt(KEY_SIZE / 2, middle - TIMESIG_STACK_OFFSET, numerator, fg, numeral, style) +337 + textAt(KEY_SIZE / 2, middle + TIMESIG_STACK_OFFSET, denominator, fg, numeral, style);
341 textAt(KEY_SIZE / 2, middle + TIMESIG_STACK_OFFSET, denominator, fg, numeral, style);
342 return staff + glyphs;338 return staff + glyphs;
343}339}
344340
...@@ -350,7 +346,7 @@ function escapeXml(value: string): string {...@@ -350,7 +346,7 @@ function escapeXml(value: string): string {
350 ? "&gt;"346 ? "&gt;"
351 : char === "&"347 : char === "&"
352 ? "&amp;"348 ? "&amp;"
353 : char === '"'349 : char === "\""
354 ? "&quot;"350 ? "&quot;"
355 : "&apos;");351 : "&apos;");
356}352}