diff --git a/apps/web/src/components/settings/settings-content.tsx b/apps/web/src/components/settings/settings-content.tsx index 73bb4a0..8866096 100644 --- a/apps/web/src/components/settings/settings-content.tsx +++ b/apps/web/src/components/settings/settings-content.tsx @@ -27,9 +27,15 @@ interface SettingsContentProps { initialSettings: UserSettings; user: { name: string; email: string; image: string | null }; githubProfile: GitHubProfile; + onThemeTransition?: () => void; } -export function SettingsContent({ initialSettings, user, githubProfile }: SettingsContentProps) { +export function SettingsContent({ + initialSettings, + user, + githubProfile, + onThemeTransition, +}: SettingsContentProps) { const [activeTab, setActiveTab] = useState("general"); const [settings, setSettings] = useState(initialSettings); const { emit } = useMutationEvents(); @@ -126,7 +132,11 @@ export function SettingsContent({ initialSettings, user, githubProfile }: Settin {/* Content — only this area scrolls */}
{activeTab === "general" && ( - + )} {activeTab === "editor" && } {activeTab === "ai" && ( diff --git a/apps/web/src/components/settings/settings-dialog.tsx b/apps/web/src/components/settings/settings-dialog.tsx index 952a8aa..4422ee2 100644 --- a/apps/web/src/components/settings/settings-dialog.tsx +++ b/apps/web/src/components/settings/settings-dialog.tsx @@ -1,5 +1,6 @@ "use client"; +import { useCallback, useRef, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; import * as VisuallyHidden from "@radix-ui/react-visually-hidden"; @@ -53,11 +54,35 @@ export function SettingsDialog({ open, onOpenChange, user, githubProfile }: Sett gcTime: 15 * 60 * 1000, }); + const [isThemeTransitioning, setIsThemeTransitioning] = useState(false); + const transitionTimeoutRef = useRef | null>(null); + + const onThemeTransition = useCallback(() => { + if (transitionTimeoutRef.current) { + clearTimeout(transitionTimeoutRef.current); + } + setIsThemeTransitioning(true); + transitionTimeoutRef.current = setTimeout(() => { + setIsThemeTransitioning(false); + }, 1000); + }, []); + + const handleInteractOutside = useCallback( + (e: Event) => { + if (isThemeTransitioning) { + e.preventDefault(); + } + }, + [isThemeTransitioning], + ); + return ( Settings @@ -68,6 +93,7 @@ export function SettingsDialog({ open, onOpenChange, user, githubProfile }: Sett initialSettings={settings} user={user} githubProfile={githubProfile} + onThemeTransition={onThemeTransition} /> ) : (
diff --git a/apps/web/src/components/settings/tabs/general-tab.tsx b/apps/web/src/components/settings/tabs/general-tab.tsx index 1201184..bc72d28 100644 --- a/apps/web/src/components/settings/tabs/general-tab.tsx +++ b/apps/web/src/components/settings/tabs/general-tab.tsx @@ -10,6 +10,7 @@ import type { UserSettings } from "@/lib/user-settings-store"; interface GeneralTabProps { settings: UserSettings; onUpdate: (updates: Partial) => Promise; + onThemeTransition?: () => void; } function ThemeGrid({ @@ -84,10 +85,24 @@ const RADIUS_OPTIONS: { id: BorderRadiusPreset; label: string; description: stri { id: "large", label: "Large", description: "Soft & rounded" }, ]; -export function GeneralTab({ settings: _settings, onUpdate: _onUpdate }: GeneralTabProps) { +export function GeneralTab({ + settings: _settings, + onUpdate: _onUpdate, + onThemeTransition, +}: GeneralTabProps) { const { themeId, mode, borderRadius, setTheme, toggleMode, setBorderRadius, themes } = useColorTheme(); + const handleSetTheme = (id: string) => { + onThemeTransition?.(); + setTheme(id); + }; + + const handleToggleMode = () => { + onThemeTransition?.(); + toggleMode(); + }; + return (
{/* Mode toggle */} @@ -105,7 +120,9 @@ export function GeneralTab({ settings: _settings, onUpdate: _onUpdate }: General