Files
Aislo/resources/tester/helper_282_1_profile_toolbar.cjs
T
eomsangdonandClaude Opus 5.5 2fa68b95d8 feat(b05,b06): 종단 패널 도구줄 — [배수유역도] 켬끔 단추 지움 · [새 창] = [초기화] 왼쪽 (282)
- 배수유역 판 켬끔(setVisible · drainage-visible 값) 지움 — 판은 늘 보임 · 접기 · 펼치기는 판 손잡이 하나로 · 옛 값 false 무시
- [새 창] 단추 = 도구 묶음 맨 앞(toolLead) — 다시 그릴 때마다 [초기화] 바로 왼쪽에 옮겨 붙임 · 열림 · 닫힘 동작 그대로
- 시험 test_282_1 추가 · 옛 단추를 찾던 시험(187-1 · drainage_side · profile_single) 새 꼴로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QfHKQs7J3hQELKMWQMUJEy
2026-10-12 19:19:05 +09:00

186 lines
6.3 KiB
JavaScript

/* 282 종단 도구줄 — 진짜 TS(도구 묶음 render · withToolLead · attachProfilePopout · 배수유역 판)를
* 가짜 DOM 위에서 돌려 JSON 으로 냄.
* · [새 창] = 도구 묶음 맨 앞([초기화] 바로 왼쪽) · 다시 그려도 그 자리
* · 새 창 열림 · 닫힘 = 글 바뀜 · 제자리 돌림
* · 옛 값 drainage-visible=false 여도 판(손잡이) 보임 */
const fs = require("fs");
const path = require("path");
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
const ROOT = path.join(__dirname, "..", "..");
const read = (...p) => fs.readFileSync(path.join(ROOT, ...p), "utf8");
const load = (code, scope) => {
const js = ts.transpileModule(code, {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
const exports = {};
const names = Object.keys(scope);
new Function("exports", ...names, js)(exports, ...names.map((n) => scope[n]));
return exports;
};
// ---- 가짜 DOM ----
function el(tag) {
const node = {
tagName: tag.toUpperCase(),
className: "",
textContent: "",
title: "",
hidden: false,
disabled: false,
attrs: {},
listeners: {},
children: [],
parent: null,
dataset: {},
style: {},
classList: {
list: () => node.className.split(/\s+/).filter(Boolean),
contains: (c) => node.classList.list().includes(c),
add: (c) =>
!node.classList.contains(c) && (node.className = [...node.classList.list(), c].join(" ")),
remove: (c) =>
(node.className = node.classList
.list()
.filter((x) => x !== c)
.join(" ")),
toggle: (c, on) =>
(on ?? !node.classList.contains(c)) ? node.classList.add(c) : node.classList.remove(c),
},
detach() {
if (node.parent) node.parent.children = node.parent.children.filter((c) => c !== node);
node.parent = null;
},
append(...kids) {
kids.forEach((k) => (k.detach(), (k.parent = node), node.children.push(k)));
},
prepend(...kids) {
kids.reverse().forEach((k) => (k.detach(), (k.parent = node), node.children.unshift(k)));
},
replaceChildren(...kids) {
node.children.slice().forEach((c) => c.detach());
node.append(...kids);
},
setAttribute: (k, v) => (node.attrs[k] = v),
addEventListener: (type, fn) => (node.listeners[type] ??= []).push(fn),
click: () => (node.listeners.click ?? []).forEach((fn) => fn({ stopPropagation() {} })),
};
return node;
}
const fakeDoc = () => {
const doc = { title: "" };
doc.createElement = el;
doc.head = el("head");
doc.body = el("body");
doc.documentElement = Object.assign(el("html"), { attributes: [] });
doc.querySelectorAll = () => [];
doc.importNode = (n) => n;
return doc;
};
const document = fakeDoc();
const frames = [];
const window = {
listeners: {},
addEventListener: (t, fn) => (window.listeners[t] ??= []).push(fn),
removeEventListener: () => {},
open: () => popup,
};
const popupListeners = {};
const popup = {
closed: false,
document: fakeDoc(),
close: () => (popup.closed = true),
addEventListener: (t, fn) => (popupListeners[t] ??= []).push(fn),
ResizeObserver: class {
observe() {}
},
requestAnimationFrame: () => 0,
cancelAnimationFrame: () => {},
};
// ---- 진짜 도구 묶음 ----
const { createProfileTools } = load(read("B05_Profile", "B05_Profile_UI_Profile_Tools.ts"), {
require: () => ({ sameRun: () => false }),
document,
});
const tools = createProfileTools({
onStraighten() {},
onShift() {},
onTilt() {},
onResetToSaved() {},
canResetToSaved: () => true,
onUndo() {},
onRedo() {},
canUndo: () => false,
canRedo: () => false,
hasStraightRuns: () => false,
onChanged() {},
onModeChange() {},
freeHint: () => "",
mountOverlay() {},
});
// ---- 진짜 withToolLead(design_profile.ts) ----
const profileSrc = read("A00_Common", "design", "design_profile.ts");
const leadAt = profileSrc.indexOf("const withToolLead");
const leadSrc = profileSrc.slice(leadAt, profileSrc.indexOf("};", leadAt) + 2);
const button = el("button");
const { withToolLead } = load(`${leadSrc}\nexports.withToolLead = withToolLead;`, {
ctx: { toolLead: button },
});
// ---- 진짜 attachProfilePopout ----
const { attachProfilePopout } = load(read("A00_Common", "design", "design_profile_popout.ts"), {
require: () => ({ showToast() {} }),
document,
window,
getComputedStyle: () => ({ backgroundColor: "" }),
requestAnimationFrame: (fn) => frames.push(fn),
});
const slot = el("div");
const root = el("div");
slot.append(root);
const balanceBar = el("div");
root.append(balanceBar);
const profile = { root, redraw() {}, dispose() {} };
attachProfilePopout(profile, slot, button);
const render = () => {
const wrap = withToolLead(tools.render());
balanceBar.replaceChildren(wrap);
return wrap.children.map((c) => c.textContent);
};
const out = { first: render(), buttonClass: button.className };
out.again = render();
button.click();
out.opened = { text: button.textContent, inPopup: popup.document.body.children[0] === root };
out.openedRender = render();
button.click();
out.closed = { text: button.textContent, inSlot: root.parent === slot, popupClosed: popup.closed };
out.closedRender = render();
// ---- 옛 값 drainage-visible=false — 판 · 손잡이 보임 ----
const store = { "drainage-visible": "false", "drainage-collapsed": "false" };
const sideSrc = read("B05_Profile", "B05_Profile_UI_Drainage_Side.ts");
const panelRoot = Object.assign(el("aside"), { className: "b05-drainage" });
const handle = el("button");
panelRoot.append(handle);
const fakePanel = { root: panelRoot, setView() {}, dispose() {} };
const side = load(sideSrc, {
require: (id) => (id.endsWith(".css") ? {} : { createDrainagePanel: () => fakePanel }),
});
const sideToolSrc = read("B05_Profile", "B05_Profile_UI_Profile_SideTool.ts");
const { drainageSide } = load(sideToolSrc, {
require: (id) =>
id.includes("b_page_state")
? { readStateRaw: (k) => store[k] ?? null, writeStateRaw: (k, v) => (store[k] = v) }
: side,
});
const board = drainageSide({ projectId: "p", view: "profile" });
out.side = {
rootHidden: board.root.hidden,
handleHidden: handle.hidden,
hasSetVisible: "setVisible" in board,
};
process.stdout.write(JSON.stringify(out));