diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index db8d0e2..d4bb9ac 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -10,7 +10,11 @@ pub fn run() { if cfg!(debug_assertions) { app.handle().plugin( tauri_plugin_log::Builder::default() - .level(log::LevelFilter::Info) + .level(log::LevelFilter::Warn) // Reduce log level to reduce noise + .filter(|metadata| { + // Filter out tao event loop warnings + !metadata.target().contains("tao::platform_impl::platform::event_loop") + }) .build(), )?; } diff --git a/src/components/TitleBar.tsx b/src/components/TitleBar.tsx index 9ee6184..abffe10 100644 --- a/src/components/TitleBar.tsx +++ b/src/components/TitleBar.tsx @@ -6,24 +6,35 @@ const TitleBar: React.FC = () => { const [isMaximized, setIsMaximized] = useState(false) useEffect(() => { + let isActive = true + const checkMaximized = async () => { + if (!isActive) return + try { const window = getCurrentWindow() const maximized = await window.isMaximized() - setIsMaximized(maximized) + if (isActive) { + setIsMaximized(maximized) + } } catch (error) { console.error('Failed to check window state:', error) } } + // Throttled check function + let checkTimeout: number | null = null + const throttledCheck = () => { + if (checkTimeout) clearTimeout(checkTimeout) + checkTimeout = setTimeout(checkMaximized, 100) + } + checkMaximized() - // Listen for window resize events + // Listen for window resize events with throttling const setupListener = async () => { try { - const unlisten = await getCurrentWindow().listen('tauri://resize', () => { - checkMaximized() - }) + const unlisten = await getCurrentWindow().listen('tauri://resize', throttledCheck) return unlisten } catch (error) { console.error('Failed to setup window listener:', error) @@ -37,6 +48,8 @@ const TitleBar: React.FC = () => { }) return () => { + isActive = false + if (checkTimeout) clearTimeout(checkTimeout) if (unlistenFn) { unlistenFn() } @@ -75,6 +88,10 @@ const TitleBar: React.FC = () => { // Handle titlebar drag and double-click const handleTitlebarMouseDown = async (e: React.MouseEvent) => { + // Prevent event bubbling + e.preventDefault() + e.stopPropagation() + if (e.buttons === 1) { // Primary (left) button try { const window = getCurrentWindow() @@ -84,8 +101,14 @@ const TitleBar: React.FC = () => { const maximized = await window.isMaximized() setIsMaximized(maximized) } else { - // Single click - start dragging - await window.startDragging() + // Single click - start dragging with throttling + setTimeout(async () => { + try { + await window.startDragging() + } catch (error) { + console.error('Failed to start dragging:', error) + } + }, 10) } } catch (error) { console.error('Failed to handle titlebar interaction:', error) diff --git a/src/hooks/useDebounce.ts b/src/hooks/useDebounce.ts new file mode 100644 index 0000000..cb338ad --- /dev/null +++ b/src/hooks/useDebounce.ts @@ -0,0 +1,73 @@ +import { useState, useEffect } from 'react' + +/** + * Custom hook for debouncing values to prevent excessive re-renders + * + * @param value - The value to debounce + * @param delay - The delay in milliseconds + * @returns The debounced value + */ +export function useDebounce(value: T, delay: number): T { + const [debouncedValue, setDebouncedValue] = useState(value) + + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedValue(value) + }, delay) + + return () => { + clearTimeout(handler) + } + }, [value, delay]) + + return debouncedValue +} + +/** + * Custom hook for throttling function calls + * + * @param callback - The function to throttle + * @param delay - The delay in milliseconds + * @returns The throttled function + */ +export function useThrottle any>( + callback: T, + delay: number +): T { + const [lastCall, setLastCall] = useState(0) + + const throttledCallback = ((...args: Parameters) => { + const now = Date.now() + if (now - lastCall >= delay) { + setLastCall(now) + return callback(...args) + } + }) as T + + return throttledCallback +} + +/** + * Custom hook for preventing rapid state updates + * + * @param initialValue - Initial state value + * @param delay - Minimum delay between updates in milliseconds + * @returns [state, setState] tuple + */ +export function useThrottledState( + initialValue: T, + delay: number = 100 +): [T, (value: T) => void] { + const [state, setState] = useState(initialValue) + const [lastUpdate, setLastUpdate] = useState(0) + + const setThrottledState = (value: T) => { + const now = Date.now() + if (now - lastUpdate >= delay) { + setState(value) + setLastUpdate(now) + } + } + + return [state, setThrottledState] +} diff --git a/src/stores/useAIVideoStore.ts b/src/stores/useAIVideoStore.ts index 48b5c9a..a601a1f 100644 --- a/src/stores/useAIVideoStore.ts +++ b/src/stores/useAIVideoStore.ts @@ -90,6 +90,15 @@ export const useAIVideoStore = create((set, get) => ({ }, updateJob: (jobId, updates) => { + // Throttle rapid updates to prevent excessive re-renders + const now = Date.now() + const lastUpdate = get().jobs.find(job => job.id === jobId)?.endTime || 0 + + if (now - lastUpdate < 100 && updates.progress !== undefined) { + // Skip rapid progress updates + return + } + set(state => ({ jobs: state.jobs.map(job => job.id === jobId ? { ...job, ...updates } : job