diff --git a/src/renderer/src/windows/dataRefactorTest/README.md b/src/renderer/src/windows/dataRefactorTest/README.md index 798c72f0c..79536c6eb 100644 --- a/src/renderer/src/windows/dataRefactorTest/README.md +++ b/src/renderer/src/windows/dataRefactorTest/README.md @@ -8,6 +8,9 @@ - 专用的测试窗口 (DataRefactorTestWindow) - **双窗口启动**:应用启动时会同时打开主窗口和两个测试窗口 - **跨窗口同步测试**:两个测试窗口可以相互验证偏好设置的实时同步 +- **实时UI联动**:主题、语言、缩放等偏好设置变化会立即反映在UI上 +- **🎛️ Slider联动测试**:多个交互式滑块控制数值类型偏好设置,支持跨窗口实时同步 +- **多源窗口编号**:支持URL参数、窗口标题、窗口名称等多种方式确定窗口编号 - 完整的测试界面,包含4个专业测试组件 - 自动窗口定位,避免重叠 - 窗口编号标识,便于区分 @@ -25,12 +28,16 @@ - `app.theme.mode` - 主题模式 - `app.language` - 语言设置 - `app.spell_check.enabled` - 拼写检查 - - `app.zoom_factor` - 缩放因子 + - `app.zoom_factor` - 缩放因子 (🎛️ 支持Slider) + - `chat.message.font_size` - 消息字体大小 (🎛️ 支持Slider) + - `feature.selection.action_window_opacity` - 选择窗口透明度 (🎛️ 支持Slider) - 实时值更新和类型转换 +- **Slider联动控制**:数值类型偏好设置提供交互式滑块,支持实时拖拽调整 ### 3. usePreferences 批量操作测试 - 测试多个偏好设置的批量管理 -- 4种预设场景:基础设置、UI设置、用户设置、自定义组合 +- 5种预设场景:基础设置、UI设置、用户设置、🎛️数值设置、自定义组合 +- **🎛️ 数值设置场景**:专门的Slider联动控制区域,包含缩放、字体、选择窗口透明度三个滑块 - 批量更新功能,支持JSON格式输入 - 快速切换操作 @@ -106,13 +113,16 @@ src/renderer/src/windows/dataRefactorTest/ 1. **启动应用** - 自动打开2个测试窗口 2. **选择测试** - 在"usePreference Hook 测试"中选择要测试的偏好设置键 -3. **跨窗口验证** - 在窗口#1中修改值,观察窗口#2是否同步 -4. **高级测试** - 使用"Hook 高级功能测试"验证订阅和缓存机制 -5. **批量测试** - 使用"usePreferences 批量操作测试"进行多项设置测试 +3. **🎛️ Slider联动测试** - 选择数值类型偏好设置,拖动Slider观察实时变化 +4. **跨窗口验证** - 在窗口#1中修改值,观察窗口#2是否同步 +5. **批量Slider测试** - 切换到"数值设置场景",同时拖动多个滑块测试批量同步 +6. **高级测试** - 使用"Hook 高级功能测试"验证订阅和缓存机制 ## 🔧 技术实现 - **窗口管理**: DataRefactorMigrateService 单例管理多个测试窗口 - **数据同步**: 基于真实的 PreferenceService 和 IPC 通信 +- **实时主题**: 使用 useSyncExternalStore 实现主题、缩放等设置的实时UI响应 +- **跨窗口识别**: 多源窗口编号支持,确保每个窗口都有唯一标识 - **UI框架**: Ant Design + styled-components + React 18 - **类型安全**: 完整的 TypeScript 类型检查和偏好设置键约束 \ No newline at end of file diff --git a/src/renderer/src/windows/dataRefactorTest/TestApp.tsx b/src/renderer/src/windows/dataRefactorTest/TestApp.tsx index 8e41e86bf..5c7488aca 100644 --- a/src/renderer/src/windows/dataRefactorTest/TestApp.tsx +++ b/src/renderer/src/windows/dataRefactorTest/TestApp.tsx @@ -1,4 +1,5 @@ import { AppLogo } from '@renderer/config/env' +import { usePreference } from '@renderer/data/hooks/usePreference' import { loggerService } from '@renderer/services/LoggerService' import { Button, Card, Col, Divider, Layout, Row, Space, Typography } from 'antd' import { Database, FlaskConical, Settings, TestTube } from 'lucide-react' @@ -16,47 +17,91 @@ const { Title, Text } = Typography const logger = loggerService.withContext('TestApp') const TestApp: React.FC = () => { - // Get window title to determine window number - const windowTitle = document.title - const windowMatch = windowTitle.match(/#(\d+)/) - const windowNumber = windowMatch ? windowMatch[1] : '1' + // Get window number from multiple sources + const getWindowNumber = () => { + // Try URL search params first + const urlParams = new URLSearchParams(window.location.search) + const windowParam = urlParams.get('window') + if (windowParam) { + return windowParam + } + + // Try document title + const windowTitle = document.title + const windowMatch = windowTitle.match(/#(\d+)/) + if (windowMatch) { + return windowMatch[1] + } + + // Try window name + if (window.name && window.name.includes('#')) { + const nameMatch = window.name.match(/#(\d+)/) + if (nameMatch) { + return nameMatch[1] + } + } + + // Fallback: generate based on window creation time + return Math.floor(Date.now() / 1000) % 100 + } + + const windowNumber = getWindowNumber() + + // Add theme preference monitoring for UI changes + const [theme, setTheme] = usePreference('app.theme.mode') + const [language] = usePreference('app.language') + const [zoomFactor] = usePreference('app.zoom_factor') + + // Apply theme-based styling + const isDarkTheme = theme === 'ThemeMode.dark' + const headerBg = isDarkTheme ? '#141414' : '#fff' + const borderColor = isDarkTheme ? '#303030' : '#f0f0f0' + const textColor = isDarkTheme ? '#fff' : '#000' + + // Apply zoom factor + const zoomValue = typeof zoomFactor === 'number' ? zoomFactor : 1.0 return ( - -
+ +
Logo - - Test Window #{windowNumber} + <Title level={4} style={{ margin: 0, color: textColor }}> + Test Window #{windowNumber} {isDarkTheme ? '🌙' : '☀️'} - - Cross-Window Sync Testing + + + Cross-Window Sync Testing | {language || 'en-US'} | Zoom: {Math.round(zoomValue * 100)}% +
- + {/* Introduction Card */} - + - + <Title level={3} style={{ margin: 0, color: textColor }}> PreferenceService 功能测试 (窗口 #{windowNumber}) - + 此测试窗口用于验证 PreferenceService 和 usePreference hooks 的各项功能,包括单个偏好设置的读写、多个偏好设置的批量操作、跨窗口数据同步等。 - 测试使用的都是真实的偏好设置系统,所有操作都会影响实际的数据库存储。 - + + 测试使用的都是真实的偏好设置系统,所有操作都会影响实际的数据库存储。 + + 📋 跨窗口测试指南:在一个窗口中修改偏好设置,观察其他窗口是否实时同步更新。 @@ -68,11 +113,12 @@ const TestApp: React.FC = () => { - - PreferenceService 基础测试 + + PreferenceService 基础测试 } - size="small"> + size="small" + style={{ backgroundColor: isDarkTheme ? '#1f1f1f' : '#fff', borderColor: borderColor }}> @@ -82,11 +128,12 @@ const TestApp: React.FC = () => { - - usePreference Hook 测试 + + usePreference Hook 测试 } - size="small"> + size="small" + style={{ backgroundColor: isDarkTheme ? '#1f1f1f' : '#fff', borderColor: borderColor }}> @@ -96,11 +143,12 @@ const TestApp: React.FC = () => { - - Hook 高级功能测试 + + Hook 高级功能测试 } - size="small"> + size="small" + style={{ backgroundColor: isDarkTheme ? '#1f1f1f' : '#fff', borderColor: borderColor }}> @@ -110,11 +158,12 @@ const TestApp: React.FC = () => { - - usePreferences 批量操作测试 + + usePreferences 批量操作测试 } - size="small"> + size="small" + style={{ backgroundColor: isDarkTheme ? '#1f1f1f' : '#fff', borderColor: borderColor }}> @@ -123,14 +172,28 @@ const TestApp: React.FC = () => { - + + + + diff --git a/src/renderer/src/windows/dataRefactorTest/components/PreferenceBasicTests.tsx b/src/renderer/src/windows/dataRefactorTest/components/PreferenceBasicTests.tsx index b302e787e..69e6be011 100644 --- a/src/renderer/src/windows/dataRefactorTest/components/PreferenceBasicTests.tsx +++ b/src/renderer/src/windows/dataRefactorTest/components/PreferenceBasicTests.tsx @@ -1,6 +1,6 @@ import { usePreference } from '@renderer/data/hooks/usePreference' import type { PreferenceKeyType } from '@shared/data/types' -import { Button, Input, message, Select, Space, Switch, Typography } from 'antd' +import { Button, Input, message, Select, Slider, Space, Switch, Typography } from 'antd' import React, { useState } from 'react' import styled from 'styled-components' @@ -19,6 +19,10 @@ const PreferenceBasicTests: React.FC = () => { const [inputValue, setInputValue] = useState('') + // Add theme monitoring for visual changes + const [currentTheme] = usePreference('app.theme.mode') + const isDarkTheme = currentTheme === 'ThemeMode.dark' + const handleSetValue = async () => { try { let parsedValue: any = inputValue @@ -47,15 +51,33 @@ const PreferenceBasicTests: React.FC = () => { } const testCases = [ - { key: 'app.theme.mode', label: 'App Theme Mode', sampleValue: 'ThemeMode.dark' }, - { key: 'app.language', label: 'App Language', sampleValue: 'zh-CN' }, - { key: 'app.spell_check.enabled', label: 'Spell Check', sampleValue: 'true' }, - { key: 'app.zoom_factor', label: 'Zoom Factor', sampleValue: '1.2' }, - { key: 'app.tray.enabled', label: 'Tray Enabled', sampleValue: 'true' } + { key: 'app.theme.mode', label: 'App Theme Mode', sampleValue: 'ThemeMode.dark', type: 'enum' }, + { key: 'app.language', label: 'App Language', sampleValue: 'zh-CN', type: 'enum' }, + { key: 'app.spell_check.enabled', label: 'Spell Check', sampleValue: 'true', type: 'boolean' }, + { key: 'app.zoom_factor', label: 'Zoom Factor', sampleValue: '1.2', type: 'number', min: 0.5, max: 2.0, step: 0.1 }, + { key: 'app.tray.enabled', label: 'Tray Enabled', sampleValue: 'true', type: 'boolean' }, + { + key: 'chat.message.font_size', + label: 'Message Font Size', + sampleValue: '14', + type: 'number', + min: 8, + max: 72, + step: 1 + }, + { + key: 'feature.selection.action_window_opacity', + label: 'Selection Window Opacity', + sampleValue: '95', + type: 'number', + min: 10, + max: 100, + step: 5 + } ] return ( - + {/* Key Selection */}
@@ -74,7 +96,7 @@ const PreferenceBasicTests: React.FC = () => {
{/* Current Value Display */} - + 当前值: {value !== undefined ? ( @@ -109,12 +131,14 @@ const PreferenceBasicTests: React.FC = () => {
快速操作: - {/* Theme Toggle */} + {/* Theme Toggle with Visual Feedback */} {selectedKey === 'app.theme.mode' && ( )} @@ -140,20 +164,134 @@ const PreferenceBasicTests: React.FC = () => { )} - {/* Zoom Factor */} - {selectedKey === 'app.zoom_factor' && ( - <> - - - - - )} + {/* Number Type Sliders */} + {(() => { + const currentTestCase = testCases.find((tc) => tc.key === selectedKey) + if (currentTestCase?.type === 'number') { + const numValue = + typeof value === 'number' + ? value + : typeof value === 'string' + ? parseFloat(value) + : currentTestCase.min || 0 + const min = currentTestCase.min || 0 + const max = currentTestCase.max || 100 + const step = currentTestCase.step || 1 + + const getDisplayValue = () => { + if (selectedKey === 'app.zoom_factor') { + return `${Math.round(numValue * 100)}%` + } else if (selectedKey === 'feature.selection.action_window_opacity') { + return `${Math.round(numValue)}%` + } else { + return numValue.toString() + } + } + + const getMarks = () => { + if (selectedKey === 'app.zoom_factor') { + return { + 0.5: '50%', + 0.8: '80%', + 1.0: '100%', + 1.2: '120%', + 1.5: '150%', + 2.0: '200%' + } + } else if (selectedKey === 'chat.message.font_size') { + return { + 8: '8px', + 12: '12px', + 14: '14px', + 16: '16px', + 18: '18px', + 24: '24px', + 36: '36px', + 72: '72px' + } + } else if (selectedKey === 'feature.selection.action_window_opacity') { + return { + 10: '10%', + 30: '30%', + 50: '50%', + 70: '70%', + 90: '90%', + 100: '100%' + } + } + return {} + } + + return ( +
+ + + {currentTestCase.label}: {getDisplayValue()} + + setValue(val)} + marks={getMarks()} + tooltip={{ + formatter: (val) => { + if (selectedKey === 'app.zoom_factor') { + return `${Math.round((val || 0) * 100)}%` + } else if (selectedKey === 'feature.selection.action_window_opacity') { + return `${Math.round(val || 0)}%` + } + return val?.toString() || '0' + } + }} + style={{ width: '100%', marginBottom: 8 }} + /> + {selectedKey === 'app.zoom_factor' && ( + + + + + + )} + {selectedKey === 'chat.message.font_size' && ( + + + + + + )} + {selectedKey === 'feature.selection.action_window_opacity' && ( + + + + + + )} + +
+ ) + } + return null + })()} {/* Sample Values */}