mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-08-28 09:11:06 +00:00
feat: light mode variations for the loading screen
This commit is contained in:
@@ -619,6 +619,18 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-xl font-bold">Delay loading screen</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Keep the loading overlay visible for 5 extra seconds so you can preview canvas variants.</p>
|
||||
</div>
|
||||
<div>
|
||||
<Switch
|
||||
state={$settingsState.devDelayLoadingScreen ?? false}
|
||||
onChange={(isOn: boolean) => settingsState.devDelayLoadingScreen = isOn}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-xl font-bold">Sensitive Hider</h2>
|
||||
|
||||
@@ -20,6 +20,7 @@ import { updateAllColors } from "@/seqta/ui/colors/Manager";
|
||||
import { applySelectedFont } from "@/seqta/ui/fonts/Manager";
|
||||
import { verboseInfo, verboseLog } from "@/utils/verboseLog";
|
||||
import loading, { stopLoadingAnimation } from "@/seqta/ui/Loading";
|
||||
import { loadingScreenHoldMs } from "@/seqta/ui/loadingHold";
|
||||
import { SendNewsPage } from "@/seqta/utils/SendNewsPage";
|
||||
import { getEngageRoutePage } from "@/seqta/utils/engageRoute";
|
||||
import {
|
||||
@@ -92,6 +93,9 @@ export async function finishLoad() {
|
||||
|
||||
document.querySelector(".legacy-root")?.classList.remove("hidden");
|
||||
|
||||
const holdMs = loadingScreenHoldMs(settingsState);
|
||||
if (holdMs > 0) await delay(holdMs);
|
||||
|
||||
const loadingbk = document.getElementById("loading");
|
||||
loadingbk?.classList.add("closeLoading");
|
||||
stopLoadingAnimation();
|
||||
|
||||
+43
-18
@@ -6,6 +6,7 @@ import loadingOverlay from "./loading-overlay.html?raw";
|
||||
import { startLoadingCanvas } from "./loadingCanvas";
|
||||
import {
|
||||
pickLoadingVariant,
|
||||
resolveLoadingTheme,
|
||||
type LoadingVariant,
|
||||
} from "./loadingVariants";
|
||||
|
||||
@@ -25,26 +26,42 @@ const THEME_GUARD_KEYS = [
|
||||
"selectedFont",
|
||||
] as const;
|
||||
|
||||
const LOADING_PALETTE = {
|
||||
const LOADING_PALETTE_DARK = {
|
||||
line: "rgba(255, 255, 255, 0.08)",
|
||||
lineAccent: "rgba(255, 255, 255, 0.15)",
|
||||
lineBlue: "rgba(96, 165, 250, 0.42)",
|
||||
grid: "rgba(255, 255, 255, 0.028)",
|
||||
text: "#f4f4f5",
|
||||
version: "rgba(255, 255, 255, 0.35)",
|
||||
} as const;
|
||||
|
||||
const LOADING_PALETTE_LIGHT = {
|
||||
line: "rgba(24, 24, 27, 0.1)",
|
||||
lineAccent: "rgba(24, 24, 27, 0.18)",
|
||||
lineBlue: "rgba(37, 99, 235, 0.5)",
|
||||
grid: "rgba(24, 24, 27, 0.055)",
|
||||
text: "#18181b",
|
||||
version: "rgba(24, 24, 27, 0.42)",
|
||||
} as const;
|
||||
|
||||
function loadingPalette(darkMode: boolean) {
|
||||
return darkMode ? LOADING_PALETTE_DARK : LOADING_PALETTE_LIGHT;
|
||||
}
|
||||
|
||||
const loadingStyles = /* css */ `
|
||||
:host {
|
||||
--bk-line-color: ${LOADING_PALETTE.line};
|
||||
--bk-line-accent: ${LOADING_PALETTE.lineAccent};
|
||||
--bk-line-blue: ${LOADING_PALETTE.lineBlue};
|
||||
--bk-grid-color: ${LOADING_PALETTE.grid};
|
||||
--bk-line-color: ${LOADING_PALETTE_DARK.line};
|
||||
--bk-line-accent: ${LOADING_PALETTE_DARK.lineAccent};
|
||||
--bk-line-blue: ${LOADING_PALETTE_DARK.lineBlue};
|
||||
--bk-grid-color: ${LOADING_PALETTE_DARK.grid};
|
||||
--bk-spin-outer: 1s;
|
||||
--bk-spin-inner: 3s;
|
||||
--bk-spin-small: 3s;
|
||||
--bk-stage-name: bkloading-stage-in;
|
||||
--bk-stage-duration: 0.85s;
|
||||
color: ${LOADING_PALETTE.text};
|
||||
--bk-vignette-fill: radial-gradient(ellipse at center, transparent 32%, rgba(0, 0, 0, 0.75) 100%);
|
||||
--bk-version-color: ${LOADING_PALETTE_DARK.version};
|
||||
color: ${LOADING_PALETTE_DARK.text};
|
||||
opacity: 1;
|
||||
transition: opacity 0.85s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
@@ -66,7 +83,7 @@ const loadingStyles = /* css */ `
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background: radial-gradient(ellipse at center, transparent 32%, rgba(0, 0, 0, 0.75) 100%);
|
||||
background: var(--bk-vignette-fill);
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
@@ -153,7 +170,7 @@ const loadingStyles = /* css */ `
|
||||
bottom: 10px;
|
||||
font-size: 0.95rem;
|
||||
letter-spacing: 0.02em;
|
||||
color: rgba(255, 255, 255, 0.35);
|
||||
color: var(--bk-version-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -199,7 +216,8 @@ function overlayWithSpinner(): string {
|
||||
);
|
||||
}
|
||||
|
||||
function applyHostShell(host: HTMLElement) {
|
||||
function applyHostShell(host: HTMLElement, darkMode: boolean) {
|
||||
const palette = loadingPalette(darkMode);
|
||||
const shell = [
|
||||
["position", "fixed"],
|
||||
["inset", "0"],
|
||||
@@ -209,11 +227,12 @@ function applyHostShell(host: HTMLElement) {
|
||||
["contain", "strict"],
|
||||
["visibility", "visible"],
|
||||
["pointer-events", "auto"],
|
||||
["color", LOADING_PALETTE.text],
|
||||
["--bk-line-color", LOADING_PALETTE.line],
|
||||
["--bk-line-accent", LOADING_PALETTE.lineAccent],
|
||||
["--bk-line-blue", LOADING_PALETTE.lineBlue],
|
||||
["--bk-grid-color", LOADING_PALETTE.grid],
|
||||
["color", palette.text],
|
||||
["--bk-line-color", palette.line],
|
||||
["--bk-line-accent", palette.lineAccent],
|
||||
["--bk-line-blue", palette.lineBlue],
|
||||
["--bk-grid-color", palette.grid],
|
||||
["--bk-version-color", palette.version],
|
||||
] as const;
|
||||
|
||||
for (const [prop, value] of shell) {
|
||||
@@ -226,18 +245,24 @@ function applyVariantTheme(
|
||||
shadow: ShadowRoot,
|
||||
variant: LoadingVariant,
|
||||
) {
|
||||
const { theme } = variant;
|
||||
const darkMode = !!settingsState.DarkMode;
|
||||
const theme = resolveLoadingTheme(variant, darkMode);
|
||||
host.dataset.variant = variant.id;
|
||||
applyHostShell(host);
|
||||
host.dataset.scheme = darkMode ? "dark" : "light";
|
||||
applyHostShell(host, darkMode);
|
||||
host.style.setProperty("background", theme.background, "important");
|
||||
host.style.setProperty("--bk-spin-outer", theme.spinOuter, "important");
|
||||
host.style.setProperty("--bk-spin-inner", theme.spinInner, "important");
|
||||
host.style.setProperty("--bk-spin-small", theme.spinSmall, "important");
|
||||
host.style.setProperty("--bk-stage-name", theme.stageName, "important");
|
||||
host.style.setProperty("--bk-spin-small", theme.spinSmall, "important");
|
||||
host.style.setProperty("--bk-stage-duration", theme.stageDuration, "important");
|
||||
host.style.setProperty("--bk-vignette-fill", theme.vignetteFill, "important");
|
||||
|
||||
const vignette = shadow.querySelector(".bkloading__vignette") as HTMLElement | null;
|
||||
if (vignette) vignette.style.opacity = String(theme.vignetteOpacity);
|
||||
if (vignette) {
|
||||
vignette.style.opacity = String(theme.vignetteOpacity);
|
||||
vignette.style.background = theme.vignetteFill;
|
||||
}
|
||||
}
|
||||
|
||||
function refreshActiveLoadingTheme() {
|
||||
|
||||
@@ -78,8 +78,15 @@ function cycle(
|
||||
return { t: fadeT, fade: true, ambient: smooth((len - tick) / revealMs) };
|
||||
}
|
||||
|
||||
function cssVar(_root: HTMLElement, _name: string, fallback: string): string {
|
||||
return fallback;
|
||||
function cssVar(root: HTMLElement, name: string, fallback: string): string {
|
||||
const inline = root.style.getPropertyValue(name).trim();
|
||||
if (inline) return inline;
|
||||
const computed = getComputedStyle(root).getPropertyValue(name).trim();
|
||||
return computed || fallback;
|
||||
}
|
||||
|
||||
function isLightScheme(root: HTMLElement): boolean {
|
||||
return root.dataset.scheme === "light";
|
||||
}
|
||||
|
||||
function drawGrid(
|
||||
@@ -122,9 +129,11 @@ function drawSoftBlobs(
|
||||
blur: number,
|
||||
fadeMs: number,
|
||||
tide: boolean,
|
||||
light: boolean,
|
||||
) {
|
||||
const time = reduced ? 0 : elapsed * 0.001;
|
||||
const fadeIn = reduced ? 1 : smooth(Math.min(1, elapsed / fadeMs));
|
||||
const alphaBoost = light ? 1.18 : 1;
|
||||
|
||||
ctx.save();
|
||||
ctx.globalAlpha = fadeIn;
|
||||
@@ -145,8 +154,8 @@ function drawSoftBlobs(
|
||||
y = b.cy * h + Math.sin(t * 0.48 + b.phase) * h * 0.025;
|
||||
rx = b.rx * w;
|
||||
ry = b.ry * h * breathe;
|
||||
a0 = 0.38;
|
||||
a1 = 0.14;
|
||||
a0 = 0.38 * alphaBoost;
|
||||
a1 = 0.14 * alphaBoost;
|
||||
} else {
|
||||
const amp = b.amp ?? 0.2;
|
||||
const scale = 1 + Math.sin(t * 1.35) * amp;
|
||||
@@ -155,8 +164,8 @@ function drawSoftBlobs(
|
||||
y = b.cy * h + Math.sin(t * 0.41) * h * 0.01;
|
||||
rx = b.rx * w * scale;
|
||||
ry = b.ry * h * stretch;
|
||||
a0 = 0.5;
|
||||
a1 = 0.18;
|
||||
a0 = 0.5 * alphaBoost;
|
||||
a1 = 0.18 * alphaBoost;
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
ctx.rotate(t * (b.rot ?? 0));
|
||||
@@ -187,6 +196,7 @@ function drawDotGlobe(
|
||||
phaseElapsed: number,
|
||||
fade: boolean,
|
||||
ambient: number,
|
||||
light: boolean,
|
||||
) {
|
||||
const time = reduced ? 0 : elapsed * 0.001;
|
||||
const cx = w * 0.5;
|
||||
@@ -206,8 +216,8 @@ function drawDotGlobe(
|
||||
ctx.save();
|
||||
ctx.globalAlpha = ambient;
|
||||
const glow = ctx.createRadialGradient(cx, cy, 0, cx, cy, radius * 1.15);
|
||||
glow.addColorStop(0, "rgba(37, 99, 235, 0.1)");
|
||||
glow.addColorStop(0.55, "rgba(37, 99, 235, 0.03)");
|
||||
glow.addColorStop(0, light ? "rgba(37, 99, 235, 0.16)" : "rgba(37, 99, 235, 0.1)");
|
||||
glow.addColorStop(0.55, light ? "rgba(37, 99, 235, 0.05)" : "rgba(37, 99, 235, 0.03)");
|
||||
glow.addColorStop(1, "rgba(37, 99, 235, 0)");
|
||||
ctx.fillStyle = glow;
|
||||
ctx.fillRect(cx - radius * 1.2, cy - radius * 1.2, radius * 2.4, radius * 2.4);
|
||||
@@ -256,8 +266,11 @@ function drawDotGlobe(
|
||||
const size = (1.2 + depth * 2.2) * dot.p;
|
||||
const alpha = (0.12 + brightness * 0.75) * dot.p;
|
||||
|
||||
ctx.fillStyle =
|
||||
brightness > 0.52
|
||||
ctx.fillStyle = light
|
||||
? brightness > 0.52
|
||||
? `rgba(29, 78, 216, ${alpha})`
|
||||
: `rgba(24, 24, 27, ${0.1 + brightness * 0.28 * dot.p})`
|
||||
: brightness > 0.52
|
||||
? `rgba(96, 165, 250, ${alpha})`
|
||||
: `rgba(255, 255, 255, ${0.08 + brightness * 0.22 * dot.p})`;
|
||||
ctx.beginPath();
|
||||
@@ -303,10 +316,11 @@ export function startLoadingCanvas(
|
||||
|
||||
const elapsed = now - start;
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
const light = isLightScheme(root);
|
||||
|
||||
if (visual === "globe") {
|
||||
const { t, fade, ambient } = cycle(elapsed, revealMs, holdMs);
|
||||
drawDotGlobe(ctx, w, h, elapsed, reduced, t, fade, reduced ? 1 : ambient);
|
||||
drawDotGlobe(ctx, w, h, elapsed, reduced, t, fade, reduced ? 1 : ambient, light);
|
||||
} else if (visual === "blobs") {
|
||||
const tide = variant.blobStyle === "tide";
|
||||
drawSoftBlobs(
|
||||
@@ -319,6 +333,7 @@ export function startLoadingCanvas(
|
||||
tide ? 58 : 52,
|
||||
tide ? 1800 : 1600,
|
||||
tide,
|
||||
light,
|
||||
);
|
||||
} else {
|
||||
const { t, fade } = cycle(elapsed, revealMs, holdMs);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { loadingScreenHoldMs } from "./loadingHold";
|
||||
|
||||
describe("loadingScreenHoldMs", () => {
|
||||
it("holds 5 seconds when developer mode and the delay option are both on", () => {
|
||||
expect(
|
||||
loadingScreenHoldMs({
|
||||
devMode: true,
|
||||
devDelayLoadingScreen: true,
|
||||
}),
|
||||
).toBe(5000);
|
||||
});
|
||||
|
||||
it("does not hold when developer mode is off", () => {
|
||||
expect(
|
||||
loadingScreenHoldMs({
|
||||
devMode: false,
|
||||
devDelayLoadingScreen: true,
|
||||
}),
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
it("does not hold when the delay option is off", () => {
|
||||
expect(
|
||||
loadingScreenHoldMs({
|
||||
devMode: true,
|
||||
devDelayLoadingScreen: false,
|
||||
}),
|
||||
).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
export const LOADING_SCREEN_DEBUG_DELAY_MS = 5000;
|
||||
|
||||
export function loadingScreenHoldMs(settings: {
|
||||
devMode?: boolean;
|
||||
devDelayLoadingScreen?: boolean;
|
||||
}): number {
|
||||
return settings.devMode && settings.devDelayLoadingScreen
|
||||
? LOADING_SCREEN_DEBUG_DELAY_MS
|
||||
: 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
listLoadingVariants,
|
||||
resolveLoadingTheme,
|
||||
} from "./loadingVariants";
|
||||
|
||||
describe("loading variant light themes", () => {
|
||||
it("defines a distinct light theme for every canvas variant", () => {
|
||||
const variants = listLoadingVariants();
|
||||
expect(variants.length).toBeGreaterThan(0);
|
||||
|
||||
for (const variant of variants) {
|
||||
expect(variant.lightTheme.background).not.toBe(variant.theme.background);
|
||||
expect(variant.lightTheme.background).not.toMatch(/#0[148]0[148]0[148]/);
|
||||
expect(variant.lightTheme.vignetteFill).not.toBe(variant.theme.vignetteFill);
|
||||
}
|
||||
});
|
||||
|
||||
it("resolves dark theme when dark mode is on and light theme when it is off", () => {
|
||||
const sweep = listLoadingVariants().find((variant) => variant.id === "sweep");
|
||||
expect(sweep).toBeDefined();
|
||||
expect(resolveLoadingTheme(sweep!, true)).toEqual(sweep!.theme);
|
||||
expect(resolveLoadingTheme(sweep!, false)).toEqual(sweep!.lightTheme);
|
||||
});
|
||||
});
|
||||
@@ -11,6 +11,17 @@ export type Line = {
|
||||
) => void;
|
||||
};
|
||||
|
||||
export type LoadingTheme = {
|
||||
background: string;
|
||||
vignetteOpacity: number;
|
||||
vignetteFill: string;
|
||||
spinOuter: string;
|
||||
spinInner: string;
|
||||
spinSmall: string;
|
||||
stageName: string;
|
||||
stageDuration: string;
|
||||
};
|
||||
|
||||
export type LoadingVariant = {
|
||||
id: string;
|
||||
visual?: "lines" | "blobs" | "globe";
|
||||
@@ -18,15 +29,8 @@ export type LoadingVariant = {
|
||||
holdMs: number;
|
||||
grid: { cols: number; rows: number };
|
||||
lines: Line[];
|
||||
theme: {
|
||||
background: string;
|
||||
vignetteOpacity: number;
|
||||
spinOuter: string;
|
||||
spinInner: string;
|
||||
spinSmall: string;
|
||||
stageName: string;
|
||||
stageDuration: string;
|
||||
};
|
||||
theme: LoadingTheme;
|
||||
lightTheme: LoadingTheme;
|
||||
};
|
||||
|
||||
const curve = (
|
||||
@@ -60,7 +64,13 @@ const segment = (x1: number, y1: number, x2: number, y2: number): Line["stroke"]
|
||||
};
|
||||
|
||||
const BASE = "linear-gradient(180deg, #010101 0%, #040404 50%, #080808 100%)";
|
||||
const BASE_LIGHT =
|
||||
"linear-gradient(180deg, #fafafa 0%, #f4f4f5 50%, #ececef 100%)";
|
||||
const STAGE = "bkloading-stage-in";
|
||||
const DARK_VIGNETTE =
|
||||
"radial-gradient(ellipse at center, transparent 32%, rgba(0, 0, 0, 0.75) 100%)";
|
||||
const LIGHT_VIGNETTE =
|
||||
"radial-gradient(ellipse at center, transparent 32%, rgba(250, 250, 250, 0.88) 100%)";
|
||||
|
||||
function theme(
|
||||
background: string,
|
||||
@@ -69,10 +79,12 @@ function theme(
|
||||
spinInner: string,
|
||||
spinSmall: string,
|
||||
stageDuration: string,
|
||||
) {
|
||||
light = false,
|
||||
): LoadingTheme {
|
||||
return {
|
||||
background,
|
||||
vignetteOpacity,
|
||||
vignetteFill: light ? LIGHT_VIGNETTE : DARK_VIGNETTE,
|
||||
spinOuter,
|
||||
spinInner,
|
||||
spinSmall,
|
||||
@@ -85,7 +97,8 @@ function canvasVariant(
|
||||
id: string,
|
||||
visual: "blobs" | "globe",
|
||||
holdMs: number,
|
||||
t: ReturnType<typeof theme>,
|
||||
t: LoadingTheme,
|
||||
lightTheme: LoadingTheme,
|
||||
blobStyle?: "diffuse" | "tide",
|
||||
): LoadingVariant {
|
||||
return {
|
||||
@@ -96,9 +109,17 @@ function canvasVariant(
|
||||
grid: { cols: 0, rows: 0 },
|
||||
lines: [],
|
||||
theme: t,
|
||||
lightTheme,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveLoadingTheme(
|
||||
variant: LoadingVariant,
|
||||
darkMode: boolean,
|
||||
): LoadingTheme {
|
||||
return darkMode ? variant.theme : variant.lightTheme;
|
||||
}
|
||||
|
||||
const VARIANTS: LoadingVariant[] = [
|
||||
{
|
||||
id: "sweep",
|
||||
@@ -112,6 +133,15 @@ const VARIANTS: LoadingVariant[] = [
|
||||
"3s",
|
||||
"0.85s",
|
||||
),
|
||||
lightTheme: theme(
|
||||
`radial-gradient(ellipse 70% 45% at 50% 95%, rgba(37, 99, 235, 0.16), transparent 62%), ${BASE_LIGHT}`,
|
||||
0.72,
|
||||
"1s",
|
||||
"3s",
|
||||
"3s",
|
||||
"0.85s",
|
||||
true,
|
||||
),
|
||||
lines: [
|
||||
{
|
||||
delay: 0,
|
||||
@@ -177,6 +207,15 @@ const VARIANTS: LoadingVariant[] = [
|
||||
"2.6s",
|
||||
"0.75s",
|
||||
),
|
||||
lightTheme: theme(
|
||||
`radial-gradient(ellipse 40% 30% at 80% 20%, rgba(59, 130, 246, 0.12), transparent 70%), ${BASE_LIGHT}`,
|
||||
0.74,
|
||||
"0.85s",
|
||||
"2.6s",
|
||||
"2.6s",
|
||||
"0.75s",
|
||||
true,
|
||||
),
|
||||
lines: [
|
||||
{ delay: 0, dur: 1200, stroke: segment(0.5, 0.06, 0.5, 0.94) },
|
||||
{ delay: 80, dur: 1200, stroke: segment(0.04, 0.5, 0.96, 0.5) },
|
||||
@@ -202,6 +241,7 @@ const VARIANTS: LoadingVariant[] = [
|
||||
"blobs",
|
||||
0,
|
||||
theme(BASE, 0.78, "1.3s", "3.8s", "3.8s", "0.9s"),
|
||||
theme(BASE_LIGHT, 0.62, "1.3s", "3.8s", "3.8s", "0.9s", true),
|
||||
"diffuse",
|
||||
),
|
||||
canvasVariant(
|
||||
@@ -216,6 +256,15 @@ const VARIANTS: LoadingVariant[] = [
|
||||
"4.2s",
|
||||
"1s",
|
||||
),
|
||||
theme(
|
||||
`radial-gradient(ellipse 85% 45% at 50% 100%, rgba(37, 99, 235, 0.14), transparent 72%), ${BASE_LIGHT}`,
|
||||
0.64,
|
||||
"1.5s",
|
||||
"4.2s",
|
||||
"4.2s",
|
||||
"1s",
|
||||
true,
|
||||
),
|
||||
"tide",
|
||||
),
|
||||
canvasVariant(
|
||||
@@ -230,6 +279,15 @@ const VARIANTS: LoadingVariant[] = [
|
||||
"3.5s",
|
||||
"0.9s",
|
||||
),
|
||||
theme(
|
||||
`radial-gradient(ellipse 55% 50% at 50% 48%, rgba(37, 99, 235, 0.14), transparent 70%), ${BASE_LIGHT}`,
|
||||
0.7,
|
||||
"1.2s",
|
||||
"3.5s",
|
||||
"3.5s",
|
||||
"0.9s",
|
||||
true,
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ const OMIT_FROM_UPLOAD_EXACT = new Set<string>([
|
||||
...CLIENT_ONLY_CLOUD_KEYS_EXACT,
|
||||
"devMode",
|
||||
"devGhReleaseVersionOverride",
|
||||
"devDelayLoadingScreen",
|
||||
]);
|
||||
|
||||
const UNSAFE_STORAGE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
||||
|
||||
@@ -30,6 +30,7 @@ const OPTIONAL_UNSET_MEANS_DEFAULT_KEYS = [
|
||||
"justupdated",
|
||||
"devMode",
|
||||
"verboseLogging",
|
||||
"devDelayLoadingScreen",
|
||||
"hideSensitiveContent",
|
||||
"mockNotices",
|
||||
"homeUpcomingAssessmentsPerSubjectMax",
|
||||
|
||||
@@ -28,6 +28,7 @@ const SETTINGS_STORAGE_KEYS = [
|
||||
"justupdated",
|
||||
"devMode",
|
||||
"verboseLogging",
|
||||
"devDelayLoadingScreen",
|
||||
"devGhReleaseVersionOverride",
|
||||
"lastSeenNightlyPublishedAt",
|
||||
"originalDarkMode",
|
||||
|
||||
@@ -57,6 +57,8 @@ export interface SettingsState {
|
||||
devMode?: boolean;
|
||||
/** Dev-only: emit verboseDebug / verboseInfo / verboseLog output. */
|
||||
verboseLogging?: boolean;
|
||||
/** Dev-only: keep the loading overlay visible for 5 extra seconds. */
|
||||
devDelayLoadingScreen?: boolean;
|
||||
/** Dev-only: pretend this is the latest GitHub release version for update badge testing. */
|
||||
devGhReleaseVersionOverride?: string;
|
||||
/** ISO timestamp of the last acknowledged nightly release publish time. */
|
||||
|
||||
Reference in New Issue
Block a user