feat: add dark/light theme toggle with system preference support (#18)

Add theme switching with three modes (system/light/dark) using Tailwind v4
class-based dark mode. Theme preference is persisted in localStorage and
defaults to the system's prefers-color-scheme. All components updated with
dark: variants for consistent dark mode rendering including SVG elements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shahondin1624
2026-03-12 12:54:04 +01:00
parent 79c14378a2
commit 60f3d8eeda
22 changed files with 418 additions and 181 deletions

View File

@@ -115,10 +115,10 @@
}
</script>
<aside class="flex h-full w-72 flex-col border-l border-gray-200 bg-white">
<div class="flex items-center justify-between border-b border-gray-200 px-4 py-3">
<aside class="flex h-full w-72 flex-col border-l border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900">
<div class="flex items-center justify-between border-b border-gray-200 dark:border-gray-700 px-4 py-3">
<div class="flex items-center gap-2">
<h2 class="text-sm font-semibold text-gray-900">Session Config</h2>
<h2 class="text-sm font-semibold text-gray-900 dark:text-gray-100">Session Config</h2>
{#if isNonDefault}
<span class="h-2 w-2 rounded-full bg-amber-500" title="Non-default config active"></span>
{/if}
@@ -126,7 +126,7 @@
<button
type="button"
onclick={resetConfig}
class="text-xs text-gray-500 hover:text-gray-700"
class="text-xs text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
>
Reset
</button>
@@ -135,27 +135,27 @@
<div class="flex-1 overflow-y-auto p-4 space-y-5">
<!-- Presets -->
<div>
<p class="mb-2 text-xs font-medium text-gray-700">Presets</p>
<p class="mb-2 text-xs font-medium text-gray-700 dark:text-gray-300">Presets</p>
<div class="space-y-1">
{#each presetStore.getAllPresets() as preset (preset.name)}
<div class="flex items-center gap-1">
<button
type="button"
onclick={() => handleLoadPreset(preset.name)}
class="flex-1 rounded-lg px-3 py-1.5 text-left text-sm text-gray-700 hover:bg-gray-50"
class="flex-1 rounded-lg px-3 py-1.5 text-left text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700"
>
{preset.name}
{#if preset.builtIn}
<span class="text-[10px] text-gray-400">built-in</span>
<span class="text-[10px] text-gray-400 dark:text-gray-500">built-in</span>
{/if}
</button>
{#if !preset.builtIn}
<button
type="button"
onclick={() => handleDeletePreset(preset.name)}
class="rounded px-1.5 py-1 text-xs text-gray-400 hover:text-red-500"
class="rounded px-1.5 py-1 text-xs text-gray-400 dark:text-gray-500 hover:text-red-500"
>
&#10005;
</button>
{/if}
</div>
@@ -168,22 +168,22 @@
bind:value={newPresetName}
onkeydown={(e) => { if (e.key === 'Enter') handleSavePreset(); }}
placeholder="Preset name"
class="flex-1 rounded-lg border border-gray-300 px-2 py-1.5 text-xs focus:border-blue-500 focus:outline-none"
class="flex-1 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-2 py-1.5 text-xs text-gray-900 dark:text-gray-100 focus:border-blue-500 focus:outline-none"
/>
<button
type="button"
onclick={handleSavePreset}
disabled={!newPresetName.trim()}
class="rounded-lg bg-blue-50 px-2 py-1.5 text-xs font-medium text-blue-700 hover:bg-blue-100 disabled:opacity-50"
class="rounded-lg bg-blue-50 dark:bg-blue-900/40 px-2 py-1.5 text-xs font-medium text-blue-700 dark:text-blue-300 hover:bg-blue-100 dark:hover:bg-blue-900/60 disabled:opacity-50"
>
Save
</button>
<button
type="button"
onclick={() => { showSavePreset = false; saveError = ''; newPresetName = ''; }}
class="rounded-lg px-1.5 py-1.5 text-xs text-gray-400 hover:text-gray-600"
class="rounded-lg px-1.5 py-1.5 text-xs text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300"
>
&#10005;
</button>
</div>
{#if saveError}
@@ -193,7 +193,7 @@
<button
type="button"
onclick={() => (showSavePreset = true)}
class="mt-2 w-full rounded-lg border border-dashed border-gray-300 px-3 py-1.5 text-xs text-gray-500 hover:border-gray-400 hover:text-gray-700"
class="mt-2 w-full rounded-lg border border-dashed border-gray-300 dark:border-gray-600 px-3 py-1.5 text-xs text-gray-500 dark:text-gray-400 hover:border-gray-400 dark:hover:border-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
>
Save current as preset
</button>
@@ -202,7 +202,7 @@
<!-- Override Level -->
<div>
<p class="mb-2 text-xs font-medium text-gray-700">Override Level</p>
<p class="mb-2 text-xs font-medium text-gray-700 dark:text-gray-300">Override Level</p>
<div class="space-y-1">
{#each overrideLevels as level (level.value)}
<button
@@ -210,11 +210,11 @@
onclick={() => setOverrideLevel(level.value)}
class="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-sm
{config.overrideLevel === level.value
? 'bg-blue-50 text-blue-700 ring-1 ring-blue-200'
: 'text-gray-700 hover:bg-gray-50'}"
? 'bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 ring-1 ring-blue-200 dark:ring-blue-700'
: 'text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700'}"
>
<span class="font-medium">{level.label}</span>
<span class="text-xs text-gray-500"> {level.description}</span>
<span class="text-xs text-gray-500 dark:text-gray-400">&mdash; {level.description}</span>
</button>
{/each}
</div>
@@ -222,17 +222,17 @@
<!-- Disabled Tools -->
<div>
<p class="mb-2 text-xs font-medium text-gray-700">Disabled Tools</p>
<p class="mb-2 text-xs font-medium text-gray-700 dark:text-gray-300">Disabled Tools</p>
<div class="space-y-1">
{#each toolTypes as tool (tool.value)}
<label class="flex items-center gap-2 rounded px-2 py-1 text-sm hover:bg-gray-50">
<label class="flex items-center gap-2 rounded px-2 py-1 text-sm hover:bg-gray-50 dark:hover:bg-gray-700">
<input
type="checkbox"
checked={config.disabledTools.includes(tool.label)}
onchange={() => toggleTool(tool.label)}
class="rounded border-gray-300 text-blue-600"
class="rounded border-gray-300 dark:border-gray-600 text-blue-600"
/>
<span class="text-gray-700">{tool.label}</span>
<span class="text-gray-700 dark:text-gray-300">{tool.label}</span>
</label>
{/each}
</div>
@@ -240,19 +240,19 @@
<!-- Granted Permissions -->
<div>
<p class="mb-2 text-xs font-medium text-gray-700">Granted Permissions</p>
<p class="mb-2 text-xs font-medium text-gray-700 dark:text-gray-300">Granted Permissions</p>
<div class="flex gap-1">
<input
type="text"
bind:value={newPermission}
onkeydown={(e) => { if (e.key === 'Enter') addPermission(); }}
placeholder="agent_type:tool"
class="flex-1 rounded-lg border border-gray-300 px-2 py-1.5 text-xs focus:border-blue-500 focus:outline-none"
class="flex-1 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-2 py-1.5 text-xs text-gray-900 dark:text-gray-100 focus:border-blue-500 focus:outline-none"
/>
<button
type="button"
onclick={addPermission}
class="rounded-lg bg-gray-100 px-2 py-1.5 text-xs font-medium text-gray-700 hover:bg-gray-200"
class="rounded-lg bg-gray-100 dark:bg-gray-700 px-2 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"
>
Add
</button>
@@ -260,14 +260,14 @@
{#if config.grantedPermissions.length > 0}
<div class="mt-2 space-y-1">
{#each config.grantedPermissions as perm (perm)}
<div class="flex items-center justify-between rounded bg-gray-50 px-2 py-1">
<code class="text-xs text-gray-700">{perm}</code>
<div class="flex items-center justify-between rounded bg-gray-50 dark:bg-gray-800 px-2 py-1">
<code class="text-xs text-gray-700 dark:text-gray-300">{perm}</code>
<button
type="button"
onclick={() => removePermission(perm)}
class="text-xs text-gray-400 hover:text-red-500"
class="text-xs text-gray-400 dark:text-gray-500 hover:text-red-500"
>
&#10005;
</button>
</div>
{/each}