Skip to content

Commit 2d56a50

Browse files
committed
merge: resolve conflicts with main
2 parents 2ea3707 + ef404ab commit 2d56a50

9 files changed

Lines changed: 96 additions & 39 deletions

File tree

.github/workflows/autofix.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515

1616
steps:
1717
- uses: actions/checkout@v6
18-
- run: npm i -g --force corepack && corepack enable
18+
- run: corepack enable
1919
- uses: actions/setup-node@v6
2020
with:
2121
node-version: lts/*

.github/workflows/ci.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ on:
88
branches:
99
- main
1010

11+
permissions:
12+
contents: read
13+
1114
jobs:
1215
lint:
1316
runs-on: ubuntu-latest
1417

1518
steps:
1619
- uses: actions/checkout@v6
17-
- run: npm i -g --force corepack && corepack enable
20+
- run: corepack enable
1821
- uses: actions/setup-node@v6
1922
with:
2023
node-version: lts/*
@@ -31,7 +34,7 @@ jobs:
3134

3235
steps:
3336
- uses: actions/checkout@v6
34-
- run: npm i -g --force corepack && corepack enable
37+
- run: corepack enable
3538
- uses: actions/setup-node@v6
3639
with:
3740
node-version: lts/*
@@ -59,7 +62,7 @@ jobs:
5962

6063
steps:
6164
- uses: actions/checkout@v6
62-
- run: npm i -g --force corepack && corepack enable
65+
- run: corepack enable
6366
- uses: actions/setup-node@v6
6467
with:
6568
node-version: lts/*

app/components/ConnectorModal.vue

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const { isConnected, isConnecting, npmUser, error, hasOperations, connect, disco
66
77
const tokenInput = ref('')
88
const portInput = ref('31415')
9+
const copied = ref(false)
910
1011
async function handleConnect() {
1112
const port = Number.parseInt(portInput.value, 10) || 31415
@@ -20,6 +21,27 @@ function handleDisconnect() {
2021
disconnect()
2122
}
2223
24+
function copyCommand() {
25+
let command = executeNpmxConnectorCommand.value
26+
if (portInput.value !== '31415') {
27+
command += ` --port ${portInput.value}`
28+
}
29+
navigator.clipboard.writeText(command)
30+
copied.value = true
31+
setTimeout(() => {
32+
copied.value = false
33+
}, 2000)
34+
}
35+
36+
const selectedPM = useSelectedPackageManager()
37+
38+
const executeNpmxConnectorCommand = computed(() => {
39+
return getExecuteCommand({
40+
packageName: 'npmx-connector',
41+
packageManager: selectedPM.value,
42+
})
43+
})
44+
2345
// Reset form when modal opens
2446
watch(open, isOpen => {
2547
if (isOpen) {
@@ -103,9 +125,24 @@ watch(open, isOpen => {
103125
Run the connector on your machine to enable admin features:
104126
</p>
105127

106-
<div class="p-3 bg-[#0d0d0d] border border-border rounded-lg font-mono text-sm">
128+
<div
129+
class="flex items-center p-3 bg-[#0d0d0d] border border-border rounded-lg font-mono text-sm"
130+
>
107131
<span class="text-fg-subtle">$</span>
108-
<span class="text-fg ml-2">npx&nbsp;npmx-connector</span>
132+
<span class="text-fg ml-2">{{ executeNpmxConnectorCommand }}</span>
133+
<button
134+
type="button"
135+
:aria-label="copied ? 'Copied' : 'Copy command'"
136+
class="ml-auto text-fg-subtle hover:text-fg transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 rounded"
137+
@click="copyCommand"
138+
>
139+
<span v-if="!copied" class="i-carbon-copy block w-5 h-5" aria-hidden="true" />
140+
<span
141+
v-else
142+
class="i-carbon-checkmark block w-5 h-5 text-green-500"
143+
aria-hidden="true"
144+
/>
145+
</button>
109146
</div>
110147

111148
<p class="text-sm text-fg-muted">Then paste the token shown in your terminal:</p>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { useLocalStorage } from '@vueuse/core'
2+
3+
export function useSelectedPackageManager() {
4+
return useLocalStorage<PackageManagerId>('npmx-pm', 'npm')
5+
}

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

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -192,19 +192,7 @@ function hasProvenance(version: PackumentVersion | null): boolean {
192192
return !!dist.attestations
193193
}
194194
195-
// Persist package manager preference in localStorage
196-
const selectedPM = ref<PackageManagerId>('npm')
197-
198-
onMounted(() => {
199-
const stored = localStorage.getItem('npmx-pm')
200-
if (stored && packageManagers.some(pm => pm.id === stored)) {
201-
selectedPM.value = stored as PackageManagerId
202-
}
203-
})
204-
205-
watch(selectedPM, value => {
206-
localStorage.setItem('npmx-pm', value)
207-
})
195+
const selectedPM = useSelectedPackageManager()
208196
209197
const installCommandParts = computed(() => {
210198
if (!pkg.value) return []
@@ -296,7 +284,7 @@ defineOgImageComponent('Package', {
296284
<div class="mb-4">
297285
<div class="flex flex-row justify-between">
298286
<!-- Package name and version -->
299-
<div class="flex items-start gap-3 mb-2 flex-wrap min-w-0">
287+
<div class="flex items-start gap-2 mb-1.5 sm:gap-3 sm:mb-2 flex-wrap min-w-0">
300288
<h1
301289
class="font-mono text-2xl sm:text-3xl font-medium min-w-0 break-words"
302290
:title="pkg.name"
@@ -326,7 +314,9 @@ defineOgImageComponent('Package', {
326314
"
327315
:title="`v${displayVersion.version}`"
328316
>
329-
<span class="truncate max-w-32 sm:max-w-48"> v{{ displayVersion.version }} </span>
317+
<span class="truncate max-w-24 sm:max-w-32 md:max-w-48">
318+
v{{ displayVersion.version }}
319+
</span>
330320
<span
331321
v-if="
332322
requestedVersion &&
@@ -396,7 +386,7 @@ defineOgImageComponent('Package', {
396386
</div>
397387

398388
<!-- Stats grid -->
399-
<dl class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-6 gap-4 mt-6">
389+
<dl class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-6 gap-3 sm:gap-4 mt-4 sm:mt-6">
400390
<div v-if="pkg.license" class="space-y-1">
401391
<dt class="text-xs text-fg-subtle uppercase tracking-wider">License</dt>
402392
<dd class="font-mono text-sm text-fg">
@@ -439,7 +429,7 @@ defineOgImageComponent('Package', {
439429
</dd>
440430
</div>
441431

442-
<div class="space-y-1 col-span-2">
432+
<div class="space-y-1 sm:col-span-2">
443433
<dt class="text-xs text-fg-subtle uppercase tracking-wider flex items-center gap-1">
444434
Install Size
445435
<span
@@ -486,7 +476,7 @@ defineOgImageComponent('Package', {
486476

487477
<!-- Links -->
488478
<nav aria-label="Package links" class="mt-6">
489-
<ul class="flex flex-wrap items-center gap-4 list-none m-0 p-0">
479+
<ul class="flex flex-wrap items-center gap-3 sm:gap-4 list-none m-0 p-0">
490480
<li v-if="repositoryUrl">
491481
<a
492482
:href="repositoryUrl"
@@ -625,12 +615,12 @@ defineOgImageComponent('Package', {
625615
<div class="relative group">
626616
<!-- Terminal-style install command -->
627617
<div class="bg-[#0d0d0d] border border-border rounded-lg overflow-hidden">
628-
<div class="flex gap-1.5 px-4 pt-3">
618+
<div class="flex gap-1.5 px-3 pt-2 sm:px-4 sm:pt-3">
629619
<span class="w-2.5 h-2.5 rounded-full bg-[#333]" />
630620
<span class="w-2.5 h-2.5 rounded-full bg-[#333]" />
631621
<span class="w-2.5 h-2.5 rounded-full bg-[#333]" />
632622
</div>
633-
<div class="flex items-center gap-2 px-4 pt-3 pb-4">
623+
<div class="flex items-center gap-2 px-3 pt-2 pb-3 sm:px-4 sm:pt-3 sm:pb-4">
634624
<span class="text-fg-subtle font-mono text-sm select-none">$</span>
635625
<code class="font-mono text-sm"
636626
><ClientOnly
@@ -680,7 +670,7 @@ defineOgImageComponent('Package', {
680670
</div>
681671

682672
<!-- Sidebar -->
683-
<aside class="order-1 lg:order-2 space-y-8 min-w-0 overflow-hidden">
673+
<aside class="order-1 lg:order-2 space-y-6 sm:space-y-8 min-w-0 overflow-hidden">
684674
<!-- Maintainers (with admin actions when connected) -->
685675
<PackageMaintainers :package-name="pkg.name" :maintainers="pkg.maintainers" />
686676

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,16 +339,17 @@ useSeoMeta({
339339

340340
<!-- Breadcrumb navigation -->
341341
<nav
342-
v-if="filePath"
343342
aria-label="File path"
344343
class="flex items-center gap-1 font-mono text-sm overflow-x-auto"
345344
>
346345
<NuxtLink
346+
v-if="filePath"
347347
:to="getCodeUrl()"
348348
class="text-fg-muted hover:text-fg transition-colors shrink-0"
349349
>
350350
root
351351
</NuxtLink>
352+
<span v-else class="text-fg shrink-0">root</span>
352353
<template v-for="(crumb, i) in breadcrumbs" :key="crumb.path">
353354
<span class="text-fg-subtle">/</span>
354355
<NuxtLink

app/utils/install-command.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import type { JsrPackageInfo } from '#shared/types/jsr'
22

33
export const packageManagers = [
4-
{ id: 'npm', label: 'npm', action: 'install' },
5-
{ id: 'pnpm', label: 'pnpm', action: 'add' },
6-
{ id: 'yarn', label: 'yarn', action: 'add' },
7-
{ id: 'bun', label: 'bun', action: 'add' },
8-
{ id: 'deno', label: 'deno', action: 'add' },
9-
{ id: 'vlt', label: 'vlt', action: 'install' },
4+
{ id: 'npm', label: 'npm', action: 'install', execute: 'npx' },
5+
{ id: 'pnpm', label: 'pnpm', action: 'add', execute: 'pnpm dlx' },
6+
{ id: 'yarn', label: 'yarn', action: 'add', execute: 'yarn dlx' },
7+
{ id: 'bun', label: 'bun', action: 'add', execute: 'bunx' },
8+
{ id: 'deno', label: 'deno', action: 'add', execute: 'deno run' },
9+
{ id: 'vlt', label: 'vlt', action: 'install', execute: 'vlt x' },
1010
] as const
1111

1212
export type PackageManagerId = (typeof packageManagers)[number]['id']
@@ -59,3 +59,13 @@ export function getInstallCommandParts(options: InstallCommandOptions): string[]
5959

6060
return [pm.label, pm.action, `${spec}${version}`]
6161
}
62+
63+
export function getExecuteCommand(options: InstallCommandOptions): string {
64+
return getExecuteCommandParts(options).join(' ')
65+
}
66+
67+
export function getExecuteCommandParts(options: InstallCommandOptions): string[] {
68+
const pm = packageManagers.find(p => p.id === options.packageManager)
69+
if (!pm) return []
70+
return [pm.execute, getPackageSpecifier(options)]
71+
}

server/api/registry/[...pkg].get.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ export default defineCachedEventHandler(
55
throw createError({ statusCode: 400, message: 'Package name is required' })
66
}
77

8-
const packageName = pkg.replace(/\//g, '/')
9-
assertValidPackageName(packageName)
8+
assertValidPackageName(pkg)
109

1110
try {
12-
return await fetchNpmPackage(packageName)
11+
return await fetchNpmPackage(pkg)
1312
} catch (error) {
1413
if (error && typeof error === 'object' && 'statusCode' in error) {
1514
throw error

server/utils/readme.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,20 @@ function resolveUrl(url: string, packageName: string, repoInfo?: RepositoryInfo)
206206
if (url.startsWith('#')) {
207207
return url
208208
}
209-
if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('//')) {
210-
return url
209+
if (hasProtocol(url, { acceptRelative: true })) {
210+
try {
211+
const parsed = new URL(url, 'https://example.com')
212+
if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
213+
return url
214+
}
215+
} catch {
216+
// Invalid URL, fall through to resolve as relative
217+
}
218+
// return protocol-relative URLs (//example.com) as-is
219+
if (url.startsWith('//')) {
220+
return url
221+
}
222+
// for non-HTTP protocols (javascript:, data:, etc.), don't return, treat as relative
211223
}
212224

213225
// Prefer GitHub raw URLs when repository info is available

0 commit comments

Comments
 (0)