Skip to content

Commit 26b4272

Browse files
committed
Merge remote-tracking branch 'upstream/main' into storybook
2 parents 2dbb95d + d4e1ae9 commit 26b4272

Some content is hidden

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

42 files changed

+860
-547
lines changed

.github/workflows/semantic-pull-requests.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,10 @@ jobs:
3434
The subject "{subject}" found in the pull request title "{title}"
3535
didn't match the configured pattern. Please ensure that the subject
3636
doesn't start with an uppercase character.
37+
38+
Examples:
39+
✅ chore(ui): fix button spacing
40+
✅ docs: update README
41+
❌ chore(ui): Fix button spacing
3742
env:
3843
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

CONTRIBUTING.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ This focus helps guide our project decisions as a community and what we choose t
3131
- [Setup](#setup)
3232
- [Development workflow](#development-workflow)
3333
- [Available commands](#available-commands)
34+
- [Clearing caches during development](#clearing-caches-during-development)
3435
- [Project structure](#project-structure)
3536
- [Local connector CLI](#local-connector-cli)
3637
- [Mock connector (for local development)](#mock-connector-for-local-development)
@@ -124,6 +125,34 @@ pnpm test:a11y # Lighthouse accessibility audits
124125
pnpm test:perf # Lighthouse performance audits (CLS)
125126
```
126127

128+
### Clearing caches during development
129+
130+
Nitro persists `defineCachedEventHandler` results to disk at `.nuxt/cache/nitro/`. This cache **survives dev server restarts**. If you're iterating on a cached API route and want fresh results, delete the relevant cache directory:
131+
132+
```bash
133+
# Clear all Nitro handler caches
134+
rm -rf .nuxt/cache/nitro/handlers/
135+
136+
# Clear a specific handler cache (e.g. picks)
137+
rm -rf .nuxt/cache/nitro/handlers/npmx-picks/
138+
```
139+
140+
Alternatively, you can bypass the cache entirely in development by adding `shouldBypassCache: () => import.meta.dev` to your `defineCachedEventHandler` options:
141+
142+
```ts
143+
export default defineCachedEventHandler(
144+
async event => {
145+
// ...
146+
},
147+
{
148+
maxAge: 60 * 5,
149+
shouldBypassCache: () => import.meta.dev,
150+
},
151+
)
152+
```
153+
154+
The `.cache/` directory is a separate storage mount used for fetch-cache and atproto data.
155+
127156
### Project structure
128157

129158
```

app/components/Header/AuthModal.client.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ watch(handleInput, newHandleInput => {
5050
handleInput.value = normalized
5151
}
5252
})
53+
54+
watch(user, async newUser => {
55+
if (newUser?.relogin) {
56+
await authRedirect(newUser.did, {
57+
redirectTo: route.fullPath,
58+
})
59+
}
60+
})
5361
</script>
5462

5563
<template>

app/components/Package/Playgrounds.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ const providerIcons: Record<string, string> = {
1616
'nuxt-new': 'i-simple-icons:nuxtdotjs',
1717
'vite-new': 'i-simple-icons:vite',
1818
'jsfiddle': 'i-lucide:code',
19+
'typescript-playground': 'i-simple-icons:typescript',
20+
'solid-playground': 'i-simple-icons:solid',
21+
'svelte-playground': 'i-simple-icons:svelte',
22+
'tailwind-playground': 'i-simple-icons:tailwindcss',
1923
}
2024
2125
// Map provider id to color class
@@ -29,6 +33,10 @@ const providerColors: Record<string, string> = {
2933
'nuxt-new': 'text-provider-nuxt',
3034
'vite-new': 'text-provider-vite',
3135
'jsfiddle': 'text-provider-jsfiddle',
36+
'typescript-playground': 'text-provider-typescript',
37+
'solid-playground': 'text-provider-solid',
38+
'svelte-playground': 'text-provider-svelte',
39+
'tailwind-playground': 'text-provider-tailwind',
3240
}
3341
3442
function getIcon(provider: string): string {

app/components/Package/TrendsChart.vue

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import type { VueUiXyDatasetItem } from 'vue-data-ui'
2+
import type { VueUiXyConfig, VueUiXyDatasetItem } from 'vue-data-ui'
33
import { VueUiXy } from 'vue-data-ui/vue-ui-xy'
44
import { useDebounceFn, useElementSize } from '@vueuse/core'
55
import { useCssVariables } from '~/composables/useColors'
@@ -1404,13 +1404,13 @@ function drawSvgPrintLegend(svg: Record<string, any>) {
14041404
}
14051405
14061406
// VueUiXy chart component configuration
1407-
const chartConfig = computed(() => {
1407+
const chartConfig = computed<VueUiXyConfig>(() => {
14081408
return {
1409-
theme: isDarkMode.value ? 'dark' : 'default',
1409+
theme: isDarkMode.value ? 'dark' : '',
14101410
chart: {
14111411
height: isMobile.value ? 950 : 600,
14121412
backgroundColor: colors.value.bg,
1413-
padding: { bottom: displayedGranularity.value === 'yearly' ? 84 : 64, right: 100 }, // padding right is set to leave space of last datapoint label(s)
1413+
padding: { bottom: displayedGranularity.value === 'yearly' ? 84 : 64, right: 128 }, // padding right is set to leave space of last datapoint label(s)
14141414
userOptions: {
14151415
buttons: {
14161416
pdf: false,
@@ -1428,10 +1428,13 @@ const chartConfig = computed(() => {
14281428
altCopy: undefined, // TODO: set to proper translation key
14291429
},
14301430
callbacks: {
1431-
img: ({ imageUri }: { imageUri: string }) => {
1431+
img: args => {
1432+
const imageUri = args?.imageUri
1433+
if (!imageUri) return
14321434
loadFile(imageUri, buildExportFilename('png'))
14331435
},
1434-
csv: (csvStr: string) => {
1436+
csv: csvStr => {
1437+
if (!csvStr) return
14351438
const PLACEHOLDER_CHAR = '\0'
14361439
const multilineDateTemplate = $t('package.trends.date_range_multiline', {
14371440
start: PLACEHOLDER_CHAR,
@@ -1448,7 +1451,9 @@ const chartConfig = computed(() => {
14481451
loadFile(url, buildExportFilename('csv'))
14491452
URL.revokeObjectURL(url)
14501453
},
1451-
svg: ({ blob }: { blob: Blob }) => {
1454+
svg: args => {
1455+
const blob = args?.blob
1456+
if (!blob) return
14521457
const url = URL.createObjectURL(blob)
14531458
loadFile(url, buildExportFilename('svg'))
14541459
URL.revokeObjectURL(url)
@@ -1510,10 +1515,9 @@ const chartConfig = computed(() => {
15101515
borderColor: 'transparent',
15111516
backdropFilter: false,
15121517
backgroundColor: 'transparent',
1513-
customFormat: ({ datapoint }: { datapoint: Record<string, any> | any[] }) => {
1514-
if (!datapoint || pending.value) return ''
1518+
customFormat: ({ datapoint: items }) => {
1519+
if (!items || pending.value) return ''
15151520
1516-
const items = Array.isArray(datapoint) ? datapoint : [datapoint[0]]
15171521
const hasMultipleItems = items.length > 1
15181522
15191523
const rows = items
@@ -1678,7 +1682,11 @@ watch(selectedMetric, value => {
16781682
</h2>
16791683

16801684
<!-- Chart panel (active metric) -->
1681-
<div role="region" aria-labelledby="trends-chart-title" class="min-h-[260px]">
1685+
<div
1686+
role="region"
1687+
aria-labelledby="trends-chart-title"
1688+
:class="isMobile === false && width > 0 ? 'min-h-[567px]' : 'min-h-[260px]'"
1689+
>
16821690
<ClientOnly v-if="chartData.dataset">
16831691
<div :data-pending="pending" :data-minimap-visible="maxDatapoints > 6">
16841692
<VueUiXy

app/components/Package/VersionDistribution.vue

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
<script setup lang="ts">
22
import { VueUiXy } from 'vue-data-ui/vue-ui-xy'
3-
import {
4-
type VueUiXyDatasetItem,
5-
type VueUiXyDatasetBarItem,
6-
type VueUiXyDatapointItem,
7-
type MinimalCustomFormatParams,
8-
} from 'vue-data-ui'
3+
import { type VueUiXyDatasetItem, type VueUiXyConfig } from 'vue-data-ui'
94
import { useElementSize } from '@vueuse/core'
105
import { useCssVariables } from '~/composables/useColors'
116
import { OKLCH_NEUTRAL_FALLBACK, transparentizeOklch, lightenHex } from '~/utils/colors'
@@ -15,10 +10,6 @@ import {
1510
} from '~/composables/useChartWatermark'
1611
import TooltipApp from '~/components/Tooltip/App.vue'
1712
18-
type TooltipParams = MinimalCustomFormatParams<VueUiXyDatapointItem[]> & {
19-
bars: VueUiXyDatasetBarItem[]
20-
}
21-
2213
const props = defineProps<{
2314
packageName: string
2415
inModal?: boolean
@@ -176,7 +167,7 @@ const hasMinimap = computed<boolean>(() => {
176167
return series.length > 6
177168
})
178169
179-
const chartConfig = computed(() => {
170+
const chartConfig = computed<VueUiXyConfig>(() => {
180171
return {
181172
theme: isDarkMode.value ? 'dark' : '',
182173
chart: {
@@ -209,10 +200,13 @@ const chartConfig = computed(() => {
209200
altCopy: undefined, // TODO: set to proper translation key
210201
},
211202
callbacks: {
212-
img: ({ imageUri }: { imageUri: string }) => {
203+
img: args => {
204+
const imageUri = args?.imageUri
205+
if (!imageUri) return
213206
loadFile(imageUri, buildExportFilename('png'))
214207
},
215-
csv: (csvStr: string) => {
208+
csv: csvStr => {
209+
if (!csvStr) return
216210
const PLACEHOLDER_CHAR = '\0'
217211
const multilineDateTemplate = $t('package.trends.date_range_multiline', {
218212
start: PLACEHOLDER_CHAR,
@@ -229,7 +223,9 @@ const chartConfig = computed(() => {
229223
loadFile(url, buildExportFilename('csv'))
230224
URL.revokeObjectURL(url)
231225
},
232-
svg: ({ blob }: { blob: Blob }) => {
226+
svg: args => {
227+
const blob = args?.blob
228+
if (!blob) return
233229
const url = URL.createObjectURL(blob)
234230
loadFile(url, buildExportFilename('svg'))
235231
URL.revokeObjectURL(url)
@@ -277,8 +273,7 @@ const chartConfig = computed(() => {
277273
borderColor: 'transparent',
278274
backdropFilter: false,
279275
backgroundColor: 'transparent',
280-
customFormat: (params: TooltipParams) => {
281-
const { datapoint, absoluteIndex, bars } = params
276+
customFormat: ({ datapoint, absoluteIndex, bars }) => {
282277
if (!datapoint || pending.value) return ''
283278
284279
// Use absoluteIndex to get the correct version from chartDataset
@@ -329,9 +324,6 @@ const chartConfig = computed(() => {
329324
},
330325
},
331326
},
332-
table: {
333-
show: false,
334-
},
335327
}
336328
})
337329
</script>
@@ -447,7 +439,7 @@ const chartConfig = computed(() => {
447439
role="region"
448440
aria-labelledby="version-distribution-title"
449441
class="relative"
450-
:class="isMobile ? 'min-h-[260px]' : 'min-h-[400px]'"
442+
:class="isMobile ? 'min-h-[260px]' : 'min-h-[520px]'"
451443
>
452444
<!-- Chart content -->
453445
<ClientOnly v-if="xyDataset.length > 0 && !error">
@@ -463,6 +455,16 @@ const chartConfig = computed(() => {
463455
v-if="svg.isPrintingSvg || svg.isPrintingImg"
464456
v-html="drawNpmxLogoAndTaglineWatermark(svg, watermarkColors, $t, 'bottom')"
465457
/>
458+
459+
<!-- Overlay covering the chart area to hide line resizing when switching granularities recalculates VueUiXy scaleMax when estimation lines are necessary -->
460+
<rect
461+
v-if="pending"
462+
:x="svg.drawingArea.left"
463+
:y="svg.drawingArea.top - 12"
464+
:width="svg.drawingArea.width + 12"
465+
:height="svg.drawingArea.height + 48"
466+
:fill="colors.bg"
467+
/>
466468
</template>
467469

468470
<!-- Custom bar gradient based on the series color -->
@@ -620,21 +622,6 @@ const chartConfig = computed(() => {
620622
:deep(.vue-ui-xy) svg rect {
621623
transition: none !important;
622624
}
623-
624-
@keyframes fadeInUp {
625-
from {
626-
opacity: 0;
627-
transform: translateY(8px);
628-
}
629-
to {
630-
opacity: 1;
631-
transform: translateY(0);
632-
}
633-
}
634-
635-
.chart-container {
636-
animation: fadeInUp 350ms cubic-bezier(0.4, 0, 0.2, 1);
637-
}
638625
</style>
639626

640627
<style>

app/components/Package/Versions.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,7 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
10771077
<!-- Avoids CLS when the dialog has transitioned -->
10781078
<div
10791079
v-if="!hasDistributionModalTransitioned"
1080-
class="w-full aspect-[272/609] sm:aspect-[718/571]"
1080+
class="w-full aspect-[272/609] sm:aspect-[718/592.67]"
10811081
/>
10821082
</PackageChartModal>
10831083
</template>

app/components/Package/WeeklyDownloadStats.vue

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useCssVariables } from '~/composables/useColors'
44
import type { WeeklyDataPoint } from '~/types/chart'
55
import { OKLCH_NEUTRAL_FALLBACK, lightenOklch } from '~/utils/colors'
66
import type { RepoRef } from '#shared/utils/git-providers'
7+
import type { VueUiSparklineConfig, VueUiSparklineDatasetItem } from 'vue-data-ui'
78
89
const props = defineProps<{
910
packageName: string
@@ -176,7 +177,7 @@ watch(
176177
() => loadWeeklyDownloads(),
177178
)
178179
179-
const dataset = computed(() =>
180+
const dataset = computed<VueUiSparklineDatasetItem[]>(() =>
180181
weeklyDownloads.value.map(d => ({
181182
value: d?.value ?? 0,
182183
period: $t('package.trends.date_range', {
@@ -188,7 +189,7 @@ const dataset = computed(() =>
188189
189190
const lastDatapoint = computed(() => dataset.value.at(-1)?.period ?? '')
190191
191-
const config = computed(() => {
192+
const config = computed<VueUiSparklineConfig>(() => {
192193
return {
193194
theme: 'dark',
194195
/**
@@ -234,7 +235,7 @@ const config = computed(() => {
234235
show: hasSparklineAnimation.value, // the pulse will not show if prefers-reduced-motion (enforced by vue-data-ui)
235236
loop: true, // runs only once if false
236237
radius: 1.5,
237-
color: pulseColor.value,
238+
color: pulseColor.value!,
238239
easing: 'ease-in-out',
239240
trail: {
240241
show: true,
@@ -248,7 +249,7 @@ const config = computed(() => {
248249
stroke: isDarkMode.value ? 'oklch(0.985 0 0)' : 'oklch(0.145 0 0)',
249250
},
250251
title: {
251-
text: lastDatapoint.value,
252+
text: String(lastDatapoint.value),
252253
fontSize: 12,
253254
color: colors.value.fgSubtle,
254255
bold: false,
@@ -263,7 +264,7 @@ const config = computed(() => {
263264
</script>
264265

265266
<template>
266-
<div class="space-y-8">
267+
<div class="space-y-8 h-[110px] motion-safe:h-[140px]">
267268
<CollapsibleSection id="downloads" :title="$t('package.downloads.title')">
268269
<template #actions>
269270
<ButtonBase
@@ -306,6 +307,10 @@ const config = computed(() => {
306307
<SkeletonInline class="h-px w-full" />
307308
</div>
308309
</div>
310+
<!-- Animation toggle placeholder -->
311+
<div class="w-full hidden motion-safe:flex flex-1 items-end justify-end">
312+
<SkeletonInline class="h-[20px] w-30" />
313+
</div>
309314
</div>
310315
</template>
311316
</ClientOnly>
@@ -350,10 +355,7 @@ const config = computed(() => {
350355

351356
<!-- This placeholder bears the same dimensions as the PackageTrendsChart component -->
352357
<!-- Avoids CLS when the dialog has transitioned -->
353-
<div
354-
v-if="!hasChartModalTransitioned"
355-
class="w-full aspect-[390/634.5] sm:aspect-[718/622.797]"
356-
/>
358+
<div v-if="!hasChartModalTransitioned" class="w-full aspect-[390/634.5] sm:aspect-[718/647]" />
357359
</PackageChartModal>
358360
</template>
359361

0 commit comments

Comments
 (0)