feat: per-workspace theme system with 5 theme options
Replaces in-memory darkMode toggle with persisted per-workspace theme selection. Adds dark, nord, dracula, and solarized CSS theme definitions. Theme is applied via data-theme attribute and derived isDark class. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a60b1a997b
commit
a1e97bc0fe
|
|
@ -12,7 +12,7 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<div class={app.darkMode ? "dark" : ""}>
|
||||
<div class={app.isDark ? "dark" : ""} data-theme={app.currentTheme ?? ""}>
|
||||
<div class="h-screen w-screen" class:p-2={isLinux}>
|
||||
<div
|
||||
class="relative h-full w-full overflow-hidden bg-surface-light text-text-light dark:bg-surface-dark dark:text-text-dark"
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
{/if}
|
||||
|
||||
{#if app.screen === "setup"}
|
||||
<SetupScreen />
|
||||
<SetupScreen cancellable={app.hasWorkspace} />
|
||||
{:else}
|
||||
<TasksScreen />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -68,3 +68,68 @@ body {
|
|||
background-color: #242424;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
/* ── Theme overrides ─────────────────────────────────────────────── */
|
||||
|
||||
[data-theme="dark"] {
|
||||
--color-primary: #2d87b8;
|
||||
--color-primary-hover: #2474a0;
|
||||
--color-surface-light: #242424;
|
||||
--color-surface-dark: #242424;
|
||||
--color-card-light: #303030;
|
||||
--color-card-dark: #303030;
|
||||
--color-text-light: #e5e7eb;
|
||||
--color-text-dark: #e5e7eb;
|
||||
--color-text-secondary-light: #9ca3af;
|
||||
--color-text-secondary-dark: #9ca3af;
|
||||
--color-border-light: #3d3d3d;
|
||||
--color-border-dark: #3d3d3d;
|
||||
}
|
||||
|
||||
[data-theme="nord"] {
|
||||
--color-primary: #88c0d0;
|
||||
--color-primary-hover: #7ab3c3;
|
||||
--color-surface-light: #2e3440;
|
||||
--color-surface-dark: #2e3440;
|
||||
--color-card-light: #3b4252;
|
||||
--color-card-dark: #3b4252;
|
||||
--color-text-light: #eceff4;
|
||||
--color-text-dark: #eceff4;
|
||||
--color-text-secondary-light: #d8dee9;
|
||||
--color-text-secondary-dark: #d8dee9;
|
||||
--color-border-light: #434c5e;
|
||||
--color-border-dark: #434c5e;
|
||||
--color-danger: #bf616a;
|
||||
}
|
||||
|
||||
[data-theme="dracula"] {
|
||||
--color-primary: #bd93f9;
|
||||
--color-primary-hover: #a87ef0;
|
||||
--color-surface-light: #282a36;
|
||||
--color-surface-dark: #282a36;
|
||||
--color-card-light: #343746;
|
||||
--color-card-dark: #343746;
|
||||
--color-text-light: #f8f8f2;
|
||||
--color-text-dark: #f8f8f2;
|
||||
--color-text-secondary-light: #bfbfbf;
|
||||
--color-text-secondary-dark: #bfbfbf;
|
||||
--color-border-light: #44475a;
|
||||
--color-border-dark: #44475a;
|
||||
--color-danger: #ff5555;
|
||||
}
|
||||
|
||||
[data-theme="solarized"] {
|
||||
--color-primary: #268bd2;
|
||||
--color-primary-hover: #1e7ac0;
|
||||
--color-surface-light: #002b36;
|
||||
--color-surface-dark: #002b36;
|
||||
--color-card-light: #073642;
|
||||
--color-card-dark: #073642;
|
||||
--color-text-light: #93a1a1;
|
||||
--color-text-dark: #93a1a1;
|
||||
--color-text-secondary-light: #657b83;
|
||||
--color-text-secondary-dark: #657b83;
|
||||
--color-border-light: #094959;
|
||||
--color-border-dark: #094959;
|
||||
--color-danger: #dc322f;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,7 @@ let config = $state<AppConfig | null>(null);
|
|||
let lists = $state<TaskList[]>([]);
|
||||
let activeListId = $state<string | null>(null);
|
||||
let tasks = $state<Task[]>([]);
|
||||
let darkMode = $state(
|
||||
globalThis.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false,
|
||||
);
|
||||
let osDark = globalThis.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
|
||||
let syncing = $state(false);
|
||||
let syncMode = $state<"full" | "push" | "pull">("full");
|
||||
let lastSyncResult = $state<SyncResult | null>(null);
|
||||
|
|
@ -56,6 +54,16 @@ let hasWorkspace = $derived(
|
|||
Object.keys(config.workspaces).length > 0,
|
||||
);
|
||||
|
||||
const DARK_THEMES = new Set(["dark", "nord", "dracula", "solarized"]);
|
||||
let currentTheme = $derived(
|
||||
config?.current_workspace
|
||||
? config.workspaces[config.current_workspace]?.theme ?? null
|
||||
: null,
|
||||
);
|
||||
let isDark = $derived(
|
||||
currentTheme ? DARK_THEMES.has(currentTheme) : osDark,
|
||||
);
|
||||
|
||||
// ── Actions ──────────────────────────────────────────────────────────
|
||||
|
||||
async function loadConfig() {
|
||||
|
|
@ -311,8 +319,31 @@ function setSyncMode(mode: "full" | "push" | "pull") {
|
|||
syncMode = mode;
|
||||
}
|
||||
|
||||
function toggleDarkMode() {
|
||||
darkMode = !darkMode;
|
||||
async function setTheme(theme: string | null) {
|
||||
if (!config?.current_workspace) return;
|
||||
try {
|
||||
await invoke("set_workspace_theme", {
|
||||
workspaceName: config.current_workspace,
|
||||
theme,
|
||||
});
|
||||
config = await invoke<AppConfig>("get_config");
|
||||
} catch (e) {
|
||||
error = String(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function addWebdavWorkspace(name: string, webdavUrl: string, username: string, password: string) {
|
||||
try {
|
||||
await invoke("add_webdav_workspace", { name, webdavUrl, username, password });
|
||||
config = await invoke<AppConfig>("get_config");
|
||||
await loadLists();
|
||||
const ws = config?.workspaces[name];
|
||||
if (ws) invoke("watch_workspace", { path: ws.path }).catch((e) => console.warn("File watcher failed:", e));
|
||||
screen = "tasks";
|
||||
error = null;
|
||||
} catch (e) {
|
||||
error = String(e);
|
||||
}
|
||||
}
|
||||
|
||||
function setScreen(s: Screen) {
|
||||
|
|
@ -350,8 +381,11 @@ export const app = {
|
|||
get completedTasks() {
|
||||
return completedTasks;
|
||||
},
|
||||
get darkMode() {
|
||||
return darkMode;
|
||||
get currentTheme() {
|
||||
return currentTheme;
|
||||
},
|
||||
get isDark() {
|
||||
return isDark;
|
||||
},
|
||||
get syncing() {
|
||||
return syncing;
|
||||
|
|
@ -388,7 +422,8 @@ export const app = {
|
|||
setGroupByDueDate,
|
||||
triggerSync,
|
||||
setSyncMode,
|
||||
toggleDarkMode,
|
||||
setTheme,
|
||||
addWebdavWorkspace,
|
||||
setScreen,
|
||||
clearError,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue