Phase 2: label formatting and the Find search

format.ts turns ids into the words printed on the panel: controlName,
targetName (a group reads "PAD 1-16" rather than sixteen names), and
comboParts/comboText for the "SHIFT + PAD 1" lines.

search.ts matches a query against everything printed about an entry -
name, panel label, description, modifiers, target and contexts - so
"metronome", "SHIFT" and "sub pad" all find something. An empty query
returns nothing, not everything: Find opens on a prompt.

One test was wrong rather than the code. Searching "dj mode" does not
only match the context name: the entry named "Enter DJ mode" lives in
sample mode, because that is where you press it. Asserting every result
was in context D was too strong, and surfacing that combo is the useful
behaviour. The assertion now documents it.

87 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-17 00:55:31 +03:00
parent b32c38019e
commit 43fb51396c
4 changed files with 208 additions and 0 deletions

34
src/domain/format.ts Normal file
View File

@@ -0,0 +1,34 @@
/** Turning ids into the words printed on the panel and in combo lines. */
import { CTX, GNAME, GROUPS, L } from "./labels";
import type { ContextCode, ControlId, Entry, GroupId, RawEntry } from "./types";
const GROUP_IDS: ReadonlySet<string> = new Set(Object.keys(GROUPS));
/** Name of a single control: `"shift"` becomes `"SHIFT"`. */
export function controlName(id: ControlId): string {
return L[id] ?? id;
}
/**
* Name of whatever an entry targets: one control, a group ("PAD 1–16"), or an
* explicit list joined with a slash.
*/
export function targetName(target: RawEntry[1]): string {
if (Array.isArray(target)) return target.map(controlName).join(" / ");
if (GROUP_IDS.has(target)) return GNAME[target as GroupId];
return controlName(target);
}
export function contextNames(ctx: readonly ContextCode[]): string[] {
return ctx.map((c) => CTX[c]);
}
/** The combo as its parts: modifiers first, then the target. */
export function comboParts(entry: Entry): string[] {
return [...entry.mods.map(controlName), targetName(entry.target)];
}
/** The combo as one line: `"SHIFT + PAD 1"`. */
export function comboText(entry: Entry): string {
return comboParts(entry).join(" + ");
}