Skip to content

Commit 63e7259

Browse files
authored
Merge branch 'main' into feat/display-author-profile-picture
2 parents 766f6bf + 6af7618 commit 63e7259

33 files changed

+455
-548
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ import { hasProtocol } from 'ufo'
187187

188188
| Type | Convention | Example |
189189
| ---------------- | ------------------------ | ------------------------------ |
190-
| Vue components | PascalCase | `MarkdownText.vue` |
190+
| Vue components | PascalCase | `DateTime.vue` |
191191
| Pages | kebab-case | `search.vue`, `[...name].vue` |
192192
| Composables | camelCase + `use` prefix | `useNpmRegistry.ts` |
193193
| Server routes | kebab-case + method | `search.get.ts` |

app/components/Package/Card.vue

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ const isExactMatch = computed(() => {
1919
const name = props.result.package.name.toLowerCase()
2020
return query === name
2121
})
22+
23+
// Process package description
24+
const pkgDescription = useMarkdown(() => ({
25+
text: props.result.package.description ?? '',
26+
plain: true,
27+
packageName: props.result.package.name,
28+
}))
2229
</script>
2330

2431
<template>
@@ -74,11 +81,8 @@ const isExactMatch = computed(() => {
7481
</div>
7582
<div class="flex justify-start items-start gap-4 sm:gap-8">
7683
<div class="min-w-0">
77-
<p
78-
v-if="result.package.description"
79-
class="text-fg-muted text-xs sm:text-sm line-clamp-2 mb-2 sm:mb-3"
80-
>
81-
<MarkdownText :text="result.package.description" plain />
84+
<p v-if="pkgDescription" class="text-fg-muted text-xs sm:text-sm line-clamp-2 mb-2 sm:mb-3">
85+
<span v-html="pkgDescription" />
8286
</p>
8387
<div class="flex flex-wrap items-center gap-x-3 sm:gap-x-4 gap-y-2 text-xs text-fg-subtle">
8488
<dl v-if="showPublisher || result.package.date" class="flex items-center gap-4 m-0">

app/components/Package/DeprecatedTree.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ function getDepthStyle(depth: DependencyDepth) {
8888
name: 'package',
8989
params: { package: [...pkg.name.split('/'), 'v', pkg.version] },
9090
}"
91-
class="font-mono text-sm font-medium hover:underline truncate"
91+
class="font-mono text-sm font-medium hover:underline truncate py-4"
9292
:class="getDepthStyle(pkg.depth).text"
9393
>
9494
{{ pkg.name }}@{{ pkg.version }}

app/components/Package/ListToolbar.vue

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,18 @@ function getSortKeyLabelKey(key: SortKey): string {
115115
>
116116
<template v-if="showingFiltered">
117117
{{
118-
$t('filters.count.showing_filtered', {
119-
filtered: filteredCount.toLocaleString(),
120-
count: totalCount.toLocaleString(),
121-
})
118+
$t(
119+
'filters.count.showing_filtered',
120+
{
121+
filtered: $n(filteredCount),
122+
count: $n(totalCount),
123+
},
124+
totalCount,
125+
)
122126
}}
123127
</template>
124128
<template v-else>
125-
{{ $t('filters.count.showing_all', { count: totalCount.toLocaleString() }) }}
129+
{{ $t('filters.count.showing_all', { count: $n(totalCount) }, totalCount) }}
126130
</template>
127131
</div>
128132

@@ -132,10 +136,14 @@ function getSortKeyLabelKey(key: SortKey): string {
132136
class="text-sm font-mono text-fg-muted"
133137
>
134138
{{
135-
$t('filters.count.showing_paginated', {
136-
pageSize: pageSize === 'all' ? filteredCount : pageSize,
137-
count: filteredCount.toLocaleString(),
138-
})
139+
$t(
140+
'filters.count.showing_paginated',
141+
{
142+
pageSize: pageSize === 'all' ? $n(filteredCount) : pageSize,
143+
count: $n(filteredCount),
144+
},
145+
filteredCount,
146+
)
139147
}}
140148
</div>
141149

app/components/PaginationControls.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ function handlePageSizeChange(event: Event) {
181181
$t('filters.pagination.showing', {
182182
start: startItem,
183183
end: endItem,
184-
total: totalItems.toLocaleString(),
184+
total: $n(totalItems),
185185
})
186186
}}
187187
</span>
Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
<script setup lang="ts">
21
import { decodeHtmlEntities } from '~/utils/formatters'
32

4-
const props = defineProps<{
3+
interface UseMarkdownOptions {
54
text: string
65
/** When true, renders link text without the anchor tag (useful when inside another link) */
76
plain?: boolean
87
/** Package name to strip from the beginning of the description (if present) */
98
packageName?: string
10-
}>()
9+
}
10+
11+
/** @public */
12+
export function useMarkdown(options: MaybeRefOrGetter<UseMarkdownOptions>) {
13+
return computed(() => parseMarkdown(toValue(options)))
14+
}
1115

1216
// Strip markdown image badges from text
1317
function stripMarkdownImages(text: string): string {
@@ -22,7 +26,7 @@ function stripMarkdownImages(text: string): string {
2226
}
2327

2428
// Strip HTML tags and escape remaining HTML to prevent XSS
25-
function stripAndEscapeHtml(text: string): string {
29+
function stripAndEscapeHtml(text: string, packageName?: string): string {
2630
// First decode any HTML entities in the input
2731
let stripped = decodeHtmlEntities(text)
2832

@@ -33,13 +37,13 @@ function stripAndEscapeHtml(text: string): string {
3337
// Only match tags that start with a letter or / (to avoid matching things like "a < b > c")
3438
stripped = stripped.replace(/<\/?[a-z][^>]*>/gi, '')
3539

36-
if (props.packageName) {
40+
if (packageName) {
3741
// Trim first to handle leading/trailing whitespace from stripped HTML
3842
stripped = stripped.trim()
3943
// Collapse multiple whitespace into single space
4044
stripped = stripped.replace(/\s+/g, ' ')
4145
// Escape special regex characters in package name
42-
const escapedName = props.packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
46+
const escapedName = packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
4347
// Match package name at the start, optionally followed by: space, dash, colon, hyphen, or just space
4448
const namePattern = new RegExp(`^${escapedName}\\s*[-:—]?\\s*`, 'i')
4549
stripped = stripped.replace(namePattern, '').trim()
@@ -55,11 +59,11 @@ function stripAndEscapeHtml(text: string): string {
5559
}
5660

5761
// Parse simple inline markdown to HTML
58-
function parseMarkdown(text: string): string {
62+
function parseMarkdown({ text, packageName, plain }: UseMarkdownOptions): string {
5963
if (!text) return ''
6064

6165
// First strip HTML tags and escape remaining HTML
62-
let html = stripAndEscapeHtml(text)
66+
let html = stripAndEscapeHtml(text, packageName)
6367

6468
// Bold: **text** or __text__
6569
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
@@ -78,7 +82,7 @@ function parseMarkdown(text: string): string {
7882
// Links: [text](url) - only allow https, mailto
7983
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, text, url) => {
8084
// In plain mode, just render the link text without the anchor
81-
if (props.plain) {
85+
if (plain) {
8286
return text
8387
}
8488
const decodedUrl = url.replace(/&amp;/g, '&')
@@ -94,11 +98,3 @@ function parseMarkdown(text: string): string {
9498

9599
return html
96100
}
97-
98-
const html = computed(() => parseMarkdown(props.text))
99-
</script>
100-
101-
<template>
102-
<!-- eslint-disable-next-line vue/no-v-html -->
103-
<span v-html="html" />
104-
</template>

app/pages/[...package].vue

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ const displayVersion = computed(() => {
122122
return pkg.value.versions[latestTag] ?? null
123123
})
124124
125+
// Process package description
126+
const pkgDescription = useMarkdown(() => ({
127+
text: pkg.value?.description ?? '',
128+
packageName: pkg.value?.name,
129+
}))
130+
125131
//copy package name
126132
const { copied: copiedPkgName, copy: copyPkgName } = useClipboard({
127133
source: packageName,
@@ -160,6 +166,10 @@ const deprecationNotice = computed(() => {
160166
return { type: 'version' as const, message: displayVersion.value.deprecated }
161167
})
162168
169+
const deprecationNoticeMessage = useMarkdown(() => ({
170+
text: deprecationNotice.value?.message ?? '',
171+
}))
172+
163173
const sizeTooltip = computed(() => {
164174
const chunks = [
165175
displayVersion.value &&
@@ -563,8 +573,8 @@ function handleClick(event: MouseEvent) {
563573
<div class="mb-4">
564574
<!-- Description container with min-height to prevent CLS -->
565575
<div class="max-w-2xl min-h-[4.5rem]">
566-
<p v-if="pkg.description" class="text-fg-muted text-base m-0">
567-
<MarkdownText :text="pkg.description" :package-name="pkg.name" />
576+
<p v-if="pkgDescription" class="text-fg-muted text-base m-0">
577+
<span v-html="pkgDescription" />
568578
</p>
569579
<p v-else class="text-fg-subtle text-base m-0 italic">
570580
{{ $t('package.no_description') }}
@@ -713,8 +723,8 @@ function handleClick(event: MouseEvent) {
713723
: $t('package.deprecation.version')
714724
}}
715725
</h2>
716-
<p v-if="deprecationNotice.message" class="text-base m-0">
717-
<MarkdownText :text="deprecationNotice.message" />
726+
<p v-if="deprecationNoticeMessage" class="text-base m-0">
727+
<span v-html="deprecationNoticeMessage" />
718728
</p>
719729
<p v-else class="text-base m-0 italic">
720730
{{ $t('package.deprecation.no_reason') }}
@@ -969,7 +979,7 @@ function handleClick(event: MouseEvent) {
969979
<h2 id="readme-heading" class="group text-xs text-fg-subtle uppercase tracking-wider mb-4">
970980
<a
971981
href="#readme"
972-
class="inline-flex items-center gap-1.5 text-fg-subtle hover:text-fg-muted transition-colors duration-200 no-underline"
982+
class="inline-flex py-4 px-2 items-center gap-1.5 text-fg-subtle hover:text-fg-muted transition-colors duration-200 no-underline"
973983
>
974984
{{ $t('package.readme.title') }}
975985
<span

app/pages/about.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const { data: contributors, status: contributorsStatus } = useFetch<GitHubContri
5353

5454
<section class="prose prose-invert max-w-none space-y-8">
5555
<div>
56-
<h2 class="text-xs text-fg-subtle uppercase tracking-wider mb-4">
56+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
5757
{{ $t('about.what_we_are.title') }}
5858
</h2>
5959
<p class="text-fg-muted leading-relaxed mb-4">
@@ -82,7 +82,7 @@ const { data: contributors, status: contributorsStatus } = useFetch<GitHubContri
8282
</div>
8383

8484
<div>
85-
<h2 class="text-xs text-fg-subtle uppercase tracking-wider mb-4">
85+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
8686
{{ $t('about.what_we_are_not.title') }}
8787
</h2>
8888
<ul class="space-y-3 text-fg-muted list-none p-0">
@@ -167,8 +167,8 @@ const { data: contributors, status: contributorsStatus } = useFetch<GitHubContri
167167
</div>
168168

169169
<div>
170-
<h2 class="text-xs text-fg-subtle uppercase tracking-wider mb-4">
171-
{{ $t('about.contributors.title') }}
170+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
171+
{{ contributors?.length ?? 0 }} {{ $t('about.contributors.title') }}
172172
</h2>
173173
<p class="text-fg-muted leading-relaxed mb-6">
174174
{{ $t('about.contributors.description') }}
@@ -205,7 +205,7 @@ const { data: contributors, status: contributorsStatus } = useFetch<GitHubContri
205205

206206
<!-- Get Involved CTAs -->
207207
<div>
208-
<h2 class="text-xs text-fg-subtle uppercase tracking-wider mb-6">
208+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-6">
209209
{{ $t('about.get_involved.title') }}
210210
</h2>
211211

app/pages/search.vue

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -680,10 +680,15 @@ defineOgImageComponent('Default', {
680680
class="text-fg-muted text-sm mt-4 font-mono"
681681
>
682682
{{
683-
$t('filters.count.showing_paginated', {
684-
pageSize: preferredPageSize === 'all' ? visibleResults.total : preferredPageSize,
685-
count: visibleResults.total.toLocaleString(),
686-
})
683+
$t(
684+
'filters.count.showing_paginated',
685+
{
686+
pageSize:
687+
preferredPageSize === 'all' ? $n(visibleResults.total) : preferredPageSize,
688+
count: $n(visibleResults.total),
689+
},
690+
visibleResults.total,
691+
)
687692
}}
688693
</p>
689694
</div>

i18n/locales/ar.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -642,9 +642,9 @@
642642
"nav_label": "التصفح"
643643
},
644644
"count": {
645-
"showing_filtered": "{filtered} من {total} حزمة",
646-
"showing_all": "{total} حزمة | حزمة واحدة | حزمتان | {total} حزم | {total} حزمة | {total} حزمة",
647-
"showing_paginated": "{pageSize} من {total} حزمة"
645+
"showing_filtered": "{filtered} من {count} حزمة",
646+
"showing_all": "{count} حزمة | حزمة واحدة | حزمتان | {count} حزم | {count} حزمة | {count} حزمة",
647+
"showing_paginated": "{pageSize} من {count} حزمة"
648648
},
649649
"table": {
650650
"security_warning": "تحذير أمني",

0 commit comments

Comments
 (0)