From 69d1ee8c61f064e74593767369b9ee0269306005 Mon Sep 17 00:00:00 2001 From: Garth de Wet Date: Thu, 29 Jan 2026 17:43:21 +0200 Subject: [PATCH 1/4] feat: keep search box in header Avoid taking up space for searchbox on search page. Mainly moved the query string watching from search.vue to AppHeader.vuue # Conflicts: # app/components/AppHeader.vue # app/pages/search.vue # Conflicts: # app/components/AppHeader.vue # app/pages/search.vue --- app/components/AppHeader.vue | 46 ++++++++++++++------ app/pages/search.vue | 84 +----------------------------------- 2 files changed, 34 insertions(+), 96 deletions(-) diff --git a/app/components/AppHeader.vue b/app/components/AppHeader.vue index 7d30b29fe..c905bad24 100644 --- a/app/components/AppHeader.vue +++ b/app/components/AppHeader.vue @@ -17,27 +17,48 @@ const { isConnected, npmUser } = useConnector() const router = useRouter() const route = useRoute() -const searchQuery = ref('') const isSearchFocused = ref(false) const showSearchBar = computed(() => { - return route.name !== 'search' && route.name !== 'index' + return route.name !== 'index' }) -const debouncedNavigate = debounce(async () => { - const query = searchQuery.value.trim() - await router.push({ +// Local input value (updates immediately as user types) +const searchQuery = ref((route.query.q as string) ?? '') + +// Debounced URL update for search query +const updateUrlQuery = debounce((value: string) => { + if (route.name === 'search') { + router.replace({ query: { q: value || undefined } }) + return + } + if (!value) { + return + } + + router.push({ name: 'search', - query: query ? { q: query } : undefined, + query: { + q: value, + }, }) - // allow time for the navigation to occur before resetting searchQuery - setTimeout(() => (searchQuery.value = ''), 1000) -}, 100) +}, 250) -async function handleSearchInput() { - debouncedNavigate() -} +// Watch input and debounce URL updates +watch(searchQuery, value => { + updateUrlQuery(value) +}) +// Sync input with URL when navigating (e.g., back button) +watch( + () => route.query.q, + urlQuery => { + const value = (urlQuery as string) ?? '' + if (searchQuery.value !== value) { + searchQuery.value = value + } + }, +) onKeyStroke(',', e => { // Don't trigger if user is typing in an input const target = e.target as HTMLElement @@ -95,7 +116,6 @@ onKeyStroke(',', e => { :placeholder="$t('search.placeholder')" v-bind="noCorrect" class="w-full bg-bg-subtle border border-border rounded-md ps-7 pe-3 py-1.5 font-mono text-sm text-fg placeholder:text-fg-subtle transition-border-color duration-300 motion-reduce:transition-none focus:border-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50" - @input="handleSearchInput" @focus="isSearchFocused = true" @blur="isSearchFocused = false" /> diff --git a/app/pages/search.vue b/app/pages/search.vue index 1453f3f65..1d893c9ef 100644 --- a/app/pages/search.vue +++ b/app/pages/search.vue @@ -18,14 +18,6 @@ const { resetColumns, } = usePackageListPreferences() -// Local input value (updates immediately as user types) -const inputValue = ref((route.query.q as string) ?? '') - -// Debounced URL update for search query -const updateUrlQuery = debounce((value: string) => { - router.replace({ query: { q: value || undefined } }) -}, 250) - // Debounced URL update for page (less aggressive to avoid too many URL changes) const updateUrlPage = debounce((page: number) => { router.replace({ @@ -36,37 +28,15 @@ const updateUrlPage = debounce((page: number) => { }) }, 500) -// Watch input and debounce URL updates -watch(inputValue, value => { - updateUrlQuery(value) -}) - // The actual search query (from URL, used for API calls) const query = computed(() => (route.query.q as string) ?? '') -// Sync input with URL when navigating (e.g., back button) -watch( - () => route.query.q, - urlQuery => { - const value = (urlQuery as string) ?? '' - if (inputValue.value !== value) { - inputValue.value = value - } - }, -) - -// For glow effect -const searchInputRef = useTemplateRef('searchInputRef') -const { focused: isSearchFocused } = useFocus(searchInputRef) - const selectedIndex = ref(0) const packageListRef = useTemplateRef('packageListRef') // Track if page just loaded (for hiding "Searching..." during view transition) const hasInteracted = ref(false) onMounted(() => { - // Focus search onMount - isSearchFocused.value = true // Small delay to let view transition complete setTimeout(() => { hasInteracted.value = true @@ -759,60 +729,8 @@ defineOgImageComponent('Default', {