diff --git a/.screenshots/browse-redesign/after-dark-desktop.png b/.screenshots/browse-redesign/after-dark-desktop.png new file mode 100644 index 00000000..4e5ccd76 Binary files /dev/null and b/.screenshots/browse-redesign/after-dark-desktop.png differ diff --git a/.screenshots/browse-redesign/after-light-desktop.png b/.screenshots/browse-redesign/after-light-desktop.png new file mode 100644 index 00000000..b8f79675 Binary files /dev/null and b/.screenshots/browse-redesign/after-light-desktop.png differ diff --git a/.screenshots/browse-redesign/after-mobile-375.png b/.screenshots/browse-redesign/after-mobile-375.png new file mode 100644 index 00000000..7e3c2dc7 Binary files /dev/null and b/.screenshots/browse-redesign/after-mobile-375.png differ diff --git a/.screenshots/browse-redesign/before-light-desktop.png b/.screenshots/browse-redesign/before-light-desktop.png new file mode 100644 index 00000000..80b4a3bb Binary files /dev/null and b/.screenshots/browse-redesign/before-light-desktop.png differ diff --git a/docs/.vitepress/theme/FormulaBrowser.vue b/docs/.vitepress/theme/FormulaBrowser.vue index df686037..5603e2c4 100644 --- a/docs/.vitepress/theme/FormulaBrowser.vue +++ b/docs/.vitepress/theme/FormulaBrowser.vue @@ -7,26 +7,28 @@ const formulasData = ref([]) const searchQuery = ref('') const selectedLicenses = ref(['all']) const selectedSources = ref(['all']) +const showMobileFilters = ref(false) +const basePath = import.meta.env.BASE_URL || '/' const licenseOptions = [ - { value: 'all', label: 'All Licenses', icon: 'all', count: 0 }, - { value: 'ofl', label: 'OFL 1.1', icon: 'ofl', count: 0 }, - { value: 'apache', label: 'Apache 2.0', icon: 'apache', count: 0 }, - { value: 'mit', label: 'MIT', icon: 'mit', count: 0 }, - { value: 'cc0', label: 'CC0 / Public Domain', icon: 'cc0', count: 0 }, - { value: 'other_open', label: 'Other Open Source', icon: 'open', count: 0 }, - { value: 'freely_dist', label: 'Freely Distributable', icon: 'free', count: 0 }, - { value: 'platform', label: 'Platform Tied', icon: 'platform', count: 0 }, - { value: 'bundled', label: 'Bundled Software', icon: 'bundled', count: 0 }, - { value: 'unknown', label: 'License Not Specified', icon: 'unknown', count: 0 }, + { value: 'all', label: 'All', count: 0 }, + { value: 'ofl', label: 'OFL', count: 0 }, + { value: 'apache', label: 'Apache', count: 0 }, + { value: 'mit', label: 'MIT', count: 0 }, + { value: 'cc0', label: 'CC0', count: 0 }, + { value: 'other_open', label: 'Other Open', count: 0 }, + { value: 'freely_dist', label: 'Freely Dist.', count: 0 }, + { value: 'platform', label: 'Platform', count: 0 }, + { value: 'bundled', label: 'Bundled', count: 0 }, + { value: 'unknown', label: 'Unknown', count: 0 }, ] const sourceOptions = [ - { value: 'all', label: 'All Sources', icon: 'all', count: 0 }, - { value: 'google', label: 'Google Fonts', icon: 'google', count: 0 }, - { value: 'sil', label: 'SIL International', icon: 'sil', count: 0 }, - { value: 'macos', label: 'Apple', icon: 'apple', count: 0 }, - { value: 'manual', label: 'Expert Curated', icon: 'fontist', count: 0 }, + { value: 'all', label: 'All', count: 0 }, + { value: 'google', label: 'Google', count: 0 }, + { value: 'sil', label: 'SIL', count: 0 }, + { value: 'macos', label: 'Apple', count: 0 }, + { value: 'manual', label: 'Curated', count: 0 }, ] // Map URL param values to internal license groups @@ -81,33 +83,14 @@ function getSourceBadge(f) { return `` } -function getSourceIcon(icon) { - const basePath = import.meta.env.BASE_URL || '/' - const icons = { - all: ``, - google: ``, - sil: ``, - apple: ``, - fontist: ``, - } - return icons[icon] || icon +function sourceLabel(value) { + const opt = sourceOptions.find(o => o.value === value) + return opt ? opt.label : value } -function getLicenseIcon(icon) { - const basePath = import.meta.env.BASE_URL || '/' - const icons = { - all: ``, - ofl: ``, - apache: ``, - mit: ``, - cc0: ``, - open: ``, - free: ``, - platform: ``, - bundled: ``, - unknown: ``, - } - return icons[icon] || icon +function licenseLabel(value) { + const opt = licenseOptions.find(o => o.value === value) + return opt ? opt.label : value } // Read URL params on mount @@ -199,6 +182,18 @@ const activeLetters = computed(() => { }) }) +const activeLicenseChips = computed(() => { + if (selectedLicenses.value.includes('all')) return [] + return selectedLicenses.value.map(v => ({ kind: 'license', value: v, label: licenseLabel(v) })) +}) + +const activeSourceChips = computed(() => { + if (selectedSources.value.includes('all')) return [] + return selectedSources.value.map(v => ({ kind: 'source', value: v, label: sourceLabel(v) })) +}) + +const activeChips = computed(() => [...activeLicenseChips.value, ...activeSourceChips.value]) + function scrollToLetter(letter) { const el = document.getElementById('letter-' + letter) if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }) @@ -211,8 +206,9 @@ function goToFormula(slug) { // "Page not found" 404 even though the SSR HTML exists on the server. // Bypass SPA routing for formula clicks — full page load fetches the // correct HTML which loads the correct app chunk for that formula's batch. - const basePath = import.meta.env.BASE_URL || '/' - window.location.href = `${basePath}browse/${slug}/` + // No trailing slash: cleanUrls:true route map expects /browse/foo, and + // GitHub Pages serves /browse/foo/index.html for either form. + window.location.href = `${basePath}browse/${slug}` } function toggleLicense(value) { @@ -230,365 +226,749 @@ function toggleSource(value) { selectedSources.value = [value] } } + +function removeChip(chip) { + if (chip.kind === 'license') selectedLicenses.value = ['all'] + else selectedSources.value = ['all'] +} + +function clearAllFilters() { + selectedLicenses.value = ['all'] + selectedSources.value = ['all'] + searchQuery.value = '' +} - - - - {{ filteredFormulas.length }} formulas - - - -