From df876651b9f145676f69c256fc18d454c932beae Mon Sep 17 00:00:00 2001 From: fullex <0xfullex@gmail.com> Date: Tue, 12 Aug 2025 23:42:10 +0800 Subject: [PATCH] feat(preferences): enhance testing framework with real-time UI synchronization and slider controls This commit updates the dataRefactorTest README to include new features such as real-time UI linkage for theme, language, and zoom preferences, as well as interactive slider controls for various settings. The TestApp component is modified to support dynamic window identification and theme monitoring. Additionally, the PreferenceBasicTests, PreferenceMultipleTests, and PreferenceServiceTests components are enhanced with slider functionality for numerical preferences, improving the testing capabilities for cross-window synchronization and user experience. --- .../src/windows/dataRefactorTest/README.md | 20 +- .../src/windows/dataRefactorTest/TestApp.tsx | 135 ++++++++--- .../components/PreferenceBasicTests.tsx | 210 +++++++++++++++--- .../components/PreferenceHookTests.tsx | 22 +- .../components/PreferenceMultipleTests.tsx | 96 +++++++- .../components/PreferenceServiceTests.tsx | 44 ++-- 6 files changed, 431 insertions(+), 96 deletions(-) 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 */}