Skip to content

Commit 865b70a

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/more-badges
# Conflicts: # server/api/registry/badge/[...pkg].get.ts
2 parents b85224b + 348de9c commit 865b70a

Some content is hidden

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

80 files changed

+2092
-1092
lines changed

app/app.vue

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ const localeMap = locales.value.reduce(
2121
{} as Record<string, Directions>,
2222
)
2323
24+
const darkMode = usePreferredDark()
25+
const colorMode = useColorMode()
26+
const colorScheme = computed(() => {
27+
return {
28+
system: darkMode ? 'dark light' : 'light dark',
29+
light: 'only light',
30+
dark: 'only dark',
31+
}[colorMode.preference]
32+
})
33+
2434
useHead({
2535
htmlAttrs: {
2636
'lang': () => locale.value,
@@ -30,6 +40,7 @@ useHead({
3040
titleTemplate: titleChunk => {
3141
return titleChunk ? titleChunk : 'npmx - Better npm Package Browser'
3242
},
43+
meta: [{ name: 'color-scheme', content: colorScheme }],
3344
})
3445
3546
if (import.meta.server) {

app/assets/main.css

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,32 @@
7575
--badge-cyan: oklch(0.571 0.181 210);
7676
}
7777

78+
@media (prefers-contrast: more) {
79+
:root[data-theme='dark'] {
80+
/* text colors */
81+
--fg: oklch(1 0 0);
82+
--fg-muted: oklch(0.769 0 0);
83+
--fg-subtle: oklch(0.693 0 0);
84+
85+
/* border, separator colors */
86+
--border: oklch(0.769 0 0);
87+
--border-subtle: oklch(0.739 0 0);
88+
--border-hover: oklch(0.771 0 0);
89+
}
90+
91+
:root[data-theme='light'] {
92+
/* text colors */
93+
--fg: oklch(0 0 0);
94+
--fg-muted: oklch(0.329 0 0);
95+
--fg-subtle: oklch(0.4 0 0);
96+
97+
/* border, separator colors */
98+
--border: oklch(0.3514 0 0);
99+
--border-subtle: oklch(0.422 0 0);
100+
--border-hover: oklch(0.315 0 0);
101+
}
102+
}
103+
78104
html {
79105
-webkit-font-smoothing: antialiased;
80106
-moz-osx-font-smoothing: grayscale;

app/components/CallToAction.vue

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<script setup lang="ts">
2+
const socialLinks = {
3+
github: 'https://repo.npmx.dev',
4+
discord: 'https://chat.npmx.dev',
5+
bluesky: 'https://social.npmx.dev',
6+
}
7+
</script>
8+
9+
<template>
10+
<div>
11+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-6">
12+
{{ $t('about.get_involved.title') }}
13+
</h2>
14+
15+
<div class="grid gap-4 sm:grid-cols-3">
16+
<a
17+
:href="socialLinks.github"
18+
target="_blank"
19+
rel="noopener noreferrer"
20+
class="group flex flex-col gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200"
21+
>
22+
<div class="flex gap-2">
23+
<span class="i-carbon:logo-github shrink-0 mt-1 w-5 h-5 text-fg" aria-hidden="true" />
24+
<span class="font-medium text-fg">
25+
{{ $t('about.get_involved.contribute.title') }}
26+
</span>
27+
</div>
28+
<p class="text-sm text-fg-muted leading-relaxed">
29+
{{ $t('about.get_involved.contribute.description') }}
30+
</p>
31+
<span
32+
class="text-sm text-fg-muted group-hover:text-fg inline-flex items-center gap-1 mt-auto"
33+
>
34+
{{ $t('about.get_involved.contribute.cta') }}
35+
<span class="i-carbon:arrow-right rtl-flip w-3 h-3" aria-hidden="true" />
36+
</span>
37+
</a>
38+
39+
<a
40+
:href="socialLinks.discord"
41+
target="_blank"
42+
rel="noopener noreferrer"
43+
class="group flex flex-col gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200"
44+
>
45+
<div class="flex gap-2">
46+
<span class="i-carbon:chat shrink-0 mt-1 w-5 h-5 text-fg" aria-hidden="true" />
47+
<span class="font-medium text-fg">
48+
{{ $t('about.get_involved.community.title') }}
49+
</span>
50+
</div>
51+
<p class="text-sm text-fg-muted leading-relaxed">
52+
{{ $t('about.get_involved.community.description') }}
53+
</p>
54+
<span
55+
class="text-sm text-fg-muted group-hover:text-fg inline-flex items-center gap-1 mt-auto"
56+
>
57+
{{ $t('about.get_involved.community.cta') }}
58+
<span class="i-carbon:arrow-right rtl-flip w-3 h-3" aria-hidden="true" />
59+
</span>
60+
</a>
61+
62+
<a
63+
:href="socialLinks.bluesky"
64+
target="_blank"
65+
rel="noopener noreferrer"
66+
class="group flex flex-col gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200"
67+
>
68+
<div class="flex gap-2">
69+
<span class="i-simple-icons:bluesky shrink-0 mt-1 w-5 h-5 text-fg" aria-hidden="true" />
70+
<span class="font-medium text-fg">
71+
{{ $t('about.get_involved.follow.title') }}
72+
</span>
73+
</div>
74+
<p class="text-sm text-fg-muted leading-relaxed">
75+
{{ $t('about.get_involved.follow.description') }}
76+
</p>
77+
<span
78+
class="text-sm text-fg-muted group-hover:text-fg inline-flex items-center gap-1 mt-auto"
79+
>
80+
{{ $t('about.get_involved.follow.cta') }}
81+
<span class="i-carbon:arrow-right rtl-flip w-3 h-3" aria-hidden="true" />
82+
</span>
83+
</a>
84+
</div>
85+
</div>
86+
</template>

app/components/Header/SearchBox.vue

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup lang="ts">
22
import { debounce } from 'perfect-debounce'
3+
import { normalizeSearchParam } from '#shared/utils/url'
34
45
withDefaults(
56
defineProps<{
@@ -22,9 +23,7 @@ const showSearchBar = computed(() => {
2223
})
2324
2425
// Local input value (updates immediately as user types)
25-
const searchQuery = shallowRef(
26-
(Array.isArray(route.query.q) ? route.query.q[0] : route.query.q) ?? '',
27-
)
26+
const searchQuery = shallowRef(normalizeSearchParam(route.query.q))
2827
2928
// Pages that have their own local filter using ?q
3029
const pagesWithLocalFilter = new Set(['~username', 'org'])
@@ -64,7 +63,7 @@ watch(
6463
if (pagesWithLocalFilter.has(route.name as string)) {
6564
return
6665
}
67-
const value = (urlQuery as string) ?? ''
66+
const value = normalizeSearchParam(urlQuery)
6867
if (searchQuery.value !== value) {
6968
searchQuery.value = value
7069
}

app/components/Package/Card.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const pkgDescription = useMarkdown(() => ({
3232
<article
3333
class="group card-interactive scroll-mt-48 scroll-mb-6 relative focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-bg focus-within:ring-offset-2 focus-within:ring-fg/50 focus-within:bg-bg-muted focus-within:border-border-hover"
3434
:class="{
35-
'border-accent/30 bg-accent/5': isExactMatch,
35+
'border-accent/30 contrast-more:border-accent/90 bg-accent/5': isExactMatch,
3636
}"
3737
>
3838
<!-- Glow effect for exact matches -->

app/components/Package/DownloadAnalytics.vue

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,9 @@ const rootEl = shallowRef<HTMLElement | null>(null)
1919
2020
const { width } = useElementSize(rootEl)
2121
22-
const chartKey = ref(0)
23-
24-
let chartRemountTimeoutId: ReturnType<typeof setTimeout> | null = null
25-
2622
onMounted(() => {
2723
rootEl.value = document.documentElement
2824
resolvedMode.value = colorMode.value === 'dark' ? 'dark' : 'light'
29-
30-
// If the chart is painted too early, built-in auto-sizing does not adapt to the final container size
31-
chartRemountTimeoutId = setTimeout(() => {
32-
chartKey.value += 1
33-
chartRemountTimeoutId = null
34-
}, 10)
35-
})
36-
37-
onBeforeUnmount(() => {
38-
if (chartRemountTimeoutId !== null) {
39-
clearTimeout(chartRemountTimeoutId)
40-
chartRemountTimeoutId = null
41-
}
4225
})
4326
4427
const { colors } = useCssVariables(
@@ -705,12 +688,7 @@ const config = computed(() => {
705688
</div>
706689

707690
<ClientOnly v-if="inModal && chartData.dataset">
708-
<VueUiXy
709-
:dataset="chartData.dataset"
710-
:config="config"
711-
class="[direction:ltr]"
712-
:key="chartKey"
713-
>
691+
<VueUiXy :dataset="chartData.dataset" :config="config" class="[direction:ltr]">
714692
<template #menuIcon="{ isOpen }">
715693
<span v-if="isOpen" class="i-carbon:close w-6 h-6" aria-hidden="true" />
716694
<span v-else class="i-carbon:overflow-menu-vertical w-6 h-6" aria-hidden="true" />

app/components/Package/Versions.vue

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import type { PackageVersionInfo, PackumentVersion } from '#shared/types'
2+
import type { PackageVersionInfo, SlimVersion } from '#shared/types'
33
import { compare } from 'semver'
44
import type { RouteLocationRaw } from 'vue-router'
55
import { fetchAllPackageVersions } from '~/composables/useNpmRegistry'
@@ -14,7 +14,7 @@ import {
1414
1515
const props = defineProps<{
1616
packageName: string
17-
versions: Record<string, PackumentVersion>
17+
versions: Record<string, SlimVersion>
1818
distTags: Record<string, string>
1919
time: Record<string, string>
2020
}>()
@@ -31,13 +31,6 @@ interface VersionDisplay {
3131
deprecated?: string
3232
}
3333
34-
// Check if a version has provenance/attestations
35-
function hasProvenance(version: PackumentVersion | undefined): boolean {
36-
if (!version?.dist) return false
37-
const dist = version.dist as { attestations?: unknown }
38-
return !!dist.attestations
39-
}
40-
4134
// Build route object for package version link
4235
function versionRoute(version: string): RouteLocationRaw {
4336
return {
@@ -53,10 +46,7 @@ const versionToTags = computed(() => buildVersionToTagsMap(props.distTags))
5346
// Deduplicates so each version appears only once, with all its tags
5447
const allTagRows = computed(() => {
5548
// Group tags by version with their metadata
56-
const versionMap = new Map<
57-
string,
58-
{ tags: string[]; versionData: PackumentVersion | undefined }
59-
>()
49+
const versionMap = new Map<string, { tags: string[]; versionData: SlimVersion | undefined }>()
6050
for (const [tag, version] of Object.entries(props.distTags)) {
6151
const existing = versionMap.get(version)
6252
if (existing) {
@@ -88,7 +78,7 @@ const allTagRows = computed(() => {
8878
version,
8979
time: props.time[version],
9080
tags,
91-
hasProvenance: hasProvenance(versionData),
81+
hasProvenance: versionData?.hasProvenance,
9282
deprecated: versionData?.deprecated,
9383
} as VersionDisplay,
9484
}))

app/components/Package/WeeklyDownloadStats.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { OKLCH_NEUTRAL_FALLBACK, lightenOklch } from '~/utils/colors'
55
66
const props = defineProps<{
77
packageName: string
8+
createdIso: string | null
89
}>()
910
1011
const chartModal = useModal('chart-modal')
@@ -16,9 +17,6 @@ function openChartModal() {
1617
nextTick(() => chartModal.open())
1718
}
1819
19-
const { data: packument } = usePackage(() => props.packageName)
20-
const createdIso = computed(() => packument.value?.time?.created ?? null)
21-
2220
const { fetchPackageDownloadEvolution } = useCharts()
2321
2422
const { accentColors, selectedAccentColor } = useAccentColor()
@@ -92,7 +90,7 @@ async function loadWeeklyDownloads() {
9290
try {
9391
const result = await fetchPackageDownloadEvolution(
9492
() => props.packageName,
95-
() => createdIso.value,
93+
() => props.createdIso,
9694
() => ({ granularity: 'week' as const, weeks: 52 }),
9795
)
9896
weeklyDownloads.value = (result as WeeklyDownloadPoint[]) ?? []

0 commit comments

Comments
 (0)