Skip to content

Commit 01d39ff

Browse files
authored
Merge branch 'main' into module-replacements-v3
2 parents e16308c + 32ae83d commit 01d39ff

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+2181
-723
lines changed

.storybook/main.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ const config = {
88
features: {
99
backgrounds: false,
1010
},
11-
async viteFinal(config) {
12-
config.plugins ??= []
11+
async viteFinal(newConfig) {
12+
newConfig.plugins ??= []
1313

14-
config.plugins.push({
14+
newConfig.plugins.push({
1515
name: 'ignore-internals',
1616
transform(_, id) {
1717
if (id.includes('/app/pages/blog/') && id.endsWith('.md')) {
@@ -23,7 +23,7 @@ const config = {
2323
// vue-docgen-api can crash on components that import types from other
2424
// .vue files (it tries to parse the SFC with @babel/parser as plain TS).
2525
// This wrapper catches those errors so the build doesn't fail.
26-
const docgenPlugin = config.plugins?.find(
26+
const docgenPlugin = newConfig.plugins?.find(
2727
(p): p is Extract<typeof p, { name: string }> =>
2828
!!p && typeof p === 'object' && 'name' in p && p.name === 'storybook:vue-docgen-plugin',
2929
)
@@ -48,7 +48,7 @@ const config = {
4848
}
4949
}
5050

51-
return config
51+
return newConfig
5252
},
5353
} satisfies StorybookConfig
5454

app/components/Compare/ComparisonGrid.vue

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const props = defineProps<{
1717
1818
/** Total column count including the optional no-dep column */
1919
const totalColumns = computed(() => props.columns.length + (props.showNoDependency ? 1 : 0))
20+
const visibleColumns = computed(() => Math.min(totalColumns.value, 4))
2021
2122
/** Compute plain-text tooltip for a replacement column */
2223
function getReplacementTooltip(col: ComparisonGridColumn): string {
@@ -30,32 +31,43 @@ function getReplacementTooltip(col: ComparisonGridColumn): string {
3031
<div class="overflow-x-auto">
3132
<div
3233
class="comparison-grid"
33-
:class="[totalColumns === 4 ? 'min-w-[800px]' : 'min-w-[600px]', `columns-${totalColumns}`]"
34-
:style="{ '--columns': totalColumns }"
34+
:style="{
35+
'--package-count': totalColumns,
36+
'--visible-columns': visibleColumns,
37+
}"
3538
>
3639
<!-- Header row -->
3740
<div class="comparison-header">
38-
<div class="comparison-label" />
41+
<div class="comparison-label relative bg-bg" />
3942

4043
<!-- Package columns -->
41-
<div v-for="col in columns" :key="col.name" class="comparison-cell comparison-cell-header">
42-
<span class="inline-flex items-center gap-1.5 truncate">
44+
<div
45+
v-for="col in columns"
46+
:key="col.name"
47+
class="comparison-cell comparison-cell-header min-w-0"
48+
>
49+
<div class="flex items-start justify-center gap-1.5 min-w-0">
4350
<LinkBase
4451
:to="packageRoute(col.name, col.version)"
45-
class="text-sm truncate"
46-
block
52+
class="flex min-w-0 flex-col items-center text-center text-sm"
4753
:title="col.version ? `${col.name}@${col.version}` : col.name"
4854
>
49-
{{ col.name }}<template v-if="col.version">@{{ col.version }}</template>
55+
<span class="min-w-0 break-words line-clamp-1">
56+
{{ col.name }}
57+
</span>
58+
<span v-if="col.version" class="text-fg-muted line-clamp-1">
59+
@{{ col.version }}
60+
</span>
5061
</LinkBase>
62+
5163
<TooltipApp v-if="col.replacement" :text="getReplacementTooltip(col)" position="bottom">
5264
<span
53-
class="i-lucide:lightbulb w-3.5 h-3.5 text-amber-500 shrink-0 cursor-help"
65+
class="i-lucide:lightbulb mt-0.5 h-3.5 w-3.5 shrink-0 cursor-help text-amber-500"
5466
role="img"
5567
:aria-label="$t('package.replacement.title')"
5668
/>
5769
</TooltipApp>
58-
</span>
70+
</div>
5971
</div>
6072

6173
<!-- "No dep" column (always last) -->
@@ -100,29 +112,30 @@ function getReplacementTooltip(col: ComparisonGridColumn): string {
100112

101113
<style scoped>
102114
.comparison-grid {
115+
--label-column-width: 140px;
116+
--package-column-width: calc((100% - var(--label-column-width)) / var(--visible-columns));
103117
display: grid;
104118
gap: 0;
105-
}
106-
107-
.comparison-grid.columns-2 {
108-
grid-template-columns: minmax(120px, 180px) repeat(2, 1fr);
109-
}
110-
111-
.comparison-grid.columns-3 {
112-
grid-template-columns: minmax(120px, 160px) repeat(3, 1fr);
113-
}
114-
115-
.comparison-grid.columns-4 {
116-
grid-template-columns: minmax(100px, 140px) repeat(4, 1fr);
119+
grid-template-columns:
120+
var(--label-column-width)
121+
repeat(var(--package-count), minmax(var(--package-column-width), var(--package-column-width)));
117122
}
118123
119124
.comparison-header {
120125
display: contents;
121126
}
122127
123128
.comparison-header > .comparison-label {
124-
padding: 0.75rem 1rem;
125-
border-bottom: 1px solid var(--color-border);
129+
z-index: 3;
130+
}
131+
132+
.comparison-label {
133+
position: sticky;
134+
left: 0;
135+
z-index: 2;
136+
inline-size: var(--label-column-width);
137+
min-inline-size: var(--label-column-width);
138+
isolation: isolate;
126139
}
127140
128141
.comparison-header > .comparison-cell-header {

app/components/Compare/FacetBarChart.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ const config = computed<VueUiHorizontalBarConfig>(() => {
178178
bold: false,
179179
color: colors.value.fg,
180180
value: {
181-
formatter: ({ config }) => {
182-
return config?.datapoint?.formattedValue ?? '0'
181+
formatter: ({ config: formatterConfig }) => {
182+
return formatterConfig?.datapoint?.formattedValue ?? '0'
183183
},
184184
},
185185
},

app/components/Compare/FacetRow.vue

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ function isCellLoading(index: number): boolean {
8888
<template>
8989
<div class="contents">
9090
<!-- Label cell -->
91-
<div class="comparison-label flex items-center gap-1.5 px-4 py-3 border-b border-border">
91+
<div
92+
class="comparison-label relative bg-bg flex items-center gap-1.5 px-4 py-3 border-b border-border"
93+
>
9294
<span class="text-xs text-fg-muted uppercase tracking-wider">{{ label }}</span>
9395
<TooltipApp v-if="description" :text="description" position="top">
9496
<span class="i-lucide:info w-3 h-3 text-fg-subtle cursor-help" aria-hidden="true" />
@@ -151,3 +153,13 @@ function isCellLoading(index: number): boolean {
151153
</div>
152154
</div>
153155
</template>
156+
157+
<style lang="css" scoped>
158+
.comparison-label {
159+
position: sticky;
160+
left: 0;
161+
z-index: 2;
162+
inline-size: var(--label-column-width);
163+
min-inline-size: var(--label-column-width);
164+
}
165+
</style>
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<script setup lang="ts">
2+
import type { IconClass } from '~/types'
3+
4+
const props = defineProps<{
5+
pkg: SlimPackument
6+
jsrInfo?: JsrPackageInfo
7+
}>()
8+
9+
const displayVersion = computed(() => props.pkg?.requestedVersion ?? null)
10+
const { repositoryUrl } = useRepositoryUrl(displayVersion)
11+
const { meta: repoMeta, repoRef, stars, starsLink, forks, forksLink } = useRepoMeta(repositoryUrl)
12+
const compactNumberFormatter = useCompactNumberFormatter()
13+
14+
const homepageUrl = computed(() => {
15+
const homepage = displayVersion.value?.homepage
16+
if (!homepage) return null
17+
18+
// Don't show homepage if it's the same as the repository URL
19+
if (repositoryUrl.value && areUrlsEquivalent(homepage, repositoryUrl.value)) {
20+
return null
21+
}
22+
23+
return homepage
24+
})
25+
26+
const fundingUrl = computed(() => {
27+
let funding = displayVersion.value?.funding
28+
if (Array.isArray(funding)) funding = funding[0]
29+
30+
if (!funding) return null
31+
32+
return typeof funding === 'string' ? funding : funding.url
33+
})
34+
35+
const PROVIDER_ICONS: Record<string, IconClass> = {
36+
github: 'i-simple-icons:github',
37+
gitlab: 'i-simple-icons:gitlab',
38+
bitbucket: 'i-simple-icons:bitbucket',
39+
codeberg: 'i-simple-icons:codeberg',
40+
gitea: 'i-simple-icons:gitea',
41+
forgejo: 'i-simple-icons:forgejo',
42+
gitee: 'i-simple-icons:gitee',
43+
sourcehut: 'i-simple-icons:sourcehut',
44+
tangled: 'i-custom:tangled',
45+
radicle: 'i-lucide:network', // Radicle is a P2P network, using network icon
46+
}
47+
48+
const repoProviderIcon = computed((): IconClass => {
49+
const provider = repoRef.value?.provider
50+
if (!provider) return 'i-simple-icons:github'
51+
return PROVIDER_ICONS[provider] ?? 'i-lucide:code'
52+
})
53+
</script>
54+
55+
<template>
56+
<ul class="flex flex-wrap items-center gap-x-3 gap-y-1.5 sm:gap-4 list-none m-0 p-0 mt-3 text-sm">
57+
<li v-if="repositoryUrl">
58+
<LinkBase :to="repositoryUrl" :classicon="repoProviderIcon">
59+
<span v-if="repoRef">
60+
{{ repoRef.owner }}<span class="opacity-50">/</span>{{ repoRef.repo }}
61+
</span>
62+
<span v-else>{{ $t('package.links.repo') }}</span>
63+
</LinkBase>
64+
</li>
65+
<li v-if="repositoryUrl && repoMeta && starsLink">
66+
<LinkBase :to="starsLink" classicon="i-lucide:star">
67+
{{ compactNumberFormatter.format(stars) }}
68+
</LinkBase>
69+
</li>
70+
<li v-if="forks && forksLink">
71+
<LinkBase :to="forksLink" classicon="i-lucide:git-fork">
72+
{{ compactNumberFormatter.format(forks) }}
73+
</LinkBase>
74+
</li>
75+
<li class="basis-full sm:hidden" />
76+
<li v-if="homepageUrl">
77+
<LinkBase :to="homepageUrl" classicon="i-lucide:link">
78+
{{ $t('package.links.homepage') }}
79+
</LinkBase>
80+
</li>
81+
<li v-if="displayVersion?.bugs?.url">
82+
<LinkBase :to="displayVersion.bugs.url" classicon="i-lucide:circle-alert">
83+
{{ $t('package.links.issues') }}
84+
</LinkBase>
85+
</li>
86+
<li>
87+
<LinkBase
88+
:to="`https://www.npmjs.com/package/${pkg.name}`"
89+
:title="$t('common.view_on.npm')"
90+
classicon="i-simple-icons:npm"
91+
>
92+
npm
93+
</LinkBase>
94+
</li>
95+
<li v-if="jsrInfo?.exists && jsrInfo.url">
96+
<LinkBase :to="jsrInfo.url" :title="$t('badges.jsr.title')" classicon="i-simple-icons:jsr">
97+
{{ $t('package.links.jsr') }}
98+
</LinkBase>
99+
</li>
100+
<li v-if="fundingUrl">
101+
<LinkBase :to="fundingUrl" classicon="i-lucide:heart">
102+
{{ $t('package.links.fund') }}
103+
</LinkBase>
104+
</li>
105+
</ul>
106+
</template>

app/components/Package/Header.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ const likeAction = async () => {
247247

248248
<template>
249249
<!-- Package header -->
250-
<header class="bg-bg pt-5 w-full container">
250+
<header class="bg-bg pt-5 pb-1 w-full container">
251251
<!-- Package name and version -->
252252
<div class="flex items-baseline justify-between gap-x-2 gap-y-1 flex-wrap min-w-0">
253253
<CopyToClipboardButton
@@ -319,7 +319,7 @@ const likeAction = async () => {
319319
</header>
320320
<div
321321
ref="header"
322-
class="w-full bg-bg sticky top-14 z-50 border-b border-border pt-2"
322+
class="w-full bg-bg sticky top-14 z-10 border-b border-border pt-2"
323323
:class="[$style.packageHeader]"
324324
data-testid="package-subheader"
325325
>
@@ -395,7 +395,7 @@ const likeAction = async () => {
395395
v-if="mainLink"
396396
:to="mainLink"
397397
aria-keyshortcuts="m"
398-
class="decoration-none border-b-2 p-1 hover:border-accent/50 lowercase"
398+
class="decoration-none border-b-2 p-1 hover:border-accent/50 lowercase focus-visible:[outline-offset:-2px]!"
399399
:class="page === 'main' ? 'border-accent text-accent!' : 'border-transparent'"
400400
>
401401
{{ $t('package.links.main') }}
@@ -404,7 +404,7 @@ const likeAction = async () => {
404404
v-if="docsLink"
405405
:to="docsLink"
406406
aria-keyshortcuts="d"
407-
class="decoration-none border-b-2 p-1 hover:border-accent/50"
407+
class="decoration-none border-b-2 p-1 hover:border-accent/50 focus-visible:[outline-offset:-2px]!"
408408
:class="page === 'docs' ? 'border-accent text-accent!' : 'border-transparent'"
409409
>
410410
{{ $t('package.links.docs') }}
@@ -413,7 +413,7 @@ const likeAction = async () => {
413413
v-if="codeLink"
414414
:to="codeLink"
415415
aria-keyshortcuts="."
416-
class="decoration-none border-b-2 p-1 hover:border-accent/50"
416+
class="decoration-none border-b-2 p-1 hover:border-accent/50 focus-visible:[outline-offset:-2px]!"
417417
:class="page === 'code' ? 'border-accent text-accent!' : 'border-transparent'"
418418
>
419419
{{ $t('package.links.code') }}
@@ -423,7 +423,7 @@ const likeAction = async () => {
423423
:to="diffLink"
424424
:title="$t('compare.compare_versions_title')"
425425
aria-keyshortcuts="f"
426-
class="decoration-none border-b-2 p-1 hover:border-accent/50"
426+
class="decoration-none border-b-2 p-1 hover:border-accent/50 focus-visible:[outline-offset:-2px]!"
427427
:class="page === 'diff' ? 'border-accent text-accent!' : 'border-transparent'"
428428
>
429429
{{ $t('compare.compare_versions') }}

app/components/Package/Versions.vue

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ function versionRoute(version: string): RouteLocationRaw {
9595
return packageRoute(props.packageName, version)
9696
}
9797
98+
// Route to the full versions history page
99+
const versionsPageRoute = computed((): RouteLocationRaw => {
100+
const [org, name = ''] = props.packageName.startsWith('@')
101+
? props.packageName.split('/')
102+
: ['', props.packageName]
103+
return { name: 'package-versions', params: { org, name } }
104+
})
105+
98106
// Version to tags lookup (supports multiple tags per version)
99107
const versionToTags = computed(() => buildVersionToTagsMap(props.distTags))
100108
@@ -521,15 +529,27 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
521529
id="versions"
522530
>
523531
<template #actions>
524-
<ButtonBase
525-
variant="secondary"
526-
class="text-fg-subtle hover:text-fg transition-colors min-w-6 min-h-6 -m-1 p-1 rounded"
527-
:title="$t('package.downloads.community_distribution')"
528-
classicon="i-lucide:file-stack"
529-
@click="openDistributionModal"
530-
>
531-
<span class="sr-only">{{ $t('package.downloads.community_distribution') }}</span>
532-
</ButtonBase>
532+
<div class="flex items-center gap-3">
533+
<LinkBase
534+
:to="versionsPageRoute"
535+
variant="button-secondary"
536+
class="text-fg-subtle hover:text-fg transition-colors min-w-6 min-h-6 p-1 rounded"
537+
:title="$t('package.versions.view_all_versions')"
538+
classicon="i-lucide:history"
539+
data-testid="view-all-versions-link"
540+
>
541+
<span class="sr-only">{{ $t('package.versions.view_all_versions') }}</span>
542+
</LinkBase>
543+
<ButtonBase
544+
variant="secondary"
545+
class="text-fg-subtle hover:text-fg transition-colors min-w-6 min-h-6 -m-1 p-1 rounded"
546+
:title="$t('package.downloads.community_distribution')"
547+
classicon="i-lucide:file-stack"
548+
@click="openDistributionModal"
549+
>
550+
<span class="sr-only">{{ $t('package.downloads.community_distribution') }}</span>
551+
</ButtonBase>
552+
</div>
533553
</template>
534554
<div class="space-y-0.5 min-w-0">
535555
<!-- Semver range filter -->

0 commit comments

Comments
 (0)