v0.0.47 공개 본문 스타일 가이드 기반 정의

Ordered list, 멀티라인/대체 인용구 문법을 추가하고 Prose 컴포넌트(리스트/인용/이미지/카드/임베드) 기본 스타일을 Thred 톤으로 통일했다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-07 18:59:12 +09:00
parent 41406ca852
commit 5f2b2b8c4f
17 changed files with 125 additions and 35 deletions

View File

@@ -26,6 +26,8 @@ const createBlock = (type = 'paragraph', text = '', level = null, id = '', optio
url: options.url || '',
alt: options.alt || '',
title: options.title || '',
variant: options.variant || '',
ordered: options.ordered || false,
width: options.width || 'regular',
images: options.images || []
})
@@ -89,6 +91,20 @@ const parseMarkdownBlocks = (markdown) => {
continue
}
if (trimmedLine === '>>>') {
const contentLines = []
index += 1
while (index < lines.length && lines[index].trim() !== '<<<') {
contentLines.push(lines[index])
index += 1
}
blocks.push(createBlock('quote', contentLines.join('\n').trim(), null, `block-${blocks.length}`, { variant: 'alt' }))
index += 1
continue
}
if (trimmedLine === ':::gallery') {
const { contentLines, nextIndex } = collectFencedLines(lines, index + 1)
const images = []
@@ -155,7 +171,7 @@ const parseMarkdownBlocks = (markdown) => {
continue
}
const headingMatch = trimmedLine.match(/^(#{1,3})\s+(.+)$/)
const headingMatch = trimmedLine.match(/^(#{1,6})\s+(.+)$/)
if (headingMatch) {
blocks.push(createBlock('heading', headingMatch[2], headingMatch[1].length, `block-${blocks.length}`))
@@ -164,8 +180,14 @@ const parseMarkdownBlocks = (markdown) => {
}
if (trimmedLine.startsWith('> ')) {
blocks.push(createBlock('quote', trimmedLine.replace(/^>\s?/, ''), null, `block-${blocks.length}`))
index += 1
const quoteLines = []
while (index < lines.length && lines[index].trim().startsWith('>')) {
quoteLines.push(lines[index].trim().replace(/^>\s?/, ''))
index += 1
}
blocks.push(createBlock('quote', quoteLines.join('\n').trim(), null, `block-${blocks.length}`))
continue
}
@@ -181,6 +203,18 @@ const parseMarkdownBlocks = (markdown) => {
continue
}
if (/^\d+\.\s+/.test(trimmedLine)) {
const items = []
while (index < lines.length && /^\d+\.\s+/.test(lines[index].trim())) {
items.push(lines[index].trim().replace(/^\d+\.\s+/, ''))
index += 1
}
blocks.push(createBlock('list', items, null, `block-${blocks.length}`, { ordered: true }))
continue
}
blocks.push(createBlock('paragraph', trimmedLine, null, `block-${blocks.length}`))
index += 1
}
@@ -236,10 +270,10 @@ const showNextImage = () => {
<ProseHeading v-if="block.type === 'heading'" :level="block.level">
{{ block.text }}
</ProseHeading>
<ProseBlockquote v-else-if="block.type === 'quote'">
<ProseBlockquote v-else-if="block.type === 'quote'" :variant="block.variant || 'default'">
{{ block.text }}
</ProseBlockquote>
<ProseList v-else-if="block.type === 'list'">
<ProseList v-else-if="block.type === 'list'" :ordered="block.ordered || false">
<li v-for="(item, itemIndex) in block.text" :key="`${block.id}-${itemIndex}`">
{{ item }}
</li>
@@ -258,7 +292,7 @@ const showNextImage = () => {
<button
v-for="(image, imageIndex) in block.images"
:key="`${block.id}-${image.url}`"
class="content-markdown-renderer__gallery-button overflow-hidden rounded bg-surface"
class="content-markdown-renderer__gallery-button overflow-hidden rounded-[10px] border border-[var(--site-line)] bg-[var(--site-panel)]"
type="button"
@click="openLightbox(block.images, imageIndex)"
>
@@ -270,7 +304,7 @@ const showNextImage = () => {
class="content-markdown-renderer__code my-6 overflow-x-auto rounded bg-[#15171a] px-4 py-3 text-sm leading-6 text-white"
><code>{{ block.text }}</code></pre>
<hr v-else-if="block.type === 'divider'" class="content-markdown-renderer__divider my-10 border-line">
<p v-else class="content-markdown-renderer__paragraph my-5 leading-8">
<p v-else class="content-markdown-renderer__paragraph my-5 text-[15px] leading-8 text-[var(--site-text)]">
{{ block.text }}
</p>
</template>