{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "slash-menu",
  "type": "registry:component",
  "title": "Slash Menu",
  "description": "A writing surface where typing / opens a command menu right at the caret, narrows as you type and drops in a styled block: heading, to-do, callout, code and more.",
  "categories": [
    "component",
    "Conversation"
  ],
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/devl/slash-menu.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport {\n  CheckIcon,\n  CodeIcon,\n  Heading2Icon,\n  LightbulbIcon,\n  ListIcon,\n  type LucideIcon,\n  MinusIcon,\n  QuoteIcon,\n  SquareCheckIcon,\n  TypeIcon,\n} from \"lucide-react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { type ChangeEvent, type KeyboardEvent, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nfunction useReducedMotionAfterMount() {\n  const prefers = useReducedMotion();\n  const [mounted, setMounted] = useState(false);\n  useEffect(() => setMounted(true), []);\n  return mounted && !!prefers;\n}\n\nexport type BlockType = \"text\" | \"heading\" | \"todo\" | \"bullet\" | \"quote\" | \"callout\" | \"code\" | \"divider\";\n\nexport interface SlashBlock {\n  id: string;\n  type: BlockType;\n  text: string;\n  checked?: boolean;\n}\n\nexport interface SlashCommand {\n  type: BlockType;\n  label: string;\n  hint: string;\n  icon: LucideIcon;\n  group: string;\n  aliases?: string[];\n}\n\nexport interface SlashMenuProps {\n  title?: string;\n  defaultBlocks?: SlashBlock[];\n  commands?: SlashCommand[];\n  onChange?: (blocks: SlashBlock[]) => void;\n  className?: string;\n}\n\nexport const defaultCommands: SlashCommand[] = [\n  { type: \"text\", label: \"Text\", hint: \"Plain paragraph\", icon: TypeIcon, group: \"Basic\", aliases: [\"paragraph\", \"p\"] },\n  { type: \"heading\", label: \"Heading\", hint: \"Section title\", icon: Heading2Icon, group: \"Basic\", aliases: [\"h2\", \"title\"] },\n  { type: \"todo\", label: \"To-do\", hint: \"Track a task\", icon: SquareCheckIcon, group: \"Basic\", aliases: [\"task\", \"checkbox\", \"check\"] },\n  { type: \"bullet\", label: \"Bulleted list\", hint: \"Simple list\", icon: ListIcon, group: \"Basic\", aliases: [\"ul\", \"list\"] },\n  { type: \"quote\", label: \"Quote\", hint: \"Pull out a line\", icon: QuoteIcon, group: \"Blocks\", aliases: [\"blockquote\"] },\n  { type: \"callout\", label: \"Callout\", hint: \"Make it stand out\", icon: LightbulbIcon, group: \"Blocks\", aliases: [\"note\", \"tip\", \"info\"] },\n  { type: \"code\", label: \"Code\", hint: \"Monospace snippet\", icon: CodeIcon, group: \"Blocks\", aliases: [\"snippet\", \"pre\"] },\n  { type: \"divider\", label: \"Divider\", hint: \"Separate sections\", icon: MinusIcon, group: \"Blocks\", aliases: [\"hr\", \"line\", \"rule\"] },\n];\n\nconst placeholders: Record<BlockType, string> = {\n  text: \"Type / for blocks\",\n  heading: \"Heading\",\n  todo: \"To-do\",\n  bullet: \"List item\",\n  quote: \"Quote\",\n  callout: \"Something worth noticing\",\n  code: \"// code\",\n  divider: \"\",\n};\n\nlet counter = 0;\nconst newId = () => `b${Date.now().toString(36)}${counter++}`;\n\nconst MENU_HEIGHT = 300;\n\nfunction caretPoint(el: HTMLTextAreaElement, index: number) {\n  const style = getComputedStyle(el);\n  const mirror = document.createElement(\"div\");\n  for (const prop of [\n    \"boxSizing\",\n    \"width\",\n    \"paddingTop\",\n    \"paddingRight\",\n    \"paddingBottom\",\n    \"paddingLeft\",\n    \"borderTopWidth\",\n    \"borderLeftWidth\",\n    \"fontFamily\",\n    \"fontSize\",\n    \"fontWeight\",\n    \"fontStyle\",\n    \"letterSpacing\",\n    \"lineHeight\",\n    \"textTransform\",\n    \"wordSpacing\",\n    \"tabSize\",\n  ] as const) {\n    mirror.style[prop] = style[prop];\n  }\n  mirror.style.position = \"absolute\";\n  mirror.style.visibility = \"hidden\";\n  mirror.style.whiteSpace = \"pre-wrap\";\n  mirror.style.overflowWrap = \"break-word\";\n  mirror.textContent = el.value.slice(0, index);\n  const marker = document.createElement(\"span\");\n  marker.textContent = \"​\";\n  mirror.appendChild(marker);\n  document.body.appendChild(mirror);\n  const point = {\n    left: marker.offsetLeft,\n    top: marker.offsetTop,\n    height: Number.parseFloat(style.lineHeight) || marker.offsetHeight,\n  };\n  mirror.remove();\n  return point;\n}\n\nfunction rank(cmd: SlashCommand, q: string) {\n  if (!q) return 1;\n  const label = cmd.label.toLowerCase();\n  if (label.startsWith(q)) return 3;\n  if (cmd.aliases?.some((a) => a.startsWith(q))) return 2;\n  if (label.includes(q)) return 1;\n  return 0;\n}\n\nfunction Highlight({ text, query }: { text: string; query: string }) {\n  const i = query ? text.toLowerCase().indexOf(query) : -1;\n  if (i < 0) return <>{text}</>;\n  return (\n    <>\n      {text.slice(0, i)}\n      <span className=\"text-foreground underline decoration-foreground/30 underline-offset-2\">{text.slice(i, i + query.length)}</span>\n      {text.slice(i + query.length)}\n    </>\n  );\n}\n\ninterface MenuState {\n  blockId: string;\n  slash: number;\n  query: string;\n  x: number;\n  y: number;\n  above: boolean;\n}\n\nexport function SlashMenu({\n  title = \"Offsite, Lisbon\",\n  defaultBlocks,\n  commands = defaultCommands,\n  onChange,\n  className,\n}: SlashMenuProps) {\n  const reduce = useReducedMotionAfterMount();\n  const listId = useId();\n  const [blocks, setBlocks] = useState<SlashBlock[]>(\n    () => defaultBlocks ?? [{ id: newId(), type: \"text\", text: \"\" }],\n  );\n  const [menu, setMenu] = useState<MenuState | null>(null);\n  const [active, setActive] = useState(0);\n  const [focusedId, setFocusedId] = useState<string | null>(null);\n  const [flashId, setFlashId] = useState<string | null>(null);\n  const refs = useRef(new Map<string, HTMLTextAreaElement>());\n  const wrapRef = useRef<HTMLDivElement>(null);\n  const pendingFocus = useRef<{ id: string; at: number | \"end\" } | null>(null);\n  const flashTimer = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n  useEffect(() => () => clearTimeout(flashTimer.current), []);\n  useEffect(() => onChange?.(blocks), [blocks, onChange]);\n\n  useLayoutEffect(() => {\n    const req = pendingFocus.current;\n    if (!req) return;\n    const el = refs.current.get(req.id);\n    if (!el) return;\n    pendingFocus.current = null;\n    el.focus();\n    const at = req.at === \"end\" ? el.value.length : req.at;\n    el.setSelectionRange(at, at);\n  });\n\n  const results = useMemo(() => {\n    if (!menu) return [];\n    const q = menu.query.toLowerCase();\n    return commands\n      .map((c, i) => ({ c, score: rank(c, q), i }))\n      .filter((r) => r.score > 0)\n      .sort((a, b) => b.score - a.score || a.i - b.i)\n      .map((r) => r.c);\n  }, [menu, commands]);\n\n  const groups = useMemo(() => {\n    if (menu?.query) return [{ name: \"Best match\", items: results }];\n    const out: { name: string; items: SlashCommand[] }[] = [];\n    for (const c of results) {\n      const g = out.find((x) => x.name === c.group);\n      if (g) g.items.push(c);\n      else out.push({ name: c.group, items: [c] });\n    }\n    return out;\n  }, [results, menu?.query]);\n\n  const activeOptionId = menu && results[active] ? `${listId}-${results[active].type}` : undefined;\n  useEffect(() => {\n    if (activeOptionId) document.getElementById(activeOptionId)?.scrollIntoView({ block: \"nearest\" });\n  }, [activeOptionId]);\n\n  const update = (id: string, patch: Partial<SlashBlock>) =>\n    setBlocks((bs) => bs.map((b) => (b.id === id ? { ...b, ...patch } : b)));\n\n  const flash = (id: string) => {\n    setFlashId(id);\n    clearTimeout(flashTimer.current);\n    flashTimer.current = setTimeout(() => setFlashId(null), 700);\n  };\n\n  const detectSlash = (block: SlashBlock, el: HTMLTextAreaElement) => {\n    const caret = el.selectionStart;\n    const before = el.value.slice(0, caret);\n    const m = /(^|\\s)\\/([\\p{L}\\d-]{0,24})$/u.exec(before);\n    if (!m || block.type === \"code\") {\n      setMenu(null);\n      return;\n    }\n    const slash = caret - m[2].length - 1;\n    const wrap = wrapRef.current;\n    if (!wrap) return;\n    const p = caretPoint(el, slash);\n    const elRect = el.getBoundingClientRect();\n    const wrapRect = wrap.getBoundingClientRect();\n    const x = Math.min(elRect.left - wrapRect.left + p.left - 8, wrap.clientWidth - 248);\n    const top = elRect.top - wrapRect.top + p.top - el.scrollTop;\n    const above = top + p.height + MENU_HEIGHT > wrap.clientHeight + 120 && top > MENU_HEIGHT;\n    setMenu((prev) => {\n      if (prev?.query !== m[2]) setActive(0);\n      return { blockId: block.id, slash, query: m[2], x: Math.max(0, x), y: above ? top - 6 : top + p.height + 6, above };\n    });\n  };\n\n  const onInput = (block: SlashBlock, e: ChangeEvent<HTMLTextAreaElement>) => {\n    update(block.id, { text: e.target.value });\n    detectSlash(block, e.target);\n  };\n\n  const apply = (cmd: SlashCommand) => {\n    if (!menu) return;\n    const block = blocks.find((b) => b.id === menu.blockId);\n    const el = refs.current.get(menu.blockId);\n    if (!block || !el) return;\n    const caret = el.selectionStart;\n    const rest = (block.text.slice(0, menu.slash) + block.text.slice(caret)).replace(/\\s+$/, \"\");\n    const index = blocks.findIndex((b) => b.id === block.id);\n    setMenu(null);\n\n    setBlocks((bs) => {\n      const next = [...bs];\n      if (cmd.type === \"divider\") {\n        const after = { id: newId(), type: \"text\" as const, text: \"\" };\n        const divider = { id: newId(), type: \"divider\" as const, text: \"\" };\n        if (rest.trim()) next.splice(index, 1, { ...block, text: rest }, divider, after);\n        else next.splice(index, 1, divider, after);\n        pendingFocus.current = { id: after.id, at: 0 };\n        flash(divider.id);\n        return next;\n      }\n      if (!rest.trim()) {\n        next[index] = { ...block, type: cmd.type, text: \"\", checked: false };\n        pendingFocus.current = { id: block.id, at: 0 };\n        flash(block.id);\n      } else {\n        const created = { id: newId(), type: cmd.type, text: \"\" };\n        next.splice(index, 1, { ...block, text: rest }, created);\n        pendingFocus.current = { id: created.id, at: 0 };\n        flash(created.id);\n      }\n      return next;\n    });\n  };\n\n  const focusBlock = (i: number, at: number | \"end\") => {\n    const target = blocks.slice(0, i + 1).reverse().find((b) => b.type !== \"divider\");\n    const forward = blocks.slice(i).find((b) => b.type !== \"divider\");\n    const pick = at === \"end\" ? target : forward;\n    if (!pick) return;\n    pendingFocus.current = { id: pick.id, at };\n    setFocusedId(pick.id);\n  };\n\n  const onKeyDown = (block: SlashBlock, e: KeyboardEvent<HTMLTextAreaElement>) => {\n    const el = e.currentTarget;\n    const index = blocks.findIndex((b) => b.id === block.id);\n\n    if (menu && menu.blockId === block.id) {\n      if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n        e.preventDefault();\n        if (!results.length) return;\n        setActive((a) => (a + (e.key === \"ArrowDown\" ? 1 : -1) + results.length) % results.length);\n        return;\n      }\n      if ((e.key === \"Enter\" || e.key === \"Tab\") && results.length) {\n        e.preventDefault();\n        apply(results[active] ?? results[0]);\n        return;\n      }\n      if (e.key === \"Escape\") {\n        e.preventDefault();\n        setMenu(null);\n        return;\n      }\n    }\n\n    const atStart = el.selectionStart === 0 && el.selectionEnd === 0;\n    const atEnd = el.selectionStart === el.value.length;\n\n    if (e.key === \"Enter\" && !e.shiftKey && block.type !== \"code\") {\n      e.preventDefault();\n      const listy = block.type === \"todo\" || block.type === \"bullet\";\n      if (listy && !block.text.trim()) {\n        update(block.id, { type: \"text\" });\n        return;\n      }\n      const head = el.value.slice(0, el.selectionStart);\n      const tail = el.value.slice(el.selectionEnd);\n      const created: SlashBlock = { id: newId(), type: listy ? block.type : \"text\", text: tail };\n      setBlocks((bs) => {\n        const next = [...bs];\n        next.splice(index, 1, { ...block, text: head }, created);\n        return next;\n      });\n      pendingFocus.current = { id: created.id, at: 0 };\n      return;\n    }\n\n    if (e.key === \"Backspace\" && atStart) {\n      if (block.type !== \"text\") {\n        e.preventDefault();\n        update(block.id, { type: \"text\" });\n        return;\n      }\n      if (index > 0) {\n        e.preventDefault();\n        const prev = blocks[index - 1];\n        if (prev.type === \"divider\") {\n          setBlocks((bs) => bs.filter((b) => b.id !== prev.id));\n          pendingFocus.current = { id: block.id, at: 0 };\n          return;\n        }\n        const join = prev.text.length;\n        setBlocks((bs) =>\n          bs.filter((b) => b.id !== block.id).map((b) => (b.id === prev.id ? { ...b, text: b.text + block.text } : b)),\n        );\n        pendingFocus.current = { id: prev.id, at: join };\n      }\n      return;\n    }\n\n    if (e.key === \"ArrowUp\" && atStart && index > 0) {\n      e.preventDefault();\n      focusBlock(index - 1, \"end\");\n    } else if (e.key === \"ArrowDown\" && atEnd && index < blocks.length - 1) {\n      e.preventDefault();\n      focusBlock(index + 1, 0);\n    }\n  };\n\n  const lastEmpty = blocks[blocks.length - 1];\n\n  return (\n    <section\n      aria-label={title}\n      className={cn(\"relative w-full max-w-[460px] rounded-2xl border bg-card text-card-foreground shadow-xs/5\", className)}\n    >\n      <header className=\"flex items-baseline justify-between gap-3 px-5 pt-5\">\n        <h3 className=\"text-balance font-semibold text-lg tracking-tight\">{title}</h3>\n        <span className=\"shrink-0 text-muted-foreground text-xs tabular-nums\">\n          {blocks.length} blocks\n        </span>\n      </header>\n\n      <div className=\"relative min-h-64 px-5 pt-3 pb-5\" ref={wrapRef}>\n        <motion.div className=\"flex flex-col gap-1\" layout=\"position\">\n          <AnimatePresence initial={false}>\n            {blocks.map((block, i) => (\n              <motion.div\n                animate={{ opacity: 1, height: \"auto\" }}\n                exit={{ opacity: 0, height: 0, transition: { duration: 0.12 } }}\n                initial={reduce ? false : { opacity: 0, height: 0 }}\n                key={block.id}\n                transition={{ type: \"spring\", stiffness: 520, damping: 40, opacity: { duration: 0.14 } }}\n              >\n                <Block\n                  block={block}\n                  flash={flashId === block.id}\n                  focused={focusedId === block.id}\n                  menuOpen={menu?.blockId === block.id}\n                  menuProps={{\n                    listId,\n                    activeId: menu?.blockId === block.id && results[active] ? `${listId}-${results[active].type}` : undefined,\n                  }}\n                  onBlur={() => {\n                    setFocusedId((f) => (f === block.id ? null : f));\n                    setMenu((m) => (m?.blockId === block.id ? null : m));\n                  }}\n                  onChange={(e) => onInput(block, e)}\n                  onFocus={() => setFocusedId(block.id)}\n                  onKeyDown={(e) => onKeyDown(block, e)}\n                  onSelect={(e) => menu?.blockId === block.id && detectSlash(block, e.currentTarget)}\n                  onToggle={() => update(block.id, { checked: !block.checked })}\n                  placeholderAlways={block === lastEmpty && block.type === \"text\" && !block.text && i > 0}\n                  register={(el) => {\n                    if (el) refs.current.set(block.id, el);\n                    else refs.current.delete(block.id);\n                  }}\n                />\n              </motion.div>\n            ))}\n          </AnimatePresence>\n        </motion.div>\n\n        <AnimatePresence>\n          {menu && (\n            <motion.div\n              animate={{ opacity: 1, scale: 1, y: 0 }}\n              className=\"absolute z-20 w-60 overflow-hidden rounded-xl border bg-popover text-popover-foreground shadow-lg/5\"\n              exit={{ opacity: 0, transition: { duration: 0.1 } }}\n              initial={reduce ? { opacity: 0 } : { opacity: 0, scale: 0.97, y: menu.above ? 4 : -4 }}\n              key=\"menu\"\n              onMouseDown={(e) => e.preventDefault()}\n              style={{\n                left: menu.x,\n                top: menu.above ? undefined : menu.y,\n                bottom: menu.above ? `calc(100% - ${menu.y}px)` : undefined,\n                transformOrigin: menu.above ? \"8px 100%\" : \"8px 0\",\n              }}\n              transition={{ duration: 0.14, ease: [0.23, 1, 0.32, 1] }}\n            >\n              <div aria-label=\"Insert a block\" className=\"max-h-[300px] overflow-y-auto p-1\" id={listId} role=\"listbox\">\n                {results.length === 0 ? (\n                  <div className=\"px-3 py-6 text-center\">\n                    <p className=\"text-sm\">No blocks for “{menu.query}”</p>\n                    <p className=\"mt-0.5 text-muted-foreground text-xs\">Keep typing, or press Esc</p>\n                  </div>\n                ) : (\n                  groups.map((g) => (\n                    <div key={g.name} role=\"presentation\">\n                      <div className=\"px-2 pt-1.5 pb-1 font-medium text-muted-foreground text-xs\">{g.name}</div>\n                      {g.items.map((c) => {\n                        const idx = results.indexOf(c);\n                        const on = idx === active;\n                        const Icon = c.icon;\n                        return (\n                          <div\n                            aria-selected={on}\n                            className={cn(\n                              \"relative flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5\",\n                              on ? \"bg-accent\" : \"\",\n                            )}\n                            id={`${listId}-${c.type}`}\n                            key={c.type}\n                            onClick={() => apply(c)}\n                            onMouseMove={() => idx !== active && setActive(idx)}\n                            role=\"option\"\n                            tabIndex={-1}\n                          >\n                            <span\n                              className={cn(\n                                \"flex size-8 shrink-0 items-center justify-center rounded-lg border bg-background text-muted-foreground shadow-xs/5 transition-colors dark:bg-input/32\",\n                                on && \"text-foreground\",\n                              )}\n                            >\n                              <Icon className=\"size-4\" />\n                            </span>\n                            <span className=\"min-w-0 flex-1\">\n                              <span className=\"block truncate text-sm text-foreground/90\">\n                                <Highlight query={menu.query.toLowerCase()} text={c.label} />\n                              </span>\n                              <span className=\"block truncate text-muted-foreground text-xs\">{c.hint}</span>\n                            </span>\n                          </div>\n                        );\n                      })}\n                    </div>\n                  ))\n                )}\n              </div>\n              <div className=\"flex items-center gap-3 border-t px-3 py-1.5 text-muted-foreground text-xs\">\n                <span>\n                  <Key>↑</Key>\n                  <Key>↓</Key> move\n                </span>\n                <span>\n                  <Key>↵</Key> insert\n                </span>\n                <span className=\"ms-auto\">\n                  <Key>esc</Key>\n                </span>\n              </div>\n            </motion.div>\n          )}\n        </AnimatePresence>\n      </div>\n    </section>\n  );\n}\n\nfunction Key({ children }: { children: React.ReactNode }) {\n  return (\n    <kbd className=\"me-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded border bg-background px-1 font-sans text-[10px] text-muted-foreground dark:bg-input/32\">\n      {children}\n    </kbd>\n  );\n}\n\ninterface BlockProps {\n  block: SlashBlock;\n  focused: boolean;\n  flash: boolean;\n  menuOpen: boolean;\n  menuProps: { listId: string; activeId?: string };\n  placeholderAlways: boolean;\n  register: (el: HTMLTextAreaElement | null) => void;\n  onChange: (e: ChangeEvent<HTMLTextAreaElement>) => void;\n  onKeyDown: (e: KeyboardEvent<HTMLTextAreaElement>) => void;\n  onSelect: (e: React.SyntheticEvent<HTMLTextAreaElement>) => void;\n  onFocus: () => void;\n  onBlur: () => void;\n  onToggle: () => void;\n}\n\nfunction Block({\n  block,\n  focused,\n  flash,\n  menuOpen,\n  menuProps,\n  placeholderAlways,\n  register,\n  onChange,\n  onKeyDown,\n  onSelect,\n  onFocus,\n  onBlur,\n  onToggle,\n}: BlockProps) {\n  const ref = useRef<HTMLTextAreaElement | null>(null);\n\n  useLayoutEffect(() => {\n    const el = ref.current;\n    if (!el) return;\n    el.style.height = \"0px\";\n    el.style.height = `${el.scrollHeight}px`;\n  }, [block.text, block.type]);\n\n  const flashClass = cn(\n    \"rounded-lg transition-[background-color,box-shadow] duration-500 ease-out\",\n    flash && \"bg-info/10 duration-0\",\n  );\n\n  if (block.type === \"divider\") {\n    return (\n      <div className={cn(\"py-3\", flashClass)} role=\"separator\">\n        <div className=\"h-px bg-border\" />\n      </div>\n    );\n  }\n\n  const showPlaceholder = !block.text && (focused || placeholderAlways || block.type !== \"text\");\n  const area = (\n    <textarea\n      aria-activedescendant={menuOpen ? menuProps.activeId : undefined}\n      aria-autocomplete=\"list\"\n      aria-controls={menuOpen ? menuProps.listId : undefined}\n      aria-label={`${block.type === \"text\" ? \"Paragraph\" : block.type} block`}\n      className={cn(\n        \"block w-full resize-none overflow-hidden bg-transparent outline-none placeholder:text-muted-foreground/60\",\n        block.type === \"heading\" && \"font-semibold text-base tracking-tight\",\n        block.type === \"code\" ? \"font-mono text-xs leading-relaxed\" : \"text-sm leading-6\",\n        block.type === \"todo\" && block.checked && \"text-muted-foreground line-through decoration-muted-foreground/50\",\n        block.type === \"quote\" && \"text-foreground/80 italic\",\n      )}\n      onBlur={onBlur}\n      onChange={onChange}\n      onFocus={onFocus}\n      onKeyDown={onKeyDown}\n      onSelect={onSelect}\n      placeholder={showPlaceholder ? placeholders[block.type] : undefined}\n      ref={(el) => {\n        ref.current = el;\n        register(el);\n      }}\n      rows={1}\n      spellCheck={block.type !== \"code\"}\n      value={block.text}\n    />\n  );\n\n  // One stable tree for every type, so the textarea keeps focus when a block changes type.\n  const t = block.type;\n  const leading =\n    t === \"todo\" ? (\n      <button\n        aria-checked={!!block.checked}\n        aria-label=\"Done\"\n        className={cn(\n          \"relative mt-[3px] flex size-[18px] shrink-0 cursor-pointer items-center justify-center rounded-[calc(var(--radius)*0.5)] border outline-none transition-[background-color,border-color,scale] duration-150 before:absolute before:-inset-2 focus-visible:ring-2 focus-visible:ring-ring active:scale-[0.92]\",\n          block.checked ? \"border-primary bg-primary text-primary-foreground\" : \"border-foreground/20 bg-background dark:bg-input/32\",\n        )}\n        onClick={onToggle}\n        role=\"checkbox\"\n        type=\"button\"\n      >\n        {block.checked && <CheckIcon className=\"size-3\" strokeWidth={3} />}\n      </button>\n    ) : t === \"bullet\" ? (\n      <span aria-hidden className=\"ms-1.5 me-0.5 mt-[10px] size-[5px] shrink-0 rounded-full bg-foreground/70\" />\n    ) : t === \"callout\" ? (\n      <LightbulbIcon aria-hidden className=\"mt-[3px] size-4 shrink-0 text-warning-foreground\" />\n    ) : null;\n\n  return (\n    <div\n      className={cn(\n        \"flex items-start gap-2.5\",\n        flashClass,\n        t === \"heading\" && \"pt-3 pb-0.5\",\n        t === \"quote\" && \"my-1 rounded-s-none border-foreground/20 border-s-2 ps-3\",\n        t === \"callout\" && cn(\"my-1 rounded-xl bg-warning/10 px-3 py-2.5\", flash && \"bg-warning/20\"),\n        t === \"code\" && cn(\"my-1 rounded-xl border bg-muted/60 px-3 py-2.5\", flash && \"bg-info/8\"),\n      )}\n    >\n      {leading}\n      {area}\n    </div>\n  );\n}\n"
    }
  ]
}