fix(m02): 이름 없는 옛 보낸 범위 연결 — 면적 열 때 도면_{연결} 이름 · 면적 저장 때 연결표 줄에 이름(114-1)

- 면적 상세를 열 때 화면이 이름 없는 보낸 범위마다 책에 이름정의를 둠(고침 표시 없음) · 그 이름으로 점선
- 면적 [저장] 때 서버 refresh_drawing 이 책에 도면_{연결} 이 있으면 연결표 줄에 이름 적음
- 옛 줄 시험 기대 뒤집음 · 새 시험(옛 연결 → 열기 → 행 넣기 → 저장 → 도면 표 값 따라감)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgChuAcyBqUkhHMTXomQC7
This commit is contained in:
2026-10-08 18:26:34 +09:00
co-authored by Claude Opus 5.5
parent 5f64633df3
commit f44789c33c
5 changed files with 195 additions and 6 deletions
+19
View File
@@ -25,6 +25,18 @@ DROPPED = "지운연결"
CANCELS = "보내기취소" # 면적 [저장] 문서의 임시 칸 — 저장 안 함(112-2)
# spreadsheet_area_table.ts `NAME_GONE` 과 같은 글자 — 이름이 지워졌을 때 까닭
NAME_GONE = "이름없음"
NAME = "도면_" # 보낸 범위 이름 앞머리 — AreaToDrawing.ts `NAME` 과 같은 글자
def _book_names(book: Any) -> set[str]:
"""책 모든 시트 이름정의 이름(작은 글자 · 화면 `resolveName` 과 같이 대소문자 무시)."""
return {
str(n.get("이름", "")).lower()
for s in (book.get("시트") or [] if isinstance(book, dict) else [])
if isinstance(s, dict)
for n in s.get("이름정의") or []
if isinstance(n, dict)
}
def to_table(
@@ -119,6 +131,7 @@ def refresh_drawing(column: str, unlink: Any = ()) -> list[str]:
"""지금 층 그 열 도면(모든 장)의 연결표마다 칸 글 다시 · 마지막 값 갱신 → 칸을 간 표의 `연결` 목록.
`unlink` = 면적 [저장] 때 [보내기 취소]한 연결 — 그 줄을 연결표에서 뺌
(도면 표는 남음 · 알림 없음 · 112-2).
이름 없는 옛 줄은 책에 `도면_{연결}` 이름이 있으면 그 이름을 줄에 적음(114-1 · 면적 화면이 열 때 둠).
이름(104-1)이 있으면 그 이름이 지금 가리키는 범위로 `범위` 도 갱신 — 이름이 지워졌으면(범위가
통째로 지워짐) 그 연결을 끊고(도면 표는 남음) `지운연결` 목록에 적어 다음에 열 때 알림."""
try:
@@ -137,6 +150,12 @@ def refresh_drawing(column: str, unlink: Any = ()) -> list[str]:
atomic_write_json(drawing, doc)
return []
book = json.loads(area.read_bytes())
names = _book_names(book)
for (
x
) in links: # 이름 없는 옛 연결(114-1) — 면적 화면이 책에 둔 `도면_{연결}` 이 있으면 줄에 적음
if not x.get("이름") and f"{NAME}{x['연결']}".lower() in names:
x["이름"] = f"{NAME}{x['연결']}"
asks = [
{
"book": book,
@@ -31,6 +31,7 @@ import {
type StructureDoc,
} from "./M02_MasterTemplete_Api_Fetch";
import type { LinkOptions } from "../A00_Common/spreadsheet/spreadsheet";
import type { Workbook } from "../A00_Common/spreadsheet/spreadsheet_types";
import type { Selection } from "./M02_MasterTemplete_UI_Main";
/** 표 칸 하나(CAD `TableCell` 꼴에서 쓰는 것만) */
@@ -141,18 +142,41 @@ export const sameValues = (
return ka.length === Object.keys(b).length && ka.every((k) => a[k] === b[k]);
};
/** 면적 시트에 겹쳐 보일 「도면으로 보낸 범위」 — 같은 열 도면 `연결표` 에서(받기 실패 = 없음 · 조용히) */
/** 이름 없는 옛 연결(114-1) — 책에 `도면_{연결}` 이름정의를 적힌 범위로 둠(이미 있으면 그대로) → 그 이름.
* 책을 바로 고침(화면에 싣기 전 · 「저장 안 한 고침」 아님) · 서버는 면적 [저장] 때 그 이름을 연결표 줄에 적음 */
async function nameOld(book: Workbook, row: TableLink): Promise<string | undefined> {
const name = `${NAME}${row.연결}`;
const low = name.toLowerCase();
if (book.시트.some((s) => s.이름정의?.some((n) => n.이름.toLowerCase() === low))) return name;
const { parseRange } = await import("../A00_Common/spreadsheet/spreadsheet_address");
const sheet = book.시트.find((s) => s.id === row.시트);
const g = parseRange(row.범위);
if (!sheet || !g) return undefined; // 못 풂 — 적힌 범위로(옛 그대로)
sheet.이름정의 = [...(sheet.이름정의 ?? []), { 이름: name, 범위: g }];
return name;
}
/** 면적 시트에 겹쳐 보일 「도면으로 보낸 범위」 — 같은 열 도면 `연결표` 에서(받기 실패 = 없음 · 조용히) ·
* `book`(면적 책 · 화면에 싣기 전) 있으면 이름 없는 옛 연결에 이름을 둠(114-1 · 행열 넣기를 따라감) */
export async function sentRanges(
at: Selection,
onCancel: (연결: string) => void,
book?: Workbook,
): Promise<NonNullable<LinkOptions["표시범위"]>> {
try {
const got = await readTemplate(at.layer, "structure", at.name, at.projectId);
const rows = (got.문서 as StructureDoc | undefined)?.연결표 ?? [];
return rows.map((row) => ({
const names = await Promise.all(
rows.map((row) =>
row.이름 || !book || !Array.isArray(book.시트)
? row.이름
: nameOld(book, row).catch(() => undefined),
),
);
return rows.map((row, i) => ({
시트: row.시트,
범위: row.범위,
범위이름: row.이름,
범위이름: names[i],
이름: fill(TEXT.tag, "id", row.연결),
설명: fill(fill(fill(TEXT.tip, "id", row.연결), "range", row.범위), "size", row.글자크기),
취소: async () => (onCancel(row.연결), true), // 화면에서만 — 서버는 면적 [저장] 때
@@ -298,7 +298,8 @@ export function createMain(
const [{ createSpreadsheet }, pills, sent] = await Promise.all([
import("../A00_Common/spreadsheet/spreadsheet"),
pillsFor(kind, at.name),
sentRanges(at, toDrawing.cancel), // 도면으로 보낸 범위 — 시트 위 점선 테두리
// 도면으로 보낸 범위 — 시트 위 점선 테두리 · 이름 없는 옛 연결은 책에 이름을 둠(114-1)
sentRanges(at, toDrawing.cancel, doc as Parameters<typeof sentRanges>[2]),
]);
if (mine !== seq) return;
if (pills.notice) showNotice([pills.notice]);