DesQTA uses Svelte stores for managing global application state. Stores provide reactive state that can be accessed from any component in the application.
Stores are located in src/lib/stores/ and follow a consistent pattern:
src/lib/stores/
├── theme.ts # Theme and accent color
├── themeBuilderSidebar.ts # Theme builder UI state
└── toast.ts # Toast notifications
Writable stores allow reading and writing values:
import { writable } from 'svelte/store' ;
export const count = writable ( 0 );
// In component
import { count } from '$lib/stores/count' ;
count. set ( 10 ); // Set value
count. update ( n => n + 1 ); // Update value
$count; // Read value (auto-subscribe)
Readable stores are read-only:
import { readable } from 'svelte/store' ;
export const time = readable ( new Date (), ( set ) => {
const interval = setInterval (() => {
set ( new Date ());
}, 1000 );
return () => clearInterval (interval);
});
Derived stores compute values from other stores:
import { derived } from 'svelte/store' ;
import { count } from './count' ;
export const doubled = derived (count, $count => $count * 2 );
The theme store manages application theming and accent colors.
src/lib/stores/theme.ts
// Basic theme mode (light/dark/system)
export const theme = writable < 'light' | 'dark' | 'system' >( 'system' );
// Accent color
export const accentColor = writable ( '#3b82f6' );
// Current theme pack name
export const currentTheme = writable ( 'default' );
// Theme manifest (theme configuration)
export const themeManifest = writable < ThemeManifest | null >( null );
// Custom CSS
export const customCSS = writable ( '' );
// Previewing theme (not yet applied)
export const previewingTheme = writable < string | null >( null );
// Theme properties from manifest
export const themeProperties = derived (
[currentTheme, themeManifest],
([ $currentTheme , $manifest ]) => {
if ( ! $manifest) return {};
return $manifest.customProperties;
}
);
import { loadAccentColor } from '$lib/stores/theme' ;
await loadAccentColor ();
// Loads accent color from settings and updates store
import { loadTheme } from '$lib/stores/theme' ;
await loadTheme ();
// Loads theme preference from settings and applies it
import { updateAccentColor } from '$lib/stores/theme' ;
await updateAccentColor ( '#6366f1' );
// Updates accent color and saves to settings
import { updateTheme } from '$lib/stores/theme' ;
await updateTheme ( 'dark' );
// Updates theme mode and saves to settings
import { loadAndApplyTheme } from '$lib/stores/theme' ;
await loadAndApplyTheme ( 'midnight' );
// Loads theme pack, applies it, and updates all related stores
import { startThemePreview, cancelThemePreview, applyPreviewTheme } from '$lib/stores/theme' ;
// Start preview (doesn't save)
await startThemePreview ( 'sunset' );
// Cancel preview (restore previous)
await cancelThemePreview ();
// Apply preview as active theme
await applyPreviewTheme ();
< script lang = "ts" >
import { theme, accentColor, loadTheme, updateTheme } from '$lib/stores/theme' ;
import { onMount } from 'svelte' ;
onMount ( async () => {
await loadTheme ();
});
function toggleTheme () {
const current = $theme;
const next = current === 'dark' ? 'light' : 'dark' ;
updateTheme (next);
}
</ script >
< div class = "bg-white dark:bg-zinc-900" style = "--accent-color: { $ accentColor }" >
< button onclick ={toggleTheme}>
Current theme: {$theme}
</ button >
</ div >
The toast store manages notification toasts.
src/lib/stores/toast.ts
import { toast } from '$lib/stores/toast' ;
// Show success toast
toast. success ( 'Operation completed successfully!' );
// Show error toast
toast. error ( 'Something went wrong' );
// Show info toast
toast. info ( 'Here is some information' );
// Show warning toast
toast. warning ( 'Please review your changes' );
// Dismiss specific toast
toast. dismiss (toastId);
// Clear all toasts
toast. clear ();
interface ToastStore {
success ( message : string , duration ?: number ) : string ;
error ( message : string , duration ?: number ) : string ;
info ( message : string , duration ?: number ) : string ;
warning ( message : string , duration ?: number ) : string ;
dismiss ( id : string ) : void ;
clear () : void ;
}
< script lang = "ts" >
import { toast } from '$lib/stores/toast' ;
async function saveData () {
try {
await save ();
toast. success ( 'Saved successfully!' );
} catch (error) {
toast. error ( 'Failed to save' );
}
}
</ script >
< button onclick ={saveData}>Save</ button >
Manages the theme builder sidebar state.
src/lib/stores/themeBuilderSidebar.ts
import { themeBuilderSidebarOpen } from '$lib/stores/themeBuilderSidebar' ;
// Toggle sidebar
themeBuilderSidebarOpen. update ( open => ! open);
// Set state
themeBuilderSidebarOpen. set ( true );
// src/lib/stores/myStore.ts
import { writable, derived } from 'svelte/store' ;
// Writable store
export const myValue = writable < string >( 'initial' );
// Derived store
export const myComputed = derived (myValue, $value => {
return $value. toUpperCase ();
});
// Store with actions
export const myStore = {
subscribe: myValue.subscribe,
set: myValue.set,
update: myValue.update,
reset : () => myValue. set ( 'initial' ),
customAction : ( value : string ) => {
// Custom logic
myValue. set (value);
}
};
// src/lib/stores/dataStore.ts
import { writable } from 'svelte/store' ;
import { invoke } from '@tauri-apps/api/core' ;
export const data = writable < any []>([]);
export const loading = writable ( false );
export const error = writable < string | null >( null );
export const dataStore = {
subscribe: data.subscribe,
loading: {
subscribe: loading.subscribe
},
error: {
subscribe: error.subscribe
},
async load () {
loading. set ( true );
error. set ( null );
try {
const result = await invoke ( 'get_data' );
data. set (result);
} catch (e) {
error. set (e instanceof Error ? e.message : 'Failed to load' );
} finally {
loading. set ( false );
}
},
reset () {
data. set ([]);
error. set ( null );
}
};
// src/lib/stores/persistentStore.ts
import { writable } from 'svelte/store' ;
import { invoke } from '@tauri-apps/api/core' ;
function createPersistentStore < T >( key : string , defaultValue : T ) {
const store = writable < T >(defaultValue);
// Load from settings
async function load () {
try {
const subset = await invoke < any >( 'get_settings_subset' , { keys: [key] });
if (subset?.[key] !== undefined ) {
store. set (subset[key]);
}
} catch (e) {
console. error ( `Failed to load ${ key }:` , e);
}
}
// Save to settings
async function save ( value : T ) {
try {
const { saveSettingsWithQueue } = await import ( '../services/settingsSync' );
await saveSettingsWithQueue ({ [key]: value });
store. set (value);
} catch (e) {
console. error ( `Failed to save ${ key }:` , e);
}
}
return {
subscribe: store.subscribe,
set : ( value : T ) => save (value),
update : ( updater : ( value : T ) => T ) => {
store. update ( current => {
const newValue = updater (current);
save (newValue);
return newValue;
});
},
load
};
}
export const myPersistentValue = createPersistentStore ( 'my_key' , 'default' );
Each store should manage a single concern:
// ✅ Good - Focused store
export const userPreferences = writable ({ theme: 'dark' });
// ❌ Avoid - Too broad
export const appState = writable ({ user: {}, theme: {}, settings: {} });
// ✅ Good - Derived store
export const fullName = derived (
[firstName, lastName],
([ $first , $last ]) => `${ $first } ${ $last }`
);
// ❌ Avoid - Computing in component
let fullName = $derived ( `${ $firstName } ${ $lastName }` );
// ✅ Good - Actions included
export const counter = {
subscribe: count.subscribe,
increment : () => count. update ( n => n + 1 ),
decrement : () => count. update ( n => n - 1 ),
reset : () => count. set ( 0 )
};
// ❌ Avoid - Direct store manipulation
count. update ( n => n + 1 );
// ✅ Good - Loading and error states
export const dataStore = {
data: writable ([]),
loading: writable ( false ),
error: writable ( null ),
async load () {
loading. set ( true );
try {
const result = await fetchData ();
data. set (result);
} catch (e) {
error. set (e.message);
} finally {
loading. set ( false );
}
}
};
// ✅ Good - Typed store
interface User {
id : number ;
name : string ;
}
export const user = writable < User | null >( null );
// ❌ Avoid - Untyped store
export const user = writable ( null );
import { derived } from 'svelte/store' ;
import { theme } from './theme' ;
import { accentColor } from './theme' ;
import { user } from './user' ;
// Combine multiple stores
export const appState = derived (
[theme, accentColor, user],
([ $theme , $accent , $user ]) => ({
theme: $theme,
accentColor: $accent,
user: $user,
isAuthenticated: $user !== null
})
);
// Store A depends on Store B
export const storeA = writable ( 0 );
export const storeB = derived (storeA, $a => $a * 2 );
export const storeC = derived (storeB, $b => $b + 10 );
import { get } from 'svelte/store' ;
import { count, increment } from './counter' ;
test ( 'increment increases count' , () => {
count. set ( 0 );
increment ();
expect ( get (count)). toBe ( 1 );
});