Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@
<link rel="icon" type="image/png" href="/icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HugAgentOS</title>
<script>
// 防闪烁:在任何 CSS/JS 加载前同步落主题,避免深色用户首屏闪白。
// key 与解析规则和 src/theme.ts 保持一致(非 light/dark 的值一律按 system);
// 分享预览页锁定浅色。主树 src/frontend/index.html 有逐字相同的一份,三处同步改。
;(function () {
try {
var mode = localStorage.getItem('hugagent_theme_mode')
var dark =
mode === 'dark' ||
(mode !== 'light' &&
typeof matchMedia === 'function' &&
matchMedia('(prefers-color-scheme: dark)').matches)
if (new URLSearchParams(location.search).has('share')) dark = false
if (dark) {
document.documentElement.setAttribute('data-theme', 'dark')
document.documentElement.style.colorScheme = 'dark'
}
} catch (e) {
/* 存储被禁等异常时保持浅色 */
}
})()
</script>
</head>
<body>
<div id="root"></div>
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ export default function App() {
onSelectSearchResult={handleSelectSearchResult}
/>

<Layout className={`jx-appMainLayout${canvasFullscreen ? ' is-canvasFullscreen' : ''}`} style={{ overflow: 'hidden', background: '#ffffff' }}>
<Layout className={`jx-appMainLayout${canvasFullscreen ? ' is-canvasFullscreen' : ''}`} style={{ overflow: 'hidden', background: 'var(--color-bg-base)' }}>
<div className={`jx-primaryPane${canvasOpen ? ' is-canvasOpen' : ''}`}>
{!showChatHeader && (
<header className="jx-mobileHeader">
Expand Down
36 changes: 36 additions & 0 deletions src/frontend/src/AppThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useEffect, useSyncExternalStore, type ReactNode } from 'react';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import enUS from 'antd/locale/en_US';
import { getLang } from './i18n';
import { getAppTheme } from './appTheme';
import { useUIStore } from './stores/uiStore';
import { applyThemeToDom, systemPrefersDark, watchSystemTheme } from './theme';

interface AppThemeProviderProps {
children: ReactNode;
/** 分享预览等对外页面锁定浅色:观看者常不是本机账号,内容外观应与作者所见一致 */
forceLight?: boolean;
}

/**
* 主题根:统一包掉 antd ConfigProvider(含 locale),按 uiStore 的档位 + 系统外观
* 解析当前深浅,切换 light/dark algorithm 并把 data-theme 落 DOM。
* 「跟随系统」经 useSyncExternalStore 订阅 matchMedia,系统翻转即时生效。
* 主入口与 CE overlay 入口共用,入口文件各自只包一层。
*/
export function AppThemeProvider({ children, forceLight = false }: AppThemeProviderProps) {
const themeMode = useUIStore((s) => s.themeMode);
const sysDark = useSyncExternalStore(watchSystemTheme, systemPrefersDark);
const isDark = !forceLight && (themeMode === 'dark' || (themeMode === 'system' && sysDark));

useEffect(() => {
applyThemeToDom(isDark);
}, [isDark]);

return (
<ConfigProvider theme={getAppTheme(isDark)} locale={getLang() === 'en' ? enUS : zhCN}>
{children}
</ConfigProvider>
);
}
86 changes: 62 additions & 24 deletions src/frontend/src/appTheme.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,63 @@
/** Ant Design theme tokens aligned with UI design spec — a single shared point for main.tsx (including the CE overlay version). */
export const appTheme = {
token: {
colorPrimary: '#126DFF',
colorSuccess: '#02B589',
colorWarning: '#F8AB42',
colorError: '#FC5D5D',
colorInfo: '#126DFF',
colorText: '#101828',
colorTextSecondary: '#475467',
colorTextTertiary: '#667085',
colorTextQuaternary: '#98A2B3',
colorBorder: '#DDE3EC',
colorBorderSecondary: '#E9EDF3',
colorBgContainer: '#FFFFFF',
colorBgLayout: '#F5F7FB',
borderRadius: 10,
borderRadiusLG: 16,
borderRadiusSM: 6,
fontFamily: '-apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", sans-serif',
fontSize: 14,
fontSizeSM: 12,
fontSizeLG: 16,
},
/**
* Ant Design theme tokens aligned with UI design spec — a single shared point for main.tsx (including the CE overlay version).
* 深浅两套 token 的取值与 styles/variables.css 的 :root / :root[data-theme="dark"] 覆盖块一一对应,两边同步改。
*/
import { theme as antdTheme } from 'antd';
import type { ThemeConfig } from 'antd';

const sharedToken = {
colorWarning: '#F8AB42',
borderRadius: 10,
borderRadiusLG: 16,
borderRadiusSM: 6,
fontFamily: '-apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", sans-serif',
fontSize: 14,
fontSizeSM: 12,
fontSizeLG: 16,
};

const lightToken = {
colorPrimary: '#126DFF',
colorSuccess: '#02B589',
colorError: '#FC5D5D',
colorInfo: '#126DFF',
colorText: '#101828',
colorTextSecondary: '#475467',
colorTextTertiary: '#667085',
colorTextQuaternary: '#98A2B3',
colorBorder: '#DDE3EC',
colorBorderSecondary: '#E9EDF3',
colorBgContainer: '#FFFFFF',
colorBgLayout: '#F5F7FB',
};

const darkToken = {
colorPrimary: '#3E8BFF',
colorSuccess: '#22C79D',
colorError: '#FF6B6B',
colorInfo: '#3E8BFF',
colorText: '#E8ECF4',
colorTextSecondary: '#B3BDCD',
colorTextTertiary: '#8792A4',
colorTextQuaternary: '#5F6B7D',
colorBorder: '#2B3442',
colorBorderSecondary: '#242C38',
colorBgContainer: '#161C25',
colorBgElevated: '#1C2330',
colorBgLayout: '#12171F',
colorBgSpotlight: '#1C2330',
};

// 模块级预建常量:同主题下引用稳定,ConfigProvider 无需重算派生 token
const LIGHT_THEME: ThemeConfig = {
algorithm: antdTheme.defaultAlgorithm,
token: { ...sharedToken, ...lightToken },
};
const DARK_THEME: ThemeConfig = {
algorithm: antdTheme.darkAlgorithm,
token: { ...sharedToken, ...darkToken },
};

export function getAppTheme(isDark: boolean): ThemeConfig {
return isDark ? DARK_THEME : LIGHT_THEME;
}
Loading
Loading