Merge remote-tracking branch 'origin/sub_laptop_3' into main_laptop_1

This commit is contained in:
2026-10-05 20:02:22 +09:00
4 changed files with 305 additions and 70 deletions
@@ -0,0 +1,90 @@
/* 51-9 — [로직 추가] 고르기 창이 공용 찾기 양식(ui_template_find_list)을 씀
* 시작 화면: M02 마스터 템플릿 · 마스터 층(#/m02-master-template?open=구조물조합&root=master) · 시스템 관리자 · item1 있음
* 부르는 법: orca eval --expression "$(tr '
' ' ' < 이 파일)" (async 식 · JSON 글을 돌려줌)
* 하는 일: item1 → 일위대가 [수정] → [로직 추가] → 칸 배치 · 줄 맞춤 · ↓ Enter 점검 → 창 닫음(저장 안 함)
* 기대 수치:
* - template = true · filters = 2 · sameLine = true(구분 · 상세구분 top 같음 · left 다름)
* - nameAlign = "left" · nameLeftGap ≤ 꼬리표 폭 + 16 · rowHeight ≤ 40 · oneLine = true
* - active0 = 0 · activeAfterDown = 1 · enterPicked = true(Enter 로 창이 닫힘) · undone = true
*/
(async () => {
const w = (ms) => new Promise((r) => setTimeout(r, ms));
const until = async (fn, ms = 8000) => {
for (let t = 0; t < ms; t += 150) {
const v = fn();
if (v) return v;
await w(150);
}
return null;
};
const out = {};
const node = await until(() => document.querySelector('[data-node="i|item1"]'));
if (!node) return JSON.stringify({ error: "item1 없음" });
node.click();
const card = await until(() =>
[...document.querySelectorAll(".m02-struct__card")].find((c) =>
c.querySelector("table.m02-labrow tbody tr[data-labor-logic]"),
),
);
if (!card) return JSON.stringify({ error: "일위대가 카드 없음" });
[...card.querySelectorAll(".m02-struct__card-tools button")]
.find((b) => b.innerText.trim() === "수정")
?.click();
const table = () =>
[...document.querySelectorAll("table.m02-labrow")].find(
(t) => !t.closest(".m02-struct__card") && t.querySelector("tr[data-labor-logic]"),
);
await until(() => table() && !/…/.test(table().querySelector("tbody").innerText), 12000);
const host = table().closest("[data-labor]");
const before = table().querySelectorAll("tbody tr[data-labor-logic]").length;
const openPick = async () => {
[...host.querySelectorAll(".m01-logic__section-head button")]
.find((b) => b.innerText.trim() === "로직 추가")
.click();
const dlg = await until(() => document.querySelector(".m01-logic__pick"));
await until(() => dlg.querySelectorAll(".ui-find__row").length > 1, 20000);
return dlg;
};
const dlg = await openPick();
out.template = !!dlg.querySelector(".ui-find .ui-find__filters") && !!dlg.querySelector(".ui-find__list");
const sels = [...dlg.querySelectorAll(".ui-find__filters > *")];
out.filters = sels.length;
const [a, b] = sels.map((x) => x.getBoundingClientRect());
out.sameLine = Math.abs(a.top - b.top) < 2 && b.left > a.right - 1;
const row = dlg.querySelector(".ui-find__row");
const name = row.querySelector(".ui-find__name");
const tag = row.querySelector(".ui-find__tag");
out.nameAlign = getComputedStyle(name).textAlign;
out.rowAlign = getComputedStyle(row).textAlign;
out.nameLeftGap = Math.round(name.getBoundingClientRect().left - row.getBoundingClientRect().left);
out.tagWidth = Math.round(tag.getBoundingClientRect().width);
out.rowHeight = Math.round(row.getBoundingClientRect().height);
out.oneLine = getComputedStyle(row).whiteSpace === "nowrap";
const rows = () => [...dlg.querySelectorAll(".ui-find__row")];
const active = () => rows().findIndex((r) => r.classList.contains("is-active"));
out.active0 = active();
const search = dlg.querySelector('input[type="search"]');
search.dispatchEvent(new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true }));
out.activeAfterDown = active();
out.rows = rows().length;
/* Enter = 고름 — 창이 닫히고 표에 붙은 줄은 바로 지움 */
const pickedKey = rows()[active()].dataset.pick;
search.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
out.enterPicked = !!(await until(() => !document.querySelector(".m01-logic__pick") || null));
out.pickedKey = pickedKey;
await until(() => table().querySelectorAll("tbody tr[data-labor-logic]").length > before, 8000);
const added = [...table().querySelectorAll("tbody tr[data-labor-logic]")].slice(before);
for (const tr of added) {
[...tr.querySelectorAll("button")].find((x) => x.innerText.trim() === "삭제")?.click();
await w(600);
}
out.undone = table().querySelectorAll("tbody tr[data-labor-logic]").length === before;
document.querySelector('[data-node="i|item1"]')?.click();
await w(800);
const ask = [...document.querySelectorAll(".ui-modal button, dialog button")].find((x) =>
/확인|버림|떠나기/.test(x.innerText),
);
ask?.click();
return JSON.stringify(out);
})();