import { parseImageMarkdownLine } from './markdown-image.js' /** * fenced 블록 시작 줄 인덱스를 찾는다. * @param {string[]} lines - 본문 줄 목록 * @param {number} currentLine - 현재 줄 * @param {string} opener - 시작 토큰 * @returns {number} 시작 줄 또는 -1 */ const findFencedBlockStart = (lines, currentLine, opener) => { for (let index = currentLine; index >= 0; index -= 1) { if ((lines[index] || '').trim() === opener) { return index } if ((lines[index] || '').trim() === ':::') { break } } return -1 } /** * fenced 블록 종료 줄 인덱스를 찾는다. * @param {string[]} lines - 본문 줄 목록 * @param {number} startLine - 시작 줄 * @returns {number} 종료 줄 또는 -1 */ const findFencedBlockEnd = (lines, startLine) => { for (let index = startLine + 1; index < lines.length; index += 1) { if ((lines[index] || '').trim() === ':::') { return index } } return -1 } /** * 갤러리 fenced 블록을 파싱한다. * @param {string[]} lines - 본문 줄 목록 * @param {number} currentLine - 현재 줄 * @returns {{ kind: 'gallery', startLine: number, endLine: number, images: Array }|null} */ const resolveGalleryBlock = (lines, currentLine) => { const galleryStart = findFencedBlockStart(lines, currentLine, ':::gallery') if (galleryStart === -1) { return null } const galleryEnd = findFencedBlockEnd(lines, galleryStart) if (galleryEnd === -1 || currentLine > galleryEnd) { return null } return { kind: 'gallery', startLine: galleryStart, endLine: galleryEnd, images: lines .slice(galleryStart + 1, galleryEnd) .map(parseImageMarkdownLine) .filter(Boolean) } } /** * 임베드 fenced 블록을 파싱한다. * @param {string[]} lines - 본문 줄 목록 * @param {number} currentLine - 현재 줄 * @returns {{ kind: 'embed', startLine: number, endLine: number, url: string }|null} */ const resolveEmbedBlock = (lines, currentLine) => { const embedStart = findFencedBlockStart(lines, currentLine, ':::embed') if (embedStart === -1) { return null } const embedEnd = findFencedBlockEnd(lines, embedStart) if (embedEnd === -1 || currentLine > embedEnd) { return null } return { kind: 'embed', startLine: embedStart, endLine: embedEnd, url: lines.slice(embedStart + 1, embedEnd).join('\n').trim() } } /** * 커서 줄 기준 활성 블록 컨텍스트를 반환한다. * @param {string} markdown - 본문 마크다운 * @param {number} lineIndex - 현재 줄(0-based) * @returns {Object|null} 블록 컨텍스트 */ export const resolveActiveBlockContext = (markdown, lineIndex) => { const lines = String(markdown || '').split('\n') const currentLine = Math.min(Math.max(0, lineIndex), Math.max(0, lines.length - 1)) const activeImage = parseImageMarkdownLine(lines[currentLine] || '') if (activeImage) { return { kind: 'image', startLine: currentLine, endLine: currentLine, images: [activeImage] } } const gallery = resolveGalleryBlock(lines, currentLine) if (gallery) { return gallery } return resolveEmbedBlock(lines, currentLine) }