From a39944b13964a880b314be1efe52046ddaf0aac2 Mon Sep 17 00:00:00 2001 From: zenn Date: Mon, 27 Jul 2026 11:40:50 +0900 Subject: [PATCH] =?UTF-8?q?=EC=8A=AC=EB=9E=98=EC=8B=9C=20=EC=A0=9C?= =?UTF-8?q?=EC=99=B8=20=EC=A4=84=20=EC=83=81=ED=83=9C=20=EA=B0=B1=EC=8B=A0?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/admin/AdminMarkdownEditor.vue | 12 ++++----- docs/map.md | 4 +-- docs/update.md | 5 ++++ lib/markdown-slash-commands.js | 32 ++++++++++++++++++++++++ package-lock.json | 4 +-- package.json | 2 +- tests/markdown-slash-commands.test.js | 19 ++++++++++++++ 7 files changed, 66 insertions(+), 12 deletions(-) diff --git a/components/admin/AdminMarkdownEditor.vue b/components/admin/AdminMarkdownEditor.vue index c8dbdc7..57613df 100644 --- a/components/admin/AdminMarkdownEditor.vue +++ b/components/admin/AdminMarkdownEditor.vue @@ -44,6 +44,7 @@ import { toggleMediaSelectionState } from '../../lib/markdown-editor-media.js' import { + addSlashSuppressedLine, filterSlashCommands, getMediaPickerKindFromSlashAction, getSlashCommandFocusTarget, @@ -51,6 +52,7 @@ import { insertMarkdownLinesAtSlashTarget, isMediaSlashAction, parseSlashInput, + removeSlashSuppressedLine, resolveSlashCommand, shouldIgnorePendingMediaSlashCommit } from '../../lib/markdown-slash-commands.js' @@ -2597,9 +2599,7 @@ watch(liveSlashVisible, (visible) => { */ const onLiveSlashEnd = (payload) => { if (typeof payload?.sourceLine === 'number') { - const next = new Set(liveSlashSuppressedLines.value) - next.delete(payload.sourceLine) - liveSlashSuppressedLines.value = next + liveSlashSuppressedLines.value = removeSlashSuppressedLine(liveSlashSuppressedLines.value, payload.sourceLine) } clearLiveSlashState() @@ -2613,7 +2613,7 @@ const cancelLiveSlashCommand = () => { const line = liveSlashSourceLine.value if (line !== null) { - liveSlashSuppressedLines.value = new Set([...liveSlashSuppressedLines.value, line]) + liveSlashSuppressedLines.value = addSlashSuppressedLine(liveSlashSuppressedLines.value, line) } clearLiveSlashState() @@ -2686,9 +2686,7 @@ const applyLiveSlashCommand = (command) => { if (command.action === 'lines' && Array.isArray(command.lines)) { const replacementLines = command.lines - const nextSuppressed = new Set(liveSlashSuppressedLines.value) - nextSuppressed.delete(line) - liveSlashSuppressedLines.value = nextSuppressed + liveSlashSuppressedLines.value = removeSlashSuppressedLine(liveSlashSuppressedLines.value, line) replaceLineRange(line, line, replacementLines, false) clearLiveSlashState() diff --git a/docs/map.md b/docs/map.md index 289ef50..21e284f 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·모달 종류 판별, 미디어 명령 지연 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·모달 종류·포커스 대상·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 1addd10..e243092 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,10 @@ # 업데이트 이력 +## v1.11.89 + +- 관리자 Markdown 에디터의 슬래시 메뉴 제외 줄 Set 갱신을 `markdown-slash-commands` helper로 분리. +- 슬래시 제외 줄 추가·제거 단위 테스트 추가. + ## v1.11.88 - 관리자 Markdown 에디터의 미디어 슬래시 action → 미디어 선택 모달 종류 변환을 `markdown-slash-commands` helper로 분리. diff --git a/lib/markdown-slash-commands.js b/lib/markdown-slash-commands.js index 6f175f2..f76ef8b 100644 --- a/lib/markdown-slash-commands.js +++ b/lib/markdown-slash-commands.js @@ -242,6 +242,38 @@ export const getMediaPickerKindFromSlashAction = (action) => { return normalized.slice('media-'.length) } +/** + * 슬래시 메뉴를 다시 열지 않을 줄 목록에 줄을 추가한다. + * @param {Set} suppressedLines - 기존 제외 줄 Set + * @param {number} line - 추가할 줄 번호 + * @returns {Set} 갱신된 Set + */ +export const addSlashSuppressedLine = (suppressedLines, line) => { + const next = new Set(suppressedLines instanceof Set ? suppressedLines : []) + + if (Number.isFinite(line)) { + next.add(line) + } + + return next +} + +/** + * 슬래시 메뉴 제외 줄 목록에서 줄을 제거한다. + * @param {Set} suppressedLines - 기존 제외 줄 Set + * @param {number} line - 제거할 줄 번호 + * @returns {Set} 갱신된 Set + */ +export const removeSlashSuppressedLine = (suppressedLines, line) => { + const next = new Set(suppressedLines instanceof Set ? suppressedLines : []) + + if (Number.isFinite(line)) { + next.delete(line) + } + + return next +} + /** * 라이브 모드 미디어 슬래시 명령 처리 중 지연 commit을 무시할지 확인한다. * @param {Object} options - 판별 옵션 diff --git a/package-lock.json b/package-lock.json index c69c29f..ac74f6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sori.studio", - "version": "1.11.88", + "version": "1.11.89", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sori.studio", - "version": "1.11.88", + "version": "1.11.89", "hasInstallScript": true, "dependencies": { "@nuxtjs/tailwindcss": "^6.14.0", diff --git a/package.json b/package.json index 38d544e..b8d496d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sori.studio", - "version": "1.11.88", + "version": "1.11.89", "private": true, "type": "module", "imports": { diff --git a/tests/markdown-slash-commands.test.js b/tests/markdown-slash-commands.test.js index 5fa2898..26ff3a9 100644 --- a/tests/markdown-slash-commands.test.js +++ b/tests/markdown-slash-commands.test.js @@ -1,12 +1,14 @@ import assert from 'node:assert/strict' import test from 'node:test' import { + addSlashSuppressedLine, getMediaPickerKindFromSlashAction, getSlashCommandFocusTarget, getSlashCommandTextareaSelection, insertMarkdownLinesAtSlashTarget, isMediaSlashAction, parseSlashInput, + removeSlashSuppressedLine, resolveSlashCommand, shouldIgnorePendingMediaSlashCommit } from '../lib/markdown-slash-commands.js' @@ -48,6 +50,23 @@ test('미디어 슬래시 명령 지연 commit은 같은 줄 slash 원문일 때 }), false) }) +test('슬래시 제외 줄 Set은 기존 값을 보존한 새 Set으로 갱신한다', () => { + const source = new Set([1, 2]) + const added = addSlashSuppressedLine(source, 3) + const removed = removeSlashSuppressedLine(added, 2) + + assert.deepEqual([...source], [1, 2]) + assert.deepEqual([...added], [1, 2, 3]) + assert.deepEqual([...removed], [1, 3]) + assert.notEqual(added, source) + assert.notEqual(removed, added) +}) + +test('슬래시 제외 줄 Set은 유효하지 않은 줄 번호를 무시한다', () => { + assert.deepEqual([...addSlashSuppressedLine(new Set([1]), Number.NaN)], [1]) + assert.deepEqual([...removeSlashSuppressedLine(new Set([1]), Number.NaN)], [1]) +}) + test('슬래시 명령 줄에 미디어를 삽입할 때 원문 줄을 교체한다', () => { const markdown = [ '첫 줄',