Central tiptap block definitions

This commit is contained in:
corsacca 2026-04-23 11:05:05 +01:00
parent 3dd17ef692
commit 2d4e47af33
4 changed files with 46 additions and 82 deletions

View file

@ -11,18 +11,7 @@
import { useEditor, EditorContent } from '@tiptap/vue-3'
import type { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import Image from '@tiptap/extension-image'
import TextAlign from '@tiptap/extension-text-align'
import { TextStyle } from '@tiptap/extension-text-style'
import Color from '@tiptap/extension-color'
import Highlight from '@tiptap/extension-highlight'
import Typography from '@tiptap/extension-typography'
import Subscript from '@tiptap/extension-subscript'
import Superscript from '@tiptap/extension-superscript'
import Youtube from '@tiptap/extension-youtube'
import { Div } from '~/utils/tiptapDiv'
import { UupgsListNode } from '~/utils/tiptapUupgsList'
import { buildTiptapExtensions } from '~/utils/tiptapExtensions'
import { uploadImage } from '~/composables/useImageUpload'
const props = withDefaults(defineProps<{
@ -41,20 +30,7 @@ const uploading = ref(false)
const editor = useEditor({
content: props.modelValue,
extensions: [
StarterKit.configure({ link: { openOnClick: false, autolink: true } }),
Image,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
TextStyle,
Color,
Highlight,
Typography,
Subscript,
Superscript,
Youtube,
Div,
UupgsListNode
],
extensions: buildTiptapExtensions(),
onUpdate({ editor: e }) {
emit('update:modelValue', e.getJSON())
}

View file

@ -0,0 +1,36 @@
// Shared Tiptap extension set — used by both the editor
// (app/components/admin/RichTextEditor.vue) and the server-side HTML
// renderer (server/utils/renderTiptap.ts) so authored content round-trips
// cleanly to the public page. Keep all extensions framework-agnostic here
// (@tiptap/core + @tiptap/extension-*): no @tiptap/vue-3 imports, or the
// server renderer will drag the Vue runtime into the Nitro bundle.
import StarterKit from '@tiptap/starter-kit'
import Image from '@tiptap/extension-image'
import TextAlign from '@tiptap/extension-text-align'
import { TextStyle } from '@tiptap/extension-text-style'
import Color from '@tiptap/extension-color'
import Highlight from '@tiptap/extension-highlight'
import Typography from '@tiptap/extension-typography'
import Subscript from '@tiptap/extension-subscript'
import Superscript from '@tiptap/extension-superscript'
import Youtube from '@tiptap/extension-youtube'
import { Div } from './tiptapDiv'
import { UupgsListNode } from './tiptapUupgsList'
export function buildTiptapExtensions() {
return [
StarterKit.configure({ link: { openOnClick: false, autolink: true } }),
Image,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
TextStyle,
Color,
Highlight,
Typography,
Subscript,
Superscript,
Youtube,
Div,
UupgsListNode
]
}

View file

@ -11,33 +11,9 @@ import { JSDOM } from 'jsdom'
// same logic @tiptap/html runs, just with a DOM we control.
import { getSchema } from '@tiptap/core'
import { DOMParser as ProseMirrorDOMParser } from 'prosemirror-model'
import StarterKit from '@tiptap/starter-kit'
import Image from '@tiptap/extension-image'
import TextAlign from '@tiptap/extension-text-align'
import { TextStyle } from '@tiptap/extension-text-style'
import Color from '@tiptap/extension-color'
import Highlight from '@tiptap/extension-highlight'
import Typography from '@tiptap/extension-typography'
import Subscript from '@tiptap/extension-subscript'
import Superscript from '@tiptap/extension-superscript'
import Youtube from '@tiptap/extension-youtube'
import { Div } from '../../app/utils/tiptapDiv'
import { UupgsListNode } from '../../app/utils/tiptapUupgsList'
import { buildTiptapExtensions } from '../../app/utils/tiptapExtensions'
const EXTENSIONS = [
StarterKit.configure({ link: { openOnClick: false, autolink: true } }),
Image,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
TextStyle,
Color,
Highlight,
Typography,
Subscript,
Superscript,
Youtube,
Div,
UupgsListNode
]
const EXTENSIONS = buildTiptapExtensions()
// @tiptap/html ultimately calls prosemirror-model's DOMParser which
// expects a global `window`/`document`. In Bun/Node we provide one via

View file

@ -1,39 +1,15 @@
// Server-side Tiptap JSON → HTML renderer. The extension set here must
// stay aligned with app/components/admin/RichTextEditor.vue (built in
// Phase 6) so content authored in the editor round-trips back out to
// the public page with the same tags/attributes.
// Server-side Tiptap JSON → HTML renderer. The extension set comes from
// app/utils/tiptapExtensions.ts, shared with the editor component so
// authored content round-trips back out to the public page with the
// same tags/attributes.
//
// Keeping this on the server keeps the public bundle free of Tiptap
// runtime code — the renderer serializes once and we send HTML.
import { generateHTML } from '@tiptap/html'
import StarterKit from '@tiptap/starter-kit'
import Image from '@tiptap/extension-image'
import TextAlign from '@tiptap/extension-text-align'
import { TextStyle } from '@tiptap/extension-text-style'
import Color from '@tiptap/extension-color'
import Highlight from '@tiptap/extension-highlight'
import Typography from '@tiptap/extension-typography'
import Subscript from '@tiptap/extension-subscript'
import Superscript from '@tiptap/extension-superscript'
import Youtube from '@tiptap/extension-youtube'
import { Div } from '~~/app/utils/tiptapDiv'
import { UupgsListNode } from '~~/app/utils/tiptapUupgsList'
import { buildTiptapExtensions } from '~~/app/utils/tiptapExtensions'
const EXTENSIONS = [
StarterKit.configure({ link: { openOnClick: false, autolink: true } }),
Image,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
TextStyle,
Color,
Highlight,
Typography,
Subscript,
Superscript,
Youtube,
Div,
UupgsListNode
]
const EXTENSIONS = buildTiptapExtensions()
function isEmptyDoc(doc: unknown): boolean {
if (!doc || typeof doc !== 'object') return true