// What the command palette calls a match, and in what order it puts the two // kinds of row (client/paletteRank.ts). // // The regression this file holds the line on has a name and a query: v1.8's UX // audit, finding F18. Typing "sort" put *Design your site* above every note in // the vault, because the matcher rejected only a MISSING character and the // command block was concatenated whole on top of the notes. Every case below is // either that bug, one of the good matches the fix must not throw out with it, // or the placement rule that decides which kind of row comes first. import assert from "node:assert/strict"; import { describe, it } from "node:test"; import { BODY_ONLY_SCORE, COMMAND_FLOOR, MAX_COMMAND_ROWS, commandCut, fuzzyMatch, noteScore, normalize, rankCommands, } from "../client/paletteRank.ts"; import { CHROME_LANG_ALIASES, chromeLangSwitch } from "../client/chromeLangSwitch.ts"; /** The normalized score of `query` against `text` — what the floor is compared * to — or null when there is no subsequence at all. */ function q(query: string, text: string): number | null { const hit = fuzzyMatch(query, text); return hit === null ? null : normalize(hit.score, query.length); } interface Row { label: string; hint?: string; } const ROWS: Row[] = [ { label: "New note", hint: "create" }, { label: "New folder", hint: "create" }, { label: "Open daily note", hint: "daily/2026-08-25.md" }, { label: "Toggle graph", hint: "view" }, { label: "Design your site", hint: "sections · presets · themes" }, { label: "Moderate comments", hint: "marginalia" }, { label: "Open trash", hint: "restore a deleted note" }, { label: "Keyboard shortcuts", hint: "Ctrl/Cmd /" }, { label: "Reveal note in sidebar", hint: "opens its folders" }, { label: "Duplicate note", hint: "a copy beside it" }, ]; describe("paletteRank: the floor", () => { it("F18: 'sort' does not match 'Design your site'", () => { // The subsequence IS there (de·S·ign y·O·u·R· si·T·e) — that was the bug. assert.notEqual(fuzzyMatch("sort", "Design your site"), null); const score = q("sort", "Design your site")!; assert.ok(score < COMMAND_FLOOR, `scored ${score}, floor is ${COMMAND_FLOOR}`); }); it("keeps a contiguous run at a word start well above the floor", () => { for (const [query, text] of [ ["trash", "Open trash"], ["graph", "Toggle graph"], ["note", "New note"], ["duplicate", "Duplicate note"], ] as const) { const score = q(query, text)!; assert.ok(score >= 7, `${query} → ${text} scored ${score}`); } }); it("keeps an acronym over word starts above the floor", () => { for (const [query, text] of [ ["tg", "Toggle graph"], ["dn", "Duplicate note"], ["ot", "Open trash"], ] as const) { const score = q(query, text)!; assert.ok(score >= COMMAND_FLOOR, `${query} → ${text} scored ${score}`); } }); it("keeps a contiguous run that starts mid-word", () => { // "raph" is inside "graph": no word-start bonus on the first character, // three consecutive ones after it. assert.ok(q("raph", "Toggle graph")! >= COMMAND_FLOOR); }); it("the floor sits in the measured gap, and these are the measurements", () => { // The numbers written into COMMAND_FLOOR's own comment. If a bonus is ever // retuned, this is the test that says which way the floor has to move. const cases: [string, string, number][] = [ ["sort", "Design your site", -0.75], // the F18 trap ["note", "Design your site", 1.5], ["tg", "Toggle graph", 3.5], // the weakest match anyone MEANT ["pane", "Split pane", 7.75], // a contiguous run at a word start ]; for (const [query, text, want] of cases) { assert.equal(q(query, text), want, `${query} → ${text}`); } assert.ok(1.5 < COMMAND_FLOOR && COMMAND_FLOOR < 3.5, "the floor is in the gap"); }); it("normalize is per character, so a floor means the same at any length", () => { assert.equal(normalize(31, 4), 7.75); assert.equal(normalize(0, 0), 0); // never divides by zero }); }); describe("paletteRank: fuzzyMatch anchors", () => { it("F18: tries every anchor, so 'note' finds the WORD in 'Design Notes'", () => { // The greedy single pass latched onto the `n` of "design" and scored 3.25; // the word two characters later is worth more than twice that. const hit = fuzzyMatch("note", "Design Notes")!; assert.deepEqual(hit.indices, [7, 8, 9, 10]); assert.ok(normalize(hit.score, 4) >= 7); }); it("highlights the run the score was paid for", () => { const hit = fuzzyMatch("sort", "Keyboard shortcuts")!; // s-h-o-r-t: the `s` of "shortcuts", then ortc… — contiguous from index 9. assert.equal(hit.indices[0], 9); }); it("returns null only when a character is genuinely absent", () => { assert.equal(fuzzyMatch("zebra", "Open trash"), null); assert.equal(fuzzyMatch("nnn", "New note"), null); // only two n's }); it("is case-insensitive and total over the empty query", () => { assert.notEqual(fuzzyMatch("NOTE", "new note"), null); assert.deepEqual(fuzzyMatch("", "anything"), { score: 0, indices: [] }); }); it("folds nothing — Arabic labels match their own script", () => { const hit = fuzzyMatch("لوح", "طي لوحة الملاحظات"); assert.notEqual(hit, null); assert.ok(normalize(hit!.score, 3) >= COMMAND_FLOOR); }); }); describe("paletteRank: rankCommands", () => { // The module's own ranking, exercised through the same shape the palette // hands it (label + optional hint thunks, already evaluated). const rank = (query: string) => rankCommands(query, ROWS, (r) => ({ label: r.label, hint: r.hint })); it("F18: 'sort' returns no command row from a table that contains the trap", () => { const out = rank("sort"); assert.deepEqual(out.map((r) => r.command.label), ["Keyboard shortcuts"]); }); it("caps the list at five, keeping the best five", () => { const out = rank("note"); assert.ok(out.length <= MAX_COMMAND_ROWS, `${out.length} rows`); // Sorted descending, and a sub-floor score can only belong to a demoted // HINT hit — which is the invariant the two-haystack rule rests on. for (let i = 1; i < out.length; i++) assert.ok(out[i - 1].score >= out[i].score); assert.ok(out.every((r) => r.score >= COMMAND_FLOOR || r.indices.length === 0)); }); it("finds a row by its HINT, and ranks it below every label hit", () => { const out = rank("marginalia"); const row = out.find((r) => r.command.label === "Moderate comments"); assert.ok(row, "the hint is visible row text and must stay searchable"); assert.deepEqual(row!.indices, [], "highlight indices index the LABEL, not the hint"); assert.ok(row!.score < 0, "a hint hit is demoted below every label hit"); }); it("a label hit wins over the same row's hint hit", () => { // "create" is the hint of both New note and New folder; "new" is in both // labels. The label spelling must not be dragged down by the hint's. const out = rank("new"); assert.ok(out.every((r) => r.score > 0)); }); it("returns nothing for a query nothing matches", () => { assert.deepEqual(rank("zzzz"), []); }); }); describe("paletteRank: where the command block lands", () => { it("a note whose TITLE is what was typed outranks the commands", () => { // "sort" against the vault: the note wins outright, so the cut is 1 and the // command block falls below it. const best = noteScore("sort", "Keyboard shortcuts") - 1; // any weaker command assert.equal(commandCut("sort", ["Sorting Algorithms", "Welcome"], best), 1); }); it("a body-only hit never outranks a command that cleared the floor", () => { // "Welcome" does not contain the letters of "trash" in order → body-only. assert.equal(noteScore("trash", "Welcome"), BODY_ONLY_SCORE); assert.ok(BODY_ONLY_SCORE < COMMAND_FLOOR); assert.equal(commandCut("trash", ["Welcome"], COMMAND_FLOOR), 0); }); it("an exact tie goes to the command", () => { const score = noteScore("note", "New note"); assert.equal(commandCut("note", ["New note"], score), 0); }); it("with no command rows at all, every note stays where the server put it", () => { const titles = ["A", "B", "C"]; assert.equal(commandCut("a", titles, -Infinity), titles.length); }); it("stops at the first note that loses, keeping each kind one run", () => { // Whatever the scores, the answer is a PREFIX length: the command block is // never split across the note list. const titles = ["Sorting Algorithms", "Welcome", "Sorting notes"]; const cut = commandCut("sort", titles, COMMAND_FLOOR + 1); assert.ok(cut >= 0 && cut <= titles.length); assert.ok( titles.slice(0, cut).every((tt) => noteScore("sort", tt) > COMMAND_FLOOR + 1), "every note before the cut outranks the best command", ); }); }); // THE WAY BACK is found in either language, whichever way it points: a reader // stuck in the wrong chrome types the name of the language they want, or the // one they are stuck in, in the script they have. The rows around it are the // real neighbours that share its words (the three editor-language rows). describe("the chrome-language row", () => { type LangRow = { id: string; label: string; hint?: string; aliases?: readonly string[] }; const table = (lang: "en" | "ar"): LangRow[] => [ { id: "chrome-lang-toggle", label: chromeLangSwitch(lang).row, hint: "Ctrl/Cmd Alt Shift L", aliases: CHROME_LANG_ALIASES }, ...(lang === "en" ? [ { id: "editor-lang-en", label: "Editor language: English", hint: "this browser" }, { id: "editor-lang-ar", label: "Editor language: العربية", hint: "this browser" }, { id: "toggle-graph", label: "Toggle graph", hint: "view" }, ] : [ { id: "editor-lang-en", label: "لغة المحرّر: الإنجليزية", hint: "هذا المتصفح" }, { id: "editor-lang-ar", label: "لغة المحرّر: العربية", hint: "هذا المتصفح" }, { id: "toggle-graph", label: "عرض المخطط", hint: "عرض" }, ]), ]; for (const lang of ["en", "ar"] as const) { for (const query of ["arabic", "عربي", "english", "إنجليزي"]) { it(`${lang} chrome: "${query}" finds it`, () => { const ranked = rankCommands(query, table(lang), (r) => ({ label: r.label, hint: r.hint, aliases: r.aliases })); assert.ok(ranked.some((r) => r.command.id === "chrome-lang-toggle"), ranked.map((r) => r.command.id).join(", ")); }); } } it("an alias hit ranks under a label hit, like a hint hit", () => { const ranked = rankCommands("english", table("en"), (r) => ({ label: r.label, hint: r.hint, aliases: r.aliases })); assert.equal(ranked[0].command.id, "editor-lang-en"); }); it("an alias the query does not spell is no match", () => { const ranked = rankCommands("graph", table("en"), (r) => ({ label: r.label, hint: r.hint, aliases: r.aliases })); assert.ok(!ranked.some((r) => r.command.id === "chrome-lang-toggle")); }); });