/* ============================================================================= * M01_MasterData_logic_source_notes.ts * 원문 표 맨 아래 서술 줄(비고 · [주] · ※ …) 가르기 — 순수 계산(화면 · fetch 없음 · PLAN 833-1) * 표 끝에 이어 붙은 서술 줄 = 표 밖 글로 · 글 순서 · 내용은 그대로 · 위쪽 줄(표 몸 · 묶음 머리)은 안 건드림 * ========================================================================== */ /** 표 밖으로 나가는 서술 줄 하나 — `label` = 「비고」 · 「[주]」 머리(없으면 빈 글) */ export interface NoteLine { label: string; text: string; } const LABEL = /^(비고|\[?주\]?\)?|※|참고)\s*\d*$/; const MARK = /^(※|-|∙|·|\[주\]|주\)|비고|참고)/; const LONG = 30; /** 한 줄이 서술 줄이면 글 — 아니면 null (머리 줄 · 숫자 줄 · 묶음 머리(짧은 한 칸)는 아님) */ export function noteLine(row: string[]): NoteLine | null { if (row.length < 2) return null; const full = row.map((c, k) => [k, c.trim()] as const).filter(([, c]) => c); if (full.length === 1) { const [[, text]] = full; return MARK.test(text) || text.length >= LONG ? { label: "", text } : null; } if (full.length === 2 && full[0][0] === 0 && LABEL.test(full[0][1])) return { label: full[0][1], text: full[1][1] }; return null; } /** 표 → 몸 줄 + 맨 아래 이어 붙은 서술 줄 — 머리(첫 줄)는 늘 몸 */ export function splitTableNotes(table: string[][]): { rows: string[][]; notes: NoteLine[] } { let end = table.length; while (end > 1 && noteLine(table[end - 1])) end--; return { rows: table.slice(0, end), notes: table.slice(end).map((r) => noteLine(r) as NoteLine), }; } /** 칸 글이 줄바꿈 대상 — 한 줄이 30자 이상(서술 칸) · 한 칸짜리 표(글 상자) · 숫자 칸은 안 접음 */ export function wrapCell(text: string, cols: number): boolean { return cols === 1 || text.split("\n").some((l) => l.length >= LONG); }