diff --git a/components/admin/AdminMarkdownEditor.vue b/components/admin/AdminMarkdownEditor.vue index 9d3e70d..0dfca45 100644 --- a/components/admin/AdminMarkdownEditor.vue +++ b/components/admin/AdminMarkdownEditor.vue @@ -48,6 +48,7 @@ import { clampSlashHighlightedIndex, filterSlashCommands, getMediaPickerKindFromSlashAction, + getNextSlashHighlightedIndex, getSlashCommandFocusTarget, getSlashCommandTextareaSelection, insertMarkdownLinesAtSlashTarget, @@ -1461,11 +1462,11 @@ onMounted(() => { liveSlashKeyboardNav.value = false }, 200) - if (event.key === 'ArrowDown') { - liveSlashHighlightedIndex.value = (liveSlashHighlightedIndex.value + 1) % count - } else { - liveSlashHighlightedIndex.value = (liveSlashHighlightedIndex.value - 1 + count) % count - } + liveSlashHighlightedIndex.value = getNextSlashHighlightedIndex({ + currentIndex: liveSlashHighlightedIndex.value, + commandCount: count, + key: event.key + }) } return diff --git a/docs/map.md b/docs/map.md index 1a19615..cc54148 100644 --- a/docs/map.md +++ b/docs/map.md @@ -60,7 +60,7 @@ | lib/markdown-image.js | 이미지 마크다운 직렬화·파싱, 단독 이미지 URL 판별 | | lib/markdown-table.js | 공개 Markdown 표 행·구분선·정렬·블록 파싱 | | lib/markdown-toc.js | 공개 게시글 TOC용 H1~H3 제목 추출과 앵커 ID 생성 | -| lib/markdown-slash-commands.js | 관리자 Markdown-first 에디터 슬래시 명령 목록과 삽입 줄 정의, 미디어 명령 action·모달 종류 판별, 슬래시 메뉴 제외 줄 Set·하이라이트 인덱스 갱신, 미디어 명령 지연 commit 무시·슬래시 원문 줄 교체 삽입, 적용 후 포커스·textarea 선택 위치 계산 helper | +| lib/markdown-slash-commands.js | 관리자 Markdown-first 에디터 슬래시 명령 목록과 삽입 줄 정의, 미디어 명령 action·모달 종류 판별, 슬래시 메뉴 제외 줄 Set·하이라이트 인덱스·방향키 순환 갱신, 미디어 명령 지연 commit 무시·슬래시 원문 줄 교체 삽입, 적용 후 포커스·textarea 선택 위치 계산 helper | | lib/markdown-slash-menu-position.js | 관리자 Markdown-first 에디터 슬래시 메뉴 뷰포트 위치 보정 helper | | lib/markdown-slash-menu-scroll.js | 관리자 Markdown-first 에디터 슬래시 메뉴 선택 항목 표시용 scrollTop 계산 helper | | lib/markdown-inline.js | 공개·관리자 공통 인라인 마크다운 파서, 링크·굵게·기울임·인라인 코드·첨자·인라인 색상 토큰(`::color[텍스트]`) 변환, 링크·굵게·기울임·색상 내부 중첩 인라인 문법과 `
` 줄바꿈 처리, 라이브 멀티라인 본문의 기본 불릿/번호 목록 HTML 변환과 contenteditable 목록 마커 기준 커서 offset 계산·직렬화 | @@ -123,7 +123,7 @@ | tests/markdown-block-context.test.js | 관리자 블록 설정 패널의 활성 블록 컨텍스트 단위 회귀 테스트 | | tests/markdown-editor-history.test.js | 관리자 Markdown 에디터 undo/redo 히스토리 항목과 선택 범위 보정 단위 회귀 테스트 | | tests/markdown-editor-media.test.js | 관리자 Markdown 에디터 미디어 선택 helper의 종류·선택 가능 여부·미리보기·선택 상태·블록 생성 단위 회귀 테스트 | -| tests/markdown-slash-commands.test.js | 관리자 슬래시 명령 파싱·미디어 action·모달 종류·제외 줄 Set 갱신·하이라이트 인덱스 보정·포커스 대상·textarea 선택 위치·원문 줄 교체 삽입 단위 회귀 테스트 | +| tests/markdown-slash-commands.test.js | 관리자 슬래시 명령 파싱·미디어 action·모달 종류·제외 줄 Set 갱신·하이라이트 인덱스 보정·방향키 순환·포커스 대상·textarea 선택 위치·원문 줄 교체 삽입 단위 회귀 테스트 | | tests/markdown-slash-menu-scroll.test.js | 관리자 슬래시 메뉴 선택 항목 scrollTop 보정 단위 테스트 | | tests/markdown-block-parser.test.js | 공개 Markdown 본문 블록 파싱 단위 회귀 테스트 | | tests/markdown-fenced-meta.test.js | Markdown fenced 블록 시작·종료 줄 탐색, 본문 수집, 메타 파싱 단위 회귀 테스트 | diff --git a/docs/update.md b/docs/update.md index fe7b2f3..1e32ba3 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,10 @@ # 업데이트 이력 +## v1.11.91 + +- 관리자 Markdown 에디터의 슬래시 메뉴 방향키 순환 인덱스 계산을 `markdown-slash-commands` helper로 분리. +- 슬래시 메뉴 방향키 위·아래 순환 단위 테스트 추가. + ## v1.11.90 - 관리자 Markdown 에디터의 슬래시 메뉴 하이라이트 인덱스 보정을 `markdown-slash-commands` helper로 분리. diff --git a/lib/markdown-slash-commands.js b/lib/markdown-slash-commands.js index b78f052..8f8b21f 100644 --- a/lib/markdown-slash-commands.js +++ b/lib/markdown-slash-commands.js @@ -291,6 +291,34 @@ export const clampSlashHighlightedIndex = (currentIndex, commandCount) => { return Math.min(index, count - 1) } +/** + * 슬래시 메뉴 방향키 탐색 후 하이라이트 인덱스를 계산한다. + * @param {Object} options - 계산 옵션 + * @param {number} options.currentIndex - 현재 하이라이트 인덱스 + * @param {number} options.commandCount - 표시 가능한 명령 개수 + * @param {string} options.key - 누른 키 + * @returns {number} 다음 하이라이트 인덱스 + */ +export const getNextSlashHighlightedIndex = ({ currentIndex, commandCount, key }) => { + const count = Math.max(0, Number.isFinite(commandCount) ? Math.floor(commandCount) : 0) + + if (count <= 0) { + return 0 + } + + const index = clampSlashHighlightedIndex(currentIndex, count) + + if (key === 'ArrowDown') { + return (index + 1) % count + } + + if (key === 'ArrowUp') { + return (index - 1 + count) % count + } + + return index +} + /** * 라이브 모드 미디어 슬래시 명령 처리 중 지연 commit을 무시할지 확인한다. * @param {Object} options - 판별 옵션 diff --git a/package-lock.json b/package-lock.json index e6ce68f..e3c22b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sori.studio", - "version": "1.11.90", + "version": "1.11.91", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sori.studio", - "version": "1.11.90", + "version": "1.11.91", "hasInstallScript": true, "dependencies": { "@nuxtjs/tailwindcss": "^6.14.0", diff --git a/package.json b/package.json index b7f8d36..4d2e14c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sori.studio", - "version": "1.11.90", + "version": "1.11.91", "private": true, "type": "module", "imports": { diff --git a/tests/markdown-slash-commands.test.js b/tests/markdown-slash-commands.test.js index 1111d6d..2ebb76f 100644 --- a/tests/markdown-slash-commands.test.js +++ b/tests/markdown-slash-commands.test.js @@ -4,6 +4,7 @@ import { addSlashSuppressedLine, clampSlashHighlightedIndex, getMediaPickerKindFromSlashAction, + getNextSlashHighlightedIndex, getSlashCommandFocusTarget, getSlashCommandTextareaSelection, insertMarkdownLinesAtSlashTarget, @@ -76,6 +77,15 @@ test('슬래시 메뉴 하이라이트 인덱스는 명령 목록 범위 안으 assert.equal(clampSlashHighlightedIndex(Number.NaN, Number.NaN), 0) }) +test('슬래시 메뉴 하이라이트 인덱스는 방향키로 순환한다', () => { + assert.equal(getNextSlashHighlightedIndex({ currentIndex: 1, commandCount: 3, key: 'ArrowDown' }), 2) + assert.equal(getNextSlashHighlightedIndex({ currentIndex: 2, commandCount: 3, key: 'ArrowDown' }), 0) + assert.equal(getNextSlashHighlightedIndex({ currentIndex: 0, commandCount: 3, key: 'ArrowUp' }), 2) + assert.equal(getNextSlashHighlightedIndex({ currentIndex: 8, commandCount: 3, key: 'ArrowUp' }), 1) + assert.equal(getNextSlashHighlightedIndex({ currentIndex: 1, commandCount: 0, key: 'ArrowDown' }), 0) + assert.equal(getNextSlashHighlightedIndex({ currentIndex: 1, commandCount: 3, key: 'Enter' }), 1) +}) + test('슬래시 명령 줄에 미디어를 삽입할 때 원문 줄을 교체한다', () => { const markdown = [ '첫 줄',