{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "shortcuts-table",
  "title": "Shortcuts table",
  "description": "Shortcuts table is a production-ready tables component from BJORK UI, packaged for shadcn registries.",
  "dependencies": [
    "clsx",
    "framer-motion",
    "lucide-react",
    "next-themes",
    "tailwind-merge"
  ],
  "files": [
    {
      "path": "components/bjork-ui/tables/shortcuts-table.tsx",
      "content": "\"use client\";\n\nimport {\n  Fragment,\n  useEffect,\n  useId,\n  useMemo,\n  useRef,\n  useState,\n  useSyncExternalStore,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type ReactNode,\n} from \"react\";\nimport { motion, useReducedMotion } from \"framer-motion\";\nimport { useTheme } from \"next-themes\";\nimport { Search, X } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  type BjorkTableThemeMode,\n  getBjorkTablePalette,\n  useBjorkTableIsDark,\n} from \"./table-theme\";\n\nexport type ShortcutPlatform = \"mac\" | \"other\";\n\nexport interface ShortcutKeys {\n  /** A sequence of chords. `[[\"Cmd\", \"K\"], [\"Z\"]]` reads \"⌘ + K then Z\". */\n  mac: string[][];\n  other: string[][];\n}\n\nexport interface Shortcut {\n  id?: string;\n  action: string;\n  keys: ShortcutKeys;\n  description?: string;\n}\n\nexport interface ShortcutGroup {\n  id: string;\n  label: string;\n  shortcuts: Shortcut[];\n}\n\nexport interface ShortcutsTableProps {\n  groups?: ShortcutGroup[];\n  title?: string;\n  subtitle?: string;\n  /** \"auto\" reads the platform from the browser. The visitor can still switch. */\n  platform?: ShortcutPlatform | \"auto\";\n  onPlatformChange?: (platform: ShortcutPlatform) => void;\n  searchable?: boolean;\n  defaultQuery?: string;\n  onQueryChange?: (query: string) => void;\n  /** Max height of the scrolling body. Header and group headings stick. */\n  maxHeight?: number;\n  className?: string;\n  theme?: BjorkTableThemeMode;\n  enableAnimations?: boolean;\n}\n\nconst k = (mac: string[][], other: string[][]): ShortcutKeys => ({ mac, other });\n\nexport const SHORTCUTS_TABLE_SAMPLE: ShortcutGroup[] = [\n  {\n    id: \"general\",\n    label: \"General\",\n    shortcuts: [\n      { action: \"Command palette\", keys: k([[\"Cmd\", \"K\"]], [[\"Ctrl\", \"K\"]]), description: \"Run any command by name\" },\n      { action: \"Quick open\", keys: k([[\"Cmd\", \"P\"]], [[\"Ctrl\", \"P\"]]), description: \"Jump to a file in the workspace\" },\n      { action: \"Save\", keys: k([[\"Cmd\", \"S\"]], [[\"Ctrl\", \"S\"]]) },\n      { action: \"Save all\", keys: k([[\"Option\", \"Cmd\", \"S\"]], [[\"Ctrl\", \"Alt\", \"S\"]]) },\n      { action: \"Settings\", keys: k([[\"Cmd\", \",\"]], [[\"Ctrl\", \",\"]]) },\n      { action: \"Focus mode\", keys: k([[\"Cmd\", \"K\"], [\"Z\"]], [[\"Ctrl\", \"K\"], [\"Z\"]]), description: \"Hide every panel except the editor\" },\n    ],\n  },\n  {\n    id: \"navigation\",\n    label: \"Navigation\",\n    shortcuts: [\n      { action: \"Go to line\", keys: k([[\"Ctrl\", \"G\"]], [[\"Ctrl\", \"G\"]]) },\n      { action: \"Go to symbol\", keys: k([[\"Shift\", \"Cmd\", \"O\"]], [[\"Ctrl\", \"Shift\", \"O\"]]) },\n      { action: \"Go to definition\", keys: k([[\"F12\"]], [[\"F12\"]]) },\n      { action: \"Go back\", keys: k([[\"Ctrl\", \"-\"]], [[\"Alt\", \"Left\"]]) },\n      { action: \"Next tab\", keys: k([[\"Shift\", \"Cmd\", \"]\"]], [[\"Ctrl\", \"Tab\"]]) },\n      { action: \"Matching bracket\", keys: k([[\"Shift\", \"Cmd\", \"\\\\\"]], [[\"Ctrl\", \"Shift\", \"\\\\\"]]) },\n    ],\n  },\n  {\n    id: \"editing\",\n    label: \"Editing\",\n    shortcuts: [\n      { action: \"Select next match\", keys: k([[\"Cmd\", \"D\"]], [[\"Ctrl\", \"D\"]]), description: \"Adds a cursor at the next occurrence\" },\n      { action: \"Add cursor below\", keys: k([[\"Option\", \"Cmd\", \"Down\"]], [[\"Ctrl\", \"Alt\", \"Down\"]]) },\n      { action: \"Move line up\", keys: k([[\"Option\", \"Up\"]], [[\"Alt\", \"Up\"]]) },\n      { action: \"Duplicate line\", keys: k([[\"Shift\", \"Option\", \"Down\"]], [[\"Shift\", \"Alt\", \"Down\"]]) },\n      { action: \"Delete line\", keys: k([[\"Shift\", \"Cmd\", \"K\"]], [[\"Ctrl\", \"Shift\", \"K\"]]) },\n      { action: \"Toggle comment\", keys: k([[\"Cmd\", \"/\"]], [[\"Ctrl\", \"/\"]]) },\n      { action: \"Rename symbol\", keys: k([[\"F2\"]], [[\"F2\"]]), description: \"Updates every reference in the project\" },\n      { action: \"Format document\", keys: k([[\"Shift\", \"Option\", \"F\"]], [[\"Shift\", \"Alt\", \"F\"]]) },\n    ],\n  },\n  {\n    id: \"view\",\n    label: \"View\",\n    shortcuts: [\n      { action: \"Toggle sidebar\", keys: k([[\"Cmd\", \"B\"]], [[\"Ctrl\", \"B\"]]) },\n      { action: \"Toggle terminal\", keys: k([[\"Ctrl\", \"`\"]], [[\"Ctrl\", \"`\"]]) },\n      { action: \"Split editor\", keys: k([[\"Cmd\", \"\\\\\"]], [[\"Ctrl\", \"\\\\\"]]) },\n      { action: \"Fold all\", keys: k([[\"Cmd\", \"K\"], [\"Cmd\", \"0\"]], [[\"Ctrl\", \"K\"], [\"Ctrl\", \"0\"]]) },\n      { action: \"Unfold all\", keys: k([[\"Cmd\", \"K\"], [\"Cmd\", \"J\"]], [[\"Ctrl\", \"K\"], [\"Ctrl\", \"J\"]]) },\n      { action: \"Zoom in\", keys: k([[\"Cmd\", \"=\"]], [[\"Ctrl\", \"=\"]]) },\n      { action: \"Zoom out\", keys: k([[\"Cmd\", \"-\"]], [[\"Ctrl\", \"-\"]]) },\n    ],\n  },\n];\n\n// ─── keys ────────────────────────────────────────────────────────────────────\n\ninterface KeyInfo {\n  glyph: string;\n  spoken: string;\n  tokens: string[];\n}\n\nconst KEY_ALIASES: Record<string, string> = {\n  command: \"cmd\",\n  meta: \"cmd\",\n  super: \"cmd\",\n  win: \"cmd\",\n  windows: \"cmd\",\n  control: \"ctrl\",\n  opt: \"option\",\n  return: \"enter\",\n  escape: \"esc\",\n  arrowup: \"up\",\n  arrowdown: \"down\",\n  arrowleft: \"left\",\n  arrowright: \"right\",\n};\n\nconst MAC_KEYS: Record<string, [glyph: string, spoken: string]> = {\n  cmd: [\"⌘\", \"Command\"],\n  option: [\"⌥\", \"Option\"],\n  alt: [\"⌥\", \"Option\"],\n  shift: [\"⇧\", \"Shift\"],\n  ctrl: [\"⌃\", \"Control\"],\n  enter: [\"↵\", \"Return\"],\n  backspace: [\"⌫\", \"Delete\"],\n  delete: [\"⌦\", \"Forward delete\"],\n  tab: [\"⇥\", \"Tab\"],\n  esc: [\"esc\", \"Escape\"],\n  space: [\"space\", \"Space\"],\n  up: [\"↑\", \"Up arrow\"],\n  down: [\"↓\", \"Down arrow\"],\n  left: [\"←\", \"Left arrow\"],\n  right: [\"→\", \"Right arrow\"],\n};\n\nconst OTHER_KEYS: Record<string, [glyph: string, spoken: string]> = {\n  cmd: [\"Win\", \"Windows key\"],\n  option: [\"Alt\", \"Alt\"],\n  alt: [\"Alt\", \"Alt\"],\n  shift: [\"Shift\", \"Shift\"],\n  ctrl: [\"Ctrl\", \"Control\"],\n  enter: [\"Enter\", \"Enter\"],\n  backspace: [\"Backspace\", \"Backspace\"],\n  delete: [\"Del\", \"Delete\"],\n  tab: [\"Tab\", \"Tab\"],\n  esc: [\"Esc\", \"Escape\"],\n  space: [\"Space\", \"Space\"],\n  up: [\"↑\", \"Up arrow\"],\n  down: [\"↓\", \"Down arrow\"],\n  left: [\"←\", \"Left arrow\"],\n  right: [\"→\", \"Right arrow\"],\n};\n\nconst SYMBOL_NAMES: Record<string, string> = {\n  \",\": \"Comma\",\n  \".\": \"Period\",\n  \"/\": \"Slash\",\n  \"\\\\\": \"Backslash\",\n  \"`\": \"Backtick\",\n  \"-\": \"Minus\",\n  \"=\": \"Equals\",\n  \"[\": \"Left bracket\",\n  \"]\": \"Right bracket\",\n  \";\": \"Semicolon\",\n};\n\nfunction describeKey(raw: string, platform: ShortcutPlatform): KeyInfo {\n  const lower = raw.toLowerCase();\n  const canonical = KEY_ALIASES[lower] ?? lower;\n  const table = platform === \"mac\" ? MAC_KEYS : OTHER_KEYS;\n  const known = table[canonical];\n  const glyph = known?.[0] ?? (raw.length === 1 ? raw.toUpperCase() : raw);\n  const spoken = known?.[1] ?? SYMBOL_NAMES[raw] ?? raw;\n  const tokens = new Set([lower, canonical, glyph.toLowerCase(), spoken.toLowerCase()]);\n  for (const [alias, target] of Object.entries(KEY_ALIASES)) if (target === canonical) tokens.add(alias);\n  return { glyph, spoken, tokens: [...tokens] };\n}\n\n// ─── helpers ─────────────────────────────────────────────────────────────────\n\nconst noopSubscribe = () => () => {};\n\nfunction useMounted() {\n  return useSyncExternalStore(noopSubscribe, () => true, () => false);\n}\n\nfunction useIsNarrow<T extends HTMLElement>(threshold: number) {\n  const ref = useRef<T>(null);\n  const [narrow, setNarrow] = useState(false);\n  useEffect(() => {\n    const node = ref.current;\n    if (!node || typeof ResizeObserver === \"undefined\") return;\n    const observer = new ResizeObserver((entries) => {\n      const width = entries[0]?.contentRect.width ?? 0;\n      setNarrow(width > 0 && width < threshold);\n    });\n    observer.observe(node);\n    return () => observer.disconnect();\n  }, [threshold]);\n  return [ref, narrow] as const;\n}\n\nfunction detectPlatform(): ShortcutPlatform {\n  const nav = navigator as Navigator & { userAgentData?: { platform?: string } };\n  const name = nav.userAgentData?.platform || nav.platform || nav.userAgent;\n  return /mac|iphone|ipad|ipod/i.test(name) ? \"mac\" : \"other\";\n}\n\nfunction useDetectedPlatform() {\n  return useSyncExternalStore<ShortcutPlatform>(noopSubscribe, detectPlatform, () => \"mac\");\n}\n\nfunction keyMatches(info: KeyInfo, terms: string[]) {\n  return terms.some((term) =>\n    info.tokens.some((token) => (term.length === 1 ? token === term : token === term || (term.length > 2 && token.startsWith(term))))\n  );\n}\n\nfunction Highlight({ text, query, markClassName }: { text: string; query: string; markClassName: string }) {\n  if (!query) return <>{text}</>;\n  const lower = text.toLowerCase();\n  const out: ReactNode[] = [];\n  let from = 0;\n  let at = lower.indexOf(query, from);\n  while (at !== -1 && query.length > 0) {\n    if (at > from) out.push(<Fragment key={`t${from}`}>{text.slice(from, at)}</Fragment>);\n    out.push(\n      <mark key={`m${at}`} className={markClassName}>\n        {text.slice(at, at + query.length)}\n      </mark>\n    );\n    from = at + query.length;\n    at = lower.indexOf(query, from);\n  }\n  if (from < text.length) out.push(<Fragment key={`t${from}`}>{text.slice(from)}</Fragment>);\n  return <>{out}</>;\n}\n\n// ─── component ───────────────────────────────────────────────────────────────\n\nconst HEADER_HEIGHT = 38;\n\nexport function ShortcutsTable({\n  groups = SHORTCUTS_TABLE_SAMPLE,\n  title = \"Keyboard shortcuts\",\n  subtitle = \"Tessel editor\",\n  platform: platformProp = \"auto\",\n  onPlatformChange,\n  searchable = true,\n  defaultQuery = \"\",\n  onQueryChange,\n  maxHeight = 520,\n  className,\n  theme = \"auto\",\n  enableAnimations = true,\n}: ShortcutsTableProps) {\n  const mounted = useMounted();\n  const { resolvedTheme } = useTheme();\n  const forcedTheme = theme === \"auto\" ? undefined : theme;\n  const detectedIsDark = useBjorkTableIsDark(resolvedTheme, forcedTheme);\n  const isDark = !mounted && theme === \"auto\" ? true : detectedIsDark;\n  const palette = getBjorkTablePalette(isDark);\n  const shouldReduceMotion = useReducedMotion();\n  const shouldAnimate = enableAnimations && !shouldReduceMotion;\n  const uid = useId();\n  const [rootRef, narrow] = useIsNarrow<HTMLDivElement>(480);\n\n  const detected = useDetectedPlatform();\n  const [override, setOverride] = useState<ShortcutPlatform | null>(null);\n  const platform: ShortcutPlatform = override ?? (platformProp === \"auto\" ? detected : platformProp);\n\n  const [query, setQuery] = useState(defaultQuery);\n  const [intro, setIntro] = useState(true);\n  const searchRef = useRef<HTMLInputElement>(null);\n  const platformRefs = useRef(new Map<ShortcutPlatform, HTMLButtonElement>());\n\n  useEffect(() => {\n    const t = window.setTimeout(() => setIntro(false), 900);\n    return () => window.clearTimeout(t);\n  }, []);\n\n  const normalized = query.trim().toLowerCase();\n  const terms = useMemo(() => normalized.split(/[\\s+]+/).filter(Boolean), [normalized]);\n\n  const { visible, matchCount, totalCount } = useMemo(() => {\n    const total = groups.reduce((sum, g) => sum + g.shortcuts.length, 0);\n    const out = groups\n      .map((group) => {\n        const rows = group.shortcuts\n          .map((shortcut) => {\n            const chords = shortcut.keys[platform].map((chord) => chord.map((key) => describeKey(key, platform)));\n            const haystack = `${shortcut.action} ${shortcut.description ?? \"\"} ${group.label}`.toLowerCase();\n            const textHit = normalized.length > 0 && haystack.includes(normalized);\n            const keyHits = chords.map((chord) => chord.map((info) => terms.length > 0 && keyMatches(info, terms)));\n            const words = haystack.split(/[^a-z0-9]+/);\n            // Short terms (\"k\", \"f2\") match word starts only, so \"k\" doesn't hit \"quick\".\n            const inText = (term: string) =>\n              term.length > 2 ? haystack.includes(term) : words.some((word) => word.startsWith(term));\n            const allTermsHit =\n              terms.length > 0 &&\n              terms.every((term) => inText(term) || chords.some((c) => c.some((info) => keyMatches(info, [term]))));\n            const hit = normalized.length === 0 || textHit || allTermsHit;\n            return { shortcut, chords, keyHits: textHit && !allTermsHit ? null : keyHits, hit };\n          })\n          .filter((r) => r.hit);\n        return { group, rows };\n      })\n      .filter((g) => g.rows.length > 0);\n    const matched = out.reduce((sum, g) => sum + g.rows.length, 0);\n    return { visible: out, matchCount: matched, totalCount: total };\n  }, [groups, platform, normalized, terms]);\n\n  const choosePlatform = (next: ShortcutPlatform, focus = false) => {\n    setOverride(next);\n    onPlatformChange?.(next);\n    if (focus) platformRefs.current.get(next)?.focus();\n  };\n\n  const updateQuery = (next: string) => {\n    setQuery(next);\n    onQueryChange?.(next);\n  };\n\n  const handleRootKey = (event: ReactKeyboardEvent<HTMLDivElement>) => {\n    if (!searchable || event.key !== \"/\" || event.metaKey || event.ctrlKey || event.altKey) return;\n    const target = event.target as HTMLElement;\n    if (target.tagName === \"INPUT\" || target.tagName === \"TEXTAREA\" || target.isContentEditable) return;\n    event.preventDefault();\n    searchRef.current?.focus();\n    searchRef.current?.select();\n  };\n\n  const platforms: { id: ShortcutPlatform; label: string }[] = [\n    { id: \"mac\", label: \"Mac\" },\n    { id: \"other\", label: \"Windows · Linux\" },\n  ];\n\n  const handlePlatformKey = (event: ReactKeyboardEvent<HTMLButtonElement>, index: number) => {\n    if (![\"ArrowLeft\", \"ArrowRight\", \"ArrowUp\", \"ArrowDown\"].includes(event.key)) return;\n    event.preventDefault();\n    choosePlatform(platforms[(index + 1) % platforms.length].id, true);\n  };\n\n  const focusRing = \"outline-none focus-visible:ring-2 focus-visible:ring-[#ec5c13]/60\";\n  const headerLabel = \"text-[11px] font-medium uppercase tracking-[0.08em]\";\n  const surface = isDark ? \"bg-[#111]\" : \"bg-[#fffcf6]\";\n  const markClass = cn(\"rounded-[3px] px-[1px] text-inherit\", isDark ? \"bg-[#ec5c13]/28\" : \"bg-[#ec5c13]/20\");\n  const keyCap = isDark\n    ? \"border-[#2e2e2e] border-b-[#3a3a3a] bg-[#1a1a1a] text-[#ededed]/86\"\n    : \"border-[#e4d9c7] border-b-[#d6c8b2] bg-[#fffcf6] text-[#171717]/80 shadow-[0_1px_0_rgba(62,52,38,0.06)]\";\n  const keyCapHit = isDark\n    ? \"border-[#ec5c13]/50 border-b-[#ec5c13]/70 bg-[#ec5c13]/14 text-[#f0a274]\"\n    : \"border-[#bd4514]/40 border-b-[#bd4514]/60 bg-[#ec5c13]/10 text-[#9f4317]\";\n\n  let rowIndex = 0;\n\n  return (\n    <div\n      ref={rootRef}\n      onKeyDown={handleRootKey}\n      className={cn(\"w-full overflow-hidden rounded-[18px] border text-[13px]\", palette.container, className)}\n    >\n      <div className={cn(\"flex flex-wrap items-center gap-x-4 gap-y-3 px-5 pb-3.5 pt-4\", narrow && \"px-4\")}>\n        <div className=\"mr-auto min-w-0\">\n          <h3 className={cn(\"text-[15px] font-semibold tracking-[-0.01em]\", palette.primaryText)}>{title}</h3>\n          {subtitle ? <p className={cn(\"mt-0.5 text-[12px]\", palette.secondaryText)}>{subtitle}</p> : null}\n        </div>\n        <div\n          role=\"radiogroup\"\n          aria-label=\"Platform\"\n          className={cn(\"grid grid-cols-2 gap-1 rounded-[10px] border p-[3px]\", palette.divider, palette.mutedSurface)}\n        >\n          {platforms.map((p, index) => {\n            const selected = p.id === platform;\n            return (\n              <button\n                key={p.id}\n                ref={(node) => {\n                  if (node) platformRefs.current.set(p.id, node);\n                  else platformRefs.current.delete(p.id);\n                }}\n                type=\"button\"\n                role=\"radio\"\n                aria-checked={selected}\n                tabIndex={selected ? 0 : -1}\n                onClick={() => choosePlatform(p.id)}\n                onKeyDown={(e) => handlePlatformKey(e, index)}\n                className={cn(\n                  \"relative whitespace-nowrap rounded-[7px] px-2.5 py-1 text-[12px] font-medium transition-colors motion-reduce:transition-none\",\n                  selected ? palette.primaryText : palette.secondaryText,\n                  focusRing\n                )}\n              >\n                {selected ? (\n                  <motion.span\n                    layoutId={shouldAnimate ? `${uid}-platform` : undefined}\n                    transition={{ type: \"spring\", stiffness: 520, damping: 40 }}\n                    className={cn(\n                      \"absolute inset-0 rounded-[7px] border\",\n                      isDark ? \"border-[#2c2c2c] bg-[#222]\" : \"border-[#eadfce] bg-[#fffcf6] shadow-[var(--bjork-shadow-soft)]\"\n                    )}\n                  />\n                ) : null}\n                <span className=\"relative\">{p.label}</span>\n              </button>\n            );\n          })}\n        </div>\n        {searchable ? (\n          <div className=\"relative w-full\">\n            <Search\n              size={14}\n              aria-hidden\n              className={cn(\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2\", palette.secondaryText)}\n            />\n            <input\n              ref={searchRef}\n              type=\"search\"\n              value={query}\n              onChange={(e) => updateQuery(e.target.value)}\n              onKeyDown={(e) => {\n                if (e.key === \"Escape\" && query) {\n                  e.preventDefault();\n                  updateQuery(\"\");\n                }\n              }}\n              placeholder=\"Search actions or keys, e.g. “cmd k”\"\n              aria-label=\"Search shortcuts\"\n              aria-controls={`${uid}-table`}\n              className={cn(\n                \"h-9 w-full rounded-[10px] border pl-8 pr-14 text-[13px] [&::-webkit-search-cancel-button]:appearance-none\",\n                isDark\n                  ? \"border-[#232323] bg-[#161616] text-[#ededed]/90 placeholder:text-[#ededed]/30\"\n                  : \"border-[#eadfce] bg-[#fbf7ef] text-[#171717]/86 placeholder:text-[#171717]/38\",\n                \"outline-none focus-visible:border-[#ec5c13]/50 focus-visible:ring-2 focus-visible:ring-[#ec5c13]/30\"\n              )}\n            />\n            <span className=\"absolute right-2 top-1/2 flex -translate-y-1/2 items-center\">\n              {query ? (\n                <button\n                  type=\"button\"\n                  onClick={() => {\n                    updateQuery(\"\");\n                    searchRef.current?.focus();\n                  }}\n                  aria-label=\"Clear search\"\n                  className={cn(\"grid size-6 place-items-center rounded-[6px]\", palette.secondaryText, palette.menuItem, focusRing)}\n                >\n                  <X size={13} />\n                </button>\n              ) : (\n                <kbd\n                  aria-hidden\n                  className={cn(\"grid h-[20px] min-w-[20px] place-items-center rounded-[5px] border px-1 font-sans text-[11px]\", keyCap)}\n                >\n                  /\n                </kbd>\n              )}\n            </span>\n          </div>\n        ) : null}\n      </div>\n\n      <div\n        role=\"region\"\n        aria-label={`${title} list`}\n        tabIndex={0}\n        className={cn(\"overflow-auto overscroll-contain border-t\", palette.divider, focusRing)}\n        style={{ maxHeight }}\n      >\n        {visible.length === 0 ? (\n          <div className=\"flex flex-col items-center gap-1 px-6 py-12 text-center\">\n            <span className={cn(\"text-[13px] font-medium\", palette.primaryText)}>\n              No shortcuts match “{query.trim()}”\n            </span>\n            <span className={cn(\"text-[12.5px]\", palette.secondaryText)}>Try an action like “rename”, or a key like “shift”.</span>\n            <button\n              type=\"button\"\n              onClick={() => {\n                updateQuery(\"\");\n                searchRef.current?.focus();\n              }}\n              className={cn(\"mt-3 rounded-[8px] border px-3 py-1.5 text-[12.5px] transition-colors\", palette.control, focusRing)}\n            >\n              Clear search\n            </button>\n          </div>\n        ) : (\n          <table id={`${uid}-table`} className=\"w-full table-fixed border-separate border-spacing-0 text-left\">\n            <caption className=\"sr-only\">\n              {title} for {platform === \"mac\" ? \"Mac\" : \"Windows and Linux\"}\n            </caption>\n            <colgroup>\n              <col />\n              <col style={{ width: narrow ? \"48%\" : \"42%\" }} />\n            </colgroup>\n            <thead>\n              <tr className={palette.header}>\n                <th\n                  scope=\"col\"\n                  className={cn(\"sticky top-0 z-20 border-b pl-5 pr-3\", narrow && \"pl-4\", palette.header, headerLabel)}\n                  style={{ height: HEADER_HEIGHT }}\n                >\n                  Action\n                </th>\n                <th\n                  scope=\"col\"\n                  className={cn(\"sticky top-0 z-20 border-b pl-3 pr-5 text-right\", narrow && \"pr-4\", palette.header, headerLabel)}\n                  style={{ height: HEADER_HEIGHT }}\n                >\n                  Shortcut\n                </th>\n              </tr>\n            </thead>\n            {visible.map(({ group, rows }) => (\n              <tbody key={group.id}>\n                <tr>\n                  <th\n                    scope=\"colgroup\"\n                    colSpan={2}\n                    className={cn(\"sticky z-10 border-b pb-2 pl-5 pr-5 pt-3.5 text-left\", narrow && \"px-4\", surface, palette.divider)}\n                    style={{ top: HEADER_HEIGHT }}\n                  >\n                    <span className=\"flex items-baseline justify-between\">\n                      <span className={cn(headerLabel, palette.accent)}>{group.label}</span>\n                      <span className={cn(\"text-[11px] tabular-nums\", palette.secondaryText)}>{rows.length}</span>\n                    </span>\n                  </th>\n                </tr>\n                {rows.map(({ shortcut, chords, keyHits }, i) => {\n                  const index = rowIndex++;\n                  const last = i === rows.length - 1;\n                  const cell = cn(\"border-b py-2.5 align-middle\", last ? palette.divider : isDark ? \"border-[#1c1c1c]\" : \"border-[#f5eee3]\");\n                  return (\n                    <motion.tr\n                      key={shortcut.id ?? shortcut.action}\n                      initial={intro && shouldAnimate ? { opacity: 0, y: 6 } : false}\n                      animate={{ opacity: 1, y: 0 }}\n                      transition={{ duration: 0.3, ease: [0.22, 1, 0.36, 1], delay: intro ? 0.04 + index * 0.016 : 0 }}\n                      className={cn(\"transition-colors duration-150 motion-reduce:transition-none\", palette.row)}\n                    >\n                      <td className={cn(cell, \"pl-5 pr-3\", narrow && \"pl-4\")}>\n                        <span className={cn(\"block\", palette.primaryText)}>\n                          <Highlight text={shortcut.action} query={normalized} markClassName={markClass} />\n                        </span>\n                        {shortcut.description ? (\n                          <span className={cn(\"mt-0.5 block text-[12px] leading-[1.4]\", palette.secondaryText)}>\n                            <Highlight text={shortcut.description} query={normalized} markClassName={markClass} />\n                          </span>\n                        ) : null}\n                      </td>\n                      <td className={cn(cell, \"pl-2 pr-5\", narrow && \"pr-4\")}>\n                        <span className=\"flex flex-wrap items-center justify-end gap-x-1.5 gap-y-1\">\n                          {chords.map((chord, c) => (\n                            <Fragment key={c}>\n                              {c > 0 ? (\n                                <span className={cn(\"px-0.5 text-[11px]\", palette.secondaryText)}>then</span>\n                              ) : null}\n                              <kbd className=\"inline-flex items-center gap-[3px] font-sans\">\n                                {chord.map((info, j) => {\n                                  const hit = keyHits?.[c]?.[j] ?? false;\n                                  return (\n                                    <Fragment key={j}>\n                                      {j > 0 ? (\n                                        <>\n                                          <span aria-hidden className={cn(\"text-[11px]\", palette.secondaryText, \"opacity-70\")}>\n                                            +\n                                          </span>\n                                          <span className=\"sr-only\"> plus </span>\n                                        </>\n                                      ) : null}\n                                      <kbd\n                                        className={cn(\n                                          \"inline-grid h-[22px] min-w-[22px] place-items-center rounded-[6px] border border-b-2 px-[6px] font-sans text-[11.5px] font-medium leading-none tabular-nums transition-colors duration-150 motion-reduce:transition-none\",\n                                          hit ? keyCapHit : keyCap\n                                        )}\n                                      >\n                                        <span aria-hidden>{info.glyph}</span>\n                                        <span className=\"sr-only\">{info.spoken}</span>\n                                      </kbd>\n                                    </Fragment>\n                                  );\n                                })}\n                              </kbd>\n                            </Fragment>\n                          ))}\n                        </span>\n                      </td>\n                    </motion.tr>\n                  );\n                })}\n              </tbody>\n            ))}\n          </table>\n        )}\n      </div>\n\n      <div\n        className={cn(\n          \"flex items-center justify-between gap-3 border-t px-5 py-2.5 text-[11.5px]\",\n          narrow && \"px-4\",\n          palette.divider,\n          palette.secondaryText\n        )}\n      >\n        <span aria-live=\"polite\" className=\"tabular-nums\">\n          {normalized ? `${matchCount} of ${totalCount} shortcuts` : `${totalCount} shortcuts`}\n        </span>\n        {searchable && !narrow ? (\n          <span>\n            Press <span className={cn(\"font-medium\", palette.primaryText)}>/</span> to search\n          </span>\n        ) : null}\n      </div>\n    </div>\n  );\n}\n\nexport default ShortcutsTable;\n",
      "type": "registry:component",
      "target": "@components/bjork-ui/tables/shortcuts-table.tsx"
    },
    {
      "path": "lib/utils.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n",
      "type": "registry:lib",
      "target": "@lib/utils.ts"
    },
    {
      "path": "components/bjork-ui/tables/table-theme.ts",
      "content": "import { useEffect, useState } from \"react\";\n\nexport type BjorkTableThemeMode = \"light\" | \"dark\" | \"auto\";\n\nexport function useBjorkTableIsDark(\n  resolvedTheme?: string,\n  forcedTheme?: Exclude<BjorkTableThemeMode, \"auto\">\n) {\n  const [documentTheme, setDocumentTheme] = useState<\"light\" | \"dark\" | null>(null);\n  const [previewTheme, setPreviewTheme] = useState<\"light\" | \"dark\" | null>(null);\n  // Server and first client render must agree, otherwise React keeps the\n  // server-rendered (dark) classes when the real theme resolves to light.\n  const [mounted, setMounted] = useState(false);\n\n  useEffect(() => {\n    setMounted(true);\n    const syncDocumentTheme = () => {\n      const root = document.documentElement;\n      const searchParams = new URLSearchParams(window.location.search);\n      const theme = searchParams.get(\"theme\");\n\n      setPreviewTheme(theme === \"light\" || theme === \"dark\" ? theme : null);\n\n      setDocumentTheme(\n        root.classList.contains(\"light\")\n          ? \"light\"\n          : root.classList.contains(\"dark\")\n            ? \"dark\"\n            : null\n      );\n    };\n\n    syncDocumentTheme();\n\n    const observer = new MutationObserver(syncDocumentTheme);\n    observer.observe(document.documentElement, {\n      attributes: true,\n      attributeFilter: [\"class\"],\n    });\n\n    return () => observer.disconnect();\n  }, []);\n\n  const activeTheme =\n    forcedTheme ?? previewTheme ?? documentTheme ?? (mounted ? resolvedTheme : undefined);\n\n  return activeTheme !== \"light\";\n}\n\nexport function getBjorkTablePalette(isDark: boolean) {\n  return {\n    container: isDark\n      ? \"border-[#1c1c1c] bg-[#121212] shadow-[inset_0_7px_14px_rgba(255,255,255,0.03),inset_0_0.5px_0.5px_rgba(255,255,255,0.06),0_14px_20px_-6px_rgba(0,0,0,0.45)]\"\n      : \"border-[#f5ede2] bg-[#fffcf6] shadow-[inset_0_7px_14px_rgba(88,72,49,0.045),inset_0_0.5px_0.5px_rgba(255,255,255,0.92),inset_1px_0_0_rgba(88,72,49,0.026),inset_-1px_0_0_rgba(255,255,255,0.68),0_14px_22px_-9px_rgba(66,52,33,0.11)]\",\n    header: isDark\n      ? \"border-[#232323] bg-[#181818] text-[#ededed]/42\"\n      : \"border-[#f1e8dc] bg-[#fbf7ef] text-[#171717]/60\",\n    row: isDark\n      ? \"border-[#232323] hover:bg-[#181818]/70\"\n      : \"border-[#f1e8dc] hover:bg-[#f8f2e7]/72\",\n    selectedRow: isDark\n      ? \"border-[#2a2a2a] bg-[#181818]\"\n      : \"border-[#eadfce] bg-[#f8f2e7]\",\n    control: isDark\n      ? \"border-[#232323] bg-[#181818] text-[#ededed]/70 hover:bg-[#202020] shadow-[inset_0_1px_0_rgba(255,255,255,0.045)]\"\n      : \"border-[#eadfce] bg-[#fffcf6] text-[#171717]/68 shadow-[var(--bjork-shadow-soft)] hover:bg-[#f8f2e7]\",\n    menu: isDark\n      ? \"border-[#232323] bg-[#181818] text-[#ededed]/76 shadow-[inset_0_1px_0_rgba(255,255,255,0.055),inset_0_12px_24px_rgba(255,255,255,0.018),0_22px_42px_-22px_rgba(0,0,0,0.9)]\"\n      : \"border-[#eadfce] bg-[#fffcf6] text-[#171717]/76 shadow-[inset_0_1px_0_rgba(88,72,49,0.04),inset_0_12px_24px_rgba(88,72,49,0.022),0_22px_44px_-26px_rgba(66,52,33,0.22)]\",\n    menuItem: isDark ? \"hover:bg-[#232323]\" : \"hover:bg-[#f8f2e7]\",\n    menuActive: isDark ? \"bg-[#232323]\" : \"bg-[#f8f2e7]\",\n    divider: isDark ? \"border-[#232323]\" : \"border-[#f1e8dc]\",\n    primaryText: isDark ? \"text-[#ededed]/90\" : \"text-[#171717]/86\",\n    secondaryText: isDark ? \"text-[#ededed]/38\" : \"text-[#171717]/60\",\n    mutedSurface: isDark ? \"bg-[#181818]\" : \"bg-[#f8f2e7]\",\n    popover: isDark ? \"bg-[#111] text-[#ededed]\" : \"bg-[#fffcf6] text-[#171717]\",\n    accent: isDark ? \"text-[#d86a2c]\" : \"text-[#bd4514]\",\n    accentBg: isDark ? \"bg-[#ec5c13]/12\" : \"bg-[#ec5c13]/14\",\n    accentBorder: isDark ? \"border-[#ec5c13]/24\" : \"border-[#bd4514]/22\",\n    checkboxAccent: isDark ? \"#d86a2c\" : \"#bd4514\",\n  };\n}\n\nexport function getBjorkSignalPalette(\n  tone: \"green\" | \"red\" | \"orange\" | \"neutral\",\n  isDark: boolean\n) {\n  const palettes = {\n    green: {\n      bgColor: isDark ? \"bg-[#3e6f52]/18\" : \"bg-[#dce8dc]\",\n      borderColor: isDark ? \"border-[#5f9f78]/28\" : \"border-[#bad0bd]\",\n      textColor: isDark ? \"text-[#77b58b]\" : \"text-[#47785c]\",\n      dotColor: isDark ? \"bg-[#77b58b]\" : \"bg-[#47785c]\",\n      color: isDark ? \"#77b58b\" : \"#47785c\",\n    },\n    red: {\n      bgColor: isDark ? \"bg-[#87463f]/18\" : \"bg-[#eadad5]\",\n      borderColor: isDark ? \"border-[#c46a61]/28\" : \"border-[#d7bcb4]\",\n      textColor: isDark ? \"text-[#cf7c72]\" : \"text-[#9d5149]\",\n      dotColor: isDark ? \"bg-[#cf7c72]\" : \"bg-[#9d5149]\",\n      color: isDark ? \"#cf7c72\" : \"#9d5149\",\n    },\n    orange: {\n      bgColor: isDark ? \"bg-[#ec5c13]/14\" : \"bg-[#ead9cb]\",\n      borderColor: isDark ? \"border-[#ec5c13]/28\" : \"border-[#d5b59f]\",\n      textColor: isDark ? \"text-[#d86a2c]\" : \"text-[#9f4317]\",\n      dotColor: isDark ? \"bg-[#d86a2c]\" : \"bg-[#9f4317]\",\n      color: isDark ? \"#d86a2c\" : \"#9f4317\",\n    },\n    neutral: {\n      bgColor: isDark ? \"bg-[#ededed]/8\" : \"bg-[#ded7ca]\",\n      borderColor: isDark ? \"border-[#ededed]/14\" : \"border-[#cfc6b7]\",\n      textColor: isDark ? \"text-[#ededed]/58\" : \"text-[#171717]/60\",\n      dotColor: isDark ? \"bg-[#ededed]/45\" : \"bg-[#171717]/42\",\n      color: isDark ? \"#a3a3a3\" : \"#706b62\",\n    },\n  } as const;\n\n  return palettes[tone];\n}\n",
      "type": "registry:component",
      "target": "@components/bjork-ui/tables/table-theme.ts"
    }
  ],
  "cssVars": {
    "light": {
      "bjork-bg": "#f7f3ea",
      "bjork-surface": "#fffcf6",
      "bjork-surface-muted": "rgba(250, 246, 237, 0.94)",
      "bjork-surface-hover": "#f5efe3",
      "bjork-surface-active": "#efe7d8",
      "bjork-panel": "#f8f2e7",
      "bjork-menu": "rgba(255, 252, 246, 0.98)",
      "bjork-field": "rgba(255, 252, 246, 0.96)",
      "bjork-field-muted": "rgba(248, 242, 231, 0.82)",
      "bjork-field-inset": "rgba(239, 231, 216, 0.72)",
      "bjork-error-bg": "#fff0e8",
      "bjork-text": "#171717",
      "bjork-text-strong": "rgba(23, 23, 23, 0.9)",
      "bjork-text-medium": "rgba(23, 23, 23, 0.78)",
      "bjork-text-muted": "rgba(23, 23, 23, 0.68)",
      "bjork-text-soft": "rgba(23, 23, 23, 0.62)",
      "bjork-text-faint": "rgba(23, 23, 23, 0.22)",
      "bjork-inverted-text": "#fff8f0",
      "bjork-border": "#eee6db",
      "bjork-border-muted": "#f5ede2",
      "bjork-border-strong": "#e1d7c8",
      "bjork-thumb": "#a49b8e",
      "bjork-track": "#ded6ca",
      "bjork-accent": "#ec7d43",
      "bjork-accent-hover": "#f0935f",
      "bjork-accent-soft": "rgba(236, 125, 67, 0.1)",
      "bjork-accent-muted": "rgba(236, 125, 67, 0.2)",
      "bjork-accent-badge": "rgba(236, 125, 67, 0.16)",
      "bjork-accent-badge-foreground": "#8d421d",
      "bjork-accent-foreground": "#3f2112",
      "bjork-accent-fill": "#ec7d43",
      "bjork-accent-fill-hover": "#f0935f",
      "bjork-shadow-surface": "inset 0 7px 14px rgba(88, 72, 49, 0.045), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.92), inset 1px 0 0 rgba(88, 72, 49, 0.026), inset -1px 0 0 rgba(255, 255, 255, 0.68), 0 14px 22px -9px rgba(66, 52, 33, 0.11)",
      "bjork-shadow-soft": "inset 0 1px 0 rgba(88, 72, 49, 0.045), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.86)",
      "bjork-shadow-panel": "inset 0 1px 0 rgba(88, 72, 49, 0.04), inset 1px 0 0 rgba(88, 72, 49, 0.024), inset -1px 0 0 rgba(255, 255, 255, 0.64), 0 18px 38px -30px rgba(66, 52, 33, 0.2)",
      "bjork-shadow-menu": "inset 0 1px 0 rgba(88, 72, 49, 0.04), inset 0 12px 24px rgba(88, 72, 49, 0.022), inset 1px 0 0 rgba(88, 72, 49, 0.024), inset -1px 0 0 rgba(255, 255, 255, 0.62), 0 22px 44px -26px rgba(66, 52, 33, 0.22)",
      "bjork-shadow-inset": "inset 0 1px 10px rgba(88, 72, 49, 0.12), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.72)",
      "bjork-ring-offset": "#f7f3ea"
    },
    "dark": {
      "bjork-bg": "#111111",
      "bjork-surface": "#121212",
      "bjork-surface-muted": "rgba(22, 22, 22, 0.92)",
      "bjork-surface-hover": "#161616",
      "bjork-surface-active": "#202020",
      "bjork-panel": "#0d0d0d",
      "bjork-menu": "rgba(18, 18, 18, 0.98)",
      "bjork-field": "rgba(18, 18, 18, 0.95)",
      "bjork-field-muted": "rgba(18, 18, 18, 0.55)",
      "bjork-field-inset": "rgba(9, 9, 9, 0.75)",
      "bjork-error-bg": "#130d0a",
      "bjork-text": "#ededed",
      "bjork-text-strong": "rgba(237, 237, 237, 0.9)",
      "bjork-text-medium": "rgba(237, 237, 237, 0.72)",
      "bjork-text-muted": "rgba(237, 237, 237, 0.6)",
      "bjork-text-soft": "rgba(237, 237, 237, 0.5)",
      "bjork-text-faint": "rgba(237, 237, 237, 0.22)",
      "bjork-inverted-text": "#080808",
      "bjork-border": "#232323",
      "bjork-border-muted": "#1c1c1c",
      "bjork-border-strong": "#343434",
      "bjork-thumb": "#626262",
      "bjork-track": "#090909",
      "bjork-accent": "#ec5c13",
      "bjork-accent-hover": "#f07832",
      "bjork-accent-soft": "rgba(236, 92, 19, 0.12)",
      "bjork-accent-muted": "rgba(236, 92, 19, 0.24)",
      "bjork-accent-badge": "#b84a12",
      "bjork-accent-badge-foreground": "#fff2ea",
      "bjork-accent-foreground": "#fff2ea",
      "bjork-accent-fill": "#b84a12",
      "bjork-accent-fill-hover": "#a84a14",
      "bjork-shadow-surface": "inset 0 7px 14px rgba(255, 255, 255, 0.03), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.06), 0 14px 20px -6px rgba(0, 0, 0, 0.45)",
      "bjork-shadow-soft": "inset 0 1px 0 rgba(255, 255, 255, 0.045)",
      "bjork-shadow-panel": "inset 0 1px 0 rgba(255, 255, 255, 0.035), 0 18px 36px -28px rgba(0, 0, 0, 0.9)",
      "bjork-shadow-menu": "inset 0 1px 0 rgba(255, 255, 255, 0.055), inset 0 12px 24px rgba(255, 255, 255, 0.018), inset 0 -18px 26px rgba(0, 0, 0, 0.24), 0 22px 42px -22px rgba(0, 0, 0, 0.9)",
      "bjork-shadow-inset": "inset 0 1px 10px rgba(0, 0, 0, 0.45)",
      "bjork-ring-offset": "#080808"
    }
  },
  "css": {
    ".bjork-range": {
      "box-shadow": "inset 0 1px 1px rgba(255, 255, 255, 0.04), 0 0 14px rgba(189, 69, 20, 0.06)"
    },
    ".bjork-range::-webkit-slider-thumb": {
      "appearance": "none",
      "width": "16px",
      "height": "8px",
      "border": "0",
      "border-radius": "999px",
      "background": "var(--bjork-thumb)",
      "box-shadow": "inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.42)"
    },
    ".bjork-range::-moz-range-thumb": {
      "width": "16px",
      "height": "8px",
      "border": "0",
      "border-radius": "999px",
      "background": "var(--bjork-thumb)",
      "box-shadow": "inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.42)"
    },
    ".bjork-layered-button-accent, .bjork-layered-button-raised": {
      "position": "relative",
      "isolation": "isolate",
      "appearance": "none",
      "overflow": "hidden",
      "border": "0",
      "border-radius": "13px",
      "box-shadow": "inset 0 1px 0 #ffffff38, 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a",
      "transition": "filter 150ms ease-out, box-shadow 150ms ease-out"
    },
    ".bjork-layered-button-accent::after, .bjork-layered-button-raised::after": {
      "position": "absolute",
      "inset": "0",
      "pointer-events": "none",
      "content": "\"\"",
      "border": "1.5px solid transparent",
      "border-radius": "inherit",
      "background": "linear-gradient(#ffffffb8, #0000003d 41% 75%, #ffffff47) border-box",
      "mix-blend-mode": "overlay",
      "-webkit-mask-image": "linear-gradient(#000, #000), linear-gradient(#000, #000)",
      "mask-image": "linear-gradient(#000, #000), linear-gradient(#000, #000)",
      "-webkit-mask-clip": "padding-box, border-box",
      "mask-clip": "padding-box, border-box",
      "-webkit-mask-origin": "padding-box, border-box",
      "mask-origin": "padding-box, border-box",
      "-webkit-mask-composite": "xor",
      "mask-composite": "exclude"
    },
    ".bjork-layered-button-accent": {
      "color": "oklch(100% 0 0)",
      "text-shadow": "0 -1px #00000040",
      "background": "radial-gradient(ellipse at -20px top, rgba(255, 255, 255, 0.14), transparent), linear-gradient(180deg, #ec5c13 0%, #ec5c13 62%, color-mix(in oklab, #ec5c13 84%, black 16%) 100%)"
    },
    ".bjork-layered-button-raised": {
      "color": "oklch(100% 0 0)",
      "text-shadow": "0 -1px #00000026",
      "background": "radial-gradient(ellipse at -20px top, #ffffff38, #fff0), linear-gradient(180deg, oklch(0.1881 0.006 265), oklch(0.5493 0.0081 265))"
    },
    ".dark .bjork-layered-button-raised": {
      "color": "oklch(0.2574 0.0056 265)",
      "text-shadow": "0 -1px #00000026",
      "background": "radial-gradient(ellipse at -20px top, #ffffff38, #fff0), linear-gradient(180deg, color-mix(in oklab, oklch(0.9674 0.0013 265) 80%, oklch(0.2574 0.0056 265) 20%), color-mix(in oklab, oklch(0.669 0.0107 265) 80%, oklch(0.2574 0.0056 265) 20%))"
    },
    "@media (hover: hover)": {
      ".bjork-layered-button-accent:hover": {
        "filter": "brightness(1.08) saturate(1.04)"
      },
      ".bjork-layered-button-raised:hover": {
        "filter": "brightness(1.08) contrast(0.9)"
      }
    }
  },
  "meta": {
    "collection": "Tables",
    "registryUrl": "https://ui.isaiahbjork.com/shortcuts-table.json",
    "installCommand": "npx shadcn@latest add https://ui.isaiahbjork.com/shortcuts-table.json"
  },
  "type": "registry:component"
}