공개 Markdown 인라인 렌더러 fragment 주석 반복을 줄이도록 정리

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-01 11:50:52 +09:00
parent 1a7b82d9fd
commit d3f08e743d
7 changed files with 46 additions and 21 deletions

View File

@@ -37,29 +37,45 @@ const getInlineColorClass = (color) => `content-markdown-inline-segments__color-
</script>
<template>
<template v-for="(lineSegments, lineIndex) in segmentLines" :key="`inline-line-${lineIndex}`">
<br v-if="lineIndex > 0">
<template v-for="(segment, segmentIndex) in lineSegments" :key="`inline-segment-${lineIndex}-${segmentIndex}`">
<strong v-if="segment.type === 'strong'">{{ segment.text }}</strong>
<em v-else-if="segment.type === 'em'">{{ segment.text }}</em>
<sub v-else-if="segment.type === 'subscript'">{{ segment.text }}</sub>
<sup v-else-if="segment.type === 'superscript'">{{ segment.text }}</sup>
<code v-else-if="segment.type === 'code'" class="content-markdown-inline-segments__inline-code rounded bg-[#252525] px-1.5 py-0.5 text-[0.9em] text-white">{{ segment.text }}</code>
<a v-else-if="segment.type === 'link'" :class="linkClass" :href="segment.href" target="_blank" rel="noreferrer">{{ segment.text }}</a>
<span class="content-markdown-inline-segments">
<span
v-for="(lineSegments, lineIndex) in segmentLines"
:key="`inline-line-${lineIndex}`"
class="content-markdown-inline-segments__line"
>
<br v-if="lineIndex > 0">
<span
v-else-if="segment.type === 'color'"
class="content-markdown-inline-segments__color"
:class="getInlineColorClass(segment.color)"
:data-inline-color="segment.color"
v-for="(segment, segmentIndex) in lineSegments"
:key="`inline-segment-${lineIndex}-${segmentIndex}`"
class="content-markdown-inline-segments__segment"
>
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" />
<strong v-if="segment.type === 'strong'">{{ segment.text }}</strong>
<em v-else-if="segment.type === 'em'">{{ segment.text }}</em>
<sub v-else-if="segment.type === 'subscript'">{{ segment.text }}</sub>
<sup v-else-if="segment.type === 'superscript'">{{ segment.text }}</sup>
<code v-else-if="segment.type === 'code'" class="content-markdown-inline-segments__inline-code rounded bg-[#252525] px-1.5 py-0.5 text-[0.9em] text-white">{{ segment.text }}</code>
<a v-else-if="segment.type === 'link'" :class="linkClass" :href="segment.href" target="_blank" rel="noreferrer">{{ segment.text }}</a>
<span
v-else-if="segment.type === 'color'"
class="content-markdown-inline-segments__color"
:class="getInlineColorClass(segment.color)"
:data-inline-color="segment.color"
>
<ContentMarkdownInlineSegments :text="segment.text" :link-class="linkClass" />
</span>
<span v-else>{{ segment.text }}</span>
</span>
<template v-else>{{ segment.text }}</template>
</template>
</template>
</span>
</span>
</template>
<style scoped>
.content-markdown-inline-segments,
.content-markdown-inline-segments__line,
.content-markdown-inline-segments__segment {
display: contents;
}
.content-markdown-inline-segments__color--gray {
color: var(--site-muted);
}

View File

@@ -1,5 +1,9 @@
# 업데이트 요약
## v1.9.33
- 사용자 화면 Markdown 본문의 인라인 렌더링 구조를 정리해 Elements 탭에 반복 표시되던 Vue fragment 주석을 줄였다.
## v1.9.32
- Markdown 리스트 항목이 여러 줄일 때 점 표시가 첫 줄 기준으로 정렬되도록 보정했다.

View File

@@ -149,7 +149,7 @@
| components/content/ContentRenderer.vue | 게시물/페이지 본문 |
| components/content/ContentMarkdownRenderer.vue | 마크다운 문자열 기반 본문 렌더링, 문단 text-base(16px), 빈 줄 spacer 보존·hard break `<br>` 처리, 표준 마크다운 표 파싱·렌더링(셀 내부 인라인 링크와 `<br>` 줄바꿈 포함), 공개 게시물 인아티클 광고를 본문 길이에 따라 0~2회 삽입, Obsidian식 위첨자·아래첨자 인라인 렌더링, 확장 블록 파싱, `:::` fenced 블록 원본 범위 보정, 닫히지 않은 코드 펜스 하위 콘텐츠 보호, 인용 막대 색상 옵션(`> [!bg=...]`), 라이브 문단 `>` 즉시 인용 변환과 ` ``` `·`!!!` Enter 코드 블록·콜아웃 단축 생성, 라이브 콜아웃·인용 포커스 기반 오른쪽 설정 패널 연결, 라이브 인용·콜아웃 멀티라인 편집 줄 범위 포커스와 위/아래 방향키 외부 문단 이탈, 인용 Backspace 문단 복귀, 라이브 방향키 이동 시 편집 가능한 줄·카드형 블록 탐색, 라이브 코드·콜아웃·토글 내부 줄 삭제와 마지막 줄 블록 삭제, 라이브 이미지·갤러리 드래그 병합·추가·분리 UI, 갤러리 비율 기반 행 레이아웃, 라이브 이미지·갤러리 hover 삭제 액션, 리스트 마커 파란 계열 통일과 여러 줄 항목 첫 줄 기준 정렬 |
| components/content/ContentMarkdownEditableInline.vue | 라이브 모드 공통 인라인 편집 영역, 일반 인라인 마크다운·Obsidian식 첨자 렌더링과 plain text 멀티라인 본문·끝 줄바꿈 보존, 한글 IME 조합 확정 Enter의 블록별 동작 연결, 위/아래 인접 블록 이동 시 편집 스크롤 추적, Shift 위/아래 인접 블록 선택 확장·단계적 `Cmd/Ctrl+A` 처리, 멀티라인 Enter 텍스트 값 치환, 첫 줄 빈 줄 포함 줄바꿈 유지 |
| components/content/ContentMarkdownInlineSegments.vue | 공개·라이브 본문 공통 인라인 세그먼트 렌더링(링크·굵게·기울임·인라인 코드·첨자·인라인 색상) |
| components/content/ContentMarkdownInlineSegments.vue | 공개·라이브 본문 공통 인라인 세그먼트 렌더링(링크·굵게·기울임·인라인 코드·첨자·인라인 색상), 사용자 화면 fragment 주석 반복을 줄이는 단일 루트 구조 |
| lib/markdown-live-selection.js | 라이브 모드 Selection Bridge, 인접 contenteditable 범위 확장·블록/문서 전체 선택·교차 선택 삭제 마크다운 반영, 콜아웃·인용 전체 선택 삭제 시 빈 본문 줄 보존, Selection focus 기준 연속 확장 |
| components/content/ProseHeading.vue | h1~h6 제목, 기본 mt-12 제거 |
| components/content/ProseImage.vue | 본문 내 이미지, 로드 실패·빈 URL placeholder |

View File

@@ -88,6 +88,7 @@
- 댓글 아바타 이미지 로드 실패 시 이니셜 아바타로 즉시 대체한다.
- 공개 게시물 본문은 콘텐츠 타입별 컴포넌트로 분리해 추후 스타일 변경이 쉽도록 구성
- 공개 게시물 오른쪽 목차는 H1~H3 제목 표시 텍스트에서 링크·굵게·기울임·인라인 코드 같은 마크다운 장식만 제거하고 `~` 같은 일반 제목 문자는 보존한다.
- 공개 Markdown 인라인 렌더러는 단일 루트와 `display: contents`를 사용해 사용자 화면 DOM의 Vue fragment 주석 반복을 줄이고, 기존 인라인 흐름은 유지한다.
- Markdown 리스트 마커는 항목이 여러 줄로 줄바꿈되어도 항목 전체 높이 중앙이 아니라 첫 줄 기준으로 정렬한다.
- 표준 마크다운 표(`| 헤더 | ... |`, `| --- | ... |`)는 본문에서 가로 스크롤 가능한 HTML table로 렌더링한다. 정렬 구분선(`:---`, `:---:`, `---:`)은 각각 좌/중앙/우 정렬로 반영한다. 표 헤더와 셀 내부의 인라인 링크·굵게·기울임·인라인 코드를 렌더링하고, 셀 내부 `<br>` 또는 `<br/>`은 줄바꿈으로 표시한다.
- 인용문(`>`)은 왼쪽 세로 막대형 기본 스타일로 표시한다. 기본 인용 텍스트는 라이트·다크 모드 모두 사이트 본문 텍스트 색상(`--site-text`)을 따른다. 첫 줄 옵션 `> [!bg=blue]` 또는 `> {bg=blue}`는 인용 막대 색상으로 반영하며, 지원 값은 콜아웃과 같은 `gray`, `blue`, `green`, `yellow`, `red`, `purple`이다.

View File

@@ -1,5 +1,9 @@
# 업데이트 이력
## v1.9.33
- 공개 Markdown 인라인 렌더러의 fragment 주석 반복을 줄이도록 단일 루트 구조로 정리.
## v1.9.32
- Markdown 리스트 항목이 여러 줄일 때 마커가 첫 줄 기준으로 정렬되도록 수정.

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "sori.studio",
"version": "1.9.32",
"version": "1.9.33",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sori.studio",
"version": "1.9.32",
"version": "1.9.33",
"hasInstallScript": true,
"dependencies": {
"@nuxtjs/tailwindcss": "^6.14.0",

View File

@@ -1,6 +1,6 @@
{
"name": "sori.studio",
"version": "1.9.32",
"version": "1.9.33",
"private": true,
"type": "module",
"imports": {