{
  "name": "svelte-ui-code-block-view",
  "title": "svelte-ui-code-block-view",
  "type": "registry:component",
  "description": "",
  "registryDependencies": [],
  "dependencies": [
    "beautiful-mermaid",
    "prosekit@^0.22.0"
  ],
  "files": [
    {
      "path": "registry/src/svelte/ui/code-block-view/code-block-view.svelte",
      "type": "registry:component",
      "target": "components/editor/ui/code-block-view/code-block-view.svelte",
      "content": "<script lang=\"ts\">\nimport { renderMermaidSVG, THEMES } from 'beautiful-mermaid'\nimport type { ProseMirrorNode } from 'prosekit/pm/model'\nimport type { CodeBlockAttrs } from 'prosekit/extensions/code-block'\nimport { isCodeBlockPreviewHiddenDecoration, shikiBundledLanguagesInfo } from 'prosekit/extensions/code-block'\nimport { TextSelection } from 'prosekit/pm/state'\nimport type { Decoration } from 'prosekit/pm/view'\nimport type { SvelteNodeViewProps } from 'prosekit/svelte'\nimport { fromStore } from 'svelte/store'\n\ninterface Props extends SvelteNodeViewProps {}\n\nconst props: Props = $props()\nconst node: ProseMirrorNode = $derived(fromStore(props.node).current)\nconst decorations: readonly Decoration[] = $derived(fromStore(props.decorations).current)\n\nconst attrs = $derived(node.attrs as CodeBlockAttrs)\nconst language = $derived(attrs.language || '')\nconst hidePreview = $derived(decorations.some(isCodeBlockPreviewHiddenDecoration))\nconst showMermaidPreview = $derived(!hidePreview && language === 'mermaid')\nlet preElement: HTMLPreElement | null = null\n\nconst mermaidPreview = $derived.by<{ svg: string | null; error: Error | null }>(() => {\n  if (language !== 'mermaid') return { svg: null, error: null }\n  try {\n    return { svg: renderMermaidSVG(node.textContent, THEMES['tokyo-night']), error: null }\n  } catch (err) {\n    return { svg: null, error: err instanceof Error ? err : new Error(String(err)) }\n  }\n})\n\nfunction setLanguage(lang: string) {\n  const newAttrs: CodeBlockAttrs = { language: lang }\n  props.setAttrs(newAttrs)\n}\n\nfunction bindContentRef(element: HTMLPreElement) {\n  props.contentRef(element)\n  preElement = element\n}\n\nfunction focusSource(event: MouseEvent) {\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  preElement?.scrollIntoView({ behavior: 'smooth', block: 'nearest' })\n}\n</script>\n\n<div\n  class=\"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    class=\"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    value={language}\n    onchange={(event) => setLanguage((event.target as HTMLSelectElement).value)}\n  >\n    <option value=\"\">Plain Text</option>\n    {#each shikiBundledLanguagesInfo as info (info.id)}\n      <option value={info.id}>\n        {info.name}\n      </option>\n    {/each}\n  </select>\n</div>\n<pre\n  use:bindContentRef\n  class=\"data-preview:hidden\"\n  data-preview={showMermaidPreview ? '' : undefined}\n  data-language={language}\n></pre>\n{#if showMermaidPreview}\n  <!-- svelte-ignore a11y_no_static_element_interactions -->\n  <div\n    aria-label=\"Edit source\"\n    class=\"block py-2 overflow-auto\"\n    contentEditable=\"false\"\n    onmousedown={focusSource}\n  >\n    {#if mermaidPreview.error}\n      <pre>{mermaidPreview.error.message}</pre>\n    {/if}\n    {#if mermaidPreview.svg}\n      <div>{@html mermaidPreview.svg}</div>\n    {/if}\n  </div>\n{/if}\n"
    },
    {
      "path": "registry/src/svelte/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 { defineSvelteNodeView, type SvelteNodeViewComponent } from 'prosekit/svelte'\n\nimport CodeBlockView from './code-block-view.svelte'\n\nexport function defineCodeBlockView(): Extension {\n  return defineSvelteNodeView({\n    name: 'codeBlock',\n    contentAs: 'code',\n    component: CodeBlockView as SvelteNodeViewComponent,\n  })\n}\n"
    }
  ],
  "meta": {
    "hasIcons": false,
    "hidden": false,
    "story": "",
    "framework": "svelte",
    "accumulatedFiles": [
      "registry/src/svelte/ui/code-block-view/code-block-view.svelte",
      "registry/src/svelte/ui/code-block-view/index.ts"
    ],
    "internalDependencies": []
  },
  "$schema": "https://ui.shadcn.com/schema/registry-item.json"
}
