Chore: Fix Zoom & Attempt Theming

This commit is contained in:
Youwes09
2026-05-25 12:31:23 -05:00
parent cbdf9e8be1
commit e9929747d2
6 changed files with 229 additions and 203 deletions
+1 -140
View File
@@ -1,4 +1,5 @@
@import '$lib/components/settings/Settings.css'; @import '$lib/components/settings/Settings.css';
@import '$lib/styles/themes.css';
:root { :root {
--bg-void: #080808; --bg-void: #080808;
@@ -90,146 +91,6 @@
--z-settings: 150; --z-settings: 150;
} }
[data-theme="dark"] {
--bg-void: #000000;
--bg-base: #080808;
--bg-surface: #0d0d0d;
--bg-raised: #111111;
--bg-overlay: #171717;
--bg-subtle: #1e1e1e;
--border-dim: #252525;
--border-base: #303030;
--border-strong: #3e3e3e;
--border-focus: #5a7a5a;
--text-primary: #ffffff;
--text-secondary: #e8e6e0;
--text-muted: #b0aea8;
--text-faint: #6e6c68;
--text-disabled: #303030;
--accent: #7aaa7a;
--accent-dim: #2e4a2e;
--accent-muted: #1e2e1e;
--accent-fg: #bcd8bc;
--accent-bright: #9fcf9f;
}
[data-theme="light"] {
--bg-void: #d8d4ce;
--bg-base: #e2deda;
--bg-surface: #ece8e2;
--bg-raised: #f5f2ec;
--bg-overlay: #ffffff;
--bg-subtle: #e4e0d8;
--border-dim: #c4c0b8;
--border-base: #b0aca4;
--border-strong: #989490;
--border-focus: #3a5a3a;
--text-primary: #080806;
--text-secondary: #181612;
--text-muted: #38342e;
--text-faint: #706c64;
--text-disabled: #b0aca4;
--accent: #2a5a2a;
--accent-dim: #b0ccb0;
--accent-muted: #c8dcc8;
--accent-fg: #183818;
--accent-bright: #1e4e1e;
--color-error: #8a1a1a;
--color-error-bg: #f8e0e0;
--color-read: #e0dcd4;
}
[data-theme="midnight"] {
--bg-void: #050810;
--bg-base: #080c18;
--bg-surface: #0c1020;
--bg-raised: #101428;
--bg-overlay: #151a30;
--bg-subtle: #1a2038;
--border-dim: #1a2035;
--border-base: #222840;
--border-strong: #2c3450;
--border-focus: #4a5c8a;
--text-primary: #eeeef8;
--text-secondary: #c0c4d8;
--text-muted: #808498;
--text-faint: #404860;
--text-disabled: #202840;
--accent: #6a7ab8;
--accent-dim: #252d50;
--accent-muted: #181e38;
--accent-fg: #a8b4e8;
--accent-bright: #8896d0;
}
[data-theme="original"] {
--bg-void: #080808;
--bg-base: #0c0c0c;
--bg-surface: #101010;
--bg-raised: #151515;
--bg-overlay: #1a1a1a;
--bg-subtle: #202020;
--border-dim: #1c1c1c;
--border-base: #242424;
--border-strong: #2e2e2e;
--border-focus: #4a5c4a;
--text-primary: #f0efec;
--text-secondary: #c8c6c0;
--text-muted: #8a8880;
--text-faint: #4e4d4a;
--text-disabled: #2a2a28;
--accent: #6b8f6b;
--accent-dim: #2a3d2a;
--accent-muted: #1a251a;
--accent-fg: #a8c4a8;
--accent-bright: #8fb88f;
--color-error: #c47a7a;
--color-error-bg: #1f1212;
--color-success: #7aab7a;
--color-info: #7a9ec4;
--color-info-bg: #121a1f;
}
[data-theme="warm"] {
--bg-void: #0c0a06;
--bg-base: #100e08;
--bg-surface: #16130c;
--bg-raised: #1c1810;
--bg-overlay: #221e14;
--bg-subtle: #28241a;
--border-dim: #201c10;
--border-base: #2c2818;
--border-strong: #3a3420;
--border-focus: #6a5a30;
--text-primary: #f5f0e0;
--text-secondary: #d8d0b0;
--text-muted: #988c60;
--text-faint: #584e30;
--text-disabled: #302a18;
--accent: #c0902a;
--accent-dim: #3a2c10;
--accent-muted: #261e0c;
--accent-fg: #e0b860;
--accent-bright: #d0a040;
}
*, *::before, *::after { *, *::before, *::after {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
-5
View File
@@ -59,11 +59,6 @@ async function boot() {
appState.serverUrl = savedUrl appState.serverUrl = savedUrl
appState.authMode = savedAuth.mode appState.authMode = savedAuth.mode
if (isTauri() && platformAdapter.isSupported('server-management')) {
// jarPath/port/dataPath come from persisted server config; omitted here
// until settings UI writes them — server auto-launch handled by Tauri side
}
configureAuth(savedUrl, savedAuth.mode, savedAuth.user, savedAuth.pass) configureAuth(savedUrl, savedAuth.mode, savedAuth.user, savedAuth.pass)
await serverAdapter.connect({ await serverAdapter.connect({
+35 -46
View File
@@ -1,67 +1,56 @@
import { settingsState, updateSettings } from "$lib/state/settings.svelte"; import type { CustomTheme } from '$lib/types/settings'
let themeStyleEl: HTMLStyleElement | null = null; let themeStyleEl: HTMLStyleElement | null = null
let mediaQuery: MediaQueryList | null = null; let mediaQuery: MediaQueryList | null = null
let mediaHandler: (() => void) | null = null; let mediaHandler: (() => void) | null = null
export function applyTheme() { export function applyTheme(themeId: string, customThemes: CustomTheme[] = []) {
const themeId = settingsState.theme ?? "dark"; const custom = customThemes.find(t => t.id === themeId)
const isCustom = themeId.startsWith("custom:");
if (!isCustom) {
themeStyleEl?.remove();
themeStyleEl = null;
document.documentElement.setAttribute("data-theme", themeId);
return;
}
const custom = settingsState.customThemes?.find(t => t.id === themeId);
if (!custom) {
themeStyleEl?.remove();
themeStyleEl = null;
document.documentElement.setAttribute("data-theme", "dark");
return;
}
if (custom) {
const vars = Object.entries(custom.tokens) const vars = Object.entries(custom.tokens)
.map(([k, v]) => ` --${k}: ${v};`) .map(([k, v]) => ` --${k}: ${v};`)
.join("\n"); .join('\n')
const css = `[data-theme="custom"] {\n${vars}\n}`;
if (!themeStyleEl) { if (!themeStyleEl) {
themeStyleEl = document.createElement("style"); themeStyleEl = document.createElement('style')
themeStyleEl.id = "moku-custom-theme"; themeStyleEl.id = 'moku-custom-theme'
document.head.appendChild(themeStyleEl); document.head.appendChild(themeStyleEl)
} }
themeStyleEl.textContent = css; themeStyleEl.textContent = `:root {\n${vars}\n}`
document.documentElement.setAttribute("data-theme", "custom"); document.documentElement.removeAttribute('data-theme')
return
} }
function applySystemTheme(dark: boolean) { if (themeStyleEl) {
const themeId = dark themeStyleEl.remove()
? (settingsState.systemThemeDark ?? "dark") themeStyleEl = null
: (settingsState.systemThemeLight ?? "light"); }
updateSettings({ theme: themeId }); document.documentElement.setAttribute('data-theme', themeId)
} }
export function mountSystemThemeSync() { export function mountSystemThemeSync(
enabled: boolean,
darkTheme: string,
lightTheme: string,
onSwitch: (themeId: string) => void
) {
if (mediaQuery && mediaHandler) { if (mediaQuery && mediaHandler) {
mediaQuery.removeEventListener("change", mediaHandler); mediaQuery.removeEventListener('change', mediaHandler)
mediaHandler = null; mediaHandler = null
} }
if (!settingsState.systemThemeSync) return; if (!enabled) { mediaQuery = null; return }
mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
mediaHandler = () => applySystemTheme(mediaQuery!.matches); mediaHandler = () => onSwitch(mediaQuery!.matches ? darkTheme : lightTheme)
mediaQuery.addEventListener("change", mediaHandler); mediaQuery.addEventListener('change', mediaHandler)
applySystemTheme(mediaQuery.matches); onSwitch(mediaQuery.matches ? darkTheme : lightTheme)
} }
export function unmountSystemThemeSync() { export function unmountSystemThemeSync() {
if (mediaQuery && mediaHandler) { if (mediaQuery && mediaHandler) {
mediaQuery.removeEventListener("change", mediaHandler); mediaQuery.removeEventListener('change', mediaHandler)
mediaHandler = null; mediaHandler = null
mediaQuery = null; mediaQuery = null
} }
} }
+10
View File
@@ -17,9 +17,19 @@ function save(s: Settings) {
export const settingsState = $state({ settings: load() }) export const settingsState = $state({ settings: load() })
if (typeof document !== 'undefined') {
document.documentElement.style.zoom = String(settingsState.settings.uiZoom ?? 1.0)
}
export function updateSettings(patch: Partial<Settings>) { export function updateSettings(patch: Partial<Settings>) {
Object.assign(settingsState.settings, patch) Object.assign(settingsState.settings, patch)
save(settingsState.settings) save(settingsState.settings)
if (typeof document !== 'undefined') {
if (patch.uiZoom !== undefined) {
document.documentElement.style.zoom = String(patch.uiZoom)
}
}
} }
export function resetSettings() { export function resetSettings() {
+139
View File
@@ -0,0 +1,139 @@
[data-theme="dark"] {
--bg-void: #000000;
--bg-base: #080808;
--bg-surface: #0d0d0d;
--bg-raised: #111111;
--bg-overlay: #171717;
--bg-subtle: #1e1e1e;
--border-dim: #252525;
--border-base: #303030;
--border-strong: #3e3e3e;
--border-focus: #5a7a5a;
--text-primary: #ffffff;
--text-secondary: #e8e6e0;
--text-muted: #b0aea8;
--text-faint: #6e6c68;
--text-disabled: #303030;
--accent: #7aaa7a;
--accent-dim: #2e4a2e;
--accent-muted: #1e2e1e;
--accent-fg: #bcd8bc;
--accent-bright: #9fcf9f;
}
[data-theme="light"] {
--bg-void: #d8d4ce;
--bg-base: #e2deda;
--bg-surface: #ece8e2;
--bg-raised: #f5f2ec;
--bg-overlay: #ffffff;
--bg-subtle: #e4e0d8;
--border-dim: #c4c0b8;
--border-base: #b0aca4;
--border-strong: #989490;
--border-focus: #3a5a3a;
--text-primary: #080806;
--text-secondary: #181612;
--text-muted: #38342e;
--text-faint: #706c64;
--text-disabled: #b0aca4;
--accent: #2a5a2a;
--accent-dim: #b0ccb0;
--accent-muted: #c8dcc8;
--accent-fg: #183818;
--accent-bright: #1e4e1e;
--color-error: #8a1a1a;
--color-error-bg: #f8e0e0;
--color-read: #e0dcd4;
}
[data-theme="midnight"] {
--bg-void: #050810;
--bg-base: #080c18;
--bg-surface: #0c1020;
--bg-raised: #101428;
--bg-overlay: #151a30;
--bg-subtle: #1a2038;
--border-dim: #1a2035;
--border-base: #222840;
--border-strong: #2c3450;
--border-focus: #4a5c8a;
--text-primary: #eeeef8;
--text-secondary: #c0c4d8;
--text-muted: #808498;
--text-faint: #404860;
--text-disabled: #202840;
--accent: #6a7ab8;
--accent-dim: #252d50;
--accent-muted: #181e38;
--accent-fg: #a8b4e8;
--accent-bright: #8896d0;
}
[data-theme="original"] {
--bg-void: #080808;
--bg-base: #0c0c0c;
--bg-surface: #101010;
--bg-raised: #151515;
--bg-overlay: #1a1a1a;
--bg-subtle: #202020;
--border-dim: #1c1c1c;
--border-base: #242424;
--border-strong: #2e2e2e;
--border-focus: #4a5c4a;
--text-primary: #f0efec;
--text-secondary: #c8c6c0;
--text-muted: #8a8880;
--text-faint: #4e4d4a;
--text-disabled: #2a2a28;
--accent: #6b8f6b;
--accent-dim: #2a3d2a;
--accent-muted: #1a251a;
--accent-fg: #a8c4a8;
--accent-bright: #8fb88f;
--color-error: #c47a7a;
--color-error-bg: #1f1212;
--color-success: #7aab7a;
--color-info: #7a9ec4;
--color-info-bg: #121a1f;
}
[data-theme="warm"] {
--bg-void: #0c0a06;
--bg-base: #100e08;
--bg-surface: #16130c;
--bg-raised: #1c1810;
--bg-overlay: #221e14;
--bg-subtle: #28241a;
--border-dim: #201c10;
--border-base: #2c2818;
--border-strong: #3a3420;
--border-focus: #6a5a30;
--text-primary: #f5f0e0;
--text-secondary: #d8d0b0;
--text-muted: #988c60;
--text-faint: #584e30;
--text-disabled: #302a18;
--accent: #c0902a;
--accent-dim: #3a2c10;
--accent-muted: #261e0c;
--accent-fg: #e0b860;
--accent-bright: #d0a040;
}
+33 -1
View File
@@ -1,6 +1,9 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'
import { appState, app } from '$lib/state/app.svelte' import { appState, app } from '$lib/state/app.svelte'
import { notifications } from '$lib/state/notifications.svelte' import { notifications } from '$lib/state/notifications.svelte'
import { settingsState, updateSettings } from '$lib/state/settings.svelte'
import { applyTheme, mountSystemThemeSync } from '$lib/core/theme'
import SplashScreen from '$lib/components/chrome/SplashScreen.svelte' import SplashScreen from '$lib/components/chrome/SplashScreen.svelte'
import AuthGate from '$lib/components/chrome/AuthGate.svelte' import AuthGate from '$lib/components/chrome/AuthGate.svelte'
import Sidebar from '$lib/components/chrome/Sidebar.svelte' import Sidebar from '$lib/components/chrome/Sidebar.svelte'
@@ -26,6 +29,32 @@
bypassed bypassed
) )
// Apply theme immediately on mount (before first paint if possible)
onMount(() => {
applyTheme(
settingsState.settings.theme ?? 'dark',
settingsState.settings.customThemes ?? []
)
})
$effect(() => {
document.documentElement.style.zoom = String(settingsState.settings.uiZoom ?? 1.0)
})
// Reactive theme application — explicitly pass values so Svelte tracks them
$effect(() => {
const theme = settingsState.settings.theme ?? 'dark'
const customThemes = settingsState.settings.customThemes ?? []
applyTheme(theme, customThemes)
})
$effect(() => {
const enabled = settingsState.settings.systemThemeSync ?? false
const darkTheme = settingsState.settings.systemThemeDark ?? 'dark'
const lightTheme = settingsState.settings.systemThemeLight ?? 'light'
mountSystemThemeSync(enabled, darkTheme, lightTheme, (id) => updateSettings({ theme: id }))
})
function onSplashReady() { splashVisible = false } function onSplashReady() { splashVisible = false }
function onSplashBypass() { bypassed = true; splashVisible = false } function onSplashBypass() { bypassed = true; splashVisible = false }
@@ -70,7 +99,10 @@
{/if} {/if}
{#if themeEditorOpen} {#if themeEditorOpen}
<ThemeEditor onclose={() => themeEditorOpen = false} editId={themeEditorId} /> <ThemeEditor
editingId={themeEditorId}
onClose={() => themeEditorOpen = false}
/>
{/if} {/if}
<AuthGate /> <AuthGate />