feat(B07,B08): 워크플로 순서 교환 — 상세설계를 수량산출 앞으로
횡단설계(B06) 다음을 상세설계 → 수량산출 → 설계도서 순으로 재배열하고, 폴더 번호가 흐름과 일치하도록 이름을 맞바꾼다. - B08_DesignDetail → B07_DesignDetail, B07_Quantity → B08_Quantity (파일 접두어·식별자·라우트·locale 키 전량 스왑) - STAGE_KEYS 4=DESIGN_DETAIL, 5=QUANTITY 스왑 + 라우터 stage 리터럴 교체 - CAD 마운트 /b08-cad → /b07-cad (main.py·vite proxy·iframe URL), openwebcad Toolbar 라벨 B07로 수정 후 재빌드 - 유지: openwebcad postMessage 프로토콜 aislo:b08:*·패키지명(내부 식별자) - 기존 프로젝트 storage 폴더 rename + project_manifest 갱신, DB project_workflow_stages stage_no 4↔5 행 스왑 완료 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import { Point } from '@flatten-js/core';
|
||||
import { type DesignMeta, HtmlEvent } from '../App.types.ts';
|
||||
import { TextEntity } from '../entities/TextEntity.ts';
|
||||
import type { JsonDrawingFileSerialized } from '../helpers/import-export-handlers/export-entities-to-json.ts';
|
||||
import { exportEntitiesAndLayersToJsonString } from '../helpers/import-export-handlers/export-entities-to-json.ts';
|
||||
import { getEntitiesAndLayersFromJsonObject } from '../helpers/import-export-handlers/import-entities-from-json.ts';
|
||||
import {
|
||||
getCanvas,
|
||||
getDesignMeta,
|
||||
getEntities,
|
||||
getLayers,
|
||||
getScreenCanvasDrawController,
|
||||
setActiveLayerId,
|
||||
setDesignMeta,
|
||||
setEntities,
|
||||
setLayers,
|
||||
} from '../state.ts';
|
||||
|
||||
export const AISLO_DRAWING_LOAD_MESSAGE = 'aislo:b08:load-drawing';
|
||||
export const AISLO_DRAWING_READY_MESSAGE = 'aislo:b08:drawing-ready';
|
||||
export const AISLO_DRAWING_LOADED_MESSAGE = 'aislo:b08:drawing-loaded';
|
||||
export const AISLO_DRAWING_ERROR_MESSAGE = 'aislo:b08:drawing-error';
|
||||
export const AISLO_DRAWING_CHANGED_MESSAGE = 'aislo:b08:drawing-changed';
|
||||
export const AISLO_DRAWING_SAVE_REQUEST_MESSAGE = 'aislo:b08:save-request';
|
||||
export const AISLO_DRAWING_SAVE_RESPONSE_MESSAGE = 'aislo:b08:save-response';
|
||||
export const AISLO_DRAWING_NAVIGATE_MESSAGE = 'aislo:b08:navigate';
|
||||
|
||||
interface DrawingLoadMessage {
|
||||
type: typeof AISLO_DRAWING_LOAD_MESSAGE;
|
||||
drawing: JsonDrawingFileSerialized;
|
||||
meta?: DesignMeta | null;
|
||||
}
|
||||
|
||||
interface DrawingSaveRequestMessage {
|
||||
type: typeof AISLO_DRAWING_SAVE_REQUEST_MESSAGE;
|
||||
}
|
||||
|
||||
function isDrawingLoadMessage(value: unknown): value is DrawingLoadMessage {
|
||||
if (!value || typeof value !== 'object') return false;
|
||||
const candidate = value as Partial<DrawingLoadMessage>;
|
||||
return candidate.type === AISLO_DRAWING_LOAD_MESSAGE && Boolean(candidate.drawing);
|
||||
}
|
||||
|
||||
function notifyParent(type: string, payload: Record<string, unknown> = {}) {
|
||||
if (window.parent === window) return;
|
||||
window.parent.postMessage({ type, ...payload }, window.location.origin);
|
||||
}
|
||||
|
||||
/** 수량 패널에서 이전/다음 도면으로 이동 요청 (부모가 처리). */
|
||||
export function requestDrawingNavigation(direction: 'prev' | 'next') {
|
||||
notifyParent(AISLO_DRAWING_NAVIGATE_MESSAGE, { direction });
|
||||
}
|
||||
|
||||
/** 수량표 값 편집을 부모에 알린다 (확정 상태 롤백 연동). */
|
||||
export function notifyDrawingChangedByTable() {
|
||||
notifyParent(AISLO_DRAWING_CHANGED_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 더블클릭한 위치의 텍스트(잠금 해제 레이어)를 찾아 내용을 즉시 편집한다.
|
||||
* CAD 테이블(측점 테이블·수량 산출표) 값 셀 수정 수단 — 잠금 레이어는 제외.
|
||||
*/
|
||||
function registerTextDoubleClickEdit() {
|
||||
const canvas = getCanvas();
|
||||
if (!canvas) return;
|
||||
canvas.addEventListener('dblclick', (event: MouseEvent) => {
|
||||
const drawController = getScreenCanvasDrawController();
|
||||
const bounds = canvas.getBoundingClientRect();
|
||||
const screenPoint = new Point(event.clientX - bounds.left, bounds.bottom - event.clientY);
|
||||
const worldPoint = drawController.targetToWorld(screenPoint);
|
||||
const lockedLayerIds = new Set(
|
||||
getLayers()
|
||||
.filter((layer) => layer.isLocked)
|
||||
.map((layer) => layer.id)
|
||||
);
|
||||
let closest: TextEntity | null = null;
|
||||
let closestDistance = Number.MAX_SAFE_INTEGER;
|
||||
for (const entity of getEntities()) {
|
||||
if (!(entity instanceof TextEntity) || lockedLayerIds.has(entity.layerId)) continue;
|
||||
const distanceInfo = entity.distanceTo(worldPoint);
|
||||
if (distanceInfo && distanceInfo[0] < closestDistance) {
|
||||
closestDistance = distanceInfo[0];
|
||||
closest = entity;
|
||||
}
|
||||
}
|
||||
if (!closest) return;
|
||||
const fontSize = closest.getTextOptions().fontSize;
|
||||
const tolerance = Math.max((closest.getLabel().length + 2) * fontSize * 0.5, fontSize * 2);
|
||||
if (closestDistance > tolerance) return;
|
||||
const nextLabel = window.prompt('값 수정', closest.getLabel());
|
||||
if (nextLabel === null || nextLabel === closest.getLabel()) return;
|
||||
closest.setLabel(nextLabel);
|
||||
setEntities([...getEntities()], true); // undo 스택 + DRAWING_CHANGED 통지
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* B08 parent page와 CAD 앱 사이의 same-origin JSON 경계다.
|
||||
* DXF/DWG 파일이나 파서 객체는 이 경계를 통과하지 않는다.
|
||||
*/
|
||||
export function registerAisloDrawingBridge() {
|
||||
window.addEventListener('message', async (event: MessageEvent<unknown>) => {
|
||||
if (event.origin !== window.location.origin || event.source !== window.parent) return;
|
||||
const message = event.data as Partial<DrawingSaveRequestMessage>;
|
||||
if (message.type === AISLO_DRAWING_SAVE_REQUEST_MESSAGE) {
|
||||
try {
|
||||
const drawing = JSON.parse(
|
||||
await exportEntitiesAndLayersToJsonString()
|
||||
) as JsonDrawingFileSerialized;
|
||||
// 편집된 수량표 값을 도면과 함께 부모로 돌려준다.
|
||||
const quantityTable = getDesignMeta()?.quantityTable ?? null;
|
||||
notifyParent(AISLO_DRAWING_SAVE_RESPONSE_MESSAGE, { drawing, quantityTable });
|
||||
} catch (error) {
|
||||
const detail = error instanceof Error ? error.message : 'Unable to serialize drawing';
|
||||
notifyParent(AISLO_DRAWING_ERROR_MESSAGE, { detail });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!isDrawingLoadMessage(event.data)) return;
|
||||
|
||||
try {
|
||||
const drawing = await getEntitiesAndLayersFromJsonObject(event.data.drawing);
|
||||
setEntities(drawing.entities, false);
|
||||
setLayers(drawing.layers);
|
||||
setActiveLayerId(drawing.layers[0].id);
|
||||
// 설계 컨텍스트(제목·측점정보·확정상태·수량표)를 수량 패널에 반영
|
||||
setDesignMeta(event.data.meta ?? null);
|
||||
getScreenCanvasDrawController().zoomToFitScreen();
|
||||
notifyParent(AISLO_DRAWING_LOADED_MESSAGE);
|
||||
} catch (error) {
|
||||
const detail = error instanceof Error ? error.message : 'Invalid drawing JSON';
|
||||
notifyParent(AISLO_DRAWING_ERROR_MESSAGE, { detail });
|
||||
}
|
||||
});
|
||||
window.addEventListener(HtmlEvent.DRAWING_CHANGED, () => {
|
||||
notifyParent(AISLO_DRAWING_CHANGED_MESSAGE);
|
||||
});
|
||||
registerTextDoubleClickEdit();
|
||||
|
||||
notifyParent(AISLO_DRAWING_READY_MESSAGE);
|
||||
}
|
||||
Reference in New Issue
Block a user