Skip to content
Merged
3 changes: 2 additions & 1 deletion app/components/Compare/PackageSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ const inputValue = shallowRef('')
const isInputFocused = shallowRef(false)

// Use the shared search composable (supports both npm and Algolia providers)
const { data: searchData, status } = useSearch(inputValue, { size: 15 })
const { searchProvider } = useSearchProvider()
const { data: searchData, status } = useSearch(inputValue, searchProvider, { size: 15 })

const isSearching = computed(() => status.value === 'pending')

Expand Down
22 changes: 17 additions & 5 deletions app/components/Header/SearchBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ const emit = defineEmits(['blur', 'focus'])

const router = useRouter()
const route = useRoute()
const { isAlgolia } = useSearchProvider()
const { searchProvider } = useSearchProvider()
const searchProviderValue = computed(() => {
const p = normalizeSearchParam(route.query.p)
if (p === 'npm' || searchProvider.value === 'npm') return 'npm'
return 'algolia'
})

const isSearchFocused = shallowRef(false)

Expand All @@ -29,13 +34,13 @@ const searchQuery = shallowRef(normalizeSearchParam(route.query.q))
// Pages that have their own local filter using ?q
const pagesWithLocalFilter = new Set(['~username', 'org'])

function updateUrlQueryImpl(value: string) {
function updateUrlQueryImpl(value: string, provider: 'npm' | 'algolia') {
// Don't navigate away from pages that use ?q for local filtering
if (pagesWithLocalFilter.has(route.name as string)) {
return
}
if (route.name === 'search') {
router.replace({ query: { q: value || undefined } })
router.replace({ query: { q: value || undefined, p: provider === 'npm' ? 'npm' : undefined } })
return
}
if (!value) {
Expand All @@ -46,6 +51,7 @@ function updateUrlQueryImpl(value: string) {
name: 'search',
query: {
q: value,
p: provider === 'npm' ? 'npm' : undefined,
},
})
}
Expand All @@ -54,9 +60,14 @@ const updateUrlQueryNpm = debounce(updateUrlQueryImpl, 250)
const updateUrlQueryAlgolia = debounce(updateUrlQueryImpl, 80)

const updateUrlQuery = Object.assign(
(value: string) => (isAlgolia.value ? updateUrlQueryAlgolia : updateUrlQueryNpm)(value),
(value: string) =>
(searchProviderValue.value === 'algolia' ? updateUrlQueryAlgolia : updateUrlQueryNpm)(
value,
searchProviderValue.value,
),
{
flush: () => (isAlgolia.value ? updateUrlQueryAlgolia : updateUrlQueryNpm).flush(),
flush: () =>
(searchProviderValue.value === 'algolia' ? updateUrlQueryAlgolia : updateUrlQueryNpm).flush(),
},
)

Expand Down Expand Up @@ -85,6 +96,7 @@ function handleSubmit() {
name: 'search',
query: {
q: searchQuery.value,
p: searchProviderValue.value === 'npm' ? 'npm' : undefined,
},
})
} else {
Expand Down
34 changes: 26 additions & 8 deletions app/components/SearchProviderToggle.client.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
<script setup lang="ts">
const { searchProvider, isAlgolia } = useSearchProvider()
const route = useRoute()
const router = useRouter()
const { searchProvider } = useSearchProvider()
const searchProviderValue = computed(() => {
const p = normalizeSearchParam(route.query.p)
if (p === 'npm' || searchProvider.value === 'npm') return 'npm'
return 'algolia'
})

const isOpen = shallowRef(false)
const toggleRef = useTemplateRef('toggleRef')
Expand Down Expand Up @@ -47,21 +54,25 @@ useEventListener('keydown', event => {
type="button"
role="menuitem"
class="w-full flex items-start gap-3 px-3 py-2.5 rounded-md text-start transition-colors hover:bg-bg-muted"
:class="[!isAlgolia ? 'bg-bg-muted' : '']"
:class="[searchProviderValue !== 'algolia' ? 'bg-bg-muted' : '']"
@click="
() => {
searchProvider = 'npm'
router.push({ query: { ...route.query, p: 'npm' } })
isOpen = false
}
"
>
<span
class="i-carbon:catalog w-4 h-4 mt-0.5 shrink-0"
:class="!isAlgolia ? 'text-accent' : 'text-fg-muted'"
:class="searchProviderValue !== 'algolia' ? 'text-accent' : 'text-fg-muted'"
aria-hidden="true"
/>
<div class="min-w-0 flex-1">
<div class="text-sm font-medium" :class="!isAlgolia ? 'text-fg' : 'text-fg-muted'">
<div
class="text-sm font-medium"
:class="searchProviderValue !== 'algolia' ? 'text-fg' : 'text-fg-muted'"
>
{{ $t('settings.data_source.npm') }}
</div>
<p class="text-xs text-fg-subtle mt-0.5">
Expand All @@ -75,21 +86,25 @@ useEventListener('keydown', event => {
type="button"
role="menuitem"
class="w-full flex items-start gap-3 px-3 py-2.5 rounded-md text-start transition-colors hover:bg-bg-muted mt-1"
:class="[isAlgolia ? 'bg-bg-muted' : '']"
:class="[searchProviderValue === 'algolia' ? 'bg-bg-muted' : '']"
@click="
() => {
searchProvider = 'algolia'
router.push({ query: { ...route.query, p: undefined } })
isOpen = false
}
"
>
<span
class="i-carbon:search w-4 h-4 mt-0.5 shrink-0"
:class="isAlgolia ? 'text-accent' : 'text-fg-muted'"
:class="searchProviderValue === 'algolia' ? 'text-accent' : 'text-fg-muted'"
aria-hidden="true"
/>
<div class="min-w-0 flex-1">
<div class="text-sm font-medium" :class="isAlgolia ? 'text-fg' : 'text-fg-muted'">
<div
class="text-sm font-medium"
:class="searchProviderValue === 'algolia' ? 'text-fg' : 'text-fg-muted'"
>
{{ $t('settings.data_source.algolia') }}
</div>
<p class="text-xs text-fg-subtle mt-0.5">
Expand All @@ -99,7 +114,10 @@ useEventListener('keydown', event => {
</button>

<!-- Algolia attribution -->
<div v-if="isAlgolia" class="border-t border-border mx-1 mt-1 pt-2 pb-1">
<div
v-if="searchProviderValue === 'algolia'"
class="border-t border-border mx-1 mt-1 pt-2 pb-1"
>
<a
href="https://www.algolia.com/developers"
target="_blank"
Expand Down
10 changes: 8 additions & 2 deletions app/composables/npm/useOrgPackages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ import { mapWithConcurrency } from '#shared/utils/async'
* 3. Falls back to lightweight server-side package-meta lookups
*/
export function useOrgPackages(orgName: MaybeRefOrGetter<string>) {
const route = useRoute()
const { searchProvider } = useSearchProvider()
const searchProviderValue = computed(() => {
const p = normalizeSearchParam(route.query.p)
if (p === 'npm' || searchProvider.value === 'npm') return 'npm'
return 'algolia'
})
const { getPackagesByName } = useAlgoliaSearch()

const asyncData = useLazyAsyncData(
() => `org-packages:${searchProvider.value}:${toValue(orgName)}`,
() => `org-packages:${searchProviderValue.value}:${toValue(orgName)}`,
async ({ ssrContext }, { signal }) => {
const org = toValue(orgName)
if (!org) {
Expand Down Expand Up @@ -51,7 +57,7 @@ export function useOrgPackages(orgName: MaybeRefOrGetter<string>) {
}

// Fetch metadata + downloads from Algolia (single request via getObjects)
if (searchProvider.value === 'algolia') {
if (searchProviderValue.value === 'algolia') {
try {
const response = await getPackagesByName(packageNames)
if (response.objects.length > 0) {
Expand Down
Loading
Loading