Files
deerflow-code/frontend-web/src/components/workspace/settings/settings-dialog.tsx
T
董宏凯 56449ed129 feat(ui): theme & layout improvements
- Dark-blue theme background assets and styling integration
- Appearance settings with layoutMode (full/compact) preference
- Settings dialog and auth context updates
- AI writing form and panel refinements
- Open Canvas context updates
- Roundtable planning styling and dashboard enhancements
- Strategy components and header updates
- Light app management and routing improvements
- Global style enhancements for dark theme
- Various page layout and workspace routing optimizations
2026-06-14 19:30:54 +08:00

137 lines
5.0 KiB
TypeScript

"use client";
import { BellIcon, PaletteIcon, SparklesIcon, UserIcon, WrenchIcon } from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { ScrollArea } from "@/components/ui/scroll-area";
import { AboutSettingsPage } from "@/components/workspace/settings/about-settings-page";
import { AccountSettingsPage } from "@/components/workspace/settings/account-settings-page";
import { AppearanceSettingsPage } from "@/components/workspace/settings/appearance-settings-page";
import { MemorySettingsPage } from "@/components/workspace/settings/memory-settings-page";
import { ToolSettingsPage } from "@/components/workspace/settings/tool-settings-page";
import { useI18n } from "@/core/i18n/hooks";
import { cn } from "@/lib/utils";
type SettingsSection =
| "account"
| "appearance"
| "memory"
| "tools"
| "about";
type SettingsDialogProps = React.ComponentProps<typeof Dialog> & {
defaultSection?: SettingsSection;
};
export function SettingsDialog(props: SettingsDialogProps) {
const { defaultSection = "appearance", ...dialogProps } = props;
const { t } = useI18n();
const [activeSection, setActiveSection] =
useState<SettingsSection>(defaultSection);
useEffect(() => {
// When opening the dialog, ensure the active section follows the caller's intent.
// This allows triggers like "About" to open the dialog directly on that page.
if (dialogProps.open) {
setActiveSection(defaultSection);
}
}, [defaultSection, dialogProps.open]);
const sections = useMemo(
() => [
{
id: "appearance",
label: t.settings.sections.appearance,
icon: PaletteIcon,
},
// {
// id: "notification",
// label: t.settings.sections.notification,
// icon: BellIcon,
// },
// { id: "tools", label: t.settings.sections.tools, icon: WrenchIcon },
// { id: "skills", label: t.settings.sections.skills, icon: SparklesIcon },
// 记忆已移至侧边栏入口 (/page/workspace/memory)
// {
// id: "memory",
// label: t.settings.sections.memory,
// icon: BrainIcon,
// },
{ id: "tools", label: t.settings.sections.tools, icon: WrenchIcon },
{
id: "account",
label: "个人信息",
icon: UserIcon,
},
// { id: "about", label: t.settings.sections.about, icon: InfoIcon },
],
[
t.settings.sections.appearance,
t.settings.sections.memory,
t.settings.sections.tools,
t.settings.sections.about,
],
);
return (
<Dialog
{...dialogProps}
onOpenChange={(open) => props.onOpenChange?.(open)}
>
<DialogContent
className="flex h-[75vh] max-h-[calc(100vh-2rem)] flex-col sm:max-w-5xl md:max-w-6xl"
aria-describedby={undefined}
>
<DialogHeader className="gap-1">
<DialogTitle>{t.settings.title}</DialogTitle>
<p className="text-muted-foreground text-sm">
{t.settings.description}
</p>
</DialogHeader>
<div className="grid min-h-0 flex-1 gap-4 md:grid-cols-[220px_minmax(0,1fr)]">
<nav className="bg-sidebar min-h-0 overflow-y-auto rounded-lg border p-2">
<ul className="space-y-1 pr-1">
{sections.map(({ id, label, icon: Icon }) => {
const active = activeSection === id;
return (
<li key={id}>
<button
type="button"
onClick={() => setActiveSection(id as SettingsSection)}
className={cn(
"flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors",
active
? "bg-primary text-primary-foreground shadow-sm"
: "text-muted-foreground hover:bg-muted hover:text-foreground",
)}
>
<Icon className="size-4" />
<span>{label}</span>
</button>
</li>
);
})}
</ul>
</nav>
<ScrollArea className="h-full min-h-0 min-w-0 rounded-lg border">
<div className="min-w-0 space-y-8 p-6">
{activeSection === "account" && <AccountSettingsPage />}
{activeSection === "appearance" && (
<AppearanceSettingsPage onClose={() => props.onOpenChange?.(false)} />
)}
{activeSection === "memory" && <MemorySettingsPage />}
{activeSection === "tools" && <ToolSettingsPage />}
{activeSection === "about" && <AboutSettingsPage />}
</div>
</ScrollArea>
</div>
</DialogContent>
</Dialog>
);
}