fix(B07-CAD): 커서 입력창 칸 · 글 색이 테마를 따름 — 자동색 뒤집기 건너뜀(97-1)
- 칸 = --cad-chrome-raised(밝음 흰 칸 + --cad-line 테두리 · 어두움 #251f38) · 글 = --cad-text · 안내 = --cad-text-dim - fillRectScreen · drawTextScreen 에 paintColor 건너뛰는 rawColor 선택 인자 - 입력창 그리기를 inputController/input-field-draw.ts 로 · 전체 맞춤 계산을 drawControllers/fit-view.ts 로(700줄) - App.consts 입력창 색 상수 = 토큰 폴백 · 틀린 주석 고침 - 시험 theme-auto-color: 칸 · 글 색이 두 테마 모두 다름 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
This commit is contained in:
@@ -176,14 +176,20 @@ export const CANVAS_INPUT_FIELD_HEIGHT = 20;
|
||||
|
||||
/**
|
||||
* Canvas input field background color
|
||||
* 자동색(검정) — 어두운 바탕 검정 칸에 흰 글 · 밝은 바탕 흰 칸에 검정 글(`theme.ts` paintColor)
|
||||
* 토큰 `--cad-chrome-raised` 못 읽을 때 폴백(어두움 값) — 칸 · 글 · 테두리는 토큰 색을 그대로 칠함
|
||||
* (자동색 뒤집기 `theme.ts` paintColor 건너뜀 · `inputController/input-field-draw.ts`)
|
||||
*/
|
||||
export const CANVAS_INPUT_FIELD_BACKGROUND_COLOR = '#000000';
|
||||
export const CANVAS_INPUT_FIELD_BACKGROUND_COLOR = '#251f38';
|
||||
|
||||
/**
|
||||
* Canvas input field text color
|
||||
* Canvas input field border color — 토큰 `--cad-line` 폴백
|
||||
*/
|
||||
export const CANVAS_INPUT_FIELD_TEXT_COLOR = '#FFF';
|
||||
export const CANVAS_INPUT_FIELD_BORDER_COLOR = '#322a45';
|
||||
|
||||
/**
|
||||
* Canvas input field text color — 토큰 `--cad-text` 폴백
|
||||
*/
|
||||
export const CANVAS_INPUT_FIELD_TEXT_COLOR = '#e4e0f0';
|
||||
|
||||
/**
|
||||
* Canvas input field background color when text is selected
|
||||
@@ -201,9 +207,9 @@ export const CANVAS_INPUT_FIELD_SELECTION_TEXT_COLOR = '#000';
|
||||
export const CANVAS_INPUT_FIELD_FONT_SIZE = 12;
|
||||
|
||||
/**
|
||||
* Canvas input field instruction text color
|
||||
* Canvas input field instruction text color — 토큰 `--cad-text-dim` 폴백
|
||||
*/
|
||||
export const CANVAS_INPUT_FIELD_INSTRUCTION_TEXT_COLOR = '#999';
|
||||
export const CANVAS_INPUT_FIELD_INSTRUCTION_TEXT_COLOR = '#b3aec6';
|
||||
|
||||
/**
|
||||
* Multiplier to determine the pdf line width from the in application line width
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/** 전체 맞춤 계산 — 맞출 개체 고르기 · 배율 · 화면 원점(`screenCanvas.drawController.ts` zoomToFitScreen 이 부름). */
|
||||
import { Point } from '@flatten-js/core';
|
||||
import type { Entity } from '../entities/Entity.ts';
|
||||
import { getBoundingBoxOfMultipleEntities } from '../helpers/get-bounding-box-of-multiple-entities.ts';
|
||||
import { getEntities, getPickableEntities } from '../state.ts';
|
||||
|
||||
/** M02 가 구조물 도면에 얹는 도각 층 — 서버 `STRUCTURE_FRAME_LAYER` 와 같음 */
|
||||
const STRUCTURE_FRAME_LAYER_ID = 'm02-structure-frame';
|
||||
|
||||
/** 맞출 개체 */
|
||||
export function fitEntities(): Entity[] {
|
||||
// 도각·원지반 같은 잠금 도면층은 빼고 설계 콘텐츠에만 맞춘다 (2026-08-31 사용자
|
||||
// 확정). 도각까지 넣으면 A1 한 장 전체가 잡혀 화면의 40%가 여백으로 간다.
|
||||
// 구조물 도면(M02·B08)은 얹은 도각(작도 틀)이 있으면 도각 전체에 맞춘다 — 옛 DXF 가 자기 도곽·
|
||||
// 표제 띠를 품어 범위가 크면 도각이 화면 밖으로 밀린다.
|
||||
const structureFrame = getEntities().filter((e) => e.layerId === STRUCTURE_FRAME_LAYER_ID);
|
||||
const pickable = getPickableEntities();
|
||||
return structureFrame.length ? structureFrame : pickable.length ? pickable : getEntities();
|
||||
}
|
||||
|
||||
/** 개체 범위를 화면 가운데에 놓는 배율 · 화면 원점(월드 좌표) */
|
||||
export function fitView(entities: Entity[], canvasSize: Point): { zoom: number; offset: Point } {
|
||||
const boundingBox = getBoundingBoxOfMultipleEntities(entities);
|
||||
const boundingWidth = boundingBox.maxX - boundingBox.minX;
|
||||
const boundingHeight = boundingBox.maxY - boundingBox.minY;
|
||||
|
||||
// 10% 여백을 남기고 두 축 중 더 빡빡한 쪽에 맞춘다 (종횡비 유지)
|
||||
const FIT_MARGIN = 0.9;
|
||||
const scaleX =
|
||||
boundingWidth > 0 ? (canvasSize.x * FIT_MARGIN) / boundingWidth : Number.POSITIVE_INFINITY;
|
||||
const scaleY =
|
||||
boundingHeight > 0 ? (canvasSize.y * FIT_MARGIN) / boundingHeight : Number.POSITIVE_INFINITY;
|
||||
let zoomLevel = Math.min(scaleX, scaleY);
|
||||
if (!Number.isFinite(zoomLevel) || zoomLevel <= 0) zoomLevel = 1;
|
||||
|
||||
// screen = (world - offset) * zoom 이므로, 도면 중심을 화면 중심에 맞추려면
|
||||
// offset = worldCenter - (화면 절반 픽셀) / zoom
|
||||
const worldCenterX = (boundingBox.minX + boundingBox.maxX) / 2;
|
||||
const worldCenterY = (boundingBox.minY + boundingBox.maxY) / 2;
|
||||
return {
|
||||
zoom: zoomLevel,
|
||||
offset: new Point(
|
||||
worldCenterX - canvasSize.x / 2 / zoomLevel,
|
||||
worldCenterY - canvasSize.y / 2 / zoomLevel
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Point, type Vector } from '@flatten-js/core';
|
||||
import { WHEEL_ZOOM_EXPONENT } from '../App.consts';
|
||||
import { getAngleWithXAxis } from '../helpers/get-angle-with-x-axis.ts';
|
||||
import { getBoundingBoxOfMultipleEntities } from '../helpers/get-bounding-box-of-multiple-entities.ts';
|
||||
import { mapNumberRange } from '../helpers/map-number-range.ts';
|
||||
import { StateVariable } from '../helpers/undo-stack.ts';
|
||||
import { getEntities, getGridEnabled, getPickableEntities, triggerReactUpdate } from '../state.ts';
|
||||
import { getGridEnabled, triggerReactUpdate } from '../state.ts';
|
||||
import { paintColor, themeColor } from '../theme.ts';
|
||||
import { DEFAULT_TEXT_OPTIONS, type DrawController } from './DrawController';
|
||||
import { fitEntities, fitView } from './fit-view.ts';
|
||||
|
||||
/** 토큰을 못 읽을 때만 쓰는 캔버스 배경 폴백. */
|
||||
const CANVAS_BACKGROUND_FALLBACK = '#111';
|
||||
@@ -31,8 +31,6 @@ const CANVAS_BACKGROUND_FALLBACK = '#111';
|
||||
*
|
||||
* To convert between the 2 coordinate systems, you need the screenOffset and screenScale
|
||||
*/
|
||||
/** M02 가 구조물 도면에 얹는 도각 층 — 서버 `STRUCTURE_FRAME_LAYER` 와 같음 */
|
||||
const STRUCTURE_FRAME_LAYER_ID = 'm02-structure-frame';
|
||||
// Batch-mode stroke decimation: skip chain segments shorter than this (screen px)
|
||||
const BATCH_LOD_PX = 0.5;
|
||||
// Batch-mode text smaller than this (screen px) is unreadable — skip drawing it
|
||||
@@ -150,17 +148,7 @@ export class ScreenCanvasDrawController implements DrawController {
|
||||
* 월드 좌표 offset에 그대로 대입해 중심 배치가 어긋나는 문제가 있었다.)
|
||||
*/
|
||||
public zoomToFitScreen() {
|
||||
// 도각·원지반 같은 잠금 도면층은 빼고 설계 콘텐츠에만 맞춘다 (2026-08-31 사용자
|
||||
// 확정). 도각까지 넣으면 A1 한 장 전체가 잡혀 화면의 40%가 여백으로 간다.
|
||||
// 구조물 도면(M02·B08)은 얹은 도각(작도 틀)이 있으면 도각 전체에 맞춘다 — 옛 DXF 가 자기 도곽·
|
||||
// 표제 띠를 품어 범위가 크면 도각이 화면 밖으로 밀린다.
|
||||
const structureFrame = getEntities().filter((e) => e.layerId === STRUCTURE_FRAME_LAYER_ID);
|
||||
const pickable = getPickableEntities();
|
||||
const entities = structureFrame.length
|
||||
? structureFrame
|
||||
: pickable.length
|
||||
? pickable
|
||||
: getEntities();
|
||||
const entities = fitEntities();
|
||||
if (!entities.length) {
|
||||
// 빈 도면 — 원점(0,0)을 화면 가운데로. 안 옮기면 기준점 십자가 왼쪽 아래 귀퉁이에 잘린다.
|
||||
const canvasSize = this.getCanvasSize();
|
||||
@@ -169,31 +157,9 @@ export class ScreenCanvasDrawController implements DrawController {
|
||||
this.viewFitted = true;
|
||||
return;
|
||||
}
|
||||
const boundingBox = getBoundingBoxOfMultipleEntities(entities);
|
||||
const boundingWidth = boundingBox.maxX - boundingBox.minX;
|
||||
const boundingHeight = boundingBox.maxY - boundingBox.minY;
|
||||
const canvasSize = this.getCanvasSize();
|
||||
|
||||
// 10% 여백을 남기고 두 축 중 더 빡빡한 쪽에 맞춘다 (종횡비 유지)
|
||||
const FIT_MARGIN = 0.9;
|
||||
const scaleX =
|
||||
boundingWidth > 0 ? (canvasSize.x * FIT_MARGIN) / boundingWidth : Number.POSITIVE_INFINITY;
|
||||
const scaleY =
|
||||
boundingHeight > 0 ? (canvasSize.y * FIT_MARGIN) / boundingHeight : Number.POSITIVE_INFINITY;
|
||||
let zoomLevel = Math.min(scaleX, scaleY);
|
||||
if (!Number.isFinite(zoomLevel) || zoomLevel <= 0) zoomLevel = 1;
|
||||
this.setScreenScale(zoomLevel);
|
||||
|
||||
// screen = (world - offset) * zoom 이므로, 도면 중심을 화면 중심에 맞추려면
|
||||
// offset = worldCenter - (화면 절반 픽셀) / zoom
|
||||
const worldCenterX = (boundingBox.minX + boundingBox.maxX) / 2;
|
||||
const worldCenterY = (boundingBox.minY + boundingBox.maxY) / 2;
|
||||
this.setScreenOffset(
|
||||
new Point(
|
||||
worldCenterX - canvasSize.x / 2 / zoomLevel,
|
||||
worldCenterY - canvasSize.y / 2 / zoomLevel
|
||||
)
|
||||
);
|
||||
const { zoom, offset } = fitView(entities, this.getCanvasSize());
|
||||
this.setScreenScale(zoom);
|
||||
this.setScreenOffset(offset);
|
||||
this.viewFitted = true;
|
||||
}
|
||||
|
||||
@@ -577,6 +543,8 @@ export class ScreenCanvasDrawController implements DrawController {
|
||||
fontFamily: string;
|
||||
bold: boolean;
|
||||
italic: boolean;
|
||||
/** 색을 그대로 칠함 — 자동색 뒤집기(paintColor) 건너뜀(테마 토큰 색) */
|
||||
rawColor: boolean;
|
||||
}> = {}
|
||||
): void {
|
||||
const opts = {
|
||||
@@ -596,7 +564,7 @@ export class ScreenCanvasDrawController implements DrawController {
|
||||
this.context.rotate(angle);
|
||||
this.context.font = `${opts.italic ? 'italic ' : ''}${opts.bold ? 'bold ' : ''}${opts.fontSize}px ${opts.fontFamily}`;
|
||||
this.context.textAlign = opts.textAlign;
|
||||
this.context.fillStyle = paintColor(opts.textColor);
|
||||
this.context.fillStyle = opts.rawColor ? opts.textColor : paintColor(opts.textColor);
|
||||
this.context.textBaseline = 'middle';
|
||||
this.context.fillText(label, 0, 0);
|
||||
this.context.restore();
|
||||
@@ -673,11 +641,19 @@ export class ScreenCanvasDrawController implements DrawController {
|
||||
* @param width
|
||||
* @param height
|
||||
* @param color
|
||||
* @param rawColor 색을 그대로 칠함 — 자동색 뒤집기(paintColor) 건너뜀(테마 토큰 색)
|
||||
*/
|
||||
public fillRectScreen(xMin: number, yMin: number, width: number, height: number, color: string) {
|
||||
public fillRectScreen(
|
||||
xMin: number,
|
||||
yMin: number,
|
||||
width: number,
|
||||
height: number,
|
||||
color: string,
|
||||
rawColor = false
|
||||
) {
|
||||
if (this.batching) this.flushBatch();
|
||||
// TODO see if we need to replace this with a call to fillPolygon
|
||||
this.context.fillStyle = paintColor(color);
|
||||
this.context.fillStyle = rawColor ? color : paintColor(color);
|
||||
// 화면 좌표는 아래에서 위로 재는 규약이다(getCanvasPoint·drawLineScreen과 같다).
|
||||
// yMin은 사각형의 아래 변이므로 캔버스 y로 옮길 때 높이를 함께 빼야 제자리다.
|
||||
// 안 빼서 선택 그립이 선보다 8px 아래에 찍혔고, 줌을 해도 화면 픽셀 어긋남이라
|
||||
|
||||
@@ -2,13 +2,6 @@ import { Point } from '@flatten-js/core';
|
||||
import { compact, round } from 'es-toolkit';
|
||||
import { Actor } from 'xstate';
|
||||
import {
|
||||
CANVAS_INPUT_FIELD_BACKGROUND_COLOR,
|
||||
CANVAS_INPUT_FIELD_FONT_SIZE,
|
||||
CANVAS_INPUT_FIELD_HEIGHT,
|
||||
CANVAS_INPUT_FIELD_INSTRUCTION_TEXT_COLOR,
|
||||
CANVAS_INPUT_FIELD_MOUSE_OFFSET,
|
||||
CANVAS_INPUT_FIELD_TEXT_COLOR,
|
||||
CANVAS_INPUT_FIELD_WIDTH,
|
||||
PINCH_ZOOM_EXPONENT,
|
||||
SNAP_POINT_DISTANCE,
|
||||
WHEEL_LINE_PX,
|
||||
@@ -66,6 +59,7 @@ import {
|
||||
type TextInputEvent,
|
||||
} from '../tools/tool.types.ts';
|
||||
import { activeToolAcceptsText, ImeSink, isImeKey } from './ime-sink.ts';
|
||||
import { drawInputField } from './input-field-draw.ts';
|
||||
import {
|
||||
ABSOLUTE_POINT_REGEXP,
|
||||
ABSOLUTE_POLAR_REGEXP,
|
||||
@@ -127,36 +121,7 @@ export class InputController {
|
||||
}
|
||||
|
||||
public draw(drawController: ScreenCanvasDrawController) {
|
||||
const screenMouseLocation = drawController.getScreenMouseLocation();
|
||||
|
||||
// draw input field
|
||||
drawController.fillRectScreen(
|
||||
screenMouseLocation.x + CANVAS_INPUT_FIELD_MOUSE_OFFSET,
|
||||
// fillRectScreen이 아래 변 기준으로 바로잡혔으므로, 칸이 있던 자리를 지키려고
|
||||
// 높이만큼 내려 잡는다.
|
||||
screenMouseLocation.y - CANVAS_INPUT_FIELD_MOUSE_OFFSET - CANVAS_INPUT_FIELD_HEIGHT,
|
||||
CANVAS_INPUT_FIELD_WIDTH,
|
||||
CANVAS_INPUT_FIELD_HEIGHT,
|
||||
CANVAS_INPUT_FIELD_BACKGROUND_COLOR
|
||||
);
|
||||
this.ime.sync(activeToolAcceptsText());
|
||||
// Draw text in input field (조합 중인 한글도 함께)
|
||||
if (this.text || this.ime.composing) {
|
||||
drawController.drawTextScreen(
|
||||
this.text + this.ime.composing,
|
||||
new Point(
|
||||
screenMouseLocation.x + CANVAS_INPUT_FIELD_MOUSE_OFFSET + 2,
|
||||
screenMouseLocation.y - CANVAS_INPUT_FIELD_MOUSE_OFFSET - CANVAS_INPUT_FIELD_HEIGHT - 2
|
||||
),
|
||||
{
|
||||
textAlign: 'left',
|
||||
textColor: CANVAS_INPUT_FIELD_TEXT_COLOR,
|
||||
fontSize: CANVAS_INPUT_FIELD_FONT_SIZE,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const matchingCommands = this.getCommandSuggestions();
|
||||
const toolInstruction = getLastStateInstructions();
|
||||
const texts: string[] = [];
|
||||
if (toolInstruction) {
|
||||
@@ -166,11 +131,10 @@ export class InputController {
|
||||
const roundedY = round(drawController.getWorldMouseLocation().y, 2);
|
||||
texts.push(`${roundedX},${roundedY}`);
|
||||
}
|
||||
if (matchingCommands.length) {
|
||||
// 입력 중인 문자로 시작하는 명령 후보. 예: C => CIRCLE (C), COPY (CO)
|
||||
texts.push(...matchingCommands);
|
||||
}
|
||||
this.drawListBelowInputField(drawController, texts);
|
||||
// 입력 중인 문자로 시작하는 명령 후보. 예: C => CIRCLE (C), COPY (CO)
|
||||
texts.push(...this.getCommandSuggestions());
|
||||
// 입력 글(조합 중인 한글도 함께)
|
||||
drawInputField(drawController, this.text + this.ime.composing, texts);
|
||||
}
|
||||
|
||||
public submitText(value: string) {
|
||||
@@ -661,32 +625,6 @@ export class InputController {
|
||||
type: ActorEvent.ESC,
|
||||
});
|
||||
}
|
||||
|
||||
private drawListBelowInputField(
|
||||
drawController: ScreenCanvasDrawController,
|
||||
texts: string[]
|
||||
): void {
|
||||
const screenMouseLocation = drawController.worldToTarget(
|
||||
drawController.getWorldMouseLocation()
|
||||
);
|
||||
const startY =
|
||||
screenMouseLocation.y - CANVAS_INPUT_FIELD_MOUSE_OFFSET - CANVAS_INPUT_FIELD_HEIGHT * 2 - 2;
|
||||
const offsetY = CANVAS_INPUT_FIELD_HEIGHT;
|
||||
texts.forEach((text, index) => {
|
||||
drawController.drawTextScreen(
|
||||
text,
|
||||
new Point(
|
||||
screenMouseLocation.x + CANVAS_INPUT_FIELD_MOUSE_OFFSET + 2,
|
||||
startY - index * offsetY
|
||||
),
|
||||
{
|
||||
textAlign: 'left',
|
||||
textColor: CANVAS_INPUT_FIELD_INSTRUCTION_TEXT_COLOR,
|
||||
fontSize: CANVAS_INPUT_FIELD_FONT_SIZE,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 커서 따라다니는 입력창 — 캔버스에 직접 그림(`input-controller.ts` draw 가 부름).
|
||||
* 칸 · 글은 테마 토큰 색을 그대로 칠함(자동색 뒤집기 건너뜀) — 명령줄 후보 상자(`App.css`
|
||||
* `.cad-command-suggestions`)와 같은 색.
|
||||
*/
|
||||
import { Point } from '@flatten-js/core';
|
||||
import {
|
||||
CANVAS_INPUT_FIELD_BACKGROUND_COLOR,
|
||||
CANVAS_INPUT_FIELD_BORDER_COLOR,
|
||||
CANVAS_INPUT_FIELD_FONT_SIZE,
|
||||
CANVAS_INPUT_FIELD_HEIGHT,
|
||||
CANVAS_INPUT_FIELD_INSTRUCTION_TEXT_COLOR,
|
||||
CANVAS_INPUT_FIELD_MOUSE_OFFSET,
|
||||
CANVAS_INPUT_FIELD_TEXT_COLOR,
|
||||
CANVAS_INPUT_FIELD_WIDTH,
|
||||
} from '../App.consts.ts';
|
||||
import type { ScreenCanvasDrawController } from '../drawControllers/screenCanvas.drawController.ts';
|
||||
import { themeColor } from '../theme.ts';
|
||||
|
||||
/** 입력창 색 — 밝음 = 흰 칸 + 테두리 · 어두움 = 짙은 보라 칸 · 글 = CAD 글 색 */
|
||||
export function inputFieldColors(read: (name: string, fallback: string) => string = themeColor) {
|
||||
return {
|
||||
background: read('--cad-chrome-raised', CANVAS_INPUT_FIELD_BACKGROUND_COLOR),
|
||||
border: read('--cad-line', CANVAS_INPUT_FIELD_BORDER_COLOR),
|
||||
text: read('--cad-text', CANVAS_INPUT_FIELD_TEXT_COLOR),
|
||||
dimText: read('--cad-text-dim', CANVAS_INPUT_FIELD_INSTRUCTION_TEXT_COLOR),
|
||||
};
|
||||
}
|
||||
|
||||
/** 입력 글 칸 + 그 아래 안내 · 좌표 · 명령 후보 줄 */
|
||||
export function drawInputField(
|
||||
drawController: ScreenCanvasDrawController,
|
||||
input: string,
|
||||
lines: string[]
|
||||
): void {
|
||||
const mouse = drawController.getScreenMouseLocation();
|
||||
const colors = inputFieldColors();
|
||||
const left = mouse.x + CANVAS_INPUT_FIELD_MOUSE_OFFSET;
|
||||
// 화면 좌표는 위가 + — fillRectScreen 의 y 는 칸 아래 변
|
||||
const bottom = mouse.y - CANVAS_INPUT_FIELD_MOUSE_OFFSET - CANVAS_INPUT_FIELD_HEIGHT;
|
||||
const width = CANVAS_INPUT_FIELD_WIDTH;
|
||||
drawController.fillRectScreen(
|
||||
left - 1,
|
||||
bottom - 1,
|
||||
width + 2,
|
||||
CANVAS_INPUT_FIELD_HEIGHT + 2,
|
||||
colors.border,
|
||||
true
|
||||
);
|
||||
drawController.fillRectScreen(
|
||||
left,
|
||||
bottom,
|
||||
width,
|
||||
CANVAS_INPUT_FIELD_HEIGHT,
|
||||
colors.background,
|
||||
true
|
||||
);
|
||||
const textOptions = {
|
||||
textAlign: 'left' as const,
|
||||
fontSize: CANVAS_INPUT_FIELD_FONT_SIZE,
|
||||
rawColor: true,
|
||||
};
|
||||
if (input) {
|
||||
drawController.drawTextScreen(input, new Point(left + 2, bottom - 2), {
|
||||
...textOptions,
|
||||
textColor: colors.text,
|
||||
});
|
||||
}
|
||||
lines.forEach((line, index) => {
|
||||
drawController.drawTextScreen(
|
||||
line,
|
||||
new Point(
|
||||
left + 2,
|
||||
bottom - CANVAS_INPUT_FIELD_HEIGHT - 2 - index * CANVAS_INPUT_FIELD_HEIGHT
|
||||
),
|
||||
{ ...textOptions, textColor: colors.dimText }
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -37,4 +37,47 @@ assert.equal(paintColor('#f5f7fa'), '#000000', '밝은 바탕: 기본색은 검
|
||||
assert.equal(paintColor('#ff0000'), '#ff0000', '밝은 바탕: 빨강 그대로');
|
||||
assert.notEqual(paintColor('#ffff00'), '#ffff00', '밝은 바탕: 노랑 등 밝은 색은 대비 보정(기존)');
|
||||
|
||||
// 커서 입력창 — 칸 · 글 색이 두 테마 모두 달라야(자동색 둘을 쓰면 paintColor 가 같은 색으로 뒤집음)
|
||||
const { inputFieldColors } = await import(
|
||||
'../../../B07_DesignDetail/openwebcad/src/inputController/input-field-draw.ts'
|
||||
);
|
||||
const TOKENS: Record<string, Record<string, string>> = {
|
||||
light: {
|
||||
'--cad-chrome-raised': '#ffffff',
|
||||
'--cad-line': '#e6e2e3',
|
||||
'--cad-text': '#333333',
|
||||
'--cad-text-dim': '#615e6e',
|
||||
},
|
||||
dark: {
|
||||
'--cad-chrome-raised': '#251f38',
|
||||
'--cad-line': '#322a45',
|
||||
'--cad-text': '#e4e0f0',
|
||||
'--cad-text-dim': '#b3aec6',
|
||||
},
|
||||
};
|
||||
for (const name of ['light', 'dark']) {
|
||||
theme = name;
|
||||
const colors = inputFieldColors(
|
||||
(token: string, fallback: string) => TOKENS[name][token] ?? fallback
|
||||
);
|
||||
assert.equal(
|
||||
colors.background,
|
||||
TOKENS[name]['--cad-chrome-raised'],
|
||||
`${name}: 칸 = 토큰 색 그대로`
|
||||
);
|
||||
assert.notEqual(colors.background, colors.text, `${name}: 칸 · 글 색 다름`);
|
||||
assert.notEqual(colors.background, colors.dimText, `${name}: 칸 · 안내 글 색 다름`);
|
||||
assert.notEqual(colors.background, colors.border, `${name}: 칸 · 테두리 색 다름`);
|
||||
// 옛 상수 짝(#000000 칸 · #FFF 글)은 paintColor 를 거치면 같은 색 — 입력창은 건너뛰어야
|
||||
assert.equal(
|
||||
paintColor('#000000'),
|
||||
paintColor('#FFF'),
|
||||
`${name}: 자동색 두 개는 같은 색으로 뒤집힘`
|
||||
);
|
||||
}
|
||||
// 토큰 못 읽으면 폴백도 칸 · 글 다름
|
||||
const fallback = inputFieldColors((_token: string, value: string) => value);
|
||||
assert.notEqual(fallback.background, fallback.text, '폴백: 칸 · 글 색 다름');
|
||||
assert.ok(!isAutoColor(fallback.background) && !isAutoColor(fallback.text), '폴백: 자동색 아님');
|
||||
|
||||
console.log('theme 자동색 시험 통과');
|
||||
|
||||
Reference in New Issue
Block a user