From 077d25a2697fa5875833d24cf41ec524a1371909 Mon Sep 17 00:00:00 2001 From: mirivlad Date: Mon, 15 Jun 2026 11:37:35 +0800 Subject: [PATCH] fix: keyboard layout map, picker type tabs as filters, add unit tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Fix RU_TO_EN keyboard map in keyboardLayout.ts: - ч was mapped to 'c' (wrong), now correctly 'x' - с was mapped to 'v' (wrong), now correctly 'c' - ю was mapped to '.' (wrong), now correctly ',' - Full rewrite with standard QWERTY/ЙЦУКЕН positional mapping - Examples: dthcnfr→верстак, руддщ→hello, цщкдв→world all correct now 2. Root cause of search breaking after 3-4 chars: Old map had ч:'c', с:'v' swapped. So dthcnfr → 'верчиак' (wrong). Each character was mapped to wrong Cyrillic equivalent. 3. Add unit tests: keyboardLayout.test.js (39 tests, node-runner): - EN→RU: dthcnfr, ghbdtn, ntcn - RU→EN: руддщ, цщкдв, ышеш - Unicode safety: Latin c (U+0063) ≠ Cyrillic с (U+0441) - expandKeyboardVariants for mixed inputs - Edge cases: empty, single char, mixed case, numbers 4. InternalLinkPicker type tabs → filters (not search modes): - Store rawResults (all) + filtered results by activeType - Switching type tab no longer clears query or triggers new search - Just filters existing rawResults by selected type - Shows 'Нет результатов для этого типа' when filtered empty 5. Both GlobalSearch and InternalLinkPicker use same expandKeyboardVariants() All tests PASS, full build OK. --- .../notes/InternalLinkPicker.svelte | 27 ++-- frontend/src/lib/util/keyboardLayout.test.js | 126 ++++++++++++++++++ frontend/src/lib/util/keyboardLayout.ts | 40 ++++-- 3 files changed, 170 insertions(+), 23 deletions(-) create mode 100644 frontend/src/lib/util/keyboardLayout.test.js diff --git a/frontend/src/lib/components/notes/InternalLinkPicker.svelte b/frontend/src/lib/components/notes/InternalLinkPicker.svelte index 8c49f1e..cb13563 100644 --- a/frontend/src/lib/components/notes/InternalLinkPicker.svelte +++ b/frontend/src/lib/components/notes/InternalLinkPicker.svelte @@ -17,7 +17,8 @@ let activeType = 'note'; let query = ''; - let results = []; + let rawResults = []; // all search results (unfiltered) + let results = []; // filtered by activeType let selectedIndex = 0; let loading = false; let error = ''; @@ -31,8 +32,15 @@ return 'case'; } + // Filter rawResults by activeType into displayed results + function applyTypeFilter() { + results = rawResults.filter(n => nodeTypeToFilter(n.type) === activeType); + selectedIndex = 0; + } + async function search() { if (!query.trim() || query.trim().length < 2) { + rawResults = []; results = []; return; } @@ -41,7 +49,6 @@ try { // Expand query with keyboard layout variants for tolerant search const variants = expandKeyboardVariants(query.trim()); - // Deduplicate: skip variants identical to the original query's lowercase const seen = new Set(); const queries = []; for (const v of variants) { @@ -63,11 +70,11 @@ } } } - // Filter by active type - results = Array.from(merged.values()).filter(n => nodeTypeToFilter(n.type) === activeType); - selectedIndex = 0; + rawResults = Array.from(merged.values()); + applyTypeFilter(); } catch (e) { error = String(e); + rawResults = []; results = []; } finally { loading = false; @@ -104,9 +111,7 @@ function handleTypeChange(typeId) { activeType = typeId; - query = ''; - results = []; - error = ''; + applyTypeFilter(); if (inputRef) inputRef.focus(); } @@ -159,8 +164,10 @@
Загрузка...
{:else if error}
{error}
- {:else if query.trim().length >= 2 && results.length === 0} -
Ничего не найдено
+ {:else if query.trim().length >= 2 && results.length === 0 && !loading} +
+ {rawResults.length > 0 ? 'Нет результатов для этого типа' : 'Ничего не найдено'} +
{:else} {#each results as item, i}