From aa2845eef7d15eb436d4b5b5e5d64420a811a857 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 5 Oct 2026 13:04:28 +0900 Subject: [PATCH] =?UTF-8?q?style(M02):=20=EA=B5=AC=EC=A1=B0=EB=AC=BC=20?= =?UTF-8?q?=EC=A1=B0=ED=95=A9=20=EC=82=AD=EC=A0=9C=20=EB=8B=A8=EC=B6=94?= =?UTF-8?q?=EB=A5=BC=20=EA=B3=B5=EC=9A=A9=20=EC=9C=84=ED=97=98(=EB=B9=A8?= =?UTF-8?q?=EA=B0=95)=20=EB=8B=A8=EC=B6=94=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 카드 셋 머리 [삭제] · 일위대가 줄 [삭제] 두 곳 = ui-btn--danger(테마 토큰) - 화면 확인 시나리오 45-6 추가(밝음 5.44 · 어두움 5.79 대비 · 옆 단추와 같은 키) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt --- .../M02_MasterTemplete_ItemCards.ts | 2 +- .../M02_MasterTemplete_Labor_Rows.ts | 12 ++-- .../tester/ui_checks/45-6_삭제_단추_색.js | 70 +++++++++++++++++++ 3 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 resources/tester/ui_checks/45-6_삭제_단추_색.js diff --git a/M02_MasterTemplete/M02_MasterTemplete_ItemCards.ts b/M02_MasterTemplete/M02_MasterTemplete_ItemCards.ts index 45cb5ede0..8f07e44ca 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_ItemCards.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_ItemCards.ts @@ -222,7 +222,7 @@ export function createItemCards(opts: ItemCardsOptions): ItemCardsHandle { const take = createButton({ label: TEXT.take, variant: "ghost", size: "sm" }); take.title = fill(TEXT.takeTip, PART_WORD[kind]); take.addEventListener("click", () => opts.onImport?.(kind)); - const del = createButton({ label: TEXT.clear, variant: "ghost", size: "sm" }); + const del = createButton({ label: TEXT.clear, variant: "danger", size: "sm" }); del.title = fill(TEXT.clearTip, PART_WORD[kind]); del.addEventListener("click", () => void clearCard(kind)); shell.tools.append(help, saver.button, take, edit, del); diff --git a/M02_MasterTemplete/M02_MasterTemplete_Labor_Rows.ts b/M02_MasterTemplete/M02_MasterTemplete_Labor_Rows.ts index f2e413ce7..657c629ec 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Labor_Rows.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Labor_Rows.ts @@ -222,10 +222,14 @@ export function logicTable(data: LogicTableData, on?: RowHandlers): HTMLElement const values = valueLine(logic?.입력 ?? [], row.입력값); const note = values.text; const why = line?.까닭 ?? (data.fail && i === 0 ? data.fail : null); - const small = (label: string, run: () => void): HTMLElement => + const small = ( + label: string, + run: () => void, + variant: "ghost" | "danger" = "ghost", + ): HTMLElement => createButton({ label, - variant: "ghost", + variant, onClick: (e) => { e.stopPropagation(); // 줄 누름(입력 모달)과 따로 run(); @@ -272,7 +276,7 @@ export function logicTable(data: LogicTableData, on?: RowHandlers): HTMLElement ...(move ? [small(tc("Up"), () => move(i, -1)), small(tc("Down"), () => move(i, 1))] : []), - ...(remove ? [small(tc("Remove"), () => remove(i))] : []), + ...(remove ? [small(tc("Remove"), () => remove(i), "danger")] : []), ], }), ] @@ -403,7 +407,7 @@ export function matTable(data: MatTableData, on?: Omit): HT children: [ createButton({ label: tc("Remove"), - variant: "ghost", + variant: "danger", onClick: (e) => { e.stopPropagation(); remove(i); diff --git a/resources/tester/ui_checks/45-6_삭제_단추_색.js b/resources/tester/ui_checks/45-6_삭제_단추_색.js new file mode 100644 index 000000000..edad394ea --- /dev/null +++ b/resources/tester/ui_checks/45-6_삭제_단추_색.js @@ -0,0 +1,70 @@ +/* 45-6 — M02 구조물 조합 카드 [삭제] = 공용 위험(빨강) 단추 · 옆 단추와 같은 키 · 밝음/어두움 대비 ≥ 4.5 + * 시작 화면: M02 마스터 템플릿 · 마스터 층(#/m02-master-template?open=구조물조합&root=master) · 시스템 관리자 · + * 나무에 item1(구조물 조합)이 있음 + * 부르는 법: orca eval --expression "$(cat 이 파일)" (async 식 · JSON 글을 돌려줌) + * 기대 수치(light · dark 각각): + * - dels ≥ 1 · 모두 ui-btn--danger · 알약 꼴 아님(round = false) + * - bg = --color-danger(light rgb(192, 57, 43) · dark rgb(232, 105, 90)) · fg = --color-canvas + * - contrast ≥ 4.5 · sameHeight = true(옆 [수정] 단추와 높이 같음) + * - 끝에 data-theme 원래 값으로 되돌림(themeAfter = themeBefore) + */ +(async () => { + const w = (ms) => new Promise((r) => setTimeout(r, ms)); + const until = async (fn, ms = 6000) => { + for (let t = 0; t < ms; t += 150) { + const v = fn(); + if (v) return v; + await w(150); + } + return null; + }; + const vis = (b) => b && !b.hidden && b.offsetParent !== null; + const rgb = (s) => s.match(/[\d.]+/g).slice(0, 3).map(Number); + const lum = (c) => + c + .map((v) => v / 255) + .map((v) => (v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4)) + .reduce((a, v, i) => a + v * [0.2126, 0.7152, 0.0722][i], 0); + const ratio = (a, b) => { + const [x, y] = [lum(rgb(a)), lum(rgb(b))].sort((p, q) => q - p); + return Math.round(((x + 0.05) / (y + 0.05)) * 100) / 100; + }; + const node = await until(() => document.querySelector('[data-node="i|item1"]')); + if (!node) return JSON.stringify({ error: "item1 없음 — 기슭막이 폴더를 펼쳐 둘 것" }); + node.click(); + await until(() => document.querySelector(".m02-struct__card-tools .ui-btn")); + await w(1500); + const root = document.documentElement; + const themeBefore = root.getAttribute("data-theme"); + const out = { themeBefore }; + // 숨은 탭에선 색 전환(transition)이 안 흘러 옛 색에 멈춤 — 재는 동안만 끔 + const still = document.head.appendChild(document.createElement("style")); + still.textContent = "*{transition:none!important}"; + for (const theme of ["light", "dark"]) { + root.setAttribute("data-theme", theme); + await w(300); + const rows = [...document.querySelectorAll(".m02-struct__card-tools")].map((tools) => { + const btns = [...tools.querySelectorAll("button")].filter(vis); + const del = btns.find((b) => b.innerText.trim() === "삭제"); + const edit = btns.find((b) => b.innerText.trim() === "수정") ?? btns.find((b) => b !== del); + if (!del) return null; + const cs = getComputedStyle(del); + const h = del.getBoundingClientRect().height; + return { + cls: del.classList.contains("ui-btn--danger") ? "danger" : del.className, + bg: cs.backgroundColor, + fg: cs.color, + contrast: ratio(cs.backgroundColor, cs.color), + round: parseFloat(cs.borderTopLeftRadius) >= h / 2 - 1, + h, + sameHeight: edit ? Math.abs(edit.getBoundingClientRect().height - h) < 0.5 : null, + }; + }); + out[theme] = rows.filter(Boolean); + } + still.remove(); + if (themeBefore === null) root.removeAttribute("data-theme"); + else root.setAttribute("data-theme", themeBefore); + out.themeAfter = root.getAttribute("data-theme"); + return JSON.stringify(out); +})()