- 옮김: B04 2 · B05 23 · B06 11 · A00_Common/design 13 (9,299 줄) · 이미 logic/ 에 있던 16 파일 그대로 - 안 옮김: 묶음 입구(package.json build:*) · 파이썬 · 순수지만 계산 아닌 8 (보기 결선 · 콜백 · API 타입 · 갈아끼움 알림판) - import 266 줄(142 파일) · 시험 · 주석 · 짝 표시 문자열 경로 고침 · B05 · B06 CLAUDE.md 짝 줄 logic/ - 시험 도우미 새 경로: 가짜 require 열쇠 · B05()/B06() 부름 · 형제 import 자리 · tsc 묶어 돌리는 시험 · 정규식 · rglob - design_view_cross 줄 한도 시험 685 → 688 (긴 import 경로 줄바꿈 3줄) - 확인: typecheck 0 · 서버 묶음 넷(corridor · server-calc · b07-extent · b07-note) 다시 지어 코드 같음(//#region 경로 주석만 다름) · 관련 시험 270 파일 — 남은 실패 19 는 옮기기 전과 같음 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
122 lines
5.0 KiB
TypeScript
122 lines
5.0 KiB
TypeScript
/* =============================================================================
|
|
* B05_Profile_UI_RouteEdit_Draft.ts
|
|
* 노선 편집기 ↔ 계산 묶음 다리(PLAN 346-1) — 노선을 한 번 고칠 때마다(놓기 · 더함 · 지움 · 곡선 ·
|
|
* 되돌리기 · 다시하기 · [저장 전으로] · 추천 [적용]) 노선 초안을 넘김. 끄는 동안은 넘기지 않음
|
|
* (사진첩에 쌓이는 때 = 손을 놓은 때).
|
|
*
|
|
* 편집기는 사진첩(`_History`)을 `watch` 로 감싸기만 함 — 고친 줄 하나. 저장 안 한 초안은
|
|
* 다리가 캐시 칸(`route-draft`)에 두고, 편집기가 자료를 읽은 뒤 `restore` 로 한 번 되살림
|
|
* (새로고침 · 보기 전환에도 남음). 계산 · 가게는 `A00_Common/design/design_pipeline_page.ts` 몫.
|
|
*
|
|
* 되돌리기 줄(346-4) — 페이지 사진첩(`_Profile_History`)이 있으면 쌓기 · [저장 전으로] 뒤 사진첩에 한 걸음 ·
|
|
* [되돌리기] · [다시하기]는 사진첩이 함(평면 · 종단 · 횡단 · 관 · 구조물 한 줄) · 사진첩이 되쓴 노선
|
|
* 초안은 `fromPage` 로 편집기 새 걸음이 됨. 사진첩이 없으면 편집기 사진 그대로.
|
|
* ========================================================================== */
|
|
|
|
import type { RouteDraft } from "../A00_Common/design/logic/design_pipeline_types";
|
|
import { pageHistory } from "./B05_Profile_UI_Profile_History";
|
|
import {
|
|
sameLine,
|
|
type RouteEditHistory,
|
|
type RouteEditSnapshot,
|
|
} from "./B05_Profile_UI_RouteEdit_History";
|
|
|
|
/** 편집기가 넘기는 노선 — 판 번호 · 지형은 다리가 붙임. */
|
|
export type RouteDraftEdit = Omit<RouteDraft, "rev" | "terrain">;
|
|
|
|
export interface RouteDraftBridge {
|
|
/** 노선이 바뀜 — 열 때(저장분)와 같으면 null. */
|
|
edited(draft: RouteDraftEdit | null): void;
|
|
/** 저장 안 한 초안(캐시 칸) — 없으면 null. */
|
|
saved(): RouteDraft | null;
|
|
}
|
|
|
|
/** 사진 → 노선 초안(고정 · 고른 점은 노선이 아님). */
|
|
export function draftOfSnapshot(snapshot: RouteEditSnapshot, stationSnap: boolean): RouteDraftEdit {
|
|
return {
|
|
nodes: snapshot.planned.map(([x, y]): [number, number] => [x, y]),
|
|
curveOn: [...snapshot.curveOn],
|
|
curveRadius: [...snapshot.curveRadius],
|
|
curveLock: [...snapshot.curveLock],
|
|
curveArc: [...snapshot.curveArc],
|
|
stationSnap,
|
|
};
|
|
}
|
|
|
|
/** 노선 초안 → 사진 — 고정은 편집기가 좌표로 다시 짚음(`restoreSnapshot` 새 걸음). */
|
|
export function snapshotOfDraft(draft: RouteDraft): RouteEditSnapshot {
|
|
return {
|
|
planned: draft.nodes.map(([x, y]): [number, number] => [x, y]),
|
|
curveOn: [...draft.curveOn],
|
|
curveRadius: [...draft.curveRadius],
|
|
curveLock: [...draft.curveLock],
|
|
curveArc: [...draft.curveArc],
|
|
apexLock: draft.nodes.map(() => false),
|
|
picked: -1,
|
|
};
|
|
}
|
|
|
|
/** 사진첩을 감싸 노선이 바뀔 때마다 다리에 알림 — 쌓기 · 되돌리기 · 다시하기 · 열 때로. */
|
|
export function watch(
|
|
history: RouteEditHistory,
|
|
bridge: RouteDraftBridge | undefined,
|
|
stationSnap: () => boolean,
|
|
): RouteEditHistory {
|
|
if (!bridge) return history;
|
|
const tell = (snapshot: RouteEditSnapshot | null): RouteEditSnapshot | null => {
|
|
if (snapshot)
|
|
bridge.edited(history.isDirty() ? draftOfSnapshot(snapshot, stationSnap()) : null);
|
|
return snapshot;
|
|
};
|
|
/** 사진첩이 되쓴 노선 초안 → 편집기 사진(같은 선의 옛 걸음이 있으면 고정 · 꼬리표째). */
|
|
const fromPage = (): RouteEditSnapshot | null => {
|
|
const kept = bridge.saved();
|
|
const opened = history.first?.();
|
|
const line = kept?.nodes.length ? snapshotOfDraft(kept) : opened;
|
|
const now = history.current?.();
|
|
if (!line || (now && sameLine(now, line))) return null;
|
|
const snapshot = history.match?.(line) ?? line;
|
|
history.commit(snapshot);
|
|
tell(snapshot);
|
|
return snapshot;
|
|
};
|
|
return {
|
|
...history,
|
|
commit: (snapshot) => {
|
|
history.commit(snapshot);
|
|
tell(snapshot);
|
|
pageHistory()?.record();
|
|
},
|
|
undo: () => {
|
|
const album = pageHistory();
|
|
if (!album) return tell(history.undo());
|
|
album.undo();
|
|
return null; // 편집기 되살림은 사진첩 restore → fromPage
|
|
},
|
|
redo: () => {
|
|
const album = pageHistory();
|
|
if (!album) return tell(history.redo());
|
|
album.redo();
|
|
return null;
|
|
},
|
|
reset: () => {
|
|
const snapshot = tell(history.reset());
|
|
if (snapshot) pageHistory()?.record();
|
|
return snapshot;
|
|
},
|
|
canUndo: () => pageHistory()?.canUndo() ?? history.canUndo(),
|
|
canRedo: () => pageHistory()?.canRedo() ?? history.canRedo(),
|
|
fromPage,
|
|
};
|
|
}
|
|
|
|
/** 저장 안 한 초안을 편집기에 한 번 되살림 — 사진첩에 새 걸음으로(되돌리면 저장분). */
|
|
export function restore(
|
|
bridge: RouteDraftBridge | undefined,
|
|
apply: (snapshot: RouteEditSnapshot, message: string, record: boolean) => void,
|
|
): void {
|
|
const kept = bridge?.saved();
|
|
if (kept?.nodes.length)
|
|
apply(snapshotOfDraft(kept), "저장 안 한 노선 초안을 되살렸습니다.", true);
|
|
}
|