LCOV - code coverage report
Current view: top level - dashql-app/src/config - color_scheme.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 90.7 % 54 49
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : 
       3              : import { useAppConfig, useAppReconfigure } from './app_config.js';
       4              : 
       5              : /// The color scheme of the app, see docs/design/app_theme.md
       6              : export type ColorScheme = 'light' | 'dark';
       7              : 
       8            1 : const DARK_QUERY = '(prefers-color-scheme: dark)';
       9              : 
      10              : /// The color scheme of the app settings, null to follow the OS
      11            1 : let currentSetting: ColorScheme | null = null;
      12            1 : const listeners = new Set<() => void>();
      13              : 
      14              : /// Pin the document to the color scheme of the app settings, null follows the OS.
      15              : /// Tells the native window of Electron too.
      16            1 : export function applyColorSchemeToDocument(setting: ColorScheme | null, doc: Document = document) {
      17            1 :     if (setting == null) {
      18            1 :         delete doc.documentElement.dataset.colorScheme;
      19              :     } else {
      20            1 :         doc.documentElement.dataset.colorScheme = setting;
      21              :     }
      22            1 :     globalThis.dashqlElectron?.setColorScheme?.(setting ?? 'system').catch(() => { });
      23            1 :     if (setting !== currentSetting) {
      24            1 :         currentSetting = setting;
      25            1 :         for (const listener of listeners) {
      26            1 :             listener();
      27              :         }
      28            1 :     }
      29            1 : }
      30              : 
      31            1 : function subscribe(listener: () => void): () => void {
      32            1 :     listeners.add(listener);
      33            1 :     const media = globalThis.matchMedia?.(DARK_QUERY);
      34            1 :     media?.addEventListener('change', listener);
      35            1 :     return () => {
      36            1 :         listeners.delete(listener);
      37            1 :         media?.removeEventListener('change', listener);
      38            1 :     };
      39            1 : }
      40              : 
      41            1 : function resolveColorScheme(): ColorScheme {
      42            1 :     return currentSetting ?? (globalThis.matchMedia?.(DARK_QUERY).matches ? 'dark' : 'light');
      43            1 : }
      44              : 
      45              : /// The color scheme the document shows: the one of the app settings, or the one of the OS.
      46              : /// Renders again when it changes, for colors that are not CSS, like the colors of charts drawn on a canvas.
      47            1 : export function useColorScheme(): ColorScheme {
      48            1 :     return React.useSyncExternalStore(subscribe, resolveColorScheme, () => 'light');
      49            1 : }
      50              : 
      51              : /// Switch to the other color scheme and pin it in the app settings
      52            1 : export function useColorSchemeToggle(): [ColorScheme, () => void] {
      53            1 :     const scheme = useColorScheme();
      54            1 :     const reconfigure = useAppReconfigure();
      55            1 :     const toggle = React.useCallback(() => {
      56            1 :         const next: ColorScheme = resolveColorScheme() === 'dark' ? 'light' : 'dark';
      57            1 :         reconfigure(value => value == null ? null : {
      58            1 :             ...value,
      59            1 :             settings: { ...value.settings, colorScheme: next },
      60            1 :         });
      61            1 :     }, [reconfigure]);
      62            1 :     return [scheme, toggle];
      63            1 : }
      64              : 
      65              : /// The color scheme of the app settings, null follows the OS, and a setter of it
      66            1 : export function useColorSchemeSetting(): [ColorScheme | null, (setting: ColorScheme | null) => void] {
      67            1 :     const config = useAppConfig();
      68            1 :     const reconfigure = useAppReconfigure();
      69            1 :     const setting = config?.settings?.colorScheme ?? null;
      70            1 :     const set = React.useCallback((next: ColorScheme | null) => {
      71            0 :         reconfigure(value => {
      72            0 :             if (value == null) return null;
      73            0 :             const { colorScheme: _colorScheme, ...settings } = value.settings ?? {};
      74            0 :             return { ...value, settings: next == null ? settings : { ...settings, colorScheme: next } };
      75            0 :         });
      76            1 :     }, [reconfigure]);
      77            1 :     return [setting, set];
      78            1 : }
        

Generated by: LCOV version 2.0-1