Browse Source

clean up themes

make themes more persistent
master
Silberengel 1 month ago
parent
commit
b673cc4bc0
  1. 74
      src/lib/components/modals/EventJsonModal.svelte
  2. 81
      src/lib/components/preferences/ThemeToggle.svelte
  3. 51
      src/lib/components/profile/ProfileMenu.svelte
  4. 55
      src/lib/components/write/AdvancedEditor.svelte
  5. 39
      src/lib/components/write/CreateEventForm.svelte
  6. 48
      src/lib/modules/comments/CommentForm.svelte
  7. 52
      src/lib/modules/rss/RSSCommentForm.svelte
  8. 95
      src/routes/+layout.svelte

74
src/lib/components/modals/EventJsonModal.svelte

@ -1,6 +1,9 @@ @@ -1,6 +1,9 @@
<script lang="ts">
import type { NostrEvent } from '../../types/nostr.js';
import Icon from '../ui/Icon.svelte';
// @ts-ignore - highlight.js default export works at runtime
import hljs from 'highlight.js';
import 'highlight.js/styles/vs2015.css';
interface Props {
open?: boolean;
@ -10,6 +13,22 @@ @@ -10,6 +13,22 @@
let { open = $bindable(false), event = $bindable(null) }: Props = $props();
let jsonText = $derived(event ? JSON.stringify(event, null, 2) : '');
let copied = $state(false);
let jsonPreviewRef: HTMLElement | null = $state(null);
// Highlight JSON when it changes
$effect(() => {
if (jsonPreviewRef && jsonText && jsonPreviewRef instanceof HTMLElement) {
try {
const highlighted = hljs.highlight(jsonText, { language: 'json' }).value;
jsonPreviewRef.innerHTML = highlighted;
jsonPreviewRef.className = 'hljs language-json';
} catch (err) {
// Fallback to plain text if highlighting fails
jsonPreviewRef.textContent = jsonText;
jsonPreviewRef.className = 'language-json';
}
}
});
function close() {
open = false;
@ -59,11 +78,7 @@ @@ -59,11 +78,7 @@
</div>
<div class="modal-body">
<textarea
class="json-textarea"
readonly
value={jsonText}
></textarea>
<pre class="json-preview"><code bind:this={jsonPreviewRef} class="language-json">{jsonText}</code></pre>
</div>
<div class="modal-footer">
@ -195,40 +210,47 @@ @@ -195,40 +210,47 @@
overflow: auto;
}
.json-textarea {
width: 100%;
min-height: 400px;
font-family: 'Courier New', monospace;
font-size: 0.875rem;
padding: 0.75rem;
border: 1px solid var(--fog-border, #e5e7eb);
.json-preview {
background: #1e1e1e !important; /* VS Code dark background, same as code blocks */
border: 1px solid #3e3e3e;
border-radius: 4px;
background: var(--fog-bg, #ffffff);
color: var(--fog-text, #1f2937);
resize: vertical;
padding: 1rem;
margin: 0;
overflow-x: auto;
max-height: 60vh;
}
.json-preview code {
display: block;
overflow-x: auto;
padding: 0;
background: transparent !important;
color: #d4d4d4; /* VS Code text color */
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Droid Sans Mono', 'Source Code Pro', monospace;
font-size: 0.875rem;
line-height: 1.5;
}
@media (max-width: 768px) {
.json-textarea {
min-height: 300px;
.json-preview {
padding: 0.75rem;
}
.json-preview code {
font-size: 0.8125rem;
padding: 0.5rem;
}
}
@media (max-width: 640px) {
.json-textarea {
min-height: calc(100vh - 200px);
font-size: 0.75rem;
.json-preview {
padding: 0.5rem;
border-radius: 0;
}
max-height: calc(100vh - 200px);
}
:global(.dark) .json-textarea {
background: var(--fog-dark-bg, #0f172a);
border-color: var(--fog-dark-border, #374151);
color: var(--fog-dark-text, #f1f5f9);
.json-preview code {
font-size: 0.75rem;
}
}
.modal-footer {

81
src/lib/components/preferences/ThemeToggle.svelte

@ -4,20 +4,30 @@ @@ -4,20 +4,30 @@
let isDark = $state(false);
onMount(() => {
// Check localStorage and system preference
const stored = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
isDark = stored === 'dark' || (!stored && prefersDark);
updateTheme();
});
function getDesignTheme(): string {
if (typeof window === 'undefined') return 'fog';
return document.documentElement.getAttribute('data-design-theme') || localStorage.getItem('designTheme') || 'fog';
}
function toggleTheme() {
const designTheme = getDesignTheme();
// Terminal theme always uses dark mode - don't allow toggling
if (designTheme === 'terminal') {
return;
}
isDark = !isDark;
updateTheme();
}
function updateTheme() {
const designTheme = getDesignTheme();
// Terminal theme always uses dark mode
if (designTheme === 'terminal') {
document.documentElement.classList.add('dark');
// Don't update localStorage for terminal theme - it's always dark
return;
}
if (isDark) {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
@ -26,6 +36,63 @@ @@ -26,6 +36,63 @@
localStorage.setItem('theme', 'light');
}
}
onMount(() => {
if (typeof window === 'undefined') return;
// Check if terminal theme (always dark)
const designTheme = getDesignTheme();
if (designTheme === 'terminal') {
isDark = true;
updateTheme();
} else {
// Check localStorage and system preference
const stored = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
isDark = stored === 'dark' || (!stored && prefersDark);
updateTheme();
}
// Watch for design theme and storage changes
// Listen for storage changes to sync theme across tabs
const handleStorageChange = (e: StorageEvent) => {
if (e.key === 'theme' || e.key === 'designTheme') {
const designTheme = getDesignTheme();
if (designTheme === 'terminal') {
isDark = true;
} else if (e.key === 'theme') {
isDark = e.newValue === 'dark';
} else {
// Design theme changed - re-read theme
const stored = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
isDark = stored === 'dark' || (!stored && prefersDark);
}
updateTheme();
}
};
// Watch for design theme attribute changes
const observer = new MutationObserver(() => {
const designTheme = getDesignTheme();
if (designTheme === 'terminal') {
isDark = true;
updateTheme();
}
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-design-theme']
});
window.addEventListener('storage', handleStorageChange);
return () => {
observer.disconnect();
window.removeEventListener('storage', handleStorageChange);
};
});
</script>
<button

51
src/lib/components/profile/ProfileMenu.svelte

@ -9,6 +9,9 @@ @@ -9,6 +9,9 @@
import type { NostrEvent } from '../../types/nostr.js';
import { toggleMute, toggleFollow, isMuted, isFollowed } from '../../services/user-actions.js';
import Icon from '../ui/Icon.svelte';
// @ts-ignore - highlight.js default export works at runtime
import hljs from 'highlight.js';
import 'highlight.js/styles/vs2015.css';
interface Props {
pubkey: string;
@ -26,6 +29,25 @@ @@ -26,6 +29,25 @@
let muting = $state(false);
let following = $state(false);
let showJsonModal = $state(false);
let jsonPreviewRef: HTMLElement | null = $state(null);
// Highlight JSON when profileEvent or showJsonModal changes
$effect(() => {
if (jsonPreviewRef && profileEvent && jsonPreviewRef instanceof HTMLElement && showJsonModal) {
try {
const jsonText = JSON.stringify(profileEvent, null, 2);
const highlighted = hljs.highlight(jsonText, { language: 'json' }).value;
jsonPreviewRef.innerHTML = highlighted;
jsonPreviewRef.className = 'hljs language-json';
} catch (err) {
// Fallback to plain text if highlighting fails
if (profileEvent) {
jsonPreviewRef.textContent = JSON.stringify(profileEvent, null, 2);
jsonPreviewRef.className = 'language-json';
}
}
}
});
let isLoggedIn = $derived(sessionManager.isLoggedIn());
let currentUserPubkey = $derived(sessionManager.getCurrentPubkey());
@ -442,7 +464,7 @@ @@ -442,7 +464,7 @@
</div>
<div class="json-modal-body">
{#if profileEvent}
<pre class="json-content">{JSON.stringify(profileEvent, null, 2)}</pre>
<pre class="json-content"><code bind:this={jsonPreviewRef} class="language-json">{JSON.stringify(profileEvent, null, 2)}</code></pre>
{:else}
<p class="text-fog-text-light dark:text-fog-dark-text-light">Loading profile event...</p>
{/if}
@ -688,18 +710,23 @@ @@ -688,18 +710,23 @@
}
.json-content {
background: #1e1e1e !important; /* VS Code dark background, same as code blocks */
border: 1px solid #3e3e3e;
border-radius: 0.25rem;
padding: 1rem;
margin: 0;
font-family: 'Courier New', monospace;
font-size: 0.875rem;
line-height: 1.5;
color: var(--fog-text, #1f2937);
white-space: pre-wrap;
word-break: break-word;
overflow-wrap: break-word;
overflow-x: auto;
}
:global(.dark) .json-content {
color: var(--fog-dark-text, #f9fafb);
.json-content code {
display: block;
overflow-x: auto;
padding: 0;
background: transparent !important;
color: #d4d4d4; /* VS Code text color */
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Droid Sans Mono', 'Source Code Pro', monospace;
font-size: 0.875rem;
line-height: 1.5;
}
@media (max-width: 640px) {
@ -721,6 +748,10 @@ @@ -721,6 +748,10 @@
}
.json-content {
padding: 0.75rem;
}
.json-content code {
font-size: 0.75rem;
}
}

55
src/lib/components/write/AdvancedEditor.svelte

@ -19,6 +19,9 @@ @@ -19,6 +19,9 @@
import MediaViewer from '../content/MediaViewer.svelte';
import type { NostrEvent } from '../../types/nostr.js';
import Icon from '../ui/Icon.svelte';
// @ts-ignore - highlight.js default export works at runtime
import hljs from 'highlight.js';
import 'highlight.js/styles/vs2015.css';
interface Props {
value: string;
@ -46,6 +49,22 @@ @@ -46,6 +49,22 @@
let previewContent = $state<string>('');
let previewEvent = $state<NostrEvent | null>(null);
let eventJson = $state('{}');
let jsonPreviewRef: HTMLElement | null = $state(null);
// Highlight JSON when it changes
$effect(() => {
if (jsonPreviewRef && eventJson && jsonPreviewRef instanceof HTMLElement) {
try {
const highlighted = hljs.highlight(eventJson, { language: 'json' }).value;
jsonPreviewRef.innerHTML = highlighted;
jsonPreviewRef.className = 'hljs language-json';
} catch (err) {
// Fallback to plain text if highlighting fails
jsonPreviewRef.textContent = eventJson;
jsonPreviewRef.className = 'language-json';
}
}
});
// Media viewer state for preview
let mediaViewerOpen = $state(false);
@ -1121,7 +1140,7 @@ @@ -1121,7 +1140,7 @@
</button>
</div>
<div class="modal-body">
<pre class="json-preview">{eventJson}</pre>
<pre class="json-preview"><code bind:this={jsonPreviewRef} class="language-json">{eventJson}</code></pre>
</div>
<div class="modal-footer">
<button onclick={() => {
@ -1492,20 +1511,32 @@ @@ -1492,20 +1511,32 @@
}
.json-preview {
background: var(--fog-highlight, #f3f4f6);
padding: 1rem;
background: #1e1e1e !important; /* VS Code dark background, same as code blocks */
border: 1px solid #3e3e3e;
border-radius: 0.25rem;
overflow: auto;
font-family: 'SF Mono', Monaco, Inconsolata, 'Fira Code', 'Droid Sans Mono', 'Source Code Pro', monospace;
font-size: 0.875rem;
line-height: 1.6;
color: var(--fog-text, #1f2937);
padding: 1rem;
margin: 0;
overflow-x: auto;
max-height: 60vh;
}
.json-preview code {
display: block;
overflow-x: auto;
padding: 0;
background: transparent !important;
color: #d4d4d4; /* VS Code text color */
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Droid Sans Mono', 'Source Code Pro', monospace;
font-size: 0.875rem;
line-height: 1.5;
}
@media (max-width: 768px) {
.json-preview {
padding: 0.75rem;
}
.json-preview code {
font-size: 0.8125rem;
}
}
@ -1513,15 +1544,13 @@ @@ -1513,15 +1544,13 @@
@media (max-width: 640px) {
.json-preview {
padding: 0.5rem;
font-size: 0.75rem;
border-radius: 0.25rem;
max-height: calc(100vh - 200px);
}
}
:global(.dark) .json-preview {
background: var(--fog-dark-highlight, #374151);
color: var(--fog-dark-text, #f9fafb);
.json-preview code {
font-size: 0.75rem;
}
}
.preview-modal {

39
src/lib/components/write/CreateEventForm.svelte

@ -19,6 +19,9 @@ @@ -19,6 +19,9 @@
import { autoExtractTags, ensureDTagForParameterizedReplaceable } from '../../services/auto-tagging.js';
import { isParameterizedReplaceableKind } from '../../types/kind-lookup.js';
import Icon from '../ui/Icon.svelte';
// @ts-ignore - highlight.js default export works at runtime
import hljs from 'highlight.js';
import 'highlight.js/styles/vs2015.css';
const SUPPORTED_KINDS = getWritableKinds();
@ -118,6 +121,22 @@ @@ -118,6 +121,22 @@
let richTextEditorRef: { clearUploadedFiles: () => void; getUploadedFiles: () => Array<{ url: string; imetaTag: string[] }> } | null = $state(null);
let uploadedFiles: Array<{ url: string; imetaTag: string[] }> = $state([]);
let eventJson = $state('{}');
let jsonPreviewRef: HTMLElement | null = $state(null);
// Highlight JSON when it changes
$effect(() => {
if (jsonPreviewRef && eventJson && jsonPreviewRef instanceof HTMLElement) {
try {
const highlighted = hljs.highlight(eventJson, { language: 'json' }).value;
jsonPreviewRef.innerHTML = highlighted;
jsonPreviewRef.className = 'hljs language-json';
} catch (err) {
// Fallback to plain text if highlighting fails
jsonPreviewRef.textContent = eventJson;
jsonPreviewRef.className = 'language-json';
}
}
});
const isUnknownKind = $derived(selectedKind === -1);
const effectiveKind = $derived(isUnknownKind ? (parseInt(customKindId) || 1) : selectedKind);
@ -721,7 +740,7 @@ @@ -721,7 +740,7 @@
</button>
</div>
<div class="modal-body">
<pre class="json-preview">{eventJson}</pre>
<pre class="json-preview"><code bind:this={jsonPreviewRef} class="language-json">{eventJson}</code></pre>
</div>
<div class="modal-footer">
<button onclick={() => {
@ -1565,15 +1584,23 @@ @@ -1565,15 +1584,23 @@
}
.json-preview {
background: #1e293b;
color: #f1f5f9;
padding: 1rem;
background: #1e1e1e !important; /* VS Code dark background, same as code blocks */
border: 1px solid #3e3e3e;
border-radius: 0.5rem;
padding: 1rem;
margin: 0;
overflow-x: auto;
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
}
.json-preview code {
display: block;
overflow-x: auto;
padding: 0;
background: transparent !important;
color: #d4d4d4; /* VS Code text color */
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Droid Sans Mono', 'Source Code Pro', monospace;
font-size: 0.875rem;
line-height: 1.5;
margin: 0;
}
@media (max-width: 768px) {

48
src/lib/modules/comments/CommentForm.svelte

@ -16,6 +16,9 @@ @@ -16,6 +16,9 @@
import { shouldIncludeClientTag } from '../../services/client-tag-preference.js';
import { cacheEvent } from '../../services/cache/event-cache.js';
import { autoExtractTags } from '../../services/auto-tagging.js';
// @ts-ignore - highlight.js default export works at runtime
import hljs from 'highlight.js';
import 'highlight.js/styles/vs2015.css';
interface Props {
threadId: string; // The root event ID
@ -96,6 +99,23 @@ @@ -96,6 +99,23 @@
}
let uploadedFiles: Array<{ url: string; imetaTag: string[] }> = $state([]);
let eventJson = $state('{}');
let jsonPreviewRef: HTMLElement | null = $state(null);
// Highlight JSON when it changes
$effect(() => {
if (jsonPreviewRef && eventJson && jsonPreviewRef instanceof HTMLElement) {
try {
const highlighted = hljs.highlight(eventJson, { language: 'json' }).value;
jsonPreviewRef.innerHTML = highlighted;
jsonPreviewRef.className = 'hljs language-json';
} catch (err) {
// Fallback to plain text if highlighting fails
jsonPreviewRef.textContent = eventJson;
jsonPreviewRef.className = 'language-json';
}
}
});
const isLoggedIn = $derived(sessionManager.isLoggedIn());
// Only show GIF/emoji buttons for non-kind-11 events (kind 1 replies and kind 1111 comments)
@ -518,7 +538,7 @@ @@ -518,7 +538,7 @@
<button onclick={() => showJsonModal = false} class="close-button">×</button>
</div>
<div class="modal-body">
<pre class="json-preview">{eventJson}</pre>
<pre class="json-preview"><code bind:this={jsonPreviewRef} class="language-json">{eventJson}</code></pre>
</div>
<div class="modal-footer">
<button onclick={() => {
@ -723,20 +743,31 @@ @@ -723,20 +743,31 @@
}
.json-preview {
background: #1e293b;
color: #f1f5f9;
padding: 1rem;
background: #1e1e1e !important; /* VS Code dark background, same as code blocks */
border: 1px solid #3e3e3e;
border-radius: 0.5rem;
padding: 1rem;
margin: 0;
overflow-x: auto;
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
}
.json-preview code {
display: block;
overflow-x: auto;
padding: 0;
background: transparent !important;
color: #d4d4d4; /* VS Code text color */
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Droid Sans Mono', 'Source Code Pro', monospace;
font-size: 0.875rem;
line-height: 1.5;
margin: 0;
}
@media (max-width: 768px) {
.json-preview {
padding: 0.75rem;
}
.json-preview code {
font-size: 0.8125rem;
}
}
@ -744,9 +775,12 @@ @@ -744,9 +775,12 @@
@media (max-width: 640px) {
.json-preview {
padding: 0.5rem;
font-size: 0.75rem;
border-radius: 0.25rem;
}
.json-preview code {
font-size: 0.75rem;
}
}
.preview-modal {

52
src/lib/modules/rss/RSSCommentForm.svelte

@ -16,6 +16,9 @@ @@ -16,6 +16,9 @@
import { autoExtractTags } from '../../services/auto-tagging.js';
import type { NostrEvent } from '../../types/nostr.js';
import { cleanTrackingParams } from '../../utils/url-cleaner.js';
// @ts-ignore - highlight.js default export works at runtime
import hljs from 'highlight.js';
import 'highlight.js/styles/vs2015.css';
interface Props {
url: string; // The RSS item URL
@ -95,6 +98,23 @@ @@ -95,6 +98,23 @@
}
let uploadedFiles: Array<{ url: string; imetaTag: string[] }> = $state([]);
let eventJson = $state('{}');
let jsonPreviewRef: HTMLElement | null = $state(null);
// Highlight JSON when it changes
$effect(() => {
if (jsonPreviewRef && eventJson && jsonPreviewRef instanceof HTMLElement) {
try {
const highlighted = hljs.highlight(eventJson, { language: 'json' }).value;
jsonPreviewRef.innerHTML = highlighted;
jsonPreviewRef.className = 'hljs language-json';
} catch (err) {
// Fallback to plain text if highlighting fails
jsonPreviewRef.textContent = eventJson;
jsonPreviewRef.className = 'language-json';
}
}
});
const isLoggedIn = $derived(sessionManager.isLoggedIn());
async function publishComment() {
@ -392,7 +412,7 @@ @@ -392,7 +412,7 @@
<button onclick={() => showJsonModal = false} class="close-button">×</button>
</div>
<div class="modal-body">
<pre class="json-preview">{eventJson}</pre>
<pre class="json-preview"><code bind:this={jsonPreviewRef} class="language-json">{eventJson}</code></pre>
</div>
<div class="modal-footer">
<button onclick={() => {
@ -656,19 +676,31 @@ @@ -656,19 +676,31 @@
}
.json-preview {
background: var(--fog-highlight, #f3f4f6);
padding: 1rem;
background: #1e1e1e !important; /* VS Code dark background, same as code blocks */
border: 1px solid #3e3e3e;
border-radius: 0.25rem;
padding: 1rem;
margin: 0;
overflow-x: auto;
font-family: 'Courier New', monospace;
}
.json-preview code {
display: block;
overflow-x: auto;
padding: 0;
background: transparent !important;
color: #d4d4d4; /* VS Code text color */
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Droid Sans Mono', 'Source Code Pro', monospace;
font-size: 0.875rem;
white-space: pre-wrap;
word-wrap: break-word;
line-height: 1.5;
}
@media (max-width: 768px) {
.json-preview {
padding: 0.75rem;
}
.json-preview code {
font-size: 0.8125rem;
}
}
@ -676,14 +708,12 @@ @@ -676,14 +708,12 @@
@media (max-width: 640px) {
.json-preview {
padding: 0.5rem;
font-size: 0.75rem;
border-radius: 0.25rem;
}
}
:global(.dark) .json-preview {
background: var(--fog-dark-highlight, #374151);
color: var(--fog-dark-text, #f9fafb);
.json-preview code {
font-size: 0.75rem;
}
}
.modal-footer {

95
src/routes/+layout.svelte

@ -17,17 +17,7 @@ @@ -17,17 +17,7 @@
// Initialize theme and preferences from localStorage immediately (before any components render)
if (browser) {
// Initialize theme
const storedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const shouldBeDark = storedTheme === 'dark' || (!storedTheme && prefersDark);
if (shouldBeDark) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
// Initialize other preferences
// Initialize other preferences first (design theme affects theme)
const textSize = localStorage.getItem('textSize') || 'medium';
const lineSpacing = localStorage.getItem('lineSpacing') || 'normal';
const contentWidth = localStorage.getItem('contentWidth') || 'medium';
@ -38,6 +28,31 @@ @@ -38,6 +28,31 @@
document.documentElement.setAttribute('data-content-width', contentWidth);
document.documentElement.setAttribute('data-design-theme', designTheme);
// Initialize theme (terminal theme always uses dark mode)
const storedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
let shouldBeDark: boolean;
if (designTheme === 'terminal') {
// Terminal theme always uses dark mode
shouldBeDark = true;
} else if (storedTheme) {
shouldBeDark = storedTheme === 'dark';
} else {
shouldBeDark = prefersDark;
}
if (shouldBeDark) {
document.documentElement.classList.add('dark');
// Ensure localStorage is set correctly
if (designTheme !== 'terminal') {
localStorage.setItem('theme', 'dark');
}
} else {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
}
// Try to restore session synchronously if possible
// This ensures session is restored before any components render
(async () => {
@ -52,7 +67,59 @@ @@ -52,7 +67,59 @@
}
// Also restore in onMount as fallback
onMount(async () => {
onMount(() => {
// Set up storage listener synchronously
let storageCleanup: (() => void) | undefined;
if (browser) {
// Listen for storage changes to sync settings across tabs
const handleStorageChange = (e: StorageEvent) => {
if (e.key === 'theme') {
const designTheme = document.documentElement.getAttribute('data-design-theme') || 'fog';
if (designTheme === 'terminal') {
// Terminal theme always uses dark mode
document.documentElement.classList.add('dark');
} else if (e.newValue === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
} else if (e.key === 'designTheme') {
const newDesignTheme = e.newValue || 'fog';
document.documentElement.setAttribute('data-design-theme', newDesignTheme);
// Terminal theme always uses dark mode
if (newDesignTheme === 'terminal') {
document.documentElement.classList.add('dark');
} else {
// Re-apply theme from localStorage
const storedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const shouldBeDark = storedTheme === 'dark' || (!storedTheme && prefersDark);
if (shouldBeDark) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}
} else if (e.key === 'textSize') {
document.documentElement.setAttribute('data-text-size', e.newValue || 'medium');
} else if (e.key === 'lineSpacing') {
document.documentElement.setAttribute('data-line-spacing', e.newValue || 'normal');
} else if (e.key === 'contentWidth') {
document.documentElement.setAttribute('data-content-width', e.newValue || 'medium');
}
};
window.addEventListener('storage', handleStorageChange);
// Cleanup
storageCleanup = () => {
window.removeEventListener('storage', handleStorageChange);
};
}
// Async operations
(async () => {
try {
// Only restore if there's no active session
// This prevents overwriting sessions that were just created during login
@ -102,6 +169,10 @@ @@ -102,6 +169,10 @@
} catch (error) {
console.error('Failed to restore session:', error);
}
})();
// Return cleanup function
return storageCleanup;
});
function handleUpdateComplete() {

Loading…
Cancel
Save