Skip to content

Commit c99d5b1

Browse files
committed
Merge remote-tracking branch 'origin/main' into neutrino2211/main
2 parents 8716f1f + 983f5f6 commit c99d5b1

16 files changed

Lines changed: 200 additions & 31 deletions

File tree

.github/workflows/autofix.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515

1616
steps:
1717
- uses: actions/checkout@v6
18-
- run: npm i -g --force corepack && corepack enable
18+
- run: corepack enable
1919
- uses: actions/setup-node@v6
2020
with:
2121
node-version: lts/*

.github/workflows/ci.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ on:
88
branches:
99
- main
1010

11+
permissions:
12+
contents: read
13+
1114
jobs:
1215
lint:
1316
runs-on: ubuntu-latest
1417

1518
steps:
1619
- uses: actions/checkout@v6
17-
- run: npm i -g --force corepack && corepack enable
20+
- run: corepack enable
1821
- uses: actions/setup-node@v6
1922
with:
2023
node-version: lts/*
@@ -31,7 +34,7 @@ jobs:
3134

3235
steps:
3336
- uses: actions/checkout@v6
34-
- run: npm i -g --force corepack && corepack enable
37+
- run: corepack enable
3538
- uses: actions/setup-node@v6
3639
with:
3740
node-version: lts/*
@@ -59,7 +62,7 @@ jobs:
5962

6063
steps:
6164
- uses: actions/checkout@v6
62-
- run: npm i -g --force corepack && corepack enable
65+
- run: corepack enable
6366
- uses: actions/setup-node@v6
6467
with:
6568
node-version: lts/*

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ The aim of [npmx.dev](https://npmx.dev) is to provide a better browser for the n
3535
- **Install size** – total install size including dependencies
3636
- **Playground links** – quick access to StackBlitz, CodeSandbox, and other demo environments from READMEs
3737
- **Infinite search** – auto-load additional search pages as you scroll
38+
- **Keyboard navigation** – press `/` to focus search, arrow keys to navigate results, Enter to select
3839
- **Claim new packages** – register new package names directly from search results (via local connector)
3940

4041
### User & org pages
@@ -65,6 +66,7 @@ The aim of [npmx.dev](https://npmx.dev) is to provide a better browser for the n
6566
| Vulnerability warnings |||
6667
| Download charts |||
6768
| Playground links |||
69+
| Keyboard navigation |||
6870
| Dependents list || 🚧 |
6971
| Package admin (access/owners) || 🚧 |
7072
| Org/team management || 🚧 |

SECURITY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
## Reporting a Vulnerability
44

5-
To report a vulnerability, please [privately report it via the Security tab](https://github.com/danielroe/npmx.dev/security/advisories/new). If that is impossible, feel free to send an email to **security@roe.dev** instead.
5+
To report a vulnerability, please [privately report it via the Security tab](https://github.com/npmx-dev/npmx.dev/security/advisories/new). If that is impossible, feel free to send an email to **security@roe.dev** instead.
66

77
All security vulnerabilities will be promptly verified and addressed.

app/app.vue

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script setup lang="ts">
2+
import { useEventListener } from '@vueuse/core'
3+
24
const route = useRoute()
35
const router = useRouter()
46
@@ -12,9 +14,12 @@ useHead({
1214
1315
// Global keyboard shortcut: "/" focuses search or navigates to search page
1416
function handleGlobalKeydown(e: KeyboardEvent) {
15-
// Ignore if user is typing in an input, textarea, or contenteditable
1617
const target = e.target as HTMLElement
17-
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {
18+
19+
const isEditableTarget =
20+
target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable
21+
22+
if (isEditableTarget) {
1823
return
1924
}
2025
@@ -28,20 +33,16 @@ function handleGlobalKeydown(e: KeyboardEvent) {
2833
2934
if (searchInput) {
3035
searchInput.focus()
31-
} else {
32-
// Navigate to search page
33-
router.push('/search')
36+
return
3437
}
38+
39+
router.push('/search')
3540
}
3641
}
3742
38-
onMounted(() => {
39-
document.addEventListener('keydown', handleGlobalKeydown)
40-
})
41-
42-
onUnmounted(() => {
43-
document.removeEventListener('keydown', handleGlobalKeydown)
44-
})
43+
if (import.meta.client) {
44+
useEventListener(document, 'keydown', handleGlobalKeydown)
45+
}
4546
</script>
4647

4748
<template>

app/components/AppFooter.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<p class="font-mono m-0">a better browser for the npm registry</p>
66
<div class="flex items-center gap-6">
77
<a
8-
href="https://github.com/danielroe/npmx.dev"
8+
href="https://github.com/npmx-dev/npmx.dev"
99
rel="noopener noreferrer"
1010
class="link-subtle font-mono text-xs"
1111
>

app/components/AppHeader.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ withDefaults(
4343
</li>
4444
<li v-else class="flex">
4545
<a
46-
href="https://github.com/danielroe/npmx.dev"
46+
href="https://github.com/npmx-dev/npmx.dev"
4747
rel="noopener noreferrer"
4848
class="link-subtle font-mono text-sm inline-flex items-center gap-1.5"
4949
>

app/components/PackageCard.vue

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,27 @@ defineProps<{
99
/** Whether to show the publisher username */
1010
showPublisher?: boolean
1111
prefetch?: boolean
12+
selected?: boolean
13+
index?: number
14+
}>()
15+
16+
const emit = defineEmits<{
17+
focus: [index: number]
1218
}>()
1319
</script>
1420

1521
<template>
16-
<article class="group card-interactive">
22+
<article
23+
class="group card-interactive scroll-mt-48 scroll-mb-6"
24+
:class="{ 'bg-bg-muted border-border-hover': selected }"
25+
>
1726
<NuxtLink
1827
:to="{ name: 'package', params: { package: result.package.name.split('/') } }"
1928
:prefetch-on="prefetch ? 'visibility' : 'interaction'"
20-
class="block focus:outline-none decoration-none"
29+
class="block focus:outline-none decoration-none scroll-mt-48 scroll-mb-6"
30+
:data-result-index="index"
31+
@focus="index != null && emit('focus', index)"
32+
@mouseenter="index != null && emit('focus', index)"
2133
>
2234
<header class="flex items-start justify-between gap-4 mb-2">
2335
<component

app/components/PackageList.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@ const props = defineProps<{
1818
pageSize?: number
1919
/** Initial page to scroll to (1-indexed) */
2020
initialPage?: number
21+
/** Selected result index (for keyboard navigation) */
22+
selectedIndex?: number
2123
}>()
2224
2325
const emit = defineEmits<{
2426
/** Emitted when scrolled near the bottom and more items should be loaded */
2527
loadMore: []
2628
/** Emitted when the visible page changes */
2729
pageChange: [page: number]
30+
/** Emitted when a result is hovered/focused */
31+
select: [index: number]
2832
}>()
2933
3034
// Reference to WindowVirtualizer for infinite scroll detection
@@ -78,6 +82,14 @@ watch(
7882
}
7983
},
8084
)
85+
86+
function scrollToIndex(index: number, smooth = true) {
87+
listRef.value?.scrollToIndex(index, { align: 'center', smooth })
88+
}
89+
90+
defineExpose({
91+
scrollToIndex,
92+
})
8193
</script>
8294

8395
<template>
@@ -97,8 +109,11 @@ watch(
97109
:result="item as NpmSearchResult"
98110
:heading-level="headingLevel"
99111
:show-publisher="showPublisher"
112+
:selected="index === (selectedIndex ?? -1)"
113+
:index="index"
100114
class="animate-fade-in animate-fill-both"
101115
:style="{ animationDelay: `${Math.min(index * 0.02, 0.3)}s` }"
116+
@focus="emit('select', $event)"
102117
/>
103118
</div>
104119
</template>

app/composables/useVirtualInfiniteScroll.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ export interface WindowVirtualizerHandle {
55
readonly viewportSize: number
66
findItemIndex: (offset: number) => number
77
getItemOffset: (index: number) => number
8-
scrollToIndex: (index: number, opts?: { align?: 'start' | 'center' | 'end' }) => void
8+
scrollToIndex: (
9+
index: number,
10+
opts?: { align?: 'start' | 'center' | 'end'; smooth?: boolean },
11+
) => void
912
}
1013

1114
export interface UseVirtualInfiniteScrollOptions {

0 commit comments

Comments
 (0)