@@ -334,14 +334,9 @@ const fullInstallCommand = computed(() => {
334334 return ` ${installCommand .value }; ${pm .label } ${pm .action } ${devFlag } ${pkgSpec } `
335335})
336336
337- // Copy install command
338- const copied = ref (false )
339- async function copyInstallCommand() {
340- if (! fullInstallCommand .value ) return
341- await navigator .clipboard .writeText (fullInstallCommand .value )
342- copied .value = true
343- setTimeout (() => (copied .value = false ), 2000 )
344- }
337+ // Copy commands
338+ const { copied : installCopied, copy : copyInstall } = useCopyToClipboard ()
339+ const copyInstallCommand = () => copyInstall (fullInstallCommand .value )
345340
346341// Executable detection for run command
347342const executableInfo = computed (() => {
@@ -404,13 +399,49 @@ const executeCommand = computed(() => {
404399})
405400
406401// Copy execute command (for binary-only packages)
407- const executeCopied = ref (false )
408- async function copyExecuteCommand() {
409- if (! executeCommand .value ) return
410- await navigator .clipboard .writeText (executeCommand .value )
411- executeCopied .value = true
412- setTimeout (() => (executeCopied .value = false ), 2000 )
413- }
402+ const { copied : executeCopied, copy : copyExecute } = useCopyToClipboard ()
403+ const copyExecuteCommand = () => copyExecute (executeCommand .value )
404+
405+ // Get associated create-* package info (e.g., vite -> create-vite)
406+ const createPackageInfo = computed (() => {
407+ if (! packageAnalysis .value ?.createPackage ) return null
408+ // Don't show if deprecated
409+ if (packageAnalysis .value .createPackage .deprecated ) return null
410+ return packageAnalysis .value .createPackage
411+ })
412+
413+ // Create command parts for associated create-* package
414+ const createCommandParts = computed (() => {
415+ if (! createPackageInfo .value ) return []
416+ const pm = packageManagers .find (p => p .id === selectedPM .value )
417+ if (! pm ) return []
418+
419+ // Extract short name: create-vite -> vite
420+ const createPkgName = createPackageInfo .value .packageName
421+ let shortName: string
422+ if (createPkgName .startsWith (' @' )) {
423+ // @scope/create-foo -> foo
424+ const slashIndex = createPkgName .indexOf (' /' )
425+ const name = createPkgName .slice (slashIndex + 1 )
426+ shortName = name .startsWith (' create-' ) ? name .slice (' create-' .length ) : name
427+ } else {
428+ // create-vite -> vite
429+ shortName = createPkgName .startsWith (' create-' )
430+ ? createPkgName .slice (' create-' .length )
431+ : createPkgName
432+ }
433+
434+ return [... pm .create .split (' ' ), shortName ]
435+ })
436+
437+ // Full create command string for copying
438+ const createCommand = computed (() => {
439+ return createCommandParts .value .join (' ' )
440+ })
441+
442+ // Copy create command
443+ const { copied : createCopied, copy : copyCreate } = useCopyToClipboard ()
444+ const copyCreateCommand = () => copyCreate (createCommand .value )
414445
415446// Primary run command parts
416447const runCommandParts = computed (() => {
@@ -430,19 +461,8 @@ function getFullRunCommand(command?: string) {
430461}
431462
432463// Copy run command
433- const runCopied = ref (false )
434- const runCopiedCommand = ref <string | null >(null )
435- async function copyRunCommand(command ? : string ) {
436- const cmd = getFullRunCommand (command )
437- if (! cmd ) return
438- await navigator .clipboard .writeText (cmd )
439- runCopied .value = true
440- runCopiedCommand .value = command || null
441- setTimeout (() => {
442- runCopied .value = false
443- runCopiedCommand .value = null
444- }, 2000 )
445- }
464+ const { copied : runCopied, copy : copyRun } = useCopyToClipboard ()
465+ const copyRunCommand = (command ? : string ) => copyRun (getFullRunCommand (command ))
446466
447467// Expandable description
448468const descriptionExpanded = ref (false )
@@ -1015,7 +1035,7 @@ defineOgImageComponent('Package', {
10151035 @click.stop =" copyInstallCommand"
10161036 >
10171037 <span aria-live =" polite" >{{
1018- copied ? t('common.copied') : t('common.copy')
1038+ installCopied ? t('common.copied') : t('common.copy')
10191039 }}</span >
10201040 </button >
10211041 </div >
@@ -1074,14 +1094,58 @@ defineOgImageComponent('Package', {
10741094 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/runcmd:opacity-100 hover:(text-fg border-border-hover) active:scale-95 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
10751095 @click.stop =" copyRunCommand(executableInfo?.primaryCommand)"
10761096 >
1077- {{
1078- runCopied && runCopiedCommand === (executableInfo?.primaryCommand || null)
1079- ? t('common.copied')
1080- : t('common.copy')
1081- }}
1097+ {{ runCopied ? t('common.copied') : t('common.copy') }}
10821098 </button >
10831099 </div >
10841100 </template >
1101+
1102+ <!-- Create command (for packages with associated create-* package) -->
1103+ <template v-if =" createPackageInfo " >
1104+ <!-- Comment line -->
1105+ <div class =" flex items-center gap-2 pt-1" >
1106+ <span class =" text-fg-subtle/50 font-mono text-sm select-none"
1107+ ># {{ t('package.create.title') }}</span
1108+ >
1109+ </div >
1110+
1111+ <!-- Create command -->
1112+ <div class =" flex items-center gap-2 group/createcmd" >
1113+ <span class =" text-fg-subtle font-mono text-sm select-none" >$</span >
1114+ <code class =" font-mono text-sm"
1115+ ><ClientOnly
1116+ ><span
1117+ v-for =" (part, i) in createCommandParts"
1118+ :key =" i"
1119+ :class =" i === 0 ? 'text-fg' : 'text-fg-muted'"
1120+ >{{ i > 0 ? ' ' : '' }}{{ part }}</span
1121+ ><template #fallback
1122+ ><span class =" text-fg" >npm</span
1123+ ><span class =" text-fg-muted" >
1124+ create {{ createPackageInfo.packageName.replace('create-', '') }}</span
1125+ ></template
1126+ ></ClientOnly
1127+ ></code
1128+ >
1129+ <button
1130+ type =" button"
1131+ 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/createcmd:opacity-100 hover:(text-fg border-border-hover) active:scale-95 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
1132+ :aria-label =" t('package.create.copy_command')"
1133+ @click.stop =" copyCreateCommand"
1134+ >
1135+ <span aria-live =" polite" >{{
1136+ createCopied ? t('common.copied') : t('common.copy')
1137+ }}</span >
1138+ </button >
1139+ <NuxtLink
1140+ :to =" `/${createPackageInfo.packageName}`"
1141+ class =" text-fg-subtle hover:text-fg-muted text-xs transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 rounded"
1142+ :title =" `View ${createPackageInfo.packageName}`"
1143+ >
1144+ <span class =" i-carbon-arrow-right w-3 h-3" aria-hidden =" true" />
1145+ <span class =" sr-only" >View {{ createPackageInfo.packageName }}</span >
1146+ </NuxtLink >
1147+ </div >
1148+ </template >
10851149 </div >
10861150 </div >
10871151 </div >
0 commit comments