From 3c1c5b2b3ae6fe8bdf576a70acb6b449edaf0197 Mon Sep 17 00:00:00 2001 From: Ku6epXBOCTuK Date: Wed, 26 Aug 2026 12:34:41 +0500 Subject: [PATCH] fix: update search flow - first tool search --- web/src/lib/components/ToolPage.svelte | 2 +- .../lib/components/search/ToolSearch.svelte | 6 +++-- web/src/lib/i18n/search-coverage.test.ts | 24 +++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 web/src/lib/i18n/search-coverage.test.ts diff --git a/web/src/lib/components/ToolPage.svelte b/web/src/lib/components/ToolPage.svelte index 47b0826..9f2966b 100644 --- a/web/src/lib/components/ToolPage.svelte +++ b/web/src/lib/components/ToolPage.svelte @@ -364,7 +364,7 @@ ✕ - applyChainTool(index, id)} /> + applyChainTool(index, id)} chainableOnly /> {:else if getTool(step.toolId)} {@const stepTool = getTool(step.toolId)!} diff --git a/web/src/lib/components/search/ToolSearch.svelte b/web/src/lib/components/search/ToolSearch.svelte index d9f78f7..7d16e65 100644 --- a/web/src/lib/components/search/ToolSearch.svelte +++ b/web/src/lib/components/search/ToolSearch.svelte @@ -9,11 +9,13 @@ interface Props { onSelect: (toolId: string) => void; + /** true — только инструменты, пригодные в шаг цепочки (слот добавления шага). */ + chainableOnly?: boolean; } - let { onSelect }: Props = $props(); + let { onSelect, chainableOnly = false }: Props = $props(); - const candidates = TOOLS.filter(isChainable); + const candidates = chainableOnly ? TOOLS.filter(isChainable) : TOOLS; let query = $state(''); let activeIndex = $state(0); diff --git a/web/src/lib/i18n/search-coverage.test.ts b/web/src/lib/i18n/search-coverage.test.ts new file mode 100644 index 0000000..5138bfa --- /dev/null +++ b/web/src/lib/i18n/search-coverage.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from 'vitest'; +import { TOOLS } from '../registry'; +import { normalizeForSearch, scoreDoc } from './matching'; +import { toolSearchDoc } from './tool-strings'; +import { isChainable } from '../registry'; + +function hits(q: string): string[] { + const nq = normalizeForSearch(q); + return TOOLS.filter((t) => { + const s = scoreDoc(toolSearchDoc(t), nq); + return s !== null && s > 0; + }).map((t) => t.id); +} + +describe('полнота поиска', () => { + it('генератор (не chainable) находится поиском — раньше отфильтровывался', () => { + expect(isChainable(TOOLS.find((t) => t.id === 'color-wheel-png')!)).toBe(false); + expect(hits('color wheel')).toContain('color-wheel-png'); + }); + + it('анализатор-маска тоже ищется', () => { + expect(hits('уникальных цветов')).toContain('unique-color-mask-png'); + }); +});