{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"message","title":"Message","description":"Chat message layout with markdown, optional avatar, actions, and attachments","dependencies":["streamdown","@streamdown/cjk","@streamdown/code","@streamdown/math","@streamdown/mermaid","radix-ui","@radix-ui/react-slot","@hugeicons/react","@hugeicons/core-free-icons"],"registryDependencies":["button","avatar","tooltip","kbd"],"files":[{"path":"components/nexus-ui/message.tsx","content":"\"use client\";\n\nimport * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { Streamdown } from \"streamdown\";\nimport { cjk } from \"@streamdown/cjk\";\nimport { code } from \"@streamdown/code\";\nimport { math } from \"@streamdown/math\";\nimport { mermaid } from \"@streamdown/mermaid\";\n\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\nimport { CodeBlock } from \"@/components/nexus-ui/codeblock\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { Kbd } from \"@/components/ui/kbd\";\nimport { cn } from \"@/lib/utils\";\n\nconst streamdownPlugins = { cjk, code, math, mermaid } as const;\n\nconst messageMarkdownProseClasses = [\n  \"prose max-w-none text-primary font-normal text-sm leading-6.5\",\n  // headings\n  \"prose-headings:font-[450] prose-headings:leading-5.5 prose-h2:tracking-[-0.45px] prose-headings:mb-4 prose-headings:mt-6 prose-h1:text-xl prose-h2:text-lg prose-h3:text-base prose-h3:leading-4.5 prose-h3:tracking-[-0.4px] prose-h4:text-sm prose-h5:text-xs prose-h6:text-xs\",\n  // heading links\n  \"prose-headings:[&_a]:no-underline prose-headings:[&_a]:shadow-none prose-headings:[&_a]:text-inherit\",\n  // body text\n  \"prose-p:mb-1 prose-p:mt-4\",\n  // lead\n  \"prose-lead:text-primary\",\n  // links\n  \"[&_[data-streamdown=link]]:text-foreground [&_[data-streamdown=link]]:font-normal [&_[data-streamdown=link]]:underline [&_[data-streamdown=link]]:underline-offset-2\",\n  // strong\n  \"[&_[data-streamdown=strong]]:text-foreground [&_[data-streamdown=strong]]:font-[550]\",\n  // lists\n  \"prose-li:my-[-0.5px] prose-li:marker:text-muted-foreground/50 prose-ul:my-0 prose-ol:my-0 prose-ol:pl-3\",\n] as const;\n\ntype MessageFrom = \"user\" | \"assistant\";\n\ntype MessageContextValue = {\n  from: MessageFrom;\n};\n\nconst MessageContext = React.createContext<MessageContextValue | null>(null);\n\nfunction useMessageContext() {\n  return React.useContext(MessageContext);\n}\n\ntype MessageProps = React.HTMLAttributes<HTMLDivElement> & {\n  from: MessageFrom;\n};\n\nconst Message = React.forwardRef<HTMLDivElement, MessageProps>(function Message(\n  {\n    className,\n    from,\n    children,\n    \"aria-label\": ariaLabelProp,\n    \"aria-labelledby\": ariaLabelledBy,\n    ...props\n  },\n  ref,\n) {\n  const ariaLabel =\n    ariaLabelProp ??\n    (ariaLabelledBy == null\n      ? from === \"user\"\n        ? \"User message\"\n        : \"Assistant message\"\n      : undefined);\n\n  return (\n    <MessageContext.Provider value={{ from }}>\n      <div\n        ref={ref}\n        data-slot=\"message\"\n        role=\"article\"\n        aria-label={ariaLabel}\n        aria-labelledby={ariaLabelledBy}\n        className={cn(\n          \"group/message flex w-full max-w-[90%] items-start gap-2\",\n          from === \"user\" ? \"ms-auto\" : \"me-auto\",\n          className,\n        )}\n        {...props}\n      >\n        {children}\n      </div>\n    </MessageContext.Provider>\n  );\n});\n\ntype MessageStackProps = React.HTMLAttributes<HTMLDivElement>;\n\nfunction MessageStack({ className, ...props }: MessageStackProps) {\n  const ctx = useMessageContext();\n  const from = ctx?.from ?? \"assistant\";\n\n  return (\n    <div\n      data-slot=\"message-stack\"\n      className={cn(\n        \"flex w-full flex-col gap-2\",\n        from === \"user\" ? \"items-end\" : \"items-start\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\ntype MessageContentProps = React.HTMLAttributes<HTMLDivElement>;\n\nfunction MessageContent({ className, ...props }: MessageContentProps) {\n  const ctx = useMessageContext();\n  const from = ctx?.from ?? \"assistant\";\n\n  return (\n    <div\n      data-slot=\"message-content\"\n      className={cn(\n        \"rounded-2xl text-sm leading-6.5 text-primary\",\n        from === \"user\"\n          ? \"w-fit bg-secondary px-4 py-2\"\n          : \"mb-1 w-full bg-transparent px-2\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\ntype MessageMarkdownProps = React.ComponentProps<typeof Streamdown>;\n\nfunction MessageMarkdown({\n  className,\n  components,\n  ...props\n}: MessageMarkdownProps) {\n  const mergedComponents = React.useMemo(\n    () => {\n      const defaultComponents = {\n        code: CodeBlock,\n        inlineCode: ({\n          children,\n          className,\n          ...props\n        }: React.HTMLAttributes<HTMLElement>) => (\n          <code\n            className={cn(\n              \"rounded-md border-none bg-muted px-1.5 py-0.5 font-mono text-xs font-[450]\",\n              className,\n            )}\n            data-slot=\"message-markdown-inline-code\"\n            {...props}\n          >\n            {children}\n          </code>\n        ),\n        table: (props: React.HTMLAttributes<HTMLTableElement>) => (\n          <div\n            data-slot=\"message-markdown-table-wrap\"\n            className={[\n              \"my-6 prose-no-margin overflow-hidden rounded-2xl border border-border bg-muted dark:border-accent dark:bg-background\",\n              \"[&_tbody_tr:first-child_td:first-child]:rounded-ss-xl\",\n              \"[&_tbody_tr:first-child_td:last-child]:rounded-se-xl\",\n              \"[&_tbody_tr:last-child_td:first-child]:rounded-es-xl\",\n              \"[&_tbody_tr:last-child_td:last-child]:rounded-ee-xl\",\n            ].join(\" \")}\n          >\n            <table\n              data-slot=\"message-markdown-table\"\n              className=\"w-full border-separate border-spacing-0 border-none bg-muted text-sm dark:bg-background\"\n              {...props}\n            />\n          </div>\n        ),\n        th: (props: React.ThHTMLAttributes<HTMLTableCellElement>) => (\n          <th\n            data-slot=\"message-markdown-th\"\n            className=\"border-none px-5 py-2 text-start text-[13px] font-normal! text-muted-foreground! dark:bg-background\"\n            {...props}\n          />\n        ),\n        td: (props: React.TdHTMLAttributes<HTMLTableCellElement>) => (\n          <td\n            data-slot=\"message-markdown-td\"\n            className=\"border-0 border-accent bg-card px-5 py-3 text-[13px] text-primary dark:bg-card [tr:not(:first-child)_&]:border-t\"\n            {...props}\n          />\n        ),\n      };\n\n      return {\n        ...(defaultComponents as object),\n        ...((components ?? {}) as object),\n      };\n    },\n    [components],\n  );\n\n  return (\n    <Streamdown\n      data-slot=\"message-markdown\"\n      className={cn(\n        ...messageMarkdownProseClasses,\n        \"[&>*:first-child]:mt-0 [&>*:last-child]:mb-0\",\n        className,\n      )}\n      components={mergedComponents as MessageMarkdownProps[\"components\"]}\n      shikiTheme={[\"github-light\", \"github-dark\"]}\n      plugins={streamdownPlugins}\n      {...props}\n    />\n  );\n}\n\ntype MessageActionsProps = React.HTMLAttributes<HTMLDivElement>;\n\nfunction MessageActions({ className, ...props }: MessageActionsProps) {\n  const ctx = useMessageContext();\n  const from = ctx?.from ?? \"assistant\";\n\n  return (\n    <div\n      data-slot=\"message-actions\"\n      className={cn(\n        \"flex w-full\",\n        from === \"user\" ? \"justify-end\" : \"justify-start\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\ntype MessageActionGroupProps = React.HTMLAttributes<HTMLDivElement>;\n\nfunction MessageActionGroup({ className, ...props }: MessageActionGroupProps) {\n  return (\n    <div\n      data-slot=\"message-action-group\"\n      className={cn(\"flex items-center gap-1\", className)}\n      {...props}\n    />\n  );\n}\n\ntype MessageActionProps = React.HTMLAttributes<HTMLDivElement> & {\n  asChild?: boolean;\n  tooltip?:\n    | string\n    | {\n        content?: string;\n        side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n        shortcut?: string;\n      };\n};\n\nfunction MessageAction({\n  asChild = false,\n  tooltip,\n  ...props\n}: MessageActionProps) {\n  const Comp = asChild ? Slot : \"div\";\n  const { content, side, shortcut } =\n    typeof tooltip === \"string\" ? { content: tooltip } : tooltip ?? {};\n\n  if (!content) {\n    return <Comp data-slot=\"message-action\" {...props} />;\n  }\n\n  return (\n    <TooltipProvider delayDuration={200}>\n      <Tooltip>\n        <TooltipTrigger asChild>\n          <Comp data-slot=\"message-action\" {...props} />\n        </TooltipTrigger>\n        <TooltipContent className=\"rounded-full\" side={side}>\n          {content}\n          {shortcut ? <Kbd className=\"rounded-md!\">{shortcut}</Kbd> : null}\n        </TooltipContent>\n      </Tooltip>\n    </TooltipProvider>\n  );\n}\n\nexport type MessageAvatarProps = {\n  src: string;\n  alt?: string;\n  fallback?: React.ReactNode;\n  delayMs?: React.ComponentProps<typeof AvatarFallback>[\"delayMs\"];\n  size?: React.ComponentProps<typeof Avatar>[\"size\"];\n  className?: string;\n};\n\nfunction MessageAvatar({\n  src,\n  alt = \"\",\n  fallback,\n  delayMs,\n  size,\n  className,\n}: MessageAvatarProps) {\n  return (\n    <Avatar\n      data-slot=\"message-avatar\"\n      size={size}\n      className={cn(\"size-7 shrink-0\", className)}\n    >\n      <AvatarImage\n        data-slot=\"message-avatar-image\"\n        src={src}\n        alt={alt}\n        className=\"my-0!\"\n      />\n      <AvatarFallback\n        data-slot=\"message-avatar-fallback\"\n        delayMs={delayMs}\n        className=\"my-0! shrink-0\"\n      >\n        {fallback}\n      </AvatarFallback>\n    </Avatar>\n  );\n}\n\nexport {\n  Message,\n  MessageStack,\n  MessageContent,\n  MessageMarkdown,\n  MessageActions,\n  MessageActionGroup,\n  MessageAction,\n  MessageAvatar,\n};\n","type":"registry:file","target":"~/components/nexus-ui/message.tsx"},{"path":"components/nexus-ui/codeblock.tsx","content":"\"use client\";\n\n/**\n * Streamdown `components.code` for fenced blocks (Shiki via {@link @streamdown/code}).\n * Installed together with **Message** (`@nexus-ui/message`): same registry item as `message.tsx`, not a separate add.\n *\n * Nexus chrome: title row (optional via **`showTitleRow`**), copy, bordered card, scroll viewport.\n * Set `components.inlineCode` per\n * [Streamdown](https://streamdown.ai/docs/components#inline-code).\n */\n\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { Copy01Icon, Tick02Icon } from \"@hugeicons/core-free-icons\";\nimport { code as codeHighlighter } from \"@streamdown/code\";\nimport type { Element as HastElement } from \"hast\";\nimport {\n  type ComponentProps,\n  type CSSProperties,\n  type DetailedHTMLProps,\n  type HTMLAttributes,\n  type MouseEventHandler,\n  type ReactNode,\n  isValidElement,\n  memo,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport type {\n  BundledLanguage,\n  CodeHighlighterPlugin,\n  ExtraProps,\n} from \"streamdown\";\nimport { StreamdownContext, useIsCodeFenceIncomplete } from \"streamdown\";\nimport { cn } from \"@/lib/utils\";\n\n// -----------------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------------\n\ntype MarkdownCodeElementProps = DetailedHTMLProps<\n  HTMLAttributes<HTMLElement>,\n  HTMLElement\n> &\n  ExtraProps;\n\n/** Streamdown `components.code` props: markdown element props plus Nexus chrome overrides. */\nexport type CodeBlockProps = MarkdownCodeElementProps & {\n  /** Language label + copy in a header row. When false, copy floats top-right. Defaults to true when omitted. */\n  showTitleRow?: boolean;\n};\n\ntype HighlightResult = NonNullable<\n  ReturnType<CodeHighlighterPlugin[\"highlight\"]>\n>;\n\ntype CodeBlockPreProps = Omit<ComponentProps<\"pre\">, \"children\"> & {\n  result: HighlightResult;\n  language: string;\n  lineNumbers?: boolean;\n  /** 1-based first line (`startLine=`); uses `app/global.css` `code .line`. */\n  lineNumbersStart?: number;\n};\n\ntype CodeBlockFencedViewProps = {\n  code: string;\n  language: string;\n  className?: string;\n  isIncomplete?: boolean;\n  startLine?: number;\n  lineNumbers?: boolean;\n  codePlugin?: CodeHighlighterPlugin;\n  showTitleRow?: boolean;\n};\n\n// -----------------------------------------------------------------------------\n// Constants\n// -----------------------------------------------------------------------------\n\nconst LANGUAGE_REGEX = /language-([^\\s]+)/;\nconst START_LINE_PATTERN = /startLine=(\\d+)/;\nconst NO_LINE_NUMBERS_PATTERN = /\\bnoLineNumbers\\b/;\n\n// -----------------------------------------------------------------------------\n// Utilities (pure)\n// -----------------------------------------------------------------------------\n\nfunction sameNodePosition(prev?: HastElement, next?: HastElement): boolean {\n  if (!(prev?.position || next?.position)) return true;\n  if (!(prev?.position && next?.position)) return false;\n  const ps = prev.position.start;\n  const ns = next.position.start;\n  const pe = prev.position.end;\n  const ne = next.position.end;\n  return (\n    ps?.line === ns?.line &&\n    ps?.column === ns?.column &&\n    pe?.line === ne?.line &&\n    pe?.column === ne?.column\n  );\n}\n\nfunction extractCodeString(children: ReactNode): string {\n  if (\n    isValidElement(children) &&\n    children.props &&\n    typeof children.props === \"object\" &&\n    \"children\" in children.props &&\n    typeof (children.props as { children?: unknown }).children === \"string\"\n  ) {\n    return (children.props as { children: string }).children;\n  }\n  if (typeof children === \"string\") return children;\n  return \"\";\n}\n\nfunction getMetastring(node?: HastElement): string | undefined {\n  const raw = node?.properties?.metastring;\n  return typeof raw === \"string\" ? raw : undefined;\n}\n\nfunction trimTrailingNewlines(str: string): string {\n  let end = str.length;\n  while (end > 0 && str[end - 1] === \"\\n\") end--;\n  return str.slice(0, end);\n}\n\nfunction buildRawHighlightResult(trimmed: string): HighlightResult {\n  return {\n    bg: \"transparent\",\n    fg: \"inherit\",\n    tokens: trimmed.split(\"\\n\").map((line) => [\n      {\n        content: line,\n        color: \"inherit\",\n        bgColor: \"transparent\",\n        htmlStyle: {},\n        offset: 0,\n      },\n    ]),\n  };\n}\n\nfunction parseRootStyle(rootStyle: string): Record<string, string> {\n  const style: Record<string, string> = {};\n  for (const decl of rootStyle.split(\";\")) {\n    const idx = decl.indexOf(\":\");\n    if (idx > 0) {\n      const prop = decl.slice(0, idx).trim();\n      const val = decl.slice(idx + 1).trim();\n      if (prop && val) style[prop] = val;\n    }\n  }\n  return style;\n}\n\n// -----------------------------------------------------------------------------\n// Primitives: copy control\n// -----------------------------------------------------------------------------\n\nconst COPIED_RESET_MS = 1500;\n\nfunction useCopyButton(\n  onCopy: () => void | Promise<void>,\n): [checked: boolean, onClick: MouseEventHandler] {\n  const [checked, setChecked] = useState(false);\n  const callbackRef = useRef(onCopy);\n  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n  useEffect(() => {\n    callbackRef.current = onCopy;\n  }, [onCopy]);\n\n  const onClick = useCallback<MouseEventHandler>(() => {\n    if (timeoutRef.current) clearTimeout(timeoutRef.current);\n    void Promise.resolve(callbackRef.current()).then(() => {\n      setChecked(true);\n      timeoutRef.current = setTimeout(() => {\n        setChecked(false);\n      }, COPIED_RESET_MS);\n    });\n  }, []);\n\n  useEffect(() => {\n    return () => {\n      if (timeoutRef.current) clearTimeout(timeoutRef.current);\n    };\n  }, []);\n\n  return [checked, onClick];\n}\n\nfunction CodeBlockCopyButton({\n  text,\n  showGlow = false,\n  className,\n}: {\n  text: string;\n  showGlow?: boolean;\n  className?: string;\n}) {\n  const [checked, onClick] = useCopyButton(() => {\n    void navigator.clipboard.writeText(text);\n  });\n\n  return (\n    <div className=\"relative\">\n      {showGlow ? (\n        <div\n          className={cn(\n            \"pointer-events-none absolute top-1/2 left-1/2 z-0 size-13.5 -translate-x-1/2 -translate-y-1/2 rounded-l-full rounded-tr-full bg-linear-to-l\",\n            \"from-card from-70% to-card/0\",\n          )}\n        />\n      ) : null}\n      <button\n        type=\"button\"\n        data-checked={checked || undefined}\n        className={cn(\n          \"relative flex size-7 cursor-pointer items-center justify-center rounded-lg text-ring hover:text-primary\",\n          className,\n        )}\n        aria-label={checked ? \"Copied\" : \"Copy code\"}\n        onClick={onClick}\n      >\n        <span className=\"flex size-5 items-center justify-center\">\n          {checked ? (\n            <HugeiconsIcon\n              icon={Tick02Icon}\n              strokeWidth={1.75}\n              className=\"size-4.5\"\n            />\n          ) : (\n            <HugeiconsIcon\n              icon={Copy01Icon}\n              strokeWidth={1.75}\n              className=\"size-4\"\n            />\n          )}\n        </span>\n      </button>\n    </div>\n  );\n}\n\n// -----------------------------------------------------------------------------\n// Primitives: title row\n// -----------------------------------------------------------------------------\n\nfunction CodeBlockTitleRow({\n  title,\n  copyText,\n}: {\n  title: string;\n  copyText: string;\n}) {\n  return (\n    <div className=\"flex h-9.5 items-center gap-2 px-4 text-muted-foreground\">\n      <figcaption className=\"flex-1 truncate text-[13px] lowercase\">\n        {title}\n      </figcaption>\n      <div className=\"-me-2 flex shrink-0 items-center\">\n        <CodeBlockCopyButton showGlow={false} text={copyText} />\n      </div>\n    </div>\n  );\n}\n\n// -----------------------------------------------------------------------------\n// Primitives: scroll viewport\n// -----------------------------------------------------------------------------\n\nfunction CodeBlockViewport({\n  /** `top` when a title row sits above; `all` when the viewport is the only block body. */\n  rounding = \"top\",\n  children,\n}: {\n  rounding?: \"top\" | \"all\";\n  children: React.ReactNode;\n}) {\n  return (\n    <div\n      className={cn(\n        \"no-scrollbar overflow-auto overscroll-x-none px-4 py-3.5 text-sm leading-6\",\n        rounding === \"top\" ? \"rounded-t-xl\" : \"rounded-xl\",\n        \"bg-card\",\n      )}\n    >\n      {children}\n    </div>\n  );\n}\n\n// -----------------------------------------------------------------------------\n// Primitives: figure chrome (outer shell + inner card)\n// -----------------------------------------------------------------------------\n\nfunction CodeBlockFigureChrome({\n  className,\n  language,\n  isIncomplete,\n  showTitleRow,\n  title,\n  copyText,\n  children,\n}: {\n  className?: string;\n  language: string;\n  isIncomplete?: boolean;\n  showTitleRow: boolean;\n  title: string;\n  copyText: string;\n  children: React.ReactNode;\n}) {\n  return (\n    <figure\n      className={cn(\n        \"my-4 rounded-xl border border-border dark:border-accent\",\n        showTitleRow ? \"bg-muted dark:bg-background\" : \"dark:bg-card\",\n        \"not-prose relative w-full overflow-hidden text-[13px] font-[450]\",\n        className,\n      )}\n      data-incomplete={isIncomplete || undefined}\n      data-language={language}\n      data-slot=\"nexus-code-block\"\n      dir=\"ltr\"\n      tabIndex={-1}\n      style={{ contentVisibility: \"auto\", containIntrinsicSize: \"auto 200px\" }}\n    >\n      {showTitleRow ? (\n        <CodeBlockTitleRow copyText={copyText} title={title} />\n      ) : (\n        <div className=\"absolute top-3 right-3 z-20\">\n          <CodeBlockCopyButton showGlow text={copyText} />\n        </div>\n      )}\n\n      <CodeBlockViewport rounding={showTitleRow ? \"top\" : \"all\"}>\n        {children}\n      </CodeBlockViewport>\n    </figure>\n  );\n}\n\n// -----------------------------------------------------------------------------\n// Primitives: highlighted token lines → pre/code\n// -----------------------------------------------------------------------------\n\nfunction CodeBlockTokenSpan({\n  token,\n}: {\n  token: HighlightResult[\"tokens\"][number][number];\n}) {\n  const tokenStyle: Record<string, string> = {};\n  let hasBg = Boolean(token.bgColor);\n  if (token.color) tokenStyle[\"--sdm-c\"] = token.color;\n  if (token.bgColor) tokenStyle[\"--sdm-tbg\"] = token.bgColor;\n  if (token.htmlStyle) {\n    for (const [key, value] of Object.entries(token.htmlStyle)) {\n      if (value == null) continue;\n      if (key === \"color\") {\n        tokenStyle[\"--sdm-c\"] = String(value);\n      } else if (key === \"background-color\") {\n        tokenStyle[\"--sdm-tbg\"] = String(value);\n        hasBg = true;\n      } else {\n        tokenStyle[key] = String(value);\n      }\n    }\n  }\n  const htmlAttrs = (\n    token as { htmlAttrs?: Record<string, string | undefined> }\n  ).htmlAttrs;\n  return (\n    <span\n      className={cn(\n        \"text-(--sdm-c,inherit)\",\n        \"dark:text-(--shiki-dark,var(--sdm-c,inherit))\",\n        hasBg && \"bg-(--sdm-tbg)\",\n        hasBg && \"dark:bg-(--shiki-dark-bg,var(--sdm-tbg))\",\n      )}\n      style={tokenStyle as CSSProperties}\n      {...htmlAttrs}\n    >\n      {token.content}\n    </span>\n  );\n}\n\nconst CodeBlockPre = memo(\n  function CodeBlockPre({\n    result,\n    language,\n    className,\n    lineNumbers = true,\n    lineNumbersStart = 1,\n    ...rest\n  }: CodeBlockPreProps) {\n    const preStyle = useMemo(() => {\n      const style: Record<string, string> = {};\n      if (result.bg) style[\"--sdm-bg\"] = result.bg;\n      if (result.fg) style[\"--sdm-fg\"] = result.fg;\n      if (result.rootStyle && typeof result.rootStyle === \"string\") {\n        Object.assign(style, parseRootStyle(result.rootStyle));\n      }\n      return style as CSSProperties;\n    }, [result.bg, result.fg, result.rootStyle]);\n\n    return (\n      <pre\n        className={cn(\n          \"w-max min-w-full bg-(--sdm-bg,inherit) *:flex *:flex-col dark:bg-(--shiki-dark-bg,var(--sdm-bg,inherit))\",\n          className,\n        )}\n        data-language={language}\n        data-slot=\"nexus-code-block-body\"\n        style={preStyle}\n        {...rest}\n      >\n        <code\n          style={\n            lineNumbers\n              ? ({\n                  counterSet: `line ${Number(lineNumbersStart) - 1}`,\n                } satisfies CSSProperties)\n              : undefined\n          }\n        >\n          {result.tokens.map((row, rowIndex) => (\n            <span\n              key={rowIndex}\n              className={lineNumbers ? \"line block\" : \"block\"}\n            >\n              {row.length === 0 || (row.length === 1 && row[0].content === \"\")\n                ? \"\\n\"\n                : row.map((token, tokenIndex) => (\n                    <CodeBlockTokenSpan key={tokenIndex} token={token} />\n                  ))}\n            </span>\n          ))}\n        </code>\n      </pre>\n    );\n  },\n  (prev, next) =>\n    prev.result === next.result &&\n    prev.language === next.language &&\n    prev.className === next.className &&\n    prev.lineNumbers === next.lineNumbers &&\n    prev.lineNumbersStart === next.lineNumbersStart,\n);\nCodeBlockPre.displayName = \"CodeBlockPre\";\n\n// -----------------------------------------------------------------------------\n// Primitives: async Shiki highlight + pre\n// -----------------------------------------------------------------------------\n\nfunction CodeBlockShikiPre({\n  code,\n  language,\n  raw,\n  className,\n  lineNumbers,\n  lineNumbersStart,\n  codePlugin,\n}: {\n  code: string;\n  language: string;\n  raw: HighlightResult;\n  className?: string;\n  lineNumbers?: boolean;\n  lineNumbersStart?: number;\n  codePlugin: CodeHighlighterPlugin;\n}) {\n  const { shikiTheme } = useContext(StreamdownContext);\n  const [result, setResult] = useState<HighlightResult>(raw);\n\n  useEffect(() => {\n    codePlugin.highlight(\n      {\n        code,\n        language: language as BundledLanguage,\n        themes: shikiTheme,\n      },\n      (highlighted) => setResult(highlighted),\n    );\n  }, [code, language, shikiTheme, codePlugin, raw]);\n\n  return (\n    <CodeBlockPre\n      className={className}\n      language={language}\n      lineNumbers={lineNumbers}\n      lineNumbersStart={lineNumbersStart}\n      result={result}\n    />\n  );\n}\n\n// -----------------------------------------------------------------------------\n// Composed: CodeBlockFencedView (string + meta → chrome + Shiki)\n// -----------------------------------------------------------------------------\n\nfunction CodeBlockFencedView({\n  code,\n  language,\n  className,\n  isIncomplete,\n  startLine,\n  lineNumbers = true,\n  codePlugin = codeHighlighter,\n  showTitleRow: showTitleRowProp,\n}: CodeBlockFencedViewProps) {\n  const showTitleRow = showTitleRowProp ?? true;\n  const trimmed = useMemo(() => trimTrailingNewlines(code), [code]);\n  const raw = useMemo(() => buildRawHighlightResult(trimmed), [trimmed]);\n  const title = (language || \"code\").toLowerCase();\n  const showLineNumbers =\n    lineNumbers !== false && trimmed.split(\"\\n\").length > 1;\n\n  return (\n    <CodeBlockFigureChrome\n      className={className}\n      copyText={trimmed}\n      isIncomplete={isIncomplete}\n      language={language}\n      showTitleRow={showTitleRow}\n      title={title}\n    >\n      <CodeBlockShikiPre\n        code={trimmed}\n        codePlugin={codePlugin}\n        language={language}\n        lineNumbers={showLineNumbers}\n        lineNumbersStart={startLine ?? 1}\n        raw={raw}\n      />\n    </CodeBlockFigureChrome>\n  );\n}\n\n// -----------------------------------------------------------------------------\n// Export: Streamdown `components.code`\n// -----------------------------------------------------------------------------\n\nexport const CodeBlock = memo(\n  function CodeBlock({\n    node,\n    className,\n    children,\n    showTitleRow,\n  }: CodeBlockProps) {\n    const { lineNumbers: contextLineNumbers } = useContext(StreamdownContext);\n    const isIncompleteFence = useIsCodeFenceIncomplete();\n\n    const match = className?.match(LANGUAGE_REGEX);\n    const language = match?.[1] ?? \"\";\n\n    const metastring = getMetastring(node);\n    const startLineMatch = metastring?.match(START_LINE_PATTERN);\n    const parsedStart = startLineMatch\n      ? Number.parseInt(startLineMatch[1], 10)\n      : undefined;\n    const startLine =\n      parsedStart !== undefined && parsedStart >= 1 ? parsedStart : undefined;\n    const metaNoLineNumbers = metastring\n      ? NO_LINE_NUMBERS_PATTERN.test(metastring)\n      : false;\n    const showLineNumbers = !metaNoLineNumbers && contextLineNumbers !== false;\n\n    const codeText = extractCodeString(children);\n\n    return (\n      <CodeBlockFencedView\n        className={className}\n        code={codeText}\n        codePlugin={codeHighlighter}\n        isIncomplete={isIncompleteFence}\n        language={language}\n        lineNumbers={showLineNumbers}\n        showTitleRow={showTitleRow}\n        startLine={startLine}\n      />\n    );\n  },\n  (p, n) =>\n    p.className === n.className &&\n    sameNodePosition(p.node, n.node) &&\n    p.showTitleRow === n.showTitleRow,\n);\nCodeBlock.displayName = \"CodeBlock\";\n","type":"registry:file","target":"~/components/nexus-ui/codeblock.tsx"}],"css":{"@source \"../node_modules/streamdown/dist/index.js\"":{},"@source \"../node_modules/@streamdown/code/dist/*.js\"":{},"@source \"../node_modules/@streamdown/mermaid/dist/*.js\"":{},"@source \"../node_modules/@streamdown/math/dist/*.js\"":{},"@source \"../node_modules/@streamdown/cjk/dist/*.js\"":{},"@layer base":{"code":{"counter-reset":"line"},"code .line::before":{"counter-increment":"line","content":"counter(line)","display":"inline-block","width":"1rem","margin-right":"1.25rem","text-align":"right","color":"#737373","padding-left":"0","font-size":"14px"},"code .line":{"padding-left":"0px !important"},".no-scrollbar":{"-ms-overflow-style":"none","scrollbar-width":"none"},".no-scrollbar::-webkit-scrollbar":{"display":"none"}}},"type":"registry:ui"}