mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-21 05:40:23 +08:00
feat: Windows 端支持原生窗口控制并实现主题同步 (#31)
This commit is contained in:
@@ -40,6 +40,7 @@ nsis:
|
||||
installerIcon: build/icon.ico
|
||||
uninstallerIcon: build/icon.ico
|
||||
include: installer-dpi.nsh
|
||||
differentialPackage: false
|
||||
|
||||
# macOS 平台配置
|
||||
mac:
|
||||
|
||||
+37
-1
@@ -118,10 +118,24 @@ class MainProcess {
|
||||
}
|
||||
|
||||
// macOS: 使用 hiddenInset 保留红绿灯按钮
|
||||
// Windows/Linux: 完全移除系统标题栏
|
||||
// Windows: 使用 titleBarOverlay,在自定义标题栏区域右侧显示原生窗口按钮
|
||||
// Linux: 使用自定义标题栏和自定义按钮
|
||||
if (platform.isMacOS) {
|
||||
windowOptions.titleBarStyle = 'hiddenInset'
|
||||
} else if (platform.isWindows) {
|
||||
// 保留系统框架,只隐藏标题栏内容,把内容区域顶到最上方
|
||||
windowOptions.titleBarStyle = 'hidden'
|
||||
// 获取当前主题状态
|
||||
const isDark = nativeTheme.shouldUseDarkColors
|
||||
windowOptions.titleBarOverlay = {
|
||||
// 背景透明
|
||||
color: '#00000000',
|
||||
// 图标颜色适配主题
|
||||
symbolColor: isDark ? '#a1a1aa' : '#52525b', // dark: zinc-400, light: zinc-600
|
||||
height: 32,
|
||||
}
|
||||
} else {
|
||||
// Linux 继续使用无边框 + 自定义按钮
|
||||
windowOptions.frame = false
|
||||
}
|
||||
|
||||
@@ -129,6 +143,28 @@ class MainProcess {
|
||||
|
||||
this.mainWindow.once('ready-to-show', () => {
|
||||
this.mainWindow?.show()
|
||||
|
||||
// Windows 上根据当前主题设置 titleBarOverlay 颜色
|
||||
if (platform.isWindows) {
|
||||
const isDark = nativeTheme.shouldUseDarkColors
|
||||
this.mainWindow?.setTitleBarOverlay({
|
||||
color: '#00000000', // 透明背景
|
||||
symbolColor: isDark ? '#a1a1aa' : '#52525b', // dark: zinc-400, light: zinc-600
|
||||
height: 32,
|
||||
})
|
||||
|
||||
// 监听主题变化,动态更新图标颜色
|
||||
nativeTheme.on('updated', () => {
|
||||
if (this.mainWindow && platform.isWindows) {
|
||||
const isDark = nativeTheme.shouldUseDarkColors
|
||||
this.mainWindow.setTitleBarOverlay({
|
||||
color: '#00000000',
|
||||
symbolColor: isDark ? '#a1a1aa' : '#52525b',
|
||||
height: 32,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 主窗口事件
|
||||
|
||||
@@ -57,6 +57,16 @@ export function registerWindowHandlers(ctx: IpcContext): void {
|
||||
ipcMain.on('window:setThemeSource', (_, mode: 'system' | 'light' | 'dark') => {
|
||||
const { nativeTheme } = require('electron')
|
||||
nativeTheme.themeSource = mode
|
||||
|
||||
// Windows 上动态更新图标颜色以匹配主题
|
||||
if (process.platform === 'win32' && win) {
|
||||
const isDark = nativeTheme.shouldUseDarkColors
|
||||
win.setTitleBarOverlay({
|
||||
color: '#00000000', // 透明背景
|
||||
symbolColor: isDark ? '#a1a1aa' : '#52525b', // dark: zinc-400, light: zinc-600
|
||||
height: 32,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// ==================== 应用信息 ====================
|
||||
|
||||
Vendored
+15
-21
@@ -13,26 +13,20 @@ declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
UAlert: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Alert.vue')['default']
|
||||
UApp: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/App.vue')['default']
|
||||
UBadge: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Badge.vue')['default']
|
||||
UButton: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Button.vue')['default']
|
||||
UCard: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Card.vue')['default']
|
||||
UChatPrompt: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/ChatPrompt.vue')['default']
|
||||
UChatPromptSubmit: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/ChatPromptSubmit.vue')['default']
|
||||
UCheckbox: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue')['default']
|
||||
UContextMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/ContextMenu.vue')['default']
|
||||
UDrawer: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Drawer.vue')['default']
|
||||
UIcon: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/vue/components/Icon.vue')['default']
|
||||
UInput: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Input.vue')['default']
|
||||
UInputTags: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/InputTags.vue')['default']
|
||||
UModal: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Modal.vue')['default']
|
||||
UPopover: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Popover.vue')['default']
|
||||
UProgress: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Progress.vue')['default']
|
||||
USelect: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Select.vue')['default']
|
||||
USwitch: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Switch.vue')['default']
|
||||
UTabs: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Tabs.vue')['default']
|
||||
UTextarea: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Textarea.vue')['default']
|
||||
UTooltip: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parser@7.28.5_@floating-ui+dom@1.7.4_@tiptap+extension-drag-handl_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Tooltip.vue')['default']
|
||||
UAlert: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Alert.vue')['default']
|
||||
UApp: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/App.vue')['default']
|
||||
UBadge: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Badge.vue')['default']
|
||||
UButton: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Button.vue')['default']
|
||||
UContextMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/ContextMenu.vue')['default']
|
||||
UDrawer: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Drawer.vue')['default']
|
||||
UIcon: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/vue/components/Icon.vue')['default']
|
||||
UInput: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Input.vue')['default']
|
||||
UModal: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Modal.vue')['default']
|
||||
UPopover: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Popover.vue')['default']
|
||||
UProgress: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Progress.vue')['default']
|
||||
USwitch: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Switch.vue')['default']
|
||||
UTabs: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Tabs.vue')['default']
|
||||
UTextarea: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Textarea.vue')['default']
|
||||
UTooltip: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.3.0_@babel+parse_a9a750d621383b428aaeaf8a849d7a17/node_modules/@nuxt/ui/dist/runtime/components/Tooltip.vue')['default']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,11 @@ const isMac = ref(false)
|
||||
const isMaximized = ref(false)
|
||||
|
||||
// 检测平台
|
||||
const isWindows = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
isMac.value = navigator.platform.toLowerCase().includes('mac')
|
||||
isWindows.value = navigator.platform.toLowerCase().includes('win')
|
||||
|
||||
// 监听窗口状态变化
|
||||
window.electron?.ipcRenderer?.on('windowState', (_: unknown, maximized: boolean) => {
|
||||
@@ -37,8 +40,9 @@ function close() {
|
||||
<!-- 中间拖拽区域 - 填充剩余空间 -->
|
||||
<div class="drag-region" />
|
||||
|
||||
<!-- 右侧窗口控制按钮 - 仅 Windows/Linux -->
|
||||
<div v-if="!isMac" class="window-controls">
|
||||
<!-- Windows: 使用原生窗口按钮(通过 titleBarOverlay),只需预留空间 -->
|
||||
<!-- Linux: 使用自定义窗口按钮 -->
|
||||
<div v-if="!isMac && !isWindows" class="window-controls">
|
||||
<!-- 最小化 -->
|
||||
<button class="control-btn" @click="minimize">
|
||||
<svg width="10" height="1" viewBox="0 0 10 1">
|
||||
|
||||
Reference in New Issue
Block a user