Skip to content

Commit be43b8e

Browse files
authored
Merge branch 'main' into fix/replace-non-rtl-classes
2 parents 3329ade + f80f5a5 commit be43b8e

29 files changed

Lines changed: 299 additions & 41 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
<p align="center">
66
<a href="https://npmx.dev/">
7-
<img width="1090" alt="Screenshot of npmx.dev showing the nuxt package" src="https://github.com/user-attachments/assets/229497a2-8491-461c-aa1d-fba981215340">
7+
<img width="1090" alt="Screenshot of npmx.dev showing the nuxt package" src="https://github.com/user-attachments/assets/1a2a3205-0227-46dc-b1f9-48f9a65691d3">
88
</a>
99
</p>
1010

app/components/FilterPanel.vue

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,11 @@ const hasActiveFilters = computed(() => !!filterSummary.value)
250250
role="radio"
251251
:aria-checked="filters.downloadRange === range.value"
252252
class="tag transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1"
253-
:class="filters.downloadRange === range.value ? 'bg-fg text-bg border-fg' : ''"
253+
:class="
254+
filters.downloadRange === range.value
255+
? 'bg-fg text-bg border-fg hover:text-bg/50'
256+
: ''
257+
"
254258
@click="emit('update:downloadRange', range.value)"
255259
>
256260
{{ $t(getDownloadRangeLabelKey(range.value)) }}
@@ -275,7 +279,11 @@ const hasActiveFilters = computed(() => !!filterSummary.value)
275279
role="radio"
276280
:aria-checked="filters.updatedWithin === option.value"
277281
class="tag transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1"
278-
:class="filters.updatedWithin === option.value ? 'bg-fg text-bg border-fg' : ''"
282+
:class="
283+
filters.updatedWithin === option.value
284+
? 'bg-fg text-bg border-fg hover:text-bg/70'
285+
: ''
286+
"
279287
@click="emit('update:updatedWithin', option.value)"
280288
>
281289
{{ $t(getUpdatedWithinLabelKey(option.value)) }}
@@ -300,7 +308,9 @@ const hasActiveFilters = computed(() => !!filterSummary.value)
300308
disabled
301309
:aria-checked="filters.security === option.value"
302310
class="tag transition-colors duration-200 opacity-50 cursor-not-allowed focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1"
303-
:class="filters.security === option.value ? 'bg-fg text-bg border-fg' : ''"
311+
:class="
312+
filters.security === option.value ? 'bg-fg text-bg border-fg hover:text-bg/70' : ''
313+
"
304314
>
305315
{{ $t(getSecurityLabelKey(option.value)) }}
306316
</button>
@@ -319,7 +329,9 @@ const hasActiveFilters = computed(() => !!filterSummary.value)
319329
type="button"
320330
:aria-pressed="filters.keywords.includes(keyword)"
321331
class="tag text-xs transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1"
322-
:class="filters.keywords.includes(keyword) ? 'bg-fg text-bg border-fg' : ''"
332+
:class="
333+
filters.keywords.includes(keyword) ? 'bg-fg text-bg border-fg hover:text-bg/70' : ''
334+
"
323335
@click="emit('toggleKeyword', keyword)"
324336
>
325337
{{ keyword }}

app/components/PackageDownloadAnalytics.vue

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ watch(
4949
5050
const isDarkMode = computed(() => resolvedMode.value === 'dark')
5151
52-
// oklh or css variables are not supported by vue-data-ui (for now)
53-
5452
const accentColorValueById = computed<Record<string, string>>(() => {
5553
const map: Record<string, string> = {}
5654
for (const item of accentColors) {

app/components/PackageManagerTabs.vue

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,55 @@
11
<script setup lang="ts">
22
const selectedPM = useSelectedPackageManager()
3+
4+
const tablistNavigationKeys = new Set(['ArrowRight', 'ArrowLeft', 'Home', 'End'])
5+
6+
function onTabListKeydown(event: KeyboardEvent) {
7+
if (!tablistNavigationKeys.has(event.key)) return
8+
const tablist = event.currentTarget as HTMLElement | null
9+
if (!tablist) return
10+
11+
const tabs = Array.from(tablist.querySelectorAll<HTMLElement>('[role="tab"]'))
12+
const count = Math.min(tabs.length, packageManagers.length)
13+
if (!count) return
14+
15+
event.preventDefault()
16+
17+
let activeIndex = packageManagers.findIndex(pm => pm.id === selectedPM.value)
18+
if (activeIndex < 0) activeIndex = 0
19+
20+
let nextIndex = activeIndex
21+
if (event.key === 'ArrowRight') nextIndex = (activeIndex + 1) % count
22+
if (event.key === 'ArrowLeft') nextIndex = (activeIndex - 1 + count) % count
23+
if (event.key === 'Home') nextIndex = 0
24+
if (event.key === 'End') nextIndex = count - 1
25+
26+
const nextTab = tabs[nextIndex]
27+
const nextId = packageManagers[nextIndex]?.id
28+
if (nextId && nextId !== selectedPM.value) {
29+
selectedPM.value = nextId
30+
}
31+
32+
nextTick(() => nextTab?.focus())
33+
}
334
</script>
435

536
<template>
637
<div
738
class="flex items-center gap-1 p-0.5 bg-bg-subtle border border-border-subtle rounded-md overflow-x-auto"
839
role="tablist"
940
:aria-label="$t('package.get_started.pm_label')"
41+
@keydown="onTabListKeydown"
1042
>
1143
<button
1244
v-for="pm in packageManagers"
1345
:key="pm.id"
46+
:id="`pm-tab-${pm.id}`"
1447
role="tab"
1548
:data-pm-tab="pm.id"
1649
:aria-selected="selectedPM === pm.id"
50+
:aria-controls="`pm-panel-${pm.id}`"
51+
:tabindex="selectedPM === pm.id ? 0 : -1"
52+
type="button"
1753
class="pm-tab px-2 py-1.5 font-mono text-xs rounded transition-colors duration-150 border border-solid focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 inline-flex items-center gap-1.5 hover:text-fg"
1854
@click="selectedPM = pm.id"
1955
>

app/components/PackageWeeklyDownloadStats.vue

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,32 @@ const dataset = computed(() =>
116116
117117
const lastDatapoint = computed(() => dataset.value.at(-1)?.period ?? '')
118118
119-
// oklh or css variables are not supported by vue-data-ui (for now)
120119
const config = computed(() => {
121120
return {
122121
theme: 'dark',
122+
/**
123+
* The built-in skeleton loader kicks in when the component is mounted but the data is not yet ready.
124+
* The configuration of the skeleton is customized for a seemless transition with the final state
125+
*/
126+
skeletonConfig: {
127+
style: {
128+
backgroundColor: 'transparent',
129+
dataLabel: {
130+
show: true,
131+
color: 'transparent',
132+
},
133+
area: {
134+
color: colors.value.borderHover,
135+
useGradient: false,
136+
opacity: 10,
137+
},
138+
line: {
139+
color: colors.value.borderHover,
140+
},
141+
},
142+
},
143+
// Same idea: initialize the line at zero, so it nicely transitions to the final dataset
144+
skeletonDataset: Array.from({ length: 52 }, () => 0),
123145
style: {
124146
backgroundColor: 'transparent',
125147
animation: { show: false },
@@ -196,16 +218,21 @@ const config = computed(() => {
196218

197219
<div class="w-full overflow-hidden">
198220
<ClientOnly>
199-
<VueUiSparkline class="w-full max-w-xs" :dataset :config />
221+
<VueUiSparkline class="w-full max-w-xs" :dataset :config>
222+
<template #skeleton>
223+
<!-- This empty div overrides the default built-in scanning animation on load -->
224+
<div />
225+
</template>
226+
</VueUiSparkline>
200227
<template #fallback>
201228
<!-- Skeleton matching sparkline layout: title row + chart with data label -->
202-
<div class="max-w-xs">
203-
<!-- Title row: date range -->
204-
<div class="h-5 flex items-center ps-3">
229+
<div class="min-h-[75.195px]">
230+
<!-- Title row: date range (24px height) -->
231+
<div class="h-6 flex items-center ps-3">
205232
<span class="skeleton h-3 w-36" />
206233
</div>
207-
<!-- Chart area: data label left, sparkline right (h-[51px] matches rendered SVG) -->
208-
<div class="h-[55px] flex items-center">
234+
<!-- Chart area: data label left, sparkline right -->
235+
<div class="aspect-[500/80] flex items-center">
209236
<!-- Data label (covers ~42% width) -->
210237
<div class="w-[42%] flex items-center ps-0.5">
211238
<span class="skeleton h-7 w-24" />

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

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ definePageMeta({
1414
const router = useRouter()
1515
1616
const { packageName, requestedVersion, orgName } = usePackageRoute()
17+
const selectedPM = useSelectedPackageManager()
18+
const activePmId = computed(() => selectedPM.value ?? 'npm')
1719
1820
if (import.meta.server) {
1921
assertValidPackageName(packageName.value)
@@ -845,11 +847,17 @@ function handleClick(event: MouseEvent) {
845847
<!-- Package manager tabs -->
846848
<PackageManagerTabs />
847849
</div>
848-
<ExecuteCommandTerminal
849-
:package-name="pkg.name"
850-
:jsr-info="jsrInfo"
851-
:is-create-package="isCreatePkg"
852-
/>
850+
<div
851+
role="tabpanel"
852+
:id="`pm-panel-${activePmId}`"
853+
:aria-labelledby="`pm-tab-${activePmId}`"
854+
>
855+
<ExecuteCommandTerminal
856+
:package-name="pkg.name"
857+
:jsr-info="jsrInfo"
858+
:is-create-package="isCreatePkg"
859+
/>
860+
</div>
853861
</section>
854862

855863
<!-- Regular packages: Install command with optional run command -->
@@ -873,14 +881,20 @@ function handleClick(event: MouseEvent) {
873881
<!-- Package manager tabs -->
874882
<PackageManagerTabs />
875883
</div>
876-
<InstallCommandTerminal
877-
:package-name="pkg.name"
878-
:requested-version="requestedVersion"
879-
:jsr-info="jsrInfo"
880-
:types-package-name="typesPackageName"
881-
:executable-info="executableInfo"
882-
:create-package-info="createPackageInfo"
883-
/>
884+
<div
885+
role="tabpanel"
886+
:id="`pm-panel-${activePmId}`"
887+
:aria-labelledby="`pm-tab-${activePmId}`"
888+
>
889+
<InstallCommandTerminal
890+
:package-name="pkg.name"
891+
:requested-version="requestedVersion"
892+
:jsr-info="jsrInfo"
893+
:types-package-name="typesPackageName"
894+
:executable-info="executableInfo"
895+
:create-package-info="createPackageInfo"
896+
/>
897+
</div>
884898
</section>
885899

886900
<div class="area-vulns space-y-6">

app/pages/code/[...path].vue

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,22 @@ const canonicalUrl = computed(() => {
243243
return url
244244
})
245245
246+
// Toggle markdown view mode
247+
const markdownViewModes = [
248+
{
249+
key: 'preview',
250+
label: $t('code.markdown_view_mode.preview'),
251+
icon: 'i-carbon-view',
252+
},
253+
{
254+
key: 'code',
255+
label: $t('code.markdown_view_mode.code'),
256+
icon: 'i-carbon-code',
257+
},
258+
] as const
259+
260+
const markdownViewMode = ref<(typeof markdownViewModes)[number]['key']>('preview')
261+
246262
useHead({
247263
link: [{ rel: 'canonical', href: canonicalUrl }],
248264
})
@@ -359,15 +375,39 @@ useSeoMeta({
359375
<!-- File viewer -->
360376
<template v-if="isViewingFile && fileContent">
361377
<div
362-
class="sticky top-0 bg-bg border-b border-border px-4 py-2 flex items-center justify-between"
378+
class="sticky z-10 top-0 bg-bg border-b border-border px-4 py-2 flex items-center justify-between"
363379
>
364-
<div class="flex items-center gap-3 text-sm">
365-
<span class="text-fg-muted">{{
366-
$t('code.lines', { count: fileContent.lines })
367-
}}</span>
368-
<span v-if="currentNode?.size" class="text-fg-subtle">{{
369-
formatBytes(currentNode.size)
370-
}}</span>
380+
<div class="flex items-center gap-2">
381+
<div
382+
v-if="fileContent.markdownHtml"
383+
class="flex items-center gap-1 p-0.5 bg-bg-subtle border border-border-subtle rounded-md overflow-x-auto"
384+
role="tablist"
385+
aria-label="Markdown view mode selector"
386+
>
387+
<button
388+
v-for="mode in markdownViewModes"
389+
:key="mode.key"
390+
role="tab"
391+
class="px-2 py-1.5 font-mono text-xs rounded transition-colors duration-150 border border-solid focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 inline-flex items-center gap-1.5"
392+
:class="
393+
markdownViewMode === mode.key
394+
? 'bg-bg shadow text-fg border-border'
395+
: 'text-fg-subtle hover:text-fg border-transparent'
396+
"
397+
@click="markdownViewMode = mode.key"
398+
>
399+
<span class="inline-block h-3 w-3" :class="mode.icon" aria-hidden="true" />
400+
{{ mode.label }}
401+
</button>
402+
</div>
403+
<div class="flex items-center gap-3 text-sm">
404+
<span class="text-fg-muted">{{
405+
$t('code.lines', { count: fileContent.lines })
406+
}}</span>
407+
<span v-if="currentNode?.size" class="text-fg-subtle">{{
408+
formatBytes(currentNode.size)
409+
}}</span>
410+
</div>
371411
</div>
372412
<div class="flex items-center gap-2">
373413
<button
@@ -389,7 +429,19 @@ useSeoMeta({
389429
</a>
390430
</div>
391431
</div>
432+
<div
433+
v-if="fileContent.markdownHtml"
434+
v-show="markdownViewMode === 'preview'"
435+
class="flex justify-center p-4"
436+
>
437+
<div
438+
class="readme-content prose prose-invert max-w-[70ch]"
439+
v-html="fileContent.markdownHtml.html"
440+
></div>
441+
</div>
442+
392443
<CodeViewer
444+
v-show="!fileContent.markdownHtml || markdownViewMode === 'code'"
393445
:html="fileContent.html"
394446
:lines="fileContent.lines"
395447
:selected-lines="selectedLines"

i18n/locales/ar.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,10 @@
532532
"table": {
533533
"name": "الاسم",
534534
"size": "الحجم"
535+
},
536+
"markdown_view_mode": {
537+
"preview": "معاينة",
538+
"code": "الكود"
535539
}
536540
},
537541
"badges": {

i18n/locales/de-DE.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,10 @@
532532
"table": {
533533
"name": "Name",
534534
"size": "Größe"
535+
},
536+
"markdown_view_mode": {
537+
"preview": "Vorschau",
538+
"code": "Code"
535539
}
536540
},
537541
"badges": {

i18n/locales/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,10 @@
550550
"table": {
551551
"name": "Name",
552552
"size": "Size"
553+
},
554+
"markdown_view_mode": {
555+
"preview": "preview",
556+
"code": "code"
553557
}
554558
},
555559
"badges": {

0 commit comments

Comments
 (0)