import { nextTick, ref } from 'vue' import { createEditorHistoryEntry, getEditorHistoryEntrySelection, getEditorHistoryEntryValue } from '../lib/markdown-editor-history.js' const DEFAULT_EDITOR_UNDO_LIMIT = 100 const DEFAULT_EDITOR_HISTORY_GROUP_DELAY_MS = 900 /** * 관리자 Markdown 에디터 히스토리 상태를 만든다. * @param {Object} options - 히스토리 옵션 * @param {import('vue').Ref} options.value - Markdown 본문 ref * @param {import('vue').Ref<{ start?: number, end?: number }>} options.lastSelectionState - 마지막 선택 위치 * @param {(selection: { start: number, end: number }|null) => void} options.restoreFocusAfterHistory - 복원 후 포커스 콜백 * @param {number} [options.undoLimit=100] - 히스토리 최대 개수 * @param {number} [options.groupDelayMs=900] - 연속 입력 그룹 지연 시간 * @returns {Object} 히스토리 상태와 이벤트 */ export const useAdminMarkdownHistory = (options) => { const rawUndoLimit = Number(options.undoLimit) const rawGroupDelayMs = Number(options.groupDelayMs) const undoLimit = Math.max(1, Number.isFinite(rawUndoLimit) ? rawUndoLimit : DEFAULT_EDITOR_UNDO_LIMIT) const groupDelayMs = Math.max(0, Number.isFinite(rawGroupDelayMs) ? rawGroupDelayMs : DEFAULT_EDITOR_HISTORY_GROUP_DELAY_MS) /** @type {import('vue').Ref>} 본문 되돌리기 스택 */ const editorUndoStack = ref([]) /** @type {import('vue').Ref>} 본문 다시 실행 스택 */ const editorRedoStack = ref([]) /** 히스토리 복원 중 변경 기록을 막는다. */ const isRestoringEditorHistory = ref(false) /** 마지막 히스토리 기록 시각 */ let lastEditorHistoryRecordAt = 0 /** * 에디터 히스토리 항목을 기록한다. * @param {string} previousValue - 변경 전 본문 * @returns {void} */ const recordEditorHistory = (previousValue) => { if (isRestoringEditorHistory.value) { return } const value = String(previousValue ?? '') const now = Date.now() const shouldGroup = now - lastEditorHistoryRecordAt < groupDelayMs if (!shouldGroup || !editorUndoStack.value.length) { editorUndoStack.value = [ ...editorUndoStack.value, createEditorHistoryEntry(value, options.lastSelectionState.value) ].slice(-undoLimit) } 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(options.value.value ?? '', options.lastSelectionState.value) ].slice(-undoLimit) isRestoringEditorHistory.value = true options.value.value = nextValue lastEditorHistoryRecordAt = 0 nextTick(() => { isRestoringEditorHistory.value = false options.restoreFocusAfterHistory(getEditorHistoryEntrySelection(nextEntry, nextValue)) }) } return { editorUndoStack, editorRedoStack, isRestoringEditorHistory, recordEditorHistory, restoreEditorHistory } }