- Ctrl+Z · Y · 편집기 [되돌리기] · [다시하기] · [반영 전으로] = 페이지 사진첩(_Profile_History) — 평면 · 종단 · 횡단 · 관 · 구조물을 만진 차례 거꾸로 - 편집기 쌓기 · [반영 전으로] 뒤 사진첩 한 걸음(_Draft.watch) · 사진첩이 되쓴 route-draft 는 fromPage 로 편집기 새 걸음(같은 선 옛 걸음의 고정 · 추천 꼬리표째) - 쌓는 줄 없는 조작(횡단 카드 등) = 놓은 뒤 · change 뒤 400 ms 한 번 찍음 · 고름 · 관 id · 종류 캐시 · 구조물 넘김 · b08 · b09 칸은 사진 밖 - 「마지막에 만진 판만」(_RouteEdit_UndoOwner) 지움 · 편집기는 단축키 안 들음 - 시험 test_346_4_one_undo.py · 345-3 시험 · 219-1 헬퍼 새 꼴로 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
162 lines
5.6 KiB
JavaScript
162 lines
5.6 KiB
JavaScript
/* 346-4 되돌리기 한 줄 헬퍼 — 진짜 `_Profile_History`(페이지 사진첩) · `_RouteEdit_History`(편집기 단추) ·
|
|
* `_RouteEdit_Draft`(다리 감싸기)를 트랜스파일해 가짜 창 · 세션에서 돌림. 다리 = 세션 칸 route-draft 를
|
|
* 바로 쓰는 흉내(`design_pipeline_page` 와 같은 때). 결과 JSON 한 벌(표준출력).
|
|
*/
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
const data = new Map();
|
|
globalThis.sessionStorage = {
|
|
get length() {
|
|
return data.size;
|
|
},
|
|
key: (i) => [...data.keys()][i] ?? null,
|
|
getItem: (k) => (data.has(k) ? data.get(k) : null),
|
|
setItem: (k, v) => data.set(k, String(v)),
|
|
removeItem: (k) => data.delete(k),
|
|
};
|
|
const win = new EventTarget();
|
|
win.setTimeout = setTimeout;
|
|
win.clearTimeout = clearTimeout;
|
|
globalThis.window = win;
|
|
|
|
const cache = new Map();
|
|
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) =>
|
|
request.startsWith(".")
|
|
? loadTs(`${path.resolve(path.dirname(full), request)}.ts`)
|
|
: require(request);
|
|
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
|
return box.exports;
|
|
}
|
|
const page = path.join(ROOT, "B05_Profile");
|
|
const album = loadTs(path.join(page, "B05_Profile_UI_Profile_History.ts"));
|
|
const edit = loadTs(path.join(page, "B05_Profile_UI_RouteEdit_History.ts"));
|
|
const draftLink = loadTs(path.join(page, "B05_Profile_UI_RouteEdit_Draft.ts"));
|
|
|
|
const P = "p1";
|
|
const ROUTE = `aislo:draft:route-draft:${P}`;
|
|
const GRADE = `aislo:draft:alignment-draft:${P}:7`;
|
|
const CROSS = `aislo:draft:crossdesign:${P}:7`;
|
|
const SELECT = `aislo:draft:selection:${P}`;
|
|
const tick = (ms = 5) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
const snap = (nodes, lock = false) => ({
|
|
planned: nodes,
|
|
curveOn: nodes.map(() => true),
|
|
curveRadius: nodes.map(() => null),
|
|
curveLock: nodes.map(() => null),
|
|
curveArc: nodes.map(() => null),
|
|
apexLock: nodes.map((_, i) => lock && i === 1),
|
|
picked: 1,
|
|
rec: lock ? "tag-moved" : "tag-open",
|
|
});
|
|
|
|
(async () => {
|
|
const out = {};
|
|
let profileReads = 0;
|
|
const pageAlbum = album.createProfileHistory(() => (profileReads += 1));
|
|
const opened = snap([
|
|
[0, 0],
|
|
[50, 0],
|
|
[100, 0],
|
|
]);
|
|
const raw = edit.createRouteEditHistory(opened);
|
|
const bridge = {
|
|
saved: () => JSON.parse(sessionStorage.getItem(ROUTE) ?? "null"),
|
|
edited: (draft) =>
|
|
draft
|
|
? sessionStorage.setItem(ROUTE, JSON.stringify({ ...draft, rev: 1, terrain: null }))
|
|
: sessionStorage.removeItem(ROUTE),
|
|
};
|
|
const history = draftLink.watch(raw, bridge, () => false);
|
|
const clicks = {};
|
|
const button = (act) => ({
|
|
disabled: false,
|
|
addEventListener: (_type, fn) => (clicks[act] = fn),
|
|
});
|
|
const buttons = { '[data-act="undo"]': button("undo"), '[data-act="redo"]': button("redo") };
|
|
const editor = []; // 편집기에 되살린 걸음(가운데 노드 x · 고정 · 꼬리표 · 글)
|
|
const controls = edit.bindHistoryControls({
|
|
overlay: { querySelector: (sel) => buttons[sel] },
|
|
getHistory: () => history,
|
|
restore: (s, message) => editor.push([s.planned[1][0], s.apexLock[1], s.rec, message]),
|
|
});
|
|
const state = () => ({
|
|
route: bridge.saved()?.nodes?.[1]?.[0] ?? null,
|
|
grade: sessionStorage.getItem(GRADE),
|
|
cross: sessionStorage.getItem(CROSS),
|
|
undo: !buttons['[data-act="undo"]'].disabled,
|
|
redo: !buttons['[data-act="redo"]'].disabled,
|
|
});
|
|
controls.sync();
|
|
out.start = state();
|
|
|
|
// ① 평면 점 옮김 — 끄는 동안 두 번 쌓아도 놓을 때 한 걸음
|
|
win.dispatchEvent(new Event("pointerdown"));
|
|
history.commit(
|
|
snap([
|
|
[0, 0],
|
|
[60, 5],
|
|
[100, 0],
|
|
]),
|
|
);
|
|
history.commit(
|
|
snap(
|
|
[
|
|
[0, 0],
|
|
[70, 5],
|
|
[100, 0],
|
|
],
|
|
true,
|
|
),
|
|
);
|
|
out.heldUndo = pageAlbum.canUndo();
|
|
win.dispatchEvent(new Event("pointerup"));
|
|
await tick();
|
|
// ② 계획고 ▲ — 종단도가 쌓음
|
|
sessionStorage.setItem(GRADE, '{"station_offsets":{"20.000":0.1}}');
|
|
pageAlbum.record();
|
|
// 고름만 바뀜 — 사진 밖이라 걸음 안 늘어남
|
|
sessionStorage.setItem(SELECT, '{"chainageM":40}');
|
|
pageAlbum.record();
|
|
// ③ 횡단 카드 고침 — 쌓는 줄 없음 · 칸 바꿈(change) 뒤 기다려 한 걸음
|
|
sessionStorage.setItem(CROSS, '{"20.000":{"ground_type":"soil"}}');
|
|
win.dispatchEvent(new Event("change"));
|
|
await tick(450);
|
|
out.afterEdits = state();
|
|
|
|
// Ctrl+Z 세 번 — 거꾸로(종단도 단축키 = 사진첩 undo) · 셋째는 편집기 [되돌리기] 단추
|
|
pageAlbum.undo();
|
|
out.undo1 = state();
|
|
pageAlbum.undo();
|
|
out.undo2 = state();
|
|
out.editorBeforeRouteUndo = editor.length;
|
|
clicks.undo();
|
|
out.undo3 = state();
|
|
out.editorAfterUndo = [...editor];
|
|
// 다시하기 세 번 — 첫째는 편집기 [다시하기] · 나머지는 Ctrl+Y
|
|
clicks.redo();
|
|
out.redo1 = state();
|
|
pageAlbum.redo();
|
|
pageAlbum.redo();
|
|
out.redo3 = state();
|
|
out.editorAfterRedo = editor.slice(out.editorAfterUndo.length);
|
|
out.profileReads = profileReads;
|
|
// [반영 전으로] — 사진첩 한 걸음 · 되돌리면 옮긴 노선
|
|
history.reset();
|
|
out.reset = state();
|
|
pageAlbum.undo();
|
|
out.resetUndo = state();
|
|
controls.dispose();
|
|
process.stdout.write(JSON.stringify(out));
|
|
})();
|