From 4eadcf073d640cc18255f6582718105fdfecd00b Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 16 Oct 2026 23:59:39 +0900 Subject: [PATCH] =?UTF-8?q?fix(=EB=90=98=EB=8F=8C=EB=A6=AC=EA=B8=B0):=2036?= =?UTF-8?q?4-1=20=ED=9A=A1=EB=8B=A8=20=EB=B3=B4=EA=B8=B0=20=EC=B2=AB=20?= =?UTF-8?q?=EC=97=B4=EA=B8=B0=EC=9D=98=20=ED=91=9C=EC=A4=80=20=ED=9A=A1?= =?UTF-8?q?=EB=8B=A8=20=EC=B1=84=EC=9B=80=20=3D=20=EC=94=A8=EC=95=97=20?= =?UTF-8?q?=C2=B7=20=EA=B1=B8=EC=9D=8C=20=EC=95=84=EB=8B=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사진첩 seedPageHistory — 비었던 칸을 채운 값을 그 칸이 없는 사진 전부에 넣음(되돌려도 안 지움) - 표준 횡단 패널이 빈 세션에 기본값을 쓸 때 · 저장 표준 횡단을 채울 때 씨앗으로 - ORCA 5182 · 검증_sub7: 계획고 ▲ 뒤 횡단 보기 · 카드 고름 · 종단 보기 = 걸음 1 · 되돌려도 표준 횡단 남음 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ --- B05_Profile/B05_Profile_UI_Profile_History.ts | 8 ++++++++ B06_Section/B06_Section_Api_Fetch.ts | 4 +++- B06_Section/B06_Section_UI_Standard_Panel.ts | 3 +++ resources/tester/test_364_1_view_no_step.py | 18 +++++++++++++++++- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/B05_Profile/B05_Profile_UI_Profile_History.ts b/B05_Profile/B05_Profile_UI_Profile_History.ts index 7c095e5fb..39b770abd 100644 --- a/B05_Profile/B05_Profile_UI_Profile_History.ts +++ b/B05_Profile/B05_Profile_UI_Profile_History.ts @@ -23,6 +23,7 @@ * 초안(표준 횡단면 · 첫 계산 결과 · 되살린 노선 초안)은 걸음이 아니라 열 때 모습. * 쉬는 동안 온 값(364-1) — 손길이 다 찍힌 뒤 새 손길이 닿기 전에 바뀐 칸(뒤에서 오는 파생 값 · 찍을 때만 * 옮겨 적는 관 자리)은 그 손길 직전에 지금 사진으로 덮음 — 판 열기 · 회전 같은 보기 손길이 걸음이 안 됨. + * 씨앗 값(364-1) — 비었을 때만 채우는 값(표준 횡단 · 횡단 보기 첫 열기)은 `seedPageHistory` 로 모든 사진에 넣음. * ========================================================================== */ /** 조작값 스냅샷 대상 세션 키 접두어. B05·B06은 한 페이지라 함께 뜬다(CLAUDE.md 5장). */ @@ -90,6 +91,9 @@ let gestures = 0; let active: ProfileHistory | null = null; /** 마지막에 세운 사진첩의 「쉬는 동안 온 값 덮기」 — 손길이 닿을 때 부름(364-1). */ let rebaseActive: (() => void) | null = null; +let seedActive: ((key: string) => void) | null = null; +/** 비었던 칸을 채운 씨앗 값 — 그 칸이 없는 사진 전부에 넣음(걸음 아님 · 되돌려도 안 지움). */ +export const seedPageHistory = (key: string | null): void => void (key && seedActive?.(key)); export const pageHistory = (): ProfileHistory | null => active; export function setPageHistory(history: ProfileHistory): void { active = history; @@ -272,6 +276,10 @@ export function createProfileHistory( sync(); stack[cursor] = takeSnapshot(stack[cursor]); // 쉬는 동안 온 값 = 지금 걸음의 모습 }; + seedActive = (key) => { + const value = isDataKey(key) ? sessionStorage.getItem(key) : null; + if (value !== null) stack.forEach((snapshot) => void (snapshot[key] ??= value)); + }; active = history; return history; } diff --git a/B06_Section/B06_Section_Api_Fetch.ts b/B06_Section/B06_Section_Api_Fetch.ts index 75b10640a..181ec122a 100644 --- a/B06_Section/B06_Section_Api_Fetch.ts +++ b/B06_Section/B06_Section_Api_Fetch.ts @@ -16,7 +16,8 @@ * 기존 호출 코드가 깨지지 않도록 여기서 그대로 다시 내보낸다. * ========================================================================== */ -import { clearState, readState, writeState } from "../A00_Common/b_page_state"; +import { clearState, readState, stateKey, writeState } from "../A00_Common/b_page_state"; +import { seedPageHistory } from "../B05_Profile/B05_Profile_UI_Profile_History"; import { API_BASE_URL, API_TIMEOUT_MS } from "@config/config_frontend"; import type { CrossDesign, @@ -102,6 +103,7 @@ function seedStandardCross( const stored = context.stored_standard_cross_section; if (stored && readState("std-cross", projectId) === null) { writeState("std-cross", stored, projectId); + seedPageHistory(stateKey("std-cross", projectId)); // 씨앗 — 되돌리기 걸음 아님(364-1) } return context; } diff --git a/B06_Section/B06_Section_UI_Standard_Panel.ts b/B06_Section/B06_Section_UI_Standard_Panel.ts index 71c09b3ae..9e445f0f6 100644 --- a/B06_Section/B06_Section_UI_Standard_Panel.ts +++ b/B06_Section/B06_Section_UI_Standard_Panel.ts @@ -21,9 +21,11 @@ import { clearState, readState, readStateRaw, + stateKey, writeState, writeStateRaw, } from "../A00_Common/b_page_state"; +import { seedPageHistory } from "../B05_Profile/B05_Profile_UI_Profile_History"; import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale"; import { createButton, createInputField, createSelectField } from "@ui/ui_template_elements"; import { buildStandardDiagram } from "./B06_Section_UI_Standard_Diagram"; @@ -385,6 +387,7 @@ export function createStandardPanel( // 옛 저장분이 그룹마다 다른 공통값을 갖고 있으면 토사 기준으로 한 벌로 맞춘다. unifyCommon(state); persist(); + if (!hadSession) seedPageHistory(stateKey("std-cross", projectId)); // 비었던 칸 채움 = 씨앗(364-1) renderBody(); /** 소스 표준값을 현재 상태에 전부 덮어쓴다(사용자가 "적용"을 눌렀을 때만 호출). */ diff --git a/resources/tester/test_364_1_view_no_step.py b/resources/tester/test_364_1_view_no_step.py index 286e71fb8..d364f3145 100644 --- a/resources/tester/test_364_1_view_no_step.py +++ b/resources/tester/test_364_1_view_no_step.py @@ -31,7 +31,7 @@ globalThis.sessionStorage = { }; globalThis.window = new EventTarget(); window.setTimeout = setTimeout; -const { createProfileHistory } = await import("./B05_Profile_UI_Profile_History.js"); +const { createProfileHistory, seedPageHistory } = await import("./B05_Profile_UI_Profile_History.js"); const wait = (ms) => new Promise((r) => setTimeout(r, ms)); const fire = (type) => window.dispatchEvent(new Event(type)); /** 누르고 놓음 — 보기 손길(세션 안 씀) 또는 편집(누른 동안 write). */ @@ -111,6 +111,17 @@ await click(); // 칸 밖 누름 fire("change"); await wait(450); out.typedKept = h3.canUndo(); + +// 씨앗 — 계획고 고친 뒤 [횡단 보기] 첫 열기가 비어 있던 표준 횡단을 채움(누른 사이) = 걸음 아님 · 되돌려도 남음 +data.clear(); +const h4 = createProfileHistory(() => {}); +await click(() => sessionStorage.setItem(K.grade, "g1")); +await click(() => { + sessionStorage.setItem(K.std, "seed"); + seedPageHistory(K.std); +}); +out.seedSteps = [h4.undo(), h4.canUndo()]; +out.seedKept = { std: sessionStorage.getItem(K.std), grade: sessionStorage.getItem(K.grade) }; writeFileSync(process.argv[2], JSON.stringify(out)); """ @@ -154,6 +165,11 @@ def test_편집만_걸음_되돌리기는_그_편집만(result) -> None: assert result["noMore"] is True +def test_씨앗_값은_걸음_아님_되돌려도_남음(result) -> None: + assert result["seedSteps"] == [True, False] # 계획고 한 걸음뿐 + assert result["seedKept"] == {"std": "seed", "grade": None} + + def test_앞_편집은_덮지_않음(result) -> None: assert result["settleKept"] is True assert result["typedKept"] is True