공개 본문 인용 편집 분리

This commit is contained in:
2026-07-27 16:39:20 +09:00
parent 9efa6d6af5
commit 1d15a0e4d1
10 changed files with 318 additions and 154 deletions

View File

@@ -16,10 +16,8 @@ import {
} from '../../lib/markdown-live-edit.js'
import { CALLOUT_BACKGROUND_OPTIONS } from '../../lib/markdown-callout.js'
import {
formatQuoteLine,
getQuoteContentStartLine,
getQuoteLineEntries,
normalizeQuoteEditableValue
getQuoteLineEntries
} from '../../lib/markdown-quote.js'
import { getGalleryRows } from '../../lib/content-gallery-images.js'
import { useContentMarkdownLightbox } from '../../composables/useContentMarkdownLightbox.js'
@@ -29,6 +27,7 @@ import { useContentMarkdownLiveSelection } from '../../composables/useContentMar
import { useContentMarkdownFencedBlocks } from '../../composables/useContentMarkdownFencedBlocks.js'
import { useContentMarkdownParagraphs } from '../../composables/useContentMarkdownParagraphs.js'
import { useContentMarkdownLists } from '../../composables/useContentMarkdownLists.js'
import { useContentMarkdownQuotes } from '../../composables/useContentMarkdownQuotes.js'
import ContentMarkdownCodeBlockEditor from './ContentMarkdownCodeBlockEditor.vue'
import ProseCodeBlock from './ProseCodeBlock.vue'
import ProseHtml from './ProseHtml.vue'
@@ -199,6 +198,19 @@ const {
insertBelowBlock: (block, options) => onInsertBelowBlock(block, options)
})
const {
onQuoteBlockCommit,
onQuoteBlockInsertBelow,
onQuoteBlockConvertToParagraph
} = useContentMarkdownQuotes({
content: computed(() => props.content),
pendingFocusLine,
pendingFocusPosition,
pendingFocusOffset,
commitInlineBlockLines: (block, replacementLines) => commitInlineBlockLines(block, replacementLines),
insertBelowBlock: (block, options) => onInsertBelowBlock(block, options)
})
/**
* 빈 줄 공백 블록 높이를 반환한다.
* @param {Object} block - 렌더링 블록
@@ -400,16 +412,6 @@ const normalizeInsertBelowPayload = (payload) => {
*/
const getMarkdownLine = (lineIndex) => String(props.content || '').split('\n')[lineIndex] ?? ''
/**
* 블록에 해당하는 원본 마크다운 줄 목록을 반환한다.
* @param {Object} block - 블록
* @returns {string[]} 줄 목록
*/
const getBlockSourceLines = (block) => String(props.content || '').split('\n').slice(
block.meta.startLine,
(block.meta.endLine ?? block.meta.startLine) + 1
)
/**
* 인라인 편집 결과를 마크다운 줄로 반영한다.
* @param {Object} block - 블록
@@ -680,142 +682,6 @@ const onHeadingInsertBelow = (block, payload) => {
commitInlineBlockLines(block, [buildHeadingLine(block.level, value), ''])
}
/**
* 인용 줄 인라인 편집 반영
* @param {Object} block - 인용 블록
* @param {number} lineIndex - 줄 인덱스
* @param {string|{ value: string, raw?: boolean }} payload - 편집 내용
* @returns {void}
*/
const onQuoteLineInlineCommit = (block, lineIndex, payload) => {
const { value, raw } = normalizeCommitPayload(payload)
const sourceLines = String(props.content || '').split('\n').slice(
block.meta.startLine,
(block.meta.endLine ?? block.meta.startLine) + 1
)
const nextLines = [...sourceLines]
nextLines[lineIndex] = formatQuoteLine(value, raw)
commitInlineBlockLines(block, nextLines)
}
/**
* 인용 줄 아래에 새 인용 줄을 삽입한다.
* @param {Object} block - 인용 블록
* @param {number} lineIndex - 줄 인덱스
* @returns {void}
*/
const onQuoteLineInsertBelow = (block, lineIndex, payload) => {
const { value, raw, before, after, caretAtStart } = normalizeInsertBelowPayload(payload)
const nextLines = getBlockSourceLines(block)
if (caretAtStart && after.length) {
nextLines[lineIndex] = formatQuoteLine(after, raw)
nextLines.splice(lineIndex, 0, '> ')
pendingFocusLine.value = block.meta.startLine + lineIndex
pendingFocusPosition.value = 'start'
commitInlineBlockLines(block, nextLines)
return
}
if (before.length && after.length) {
nextLines[lineIndex] = formatQuoteLine(before, raw)
nextLines.splice(lineIndex + 1, 0, formatQuoteLine(after, raw))
pendingFocusLine.value = block.meta.startLine + lineIndex + 1
pendingFocusPosition.value = 'start'
commitInlineBlockLines(block, nextLines)
return
}
if (lineIndex < nextLines.length) {
nextLines[lineIndex] = formatQuoteLine(value, raw)
}
nextLines.splice(lineIndex + 1, 0, '> ')
pendingFocusLine.value = block.meta.startLine + lineIndex + 1
pendingFocusPosition.value = 'start'
commitInlineBlockLines(block, nextLines)
}
/**
* 인용 블록 원본에서 옵션 선언 줄을 반환한다.
* @param {Object} block - 인용 블록
* @returns {string[]} 옵션 선언 줄
*/
const getQuoteOptionLines = (block) => {
const contentStartLine = getQuoteContentStartLine(block)
if (contentStartLine <= block.meta.startLine) {
return []
}
return getBlockSourceLines(block).slice(0, contentStartLine - block.meta.startLine)
}
/**
* 인용 본문 문자열을 마크다운 줄로 변환한다.
* @param {Object} block - 인용 블록
* @param {string|{ value?: string }} payload - 편집 페이로드
* @returns {string[]} 인용 마크다운 줄
*/
const buildQuoteBlockLines = (block, payload) => {
const value = typeof payload === 'string'
? payload
: String(payload?.value ?? '')
const bodyLines = String(value ?? '').replace(/\r/g, '').split('\n')
const normalizedBodyLines = bodyLines.length ? bodyLines : ['']
return [
...getQuoteOptionLines(block),
...normalizedBodyLines.map((line) => formatQuoteLine(line, false))
]
}
/**
* 인용 블록 편집 반영
* @param {Object} block - 인용 블록
* @param {string|{ value?: string }} payload - 편집 페이로드
* @returns {void}
*/
const onQuoteBlockCommit = (block, payload) => {
const normalizedPayload = typeof payload === 'object' && payload !== null
? { ...payload, value: normalizeQuoteEditableValue(payload.value) }
: normalizeQuoteEditableValue(payload)
commitInlineBlockLines(block, buildQuoteBlockLines(block, normalizedPayload))
}
/**
* 인용 블록 마지막 줄에서 아래로 이탈한다.
* @param {Object} block - 인용 블록
* @param {string|Object} payload - insert-below 페이로드
* @returns {void}
*/
const onQuoteBlockInsertBelow = (block, payload) => {
const { value } = normalizeInsertBelowPayload(payload)
onQuoteBlockCommit(block, value)
pendingFocusPosition.value = 'start'
pendingFocusOffset.value = 0
onInsertBelowBlock(block, { lines: [''] })
}
/**
* 인용 블록을 일반 문단 줄로 되돌린다.
* @param {Object} block - 인용 블록
* @param {string|Object} payload - 편집 페이로드
* @returns {void}
*/
const onQuoteBlockConvertToParagraph = (block, payload) => {
const { value } = normalizeCommitPayload(payload)
const lines = normalizeQuoteEditableValue(value).split('\n')
const replacementLines = lines.length ? lines : ['']
pendingFocusLine.value = block.meta.startLine
pendingFocusPosition.value = 'start'
commitInlineBlockLines(block, replacementLines)
}
/**
* 라이브 모드에서 현재 줄을 삭제한다.
* @param {number} lineIndex - 줄 번호