미리보기 액션 적용 분리

This commit is contained in:
2026-07-29 10:30:24 +09:00
parent 980ef92a68
commit d8cfa77080
10 changed files with 456 additions and 228 deletions

View File

@@ -0,0 +1,243 @@
import { nextTick } from 'vue'
import {
createPreviewAppendParagraphValue,
createPreviewDeleteLineValue,
createPreviewExtractGalleryImageValue,
createPreviewGalleryReorderValue,
createPreviewImagesMergeValue,
createPreviewInsertAfterLineResult,
createPreviewInsertImageToGalleryValue,
createPreviewMergeWithPreviousLineValue,
createPreviewRemoveGalleryImageResult
} from '../lib/markdown-editor-preview-blocks.js'
import { shouldIgnorePendingMediaSlashCommit } from '../lib/markdown-slash-commands.js'
/**
* 관리자 Markdown 라이브 미리보기 이벤트 적용 함수를 만든다.
* @param {Object} options - 미리보기 액션 옵션
* @param {import('vue').Ref<string>} options.value - Markdown 본문 ref
* @param {import('vue').Ref<number>} options.activeLogicalLineIndex - 활성 논리 줄 ref
* @param {import('vue').Ref<number|null>} options.liveSlashInsertLine - 슬래시 미디어 삽입 대기 줄 ref
* @param {(startLine: number, endLine: number, replacementLines: string[], focusEditor?: boolean) => void} options.replaceLineRange - 줄 범위 교체 함수
* @param {(startLine: number, fallbackEndLine: number) => number} options.resolveCurrentFencedBlockEndLine - 현재 fenced 블록 끝 줄 계산 함수
* @param {() => void} options.resetBlockPanelContext - 블록 패널 컨텍스트 초기화 함수
* @param {(line: number, column?: number, align?: string, offset?: number, block?: string) => void} options.focusPreviewEditableAtLine - 미리보기 줄 포커스 함수
* @returns {Object} 미리보기 이벤트 적용 함수
*/
export const useAdminMarkdownPreviewActions = (options) => {
/**
* 계산된 Markdown 값을 현재 본문에 반영한다.
* @param {string|null} nextValue - 다음 Markdown 값
* @returns {boolean} 적용 여부
*/
const applyNextValue = (nextValue) => {
if (nextValue === null) {
return false
}
options.value.value = nextValue
return true
}
/**
* 미리보기에서 갤러리 순서 변경을 반영한다.
* @param {{ startLine: number, endLine: number, images: Array<Object> }} payload - 갤러리 줄 범위·이미지 목록
* @returns {void}
*/
const onPreviewGalleryReorder = ({ startLine, endLine, images }) => {
applyNextValue(createPreviewGalleryReorderValue({
value: options.value.value,
startLine,
endLine,
images
}))
}
/**
* 라이브 모드에서 두 단일 이미지 블록을 갤러리로 병합한다.
* @param {{ sourceLine: number, targetLine: number }} payload - 원본·대상 줄
* @returns {void}
*/
const onPreviewMergeImagesToGallery = ({ sourceLine, targetLine }) => {
applyNextValue(createPreviewImagesMergeValue({
value: options.value.value,
sourceLine,
targetLine
}))
}
/**
* 라이브 모드에서 단일 이미지 블록을 기존 갤러리에 추가한다.
* @param {{ sourceLine: number, startLine: number, endLine: number, targetIndex: number, image?: Object }} payload - 이미지 줄·갤러리 범위·삽입 위치
* @returns {void}
*/
const onPreviewInsertImageToGallery = ({
sourceLine,
startLine,
endLine,
targetIndex,
image
}) => {
applyNextValue(createPreviewInsertImageToGalleryValue({
value: options.value.value,
sourceLine,
startLine,
endLine,
targetIndex,
image
}))
}
/**
* 라이브 모드 갤러리에서 특정 이미지를 삭제한다.
* @param {{ startLine: number, endLine: number, imageIndex: number }} payload - 갤러리 범위·이미지 인덱스
* @returns {void}
*/
const onPreviewRemoveGalleryImage = ({ startLine, endLine, imageIndex }) => {
const result = createPreviewRemoveGalleryImageResult({
value: options.value.value,
startLine,
endLine,
imageIndex
})
if (!result) {
return
}
options.value.value = result.value
options.activeLogicalLineIndex.value = result.focusLine
}
/**
* 라이브 모드에서 갤러리 이미지를 블록 사이에 단일 이미지 줄로 분리한다.
* @param {{ startLine: number, endLine: number, imageIndex: number, insertBeforeLine: number, image?: Object }} payload - 갤러리 범위·삽입 위치
* @returns {void}
*/
const onPreviewExtractGalleryImage = ({
startLine,
endLine,
imageIndex,
insertBeforeLine,
image
}) => {
applyNextValue(createPreviewExtractGalleryImageValue({
value: options.value.value,
startLine,
endLine,
imageIndex,
insertBeforeLine,
image
}))
}
/**
* 미리보기 인라인 편집 결과를 마크다운 본문에 반영한다.
* @param {{ startLine: number, endLine: number, replacementLines: string[] }} payload - 줄 범위·대체 줄
* @returns {void}
*/
const onPreviewBlockContentChange = ({ startLine, endLine, replacementLines }) => {
if (typeof startLine !== 'number' || typeof endLine !== 'number' || !Array.isArray(replacementLines)) {
return
}
if (shouldIgnorePendingMediaSlashCommit({
pendingInsertLine: options.liveSlashInsertLine.value,
startLine,
replacementLines
})) {
return
}
options.replaceLineRange(
startLine,
options.resolveCurrentFencedBlockEndLine(startLine, endLine),
replacementLines,
false
)
}
/**
* 라이브 모드 교차 선택 삭제 결과를 본문에 반영한다.
* @param {{ value: string }} payload - 갱신된 마크다운
* @returns {void}
*/
const onPreviewContentReplace = ({ value }) => {
if (typeof value === 'string') {
options.value.value = value
}
}
/**
* 라이브 모드 하단에서 새 문단 줄을 추가한다.
* @returns {void}
*/
const onPreviewAppendParagraph = () => {
options.value.value = createPreviewAppendParagraphValue(options.value.value)
}
/**
* 라이브 모드에서 지정 줄 아래에 마크다운 줄을 삽입한다.
* @param {{ afterLine: number, lines: string[], focusLine?: number }} payload - 삽입 위치·줄
* @returns {void}
*/
const onPreviewInsertAfterLine = ({ afterLine, lines, focusLine }) => {
const result = createPreviewInsertAfterLineResult({
value: options.value.value,
afterLine,
lines,
focusLine
})
if (!result) {
return
}
options.value.value = result.value
options.activeLogicalLineIndex.value = result.focusLine
options.resetBlockPanelContext()
nextTick(() => {
options.focusPreviewEditableAtLine(result.focusLine, 0, 'start')
})
}
/**
* 라이브 모드에서 현재 줄을 삭제한다.
* @param {number} lineIndex - 줄 번호
* @returns {void}
*/
const onPreviewDeleteLine = (lineIndex) => {
applyNextValue(createPreviewDeleteLineValue({
value: options.value.value,
lineIndex
}))
}
/**
* 라이브 모드에서 현재 줄을 이전 줄 끝에 병합한다.
* @param {{ lineIndex: number, mergedLine: string }} payload - 병합 위치·결과 줄
* @returns {void}
*/
const onPreviewMergeWithPreviousLine = ({ lineIndex, mergedLine }) => {
applyNextValue(createPreviewMergeWithPreviousLineValue({
value: options.value.value,
lineIndex,
mergedLine
}))
}
return {
onPreviewAppendParagraph,
onPreviewBlockContentChange,
onPreviewContentReplace,
onPreviewDeleteLine,
onPreviewExtractGalleryImage,
onPreviewGalleryReorder,
onPreviewInsertAfterLine,
onPreviewInsertImageToGallery,
onPreviewMergeImagesToGallery,
onPreviewMergeWithPreviousLine,
onPreviewRemoveGalleryImage
}
}