인라인 텍스트 색상 패널 추가
This commit is contained in:
@@ -1,5 +1,15 @@
|
||||
/** @type {string[]} 인라인 색상 프리셋 */
|
||||
export const INLINE_COLOR_NAMES = ['gray', 'blue', 'green', 'yellow', 'red', 'purple']
|
||||
|
||||
/** @type {RegExp} 인라인 마크다운 패턴 */
|
||||
const INLINE_MARKDOWN_RE = /(\$([^$\n]+?)\$|\[([^\]]+)\]\((https?:\/\/[^)\s]+|\/[^)\s]+)\)|\*\*([^*]+)\*\*|`([^`]+)`|\*([^*]+)\*)/g
|
||||
const INLINE_MARKDOWN_RE = /(::(gray|blue|green|yellow|red|purple)\[((?:\\\]|[^\]\n])+)\]|\$([^$\n]+?)\$|\[([^\]]+)\]\((https?:\/\/[^)\s]+|\/[^)\s]+)\)|\*\*([^*]+)\*\*|`([^`]+)`|\*([^*]+)\*)/g
|
||||
|
||||
/**
|
||||
* 지원하는 인라인 색상인지 확인한다.
|
||||
* @param {string|null|undefined} color - 색상 이름
|
||||
* @returns {boolean} 지원 여부
|
||||
*/
|
||||
export const isInlineColorName = (color) => INLINE_COLOR_NAMES.includes(String(color || '').trim().toLowerCase())
|
||||
|
||||
/**
|
||||
* HTML 특수문자 이스케이프
|
||||
@@ -126,10 +136,23 @@ const formatScriptMarkdown = (marker, value) => {
|
||||
return `$${marker}${body}$`
|
||||
}
|
||||
|
||||
/**
|
||||
* 색상 인라인 마크다운을 만든다.
|
||||
* @param {string} color - 색상 이름
|
||||
* @param {string} value - 표시 텍스트
|
||||
* @returns {string} 색상 인라인 마크다운
|
||||
*/
|
||||
export const formatInlineColorMarkdown = (color, value) => {
|
||||
const normalizedColor = String(color || '').trim().toLowerCase()
|
||||
const safeColor = isInlineColorName(normalizedColor) ? normalizedColor : 'red'
|
||||
const text = String(value || '').replaceAll(']', '\\]')
|
||||
return `::${safeColor}[${text}]`
|
||||
}
|
||||
|
||||
/**
|
||||
* 인라인 마크다운을 표시 세그먼트로 변환한다.
|
||||
* @param {string} value - 원본 문자열
|
||||
* @returns {Array<{ type: string, text: string, href?: string }>} 인라인 세그먼트
|
||||
* @returns {Array<{ type: string, text: string, href?: string, color?: string }>} 인라인 세그먼트
|
||||
*/
|
||||
export const parseInlineSegments = (value) => {
|
||||
const source = String(value || '')
|
||||
@@ -146,29 +169,35 @@ export const parseInlineSegments = (value) => {
|
||||
})
|
||||
}
|
||||
|
||||
if (match[2]) {
|
||||
segments.push(...parseObsidianMathSegments(match[2]))
|
||||
} else if (match[3] && match[4]) {
|
||||
if (match[2] && match[3]) {
|
||||
segments.push({
|
||||
type: 'color',
|
||||
color: match[2],
|
||||
text: match[3].replaceAll('\\]', ']')
|
||||
})
|
||||
} else if (match[4]) {
|
||||
segments.push(...parseObsidianMathSegments(match[4]))
|
||||
} else if (match[5] && match[6]) {
|
||||
segments.push({
|
||||
type: 'link',
|
||||
text: match[3],
|
||||
href: match[4]
|
||||
})
|
||||
} else if (match[5]) {
|
||||
segments.push({
|
||||
type: 'strong',
|
||||
text: match[5]
|
||||
})
|
||||
} else if (match[6]) {
|
||||
segments.push({
|
||||
type: 'code',
|
||||
text: match[6]
|
||||
text: match[5],
|
||||
href: match[6]
|
||||
})
|
||||
} else if (match[7]) {
|
||||
segments.push({
|
||||
type: 'em',
|
||||
type: 'strong',
|
||||
text: match[7]
|
||||
})
|
||||
} else if (match[8]) {
|
||||
segments.push({
|
||||
type: 'code',
|
||||
text: match[8]
|
||||
})
|
||||
} else if (match[9]) {
|
||||
segments.push({
|
||||
type: 'em',
|
||||
text: match[9]
|
||||
})
|
||||
}
|
||||
|
||||
lastIndex = INLINE_MARKDOWN_RE.lastIndex
|
||||
@@ -187,7 +216,7 @@ export const parseInlineSegments = (value) => {
|
||||
|
||||
/**
|
||||
* 인라인 세그먼트를 HTML 문자열로 변환한다.
|
||||
* @param {Array<{ type: string, text: string, href?: string }>} segments - 세그먼트
|
||||
* @param {Array<{ type: string, text: string, href?: string, color?: string }>} segments - 세그먼트
|
||||
* @returns {string} HTML
|
||||
*/
|
||||
export const segmentsToInlineHtml = (segments) => segments.map((segment) => {
|
||||
@@ -215,6 +244,12 @@ export const segmentsToInlineHtml = (segments) => segments.map((segment) => {
|
||||
return `<a href="${escapeHtml(segment.href || '')}">${escapeHtml(segment.text)}</a>`
|
||||
}
|
||||
|
||||
if (segment.type === 'color' && isInlineColorName(segment.color)) {
|
||||
const color = String(segment.color || '').trim().toLowerCase()
|
||||
const nestedHtml = segmentsToInlineHtml(parseInlineSegments(segment.text))
|
||||
return `<span data-inline-color="${escapeHtml(color)}">${nestedHtml}</span>`
|
||||
}
|
||||
|
||||
return escapeHtml(segment.text)
|
||||
}).join('')
|
||||
|
||||
@@ -278,6 +313,11 @@ export const convertHtmlInlineNodeToMarkdown = (node) => {
|
||||
return href ? `[${childText || href}](${href})` : childText
|
||||
}
|
||||
|
||||
if (tagName === 'span') {
|
||||
const inlineColor = element.getAttribute('data-inline-color')
|
||||
return isInlineColorName(inlineColor) ? formatInlineColorMarkdown(inlineColor, childText) : childText
|
||||
}
|
||||
|
||||
if (tagName === 'img') {
|
||||
const src = element.getAttribute('src')
|
||||
const alt = element.getAttribute('alt') || ''
|
||||
@@ -288,7 +328,7 @@ export const convertHtmlInlineNodeToMarkdown = (node) => {
|
||||
return '\n'
|
||||
}
|
||||
|
||||
if (tagName === 'div' || tagName === 'p' || tagName === 'span') {
|
||||
if (tagName === 'div' || tagName === 'p') {
|
||||
return childText
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user