--- phase: 05-search plan: 03 type: execute wave: 2 depends_on: ["05-01", "05-02"] files_modified: - src/routes/+page.svelte - src/lib/components/EntryList.svelte - src/lib/components/EntryCard.svelte - src/lib/stores/recentSearches.ts autonomous: true must_haves: truths: - "User can search entries by typing in search bar" - "User can filter entries using filter controls" - "Search results show matching text highlighted with bold" - "Pinned entries appear in flat list during search/filter (not separated)" - "Empty state shows friendly message when no matches" - "Pressing '/' key focuses search input" - "Recent searches appear as quick picks" - "Clear button resets all filters" artifacts: - path: "src/routes/+page.svelte" provides: "Integrated search UI at page level" contains: "SearchBar" - path: "src/lib/components/EntryList.svelte" provides: "Filtered entry rendering with flat list mode" contains: "filterEntries" - path: "src/lib/components/EntryCard.svelte" provides: "Highlighted text display" contains: "highlightText" - path: "src/lib/stores/recentSearches.ts" provides: "Persisted recent searches store" exports: ["recentSearches", "addRecentSearch"] key_links: - from: "src/routes/+page.svelte" to: "src/lib/components/SearchBar.svelte" via: "component import" pattern: "import SearchBar" - from: "src/lib/components/EntryList.svelte" to: "src/lib/utils/filterEntries.ts" via: "$derived filtering" pattern: "filterEntries.*filters" - from: "src/lib/components/EntryCard.svelte" to: "src/lib/utils/highlightText.ts" via: "{@html} rendering" pattern: "\\{@html.*highlightText" --- Integrate search components, filtering logic, and highlighting into the main page. Purpose: Wire together SearchBar, FilterBar, filterEntries, and highlightText to create a complete search experience. Add recent searches persistence and "/" keyboard shortcut. Output: Fully functional search and filtering on the main entries page. @/home/tho/.claude/get-shit-done/workflows/execute-plan.md @/home/tho/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/05-search/05-CONTEXT.md @.planning/phases/05-search/05-RESEARCH.md @.planning/phases/05-search/05-01-SUMMARY.md @.planning/phases/05-search/05-02-SUMMARY.md @src/routes/+page.svelte @src/lib/components/EntryList.svelte @src/lib/components/EntryCard.svelte Task 1: Create recent searches store and add "/" keyboard shortcut to SearchBar src/lib/stores/recentSearches.ts, src/lib/components/SearchBar.svelte 1. Create src/lib/stores/recentSearches.ts: ```typescript import { persisted } from 'svelte-persisted-store'; const MAX_RECENT = 5; export const recentSearches = persisted('taskplaner-recent-searches', []); export function addRecentSearch(query: string): void { if (!query || query.length < 2) return; recentSearches.update(searches => { // Remove if already exists (will re-add at front) const filtered = searches.filter(s => s.toLowerCase() !== query.toLowerCase()); // Add to front, limit to MAX_RECENT return [query, ...filtered].slice(0, MAX_RECENT); }); } ``` 2. Update SearchBar.svelte to add: - "/" keyboard shortcut using native document.addEventListener in onMount - Skip shortcut if already in input/textarea (check e.target) - e.preventDefault() to prevent "/" from being typed - Recent searches dropdown (show when input focused and empty) - Clicking a recent search fills the input - Call addRecentSearch when search is executed (on blur with value >= 2 chars) Implementation notes for "/" shortcut: - Use native document.addEventListener (NOT svelte:window) to avoid known Svelte 5 bug - Return cleanup function from onMount to remove listener - Check if target is HTMLInputElement or HTMLTextAreaElement before handling Recent searches UI: - Show dropdown below input when: focused AND inputValue is empty AND recentSearches.length > 0 - List shows "Recent searches" header + up to 5 items - onmousedown (not onclick) to fire before onblur Pressing "/" focuses search when not in an input field; recent searches appear on focus Search input has "/" shortcut and shows recent searches dropdown Task 2: Integrate search/filter into EntryList with flat list mode src/lib/components/EntryList.svelte Modify EntryList.svelte to: 1. Accept new props: ```typescript interface Props { entries: EntryWithData[]; availableTags: Tag[]; filters: SearchFilters; // NEW searchQuery: string; // NEW - for highlighting } ``` 2. Apply filtering using $derived: ```typescript import { filterEntries } from '$lib/utils/filterEntries'; import { hasActiveFilters } from '$lib/types/search'; let filteredEntries = $derived(filterEntries(entries, filters)); let isFiltering = $derived(hasActiveFilters(filters)); ``` 3. Modify display logic: - When NOT filtering: Keep current pinned/unpinned separation - When filtering: Flat list (no pinned section), just render all filteredEntries - Pass searchQuery to each EntryCard for highlighting 4. Update empty state: - When no entries at all: "No entries yet" (existing) - When filtering with no results: "No entries match your search" (new) Implementation: ```svelte {#if filteredEntries.length === 0} {#if isFiltering}

No entries match your search

Try adjusting your filters or search term

{:else} {/if} {:else if isFiltering}
{#each filteredEntries as entry (entry.id)} {/each}
{:else} {/if} ```
EntryList filters entries and shows flat list during search EntryList applies filters via $derived and displays flat list when filtering is active
Task 3: Add highlighting to EntryCard and integrate search UI in +page.svelte src/lib/components/EntryCard.svelte, src/routes/+page.svelte 1. Update EntryCard.svelte: - Add new prop: searchQuery: string (default '') - Import highlightText from '$lib/utils/highlightText' - Use {@html highlightText(entry.title, searchQuery)} for title display - Use {@html highlightText(entry.content, searchQuery)} for content preview - Only apply highlighting in collapsed view (where preview text shows) ```typescript interface Props { entry: EntryWithData; availableTags: Tag[]; searchQuery?: string; // NEW, optional with default '' } let { entry, availableTags, searchQuery = '' }: Props = $props(); ``` For title (collapsed view): ```svelte

{@html highlightText(entry.title || 'Untitled', searchQuery)}

``` For content preview (collapsed view): ```svelte

{@html highlightText(entry.content, searchQuery)}

``` 2. Update src/routes/+page.svelte: - Import SearchBar, FilterBar from components - Import SearchFilters, defaultFilters from types/search - Import recentSearches, addRecentSearch from stores - Add filter state with $state(defaultFilters) - Add searchQuery state bound to SearchBar - Add FilterBar with onchange handler - Place SearchBar above EntryList (visible at top, in header or just below) - Place FilterBar below SearchBar - Pass filters and searchQuery to EntryList - Handle onSearch callback to add to recent searches Layout in +page.svelte: ```svelte

TaskPlaner

filters = f} />
```
Full search flow works: type in search, see highlighted results, use filters Search and filters are fully integrated with highlighting in search results
- `npm run check` passes - `npm run dev` starts and search/filter UI is visible - Typing in search filters entries (after 2 chars, debounced) - Matching text is highlighted with bold - Pressing "/" focuses search input (when not already in input) - Recent searches appear on focus - Filters work correctly (type, tags, date range) - Clear button resets all filters - Pinned entries appear in flat list during search (no separation) - Empty state shows "No entries match your search" when filtering yields nothing - Search bar visible at top of page - "/" key focuses search input - Text search filters entries with 2-char minimum and debounce - Recent searches show as quick picks (last 5) - Type filter toggles between All/Tasks/Thoughts - Tag filter with AND logic (multiple tags) - Date range with presets and custom option - Clear button appears when filters active - Matching text highlighted with bold (not background) - Flat list during search (no pinned separation) - Friendly empty state message After completion, create `.planning/phases/05-search/05-03-SUMMARY.md`