feat(ui): 浅蓝主题、外观设置与圆桌看板主题适配,优化工作区布局

新增 lightblue 主题与外观设置联动,圆桌 Step3 看板/report 统一配色;工作区侧边栏、输入框与轻应用 iframe 主题参数同步更新。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
董宏凯andCursor committed 2026-06-14 12:43:16 +08:00
1 parent 30d4f9667b
commit c702119e66
39 files changed
+2562 -1441

No files matched your search

+61 -10
View File
@@ -1,6 +1,6 @@
"use client";
import { useRouter, useSearchParams } from "next/navigation";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
@@ -28,6 +28,12 @@ import { ThreadTitle } from "@/components/workspace/thread-title";
import { TodoList } from "@/components/workspace/todo-list";
import { TokenUsageIndicator } from "@/components/workspace/token-usage-indicator";
import { Welcome } from "@/components/workspace/welcome";
import { CompactWelcome } from "@/components/workspace/compact-welcome";
import { readSidebarEnabled } from "@/core/page-layout/sidebar-enabled";
import { isChatsIframeWorkspacePath } from "@/core/page-layout/chats-iframe-route";
import { useSidebar } from "@/components/ui/sidebar";
import { Button } from "@/components/ui/button";
import { PanelLeftOpen } from "lucide-react";
import { useAgentSelector } from "@/core/agents";
import { useI18n } from "@/core/i18n/hooks";
import { useModels } from "@/core/models/hooks";
@@ -85,6 +91,9 @@ export default function ChatPage() {
const routeBase = useRouteBase();
const router = useRouter();
const searchParams = useSearchParams();
const pathname = usePathname();
const { open: historySidebarOpen, toggleSidebar: toggleHistorySidebar } =
useSidebar();
const { t } = useI18n();
const [showFollowups, setShowFollowups] = useState(false);
const [hasStartedConversation, setHasStartedConversation] = useState(false);
@@ -402,6 +411,11 @@ export default function ChatPage() {
MESSAGE_LIST_FOLLOWUPS_EXTRA_PADDING_BOTTOM
: undefined;
const isLandingState = isNewThread && !hasStartedConversation;
// 紧凑模式(正常 App,有外层页面侧栏)落地态:用 CM 参考图样式重画;
// 判定与 PageRoutes 的 compactWorkspaceSidebar 一致,非紧凑(嵌入/standalone)保持原样。
const compactChrome =
readSidebarEnabled() && !isChatsIframeWorkspacePath(pathname);
const compactLanding = isLandingState && compactChrome;
return (
<ThreadContext.Provider value={{ thread, isMock }}>
@@ -419,6 +433,20 @@ export default function ChatPage() {
<ThreadTitle threadId={threadId} thread={thread} />
</div>
<div className="flex items-center gap-2">
{/* 仅在收起态显示「展开历史」钮;展开后由侧栏内(新对话右侧)的收起钮负责收起。 */}
{compactChrome && !historySidebarOpen && (
<Button
type="button"
variant="ghost"
size="icon-sm"
onClick={toggleHistorySidebar}
title="打开历史记录"
aria-label="打开历史记录"
className="text-muted-foreground hover:text-foreground"
>
<PanelLeftOpen className="size-4" />
</Button>
)}
<TokenUsageIndicator
enabled={tokenUsageEnabled}
messages={thread.messages}
@@ -453,11 +481,25 @@ export default function ChatPage() {
/>
)}
</div>
<div className="absolute right-0 bottom-0 left-0 z-30 flex justify-center px-4">
<div
className={cn(
// 外层居中容器不吃指针事件:空白区域(尤其落地态铺满 inset-0 时盖住的
// 右上角「展开历史」按钮)让点击穿透,仅真正的内容块(下方 pointer-events-auto)
// 接收交互。否则该透明层会吞掉顶栏按钮的 hover/光标/点击。
"pointer-events-none absolute z-30 flex justify-center px-4",
// 紧凑落地态:整块垂直+水平居中于对话区
compactLanding
? "inset-0 items-center"
: "right-0 bottom-0 left-0",
)}
>
<div
className={cn(
"relative w-full",
isLandingState && "-translate-y-[calc(50vh-96px)]",
"pointer-events-auto relative w-full",
// 非紧凑落地态沿用上移居中;紧凑态由外层 items-center 居中,无需位移
isLandingState &&
!compactLanding &&
"-translate-y-[calc(50vh-96px)]",
isLandingState
? "max-w-(--container-width-md)"
: "max-w-(--container-width-lg)",
@@ -474,17 +516,21 @@ export default function ChatPage() {
/>
</div>
</div>
{isLandingState && mounted && (
<Welcome
mode={settings.context.mode}
className="mb-6 -translate-y-4"
/>
)}
{isLandingState && mounted &&
(compactLanding ? (
<CompactWelcome className="mb-6 -translate-y-4" />
) : (
<Welcome
mode={settings.context.mode}
className="mb-6 -translate-y-4"
/>
))}
{mounted ? (
<div className="w-full -translate-y-4">
<InputBox
className="w-full"
isNewThread={isLandingState}
compact={compactLanding}
threadId={threadId}
autoFocus={isLandingState}
status={
@@ -525,6 +571,11 @@ export default function ChatPage() {
)}
/>
)}
{compactLanding && (
<div className="text-muted-foreground/70 w-full -translate-y-2 text-center text-[11px]">
CM Assistant V1.2 · 深度推理可能会涉及数据同步 · 信息由AI自动生成,请审慎参考
</div>
)}
{env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" && (
<div className="text-muted-foreground/67 w-full translate-y-12 text-center text-xs">
{t.common.notAvailableInDemoMode}
@@ -19,8 +19,9 @@ export default function LightAppIframeView() {
// 由 useTheme 的 theme 直接换算主题色并显式传入,切主题时 src 立即重建、
// 把新主题色传给被嵌套应用(不依赖 <html> 的 dark class,避免时序读旧值)。
// TODO: 后续接入 blue 主题后,这里区分 blue 与 dark-blue。
const themeColor: AppThemeColor = isDark ? "dark-blue" : "red";
// 深色=dark-blue,浅蓝=blue,其余(红白)=red。
const themeColor: AppThemeColor =
theme === "dark" ? "dark-blue" : theme === "lightblue" ? "blue" : "red";
const src = useMemo(
() => (app ? buildLightAppUrl(app, undefined, themeColor) : ""),
[app, themeColor],
+2 -1
View File
@@ -109,7 +109,8 @@ export function WorkspaceLayout({
} as CSSProperties
}
data-page-sidebar-collapsed={pageSidebarCollapsed}
defaultOpen
// 紧凑模式下历史侧栏默认收起(聊天区更宽),由聊天页右上角图标钮展开
defaultOpen={!compactWorkspaceSidebar}
>
{!workspaceSidebarHidden && (
<WorkspaceSidebar compactChrome={compactWorkspaceSidebar} />