All files / app colorSchemePreference.ts

100% Statements 40/40
100% Branches 0/0
100% Functions 0/0
100% Lines 40/40

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102              38x   38x                 38x 38x     38x       38x 38x 38x   1x         38x 38x 38x 4x 3x 2x 2x 12x 12x 7x     2x   2x       38x 38x     167x       3x 3x 3x             191x       1x 1x 1x             1x 1x 1x             112x 112x 672x 672x 672x     112x    
import type { ColorScheme, CustomFaceColors } from '../adapters/three/faceColors';
 
export type { ColorScheme };
 
export type CubeFace = 'U' | 'L' | 'F' | 'R' | 'B' | 'D';
export type CustomColorScheme = Record<CubeFace, string>;
 
export const CUBE_FACES: readonly CubeFace[] = ['U', 'L', 'F', 'R', 'B', 'D'] as const;
 
export const DEFAULT_CUSTOM_COLOR_SCHEME: Readonly<CustomColorScheme> = {
  U: '#ffffff',
  L: '#ff8a2a',
  F: '#78ed3e',
  R: '#ff3131',
  B: '#3568ff',
  D: '#fff34a',
};
 
const STORAGE_KEY = 'regrip-color-scheme';
const CUSTOM_STORAGE_KEY = 'regrip-custom-color-scheme';
 
function isColorScheme(value: string | null): value is ColorScheme {
  return value === 'western' || value === 'japanese' || value === 'custom';
}
 
function loadInitialColorScheme(): ColorScheme {
  try {
    const stored = localStorage.getItem(STORAGE_KEY);
    return isColorScheme(stored) ? stored : 'western';
  } catch {
    return 'western';
  }
}
 
function loadInitialCustomColorScheme(): CustomColorScheme {
  try {
    const stored = localStorage.getItem(CUSTOM_STORAGE_KEY);
    if (!stored) return { ...DEFAULT_CUSTOM_COLOR_SCHEME };
    const parsed = JSON.parse(stored) as unknown;
    if (typeof parsed !== 'object' || parsed === null) return { ...DEFAULT_CUSTOM_COLOR_SCHEME };
    const scheme: CustomColorScheme = { ...DEFAULT_CUSTOM_COLOR_SCHEME };
    for (const face of CUBE_FACES) {
      const val = (parsed as Record<string, unknown>)[face];
      if (typeof val === 'string' && /^#[0-9a-fA-F]{6}$/.test(val)) {
        scheme[face] = val.toLowerCase();
      }
    }
    return scheme;
  } catch {
    return { ...DEFAULT_CUSTOM_COLOR_SCHEME };
  }
}
 
let current: ColorScheme = loadInitialColorScheme();
let currentCustom: CustomColorScheme = loadInitialCustomColorScheme();
 
export function getColorScheme(): ColorScheme {
  return current;
}
 
export function setColorScheme(scheme: ColorScheme): void {
  current = scheme;
  try {
    localStorage.setItem(STORAGE_KEY, scheme);
  } catch {
    // Private browsing or a full quota still keeps the in-memory selection.
  }
}
 
export function getCustomColorScheme(): CustomColorScheme {
  return { ...currentCustom };
}
 
export function setCustomColorScheme(updates: Partial<CustomColorScheme>): void {
  currentCustom = { ...currentCustom, ...updates };
  try {
    localStorage.setItem(CUSTOM_STORAGE_KEY, JSON.stringify(currentCustom));
  } catch {
    // Keep in-memory selection.
  }
}
 
export function resetCustomColorScheme(): void {
  currentCustom = { ...DEFAULT_CUSTOM_COLOR_SCHEME };
  try {
    localStorage.removeItem(CUSTOM_STORAGE_KEY);
  } catch {
    // Keep in-memory selection.
  }
}
 
export function customColorsToNumeric(custom: CustomColorScheme): CustomFaceColors {
  const result: Record<string, number> = {};
  for (const face of CUBE_FACES) {
    const hex = custom[face];
    if (hex) {
      result[face] = parseInt(hex.replace('#', ''), 16);
    }
  }
  return result;
}