From 24eb8b3b7f41e2c63b76f137544ae767ac0c263c Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 15 Apr 2026 01:06:37 +0200 Subject: [PATCH] refactor(shared-ui): extract search-core for highlight + debounce MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both QuickInputBar (InputBar.svelte), CommandBar.svelte, and GlobalSpotlight were duplicating syntax highlighting and the 150ms search debounce. Pull these into a new `packages/shared-ui/src/search-core/` module so the two input surfaces stay in sync on feel and matching rules. - search-core/highlight.ts — HighlightPattern type, locale-aware getHighlightPatterns(), and the shared highlightText() (HTML-escape + span wrap). Patterns were previously in quick-input/highlightPatterns.ts + inline in CommandBar.svelte. - search-core/config.ts — SEARCH_DEBOUNCE_MS = 150. Used from InputBar, CommandBar, GlobalSpotlight, and apps/mana web SearchEngine. - quick-input/highlightPatterns.ts + types.ts become thin back-compat re-exports. - Public surface: @mana/shared-ui now exports getHighlightPatterns, highlightText, SEARCH_DEBOUNCE_MS, and the HighlightPattern type. No UX change. UIs still live in their own files (per earlier split recommendation: shared backend, separate surfaces). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../apps/web/src/lib/search/engine.svelte.ts | 3 +- .../src/command-bar/CommandBar.svelte | 46 +------ packages/shared-ui/src/index.ts | 12 +- .../src/navigation/GlobalSpotlight.svelte | 3 +- .../shared-ui/src/quick-input/InputBar.svelte | 48 +------ .../src/quick-input/highlightPatterns.ts | 107 +-------------- packages/shared-ui/src/quick-input/types.ts | 7 +- packages/shared-ui/src/search-core/config.ts | 9 ++ .../shared-ui/src/search-core/highlight.ts | 127 ++++++++++++++++++ packages/shared-ui/src/search-core/index.ts | 3 + 10 files changed, 161 insertions(+), 204 deletions(-) create mode 100644 packages/shared-ui/src/search-core/config.ts create mode 100644 packages/shared-ui/src/search-core/highlight.ts create mode 100644 packages/shared-ui/src/search-core/index.ts diff --git a/apps/mana/apps/web/src/lib/search/engine.svelte.ts b/apps/mana/apps/web/src/lib/search/engine.svelte.ts index 0ed7baa3b..d552b0ae3 100644 --- a/apps/mana/apps/web/src/lib/search/engine.svelte.ts +++ b/apps/mana/apps/web/src/lib/search/engine.svelte.ts @@ -6,8 +6,7 @@ import { SearchRegistry } from './registry'; import type { GroupedSearchResults } from './types'; - -const DEBOUNCE_MS = 150; +import { SEARCH_DEBOUNCE_MS as DEBOUNCE_MS } from '@mana/shared-ui'; export function createSearchEngine() { const registry = new SearchRegistry(); diff --git a/packages/shared-ui/src/command-bar/CommandBar.svelte b/packages/shared-ui/src/command-bar/CommandBar.svelte index 9a72b8446..c0162f40c 100644 --- a/packages/shared-ui/src/command-bar/CommandBar.svelte +++ b/packages/shared-ui/src/command-bar/CommandBar.svelte @@ -2,47 +2,9 @@ import { goto } from '$app/navigation'; import type { CommandBarItem, QuickAction, CreatePreview } from './CommandBar.types'; import { Heart, MagnifyingGlass, Plus } from '@mana/shared-icons'; + import { getHighlightPatterns, highlightText, SEARCH_DEBOUNCE_MS } from '../search-core'; - // Syntax highlighting patterns for command keywords - interface HighlightPattern { - pattern: RegExp; - className: string; - } - - const HIGHLIGHT_PATTERNS: HighlightPattern[] = [ - // Priority keywords (Todo) - with specific colors per level - { pattern: /(!{3,}|!?dringend)\b/gi, className: 'hl-priority-urgent' }, - { pattern: /(!{2}|!?wichtig)\b/gi, className: 'hl-priority-high' }, - { pattern: /!?normal\b/gi, className: 'hl-priority-medium' }, - { pattern: /!?sp[aä]ter\b/gi, className: 'hl-priority-low' }, - // Tags - { pattern: /#\w+/g, className: 'hl-tag' }, - // Projects/Calendars/Companies (@reference) - { pattern: /@\w+/g, className: 'hl-reference' }, - // Date keywords - { - pattern: - /\b(heute|morgen|übermorgen|montag|dienstag|mittwoch|donnerstag|freitag|samstag|sonntag|nächsten?\s+\w+|in\s+\d+\s+tagen?)\b/gi, - className: 'hl-date', - }, - // Time patterns - { pattern: /\b(\d{1,2}:\d{2}|um\s+\d{1,2}(\s*uhr)?|\d{1,2}\s*uhr)\b/gi, className: 'hl-time' }, - ]; - - function highlightText(text: string): string { - if (!text) return ''; - - let result = text; - // Escape HTML first - result = result.replace(/&/g, '&').replace(//g, '>'); - - // Apply highlights (process in order, avoiding double-highlighting) - for (const { pattern, className } of HIGHLIGHT_PATTERNS) { - result = result.replace(pattern, (match) => `${match}`); - } - - return result; - } + const HIGHLIGHT_PATTERNS = getHighlightPatterns('de'); interface Props { open: boolean; @@ -89,7 +51,7 @@ ); // Highlighted text for overlay - let highlightedQuery = $derived(highlightText(searchQuery)); + let highlightedQuery = $derived(highlightText(searchQuery, HIGHLIGHT_PATTERNS)); // Check if create option is selected (it's always first when available) let isCreateSelected = $derived(selectedIndex === 0 && createPreview !== null); @@ -126,7 +88,7 @@ } finally { loading = false; } - }, 150); + }, SEARCH_DEBOUNCE_MS); } async function handleCreate() { diff --git a/packages/shared-ui/src/index.ts b/packages/shared-ui/src/index.ts index 40cadac0b..7abfb218b 100644 --- a/packages/shared-ui/src/index.ts +++ b/packages/shared-ui/src/index.ts @@ -185,13 +185,11 @@ export { createInputBarSettingsStore, getInputBarSettingsStore, } from './quick-input'; -export type { - QuickInputItem, - QuickAction, - CreatePreview, - HighlightPattern, - InputBarSettings, -} from './quick-input'; +export type { QuickInputItem, QuickAction, CreatePreview, InputBarSettings } from './quick-input'; + +// Shared search/command core — highlight patterns, debounce, common helpers. +export { getHighlightPatterns, highlightText, SEARCH_DEBOUNCE_MS } from './search-core'; +export type { HighlightPattern } from './search-core'; // Pages export { default as AppsPage } from './pages/AppsPage.svelte'; diff --git a/packages/shared-ui/src/navigation/GlobalSpotlight.svelte b/packages/shared-ui/src/navigation/GlobalSpotlight.svelte index 574cbed14..1c246cabb 100644 --- a/packages/shared-ui/src/navigation/GlobalSpotlight.svelte +++ b/packages/shared-ui/src/navigation/GlobalSpotlight.svelte @@ -7,6 +7,7 @@ ContentSearcher, } from './types'; import { createAppNavigationStore } from './appNavigationStore.svelte'; + import { SEARCH_DEBOUNCE_MS } from '../search-core'; interface Props { open: boolean; @@ -160,7 +161,7 @@ contentLoading = false; } } - }, 150); + }, SEARCH_DEBOUNCE_MS); }); // Group results by category for display diff --git a/packages/shared-ui/src/quick-input/InputBar.svelte b/packages/shared-ui/src/quick-input/InputBar.svelte index b2d8145e9..e6edf99ed 100644 --- a/packages/shared-ui/src/quick-input/InputBar.svelte +++ b/packages/shared-ui/src/quick-input/InputBar.svelte @@ -1,29 +1,15 @@