From 192d4feebe49f32377c1ef707b8bbba3c44b7c1e Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 11 Oct 2026 21:28:54 +0900 Subject: [PATCH] =?UTF-8?q?fix(b01):=20=EC=B1=97=EB=B4=87=20=EC=B0=BD=20?= =?UTF-8?q?=EB=B8=94=EB=9E=99=20=C2=B7=20=ED=99=94=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=ED=85=8C=EB=A7=88=20=EC=83=89=20=EB=A7=9E=EC=B6=A4=20=C2=B7=20?= =?UTF-8?q?Enter=20=EB=B3=B4=EB=82=B4=EA=B8=B0=20=C2=B7=20Shift+Enter=20?= =?UTF-8?q?=EC=A4=84=EB=B0=94=EA=BF=88=20(275-1=C2=B72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 없는 색 변수(--color-text-primary · --color-background) 걷고 테마 변수로 — 어두운 테마 제목 · 말풍선 글자 안 보임 해소 - 단추 · 내 말풍선 = primary 색 짝 · 답 말풍선 = 떠 있는 표면 + 테두리 · 입력칸 바탕 · 글자 - Enter = 보내기 · Shift+Enter = 줄바꿈 · 한글 조합 중 Enter 무시 - 화면 시험 ui_checks/275 추가 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KjUPRxjDxULvh5VA7kX5py --- .../B01_Dashboard_UI_Knowledge_Chat.ts | 8 +- B01_Dashboard/B01_Dashboard_UI_Style.css | 27 ++++-- .../tester/ui_checks/275_챗봇_테마_엔터.js | 96 +++++++++++++++++++ 3 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 resources/tester/ui_checks/275_챗봇_테마_엔터.js diff --git a/B01_Dashboard/B01_Dashboard_UI_Knowledge_Chat.ts b/B01_Dashboard/B01_Dashboard_UI_Knowledge_Chat.ts index a30d9844e..5f35ee9ff 100644 --- a/B01_Dashboard/B01_Dashboard_UI_Knowledge_Chat.ts +++ b/B01_Dashboard/B01_Dashboard_UI_Knowledge_Chat.ts @@ -58,11 +58,17 @@ export function buildKnowledgeChat(): HTMLElement { input.rows = 2; input.required = true; input.maxLength = 500; - input.placeholder = "메시지를 입력하세요"; + input.placeholder = "메시지를 입력하세요 (Shift+Enter 줄바꿈)"; input.setAttribute("aria-label", "질문"); const submit = document.createElement("button"); submit.type = "submit"; submit.textContent = "보내기"; + input.addEventListener("keydown", (event) => { + if (event.key === "Enter" && !event.shiftKey && !event.isComposing) { + event.preventDefault(); + submit.click(); + } + }); form.append(input, submit); panel.append(header, messages, form); wrap.append(toggle, panel); diff --git a/B01_Dashboard/B01_Dashboard_UI_Style.css b/B01_Dashboard/B01_Dashboard_UI_Style.css index de11a4c05..f4f62046f 100644 --- a/B01_Dashboard/B01_Dashboard_UI_Style.css +++ b/B01_Dashboard/B01_Dashboard_UI_Style.css @@ -536,8 +536,8 @@ .b01-knowledge-chat__toggle, .b01-knowledge-chat__form button { border: 0; - background: var(--color-royal-amethyst, #6843a7); - color: #fff; + background: var(--color-primary); + color: var(--color-primary-text); font: inherit; cursor: pointer; } @@ -565,7 +565,7 @@ border-radius: 12px; background: var(--color-surface, #fff); box-shadow: 0 8px 28px #0003; - color: var(--color-text-primary, #222); + color: var(--color-text-body); overflow: hidden; overscroll-behavior: contain; } @@ -588,8 +588,8 @@ height: 34px; place-items: center; border-radius: 50%; - background: var(--color-royal-amethyst, #6843a7); - color: #fff; + background: var(--color-primary); + color: var(--color-primary-text); } .b01-knowledge-chat__avatar svg { @@ -631,14 +631,15 @@ .b01-knowledge-chat__user { align-self: flex-end; border-bottom-right-radius: 4px; - background: var(--color-royal-amethyst, #6843a7); - color: #fff; + background: var(--color-primary); + color: var(--color-primary-text); } .b01-knowledge-chat__reply { align-self: flex-start; + border: 1px solid var(--color-border); border-bottom-left-radius: 4px; - background: var(--color-background, #f4f4f4); + background: var(--color-surface-raised); } .b01-knowledge-chat__reply details { @@ -722,7 +723,7 @@ .b01-knowledge-source-modal__content code { padding: 0.1em 0.35em; border-radius: 4px; - background: var(--color-background, #f4f4f4); + background: var(--color-surface); } .b01-knowledge-source-modal__content .ui-markdown-table { @@ -746,7 +747,7 @@ } .b01-knowledge-source-modal__content th { - background: var(--color-background, #f4f4f4); + background: var(--color-surface); } .b01-knowledge-chat__form { @@ -765,11 +766,17 @@ padding: 8px; border: 1px solid var(--color-border); border-radius: 6px; + background: var(--color-canvas); + color: var(--color-text-body); font: inherit; resize: vertical; overflow-y: auto; } +.b01-knowledge-chat__form textarea::placeholder { + color: var(--color-text-muted); +} + .b01-knowledge-chat__form button { padding: 9px 14px; border-radius: 8px; diff --git a/resources/tester/ui_checks/275_챗봇_테마_엔터.js b/resources/tester/ui_checks/275_챗봇_테마_엔터.js new file mode 100644 index 000000000..1bca61a6d --- /dev/null +++ b/resources/tester/ui_checks/275_챗봇_테마_엔터.js @@ -0,0 +1,96 @@ +/* 275 · 대시보드 챗봇 — 블랙 · 화이트 테마 색 · 엔터 보내기 + * 시작 화면: B01 대시보드(#/b01-account) · 로그인 + * 부르는 법: orca eval --expression "$(cat 이 파일)" (async 식 · JSON 글을 돌려줌) + * 기대 수치: + * - 밝음 · 어두움 모두 글자 대비 ≥ 4.5(제목 · 답 말풍선 · 내 말풍선 · 보내기 · 입력칸 · 열기 단추 · 머리 아이콘) + * - Enter = 보냄(내 말풍선 +1 · 입력칸 빔) · Shift+Enter · 한글 조합 중 Enter = 안 보냄 + * - 서버로 안 나감(fetch 가로챔) · 끝나면 테마 · 말풍선 · fetch 되돌림 + * ⚠ 대화 기록(화면 메모리)에 시험 한 줄이 남음 — 끝나면 새로고침 + */ +(async () => { + const w = (ms) => new Promise((r) => setTimeout(r, ms)); + const rgb = (s) => (s.match(/[\d.]+/g) || []).map(Number); + const lum = ([r, g, b]) => { + const f = (v) => ((v /= 255) <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4); + return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b); + }; + /** 실제로 보이는 바탕 — 투명이면 조상으로 */ + const bgOf = (el) => { + for (let e = el; e; e = e.parentElement) { + const c = rgb(getComputedStyle(e).backgroundColor); + if (c.length >= 3 && (c.length < 4 || c[3] > 0.5)) return c.slice(0, 3); + } + return [255, 255, 255]; + }; + const ratio = (a, b) => { + const [x, y] = [lum(a), lum(b)]; + return +((Math.max(x, y) + 0.05) / (Math.min(x, y) + 0.05)).toFixed(2); + }; + const text = (el) => ratio(rgb(getComputedStyle(el).color).slice(0, 3), bgOf(el)); + + const root = document.documentElement; + const startTheme = root.getAttribute("data-theme"); + const chat = document.querySelector(".b01-knowledge-chat"); + if (!chat) return JSON.stringify({ error: "챗봇 없음 — 대시보드에서 부를 것" }); + const panel = chat.querySelector(".b01-knowledge-chat__panel"); + const input = chat.querySelector("textarea"); + const messages = chat.querySelector(".b01-knowledge-chat__messages"); + const users = () => messages.querySelectorAll(".b01-knowledge-chat__user").length; + const wasHidden = panel.hidden; + if (wasHidden) chat.querySelector(".b01-knowledge-chat__toggle").click(); + const before = messages.children.length; + const realFetch = window.fetch; + window.fetch = async (url, init) => + String(url).endsWith("/knowledge-chat") + ? new Response(JSON.stringify({ answer: "시험 답", sources: [] }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }) + : realFetch(url, init); + const press = (opts) => { + const event = new KeyboardEvent("keydown", { + key: "Enter", + bubbles: true, + cancelable: true, + ...opts, + }); + input.dispatchEvent(event); + return event.defaultPrevented; + }; + const out = {}; + try { + input.value = "시험 질문"; + const start = users(); + out.shiftEnter = { sent: press({ shiftKey: true }) || users() > start }; + out.composing = { sent: press({ isComposing: true }) || users() > start }; + out.enter = { handled: press({}) }; + await w(300); + out.enter.userBubbles = users() - start; + out.enter.inputEmpty = input.value === ""; + for (const theme of ["light", "dark"]) { + root.setAttribute("data-theme", theme); + await w(50); + const reply = messages.querySelector(".b01-knowledge-chat__reply"); + out[theme] = { + title: text(panel.querySelector("header strong")), + avatar: text(chat.querySelector(".b01-knowledge-chat__avatar")), + reply: text(reply), + user: text(messages.querySelector(".b01-knowledge-chat__user")), + send: text(chat.querySelector(".b01-knowledge-chat__form button")), + input: text(input), + toggle: text(chat.querySelector(".b01-knowledge-chat__toggle")), + panelBg: getComputedStyle(panel).backgroundColor, + replyBg: getComputedStyle(reply).backgroundColor, + inputBg: getComputedStyle(input).backgroundColor, + }; + } + } finally { + window.fetch = realFetch; + if (startTheme === null) root.removeAttribute("data-theme"); + else root.setAttribute("data-theme", startTheme); + [...messages.children].slice(before).forEach((node) => node.remove()); + if (wasHidden) chat.querySelector(".b01-knowledge-chat__close").click(); + } + out.themeRestored = root.getAttribute("data-theme") === startTheme; + return JSON.stringify(out); +})();