mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 01:09:56 -05:00
Chore: Fix Zoom & Attempt Theming
This commit is contained in:
+1
-140
@@ -1,4 +1,5 @@
|
||||
@import '$lib/components/settings/Settings.css';
|
||||
@import '$lib/styles/themes.css';
|
||||
|
||||
:root {
|
||||
--bg-void: #080808;
|
||||
@@ -90,146 +91,6 @@
|
||||
--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 {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
|
||||
@@ -59,11 +59,6 @@ async function boot() {
|
||||
appState.serverUrl = savedUrl
|
||||
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)
|
||||
|
||||
await serverAdapter.connect({
|
||||
|
||||
+37
-48
@@ -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 mediaQuery: MediaQueryList | null = null;
|
||||
let mediaHandler: (() => void) | null = null;
|
||||
let themeStyleEl: HTMLStyleElement | null = null
|
||||
let mediaQuery: MediaQueryList | null = null
|
||||
let mediaHandler: (() => void) | null = null
|
||||
|
||||
export function applyTheme() {
|
||||
const themeId = settingsState.theme ?? "dark";
|
||||
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;
|
||||
}
|
||||
export function applyTheme(themeId: string, customThemes: CustomTheme[] = []) {
|
||||
const custom = customThemes.find(t => t.id === themeId)
|
||||
|
||||
if (custom) {
|
||||
const vars = Object.entries(custom.tokens)
|
||||
.map(([k, v]) => ` --${k}: ${v};`)
|
||||
.join("\n");
|
||||
const css = `[data-theme="custom"] {\n${vars}\n}`;
|
||||
|
||||
.join('\n')
|
||||
if (!themeStyleEl) {
|
||||
themeStyleEl = document.createElement("style");
|
||||
themeStyleEl.id = "moku-custom-theme";
|
||||
document.head.appendChild(themeStyleEl);
|
||||
themeStyleEl = document.createElement('style')
|
||||
themeStyleEl.id = 'moku-custom-theme'
|
||||
document.head.appendChild(themeStyleEl)
|
||||
}
|
||||
themeStyleEl.textContent = css;
|
||||
document.documentElement.setAttribute("data-theme", "custom");
|
||||
themeStyleEl.textContent = `:root {\n${vars}\n}`
|
||||
document.documentElement.removeAttribute('data-theme')
|
||||
return
|
||||
}
|
||||
|
||||
if (themeStyleEl) {
|
||||
themeStyleEl.remove()
|
||||
themeStyleEl = null
|
||||
}
|
||||
document.documentElement.setAttribute('data-theme', themeId)
|
||||
}
|
||||
|
||||
function applySystemTheme(dark: boolean) {
|
||||
const themeId = dark
|
||||
? (settingsState.systemThemeDark ?? "dark")
|
||||
: (settingsState.systemThemeLight ?? "light");
|
||||
updateSettings({ theme: themeId });
|
||||
}
|
||||
|
||||
export function mountSystemThemeSync() {
|
||||
export function mountSystemThemeSync(
|
||||
enabled: boolean,
|
||||
darkTheme: string,
|
||||
lightTheme: string,
|
||||
onSwitch: (themeId: string) => void
|
||||
) {
|
||||
if (mediaQuery && mediaHandler) {
|
||||
mediaQuery.removeEventListener("change", mediaHandler);
|
||||
mediaHandler = null;
|
||||
mediaQuery.removeEventListener('change', mediaHandler)
|
||||
mediaHandler = null
|
||||
}
|
||||
|
||||
if (!settingsState.systemThemeSync) return;
|
||||
if (!enabled) { mediaQuery = null; return }
|
||||
|
||||
mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
mediaHandler = () => applySystemTheme(mediaQuery!.matches);
|
||||
mediaQuery.addEventListener("change", mediaHandler);
|
||||
applySystemTheme(mediaQuery.matches);
|
||||
mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
mediaHandler = () => onSwitch(mediaQuery!.matches ? darkTheme : lightTheme)
|
||||
mediaQuery.addEventListener('change', mediaHandler)
|
||||
onSwitch(mediaQuery.matches ? darkTheme : lightTheme)
|
||||
}
|
||||
|
||||
export function unmountSystemThemeSync() {
|
||||
if (mediaQuery && mediaHandler) {
|
||||
mediaQuery.removeEventListener("change", mediaHandler);
|
||||
mediaHandler = null;
|
||||
mediaQuery = null;
|
||||
mediaQuery.removeEventListener('change', mediaHandler)
|
||||
mediaHandler = null
|
||||
mediaQuery = null
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,19 @@ function save(s: Settings) {
|
||||
|
||||
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>) {
|
||||
Object.assign(settingsState.settings, patch)
|
||||
save(settingsState.settings)
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
if (patch.uiZoom !== undefined) {
|
||||
document.documentElement.style.zoom = String(patch.uiZoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function resetSettings() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
import { appState, app } from '$lib/state/app.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 AuthGate from '$lib/components/chrome/AuthGate.svelte'
|
||||
import Sidebar from '$lib/components/chrome/Sidebar.svelte'
|
||||
@@ -26,6 +29,32 @@
|
||||
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 onSplashBypass() { bypassed = true; splashVisible = false }
|
||||
|
||||
@@ -70,7 +99,10 @@
|
||||
{/if}
|
||||
|
||||
{#if themeEditorOpen}
|
||||
<ThemeEditor onclose={() => themeEditorOpen = false} editId={themeEditorId} />
|
||||
<ThemeEditor
|
||||
editingId={themeEditorId}
|
||||
onClose={() => themeEditorOpen = false}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<AuthGate />
|
||||
|
||||
Reference in New Issue
Block a user