Merge remote-tracking branches 'origin/sub_laptop_2' and 'origin/sub_laptop_4' into sub_laptop_8

This commit is contained in:
2026-10-26 22:52:31 +09:00
36 changed files with 197 additions and 308 deletions
+1 -8
View File
@@ -55,16 +55,9 @@
display: none;
}
.b05-route-profile__tools > .b05-route-profile__popout {
padding: var(--btn-bar-padding);
font-size: var(--btn-bar-font-size);
}
/* 유토곡선 · 배수유역 판 [창 분할](계획서 325-3) — 종단 [창 분할]과 같은 양식 · 유토는 판 좌상단에 띄움. */
/* 유토곡선 · 배수유역 판 [창 분할](계획서 325-3) — 종단 [창 분할]과 같은 양식(부품 단추 단 18 · 553-2) · 유토는 판 좌상단에 띄움. */
.b05-subpanel__popout {
flex: none;
padding: var(--btn-bar-padding);
font-size: var(--btn-bar-font-size);
}
.b05-profile__masshaul-overlay > .b05-subpanel__popout {
@@ -123,6 +123,7 @@ export function attachProfilePopout(
button: HTMLButtonElement,
): void {
button.className = "ui-btn ui-btn--ghost b05-route-profile__popout";
button.setAttribute("data-size", "18"); // 부품 단추 단 18(553-2) — 옆 도구 단추와 같은 키
button.title = "종단 하단 패널을 별도 창으로 분할 · 다시 누르면 결합";
attachPopout(profile, {
piece: profile.root,
@@ -139,6 +140,7 @@ export function attachSubPanelPopouts(profile: DesignProfile): void {
const subButton = (title: string): HTMLButtonElement => {
const button = document.createElement("button");
button.className = "ui-btn ui-btn--ghost b05-subpanel__popout";
button.setAttribute("data-size", "18");
button.title = title;
return button;
};
+2 -2
View File
@@ -283,7 +283,7 @@ export function createSpecForm(links: SpecLinks | null, hooks: SpecFormHooks = {
if (!c.required && hooks.count?.(c) === undefined) {
const remove = document.createElement("button");
remove.type = "button";
remove.className = "b05-route__irregular-btn";
remove.className = "ui-btn ui-btn--danger-ghost b05-route__irregular-btn";
remove.textContent = "삭제";
remove.title = `${c.title} 상자 삭제`;
remove.dataset.specRemove = c.id;
@@ -333,7 +333,7 @@ export function createSpecForm(links: SpecLinks | null, hooks: SpecFormHooks = {
if (c.required || (c.max && list.length >= c.max) || hooks.count?.(c) !== undefined) continue;
const add = document.createElement("button");
add.type = "button";
add.className = "b05-route__irregular-btn b05-structure__spec-add";
add.className = "ui-btn ui-btn--ghost b05-route__irregular-btn b05-structure__spec-add";
add.textContent = `+ ${containerTitle(c, names, groupNames[c.link])}`;
add.title = "추가";
add.dataset.specAdd = c.id;
@@ -72,11 +72,15 @@ export function createDrainageChrome(
const updateButton = document.createElement("button");
updateButton.type = "button";
updateButton.className = "b05-drainage__analyze b05-drainage__update";
// 머리 줄 단추 = 부품 「단추」 보조 · 단 22(553-2)
updateButton.className = "ui-btn ui-btn--ghost b05-drainage__analyze b05-drainage__update";
updateButton.setAttribute("data-size", "22");
updateButton.textContent = "업데이트";
const proposeButton = document.createElement("button");
proposeButton.type = "button";
proposeButton.className = "b05-drainage__analyze b05-drainage__tool b05-drainage__propose";
proposeButton.className =
"ui-btn ui-btn--ghost b05-drainage__analyze b05-drainage__tool b05-drainage__propose";
proposeButton.setAttribute("data-size", "22");
proposeButton.textContent = ui_locales.B05_Drainage_Btn_Propose[currentLanguageIndex];
proposeButton.title = ui_locales.B05_Drainage_Btn_Propose_Tip[currentLanguageIndex];
header.append(updateButton, proposeButton);
@@ -121,9 +121,12 @@ export function stepper(input: HTMLInputElement, stepM: number, decimals = 1): H
const button = (label: string, delta: number): HTMLButtonElement => {
const element = document.createElement("button");
element.type = "button";
element.className = "b05-structure__step";
// 부품 「아이콘 단추」 단 24(553-2) · 폭은 덩어리 몫(28)
element.className = "ui-icon-btn ui-icon-btn--ghost b05-structure__step";
element.setAttribute("data-size", "24");
element.textContent = label;
element.title = `${stepM}m ${delta > 0 ? "늘리기" : "줄이기"}`;
element.setAttribute("aria-label", element.title);
element.addEventListener("click", nudge(delta));
return element;
};
+22 -15
View File
@@ -10,6 +10,8 @@ import { stateKey } from "../A00_Common/b_page_state";
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
import { themeColor } from "@ui/ui_template_palette";
import { showToast } from "@ui/ui_template_elements";
import { createToggle } from "@ui/ui_template_toggle";
import { setListRowActive } from "@ui/ui_template_list_row";
import {
DRAINAGE_SHEET_LAYERS,
fetchCachedSheetLayer,
@@ -92,7 +94,7 @@ export function formatArea(areaM2: number): string {
return areaM2 >= 10000 ? `${(areaM2 / 10000).toFixed(2)}ha` : `${Math.round(areaM2)}㎡`;
}
/** 제목 우측 표시 토글 — 등고선·세류·흐름 화살표·상류 세류가 모두 같은 양식을 쓴다. */
/** 제목 우측 표시 토글 — 등고선·세류·흐름 화살표·상류 세류가 모두 같은 양식(부품 「켜기」 칩 · 단 20 · 색 점 = 층 색 · 553-2). */
export function addLayerToggle(
container: HTMLElement,
label: string,
@@ -101,20 +103,20 @@ export function addLayerToggle(
onToggle: (next: boolean) => void,
title?: string,
): HTMLButtonElement {
const button = document.createElement("button");
button.type = "button";
button.className = "b05-drainage__layer-button" + (initial ? " is-active" : "");
button.textContent = label;
button.style.setProperty("--b05-layer-color", color);
button.setAttribute("aria-pressed", String(initial));
const button = createToggle({
kind: "chip",
label,
checked: initial,
dot: color,
size: 20,
onChange: (active) => {
button.classList.toggle("is-active", active);
onToggle(active);
},
}).root;
button.classList.add("b05-drainage__layer-button");
button.classList.toggle("is-active", initial);
if (title) button.title = title;
let active = initial;
button.addEventListener("click", () => {
active = !active;
button.classList.toggle("is-active", active);
button.setAttribute("aria-pressed", String(active));
onToggle(active);
});
container.append(button);
return button;
}
@@ -140,7 +142,12 @@ export function renderBasinRows(
basins.forEach((basin) => {
const row = document.createElement("button");
row.type = "button";
row.className = "b05-drainage__basin" + (selected === basin.index ? " is-selected" : "");
// 목록 줄 = 부품 「목록 줄」 단 32(553-2) · 옛 이름(is-selected)은 자리 몫
const picked = selected === basin.index;
row.className =
"ui-list-row ui-sidebar-row b05-drainage__basin" + (picked ? " is-selected" : "");
row.setAttribute("data-size", "32");
setListRowActive(row, picked);
const badge = document.createElement("span");
badge.className = "b05-drainage__basin-index";
badge.textContent = String(basin.index);
@@ -58,12 +58,14 @@ export function createDrainagePropose(deps: {
const title = document.createElement("span");
const add = document.createElement("button");
add.type = "button";
add.className = "b05-drainage__analyze";
add.className = "ui-btn ui-btn--filled b05-drainage__analyze";
add.setAttribute("data-size", "22");
add.textContent = "넣기";
add.title = "고른 후보만 관으로 넣습니다 — 초안이라 [저장] 때 남습니다.";
const shut = document.createElement("button");
shut.type = "button";
shut.className = "b05-drainage__analyze";
shut.className = "ui-btn ui-btn--ghost b05-drainage__analyze";
shut.setAttribute("data-size", "22");
shut.textContent = "닫기";
head.append(title, add, shut);
const list = document.createElement("ul");
@@ -51,7 +51,8 @@ export function createDrainageSuggest(
const title = document.createElement("span");
const apply = document.createElement("button");
apply.type = "button";
apply.className = "b05-drainage__analyze";
apply.className = "ui-btn ui-btn--filled b05-drainage__analyze";
apply.setAttribute("data-size", "22");
apply.textContent = "적용";
const applyTitle =
"관경 · 시설 제안을 관 초안에 한 번에 얹습니다 — [저장] 때 남습니다(관 자리는 [자동 제안]).";
@@ -233,14 +233,14 @@ export function createIrregularStationsSection(
const primary = document.createElement("button");
primary.type = "button";
primary.className = "b05-route__irregular-btn is-primary";
primary.className = "ui-btn ui-btn--filled b05-route__irregular-btn is-primary";
const remove = document.createElement("button");
remove.type = "button";
remove.className = "b05-route__irregular-btn is-danger";
remove.className = "ui-btn ui-btn--danger-ghost b05-route__irregular-btn is-danger";
remove.textContent = "삭제";
const reset = document.createElement("button");
reset.type = "button";
reset.className = "b05-route__irregular-btn";
reset.className = "ui-btn ui-btn--ghost b05-route__irregular-btn";
reset.textContent = "리셋";
const actions = document.createElement("div");
actions.className = "b05-route__irregular-actions";
+4
View File
@@ -109,6 +109,10 @@ export function createViewControls(callbacks: ViewControlCallbacks): {
separator3,
corridorRefresh,
);
// 보기 줄 단추 = 부품 단추 단 32(553-2)
viewControls
.querySelectorAll<HTMLElement>(".ui-btn")
.forEach((b) => b.setAttribute("data-size", "32"));
return {
viewControls,
+5 -2
View File
@@ -23,7 +23,7 @@ import { viewOf } from "../common_util/common_util_view_window";
/** 버튼 폭(21px)에 여유를 더한 값 — 이보다 가까우면 겹친 것으로 본다. */
const BUTTON_CLEARANCE_PX = 23;
const BUTTON_HALF_PX = 10;
const BUTTON_HALF_PX = 9; // 부품 아이콘 단추 단 18 의 반(553-2)
/** 길게 누르기: 이만큼 유지하면 반복이 시작되고, 그 뒤 초당 10회(0.1m씩)로 이어진다. */
export const HOLD_DELAY_MS = 500;
export const HOLD_INTERVAL_MS = 100;
@@ -286,8 +286,11 @@ function overlayButton(
): HTMLButtonElement {
const button = document.createElement("button");
button.type = "button";
button.className = `b05-profile-edit__btn ${className}`;
// 부품 「아이콘 단추」 선 꼴 · 단 18(553-2) · 글자 기호 그대로
button.className = `ui-icon-btn ui-icon-btn--outline b05-profile-edit__btn ${className}`;
button.setAttribute("data-size", "18");
button.textContent = glyph;
button.setAttribute("aria-label", title);
button.title = `${title}\n(길게 누르면 0.5초 뒤부터 연속 조정)`;
// 포인터로 누르면 즉시 1회 반응하고, 이어지는 click은 중복이므로 삼킨다.
let swallowClick = false;
+3 -1
View File
@@ -73,10 +73,12 @@ export interface ProfileTools {
mountOverlay: (geom: FreeChartGeom) => void;
}
/** 도구 단추 = 부품 「단추」 유리 · 단 18(553-2 · 클래스로 — 시험이 별칭 없이 이 파일을 돌림) */
function toolButton(label: string, title: string, onClick: () => void): HTMLButtonElement {
const button = document.createElement("button");
button.type = "button";
button.className = "b05-route-profile__tool";
button.className = "ui-btn ui-btn--glass b05-route-profile__tool";
button.setAttribute("data-size", "18");
button.textContent = label;
button.title = title;
button.addEventListener("click", (event) => {
+4 -1
View File
@@ -45,9 +45,12 @@ export function createProfileZoom(onChange: () => void): ProfileZoom {
function add(label: string, title: string, action: () => void): HTMLButtonElement {
const button = document.createElement("button");
button.type = "button";
button.className = "b05-profile__zoom-btn";
// 부품 「아이콘 단추」 단 22(553-2) · 글자 기호 그대로
button.className = "ui-icon-btn ui-icon-btn--ghost b05-profile__zoom-btn";
button.setAttribute("data-size", "22");
button.textContent = label;
button.title = title;
button.setAttribute("aria-label", title);
button.addEventListener("click", (event) => {
// 카드·측점 선택으로 번지면 그래프를 다시 그리며 방금 맞춘 배율이 날아간다.
event.stopPropagation();
+19 -18
View File
@@ -1,4 +1,4 @@
import { createButton } from "@ui/ui_template_elements";
import { createIconButton } from "@ui/ui_template_elements";
import { createSidePanels } from "@ui/ui_template_side_panels";
import { bindFootprintToggle } from "./B05_Profile_UI_RouteEdit_Footprint";
import { bindMarkToggle } from "./B05_Profile_UI_RouteEdit_Marks";
@@ -80,38 +80,38 @@ export function createRouteEditChrome(
<div class="b05-routeedit__canvas-wrap" aria-label="계획노선 편집">
<canvas class="b05-routeedit__canvas"></canvas>
<div class="b05-route__view-controls b05-routeedit__tools">
<button type="button" class="ui-btn ui-btn--glass b05-routeedit__hint-toggle"
<button type="button" data-size="32" class="ui-btn ui-btn--glass b05-routeedit__hint-toggle"
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"
<button type="button" data-size="32" class="ui-btn ui-btn--glass" data-act="undo"
title="되돌리기 (Ctrl+Z)" aria-label="되돌리기" disabled>${HISTORY_ICON.undo}</button>
<button type="button" class="ui-btn ui-btn--glass" data-act="redo"
<button type="button" data-size="32" class="ui-btn ui-btn--glass" data-act="redo"
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>
<button type="button" class="ui-btn ui-btn--glass" data-act="rotate-ccw"
<button type="button" data-size="32" class="ui-btn ui-btn--glass" data-act="rotate-ccw"
title="반시계로 돌리기" aria-label="반시계로 돌리기">${HALF_TURN_ICON.ccw}</button>
<button type="button" class="ui-btn ui-btn--glass" data-act="rotate-cw"
<button type="button" data-size="32" class="ui-btn ui-btn--glass" data-act="rotate-cw"
title="시계로 돌리기" aria-label="시계로 돌리기">${HALF_TURN_ICON.cw}</button>
<button type="button" class="ui-btn ui-btn--glass" data-act="contour-minor"
<button type="button" data-size="32" class="ui-btn ui-btn--glass" data-act="contour-minor"
aria-pressed="false" disabled
title="1m 간격 등고선을 얇고 옅게 겹칩니다 (확대하면 보임)">1m 등고선</button>
<button type="button" class="ui-btn ui-btn--glass" data-act="stations"
<button type="button" data-size="32" class="ui-btn ui-btn--glass" data-act="stations"
title="측점 눈금 · 번호를 평면에 보이기 · 숨기기 (시점 · 종점 이름표는 늘 보임)">측점</button>
<button type="button" class="ui-btn ui-btn--glass" data-act="slopes"
<button type="button" data-size="32" class="ui-btn ui-btn--glass" data-act="slopes"
title="측점 사이 기울기(%)를 평면에 보이기 · 숨기기 (확대해야 보임)">기울기</button>
<button type="button" class="ui-btn ui-btn--glass" data-act="footprint"
<button type="button" data-size="32" class="ui-btn ui-btn--glass" data-act="footprint"
title="도로 끝 · 비탈 끝(깎기 붉음 · 쌓기 청록)을 평면에 겹칩니다">절/성토선</button>
<button type="button" class="ui-btn ui-btn--glass" data-act="measure"
<button type="button" data-size="32" class="ui-btn ui-btn--glass" data-act="measure"
title="노선 위 두 점을 눌러 거리·기울기를 잽니다 (Shift+클릭도 같음)">거리 재기</button>
<i class="b05-route__view-separator" aria-hidden="true"></i>
<button type="button" class="ui-btn ui-btn--glass" data-act="station-snap"
<button type="button" data-size="32" class="ui-btn ui-btn--glass" data-act="station-snap"
aria-pressed="false"
title="켜면 고친 곡선의 가운데(MC)를 고른 간격 배수에 맞춥니다 — 일반측점(20 m)은 그대로 · MC 가 일반측점 위가 아니면 「No.N+10 (MC)」 특별측점을 더함 · 손대지 않은 곡선은 그 자리 유지 · 고정 교각점은 그대로 · 못 맞춘 곡선은 상태줄 「측점 밖 곡선」 · [저장] 때 저장">측점 스냅</button>
<label class="b05-routeedit__terrain b05-routeedit__snap-step"
title="측점 스냅 간격 — 곡선 가운데(MC)를 이 간격 배수에 맞춥니다(측점 No. = 측점 간격)">간격
<select class="ui-select ui-select--sm" data-act="snap-step">
<select class="ui-select ui-select--sm" data-size="32" data-act="snap-step">
<option value="10">10 m</option>
<option value="station">측점 No.</option>
</select>
@@ -140,7 +140,7 @@ export function createRouteEditChrome(
<span><i class="is-planned"></i> 계획노선</span>
</span>
<span class="b05-routeedit__summary"></span>
<button type="button" class="b05-routeedit__info-toggle" data-act="info-toggle"
<button type="button" data-size="18" class="ui-icon-btn ui-icon-btn--ghost b05-routeedit__info-toggle" data-act="info-toggle"
aria-expanded="true" title="상태 · 범례 글판 접기 · 펴기">▾</button>
</div>
</div>
@@ -148,7 +148,10 @@ export function createRouteEditChrome(
<aside class="b05-routeedit__side-wrap"></aside>`;
parent.append(overlay);
// 부른 쪽 단추([3D])도 같은 유리 단추로 — 지도 위에 떠 있는 도구줄.
tools.forEach((tool) => tool.classList.replace("ui-btn--ghost", "ui-btn--glass"));
tools.forEach((tool) => {
tool.classList.replace("ui-btn--ghost", "ui-btn--glass");
tool.setAttribute("data-size", "32"); // 도구줄 단 32(553-2)
});
overlay.querySelector('[data-slot="tools"]')!.replaceWith(...tools);
// 횡단 미리보기 = 패널 양식(382-1 · 손잡이 · 머리 · 폭 끌기 · 열림 · 폭 기억) · 자리는 지도 위 덮개 그대로 ·
@@ -223,10 +226,8 @@ export function createRouteEditChrome(
lift.observe(info);
lift.observe(overlay.querySelector(".b05-routeedit__side-wrap")!);
const measureCloseButton = createButton({ label: "✕", variant: "ghost" });
const measureCloseButton = createIconButton({ glyph: "✕", title: "닫기", size: 24 });
measureCloseButton.classList.add("b05-routeedit__measure-close");
measureCloseButton.setAttribute("aria-label", "닫기");
measureCloseButton.title = "닫기";
overlay.querySelector('[data-slot="measure-close"]')!.replaceWith(measureCloseButton);
const canvas = overlay.querySelector<HTMLCanvasElement>(".b05-routeedit__canvas")!;
@@ -31,9 +31,9 @@ const FIT_MARGIN_M = 30;
export const RECOMMEND_MAP_HTML = `
<canvas class="b05-routerec__map"></canvas>
<div class="b05-routeedit__spin">
<button type="button" class="ui-btn ui-btn--glass" data-act="rotate-ccw"
<button type="button" data-size="32" class="ui-btn ui-btn--glass" data-act="rotate-ccw"
title="반시계로 돌리기" aria-label="반시계로 돌리기">${HALF_TURN_ICON.ccw}</button>
<button type="button" class="ui-btn ui-btn--glass" data-act="rotate-cw"
<button type="button" data-size="32" class="ui-btn ui-btn--glass" data-act="rotate-cw"
title="시계로 돌리기" aria-label="시계로 돌리기">${HALF_TURN_ICON.cw}</button>
</div>`;
@@ -123,14 +123,14 @@ export function buildStructuresForm(options: {
const primary = document.createElement("button");
primary.type = "button";
primary.className = "b05-route__irregular-btn is-primary";
primary.className = "ui-btn ui-btn--filled b05-route__irregular-btn is-primary";
const removeButton = document.createElement("button");
removeButton.type = "button";
removeButton.className = "b05-route__irregular-btn is-danger";
removeButton.className = "ui-btn ui-btn--danger-ghost b05-route__irregular-btn is-danger";
removeButton.textContent = "삭제";
const resetButton = document.createElement("button");
resetButton.type = "button";
resetButton.className = "b05-route__irregular-btn";
resetButton.className = "ui-btn ui-btn--ghost b05-route__irregular-btn";
resetButton.textContent = "리셋";
const actions = document.createElement("div");
actions.className = "b05-route__irregular-actions";
@@ -249,7 +249,7 @@ export function renderStructureList(params: StructureListParams): void {
clear.className = "b05-route__irregular-clear";
const button = document.createElement("button");
button.type = "button";
button.className = "b05-route__irregular-btn is-danger";
button.className = "ui-btn ui-btn--danger-ghost b05-route__irregular-btn is-danger";
button.textContent = "구조물 전체 삭제";
button.addEventListener("click", params.onClearAll);
clear.append(button);
@@ -40,7 +40,7 @@ export function buildSummaryRow(): SummaryRow {
name.dataset.b05Summary = "pick";
const clear = document.createElement("button");
clear.type = "button";
clear.className = "b05-route__irregular-btn";
clear.className = "ui-btn ui-btn--ghost b05-route__irregular-btn";
clear.textContent = "해제";
clear.dataset.b05Summary = "clear";
const line = document.createElement("div");
@@ -69,7 +69,7 @@ export function createWideningBox(hooks: {
const on = !isRunOff(run, off);
const button = document.createElement("button");
button.type = "button";
button.className = `b05-route__irregular-btn${on ? " is-on" : ""}`;
button.className = `ui-btn ui-btn--ghost b05-route__irregular-btn${on ? " is-on" : ""}`;
button.setAttribute("aria-pressed", String(on));
button.dataset.wideningRun = `${run.start_m}-${run.end_m}`;
const from = stationLabel(run.start_m, step);
+12 -48
View File
@@ -197,10 +197,10 @@ html.is-bm-route .b05-route__view-controls {
}
/* 방위 나침반을 놓을 자리 — ISO 버튼 바로 아래(2026-09-03 사용자 지시).
버튼 줄 높이 34px + 위 여백 16px 아래에 붙인다. 위젯 모양·색은 공용
버튼 줄 높이 32px(단추 단 32) + 위 여백 16px 아래에 붙인다. 위젯 모양·색은 공용
(`ui_template/ui_template_compass.css`)이고 여기서는 위치만 준다. */
.b05-route__compass {
top: calc(var(--spacing-16) + 34px + var(--spacing-8));
top: calc(var(--spacing-16) + 32px + var(--spacing-8));
left: var(--spacing-16);
}
@@ -217,9 +217,9 @@ html.is-bm-route .b05-route__view-controls {
white-space: nowrap;
}
.b05-route__view-controls .ui-btn {
height: 34px;
padding: 0 var(--spacing-8);
/* 보기 줄 단추 = 부품 단추 단 32(553-2) · 줄 폭이 지도 폭을 넘지 않게 안 여백은 8 그대로 */
.b05-route__view-controls .ui-btn[data-size] {
padding-inline: var(--spacing-8);
}
.b05-route__view-separator {
@@ -231,7 +231,7 @@ html.is-bm-route .b05-route__view-controls {
/* [3D 업데이트] 대기 표시 — 계획선 편집이 3D에 아직 안 들어갔음을 점으로 알린다. */
.b05-route__view-controls .ui-btn.is-pending {
position: relative;
border-color: var(--color-warning, #d98a00);
border-color: var(--color-warning);
}
.b05-route__view-controls .ui-btn.is-pending::after {
@@ -242,7 +242,7 @@ html.is-bm-route .b05-route__view-controls {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--color-warning, #d98a00);
background: var(--color-warning);
}
.b05-route__panel {
@@ -356,30 +356,14 @@ html.is-bm-route .b05-route__view-controls {
gap: var(--spacing-8);
}
/* [추가] · [삭제] · [리셋] = 부품 「단추」(553-2 · 주 filled · 위험 고스트 · 보조 ghost) — 크기 = 좌측 패널 양식 */
.b05-route__irregular-btn {
flex: 1 1 0;
padding: var(--spacing-8);
border: 1px solid var(--color-border);
border-radius: var(--radius-inputs);
background: var(--color-surface);
color: var(--color-text-body);
font-size: var(--text-caption);
cursor: pointer;
}
.b05-route__irregular-btn.is-primary {
border-color: color-mix(in srgb, var(--color-royal-amethyst, rgb(109 40 217)) 55%, transparent);
color: var(--color-text);
}
.b05-route__irregular-btn.is-danger {
border-color: color-mix(in srgb, var(--color-danger) 50%, transparent);
color: var(--color-danger);
}
.b05-route__irregular-btn:disabled {
opacity: 0.45;
cursor: default;
/* 안 여백 8 그대로 — 옆 칸 폭 보호(구조물 조합 [해제]) */
.b05-route__irregular-btn.ui-btn {
padding-inline: var(--spacing-8);
}
.b05-route__irregular-list {
@@ -590,27 +574,7 @@ html.is-bm-route .b05-route__view-controls {
margin-right: var(--spacing-8);
}
.b05-route-profile__tool {
padding: var(--btn-bar-padding);
border: 1px solid color-mix(in srgb, var(--color-border) 75%, transparent);
border-radius: 3px;
background: color-mix(in srgb, var(--color-surface-raised) 80%, transparent);
color: var(--color-text);
font-family: var(--font-body);
font-size: var(--btn-bar-font-size);
line-height: 1;
cursor: pointer;
}
.b05-route-profile__tool:disabled {
opacity: 0.35;
cursor: default;
}
.b05-route-profile__tool.is-active {
border-color: var(--color-primary);
background: color-mix(in srgb, var(--color-primary) 22%, transparent);
}
/* 종단 도구 단추 = 부품 「단추」 유리 · 단 18(553-2 · `_Profile_Tools.ts`) */
.b05-route-profile__tool-hint {
color: var(--color-text-muted);
+9 -64
View File
@@ -87,28 +87,9 @@
pointer-events: auto;
}
/* 표시 토글 버튼 — B04 지도 레이어 버튼(.b04-map__layer-button--gis)과 같은 양식을 쓴다.
크기(패딩·글자)만 이 패널 기준을 유지한다(2026-08-01 사용자 지시). */
/* 표시 토글 = 부품 「켜기」 칩 단 20(553-2 · 색 점 = 층 선 색) — 한 줄 유지 · 좁아져도 안 쭈그러듦. */
.b05-drainage__layer-button {
/* 한 줄 유지 — 좁아져도 글자가 접히거나 쭈그러들지 않게 크기를 고정한다. */
flex: 0 0 auto;
white-space: nowrap;
padding: 2px var(--spacing-8);
border: 1px solid var(--color-border);
border-radius: var(--radius-inputs);
background: var(--color-surface);
color: var(--color-text-secondary);
font-size: var(--text-caption);
opacity: 0.55;
cursor: pointer;
}
/* 켜진 레이어는 그 레이어의 선 색을 테두리·글자·안쪽 링에 그대로 쓴다(지도와 바로 대조). */
.b05-drainage__layer-button.is-active {
border-color: var(--b05-layer-color, var(--color-border));
box-shadow: inset 0 0 0 1px var(--b05-layer-color, transparent);
color: var(--b05-layer-color, var(--color-text-body));
opacity: 1;
}
/* 배관 우클릭 메뉴 — 뷰포트 기준 절대 위치(B04 지도와 같은 조작) · 모양은 공용 `.ui-ctx-menu`. */
@@ -156,21 +137,9 @@
pointer-events: none;
}
/* 머리 줄 단추 = 부품 「단추」 단 22(553-2) — 자리만 */
.b05-drainage__analyze {
margin-left: auto;
padding: 2px var(--spacing-8);
border: 1px solid
color-mix(in srgb, var(--color-royal-amethyst, rgb(109 40 217)) 55%, transparent);
border-radius: var(--radius-inputs);
background: var(--color-surface);
color: var(--color-text-body);
font-size: var(--text-caption);
cursor: pointer;
}
.b05-drainage__analyze:disabled {
opacity: 0.45;
cursor: default;
}
/* 머리 줄 둘째 단추([자동 제안]) — [업데이트] 우측에 나란히(auto 마진 해제). */
@@ -178,21 +147,11 @@
margin-left: var(--spacing-4, 4px);
}
/* 배관 편집 토글 활성 상태. */
.b05-drainage__analyze.is-active {
background: color-mix(
in srgb,
var(--color-royal-amethyst, rgb(109 40 217)) 18%,
var(--color-surface)
);
border-color: var(--color-royal-amethyst, rgb(109 40 217));
}
/* 유역 제원 목록 — 면적·유역표고·유하거리·관경(수식 확정 전까지 "미정"). */
/* 유역 목록은 3행까지만 보이고 나머지는 스크롤한다(2026-08-01 사용자 지시).
높이를 비율로 잡으면 유역 수와 무관하게 잘려 몇 개인지 가늠이 안 된다. */
.b05-drainage__basins {
--b05-basin-row: 34px;
--b05-basin-row: 32px;
display: flex;
max-height: calc(3 * var(--b05-basin-row) + 2 * 2px + 2 * var(--spacing-8));
@@ -217,25 +176,11 @@
display: none;
}
.b05-drainage__basin {
display: flex;
min-height: var(--b05-basin-row);
align-items: center;
gap: var(--spacing-8);
padding: var(--spacing-4) var(--spacing-8);
border: 1px solid transparent;
border-radius: var(--radius-inputs);
background: none;
color: var(--color-text-body);
/* 유역 줄 = 부품 「목록 줄」 단 32(553-2) — 번호 원 · 제원 왼쪽 붙임 · 글 12(제원 한 줄) */
.b05-drainage__basin.ui-list-row {
flex: none;
justify-content: flex-start;
font-size: var(--text-caption);
text-align: left;
cursor: pointer;
}
.b05-drainage__basin:hover,
.b05-drainage__basin.is-selected {
border-color: var(--color-border);
background: var(--color-surface);
}
/* 지도 위 서클 번호와 같은 파스텔 색을 써서 목록 항목과 유역을 눈으로 잇는다. */
@@ -270,7 +215,7 @@
/* 배수유역 낡음(326-6) — 옛 그림 그대로 · [업데이트] 단추에 점([3D 업데이트] 꼴) · 구석 「계산 중」. */
.b05-drainage__update.is-pending {
position: relative;
border-color: var(--color-warning, #d98a00);
border-color: var(--color-warning);
}
.b05-drainage__update.is-pending::after {
@@ -281,7 +226,7 @@
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--color-warning, #d98a00);
background: var(--color-warning);
}
.b05-drainage__busy {
+6 -23
View File
@@ -63,7 +63,7 @@
/* 조작 설명 — 도구줄 아래 왼쪽 · 기본 접힘 · [?] 로 펼침(345-5 덧 3 · 352-1) · 펼치면 **2열**(사용자 지시 ⑩). */
.b05-routeedit__hint {
top: calc(var(--spacing-16) + 34px + var(--spacing-8));
top: calc(var(--spacing-16) + 32px + var(--spacing-8));
left: var(--spacing-16);
display: flex;
align-items: flex-start;
@@ -72,11 +72,10 @@
pointer-events: none;
}
.b05-routeedit__hint-toggle {
.b05-routeedit__hint-toggle.ui-btn[data-size] {
flex: none;
padding: var(--spacing-4, 4px) var(--spacing-8);
padding-inline: var(--spacing-8);
font-weight: 600;
line-height: 1;
pointer-events: auto;
}
@@ -132,22 +131,12 @@
display: block;
}
/* 접기 = 부품 「아이콘 단추」 단 18(553-2) */
.b05-routeedit__info-toggle {
grid-column: -2;
padding: 0 2px;
border: 0;
background: none;
color: inherit;
font: inherit;
line-height: inherit;
cursor: pointer;
pointer-events: auto;
}
.b05-routeedit__info-toggle:hover {
color: var(--color-text-body);
}
/* 회전 단추 — 지도 오른쪽 위(사용자 지시 ⑯) · 횡단 판 왼쪽에 앉음(352-1). 여기만 클릭을 받는다. */
.b05-routeedit__spin {
position: absolute;
@@ -158,12 +147,6 @@
gap: var(--spacing-8);
}
.b05-routeedit__spin .ui-btn {
padding: var(--spacing-8) var(--spacing-12);
font-size: var(--text-body);
line-height: 1;
}
.b05-routeedit__legend {
display: flex;
flex-direction: column;
@@ -192,7 +175,7 @@
.b05-routeedit__side-wrap {
position: absolute;
z-index: 2;
top: calc(var(--spacing-16) + 34px + var(--spacing-8)); /* 도구줄 · 회전 단추 줄 아래 */
top: calc(var(--spacing-16) + 32px + var(--spacing-8)); /* 도구줄(단 32) · 회전 단추 줄 아래 */
right: var(--spacing-12);
bottom: calc(var(--spacing-12) + var(--b05-profile-cover, 0px));
display: flex;
@@ -377,7 +360,7 @@
display: inline-flex;
align-items: center;
gap: var(--spacing-4, 4px);
height: 34px;
height: 32px;
padding: 0 var(--spacing-8);
border: 1px solid color-mix(in srgb, var(--color-border) 65%, transparent);
border-radius: var(--radius-8, 6px);
+14 -28
View File
@@ -284,15 +284,15 @@
align-items: stretch;
margin: 0;
padding: 0;
border: 1px solid var(--color-border, #3a3f4a);
border-radius: var(--radius-inputs, 4px);
border: 1px solid var(--color-border);
border-radius: var(--radius-inputs);
background: var(--color-surface);
gap: 0;
overflow: hidden;
}
.b05-structure__stepper:focus-within {
border-color: var(--color-royal-amethyst, rgb(109 40 217));
border-color: var(--color-accent);
}
/* 가운데 숫자칸은 자기 테두리·배경을 버리고 덩어리 안에 녹는다. */
@@ -309,32 +309,22 @@
outline: none;
}
/* 좌우 끝 버튼 — 덩어리 안에서는 가르는 선 하나씩만 갖는다. */
.b05-structure__step {
flex: none;
/* 좌우 끝 버튼 = 부품 「아이콘 단추」(553-2) — 덩어리 안에서는 가르는 선 하나씩만 갖는다. */
.b05-structure__stepper > .b05-structure__step {
/* 손가락·마우스로 집기 쉽게 넓힌다(2026-08-29 사용자 지시). 숫자칸이 남는 폭을
차지하므로 이 값이 곧 버튼 폭이다. */
width: 28px;
padding: 0;
height: 100%;
border: 0;
border-radius: 0;
background: transparent;
color: var(--color-text-body);
font-size: 12px;
line-height: 1;
cursor: pointer;
}
.b05-structure__step:first-child {
border-right: 1px solid var(--color-border, #3a3f4a);
.b05-structure__stepper > .b05-structure__step:first-child {
border-right: 1px solid var(--color-border);
}
.b05-structure__step:last-child {
border-left: 1px solid var(--color-border, #3a3f4a);
}
.b05-structure__step:hover {
background: color-mix(in srgb, var(--color-royal-amethyst, rgb(109 40 217)) 14%, transparent);
.b05-structure__stepper > .b05-structure__step:last-child {
border-left: 1px solid var(--color-border);
}
/* 구조물 배치 폼의 숫자 칸 — 브라우저 기본 증감 화살표(스피너)를 없앤다
@@ -389,10 +379,6 @@
height: var(--b05-control-h);
}
.b05-structure__step {
height: 100%;
}
/* 조정창에서 옮겨 온 행(단 수·옵션) — 같은 변수로 맞춘다. */
.b05-structure__adjust-slot .b06-structure-panel__controls {
height: var(--b05-control-h);
@@ -634,10 +620,10 @@
font-size: var(--text-caption);
}
/* 켬 = 확폭 포함 — 테두리색 + 표식(`.is-primary` 는 [추가] 단추 몫이라 안 씀) */
.b05-structure__widening .b05-route__irregular-btn.is-on {
border-color: color-mix(in srgb, var(--color-royal-amethyst, rgb(109 40 217)) 55%, transparent);
color: var(--color-text);
/* 켬 = 확폭 포함 — 부품 보조 단추 꼴 + 표식 · 끔 = 흐린 선 · 글(`.is-primary` 는 [추가] 단추 몫이라 안 씀) */
.b05-structure__widening .b05-route__irregular-btn[aria-pressed="false"] {
border-color: var(--color-border);
color: var(--color-text-secondary);
}
.b05-structure__widening [data-widening-run][aria-pressed="true"]::before {
+14 -52
View File
@@ -413,34 +413,15 @@
pointer-events: none;
}
/* 상시 보이게 둔다(예전엔 패널 hover 시에만 노출). 크기는 ▲▼·↺·⬆⬇ 전부 21×17로
키웠다(2026-08-04 사용자 지시 — 18×15는 누르기도 읽기도 작았다). */
/* 상시 보이게 둔다(예전엔 패널 hover 시에만 노출). ▲▼·↺·⬆⬇ = 부품 「아이콘 단추」 선 꼴 단 18(553-2 · 18×18). */
.b05-profile-edit__btn {
position: absolute;
width: 21px;
height: 17px;
padding: 0;
font-size: 12px;
line-height: 1;
border: 1px solid color-mix(in srgb, var(--color-border) 75%, transparent);
border-radius: 3px;
background: color-mix(in srgb, var(--color-surface-raised) 80%, transparent);
color: var(--color-text);
font-size: 9px;
line-height: 1;
cursor: pointer;
opacity: 0.9;
pointer-events: auto;
transition:
opacity var(--transition-fast),
background var(--transition-fast);
}
.b05-route-profile .b05-profile-edit__btn:hover,
.b05-route-profile .b05-profile-edit__btn:focus-visible {
border-color: var(--color-border);
background: var(--color-surface-raised);
color: var(--color-text);
opacity: 1;
}
@@ -470,8 +451,8 @@
}
.b05-profile-edit__btn.is-down {
border-color: color-mix(in srgb, var(--color-chart-0, #5b8def) 65%, transparent);
color: var(--color-chart-0, #5b8def);
border-color: color-mix(in srgb, var(--color-chart-0) 65%, transparent);
color: var(--color-chart-0);
}
.b05-route-profile .b05-profile-edit__btn.is-up:hover,
@@ -483,16 +464,15 @@
.b05-route-profile .b05-profile-edit__btn.is-down:hover,
.b05-route-profile .b05-profile-edit__btn.is-down:focus-visible {
border-color: var(--color-chart-0, #5b8def);
background: color-mix(in srgb, var(--color-chart-0, #5b8def) 14%, var(--color-surface-raised));
color: var(--color-chart-0, #5b8def);
border-color: var(--color-chart-0);
background: color-mix(in srgb, var(--color-chart-0) 14%, var(--color-surface-raised));
color: var(--color-chart-0);
}
.b05-profile-edit__btn.is-reset {
top: 22px;
border-color: color-mix(in srgb, var(--color-royal-amethyst, rgb(109 40 217)) 55%, transparent);
background: var(--color-surface-raised);
color: color-mix(in srgb, var(--color-royal-amethyst, rgb(139 92 246)) 85%, var(--color-text));
border-color: color-mix(in srgb, var(--color-accent) 55%, transparent);
color: var(--color-accent);
opacity: 0.95;
}
.b05-profile-table__segment.is-structure {
@@ -528,30 +508,12 @@
margin-left: auto;
}
.b05-profile__zoom-btn {
width: 22px;
height: 22px;
padding: 0;
border: none;
border-left: 1px solid var(--color-border);
background: none;
color: var(--color-text-secondary);
font-size: 0.8rem;
line-height: 1;
cursor: pointer;
/* 묶음 안 단추 = 부품 「아이콘 단추」 단 22(553-2) — 붙은 묶음이라 둥글기 없이 가르는 선 하나씩 ·
* 배율 한계(축소는 폭맞춤, 확대는 8배)에 닿은 단추는 부품 막힘 꼴(흐림). */
.b05-profile__zoom > .b05-profile__zoom-btn {
border-radius: 0;
}
.b05-profile__zoom-btn:first-child {
border-left: none;
}
.b05-profile__zoom-btn:hover:not(:disabled) {
color: var(--color-text);
background: var(--color-surface);
}
/* 배율 한계(축소는 폭맞춤, 확대는 8배)에 닿은 버튼 — 눌러도 변화가 없으니 흐리게 죽인다. */
.b05-profile__zoom-btn:disabled {
opacity: 0.35;
cursor: default;
.b05-profile__zoom-btn + .b05-profile__zoom-btn {
border-left-color: var(--color-border);
}
+8 -9
View File
@@ -17,6 +17,7 @@ import "./B08_Quantity_UI_Drawing.css";
import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale";
import { createText } from "@ui/ui_template_text";
import { openModal } from "@ui/ui_template_modal";
import { createPanelHead } from "@ui/ui_template_panel_head";
import { showToast } from "@ui/ui_template_elements";
import { PART_OF, projectItems, withCopyValues } from "@ui/ui_template_item_picker_project";
import type { EditView } from "@ui/ui_template_item_picker_edit";
@@ -106,20 +107,18 @@ function comboSection(
const section = document.createElement("section");
section.className = "b08-sheets__combo";
section.dataset.b08Combo = combo.id;
const title = document.createElement("h4");
title.className = "b08-sheets__title";
title.textContent = name;
title.title = summaryTooltip(combo.item);
section.append(title);
const head = createPanelHead({
title: name,
sub: copy ? L("B08_Quantity_Sheets_FromCopy") : undefined,
level: 4,
});
head.title.title = summaryTooltip(combo.item);
section.append(head.root);
if (!copy) {
section.append(note(L("B08_Quantity_Drawing_Missing")));
return section;
}
const source = sourceOf(request.projectId, copy.폴더);
const from = document.createElement("span");
from.className = "b08-sheets__from";
from.textContent = L("B08_Quantity_Sheets_FromCopy");
title.append(from);
if (!copy.부분.includes("도면")) section.append(note(L("B08_Quantity_Drawing_NoDrawing")));
const load = (): void => void cards.load({ id: copy.폴더, label: name });
// 카드 안 [저장]은 없음(자료 자리에 write 없음) — [수정]만 사본 편집 화면으로(163-3)
+8 -13
View File
@@ -30,17 +30,6 @@
color: var(--color-text-secondary);
}
.b08-milestone__head {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
gap: var(--spacing-8) var(--spacing-16);
}
.b08-milestone__head > .ui-field {
min-width: 160px;
}
.b08-milestone__image {
max-width: 100%;
max-height: 70vh;
@@ -180,8 +169,8 @@
}
/* 등록 · 연결선 모달(244-13) — 더 큰 창 · 2열 칸 */
/* 폭 = 부품 모달 폭 단(960) · 키 큰 창만 여기 */
.ui-modal.b08-milestone__modal {
width: min(960px, 94vw);
--modal-max: 90vh;
}
@@ -268,5 +257,11 @@
}
[data-b08-milestone="table"] td.b08-milestone__lamp.is-linked::before {
background: var(--color-success);
content: "✓"; /* 성공 램프는 ✓ 같이(색만으로 안 가름) */
background: none;
color: var(--color-success);
font-size: 9px;
font-weight: var(--font-weight-bold);
line-height: 8px;
text-align: center;
}
@@ -124,6 +124,7 @@ function openMidLeg(projectId: string): void {
title: "구간 — 시점 → 중점",
closeLabel: "닫기",
dialogClass: "b08-milestone__modal",
width: "960px",
mount: (body, close, actions) => {
const road = createSelectField({
label: "도로 구분",
@@ -172,6 +173,7 @@ export function openLegInfo(projectId: string, from: string): void {
title: `구간 — ${seg.text}`,
closeLabel: "닫기",
dialogClass: "b08-milestone__modal",
width: "960px",
mount: (body, close, actions) => {
const legsOf = (): Leg[] => ownerOf(liveMilestone(projectId), from)?.legs ?? [];
const isBase = liveMilestone(projectId).bases.some((b) => b.id === from);
@@ -113,6 +113,7 @@ export function openPlaceModal(
title,
closeLabel: "닫기",
dialogClass: "b08-milestone__modal",
width: "960px",
mount: (body, close, actions) => {
let kind: "route" | "base" = editBase ? "base" : "route";
const sort = createSelectField({
@@ -263,6 +264,7 @@ export function openSiteInfo(projectId: string): void {
title: "시점 · 중점",
closeLabel: "닫기",
dialogClass: "b08-milestone__modal",
width: "960px",
mount: (body, close, actions) => {
// 중점 측점 · 시점 → 중점 도로(좌측 패널 카드에서 옮김 · 244-6) · 보이고 받는 것만 km(255-1)
const data = liveMilestone(projectId);
+7 -1
View File
@@ -72,7 +72,13 @@ html.is-bm-route .b08-quantity > .b08-quantity__tabs:first-child {
/* 247-1 — 모두 연결 = 같은 램프 초록 */
[data-b08-summary="table"] td.b08-summary__lamp.is-linked::before {
background: var(--color-success);
content: "✓"; /* 성공 램프는 ✓ 같이(색만으로 안 가름) */
background: none;
color: var(--color-success);
font-size: 9px;
font-weight: var(--font-weight-bold);
line-height: 8px;
text-align: center;
}
/* 좌측 패널 옵션(122) — 연암 · 경암 두 칸 나란히 · 설명 한 줄 */
+7 -1
View File
@@ -155,7 +155,13 @@ html.is-bm-route .b09-estimation > .b09-estimation__tabs:first-child {
}
.b09-estimation .ui-data-table__table tbody td.b09-bill__lamp.is-linked::before {
background: var(--color-success);
content: "✓"; /* 성공 램프는 ✓ 같이(색만으로 안 가름) */
background: none;
color: var(--color-success);
font-size: 9px;
font-weight: var(--font-weight-bold);
line-height: 8px;
text-align: center;
}
/* 184-3 설계내역서 고친 칸 — 자동 값과 다른 칸(풍선 = 자동 값) */
@@ -25,6 +25,11 @@ function el(tag, className = "") {
parent: null,
listeners: {},
style: { height: "", cssText: "" },
attrs: {},
setAttribute(k, v) {
node.attrs[k] = String(v);
},
getAttribute: (k) => node.attrs[k] ?? null,
};
node.classList = {
list: () => node.className.split(/\s+/).filter(Boolean),
+3 -1
View File
@@ -151,7 +151,9 @@ def test_도구줄_직선화_오른쪽_자유지정():
tools = _read("B05_Profile/B05_Profile_UI_Profile_Tools.ts")
assert '"자유지정"' in tools
assert "wrap.append(reset, undo, redo, straighten, free, shift, up, down)" in tools
assert 'className = "b05-route-profile__tool"' in tools # 같은 단추 양식
assert (
'className = "ui-btn ui-btn--glass b05-route-profile__tool"' in tools
) # 같은 단추 양식(부품 단추 · 553-2)
def test_적용은_직선화와_같은_길_키_배선():
@@ -102,6 +102,9 @@ def test_배선과_양식():
assert "attachSubPanelPopouts(profile);" in shared
css = (ROOT / "A00_Common/design/design_page.css").read_text(encoding="utf-8")
block = re.search(r"\n\.b05-subpanel__popout \{([^}]*)\}", css).group(1)
assert "var(--btn-bar-padding)" in block and "var(--btn-bar-font-size)" in block
# 크기 = 부품 단추 단 18(553-2) — 페이지 CSS 여백 덮어쓰기 없음
assert "flex: none;" in block and "padding" not in block
popout = (ROOT / "A00_Common/design/design_profile_popout.ts").read_text(encoding="utf-8")
assert popout.count('button.setAttribute("data-size", "18")') == 2
assert ".b05-profile__masshaul-overlay.is-popout," in css
assert ".b05-drainage.is-popout {" in css
@@ -282,7 +282,7 @@ def test_화면_등록_모달_연결선_모달():
css = _read(B08 + "UI_Milestone.css")
# ① 큰 창 · 2열 · 분류 · 항목 목록 · 소재지 · 직접 입력 없음 · 다음 지도
assert 'dialogClass: "b08-milestone__modal"' in modal
assert ".ui-modal.b08-milestone__modal {\n width: min(960px, 94vw);" in css
assert 'width: "960px"' in modal # 폭 = 부품 모달 폭 단
assert "grid-template-columns: 1fr 1fr;" in css
assert "children: [sort.root, first.item.root, name.root, first.dest.root]" in modal
assert (
@@ -102,6 +102,10 @@ console.log(JSON.stringify([text(cell(0)), text(cell(1))]));
def test_초록_성공_토큰():
css = (ROOT / "B08_Quantity" / "B08_Quantity_UI_Page.css").read_text(encoding="utf-8")
m = re.search(r"td\.b08-summary__lamp\.is-linked::before\s*\{([^}]*)\}", css)
assert m and re.search(r"background:\s*var\(--color-success\)", m.group(1))
assert (
m
and re.search(r"color:\s*var\(--color-success\)", m.group(1))
and 'content: "✓"' in m.group(1)
)
theme = (ROOT / "ui_template" / "ui_template_theme.css").read_text(encoding="utf-8")
assert len(re.findall(r"--color-success:\s*#", theme)) >= 2 # 밝음 · 어두움
@@ -35,7 +35,7 @@ def test_도움말_기본_접힘_펼침_기억() -> None:
# 접힘 = 몸통 없음 · 펼침 = 2열 · 단추만 클릭 받음
assert "display: none;" in _rule(".b05-routeedit__hint-body")
assert "display: grid;" in _rule(".b05-routeedit__hint.is-open > .b05-routeedit__hint-body")
assert "pointer-events: auto;" in _rule(".b05-routeedit__hint-toggle")
assert "pointer-events: auto;" in _rule(".b05-routeedit__hint-toggle.ui-btn[data-size]")
assert "pointer-events: none;" in _rule(".b05-routeedit__hint")