Back to spacebar for speed dial shortcut. Integrated inline process settings editing. Optimized saerch results

This commit is contained in:
Lam Wei Lun
2026-09-08 16:46:56 +08:00
parent b32f28e712
commit 2dce0ad24f
12 changed files with 1833 additions and 338 deletions
+39
View File
@@ -7,6 +7,10 @@
// Fold per-character so matched offsets stay in ORIGINAL string coordinates (highlighting slices the
// original text; a separately-folded string would desync offsets).
function FoldChar(ch) {
// why: fast path - ASCII is already NFD-stable and diacritic-free, so the normalize/regex below are
// no-ops. This skip is the hot cost in the Speed Dial search (thousands of settings per keystroke).
if (ch.length === 1 && ch.charCodeAt(0) < 0x80)
return ch;
return ch.normalize("NFD").replace(/\p{Diacritic}/gu, ""); // accents always folded
}
@@ -15,6 +19,41 @@ function Norm(ch, caseSensitive) {
return caseSensitive ? folded : folded.toLowerCase(); // case-sensitivity is the only toggle
}
// Pre-normalize a whole haystack with the SAME per-char fold FuzzyRanges uses, so a caller can match
// it repeatedly against one cached string. The fold is 1:1 in length, so indices stay aligned to the
// ORIGINAL text - the highlight ranges that FuzzyRangesNorm returns slice the original correctly.
// Iterate by UTF-16 code unit (not Array.from code point) to mirror FuzzyRanges' own indexing exactly.
function NormText(text, caseSensitive) {
const src = text || "";
let out = "";
for (let i = 0; i < src.length; i++)
out += Norm(src[i], caseSensitive);
return out;
}
// Match a PRE-normalized haystack against a PRE-normalized needle (both produced by NormText with the
// same caseSensitive flag). Skipping the per-character fold makes repeated matching (per keystroke over a
// cached pool) cheap. Returns ranges in original coordinates, or null on no match.
function FuzzyRangesNorm(haystackNorm, needleNorm) {
const t = haystackNorm || "";
const needle = needleNorm || "";
if (!needle)
return null;
const ranges = [];
let qi = 0;
for (let i = 0; i < t.length && qi < needle.length; i++) {
if (t[i] === needle[qi]) {
const last = ranges[ranges.length - 1];
if (last && last[1] === i)
last[1] = i + 1;
else
ranges.push([i, i + 1]);
qi++;
}
}
return qi === needle.length ? ranges : null;
}
function EscapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}