feat(M02): 보낸 범위를 면적 시트 이름정의로 추적, 행열 넣기·지우기 따라감(104-1)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MPcuokNm1ZR2kqgGGDfaJA
This commit is contained in:
2026-10-08 16:43:45 +09:00
co-authored by Claude Sonnet 5
parent d14513217c
commit 162880c012
8 changed files with 333 additions and 20 deletions
+51 -7
View File
@@ -26,19 +26,33 @@ import {
import { cellStyleAt } from "../A00_Common/spreadsheet/spreadsheet_cellstyle";
import { createCalcEngine } from "../A00_Common/spreadsheet/spreadsheet_graph";
import { DEFAULT_COL_CHARS, DEFAULT_ROW_PT } from "../A00_Common/spreadsheet/spreadsheet_headers";
import { resolveName } from "../A00_Common/spreadsheet/spreadsheet_names";
import { formatValue } from "../A00_Common/spreadsheet/spreadsheet_numfmt";
import { recalcWorkbook } from "../A00_Common/spreadsheet/spreadsheet_recalc";
import type { CellRange, HAlign, Workbook } from "../A00_Common/spreadsheet/spreadsheet_types";
/** 표 하나 물음 — `시트` = 시트 id 또는 이름 · `범위` = `B3:F12` · `글자크기` = 도면 단위(기본 2.5) */
/** 범위 대신 이름을 줬는데 그 이름이 지금 책에 없음(104-1 · 범위가 통째로 지워짐) — python 쪽이 문자열로 가름 */
export const NAME_GONE = "이름없음";
/** 표 하나 물음 — `시트` = 시트 id 또는 이름 · `범위` = `B3:F12`(이름 없을 때만 씀) · `이름` 이 있으면
* 그 이름이 지금 가리키는 범위로 품(104-1 · 행열 넣기 · 지우기를 따라간 뒤 범위) · `글자크기` = 도면 단위(기본 2.5) */
interface TableAsk {
book: Workbook;
시트: string;
범위: string;
이름?: string;
글자크기?: number;
한도?: number;
}
/** 면적 시트에 보낸 범위 이름을 더하거나(같은 이름 있으면) 범위를 갱신(104-1 · 처음 보낼 때 1번) */
interface NameAsk {
book: Workbook;
시트: string;
범위: string;
이름: string;
}
/** CAD `TableCell`(openwebcad `helpers/table-geometry.ts`) 꼴 */
interface TableCell {
text: string;
@@ -64,11 +78,20 @@ const ALIGN: Partial<Record<HAlign, TableCell["align"]>> = {
function areaTable(ask: TableAsk) {
const book = ask.book;
const sheet =
book.시트.find((s) => s.id === ask.시트) ?? book.시트.find((s) => s.이름 === ask.시트);
if (!sheet) throw new Error(`없는 시트 「${ask.시트}」`);
const range = parseRange(ask.범위);
if (!range) throw new Error(`범위 꼴 아님 「${ask.범위}」`);
let sheetId = ask.시트;
let range: CellRange;
if (ask.이름) {
const found = resolveName(book, ask.시트, ask.이름);
if (!found) throw new Error(NAME_GONE); // 이름을 지웠거나 범위가 통째로 지워짐
sheetId = found.시트;
range = found.범위;
} else {
const parsed = parseRange(ask.범위);
if (!parsed) throw new Error(`범위 꼴 아님 「${ask.범위}」`);
range = parsed;
}
const sheet = book.시트.find((s) => s.id === sheetId) ?? book.시트.find((s) => s.이름 === sheetId);
if (!sheet) throw new Error(`없는 시트 「${sheetId}」`);
const font = ask.글자크기 && ask.글자크기 > 0 ? ask.글자크기 : FONT;
const limit = ask.한도 ?? LIMIT;
const span = (a: number, b: number): number[] =>
@@ -146,6 +169,19 @@ function areaTable(ask: TableAsk) {
return { 표, 시트: sheet.id, 범위: rangeToA1(range), 값 };
}
/** 이름정의를 더하거나(있으면) 범위만 바꿈 — 책 사본을 돌려줌(저장은 python 쪽) */
function defineAreaName(ask: NameAsk): { book: Workbook } {
const book = ask.book;
const sheet =
book.시트.find((s) => s.id === ask.시트) ?? book.시트.find((s) => s.이름 === ask.시트);
if (!sheet) throw new Error(`없는 시트 「${ask.시트}」`);
const range = parseRange(ask.범위);
if (!range) throw new Error(`범위 꼴 아님 「${ask.범위}」`);
const kept = (sheet.이름정의 ?? []).filter((n) => n.이름 !== ask.이름);
sheet.이름정의 = [...kept, { 이름: ask.이름, 범위: range }];
return { book };
}
const [inputPath, outputPath] = process.argv.slice(2);
if (!inputPath || !outputPath) {
console.error("사용법: node <번들> <입력.json> <출력.json>");
@@ -155,6 +191,7 @@ if (!inputPath || !outputPath) {
const input = JSON.parse(readFileSync(inputPath, "utf8")) as {
books?: Workbook[];
tables?: TableAsk[];
names?: NameAsk[];
};
const results = (input.books ?? []).map((book) => {
try {
@@ -170,4 +207,11 @@ const tables = (input.tables ?? []).map((ask) => {
return { ok: false, 까닭: error instanceof Error ? error.message : String(error) };
}
});
writeFileSync(outputPath, JSON.stringify({ results, tables }));
const names = (input.names ?? []).map((ask) => {
try {
return { ok: true, ...defineAreaName(ask) };
} catch (error) {
return { ok: false, 까닭: error instanceof Error ? error.message : String(error) };
}
});
writeFileSync(outputPath, JSON.stringify({ results, tables, names }));