fix(editor): preserve consecutive whitespace in comments in TipTap

Ensure multiple spaces in comment content are no longer collapsed when editing/saving by:
- Adding SetContentOptions with parseOptions.preserveWhitespace = 'full'
- Applying those options to all setContent calls (initial load, exit edit mode, post-upload cleanup)
- Enabling preserveWhitespace in editor parseOptions

Previously, repeated spaces were normalized away after setContent(false), causing comments with deliberate spacing to be altered unexpectedly.

No behavioral changes beyond whitespace retention; renders identical except space fidelity.
This commit is contained in:
maggch
2025-11-19 18:37:46 +08:00
parent 572140f744
commit 30577403c6

View File

@@ -145,7 +145,7 @@ import {eventToHotkeyString} from '@github/hotkey'
import EditorToolbar from './EditorToolbar.vue' import EditorToolbar from './EditorToolbar.vue'
import StarterKit from '@tiptap/starter-kit' import StarterKit from '@tiptap/starter-kit'
import {Extension, mergeAttributes} from '@tiptap/core' import {Extension, mergeAttributes, type SetContentOptions} from '@tiptap/core'
import {EditorContent, type Extensions, useEditor, VueNodeViewRenderer} from '@tiptap/vue-3' import {EditorContent, type Extensions, useEditor, VueNodeViewRenderer} from '@tiptap/vue-3'
import {Plugin, PluginKey} from '@tiptap/pm/state' import {Plugin, PluginKey} from '@tiptap/pm/state'
import {marked} from 'marked' import {marked} from 'marked'
@@ -213,6 +213,12 @@ const tiptapInstanceRef = ref<HTMLInputElement | null>(null)
const {t} = useI18n() const {t} = useI18n()
const defaultSetContentOptions: SetContentOptions = {
parseOptions: {
preserveWhitespace: 'full',
},
}
const CustomTableCell = TableCell.extend({ const CustomTableCell = TableCell.extend({
addAttributes() { addAttributes() {
return { return {
@@ -539,6 +545,9 @@ const editor = useEditor({
onUpdate: () => { onUpdate: () => {
bubbleNow() bubbleNow()
}, },
parseOptions: {
preserveWhitespace: 'full',
},
}) })
watch( watch(
@@ -583,7 +592,7 @@ function bubbleSave() {
} }
function exitEditMode() { function exitEditMode() {
editor.value?.commands.setContent(lastSavedState, false) editor.value?.commands.setContent(lastSavedState, defaultSetContentOptions)
if (isEditing.value) { if (isEditing.value) {
internalMode.value = 'preview' internalMode.value = 'preview'
} }
@@ -630,7 +639,7 @@ function uploadAndInsertFiles(files: File[] | FileList) {
const html = editor.value?.getHTML().replace(UPLOAD_PLACEHOLDER_ELEMENT, '') ?? '' const html = editor.value?.getHTML().replace(UPLOAD_PLACEHOLDER_ELEMENT, '') ?? ''
editor.value?.commands.setContent(html, false) editor.value?.commands.setContent(html, defaultSetContentOptions)
bubbleSave() bubbleSave()
}) })
@@ -689,7 +698,7 @@ onBeforeUnmount(() => {
function setModeAndValue(value: string) { function setModeAndValue(value: string) {
internalMode.value = isEditorContentEmpty(value) ? 'edit' : 'preview' internalMode.value = isEditorContentEmpty(value) ? 'edit' : 'preview'
editor.value?.commands.setContent(value, false) editor.value?.commands.setContent(value, defaultSetContentOptions)
} }