| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { Node, mergeAttributes, type Extensions } from "@tiptap/core"; |
| import { SHARED_COMPONENT_DEFS, type SharedComponentDef } from "../shared/component-defs.js"; |
| import StarterKit from "@tiptap/starter-kit"; |
| import { CodeBlock } from "@tiptap/extension-code-block"; |
| import Mathematics from "@tiptap/extension-mathematics"; |
| import { Image } from "@tiptap/extension-image"; |
| import { Table } from "@tiptap/extension-table"; |
| import { TableRow } from "@tiptap/extension-table-row"; |
| import { TableCell } from "@tiptap/extension-table-cell"; |
| import { TableHeader } from "@tiptap/extension-table-header"; |
|
|
| |
|
|
| const CitationServer = Node.create({ |
| name: "citation", |
| group: "inline", |
| inline: true, |
| atom: true, |
| addAttributes() { |
| return { |
| key: { default: "" }, |
| label: { default: "" }, |
| }; |
| }, |
| parseHTML() { |
| return [{ tag: 'span[data-type="citation"]' }]; |
| }, |
| renderHTML({ HTMLAttributes, node }) { |
| const label = node.attrs.label || `[${node.attrs.key}]`; |
| return [ |
| "span", |
| mergeAttributes(HTMLAttributes, { |
| "data-type": "citation", |
| class: "citation-node", |
| }), |
| label, |
| ]; |
| }, |
| }); |
|
|
| const BibliographyServer = Node.create({ |
| name: "bibliography", |
| group: "block", |
| atom: true, |
| addAttributes() { |
| return { |
| renderedHtml: { default: "" }, |
| }; |
| }, |
| parseHTML() { |
| return [{ tag: 'div[data-type="bibliography"]' }]; |
| }, |
| renderHTML({ HTMLAttributes }) { |
| |
| |
| return [ |
| "div", |
| mergeAttributes(HTMLAttributes, { |
| "data-type": "bibliography", |
| class: "bibliography-block", |
| }), |
| ]; |
| }, |
| }); |
|
|
| const GlossaryServer = Node.create({ |
| name: "glossary", |
| group: "inline", |
| inline: true, |
| atom: true, |
| addAttributes() { |
| return { |
| term: { default: "" }, |
| definition: { default: "" }, |
| }; |
| }, |
| parseHTML() { |
| return [{ tag: 'span[data-type="glossary"]' }]; |
| }, |
| renderHTML({ HTMLAttributes, node }) { |
| const term = (node.attrs.term as string) || ""; |
| const definition = (node.attrs.definition as string) || ""; |
| return [ |
| "span", |
| mergeAttributes(HTMLAttributes, { |
| "data-type": "glossary", |
| class: "glossary-node", |
| tabindex: "0", |
| }), |
| term, |
| [ |
| "span", |
| { class: "pub-tooltip pub-tooltip--glossary", "aria-hidden": "true" }, |
| ["span", { class: "pub-tooltip__title" }, term], |
| ["span", { class: "pub-tooltip__body" }, definition], |
| ], |
| ]; |
| }, |
| }); |
|
|
| const FootnoteServer = Node.create({ |
| name: "footnote", |
| group: "inline", |
| inline: true, |
| atom: true, |
| addAttributes() { |
| return { |
| content: { default: "" }, |
| }; |
| }, |
| parseHTML() { |
| return [{ tag: 'span[data-type="footnote"]' }]; |
| }, |
| renderHTML({ HTMLAttributes, node }) { |
| return [ |
| "span", |
| mergeAttributes(HTMLAttributes, { |
| "data-type": "footnote", |
| class: "footnote-node", |
| title: node.attrs.content, |
| tabindex: "0", |
| }), |
| ["sup", { class: "footnote-marker" }, "*"], |
| ]; |
| }, |
| }); |
|
|
| |
|
|
| const StackColumnServer = Node.create({ |
| name: "stackColumn", |
| group: "stackColumn", |
| content: "block+", |
| defining: true, |
| isolating: true, |
| parseHTML() { |
| return [{ tag: 'div[data-type="stack-column"]' }]; |
| }, |
| renderHTML({ HTMLAttributes }) { |
| return [ |
| "div", |
| mergeAttributes(HTMLAttributes, { "data-type": "stack-column" }), |
| 0, |
| ]; |
| }, |
| }); |
|
|
| const StackServer = Node.create({ |
| name: "stack", |
| group: "block", |
| content: "stackColumn{2,4}", |
| defining: true, |
| isolating: true, |
| addAttributes() { |
| return { |
| layout: { default: "2-column" }, |
| gap: { default: "medium" }, |
| }; |
| }, |
| parseHTML() { |
| return [{ tag: 'div[data-component="stack"]' }]; |
| }, |
| renderHTML({ HTMLAttributes }) { |
| return [ |
| "div", |
| mergeAttributes(HTMLAttributes, { "data-component": "stack" }), |
| 0, |
| ]; |
| }, |
| }); |
|
|
| |
|
|
| function makeServerWrapperExt(def: SharedComponentDef) { |
| return Node.create({ |
| name: def.name, |
| group: "block", |
| content: def.content || "block+", |
| defining: true, |
| isolating: true, |
| addAttributes() { |
| const attrs: Record<string, { default: unknown }> = {}; |
| for (const f of def.fields) attrs[f.name] = { default: f.default ?? null }; |
| return attrs; |
| }, |
| parseHTML() { |
| return [{ tag: `div[data-component="${def.name}"]` }]; |
| }, |
| renderHTML({ HTMLAttributes }) { |
| return [ |
| "div", |
| mergeAttributes(HTMLAttributes, { "data-component": def.name }), |
| 0, |
| ]; |
| }, |
| }); |
| } |
|
|
| function makeServerAtomicExt(def: SharedComponentDef) { |
| return Node.create({ |
| name: def.name, |
| group: "block", |
| atom: true, |
| addAttributes() { |
| const attrs: Record<string, { default: unknown }> = {}; |
| for (const f of def.fields) attrs[f.name] = { default: f.default ?? null }; |
| return attrs; |
| }, |
| parseHTML() { |
| return [{ tag: `div[data-component="${def.name}"]` }]; |
| }, |
| renderHTML({ HTMLAttributes }) { |
| return [ |
| "div", |
| mergeAttributes(HTMLAttributes, { "data-component": def.name }), |
| ]; |
| }, |
| }); |
| } |
|
|
| export function getServerExtensions(): Extensions { |
| const wrappers = SHARED_COMPONENT_DEFS.filter((d) => d.kind === "wrapper").map(makeServerWrapperExt); |
| const atomics = SHARED_COMPONENT_DEFS.filter((d) => d.kind === "atomic").map(makeServerAtomicExt); |
|
|
| return [ |
| StarterKit.configure({ |
| codeBlock: false, |
| undoRedo: false, |
| link: { openOnClick: false }, |
| } as any), |
| CodeBlock, |
| Mathematics.configure({ katexOptions: { throwOnError: false } }), |
| Image.configure({ allowBase64: true }), |
| Table.configure({ resizable: false }), |
| TableRow, |
| TableCell, |
| TableHeader, |
| CitationServer, |
| BibliographyServer, |
| GlossaryServer, |
| FootnoteServer, |
| StackServer, |
| StackColumnServer, |
| ...wrappers, |
| ...atomics, |
| ]; |
| } |
|
|