@domternal/extension-code-block-lowlight 0.6.2 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,29 +3,31 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@domternal/extension-code-block-lowlight)
|
|
4
4
|
[](https://github.com/domternal/domternal/blob/main/LICENSE)
|
|
5
5
|
|
|
6
|
-
A lightweight, extensible rich text editor toolkit built on <u>[ProseMirror](https://prosemirror.net/)</u>. Framework-agnostic headless core with first-class Angular, React, and
|
|
6
|
+
A lightweight, extensible rich text editor toolkit built on <u>[ProseMirror](https://prosemirror.net/)</u>. Framework-agnostic headless core with first-class Angular, React, Vue, and Vanilla wrappers.
|
|
7
7
|
Use it headless with vanilla JS/TS, add the built-in toolbar and theme, or drop in ready-made framework components. Fully tree-shakeable, import only what you use, unused extensions are stripped from your bundle.
|
|
8
8
|
|
|
9
9
|
## Links
|
|
10
10
|
|
|
11
|
-
<u>[Website](https://domternal.dev)</u> • <u>[Documentation](https://domternal.dev/v1/introduction)</u>
|
|
12
|
-
<u>[StackBlitz (Angular)](https://stackblitz.com/edit/domternal-angular-full-example)</u> • <u>[StackBlitz (React)](https://stackblitz.com/edit/domternal-react-full-example)</u> • <u>[StackBlitz (Vue)](https://stackblitz.com/edit/domternal-vue-full-example)</u> • <u>[StackBlitz (Vanilla TS)](https://stackblitz.com/edit/domternal-vanilla-full-example)</u>
|
|
11
|
+
<u>[Website](https://domternal.dev)</u> • <u>[Documentation](https://domternal.dev/v1/introduction)</u> •
|
|
12
|
+
<u>[StackBlitz (Angular)](https://stackblitz.com/edit/domternal-angular-full-example)</u> • <u>[StackBlitz (React)](https://stackblitz.com/edit/domternal-react-full-example)</u> • <u>[StackBlitz (Vue)](https://stackblitz.com/edit/domternal-vue-full-example)</u> • <u>[StackBlitz (Vanilla TS)](https://stackblitz.com/edit/domternal-vanilla-full-example)</u>
|
|
13
13
|
|
|
14
14
|
## Features
|
|
15
15
|
|
|
16
16
|
See <u>[Packages & Bundle Size](https://domternal.dev/v1/packages)</u> for a full breakdown of all packages and what each one includes.
|
|
17
17
|
|
|
18
18
|
- **Headless core** - use with any framework or vanilla JS/TS
|
|
19
|
-
- **Angular components** - editor, toolbar, bubble menu, floating menu, emoji picker (signals, OnPush, zoneless-ready)
|
|
20
|
-
- **React components** - composable `Domternal` component, toolbar, bubble menu, floating menu, emoji picker, custom node views (React 18+)
|
|
21
|
-
- **Vue components** - composable `Domternal` component, `useEditor`/`useEditorState` composables, toolbar, bubble menu, floating menu, emoji picker, custom node views (Vue 3.3+)
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
19
|
+
- **Angular components** - editor, toolbar, bubble menu, floating menu, emoji picker, notion color picker (signals, OnPush, zoneless-ready)
|
|
20
|
+
- **React components** - composable `Domternal` component, toolbar, bubble menu, floating menu, emoji picker, notion color picker, custom node views (React 18+)
|
|
21
|
+
- **Vue components** - composable `Domternal` component, `useEditor`/`useEditorState` composables, toolbar, bubble menu, floating menu, emoji picker, notion color picker, custom node views (Vue 3.3+)
|
|
22
|
+
- **Vanilla wrapper** - framework-free class-based API for Astro, Svelte, Solid, plain HTML, and Web Components - editor, toolbar, bubble menu, floating menu, emoji picker, notion color picker
|
|
23
|
+
- **Notion-style block UX** - drag-to-reorder, block context menu, slash command, smart paste, keyboard reorder, floating Table of Contents
|
|
24
|
+
- **65+ extensions across 15 packages** - nodes, marks, and behavior extensions
|
|
25
|
+
- **120+ chainable commands** - `editor.chain().focus().toggleBold().run()`
|
|
24
26
|
- **Full table support** - cell merging, column resize, row/column controls, cell toolbar, all free and MIT licensed
|
|
25
27
|
- **Tree-shakeable** - import only what you use, your bundler strips the rest
|
|
26
|
-
- **~
|
|
28
|
+
- **~44 KB gzipped** (own code), <u>[see Packages](https://domternal.dev/v1/packages)</u> for full bundle breakdown with ProseMirror
|
|
27
29
|
- **TypeScript first** - 100% typed, zero `any`
|
|
28
|
-
- **
|
|
30
|
+
- **15,000+ tests** - 4,000+ unit and 11,000+ E2E across 230+ Playwright specs and 4 demo apps
|
|
29
31
|
- **Light and dark theme** - 70+ CSS custom properties for full visual control
|
|
30
32
|
- **Inline styles export** - `getHTML({ styled: true })` produces inline CSS ready for email clients, CMS, and Google Docs
|
|
31
33
|
- **SSR helpers** - `generateHTML`, `generateJSON`, `generateText` for server-side rendering
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lowlightPlugin.ts","../src/CodeBlockLowlight.ts","../src/generateHighlightedHTML.ts","../src/createCodeHighlighter.ts"],"names":["Decoration","DecorationSet","PluginKey","Plugin","CodeBlock","generateHTML","toHtml","codeClass"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Plugin, PluginKey } from '@domternal/pm/state';\nimport type { EditorState, Transaction } from '@domternal/pm/state';\nimport { Decoration, DecorationSet } from '@domternal/pm/view';\nimport type { Node as PMNode } from '@domternal/pm/model';\nimport type { createLowlight } from 'lowlight';\n\n/** The lowlight instance type (return type of createLowlight) */\nexport type Lowlight = ReturnType<typeof createLowlight>;\n\n// Minimal hast types (lowlight output)\ninterface HastText {\n readonly type: 'text';\n readonly value: string;\n}\n\ninterface HastElement {\n readonly type: 'element';\n readonly properties?: { readonly className?: readonly string[] };\n readonly children: readonly HastNode[];\n}\n\ntype HastNode = HastText | HastElement;\n\nexport interface LowlightPluginOptions {\n name: string;\n lowlight: Lowlight | null;\n defaultLanguage: string | null;\n autoDetect: boolean;\n}\n\n/** Internal type after validation — lowlight guaranteed non-null */\ninterface ValidatedOptions extends LowlightPluginOptions {\n lowlight: Lowlight;\n}\n\ninterface Token {\n text: string;\n classes: string[];\n}\n\n/** Flatten hast tree into a list of text tokens with their CSS classes */\nfunction flattenNodes(nodes: readonly HastNode[], classes: string[] = []): Token[] {\n return nodes.flatMap((node): Token[] => {\n if (node.type === 'element') {\n const childClasses = [...classes, ...(node.properties?.className ?? [])];\n return flattenNodes(node.children, childClasses);\n }\n return [{ text: node.value, classes }];\n });\n}\n\n/** Create inline decorations for a single code block */\nfunction decorateCodeBlock(\n node: PMNode,\n pos: number,\n lowlight: Lowlight,\n defaultLanguage: string | null,\n autoDetect: boolean,\n): Decoration[] {\n const language = (node.attrs['language'] as string | null) ?? defaultLanguage;\n const text = node.textContent;\n if (!text) return [];\n\n let result;\n if (language && lowlight.registered(language)) {\n result = lowlight.highlight(language, text);\n } else if (autoDetect) {\n result = lowlight.highlightAuto(text);\n } else {\n return [];\n }\n\n const tokens = flattenNodes(result.children as HastNode[]);\n const decorations: Decoration[] = [];\n let offset = pos + 1; // +1 to skip the node's opening token\n\n for (const token of tokens) {\n if (token.classes.length > 0) {\n decorations.push(\n Decoration.inline(offset, offset + token.text.length, {\n class: token.classes.join(' '),\n }),\n );\n }\n offset += token.text.length;\n }\n\n return decorations;\n}\n\n/** Build decorations for all code blocks in the document */\nfunction decorateAll(doc: PMNode, options: ValidatedOptions): DecorationSet {\n const decorations: Decoration[] = [];\n doc.descendants((node, pos) => {\n if (node.type.name === options.name) {\n decorations.push(\n ...decorateCodeBlock(node, pos, options.lowlight, options.defaultLanguage, options.autoDetect),\n );\n }\n });\n return DecorationSet.create(doc, decorations);\n}\n\nexport const lowlightPluginKey = new PluginKey('lowlight');\n\nexport function lowlightPlugin(options: LowlightPluginOptions): Plugin {\n const { lowlight } = options;\n if (!lowlight) {\n throw new Error(\n '[@domternal/extension-code-block-lowlight] The \"lowlight\" option is required. ' +\n 'Provide a lowlight instance: CodeBlockLowlight.configure({ lowlight: createLowlight(common) })',\n );\n }\n\n // After validation, lowlight is guaranteed non-null\n const validated = { ...options, lowlight };\n\n return new Plugin({\n key: lowlightPluginKey,\n\n state: {\n init(_config: unknown, state: EditorState) {\n return decorateAll(state.doc, validated);\n },\n\n apply(tr: Transaction, decorationSet: DecorationSet, _oldState: EditorState, newState: EditorState) {\n if (!tr.docChanged) return decorationSet;\n\n // Compute changed ranges in final document coordinates\n // Uses oldStart/oldEnd from each step map, mapped through the full\n // remaining mapping to get final positions.\n const changedRanges: { from: number; to: number }[] = [];\n for (let i = 0; i < tr.steps.length; i++) {\n const map = tr.mapping.maps[i];\n if (!map) continue;\n map.forEach((oldStart: number, oldEnd: number) => {\n const from = tr.mapping.slice(i).map(oldStart, -1);\n const to = tr.mapping.slice(i).map(oldEnd, 1);\n changedRanges.push({ from, to });\n });\n }\n\n // Map existing decorations to new positions\n let updated = decorationSet.map(tr.mapping, tr.doc);\n\n // Find code blocks that overlap with changed ranges\n const affected: { node: PMNode; pos: number }[] = [];\n newState.doc.descendants((node, pos) => {\n if (node.type.name !== validated.name) return;\n const end = pos + node.nodeSize;\n if (changedRanges.some((r) => r.from <= end && r.to >= pos)) {\n affected.push({ node, pos });\n }\n });\n\n if (affected.length === 0) return updated;\n\n // Re-highlight only affected code blocks\n for (const block of affected) {\n const end = block.pos + block.node.nodeSize;\n const stale = updated.find(block.pos, end);\n updated = updated.remove(stale);\n\n const fresh = decorateCodeBlock(\n block.node, block.pos,\n validated.lowlight, validated.defaultLanguage, validated.autoDetect,\n );\n updated = updated.add(newState.doc, fresh);\n }\n\n return updated;\n },\n },\n\n props: {\n decorations(state: EditorState) {\n return lowlightPluginKey.getState(state) as DecorationSet | undefined;\n },\n },\n });\n}\n","import { CodeBlock } from '@domternal/core';\nimport type { CodeBlockOptions } from '@domternal/core';\nimport { lowlightPlugin } from './lowlightPlugin.js';\nimport type { Lowlight } from './lowlightPlugin.js';\n\nexport interface CodeBlockLowlightOptions extends CodeBlockOptions {\n /** The lowlight instance (required). Create with createLowlight(). Null before configure(). */\n lowlight: Lowlight | null;\n /** Default language when none is specified. @default null */\n defaultLanguage: string | null;\n /** Auto-detect language when none specified. @default true */\n autoDetect: boolean;\n /** Tab key inserts spaces in code blocks. @default true */\n tabIndentation: boolean;\n /** Number of spaces per tab. @default 2 */\n tabSize: number;\n}\n\nexport interface CodeBlockLowlightStorage {\n /** Returns list of registered language names */\n listLanguages: () => string[];\n}\n\nexport const CodeBlockLowlight = CodeBlock.extend<CodeBlockLowlightOptions, CodeBlockLowlightStorage>({\n addOptions() {\n return {\n ...CodeBlock.options,\n lowlight: null as unknown as Lowlight,\n defaultLanguage: null,\n autoDetect: true,\n tabIndentation: true,\n tabSize: 2,\n };\n },\n\n addStorage() {\n return {\n listLanguages: (): string[] => {\n return this.options.lowlight ? this.options.lowlight.listLanguages() : [];\n },\n };\n },\n\n addKeyboardShortcuts() {\n const parentShortcuts = CodeBlock.config.addKeyboardShortcuts?.call(this) ?? {};\n\n if (!this.options.tabIndentation) return parentShortcuts;\n\n const spaces = ' '.repeat(this.options.tabSize);\n\n return {\n ...parentShortcuts,\n Tab: () => {\n if (!this.editor) return false;\n const { state } = this.editor;\n if (state.selection.$head.parent.type.name !== this.name) return false;\n return this.editor.commands['insertText']?.(spaces) ?? false;\n },\n 'Shift-Tab': () => {\n if (!this.editor) return false;\n const { state } = this.editor;\n const { $head } = state.selection;\n if ($head.parent.type.name !== this.name) return false;\n\n const textBefore = $head.parent.textBetween(0, $head.parentOffset);\n const lastNewline = textBefore.lastIndexOf('\\n');\n const lineStart = lastNewline + 1;\n const lineText = textBefore.slice(lineStart);\n const leadingSpaces = (/^ */).exec(lineText)?.[0]?.length ?? 0;\n const spacesToRemove = Math.min(leadingSpaces, this.options.tabSize);\n\n if (spacesToRemove === 0) return false;\n\n const deleteFrom = $head.start() + lineStart;\n const { tr } = state;\n tr.delete(deleteFrom, deleteFrom + spacesToRemove);\n this.editor.view.dispatch(tr);\n return true;\n },\n };\n },\n\n addProseMirrorPlugins() {\n return [\n ...(CodeBlock.config.addProseMirrorPlugins?.call(this) ?? []),\n lowlightPlugin({\n name: this.name,\n lowlight: this.options.lowlight,\n defaultLanguage: this.options.defaultLanguage,\n autoDetect: this.options.autoDetect,\n }),\n ];\n },\n});\n","import type { AnyExtension, JSONContent, GenerateHTMLOptions } from '@domternal/core';\nimport { generateHTML } from '@domternal/core';\nimport type { Lowlight } from './lowlightPlugin.js';\nimport { toHtml } from 'hast-util-to-html';\n\nexport interface GenerateHighlightedHTMLOptions {\n /** Default language for code blocks without a language. */\n defaultLanguage?: string;\n /** Auto-detect language when none specified. @default false */\n autoDetect?: boolean;\n /** Custom document implementation for generateHTML. */\n document?: Document;\n}\n\n/**\n * Generate HTML with syntax-highlighted code blocks.\n *\n * Unlike generateHTML(), this applies lowlight highlighting to code blocks,\n * producing `<span class=\"hljs-keyword\">` etc. inside `<code>` elements.\n *\n * @example\n * ```ts\n * import { generateHighlightedHTML } from '@domternal/extension-code-block-lowlight';\n * import { createLowlight, common } from 'lowlight';\n *\n * const lowlight = createLowlight(common);\n * const html = generateHighlightedHTML(json, extensions, lowlight);\n * ```\n */\nexport function generateHighlightedHTML(\n content: JSONContent,\n extensions: AnyExtension[],\n lowlight: Lowlight,\n options: GenerateHighlightedHTMLOptions = {},\n): string {\n const htmlOptions: GenerateHTMLOptions = {};\n if (options.document !== undefined) {\n htmlOptions.document = options.document;\n }\n\n const html = generateHTML(content, extensions, htmlOptions);\n\n return html.replace(\n /<pre([^>]*)><code(?:\\s+class=\"language-([^\"]*)\")?>([\\s\\S]*?)<\\/code><\\/pre>/g,\n (_match, preAttrs: string, language: string | undefined, code: string) => {\n // Unescape HTML entities in code content\n const decoded = code\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n\n const lang = language ?? options.defaultLanguage ?? null;\n let highlighted: string;\n\n if (lang && lowlight.registered(lang)) {\n const result = lowlight.highlight(lang, decoded);\n highlighted = toHtml(result);\n } else if (options.autoDetect && decoded.length > 0) {\n const result = lowlight.highlightAuto(decoded);\n highlighted = toHtml(result);\n } else {\n const codeClass = language ? ` class=\"language-${language}\"` : '';\n return `<pre${preAttrs}><code${codeClass}>${code}</code></pre>`;\n }\n\n const codeClass = language ? ` class=\"language-${language}\"` : '';\n return `<pre${preAttrs}><code${codeClass}>${highlighted}</code></pre>`;\n },\n );\n}\n","import type { Lowlight } from './lowlightPlugin.js';\nimport { toHtml } from 'hast-util-to-html';\n\nexport interface CreateCodeHighlighterOptions {\n /** Default language when none specified on the code block. */\n defaultLanguage?: string;\n /** Auto-detect language when none specified. @default false */\n autoDetect?: boolean;\n}\n\n/**\n * Creates a `codeHighlighter` callback for use with `inlineStyles()`.\n *\n * @example\n * ```ts\n * import { createLowlight, common } from 'lowlight';\n * import { createCodeHighlighter } from '@domternal/extension-code-block-lowlight';\n * import { inlineStyles } from '@domternal/core';\n *\n * const lowlight = createLowlight(common);\n * const styled = inlineStyles(html, {\n * codeHighlighter: createCodeHighlighter(lowlight),\n * });\n * ```\n */\nexport function createCodeHighlighter(\n lowlight: Lowlight,\n options: CreateCodeHighlighterOptions = {},\n): (code: string, language: string | null) => string | null {\n return (code: string, language: string | null): string | null => {\n const lang = language ?? options.defaultLanguage ?? null;\n\n if (lang && lowlight.registered(lang)) {\n return toHtml(lowlight.highlight(lang, code));\n }\n\n if (options.autoDetect && code.length > 0) {\n return toHtml(lowlight.highlightAuto(code));\n }\n\n return null;\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lowlightPlugin.ts","../src/CodeBlockLowlight.ts","../src/generateHighlightedHTML.ts","../src/createCodeHighlighter.ts"],"names":["Decoration","DecorationSet","PluginKey","Plugin","CodeBlock","generateHTML","toHtml","codeClass"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Plugin, PluginKey } from '@domternal/pm/state';\nimport type { EditorState, Transaction } from '@domternal/pm/state';\nimport { Decoration, DecorationSet } from '@domternal/pm/view';\nimport type { Node as PMNode } from '@domternal/pm/model';\nimport type { createLowlight } from 'lowlight';\n\n/** The lowlight instance type (return type of createLowlight) */\nexport type Lowlight = ReturnType<typeof createLowlight>;\n\n// Minimal hast types (lowlight output)\ninterface HastText {\n readonly type: 'text';\n readonly value: string;\n}\n\ninterface HastElement {\n readonly type: 'element';\n readonly properties?: { readonly className?: readonly string[] };\n readonly children: readonly HastNode[];\n}\n\ntype HastNode = HastText | HastElement;\n\nexport interface LowlightPluginOptions {\n name: string;\n lowlight: Lowlight | null;\n defaultLanguage: string | null;\n autoDetect: boolean;\n}\n\n/** Internal type after validation - lowlight guaranteed non-null */\ninterface ValidatedOptions extends LowlightPluginOptions {\n lowlight: Lowlight;\n}\n\ninterface Token {\n text: string;\n classes: string[];\n}\n\n/** Flatten hast tree into a list of text tokens with their CSS classes */\nfunction flattenNodes(nodes: readonly HastNode[], classes: string[] = []): Token[] {\n return nodes.flatMap((node): Token[] => {\n if (node.type === 'element') {\n const childClasses = [...classes, ...(node.properties?.className ?? [])];\n return flattenNodes(node.children, childClasses);\n }\n return [{ text: node.value, classes }];\n });\n}\n\n/** Create inline decorations for a single code block */\nfunction decorateCodeBlock(\n node: PMNode,\n pos: number,\n lowlight: Lowlight,\n defaultLanguage: string | null,\n autoDetect: boolean,\n): Decoration[] {\n const language = (node.attrs['language'] as string | null) ?? defaultLanguage;\n const text = node.textContent;\n if (!text) return [];\n\n let result;\n if (language && lowlight.registered(language)) {\n result = lowlight.highlight(language, text);\n } else if (autoDetect) {\n result = lowlight.highlightAuto(text);\n } else {\n return [];\n }\n\n const tokens = flattenNodes(result.children as HastNode[]);\n const decorations: Decoration[] = [];\n let offset = pos + 1; // +1 to skip the node's opening token\n\n for (const token of tokens) {\n if (token.classes.length > 0) {\n decorations.push(\n Decoration.inline(offset, offset + token.text.length, {\n class: token.classes.join(' '),\n }),\n );\n }\n offset += token.text.length;\n }\n\n return decorations;\n}\n\n/** Build decorations for all code blocks in the document */\nfunction decorateAll(doc: PMNode, options: ValidatedOptions): DecorationSet {\n const decorations: Decoration[] = [];\n doc.descendants((node, pos) => {\n if (node.type.name === options.name) {\n decorations.push(\n ...decorateCodeBlock(node, pos, options.lowlight, options.defaultLanguage, options.autoDetect),\n );\n }\n });\n return DecorationSet.create(doc, decorations);\n}\n\nexport const lowlightPluginKey = new PluginKey('lowlight');\n\nexport function lowlightPlugin(options: LowlightPluginOptions): Plugin {\n const { lowlight } = options;\n if (!lowlight) {\n throw new Error(\n '[@domternal/extension-code-block-lowlight] The \"lowlight\" option is required. ' +\n 'Provide a lowlight instance: CodeBlockLowlight.configure({ lowlight: createLowlight(common) })',\n );\n }\n\n // After validation, lowlight is guaranteed non-null\n const validated = { ...options, lowlight };\n\n return new Plugin({\n key: lowlightPluginKey,\n\n state: {\n init(_config: unknown, state: EditorState) {\n return decorateAll(state.doc, validated);\n },\n\n apply(tr: Transaction, decorationSet: DecorationSet, _oldState: EditorState, newState: EditorState) {\n if (!tr.docChanged) return decorationSet;\n\n // Compute changed ranges in final document coordinates\n // Uses oldStart/oldEnd from each step map, mapped through the full\n // remaining mapping to get final positions.\n const changedRanges: { from: number; to: number }[] = [];\n for (let i = 0; i < tr.steps.length; i++) {\n const map = tr.mapping.maps[i];\n if (!map) continue;\n map.forEach((oldStart: number, oldEnd: number) => {\n const from = tr.mapping.slice(i).map(oldStart, -1);\n const to = tr.mapping.slice(i).map(oldEnd, 1);\n changedRanges.push({ from, to });\n });\n }\n\n // Map existing decorations to new positions\n let updated = decorationSet.map(tr.mapping, tr.doc);\n\n // Find code blocks that overlap with changed ranges\n const affected: { node: PMNode; pos: number }[] = [];\n newState.doc.descendants((node, pos) => {\n if (node.type.name !== validated.name) return;\n const end = pos + node.nodeSize;\n if (changedRanges.some((r) => r.from <= end && r.to >= pos)) {\n affected.push({ node, pos });\n }\n });\n\n if (affected.length === 0) return updated;\n\n // Re-highlight only affected code blocks\n for (const block of affected) {\n const end = block.pos + block.node.nodeSize;\n const stale = updated.find(block.pos, end);\n updated = updated.remove(stale);\n\n const fresh = decorateCodeBlock(\n block.node, block.pos,\n validated.lowlight, validated.defaultLanguage, validated.autoDetect,\n );\n updated = updated.add(newState.doc, fresh);\n }\n\n return updated;\n },\n },\n\n props: {\n decorations(state: EditorState) {\n return lowlightPluginKey.getState(state) as DecorationSet | undefined;\n },\n },\n });\n}\n","import { CodeBlock } from '@domternal/core';\nimport type { CodeBlockOptions } from '@domternal/core';\nimport { lowlightPlugin } from './lowlightPlugin.js';\nimport type { Lowlight } from './lowlightPlugin.js';\n\nexport interface CodeBlockLowlightOptions extends CodeBlockOptions {\n /** The lowlight instance (required). Create with createLowlight(). Null before configure(). */\n lowlight: Lowlight | null;\n /** Default language when none is specified. @default null */\n defaultLanguage: string | null;\n /** Auto-detect language when none specified. @default true */\n autoDetect: boolean;\n /** Tab key inserts spaces in code blocks. @default true */\n tabIndentation: boolean;\n /** Number of spaces per tab. @default 2 */\n tabSize: number;\n}\n\nexport interface CodeBlockLowlightStorage {\n /** Returns list of registered language names */\n listLanguages: () => string[];\n}\n\nexport const CodeBlockLowlight = CodeBlock.extend<CodeBlockLowlightOptions, CodeBlockLowlightStorage>({\n addOptions() {\n return {\n ...CodeBlock.options,\n lowlight: null as unknown as Lowlight,\n defaultLanguage: null,\n autoDetect: true,\n tabIndentation: true,\n tabSize: 2,\n };\n },\n\n addStorage() {\n return {\n listLanguages: (): string[] => {\n return this.options.lowlight ? this.options.lowlight.listLanguages() : [];\n },\n };\n },\n\n addKeyboardShortcuts() {\n const parentShortcuts = CodeBlock.config.addKeyboardShortcuts?.call(this) ?? {};\n\n if (!this.options.tabIndentation) return parentShortcuts;\n\n const spaces = ' '.repeat(this.options.tabSize);\n\n return {\n ...parentShortcuts,\n Tab: () => {\n if (!this.editor) return false;\n const { state } = this.editor;\n if (state.selection.$head.parent.type.name !== this.name) return false;\n return this.editor.commands['insertText']?.(spaces) ?? false;\n },\n 'Shift-Tab': () => {\n if (!this.editor) return false;\n const { state } = this.editor;\n const { $head } = state.selection;\n if ($head.parent.type.name !== this.name) return false;\n\n const textBefore = $head.parent.textBetween(0, $head.parentOffset);\n const lastNewline = textBefore.lastIndexOf('\\n');\n const lineStart = lastNewline + 1;\n const lineText = textBefore.slice(lineStart);\n const leadingSpaces = (/^ */).exec(lineText)?.[0]?.length ?? 0;\n const spacesToRemove = Math.min(leadingSpaces, this.options.tabSize);\n\n if (spacesToRemove === 0) return false;\n\n const deleteFrom = $head.start() + lineStart;\n const { tr } = state;\n tr.delete(deleteFrom, deleteFrom + spacesToRemove);\n this.editor.view.dispatch(tr);\n return true;\n },\n };\n },\n\n addProseMirrorPlugins() {\n return [\n ...(CodeBlock.config.addProseMirrorPlugins?.call(this) ?? []),\n lowlightPlugin({\n name: this.name,\n lowlight: this.options.lowlight,\n defaultLanguage: this.options.defaultLanguage,\n autoDetect: this.options.autoDetect,\n }),\n ];\n },\n});\n","import type { AnyExtension, JSONContent, GenerateHTMLOptions } from '@domternal/core';\nimport { generateHTML } from '@domternal/core';\nimport type { Lowlight } from './lowlightPlugin.js';\nimport { toHtml } from 'hast-util-to-html';\n\nexport interface GenerateHighlightedHTMLOptions {\n /** Default language for code blocks without a language. */\n defaultLanguage?: string;\n /** Auto-detect language when none specified. @default false */\n autoDetect?: boolean;\n /** Custom document implementation for generateHTML. */\n document?: Document;\n}\n\n/**\n * Generate HTML with syntax-highlighted code blocks.\n *\n * Unlike generateHTML(), this applies lowlight highlighting to code blocks,\n * producing `<span class=\"hljs-keyword\">` etc. inside `<code>` elements.\n *\n * @example\n * ```ts\n * import { generateHighlightedHTML } from '@domternal/extension-code-block-lowlight';\n * import { createLowlight, common } from 'lowlight';\n *\n * const lowlight = createLowlight(common);\n * const html = generateHighlightedHTML(json, extensions, lowlight);\n * ```\n */\nexport function generateHighlightedHTML(\n content: JSONContent,\n extensions: AnyExtension[],\n lowlight: Lowlight,\n options: GenerateHighlightedHTMLOptions = {},\n): string {\n const htmlOptions: GenerateHTMLOptions = {};\n if (options.document !== undefined) {\n htmlOptions.document = options.document;\n }\n\n const html = generateHTML(content, extensions, htmlOptions);\n\n return html.replace(\n /<pre([^>]*)><code(?:\\s+class=\"language-([^\"]*)\")?>([\\s\\S]*?)<\\/code><\\/pre>/g,\n (_match, preAttrs: string, language: string | undefined, code: string) => {\n // Unescape HTML entities in code content\n const decoded = code\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n\n const lang = language ?? options.defaultLanguage ?? null;\n let highlighted: string;\n\n if (lang && lowlight.registered(lang)) {\n const result = lowlight.highlight(lang, decoded);\n highlighted = toHtml(result);\n } else if (options.autoDetect && decoded.length > 0) {\n const result = lowlight.highlightAuto(decoded);\n highlighted = toHtml(result);\n } else {\n const codeClass = language ? ` class=\"language-${language}\"` : '';\n return `<pre${preAttrs}><code${codeClass}>${code}</code></pre>`;\n }\n\n const codeClass = language ? ` class=\"language-${language}\"` : '';\n return `<pre${preAttrs}><code${codeClass}>${highlighted}</code></pre>`;\n },\n );\n}\n","import type { Lowlight } from './lowlightPlugin.js';\nimport { toHtml } from 'hast-util-to-html';\n\nexport interface CreateCodeHighlighterOptions {\n /** Default language when none specified on the code block. */\n defaultLanguage?: string;\n /** Auto-detect language when none specified. @default false */\n autoDetect?: boolean;\n}\n\n/**\n * Creates a `codeHighlighter` callback for use with `inlineStyles()`.\n *\n * @example\n * ```ts\n * import { createLowlight, common } from 'lowlight';\n * import { createCodeHighlighter } from '@domternal/extension-code-block-lowlight';\n * import { inlineStyles } from '@domternal/core';\n *\n * const lowlight = createLowlight(common);\n * const styled = inlineStyles(html, {\n * codeHighlighter: createCodeHighlighter(lowlight),\n * });\n * ```\n */\nexport function createCodeHighlighter(\n lowlight: Lowlight,\n options: CreateCodeHighlighterOptions = {},\n): (code: string, language: string | null) => string | null {\n return (code: string, language: string | null): string | null => {\n const lang = language ?? options.defaultLanguage ?? null;\n\n if (lang && lowlight.registered(lang)) {\n return toHtml(lowlight.highlight(lang, code));\n }\n\n if (options.autoDetect && code.length > 0) {\n return toHtml(lowlight.highlightAuto(code));\n }\n\n return null;\n };\n}\n"]}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lowlightPlugin.ts","../src/CodeBlockLowlight.ts","../src/generateHighlightedHTML.ts","../src/createCodeHighlighter.ts"],"names":["codeClass","toHtml"],"mappings":"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","file":"index.js","sourcesContent":["import { Plugin, PluginKey } from '@domternal/pm/state';\nimport type { EditorState, Transaction } from '@domternal/pm/state';\nimport { Decoration, DecorationSet } from '@domternal/pm/view';\nimport type { Node as PMNode } from '@domternal/pm/model';\nimport type { createLowlight } from 'lowlight';\n\n/** The lowlight instance type (return type of createLowlight) */\nexport type Lowlight = ReturnType<typeof createLowlight>;\n\n// Minimal hast types (lowlight output)\ninterface HastText {\n readonly type: 'text';\n readonly value: string;\n}\n\ninterface HastElement {\n readonly type: 'element';\n readonly properties?: { readonly className?: readonly string[] };\n readonly children: readonly HastNode[];\n}\n\ntype HastNode = HastText | HastElement;\n\nexport interface LowlightPluginOptions {\n name: string;\n lowlight: Lowlight | null;\n defaultLanguage: string | null;\n autoDetect: boolean;\n}\n\n/** Internal type after validation — lowlight guaranteed non-null */\ninterface ValidatedOptions extends LowlightPluginOptions {\n lowlight: Lowlight;\n}\n\ninterface Token {\n text: string;\n classes: string[];\n}\n\n/** Flatten hast tree into a list of text tokens with their CSS classes */\nfunction flattenNodes(nodes: readonly HastNode[], classes: string[] = []): Token[] {\n return nodes.flatMap((node): Token[] => {\n if (node.type === 'element') {\n const childClasses = [...classes, ...(node.properties?.className ?? [])];\n return flattenNodes(node.children, childClasses);\n }\n return [{ text: node.value, classes }];\n });\n}\n\n/** Create inline decorations for a single code block */\nfunction decorateCodeBlock(\n node: PMNode,\n pos: number,\n lowlight: Lowlight,\n defaultLanguage: string | null,\n autoDetect: boolean,\n): Decoration[] {\n const language = (node.attrs['language'] as string | null) ?? defaultLanguage;\n const text = node.textContent;\n if (!text) return [];\n\n let result;\n if (language && lowlight.registered(language)) {\n result = lowlight.highlight(language, text);\n } else if (autoDetect) {\n result = lowlight.highlightAuto(text);\n } else {\n return [];\n }\n\n const tokens = flattenNodes(result.children as HastNode[]);\n const decorations: Decoration[] = [];\n let offset = pos + 1; // +1 to skip the node's opening token\n\n for (const token of tokens) {\n if (token.classes.length > 0) {\n decorations.push(\n Decoration.inline(offset, offset + token.text.length, {\n class: token.classes.join(' '),\n }),\n );\n }\n offset += token.text.length;\n }\n\n return decorations;\n}\n\n/** Build decorations for all code blocks in the document */\nfunction decorateAll(doc: PMNode, options: ValidatedOptions): DecorationSet {\n const decorations: Decoration[] = [];\n doc.descendants((node, pos) => {\n if (node.type.name === options.name) {\n decorations.push(\n ...decorateCodeBlock(node, pos, options.lowlight, options.defaultLanguage, options.autoDetect),\n );\n }\n });\n return DecorationSet.create(doc, decorations);\n}\n\nexport const lowlightPluginKey = new PluginKey('lowlight');\n\nexport function lowlightPlugin(options: LowlightPluginOptions): Plugin {\n const { lowlight } = options;\n if (!lowlight) {\n throw new Error(\n '[@domternal/extension-code-block-lowlight] The \"lowlight\" option is required. ' +\n 'Provide a lowlight instance: CodeBlockLowlight.configure({ lowlight: createLowlight(common) })',\n );\n }\n\n // After validation, lowlight is guaranteed non-null\n const validated = { ...options, lowlight };\n\n return new Plugin({\n key: lowlightPluginKey,\n\n state: {\n init(_config: unknown, state: EditorState) {\n return decorateAll(state.doc, validated);\n },\n\n apply(tr: Transaction, decorationSet: DecorationSet, _oldState: EditorState, newState: EditorState) {\n if (!tr.docChanged) return decorationSet;\n\n // Compute changed ranges in final document coordinates\n // Uses oldStart/oldEnd from each step map, mapped through the full\n // remaining mapping to get final positions.\n const changedRanges: { from: number; to: number }[] = [];\n for (let i = 0; i < tr.steps.length; i++) {\n const map = tr.mapping.maps[i];\n if (!map) continue;\n map.forEach((oldStart: number, oldEnd: number) => {\n const from = tr.mapping.slice(i).map(oldStart, -1);\n const to = tr.mapping.slice(i).map(oldEnd, 1);\n changedRanges.push({ from, to });\n });\n }\n\n // Map existing decorations to new positions\n let updated = decorationSet.map(tr.mapping, tr.doc);\n\n // Find code blocks that overlap with changed ranges\n const affected: { node: PMNode; pos: number }[] = [];\n newState.doc.descendants((node, pos) => {\n if (node.type.name !== validated.name) return;\n const end = pos + node.nodeSize;\n if (changedRanges.some((r) => r.from <= end && r.to >= pos)) {\n affected.push({ node, pos });\n }\n });\n\n if (affected.length === 0) return updated;\n\n // Re-highlight only affected code blocks\n for (const block of affected) {\n const end = block.pos + block.node.nodeSize;\n const stale = updated.find(block.pos, end);\n updated = updated.remove(stale);\n\n const fresh = decorateCodeBlock(\n block.node, block.pos,\n validated.lowlight, validated.defaultLanguage, validated.autoDetect,\n );\n updated = updated.add(newState.doc, fresh);\n }\n\n return updated;\n },\n },\n\n props: {\n decorations(state: EditorState) {\n return lowlightPluginKey.getState(state) as DecorationSet | undefined;\n },\n },\n });\n}\n","import { CodeBlock } from '@domternal/core';\nimport type { CodeBlockOptions } from '@domternal/core';\nimport { lowlightPlugin } from './lowlightPlugin.js';\nimport type { Lowlight } from './lowlightPlugin.js';\n\nexport interface CodeBlockLowlightOptions extends CodeBlockOptions {\n /** The lowlight instance (required). Create with createLowlight(). Null before configure(). */\n lowlight: Lowlight | null;\n /** Default language when none is specified. @default null */\n defaultLanguage: string | null;\n /** Auto-detect language when none specified. @default true */\n autoDetect: boolean;\n /** Tab key inserts spaces in code blocks. @default true */\n tabIndentation: boolean;\n /** Number of spaces per tab. @default 2 */\n tabSize: number;\n}\n\nexport interface CodeBlockLowlightStorage {\n /** Returns list of registered language names */\n listLanguages: () => string[];\n}\n\nexport const CodeBlockLowlight = CodeBlock.extend<CodeBlockLowlightOptions, CodeBlockLowlightStorage>({\n addOptions() {\n return {\n ...CodeBlock.options,\n lowlight: null as unknown as Lowlight,\n defaultLanguage: null,\n autoDetect: true,\n tabIndentation: true,\n tabSize: 2,\n };\n },\n\n addStorage() {\n return {\n listLanguages: (): string[] => {\n return this.options.lowlight ? this.options.lowlight.listLanguages() : [];\n },\n };\n },\n\n addKeyboardShortcuts() {\n const parentShortcuts = CodeBlock.config.addKeyboardShortcuts?.call(this) ?? {};\n\n if (!this.options.tabIndentation) return parentShortcuts;\n\n const spaces = ' '.repeat(this.options.tabSize);\n\n return {\n ...parentShortcuts,\n Tab: () => {\n if (!this.editor) return false;\n const { state } = this.editor;\n if (state.selection.$head.parent.type.name !== this.name) return false;\n return this.editor.commands['insertText']?.(spaces) ?? false;\n },\n 'Shift-Tab': () => {\n if (!this.editor) return false;\n const { state } = this.editor;\n const { $head } = state.selection;\n if ($head.parent.type.name !== this.name) return false;\n\n const textBefore = $head.parent.textBetween(0, $head.parentOffset);\n const lastNewline = textBefore.lastIndexOf('\\n');\n const lineStart = lastNewline + 1;\n const lineText = textBefore.slice(lineStart);\n const leadingSpaces = (/^ */).exec(lineText)?.[0]?.length ?? 0;\n const spacesToRemove = Math.min(leadingSpaces, this.options.tabSize);\n\n if (spacesToRemove === 0) return false;\n\n const deleteFrom = $head.start() + lineStart;\n const { tr } = state;\n tr.delete(deleteFrom, deleteFrom + spacesToRemove);\n this.editor.view.dispatch(tr);\n return true;\n },\n };\n },\n\n addProseMirrorPlugins() {\n return [\n ...(CodeBlock.config.addProseMirrorPlugins?.call(this) ?? []),\n lowlightPlugin({\n name: this.name,\n lowlight: this.options.lowlight,\n defaultLanguage: this.options.defaultLanguage,\n autoDetect: this.options.autoDetect,\n }),\n ];\n },\n});\n","import type { AnyExtension, JSONContent, GenerateHTMLOptions } from '@domternal/core';\nimport { generateHTML } from '@domternal/core';\nimport type { Lowlight } from './lowlightPlugin.js';\nimport { toHtml } from 'hast-util-to-html';\n\nexport interface GenerateHighlightedHTMLOptions {\n /** Default language for code blocks without a language. */\n defaultLanguage?: string;\n /** Auto-detect language when none specified. @default false */\n autoDetect?: boolean;\n /** Custom document implementation for generateHTML. */\n document?: Document;\n}\n\n/**\n * Generate HTML with syntax-highlighted code blocks.\n *\n * Unlike generateHTML(), this applies lowlight highlighting to code blocks,\n * producing `<span class=\"hljs-keyword\">` etc. inside `<code>` elements.\n *\n * @example\n * ```ts\n * import { generateHighlightedHTML } from '@domternal/extension-code-block-lowlight';\n * import { createLowlight, common } from 'lowlight';\n *\n * const lowlight = createLowlight(common);\n * const html = generateHighlightedHTML(json, extensions, lowlight);\n * ```\n */\nexport function generateHighlightedHTML(\n content: JSONContent,\n extensions: AnyExtension[],\n lowlight: Lowlight,\n options: GenerateHighlightedHTMLOptions = {},\n): string {\n const htmlOptions: GenerateHTMLOptions = {};\n if (options.document !== undefined) {\n htmlOptions.document = options.document;\n }\n\n const html = generateHTML(content, extensions, htmlOptions);\n\n return html.replace(\n /<pre([^>]*)><code(?:\\s+class=\"language-([^\"]*)\")?>([\\s\\S]*?)<\\/code><\\/pre>/g,\n (_match, preAttrs: string, language: string | undefined, code: string) => {\n // Unescape HTML entities in code content\n const decoded = code\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n\n const lang = language ?? options.defaultLanguage ?? null;\n let highlighted: string;\n\n if (lang && lowlight.registered(lang)) {\n const result = lowlight.highlight(lang, decoded);\n highlighted = toHtml(result);\n } else if (options.autoDetect && decoded.length > 0) {\n const result = lowlight.highlightAuto(decoded);\n highlighted = toHtml(result);\n } else {\n const codeClass = language ? ` class=\"language-${language}\"` : '';\n return `<pre${preAttrs}><code${codeClass}>${code}</code></pre>`;\n }\n\n const codeClass = language ? ` class=\"language-${language}\"` : '';\n return `<pre${preAttrs}><code${codeClass}>${highlighted}</code></pre>`;\n },\n );\n}\n","import type { Lowlight } from './lowlightPlugin.js';\nimport { toHtml } from 'hast-util-to-html';\n\nexport interface CreateCodeHighlighterOptions {\n /** Default language when none specified on the code block. */\n defaultLanguage?: string;\n /** Auto-detect language when none specified. @default false */\n autoDetect?: boolean;\n}\n\n/**\n * Creates a `codeHighlighter` callback for use with `inlineStyles()`.\n *\n * @example\n * ```ts\n * import { createLowlight, common } from 'lowlight';\n * import { createCodeHighlighter } from '@domternal/extension-code-block-lowlight';\n * import { inlineStyles } from '@domternal/core';\n *\n * const lowlight = createLowlight(common);\n * const styled = inlineStyles(html, {\n * codeHighlighter: createCodeHighlighter(lowlight),\n * });\n * ```\n */\nexport function createCodeHighlighter(\n lowlight: Lowlight,\n options: CreateCodeHighlighterOptions = {},\n): (code: string, language: string | null) => string | null {\n return (code: string, language: string | null): string | null => {\n const lang = language ?? options.defaultLanguage ?? null;\n\n if (lang && lowlight.registered(lang)) {\n return toHtml(lowlight.highlight(lang, code));\n }\n\n if (options.autoDetect && code.length > 0) {\n return toHtml(lowlight.highlightAuto(code));\n }\n\n return null;\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lowlightPlugin.ts","../src/CodeBlockLowlight.ts","../src/generateHighlightedHTML.ts","../src/createCodeHighlighter.ts"],"names":["codeClass","toHtml"],"mappings":"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","file":"index.js","sourcesContent":["import { Plugin, PluginKey } from '@domternal/pm/state';\nimport type { EditorState, Transaction } from '@domternal/pm/state';\nimport { Decoration, DecorationSet } from '@domternal/pm/view';\nimport type { Node as PMNode } from '@domternal/pm/model';\nimport type { createLowlight } from 'lowlight';\n\n/** The lowlight instance type (return type of createLowlight) */\nexport type Lowlight = ReturnType<typeof createLowlight>;\n\n// Minimal hast types (lowlight output)\ninterface HastText {\n readonly type: 'text';\n readonly value: string;\n}\n\ninterface HastElement {\n readonly type: 'element';\n readonly properties?: { readonly className?: readonly string[] };\n readonly children: readonly HastNode[];\n}\n\ntype HastNode = HastText | HastElement;\n\nexport interface LowlightPluginOptions {\n name: string;\n lowlight: Lowlight | null;\n defaultLanguage: string | null;\n autoDetect: boolean;\n}\n\n/** Internal type after validation - lowlight guaranteed non-null */\ninterface ValidatedOptions extends LowlightPluginOptions {\n lowlight: Lowlight;\n}\n\ninterface Token {\n text: string;\n classes: string[];\n}\n\n/** Flatten hast tree into a list of text tokens with their CSS classes */\nfunction flattenNodes(nodes: readonly HastNode[], classes: string[] = []): Token[] {\n return nodes.flatMap((node): Token[] => {\n if (node.type === 'element') {\n const childClasses = [...classes, ...(node.properties?.className ?? [])];\n return flattenNodes(node.children, childClasses);\n }\n return [{ text: node.value, classes }];\n });\n}\n\n/** Create inline decorations for a single code block */\nfunction decorateCodeBlock(\n node: PMNode,\n pos: number,\n lowlight: Lowlight,\n defaultLanguage: string | null,\n autoDetect: boolean,\n): Decoration[] {\n const language = (node.attrs['language'] as string | null) ?? defaultLanguage;\n const text = node.textContent;\n if (!text) return [];\n\n let result;\n if (language && lowlight.registered(language)) {\n result = lowlight.highlight(language, text);\n } else if (autoDetect) {\n result = lowlight.highlightAuto(text);\n } else {\n return [];\n }\n\n const tokens = flattenNodes(result.children as HastNode[]);\n const decorations: Decoration[] = [];\n let offset = pos + 1; // +1 to skip the node's opening token\n\n for (const token of tokens) {\n if (token.classes.length > 0) {\n decorations.push(\n Decoration.inline(offset, offset + token.text.length, {\n class: token.classes.join(' '),\n }),\n );\n }\n offset += token.text.length;\n }\n\n return decorations;\n}\n\n/** Build decorations for all code blocks in the document */\nfunction decorateAll(doc: PMNode, options: ValidatedOptions): DecorationSet {\n const decorations: Decoration[] = [];\n doc.descendants((node, pos) => {\n if (node.type.name === options.name) {\n decorations.push(\n ...decorateCodeBlock(node, pos, options.lowlight, options.defaultLanguage, options.autoDetect),\n );\n }\n });\n return DecorationSet.create(doc, decorations);\n}\n\nexport const lowlightPluginKey = new PluginKey('lowlight');\n\nexport function lowlightPlugin(options: LowlightPluginOptions): Plugin {\n const { lowlight } = options;\n if (!lowlight) {\n throw new Error(\n '[@domternal/extension-code-block-lowlight] The \"lowlight\" option is required. ' +\n 'Provide a lowlight instance: CodeBlockLowlight.configure({ lowlight: createLowlight(common) })',\n );\n }\n\n // After validation, lowlight is guaranteed non-null\n const validated = { ...options, lowlight };\n\n return new Plugin({\n key: lowlightPluginKey,\n\n state: {\n init(_config: unknown, state: EditorState) {\n return decorateAll(state.doc, validated);\n },\n\n apply(tr: Transaction, decorationSet: DecorationSet, _oldState: EditorState, newState: EditorState) {\n if (!tr.docChanged) return decorationSet;\n\n // Compute changed ranges in final document coordinates\n // Uses oldStart/oldEnd from each step map, mapped through the full\n // remaining mapping to get final positions.\n const changedRanges: { from: number; to: number }[] = [];\n for (let i = 0; i < tr.steps.length; i++) {\n const map = tr.mapping.maps[i];\n if (!map) continue;\n map.forEach((oldStart: number, oldEnd: number) => {\n const from = tr.mapping.slice(i).map(oldStart, -1);\n const to = tr.mapping.slice(i).map(oldEnd, 1);\n changedRanges.push({ from, to });\n });\n }\n\n // Map existing decorations to new positions\n let updated = decorationSet.map(tr.mapping, tr.doc);\n\n // Find code blocks that overlap with changed ranges\n const affected: { node: PMNode; pos: number }[] = [];\n newState.doc.descendants((node, pos) => {\n if (node.type.name !== validated.name) return;\n const end = pos + node.nodeSize;\n if (changedRanges.some((r) => r.from <= end && r.to >= pos)) {\n affected.push({ node, pos });\n }\n });\n\n if (affected.length === 0) return updated;\n\n // Re-highlight only affected code blocks\n for (const block of affected) {\n const end = block.pos + block.node.nodeSize;\n const stale = updated.find(block.pos, end);\n updated = updated.remove(stale);\n\n const fresh = decorateCodeBlock(\n block.node, block.pos,\n validated.lowlight, validated.defaultLanguage, validated.autoDetect,\n );\n updated = updated.add(newState.doc, fresh);\n }\n\n return updated;\n },\n },\n\n props: {\n decorations(state: EditorState) {\n return lowlightPluginKey.getState(state) as DecorationSet | undefined;\n },\n },\n });\n}\n","import { CodeBlock } from '@domternal/core';\nimport type { CodeBlockOptions } from '@domternal/core';\nimport { lowlightPlugin } from './lowlightPlugin.js';\nimport type { Lowlight } from './lowlightPlugin.js';\n\nexport interface CodeBlockLowlightOptions extends CodeBlockOptions {\n /** The lowlight instance (required). Create with createLowlight(). Null before configure(). */\n lowlight: Lowlight | null;\n /** Default language when none is specified. @default null */\n defaultLanguage: string | null;\n /** Auto-detect language when none specified. @default true */\n autoDetect: boolean;\n /** Tab key inserts spaces in code blocks. @default true */\n tabIndentation: boolean;\n /** Number of spaces per tab. @default 2 */\n tabSize: number;\n}\n\nexport interface CodeBlockLowlightStorage {\n /** Returns list of registered language names */\n listLanguages: () => string[];\n}\n\nexport const CodeBlockLowlight = CodeBlock.extend<CodeBlockLowlightOptions, CodeBlockLowlightStorage>({\n addOptions() {\n return {\n ...CodeBlock.options,\n lowlight: null as unknown as Lowlight,\n defaultLanguage: null,\n autoDetect: true,\n tabIndentation: true,\n tabSize: 2,\n };\n },\n\n addStorage() {\n return {\n listLanguages: (): string[] => {\n return this.options.lowlight ? this.options.lowlight.listLanguages() : [];\n },\n };\n },\n\n addKeyboardShortcuts() {\n const parentShortcuts = CodeBlock.config.addKeyboardShortcuts?.call(this) ?? {};\n\n if (!this.options.tabIndentation) return parentShortcuts;\n\n const spaces = ' '.repeat(this.options.tabSize);\n\n return {\n ...parentShortcuts,\n Tab: () => {\n if (!this.editor) return false;\n const { state } = this.editor;\n if (state.selection.$head.parent.type.name !== this.name) return false;\n return this.editor.commands['insertText']?.(spaces) ?? false;\n },\n 'Shift-Tab': () => {\n if (!this.editor) return false;\n const { state } = this.editor;\n const { $head } = state.selection;\n if ($head.parent.type.name !== this.name) return false;\n\n const textBefore = $head.parent.textBetween(0, $head.parentOffset);\n const lastNewline = textBefore.lastIndexOf('\\n');\n const lineStart = lastNewline + 1;\n const lineText = textBefore.slice(lineStart);\n const leadingSpaces = (/^ */).exec(lineText)?.[0]?.length ?? 0;\n const spacesToRemove = Math.min(leadingSpaces, this.options.tabSize);\n\n if (spacesToRemove === 0) return false;\n\n const deleteFrom = $head.start() + lineStart;\n const { tr } = state;\n tr.delete(deleteFrom, deleteFrom + spacesToRemove);\n this.editor.view.dispatch(tr);\n return true;\n },\n };\n },\n\n addProseMirrorPlugins() {\n return [\n ...(CodeBlock.config.addProseMirrorPlugins?.call(this) ?? []),\n lowlightPlugin({\n name: this.name,\n lowlight: this.options.lowlight,\n defaultLanguage: this.options.defaultLanguage,\n autoDetect: this.options.autoDetect,\n }),\n ];\n },\n});\n","import type { AnyExtension, JSONContent, GenerateHTMLOptions } from '@domternal/core';\nimport { generateHTML } from '@domternal/core';\nimport type { Lowlight } from './lowlightPlugin.js';\nimport { toHtml } from 'hast-util-to-html';\n\nexport interface GenerateHighlightedHTMLOptions {\n /** Default language for code blocks without a language. */\n defaultLanguage?: string;\n /** Auto-detect language when none specified. @default false */\n autoDetect?: boolean;\n /** Custom document implementation for generateHTML. */\n document?: Document;\n}\n\n/**\n * Generate HTML with syntax-highlighted code blocks.\n *\n * Unlike generateHTML(), this applies lowlight highlighting to code blocks,\n * producing `<span class=\"hljs-keyword\">` etc. inside `<code>` elements.\n *\n * @example\n * ```ts\n * import { generateHighlightedHTML } from '@domternal/extension-code-block-lowlight';\n * import { createLowlight, common } from 'lowlight';\n *\n * const lowlight = createLowlight(common);\n * const html = generateHighlightedHTML(json, extensions, lowlight);\n * ```\n */\nexport function generateHighlightedHTML(\n content: JSONContent,\n extensions: AnyExtension[],\n lowlight: Lowlight,\n options: GenerateHighlightedHTMLOptions = {},\n): string {\n const htmlOptions: GenerateHTMLOptions = {};\n if (options.document !== undefined) {\n htmlOptions.document = options.document;\n }\n\n const html = generateHTML(content, extensions, htmlOptions);\n\n return html.replace(\n /<pre([^>]*)><code(?:\\s+class=\"language-([^\"]*)\")?>([\\s\\S]*?)<\\/code><\\/pre>/g,\n (_match, preAttrs: string, language: string | undefined, code: string) => {\n // Unescape HTML entities in code content\n const decoded = code\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n\n const lang = language ?? options.defaultLanguage ?? null;\n let highlighted: string;\n\n if (lang && lowlight.registered(lang)) {\n const result = lowlight.highlight(lang, decoded);\n highlighted = toHtml(result);\n } else if (options.autoDetect && decoded.length > 0) {\n const result = lowlight.highlightAuto(decoded);\n highlighted = toHtml(result);\n } else {\n const codeClass = language ? ` class=\"language-${language}\"` : '';\n return `<pre${preAttrs}><code${codeClass}>${code}</code></pre>`;\n }\n\n const codeClass = language ? ` class=\"language-${language}\"` : '';\n return `<pre${preAttrs}><code${codeClass}>${highlighted}</code></pre>`;\n },\n );\n}\n","import type { Lowlight } from './lowlightPlugin.js';\nimport { toHtml } from 'hast-util-to-html';\n\nexport interface CreateCodeHighlighterOptions {\n /** Default language when none specified on the code block. */\n defaultLanguage?: string;\n /** Auto-detect language when none specified. @default false */\n autoDetect?: boolean;\n}\n\n/**\n * Creates a `codeHighlighter` callback for use with `inlineStyles()`.\n *\n * @example\n * ```ts\n * import { createLowlight, common } from 'lowlight';\n * import { createCodeHighlighter } from '@domternal/extension-code-block-lowlight';\n * import { inlineStyles } from '@domternal/core';\n *\n * const lowlight = createLowlight(common);\n * const styled = inlineStyles(html, {\n * codeHighlighter: createCodeHighlighter(lowlight),\n * });\n * ```\n */\nexport function createCodeHighlighter(\n lowlight: Lowlight,\n options: CreateCodeHighlighterOptions = {},\n): (code: string, language: string | null) => string | null {\n return (code: string, language: string | null): string | null => {\n const lang = language ?? options.defaultLanguage ?? null;\n\n if (lang && lowlight.registered(lang)) {\n return toHtml(lowlight.highlight(lang, code));\n }\n\n if (options.autoDetect && code.length > 0) {\n return toHtml(lowlight.highlightAuto(code));\n }\n\n return null;\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@domternal/extension-code-block-lowlight",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Code block with syntax highlighting via lowlight for Domternal editor",
|
|
5
5
|
"author": "https://github.com/ThomasNowHere",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"hast-util-to-html": "^9.0.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@domternal/core": ">=0.
|
|
35
|
-
"@domternal/pm": ">=0.
|
|
34
|
+
"@domternal/core": ">=0.7.0",
|
|
35
|
+
"@domternal/pm": ">=0.7.0",
|
|
36
36
|
"lowlight": "^3.0.0"
|
|
37
37
|
},
|
|
38
38
|
"keywords": [
|