Skip to content

Commit 21eb54e

Browse files
authored
Merge branch 'main' into patch-1
2 parents d6e0e1b + ce5be10 commit 21eb54e

10 files changed

Lines changed: 102 additions & 23 deletions

File tree

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/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/pages/[...package].vue

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ defineOgImageComponent('Package', {
300300

301301
<article
302302
v-else-if="status === 'success' && pkg"
303-
class="package-page motion-safe:animate-fade-in"
303+
class="package-page motion-safe:animate-fade-in overflow-x-hidden"
304304
>
305305
<!-- Package header -->
306306
<header class="area-header pb-8 border-b border-border">
@@ -725,11 +725,11 @@ defineOgImageComponent('Package', {
725725
<span class="w-2.5 h-2.5 rounded-full bg-fg-subtle" />
726726
<span class="w-2.5 h-2.5 rounded-full bg-fg-subtle" />
727727
</div>
728-
<div class="space-y-1 px-3 pt-2 pb-3 sm:px-4 sm:pt-3 sm:pb-4">
728+
<div class="space-y-1 px-3 pt-2 pb-3 sm:px-4 sm:pt-3 sm:pb-4 overflow-x-auto">
729729
<!-- Main package install -->
730-
<div class="flex items-center gap-2">
731-
<span class="text-fg-subtle font-mono text-sm select-none">$</span>
732-
<code class="font-mono text-sm"
730+
<div class="flex items-center gap-2 min-w-0">
731+
<span class="text-fg-subtle font-mono text-sm select-none shrink-0">$</span>
732+
<code class="font-mono text-sm min-w-0"
733733
><ClientOnly
734734
><span
735735
v-for="(part, i) in installCommandParts"
@@ -744,9 +744,9 @@ defineOgImageComponent('Package', {
744744
>
745745
</div>
746746
<!-- @types package install (when enabled) -->
747-
<div v-if="showTypesInInstall" class="flex items-center gap-2">
748-
<span class="text-fg-subtle font-mono text-sm select-none">$</span>
749-
<code class="font-mono text-sm"
747+
<div v-if="showTypesInInstall" class="flex items-center gap-2 min-w-0">
748+
<span class="text-fg-subtle font-mono text-sm select-none shrink-0">$</span>
749+
<code class="font-mono text-sm min-w-0"
750750
><span
751751
v-for="(part, i) in typesInstallCommandParts"
752752
:key="i"
@@ -997,4 +997,36 @@ defineOgImageComponent('Package', {
997997
.area-sidebar {
998998
grid-area: sidebar;
999999
}
1000+
1001+
/* Improve package name wrapping for narrow screens */
1002+
.area-header h1 {
1003+
overflow-wrap: anywhere;
1004+
}
1005+
1006+
/* Ensure description text wraps properly */
1007+
.area-header p {
1008+
word-wrap: break-word;
1009+
overflow-wrap: break-word;
1010+
word-break: break-word;
1011+
}
1012+
1013+
/* Allow install command text to break on narrow screens */
1014+
.area-install code {
1015+
word-break: break-word;
1016+
overflow-wrap: anywhere;
1017+
white-space: pre-wrap;
1018+
}
1019+
1020+
/* Ensure all text content wraps on narrow screens */
1021+
.package-page {
1022+
word-wrap: break-word;
1023+
overflow-wrap: break-word;
1024+
max-width: 100%;
1025+
}
1026+
1027+
/* Ensure all children respect max-width */
1028+
.package-page > * {
1029+
max-width: 100%;
1030+
min-width: 0;
1031+
}
10001032
</style>

i18n/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"non_affiliation_disclaimer": "not affiliated with npm, Inc.",
1010
"trademark_disclaimer": "npm is a registered trademark of npm, Inc. This site is not affiliated with npm, Inc.",
1111
"footer": {
12+
"docs": "docs",
1213
"source": "source",
1314
"social": "social",
1415
"chat": "chat"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"validate-npm-package-name": "^7.0.2",
5454
"virtua": "^0.48.3",
5555
"vue": "3.5.27",
56-
"vue-data-ui": "^3.13.4"
56+
"vue-data-ui": "^3.13.5"
5757
},
5858
"devDependencies": {
5959
"@iconify-json/carbon": "1.2.18",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/utils/code-highlight.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,8 @@ export async function highlightCode(
265265
try {
266266
let html = shiki.codeToHtml(code, {
267267
lang: language,
268-
theme: 'github-dark',
268+
themes: { light: 'github-light', dark: 'github-dark' },
269+
defaultColor: 'dark',
269270
})
270271

271272
// Shiki doesn't encode > in text content (e.g., arrow functions)

server/utils/shiki.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ let highlighter: HighlighterCore | null = null
66
export async function getShikiHighlighter(): Promise<HighlighterCore> {
77
if (!highlighter) {
88
highlighter = await createHighlighterCore({
9-
themes: [import('@shikijs/themes/github-dark')],
9+
themes: [import('@shikijs/themes/github-dark'), import('@shikijs/themes/github-light')],
1010
langs: [
1111
// Core web languages
1212
import('@shikijs/langs/javascript'),
@@ -66,7 +66,8 @@ export function highlightCodeSync(shiki: HighlighterCore, code: string, language
6666
try {
6767
let html = shiki.codeToHtml(code, {
6868
lang: language,
69-
theme: 'github-dark',
69+
themes: { light: 'github-light', dark: 'github-dark' },
70+
defaultColor: 'dark',
7071
})
7172
// Remove inline style from <pre> tag so CSS can control appearance
7273
html = html.replace(/<pre([^>]*)\s+style="[^"]*"/, '<pre$1')

test/unit/docs-text.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,8 @@ describe('renderMarkdown', () => {
242242
const input = '```ts\nconst a = 1\n```\n\nSome text\n\n```js\nconst b = 2\n```'
243243
const result = await renderMarkdown(input, emptyLookup)
244244
expect(result).toContain('Some text')
245-
// Both code blocks should be highlighted
246-
expect((result.match(/shiki/g) || []).length).toBe(2)
245+
// Both code blocks should be highlighted, search for `shiki` class
246+
expect((result.match(/["\s]shiki["\s]/g) || []).length).toBe(2)
247247
})
248248

249249
it('should not confuse inline code with fenced code blocks', async () => {

0 commit comments

Comments
 (0)