feat(spreadsheet): 공개 손잡이 addSheet · 되돌리기 한 번 · 탭 없이 띄운 시트에 탭 줄 붙임 · 도면 표 → 면적 새 시트 검증(83-3)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
/* 83-3 — 받은 표 → 면적 새 시트(addSheet) · 탭 줄 · 되돌리기 한 번 · 화면 고침 표시
|
||||
* 시작 화면: M02 · 시스템 층 임시 항목(「데스크탑보조임시」 · 시트 1장) 면적산출 상세 · 「도면」 패널 펼침
|
||||
* 뒤: [저장] → area/<열>.json 새 시트 병합 B1:C1 · 열 폭 21.6/16.2/16.2 · 행 높이 28.3 · 다시 열기 = 탭 2장 · 임시 항목 지움
|
||||
* 부르는 법: orca eval --expression "$(cat 이 파일)" (async 식 · JSON 글을 돌려줌)
|
||||
* 하는 일: 패널 CAD 에 선 표 견본을 보기 전용으로 싣고(저장 안 됨) 끌어 고른 뒤 [면적산출로 보내기]
|
||||
* → 탭 · 활성 시트 · 칸 글 · 병합 수를 읽음 → 되돌리기(↶) → 다시 읽음 → 다시 보냄(저장은 부른 쪽)
|
||||
* 기대 수치:
|
||||
* - before.tabs = 0(탭 줄 없음) · added.tabs = ["…", "도면에서 1"] · added.active = "도면에서 1"
|
||||
* - added.cells = 구분 · 수량 · 흙깎기 · 1500 · m3 · 돌쌓기 · 2.5 · m2 · added.widths 비 = 40:30:30(156 · 118 · 118 px)
|
||||
* - undone.tabsShown = false · undone.sheets = 1(탭 줄 숨음) · again.active = "도면에서 1"
|
||||
* - 끝에 원래 도면을 다시 실음(restored)
|
||||
*/
|
||||
(async () => {
|
||||
const W = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
const $ = (s, root = document) => root.querySelector(s);
|
||||
const $$ = (s, root = document) => [...root.querySelectorAll(s)];
|
||||
const frame = $('.ui-side-panels__panel[data-panel="drawing"] iframe');
|
||||
const btn = $("[data-area-from-drawing]");
|
||||
if (!frame || !btn) return JSON.stringify({ error: "도면 패널 · 단추 없음" });
|
||||
const win = frame.contentWindow;
|
||||
const cad = win.__aisloCad;
|
||||
const L = "L83";
|
||||
let n = 0;
|
||||
const base = { lineColor: "#fff", lineWidth: 1, layerId: L };
|
||||
const line = (x1, y1, x2, y2) => ({
|
||||
id: `t83-${(n += 1)}`,
|
||||
type: "Line",
|
||||
...base,
|
||||
shapeData: { startPoint: { x: x1, y: y1 }, endPoint: { x: x2, y: y2 } },
|
||||
});
|
||||
const text = (label, x, y) => ({
|
||||
id: `t83-${(n += 1)}`,
|
||||
type: "Text",
|
||||
...base,
|
||||
shapeData: {
|
||||
label,
|
||||
basePoint: { x, y },
|
||||
options: {
|
||||
textDirection: { x: 1, y: 0 },
|
||||
textAlign: "left",
|
||||
textColor: "#fff",
|
||||
fontSize: 2.5,
|
||||
fontFamily: "Noto Sans KR",
|
||||
},
|
||||
},
|
||||
});
|
||||
const grid = [
|
||||
line(100, 60, 200, 60),
|
||||
line(100, 30, 200, 30),
|
||||
line(100, 60, 100, 30),
|
||||
line(200, 60, 200, 30),
|
||||
line(100, 50, 200, 50),
|
||||
line(100, 40, 200, 40),
|
||||
line(140, 60, 140, 30),
|
||||
line(170, 50, 170, 30),
|
||||
text("구분", 102, 53),
|
||||
text("수량", 145, 53),
|
||||
text("흙깎기", 102, 43),
|
||||
text("1,500", 142, 43),
|
||||
text("m3", 172, 43),
|
||||
text("돌쌓기", 102, 33),
|
||||
text("2.5", 142, 33),
|
||||
text("m2", 172, 33),
|
||||
];
|
||||
const ask = (type, want, extra = {}) =>
|
||||
new Promise((resolve) => {
|
||||
const on = (e) => {
|
||||
if (e.source !== win || e.data?.type !== want) return;
|
||||
window.removeEventListener("message", on);
|
||||
resolve(e.data);
|
||||
};
|
||||
window.addEventListener("message", on);
|
||||
win.postMessage({ type, ...extra }, location.origin);
|
||||
setTimeout(() => resolve(null), 5000);
|
||||
});
|
||||
const original = (await ask("aislo:b08:save-request", "aislo:b08:save-response"))?.drawing;
|
||||
const load = (drawing) =>
|
||||
ask("aislo:b08:load-drawing", "aislo:b08:drawing-loaded", {
|
||||
drawing,
|
||||
readOnly: true,
|
||||
panelsCollapsed: true,
|
||||
recoveryScope: "verify83",
|
||||
});
|
||||
await load({
|
||||
format: 6,
|
||||
entities: grid,
|
||||
layers: [{ id: L, name: "검증83", isVisible: true, isLocked: false }],
|
||||
});
|
||||
await W(400);
|
||||
const canvas = frame.contentDocument.querySelector("canvas");
|
||||
const fire = (type, p) => {
|
||||
const r = canvas.getBoundingClientRect();
|
||||
canvas.dispatchEvent(
|
||||
new win.MouseEvent(type, {
|
||||
bubbles: true,
|
||||
clientX: r.left + p.x,
|
||||
clientY: r.bottom - p.y,
|
||||
button: 0,
|
||||
buttons: type === "mouseup" ? 0 : 1,
|
||||
}),
|
||||
);
|
||||
};
|
||||
const a = cad.screen(95, 25),
|
||||
b = cad.screen(205, 65);
|
||||
fire("mousemove", a);
|
||||
await W(60);
|
||||
fire("mousedown", a);
|
||||
await W(60);
|
||||
fire("mousemove", b);
|
||||
await W(60);
|
||||
fire("mouseup", b);
|
||||
await W(250);
|
||||
const ss = $(".m02-main__host .ss");
|
||||
const state = () => {
|
||||
const bar = $(".ss-tabs", ss);
|
||||
return {
|
||||
tabsShown: !!bar && !bar.hidden,
|
||||
tabs: bar ? $$(".ss-tabs__tab", bar).map((t) => t.innerText.trim()) : [],
|
||||
active: $(".ss-tabs__tab.is-active", ss)?.innerText.trim() ?? null,
|
||||
sheets: bar ? $$(".ss-tabs__tab", bar).length : 1,
|
||||
cells: $$(".aislo-cell__text", ss)
|
||||
.map((c) => c.innerText.trim())
|
||||
.filter(Boolean)
|
||||
.slice(0, 12),
|
||||
widths: $$(".aislo-grid__colhead", ss)
|
||||
.slice(0, 3)
|
||||
.map((c) => Math.round(c.getBoundingClientRect().width)),
|
||||
notabsClass: ss.classList.contains("ss--notabs"),
|
||||
};
|
||||
};
|
||||
const out = { selected: cad.selection().length, before: state() };
|
||||
btn.click();
|
||||
await W(800);
|
||||
out.added = state();
|
||||
const undo = $$("button", ss).find((x) => /되돌리기|실행 취소|↶/.test(x.title + x.innerText));
|
||||
undo?.click();
|
||||
await W(500);
|
||||
out.undone = state();
|
||||
btn.click();
|
||||
await W(800);
|
||||
out.again = state();
|
||||
out.restored = !!(await load(original));
|
||||
return JSON.stringify(out);
|
||||
})();
|
||||
Reference in New Issue
Block a user