미리보기 집중 모드와 빈 줄 공백 보존
This commit is contained in:
@@ -1002,7 +1002,7 @@ const handleKeydown = (event) => {
|
||||
|
||||
<template>
|
||||
<div ref="editorRootRef" class="admin-markdown-editor grid gap-3">
|
||||
<div class="admin-markdown-editor__toolbar flex flex-wrap items-center gap-1.5 rounded border border-[#e3e6e8] bg-white p-2">
|
||||
<div v-if="activeMode === 'write'" class="admin-markdown-editor__toolbar flex flex-wrap items-center gap-1.5 rounded border border-[#e3e6e8] bg-white p-2">
|
||||
<button class="admin-markdown-editor__tool rounded px-2.5 py-1.5 text-sm font-semibold text-[#394047] hover:bg-[#eff1f2]" type="button" @click="applyHeading(1)">
|
||||
H1
|
||||
</button>
|
||||
@@ -1077,7 +1077,7 @@ const handleKeydown = (event) => {
|
||||
<div class="admin-markdown-editor__editor-surface min-h-[620px]">
|
||||
<div
|
||||
ref="gutterRef"
|
||||
class="admin-markdown-editor__gutter absolute bottom-0 left-0 top-0 w-10 select-none overflow-y-auto overflow-x-hidden py-5 font-mono text-[13px] leading-7 text-[#a0a8b0]"
|
||||
class="admin-markdown-editor__gutter absolute bottom-0 left-0 top-0 w-10 select-none overflow-y-hidden overflow-x-hidden py-5 font-mono text-[13px] leading-7 text-[#a0a8b0]"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div
|
||||
@@ -1208,7 +1208,7 @@ const handleKeydown = (event) => {
|
||||
<div
|
||||
v-else
|
||||
ref="previewRef"
|
||||
class="admin-markdown-editor__preview min-h-[620px] rounded border border-[#e3e6e8] bg-white px-6 py-5 text-[#15171a]"
|
||||
class="admin-markdown-editor__preview min-h-[620px] px-0 py-5 text-[#15171a] outline-none"
|
||||
style="--site-text: #15171a; --site-muted: #6b7280; --site-panel: #f6f7f8; --site-line: #e3e6e8; --site-accent: #2eb6ea;"
|
||||
tabindex="0"
|
||||
>
|
||||
@@ -1275,3 +1275,13 @@ const handleKeydown = (event) => {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.admin-markdown-editor__gutter {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.admin-markdown-editor__gutter::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -138,6 +138,13 @@ const hasMarkdownHardBreak = (line) => / {2,}$/.test(line)
|
||||
*/
|
||||
const cleanParagraphLine = (line) => line.replace(/ {2,}$/, '').trim()
|
||||
|
||||
/**
|
||||
* 빈 줄 공백 블록 높이를 반환한다.
|
||||
* @param {Object} block - 렌더링 블록
|
||||
* @returns {string} Tailwind 높이 클래스
|
||||
*/
|
||||
const getSpacerHeightClass = (block) => block.meta?.legacy ? 'h-6' : 'h-8'
|
||||
|
||||
/**
|
||||
* 닫힘 표식까지의 행 목록을 반환
|
||||
* @param {Array<string>} lines - 전체 마크다운 행
|
||||
@@ -263,7 +270,14 @@ const parseMarkdownBlocks = (markdown) => {
|
||||
const line = lines[index]
|
||||
const trimmedLine = line.trim()
|
||||
|
||||
if (trimmedLine === BLANK_PARAGRAPH_MARKER || !trimmedLine) {
|
||||
if (trimmedLine === BLANK_PARAGRAPH_MARKER) {
|
||||
blocks.push(createBlock('spacer', '', null, `block-${blocks.length}`, { meta: { legacy: true } }))
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (!trimmedLine) {
|
||||
blocks.push(createBlock('spacer', '', null, `block-${blocks.length}`))
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
@@ -546,7 +560,8 @@ const showNextImage = () => {
|
||||
<template>
|
||||
<div class="content-markdown-renderer">
|
||||
<template v-for="block in blocks" :key="block.id">
|
||||
<ProseHeading v-if="block.type === 'heading'" :level="block.level">
|
||||
<div v-if="block.type === 'spacer'" class="content-markdown-renderer__spacer" :class="getSpacerHeightClass(block)" aria-hidden="true" />
|
||||
<ProseHeading v-else-if="block.type === 'heading'" :level="block.level">
|
||||
<template v-for="(segment, segmentIndex) in parseInlineSegments(block.text)" :key="`${block.id}-heading-${segmentIndex}`">
|
||||
<strong v-if="segment.type === 'strong'">{{ segment.text }}</strong>
|
||||
<em v-else-if="segment.type === 'em'">{{ segment.text }}</em>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# 업데이트 요약
|
||||
|
||||
## v1.0.18
|
||||
|
||||
- 여러 줄을 비워둔 경우 미리보기와 공개 본문에서도 비운 만큼 공백이 보이도록 보강.
|
||||
- 미리보기 모드에서 편집 툴바와 카드형 패널 외곽을 숨겨 본문만 보이게 정리.
|
||||
- 줄 번호 영역의 스크롤바를 숨겨 작성 화면을 더 차분하게 정리.
|
||||
|
||||
## v1.0.17
|
||||
|
||||
- 글쓰기 영역의 보더와 카드형 배경을 제거해 본문 편집 화면을 더 가볍게 정리.
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# 의사결정 이력
|
||||
|
||||
## 2026-05-14 v1.0.18
|
||||
|
||||
### 빈 줄 공백 보존과 미리보기 집중 모드
|
||||
|
||||
일반 Enter를 단일 줄 이동으로 유지하더라도, 작성자가 의도적으로 여러 줄을 비우면 미리보기와 공개 본문에서도 그만큼의 공백이 보여야 한다. 빈 줄을 문단 경계로만 소비하면 제목 아래나 문단 사이에 넣은 여백이 모두 사라지므로, 내용 없는 줄은 다시 spacer 블록으로 렌더링해 줄 수 정보를 보존한다. 다만 기본 문단 간격은 10px로 유지해 일반 문단 흐름과 의도적 공백을 분리한다.
|
||||
|
||||
미리보기 모드는 작성 도구가 아니라 결과 확인 화면이므로 툴바와 카드형 패널 외곽을 숨긴다. 작성 모드 줄 번호 거터는 본문 바깥 위치 안내로만 쓰고 스크롤바는 숨겨 편집 화면의 시각 잡음을 줄인다.
|
||||
|
||||
## 2026-05-14 v1.0.17
|
||||
|
||||
### textarea 기준 문단 입력 재조정
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
|------|-----------|
|
||||
| components/admin/AdminPostForm.vue | 관리자 글 작성/수정 폼, Ghost 스타일 전체 화면 에디터, 변경사항 기반 저장 버튼 활성화, 저장 클릭 시 전체 화면 발행 모달(행 접기/펼침, Ghost 동일 SVG 아이콘, 상태 요약 후 발행/초안/비공개 선택, 발행 시에만 시점 행·즉시/예약·datetime-local), 좌우 분할 설정 패널, 설정 패널 전환 애니메이션, Post URL 보기 액션, 중립 톤 삭제 액션, 대표 이미지 hover 액션과 업로드/미디어 선택 확정, 제목 IME Enter 가드, SVG 닫기 아이콘 배지형 태그 입력(한글 유지), 로컬 자동 저장(툴바 상태 옆 복원·무시), 미저장 변경사항 이탈 확인, 검색 노출 제외(noindex), 저장 시 제목·요약을 SEO 메타로 반영, 미리보기 요청 |
|
||||
| components/admin/AdminPageForm.vue | 관리자 페이지 작성/수정 폼, 대표 이미지 선택 |
|
||||
| components/admin/AdminMarkdownEditor.vue | 관리자 글 Markdown-first 에디터, textarea 기반 범위 선택·복사/붙여넣기, HTML 클립보드 마크다운 변환, Enter 새 문단·Shift+Enter hard break 줄바꿈 입력, 작성 모드 왼쪽 바깥 absolute 논리 줄 번호 거터·거터 스크롤 동기화, 작성/미리보기 전환(`Cmd/Ctrl+E`)과 커서 복원, 툴바 마크다운 삽입, 이미지·갤러리 업로드 및 미디어 라이브러리 삽입, 현재 이미지·갤러리 편집 패널 |
|
||||
| components/admin/AdminMarkdownEditor.vue | 관리자 글 Markdown-first 에디터, textarea 기반 범위 선택·복사/붙여넣기, HTML 클립보드 마크다운 변환, Enter 새 문단·Shift+Enter hard break 줄바꿈 입력, 작성 모드 왼쪽 바깥 absolute 논리 줄 번호 거터·거터 스크롤 동기화·스크롤바 숨김, 작성/미리보기 전환(`Cmd/Ctrl+E`)과 커서 복원, 작성 모드 툴바 마크다운 삽입, 미리보기 모드 툴바 숨김, 이미지·갤러리 업로드 및 미디어 라이브러리 삽입, 현재 이미지·갤러리 편집 패널 |
|
||||
| components/admin/AdminBlockEditor.vue | 관리자 글 블록형 에디터, 이미지/갤러리/콜아웃/토글/임베드 블록, 콜아웃 Emoji on/off·이모지 프리셋·배경 프리셋 선택(우측 고정 설정 패널), 갤러리 복수 미디어 선택·이미지 수별 열 배치·삽입 위치 표시 드래그 순서 변경, 한글 조합 입력 처리, Shift+Enter 줄바꿈, 코드 블록 단축 변환, AFFiNE 참고 세로 막대형 블록 핸들 선택/삭제/드래그 이동과 삽입선 표시, 하단 빈 입력 블록 유지, 본문 placeholder 표시 |
|
||||
| components/admin/AdminNavPrimaryBranch.vue | 관리자 상단 네비 트리(테이블·태그와 동일한 행 드래그 하이라이트, 하위·삭제) |
|
||||
| components/admin/AdminTagForm.vue | 관리자 태그 생성/수정 폼(이름/슬러그/설명/색상만 편집) |
|
||||
@@ -83,7 +83,7 @@
|
||||
| 파일 | 화면 위치 |
|
||||
|------|-----------|
|
||||
| components/content/ContentRenderer.vue | 게시물/페이지 본문 |
|
||||
| components/content/ContentMarkdownRenderer.vue | 마크다운 문자열 기반 본문 렌더링, 빈 줄 문단 경계·hard break `<br>` 처리, 확장 블록 파싱 |
|
||||
| components/content/ContentMarkdownRenderer.vue | 마크다운 문자열 기반 본문 렌더링, 빈 줄 spacer 보존·hard break `<br>` 처리, 확장 블록 파싱 |
|
||||
| components/content/ProseHeading.vue | h1~h6 제목 |
|
||||
| components/content/ProseImage.vue | 본문 내 이미지 |
|
||||
| components/content/ProseList.vue | 목록 |
|
||||
|
||||
@@ -184,7 +184,7 @@ components/content/
|
||||
- 관리자 Markdown-first 에디터에서 일반 Enter는 브라우저 기본 단일 줄 이동으로 새 문단을 만든다.
|
||||
- Shift+Enter는 같은 문단 안 줄바꿈을 위해 마크다운 hard break(`공백 2개 + \\n`)를 삽입한다.
|
||||
- 공개 본문 렌더러는 hard break가 있는 행만 같은 문단으로 묶고 `<br>`로 표시한다.
|
||||
- 빈 줄과 레거시 빈 문단 마커(`<!--sori:blank-paragraph-->`)는 별도 간격 블록이 아니라 문단 경계로만 사용한다.
|
||||
- 내용 없는 빈 줄과 레거시 빈 문단 마커(`<!--sori:blank-paragraph-->`)는 spacer 블록으로 렌더링해 작성자가 비운 줄 수만큼 공백을 보존한다.
|
||||
- 문단과 제목 하단 기본 간격은 10px 기준으로 유지한다.
|
||||
- 카드류
|
||||
- Callout: `:::callout` ~ `:::` (왼쪽 강조선은 `var(--site-accent)`)
|
||||
@@ -447,13 +447,13 @@ components/content/
|
||||
### 관리자 글 편집
|
||||
|
||||
- 글 작성/수정 화면은 Markdown-first 에디터(`AdminMarkdownEditor`)를 사용한다.
|
||||
- 작성 모드 textarea 왼쪽 바깥에 **논리 줄 번호** 거터(`\\n` 기준 줄 수, 빈 본문은 1줄)를 absolute 영역으로 두고 textarea와 거터의 세로 스크롤을 동기화한다. 한 논리 줄이 화면에서 여러 줄로 줄바꿈될 때는 옵시디언·CodeMirror처럼 시각 줄마다 번호가 늘지 않으며, 논리 줄 단위로만 맞춘다.
|
||||
- 작성 모드 textarea 왼쪽 바깥에 **논리 줄 번호** 거터(`\\n` 기준 줄 수, 빈 본문은 1줄)를 absolute 영역으로 두고 textarea와 거터의 세로 스크롤을 동기화한다. 거터 스크롤바는 숨긴다. 한 논리 줄이 화면에서 여러 줄로 줄바꿈될 때는 옵시디언·CodeMirror처럼 시각 줄마다 번호가 늘지 않으며, 논리 줄 단위로만 맞춘다.
|
||||
- 저장 데이터는 기존 `content` 필드의 마크다운 문자열을 그대로 유지한다.
|
||||
- 관리자 게시물/페이지 저장 API는 레거시 블록 배열·객체 본문 값이 들어와도 마크다운 문자열로 정규화한 뒤 저장한다.
|
||||
- 본문 작성 모드는 textarea 기반으로 범위 선택, 다중 문단 복사/붙여넣기, 외부 마크다운 붙여넣기를 브라우저 기본 동작에 가깝게 처리한다.
|
||||
- 본문 작성 모드에서 일반 Enter는 새 문단, Shift+Enter는 같은 문단 안 줄바꿈으로 처리한다. 일반 Enter는 단일 줄 이동으로 보여야 하며, Shift+Enter는 저장 마크다운에 hard break를 남긴다.
|
||||
- 클립보드에 `text/html`이 있으면 제목, 문단, 목록, 인용, 코드, 링크, 굵게, 기울임, 이미지를 기본 마크다운 조각으로 변환해 삽입한다.
|
||||
- 본문 미리보기 모드는 공개 본문과 같은 `ContentMarkdownRenderer`를 사용한다.
|
||||
- 본문 미리보기 모드는 공개 본문과 같은 `ContentMarkdownRenderer`를 사용하며, 툴바와 카드형 패널 외곽을 숨겨 본문만 표시한다.
|
||||
- 툴바는 제목 1/2/3, 굵게, 기울임, 인라인 코드, 인용, 목록, 코드 블록, 구분선 삽입을 제공한다.
|
||||
- `Cmd/Ctrl+B`, `Cmd/Ctrl+I`는 현재 선택 텍스트에 각각 굵게, 기울임 마크다운을 적용한다.
|
||||
- `Cmd/Ctrl+E`는 작성 모드와 미리보기 모드를 전환하며, 미리보기에서 작성 모드로 돌아올 때 기존 커서 위치와 textarea 스크롤을 복원한다.
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# 업데이트 이력
|
||||
|
||||
## v1.0.18
|
||||
|
||||
- 공개 본문/관리자 미리보기 공통 `ContentMarkdownRenderer`가 내용 없는 빈 줄을 다시 spacer 블록으로 렌더링해 여러 줄을 비우면 비운 줄 수만큼 공백이 보이도록 수정.
|
||||
- 관리자 `AdminMarkdownEditor` 미리보기 모드에서 툴바를 숨기도록 수정.
|
||||
- 관리자 미리보기 패널의 보더·라운드·흰 배경 카드 처리를 제거.
|
||||
- 작성 모드 줄 번호 거터의 스크롤바를 숨기도록 정리.
|
||||
- 패키지 버전 `1.0.18`로 갱신.
|
||||
|
||||
## v1.0.17
|
||||
|
||||
- 관리자 `AdminMarkdownEditor` 작성 영역의 외곽 보더와 배경 카드 처리를 제거.
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sori.studio",
|
||||
"version": "1.0.17",
|
||||
"version": "1.0.18",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sori.studio",
|
||||
"version": "1.0.17",
|
||||
"version": "1.0.18",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sori.studio",
|
||||
"version": "1.0.17",
|
||||
"version": "1.0.18",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"imports": {
|
||||
|
||||
Reference in New Issue
Block a user