fix(B05 배수유역): 349-0e 자동은 흐림 없이 · 낡은 동안 제안 [적용] 끔
- 자동 모드 = 칸 흐림 · [업데이트] 없음 · 계산 중엔 구석 「계산 중」만 · 답 오면 조용히 바꿈(수동 그대로) - 낡은 동안 제안 [적용] 끔(두 모드) · title 「노선이 바뀌어 제안이 낡음 — 새 계산 뒤 적용」 · 새 답이 오면 다시 켬 - test_349_drainage_live_flow 고쳐 씀(자동 흐림 없음 · 낡은 [적용] 막음) - PLAN.md 349-0e Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWh5wNNycMkbwMpACz1rte
This commit is contained in:
@@ -5,7 +5,8 @@
|
||||
*
|
||||
* - 단계(`stage`)는 계산 묶음(`design_pipeline` · 붙이기 = sub7)이 노선 초안 · 관이 바뀔 때 부름 —
|
||||
* 기다리지 않고 null 을 돌려줌(배수유역은 뒤 단계 · 출력 없음).
|
||||
* - 노선 초안이 있으면 「낡음」 — 자동 = 3 s 조용한 뒤 셈 · 수동 = 칸만 흐림 + 가운데 [업데이트].
|
||||
* - 노선 초안이 있으면 「낡음」 — 자동 = 흐림 없이 3 s 조용한 뒤 셈(구석 「계산 중」만) · 수동 = 칸만
|
||||
* 흐림 + 가운데 [업데이트]. 낡은 동안 제안 [적용]은 끔(두 모드 · 새 답이 오면 다시 켬).
|
||||
* - 다시 조작하면 처음으로 — 떠 있는 요청을 끊고(AbortController) 다시 흐림 또는 다시 3 s.
|
||||
* 묶음이 같은 노선 · 관으로 다시 돌면(입력 지문 같음) 그대로 둠 — 떠 있는 답을 받음.
|
||||
* - 서버가 「낡음」으로 답하면(노선 [저장] 뒤 등) 같은 흐림 + [업데이트] · 자동이면 3 s 뒤.
|
||||
@@ -125,7 +126,8 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
header.append(gear);
|
||||
|
||||
const paint = (): void => {
|
||||
deps.root.classList.toggle("is-stale", stale);
|
||||
deps.root.classList.toggle("is-stale", stale && mode === "manual");
|
||||
suggest.setStale(stale);
|
||||
veil.hidden = !stale || mode === "auto" || inflight !== null;
|
||||
busy.root.hidden = inflight === null;
|
||||
};
|
||||
|
||||
@@ -35,6 +35,8 @@ export function suggestionText(item: Suggestion): string {
|
||||
export interface DrainageSuggest {
|
||||
root: HTMLElement;
|
||||
update(response: DetailBasinResponse): void;
|
||||
/** 노선 · 관이 바뀌어 낡음 — [적용] 끔(옛 노선 기준 제안이 새 노선에 안 들어가게). */
|
||||
setStale(stale: boolean): void;
|
||||
/** 관 자리 후보 누가거리 — 배수유역도 점선 원. */
|
||||
candidates(): number[];
|
||||
}
|
||||
@@ -53,8 +55,9 @@ export function createDrainageSuggest(
|
||||
apply.type = "button";
|
||||
apply.className = "b05-drainage__analyze";
|
||||
apply.textContent = "적용";
|
||||
apply.title =
|
||||
const applyTitle =
|
||||
"제안을 관 초안에 한 번에 얹습니다(관 더함 · 관경 · 시설 바꿈) — [저장] 때 남습니다.";
|
||||
apply.title = applyTitle;
|
||||
head.append(title, apply);
|
||||
const list = document.createElement("ul");
|
||||
list.className = "b05-drainage__suggest-list";
|
||||
@@ -62,7 +65,8 @@ export function createDrainageSuggest(
|
||||
let suggestions: Suggestion[] = [];
|
||||
|
||||
apply.addEventListener("click", () => {
|
||||
if (suggestions.length) applyPoints(applySuggestions(pipeRequest(), suggestions));
|
||||
if (suggestions.length && !apply.disabled)
|
||||
applyPoints(applySuggestions(pipeRequest(), suggestions));
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -81,6 +85,10 @@ export function createDrainageSuggest(
|
||||
}),
|
||||
);
|
||||
},
|
||||
setStale(stale) {
|
||||
apply.disabled = stale;
|
||||
apply.title = stale ? "노선이 바뀌어 제안이 낡음 — 새 계산 뒤 적용" : applyTitle;
|
||||
},
|
||||
candidates: () =>
|
||||
suggestions.filter((item) => item.kind === "position").map((item) => item.chainageM),
|
||||
};
|
||||
|
||||
@@ -177,6 +177,8 @@ async function main() {
|
||||
veil: !veil.hidden,
|
||||
busy: !busy.hidden,
|
||||
fetches: calls.length,
|
||||
applyOff: live.suggest.root.children[0].children[1].disabled,
|
||||
applyTitle: live.suggest.root.children[0].children[1].title,
|
||||
});
|
||||
const draft = (rev, x) => ({
|
||||
rev,
|
||||
@@ -227,6 +229,10 @@ async function main() {
|
||||
update.fire("click");
|
||||
stage(draft(5, 90));
|
||||
out.movedWhileBusy = { ...look(), aborted: calls[1].aborted };
|
||||
// 낡은 동안 제안 [적용] — 끔 · 눌러도 옛 제안이 안 들어감
|
||||
live.suggest.root.children[0].children[1].fire("click");
|
||||
await flush();
|
||||
out.staleApply = detailCalls.length;
|
||||
update.fire("click");
|
||||
answer(calls[2]);
|
||||
await flush();
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
(입력 지문) · 답이 오면 흐림 · 「계산 중」 사라짐 · 제안 「제안 N건」 · [적용].
|
||||
- 요청 중 또 옮기면 앞 요청 끊고 다시 흐림.
|
||||
- 노선 [저장] 뒤 서버 답이 「낡음」 = 수동은 흐림 + [업데이트] · 자동은 3 s 뒤 조용히.
|
||||
- ⚙ 자동: 3 s 조용한 뒤 셈 · 그 사이 또 옮기면 처음부터 · [업데이트] 막 없음.
|
||||
- ⚙ 자동: 흐림 없음 · 3 s 조용한 뒤 셈 · 그 사이 또 옮기면 처음부터 · [업데이트] 막 없음(349-0e).
|
||||
- 낡은 동안 제안 [적용] 끔(두 모드) · 새 답이 오면 다시 켬(349-0e).
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -30,17 +31,31 @@ def r():
|
||||
return json.loads(proc.stdout)
|
||||
|
||||
|
||||
def state(stale, veil, busy, fetches):
|
||||
return {"stale": stale, "blur": stale, "veil": veil, "busy": busy, "fetches": fetches}
|
||||
STALE_TITLE = "노선이 바뀌어 제안이 낡음 — 새 계산 뒤 적용"
|
||||
|
||||
|
||||
def state(stale, veil, busy, fetches, auto=False):
|
||||
return {
|
||||
"stale": stale,
|
||||
"blur": stale and not auto, # 자동 = 흐림 없음
|
||||
"veil": veil,
|
||||
"busy": busy,
|
||||
"fetches": fetches,
|
||||
"applyOff": stale,
|
||||
}
|
||||
|
||||
|
||||
def look(row):
|
||||
return {k: v for k, v in row.items() if k != "applyTitle"}
|
||||
|
||||
|
||||
def test_manual_update_answer_lands_after_same_rerun(r):
|
||||
assert r["moved"] == state(True, True, False, 0)
|
||||
assert r["sameAgain"] == state(True, True, False, 0) # 판 번호만 바뀜 — 다시 안 흐림
|
||||
assert look(r["moved"]) == state(True, True, False, 0)
|
||||
assert look(r["sameAgain"]) == state(True, True, False, 0) # 판 번호만 바뀜 — 다시 안 흐림
|
||||
assert r["sent"]["fetches"] == 1 and r["sent"]["busy"] and not r["sent"]["veil"]
|
||||
rerun = r["rerunWhileBusy"]
|
||||
assert not rerun["aborted"] and rerun["busy"] # 같은 입력 다시 돎 — 요청 그대로
|
||||
assert r["answered"] == {**state(False, False, False, 1), "applied": 1}
|
||||
assert look(r["answered"]) == {**state(False, False, False, 1), "applied": 1}
|
||||
|
||||
|
||||
def test_suggestions_and_apply(r):
|
||||
@@ -55,29 +70,41 @@ def test_suggestions_and_apply(r):
|
||||
def test_move_while_busy_cancels(r):
|
||||
moved = r["movedWhileBusy"]
|
||||
assert moved["aborted"] and moved["veil"] and not moved["busy"]
|
||||
assert r["fresh"] == state(False, False, False, 3)
|
||||
assert look(r["fresh"]) == state(False, False, False, 3)
|
||||
|
||||
|
||||
def test_stale_blocks_apply(r):
|
||||
assert r["movedWhileBusy"]["applyOff"]
|
||||
assert r["movedWhileBusy"]["applyTitle"] == STALE_TITLE
|
||||
assert r["staleApply"] == 1 # 눌러도 옛 제안이 안 들어감(앞 [적용] 한 번뿐)
|
||||
assert not r["fresh"]["applyOff"] and r["fresh"]["applyTitle"] != STALE_TITLE
|
||||
|
||||
|
||||
def test_saved_stale_shows_update(r):
|
||||
assert {k: v for k, v in r["savedStale"].items() if k != "status"} == state(
|
||||
assert {k: v for k, v in look(r["savedStale"]).items() if k != "status"} == state(
|
||||
True, True, False, 3
|
||||
)
|
||||
assert r["savedStale"]["status"] == "배수유역 낡음"
|
||||
|
||||
|
||||
def test_auto_mode(r):
|
||||
assert r["autoSwitch"] == state(True, False, False, 3) # 자동 — 흐림만 · [업데이트] 없음
|
||||
assert look(r["autoSwitch"]) == state(
|
||||
True, False, False, 3, True
|
||||
) # 자동 — 흐림 없음 · [업데이트] 없음
|
||||
assert r["auto2999"]["fetches"] == 3
|
||||
assert r["auto3000"]["fetches"] == 4 and r["auto3000"]["route"] is None
|
||||
assert r["autoDone"] == state(False, False, False, 4)
|
||||
assert r["autoRestart"] == state(True, False, False, 4) # 2 s + 2 s — 처음부터 다시 3 s
|
||||
assert r["autoMoved"] == {**state(False, False, False, 5), "mode": "auto"}
|
||||
assert r["autoSavedStale"] == state(True, False, False, 5)
|
||||
assert r["autoSavedDone"] == state(False, False, False, 6)
|
||||
assert r["auto3000"]["busy"] and not r["auto3000"]["blur"] # 계산 중 = 구석 표시만
|
||||
assert look(r["autoDone"]) == state(False, False, False, 4)
|
||||
assert look(r["autoRestart"]) == state(
|
||||
True, False, False, 4, True
|
||||
) # 2 s + 2 s — 처음부터 다시 3 s
|
||||
assert look(r["autoMoved"]) == {**state(False, False, False, 5), "mode": "auto"}
|
||||
assert look(r["autoSavedStale"]) == state(True, False, False, 5, True)
|
||||
assert look(r["autoSavedDone"]) == state(False, False, False, 6)
|
||||
|
||||
|
||||
def test_input_key(r):
|
||||
assert r["keys"] == {
|
||||
assert look(r["keys"]) == {
|
||||
"rev": True,
|
||||
"order": True,
|
||||
"node": False,
|
||||
|
||||
Reference in New Issue
Block a user