import { Extension } from "@tiptap/core" import { buildParagraphSpacingStyle, DOCS_DEFAULT_PARAGRAPH_SPACING, parseLineHeightAttr, parseSpacingPt, readParagraphSpacingAttrs, } from "@/lib/drive/docs-line-spacing" import { setDocsCustomSpacing, setDocsLineHeight, toggleDocsKeepLinesTogether, toggleDocsKeepWithNext, toggleDocsPageBreakBefore, toggleDocsPreventWidowOrphan, toggleDocsSpaceAfter, toggleDocsSpaceBefore, } from "@/lib/drive/docs-line-spacing-actions" declare module "@tiptap/core" { interface Commands { docsParagraphSpacing: { setDocsLineHeight: (lineHeight: number) => ReturnType setDocsCustomSpacing: (input: { lineHeight: number | null spaceBeforePt: number spaceAfterPt: number }) => ReturnType toggleDocsSpaceBefore: () => ReturnType toggleDocsSpaceAfter: () => ReturnType toggleDocsKeepWithNext: () => ReturnType toggleDocsKeepLinesTogether: () => ReturnType toggleDocsPreventWidowOrphan: () => ReturnType toggleDocsPageBreakBefore: () => ReturnType } } } function parseBoolAttr(raw: unknown): boolean { return raw === true || raw === "true" || raw === "1" } function spacingRenderHtml(attributes: Record) { const spacing = readParagraphSpacingAttrs({ attrs: attributes }) const style = buildParagraphSpacingStyle(spacing) const html: Record = {} if (spacing.lineHeight != null) html["data-line-height"] = String(spacing.lineHeight) if (spacing.spaceBeforePt > 0) html["data-space-before-pt"] = String(spacing.spaceBeforePt) if (spacing.spaceAfterPt > 0) html["data-space-after-pt"] = String(spacing.spaceAfterPt) if (spacing.keepWithNext) html["data-keep-with-next"] = "true" if (spacing.keepLinesTogether) html["data-keep-lines-together"] = "true" if (spacing.preventWidowOrphan) html["data-prevent-widow-orphan"] = "true" if (spacing.pageBreakBefore) html["data-page-break-before"] = "true" if (style) html.style = style if (spacing.pageBreakBefore) { html.class = "docs-paragraph-spacing--page-break-before" } return html } export const DocsParagraphSpacing = Extension.create({ name: "docsParagraphSpacing", addGlobalAttributes() { return [ { types: ["paragraph", "heading"], attributes: { lineHeight: { default: DOCS_DEFAULT_PARAGRAPH_SPACING.lineHeight, parseHTML: (element) => { const raw = element.getAttribute("data-line-height") if (raw != null) return parseLineHeightAttr(raw) const style = (element as HTMLElement).style?.lineHeight if (!style) return null const numeric = Number.parseFloat(style) return Number.isFinite(numeric) ? numeric : null }, renderHTML: (attributes) => spacingRenderHtml(attributes), }, spaceBeforePt: { default: 0, parseHTML: (element) => parseSpacingPt(element.getAttribute("data-space-before-pt")), renderHTML: () => ({}), }, spaceAfterPt: { default: 0, parseHTML: (element) => parseSpacingPt(element.getAttribute("data-space-after-pt")), renderHTML: () => ({}), }, keepWithNext: { default: false, parseHTML: (element) => parseBoolAttr(element.getAttribute("data-keep-with-next")), renderHTML: () => ({}), }, keepLinesTogether: { default: false, parseHTML: (element) => parseBoolAttr(element.getAttribute("data-keep-lines-together")), renderHTML: () => ({}), }, preventWidowOrphan: { default: false, parseHTML: (element) => parseBoolAttr(element.getAttribute("data-prevent-widow-orphan")), renderHTML: () => ({}), }, pageBreakBefore: { default: false, parseHTML: (element) => parseBoolAttr(element.getAttribute("data-page-break-before")), renderHTML: () => ({}), }, }, }, ] }, addCommands() { return { setDocsLineHeight: (lineHeight: number) => ({ editor }) => setDocsLineHeight(editor, lineHeight), setDocsCustomSpacing: (input) => ({ editor }) => setDocsCustomSpacing(editor, input), toggleDocsSpaceBefore: () => ({ editor }) => toggleDocsSpaceBefore(editor), toggleDocsSpaceAfter: () => ({ editor }) => toggleDocsSpaceAfter(editor), toggleDocsKeepWithNext: () => ({ editor }) => toggleDocsKeepWithNext(editor), toggleDocsKeepLinesTogether: () => ({ editor }) => toggleDocsKeepLinesTogether(editor), toggleDocsPreventWidowOrphan: () => ({ editor }) => toggleDocsPreventWidowOrphan(editor), toggleDocsPageBreakBefore: () => ({ editor }) => toggleDocsPageBreakBefore(editor), } }, })