라이브 모드 코드/콜아웃/토글 편집, 슬래시 명령, 홈 Latest List·Compact·Cards 보기, 사이트 설정 메인 화면 커버(720px) 및 HomeHero 반영. Co-authored-by: Cursor <cursoragent@cursor.com>
243 lines
5.3 KiB
JavaScript
243 lines
5.3 KiB
JavaScript
/**
|
|
* 인용 접두사 제거
|
|
* @param {string} value - 원본
|
|
* @returns {string} 본문
|
|
*/
|
|
export const stripQuoteMarker = (value) => String(value ?? '').replace(/^(?:>\s*)+/, '').trim()
|
|
|
|
/**
|
|
* 목록 접두사 제거
|
|
* @param {string} value - 원본
|
|
* @param {boolean} ordered - 순서 목록 여부
|
|
* @returns {string} 본문
|
|
*/
|
|
export const stripListMarker = (value, ordered = false) => {
|
|
const raw = String(value ?? '').trim()
|
|
|
|
if (ordered) {
|
|
return raw.replace(/^\d+\.\s*/, '').trim()
|
|
}
|
|
|
|
return raw.replace(/^[-*+]\s+/, '').trim()
|
|
}
|
|
|
|
/**
|
|
* 인용 마커 포함 여부
|
|
* @param {string} value - 원본
|
|
* @returns {boolean}
|
|
*/
|
|
export const hasQuoteMarker = (value) => /^\s*>/.test(String(value ?? ''))
|
|
|
|
/**
|
|
* 목록 마커 포함 여부
|
|
* @param {string} value - 원본
|
|
* @param {boolean} [ordered=false] - 순서 목록 여부
|
|
* @returns {boolean}
|
|
*/
|
|
export const hasListMarker = (value, ordered = false) => {
|
|
const raw = String(value ?? '').trim()
|
|
|
|
if (ordered) {
|
|
return /^\d+\.\s*/.test(raw)
|
|
}
|
|
|
|
return /^[-*+]\s/.test(raw)
|
|
}
|
|
|
|
/**
|
|
* 순서 목록 마커를 파싱한다.
|
|
* @param {string} value - 원본 줄
|
|
* @returns {{ number: number, body: string }|null}
|
|
*/
|
|
export const parseOrderedListMarker = (value) => {
|
|
const raw = String(value ?? '').trim()
|
|
const match = raw.match(/^(\d+)\.\s*(.*)$/)
|
|
|
|
if (!match) {
|
|
return null
|
|
}
|
|
|
|
return {
|
|
number: Number(match[1]),
|
|
body: match[2].trim()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 순서 목록 다음 줄 마커를 만든다.
|
|
* @param {string} line - 현재 줄
|
|
* @returns {string} 다음 줄 마커
|
|
*/
|
|
export const getNextOrderedListLine = (line) => {
|
|
const parsed = parseOrderedListMarker(line)
|
|
|
|
if (!parsed) {
|
|
return ''
|
|
}
|
|
|
|
return `${parsed.number + 1}. `
|
|
}
|
|
|
|
/**
|
|
* 마커만 있고 본문이 없는 목록 줄인지 확인한다.
|
|
* @param {string} line - 마크다운 줄
|
|
* @param {boolean} ordered - 순서 목록 여부
|
|
* @returns {boolean}
|
|
*/
|
|
export const isEmptyListMarkerLine = (line, ordered = false) => {
|
|
if (!hasListMarker(line, ordered)) {
|
|
return false
|
|
}
|
|
|
|
if (ordered) {
|
|
const parsed = parseOrderedListMarker(line)
|
|
return parsed ? !parsed.body : false
|
|
}
|
|
|
|
return !stripListMarker(line, false).trim()
|
|
}
|
|
|
|
/**
|
|
* 마커만 있고 본문이 없는 인용 줄인지 확인한다.
|
|
* @param {string} line - 마크다운 줄
|
|
* @returns {boolean}
|
|
*/
|
|
export const isEmptyQuoteMarkerLine = (line) => {
|
|
if (!hasQuoteMarker(line)) {
|
|
return false
|
|
}
|
|
|
|
return !stripQuoteMarker(line).trim()
|
|
}
|
|
|
|
/**
|
|
* 이전 마크다운 줄 끝에 텍스트를 이어 붙인다.
|
|
* @param {string} line - 이전 줄
|
|
* @param {string} appendText - 붙일 본문
|
|
* @returns {string} 병합된 줄
|
|
*/
|
|
export const appendTextToMarkdownLine = (line, appendText) => {
|
|
const prev = String(line ?? '')
|
|
const add = String(appendText ?? '')
|
|
|
|
if (!add) {
|
|
return prev
|
|
}
|
|
|
|
const ordered = parseOrderedListMarker(prev)
|
|
|
|
if (ordered) {
|
|
return `${ordered.number}. ${ordered.body}${add}`
|
|
}
|
|
|
|
if (hasQuoteMarker(prev)) {
|
|
const body = stripQuoteMarker(prev)
|
|
return `> ${body}${add}`
|
|
}
|
|
|
|
if (hasListMarker(prev, false)) {
|
|
const body = stripListMarker(prev, false)
|
|
return `- ${body}${add}`
|
|
}
|
|
|
|
return `${prev}${add}`
|
|
}
|
|
|
|
/**
|
|
* 편집 값에서 이전 줄에 붙일 본문만 추출한다.
|
|
* @param {string} value - 편집 값
|
|
* @param {string} previousLine - 이전 마크다운 줄
|
|
* @param {boolean} raw - 원문 모드 여부
|
|
* @returns {string} 본문
|
|
*/
|
|
export const getAppendTextForMerge = (value, previousLine, raw = false) => {
|
|
const text = String(value ?? '').trim()
|
|
|
|
if (!text) {
|
|
return ''
|
|
}
|
|
|
|
if (raw) {
|
|
if (hasQuoteMarker(text)) {
|
|
return stripQuoteMarker(text)
|
|
}
|
|
|
|
if (hasListMarker(text, true)) {
|
|
return stripListMarker(text, true)
|
|
}
|
|
|
|
if (hasListMarker(text, false)) {
|
|
return stripListMarker(text, false)
|
|
}
|
|
|
|
return text
|
|
}
|
|
|
|
if (hasListMarker(previousLine, true)) {
|
|
return stripListMarker(text, true)
|
|
}
|
|
|
|
if (hasListMarker(previousLine, false)) {
|
|
return stripListMarker(text, false)
|
|
}
|
|
|
|
if (hasQuoteMarker(previousLine)) {
|
|
return stripQuoteMarker(text)
|
|
}
|
|
|
|
return text
|
|
}
|
|
|
|
/**
|
|
* 줄 병합 후 편집 영역에 둘 커서 오프셋(이전 줄 본문 끝)을 반환한다.
|
|
* @param {string} previousLine - 이전 마크다운 줄
|
|
* @param {boolean} [raw=false] - 원문 모드 여부
|
|
* @returns {number} 표시 텍스트 기준 오프셋
|
|
*/
|
|
export const getMergeJunctionDisplayOffset = (previousLine, raw = false) => {
|
|
const prev = String(previousLine ?? '')
|
|
|
|
if (raw) {
|
|
return prev.length
|
|
}
|
|
|
|
const ordered = parseOrderedListMarker(prev)
|
|
|
|
if (ordered) {
|
|
return ordered.body.length
|
|
}
|
|
|
|
if (hasListMarker(prev, true)) {
|
|
return stripListMarker(prev, true).length
|
|
}
|
|
|
|
if (hasListMarker(prev, false)) {
|
|
return stripListMarker(prev, false).length
|
|
}
|
|
|
|
if (hasQuoteMarker(prev)) {
|
|
return stripQuoteMarker(prev).length
|
|
}
|
|
|
|
return prev.length
|
|
}
|
|
|
|
/**
|
|
* 제목 접두사 제거
|
|
* @param {string} value - 원본
|
|
* @returns {{ level: number, text: string }} 레벨·본문
|
|
*/
|
|
export const stripHeadingMarker = (value) => {
|
|
const raw = String(value ?? '').trim()
|
|
const match = raw.match(/^(#{1,6})\s+(.*)$/)
|
|
|
|
if (!match) {
|
|
return { level: 0, text: raw }
|
|
}
|
|
|
|
return {
|
|
level: match[1].length,
|
|
text: match[2].trim()
|
|
}
|
|
}
|