fix(B05 노선편집): 349 테마 바꿈 뒤 평면 캔버스 다시 그림 — 밝음 ↔ 어두움(단추 · 시스템) 알림에 다음 프레임 다시 그리기 · 바탕 · 등고선 · 노선 · 발자국 색 토큰 다시 읽음

- _RouteEdit_Render.watchTheme(data-theme · prefers-color-scheme · 공용 색 캐시가 먼저 비워지게 다음 프레임) · 편집기가 붙이고 떠날 때 뗌
- 시험: test_349_routeedit_theme_redraw 3 · 관련 37 통과 · typecheck 0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Se7ytqbohmBQnfYpwYFnYf
This commit is contained in:
2026-10-16 14:09:13 +09:00
co-authored by Claude Opus 5.5
parent 031b5be1d4
commit 337edb3f20
3 changed files with 68 additions and 1 deletions
+3 -1
View File
@@ -42,7 +42,7 @@ import {
type EditedCurve,
type EditedNode,
} from "./logic/B05_Profile_UI_RouteEdit_Curve";
import { drawRouteEditScene, polylineLengthM } from "./B05_Profile_UI_RouteEdit_Render";
import { drawRouteEditScene, polylineLengthM, watchTheme } from "./B05_Profile_UI_RouteEdit_Render";
import { bindNodeSplice, bindRouteEditNavigation } from "./B05_Profile_UI_RouteEdit_Input";
import { bindRouteEditPointer } from "./B05_Profile_UI_RouteEdit_Pointer";
import { createCrossPreview } from "./B05_Profile_UI_RouteEdit_Cross";
@@ -271,6 +271,7 @@ export async function mountRouteEditor(
viewMemory.flush();
closed = true;
sizeWatch.disconnect();
themeWatch();
historyControls.dispose(); // 단축키는 창(window)에 달려 있어 안 떼면 떠난 뒤에도 산다.
curveLabelBox.destroy();
overlay.remove();
@@ -305,6 +306,7 @@ export async function mountRouteEditor(
// 칸 크기를 따라감 — 창 크기 · 아래 종단 판 끌기 · 좌측 접기 모두(345-3).
const sizeWatch = new ResizeObserver(() => resize());
sizeWatch.observe(overlay.querySelector<HTMLElement>(".b05-routeedit__canvas-wrap")!);
const themeWatch = watchTheme(() => draw()); // 밝음 · 어두움 바꿈 — 색 다시 읽어 그림(349).
let viewWanted = false; // 자료를 읽었다 — 칸 크기가 서면 보던 화면(18 ③) · 없으면 노선 전체로.
function openView(): void {
if (!viewWanted || !meta || view.width <= 0) return;
@@ -131,6 +131,26 @@ function upright(
context.restore();
}
/** 테마 바꿈(`data-theme` · 시스템 밝음/어두움)마다 부름(349) — 그리기가 그릴 때마다 토큰을
* 다시 읽으므로 다시 그리기만 하면 바탕 · 등고선 · 노선 · 발자국 색이 바뀜. 돌려준 함수로 뗌. */
export function watchTheme(onChange: () => void): () => void {
// 다음 프레임에 — 공용 색 캐시(`themeColor`)가 같은 알림에서 먼저 비워지게.
let frame = 0;
const later = (): void => {
cancelAnimationFrame(frame);
frame = requestAnimationFrame(onChange);
};
const observer = new MutationObserver(later);
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
const system = window.matchMedia("(prefers-color-scheme: dark)");
system.addEventListener("change", later);
return () => {
cancelAnimationFrame(frame);
observer.disconnect();
system.removeEventListener("change", later);
};
}
export function drawRouteEditScene(context: CanvasRenderingContext2D, scene: RouteEditScene): void {
const { view, toScreen } = scene;
const style = getComputedStyle(document.documentElement);