{
  "name": "preact-ui-code-block-view",
  "title": "preact-ui-code-block-view",
  "type": "registry:component",
  "description": "",
  "registryDependencies": [],
  "dependencies": [
    "beautiful-mermaid",
    "prosekit@^0.22.0"
  ],
  "files": [
    {
      "path": "registry/src/preact/ui/code-block-view/code-block-view.tsx",
      "type": "registry:component",
      "target": "components/editor/ui/code-block-view/code-block-view.tsx",
      "content": "import { renderMermaidSVG, THEMES } from 'beautiful-mermaid'\nimport type { JSX } from 'preact'\nimport { useMemo, useRef } from 'preact/hooks'\nimport { isCodeBlockPreviewHiddenDecoration, shikiBundledLanguagesInfo, type CodeBlockAttrs } from 'prosekit/extensions/code-block'\nimport { TextSelection } from 'prosekit/pm/state'\nimport type { PreactNodeViewProps } from 'prosekit/preact'\n\nexport default function CodeBlockView(props: PreactNodeViewProps) {\n  const attrs = props.node.attrs as CodeBlockAttrs\n  const language = attrs.language || ''\n  const hidePreview = props.decorations.some(isCodeBlockPreviewHiddenDecoration)\n  const preRef = useRef<HTMLPreElement | null>(null)\n\n  const showMermaidPreview = !hidePreview && language === 'mermaid'\n\n  const setLanguage = (language: string) => {\n    const attrs: CodeBlockAttrs = { language }\n    props.setAttrs(attrs)\n  }\n\n  const handleChange = (\n    event: JSX.TargetedEvent<HTMLSelectElement, Event>,\n  ) => {\n    setLanguage(event.currentTarget.value)\n  }\n\n  const focusSource = (event: JSX.TargetedMouseEvent<HTMLDivElement>) => {\n    event.preventDefault()\n    const pos = props.getPos()\n    if (typeof pos !== 'number') return\n    const { state, dispatch } = props.view\n    const selection = TextSelection.near(state.doc.resolve(pos + 1), 1)\n    dispatch(state.tr.setSelection(selection))\n    props.view.focus()\n    preRef.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' })\n  }\n\n  const code = props.node.textContent\n\n  const mermaidPreview = useMemo(() => {\n    if (language !== 'mermaid') return { svg: null as string | null, error: null as Error | null }\n    try {\n      return { svg: renderMermaidSVG(code, THEMES['tokyo-night']), error: null }\n    } catch (err) {\n      return { svg: null, error: err instanceof Error ? err : new Error(String(err)) }\n    }\n  }, [code, language])\n\n  return (\n    <>\n      <div\n        className=\"relative mx-2 top-3 h-0 select-none overflow-visible text-xs data-preview:hidden\"\n        contentEditable={false}\n        data-preview={showMermaidPreview ? '' : undefined}\n      >\n        <select\n          aria-label=\"Code block language\"\n          className=\"outline-unset focus:outline-unset relative box-border w-auto cursor-pointer select-none appearance-none rounded-sm border-none bg-transparent px-2 py-1 text-xs transition text-(--prosemirror-highlight) opacity-0 hover:opacity-80 [div[data-node-view-root]:hover_&]:opacity-50 hover:[div[data-node-view-root]:hover_&]:opacity-80\"\n          onChange={handleChange}\n          value={language}\n        >\n          <option value=\"\">Plain Text</option>\n          {shikiBundledLanguagesInfo.map((info) => (\n            <option key={info.id} value={info.id}>\n              {info.name}\n            </option>\n          ))}\n        </select>\n      </div>\n      <pre\n        ref={(element) => {\n          props.contentRef(element)\n          preRef.current = element\n        }}\n        className=\"data-preview:hidden\"\n        data-preview={showMermaidPreview ? '' : undefined}\n        data-language={language}\n      ></pre>\n      {showMermaidPreview && (\n        <div\n          aria-label=\"Edit source\"\n          className=\"block py-2 overflow-auto\"\n          contentEditable={false}\n          onMouseDown={focusSource}\n        >\n          {mermaidPreview.error ? <pre>{mermaidPreview.error.message}</pre> : null}\n          {mermaidPreview.svg\n            ? <div dangerouslySetInnerHTML={{ __html: mermaidPreview.svg }}></div>\n            : null}\n        </div>\n      )}\n    </>\n  )\n}\n"
    },
    {
      "path": "registry/src/preact/ui/code-block-view/index.ts",
      "type": "registry:component",
      "target": "components/editor/ui/code-block-view/index.ts",
      "content": "import type { Extension } from 'prosekit/core'\nimport { definePreactNodeView, type PreactNodeViewComponent } from 'prosekit/preact'\n\nimport CodeBlockView from './code-block-view.tsx'\n\nexport function defineCodeBlockView(): Extension {\n  return definePreactNodeView({\n    name: 'codeBlock',\n    contentAs: 'code',\n    component: CodeBlockView satisfies PreactNodeViewComponent,\n  })\n}\n"
    }
  ],
  "meta": {
    "hasIcons": false,
    "hidden": false,
    "story": "",
    "framework": "preact",
    "accumulatedFiles": [
      "registry/src/preact/ui/code-block-view/code-block-view.tsx",
      "registry/src/preact/ui/code-block-view/index.ts"
    ],
    "internalDependencies": []
  },
  "$schema": "https://ui.shadcn.com/schema/registry-item.json"
}
