# Package Command

A command with a tab for each package manager, and a copy button.

Source: [package-command.tsx](https://github.com/jakejarvis/jarvis-ui/blob/main/src/components/ui/package-command.tsx)

```tsx
import { PackageCommand } from "@/components/ui/package-command";

export default function PackageCommandBasic() {
  return <PackageCommand command="shadcn@latest add @jarvis/button" className="w-full max-w-lg" />;
}
```

## Installation

Add the registry, once per project:

```bash
npx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json
```

Then add the component:

```bash
npx shadcn@latest add @jarvis/package-command
```

## Usage

```tsx
import { PackageCommand } from "@/components/ui/package-command";

export function AddButton() {
  return <PackageCommand command="shadcn@latest add @jarvis/button" />;
}
```

Write `command` without the package manager, and each tab fills it in for pnpm, npm, yarn, Bun or
Vite+. Picking a tab switches every Package Command on the page, and the choice is saved in local
storage. Clicking anywhere in the command copies it.

## Examples

### Installing packages

`type="install"` adds packages, as `pnpm add` or `npm install`. Flags such as `-D` work too.

```tsx
import { PackageCommand } from "@/components/ui/package-command";

export default function PackageCommandInstall() {
  return <PackageCommand type="install" command="@base-ui/react" className="w-full max-w-lg" />;
}
```

### Running scripts

`type="run"` runs a script, as `pnpm dev` or `npm run dev`.

```tsx
import { PackageCommand } from "@/components/ui/package-command";

export default function PackageCommandRun() {
  return <PackageCommand type="run" command="dev" className="w-full max-w-lg" />;
}
```

### Monochrome logos

`iconVariant="mono"` draws the logos in the text color.

```tsx
import { PackageCommand } from "@/components/ui/package-command";

export default function PackageCommandMono() {
  return (
    <PackageCommand
      iconVariant="mono"
      command="shadcn@latest add @jarvis/button"
      className="w-full max-w-lg"
    />
  );
}
```

### Your own picker

`usePackageManager` returns the shared package manager and a setter. `getPackageCommand` writes a
command for one package manager, and `packageManagers` lists them with their logos.
`PackageCommandText` draws that command with its parts colored by the theme's syntax tokens, to
pass as the `children` of a [Copy Field](https://ui.jarv.is/components/copy-field.md).

```tsx
import { CodeBlock } from "@/components/ui/code-block";
import {
  getPackageCommand,
  packageManagers,
  usePackageManager,
} from "@/components/ui/package-command";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";

const items = packageManagers.map(({ id, Logo }) => ({
  value: id,
  label: (
    <>
      <Logo className="size-3.5" />
      {id}
    </>
  ),
}));

export default function PackageCommandCustom() {
  const [packageManager, setPackageManager] = usePackageManager();
  const code = [
    getPackageCommand(packageManager, "create-vite@latest my-app"),
    "cd my-app",
    getPackageCommand(packageManager, "@base-ui/react", "install"),
    getPackageCommand(packageManager, "dev", "run"),
  ].join("\n");

  return (
    <div className="flex w-full max-w-lg flex-col items-start gap-3">
      <Select
        items={items}
        value={packageManager}
        onValueChange={(value) => {
          const manager = packageManagers.find(({ id }) => id === value);
          if (manager) setPackageManager(manager.id);
        }}
      >
        <SelectTrigger size="sm" aria-label="Package manager">
          <SelectValue />
        </SelectTrigger>
        <SelectContent>
          {items.map(({ value, label }) => (
            <SelectItem key={value} value={value}>
              {label}
            </SelectItem>
          ))}
        </SelectContent>
      </Select>
      <CodeBlock language="bash" code={code} />
    </div>
  );
}
```

## Props

`PackageCommand` takes the props of [Tabs](https://ui.jarv.is/components/tabs.md), apart from `defaultValue`, `orientation`
and `children`. Passing `value` overrides the shared choice.

| Part | Extends |
| --- | --- |
| `PackageCommand` | [`Tabs`](https://ui.jarv.is/components/tabs.md) |
| `PackageCommandText` | — |

### PackageCommand

| Prop             | Type                                         | Description                                                                                                                                                                                          |
| ---------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `command`        | `string`                                     | The command after the package manager's own, such as `shadcn@latest add button`.                                                                                                                     |
| `type?`          | `"execute" \| "install" \| "run"`            | How each package manager runs the command: `execute` runs a package without installing it, `install` adds packages to the project, and `run` runs a script from `package.json`. Default: `"execute"` |
| `iconVariant?`   | `"color" \| "mono"`                          | Draws the package managers' logos in their own colors, or in the tab's text color. Default: `"color"`                                                                                                |
| `value?`         | `"bun" \| "npm" \| "pnpm" \| "vp" \| "yarn"` | The package manager to show, in place of the choice every Package Command shares. Picking a tab then calls `onValueChange` and leaves the shared choice alone.                                       |
| `onValueChange?` | `((value: PackageManager) => void)`          | Called with the package manager a tab picks.                                                                                                                                                         |
| `onCopy?`        | `((value: string) => void)`                  |                                                                                                                                                                                                      |

### PackageCommandText

| Prop      | Type                                         | Description                                                    |
| --------- | -------------------------------------------- | -------------------------------------------------------------- |
| `manager` | `"bun" \| "npm" \| "pnpm" \| "vp" \| "yarn"` | The package manager to write the command for.                  |
| `command` | `string`                                     | The command after the package manager's own.                   |
| `type?`   | `"execute" \| "install" \| "run"`            | How the package manager runs the command. Default: `"execute"` |
