From 93036afa2e9e91151c75105b111457112e50e288 Mon Sep 17 00:00:00 2001 From: Ghxst Date: Thu, 21 May 2026 13:18:32 +0200 Subject: [PATCH] Fix sketch default color in dark mode (#2488) Co-authored-by: Ghxst <200635707+GHX5T-SOL@users.noreply.github.com> --- apps/web/src/components/SketchEditor.tsx | 3 +- apps/web/src/components/sketch-colors.ts | 18 +++++ .../SketchEditor.default-color.test.tsx | 68 +++++++++++++++++++ .../tests/components/sketch-colors.test.ts | 24 +++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/components/sketch-colors.ts create mode 100644 apps/web/tests/components/SketchEditor.default-color.test.tsx create mode 100644 apps/web/tests/components/sketch-colors.test.ts diff --git a/apps/web/src/components/SketchEditor.tsx b/apps/web/src/components/SketchEditor.tsx index 38dfdb2e65..6c086b9705 100644 --- a/apps/web/src/components/SketchEditor.tsx +++ b/apps/web/src/components/SketchEditor.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { useT } from '../i18n'; +import { readDefaultSketchToolColor } from './sketch-colors'; import type { SketchItem } from './sketch-model'; export type Tool = 'select' | 'pen' | 'text' | 'rect' | 'arrow' | 'eraser'; @@ -34,7 +35,7 @@ export function SketchEditor({ const canvasRef = useRef(null); const wrapRef = useRef(null); const [tool, setTool] = useState('pen'); - const [color, setColor] = useState('#1c1b1a'); + const [color, setColor] = useState(() => readDefaultSketchToolColor()); const [size, setSize] = useState(2); const drawingRef = useRef(null); const [, force] = useState(0); diff --git a/apps/web/src/components/sketch-colors.ts b/apps/web/src/components/sketch-colors.ts new file mode 100644 index 0000000000..056d191a8c --- /dev/null +++ b/apps/web/src/components/sketch-colors.ts @@ -0,0 +1,18 @@ +export const DEFAULT_SKETCH_LIGHT_TOOL_COLOR = '#1c1b1a'; +export const DEFAULT_SKETCH_DARK_TOOL_COLOR = '#ffffff'; + +export function resolveDefaultSketchToolColor( + theme: string | null, + prefersDark: boolean, +): string { + if (theme === 'dark') return DEFAULT_SKETCH_DARK_TOOL_COLOR; + if (theme === 'light') return DEFAULT_SKETCH_LIGHT_TOOL_COLOR; + return prefersDark ? DEFAULT_SKETCH_DARK_TOOL_COLOR : DEFAULT_SKETCH_LIGHT_TOOL_COLOR; +} + +export function readDefaultSketchToolColor(): string { + if (typeof document === 'undefined') return DEFAULT_SKETCH_LIGHT_TOOL_COLOR; + const theme = document.documentElement.getAttribute('data-theme'); + const prefersDark = window.matchMedia?.('(prefers-color-scheme: dark)').matches === true; + return resolveDefaultSketchToolColor(theme, prefersDark); +} diff --git a/apps/web/tests/components/SketchEditor.default-color.test.tsx b/apps/web/tests/components/SketchEditor.default-color.test.tsx new file mode 100644 index 0000000000..b3c6eac6c1 --- /dev/null +++ b/apps/web/tests/components/SketchEditor.default-color.test.tsx @@ -0,0 +1,68 @@ +// @vitest-environment jsdom + +import { cleanup, render } from '@testing-library/react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { SketchEditor } from '../../src/components/SketchEditor'; +import { I18nProvider } from '../../src/i18n'; + +class ResizeObserverMock { + observe() {} + disconnect() {} +} + +function renderEditor() { + return render( + + + , + ); +} + +describe('SketchEditor default color', () => { + beforeEach(() => { + vi.stubGlobal('ResizeObserver', ResizeObserverMock); + vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue({ + setTransform: vi.fn(), + clearRect: vi.fn(), + save: vi.fn(), + fillRect: vi.fn(), + restore: vi.fn(), + beginPath: vi.fn(), + moveTo: vi.fn(), + lineTo: vi.fn(), + stroke: vi.fn(), + strokeRect: vi.fn(), + fillText: vi.fn(), + } as unknown as CanvasRenderingContext2D); + }); + + afterEach(() => { + cleanup(); + document.documentElement.removeAttribute('data-theme'); + vi.restoreAllMocks(); + vi.unstubAllGlobals(); + }); + + it('starts with a light pen color under the dark theme', () => { + document.documentElement.setAttribute('data-theme', 'dark'); + + const { container } = renderEditor(); + + const colorInput = container.querySelector('input[type="color"]'); + expect(colorInput?.value).toBe('#ffffff'); + }); + + it('keeps the existing default pen color under the light theme', () => { + document.documentElement.setAttribute('data-theme', 'light'); + + const { container } = renderEditor(); + + const colorInput = container.querySelector('input[type="color"]'); + expect(colorInput?.value).toBe('#1c1b1a'); + }); +}); diff --git a/apps/web/tests/components/sketch-colors.test.ts b/apps/web/tests/components/sketch-colors.test.ts new file mode 100644 index 0000000000..c62294c496 --- /dev/null +++ b/apps/web/tests/components/sketch-colors.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from 'vitest'; +import { + DEFAULT_SKETCH_DARK_TOOL_COLOR, + DEFAULT_SKETCH_LIGHT_TOOL_COLOR, + resolveDefaultSketchToolColor, +} from '../../src/components/sketch-colors'; + +describe('sketch-colors', () => { + it('uses a light tool color for an explicit dark theme', () => { + expect(resolveDefaultSketchToolColor('dark', false)).toBe(DEFAULT_SKETCH_DARK_TOOL_COLOR); + }); + + it('keeps the existing dark tool color for an explicit light theme', () => { + expect(resolveDefaultSketchToolColor('light', true)).toBe(DEFAULT_SKETCH_LIGHT_TOOL_COLOR); + }); + + it('follows the system dark preference when no explicit theme is set', () => { + expect(resolveDefaultSketchToolColor(null, true)).toBe(DEFAULT_SKETCH_DARK_TOOL_COLOR); + }); + + it('keeps the light-mode default when system mode is not dark', () => { + expect(resolveDefaultSketchToolColor(null, false)).toBe(DEFAULT_SKETCH_LIGHT_TOOL_COLOR); + }); +});