From f916120dea654ba3cbe157e9e39c03513c19d3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rlon=20C=2E?= Date: Wed, 25 Feb 2026 11:02:12 -0300 Subject: [PATCH 1/5] fix(search): prevent scroll jump during pagination --- app/pages/search.vue | 57 ++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/app/pages/search.vue b/app/pages/search.vue index dcb285382..90f9c2e97 100644 --- a/app/pages/search.vue +++ b/app/pages/search.vue @@ -21,13 +21,16 @@ const { } = usePackageListPreferences() // Debounced URL update for page (less aggressive to avoid too many URL changes) +//Use History API directly to update URL without triggering Router's scroll-to-top const updateUrlPage = debounce((page: number) => { - router.replace({ - query: { - ...route.query, - page: page > 1 ? page : undefined, - }, - }) + const url = new URL(window.location.href) + if (page > 1) { + url.searchParams.set('page', page.toString()) + } else { + url.searchParams.delete('page') + } + // This updates the address bar "silently" + window.history.replaceState({}, '', url) }, 500) const { model: searchQuery, provider: searchProvider } = useGlobalSearch() @@ -276,7 +279,8 @@ function handlePageChange(page: number) { } // Reset page when query changes -watch(query, () => { +watch(query, (newQuery, oldQuery) => { + if (newQuery.trim() === (oldQuery || '').trim()) return currentPage.value = 1 hasInteracted.value = true }) @@ -536,7 +540,7 @@ defineOgImageComponent('Default', { + + From 307b7336660b6be27cef7b6e36eed611f1fdf7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rlon=20C=2E?= Date: Wed, 25 Feb 2026 11:17:09 -0300 Subject: [PATCH 2/5] fix(ui): remove unused router declaration --- app/pages/search.vue | 1 - package.json | 22 ++++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/pages/search.vue b/app/pages/search.vue index 90f9c2e97..dcfd7674a 100644 --- a/app/pages/search.vue +++ b/app/pages/search.vue @@ -8,7 +8,6 @@ import { isPlatformSpecificPackage } from '~/utils/platform-packages' import { normalizeSearchParam } from '#shared/utils/url' const route = useRoute() -const router = useRouter() // Preferences (persisted to localStorage) const { diff --git a/package.json b/package.json index d0d000c24..22e65e6a9 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,7 @@ "private": true, "type": "module", "version": "0.0.0", - "author": { - "name": "Daniel Roe", - "email": "daniel@roe.dev", - "url": "https://roe.dev" - }, + "author": "Daniel Roe (https://roe.dev)", "scripts": { "build": "nuxt build", "build:lunaria": "node ./lunaria/lunaria.ts", @@ -167,5 +163,19 @@ "pnpm oxfmt" ] }, - "packageManager": "pnpm@10.30.1" + "packageManager": "pnpm@10.30.1", + "description": "> A fast, modern browser for the npm registry.", + "main": "index.js", + "directories": { + "doc": "docs", + "test": "test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/marlonwq/npmx.dev.git" + }, + "bugs": { + "url": "https://github.com/marlonwq/npmx.dev/issues" + }, + "homepage": "https://github.com/marlonwq/npmx.dev#readme" } From 32efa446acf07b5ae2b3117c870d1469172dba2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rlon=20C=2E?= Date: Wed, 25 Feb 2026 17:54:45 -0300 Subject: [PATCH 3/5] fix(ui): apply accessibility and history state review suggestions --- app/pages/search.vue | 49 ++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/app/pages/search.vue b/app/pages/search.vue index dcfd7674a..b1a83da2d 100644 --- a/app/pages/search.vue +++ b/app/pages/search.vue @@ -29,7 +29,7 @@ const updateUrlPage = debounce((page: number) => { url.searchParams.delete('page') } // This updates the address bar "silently" - window.history.replaceState({}, '', url) + window.history.replaceState(window.history.state, '', url) }, 500) const { model: searchQuery, provider: searchProvider } = useGlobalSearch() @@ -393,20 +393,24 @@ const totalSelectableCount = computed(() => suggestionCount.value + resultCount. * Get all focusable result elements in DOM order (suggestions first, then packages) */ function getFocusableElements(): HTMLElement[] { - const suggestions = Array.from( - document.querySelectorAll('[data-suggestion-index]'), - ).sort((a, b) => { - const aIdx = Number.parseInt(a.dataset.suggestionIndex ?? '0', 10) - const bIdx = Number.parseInt(b.dataset.suggestionIndex ?? '0', 10) - return aIdx - bIdx - }) - const packages = Array.from(document.querySelectorAll('[data-result-index]')).sort( - (a, b) => { + const isVisible = (el: HTMLElement) => el.getClientRects().length > 0 + + const suggestions = Array.from(document.querySelectorAll('[data-suggestion-index]')) + .filter(isVisible) + .sort((a, b) => { + const aIdx = Number.parseInt(a.dataset.suggestionIndex ?? '0', 10) + const bIdx = Number.parseInt(b.dataset.suggestionIndex ?? '0', 10) + return aIdx - bIdx + }) + + const packages = Array.from(document.querySelectorAll('[data-result-index]')) + .filter(isVisible) + .sort((a, b) => { const aIdx = Number.parseInt(a.dataset.resultIndex ?? '0', 10) const bIdx = Number.parseInt(b.dataset.resultIndex ?? '0', 10) return aIdx - bIdx - }, - ) + }) + return [...suggestions, ...packages] } @@ -549,13 +553,13 @@ defineOgImageComponent('Default', {
- +
-
+