Developerui
Install command
One command shown for npm, pnpm, bun, and yarn; the choice persists.
npx shadcn@latest add @particular/log-viewernpm install @particular-tech/sdknpm install --save-dev @particular-tech/cliInstallation
npx shadcn@latest add @particular/install-commandnpm dependencies
- cn@^0.4.0
Installs these registry items
Source
"use client"
import * as React from "react"
import { cn } from "cn"
import { CopyButton } from "@/components/ui/copy-button"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
const packageManagers = ["npm", "pnpm", "bun", "yarn"] as const
type PackageManager = (typeof packageManagers)[number]
type CommandSpec =
| { exec: string; install?: never; dev?: never }
| { install: string; dev?: boolean; exec?: never }
function formatCommand(pm: PackageManager, spec: CommandSpec): string {
if (spec.exec !== undefined) {
const runner = { npm: "npx", pnpm: "pnpm dlx", bun: "bunx --bun", yarn: "yarn dlx" }[pm]
return `${runner} ${spec.exec}`
}
const add = { npm: "npm install", pnpm: "pnpm add", bun: "bun add", yarn: "yarn add" }[pm]
const devFlag = spec.dev ? (pm === "npm" ? " --save-dev" : " -D") : ""
return `${add}${devFlag} ${spec.install}`
}
const STORAGE_KEY = "particular:package-manager"
const CHANGE_EVENT = "particular:package-manager-change"
function isPackageManager(value: unknown): value is PackageManager {
return packageManagers.some((pm) => pm === value)
}
function subscribe(onChange: () => void) {
window.addEventListener("storage", onChange)
window.addEventListener(CHANGE_EVENT, onChange)
return () => {
window.removeEventListener("storage", onChange)
window.removeEventListener(CHANGE_EVENT, onChange)
}
}
function readPackageManager(): PackageManager {
const stored = window.localStorage.getItem(STORAGE_KEY)
return isPackageManager(stored) ? stored : "npm"
}
/** The visitor's package manager, shared by every InstallCommand on the page and persisted. */
function usePackageManager() {
const pm = React.useSyncExternalStore(subscribe, readPackageManager, () => "npm" as const)
const setPm = React.useCallback((next: PackageManager) => {
window.localStorage.setItem(STORAGE_KEY, next)
window.dispatchEvent(new Event(CHANGE_EVENT))
}, [])
return [pm, setPm] as const
}
function InstallCommand({
className,
...props
}: CommandSpec & Omit<React.ComponentProps<"div">, keyof CommandSpec>) {
const { exec, install, dev, ...rest } = props
const spec: CommandSpec = exec !== undefined ? { exec } : { install: install ?? "", dev }
const [pm, setPm] = usePackageManager()
const command = formatCommand(pm, spec)
return (
<div
data-slot="install-command"
className={cn("overflow-hidden rounded-lg border bg-card", className)}
{...rest}
>
<Tabs
value={pm}
onValueChange={(value) => {
if (isPackageManager(value)) setPm(value)
}}
>
<div className="flex items-center justify-between gap-2 border-b bg-muted/40 pr-1.5 pl-1">
<TabsList variant="line" className="h-9">
{packageManagers.map((name) => (
<TabsTrigger key={name} value={name} className="font-mono text-xs">
{name}
</TabsTrigger>
))}
</TabsList>
<CopyButton value={command} size="icon-xs" label="Copy command" />
</div>
</Tabs>
<pre className="overflow-x-auto px-4 py-3 font-mono text-[0.8125rem]">
<code>
<span aria-hidden className="mr-2 text-muted-foreground select-none">
$
</span>
{command}
</code>
</pre>
</div>
)
}
export { InstallCommand, formatCommand, usePackageManager, packageManagers, type PackageManager, type CommandSpec }