연속 콜아웃 편집 범위 보정
This commit is contained in:
@@ -108,9 +108,8 @@ const suppressBlurCommit = ref(false)
|
||||
const splitLock = ref(false)
|
||||
/** 조합 중 Enter 후 compositionend에서 분리할지 */
|
||||
const pendingSplitAfterComposition = ref(false)
|
||||
/** 조합 종료 직후 중복 Enter를 1회 무시할지 */
|
||||
const suppressNextEnterAfterComposition = ref(false)
|
||||
const showingRaw = ref(false)
|
||||
let cleanupComposedEnterSuppressor = null
|
||||
|
||||
/** @returns {string} Enter 동작 모드 */
|
||||
const resolvedEnterMode = computed(() => {
|
||||
@@ -186,6 +185,10 @@ onMounted(() => {
|
||||
syncEditorHtml()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
cleanupComposedEnterSuppressor?.()
|
||||
})
|
||||
|
||||
/**
|
||||
* 포커스 시 편집 상태를 표시한다.
|
||||
* @returns {void}
|
||||
@@ -680,6 +683,46 @@ const scheduleEnterAction = (action) => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 조합 종료 직후 브라우저가 다시 전달하는 Enter를 한 번 차단한다.
|
||||
* @returns {void}
|
||||
*/
|
||||
const suppressNextComposedEnterGlobally = () => {
|
||||
if (!import.meta.client) {
|
||||
return
|
||||
}
|
||||
|
||||
cleanupComposedEnterSuppressor?.()
|
||||
|
||||
const deadline = Date.now() + 500
|
||||
|
||||
const handler = (event) => {
|
||||
if (
|
||||
event.key === 'Enter'
|
||||
&& !event.shiftKey
|
||||
&& !event.metaKey
|
||||
&& !event.ctrlKey
|
||||
&& !event.altKey
|
||||
&& Date.now() <= deadline
|
||||
) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
event.stopImmediatePropagation?.()
|
||||
cleanupComposedEnterSuppressor?.()
|
||||
}
|
||||
}
|
||||
|
||||
cleanupComposedEnterSuppressor = () => {
|
||||
window.removeEventListener('keydown', handler, true)
|
||||
cleanupComposedEnterSuppressor = null
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', handler, true)
|
||||
window.setTimeout(() => {
|
||||
cleanupComposedEnterSuppressor?.()
|
||||
}, 520)
|
||||
}
|
||||
|
||||
/**
|
||||
* 원문 모드 상태를 부모에 알린다.
|
||||
* @param {boolean} active - 활성 여부
|
||||
@@ -878,17 +921,6 @@ const onKeydown = (event) => {
|
||||
|
||||
const enterMode = showingRaw.value ? 'insert-below' : resolvedEnterMode.value
|
||||
|
||||
if (
|
||||
event.key === 'Enter'
|
||||
&& !event.shiftKey
|
||||
&& suppressNextEnterAfterComposition.value
|
||||
) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
suppressNextEnterAfterComposition.value = false
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === 'Enter' && !event.shiftKey && parseSlashInput(readEditorValue())) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
@@ -950,11 +982,8 @@ const onCompositionEnd = () => {
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
suppressNextEnterAfterComposition.value = true
|
||||
suppressNextComposedEnterGlobally()
|
||||
scheduleEnterAction(enterMode === 'split-paragraph' ? 'split' : 'insert-below')
|
||||
window.setTimeout(() => {
|
||||
suppressNextEnterAfterComposition.value = false
|
||||
}, 1200)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@ const getSpacerHeightClass = (block) => block.meta?.legacy ? 'h-6' : 'h-8'
|
||||
* 닫힘 표식까지의 행 목록을 반환
|
||||
* @param {Array<string>} lines - 전체 마크다운 행
|
||||
* @param {number} startIndex - 본문 시작 인덱스
|
||||
* @returns {{contentLines: Array<string>, nextIndex: number}} 블록 본문과 다음 인덱스
|
||||
* @returns {{contentLines: Array<string>, endLine: number, nextIndex: number}} 블록 본문과 다음 인덱스
|
||||
*/
|
||||
const collectFencedLines = (lines, startIndex) => {
|
||||
const contentLines = []
|
||||
@@ -285,7 +285,8 @@ const collectFencedLines = (lines, startIndex) => {
|
||||
|
||||
return {
|
||||
contentLines,
|
||||
nextIndex: index + 1
|
||||
endLine: index < lines.length ? index : Math.max(startIndex - 1, 0),
|
||||
nextIndex: index < lines.length ? index + 1 : lines.length
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,14 +490,14 @@ const parseMarkdownBlocks = (markdown) => {
|
||||
|
||||
if (trimmedLine === ':::bookmark') {
|
||||
const startLine = index
|
||||
const { contentLines, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
const { contentLines, endLine, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
const bookmarkMeta = parseBookmarkMeta(contentLines.join('\n'))
|
||||
|
||||
if (bookmarkMeta.url) {
|
||||
blocks.push(attachSourceRange(
|
||||
createBlock('bookmark', '', null, `block-${blocks.length}`, { meta: bookmarkMeta }),
|
||||
startLine,
|
||||
nextIndex
|
||||
endLine
|
||||
))
|
||||
}
|
||||
|
||||
@@ -506,12 +507,12 @@ const parseMarkdownBlocks = (markdown) => {
|
||||
|
||||
if (trimmedLine === ':::signup') {
|
||||
const startLine = index
|
||||
const { contentLines, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
const { contentLines, endLine, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
const signupMeta = parseSignupMeta(contentLines.join('\n'))
|
||||
blocks.push(attachSourceRange(
|
||||
createBlock('signup', '', null, `block-${blocks.length}`, { meta: signupMeta }),
|
||||
startLine,
|
||||
nextIndex
|
||||
endLine
|
||||
))
|
||||
index = nextIndex
|
||||
continue
|
||||
@@ -519,7 +520,7 @@ const parseMarkdownBlocks = (markdown) => {
|
||||
|
||||
if (trimmedLine === ':::gallery') {
|
||||
const startLine = index
|
||||
const { contentLines, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
const { contentLines, endLine, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
const images = []
|
||||
|
||||
contentLines.forEach((contentLine) => {
|
||||
@@ -531,7 +532,7 @@ const parseMarkdownBlocks = (markdown) => {
|
||||
|
||||
blocks.push(attachSourceRange(createBlock('gallery', '', null, `block-${blocks.length}`, {
|
||||
images
|
||||
}), startLine, nextIndex))
|
||||
}), startLine, endLine))
|
||||
index = nextIndex
|
||||
continue
|
||||
}
|
||||
@@ -539,13 +540,13 @@ const parseMarkdownBlocks = (markdown) => {
|
||||
if ([':::video', ':::audio', ':::file'].includes(trimmedLine)) {
|
||||
const startLine = index
|
||||
const blockType = trimmedLine.replace(':::', '')
|
||||
const { contentLines, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
const { contentLines, endLine, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
const mediaMeta = parseMediaMeta(contentLines.join('\n'))
|
||||
|
||||
blocks.push(attachSourceRange(
|
||||
createBlock(blockType, '', null, `block-${blocks.length}`, { meta: mediaMeta }),
|
||||
startLine,
|
||||
nextIndex
|
||||
endLine
|
||||
))
|
||||
|
||||
index = nextIndex
|
||||
@@ -554,11 +555,11 @@ const parseMarkdownBlocks = (markdown) => {
|
||||
|
||||
if (trimmedLine.startsWith(':::callout')) {
|
||||
const startLine = index
|
||||
const { contentLines, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
const { contentLines, endLine, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
blocks.push(attachSourceRange(
|
||||
createBlock('callout', contentLines.join('\n'), null, `block-${blocks.length}`, parseCalloutOptions(trimmedLine)),
|
||||
startLine,
|
||||
nextIndex
|
||||
endLine
|
||||
))
|
||||
index = nextIndex
|
||||
continue
|
||||
@@ -566,12 +567,12 @@ const parseMarkdownBlocks = (markdown) => {
|
||||
|
||||
if (trimmedLine.startsWith(':::toggle')) {
|
||||
const startLine = index
|
||||
const { contentLines, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
const { contentLines, endLine, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
const toggleOptions = parseToggleOpenerLine(trimmedLine)
|
||||
blocks.push(attachSourceRange(
|
||||
createBlock('toggle', contentLines.join('\n'), null, `block-${blocks.length}`, toggleOptions),
|
||||
startLine,
|
||||
nextIndex
|
||||
endLine
|
||||
))
|
||||
index = nextIndex
|
||||
continue
|
||||
@@ -579,11 +580,11 @@ const parseMarkdownBlocks = (markdown) => {
|
||||
|
||||
if (trimmedLine === ':::embed') {
|
||||
const startLine = index
|
||||
const { contentLines, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
const { contentLines, endLine, nextIndex } = collectFencedLines(lines, index + 1)
|
||||
blocks.push(attachSourceRange(
|
||||
createBlock('embed', '', null, `block-${blocks.length}`, { url: contentLines.join('\n').trim() }),
|
||||
startLine,
|
||||
nextIndex
|
||||
endLine
|
||||
))
|
||||
index = nextIndex
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user