{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "shortcuts-dialog",
  "title": "Shortcuts Dialog",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-svelte/blocks/shortcuts-dialog/ShortcutsDialog.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n\n  export interface Shortcut {\n    keys: string[]\n    description: string\n  }\n\n  export interface ShortcutGroup {\n    label: string\n    shortcuts: Shortcut[]\n  }\n\n  export interface ShortcutsDialogProps {\n    groups?: ShortcutGroup[]\n    /** Render the built-in trigger button. */\n    trigger?: boolean\n    /**\n     * Custom trigger content. Receives the trigger `props` (open handler,\n     * a11y attributes) — spread them onto your element. Used only when\n     * `trigger` is false.\n     */\n    triggerSnippet?: Snippet<[{ props: Record<string, unknown> }]>\n    /** Show the 'Press Esc to close' footer hint. */\n    footer?: boolean\n    /** Controlled open state. Omit for uncontrolled (internal state). */\n    open?: boolean\n    onOpenChange?: (open: boolean) => void\n    class?: string\n  }\n</script>\n\n<script lang=\"ts\">\n  import { Keyboard } from '@lucide/svelte'\n  import { cn } from '$lib/utils'\n  import { Button } from '$lib/components/ui/button'\n  import {\n    Dialog,\n    DialogContent,\n    DialogDescription,\n    DialogFooter,\n    DialogHeader,\n    DialogTitle,\n    DialogTrigger,\n  } from '$lib/components/ui/dialog'\n  import { Kbd } from '$lib/components/ui/kbd'\n\n  let {\n    groups = [\n      {\n        label: 'Navigation',\n        shortcuts: [\n          { keys: ['G', 'I'], description: 'Go to inbox' },\n          { keys: ['G', 'D'], description: 'Go to drafts' },\n          { keys: ['⌘', 'K'], description: 'Open command menu' },\n          { keys: ['⌥', '↑', '↓'], description: 'Move between items' },\n        ],\n      },\n      {\n        label: 'Actions',\n        shortcuts: [\n          { keys: ['C'], description: 'Compose new' },\n          { keys: ['/'], description: 'Focus search' },\n          { keys: ['⌘', 'S'], description: 'Save changes' },\n          { keys: ['E'], description: 'Archive selection' },\n        ],\n      },\n      {\n        label: 'General',\n        shortcuts: [\n          { keys: ['?'], description: 'Show this dialog' },\n          { keys: ['⇧', '?'], description: 'Show cheat sheet' },\n          { keys: ['Esc'], description: 'Close dialogs' },\n        ],\n      },\n    ],\n    trigger = true,\n    triggerSnippet,\n    footer = true,\n    open = $bindable(undefined),\n    onOpenChange,\n    class: className,\n  }: ShortcutsDialogProps = $props()\n\n  // Local state drives the uncontrolled case; the `open` prop takes precedence.\n  let internalOpen = $state(false)\n  const isOpen = $derived(open ?? internalOpen)\n\n  function handleOpenChange(value: boolean) {\n    internalOpen = value\n    open = value\n    onOpenChange?.(value)\n  }\n</script>\n\n<!-- Dialog renders only its children, so the slot marker lives on a layout-free wrapper. -->\n<div data-slot=\"shortcuts-dialog\" class=\"contents\">\n<Dialog open={isOpen} onOpenChange={handleOpenChange}>\n  {#if trigger}\n    <DialogTrigger>\n      {#snippet child({ props })}\n        <Button variant=\"outline\" size=\"sm\" {...props}>\n          <Keyboard aria-hidden=\"true\" />\n          Shortcuts\n          <Kbd>?</Kbd>\n        </Button>\n      {/snippet}\n    </DialogTrigger>\n  {:else if triggerSnippet}\n    <DialogTrigger>\n      {#snippet child({ props })}\n        {@render triggerSnippet({ props })}\n      {/snippet}\n    </DialogTrigger>\n  {/if}\n  <DialogContent class={cn('max-w-lg', className)}>\n    <DialogHeader>\n      <DialogTitle>Keyboard shortcuts</DialogTitle>\n      <DialogDescription>Move faster without leaving the keyboard.</DialogDescription>\n    </DialogHeader>\n    <div class=\"grid max-h-[60vh] gap-x-8 gap-y-6 overflow-y-auto pr-1 sm:grid-cols-2\">\n      {#each groups as group (group.label)}\n        <div class=\"space-y-2\">\n          <p class=\"text-muted-foreground text-xs font-medium tracking-widest uppercase\">{group.label}</p>\n          <ul class=\"divide-y\">\n            {#each group.shortcuts as shortcut (shortcut.description)}\n              <li class=\"flex items-center justify-between gap-4 py-1.5\">\n                <span class=\"text-sm\">{shortcut.description}</span>\n                <span class=\"flex shrink-0 items-center gap-1\">\n                  {#each shortcut.keys as key (key)}\n                    <Kbd>{key}</Kbd>\n                  {/each}\n                </span>\n              </li>\n            {/each}\n          </ul>\n        </div>\n      {/each}\n    </div>\n    {#if footer}\n      <DialogFooter class=\"justify-start\">\n        <p class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">Press <Kbd>Esc</Kbd> to close</p>\n      </DialogFooter>\n    {/if}\n  </DialogContent>\n</Dialog>\n</div>\n",
      "type": "registry:block",
      "target": "~/src/lib/components/blocks/shortcuts-dialog/ShortcutsDialog.svelte"
    }
  ],
  "dependencies": [
    "@lucide/svelte"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/svelte/button.json",
    "https://uipkge.dev/r/svelte/dialog.json",
    "https://uipkge.dev/r/svelte/kbd.json"
  ],
  "description": "Linear-style keyboard reference sheet: a '?' dialog listing shortcut groups (label, description, Kbd combo) in a responsive two-column grid. Ships a built-in trigger button, controlled/uncontrolled open, custom groups via prop, and an Esc footer hint. Display only — wire real handlers in your app.",
  "categories": [
    "layout",
    "dashboard",
    "navigation"
  ]
}