From 648ce5fbab68d93cba12b5ea1c815dd1cadf2b80 Mon Sep 17 00:00:00 2001 From: zenn Date: Thu, 4 Jun 2026 15:12:46 +0900 Subject: [PATCH] =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=20=ED=95=9C?= =?UTF-8?q?=EA=B8=80=20Enter=20=EC=A4=91=EB=B3=B5=20=EB=B3=B4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/ContentMarkdownEditableInline.vue | 24 +++++++++++++------ .../content/ContentMarkdownRenderer.vue | 1 + docs/changelog.md | 6 +++++ docs/deploy.md | 8 +++++++ docs/map.md | 2 +- docs/spec.md | 1 + docs/update.md | 6 +++++ package-lock.json | 4 ++-- package.json | 2 +- 9 files changed, 43 insertions(+), 11 deletions(-) diff --git a/components/content/ContentMarkdownEditableInline.vue b/components/content/ContentMarkdownEditableInline.vue index 42fa721..ffac635 100644 --- a/components/content/ContentMarkdownEditableInline.vue +++ b/components/content/ContentMarkdownEditableInline.vue @@ -70,6 +70,11 @@ const props = defineProps({ type: Boolean, default: false }, + /** 마지막 줄 아래 방향키로 외부 줄을 만들지 여부 */ + arrowExitCreatesLine: { + type: Boolean, + default: false + }, /** ESC 등으로 이 줄의 슬래시 메뉴를 닫은 상태(문자 `/`는 유지) */ slashCommandSuppressed: { type: Boolean, @@ -103,8 +108,8 @@ const suppressBlurCommit = ref(false) const splitLock = ref(false) /** 조합 중 Enter 후 compositionend에서 분리할지 */ const pendingSplitAfterComposition = ref(false) -/** 조합 종료 직후 중복 Enter를 무시할 기준 시각 */ -const suppressComposedEnterUntil = ref(0) +/** 조합 종료 직후 중복 Enter를 1회 무시할지 */ +const suppressNextEnterAfterComposition = ref(false) const showingRaw = ref(false) /** @returns {string} Enter 동작 모드 */ @@ -528,7 +533,10 @@ const navigateToAdjacentBlock = (direction, column, caretMode = 'column') => { const target = elements[currentIndex + direction] if (!target) { - if (['insert-below', 'multiline'].includes(resolvedEnterMode.value) && direction === 1) { + if ( + direction === 1 + && (resolvedEnterMode.value === 'multiline' || props.arrowExitCreatesLine) + ) { emit('insert-below', buildInsertBelowPayload()) } @@ -873,11 +881,11 @@ const onKeydown = (event) => { if ( event.key === 'Enter' && !event.shiftKey - && suppressComposedEnterUntil.value - && Date.now() < suppressComposedEnterUntil.value + && suppressNextEnterAfterComposition.value ) { event.preventDefault() event.stopPropagation() + suppressNextEnterAfterComposition.value = false return } @@ -898,7 +906,6 @@ const onKeydown = (event) => { if (event.isComposing || event.keyCode === 229) { pendingSplitAfterComposition.value = true - suppressComposedEnterUntil.value = Date.now() + 240 return } @@ -936,7 +943,6 @@ const onCompositionEnd = () => { } pendingSplitAfterComposition.value = false - suppressComposedEnterUntil.value = Date.now() + 240 const enterMode = showingRaw.value ? 'insert-below' : resolvedEnterMode.value if (enterMode !== 'split-paragraph' && enterMode !== 'insert-below') { @@ -944,7 +950,11 @@ const onCompositionEnd = () => { } nextTick(() => { + suppressNextEnterAfterComposition.value = true scheduleEnterAction(enterMode === 'split-paragraph' ? 'split' : 'insert-below') + window.setTimeout(() => { + suppressNextEnterAfterComposition.value = false + }, 1200) }) } diff --git a/components/content/ContentMarkdownRenderer.vue b/components/content/ContentMarkdownRenderer.vue index 7fa2a01..1a170a6 100644 --- a/components/content/ContentMarkdownRenderer.vue +++ b/components/content/ContentMarkdownRenderer.vue @@ -2469,6 +2469,7 @@ onBeforeUnmount(() => { :model-value="quoteLine.text" enter-mode="insert-below" allow-raw-toggle + arrow-exit-creates-line :raw-line="getMarkdownLine(quoteLine.sourceLine)" :source-line="quoteLine.sourceLine" @commit="onQuoteLineInlineCommit(block, quoteLine.sourceIndex, $event)" diff --git a/docs/changelog.md b/docs/changelog.md index b23b789..2987d1d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,11 @@ # 업데이트 요약 +## v1.5.51 + +- 라이브 인용·콜아웃에서 한글 입력 후 Enter가 줄을 2개 만들던 문제를 다시 보정했다. +- 콜아웃 마지막 줄에서 아래 방향키를 눌러도 새 본문 줄이 생기지 않도록 했다. +- 인용은 마지막 줄 아래 방향키에서만 외부 문단을 만들도록 동작을 분리했다. + ## v1.5.50 - 라이브 작성 모드에서 한글 인용문 Enter가 외부 문단으로 빠지지 않고 다음 인용 줄을 만들도록 보강했다. diff --git a/docs/deploy.md b/docs/deploy.md index 02ed8bc..5ac1d7a 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -68,6 +68,14 @@ docker exec sori-studio-db pg_isready -U sori_studio -d sori_studio docker exec sori-studio-db psql -U sori_studio -d sori_studio -c 'SELECT count(*) AS posts_count FROM posts;' ``` +### v1.5.51 참고 + +- 추가 DB 마이그레이션은 없다. +- 라이브 인용 안에서 한글 입력 후 Enter 시 인용 줄이 1줄만 추가되는지 확인한다. +- 라이브 콜아웃 안에서 한글 입력 후 Enter 시 콜아웃 본문 줄이 1줄만 추가되는지 확인한다. +- 라이브 콜아웃 마지막 줄에서 아래 방향키 입력 시 새 본문 줄이 생성되지 않는지 확인한다. +- 라이브 인용 마지막 줄에서 아래 방향키 입력 시 외부 빈 문단이 생성되고 커서가 이동하는지 확인한다. + ### v1.5.50 참고 - 추가 DB 마이그레이션은 없다. diff --git a/docs/map.md b/docs/map.md index 38f5f8d..b3c1aab 100644 --- a/docs/map.md +++ b/docs/map.md @@ -111,7 +111,7 @@ | 파일 | 화면 위치 | |------|-----------| | components/content/ContentRenderer.vue | 게시물/페이지 본문 | -| components/content/ContentMarkdownRenderer.vue | 마크다운 문자열 기반 본문 렌더링, 문단 text-base(16px), 빈 줄 spacer 보존·hard break `
` 처리, 확장 블록 파싱, 인용 배경 옵션(`> [!bg=...]`), 라이브 콜아웃·인용 포커스 기반 오른쪽 설정 패널 연결, 라이브 인용 Enter 줄 추가·마지막 줄 아래 방향키 이탈, 라이브 방향키 이동 시 편집 가능한 줄·카드형 블록 탐색, 라이브 코드·콜아웃·토글 내부 줄 삭제와 마지막 줄 블록 삭제, 라이브 이미지·갤러리 드래그 병합·추가·분리 UI, 갤러리 비율 기반 행 레이아웃, 라이브 갤러리 개별 이미지 편집·삭제, 리스트 마커 파란 계열 통일 | +| components/content/ContentMarkdownRenderer.vue | 마크다운 문자열 기반 본문 렌더링, 문단 text-base(16px), 빈 줄 spacer 보존·hard break `
` 처리, 확장 블록 파싱, 인용 배경 옵션(`> [!bg=...]`), 라이브 콜아웃·인용 포커스 기반 오른쪽 설정 패널 연결, 라이브 인용 Enter 줄 추가·마지막 줄 아래 방향키 이탈, 콜아웃 아래 방향키 새 줄 생성 차단, 라이브 방향키 이동 시 편집 가능한 줄·카드형 블록 탐색, 라이브 코드·콜아웃·토글 내부 줄 삭제와 마지막 줄 블록 삭제, 라이브 이미지·갤러리 드래그 병합·추가·분리 UI, 갤러리 비율 기반 행 레이아웃, 라이브 갤러리 개별 이미지 편집·삭제, 리스트 마커 파란 계열 통일 | | components/content/ProseHeading.vue | h1~h6 제목, 기본 mt-12 제거 | | components/content/ProseImage.vue | 본문 내 이미지, 로드 실패·빈 URL placeholder | | components/content/ProseList.vue | 목록 | diff --git a/docs/spec.md b/docs/spec.md index e1f6efc..6ff7d23 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -628,6 +628,7 @@ components/content/ - `ProseImage`는 URL이 비어 있거나 로드에 실패해도 최소 높이 placeholder와 「이미지를 불러올 수 없음」 안내를 표시해 라이브 모드에서 블록 선택·편집이 가능하다. - 인용(`>`) 블록은 첫 인용 줄에 `> [!bg=yellow]` 또는 `> {bg=yellow}` 옵션 줄을 두면 해당 줄은 숨기고 블록 배경을 바꾼다. 지원 배경 프리셋은 콜아웃과 같은 `gray`, `blue`, `green`, `yellow`, `red`, `purple`이며, 옵션이 없으면 회색 기본 인용 스타일을 쓴다. - 관리자 **라이브 모드**(미리보기) 인라인 편집: 문단·빈 줄·제목·인용·목록·코드 블록·콜아웃·토글을 렌더 스타일 그대로 contenteditable로 수정한다. blur·문단 이동(방향키) 시 편집 영역의 ``·`` 등을 `**`·`*` 마크다운으로 다시 직렬화해 저장한다. **Enter**·**Shift+Enter** 모두 다음 문단(블록) 분리. 문단 안 `/`로 슬래시 명령 메뉴(`/image`+Enter 이미지 삽입 등). **소스(작성) 모드** textarea에서도 동일한 `/` 슬래시 메뉴를 사용하며, 상단 마크다운 툴바는 두지 않는다. 슬래시 기본 제목은 **h2·h3·h4**만 표시하며, 본문 **h1**은 `/h1` 검색 시에만 삽입한다(게시물 **제목 필드**가 페이지의 유일한 h1). `Cmd+Shift+K`는 현재 줄을 삭제하며 코드·콜아웃·토글 블록 내부에서는 커서가 있는 본문 줄을 삭제하고, 남은 본문 줄이 1개뿐이면 fenced 블록 전체를 삭제한다. 콜아웃 옵션은 첫 줄 `:::callout emoji=💡 bg=blue`처럼 `emoji`·`bg`(gray|blue|green|yellow|red|purple)로 지정하며, 라이브 모드에서는 블록에 포커스가 들어오면 오른쪽 설정 패널에서 수정한다. 코드 블록은 ` ```언어`·`nolinenos`(줄 번호 숨김)를 지원한다. 라이브·공개 모두 `ProseCodeBlock`(`#15171a`, `px-4 py-3`, `text-sm leading-6`)으로 동일하게 표시한다. 라이브 모드 호버·포커스 시 Language 입력·줄번호 토글이 보인다. 공개 화면에는 언어 라벨 옆 **복사** 버튼으로 본문을 클립보드에 넣는다. 본문 하단 클릭으로 새 문단을 추가한다. +- 라이브 모드 인용·콜아웃 내부 Enter는 한글 IME 조합 확정 뒤에도 한 번만 줄을 추가한다. 인용 마지막 줄에서 아래 방향키를 누르면 외부 빈 문단을 만들 수 있지만, 콜아웃 아래 방향키는 본문 줄을 새로 만들지 않는다. - 이미지 파일을 붙여넣거나 드롭하면 관리자 업로드 API로 저장한 뒤 현재 커서 위치에 이미지 또는 갤러리 마크다운을 삽입한다. - 툴바 `이미지`·`갤러리`는 미디어 모달을 연다. 모달 기본 탭은 **미디어 라이브러리**이며 **업로드** 탭에서 드래그·파일 선택 후 즉시 삽입한다. - 미디어 라이브러리에서 단일 이미지를 선택하면 `![alt](url)` 형식으로 삽입한다. diff --git a/docs/update.md b/docs/update.md index 6b3def1..149e737 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,11 @@ # 업데이트 이력 +## v1.5.51 + +- 게시물 글쓰기: 라이브 모드 인용·콜아웃에서 한글 조합 Enter가 줄 추가를 중복 실행하지 않도록 보정. +- 게시물 글쓰기: 라이브 모드 콜아웃 마지막 줄 아래 방향키가 새 본문 줄을 만들지 않도록 수정. +- 게시물 글쓰기: 라이브 모드 인용 마지막 줄 아래 방향키만 외부 문단을 만들도록 분리. + ## v1.5.50 - 게시물 글쓰기: 라이브 모드 인용 Enter가 한글 조합 입력 뒤에도 내부 다음 인용 줄을 만들도록 수정. diff --git a/package-lock.json b/package-lock.json index 93d35bf..ab560d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sori.studio", - "version": "1.5.50", + "version": "1.5.51", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sori.studio", - "version": "1.5.50", + "version": "1.5.51", "hasInstallScript": true, "dependencies": { "@nuxtjs/tailwindcss": "^6.14.0", diff --git a/package.json b/package.json index f94260e..69455d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sori.studio", - "version": "1.5.50", + "version": "1.5.51", "private": true, "type": "module", "imports": {