diff --git a/src/renderer/stores/preview-store.ts b/src/renderer/stores/preview-store.ts new file mode 100644 index 0000000..250c601 --- /dev/null +++ b/src/renderer/stores/preview-store.ts @@ -0,0 +1,25 @@ +import { create } from 'zustand'; + +interface PreviewState { + source: string; + scrollRatio: number; + setSource: (s: string) => void; + setScrollRatio: (r: number) => void; +} + +const DEBOUNCE_MS = 300; +let timer: ReturnType | null = null; +let pending: string = ''; + +export const usePreviewStore = create((set) => ({ + source: '', + scrollRatio: 0, + setSource: (s) => { + pending = s; + if (timer) clearTimeout(timer); + timer = setTimeout(() => { + set({ source: pending }); + }, DEBOUNCE_MS); + }, + setScrollRatio: (r) => set({ scrollRatio: r }), +})); diff --git a/tests/unit/stores/preview-store.test.ts b/tests/unit/stores/preview-store.test.ts new file mode 100644 index 0000000..92cd380 --- /dev/null +++ b/tests/unit/stores/preview-store.test.ts @@ -0,0 +1,23 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { usePreviewStore } from '@/stores/preview-store'; + +describe('usePreviewStore', () => { + beforeEach(() => { + vi.useFakeTimers(); + usePreviewStore.setState({ html: '', scrollRatio: 0, source: '' }); + }); + + it('debounces source updates (300 ms)', () => { + usePreviewStore.getState().setSource('# a'); + usePreviewStore.getState().setSource('# b'); + usePreviewStore.getState().setSource('# c'); + expect(usePreviewStore.getState().source).toBe(''); + vi.advanceTimersByTime(300); + expect(usePreviewStore.getState().source).toBe('# c'); + }); + + it('updates scroll ratio', () => { + usePreviewStore.getState().setScrollRatio(0.5); + expect(usePreviewStore.getState().scrollRatio).toBe(0.5); + }); +});