관리자 인용 컨텍스트 파서 통합
This commit is contained in:
@@ -44,7 +44,7 @@
|
|||||||
| lib/recommended-site-icon-url.js | 추천 사이트 아이콘 URL 우선순위(수동 썸네일 > 서버 캐시 > 외부 프록시) |
|
| lib/recommended-site-icon-url.js | 추천 사이트 아이콘 URL 우선순위(수동 썸네일 > 서버 캐시 > 외부 프록시) |
|
||||||
| lib/navigation-editor-tree.js | 관리자 메뉴 UI·서버 `renumberSortOrderByTree`가 쓰는 `buildNavigationEditorTree`, 관리자 상단 네비 평면 표용 `flattenNavigationEditorWrappers` |
|
| lib/navigation-editor-tree.js | 관리자 메뉴 UI·서버 `renumberSortOrderByTree`가 쓰는 `buildNavigationEditorTree`, 관리자 상단 네비 평면 표용 `flattenNavigationEditorWrappers` |
|
||||||
| lib/markdown-content-normalizer.js | 관리자 Markdown-first 전환 후 레거시 블록 배열·객체 본문 값을 저장용 마크다운 문자열로 변환 |
|
| lib/markdown-content-normalizer.js | 관리자 Markdown-first 전환 후 레거시 블록 배열·객체 본문 값을 저장용 마크다운 문자열로 변환 |
|
||||||
| lib/markdown-block-context.js | 관리자 Markdown textarea·라이브 편집 포커스 위치 기준 이미지·갤러리·링크·iframe·HTML·인용·콜아웃·코드·토글 블록 설정 패널 대상 판별, 코드 fenced 블록 여닫는 펜스 쌍 판별 |
|
| lib/markdown-block-context.js | 관리자 Markdown textarea·라이브 편집 포커스 위치 기준 이미지·갤러리·링크·iframe·HTML·인용·콜아웃·코드·토글 블록 설정 패널 대상 판별, 인용 옵션 공통 파서 연결, 코드 fenced 블록 여닫는 펜스 쌍 판별 |
|
||||||
| lib/markdown-iframe-embed.js | 게시물 본문 iframe 블록의 코드 판별과 안전 속성 추출 |
|
| lib/markdown-iframe-embed.js | 게시물 본문 iframe 블록의 코드 판별과 안전 속성 추출 |
|
||||||
| 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 | 인용 막대 색상·콜아웃 배경색 옵션, 선언 줄 파싱·직렬화 |
|
||||||
@@ -109,6 +109,7 @@
|
|||||||
| docker-compose.test.yml | 실행마다 생성·폐기하는 PostgreSQL 16 테스트 DB·볼륨·네트워크 |
|
| docker-compose.test.yml | 실행마다 생성·폐기하는 PostgreSQL 16 테스트 DB·볼륨·네트워크 |
|
||||||
| 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-block-context.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/markdown-quote.test.js | Markdown 인용 옵션 파싱과 편집 줄 변환 단위 회귀 테스트 |
|
||||||
| tests/multipart-file-upload.test.js | multipart 스트리밍·제한 단위 회귀 테스트 |
|
| tests/multipart-file-upload.test.js | multipart 스트리밍·제한 단위 회귀 테스트 |
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# 업데이트 이력
|
# 업데이트 이력
|
||||||
|
|
||||||
|
## v1.11.52
|
||||||
|
|
||||||
|
- 관리자 블록 컨텍스트의 인용 줄 판별·옵션 파싱 중복을 `lib/markdown-quote.js` 공통 helper로 통합.
|
||||||
|
- 인용 블록 설정 패널이 받는 `quoteBackground`·옵션 존재 여부 컨텍스트 단위 테스트를 추가.
|
||||||
|
- 공개 렌더러와 관리자 블록 설정 패널의 인용 옵션 해석 경로를 맞춤.
|
||||||
|
|
||||||
## v1.11.51
|
## v1.11.51
|
||||||
|
|
||||||
- 공개 Markdown 렌더러의 인용 줄 판별·옵션 파싱·편집 줄 변환 helper를 `lib/markdown-quote.js`로 분리.
|
- 공개 Markdown 렌더러의 인용 줄 판별·옵션 파싱·편집 줄 변환 helper를 `lib/markdown-quote.js`로 분리.
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { isImageUrl, parseImageMarkdownLine } from './markdown-image.js'
|
import { isImageUrl, parseImageMarkdownLine } from './markdown-image.js'
|
||||||
import { CALLOUT_BACKGROUND_OPTIONS, QUOTE_BACKGROUND_OPTIONS, parseCalloutOptions } from './markdown-callout.js'
|
import { CALLOUT_BACKGROUND_OPTIONS, parseCalloutOptions } from './markdown-callout.js'
|
||||||
import { parseCodeFenceLine } from './markdown-code-block.js'
|
import { parseCodeFenceLine } from './markdown-code-block.js'
|
||||||
import { parseToggleOpenerLine } from './markdown-toggle.js'
|
import { parseToggleOpenerLine } from './markdown-toggle.js'
|
||||||
|
import { getQuoteLineBody, isQuoteMarkerLine, parseQuoteOptions } from './markdown-quote.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fenced 블록 시작 줄 인덱스를 찾는다.
|
* fenced 블록 시작 줄 인덱스를 찾는다.
|
||||||
@@ -75,53 +76,6 @@ const isStandaloneUrlLine = (line) => /^https?:\/\/\S+$/i.test(String(line || ''
|
|||||||
*/
|
*/
|
||||||
const isStandaloneImageUrlLine = (line) => isStandaloneUrlLine(line) && isImageUrl(line)
|
const isStandaloneImageUrlLine = (line) => isStandaloneUrlLine(line) && isImageUrl(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 parseQuoteOptionsLine = (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[]} lines - 본문 줄 목록
|
* @param {string[]} lines - 본문 줄 목록
|
||||||
@@ -145,7 +99,7 @@ const resolveQuoteBlock = (lines, currentLine) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const firstQuoteBody = getQuoteLineBody(lines[startLine] || '')
|
const firstQuoteBody = getQuoteLineBody(lines[startLine] || '')
|
||||||
const quoteOptions = parseQuoteOptionsLine(firstQuoteBody)
|
const quoteOptions = parseQuoteOptions(firstQuoteBody)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: 'quote',
|
kind: 'quote',
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "sori.studio",
|
"name": "sori.studio",
|
||||||
"version": "1.11.51",
|
"version": "1.11.52",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "sori.studio",
|
"name": "sori.studio",
|
||||||
"version": "1.11.51",
|
"version": "1.11.52",
|
||||||
"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.51",
|
"version": "1.11.52",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"imports": {
|
"imports": {
|
||||||
|
|||||||
33
tests/markdown-block-context.test.js
Normal file
33
tests/markdown-block-context.test.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import test from 'node:test'
|
||||||
|
import { resolveActiveBlockContext } from '../lib/markdown-block-context.js'
|
||||||
|
|
||||||
|
test('인용 옵션이 있는 블록 컨텍스트를 공통 인용 파서로 판별한다', () => {
|
||||||
|
assert.deepEqual(resolveActiveBlockContext([
|
||||||
|
'> {bg=purple}',
|
||||||
|
'> 첫 줄',
|
||||||
|
'> 둘째 줄',
|
||||||
|
'',
|
||||||
|
'다음 문단'
|
||||||
|
].join('\n'), 1), {
|
||||||
|
kind: 'quote',
|
||||||
|
startLine: 0,
|
||||||
|
endLine: 2,
|
||||||
|
quoteBackground: 'purple',
|
||||||
|
hasQuoteOptions: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('인용 옵션이 없는 블록 컨텍스트는 기본 배경을 사용한다', () => {
|
||||||
|
assert.deepEqual(resolveActiveBlockContext([
|
||||||
|
'이전 문단',
|
||||||
|
'> 첫 줄',
|
||||||
|
'> 둘째 줄'
|
||||||
|
].join('\n'), 2), {
|
||||||
|
kind: 'quote',
|
||||||
|
startLine: 1,
|
||||||
|
endLine: 2,
|
||||||
|
quoteBackground: 'gray',
|
||||||
|
hasQuoteOptions: false
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user