frontend: FE-033 明暗主题三态切换并跟随系统,防闪白+全局过渡+Monaco同步

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
fenghp
2026-06-11 17:38:55 +08:00
co-authored by Cursor
parent 9f9899b1d1
commit 67ecb24ffc
7 changed files with 144 additions and 8 deletions
+20 -1
View File
@@ -1,12 +1,20 @@
import { FolderCog, PanelBottom, PanelLeft, PanelRight, Unplug } from "lucide-react"
import { FolderCog, Monitor, Moon, PanelBottom, PanelLeft, PanelRight, Sun, Unplug } from "lucide-react"
import { Button } from "~/components/ui/button"
import { Separator } from "~/components/ui/separator"
import { Tooltip, TooltipContent, TooltipTrigger } from "~/components/ui/tooltip"
import { useTheme, type ThemePreference } from "~/hooks/use-theme"
import { isMacElectron } from "~/lib/platform"
import type { WorkspaceSession } from "~/lib/tfs/session"
import { cn } from "~/lib/utils"
// 主题三态对应的图标与说明文案。
const THEME_VISUALS: Record<ThemePreference, { icon: typeof Monitor; label: string }> = {
system: { icon: Monitor, label: "主题:跟随系统" },
light: { icon: Sun, label: "主题:亮色" },
dark: { icon: Moon, label: "主题:暗色" },
}
// 三个可折叠面板的显隐状态,由 WorkspaceShell 持有。
export interface PanelVisibility {
tree: boolean
@@ -33,6 +41,9 @@ export function TopBar({
}) {
// 隐藏式标题栏下顶栏整体作为拖拽区,并为左上角红绿灯预留空间。
const macInset = isMacElectron()
const theme = useTheme()
const themeVisual = THEME_VISUALS[theme.preference]
const ThemeIcon = themeVisual.icon
return (
<header
className={cn(
@@ -79,6 +90,14 @@ export function TopBar({
<PanelBottom />
</PanelToggle>
<Separator orientation="vertical" className="mx-1 h-4!" />
<Tooltip>
<TooltipTrigger asChild>
<Button size="icon-sm" variant="ghost" onClick={theme.cycle} aria-label={themeVisual.label}>
<ThemeIcon />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">{themeVisual.label}(点击切换)</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button size="icon-sm" variant="ghost" onClick={onManageWorkspace} aria-label="工作区与映射管理">
@@ -126,6 +126,7 @@ export function DiffDialog({
let diffEditor: MonacoEditor.IStandaloneDiffEditor | null = null
let originalModel: MonacoEditor.ITextModel | null = null
let modifiedModel: MonacoEditor.ITextModel | null = null
let themeObserver: MutationObserver | null = null
void (async () => {
const { monaco, detectLanguage } = await import("~/lib/monaco")
if (disposed || !containerRef.current) {
@@ -167,10 +168,16 @@ export function DiffDialog({
setBlockCount(diffEditor?.getLineChanges()?.length ?? 0)
})
editorRef.current = diffEditor
// 弹窗打开期间应用切换明暗主题时,同步更新 Monaco 主题。
themeObserver = new MutationObserver(() => {
monaco.editor.setTheme(isDarkTheme() ? "mactfs-dark" : "mactfs-light")
})
themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] })
})()
return () => {
disposed = true
editorRef.current = null
themeObserver?.disconnect()
diffEditor?.dispose()
originalModel?.dispose()
modifiedModel?.dispose()