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:
@@ -35,7 +35,18 @@ function literalAfter(name) {
|
||||
throw new Error(`unbalanced literal: ${name}`);
|
||||
}
|
||||
|
||||
const NAMES = ["L", "PL", "GROUPS", "GNAME", "NOLATCH", "MODORDER", "RAW", "CTX", "CTXHINT"];
|
||||
const NAMES = [
|
||||
"L",
|
||||
"PL",
|
||||
"GROUPS",
|
||||
"GNAME",
|
||||
"NOLATCH",
|
||||
"MODORDER",
|
||||
"RAW",
|
||||
"CTX",
|
||||
"CTXHINT",
|
||||
"PADMAP",
|
||||
];
|
||||
const ctx = vm.createContext({});
|
||||
for (const n of NAMES) vm.runInContext(`var ${n} = ${literalAfter(n)};`, ctx);
|
||||
// The reference adds the 16 pad labels in a loop right after declaring L.
|
||||
@@ -73,7 +84,34 @@ writeFileSync(
|
||||
`export const CTXHINT: Record<ContextCode, string> = ${j(g("CTXHINT"))};\n`
|
||||
);
|
||||
|
||||
// The nine rules live in a template literal that itself contains template
|
||||
// literals inside a ${...} interpolation, so the bracket scanner above would
|
||||
// stop at the first inner backtick. Match the pairs out of the source instead.
|
||||
const rulePairs = [...src.matchAll(/<h3>(.*?)<\/h3><p>(.*?)<\/p>/gs)].map((m) => ({
|
||||
title: m[1].trim(),
|
||||
body: m[2].trim(),
|
||||
}));
|
||||
if (rulePairs.length !== 9) {
|
||||
throw new Error(`expected 9 rules, found ${rulePairs.length}`);
|
||||
}
|
||||
|
||||
const padMap = g("PADMAP").map(([label, pads]) => ({ label, pads }));
|
||||
if (padMap.length !== 4) {
|
||||
throw new Error(`expected 4 pad-map rows, found ${padMap.length}`);
|
||||
}
|
||||
|
||||
writeFileSync(
|
||||
"src/domain/rules.ts",
|
||||
header("The Rules tab: nine rules, and the SHIFT + pad map.") +
|
||||
`export interface Rule {\n readonly title: string;\n readonly body: string;\n}\n\n` +
|
||||
`export interface PadMapRow {\n readonly label: string;\n readonly pads: readonly string[];\n}\n\n` +
|
||||
`export const RULES: readonly Rule[] = ${j(rulePairs)};\n\n` +
|
||||
`export const PAD_MAP: readonly PadMapRow[] = ${j(padMap)};\n`
|
||||
);
|
||||
|
||||
const raw = g("RAW");
|
||||
console.log(`rules ${rulePairs.length}`);
|
||||
console.log(`pad-map rows ${padMap.length}`);
|
||||
console.log(`RAW entries ${raw.length}`);
|
||||
console.log(`control labels ${Object.keys(g("L")).length}`);
|
||||
console.log(`contexts ${Object.keys(g("CTX")).join(", ")}`);
|
||||
|
||||
Reference in New Issue
Block a user