콜아웃 제목과 기본 아이콘 정리

This commit is contained in:
2026-06-04 15:40:43 +09:00
parent 67fbba3814
commit 94226423c7
17 changed files with 170 additions and 62 deletions

View File

@@ -30,18 +30,67 @@ export const CALLOUT_BACKGROUND_SWATCHES = QUOTE_BACKGROUND_SWATCHES
/** @type {string[]} */
export const CALLOUT_EMOJI_OPTIONS = ['💡', '⚠️', '❗', '✅', '📌', '🔥', '💬']
/**
* 옵션 선언부를 key=value 토큰 목록으로 분리한다.
* @param {string} input - 옵션 문자열
* @returns {string[]} 토큰 목록
*/
const tokenizeOptionString = (input) => {
const tokens = []
const pattern = /[^\s"']+(?:"[^"]*"|'[^']*')?/g
const matches = String(input ?? '').match(pattern)
return matches ? tokens.concat(matches) : tokens
}
/**
* 선언부 옵션 값을 정리한다.
* @param {string} value - 원본 값
* @returns {string} 정리된 값
*/
const normalizeOptionValue = (value) => {
const normalized = String(value ?? '').trim()
if ((normalized.startsWith('"') && normalized.endsWith('"'))
|| (normalized.startsWith("'") && normalized.endsWith("'"))) {
return normalized.slice(1, -1)
}
return normalized
}
/**
* 선언부 옵션 값을 직렬화한다.
* @param {string} value - 옵션 값
* @returns {string} 직렬화 값
*/
const serializeOptionValue = (value) => {
const normalized = String(value ?? '').trim()
if (!normalized) {
return ''
}
if (/[\s"']/.test(normalized)) {
return `"${normalized.replace(/"/g, '\\"')}"`
}
return normalized
}
/**
* 콜아웃 선언부 옵션을 파싱한다.
* @param {string} line - 콜아웃 선언 라인
* @returns {{ calloutEmojiEnabled: boolean, calloutEmoji: string, calloutBackground: string }}
* @returns {{ calloutEmojiEnabled: boolean, calloutEmoji: string, calloutBackground: string, title: string }}
*/
export const parseCalloutOptions = (line) => {
const options = {
calloutEmojiEnabled: true,
calloutEmojiEnabled: false,
calloutEmoji: '💡',
calloutBackground: 'blue'
calloutBackground: 'blue',
title: ''
}
const tokens = String(line ?? '').trim().split(/\s+/).slice(1)
const tokens = tokenizeOptionString(String(line ?? '').trim()).slice(1)
tokens.forEach((token) => {
const [rawKey, ...rawValueParts] = token.split('=')
@@ -51,7 +100,7 @@ export const parseCalloutOptions = (line) => {
}
const key = rawKey.toLowerCase()
const value = rawValueParts.join('=').trim()
const value = normalizeOptionValue(rawValueParts.join('='))
if (key === 'emoji') {
if (!value || value === 'none') {
@@ -67,6 +116,11 @@ export const parseCalloutOptions = (line) => {
if (key === 'bg' && CALLOUT_BACKGROUND_OPTIONS.includes(value)) {
options.calloutBackground = value
return
}
if (key === 'title') {
options.title = value
}
})
@@ -75,15 +129,17 @@ export const parseCalloutOptions = (line) => {
/**
* 콜아웃 선언 줄을 만든다.
* @param {{ calloutEmojiEnabled?: boolean, calloutEmoji?: string, calloutBackground?: string }} options - 옵션
* @param {{ calloutEmojiEnabled?: boolean, calloutEmoji?: string, calloutBackground?: string, title?: string }} options - 옵션
* @returns {string} 선언 줄
*/
export const buildCalloutOpenerLine = (options = {}) => {
const emojiEnabled = options.calloutEmojiEnabled !== false
const emojiEnabled = options.calloutEmojiEnabled === true
const emoji = emojiEnabled ? (options.calloutEmoji || '💡') : 'none'
const background = CALLOUT_BACKGROUND_OPTIONS.includes(options.calloutBackground)
? options.calloutBackground
: 'blue'
const title = serializeOptionValue(options.title)
const titleOption = title ? ` title=${title}` : ''
return `:::callout emoji=${emoji} bg=${background}`
return `:::callout emoji=${emoji} bg=${background}${titleOption}`
}