{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"prompt-suggestions","type":"registry:ui","title":"Prompt Suggestions","description":"Things to ask in an empty AI conversation: chips, one scrolling row, or cards with an icon and a line.","dependencies":["lucide-react"],"registryDependencies":["https://dashral.com/r/utils.json","https://dashral.com/r/lib-locale.json","https://dashral.com/r/scroll-shadow.json"],"files":[{"path":"registry/dashral/prompt-suggestions.tsx","type":"registry:file","target":"components/dashral/prompt-suggestions.tsx","content":"\"use client\"\n\nimport * as React from \"react\"\nimport { ArrowUpRight } from \"lucide-react\"\n\nimport { useLocale } from \"./lib/locale\"\nimport { cn } from \"./lib/utils\"\nimport { ScrollShadow } from \"./scroll-shadow\"\n\ntype PromptSuggestion =\n  | string\n  | {\n      /** What the suggestion says. */\n      label: React.ReactNode\n      /** What it sends, when that differs from the label (\"Unpaid invoices\" sends \"Who hasn't paid yet?\"). */\n      prompt?: string\n      /** A line under the label, in cards. */\n      description?: React.ReactNode\n      icon?: React.ReactNode\n      disabled?: boolean\n    }\n\ntype PromptSuggestionsProps = Omit<React.ComponentProps<\"div\">, \"onSelect\" | \"children\"> & {\n  suggestions: PromptSuggestion[]\n  /** Called with the prompt when one is picked. */\n  onSelect?: (prompt: string) => void\n  /** chips: round buttons in a row; cards: a grid of cards with an icon, a title and a line. */\n  variant?: \"chips\" | \"cards\"\n  /** Chips only: one row that scrolls sideways (its edges fade) instead of wrapping, for narrow screens. */\n  scroll?: boolean\n  /** Names the group for screen readers. \"Suggestions\" by default. */\n  \"aria-label\"?: string\n  classNames?: { item?: string; icon?: string; label?: string; description?: string }\n}\n\nconst normalize = (suggestion: PromptSuggestion) => (typeof suggestion === \"string\" ? { label: suggestion, prompt: suggestion } : suggestion)\n\n/** The text a suggestion sends: its prompt, or its label when that's text. */\nconst promptOf = (item: ReturnType<typeof normalize>) => item.prompt ?? (typeof item.label === \"string\" ? item.label : \"\")\n\n/**\n * Things to ask, for an empty conversation: round chips in a row (or one row that scrolls), or cards with an icon and a\n * line each. Picking one hands its prompt to onSelect, to send or to put in the composer.\n */\nfunction PromptSuggestions({\n  suggestions,\n  onSelect,\n  variant = \"chips\",\n  scroll = false,\n  className,\n  classNames,\n  \"aria-label\": ariaLabel,\n  ...props\n}: PromptSuggestionsProps) {\n  const { t } = useLocale()\n  const items = suggestions.map(normalize)\n\n  const buttons = items.map((item, index) => {\n    const prompt = promptOf(item)\n    const choose = () => onSelect?.(prompt)\n    if (variant === \"cards\") {\n      return (\n        <button\n          key={index}\n          type=\"button\"\n          onClick={choose}\n          disabled={item.disabled}\n          className={cn(\n            \"group/suggestion flex min-w-0 items-start gap-3 rounded-xl border bg-card p-3.5 text-start text-sm text-muted-foreground shadow-xs transition-[border-color,color,background-color] duration-(--duration-fast) outline-none\",\n            \"hover:border-ring/40 hover:bg-accent/40 hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50\",\n            classNames?.item\n          )}\n        >\n          {item.icon && (\n            <span aria-hidden className={cn(\"mt-px grid size-5 shrink-0 place-items-center text-muted-foreground [&_svg]:size-4\", classNames?.icon)}>\n              {item.icon}\n            </span>\n          )}\n          <span className=\"min-w-0 flex-1\">\n            <span className={cn(\"block font-medium text-foreground\", classNames?.label)} dir=\"auto\">\n              {item.label}\n            </span>\n            {item.description && (\n              <span className={cn(\"mt-0.5 block text-xs leading-relaxed\", classNames?.description)} dir=\"auto\">\n                {item.description}\n              </span>\n            )}\n          </span>\n          <ArrowUpRight\n            aria-hidden\n            className=\"size-3.5 shrink-0 opacity-0 transition-[opacity,translate] duration-(--duration-fast) group-hover/suggestion:translate-x-0.5 rtl:group-hover/suggestion:-translate-x-0.5 group-hover/suggestion:-translate-y-0.5 group-hover/suggestion:opacity-100 group-focus-visible/suggestion:opacity-100 rtl:-scale-x-100 motion-reduce:translate-none\"\n          />\n        </button>\n      )\n    }\n    return (\n      <button\n        key={index}\n        type=\"button\"\n        onClick={choose}\n        disabled={item.disabled}\n        className={cn(\n          \"inline-flex h-8 shrink-0 items-center gap-1.5 rounded-full border bg-card px-3 text-sm whitespace-nowrap text-muted-foreground shadow-xs transition-[border-color,color,background-color] duration-(--duration-fast) outline-none\",\n          \"hover:border-ring/40 hover:bg-accent/40 hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-3.5 [&_svg]:shrink-0\",\n          classNames?.item\n        )}\n      >\n        {item.icon && (\n          <span aria-hidden className={cn(\"contents\", classNames?.icon)}>\n            {item.icon}\n          </span>\n        )}\n        <span className={classNames?.label} dir=\"auto\">\n          {item.label}\n        </span>\n      </button>\n    )\n  })\n\n  if (variant === \"chips\" && scroll) {\n    return (\n      <ScrollShadow\n        orientation=\"horizontal\"\n        role=\"group\"\n        aria-label={ariaLabel ?? t(\"suggestions\")}\n        data-slot=\"prompt-suggestions\"\n        className={cn(\"flex gap-2 px-px py-px\", className)}\n        {...props}\n      >\n        {buttons}\n      </ScrollShadow>\n    )\n  }\n\n  return (\n    <div\n      role=\"group\"\n      aria-label={ariaLabel ?? t(\"suggestions\")}\n      data-slot=\"prompt-suggestions\"\n      data-variant={variant}\n      className={cn(variant === \"cards\" ? \"grid gap-2 sm:grid-cols-2\" : \"flex flex-wrap justify-center gap-2\", className)}\n      {...props}\n    >\n      {buttons}\n    </div>\n  )\n}\n\nexport { PromptSuggestions, type PromptSuggestionsProps, type PromptSuggestion }\n"}],"cssVars":{"theme":{"ease-standard":"var(--ease-standard)","ease-enter":"var(--ease-enter)","ease-exit":"var(--ease-exit)","ease-spring":"var(--ease-spring)"},"light":{"duration-fast":"160ms","duration-standard":"240ms","duration-considered":"480ms","ease-standard":"cubic-bezier(0.2, 0, 0, 1)","ease-enter":"cubic-bezier(0, 0, 0, 1)","ease-exit":"cubic-bezier(0.3, 0, 1, 1)","ease-spring":"linear(0, 0.071 4%, 0.234 8%, 0.433 12%, 0.626 17%, 0.791 21%, 0.917 25%, 1.004 29%, 1.055 33%, 1.079 38%, 1.083 42%, 1.075 46%, 1.06 50%, 1.043 54%, 1.027 58%, 1.015 62%, 1.005 67%, 0.998 71%, 0.995 75%, 0.993 79%, 0.993 83%, 0.994 88%, 0.995 92%, 0.997 96%, 1)"},"dark":{}}}