fix(B07 CAD): 어두운 테마에서 검정 선이 안 보이던 것 — 자동색(흰/검정)

검정 · 흰색(DXF 색 7) · 기본색(#f5f7fa = 새 개체 · DXF ByLayer)은 오토캐드처럼 자동색으로 보아
어두운 바탕에서는 흰색 · 밝은 바탕에서는 검정으로 그림(그릴 때만 · 저장 값은 그대로).
다른 색은 그대로 · 어두운 바탕에서 어두운 색을 밝히지 않음 · 밝은 바탕의 밝은 색 대비 보정은 기존대로.
좌표 입력 칸 바탕도 자동색(검정)으로 바꿔 두 테마에서 글이 읽힘.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K7JS47SacjPxZ718QsZKyr
This commit is contained in:
2026-09-28 16:14:22 +09:00
co-authored by Claude Opus 5.5
parent fa572cd0b7
commit 44a6ec51db
4 changed files with 67 additions and 14 deletions
@@ -176,8 +176,9 @@ export const CANVAS_INPUT_FIELD_HEIGHT = 20;
/**
* Canvas input field background color
* 자동색(검정) — 어두운 바탕 검정 칸에 흰 글 · 밝은 바탕 흰 칸에 검정 글(`theme.ts` paintColor)
*/
export const CANVAS_INPUT_FIELD_BACKGROUND_COLOR = '#161616';
export const CANVAS_INPUT_FIELD_BACKGROUND_COLOR = '#000000';
/**
* Canvas input field text color
@@ -242,7 +242,7 @@ export class ScreenCanvasDrawController implements DrawController {
lineWidth: number,
dash: number[] = []
) {
// 저장된 도면 색은 다크 배경 기준 — 라이트 테마에서는 그릴 때만 대비를 맞춘다.
// 자동색(검정·흰색·기본색)은 바탕 따라 흰/검정 · 라이트 테마는 밝은 색 대비도 맞춤(그릴 때만).
const color = paintColor(rawColor);
if (this.batching) {
const effectiveWidth = isHighlighted ? lineWidth + 1 : lineWidth;
+24 -12
View File
@@ -61,26 +61,38 @@ function relativeLuminance(r: number, g: number, b: number): number {
}
/**
* 도면 색 보정 — 저장된 엔티티 색은 다크 배경 기준(밝은 회색·흰색)이라
* 라이트 모드 흰 종이 위에서는 안 보인다. **그릴 때만** 휘도를 낮춰 색상(색조)은
* 유지한 채 대비를 살린다. 저장 데이터는 건드리지 않는다.
* 자동색 — 오토캐드 색 번호 7 처럼 바탕을 따라 흰/검정으로 그리는 색.
* 검정 · 흰색(DXF 색 7) · 기본색(`#f5f7fa` = 새 개체 · DXF ByLayer 가져온 색).
*/
const AUTO_COLORS = new Set(['#000000', '#ffffff', '#f5f7fa', 'black', 'white']);
function normalizeHex(color: string): string {
const value = color.trim().toLowerCase();
const short = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/.exec(value);
return short ? `#${short[1]}${short[1]}${short[2]}${short[2]}${short[3]}${short[3]}` : value;
}
export function isAutoColor(color: string): boolean {
return AUTO_COLORS.has(normalizeHex(color));
}
/**
* 도면 색 보정 — **그릴 때만** 바꾼다(저장 데이터는 그대로).
* - 자동색: 어두운 바탕 흰색 · 밝은 바탕 검정.
* - 밝은 바탕: 흰 종이에 묻히는 밝은 색은 휘도를 낮춰 색조를 살린 채 대비를 맞춘다.
* - 어두운 바탕: 자동색 밖은 그대로(어두운 색을 밝히지 않음).
*/
export function paintColor(color: string): string {
if (!isLightTheme()) return color;
const light = isLightTheme();
if (isAutoColor(color)) return light ? '#000000' : '#ffffff';
if (!light) return color;
const cached = paintCache.get(color);
if (cached !== undefined) return cached;
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color);
let result = color;
if (hex) {
const raw = hex[1];
const full =
raw.length === 3
? raw
.split('')
.map((c) => c + c)
.join('')
: raw;
const full = normalizeHex(color).slice(1);
const r = Number.parseInt(full.slice(0, 2), 16);
const g = Number.parseInt(full.slice(2, 4), 16);
const b = Number.parseInt(full.slice(4, 6), 16);
@@ -0,0 +1,40 @@
/**
* CAD 자동색(`theme.ts` paintColor) 검증 — 검정 · 흰색 · 기본색은 바탕 따라 흰/검정, 다른 색은 그대로.
* 실행: node --experimental-strip-types resources/tester/cad/theme-auto-color.test.ts
*/
import assert from 'node:assert/strict';
let theme = 'dark';
// biome-ignore lint: 시험 — 브라우저 전역 흉내
const g = globalThis as any;
g.document = { documentElement: { getAttribute: () => theme } };
g.window = { matchMedia: () => ({ matches: true }) };
const { paintColor, isAutoColor } = await import(
'../../../B07_DesignDetail/openwebcad/src/theme.ts'
);
for (const color of ['#000000', '#000', '#FFFFFF', '#fff', '#f5f7fa', 'black']) {
assert.ok(isAutoColor(color), `${color} 자동색`);
}
for (const color of ['#ff0000', '#111111', '#333333', '#1a237e', '#a55200']) {
assert.ok(!isAutoColor(color), `${color} 자동색 아님`);
}
theme = 'dark';
assert.equal(paintColor('#000000'), '#ffffff', '어두운 바탕: 검정은 흰색');
assert.equal(paintColor('#f5f7fa'), '#ffffff', '어두운 바탕: 기본색은 흰색');
assert.equal(paintColor('#ffffff'), '#ffffff');
assert.equal(paintColor('#ff0000'), '#ff0000', '어두운 바탕: 빨강 그대로');
assert.equal(paintColor('#1a237e'), '#1a237e', '어두운 바탕: 남색 밝히지 않음');
assert.equal(paintColor('#333333'), '#333333', '어두운 바탕: 어두운 회색 밝히지 않음');
theme = 'light';
assert.equal(paintColor('#ffffff'), '#000000', '밝은 바탕: 흰색은 검정');
assert.equal(paintColor('#FFF'), '#000000');
assert.equal(paintColor('#000000'), '#000000');
assert.equal(paintColor('#f5f7fa'), '#000000', '밝은 바탕: 기본색은 검정');
assert.equal(paintColor('#ff0000'), '#ff0000', '밝은 바탕: 빨강 그대로');
assert.notEqual(paintColor('#ffff00'), '#ffff00', '밝은 바탕: 노랑 등 밝은 색은 대비 보정(기존)');
console.log('theme 자동색 시험 통과');