Command Tabs
dominik-portfolio/command-tabsFreeComponent
A copyable command block with attached agent tabs and a sliding active pill — tabs can sit above or below the command.
Install it
npx shadcn@latest add https://dominikkoch.dev/r/command-tabs.jsonSource
1"use client";23import { CheckIcon, CopyIcon } from "@phosphor-icons/react";4import {5 type KeyboardEvent as ReactKeyboardEvent,6 type ReactNode,7 type PointerEvent as ReactPointerEvent,8 useCallback,9 useEffect,10 useId,11 useLayoutEffect,12 useRef,13 useState,14} from "react";15import { Button } from "@/components/ui/button";16import { cn } from "@/lib/utils";1718interface CommandTabsItem {19 value: string;20 label: string;21 command: string;22 icon?: ReactNode;23}2425interface CommandTabsProps {26 items: CommandTabsItem[];27 value?: string;28 defaultValue?: string;29 onValueChange?: (value: string) => void;30 tabsPosition?: "top" | "bottom" | "none";31 invertActiveIcon?: boolean;32 highlight?: boolean;33 label?: string;34 copyLabel?: string;35 onCopy?: (command: string) => void;36 className?: string;37}3839const COPY_FEEDBACK_MS = 2000;40const SCROLLBAR_HIDE_MS = 800;41const PERCENT = 100;42const SCROLL_STEP_PX = 40;4344type CommandTokenType = "command" | "string" | "flag" | "plain";4546interface CommandToken {47 text: string;48 type: CommandTokenType;49 start: number;50}5152const COMMAND_TOKEN_RE = /"[^"]*"?|'[^']*'?|\s+|[^\s"']+/g;53const WHITESPACE_RE = /^\s+$/;5455const TOKEN_CLASS: Record<CommandTokenType, string | undefined> = {56 command: "text-sky-600 dark:text-sky-400",57 string: "text-emerald-600 dark:text-emerald-400",58 flag: "text-muted-foreground",59 plain: undefined,60};6162function classifyToken(text: string, isFirstWord: boolean): CommandTokenType {63 if (text.startsWith('"') || text.startsWith("'")) {64 return "string";65 }66 if (text.startsWith("-")) {67 return "flag";68 }69 return isFirstWord ? "command" : "plain";70}7172function tokenizeCommand(command: string): CommandToken[] {73 const tokens: CommandToken[] = [];74 let firstWord = true;75 let start = 0;76 for (const text of command.match(COMMAND_TOKEN_RE) ?? []) {77 if (WHITESPACE_RE.test(text)) {78 tokens.push({ text, type: "plain", start });79 } else {80 tokens.push({ text, type: classifyToken(text, firstWord), start });81 firstWord = false;82 }83 start += text.length;84 }85 return tokens;86}8788function HighlightedCommand({ command }: { command: string }) {89 return tokenizeCommand(command).map((token) => (90 <span className={TOKEN_CLASS[token.type]} key={token.start}>91 {token.text}92 </span>93 ));94}9596interface CommandTabProps {97 item: CommandTabsItem;98 isActive: boolean;99 invertIcon: boolean;100 onSelect: (value: string) => void;101}102103// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from dominik-portfolio
All 4 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Braille Loaderbraille-loader | dominik-portfolio | Components | Free | no deps · 2 files | |
| Expandable Tabsexpandable-tabs | dominik-portfolio | Components | Free | no deps | |
| Permission Selectorpermission-selector | dominik-portfolio | Components | Free | 1 dep |
