CodeBlock
The codeBlock
node is designed to represent blocks of code within your document.
import 'prosekit/basic/style.css' import 'prosekit/basic/typography.css' import { createEditor } from 'prosekit/core' import { ProseKit } from 'prosekit/react' import { useMemo } from 'react' import { defaultContent } from './default-doc' import { defineExtension } from './extension' import Toolbar from './toolbar' export default function Editor() { const editor = useMemo(() => { const extension = defineExtension() return createEditor({ extension, defaultContent }) }, []) return ( <ProseKit editor={editor}> <div className='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white'> <Toolbar /> <div className='relative w-full flex-1 box-border overflow-y-scroll'> <div ref={editor.mount} className='ProseMirror box-border min-h-full px-[max(4rem,_calc(50%-20rem))] py-8 outline-none outline-0 [&_span[data-mention="user"]]:text-blue-500 [&_span[data-mention="tag"]]:text-violet-500'></div> </div> </div> </ProseKit> ) }
import { defineBasicExtension } from 'prosekit/basic' import { union } from 'prosekit/core' import { defineCodeBlock, defineCodeBlockShiki, } from 'prosekit/extensions/code-block' import { defineReactNodeView, type ReactNodeViewComponent, } from 'prosekit/react' import CodeBlockView from './code-block-view' export function defineExtension() { return union( defineBasicExtension(), defineCodeBlock(), defineCodeBlockShiki(), defineReactNodeView({ name: 'codeBlock', contentAs: 'code', component: CodeBlockView satisfies ReactNodeViewComponent, }), ) } export type EditorExtension = ReturnType<typeof defineExtension>
import { useEditor } from 'prosekit/react' import Button from './button' import type { EditorExtension } from './extension' export default function Toolbar() { const editor = useEditor<EditorExtension>({ update: true }) return ( <div className='z-2 box-border border-gray-200 dark:border-gray-800 border-solid border-l-0 border-r-0 border-t-0 border-b flex flex-wrap gap-1 p-2 items-center'> <Button pressed={editor.nodes.codeBlock.isActive()} disabled={!editor.commands.setCodeBlock.canExec()} onClick={() => editor.commands.setCodeBlock()} > <div className='i-lucide-square-code h-5 w-5' /> </Button> </div> ) }
import type { CodeBlockAttrs } from 'prosekit/extensions/code-block' import { shikiBundledLanguagesInfo } from 'prosekit/extensions/code-block' import type { ReactNodeViewProps } from 'prosekit/react' export default function CodeBlockView(props: ReactNodeViewProps) { const attrs = props.node.attrs as CodeBlockAttrs const language = attrs.language const setLanguage = (language: string) => { const attrs: CodeBlockAttrs = { language } props.setAttrs(attrs) } return ( <> <div className='relative mx-2 top-3 h-0 select-none overflow-visible text-xs' contentEditable={false}> <select className='outline-unset focus:outline-unset relative box-border w-auto cursor-pointer select-none appearance-none rounded border-none bg-transparent px-2 py-1 text-xs transition text-[var(--prosemirror-highlight)] opacity-0 hover:opacity-80 [div[data-node-view-root]:hover_&]:opacity-50 [div[data-node-view-root]:hover_&]:hover:opacity-80' onChange={(event) => setLanguage(event.target.value)} value={language || ''} > <option value="">Plain Text</option> {shikiBundledLanguagesInfo.map((info) => ( <option key={info.id} value={info.id}> {info.name} </option> ))} </select> </div> <pre ref={props.contentRef} data-language={language}></pre> </> ) }
import type { NodeJSON } from 'prosekit/core' const js = `async function main() {\n while (true) {\n await sleep();\n await eat();\n await code('JavaScript!');\n }\n}` const py = `async def main():\n while True:\n await sleep()\n await eat()\n await code("Python!")` const go = `func main() {\n\tfor {\n\t\tsleep()\n\t\teat()\n\t\tcode("Go!")\n\t}\n}` export const defaultContent: NodeJSON = { type: 'doc', content: [ { type: 'codeBlock', attrs: { language: 'javascript' }, content: [{ type: 'text', text: js }], }, { type: 'codeBlock', attrs: { language: 'python' }, content: [{ type: 'text', text: py }], }, { type: 'codeBlock', attrs: { language: 'go' }, content: [{ type: 'text', text: go }], }, ], }
import { TooltipContent, TooltipRoot, TooltipTrigger, } from 'prosekit/react/tooltip' import type { ReactNode } from 'react' export default function Button({ pressed, disabled, onClick, tooltip, children, }: { pressed?: boolean disabled?: boolean onClick?: VoidFunction tooltip?: string children: ReactNode }) { return ( <TooltipRoot> <TooltipTrigger className='block'> <button data-state={pressed ? 'on' : 'off'} disabled={disabled} onClick={() => onClick?.()} onMouseDown={(event) => event.preventDefault()} className='outline-unset focus-visible:outline-unset flex items-center justify-center rounded-md p-2 font-medium transition focus-visible:ring-2 text-sm focus-visible:ring-gray-900 dark:focus-visible:ring-gray-300 disabled:pointer-events-none min-w-9 min-h-9 disabled:opacity-50 hover:disabled:opacity-50 bg-transparent hover:bg-gray-100 dark:hover:bg-gray-800 data-[state=on]:bg-gray-200 dark:data-[state=on]:bg-gray-700' > {children} {tooltip ? <span className="sr-only">{tooltip}</span> : null} </button> </TooltipTrigger> {tooltip ? ( <TooltipContent className='z-50 overflow-hidden rounded-md border border-solid bg-gray-900 dark:bg-gray-50 px-3 py-1.5 text-xs text-gray-50 dark:text-gray-900 shadow-sm [&:not([data-state])]:hidden will-change-transform data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 data-[state=open]:animate-duration-150 data-[state=closed]:animate-duration-200 data-[side=bottom]:slide-in-from-top-2 data-[side=bottom]:slide-out-to-top-2 data-[side=left]:slide-in-from-right-2 data-[side=left]:slide-out-to-right-2 data-[side=right]:slide-in-from-left-2 data-[side=right]:slide-out-to-left-2 data-[side=top]:slide-in-from-bottom-2 data-[side=top]:slide-out-to-bottom-2'> {tooltip} </TooltipContent> ) : null} </TooltipRoot> ) }
import 'prosekit/basic/style.css' import 'prosekit/basic/typography.css' import { createEditor } from 'prosekit/core' import { ProseKit } from 'prosekit/solid' import { defaultContent } from './default-doc' import { defineExtension } from './extension' import Toolbar from './toolbar' export default function Editor() { const editor = createEditor({ extension: defineExtension(), defaultContent }) return ( <ProseKit editor={editor}> <div class='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white'> <Toolbar /> <div class='relative w-full flex-1 box-border overflow-y-scroll'> <div ref={editor.mount} class='ProseMirror box-border min-h-full px-[max(4rem,_calc(50%-20rem))] py-8 outline-none outline-0 [&_span[data-mention="user"]]:text-blue-500 [&_span[data-mention="tag"]]:text-violet-500'></div> </div> </div> </ProseKit> ) }
import { defineBasicExtension } from 'prosekit/basic' import { union } from 'prosekit/core' import { defineCodeBlock, defineCodeBlockShiki, } from 'prosekit/extensions/code-block' import { defineSolidNodeView, type SolidNodeViewComponent, } from 'prosekit/solid' import CodeBlockView from './code-block-view' export function defineExtension() { return union( defineBasicExtension(), defineCodeBlock(), defineCodeBlockShiki(), defineSolidNodeView({ name: 'codeBlock', contentAs: 'code', component: CodeBlockView satisfies SolidNodeViewComponent, }), ) } export type EditorExtension = ReturnType<typeof defineExtension>
import { useEditor } from 'prosekit/solid' import Button from './button' import type { EditorExtension } from './extension' export default function Toolbar() { const editor = useEditor<EditorExtension>({ update: true }) return ( <div class='z-2 box-border border-gray-200 dark:border-gray-800 border-solid border-l-0 border-r-0 border-t-0 border-b flex flex-wrap gap-1 p-2 items-center'> <Button pressed={editor().nodes.codeBlock.isActive} disabled={() => !editor().commands.setCodeBlock.canExec()} onClick={editor().commands.setCodeBlock} > <div class='i-lucide-square-code h-5 w-5' /> </Button> </div> ) }
import type { CodeBlockAttrs } from 'prosekit/extensions/code-block' import { shikiBundledLanguagesInfo } from 'prosekit/extensions/code-block' import type { SolidNodeViewProps } from 'prosekit/solid' import { For } from 'solid-js' export default function CodeBlockView(props: SolidNodeViewProps) { const attrs = props.node.attrs as CodeBlockAttrs const language = attrs.language const setLanguage = (language: string) => { const attrs: CodeBlockAttrs = { language } props.setAttrs(attrs) } return ( <> <div class='relative mx-2 top-3 h-0 select-none overflow-visible text-xs' contentEditable={false}> <select class='outline-unset focus:outline-unset relative box-border w-auto cursor-pointer select-none appearance-none rounded border-none bg-transparent px-2 py-1 text-xs transition text-[var(--prosemirror-highlight)] opacity-0 hover:opacity-80 [div[data-node-view-root]:hover_&]:opacity-50 [div[data-node-view-root]:hover_&]:hover:opacity-80' onChange={(event) => setLanguage(event.target.value)} value={language || ''} > <option value="">Plain Text</option> <For each={shikiBundledLanguagesInfo}> {(info) => <option value={info.id}>{info.name}</option>} </For> </select> </div> <pre ref={props.contentRef} data-language={language}></pre> </> ) }
import type { NodeJSON } from 'prosekit/core' const js = `async function main() {\n while (true) {\n await sleep();\n await eat();\n await code('JavaScript!');\n }\n}` const py = `async def main():\n while True:\n await sleep()\n await eat()\n await code("Python!")` const go = `func main() {\n\tfor {\n\t\tsleep()\n\t\teat()\n\t\tcode("Go!")\n\t}\n}` export const defaultContent: NodeJSON = { type: 'doc', content: [ { type: 'codeBlock', attrs: { language: 'javascript' }, content: [{ type: 'text', text: js }], }, { type: 'codeBlock', attrs: { language: 'python' }, content: [{ type: 'text', text: py }], }, { type: 'codeBlock', attrs: { language: 'go' }, content: [{ type: 'text', text: go }], }, ], }
import type { ParentProps } from 'solid-js' export default function Button({ pressed, disabled, onClick, children, }: ParentProps<{ pressed: () => boolean disabled?: () => boolean onClick: () => void }>) { return ( <button data-state={pressed() ? 'on' : 'off'} disabled={disabled?.()} onClick={() => onClick()} onMouseDown={(event) => event.preventDefault()} class='outline-unset focus-visible:outline-unset flex items-center justify-center rounded-md p-2 font-medium transition focus-visible:ring-2 text-sm focus-visible:ring-gray-900 dark:focus-visible:ring-gray-300 disabled:pointer-events-none min-w-9 min-h-9 disabled:opacity-50 hover:disabled:opacity-50 bg-transparent hover:bg-gray-100 dark:hover:bg-gray-800 data-[state=on]:bg-gray-200 dark:data-[state=on]:bg-gray-700' > {children} </button> ) }
<script lang="ts"> import 'prosekit/basic/style.css' import 'prosekit/basic/typography.css' import { createEditor } from 'prosekit/core' import { ProseKit } from 'prosekit/svelte' import { defaultContent } from './default-doc' import { defineExtension } from './extension' import Toolbar from './toolbar.svelte' const extension = defineExtension() const editor = createEditor({ extension, defaultContent }) const mount = (element: HTMLElement) => { editor.mount(element) return { destroy: () => editor.unmount() } } </script> <ProseKit {editor}> <div class='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white'> <Toolbar /> <div class='relative w-full flex-1 box-border overflow-y-scroll'> <div use:mount class='ProseMirror box-border min-h-full px-[max(4rem,_calc(50%-20rem))] py-8 outline-none outline-0 [&_span[data-mention="user"]]:text-blue-500 [&_span[data-mention="tag"]]:text-violet-500'></div> </div> </div> </ProseKit>
import { defineBasicExtension } from 'prosekit/basic' import { union } from 'prosekit/core' import { defineCodeBlock, defineCodeBlockShiki, } from 'prosekit/extensions/code-block' import { defineSvelteNodeView, type SvelteNodeViewComponent, } from 'prosekit/svelte' import CodeBlockView from './code-block-view.svelte' export function defineExtension() { return union( defineBasicExtension(), defineCodeBlock(), defineCodeBlockShiki(), defineSvelteNodeView({ name: 'codeBlock', contentAs: 'code', component: CodeBlockView as SvelteNodeViewComponent, }), ) } export type EditorExtension = ReturnType<typeof defineExtension>
<script lang="ts"> import { useEditor } from 'prosekit/svelte' import Button from './button.svelte' import type { EditorExtension } from './extension' const editor = useEditor<EditorExtension>({ update: true }) </script> <div class='z-2 box-border border-gray-200 dark:border-gray-800 border-solid border-l-0 border-r-0 border-t-0 border-b flex flex-wrap gap-1 p-2 items-center'> <Button pressed={$editor.nodes.codeBlock.isActive()} disabled={!$editor.commands.setCodeBlock.canExec()} onClick={() => $editor.commands.setCodeBlock()} tooltip="Heading 1" > <div class='i-lucide-square-code h-5 w-5'></div> </Button> </div>
<script lang="ts"> import type { CodeBlockAttrs } from 'prosekit/extensions/code-block' import { shikiBundledLanguagesInfo } from 'prosekit/extensions/code-block' import type { SvelteNodeViewProps } from 'prosekit/svelte' const { node, setAttrs, contentRef }: SvelteNodeViewProps = $props() const attrs = $node.attrs as CodeBlockAttrs const language = attrs.language const setLanguage = (language: string) => { const attrs: CodeBlockAttrs = { language } setAttrs(attrs) } const handleLanguageChange = (event: Event) => { setLanguage((event.target as HTMLSelectElement).value) } </script> <div class='relative mx-2 top-3 h-0 select-none overflow-visible text-xs' contentEditable={false}> <select class='outline-unset focus:outline-unset relative box-border w-auto cursor-pointer select-none appearance-none rounded border-none bg-transparent px-2 py-1 text-xs transition text-[var(--prosemirror-highlight)] opacity-0 hover:opacity-80 [div[data-node-view-root]:hover_&]:opacity-50 [div[data-node-view-root]:hover_&]:hover:opacity-80' onchange={handleLanguageChange} value={language || ''} > <option value="">Plain Text</option> {#each shikiBundledLanguagesInfo as info (info.id)} <option value={info.id}>{info.name}</option> {/each} </select> </div> <pre use:contentRef data-language={language}></pre>
import type { NodeJSON } from 'prosekit/core' const js = `async function main() {\n while (true) {\n await sleep();\n await eat();\n await code('JavaScript!');\n }\n}` const py = `async def main():\n while True:\n await sleep()\n await eat()\n await code("Python!")` const go = `func main() {\n\tfor {\n\t\tsleep()\n\t\teat()\n\t\tcode("Go!")\n\t}\n}` export const defaultContent: NodeJSON = { type: 'doc', content: [ { type: 'codeBlock', attrs: { language: 'javascript' }, content: [{ type: 'text', text: js }], }, { type: 'codeBlock', attrs: { language: 'python' }, content: [{ type: 'text', text: py }], }, { type: 'codeBlock', attrs: { language: 'go' }, content: [{ type: 'text', text: go }], }, ], }
import { defineEnterRule } from 'prosekit/extensions/enter-rule' /** * Converts the text before the text cursor into an emoji when pressing `Enter`. */ export function defineEmojiEnterRule() { return defineEnterRule({ regex: /:(apple|banana):$/, handler: ({ match, from, to, state }) => { const text = match[1] as 'apple' | 'banana' const emoji = text === 'apple' ? '🍎' : '🍌' return state.tr.replaceWith(from, to, state.schema.text(emoji)) }, }) }
<script lang="ts"> import { TooltipContent, TooltipRoot, TooltipTrigger, } from 'prosekit/svelte/tooltip' import type { Snippet } from 'svelte' interface Props { pressed?: boolean disabled?: boolean tooltip?: string onClick?: VoidFunction children?: Snippet } let { pressed = false, disabled = false, tooltip = '', onClick = undefined, children, }: Props = $props() </script> <TooltipRoot> <TooltipTrigger class='block'> <button data-state={pressed ? 'on' : 'off'} {disabled} onclick={() => onClick?.()} onmousedown={(event) => event.preventDefault()} class='outline-unset focus-visible:outline-unset flex items-center justify-center rounded-md p-2 font-medium transition focus-visible:ring-2 text-sm focus-visible:ring-gray-900 dark:focus-visible:ring-gray-300 disabled:pointer-events-none min-w-9 min-h-9 disabled:opacity-50 hover:disabled:opacity-50 bg-transparent hover:bg-gray-100 dark:hover:bg-gray-800 data-[state=on]:bg-gray-200 dark:data-[state=on]:bg-gray-700' > {@render children?.()} {#if tooltip} <span class="sr-only">{tooltip}</span> {/if} </button> </TooltipTrigger> {#if tooltip} <TooltipContent class='z-50 overflow-hidden rounded-md border border-solid bg-gray-900 dark:bg-gray-50 px-3 py-1.5 text-xs text-gray-50 dark:text-gray-900 shadow-sm [&:not([data-state])]:hidden will-change-transform data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 data-[state=open]:animate-duration-150 data-[state=closed]:animate-duration-200 data-[side=bottom]:slide-in-from-top-2 data-[side=bottom]:slide-out-to-top-2 data-[side=left]:slide-in-from-right-2 data-[side=left]:slide-out-to-right-2 data-[side=right]:slide-in-from-left-2 data-[side=right]:slide-out-to-left-2 data-[side=top]:slide-in-from-bottom-2 data-[side=top]:slide-out-to-bottom-2'> {tooltip} </TooltipContent> {/if} </TooltipRoot>
<script setup lang="ts"> import 'prosekit/basic/style.css' import 'prosekit/basic/typography.css' import { createEditor } from 'prosekit/core' import { ProseKit } from 'prosekit/vue' import { ref, watchPostEffect, } from 'vue' import { defaultContent } from './default-doc' import { defineExtension } from './extension' import Toolbar from './toolbar.vue' const editor = createEditor({ extension: defineExtension(), defaultContent }) const editorRef = ref<HTMLDivElement | null>(null) watchPostEffect((onCleanup) => { editor.mount(editorRef.value) onCleanup(() => editor.unmount()) }) </script> <template> <ProseKit :editor="editor"> <div class='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white'> <Toolbar /> <div class='relative w-full flex-1 box-border overflow-y-scroll'> <div ref="editorRef" class='ProseMirror box-border min-h-full px-[max(4rem,_calc(50%-20rem))] py-8 outline-none outline-0 [&_span[data-mention="user"]]:text-blue-500 [&_span[data-mention="tag"]]:text-violet-500' /> </div> </div> </ProseKit> </template>
import { defineBasicExtension } from 'prosekit/basic' import { union } from 'prosekit/core' import { defineCodeBlock, defineCodeBlockShiki, } from 'prosekit/extensions/code-block' import { defineVueNodeView, type VueNodeViewComponent, } from 'prosekit/vue' import CodeBlockView from './code-block-view.vue' export function defineExtension() { return union( defineBasicExtension(), defineCodeBlock(), defineCodeBlockShiki(), defineVueNodeView({ name: 'codeBlock', contentAs: 'code', component: CodeBlockView as VueNodeViewComponent, }), ) } export type EditorExtension = ReturnType<typeof defineExtension>
<script setup lang="ts"> import { useEditor } from 'prosekit/vue' import Button from './button.vue' import type { EditorExtension } from './extension' const editor = useEditor<EditorExtension>({ update: true }) </script> <template> <div class='z-2 box-border border-gray-200 dark:border-gray-800 border-solid border-l-0 border-r-0 border-t-0 border-b flex flex-wrap gap-1 p-2 items-center'> <Button :pressed="editor.nodes.codeBlock.isActive()" :disabled="!editor.commands.setCodeBlock.canExec()" @click="() => editor.commands.setCodeBlock()" > <div class='i-lucide-square-code h-5 w-5' /> </Button> </div> </template>
<script setup lang="ts"> import type { CodeBlockAttrs } from 'prosekit/extensions/code-block' import { shikiBundledLanguagesInfo } from 'prosekit/extensions/code-block' import type { VueNodeViewProps } from 'prosekit/vue' import { computed } from 'vue' const props = defineProps<VueNodeViewProps>() const language = computed({ get() { const attrs = props.node.value.attrs as CodeBlockAttrs return attrs.language || '' }, set(language: string) { const attrs: CodeBlockAttrs = { language } props.setAttrs(attrs) }, }) </script> <template> <div class='relative mx-2 top-3 h-0 select-none overflow-visible text-xs' contenteditable="false"> <select v-model="language" class='outline-unset focus:outline-unset relative box-border w-auto cursor-pointer select-none appearance-none rounded border-none bg-transparent px-2 py-1 text-xs transition text-[var(--prosemirror-highlight)] opacity-0 hover:opacity-80 [div[data-node-view-root]:hover_&]:opacity-50 [div[data-node-view-root]:hover_&]:hover:opacity-80'> <option value="">Plain Text</option> <option v-for="info of shikiBundledLanguagesInfo" :key="info.id" :value="info.id" > {{ info.name }} </option> </select> </div> <pre :ref="props.contentRef" :data-language="language" /> </template>
import type { NodeJSON } from 'prosekit/core' const js = `async function main() {\n while (true) {\n await sleep();\n await eat();\n await code('JavaScript!');\n }\n}` const py = `async def main():\n while True:\n await sleep()\n await eat()\n await code("Python!")` const go = `func main() {\n\tfor {\n\t\tsleep()\n\t\teat()\n\t\tcode("Go!")\n\t}\n}` export const defaultContent: NodeJSON = { type: 'doc', content: [ { type: 'codeBlock', attrs: { language: 'javascript' }, content: [{ type: 'text', text: js }], }, { type: 'codeBlock', attrs: { language: 'python' }, content: [{ type: 'text', text: py }], }, { type: 'codeBlock', attrs: { language: 'go' }, content: [{ type: 'text', text: go }], }, ], }
<script setup lang="ts"> import { TooltipContent, TooltipRoot, TooltipTrigger, } from 'prosekit/vue/tooltip' defineProps<{ pressed?: Boolean disabled?: Boolean tooltip?: string }>() const emit = defineEmits<{ click: [] }>() </script> <template> <TooltipRoot> <TooltipTrigger class='block'> <button :data-state="pressed ? 'on' : 'off'" :disabled="disabled ? true : undefined" class='outline-unset focus-visible:outline-unset flex items-center justify-center rounded-md p-2 font-medium transition focus-visible:ring-2 text-sm focus-visible:ring-gray-900 dark:focus-visible:ring-gray-300 disabled:pointer-events-none min-w-9 min-h-9 disabled:opacity-50 hover:disabled:opacity-50 bg-transparent hover:bg-gray-100 dark:hover:bg-gray-800 data-[state=on]:bg-gray-200 dark:data-[state=on]:bg-gray-700' @click="() => emit('click')" @mousedown.prevent > <slot /> <span v-if="tooltip" class="sr-only">{{ tooltip }}</span> </button> </TooltipTrigger> <TooltipContent v-if="tooltip" class='z-50 overflow-hidden rounded-md border border-solid bg-gray-900 dark:bg-gray-50 px-3 py-1.5 text-xs text-gray-50 dark:text-gray-900 shadow-sm [&:not([data-state])]:hidden will-change-transform data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 data-[state=open]:animate-duration-150 data-[state=closed]:animate-duration-200 data-[side=bottom]:slide-in-from-top-2 data-[side=bottom]:slide-out-to-top-2 data-[side=left]:slide-in-from-right-2 data-[side=left]:slide-out-to-right-2 data-[side=right]:slide-in-from-left-2 data-[side=right]:slide-out-to-left-2 data-[side=top]:slide-in-from-bottom-2 data-[side=top]:slide-out-to-bottom-2'> {{ tooltip }} </TooltipContent> </TooltipRoot> </template>
import {
} from 'prosekit/extensions/code-block' const defineCodeBlock function defineCodeBlock(): CodeBlockExtension
Adds `codeBlock` nodes to the editor. This includes the following extensions: - {@link defineCodeBlockSpec } - {@link defineCodeBlockInputRule } - {@link defineCodeBlockEnterRule } - {@link defineCodeBlockKeymap } - {@link defineCodeBlockCommands } .= extension const extension: CodeBlockExtension
() defineCodeBlock function defineCodeBlock(): CodeBlockExtension
Adds `codeBlock` nodes to the editor. This includes the following extensions: - {@link defineCodeBlockSpec } - {@link defineCodeBlockInputRule } - {@link defineCodeBlockEnterRule } - {@link defineCodeBlockKeymap } - {@link defineCodeBlockCommands } .
Set the selected node to a codeBlock
node.
. editor const editor: Editor<CodeBlockExtension>
. commands
Editor<CodeBlockExtension>.commands: ToCommandAction<{ setCodeBlock: [attrs?: CodeBlockAttrs]; insertCodeBlock: [attrs?: CodeBlockAttrs]; toggleCodeBlock: [attrs?: CodeBlockAttrs]; setCodeBlockAttrs: [attrs: CodeBlockAttrs]; }>
All {@link CommandAction } s defined by the editor.() setCodeBlock
setCodeBlock: CommandAction (attrs?: CodeBlockAttrs | undefined) => boolean
Execute the current command. Return `true` if the command was successfully executed, otherwise `false`.
Insert a new codeBlock
node.
. editor const editor: Editor<CodeBlockExtension>
. commands
Editor<CodeBlockExtension>.commands: ToCommandAction<{ setCodeBlock: [attrs?: CodeBlockAttrs]; insertCodeBlock: [attrs?: CodeBlockAttrs]; toggleCodeBlock: [attrs?: CodeBlockAttrs]; setCodeBlockAttrs: [attrs: CodeBlockAttrs]; }>
All {@link CommandAction } s defined by the editor.() insertCodeBlock
insertCodeBlock: CommandAction (attrs?: CodeBlockAttrs | undefined) => boolean
Execute the current command. Return `true` if the command was successfully executed, otherwise `false`.
Toggle the selected node between a codeBlock
node and a default node (e.g. a paragraph
node).
. editor const editor: Editor<CodeBlockExtension>
. commands
Editor<CodeBlockExtension>.commands: ToCommandAction<{ setCodeBlock: [attrs?: CodeBlockAttrs]; insertCodeBlock: [attrs?: CodeBlockAttrs]; toggleCodeBlock: [attrs?: CodeBlockAttrs]; setCodeBlockAttrs: [attrs: CodeBlockAttrs]; }>
All {@link CommandAction } s defined by the editor.() toggleCodeBlock
toggleCodeBlock: CommandAction (attrs?: CodeBlockAttrs | undefined) => boolean
Execute the current command. Return `true` if the command was successfully executed, otherwise `false`.
Set the attributes of the selected codeBlock
node.
. editor const editor: Editor<CodeBlockExtension>
. commands
Editor<CodeBlockExtension>.commands: ToCommandAction<{ setCodeBlock: [attrs?: CodeBlockAttrs]; insertCodeBlock: [attrs?: CodeBlockAttrs]; toggleCodeBlock: [attrs?: CodeBlockAttrs]; setCodeBlockAttrs: [attrs: CodeBlockAttrs]; }>
All {@link CommandAction } s defined by the editor.({ setCodeBlockAttrs
setCodeBlockAttrs: CommandAction (attrs: CodeBlockAttrs) => boolean
Execute the current command. Return `true` if the command was successfully executed, otherwise `false`.: 'javascript' }) language CodeBlockAttrs.language: string
Input ```
followed by an optional language name and press Enter
or Space
to create a new codeBlock
node.
Press Enter
three times at the end of or press Shift-Enter
to exit the current codeBlock
node.
You can use defineCodeBlockShiki
to enable syntax highlighting for the codeBlock
node using the Shiki library. defineCodeBlockShiki
will only load used languages and themes asynchronously, which is useful for reducing the initial bundle size of your application.
import {
} from 'prosekit/extensions/code-block' const defineCodeBlockShiki function defineCodeBlockShiki({ themes, langs, ...rest }?: CodeBlockShikiOptions): Extension
Adds syntax highlighting to code blocks using the [Shiki](https://github.com/shikijs/shiki) package. It will set two CSS variables on the code block elements: - `--prosemirror-highlight`: sets text color - `--prosemirror-highlight-bg`: sets background color= extension const extension: Extension<ExtensionTyping<any, any, any>>
({ defineCodeBlockShiki function defineCodeBlockShiki({ themes, langs, ...rest }?: CodeBlockShikiOptions): Extension
Adds syntax highlighting to code blocks using the [Shiki](https://github.com/shikijs/shiki) package. It will set two CSS variables on the code block elements: - `--prosemirror-highlight`: sets text color - `--prosemirror-highlight-bg`: sets background color: ['github-light'] }) themes CodeBlockShikiOptions.themes?: BundledTheme[] | undefined
A list of Shiki themes to pre-load. The first theme in the list will be used to render the code block.
If you want to use a different syntax highlighter or have more control over the syntax highlighting, you can use the defineCodeBlockHighlight
function to create an extension. This function accepts a parser
object, defined by the prosemirror-highlight library. For more details on how to use the other syntax highlighters, refer to the prosemirror-highlight documentation.
import {
} from 'prosekit/extensions/code-block' import { defineCodeBlockHighlight function defineCodeBlockHighlight({ parser, }: CodeBlockHighlightOptions): Extension
Adds syntax highlighting to code blocks. This function requires a `Parser` instance from the `prosemirror-highlight` package. See the [documentation](https://github.com/ocavue/prosemirror-highlight) for more information.} from './my-prosemirror-highlight-parser' const parser const parser: Parser
= extension const extension: Extension<ExtensionTyping<any, any, any>>
({ defineCodeBlockHighlight function defineCodeBlockHighlight({ parser, }: CodeBlockHighlightOptions): Extension
Adds syntax highlighting to code blocks. This function requires a `Parser` instance from the `prosemirror-highlight` package. See the [documentation](https://github.com/ocavue/prosemirror-highlight) for more information.}) parser parser: Parser