diff --git a/CHANGELOG.md b/CHANGELOG.md
index 293fc3e..4ec7bd4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,17 @@
### 变更
- 暂无
+## [4.1.3] - 2026-04-08
+
+### 修复
+- 修复独立朋友圈窗口顶部标题栏在 Windows 和 macOS 下的控件预留与标题布局错位问题
+- 修复聊天记录、内置浏览器、AI 摘要、协议页、图片查看器、视频播放页等独立窗口标题栏高度不一致导致的偏移和首屏抖动问题
+
+### 变更
+- 为独立窗口统一引入跨平台 window chrome 安全区变量,按平台区分左侧 traffic lights / 右侧系统按钮预留
+- 通用 `TitleBar` 新增独立窗口布局模式,统一 Windows 左对齐标题和 macOS 居中标题策略
+- 优化朋友圈标题栏右侧操作区在 macOS 下的收缩规则,避免按钮区挤压标题
+
## [4.1.1] - 2026-04-08
### 变更
diff --git a/README.md b/README.md
index 6c94268..27be525 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
**一款现代化的微信聊天记录查看与分析工具**
[](LICENSE)
-[](package.json)
+[](package.json)
[]()
[]()
[]()
diff --git a/electron/main.ts b/electron/main.ts
index c4586e6..fb8c18b 100644
--- a/electron/main.ts
+++ b/electron/main.ts
@@ -462,7 +462,7 @@ function createChatWindow() {
titleBarOverlay: {
color: '#00000000',
symbolColor: '#666666',
- height: 32
+ height: 40
},
show: false,
backgroundColor: isDark ? '#1A1A1A' : '#F0F0F0'
@@ -537,7 +537,7 @@ function createGroupAnalyticsWindow() {
titleBarOverlay: {
color: '#00000000',
symbolColor: '#666666',
- height: 32
+ height: 40
},
show: false,
backgroundColor: isDark ? '#1A1A1A' : '#F0F0F0'
@@ -615,7 +615,7 @@ function createMomentsWindow(filterUsername?: string) {
titleBarOverlay: {
color: '#00000000',
symbolColor: '#666666',
- height: 32
+ height: 40
},
show: false,
backgroundColor: isDark ? '#1A1A1A' : '#F0F0F0'
@@ -704,7 +704,7 @@ function createChatHistoryWindow(sessionId: string, messageId: number) {
titleBarOverlay: {
color: '#00000000',
symbolColor: isDark ? '#ffffff' : '#1a1a1a',
- height: 32
+ height: 40
},
show: false,
backgroundColor: isDark ? '#1A1A1A' : '#F0F0F0',
@@ -770,7 +770,7 @@ function createAnnualReportWindow(year: number) {
titleBarOverlay: {
color: '#00000000',
symbolColor: isDark ? '#FFFFFF' : '#333333',
- height: 32
+ height: 40
},
show: false,
backgroundColor: isDark ? '#1A1A1A' : '#F9F8F6'
@@ -842,7 +842,7 @@ function createAgreementWindow() {
titleBarOverlay: {
color: '#00000000',
symbolColor: isDark ? '#FFFFFF' : '#333333',
- height: 32
+ height: 40
},
show: false,
backgroundColor: isDark ? '#1A1A1A' : '#FFFFFF'
@@ -993,7 +993,7 @@ function createImageViewerWindow(
titleBarOverlay: {
color: '#00000000',
symbolColor: '#ffffff',
- height: 32
+ height: 40
},
show: false,
backgroundColor: '#000000',
diff --git a/package-lock.json b/package-lock.json
index 2bd12e2..396d81d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "ciphertalk",
- "version": "4.1.2",
+ "version": "4.1.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ciphertalk",
- "version": "4.1.2",
+ "version": "4.1.3",
"hasInstallScript": true,
"license": "CC-BY-NC-SA-4.0",
"dependencies": {
diff --git a/package.json b/package.json
index 11bebe5..fb07245 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ciphertalk",
- "version": "4.1.2",
+ "version": "4.1.3",
"description": "密语 - 微信聊天记录查看工具",
"author": "ILoveBingLu",
"license": "CC-BY-NC-SA-4.0",
diff --git a/src/App.tsx b/src/App.tsx
index 8fca0a5..2e2bccf 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -36,6 +36,7 @@ import { initTldList } from './utils/linkify'
import LockScreen from './pages/LockScreen'
import { useAuthStore } from './stores/authStore'
import { X, Shield, Loader2 } from 'lucide-react'
+import { applyWindowChromeToDocument } from './utils/windowChrome'
import './App.scss'
type AppUpdateInfo = {
@@ -119,6 +120,25 @@ function App() {
initAuth()
}, [loadTheme])
+ useEffect(() => {
+ let cancelled = false
+
+ const applyPlatformChrome = (platform?: string) => {
+ if (cancelled) return
+ applyWindowChromeToDocument(platform)
+ }
+
+ void window.electronAPI.app.getPlatformInfo().then((info) => {
+ applyPlatformChrome(info.platform)
+ }).catch(() => {
+ applyPlatformChrome('win32')
+ })
+
+ return () => {
+ cancelled = true
+ }
+ }, [])
+
// 应用主题
useEffect(() => {
if (!isLoaded) return
diff --git a/src/components/TitleBar.scss b/src/components/TitleBar.scss
index 3af43d9..bf5ba2b 100644
--- a/src/components/TitleBar.scss
+++ b/src/components/TitleBar.scss
@@ -1,19 +1,40 @@
.title-bar {
- height: 41px;
background: var(--bg-secondary);
- display: grid;
- grid-template-columns: minmax(140px, 1fr) auto minmax(140px, 1fr);
- align-items: center;
- padding-left: 16px;
- padding-right: 144px; // 为窗口控件留空间
border-bottom: 1px solid var(--border-color);
-webkit-app-region: drag;
flex-shrink: 0;
+ user-select: none;
- &.is-mac {
+ &.variant-app {
+ height: 41px;
+ display: grid;
+ grid-template-columns: minmax(140px, 1fr) auto minmax(140px, 1fr);
+ align-items: center;
+ padding-left: 16px;
+ padding-right: 144px; // 为窗口控件留空间
+ }
+
+ &.variant-app.is-mac {
padding-left: 84px;
padding-right: 16px;
}
+
+ &.variant-standalone {
+ height: var(--window-chrome-height);
+ min-height: var(--window-chrome-height);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--window-toolbar-gap);
+ padding-left: 16px;
+ padding-right: var(--window-controls-right-safe);
+ }
+
+ &.variant-standalone.is-mac {
+ display: grid;
+ grid-template-columns: minmax(calc(var(--window-controls-left-safe) - 16px), 1fr) auto minmax(0, 1fr);
+ padding-right: var(--window-controls-right-safe);
+ }
}
.title-bar-left {
@@ -36,7 +57,8 @@
}
.title-bar-traffic-spacer {
- width: 68px;
+ width: calc(var(--window-controls-left-safe) - 16px);
+ min-width: 68px;
height: 12px;
flex-shrink: 0;
}
@@ -78,7 +100,7 @@
display: flex;
align-items: center;
justify-content: flex-end;
- gap: 10px;
+ gap: var(--window-toolbar-gap);
min-width: 0;
-webkit-app-region: no-drag;
}
@@ -99,6 +121,23 @@
max-width: 600px;
}
+.title-bar.variant-standalone.is-win {
+ .title-bar-left {
+ flex: 1;
+ min-width: 0;
+ }
+
+ .title-bar-right {
+ flex-shrink: 0;
+ max-width: min(56vw, 520px);
+ overflow: hidden;
+ }
+
+ .titles {
+ max-width: min(46vw, 420px);
+ }
+}
+
.title-bar.is-mac {
.title-bar-left {
justify-content: flex-start;
@@ -114,6 +153,11 @@
}
}
+.title-bar.variant-standalone.is-mac {
+ .title-bar-center .titles {
+ max-width: 320px;
+ }
+}
// 导出页面标签切换
.export-tabs {
@@ -125,15 +169,23 @@
}
@media (max-width: 960px) {
- .title-bar {
+ .title-bar.variant-app {
grid-template-columns: minmax(90px, 1fr) auto minmax(90px, 1fr);
}
- .title-bar.is-mac {
+ .title-bar.variant-app.is-mac {
padding-left: 76px;
}
- .title-bar.is-mac .title-bar-center .titles {
+ .title-bar.variant-app.is-mac .title-bar-center .titles {
+ max-width: 220px;
+ }
+
+ .title-bar.variant-standalone.is-win .titles {
+ max-width: 240px;
+ }
+
+ .title-bar.variant-standalone.is-mac .title-bar-center .titles {
max-width: 220px;
}
diff --git a/src/components/TitleBar.tsx b/src/components/TitleBar.tsx
index cb69aff..84851c2 100644
--- a/src/components/TitleBar.tsx
+++ b/src/components/TitleBar.tsx
@@ -1,5 +1,6 @@
-import { ReactNode, useEffect, useState } from 'react'
+import { ReactNode } from 'react'
import { RefreshCw } from 'lucide-react'
+import { usePlatformInfo } from '../hooks/usePlatformInfo'
import { useTitleBarStore } from '../stores/titleBarStore'
import { useUpdateStatusStore } from '../stores/updateStatusStore'
import { useThemeStore } from '../stores/themeStore'
@@ -8,24 +9,16 @@ import './TitleBar.scss'
interface TitleBarProps {
rightContent?: ReactNode
title?: string
+ variant?: 'app' | 'standalone'
}
-function TitleBar({ rightContent, title }: TitleBarProps) {
+function TitleBar({ rightContent, title, variant = 'app' }: TitleBarProps) {
const storeRightContent = useTitleBarStore(state => state.rightContent)
const displayContent = rightContent ?? storeRightContent
const isUpdating = useUpdateStatusStore(state => state.isUpdating)
const appIcon = useThemeStore(state => state.appIcon)
- const [platform, setPlatform] = useState<'win32' | 'darwin' | 'linux'>('win32')
+ const { isMac } = usePlatformInfo()
- useEffect(() => {
- void window.electronAPI.app.getPlatformInfo().then((info) => {
- setPlatform((info.platform as 'win32' | 'darwin' | 'linux') || 'win32')
- }).catch(() => {
- // ignore
- })
- }, [])
-
- const isMac = platform === 'darwin'
const updateStatusNode = isUpdating ? (
) : null
+ const titleNode = (
+ <>
+
+ {title || 'CipherTalk'}
+ >
+ )
+
return (
-
+
{isMac ? (
) : (
<>
-

-
{title || 'CipherTalk'}
+ {titleNode}
{updateStatusNode}
>
)}
{isMac && (
-

-
{title || 'CipherTalk'}
+ {titleNode}
)}
diff --git a/src/hooks/usePlatformInfo.ts b/src/hooks/usePlatformInfo.ts
new file mode 100644
index 0000000..7ea4ab3
--- /dev/null
+++ b/src/hooks/usePlatformInfo.ts
@@ -0,0 +1,36 @@
+import { useEffect, useState } from 'react'
+import { normalizeWindowPlatform, type WindowPlatform } from '../utils/windowChrome'
+
+const getInitialPlatform = (): WindowPlatform => {
+ if (typeof document === 'undefined') {
+ return 'win32'
+ }
+
+ return normalizeWindowPlatform(document.documentElement.dataset.windowPlatform)
+}
+
+export function usePlatformInfo() {
+ const [platform, setPlatform] = useState
(getInitialPlatform)
+
+ useEffect(() => {
+ let cancelled = false
+
+ void window.electronAPI.app.getPlatformInfo().then((info) => {
+ if (cancelled) return
+ setPlatform(normalizeWindowPlatform(info.platform))
+ }).catch(() => {
+ // ignore
+ })
+
+ return () => {
+ cancelled = true
+ }
+ }, [])
+
+ return {
+ platform,
+ isMac: platform === 'darwin',
+ isWindows: platform === 'win32',
+ isLinux: platform === 'linux'
+ }
+}
diff --git a/src/pages/AISummaryWindow.scss b/src/pages/AISummaryWindow.scss
index 9037050..f4fc727 100644
--- a/src/pages/AISummaryWindow.scss
+++ b/src/pages/AISummaryWindow.scss
@@ -7,137 +7,190 @@
overflow: hidden;
.title-bar {
- display: flex;
align-items: center;
- justify-content: space-between;
- height: 40px;
- padding: 0 140px 0 12px; // 右侧留出空间给原生窗口控件
+ height: var(--window-chrome-height);
+ min-height: var(--window-chrome-height);
background: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
-webkit-app-region: drag;
user-select: none;
- gap: 12px;
+ }
- .title-content {
+ .title-bar-leading-spacer {
+ width: calc(var(--window-controls-left-safe) - 16px);
+ min-width: 68px;
+ height: 12px;
+ }
+
+ .title-bar-center {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ min-width: 0;
+ }
+
+ .title-content {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ min-width: 0;
+ flex-shrink: 1;
+
+ .session-avatar {
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
+ object-fit: cover;
+ flex-shrink: 0;
+ }
+
+ .multiply-symbol {
+ font-size: 14px;
+ color: var(--text-tertiary);
+ font-weight: 300;
+ margin: 0 -2px;
+ flex-shrink: 0;
+ }
+
+ .ai-provider-badge {
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
display: flex;
align-items: center;
- gap: 8px;
+ justify-content: center;
flex-shrink: 0;
- .session-avatar {
- width: 24px;
- height: 24px;
- border-radius: 50%;
- object-fit: cover;
- flex-shrink: 0;
- }
+ .ai-provider-logo {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
- .multiply-symbol {
- font-size: 14px;
- color: var(--text-tertiary);
- font-weight: 300;
- margin: 0 -2px;
- }
-
- .ai-provider-badge {
- width: 24px;
- height: 24px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
-
- .ai-provider-logo {
- width: 100%;
- height: 100%;
- object-fit: contain;
-
- // 对于不带 -color 的 logo,根据主题自适应颜色
- &:not([src*="-color"]) {
- filter: brightness(0) saturate(100%) invert(var(--logo-invert, 0));
- opacity: 0.85;
- }
+ &:not([src*="-color"]) {
+ filter: brightness(0) saturate(100%) invert(var(--logo-invert, 0));
+ opacity: 0.85;
}
}
-
- .session-name {
- font-size: 13px;
- font-weight: 600;
- color: var(--text-primary);
- white-space: nowrap;
- }
}
- .message-count {
- font-size: 11px;
- color: var(--primary);
- background: var(--primary-light);
- padding: 2px 8px;
- border-radius: 10px;
+ .session-name {
+ font-size: 13px;
+ font-weight: 600;
+ color: var(--text-primary);
white-space: nowrap;
- flex-shrink: 0;
- font-weight: 500;
- margin-right: auto; // 推到左边
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: min(34vw, 300px);
+ }
+ }
+
+ .message-count {
+ font-size: 11px;
+ color: var(--primary);
+ background: var(--primary-light);
+ padding: 2px 8px;
+ border-radius: 10px;
+ white-space: nowrap;
+ flex-shrink: 0;
+ font-weight: 500;
+ }
+
+ .title-actions {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ -webkit-app-region: no-drag;
+ flex-shrink: 0;
+
+ .generating-status {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 28px;
+ height: 28px;
+ margin-right: 8px;
+ position: relative;
+
+ .spinner {
+ animation: spin 1s linear infinite;
+ color: var(--primary);
+ }
+
+ &:hover::after {
+ content: attr(data-tooltip);
+ position: absolute;
+ top: 100%;
+ right: 0;
+ transform: translateY(8px);
+ padding: 4px 8px;
+ background: var(--bg-tooltip, rgba(0, 0, 0, 0.8));
+ color: white;
+ font-size: 11px;
+ border-radius: 4px;
+ white-space: nowrap;
+ pointer-events: none;
+ z-index: 1000;
+ animation: tooltipFadeIn 0.2s;
+ }
+ }
+
+ .title-btn {
+ width: 28px;
+ height: 28px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: transparent;
+ border: none;
+ border-radius: 4px;
+ color: var(--text-secondary);
+ cursor: pointer;
+ transition: all 0.2s;
+
+ &:hover {
+ background: var(--bg-hover);
+ color: var(--primary);
+ }
+ }
+ }
+
+ &.is-win {
+ .title-bar {
+ display: flex;
+ justify-content: space-between;
+ gap: var(--window-toolbar-gap);
+ padding: 0 var(--window-controls-right-safe) 0 12px;
+ }
+
+ .title-bar-center {
+ flex: 1;
+ margin-right: auto;
}
.title-actions {
- display: flex;
- align-items: center;
- gap: 4px;
- -webkit-app-region: no-drag;
- flex-shrink: 0;
+ margin-left: auto;
+ }
+ }
- .generating-status {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 28px;
- height: 28px;
- margin-right: 8px;
- position: relative;
+ &.is-mac {
+ .title-bar {
+ display: grid;
+ grid-template-columns: minmax(calc(var(--window-controls-left-safe) - 16px), 1fr) auto minmax(0, 1fr);
+ gap: var(--window-toolbar-gap);
+ padding: 0 var(--window-controls-right-safe) 0 16px;
+ }
- .spinner {
- animation: spin 1s linear infinite;
- color: var(--primary);
- }
+ .title-bar-center {
+ justify-self: center;
+ }
- &:hover::after {
- content: attr(data-tooltip);
- position: absolute;
- top: 100%;
- right: 0;
- transform: translateY(8px);
- padding: 4px 8px;
- background: var(--bg-tooltip, rgba(0, 0, 0, 0.8));
- color: white;
- font-size: 11px;
- border-radius: 4px;
- white-space: nowrap;
- pointer-events: none;
- z-index: 1000;
- animation: tooltipFadeIn 0.2s;
- }
- }
+ .title-actions {
+ justify-self: end;
+ overflow: hidden;
+ }
- .title-btn {
- width: 28px;
- height: 28px;
- display: flex;
- align-items: center;
- justify-content: center;
- background: transparent;
- border: none;
- border-radius: 4px;
- color: var(--text-secondary);
- cursor: pointer;
- transition: all 0.2s;
-
- &:hover {
- background: var(--bg-hover);
- color: var(--primary);
- }
- }
+ .title-content .session-name {
+ max-width: min(28vw, 240px);
}
}
diff --git a/src/pages/AISummaryWindow.tsx b/src/pages/AISummaryWindow.tsx
index cb42816..e4bdd78 100644
--- a/src/pages/AISummaryWindow.tsx
+++ b/src/pages/AISummaryWindow.tsx
@@ -3,10 +3,12 @@ import { Copy, Download, RefreshCw, Loader2, Send, ArrowLeft, Trash2, LoaderPinw
import { marked } from 'marked'
import DOMPurify from 'dompurify'
import { TIME_RANGE_OPTIONS, type SummaryResult } from '../types/ai'
+import { usePlatformInfo } from '../hooks/usePlatformInfo'
import AIProviderLogo from '../components/ai/AIProviderLogo'
import './AISummaryWindow.scss'
function AISummaryWindow() {
+ const { isMac } = usePlatformInfo()
const [sessionId, setSessionId] = useState('')
const [sessionName, setSessionName] = useState('')
const [avatarUrl, setAvatarUrl] = useState('')
@@ -363,33 +365,37 @@ function AISummaryWindow() {
}
return (
-
+
{/* 自定义标题栏 */}
-
- {avatarUrl && (
-

+ {isMac &&
}
+
+
+
+ {avatarUrl && (
+

+ )}
+ {aiProviderInfo && (
+ <>
+
×
+
+ >
+ )}
+
{sessionName}
+
+
+ {result && (
+
{result.messageCount}条
)}
- {aiProviderInfo && (
- <>
-
×
-
- >
- )}
-
{sessionName}
-
- {result && (
-
{result.messageCount}条
- )}
{isGenerating && (
diff --git a/src/pages/AgreementPage.scss b/src/pages/AgreementPage.scss
index 906f0fa..7edc294 100644
--- a/src/pages/AgreementPage.scss
+++ b/src/pages/AgreementPage.scss
@@ -6,19 +6,42 @@
}
.agreement-titlebar {
- height: 40px;
+ height: var(--window-chrome-height);
+ min-height: var(--window-chrome-height);
display: flex;
align-items: center;
- justify-content: center;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
-webkit-app-region: drag;
flex-shrink: 0;
+ user-select: none;
+
+ &.is-win {
+ justify-content: flex-start;
+ padding-left: 16px;
+ padding-right: var(--window-controls-right-safe);
+ }
+
+ &.is-mac {
+ display: grid;
+ grid-template-columns: minmax(calc(var(--window-controls-left-safe) - 16px), 1fr) auto minmax(0, 1fr);
+ padding-left: 16px;
+ padding-right: var(--window-controls-right-safe);
+ }
+
+ .agreement-titlebar-spacer {
+ width: calc(var(--window-controls-left-safe) - 16px);
+ min-width: 68px;
+ height: 12px;
+ }
span {
font-size: 14px;
font-weight: 500;
color: var(--text-primary);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
}
@@ -92,4 +115,4 @@
background: var(--text-tertiary);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/pages/AgreementPage.tsx b/src/pages/AgreementPage.tsx
index 600ecb9..3c2f758 100644
--- a/src/pages/AgreementPage.tsx
+++ b/src/pages/AgreementPage.tsx
@@ -1,10 +1,14 @@
import './AgreementPage.scss'
import * as configService from '../services/config'
+import { usePlatformInfo } from '../hooks/usePlatformInfo'
function AgreementPage() {
+ const { isMac } = usePlatformInfo()
+
return (
-
+
diff --git a/src/pages/AnnualReportWindow.scss b/src/pages/AnnualReportWindow.scss
index dd5e3b7..966784a 100644
--- a/src/pages/AnnualReportWindow.scss
+++ b/src/pages/AnnualReportWindow.scss
@@ -804,12 +804,16 @@
position: fixed;
top: 0;
left: 0;
- right: 138px;
- height: 32px;
+ right: var(--window-controls-right-safe);
+ height: var(--window-chrome-height);
-webkit-app-region: drag !important;
z-index: 100;
}
+html[data-window-platform="darwin"] .annual-report-window .drag-region {
+ left: var(--window-controls-left-safe);
+}
+
// 浮动操作按钮
.fab-container {
position: fixed;
diff --git a/src/pages/BrowserWindowPage.tsx b/src/pages/BrowserWindowPage.tsx
index 2d2353b..5dc663d 100644
--- a/src/pages/BrowserWindowPage.tsx
+++ b/src/pages/BrowserWindowPage.tsx
@@ -74,7 +74,7 @@ const BrowserWindowPage = () => {
return (
-
+
{/* 简单的进度条 */}
{isLoading && (
@@ -83,7 +83,7 @@ const BrowserWindowPage = () => {
background: 'var(--primary)',
width: '100%',
position: 'fixed',
- top: '32px', // TitleBar height
+ top: 'var(--window-chrome-height)',
zIndex: 9999,
animation: 'loading 2s infinite linear'
}} />
diff --git a/src/pages/ChatHistoryPage.tsx b/src/pages/ChatHistoryPage.tsx
index 7fdfbd8..39bd820 100644
--- a/src/pages/ChatHistoryPage.tsx
+++ b/src/pages/ChatHistoryPage.tsx
@@ -153,7 +153,7 @@ export default function ChatHistoryPage() {
return (
-
+
{loading ? (
加载中...
diff --git a/src/pages/ChatPage.scss b/src/pages/ChatPage.scss
index 37c0b5e..c54464f 100644
--- a/src/pages/ChatPage.scss
+++ b/src/pages/ChatPage.scss
@@ -18,7 +18,7 @@
backdrop-filter: blur(20px);
.session-header {
- padding-top: 38px;
+ padding-top: calc(var(--window-chrome-height) + 6px);
padding-bottom: 12px;
padding-left: 16px;
padding-right: 16px;
@@ -248,7 +248,7 @@
.message-header {
height: auto;
padding: 0 24px;
- padding-top: 38px;
+ padding-top: calc(var(--window-chrome-height) + 6px);
padding-bottom: 12px;
background: var(--card-bg);
border-bottom: 1px solid var(--border-color);
diff --git a/src/pages/GroupAnalyticsPage.scss b/src/pages/GroupAnalyticsPage.scss
index 8279312..c7647cd 100644
--- a/src/pages/GroupAnalyticsPage.scss
+++ b/src/pages/GroupAnalyticsPage.scss
@@ -13,7 +13,7 @@
-webkit-app-region: no-drag;
.sidebar-header {
- padding-top: 38px;
+ padding-top: calc(var(--window-chrome-height) + 6px);
padding-bottom: 12px;
padding-left: 16px;
padding-right: 16px;
@@ -428,7 +428,7 @@
align-items: center;
gap: 12px;
padding: 0 24px;
- padding-top: 38px;
+ padding-top: calc(var(--window-chrome-height) + 6px);
padding-bottom: 12px;
background: var(--card-bg);
border-bottom: 1px solid var(--border-color);
diff --git a/src/pages/ImageWindow.scss b/src/pages/ImageWindow.scss
index 820cd6c..c3ab7ed 100644
--- a/src/pages/ImageWindow.scss
+++ b/src/pages/ImageWindow.scss
@@ -9,14 +9,15 @@
// -webkit-app-region: drag; // Removed global drag to prevent unwanted resize/drag behaviors
.title-bar {
- height: 40px;
- min-height: 40px;
+ height: var(--window-chrome-height);
+ min-height: var(--window-chrome-height);
display: flex;
justify-content: space-between;
align-items: center;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
- padding-right: 140px; // 为原生窗口控件留出空间
+ padding-left: var(--window-controls-left-safe);
+ padding-right: var(--window-controls-right-safe);
-webkit-app-region: drag; // Only title bar is natively draggable
z-index: 500; // Even higher to stay above image content
position: relative;
@@ -30,9 +31,8 @@
.title-bar-controls {
display: flex;
align-items: center;
- gap: 8px;
+ gap: var(--window-toolbar-gap);
-webkit-app-region: no-drag;
- margin-right: 16px;
button {
background: transparent;
@@ -275,4 +275,4 @@
@keyframes tooltipFadeInSns {
// Deprecated, using transitions to avoid transform conflicts
-}
\ No newline at end of file
+}
diff --git a/src/pages/MomentsWindow.scss b/src/pages/MomentsWindow.scss
index 90e9836..77a3fdf 100644
--- a/src/pages/MomentsWindow.scss
+++ b/src/pages/MomentsWindow.scss
@@ -1241,7 +1241,9 @@
.title-actions {
display: flex;
align-items: center;
- gap: 8px;
+ gap: var(--window-toolbar-gap);
+ min-width: 0;
+ max-width: 100%;
.export-btn {
display: flex;
@@ -1264,6 +1266,7 @@
line-height: 1;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.25, 1.5, 0.5, 1);
+ flex-shrink: 0;
&:hover {
background: linear-gradient(135deg, rgba(var(--primary-rgb), 0.25) 0%, rgba(var(--primary-rgb), 0.1) 100%);
@@ -1295,6 +1298,7 @@
align-items: center;
justify-content: center;
position: relative;
+ flex-shrink: 0;
&:hover {
background: var(--hover-bg);
@@ -1333,6 +1337,29 @@
}
}
+html[data-window-platform="darwin"] .moments-window .title-actions {
+ gap: 6px;
+}
+
+@media (max-width: 1180px) {
+ html[data-window-platform="darwin"] .moments-window .title-actions .divider {
+ display: none;
+ }
+}
+
+@media (max-width: 1040px) {
+ html[data-window-platform="darwin"] .moments-window .title-actions .export-btn {
+ width: 30px;
+ padding: 6px;
+ gap: 0;
+ justify-content: center;
+ }
+
+ html[data-window-platform="darwin"] .moments-window .title-actions .export-btn span {
+ display: none;
+ }
+}
+
.icon-btn {
width: 30px;
height: 30px;
@@ -1784,4 +1811,4 @@
100% {
background-position: -200% 0;
}
-}
\ No newline at end of file
+}
diff --git a/src/pages/MomentsWindow.tsx b/src/pages/MomentsWindow.tsx
index 38dcf05..d1bc10c 100644
--- a/src/pages/MomentsWindow.tsx
+++ b/src/pages/MomentsWindow.tsx
@@ -1535,9 +1535,10 @@ document.querySelectorAll('.vi video').forEach(function(v) {
-