style(M02): 구조물 조합 삭제 단추를 공용 위험(빨강) 단추로
- 카드 셋 머리 [삭제] · 일위대가 줄 [삭제] 두 곳 = ui-btn--danger(테마 토큰) - 화면 확인 시나리오 45-6 추가(밝음 5.44 · 어두움 5.79 대비 · 옆 단추와 같은 키) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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<RowHandlers, "move">): HT
|
||||
children: [
|
||||
createButton({
|
||||
label: tc("Remove"),
|
||||
variant: "ghost",
|
||||
variant: "danger",
|
||||
onClick: (e) => {
|
||||
e.stopPropagation();
|
||||
remove(i);
|
||||
|
||||
@@ -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);
|
||||
})()
|
||||
Reference in New Issue
Block a user