import { useLingui } from "@lingui/react/macro"; import type { AvatarStyle, SpaceMemoryConfig } from "@rakazo/contracts"; import { Button, Dialog, DialogClose, DialogContent, DialogTitle } from "@rakazo/ui-web"; import { Bell, Brain, Clapperboard, CloudDownload, Cpu, Gauge, LayoutGrid, Monitor, Settings, ShieldCheck, UserRound, Volume2, XIcon, } from "lucide-react"; import { type ComponentType, useEffect, useRef, useState } from "react"; import { computersAreUnavailable } from "../components/ComputersUnavailableHint"; import { MediaSettings } from "../components/MediaSettings"; import { NotificationSettingsPanel } from "../components/NotificationSettingsPanel"; import { UserProfileSettings } from "../components/UserProfileSettings"; import { ComputerSettingsPanel, GeneralSettingsPanels, UpdatesSettingsPanel, UsageSettingsPanel, } from "./AccountSettingsOverlay"; import { MemorySettingsOverlay } from "./MemorySettingsOverlay"; import { ModelSettingsOverlay } from "./ModelSettingsOverlay"; import { PluginsOverlay } from "./PluginsOverlay"; import { PrivacySettingsPanel } from "./PrivacySettingsOverlay"; import { VoiceSettingsOverlay } from "./VoiceSettingsOverlay"; export type SettingsSection = | "general" | "integrations" | "models" | "memory" | "profile" | "voice" | "media" | "notifications" | "usage" | "computer" | "updates" | "privacy"; type NavItem = { id: SettingsSection; label: string; icon: ComponentType<{ className?: string; strokeWidth?: number }>; }; export function SettingsOverlay({ email, name, usage, initialSection = "general", avatarStyle, onAvatarStyleChange, isDeploymentOwner = false, sandboxProvider, messagingEnabled = false, onOpenMessaging, memoryConfig, onMemoryConfigChange, onClose, onVoiceStatusMaybeChanged, activeBotId, onOpenMcp, }: { email?: string | null; name: string; usage?: { runs: number; inputTokens: number; outputTokens: number } | null; initialSection?: SettingsSection; avatarStyle: AvatarStyle; onAvatarStyleChange: (style: AvatarStyle) => Promise; isDeploymentOwner?: boolean; sandboxProvider?: string | null; messagingEnabled?: boolean; onOpenMessaging?: () => void; memoryConfig: SpaceMemoryConfig | null | undefined; onMemoryConfigChange: (config: SpaceMemoryConfig | null) => void; onClose: () => void; onVoiceStatusMaybeChanged?: () => void | Promise; activeBotId?: string; onOpenMcp?: () => void; }) { const { t } = useLingui(); const panelRef = useRef(null); const usageRef = useRef(null); const [section, setSection] = useState(initialSection); const [memoryBusy, setMemoryBusy] = useState(false); const [voiceBusy, setVoiceBusy] = useState(false); const showComputer = isDeploymentOwner && computersAreUnavailable(sandboxProvider); const panelBusy = memoryBusy || voiceBusy; useEffect(() => { setSection(initialSection); }, [initialSection]); useEffect(() => { if (section === "usage") { usageRef.current?.focus(); } }, [section]); const navItems: NavItem[] = [ { id: "general", label: t`General`, icon: Settings }, { id: "integrations", label: t`Integrations`, icon: LayoutGrid }, { id: "models", label: t`Models`, icon: Cpu }, { id: "memory", label: t`Memory`, icon: Brain }, { id: "profile", label: t`About you`, icon: UserRound }, { id: "voice", label: t`Voice`, icon: Volume2 }, ...(isDeploymentOwner ? [{ id: "media" as const, label: t`Media`, icon: Clapperboard }] : []), { id: "notifications", label: t`Notifications`, icon: Bell }, { id: "usage", label: t`Usage`, icon: Gauge }, ...(showComputer ? [{ id: "computer" as const, label: t`Computer`, icon: Monitor }] : []), { id: "updates", label: t`Updates`, icon: CloudDownload }, { id: "privacy", label: t`Privacy & Data`, icon: ShieldCheck }, ]; const sectionTitle = navItems.find((item) => item.id === section)?.label ?? (section === "general" ? t`General` : t`Settings`); const closeLabel = section === "models" ? t`Close model settings` : section === "memory" ? t`Close memory settings` : section === "voice" ? t`Close voice settings` : section === "integrations" ? t`Close integrations settings` : t`Close user settings`; async function refreshVoiceStatus() { await onVoiceStatusMaybeChanged?.(); } function leaveSettings(next: () => void) { if (panelBusy) return; void refreshVoiceStatus().finally(next); } function requestClose() { leaveSettings(onClose); } const widePane = section === "models" || section === "voice" || section === "integrations"; return ( { if (open) return; if (panelBusy) { details.cancel(); return; } requestClose(); }} > section === "usage" ? (usageRef.current ?? panelRef.current) : panelRef.current } className={`flex max-h-[calc(100%-2rem)] flex-col gap-0 overflow-hidden rounded-2xl p-0 sm:max-h-[calc(100%-5rem)] ${ widePane ? "h-[min(760px,calc(100%-2rem))] w-[min(1080px,calc(100%-2rem))] sm:max-w-[1080px]" : "h-[min(720px,calc(100%-2rem))] w-[min(920px,calc(100%-2rem))] sm:max-w-[920px]" }`} >
{sectionTitle} } >
{section === "general" ? ( leaveSettings(onOpenMessaging) : undefined } isDeploymentOwner={isDeploymentOwner} /> ) : null} {section === "integrations" ? ( leaveSettings(onOpenMcp) : undefined} /> ) : null} {section === "profile" ? : null} {section === "media" && isDeploymentOwner ? : null} {section === "notifications" ? : null} {section === "usage" ? ( ) : null} {section === "computer" && showComputer ? : null} {section === "updates" ? ( ) : null} {section === "models" ? ( ) : null} {section === "memory" ? ( ) : null} {section === "voice" ? ( ) : null} {section === "privacy" ? ( ) : null}
); }