v1.4.2: 라이브 이미지·갤러리 편집 UX와 공개 화면 색상 정리
라이브 모드 이미지·갤러리 드래그 병합·분리, 갤러리 개별 편집, 블록 패널 유지, 다크모드 인용·사이드바·리스트 마커 색상을 보정한다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { parseImageMarkdownLine } from './markdown-image.js'
|
||||
import { isImageUrl, parseImageMarkdownLine } from './markdown-image.js'
|
||||
|
||||
/**
|
||||
* fenced 블록 시작 줄 인덱스를 찾는다.
|
||||
@@ -44,6 +44,13 @@ const findFencedBlockEnd = (lines, startLine) => {
|
||||
*/
|
||||
const isStandaloneUrlLine = (line) => /^https?:\/\/\S+$/i.test(String(line || '').trim())
|
||||
|
||||
/**
|
||||
* 단독 이미지 URL 줄인지 확인한다.
|
||||
* @param {string} line - 마크다운 줄
|
||||
* @returns {boolean} 이미지 URL 여부
|
||||
*/
|
||||
const isStandaloneImageUrlLine = (line) => isStandaloneUrlLine(line) && isImageUrl(line)
|
||||
|
||||
/**
|
||||
* 갤러리 fenced 블록을 파싱한다.
|
||||
* @param {string[]} lines - 본문 줄 목록
|
||||
@@ -67,6 +74,9 @@ const resolveGalleryBlock = (lines, currentLine) => {
|
||||
kind: 'gallery',
|
||||
startLine: galleryStart,
|
||||
endLine: galleryEnd,
|
||||
selectedImageIndex: currentLine > galleryStart && currentLine < galleryEnd
|
||||
? currentLine - galleryStart - 1
|
||||
: null,
|
||||
images: lines
|
||||
.slice(galleryStart + 1, galleryEnd)
|
||||
.map(parseImageMarkdownLine)
|
||||
@@ -83,7 +93,7 @@ const resolveGalleryBlock = (lines, currentLine) => {
|
||||
const resolveEmbedBlock = (lines, currentLine) => {
|
||||
const standaloneUrl = String(lines[currentLine] || '').trim()
|
||||
|
||||
if (isStandaloneUrlLine(standaloneUrl)) {
|
||||
if (isStandaloneUrlLine(standaloneUrl) && !isStandaloneImageUrlLine(standaloneUrl)) {
|
||||
return {
|
||||
kind: 'embed',
|
||||
startLine: currentLine,
|
||||
@@ -122,6 +132,7 @@ 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] || '')
|
||||
const activeImageUrl = String(lines[currentLine] || '').trim()
|
||||
|
||||
if (activeImage) {
|
||||
return {
|
||||
@@ -132,6 +143,15 @@ export const resolveActiveBlockContext = (markdown, lineIndex) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (isStandaloneImageUrlLine(activeImageUrl)) {
|
||||
return {
|
||||
kind: 'image',
|
||||
startLine: currentLine,
|
||||
endLine: currentLine,
|
||||
images: [{ url: activeImageUrl, width: 'regular', caption: '', useAlt: false }]
|
||||
}
|
||||
}
|
||||
|
||||
const gallery = resolveGalleryBlock(lines, currentLine)
|
||||
|
||||
if (gallery) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/** @type {RegExp} 이미지 마크다운 한 줄 */
|
||||
const IMAGE_MARKDOWN_LINE_RE = /^!\[(.*?)\]\((\S+?)(?:\s+"((?:[^"\\]|\\.)*)")?\)(?:\{width=(regular|wide|full)\})?$/
|
||||
/** @type {RegExp} 이미지 파일 확장자 */
|
||||
const IMAGE_URL_EXTENSION_RE = /\.(?:jpe?g|png|webp|gif|avif|svg)(?:$|[?#])/i
|
||||
|
||||
/**
|
||||
* 캡션 문자열 이스케이프 해제
|
||||
@@ -55,6 +57,25 @@ export const getImageDefaultAltLabel = (url) => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 파일 URL인지 확인한다.
|
||||
* @param {string} url - 검사할 URL
|
||||
* @returns {boolean} 이미지 URL 여부
|
||||
*/
|
||||
export const isImageUrl = (url) => {
|
||||
const raw = String(url || '').trim()
|
||||
|
||||
if (!raw) {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
return IMAGE_URL_EXTENSION_RE.test(new URL(raw, 'https://sori.studio').pathname)
|
||||
} catch {
|
||||
return IMAGE_URL_EXTENSION_RE.test(raw)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 마크다운 한 줄 파싱
|
||||
* @param {string} line - 마크다운 줄
|
||||
|
||||
Reference in New Issue
Block a user