관리자 본문 히스토리 상태 분리

This commit is contained in:
2026-07-27 16:44:58 +09:00
parent 1d15a0e4d1
commit a8119d22ca
9 changed files with 238 additions and 67 deletions

View File

@@ -16,11 +16,6 @@ import {
getMarkdownLineBodyPrefixLength,
getSelectionOffsetInElement
} from '../../lib/markdown-live-selection.js'
import {
createEditorHistoryEntry,
getEditorHistoryEntrySelection,
getEditorHistoryEntryValue
} from '../../lib/markdown-editor-history.js'
import {
getEditorHistoryKeyboardAction,
isEditorModeToggleShortcut,
@@ -71,6 +66,7 @@ import { buildCodeFenceOpener } from '../../lib/markdown-code-block.js'
import { buildToggleOpenerLine } from '../../lib/markdown-toggle.js'
import { getTextareaCaretCoordinates } from '../../lib/textarea-caret-coordinates.js'
import { useAdminMarkdownSlashMenu } from '../../composables/useAdminMarkdownSlashMenu.js'
import { useAdminMarkdownHistory } from '../../composables/useAdminMarkdownHistory.js'
import {
buildDefaultUploadSizeLimits,
DEFAULT_UPLOAD_MAX_FILE_COUNT,
@@ -175,16 +171,6 @@ const {
let liveSlashPreviewScrollElement = null
/** @type {HTMLElement|null} 슬래시 메뉴 위치 갱신을 위해 구독한 작성 textarea */
let liveSlashTextareaScrollElement = null
/** 되돌리기 히스토리 최대 보관 개수 */
const EDITOR_UNDO_LIMIT = 100
/** @type {import('vue').Ref<Array<string|{ value: string, selectionStart: number, selectionEnd: number }>>} 본문 되돌리기 스택 */
const editorUndoStack = ref([])
/** @type {import('vue').Ref<Array<string|{ value: string, selectionStart: number, selectionEnd: number }>>} 본문 다시 실행 스택 */
const editorRedoStack = ref([])
/** 히스토리 복원 중 변경 기록을 막는다. */
const isRestoringEditorHistory = ref(false)
/** 마지막 히스토리 기록 시각 */
let lastEditorHistoryRecordAt = 0
const mediaPickerAccept = computed(() => getMediaPickerAcceptForTarget(mediaPickerTarget.value))
const mediaPickerKind = computed(() => getMediaPickerKindForTarget(mediaPickerTarget.value))
@@ -732,54 +718,14 @@ const restoreEditorFocusAfterHistory = (selection = null) => {
})
}
/**
* 에디터 히스토리 항목을 기록한다.
* @param {string} previousValue - 변경 전 본문
* @returns {void}
*/
const recordEditorHistory = (previousValue) => {
if (isRestoringEditorHistory.value) {
return
}
const value = String(previousValue ?? '')
const now = Date.now()
const shouldGroup = now - lastEditorHistoryRecordAt < 900
if (!shouldGroup || !editorUndoStack.value.length) {
editorUndoStack.value = [...editorUndoStack.value, createEditorHistoryEntry(value, lastSelectionState.value)].slice(-EDITOR_UNDO_LIMIT)
}
editorRedoStack.value = []
lastEditorHistoryRecordAt = now
}
/**
* 에디터 본문 히스토리를 복원한다.
* @param {'undo'|'redo'} direction - 복원 방향
* @returns {void}
*/
const restoreEditorHistory = (direction) => {
const sourceStack = direction === 'undo' ? editorUndoStack : editorRedoStack
const targetStack = direction === 'undo' ? editorRedoStack : editorUndoStack
const nextEntry = sourceStack.value[sourceStack.value.length - 1]
const nextValue = getEditorHistoryEntryValue(nextEntry)
if (typeof nextValue !== 'string') {
return
}
sourceStack.value = sourceStack.value.slice(0, -1)
targetStack.value = [...targetStack.value, createEditorHistoryEntry(markdownValue.value ?? '', lastSelectionState.value)].slice(-EDITOR_UNDO_LIMIT)
isRestoringEditorHistory.value = true
markdownValue.value = nextValue
lastEditorHistoryRecordAt = 0
nextTick(() => {
isRestoringEditorHistory.value = false
restoreEditorFocusAfterHistory(getEditorHistoryEntrySelection(nextEntry, nextValue))
})
}
const {
recordEditorHistory,
restoreEditorHistory
} = useAdminMarkdownHistory({
value: markdownValue,
lastSelectionState,
restoreFocusAfterHistory: restoreEditorFocusAfterHistory
})
/**
* 라이브 모드에 실제로 존재하는 편집 줄로 포커스 대상을 보정한다.