Phase 2: Find and Rules

Both tabs were rendering Explore. They now take the whole screen, as the
reference does by hiding the unit on each: a list or a page of prose
wants the room more than the panel does. Picking a search result jumps
to Explore with that combo lit, which is the reference's data-go.

The rules text is extracted verbatim rather than retyped or reworded.
Two bugs on the way there, both worth recording:

- RULES is a template literal that itself contains template literals
  inside a ${} interpolation, so the bracket scanner used for the combo
  table stops at the first inner backtick. The nine title and body pairs
  are matched out of the source instead, with a count assertion so drift
  fails loudly rather than silently dropping a rule.
- PADMAP was read with g() but never added to the list of declarations
  evaluated into the vm, so it was simply not in scope.

The extractor now asserts 9 rules and 4 pad-map rows alongside the 159
combos.

141 tests, typecheck clean. Both tabs verified by rendering them, Find
with a seeded query since headless Firefox cannot type.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-17 10:49:50 +03:00
parent b875342864
commit 6d1f219e8a
7 changed files with 467 additions and 1 deletions

90
src/domain/rules.ts Normal file
View File

@@ -0,0 +1,90 @@
// GENERATED from reference/index.html by scripts/extract-reference-data.mjs — do not edit by hand.
// The Rules tab: nine rules, and the SHIFT + pad map.
export interface Rule {
readonly title: string;
readonly body: string;
}
export interface PadMapRow {
readonly label: string;
readonly pads: readonly string[];
}
export const RULES: readonly Rule[] = [
{
"title": "SHIFT + pad is a 4×4 map, row by row",
"body": "Learn the row themes first; the individual pads fall out of them."
},
{
"title": "SHIFT + a playback button = the same thing, bigger or weirder",
"body": "GATE and BPM SYNC apply to the whole bank. LOOP becomes ping-pong. ROLL sets its rate. HOLD pauses. EXIT stops everything. REC in pattern mode becomes TR-REC."
},
{
"title": "SHIFT + an edit button = its sibling screen",
"body": "START/END → markers and chop. PITCH/SPEED → envelope. RESAMPLE → merge. RECORD SETTING → synth. SUB PAD → projects. PATTERN SELECT → undo."
},
{
"title": "REMAIN means “where does it go, and keep it there”",
"body": "With pads, banks or EXT SOURCE it sets bus routing. With an FX button it pins the screen. On edit screens it applies knob moves to the whole bank. Alone it shows pad info."
},
{
"title": "Holding VALUE means “quietly, or only for now”",
"body": "Pads select without sound, FX buttons turn momentary, knobs reach sub-parameters, BUS FX bypasses 3/4 for a moment. Pressing VALUE on any edit screen opens its hidden menu."
},
{
"title": "DEL or COPY + EXIT acts on a whole bank",
"body": "DEL + EXIT deletes a bank, COPY + EXIT copies it. COPY on edit screens scopes changes to the mute group instead of the bank."
},
{
"title": "Three-second holds guard things you do not want by accident",
"body": "SHIFT + REMAIN (live mode) and SHIFT + MARK (save BUS 1/2 effects)."
},
{
"title": "Pattern mode reuses keys by meaning",
"body": "HOLD + pad = chains. SUB PAD + pad = switch now. MARK while recording = FX motion; SHIFT + MARK erases it. DEL while recording = erase notes."
},
{
"title": "Lost? EXIT four times.",
"body": "Stops everything and clears pad mutes. EXIT alone backs out one screen."
}
];
export const PAD_MAP: readonly PadMapRow[] = [
{
"label": "Row 1: how pads play",
"pads": [
"Fixed vel",
"16 vel",
"Cue",
"Chromatic"
]
},
{
"label": "Row 2: organise pads",
"pads": [
"Exchange",
"Init",
"Link",
"Mute group"
]
},
{
"label": "Row 3: time and input",
"pads": [
"Metronome",
"Count-in",
"Tempo / tap",
"Gain"
]
},
{
"label": "Row 4: menus",
"pads": [
"Utility",
"Import/export",
"Pad set",
"EFX set"
]
}
];