마크다운 인용 처리 분리
This commit is contained in:
@@ -26,11 +26,9 @@ import {
|
|||||||
getAppendTextForMerge,
|
getAppendTextForMerge,
|
||||||
getMergeJunctionDisplayOffset,
|
getMergeJunctionDisplayOffset,
|
||||||
hasListMarker,
|
hasListMarker,
|
||||||
hasQuoteMarker,
|
|
||||||
isEmptyListMarkerLine,
|
isEmptyListMarkerLine,
|
||||||
parseOrderedListMarker,
|
parseOrderedListMarker,
|
||||||
stripListMarker,
|
stripListMarker
|
||||||
stripQuoteMarker
|
|
||||||
} from '../../lib/markdown-live-edit.js'
|
} from '../../lib/markdown-live-edit.js'
|
||||||
import { buildCodeBlockLines, buildCodeFenceOpener, parseCodeFenceLine } from '../../lib/markdown-code-block.js'
|
import { buildCodeBlockLines, buildCodeFenceOpener, parseCodeFenceLine } from '../../lib/markdown-code-block.js'
|
||||||
import { buildToggleBlockLines, parseToggleOpenerLine } from '../../lib/markdown-toggle.js'
|
import { buildToggleBlockLines, parseToggleOpenerLine } from '../../lib/markdown-toggle.js'
|
||||||
@@ -44,9 +42,17 @@ import {
|
|||||||
import {
|
import {
|
||||||
buildCalloutOpenerLine,
|
buildCalloutOpenerLine,
|
||||||
CALLOUT_BACKGROUND_OPTIONS,
|
CALLOUT_BACKGROUND_OPTIONS,
|
||||||
QUOTE_BACKGROUND_OPTIONS,
|
|
||||||
parseCalloutOptions
|
parseCalloutOptions
|
||||||
} from '../../lib/markdown-callout.js'
|
} from '../../lib/markdown-callout.js'
|
||||||
|
import {
|
||||||
|
formatQuoteLine,
|
||||||
|
getQuoteContentStartLine,
|
||||||
|
getQuoteLineBody,
|
||||||
|
getQuoteLineEntries,
|
||||||
|
isQuoteMarkerLine,
|
||||||
|
normalizeQuoteEditableValue,
|
||||||
|
parseQuoteOptions
|
||||||
|
} from '../../lib/markdown-quote.js'
|
||||||
import { getInArticleAdAfterBlockIds } from '../../lib/content-in-article-ad.js'
|
import { getInArticleAdAfterBlockIds } from '../../lib/content-in-article-ad.js'
|
||||||
import { createHeadingIdFactory } from '../../lib/markdown-toc.js'
|
import { createHeadingIdFactory } from '../../lib/markdown-toc.js'
|
||||||
import ContentMarkdownCodeBlockEditor from './ContentMarkdownCodeBlockEditor.vue'
|
import ContentMarkdownCodeBlockEditor from './ContentMarkdownCodeBlockEditor.vue'
|
||||||
@@ -188,53 +194,6 @@ const createBlock = (type = 'paragraph', text = '', level = null, id = '', optio
|
|||||||
*/
|
*/
|
||||||
const parseImageLine = (line) => parseImageMarkdownLine(line)
|
const parseImageLine = (line) => parseImageMarkdownLine(line)
|
||||||
|
|
||||||
/**
|
|
||||||
* 인용 마커 줄인지 확인한다.
|
|
||||||
* @param {string} line - 마크다운 행
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
const isQuoteMarkerLine = (line) => {
|
|
||||||
const trimmed = String(line ?? '').trim()
|
|
||||||
return trimmed === '>' || /^>\s/.test(trimmed)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 인용 마커를 제거한 본문을 반환한다.
|
|
||||||
* @param {string} line - 마크다운 행
|
|
||||||
* @returns {string} 인용 본문
|
|
||||||
*/
|
|
||||||
const getQuoteLineBody = (line) => String(line ?? '').trim().replace(/^>\s?/, '')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 인용 옵션 줄을 파싱한다.
|
|
||||||
* @param {string} value - 인용 본문 줄
|
|
||||||
* @returns {{ quoteBackground: string }|null} 인용 옵션
|
|
||||||
*/
|
|
||||||
const parseQuoteOptions = (value) => {
|
|
||||||
const raw = String(value ?? '').trim()
|
|
||||||
const bracketMatch = raw.match(/^\[!(.+)\]$/)
|
|
||||||
const braceMatch = raw.match(/^\{(.+)\}$/)
|
|
||||||
const optionSource = bracketMatch?.[1] || braceMatch?.[1] || ''
|
|
||||||
|
|
||||||
if (!optionSource) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokens = optionSource.trim().split(/\s+/)
|
|
||||||
let quoteBackground = ''
|
|
||||||
|
|
||||||
tokens.forEach((token) => {
|
|
||||||
const [key, rawOptionValue] = token.split('=')
|
|
||||||
const optionValue = String(rawOptionValue || '').trim()
|
|
||||||
|
|
||||||
if (key?.toLowerCase() === 'bg' && QUOTE_BACKGROUND_OPTIONS.includes(optionValue)) {
|
|
||||||
quoteBackground = optionValue
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return quoteBackground ? { quoteBackground } : null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 불릿 목록 마커 줄인지 확인한다.
|
* 불릿 목록 마커 줄인지 확인한다.
|
||||||
* @param {string} line - 마크다운 행
|
* @param {string} line - 마크다운 행
|
||||||
@@ -1276,25 +1235,6 @@ const getBlockSourceLines = (block) => String(props.content || '').split('\n').s
|
|||||||
(block.meta.endLine ?? block.meta.startLine) + 1
|
(block.meta.endLine ?? block.meta.startLine) + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
|
||||||
* 인용 줄 마크다운을 만든다.
|
|
||||||
* @param {string} value - 편집 값
|
|
||||||
* @param {boolean} raw - 원문 모드 여부
|
|
||||||
* @returns {string} 마크다운 줄
|
|
||||||
*/
|
|
||||||
const formatQuoteLine = (value, raw) => {
|
|
||||||
if (raw) {
|
|
||||||
if (!hasQuoteMarker(value)) {
|
|
||||||
return String(value ?? '').trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
const clean = stripQuoteMarker(value)
|
|
||||||
return clean ? `> ${clean}` : '> '
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 목록 항목 마크다운 줄을 만든다.
|
* 목록 항목 마크다운 줄을 만든다.
|
||||||
* @param {string} value - 편집 값
|
* @param {string} value - 편집 값
|
||||||
@@ -1353,52 +1293,6 @@ const createEmptyListMarkerLine = (block, itemIndex) => {
|
|||||||
return '- '
|
return '- '
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 인용 블록의 본문 시작 줄을 반환한다.
|
|
||||||
* @param {Object} block - 인용 블록
|
|
||||||
* @returns {number} 본문 시작 줄
|
|
||||||
*/
|
|
||||||
const getQuoteContentStartLine = (block) => {
|
|
||||||
if (typeof block.meta?.quoteContentStartLine === 'number') {
|
|
||||||
return block.meta.quoteContentStartLine
|
|
||||||
}
|
|
||||||
|
|
||||||
return block.meta.startLine
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 인용 블록을 편집 가능한 줄 단위로 분리한다.
|
|
||||||
* @param {Object} block - 인용 블록
|
|
||||||
* @returns {Array<{ text: string, sourceLine: number, sourceIndex: number }>} 줄 목록
|
|
||||||
*/
|
|
||||||
const getQuoteLineEntries = (block) => {
|
|
||||||
const contentStartLine = getQuoteContentStartLine(block)
|
|
||||||
const endLine = block.meta.endLine ?? block.meta.startLine
|
|
||||||
const lineCount = Math.max(1, endLine - contentStartLine + 1)
|
|
||||||
const fromText = String(block.text ?? '').split('\n')
|
|
||||||
|
|
||||||
while (fromText.length < lineCount) {
|
|
||||||
fromText.push('')
|
|
||||||
}
|
|
||||||
|
|
||||||
return fromText.slice(0, lineCount).map((text, index) => ({
|
|
||||||
text,
|
|
||||||
sourceLine: contentStartLine + index,
|
|
||||||
sourceIndex: contentStartLine + index - block.meta.startLine
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IME·브라우저 자동 목록 보정으로 생긴 인용 접두를 정리한다.
|
|
||||||
* @param {string} value - 인용 본문
|
|
||||||
* @returns {string} 정리된 본문
|
|
||||||
*/
|
|
||||||
const normalizeQuoteEditableValue = (value) => {
|
|
||||||
return String(value ?? '').replace(/\r/g, '').split('\n').map((line) => {
|
|
||||||
return line.replace(/^>\.\s?/, '')
|
|
||||||
}).join('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 코드 블록 줄 번호 목록을 만든다.
|
* 코드 블록 줄 번호 목록을 만든다.
|
||||||
* @param {Object} block - 코드 블록
|
* @param {Object} block - 코드 블록
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
| lib/markdown-html-block.js | 게시물 본문 HTML 블록의 parser 기반 허용 태그·속성 sanitizer, 실행 URL·인라인 style 제거와 새 창 링크 rel 보정 |
|
| lib/markdown-html-block.js | 게시물 본문 HTML 블록의 parser 기반 허용 태그·속성 sanitizer, 실행 URL·인라인 style 제거와 새 창 링크 rel 보정 |
|
||||||
| lib/markdown-callout.js | 인용 막대 색상·콜아웃 배경색 옵션, 선언 줄 파싱·직렬화 |
|
| lib/markdown-callout.js | 인용 막대 색상·콜아웃 배경색 옵션, 선언 줄 파싱·직렬화 |
|
||||||
| lib/markdown-fenced-meta.js | 공개 Markdown fenced 블록 본문 수집과 북마크·회원가입·미디어 메타 파싱 |
|
| lib/markdown-fenced-meta.js | 공개 Markdown fenced 블록 본문 수집과 북마크·회원가입·미디어 메타 파싱 |
|
||||||
|
| lib/markdown-quote.js | 공개 Markdown 인용 줄 판별·옵션 파싱·라이브 편집 줄 변환 |
|
||||||
| lib/brand-color.js | 사이트 브랜드 컬러 기본값·hex 검증·정규화 |
|
| lib/brand-color.js | 사이트 브랜드 컬러 기본값·hex 검증·정규화 |
|
||||||
| lib/markdown-image.js | 이미지 마크다운 직렬화·파싱, 단독 이미지 URL 판별 |
|
| lib/markdown-image.js | 이미지 마크다운 직렬화·파싱, 단독 이미지 URL 판별 |
|
||||||
| lib/markdown-table.js | 공개 Markdown 표 행·구분선·정렬·블록 파싱 |
|
| lib/markdown-table.js | 공개 Markdown 표 행·구분선·정렬·블록 파싱 |
|
||||||
@@ -109,6 +110,7 @@
|
|||||||
| tests/markdown-html-block.test.js | HTML sanitizer 단위 회귀 테스트 |
|
| tests/markdown-html-block.test.js | HTML sanitizer 단위 회귀 테스트 |
|
||||||
| tests/content-in-article-ad.test.js | 공개 본문 인아티클 광고 삽입 위치 계산 단위 회귀 테스트 |
|
| tests/content-in-article-ad.test.js | 공개 본문 인아티클 광고 삽입 위치 계산 단위 회귀 테스트 |
|
||||||
| tests/markdown-fenced-meta.test.js | Markdown fenced 블록 본문 수집과 메타 파싱 단위 회귀 테스트 |
|
| tests/markdown-fenced-meta.test.js | Markdown fenced 블록 본문 수집과 메타 파싱 단위 회귀 테스트 |
|
||||||
|
| tests/markdown-quote.test.js | Markdown 인용 옵션 파싱과 편집 줄 변환 단위 회귀 테스트 |
|
||||||
| tests/multipart-file-upload.test.js | multipart 스트리밍·제한 단위 회귀 테스트 |
|
| tests/multipart-file-upload.test.js | multipart 스트리밍·제한 단위 회귀 테스트 |
|
||||||
| tests/csp-report.test.js | CSP URL 최소화·payload 형식 단위 회귀 테스트 |
|
| tests/csp-report.test.js | CSP URL 최소화·payload 형식 단위 회귀 테스트 |
|
||||||
| tests/media-catalog.test.js | 미디어 URL·역할·참조·LIKE 이스케이프 단위 회귀 테스트 |
|
| tests/media-catalog.test.js | 미디어 URL·역할·참조·LIKE 이스케이프 단위 회귀 테스트 |
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# 업데이트 이력
|
# 업데이트 이력
|
||||||
|
|
||||||
|
## v1.11.51
|
||||||
|
|
||||||
|
- 공개 Markdown 렌더러의 인용 줄 판별·옵션 파싱·편집 줄 변환 helper를 `lib/markdown-quote.js`로 분리.
|
||||||
|
- 인용 옵션 선언과 라이브 편집 중 `>. ` 접두 정리 단위 테스트를 추가.
|
||||||
|
- `ContentMarkdownRenderer`는 인용 블록 생성과 편집 이벤트 연결만 담당하도록 정리.
|
||||||
|
|
||||||
## v1.11.50
|
## v1.11.50
|
||||||
|
|
||||||
- 공개 Markdown 렌더러의 인아티클 광고 삽입 위치 계산을 `lib/content-in-article-ad.js`로 분리.
|
- 공개 Markdown 렌더러의 인아티클 광고 삽입 위치 계산을 `lib/content-in-article-ad.js`로 분리.
|
||||||
|
|||||||
114
lib/markdown-quote.js
Normal file
114
lib/markdown-quote.js
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { QUOTE_BACKGROUND_OPTIONS } from './markdown-callout.js'
|
||||||
|
import { hasQuoteMarker, stripQuoteMarker } from './markdown-live-edit.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 인용 마커 줄인지 확인한다.
|
||||||
|
* @param {string} line - 마크다운 행
|
||||||
|
* @returns {boolean} 인용 마커 줄 여부
|
||||||
|
*/
|
||||||
|
export const isQuoteMarkerLine = (line) => {
|
||||||
|
const trimmed = String(line ?? '').trim()
|
||||||
|
return trimmed === '>' || /^>\s/.test(trimmed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 인용 마커를 제거한 본문을 반환한다.
|
||||||
|
* @param {string} line - 마크다운 행
|
||||||
|
* @returns {string} 인용 본문
|
||||||
|
*/
|
||||||
|
export const getQuoteLineBody = (line) => String(line ?? '').trim().replace(/^>\s?/, '')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 인용 옵션 줄을 파싱한다.
|
||||||
|
* @param {string} value - 인용 본문 줄
|
||||||
|
* @returns {{ quoteBackground: string }|null} 인용 옵션
|
||||||
|
*/
|
||||||
|
export const parseQuoteOptions = (value) => {
|
||||||
|
const raw = String(value ?? '').trim()
|
||||||
|
const bracketMatch = raw.match(/^\[!(.+)\]$/)
|
||||||
|
const braceMatch = raw.match(/^\{(.+)\}$/)
|
||||||
|
const optionSource = bracketMatch?.[1] || braceMatch?.[1] || ''
|
||||||
|
|
||||||
|
if (!optionSource) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokens = optionSource.trim().split(/\s+/)
|
||||||
|
let quoteBackground = ''
|
||||||
|
|
||||||
|
tokens.forEach((token) => {
|
||||||
|
const [key, rawOptionValue] = token.split('=')
|
||||||
|
const optionValue = String(rawOptionValue || '').trim()
|
||||||
|
|
||||||
|
if (key?.toLowerCase() === 'bg' && QUOTE_BACKGROUND_OPTIONS.includes(optionValue)) {
|
||||||
|
quoteBackground = optionValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return quoteBackground ? { quoteBackground } : null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 인용 줄 마크다운을 만든다.
|
||||||
|
* @param {string} value - 편집 값
|
||||||
|
* @param {boolean} raw - 원문 모드 여부
|
||||||
|
* @returns {string} 마크다운 줄
|
||||||
|
*/
|
||||||
|
export const formatQuoteLine = (value, raw) => {
|
||||||
|
if (raw) {
|
||||||
|
if (!hasQuoteMarker(value)) {
|
||||||
|
return String(value ?? '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
const clean = stripQuoteMarker(value)
|
||||||
|
return clean ? `> ${clean}` : '> '
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 인용 블록의 본문 시작 줄을 반환한다.
|
||||||
|
* @param {Object} block - 인용 블록
|
||||||
|
* @returns {number} 본문 시작 줄
|
||||||
|
*/
|
||||||
|
export const getQuoteContentStartLine = (block) => {
|
||||||
|
if (typeof block?.meta?.quoteContentStartLine === 'number') {
|
||||||
|
return block.meta.quoteContentStartLine
|
||||||
|
}
|
||||||
|
|
||||||
|
return block?.meta?.startLine ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 인용 블록을 편집 가능한 줄 단위로 분리한다.
|
||||||
|
* @param {Object} block - 인용 블록
|
||||||
|
* @returns {Array<{ text: string, sourceLine: number, sourceIndex: number }>} 줄 목록
|
||||||
|
*/
|
||||||
|
export const getQuoteLineEntries = (block) => {
|
||||||
|
const contentStartLine = getQuoteContentStartLine(block)
|
||||||
|
const endLine = block?.meta?.endLine ?? block?.meta?.startLine ?? contentStartLine
|
||||||
|
const lineCount = Math.max(1, endLine - contentStartLine + 1)
|
||||||
|
const fromText = String(block?.text ?? '').split('\n')
|
||||||
|
|
||||||
|
while (fromText.length < lineCount) {
|
||||||
|
fromText.push('')
|
||||||
|
}
|
||||||
|
|
||||||
|
return fromText.slice(0, lineCount).map((text, index) => ({
|
||||||
|
text,
|
||||||
|
sourceLine: contentStartLine + index,
|
||||||
|
sourceIndex: contentStartLine + index - (block?.meta?.startLine ?? contentStartLine)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IME·브라우저 자동 목록 보정으로 생긴 인용 접두를 정리한다.
|
||||||
|
* @param {string} value - 인용 본문
|
||||||
|
* @returns {string} 정리된 본문
|
||||||
|
*/
|
||||||
|
export const normalizeQuoteEditableValue = (value) => {
|
||||||
|
return String(value ?? '').replace(/\r/g, '').split('\n').map((line) => {
|
||||||
|
return line.replace(/^>\.\s?/, '')
|
||||||
|
}).join('\n')
|
||||||
|
}
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "sori.studio",
|
"name": "sori.studio",
|
||||||
"version": "1.11.50",
|
"version": "1.11.51",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "sori.studio",
|
"name": "sori.studio",
|
||||||
"version": "1.11.50",
|
"version": "1.11.51",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sori.studio",
|
"name": "sori.studio",
|
||||||
"version": "1.11.50",
|
"version": "1.11.51",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"imports": {
|
"imports": {
|
||||||
|
|||||||
60
tests/markdown-quote.test.js
Normal file
60
tests/markdown-quote.test.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import test from 'node:test'
|
||||||
|
import {
|
||||||
|
formatQuoteLine,
|
||||||
|
getQuoteContentStartLine,
|
||||||
|
getQuoteLineBody,
|
||||||
|
getQuoteLineEntries,
|
||||||
|
isQuoteMarkerLine,
|
||||||
|
normalizeQuoteEditableValue,
|
||||||
|
parseQuoteOptions
|
||||||
|
} from '../lib/markdown-quote.js'
|
||||||
|
|
||||||
|
test('인용 마커 줄과 본문을 판별한다', () => {
|
||||||
|
assert.equal(isQuoteMarkerLine('>'), true)
|
||||||
|
assert.equal(isQuoteMarkerLine('> 본문'), true)
|
||||||
|
assert.equal(isQuoteMarkerLine('본문'), false)
|
||||||
|
assert.equal(getQuoteLineBody('> 본문'), '본문')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('인용 옵션은 대괄호와 중괄호 선언을 모두 파싱한다', () => {
|
||||||
|
assert.deepEqual(parseQuoteOptions('[!bg=blue]'), { quoteBackground: 'blue' })
|
||||||
|
assert.deepEqual(parseQuoteOptions('{bg=purple}'), { quoteBackground: 'purple' })
|
||||||
|
assert.equal(parseQuoteOptions('{bg=unknown}'), null)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('인용 줄 직렬화는 원문 모드와 편집 모드를 구분한다', () => {
|
||||||
|
assert.equal(formatQuoteLine('본문', false), '> 본문')
|
||||||
|
assert.equal(formatQuoteLine('', false), '> ')
|
||||||
|
assert.equal(formatQuoteLine('> 원문', true), '> 원문')
|
||||||
|
assert.equal(formatQuoteLine('원문', true), '원문')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('인용 블록 본문 줄 위치를 옵션 줄 이후로 계산한다', () => {
|
||||||
|
const block = {
|
||||||
|
text: '첫 줄\n둘째 줄',
|
||||||
|
meta: {
|
||||||
|
startLine: 10,
|
||||||
|
endLine: 12,
|
||||||
|
quoteContentStartLine: 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(getQuoteContentStartLine(block), 11)
|
||||||
|
assert.deepEqual(getQuoteLineEntries(block), [
|
||||||
|
{
|
||||||
|
text: '첫 줄',
|
||||||
|
sourceLine: 11,
|
||||||
|
sourceIndex: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '둘째 줄',
|
||||||
|
sourceLine: 12,
|
||||||
|
sourceIndex: 2
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('인용 편집 중 브라우저가 만든 >. 접두를 정리한다', () => {
|
||||||
|
assert.equal(normalizeQuoteEditableValue('>. 발타자르\n> 정상'), '발타자르\n> 정상')
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user