Skip to content

Commit 03c706f

Browse files
committed
Merge main into feat/deps-vulnerability
2 parents ab53d74 + 20892fc commit 03c706f

25 files changed

Lines changed: 406 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
@@ -64,9 +64,26 @@ const sortedOptionalDependencies = computed(() => {
6464
<template>
6565
<div class="space-y-8">
6666
<!-- Dependencies -->
67-
<section v-if="sortedDependencies.length > 0" aria-labelledby="dependencies-heading">
68-
<h2 id="dependencies-heading" class="text-xs text-fg-subtle uppercase tracking-wider mb-3">
69-
{{ $t('package.dependencies.title', { count: sortedDependencies.length }) }}
67+
<section
68+
id="dependencies"
69+
v-if="sortedDependencies.length > 0"
70+
aria-labelledby="dependencies-heading"
71+
class="scroll-mt-20"
72+
>
73+
<h2
74+
id="dependencies-heading"
75+
class="group text-xs text-fg-subtle uppercase tracking-wider mb-3"
76+
>
77+
<a
78+
href="#dependencies"
79+
class="inline-flex items-center gap-1.5 text-fg-subtle hover:text-fg-muted transition-colors duration-200 no-underline"
80+
>
81+
{{ $t('package.dependencies.title', { count: sortedDependencies.length }) }}
82+
<span
83+
class="i-carbon-link w-3 h-3 block opacity-0 group-hover:opacity-100 transition-opacity duration-200"
84+
aria-hidden="true"
85+
/>
86+
</a>
7087
</h2>
7188
<ul class="space-y-1 list-none m-0 p-0" :aria-label="$t('package.dependencies.list_label')">
7289
<li
@@ -131,12 +148,26 @@ const sortedOptionalDependencies = computed(() => {
131148
</section>
132149

133150
<!-- Peer Dependencies -->
134-
<section v-if="sortedPeerDependencies.length > 0" aria-labelledby="peer-dependencies-heading">
151+
<section
152+
id="peer-dependencies"
153+
v-if="sortedPeerDependencies.length > 0"
154+
aria-labelledby="peer-dependencies-heading"
155+
class="scroll-mt-20"
156+
>
135157
<h2
136158
id="peer-dependencies-heading"
137-
class="text-xs text-fg-subtle uppercase tracking-wider mb-3"
159+
class="group text-xs text-fg-subtle uppercase tracking-wider mb-3"
138160
>
139-
{{ $t('package.peer_dependencies.title', { count: sortedPeerDependencies.length }) }}
161+
<a
162+
href="#peer-dependencies"
163+
class="inline-flex items-center gap-1.5 text-fg-subtle hover:text-fg-muted transition-colors duration-200 no-underline"
164+
>
165+
{{ $t('package.peer_dependencies.title', { count: sortedPeerDependencies.length }) }}
166+
<span
167+
class="i-carbon-link w-3 h-3 block opacity-0 group-hover:opacity-100 transition-opacity duration-200"
168+
aria-hidden="true"
169+
/>
170+
</a>
140171
</h2>
141172
<ul
142173
class="space-y-1 list-none m-0 p-0"
@@ -186,16 +217,27 @@ const sortedOptionalDependencies = computed(() => {
186217

187218
<!-- Optional Dependencies -->
188219
<section
220+
id="optional-dependencies"
189221
v-if="sortedOptionalDependencies.length > 0"
190222
aria-labelledby="optional-dependencies-heading"
223+
class="scroll-mt-20"
191224
>
192225
<h2
193226
id="optional-dependencies-heading"
194-
class="text-xs text-fg-subtle uppercase tracking-wider mb-3"
227+
class="group text-xs text-fg-subtle uppercase tracking-wider mb-3"
195228
>
196-
{{
197-
$t('package.optional_dependencies.title', { count: sortedOptionalDependencies.length })
198-
}}
229+
<a
230+
href="#optional-dependencies"
231+
class="inline-flex items-center gap-1.5 text-fg-subtle hover:text-fg-muted transition-colors duration-200 no-underline"
232+
>
233+
{{
234+
$t('package.optional_dependencies.title', { count: sortedOptionalDependencies.length })
235+
}}
236+
<span
237+
class="i-carbon-link w-3 h-3 block opacity-0 group-hover:opacity-100 transition-opacity duration-200"
238+
aria-hidden="true"
239+
/>
240+
</a>
199241
</h2>
200242
<ul
201243
class="space-y-1 list-none m-0 p-0"

app/components/PackageDownloadAnalytics.vue

Lines changed: 29 additions & 3 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
},
@@ -512,6 +531,13 @@ const config = computed(() => ({
512531
},
513532
zoom: {
514533
maxWidth: 500,
534+
customFormat:
535+
displayedGranularity.value !== 'weekly'
536+
? undefined
537+
: ({ absoluteIndex, side }: { absoluteIndex: number; side: 'left' | 'right' }) => {
538+
const parts = extractDates(chartData.value.dates[absoluteIndex] ?? '')
539+
return side === 'left' ? parts[0] : parts.at(-1)
540+
},
515541
highlightColor: isDarkMode.value ? '#2A2A2A' : '#E1E5E8',
516542
minimap: {
517543
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)