代码修改

This commit is contained in:
董宏凯 committed 2026-07-22 15:43:06 +08:00
1 parent b93b82fcaf
commit 055523bf33
28 files changed
+483 -92

No files matched your search

+17 -13
View File
@@ -57,18 +57,21 @@ function useSidebarSafe() {
return React.useContext(SidebarContext);
}
function SidebarProvider({
defaultOpen = true,
open: openProp,
onOpenChange: setOpenProp,
className,
function SidebarProvider({
defaultOpen = true,
open: openProp,
onOpenChange: setOpenProp,
disableToggle = false,
className,
style,
children,
...props
}: React.ComponentProps<"div"> & {
defaultOpen?: boolean;
open?: boolean;
onOpenChange?: (open: boolean) => void;
}: React.ComponentProps<"div"> & {
defaultOpen?: boolean;
open?: boolean;
onOpenChange?: (open: boolean) => void;
/** Keep a controlled deployment-level rail from being reopened by hotkeys. */
disableToggle?: boolean;
}) {
const isMobile = useIsMobile();
const [openMobile, setOpenMobile] = React.useState(false);
@@ -92,10 +95,11 @@ function SidebarProvider({
[setOpenProp, open],
);
// Helper to toggle the sidebar.
const toggleSidebar = React.useCallback(() => {
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
}, [isMobile, setOpen, setOpenMobile]);
// Helper to toggle the sidebar.
const toggleSidebar = React.useCallback(() => {
if (disableToggle) return;
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
}, [disableToggle, isMobile, setOpen, setOpenMobile]);
// Adds a keyboard shortcut to toggle the sidebar.
React.useEffect(() => {
@@ -15,6 +15,7 @@ import {
} from "@/components/ui/dropdown-menu";
import { useTheme, type ThemeType } from "@/contexts/ThemeContext";
import { useAppearanceSettings, useLayoutMode } from "@/core/system-settings/hooks";
import { env } from "@/env";
import { cn } from "@/lib/utils";
import { buildLightAppUrl } from "@/strategy-components/lib/light-app";
@@ -29,6 +30,7 @@ export type AppearanceControlTone = "banner-dark" | "banner-light" | "content";
// 换肤色板:红色=红白主题(light),蓝色=浅蓝主题(lightblue),深蓝色=深色主题(dark)
export const THEME_SWATCHES: { id: ThemeType; label: string; color: string }[] = [
{ id: "light", label: "红色", color: "#cf1322" },
{ id: "lightgreen", label: "绿色", color: "#07c05f" },
{ id: "lightblue", label: "蓝色", color: "#0062d9" },
{ id: "dark", label: "深蓝色", color: "#002466" },
];
@@ -95,6 +97,8 @@ export function ThemeSwitcher({ tone }: { tone: AppearanceControlTone }) {
useEffect(() => () => cancelClose(), []);
if (env.FORCE_COLLAPSED_SIDEBAR) return null;
return (
<div
className="relative"
@@ -185,6 +189,8 @@ export function LayoutModeSwitcher({
const current = mode ?? fallback;
const TriggerIcon = current === "full" ? PanelLeftIcon : LayoutIcon;
if (env.FORCE_COLLAPSED_SIDEBAR) return null;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -7,6 +7,7 @@ import {
ExternalLink,
LayoutGridIcon,
LayoutIcon,
LeafIcon,
ListIcon,
MoonIcon,
PanelLeftIcon,
@@ -70,7 +71,7 @@ const languageOptions: { value: Locale; label: string }[] = [
{ value: "zh-CN", label: zhCN.locale.localName },
];
const DEFAULT_SHORTCUT_THEME_MAP = "red=red;blue=blue;dark-blue=dark-blue";
const DEFAULT_SHORTCUT_THEME_MAP = "red=red;green=green;blue=blue;dark-blue=dark-blue";
function makeShortcutId() {
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
@@ -211,14 +212,20 @@ export function AppearanceSettingsPage({ onClose }: { onClose?: () => void }) {
}
};
const handleSetGlobalDefault = async (
value: "light" | "dark" | "lightblue",
const handleSetGlobalDefault = async (
value: "light" | "dark" | "lightblue" | "lightgreen",
) => {
if (value === globalDefault) return;
try {
await updateAppearance.mutateAsync({ default_theme: value });
const label =
value === "light" ? "浅色" : value === "dark" ? "深色" : "浅蓝";
const label =
value === "light"
? "浅色"
: value === "dark"
? "深色"
: value === "lightgreen"
? "绿色"
: "浅蓝";
toast.success(`已将系统默认主题设置为${label}`);
} catch (e) {
toast.error(e instanceof Error ? e.message : "保存失败");
@@ -368,20 +375,28 @@ export function AppearanceSettingsPage({ onClose }: { onClose?: () => void }) {
description: t.settings.appearance.darkDescription,
icon: MoonIcon,
},
{
id: "lightblue",
label: t.settings.appearance.lightblue,
description: t.settings.appearance.lightblueDescription,
icon: DropletIcon,
},
{
id: "lightblue",
label: t.settings.appearance.lightblue,
description: t.settings.appearance.lightblueDescription,
icon: DropletIcon,
},
{
id: "lightgreen",
label: t.settings.appearance.lightgreen,
description: t.settings.appearance.lightgreenDescription,
icon: LeafIcon,
},
],
[
t.settings.appearance.dark,
t.settings.appearance.darkDescription,
t.settings.appearance.light,
t.settings.appearance.lightDescription,
t.settings.appearance.lightblue,
t.settings.appearance.lightblueDescription,
t.settings.appearance.lightblue,
t.settings.appearance.lightblueDescription,
t.settings.appearance.lightgreen,
t.settings.appearance.lightgreenDescription,
],
);
@@ -399,7 +414,7 @@ export function AppearanceSettingsPage({ onClose }: { onClose?: () => void }) {
label={option.label}
description={option.description}
active={currentTheme === option.id}
mode={option.id as "light" | "dark" | "lightblue"}
mode={option.id as "light" | "dark" | "lightblue" | "lightgreen"}
onSelect={(value) => setTheme(value)}
/>
))}
@@ -541,23 +556,35 @@ export function AppearanceSettingsPage({ onClose }: { onClose?: () => void }) {
<MoonIcon className="mr-1.5 size-4" />
深色
</Button>
<Button
type="button"
variant={globalDefault === "lightblue" ? "default" : "outline"}
<Button
type="button"
variant={globalDefault === "lightblue" ? "default" : "outline"}
size="sm"
onClick={() => void handleSetGlobalDefault("lightblue")}
disabled={updateAppearance.isPending}
>
<DropletIcon className="mr-1.5 size-4" />
浅蓝
</Button>
浅蓝
</Button>
<Button
type="button"
variant={globalDefault === "lightgreen" ? "default" : "outline"}
size="sm"
onClick={() => void handleSetGlobalDefault("lightgreen")}
disabled={updateAppearance.isPending}
>
<LeafIcon className="mr-1.5 size-4" />
绿色
</Button>
<p className="text-muted-foreground ml-2 text-xs">
当前系统默认:
{globalDefault === "light"
? "浅色"
: globalDefault === "dark"
? "深色"
: "浅蓝"}
: globalDefault === "dark"
? "深色"
: globalDefault === "lightgreen"
? "绿色"
: "浅蓝"}
</p>
</div>
</SettingsSection>
@@ -1074,8 +1101,8 @@ function ThemePreviewCard({
label: string;
description: string;
active: boolean;
mode: "light" | "dark" | "lightblue";
onSelect: (mode: "light" | "dark" | "lightblue") => void;
mode: "light" | "dark" | "lightblue" | "lightgreen";
onSelect: (mode: "light" | "dark" | "lightblue" | "lightgreen") => void;
}) {
const previewMode = mode;
return (
@@ -1104,10 +1131,12 @@ function ThemePreviewCard({
className={cn(
"relative overflow-hidden rounded-md border text-xs transition-colors",
previewMode === "dark"
? "border-neutral-800 bg-neutral-900 text-neutral-200"
: previewMode === "lightblue"
? "border-blue-200 bg-white text-slate-900"
: "border-slate-200 bg-white text-slate-900",
? "border-neutral-800 bg-neutral-900 text-neutral-200"
: previewMode === "lightblue"
? "border-blue-200 bg-white text-slate-900"
: previewMode === "lightgreen"
? "border-emerald-200 bg-white text-emerald-950"
: "border-slate-200 bg-white text-slate-900",
)}
>
<div className="border-border/50 flex items-center gap-2 border-b px-3 py-2">
@@ -1115,10 +1144,12 @@ function ThemePreviewCard({
className={cn(
"h-2 w-2 rounded-full",
previewMode === "dark"
? "bg-emerald-400"
: previewMode === "lightblue"
? "bg-blue-500"
: "bg-emerald-500",
? "bg-emerald-400"
: previewMode === "lightblue"
? "bg-blue-500"
: previewMode === "lightgreen"
? "bg-[#07c05f]"
: "bg-[#cf1322]",
)}
/>
<div className="h-2 w-10 rounded-full bg-current/20" />
@@ -20,11 +20,15 @@ import { WorkspaceHeader } from "./workspace-header";
import { WorkspaceNavChatList } from "./workspace-nav-chat-list";
import { WorkspaceNavMenu } from "./workspace-nav-menu";
export function WorkspaceSidebar({
compactChrome = false,
className,
...props
}: React.ComponentProps<typeof Sidebar> & { compactChrome?: boolean }) {
export function WorkspaceSidebar({
compactChrome = false,
forceCollapsed = false,
className,
...props
}: React.ComponentProps<typeof Sidebar> & {
compactChrome?: boolean;
forceCollapsed?: boolean;
}) {
const { open: isSidebarOpen } = useSidebar();
const { theme } = useTheme();
const pathname = usePathname();
@@ -32,8 +36,26 @@ export function WorkspaceSidebar({
// 通用问答(全量模式)左栏:新建回答 → 常用智能体 → 聊天记录(需求 0616 #1)。
// 仅在通用问答(/page/workspace/chats*)下启用三块式布局;点击常用智能体后是
// 「原地嵌入」(停留在 /chats/new?agent=...,仅右侧问答区换成智能体对话),左侧栏不变。
const isGeneralQa = pathname.startsWith("/page/workspace/chats");
const isGeneralQa = pathname.startsWith("/page/workspace/chats");
if (forceCollapsed) {
return (
<Sidebar
variant="sidebar"
collapsible="icon"
className={cn(
"bg-sidebar [&_[data-slot=sidebar-inner]]:bg-sidebar",
className,
)}
{...props}
>
<SidebarContent>
<WorkspaceNavChatList />
</SidebarContent>
</Sidebar>
);
}
// 紧凑模式:用「外层定宽容器 + transition-[width]」做展开/收起过渡动画。
// collapsible="none" 的 Sidebar 是普通 in-flow 定宽块(非 absolute,故不受本布局对
// sidebar-container 的 absolute/left-0 覆盖影响),外层 overflow-hidden 把它从