Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
ec549f9
fix: apply limits to algolia and npm search output, fix visual issues
alex-key Mar 8, 2026
739a3a7
Merge branch 'main' of https://github.com/alex-key/npmx.dev into fix/…
alex-key Mar 8, 2026
d6acbe4
Merge branch 'main' of https://github.com/alex-key/npmx.dev into fix/…
alex-key Mar 10, 2026
4c453dd
fix: remove log, add typesafe condition in search
alex-key Mar 10, 2026
452bd47
Merge branch 'main' of https://github.com/alex-key/npmx.dev into fix/…
alex-key Mar 12, 2026
62b7f6e
fix: max available results hint fixed og search page
alex-key Mar 12, 2026
6407929
fix: improve results handling for algolia search, add types
alex-key Mar 13, 2026
f3e2fe6
Merge branch 'main' of https://github.com/alex-key/npmx.dev into fix/…
alex-key Mar 13, 2026
65b6d30
fix: add missing expression in search, fix empty results output
alex-key Mar 13, 2026
b336e9b
Merge branch 'main' into fix/search-results-limit
alex-key Mar 13, 2026
543e481
Merge branch 'main' into fix/search-results-limit
alex-key Mar 13, 2026
0d42e69
Merge branch 'main' into fix/search-results-limit
alex-key Mar 14, 2026
baeb512
Merge branch 'main' into fix/search-results-limit
alex-key Mar 17, 2026
60b8365
Merge branch 'main' of https://github.com/alex-key/npmx.dev into fix/…
alex-key Mar 19, 2026
21be9db
fix: resolve type conflicts in useSettings
alex-key Mar 19, 2026
50adc52
fix: align some classes in PaginationControls
alex-key Mar 19, 2026
88692bb
[autofix.ci] apply automated fixes
autofix-ci[bot] Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion app/components/Package/Card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ const numberFormatter = useNumberFormatter()
</div>

<ul
role="list"
v-if="result.package.keywords?.length"
:aria-label="$t('package.card.keywords')"
class="relative z-10 flex flex-wrap gap-1.5 mt-3 pt-3 border-t border-border list-none m-0 p-0 pointer-events-none items-center"
Expand Down
20 changes: 13 additions & 7 deletions app/components/PaginationControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import type { PageSize, PaginationMode, ViewMode } from '#shared/types/preferences'
import { PAGE_SIZE_OPTIONS } from '#shared/types/preferences'

const ALL_PAGES_VISIBLE_TRESHOLD = 7

const props = defineProps<{
totalItems: number
/** When in table view, force pagination mode (no infinite scroll for tables) */
Expand Down Expand Up @@ -65,7 +67,7 @@ const visiblePages = computed(() => {
const current = currentPage.value
const pages: (number | 'ellipsis')[] = []

if (total <= 7) {
if (total <= ALL_PAGES_VISIBLE_TRESHOLD) {
// Show all pages
for (let i = 1; i <= total; i++) {
pages.push(i)
Expand Down Expand Up @@ -99,6 +101,11 @@ const visiblePages = computed(() => {
return pages
})

// disable last page button to prevent TOO MANY REQUESTS error
function isPageButtonDisabled(page: number): boolean {
return totalPages.value > ALL_PAGES_VISIBLE_TRESHOLD && page > currentPage.value + 2
}

function handlePageSizeChange(event: Event) {
const target = event.target as HTMLSelectElement
const value = target.value
Expand Down Expand Up @@ -184,7 +191,7 @@ function handlePageSizeChange(event: Event) {
<!-- Previous button -->
<button
type="button"
class="p-1.5 rounded hover:bg-bg-muted text-fg-muted hover:text-fg disabled:opacity-40 disabled:cursor-not-allowed transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1"
class="p-1.5 rounded text-fg-muted transition-colors duration-200 hover:(text-fg bg-bg-muted) focus-visible:(ring-2 ring-fg ring-offset-1) disabled:(opacity-40 cursor-not-allowed)"
:disabled="!canGoPrev"
:aria-label="$t('filters.pagination.previous')"
@click="goPrev"
Expand All @@ -198,11 +205,10 @@ function handlePageSizeChange(event: Event) {
<button
v-else
type="button"
class="min-w-[32px] h-8 px-2 font-mono text-sm rounded transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1"
:disabled="isPageButtonDisabled(page)"
class="min-w-[32px] h-8 px-2 font-mono text-sm rounded transition-colors duration-200 focus-visible:(ring-2 ring-fg ring-offset-1) disabled:(opacity-40 cursor-not-allowed)"
:class="
page === currentPage
? 'bg-fg text-bg'
: 'text-fg-muted hover:text-fg hover:bg-bg-muted'
page === currentPage ? 'bg-fg text-bg' : 'text-fg-muted hover:(text-fg bg-bg-muted)'
"
:aria-current="page === currentPage ? 'page' : undefined"
@click="goToPage(page)"
Expand All @@ -214,7 +220,7 @@ function handlePageSizeChange(event: Event) {
<!-- Next button -->
<button
type="button"
class="p-1.5 rounded hover:bg-bg-muted text-fg-muted hover:text-fg disabled:opacity-40 disabled:cursor-not-allowed transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1"
class="p-1.5 rounded text-fg-muted transition-colors duration-200 hover:(text-fg bg-bg-muted) focus-visible:(ring-2 ring-fg ring-offset-1) disabled:(opacity-40 cursor-not-allowed)"
:disabled="!canGoNext"
:aria-label="$t('filters.pagination.next')"
@click="goNext"
Expand Down
11 changes: 4 additions & 7 deletions app/composables/npm/search-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { NpmSearchResult, PackageMetaResponse, SearchResponse } from '#shared/types'

export function metaToSearchResult(meta: PackageMetaResponse): NpmSearchResult {
return {
package: {
Expand All @@ -18,21 +20,16 @@ export function metaToSearchResult(meta: PackageMetaResponse): NpmSearchResult {
}
}

export function emptySearchResponse(): NpmSearchResponse {
export function emptySearchResponse(): SearchResponse {
return {
objects: [],
totalUnlimited: 0,
total: 0,
isStale: false,
time: new Date().toISOString(),
}
}

export interface SearchSuggestion {
type: 'user' | 'org'
name: string
exists: boolean
}

export type SuggestionIntent = 'user' | 'org' | 'both' | null

export function isValidNpmName(name: string): boolean {
Expand Down
16 changes: 9 additions & 7 deletions app/composables/npm/useAlgoliaSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
liteClient as algoliasearch,
type LiteClient,
type SearchQuery,
type SearchResponse,
type SearchResponse as AlgoliaSearchResponse,
} from 'algoliasearch/lite'

let _searchClient: LiteClient | null = null
Expand Down Expand Up @@ -156,7 +156,7 @@ export function useAlgoliaSearch() {
],
})

const response = results[0] as SearchResponse<AlgoliaHit> | undefined
const response = results[0] as AlgoliaSearchResponse<AlgoliaHit> | undefined
if (!response) {
throw new Error('Algolia returned an empty response')
}
Expand Down Expand Up @@ -201,7 +201,7 @@ export function useAlgoliaSearch() {
],
})

const response = results[0] as SearchResponse<AlgoliaHit> | undefined
const response = results[0] as AlgoliaSearchResponse<AlgoliaHit> | undefined
if (!response) break

serverTotal = response.nbHits ?? 0
Expand Down Expand Up @@ -317,7 +317,7 @@ export function useAlgoliaSearch() {

const { results } = await client.search({ requests })

const mainResponse = results[0] as SearchResponse<AlgoliaHit> | undefined
const mainResponse = results[0] as AlgoliaSearchResponse<AlgoliaHit> | undefined
if (!mainResponse) {
throw new Error('Algolia returned an empty response')
}
Expand All @@ -331,21 +331,23 @@ export function useAlgoliaSearch() {

let orgExists = false
if (orgQueryIndex >= 0 && checks?.name) {
const orgResponse = results[orgQueryIndex] as SearchResponse<AlgoliaHit> | undefined
const orgResponse = results[orgQueryIndex] as AlgoliaSearchResponse<AlgoliaHit> | undefined
const scopePrefix = `@${checks.name.toLowerCase()}/`
orgExists =
orgResponse?.hits?.some(h => h.name?.toLowerCase().startsWith(scopePrefix)) ?? false
}

let userExists = false
if (userQueryIndex >= 0) {
const userResponse = results[userQueryIndex] as SearchResponse<AlgoliaHit> | undefined
const userResponse = results[userQueryIndex] as AlgoliaSearchResponse<AlgoliaHit> | undefined
userExists = (userResponse?.nbHits ?? 0) > 0
}

let packageExists: boolean | null = null
if (packageQueryIndex >= 0) {
const pkgResponse = results[packageQueryIndex] as SearchResponse<AlgoliaHit> | undefined
const pkgResponse = results[packageQueryIndex] as
| AlgoliaSearchResponse<AlgoliaHit>
| undefined
packageExists = (pkgResponse?.nbHits ?? 0) > 0
}

Expand Down
Loading
Loading