feat(appearance): opt-in glass tuner toggle in Settings
Graduate the floating glass tuner from a localhost-only dev tool to a user-facing opt-in: it's now gated on a per-user `theme.showTuner` pref (off by default) with a "Show the glass tuner" switch in Settings → Appearance, riding the existing theme draft-save. Softened the dev-only framing. Behaviour and look are unchanged; the full preset system is a separate epic.
This commit is contained in:
@@ -985,8 +985,8 @@ export default function App() {
|
|||||||
<ErrorDialog />
|
<ErrorDialog />
|
||||||
{/* Re-binds to the active page's scroll container on navigation (page or channel change). */}
|
{/* Re-binds to the active page's scroll container on navigation (page or channel change). */}
|
||||||
<BackToTop dep={channelView ? `chan:${channelView.id}` : page} />
|
<BackToTop dep={channelView ? `chan:${channelView.id}` : page} />
|
||||||
{/* Local-only live appearance tuner (renders only on localhost; null everywhere else). */}
|
{/* Opt-in live appearance tuner (Settings → Appearance; off by default). */}
|
||||||
<GlassTuner />
|
<GlassTuner enabled={theme.showTuner} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local-only live appearance tuner. Mounted at the app root so it's reachable on every page; it
|
* A floating live appearance tuner. Mounted at the app root so it's reachable on every page; it
|
||||||
* writes the glass CSS custom properties (index.css `:root`) inline on the document element for
|
* writes the glass CSS custom properties (index.css `:root`) inline on the document element for
|
||||||
* instant, build-free feedback, and persists to localStorage.
|
* instant, build-free feedback, and persists to localStorage.
|
||||||
*
|
*
|
||||||
* Only active on localhost, so it never appears in a hosted build. A scratch tool for dialling in
|
* Opt-in via Settings → Appearance (`theme.showTuner`, off by default) — a secondary "geek toy"
|
||||||
* the surface values — "Copy CSS" exports them to paste into index.css.
|
* for dialling in the surface values. "Copy CSS" exports the values.
|
||||||
*/
|
*/
|
||||||
const DEV_TUNER =
|
|
||||||
typeof window !== "undefined" && /^(localhost|127\.0\.0\.1)$/.test(window.location.hostname);
|
|
||||||
|
|
||||||
const LS_KEY = "siftlode.devGlassTuner";
|
const LS_KEY = "siftlode.devGlassTuner";
|
||||||
|
|
||||||
type Slider = {
|
type Slider = {
|
||||||
@@ -93,7 +90,7 @@ function loadSaved(): Saved {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function GlassTuner() {
|
export default function GlassTuner({ enabled }: { enabled: boolean }) {
|
||||||
const initial = useMemo(loadSaved, []);
|
const initial = useMemo(loadSaved, []);
|
||||||
const [vars, setVars] = useState<Record<string, number>>(() => {
|
const [vars, setVars] = useState<Record<string, number>>(() => {
|
||||||
const base: Record<string, number> = {};
|
const base: Record<string, number> = {};
|
||||||
@@ -216,7 +213,7 @@ export default function GlassTuner() {
|
|||||||
persist({ open: v });
|
persist({ open: v });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DEV_TUNER) return null;
|
if (!enabled) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed right-0 top-24 z-[9999] flex items-start" style={{ pointerEvents: "none" }}>
|
<div className="fixed right-0 top-24 z-[9999] flex items-start" style={{ pointerEvents: "none" }}>
|
||||||
@@ -225,7 +222,7 @@ export default function GlassTuner() {
|
|||||||
onClick={() => persistOpen(true)}
|
onClick={() => persistOpen(true)}
|
||||||
style={{ pointerEvents: "auto" }}
|
style={{ pointerEvents: "auto" }}
|
||||||
className="glass-menu glass-hover rounded-l-xl rounded-r-none px-2 py-3 text-xs font-semibold tracking-wide text-fg"
|
className="glass-menu glass-hover rounded-l-xl rounded-r-none px-2 py-3 text-xs font-semibold tracking-wide text-fg"
|
||||||
title="Open the glass tuner (dev only)"
|
title="Open the glass tuner"
|
||||||
>
|
>
|
||||||
<span style={{ writingMode: "vertical-rl" }}>◐ GLASS</span>
|
<span style={{ writingMode: "vertical-rl" }}>◐ GLASS</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -237,7 +234,7 @@ export default function GlassTuner() {
|
|||||||
>
|
>
|
||||||
<div className="flex items-center gap-2 px-3 py-2 border-b border-border/60">
|
<div className="flex items-center gap-2 px-3 py-2 border-b border-border/60">
|
||||||
<span className="text-sm font-semibold text-fg">◐ Glass Tuner</span>
|
<span className="text-sm font-semibold text-fg">◐ Glass Tuner</span>
|
||||||
<span className="text-[10px] uppercase tracking-wider text-muted">dev</span>
|
<span className="text-[10px] uppercase tracking-wider text-muted">lab</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => persistOpen(false)}
|
onClick={() => persistOpen(false)}
|
||||||
className="ml-auto text-muted hover:text-fg text-lg leading-none px-1"
|
className="ml-auto text-muted hover:text-fg text-lg leading-none px-1"
|
||||||
|
|||||||
@@ -205,6 +205,16 @@ function Appearance({ prefs }: { prefs: PrefsController }) {
|
|||||||
>
|
>
|
||||||
<Switch label={t("settings.appearance.showHints")} checked={hints} onChange={setHints} />
|
<Switch label={t("settings.appearance.showHints")} checked={hints} onChange={setHints} />
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
|
<SettingRow
|
||||||
|
label={t("settings.appearance.showTuner")}
|
||||||
|
hint={t("settings.appearance.showTunerHint")}
|
||||||
|
>
|
||||||
|
<Switch
|
||||||
|
label={t("settings.appearance.showTuner")}
|
||||||
|
checked={theme.showTuner}
|
||||||
|
onChange={(v) => setTheme({ ...theme, showTuner: v })}
|
||||||
|
/>
|
||||||
|
</SettingRow>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section title={t("settings.appearance.textSize")}>
|
<Section title={t("settings.appearance.textSize")}>
|
||||||
|
|||||||
@@ -42,6 +42,8 @@
|
|||||||
"performanceModeHint": "Turns off the translucent glass blur and soft shadows for snappier rendering on slower machines.",
|
"performanceModeHint": "Turns off the translucent glass blur and soft shadows for snappier rendering on slower machines.",
|
||||||
"showHints": "Show hints",
|
"showHints": "Show hints",
|
||||||
"showHintsHint": "These little hover explanations. Turn them off once you know your way around.",
|
"showHintsHint": "These little hover explanations. Turn them off once you know your way around.",
|
||||||
|
"showTuner": "Show the glass tuner",
|
||||||
|
"showTunerHint": "A floating panel for live-tweaking the frosted-glass look. A geek toy — off by default.",
|
||||||
"textSize": "Text size"
|
"textSize": "Text size"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
|
|||||||
@@ -42,6 +42,8 @@
|
|||||||
"performanceModeHint": "Kikapcsolja az áttetsző üvegelmosást és a lágy árnyékokat a gyorsabb megjelenítésért lassabb gépeken.",
|
"performanceModeHint": "Kikapcsolja az áttetsző üvegelmosást és a lágy árnyékokat a gyorsabb megjelenítésért lassabb gépeken.",
|
||||||
"showHints": "Tippek megjelenítése",
|
"showHints": "Tippek megjelenítése",
|
||||||
"showHintsHint": "Ezek a kis lebegő magyarázatok. Kapcsold ki őket, ha már kiismerted magad.",
|
"showHintsHint": "Ezek a kis lebegő magyarázatok. Kapcsold ki őket, ha már kiismerted magad.",
|
||||||
|
"showTuner": "Glass tuner megjelenítése",
|
||||||
|
"showTunerHint": "Lebegő panel a mattüveg-hatás élő hangolásához. Geek-játékszer — alapból kikapcsolva.",
|
||||||
"textSize": "Betűméret"
|
"textSize": "Betűméret"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export interface ThemePrefs {
|
|||||||
bgImage: boolean;
|
bgImage: boolean;
|
||||||
/** How strongly the background image is faded out, 0–100% (higher = fainter). */
|
/** How strongly the background image is faded out, 0–100% (higher = fainter). */
|
||||||
bgFade: number;
|
bgFade: number;
|
||||||
|
/** Show the floating glass/theme tuner (a secondary "geek toy"); off by default. */
|
||||||
|
showTuner: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_THEME: ThemePrefs = {
|
export const DEFAULT_THEME: ThemePrefs = {
|
||||||
@@ -28,6 +30,7 @@ export const DEFAULT_THEME: ThemePrefs = {
|
|||||||
fontScale: 1.06,
|
fontScale: 1.06,
|
||||||
bgImage: true,
|
bgImage: true,
|
||||||
bgFade: 60,
|
bgFade: 60,
|
||||||
|
showTuner: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function applyTheme(t: ThemePrefs): void {
|
export function applyTheme(t: ThemePrefs): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user