feat: replace hardcoded protocol filters with searchable 217-language dropdown
Author: Aaron Steven White
Commit
38eb4efa08ade58bdd49ca5c05f17cf20e53d567Parent: 69ab7395b1
Structural diff unavailable
These commits were pushed via plain git push, so no pre-parsed
schemas are available. Install git-remote-cospan and re-push via panproto:// to
see scope-level changes, breaking change detection, and semantic diffs.
brew install panproto/tap/git-remote-cospan1 file changed +78 -31
@@ -1,20 +1,38 @@
11 <script lang="ts"> 22 import RepoCard from '$lib/components/repo/RepoCard.svelte'; 3+ import { ALL_LANGUAGES } from '$lib/data/languages'; 4+ import { goto } from '$app/navigation'; 35 46 let { data } = $props(); 57 6- const protocols: { value: string; label: string; color: string }[] = [ 7- { value: 'typescript', label: 'TypeScript', color: 'oklch(0.65 0.15 230)' }, 8- { value: 'python', label: 'Python', color: 'oklch(0.65 0.15 90)' }, 9- { value: 'rust', label: 'Rust', color: 'oklch(0.60 0.18 30)' }, 10- { value: 'go', label: 'Go', color: 'oklch(0.65 0.15 195)' }, 11- { value: 'protobuf', label: 'Protobuf', color: 'oklch(0.65 0.12 145)' }, 12- { value: 'graphql', label: 'GraphQL', color: 'oklch(0.55 0.20 330)' }, 13- { value: 'sql', label: 'SQL', color: 'oklch(0.65 0.10 260)' }, 14- { value: 'atproto_lexicon', label: 'ATProto', color: 'oklch(0.65 0.15 250)' }, 15- ]; 16- 178 let activeProtocol = $derived(data.protocol); 9+ let searchQuery = $state(''); 10+ let showDropdown = $state(false); 11+ 12+ let filtered = $derived( 13+ searchQuery.trim() 14+ ? ALL_LANGUAGES.filter((l) => 15+ l.label.toLowerCase().includes(searchQuery.toLowerCase()) || 16+ l.value.toLowerCase().includes(searchQuery.toLowerCase()) 17+ ).slice(0, 20) 18+ : [] 19+ ); 20+ 21+ function selectProtocol(value: string) { 22+ showDropdown = false; 23+ searchQuery = ''; 24+ goto(`/?protocol=${value}`); 25+ } 26+ 27+ // Generate a deterministic color from language name 28+ function langColor(value: string): string { 29+ let hash = 0; 30+ for (let i = 0; i < value.length; i++) { 31+ hash = value.charCodeAt(i) + ((hash << 5) - hash); 32+ } 33+ const hue = Math.abs(hash % 360); 34+ return `oklch(0.65 0.15 ${hue})`; 35+ } 1836 </script> 1937 2038 <svelte:head>
@@ -27,32 +45,61 @@
2745 Discover repositories across protocols on the AT Protocol network. 2846 </p> 2947 30- <!-- Protocol filters --> 31- <div class="mb-8 flex items-center gap-2 overflow-x-auto pb-2 scrollbar-none"> 32- <a 33- href="/" 34- class="shrink-0 whitespace-nowrap rounded-full border px-3 py-1 text-xs font-medium transition-colors 35- {!activeProtocol 36- ? 'border-accent bg-accent/10 text-accent' 37- : 'border-border text-text-secondary hover:border-accent/30 hover:text-text-primary'}" 38- > 39- All 40- </a> 41- {#each protocols as proto (proto.value)} 48+ <!-- Language filter --> 49+ <div class="mb-8"> 50+ <div class="flex flex-wrap items-center gap-2"> 4251 <a 43- href="/?protocol={proto.value}" 52+ href="/" 4453 class="shrink-0 whitespace-nowrap rounded-full border px-3 py-1 text-xs font-medium transition-colors 45- {activeProtocol === proto.value 54+ {!activeProtocol 4655 ? 'border-accent bg-accent/10 text-accent' 4756 : 'border-border text-text-secondary hover:border-accent/30 hover:text-text-primary'}" 4857 > 49- <span 50- class="mr-1.5 inline-block h-2 w-2 rounded-full" 51- style="background-color: {proto.color}" 52- ></span> 53- {proto.label} 58+ All {ALL_LANGUAGES.length} languages 5459 </a> 55- {/each} 60+ 61+ {#if activeProtocol} 62+ <span class="flex items-center gap-1.5 rounded-full border border-accent bg-accent/10 px-3 py-1 text-xs font-medium text-accent"> 63+ <span class="h-2 w-2 rounded-full" style="background-color: {langColor(activeProtocol)}"></span> 64+ {ALL_LANGUAGES.find((l) => l.value === activeProtocol)?.label ?? activeProtocol} 65+ <a href="/" class="ml-1 hover:text-text-primary"> 66+ <svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> 67+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /> 68+ </svg> 69+ </a> 70+ </span> 71+ {/if} 72+ 73+ <!-- Search input --> 74+ <div class="relative"> 75+ <input 76+ type="text" 77+ bind:value={searchQuery} 78+ onfocus={() => showDropdown = true} 79+ onblur={() => setTimeout(() => showDropdown = false, 200)} 80+ placeholder="Filter by language..." 81+ class="w-44 rounded-full border border-border bg-surface-0 px-3 py-1 text-xs text-text-primary placeholder:text-text-secondary focus:border-accent focus:outline-none" 82+ /> 83+ 84+ {#if showDropdown && filtered.length > 0} 85+ <ul class="absolute left-0 top-full z-50 mt-1 max-h-48 w-56 overflow-y-auto rounded-lg border border-border bg-surface-1 shadow-lg"> 86+ {#each filtered as lang} 87+ <li> 88+ <button 89+ type="button" 90+ onmousedown={() => selectProtocol(lang.value)} 91+ class="flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs transition-colors hover:bg-surface-2 92+ {activeProtocol === lang.value ? 'text-accent' : 'text-text-secondary'}" 93+ > 94+ <span class="h-2 w-2 shrink-0 rounded-full" style="background-color: {langColor(lang.value)}"></span> 95+ {lang.label} 96+ </button> 97+ </li> 98+ {/each} 99+ </ul> 100+ {/if} 101+ </div> 102+ </div> 56103 </div> 57104 58105 <!-- Trending section -->