Skip to content

Commit 6f25aa2

Browse files
committed
Merge remote-tracking branch 'origin/main' into lunaria
2 parents 8f43a43 + e547675 commit 6f25aa2

25 files changed

Lines changed: 405 additions & 107 deletions

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text eol=lf
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: chore
2+
3+
on:
4+
pull_request_target:
5+
types:
6+
- opened
7+
- edited
8+
- synchronize
9+
10+
permissions: {}
11+
12+
jobs:
13+
semantic-pr:
14+
permissions:
15+
contents: read
16+
pull-requests: read # for amannn/action-semantic-pull-request to analyze PRs
17+
statuses: write # for amannn/action-semantic-pull-request to mark status of analyzed PR
18+
if: github.repository == 'npmx-dev/npmx.dev'
19+
runs-on: ubuntu-latest
20+
name: semantic-pr
21+
steps:
22+
- name: Validate PR title
23+
uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
24+
with:
25+
scopes: |
26+
docs
27+
i18n
28+
deps
29+
subjectPattern: ^(?![A-Z]).+$
30+
subjectPatternError: |
31+
The subject "{subject}" found in the pull request title "{title}"
32+
didn't match the configured pattern. Please ensure that the subject
33+
doesn't start with an uppercase character.
34+
env:
35+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

CONTRIBUTING.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,13 +365,27 @@ Make sure to read about [Playwright best practices](https://playwright.dev/docs/
365365
4. ensure CI checks pass (lint, type check, tests)
366366
5. request review from maintainers
367367

368-
### Commit messages
368+
### Commit messages and PR titles
369369

370-
Write clear, concise commit messages that explain the "why" behind changes:
370+
Write clear, concise PR titles that explain the "why" behind changes.
371+
372+
We use [Conventional Commits](https://www.conventionalcommits.org/). Since we squash on merge, the PR title becomes the commit message in `main`, so it's important to get it right.
373+
374+
Format: `type(scope): description`
375+
376+
**Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`
377+
378+
**Scopes (optional):** `docs`, `i18n`, `deps`
379+
380+
**Examples:**
371381

372382
- `fix: resolve search pagination issue`
373383
- `feat: add package version comparison`
374-
- `docs: update installation instructions`
384+
- `fix(i18n): update French translations`
385+
- `chore(deps): update vite to v6`
386+
387+
> [!NOTE]
388+
> The subject must start with a lowercase letter. Individual commit messages within your PR don't need to follow this format since they'll be squashed.
375389
376390
## Pre-commit hooks
377391

app/assets/main.css

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,16 @@ button {
157157
top: 0;
158158
}
159159

160+
/* Shiki theme colors */
161+
html.light .shiki,
162+
html.light .shiki span {
163+
color: var(--shiki-light) !important;
164+
background-color: var(--shiki-light-bg) !important;
165+
font-style: var(--shiki-light-font-style) !important;
166+
font-weight: var(--shiki-light-font-weight) !important;
167+
text-decoration: var(--shiki-light-text-decoration) !important;
168+
}
169+
160170
/* README prose styling */
161171
.readme-content {
162172
color: var(--fg-muted);
@@ -238,7 +248,7 @@ button {
238248
.readme-content pre,
239249
.readme-content .shiki {
240250
background: oklch(0.145 0 0) !important;
241-
border: 1px solid oklch(0.2686 0 0);
251+
border: 1px solid var(--border);
242252
border-radius: 8px;
243253
padding: 1rem;
244254
overflow-x: auto;

app/components/AppFooter.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ onMounted(() => {
8989
<!-- On mobile, show disclaimer here instead of tagline -->
9090
<p class="text-xs text-fg-muted m-0 sm:hidden">{{ $t('non_affiliation_disclaimer') }}</p>
9191
<div class="flex items-center gap-4 sm:gap-6">
92+
<a
93+
href="https://docs.npmx.dev"
94+
target="_blank"
95+
rel="noopener noreferrer"
96+
class="link-subtle font-mono text-xs min-h-11 min-w- flex items-center"
97+
>
98+
{{ $t('footer.docs') }}
99+
</a>
92100
<a
93101
href="https://repo.npmx.dev"
94102
target="_blank"

app/components/ClaimPackageModal.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ const connectorModalOpen = shallowRef(false)
160160
<button
161161
type="button"
162162
class="text-fg-subtle hover:text-fg transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 rounded"
163-
:aria-label="$t('claim.modal.close')"
163+
:aria-label="$t('common.close')"
164164
@click="open = false"
165165
>
166166
<span class="i-carbon-close block w-5 h-5" aria-hidden="true" />
@@ -203,7 +203,7 @@ const connectorModalOpen = shallowRef(false)
203203
class="flex-1 px-4 py-2 font-mono text-sm text-fg-muted bg-bg-subtle border border-border rounded-md transition-colors duration-200 hover:text-fg hover:border-border-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
204204
@click="open = false"
205205
>
206-
{{ $t('claim.modal.close') }}
206+
{{ $t('common.close') }}
207207
</button>
208208
</div>
209209
</div>
@@ -395,7 +395,7 @@ const connectorModalOpen = shallowRef(false)
395395
class="w-full px-4 py-2 font-mono text-sm text-fg-muted bg-bg-subtle border border-border rounded-md transition-colors duration-200 hover:text-fg hover:border-border-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
396396
@click="open = false"
397397
>
398-
{{ $t('claim.modal.close') }}
398+
{{ $t('common.close') }}
399399
</button>
400400
</div>
401401

@@ -412,7 +412,7 @@ const connectorModalOpen = shallowRef(false)
412412
class="w-full px-4 py-2 font-mono text-sm text-fg-muted bg-bg-subtle border border-border rounded-md transition-colors duration-200 hover:text-fg hover:border-border-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
413413
@click="checkAvailability"
414414
>
415-
{{ $t('claim.modal.retry') }}
415+
{{ $t('common.retry') }}
416416
</button>
417417
</div>
418418
</div>

app/components/ConnectorModal.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ watch(open, isOpen => {
8383
<button
8484
type="button"
8585
class="text-fg-subtle hover:text-fg transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 rounded"
86-
:aria-label="$t('connector.modal.close')"
86+
:aria-label="$t('common.close')"
8787
@click="open = false"
8888
>
8989
<span class="i-carbon-close block w-5 h-5" aria-hidden="true" />

app/components/PackageDependencies.vue

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,26 @@ const sortedOptionalDependencies = computed(() => {
4848
<template>
4949
<div class="space-y-8">
5050
<!-- Dependencies -->
51-
<section v-if="sortedDependencies.length > 0" aria-labelledby="dependencies-heading">
52-
<h2 id="dependencies-heading" class="text-xs text-fg-subtle uppercase tracking-wider mb-3">
53-
{{ $t('package.dependencies.title', { count: sortedDependencies.length }) }}
51+
<section
52+
id="dependencies"
53+
v-if="sortedDependencies.length > 0"
54+
aria-labelledby="dependencies-heading"
55+
class="scroll-mt-20"
56+
>
57+
<h2
58+
id="dependencies-heading"
59+
class="group text-xs text-fg-subtle uppercase tracking-wider mb-3"
60+
>
61+
<a
62+
href="#dependencies"
63+
class="inline-flex items-center gap-1.5 text-fg-subtle hover:text-fg-muted transition-colors duration-200 no-underline"
64+
>
65+
{{ $t('package.dependencies.title', { count: sortedDependencies.length }) }}
66+
<span
67+
class="i-carbon-link w-3 h-3 block opacity-0 group-hover:opacity-100 transition-opacity duration-200"
68+
aria-hidden="true"
69+
/>
70+
</a>
5471
</h2>
5572
<ul class="space-y-1 list-none m-0 p-0" :aria-label="$t('package.dependencies.list_label')">
5673
<li
@@ -99,12 +116,26 @@ const sortedOptionalDependencies = computed(() => {
99116
</section>
100117

101118
<!-- Peer Dependencies -->
102-
<section v-if="sortedPeerDependencies.length > 0" aria-labelledby="peer-dependencies-heading">
119+
<section
120+
id="peer-dependencies"
121+
v-if="sortedPeerDependencies.length > 0"
122+
aria-labelledby="peer-dependencies-heading"
123+
class="scroll-mt-20"
124+
>
103125
<h2
104126
id="peer-dependencies-heading"
105-
class="text-xs text-fg-subtle uppercase tracking-wider mb-3"
127+
class="group text-xs text-fg-subtle uppercase tracking-wider mb-3"
106128
>
107-
{{ $t('package.peer_dependencies.title', { count: sortedPeerDependencies.length }) }}
129+
<a
130+
href="#peer-dependencies"
131+
class="inline-flex items-center gap-1.5 text-fg-subtle hover:text-fg-muted transition-colors duration-200 no-underline"
132+
>
133+
{{ $t('package.peer_dependencies.title', { count: sortedPeerDependencies.length }) }}
134+
<span
135+
class="i-carbon-link w-3 h-3 block opacity-0 group-hover:opacity-100 transition-opacity duration-200"
136+
aria-hidden="true"
137+
/>
138+
</a>
108139
</h2>
109140
<ul
110141
class="space-y-1 list-none m-0 p-0"
@@ -154,16 +185,27 @@ const sortedOptionalDependencies = computed(() => {
154185

155186
<!-- Optional Dependencies -->
156187
<section
188+
id="optional-dependencies"
157189
v-if="sortedOptionalDependencies.length > 0"
158190
aria-labelledby="optional-dependencies-heading"
191+
class="scroll-mt-20"
159192
>
160193
<h2
161194
id="optional-dependencies-heading"
162-
class="text-xs text-fg-subtle uppercase tracking-wider mb-3"
195+
class="group text-xs text-fg-subtle uppercase tracking-wider mb-3"
163196
>
164-
{{
165-
$t('package.optional_dependencies.title', { count: sortedOptionalDependencies.length })
166-
}}
197+
<a
198+
href="#optional-dependencies"
199+
class="inline-flex items-center gap-1.5 text-fg-subtle hover:text-fg-muted transition-colors duration-200 no-underline"
200+
>
201+
{{
202+
$t('package.optional_dependencies.title', { count: sortedOptionalDependencies.length })
203+
}}
204+
<span
205+
class="i-carbon-link w-3 h-3 block opacity-0 group-hover:opacity-100 transition-opacity duration-200"
206+
aria-hidden="true"
207+
/>
208+
</a>
167209
</h2>
168210
<ul
169211
class="space-y-1 list-none m-0 p-0"

app/components/PackageDownloadAnalytics.vue

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,16 @@ function safeMax(a: string, b: string): string {
191191
return a.localeCompare(b) >= 0 ? a : b
192192
}
193193
194+
function extractDates(dateLabel: string) {
195+
if (typeof dateLabel !== 'string') return []
196+
197+
const parts = dateLabel.trim().split(/\s+/).filter(Boolean)
198+
199+
if (parts.length < 2) return []
200+
201+
return [parts[0], parts[parts.length - 1]]
202+
}
203+
194204
/**
195205
* Two-phase state:
196206
* - selectedGranularity: immediate UI
@@ -448,17 +458,26 @@ const config = computed(() => ({
448458
},
449459
callbacks: {
450460
img: ({ imageUri }: { imageUri: string }) => {
451-
loadFile(imageUri, `${packageName}-${selectedGranularity.value}.png`)
461+
loadFile(
462+
imageUri,
463+
`${packageName}-${selectedGranularity.value}_${startDate.value}_${endDate.value}.png`,
464+
)
452465
},
453466
csv: (csvStr: string) => {
454467
const blob = new Blob([csvStr.replace('data:text/csv;charset=utf-8,', '')])
455468
const url = URL.createObjectURL(blob)
456-
loadFile(url, `${packageName}-${selectedGranularity.value}.csv`)
469+
loadFile(
470+
url,
471+
`${packageName}-${selectedGranularity.value}_${startDate.value}_${endDate.value}.csv`,
472+
)
457473
URL.revokeObjectURL(url)
458474
},
459475
svg: ({ blob }: { blob: Blob }) => {
460476
const url = URL.createObjectURL(blob)
461-
loadFile(url, `${packageName}-${selectedGranularity.value}.svg`)
477+
loadFile(
478+
url,
479+
`${packageName}-${selectedGranularity.value}_${startDate.value}_${endDate.value}.svg`,
480+
)
462481
URL.revokeObjectURL(url)
463482
},
464483
},
@@ -468,7 +487,9 @@ const config = computed(() => ({
468487
stroke: isDarkMode.value ? '#4A4A4A' : '#a3a3a3',
469488
labels: {
470489
axis: {
471-
yLabel: $t('package.downloads.y_axis_label', { granularity: selectedGranularity.value }),
490+
yLabel: $t('package.downloads.y_axis_label', {
491+
granularity: $t(`package.downloads.granularity_${selectedGranularity.value}`),
492+
}),
472493
xLabel: packageName,
473494
yLabelOffsetX: 12,
474495
fontSize: 24,
@@ -512,6 +533,13 @@ const config = computed(() => ({
512533
},
513534
zoom: {
514535
maxWidth: 500,
536+
customFormat:
537+
displayedGranularity.value !== 'weekly'
538+
? undefined
539+
: ({ absoluteIndex, side }: { absoluteIndex: number; side: 'left' | 'right' }) => {
540+
const parts = extractDates(chartData.value.dates[absoluteIndex] ?? '')
541+
return side === 'left' ? parts[0] : parts.at(-1)
542+
},
515543
highlightColor: isDarkMode.value ? '#2A2A2A' : '#E1E5E8',
516544
minimap: {
517545
show: true,

app/components/PackageMaintainers.vue

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,23 @@ watch(
153153
</script>
154154

155155
<template>
156-
<section v-if="maintainers?.length" aria-labelledby="maintainers-heading">
157-
<h2 id="maintainers-heading" class="text-xs text-fg-subtle uppercase tracking-wider mb-3">
158-
{{ $t('package.maintainers.title') }}
156+
<section
157+
id="maintainers"
158+
v-if="maintainers?.length"
159+
aria-labelledby="maintainers-heading"
160+
class="scroll-mt-20"
161+
>
162+
<h2 id="maintainers-heading" class="group text-xs text-fg-subtle uppercase tracking-wider mb-3">
163+
<a
164+
href="#maintainers"
165+
class="inline-flex items-center gap-1.5 text-fg-subtle hover:text-fg-muted transition-colors duration-200 no-underline"
166+
>
167+
{{ $t('package.maintainers.title') }}
168+
<span
169+
class="i-carbon-link w-3 h-3 block opacity-0 group-hover:opacity-100 transition-opacity duration-200"
170+
aria-hidden="true"
171+
/>
172+
</a>
159173
</h2>
160174
<ul class="space-y-2 list-none m-0 p-0" :aria-label="$t('package.maintainers.list_label')">
161175
<li

0 commit comments

Comments
 (0)