브랜드 컬러 설정 추가 v1.5.36
This commit is contained in:
27
lib/brand-color.js
Normal file
27
lib/brand-color.js
Normal file
@@ -0,0 +1,27 @@
|
||||
export const DEFAULT_BRAND_COLOR = '#ff4f2e'
|
||||
|
||||
/**
|
||||
* 브랜드 컬러 형식이 올바른지 확인한다.
|
||||
* @param {unknown} value - 검사할 값
|
||||
* @returns {boolean} 유효한 색상 여부
|
||||
*/
|
||||
export const isValidBrandColor = (value) => /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test(String(value || '').trim())
|
||||
|
||||
/**
|
||||
* 브랜드 컬러를 6자리 hex 값으로 정규화한다.
|
||||
* @param {unknown} value - 정규화할 색상 값
|
||||
* @returns {string} 정규화된 색상
|
||||
*/
|
||||
export const normalizeBrandColor = (value) => {
|
||||
const color = String(value || '').trim().toLowerCase()
|
||||
|
||||
if (!isValidBrandColor(color)) {
|
||||
return DEFAULT_BRAND_COLOR
|
||||
}
|
||||
|
||||
if (color.length === 4) {
|
||||
return `#${color[1]}${color[1]}${color[2]}${color[2]}${color[3]}${color[3]}`
|
||||
}
|
||||
|
||||
return color
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { isImageUrl, parseImageMarkdownLine } from './markdown-image.js'
|
||||
import { CALLOUT_BACKGROUND_OPTIONS } from './markdown-callout.js'
|
||||
|
||||
/**
|
||||
* fenced 블록 시작 줄 인덱스를 찾는다.
|
||||
@@ -51,6 +52,87 @@ const isStandaloneUrlLine = (line) => /^https?:\/\/\S+$/i.test(String(line || ''
|
||||
*/
|
||||
const isStandaloneImageUrlLine = (line) => isStandaloneUrlLine(line) && isImageUrl(line)
|
||||
|
||||
/**
|
||||
* 인용 마커 줄인지 확인한다.
|
||||
* @param {string} line - 마크다운 줄
|
||||
* @returns {boolean} 인용 줄 여부
|
||||
*/
|
||||
const isQuoteMarkerLine = (line) => {
|
||||
const trimmed = String(line ?? '').trim()
|
||||
return trimmed === '>' || /^>\s/.test(trimmed)
|
||||
}
|
||||
|
||||
/**
|
||||
* 인용 마커를 제거한 본문을 반환한다.
|
||||
* @param {string} line - 마크다운 줄
|
||||
* @returns {string} 인용 본문
|
||||
*/
|
||||
const getQuoteLineBody = (line) => String(line ?? '').trim().replace(/^>\s?/, '')
|
||||
|
||||
/**
|
||||
* 인용 옵션 줄을 파싱한다.
|
||||
* @param {string} value - 인용 본문 줄
|
||||
* @returns {{ quoteBackground: string }|null} 인용 옵션
|
||||
*/
|
||||
const parseQuoteOptionsLine = (value) => {
|
||||
const raw = String(value ?? '').trim()
|
||||
const bracketMatch = raw.match(/^\[!(.+)\]$/)
|
||||
const braceMatch = raw.match(/^\{(.+)\}$/)
|
||||
const optionSource = bracketMatch?.[1] || braceMatch?.[1] || ''
|
||||
|
||||
if (!optionSource) {
|
||||
return null
|
||||
}
|
||||
|
||||
const tokens = optionSource.trim().split(/\s+/)
|
||||
let quoteBackground = ''
|
||||
|
||||
tokens.forEach((token) => {
|
||||
const [key, rawOptionValue] = token.split('=')
|
||||
const optionValue = String(rawOptionValue || '').trim()
|
||||
|
||||
if (key?.toLowerCase() === 'bg' && CALLOUT_BACKGROUND_OPTIONS.includes(optionValue)) {
|
||||
quoteBackground = optionValue
|
||||
}
|
||||
})
|
||||
|
||||
return quoteBackground ? { quoteBackground } : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 인용 블록을 파싱한다.
|
||||
* @param {string[]} lines - 본문 줄 목록
|
||||
* @param {number} currentLine - 현재 줄
|
||||
* @returns {{ kind: 'quote', startLine: number, endLine: number, quoteBackground: string, hasQuoteOptions: boolean }|null}
|
||||
*/
|
||||
const resolveQuoteBlock = (lines, currentLine) => {
|
||||
if (!isQuoteMarkerLine(lines[currentLine] || '')) {
|
||||
return null
|
||||
}
|
||||
|
||||
let startLine = currentLine
|
||||
let endLine = currentLine
|
||||
|
||||
while (startLine > 0 && isQuoteMarkerLine(lines[startLine - 1] || '')) {
|
||||
startLine -= 1
|
||||
}
|
||||
|
||||
while (endLine < lines.length - 1 && isQuoteMarkerLine(lines[endLine + 1] || '')) {
|
||||
endLine += 1
|
||||
}
|
||||
|
||||
const firstQuoteBody = getQuoteLineBody(lines[startLine] || '')
|
||||
const quoteOptions = parseQuoteOptionsLine(firstQuoteBody)
|
||||
|
||||
return {
|
||||
kind: 'quote',
|
||||
startLine,
|
||||
endLine,
|
||||
quoteBackground: quoteOptions?.quoteBackground || 'pink',
|
||||
hasQuoteOptions: Boolean(quoteOptions)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 갤러리 fenced 블록을 파싱한다.
|
||||
* @param {string[]} lines - 본문 줄 목록
|
||||
@@ -158,5 +240,11 @@ export const resolveActiveBlockContext = (markdown, lineIndex) => {
|
||||
return gallery
|
||||
}
|
||||
|
||||
const quote = resolveQuoteBlock(lines, currentLine)
|
||||
|
||||
if (quote) {
|
||||
return quote
|
||||
}
|
||||
|
||||
return resolveEmbedBlock(lines, currentLine)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user