566 lines
20 KiB
JavaScript
566 lines
20 KiB
JavaScript
/* 280-5 · 281 B07 ⚙ 설정 · 저장 후 이동 헬퍼 — 좌측 목록 ⚙ 자리 · 누름(접힘 · 도면 열기 안 감) ·
|
|
* ⚙ 모달(평면도 = 평면도 관리 · 횡단 = 배치 칸 + 안내선 · 구조물 = 축척 없음 줄 · [원본에서 다시 그리기]) ·
|
|
* 저장 묶음(`_UI_Save` — 고친 탭만 · 실패 = 거짓 · 원본 되돌림)을 Node 로 돌려 표준출력 JSON 한 벌로.
|
|
* TS 는 그 자리에서 트랜스파일 · 화면 그릇은 쓰는 만큼만 흉내 · 서버 길 · 모달 · 단추는 기록용 가짜.
|
|
*/
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1)
|
|
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
const B07 = path.join(ROOT, "B07_DesignDetail");
|
|
const UI = path.join(ROOT, "ui_template");
|
|
const cache = new Map();
|
|
|
|
class FakeNode {
|
|
constructor(tag) {
|
|
this.tagName = tag.toUpperCase();
|
|
this.children = [];
|
|
this.parent = null;
|
|
this.dataset = {};
|
|
this.attrs = {};
|
|
this.listeners = {};
|
|
this._text = "";
|
|
this._classes = new Set();
|
|
this.value = "";
|
|
this.disabled = false;
|
|
const self = this;
|
|
this.classList = {
|
|
add: (...names) => names.forEach((n) => self._classes.add(n)),
|
|
remove: (...names) => names.forEach((n) => self._classes.delete(n)),
|
|
toggle: (name) =>
|
|
self._classes.has(name) ? self._classes.delete(name) : self._classes.add(name),
|
|
contains: (name) => self._classes.has(name),
|
|
};
|
|
}
|
|
set className(value) {
|
|
this._classes = new Set(String(value).split(/\s+/).filter(Boolean));
|
|
}
|
|
get className() {
|
|
return [...this._classes].join(" ");
|
|
}
|
|
set textContent(value) {
|
|
this._text = String(value);
|
|
this.children = [];
|
|
}
|
|
get textContent() {
|
|
return this._text + this.children.map((c) => c.textContent).join("");
|
|
}
|
|
setAttribute(key, value) {
|
|
this.attrs[key] = String(value);
|
|
}
|
|
append(...kids) {
|
|
kids.forEach((k) => {
|
|
if (k.parent) k.parent.children = k.parent.children.filter((c) => c !== k);
|
|
k.parent = this;
|
|
this.children.push(k);
|
|
});
|
|
}
|
|
prepend(...kids) {
|
|
kids.forEach((k) => (k.parent = this));
|
|
this.children.unshift(...kids);
|
|
}
|
|
replaceWith(node) {
|
|
const at = this.parent.children.indexOf(this);
|
|
node.parent = this.parent;
|
|
this.parent.children[at] = node;
|
|
}
|
|
addEventListener(type, handler) {
|
|
(this.listeners[type] ||= []).push(handler);
|
|
}
|
|
/** 누름 — 올라가기(bubble)까지 · stopPropagation 이면 멈춤 */
|
|
click() {
|
|
if (this.disabled) return;
|
|
let stopped = false;
|
|
const event = { target: this, stopPropagation: () => (stopped = true), preventDefault() {} };
|
|
for (let node = this; node && !stopped; node = node.parent)
|
|
(node.listeners.click || []).forEach((handler) => handler(event));
|
|
}
|
|
closest(selector) {
|
|
const name = selector.slice(1);
|
|
for (let node = this; node; node = node.parent) if (node.classList.contains(name)) return node;
|
|
return null;
|
|
}
|
|
}
|
|
const all = (node) => [node, ...node.children.flatMap(all)];
|
|
const byClass = (node, name) => all(node).filter((n) => n.classList.contains(name));
|
|
|
|
globalThis.document = {
|
|
createElement: (tag) => new FakeNode(tag),
|
|
querySelectorAll: () => [],
|
|
};
|
|
|
|
const toasts = [];
|
|
const modals = [];
|
|
const plans = [];
|
|
const calls = [];
|
|
let failConfirm = false;
|
|
const SCALES = {
|
|
scales: {
|
|
longitudinal: { x: 1000, y: 200 },
|
|
cross: { x: 100, y: 100 },
|
|
mass_haul: { x: null, y: 50000 },
|
|
development: { x: null, y: null },
|
|
},
|
|
defaults: {
|
|
longitudinal: { x: 1000, y: 200 },
|
|
cross: { x: 100, y: 100 },
|
|
mass_haul: { x: null, y: 50000 },
|
|
development: { x: null, y: null },
|
|
},
|
|
ranges: {
|
|
longitudinal: { x: [100, 10000], y: [10, 2000] },
|
|
cross: { x: [10, 1000], y: [10, 1000] },
|
|
mass_haul: { x: [100, 20000], y: [1000, 1000000] },
|
|
development: { x: [10, 10000], y: [10, 10000] },
|
|
},
|
|
saved: { longitudinal: 2, cross: 0, mass_haul: 0, development: 0 },
|
|
};
|
|
let answer = true;
|
|
|
|
const button = (opts) => {
|
|
const node = new FakeNode("button");
|
|
node.className = "ui-btn";
|
|
node.textContent = opts.label;
|
|
node.disabled = !!opts.disabled;
|
|
if (opts.onClick) node.addEventListener("click", opts.onClick);
|
|
return node;
|
|
};
|
|
const STUBS = {
|
|
"@ui/ui_template_elements": {
|
|
createButton: button,
|
|
createInputField: (opts) => {
|
|
const root = new FakeNode("label");
|
|
const input = new FakeNode("input");
|
|
input.value = opts.value ?? "";
|
|
input.setAttribute("type", opts.type);
|
|
const label = new FakeNode("span");
|
|
label.className = "ui-field__label";
|
|
label.textContent = opts.label;
|
|
root.append(label, input);
|
|
const setError = (msg) => (input.error = msg ?? null);
|
|
return { root, input, setError };
|
|
},
|
|
showToast: (message, kind) => toasts.push([kind, message]),
|
|
showLoadingOverlay: () => calls.push(["overlay+"]),
|
|
hideLoadingOverlay: () => calls.push(["overlay-"]),
|
|
showConfirmDialog: async (message) => (calls.push(["ask", message]), answer),
|
|
},
|
|
"@ui/ui_template_checkbox": {
|
|
createCheckbox: (text) => {
|
|
const root = new FakeNode("label");
|
|
root.className = "ui-checkbox";
|
|
const input = new FakeNode("input");
|
|
input.checked = false;
|
|
root.append(input);
|
|
return { root, input };
|
|
},
|
|
},
|
|
"@ui/ui_template_modal": {
|
|
openModal: (opts) => {
|
|
const body = new FakeNode("div");
|
|
const modal = { opts, body, open: true };
|
|
modals.push(modal);
|
|
opts.mount(body, () => (modal.open = false), new FakeNode("div"));
|
|
return { close: () => (modal.open = false) };
|
|
},
|
|
},
|
|
"@ui/ui_template_locale": {
|
|
currentLanguageIndex: 0,
|
|
ui_locales: new Proxy({}, { get: (_, key) => [String(key)] }),
|
|
},
|
|
"./B07_DesignDetail_UI_PlanManage": {
|
|
openPlanManager: (controls) => {
|
|
const plan = { controls, open: true };
|
|
plans.push(plan);
|
|
return { close: () => (plan.open = false) };
|
|
},
|
|
},
|
|
"./B07_DesignDetail_Api_Fetch": {
|
|
confirmDesignDrawing: async (projectId, id, drawing, table) => {
|
|
calls.push(["confirm", id, drawing.tag, table]);
|
|
if (failConfirm) throw new Error("서버 오류");
|
|
return { all_confirmed: true };
|
|
},
|
|
invalidateDesignDrawing: async (projectId, id) => calls.push(["invalidate", id]),
|
|
// 485-2 축척 — 범위 · 기본은 서버(`_Router_Support_Scale.py`)와 같은 값
|
|
fetchDrawingScales: async () => JSON.parse(JSON.stringify(SCALES)),
|
|
saveDrawingScales: async (projectId, scales) => calls.push(["scales", scales]),
|
|
},
|
|
};
|
|
|
|
function loadTs(file) {
|
|
const full = path.resolve(file);
|
|
if (cache.has(full)) return cache.get(full);
|
|
const js = ts.transpileModule(fs.readFileSync(full, "utf8"), {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
cache.set(full, box.exports);
|
|
const localRequire = (request) => {
|
|
if (CATALOG.isCatalogRequest(request)) return CATALOG;
|
|
if (STUBS[request]) return STUBS[request];
|
|
if (request === "@ui/ui_template_collapsible")
|
|
return loadTs(path.join(UI, "ui_template_collapsible.ts"));
|
|
if (request.startsWith(".")) {
|
|
const target = path.join(path.dirname(full), `${request}.ts`);
|
|
return fs.existsSync(target) ? loadTs(target) : {};
|
|
}
|
|
return {};
|
|
};
|
|
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
|
cache.set(full, box.exports);
|
|
return box.exports;
|
|
}
|
|
|
|
const { buildDrawingSidePanel } = loadTs(path.join(B07, "B07_DesignDetail_UI_Panels.ts"));
|
|
const { attachCollapsible } = loadTs(path.join(UI, "ui_template_collapsible.ts"));
|
|
const { openDrawingSettings } = loadTs(path.join(B07, "B07_DesignDetail_UI_Settings.ts"));
|
|
const Save = loadTs(path.join(B07, "B07_DesignDetail_UI_Save.ts"));
|
|
|
|
const item = (id, kind, extra = {}) => ({ id, kind, label: id, confirmed: false, ...extra });
|
|
const drawings = [
|
|
item("cover", "cover"),
|
|
item("plan_k", "plan", { plan_kind: "k", plan_kind_name: "지형" }),
|
|
item("longitudinal", "longitudinal"),
|
|
item("cross_standard", "cross_standard"),
|
|
item("cross_s1", "cross"),
|
|
item("cross_s2", "cross"),
|
|
item("mass_haul", "mass_haul"),
|
|
item("structure_k", "structure"),
|
|
item("dev_k", "development", { plan_kind: "dk", plan_kind_name: "옹벽" }),
|
|
];
|
|
|
|
(async () => {
|
|
const out = {};
|
|
// ── 좌측 ⚙ 자리 ───────────────────────────────────────────────────────────
|
|
const settings = [];
|
|
const selected = [];
|
|
const panel = buildDrawingSidePanel(
|
|
drawings,
|
|
(d) => selected.push(d.id),
|
|
undefined,
|
|
false,
|
|
(kind) => settings.push(kind),
|
|
);
|
|
const cards = byClass(panel, "b07-drawing-group");
|
|
out.cardGears = cards
|
|
.filter((c) => byClass(c, "ui-collapsible__settings").length)
|
|
.map((c) => c.dataset.kind);
|
|
out.cardGearInTitle = cards.every((c) =>
|
|
byClass(c, "ui-collapsible__settings").every((g) =>
|
|
g.parent.classList.contains("ui-collapsible__title"),
|
|
),
|
|
);
|
|
const buttonGears = byClass(panel, "b07-drawing-button__settings");
|
|
out.buttonGears = buttonGears.map((g) => g.parent.dataset.drawingId);
|
|
out.buttonGearRole = buttonGears.map((g) => [g.tagName, g.attrs.role, g.tabIndex]);
|
|
out.coverGear = byClass(panel, "b07-drawing-button")
|
|
.filter((b) => b.dataset.drawingId === "cover")
|
|
.flatMap((b) => byClass(b, "b07-drawing-button__settings")).length;
|
|
|
|
// 누름 — 카드 ⚙ = 접힘 그대로 · 단추 ⚙ = 도면 안 엶 · 제목 누름은 여전히 접음
|
|
const before = cards.map((c) => c.classList.contains("is-collapsed"));
|
|
cards.forEach((c) => byClass(c, "ui-collapsible__settings")[0]?.click());
|
|
out.collapsedSame = cards.every((c, i) => c.classList.contains("is-collapsed") === before[i]);
|
|
buttonGears.forEach((g) => g.click());
|
|
out.settingsCalls = [...settings];
|
|
out.selectedAfterGear = [...selected];
|
|
const crossCard = cards.find((c) => c.dataset.kind === "cross");
|
|
const crossTitle = byClass(crossCard, "ui-collapsible__title")[0];
|
|
const wasCollapsed = crossCard.classList.contains("is-collapsed");
|
|
crossTitle.click();
|
|
out.titleClickToggles = crossCard.classList.contains("is-collapsed") !== wasCollapsed;
|
|
byClass(panel, "b07-drawing-button")
|
|
.find((b) => b.dataset.drawingId === "mass_haul")
|
|
.click();
|
|
out.buttonClickOpens = selected.at(-1) === "mass_haul";
|
|
// Enter 도 도면 안 엶
|
|
const enterBefore = selected.length;
|
|
const gear = buttonGears[0];
|
|
let stopped = false;
|
|
gear.listeners.keydown.forEach((h) =>
|
|
h({ key: "Enter", preventDefault() {}, stopPropagation: () => (stopped = true) }),
|
|
);
|
|
out.enterStops = stopped && selected.length === enterBefore;
|
|
|
|
// ── ⚙ 모달 ──────────────────────────────────────────────────────────────
|
|
let current = drawings[4]; // cross_s1
|
|
let redraws = 0;
|
|
const saved = [];
|
|
const guideToggle = button({ label: "안내선 숨기기" });
|
|
const deps = {
|
|
drawings,
|
|
current: () => current,
|
|
canRedraw: () => true,
|
|
redraw: async () => (redraws++, true),
|
|
plan: { projectId: "p1", onClosed: () => {} },
|
|
cross: {
|
|
layout: async () => ({ default: [4, 4], sheets: { 1: [3, 2] }, table_scale: 1.2 }),
|
|
save: (next, sheet) => saved.push([next, sheet]),
|
|
guideToggle,
|
|
},
|
|
};
|
|
const texts = (node) =>
|
|
all(node)
|
|
.map((n) => n._text)
|
|
.filter(Boolean);
|
|
openDrawingSettings("cross", deps);
|
|
await new Promise((r) => setTimeout(r, 0));
|
|
const cross = modals.at(-1);
|
|
const layoutNode = byClass(cross.body, "b07-cross-layout")[0];
|
|
const crossInputs = all(layoutNode).filter((n) => n.tagName === "INPUT");
|
|
out.crossModal = {
|
|
title: cross.opts.title,
|
|
labels: byClass(layoutNode, "ui-field__label").map((n) => n.textContent),
|
|
values: crossInputs.map((n) => n.value),
|
|
scaleTitle: texts(byClass(cross.body, "b07-scale")[0])[0],
|
|
buttons: all(layoutNode)
|
|
.filter((n) => n.tagName === "BUTTON")
|
|
.map((n) => [n.textContent, n.disabled]),
|
|
guideLast: byClass(cross.body, "b07-cross-layout")[0].children.at(-1) === guideToggle,
|
|
head: cross.opts.actions.map((n) => [n.textContent, n.disabled]),
|
|
notes: texts(cross.body).filter((t) => t.startsWith("지금 도면")),
|
|
};
|
|
crossInputs[0].value = "2";
|
|
all(layoutNode)
|
|
.find((n) => n.textContent === "이 장")
|
|
.click();
|
|
out.crossSave = { saved, closed: !cross.open };
|
|
|
|
openDrawingSettings("structure", deps);
|
|
const structure = modals.at(-1);
|
|
out.structureModal = {
|
|
title: structure.opts.title,
|
|
notes: texts(structure.body),
|
|
head: structure.opts.actions.map((n) => [n.textContent, n.disabled, n.title]),
|
|
};
|
|
|
|
openDrawingSettings("longitudinal", deps);
|
|
out.longModal = { title: modals.at(-1).opts.title, bodyKids: modals.at(-1).body.children.length };
|
|
|
|
current = drawings[1]; // plan_k
|
|
openDrawingSettings("plan", deps);
|
|
const plan = plans.at(-1);
|
|
out.planModal = {
|
|
viaManager: plans.length === 1 && modals.length === 3,
|
|
head: plan.controls.actions.map((n) => [n.textContent, n.disabled]),
|
|
};
|
|
plan.controls.actions[0].click();
|
|
await new Promise((r) => setTimeout(r, 0));
|
|
out.planRedraw = { redraws, closed: !plan.open };
|
|
|
|
current = drawings[3]; // 표준 횡단면도도 횡단 묶음
|
|
openDrawingSettings("cross", deps);
|
|
out.standardInCross = !modals.at(-1).opts.actions[0].disabled;
|
|
|
|
// ── 축척 칸(485-2) ─────────────────────────────────────────────────────
|
|
const closedCalls = [];
|
|
const scaleDeps = { ...deps, plan: { projectId: "p1", onClosed: (...a) => closedCalls.push(a) } };
|
|
const tick = () => new Promise((r) => setTimeout(r, 0));
|
|
const scaleOf = async (kind) => {
|
|
openDrawingSettings(kind, scaleDeps);
|
|
await tick();
|
|
const modal = modals.at(-1);
|
|
const section = byClass(modal.body, "b07-scale")[0];
|
|
const inputs = all(section).filter(
|
|
(n) => n.tagName === "INPUT" && !n.parent.classList.contains("ui-checkbox"),
|
|
);
|
|
const autos = byClass(section, "ui-checkbox").map((n) => n.children[0]);
|
|
const btn = (label) =>
|
|
all(section).find((n) => n.tagName === "BUTTON" && n.textContent === label);
|
|
const type = (input, value) => {
|
|
input.value = value;
|
|
input.listeners.input.forEach((h) => h());
|
|
};
|
|
const toggle = (box) => {
|
|
box.checked = !box.checked;
|
|
box.listeners.change.forEach((h) => h());
|
|
};
|
|
const state = () => ({
|
|
values: inputs.map((n) => n.value),
|
|
errors: inputs.map((n) => n.error ?? null),
|
|
disabled: inputs.map((n) => !!n.disabled),
|
|
autos: autos.map((n) => n.checked),
|
|
save: !btn("축척 저장").disabled,
|
|
});
|
|
return { modal, section, inputs, autos, btn, type, toggle, state };
|
|
};
|
|
const scale = {};
|
|
current = drawings[2]; // longitudinal
|
|
let s = await scaleOf("longitudinal");
|
|
scale.longLabels = byClass(s.section, "ui-field__label").map((n) => n.textContent);
|
|
scale.longStart = s.state();
|
|
s.type(s.inputs[0], "50");
|
|
scale.longLow = s.state();
|
|
s.type(s.inputs[0], "500.5");
|
|
scale.longFrac = s.state();
|
|
s.type(s.inputs[0], "500");
|
|
scale.longOk = s.state();
|
|
calls.length = 0;
|
|
answer = false;
|
|
s.btn("축척 저장").click();
|
|
await tick();
|
|
scale.cancel = { calls: calls.map((c) => c[0]), open: s.modal.open, closed: closedCalls.length };
|
|
answer = true;
|
|
calls.length = 0;
|
|
s.btn("축척 저장").click();
|
|
await tick();
|
|
await tick();
|
|
scale.save = {
|
|
ask: calls.find((c) => c[0] === "ask")?.[1],
|
|
sent: calls.find((c) => c[0] === "scales")?.[1],
|
|
open: s.modal.open,
|
|
closed: [...closedCalls],
|
|
};
|
|
s = await scaleOf("longitudinal");
|
|
s.type(s.inputs[1], "300");
|
|
s.btn("기본값").click();
|
|
scale.reset = s.state();
|
|
calls.length = 0;
|
|
const toastAt = toasts.length;
|
|
s.btn("축척 저장").click();
|
|
await tick();
|
|
scale.same = { calls: calls.length, toasts: toasts.slice(toastAt).map((t) => t[1]) };
|
|
|
|
s = await scaleOf("mass_haul");
|
|
scale.massStart = s.state();
|
|
s.toggle(s.autos[0]);
|
|
scale.massManual = s.state();
|
|
s.type(s.inputs[0], "2000");
|
|
scale.massTyped = s.state();
|
|
calls.length = 0;
|
|
s.btn("축척 저장").click();
|
|
await tick();
|
|
await tick();
|
|
scale.massSent = {
|
|
ask: calls.filter((c) => c[0] === "ask").length,
|
|
sent: calls.find((c) => c[0] === "scales")?.[1],
|
|
};
|
|
s = await scaleOf("mass_haul");
|
|
s.toggle(s.autos[0]);
|
|
s.toggle(s.autos[0]);
|
|
scale.massBack = s.state();
|
|
|
|
s = await scaleOf("development");
|
|
scale.dev = s.state();
|
|
openDrawingSettings("structure", scaleDeps);
|
|
scale.structure = byClass(modals.at(-1).body, "b07-scale").length;
|
|
out.scale = scale;
|
|
const Check = loadTs(path.join(B07, "logic", "B07_DesignDetail_Scale_Check.ts"));
|
|
out.readAxis = [
|
|
["1,000", false],
|
|
[" 99 ", false],
|
|
["10001", false],
|
|
["12.5", false],
|
|
["", false],
|
|
["abc", false],
|
|
["", true],
|
|
].map(([raw, auto]) => Check.readScaleAxis(raw, auto, [100, 10000]));
|
|
out.toSend = [
|
|
Check.scaleToSend(200, 200),
|
|
Check.scaleToSend(500, 200),
|
|
Check.scaleToSend(null, null),
|
|
];
|
|
|
|
// ── 저장 묶음 ────────────────────────────────────────────────────────────
|
|
const tabsOf = (list, activeId) => ({
|
|
tabs: () => list,
|
|
active: () => list.find((t) => t.id === activeId),
|
|
markClean: (tab) => {
|
|
tab.dirty = false;
|
|
delete tab.drawing;
|
|
},
|
|
});
|
|
const storeOf = (over = {}) => {
|
|
let dirty = over.cadDirty ?? false;
|
|
const store = {
|
|
projectId: "p1",
|
|
drawings,
|
|
tabs: over.tabs,
|
|
current: () => over.current,
|
|
cadDirty: () => dirty,
|
|
setCadClean: () => (dirty = false),
|
|
requestCad: async () => ({ drawing: { tag: "live" }, quantityTable: { q: 1 } }),
|
|
isFrameEditing: () => !!over.frame,
|
|
forget: (id) => calls.push(["forget", id]),
|
|
reload: async (d) => calls.push(["reload", d.id]),
|
|
};
|
|
return store;
|
|
};
|
|
const reset = () => {
|
|
calls.length = 0;
|
|
drawings.forEach((d) => (d.confirmed = false));
|
|
};
|
|
|
|
// 고친 것 없음 = 요청 0 · 참
|
|
reset();
|
|
let ok = await Save.saveEditedDrawings(
|
|
storeOf({ current: drawings[4], tabs: tabsOf([{ id: "cross_s1" }], "cross_s1") }),
|
|
);
|
|
out.noEdits = { ok, calls: [...calls] };
|
|
|
|
// 지금 탭(CAD 편집) + 다른 고친 탭 = confirm 둘 · 안 고친 탭은 안 보냄
|
|
reset();
|
|
const list = [
|
|
{ id: "cross_s1", dirty: true },
|
|
{ id: "longitudinal", dirty: true, drawing: { tag: "stash" } },
|
|
{ id: "mass_haul" },
|
|
];
|
|
ok = await Save.saveEditedDrawings(
|
|
storeOf({ current: drawings[4], cadDirty: true, tabs: tabsOf(list, "cross_s1") }),
|
|
);
|
|
out.saveEdited = {
|
|
ok,
|
|
confirms: calls.filter((c) => c[0] === "confirm"),
|
|
clean: list.map((t) => !!t.dirty),
|
|
confirmed: drawings.filter((d) => d.confirmed).map((d) => d.id),
|
|
};
|
|
|
|
// 실패 = 거짓 · 알림
|
|
reset();
|
|
failConfirm = true;
|
|
const toastStart = toasts.length;
|
|
ok = await Save.saveEditedDrawings(
|
|
storeOf({
|
|
current: drawings[4],
|
|
cadDirty: true,
|
|
tabs: tabsOf([{ id: "cross_s1", dirty: true }], "cross_s1"),
|
|
}),
|
|
);
|
|
failConfirm = false;
|
|
out.saveFail = { ok, toasts: toasts.slice(toastStart).map((t) => t[0]) };
|
|
|
|
// 도각 편집 중 = 거짓 · 요청 0
|
|
reset();
|
|
ok = await Save.saveEditedDrawings(
|
|
storeOf({ current: drawings[4], cadDirty: true, frame: true, tabs: tabsOf([], "") }),
|
|
);
|
|
out.frameEditing = { ok, calls: calls.filter((c) => c[0] === "confirm").length };
|
|
|
|
// 원본에서 다시 그리기 — 저장본 = invalidate + 다시 실음 · 원본 그대로 = 꺼짐 · 물음 [취소] = 그대로
|
|
reset();
|
|
drawings[6].confirmed = true;
|
|
const mh = storeOf({ current: drawings[6], tabs: tabsOf([{ id: "mass_haul" }], "mass_haul") });
|
|
out.redrawable = Save.hasRedrawable(mh);
|
|
ok = await Save.redrawFromSource(mh);
|
|
out.redraw = {
|
|
ok,
|
|
calls: calls.filter((c) => c[0] !== "overlay+" && c[0] !== "overlay-").map((c) => c[0]),
|
|
confirmed: drawings[6].confirmed,
|
|
};
|
|
out.pristine = Save.hasRedrawable(mh);
|
|
answer = false;
|
|
reset();
|
|
drawings[6].confirmed = true;
|
|
ok = await Save.redrawFromSource(mh);
|
|
answer = true;
|
|
out.redrawCancel = { ok, invalidates: calls.filter((c) => c[0] === "invalidate").length };
|
|
|
|
process.stdout.write(JSON.stringify(out));
|
|
})().catch((error) => {
|
|
process.stderr.write(String(error && error.stack ? error.stack : error));
|
|
process.exit(1);
|
|
});
|