Files
Aislo/B06_Section/B06_Section_Swap_Bus.ts
T
eomsangdonandClaude Opus 5.5 1ee4febad7 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
2026-10-18 13:34:28 +09:00

58 lines
2.3 KiB
TypeScript

/* =============================================================================
* B06_Section_Swap_Bus.ts
* 상세 갈아끼움 알림판(PLAN 346-2) — 측점 수가 바뀌어 `Section_Store` 가 상세 객체를 갈아끼우면 여기로 알림.
* 가져오는 것 없음 — 세션 조작값 맵(`_UI_Page_Link_Session`) · 암 경계 저장소(`_UI_Page_Persist`)가 모듈을 읽을 때
* 붙어도 `Section_Store` 와 돌고 도는 가져오기가 안 생김.
* ========================================================================== */
import type { SectionDetailResponse } from "./B06_Section_Api_Types";
/** 새 상세 · 이어 받은 측점의 누가거리 옮김(옛 0.01 m 글 → 새 글 · 조작값 맵을 옮길 때). */
export type DetailListener = (
detail: SectionDetailResponse,
moves: ReadonlyMap<string, string>,
) => void;
const anyListeners = new Set<DetailListener>();
/** 어느 노선이든 상세가 갈아끼워지면 부름(조정창 값 · 암 경계 옮김 · 다시 그림) — 돌려준 함수로 끊음. */
export function onAnySwap(fn: DetailListener): () => void {
anyListeners.add(fn);
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));
}