diff --git a/packages/shared/IpcChannel.ts b/packages/shared/IpcChannel.ts index 8361a917e5..8504c47005 100644 --- a/packages/shared/IpcChannel.ts +++ b/packages/shared/IpcChannel.ts @@ -48,6 +48,7 @@ export enum IpcChannel { App_QuoteToMain = 'app:quote-to-main', App_SetDisableHardwareAcceleration = 'app:set-disable-hardware-acceleration', + App_SetUseSystemTitleBar = 'app:set-use-system-title-bar', Notification_Send = 'notification:send', Notification_OnClick = 'notification:on-click', diff --git a/src/main/ipc.ts b/src/main/ipc.ts index ccaa664ab8..4d32ada513 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -900,6 +900,9 @@ export async function registerIpc(mainWindow: BrowserWindow, app: Electron.App) ipcMain.handle(IpcChannel.App_SetDisableHardwareAcceleration, (_, isDisable: boolean) => { configManager.setDisableHardwareAcceleration(isDisable) }) + ipcMain.handle(IpcChannel.App_SetUseSystemTitleBar, (_, isActive: boolean) => { + configManager.setUseSystemTitleBar(isActive) + }) ipcMain.handle(IpcChannel.TRACE_SAVE_DATA, (_, topicId: string) => saveSpans(topicId)) ipcMain.handle(IpcChannel.TRACE_GET_DATA, (_, topicId: string, traceId: string, modelName?: string) => getSpans(topicId, traceId, modelName) diff --git a/src/main/services/ConfigManager.ts b/src/main/services/ConfigManager.ts index 98537c85a1..b1eafb31b2 100644 --- a/src/main/services/ConfigManager.ts +++ b/src/main/services/ConfigManager.ts @@ -45,6 +45,7 @@ export enum ConfigKeys { SelectionAssistantFilterMode = 'selectionAssistantFilterMode', SelectionAssistantFilterList = 'selectionAssistantFilterList', DisableHardwareAcceleration = 'disableHardwareAcceleration', + UseSystemTitleBar = 'useSystemTitleBar', Proxy = 'proxy', EnableDeveloperMode = 'enableDeveloperMode', ClientId = 'clientId', @@ -251,6 +252,14 @@ export class ConfigManager { this.set(ConfigKeys.DisableHardwareAcceleration, value) } + getUseSystemTitleBar(): boolean { + return this.get(ConfigKeys.UseSystemTitleBar, false) + } + + setUseSystemTitleBar(value: boolean) { + this.set(ConfigKeys.UseSystemTitleBar, value) + } + setAndNotify(key: string, value: unknown) { this.set(key, value, true) } diff --git a/src/main/services/WindowService.ts b/src/main/services/WindowService.ts index c76753156d..ae8bc71d8a 100644 --- a/src/main/services/WindowService.ts +++ b/src/main/services/WindowService.ts @@ -75,7 +75,8 @@ export class WindowService { trafficLightPosition: { x: 8, y: 13 } } : { - frame: false // Frameless window for Windows and Linux + // On Linux, allow using system title bar if setting is enabled + frame: isLinux && configManager.getUseSystemTitleBar() ? true : false }), backgroundColor: isMac ? undefined : nativeTheme.shouldUseDarkColors ? '#181818' : '#FFFFFF', darkTheme: nativeTheme.shouldUseDarkColors, diff --git a/src/preload/index.ts b/src/preload/index.ts index cb8b0f6919..f2a892142e 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -503,6 +503,7 @@ const api = { quoteToMainWindow: (text: string) => ipcRenderer.invoke(IpcChannel.App_QuoteToMain, text), setDisableHardwareAcceleration: (isDisable: boolean) => ipcRenderer.invoke(IpcChannel.App_SetDisableHardwareAcceleration, isDisable), + setUseSystemTitleBar: (isActive: boolean) => ipcRenderer.invoke(IpcChannel.App_SetUseSystemTitleBar, isActive), trace: { saveData: (topicId: string) => ipcRenderer.invoke(IpcChannel.TRACE_SAVE_DATA, topicId), getData: (topicId: string, traceId: string, modelName?: string) => diff --git a/src/renderer/src/components/Tab/TabContainer.tsx b/src/renderer/src/components/Tab/TabContainer.tsx index 5ac1353be8..2d4d34a249 100644 --- a/src/renderer/src/components/Tab/TabContainer.tsx +++ b/src/renderer/src/components/Tab/TabContainer.tsx @@ -2,12 +2,13 @@ import { PlusOutlined } from '@ant-design/icons' import { loggerService } from '@logger' import { Sortable, useDndReorder } from '@renderer/components/dnd' import HorizontalScrollContainer from '@renderer/components/HorizontalScrollContainer' -import { isMac } from '@renderer/config/constant' +import { isLinux, isMac } from '@renderer/config/constant' import { DEFAULT_MIN_APPS } from '@renderer/config/minapps' import { useTheme } from '@renderer/context/ThemeProvider' import { useFullscreen } from '@renderer/hooks/useFullscreen' import { useMinappPopup } from '@renderer/hooks/useMinappPopup' import { useMinapps } from '@renderer/hooks/useMinapps' +import { useSettings } from '@renderer/hooks/useSettings' import { getThemeModeLabel, getTitleLabel } from '@renderer/i18n/label' import tabsService from '@renderer/services/TabsService' import { useAppDispatch, useAppSelector } from '@renderer/store' @@ -122,6 +123,7 @@ const TabsContainer: React.FC = ({ children }) => { const { settedTheme, toggleTheme } = useTheme() const { hideMinappPopup, minAppsCache } = useMinappPopup() const { minapps } = useMinapps() + const { useSystemTitleBar } = useSettings() const { t } = useTranslation() const getTabId = (path: string): string => { @@ -268,7 +270,7 @@ const TabsContainer: React.FC = ({ children }) => { - + { const [isMaximized, setIsMaximized] = useState(false) const { t } = useTranslation() + const { useSystemTitleBar } = useSettings() useEffect(() => { // Check initial maximized state @@ -67,6 +69,11 @@ const WindowControls: React.FC = () => { return null } + // Hide on Linux if using system title bar + if (isLinux && useSystemTitleBar) { + return null + } + const handleMinimize = () => { window.api.windowControls.minimize() } diff --git a/src/renderer/src/components/app/Navbar.tsx b/src/renderer/src/components/app/Navbar.tsx index bfad371f1b..2a5a4e7864 100644 --- a/src/renderer/src/components/app/Navbar.tsx +++ b/src/renderer/src/components/app/Navbar.tsx @@ -22,12 +22,10 @@ export const Navbar: FC = ({ children, ...props }) => { } return ( - <> - - {children} - - {!isTopNavbar && !minappShow && } - + + {children} + {!minappShow && } + ) } diff --git a/src/renderer/src/hooks/useSettings.ts b/src/renderer/src/hooks/useSettings.ts index 3a3de7f89a..84549d40de 100644 --- a/src/renderer/src/hooks/useSettings.ts +++ b/src/renderer/src/hooks/useSettings.ts @@ -34,6 +34,7 @@ import { setTopicPosition, setTray as _setTray, setTrayOnClose, + setUseSystemTitleBar as _setUseSystemTitleBar, setWindowStyle } from '@renderer/store/settings' import type { SidebarIcon, ThemeMode, TranslateLanguageCode } from '@renderer/types' @@ -117,6 +118,10 @@ export function useSettings() { setDisableHardwareAcceleration(disableHardwareAcceleration: boolean) { dispatch(setDisableHardwareAcceleration(disableHardwareAcceleration)) window.api.setDisableHardwareAcceleration(disableHardwareAcceleration) + }, + setUseSystemTitleBar(useSystemTitleBar: boolean) { + dispatch(_setUseSystemTitleBar(useSystemTitleBar)) + window.api.setUseSystemTitleBar(useSystemTitleBar) } } } diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 1f7dece2be..a1fefe1277 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -4944,6 +4944,13 @@ "show": "Show Tray Icon", "title": "Tray" }, + "use_system_title_bar": { + "confirm": { + "content": "Changing the title bar style requires restarting the app to take effect. Do you want to restart now?", + "title": "Restart Required" + }, + "title": "Use System Title Bar (Linux)" + }, "zoom": { "reset": "Reset", "title": "Page Zoom" diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index fe5078e684..51d71aa7c3 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -4944,6 +4944,13 @@ "show": "显示托盘图标", "title": "托盘" }, + "use_system_title_bar": { + "confirm": { + "content": "更改标题栏样式需要重启应用才能生效,是否现在重启?", + "title": "需要重启应用" + }, + "title": "使用系统标题栏 (Linux)" + }, "zoom": { "reset": "重置", "title": "缩放" diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index b6697bca37..ea880e5965 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -4944,6 +4944,13 @@ "show": "顯示系統匣圖示", "title": "系統匣" }, + "use_system_title_bar": { + "confirm": { + "content": "[to be translated]:Changing the title bar style requires restarting the app to take effect. Do you want to restart now?", + "title": "[to be translated]:Restart Required" + }, + "title": "[to be translated]:Use System Title Bar (Linux)" + }, "zoom": { "reset": "重設", "title": "縮放" diff --git a/src/renderer/src/i18n/translate/de-de.json b/src/renderer/src/i18n/translate/de-de.json index 98b6ec7d84..99733e41a8 100644 --- a/src/renderer/src/i18n/translate/de-de.json +++ b/src/renderer/src/i18n/translate/de-de.json @@ -4944,6 +4944,13 @@ "show": "Tray-Symbol anzeigen", "title": "Tray" }, + "use_system_title_bar": { + "confirm": { + "content": "[to be translated]:Changing the title bar style requires restarting the app to take effect. Do you want to restart now?", + "title": "[to be translated]:Restart Required" + }, + "title": "[to be translated]:Use System Title Bar (Linux)" + }, "zoom": { "reset": "Zurücksetzen", "title": "Zoom" diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 3add278020..5b0d6a6e06 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -4944,6 +4944,13 @@ "show": "Εμφάνιση εικονιδίου συνδρομής", "title": "Συνδρομή" }, + "use_system_title_bar": { + "confirm": { + "content": "[to be translated]:Changing the title bar style requires restarting the app to take effect. Do you want to restart now?", + "title": "[to be translated]:Restart Required" + }, + "title": "[to be translated]:Use System Title Bar (Linux)" + }, "zoom": { "reset": "Επαναφορά", "title": "Κλίμακα" diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index 620e731c9f..4b08aeafd0 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -4944,6 +4944,13 @@ "show": "Mostrar bandera del sistema", "title": "Bandera" }, + "use_system_title_bar": { + "confirm": { + "content": "[to be translated]:Changing the title bar style requires restarting the app to take effect. Do you want to restart now?", + "title": "[to be translated]:Restart Required" + }, + "title": "[to be translated]:Use System Title Bar (Linux)" + }, "zoom": { "reset": "Restablecer", "title": "Escala" diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index 30af8e8d6c..a3affc5609 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -4944,6 +4944,13 @@ "show": "Afficher l'icône dans la barre d'état système", "title": "Barre d'état système" }, + "use_system_title_bar": { + "confirm": { + "content": "[to be translated]:Changing the title bar style requires restarting the app to take effect. Do you want to restart now?", + "title": "[to be translated]:Restart Required" + }, + "title": "[to be translated]:Use System Title Bar (Linux)" + }, "zoom": { "reset": "Réinitialiser", "title": "Zoom" diff --git a/src/renderer/src/i18n/translate/ja-jp.json b/src/renderer/src/i18n/translate/ja-jp.json index 0c69fb863c..01a89c23f6 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -4944,6 +4944,13 @@ "show": "トレイアイコンを表示", "title": "トレイ" }, + "use_system_title_bar": { + "confirm": { + "content": "[to be translated]:Changing the title bar style requires restarting the app to take effect. Do you want to restart now?", + "title": "[to be translated]:Restart Required" + }, + "title": "[to be translated]:Use System Title Bar (Linux)" + }, "zoom": { "reset": "リセット", "title": "ページズーム" diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index 94534d9e4d..70b08fc8c0 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -4944,6 +4944,13 @@ "show": "Mostrar ícone de bandeja", "title": "Tray" }, + "use_system_title_bar": { + "confirm": { + "content": "[to be translated]:Changing the title bar style requires restarting the app to take effect. Do you want to restart now?", + "title": "[to be translated]:Restart Required" + }, + "title": "[to be translated]:Use System Title Bar (Linux)" + }, "zoom": { "reset": "Redefinir", "title": "Escala" diff --git a/src/renderer/src/i18n/translate/ru-ru.json b/src/renderer/src/i18n/translate/ru-ru.json index 519918de1a..229a36f2a1 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -4944,6 +4944,13 @@ "show": "Показать значок в трее", "title": "Трей" }, + "use_system_title_bar": { + "confirm": { + "content": "[to be translated]:Changing the title bar style requires restarting the app to take effect. Do you want to restart now?", + "title": "[to be translated]:Restart Required" + }, + "title": "[to be translated]:Use System Title Bar (Linux)" + }, "zoom": { "reset": "Сбросить", "title": "Масштаб страницы" diff --git a/src/renderer/src/pages/home/Navbar.tsx b/src/renderer/src/pages/home/Navbar.tsx index 0d93a4bd01..f63939eb01 100644 --- a/src/renderer/src/pages/home/Navbar.tsx +++ b/src/renderer/src/pages/home/Navbar.tsx @@ -1,7 +1,6 @@ import { Navbar, NavbarCenter, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar' import { HStack } from '@renderer/components/Layout' import SearchPopup from '@renderer/components/Popups/SearchPopup' -import { isLinux, isWin } from '@renderer/config/constant' import { modelGenerating } from '@renderer/hooks/useRuntime' import { useSettings } from '@renderer/hooks/useSettings' import { useShortcut } from '@renderer/hooks/useShortcuts' @@ -123,7 +122,7 @@ const HeaderNavbar: FC = ({ justifyContent: 'flex-end', flex: activeTopicOrSession === 'topic' ? 1 : 'none', position: 'relative', - paddingRight: isWin || isLinux ? '144px' : '15px', + paddingRight: '15px', minWidth: activeTopicOrSession === 'topic' ? '' : 'auto' }} className="home-navbar-right"> diff --git a/src/renderer/src/pages/settings/DisplaySettings/DisplaySettings.tsx b/src/renderer/src/pages/settings/DisplaySettings/DisplaySettings.tsx index 444fba569f..65ccd93442 100644 --- a/src/renderer/src/pages/settings/DisplaySettings/DisplaySettings.tsx +++ b/src/renderer/src/pages/settings/DisplaySettings/DisplaySettings.tsx @@ -2,10 +2,11 @@ import CodeEditor from '@renderer/components/CodeEditor' import { ResetIcon } from '@renderer/components/Icons' import { HStack } from '@renderer/components/Layout' import TextBadge from '@renderer/components/TextBadge' -import { isMac, THEME_COLOR_PRESETS } from '@renderer/config/constant' +import { isLinux, isMac, THEME_COLOR_PRESETS } from '@renderer/config/constant' import { DEFAULT_SIDEBAR_ICONS } from '@renderer/config/sidebar' import { useTheme } from '@renderer/context/ThemeProvider' import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings' +import { useTimer } from '@renderer/hooks/useTimer' import useUserTheme from '@renderer/hooks/useUserTheme' import { useAppDispatch } from '@renderer/store' import type { AssistantIconType } from '@renderer/store/settings' @@ -68,12 +69,15 @@ const DisplaySettings: FC = () => { sidebarIcons, setTheme, assistantIconType, - userTheme + userTheme, + useSystemTitleBar, + setUseSystemTitleBar } = useSettings() const { navbarPosition, setNavbarPosition } = useNavbarPosition() const { theme, settedTheme } = useTheme() const { t } = useTranslation() const dispatch = useAppDispatch() + const { setTimeoutTimer } = useTimer() const [currentZoom, setCurrentZoom] = useState(1.0) const { setUserTheme } = useUserTheme() @@ -88,6 +92,26 @@ const DisplaySettings: FC = () => { [setWindowStyle] ) + const handleUseSystemTitleBarChange = (checked: boolean) => { + window.modal.confirm({ + title: t('settings.use_system_title_bar.confirm.title'), + content: t('settings.use_system_title_bar.confirm.content'), + okText: t('common.confirm'), + cancelText: t('common.cancel'), + centered: true, + onOk() { + setUseSystemTitleBar(checked) + setTimeoutTimer( + 'handleUseSystemTitleBarChange', + () => { + window.api.relaunchApp() + }, + 500 + ) + } + }) + } + const handleColorPrimaryChange = useCallback( (colorHex: string) => { setUserTheme({ @@ -260,6 +284,15 @@ const DisplaySettings: FC = () => { )} + {isLinux && ( + <> + + + {t('settings.use_system_title_bar.title')} + + + + )} diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index 3ba3cc4da8..01460ba035 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -198,6 +198,8 @@ export interface SettingsState { enableQuickPanelTriggers: boolean // 硬件加速设置 disableHardwareAcceleration: boolean + // 使用系统标题栏 (仅Linux) + useSystemTitleBar: boolean exportMenuOptions: { image: boolean markdown: boolean @@ -383,6 +385,8 @@ export const initialState: SettingsState = { confirmRegenerateMessage: true, // 硬件加速设置 disableHardwareAcceleration: false, + // 使用系统标题栏 (仅Linux) + useSystemTitleBar: false, exportMenuOptions: { image: true, markdown: true, @@ -818,6 +822,9 @@ const settingsSlice = createSlice({ setDisableHardwareAcceleration: (state, action: PayloadAction) => { state.disableHardwareAcceleration = action.payload }, + setUseSystemTitleBar: (state, action: PayloadAction) => { + state.useSystemTitleBar = action.payload + }, setOpenAISummaryText: (state, action: PayloadAction) => { state.openAI.summaryText = action.payload }, @@ -998,6 +1005,7 @@ export const { setConfirmDeleteMessage, setConfirmRegenerateMessage, setDisableHardwareAcceleration, + setUseSystemTitleBar, setOpenAISummaryText, setOpenAIVerbosity, setOpenAIStreamOptionsIncludeUsage,