Files
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

73 lines
2.4 KiB
TypeScript

import React, { useEffect } from 'react';
import { useOsStateStore } from '../OsStateStore';
import { displayScaleFromPct, fontScaleFromPct } from '../managers/DisplayManager';
function clamp01(n: number): number {
if (!Number.isFinite(n)) return 0;
return Math.min(1, Math.max(0, n));
}
function computeBrightnessFactor(brightnessPct: number): number {
// Keep a minimum so the UI never becomes fully black.
const p = clamp01(brightnessPct / 100);
return 0.25 + p * 0.75; // 0.25..1.0
}
function buildFilter(brightness: number, eyeComfortEnabled: boolean, eyeComfortLevel: number): string {
const parts: string[] = [];
const brightnessFactor = computeBrightnessFactor(brightness);
parts.push(`brightness(${brightnessFactor})`);
if (eyeComfortEnabled) {
const level = clamp01(eyeComfortLevel / 100);
// Warmth / blue-light reduction approximation
const sepia = 0.45 * level;
const sat = 1.05 + 0.35 * level;
const hue = -12 * level;
parts.push(`sepia(${sepia})`);
parts.push(`saturate(${sat})`);
parts.push(`hue-rotate(${hue}deg)`);
}
return parts.join(' ');
}
function applyEffects(state: ReturnType<typeof useOsStateStore.getState>) {
// Apply on the mount root so the whole simulated OS is affected.
const root = document.getElementById('root');
if (root) {
root.style.filter = buildFilter(
state.settings.system.brightness,
state.settings.global.eyeComfortEnabled,
state.settings.system.eyeComfortLevel,
);
}
// Font scaling: affects rem-based styles (safe, low risk).
const basePx = 16;
const fontScale = Number.isFinite(fontScaleFromPct(state.settings.system.fontSizePct))
? fontScaleFromPct(state.settings.system.fontSizePct)
: 1;
document.documentElement.style.fontSize = `${Math.max(10, Math.min(22, basePx * fontScale))}px`;
// Dark mode flag: exposed for future CSS adjustments.
document.documentElement.classList.toggle('os-dark', Boolean(state.settings.global.darkModeEnabled));
// Hint native controls (inputs, scrollbars, etc.)
document.documentElement.style.colorScheme = state.settings.global.darkModeEnabled ? 'dark' : 'light';
document.documentElement.style.setProperty('--os-display-scale', String(displayScaleFromPct(state.settings.system.displaySizePct)));
}
export const DeviceEffects: React.FC = () => {
const state = useOsStateStore((s) => s);
useEffect(() => {
applyEffects(state);
}, [state]);
return null;
};
export default DeviceEffects;