ZERO SERVER-SIDE SEARCH - Everything happens in the browser using P2P data.
┌──────────────────────────────────────────┐
│ Browser (Flagship) │
│ │
│ ┌────────────────────────────────────┐ │
│ │ Search UI Component │ │
│ │ (search bar, results display) │ │
│ └────────────┬───────────────────────┘ │
│ │ user query │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ Client-Side Search Engine │ │
│ │ - MiniSearch/Lunr.js index │ │
│ │ - Fuzzy matching │ │
│ │ - Field weights (title > desc) │ │
│ │ - Result ranking │ │
│ └────────────┬───────────────────────┘ │
│ │ search results │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ Local Index (IndexedDB) │ │
│ │ - All catalog metadata │ │
│ │ - Synced from lens-node │ │
│ │ - Offline-capable │ │
│ └────────────────────────────────────┘ │
│ ▲ │
└───────────────┼─────────────────────────┘
│ P2P sync
│
┌──────┴──────┐
│ Lens Node │
│ (Local P2P)│
└─────────────┘
MiniSearch - lightweight, fast, full-text search
- 6.1KB gzipped
- Fuzzy search out of the box
- Field boosting
- Auto-suggestions
- Zero dependencies
interface SearchableContent {
id: string;
title: string; // Boost: 3x
artist?: string; // Boost: 2x
description?: string; // Boost: 1x
category: string; // Exact match
tags?: string[]; // Boost: 1.5x
year?: number;
type: 'music' | 'movie' | 'tv' | 'other';
}- Instant search - Results as you type
- Fuzzy matching - Handles typos
- Category filtering - Music, Movies, TV Shows
- Offline support - Works without network
- Auto-suggestions (Phase 2)
- Recent searches (Phase 2)
- Advanced filters (Phase 3)
packages/renderer/src/components/search/
├── SearchBar.vue # Main search input
├── SearchResults.vue # Results display
├── SearchFilters.vue # Category/type filters
└── useSearch.ts # Composable with search logic
- Index Build: When lens-node data loads, build MiniSearch index
- User Types: Debounced search (300ms) against local index
- Results: Display top 20 results with highlight
- Click: Navigate to content page (existing routes)
cd packages/renderer
pnpm add minisearch// src/composables/useLocalSearch.ts
import MiniSearch from 'minisearch';
import { ref, computed } from 'vue';
export function useLocalSearch() {
const searchIndex = new MiniSearch({
fields: ['title', 'artist', 'description', 'tags'],
storeFields: ['id', 'title', 'artist', 'category', 'type'],
searchOptions: {
boost: { title: 3, artist: 2, tags: 1.5 },
fuzzy: 0.2,
prefix: true
}
});
function indexContent(content: SearchableContent[]) {
searchIndex.addAll(content);
}
function search(query: string, filters?: SearchFilters) {
return searchIndex.search(query, {
filter: (result) => {
if (filters?.category && result.category !== filters.category) {
return false;
}
return true;
}
});
}
return { indexContent, search };
}<template>
<v-text-field
v-model="query"
label="Search music, movies, TV shows..."
prepend-inner-icon="mdi-magnify"
clearable
@update:model-value="onSearch"
/>
<SearchResults v-if="results.length" :results="results" />
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { useDebounceFn } from '@vueuse/core';
import { useLocalSearch } from '/@/composables/useLocalSearch';
const { search } = useLocalSearch();
const query = ref('');
const results = ref([]);
const onSearch = useDebounceFn((value: string) => {
if (!value || value.length < 2) {
results.value = [];
return;
}
results.value = search(value).slice(0, 20);
}, 300);
</script>- Index build: < 500ms for 10,000 items
- Search latency: < 50ms for typical queries
- Memory usage: < 10MB for index
- Offline: 100% functional without network
✅ User can search entire catalog from browser ✅ Results appear instantly (< 100ms perceived) ✅ Works offline from cached data ✅ No server-side dependencies ✅ Fuzzy search handles typos
- Voice search
- Natural language queries ("80s rock music")
- Semantic search (ML embeddings)
- Collaborative filtering ("Users who liked X also searched for Y")
- Search analytics (local only, privacy-preserving)