Files
Aislo/resources/tester/helper_219_1_arrow_owner.cjs
T
eomsangdonandClaude Opus 5.5 bbd6254892 feat(구조물표): 322-1 부품 상자 · 표 읽기 — 목록 · 측점 · 카드 이름 · 형상 고르기 · 3D 몸체 · 알약 띠가 종류 키 · 군 글자 · 자리꼴 · managed_by 대신 구조물 표(catalog) 한 줄을 읽음 · 코드 숨김 목록 걷음 · ★ 넷 + 관 공식 한 벌
- 부품 상자 common_util_structure_parts.json(갈래마다 부품 · 읽는 칸 · 자리꼴 · 정본 · 42종 처음값) · 표 읽기 common_util_structure_catalog.ts / .py 짝(정의 반영 ⊕ 처음값 ⊕ 부품 끔) · 거울 시험 test_322_1_catalog_mirror
- 목록: pickGroups 군 차례 = 구조물군 열쇠 · 페이지 거르기 listPick(B05 both · B06 both + b06) · HIDDEN_PICK_GROUPS · HIDDEN_PICK_TYPES · pickable 걷음(옛 대피 셋 asRoad 그대로)
- 측점: structureStationMarks · pipeStationMarks · is_station_planting_type · resolve_extra_stations = 표 횡단도(anchor · span3 · none)
- 카드 이름: cardTitle / card_title 한 공식(빈칸 = 그 종류 정의 기본값) · 표시 이름이 정의 관종도 앎
- 형상: 벽 = shape wall(형태 = 형상칸.form · 물빼기 = 형상칸.weep) · 포장 = shape pavement(「사용 중」 거르기 없앰) · 맹암거 · 사토 · 관 지점 시설 세트 = shape
- 3D 몸체 = 그림 자료 주인 종류의 표 3D 부품 · 알약 띠 = 표 자리꼴 구간(관 지점 시설은 알약만)
- ★1 노출형 · 개거 횡단도 none · ★2 쇄석 화면에서도 포장층 · ★3 기슭막이 표 자리꼴 interval(정의 파일 그대로) · ★4 코드 숨김 10종 list hidden · ★5 관 빈칸 D800 → 정의 기본 D1000
- 옛 시험: 도움 파일 로더 27개가 표 모듈 공용 한 벌(helper_322_catalog_module) 씀 · 기대값 고침(272_1 · b05_extra_stations · roundtrip · 314 · 52_7 · walls 거울 · wall_drain_pipe)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
2026-10-14 15:09:45 +09:00

187 lines
6.8 KiB
JavaScript

/* PLAN 219-1 단위검증 헬퍼 — 횡단 보기 방향키가 관을 몰래 옮기던 흠.
* 실제 TS(`B05_Profile_UI_Profile_Panel_Tools` · `B06_Section_UI_Cross_Panel_Base`)를 그 자리에서
* 트랜스파일해 Node 로 돌림 · 결과 = 표준출력 JSON 한 벌. 짝 = `test_219_1_arrow_owner.py`.
* 창 · 문서 듣개는 흉내(누름 = 문서 잡기 단계 · 키 = 문서 듣개(횡단 조정창) 뒤 창 듣개(종단도) 차례).
*/
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 cache = new Map();
const listeners = { window: {}, document: {} };
const on = (where) => (type, fn) => (listeners[where][type] ??= new Set()).add(fn);
const off = (where) => (type, fn) => listeners[where][type]?.delete(fn);
const session = new Map();
globalThis.sessionStorage = {
getItem: (k) => session.get(k) ?? null,
setItem: (k, v) => session.set(k, String(v)),
removeItem: (k) => session.delete(k),
};
globalThis.window = {
addEventListener: on("window"),
removeEventListener: off("window"),
setTimeout: () => 0,
clearTimeout: () => undefined,
setInterval: () => 0,
clearInterval: () => undefined,
};
globalThis.document = { addEventListener: on("document"), removeEventListener: off("document") };
// 화면 계산 · 이력 · 도구줄은 이 흠과 무관 — 쓰는 꼴만 흉내
const calls = { move: [], edits: 0 };
const STUBS = {
"./B05_Profile_UI_Profile_Alignment": { adjustStation: () => (calls.edits++, {}) },
"./B05_Profile_UI_Profile_Edit": { HOLD_DELAY_MS: 500, HOLD_INTERVAL_MS: 100 },
"./B05_Profile_UI_Profile_History": {
createProfileHistory: () => ({
record: () => undefined,
pause: () => undefined,
resume: () => undefined,
undo: () => undefined,
redo: () => undefined,
canUndo: () => false,
canRedo: () => false,
}),
},
"./B05_Profile_UI_Profile_Tools": {
createProfileTools: () => ({ mode: () => "none", clear: () => undefined }),
},
"./B05_Profile_UI_Profile_Straighten": {},
// [자유지정](215-1) — 화면 설정(@config) 줄기를 끌고 옴 · 이 흠과 무관
"../A00_Common/design/design_profile_free": {
createFreeDraw: () => ({ key: () => false, hint: () => "" }),
},
"../A00_Common/design/design_profile_free_view": {
createFreeView: () => ({ mount: () => undefined, redraw: () => undefined }),
},
};
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) =>
(CATALOG.isCatalogRequest(request) ? CATALOG : null) ??
STUBS[request] ??
(request.startsWith(".")
? loadTs(path.join(path.dirname(full), `${request}.ts`))
: require(request));
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
cache.set(full, box.exports);
return box.exports;
}
const tools = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_Profile_Panel_Tools.ts"));
const base = loadTs(path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Panel_Base.ts"));
// 엄상준 263.21 관 — 횡단 보기에서 그 관 카드가 골라져 종단도도 그 관 측점을 고른 상태
const pipe = { id: "p1", chainage_m: 263.21, origin: "pipe" };
tools.createPanelTools({
root: { isConnected: true },
base: () => ({}),
alignment: () => null,
edits: () => ({}),
applyEdits: () => undefined,
selectedStationId: () => "irregular:p1",
irregularStations: () => [pipe],
stationIdOf: (s) => `irregular:${s.id}`,
moveStation: (s, to) => calls.move.push([s.chainage_m, to]),
drainage: { pipeChainages: () => [], setPipeChainages: () => undefined },
restore: () => undefined,
restoreSaved: () => undefined,
canRestoreSaved: () => false,
refresh: () => undefined,
});
/** 누른 자리 — 종단도 안이면 closest 가 그 판을 줌(새 창 요소처럼 Element 가 아닌 것도). */
const spot = (inProfile) => ({
closest: (sel) => (inProfile && sel === ".b05-route-profile" ? {} : null),
});
const press = (target) => listeners.document.pointerdown?.forEach((fn) => fn({ target }));
/** 횡단 조정창 흉내 — 관 · 도수로 띠를 고른 동안 `swallowsArrows` 면 삼킴(Cross_Structure_Panel 배선과 같음). */
let side = "";
const crossPanel = (event) => {
if (base.swallowsArrows(side, event.key)) event.preventDefault();
};
listeners.document.keydown = new Set([crossPanel]);
function key(name) {
const event = {
key: name,
target: null,
repeat: false,
defaultPrevented: false,
preventDefault() {
this.defaultPrevented = true;
},
};
listeners.document.keydown.forEach((fn) => fn(event));
listeners.window.keydown?.forEach((fn) => fn(event));
listeners.window.keyup?.forEach((fn) => fn({ key: name }));
return event.defaultPrevented;
}
function run(steps) {
calls.move = [];
calls.edits = 0;
steps();
return { move: calls.move, edits: calls.edits };
}
const out = {
// 횡단 카드를 누른 뒤(조정창 없음) ← → = 안 옮김 — 옛 흠: 263.21 → 263.31
cardThenArrows: run(() => {
press(spot(false));
key("ArrowRight");
key("ArrowLeft");
}),
// 종단도를 누른 뒤 ← → = 지금처럼 0.1 m 이동
profileThenArrows: run(() => {
press(spot(true));
key("ArrowRight");
}),
// 새 창 종단도(다른 창 요소)도 종단도 안
popoutThenArrows: run(() => {
press(Object.create(null, { closest: { value: spot(true).closest } }));
key("ArrowLeft");
}),
// 횡단에서 도수로 띠 고름 · 카드 쪽 누름 = 방향키 넷 다 아무것도 안 옮김(계획고도)
chute: run(() => {
side = "chute";
press(spot(false));
["ArrowRight", "ArrowLeft", "ArrowUp", "ArrowDown"].forEach(key);
}),
pipeSide: run(() => {
side = "pipe";
press(spot(false));
key("ArrowRight");
key("ArrowUp");
}),
// 도수로 고른 채 종단도를 누르면 방향키는 종단도 몫
chuteThenProfile: run(() => {
side = "chute";
press(spot(true));
key("ArrowRight");
}),
// 조정창 있는 벽 고름 · 고름 없음 = 삼키지 않음(조정창 · 종단도 제 길)
swallow: {
wall: base.swallowsArrows("inlet", "ArrowRight"),
none: base.swallowsArrows("", "ArrowRight"),
letter: base.swallowsArrows("chute", "a"),
},
};
side = "";
out.cardUpArrow = run(() => {
press(spot(false));
key("ArrowUp"); // 고름 없는 카드 ▲ = 계획고(24-3b 그대로)
});
process.stdout.write(JSON.stringify(out));