게시물 설정 사이드바 오버레이로 이미지·갤러리·임베드를 편집하고, 파일명 alt 토글과 패널 입력 중 닫힘 문제를 해결했다. Co-authored-by: Cursor <cursoragent@cursor.com>
125 lines
3.2 KiB
JavaScript
125 lines
3.2 KiB
JavaScript
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<Object> }|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)
|
|
}
|