Files
DeepSeek-TUI/web/lib
Hunter Bown 1f4e0432ca fix(web): highlight search matches using the source string's own indices
Defect: both search surfaces carried an identical `highlight()` that
took `idx` from `text.toLowerCase()` and then sliced `text` with it:

    const lower = text.toLowerCase();
    const idx = lower.indexOf(q);
    text.slice(idx, idx + q.length)

That assumes lowercasing preserves length. It does not — `"İ"` (U+0130,
Turkish dotted capital I) lowercases to two code units, so every index
after one is off by one. Searching "stanbul" in "İstanbul kurulumu"
marks "tanbul " instead of "stanbul": the highlight starts and ends one
character late. Turkish is a routed locale (`tr`), and the search
haystack is where localized copy is headed.

Fix: `highlightSpan()` in lib/search-utils.ts — the module that exists
so the search components' pure logic can be unit-tested — folds case
one character at a time and keeps a position map, so the three returned
pieces are cut at real character boundaries and always reassemble the
input exactly. Both components now call it instead of repeating the
arithmetic.

Scope, honestly: no string in the current haystack contains a
length-changing character, so this is a latent bug rather than an
observed one. It is still wrong output for valid input, and it was
wrong in two places.

Evidence: lib/search-utils.test.ts. Restoring the lowercased-index
implementation fails "keeps indices in the source string when
lowercasing changes length" with `expected 'tanbul ' to be 'stanbul'`.
The last case pins both components onto the shared rule.

npm test 327 passed, npm run lint clean, npx tsc --noEmit clean.

Implemented with agent assistance.
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-20 16:08:38 -07:00
..