代码修改
This commit is contained in:
1 parent
b93b82fcaf
commit
055523bf33
28 files changed
+483
-92
No files matched your search
@@ -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 把它从
|
||||
|
||||
Reference in new issue
Block a user