diff --git a/components/admin/AdminBlockEditor.vue b/components/admin/AdminBlockEditor.vue index 90dc844..8a34e9d 100644 --- a/components/admin/AdminBlockEditor.vue +++ b/components/admin/AdminBlockEditor.vue @@ -1236,6 +1236,46 @@ const updateBlockDropTarget = (event, targetIndex) => { dragTargetPosition.value = event.clientY < rect.top + rect.height / 2 ? 'before' : 'after' } +/** + * 블록 드래그 상태 초기화 + * @returns {void} + */ +const clearBlockDragState = () => { + draggingBlockId.value = '' + dragTargetIndex.value = -1 + dragTargetPosition.value = '' +} + +/** + * 현재 드롭 위치 기준으로 블록 이동 + * @param {string} draggedId - 이동할 블록 ID + * @param {number} targetIndex - 이동 대상 인덱스 + * @param {string} targetPosition - 이동 위치 + * @returns {boolean} 이동 여부 + */ +const moveDraggedBlock = (draggedId, targetIndex, targetPosition) => { + const sourceIndex = getBlockIndex(draggedId) + const insertionIndex = targetPosition === 'after' ? targetIndex + 1 : targetIndex + + if (sourceIndex < 0 || targetIndex < 0) { + return false + } + + const nextTargetIndex = sourceIndex < insertionIndex ? insertionIndex - 1 : insertionIndex + + if (sourceIndex === nextTargetIndex) { + return false + } + + const [draggedBlock] = editorBlocks.value.splice(sourceIndex, 1) + editorBlocks.value.splice(nextTargetIndex, 0, draggedBlock) + selectedBlockId.value = draggedBlock.id + normalizeTrailingTextBlock() + emitContent() + + return true +} + /** * 블록 드롭 이동 처리 * @param {DragEvent} event - 드롭 이벤트 @@ -1245,26 +1285,10 @@ const updateBlockDropTarget = (event, targetIndex) => { const dropBlock = (event, targetIndex) => { event.preventDefault() const draggedId = event.dataTransfer.getData('text/plain') || draggingBlockId.value - const sourceIndex = getBlockIndex(draggedId) const targetPosition = dragTargetIndex.value === targetIndex ? dragTargetPosition.value : 'after' - const insertionIndex = targetPosition === 'after' ? targetIndex + 1 : targetIndex - if (sourceIndex < 0 || targetIndex < 0) { - draggingBlockId.value = '' - dragTargetIndex.value = -1 - dragTargetPosition.value = '' - return - } - - const [draggedBlock] = editorBlocks.value.splice(sourceIndex, 1) - const nextTargetIndex = sourceIndex < insertionIndex ? insertionIndex - 1 : insertionIndex - editorBlocks.value.splice(nextTargetIndex, 0, draggedBlock) - draggingBlockId.value = '' - dragTargetIndex.value = -1 - dragTargetPosition.value = '' - selectedBlockId.value = draggedBlock.id - normalizeTrailingTextBlock() - emitContent() + moveDraggedBlock(draggedId, targetIndex, targetPosition) + clearBlockDragState() } /** @@ -1272,9 +1296,11 @@ const dropBlock = (event, targetIndex) => { * @returns {void} */ const finishBlockDrag = () => { - draggingBlockId.value = '' - dragTargetIndex.value = -1 - dragTargetPosition.value = '' + if (draggingBlockId.value && dragTargetIndex.value >= 0 && dragTargetPosition.value) { + moveDraggedBlock(draggingBlockId.value, dragTargetIndex.value, dragTargetPosition.value) + } + + clearBlockDragState() } /** @@ -1350,6 +1376,7 @@ defineExpose({ 'admin-block-editor__row--dragging opacity-50': draggingBlockId === block.id, 'admin-block-editor__row--drop-before': dragTargetIndex === index && dragTargetPosition === 'before', 'admin-block-editor__row--drop-after': dragTargetIndex === index && dragTargetPosition === 'after', + 'admin-block-editor__row--menu-open z-30': visibleCommands.length && activeBlockId === block.id, 'admin-block-editor__row--text': isTextBlock(block), 'admin-block-editor__row--structure': !isTextBlock(block) }" diff --git a/docs/history.md b/docs/history.md index 8b27b57..a3599d8 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,5 +1,15 @@ # 의사결정 이력 +## 2026-05-07 v0.0.41 + +### 명령 메뉴 계층과 개발 도구 표시 결정 + +관리자 블록 에디터의 `/` 명령 메뉴가 열린 행은 다른 블록 행보다 위 stacking 순서로 올린다. 메뉴가 절대 위치로 열릴 때 아래 블록의 텍스트가 같은 레이어에 남아 있으면 메뉴 배경 위로 겹쳐 보일 수 있기 때문이다. + +블록 이동은 `drop` 이벤트뿐 아니라 `dragend`에서도 현재 삽입선 위치를 기준으로 확정한다. 브라우저와 입력 요소 조합에 따라 contenteditable 주변에서 `drop` 이벤트가 안정적으로 들어오지 않을 수 있으므로, 사용자가 본 삽입선과 실제 결과가 어긋나지 않게 하기 위해서다. + +개발 서버의 Nuxt DevTools는 현재 관리자 글쓰기 전체 화면 QA를 방해하므로 기본 비활성화한다. 하단 검은 도킹 패널은 애플리케이션 UI가 아니라 개발 도구 영역이지만, 편집 화면 높이와 스크롤 문제를 확인할 때 혼동을 만들 수 있기 때문이다. + ## 2026-05-07 v0.0.40 ### 글쓰기 스크롤과 드래그 드롭 피드백 결정 diff --git a/docs/map.md b/docs/map.md index 1a7faf8..f4c8b05 100644 --- a/docs/map.md +++ b/docs/map.md @@ -156,7 +156,7 @@ | 파일 | 기능 | |------|------| | package.json | Nuxt 실행 스크립트와 의존성 | -| nuxt.config.js | Nuxt 앱 설정 | +| nuxt.config.js | Nuxt 앱 설정, Tailwind 모듈 연결, 관리자 QA를 위한 개발 도구 비활성화 | | tailwind.config.js | Tailwind 테마 설정 | | assets/css/main.css | 전역 스타일 | | composables/useMenuState.js | 좌측 메뉴 열림 상태 관리 | diff --git a/docs/spec.md b/docs/spec.md index 1467020..dba6399 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -277,6 +277,7 @@ components/content/ - 저장 데이터는 기존 `content` 필드의 마크다운 문자열을 유지한다. - `/` 입력 시 블록 선택 메뉴를 표시한다. - `/` 명령 메뉴는 화면 하단 공간이 부족하면 현재 블록 위쪽으로 표시한다. +- `/` 명령 메뉴가 열린 블록 행은 아래 블록보다 위 stacking 순서로 표시해 메뉴와 본문 텍스트가 겹쳐 보이지 않게 한다. - `/` 명령 메뉴가 열린 상태에서 Enter를 누르면 현재 강조된 메뉴 항목을 적용한다. - `/` 명령 메뉴가 열린 상태에서 위/아래 방향키로 강조 항목을 이동한다. - `/` 명령 메뉴 필터는 한글 조합 입력 완료와 방향키/Enter 입력 직전에 현재 DOM 텍스트를 기준으로 동기화한다. @@ -292,7 +293,7 @@ components/content/ - 블록 왼쪽 핸들은 hover/focus 상태에서 AFFiNE 참고 스타일의 세로 막대로 표시되며, hover 시 해당 블록 높이만큼 확장해 선택 범위를 드러낸다. - 블록 왼쪽 핸들을 클릭하면 블록을 선택하고 Delete 또는 Backspace로 해당 블록을 삭제할 수 있다. - 블록 왼쪽 핸들을 드래그하면 블록 순서를 이동할 수 있다. -- 블록 드래그 중에는 현재 포인터 위치 기준으로 대상 블록 위 또는 아래에 삽입선을 표시하고, 드롭 시 표시 위치와 같은 곳으로 이동한다. +- 블록 드래그 중에는 현재 포인터 위치 기준으로 대상 블록 위 또는 아래에 삽입선을 표시하고, 드롭 또는 드래그 종료 시 표시 위치와 같은 곳으로 이동한다. - 빈 블록 placeholder는 현재 활성 블록 또는 첫 빈 블록에만 표시한다. - 에디터 마지막에는 클릭 가능한 빈 문단 블록을 항상 유지하며, 해당 블록이 비어 있으면 저장 콘텐츠에는 포함하지 않는다. - 제목은 별도 라벨 영역이 아니라 에디터 상단의 큰 제목 입력으로 표시한다. @@ -451,6 +452,6 @@ APP_PORT=43118 ## 버전 관리 -- 현재 버전: v0.0.40 +- 현재 버전: v0.0.41 - 첫 커밋 이후 변경사항을 커밋할 때마다 패치 버전 증가 - 메이저/마이너 버전은 구조 변경 또는 기능 묶음 단위로 결정 diff --git a/docs/update.md b/docs/update.md index 3a0e5f3..33b3eea 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,13 @@ # 업데이트 이력 +## v0.0.41 + +- 관리자 블록 에디터 `/` 명령 메뉴가 아래 블록 텍스트와 겹쳐 보이던 문제 수정. +- 관리자 블록 에디터 드래그 종료 시 삽입선 위치 기준으로 블록 이동이 확정되도록 보정. +- 개발 서버에서 Nuxt DevTools 도킹 패널로 보이는 하단 검은 영역을 막기 위해 DevTools 비활성화. +- 기술 명세 현재 버전을 v0.0.41로 갱신. +- 패키지 버전을 0.0.41로 갱신. + ## v0.0.40 - 관리자 글쓰기 화면에서 바깥 문서가 함께 스크롤되어 하단 배경이 노출되던 문제 수정. diff --git a/nuxt.config.js b/nuxt.config.js index d5b6632..acae519 100644 --- a/nuxt.config.js +++ b/nuxt.config.js @@ -1,6 +1,9 @@ // https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ compatibilityDate: '2026-04-29', + devtools: { + enabled: false + }, modules: ['@nuxtjs/tailwindcss'], components: [ { diff --git a/package-lock.json b/package-lock.json index 8620d81..b05781b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sori.studio", - "version": "0.0.40", + "version": "0.0.41", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sori.studio", - "version": "0.0.40", + "version": "0.0.41", "hasInstallScript": true, "dependencies": { "@nuxtjs/tailwindcss": "^6.14.0", diff --git a/package.json b/package.json index 385d7e6..2bc81d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sori.studio", - "version": "0.0.40", + "version": "0.0.41", "private": true, "type": "module", "scripts": {