Compare commits

...
Author SHA1 Message Date
oomeow da050d3e7e docs: update Changelog.md 2026-10-13 19:55:45 +08:00
oomeow 9a7b15eb15 fix(setting): default system titlebar to enabled if undefined 2026-10-12 21:17:53 +08:00
oomeow 98072a17a3 feat(bindings): add enable_system_titlebar option to Verge types 2026-10-12 21:16:06 +08:00
oomeow 32a093e44b Merge remote-tracking branch 'origin/dev' into refactor/custom-titlebar 2026-10-12 20:59:57 +08:00
oomeow 7ecec42f4d feat(window): add persistent system titlebar setting
- persist enable_system_titlebar in verge config and apply it to window decorations
- replace window provider/context with useWindowMaximized hook and inline controls
- add i18n field and tooltip for the setting across locales
2026-10-12 20:05:02 +08:00
33 changed files with 218 additions and 283 deletions
+1
View File
@@ -17,6 +17,7 @@
<details> <details>
<summary><strong> 🚀 优化改进 </strong></summary> <summary><strong> 🚀 优化改进 </strong></summary>
- 记住「启用系统标题栏」设置,Windows/macOS 即时生效,Linux 重启后生效
**🖥️ Windows** **🖥️ Windows**
+5
View File
@@ -73,6 +73,9 @@ pub struct IVerge {
pub collapse_navbar: Option<bool>, pub collapse_navbar: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_system_titlebar: Option<bool>,
pub sysproxy_tray_icon: Option<bool>, pub sysproxy_tray_icon: Option<bool>,
pub tun_tray_icon: Option<bool>, pub tun_tray_icon: Option<bool>,
@@ -374,6 +377,7 @@ impl IVerge {
menu_icon: Some("monochrome".into()), menu_icon: Some("monochrome".into()),
notice_position: Some("top-right".into()), notice_position: Some("top-right".into()),
collapse_navbar: Some(false), collapse_navbar: Some(false),
enable_system_titlebar: Some(true),
common_tray_icon: Some(false), common_tray_icon: Some(false),
sysproxy_tray_icon: Some(false), sysproxy_tray_icon: Some(false),
tun_tray_icon: Some(false), tun_tray_icon: Some(false),
@@ -461,6 +465,7 @@ impl IVerge {
patch!(proxy_group_tools_position); patch!(proxy_group_tools_position);
patch!(notice_position); patch!(notice_position);
patch!(collapse_navbar); patch!(collapse_navbar);
patch!(enable_system_titlebar);
patch!(common_tray_icon); patch!(common_tray_icon);
patch!(sysproxy_tray_icon); patch!(sysproxy_tray_icon);
patch!(tun_tray_icon); patch!(tun_tray_icon);
+1
View File
@@ -67,6 +67,7 @@ verge_registry! {
proxy_group_tools_position => NoEffect; proxy_group_tools_position => NoEffect;
notice_position => NoEffect; notice_position => NoEffect;
collapse_navbar => NoEffect; collapse_navbar => NoEffect;
enable_system_titlebar => NoEffect;
sysproxy_tray_icon => [TrayIcon]; sysproxy_tray_icon => [TrayIcon];
tun_tray_icon => [TrayIcon]; tun_tray_icon => [TrayIcon];
enable_tun_mode => Tun; enable_tun_mode => Tun;
+5 -1
View File
@@ -116,9 +116,13 @@ mod app_init {
/// Setup window state management /// Setup window state management
pub fn setup_window_state(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> { pub fn setup_window_state(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> {
logging!(debug, Type::Setup, "初始化窗口状态管理..."); logging!(debug, Type::Setup, "初始化窗口状态管理...");
// Decorations are config-driven (`enable_system_titlebar`); restoring a stale
// value here would override the builder's `.decorations(...)` on window recreate.
let state_flags =
tauri_plugin_window_state::StateFlags::all() & !tauri_plugin_window_state::StateFlags::DECORATIONS;
let window_state_plugin = tauri_plugin_window_state::Builder::new() let window_state_plugin = tauri_plugin_window_state::Builder::new()
.with_filename(files::WINDOW_STATE) .with_filename(files::WINDOW_STATE)
.with_state_flags(tauri_plugin_window_state::StateFlags::default()) .with_state_flags(state_flags)
.build(); .build();
app.handle().plugin(window_state_plugin)?; app.handle().plugin(window_state_plugin)?;
Ok(()) Ok(())
+2 -6
View File
@@ -19,11 +19,6 @@ const DEFAULT_HEIGHT: f64 = 700.0;
const MINIMAL_WIDTH: f64 = 520.0; const MINIMAL_WIDTH: f64 = 520.0;
const MINIMAL_HEIGHT: f64 = 520.0; const MINIMAL_HEIGHT: f64 = 520.0;
#[cfg(target_os = "linux")]
const DEFAULT_DECORATIONS: bool = false;
#[cfg(not(target_os = "linux"))]
const DEFAULT_DECORATIONS: bool = true;
const fn restored_window_size_is_too_small(width: u32, height: u32) -> bool { const fn restored_window_size_is_too_small(width: u32, height: u32) -> bool {
width < MINIMAL_WIDTH as u32 || height < MINIMAL_HEIGHT as u32 width < MINIMAL_WIDTH as u32 || height < MINIMAL_HEIGHT as u32
} }
@@ -49,6 +44,7 @@ pub async fn build_new_window() -> Result<WebviewWindow, String> {
let config = Config::verge().await; let config = Config::verge().await;
let latest = config.latest_arc(); let latest = config.latest_arc();
let decorations = latest.enable_system_titlebar.unwrap_or(true);
let start_page = latest.start_page.as_deref().unwrap_or("/"); let start_page = latest.start_page.as_deref().unwrap_or("/");
let initial_theme_mode = match latest.theme_mode.as_deref() { let initial_theme_mode = match latest.theme_mode.as_deref() {
Some("dark") => "dark", Some("dark") => "dark",
@@ -83,7 +79,7 @@ pub async fn build_new_window() -> Result<WebviewWindow, String> {
) )
.title("Clash Verge") .title("Clash Verge")
.center() .center()
.decorations(DEFAULT_DECORATIONS) .decorations(decorations)
.fullscreen(false) .fullscreen(false)
.inner_size(DEFAULT_WIDTH, DEFAULT_HEIGHT) .inner_size(DEFAULT_WIDTH, DEFAULT_HEIGHT)
.min_inner_size(MINIMAL_WIDTH, MINIMAL_HEIGHT) .min_inner_size(MINIMAL_WIDTH, MINIMAL_HEIGHT)
-13
View File
@@ -202,15 +202,9 @@
width: 100%; width: 100%;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
padding: 10px;
box-sizing: border-box; box-sizing: border-box;
height: 36px; height: 36px;
border-bottom: 1px solid var(--divider-color); border-bottom: 1px solid var(--divider-color);
&-drag-region {
align-self: stretch;
flex: 1 1 auto;
}
} }
.layout-content__left .the-logo { .layout-content__left .the-logo {
@@ -235,16 +229,9 @@
width: 100%; width: 100%;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
padding: 10px;
box-sizing: border-box; box-sizing: border-box;
height: 36px; height: 36px;
border-bottom: 1px solid var(--divider-color); border-bottom: 1px solid var(--divider-color);
&-drag-region {
align-self: stretch;
flex: 1 1 auto;
order: 1;
}
} }
.layout-content__left { .layout-content__left {
+69 -38
View File
@@ -1,13 +1,13 @@
import { Close, CropSquare, FilterNone, Minimize } from '@mui/icons-material' import { Close, CropSquare, FilterNone, Minimize } from '@mui/icons-material'
import { Box, IconButton } from '@mui/material' import { Box, IconButton } from '@mui/material'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { import {
forwardRef, type MouseEvent,
type PointerEvent, type PointerEvent,
useCallback, useCallback,
useImperativeHandle, useEffect,
} from 'react' } from 'react'
import { useWindowControls } from '@/hooks/use-window'
import getSystem from '@/utils/get-system' import getSystem from '@/utils/get-system'
const RESIZE_HANDLES = [ const RESIZE_HANDLES = [
@@ -21,9 +21,7 @@ const RESIZE_HANDLES = [
{ direction: 'NorthWest', position: 'north-west' }, { direction: 'NorthWest', position: 'north-west' },
] as const ] as const
export const WindowResizeHandles = () => { export const WindowResizeHandles = ({ maximized }: { maximized: boolean }) => {
const { currentWindow, maximized } = useWindowControls()
const startResizeDragging = useCallback( const startResizeDragging = useCallback(
(event: PointerEvent<HTMLDivElement>) => { (event: PointerEvent<HTMLDivElement>) => {
if (event.button !== 0) return if (event.button !== 0) return
@@ -33,14 +31,14 @@ export const WindowResizeHandles = () => {
const handle = RESIZE_HANDLES.find((item) => item.direction === direction) const handle = RESIZE_HANDLES.find((item) => item.direction === direction)
if (handle) { if (handle) {
void currentWindow void getCurrentWindow()
.startResizeDragging(handle.direction) .startResizeDragging(handle.direction)
.catch((error) => .catch((error) =>
console.warn('[WindowResizeHandles] 调整窗口大小失败:', error), console.warn('[WindowResizeHandles] 调整窗口大小失败:', error),
) )
} }
}, },
[currentWindow], [],
) )
if (getSystem() !== 'linux' || maximized) return null if (getSystem() !== 'linux' || maximized) return null
@@ -63,44 +61,77 @@ export const WindowResizeHandles = () => {
) )
} }
export const WindowControls = forwardRef(function WindowControls(props, ref) { export const WindowControls = ({
maximized,
setMaximized,
}: {
maximized: boolean
setMaximized: (value: boolean) => void
}) => {
const OS = getSystem() const OS = getSystem()
const {
currentWindow,
maximized,
minimize,
close,
toggleFullscreen,
toggleMaximize,
} = useWindowControls()
useImperativeHandle( const close = useCallback(async () => {
ref, // Delay one frame so the UI can clear :hover before the window hides.
() => ({ await new Promise((resolve) => setTimeout(resolve, 20))
currentWindow, await getCurrentWindow().close()
maximized, }, [])
minimize,
close, const minimize = useCallback(async () => {
toggleFullscreen, // Delay one frame so the UI can clear :hover before the window hides.
toggleMaximize, await new Promise((resolve) => setTimeout(resolve, 10))
}), await getCurrentWindow().minimize()
[ }, [])
currentWindow,
maximized, const toggleMaximize = useCallback(async () => {
minimize, const currentWindow = getCurrentWindow()
close, if (await currentWindow.isMaximized()) {
toggleFullscreen, await currentWindow.unmaximize()
toggleMaximize, setMaximized(false)
], } else {
await currentWindow.maximize()
setMaximized(true)
}
}, [setMaximized])
useEffect(() => {
getCurrentWindow().setMinimizable(true)
}, [])
const handleTitlebarMouseDown = useCallback(
(event: MouseEvent<HTMLDivElement>) => {
// Left button only, and only on the first press: detail 2 is the second
// press of a double-click, which `handleTitlebarDoubleClick` turns into maximize.
if (event.button !== 0 || event.detail !== 1) return
// Only the empty bar area drags; the control buttons keep their own clicks.
if (event.target !== event.currentTarget) return
event.preventDefault()
void getCurrentWindow()
.startDragging()
.catch((error) => console.warn('[WindowControls] 开始拖动失败:', error))
},
[],
) )
// 通过前端对 tauri 窗口进行翻转全屏时会短暂地与系统图标重叠渲染。 const handleTitlebarDoubleClick = useCallback(
// 这可能是上游缺陷,保险起见跨平台以窗口的最大化翻转为准。 (event: MouseEvent<HTMLDivElement>) => {
if (event.target !== event.currentTarget) return
void toggleMaximize().catch((error) =>
console.warn('[WindowControls] 切换最大化失败:', error),
)
},
[toggleMaximize],
)
return ( return (
<Box <Box
onMouseDown={handleTitlebarMouseDown}
onDoubleClick={handleTitlebarDoubleClick}
sx={{ sx={{
display: 'flex', display: 'flex',
justifyContent: OS === 'macos' ? 'flex-start' : 'flex-end',
width: '100%',
height: '100%',
px: 1,
gap: 1, gap: 1,
alignItems: 'center', alignItems: 'center',
'> button': { '> button': {
@@ -186,4 +217,4 @@ export const WindowControls = forwardRef(function WindowControls(props, ref) {
)} )}
</Box> </Box>
) )
}) }
+47 -11
View File
@@ -12,16 +12,21 @@ import {
} from '@mui/material' } from '@mui/material'
import { convertFileSrc } from '@tauri-apps/api/core' import { convertFileSrc } from '@tauri-apps/api/core'
import { join } from '@tauri-apps/api/path' import { join } from '@tauri-apps/api/path'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { open as openDialog } from '@tauri-apps/plugin-dialog' import { open as openDialog } from '@tauri-apps/plugin-dialog'
import { exists } from '@tauri-apps/plugin-fs' import { exists } from '@tauri-apps/plugin-fs'
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react' import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { BaseDialog, DialogRef, Switch, TooltipIcon } from '@/components/base' import {
BaseDialog,
type DialogRef,
Switch,
TooltipIcon,
} from '@/components/base'
import { DEFAULT_HOVER_DELAY } from '@/components/proxy/proxy-group-navigator' import { DEFAULT_HOVER_DELAY } from '@/components/proxy/proxy-group-navigator'
import { useVerge } from '@/hooks/use-verge' import { useVerge } from '@/hooks/use-verge'
import { useWindowDecorations } from '@/hooks/use-window' import { copyIconFile, getAppDir, restartApp } from '@/services/cmds'
import { copyIconFile, getAppDir } from '@/services/cmds'
import { mutate } from '@/services/mutate' import { mutate } from '@/services/mutate'
import { showNotice } from '@/services/notice-service' import { showNotice } from '@/services/notice-service'
import getSystem from '@/utils/get-system' import getSystem from '@/utils/get-system'
@@ -58,8 +63,6 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
const [sysproxyIcon, setSysproxyIcon] = useState('') const [sysproxyIcon, setSysproxyIcon] = useState('')
const [tunIcon, setTunIcon] = useState('') const [tunIcon, setTunIcon] = useState('')
const { decorated, toggleDecorations } = useWindowDecorations()
useEffect(() => { useEffect(() => {
initIconPath() initIconPath()
}, []) }, [])
@@ -123,17 +126,50 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
<List> <List>
<Item> <Item>
<ListItemText <ListItemText
primary={t( primary={
'settings.components.verge.layout.fields.preferSystemTitlebar', <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
)} <span>
{t(
'settings.components.verge.layout.fields.enableSystemTitlebar',
)}
</span>
<TooltipIcon
title={t(
'settings.components.verge.layout.tooltips.enableSystemTitlebar',
)}
sx={{ opacity: '0.7' }}
/>
</Box>
}
/> />
<GuardState <GuardState
value={decorated} value={verge?.enable_system_titlebar ?? true}
valueProps="checked" valueProps="checked"
onCatch={onError} onCatch={onError}
onFormat={onSwitchFormat} onFormat={onSwitchFormat}
onChange={async () => { onChange={(e) => onChangeData({ enable_system_titlebar: e })}
await toggleDecorations() onGuard={async (e) => {
await patchVerge({ enable_system_titlebar: e })
if (OS === 'linux') {
void mutate(() => restartApp(), {
id: 'restart-app',
errorNotice: false,
}).catch((error) => {
console.warn('[LayoutViewer] failed to restart app:', error)
})
} else {
const currentWindow = getCurrentWindow()
try {
await currentWindow.setDecorations(e)
// macOS clears the Miniaturizable style bit when decorations are disabled; re-enable minimize.
await currentWindow.setMinimizable(true)
} catch (error) {
console.warn(
'[LayoutViewer] failed to apply system titlebar setting:',
error,
)
}
}
}} }}
> >
<Switch edge="end" /> <Switch edge="end" />
+41
View File
@@ -0,0 +1,41 @@
import { getCurrentWindow } from '@tauri-apps/api/window'
import { useEffect, useState } from 'react'
import debounce from '@/utils/debounce'
export const useWindowMaximized = (enabled: boolean) => {
const [maximized, setMaximized] = useState(false)
useEffect(() => {
if (!enabled) return
const currentWindow = getCurrentWindow()
let isUnmounted = false
const syncMaximized = async () => {
if (isUnmounted) return
try {
setMaximized(await currentWindow.isMaximized())
} catch (error) {
console.warn('[useWindowMaximized] 读取最大化状态失败:', error)
}
}
void syncMaximized()
const unlistenPromise = currentWindow.onResized(
debounce(syncMaximized, 300),
)
return () => {
isUnmounted = true
unlistenPromise
.then((unlisten) => unlisten())
.catch((err) =>
console.warn('[useWindowMaximized] 清理监听器失败:', err),
)
}
}, [enabled])
return { maximized, setMaximized }
}
-46
View File
@@ -1,46 +0,0 @@
import { use } from 'react'
import { WindowContext, type WindowContextType } from '@/providers/window'
const useWindow = () => {
const context = use(WindowContext)
if (context === undefined) {
throw new Error('useWindow must be used within WindowProvider')
}
return context
}
export const useWindowControls = () => {
const {
maximized,
minimize,
toggleMaximize,
close,
toggleFullscreen,
currentWindow,
} = useWindow()
return {
maximized,
minimize,
toggleMaximize,
close,
toggleFullscreen,
currentWindow,
} satisfies Pick<
WindowContextType,
| 'maximized'
| 'minimize'
| 'toggleMaximize'
| 'close'
| 'toggleFullscreen'
| 'currentWindow'
>
}
export const useWindowDecorations = () => {
const { decorated, toggleDecorations, refreshDecorated } = useWindow()
return { decorated, toggleDecorations, refreshDecorated } satisfies Pick<
WindowContextType,
'decorated' | 'toggleDecorations' | 'refreshDecorated'
>
}
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "إعدادات التخطيط", "title": "إعدادات التخطيط",
"fields": { "fields": {
"preferSystemTitlebar": "Prefer System Titlebar", "enableSystemTitlebar": "تفعيل شريط عنوان النظام",
"trafficGraph": "مخطط حركة المرور", "trafficGraph": "مخطط حركة المرور",
"memoryUsage": "استهلاك الذاكرة", "memoryUsage": "استهلاك الذاكرة",
"proxyGroupIcon": "أيقونة مجموعة الوكلاء", "proxyGroupIcon": "أيقونة مجموعة الوكلاء",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "إيقاف عرض إحصاءات حركة المرور مؤقتًا عند فقدان التركيز" "pauseRenderTrafficStatsOnBlur": "إيقاف عرض إحصاءات حركة المرور مؤقتًا عند فقدان التركيز"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "استخدام شريط عنوان النظام الأصلي بدلاً من شريط العنوان المخصص المدمج (يلزم إعادة التشغيل على Linux).",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters", "hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds" "hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "Layout-Einstellungen", "title": "Layout-Einstellungen",
"fields": { "fields": {
"preferSystemTitlebar": "System-Titelleiste bevorzugen", "enableSystemTitlebar": "System-Titelleiste aktivieren",
"trafficGraph": "Verkehrsdiagramm", "trafficGraph": "Verkehrsdiagramm",
"memoryUsage": "Kern-Speichernutzung", "memoryUsage": "Kern-Speichernutzung",
"proxyGroupIcon": "Proxy-Gruppen-Symbol", "proxyGroupIcon": "Proxy-Gruppen-Symbol",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Rendering der Verkehrsstatistiken bei Fokusverlust pausieren" "pauseRenderTrafficStatsOnBlur": "Rendering der Verkehrsstatistiken bei Fokusverlust pausieren"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "Die native Titelleiste des Systems anstelle der integrierten benutzerdefinierten Titelleiste verwenden (unter Linux ist ein Neustart erforderlich).",
"hoverNavigator": "Beim Darüberfahren mit der Maus über die Buchstaben automatisch zur passenden Proxy-Gruppe scrollen", "hoverNavigator": "Beim Darüberfahren mit der Maus über die Buchstaben automatisch zur passenden Proxy-Gruppe scrollen",
"hoverNavigatorDelay": "Wartezeit vor dem automatischen Scrollen beim Darüberfahren, in Millisekunden" "hoverNavigatorDelay": "Wartezeit vor dem automatischen Scrollen beim Darüberfahren, in Millisekunden"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "Layout Setting", "title": "Layout Setting",
"fields": { "fields": {
"preferSystemTitlebar": "Prefer System Titlebar", "enableSystemTitlebar": "Enable System Titlebar",
"trafficGraph": "Traffic Graph", "trafficGraph": "Traffic Graph",
"memoryUsage": "Core Usage", "memoryUsage": "Core Usage",
"proxyGroupIcon": "Proxy Group Icon", "proxyGroupIcon": "Proxy Group Icon",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Pause rendering traffic statistics when blurred" "pauseRenderTrafficStatsOnBlur": "Pause rendering traffic statistics when blurred"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "Use the system's native title bar instead of the built-in custom title bar (restart required on Linux).",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters", "hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds" "hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "Configuración de la interfaz", "title": "Configuración de la interfaz",
"fields": { "fields": {
"preferSystemTitlebar": "Prefer System Titlebar", "enableSystemTitlebar": "Activar barra de título del sistema",
"trafficGraph": "Gráfico de tráfico", "trafficGraph": "Gráfico de tráfico",
"memoryUsage": "Uso de memoria del núcleo", "memoryUsage": "Uso de memoria del núcleo",
"proxyGroupIcon": "Icono del grupo de proxy", "proxyGroupIcon": "Icono del grupo de proxy",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Pausar el renderizado de estadísticas de tráfico al perder el foco" "pauseRenderTrafficStatsOnBlur": "Pausar el renderizado de estadísticas de tráfico al perder el foco"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "Usar la barra de título nativa del sistema en lugar de la barra de título personalizada integrada (se requiere reiniciar en Linux).",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters", "hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds" "hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "تنظیمات چیدمان", "title": "تنظیمات چیدمان",
"fields": { "fields": {
"preferSystemTitlebar": "Prefer System Titlebar", "enableSystemTitlebar": "فعالسازی نوار عنوان سیستم",
"trafficGraph": "نمودار ترافیک", "trafficGraph": "نمودار ترافیک",
"memoryUsage": "استفاده از حافظه", "memoryUsage": "استفاده از حافظه",
"proxyGroupIcon": "آیکون گروه پراکسی", "proxyGroupIcon": "آیکون گروه پراکسی",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "توقف رندر آمار ترافیک هنگام از دست رفتن فوکوس" "pauseRenderTrafficStatsOnBlur": "توقف رندر آمار ترافیک هنگام از دست رفتن فوکوس"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "استفاده از نوار عنوان بومی سیستم بهجای نوار عنوان سفارشی داخلی (در لینوکس نیاز به راهاندازی مجدد است).",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters", "hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds" "hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "Pengaturan Tata Letak", "title": "Pengaturan Tata Letak",
"fields": { "fields": {
"preferSystemTitlebar": "Prefer System Titlebar", "enableSystemTitlebar": "Aktifkan bilah judul sistem",
"trafficGraph": "Grafik Lalu Lintas", "trafficGraph": "Grafik Lalu Lintas",
"memoryUsage": "Penggunaan Memori", "memoryUsage": "Penggunaan Memori",
"proxyGroupIcon": "Ikon Grup Proksi", "proxyGroupIcon": "Ikon Grup Proksi",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Jeda render statistik lalu lintas saat jendela tidak fokus" "pauseRenderTrafficStatsOnBlur": "Jeda render statistik lalu lintas saat jendela tidak fokus"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "Gunakan bilah judul asli sistem sebagai ganti bilah judul kustom bawaan (perlu mulai ulang di Linux).",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters", "hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds" "hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "レイアウト設定", "title": "レイアウト設定",
"fields": { "fields": {
"preferSystemTitlebar": "Prefer System Titlebar", "enableSystemTitlebar": "システムタイトルバーを有効にする",
"trafficGraph": "トラフィックグラフ", "trafficGraph": "トラフィックグラフ",
"memoryUsage": "コアメモリ使用量", "memoryUsage": "コアメモリ使用量",
"proxyGroupIcon": "プロキシグループアイコン", "proxyGroupIcon": "プロキシグループアイコン",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "フォーカスを失ったときにトラフィック統計の描画を一時停止" "pauseRenderTrafficStatsOnBlur": "フォーカスを失ったときにトラフィック統計の描画を一時停止"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "内蔵のカスタムタイトルバーの代わりにシステム標準のタイトルバーを使用します(Linux では再起動が必要です)。",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters", "hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds" "hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "레이아웃 설정", "title": "레이아웃 설정",
"fields": { "fields": {
"preferSystemTitlebar": "시스템 제목 표시줄 사용", "enableSystemTitlebar": "시스템 제목 표시줄 활성화",
"trafficGraph": "트래픽 그래프", "trafficGraph": "트래픽 그래프",
"memoryUsage": "메모리 사용량", "memoryUsage": "메모리 사용량",
"proxyGroupIcon": "프록시 그룹 아이콘", "proxyGroupIcon": "프록시 그룹 아이콘",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "포커스를 잃으면 트래픽 통계 렌더링 일시 중지" "pauseRenderTrafficStatsOnBlur": "포커스를 잃으면 트래픽 통계 렌더링 일시 중지"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "내장 사용자 지정 제목 표시줄 대신 시스템 기본 제목 표시줄을 사용합니다(Linux에서는 다시 시작해야 함).",
"hoverNavigator": "알파벳에 마우스를 올리면 해당 프록시 그룹으로 자동 스크롤합니다", "hoverNavigator": "알파벳에 마우스를 올리면 해당 프록시 그룹으로 자동 스크롤합니다",
"hoverNavigatorDelay": "호버 시 자동 스크롤까지의 지연(밀리초)" "hoverNavigatorDelay": "호버 시 자동 스크롤까지의 지연(밀리초)"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "Configurações de Layout", "title": "Configurações de Layout",
"fields": { "fields": {
"preferSystemTitlebar": "Preferir Barra de Título do Sistema", "enableSystemTitlebar": "Ativar barra de título do sistema",
"trafficGraph": "Gráfico de Tráfego", "trafficGraph": "Gráfico de Tráfego",
"memoryUsage": "Uso do Núcleo", "memoryUsage": "Uso do Núcleo",
"proxyGroupIcon": "Ícone do Grupo de Proxy", "proxyGroupIcon": "Ícone do Grupo de Proxy",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Pausar a renderização das estatísticas de tráfego quando sem foco" "pauseRenderTrafficStatsOnBlur": "Pausar a renderização das estatísticas de tráfego quando sem foco"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "Usar a barra de título nativa do sistema em vez da barra de título personalizada integrada (é necessário reiniciar no Linux).",
"hoverNavigator": "Rola automaticamente até o grupo de proxy correspondente ao passar o mouse sobre as letras do alfabeto", "hoverNavigator": "Rola automaticamente até o grupo de proxy correspondente ao passar o mouse sobre as letras do alfabeto",
"hoverNavigatorDelay": "Atraso antes da rolagem automática ao passar o mouse, em milissegundos" "hoverNavigatorDelay": "Atraso antes da rolagem automática ao passar o mouse, em milissegundos"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "Настройки раскладки", "title": "Настройки раскладки",
"fields": { "fields": {
"preferSystemTitlebar": "Использовать системную панель заголовка", "enableSystemTitlebar": "Включить системную панель заголовка",
"trafficGraph": "График трафика", "trafficGraph": "График трафика",
"memoryUsage": "Использование памяти", "memoryUsage": "Использование памяти",
"proxyGroupIcon": "Значок группы прокси", "proxyGroupIcon": "Значок группы прокси",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "При потере фокуса приостанавливать отрисовку статистики трафика" "pauseRenderTrafficStatsOnBlur": "При потере фокуса приостанавливать отрисовку статистики трафика"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "Использовать системную панель заголовка вместо встроенной настраиваемой (в Linux требуется перезапуск).",
"hoverNavigator": "Автоматически прокручивает к группе прокси при наведении на буквы", "hoverNavigator": "Автоматически прокручивает к группе прокси при наведении на буквы",
"hoverNavigatorDelay": "Задержка перед прокруткой при наведении (в миллисекундах)" "hoverNavigatorDelay": "Задержка перед прокруткой при наведении (в миллисекундах)"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "Düzen Ayarı", "title": "Düzen Ayarı",
"fields": { "fields": {
"preferSystemTitlebar": "Sistem Başlık Çubuğunu Tercih Et", "enableSystemTitlebar": "Sistem başlık çubuğunu etkinleştir",
"trafficGraph": "Trafik Grafiği", "trafficGraph": "Trafik Grafiği",
"memoryUsage": "Çekirdek Kullanımı", "memoryUsage": "Çekirdek Kullanımı",
"proxyGroupIcon": "Vekil Grup Simgesi", "proxyGroupIcon": "Vekil Grup Simgesi",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Odak kaybolduğunda trafik istatistiklerinin işlenmesini duraklat" "pauseRenderTrafficStatsOnBlur": "Odak kaybolduğunda trafik istatistiklerinin işlenmesini duraklat"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "Yerleşik özel başlık çubuğu yerine sistemin yerel başlık çubuğunu kullan (Linux'ta yeniden başlatma gerekir).",
"hoverNavigator": "Alfabe harflerinin üzerine gelindiğinde ilgili vekil grubuna otomatik olarak kaydırır", "hoverNavigator": "Alfabe harflerinin üzerine gelindiğinde ilgili vekil grubuna otomatik olarak kaydırır",
"hoverNavigatorDelay": "Üzerine gelindiğinde otomatik kaydırmadan önceki gecikme, milisaniye cinsinden" "hoverNavigatorDelay": "Üzerine gelindiğinde otomatik kaydırmadan önceki gecikme, milisaniye cinsinden"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "Расположение көйләүләре", "title": "Расположение көйләүләре",
"fields": { "fields": {
"preferSystemTitlebar": "Prefer System Titlebar", "enableSystemTitlebar": "Enable System Titlebar",
"trafficGraph": "Трафик графигы", "trafficGraph": "Трафик графигы",
"memoryUsage": "Хәтер куллану", "memoryUsage": "Хәтер куллану",
"proxyGroupIcon": "Прокси төркеме иконкасы", "proxyGroupIcon": "Прокси төркеме иконкасы",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Фокус югалганда трафик статистикасын сызуны туктатып тору" "pauseRenderTrafficStatsOnBlur": "Фокус югалганда трафик статистикасын сызуны туктатып тору"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "Use the system's native title bar instead of the built-in custom title bar (restart required on Linux).",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters", "hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds" "hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "界面设置", "title": "界面设置",
"fields": { "fields": {
"preferSystemTitlebar": "优先使用系统标题栏", "enableSystemTitlebar": "启用系统标题栏",
"trafficGraph": "流量图显", "trafficGraph": "流量图显",
"memoryUsage": "内核占用", "memoryUsage": "内核占用",
"proxyGroupIcon": "代理组图标", "proxyGroupIcon": "代理组图标",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "失去焦点时暂停渲染流量统计" "pauseRenderTrafficStatsOnBlur": "失去焦点时暂停渲染流量统计"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "使用系统原生标题栏替代内置的自定义标题栏(Linux 下需重启生效)",
"hoverNavigator": "鼠标悬停在字母上时自动滚动到对应代理组", "hoverNavigator": "鼠标悬停在字母上时自动滚动到对应代理组",
"hoverNavigatorDelay": "鼠标悬停后触发自动跳转前等待的毫秒数" "hoverNavigatorDelay": "鼠标悬停后触发自动跳转前等待的毫秒数"
}, },
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": { "layout": {
"title": "介面設定", "title": "介面設定",
"fields": { "fields": {
"preferSystemTitlebar": "優先使用系統標題欄", "enableSystemTitlebar": "啟用系統標題欄",
"trafficGraph": "流量圖表", "trafficGraph": "流量圖表",
"memoryUsage": "內核佔用", "memoryUsage": "內核佔用",
"proxyGroupIcon": "代理組圖示", "proxyGroupIcon": "代理組圖示",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "失去焦點時暫停渲染流量統計" "pauseRenderTrafficStatsOnBlur": "失去焦點時暫停渲染流量統計"
}, },
"tooltips": { "tooltips": {
"enableSystemTitlebar": "使用系統原生標題欄取代內建的自訂標題欄(Linux 需重新啟動生效)",
"hoverNavigator": "滑鼠懸停在字母上時自動捲動到對應代理組", "hoverNavigator": "滑鼠懸停在字母上時自動捲動到對應代理組",
"hoverNavigatorDelay": "滑鼠懸停後觸發自動跳轉前等待的毫秒數" "hoverNavigatorDelay": "滑鼠懸停後觸發自動跳轉前等待的毫秒數"
}, },
+3 -6
View File
@@ -12,7 +12,6 @@ import { BaseErrorBoundary } from './components/base'
import { router } from './pages/_routers' import { router } from './pages/_routers'
import { preloadHomePageCards } from './pages/home' import { preloadHomePageCards } from './pages/home'
import { AppDataProvider } from './providers/app-data-provider' import { AppDataProvider } from './providers/app-data-provider'
import { WindowProvider } from './providers/window'
import { FALLBACK_LANGUAGE, initializeLanguage } from './services/i18n' import { FALLBACK_LANGUAGE, initializeLanguage } from './services/i18n'
import { import {
preloadAppData, preloadAppData,
@@ -50,11 +49,9 @@ const initializeApp = (initialThemeMode: 'light' | 'dark') => {
<ComposeContextProvider contexts={contexts}> <ComposeContextProvider contexts={contexts}>
<BaseErrorBoundary> <BaseErrorBoundary>
<SWRConfig value={swrConfig}> <SWRConfig value={swrConfig}>
<WindowProvider> <AppDataProvider>
<AppDataProvider> <RouterProvider router={router} />
<RouterProvider router={router} /> </AppDataProvider>
</AppDataProvider>
</WindowProvider>
</SWRConfig> </SWRConfig>
</BaseErrorBoundary> </BaseErrorBoundary>
</ComposeContextProvider> </ComposeContextProvider>
+9 -13
View File
@@ -1,7 +1,7 @@
import { Paper, ThemeProvider } from '@mui/material' import { Paper, ThemeProvider } from '@mui/material'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime' import relativeTime from 'dayjs/plugin/relativeTime'
import { useCallback, useEffect, useMemo, useRef } from 'react' import { useCallback, useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Outlet, useNavigate } from 'react-router' import { Outlet, useNavigate } from 'react-router'
@@ -16,7 +16,7 @@ import {
} from '@/components/layout/window-controller' } from '@/components/layout/window-controller'
import { useI18n } from '@/hooks/use-i18n' import { useI18n } from '@/hooks/use-i18n'
import { useVerge } from '@/hooks/use-verge' import { useVerge } from '@/hooks/use-verge'
import { useWindowDecorations } from '@/hooks/use-window' import { useWindowMaximized } from '@/hooks/use-window-maximized'
import { useEventBus } from '@/services/bus' import { useEventBus } from '@/services/bus'
import { handleNoticeMessage } from '@/services/notice-handlers' import { handleNoticeMessage } from '@/services/notice-handlers'
import { useThemeMode } from '@/services/states' import { useThemeMode } from '@/services/states'
@@ -46,21 +46,17 @@ const Layout = () => {
const { switchLanguage } = useI18n() const { switchLanguage } = useI18n()
const navigate = useNavigate() const navigate = useNavigate()
const themeReady = useMemo(() => Boolean(theme), [theme]) const themeReady = useMemo(() => Boolean(theme), [theme])
const windowControlsRef = useRef<any>(null) const showCustomTitlebar = verge?.enable_system_titlebar === false
const { decorated } = useWindowDecorations() const { maximized, setMaximized } = useWindowMaximized(showCustomTitlebar)
const customTitlebar = useMemo( const customTitlebar = useMemo(
() => () =>
decorated === false ? ( showCustomTitlebar ? (
<div className="the_titlebar"> <div className="the_titlebar">
<div <WindowControls maximized={maximized} setMaximized={setMaximized} />
className="the_titlebar-drag-region"
data-tauri-drag-region="true"
/>
<WindowControls ref={windowControlsRef} />
</div> </div>
) : null, ) : null,
[decorated], [showCustomTitlebar, maximized, setMaximized],
) )
useLoadingOverlay(themeReady) useLoadingOverlay(themeReady)
@@ -154,9 +150,9 @@ const Layout = () => {
: {}, : {},
]} ]}
> >
{decorated === false && <WindowResizeHandles />} {showCustomTitlebar && <WindowResizeHandles maximized={maximized} />}
{/* Custom titlebar - rendered only when decorated is false, memoized for performance */} {/* Custom titlebar - rendered only when the system titlebar is not preferred, memoized for performance */}
{customTitlebar} {customTitlebar}
<div className="layout-content"> <div className="layout-content">
-3
View File
@@ -1,3 +0,0 @@
export { WindowContext } from './window-context'
export type { WindowContextType } from './window-context'
export { WindowProvider } from './window-provider'
-18
View File
@@ -1,18 +0,0 @@
import { getCurrentWindow } from '@tauri-apps/api/window'
import { createContext } from 'react'
export interface WindowContextType {
decorated: boolean | null
maximized: boolean | null
toggleDecorations: () => Promise<void>
refreshDecorated: () => Promise<boolean>
minimize: () => Promise<void>
close: () => Promise<void>
toggleMaximize: () => Promise<void>
toggleFullscreen: () => Promise<void>
currentWindow: ReturnType<typeof getCurrentWindow>
}
export const WindowContext = createContext<WindowContextType | undefined>(
undefined,
)
-111
View File
@@ -1,111 +0,0 @@
import { getCurrentWindow } from '@tauri-apps/api/window'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import debounce from '@/utils/debounce'
import { WindowContext } from './window-context'
export const WindowProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const currentWindow = useMemo(() => getCurrentWindow(), [])
const [decorated, setDecorated] = useState<boolean | null>(null)
const [maximized, setMaximized] = useState<boolean | null>(null)
const close = useCallback(async () => {
// Delay one frame so the UI can clear :hover before the window hides.
await new Promise((resolve) => setTimeout(resolve, 20))
await currentWindow.close()
}, [currentWindow])
const minimize = useCallback(async () => {
// Delay one frame so the UI can clear :hover before the window hides.
await new Promise((resolve) => setTimeout(resolve, 10))
await currentWindow.minimize()
}, [currentWindow])
useEffect(() => {
let isUnmounted = false
let lastWidth = -1
let lastHeight = -1
const checkMaximized = debounce(
async (event: { payload: { width: number; height: number } }) => {
if (isUnmounted) return
const { width, height } = event.payload
if (width === lastWidth && height === lastHeight) return
lastWidth = width
lastHeight = height
const value = await currentWindow.isMaximized()
setMaximized(value)
},
300,
)
const unlistenPromise = currentWindow.onResized(checkMaximized)
return () => {
isUnmounted = true
unlistenPromise
.then((unlisten) => unlisten())
.catch((err) => console.warn('[WindowProvider] 清理监听器失败:', err))
}
}, [currentWindow])
const toggleMaximize = useCallback(async () => {
if (await currentWindow.isMaximized()) {
await currentWindow.unmaximize()
setMaximized(false)
} else {
await currentWindow.maximize()
setMaximized(true)
}
}, [currentWindow])
const toggleFullscreen = useCallback(async () => {
await currentWindow.setFullscreen(!(await currentWindow.isFullscreen()))
}, [currentWindow])
const refreshDecorated = useCallback(async () => {
const val = await currentWindow.isDecorated()
setDecorated(val)
return val
}, [currentWindow])
const toggleDecorations = useCallback(async () => {
const currentVal = await currentWindow.isDecorated()
await currentWindow.setDecorations(!currentVal)
setDecorated(!currentVal)
}, [currentWindow])
useEffect(() => {
refreshDecorated()
currentWindow.setMinimizable?.(true)
}, [currentWindow, refreshDecorated])
const contextValue = useMemo(
() => ({
decorated,
maximized,
toggleDecorations,
refreshDecorated,
minimize,
close,
toggleMaximize,
toggleFullscreen,
currentWindow,
}),
[
decorated,
maximized,
toggleDecorations,
refreshDecorated,
minimize,
close,
toggleMaximize,
toggleFullscreen,
currentWindow,
],
)
return <WindowContext value={contextValue}>{children}</WindowContext>
}
+2
View File
@@ -375,6 +375,7 @@ export type IVerge_Deserialize = {
proxy_group_tools_position?: string | null proxy_group_tools_position?: string | null
notice_position?: string | null notice_position?: string | null
collapse_navbar?: boolean | null collapse_navbar?: boolean | null
enable_system_titlebar?: boolean | null
sysproxy_tray_icon?: boolean | null sysproxy_tray_icon?: boolean | null
tun_tray_icon?: boolean | null tun_tray_icon?: boolean | null
enable_tun_mode?: boolean | null enable_tun_mode?: boolean | null
@@ -465,6 +466,7 @@ export type IVerge_Serialize = {
proxy_group_tools_position?: string | null proxy_group_tools_position?: string | null
notice_position?: string | null notice_position?: string | null
collapse_navbar: boolean | null collapse_navbar: boolean | null
enable_system_titlebar?: boolean | null
sysproxy_tray_icon: boolean | null sysproxy_tray_icon: boolean | null
tun_tray_icon: boolean | null tun_tray_icon: boolean | null
enable_tun_mode: boolean | null enable_tun_mode: boolean | null
+2 -1
View File
@@ -533,7 +533,7 @@ export const translationKeys = [
'settings.components.verge.theme.actions.editCss', 'settings.components.verge.theme.actions.editCss',
'settings.components.verge.theme.dialogs.editCssTitle', 'settings.components.verge.theme.dialogs.editCssTitle',
'settings.components.verge.layout.title', 'settings.components.verge.layout.title',
'settings.components.verge.layout.fields.preferSystemTitlebar', 'settings.components.verge.layout.fields.enableSystemTitlebar',
'settings.components.verge.layout.fields.trafficGraph', 'settings.components.verge.layout.fields.trafficGraph',
'settings.components.verge.layout.fields.memoryUsage', 'settings.components.verge.layout.fields.memoryUsage',
'settings.components.verge.layout.fields.proxyGroupIcon', 'settings.components.verge.layout.fields.proxyGroupIcon',
@@ -551,6 +551,7 @@ export const translationKeys = [
'settings.components.verge.layout.fields.enableTrayIcon', 'settings.components.verge.layout.fields.enableTrayIcon',
'settings.components.verge.layout.fields.enableTraySpeed', 'settings.components.verge.layout.fields.enableTraySpeed',
'settings.components.verge.layout.fields.pauseRenderTrafficStatsOnBlur', 'settings.components.verge.layout.fields.pauseRenderTrafficStatsOnBlur',
'settings.components.verge.layout.tooltips.enableSystemTitlebar',
'settings.components.verge.layout.tooltips.hoverNavigator', 'settings.components.verge.layout.tooltips.hoverNavigator',
'settings.components.verge.layout.tooltips.hoverNavigatorDelay', 'settings.components.verge.layout.tooltips.hoverNavigatorDelay',
'settings.components.verge.layout.options.icon.monochrome', 'settings.components.verge.layout.options.icon.monochrome',
+2 -1
View File
@@ -746,6 +746,7 @@ export interface TranslationResources {
fields: { fields: {
collapseNavBar: string collapseNavBar: string
commonTrayIcon: string commonTrayIcon: string
enableSystemTitlebar: string
enableTrayIcon: string enableTrayIcon: string
enableTraySpeed: string enableTraySpeed: string
hoverNavigator: string hoverNavigator: string
@@ -753,7 +754,6 @@ export interface TranslationResources {
memoryUsage: string memoryUsage: string
navIcon: string navIcon: string
pauseRenderTrafficStatsOnBlur: string pauseRenderTrafficStatsOnBlur: string
preferSystemTitlebar: string
proxyGroupIcon: string proxyGroupIcon: string
proxyGroupsDisplayMode: string proxyGroupsDisplayMode: string
showOutboundModesInline: string showOutboundModesInline: string
@@ -783,6 +783,7 @@ export interface TranslationResources {
} }
title: string title: string
tooltips: { tooltips: {
enableSystemTitlebar: string
hoverNavigator: string hoverNavigator: string
hoverNavigatorDelay: string hoverNavigatorDelay: string
} }
+1 -1
View File
@@ -8,7 +8,7 @@ export default function getSystem() {
if (/win64|win32/i.test(ua) || platform === 'win32') return 'windows' if (/win64|win32/i.test(ua) || platform === 'win32') return 'windows'
if (/linux/i.test(ua)) return 'linux' if (/linux/i.test(ua) || platform === 'linux') return 'linux'
return 'unknown' return 'unknown'
} }