- berm 은 구조물(소단)에서 셈한 값 · 손 고침 길 없음(쓰는 곳 = 페이지 시설 줄 하나) → 등록부 결과 통으로 - 떠남 확인 제외 목록의 berm 줄 지움(초안 통이 아니라 필요 없음) - 쓰는 곳 없는 readBermSpans 지움 · 읽는 곳(시설 줄 · 다시 셈) 이름 그대로 - 지킴 시험 test_477_undo_now_berm(즉시 켜짐 · flush · 묶음 · berm 통) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
108 lines
4.3 KiB
TypeScript
108 lines
4.3 KiB
TypeScript
/* =============================================================================
|
|
* A00_Common/design/design_leave.ts
|
|
* B05 · B06 떠남 확인(346-4) — 저장 안 한 조작(초안 통)이 있으면 탭 닫기 · 다른 페이지로 갈 때
|
|
* 공용 물음(`ui_template_leave_dialog` · [취소] [폐기] [저장]).
|
|
*
|
|
* 고침 = 초안 통 칸 중 고침이 아닌 칸(고름 · 관 id 장부 · 종류 캐시 · 고른 구조물 넘김 · 소단 사본 = 구조물 파생) ·
|
|
* 다른 페이지 칸(b08 · b09 · D · E) · 저장 뒤에도 남기는 표준 횡단면(std-cross)을 뺀 것.
|
|
* 횡단 초안은 [저장] 뒤에도 세션에 남음 — 그래서 「있음」이 아니라 **마지막 깨끗한 때와 다름**으로 봄:
|
|
* [저장] · [확정]이 끝나면 `markDesignClean` 이 그때 지문을 적음 · [초기화]는 초안을 다 지우고 새로 읽음.
|
|
* B05 ↔ B06 는 한 페이지 — 그 사이 이동은 묻지 않음.
|
|
* 프로젝트 바꿈은 이 페이지를 떠난 뒤라 이 물음이 먼저 · 들어갈 때 물음은 `b_project_switch`.
|
|
* ========================================================================== */
|
|
|
|
import { ROUTES } from "@config/config_frontend";
|
|
import { t as L } from "@ui/ui_template_locale";
|
|
import { guardProjectLeave } from "../b_project_switch";
|
|
import {
|
|
clearState,
|
|
draftFilled,
|
|
namesInBucket,
|
|
readByKey,
|
|
routeStateKeys,
|
|
stateKey,
|
|
type StateName,
|
|
} from "../b_page_state";
|
|
|
|
/** 초안 통이지만 고침으로 안 보는 칸 — 묻지 않고 [폐기]에도 안 지움. */
|
|
const NOT_EDITS = new Set<StateName>([
|
|
"selection",
|
|
"pipe-ids",
|
|
"structure-pick",
|
|
"structure-type-cache",
|
|
"std-cross",
|
|
]);
|
|
const OTHER_PAGE = /^(b0[89]|d0\d|e0\d)-/;
|
|
|
|
export function editDraftNames(): StateName[] {
|
|
return namesInBucket("draft").filter((name) => !NOT_EDITS.has(name) && !OTHER_PAGE.test(name));
|
|
}
|
|
|
|
/** 고침 칸 지문 — 모든 노선 것 · 빈 칸 뺌 · 고침이 없으면 "". */
|
|
export function editSignature(projectId: string): string {
|
|
const parts: string[] = [];
|
|
for (const name of editDraftNames()) {
|
|
const own = stateKey(name, projectId);
|
|
const keys = own ? [own] : routeStateKeys(name, projectId);
|
|
for (const key of keys.sort()) {
|
|
const raw = readByKey(key);
|
|
if (raw !== null && draftFilled(raw, name, projectId)) parts.push(`${key}=${raw}`);
|
|
}
|
|
}
|
|
return parts.join("\n");
|
|
}
|
|
|
|
/** 긴 지문을 들고 있지 않게 — FNV-1a 32비트 + 길이. */
|
|
function digest(text: string): string {
|
|
let hash = 0x811c9dc5;
|
|
for (let index = 0; index < text.length; index += 1) {
|
|
hash ^= text.charCodeAt(index);
|
|
hash = Math.imul(hash, 0x01000193);
|
|
}
|
|
return `${text.length}:${hash >>> 0}`;
|
|
}
|
|
|
|
// ponytail: 깨끗한 표는 메모리만 — [저장] 뒤 새로고침하면 남은 횡단 초안 때문에 한 번 더 물음.
|
|
const cleanMarks = new Map<string, string>();
|
|
|
|
/** 지금 고침 지문(짧게) — 저장 본문을 낸 때와 끝난 때를 견줌(498-3). */
|
|
export function designSignature(projectId: string): string {
|
|
return digest(editSignature(projectId));
|
|
}
|
|
|
|
/** [저장] · [확정]이 끝난 때 — 지금 초안을 깨끗한 것으로 적음. */
|
|
export function markDesignClean(projectId: string): void {
|
|
if (projectId) cleanMarks.set(projectId, digest(editSignature(projectId)));
|
|
}
|
|
|
|
/** 저장 안 한 조작이 있나. */
|
|
export function designDirty(projectId: string): boolean {
|
|
if (!projectId) return false;
|
|
const signature = editSignature(projectId);
|
|
if (!signature) return false;
|
|
return cleanMarks.get(projectId) !== digest(signature);
|
|
}
|
|
|
|
/** [폐기] — 고침 칸만 지움(모든 노선). */
|
|
export function discardDesignEdits(projectId: string): void {
|
|
editDraftNames().forEach((name) => clearState(name, projectId));
|
|
}
|
|
|
|
/**
|
|
* 페이지를 세울 때 한 번 — `root` 가 화면에서 빠지면 스스로 풂(공용 `guardProjectLeave` 위 얇은 껍질).
|
|
* `save` = [저장] 한 줄기(`saveSharedDesign(shared, false)`).
|
|
*/
|
|
export function guardDesignLeave(
|
|
projectId: string,
|
|
root: HTMLElement,
|
|
save: () => Promise<unknown>,
|
|
): void {
|
|
guardProjectLeave(root, {
|
|
dirty: () => designDirty(projectId),
|
|
save,
|
|
discard: () => discardDesignEdits(projectId),
|
|
message: L("B05_Design_Leave"),
|
|
samePage: [ROUTES.B05_PROFILE, ROUTES.B06_SECTION],
|
|
});
|
|
}
|