From 927eaa1e03d3daf60be407fea68c9b82f22489b3 Mon Sep 17 00:00:00 2001 From: shenjianZ Date: Tue, 10 Feb 2026 18:46:11 +0800 Subject: [PATCH] =?UTF-8?q?=20=20feat:=20=E5=AE=9E=E7=8E=B0=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E9=9D=A2=E6=9D=BF=E3=80=81=E9=A2=9C=E8=89=B2=E5=8F=96?= =?UTF-8?q?=E8=89=B2=E3=80=81JSON=E6=A0=BC=E5=BC=8F=E5=8C=96=E5=92=8C?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E4=BF=A1=E6=81=AF=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构项目架构,采用四层架构模式 (Command → Service → Platform → Utils) - 实现命令面板功能,支持快捷搜索和特征分类 - 添加颜色取色功能,支持屏幕像素颜色获取 - 添加JSON格式化功能,支持JSON格式化和压缩 - 添加系统信息功能,显示操作系统和硬件信息 - 移除旧的状态文档和无用配置文件 --- .claude/settings.local.json | 9 + .gemini/setttings.json | 5 - .vscode/extensions.json | 3 - STATUS.md | 101 - docs/开发指南.md | 1182 ++++++++++ docs/快速参考.md | 207 ++ package.json | 2 + picker.html | 257 ++ src-tauri/Cargo.lock | 2064 ++++++++++------- src-tauri/Cargo.toml | 24 +- src-tauri/capabilities/default.json | 7 +- .../src/commands/json_format_commands.rs | 110 + src-tauri/src/commands/mod.rs | 8 + .../src/commands/picker_color_commands.rs | 311 +++ .../src/commands/system_info_commands.rs | 29 + src-tauri/src/commands/window_commands.rs | 70 + src-tauri/src/error.rs | 76 + src-tauri/src/lib.rs | 54 +- src-tauri/src/models/color.rs | 91 + src-tauri/src/models/json_format.rs | 85 + src-tauri/src/models/mod.rs | 7 + src-tauri/src/models/system_info.rs | 170 ++ src-tauri/src/platforms/mod.rs | 9 + src-tauri/src/platforms/system_info.rs | 22 + src-tauri/src/platforms/windows/mod.rs | 3 + .../src/platforms/windows/system_info_impl.rs | 509 ++++ src-tauri/src/services/json_format_service.rs | 200 ++ src-tauri/src/services/mod.rs | 7 + src-tauri/src/services/system_info_service.rs | 38 + src-tauri/src/services/window_service.rs | 81 + src-tauri/src/utils/color_conversion.rs | 93 + src-tauri/src/utils/json_formatter.rs | 290 +++ src-tauri/src/utils/mod.rs | 8 + src-tauri/src/utils/screen.rs | 177 ++ src-tauri/src/utils/shortcut.rs | 54 + src-tauri/tauri.conf.json | 20 +- src/App.tsx | 42 +- .../command-palette/CommandPalette.tsx | 32 + src/components/command-palette/ResultItem.tsx | 45 + src/components/command-palette/ResultList.tsx | 31 + .../command-palette/SearchInput.tsx | 39 + .../features/ColorPicker/ColorPickerPage.tsx | 237 ++ .../JsonFormatter/JsonFormatterPage.tsx | 402 ++++ .../features/SystemInfo/SystemInfoPage.tsx | 581 +++++ src/components/home/CategoryFilter.tsx | 32 + src/components/home/FeatureCard.tsx | 55 + src/components/home/FeatureGrid.tsx | 30 + src/components/layout/MainLayout.tsx | 27 - src/components/layout/Sidebar.tsx | 108 - src/components/layout/TopBar.tsx | 84 - src/components/search/SearchResult.tsx | 78 + src/features/data.ts | 201 ++ src/features/registry.ts | 11 + src/features/types.ts | 28 + src/main.tsx | 5 +- src/pages/Components.tsx | 342 --- src/pages/Dashboard.tsx | 212 -- src/pages/Features.tsx | 202 -- src/pages/Home.tsx | 105 + src/pages/Search.tsx | 156 ++ src/pages/Settings.tsx | 16 + vite.config.ts | 10 + 62 files changed, 7536 insertions(+), 1958 deletions(-) create mode 100644 .claude/settings.local.json delete mode 100644 .gemini/setttings.json delete mode 100644 .vscode/extensions.json delete mode 100644 STATUS.md create mode 100644 docs/开发指南.md create mode 100644 docs/快速参考.md create mode 100644 picker.html create mode 100644 src-tauri/src/commands/json_format_commands.rs create mode 100644 src-tauri/src/commands/mod.rs create mode 100644 src-tauri/src/commands/picker_color_commands.rs create mode 100644 src-tauri/src/commands/system_info_commands.rs create mode 100644 src-tauri/src/commands/window_commands.rs create mode 100644 src-tauri/src/error.rs create mode 100644 src-tauri/src/models/color.rs create mode 100644 src-tauri/src/models/json_format.rs create mode 100644 src-tauri/src/models/mod.rs create mode 100644 src-tauri/src/models/system_info.rs create mode 100644 src-tauri/src/platforms/mod.rs create mode 100644 src-tauri/src/platforms/system_info.rs create mode 100644 src-tauri/src/platforms/windows/mod.rs create mode 100644 src-tauri/src/platforms/windows/system_info_impl.rs create mode 100644 src-tauri/src/services/json_format_service.rs create mode 100644 src-tauri/src/services/mod.rs create mode 100644 src-tauri/src/services/system_info_service.rs create mode 100644 src-tauri/src/services/window_service.rs create mode 100644 src-tauri/src/utils/color_conversion.rs create mode 100644 src-tauri/src/utils/json_formatter.rs create mode 100644 src-tauri/src/utils/mod.rs create mode 100644 src-tauri/src/utils/screen.rs create mode 100644 src-tauri/src/utils/shortcut.rs create mode 100644 src/components/command-palette/CommandPalette.tsx create mode 100644 src/components/command-palette/ResultItem.tsx create mode 100644 src/components/command-palette/ResultList.tsx create mode 100644 src/components/command-palette/SearchInput.tsx create mode 100644 src/components/features/ColorPicker/ColorPickerPage.tsx create mode 100644 src/components/features/JsonFormatter/JsonFormatterPage.tsx create mode 100644 src/components/features/SystemInfo/SystemInfoPage.tsx create mode 100644 src/components/home/CategoryFilter.tsx create mode 100644 src/components/home/FeatureCard.tsx create mode 100644 src/components/home/FeatureGrid.tsx delete mode 100644 src/components/layout/MainLayout.tsx delete mode 100644 src/components/layout/Sidebar.tsx delete mode 100644 src/components/layout/TopBar.tsx create mode 100644 src/components/search/SearchResult.tsx create mode 100644 src/features/data.ts create mode 100644 src/features/registry.ts create mode 100644 src/features/types.ts delete mode 100644 src/pages/Components.tsx delete mode 100644 src/pages/Dashboard.tsx delete mode 100644 src/pages/Features.tsx create mode 100644 src/pages/Home.tsx create mode 100644 src/pages/Search.tsx create mode 100644 src/pages/Settings.tsx diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..4db0100 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(cargo check:*)", + "Bash(pnpm build:*)", + "Bash(tree:*)" + ] + } +} diff --git a/.gemini/setttings.json b/.gemini/setttings.json deleted file mode 100644 index 8251a1f..0000000 --- a/.gemini/setttings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "mcpServers": {}, - "DEBUG": true, - "DEBUG_MODE": "true" -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 24d7cc6..0000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"] -} diff --git a/STATUS.md b/STATUS.md deleted file mode 100644 index 588b34a..0000000 --- a/STATUS.md +++ /dev/null @@ -1,101 +0,0 @@ -# 📊 项目状态 - -## 🎯 项目完成度 - -| 功能模块 | 状态 | 说明 | -|---------|------|------| -| **前端框架** | ✅ 完成 | React 19 + TypeScript | -| **UI 组件库** | ✅ 完成 | shadcn/ui + Tailwind CSS | -| **桌面应用** | ✅ 完成 | Tauri 2.0 集成 | -| **开发工具** | ✅ 完成 | ESLint + 热重载 | -| **构建系统** | ✅ 完成 | Vite + Rust | -| **文档** | ✅ 完成 | 完整的使用指南 | -| **CI/CD** | ✅ 完成 | GitHub Actions | - -## 🚀 技术栈状态 - -### 前端技术 -- **React 19** - 最新版本,支持现代特性 -- **TypeScript 5.8+** - 完整类型支持 -- **Tailwind CSS 4.0** - 最新版本,支持 CSS 变量 -- **shadcn/ui** - 生产就绪的组件库 - -### 桌面应用 -- **Tauri 2.0** - 跨平台桌面应用框架 -- **Rust** - 高性能后端 -- **多平台支持** - Windows, macOS, Linux, Android - -### 开发工具 -- **Vite** - 快速构建工具 -- **ESLint** - 代码质量检查 -- **pnpm** - 快速包管理器 - -## 📈 性能指标 - -| 指标 | 数值 | 说明 | -|------|------|------| -| **启动时间** | < 2s | 冷启动时间 | -| **构建时间** | < 30s | 完整构建时间 | -| **包大小** | < 50MB | 最终应用大小 | -| **内存占用** | < 100MB | 运行时内存使用 | - -## 🔧 配置完整性 - -### TypeScript 配置 -- ✅ 严格模式启用 -- ✅ 路径别名配置 -- ✅ 类型检查完整 - -### Tailwind CSS 配置 -- ✅ 主题系统 -- ✅ 响应式设计 -- ✅ 深色模式支持 - -### Tauri 配置 -- ✅ 多平台构建 -- ✅ 权限管理 -- ✅ 应用图标 - -## 📱 平台兼容性 - -| 平台 | 状态 | 说明 | -|------|------|------| -| **Windows** | ✅ 完全支持 | x64, x86, ARM64 | -| **macOS** | ✅ 完全支持 | Intel, Apple Silicon | -| **Linux** | ✅ 完全支持 | AppImage, Snap, Flatpak | -| **Android** | 🔄 实验性 | 基础功能支持 | - -## 🎨 UI/UX 特性 - -- ✅ 响应式设计 -- ✅ 深色/浅色主题 -- ✅ 无障碍访问 -- ✅ 现代设计语言 -- ✅ 组件动画 -- ✅ 交互反馈 - -## 🔒 安全性 - -- ✅ 类型安全 -- ✅ 代码质量检查 -- ✅ 依赖安全扫描 -- ✅ 权限最小化 - -## 📚 文档完整性 - -- ✅ 快速开始指南 -- ✅ API 文档 -- ✅ 组件使用示例 -- ✅ 部署指南 -- ✅ 故障排除 - -## 🚀 部署就绪 - -- ✅ 生产构建配置 -- ✅ 应用签名支持 -- ✅ 自动更新机制 -- ✅ 错误监控 - ---- - -**🎉 这是一个生产就绪的模板项目,可以直接用于构建企业级桌面应用!** diff --git a/docs/开发指南.md b/docs/开发指南.md new file mode 100644 index 0000000..2352103 --- /dev/null +++ b/docs/开发指南.md @@ -0,0 +1,1182 @@ +# Tauri 应用开发指南 + +## 目录 + +- [架构概述](#架构概述) +- [添加新功能的完整流程](#添加新功能的完整流程) +- [各层开发规范](#各层开发规范) +- [代码示例](#代码示例) +- [最佳实践](#最佳实践) +- [测试规范](#测试规范) + +--- + +## 架构概述 + +### 四层架构 + +``` +┌─────────────────────────────────────────────────┐ +│ Command 层 │ +│ 职责:参数验证、调用 Service、结果转换 │ +│ 文件:src/commands/*.rs │ +├─────────────────────────────────────────────────┤ +│ Service 层 │ +│ 职责:业务逻辑、流程编排、状态管理 │ +│ 文件:src/services/*.rs │ +├─────────────────────────────────────────────────┤ +│ Platform 层 │ +│ 职责:平台差异抽象、统一接口 │ +│ 文件:src/platforms/*.rs │ +├─────────────────────────────────────────────────┤ +│ Utils 层 │ +│ 职责:纯算法、无状态工具函数 │ +│ 文件:src/utils/*.rs │ +└─────────────────────────────────────────────────┘ +``` + +### 依赖原则 + +- **单向依赖**:上层依赖下层,下层不依赖上层 +- **依赖链**:Command → Service → Platform → Utils +- **Utils 完全独立**:只依赖标准库和 models +- **Platform 通过 trait 解耦**:Service 层通过 trait 调用,不关心具体实现 + +--- + +## 添加新功能的完整流程 + +### 步骤 1:需求分析 + +在开始编码前,明确以下内容: + +1. **功能描述**:这个功能做什么? +2. **用户交互**:用户如何触发这个功能? +3. **输入输出**:需要什么参数?返回什么结果? +4. **平台差异**:不同平台是否需要不同实现? +5. **错误场景**:哪些情况下会出错?如何处理? + +### 步骤 2:设计数据模型(如需要) + +如果新功能需要新的数据结构,在 `models/` 中定义: + +```rust +// src/models/new_feature.rs + +use serde::{Deserialize, Serialize}; + +/// 新功能的配置信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NewFeatureConfig { + /// 配置项 1 + pub option1: String, + /// 配置项 2 + pub option2: u32, +} + +/// 新功能的输出结果 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NewFeatureResult { + /// 状态码 + pub status: u16, + /// 结果数据 + pub data: String, +} +``` + +**规范**: +- ✅ 使用中文注释 +- ✅ 每个字段都要有注释说明含义 +- ✅ 实现 `Debug`, `Clone`, `Serialize`, `Deserialize` +- ✅ 结构体命名使用大驼峰(PascalCase) +- ✅ 字段命名使用下划线命名(snake_case) + +### 步骤 3:实现工具函数(如果需要纯算法) + +如果有纯算法逻辑,在 `utils/` 中实现: + +```rust +// src/utils/new_algorithm.rs + +/// 新算法函数 +/// +/// 对输入数据进行处理 +/// +/// # 参数 +/// +/// * `input` - 输入数据 +/// +/// # 返回 +/// +/// 返回处理后的结果 +/// +/// # 示例 +/// +/// ``` +/// use crate::utils::new_algorithm::process_data; +/// +/// let result = process_data(42); +/// assert_eq!(result, 84); +/// ``` +pub fn process_data(input: u32) -> u32 { + input * 2 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_process_data() { + assert_eq!(process_data(2), 4); + assert_eq!(process_data(0), 0); + } +} +``` + +**规范**: +- ✅ 纯函数,无副作用 +- ✅ 添加详细的文档注释 +- ✅ 包含单元测试 +- ✅ 不依赖任何外部状态 + +### 步骤 4:定义平台抽象(如果需要平台特定实现) + +如果功能需要访问平台 API,定义 trait: + +```rust +// src/platforms/new_feature.rs + +use crate::error::AppResult; + +/// 新功能平台抽象 trait +pub trait NewFeatureAccessor { + /// 执行平台特定的操作 + /// + /// # 参数 + /// + /// * `param1` - 参数 1 说明 + /// + /// # 返回 + /// + /// 返回操作结果 + /// + /// # 错误 + /// + /// 操作失败时返回错误 + fn execute_platform_operation(&self, param1: &str) -> AppResult; +} + +/// 平台特定实现类型别名 +#[cfg(windows)] +pub type PlatformNewFeature = crate::platforms::windows::new_feature_impl::WindowsNewFeature; + +#[cfg(not(windows))] +pub type PlatformNewFeature = crate::platforms::windows::new_feature_impl::DummyNewFeature; +``` + +**规范**: +- ✅ 使用 trait 定义接口 +- ✅ 所有方法返回 `AppResult` +- ✅ 方法参数使用引用(`&str` 而不是 `String`) +- ✅ 添加详细的文档注释 + +### 步骤 5:实现平台特定代码 + +```rust +// src/platforms/windows/new_feature_impl.rs + +use crate::error::{AppError, AppResult}; +use crate::platforms::new_feature::NewFeatureAccessor; + +/// Windows 平台实现 +#[cfg(windows)] +pub struct WindowsNewFeature; + +#[cfg(windows)] +impl NewFeatureAccessor for WindowsNewFeature { + fn execute_platform_operation(&self, param1: &str) -> AppResult { + // Windows 特定实现 + Ok(format!("Windows: {}", param1)) + } +} + +/// 其他平台占位实现 +#[cfg(not(windows))] +pub struct DummyNewFeature; + +#[cfg(not(windows))] +impl NewFeatureAccessor for DummyNewFeature { + fn execute_platform_operation(&self, _param1: &str) -> AppResult { + Err(AppError::PlatformNotSupported( + "此平台暂不支持该功能".to_string() + )) + } +} +``` + +**规范**: +- ✅ 使用 `#[cfg(windows)]` 条件编译 +- ✅ 提供其他平台的占位实现 +- ✅ 占位实现返回 `PlatformNotSupported` 错误 +- ✅ 使用中文错误消息 + +### 步骤 6:实现业务逻辑(Service 层) + +```rust +// src/services/new_feature_service.rs + +//! 新功能服务 +//! +//! 提供新功能的核心业务逻辑 + +use crate::error::AppResult; +use crate::models::new_feature::{NewFeatureConfig, NewFeatureResult}; + +/// 新功能服务 +pub struct NewFeatureService; + +impl NewFeatureService { + /// 执行新功能 + /// + /// 根据配置执行相应的操作 + /// + /// # 参数 + /// + /// * `config` - 功能配置 + /// + /// # 返回 + /// + /// 返回执行结果 + /// + /// # 错误 + /// + /// - 配置无效时返回 `AppError::InvalidColorData` + /// - 平台不支持时返回 `AppError::PlatformNotSupported` + pub fn execute(&self, config: &NewFeatureConfig) -> AppResult { + // 1. 参数验证 + if config.option1.is_empty() { + return Err(AppError::InvalidColorData( + "配置项 option1 不能为空".to_string() + )); + } + + // 2. 业务逻辑 + let result = format!("处理: {} - {}", config.option1, config.option2); + + // 3. 返回结果 + Ok(NewFeatureResult { + status: 200, + data: result, + }) + } + + /// 执行新功能(异步版本) + /// + /// 异步执行新功能 + pub async fn execute_async(&self, config: NewFeatureConfig) -> AppResult { + // 使用 tokio 或其他异步运行时 + Ok(self.execute(&config)?) + } +} +``` + +**规范**: +- ✅ 使用 struct 命名空间(如 `NewFeatureService`) +- ✅ 所有方法返回 `AppResult` +- ✅ 参数验证放在 Service 层 +- ✅ 添加详细的文档注释 +- ✅ 包含同步和异步两个版本(如需要) + +### 步骤 7:创建 Tauri 命令(Command 层) + +```rust +// src/commands/new_feature_commands.rs + +//! 新功能命令 +//! +//! 定义新功能相关的 Tauri 命令 + +use crate::models::new_feature::{NewFeatureConfig, NewFeatureResult}; +use crate::services::new_feature_service::NewFeatureService; + +/// 执行新功能命令 +/// +/// Tauri 命令,用于从前端调用新功能 +/// +/// # 参数 +/// +/// * `config` - 功能配置 +/// +/// # 返回 +/// +/// 返回执行结果,包含状态码和数据 +/// +/// # 前端调用示例 +/// +/// ```typescript +/// import { invoke } from '@tauri-apps/api/tauri'; +/// +/// const result = await invoke('execute_new_feature', { +/// config: { +/// option1: 'test', +/// option2: 100 +/// } +/// }); +/// console.log(result.status); // 200 +/// console.log(result.data); // "处理: test - 100" +/// ``` +#[tauri::command] +pub fn execute_new_feature(config: NewFeatureConfig) -> Result { + // 创建服务实例 + let service = NewFeatureService; + + // 调用服务层 + service + .execute(&config) + .map_err(|e| e.to_string()) +} + +/// 异步执行新功能命令 +/// +/// 异步版本的执行命令 +/// +/// # 参数 +/// +/// * `config` - 功能配置 +/// +/// # 返回 +/// +/// 返回执行结果 +#[tauri::command] +pub async fn execute_new_feature_async(config: NewFeatureConfig) -> Result { + let service = NewFeatureService; + service + .execute_async(config) + .await + .map_err(|e| e.to_string()) +} +``` + +**规范**: +- ✅ 使用 `#[tauri::command]` 宏 +- ✅ 返回类型为 `Result` +- ✅ 使用 `.map_err(|e| e.to_string())` 转换错误 +- ✅ 包含前端调用示例 +- ✅ 参数使用结构体,便于扩展 +- ✅ 添加详细的文档注释 + +### 步骤 8:注册模块 + +更新各模块的 `mod.rs` 和 `lib.rs`: + +```rust +// src/models/mod.rs +pub mod color; +pub mod new_feature; // 新增 + +pub use color::{ColorInfo, RgbInfo, HslInfo}; +pub use new_feature::{NewFeatureConfig, NewFeatureResult}; // 新增 + +// src/commands/mod.rs +pub mod color_commands; +pub mod window_commands; +pub mod new_feature_commands; // 新增 + +// src/lib.rs +.invoke_handler(tauri::generate_handler![ + // ... 其他命令 + commands::new_feature_commands::execute_new_feature, + commands::new_feature_commands::execute_new_feature_async, +]) +``` + +**规范**: +- ✅ 按字母顺序排列 +- ✅ 导出常用类型 +- ✅ 注册所有新命令 + +### 步骤 9:错误处理扩展(如需要) + +如果需要新的错误类型,在 `error.rs` 中添加: + +```rust +// src/error.rs + +#[derive(Debug)] +pub enum AppError { + // ... 现有错误类型 + + /// 新功能相关错误 + NewFeatureFailed(String), +} + +impl fmt::Display for AppError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + // ... 现有错误 + AppError::NewFeatureFailed(msg) => write!(f, "新功能执行失败: {}", msg), + } + } +} +``` + +**规范**: +- ✅ 错误类型使用中文描述 +- ✅ 携带详细的错误信息(`String`) +- ✅ 在 `Display` 实现中添加上下文 + +### 步骤 10:更新前端类型定义 + +在 `src/types/` 或相关位置添加 TypeScript 类型定义: + +```typescript +// src/types/new-feature.ts + +/** + * 新功能配置 + */ +export interface NewFeatureConfig { + /** 配置项 1 */ + option1: string; + /** 配置项 2 */ + option2: number; +} + +/** + * 新功能结果 + */ +export interface NewFeatureResult { + /** 状态码 */ + status: number; + /** 结果数据 */ + data: string; +} + +// 导出 Tauri 命令类型 +export type NewFeatureCommands = { + execute_new_feature: (config: NewFeatureConfig) => Promise; + execute_new_feature_async: (config: NewFeatureConfig) => Promise; +}; +``` + +### 步骤 11:编写测试 + +```rust +// src/services/new_feature_service.rs 中的测试 + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_execute_with_valid_config() { + let service = NewFeatureService; + let config = NewFeatureConfig { + option1: "test".to_string(), + option2: 100, + }; + + let result = service.execute(&config).unwrap(); + assert_eq!(result.status, 200); + } + + #[test] + fn test_execute_with_empty_option1() { + let service = NewFeatureService; + let config = NewFeatureConfig { + option1: "".to_string(), + option2: 100, + }; + + assert!(service.execute(&config).is_err()); + } +} +``` + +**规范**: +- ✅ 测试函数命名使用 `test_` 前缀 +- ✅ 测试正常场景和错误场景 +- ✅ 使用 `assert!` 和 `assert_eq!` 断言 +- ✅ 运行 `cargo test` 验证 + +### 步骤 12:验证和测试 + +```bash +# 1. 检查代码编译 +cargo check + +# 2. 运行测试 +cargo test + +# 3. 构建应用 +pnpm build + +# 4. 运行应用 +pnpm tauri dev + +# 5. 测试功能 +# - 在前端界面测试新功能 +# - 检查控制台输出 +# - 验证错误处理 +``` + +--- + +## 各层开发规范 + +### Models 层规范 + +**职责**:定义数据结构,不包含业务逻辑 + +**规范清单**: +- ✅ 使用 `serde` 的 `Serialize` 和 `Deserialize` +- ✅ 实现 `Debug` 和 `Clone` +- ✅ 所有字段必须有文档注释 +- ✅ 使用 `new()` 方法构造复杂对象 +- ✅ 字段类型优先使用基本类型 +- ✅ 集中定义在一个模块中 + +**示例**: +```rust +/// 用户信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UserInfo { + /// 用户 ID + pub id: u32, + /// 用户名 + pub name: String, +} + +impl UserInfo { + /// 创建新用户 + pub fn new(id: u32, name: String) -> Self { + Self { id, name } + } +} +``` + +### Utils 层规范 + +**职责**:提供纯函数算法,无副作用 + +**规范清单**: +- ✅ 纯函数,不依赖外部状态 +- ✅ 不使用 `unsafe` 代码 +- ✅ 不执行 I/O 操作 +- ✅ 包含单元测试 +- ✅ 使用清晰的函数命名 + +**示例**: +```rust +/// 计算两点之间的距离 +pub fn calculate_distance(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 { + let dx = x2 - x1; + let dy = y2 - y1; + (dx * dx + dy * dy).sqrt() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_calculate_distance() { + let dist = calculate_distance(0.0, 0.0, 3.0, 4.0); + assert_eq!(dist, 5.0); + } +} +``` + +### Platform 层规范 + +**职责**:抽象平台差异,提供统一接口 + +**规范清单**: +- ✅ 使用 trait 定义接口 +- ✅ 通过类型别名选择实现 +- ✅ 提供所有平台的占位实现 +- ✅ 使用 `#[cfg(windows)]` 条件编译 +- ✅ 占位实现返回 `PlatformNotSupported` + +**示例**: +```rust +// trait 定义 +pub trait FileAccessor { + fn read_file(&self, path: &str) -> AppResult; +} + +// Windows 实现 +#[cfg(windows)] +pub type PlatformFile = WindowsFile; + +// 占位实现 +#[cfg(not(windows))] +pub type PlatformFile = DummyFile; +``` + +### Service 层规范 + +**职责**:业务逻辑实现和流程编排 + +**规范清单**: +- ✅ 使用 struct 命名空间 +- ✅ 所有方法返回 `AppResult` +- ✅ 参数验证在 Service 层进行 +- ✅ 通过 trait 调用 Platform 层 +- ✅ 可以调用 Utils 层函数 +- ✅ 提供同步和异步版本(如需要) +- ✅ 使用详细的中文档注释 + +**示例**: +```rust +pub struct FileService; + +impl FileService { + pub fn read_and_process(&self, path: &str) -> AppResult { + // 1. 参数验证 + if path.is_empty() { + return Err(AppError::InvalidData("路径不能为空".to_string())); + } + + // 2. 调用 Platform 层 + let content = PlatformFile::read_file(path)?; + + // 3. 调用 Utils 层处理 + let processed = utils::text::trim_whitespace(&content); + + Ok(processed.to_string()) + } +} +``` + +### Command 层规范 + +**职责**:Tauri 命令处理,连接前端和 Service + +**规范清单**: +- ✅ 使用 `#[tauri::command]` 宏 +- ✅ 返回 `Result` +- ✅ 参数使用结构体(便于扩展) +- ✅ 简洁的适配器代码 +- ✅ 包含前端调用示例 +- ✅ 错误转换为字符串 + +**示例**: +```rust +#[tauri::command] +pub fn process_file(path: String) -> Result { + let service = FileService; + service + .read_and_process(&path) + .map_err(|e| e.to_string()) +} +``` + +--- + +## 代码示例 + +### 完整示例:添加一个屏幕截图功能 + +#### 1. 定义数据模型 + +```rust +// src/models/screenshot.rs + +use serde::{Deserialize, Serialize}; + +/// 截图配置 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ScreenshotConfig { + /// 是否包含光标 + pub include_cursor: bool, + /// JPEG 质量 (1-100) + pub quality: u8, +} + +/// 截图结果 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ScreenshotResult { + /// 图片数据(Base64) + pub data: String, + /// 图片格式 + pub format: String, + /// 图片宽度 + pub width: u32, + /// 图片高度 + pub height: u32, +} +``` + +#### 2. 定义平台抽象 + +```rust +// src/platforms/screenshot.rs + +use crate::error::AppResult; +use crate::models::screenshot::ScreenshotConfig; + +/// 屏幕截图 trait +pub trait ScreenshotCapturer { + /// 截取整个屏幕 + /// + /// # 参数 + /// + /// * `config` - 截图配置 + /// + /// # 返回 + /// + /// 返回截图结果,包含 Base64 编码的图片数据 + fn capture_screen(&self, config: &ScreenshotConfig) -> AppResult; + + /// 截取指定区域 + /// + /// # 参数 + /// + /// * `x` - 起始 X 坐标 + /// * `y` - 起始 Y 坐标 + /// * `width` - 宽度 + /// * `height` - 高度 + /// * `config` - 截图配置 + fn capture_region( + &self, + x: u32, + y: u32, + width: u32, + height: u32, + config: &ScreenshotConfig, + ) -> AppResult; +} + +// 类型别名 +#[cfg(windows)] +pub type PlatformScreenshot = crate::platforms::windows::screenshot_impl::WindowsScreenshot; + +#[cfg(not(windows))] +pub type PlatformScreenshot = crate::platforms::windows::screenshot_impl::DummyScreenshot; +``` + +#### 3. 实现 Service + +```rust +// src/services/screenshot_service.rs + +//! 屏幕截图服务 +//! +//! 提供屏幕截图功能 + +use crate::error::{AppError, AppResult}; +use crate::models::screenshot::{ScreenshotConfig, ScreenshotResult}; +use crate::platforms::screenshot::ScreenshotCapturer; + +/// 截图服务 +pub struct ScreenshotService; + +impl ScreenshotService { + /// 截取屏幕 + /// + /// 根据配置截取屏幕画面 + /// + /// # 参数 + /// + /// * `config` - 截图配置 + /// + /// # 返回 + /// + /// 返回截图结果 + pub fn capture(config: &ScreenshotConfig) -> AppResult { + // 验证配置 + if config.quality < 1 || config.quality > 100 { + return Err(AppError::InvalidColorData( + "图片质量必须在 1-100 之间".to_string() + )); + } + + // 调用平台实现 + PlatformScreenshot::capture_screen(config) + } + + /// 截取指定区域 + pub fn capture_region( + x: u32, + y: u32, + width: u32, + height: u32, + config: &ScreenshotConfig, + ) -> AppResult { + if width == 0 || height == 0 { + return Err(AppError::InvalidColorData( + "宽度和高度必须大于 0".to_string() + )); + } + + PlatformScreenshot::capture_region(x, y, width, height, config) + } +} +``` + +#### 4. 创建命令 + +```rust +// src/commands/screenshot_commands.rs + +//! 截图命令 + +use crate::models::screenshot::{ScreenshotConfig, ScreenshotResult}; +use crate::services::screenshot_service::ScreenshotService; + +/// 截取屏幕命令 +/// +/// # 参数 +/// +/// * `config` - 截图配置 +/// +/// # 返回 +/// +/// 返回截图结果 +/// +/// # 前端调用示例 +/// +/// ```typescript +/// import { invoke } from '@tauri-apps/api/tauri'; +/// +/// const result = await invoke('capture_screen', { +/// config: { +/// include_cursor: false, +/// quality: 90 +/// } +/// }); +/// console.log(result.data); // Base64 图片数据 +/// ``` +#[tauri::command] +pub fn capture_screen(config: ScreenshotConfig) -> Result { + ScreenshotService::capture(&config).map_err(|e| e.to_string()) +} + +/// 截取区域命令 +#[tauri::command] +pub fn capture_region( + x: u32, + y: u32, + width: u32, + height: u32, + config: ScreenshotConfig, +) -> Result { + ScreenshotService::capture_region(x, y, width, height, config) + .map_err(|e| e.to_string()) +} +``` + +--- + +## 最佳实践 + +### 1. 命名规范 + +| 类型 | 命名风格 | 示例 | +|------|----------|------| +| 模块 | snake_case | `color_service.rs` | +| 结构体 | PascalCase | `ColorInfo`, `NewFeatureService` | +| 函数 | snake_case | `get_pixel_color`, `toggle_window` | +| 常量 | SCREAMING_SNAKE_CASE | `MAX_RETRY_COUNT` | +| Trait | PascalCase + 能力 | `ScreenAccessor`, `CursorController` | +| 类型别名 | PascalCase + Platform/Type | `PlatformScreen`, `AppResult` | + +### 2. 文档注释规范 + +```rust +/// 简短描述(一句话) +/// +/// 详细描述(可以多行,说明功能、用途、注意事项) +/// +/// # 参数 +/// +/// * `param1` - 参数说明 +/// * `param2` - 参数说明 +/// +/// # 返回 +/// +/// 返回值说明 +/// +/// # 错误 +/// +/// - 错误类型1:错误场景 +/// - 错误类型2:错误场景 +/// +/// # 示例 +/// +/// ``` +/// let result = function_name(args); +/// assert!(result.is_ok()); +/// ``` +pub fn function_name(param1: Type1, param2: Type2) -> AppResult { + // 实现 +} +``` + +### 3. 错误处理规范 + +```rust +// ✅ 推荐:使用具体的错误类型 +fn do_something() -> AppResult<()> { + if condition { + return Err(AppError::InvalidData("数据无效".to_string())); + } + Ok(()) +} + +// ❌ 不推荐:直接返回 String +fn do_something() -> Result<(), String> { + if condition { + return Err("错误".to_string()); + } + Ok(()) +} +``` + +### 4. 参数验证规范 + +```rust +// ✅ 在 Service 层验证参数 +impl SomeService { + pub fn execute(&self, input: &str) -> AppResult { + // 参数验证 + if input.is_empty() { + return Err(AppError::InvalidData("输入不能为空".to_string())); + } + + if input.len() > 1000 { + return Err(AppError::InvalidData("输入过长".to_string())); + } + + // 业务逻辑 + // ... + } +} +``` + +### 5. 异步处理规范 + +```rust +// ✅ 提供 sync 和 async 两个版本 +impl SomeService { + // 同步版本 + pub fn execute(&self, input: &str) -> AppResult { + // 直接调用 + } + + // 异步版本 + pub async fn execute_async(&self, input: String) -> AppResult { + // 使用 async/await + tokio::time::sleep(Duration::from_secs(1)).await; + self.execute(&input) + } +} +``` + +### 6. 平台检测规范 + +```rust +// ✅ 使用条件编译和 trait +#[cfg(windows)] +fn platform_specific() -> AppResult<()> { + WindowsImpl::do_something() +} + +#[cfg(not(windows))] +fn platform_specific() -> AppResult<()> { + Err(AppError::PlatformNotSupported( + "此平台暂不支持".to_string() + )) +} + +// ❌ 不推荐:运行时检测 +fn platform_specific() -> AppResult<()> { + if cfg!(windows) { + // Windows 代码 + } else { + Err(AppError::PlatformNotSupported("...".to_string())) + } +} +``` + +### 7. 代码组织规范 + +```rust +// ✅ 推荐:按功能分组 +pub struct ColorService; + +impl ColorService { + // 公共方法 + pub fn new() -> Self { Self } + pub fn do_work(&self) -> AppResult<()> { } + + // 私有辅助方法 + fn validate(&self) -> AppResult<()> { } + fn process(&self) -> AppResult<()> { } +} + +// ✅ 使用 mod 组织相关功能 +mod color_conversion { + pub fn rgb_to_hsl() { } + pub fn hsl_to_rgb() { } +} +``` + +--- + +## 测试规范 + +### 单元测试 + +```rust +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_normal_case() { + // 测试正常场景 + let result = function_under_test(normal_input); + assert_eq!(result, expected); + } + + #[test] + fn test_edge_case() { + // 测试边界条件 + let result = function_under_test(edge_input); + assert!(result.is_ok()); + } + + #[test] + fn test_error_case() { + // 测试错误场景 + let result = function_under_test(invalid_input); + assert!(result.is_err()); + } +} +``` + +### 集成测试 + +```rust +// tests/integration_test.rs + +#[test] +fn test_full_workflow() { + // 测试完整的业务流程 + let service = MyService::new(); + let config = MyConfig::default(); + + let result = service.execute(&config).unwrap(); + assert_eq!(result.status, 200); +} +``` + +### 运行测试 + +```bash +# 运行所有测试 +cargo test + +# 运行特定测试 +cargo test test_color_conversion + +# 运行测试并显示输出 +cargo test -- --nocapture + +# 运行测试并生成文档 +cargo test --doc +``` + +--- + +## 常见问题 + +### Q: 如何添加新的错误类型? + +A: 在 `error.rs` 中添加新的枚举变体,并在 `Display` 实现中添加处理: + +```rust +pub enum AppError { + // ... + NewErrorType(String), +} + +impl fmt::Display for AppError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + // ... + AppError::NewErrorType(msg) => write!(f, "新错误: {}", msg), + } + } +} +``` + +### Q: 如何在多个平台实现同一个功能? + +A: 为每个平台创建独立的实现文件,并通过条件编译选择: + +```rust +// platforms/windows/impl.rs +#[cfg(windows)] +pub struct PlatformImpl; +impl PlatformTrait for PlatformImpl { } + +// platforms/macos/impl.rs +#[cfg(target_os = "macos")] +pub struct PlatformImpl; +impl PlatformTrait for PlatformImpl { } + +// platforms/mod.rs +#[cfg(windows)] +pub type Platform = crate::platforms::windows::impl::PlatformImpl; + +#[cfg(target_os = "macos")] +pub type Platform = crate::platforms::macos::impl::PlatformImpl; +``` + +### Q: 如何处理异步操作? + +A: 在 Service 层提供 async 函数,Command 层使用 async 命令: + +```rust +// Service +pub async fn fetch_data(&self) -> AppResult { + // 异步操作 +} + +// Command +#[tauri::command] +pub async fn get_data() -> Result { + service.fetch_data().await.map_err(|e| e.to_string()) +} +``` + +--- + +## 检查清单 + +在提交代码前,确保: + +- [ ] 代码符合 Rust 格式规范(`cargo fmt --check`) +- [ ] 代码通过 clippy 检查(`cargo clippy`) +- [ ] 所有测试通过(`cargo test`) +- [ ] 编译成功(`cargo check`) +- [ ] 所有公开 API 都有文档注释 +- [ ] 文档注释包含示例代码 +- [ ] 错误消息使用中文 +- [ ] 遵循依赖原则(上层依赖下层) +- [ ] 平台特定代码正确使用条件编译 +- [ ] 新增类型已导出(如需要) +- [ ] 命令已在 `lib.rs` 中注册 + +--- + +## 参考资源 + +- [Rust 官方文档](https://doc.rust-lang.org/) +- [Tauri 官方文档](https://tauri.app/) +- [Rust API 指南](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html) +- [Rust 命名规范](https://rust-lang.github.io/api-guidelines/naming.html) diff --git a/docs/快速参考.md b/docs/快速参考.md new file mode 100644 index 0000000..24ee4ef --- /dev/null +++ b/docs/快速参考.md @@ -0,0 +1,207 @@ +# 添加新功能快速参考 + +## 🚀 快速步骤 + +``` +1. 定义 Models → src/models/new_feature.rs +2. 实现 Utils → src/utils/new_algorithm.rs (可选) +3. 定义 Platform → src/platforms/new_feature.rs +4. 实现平台代码 → src/platforms/windows/new_feature_impl.rs +5. 实现 Service → src/services/new_feature_service.rs +6. 创建 Command → src/commands/new_feature_commands.rs +7. 注册模块 → 更新 mod.rs 和 lib.rs +8. 测试验证 → cargo check && cargo test +``` + +## 📁 文件模板 + +### 1. Model 模板 + +```rust +// src/models/feature.rs +use serde::{Deserialize, Serialize}; + +/// 数据说明 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FeatureData { + /// 字段说明 + pub field: String, +} +``` + +### 2. Service 模板 + +```rust +// src/services/feature_service.rs +//! 功能说明 + +use crate::error::AppResult; +use crate::models::feature::FeatureData; + +/// 功能服务 +pub struct FeatureService; + +impl FeatureService { + /// 功能说明 + pub fn execute(&self, input: &FeatureData) -> AppResult { + // 实现 + } +} +``` + +### 3. Command 模板 + +```rust +// src/commands/feature_commands.rs +//! 命令说明 + +use crate::models::feature::FeatureData; + +/// 命令说明 +#[tauri::command] +pub fn execute_feature(input: FeatureData) -> Result { + FeatureService::execute(&input).map_err(|e| e.to_string()) +} +``` + +### 4. Platform Trait 模板 + +```rust +// src/platforms/feature.rs +use crate::error::AppResult; + +/// Trait 说明 +pub trait FeatureAccessor { + fn do_something(&self, param: &str) -> AppResult; +} + +#[cfg(windows)] +pub type PlatformFeature = crate::platforms::windows::feature_impl::WindowsFeature; + +#[cfg(not(windows))] +pub type PlatformFeature = crate::platforms::windows::feature_impl::DummyFeature; +``` + +## ✅ 代码规范清单 + +### 命名规范 +- [ ] 模块文件: `snake_case` +- [ ] 结构体: `PascalCase` +- [ ] 函数: `snake_case` +- [ ] Trait: `PascalCase` + 能力描述 + +### 文档规范 +- [ ] 所有公开 API 有 `///` 注释 +- [ ] 所有模块有 `//!` 注释 +- [ ] 包含参数说明 +- [ ] 包含返回值说明 +- [ ] 包含错误说明 +- [ ] 包含使用示例 + +### 代码规范 +- [ ] 使用 `AppResult` 返回 +- [ ] 使用中文错误消息 +- [ ] 参数验证在 Service 层 +- [ ] Command 层简洁(仅适配) +- [ ] 使用 `#[cfg(windows)]` 条件编译 + +## 🔧 常用命令 + +```bash +# 格式化代码 +cargo fmt + +# 检查代码 +cargo check + +# 代码检查 +cargo clippy + +# 运行测试 +cargo test + +# 构建应用 +pnpm build + +# 开发运行 +pnpm tauri dev + +# 生成文档 +cargo doc --open +``` + +## 📋 检查清单 + +提交前检查: +- [ ] `cargo fmt` 通过 +- [ ] `cargo clippy` 无警告 +- [ ] `cargo test` 全部通过 +- [ ] `cargo check` 编译成功 +- [ ] 文档注释完整 +- [ ] 错误消息中文化 +- [ ] 命令已注册 + +## 🎯 示例对照 + +### 参考:颜色取色功能 + +``` +models/color.rs → ColorInfo, RgbInfo, HslInfo +utils/color_conversion.rs → rgb_to_hsl() +platforms/screen.rs → ScreenAccessor trait +platforms/windows/screen_impl.rs → WindowsScreen +services/color_service.rs → ColorService +commands/color_commands.rs → pick_color_interactive +lib.rs → 注册命令 +``` + +### 添加类似功能:截图 + +``` +models/screenshot.rs → ScreenshotConfig, ScreenshotResult +utils/image_utils.rs → (可选) 图像处理工具 +platforms/screenshot.rs → ScreenshotCapturer trait +platforms/windows/screenshot_impl.rs → WindowsScreenshot +services/screenshot_service.rs → ScreenshotService +commands/screenshot_commands.rs → capture_screen +lib.rs → 注册 capture_screen +``` + +## ⚠️ 常见错误 + +### 错误 1: Trait 方法未找到 +```rust +// ❌ 错误 +use crate::platforms::screen::PlatformScreen; +PlatformScreen::get_pixel_color(x, y)?; // 找不到方法 + +// ✅ 正确 +use crate::platforms::screen::PlatformScreen; +use crate::platforms::screen::ScreenAccessor; // 导入 trait +PlatformScreen::get_pixel_color(x, y)?; +``` + +### 错误 2: 类型不匹配 +```rust +// ❌ 错误 +pub fn toggle_window(window: &WebviewWindow) { } + +// ✅ 正确 +pub fn toggle_window(window: &Window) { } +``` + +### 错误 3: 忘记注册命令 +```rust +// ❌ 错误:命令未注册,前端无法调用 + +// ✅ 正确:在 lib.rs 中注册 +.invoke_handler(tauri::generate_handler![ + commands::new_commands::new_command, // 添加这一行 +]) +``` + +## 📚 相关文档 + +- [完整开发指南](./开发指南.md) - 详细的开发规范和教程 +- [Rust 官方文档](https://doc.rust-lang.org/) - Rust 语言参考 +- [Tauri 官方文档](https://tauri.app/) - Tauri 框架文档 diff --git a/package.json b/package.json index 2dedf15..742a53a 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,11 @@ "@radix-ui/react-tabs": "^1.0.4", "@tailwindcss/vite": "^4.1.12", "@tauri-apps/api": "^2", + "@tauri-apps/plugin-global-shortcut": "^2", "@tauri-apps/plugin-opener": "^2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "fuse.js": "^7.1.0", "lucide-react": "^0.539.0", "react": "^19.1.0", "react-dom": "^19.1.0", diff --git a/picker.html b/picker.html new file mode 100644 index 0000000..27f6630 --- /dev/null +++ b/picker.html @@ -0,0 +1,257 @@ + + + + + + 取色遮罩 + + + +
+
+ +
+
#------
+
+
+
+ + + + + + diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 699375e..86b7bee 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - [[package]] name = "adler2" version = "2.0.1" @@ -19,9 +10,9 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -41,12 +32,6 @@ dependencies = [ "alloc-no-stdlib", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -58,9 +43,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.99" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" [[package]] name = "async-broadcast" @@ -88,9 +73,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" dependencies = [ "async-task", "concurrent-queue", @@ -102,11 +87,11 @@ dependencies = [ [[package]] name = "async-io" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" dependencies = [ - "async-lock", + "autocfg", "cfg-if", "concurrent-queue", "futures-io", @@ -115,14 +100,14 @@ dependencies = [ "polling", "rustix", "slab", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "async-lock" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ "event-listener", "event-listener-strategy", @@ -131,9 +116,9 @@ dependencies = [ [[package]] name = "async-process" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" dependencies = [ "async-channel", "async-io", @@ -155,14 +140,14 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "async-signal" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" dependencies = [ "async-io", "async-lock", @@ -173,7 +158,7 @@ dependencies = [ "rustix", "signal-hook-registry", "slab", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -190,7 +175,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -228,21 +213,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - [[package]] name = "base64" version = "0.21.7" @@ -263,11 +233,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.3" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -281,20 +251,11 @@ dependencies = [ [[package]] name = "block2" -version = "0.5.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "objc2 0.5.2", -] - -[[package]] -name = "block2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" -dependencies = [ - "objc2 0.6.2", + "objc2", ] [[package]] @@ -333,15 +294,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" [[package]] name = "byteorder" @@ -351,9 +312,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" dependencies = [ "serde", ] @@ -364,7 +325,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.10.0", "cairo-sys-rs", "glib", "libc", @@ -385,11 +346,11 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.11" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d07aa9a93b00c76f71bc35d598bed923f6d4f3a9ca5c24b7737ae1a292841c0" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -412,7 +373,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] @@ -422,15 +383,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" dependencies = [ "serde", - "toml 0.9.5", + "toml 0.9.11+spec-1.1.0", ] [[package]] name = "cc" -version = "1.2.34" +version = "1.2.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" +checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" dependencies = [ + "find-msvc-tools", "shlex", ] @@ -463,27 +425,20 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ - "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -543,7 +498,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.10.0", "core-foundation", "core-graphics-types", "foreign-types", @@ -556,7 +511,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.10.0", "core-foundation", "libc", ] @@ -588,6 +543,25 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -596,9 +570,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -628,7 +602,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -638,14 +612,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "darling" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" dependencies = [ "darling_core", "darling_macro", @@ -653,37 +627,37 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "darling_macro" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -696,7 +670,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -727,7 +701,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -742,8 +716,8 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.3", - "objc2 0.6.2", + "bitflags 2.10.0", + "objc2", ] [[package]] @@ -754,14 +728,14 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "dlopen2" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b54f373ccf864bf587a89e880fb7610f8d73f3045f13580948ccbcaff26febff" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" dependencies = [ "dlopen2_derive", "libc", @@ -771,13 +745,13 @@ dependencies = [ [[package]] name = "dlopen2_derive" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -791,9 +765,9 @@ dependencies = [ [[package]] name = "dtoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" [[package]] name = "dtoa-short" @@ -817,15 +791,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] -name = "embed-resource" -version = "3.0.5" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6d81016d6c977deefb2ef8d8290da019e27cc26167e102185da528e6c0ab38" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "embed-resource" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55a075fc573c64510038d7ee9abc7990635863992f83ebc52c8b433b8411a02e" dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.9.5", + "toml 0.9.11+spec-1.1.0", "vswhom", "winreg", ] @@ -838,9 +818,9 @@ checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" [[package]] name = "endi" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" [[package]] name = "enumflags2" @@ -860,7 +840,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -871,22 +851,23 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" dependencies = [ "serde", + "serde_core", "typeid", ] [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -936,10 +917,16 @@ dependencies = [ ] [[package]] -name = "flate2" -version = "1.1.2" +name = "find-msvc-tools" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -951,6 +938,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.5.0" @@ -969,7 +962,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -997,6 +990,21 @@ dependencies = [ "new_debug_unreachable", ] +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -1004,6 +1012,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -1050,7 +1059,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1071,6 +1080,7 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -1200,6 +1210,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix", + "windows-link 0.2.1", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -1213,9 +1233,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "libc", @@ -1224,21 +1244,28 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", ] [[package]] -name = "gimli" -version = "0.31.1" +name = "getrandom" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] [[package]] name = "gio" @@ -1278,7 +1305,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.10.0", "futures-channel", "futures-core", "futures-executor", @@ -1302,11 +1329,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ "heck 0.4.1", - "proc-macro-crate 2.0.0", + "proc-macro-crate 2.0.2", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1325,6 +1352,24 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "global-hotkey" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7" +dependencies = [ + "crossbeam-channel", + "keyboard-types", + "objc2", + "objc2-app-kit", + "once_cell", + "serde", + "thiserror 2.0.18", + "windows-sys 0.59.0", + "x11rb", + "xkeysym", +] + [[package]] name = "gobject-sys" version = "0.18.0" @@ -1385,7 +1430,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1399,6 +1444,15 @@ name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heck" @@ -1438,12 +1492,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -1478,9 +1531,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", @@ -1499,14 +1552,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.16" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", @@ -1523,9 +1575,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1533,7 +1585,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.2", ] [[package]] @@ -1547,9 +1599,9 @@ dependencies = [ [[package]] name = "ico" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" dependencies = [ "byteorder", "png", @@ -1557,9 +1609,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -1570,9 +1622,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -1583,11 +1635,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -1598,42 +1649,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -1641,6 +1688,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -1681,13 +1734,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.15.5", + "hashbrown 0.16.1", "serde", + "serde_core", ] [[package]] @@ -1699,17 +1753,6 @@ dependencies = [ "cfb", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.3", - "cfg-if", - "libc", -] - [[package]] name = "ipnet" version = "2.11.0" @@ -1718,9 +1761,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ "memchr", "serde", @@ -1747,9 +1790,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "javascriptcore-rs" @@ -1798,9 +1841,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -1834,7 +1877,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.10.0", "serde", "unicode-segmentation", ] @@ -1847,7 +1890,7 @@ checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ "cssparser", "html5ever", - "indexmap 2.11.0", + "indexmap 2.13.0", "selectors", ] @@ -1857,6 +1900,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libappindicator" version = "0.9.0" @@ -1883,9 +1932,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.175" +version = "0.2.181" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5" [[package]] name = "libloading" @@ -1899,41 +1948,40 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.10.0", "libc", ] [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "mac" @@ -1963,7 +2011,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1974,9 +2022,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.7.5" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memoffset" @@ -2005,13 +2053,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2024,14 +2072,14 @@ dependencies = [ "dpi", "gtk", "keyboard-types", - "objc2 0.6.2", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.1", + "objc2-foundation", "once_cell", "png", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", "windows-sys 0.60.2", ] @@ -2041,7 +2089,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.10.0", "jni-sys", "log", "ndk-sys", @@ -2071,19 +2119,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.9.3", - "cfg-if", - "cfg_aliases", - "libc", - "memoffset", -] - [[package]] name = "nodrop" version = "0.1.14" @@ -2091,10 +2126,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] -name = "num-conv" -version = "0.1.0" +name = "ntapi" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-traits" @@ -2107,9 +2151,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", "rustversion", @@ -2117,37 +2161,21 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "objc-sys" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" - -[[package]] -name = "objc2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" -dependencies = [ - "objc-sys", - "objc2-encode", + "syn 2.0.114", ] [[package]] name = "objc2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" dependencies = [ "objc2-encode", "objc2-exception-helper", @@ -2155,77 +2183,104 @@ dependencies = [ [[package]] name = "objc2-app-kit" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.9.3", - "block2 0.6.1", + "bitflags 2.10.0", + "block2", "libc", - "objc2 0.6.2", + "objc2", "objc2-cloud-kit", "objc2-core-data", "objc2-core-foundation", "objc2-core-graphics", "objc2-core-image", - "objc2-foundation 0.3.1", - "objc2-quartz-core 0.3.1", + "objc2-core-text", + "objc2-core-video", + "objc2-foundation", + "objc2-quartz-core", ] [[package]] name = "objc2-cloud-kit" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" dependencies = [ - "bitflags 2.9.3", - "objc2 0.6.2", - "objc2-foundation 0.3.1", + "bitflags 2.10.0", + "objc2", + "objc2-foundation", ] [[package]] name = "objc2-core-data" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ - "bitflags 2.9.3", - "objc2 0.6.2", - "objc2-foundation 0.3.1", + "bitflags 2.10.0", + "objc2", + "objc2-foundation", ] [[package]] name = "objc2-core-foundation" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.10.0", "dispatch2", - "objc2 0.6.2", + "objc2", ] [[package]] name = "objc2-core-graphics" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.10.0", "dispatch2", - "objc2 0.6.2", + "objc2", "objc2-core-foundation", "objc2-io-surface", ] [[package]] name = "objc2-core-image" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" dependencies = [ - "objc2 0.6.2", - "objc2-foundation 0.3.1", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-core-video" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-io-surface", ] [[package]] @@ -2245,134 +2300,89 @@ dependencies = [ [[package]] name = "objc2-foundation" -version = "0.2.2" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.9.3", - "block2 0.5.1", + "bitflags 2.10.0", + "block2", "libc", - "objc2 0.5.2", -] - -[[package]] -name = "objc2-foundation" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" -dependencies = [ - "bitflags 2.9.3", - "block2 0.6.1", - "libc", - "objc2 0.6.2", + "objc2", "objc2-core-foundation", ] [[package]] name = "objc2-io-surface" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.9.3", - "objc2 0.6.2", + "bitflags 2.10.0", + "objc2", "objc2-core-foundation", ] [[package]] name = "objc2-javascript-core" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9052cb1bb50a4c161d934befcf879526fb87ae9a68858f241e693ca46225cf5a" +checksum = "2a1e6550c4caed348956ce3370c9ffeca70bb1dbed4fa96112e7c6170e074586" dependencies = [ - "objc2 0.6.2", + "objc2", "objc2-core-foundation", ] -[[package]] -name = "objc2-metal" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" -dependencies = [ - "bitflags 2.9.3", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", -] - [[package]] name = "objc2-quartz-core" -version = "0.2.2" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.9.3", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-metal", -] - -[[package]] -name = "objc2-quartz-core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" -dependencies = [ - "bitflags 2.9.3", - "objc2 0.6.2", - "objc2-foundation 0.3.1", + "bitflags 2.10.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", ] [[package]] name = "objc2-security" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1f8e0ef3ab66b08c42644dcb34dba6ec0a574bbd8adbb8bdbdc7a2779731a44" +checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a" dependencies = [ - "bitflags 2.9.3", - "objc2 0.6.2", + "bitflags 2.10.0", + "objc2", "objc2-core-foundation", ] [[package]] name = "objc2-ui-kit" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b1312ad7bc8a0e92adae17aa10f90aae1fb618832f9b993b022b591027daed" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.9.3", - "objc2 0.6.2", + "bitflags 2.10.0", + "objc2", "objc2-core-foundation", - "objc2-foundation 0.3.1", + "objc2-foundation", ] [[package]] name = "objc2-web-kit" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91672909de8b1ce1c2252e95bbee8c1649c9ad9d14b9248b3d7b4c47903c47ad" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" dependencies = [ - "bitflags 2.9.3", - "block2 0.6.1", - "objc2 0.6.2", + "bitflags 2.10.0", + "block2", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.1", + "objc2-foundation", "objc2-javascript-core", "objc2-security", ] -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -2381,9 +2391,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "open" -version = "5.3.2" +version = "5.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" dependencies = [ "dunce", "is-wsl", @@ -2440,9 +2450,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -2450,15 +2460,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -2577,7 +2587,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -2604,7 +2614,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher 1.0.1", + "siphasher 1.0.2", ] [[package]] @@ -2638,12 +2648,12 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.4" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" +checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", - "indexmap 2.11.0", + "indexmap 2.13.0", "quick-xml", "serde", "time", @@ -2664,23 +2674,23 @@ dependencies = [ [[package]] name = "polling" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -2706,6 +2716,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.114", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -2718,20 +2738,21 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" dependencies = [ - "toml_edit 0.20.7", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", ] [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.22.27", + "toml_edit 0.23.10+spec-1.0.0", ] [[package]] @@ -2766,27 +2787,27 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quick-xml" -version = "0.38.3" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -2857,7 +2878,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] @@ -2885,12 +2906,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] -name = "redox_syscall" -version = "0.5.17" +name = "rayon" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ - "bitflags 2.9.3", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", ] [[package]] @@ -2899,36 +2940,36 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "regex" -version = "1.11.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -2938,9 +2979,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -2949,15 +2990,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] name = "reqwest" -version = "0.12.23" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" dependencies = [ "base64 0.22.1", "bytes", @@ -2974,7 +3015,6 @@ dependencies = [ "pin-project-lite", "serde", "serde_json", - "serde_urlencoded", "sync_wrapper", "tokio", "tokio-util", @@ -2988,12 +3028,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustc_version" version = "0.4.1" @@ -3005,15 +3039,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3022,12 +3056,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - [[package]] name = "same-file" version = "1.0.6" @@ -3066,9 +3094,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -3085,7 +3113,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -3114,42 +3142,54 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ "serde", + "serde_core", ] [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] [[package]] name = "serde-untagged" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34836a629bcbc6f1afdf0907a744870039b1e14c0561cb26094fa683b158eff3" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" dependencies = [ "erased-serde", "serde", + "serde_core", "typeid", ] [[package]] -name = "serde_derive" -version = "1.0.219" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -3160,19 +3200,21 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "serde_json" -version = "1.0.143" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ + "indexmap 2.13.0", "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -3183,7 +3225,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -3197,40 +3239,27 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", + "serde_core", ] [[package]] name = "serde_with" -version = "3.14.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.0", + "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.0.4", - "serde", - "serde_derive", + "schemars 1.2.1", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -3238,14 +3267,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.14.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -3267,7 +3296,7 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -3299,18 +3328,19 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "siphasher" @@ -3320,15 +3350,15 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -3338,34 +3368,34 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "softbuffer" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" dependencies = [ "bytemuck", - "cfg_aliases", - "core-graphics", - "foreign-types", "js-sys", - "log", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-quartz-core 0.2.2", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", "raw-window-handle", "redox_syscall", + "tracing", "wasm-bindgen", "web-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3396,15 +3426,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "string_cache" @@ -3461,9 +3485,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -3487,7 +3511,22 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", +] + +[[package]] +name = "sysinfo" +version = "0.30.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "windows 0.52.0", ] [[package]] @@ -3499,18 +3538,18 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.23", + "toml 0.8.2", "version-compare", ] [[package]] name = "tao" -version = "0.34.2" +version = "0.34.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4daa814018fecdfb977b59a094df4bd43b42e8e21f88fddfc05807e6f46efaaf" +checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7" dependencies = [ - "bitflags 2.9.3", - "block2 0.6.1", + "bitflags 2.10.0", + "block2", "core-foundation", "core-graphics", "crossbeam-channel", @@ -3527,9 +3566,9 @@ dependencies = [ "ndk", "ndk-context", "ndk-sys", - "objc2 0.6.2", + "objc2", "objc2-app-kit", - "objc2-foundation 0.3.1", + "objc2-foundation", "once_cell", "parking_lot", "raw-window-handle", @@ -3537,8 +3576,8 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows", - "windows-core", + "windows 0.61.3", + "windows-core 0.61.2", "windows-version", "x11-dl", ] @@ -3551,7 +3590,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -3562,9 +3601,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.8.4" +version = "2.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d545ccf7b60dcd44e07c6fb5aeb09140966f0aabd5d2aa14a6821df7bc99348" +checksum = "463ae8677aa6d0f063a900b9c41ecd4ac2b7ca82f0b058cc4491540e55b20129" dependencies = [ "anyhow", "bytes", @@ -3572,7 +3611,7 @@ dependencies = [ "dirs", "dunce", "embed_plist", - "getrandom 0.3.3", + "getrandom 0.3.4", "glob", "gtk", "heck 0.5.0", @@ -3582,9 +3621,9 @@ dependencies = [ "log", "mime", "muda", - "objc2 0.6.2", + "objc2", "objc2-app-kit", - "objc2-foundation 0.3.1", + "objc2-foundation", "objc2-ui-kit", "objc2-web-kit", "percent-encoding", @@ -3601,15 +3640,14 @@ dependencies = [ "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "tray-icon", "url", - "urlpattern", "webkit2gtk", "webview2-com", "window-vibrancy", - "windows", + "windows 0.61.3", ] [[package]] @@ -3617,17 +3655,22 @@ name = "tauri-app" version = "0.1.0" dependencies = [ "serde", + "serde_derive", "serde_json", + "sysinfo", "tauri", "tauri-build", + "tauri-plugin-global-shortcut", "tauri-plugin-opener", + "windows 0.58.0", + "wmi", ] [[package]] name = "tauri-build" -version = "2.4.0" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67945dbaf8920dbe3a1e56721a419a0c3d085254ab24cff5b9ad55e2b0016e0b" +checksum = "ca7bd893329425df750813e95bd2b643d5369d929438da96d5bbb7cc2c918f74" dependencies = [ "anyhow", "cargo_toml", @@ -3641,15 +3684,15 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.9.5", + "toml 0.9.11+spec-1.1.0", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.4.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a" +checksum = "aac423e5859d9f9ccdd32e3cf6a5866a15bedbf25aa6630bcb2acde9468f6ae3" dependencies = [ "base64 0.22.1", "brotli", @@ -3663,9 +3706,9 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.106", + "syn 2.0.114", "tauri-utils", - "thiserror 2.0.16", + "thiserror 2.0.18", "time", "url", "uuid", @@ -3674,23 +3717,23 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.4.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e" +checksum = "1b6a1bd2861ff0c8766b1d38b32a6a410f6dc6532d4ef534c47cfb2236092f59" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", "tauri-codegen", "tauri-utils", ] [[package]] name = "tauri-plugin" -version = "2.4.0" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9946a3cede302eac0c6eb6c6070ac47b1768e326092d32efbb91f21ed58d978f" +checksum = "692a77abd8b8773e107a42ec0e05b767b8d2b7ece76ab36c6c3947e34df9f53f" dependencies = [ "anyhow", "glob", @@ -3699,70 +3742,85 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.9.5", + "toml 0.9.11+spec-1.1.0", "walkdir", ] [[package]] -name = "tauri-plugin-opener" -version = "2.5.0" +name = "tauri-plugin-global-shortcut" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786156aa8e89e03d271fbd3fe642207da8e65f3c961baa9e2930f332bf80a1f5" +checksum = "424af23c7e88d05e4a1a6fc2c7be077912f8c76bd7900fd50aa2b7cbf5a2c405" +dependencies = [ + "global-hotkey", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", +] + +[[package]] +name = "tauri-plugin-opener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc624469b06f59f5a29f874bbc61a2ed737c0f9c23ef09855a292c389c42e83f" dependencies = [ "dunce", "glob", "objc2-app-kit", - "objc2-foundation 0.3.1", + "objc2-foundation", "open", "schemars 0.8.22", "serde", "serde_json", "tauri", "tauri-plugin", - "thiserror 2.0.16", + "thiserror 2.0.18", "url", - "windows", + "windows 0.61.3", "zbus", ] [[package]] name = "tauri-runtime" -version = "2.8.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846" +checksum = "b885ffeac82b00f1f6fd292b6e5aabfa7435d537cef57d11e38a489956535651" dependencies = [ "cookie", "dpi", "gtk", "http", "jni", - "objc2 0.6.2", + "objc2", "objc2-ui-kit", "objc2-web-kit", "raw-window-handle", "serde", "serde_json", "tauri-utils", - "thiserror 2.0.16", + "thiserror 2.0.18", "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.61.3", ] [[package]] name = "tauri-runtime-wry" -version = "2.8.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fe9d48bd122ff002064e88cfcd7027090d789c4302714e68fcccba0f4b7807" +checksum = "5204682391625e867d16584fedc83fc292fb998814c9f7918605c789cd876314" dependencies = [ "gtk", "http", "jni", "log", - "objc2 0.6.2", + "objc2", "objc2-app-kit", - "objc2-foundation 0.3.1", + "objc2-foundation", "once_cell", "percent-encoding", "raw-window-handle", @@ -3773,15 +3831,15 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.61.3", "wry", ] [[package]] name = "tauri-utils" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212" +checksum = "fcd169fccdff05eff2c1033210b9b94acd07a47e6fa9a3431cf09cfd4f01c87e" dependencies = [ "anyhow", "brotli", @@ -3807,8 +3865,8 @@ dependencies = [ "serde_json", "serde_with", "swift-rs", - "thiserror 2.0.16", - "toml 0.9.5", + "thiserror 2.0.18", + "toml 0.9.11+spec-1.1.0", "url", "urlpattern", "uuid", @@ -3817,25 +3875,26 @@ dependencies = [ [[package]] name = "tauri-winres" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd21509dd1fa9bd355dc29894a6ff10635880732396aa38c0066c1e6c1ab8074" +checksum = "1087b111fe2b005e42dbdc1990fc18593234238d47453b0c99b7de1c9ab2c1e0" dependencies = [ + "dunce", "embed-resource", - "toml 0.9.5", + "toml 0.9.11+spec-1.1.0", ] [[package]] name = "tempfile" -version = "3.21.0" +version = "3.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.4.1", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3860,11 +3919,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.18", ] [[package]] @@ -3875,46 +3934,46 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "time" -version = "0.3.41" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -3922,9 +3981,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -3932,26 +3991,23 @@ dependencies = [ [[package]] name = "tokio" -version = "1.47.1" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "pin-project-lite", - "slab", "socket2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -3962,47 +4018,47 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" dependencies = [ "serde", "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", ] [[package]] name = "toml" -version = "0.9.5" +version = "0.9.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ - "indexmap 2.11.0", - "serde", - "serde_spanned 1.0.0", - "toml_datetime 0.7.0", + "indexmap 2.13.0", + "serde_core", + "serde_spanned 1.0.4", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 0.7.13", + "winnow 0.7.14", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] [[package]] name = "toml_datetime" -version = "0.7.0" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -4011,55 +4067,56 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.11.0", - "toml_datetime 0.6.11", + "indexmap 2.13.0", + "toml_datetime 0.6.3", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.20.7" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.11.0", - "toml_datetime 0.6.11", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap 2.11.0", + "indexmap 2.13.0", "serde", "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "winnow 0.7.13", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap 2.13.0", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow 0.7.14", ] [[package]] name = "toml_parser" -version = "1.0.2" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ - "winnow 0.7.13", + "winnow 0.7.14", ] [[package]] name = "toml_writer" -version = "1.0.2" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -4072,11 +4129,11 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.10.0", "bytes", "futures-util", "http", @@ -4102,9 +4159,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -4113,44 +4170,44 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", ] [[package]] name = "tray-icon" -version = "0.21.1" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d92153331e7d02ec09137538996a7786fe679c629c279e82a6be762b7e6fe2" +checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c" dependencies = [ "crossbeam-channel", "dirs", "libappindicator", "muda", - "objc2 0.6.2", + "objc2", "objc2-app-kit", "objc2-core-foundation", "objc2-core-graphics", - "objc2-foundation 0.3.1", + "objc2-foundation", "once_cell", "png", "serde", - "thiserror 2.0.16", - "windows-sys 0.59.0", + "thiserror 2.0.18", + "windows-sys 0.60.2", ] [[package]] @@ -4167,9 +4224,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "uds_windows" @@ -4225,9 +4282,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" [[package]] name = "unicode-segmentation" @@ -4236,15 +4293,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] -name = "url" -version = "2.5.7" +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -4273,21 +4337,21 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.18.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" +checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] [[package]] name = "version-compare" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" [[package]] name = "version_check" @@ -4347,47 +4411,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -4396,9 +4457,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4406,31 +4467,53 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.106", - "wasm-bindgen-backend", + "syn 2.0.114", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] [[package]] -name = "wasm-streams" -version = "0.4.2" +name = "wasm-encoder" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.13.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" dependencies = [ "futures-util", "js-sys", @@ -4440,10 +4523,22 @@ dependencies = [ ] [[package]] -name = "web-sys" -version = "0.3.77" +name = "wasmparser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.10.0", + "hashbrown 0.15.5", + "indexmap 2.13.0", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -4451,9 +4546,9 @@ dependencies = [ [[package]] name = "webkit2gtk" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -4475,9 +4570,9 @@ dependencies = [ [[package]] name = "webkit2gtk-sys" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" dependencies = [ "bitflags 1.3.2", "cairo-sys-rs", @@ -4495,38 +4590,38 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.38.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows", - "windows-core", - "windows-implement", - "windows-interface", + "windows 0.61.3", + "windows-core 0.61.2", + "windows-implement 0.60.2", + "windows-interface 0.59.3", ] [[package]] name = "webview2-com-macros" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "webview2-com-sys" -version = "0.38.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" dependencies = [ - "thiserror 2.0.16", - "windows", - "windows-core", + "thiserror 2.0.18", + "windows 0.61.3", + "windows-core 0.61.2", ] [[package]] @@ -4547,11 +4642,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4566,15 +4661,45 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "objc2 0.6.2", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.1", + "objc2-foundation", "raw-window-handle", "windows-sys 0.59.0", "windows-version", ] +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" +dependencies = [ + "windows-core 0.59.0", + "windows-targets 0.53.5", +] + [[package]] name = "windows" version = "0.61.3" @@ -4582,9 +4707,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", - "windows-core", + "windows-core 0.61.2", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] @@ -4594,7 +4719,42 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core", + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" +dependencies = [ + "windows-implement 0.59.0", + "windows-interface 0.59.3", + "windows-result 0.3.4", + "windows-strings 0.3.1", + "windows-targets 0.53.5", ] [[package]] @@ -4603,11 +4763,24 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -4616,31 +4789,64 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core", - "windows-link", + "windows-core 0.61.2", + "windows-link 0.1.3", "windows-threading", ] [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", +] + +[[package]] +name = "windows-implement" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", ] [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", ] [[package]] @@ -4649,14 +4855,29 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-numerics" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core", - "windows-link", + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -4665,7 +4886,35 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link 0.1.3", ] [[package]] @@ -4674,7 +4923,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", ] [[package]] @@ -4701,7 +4959,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", ] [[package]] @@ -4737,19 +5004,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -4758,16 +5025,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] name = "windows-version" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -4784,9 +5051,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -4802,9 +5069,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -4820,9 +5087,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -4832,9 +5099,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -4850,9 +5117,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -4868,9 +5135,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -4886,9 +5153,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -4904,9 +5171,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" @@ -4919,9 +5186,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -4937,28 +5204,122 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" dependencies = [ - "bitflags 2.9.3", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap 2.13.0", + "prettyplease", + "syn 2.0.114", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.114", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.10.0", + "indexmap 2.13.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "wmi" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7787dacdd8e71cbc104658aade4009300777f9b5fda6a75f19145fedb8a18e71" +dependencies = [ + "chrono", + "futures", + "log", + "serde", + "thiserror 2.0.18", + "windows 0.59.0", + "windows-core 0.59.0", ] [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wry" -version = "0.53.2" +version = "0.54.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b6763512fe4b51c80b3ce9b50939d682acb4de335dfabbdb20d7a2642199b7" +checksum = "5ed1a195b0375491dd15a7066a10251be217ce743cf4bbbbdcf5391d6473bee0" dependencies = [ "base64 0.22.1", - "block2 0.6.1", + "block2", "cookie", "crossbeam-channel", "dirs", @@ -4973,10 +5334,10 @@ dependencies = [ "kuchikiki", "libc", "ndk", - "objc2 0.6.2", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.1", + "objc2-foundation", "objc2-ui-kit", "objc2-web-kit", "once_cell", @@ -4985,13 +5346,13 @@ dependencies = [ "sha2", "soup3", "tao-macros", - "thiserror 2.0.16", + "thiserror 2.0.18", "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows", - "windows-core", + "windows 0.61.3", + "windows-core 0.61.2", "windows-version", "x11-dl", ] @@ -5018,12 +5379,34 @@ dependencies = [ ] [[package]] -name = "yoke" -version = "0.8.0" +name = "x11rb" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -5031,21 +5414,21 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", "synstructure", ] [[package]] name = "zbus" -version = "5.10.0" +version = "5.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a073be99ace1adc48af593701c8015cd9817df372e14a1a6b0ee8f8bf043be" +checksum = "1bfeff997a0aaa3eb20c4652baf788d2dfa6d2839a0ead0b3ff69ce2f9c4bdd1" dependencies = [ "async-broadcast", "async-executor", @@ -5061,14 +5444,16 @@ dependencies = [ "futures-core", "futures-lite", "hex", - "nix", + "libc", "ordered-stream", + "rustix", "serde", "serde_repr", "tracing", "uds_windows", - "windows-sys 0.60.2", - "winnow 0.7.13", + "uuid", + "windows-sys 0.61.2", + "winnow 0.7.14", "zbus_macros", "zbus_names", "zvariant", @@ -5076,14 +5461,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.10.0" +version = "5.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e80cd713a45a49859dcb648053f63265f4f2851b6420d47a958e5697c68b131" +checksum = "0bbd5a90dbe8feee5b13def448427ae314ccd26a49cac47905cafefb9ff846f1" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", "zbus_names", "zvariant", "zvariant_utils", @@ -5091,34 +5476,33 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.2.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" dependencies = [ "serde", - "static_assertions", - "winnow 0.7.13", + "winnow 0.7.14", "zvariant", ] [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -5138,15 +5522,15 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", "synstructure", ] [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -5155,9 +5539,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -5166,51 +5550,57 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] -name = "zvariant" -version = "5.7.0" +name = "zmij" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db" +checksum = "4de98dfa5d5b7fef4ee834d0073d560c9ca7b6c46a71d058c48db7960f8cfaf7" + +[[package]] +name = "zvariant" +version = "5.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b64ef4f40c7951337ddc7023dd03528a57a3ce3408ee9da5e948bd29b232c4" dependencies = [ "endi", "enumflags2", "serde", - "winnow 0.7.13", + "winnow 0.7.14", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.7.0" +version = "5.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e" +checksum = "484d5d975eb7afb52cc6b929c13d3719a20ad650fea4120e6310de3fc55e415c" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.106", - "winnow 0.7.13", + "syn 2.0.114", + "winnow 0.7.14", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 0c68d52..52a9ce3 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -15,12 +15,26 @@ name = "tauri_app_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] -tauri-build = { version = "2.4.0", features = [] } +tauri-build = { version = "2.4", features = [] } [dependencies] -tauri = { version = "2.4.0", features = [] } -tauri-plugin-opener = "2.5.0" +tauri = { version = "2.4", features = [] } +tauri-plugin-opener = "2.5" +tauri-plugin-global-shortcut = "2" serde = { version = "1", features = ["derive"] } -serde_json = "1" - +serde_json = { version = "1", features = ["preserve_order"] } +sysinfo = "0.30" +[target.'cfg(windows)'.dependencies] +windows = { version = "0.58", features = [ + "Win32_Foundation", + "Win32_Graphics_Gdi", + "Win32_UI_HiDpi", + "Win32_UI_WindowsAndMessaging", + "Win32_UI_Input_KeyboardAndMouse", + "Win32_System_StationsAndDesktops", + "Win32_System_SystemInformation", + "Win32_Storage_FileSystem", +] } +wmi = "0.14" +serde_derive = "1.0" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 4cdbf49..ab10709 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -2,9 +2,14 @@ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "Capability for the main window", - "windows": ["main"], + "windows": ["main", "picker_overlay"], "permissions": [ "core:default", + "core:window:allow-hide", + "core:window:allow-show", + "core:window:allow-close", + "core:window:allow-set-focus", + "core:window:allow-is-visible", "opener:default" ] } diff --git a/src-tauri/src/commands/json_format_commands.rs b/src-tauri/src/commands/json_format_commands.rs new file mode 100644 index 0000000..3442668 --- /dev/null +++ b/src-tauri/src/commands/json_format_commands.rs @@ -0,0 +1,110 @@ +//! JSON 格式化命令 +//! +//! 定义 JSON 格式化相关的 Tauri 命令 + +use crate::models::json_format::{JsonFormatConfig, JsonFormatResult, JsonValidateResult}; +use crate::services::json_format_service::JsonFormatService; + +/// 格式化 JSON 命令 +/// +/// Tauri 命令,用于从前端调用 JSON 格式化功能 +/// +/// # 参数 +/// +/// * `input` - 输入的 JSON 字符串 +/// * `config` - 格式化配置 +/// +/// # 返回 +/// +/// 返回格式化结果,包含成功标志、结果字符串和错误信息 +/// +/// # 前端调用示例 +/// +/// ```typescript +/// import { invoke } from '@tauri-apps/api/tauri'; +/// +/// const result = await invoke('format_json', { +/// input: '{"name":"test","value":123}', +/// config: { +/// indent: 2, +/// sort_keys: false, +/// mode: 'pretty' +/// } +/// }); +/// console.log(result.success); // true +/// console.log(result.result); // 格式化后的 JSON +/// ``` +#[tauri::command] +pub fn format_json(input: String, config: JsonFormatConfig) -> JsonFormatResult { + JsonFormatService::format(&input, &config) + .unwrap_or_else(|e| JsonFormatResult { + success: false, + result: String::new(), + error: Some(e.to_string()), + is_valid: false, + }) +} + +/// 验证 JSON 命令 +/// +/// 验证输入的字符串是否为有效的 JSON +/// +/// # 参数 +/// +/// * `input` - 输入的 JSON 字符串 +/// +/// # 返回 +/// +/// 返回验证结果 +/// +/// # 前端调用示例 +/// +/// ```typescript +/// import { invoke } from '@tauri-apps/api/tauri'; +/// +/// const result = await invoke('validate_json', { +/// input: '{"valid": true}' +/// }); +/// console.log(result.is_valid); // true +/// ``` +#[tauri::command] +pub fn validate_json(input: String) -> JsonValidateResult { + JsonFormatService::validate(&input).unwrap_or_else(|e| JsonValidateResult { + is_valid: false, + error_message: Some(e.to_string()), + error_line: None, + error_column: None, + }) +} + +/// 压缩 JSON 命令 +/// +/// 去除 JSON 中的所有空格和换行 +/// +/// # 参数 +/// +/// * `input` - 输入的 JSON 字符串 +/// +/// # 返回 +/// +/// 返回压缩后的 JSON +/// +/// # 前端调用示例 +/// +/// ```typescript +/// import { invoke } from '@tauri-apps/api/tauri'; +/// +/// const result = await invoke('compact_json', { +/// input: '{ "name" : "test" }' +/// }); +/// console.log(result.result); // '{"name":"test"}' +/// ``` +#[tauri::command] +pub fn compact_json(input: String) -> JsonFormatResult { + JsonFormatService::compact(&input).unwrap_or_else(|e| JsonFormatResult { + success: false, + result: String::new(), + error: Some(e.to_string()), + is_valid: false, + }) +} diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs new file mode 100644 index 0000000..cc9ad80 --- /dev/null +++ b/src-tauri/src/commands/mod.rs @@ -0,0 +1,8 @@ +//! Tauri 命令处理层 +//! +//! 定义与前端交互的 Tauri 命令,作为前端和业务逻辑之间的适配器 + +pub mod json_format_commands; +pub mod picker_color_commands; +pub mod system_info_commands; +pub mod window_commands; diff --git a/src-tauri/src/commands/picker_color_commands.rs b/src-tauri/src/commands/picker_color_commands.rs new file mode 100644 index 0000000..0ee0c83 --- /dev/null +++ b/src-tauri/src/commands/picker_color_commands.rs @@ -0,0 +1,311 @@ +//! 取色器命令 +//! +//! 提供完整的屏幕取色功能(使用透明遮罩窗口方案) +//! +//! # 架构设计 +//! +//! - **后端(Rust)**:负责窗口创建/销毁和屏幕取色 +//! - **前端(HTML/CSS/JS)**:负责光标样式和用户交互 +//! +//! # 优势 +//! +//! 使用透明全屏遮罩 + CSS 光标,完美解决 Windows 系统光标竞争问题, +//! 避免了传统的 SetCursor API 与系统的 race condition。 + +use std::thread; +use std::time::Duration; +use serde::Serialize; +use tauri::{AppHandle, Manager}; + +#[derive(Serialize)] +pub struct ScreenRegionRgba { + pub width: i32, + pub height: i32, + /// RGBA 字节数组(长度 = width * height * 4) + pub data: Vec, + /// 中心点颜色(从 data 直接计算),便于前端展示 + pub center: crate::models::color::RgbInfo, + pub center_hex: String, +} + +/// 预热取色器窗口(隐藏创建) +/// +/// 目的:避免第一次显示 WebView 时的“白屏闪一下”(WebView 首帧默认白底/初始化抖动)。 +pub(crate) fn prewarm_picker_window(app: &AppHandle) -> Result<(), String> { + use tauri::WebviewWindowBuilder; + + if app.get_webview_window("picker_overlay").is_some() { + return Ok(()); + } + + WebviewWindowBuilder::new( + app, + "picker_overlay", + tauri::WebviewUrl::App("picker.html".into()), + ) + .title("取色器") + .fullscreen(true) + .transparent(true) + .always_on_top(true) + .decorations(false) + .skip_taskbar(true) + .resizable(false) + // 关键:先不可见创建,等真正开始取色时再 show + .visible(false) + // 尽可能早地把背景设为透明,降低首帧白底概率 + .initialization_script( + r#" + try { + document.documentElement.style.background = 'transparent'; + document.body && (document.body.style.background = 'transparent'); + } catch (_) {} + "#, + ) + .build() + .map_err(|e| format!("预热取色器窗口失败: {}", e))?; + + Ok(()) +} + +/// 启动取色器(推荐使用 ⭐) +/// +/// 打开透明全屏遮罩窗口,光标由前端 CSS 控制。 +/// +/// # 工作流程 +/// +/// 1. 后端隐藏主窗口 +/// 2. 后端创建全屏透明遮罩窗口 +/// 3. **前端通过 CSS 设置 `cursor: crosshair` 控制光标** +/// 4. 用户点击任意位置,前端调用 `pick_color_at_point` 取色 +/// 5. 取色完成后前端调用 `close_picker_window` 关闭遮罩窗口 +/// +/// # 参数 +/// +/// * `app` - Tauri 应用句柄 +/// +/// # 前端实现示例 +/// +/// picker.html: +/// ```html +/// +/// +/// ``` +#[tauri::command] +pub async fn start_color_picker(app: AppHandle) -> Result<(), String> { + // 先隐藏主窗口 + if let Some(main_window) = app.get_webview_window("main") { + main_window.hide().map_err(|e| e.to_string())?; + } + + // 等待窗口完全隐藏 + thread::sleep(Duration::from_millis(150)); + + // 打开透明遮罩窗口 + open_picker_window(app).await?; + + Ok(()) +} + +/// 打开取色器遮罩窗口(内部辅助函数) +/// +/// # 参数 +/// +/// * `app` - Tauri 应用句柄 +/// +/// # 返回 +/// +/// 返回成功或错误信息 +pub(crate) async fn open_picker_window(app: AppHandle) -> Result<(), String> { + use tauri::WebviewWindowBuilder; + + // 检查窗口是否已存在 + if let Some(existing) = app.get_webview_window("picker_overlay") { + // 复用已存在窗口:避免频繁 close/build 引起的白屏闪烁 + existing + .show() + .and_then(|_| existing.set_focus()) + .map_err(|e| format!("显示取色器窗口失败: {}", e))?; + return Ok(()); + } + + // 创建全屏透明遮罩窗口 + let picker_window = WebviewWindowBuilder::new( + &app, + "picker_overlay", + tauri::WebviewUrl::App("picker.html".into()), + ) + .title("取色器") + .fullscreen(true) + .transparent(true) + .always_on_top(true) + .decorations(false) + .skip_taskbar(true) + .resizable(false) + // 先不可见创建,再显式 show(降低首帧白底闪烁) + .visible(false) + .initialization_script( + r#" + try { + document.documentElement.style.background = 'transparent'; + document.body && (document.body.style.background = 'transparent'); + } catch (_) {} + "#, + ) + .build() + .map_err(|e| format!("创建取色器窗口失败: {}", e))?; + + // 显式 show + focus,确保在某些系统上立即可见 + picker_window + .show() + .and_then(|_| picker_window.set_focus()) + .map_err(|e| format!("显示取色器窗口失败: {}", e))?; + + Ok(()) +} + +/// 关闭取色器遮罩窗口 +/// +/// # 前端调用示例 +/// +/// ```typescript +/// import { invoke } from '@tauri-apps/api/tauri'; +/// +/// await invoke('close_picker_window'); +/// ``` +#[tauri::command] +pub async fn close_picker_window(app: AppHandle) -> Result<(), String> { + if let Some(window) = app.get_webview_window("picker_overlay") { + // 不 close:只 hide,避免窗口销毁/重建导致的白屏闪烁 + // 先隐藏遮罩窗口,再恢复主窗口,过渡更自然 + window + .hide() + .map_err(|e| format!("隐藏取色器窗口失败: {}", e))?; + + // 恢复主窗口 + if let Some(main_window) = app.get_webview_window("main") { + main_window + .show() + .and_then(|_| main_window.set_focus()) + .map_err(|e| format!("显示主窗口失败: {}", e))?; + } + } + Ok(()) +} + +/// 在指定坐标取色 +/// +/// # 参数 +/// +/// * `x` - 屏幕 X 坐标 +/// * `y` - 屏幕 Y 坐标 +/// +/// # 返回 +/// +/// 返回颜色信息 +#[tauri::command] +pub async fn pick_color_at_point( + x: i32, + y: i32, +) -> Result { + let (r, g, b) = crate::utils::screen::WindowsScreen::get_pixel_color(x, y) + .map_err(|e| e.to_string())?; + + Ok(crate::models::color::ColorInfo::new(r, g, b, x, y)) +} + +/// 获取“最上层应用”的颜色(排除取色遮罩自身的影响) +/// +/// 在 Windows 上,如果遮罩窗口位于最顶层,`GetPixel` 读到的是**合成后的颜色**, +/// 即可能包含遮罩的透明叠加,从而导致取色偏暗/偏差。 +/// +/// 这里的策略是:先隐藏遮罩窗口,等待一帧左右让桌面合成刷新,再读取屏幕像素。 +#[tauri::command] +pub async fn pick_color_at_point_topmost( + app: AppHandle, + x: i32, + y: i32, +) -> Result { + // 先隐藏遮罩窗口(不 close,避免白屏闪烁) + if let Some(overlay) = app.get_webview_window("picker_overlay") { + let _ = overlay.hide(); + } + + // 给桌面合成一点时间刷新(过短可能还会读到遮罩叠加结果) + thread::sleep(Duration::from_millis(35)); + + let (r, g, b) = crate::utils::screen::WindowsScreen::get_pixel_color(x, y) + .map_err(|e| e.to_string())?; + + Ok(crate::models::color::ColorInfo::new(r, g, b, x, y)) +} + +/// 捕获屏幕区域像素(用于前端放大镜) +#[tauri::command] +pub async fn capture_screen_region_rgba( + x: i32, + y: i32, + width: i32, + height: i32, +) -> Result { + let data = crate::utils::screen::WindowsScreen::capture_region_rgba(x, y, width, height) + .map_err(|e| e.to_string())?; + + let cx = width / 2; + let cy = height / 2; + let idx = ((cy as usize) * (width as usize) + (cx as usize)) * 4; + let r = data[idx]; + let g = data[idx + 1]; + let b = data[idx + 2]; + + Ok(ScreenRegionRgba { + width, + height, + data, + center: crate::models::color::RgbInfo { r, g, b }, + center_hex: format!("#{:02X}{:02X}{:02X}", r, g, b), + }) +} + +/// RGB 转 HSL 命令 +/// +/// 将 RGB 颜色值转换为 HSL 颜色值 +/// +/// # 参数 +/// +/// * `r` - 红色分量 (0-255) +/// * `g` - 绿色分量 (0-255) +/// * `b` - 蓝色分量 (0-255) +/// +/// # 返回 +/// +/// 返回 HSL 颜色值 +/// +/// # 前端调用示例 +/// +/// ```typescript +/// import { invoke } from '@tauri-apps/api/tauri'; +/// +/// const hsl = await invoke('rgb_to_hsl', { r: 255, g: 0, b: 0 }); +/// console.log(hsl); // { h: 0, s: 100, l: 50 } +/// ``` +#[tauri::command] +pub fn rgb_to_hsl(r: u8, g: u8, b: u8) -> crate::models::color::HslInfo { + crate::utils::color_conversion::rgb_to_hsl(r, g, b) +} diff --git a/src-tauri/src/commands/system_info_commands.rs b/src-tauri/src/commands/system_info_commands.rs new file mode 100644 index 0000000..d33e148 --- /dev/null +++ b/src-tauri/src/commands/system_info_commands.rs @@ -0,0 +1,29 @@ +//! 系统信息命令 +//! +//! 定义系统信息相关的 Tauri 命令 + +use crate::models::system_info::SystemInfo; +use crate::services::system_info_service::SystemInfoService; + +/// 获取系统信息命令 +/// +/// Tauri 命令,用于从前端调用系统信息查询功能 +/// +/// # 返回 +/// +/// 返回包含所有系统信息的结构体 +/// +/// # 前端调用示例 +/// +/// ```typescript +/// import { invoke } from '@tauri-apps/api/tauri'; +/// +/// const info = await invoke('get_system_info'); +/// console.log(info.os.name); // "Windows" +/// console.log(info.cpu.model); // "Intel Core i7..." +/// console.log(info.memory.total_gb); // 16.0 +/// ``` +#[tauri::command] +pub fn get_system_info() -> Result { + SystemInfoService::get_system_info().map_err(|e| e.to_string()) +} diff --git a/src-tauri/src/commands/window_commands.rs b/src-tauri/src/commands/window_commands.rs new file mode 100644 index 0000000..de5bb55 --- /dev/null +++ b/src-tauri/src/commands/window_commands.rs @@ -0,0 +1,70 @@ +//! 窗口命令 +//! +//! 定义窗口管理相关的 Tauri 命令 + +use tauri::Window; +use crate::services::window_service::WindowService; + +/// 切换窗口显示/隐藏命令 +/// +/// 根据窗口当前状态切换显示或隐藏 +/// +/// # 参数 +/// +/// * `window` - Tauri 窗口对象,自动由框架注入 +/// +/// # 前端调用示例 +/// +/// ```typescript +/// import { invoke } from '@tauri-apps/api/tauri'; +/// +/// await invoke('toggle_window'); +/// ``` +#[tauri::command] +pub fn toggle_window(window: Window) -> Result<(), String> { + WindowService::toggle_window(&window) + .map_err(|e| e.to_string()) +} + +/// 隐藏窗口命令 +/// +/// 将窗口隐藏,使其不再可见 +/// +/// # 参数 +/// +/// * `window` - Tauri 窗口对象,自动由框架注入 +/// +/// # 前端调用示例 +/// +/// ```typescript +/// import { invoke } from '@tauri-apps/api/tauri'; +/// +/// await invoke('hide_window'); +/// ``` +#[tauri::command] +pub fn hide_window(window: Window) -> Result<(), String> { + WindowService::hide_window(&window) + .map_err(|e| e.to_string()) +} + +/// 显示窗口命令 +/// +/// 显示窗口并将其设置为焦点窗口 +/// +/// # 参数 +/// +/// * `window` - Tauri 窗口对象,自动由框架注入 +/// +/// # 前端调用示例 +/// +/// ```typescript +/// import { invoke } from '@tauri-apps/api/tauri'; +/// +/// await invoke('show_window'); +/// ``` +#[tauri::command] +pub fn show_window(window: Window) -> Result<(), String> { + WindowService::show_window(&window) + .map_err(|e| e.to_string()) +} + diff --git a/src-tauri/src/error.rs b/src-tauri/src/error.rs new file mode 100644 index 0000000..edba99f --- /dev/null +++ b/src-tauri/src/error.rs @@ -0,0 +1,76 @@ +//! 错误处理模块 +//! +//! 提供统一的错误类型定义和错误处理机制 + +use std::fmt; + +/// 应用统一错误类型 +/// +/// 定义了应用中可能出现的所有错误类型,每个错误都携带详细的错误信息 +#[derive(Debug)] +pub enum AppError { + /// 平台不支持 + /// + /// 表示当前平台不支持某项功能 + PlatformNotSupported(String), + + /// 屏幕访问失败 + /// + /// 表示无法获取或访问屏幕设备 + ScreenAccessFailed(String), + + /// 窗口操作失败 + /// + /// 表示窗口显示、隐藏或聚焦等操作失败 + WindowOperationFailed(String), + + /// 光标操作失败 + /// + /// 表示光标设置或恢复操作失败 + CursorOperationFailed(String), + + /// 无效的颜色数据 + /// + /// 表示提供的颜色数据格式不正确或超出范围 + InvalidColorData(String), + + /// 颜色转换失败 + /// + /// 表示颜色空间转换(如 RGB 到 HSL)失败 + ColorConversionFailed(String), + + /// 系统信息获取失败 + /// + /// 表示获取系统信息时失败 + SystemInfoFailed(String), +} + +impl fmt::Display for AppError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AppError::PlatformNotSupported(msg) => write!(f, "平台不支持: {}", msg), + AppError::ScreenAccessFailed(msg) => write!(f, "屏幕访问失败: {}", msg), + AppError::WindowOperationFailed(msg) => write!(f, "窗口操作失败: {}", msg), + AppError::CursorOperationFailed(msg) => write!(f, "光标操作失败: {}", msg), + AppError::InvalidColorData(msg) => write!(f, "颜色数据无效: {}", msg), + AppError::ColorConversionFailed(msg) => write!(f, "颜色转换失败: {}", msg), + AppError::SystemInfoFailed(msg) => write!(f, "系统信息获取失败: {}", msg), + } + } +} + +impl std::error::Error for AppError {} + +/// 应用统一返回类型 +/// +/// 用于所有可能返回错误的函数,简化错误处理代码 +pub type AppResult = Result; + +/// 为 Tauri 实现自动转换 +/// +/// 允许 `AppError` 自动转换为 `String`,以满足 Tauri 命令的要求 +impl From for String { + fn from(error: AppError) -> String { + error.to_string() + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4a277ef..3e0c317 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,14 +1,54 @@ -// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ -#[tauri::command] -fn greet(name: &str) -> String { - format!("Hello, {}! You've been greeted from Rust!", name) -} +//! Tauri 应用入口 +//! +//! 提供应用初始化和模块组装功能 +// 模块声明 +mod commands; +mod error; +mod models; +mod platforms; +mod services; +mod utils; + +// 重新导出常用类型 +pub use error::{AppError, AppResult}; + +/// 运行 Tauri 应用 +/// +/// 初始化应用、注册插件、设置全局快捷键并启动应用 +/// #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_opener::init()) - .invoke_handler(tauri::generate_handler![greet]) + .plugin(tauri_plugin_global_shortcut::Builder::new().build()) + .setup(|app| { + // 预热取色器窗口:避免第一次取色出现“白屏闪一下” + // 窗口会以 hidden 状态创建,不会影响用户体验 + let _ = commands::picker_color_commands::prewarm_picker_window(app.handle()); + + utils::shortcut::register_global_shortcuts(app)?; + Ok(()) + }) + .invoke_handler(tauri::generate_handler![ + // window 窗口操作 + commands::window_commands::toggle_window, + commands::window_commands::hide_window, + commands::window_commands::show_window, + // 取色器命令 + commands::picker_color_commands::rgb_to_hsl, + commands::picker_color_commands::start_color_picker, + commands::picker_color_commands::close_picker_window, + commands::picker_color_commands::pick_color_at_point, + commands::picker_color_commands::pick_color_at_point_topmost, + commands::picker_color_commands::capture_screen_region_rgba, + // Json格式化命令 + commands::json_format_commands::format_json, + commands::json_format_commands::validate_json, + commands::json_format_commands::compact_json, + // 操作系统信息命令 + commands::system_info_commands::get_system_info, + ]) .run(tauri::generate_context!()) - .expect("error while running tauri application"); + .expect("运行 Tauri 应用时出错"); } diff --git a/src-tauri/src/models/color.rs b/src-tauri/src/models/color.rs new file mode 100644 index 0000000..b05c295 --- /dev/null +++ b/src-tauri/src/models/color.rs @@ -0,0 +1,91 @@ +//! 颜色数据模型 +//! +//! 定义颜色相关的数据结构,包括 RGB、HSL 和完整的颜色信息 + +use serde::{Deserialize, Serialize}; +use crate::utils::color_conversion; + +/// 颜色信息 +/// +/// 包含颜色的完整信息,支持多种颜色格式和屏幕坐标 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ColorInfo { + /// 十六进制颜色值(格式:#RRGGBB) + pub hex: String, + /// RGB 颜色值 + pub rgb: RgbInfo, + /// HSL 颜色值 + pub hsl: HslInfo, + /// 屏幕坐标 X(像素) + pub x: i32, + /// 屏幕坐标 Y(像素) + pub y: i32, +} + +impl ColorInfo { + /// 从 RGB 值创建颜色信息 + /// + /// # 参数 + /// + /// * `r` - 红色分量 (0-255) + /// * `g` - 绿色分量 (0-255) + /// * `b` - 蓝色分量 (0-255) + /// * `x` - 屏幕坐标 X(像素) + /// * `y` - 屏幕坐标 Y(像素) + /// + /// # 返回 + /// + /// 返回包含完整颜色信息的 `ColorInfo` 实例 + /// + /// # 示例 + /// + /// ```no_run + /// use crate::models::color::ColorInfo; + /// + /// let color = ColorInfo::new(255, 0, 0, 100, 200); + /// assert_eq!(color.hex, "#FF0000"); + /// ``` + pub fn new(r: u8, g: u8, b: u8, x: i32, y: i32) -> Self { + let hex = format!("#{:02X}{:02X}{:02X}", r, g, b); + + Self { + hex, + rgb: RgbInfo { r, g, b }, + hsl: color_conversion::rgb_to_hsl(r, g, b), + x, + y, + } + } +} + +/// RGB 颜色 +/// +/// 表示 RGB 颜色模式的颜色值 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RgbInfo { + /// 红色分量 (0-255) + pub r: u8, + /// 绿色分量 (0-255) + pub g: u8, + /// 蓝色分量 (0-255) + pub b: u8, +} + +/// HSL 颜色 +/// +/// 表示 HSL 颜色模式的颜色值 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HslInfo { + /// 色相 (0-360) + /// + /// 表示颜色在色轮上的角度,0° 为红色,120° 为绿色,240° 为蓝色 + pub h: u16, + /// 饱和度 (0-100) + /// + /// 表示颜色的鲜艳程度,0% 为灰色,100% 为完全饱和 + pub s: u8, + /// 亮度 (0-100) + /// + /// 表示颜色的明暗程度,0% 为黑色,100% 为白色 + pub l: u8, +} diff --git a/src-tauri/src/models/json_format.rs b/src-tauri/src/models/json_format.rs new file mode 100644 index 0000000..acb8f91 --- /dev/null +++ b/src-tauri/src/models/json_format.rs @@ -0,0 +1,85 @@ +//! JSON 格式化相关数据模型 +//! +//! 定义 JSON 格式化工具使用的数据结构 + +use serde::{Deserialize, Serialize}; + +/// JSON 格式化配置 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct JsonFormatConfig { + /// 缩进空格数(默认 2) + #[serde(default = "default_indent")] + pub indent: u32, + + /// 是否对 key 进行排序 + #[serde(default)] + pub sort_keys: bool, + + /// 格式化模式 + #[serde(default)] + pub mode: FormatMode, +} + +/// 默认缩进空格数 +fn default_indent() -> u32 { + 2 +} + +/// JSON 格式化模式 +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub enum FormatMode { + /// 标准格式化(美化) + #[serde(rename = "pretty")] + Pretty, + /// 压缩格式(去除空格和换行) + #[serde(rename = "compact")] + Compact, +} + +impl Default for FormatMode { + fn default() -> Self { + Self::Pretty + } +} + +impl Default for JsonFormatConfig { + fn default() -> Self { + Self { + indent: default_indent(), + sort_keys: false, + mode: FormatMode::default(), + } + } +} + +/// JSON 格式化结果 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct JsonFormatResult { + /// 是否成功 + pub success: bool, + + /// 格式化后的 JSON 字符串 + pub result: String, + + /// 错误信息(如果失败) + pub error: Option, + + /// 原始 JSON 是否有效 + pub is_valid: bool, +} + +/// JSON 验证结果 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct JsonValidateResult { + /// 是否有效的 JSON + pub is_valid: bool, + + /// 错误信息(如果无效) + pub error_message: Option, + + /// 错误位置(行号,从 1 开始) + pub error_line: Option, + + /// 错误位置(列号,从 1 开始) + pub error_column: Option, +} diff --git a/src-tauri/src/models/mod.rs b/src-tauri/src/models/mod.rs new file mode 100644 index 0000000..85bf2ad --- /dev/null +++ b/src-tauri/src/models/mod.rs @@ -0,0 +1,7 @@ +//! 数据模型模块 +//! +//! 定义应用中使用的数据结构 + +pub mod color; +pub mod json_format; +pub mod system_info; diff --git a/src-tauri/src/models/system_info.rs b/src-tauri/src/models/system_info.rs new file mode 100644 index 0000000..c4709eb --- /dev/null +++ b/src-tauri/src/models/system_info.rs @@ -0,0 +1,170 @@ +//! 系统信息相关数据模型 +//! +//! 定义系统信息工具使用的数据结构 + +use serde::{Deserialize, Serialize}; + +/// 系统信息(完整版) +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SystemInfo { + /// 操作系统信息 + pub os: OsInfo, + /// 硬件信息(主板、BIOS) + pub hardware: HardwareInfo, + /// CPU 信息 + pub cpu: CpuInfo, + /// 内存信息 + pub memory: MemoryInfo, + /// GPU 信息列表 + pub gpu: Vec, + /// 磁盘信息列表 + pub disks: Vec, + /// 计算机信息 + pub computer: ComputerInfo, + /// 显示器信息 + pub display: DisplayInfo, + /// 网络信息 + pub network: NetworkInfo, +} + +/// 操作系统信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OsInfo { + /// 操作系统名称 + pub name: String, + /// 操作系统版本 + pub version: String, + /// 系统架构 + pub arch: String, + /// 内核版本 + pub kernel_version: String, + /// 主机名 + pub host_name: String, + /// 运行时间(可读格式) + pub uptime_readable: String, +} + +/// 硬件信息(主板、BIOS) +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HardwareInfo { + /// 制造商 + pub manufacturer: String, + /// 型号 + pub model: String, + /// BIOS 版本 + pub bios_version: String, + /// BIOS 序列号 + pub bios_serial: String, +} + +/// CPU 信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CpuInfo { + /// CPU 型号 + pub model: String, + /// 物理核心数 + pub cores: usize, + /// 逻辑处理器数 + pub processors: usize, + /// 最大频率 (MHz) + pub max_frequency: u32, + /// 当前使用率 (0-100) + pub usage_percent: f32, +} + +/// 内存信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MemoryInfo { + /// 总内存 (GB) + pub total_gb: f64, + /// 可用内存 (GB) + pub available_gb: f64, + /// 已用内存 (GB) + pub used_gb: f64, + /// 使用率 (0-100) + pub usage_percent: f64, +} + +/// GPU 信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GpuInfo { + /// GPU 名称 + pub name: String, + /// 显存 (GB) + pub vram_gb: f64, + /// 驱动版本 + pub driver_version: String, +} + +/// 磁盘信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DiskInfo { + /// 盘符 (如 "C:") + pub drive_letter: String, + /// 卷标 + pub volume_label: String, + /// 文件系统类型 + pub file_system: String, + /// 总容量 (GB) + pub total_gb: f64, + /// 可用空间 (GB) + pub available_gb: f64, + /// 已用空间 (GB) + pub used_gb: f64, + /// 使用率 (0-100) + pub usage_percent: f64, +} + +/// 计算机信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ComputerInfo { + /// 计算机名称 + pub name: String, + /// 用户名 + pub username: String, + /// 域名/工作组 + pub domain: String, + /// 制造商 + pub manufacturer: String, + /// 型号 + pub model: String, + /// 序列号 + pub serial_number: String, +} + +/// 显示器信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DisplayInfo { + /// 屏幕数量 + pub monitor_count: u32, + /// 主显示器分辨率 + pub primary_resolution: String, + /// 所有显示器分辨率列表 + pub all_resolutions: Vec, +} + +/// 网络信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NetworkInfo { + /// 网络接口列表 + pub interfaces: Vec, + /// 总下载 (MB) + pub total_downloaded_mb: f64, + /// 总上传 (MB) + pub total_uploaded_mb: f64, +} + +/// 网络接口信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct InterfaceInfo { + /// 接口名称 + pub name: String, + /// MAC 地址 + pub mac_address: String, + /// IP 地址列表 + pub ip_networks: Vec, + /// 上传速度 (KB/s) + pub upload_speed_kb: f64, + /// 下载速度 (KB/s) + pub download_speed_kb: f64, +} diff --git a/src-tauri/src/platforms/mod.rs b/src-tauri/src/platforms/mod.rs new file mode 100644 index 0000000..c91a99c --- /dev/null +++ b/src-tauri/src/platforms/mod.rs @@ -0,0 +1,9 @@ +//! 平台相关模块 +//! +//! 定义不同平台的特定实现 + +pub mod system_info; + +// Windows 平台实现 +#[cfg(windows)] +pub mod windows; diff --git a/src-tauri/src/platforms/system_info.rs b/src-tauri/src/platforms/system_info.rs new file mode 100644 index 0000000..bc3d266 --- /dev/null +++ b/src-tauri/src/platforms/system_info.rs @@ -0,0 +1,22 @@ +//! 系统信息平台抽象 +//! +//! 定义获取系统信息的平台相关接口 + +use crate::error::AppResult; +use crate::models::system_info::SystemInfo; + +/// 系统信息获取 trait +/// +/// 定义获取系统信息的接口,不同平台需要实现此 trait +pub trait SystemInfoAccessor { + /// 获取完整的系统信息 + /// + /// # 返回 + /// + /// 返回包含所有系统信息的结构体 + /// + /// # 错误 + /// + /// 平台不支持或获取信息失败时返回错误 + fn get_system_info(&self) -> AppResult; +} diff --git a/src-tauri/src/platforms/windows/mod.rs b/src-tauri/src/platforms/windows/mod.rs new file mode 100644 index 0000000..f2ccf1b --- /dev/null +++ b/src-tauri/src/platforms/windows/mod.rs @@ -0,0 +1,3 @@ +//! Windows 平台特定实现 + +pub mod system_info_impl; diff --git a/src-tauri/src/platforms/windows/system_info_impl.rs b/src-tauri/src/platforms/windows/system_info_impl.rs new file mode 100644 index 0000000..a4ed0b2 --- /dev/null +++ b/src-tauri/src/platforms/windows/system_info_impl.rs @@ -0,0 +1,509 @@ +//! Windows 平台系统信息实现 +//! +//! 使用 WMI 和 sysinfo 获取系统信息 + +use crate::error::{AppError, AppResult}; +use crate::models::system_info::{ + ComputerInfo, CpuInfo, DisplayInfo, DiskInfo, GpuInfo, HardwareInfo, InterfaceInfo, + MemoryInfo, NetworkInfo, OsInfo, SystemInfo, +}; +use crate::platforms::system_info::SystemInfoAccessor; +use serde::Deserialize; +use std::time::Duration; +use sysinfo::System; + +/// Windows 平台系统信息实现 +#[cfg(windows)] +pub struct WindowsSystemInfo; + +#[cfg(windows)] +impl SystemInfoAccessor for WindowsSystemInfo { + fn get_system_info(&self) -> AppResult { + // 使用 sysinfo 获取基础信息 + let mut sys = System::new_all(); + + // 等待一小会儿以收集CPU使用率和网络速率 + std::thread::sleep(Duration::from_millis(200)); + sys.refresh_all(); + sys.refresh_cpu(); + + // 使用 WMI 获取详细硬件信息 + let wmi_result = Self::get_wmi_info(); + + // 解构 WMI 结果,提供默认值 + let (hw_info, gpu_infos, disk_labels, net_ips) = match wmi_result { + Ok((hw, gpus, labels, ips)) => (hw, gpus, labels, ips), + Err(_) => ( + HardwareInfo { + manufacturer: "Unknown".to_string(), + model: "Unknown".to_string(), + bios_version: "Unknown".to_string(), + bios_serial: "Unknown".to_string(), + }, + vec![], + std::collections::HashMap::new(), + std::collections::HashMap::new(), + ) + }; + + Ok(SystemInfo { + os: Self::get_os_info(&sys)?, + hardware: hw_info, + cpu: Self::get_cpu_info(&sys)?, + memory: Self::get_memory_info(&sys)?, + gpu: gpu_infos, + disks: Self::get_disk_info(&sys, &disk_labels)?, + computer: Self::get_computer_info()?, + display: Self::get_display_info()?, + network: Self::get_network_info(&sys, &net_ips)?, + }) + } +} + +/// WMI 结构体映射 +/// 这些结构体名称必须匹配 Windows WMI 类名(包含下划线) +#[derive(Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +#[allow(non_camel_case_types)] +struct Win32_VideoController { + name: String, + driver_version: Option, + adapter_ram: Option, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +#[allow(non_camel_case_types)] +struct Win32_ComputerSystem { + manufacturer: Option, + model: Option, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +#[allow(non_camel_case_types)] +struct Win32_BaseBoard { + manufacturer: Option, + product: Option, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +#[allow(non_camel_case_types)] +struct Win32_Bios { + #[allow(dead_code)] + manufacturer: Option, + version: Option, + serial_number: Option, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +#[allow(non_camel_case_types)] +struct Win32_LogicalDisk { + device_id: String, + volume_name: Option, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +#[allow(non_camel_case_types)] +struct Win32_NetworkAdapterConfiguration { + mac_address: Option, + ip_address: Option>, + ip_enabled: Option, +} + +/// WMI 信息返回类型 +type WmiInfoResult = Result<( + HardwareInfo, + Vec, + std::collections::HashMap, + std::collections::HashMap>, +), String>; + +#[cfg(windows)] +impl WindowsSystemInfo { + /// 获取 WMI 硬件信息(容错版本,某个查询失败不影响其他查询) + fn get_wmi_info() -> WmiInfoResult { + use wmi::{COMLibrary, WMIConnection}; + use std::collections::HashMap; + + let com = COMLibrary::new() + .map_err(|e| format!("初始化 COM 失败: {:?}", e))?; + + let wmi_con = WMIConnection::new(com) + .map_err(|e| format!("连接 WMI 失败: {:?}", e))?; + + // 1. 获取硬件信息(结合 ComputerSystem 和 BaseBoard) + let hw_info = { + let sys_query: Result, _> = wmi_con.query(); + let board_query: Result, _> = wmi_con.query(); + let bios_query: Result, _> = wmi_con.query(); + + let sys_result = sys_query.ok(); + let board_result = board_query.ok(); + let bios_result = bios_query.ok(); + + let sys = sys_result.as_ref().and_then(|v| v.first()); + let board = board_result.as_ref().and_then(|v| v.first()); + let bios = bios_result.as_ref().and_then(|v| v.first()); + + // 优先使用主板信息,回退到系统信息 + let manufacturer = board + .and_then(|b| b.manufacturer.clone()) + .or_else(|| sys.and_then(|s| s.manufacturer.clone())) + .unwrap_or_else(|| "Unknown".to_string()); + + let model = board + .and_then(|b| b.product.clone()) + .or_else(|| sys.and_then(|s| s.model.clone())) + .unwrap_or_else(|| "Unknown".to_string()); + + HardwareInfo { + manufacturer, + model, + bios_version: bios + .and_then(|b| b.version.clone()) + .unwrap_or_else(|| "Unknown".to_string()), + bios_serial: bios + .and_then(|b| b.serial_number.clone()) + .unwrap_or_else(|| "Unknown".to_string()), + } + }; + + // 2. 获取显卡信息(容错) + let gpu_infos = { + let gpu_query: Result, _> = wmi_con.query(); + gpu_query + .unwrap_or_default() + .into_iter() + .map(|g| GpuInfo { + name: g.name, + vram_gb: g.adapter_ram.unwrap_or(0) as f64 / (1024.0 * 1024.0 * 1024.0), + driver_version: g.driver_version.unwrap_or_else(|| "Unknown".to_string()), + }) + .collect() + }; + + // 3. 获取磁盘卷标 + let mut disk_labels = HashMap::new(); + if let Ok(disk_query_result) = wmi_con.query::() { + for disk in disk_query_result { + if let Some(vol) = disk.volume_name { + if !vol.is_empty() { + disk_labels.insert(disk.device_id, vol); + } + } + } + } + + // 4. 获取网络 IP(修复 MAC 大小写匹配) + let mut net_ips = HashMap::new(); + if let Ok(net_query_result) = wmi_con.query::() { + for net in net_query_result { + if let (Some(true), Some(mac), Some(ips)) = (net.ip_enabled, net.mac_address, net.ip_address) { + // WMI 返回大写 MAC,sysinfo 返回小写,统一转为小写 + net_ips.insert(mac.to_lowercase(), ips); + } + } + } + + Ok((hw_info, gpu_infos, disk_labels, net_ips)) + } + + /// 获取操作系统信息 + fn get_os_info(_sys: &System) -> AppResult { + use windows::Win32::System::SystemInformation::GetNativeSystemInfo; + + let mut sys_info = unsafe { std::mem::zeroed() }; + unsafe { + GetNativeSystemInfo(&mut sys_info); + } + + let arch = unsafe { + match sys_info.Anonymous.Anonymous.wProcessorArchitecture.0 { + 0 => "x86 (32-bit)".to_string(), + 9 => "x64 (64-bit)".to_string(), + 5 => "ARM".to_string(), + 12 => "ARM64".to_string(), + _ => "Unknown".to_string(), + } + }; + + Ok(OsInfo { + name: "Windows".to_string(), + version: System::os_version().unwrap_or_else(|| "Unknown".to_string()), + arch, + kernel_version: System::kernel_version().unwrap_or_else(|| "Unknown".to_string()), + host_name: System::host_name().unwrap_or_else(|| "Unknown".to_string()), + uptime_readable: Self::format_uptime(System::uptime()), + }) + } + + /// 获取 CPU 信息 + fn get_cpu_info(sys: &System) -> AppResult { + let cpus = sys.cpus(); + let cpu = cpus.first().ok_or_else(|| { + AppError::SystemInfoFailed("无法获取 CPU 信息".to_string()) + })?; + + let physical_cores = sys.physical_core_count().unwrap_or(1); + let usage = sys.global_cpu_info().cpu_usage(); + + Ok(CpuInfo { + model: cpu.brand().to_string(), + cores: physical_cores, + processors: cpus.len(), + max_frequency: cpu.frequency() as u32, + usage_percent: usage, + }) + } + + /// 获取内存信息 + fn get_memory_info(sys: &System) -> AppResult { + let total = sys.total_memory() as f64; + let available = sys.available_memory() as f64; + let used = total - available; + + Ok(MemoryInfo { + total_gb: Self::bytes_to_gb(total), + available_gb: Self::bytes_to_gb(available), + used_gb: Self::bytes_to_gb(used), + usage_percent: if total > 0.0 { (used / total) * 100.0 } else { 0.0 }, + }) + } + + /// 获取磁盘信息 + fn get_disk_info(_sys: &System, disk_labels: &std::collections::HashMap) -> AppResult> { + let mut disk_infos = Vec::new(); + + // 在 sysinfo 0.30 中使用新的 API + use sysinfo::Disks; + + let disks = Disks::new_with_refreshed_list(); + + for disk in disks.list() { + let total = disk.total_space() as f64; + let available = disk.available_space() as f64; + let used = total - available; + + // 获取盘符,处理 "C:\" -> "C:" 的情况 + let name = disk.name().to_string_lossy(); + let drive_letter = name.trim_end_matches('\\').to_string(); + + // 从 WMI 查询结果中获取真实卷标 + let volume_label = disk_labels + .get(&drive_letter) + .cloned() + .unwrap_or_else(|| "Local Disk".to_string()); + + disk_infos.push(DiskInfo { + drive_letter, + volume_label, + file_system: disk.file_system().to_string_lossy().to_string(), + total_gb: Self::bytes_to_gb(total), + available_gb: Self::bytes_to_gb(available), + used_gb: Self::bytes_to_gb(used), + usage_percent: if total > 0.0 { (used / total) * 100.0 } else { 0.0 }, + }); + } + + Ok(disk_infos) + } + + /// 获取计算机信息 + fn get_computer_info() -> AppResult { + use windows::Win32::System::SystemInformation::{ + ComputerNamePhysicalDnsHostname, GetComputerNameExW, + }; + use windows::core::PWSTR; + + let mut computer_name = [0u16; 256]; + let mut size = computer_name.len() as u32; + + unsafe { + let _ = GetComputerNameExW( + ComputerNamePhysicalDnsHostname, + PWSTR(computer_name.as_mut_ptr()), + &mut size, + ); + } + + let name = String::from_utf16_lossy(&computer_name[..size as usize]); + + Ok(ComputerInfo { + name: name.clone(), + username: std::env::var("USERNAME").unwrap_or_else(|_| "Unknown".to_string()), + domain: "WORKGROUP".to_string(), + manufacturer: name.clone(), + model: "PC".to_string(), + serial_number: "Unknown".to_string(), + }) + } + + /// 获取显示器信息 + fn get_display_info() -> AppResult { + use windows::Win32::UI::WindowsAndMessaging::{GetSystemMetrics, SM_CXSCREEN, SM_CYSCREEN}; + + let width = unsafe { GetSystemMetrics(SM_CXSCREEN) }; + let height = unsafe { GetSystemMetrics(SM_CYSCREEN) }; + let resolution = format!("{}x{}", width, height); + + Ok(DisplayInfo { + monitor_count: 1, + primary_resolution: resolution.clone(), + all_resolutions: vec![resolution], + }) + } + + /// 获取网络信息 + fn get_network_info(_sys: &System, net_ips: &std::collections::HashMap>) -> AppResult { + use sysinfo::Networks; + + let networks = Networks::new_with_refreshed_list(); + let mut interfaces = Vec::new(); + let mut total_down = 0.0; + let mut total_up = 0.0; + + for (name, data) in networks.list() { + total_down += data.total_received() as f64; + total_up += data.total_transmitted() as f64; + + // 过滤掉回环接口 + if name == "LO" { + continue; + } + + // 修复 MAC 地址匹配:统一转为小写 + let mac = data.mac_address().to_string().to_lowercase(); + let ip_list = net_ips.get(&mac).cloned().unwrap_or_default(); + + // 只显示有 IP 或有流量的接口 + if !ip_list.is_empty() || data.total_received() > 0 { + interfaces.push(InterfaceInfo { + name: name.clone(), + mac_address: mac, + ip_networks: ip_list, + upload_speed_kb: data.transmitted() as f64 / 1024.0, + download_speed_kb: data.received() as f64 / 1024.0, + }); + } + } + + Ok(NetworkInfo { + interfaces, + total_downloaded_mb: total_down / 1024.0 / 1024.0, + total_uploaded_mb: total_up / 1024.0 / 1024.0, + }) + } + + /// 字节转换为 GB + fn bytes_to_gb(bytes: f64) -> f64 { + bytes / 1024.0 / 1024.0 / 1024.0 + } + + /// 格式化运行时间为人类可读格式 + fn format_uptime(seconds: u64) -> String { + let days = seconds / 86400; + let hours = (seconds % 86400) / 3600; + let minutes = (seconds % 3600) / 60; + + if days > 0 { + format!("{}天 {}小时 {}分钟", days, hours, minutes) + } else if hours > 0 { + format!("{}小时 {}分钟", hours, minutes) + } else { + format!("{}分钟", minutes) + } + } +} + +/// 其他平台占位实现 +#[cfg(not(windows))] +pub struct DummySystemInfo; + +#[cfg(not(windows))] +impl SystemInfoAccessor for DummySystemInfo { + fn get_system_info(&self) -> AppResult { + use sysinfo::System; + let mut sys = System::new_all(); + sys.refresh_all(); + + Ok(SystemInfo { + os: OsInfo { + name: System::name().unwrap_or_else(|| "Unknown".to_string()), + version: System::os_version().unwrap_or_default(), + arch: std::env::consts::ARCH.to_string(), + kernel_version: System::kernel_version().unwrap_or_default(), + host_name: System::host_name().unwrap_or_default(), + uptime_readable: format!("{} seconds", System::uptime()), + }, + hardware: HardwareInfo { + manufacturer: "Unknown".to_string(), + model: "Unknown".to_string(), + bios_version: "Unknown".to_string(), + bios_serial: "Unknown".to_string(), + }, + cpu: { + let cpus = sys.cpus(); + let cpu = cpus.first().unwrap(); + CpuInfo { + model: cpu.brand().to_string(), + cores: sys.physical_core_count().unwrap_or(1), + processors: cpus.len(), + max_frequency: cpu.frequency(), + usage_percent: sys.global_cpu_info().cpu_usage(), + } + }, + memory: { + let total = sys.total_memory() as f64; + let available = sys.available_memory() as f64; + let used = total - available; + MemoryInfo { + total_gb: total / 1024.0 / 1024.0 / 1024.0, + available_gb: available / 1024.0 / 1024.0 / 1024.0, + used_gb: used / 1024.0 / 1024.0 / 1024.0, + usage_percent: if total > 0.0 { (used / total) * 100.0 } else { 0.0 }, + } + }, + gpu: vec![], + disks: sys + .disks() + .iter() + .map(|disk| { + let total = disk.total_space() as f64; + let available = disk.available_space() as f64; + let used = total - available; + DiskInfo { + drive_letter: disk.name().to_string_lossy().into_owned(), + volume_label: "Local Disk".to_string(), + file_system: disk.file_system().to_string_lossy().into_owned(), + total_gb: total / 1024.0 / 1024.0 / 1024.0, + available_gb: available / 1024.0 / 1024.0 / 1024.0, + used_gb: used / 1024.0 / 1024.0 / 1024.0, + usage_percent: if total > 0.0 { (used / total) * 100.0 } else { 0.0 }, + } + }) + .collect(), + computer: ComputerInfo { + name: System::host_name().unwrap_or_default(), + username: std::env::var("USERNAME").unwrap_or_default(), + domain: "WORKGROUP".to_string(), + manufacturer: "Unknown".to_string(), + model: "Unknown".to_string(), + serial_number: "Unknown".to_string(), + }, + display: DisplayInfo { + monitor_count: 1, + primary_resolution: "Unknown".to_string(), + all_resolutions: vec![], + }, + network: NetworkInfo { + interfaces: vec![], + total_downloaded_mb: 0.0, + total_uploaded_mb: 0.0, + }, + }) + } +} diff --git a/src-tauri/src/services/json_format_service.rs b/src-tauri/src/services/json_format_service.rs new file mode 100644 index 0000000..15d1caa --- /dev/null +++ b/src-tauri/src/services/json_format_service.rs @@ -0,0 +1,200 @@ +//! JSON 格式化服务 +//! +//! 提供 JSON 格式化功能的核心业务逻辑 + +use crate::error::AppResult; +use crate::models::json_format::{JsonFormatConfig, JsonFormatResult, JsonValidateResult}; +use crate::utils::json_formatter; + +/// JSON 格式化服务 +pub struct JsonFormatService; + +impl JsonFormatService { + /// 格式化 JSON 字符串 + /// + /// 根据配置对输入的 JSON 字符串进行格式化 + /// + /// # 参数 + /// + /// * `input` - 输入的 JSON 字符串 + /// * `config` - 格式化配置 + /// + /// # 返回 + /// + /// 返回格式化结果 + /// + /// # 错误 + /// + /// - 输入为空时返回 `AppError::InvalidData` + pub fn format(input: &str, config: &JsonFormatConfig) -> AppResult { + // 参数验证 + if input.trim().is_empty() { + return Ok(JsonFormatResult { + success: false, + result: String::new(), + error: Some("输入内容不能为空".to_string()), + is_valid: false, + }); + } + + // 调用工具函数进行格式化 + match json_formatter::format_json(input, config) { + Ok(formatted) => Ok(JsonFormatResult { + success: true, + result: formatted, + error: None, + is_valid: true, + }), + Err(err) => Ok(JsonFormatResult { + success: false, + result: String::new(), + error: Some(err), + is_valid: false, + }), + } + } + + /// 验证 JSON 字符串 + /// + /// 检查输入的字符串是否为有效的 JSON + /// + /// # 参数 + /// + /// * `input` - 输入的 JSON 字符串 + /// + /// # 返回 + /// + /// 返回验证结果 + pub fn validate(input: &str) -> AppResult { + // 参数验证 + if input.trim().is_empty() { + return Ok(JsonValidateResult { + is_valid: false, + error_message: Some("输入内容不能为空".to_string()), + error_line: None, + error_column: None, + }); + } + + // 调用工具函数进行验证 + let result = json_formatter::validate_json(input); + Ok(JsonValidateResult { + is_valid: result.is_valid, + error_message: result.error_message, + error_line: result.error_line, + error_column: result.error_column, + }) + } + + /// 压缩 JSON 字符串 + /// + /// 去除 JSON 中的所有空格和换行 + /// + /// # 参数 + /// + /// * `input` - 输入的 JSON 字符串 + /// + /// # 返回 + /// + /// 返回格式化结果 + pub fn compact(input: &str) -> AppResult { + // 参数验证 + if input.trim().is_empty() { + return Ok(JsonFormatResult { + success: false, + result: String::new(), + error: Some("输入内容不能为空".to_string()), + is_valid: false, + }); + } + + // 调用工具函数进行压缩 + match json_formatter::compact_json(input) { + Ok(compacted) => Ok(JsonFormatResult { + success: true, + result: compacted, + error: None, + is_valid: true, + }), + Err(err) => Ok(JsonFormatResult { + success: false, + result: String::new(), + error: Some(err), + is_valid: false, + }), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_format_valid_json() { + let service = JsonFormatService; + let input = r#"{"name":"test","value":123}"#; + let config = JsonFormatConfig::default(); + + let result = service.format(input, &config).unwrap(); + assert!(result.success); + assert!(result.is_valid); + assert!(result.error.is_none()); + assert!(result.result.contains('\n')); + } + + #[test] + fn test_format_invalid_json() { + let service = JsonFormatService; + let input = r#"{"invalid": }"#; + let config = JsonFormatConfig::default(); + + let result = service.format(input, &config).unwrap(); + assert!(!result.success); + assert!(!result.is_valid); + assert!(result.error.is_some()); + } + + #[test] + fn test_format_empty_input() { + let service = JsonFormatService; + let input = ""; + let config = JsonFormatConfig::default(); + + let result = service.format(input, &config).unwrap(); + assert!(!result.success); + assert!(!result.is_valid); + assert!(result.error.is_some()); + } + + #[test] + fn test_validate_valid_json() { + let service = JsonFormatService; + let input = r#"{"valid": true}"#; + + let result = service.validate(input).unwrap(); + assert!(result.is_valid); + assert!(result.error_message.is_none()); + } + + #[test] + fn test_validate_invalid_json() { + let service = JsonFormatService; + let input = r#"{"invalid": }"#; + + let result = service.validate(input).unwrap(); + assert!(!result.is_valid); + assert!(result.error_message.is_some()); + } + + #[test] + fn test_compact_json() { + let service = JsonFormatService; + let input = r#"{ "name" : "test" }"#; + + let result = service.compact(input).unwrap(); + assert!(result.success); + assert!(result.is_valid); + assert_eq!(result.result, r#"{"name":"test"}"#); + } +} diff --git a/src-tauri/src/services/mod.rs b/src-tauri/src/services/mod.rs new file mode 100644 index 0000000..35143f6 --- /dev/null +++ b/src-tauri/src/services/mod.rs @@ -0,0 +1,7 @@ +//! 业务逻辑层 +//! +//! 提供应用的核心业务逻辑实现 + +pub mod json_format_service; +pub mod system_info_service; +pub mod window_service; diff --git a/src-tauri/src/services/system_info_service.rs b/src-tauri/src/services/system_info_service.rs new file mode 100644 index 0000000..a3e0ff9 --- /dev/null +++ b/src-tauri/src/services/system_info_service.rs @@ -0,0 +1,38 @@ +//! 系统信息服务 +//! +//! 提供系统信息查询功能的核心业务逻辑 + +use crate::error::AppResult; +use crate::models::system_info::SystemInfo; +use crate::platforms::system_info::SystemInfoAccessor; + +/// 系统信息服务 +pub struct SystemInfoService; + +impl SystemInfoService { + /// 获取系统信息 + /// + /// 查询并返回当前系统的完整信息 + /// + /// # 返回 + /// + /// 返回包含所有系统信息的结构体 + /// + /// # 错误 + /// + /// 平台不支持或获取信息失败时返回错误 + pub fn get_system_info() -> AppResult { + // 调用平台实现获取系统信息 + #[cfg(windows)] + { + let accessor = crate::platforms::windows::system_info_impl::WindowsSystemInfo; + accessor.get_system_info() + } + + #[cfg(not(windows))] + { + let accessor = crate::platforms::windows::system_info_impl::DummySystemInfo; + accessor.get_system_info() + } + } +} diff --git a/src-tauri/src/services/window_service.rs b/src-tauri/src/services/window_service.rs new file mode 100644 index 0000000..78a880f --- /dev/null +++ b/src-tauri/src/services/window_service.rs @@ -0,0 +1,81 @@ +//! 窗口服务 +//! +//! 提供窗口管理相关的业务逻辑 + +use tauri::Window; +use crate::error::{AppError, AppResult}; + +/// 窗口服务 +/// +/// 提供窗口显示、隐藏和切换等管理功能 +pub struct WindowService; + +impl WindowService { + /// 切换窗口显示/隐藏 + /// + /// 根据窗口当前状态切换显示或隐藏 + /// + /// # 参数 + /// + /// * `window` - Tauri 窗口引用 + /// + /// # 行为 + /// + /// - 如果窗口当前可见,则隐藏窗口 + /// - 如果窗口当前隐藏,则显示窗口并聚焦 + /// + /// # 错误 + /// + /// 窗口操作失败时返回 `AppError::WindowOperationFailed` + pub fn toggle_window(window: &Window) -> AppResult<()> { + let is_visible = window.is_visible() + .map_err(|e| AppError::WindowOperationFailed(e.to_string()))?; + + if is_visible { + Self::hide_window(window)?; + } else { + Self::show_window(window)?; + } + + Ok(()) + } + + /// 隐藏窗口 + /// + /// 将窗口隐藏,使其不再可见 + /// + /// # 参数 + /// + /// * `window` - Tauri 窗口引用 + /// + /// # 错误 + /// + /// 窗口操作失败时返回 `AppError::WindowOperationFailed` + pub fn hide_window(window: &Window) -> AppResult<()> { + window.hide() + .map_err(|e| AppError::WindowOperationFailed(e.to_string())) + } + + /// 显示窗口并聚焦 + /// + /// 显示窗口并将其设置为焦点窗口 + /// + /// # 参数 + /// + /// * `window` - Tauri 窗口引用 + /// + /// # 行为 + /// + /// - 显示窗口 + /// - 将窗口设置为焦点窗口,用户可以直接与之交互 + /// + /// # 错误 + /// + /// 窗口操作失败时返回 `AppError::WindowOperationFailed` + pub fn show_window(window: &Window) -> AppResult<()> { + window.show() + .and_then(|_| window.set_focus()) + .map_err(|e| AppError::WindowOperationFailed(e.to_string())) + } +} + diff --git a/src-tauri/src/utils/color_conversion.rs b/src-tauri/src/utils/color_conversion.rs new file mode 100644 index 0000000..ef25af1 --- /dev/null +++ b/src-tauri/src/utils/color_conversion.rs @@ -0,0 +1,93 @@ +//! 颜色转换工具 +//! +//! 提供颜色空间转换算法实现 + +use crate::models::color::HslInfo; + +/// RGB 转 HSL +/// +/// 将 RGB 颜色值转换为 HSL 颜色值 +/// +/// # 参数 +/// +/// * `r` - 红色分量 (0-255) +/// * `g` - 绿色分量 (0-255) +/// * `b` - 蓝色分量 (0-255) +/// +/// # 返回 +/// +/// 返回 HSL 颜色信息 +/// +/// # 算法说明 +/// +/// 该函数使用标准的 RGB 到 HSL 转换算法: +/// 1. 将 RGB 值归一化到 [0, 1] 范围 +/// 2. 计算最大值和最小值 +/// 3. 根据最大值计算色相(H) +/// 4. 根据最大值和最小值之差计算饱和度(S) +/// 5. 亮度为最大值和最小值的平均值 +/// +/// # 示例 +/// +/// ``` +/// use crate::utils::color_conversion::rgb_to_hsl; +/// +/// let hsl = rgb_to_hsl(255, 0, 0); +/// assert_eq!(hsl.h, 0); // 红色 +/// assert_eq!(hsl.s, 100); // 完全饱和 +/// assert_eq!(hsl.l, 50); // 中等亮度 +/// ``` +pub fn rgb_to_hsl(r: u8, g: u8, b: u8) -> HslInfo { + let r = r as f64 / 255.0; + let g = g as f64 / 255.0; + let b = b as f64 / 255.0; + + let max = r.max(g).max(b); + let min = r.min(g).min(b); + let mut h = 0.0; + let mut s = 0.0; + let l = (max + min) / 2.0; + + if max != min { + let d = max - min; + s = if l > 0.5 { + d / (2.0 - max - min) + } else { + d / (max + min) + }; + + h = match max { + x if x == r => (g - b) / d + if g < b { 6.0 } else { 0.0 }, + x if x == g => (b - r) / d + 2.0, + _ => (r - g) / d + 4.0, + }; + + h /= 6.0; + } + + HslInfo { + h: (h * 360.0).round() as u16, + s: (s * 100.0).round() as u8, + l: (l * 100.0).round() as u8, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_red_color() { + let hsl = rgb_to_hsl(255, 0, 0); + assert_eq!(hsl.h, 0); + assert_eq!(hsl.s, 100); + assert_eq!(hsl.l, 50); + } + + #[test] + fn test_gray_color() { + let hsl = rgb_to_hsl(128, 128, 128); + assert_eq!(hsl.s, 0); + assert_eq!(hsl.l, 50); + } +} diff --git a/src-tauri/src/utils/json_formatter.rs b/src-tauri/src/utils/json_formatter.rs new file mode 100644 index 0000000..18eb942 --- /dev/null +++ b/src-tauri/src/utils/json_formatter.rs @@ -0,0 +1,290 @@ +//! JSON 格式化工具函数 +//! +//! 提供纯函数的 JSON 处理算法 + +use crate::models::json_format::{FormatMode, JsonFormatConfig}; +use serde_json::{self, Value}; + +/// 格式化 JSON 字符串 +/// +/// 对输入的 JSON 字符串进行格式化,支持美化和压缩模式 +/// +/// # 参数 +/// +/// * `input` - 输入的 JSON 字符串 +/// * `config` - 格式化配置 +/// +/// # 返回 +/// +/// 返回格式化后的 JSON 字符串 +/// +/// # 错误 +/// +/// 当输入不是有效的 JSON 时返回错误 +/// +/// # 示例 +/// +/// ``` +/// use crate::utils::json_formatter::format_json; +/// use crate::models::json_format::{JsonFormatConfig, FormatMode}; +/// +/// let input = r#"{"name":"test","value":123}"#; +/// let config = JsonFormatConfig::default(); +/// let result = format_json(input, &config).unwrap(); +/// assert!(result.contains('\n')); +/// ``` +pub fn format_json(input: &str, config: &JsonFormatConfig) -> Result { + // 解析 JSON + let mut value: Value = serde_json::from_str(input) + .map_err(|e| format!("JSON 解析失败: {}", e))?; + + // 如果需要排序 key + if config.sort_keys { + sort_keys(&mut value); + } + + // 根据模式格式化 + match config.mode { + FormatMode::Pretty => { + let indent_str = " ".repeat(config.indent as usize); + serde_json::to_string_pretty(&value) + .map_err(|e| format!("JSON 格式化失败: {}", e)) + .map(|s| replace_indent(&s, &indent_str)) + } + FormatMode::Compact => { + serde_json::to_string(&value) + .map_err(|e| format!("JSON 序列化失败: {}", e)) + } + } +} + +/// 替换缩进空格数 +/// +/// serde_json 默认使用 2 空格缩进,此函数将其替换为配置的缩进数 +fn replace_indent(json: &str, indent: &str) -> String { + if indent == " " { + return json.to_string(); + } + + json.lines() + .map(|line| { + let trimmed = line.trim_start(); + if trimmed.is_empty() { + return String::new(); + } + let leading_spaces = line.len() - trimmed.len(); + if leading_spaces > 0 { + let indent_level = leading_spaces / 2; + format!("{}{}", indent.repeat(indent_level), trimmed) + } else { + line.to_string() + } + }) + .collect::>() + .join("\n") +} + +/// 对 JSON 对象的 key 进行排序 +/// +/// 递归遍历 JSON 结构,对所有对象的 key 按字母顺序排序 +fn sort_keys(value: &mut Value) { + match value { + Value::Object(map) => { + // 收集所有 key-value 对 + let mut entries: Vec<(String, Value)> = map + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); + + // 排序 key + entries.sort_by(|a, b| a.0.cmp(&b.0)); + + // 递归处理每个值 + for (_, v) in &mut entries { + sort_keys(v); + } + + // 清空并重新插入 + map.clear(); + for (k, v) in entries { + map.insert(k, v); + } + } + Value::Array(arr) => { + // 递归处理数组中的每个元素 + for v in arr { + sort_keys(v); + } + } + _ => {} + } +} + +/// 验证 JSON 字符串是否有效 +/// +/// 检查输入的字符串是否为有效的 JSON +/// +/// # 参数 +/// +/// * `input` - 输入的 JSON 字符串 +/// +/// # 返回 +/// +/// 返回验证结果,包含是否有效和错误信息 +/// +/// # 示例 +/// +/// ``` +/// use crate::utils::json_formatter::validate_json; +/// +/// let result = validate_json(r#"{"valid": true}"#); +/// assert!(result.is_valid); +/// +/// let result = validate_json(r#"{"invalid": }"#); +/// assert!(!result.is_valid); +/// ``` +pub fn validate_json(input: &str) -> JsonValidateResult { + match serde_json::from_str::(input) { + Ok(_) => JsonValidateResult { + is_valid: true, + error_message: None, + error_line: None, + error_column: None, + }, + Err(e) => { + // 解析错误信息以获取行号和列号 + let error_msg = e.to_string(); + let (line, column) = parse_error_position(&error_msg); + + JsonValidateResult { + is_valid: false, + error_message: Some(error_msg), + error_line: line, + error_column: column, + } + } + } +} + +/// JSON 验证结果结构 +#[derive(Debug, Clone)] +pub struct JsonValidateResult { + pub is_valid: bool, + pub error_message: Option, + pub error_line: Option, + pub error_column: Option, +} + +/// 从错误信息中解析行号和列号 +fn parse_error_position(error_msg: &str) -> (Option, Option) { + // serde_json 的错误格式通常是 "line X, column Y" + if let Some(line_pos) = error_msg.find("line ") { + let after_line = &error_msg[line_pos + 5..]; + if let Some(comma_pos) = after_line.find(',') { + if let Ok(line) = after_line[..comma_pos].parse::() { + if let Some(col_pos) = after_line.find("column ") { + let after_col = &after_line[col_pos + 7..]; + if let Some(end_pos) = after_col.find(|c: char| !c.is_ascii_digit()) { + if let Ok(col) = after_col[..end_pos].parse::() { + return (Some(line), Some(col)); + } + } + } + } + } + } + (None, None) +} + +/// 压缩 JSON 字符串 +/// +/// 去除所有空格和换行,生成最紧凑的 JSON 格式 +/// +/// # 参数 +/// +/// * `input` - 输入的 JSON 字符串 +/// +/// # 返回 +/// +/// 返回压缩后的 JSON 字符串 +/// +/// # 错误 +/// +/// 当输入不是有效的 JSON 时返回错误 +pub fn compact_json(input: &str) -> Result { + let value: Value = serde_json::from_str(input) + .map_err(|e| format!("JSON 解析失败: {}", e))?; + + serde_json::to_string(&value) + .map_err(|e| format!("JSON 序列化失败: {}", e)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_format_json_pretty() { + let input = r#"{"name":"test","value":123}"#; + let config = JsonFormatConfig::default(); + let result = format_json(input, &config).unwrap(); + assert!(result.contains('\n')); + assert!(result.contains(" ")); + } + + #[test] + fn test_format_json_compact() { + let input = r#"{ "name" : "test" , "value" : 123 }"#; + let config = JsonFormatConfig { + mode: FormatMode::Compact, + ..Default::default() + }; + let result = format_json(input, &config).unwrap(); + assert!(!result.contains('\n')); + assert!(!result.contains(' ')); + } + + #[test] + fn test_format_json_invalid() { + let input = r#"{"invalid": }"#; + let config = JsonFormatConfig::default(); + assert!(format_json(input, &config).is_err()); + } + + #[test] + fn test_format_json_with_sort_keys() { + let input = r#"{"z":1,"a":2,"m":3}"#; + let config = JsonFormatConfig { + sort_keys: true, + ..Default::default() + }; + let result = format_json(input, &config).unwrap(); + // 验证 key 已排序 + let a_pos = result.find("\"a\"").unwrap(); + let m_pos = result.find("\"m\"").unwrap(); + let z_pos = result.find("\"z\"").unwrap(); + assert!(a_pos < m_pos); + assert!(m_pos < z_pos); + } + + #[test] + fn test_validate_json_valid() { + let result = validate_json(r#"{"valid": true}"#); + assert!(result.is_valid); + assert!(result.error_message.is_none()); + } + + #[test] + fn test_validate_json_invalid() { + let result = validate_json(r#"{"invalid": }"#); + assert!(!result.is_valid); + assert!(result.error_message.is_some()); + } + + #[test] + fn test_compact_json() { + let input = r#"{ "name" : "test" }"#; + let result = compact_json(input).unwrap(); + assert_eq!(result, r#"{"name":"test"}"#); + } +} diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs new file mode 100644 index 0000000..379fbc0 --- /dev/null +++ b/src-tauri/src/utils/mod.rs @@ -0,0 +1,8 @@ +//! 工具函数模块 +//! +//! 提供纯函数算法实现,无副作用 + +pub mod color_conversion; +pub mod json_formatter; +pub mod screen; +pub mod shortcut; diff --git a/src-tauri/src/utils/screen.rs b/src-tauri/src/utils/screen.rs new file mode 100644 index 0000000..b61545d --- /dev/null +++ b/src-tauri/src/utils/screen.rs @@ -0,0 +1,177 @@ +//! Windows 屏幕访问模块 +//! +//! 提供屏幕像素颜色获取功能 + +use crate::error::{AppError, AppResult}; +use windows::Win32::Foundation::HWND; +use windows::Win32::Graphics::Gdi::{ + BitBlt, CreateCompatibleBitmap, CreateCompatibleDC, DeleteDC, DeleteObject, GetDC, GetDIBits, + GetPixel, ReleaseDC, SelectObject, BITMAPINFO, BITMAPINFOHEADER, BI_RGB, DIB_RGB_COLORS, + HBITMAP, HGDIOBJ, SRCCOPY, +}; +use windows::Win32::UI::WindowsAndMessaging::{ + GetSystemMetrics, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, +}; + +/// Windows 屏幕访问器 +pub struct WindowsScreen; + +impl WindowsScreen { + /// 获取屏幕指定像素的 RGB 颜色 + /// + /// # 参数 + /// + /// * `x` - 屏幕横坐标(像素) + /// * `y` - 屏幕纵坐标(像素) + /// + /// # 返回 + /// + /// 返回 RGB 三个分量的值,每个分量范围是 0-255 + /// + /// # 错误 + /// + /// 如果无法访问屏幕或坐标无效,返回错误 + pub fn get_pixel_color(x: i32, y: i32) -> AppResult<(u8, u8, u8)> { + unsafe { + let screen_dc = GetDC(HWND::default()); + if screen_dc.is_invalid() { + return Err(AppError::ScreenAccessFailed("无法获取屏幕设备上下文".to_string())); + } + + let color = GetPixel(screen_dc, x, y); + ReleaseDC(HWND::default(), screen_dc); + + // COLORREF 是一个 newtype,包含 u32 值 + // 格式: 0x00BBGGRR (蓝、绿、红) + let color_value = color.0; + + if color_value == 0xFFFFFFFF { + // GetPixel 在失败时返回 CLR_INVALID (0xFFFFFFFF) + return Err(AppError::ScreenAccessFailed("无法获取像素颜色".to_string())); + } + + let r = (color_value & 0xFF) as u8; + let g = ((color_value >> 8) & 0xFF) as u8; + let b = ((color_value >> 16) & 0xFF) as u8; + + Ok((r, g, b)) + } + } + + /// 捕获屏幕指定区域像素(RGBA,行优先,左上角开始) + /// + /// 该函数用于前端放大镜实时预览。 + pub fn capture_region_rgba( + x: i32, + y: i32, + width: i32, + height: i32, + ) -> AppResult> { + if width <= 0 || height <= 0 { + return Err(AppError::ScreenAccessFailed("无效的捕获区域尺寸".to_string())); + } + + // 将捕获区域 clamp 到“虚拟屏幕”范围,避免在屏幕边缘 BitBlt 失败 + let v_left = unsafe { GetSystemMetrics(SM_XVIRTUALSCREEN) }; + let v_top = unsafe { GetSystemMetrics(SM_YVIRTUALSCREEN) }; + let v_w = unsafe { GetSystemMetrics(SM_CXVIRTUALSCREEN) }; + let v_h = unsafe { GetSystemMetrics(SM_CYVIRTUALSCREEN) }; + + // 如果请求区域比虚拟屏幕还大,直接报错(避免溢出/异常) + if width > v_w || height > v_h { + return Err(AppError::ScreenAccessFailed("捕获区域超出屏幕范围".to_string())); + } + + let max_x = v_left + v_w - width; + let max_y = v_top + v_h - height; + let x = x.clamp(v_left, max_x); + let y = y.clamp(v_top, max_y); + + unsafe { + // 屏幕 DC + let screen_dc = GetDC(HWND::default()); + if screen_dc.is_invalid() { + return Err(AppError::ScreenAccessFailed("无法获取屏幕设备上下文".to_string())); + } + + // 内存 DC + 位图 + let mem_dc = CreateCompatibleDC(screen_dc); + if mem_dc.is_invalid() { + ReleaseDC(HWND::default(), screen_dc); + return Err(AppError::ScreenAccessFailed("无法创建兼容设备上下文".to_string())); + } + + let bitmap: HBITMAP = CreateCompatibleBitmap(screen_dc, width, height); + if bitmap.is_invalid() { + let _ = DeleteDC(mem_dc); + ReleaseDC(HWND::default(), screen_dc); + return Err(AppError::ScreenAccessFailed("无法创建兼容位图".to_string())); + } + + let old_obj: HGDIOBJ = SelectObject(mem_dc, bitmap); + + // 拷贝屏幕到位图 + let ok = BitBlt(mem_dc, 0, 0, width, height, screen_dc, x, y, SRCCOPY); + + // 释放 screen dc(尽早) + ReleaseDC(HWND::default(), screen_dc); + + if ok.is_err() { + // 恢复/清理 + let _ = SelectObject(mem_dc, old_obj); + let _ = DeleteObject(bitmap); + let _ = DeleteDC(mem_dc); + return Err(AppError::ScreenAccessFailed("BitBlt 捕获失败".to_string())); + } + + // 准备 BITMAPINFO(32-bit BGRA),并用负高度得到“自顶向下”顺序 + let mut bmi = BITMAPINFO { + bmiHeader: BITMAPINFOHEADER { + biSize: std::mem::size_of::() as u32, + biWidth: width, + biHeight: -height, // top-down + biPlanes: 1, + biBitCount: 32, + biCompression: BI_RGB.0 as u32, + biSizeImage: 0, + biXPelsPerMeter: 0, + biYPelsPerMeter: 0, + biClrUsed: 0, + biClrImportant: 0, + }, + bmiColors: [Default::default(); 1], + }; + + let mut bgra = vec![0u8; (width as usize) * (height as usize) * 4]; + let lines = GetDIBits( + mem_dc, + bitmap, + 0, + height as u32, + Some(bgra.as_mut_ptr() as *mut _), + &mut bmi, + DIB_RGB_COLORS, + ); + + // 恢复/清理 GDI 对象 + let _ = SelectObject(mem_dc, old_obj); + let _ = DeleteObject(bitmap); + let _ = DeleteDC(mem_dc); + + if lines == 0 { + return Err(AppError::ScreenAccessFailed("GetDIBits 读取失败".to_string())); + } + + // BGRA -> RGBA(给前端 canvas 更直接) + for px in bgra.chunks_exact_mut(4) { + let b = px[0]; + let r = px[2]; + px[0] = r; + px[2] = b; + // px[1] = g, px[3] = a 保持 + } + + Ok(bgra) + } + } +} diff --git a/src-tauri/src/utils/shortcut.rs b/src-tauri/src/utils/shortcut.rs new file mode 100644 index 0000000..5fce4af --- /dev/null +++ b/src-tauri/src/utils/shortcut.rs @@ -0,0 +1,54 @@ +//! 全局快捷键工具 +//! +//! 将快捷键注册逻辑从 `lib.rs` 拆分出来,避免入口文件过于拥挤。 + +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use tauri::Manager; +use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, Modifiers, Shortcut, ShortcutState}; + +/// 注册全局快捷键 +/// +/// - `Alt+Space`: 切换主窗口显示/隐藏 +pub fn register_global_shortcuts(app: &tauri::App) -> Result<(), String> { + let shortcut = Shortcut::new(Some(Modifiers::ALT), Code::Space); + let app_handle = app.handle().clone(); + let is_processing = Arc::new(AtomicBool::new(false)); + let is_processing_clone = is_processing.clone(); + + app.global_shortcut() + .on_shortcut(shortcut, move |_app_handle, _shortcut, event| { + // 忽略按键释放事件 + if event.state == ShortcutState::Released { + return; + } + + // 防止重复触发 + if is_processing_clone.load(Ordering::SeqCst) { + return; + } + is_processing_clone.store(true, Ordering::SeqCst); + + if let Some(window) = app_handle.get_webview_window("main") { + let _ = window.is_visible().and_then(|is_visible| { + if is_visible { + window.hide() + } else { + window.show().and_then(|_| window.set_focus()) + } + }); + } + + // 延迟重置处理标志,防止快速重复触发 + let is_processing_reset = is_processing_clone.clone(); + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_millis(500)); + is_processing_reset.store(false, Ordering::SeqCst); + }); + }) + .map_err(|e| format!("注册全局快捷键失败: {}", e))?; + + println!("全局快捷键 Alt+Space 注册成功"); + Ok(()) +} + diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e25f99d..83ad5a8 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "$schema": "https://schema.tauri.app/config/2", - "productName": "tauri-app", + "productName": "CmdRs", "version": "0.1.0", "identifier": "com.shenjianz.tauri-app", "build": { @@ -12,11 +12,19 @@ "app": { "windows": [ { - "title": "tauri-app", - "width": 800, - "height": 600, - "minWidth": 600, - "minHeight": 500 + "title": "CmdRs - 功能集合", + "label": "main", + "width": 1200, + "height": 800, + "minWidth": 800, + "minHeight": 600, + "decorations": true, + "transparent": false, + "alwaysOnTop": false, + "skipTaskbar": false, + "visible": false, + "center": true, + "resizable": true } ], "security": { diff --git a/src/App.tsx b/src/App.tsx index 1491e8b..440e58d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,24 +1,32 @@ -import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; -import { MainLayout } from "@/components/layout/MainLayout"; -import { Dashboard } from "@/pages/Dashboard"; -import { Features } from "@/pages/Features"; -import { Components } from "@/pages/Components"; +import { Routes, Route, Navigate } from "react-router-dom"; import { ThemeProvider } from "@/components/theme-provider"; +import { Home } from "@/pages/Home"; +import { Search } from "@/pages/Search"; +import Settings from "@/pages/Settings"; +import { CommandPalette } from "@/components/command-palette/CommandPalette"; +import { ColorPickerPage } from "@/components/features/ColorPicker/ColorPickerPage"; +import { JsonFormatterPage } from "@/components/features/JsonFormatter/JsonFormatterPage"; +import { SystemInfoPage } from "@/components/features/SystemInfo/SystemInfoPage"; function App() { return ( - - - - - } /> - } /> - } /> - } /> - - - - + +
+ {/* 全局快捷键监听 */} + + + {/* 路由配置 */} + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +
+
); } diff --git a/src/components/command-palette/CommandPalette.tsx b/src/components/command-palette/CommandPalette.tsx new file mode 100644 index 0000000..9486f8f --- /dev/null +++ b/src/components/command-palette/CommandPalette.tsx @@ -0,0 +1,32 @@ +import { useEffect } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { listen } from '@tauri-apps/api/event'; + +/** + * 全局快捷键处理组件 + * 负责监听全局快捷键并控制窗口显示/隐藏 + */ +export function CommandPalette() { + useEffect(() => { + // 监听热键事件 + const unlistenPromise = listen('hotkey-pressed', async () => { + await invoke('toggle_window'); + }); + + // 清理监听器 + return () => { + unlistenPromise.then((unlisten) => unlisten()); + }; + }, []); + + // 这个组件不渲染任何可见 UI + return null; +} + +// 保留 SearchResult 类型以供其他组件使用 +export interface SearchResult { + id: string; + title: string; + description?: string; + icon?: string; +} diff --git a/src/components/command-palette/ResultItem.tsx b/src/components/command-palette/ResultItem.tsx new file mode 100644 index 0000000..4b06354 --- /dev/null +++ b/src/components/command-palette/ResultItem.tsx @@ -0,0 +1,45 @@ +import type { SearchResult } from './CommandPalette'; +import { AppWindow } from 'lucide-react'; + +interface ResultItemProps { + result: SearchResult; + isSelected: boolean; + onClick: () => void; +} + +export function ResultItem({ result, isSelected, onClick }: ResultItemProps) { + const IconComponent = AppWindow; // 默认图标 + + return ( +
+ {/* 图标 */} +
+ +
+ + {/* 内容 */} +
+
{result.title}
+ {result.description && ( +
+ {result.description} +
+ )} +
+ + {/* 快捷键提示(可选) */} + {result.icon && ( +
+ {result.icon} +
+ )} +
+ ); +} diff --git a/src/components/command-palette/ResultList.tsx b/src/components/command-palette/ResultList.tsx new file mode 100644 index 0000000..eb82264 --- /dev/null +++ b/src/components/command-palette/ResultList.tsx @@ -0,0 +1,31 @@ +import { ResultItem } from './ResultItem'; +import type { SearchResult } from './CommandPalette'; + +interface ResultListProps { + results: SearchResult[]; + selectedIndex: number; + onSelect: (index: number) => void; +} + +export function ResultList({ results, selectedIndex, onSelect }: ResultListProps) { + if (results.length === 0) { + return ( +
+

输入搜索内容开始...

+
+ ); + } + + return ( +
+ {results.map((result, index) => ( + onSelect(index)} + /> + ))} +
+ ); +} diff --git a/src/components/command-palette/SearchInput.tsx b/src/components/command-palette/SearchInput.tsx new file mode 100644 index 0000000..61c0d44 --- /dev/null +++ b/src/components/command-palette/SearchInput.tsx @@ -0,0 +1,39 @@ +import { Search } from 'lucide-react'; +import { forwardRef, useEffect } from 'react'; + +interface SearchInputProps { + value: string; + onChange: (value: string) => void; + placeholder?: string; +} + +export const SearchInput = forwardRef( + ({ value, onChange, placeholder = '搜索...' }, ref) => { + // 内部 ref 用于自动聚焦 + const innerRef = ref || useEffect; + + useEffect(() => { + // 自动聚焦到输入框 + (innerRef as any)?.current?.focus(); + }, []); + + return ( +
+
+ +
+ onChange(e.target.value)} + placeholder={placeholder} + className="w-full pl-10 pr-4 py-3 bg-muted/50 rounded-lg border border-border focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent transition-all" + autoComplete="off" + /> +
+ ); + } +); + +SearchInput.displayName = 'SearchInput'; diff --git a/src/components/features/ColorPicker/ColorPickerPage.tsx b/src/components/features/ColorPicker/ColorPickerPage.tsx new file mode 100644 index 0000000..5c49cd1 --- /dev/null +++ b/src/components/features/ColorPicker/ColorPickerPage.tsx @@ -0,0 +1,237 @@ +import { useState, useCallback, useEffect } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { listen } from '@tauri-apps/api/event'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { Input } from '@/components/ui/input'; +import { Copy, Check, Droplet, RefreshCw } from 'lucide-react'; + +interface ColorInfo { + hex: string; + rgb: { r: number; g: number; b: number }; + hsl: { h: number; s: number; l: number }; + x: number; + y: number; +} + +interface ColorHistory { + color: ColorInfo; + timestamp: number; +} + +export function ColorPickerPage() { + const [currentColor, setCurrentColor] = useState(null); + const [history, setHistory] = useState([]); + const [copied, setCopied] = useState(''); + const [isPicking, setIsPicking] = useState(false); + + // 监听从取色器窗口返回的颜色 + useEffect(() => { + const unlistenPicked = listen('color-picked', (event) => { + setCurrentColor(event.payload); + setHistory(prev => [{ color: event.payload, timestamp: Date.now() }, ...prev].slice(0, 10)); + setIsPicking(false); + }); + + const unlistenCancelled = listen('color-picker-cancelled', () => { + setIsPicking(false); + }); + + return () => { + unlistenPicked.then(fn => fn()); + unlistenCancelled.then(fn => fn()); + }; + }, []); + + // 开始拾色 + const pickColor = useCallback(async () => { + try { + setIsPicking(true); + + // 调用启动取色器命令,打开透明遮罩窗口 + await invoke('start_color_picker'); + } catch (error) { + console.error('拾色失败:', error); + alert('取色失败: ' + String(error)); + setIsPicking(false); + } + }, []); + + // 复制到剪贴板 + const copyToClipboard = useCallback(async (text: string, type: string) => { + try { + await navigator.clipboard.writeText(text); + setCopied(type); + setTimeout(() => setCopied(''), 2000); + } catch (error) { + console.error('复制失败:', error); + } + }, []); + + return ( +
+ {/* 顶部导航栏 */} +
+
+
+ +
+ +

取色器

+
+
+
+
+ + {/* 主内容区 */} +
+
+ {/* 拾色按钮 */} + + +
+
+

屏幕取色

+

+ 点击按钮后,窗口会隐藏。移动鼠标到目标位置,点击左键确认取色。 +

+
+ +
+
+
+ + {/* 当前颜色 */} + {currentColor && ( + + + 当前颜色 + + 位置: ({currentColor.x}, {currentColor.y}) + + + + {/* 颜色预览 */} +
+
+
+ {/* HEX */} +
+ HEX + + +
+ + {/* RGB */} +
+ RGB + + +
+ + {/* HSL */} +
+ HSL + + +
+
+
+ + + )} + + {/* 历史记录 */} + {history.length > 0 && ( + + + 历史记录 + 最近拾取的颜色 + + +
+ {history.map((item, index) => ( +
+
+
+ )} + + {/* 使用说明 */} + + + 使用说明 + + +

1. 点击"开始拾色"按钮,窗口会自动隐藏

+

2. 将鼠标移动到屏幕上想要取色的位置

+

3. 点击鼠标左键确认取色,窗口自动恢复

+

4. 颜色会以多种格式显示,点击复制按钮即可复制

+

5. 点击历史记录中的色块可以快速切换回该颜色

+
+
+
+
+
+ ); +} diff --git a/src/components/features/JsonFormatter/JsonFormatterPage.tsx b/src/components/features/JsonFormatter/JsonFormatterPage.tsx new file mode 100644 index 0000000..a840dcf --- /dev/null +++ b/src/components/features/JsonFormatter/JsonFormatterPage.tsx @@ -0,0 +1,402 @@ +import { useState, useCallback, useEffect } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { Copy, Check, FileCode, Sparkles, Minimize2, CheckCircle2, XCircle, Upload } from 'lucide-react'; + +// 类型定义 +interface JsonFormatConfig { + indent: number; + sort_keys: boolean; + mode: 'pretty' | 'compact'; +} + +interface JsonFormatResult { + success: boolean; + result: string; + error: string | null; + is_valid: boolean; +} + +interface JsonValidateResult { + is_valid: boolean; + error_message: string | null; + error_line: number | null; + error_column: number | null; +} + +export function JsonFormatterPage() { + const [input, setInput] = useState(''); + const [output, setOutput] = useState(''); + const [validation, setValidation] = useState(null); + const [config, setConfig] = useState({ + indent: 2, + sort_keys: false, + mode: 'pretty', + }); + const [copied, setCopied] = useState(false); + const [isProcessing, setIsProcessing] = useState(false); + + // 监听输入变化,自动验证 + useEffect(() => { + if (input.trim()) { + validateJson(); + } else { + setValidation(null); + } + }, [input]); + + // 验证 JSON + const validateJson = useCallback(async () => { + if (!input.trim()) { + setValidation(null); + return; + } + + try { + const result = await invoke('validate_json', { + input, + }); + setValidation(result); + } catch (error) { + console.error('验证失败:', error); + } + }, [input]); + + // 格式化 JSON + const formatJson = useCallback(async () => { + if (!input.trim()) { + return; + } + + setIsProcessing(true); + try { + const result = await invoke('format_json', { + input, + config, + }); + + if (result.success) { + setOutput(result.result); + } else { + setOutput(result.error || '格式化失败'); + } + } catch (error) { + console.error('格式化失败:', error); + setOutput('错误: ' + String(error)); + } finally { + setIsProcessing(false); + } + }, [input, config]); + + // 压缩 JSON + const compactJson = useCallback(async () => { + if (!input.trim()) { + return; + } + + setIsProcessing(true); + try { + const result = await invoke('compact_json', { + input, + }); + + if (result.success) { + setOutput(result.result); + } else { + setOutput(result.error || '压缩失败'); + } + } catch (error) { + console.error('压缩失败:', error); + setOutput('错误: ' + String(error)); + } finally { + setIsProcessing(false); + } + }, [input]); + + // 复制到剪贴板 + const copyToClipboard = useCallback(async () => { + if (!output) return; + + try { + await navigator.clipboard.writeText(output); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (error) { + console.error('复制失败:', error); + } + }, [output]); + + // 清空输入 + const clearInput = useCallback(() => { + setInput(''); + setOutput(''); + setValidation(null); + }, []); + + // 使用示例 + const loadExample = useCallback(() => { + const example = { + "name": "JSON 格式化工具", + "version": "1.0.0", + "features": ["格式化", "验证", "压缩"], + "config": { + "indent": 2, + "sortKeys": false + }, + "active": true + }; + setInput(JSON.stringify(example)); + }, []); + + return ( +
+ {/* 顶部导航栏 */} +
+
+
+ +
+ +

JSON 格式化工具

+
+
+
+
+ + {/* 主内容区 */} +
+
+ {/* 配置选项 */} + + + 配置选项 + 自定义 JSON 格式化行为 + + +
+ {/* 缩进空格数 */} +
+ +
+ {[2, 4].map((spaces) => ( + + ))} +
+
+ + {/* 排序 Keys */} +
+ + +
+ + {/* 格式化模式 */} +
+ +
+ {(['pretty', 'compact'] as const).map((mode) => ( + + ))} +
+
+
+
+
+ + {/* 输入输出区域 */} +
+ {/* 输入区域 */} + + +
+
+ 输入 JSON + 粘贴或输入 JSON 数据 +
+
+ + {input && ( + + )} +
+
+
+ +
+