feat(M02): 면적 값 고침이 저장 전 도면 패널 표에 바로 비침 · 이름 · 보내기 취소는 [저장] 때 서버가 기록(112-1·112-2)

- 112-1 면적 표 변환을 공용 파일(spreadsheet_area_table.ts)로 빼 서버 Node 와 브라우저가 한 벌로 씀 · 도면 패널이 칸 고침마다(값 바뀐 표만 · 펼친 동안만) CAD 연결 표 갈기를 보기 전용 갈음(preview)으로 보냄
- 112-2 보낸 범위 이름은 도면 [저장] 요청 안에서 서버가 면적 문서에 둠(놓기만 하고 저장 안 하면 안 생김) · [보내기 취소]는 화면에서만 빼고 면적 [저장] 때 서버가 연결표 줄 · 이름을 뺌 · /name · /unlink 길 삭제

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgChuAcyBqUkhHMTXomQC7
This commit is contained in:
2026-10-08 17:36:44 +09:00
co-authored by Claude Sonnet 5.5
parent 2a830980c7
commit 5638bd5101
17 changed files with 797 additions and 322 deletions
@@ -3,7 +3,13 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
import { Actor } from 'xstate';
import { TableEntity } from '../entities/TableEntity.ts';
import { ActorEvent } from '../tools/tool.types.ts';
import { getEntities, isDrawingDirty, setEntities, setLayers } from '../state.ts';
import {
getEntities,
isDrawingDirty,
setEntities,
setHostReadOnly,
setLayers,
} from '../state.ts';
import {
getPendingTable,
placeTableToolStateMachine,
@@ -158,6 +164,27 @@ describe('표 칸 갈기(update-table)', () => {
expect(getEntities()).toBe(before);
});
it('보기 전용 도면은 막고(0) · preview 로 온 갈기만 보기 전용에서도 갈음(112-1)', async () => {
const linked = table('T1');
setEntities([linked]);
setHostReadOnly(true);
const cells = [
[{ text: '항목' }, null],
[{ text: '면적' }, { text: '77.0' }],
];
const notify = vi.fn();
await handleTableMessage({ type: AISLO_UPDATE_TABLE_MESSAGE, link: 'T1', cells }, notify);
expect(notify).toHaveBeenLastCalledWith(AISLO_TABLE_UPDATED_MESSAGE, { link: 'T1', count: 0 });
expect(linked.getCell(1, 1)?.text).toBe('12.5');
await handleTableMessage(
{ type: AISLO_UPDATE_TABLE_MESSAGE, link: 'T1', cells, preview: true },
notify
);
expect(notify).toHaveBeenLastCalledWith(AISLO_TABLE_UPDATED_MESSAGE, { link: 'T1', count: 1 });
expect(linked.getCell(1, 1)?.text).toBe('77.0');
setHostReadOnly(false);
});
it('표 메시지가 아니면 false', async () => {
expect(await handleTableMessage({ type: 'aislo:b08:save-request' }, vi.fn())).toBe(false);
});
@@ -1,8 +1,8 @@
/**
* 부모 화면 ↔ CAD 표 메시지(M02 41장 · 면적산출 범위 → 도면 표). 짝 = `ui_template/cad_host/cad_host.ts`.
* place-table{entity, link} 표 JSON(TableEntity 꼴) → `style.연결 = link` 로 표 놓기 도구(PLACETABLE) 시작
* update-table{link, cells} `style.연결 === link` 인 표의 칸만 갈아 끼움(자리 · 크기 그대로 · 되돌리기 한 걸음)
* → 부모에 table-updated{link, count}
* update-table{link, cells, preview?} `style.연결 === link` 인 표의 칸만 갈아 끼움(자리 · 크기 그대로 · 되돌리기 한 걸음)
* → 부모에 table-updated{link, count} · preview 면 보기 전용이어도 갈음(M02 면적 도면 패널 · 112-1)
* place-cancelled{} (CAD → 부모) 표를 놓지 않고 Esc 로 끝남
* 놓기 · 갈기 모두 `setEntities(…, true)` 를 거쳐 drawing-changed 가 간다. 확정 · 보기 전용이면 안 함.
*/
@@ -25,6 +25,8 @@ interface TableMessage {
entity?: JsonEntity;
link?: string;
cells?: TableCells;
/** update 전용 — 보기 전용 미리보기 패널이 저장 전 값을 비춤(도면 문서는 안 고침 · 보기 전용이어도 갈음) */
preview?: boolean;
}
/** 이 표가 그 연결의 것인가 */
@@ -77,7 +79,7 @@ export async function handleTableMessage(
return true;
}
if (message.type === AISLO_UPDATE_TABLE_MESSAGE && message.link && message.cells) {
const count = blocked() ? 0 : updateLinkedTables(message.link, message.cells);
const count = message.preview || !blocked() ? updateLinkedTables(message.link, message.cells) : 0;
notify(AISLO_TABLE_UPDATED_MESSAGE, { link: message.link, count });
return true;
}