Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ test-results/

# generated files
shared/types/lexicons

# output
.vercel
2 changes: 1 addition & 1 deletion app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ onKeyDown(
return
}

router.push('/search')
router.push({ name: 'search' })
},
{ dedupe: true },
)
Expand Down
4 changes: 2 additions & 2 deletions app/components/AppFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ const isHome = computed(() => route.name === 'index')
</div>
<!-- Desktop: Show all links. Mobile: Links are in MobileMenu -->
<div class="hidden sm:flex items-center gap-6">
<NuxtLink to="/about" class="link-subtle font-mono text-xs flex items-center">
<NuxtLink :to="{ name: 'about' }" class="link-subtle font-mono text-xs flex items-center">
{{ $t('footer.about') }}
</NuxtLink>
<NuxtLink
to="/privacy"
:to="{ name: 'privacy' }"
class="link-subtle font-mono text-xs min-h-11 flex items-center gap-1 lowercase"
>
{{ $t('privacy_policy.title') }}
Expand Down
10 changes: 5 additions & 5 deletions app/components/AppHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ onKeyStroke(
e => isKeyWithoutModifiers(e, ',') && !isEditableElement(e.target),
e => {
e.preventDefault()
navigateTo('/settings')
navigateTo({ name: 'settings' })
},
{ dedupe: true },
)
Expand All @@ -78,7 +78,7 @@ onKeyStroke(
!e.defaultPrevented,
e => {
e.preventDefault()
navigateTo('/compare')
navigateTo({ name: 'compare' })
},
{ dedupe: true },
)
Expand Down Expand Up @@ -106,7 +106,7 @@ onKeyStroke(
<!-- Desktop: Logo (navigates home) -->
<div v-if="showLogo" class="hidden sm:flex flex-shrink-0 items-center">
<NuxtLink
to="/"
:to="{ name: 'index' }"
:aria-label="$t('header.home')"
dir="ltr"
class="inline-flex items-center gap-1 header-logo font-mono text-lg font-medium text-fg hover:text-fg/90 transition-colors duration-200 rounded"
Expand Down Expand Up @@ -152,7 +152,7 @@ onKeyStroke(
<div class="flex-shrink-0 flex items-center gap-0.5 sm:gap-2">
<!-- Desktop: Compare link -->
<NuxtLink
to="/compare"
:to="{ name: 'compare' }"
class="hidden sm:inline-flex link-subtle font-mono text-sm items-center gap-2 px-2 py-1.5 hover:bg-bg-subtle focus-visible:outline-accent/70 rounded"
aria-keyshortcuts="c"
>
Expand All @@ -167,7 +167,7 @@ onKeyStroke(

<!-- Desktop: Settings link -->
<NuxtLink
to="/settings"
:to="{ name: 'settings' }"
class="hidden sm:inline-flex link-subtle font-mono text-sm items-center gap-2 px-2 py-1.5 hover:bg-bg-subtle focus-visible:outline-accent/70 rounded"
aria-keyshortcuts=","
>
Expand Down
19 changes: 17 additions & 2 deletions app/components/Code/DirectoryListing.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
<script setup lang="ts">
import type { PackageFileTree } from '#shared/types'
import type { RouteLocationRaw } from 'vue-router'
import { getFileIcon } from '~/utils/file-icons'
import { formatBytes } from '~/utils/formatters'

const props = defineProps<{
tree: PackageFileTree[]
currentPath: string
baseUrl: string
/** Base path segments for the code route (e.g., ['nuxt', 'v', '4.2.0']) */
basePath: string[]
}>()

// Get the current directory's contents
Expand Down Expand Up @@ -36,6 +39,18 @@ const parentPath = computed(() => {
if (parts.length <= 1) return ''
return parts.slice(0, -1).join('/')
})

// Build route object for a path
function getCodeRoute(nodePath?: string): RouteLocationRaw {
if (!nodePath) {
return { name: 'code', params: { path: props.basePath as [string, ...string[]] } }
}
const pathSegments = [...props.basePath, ...nodePath.split('/')]
return {
name: 'code',
params: { path: pathSegments as [string, ...string[]] },
}
}
</script>

<template>
Expand All @@ -61,7 +76,7 @@ const parentPath = computed(() => {
>
<td class="py-2 px-4">
<NuxtLink
:to="parentPath ? `${baseUrl}/${parentPath}` : baseUrl"
:to="getCodeRoute(parentPath || undefined)"
class="flex items-center gap-2 font-mono text-sm text-fg-muted hover:text-fg transition-colors"
>
<span class="i-carbon:folder w-4 h-4 text-yellow-600" />
Expand All @@ -79,7 +94,7 @@ const parentPath = computed(() => {
>
<td class="py-2 px-4">
<NuxtLink
:to="`${baseUrl}/${node.path}`"
:to="getCodeRoute(node.path)"
class="flex items-center gap-2 font-mono text-sm hover:text-fg transition-colors"
:class="node.type === 'directory' ? 'text-fg' : 'text-fg-muted'"
>
Expand Down
15 changes: 14 additions & 1 deletion app/components/Code/FileTree.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<script setup lang="ts">
import type { PackageFileTree } from '#shared/types'
import type { RouteLocationRaw } from 'vue-router'
import { getFileIcon } from '~/utils/file-icons'
const props = defineProps<{
tree: PackageFileTree[]
currentPath: string
baseUrl: string
/** Base path segments for the code route (e.g., ['nuxt', 'v', '4.2.0']) */
basePath: string[]
depth?: number
}>()
Expand All @@ -18,6 +21,15 @@ function isNodeActive(node: PackageFileTree): boolean {
return false
}
// Build route object for a file path
function getFileRoute(nodePath: string): RouteLocationRaw {
const pathSegments = [...props.basePath, ...nodePath.split('/')]
return {
name: 'code',
params: { path: pathSegments as [string, ...string[]] },
}
}
const { toggleDir, isExpanded, autoExpandAncestors } = useFileTreeState(props.baseUrl)
// Auto-expand directories in the current path
Expand Down Expand Up @@ -63,14 +75,15 @@ watch(
:tree="node.children"
:current-path="currentPath"
:base-url="baseUrl"
:base-path="basePath"
:depth="depth + 1"
/>
</template>

<!-- File -->
<template v-else>
<NuxtLink
:to="`${baseUrl}/${node.path}`"
:to="getFileRoute(node.path)"
class="flex items-center gap-1.5 py-1.5 px-3 font-mono text-sm transition-colors hover:bg-bg-muted"
:class="currentPath === node.path ? 'bg-bg-muted text-fg' : 'text-fg-muted'"
:style="{ paddingLeft: `${depth * 12 + 32}px` }"
Expand Down
9 changes: 8 additions & 1 deletion app/components/Code/MobileTreeDrawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ defineProps<{
tree: PackageFileTree[]
currentPath: string
baseUrl: string
/** Base path segments for the code route (e.g., ['nuxt', 'v', '4.2.0']) */
basePath: string[]
}>()

const isOpen = shallowRef(false)
Expand Down Expand Up @@ -73,7 +75,12 @@ watch(isOpen, open => (isLocked.value = open))
<span class="i-carbon:close w-5 h-5" />
</button>
</div>
<CodeFileTree :tree="tree" :current-path="currentPath" :base-url="baseUrl" />
<CodeFileTree
:tree="tree"
:current-path="currentPath"
:base-url="baseUrl"
:base-path="basePath"
/>
</aside>
</Transition>
</template>
2 changes: 1 addition & 1 deletion app/components/Compare/ComparisonGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function getReplacementTooltip(col: ComparisonGridColumn): string {
>
<span class="inline-flex items-center gap-1.5 truncate">
<NuxtLink
:to="`/package/${col.header}`"
:to="packageRoute(col.header)"
class="link-subtle font-mono text-sm font-medium text-fg truncate"
:title="col.header"
>
Expand Down
2 changes: 1 addition & 1 deletion app/components/Compare/PackageSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ function handleBlur() {
</template>
<NuxtLink
v-else
:to="`/package/${pkg}`"
:to="packageRoute(pkg)"
class="font-mono text-sm text-fg hover:text-accent transition-colors"
>
{{ pkg }}
Expand Down
16 changes: 6 additions & 10 deletions app/components/DependencyPathPopup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,12 @@ function parsePackageString(pkg: string): { name: string; version: string } {
>
<span v-if="idx > 0" class="text-fg-subtle me-1">└─</span>
<NuxtLink
:to="{
name: 'package',
params: {
package: [
...parsePackageString(pathItem).name.split('/'),
'v',
parsePackageString(pathItem).version,
],
},
}"
:to="
packageRoute(
parsePackageString(pathItem).name,
parsePackageString(pathItem).version,
)
"
class="hover:underline"
:class="idx === path.length - 1 ? 'text-fg font-medium' : 'text-fg-muted'"
@click="closePopup"
Expand Down
12 changes: 6 additions & 6 deletions app/components/Header/MobileMenu.client.vue
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ onUnmounted(deactivate)
<!-- Main navigation -->
<div class="px-2 py-2">
<NuxtLink
to="/about"
:to="{ name: 'about' }"
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@click="closeMenu"
>
Expand All @@ -113,7 +113,7 @@ onUnmounted(deactivate)
</NuxtLink>

<NuxtLink
to="/privacy"
:to="{ name: 'privacy' }"
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@click="closeMenu"
>
Expand All @@ -122,7 +122,7 @@ onUnmounted(deactivate)
</NuxtLink>

<NuxtLink
to="/compare"
:to="{ name: 'compare' }"
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@click="closeMenu"
>
Expand All @@ -131,7 +131,7 @@ onUnmounted(deactivate)
</NuxtLink>

<NuxtLink
to="/settings"
:to="{ name: 'settings' }"
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@click="closeMenu"
>
Expand All @@ -142,7 +142,7 @@ onUnmounted(deactivate)
<!-- Connected user links -->
<template v-if="isConnected && npmUser">
<NuxtLink
:to="`/~${npmUser}`"
:to="{ name: '~username', params: { username: npmUser } }"
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@click="closeMenu"
>
Expand All @@ -151,7 +151,7 @@ onUnmounted(deactivate)
</NuxtLink>

<NuxtLink
:to="`/~${npmUser}/orgs`"
:to="{ name: '~username-orgs', params: { username: npmUser } }"
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@click="closeMenu"
>
Expand Down
6 changes: 3 additions & 3 deletions app/components/Header/OrgsDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function handleKeydown(event: KeyboardEvent) {
@keydown="handleKeydown"
>
<NuxtLink
:to="`/~${username}/orgs`"
:to="{ name: '~username-orgs', params: { username } }"
class="link-subtle font-mono text-sm inline-flex items-center gap-1"
>
{{ $t('header.orgs') }}
Expand Down Expand Up @@ -94,7 +94,7 @@ function handleKeydown(event: KeyboardEvent) {
<ul v-else-if="orgs.length > 0" class="py-1 max-h-80 overflow-y-auto">
<li v-for="org in orgs" :key="org">
<NuxtLink
:to="`/@${org}`"
:to="{ name: 'org', params: { org } }"
class="block px-3 py-2 font-mono text-sm text-fg hover:bg-bg-subtle transition-colors"
>
@{{ org }}
Expand All @@ -108,7 +108,7 @@ function handleKeydown(event: KeyboardEvent) {

<div class="px-3 py-2 border-t border-border">
<NuxtLink
:to="`/~${username}/orgs`"
:to="{ name: '~username-orgs', params: { username } }"
class="link-subtle font-mono text-xs inline-flex items-center gap-1"
>
{{ $t('header.orgs_dropdown.view_all') }}
Expand Down
6 changes: 3 additions & 3 deletions app/components/Header/PackagesDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function handleKeydown(event: KeyboardEvent) {
@keydown="handleKeydown"
>
<NuxtLink
:to="`/~${username}`"
:to="{ name: '~username', params: { username } }"
class="link-subtle font-mono text-sm inline-flex items-center gap-1"
>
{{ $t('header.packages') }}
Expand Down Expand Up @@ -94,7 +94,7 @@ function handleKeydown(event: KeyboardEvent) {
<ul v-else-if="packages.length > 0" class="py-1 max-h-80 overflow-y-auto">
<li v-for="pkg in packages" :key="pkg">
<NuxtLink
:to="`/package/${pkg}`"
:to="packageRoute(pkg)"
class="block px-3 py-2 font-mono text-sm text-fg hover:bg-bg-subtle transition-colors truncate"
>
{{ pkg }}
Expand All @@ -108,7 +108,7 @@ function handleKeydown(event: KeyboardEvent) {

<div class="px-3 py-2 border-t border-border">
<NuxtLink
:to="`/~${username}`"
:to="{ name: '~username', params: { username } }"
class="link-subtle font-mono text-xs inline-flex items-center gap-1"
>
{{ $t('header.packages_dropdown.view_all') }}
Expand Down
2 changes: 1 addition & 1 deletion app/components/Package/Card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const pkgDescription = useMarkdown(() => ({
class="font-mono text-sm sm:text-base font-medium text-fg group-hover:text-fg transition-colors duration-200 min-w-0 break-all"
>
<NuxtLink
:to="{ name: 'package', params: { package: result.package.name.split('/') } }"
:to="packageRoute(result.package.name)"
:prefetch-on="prefetch ? 'visibility' : 'interaction'"
class="decoration-none scroll-mt-48 scroll-mb-6 after:content-[''] after:absolute after:inset-0"
:data-result-index="index"
Expand Down
4 changes: 2 additions & 2 deletions app/components/Package/ClaimPackageModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ const previewPackageJson = computed(() => {

<div class="flex gap-3">
<NuxtLink
:to="`/package/${packageName}`"
:to="packageRoute(packageName)"
class="flex-1 px-4 py-2 font-mono text-sm text-center text-bg bg-fg rounded-md transition-colors duration-200 hover:bg-fg/90 focus-visible:outline-accent/70"
@click="close"
>
Expand Down Expand Up @@ -277,7 +277,7 @@ const previewPackageJson = computed(() => {
<span v-else class="w-4 h-4 shrink-0" />
<div class="min-w-0">
<NuxtLink
:to="`/package/${pkg.name}`"
:to="packageRoute(pkg.name)"
class="font-mono text-sm text-fg hover:underline focus-visible:outline-accent/70 rounded"
target="_blank"
>
Expand Down
Loading
Loading