fix(B05·B06 한 페이지): 측점 갈아끼움 뒤 누가거리 조절 세션 칸 바로 옮김 — 조정창 맵이 안 선 페이지도 초안 카드로 (415-3)

- 관 측점 열쇠 갈림(세션 pipe-s1 ↔ 계산 줄 pipe:p35100) = 415-1 로 풀림 · B06 연 길은 초안 카드 조절 그대로(화면)
- B05 만 연 길(새로고침 · 조정창 맵 없음) · 세월 · BOX · 연동 칸은 안 옮겨짐 → 갈아끼움 알림 뒤 Station_Keys 가
  ROUTE_CARRY_STATES(측점 열쇠 칸 빼고) 세션 칸을 바로 새 자리로 · 조정창 맵이 옮긴 칸은 그대로(Swap_Bus afterAnySwap)
- 시험: test_415 누가거리 조절 · 노선 바뀜 대조 = 417 넘김 규칙(사면 쉬프트 · 소단 깊이 넘김)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
This commit is contained in:
2026-10-18 13:34:28 +09:00
co-authored by Claude Opus 5.5
parent c263bef6f5
commit 1ee4febad7
5 changed files with 89 additions and 4 deletions
+36 -1
View File
@@ -17,7 +17,9 @@
* `ROUTE_CARRY_STATES`)도 측점 열쇠로 새 노선 측점에 옮김(구조물 조절 = 관 · 구조물 열쇠만 · 서버와 같음).
* ========================================================================== */
import { readState, writeState, type StateName } from "../A00_Common/b_page_state";
import { readState, stateKey, writeState, type StateName } from "../A00_Common/b_page_state";
import { renameChainageKeys } from "./logic/B06_Section_Station_Swap";
import { afterAnySwap } from "./B06_Section_Swap_Bus";
import { pipeIdAt, pipeIdNear } from "../A00_Common/design/design_pipe_ids";
import { showToast } from "@ui/ui_template_elements";
import {
@@ -466,3 +468,36 @@ export function carryChainageStates(
}
return outside.size;
}
/**
* 측점이 움직임(갈아끼움 · 옛 0.01 m 글 → 새 글) — 조정창 맵이 안 선 누가거리 칸(B05 만 연 페이지 · 새로고침 ·
* 세월 · BOX · 연동)을 세션에서 바로 새 자리로(415-3). 칸 = `ROUTE_CARRY_STATES`(측점 열쇠 칸 빼고) · 조정창
* 맵이 이번에 옮긴 칸(`owned`)은 그대로.
*/
function moveIdleStates(moves: ReadonlyMap<string, string>, owned: ReadonlySet<string>): void {
if (!moves.size || !current) return;
const { projectId, routeId } = current;
const move = (cells: Record<string, unknown>) =>
Object.fromEntries(renameChainageKeys(new Map(Object.entries(cells)), moves));
for (const state of Object.keys(ROUTE_CARRY_STATES)) {
if ((KEYED_STATES as readonly string[]).includes(state)) continue;
const name = state as StateName;
const key = stateKey(name, projectId, routeId);
if (!key || owned.has(key)) continue;
const raw = readState<Record<string, unknown>>(name, projectId, routeId);
if (!raw || typeof raw !== "object") continue;
// 연동 = { detached: {누가거리: …}, grade: {…} } 두 겹
const next =
state === "revetlink"
? Object.fromEntries(
Object.entries(raw).map(([part, cells]) => [
part,
cells && typeof cells === "object" ? move(cells as Record<string, unknown>) : cells,
]),
)
: move(raw);
writeState(name, next, projectId, routeId);
}
}
afterAnySwap((_detail, moves, owned) => moveIdleStates(moves, owned));
+30
View File
@@ -21,7 +21,37 @@ export function onAnySwap(fn: DetailListener): () => void {
return () => anyListeners.delete(fn);
}
/** 알림 한 번 동안 조정창 맵이 옮긴 세션 칸(열쇠 글) — 뒤 차례(`afterAnySwap`)는 그 칸을 다시 안 옮김(415-3). */
const moved = new Set<string>();
const afterListeners = new Set<
(
detail: SectionDetailResponse,
moves: ReadonlyMap<string, string>,
owned: ReadonlySet<string>,
) => void
>();
/** 조정창 맵이 세션 칸을 옮겼다고 적음(`_UI_Page_Link_Session`). */
export function markMovedStates(keys: Iterable<string>): void {
for (const key of keys) moved.add(key);
}
/** 갈아끼움 알림이 다 돈 뒤 부름 — `owned` = 그 사이 조정창 맵이 옮긴 세션 칸. */
export function afterAnySwap(
fn: (
detail: SectionDetailResponse,
moves: ReadonlyMap<string, string>,
owned: ReadonlySet<string>,
) => void,
): () => void {
afterListeners.add(fn);
return () => afterListeners.delete(fn);
}
/** 갈아끼운 쪽(`Section_Store`)이 부름. */
export function emitSwap(detail: SectionDetailResponse, moves: ReadonlyMap<string, string>): void {
moved.clear();
anyListeners.forEach((fn) => fn(detail, moves));
const owned = new Set(moved);
afterListeners.forEach((fn) => fn(detail, moves, owned));
}
@@ -27,7 +27,7 @@ import {
import { culvertCardState } from "./B06_Section_UI_Cross_View_Structure";
import type { PipeRevetChoice } from "../A00_Common/design/design_structure_pipe_calc";
import { renameChainageKeys } from "./logic/B06_Section_Station_Swap";
import { onAnySwap } from "./B06_Section_Swap_Bus";
import { markMovedStates, onAnySwap } from "./B06_Section_Swap_Bus";
import { readByKey, writeByKey } from "../A00_Common/b_page_state";
/** 세션에 담기는 측점별 조작값 한 겹 맵. */
@@ -65,6 +65,7 @@ export function moveSessionMaps(moves: ReadonlyMap<string, string>): void {
next.forEach((value, key) => map.values.set(key, value));
map.persist();
}
markMovedStates(latest.keys()); // 나머지 누가거리 칸은 `Station_Keys` 가 옮김(415-3)
}
// 상세 갈아끼움(어느 보기가 했든)마다 한 번 — 조정창 값을 옮긴 측점 자리로