页面布局调整

This commit is contained in:
fengxiangdebaozi committed 2026-05-14 16:01:25 +08:00
1 parent 139dfb76e4
commit e6ad3c414f
7 files changed
+157 -62

No files matched your search

+3 -3
View File
@@ -139,7 +139,7 @@ function SidebarProvider({
} as React.CSSProperties } as React.CSSProperties
} }
className={cn( className={cn(
"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full", "group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex h-full w-full",
className, className,
)} )}
{...props} {...props}
@@ -229,7 +229,7 @@ function Sidebar({
<div <div
data-slot="sidebar-container" data-slot="sidebar-container"
className={cn( className={cn(
"fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex", "fixed top-16 bottom-0 z-10 hidden w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
side === "left" side === "left"
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
@@ -309,7 +309,7 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
<main <main
data-slot="sidebar-inset" data-slot="sidebar-inset"
className={cn( className={cn(
"bg-[#fffdfa] relative flex w-full flex-1 flex-col", "bg-background relative flex w-full flex-1 flex-col",
"md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2", "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
className, className,
)} )}
@@ -449,7 +449,7 @@ export function InputBox({
<button <button
key={q.id} key={q.id}
type="button" type="button"
className="rounded-full border border-[#e4e4e7] bg-white px-4 py-2 text-sm text-[#18181b] shadow-none transition-colors hover:bg-[#f4f4f5] cursor-pointer" className="rounded-full border border-[#e4e4e7] bg-white px-4 py-2 text-sm text-[#18181b] shadow-none transition-colors hover:bg-[#f4f4f5] cursor-pointer dark:border-slate-600 dark:bg-slate-700/50 dark:text-slate-300 dark:hover:bg-slate-700"
onClick={() => { onClick={() => {
textInput.setInput(q.content); textInput.setInput(q.content);
setFollowupsHidden(true); setFollowupsHidden(true);
@@ -495,7 +495,7 @@ export function InputBox({
)} )}
<PromptInput <PromptInput
className={cn( className={cn(
"rounded-[1.85rem] border border-[#eadfce] bg-white shadow-[0_24px_70px_-34px_rgba(120,83,42,0.32)] ring-1 ring-[#f6efe5] transition-all duration-300 ease-out *:data-[slot='input-group']:rounded-[1.85rem]", "rounded-[1.85rem] border border-[#eadfce] bg-white shadow-[0_24px_70px_-34px_rgba(120,83,42,0.32)] ring-1 ring-[#f6efe5] transition-all duration-300 ease-out *:data-[slot='input-group']:rounded-[1.85rem] dark:bg-slate-800 dark:border-slate-700 dark:ring-slate-700 dark:shadow-none",
className, className,
)} )}
disabled={disabled} disabled={disabled}
@@ -510,7 +510,7 @@ export function InputBox({
<PromptInputBody className="absolute top-0 right-0 left-0 z-3"> <PromptInputBody className="absolute top-0 right-0 left-0 z-3">
<PromptInputTextarea <PromptInputTextarea
className={cn( className={cn(
"size-full px-2 py-1 text-[15px] leading-7 placeholder:text-[#b7ab9a]", "size-full px-2 py-1 text-[15px] leading-7 placeholder:text-[#b7ab9a] dark:placeholder:text-slate-500",
)} )}
disabled={disabled} disabled={disabled}
placeholder={t.inputBox.placeholder} placeholder={t.inputBox.placeholder}
@@ -518,7 +518,7 @@ export function InputBox({
defaultValue={initialValue} defaultValue={initialValue}
/> />
</PromptInputBody> </PromptInputBody>
<PromptInputFooter className="flex items-center border-t border-[#f4ede4] px-2 pt-2.5 pb-1.5"> <PromptInputFooter className="flex items-center border-t border-[#f4ede4] dark:border-slate-700 px-2 pt-2.5 pb-1.5">
<PromptInputTools className="gap-1.5"> <PromptInputTools className="gap-1.5">
{/* TODO: Add more connectors here {/* TODO: Add more connectors here
<PromptInputActionMenu> <PromptInputActionMenu>
@@ -529,7 +529,7 @@ export function InputBox({
/> />
</PromptInputActionMenuContent> </PromptInputActionMenuContent>
</PromptInputActionMenu> */} </PromptInputActionMenu> */}
<AddAttachmentsButton className="rounded-full border border-[#eee3d5] bg-[#fffaf5] px-2.5! shadow-none hover:bg-white" /> <AddAttachmentsButton className="rounded-full border border-[#eee3d5] bg-[#fffaf5] px-2.5! shadow-none hover:bg-white dark:border-slate-600 dark:bg-slate-700/50 dark:text-slate-300 dark:hover:bg-slate-700" />
<PromptInputActionMenu> <PromptInputActionMenu>
<ModeHoverGuide <ModeHoverGuide
mode={ mode={
@@ -541,7 +541,7 @@ export function InputBox({
: "flash" : "flash"
} }
> >
<PromptInputActionMenuTrigger className="gap-1.5! rounded-full border border-[#eee3d5] bg-[#fffaf5] px-3! text-[#7e6a57] shadow-none hover:bg-white"> <PromptInputActionMenuTrigger className="gap-1.5! rounded-full border border-[#eee3d5] bg-[#fffaf5] px-3! text-[#7e6a57] shadow-none hover:bg-white dark:border-slate-600 dark:bg-slate-700/50 dark:text-slate-300 dark:hover:bg-slate-700">
<div> <div>
{context.mode === "flash" && <ZapIcon className="size-3" />} {context.mode === "flash" && <ZapIcon className="size-3" />}
{context.mode === "thinking" && ( {context.mode === "thinking" && (
@@ -703,7 +703,7 @@ export function InputBox({
</PromptInputActionMenu> </PromptInputActionMenu>
{supportReasoningEffort && context.mode !== "flash" && ( {supportReasoningEffort && context.mode !== "flash" && (
<PromptInputActionMenu> <PromptInputActionMenu>
<PromptInputActionMenuTrigger className="gap-1.5! rounded-full border border-[#eee3d5] bg-[#fffaf5] px-3! text-[#7e6a57] shadow-none hover:bg-white"> <PromptInputActionMenuTrigger className="gap-1.5! rounded-full border border-[#eee3d5] bg-[#fffaf5] px-3! text-[#7e6a57] shadow-none hover:bg-white dark:border-slate-600 dark:bg-slate-700/50 dark:text-slate-300 dark:hover:bg-slate-700">
<div className="text-xs font-normal"> <div className="text-xs font-normal">
{t.inputBox.reasoningEffort}: {t.inputBox.reasoningEffort}:
{context.reasoning_effort === "minimal" && {context.reasoning_effort === "minimal" &&
@@ -824,7 +824,7 @@ export function InputBox({
onOpenChange={setModelDialogOpen} onOpenChange={setModelDialogOpen}
> >
<ModelSelectorTrigger asChild> <ModelSelectorTrigger asChild>
<PromptInputButton className="rounded-full border border-[#eee3d5] bg-[#fffaf5] px-3 shadow-none hover:bg-white"> <PromptInputButton className="rounded-full border border-[#eee3d5] bg-[#fffaf5] px-3 shadow-none hover:bg-white dark:border-slate-600 dark:bg-slate-700/50 dark:text-slate-300 dark:hover:bg-slate-700">
<div className="flex min-w-0 flex-col items-start text-left"> <div className="flex min-w-0 flex-col items-start text-left">
<ModelSelectorName className="text-xs font-normal"> <ModelSelectorName className="text-xs font-normal">
{selectedModel?.display_name} {selectedModel?.display_name}
@@ -858,7 +858,7 @@ export function InputBox({
</ModelSelectorContent> </ModelSelectorContent>
</ModelSelector> </ModelSelector>
<PromptInputSubmit <PromptInputSubmit
className="rounded-full border-[#e7d9c7] bg-white shadow-[0_10px_24px_-18px_rgba(120,83,42,0.34)]" className="rounded-full border-[#e7d9c7] bg-white shadow-[0_10px_24px_-18px_rgba(120,83,42,0.34)] dark:bg-blue-500 dark:border-blue-500 dark:text-white dark:hover:bg-blue-400 dark:shadow-none"
disabled={disabled} disabled={disabled}
variant="outline" variant="outline"
status={status} status={status}
@@ -866,7 +866,7 @@ export function InputBox({
</PromptInputTools> </PromptInputTools>
</PromptInputFooter> </PromptInputFooter>
{!isNewThread && ( {!isNewThread && (
<div className="absolute right-0 -bottom-[17px] left-0 z-0 h-4 bg-[#fffdfa]"></div> <div className="absolute right-0 -bottom-[17px] left-0 z-0 h-4 bg-[#fffdfa] dark:bg-background"></div>
)} )}
</PromptInput> </PromptInput>
+4 -4
View File
@@ -39,16 +39,16 @@ const themes: Record<ThemeType, ThemeConfig> = {
textSecondary: 'slate-400', textSecondary: 'slate-400',
primary: 'blue-500', primary: 'blue-500',
primaryHover: 'blue-400', primaryHover: 'blue-400',
border: 'blue-800', border: 'slate-700',
accent: 'cyan-400', accent: 'blue-600',
success: 'emerald-500', success: 'emerald-500',
warning: 'amber-500', warning: 'amber-500',
error: 'rose-500', error: 'rose-500',
card: 'slate-800', card: 'slate-800',
cardHover: 'slate-700', cardHover: 'slate-700',
input: 'slate-800', input: 'slate-800',
inputBorder: 'blue-800', inputBorder: 'slate-600',
overlay: 'slate-900/50', overlay: 'slate-900/80',
}, },
}, },
light: { light: {
+21 -17
View File
@@ -4,6 +4,7 @@ import { Navigate, Route, Routes } from "react-router-dom";
import { PageSidebar } from "@/components/page-sidebar"; import { PageSidebar } from "@/components/page-sidebar";
import WorkspaceRoutes from "./WorkspaceRoutes"; import WorkspaceRoutes from "./WorkspaceRoutes";
import Header from "@/strategy-components/Header";
// 设为 true 时隐藏侧边栏(开发中,暂未完成) // 设为 true 时隐藏侧边栏(开发中,暂未完成)
const SIDEBAR_ENABLED = false; const SIDEBAR_ENABLED = false;
@@ -12,31 +13,34 @@ function PageLayout() {
const [collapsed, setCollapsed] = useState(false); const [collapsed, setCollapsed] = useState(false);
return ( return (
<div className="min-h-screen bg-muted/20"> <div className="h-screen flex bg-muted/20">
{SIDEBAR_ENABLED && ( {SIDEBAR_ENABLED && (
<PageSidebar <PageSidebar
collapsed={collapsed} collapsed={collapsed}
onToggleCollapsed={() => setCollapsed((prev) => !prev)} onToggleCollapsed={() => setCollapsed((prev) => !prev)}
/> />
)} )}
<main <div
className={`min-h-screen transition-[padding] duration-300 ${ className={`flex flex-col flex-1 overflow-hidden transition-[padding] duration-300 ${
!SIDEBAR_ENABLED ? "" : collapsed ? "pl-16" : "pl-16 md:pl-64" !SIDEBAR_ENABLED ? "" : collapsed ? "pl-16" : "pl-64"
}`} }`}
> >
<Routes> <Header />
<Route index element={<Navigate to="workspace" replace />} /> <main className="flex-1 overflow-hidden">
<Route <Routes>
path="workspace/*" <Route index element={<Navigate to="workspace" replace />} />
element={ <Route
<WorkspaceRoutes path="workspace/*"
pageSidebarCollapsed={collapsed} element={
pageSidebarHidden={!SIDEBAR_ENABLED} <WorkspaceRoutes
/> pageSidebarCollapsed={collapsed}
} pageSidebarHidden={!SIDEBAR_ENABLED}
/> />
</Routes> }
</main> />
</Routes>
</main>
</div>
</div> </div>
); );
} }
+1 -1
View File
@@ -37,7 +37,7 @@ function WorkspaceLayout({
return ( return (
<SidebarProvider <SidebarProvider
className="h-screen w-full overflow-x-hidden [&_[data-slot=sidebar-container]]:left-[var(--page-sidebar-width)]" className="h-full w-full overflow-x-hidden [&_[data-slot=sidebar-container]]:left-[var(--page-sidebar-width)]"
style={ style={
{ {
"--page-sidebar-width": pageSidebarWidth, "--page-sidebar-width": pageSidebarWidth,
@@ -0,0 +1,83 @@
import React from 'react';
import { Bell, Search, User, ChevronDown, Moon, Sun, Palette } from 'lucide-react';
import { useTheme } from '../contexts/ThemeContext';
const Header: React.FC = () => {
const { theme, toggleTheme } = useTheme();
const isDark = theme === 'dark';
return (
<header className={`h-16 flex items-center justify-between px-6 theme-transition ${
isDark
? 'bg-slate-900/50 backdrop-blur-xl border-b border-blue-800/30'
: 'bg-red-600 border-b border-red-700'
}`}>
{/* 左侧 - 搜索框 */}
<div className="flex items-center gap-4">
<div className="relative">
<Search className={`absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 ${isDark ? 'text-slate-500' : 'text-red-200'}`} />
<input
type="text"
placeholder="搜索场景、智能体、任务..."
className={`w-80 pl-10 pr-4 py-2 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-white/20 transition-all duration-200 theme-transition ${
isDark
? 'bg-slate-800/50 border border-blue-800/30 text-white placeholder-slate-500'
: 'bg-red-700 border border-red-800 text-white placeholder-red-200'
}`}
/>
</div>
</div>
{/* 右侧 - 用户信息和通知 */}
<div className="flex items-center gap-4">
{/* 主题切换按钮 */}
<button
onClick={toggleTheme}
className={`p-2 rounded-lg transition-all duration-200 ${
isDark
? 'hover:bg-slate-800/50 text-slate-400 hover:text-white'
: 'hover:bg-red-500 text-white'
}`}
title={isDark ? '切换到红白主题' : '切换到深色主题'}
>
{isDark ? (
<Palette className="w-5 h-5" />
) : (
<Moon className="w-5 h-5" />
)}
</button>
{/* 通知 */}
<button className={`relative p-2 rounded-lg transition-colors ${
isDark ? 'hover:bg-slate-800/50' : 'hover:bg-red-500'
}`}>
<Bell className={`w-5 h-5 ${isDark ? 'text-slate-400' : 'text-white'}`} />
<span className={`absolute top-1 right-1 w-2 h-2 rounded-full ${isDark ? 'bg-cyan-500' : 'bg-white'}`} />
</button>
{/* 分隔线 */}
<div className={`w-px h-8 ${isDark ? 'bg-blue-800/30' : 'bg-red-500'}`} />
{/* 用户信息 */}
<button className={`flex items-center gap-3 px-3 py-1.5 rounded-lg transition-colors ${
isDark ? 'hover:bg-slate-800/50' : 'hover:bg-red-500'
}`}>
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
isDark
? 'bg-gradient-to-br from-blue-500 to-cyan-400'
: 'bg-white'
}`}>
<User className={`w-4 h-4 ${isDark ? 'text-white' : 'text-red-600'}`} />
</div>
<div className="text-left">
<p className={`text-sm font-medium ${isDark ? 'text-white' : 'text-white'}`}>张策略</p>
<p className={`text-xs ${isDark ? 'text-blue-400/60' : 'text-red-200'}`}>策略分析师</p>
</div>
<ChevronDown className={`w-4 h-4 ${isDark ? 'text-slate-500' : 'text-white'}`} />
</button>
</div>
</header>
);
};
export default Header;
+35 -27
View File
@@ -243,7 +243,7 @@
--workspace-brand: #d92121; --workspace-brand: #d92121;
--workspace-brand-foreground: #ffffff; --workspace-brand-foreground: #ffffff;
--radius: 0.75rem; --radius: 0.75rem;
--background: oklch(0.985 0.004 95); --background: oklch(1 0 0);
--foreground: oklch(0.22 0.02 25); --foreground: oklch(0.22 0.02 25);
--card: oklch(1 0.002 95); --card: oklch(1 0.002 95);
--card-foreground: oklch(0.22 0.02 25); --card-foreground: oklch(0.22 0.02 25);
@@ -277,39 +277,39 @@
} }
.dark { .dark {
--workspace-brand: #e11d1d; --workspace-brand: #3b82f6;
--workspace-brand-foreground: #ffffff; --workspace-brand-foreground: #ffffff;
--background: oklch(0.22 0.015 25); --background: oklch(0.208 0.042 264);
--foreground: oklch(0.96 0.01 95); --foreground: oklch(0.96 0.005 240);
--card: oklch(0.26 0.018 25); --card: oklch(0.265 0.04 260);
--card-foreground: oklch(0.96 0.01 95); --card-foreground: oklch(0.96 0.005 240);
--popover: oklch(0.24 0.018 25); --popover: oklch(0.245 0.04 262);
--popover-foreground: oklch(0.96 0.01 95); --popover-foreground: oklch(0.96 0.005 240);
--primary: oklch(0.62 0.17 27); --primary: oklch(0.623 0.214 259);
--primary-foreground: oklch(0.12 0.02 25); --primary-foreground: oklch(0.99 0 0);
--secondary: oklch(0.32 0.02 25); --secondary: oklch(0.32 0.038 260);
--secondary-foreground: oklch(0.96 0.01 95); --secondary-foreground: oklch(0.96 0.005 240);
--muted: oklch(0.3 0.02 25); --muted: oklch(0.30 0.035 260);
--muted-foreground: oklch(0.72 0.02 95); --muted-foreground: oklch(0.68 0.02 240);
--accent: oklch(0.34 0.06 25); --accent: oklch(0.35 0.06 260);
--accent-foreground: oklch(0.96 0.01 95); --accent-foreground: oklch(0.96 0.005 240);
--destructive: oklch(0.704 0.191 22.216); --destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 12%); --border: oklch(1 0 0 / 12%);
--input: oklch(1 0 0 / 14%); --input: oklch(1 0 0 / 14%);
--ring: oklch(0.62 0.17 27 / 0.45); --ring: oklch(0.623 0.214 259 / 0.45);
--chart-1: oklch(0.488 0.243 264.376); --chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48); --chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08); --chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9); --chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439); --chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.2 0.02 25); --sidebar: oklch(0.185 0.04 265);
--sidebar-foreground: oklch(0.94 0.01 95); --sidebar-foreground: oklch(0.94 0.005 240);
--sidebar-primary: oklch(0.62 0.17 27); --sidebar-primary: oklch(0.623 0.214 259);
--sidebar-primary-foreground: oklch(0.99 0 0); --sidebar-primary-foreground: oklch(0.99 0 0);
--sidebar-accent: oklch(0.3 0.05 25); --sidebar-accent: oklch(0.30 0.055 260);
--sidebar-accent-foreground: oklch(0.96 0.01 95); --sidebar-accent-foreground: oklch(0.96 0.005 240);
--sidebar-border: oklch(1 0 0 / 10%); --sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.62 0.17 27 / 0.45); --sidebar-ring: oklch(0.623 0.214 259 / 0.45);
font-weight: 300; font-weight: 300;
} }
@@ -430,19 +430,27 @@
} }
.workspace-shell { .workspace-shell {
background: background: #ffffff;
radial-gradient(circle at top right, rgb(255 214 214 / 0.72), transparent 18%), }
linear-gradient(180deg, #fbf7f4 0%, #f6efea 100%);
.dark .workspace-shell {
background: var(--background);
} }
.workspace-panel { .workspace-panel {
border: 1px solid #f0d7d0; border: 1px solid #f0d7d0;
background: linear-gradient(180deg, rgb(255 253 251 / 0.98), rgb(255 247 244 / 0.98)); background: #ffffff;
box-shadow: box-shadow:
0 32px 72px -46px rgb(98 63 42 / 0.35), 0 32px 72px -46px rgb(98 63 42 / 0.35),
0 12px 28px -22px rgb(170 91 75 / 0.24); 0 12px 28px -22px rgb(170 91 75 / 0.24);
} }
.dark .workspace-panel {
border: 1px solid oklch(1 0 0 / 10%);
background: var(--card);
box-shadow: none;
}
.workspace-user-bubble { .workspace-user-bubble {
max-width: min(82%, 42rem); max-width: min(82%, 42rem);
border: 1px solid rgb(255 255 255 / 0.14); border: 1px solid rgb(255 255 255 / 0.14);