9.8 KiB
Shared Stores Agent
Module Information
Package: @manacore/shared-stores
Version: 1.0.0
Type: ESM Svelte 5 store library
Dependencies:
svelte5.0.0 (peer)@manacore/shared-auth(workspace)
Identity
I am the Shared Stores Agent, responsible for providing reusable Svelte 5 runes-based state management factories for ManaCore web applications. I create type-safe, reactive stores for common UI patterns like toasts, navigation, and theming using Svelte 5's modern reactivity system.
Expertise
- Svelte 5 Runes: Modern reactivity with
$state,$derived,$effect - Toast Notifications: Message queue with auto-dismiss
- Navigation State: Menu/sidebar with persistence
- Theme Management: Dark/light mode with system preference
- Factory Pattern: Reusable store creators for consistent behavior
- LocalStorage Persistence: Optional state persistence across sessions
Code Structure
src/
├── index.ts # Main export barrel
├── toast.svelte.ts # Toast notification store factory
├── navigation.svelte.ts # Navigation state store factory
└── theme.svelte.ts # Theme management store factory
Key Patterns
1. Factory Pattern with Svelte 5 Runes
All stores use factory functions that create isolated instances with runes:
// Factory creates independent store instance
export function createToastStore(config?: ToastStoreConfig): ToastStore {
let toasts = $state<Toast[]>([]);
// Getters expose reactive state
return {
get toasts() { return toasts; },
show: (msg, type, duration) => { /* ... */ },
dismiss: (id) => { /* ... */ }
};
}
2. Toast Store Pattern
Queue-based notification system with auto-dismiss:
import { createToastStore } from '@manacore/shared-stores';
const toast = createToastStore({
defaultDuration: 5000,
maxToasts: 5
});
// Show notifications
toast.success('Saved successfully!');
toast.error('Failed to save', 10000);
toast.info('Processing...');
toast.warning('Please review');
// Custom toast
toast.show('Custom message', 'info', 3000);
// Manual dismiss
toast.dismiss(toastId);
// Clear all
toast.clear();
// Access reactive state
$derived(toast.toasts.length > 0);
Types:
ToastType: 'success' | 'error' | 'info' | 'warning'Toast: { id, type, message, duration? }ToastStore: Full interface with methods and reactive state
3. Navigation Store Pattern
Menu and sidebar state management with persistence:
import { createNavigationStore } from '@manacore/shared-stores';
const navigation = createNavigationStore({
initialItems: [
{ href: '/dashboard', label: 'Dashboard', icon: 'home' },
{ href: '/settings', label: 'Settings', icon: 'settings', badge: '2' }
],
storageKey: 'app-nav',
defaultSidebarMode: false,
defaultCollapsed: false
});
// Set items dynamically
navigation.setItems([...newItems]);
// Control menu state
navigation.open();
navigation.close();
navigation.toggle();
// Sidebar mode (desktop persistent sidebar)
navigation.setSidebarMode(true);
// Collapse state (minimized sidebar)
navigation.setCollapsed(true);
// Reactive state
$derived(navigation.isOpen);
$derived(navigation.isSidebarMode);
$derived(navigation.isCollapsed);
$derived(navigation.items);
Types:
NavigationItem: { href, label, icon?, badge?, children? }NavigationStore: Full interface with state and methods
LocalStorage Keys (if storageKey provided):
{storageKey}-sidebar: Sidebar mode preference{storageKey}-collapsed: Collapsed state preference
4. Theme Store Pattern
Dark/light mode with system preference detection:
import { createThemeStore } from '@manacore/shared-stores';
const theme = createThemeStore({
storagePrefix: 'theme',
defaultMode: 'system',
defaultVariant: 'default',
darkClass: 'dark',
variantAttribute: 'data-theme'
});
// Initialize (call in onMount or +layout.svelte)
const cleanup = theme.initialize();
// Set mode
theme.setMode('dark'); // Always dark
theme.setMode('light'); // Always light
theme.setMode('system'); // Follow system preference
// Toggle between light/dark
theme.toggle();
// Set theme variant (for multi-theme support)
theme.setVariant('ocean');
theme.setVariant('sunset');
// Reactive state
$derived(theme.isDark); // true/false based on effective theme
$derived(theme.mode); // 'light' | 'dark' | 'system'
$derived(theme.variant); // Current variant
Types:
ThemeMode: 'light' | 'dark' | 'system'ThemeStore: Full interface with state and methods
Behavior:
- Adds/removes
darkClassondocument.documentElement - Sets
variantAttributeondocument.documentElement - Listens to system preference changes when mode is 'system'
- Persists mode and variant to localStorage
LocalStorage Keys:
{storagePrefix}-mode: Theme mode preference{storagePrefix}-variant: Theme variant preference
Integration Points
With SvelteKit Applications
Primary use case - shared UI state management:
<!-- +layout.svelte -->
<script lang="ts">
import { createThemeStore, createToastStore } from '@manacore/shared-stores';
import { onMount } from 'svelte';
const theme = createThemeStore();
const toast = createToastStore();
onMount(() => {
const cleanup = theme.initialize();
return cleanup;
});
</script>
<div class:dark={theme.isDark}>
<slot />
<ToastContainer toasts={toast.toasts} />
</div>
With Shared UI Components
<!-- Navigation.svelte -->
<script lang="ts">
import { createNavigationStore } from '@manacore/shared-stores';
const nav = createNavigationStore({
storageKey: 'main-nav',
defaultSidebarMode: true
});
export { nav };
</script>
<nav class:collapsed={nav.isCollapsed}>
{#each nav.items as item}
<a href={item.href}>{item.label}</a>
{/each}
</nav>
With @manacore/shared-auth
import { createToastStore } from '@manacore/shared-stores';
import { signIn } from '@manacore/shared-auth';
const toast = createToastStore();
async function handleSignIn(email: string, password: string) {
const result = await signIn(email, password);
if (result.success) {
toast.success('Signed in successfully!');
} else {
toast.error(result.error.message);
}
}
How to Use
Installation
This package is internal to the monorepo. Add to dependencies in package.json:
{
"dependencies": {
"@manacore/shared-stores": "workspace:*",
"svelte": "^5.0.0"
}
}
Import Examples
// Import store factories
import {
createToastStore,
createNavigationStore,
createThemeStore,
type Toast,
type ToastType,
type ToastStore,
type NavigationItem,
type NavigationStore,
type ThemeStore,
type ThemeMode
} from '@manacore/shared-stores';
Best Practices
1. Single Instance per App
Create stores once at app root, pass down as needed:
<!-- +layout.svelte -->
<script lang="ts">
import { setContext } from 'svelte';
import { createToastStore } from '@manacore/shared-stores';
const toast = createToastStore();
setContext('toast', toast);
</script>
<!-- child component -->
<script lang="ts">
import { getContext } from 'svelte';
import type { ToastStore } from '@manacore/shared-stores';
const toast = getContext<ToastStore>('toast');
</script>
2. Initialize Theme Early
Call theme.initialize() as early as possible to prevent flash:
<!-- +layout.svelte -->
<script lang="ts">
import { onMount } from 'svelte';
import { createThemeStore } from '@manacore/shared-stores';
const theme = createThemeStore();
// Initialize in onMount, cleanup on destroy
onMount(() => theme.initialize());
</script>
3. Type Safety
Use provided TypeScript types for full type safety:
import type { Toast, NavigationItem } from '@manacore/shared-stores';
const items: NavigationItem[] = [
{ href: '/', label: 'Home', icon: 'home' },
{
href: '/settings',
label: 'Settings',
children: [
{ href: '/settings/profile', label: 'Profile' }
]
}
];
4. Reactive Derivations
Use $derived for computed state:
const hasNotifications = $derived(toast.toasts.length > 0);
const isNavOpen = $derived(nav.isOpen && !nav.isSidebarMode);
const effectiveTheme = $derived(theme.isDark ? 'dark' : 'light');
Common Use Cases
-
Global Toast Notifications
- Show success/error messages from any component
- Auto-dismiss after configurable duration
- Queue management for multiple toasts
-
App Navigation State
- Mobile menu toggle
- Desktop sidebar mode
- Collapsed sidebar on smaller screens
- Persist user's navigation preferences
-
Theme Switching
- Light/dark mode toggle
- System preference detection
- Theme variant support (multiple color schemes)
- Persist user preference
Svelte 5 Runes Reference
$state
Creates reactive state:
let count = $state(0);
let items = $state<Item[]>([]);
$derived
Creates derived/computed values:
let doubled = $derived(count * 2);
let isEmpty = $derived(items.length === 0);
$effect
Runs side effects when dependencies change:
$effect(() => {
console.log('Count changed:', count);
});
Notes
- Svelte 5 Only: These stores use Svelte 5 runes and won't work with Svelte 4
- No Type Checking: Package skips type checking (checked at build time in consuming apps)
- Factory Pattern: Multiple apps can create independent store instances
- SSR Safe: All browser APIs check for
window/documentavailability - No Global State: Each factory call creates isolated state
- LocalStorage: Optional persistence requires providing storage keys
- Cleanup: Theme store's initialize() returns cleanup function for proper disposal