[READ-ONLY] a fast, modern browser for the npm registry
at main 105 lines 3.6 kB view raw
1<script setup lang="ts"> 2import type { JsrPackageInfo } from '#shared/types/jsr' 3import type { PackageManagerId } from '~/utils/install-command' 4 5/** 6 * A terminal-style execute command display for binary-only packages. 7 * Renders all package manager variants with CSS-based visibility. 8 */ 9 10const props = defineProps<{ 11 packageName: string 12 jsrInfo?: JsrPackageInfo | null 13 isCreatePackage?: boolean 14}>() 15 16const selectedPM = useSelectedPackageManager() 17 18// Generate execute command parts for a specific package manager 19function getExecutePartsForPM(pmId: PackageManagerId) { 20 return getExecuteCommandParts({ 21 packageName: props.packageName, 22 packageManager: pmId, 23 jsrInfo: props.jsrInfo, 24 isBinaryOnly: true, 25 isCreatePackage: props.isCreatePackage, 26 }) 27} 28 29// Full execute command for copying (uses current selected PM) 30function getFullExecuteCommand() { 31 return getExecuteCommand({ 32 packageName: props.packageName, 33 packageManager: selectedPM.value, 34 jsrInfo: props.jsrInfo, 35 isBinaryOnly: true, 36 isCreatePackage: props.isCreatePackage, 37 }) 38} 39 40// Copy handler 41const { copied: executeCopied, copy: copyExecute } = useClipboard({ copiedDuring: 2000 }) 42const copyExecuteCommand = () => copyExecute(getFullExecuteCommand()) 43</script> 44 45<template> 46 <div class="relative group"> 47 <!-- Terminal-style execute command --> 48 <div class="bg-bg-subtle border border-border rounded-lg overflow-hidden"> 49 <div class="flex gap-1.5 px-3 pt-2 sm:px-4 sm:pt-3"> 50 <span class="w-2.5 h-2.5 rounded-full bg-fg-subtle" /> 51 <span class="w-2.5 h-2.5 rounded-full bg-fg-subtle" /> 52 <span class="w-2.5 h-2.5 rounded-full bg-fg-subtle" /> 53 </div> 54 <div class="px-3 pt-2 pb-3 sm:px-4 sm:pt-3 sm:pb-4 space-y-1"> 55 <!-- Execute command - render all PM variants, CSS controls visibility --> 56 <div 57 v-for="pm in packageManagers" 58 :key="`execute-${pm.id}`" 59 :data-pm-cmd="pm.id" 60 class="flex items-center gap-2 group/executecmd" 61 > 62 <span class="text-fg-subtle font-mono text-sm select-none">$</span> 63 <code class="font-mono text-sm" 64 ><span 65 v-for="(part, i) in getExecutePartsForPM(pm.id)" 66 :key="i" 67 :class="i === 0 ? 'text-fg' : 'text-fg-muted'" 68 >{{ i > 0 ? ' ' : '' }}{{ part }}</span 69 ></code 70 > 71 <button 72 type="button" 73 class="px-2 py-0.5 font-mono text-xs text-fg-muted bg-bg-subtle/80 border border-border rounded transition-colors duration-200 opacity-0 group-hover/executecmd:opacity-100 hover:(text-fg border-border-hover) active:scale-95 focus-visible:opacity-100 focus-visible:outline-accent/70" 74 :aria-label="$t('package.get_started.copy_command')" 75 @click.stop="copyExecuteCommand" 76 > 77 {{ executeCopied ? $t('common.copied') : $t('common.copy') }} 78 </button> 79 </div> 80 </div> 81 </div> 82 </div> 83</template> 84 85<style> 86/* Hide all variants by default when preference is set */ 87:root[data-pm] [data-pm-cmd] { 88 display: none; 89} 90 91/* Show only the matching package manager command */ 92:root[data-pm='npm'] [data-pm-cmd='npm'], 93:root[data-pm='pnpm'] [data-pm-cmd='pnpm'], 94:root[data-pm='yarn'] [data-pm-cmd='yarn'], 95:root[data-pm='bun'] [data-pm-cmd='bun'], 96:root[data-pm='deno'] [data-pm-cmd='deno'], 97:root[data-pm='vlt'] [data-pm-cmd='vlt'] { 98 display: flex; 99} 100 101/* Fallback: when no data-pm is set (SSR initial), show npm as default */ 102:root:not([data-pm]) [data-pm-cmd]:not([data-pm-cmd='npm']) { 103 display: none; 104} 105</style>