2026-04-30 06:06:36 +02:00
|
|
|
import { codeBlockOptions } from '@blocknote/code-block'
|
2026-05-12 18:21:26 +02:00
|
|
|
import { createCodeBlockSpec, type CodeBlockOptions } from '@blocknote/core'
|
2026-05-08 14:59:44 +02:00
|
|
|
import { supportsModernRegexFeatures } from '../utils/regexCapabilities'
|
2026-05-12 18:21:26 +02:00
|
|
|
import { lineNumbersForText } from '../utils/codeBlockEnhancements'
|
2026-04-30 06:06:36 +02:00
|
|
|
|
2026-05-03 12:28:00 +02:00
|
|
|
const LIGHT_CODE_THEME = 'github-light'
|
|
|
|
|
const DARK_CODE_THEME = 'github-dark'
|
|
|
|
|
|
|
|
|
|
type TolariaCodeHighlighter = Awaited<ReturnType<NonNullable<typeof codeBlockOptions.createHighlighter>>>
|
|
|
|
|
|
|
|
|
|
function currentCodeBlockTheme() {
|
|
|
|
|
if (typeof document === 'undefined') return LIGHT_CODE_THEME
|
|
|
|
|
|
|
|
|
|
const root = document.documentElement
|
|
|
|
|
return root.classList.contains('dark') || root.dataset.theme === 'dark'
|
|
|
|
|
? DARK_CODE_THEME
|
|
|
|
|
: LIGHT_CODE_THEME
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function prioritizeTheme(themes: string[], theme: string) {
|
|
|
|
|
return [theme, ...themes.filter((candidate) => candidate !== theme)]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function createTolariaCodeHighlighter(): Promise<TolariaCodeHighlighter> {
|
|
|
|
|
const highlighter = await codeBlockOptions.createHighlighter()
|
|
|
|
|
return {
|
|
|
|
|
...highlighter,
|
|
|
|
|
getLoadedThemes: () => prioritizeTheme(highlighter.getLoadedThemes(), currentCodeBlockTheme()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 06:06:36 +02:00
|
|
|
export function createTolariaCodeBlockOptions(): Partial<CodeBlockOptions> {
|
|
|
|
|
const options: Partial<CodeBlockOptions> = {
|
|
|
|
|
...codeBlockOptions,
|
2026-05-03 12:28:00 +02:00
|
|
|
createHighlighter: createTolariaCodeHighlighter,
|
2026-04-30 06:06:36 +02:00
|
|
|
defaultLanguage: 'text',
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-08 14:59:44 +02:00
|
|
|
if (supportsModernRegexFeatures()) return options
|
2026-04-30 06:06:36 +02:00
|
|
|
|
|
|
|
|
delete options.createHighlighter
|
|
|
|
|
return options
|
|
|
|
|
}
|
2026-05-12 18:21:26 +02:00
|
|
|
|
|
|
|
|
interface InlineTextItem {
|
|
|
|
|
text?: unknown
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface CodeBlockRenderContext {
|
|
|
|
|
blockContentDOMAttributes?: Record<string, string>
|
|
|
|
|
props?: unknown
|
|
|
|
|
renderType?: unknown
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function textFromInlineContent(content: unknown): string {
|
|
|
|
|
if (!Array.isArray(content)) return ''
|
|
|
|
|
|
|
|
|
|
return content
|
|
|
|
|
.map((item: InlineTextItem) => typeof item.text === 'string' ? item.text : '')
|
|
|
|
|
.join('')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function createTolariaCodeBlockSpec() {
|
|
|
|
|
const spec = createCodeBlockSpec(createTolariaCodeBlockOptions())
|
|
|
|
|
if (!spec.implementation?.render) return spec
|
|
|
|
|
|
|
|
|
|
const baseRender = spec.implementation.render
|
|
|
|
|
const render: typeof baseRender = function (this: ThisParameterType<typeof baseRender>, block, editor) {
|
|
|
|
|
const context = this as CodeBlockRenderContext
|
|
|
|
|
const blockContentDOMAttributes = {
|
|
|
|
|
...context.blockContentDOMAttributes,
|
|
|
|
|
'data-line-numbers': lineNumbersForText(textFromInlineContent(block.content)),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return baseRender.call(
|
|
|
|
|
{
|
|
|
|
|
...context,
|
|
|
|
|
blockContentDOMAttributes,
|
|
|
|
|
} as ThisParameterType<typeof baseRender>,
|
|
|
|
|
block,
|
|
|
|
|
editor,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...spec,
|
|
|
|
|
implementation: {
|
|
|
|
|
...spec.implementation,
|
|
|
|
|
render,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|