feat(381-1 B05 평면): 되돌리기 · 다시하기 아이콘 곧은 꼬리 + 갈고리 · 절/성토선 테마 색 · 굵게

- 평면 도구줄 · 종단 도구줄 ↶ ↷ → HISTORY_ICON SVG(↩ ↪ 꼴 · 회전 반 바퀴 그림과 구분)
- 절토 #e11d48/#ff6b6b · 성토 #0d9488/#2dd4bf · 도로 끝 1.8 · 비탈 끝 2.6 px
- 시험 chrome_modal · 346_2 고쳐 씀 · 282 가짜 require 에 HISTORY_ICON

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
This commit is contained in:
2026-10-17 14:41:24 +09:00
co-authored by Claude Opus 5.5
parent 7329065c3b
commit e9d30cdeac
7 changed files with 48 additions and 22 deletions
@@ -16,6 +16,7 @@
import type { StraightRun } from "./logic/B05_Profile_UI_Profile_Straighten";
import { sameRun } from "./logic/B05_Profile_UI_Profile_Straighten";
import { HISTORY_ICON } from "./B05_Profile_UI_RouteEdit_Chrome";
import type { FreeChartGeom } from "../A00_Common/design/design_profile_free_view";
export type ProfileToolMode = "none" | "straighten" | "shift" | "free";
@@ -174,6 +175,11 @@ export function createProfileTools(callbacks: ProfileToolsCallbacks): ProfileToo
undo.disabled = !callbacks.canUndo();
const redo = toolButton("↷", "다시하기 (Ctrl+Shift+Z)", callbacks.onRedo);
redo.disabled = !callbacks.canRedo();
// 평면 편집과 같은 그림(요구 241-9) — 글은 aria-label 로.
undo.innerHTML = HISTORY_ICON.undo;
undo.setAttribute("aria-label", "되돌리기");
redo.innerHTML = HISTORY_ICON.redo;
redo.setAttribute("aria-label", "다시하기");
const straighten = toolButton(
"직선화",
+14 -3
View File
@@ -31,6 +31,17 @@ export const HALF_TURN_ICON = {
<path d="M3 8a5 5 0 0 1 10 0" /><path d="M13 8 14.8 5.6" /><path d="M13 8 10.6 6.6" /></svg>`,
};
/** 되돌리기 · 다시하기 아이콘 — **곧은 꼬리 + 갈고리**(↩ ↪ · 요구 241-9). 둥근 화살표(`↶`·`↷`)는
* 회전 단추와 헷갈렸다. 종단 도구줄도 같은 그림. */
export const HISTORY_ICON = {
undo: `<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" fill="none"
stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 6h7.5a3.5 3.5 0 0 1 0 7H7" /><path d="M6 3 3 6l3 3" /></svg>`,
redo: `<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" fill="none"
stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
<path d="M13 6H5.5a3.5 3.5 0 0 0 0 7H9" /><path d="M10 3l3 3-3 3" /></svg>`,
};
export interface RouteEditChrome {
overlay: HTMLElement;
canvas: HTMLCanvasElement;
@@ -65,9 +76,9 @@ export function createRouteEditChrome(
data-act="hint-toggle" aria-expanded="false" title="조작 도움말 펼치기 · 접기">?</button>
<i class="b05-route__view-separator" aria-hidden="true"></i>
<button type="button" class="ui-btn ui-btn--glass" data-act="undo"
title="되돌리기 (Ctrl+Z)" aria-label="되돌리기" disabled>↶</button>
title="되돌리기 (Ctrl+Z)" aria-label="되돌리기" disabled>${HISTORY_ICON.undo}</button>
<button type="button" class="ui-btn ui-btn--glass" data-act="redo"
title="다시하기 (Ctrl+Y)" aria-label="다시하기" disabled>↷</button>
title="다시하기 (Ctrl+Y)" aria-label="다시하기" disabled>${HISTORY_ICON.redo}</button>
<i class="b05-route__view-separator" aria-hidden="true"></i>
<span data-slot="tools"></span>
<i class="b05-route__view-separator" aria-hidden="true"></i>
@@ -79,7 +90,7 @@ export function createRouteEditChrome(
aria-pressed="false" disabled
title="1m 간격 등고선을 얇고 옅게 겹칩니다 (확대하면 보임)">1m 등고선</button>
<button type="button" class="ui-btn ui-btn--glass" data-act="footprint"
title="도로 끝 · 비탈 끝(깎기 붉음 · 쌓기 파랑)을 평면에 겹칩니다">절/성토선</button>
title="도로 끝 · 비탈 끝(깎기 붉음 · 쌓기 청록)을 평면에 겹칩니다">절/성토선</button>
<button type="button" class="ui-btn ui-btn--glass" data-act="measure"
title="노선 위 두 점을 눌러 거리·기울기를 잽니다 (Shift+클릭도 같음)">거리 재기</button>
<i class="b05-route__view-separator" aria-hidden="true"></i>
@@ -17,14 +17,13 @@ import {
} from "./logic/B05_Profile_Footprint_Strokes";
import "./B05_Profile_UI_Style_RouteEdit_Footprint.css";
/** 갈래 → 색 토큰 · 대체 색. */
const STROKE_STYLE: Record<FootprintStrokeKind, [string, string]> = {
road: ["--b05-footprint-road", "#475569"],
cut: ["--b05-footprint-cut", "#dc2626"],
fill: ["--b05-footprint-fill", "#2563eb"],
slope: ["--b05-footprint-road", "#475569"],
/** 갈래 → 색 토큰 · 대체 색 · 굵기(px · 비탈 끝이 더 굵음 · 요구 241-10). */
const STROKE_STYLE: Record<FootprintStrokeKind, [string, string, number]> = {
road: ["--b05-footprint-road", "#475569", 1.8],
cut: ["--b05-footprint-cut", "#e11d48", 2.6],
fill: ["--b05-footprint-fill", "#0d9488", 2.6],
slope: ["--b05-footprint-road", "#475569", 2.6],
};
const STROKE_WIDTH = 1.4;
let shown = true;
let redraw: (() => void) | null = null;
@@ -77,9 +76,9 @@ export function drawFootprint(
if (!strokes.length) return;
context.save();
context.setLineDash([]);
context.lineWidth = STROKE_WIDTH;
for (const { kind, points } of strokes) {
const [token, fallback] = STROKE_STYLE[kind];
const [token, fallback, width] = STROKE_STYLE[kind];
context.lineWidth = width;
context.strokeStyle = style.getPropertyValue(token).trim() || fallback;
context.beginPath();
points.forEach((vertex, index) => {
@@ -1,20 +1,20 @@
/* 노선 편집 평면 발자국(PLAN 346-2) — 도로 끝 · 비탈 끝 깎기(붉음) · 쌓기(파랑) */
/* 노선 편집 평면 발자국(PLAN 346-2) — 도로 끝 · 비탈 끝 깎기(붉음) · 쌓기(청록) */
:root {
--b05-footprint-road: #475569;
--b05-footprint-cut: #dc2626;
--b05-footprint-fill: #2563eb;
--b05-footprint-cut: #e11d48;
--b05-footprint-fill: #0d9488;
}
:root[data-theme="dark"] {
--b05-footprint-road: #cbd5e1;
--b05-footprint-cut: #f87171;
--b05-footprint-fill: #60a5fa;
--b05-footprint-cut: #ff6b6b;
--b05-footprint-fill: #2dd4bf;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--b05-footprint-road: #cbd5e1;
--b05-footprint-cut: #f87171;
--b05-footprint-fill: #60a5fa;
--b05-footprint-cut: #ff6b6b;
--b05-footprint-fill: #2dd4bf;
}
}
@@ -103,7 +103,7 @@ const popup = {
// ---- 진짜 도구 묶음 ----
const { createProfileTools } = load(read("B05_Profile", "B05_Profile_UI_Profile_Tools.ts"), {
require: () => ({ sameRun: () => false }),
require: () => ({ sameRun: () => false, HISTORY_ICON: { undo: "", redo: "" } }),
document,
});
const tools = createProfileTools({
@@ -111,3 +111,8 @@ def test_편집기가_발자국을_그리고_단추로_켬끔() -> None:
for token in ("--b05-footprint-road", "--b05-footprint-cut", "--b05-footprint-fill"):
assert token in footprint
assert css.count(f"{token}:") == 3 # 밝음 · 어두움 · 시스템 어두움
# 381-1 — 절토 · 성토 테마 색 · 굵게(도로 끝 1.8 · 비탈 끝 2.6)
for color in ("#e11d48", "#ff6b6b", "#0d9488", "#2dd4bf"):
assert color in css
assert '"#e11d48", 2.6]' in footprint and '"#0d9488", 2.6]' in footprint
assert 'road: ["--b05-footprint-road", "#475569", 1.8]' in footprint
@@ -38,8 +38,13 @@ def test_겉틀은_본문_칸():
assert ">저장 전으로</button>" not in chrome and "노선 반영</button>" not in chrome
assert ">취소</button>" not in chrome and ">확인</button>" not in chrome
# 352-2 — 되돌리기 · 다시하기 = 글 없이 기호 + aria-label · title(단축키)
assert 'aria-label="되돌리기" disabled>↶</button>' in chrome
assert 'aria-label="다시하기" disabled>↷</button>' in chrome
# 381-1 — 곧은 꼬리 + 갈고리 SVG(회전 반 바퀴 그림과 헷갈리지 않게)
assert 'aria-label="되돌리기" disabled>${HISTORY_ICON.undo}</button>' in chrome
assert 'aria-label="다시하기" disabled>${HISTORY_ICON.redo}</button>' in chrome
assert ">↶</button>" not in chrome and ">↷</button>" not in chrome
tools = _read("B05_Profile_UI_Profile_Tools.ts")
assert "undo.innerHTML = HISTORY_ICON.undo;" in tools
assert "redo.innerHTML = HISTORY_ICON.redo;" in tools
assert 'title="되돌리기 (Ctrl+Z)"' in chrome and 'title="다시하기 (Ctrl+Y)"' in chrome
# 시험 · 배선이 집는 자리는 남김
assert "b05-routeedit__measure-close" in chrome