Refactor/reacrhitecture to fsd+ #49

Merged
ilia merged 70 commits from refactor/reacrhitecture-to-fsd+ into main 2026-06-03 09:55:47 +00:00
6 changed files with 33 additions and 9 deletions
Showing only changes of commit 3dca11fea8 - Show all commits
+3 -1
View File
@@ -3,7 +3,7 @@
Application shell with providers and page wrapper
-->
<script lang="ts">
import { themeManager } from '$features/ChangeAppTheme';
import { getThemeManager } from '$features/ChangeAppTheme';
import G from '$shared/assets/G.svg';
import { ResponsiveProvider } from '$shared/lib';
import { cn } from '$shared/lib';
@@ -32,6 +32,8 @@ interface Props {
let { children }: Props = $props();
let fontsReady = $state(true);
const themeManager = getThemeManager();
const theme = $derived(themeManager.value);
onMount(() => themeManager.init());
+1 -1
View File
@@ -1 +1 @@
export { themeManager } from './store/ThemeManager/ThemeManager.svelte';
export { getThemeManager } from './store/ThemeManager/ThemeManager.svelte';
@@ -194,15 +194,26 @@ class ThemeManager {
}
}
let _themeManager: ThemeManager | undefined;
/**
* Singleton theme manager instance
* App-wide theme manager, created on first access.
*
* Use throughout the app for consistent theme state.
* Lazy so its persistent-store subscription isn't set up at module load.
* Call init() on mount and destroy() on unmount (see Layout).
*/
export const themeManager = new ThemeManager();
export function getThemeManager(): ThemeManager {
return (_themeManager ??= new ThemeManager());
}
// test-only reset, so specs don't share persisted theme state
export function __resetThemeManager() {
_themeManager?.destroy();
_themeManager = undefined;
}
/**
* ThemeManager class exported for testing purposes
* Use the singleton `themeManager` in application code.
* Use the `getThemeManager()` accessor in application code.
*/
export { ThemeManager };
@@ -22,8 +22,9 @@ const { Story } = defineMeta({
</script>
<script lang="ts">
import { themeManager } from '$features/ChangeAppTheme';
import { getThemeManager } from '$features/ChangeAppTheme';
const themeManager = getThemeManager();
// Current theme state for display
const currentTheme = $derived(themeManager.value);
const themeSource = $derived(themeManager.source);
@@ -8,10 +8,11 @@ import { IconButton } from '$shared/ui';
import MoonIcon from '@lucide/svelte/icons/moon';
import SunIcon from '@lucide/svelte/icons/sun';
import { getContext } from 'svelte';
import { themeManager } from '../../model';
import { getThemeManager } from '../../model';
const responsive = getContext<ResponsiveManager>('responsive');
const themeManager = getThemeManager();
const theme = $derived(themeManager.value);
</script>
@@ -3,16 +3,25 @@ import {
render,
screen,
} from '@testing-library/svelte';
import { themeManager } from '../../model';
import { afterEach } from 'vitest';
import { getThemeManager } from '../../model';
import { __resetThemeManager } from '../../model/store/ThemeManager/ThemeManager.svelte';
import ThemeSwitch from './ThemeSwitch.svelte';
const context = new Map([['responsive', { isMobile: false }]]);
describe('ThemeSwitch', () => {
let themeManager: ReturnType<typeof getThemeManager>;
beforeEach(() => {
themeManager = getThemeManager();
themeManager.setTheme('light');
});
afterEach(() => {
__resetThemeManager();
});
describe('Rendering', () => {
it('renders an icon button', () => {
render(ThemeSwitch, { context });