Files
ele-HeiXiu/src/pages/settings/SettingsPage.tsx
YoungestSongMo eb12248a62 feat: 主题切换性能优化 — View Transitions API + 0.15s 统一过渡 + 设置页增强
【问题】
  桌面端(Electron)切换主题时颜色变化速度不一致、明显卡顿。
  根因分析(4 轮迭代):
    1. 过渡时长不统一(body 0.3s / 组件 0.15s)→ 统一到 0.2s
    2. antd CSS-in-JS 标签替换导致瞬间变色 → 尝试 cssVar(无效,仍走标签替换)
    3. applyHtmlTheme 在 updater 回调中执行,与 antd CSS 变更不同帧 → 移入 useLayoutEffect
    4. html * 全局过渡在 Electron 中为数千 DOM 节点同步插值 → 合成器掉帧
  终极方案:View Transitions API(GPU 合成器 1 次 cross-fade 替代 N 个 CSS 过渡)
   + 精准 CSS 过渡覆盖 ~80 个 antd 容器类(回退方案)

  【修改】

  ThemeProvider.tsx — 核心重构
    - applyHtmlTheme 从 setIsDark updater → useLayoutEffect([isDark])
      确保自定义 CSS 变量与 antd CSS-in-JS 在同一次 React 提交中生效
    - View Transitions API:document.startViewTransition + flushSync
      GPU 合成器截取旧/新两帧做单次 cross-fade,消除多元素插值卡顿
    - 浏览器不支持时自动回退到 CSS transition 方案

  globals.css — 过渡策略
    - body: 0.3s ease → 0.15s linear
    - View Transitions 动画:::view-transition-old/new(root) 0.15s linear
    - ~80 个 antd 容器类精准过渡(Layout/Card/Modal/Table/Input/
      Select/Picker/Menu/Tabs/Tag/Alert/Empty/Result/Skeleton 等)
      按功能分组:布局 → 表格 → 表单 → 导航 → 反馈
      不覆盖交互组件自有 transition(Button/Switch/Slider 等)
      linear 替代 ease — 匀速插值,桌面应用更利落

  Layout.tsx / HomeContent.tsx / LeftPanel.tsx / ModelSelector.tsx
    - 过渡参数对齐:全部 0.2s → 0.15s linear

  SettingsPage.tsx — 用户体验
    - Modal 取消 keyboard={false},Esc 键可关闭
    - macOS:关闭按钮移至标题左侧(遵循 macOS HIG)
    - afterClose 中 blur 当前焦点元素,防止聚焦跳到导航栏

  【文档】
  CHANGELOG.md — 新增 0.0.15 版本记录
  CLAUDE.md — 新增主题系统架构章节(双轨颜色体系 / 时序 / 过渡策略)
2026-06-09 11:02:40 +08:00

73 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// SettingsPage — 设置页面Modal 居中叠加,事件驱动显隐)
//
// 核心设计:
// - 通过 open/onClose props 控制显隐,不使用路由(避免主页面被卸载)
// - Modal 居中弹出 + 遮罩层,首页内容始终保持挂载不动
// - destroyOnHidden → 关闭即销毁 DOM不堆内存
// - Esc 键可关闭(跨平台统一)
// - macOS关闭按钮移至标题左侧遵循 macOS HIG
//
// 对应 QT 原版:设置窗口为独立窗口叠加在主窗口之上,主窗口不销毁
// ============================================================
import { useCallback } from 'react';
import { Modal } from 'antd';
import { SettingOutlined, CloseOutlined } from '@ant-design/icons';
import { useAppContext } from '@/contexts/app-context';
import { isMacOS } from '@/utils/platform';
import { ThemeSetting } from './blocks/ThemeSetting';
import { StoragePathSetting } from './blocks/StoragePathSetting';
import { SystemInfoSetting } from './blocks/SystemInfoSetting';
import { UpdateSetting } from './blocks/UpdateSetting';
import { PasswordSetting } from './blocks/PasswordSetting';
interface SettingsPageProps {
open: boolean;
onClose: () => void;
}
export function SettingsPage({ open, onClose }: SettingsPageProps) {
const { edition } = useAppContext();
const isMac = isMacOS();
// 关闭后移除焦点,避免跑到导航栏设置按钮上
const handleAfterClose = useCallback(() => {
(document.activeElement as HTMLElement)?.blur();
}, []);
return (
<Modal
open={open}
width={480}
onCancel={onClose}
afterClose={handleAfterClose}
destroyOnHidden={true}
mask={{ closable: false }}
closable={!isMac}
footer={null}
title={
<span className="flex items-center gap-2">
{isMac && (
<CloseOutlined
onClick={onClose}
className="cursor-pointer text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"
/>
)}
<SettingOutlined />
</span>
}
styles={{ body: { padding: 0, maxHeight: '70vh', overflow: 'auto' } }}
>
<div className="flex flex-col gap-4 p-4">
<ThemeSetting />
<SystemInfoSetting />
<UpdateSetting />
<StoragePathSetting edition={edition} />
<PasswordSetting />
</div>
</Modal>
);
}