Compare commits

..

2363 Commits
main ... v2.2.1

Author SHA1 Message Date
Tunglies
20e93ecc8e Revert "chore: updater channel combined"
This reverts commit aeaab10fcbe29e792285fec97ecb154087dfb7a9.
2025-03-25 01:38:52 +08:00
Tunglies
aeaab10fcb chore: updater channel combined 2025-03-25 01:32:37 +08:00
wonfen
cbbd99a748 refactor: service reinstallation logic on detection failure 2025-03-25 00:51:38 +08:00
Tunglies
228b013249 chore: remove update endpoints temporary 2025-03-25 00:28:28 +08:00
Tunglies
55fdce58d0 feat: add default update log resolution and improve error handling 2025-03-25 00:23:19 +08:00
Tunglies
c1abb78cf9 refactor: remove deprecated lightweight module and adjust macOS activation policy 2025-03-24 20:11:38 +08:00
Tunglies
53023fa189 fix: update lightweight mode implementation and fix MacOS dock icon visibility 2025-03-24 19:16:21 +08:00
Tunglies
827ab4b1c5 fix: correct spelling of "Lightweight" in tray menu 2025-03-24 18:54:39 +08:00
Skrepysh
2fbcbafc53
Update russian translation (#3109) 2025-03-24 18:34:50 +08:00
wonfen
ab140d6990 chore: update required service version 2025-03-24 03:59:55 +08:00
Tunglies
0e38ed7112 chore: alpha update cron 2025-03-23 23:04:40 +08:00
Tunglies
5e3ff37602 chore: update bug_report.yml 2025-03-23 22:56:42 +08:00
Tunglies
3f84368fc0 feat: add option to enable/disable tray icon display on MacOS 2025-03-23 16:37:27 +08:00
Tunglies
6ded03c616 chore: remove unused import 2025-03-23 14:28:28 +08:00
wonfen
15fd8b17c3 perf: improve Clash mode switch responsiveness on home card 2025-03-23 04:54:18 +08:00
Tunglies
7cb0c8173a feat: add lightweight mode entry and related hotkey support 2025-03-23 03:10:48 +08:00
wonfen
9b04721b3d perf: simplify code logic and improve efficiency 2025-03-23 02:16:06 +08:00
wonfen
bc39d56b0f chore: update required service version 2025-03-23 00:16:32 +08:00
Tunglies
a1f671f380 feat: enhance tray icon handling with caching and speed rate rendering 2025-03-22 23:00:45 +08:00
Tunglies
147e7d7390 chore: update UPDATELOG 2025-03-22 17:23:10 +08:00
Tunglies
e9684fd639 fix: update alpha workflow to correctly extract alpha logs from UPDATELOG.md 2025-03-22 17:21:45 +08:00
Tunglies
d7e9d9498d feat: update versioning in package.json, Cargo.toml, and tauri.conf.json to append '-alpha' 2025-03-22 17:19:11 +08:00
Tunglies
b8161b01c1 feat: ensure Mihomo and Verge services are running before executing commands 2025-03-22 17:05:42 +08:00
wonfen
079591da59 feat: add singleton check after core startup in sidecar mode 2025-03-22 15:01:55 +08:00
wonfen
1461ab0d37 chore: update preview img 2025-03-22 11:07:20 +08:00
wonfen
c37a69ea1b fix: alpha workflow 2025-03-22 08:45:55 +08:00
wonfen
f78977e267 chore: remove unused code of current proxy card 2025-03-22 06:25:10 +08:00
Tunglies
85124549d7 feat: add scripts for managing alpha versioning and update workflow 2025-03-22 05:11:32 +08:00
Tunglies
90f99a3879 chore: update UPDATELOG 2025-03-22 04:53:27 +08:00
Tunglies
8336c43e86 fix: linux hanlding mihomo-core and verge-service communication 2025-03-22 04:51:38 +08:00
wonfen
dbbd7a1ed7 perf: optimize proxy refresh mechanism for home page current proxy card 2025-03-22 04:34:19 +08:00
wonfen
e246cf33da fix: theme color on connection detail card
fix: home page clash info card proxy address
2025-03-22 04:26:28 +08:00
wonfen
22ae6bc583 fix: port setting sync problem 2025-03-21 12:28:51 +08:00
wonfen
9d13063d53 chore: add missing i18n 2025-03-21 10:29:20 +08:00
wonfen
47d581f042 refactor: auto-truncate long text on home profile card
fix: sync system proxy and TUN mode status indicators on home proxy mode card
2025-03-21 05:23:45 +08:00
Tunglies
6a1587ec73 fix: add libxslt1.1 dependency to Ubuntu installation in workflows 2025-03-20 23:36:51 +08:00
Tunglies
fe891d23d2 v2.2.1 2025-03-20 23:22:34 +08:00
Tunglies
07c151b287 v2.2.1-alpha.1 2025-03-20 23:20:07 +08:00
Tunglies
51d61a4bb7 fix: homepage entry lightweight mode exiting Macos tray icon
fix: lightweight mode better handling and logging logic
2025-03-20 23:17:37 +08:00
Tunglies
37e63ec67c chore: v2.2.1 2025-03-20 22:33:20 +08:00
Tunglies
106861f761 fix: homepage proxy card handle direct mode 2025-03-20 21:51:12 +08:00
Tunglies
edb9ccdca9 feat: enhance alpha release workflow to fetch update logs and generate release notes 2025-03-20 19:42:04 +08:00
Tunglies
6dfa53fc23 fix: update workflow to delete old release assets instead of the release itself 2025-03-20 19:33:26 +08:00
Tunglies
abe0750d07 chore: update version to 2.2.1-alpha and add alpha update logs
refactor: simplify version change detection in alpha workflow

chore: alpha delete old release then release new one

fix: update alpha workflow to handle missing ALPHA_LOGS and improve release notes generation

fix: update job dependencies in alpha workflow to include delete_otld_release
2025-03-20 19:11:48 +08:00
Tunglies
2195576553 fix: remove macOS application menu setup due to CMD+C/V/A issues 2025-03-20 18:14:18 +08:00
Tunglies
1dc015f411 chore: bug report template remove os label 2025-03-20 16:39:29 +08:00
Tunglies
49d13c9ecb chore: UPDATELOG update 2025-03-20 15:49:00 +08:00
Tunglies
d8cd91b725 fix: ensure main window title is set correctly on macOS 2025-03-20 15:43:59 +08:00
wonfen
40eede325b release 2.2.0 2025-03-20 14:44:26 +08:00
Tunglies
59e07a3e55 Revert "fix: update permission config with app icon and name"
This reverts commit 46af096cb1152fa7ba7c7b5ff82daf949079fca5.
2025-03-20 14:17:41 +08:00
Tunglies
3d32d2d08f feat(hotkey): macos support CMD+W to close window as default 2025-03-20 13:02:26 +08:00
wonfen
46af096cb1 fix: update permission config with app icon and name 2025-03-20 12:43:22 +08:00
Tunglies
486f33fd7a fix: update system architecture retrieval method in PlatformSpecification 2025-03-20 06:20:19 +08:00
Tunglies
5ebfcc6f98 fix: update service response check for correct status code and message 2025-03-20 06:18:14 +08:00
Tunglies
7fa1767645 feat: implement auto lightweight mode timer functionality
This commit implements the automatic lightweight mode feature with timer functionality:

- Rename configuration properties from auto_enter_lite_mode to enable_auto_light_weight_mode and auto_enter_lite_mode_delay to auto_light_weight_minutes for better clarity
- Add window event listeners to detect when window is closed or gets focus
- Implement timer system to automatically enter lightweight mode after configured time
- Remove exit_lightweight_mode function as it's no longer needed with the new implementation
- Update UI components to reflect the new property names
- Add logging for lightweight mode operations
- Initialize lightweight mode based on user configuration at startup

The feature now allows users to set a timer that will automatically enter lightweight mode
after closing the main window, which can be cancelled by focusing the window again.
2025-03-20 06:01:38 +08:00
Tunglies
33d54b27f4 feat: implement lightweight mode functionality and update related settings 2025-03-20 03:23:14 +08:00
wonfen
f8dbc7bea0 feat: lite mode settings 2025-03-20 01:44:43 +08:00
Tunglies
83d338eaa4 fix: hotkeys on windows crash 2025-03-19 18:41:26 +08:00
wonfen
9732b2be14 chore: update deps 2025-03-19 11:07:57 +08:00
wonfen
8a5232df20 feat: add up/down name to home traffic graph card 2025-03-19 10:38:21 +08:00
wonfen
5f0f6db0d5 chore: change defaut start page to home 2025-03-19 05:48:20 +08:00
wonfen
7a86e4bd85 fix: cannot detect service mode on home card 2025-03-19 05:25:38 +08:00
Tunglies
f18a8c901f feat: add lite mode toggle to home page 2025-03-19 02:18:20 +08:00
Tunglies
23d161c298 refactor: improve proxy retrieval and add window destruction method 2025-03-19 02:04:01 +08:00
Tunglies
3e2148e236 feat: add macOS application menu integration 2025-03-18 18:40:53 +08:00
Tunglies
70a4bd1074 chore: remove tray icon configuration from webview JSON files 2025-03-18 15:31:23 +08:00
Tunglies
e13d3e7f83 Revert "feat: add Rust installation step and configure alpha release details"
This reverts commit 1d5471e70dc2da7d15df3fdd8566e14023384f90.
2025-03-18 14:37:56 +08:00
wonfen
e21bc22fa3 chore: add missing colorful svg for home and unlock menu 2025-03-18 10:37:00 +08:00
wonfen
d8e85c9cfc refactor: revise data retrieval for homepage traffic stats 2025-03-18 09:05:44 +08:00
Tunglies
1d5471e70d feat: add Rust installation step and configure alpha release details 2025-03-18 01:08:46 +08:00
wonfen
a2a8dd182a feat: persist graph data after page reload 2025-03-18 00:37:10 +08:00
wonfen
511fb9e2cf feat: limit max url lenght on home profile card 2025-03-18 00:18:26 +08:00
wonfen
14f90ac925 fix: auto launch 2025-03-17 13:51:52 +08:00
wonfen
8e09bf525e fix: resolve Netflix detection error 2025-03-17 11:57:12 +08:00
wonfen
7e7ef5766e perf: optimize all home page components 2025-03-17 11:47:02 +08:00
wonfen
b078560ac1 feat: sync auto-start status 2025-03-17 09:48:44 +08:00
wonfen
1e481ed1c1 chore: update i18n for unlock test 2025-03-17 07:45:49 +08:00
wonfen
e957c10138 perf: optimize CPU and memory usage of homepage traffic chart 2025-03-16 14:34:29 +08:00
wonfen
79bb0f29f9 fix: sync proxy node selection 2025-03-16 14:24:58 +08:00
wonfen
f4cb978118 feat: unlock test page 2025-03-16 12:15:35 +08:00
Tunglies
e16b23946a refactor(timer): improve timer management with robust error handling
This commit improves the timer management system with the following enhancements:

Replace Mutex with RwLock for better read concurrency in timer state
Add structured TimerTask type to store task metadata
Use atomic boolean for initialization flag instead of mutex
Implement comprehensive error handling with detailed logging
Add rollback capability when task operations fail
Reduce lock contention by generating task diffs outside locks
Add timing metrics for task execution
Improve code organization and documentation
2025-03-15 18:58:12 +08:00
Tunglies
df538ca9a3 refactor(config): use bitflags for tracking update operations
Replace multiple boolean variables with a bitflag approach for tracking
required update operations in the patch_verge function. This improves
code maintainability and potentially performance by:

1. Using a single integer variable with bit operations instead of multiple booleans
2. Defining clear flags as enum variants for better code readability
3. Simplifying flag checks with bitwise operations

The UpdateFlags enum provides a clear and type-safe way to represent
different types of updates needed when patching Verge configuration.
2025-03-15 18:42:57 +08:00
Tunglies
b97f0734d0 Optimize hotkey management to reduce lock contention and improve performance
- Minimize mutex lock durations in update() by processing data outside critical sections
- Pre-allocate collections to avoid unnecessary reallocations
- Replace forEach-style loops with more efficient for loops
- Add defensive null checks when accessing app_handle
- Improve error handling with more robust Option unwrapping
- Enhance code readability with descriptive comments
2025-03-15 17:52:14 +08:00
Tunglies
cbd2dd972a structure: move out crate_mihomo_api 2025-03-15 14:47:02 +08:00
Tunglies
00d6bcc00f refactor: update request method handling to use reqwest::Method enum
fix: duplicated checks tray menu
2025-03-15 13:23:17 +08:00
Tunglies
43ffb3c9e0 fix: optimize speed rate update logic for small values 2025-03-14 22:40:56 +08:00
wonfen
9fd75f0efb feat: home page 2025-03-14 13:31:34 +08:00
Tunglies
49f0ac407e Revert "feat: add trigger updater workflow to GitHub actions for release and alpha workflows"
This reverts commit ff8426d95e118fca572e98f618f28bb5b3d4d271.
2025-03-14 02:00:55 +08:00
Tunglies
ff8426d95e feat: add trigger updater workflow to GitHub actions for release and alpha workflows 2025-03-14 01:31:19 +08:00
GKarbon
3ca4ac2726
chore: correct typo in src/locales/en.json (#2978)
This pull request corrects a typo in src/locales/en.json by replacing “than” with “then”, gives more accurate instructions.
2025-03-14 01:04:44 +08:00
Christine.
07441ba95e
fix: build failed due to vite unexpected output dir config. (#2981) 2025-03-14 00:57:17 +08:00
Tunglies
e74fd068d5 chore: remove tray icon configuration from Linux app settings 2025-03-13 19:03:27 +08:00
Tunglies
965e281348 fix: increase request timeout to 60 seconds for better reliability 2025-03-13 15:16:54 +08:00
Tunglies
a7f66c3d65 chore: git hooks for linter and formatter 2025-03-13 12:51:20 +08:00
Mimi
f7f15544ba feat: additional macos tray event handling option for menu display (#2958) 2025-03-13 10:08:38 +08:00
MaqicXu
6ea79daf73
refactor(timer): enhance timer initialization and task handling (#2956)
- Add initialization flag to prevent duplicate timer initialization
- Improve logging for better debugging and monitoring
- Refactor async task function with proper error handling
- Add more detailed log messages throughout the timer lifecycle
2025-03-12 22:36:25 +08:00
Christine.
99abbfbc4c
chore: Change default TUN stack from 'mixed' to 'gvisor' (#2967) 2025-03-12 21:45:14 +08:00
Tunglies
24df5e02d6 fix: windows unmatched tray 2025-03-12 13:55:11 +08:00
Tunglies
6aaec3de5d fix: windows different tray icon display 2025-03-12 13:28:04 +08:00
Tunglies
39d811cc62 feat: update systray creation to use TrayIconBuilder and pass app reference
fix: macos systray duplicated icon
2025-03-12 13:04:15 +08:00
Tunglies
832a0ac281 feat: add alpha update endpoints to tauri configuration 2025-03-11 01:31:31 +08:00
Tunglies
7b0df043be feat: add support for alpha updates and enhance updater functionality
feat: improve release handling by adding creation logic for non-existent releases
2025-03-11 01:27:17 +08:00
Tunglies
d470bf2dd9 rm: label issues workflow 2025-03-10 01:01:12 +08:00
Tunglies
b5e8a1ddb5 chore: rename "DNS Settings" to "DNS Overwrite" in UI for consistency
This change updates the label for the DNS toggle setting from "DNS Settings" to "DNS Overwrite"
in the ClashVerge settings interface. The change provides better consistency with the translation
keys and more clearly communicates the function of the setting, which is to override system DNS.
The corresponding translation keys have been updated in both English and Chinese localization files.
2025-03-10 00:57:55 +08:00
Tunglies
08f3357d9a feat: add scheduled workflow and commit change check to alpha build 2025-03-09 17:42:13 +08:00
Tunglies
107c54921d refactor: update MihomoManager to handle traffic WebSocket URL and authorization 2025-03-09 14:44:15 +08:00
wonfen
ca1f7a2b31 feat: enhance latency test logging and error handling 2025-03-09 04:22:34 +08:00
wonfen
95e8fe38f2 refactor: improve webSocket connection handling and error recovery 2025-03-09 04:22:01 +08:00
wonfen
cd6eb26a41 feat: add icon file content check to prevent saving failed downloads 2025-03-09 02:34:57 +08:00
Tunglies
9f2c4b4f35 refactor: migrate clash client info retrieval to MihomoManager 2025-03-09 00:40:16 +08:00
Tunglies
adbf98cb15 rm: clash-api dead code 2025-03-09 00:29:14 +08:00
Tunglies
c9b3fec09c refactor: improve request handling and response processing in MihomoManager 2025-03-09 00:27:12 +08:00
Tunglies
364fb16b7b refactor: streamline clash delay test and improve API interactions 2025-03-09 00:04:48 +08:00
Tunglies
634ed9cb46 refactor: streamline clash mode handling and improve API interactions 2025-03-08 22:41:14 +08:00
wonfen
cddb117402 feat: optimize icon cache download and DNS view styling 2025-03-08 13:31:20 +08:00
wonfen
bd6c4e00c4 refactor: restructure DNS setting logic 2025-03-08 11:25:00 +08:00
wonfen
bbcfb4901f refactor: refine DNS handling to follow config and merge settings 2025-03-08 03:34:25 +08:00
wonfen
230a0eeb64 fix: regex for first character matching 2025-03-07 14:07:10 +08:00
wonfen
c4c49f61df perf: faster app exit 2025-03-07 13:44:07 +08:00
TianHua Liu
6d5c4f86d3
fix: Notice @ts-ignore (#2896)
thx
2025-03-07 12:46:30 +08:00
0XE
59f22b9cd2
feat: Add x-data-grid component localization (#2925)
thx
2025-03-07 12:45:56 +08:00
wonfen
6caf2cd057 test: remove entitlements.plist items 2025-03-07 06:49:24 +08:00
Tunglies
684128ca68 Update dependencies and refactor encryption logic
Updates multiple dependencies to their latest versions in Cargo.lock and Cargo.toml.
Refactors encryption logic to use updated getrandom API.
Improves tray speed rate display by using ab_glyph for font rendering.
2025-03-06 18:56:31 +08:00
wonfen
6925adc66f feat: enable dns settings by default 2025-03-06 14:40:35 +08:00
wonfen
e1e9d91d86 feat: add dns settings 2025-03-06 14:30:43 +08:00
Christine.
881c136b1e
add: missing i18n text. (#2917) 2025-03-05 22:28:28 +08:00
0XE
5bc699b19c
fix: ISSUES #2727 (#2913) 2025-03-05 14:20:07 +08:00
Christine.
52ba1c5a30
workflow: remove renaming behavior. (#2909) 2025-03-05 11:41:15 +08:00
Tunglies
817d68546e
feat(mihomo): refactor MihomoManager for global access and improve proxy retrieval (#2906) 2025-03-05 10:58:54 +08:00
wonfen
c0f5c231ad feat: add admin permission prompt for system service 2025-03-05 10:22:57 +08:00
Tunglies
9677d8670b test: crate_mihomo_api additional headers 2025-03-05 08:14:37 +08:00
Tunglies
b01630d31c fix: extern controler api secert with headers 2025-03-05 08:09:42 +08:00
Tunglies
f771f4720f
refacture: Mihomo API integration (#2900)
* feat: add mihomo_api crate as a workspace member

Added a new mihomo_api crate to handle interactions with the Mihomo API. This modular approach provides a dedicated interface for fetching and managing proxy data from Mihomo servers. The implementation includes functionality to refresh and retrieve both proxies and provider proxies with proper error handling and timeouts. Added this crate as a workspace member and included it as a dependency in the main project.

* Refactors Mihomo API integration

Simplifies proxy fetching by removing the MihomoManager structure.

Updates the get_proxies and get_providers_proxies functions to directly use the mihomo_api module.

Removes unused Mihomo API related files and modules for cleaner codebase.

Enhances overall maintainability and performance.
2025-03-05 00:45:08 +08:00
0XE
f0becb5003
fix: issues #2838 (#2886) 2025-03-04 20:46:17 +08:00
Tunglies
405e8df825
feat(sysinfo): Add diagnostic information enhancements (#2880)
Enhanced the PlatformSpecification struct with additional diagnostic information including:
- Added Verge version information to diagnostic output
- Added running mode information (Service/Sidecar/Not Running)
- Improved Debug implementation to display all diagnostic fields
- Implemented asynchronous detection of core running mode

This change helps users provide more complete system information when reporting issues.
2025-03-04 11:52:22 +08:00
wonfen
49fc7ff7ca fix: sync system proxy status indicator with hotkey 2025-03-04 06:57:42 +08:00
wonfen
fde9c8aaee fix: correct type declarations for getProxiesInner and getProxyProviders 2025-03-04 02:26:26 +08:00
Tunglies
8f95c28050 feat(proxy): add proxy commands and integrate with API
Add new proxy.rs module with get_proxies and get_providers_proxies commands.
Update mod.rs and lib.rs to re-export and register proxy commands.
Update API.ts to use invoke for proxy commands.
Minor formatting improvements in module/mihomo.rs.
2025-03-04 01:01:24 +08:00
Tunglies
2cd97c7785
feat: add Mihomo API modules and manager (#2869)
• Introduce new API caller implementations for Mihomo in model and module layers.
• Add configuration and API integration files under /src-tauri/src/config/api and /src-tauri/src/model/api.
• Implement a singleton MihomoAPICaller with async API call support and integration tests.
• Create a new MihomoManager module to refresh and fetch proxies from the API.
• Update Cargo.lock and Cargo.toml with additional dependencies (async-trait, env_logger, mockito, tempfile, etc.) related to the Mihomo API support.
2025-03-03 19:31:44 +08:00
wonfen
6e48781687 Revert "refactor: improve proxy group UI and spacing (#2835)"
This reverts commit b31cbda61507f68473762e5b5e86e8ddcab7b3c6.
2025-03-03 14:47:05 +08:00
wonfen
dd510b2ee9 feat: show service mode installation prompts in user mode 2025-03-03 14:42:31 +08:00
Tunglies
fadae3f7dc fix: speed format runns by docs 2025-03-03 11:36:21 +08:00
Tunglies
fdf8a6b2ba Revert "style: update box styling in settings page for improved layout (#2857)"
This reverts commit 4382df7951aefc973ec6f9d9cba0fafc88134e93.
2025-03-03 08:00:10 +08:00
Tunglies
4382df7951
style: update box styling in settings page for improved layout (#2857) 2025-03-03 06:38:32 +08:00
Tunglies
6cb7d48530
feat: add export diagnostic info functionality (#2856) 2025-03-03 05:58:12 +08:00
wonfen
9d74b93ee0 feat: Add sidecar mode as an alternative to service mode
- Auto-fallback to sidecar mode if service mode fails
2025-03-03 03:34:34 +08:00
Tunglies
d370868222 fix: windows/linux runtime crash 2025-03-03 02:27:45 +08:00
Christine.
9278f9e193
workflow: remove 32-bit platform (#2855)
* chore: build portable by self

* chore: remove 32bit platform

* Update CONTRIBUTING.md

* update alpha version
2025-03-03 01:16:33 +08:00
Tunglies
a708db6f2b Revert "chore: alpha ci should remove old builds"
This reverts commit d2c4a44297d98ca3b7df308f2d2422c88e9aa1aa.
2025-03-03 00:16:17 +08:00
Tunglies
d2c4a44297 chore: alpha ci should remove old builds 2025-03-02 23:46:20 +08:00
Tunglies
3a24623e4b feat: fish env export support 2025-03-02 23:20:10 +08:00
Tunglies
d2084d8d21 version: 2.1.3 a 2025-03-02 20:46:53 +08:00
Tunglies
d9b4ca91e7 version: 2.1.3 alpha 2025-03-02 20:43:54 +08:00
Tunglies
13da571ad2
version: 2.1.3 alpha (#2851)
* version: 2.1.3 alpha
2025-03-02 19:08:27 +08:00
Tunglies
662d60c0f5 chore: automatically label issues (#2844) 2025-03-02 18:50:31 +08:00
Tunglies
368095d2d4
docs: add fast build and clean commands to contributing guide (#2842)
docs: add fast build and clean commands to contributing guide

- Added documentation for the `pnpm build:fast` command which uses Rust's fast-release profile to reduce compilation time
- Added explanation that fast builds disable optimization and LTO, resulting in larger but quicker builds
- Added documentation for the `pnpm clean` command to clean Rust build files
2025-03-02 15:13:17 +08:00
Tunglies
496aeeb06d
chore: fast-dev and fast-build profile (#2841)
* refactor: improve proxy group UI and spacing

- Increased spacing in proxy-groups.tsx by adjusting the right position
  of the alphabet selector to provide better visual separation
- Enhanced spacing in proxy-render.tsx with larger margins and padding
  - Increased group item margins from 8px to 10px with 16px horizontal spacing
  - Expanded border radius from 8px to 10px for smoother appearance
  - Improved ProxyHead component spacing with pl: 3, pr: 3.5
  - Enhanced grid spacing in proxy collection items from 1 to 1.5
  - Adjusted padding for better visual hierarchy

These changes create a more polished, spacious layout with improved
readability and touch targets.

* - Update package.json with improved dev and build scripts:
  - Add fast-dev profile to development scripts
  - Configure build:fast with fast-release profile
  - Add clean command for cargo cleaning
2025-03-02 14:58:59 +08:00
Tunglies
b31cbda615
refactor: improve proxy group UI and spacing (#2835)
- Increased spacing in proxy-groups.tsx by adjusting the right position
  of the alphabet selector to provide better visual separation
- Enhanced spacing in proxy-render.tsx with larger margins and padding
  - Increased group item margins from 8px to 10px with 16px horizontal spacing
  - Expanded border radius from 8px to 10px for smoother appearance
  - Improved ProxyHead component spacing with pl: 3, pr: 3.5
  - Enhanced grid spacing in proxy collection items from 1 to 1.5
  - Adjusted padding for better visual hierarchy

These changes create a more polished, spacious layout with improved
readability and touch targets.
2025-03-02 05:36:18 +08:00
Tunglies
7898f3a119 fix: macos frameless title 2025-03-02 04:30:58 +08:00
wonfen
ba0a291d97 feat: refactor logging system into a global service 2025-03-02 04:20:38 +08:00
wonfen
b3e4defc0f fix: remove macos window title 2025-03-02 04:09:50 +08:00
wonfen
1ae0ad8a0e fix: add bottom padding to prevent jitter 2025-03-02 04:08:13 +08:00
Tunglies
79b20694c7
feat: add fast compilation options for development and release (#2831)
- Added fast compilation profiles in Cargo.toml
  - fast-dev profile with max codegen units and disabled optimizations
  - fast-release profile with debugging support and faster build time
- Added new npm scripts for quick development iterations
  - dev:fast command for standard development without extra features
  - build:fast command for quick release builds
- Updated default dev command to use verge-dev feature flag
- Both profiles retain debug symbols and disable stripping for better debugging
2025-03-02 00:40:07 +08:00
Tunglies
1ad4941ed8
refactor: rename cmds module to cmd for better consistency (#2830)
- Renamed `cmds` module to `cmd` for better naming consistency
- Reorganized command modules into separate files under src/cmd/
- Updated all imports and references to use the new module name
- Fixed missing dependency in webdav.rs to reference core::backup
- Updated tray module to use new cmd namespace
- Improved uwp.rs module structure using platform-specific implementations
- Removed unnecessary imports from various command files
2025-03-01 22:52:43 +08:00
Tunglies
b3fd44d165
refactor: reorganize feat.rs into modular structure (#2827)
Split the monolithic feat.rs file into specialized modules:
- backup.rs: WebDAV backup and restore functions
- clash.rs: Core management and testing functions
- config.rs: Configuration handling
- profile.rs: Profile management
- proxy.rs: Proxy and TUN mode controls
- window.rs: Dashboard window management

This improves code organization, readability, and maintainability
by grouping related functionality into logical modules.
2025-03-01 20:44:35 +08:00
Christine.
391a494af6
fix: build failed with Windows (#2825) 2025-03-01 19:52:42 +08:00
Tunglies
623d075ab8 add: issues type template 2025-03-01 19:12:12 +08:00
Tunglies
5d70b77316
Issues label options template (#2820)
* fix: macos dock display icon and text

* add: issues label option template
2025-03-01 02:46:16 -08:00
Tunglies
f5f54c0f0e
fix: macos dock display icon and text (#2818) 2025-03-01 02:29:40 -08:00
wonfen
91b0b1f279 perf: improve scrolling performance and interaction in proxy group list 2025-03-01 08:31:31 +08:00
wonfen
e0fa1f3efe fix: v2 action file rename 2025-03-01 08:04:31 +08:00
wonfen
b00abf3337 chore: updatelog 2025-03-01 03:45:03 +08:00
wonfen
a5d846ce4e feat: add exit status check in core config validation 2025-03-01 03:39:13 +08:00
wonfen
7743c42dd1 Release - 2.1.2 真·臻 2025-03-01 01:49:01 +08:00
Christine.
3f3dab9495
add: i18n text for settings page. (#2815) 2025-03-01 01:36:36 +08:00
Tunglies
2dc9672b20 feat: better setting UI layout (#2814) 2025-03-01 01:29:23 +08:00
wonfen
2c90d1ca69 chore: v2 updater 2025-03-01 01:29:23 +08:00
Tunglies
1555e2910d feat: better setting layout ui (#2809) 2025-03-01 01:29:23 +08:00
Tunglies
19b6bd35f5 feat: quiting when enable tun mode no more blocking system network (#2805) 2025-03-01 01:29:23 +08:00
wonfen
3d4d60edc8 test updater 2025-03-01 01:29:23 +08:00
wonfen
db0a000ecb chore: updater v2 Capabilities 2025-02-28 10:55:53 +08:00
wonfen
24ff48b41c fix: updater 2025-02-28 09:33:50 +08:00
wonfen
18a67d0b2f chore: updatelog 2025-02-28 07:34:56 +08:00
wonfen
e23af1ad58 feat: add ability to check service version and auto-reinstall 2025-02-28 06:45:30 +08:00
wonfen
70af392059 feat: enhance updater script with comprehensive platform support and logging 2025-02-27 17:19:04 +08:00
Christine.
d255df23ee
fix: connection details (#2778) 2025-02-27 14:53:17 +08:00
wonfen
1317a8b2db feat: enhance merge config validation and error handling 2025-02-27 14:49:55 +08:00
wonfen
4cac118442 style: UI tweak 2025-02-27 13:04:46 +08:00
wonfen
2ce43ccd23 Release - 2.1.1 臻fix 2025-02-27 03:18:23 +08:00
Tunglies
d23b2949d8 feat: macos display colorful icon with speed rate (#2771) 2025-02-27 01:51:52 +08:00
wonfen
3471476ba2 feat: improve file type detection for better script recognition 2025-02-26 15:59:19 +08:00
wonfen
cee61e5619 chore: downgrade Tauri updater plugin and add i18n for core switching 2025-02-26 15:04:47 +08:00
wonfen
5026e2bade feat: optimize hotkey behavior and window management logic 2025-02-26 11:03:50 +08:00
wonfen
83f005c256 fix: CI 2025-02-26 09:52:15 +08:00
wonfen
80843c5ee3 Release 2.1.0 - 臻 2025-02-26 08:36:02 +08:00
wonfen
ee00defe43 feat: enhance script validation and error handling 2025-02-26 05:21:14 +08:00
wonfen
7696504d97 feat: Add window state monitoring and auto-save in real-time 2025-02-26 00:36:02 +08:00
wonfen
7a0e38a1b4 perf: Improve config validation error messages and handling 2025-02-25 13:47:29 +08:00
wonfen
bc5d577553 chore: revise translation 2025-02-24 23:26:04 +08:00
wonfen
cfed2b7236 chore: Change default TUN stack from 'mixed' to 'gvisor' 2025-02-24 23:10:09 +08:00
Christine.
e34e6654bc
add: i18n text for config check (#2750) 2025-02-24 22:23:42 +08:00
wonfen
d6b85f1a01 fix: remove node related group info when deleting a node 2025-02-24 11:40:28 +08:00
wonfen
16af7b53cb fix: menu switching issue 2025-02-24 11:37:23 +08:00
wonfen
befc856207 feat: Improve core change configuration validation and error handling 2025-02-24 07:34:03 +08:00
wonfen
5ab8e7a7c7 feat: Enhance configuration validation and error handling during app startup 2025-02-24 06:21:32 +08:00
zhaoyuan
443bfa5928
feat: 通过添加CLASH_VERGE_REV_IP环境变量的方式,修改复制环境变量按钮的IP (#2734)
Co-authored-by: zymouse <zymouse@pixmoving.net>
2025-02-24 03:42:40 +08:00
wonfen
52627575ff feat: Enhance configuration validation and error handling
- Improve config validation process with detailed logging and error tracking
- Add more robust error handling in profile updates and config patches
- Implement comprehensive config validation using clash core subprocess
2025-02-23 10:53:09 +08:00
Christine.
39973f2d24
feat: auto rename alpha version (#2740) 2025-02-23 02:30:15 +08:00
wonfen
2b01857d15 chore: Disable automatic alpha tag update in workflow 2025-02-20 14:25:58 +08:00
wonfen
2650aa845f refactor: Optimize proxy rendering and layout calculation 2025-02-20 14:21:55 +08:00
wonfen
8400a61bf0 refactor: Simplify tray icon event handling across platforms 2025-02-20 07:03:28 +08:00
wonfen
763b6b9003 chore: Update Node.js version and remove updater file generation 2025-02-20 02:55:47 +08:00
wonfen
053f414f3c feat: Add system proxy status indicators 2025-02-19 13:56:22 +08:00
wonfen
2defa2cc56 refactor: Simplify log data management and improve search functionality 2025-02-19 13:06:15 +08:00
wonfen
bb1dbfcfc3 fix: connections page traffic calculation 2025-02-19 02:24:21 +08:00
Tunglies
1378068a30
rm dead code (#2718)
* rm: verge service takes full control of mihomo process. no more required.

* rm: dead code
2025-02-19 01:13:52 +08:00
wonfen
5d9dce7d10 style: tweak 2025-02-18 11:40:41 +08:00
wonfen
acb66a3012 feat: Add persistent column width settings for connection table & process filtering 2025-02-18 11:04:22 +08:00
wonfen
054f902cc6 refactor: Remove unused routing select component from settings page 2025-02-18 09:04:23 +08:00
wonfen
4333153a59 feat: Improve letter item hover interaction with debounce mechanism 2025-02-18 09:00:03 +08:00
wonfen
19e623d7c2 feat: Add pause/resume functionality for connection tracking 2025-02-18 08:14:09 +08:00
wonfen
2dd646537e fix: resolve deprecated warnings in console 2025-02-18 07:10:28 +08:00
wonfen
1ece079978 perf: Improve proxy list interaction and UI responsiveness 2025-02-18 05:54:16 +08:00
wonfen
0d8f779634 feat: Enhance alphabet selector with dynamic tooltip and scrolling 2025-02-18 01:55:44 +08:00
wonfen
a7ffc9fc38 refactor: Improve proxy data update mechanism with optimistic UI and error handling 2025-02-18 00:37:55 +08:00
wonfen
932b29e66f fix: Refine TypeScript types for proxy groups component 2025-02-17 16:27:06 +08:00
wonfen
6be7a3b94c feat: Enhance proxy groups with Initials navigation and performance optimizations 2025-02-17 16:07:46 +08:00
wonfen
8e8dd1ec03 feat: Optimize tray speed rate rendering and update logic 2025-02-17 15:08:19 +08:00
wonfen
772f3268ce feat: Improve Virtuoso list rendering for proxy groups 2025-02-17 14:30:21 +08:00
wonfen
a179591ac2 feat: Add persistent scroll position for proxy groups 2025-02-17 14:24:33 +08:00
wonfen
d071e5971f fix: alpha build win webview pack rename issue 2025-02-17 12:06:57 +08:00
wonfen
a9032f5f20 chore: Update updater artifacts configuration 2025-02-17 11:22:14 +08:00
Akioe Yu
0190702616 fix: check script error (#2453)
(cherry picked from commit 97bde64fd6516019a190d52f05af54ade623d57c)
2025-02-17 03:10:19 +08:00
wonfen
c35bf38420 chore: Add macOS-specific test for format_bytes_speed function 2025-02-17 03:10:03 +08:00
wonfen
89d20e564a Revert "perf: Improve kernel management logic & add more dev mode logs"
This reverts commit 18c7ed1ccc15b98e4873a276ac75e27276ab9d65.
2025-02-15 05:51:46 +08:00
Christine.
3c24ff3afc
add: i18n text for lightweight mode (#2692) 2025-02-13 14:27:12 +08:00
Tunglies
daf0398750
fix: build front cannot find IvergeConfig.enable_lite_mode and macos port switching causes crash (#2691)
* fix: macos switch protocol port or enable protocol port causes crash

* fix: build time front cannot find IVergeConfig attribute enable_lite_mode
2025-02-12 18:14:13 -08:00
wonfen
6b14c2b763 feat: add lightweight mode 2025-02-13 02:18:17 +08:00
wonfen
5bf2f9b8ed fix(macos): add missing required dependencies 2025-02-12 15:06:42 +08:00
wonfen
10a2655288 fix: remove unused imports to resolve compile warnings 2025-02-12 14:35:49 +08:00
wonfen
ac07397818 refactor: improve hotkey management, logging, and error handling; fix tray freeze and hotkey failure on silent startup 2025-02-12 14:23:42 +08:00
wonfen
30d061d00f fix(tray): resolve blank icon issue on Windows and optimize creation logic 2025-02-12 13:56:33 +08:00
Christine.
360d8a5143
fix: use remoteDestination replace DestinationIP in connection page, #2668 (#2679)
* fix: use remoteDestination replace DestinationIP  in connection page, #2668

* add: missing i18n text

* fix: display the target address details in connection page
2025-02-11 14:52:27 +08:00
wonfen
70e0a5adc8 refactor: Improve tray icon and event handling across platforms & unify click behavior 2025-02-11 14:48:31 +08:00
wonfen
ef52f81494 fix: app crash without a default global_hotkey vaule 2025-02-11 01:50:48 +08:00
wonfen
e5c1de3ad3 fix: deprecated CLI warnings 2025-02-11 01:05:51 +08:00
wonfen
576fd700ae feat: Add independent alpha update channel 2025-02-11 00:53:44 +08:00
wonfen
476df65a1b fix: try to fix linux CI updater problem 2025-02-11 00:53:44 +08:00
Christine.
bd0a4863a8
add: i18n text (#2675) 2025-02-10 00:29:43 -08:00
wonfen
d25fbc05e2 perf: Optimize kernel shutdown speed & logic 2025-02-10 13:18:00 +08:00
wonfen
b8a0b6f1f4 fix: crash caused by global_hotkey 2025-02-10 12:39:07 +08:00
wonfen
18c7ed1ccc perf: Improve kernel management logic & add more dev mode logs 2025-02-09 14:19:15 +08:00
Tunglies
b14db06955
perf: imporve clash mode switching performance on the main window (#2667) 2025-02-09 07:45:46 +08:00
Tunglies
215dcee3f1
feat: option to enable global hotkey (#2665) 2025-02-09 07:45:22 +08:00
Tunglies
37e5c22a5a
fix: duplicate checked tray menu when profile name are same (#2660) 2025-02-08 09:36:04 +08:00
wonfen
bb0b6c3f77 feat: Optimize UI layout and page hierarchy 2025-02-07 09:13:30 +08:00
wonfen
a37df46ce7 perf: optimize node latency refresh rate for faster updates 2025-02-06 07:58:42 +08:00
wonfen
73fbec2514 chore: update deps 2025-02-05 14:19:43 +08:00
Tunglies
f66fa08b2c
Feature: Switch Proxy Profile from Tray Menu (#2644) 2025-02-05 08:52:47 +08:00
wonfen
bae606bc9d fix: restore hotkey functionality after silent startup 2025-02-02 11:37:10 +08:00
wonfen
1a2d5e988a feat: allow users to customize enhanced-mode and fakeip-range in tun mode by wonfen 2025-02-02 05:12:16 +08:00
wonfen
d20bd62b90 style: refine tray speed display 2025-02-02 03:43:15 +08:00
wonfen
204bf43b9a fix: windows rounded corners 2025-01-19 09:47:43 +08:00
wonfen
3ea0d20e2c add donation 2025-01-18 16:08:24 +08:00
wonfen
6d527a1cdb release 2.0.3 2025-01-16 03:30:07 +08:00
白铭骢 (Mingcong Bai)
9c1ece754e
fix(locales): clean up typesetting (#2539)
Following localisation guidelines from 《大陆简中自由软件本地化工作指南》
1.5.4 版 (Simplified Chinese - Mainland China FOSS Localization
Guidelines, version 1.5.4).

Link: https://repo.aosc.io/aosc-l10n/zh_CN_l10n_1.5.4.pdf
2025-01-14 13:00:30 +08:00
huzibaca
9070260d41
chore: update 2025-01-14 12:56:08 +08:00
huzibaca
cd8df52aad
revert: update deps 2025-01-14 12:41:52 +08:00
huzibaca
fa86efcdfb
chore: avoid duplicate updates when tray rate is off 2025-01-14 11:48:43 +08:00
huzibaca
1a61fab79a
feat: maoos tray speed can be closed 2025-01-13 20:48:25 +08:00
huzibaca
41a27641df
fix: fix: try to fix the language pack issue(2) 2025-01-13 17:09:38 +08:00
huzibaca
a39696151d
fix: windows tray icon color not updated 2025-01-13 13:49:56 +08:00
huzibaca
4a4ca5c409
chore: update husky/pre-commit 2025-01-13 13:03:46 +08:00
huzibaca
5bff7ea469
fix: tray tooltip not updating 2025-01-13 13:01:12 +08:00
huzibaca
f2cc116ff9
chore: update 2025-01-13 12:41:07 +08:00
huzibaca
80a18c9172
chore: update deps 2025-01-12 23:10:15 +08:00
huzibaca
a56732e0a3
fix: try to fix the language pack issue 2025-01-12 22:22:06 +08:00
huzibaca
9655f7712b
Merge branch 'languagefixes' into dev
# Conflicts:
#	src-tauri/Cargo.lock
#	src-tauri/Cargo.toml
#	src-tauri/src/core/tray/mod.rs
2025-01-11 15:07:30 +08:00
huzibaca
13b63b5d96
fix: fixes #2502 2025-01-11 12:55:20 +08:00
lucidhz
c5989d2735
fix: put_configs response add detail error message (#2492)
感谢pr
2025-01-05 01:27:12 -08:00
huzibaca
fb4032d6ce
fix: modify the external control access key, the tray rate display is abnormal 2025-01-02 15:27:28 +08:00
huzibaca
a29c2d4b14
chore: update deps 2025-01-02 15:02:07 +08:00
huzibaca
aa7e911c63
chore: update 2025-01-01 08:25:31 +08:00
huzibaca
eeff4d41f4
feat: the tray displays the shortcut keys that have been set 2025-01-01 08:14:15 +08:00
huzibaca
086f023ebc
chore: update 2024-12-31 11:11:29 +08:00
huzibaca
b70336c026
fix: unused code 2024-12-31 08:08:52 +08:00
huzibaca
7d84279370
fix: syntax issues 2024-12-31 04:50:12 +08:00
huzibaca
1b2f1b6106
Merge branch 'feat-macos-spped-rate-icon' into dev 2024-12-31 04:43:54 +08:00
huzibaca
fb41c915cc
feat: macos system tray addition rate display 2024-12-31 04:42:55 +08:00
blagodaren
bab291a60b fix: small refactor 2024-12-28 13:57:30 +03:00
blagodaren
8461046a4f fix: add more lang and lang fix for tray 2024-12-28 10:05:30 +03:00
blagodaren
fc925ea032 fix: add system language sup 2024-12-28 08:12:46 +03:00
huzibaca
542baf9d69
fix: fixes #2460 2024-12-28 06:05:09 +08:00
huzibaca
3f02859203
chore:add macos entitlements.list 2024-12-27 04:38:34 +08:00
huzibaca
af97bd15a9
chore: update 2024-12-27 02:42:35 +08:00
huzibaca
2c081e5a04
chore: secret is empty and no parameters are passed 2024-12-25 02:15:06 +08:00
huzibaca
2d2521e434
chore: update 2024-12-24 06:03:23 +08:00
huzibaca
d9291d4f79
refactor: when updating the tray, the logic is split to improve performance. 2024-12-24 04:52:14 +08:00
huzibaca
0669f7a10b
chore: after saving the configuration file, restart the core 2024-12-24 02:22:46 +08:00
huzibaca
f0d953ff59
chore: update default_bypass
1. add 172.29.0.0/16
2024-12-23 06:06:46 +08:00
huzibaca
4f6ca40811
fix: turn off window shadow fixer, fixes #2425 2024-12-23 04:36:41 +08:00
huzibaca
47848099be
chore: update 2024-12-15 01:14:16 +08:00
huzibaca
f839d3bc88
fix: fixer #2346 2024-12-14 17:30:58 +08:00
huzibaca
800f994b10
chore: update deps 2024-12-14 16:21:16 +08:00
Langning Chen
b149084e39
Fix l10n issues (#2315)
* Complete the missing translation

* Match the Chinese language file order with others
2024-12-11 01:02:21 +08:00
huzibaca
e6580f2f05
chorea: update deps 2024-12-10 22:36:13 +08:00
huzibaca
c5c840d378
feat: improve system bypass settings 2024-12-10 14:37:11 +08:00
huzibaca
2e0be4b426
chore: enable default proxy bypass and hide custom bypass settings 2024-12-10 13:59:13 +08:00
huzibaca
460d72f392
chore: update deps(tauri-plugin-autostart) 2024-12-08 22:10:15 +08:00
huzibaca
aa18c4870d
chore: deeplink uses the latest API 2024-12-08 15:54:46 +08:00
huzibaca
61533646ad
chore: update deps 2024-12-08 09:58:59 +08:00
huzibaca
b42d13f573
fix: when the window is hidden, close the websocket connection, reduce the risk of memory leaks 2024-12-07 16:47:41 +08:00
wonfen
0e3b631118 chore: change fakeip range 2024-12-06 15:34:41 +08:00
huzibaca
38745d4513
chore:set the sysproxy_rs version to a fixed version 2024-12-06 15:03:04 +08:00
huzibaca
d22b37c7bf
fix: extension script dns is overwritten fixer #2235 2024-12-06 12:35:09 +08:00
Myles Mo
d233a84362
feat: add nullshell env variable (#2285) 2024-12-04 19:01:13 +08:00
huzibaca
589324b582
fix: shift hotkey conversion fixer #2278 2024-12-04 13:19:56 +08:00
huzibaca
c11efcb9be
chore: update version 2024-12-04 00:30:46 +08:00
huzibaca
6197249377
fix: set fontLigatures to false fixer #2267 2024-12-04 00:28:33 +08:00
lollapalooza
c71e18e97e
feat: add bypass check feature (#2272) 2024-12-03 16:18:07 +08:00
Christine.
f400f900e6
fix: #2126 (#2233) 2024-12-03 16:03:21 +08:00
wonfen
ae5b2cfb79 release 2.0.2 2024-12-01 11:00:09 +08:00
huzibaca
0bb8786ef2
fix: resource file initialization failed 2024-12-01 09:38:29 +08:00
huzibaca
f7d5be774d
fix: resolve service permission failed(2) 2024-11-30 10:45:53 +08:00
huzibaca
c0a0b82fa6
chore: update 2024-11-30 10:38:15 +08:00
huzibaca
277ef51375
fix: resolve service permission failed 2024-11-30 10:29:16 +08:00
huzibaca
67b1cf9e1b
feat: user uploaded icons can use templates, provided they are monochrome icons fixer #2213 2024-11-30 09:49:43 +08:00
huzibaca
4f797eb7b8
chore: update 2024-11-30 07:04:09 +08:00
huzibaca
29ccabc054
refactor: update_core_config, simplify logic and delete invalid notifications 2024-11-30 05:43:59 +08:00
yyhhyy
857574cbc1
🐛 fix: Dynamically set IPv6 DNS configuration based on existing config (#2198)
thanks
2024-11-30 00:55:15 +08:00
wonfen
acc97f28b5 chore: change mac tun dns 2024-11-29 11:10:41 +08:00
huzibaca
02e32aec41
chore: update 2024-11-29 03:46:37 +08:00
huzibaca
fe0fdc5603
chore: remove debug code 2024-11-28 14:44:13 +08:00
Yu-Haifeng
66941a18c0
fix: macOS tray icon not changed when use system proxy or tun mode (#2176) 2024-11-28 14:42:05 +08:00
huzibaca
f772f92b88
fix: the deadlock caused by incorrect call of window_state due to document error 2024-11-28 12:54:55 +08:00
huzibaca
d80db8c91e
chore: clean startup registry keys for older versions(2) 2024-11-28 06:40:44 +08:00
huzibaca
a1e67820c7
chore: clean startup registry keys for older versions 2024-11-28 05:34:56 +08:00
wonfen
ebe0cfbb0c chore: set tun default stack 2024-11-28 04:22:13 +08:00
huzibaca
59c52199e3
chore: update 2024-11-27 14:27:16 +08:00
wonfen
148def4c0f release 2.0.2 2024-11-27 12:41:51 +08:00
huzibaca
3177ea0f4d
chore: update 2024-11-27 11:34:52 +08:00
huzibaca
7d65ce847a
fix: when tun is closed, the full profile configuration is not restored 2024-11-27 10:35:42 +08:00
huzibaca
2ac27fcfa7
refactor: backup implementation
1. timeouts can be set for different operations
2. performance optimization
2024-11-27 07:34:34 +08:00
huzibaca
373c8ec0e5
chore: update 2024-11-27 07:09:03 +08:00
huzibaca
7df5ca1912
fix: linux build failed 2024-11-27 05:54:48 +08:00
huzibaca
d8dc60ff0d
fix: fixer #2158 2024-11-27 05:47:08 +08:00
huzibaca
56deafee61
chore: update version 2024-11-27 05:09:46 +08:00
huzibaca
4c29850e1c
fix: build failed. #2156 2024-11-27 05:07:02 +08:00
huzibaca
90bf5e0782
chore: timeout adjusted to 15 seconds 2024-11-26 15:09:58 +08:00
huzibaca
c2d07ce4c1
fix: add scroll bar and scroll to top button to the test page . fixer #2118 2024-11-26 14:44:43 +08:00
wonfen
564d81fd31 style: adjust 2024-11-26 12:11:20 +08:00
huzibaca
f8b7b82ba9
chore: remove window title 2024-11-26 10:11:55 +08:00
huzibaca
71cb2a97c0
fix: kernel-caused silent mode failure to start windows 2024-11-26 09:00:01 +08:00
huzibaca
1cc6472002
chore: update 2024-11-26 05:46:34 +08:00
huzibaca
9b2c80494d
refactor: Implement using third-party libraries 2024-11-26 04:51:19 +08:00
huzibaca
4ccc2a2961
chore: update 2024-11-26 03:30:15 +08:00
huzibaca
17a8dfb58a
fix: file drag and drop import cannot be used 2024-11-26 03:07:25 +08:00
wonfen
184b588f20 style: Increased light color contrast to prevent blurring on some displays 2024-11-26 01:05:30 +08:00
huzibaca
87c4ebe0da
chore: replace mui grid with mui grid2 2024-11-25 12:51:13 +08:00
wonfen
18a0a15e16 update change log 2024-11-25 06:46:29 +08:00
huzibaca
c8fd62f388
fix: Try to fix vcruntime duplicate installation 2024-11-25 06:33:47 +08:00
huzibaca
97f03de295
fix: update failed(updater.install called before updater.download) 2024-11-25 05:27:26 +08:00
huzibaca
2c0a5666fc
fix: when restoring webdav, the current username, password and url are not preserved 2024-11-25 03:41:35 +08:00
huzibaca
5c3f0d5b60
fix: password failed due to character escaping 2024-11-25 02:48:56 +08:00
huzibaca
746763ed34
fix: remove comments from svg icons to prevent front-end crashes. fixer #2093 2024-11-25 01:58:02 +08:00
wonfen
e3800a575f fix: fakeip dns 2024-11-25 01:50:30 +08:00
huzibaca
c78c936762
chore: replace mui grid with mui grid2 2024-11-25 01:34:18 +08:00
huzibaca
ea5d6f9c46
fix: tun allocates the wrong private network segment, causing conflicts. 2024-11-25 01:06:51 +08:00
wonfen
97f8022276 release 2.0.1 2024-11-24 09:00:20 +08:00
huzibaca
d718bd9141
fix: If an older version of the executable exists, delete it(2) 2024-11-24 08:25:46 +08:00
huzibaca
8ddd48eda1
feat: try to use vscode first, if not found then use system default app 2024-11-24 08:20:00 +08:00
huzibaca
3e20404959
fix: If an older version of the executable exists, delete it 2024-11-24 01:24:51 +08:00
huzibaca
dde7ead751
Merge branch 'fix-migrate-tauri2-errors'
* fix-migrate-tauri2-errors: (288 commits)

# Conflicts:
#	.github/ISSUE_TEMPLATE/bug_report.yml
2024-11-24 00:14:46 +08:00
huzibaca
be719680b0
fix: windows cannot open yaml file 2024-11-23 19:46:53 +08:00
wonfen
d6f5a79ac9 fix: release workflow 2024-11-23 12:36:16 +08:00
wonfen
a3140f48b4 release 2.0.0 2024-11-23 11:34:17 +08:00
huzibaca
335883f9f0
chore: set the request timeout to 3 seconds 2024-11-23 06:50:28 +08:00
huzibaca
f2b1b88242
chore: remove notes 2024-11-23 06:47:43 +08:00
huzibaca
4163ee484c
chore: update 2024-11-23 06:42:05 +08:00
huzibaca
a14c3f81b0
fix: restore the window state first, then set the window size 2024-11-23 06:39:25 +08:00
huzibaca
f08d2c998d
chore: update 2024-11-23 06:22:46 +08:00
huzibaca
1b0cf938d9
chore: add debug codes & logs(2) 2024-11-23 06:20:13 +08:00
huzibaca
03d714f2d4
chore: add debug codes & logs 2024-11-23 05:43:13 +08:00
huzibaca
4feef53cb0
chore: set the request timeout to 5 seconds 2024-11-22 14:15:31 +08:00
huzibaca
397e6a9d57
feat: added scroll top button for agent and rule pages 2024-11-22 09:22:44 +08:00
huzibaca
9df2ceba84
chore: remove debug code 2024-11-22 09:08:25 +08:00
huzibaca
1b65dc7256
fix: macOS DNS restore failed 2024-11-22 03:09:39 +08:00
huzibaca
4ffadab4c5
chore: hide DNS cache file 2024-11-22 02:42:55 +08:00
huzibaca
7ed90ff7f8
fix: husky - install command is DEPRECATED 2024-11-21 12:06:52 +08:00
huzibaca
ec79126b4a
chore: remove unused code 2024-11-21 11:44:19 +08:00
huzibaca
ee13bb559e
chore: remove unused code 2024-11-21 11:24:19 +08:00
huzibaca
ffbc0cdff4
fix: password should not be trimmed 2024-11-21 11:14:40 +08:00
huzibaca
801b79b728
chore: remove unused code 2024-11-21 11:13:11 +08:00
huzibaca
cd1d719a92
fix: serde::json passing IVerge to the front end without deserialization 2024-11-21 06:01:56 +08:00
huzibaca
cf3848f15d
chore: update 2024-11-20 07:37:30 +08:00
huzibaca
df5d43283b
chore: remove notes 2024-11-20 07:37:03 +08:00
huzibaca
91f0f9f65e
feat: encryption configuration properties 2024-11-20 07:27:42 +08:00
huzibaca
708e5d1941
fix: auto launch does not worki 2024-11-20 03:52:19 +08:00
huzibaca
8773117e50
chore: update 2024-11-20 01:15:03 +08:00
huzibaca
8840e63c1c
fix: exit_app does not work 2024-11-20 01:04:55 +08:00
huzibaca
4e2d9d6acd
fix: windows cannot save window state(2) 2024-11-20 00:27:53 +08:00
huzibaca
94259f9515
fix: windows cannot save window state 2024-11-19 23:32:32 +08:00
huzibaca
5b0ff01cbf
chore: update deps 2024-11-19 13:35:57 +08:00
huzibaca
3ae1109a31
chore: format 2024-11-19 13:28:17 +08:00
wonfen
f50b9655ae feat: improve set dns logic 2024-11-19 11:38:23 +08:00
huzibaca
081daee53f
chore: update web:build comman, use tsc --noEmit 2024-11-19 04:42:37 +08:00
huzibaca
95123aceb5
feat: add logger highlighting, support regular and case matching 2024-11-19 04:10:10 +08:00
huzibaca
2a7b22c96f
feat: add logger highlighting 2024-11-19 03:29:44 +08:00
huzibaca
c1b15490c1
fix: log pause button not working 2024-11-19 02:56:58 +08:00
huzibaca
f8cb84a706
feat: logger support all level filters 2024-11-19 01:40:45 +08:00
huzibaca
063f9034a1
chore: update deps 2024-11-18 23:51:43 +08:00
huzibaca
9c1b2ff89c
chore: remove unused code 2024-11-18 23:48:02 +08:00
Christine.
44c05d9fd9
fix: field error, #2044 (#2045) 2024-11-18 07:34:57 -08:00
huzibaca
dd3c149e98
refactor: remove useSWRSubscription and use useEffect 2024-11-18 16:41:17 +08:00
huzibaca
0623fe4dc3
chore: remove unused code 2024-11-18 08:16:31 +08:00
huzibaca
2746ff68c8
feat: Log level status is saved to local storage 2024-11-18 08:14:21 +08:00
huzibaca
62eb070c1b
refactor: use zustand store, rewrite log clearing logic 2024-11-18 06:48:23 +08:00
huzibaca
3b8147b6ad
chore: typo warn -> warning 2024-11-18 06:01:51 +08:00
huzibaca
221b732472
refactor: logger fetch logic 2024-11-18 05:58:06 +08:00
huzibaca
132641f269
fix: the CJS build of Vite's Node API is deprecated, part 2 2024-11-18 01:21:00 +08:00
huzibaca
d8cf585fc5
fix: the CJS build of Vite's Node API is deprecated 2024-11-18 01:07:16 +08:00
Christine.
7f0d9952aa
chore: Replace test URL to support iPv4&iPv6 (#2033) 2024-11-18 00:20:21 +08:00
huzibaca
415b4879f1
fix: webdav list interface compatibility issue 2024-11-17 23:57:28 +08:00
huzibaca
7e5d6ef9b6
fix: webdav refreshes data and clears the original data when an error occurs. 2024-11-17 23:50:34 +08:00
huzibaca
ff297957c7
chore: update 2024-11-17 01:01:36 +08:00
huzibaca
7489f5e62d
chore: update 2024-11-17 00:46:35 +08:00
huzibaca
3575c16326
fix: fixes #1968 2024-11-16 23:09:10 +08:00
wonfen
04f4f5f713 release rc.7 2024-11-16 10:15:50 +08:00
huzibaca
eb78af7c77
chore: remove debug code 2024-11-16 07:35:34 +08:00
huzibaca
114d5ab541
fix: fixes #1940 2024-11-16 06:08:49 +08:00
huzibaca
f13140e39e
fix: When the shortcut key closes the window, the window is minimized 2024-11-16 04:46:20 +08:00
huzibaca
97717c648e
fix: windows arm64 vsruntime is not installed 2024-11-16 03:12:11 +08:00
huzibaca
ec6705eaf4
Merge branch 'fix-tray-menu-flash-on-windows' into fix-migrate-tauri2-errors
* fix-tray-menu-flash-on-windows:
  chore: update
2024-11-16 02:54:24 +08:00
Chenx Dust
1e11eff811
feat: support mptcp and smux display (#1995)
Corresponding pull request in mihomo: https://github.com/MetaCubeX/mihomo/pull/1646
2024-11-16 01:35:22 +08:00
huzibaca
13fb8c5d7d
chore: update 2024-11-15 17:19:14 +08:00
huzibaca
e87e06dd37
chore: unified code format 2024-11-14 03:21:18 +08:00
huzibaca
b9bb79b4dc
fix: call parameter error 2024-11-14 03:16:32 +08:00
wonfen
d0f907f349 chore: update info & i18n 2024-11-14 03:01:37 +08:00
huzibaca
867c83b84c
chore: update dep(tauri-bubild) 2024-11-13 21:49:53 +08:00
huzibaca
632d389411
chore: update deps 2024-11-13 21:39:55 +08:00
huzibaca
b4df1b6e42
feat: add refresh button 2024-11-13 00:53:52 +08:00
huzibaca
176620e3bb
fix: InputProps is deprecated 2024-11-13 00:30:30 +08:00
huzibaca
c37f22dc65
fix: mui grid has been deprecated 2024-11-13 00:21:22 +08:00
huzibaca
9d9cf27460
fix: application restart, window status not saved 2024-11-12 22:49:08 +08:00
huzibaca
6e8def3ef7
fix: syntax issues caused by upgrading mui5 2024-11-12 20:05:28 +08:00
huzibaca
ec0d872f11
chore: update 2024-11-12 19:44:57 +08:00
huzibaca
9e4a8708e7
fix: import error caused by dependency upgrade 2024-11-12 19:35:56 +08:00
huzibaca
a681fdeee1
fix: undefined error 2024-11-12 19:34:53 +08:00
huzibaca
8763a76475
chore: update deps 2024-11-12 19:06:04 +08:00
wonfen
4306fba997 release rc.6 2024-11-12 03:49:21 +08:00
huzibaca
3759239dac
feat: add webdav backup 2024-11-12 02:55:02 +08:00
huzibaca
4ec0b1d6e4
chore: update 2024-11-09 12:13:50 +08:00
huzibaca
6271726f07
Merge branch 'feat-webdav-backup' of ssh://github.com/clash-verge-rev/clash-verge-rev into feat-webdav-backup
# Conflicts:
#	src/locales/fa.json
#	src/locales/ru.json
#	src/locales/zh.json
2024-11-09 11:54:33 +08:00
wonfen
26dd097962 update: Readme 2024-11-09 09:00:53 +08:00
wonfen
e5740579f4 fix: translation 2024-11-09 08:59:30 +08:00
huzibaca
ca97e3a3e6
chore: update 2024-11-09 06:56:58 +08:00
huzibaca
d3f6822080
chore: update 2024-11-09 05:50:51 +08:00
huzibaca
9d9a6dfddb
chore: update 2024-11-08 23:42:00 +08:00
huzibaca
c3e24d7b96
chore: update 2024-11-08 21:46:15 +08:00
huzibaca
20d163cf3a
Merge branch 'feat-add-unified-delay' into fix-migrate-tauri2-errors 2024-11-06 13:56:36 +08:00
huzibaca
cf90f3abc9
chore: update 2024-11-06 13:52:36 +08:00
huzibaca
8871660f0e
chore: update 2024-11-06 10:07:02 +08:00
huzibaca
97a030c22e
fix: @use rules must be written before any other rules. 2024-11-06 09:48:31 +08:00
huzibaca
8a6ce0f0db
fix: Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0. 2024-11-06 09:46:17 +08:00
huzibaca
285b6b9287
chore: update 2024-11-05 21:40:02 +08:00
huzibaca
3515cc8e63
chore: update 2024-11-05 21:35:17 +08:00
huzibaca
b0132c9718
chore: update 2024-11-05 18:38:50 +08:00
huzibaca
ef84c31761
chore: update 2024-11-05 18:30:28 +08:00
huzibaca
4953d4b4d0
chore: add tooltip 2024-11-05 17:39:59 +08:00
huzibaca
2833718c90
feat: add unified delay 2024-11-05 16:24:58 +08:00
huzibaca
ab7775e1ef
fix: wrong window state save point 2024-11-04 09:53:40 +08:00
huzibaca
a5c4562f59
chore: update deps 2024-11-02 10:25:38 +08:00
huzibaca
00bd5b0e1e
chore: update 2024-11-02 07:27:08 +08:00
huzibaca
b66c07bd96
chore: update (+1 squashed commit)
Squashed commits:
[78899ef] fix: bbr2 not working in windows 11(24H2)
2024-11-02 07:10:47 +08:00
huzibaca
d8d0a59371
chore: update 2024-11-01 06:03:01 +08:00
The1111mp
953153ea1a
chore: added support for compiling linux arm architecture (#1945)
* chore: added support for compiling linux arm architecture

Signed-off-by: The1111mp <The1111mp@outlook.com>

* chore: remove debug code

Signed-off-by: The1111mp <The1111mp@outlook.com>

---------

Signed-off-by: The1111mp <The1111mp@outlook.com>
2024-10-31 17:20:16 +08:00
downer
fc03ce1247
fix: fix TUN icon is overwritten by system proxy icon (#1961)
Co-authored-by: 周晓亮 <zhouxl@jiyitech.com>
2024-10-31 09:07:21 +08:00
huzibaca
08bf70681c
feat: use tauri_plugin_window_state 2024-10-30 18:52:53 +08:00
huzibaca
00bc6a6301
revert: feat: use tauri-plugin-persisted-scope 2024-10-30 18:28:55 +08:00
huzibaca
c5916cf5ec
feat: use tauri-plugin-persisted-scope 2024-10-30 16:37:47 +08:00
huzibaca
4213a5fad1
chore: window hide replaces window minimize 2024-10-30 13:51:58 +08:00
wonfen
17fc9cf0eb release: rc.5 2024-10-30 13:08:37 +08:00
wonfen
eb1a1b3786 chore: update bug report template 2024-10-30 12:59:19 +08:00
huzibaca
dce72a16f0
chore: remove useless hooks
the window is not closed, it is minimized, so the position still exists
2024-10-30 10:06:54 +08:00
huzibaca
7550c2321c
fix: linux file permissions problem
check-config configuration file moved from temp_dir to home_dir, resolved
2024-10-30 09:08:44 +08:00
huzibaca
f0c4360210
chore: restore linux custom close button 2024-10-30 06:30:49 +08:00
huzibaca
d10323701a
fix: performance issues caused by closing windows on mac 2024-10-30 06:27:29 +08:00
huzibaca
94c437c3e3
chore: update 2024-10-29 10:15:07 +08:00
huzibaca
202fb19ab1
chore: update 2024-10-29 10:13:53 +08:00
huzibaca
2bb3498602
chore: disable websocket logging by default to reduce performance consumption. 2024-10-29 09:19:21 +08:00
huzibaca
9d476d7add
fix incorrect usage of useCustomTheme 2024-10-29 09:07:08 +08:00
huzibaca
014829e69a
chore: optimised the logic of dns processing 2024-10-28 13:09:55 +08:00
huzibaca
a127cd6444
fix: dns not restored when exiting the app 2024-10-28 00:24:57 +08:00
Christine.
486ffbdc08
add more contribution details for Windows (#1939)
* doc: add more building details

Some friends said the build failed.

* chore: replace test url

replace default delay check URL, some user say that the delay numbers seen in CVR are very different from those in other software, and this default test result is not a valid reference.
2024-10-27 13:00:47 +08:00
huzibaca
526c5bdd91
fix: unused variable 2024-10-27 10:19:42 +08:00
huzibaca
db11651f85
chore: update 2024-10-27 06:47:16 +08:00
huzibaca
4ce7d9f7a9
feat: macos service installation query, can't avoid it, add service run check, if installation not confirmed, exit automatically after 10 seconds 2024-10-27 06:32:25 +08:00
huzibaca
df3c296850
Revert "chore: update resource"
This reverts commit e3c52781f34b9bb921ac590ff4d672d1f1d5ec06.
2024-10-27 05:10:26 +08:00
huzibaca
5ef9220175
chore: update 2024-10-27 05:06:42 +08:00
huzibaca
e3c52781f3
chore: update resource 2024-10-27 04:46:24 +08:00
huzibaca
1cb1946497
chore: update deps 2024-10-27 01:24:13 +08:00
huzibaca
9e486e6836
chore: update 2024-10-26 19:23:49 +08:00
wonfen
e5dc4a55b0 release rc.4 2024-10-26 12:05:09 +08:00
huzibaca
76d04805a4
fix:public DNS not set in macos, tun+fake-ip 2024-10-26 08:55:00 +08:00
huzibaca
652a20c523
chore: update 2024-10-26 01:44:06 +08:00
huzibaca
6c2af1bef5
chore: update 2024-10-25 21:25:06 +08:00
huzibaca
ba2aa9d6e7
chore: update 2024-10-25 14:13:30 +08:00
huzibaca
a1c72fe780
chore: update 2024-10-25 14:12:27 +08:00
huzibaca
197f9fd964
fix: the tray icon does not display the correct colours on macos. 2024-10-25 13:58:22 +08:00
wonfen
fc2c10bb3f chore: add mac sun ico 2024-10-25 12:27:22 +08:00
wonfen
d7dd797494 release 2.0rc3 2024-10-24 12:52:20 +08:00
huzibaca
4f954657a1
chore: update 2024-10-24 11:14:18 +08:00
huzibaca
3eddcafeb6
Revert "chore: global mode doesn't show proxy groups, use placeholder images instead"
This reverts commit 3df4718709c49084a635ab190892d14b1744574b.
2024-10-24 07:44:21 +08:00
huzibaca
3df4718709
chore: global mode doesn't show proxy groups, use placeholder images instead 2024-10-24 07:30:30 +08:00
huzibaca
b5fddf8fa1
chore: update 2024-10-24 06:58:21 +08:00
huzibaca
fdc8a83f25
fix: proxy view display error 2024-10-24 06:54:27 +08:00
huzibaca
e79121e46a
refactor: the logic of profiles activation 2024-10-24 05:02:47 +08:00
huzibaca
aab71e55de
fix: restart app failure 2024-10-24 02:54:57 +08:00
huzibaca
caf4ee6863
fix: tun mode switch is not effective 2024-10-24 02:16:28 +08:00
huzibaca
30b0c45539
refactor: IRuntime::patch_config() 2024-10-23 10:34:14 +08:00
huzibaca
2ab75db9c9
chore: update 2024-10-23 09:26:14 +08:00
huzibaca
f9009de894
chore: update 2024-10-23 05:48:01 +08:00
huzibaca
da8ed1ac5a
fix: failed to install service on macos 2024-10-23 04:46:47 +08:00
huzibaca
afde7e4a44
feat: add windows uninstall script 2024-10-22 04:46:08 +08:00
huzibaca
3e94adb4b1
feat: add linux uninstall script 2024-10-22 03:17:04 +08:00
huzibaca
5c577e533f
fix: debian executable file has no permissions 2024-10-22 02:37:36 +08:00
huzibaca
c64b9d5a2b
chore: update 2024-10-22 01:34:26 +08:00
huzibaca
545ea16ae8
fix: change the window close to minimize the window.
there is currently an error in the tauri library.
2024-10-21 23:16:45 +08:00
huzibaca
bf245a4ee1
fix: pac url error ,fixes #1889 2024-10-21 22:52:10 +08:00
wonfen
b28fc26ae6 chore: update bug_report template 2024-10-21 11:30:07 +08:00
huzibaca
750dfdb3a8
Merge branch 'fix-migrate-tauri2-errors' of ssh://github.com/clash-verge-rev/clash-verge-rev into fix-migrate-tauri2-errors
* 'fix-migrate-tauri2-errors' of ssh://github.com/clash-verge-rev/clash-verge-rev:
  chore: update to 2.0 rc2
2024-10-21 04:35:42 +08:00
huzibaca
5b93c2b907
chore: update 2024-10-21 04:35:21 +08:00
wonfen
bbd72a79cb chore: update to 2.0 rc2 2024-10-21 03:15:02 +08:00
huzibaca
babf5d840c
chore: replace sudo with pkexec execution 2024-10-21 02:31:43 +08:00
huzibaca
90091db3b1
chore: update 2024-10-21 02:17:41 +08:00
huzibaca
ef03dc12bb
chore: update 2024-10-21 01:35:28 +08:00
huzibaca
a35014f0b5
chore: update 2024-10-21 01:30:13 +08:00
huzibaca
abb0a049a8
chore: update 2024-10-21 01:12:52 +08:00
huzibaca
3de18e2a86
chore: update 2024-10-21 00:50:43 +08:00
huzibaca
399bbd56ec
chore: update deps 2024-10-21 00:45:06 +08:00
huzibaca
38999d284a
chore: update 2024-10-21 00:34:28 +08:00
huzibaca
b61b63d100
fix: failed to start system proxy with PAC mode 2024-10-20 23:13:23 +08:00
huzibaca
0dc92fbc99
chore: update deps 2024-10-20 22:35:16 +08:00
huzibaca
1cb7cd8859
Merge branch 'fix-linux-errors' into fix-migrate-tauri2-errors
* fix-linux-errors:
  chore: try to fix service not started on linux
2024-10-20 21:57:41 +08:00
huzibaca
199700cf09
fix: mac commad+q global hijack is not released 2024-10-20 06:06:47 +08:00
huzibaca
ee78b7898b
chore: try to fix service not started on linux 2024-10-20 04:48:42 +08:00
huzibaca
a47ded7cf2
chore: update 2024-10-18 22:50:03 +08:00
huzibaca
770f031b8b
fix: try to fix service not started on linux 2024-10-18 07:08:34 +08:00
wonfen
18139c7114 chore: adjust translation 2024-10-17 05:09:00 +08:00
huzibaca
4a64654f49
chore: update deps 2024-10-16 22:49:24 +08:00
huzibaca
377ff279b8
chore: update 2024-10-16 02:55:23 +08:00
huzibaca
d5d39e1c2a
fix: system proxy cannot be closed on mac 2024-10-16 02:52:48 +08:00
huzibaca
f7b1da7557
chore: update 2024-10-16 01:55:22 +08:00
huzibaca
5e8c6c6b23
chore : update version 2024-10-16 01:53:33 +08:00
huzibaca
64634ef07b
chore: t emporarily cancel window closure and change to minimize 2024-10-16 01:53:16 +08:00
huzibaca
7de2474611
Merge branch 'fix-migrate-tauri2-errors' of ssh://github.com/clash-verge-rev/clash-verge-rev into fix-migrate-tauri2-errors 2024-10-15 02:38:57 +08:00
huzibaca
ea2b6a9ad8
fix: path escape issue 2024-10-15 02:35:50 +08:00
wonfen
6ee7be7cae chore: update changelog and texts 2024-10-15 02:32:25 +08:00
huzibaca
12690ed464
chore: remove windows esc shortcut key 2024-10-14 13:45:26 +08:00
huzibaca
90361242ac
chore: update 2024-10-13 03:01:32 +08:00
huzibaca
cd8daa929a
chore: update deps 2024-10-13 02:55:35 +08:00
huzibaca
a07c579497
chore: adjust tun default parameters 2024-10-13 02:42:37 +08:00
huzibaca
e78f619be1
fox: external-controller cors error 2024-10-11 22:01:57 +08:00
huzibaca
5e7ff36e8e
chore: complete field identifier 2024-10-11 14:33:11 +08:00
huzibaca
e7e0caaa41
Merge branch 'main' into fix-migrate-tauri2-errors 2024-10-11 02:56:30 +08:00
Lvc Revincx
00c6e41239
fix: task bar icon misssing under linux wayland (#1816) 2024-10-11 02:52:57 +08:00
huzibaca
c604ae38dd
fix: installation service error 2024-10-11 00:59:34 +08:00
huzibaca
25e25c2285
chore: update 2024-10-10 23:34:55 +08:00
huzibaca
b4180b5b48
chore:update 2024-10-10 18:52:20 +08:00
huzibaca
d14bda7e7a
chore: update 2024-10-10 18:40:39 +08:00
huzibaca
045ddc5ca5
chore: update 2024-10-10 02:21:22 +08:00
huzibaca
3f3fad0db7
feat: Modify startup logic and install services by default 2024-10-10 00:34:36 +08:00
huzibaca
30c77b891d
chore: update 2024-10-09 01:14:03 +08:00
huzibaca
5e126364ed
refactor: core logic 2024-10-08 02:39:17 +08:00
huzibaca
7f2f9588ac
chore: update 2024-10-06 02:03:32 +08:00
huzibaca
e299e246e3
chore: revert 2024-10-06 01:09:59 +08:00
huzibaca
0541a0c69f
refactor: tun mode is turned on and off, does not depend on services, and does not affect other configurations 2024-10-05 22:45:59 +08:00
huzibaca
1178f7c892
refactor: init_log 2024-10-05 12:19:44 +08:00
huzibaca
a3e78bd76d
refactor: get_bypass func 2024-10-05 02:58:41 +08:00
huzibaca
bcf33b779e
chore: update 2024-10-05 00:03:11 +08:00
huzibaca
d0ba506011
chore: update deps 2024-10-04 23:56:50 +08:00
huzibaca
477d1f2aee
feat: add color to log 2024-10-04 22:38:06 +08:00
huzibaca
aa4b1ad8e8
chore: update 2024-10-04 05:37:53 +08:00
huzibaca
6b902a7ad8
chore: update 2024-10-04 05:27:59 +08:00
huzibaca
a626f2ce29
chore: update 2024-10-03 14:31:40 +08:00
huzibaca
2685a02e1a
chore: update 2024-10-03 12:01:06 +08:00
huzibaca
bf1f201d4b
feat: windows uses sysproxy.exe for system proxy 2024-10-03 02:09:22 +08:00
huzibaca
335578dd91
refactor: simplify sysproxy logic
1. close all proxies directly when reset_proxy
2. init_sysproxy and update_sysproxy combined into one
3. optimize lock usage
4 ptimize the thread loop of guard_sysproxy,
2024-10-03 01:31:37 +08:00
huzibaca
3a3fc986c4
chore: replace sysproxy-rs repo 2024-10-02 17:43:14 +08:00
huzibaca
0e1e54aff4
feat: test item, when icon is svg, add svg format check 2024-10-01 20:49:03 +08:00
huzibaca
0b8f24a92e
chore: optimize lock 2024-10-01 00:27:08 +08:00
huzibaca
3cbcd4630c
chore: remove the manual release lock 2024-09-30 23:07:46 +08:00
wonfen
709ab3dd88 chore: add service mode info 2024-09-30 01:04:15 +08:00
huzibaca
b3d93d0761
chore: update 2024-09-28 20:20:40 +08:00
huzibaca
79d1539149
refactor: logic optimization 2024-09-28 12:37:01 +08:00
huzibaca
ba1a7e0fd6
,fix: when updating the verge configuration, notification error 2024-09-27 22:25:23 +08:00
wonfen
30ebfd84cf chore: update bug report template 2024-09-27 14:08:50 +08:00
huzibaca
aef215d810
chore: update 2024-09-27 10:05:31 +08:00
huzibaca
cee872e944
chore: remove compatibility code 2024-09-27 10:04:23 +08:00
huzibaca
c50e8f9de8
refactor: logic optimization 2024-09-27 00:24:05 +08:00
huzibaca
11a8e3465f
chore: update 2024-09-26 19:47:25 +08:00
huzibaca
e8ed7c1e3e
chore: remove compatibility code 2024-09-26 19:47:00 +08:00
huzibaca
5b822d238d
chore: update 2024-09-26 19:23:16 +08:00
huzibaca
053754fea8
chore: update 2024-09-26 12:20:57 +08:00
huzibaca
7a9fd118a3
chore: update 2024-09-25 21:20:36 +08:00
huzibaca
2aa3c8cfe1
chore: update 2024-09-25 21:07:01 +08:00
huzibaca
ec2bf37ad4
Revert "chore: cover panic error"
This reverts commit b8cb48aeada25ae71371ee48a0010f6fa9b68c8c.
2024-09-25 20:22:49 +08:00
huzibaca
fc5c959a55
chore: unified hotkey registration 2024-09-25 15:45:12 +08:00
huzibaca
b8cb48aead
chore: cover panic error 2024-09-25 15:44:05 +08:00
huzibaca
4eccedcd78
fix: the save_indow_size_position method has been triggered twice, at the same time
1. remove windowEvent::destoryed  hook
2024-09-25 13:54:05 +08:00
huzibaca
08b1160d63
chore: remove useless exit codes 2024-09-25 11:47:01 +08:00
huzibaca
3bcd8b8b2c
fix: code lint 2024-09-24 20:11:33 +08:00
huzibaca
4b20b65a22
fix: code lint 2024-09-24 20:09:30 +08:00
huzibaca
4b234a3211
fix: code lint 2024-09-24 20:06:25 +08:00
huzibaca
2ca7310baf
chore: add cross-env dev dependency 2024-09-24 16:35:19 +08:00
huzibaca
5d742a9037
chore: update 2024-09-23 23:57:08 +08:00
huzibaca
a3465d292c
refactor: unify and simplify the call of app_handle(2) 2024-09-23 23:15:51 +08:00
huzibaca
8a3a094414
chore: update 2024-09-23 17:15:28 +08:00
huzibaca
52d5f2c992
chore: update 2024-09-23 16:53:00 +08:00
huzibaca
60e0f972d0
refactor: unify and simplify the call of app_handle 2024-09-23 16:31:58 +08:00
huzibaca
0a9182519d
chore: update 2024-09-22 22:15:36 +08:00
huzibaca
bfded924d7
chore: rename method 2024-09-22 20:29:43 +08:00
huzibaca
c653c458b9
Revert "chore: update"
This reverts commit 4b7ffa146525d035434f3104a82319b6ad527ce9.
2024-09-22 16:46:24 +08:00
huzibaca
4b7ffa1465
chore: update 2024-09-22 15:59:59 +08:00
huzibaca
a5c871e933
chore: update 2024-09-22 15:39:52 +08:00
huzibaca
56983c4d2a
chore: update 2024-09-22 14:08:08 +08:00
huzibaca
e9721ecc4d
Revert "chore: remove useless exit codes and hooks"
This reverts commit 40ff3fd4bf59f52f23904c3150de6ff17974d23f.
2024-09-22 00:41:20 +08:00
huzibaca
84fe9c84a8
fix: remove unused variable 2024-09-22 00:32:40 +08:00
huzibaca
40ff3fd4bf
chore: remove useless exit codes and hooks 2024-09-21 21:05:34 +08:00
huzibaca
e92074e586
chore: try adjusting the triggering of the tray mouse button 2024-09-21 15:38:14 +08:00
huzibaca
424c1f7d8f
chore : update 2024-09-20 18:02:19 +08:00
huzibaca
868707dbde
fix: tray menu text was not aligned 2024-09-20 17:59:24 +08:00
huzibaca
29d54fe589
chore: update 2024-09-20 17:49:31 +08:00
huzibaca
c246ae3bcf
Fix: Custom tray icon not working
1. Contains potential deadlock
2024-09-20 16:26:23 +08:00
huzibaca
ab887c656a
fix: function parameter call error 2024-09-20 08:27:06 +08:00
Sukka
eefeec7f0a
chore: drop fs-extra (#1739)
nb!
2024-09-20 02:01:36 +08:00
huzibaca
74777df344
chore: update dev dependencis 2024-09-18 21:34:20 +08:00
huzibaca
c21ce578f5
fix: exit hotkey conflict 2024-09-18 15:16:43 +08:00
huzibaca
7d1b7adda5
fix: whether the window starts as fullscreen or not. 2024-09-18 13:23:27 +08:00
huzibaca
f64528dd87
chore: revert 2024-09-18 12:24:52 +08:00
huzibaca
3ca1d03d34
refactor: exit app 2024-09-18 02:48:55 +08:00
huzibaca
49929bd473
fix: the shortcut key to exit cannot be used 2024-09-18 01:41:46 +08:00
huzibaca
25f15e12cf
fix: hotkey status not accurately processed, resulting in two triggers 2024-09-17 10:59:39 +08:00
huzibaca
b6d15c27b6
chore: update 2024-09-17 08:28:13 +08:00
huzibaca
2f8c5c9b20
chore: update 2024-09-17 08:02:29 +08:00
huzibaca
bee1373f92
chore: add window to allow full screen configuration 2024-09-16 15:25:01 +08:00
huzibaca
8a4535d55e
chore: update 2024-09-16 07:08:22 +08:00
huzibaca
613fb60859
chore: update 2024-09-16 06:43:25 +08:00
huzibaca
18f9978fcf
chore: update 2024-09-16 06:37:39 +08:00
huzibaca
9b527cb53c
chore: update 2024-09-16 06:17:22 +08:00
huzibaca
c06e4af79d
chore: update 2024-09-16 05:50:05 +08:00
huzibaca
e89c3ed65e
chore: modify application description definition 2024-09-16 05:38:52 +08:00
huzibaca
eb60cfb083
fox: try to restore the processing logic of windows custom scheme 2024-09-16 05:30:29 +08:00
huzibaca
65ce7429b7
chore: update dev workflow 2024-09-15 07:57:34 +08:00
huzibaca
4b4a4927c4
chore: add log 2024-09-15 07:03:04 +08:00
huzibaca
f57d2df5ec
chore: follow official standards and adjust the main file 2024-09-15 06:24:53 +08:00
wonfen
d70e18ba8d chore: update change log and texts 2024-09-15 00:33:54 +08:00
huzibaca
962aeb1c75
fix: when importing subscriptions, force the window to open 2024-09-14 10:03:19 +08:00
huzibaca
6d7711f3ea
fix: after importing a subscription, it cannot be automatically switched to the current subscription. 2024-09-13 18:17:14 +08:00
huzibaca
ba487611ee
chore: update action(rust-toolchain) 2024-09-13 14:21:51 +08:00
huzibaca
6a636444a7
chore: update action(rust-toolchain) 2024-09-13 14:14:27 +08:00
wonfen
25c766ed94 chore: change folder name 2024-09-13 12:03:28 +08:00
huzibaca
dbc3723bdc
feat: Added web notification of successful subscription import 2024-09-13 11:24:49 +08:00
huzibaca
3963c60fc1
fix: when the service is not installed, the tray hides the Tun mode 2024-09-13 04:12:25 +08:00
huzibaca
b888c7729e
refactor: url scheme implementation 2024-09-13 03:21:55 +08:00
huzibaca
a39ece6156
chore: delete useless emebd server api 2024-09-12 19:01:08 +08:00
huzibaca
624eb5b085
fix:tray click event 2024-09-12 17:02:17 +08:00
huzibaca
9ec8c903c5
feat: Optimize kernel startup logic
1. tun mode startup logic
2. Remove invalid creation process PID logic
2024-09-12 15:35:08 +08:00
huzibaca
2396a6b35a
feat: log panic 2024-09-12 07:59:51 +08:00
huzibaca
1fc54e49d9
chore: update deps 2024-09-12 06:14:23 +08:00
huzibaca
30ee7a9e85
fix: missing items in system tray 2024-09-12 05:45:15 +08:00
huzibaca
b9be5e8c44
feat: migrate tauri 2.0 2024-09-11 08:15:03 +08:00
huzibaca
ae1c6d4617
chore: update 2024-09-04 07:53:16 +08:00
huzibaca
7b11e157c4
chore: update capabilities 2024-09-04 05:57:37 +08:00
wonfen
5783b7b392 chore: 2.0.0 beta release 2024-09-03 00:00:12 +08:00
huzibaca
507d52bcb9
chore: update 2024-09-02 19:33:17 +08:00
wonfen
d45929c604 chore: use in-house update proxy 2024-08-24 02:02:17 +08:00
wonfen
632c32addb Style: fix macos new version btn position 2024-08-22 07:33:51 +08:00
wonfen
7d2b18759e Release 1.7.7 2024-08-22 03:47:33 +08:00
wonfen
aa8f6f4ad6 chore: Shorten text to prevent Windows tooltip truncation. 2024-08-22 03:41:16 +08:00
wonfen
592167ffb7 fix: profiles will not be selected after import 2024-08-22 00:57:04 +08:00
wonfen
12a6bfad00 Release 1.7.6 2024-08-18 00:49:23 +08:00
tony-sung
1ea3b956ad
Update web-ui-viewer.tsx Fix yacd parameter hostname error (#1484) 2024-08-18 00:23:24 +08:00
MystiPanda
1b7dafe743
feat: auto select profile 2024-07-30 09:06:12 +08:00
MystiPanda
624eb2a2ba
chore: prepend new added rules 2024-07-24 14:25:06 +08:00
MystiPanda
88d98517c8
fix: wrap the password in quotes
#1460
2024-07-24 14:22:23 +08:00
Avan
9528606906
style: AddressDisplay padding (#1457) 2024-07-23 22:17:51 +08:00
nhsmw
09b44a3af3
fix: update test url 2024-07-19 10:32:41 +08:00
MystiPanda
dc861eca7d
chore: remove unnecessary console log 2024-07-18 23:37:54 +08:00
MystiPanda
e749fe70e2
fix: use group testUrl
#1384
2024-07-18 15:04:55 +08:00
MystiPanda
d20c082deb
fix: windows x86 updater url 2024-07-18 14:38:31 +08:00
MystiPanda
c4abc3ade3
fix: error command status 2024-07-16 18:37:56 +08:00
MystiPanda
ff28aab56e
fix: MacOS service install error 2024-07-16 13:01:27 +08:00
MystiPanda
1a6f842492
fix(#1335): support cidr 2024-07-16 12:22:11 +08:00
MystiPanda
af0cd4a342
fix: try to fix install service 2024-07-16 12:07:23 +08:00
MystiPanda
d98b3224cf
chore: disable okBtn 2024-07-15 23:38:49 +08:00
MystiPanda
f2e5f0754c
ci: fix winget uploader 2024-07-15 23:27:38 +08:00
MystiPanda
ec1601f317
Release 1.7.5 2024-07-15 22:35:33 +08:00
MystiPanda
54116608dc
ci: fix winget uploader 2024-07-15 22:33:57 +08:00
MystiPanda
fea6735ea1
fix: copy necessary files 2024-07-15 22:25:04 +08:00
MystiPanda
2f77a07b3d
Release 1.7.4 2024-07-15 20:56:18 +08:00
MystiPanda
4cd7ccda57
fix: unified switch style 2024-07-15 20:42:00 +08:00
MystiPanda
ef337ffb69
feat: optimize the service mode interaction logic. 2024-07-15 20:02:05 +08:00
MystiPanda
9ff5d8d4e0
Revert "fix: try to fix abnormal stuck"
This reverts commit e14ddbe23cae0bc5437b9ba9184e35909e6825c4.
2024-07-14 12:49:06 +08:00
Sukka
199a5eed60
fix(#1387): avoid catastrophic backtracking (#1396) 2024-07-14 12:26:02 +08:00
MystiPanda
e14ddbe23c
fix: try to fix abnormal stuck
#1387
2024-07-13 21:41:04 +08:00
MystiPanda
3878e66ea4
chore: update locale 2024-07-13 19:15:07 +08:00
MystiPanda
c14a33444b
chore: unified icon style 2024-07-13 19:01:16 +08:00
MystiPanda
48f7c15035
feat: display network interface 2024-07-13 14:15:13 +08:00
Avan
32212a46e2
feature: copy clash env (#1391)
* feat: copy clash env

* style: use ContentCopyRounded replace CopyAll
2024-07-13 01:03:46 +08:00
Avan
b4025c45da
style: EditorViewer title align center (#1390) 2024-07-12 23:29:55 +08:00
MystiPanda
bd589c4422
fix: service install failed on macos 2024-07-12 20:54:31 +08:00
dongchengjie
f85f7758e6 chore: checkbox items' title & button icons 2024-07-12 18:24:19 +08:00
dongchengjie
49e36b6e00 chore: editor resizing debounce 2024-07-08 23:18:06 +08:00
dongchengjie
df6e245e5c chore: remove dialog overflow-scroll 2024-07-08 22:07:43 +08:00
dongchengjie
4e4f9af5b7 fix: editor fails to resize after toggling on macos 2024-07-08 21:40:56 +08:00
MystiPanda
aa9e63b51a
fix dev.yml 2024-07-08 16:17:23 +08:00
MystiPanda
82340582b2
ci: add dev workflow 2024-07-08 13:20:36 +08:00
Akioe Yu
9d045e24db
fix: dnd box z-index (#1353)
* fix: dnd box z-index

* fix: dnd boxes
2024-07-08 10:49:57 +08:00
MystiPanda
1dcc95d16a
perf: optimized response speed 2024-07-08 00:29:49 +08:00
dongchengjie
78c32d51db chore: group types locale 2024-07-07 21:56:20 +08:00
❤是纱雾酱哟~
545a2c6688
ci: Integrate "Winget Releaser" (#1326) 2024-07-07 20:42:17 +08:00
MystiPanda
359d9285fe
perf: change port too slow 2024-07-07 18:22:02 +08:00
MystiPanda
1bd51be99c
feat: get network interface 2024-07-07 18:13:10 +08:00
dongchengjie
ee56080af0 fix: usage percent style 2024-07-07 18:08:02 +08:00
MystiPanda
18196c4a77
fix: trojan uri parser 2024-07-07 11:47:49 +08:00
MystiPanda
14cd3b99cc
fix: tray icon size
#1341
2024-07-07 11:18:22 +08:00
MystiPanda
ff75fe47ee
chore: disable browser autocomplete 2024-07-07 11:16:59 +08:00
MystiPanda
7e2cc180c3
Release 1.7.3 2024-07-06 18:48:25 +08:00
MystiPanda
bef9eea87b
fix: bypass reg error
#1335
2024-07-06 18:23:23 +08:00
MystiPanda
9cd1aef1db
fix: limite cipher types 2024-07-06 10:49:42 +08:00
MystiPanda
ab25cf0637
fix: rule parser 2024-07-06 10:35:29 +08:00
MystiPanda
32f37720dd
Revert "chore: add base64 decode step"
This reverts commit 38eb3123beb9820e19cbeba3cbd015e38ee282cf.
2024-07-06 10:12:51 +08:00
MystiPanda
766cf3aeae
chore: decode base64 2024-07-06 10:12:40 +08:00
MystiPanda
905353d540
fix: some error 2024-07-06 09:54:16 +08:00
MystiPanda
38eb3123be
chore: add base64 decode step 2024-07-06 00:57:38 +08:00
MystiPanda
0183edd450
fix: style 2024-07-06 00:45:21 +08:00
MystiPanda
2f640a946e
fix: type 2024-07-06 00:23:52 +08:00
MystiPanda
ec6c2adf9b
chore: Improve URI parser 2024-07-05 22:44:05 +08:00
MystiPanda
5a1edc5ffb
feat: finish rpoxies editor 2024-07-05 19:49:32 +08:00
MystiPanda
149d482c7d
fix: groups config type error
feat(unfinished): add proxy editor
2024-07-05 00:38:50 +08:00
MystiPanda
c9f784e4fa
feat: add profile name param for script 2024-07-04 23:11:54 +08:00
MystiPanda
9edafa75e7
build: remove unused resource file 2024-07-04 22:34:48 +08:00
MystiPanda
08baba545d
chore: disable unnecessary ports by default 2024-07-04 18:58:34 +08:00
MystiPanda
60d0b29236
feat: support cache for groups editor 2024-07-04 18:53:39 +08:00
MystiPanda
f5ee4fb5b5
chore: unified style 2024-07-04 18:50:21 +08:00
MystiPanda
5b7b3be6f9
fix: useseq error 2024-07-04 14:30:34 +08:00
MystiPanda
c933560102
fix: locale typo 2024-07-04 14:18:21 +08:00
MystiPanda
12a80f35fe
feat: support visual edit for proxy group 2024-07-04 14:13:19 +08:00
MystiPanda
43f901eeb9
fix: rules editor get groups error 2024-07-04 11:03:51 +08:00
MystiPanda
e8440e06a1
feat: support get merged rule-set name 2024-07-03 22:13:24 +08:00
dongchengjie
19e9e9d032 fix: remove rule condition where is not required 2024-07-03 17:03:12 +08:00
MystiPanda
8ff2d687e4
Release 1.7.2 2024-07-03 13:26:06 +08:00
MystiPanda
e616e8f9aa
fix: Insertion order
#1300
2024-07-03 13:13:54 +08:00
MystiPanda
1fbc67fe98
fix: dialog styles 2024-07-03 13:13:29 +08:00
MystiPanda
a49e4d8d0c
fix: panic 2024-07-03 12:49:29 +08:00
MystiPanda
fa4ac00504
feat: support edit rules file 2024-07-03 12:37:08 +08:00
MystiPanda
dc066edec4
fix: edit groups error 2024-07-03 10:58:57 +08:00
MystiPanda
eaed7b2899
Release 1.7.1 2024-07-03 09:52:19 +08:00
MystiPanda
123ecc3548
fix: rules drag error 2024-07-03 09:45:14 +08:00
MystiPanda
c857fcf035
fix: compatibility 2024-07-03 08:15:13 +08:00
dongchengjie
923f710e53 fix: search-box takes no effect in rule-editor #1288 2024-07-03 03:08:01 +08:00
dongchengjie
8d8c2ed262 fix: sub usage percent fails to display when number is too small #1290 2024-07-03 02:59:32 +08:00
MystiPanda
8558673a5a
Release 1.7.0 2024-07-03 00:32:14 +08:00
MystiPanda
7372f330a4
perf: optimize performance of the rule editor 2024-07-02 23:55:29 +08:00
dongchengjie
07c145c661 feat: editor add tool-tar buttons 2024-07-02 23:24:44 +08:00
dongchengjie
f69e1d2a0c refactor: editor-viewer using react-monaco-editor 2024-07-02 21:02:29 +08:00
dongchengjie
0021fc24bb chore: bump schema to 1.18.5-alpha7 2024-07-02 19:38:44 +08:00
MystiPanda
82bc1d5da5
chore: disable autocomplete 2024-07-02 19:02:05 +08:00
MystiPanda
ffc343b471
fix: default value of global extend config 2024-07-02 18:46:23 +08:00
dongchengjie
86e43123b0 chore: add rule list search-box 2024-07-02 17:32:49 +08:00
dongchengjie
b558202441 chore: rule types locale 2024-07-02 17:04:22 +08:00
MystiPanda
83947b6725
chore: check the validity of the rule content 2024-07-02 13:11:54 +08:00
MystiPanda
7e8b65e61f
chore: Adjust the chain processing execution order and default value 2024-07-02 12:40:28 +08:00
MystiPanda
cff0ea425c
build: update depends 2024-07-02 12:21:17 +08:00
MystiPanda
f6ba6d0310
chore: update locale 2024-07-01 23:30:14 +08:00
MystiPanda
7b9bf9e552
feat: allow set bypass without using default value 2024-07-01 22:53:32 +08:00
Sukka
c91ad1e016
ci: speed up cargo install by enabling cache (webview2) (#1283) 2024-07-01 14:23:21 +08:00
MystiPanda
b1444b8635
feat: global merge and script 2024-07-01 08:25:03 +08:00
MystiPanda
51a49b94d8
feat: drag to reorder rules 2024-06-30 23:16:21 +08:00
MystiPanda
720b46d790
chore: select rule-set name 2024-06-30 22:46:11 +08:00
MystiPanda
9089c30d57
feat: display rules list 2024-06-30 22:34:26 +08:00
MystiPanda
e2d522803c
fix: editor init error 2024-06-30 18:05:38 +08:00
Sukka
babcb00621
ci: speed up cargo install by enabling cache (#1279) 2024-06-30 17:21:55 +08:00
MystiPanda
901a983150
feat: rules editor 2024-06-30 17:17:04 +08:00
MystiPanda
955182b05b
chore: locale 2024-06-30 13:03:36 +08:00
MystiPanda
f4dfe8eeb4
feat(unfinished): rules editor 2024-06-30 12:46:31 +08:00
MystiPanda
93904b8278
refactor: pure merge 2024-06-30 07:58:44 +08:00
MystiPanda
495580ae2b
fix: delete logic 2024-06-30 07:37:52 +08:00
MystiPanda
3051004217
chore: update locale 2024-06-30 07:18:10 +08:00
MystiPanda
b854b5e1ac
feat: support seq editor 2024-06-30 00:22:05 +08:00
MystiPanda
cf61a96ef6
refactor: Associate Profile with Merge/Script. 2024-06-29 23:07:44 +08:00
MystiPanda
1293d25e1b
refactor: use async instead of block_on 2024-06-29 19:02:37 +08:00
dongchengjie
c1a201f358 fix: do not reactive when changed profile is not current 2024-06-29 11:22:11 +08:00
dongchengjie
9ee5390ec7 feat: reactive after save when profile content changes 2024-06-29 09:21:50 +08:00
dongchengjie
3f1caa702b fix: #1261 2024-06-29 05:02:06 +08:00
wonfen
24f9573c05 chore: add TG link & descriptions 2024-06-29 04:55:43 +08:00
dongchengjie
83b25920ea chore: add descriptions for Miscellaneous 2024-06-28 15:54:27 +08:00
MystiPanda
834edcd03e
fix: unset dns when exit 2024-06-28 12:18:18 +08:00
MystiPanda
8f3801e3c2
feat: set dns by service 2024-06-28 11:45:44 +08:00
dongchengjie
b5f7c58276 chore: remove duplicate locales 2024-06-26 19:11:39 +08:00
dongchengjie
66ae293ddd fix: await compatibility in #8e78b9e 2024-06-26 18:29:50 +08:00
dongchengjie
8e78b9e405 feat: support manual memory cleanup when running in debug mode 2024-06-26 17:44:42 +08:00
dongchengjie
9da1759247 chore: cleanup 2024-06-26 08:24:43 +08:00
dongchengjie
704c41c0f2 refactor: extract tooltip icon as component 2024-06-26 08:10:18 +08:00
dongchengjie
1f422afe3d chore: tooltips and locales 2024-06-26 05:33:06 +08:00
dongchengjie
709a23cf09 chore: fix connection table bg 2024-06-23 20:40:09 +08:00
dongchengjie
bfa3fa293f fix: update_interval won't save when creating local profile while updating does & number input locales 2024-06-23 06:47:51 +08:00
MystiPanda
d8b878b1bb
feat: handle break change update 2024-06-22 21:05:46 +08:00
dongchengjie
b2589dbc04 chore: hotkeys display delimiter 2024-06-21 18:24:58 +08:00
MystiPanda
2a4b605794
chore: hide delay for preset outbound 2024-06-21 00:16:41 +08:00
MystiPanda
a3080a3373
revert: just kill 2024-06-20 14:27:13 +08:00
MystiPanda
b4b9ae5d7f
feat: custom dmg background 2024-06-20 13:50:28 +08:00
dongchengjie
bb9a93e462 chore: fix table header bg in connections 2024-06-20 02:01:51 +08:00
dongchengjie
e55fbf675e chore: disable shortcuts #1239 2024-06-20 01:39:51 +08:00
MystiPanda
0342477733
fix: runtime error 2024-06-19 11:10:40 +08:00
MystiPanda
e6e2b1f142
refactor: change core binary name 2024-06-19 10:04:28 +08:00
MystiPanda
95c23a93cd
refactor: kill core by process name 2024-06-18 12:36:58 +08:00
MystiPanda
ae784cb985
fix: reg error 2024-06-17 16:38:30 +08:00
Sukka
17765bdba9
fix(#1226): missing conn unsub (#1228) 2024-06-17 13:36:35 +08:00
Sukka
45f791b3c8
refactor(connections): use swr subscription (#1226) 2024-06-17 13:14:36 +08:00
dongchengjie
b0ad39ed0b fix: resizing reset when data changes 2024-06-17 13:03:47 +08:00
Sukka
1988aeb945
refactor(log): use swr subscription (#1220) 2024-06-17 10:48:37 +08:00
MystiPanda
f48a5710aa
chore: update depends 2024-06-17 10:46:18 +08:00
Sukka
c8d2410c27
feat: add a wrapper around sockette w/ error retry (#1219)
* feat: add a wrapper around sockette w/ error retry

* chore: use import path alias

* perf: reduce retry
2024-06-16 18:25:33 +08:00
MystiPanda
a9ef32c32f
Revert "feat: disable running with admin permission and check service mode"
This reverts commit a0f9fb90ee7034043f1d8279db9e31f753efe74d.
2024-06-16 12:06:23 +08:00
Sukka
19b6f78c8a
ci(alpha): avoid race by cancel non-latest concurrent runs (#1213) 2024-06-16 01:10:30 +08:00
Sukka
4ab507fd43
fix: hide save button in readonly editor view (#1208) 2024-06-15 19:24:26 +08:00
Sukka
2662df2547
fix(#1203): correct types (#1207) 2024-06-15 19:23:58 +08:00
Sukka
0332415ac9
perf: replace Array#map Array#filter chain w/ Array#reduce (#1203) 2024-06-15 12:22:33 +08:00
MystiPanda
a0f9fb90ee
feat: disable running with admin permission and check service mode 2024-06-14 23:15:49 +08:00
MystiPanda
1b8c4cb832
build: downgrade auto-launch 2024-06-14 21:26:30 +08:00
MystiPanda
cfd50d281b
fix: fix bypass check regex 2024-06-14 21:23:58 +08:00
Sukka
1667856894
chore: use swr subscription for layout traffic / memory (#1202)
* chore: update swr to 2

* refactor: use swr subscription for memory & traffic

* refactor: introduce `sockette`
2024-06-14 18:23:29 +08:00
dongchengjie
c5c76ab539 chore: update metacubexd #1200 2024-06-14 17:09:12 +08:00
dongchengjie
eb060d2e43 feat: local profile name autofill #1191 2024-06-13 16:29:25 +08:00
MystiPanda
b5556613cf
refactor: remove grant logic 2024-06-13 16:07:56 +08:00
MystiPanda
ad1a057edb
fix: check service 2024-06-13 12:58:47 +08:00
MystiPanda
8342f985e5
ci: fix release 2024-06-12 12:25:12 +08:00
MystiPanda
9d32e58915
chore: update publisher 2024-06-12 11:51:44 +08:00
MystiPanda
84027cbae1
Release 1.6.6 2024-06-12 10:33:20 +08:00
MystiPanda
5343040ef3
fix: start param error 2024-06-12 10:19:23 +08:00
MystiPanda
75cd7dd4b9
chore: update & fmt & clippy 2024-06-12 10:00:22 +08:00
dongchengjie
fb6ffd8e09 chore: missing locale 2024-06-11 16:19:25 +08:00
dongchengjie
aca9e61f39 chore: locale "Invalid Bypass Format" 2024-06-09 20:02:20 +08:00
MystiPanda
56078098a4
fix: start param error 2024-06-09 20:00:17 +08:00
MystiPanda
1f197965e0
feat: check bypass format 2024-06-09 13:37:47 +08:00
MystiPanda
183f0f6c42
feat: keep default bypass 2024-06-09 12:45:57 +08:00
dongchengjie
76624c7d83 chore: missing locale 2024-06-09 11:16:13 +08:00
dongchengjie
ab47c56ec5 chore: locale reorganization 2024-06-09 09:12:14 +08:00
Eric Huang
ad35ed96c8
feat(settings page): add loading state (#1157)
* feat(settings page): add loading state

* fix: type
2024-06-09 06:26:07 +08:00
wonfen
97bd51cf5f chore: update ci with apple notarization 2024-06-09 02:01:33 +08:00
MystiPanda
f4a110cfd7
ci: fix error 2024-06-08 20:58:57 +08:00
MystiPanda
e9ed06adfd
ci: update 2024-06-08 20:45:28 +08:00
MystiPanda
bc5eaf34fe
feat: add download button on updater dialog
#1129
2024-06-08 20:35:23 +08:00
MystiPanda
3e8b891dd0
fix: run app as normal user 2024-06-08 20:20:47 +08:00
MystiPanda
3f2ea4bc64
build: update depends 2024-06-08 20:02:25 +08:00
MystiPanda
ae81b37c1d
build: remove appimage 2024-06-08 19:36:58 +08:00
MystiPanda
b4349afb1b
ci: codesign 2024-06-08 18:52:34 +08:00
dongchengjie
0cffe8e3f7 chore: disbale Meta+Q on macOS 2024-06-08 13:12:24 +08:00
Sukka
7a1f4f9abe
refactor: simplify useConnectionSetting (#1141) 2024-06-07 17:23:53 +08:00
wonfen
5768b01786 fix: apple sign error again 2024-06-07 17:19:44 +08:00
MystiPanda
7e9079467f
fix: sign error 2024-06-07 16:39:06 +08:00
MystiPanda
615e96922e
fix: sign error 2024-06-07 16:37:58 +08:00
Eric Huang
8b94c452fb
feat: make SettingItem clickable (#1138)
* feat: make `SettingItem` clickable

* clean up
2024-06-07 15:51:51 +08:00
Sukka
66dd510acc
refactor: replace recoil (#1137) 2024-06-07 12:27:37 +08:00
wonfen
96e044566c Style: modify proxy pages 2024-06-07 10:32:27 +08:00
MystiPanda
726ad84d7f
fix: sign error 2024-06-06 16:06:58 +08:00
wonfen
4fc422f880 chore: apple developer sign test 2024-06-06 15:21:53 +08:00
dongchengjie
1938024ab3 chore: test_delay trace 2024-06-05 19:17:43 +08:00
dongchengjie
05b1de6fd5 chore: theme colors to uppercase 2024-06-05 14:20:24 +08:00
dongchengjie
0bf8807b50 build: no legacy chunks 2024-06-05 09:04:08 +08:00
Sukka
23668ee74e
fix(#1126): add Object.hasOwn polyfill (#1127) 2024-06-05 00:12:06 +08:00
dongchengjie
6bc5c5a808 fix: csp missing asset: 2024-06-04 15:26:39 +08:00
MystiPanda
330004dc72
build: update depends 2024-06-04 12:16:42 +08:00
MystiPanda
a900d6d742
Release 1.6.5 2024-06-04 12:11:38 +08:00
MystiPanda
bfea70eec9
fix: package error 2024-06-04 10:47:17 +08:00
MystiPanda
13babbf330
feat: support rpm package 2024-06-04 10:16:25 +08:00
MystiPanda
0a6fe382ac
build: use latest tauri 2024-06-04 10:09:04 +08:00
Zhenfu Shi
dd6dea9dfc
chore: Enable Ad Hoc signing on macOS (#1117)
Closes #1116
2024-06-03 09:01:33 +08:00
wonfen
11434fba68 Revert "chore: change default test url"
This reverts commit 69b65d06a4cd0988f28ed52f91c13c833583b0dc.
2024-06-03 08:25:06 +08:00
dongchengjie
10bf90c92a chore: use icons files instead of hard coding 2024-06-03 07:21:40 +08:00
dongchengjie
5193fe4bab fix: group selected be overwritten when saving test 2024-06-03 06:38:20 +08:00
dongchengjie
4df4995623 chore: improve UI in connections page 2024-06-02 21:52:31 +08:00
aixiao0621
f5f4ecf46a fix: a display error on the connections page 2024-06-02 17:26:23 +08:00
MystiPanda
69b65d06a4
chore: change default test url 2024-06-01 23:14:47 +08:00
MystiPanda
e6bf4e836d
build: update depends 2024-06-01 23:01:27 +08:00
dongchengjie
01d67eb239 refactor: polyfills review 2024-05-30 20:27:12 +08:00
dongchengjie
3514cfbd44 fix: research when search box mode changes 2024-05-30 10:45:24 +08:00
dongchengjie
cade35fe10 chore: Content-Security-Policy 2024-05-29 10:20:56 +08:00
dongchengjie
2c6df77f5a ci: fix heap oom 2024-05-29 09:49:11 +08:00
dongchengjie
0276ae91b5 build: polyfills 2024-05-29 09:39:26 +08:00
dongchengjie
60fee2accb build: import babel 2024-05-28 09:59:41 +08:00
dongchengjie
03e2632294 fix: try to fix #1084 2024-05-27 23:19:44 +08:00
dongchengjie
194fe59458 typo: inconsistent style in layout-viewer 2024-05-27 16:37:48 +08:00
MystiPanda
e400111f8a Release 1.6.4 2024-05-26 22:07:53 +08:00
MystiPanda
8bc7607783 Revert "refactor: use axios tauri adapter"
This reverts commit 7e361cb3f195f18353446947b66775c36644237b.
2024-05-26 21:18:02 +08:00
dongchengjie
4108451ebe chore: %mixed-port% hint for PAC script 2024-05-26 19:53:42 +08:00
MystiPanda
0f56257aa1 Release 1.6.3 2024-05-26 19:43:26 +08:00
MystiPanda
2a575284b9 chore: add content type 2024-05-26 19:26:57 +08:00
MystiPanda
444bcbfb30 fix: auto proxy changed by guard 2024-05-26 19:07:14 +08:00
MystiPanda
212021c878 feat: Support PAC Mode 2024-05-26 17:59:39 +08:00
dongchengjie
600b687253 chore: emoji display support in editor 2024-05-25 18:02:32 +08:00
farzadhallaji
ae3aada172
Update fa.json (#1060) 2024-05-24 19:39:40 +08:00
MystiPanda
50cc8af8c3 ci: use rust 1.77.0 2024-05-23 11:47:41 +08:00
dongchengjie
1809cdbf37 fix: no snippets and warnings in runtime config editor 2024-05-21 06:18:09 +08:00
dongchengjie
fb38047769 chore: update locale in connection 2024-05-20 13:42:45 +08:00
dongchengjie
08e0d6a34a chore: text overflow word-wrap & cleanup 2024-05-20 12:38:41 +08:00
MystiPanda
b4fddaec7f build: use compatible 2024-05-19 19:30:53 +08:00
MystiPanda
3f19d22941 fix: installer package error 2024-05-19 19:13:21 +08:00
MystiPanda
c7d51cfc4c build: use latest core 2024-05-19 13:00:27 +08:00
dongchengjie
6829d34e90 chore: schema 1.18.5-alpha 2024-05-19 12:37:18 +08:00
dongchengjie
021e430103 chore: fix select component bg in connection 2024-05-18 18:42:51 +08:00
dongchengjie
ae6f42a7fb chore: fix search-box bg 2024-05-18 18:20:05 +08:00
MystiPanda
da98097394 chore: support more asset scope 2024-05-18 15:47:45 +08:00
oomeow
43f0b935cf
perf: memoize the proxy col items (#1029) 2024-05-18 15:14:22 +08:00
oomeow
db5d14e0ce
fix: clipboard doesn't work and set_shadow method is not supported in Linux (#1030) 2024-05-18 15:14:00 +08:00
dongchengjie
b25cf5eadb feat: editor import PAC definition 2024-05-18 14:59:17 +08:00
dongchengjie
bd3b41c809 chore: hint for canceling fixed #840 2024-05-17 20:44:18 +08:00
dongchengjie
31d301064a chore: use search-box in logs and connections 2024-05-17 19:44:42 +08:00
dongchengjie
b95b9bdaf3 chore: component base-search-box 2024-05-17 19:13:33 +08:00
dongchengjie
937f0b5687 chore: fix editor dialog scroll overflow 2024-05-17 04:03:49 +08:00
dongchengjie
ffbc892e44 fix: missing locale 2024-05-15 23:27:31 +08:00
dongchengjie
9e872932d1 chore: current bypass wrap 2024-05-15 22:59:53 +08:00
MystiPanda
ecb2fbf900 feat: support monochrome tray icon 2024-05-15 14:50:10 +08:00
dongchengjie
72edd2e15d chore: logo svg 2024-05-14 16:53:39 +08:00
dongchengjie
c3d22c554f feat: doc reference link on settings header 2024-05-14 14:40:47 +08:00
dongchengjie
e72f4a1582 chore: use coding fonts in editor 2024-05-14 14:20:50 +08:00
dongchengjie
5e43c060fa fix: switch missing break 2024-05-14 01:24:23 +08:00
dongchengjie
e8dee1ddd9 chore: @ts-ignore schema check 2024-05-14 01:21:28 +08:00
dongchengjie
d85b8c776f chore: revert schema to beta4 2024-05-14 00:59:05 +08:00
dongchengjie
fb518b6218 chore: palette locale 2024-05-13 23:12:29 +08:00
dongchengjie
8d510cde21 typo: unused import 2024-05-13 22:59:24 +08:00
dongchengjie
da4bf167ee feat: css injection editor 2024-05-13 22:58:25 +08:00
Remember
dc1abf8dcb
Update 172.16.0.0/12 on Windows (#1013) 2024-05-13 18:28:41 +08:00
MystiPanda
3fc969a50b fix: use default bypass when empty 2024-05-13 18:27:19 +08:00
MystiPanda
4064129d49 chore: Adapt Mac icon 2024-05-13 13:11:05 +08:00
MystiPanda
22b4e0bfff docs: Update README 2024-05-12 22:17:05 +08:00
MystiPanda
53fa1f88fd chore: Change HomePage Logo 2024-05-12 21:17:21 +08:00
MystiPanda
5fefcd92f3 chore: Try a new icon 2024-05-12 19:31:06 +08:00
dongchengjie
d20bc936da chore: ru locale 2024-05-12 16:18:49 +08:00
RikudouPatrickstar
e79f036a70
chore: update notification message and zh translation (#1011) 2024-05-12 14:40:18 +08:00
MystiPanda
0f2af91a04 perf: Disable Tun mode before shutting down 2024-05-12 11:06:44 +08:00
MystiPanda
7e361cb3f1 refactor: use axios tauri adapter 2024-05-11 21:54:56 +08:00
MystiPanda
1f7acd4027 feat: Allow disable unused ports 2024-05-09 09:22:32 +08:00
MystiPanda
dddca7bf4b chore: Update locale 2024-05-08 23:45:55 +08:00
MystiPanda
e520be80c0 perf: Limit drawing frame rate 2024-05-08 22:33:13 +08:00
MystiPanda
0b78dedf04 fix: bundle error 2024-05-08 14:52:38 +08:00
MystiPanda
8f409477c8 build: fix bundle error 2024-05-08 14:20:30 +08:00
MystiPanda
9c22f7ce69 fix: depend error 2024-05-08 13:24:34 +08:00
MystiPanda
05fa916915 Release 1.6.2 2024-05-08 13:14:00 +08:00
MystiPanda
9a593d7a61 feat: support upgrade for release core 2024-05-08 12:48:27 +08:00
MystiPanda
30017eeaa0 docs: Update FAQ URL 2024-05-08 11:44:57 +08:00
MystiPanda
9b03cacc94 ci: update ci 2024-05-08 11:12:06 +08:00
MystiPanda
557970cf54 refactor: remove prepend and append for provider 2024-05-08 00:22:14 +08:00
MystiPanda
d90dac60ef feat: deep merge
#983
2024-05-08 00:13:32 +08:00
MystiPanda
b4b2e67260 build: Add portable for fixed webview2 2024-05-06 19:13:08 +08:00
MystiPanda
bcd5e935e7 ci: debug ci 2024-05-06 16:16:47 +08:00
MystiPanda
b322627609 ci: fix ci script 2024-05-06 15:30:00 +08:00
MystiPanda
09861095ff ci: update release body 2024-05-06 15:15:07 +08:00
MystiPanda
47e567fb40 docs: update url 2024-05-06 15:05:56 +08:00
MystiPanda
5bb30ad28f build: try support fixed webview2 runtime 2024-05-06 14:18:12 +08:00
MystiPanda
a46bbf05ec ci: try resupport x86 2024-05-06 13:04:29 +08:00
dongchengjie
04a4705dbd fix: non-ascii character secret causes controller link error
https://github.com/clash-verge-rev/clash-verge-rev/issues/973#issuecomment-2094839700
2024-05-05 23:04:33 +08:00
MystiPanda
3271c96531 fix: enhance when change tun config 2024-05-04 16:12:10 +08:00
dongchengjie
05fa6b9aba chore: profile template typo 2024-05-04 14:24:11 +08:00
MystiPanda
7edd7f27cb fix: try to set env 2024-05-03 18:00:55 +08:00
dongchengjie
4a6b12eda1 fix: #974 2024-05-03 12:50:04 +08:00
MystiPanda
a354e1a0eb chore: update locale 2024-05-02 23:44:09 +08:00
dongchengjie
c2c419522a chore: add service mode locale 2024-05-02 22:16:05 +08:00
MystiPanda
7a3cc7d242 feat: Support drag and drop local files 2024-05-02 20:41:43 +08:00
MystiPanda
bd6f02f6af fix: change script execution path 2024-05-01 11:39:09 +08:00
MystiPanda
bd33728fd7 Release 1.6.1 2024-04-30 23:43:51 +08:00
MystiPanda
8400ee8a3d fix: MacOS tray click 2024-04-30 23:21:25 +08:00
lxk955
80ee7aef8e
feat: Support for using regular expressions on the log page #707 (#959) 2024-04-30 22:59:42 +08:00
MystiPanda
41762be3a5 fix: service install path 2024-04-30 20:20:39 +08:00
MystiPanda
605dda7b76 build: Restore core version 2024-04-30 09:42:25 +08:00
dongchengjie
8d6e3b5a58 fixup: wrong version 2024-04-30 02:13:24 +08:00
dongchengjie
53920fc368
chore: update pnpm-lock.yaml (#952)
* chore: update schema version

* Update pnpm-lock.yaml
2024-04-30 01:44:05 +08:00
dongchengjie
96d10423d9
chore: update schema version (#950) 2024-04-29 22:46:29 +08:00
MystiPanda
42be9b5b51 chore: Config issue template 2024-04-29 15:43:43 +08:00
MystiPanda
0add041516
docs: Update FAQ URL 2024-04-28 20:04:03 +08:00
MystiPanda
dee76cac8d
build: cargo update 2024-04-28 19:59:46 +08:00
MystiPanda
9646fab22c fix: Open the link with browser 2024-04-28 18:46:49 +08:00
dongchengjie
a54e9cb244
fixup: can't edit file (#943) 2024-04-28 17:00:33 +08:00
dongchengjie
f8339c7a9a
chore: disable WebView keyboard shortcuts (#942) 2024-04-28 16:19:13 +08:00
lxk955
9e2c864117
feat: support display current profile when the mouse is placed on the tray icon #782 (#938) 2024-04-27 20:54:02 +08:00
PlayerNeo
e1ccd71455
fix: disable left click menu on macOS (#930) 2024-04-27 20:52:29 +08:00
LiuTianYu
0e82426ea1
fix: #730 icon not change when the window is opened maximized (#924)
Co-authored-by: tyliu9 <tyliu9@toycloud.com>
2024-04-24 22:36:15 +08:00
wonfen
6c7afd168a Release 1.6.0 2024-04-23 14:25:09 +08:00
MystiPanda
e014fdf3da
feat: support ico format for tray icon (#911) 2024-04-22 20:43:15 +08:00
dongchengjie
2074da05c8
fix: #907 (#908) 2024-04-22 01:40:25 +08:00
dongchengjie
84cd87b70a
chore: update locale (#904)
* chore: missing locale

* chore: External Controller locale
2024-04-21 14:10:34 +08:00
dongchengjie
75f87044f6
chore: Proxy Bypass placeholder (#901) 2024-04-20 19:27:17 +08:00
dongchengjie
f71b51e64a
fix: minor glitches (#900)
* feat: show actual proxy name instead of proxy group when hovering on a group outbound

* fix: open empty edit form and save will cause `UID not found`

* chore: tauri.conf.json json schema

* chore: missing locales
2024-04-20 18:02:15 +08:00
MystiPanda
9d741cdd63 fix: startup script blocking 2024-04-20 17:52:54 +08:00
MystiPanda
56be17000a fix: default value for tun 2024-04-20 17:42:36 +08:00
MystiPanda
f6aacbc31d build: add depends 2024-04-19 15:55:46 +08:00
MystiPanda
ad4b57c327 chore: update lock file 2024-04-19 15:38:32 +08:00
dongchengjie
3794b2f0de
fix: 使用npm安装meta-json-schema (#895)
* feat: allow manual selection of url-test group

* feat: fixed proxy indicator

* fix: try to fix traffic websocket no longer updating

* fixup: group delay test use defined url

* feat: connections sorted by start by default

* feat: Connection details show the full path of the process

* fix: editor no hints and add yaml support

* feat: quick suggestions

* chore: use monaco-editor

* chore: update schema url

* chore: change default merge config content

* fix: load schema via npm

* feat: runtime config viewer style auto adjust

* feat: adjust fixed proxy style

* fix: headState "showType" won't toggle hover text

* chore: switch version

* chore: Update pnpm lockfile
2024-04-19 13:54:16 +08:00
MystiPanda
e0e8412728 refactor: change default value of mtu 2024-04-18 10:27:09 +08:00
dongchengjie
05b8175aad
chore: 修改Merge配置文件默认内容 (#889) 2024-04-17 22:56:54 +08:00
MystiPanda
339c89dd1c chore: Update pnpm lockfile 2024-04-17 22:16:50 +08:00
dongchengjie
402299e9c8
feat: Clash配置、Merge配置提供JSON Schema语法支持、[连接]界面调整 (#887) 2024-04-17 21:19:37 +08:00
MystiPanda
d0e8f450fd fix: SymbolicLink
#750
2024-04-13 15:46:11 +08:00
MystiPanda
e97b1ccd7a fix: service install path 2024-04-13 15:12:41 +08:00
汐殇
68691b91ef
fix: Allow core files in specific directories to be upgraded normally (#857) 2024-04-12 01:16:57 +08:00
dongchengjie
213d417481
feat: url-test支持手动选择、节点组fixed节点使用角标展示 (#840)
* feat: allow manual selection of url-test group

* feat: fixed proxy indicator

* fix: try to fix traffic websocket no longer updating

* fixup: group delay test use defined url
2024-04-09 13:15:45 +08:00
MystiPanda
2ec841eb61 fix: Update the home while updating profile 2024-04-04 15:07:55 +08:00
MystiPanda
60b5c54be1 fix: Use System Browser 2024-04-04 14:56:10 +08:00
Damian Johnson
448412a191
style: adjust confirm dialog & web ui settings dialog (#821) 2024-04-03 23:19:00 +08:00
Damian Johnson
e06327936e
fix: service mode install script download not found (#822) 2024-04-03 23:18:10 +08:00
dongchengjie
5f044e1aed
feat: support URL Schema 'profile-web-page-url' (#816) 2024-04-01 19:28:28 +08:00
HZ is not Chatty
d90fb6fc9b
fixup! feat: Service Mode for Linux (#804) (#815)
* fixup! feat: Service Mode for Linux (#804)

* fixup! feat: Service Mode for Linux (#804)

* Partially revert "fixup! feat: Service Mode for Linux (#804)"

This reverts commit e6a5a2b4961dba4e891b1b62d6f35db4ca9ee5ce.
2024-04-01 19:25:58 +08:00
MystiPanda
1c583d2ea9 feat: Try support service mode for MacOS 2024-03-31 23:16:47 +08:00
MystiPanda
ddab131102
fix: script error 2024-03-31 16:37:33 +08:00
HZ is not Chatty
3b06cf8a2a
feat: Service Mode for Linux (#804) 2024-03-31 16:16:23 +08:00
wonfen
c5d7c29f3d Sytle: fix mac logo padding 2024-03-30 16:31:17 +08:00
Damian Johnson
26af860eac
fix: icon not change when toggle window maxinized (#799) 2024-03-30 01:16:40 +08:00
cismous
ec9852eb98
Style filter input (#724)
* refactor: reduce duplicate code

* style: add a white background to the light color theme to avoid the gray text being too light
2024-03-30 01:14:03 +08:00
Damian Johnson
f2b8c6d0ca
fix: missing proxy group delay check animations (#788)
* fix: missing proxy group delay check animation

* chore: cleanup

* chore: adjust content style
2024-03-29 08:20:31 +08:00
wonfen
2cf7a048cf Release 1.5.11 2024-03-28 12:06:41 +08:00
MystiPanda
3dbb71a076 feat: Support Persian
#715
2024-03-21 20:51:33 +08:00
MystiPanda
ddf6760543 fix: Duplicate icon display error
#719
2024-03-21 20:29:16 +08:00
MystiPanda
a6fa323f33 fix: The update button cannot be clicked
#716
2024-03-21 19:56:30 +08:00
MystiPanda
2df8f1acdb fix: Check if the install directory is empty when uninstall 2024-03-21 15:24:12 +08:00
MystiPanda
74a3290abc
chore: fix typo 2024-03-21 14:09:59 +08:00
MystiPanda
ab306d21f3
Release 1.5.10 2024-03-21 13:47:39 +08:00
MystiPanda
1bd7795851
chore: Change a little 2024-03-21 12:55:22 +08:00
MystiPanda
302c142346
build: Update depends 2024-03-21 11:53:25 +08:00
MystiPanda
7c254c9b45
fix: a little 2024-03-21 11:43:16 +08:00
MystiPanda
b1c458359d
feat: Confirm before deletion
#703
2024-03-21 11:39:01 +08:00
MystiPanda
2d925c62f5
feat: Support config redir port and tproxy port 2024-03-21 10:54:56 +08:00
MystiPanda
ef4711987f chore: limit port config
#699
2024-03-20 21:23:10 +08:00
MystiPanda
8910dfaecf fix: Limit icon width
#697
2024-03-20 20:38:45 +08:00
MystiPanda
e8b2e79c7d fix: service logs are not cleared
#695
2024-03-20 20:31:00 +08:00
MystiPanda
333ee1e1f7
docs: Improve the issue template 2024-03-20 18:06:02 +08:00
MystiPanda
ba013bbe96
feat: Optimize Linux tray menu 2024-03-18 11:11:18 +08:00
MystiPanda
636e66fe7a
Revert "fix: remove activation policy" 2024-03-18 09:53:48 +08:00
MystiPanda
372a45e667
Release 1.5.9 2024-03-17 11:25:25 +08:00
MystiPanda
ab6b796ce2 fix: drag error
#643
2024-03-16 20:37:39 +08:00
MystiPanda
71e6c02897
fix: drag error 2024-03-16 17:06:59 +08:00
MystiPanda
3edeaa7038 fix: Try to fix touch drag
#456
2024-03-16 10:54:19 +08:00
MystiPanda
f2f75d4015 build: Update to Tauri 1.6.1 2024-03-16 00:03:34 +08:00
MystiPanda
69adade738 ci: update alpha script 2024-03-15 21:52:56 +08:00
MystiPanda
1bd88828e4 ci: remove 2024-03-15 21:42:55 +08:00
MystiPanda
977fcbe6cd feat: Try to support more architecture 2024-03-15 21:14:05 +08:00
MystiPanda
aa76a5c436 refactor: Try to migrate to boa_engine
#634
2024-03-15 19:58:22 +08:00
MystiPanda
ea5fad428d fix: Avoid empty user-agent 2024-03-15 18:27:24 +08:00
MystiPanda
0e1e27b35a feat: Try to cache remote images
#603
2024-03-15 16:43:39 +08:00
screw-hand
d41f67a156
fix: mac setting theme font-famil not working (#632) 2024-03-15 15:17:05 +08:00
MystiPanda
6f10dec808 fix: remove activation policy
#592
2024-03-15 12:44:25 +08:00
xkww3n
9b79da2cdf
fix: Set REJECT-DROP policy the same text color as REJECT (#622) 2024-03-14 18:54:26 +08:00
MystiPanda
c11b8519f1 Release 1.5.8 2024-03-13 14:17:06 +08:00
wonfen
b9e23a0b59 Sytle: A little tweak 2024-03-13 14:09:08 +08:00
MystiPanda
75d41b6fe5 feat: Add border-radius for window on linux 2024-03-13 13:58:29 +08:00
Amnesiash
4256590735
update profile ui (#594) 2024-03-13 10:53:39 +08:00
MystiPanda
462fb05ea8 fix: Try to fix #577 again 2024-03-11 22:52:29 +08:00
MystiPanda
812dbfd836 fix: script 2024-03-11 22:10:20 +08:00
MystiPanda
cfca837777 fix: typo 2024-03-11 22:05:56 +08:00
MystiPanda
ac5fb1948a fix: Try to fix #577 2024-03-11 22:03:45 +08:00
MystiPanda
22d8b73625 feat: Allow open devtools 2024-03-11 20:19:21 +08:00
MystiPanda
edde40c298 docs: Update Readme 2024-03-11 18:38:58 +08:00
MystiPanda
50ade92238 Release 1.5.7 2024-03-11 17:09:01 +08:00
MystiPanda
b598d00aef fix: display error 2024-03-11 16:18:39 +08:00
MystiPanda
97bee17e4e chore: Change IP 2024-03-11 16:05:09 +08:00
MystiPanda
6aa5c79332 fix: a little 2024-03-11 15:02:41 +08:00
MystiPanda
7c5ce756f9 fix: Change DNS for MacOS Tun Mode
#568
2024-03-11 14:55:00 +08:00
MystiPanda
43d53a9ffa fix: default value 2024-03-11 13:17:45 +08:00
MystiPanda
e6589bee5b fix: Try to fix touch drag
#456
2024-03-11 12:36:20 +08:00
MystiPanda
9f94cad615 feat: Allow to control whether auto check update 2024-03-11 12:17:46 +08:00
MystiPanda
7778d9f773 style: fix a little 2024-03-10 23:56:04 +08:00
MystiPanda
564bd55147 style: fix styles 2024-03-10 23:47:12 +08:00
MystiPanda
a7ff806522 fix styles 2024-03-10 22:13:25 +08:00
MystiPanda
ac3b074d66 chore: proxy group header height 2024-03-10 21:56:49 +08:00
MystiPanda
da949c2604 chore: adjust proxy group font style 2024-03-10 21:52:40 +08:00
MystiPanda
20a9775dc6 chore: Adjust Profile Style 2024-03-10 21:37:52 +08:00
MystiPanda
8ec19e058f chore: Add Default value 2024-03-10 21:15:22 +08:00
MystiPanda
a5c4549722 style: Adjust icon color 2024-03-10 20:48:13 +08:00
Amnesiash
c7d302aa16
style: Adjust colorful icon (#558)
Co-authored-by: MystiPanda <mystipanda@proton.me>
2024-03-10 20:37:00 +08:00
MystiPanda
77ac565ff3 chore: i18n 2024-03-10 20:33:27 +08:00
MystiPanda
2e6f834a50 chore: Update Icon 2024-03-10 20:14:51 +08:00
MystiPanda
5b044cca9e feat: Add option to control menu icon 2024-03-10 19:54:47 +08:00
MystiPanda
2ab5623809 chore: Adjust styles 2024-03-10 13:24:38 +08:00
MystiPanda
2d07f0d77c Revert "chore: Adjust secondary text style (#545)"
This reverts commit 8ba7f9f70216ac3ae5ff2ac859ec2170737d0cd1.
2024-03-10 13:08:09 +08:00
Amnesiash
8ba7f9f702
chore: Adjust secondary text style (#545) 2024-03-10 12:54:34 +08:00
MystiPanda
460ac7a86b style: Adjust delay fontSize
#544
2024-03-10 12:51:53 +08:00
MystiPanda
f93a1ab3eb docs: update preview 2024-03-10 12:32:08 +08:00
MystiPanda
6b02696aa8 chore: Change Default Font 2024-03-10 12:23:11 +08:00
MystiPanda
25f20d6a85 chore: delete debug output 2024-03-10 11:47:55 +08:00
MystiPanda
8387964bae Release 1.5.6 2024-03-10 11:43:37 +08:00
MystiPanda
926278617f feat: Add default webui
#530
2024-03-10 11:41:22 +08:00
MystiPanda
1933737a0c Adjust styles 2024-03-10 11:21:17 +08:00
MystiPanda
b3e5288bde Adjust styles 2024-03-10 11:12:54 +08:00
wonfen
ed6e966b2f Sytle: UI improvement & Update Readme 2024-03-10 07:00:24 +08:00
MystiPanda
9315fe36b6 fix: fix a little 2024-03-10 00:41:18 +08:00
black23eep
41fbf5ba36
Update layout-traffic.tsx (#528) 2024-03-10 00:35:26 +08:00
MystiPanda
40db481453 chore: update 2024-03-10 00:34:22 +08:00
MystiPanda
1b5e295744 fix: fontSize and some styles 2024-03-10 00:22:22 +08:00
Amnesiash
b42a52f7f6
fix settings.svg (#526) 2024-03-10 00:04:06 +08:00
MystiPanda
8268df84d0 chore: center 2024-03-09 23:38:03 +08:00
Charles
eb6fa5f1f1
tweak(ui): menu icon use svg component (#524) 2024-03-09 23:13:08 +08:00
MystiPanda
025c8856ed fix: img path error 2024-03-09 22:43:53 +08:00
MystiPanda
ed421445e9 Release 1.5.5 2024-03-09 22:02:36 +08:00
Amnesiash
0cda07106b
refactor: Upgrade to the new UI (#521)
Co-authored-by: MystiPanda <mystipanda@proton.me>
2024-03-09 21:37:21 +08:00
wonfen
f335941b62 UI: change paste icon, delete default profile name 2024-03-09 02:34:33 +08:00
Pylogmon
f0719f8bde
feat: Merge Providers (#508) 2024-03-08 11:37:52 +08:00
MystiPanda
27fe28661c
feat: Add animation for provider update 2024-03-02 13:52:48 +08:00
MystiPanda
bb13b12de6
chore: fix style 2024-02-28 18:25:50 +08:00
MystiPanda
1117e28b2e
feat: Try to support touch drag 2024-02-27 23:41:52 +08:00
MystiPanda
0f48873c25
chore: Remove unnecessary hotkey 2024-02-27 11:18:52 +08:00
MystiPanda
faf61b4af3
fix: Image display failed on Linux 2024-02-26 13:31:51 +08:00
Cyenoch
e29ce93d32
Feat: Provide a switch for allowing invalid certificates (#450) 2024-02-25 16:07:06 +08:00
MystiPanda
38ab68492e
chore: change style 2024-02-24 15:39:29 +08:00
MystiPanda
53b1576e5f
Release 1.5.4 2024-02-24 14:00:01 +08:00
MystiPanda
2835e79973
feat: Support Open/Close Dashboard Hotkey
#439
2024-02-24 13:39:30 +08:00
MystiPanda
8e4d7e989b
feat: Show current proxy for group node
#444
2024-02-24 13:09:53 +08:00
MystiPanda
603c6b826c
feat: allow disable group icon 2024-02-24 12:38:17 +08:00
MystiPanda
51bcc77cb3
refactor: Optimize implementation of Custom tray icon 2024-02-24 11:25:22 +08:00
MystiPanda
a30d07b924
feat: Support Custom Tray Icon 2024-02-24 00:52:21 +08:00
MystiPanda
11faa5fe39
Release 1.5.3 2024-02-22 00:23:56 +08:00
MystiPanda
ce8383b26f
feat: add reset button 2024-02-22 00:19:45 +08:00
MystiPanda
bd8f07c90a
fix: Do not set autolaunch at init
#423 #424
2024-02-21 23:50:50 +08:00
MystiPanda
fee077bebd
chore: rm -m arg 2024-02-21 23:38:01 +08:00
MystiPanda
558f4d93ba
chore: change default value of dns hijack 2024-02-21 16:40:47 +08:00
MystiPanda
e77d126349
chore: change default value of strict route 2024-02-21 11:32:51 +08:00
MystiPanda
cd8667d6de
chore: fix placeholder 2024-02-21 11:13:28 +08:00
MystiPanda
8a2d06d010
chore: Add Translation 2024-02-21 11:06:32 +08:00
MystiPanda
92741fc733
feat: Disable system stack when service mode is turned off 2024-02-21 10:52:03 +08:00
MystiPanda
1cff162649
fix: Config data display error
#417
2024-02-21 10:02:28 +08:00
MystiPanda
f59be465ea
Release 1.5.2 2024-02-21 00:04:11 +08:00
MystiPanda
27305317ce
chore: Auto Update config.yml 2024-02-20 23:54:02 +08:00
MystiPanda
8378320e50
feat: Support Tun Config (#416) 2024-02-20 23:27:03 +08:00
MystiPanda
86a69952db
chore: Update patch file 2024-02-20 18:39:36 +08:00
MystiPanda
e212ebfdb9
fix: Allow program run with administrator to start at startup 2024-02-20 18:35:39 +08:00
MystiPanda
f235125d48
chore: add tooltip for tun mode 2024-02-19 18:10:10 +08:00
MystiPanda
8efa815eb9
feat: Support custom delay timeout (#397) 2024-02-18 11:11:22 +08:00
MystiPanda
e54d42576b
build: Try restart windows service after install (#395) 2024-02-18 10:19:54 +08:00
MystiPanda
1e18539862 fix: Merge profile unexpect behavior 2024-02-17 15:58:52 +08:00
MystiPanda
673fdf4721 ci: Update Release Note 2024-02-15 20:05:00 +08:00
MystiPanda
57d6bfba51 ci: change tag 2024-02-15 19:45:37 +08:00
MystiPanda
8fa23aecda chore: update ci script 2024-02-15 19:14:35 +08:00
MystiPanda
f9c10533e8 chore: Change CI Name 2024-02-15 18:45:13 +08:00
MystiPanda
c891c3723e ci: Support alpha package 2024-02-15 18:43:20 +08:00
MystiPanda
a0625ef2e7 chore: remove 32bit package 2024-02-15 18:22:15 +08:00
MystiPanda
b2774abb54 chore: default show proxy detials 2024-02-15 18:17:21 +08:00
wonfen
fa089bbc40 doc: remove 32bit package 2024-02-12 02:25:18 +08:00
MystiPanda
8437d1763f fix: build error 2024-02-11 20:51:00 +08:00
MystiPanda
0532b0f9df Release v1.5.1 2024-02-11 20:32:25 +08:00
MystiPanda
5f8eb853f0 style: clear script 2024-02-11 20:03:14 +08:00
MystiPanda
9125a85382 fix: Custom GLOBAL group display error 2024-02-11 18:27:27 +08:00
MystiPanda
5b3dc44c72 feat: Show proxies count for provider 2024-02-10 13:13:27 +08:00
MystiPanda
ee5147f111 feat: Save window maximize state 2024-02-10 12:51:30 +08:00
MystiPanda
f67137fd5e style: change config name
#350
2024-02-08 10:23:54 +08:00
MystiPanda
b67db4a896 fix: Script profile invalid
#347
2024-02-06 08:55:44 +08:00
MystiPanda
067018828c Release 1.5.0 2024-02-06 00:41:12 +08:00
MystiPanda
e990530ada refactor: Remove clash field filter 2024-02-05 17:28:36 +08:00
wonfen
45f863a72a chore: enable clash field TLS fingerprint 2024-02-04 13:07:25 +08:00
MystiPanda
519febbeb2 fix: Try to fix traffic parse error
#337
2024-02-04 10:24:37 +08:00
wonfen
854b1a4caf chore: update changelog 2024-02-03 16:14:26 +08:00
MystiPanda
08eb95e73a fix: Copy Env Type Select Error 2024-02-02 17:57:56 +08:00
MystiPanda
66f8fbf08c chore: typo 2024-02-02 17:47:28 +08:00
MystiPanda
86a30bbb29 chore: Update Changelog 2024-02-02 17:34:15 +08:00
MystiPanda
966c453c27 fix: prevent_exit 2024-02-02 17:26:31 +08:00
MystiPanda
c7bcaf0193 Release 1.4.11 2024-02-02 16:56:39 +08:00
MystiPanda
0ac91e36a0 fix: exit_app event 2024-02-02 16:32:19 +08:00
MystiPanda
6f1299ce9e chore: fix styles 2024-02-02 16:15:23 +08:00
MystiPanda
83ca29d649 chore: Remove prevent_close 2024-02-02 15:57:03 +08:00
MystiPanda
d0206044dc fix: Fix Nisi Error 2024-02-02 15:53:28 +08:00
ycjcl868
d4623e4175
chore: unused clash core (#284) 2024-01-23 11:22:15 +08:00
MystiPanda
68ef03377d chore: fix style 2024-01-21 13:49:07 +08:00
MystiPanda
d3f9d3d033 revert: some style 2024-01-21 13:49:07 +08:00
wonfen
a6ccd04471 Sytle: UI improvement 2024-01-20 16:05:01 +08:00
Lai Zn
0fd8174e77
fix: Solve the confliction issues on auto updater failure (#273) 2024-01-20 13:03:48 +08:00
MystiPanda
ebc63f479a revert: Support both registry and api for windows sysproxy 2024-01-20 12:16:46 +08:00
MystiPanda
570c39c20a build: Update lock file 2024-01-18 22:34:14 +08:00
MystiPanda
1d57257cf5 Release v1.4.10 2024-01-18 22:26:23 +08:00
MystiPanda
82d7baee0b feat: Add exit button on setting page 2024-01-18 22:19:14 +08:00
MystiPanda
1d91e1f1f7 build: Update Depends 2024-01-18 16:26:38 +08:00
MystiPanda
15e8894614 feat: Support Custom Start Page 2024-01-18 15:37:02 +08:00
Lai Zn
0ae96918e9
feat: Use url path name as fallback subscription name (#255) 2024-01-18 14:36:37 +08:00
MystiPanda
e970880059 feat: Show SubInfo for Proxy Provider
#211
2024-01-18 14:26:57 +08:00
Lai Zn
69ae86aba8
fix: make port change set to system proxy immediately (#256) 2024-01-18 09:37:46 +08:00
MystiPanda
8142e1be49 fix: use nanoid to compatible with old devices 2024-01-18 01:16:39 +08:00
MystiPanda
cbc7612451 feat: Optimizing Provider Support 2024-01-18 01:02:30 +08:00
Kiri
09e6a7b7cc
feat: Add support for loong64 (#246) 2024-01-17 22:30:51 +08:00
Lai Zn
082e35668a
docs: Add guidelines for windows development (#250) 2024-01-17 19:02:39 +08:00
MystiPanda
c9e78c837b fix: Do not use proxy when test on tun mode 2024-01-17 18:08:09 +08:00
MystiPanda
2d171db672 Release v1.4.9 2024-01-17 15:43:15 +08:00
MystiPanda
a8b11abec8 feat: Support Startup Script 2024-01-17 15:06:16 +08:00
MystiPanda
b08333dccd feat: Support proxy group icon 2024-01-17 13:32:56 +08:00
MystiPanda
98bad48971 fix: Fix connection table sort error
#108
2024-01-17 12:54:54 +08:00
MystiPanda
53375bb536 chore: Update I18n 2024-01-17 11:30:19 +08:00
MystiPanda
88798093e1 fix: csp error 2024-01-17 11:08:14 +08:00
MystiPanda
45a28751af feat: Add Test Page 2024-01-17 11:02:17 +08:00
MystiPanda
1670c44464 fix: fix column width 2024-01-16 19:02:38 +08:00
MystiPanda
a286ac85dc Release v1.4.8 2024-01-16 15:36:04 +08:00
MystiPanda
1606adc0ab feat: Support both registry and api for windows sysproxy 2024-01-16 15:11:53 +08:00
MystiPanda
b4ce557d92 fix: Can not use specify update time when create profile 2024-01-16 10:29:04 +08:00
MystiPanda
2ba7fff8d4 refactor: rm theme blur 2024-01-15 17:13:55 +08:00
MystiPanda
98efc93610 Revert Use Tauri Http Api 2024-01-15 10:18:04 +08:00
MystiPanda
2680507eae Revert Use Tauri Websocket 2024-01-15 10:17:00 +08:00
MystiPanda
df72392ad2 fix: Fix connections sort issue and add total traffic info 2024-01-15 00:46:19 +08:00
MystiPanda
a6acb15a00 refactor: Use Tauri Http API 2024-01-14 19:35:03 +08:00
MystiPanda
ba7242a815 refactor: Use Tauri WebSocket 2024-01-14 17:30:18 +08:00
MystiPanda
99740c1324 fix patch error 2024-01-11 14:47:25 +08:00
MystiPanda
c04b20d1fa Release v1.4.7 2024-01-11 13:19:05 +08:00
MystiPanda
e127067878 feat: Disable updater for portable 2024-01-11 12:44:30 +08:00
MystiPanda
6f03b72368 feat: Support hide group
#214
2024-01-11 12:34:05 +08:00
Morris Li
7bd3ee9340
Fix expired Tauri domain (#222) 2024-01-11 10:17:56 +08:00
MystiPanda
c94db606e5 build: Use old sysproxy 2024-01-10 19:24:14 +08:00
MystiPanda
4d2474226b refactor: cargo clippy 2024-01-10 17:36:35 +08:00
MystiPanda
1ffc4f538b refactor: Optimizing the implementation of Linux URL Scheme registration 2024-01-10 16:34:35 +08:00
MystiPanda
6702ac957b fix: resolve scheme error 2024-01-10 15:37:40 +08:00
MystiPanda
ba42b2e77d fix: linux build error 2024-01-10 15:22:08 +08:00
MystiPanda
fba18ca40a feat: Support URL Scheme for MacOS 2024-01-10 14:04:06 +08:00
MystiPanda
3a2a7a1476 feat: Support URL Scheme for Linux 2024-01-10 13:03:34 +08:00
MystiPanda
3c6d6b90bc build: fix macos build script 2024-01-09 22:49:11 +08:00
MystiPanda
47dc9ee304 fix patch error 2024-01-09 22:25:25 +08:00
MystiPanda
0045bf206c Release v1.4.6 2024-01-09 22:17:29 +08:00
MystiPanda
669a1a6953 feat: Support URL Scheme for Windows
#165
2024-01-09 21:57:06 +08:00
MystiPanda
e28452cc7b feat: Optimize control button style 2024-01-09 16:04:56 +08:00
MystiPanda
5ceac03db1 feat: Add pin button 2024-01-09 15:13:59 +08:00
MystiPanda
96215e5950 fix: Fix some compile error 2024-01-09 14:52:43 +08:00
wonfen
7a030b9224 Style: UI improvement & 1.4.6 ready 2024-01-09 13:57:53 +08:00
wonfen
e743478a4d chore: UI adjustment 2024-01-08 19:32:21 +08:00
MystiPanda
bc29c80d44 chore: Use Latest and compatible core 2023-12-23 12:09:19 +08:00
MystiPanda
82b8a474d7 fix: Cargo clippy 2023-12-21 16:49:21 +08:00
MystiPanda
99851b297d fix: Get filename error
#165
2023-12-19 19:52:13 +08:00
MystiPanda
e3a500e12c fix: portable flag 2023-12-15 21:39:34 +08:00
MystiPanda
378666e3cc chore: update service url 2023-12-15 20:38:17 +08:00
MystiPanda
b65ad1ebd7 fix: user-agent version error 2023-12-15 15:18:01 +08:00
MystiPanda
933a821b5f fix: Subinfo parse error 2023-12-15 11:35:10 +08:00
MystiPanda
159337ddbd Release 1.4.5 2023-12-14 17:40:48 +08:00
MystiPanda
2e25a4333a fix: Window control button icon issue
#136
2023-12-14 14:59:55 +08:00
MystiPanda
64fafb0795 chore: Remove unsafe function 2023-12-14 13:56:51 +08:00
MystiPanda
5927e0bb3b fix: icon size 2023-12-14 13:20:01 +08:00
MystiPanda
e21596db83 feat: Update MacOS tray icon 2023-12-14 13:11:46 +08:00
MystiPanda
8a608c3c3e chore: Optimize service path 2023-12-14 13:03:52 +08:00
MystiPanda
76f9db8516 chore: Optimize upgrade process 2023-12-14 11:24:26 +08:00
MystiPanda
90f4809b7c chore: default enable clash field filter 2023-12-14 10:49:58 +08:00
MystiPanda
3ff1af9fd6 chore: Delete unnecessary drag area 2023-12-13 11:09:19 +08:00
MystiPanda
711dd520f7 chore: Change service path 2023-12-13 10:59:07 +08:00
MystiPanda
99503c836a fix: Save wrong window size 2023-12-12 16:42:19 +08:00
MystiPanda
2c8bc51862 Revert "fix: Change PID file path"
This reverts commit d64bdf02de5bb0c34c2165e3dddcb239e6c26034.
2023-12-12 15:59:00 +08:00
MystiPanda
5e7aa15232 fix: Can't switch env type 2023-12-11 10:51:59 +08:00
MystiPanda
d0761869b6 build: fix update issue 2023-12-10 22:24:46 +08:00
MystiPanda
bb99b89228 build: fix patch 2023-12-10 21:40:57 +08:00
MystiPanda
7b88beb0b5 v1.4.4 2023-12-10 21:36:22 +08:00
MystiPanda
82c630bd0e
feat: Support windows aarch64 (#112) 2023-12-10 20:45:27 +08:00
MystiPanda
bb4f11e1b8 feat: Patch for windows aarch64 2023-12-10 19:50:01 +08:00
MystiPanda
3182d20b7c build: Update Depends 2023-12-10 17:14:48 +08:00
MystiPanda
a6f15e2474 chore: Hide script mode for alpha core 2023-12-10 16:31:55 +08:00
MystiPanda
0c3f03680c Support upgrade alpha core 2023-12-10 15:57:10 +08:00
MystiPanda
a66e8dfc9f feat: Support update geodata
#37
2023-12-10 15:22:04 +08:00
zclkkk
556232aac4
chore: Allow user to choose where to install (#110) 2023-12-10 09:50:43 +08:00
MystiPanda
7fcfaadd91 chore: Remove script mode 2023-12-09 17:04:03 +08:00
MystiPanda
18d388e1e2 docs: Update ISSUE_TEMPLATE 2023-12-09 12:00:01 +08:00
MystiPanda
7de6622d74 feat: Support different tray icon for macos 2023-12-09 11:22:02 +08:00
MystiPanda
9af1f90990 feat: Support different tray icon for linux 2023-12-09 11:03:19 +08:00
Pylogmon
15ab43963d
feat: Optimize copy environment variable logic (#106) 2023-12-08 22:16:42 +08:00
MystiPanda
6b7465a4b0 perf: Improves window creation speed 2023-12-08 13:10:35 +08:00
MystiPanda
d64bdf02de fix: Change PID file path
#99
2023-12-08 11:59:40 +08:00
MystiPanda
4a2c94993a docs: Update README 2023-12-07 16:49:59 +08:00
MystiPanda
86c318d86b feat: Add AppImage for x86 linux 2023-12-07 16:38:39 +08:00
MystiPanda
bffc1ad41d ci: fix build script 2023-12-07 16:17:34 +08:00
MystiPanda
2a685c116f chore: Fix build error 2023-12-07 16:02:29 +08:00
MystiPanda
0e271d2924 ci: Fix Linux CI Script 2023-12-07 15:57:53 +08:00
MystiPanda
7127c4d590 ci: Fix Linux Build Script 2023-12-07 15:34:49 +08:00
MystiPanda
a82929996b chore: change default port to 7897 2023-12-07 14:52:14 +08:00
MystiPanda
3f01f52c7b refactor: fix depends 2023-12-07 14:44:44 +08:00
MystiPanda
9c94494622 refactor: Remove unnecessary changes 2023-12-07 13:43:53 +08:00
MystiPanda
c8a508f35c Revert "chore: change default port to 7897"
This reverts commit 4ec73ef1c3e546d36706a034f65781cf0694deed.
2023-12-07 13:32:49 +08:00
wonfen
4ec73ef1c3 chore: change default port to 7897 2023-12-06 03:37:16 +08:00
MystiPanda
3465b79e5b docs: Update README 2023-12-05 17:02:54 +08:00
MystiPanda
726a0a33ae docs: UPDATELOG 2023-12-05 16:47:42 +08:00
MystiPanda
ba05810d80 Release 1.4.3 2023-12-05 16:28:50 +08:00
MystiPanda
10bb53e7de chore: fix appid 2023-12-05 15:41:13 +08:00
MystiPanda
dd8a083546 refactor: Change app_home to standard locations of app_data 2023-12-05 15:39:44 +08:00
MystiPanda
84131a71c9 fix: Stop core before install update 2023-12-05 15:01:15 +08:00
MystiPanda
4909a11896 docs: Update README 2023-12-05 13:33:23 +08:00
MystiPanda
4069357b81 refactor: Remove page animation 2023-12-05 13:30:55 +08:00
MystiPanda
e6e87bcc40 refactor: Change Tun Icon Color 2023-12-05 13:23:54 +08:00
MystiPanda
7ba22045e8 build: Dependency downgrade 2023-12-05 12:55:20 +08:00
Pylogmon
d4040b61c4
refactor: Change Portable Config Path (#66) 2023-12-05 12:52:26 +08:00
Pylogmon
7534f8fc37
refactor: Hide Clash Field Option when Disable Filter (#63) 2023-12-05 08:34:57 +08:00
MystiPanda
7ced009e92
Update README.md 2023-12-04 14:51:46 +08:00
MystiPanda
cc13c71e40 ci: fix updater 2023-12-04 13:51:46 +08:00
MystiPanda
d2f09209a7 chore: Use Nsis 2023-12-04 13:45:28 +08:00
Pylogmon
a241b04d99
fix: Open File (#56) 2023-12-04 12:15:12 +08:00
Pylogmon
4f35d907cb
fix: Get Profile Filename (#54) 2023-12-04 12:15:01 +08:00
MystiPanda
9e50d144a3
chore: change linux meta core to compatible 2023-12-04 11:38:54 +08:00
wonfen
8514bb48c4 chore: change windows meta core to compatible 2023-12-04 06:00:31 +08:00
WhizPanda
3ae633a2a1 fix: alpha core can't display memory 2023-12-03 18:55:38 +08:00
WhizPanda
2799cb9118 style: fix icon size 2023-12-03 18:28:41 +08:00
WhizPanda
d20346b6ac docs: Update README.md 2023-12-03 16:42:49 +08:00
Pylogmon
2f00666a68
style: improve drag icon style (#51) 2023-12-03 16:27:13 +08:00
WhizPanda
80f4570093 ci: Fix Portable Script 2023-12-03 15:36:39 +08:00
wonfen
3f0a2ba48f docs: update readme 2023-12-03 15:25:33 +08:00
WhizPanda
fc90094e8a
feat: Support Both Stable and Alpha Version (#47) 2023-12-03 14:26:03 +08:00
wonfen
20d580ade8 release: 1.4.2, tweak UI, fix emoji on mac 2023-12-03 14:01:53 +08:00
WhizPanda
290a024a9e chore: Adjust style 2023-12-02 23:20:04 +08:00
WhizPanda
64a283c3a6 chore: Replace Repo Name 2023-12-02 23:04:04 +08:00
WhizPanda
3e93f0ecbc build: Update Depends 2023-12-02 22:36:04 +08:00
WhizPanda
b12bcc1c49 style: Improve Style 2023-12-02 16:23:53 +08:00
WhizPanda
05d7313dcc feat: Support New Clash Field
#46
2023-12-02 15:18:54 +08:00
WhizPanda
c0b6e549f0 build: Update Cargo Depends 2023-12-01 15:52:49 +08:00
WhizPanda
31f9bf219e build: Update Depends 2023-12-01 14:49:32 +08:00
WhizPanda
4906ca7059 feat: support random mixed port 2023-12-01 12:56:18 +08:00
WhizPanda
b963a7a0e5
fix: Fix Updater Script 2023-12-01 11:19:49 +08:00
WhizPanda
5d6dadda76
Remove unnecessary conditions 2023-12-01 11:11:20 +08:00
Pylogmon
77f69fd223
feat: Add Windows x86 and Linux armv7 Support (#44)
* feat: Add Windows x86 and Linux armv7 Support

* ci: Remove Linux armv7 Support
2023-12-01 11:03:18 +08:00
Pylogmon
7f5052bc87
feat: Use Latest Meta Core mihomo (#41) 2023-12-01 02:44:18 +08:00
Pylogmon
3009c1f4f6
feat: Support cross-compiling to aarch64 (#40)
#19
2023-11-30 22:46:09 +08:00
Pylogmon
5425872bba
feat: Support Disable Tray Click Event (#38)
#21
2023-11-30 22:45:02 +08:00
Pylogmon
0759e17295
feat: Set different tray icon on tun mode (#33)
好看!
2023-11-30 01:29:11 +08:00
Pylogmon
56a59d25df
feat: Add Download Progress for Updater (#34) 2023-11-30 01:28:46 +08:00
Pylogmon
887f92babe
feat: Support Drag to Reorder the Profile (#29)
* feat: Support Drag to Reorder the Profile

* style: Remove unnecessary styles
2023-11-29 08:54:02 +08:00
wonfen
197f942b3f
Merge pull request #27 from Pylogmon/emoji-font
feat: Embed Emoji fonts
2023-11-28 10:41:35 +08:00
Pylogmon
a5be7a35e9 feat: Embed Emoji fonts 2023-11-28 10:36:32 +08:00
wonfen
e347675265 fix: Adjust font order 2023-11-28 10:03:01 +08:00
wonfen
02a084d571 chore: update preview gif 2023-11-28 08:49:22 +08:00
wonfen
7fbcc23d94 Merge branch 'main' of github.com:wonfen/clash-verge-rev 2023-11-28 07:53:11 +08:00
wonfen
bda87167a3 update clashmeta core, Imporve UI, merge PR, reset icons, fix CI 2023-11-28 07:49:44 +08:00
wonfen
34f53c287e
Merge pull request #24 from Pylogmon/tray
fix: Tray Icon Tooltip is Empty
2023-11-28 01:46:41 +08:00
wonfen
9ab07f37d7
Merge pull request #21 from Pylogmon/tray-event
feat: Config Tray Click Event
2023-11-28 01:46:16 +08:00
Pylogmon
36399b39fb chore: fix style 2023-11-27 20:10:31 +08:00
Pylogmon
1e015287a1 chore: Add Translation 2023-11-27 20:04:47 +08:00
Pylogmon
a590aa8485 fix: Tray Icon Tooltip is Empty 2023-11-27 19:55:42 +08:00
Pylogmon
c0582fde66 chore: Remove Debug Info 2023-11-26 18:49:48 +08:00
Pylogmon
d8d75b4afa feat: Config Tray Click Event 2023-11-26 18:35:21 +08:00
wonfen
9d4942723c fix: portable CI again 2023-11-23 15:01:30 +08:00
wonfen
e5b1ece5c0 chore: change preview gif and fix portable CI 2023-11-23 14:34:11 +08:00
wonfen
1b25bfbf5a fix: CI portable 2023-11-23 13:42:16 +08:00
wonfen
7c64d9f42a
Merge pull request #3 from Kuingsmile/main
fix: Downgrade runas to fix service install bug
2023-11-23 11:11:06 +08:00
wonfen
61f9f4ef58 chore: update clash.meta core to 2023.11.23, change win icons. 2023-11-23 11:10:32 +08:00
Kuingsmile
b8eac56213 fix: Downgrade runas to fix service install bug 2023-11-22 17:17:12 -08:00
wonfen
ab5fb5749b
Merge pull request #2 from Kuingsmile/main
feat: Update DialogContent width in EditorViewer
2023-11-23 07:46:12 +08:00
Kuingsmile
e0fb9a1b25 feat: Update DialogContent width in EditorViewer
component
2023-11-22 06:49:47 -08:00
wonfen
97e717f646
Merge pull request #1 from Kuingsmile/main
feat: add UWP loopback tools
2023-11-22 16:23:38 +08:00
Kuingsmile
caa82ad1e6 feat: add UWP loopback tools 2023-11-22 00:15:41 -08:00
wonfen
7ec251ea6d chore: UI adjustment, add translation, fix CI 2023-11-22 14:52:14 +08:00
wonfen
675b0c3cca chore: update readme & fix updater issue 2023-11-22 07:31:38 +08:00
wonfen
05e54d4b7f chore: remove old updater CI 2023-11-22 07:06:08 +08:00
wonfen
b4527c90e5 Merge remote-tracking branch 'nyanpasu/main' 2023-11-22 06:48:30 +08:00
wonfen
dbc626734d chore: delete clash core, update CI, change profile name, change URL test link 2023-11-22 02:56:47 +08:00
keiko233
3f2ce3cb80 chore: fix typos 2023-11-16 11:31:06 +08:00
keiko233
0820d6d6fb chore: add updater workflow 2023-11-16 10:59:27 +08:00
keiko233
a8c74e39c2 chore: remove un supported platform
* I don't have the relevant equipment to test with
* May support in future
2023-11-16 10:56:03 +08:00
keiko233
d58c29907d chore: no need for second build 2023-11-16 10:41:28 +08:00
keiko233
2322733427 chore: fix missing assets type 2023-11-16 10:37:26 +08:00
keiko233
d76e8fe85a chore: remove un supported platform 2023-11-16 10:30:25 +08:00
keiko233
0c2e4fe34a chore: fix typos 2023-11-16 10:30:01 +08:00
keiko233
30b6c87a49 docs: add preview gif 2023-11-15 15:17:51 +08:00
keiko233
540221467a chore: clean up workflows 2023-11-15 14:37:10 +08:00
keiko233
d44d331a78 Bump Version 1.4.0 2023-11-15 14:06:29 +08:00
keiko233
595554f18a chore: add release build 2023-11-15 13:52:56 +08:00
keiko233
84d3c3f7eb fix: rust lint 2023-11-15 13:49:06 +08:00
keiko233
890f55c9dc chore: test: use pnpm 2023-11-13 14:49:28 +08:00
keiko233
575a14e1f3 feat: minor tweaks 2023-11-12 00:10:23 +08:00
keiko233
91074bebd6 feat: Nyanpasu Misc 2023-11-12 00:09:32 +08:00
keiko233
3459a16b48 chore: switch updater endpoints 2023-11-12 00:08:23 +08:00
keiko233
88c9b6849f fix: valid with unified-delay & tcp-concurrent 2023-11-11 22:34:30 +08:00
keiko233
d87bb25baf chore: delete current release assets 2023-11-11 22:27:28 +08:00
keiko233
34cb796505 feat: add baseContentIn animation 2023-11-11 19:34:03 +08:00
keiko233
1eb34e0662 feat: add route transition 2023-11-11 18:32:11 +08:00
keiko233
01d631033f feat: Material You! 2023-11-11 17:12:57 +08:00
keiko233
84b2c07340 fix: touchpad scrolling causes blank area to appear 2023-11-11 15:03:41 +08:00
keiko233
a86eeb636d chore: drop upload-artifact & use prerelease 2023-11-11 15:00:54 +08:00
keiko233
417a5a8214 chore: fix: typos
* ↑ baka
2023-11-10 11:50:01 +08:00
keiko233
bcf40dde8c fix: typos 2023-11-10 11:35:38 +08:00
keiko233
cad87484c7 fix: download clash core from backup repo 2023-11-10 11:26:05 +08:00
keiko233
b8ad641ed6 chore: add dev workflow 2023-11-10 10:38:41 +08:00
keiko233
ae8197be8b feat: default disable ipv6 2023-11-10 09:10:15 +08:00
keiko233
eafb0274a7 feat: default enable unified-delay & tcp-concurrent with use meta core 2023-11-10 09:08:17 +08:00
keiko233
71a23a4e02 refactor: copy_clash_env 2023-11-09 10:52:52 +08:00
keiko233
26fd90dfa3 feat: support copy CMD & PowerShell proxy env 2023-11-09 09:58:17 +08:00
keiko233
e393ebede2 fix: use meta Country.mmdb 2023-11-09 09:35:41 +08:00
keiko233
2981bb3f19 feat: default use meta core
* :)
2023-11-09 09:34:03 +08:00
keiko233
6e9f05abb1 feat: update Clash Default bypass addrs
*Enabling TUN will cause the local address to go through the proxy
2023-11-09 09:33:38 +08:00
GyDi
9df1115380 chore: fix check 2023-11-03 16:00:34 +08:00
GyDi
f22e360cbb chore: fix check script 2023-11-03 15:52:36 +08:00
MZhao
67769af6f4
feat: new windows tray icons (#899) 2023-11-03 15:01:46 +08:00
Aromia
b1a9a1d6d9
chore: update download links in README.md (#896) 2023-11-03 11:21:20 +08:00
Goooler
cf0606ecb7
chore: bump github actions (#868)
https://github.com/actions/checkout/releases/tag/v4.0.0
https://github.com/actions/setup-node/releases/tag/v4.0.0
2023-11-02 20:14:05 +08:00
neovali
7287edcd6f
chore: add fedora linux install description (#874) 2023-11-02 20:13:24 +08:00
GyDi
e0d26203dd feat: support auto clean log files 2023-11-02 20:12:46 +08:00
GyDi
7e3a85e9da
fix: latency url empty 2023-11-01 23:22:30 +08:00
GyDi
5a0fed9c93 feat: increase the concurrency of latency test 2023-11-01 20:52:38 +08:00
GyDi
1f1e743912
fix: change default port 2023-10-31 14:56:19 +08:00
GyDi
b4301ed0d5
fix: csp 2023-10-31 12:00:57 +08:00
GyDi
b5391560fc
v1.3.8 2023-10-31 01:29:20 +08:00
GyDi
718989cbcf
chore: update log 2023-10-31 01:28:57 +08:00
GyDi
d0aee76962
fix: add default valid key 2023-10-30 17:10:51 +08:00
GyDi
fb08af96bd
fix: page null exception, close #821 2023-10-30 00:53:24 +08:00
GyDi
510a0c5e70
feat: adjust the delay display interval and color, close #836 2023-10-29 23:01:05 +08:00
keiko233
89bdc8ec75 chore: update dependencies 2023-10-22 00:44:01 +08:00
keiko233
ae25ade318 chore: tsconfig: skip Lib Check 2023-10-21 23:53:57 +08:00
keiko233
dd5e46a8a7 feat: theme: change color 2023-10-21 17:30:50 +08:00
keiko233
f7218aaa9e chore: update README.md 2023-10-21 17:02:13 +08:00
keiko233
ee79bcfc44 feat: profiles: import btn with loading state 2023-10-21 16:50:46 +08:00
keiko233
2c2c174874 feat: profile-viewer: handleOk with loading state 2023-10-21 16:47:39 +08:00
keiko233
f57c49ce3a feat: base-dialog: okBtn use LoadingButton 2023-10-21 16:47:10 +08:00
keiko233
06121acfac chore: import MUI Lab 2023-10-21 16:43:30 +08:00
keiko233
2078ce7446 chore: Update dependencies 2023-10-21 16:43:16 +08:00
keiko233
49f41abfdb feat: Nyanpasu Misc 2023-10-20 11:52:40 +08:00
keiko233
70fcfe6d6c feat: Theme support modify --background-color 2023-10-20 11:12:49 +08:00
keiko233
b060b4b9bf feat: settings use Grid layout 2023-10-20 10:05:03 +08:00
keiko233
ee2135bfb3 chore: Use your own update endpoints and public keys 2023-10-19 09:37:31 +08:00
keiko233
7daa322441 chore: readme remove ad 2023-10-18 13:38:58 +08:00
GyDi
74252cb66b chore: fix rust lint 2023-10-18 13:38:03 +08:00
GyDi
5fe2be031f
chore: fix rust lint 2023-10-11 14:55:20 +08:00
GyDi
2ba3aaba47
chore: fix alpha ci 2023-10-11 14:31:44 +08:00
GyDi
ad8903991c
fix: try fix csp 2023-10-11 14:21:56 +08:00
GyDi
3e5624c570
chore: update readme 2023-10-11 00:04:56 +08:00
keiko233
88d3bba300 feat: add Connections Info to ConnectionsPage
*Add Upload Traffic, Download Traffic and Active Connections to ConnectionsPage.
*IConnections uploadTotal and downloadTotal data missing not displayed, add it to ConnectionsPage interface here.
2023-10-10 17:05:31 +08:00
Majokeiko
f5ee6f3537
feat: ClashFieldViewer BaseDialog maxHeight usage percentage (#813)
*The overall interface will be more intuitive when the content is longer.
2023-10-10 14:29:27 +08:00
GyDi
afc77e7adc
chore: update readme 2023-10-10 10:25:57 +08:00
GyDi
024f42fce6
chore: update readme 2023-10-10 10:25:01 +08:00
GyDi
8a5f12b97c
chore: update clash meta 2023-10-08 21:45:12 +08:00
GyDi
954b21cf39
chore: update readme 2023-09-11 10:24:44 +08:00
GyDi
74d095774d
chore: fix updater 2023-09-11 10:16:54 +08:00
GyDi
17a2722e6d
chore: fix updater 2023-09-11 10:12:40 +08:00
GyDi
c843bddbfe
chore: fix clash map, close #736 2023-09-10 19:06:02 +08:00
GyDi
3f22a49755
v1.3.7 2023-09-10 19:02:10 +08:00
GyDi
7af2ffcebf
chore: update log 2023-09-10 19:01:59 +08:00
GyDi
de90c959e0
chore: update auto launch 2023-09-10 18:45:17 +08:00
GyDi
9987dc1eb4
fix: i18n 2023-09-10 15:03:29 +08:00
GyDi
3efd575dd2
feat: add Open Dashboard to the hotkey, close #723 2023-09-10 14:46:03 +08:00
GyDi
f4c7b17a87
feat: add check for updates button, close #766 2023-09-10 14:30:31 +08:00
GyDi
16d80718cb
fix: fix page undefined exception, close #770 2023-09-10 13:45:18 +08:00
GyDi
ad228d53b7
feat: add paste and clear icon 2023-09-09 16:52:00 +08:00
Majokeiko
15ee1e531b
feat: Subscription URL TextField use multiline (#761)
*Subscription link that are too long can make reading difficult, so use multiline TextField.
2023-09-07 16:14:42 +08:00
GyDi
1c8fb3392a
chore: update mmdb 2023-08-28 15:06:13 +08:00
GyDi
8647866a32
fix: set min window size, close #734 2023-08-28 15:00:27 +08:00
GyDi
23351c4f1c
chore: change ubuntu ci version 2023-08-28 14:44:09 +08:00
GyDi
1367c304cf
chore: update clash 2023-08-28 14:22:50 +08:00
GyDi
26d6bcb074
chore: update release link 2023-08-14 11:11:15 +08:00
GyDi
b0d651ece1
chore: update clash meta 2023-08-14 11:09:19 +08:00
GyDi
b6d50ba6a4
fix: rm debug code 2023-08-12 19:16:20 +08:00
GyDi
b3ab6a9166
v1.3.6 2023-08-12 16:12:03 +08:00
GyDi
f39a5ac9c2
chore: update log 2023-08-12 16:11:50 +08:00
GyDi
38a9a9240d
fix: use sudo when pkexec not found 2023-08-12 15:58:37 +08:00
GyDi
241b22a465
chore: update ci 2023-08-12 15:41:26 +08:00
GyDi
741abc0366
feat: show loading when change profile 2023-08-05 22:07:30 +08:00
GyDi
7854775de5
fix: remove div 2023-08-05 21:43:58 +08:00
GyDi
e62eaa6b4b
fix: list key 2023-08-05 21:43:05 +08:00
GyDi
b4cce23ef4
feat: support proxy provider update 2023-08-05 21:38:44 +08:00
GyDi
2bcaf90fc8
feat: add repo link 2023-08-05 19:54:59 +08:00
GyDi
96ffbe2f84
feat: support clash meta memory usage display 2023-08-05 19:40:23 +08:00
GyDi
6f5acee1c3
fix: websocket disconnect when window focus 2023-08-05 17:21:15 +08:00
GyDi
54e491d8bf
feat: supports show connection detail 2023-08-05 16:52:14 +08:00
GyDi
ab6374e278
chore: update geo data to meta, close #707 2023-08-05 13:42:37 +08:00
GyDi
2fda4c9f67
chore: fix faq 2023-08-05 13:41:30 +08:00
GyDi
5138a45b0f
chore: add faq and download link 2023-08-05 13:38:19 +08:00
GyDi
b224d4fa8a
chore: add promotion 2023-08-05 12:57:48 +08:00
whitemirror33
a552e44483
feat: update connection table with wider process column and click to show full detail (#696) 2023-08-04 14:36:28 +08:00
GyDi
0cf3bba118
feat: more trace logs 2023-08-04 14:15:15 +08:00
Andrei Shevchuk
2c48ea3508
feat: Add Russian Language (#697)
* Add Russian Language

* Add Russian support

* Minor update

* Update Russian translation
2023-08-03 11:07:58 +08:00
GyDi
b9b6212b75
fix: try fix undefined error 2023-07-28 09:26:06 +08:00
GyDi
b978aaec21
chore: alpha ci 2023-07-26 17:07:20 +08:00
GyDi
af704681d9
feat: center window when out of monitor 2023-07-24 20:55:26 +08:00
GyDi
1443ddfe6c
chore: fix test ci 2023-07-24 09:59:13 +08:00
GyDi
54457a3e1b
chore: fix test ci 2023-07-24 09:47:05 +08:00
GyDi
bf180e6a2c
chore: test ci 2023-07-24 09:28:54 +08:00
GyDi
864a5820c9
chore: test ci 2023-07-24 09:13:19 +08:00
GyDi
4d3ca49c3f
v1.3.5 2023-07-23 13:31:51 +08:00
GyDi
c49c3cf7f0
chore: update log 2023-07-23 13:31:34 +08:00
GyDi
5d5ab57469
fix: blurry tray icon in Windows 2023-07-23 13:25:54 +08:00
GyDi
31978d8de0
chore: update clash core 2023-07-23 13:11:35 +08:00
GyDi
e8eb68bf24
chore: fix check script 2023-07-23 13:11:17 +08:00
GyDi
9ea08f4fed
chore: update issue templates 2023-07-22 23:04:13 +08:00
GyDi
fe078a5c5b
v1.3.4 2023-07-22 20:38:08 +08:00
GyDi
61933954f3
chore: update log 2023-07-22 20:37:22 +08:00
GyDi
4c243638cb
fix: enable context menu in editable element 2023-07-22 17:21:04 +08:00
GyDi
02ba04b5d8
feat: support copy environment variable 2023-07-22 15:35:32 +08:00
GyDi
4f158a4829
fix: save window size and pos in Windows 2023-07-22 13:13:16 +08:00
GyDi
177a22df59
feat: save window size and position 2023-07-22 10:58:16 +08:00
GyDi
6b0ca2966e
feat: app log level add silent 2023-07-22 09:25:54 +08:00
GyDi
aadfaf7150
feat: overwrite resource file according to file modified 2023-07-22 09:18:54 +08:00
GyDi
b307b9a66b
feat: support app log level settings 2023-07-22 08:53:37 +08:00
Kimiblock Moe
6c1ab6002d
feat: Use polkit to elevate permission instaed of sudo (#678) 2023-07-21 23:05:33 +08:00
GyDi
9638eefc91
chore: update check script 2023-07-11 13:25:55 +08:00
GyDi
9e9c4ad587
fix: optimize traffic graph high CPU usage when hidden 2023-07-10 23:44:09 +08:00
GyDi
ce231431b9
fix: remove fallback group select status, close #659 2023-07-10 23:16:19 +08:00
GyDi
06e1e14e02
chore: update clash 2023-07-10 13:36:17 +08:00
GyDi
416e7884f5
feat: add unified-delay field 2023-06-30 13:58:51 +08:00
GyDi
d579222007
v1.3.3 2023-06-30 09:38:33 +08:00
GyDi
30243c84cd
chore: update log 2023-06-30 09:35:49 +08:00
GyDi
3557a77645
fix: error boundary with key 2023-06-30 09:17:59 +08:00
GyDi
97be28638b
chore: update clash meta 2023-06-30 09:02:26 +08:00
GyDi
aba0826c38
fix: connections is null 2023-06-30 09:02:17 +08:00
GyDi
f032228d0e
fix: font family not works in some interfaces, close #639 2023-06-29 14:21:14 +08:00
GyDi
6cf174c5ed
fix: encodeURIComponent secret 2023-06-29 14:15:57 +08:00
GyDi
c2109d245f
chore: update clash & clash meta 2023-06-08 13:52:40 +08:00
GyDi
6a9745171e
feat: add error boundary to the app root 2023-06-08 13:50:45 +08:00
GyDi
f9a68e8b23
fix: encode controller secret, close #601 2023-06-08 13:48:58 +08:00
GyDi
6e391df5ee
fix: linux not change icon 2023-05-28 18:14:11 +08:00
GyDi
f5edca94d3
fix: try fix blank error 2023-05-28 17:35:00 +08:00
GyDi
60046abec3
fix: close all connections when change mode 2023-05-28 17:07:39 +08:00
GyDi
cafc2060b8 fix: macos not change icon 2023-05-28 16:46:17 +08:00
GyDi
b1f45752cf chore: update deps 2023-05-28 16:45:43 +08:00
GyDi
ed17551170 chore: update clash 2023-05-28 16:44:07 +08:00
w568w
ef5adab638
feat: show tray icon variants in different status (#537) 2023-05-28 10:55:39 +08:00
GyDi
fb653ff99d
fix: error message null 2023-05-19 10:53:11 +08:00
GyDi
78fc47a9c4
chore: update gh proxy 2023-05-19 09:44:31 +08:00
GyDi
2a124cea61
v1.3.2 2023-05-19 00:57:22 +08:00
GyDi
4c7cc563dc
chore: update log 2023-05-19 00:57:10 +08:00
GyDi
6114af4f93
fix: profile data undefined error, close #566 2023-05-18 20:02:46 +08:00
GyDi
8c31629655
chore: update clash core 2023-05-05 20:45:52 +08:00
GyDi
03c8a8edb2
chore: update deps 2023-05-05 20:27:46 +08:00
yettera765
3eeaee154f
fix: import url error (#543)
use rustls instead of depending user's system tls
2023-05-05 12:08:08 +08:00
GyDi
8cf8fa7c80
v1.3.1 2023-04-11 10:04:55 +08:00
GyDi
6b4f6fc71e
chore: update log 2023-04-11 10:04:28 +08:00
Tatius Titus
30c2680b6f
chore: Upgrade to React 18 (#495)
* chore: Upgrade to React 18

* runfix: Add children type to FC components

* chore: Remove @types/react
2023-04-07 12:59:44 +08:00
Mr-Spade
fb7b1800cc
fix: linux DEFAULT_BYPASS (#503) 2023-04-07 12:47:13 +08:00
GyDi
ff573bf377
chore: update deps 2023-04-02 11:20:18 +08:00
GyDi
0a33bb861e
fix: open file with vscode 2023-04-02 11:19:48 +08:00
GyDi
728756289b
chore: update deps 2023-04-02 10:09:14 +08:00
GyDi
56ccd3a0ac
chore: update clash meta 2023-04-02 09:43:32 +08:00
Tatius Titus
66f3f0ba07
fix: Do not render div as a descendant of p (#494) 2023-04-02 09:37:09 +08:00
Srinivas Gowda
af5e0d589e
chore: update clash core version (#476) 2023-03-27 10:59:27 +08:00
GyDi
533dc99e7d
fix: use replace instead 2023-03-17 08:37:45 +08:00
GyDi
fc5ca965ba
fix: escape path space 2023-03-17 08:36:19 +08:00
John Smith
9c4a46bcdb
fix: escape the space in path (#451) 2023-03-17 08:20:35 +08:00
GyDi
52658886e7
fix: add target os linux 2023-03-16 23:57:34 +08:00
GyDi
8174ab7616
chore: fix ci 2023-03-16 23:54:14 +08:00
GyDi
2b6acedae1
fix: appimage path unwrap panic 2023-03-16 23:45:48 +08:00
GyDi
d00fe9c5f4
v1.3.0 2023-03-16 21:33:51 +08:00
GyDi
88aa270728
chore: update log 2023-03-16 21:33:14 +08:00
GyDi
4ae409c7f4
feat: auto restart core after grand permission 2023-03-16 21:32:39 +08:00
GyDi
9a29c9abdd
feat: add restart core button 2023-03-16 20:56:37 +08:00
GyDi
66d93ea037
fix: remove esc key listener in macOS 2023-03-16 20:42:45 +08:00
GyDi
6c0066dbfb
feat: support update all profiles 2023-03-16 20:32:41 +08:00
GyDi
4fde644733
chore: rm file 2023-03-16 20:31:48 +08:00
GyDi
e7841c60df
fix: adjust style 2023-03-16 19:32:59 +08:00
hybo
94f647b24a
chore: evaluate error context lazily (#447) 2023-03-16 17:06:52 +08:00
GyDi
630249d22a
fix: adjust swr option 2023-03-16 17:03:12 +08:00
GyDi
db99b4cb54
fix: infinite retry when websocket error 2023-03-16 17:03:00 +08:00
GyDi
c77db23586
fix: type error 2023-03-16 13:51:46 +08:00
GyDi
daf66bcec4
chore: update deps 2023-03-16 13:42:27 +08:00
GyDi
8caf36349f
chore: fix ci 2023-03-16 13:18:05 +08:00
GyDi
6934de58e5
chore: fix ci 2023-03-16 13:16:30 +08:00
GyDi
54a5007c01
feat: support to grant permission to clash core 2023-03-16 11:16:54 +08:00
GyDi
e25a455698
fix: do not parse log except the clash core 2023-03-16 10:34:28 +08:00
GyDi
ab429dfeb6
feat: support clash fields filter in ui 2023-03-15 08:43:24 +08:00
GyDi
c5289dc0e8
fix: field sort for filter 2023-03-15 08:43:03 +08:00
boatrainlsz
d191877002
chore: update README (#435) 2023-03-07 22:55:38 +08:00
GyDi
4d979160c2
chore: update deps 2023-03-07 00:10:49 +08:00
GyDi
d00e8f6e19
chore: update clash 2023-03-06 23:35:30 +08:00
GyDi
91b77e5237
fix: add meta fields 2023-02-18 00:46:03 +08:00
GyDi
5b8c246d53
chore: update clash 2023-02-18 00:09:40 +08:00
GyDi
b374b9b91c
feat: open dir on the tray 2023-02-17 00:15:21 +08:00
GyDi
403717117e
fix: runtime config user select 2023-02-17 00:00:23 +08:00
GyDi
027295d995
feat: support to disable clash fields filter 2023-02-16 23:52:55 +08:00
GyDi
c9b7eccbc1
fix: app_handle as_ref 2023-02-15 17:25:35 +08:00
GyDi
2b6d9348cd
fix: use crate 2023-02-11 23:31:55 +08:00
GyDi
692f8c8454
fix: appimage auto launch, close #403 2023-02-11 23:19:08 +08:00
inRm3D
6783355c4d
chore: add openssl depend (#395) 2023-02-02 10:54:15 +08:00
GyDi
fb9cca1e99
v1.2.3 2023-02-01 22:11:34 +08:00
GyDi
eb770ede1a
chore: update log 2023-02-01 22:11:06 +08:00
GyDi
2643e853af
chore: aarch script 2023-02-01 21:41:15 +08:00
GyDi
b79456e91b
chore: update clash 2023-01-30 20:50:08 +08:00
GyDi
ac66c086f8
chore: meta ci 2023-01-30 20:40:57 +08:00
GyDi
ebccf401dd
chore: fix ci 2023-01-22 13:31:34 +08:00
GyDi
66494845b7
chore: fix ci 2023-01-19 18:02:08 +08:00
GyDi
e6c36ad602
chore: alpha ci 2023-01-18 23:39:27 +08:00
inRm3D
26379182db
chore: try fix missing libssl3 (#378)
* Update ci.yml

* use package from source
2023-01-18 23:36:18 +08:00
GyDi
bba03d14d4
fix: compatible with UTF8 BOM, close #283 2023-01-17 19:51:02 +08:00
GyDi
23b728a762
feat: adjust macOS window style 2023-01-16 22:57:53 +08:00
GyDi
819c5207d2
fix: use selected proxy after profile changed 2023-01-15 21:33:03 +08:00
GyDi
311358544e
fix: error log 2023-01-15 19:11:51 +08:00
GyDi
4480ecc96d
chore: ci 2023-01-14 22:22:18 +08:00
GyDi
6c5f70a205
v1.2.2 2023-01-14 22:01:28 +08:00
GyDi
99adfb4a9e
chore: update log 2023-01-14 21:56:27 +08:00
GyDi
7909cf4067
fix: adjust fields order 2023-01-14 14:57:55 +08:00
GyDi
780ab20aeb
fix: add meta fields 2023-01-14 14:51:29 +08:00
GyDi
73119bb7c5
chore: ci 2023-01-14 12:20:17 +08:00
GyDi
f20f0f064e
chore: update clash meta 2023-01-14 12:16:09 +08:00
GyDi
1b44ae098c
fix: add os platform value 2023-01-14 12:07:31 +08:00
GyDi
453c230716
feat: recover core after panic, close #353 2023-01-14 11:45:47 +08:00
GyDi
439d885ee1
chore: allow unused 2023-01-13 23:11:48 +08:00
GyDi
43dee3ef76
fix: reconnect traffic websocket 2023-01-13 23:02:45 +08:00
GyDi
c71ba6ff8d
chore: update deps 2023-01-13 21:29:21 +08:00
GyDi
fb7a36eb73
feat: use decorations in Linux, close #354 2023-01-12 00:42:33 +08:00
GyDi
e7f294a065
fix: parse bytes precision, close #334 2023-01-11 14:05:49 +08:00
GyDi
d5037f180e
fix: trigger new profile dialog, close #356 2023-01-11 13:45:16 +08:00
GyDi
e90158809a
fix: parse log cause panic 2023-01-11 13:30:14 +08:00
GyDi
0cb802ed9a
chore: update clash meta 2023-01-09 21:55:03 +08:00
GyDi
d0b47204f4
v1.2.1 2022-12-23 22:44:27 +08:00
GyDi
351cb391e5
chore: update log 2022-12-23 22:44:00 +08:00
GyDi
051be927cd
fix: avoid setting login item repeatedly, close #326 2022-12-23 22:39:27 +08:00
GyDi
8bad2c2113
fix: adjust code 2022-12-15 21:54:48 +08:00
GyDi
2bcf6fb3eb
fix: adjust delay check concurrency 2022-12-15 12:23:57 +08:00
GyDi
d1ba0ed2b2
chore: adjust code 2022-12-15 12:22:20 +08:00
GyDi
6e421e60c5
fix: change default column to auto 2022-12-14 16:56:33 +08:00
GyDi
8385050804
fix: change default app version 2022-12-14 16:15:53 +08:00
GyDi
bfe4f08232
fix: adjust rule ui 2022-12-14 15:59:41 +08:00
GyDi
132f914b0d
fix: adjust log ui 2022-12-14 15:49:05 +08:00
GyDi
97d82b03ab
fix: keep delay data 2022-12-14 15:29:02 +08:00
GyDi
f06fa3f9b7
fix: use list item button 2022-12-14 15:16:49 +08:00
GyDi
6337788a22
fix: proxy item style 2022-12-14 15:15:44 +08:00
GyDi
024db4358b
feat: auto proxy layout column 2022-12-14 15:07:51 +08:00
GyDi
173f35487e
chore: fix ci 2022-12-13 18:13:28 +08:00
GyDi
74cbe82dd1
chore: fix ci 2022-12-13 18:11:43 +08:00
GyDi
a8c30d30a9
chore: fix ci 2022-12-13 18:02:23 +08:00
GyDi
1b7a52d5af
chore: alpha ci 2022-12-13 17:44:46 +08:00
GyDi
e5109789bf
chore: alpha ci 2022-12-13 17:41:53 +08:00
GyDi
4d2b35e09d
feat: support to change proxy layout column 2022-12-13 17:34:39 +08:00
GyDi
5c5177ec57
feat: support to open core dir 2022-12-13 00:44:24 +08:00
GyDi
d93b00cd15
chore: update deps 2022-12-12 00:00:45 +08:00
MoeShin
39ade59174
fix: Virtuoso no work in legacy browsers (#318) 2022-12-08 10:47:42 +08:00
GyDi
0309c815b9
chore: update deps 2022-12-04 20:54:43 +08:00
GyDi
ce613098db
fix: adjust ui 2022-12-04 00:45:39 +08:00
GyDi
ab34044196
feat: profile page ui 2022-11-28 22:29:58 +08:00
GyDi
17f724748f
chore: deps 2022-11-28 22:27:23 +08:00
GyDi
f3a917b5e7
chore: update clash version 2022-11-26 13:25:52 +08:00
GyDi
376011ea08
fix: refresh websocket 2022-11-25 22:22:57 +08:00
GyDi
2ce7624c14
fix: adjust ui 2022-11-24 18:24:34 +08:00
GyDi
c62dddd5b9
feat: save some fields in the runtime config, close #292 2022-11-24 17:52:25 +08:00
GyDi
490ba9f140
chore: alpha ci 2022-11-24 11:14:03 +08:00
GyDi
f76890cc56
fix: parse bytes base 1024 2022-11-24 11:11:31 +08:00
GyDi
e1c8f1fed9
fix: add clash fields 2022-11-24 10:46:21 +08:00
GyDi
6e19a4ab8b
chore: ci 2022-11-24 10:41:03 +08:00
GyDi
b156523a7f
chore: ci 2022-11-24 10:35:22 +08:00
GyDi
d20b745ae5
fix: direct mode hide proxies 2022-11-24 10:35:09 +08:00
GyDi
c51e9e6b2c
feat: add meta feature 2022-11-24 10:26:41 +08:00
GyDi
1a31fa9067
fix: profile can not edit 2022-11-24 10:26:25 +08:00
GyDi
558b8499af
chore: ci 2022-11-23 23:46:32 +08:00
GyDi
986c162988
v1.2.0 2022-11-23 23:15:39 +08:00
GyDi
770d5cd11c
chore: ci 2022-11-23 23:15:20 +08:00
GyDi
446d2ab3af
chore: update log 2022-11-23 23:13:44 +08:00
GyDi
2f9bf7f063
feat: display proxy group type 2022-11-23 18:27:57 +08:00
GyDi
72ff9c0964
chore: rm dead code 2022-11-23 17:45:49 +08:00
GyDi
33b1a11d85
fix: parse logger time 2022-11-23 17:45:43 +08:00
GyDi
06dabf1e4e
fix: adjust service mode ui 2022-11-23 17:45:22 +08:00
GyDi
28d3691e0b
feat: add use clash hook 2022-11-23 17:44:40 +08:00
GyDi
ffa21fbfd2
fix: adjust style 2022-11-23 17:42:01 +08:00
GyDi
db028665fd
fix: check hotkey and optimize hotkey input, close #287 2022-11-23 17:30:19 +08:00
GyDi
6bc83d9f27
fix: mutex dead lock 2022-11-23 16:04:25 +08:00
GyDi
790d832155
fix: adjust item ui 2022-11-23 15:29:42 +08:00
GyDi
f477cecdeb
fix: regenerate config before change core 2022-11-23 09:57:21 +08:00
GyDi
e031389021
fix: close connections when profile change 2022-11-23 00:20:57 +08:00
GyDi
e00f826eb8
fix: lint 2022-11-22 23:02:18 +08:00
GyDi
24f4e8ab99
chore: fix check script 2022-11-22 23:01:04 +08:00
GyDi
1550d528bd
fix: windows service mode 2022-11-22 22:01:34 +08:00
GyDi
40c041031e
fix: init config file 2022-11-22 22:01:19 +08:00
GyDi
3e555ec9f1
chore: fix mmdb url 2022-11-22 21:15:45 +08:00
GyDi
5098f14aab
fix: service mode error and fallback to sidecar 2022-11-22 20:47:21 +08:00
GyDi
a355a9c85e
fix: service mode viewer ui 2022-11-22 20:45:17 +08:00
GyDi
e7db2a8573
chore: update check script 2022-11-22 20:44:44 +08:00
GyDi
9b18bd0b48
fix: create theme error, close #294 2022-11-22 16:01:33 +08:00
GyDi
f95ddd594e
feat: guard the mixed-port and external-controller 2022-11-22 15:45:17 +08:00
GyDi
fe8168784f
feat: adjust builtin script and support meta guard script 2022-11-22 12:00:48 +08:00
MoeShin
4046f143f6
fix: matchMedia().addEventListener #258 (#296) 2022-11-22 09:25:39 +08:00
GyDi
e4e16999c8
chore: alpha release add portable 2022-11-22 00:16:30 +08:00
GyDi
10f3ba4ff4
chore: alpha ci 2022-11-21 23:19:25 +08:00
GyDi
3cd2be5081
fix: check config 2022-11-21 23:11:56 +08:00
GyDi
c9359978f9
fix: show global when no rule groups 2022-11-21 23:06:32 +08:00
GyDi
781c67b31a
fix: service viewer ref 2022-11-21 23:02:48 +08:00
GyDi
020bd129fb
fix: service ref error 2022-11-21 22:33:06 +08:00
GyDi
8086b6d78c
feat: disable script mode when use clash meta 2022-11-21 22:28:57 +08:00
GyDi
48e14b36b8
feat: check config when change core 2022-11-21 22:27:55 +08:00
GyDi
b3c1c56579
fix: group proxies render list is null 2022-11-21 22:10:24 +08:00
GyDi
bd0e932910
feat: support builtin script for enhanced mode 2022-11-21 21:05:00 +08:00
GyDi
525e5f88ae
chore: update ci 2022-11-20 23:17:05 +08:00
GyDi
005eeb0e0b
chore: update ci node version 2022-11-20 23:14:43 +08:00
GyDi
d21bb015e8
fix: pretty bytes 2022-11-20 23:08:30 +08:00
GyDi
7338838b0e
feat: adjust profiles page ui 2022-11-20 22:37:34 +08:00
GyDi
8bb4803ff9
refactor: adjust base components export 2022-11-20 22:03:55 +08:00
GyDi
892b919cf3
refactor: adjust setting dialog component 2022-11-20 21:48:39 +08:00
GyDi
572d81ecef
fix: use verge hook 2022-11-20 20:12:58 +08:00
GyDi
a4ce7a4037
feat: optimize proxy page ui 2022-11-20 19:46:16 +08:00
GyDi
6eafb15cf9
chore: adjust type 2022-11-19 17:22:29 +08:00
GyDi
e19fe5ce1c
fix: adjust notice 2022-11-19 01:24:07 +08:00
GyDi
9d2017e598
feat: add error boundary 2022-11-19 01:22:19 +08:00
GyDi
f33c419ed9
chore: rm polyfill 2022-11-19 01:22:00 +08:00
GyDi
f425fbaf9d
feat: adjust clash log 2022-11-18 22:08:06 +08:00
GyDi
bcc5ec897a
fix: windows issue 2022-11-18 20:15:34 +08:00
GyDi
f5f2fe3472
fix: change dev log level 2022-11-18 18:37:29 +08:00
GyDi
b7c3863882
fix: patch clash config 2022-11-18 18:37:17 +08:00
GyDi
d759f48ee8
fix: cmds params 2022-11-18 18:26:55 +08:00
GyDi
3a37075e71
Merge branch 'refactor' 2022-11-18 18:21:49 +08:00
GyDi
58366c0b87
chore: rm code 2022-11-18 18:19:26 +08:00
GyDi
2667ed13f1
refactor: done 2022-11-18 18:18:41 +08:00
GyDi
34daffbc96
refactor: adjust all path methods and reduce unwrap 2022-11-18 10:26:39 +08:00
GyDi
be81cd72af
fix: adjust singleton detect 2022-11-18 08:24:27 +08:00
GyDi
4ae00714d2
fix: change template 2022-11-18 08:04:26 +08:00
GyDi
f24cbb6692
refactor: rm code 2022-11-17 23:21:13 +08:00
GyDi
5a35c5b928
refactor: fix 2022-11-17 22:53:41 +08:00
GyDi
1880da6351
refactor: rm dead code 2022-11-17 22:52:22 +08:00
GyDi
df93cb103c
refactor: for windows 2022-11-17 20:19:40 +08:00
GyDi
63b474a32c
refactor: wip 2022-11-17 17:07:13 +08:00
GyDi
abdbf158d1
refactor: wip 2022-11-16 01:26:41 +08:00
GyDi
ee68d80d0a
refactor: wip 2022-11-15 01:33:50 +08:00
GyDi
c8e6f3a627
refactor: rm update item block_on 2022-11-14 23:07:51 +08:00
GyDi
dc941575fe
refactor: fix 2022-11-14 22:50:47 +08:00
GyDi
e64103e5f2
fix: copy resource file 2022-11-14 21:11:42 +08:00
GyDi
f0ab03a9fb
chore: tmpl add clash core 2022-11-14 21:10:29 +08:00
GyDi
09965f1cc6
feat: add draft 2022-11-14 19:31:22 +08:00
GyDi
d2852bb34a
refactor: fix 2022-11-14 01:45:58 +08:00
GyDi
b03c52a501
refactor: wip 2022-11-14 01:26:33 +08:00
GyDi
fd6633f536
fix: MediaQueryList addEventListener polyfill 2022-11-13 10:27:26 +08:00
GyDi
320ac81f48
chore: fix ci 2022-11-12 17:07:47 +08:00
GyDi
70bcd2428f
chore: alpha ci 2022-11-12 17:06:26 +08:00
GyDi
71f5ada0a3
chore: lock version 2022-11-12 17:06:18 +08:00
GyDi
aab5141404
chore: fix ci 2022-11-12 16:54:57 +08:00
GyDi
ff6b119f27
chore: alpha ci 2022-11-12 16:47:27 +08:00
GyDi
7ef4b7eeb8
fix: change default tun dns-hijack 2022-11-12 11:48:02 +08:00
GyDi
4668be6e24
chore: format rust code 2022-11-12 11:37:23 +08:00
GyDi
a211fc7c97
fix: something 2022-11-11 22:45:32 +08:00
GyDi
54af0b675d
chore: update deps 2022-11-11 21:28:34 +08:00
GyDi
8c8171e774
feat: change default latency test url 2022-11-11 01:24:18 +08:00
GyDi
0bb1790206
fix: provider proxy sort by delay 2022-11-11 01:21:23 +08:00
GyDi
45fc84d8be
chore: rm dead code 2022-11-10 22:58:46 +08:00
GyDi
f7500f4cad
chore: clash meta compatible and geosite.dat 2022-11-10 22:58:34 +08:00
GyDi
0cfd718d8a
feat: auto close connection when proxy changed 2022-11-10 01:27:05 +08:00
GyDi
5f486d0f51
fix: profile item menu ui dense 2022-11-09 15:53:42 +08:00
GyDi
6e5a2f85a1
fix: disable auto scroll to proxy 2022-11-09 11:44:09 +08:00
GyDi
e66a89208d
feat: support to change external controller 2022-11-06 23:23:26 +08:00
GyDi
7f65c501c6
feat: add sub-rules 2022-11-05 15:50:06 +08:00
GyDi
22c2382765
feat(macOS): support cmd+w and cmd+q 2022-11-04 00:51:46 +08:00
GyDi
38e1a4febf
chore: update clash meta 2022-11-04 00:36:32 +08:00
GyDi
8db554b377
chore: fix ci 2022-11-03 01:34:23 +08:00
GyDi
5f5cc55331
chore: compatible ci 2022-11-03 01:32:16 +08:00
GyDi
2f8b39186f
chore: update ci 2022-11-03 01:15:50 +08:00
GyDi
a3a724e2e6
chore: test ci 2022-11-03 00:51:49 +08:00
GyDi
73235c8699
chore: test ci 2022-11-03 00:48:27 +08:00
GyDi
7e5999e862
chore: add x 2022-11-02 00:57:27 +08:00
GyDi
afa244dcb0
v1.1.2 2022-11-02 00:54:51 +08:00
GyDi
4ea5bb2390
fix: check remote profile 2022-11-02 00:51:25 +08:00
GyDi
e545d552f6
chore: update log 2022-11-02 00:44:45 +08:00
GyDi
56fe7b3596
feat: add version on tray 2022-11-01 23:29:59 +08:00
GyDi
eb28ec866a
feat: add animation 2022-10-29 23:36:10 +08:00
GyDi
4649454282
fix: remove smoother 2022-10-29 20:05:55 +08:00
angrylid
a45dc6efda
feat: add animation to ProfileNew component (#252)
* chore: add .vscode to .gitignore

* feat: add animation to ProfileNew component
2022-10-29 20:02:51 +08:00
GyDi
9e56b9fbb5
fix: icon button color 2022-10-29 19:22:15 +08:00
GyDi
5504994cb9
feat: check remote profile field 2022-10-28 13:00:52 +08:00
GyDi
acff6d0432
fix: init system proxy correctly 2022-10-28 01:33:21 +08:00
GyDi
eea9cb7c5b
fix: open file 2022-10-28 01:26:45 +08:00
GyDi
c0ddddfb1f
fix: reset proxy 2022-10-28 01:06:54 +08:00
GyDi
f7dab3ca56
fix: init config error 2022-10-28 01:02:47 +08:00
GyDi
e11b4038a3
feat: system tray support zh language 2022-10-28 00:40:29 +08:00
GyDi
b635e64803
fix: adjust reset proxy 2022-10-27 21:14:14 +08:00
GyDi
20a194b49a
fix: adjust code 2022-10-27 21:13:27 +08:00
GyDi
33a5fb8837
fix: add https proxy 2022-10-26 17:11:54 +08:00
GyDi
59dae640db
fix: auto scroll into view when sorted proxies changed 2022-10-26 01:24:06 +08:00
GyDi
a6ac75e97b
feat: display delay check result timely 2022-10-26 01:11:02 +08:00
GyDi
cc5b33a8ec
fix: refresh proxies interval, close #235 2022-10-26 01:08:34 +08:00
GyDi
6e1a627b84
fix: style 2022-10-25 23:54:21 +08:00
GyDi
62d4c65e1c
chore: update deps 2022-10-25 23:54:10 +08:00
GyDi
600134a3ac
fix: fetch profile with system proxy, close #249 2022-10-25 23:45:24 +08:00
LooSheng
df14af7337
fix: The profile is replaced when the request fails. (#246) 2022-10-23 17:22:26 +08:00
GyDi
2f740b570d
fix: default dns config 2022-10-22 22:26:40 +08:00
GyDi
294d980b52
fix: kill clash when exit in service mode, close #241 2022-10-22 13:55:06 +08:00
GyDi
c9d9909d74
chore: update mmdb 2022-10-22 13:30:08 +08:00
GyDi
90eeabae7b
feat: update profile with system proxy/clash proxy 2022-10-18 23:19:21 +08:00
GyDi
a32c77c5f1
chore: adjust code 2022-10-17 23:26:25 +08:00
GyDi
910846f2ce
fix: icon button color inherit 2022-10-16 14:40:45 +08:00
GyDi
35d0438261
fix: app version to string 2022-10-16 14:38:57 +08:00
GyDi
515af472ce
fix: break loop when core terminated 2022-10-14 14:46:15 +08:00
GyDi
f062f7f9fe
feat: change global mode ui, close #226 2022-10-13 11:54:52 +08:00
GyDi
f68378041f
feat: default user agent same with app version 2022-10-12 11:28:47 +08:00
GyDi
30ef3057ac
v1.1.1 2022-10-11 23:03:12 +08:00
GyDi
48f3a934c9
chore: update log 2022-10-11 23:02:49 +08:00
GyDi
38d1fde84f
fix: api error handle 2022-10-11 22:51:18 +08:00
GyDi
dd78670a4b
fix: clash meta not load geoip, close #212 2022-10-11 22:24:54 +08:00
GyDi
b8a8190a43
fix: sort proxy during loading, close #221 2022-10-11 22:06:55 +08:00
GyDi
2d0989342f
fix: not create windows when enable slient start 2022-10-11 21:50:00 +08:00
GyDi
15ff9b06a1
fix: root background color 2022-10-11 20:49:03 +08:00
GyDi
41b19f69de
fix: create window correctly 2022-10-11 00:57:34 +08:00
GyDi
f2bd6f1fce
fix: set_activation_policy 2022-10-10 22:15:21 +08:00
GyDi
ec94218a4b
chore: rm aarch64 ci 2022-09-28 18:50:33 +08:00
GyDi
c2e5c7cf38
chore: test ci 2022-09-28 17:26:32 +08:00
GyDi
bac5734527
chore: test ci 2022-09-28 17:12:27 +08:00
GyDi
bef4033d94
chore: fix ci 2022-09-28 16:04:05 +08:00
GyDi
64216cba67
chore: fix test ci 2022-09-28 15:19:50 +08:00
Particle_G
a9d6167a9f
chore: add support for windows arm64, close #216 (#209) 2022-09-28 15:13:24 +08:00
GyDi
6c6b40548f
fix: disable spell check 2022-09-28 14:15:22 +08:00
苏业钦
a916d88e85
chore: add support linux arm64 (#215) 2022-09-28 10:40:41 +08:00
GyDi
ab2f0548a3
chore: clash meta linux use compatible version 2022-09-27 00:16:22 +08:00
GyDi
e30ba07285
feat: optimize config feedback 2022-09-26 20:46:29 +08:00
GyDi
1b336d973d
fix: adjust init launch on dev 2022-09-26 01:35:19 +08:00
GyDi
7b1866737f
v1.1.0 2022-09-26 01:27:56 +08:00
GyDi
eedc4ab648
chore: update log 2022-09-26 01:27:32 +08:00
GyDi
5e429c7a94
fix: ignore disable auto launch error 2022-09-26 01:09:05 +08:00
GyDi
57d23eb043
fix(macos): set auto launch path to application 2022-09-25 23:36:55 +08:00
GyDi
5ebd9be89a
fix: i18n 2022-09-25 01:41:46 +08:00
GyDi
5a743779e2
feat: show connections with table layout 2022-09-25 01:35:21 +08:00
GyDi
0495062110
fix: style 2022-09-24 20:55:40 +08:00
GyDi
d522191f69
fix: save enable log on localstorage 2022-09-24 19:03:14 +08:00
Priestch
bbbdc8b7a6
fix: typo in api.ts (#207) 2022-09-24 18:48:11 +08:00
GyDi
c00ed8aa5a
chore: use ubuntu latest 2022-09-24 16:08:39 +08:00
GyDi
0beaa94068
feat: show loading on proxy group delay check 2022-09-24 15:31:09 +08:00
GyDi
96e76665d6
fix: refresh clash ui await patch 2022-09-24 14:01:28 +08:00
Shun Li
6423a29600
feat: add chains[0] and process to connections display (#205)
* add chains[0] display

* add metadata.process to connections
2022-09-24 13:06:32 +08:00
GyDi
8bddf30dcf
refactor(hotkey): use tauri global shortcut 2022-09-23 15:31:01 +08:00
GyDi
a6b2db182d
feat: adjust connection page ui 2022-09-23 00:08:52 +08:00
GyDi
b9f3f9d859
Merge pull request #197 from FoundTheWOUT/main
Support ordering connection
2022-09-22 22:55:46 +08:00
GyDi
6331447dcd
feat: yaml merge key 2022-09-21 22:15:24 +08:00
GyDi
4213ee660f
feat: toggle log ws 2022-09-20 22:15:28 +08:00
GyDi
3bd9287c5d
chore: update deps 2022-09-20 21:26:38 +08:00
GyDi
b23d3f7c8b
feat: add rule page 2022-09-18 23:19:02 +08:00
GyDi
f8d9e5e027
feat: hotkey viewer 2022-09-18 15:52:53 +08:00
GyDi
8fa7fb3b1f
feat: refresh ui when hotkey clicked 2022-09-18 15:50:03 +08:00
FoundTheWOUT
63d92a0872 Support ordering connection 2022-09-15 15:52:12 +08:00
GyDi
509d83365e
feat: support hotkey (wip) 2022-09-14 01:19:02 +08:00
GyDi
d6ab73c905
fix: remove dead code 2022-09-12 13:42:21 +08:00
GyDi
5a93ba05d5
fix: style 2022-09-12 13:42:13 +08:00
GyDi
2d2fdf0b1e
fix: handle is none 2022-09-12 00:45:19 +08:00
GyDi
cf96622261
fix: unused 2022-09-12 00:02:25 +08:00
GyDi
47c8ccb0e5
refactor: optimize 2022-09-11 20:58:55 +08:00
GyDi
02fdb8778b
fix: style 2022-09-11 18:38:51 +08:00
GyDi
6bed7f0e66
fix: windows logo size 2022-09-11 17:50:58 +08:00
GyDi
7597d335b9
feat: hide window on macos 2022-09-09 16:42:35 +08:00
GyDi
f32c5ba244
fix: do not kill sidecar during updating 2022-09-09 16:33:04 +08:00
GyDi
ab53ab21e2
fix: delay update config 2022-09-09 16:30:27 +08:00
GyDi
acfe5dbb49
fix: reduce logo size 2022-09-09 16:19:13 +08:00
GyDi
f9b91fa189
feat: system proxy setting 2022-09-07 01:51:43 +08:00
GyDi
2462e68ba1
fix: window center 2022-09-06 22:18:06 +08:00
GyDi
019b2a1681
chore: update resource 2022-09-06 15:32:22 +08:00
GyDi
e94a07b677
fix: log level warn value 2022-09-06 14:42:58 +08:00
GyDi
c058c29755
feat: change default singleton port and support to change the port 2022-09-06 00:45:01 +08:00
GyDi
9e7c7ac163
feat: log info 2022-09-05 20:30:39 +08:00
GyDi
fcee41f00d
feat: kill clash by pid 2022-09-05 16:30:29 +08:00
GyDi
ff2c1bf8ed
feat: change clash port in dialog 2022-09-05 02:12:25 +08:00
GyDi
a2cf26e7ed
feat: add proxy item check loading 2022-09-04 23:53:48 +08:00
GyDi
71e6900375
feat: compatible with proxy providers health check 2022-09-04 22:55:54 +08:00
GyDi
3bdc98bd12 v1.0.6 2022-09-03 00:40:50 +08:00
GyDi
68d2a6e951
chore: update log 2022-09-03 00:39:39 +08:00
GyDi
acf47ac947
chore: update deps 2022-09-02 23:06:00 +08:00
GyDi
fda24e5f5a
chore: update deps 2022-09-02 01:35:06 +08:00
GyDi
05eca8e4d8
chore: use ubuntu 18.04 2022-09-02 01:27:06 +08:00
GyDi
b915f3b1a9
feat: add empty ui 2022-09-02 01:09:38 +08:00
GyDi
ab58968f4d
feat: complete i18n 2022-09-02 01:05:45 +08:00
GyDi
820d1e7570
fix: increase delay checker concurrency 2022-09-02 00:41:43 +08:00
GyDi
a120c8cf98
fix: external controller allow lan 2022-09-02 00:24:19 +08:00
GyDi
5a35ea116f
chore: update clash meta version 2022-09-02 00:11:52 +08:00
GyDi
e72ad1f030
fix: remove useless optimizations 2022-08-31 21:44:23 +08:00
GyDi
a17362437a
chore: test ci 2022-08-29 17:26:09 +08:00
GyDi
8643dc43b1
chore: update clash 2022-08-29 11:11:55 +08:00
GyDi
e1c869a358
chore: add test ci os version 2022-08-29 11:02:14 +08:00
GyDi
2f5b8d9abe Merge branch 'main' of github.com:zzzgydi/clash-verge 2022-08-24 22:41:36 +08:00
GyDi
db324f54eb
chore: update auto launch 2022-08-24 22:41:12 +08:00
GyDi
3242efb1a2
fix: reduce unsafe unwrap 2022-08-23 15:19:04 +08:00
FoundTheWOUT
30f9f1a021 fix: timer restore at app launch 2022-08-23 10:35:36 +08:00
GyDi
3f58d05aa7
fix: adjust log text 2022-08-19 17:11:59 +08:00
GyDi
c611a51575
fix: only script profile can display console 2022-08-17 01:45:37 +08:00
GyDi
73458dcd28 v1.0.5 2022-08-16 23:56:29 +08:00
GyDi
23ebeb1cc0
chore: update log 2022-08-16 23:56:04 +08:00
GyDi
a7ba9f1886
fix: fill button title attr 2022-08-16 23:46:33 +08:00
GyDi
d5192e2244
feat: windows portable version do not check update 2022-08-16 01:53:40 +08:00
GyDi
7eb595170f
fix: do not reset system proxy when consistent 2022-08-16 01:27:32 +08:00
GyDi
bd576ca808
fix: adjust web ui item style 2022-08-16 01:08:54 +08:00
GyDi
2f8146b11f
fix: clash field state error 2022-08-16 00:48:06 +08:00
GyDi
7f321c89cb
feat: adjust clash info parsing logs 2022-08-15 20:21:43 +08:00
GyDi
fa65f606b8
fix: badge color error 2022-08-15 20:15:44 +08:00
GyDi
7a3285adaf
fix: web ui port value error 2022-08-15 20:14:33 +08:00
GyDi
8bf78fef10
fix: delay show window 2022-08-15 01:37:26 +08:00
GyDi
78f97ce4df
feat: adjust runtime config 2022-08-15 01:30:37 +08:00
GyDi
66ccbf70f8
feat: support restart app on tray 2022-08-15 01:22:39 +08:00
GyDi
cb48600b40
fix: adjust dialog action button variant 2022-08-15 00:55:35 +08:00
GyDi
33ce235713
feat: optimize profile page 2022-08-14 23:10:19 +08:00
GyDi
f1a68ece01
fix: script code error 2022-08-12 23:41:25 +08:00
GyDi
dd563360af
fix: script exception handle 2022-08-12 11:14:34 +08:00
GyDi
7f6dac4271
feat: refactor 2022-08-12 03:20:55 +08:00
GyDi
178fd8e828
feat: adjust tun mode config 2022-08-11 03:26:08 +08:00
GyDi
1641e02a7d
feat: reimplement enhanced mode 2022-08-11 02:55:10 +08:00
GyDi
cfd04e9bb4
feat: use rquickjs crate 2022-08-10 13:05:48 +08:00
GyDi
38effaf740
feat: reimplement enhanced mode 2022-08-09 21:15:55 +08:00
GyDi
a68eb4a73e
chore: format 2022-08-09 14:33:47 +08:00
GyDi
c02990ef98
fix: change fields 2022-08-09 14:31:59 +08:00
FoundTheWOUT
5aa7d5ffe9
fix: silent start (#150) 2022-08-09 14:14:06 +08:00
GyDi
4942b0fca5
fix: save profile when update 2022-08-08 23:17:22 +08:00
GyDi
aed1bdff5a
fix: list compare wrong 2022-08-08 23:16:28 +08:00
GyDi
929c840006
fix: button color 2022-08-08 23:15:05 +08:00
GyDi
57aef1d3c2
chore: rm code 2022-08-08 22:30:13 +08:00
GyDi
c1734a094c
fix: limit theme mode value 2022-08-08 22:28:44 +08:00
GyDi
99c46685ac
feat: finish clash field control 2022-08-08 22:14:03 +08:00
GyDi
066b08040a
feat: clash field viewer wip 2022-08-08 01:51:30 +08:00
GyDi
35de2334fb fix: add valid clash field 2022-08-07 20:52:50 +08:00
GyDi
5564c966a5
feat: support web ui 2022-08-06 21:56:54 +08:00
GyDi
0891b5e7b7
feat: adjust setting page style 2022-08-06 03:48:03 +08:00
GyDi
f3341f201f
refactor: ts path alias 2022-08-06 02:35:11 +08:00
GyDi
bf0dafabe2
fix: icon style 2022-08-06 01:44:33 +08:00
GyDi
60f6587169
fix: reduce unwrap 2022-07-30 23:32:52 +08:00
GyDi
a9bf32919e
feat: runtime config viewer 2022-07-25 01:20:13 +08:00
GyDi
7633f9f88b
chore: fix updater script 2022-07-18 12:55:59 +08:00
GyDi
9b56233938
v1.0.4 2022-07-17 17:56:07 +08:00
GyDi
65074264b8
chore: update log 2022-07-17 17:52:41 +08:00
GyDi
4f6fceb87f
feat: improve log rule 2022-07-17 17:39:44 +08:00
GyDi
659fdd1d37
fix: import mod 2022-07-17 16:51:54 +08:00
GyDi
8bce2ce040
feat: theme mode support follows system 2022-07-17 16:02:17 +08:00
GyDi
55cc83a5d4
chore: update deps 2022-07-17 14:54:09 +08:00
GyDi
6a51e93ded
refactor: mode manage on tray 2022-07-13 02:26:54 +08:00
GyDi
fcf570e96e
chore: rm code 2022-07-13 01:07:29 +08:00
GyDi
cbc184e953
fix: add tray separator 2022-07-13 01:05:53 +08:00
GyDi
98f9063352
Merge pull request #128 from Limsanity/main
feat: support switch proxy mode
2022-07-13 01:02:14 +08:00
limsanity
c278f1af00 style: resolve formatting problem 2022-07-13 00:54:47 +08:00
limsanity
fbb17a0ba5 feat(system tray): support switch rule/global/direct/script mode in system tray 2022-07-13 00:43:27 +08:00
GyDi
8637a9823e
chore: update deps 2022-07-11 00:41:21 +08:00
GyDi
d717fe7e8c
chore: update clash version 2022-07-08 01:42:14 +08:00
GyDi
5e8dfe7267
chore: update aarch rule 2022-07-07 02:54:20 +08:00
GyDi
aaa4fbcdbd
chore: aarch upload script 2022-07-07 02:53:53 +08:00
GyDi
150f0cf486
fix: instantiate core after init app, close #122 2022-07-06 15:14:33 +08:00
GyDi
711b220a05
chore: update clash version 2022-07-06 00:54:01 +08:00
GyDi
b615c485f7
chore: update deps 2022-07-06 00:49:09 +08:00
GyDi
2f3b6b29ae
fix: rm macOS transition props 2022-07-05 01:24:23 +08:00
GyDi
7aecd83c4a
chore: update deps 2022-07-05 00:52:22 +08:00
GyDi
3b460ab91f
chore: change tray icon 2022-06-22 01:26:25 +08:00
GyDi
661c0eb970 v1.0.3 2022-06-20 02:09:12 +08:00
GyDi
f4a1f1fdc8
chore: update log 2022-06-20 02:06:46 +08:00
GyDi
b5432c3728
chore: update clash version 2022-06-20 01:47:15 +08:00
GyDi
6d0625c409
fix: improve external-controller parse and log 2022-06-20 01:36:56 +08:00
GyDi
2f1ea08b8a
chore: update macos icon 2022-06-18 18:41:53 +08:00
GyDi
a092da1943
fix: show windows on click 2022-06-18 17:28:37 +08:00
GyDi
43ef3cc562
chore: new logo for mac 2022-06-18 17:28:05 +08:00
GyDi
047774475c
chore: update alpha ci 2022-06-17 01:18:19 +08:00
GyDi
91b8504df5
chore: update tauri 2022-06-17 01:16:46 +08:00
GyDi
a4fb2dfcf8
fix: adjust update profile notice error 2022-06-15 02:42:04 +08:00
GyDi
ba16ec02e5
fix: style issue on mac 2022-06-15 02:41:37 +08:00
GyDi
55b7af2623
chore: update version 2022-06-15 01:15:05 +08:00
GyDi
b428eff10e
feat: improve yaml file error log 2022-06-14 01:40:02 +08:00
GyDi
35aee15b6d
chore: update deps 2022-06-14 01:23:58 +08:00
GyDi
6f53c1bfde
Merge pull request #104 from FoundTheWOUT/main
fix: check script run on all OS
2022-06-12 22:25:05 +08:00
FoundTheWOUT
db0230ed75 fix: check script run on all OS 2022-06-05 23:55:41 +08:00
GyDi
7fa3c1e12a
feat: save proxy page state 2022-06-04 18:55:39 +08:00
GyDi
3b5993652f
chore: portable script 2022-06-02 00:56:05 +08:00
GyDi
be4ad8947f
chore: updater script 2022-06-01 10:06:08 +08:00
GyDi
2ead15e78e v1.0.2 2022-06-01 01:26:05 +08:00
GyDi
aa0740ff94
chore: update log 2022-06-01 01:25:39 +08:00
GyDi
6d3f837820
fix: macOS disable transparent 2022-06-01 01:04:46 +08:00
GyDi
359b82c29c
chore: rm some files 2022-06-01 00:49:52 +08:00
GyDi
b54171bc2c
chore: rust cache 2022-06-01 00:47:04 +08:00
GyDi
936b2131e0
chore: adjust ci and script 2022-06-01 00:43:57 +08:00
GyDi
aaef6a9e9c chore: enable meta by default 2022-06-01 00:31:12 +08:00
GyDi
9327006e61
chore: rm file 2022-05-30 00:58:48 +08:00
GyDi
5225c841ae
chore: adjust code 2022-05-30 00:57:31 +08:00
GyDi
8464e319fd
chore: update deps 2022-05-29 23:35:22 +08:00
GyDi
98b8bd90ea
fix: window transparent and can not get hwnd 2022-05-25 19:06:06 +08:00
GyDi
ae94993b09
fix: create main window 2022-05-25 16:45:18 +08:00
GyDi
72f10aaed1
chore: update deps 2022-05-25 16:12:48 +08:00
GyDi
4a74bae8c7
chore: update clash core 2022-05-25 16:08:58 +08:00
ctaoist
5164aec37b
feat: light mode wip (#96)
* 关闭窗口释放UI资源

* windows 还有左键点击事件

* 兼容enhance profile

* bug 修复
2022-05-25 16:06:39 +08:00
GyDi
1581e9b1cd
chore: update clash version 2022-05-18 14:14:37 +08:00
GyDi
536e3ffb11
chore: alpha ci 2022-05-18 14:07:41 +08:00
GyDi
d6103191ba
chore: error text 2022-05-18 09:58:20 +08:00
GyDi
49e37a19a5
chore: ci 2022-05-17 12:35:42 +08:00
GyDi
d02431e260
chore: fix alpha ci 2022-05-17 12:34:30 +08:00
GyDi
f24f6ead92
chore: alpha ci 2022-05-17 11:41:45 +08:00
GyDi
b7bdb7ae50
chore: meta 2022-05-17 02:46:15 +08:00
GyDi
2dcf8ac96f
chore: fix ci 2022-05-17 02:21:58 +08:00
GyDi
5421c94853
chore: test ci 2022-05-17 02:17:02 +08:00
GyDi
10f6bc092a
chore: alpha 2022-05-17 02:11:29 +08:00
GyDi
be9ea4ea8e
feat: clash meta core supports 2022-05-17 01:59:49 +08:00
GyDi
f5c6fa842a
fix: adjust notice 2022-05-17 00:51:37 +08:00
GyDi
e0943ce905
fix: label text 2022-05-16 20:26:13 +08:00
GyDi
61e7df77a7
feat: script mode 2022-05-16 20:18:56 +08:00
GyDi
a5434360bc
chore: test ci 2022-05-16 17:55:14 +08:00
GyDi
ba29c66e3b
chore: update deps 2022-05-16 17:54:21 +08:00
GyDi
b3a72d55ae
feat: clash meta core support (wip) 2022-05-16 01:52:50 +08:00
GyDi
c382ad1cc8
fix: icon path 2022-05-13 12:28:36 +08:00
GyDi
363e28f323
fix: icon issue 2022-05-13 02:46:06 +08:00
GyDi
d695656b8c
fix: notice ui blocking 2022-05-13 02:29:43 +08:00
GyDi
31c6cbc0a2
fix: service mode error 2022-05-13 02:11:03 +08:00
GyDi
b93284bc2f
chore: fix linux build ci 2022-05-11 12:39:41 +08:00
GyDi
99c855b01b
chore: test ci 2022-05-11 12:20:20 +08:00
GyDi
c2eaedc959
chore: test ci 2022-05-11 12:12:55 +08:00
GyDi
a631cd67ec
chore: test ci fix deps 2022-05-11 11:30:19 +08:00
GyDi
66fccd3c68
chore: test ci 2022-05-11 11:22:09 +08:00
GyDi
9a4ebf4daa
chore: test ci 2022-05-11 10:51:28 +08:00
GyDi
91c14211c6
chore: continue on error 2022-05-11 01:26:11 +08:00
GyDi
1ea07c458b
chore: test linux 2022-05-11 00:52:20 +08:00
GyDi
591add6e0c
chore: test ci 2022-05-11 00:31:19 +08:00
GyDi
b99fff66df v1.0.1 2022-05-10 21:40:33 +08:00
GyDi
f54ba05b00
chore: update log 2022-05-10 21:40:07 +08:00
GyDi
6596fb00c7
fix: win11 drag lag 2022-05-09 14:41:26 +08:00
GyDi
df0b5a80dc
chore: change default height 2022-05-09 14:01:46 +08:00
GyDi
8cdbb31dbe
fix: rm unwrap 2022-05-09 14:01:14 +08:00
GyDi
0be4b1222d
feat: reduce gpu usage when hidden 2022-05-07 14:43:52 +08:00
GyDi
18a6bfd73a
feat: interval update from now field 2022-05-07 14:43:08 +08:00
GyDi
5e2271b237
feat: adjust theme 2022-05-06 14:04:39 +08:00
GyDi
798999d490
fix: edit profile info 2022-05-06 12:46:27 +08:00
GyDi
0e68c5e8bc
feat: supports more remote headers close #81 2022-05-06 10:52:59 +08:00
GyDi
9694af82f4
feat: check the remote profile 2022-05-06 01:26:24 +08:00
GyDi
28a4386975
fix: change window default size 2022-05-06 01:15:15 +08:00
GyDi
a238f7beba
chore: update deps 2022-05-06 01:14:13 +08:00
GyDi
75ba16281b
chore: change default user agent 2022-05-06 00:42:20 +08:00
GyDi
ad65a278d4
chore: merge
feat: remove outdated config
2022-04-28 16:28:18 +08:00
tianyoulan
a2320b3f8d feat: fix typo 2022-04-28 15:35:17 +08:00
tianyoulan
ae12853ad0 feat: remove trailing comma 2022-04-28 15:05:10 +08:00
tianyoulan
68adf6dc2f feat: remove outdated config 2022-04-28 15:02:37 +08:00
GyDi
f88989bd4b
fix: change service installer and uninstaller 2022-04-27 15:46:44 +08:00
GyDi
77ef3847ce v1.0.0 2022-04-26 00:50:48 +08:00
GyDi
b8291837fc
chore: update log 2022-04-26 00:49:07 +08:00
GyDi
423a7f951a
fix: adjust connection scroll 2022-04-26 00:37:28 +08:00
GyDi
557f5fe364
fix: adjust something 2022-04-25 20:00:11 +08:00
GyDi
5308970ad8
fix: adjust debounce wait time 2022-04-25 19:39:21 +08:00
GyDi
6b368953f4
feat: windows service mode ui 2022-04-25 16:12:04 +08:00
GyDi
2d0b63c29d
feat: add some commands 2022-04-24 21:03:47 +08:00
GyDi
34e941c8cb
feat: windows service mode 2022-04-24 21:00:17 +08:00
GyDi
76cf007fff
chore: update check script 2022-04-24 15:35:30 +08:00
GyDi
321963be83
wip: windows service mode 2022-04-23 17:26:32 +08:00
GyDi
c733bda6c3
test: windows service 2022-04-21 21:28:44 +08:00
GyDi
e67b50b976
chore: adjust guard log 2022-04-21 19:51:20 +08:00
GyDi
9e3c080909
fix: adjust dns config 2022-04-21 19:50:22 +08:00
GyDi
cb661aaebd
feat: add update interval 2022-04-21 14:26:41 +08:00
GyDi
573571978c
chore: adjust 2022-04-21 14:24:35 +08:00
GyDi
dc492a2a0a
chore: update clash version 2022-04-20 21:02:41 +08:00
GyDi
e47747dd0e
feat: refactor and supports cron tasks 2022-04-20 20:39:27 +08:00
GyDi
4de944b41e
feat: supports cron update profiles 2022-04-20 20:37:16 +08:00
GyDi
5f7a1fa5cd
refactor: verge 2022-04-20 11:17:54 +08:00
GyDi
b8ad328cde
refactor: wip 2022-04-20 01:44:47 +08:00
GyDi
3076fd19c1
refactor: mutex 2022-04-19 14:38:59 +08:00
GyDi
fac437b8c1
fix: traffic graph adapt to different fps 2022-04-19 13:55:26 +08:00
GyDi
697c25015e
refactor: wip 2022-04-19 01:41:20 +08:00
GyDi
d83b404fc3
chore: check script proxy agent supports 2022-04-17 00:37:21 +08:00
GyDi
ab7313cbc4
feat: optimize traffic graph quadratic curve 2022-04-16 22:32:44 +08:00
GyDi
1b8d70322b
feat: optimize the animation of the traffic graph 2022-04-16 17:28:30 +08:00
GyDi
844ffab4ed
fix: optimize clash launch 2022-04-15 01:29:25 +08:00
GyDi
f5d0513d1f
fix: reset after exit 2022-04-14 01:29:33 +08:00
GyDi
557abd4285
fix: adjust code 2022-04-14 01:29:02 +08:00
GyDi
f4f1a0fbc6 v0.0.29 2022-04-13 01:30:08 +08:00
GyDi
74e10dc012
chore: update log 2022-04-13 01:29:48 +08:00
GyDi
359812b7ed
chore: update dep 2022-04-13 01:21:49 +08:00
GyDi
c2449e53c4
chore: rename green to portable 2022-04-13 01:17:40 +08:00
GyDi
1a91249da2
feat: system tray add tun mode 2022-04-13 01:09:51 +08:00
GyDi
b9162f9576
fix: adjust log 2022-04-13 00:49:30 +08:00
GyDi
f726e8a7b3
feat: supports change config dir 2022-04-12 23:09:32 +08:00
GyDi
2f284cfdc9
chore: update clash version 2022-04-12 23:04:19 +08:00
GyDi
b74696adba
feat: add default user agent 2022-04-12 01:05:51 +08:00
GyDi
c8ccba0192
chore: rm console 2022-04-11 02:26:09 +08:00
GyDi
41b0e05f62
feat: connections page supports filter 2022-04-11 02:25:34 +08:00
GyDi
847d5f1b3b
feat: log page supports filter 2022-04-11 01:46:33 +08:00
GyDi
0445f9dfc2
fix: check button hover style 2022-04-10 03:33:17 +08:00
GyDi
3001c780bd
feat: optimize delay checker concurrency strategy 2022-04-10 02:58:48 +08:00
GyDi
68ad5e2320
feat: support sort proxy node and custom test url 2022-04-10 02:09:36 +08:00
GyDi
b5e229b19c
chore: update deps 2022-04-10 01:31:05 +08:00
GyDi
453d798fcf
refactor: proxy head 2022-04-08 01:35:18 +08:00
GyDi
451afdb660 v0.0.28 2022-04-07 01:28:30 +08:00
GyDi
d298bda92c
chore: update log 2022-04-07 01:27:48 +08:00
GyDi
fd99ba6255
feat: handle remote clash config fields 2022-04-07 01:20:44 +08:00
GyDi
6ade0b2b1a
fix: icon button color inherit 2022-04-06 22:52:00 +08:00
GyDi
9902003da9
fix: remove the lonely zero 2022-04-06 22:48:10 +08:00
GyDi
0ff2fcac11
chore: readme 2022-04-06 01:52:20 +08:00
GyDi
e80be8e7b6 v0.0.27 2022-04-06 01:21:33 +08:00
GyDi
fe0ad0f5cb
chore: update log 2022-04-06 01:19:13 +08:00
GyDi
cb8e162f4e
refactor: update profile menu 2022-04-06 01:13:06 +08:00
GyDi
ae8c30fe57
chore: add updater script 2022-04-05 23:24:52 +08:00
GyDi
ab82db9e22
chore: updater json supports arch field 2022-04-05 23:19:54 +08:00
GyDi
cb9d3098de
chore: add ci 2022-04-05 20:21:53 +08:00
GyDi
c927419c99
refactor: enhanced mode ui component 2022-04-03 01:41:48 +08:00
GyDi
c009026961
feat: add text color 2022-04-02 17:18:38 +08:00
GyDi
51cf442fa5
fix: i18n add value 2022-04-02 13:49:48 +08:00
GyDi
954e3553ee
fix: proxy page first render 2022-04-02 01:23:31 +08:00
GyDi
cde17385b4
feat: control final tun config 2022-04-01 23:55:44 +08:00
GyDi
5b9e078061
chore: add debug feature 2022-04-01 23:43:35 +08:00
GyDi
0290d9ddfc
feat: support css injection 2022-04-01 02:08:42 +08:00
GyDi
eab671d102
fix: console warning 2022-04-01 01:16:23 +08:00
GyDi
f9a96ff914
feat: support theme setting 2022-03-31 23:38:00 +08:00
GyDi
3ec2b46d28
feat: add text color 2022-03-31 23:34:36 +08:00
GyDi
aec30b89e0
feat: add theme setting 2022-03-31 23:11:50 +08:00
GyDi
309c33e190
refactor: ui theme 2022-03-30 12:36:39 +08:00
GyDi
9c0276f97b
chore: add api 2022-03-30 01:30:22 +08:00
GyDi
0a3402ff43
fix: icon button title 2022-03-30 01:29:25 +08:00
GyDi
bd82308024
chore: update tauri 2022-03-29 23:05:32 +08:00
GyDi
db3b634e62
fix: macOS transition flickers close #47 2022-03-29 01:39:54 +08:00
GyDi
40bcb22977
chore: update deps 2022-03-29 01:33:14 +08:00
GyDi
4bd94092f1
fix: csp image data 2022-03-28 23:17:11 +08:00
GyDi
cac1ce6895
chore: readme 2022-03-28 01:33:58 +08:00
GyDi
5c3696123a v0.0.26 2022-03-28 01:30:54 +08:00
GyDi
4140fc86ee
chore: update log 2022-03-28 01:18:18 +08:00
GyDi
2ad771e7fd
fix: close dialog after save 2022-03-28 01:09:34 +08:00
GyDi
5a38468144
refactor: optimize enhance mode strategy 2022-03-28 01:07:14 +08:00
GyDi
0a9c81772f
fix: change to deep copy 2022-03-28 00:56:48 +08:00
GyDi
c9649ac501
feat: enhanced mode supports more fields 2022-03-27 20:36:13 +08:00
GyDi
6ea567742b
chore: profile release 2022-03-27 01:38:40 +08:00
GyDi
f31349eaa0
feat: supports edit profile file 2022-03-27 00:58:17 +08:00
GyDi
9d44668d5f
feat: supports silent start 2022-03-26 18:56:16 +08:00
GyDi
a12f58c1c7
feat: use crate open 2022-03-23 23:00:14 +08:00
GyDi
b5283eaaed
fix: window style close #45 2022-03-23 21:43:13 +08:00
GyDi
4b6189af5f
feat: enhance connections display order 2022-03-23 14:02:08 +08:00
GyDi
5d0ffbe453
chore: adjust style 2022-03-23 13:58:57 +08:00
GyDi
502706931e
fix: manage global proxy correctly 2022-03-23 12:49:34 +08:00
GyDi
57c411288f
fix: tauri csp 2022-03-23 01:47:35 +08:00
GyDi
b09b7b11a1
feat: save global selected 2022-03-22 12:38:59 +08:00
GyDi
1a7b3c7294 v0.0.25 2022-03-22 01:51:06 +08:00
GyDi
4678fc7dde
chore: update log 2022-03-22 01:50:24 +08:00
GyDi
9f492fad49
chore: update clash core 2022-03-22 01:50:15 +08:00
GyDi
bd0a959e18
fix: windows style 2022-03-22 01:36:06 +08:00
GyDi
dd605e2610
fix: update state 2022-03-22 01:27:22 +08:00
inRm3D
9910f6b817
chore: readme (#43) 2022-03-21 21:55:45 +08:00
GyDi
991897aff4
chore: tauri feature 2022-03-21 11:40:52 +08:00
GyDi
3cde019208
fix: profile item loading state 2022-03-21 11:40:27 +08:00
GyDi
2f6efbed63
chore: tauri allowList 2022-03-20 13:16:50 +08:00
GyDi
5cff4e299b
chore: update clash version 2022-03-20 12:37:55 +08:00
GyDi
bce33639da v0.0.24 2022-03-20 01:50:39 +08:00
GyDi
366c465cad
chore: update log 2022-03-20 01:50:15 +08:00
GyDi
acc6e05bdc
feat: system tray supports system proxy setting 2022-03-20 01:36:43 +08:00
GyDi
4ce15577cd
feat: prevent context menu on Windows close #22 2022-03-19 19:28:53 +08:00
GyDi
98fa4d5e65
feat: create local profile with selected file 2022-03-19 19:21:55 +08:00
GyDi
7fe94076c7
feat: reduce the impact of the enhanced mode 2022-03-19 16:02:47 +08:00
GyDi
b8b0c8fa63
fix: adjust windows style 2022-03-19 15:35:59 +08:00
GyDi
e585e87bec
feat: parse update log 2022-03-19 15:31:58 +08:00
GyDi
6a4924bb16
chore: update log supports 2022-03-19 14:04:58 +08:00
GyDi
ab0d516d91
chore: updater proxy 2022-03-19 11:14:12 +08:00
GyDi
b756ae39d0
chore: ci support linux 2022-03-19 10:50:44 +08:00
GyDi
c15c38ea8f
chore: readme 2022-03-18 19:02:53 +08:00
GyDi
743963318f
feat: fill i18n 2022-03-18 14:59:23 +08:00
GyDi
ed3fc50858
feat: dayjs i18n 2022-03-18 14:45:24 +08:00
GyDi
5b886fe6be
feat: connections page simply support 2022-03-18 14:43:22 +08:00
GyDi
7074bbc405
feat: add wintun.dll by default 2022-03-18 11:49:00 +08:00
GyDi
ef314c1707
fix: change mixed port error 2022-03-18 11:39:02 +08:00
GyDi
a3e7626dd9
chore: add dev feature 2022-03-18 10:59:35 +08:00
GyDi
5cb5d74eed
chore: rename temp dir 2022-03-18 10:20:24 +08:00
GyDi
6eee10d46d
chore: resolve wintun.dll 2022-03-17 19:29:30 +08:00
GyDi
2d95f2b0d6
fix: auto launch path 2022-03-16 18:44:25 +08:00
GyDi
ec41bb9c70
fix: tun mode config 2022-03-15 14:23:57 +08:00
GyDi
2d1780b1cf
fix: adjsut open cmd error 2022-03-15 12:51:39 +08:00
GyDi
d9ce99887c
chore: fix ci env 2022-03-15 00:00:20 +08:00
GyDi
9b9cc90414 v0.0.23 2022-03-14 01:34:26 +08:00
GyDi
c1eb539a5c
feat: event emit when clash config update 2022-03-13 01:37:41 +08:00
GyDi
e1793f57ef
chore: fix ci 2022-03-13 00:59:42 +08:00
GyDi
dbd09a8743
chore: update tauri action 2022-03-13 00:58:49 +08:00
GyDi
0d189ca617
chore: green version bundle 2022-03-13 00:52:31 +08:00
GyDi
dc9bcc40ee
chore: rm then 2022-03-12 23:58:20 +08:00
GyDi
4991f7ff39
feat: i18n supports 2022-03-12 23:07:45 +08:00
GyDi
a393b8b122
fix: parse external-controller 2022-03-12 21:35:20 +08:00
GyDi
c73b354386
fix: config file case close #18 2022-03-12 21:03:17 +08:00
GyDi
392ecee3ff
chore: merge 2022-03-12 18:45:07 +08:00
ttyS3
bae721c49e
chore: show open app and log dir failed message 2022-03-12 18:04:56 +08:00
GyDi
4e806e21a6
chore: adjust error log 2022-03-12 15:57:16 +08:00
GyDi
ec0fdf83b2
feat: change open command on linux
refactor(logging): refine open dir failed log message, add Linux support
2022-03-12 15:40:23 +08:00
ttyS3
cb94d8414f
feat: add Linux open dir support
refactor(logging): refine open dir failed log message
2022-03-12 03:18:25 +08:00
GyDi
8890051c17
fix: patch item option 2022-03-11 19:56:56 +08:00
GyDi
cf00c9476f
fix: user agent not works 2022-03-11 19:50:51 +08:00
GyDi
b2a24c7abd v0.0.22 2022-03-10 02:27:00 +08:00
GyDi
732a1f4694
chore: rm dead code 2022-03-10 02:25:35 +08:00
GyDi
4c5aa7084e
fix: external-controller 2022-03-10 02:19:06 +08:00
GyDi
fe1fea671c
feat: support more options for remote profile 2022-03-10 02:03:55 +08:00
GyDi
04c754c0ac
chore: reduce icon size on macOS 2022-03-10 01:04:53 +08:00
GyDi
754c22c84e
chore: adjust icon 2022-03-10 00:04:59 +08:00
GyDi
629331870b
chore: add linux link 2022-03-09 20:14:15 +08:00
GyDi
78774315cb
feat: linux system proxy 2022-03-09 19:48:32 +08:00
GyDi
36b9c07928
chore: update deps 2022-03-09 02:36:40 +08:00
GyDi
40a818630d
fix: change proxy bypass on mac 2022-03-09 02:36:03 +08:00
GyDi
568511a4cf
fix: kill sidecars after install still in test 2022-03-09 02:09:51 +08:00
GyDi
109fb39e09
fix: log some error 2022-03-09 02:00:56 +08:00
GyDi
68450d2042
chore: rename productName 2022-03-09 01:53:34 +08:00
GyDi
8a052bbed6
chore: update tauri version 2022-03-09 01:50:37 +08:00
GyDi
3afbb56640
fix: apply_blur parameter 2022-03-08 10:44:41 +08:00
GyDi
c0ad84a491
fix: limit enhanced profile range 2022-03-08 01:45:46 +08:00
GyDi
c72f17605c
chore: update deps 2022-03-08 01:18:20 +08:00
GyDi
42fbee0cdb
chore: add default bypass 2022-03-08 00:39:09 +08:00
GyDi
e9b7ec735f
fix: profile updated field 2022-03-07 01:41:42 +08:00
GyDi
743788135f
fix: profile field check 2022-03-07 01:30:32 +08:00
GyDi
8ea3e6fa26
fix: create dir panic 2022-03-07 01:06:21 +08:00
GyDi
f23c83e681 v0.0.21 2022-03-06 18:36:02 +08:00
GyDi
b615bda17e
chore: default release body 2022-03-06 18:34:38 +08:00
GyDi
f7c7cd1d3c chore: update deps 2022-03-06 18:04:44 +08:00
GyDi
c7e7be4379
fix: only error when selected 2022-03-06 17:06:45 +08:00
GyDi
d63d49f246
feat: enhance profile status 2022-03-06 17:02:29 +08:00
GyDi
dad94edb20
feat: menu item refresh enhanced mode 2022-03-06 15:46:16 +08:00
GyDi
7108d5f3ab
fix: enhanced profile consistency 2022-03-06 15:40:16 +08:00
GyDi
ef47a74920
feat: profile enhanced mode 2022-03-06 14:59:25 +08:00
GyDi
a43dab8057
feat: profile enhanced ui 2022-03-05 22:54:39 +08:00
GyDi
f44039b628
feat: profile item adjust 2022-03-05 19:04:20 +08:00
GyDi
08fa5205b0
fix: simply compatible with proxy providers 2022-03-05 16:18:44 +08:00
GyDi
b91daebd92
fix: component warning 2022-03-05 15:48:39 +08:00
GyDi
9cd6c5c624
chore: update copyright 2022-03-05 01:51:29 +08:00
GyDi
650e017b72
fix: when updater failed 2022-03-04 02:10:27 +08:00
GyDi
18f9d6dec5 fix: log file 2022-03-04 02:08:26 +08:00
GyDi
4df6571ad9
chore: update deps 2022-03-03 20:37:06 +08:00
GyDi
0f5923a10a
chore: enhance wip 2022-03-03 01:58:05 +08:00
GyDi
bcdae1169e
fix: result 2022-03-03 01:56:47 +08:00
GyDi
f260d5df49
feat: enhanced profile (wip) 2022-03-03 01:52:02 +08:00
GyDi
808b861dd1 v0.0.20 2022-03-02 01:59:53 +08:00
GyDi
17f1c487a8
feat: edit profile item 2022-03-02 01:58:16 +08:00
GyDi
8dc2c1a38f
fix: cover profile extra 2022-03-02 01:45:00 +08:00
GyDi
220a494692
chore: adjust log field 2022-03-02 01:13:31 +08:00
GyDi
db6bc10196
chore: rm file 2022-03-02 00:58:07 +08:00
GyDi
1880363aeb
feat: use nanoid 2022-03-02 00:56:05 +08:00
GyDi
19c7b59883
feat: compatible profile config 2022-03-01 11:05:33 +08:00
GyDi
749df89229
refactor: profile config 2022-03-01 08:58:47 +08:00
GyDi
444f2172fa v0.0.19 2022-02-28 01:59:53 +08:00
GyDi
77a77c0ea7
wip: refactor profile 2022-02-28 01:59:13 +08:00
GyDi
dbf380a0d1
refactor: use anyhow to handle error 2022-02-28 01:34:25 +08:00
GyDi
ade34f5217
fix: display menu only on macos 2022-02-27 01:47:56 +08:00
GyDi
e89607799a
fix: proxy global showType 2022-02-27 01:33:22 +08:00
GyDi
d05d8d6a9e
feat: native menu supports 2022-02-27 01:29:57 +08:00
GyDi
b6aa50d3dc
chore: update deps 2022-02-27 01:16:43 +08:00
GyDi
9df361935f
feat: filter proxy and display type 2022-02-27 00:58:14 +08:00
GyDi
98b8a122b6
feat: use lock fn 2022-02-26 17:39:36 +08:00
GyDi
c7232522ee
feat: refactor proxy page 2022-02-26 17:39:08 +08:00
GyDi
5280f1d745
feat: proxy group auto scroll to current 2022-02-26 01:21:39 +08:00
GyDi
b52a081e7b v0.0.18 2022-02-25 02:13:41 +08:00
GyDi
f981a44861
chore: ci 2022-02-25 02:12:55 +08:00
GyDi
d7c5ce0750
feat: clash tun mode supports 2022-02-25 02:09:39 +08:00
GyDi
9ccc66ca1e
feat: use enhanced guard-state 2022-02-25 01:22:33 +08:00
GyDi
8606af3616
feat: guard state supports debounce guard 2022-02-25 01:21:13 +08:00
GyDi
f6e821ba6b
feat: adjust clash version display 2022-02-24 23:04:18 +08:00
GyDi
e8dbcf819b
feat: hide command window 2022-02-24 22:46:12 +08:00
GyDi
bbe2ef4e8e
fix: use full clash config 2022-02-23 23:23:07 +08:00
GyDi
dd15455031
feat: enhance log data 2022-02-23 02:00:45 +08:00
GyDi
12ac7bb338
chore: update readme 2022-02-22 21:54:33 +08:00
GyDi
46ef348f0d v0.0.17 2022-02-22 02:08:31 +08:00
GyDi
1a55cca8af
fix: reconnect websocket when restart clash 2022-02-22 02:05:22 +08:00
GyDi
81ee989f1f
chore: enhance publish ci 2022-02-22 01:56:06 +08:00
GyDi
c9c06f8a3d
fix: wrong exe path 2022-02-21 22:33:37 +08:00
GyDi
72127979c3
chore: use tauri cli 2022-02-21 22:31:00 +08:00
GyDi
1ad3ddef94 v0.0.16 2022-02-21 01:33:11 +08:00
GyDi
97ec5eabf7
feat: change window style 2022-02-20 23:46:13 +08:00
GyDi
e12e3a3f2d
feat: fill verge template 2022-02-20 20:12:55 +08:00
GyDi
4ff625f23b
feat: enable customize guard duration 2022-02-20 20:11:39 +08:00
GyDi
e38dcd85ac
feat: system proxy guard 2022-02-20 18:53:21 +08:00
GyDi
0245baf1b6
feat: enable show or hide traffic graph 2022-02-19 18:14:40 +08:00
GyDi
10b55c043c
fix: patch verge config 2022-02-19 17:32:23 +08:00
GyDi
457655b416
feat: traffic line graph 2022-02-19 17:02:24 +08:00
GyDi
7e4506c860
chore: update deps 2022-02-19 00:34:09 +08:00
GyDi
794d376348
feat: adjust profile item ui 2022-02-19 00:23:47 +08:00
GyDi
c60578f5b5
feat: adjust fetch profile url 2022-02-19 00:09:36 +08:00
GyDi
3a9a392a77
feat: inline config file template 2022-02-18 23:57:13 +08:00
GyDi
a13d4698be
chore: update check script 2022-02-18 23:49:39 +08:00
GyDi
c046a1993e
fix: fetch profile panic 2022-02-17 13:44:26 +08:00
GyDi
f709117cc4
feat: kill sidecars when update app 2022-02-17 02:10:25 +08:00
GyDi
30dd298fca
feat: delete file 2022-02-17 01:58:12 +08:00
GyDi
0ff8bb8090
feat: lock some async functions 2022-02-17 01:42:25 +08:00
GyDi
74bbd3c3a2
chore: tauri updater env 2022-02-16 11:02:00 +08:00
GyDi
2e82eaf59a
chore: add pubkey 2022-02-16 10:59:31 +08:00
GyDi
7f1df1f1bd v0.0.15 2022-02-16 03:22:25 +08:00
GyDi
3c79238a44
feat: support open dir 2022-02-16 03:21:34 +08:00
GyDi
0aa2565df3
fix: spawn command 2022-02-16 02:43:52 +08:00
GyDi
22b11db16e
feat: change allow list 2022-02-16 02:42:56 +08:00
GyDi
d0e678b5e9
feat: support check delay 2022-02-16 02:22:01 +08:00
GyDi
e7bba968b3 fix: import error 2022-02-15 01:36:19 +08:00
GyDi
4934a24293
feat: scroll to proxy item 2022-02-15 01:34:10 +08:00
GyDi
ccb68bcda9
chore: update deps 2022-02-15 00:33:58 +08:00
GyDi
66bf4ba3ad
fix: not open file when new profile 2022-02-15 00:21:34 +08:00
GyDi
78a0cfd052
feat: edit system proxy bypass 2022-02-14 01:26:24 +08:00
GyDi
8548373742
feat: disable user select 2022-02-13 19:52:35 +08:00
GyDi
2dfd725ee0
fix: reset value correctly 2022-02-13 19:40:31 +08:00
GyDi
5b779b4f14
feat: new profile able to edit name and desc 2022-02-13 19:33:24 +08:00
GyDi
fc48aa7155 chore: adjust files 2022-02-13 19:27:24 +08:00
GyDi
6193a842f4
feat: update tauri version 2022-02-13 18:45:03 +08:00
GyDi
eb86b471fe
fix: something 2022-02-12 21:12:39 +08:00
GyDi
2b52584547
fix: menu without fragment 2022-02-10 01:42:52 +08:00
GyDi
6e3cc57f48
feat: display clash core version 2022-02-10 01:35:55 +08:00
GyDi
f2c04621a5
feat: adjust profile item menu 2022-02-10 01:14:57 +08:00
GyDi
3ed6938d4a v0.0.14 2022-02-09 02:11:01 +08:00
GyDi
99fec25ed5
feat: profile item ui 2022-02-09 02:08:27 +08:00
GyDi
73758ad1fd
fix: proxy list error 2022-02-09 02:02:29 +08:00
GyDi
c53fe0ed1f
feat: support new profile 2022-02-07 17:26:05 +08:00
GyDi
6082c2bcac
feat: support open command for viewing 2022-02-07 16:45:20 +08:00
GyDi
069abed784
chore: add item template yaml 2022-02-02 20:56:06 +08:00
GyDi
7d8fa4d78a
fix: something 2022-01-31 23:34:58 +08:00
GyDi
76081f8d89
fix: macos auto launch fail 2022-01-31 23:32:41 +08:00
GyDi
5ef4285558
chore: update deps and app name 2022-01-31 23:27:11 +08:00
GyDi
aa7df4282e
chore: cargo update 2022-01-28 22:02:17 +08:00
GyDi
0ef1d5d0de
chore: update clash version 2022-01-28 22:00:15 +08:00
GyDi
cceb4bb81f v0.0.13 2022-01-25 02:11:00 +08:00
GyDi
f0f45e007d
feat: global proxies use virtual list 2022-01-25 02:08:10 +08:00
GyDi
6a8ffe1642
feat: enable change proxy mode 2022-01-25 01:51:44 +08:00
GyDi
3a73868c10
feat: update styles 2022-01-25 01:48:26 +08:00
GyDi
ab1b5897a6
feat: manage clash mode 2022-01-24 23:13:13 +08:00
GyDi
f94734a5c8 chore: update readme 2022-01-22 23:57:39 +08:00
GyDi
61b86c9584 v0.0.12 2022-01-21 03:09:21 +08:00
GyDi
1ac1d6e903
chore: update ci 2022-01-21 03:08:40 +08:00
GyDi
b6c58f74c0
fix: type error 2022-01-21 03:08:20 +08:00
GyDi
c88e99d87c v0.0.12 2022-01-21 02:59:33 +08:00
GyDi
8d7ab9d05e
refactor: rename profiles & command state 2022-01-21 02:57:15 +08:00
GyDi
47155a4a29
feat: change system porxy when changed port 2022-01-21 02:50:13 +08:00
GyDi
d0b87fd7c3
feat: enable change mixed port 2022-01-21 02:32:23 +08:00
GyDi
d49fd37656
feat: manage clash config 2022-01-21 02:31:44 +08:00
GyDi
0bd29d71be
chore: add ahooks 2022-01-21 02:29:45 +08:00
GyDi
4b5b62c8ae
fix: restart clash should update something 2022-01-21 00:29:33 +08:00
GyDi
65fb2ca2d5
feat: enable update clash info 2022-01-20 23:41:08 +08:00
GyDi
0d5bfc0997
feat: rename edit as view 2022-01-19 23:58:34 +08:00
GyDi
4f02c373c2
chore: adjust ci 2022-01-19 23:55:05 +08:00
GyDi
209a5b1207
fix: script error... 2022-01-19 00:49:23 +08:00
GyDi
95349eacab
fix: tag error 2022-01-19 00:46:43 +08:00
GyDi
82ba604b99
fix: script error 2022-01-19 00:42:37 +08:00
GyDi
46a8dec655
feat: test auto gen update.json ci 2022-01-19 00:37:59 +08:00
GyDi
b5af234524 v0.0.11 2022-01-17 02:53:07 +08:00
GyDi
b5c41750f7
fix: remove cargo test 2022-01-17 02:50:19 +08:00
GyDi
6083824eec v0.0.11 2022-01-17 02:44:08 +08:00
GyDi
40977785c3
feat: adjust setting typography 2022-01-17 02:42:52 +08:00
GyDi
5eddf4f1aa
feat: enable force select profile 2022-01-17 02:28:23 +08:00
GyDi
99a8e25411
feat: support edit profile item 2022-01-17 02:16:17 +08:00
GyDi
08587d8f2f
fix: reduce proxy item height 2022-01-17 02:15:54 +08:00
GyDi
3480d50f61
feat: adjust control ui 2022-01-17 02:15:06 +08:00
GyDi
43af55252d
feat: update profile supports noproxy 2022-01-16 22:57:42 +08:00
GyDi
9c43b31fc0
refactor: something 2022-01-16 18:30:25 +08:00
GyDi
9ec7184aa1
fix: put profile request with no proxy 2022-01-16 18:20:01 +08:00
GyDi
4e2cb30db7
refactor: notice caller 2022-01-16 17:56:43 +08:00
GyDi
9ca83d3291
chore: change ci 2022-01-16 16:03:53 +08:00
GyDi
6b2172d873
fix: ci strategy 2022-01-16 14:30:49 +08:00
GyDi
1a5d9f7dad
chore: enhance ci 2022-01-16 14:27:41 +08:00
GyDi
a8425862f0
feat: rename page 2022-01-16 03:25:50 +08:00
GyDi
a3a3db6abb
refactor: setting page 2022-01-16 03:22:37 +08:00
GyDi
d6c3bc57c0
feat: refactor and adjust ui 2022-01-16 03:11:07 +08:00
GyDi
59c09f90f9
feat: rm some commands 2022-01-16 03:09:36 +08:00
GyDi
d982b83e14
feat: change type 2022-01-15 21:58:13 +08:00
GyDi
cc0e930d34
feat: supports auto launch on macos and windows 2022-01-15 21:55:05 +08:00
GyDi
3c3d77fbea
chore: add auto-launch 2022-01-15 21:00:19 +08:00
GyDi
da7453fdbf v0.0.10 2022-01-13 02:53:29 +08:00
GyDi
5fcd25506e feat: adjust proxy page 2022-01-13 02:51:30 +08:00
GyDi
4979a472de feat: press esc hide the window 2022-01-13 02:11:50 +08:00
GyDi
4e8d4f4591 fix: version update error 2022-01-12 22:19:44 +08:00
GyDi
0f5d2b15e0 v0.0.9 2022-01-12 02:55:07 +08:00
GyDi
7fc9631434 feat: show system proxy info 2022-01-12 02:54:50 +08:00
GyDi
df5953dd7b feat: support blur window 2022-01-12 02:27:29 +08:00
GyDi
8f5b2b4a0e chore: add macos startup todo 2022-01-11 23:14:43 +08:00
GyDi
43c63ffa70 chore: cargo update 2022-01-11 23:13:02 +08:00
GyDi
6779bc7459 v0.0.8 2022-01-11 02:27:04 +08:00
GyDi
664be2d0ba chore: update ci 2022-01-11 02:26:50 +08:00
GyDi
6113898b69 feat: windows support startup 2022-01-11 02:24:43 +08:00
GyDi
79aad6b5c2 feat: window self startup 2022-01-11 02:21:51 +08:00
GyDi
6da7757d36 fix: text 2022-01-10 22:57:21 +08:00
GyDi
dbb3cb8cc8 chore: ignore update json 2022-01-10 22:08:57 +08:00
GyDi
579f36a1dd fix: update profile after restart clash 2022-01-10 22:08:18 +08:00
GyDi
72c2b306cf fix: get proxies multiple times 2022-01-10 21:25:41 +08:00
GyDi
c2673cd396 docs: fix img width 2022-01-10 02:52:12 +08:00
GyDi
8eb152816a docs: update 2022-01-10 02:49:42 +08:00
GyDi
a0bc8a21a5 v0.0.7 2022-01-10 02:17:35 +08:00
GyDi
08e4d72758 feat: use tauri updater 2022-01-10 02:15:38 +08:00
GyDi
66340a27fa feat: support update checker 2022-01-10 02:05:35 +08:00
GyDi
83fe9835b6 v0.0.6 2022-01-09 21:36:43 +08:00
GyDi
4c1a50a3ca chore: ci add macos 2022-01-09 21:34:14 +08:00
GyDi
fe44a7b3bc feat: support macos proxy config 2022-01-09 21:19:35 +08:00
GyDi
e86d192db7 feat: custom window decorations 2022-01-08 22:23:48 +08:00
GyDi
ea8f1c52f9 feat: profiles add menu and delete button 2022-01-08 16:52:18 +08:00
GyDi
a4c1573c45 fix: delete profile item command 2022-01-08 14:21:12 +08:00
GyDi
182bf49ad0 chore: temp 2022-01-08 02:54:37 +08:00
GyDi
13e1ddbccd v0.0.5 2022-01-08 02:12:56 +08:00
GyDi
327b9a1757 chore: enhance ci 2022-01-08 02:12:12 +08:00
GyDi
18c48db7f7 chore: rename script 2022-01-08 01:56:28 +08:00
GyDi
b6543bd87f chore: update publish script 2022-01-08 01:51:24 +08:00
GyDi
9ad8f71d7c refactor: rename 2022-01-08 01:27:25 +08:00
GyDi
e369311fc2 refactor: impl structs methods 2022-01-07 23:29:20 +08:00
GyDi
72ff261fe3 chore: update clash version 2022-01-06 23:40:57 +08:00
GyDi
774c6f7e05 fix: initialize profiles state 2022-01-05 23:30:18 +08:00
GyDi
771af6ae08 chore: fixed tauri rev 2022-01-05 23:27:26 +08:00
GyDi
b3cd207444 feat: delay put profiles and retry 2022-01-05 02:01:32 +08:00
GyDi
03f9fa4bc2 refactor: impl as struct methods 2022-01-05 02:00:59 +08:00
GyDi
e32bfd9aab feat: Window Send and Sync 2022-01-04 21:29:04 +08:00
GyDi
7e47f8f893 chore: update tauri 2022-01-04 21:25:00 +08:00
GyDi
cb816e9653 feat: support restart sidecar tray event 2021-12-31 18:24:50 +08:00
GyDi
6b3e7cbc08 feat: prevent click same 2021-12-31 18:21:35 +08:00
GyDi
db4993ae9b fix: item header bgcolor 2021-12-31 18:19:53 +08:00
GyDi
4dc3cf6c6b feat: scroller stable 2021-12-31 18:19:17 +08:00
GyDi
2b84bbf3a8 feat: compatible with macos(wip) 2021-12-29 18:49:38 +08:00
GyDi
26ef4c9961 chore: change tauri to git repo 2021-12-29 18:47:29 +08:00
GyDi
4f56c38599 fix: null type error 2021-12-29 01:01:22 +08:00
GyDi
240f4dcfb1 chore: dev support macos 2021-12-29 01:01:09 +08:00
GyDi
ac6abd81c9 v0.0.4 2021-12-28 01:48:25 +08:00
GyDi
14bda4f3a5 feat: record selected proxy 2021-12-28 01:47:43 +08:00
GyDi
61b9670b45 feat: display version 2021-12-28 01:35:58 +08:00
GyDi
01e8db317e chore: save lock file 2021-12-28 00:14:15 +08:00
GyDi
92fc09493e chore: post version script 2021-12-27 23:07:56 +08:00
GyDi
d927209db7 v0.0.3 2021-12-27 23:06:46 +08:00
GyDi
e94007b21f v0.0.3 2021-12-27 23:06:45 +08:00
GyDi
18750f275a
chore: update version 2021-12-27 10:21:49 +08:00
GyDi
d686a853f4
chore: update version 2021-12-27 10:21:10 +08:00
85 changed files with 3849 additions and 4745 deletions

View File

@ -59,7 +59,7 @@ body:
required: true
- type: textarea
attributes:
label: 日志(勿上传日志文件,请粘贴日志内容) / Log (Do not upload the log file, paste the log content directly)
description: 请提供完整或相关部分的Debug日志请在“软件左侧菜单”->“设置”->“日志等级”调整到debugVerge错误请把“杂项设置”->“app日志等级”调整到debug并重启Verge生效。日志文件在“软件左侧菜单”->“设置”->“日志目录”下) / Please provide a complete or relevant part of the Debug log (please adjust the "Log level" to debug in "Software left menu" -> "Settings" -> "Log level". If there is a Verge error, please adjust "Miscellaneous settings" -> "app log level" to debug, and restart Verge to take effect. The log file is under "Software left menu" -> "Settings" -> "Log directory")
label: 日志 / Log
description: 请提供完整或相关部分的Debug日志请在“软件左侧菜单”->“设置”->“日志等级”调整到debugVerge错误请把“杂项设置”->“app日志等级”调整到debug/trace并重启Verge生效。日志文件在“软件左侧菜单”->“设置”->“日志目录”下) / Please provide a complete or relevant part of the Debug log (please adjust the "Log level" to debug in "Software left menu" -> "Settings" -> "Log level". If there is a Verge error, please adjust "Miscellaneous settings" -> "app log level" to trace, and restart Verge to take effect. The log file is under "Software left menu" -> "Settings" -> "Log directory")
validations:
required: true

View File

@ -25,7 +25,7 @@ jobs:
with:
fetch-depth: 2
- name: Check if version changed or src changed
- name: Check if version changed
id: check
run: |
# For manual workflow_dispatch, always run
@ -50,25 +50,8 @@ jobs:
if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then
echo "Version changed from $PREVIOUS_VERSION to $CURRENT_VERSION"
echo "should_run=true" >> $GITHUB_OUTPUT
exit 0
fi
# Check if src or src-tauri directories changed
CURRENT_SRC_HASH=$(git rev-parse HEAD:src)
PREVIOUS_SRC_HASH=$(git rev-parse HEAD~1:src 2>/dev/null || echo "")
CURRENT_TAURI_HASH=$(git rev-parse HEAD:src-tauri 2>/dev/null || echo "")
PREVIOUS_TAURI_HASH=$(git rev-parse HEAD~1:src-tauri 2>/dev/null || echo "")
echo "Current src hash: $CURRENT_SRC_HASH"
echo "Previous src hash: $PREVIOUS_SRC_HASH"
echo "Current tauri hash: $CURRENT_TAURI_HASH"
echo "Previous tauri hash: $PREVIOUS_TAURI_HASH"
if [ "$CURRENT_SRC_HASH" != "$PREVIOUS_SRC_HASH" ] || [ "$CURRENT_TAURI_HASH" != "$PREVIOUS_TAURI_HASH" ]; then
echo "Source directories changed"
echo "should_run=true" >> $GITHUB_OUTPUT
else
echo "Version and source directories unchanged"
echo "Version unchanged: $CURRENT_VERSION"
echo "should_run=false" >> $GITHUB_OUTPUT
fi

View File

@ -3,12 +3,12 @@
#pnpm pretty-quick --staged
# 运行 clippy fmt
cargo fmt --manifest-path ./src-tauri/Cargo.toml
#cargo fmt --manifest-path ./src-tauri/Cargo.toml
if [ $? -ne 0 ]; then
echo "rustfmt failed to format the code. Please fix the issues and try again."
exit 1
fi
# if [ $? -ne 0 ]; then
# echo "rustfmt failed to format the code. Please fix the issues and try again."
# exit 1
# fi
#git add .

View File

@ -1,13 +1,13 @@
#!/bin/bash
# 运行 clippy
# cargo clippy --manifest-path ./src-tauri/Cargo.toml --fix
#cargo clippy --manifest-path ./src-tauri/Cargo.toml --fix
# 如果 clippy 失败,阻止 push
# if [ $? -ne 0 ]; then
#if [ $? -ne 0 ]; then
# echo "Clippy found issues in sub_crate. Please fix them before pushing."
# exit 1
# fi
#fi
# 允许 push
exit 0
exit 0

View File

@ -1,49 +1,4 @@
## v2.2.3
#### 已知问题
- 仅在Ubuntu 22.04/24.04Fedora 41 **Gnome桌面环境** 做过简单测试不保证其他其他Linux发行版可用将在未来做进一步适配和调优
- MacOS 自定义图标与速率显示推荐图标尺寸为 256x256。其他尺寸可能会导致不正常图标和速率间隙
- MacOS 下 墙贴主要为浅色Tray 图标深色时图标闪烁;彩色 Tray 速率颜色淡
- Linux 下 Clash Verge Rev 内存占用显著高于 Windows / MacOS
### 2.2.3 相对于 2.2.2
#### 修复了:
- 首页“当前代理”因为重复刷新导致的CPU占用过高的问题
- “开机自启”和“DNS覆写”开关跳动问题
- 自定义托盘图标未能应用更改
- MacOS 自定义托盘图标显示速率时图标和文本间隙过大
- MacOS 托盘速率显示不全
- Linux 在系统服务模式下无法拉起 Mihomo 内核
- 使用异步操作,避免获取系统信息和切换代理模式可能带来的崩溃
- 相同节点名称可能导致的页面渲染出错
- URL Schemes被截断的问题
- 首页流量统计卡更好的时间戳范围
- 静默启动无法触发自动轻量化计时器
#### 新增了:
- Mihomo(Meta)内核升级至 1.19.4
- Clash Verge Rev 从现在开始不再强依赖系统服务和管理权限
- 支持根据用户偏好选择Sidecar(用户空间)模式或安装服务
- 增加载入初始配置文件的错误提示,防止切换到错误的订阅配置
- 检测是否以管理员模式运行软件,如果是提示无法使用开机自启
- 代理组显示节点数量
- 统一运行模式检测支持管理员模式下开启TUN模式
- 托盘切换代理模式会根据设置自动断开之前连接
- 如订阅获取失败回退使用Clash内核代理再次尝试
#### 移除了:
- 实时保存窗口位置和大小。这个功能可能会导致窗口异常大小和位置,还需观察。
#### 优化了:
- 重构了后端内核管理逻辑,更轻量化和有效的管理内核,提高了性能和稳定性
- 前端统一刷新应用数据,优化数据获取和刷新逻辑
- 优化首页流量图表代码,调整图表文字边距
- MacOS 托盘速率更好的显示样式和更新逻辑
- 首页仅在有流量图表时显示流量图表区域
- 更新DNS默认覆写配置
- 移除测试目录,简化资源初始化逻辑
## v2.2.2
## v2.2.1
**发行代号:拓**
@ -51,24 +6,8 @@
代号释义: 本次发布在功能上的大幅扩展。新首页设计为用户带来全新交互体验DNS 覆写功能增强网络控制能力解锁测试页面助力内容访问自由度提升轻量模式提供灵活使用选择。此外macOS 应用菜单集成、sidecar 模式、诊断信息导出等新特性进一步丰富了软件的适用场景。这些新增功能显著拓宽了 Clash Verge 的功能边界,为用户提供了更强大的工具和可能性。
#### 已知问题
- 仅在Ubuntu 22.04/24.04Fedora 41 **Gnome桌面环境** 做过简单测试不保证其他其他Linux发行版可用将在未来做进一步适配和调优
### 2.2.2 相对于 2.2.1(已下架不再提供)
#### 修复了:
- 弹黑框的问题(原因是服务崩溃触发重装机制)
- MacOS进入轻量模式以后隐藏Dock图标
- 增加轻量模式缺失的tray翻译
- Linux下的窗口边框被削掉的问题
#### 新增了:
- 加强服务检测和重装逻辑
- 增强内核与服务保活机制
- 增加服务模式下的僵尸进程清理机制
- 新增当服务模式多次尝试失败后自动回退至用户空间模式
### 2.2.1 相对于 2.2.0(已下架不再提供)
#### 修复了:
2.2.1 相对于 2.2.0(已下架不在提供)
修复了:
1. **首页**
- 修复 Direct 模式首页无法渲染
- 修复 首页启用轻量模式导致 ClashVergeRev 从托盘退出
@ -84,7 +23,7 @@
4. **轻量模式**
- 修复 MacOS 轻量模式下 Dock 栏图标无法隐藏。
#### 新增了:
新增了:
1. **首页**
- 首页文本过长自动截断
2. **轻量模式**
@ -98,7 +37,7 @@
---
## 2.2.0(已下架不再提供)
## v2.2.0(已下架不在提供)
#### 新增功能
1. **首页**
@ -139,7 +78,6 @@
- 修复 macOS tray图标错位到左上角的问题。
- 修复 Windows/Linux 运行时崩溃。
- 修复 Win10 阴影和边框问题。
- 修复 升级或重装后开机自启状态检测和同步问题。
2. **构建**
- 修复构建失败问题。
@ -177,14 +115,14 @@
感谢 Tychristine 对社区群组管理做出的重大贡献!
##### 2.1.2相对2.1.1(已下架不提供)更新了:
##### 2.1.2相对2.1.1(已下架不提供)更新了:
- 无法更新和签名验证失败的问题(该死的CDN缓存)
- 设置菜单区分Verge基本设置和高级设置
- 增加v2 Updater的更多功能和权限
- 退出Verge后Tun代理状态仍保留的问题
##### 2.1.1相对2.1.0(已下架不提供)更新了:
##### 2.1.1相对2.1.0(已下架不提供)更新了:
- 检测所需的Clash Verge Service版本杀毒软件误报可能与此有关因为检测和安装新版本Service需管理员权限
- MacOS下支持彩色托盘图标和更好速率显示感谢Tunglies

View File

@ -1,6 +1,6 @@
{
"name": "clash-verge",
"version": "2.2.3",
"version": "2.2.1",
"license": "GPL-3.0-only",
"scripts": {
"dev": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev -- --profile fast-dev",
@ -17,10 +17,9 @@
"portable": "node scripts/portable.mjs",
"portable-fixed-webview2": "node scripts/portable-fixed-webview2.mjs",
"fix-alpha-version": "node scripts/fix-alpha_version.mjs",
"release-version": "node scripts/release_version.mjs",
"release-alpha-version": "node scripts/release-alpha_version.mjs",
"prepare": "husky",
"clippy": "cargo clippy --manifest-path ./src-tauri/Cargo.toml"
"clean": "cd ./src-tauri && cargo clean && cd -"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",

View File

@ -1,197 +0,0 @@
import fs from "fs/promises";
import path from "path";
import { program } from "commander";
/**
* 验证版本号格式
* @param {string} version
* @returns {boolean}
*/
function isValidVersion(version) {
return /^v?\d+\.\d+\.\d+(-(alpha|beta|rc)(\.\d+)?)?$/i.test(version);
}
/**
* 标准化版本号确保v前缀可选
* @param {string} version
* @returns {string}
*/
function normalizeVersion(version) {
return version.startsWith("v") ? version : `v${version}`;
}
/**
* 更新 package.json 文件中的版本号
* @param {string} newVersion 新版本号
*/
async function updatePackageVersion(newVersion) {
const _dirname = process.cwd();
const packageJsonPath = path.join(_dirname, "package.json");
try {
const data = await fs.readFile(packageJsonPath, "utf8");
const packageJson = JSON.parse(data);
console.log(
"[INFO]: Current package.json version is: ",
packageJson.version,
);
packageJson.version = newVersion.startsWith("v")
? newVersion.slice(1)
: newVersion;
await fs.writeFile(
packageJsonPath,
JSON.stringify(packageJson, null, 2),
"utf8",
);
console.log(
`[INFO]: package.json version updated to: ${packageJson.version}`,
);
return packageJson.version;
} catch (error) {
console.error("Error updating package.json version:", error);
throw error;
}
}
/**
* 更新 Cargo.toml 文件中的版本号
* @param {string} newVersion 新版本号
*/
async function updateCargoVersion(newVersion) {
const _dirname = process.cwd();
const cargoTomlPath = path.join(_dirname, "src-tauri", "Cargo.toml");
try {
const data = await fs.readFile(cargoTomlPath, "utf8");
const lines = data.split("\n");
const versionWithoutV = newVersion.startsWith("v")
? newVersion.slice(1)
: newVersion;
const updatedLines = lines.map((line) => {
if (line.trim().startsWith("version =")) {
return line.replace(
/version\s*=\s*"[^"]+"/,
`version = "${versionWithoutV}"`,
);
}
return line;
});
await fs.writeFile(cargoTomlPath, updatedLines.join("\n"), "utf8");
console.log(`[INFO]: Cargo.toml version updated to: ${versionWithoutV}`);
} catch (error) {
console.error("Error updating Cargo.toml version:", error);
throw error;
}
}
/**
* 更新 tauri.conf.json 文件中的版本号
* @param {string} newVersion 新版本号
*/
async function updateTauriConfigVersion(newVersion) {
const _dirname = process.cwd();
const tauriConfigPath = path.join(_dirname, "src-tauri", "tauri.conf.json");
try {
const data = await fs.readFile(tauriConfigPath, "utf8");
const tauriConfig = JSON.parse(data);
const versionWithoutV = newVersion.startsWith("v")
? newVersion.slice(1)
: newVersion;
console.log(
"[INFO]: Current tauri.conf.json version is: ",
tauriConfig.version,
);
tauriConfig.version = versionWithoutV;
await fs.writeFile(
tauriConfigPath,
JSON.stringify(tauriConfig, null, 2),
"utf8",
);
console.log(
`[INFO]: tauri.conf.json version updated to: ${versionWithoutV}`,
);
} catch (error) {
console.error("Error updating tauri.conf.json version:", error);
throw error;
}
}
/**
* 获取当前版本号从package.json
*/
async function getCurrentVersion() {
const _dirname = process.cwd();
const packageJsonPath = path.join(_dirname, "package.json");
try {
const data = await fs.readFile(packageJsonPath, "utf8");
const packageJson = JSON.parse(data);
return packageJson.version;
} catch (error) {
console.error("Error getting current version:", error);
throw error;
}
}
/**
* 主函数更新所有文件的版本号
* @param {string} versionArg 版本参数可以是标签或完整版本号
*/
async function main(versionArg) {
if (!versionArg) {
console.error("Error: Version argument is required");
process.exit(1);
}
try {
let newVersion;
const validTags = ["alpha", "beta", "rc"];
// 判断参数是标签还是完整版本号
if (validTags.includes(versionArg.toLowerCase())) {
// 标签模式:在当前版本基础上添加标签
const currentVersion = await getCurrentVersion();
const baseVersion = currentVersion.replace(
/-(alpha|beta|rc)(\.\d+)?$/i,
"",
);
newVersion = `${baseVersion}-${versionArg.toLowerCase()}`;
} else {
// 完整版本号模式
if (!isValidVersion(versionArg)) {
console.error(
"Error: Invalid version format. Expected format: vX.X.X or vX.X.X-tag (e.g. v2.2.3 or v2.2.3-alpha)",
);
process.exit(1);
}
newVersion = normalizeVersion(versionArg);
}
console.log(`[INFO]: Updating versions to: ${newVersion}`);
await updatePackageVersion(newVersion);
await updateCargoVersion(newVersion);
await updateTauriConfigVersion(newVersion);
console.log("[SUCCESS]: All version updates completed successfully!");
} catch (error) {
console.error("[ERROR]: Failed to update versions:", error);
process.exit(1);
}
}
// Example:
// pnpm release-version 2.2.3-alpha
// 设置命令行界面
program
.name("pnpm release-version")
.description(
"Update project version numbers. Can add tag (alpha/beta/rc) or set full version (e.g. v2.2.3 or v2.2.3-alpha)",
)
.argument(
"<version>",
"version tag (alpha/beta/rc) or full version (e.g. v2.2.3 or v2.2.3-alpha)",
)
.action(main)
.parse(process.argv);

View File

@ -8,8 +8,8 @@ const UPDATE_JSON_FILE = "update.json";
const UPDATE_JSON_PROXY = "update-proxy.json";
// Add alpha update JSON filenames
const ALPHA_TAG_NAME = "updater-alpha";
const ALPHA_UPDATE_JSON_FILE = "update.json";
const ALPHA_UPDATE_JSON_PROXY = "update-proxy.json";
const ALPHA_UPDATE_JSON_FILE = "update-alpha.json";
const ALPHA_UPDATE_JSON_PROXY = "update-alpha-proxy.json";
/// generate update.json
/// upload to update tag's release asset
@ -78,235 +78,224 @@ async function resolveUpdater() {
async function processRelease(github, options, tag, isAlpha) {
if (!tag) return;
const { data: release } = await github.rest.repos.getReleaseByTag({
...options,
tag: tag.name,
});
const updateData = {
name: tag.name,
notes: await resolveUpdateLog(tag.name).catch(() =>
resolveUpdateLogDefault().catch(() => "No changelog available"),
),
pub_date: new Date().toISOString(),
platforms: {
win64: { signature: "", url: "" }, // compatible with older formats
linux: { signature: "", url: "" }, // compatible with older formats
darwin: { signature: "", url: "" }, // compatible with older formats
"darwin-aarch64": { signature: "", url: "" },
"darwin-intel": { signature: "", url: "" },
"darwin-x86_64": { signature: "", url: "" },
"linux-x86_64": { signature: "", url: "" },
"linux-x86": { signature: "", url: "" },
"linux-i686": { signature: "", url: "" },
"linux-aarch64": { signature: "", url: "" },
"linux-armv7": { signature: "", url: "" },
"windows-x86_64": { signature: "", url: "" },
"windows-aarch64": { signature: "", url: "" },
"windows-x86": { signature: "", url: "" },
"windows-i686": { signature: "", url: "" },
},
};
const promises = release.assets.map(async (asset) => {
const { name, browser_download_url } = asset;
// Process all the platform URL and signature data
// win64 url
if (name.endsWith("x64-setup.exe")) {
updateData.platforms.win64.url = browser_download_url;
updateData.platforms["windows-x86_64"].url = browser_download_url;
}
// win64 signature
if (name.endsWith("x64-setup.exe.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms.win64.signature = sig;
updateData.platforms["windows-x86_64"].signature = sig;
}
// win32 url
if (name.endsWith("x86-setup.exe")) {
updateData.platforms["windows-x86"].url = browser_download_url;
updateData.platforms["windows-i686"].url = browser_download_url;
}
// win32 signature
if (name.endsWith("x86-setup.exe.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["windows-x86"].signature = sig;
updateData.platforms["windows-i686"].signature = sig;
}
// win arm url
if (name.endsWith("arm64-setup.exe")) {
updateData.platforms["windows-aarch64"].url = browser_download_url;
}
// win arm signature
if (name.endsWith("arm64-setup.exe.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["windows-aarch64"].signature = sig;
}
// darwin url (intel)
if (name.endsWith(".app.tar.gz") && !name.includes("aarch")) {
updateData.platforms.darwin.url = browser_download_url;
updateData.platforms["darwin-intel"].url = browser_download_url;
updateData.platforms["darwin-x86_64"].url = browser_download_url;
}
// darwin signature (intel)
if (name.endsWith(".app.tar.gz.sig") && !name.includes("aarch")) {
const sig = await getSignature(browser_download_url);
updateData.platforms.darwin.signature = sig;
updateData.platforms["darwin-intel"].signature = sig;
updateData.platforms["darwin-x86_64"].signature = sig;
}
// darwin url (aarch)
if (name.endsWith("aarch64.app.tar.gz")) {
updateData.platforms["darwin-aarch64"].url = browser_download_url;
// 使linux可以检查更新
updateData.platforms.linux.url = browser_download_url;
updateData.platforms["linux-x86_64"].url = browser_download_url;
updateData.platforms["linux-x86"].url = browser_download_url;
updateData.platforms["linux-i686"].url = browser_download_url;
updateData.platforms["linux-aarch64"].url = browser_download_url;
updateData.platforms["linux-armv7"].url = browser_download_url;
}
// darwin signature (aarch)
if (name.endsWith("aarch64.app.tar.gz.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["darwin-aarch64"].signature = sig;
updateData.platforms.linux.signature = sig;
updateData.platforms["linux-x86_64"].signature = sig;
updateData.platforms["linux-x86"].url = browser_download_url;
updateData.platforms["linux-i686"].url = browser_download_url;
updateData.platforms["linux-aarch64"].signature = sig;
updateData.platforms["linux-armv7"].signature = sig;
}
});
await Promise.allSettled(promises);
console.log(updateData);
// maybe should test the signature as well
// delete the null field
Object.entries(updateData.platforms).forEach(([key, value]) => {
if (!value.url) {
console.log(`[Error]: failed to parse release for "${key}"`);
delete updateData.platforms[key];
}
});
// Generate a proxy update file for accelerated GitHub resources
const updateDataNew = JSON.parse(JSON.stringify(updateData));
Object.entries(updateDataNew.platforms).forEach(([key, value]) => {
if (value.url) {
updateDataNew.platforms[key].url =
"https://download.clashverge.dev/" + value.url;
} else {
console.log(`[Error]: updateDataNew.platforms.${key} is null`);
}
});
// Get the appropriate updater release based on isAlpha flag
const releaseTag = isAlpha ? ALPHA_TAG_NAME : UPDATE_TAG_NAME;
console.log(
`Processing ${isAlpha ? "alpha" : "stable"} release:`,
releaseTag,
);
try {
const { data: release } = await github.rest.repos.getReleaseByTag({
...options,
tag: tag.name,
});
const updateData = {
name: tag.name,
notes: await resolveUpdateLog(tag.name).catch(() =>
resolveUpdateLogDefault().catch(() => "No changelog available"),
),
pub_date: new Date().toISOString(),
platforms: {
win64: { signature: "", url: "" }, // compatible with older formats
linux: { signature: "", url: "" }, // compatible with older formats
darwin: { signature: "", url: "" }, // compatible with older formats
"darwin-aarch64": { signature: "", url: "" },
"darwin-intel": { signature: "", url: "" },
"darwin-x86_64": { signature: "", url: "" },
"linux-x86_64": { signature: "", url: "" },
"linux-x86": { signature: "", url: "" },
"linux-i686": { signature: "", url: "" },
"linux-aarch64": { signature: "", url: "" },
"linux-armv7": { signature: "", url: "" },
"windows-x86_64": { signature: "", url: "" },
"windows-aarch64": { signature: "", url: "" },
"windows-x86": { signature: "", url: "" },
"windows-i686": { signature: "", url: "" },
},
};
const promises = release.assets.map(async (asset) => {
const { name, browser_download_url } = asset;
// Process all the platform URL and signature data
// win64 url
if (name.endsWith("x64-setup.exe")) {
updateData.platforms.win64.url = browser_download_url;
updateData.platforms["windows-x86_64"].url = browser_download_url;
}
// win64 signature
if (name.endsWith("x64-setup.exe.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms.win64.signature = sig;
updateData.platforms["windows-x86_64"].signature = sig;
}
// win32 url
if (name.endsWith("x86-setup.exe")) {
updateData.platforms["windows-x86"].url = browser_download_url;
updateData.platforms["windows-i686"].url = browser_download_url;
}
// win32 signature
if (name.endsWith("x86-setup.exe.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["windows-x86"].signature = sig;
updateData.platforms["windows-i686"].signature = sig;
}
// win arm url
if (name.endsWith("arm64-setup.exe")) {
updateData.platforms["windows-aarch64"].url = browser_download_url;
}
// win arm signature
if (name.endsWith("arm64-setup.exe.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["windows-aarch64"].signature = sig;
}
// darwin url (intel)
if (name.endsWith(".app.tar.gz") && !name.includes("aarch")) {
updateData.platforms.darwin.url = browser_download_url;
updateData.platforms["darwin-intel"].url = browser_download_url;
updateData.platforms["darwin-x86_64"].url = browser_download_url;
}
// darwin signature (intel)
if (name.endsWith(".app.tar.gz.sig") && !name.includes("aarch")) {
const sig = await getSignature(browser_download_url);
updateData.platforms.darwin.signature = sig;
updateData.platforms["darwin-intel"].signature = sig;
updateData.platforms["darwin-x86_64"].signature = sig;
}
// darwin url (aarch)
if (name.endsWith("aarch64.app.tar.gz")) {
updateData.platforms["darwin-aarch64"].url = browser_download_url;
// 使linux可以检查更新
updateData.platforms.linux.url = browser_download_url;
updateData.platforms["linux-x86_64"].url = browser_download_url;
updateData.platforms["linux-x86"].url = browser_download_url;
updateData.platforms["linux-i686"].url = browser_download_url;
updateData.platforms["linux-aarch64"].url = browser_download_url;
updateData.platforms["linux-armv7"].url = browser_download_url;
}
// darwin signature (aarch)
if (name.endsWith("aarch64.app.tar.gz.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["darwin-aarch64"].signature = sig;
updateData.platforms.linux.signature = sig;
updateData.platforms["linux-x86_64"].signature = sig;
updateData.platforms["linux-x86"].url = browser_download_url;
updateData.platforms["linux-i686"].url = browser_download_url;
updateData.platforms["linux-aarch64"].signature = sig;
updateData.platforms["linux-armv7"].signature = sig;
}
});
await Promise.allSettled(promises);
console.log(updateData);
// maybe should test the signature as well
// delete the null field
Object.entries(updateData.platforms).forEach(([key, value]) => {
if (!value.url) {
console.log(`[Error]: failed to parse release for "${key}"`);
delete updateData.platforms[key];
}
});
// Generate a proxy update file for accelerated GitHub resources
const updateDataNew = JSON.parse(JSON.stringify(updateData));
Object.entries(updateDataNew.platforms).forEach(([key, value]) => {
if (value.url) {
updateDataNew.platforms[key].url =
"https://download.clashverge.dev/" + value.url;
} else {
console.log(`[Error]: updateDataNew.platforms.${key} is null`);
}
});
// Get the appropriate updater release based on isAlpha flag
const releaseTag = isAlpha ? ALPHA_TAG_NAME : UPDATE_TAG_NAME;
console.log(
`Processing ${isAlpha ? "alpha" : "stable"} release:`,
releaseTag,
);
let updateRelease;
try {
let updateRelease;
try {
// Try to get the existing release
const response = await github.rest.repos.getReleaseByTag({
...options,
tag: releaseTag,
});
updateRelease = response.data;
console.log(
`Found existing ${releaseTag} release with ID: ${updateRelease.id}`,
);
} catch (error) {
// If release doesn't exist, create it
if (error.status === 404) {
console.log(
`Release with tag ${releaseTag} not found, creating new release...`,
);
const createResponse = await github.rest.repos.createRelease({
...options,
tag_name: releaseTag,
name: isAlpha
? "Auto-update Alpha Channel"
: "Auto-update Stable Channel",
body: `This release contains the update information for ${isAlpha ? "alpha" : "stable"} channel.`,
prerelease: isAlpha,
});
updateRelease = createResponse.data;
console.log(
`Created new ${releaseTag} release with ID: ${updateRelease.id}`,
);
} else {
// If it's another error, throw it
throw error;
}
}
// File names based on release type
const jsonFile = isAlpha ? ALPHA_UPDATE_JSON_FILE : UPDATE_JSON_FILE;
const proxyFile = isAlpha ? ALPHA_UPDATE_JSON_PROXY : UPDATE_JSON_PROXY;
// Delete existing assets with these names
for (let asset of updateRelease.assets) {
if (asset.name === jsonFile) {
await github.rest.repos.deleteReleaseAsset({
...options,
asset_id: asset.id,
});
}
if (asset.name === proxyFile) {
await github.rest.repos
.deleteReleaseAsset({ ...options, asset_id: asset.id })
.catch(console.error); // do not break the pipeline
}
}
// Upload new assets
await github.rest.repos.uploadReleaseAsset({
// Try to get the existing release
const response = await github.rest.repos.getReleaseByTag({
...options,
release_id: updateRelease.id,
name: jsonFile,
data: JSON.stringify(updateData, null, 2),
tag: releaseTag,
});
await github.rest.repos.uploadReleaseAsset({
...options,
release_id: updateRelease.id,
name: proxyFile,
data: JSON.stringify(updateDataNew, null, 2),
});
updateRelease = response.data;
console.log(
`Successfully uploaded ${isAlpha ? "alpha" : "stable"} update files to ${releaseTag}`,
`Found existing ${releaseTag} release with ID: ${updateRelease.id}`,
);
} catch (error) {
console.error(
`Failed to process ${isAlpha ? "alpha" : "stable"} release:`,
error.message,
);
// If release doesn't exist, create it
if (error.status === 404) {
console.log(
`Release with tag ${releaseTag} not found, creating new release...`,
);
const createResponse = await github.rest.repos.createRelease({
...options,
tag_name: releaseTag,
name: isAlpha
? "Auto-update Alpha Channel"
: "Auto-update Stable Channel",
body: `This release contains the update information for ${isAlpha ? "alpha" : "stable"} channel.`,
prerelease: isAlpha,
});
updateRelease = createResponse.data;
console.log(
`Created new ${releaseTag} release with ID: ${updateRelease.id}`,
);
} else {
// If it's another error, throw it
throw error;
}
}
// File names based on release type
const jsonFile = isAlpha ? ALPHA_UPDATE_JSON_FILE : UPDATE_JSON_FILE;
const proxyFile = isAlpha ? ALPHA_UPDATE_JSON_PROXY : UPDATE_JSON_PROXY;
// Delete existing assets with these names
for (let asset of updateRelease.assets) {
if (asset.name === jsonFile) {
await github.rest.repos.deleteReleaseAsset({
...options,
asset_id: asset.id,
});
}
if (asset.name === proxyFile) {
await github.rest.repos
.deleteReleaseAsset({ ...options, asset_id: asset.id })
.catch(console.error); // do not break the pipeline
}
}
// Upload new assets
await github.rest.repos.uploadReleaseAsset({
...options,
release_id: updateRelease.id,
name: jsonFile,
data: JSON.stringify(updateData, null, 2),
});
await github.rest.repos.uploadReleaseAsset({
...options,
release_id: updateRelease.id,
name: proxyFile,
data: JSON.stringify(updateDataNew, null, 2),
});
console.log(
`Successfully uploaded ${isAlpha ? "alpha" : "stable"} update files to ${releaseTag}`,
);
} catch (error) {
if (error.status === 404) {
console.log(`Release not found for tag: ${tag.name}, skipping...`);
} else {
console.error(
`Failed to get release for tag: ${tag.name}`,
error.message,
);
}
console.error(
`Failed to process ${isAlpha ? "alpha" : "stable"} release:`,
error.message,
);
}
}

643
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package]
name = "clash-verge"
version = "2.2.3"
version = "2.2.1"
description = "clash verge"
authors = ["zzzgydi", "wonfen", "MystiPanda"]
license = "GPL-3.0-only"
@ -13,72 +13,73 @@ build = "build.rs"
identifier = "io.github.clash-verge-rev.clash-verge-rev"
[build-dependencies]
tauri-build = { version = "2.1.0", features = [] }
tauri-build = { version = "2.0.6", features = [] }
[dependencies]
warp = "0.3"
anyhow = "1.0.97"
dirs = "6.0"
open = "5.3"
open = "5.1"
log = "0.4"
dunce = "1.0"
log4rs = "1"
nanoid = "0.4"
chrono = "0.4.40"
sysinfo = "0.34"
sysinfo = "0.33.1"
boa_engine = "0.20.0"
serde_json = "1.0"
serde_yaml = "0.9"
once_cell = "1.21.3"
once_cell = "1.20.3"
port_scanner = "0.1.5"
delay_timer = "0.11.6"
parking_lot = "0.12"
percent-encoding = "2.3.1"
tokio = { version = "1.44", features = [
"rt-multi-thread",
"macros",
"time",
"sync",
] }
fs2 = "0.4.3"
window-shadows = { version = "0.2.2" }
tokio = { version = "1.43", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.12", features = ["json", "rustls-tls", "cookies"] }
regex = "1.11.1"
regex = "1.10.5"
sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs", rev = "3d748b5" }
image = "0.25.6"
image = "0.25.5"
imageproc = "0.25.0"
tauri = { version = "2.4.0", features = [
"protocol-asset",
"devtools",
"tray-icon",
"image-ico",
"image-png",
rusttype = "0.9"
tauri = { version = "2.3.1", features = [
"protocol-asset",
"devtools",
"tray-icon",
"image-ico",
"image-png",
] }
network-interface = { version = "2.0.1", features = ["serde"] }
network-interface = { version = "2.0.0", features = ["serde"] }
tauri-plugin-shell = "2.2.0"
tauri-plugin-dialog = "2.2.0"
tauri-plugin-fs = "2.2.0"
tauri-plugin-notification = "2.2.1"
tauri-plugin-process = "2.2.0"
tauri-plugin-clipboard-manager = "2.2.2"
tauri-plugin-clipboard-manager = "2.2.1"
tauri-plugin-deep-link = "2.2.0"
tauri-plugin-devtools = "2.0.0"
zip = "2.5.0"
reqwest_dav = "0.1.15"
tauri-plugin-devtools = "2.0.0-rc"
url = "2.5.4"
zip = "2.2.3"
reqwest_dav = "0.1.14"
aes-gcm = { version = "0.10.3", features = ["std"] }
base64 = "0.22.1"
getrandom = "0.3.2"
getrandom = "0.3.1"
tokio-tungstenite = "0.26.2"
futures = "0.3"
sys-locale = "0.3.2"
async-trait = "0.1.88"
sys-locale = "0.3.1"
async-trait = "0.1.87"
mihomo_api = { path = "src_crates/crate_mihomo_api" }
ab_glyph = "0.2.29"
tungstenite = "0.26.2"
libc = "0.2"
[target.'cfg(windows)'.dependencies]
runas = "=1.2.0"
deelevate = "0.2.0"
winreg = "0.55.0"
url = "2.5.4"
[target.'cfg(target_os = "linux")'.dependencies]
users = "0.11.0"
@ -86,8 +87,9 @@ users = "0.11.0"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-autostart = "2.2.0"
tauri-plugin-global-shortcut = "2.2.0"
tauri-plugin-updater = "2.6.1"
tauri-plugin-updater = "2.5.1"
tauri-plugin-window-state = "2.2.1"
#openssl
[features]
default = ["custom-protocol"]
@ -105,33 +107,31 @@ strip = true
incremental = true
[profile.fast-release]
inherits = "release" # 继承 release 的配置
panic = "abort" # 与 release 相同
codegen-units = 256 # 增加编译单元,提升编译速度
lto = false # 禁用 LTO提升编译速度
opt-level = 0 # 禁用优化,大幅提升编译速度
debug = true # 保留调试信息
strip = false # 不剥离符号,保留调试信息
inherits = "release" # 继承 release 的配置
panic = "abort" # 与 release 相同
codegen-units = 256 # 增加编译单元,提升编译速度
lto = false # 禁用 LTO提升编译速度
opt-level = 0 # 禁用优化,大幅提升编译速度
debug = true # 保留调试信息
strip = false # 不剥离符号,保留调试信息
[profile.fast-dev]
inherits = "dev" # 继承 dev 的配置
codegen-units = 256 # 增加编译单元,提升编译速度
opt-level = 0 # 禁用优化,进一步提升编译速度
incremental = true # 启用增量编译
debug = true # 保留调试信息
strip = false # 不剥离符号,保留调试信息
inherits = "dev" # 继承 dev 的配置
codegen-units = 256 # 增加编译单元,提升编译速度
opt-level = 0 # 禁用优化,进一步提升编译速度
incremental = true # 启用增量编译
debug = true # 保留调试信息
strip = false # 不剥离符号,保留调试信息
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[dev-dependencies]
tempfile = "3.19.1"
env_logger = "0.11.0"
mockito = "1.7.0"
tempfile = "3.17.1"
[workspace]
members = ["src_crates/crate_mihomo_api"]
# [patch.crates-io]
# bitflags = { git = "https://github.com/bitflags/bitflags", rev = "2.9.0" }
# zerocopy = { git = "https://github.com/google/zerocopy", rev = "v0.8.24" }
# tungstenite = { git = "https://github.com/snapview/tungstenite-rs", rev = "v0.26.2" }
members = [
"src_crates/crate_mihomo_api",]

View File

@ -68,6 +68,7 @@
"shell:allow-spawn",
"shell:allow-stdin-write",
"dialog:allow-open",
"notification:default",
"global-shortcut:allow-is-registered",
"global-shortcut:allow-register",
"global-shortcut:allow-register-all",
@ -78,6 +79,7 @@
"clipboard-manager:allow-read-text",
"clipboard-manager:allow-write-text",
"shell:default",
"dialog:default"
"dialog:default",
"notification:default"
]
}

View File

@ -1,9 +1,5 @@
use super::CmdResult;
use crate::{
feat, logging,
utils::{dirs, logging::Type},
wrap_err,
};
use crate::{feat, utils::dirs, wrap_err};
use tauri::Manager;
/// 打开应用程序所在目录
@ -198,14 +194,7 @@ pub fn copy_icon_file(path: String, icon_info: IconInfo) -> CmdResult<String> {
)
.unwrap_or_default();
}
logging!(
info,
Type::Cmd,
true,
"Copying icon file path: {:?} -> file dist: {:?}",
path,
dest_path
);
match fs::copy(file_path, &dest_path) {
Ok(_) => Ok(dest_path.to_string_lossy().to_string()),
Err(err) => Err(err.to_string()),

View File

@ -0,0 +1,8 @@
use super::CmdResult;
use crate::{core::CoreManager, wrap_err};
/// 修复系统服务
#[tauri::command]
pub async fn repair_service() -> CmdResult {
wrap_err!(CoreManager::global().repair_service().await)
}

View File

@ -2,9 +2,11 @@ use chrono::Local;
use regex::Regex;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, sync::Arc};
use std::collections::HashMap;
use std::sync::Arc;
use tauri::command;
use tokio::{sync::Mutex, task::JoinSet};
use tokio::sync::Mutex;
use tokio::task::JoinSet;
// 定义解锁测试项目的结构
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -462,11 +464,9 @@ async fn check_netflix(client: &Client) -> UnlockItem {
let url2 = "https://www.netflix.com/title/70143836"; // Breaking Bad
// 创建简单的请求(不添加太多头部信息)
let result1 = client
.get(url1)
let result1 = client.get(url1)
.timeout(std::time::Duration::from_secs(30))
.send()
.await;
.send().await;
// 检查连接失败情况
if let Err(e) = &result1 {
@ -480,11 +480,9 @@ async fn check_netflix(client: &Client) -> UnlockItem {
}
// 如果第一个请求成功,尝试第二个请求
let result2 = client
.get(url2)
let result2 = client.get(url2)
.timeout(std::time::Duration::from_secs(30))
.send()
.await;
.send().await;
if let Err(e) = &result2 {
eprintln!("Netflix请求错误: {}", e);
@ -523,8 +521,7 @@ async fn check_netflix(client: &Client) -> UnlockItem {
// 成功解锁,尝试获取地区信息
// 使用Netflix测试内容获取区域
let test_url = "https://www.netflix.com/title/80018499";
match client
.get(test_url)
match client.get(test_url)
.timeout(std::time::Duration::from_secs(30))
.send()
.await
@ -564,7 +561,7 @@ async fn check_netflix(client: &Client) -> UnlockItem {
region: None,
check_time: Some(get_local_date_string()),
}
}
},
}
} else {
// 其他未知错误状态
@ -581,13 +578,11 @@ async fn check_netflix(client: &Client) -> UnlockItem {
async fn check_netflix_cdn(client: &Client) -> UnlockItem {
// Fast.com API URL
let url = "https://api.fast.com/netflix/speedtest/v2?https=true&token=YXNkZmFzZGxmbnNkYWZoYXNkZmhrYWxm&urlCount=5";
let result = client
.get(url)
let result = client.get(url)
.timeout(std::time::Duration::from_secs(30))
.send()
.await;
.send().await;
match result {
Ok(response) => {
// 检查状态码
@ -599,7 +594,7 @@ async fn check_netflix_cdn(client: &Client) -> UnlockItem {
check_time: Some(get_local_date_string()),
};
}
// 尝试解析响应
match response.json::<serde_json::Value>().await {
Ok(data) => {
@ -607,9 +602,7 @@ async fn check_netflix_cdn(client: &Client) -> UnlockItem {
if let Some(targets) = data.get("targets").and_then(|t| t.as_array()) {
if !targets.is_empty() {
if let Some(location) = targets[0].get("location") {
if let Some(country) =
location.get("country").and_then(|c| c.as_str())
{
if let Some(country) = location.get("country").and_then(|c| c.as_str()) {
let emoji = country_code_to_emoji(country);
return UnlockItem {
name: "Netflix".to_string(),
@ -621,14 +614,14 @@ async fn check_netflix_cdn(client: &Client) -> UnlockItem {
}
}
}
// 如果无法解析区域信息
UnlockItem {
return UnlockItem {
name: "Netflix".to_string(),
status: "Unknown".to_string(),
region: None,
check_time: Some(get_local_date_string()),
}
};
}
Err(e) => {
eprintln!("解析Fast.com API响应失败: {}", e);

View File

@ -6,33 +6,33 @@ pub type CmdResult<T = ()> = Result<T, String>;
// Command modules
pub mod app;
pub mod clash;
pub mod lightweight;
pub mod core;
pub mod media_unlock_checker;
pub mod network;
pub mod profile;
pub mod proxy;
pub mod runtime;
pub mod save_profile;
pub mod service;
pub mod system;
pub mod uwp;
pub mod validate;
pub mod verge;
pub mod webdav;
pub mod lighteweight;
// Re-export all command functions for backwards compatibility
pub use app::*;
pub use clash::*;
pub use lightweight::*;
pub use core::*;
pub use media_unlock_checker::*;
pub use network::*;
pub use profile::*;
pub use proxy::*;
pub use runtime::*;
pub use save_profile::*;
pub use service::*;
pub use system::*;
pub use uwp::*;
pub use validate::*;
pub use verge::*;
pub use webdav::*;
pub use lighteweight::*;

View File

@ -1,25 +1,40 @@
use super::CmdResult;
use crate::{
config::{Config, IProfiles, PrfItem, PrfOption},
core::{handle, tray::Tray, CoreManager},
feat, logging, ret_err,
utils::{dirs, help, logging::Type},
config::*,
core::*,
feat, log_err, ret_err,
utils::{dirs, help},
wrap_err,
};
/// 获取配置文件列表
#[tauri::command]
pub fn get_profiles() -> CmdResult<IProfiles> {
let _ = Tray::global().update_menu();
let _ = tray::Tray::global().update_menu();
Ok(Config::profiles().data().clone())
}
/// 增强配置文件
#[tauri::command]
pub async fn enhance_profiles() -> CmdResult {
wrap_err!(feat::enhance_profiles().await)?;
handle::Handle::refresh_clash();
Ok(())
match CoreManager::global().update_config().await {
Ok((true, _)) => {
println!("[enhance_profiles] 配置更新成功");
log_err!(tray::Tray::global().update_tooltip());
handle::Handle::refresh_clash();
Ok(())
}
Ok((false, error_msg)) => {
println!("[enhance_profiles] 配置验证失败: {}", error_msg);
handle::Handle::notice_message("config_validate::error", &error_msg);
Ok(())
}
Err(e) => {
println!("[enhance_profiles] 更新过程发生错误: {}", e);
handle::Handle::notice_message("config_validate::process_terminated", e.to_string());
Ok(())
}
}
}
/// 导入配置文件
@ -62,113 +77,33 @@ pub async fn delete_profile(index: String) -> CmdResult {
/// 修改profiles的配置
#[tauri::command]
pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
logging!(info, Type::Cmd, true, "开始修改配置文件");
println!("[cmd配置patch] 开始修改配置文件");
// 保存当前配置,以便在验证失败时恢复
let current_profile = Config::profiles().latest().current.clone();
logging!(info, Type::Cmd, true, "当前配置: {:?}", current_profile);
// 如果要切换配置,先检查目标配置文件是否有语法错误
if let Some(new_profile) = profiles.current.as_ref() {
if current_profile.as_ref() != Some(new_profile) {
logging!(info, Type::Cmd, true, "正在切换到新配置: {}", new_profile);
// 获取目标配置文件路径
let profiles_config = Config::profiles();
let profiles_data = profiles_config.latest();
let config_file_result = match profiles_data.get_item(new_profile) {
Ok(item) => {
if let Some(file) = &item.file {
let path = dirs::app_profiles_dir().map(|dir| dir.join(file));
path.ok()
} else {
None
}
}
Err(e) => {
logging!(error, Type::Cmd, true, "获取目标配置信息失败: {}", e);
None
}
};
// 如果获取到文件路径检查YAML语法
if let Some(file_path) = config_file_result {
if !file_path.exists() {
logging!(
error,
Type::Cmd,
true,
"目标配置文件不存在: {}",
file_path.display()
);
handle::Handle::notice_message(
"config_validate::file_not_found",
format!("{}", file_path.display()),
);
return Ok(false);
}
match std::fs::read_to_string(&file_path) {
Ok(content) => match serde_yaml::from_str::<serde_yaml::Value>(&content) {
Ok(_) => {
logging!(info, Type::Cmd, true, "目标配置文件语法正确");
}
Err(err) => {
let error_msg = format!(" {}", err);
logging!(
error,
Type::Cmd,
true,
"目标配置文件存在YAML语法错误:{}",
error_msg
);
handle::Handle::notice_message(
"config_validate::yaml_syntax_error",
&error_msg,
);
return Ok(false);
}
},
Err(err) => {
let error_msg = format!("无法读取目标配置文件: {}", err);
logging!(error, Type::Cmd, true, "{}", error_msg);
handle::Handle::notice_message(
"config_validate::file_read_error",
&error_msg,
);
return Ok(false);
}
}
}
}
}
println!("[cmd配置patch] 当前配置: {:?}", current_profile);
// 更新profiles配置
logging!(info, Type::Cmd, true, "正在更新配置草稿");
let _ = Config::profiles().draft().patch_config(profiles);
println!("[cmd配置patch] 正在更新配置草稿");
wrap_err!({ Config::profiles().draft().patch_config(profiles) })?;
// 更新配置并进行验证
match CoreManager::global().update_config().await {
Ok((true, _)) => {
logging!(info, Type::Cmd, true, "配置更新成功");
println!("[cmd配置patch] 配置更新成功");
handle::Handle::refresh_clash();
let _ = Tray::global().update_tooltip();
let _ = tray::Tray::global().update_tooltip();
Config::profiles().apply();
wrap_err!(Config::profiles().data().save_file())?;
Ok(true)
}
Ok((false, error_msg)) => {
logging!(warn, Type::Cmd, true, "配置验证失败: {}", error_msg);
println!("[cmd配置patch] 配置验证失败: {}", error_msg);
Config::profiles().discard();
// 如果验证失败,恢复到之前的配置
if let Some(prev_profile) = current_profile {
logging!(
info,
Type::Cmd,
true,
"尝试恢复到之前的配置: {}",
prev_profile
);
println!("[cmd配置patch] 尝试恢复到之前的配置: {}", prev_profile);
let restore_profiles = IProfiles {
current: Some(prev_profile),
items: None,
@ -177,7 +112,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
wrap_err!({ Config::profiles().draft().patch_config(restore_profiles) })?;
Config::profiles().apply();
wrap_err!(Config::profiles().data().save_file())?;
logging!(info, Type::Cmd, true, "成功恢复到之前的配置");
println!("[cmd配置patch] 成功恢复到之前的配置");
}
// 发送验证错误通知
@ -185,7 +120,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
Ok(false)
}
Err(e) => {
logging!(warn, Type::Cmd, true, "更新过程发生错误: {}", e);
println!("[cmd配置patch] 更新过程发生错误: {}", e);
Config::profiles().discard();
handle::Handle::notice_message("config_validate::boot_error", e.to_string());
Ok(false)
@ -199,8 +134,6 @@ pub async fn patch_profiles_config_by_profile_index(
_app_handle: tauri::AppHandle,
profile_index: String,
) -> CmdResult<bool> {
logging!(info, Type::Cmd, true, "切换配置到: {}", profile_index);
let profiles = IProfiles {
current: Some(profile_index),
items: None,

View File

@ -1,24 +1,26 @@
use super::CmdResult;
use crate::module::mihomo::MihomoManager;
use crate::{core::CoreManager, module::mihomo::MihomoManager};
#[tauri::command]
pub async fn get_proxies() -> CmdResult<serde_json::Value> {
CoreManager::global().ensure_running_core().await;
let mannager = MihomoManager::global();
mannager
let proxies = mannager
.refresh_proxies()
.await
.map(|_| mannager.get_proxies())
.or_else(|_| Ok(mannager.get_proxies()))
.or_else(|_| Ok(mannager.get_proxies()));
proxies
}
#[tauri::command]
pub async fn get_providers_proxies() -> CmdResult<serde_json::Value> {
CoreManager::global().ensure_running_core().await;
let mannager = MihomoManager::global();
mannager
let providers = mannager
.refresh_providers_proxies()
.await
.map(|_| mannager.get_providers_proxies())
.or_else(|_| Ok(mannager.get_providers_proxies()))
.or_else(|_| Ok(mannager.get_providers_proxies()));
providers
}

View File

@ -1,40 +0,0 @@
use super::CmdResult;
use crate::{
core::{service, CoreManager},
utils::i18n::t,
};
async fn execute_service_operation(
service_op: impl std::future::Future<Output = Result<(), impl ToString + std::fmt::Debug>>,
op_type: &str,
) -> CmdResult {
if service_op.await.is_err() {
let emsg = format!("{} {} failed", op_type, "Service");
return Err(t(emsg.as_str()));
}
if CoreManager::global().restart_core().await.is_err() {
let emsg = format!("{} {} failed", "Restart", "Core");
return Err(t(emsg.as_str()));
}
Ok(())
}
#[tauri::command]
pub async fn install_service() -> CmdResult {
execute_service_operation(service::install_service(), "Install").await
}
#[tauri::command]
pub async fn uninstall_service() -> CmdResult {
execute_service_operation(service::uninstall_service(), "Uninstall").await
}
#[tauri::command]
pub async fn reinstall_service() -> CmdResult {
execute_service_operation(service::reinstall_service(), "Reinstall").await
}
#[tauri::command]
pub async fn repair_service() -> CmdResult {
execute_service_operation(service::force_reinstall_service(), "Repair").await
}

View File

@ -1,7 +1,8 @@
use super::CmdResult;
use crate::{
core::{handle, CoreManager},
core::{self, handle, service, CoreManager},
module::sysinfo::PlatformSpecification,
wrap_err,
};
use once_cell::sync::Lazy;
use std::{
@ -23,7 +24,7 @@ static APP_START_TIME: Lazy<AtomicI64> = Lazy::new(|| {
#[tauri::command]
pub async fn export_diagnostic_info() -> CmdResult<()> {
let sysinfo = PlatformSpecification::new_async().await;
let sysinfo = PlatformSpecification::new();
let info = format!("{:?}", sysinfo);
let app_handle = handle::Handle::global().app_handle().unwrap();
@ -36,7 +37,7 @@ pub async fn export_diagnostic_info() -> CmdResult<()> {
#[tauri::command]
pub async fn get_system_info() -> CmdResult<String> {
let sysinfo = PlatformSpecification::new_async().await;
let sysinfo = PlatformSpecification::new();
let info = format!("{:?}", sysinfo);
Ok(info)
}
@ -44,7 +45,17 @@ pub async fn get_system_info() -> CmdResult<String> {
/// 获取当前内核运行模式
#[tauri::command]
pub async fn get_running_mode() -> Result<String, String> {
Ok(CoreManager::global().get_running_mode().await.to_string())
match CoreManager::global().get_running_mode().await {
core::RunningMode::Service => Ok("service".to_string()),
core::RunningMode::Sidecar => Ok("sidecar".to_string()),
core::RunningMode::NotRunning => Ok("not_running".to_string()),
}
}
/// 安装/重装系统服务
#[tauri::command]
pub async fn install_service() -> CmdResult {
wrap_err!(service::reinstall_service().await)
}
/// 获取应用的运行时间(毫秒)
@ -58,37 +69,3 @@ pub fn get_app_uptime() -> CmdResult<i64> {
Ok(now - start_time)
}
/// 检查应用是否以管理员身份运行
#[tauri::command]
#[cfg(target_os = "windows")]
pub fn is_admin() -> CmdResult<bool> {
use deelevate::{PrivilegeLevel, Token};
let result = Token::with_current_process()
.and_then(|token| token.privilege_level())
.map(|level| level != PrivilegeLevel::NotPrivileged)
.unwrap_or(false);
Ok(result)
}
/// 非Windows平台检测是否以管理员身份运行
#[tauri::command]
#[cfg(not(target_os = "windows"))]
pub fn is_admin() -> CmdResult<bool> {
#[cfg(target_os = "macos")]
{
Ok(unsafe { libc::geteuid() } == 0)
}
#[cfg(target_os = "linux")]
{
Ok(unsafe { libc::geteuid() } == 0)
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
{
Ok(false)
}
}

View File

@ -2,8 +2,8 @@ use super::{Draft, IClashTemp, IProfiles, IRuntime, IVerge};
use crate::{
config::PrfItem,
core::{handle, CoreManager},
enhance, logging,
utils::{dirs, help, logging::Type},
enhance,
utils::{dirs, help},
};
use anyhow::{anyhow, Result};
use once_cell::sync::OnceCell;
@ -66,27 +66,21 @@ impl Config {
let script_item = PrfItem::from_script(Some("Script".to_string()))?;
Self::profiles().data().append_item(script_item.clone())?;
}
// 生成运行时配置
if let Err(err) = Self::generate().await {
logging!(error, Type::Config, true, "生成运行时配置失败: {}", err);
} else {
logging!(info, Type::Config, true, "生成运行时配置成功");
}
crate::log_err!(Self::generate().await);
// 生成运行时配置文件并验证
let config_result = Self::generate_file(ConfigType::Run);
let validation_result = if config_result.is_ok() {
// 验证配置文件
logging!(info, Type::Config, true, "开始验证配置");
println!("[首次启动] 开始验证配置");
match CoreManager::global().validate_config().await {
Ok((is_valid, error_msg)) => {
if !is_valid {
logging!(
warn,
Type::Config,
true,
println!(
"[首次启动] 配置验证失败,使用默认最小配置启动: {}",
error_msg
);
@ -95,12 +89,12 @@ impl Config {
.await?;
Some(("config_validate::boot_error", error_msg))
} else {
logging!(info, Type::Config, true, "配置验证成功");
println!("[首次启动] 配置验证成功");
Some(("config_validate::success", String::new()))
}
}
Err(err) => {
logging!(warn, Type::Config, true, "验证进程执行失败: {}", err);
println!("[首次启动] 验证进程执行失败: {}", err);
CoreManager::global()
.use_default_config("config_validate::process_terminated", "")
.await?;
@ -108,7 +102,7 @@ impl Config {
}
}
} else {
logging!(warn, Type::Config, true, "生成配置文件失败,使用默认配置");
println!("[首次启动] 生成配置文件失败,使用默认配置");
CoreManager::global()
.use_default_config("config_validate::error", "")
.await?;

View File

@ -196,7 +196,7 @@ pub struct IVerge {
/// 自动进入轻量模式的延迟(分钟)
pub auto_light_weight_minutes: Option<u64>,
/// 服务状态跟踪
pub service_state: Option<crate::core::service::ServiceState>,
}

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,17 @@
use crate::log_err;
use once_cell::sync::OnceCell;
use parking_lot::RwLock;
use std::{sync::Arc, time::Duration};
use std::sync::Arc;
use tauri::{AppHandle, Emitter, Manager, WebviewWindow};
use crate::{logging, logging_error, utils::logging::Type};
/// 存储启动期间的错误消息
#[derive(Debug, Clone)]
struct ErrorMessage {
status: String,
message: String,
}
use tauri_plugin_shell::process::CommandChild;
use std::fs::File;
#[derive(Debug, Default, Clone)]
pub struct Handle {
pub app_handle: Arc<RwLock<Option<AppHandle>>>,
pub is_exiting: Arc<RwLock<bool>>,
/// 存储启动过程中产生的错误消息队列
startup_errors: Arc<RwLock<Vec<ErrorMessage>>>,
startup_completed: Arc<RwLock<bool>>,
pub core_process: Arc<RwLock<Option<CommandChild>>>,
pub core_lock: Arc<RwLock<Option<File>>>,
}
impl Handle {
@ -28,8 +21,8 @@ impl Handle {
HANDLE.get_or_init(|| Handle {
app_handle: Arc::new(RwLock::new(None)),
is_exiting: Arc::new(RwLock::new(false)),
startup_errors: Arc::new(RwLock::new(Vec::new())),
startup_completed: Arc::new(RwLock::new(false)),
core_process: Arc::new(RwLock::new(None)),
core_lock: Arc::new(RwLock::new(None)),
})
}
@ -53,115 +46,26 @@ impl Handle {
pub fn refresh_clash() {
if let Some(window) = Self::global().get_window() {
logging_error!(
Type::Frontend,
true,
window.emit("verge://refresh-clash-config", "yes")
);
log_err!(window.emit("verge://refresh-clash-config", "yes"));
}
}
pub fn refresh_verge() {
if let Some(window) = Self::global().get_window() {
logging_error!(
Type::Frontend,
true,
window.emit("verge://refresh-verge-config", "yes")
);
log_err!(window.emit("verge://refresh-verge-config", "yes"));
}
}
#[allow(unused)]
pub fn refresh_profiles() {
if let Some(window) = Self::global().get_window() {
logging_error!(
Type::Frontend,
true,
window.emit("verge://refresh-profiles-config", "yes")
);
log_err!(window.emit("verge://refresh-profiles-config", "yes"));
}
}
/// 通知前端显示消息,如果在启动过程中,则将消息存入启动错误队列
pub fn notice_message<S: Into<String>, M: Into<String>>(status: S, msg: M) {
let handle = Self::global();
let status_str = status.into();
let msg_str = msg.into();
// 检查是否正在启动过程中
if !*handle.startup_completed.read() {
logging!(
info,
Type::Frontend,
true,
"启动过程中发现错误,加入消息队列: {} - {}",
status_str,
msg_str
);
// 将消息添加到启动错误队列
let mut errors = handle.startup_errors.write();
errors.push(ErrorMessage {
status: status_str,
message: msg_str,
});
return;
}
// 已经完成启动,直接发送消息
if let Some(window) = handle.get_window() {
logging_error!(
Type::Frontend,
true,
window.emit("verge://notice-message", (status_str, msg_str))
);
}
}
/// 标记启动已完成,并发送所有启动阶段累积的错误消息
pub fn mark_startup_completed(&self) {
{
let mut completed = self.startup_completed.write();
*completed = true;
}
self.send_startup_errors();
}
/// 发送启动时累积的所有错误消息
fn send_startup_errors(&self) {
let errors = {
let mut errors = self.startup_errors.write();
std::mem::take(&mut *errors)
};
if errors.is_empty() {
return;
}
logging!(
info,
Type::Frontend,
true,
"发送{}条启动时累积的错误消息",
errors.len()
);
// 等待2秒以确保前端已完全加载延迟发送错误通知
if let Some(window) = self.get_window() {
let window_clone = window.clone();
let errors_clone = errors.clone();
tauri::async_runtime::spawn(async move {
tokio::time::sleep(Duration::from_secs(2)).await;
for error in errors_clone {
let _ =
window_clone.emit("verge://notice-message", (error.status, error.message));
// 每条消息之间间隔500ms避免消息堆积
tokio::time::sleep(Duration::from_millis(500)).await;
}
});
if let Some(window) = Self::global().get_window() {
log_err!(window.emit("verge://notice-message", (status.into(), msg.into())));
}
}
@ -170,7 +74,39 @@ impl Handle {
*is_exiting = true;
}
pub fn set_core_process(&self, process: CommandChild) {
let mut core_process = self.core_process.write();
*core_process = Some(process);
}
pub fn take_core_process(&self) -> Option<CommandChild> {
let mut core_process = self.core_process.write();
core_process.take()
}
/// 检查是否有运行中的核心进程
pub fn has_core_process(&self) -> bool {
self.core_process.read().is_some()
}
pub fn is_exiting(&self) -> bool {
*self.is_exiting.read()
}
/// 设置核心文件锁
pub fn set_core_lock(&self, file: File) {
let mut core_lock = self.core_lock.write();
*core_lock = Some(file);
}
/// 释放核心文件锁
pub fn release_core_lock(&self) -> Option<File> {
let mut core_lock = self.core_lock.write();
core_lock.take()
}
/// 检查是否持有核心文件锁
pub fn has_core_lock(&self) -> bool {
self.core_lock.read().is_some()
}
}

View File

@ -1,9 +1,6 @@
use crate::{
config::Config,
core::handle,
feat, logging, logging_error,
module::lightweight::entry_lightweight_mode,
utils::{logging::Type, resolve},
config::Config, core::handle, feat, log_err, module::lightweight::entry_lightweight_mode,
utils::resolve,
};
use anyhow::{bail, Result};
use once_cell::sync::OnceCell;
@ -29,27 +26,22 @@ impl Hotkey {
let verge = Config::verge();
let enable_global_hotkey = verge.latest().enable_global_hotkey.unwrap_or(true);
logging!(
debug,
Type::Hotkey,
true,
"Initializing hotkeys with enable: {}",
println!(
"Initializing hotkeys, global hotkey enabled: {}",
enable_global_hotkey
);
log::info!(target: "app", "Initializing hotkeys, global hotkey enabled: {}", enable_global_hotkey);
// 如果全局热键被禁用,则不注册热键
if !enable_global_hotkey {
println!("Global hotkey is disabled, skipping registration");
log::info!(target: "app", "Global hotkey is disabled, skipping registration");
return Ok(());
}
if let Some(hotkeys) = verge.latest().hotkeys.as_ref() {
logging!(
debug,
Type::Hotkey,
true,
"Has {} hotkeys need to register",
hotkeys.len()
);
println!("Found {} hotkeys to register", hotkeys.len());
log::info!(target: "app", "Found {} hotkeys to register", hotkeys.len());
for hotkey in hotkeys.iter() {
let mut iter = hotkey.split(',');
@ -58,52 +50,28 @@ impl Hotkey {
match (key, func) {
(Some(key), Some(func)) => {
logging!(
debug,
Type::Hotkey,
true,
"Registering hotkey: {} -> {}",
key,
func
);
println!("Registering hotkey: {} -> {}", key, func);
log::info!(target: "app", "Registering hotkey: {} -> {}", key, func);
if let Err(e) = self.register(key, func) {
logging!(
error,
Type::Hotkey,
true,
"Failed to register hotkey {} -> {}: {:?}",
key,
func,
e
);
println!("Failed to register hotkey {} -> {}: {:?}", key, func, e);
log::error!(target: "app", "Failed to register hotkey {} -> {}: {:?}", key, func, e);
} else {
logging!(
debug,
Type::Hotkey,
true,
"Successfully registered hotkey {} -> {}",
key,
func
);
println!("Successfully registered hotkey {} -> {}", key, func);
log::info!(target: "app", "Successfully registered hotkey {} -> {}", key, func);
}
}
_ => {
let key = key.unwrap_or("None");
let func = func.unwrap_or("None");
logging!(
error,
Type::Hotkey,
true,
"Invalid hotkey configuration: `{}`:`{}`",
key,
func
);
println!("Invalid hotkey configuration: `{key}`:`{func}`");
log::error!(target: "app", "Invalid hotkey configuration: `{key}`:`{func}`");
}
}
}
self.current.lock().clone_from(hotkeys);
} else {
logging!(debug, Type::Hotkey, true, "No hotkeys configured");
println!("No hotkeys configured");
log::info!(target: "app", "No hotkeys configured");
}
Ok(())
@ -120,60 +88,45 @@ impl Hotkey {
let app_handle = handle::Handle::global().app_handle().unwrap();
let manager = app_handle.global_shortcut();
logging!(
debug,
Type::Hotkey,
true,
println!(
"Attempting to register hotkey: {} for function: {}",
hotkey,
func
hotkey, func
);
log::info!(target: "app", "Attempting to register hotkey: {} for function: {}", hotkey, func);
if manager.is_registered(hotkey) {
logging!(
debug,
Type::Hotkey,
true,
println!(
"Hotkey {} was already registered, unregistering first",
hotkey
);
log::info!(target: "app", "Hotkey {} was already registered, unregistering first", hotkey);
manager.unregister(hotkey)?;
}
let f = match func.trim() {
"open_or_close_dashboard" => {
logging!(
debug,
Type::Hotkey,
true,
"Registering open_or_close_dashboard function"
);
println!("Registering open_or_close_dashboard function");
log::info!(target: "app", "Registering open_or_close_dashboard function");
|| {
logging!(
debug,
Type::Hotkey,
true,
"=== Hotkey Dashboard Window Operation Start ==="
);
println!("=== Hotkey Dashboard Window Operation Start ===");
log::info!(target: "app", "=== Hotkey Dashboard Window Operation Start ===");
// 使用 spawn_blocking 来确保在正确的线程上执行
async_runtime::spawn_blocking(|| {
logging!(
debug,
Type::Hotkey,
true,
"Toggle dashboard window visibility"
);
println!("Toggle dashboard window visibility");
log::info!(target: "app", "Toggle dashboard window visibility");
// 检查窗口是否存在
if let Some(window) = handle::Handle::global().get_window() {
// 如果窗口可见,则隐藏它
if window.is_visible().unwrap_or(false) {
logging!(info, Type::Window, true, "Window is visible, hiding it");
println!("Window is visible, hiding it");
log::info!(target: "app", "Window is visible, hiding it");
let _ = window.hide();
} else {
// 如果窗口不可见,则显示它
logging!(info, Type::Window, true, "Window is hidden, showing it");
println!("Window is hidden, showing it");
log::info!(target: "app", "Window is hidden, showing it");
if window.is_minimized().unwrap_or(false) {
let _ = window.unminimize();
}
@ -182,22 +135,14 @@ impl Hotkey {
}
} else {
// 如果窗口不存在,创建一个新窗口
logging!(
info,
Type::Window,
true,
"Window does not exist, creating a new one"
);
resolve::create_window(true);
println!("Window does not exist, creating a new one");
log::info!(target: "app", "Window does not exist, creating a new one");
resolve::create_window();
}
});
logging!(
debug,
Type::Hotkey,
true,
"=== Hotkey Dashboard Window Operation End ==="
);
println!("=== Hotkey Dashboard Window Operation End ===");
log::info!(target: "app", "=== Hotkey Dashboard Window Operation End ===");
}
}
"clash_mode_rule" => || feat::change_clash_mode("rule".into()),
@ -211,7 +156,8 @@ impl Hotkey {
"hide" => || feat::hide(),
_ => {
logging!(error, Type::Hotkey, true, "Invalid function: {}", func);
println!("Invalid function: {}", func);
log::error!(target: "app", "Invalid function: {}", func);
bail!("invalid function \"{func}\"");
}
};
@ -220,18 +166,21 @@ impl Hotkey {
let _ = manager.on_shortcut(hotkey, move |app_handle, hotkey, event| {
if event.state == ShortcutState::Pressed {
logging!(debug, Type::Hotkey, true, "Hotkey pressed: {:?}", hotkey);
println!("Hotkey pressed: {:?}", hotkey);
log::info!(target: "app", "Hotkey pressed: {:?}", hotkey);
if hotkey.key == Code::KeyQ && is_quit {
if let Some(window) = app_handle.get_webview_window("main") {
if window.is_focused().unwrap_or(false) {
logging!(debug, Type::Hotkey, true, "Executing quit function");
println!("Executing quit function");
log::info!(target: "app", "Executing quit function");
f();
}
}
} else {
// 直接执行函数,不做任何状态检查
logging!(debug, Type::Hotkey, true, "Executing function directly");
println!("Executing function directly");
log::info!(target: "app", "Executing function directly");
// 获取全局热键状态
let is_enable_global_hotkey = Config::verge()
@ -254,14 +203,8 @@ impl Hotkey {
}
});
logging!(
debug,
Type::Hotkey,
true,
"Successfully registered hotkey {} for {}",
hotkey,
func
);
println!("Successfully registered hotkey {} for {}", hotkey, func);
log::info!(target: "app", "Successfully registered hotkey {} for {}", hotkey, func);
Ok(())
}
@ -269,7 +212,7 @@ impl Hotkey {
let app_handle = handle::Handle::global().app_handle().unwrap();
let manager = app_handle.global_shortcut();
manager.unregister(hotkey)?;
logging!(debug, Type::Hotkey, true, "Unregister hotkey {}", hotkey);
log::debug!(target: "app", "unregister hotkey {hotkey}");
Ok(())
}
@ -285,7 +228,7 @@ impl Hotkey {
});
add.iter().for_each(|(key, func)| {
logging_error!(Type::Hotkey, true, self.register(key, func));
log_err!(self.register(key, func));
});
*current = new_hotkeys;
@ -342,13 +285,7 @@ impl Drop for Hotkey {
fn drop(&mut self) {
let app_handle = handle::Handle::global().app_handle().unwrap();
if let Err(e) = app_handle.global_shortcut().unregister_all() {
logging!(
error,
Type::Hotkey,
true,
"Error unregistering all hotkeys: {:?}",
e
);
log::error!(target:"app", "Error unregistering all hotkeys: {:?}", e);
}
}
}

View File

@ -1,17 +1,7 @@
use crate::{
config::Config,
logging,
utils::{dirs, logging::Type},
};
use crate::{config::Config, utils::dirs};
use anyhow::{bail, Context, Result};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
env::current_exe,
path::PathBuf,
process::Command as StdCommand,
time::{SystemTime, UNIX_EPOCH},
};
use std::{collections::HashMap, env::current_exe, path::PathBuf, process::Command as StdCommand, time::{SystemTime, UNIX_EPOCH}};
use tokio::time::Duration;
// Windows only
@ -21,16 +11,15 @@ const REQUIRED_SERVICE_VERSION: &str = "1.0.5"; // 定义所需的服务版本
// 限制重装时间和次数的常量
const REINSTALL_COOLDOWN_SECS: u64 = 300; // 5分钟冷却期
const MAX_REINSTALLS_PER_DAY: u32 = 3; // 每24小时最多重装3次
const ONE_DAY_SECS: u64 = 86400; // 24小时的秒数
const MAX_REINSTALLS_PER_DAY: u32 = 3; // 每24小时最多重装3次
const ONE_DAY_SECS: u64 = 86400; // 24小时的秒数
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct ServiceState {
pub last_install_time: u64, // 上次安装时间戳 (Unix 时间戳,秒)
pub install_count: u32, // 24小时内安装次数
pub last_check_time: u64, // 上次检查时间
pub last_error: Option<String>, // 上次错误信息
pub prefer_sidecar: bool, // 用户是否偏好sidecar模式如拒绝安装服务或安装失败
pub last_install_time: u64, // 上次安装时间戳 (Unix 时间戳,秒)
pub install_count: u32, // 24小时内安装次数
pub last_check_time: u64, // 上次检查时间
pub last_error: Option<String>, // 上次错误信息
}
impl ServiceState {
@ -58,12 +47,12 @@ impl ServiceState {
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
// 检查是否需要重置计数器24小时已过
if now - self.last_install_time > ONE_DAY_SECS {
self.install_count = 0;
}
self.last_install_time = now;
self.install_count += 1;
}
@ -74,19 +63,17 @@ impl ServiceState {
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
// 如果在冷却期内,不允许重装
if now - self.last_install_time < REINSTALL_COOLDOWN_SECS {
return false;
}
// 如果24小时内安装次数过多也不允许
if now - self.last_install_time < ONE_DAY_SECS
&& self.install_count >= MAX_REINSTALLS_PER_DAY
{
if now - self.last_install_time < ONE_DAY_SECS && self.install_count >= MAX_REINSTALLS_PER_DAY {
return false;
}
true
}
}
@ -120,56 +107,43 @@ pub struct VersionJsonResponse {
}
#[cfg(target_os = "windows")]
pub async fn uninstall_service() -> Result<()> {
logging!(info, Type::Service, true, "uninstall service");
pub async fn reinstall_service() -> Result<()> {
log::info!(target:"app", "reinstall service");
use deelevate::{PrivilegeLevel, Token};
use runas::Command as RunasCommand;
use std::os::windows::process::CommandExt;
let binary_path = dirs::service_path()?;
let uninstall_path = binary_path.with_file_name("uninstall-service.exe");
if !uninstall_path.exists() {
bail!(format!("uninstaller not found: {uninstall_path:?}"));
// 获取当前服务状态
let mut service_state = ServiceState::get();
// 检查是否允许重装
if !service_state.can_reinstall() {
log::warn!(target:"app", "service reinstall rejected: cooldown period or max attempts reached");
bail!("Service reinstallation is rate limited. Please try again later.");
}
let token = Token::with_current_process()?;
let level = token.privilege_level()?;
let status = match level {
PrivilegeLevel::NotPrivileged => RunasCommand::new(uninstall_path).show(false).status()?,
_ => StdCommand::new(uninstall_path)
.creation_flags(0x08000000)
.status()?,
};
if !status.success() {
bail!(
"failed to uninstall service with status {}",
status.code().unwrap()
);
}
Ok(())
}
#[cfg(target_os = "windows")]
pub async fn install_service() -> Result<()> {
logging!(info, Type::Service, true, "install service");
use deelevate::{PrivilegeLevel, Token};
use runas::Command as RunasCommand;
use std::os::windows::process::CommandExt;
let binary_path = dirs::service_path()?;
let install_path = binary_path.with_file_name("install-service.exe");
let uninstall_path = binary_path.with_file_name("uninstall-service.exe");
if !install_path.exists() {
bail!(format!("installer not found: {install_path:?}"));
}
if !uninstall_path.exists() {
bail!(format!("uninstaller not found: {uninstall_path:?}"));
}
let token = Token::with_current_process()?;
let level = token.privilege_level()?;
let _ = match level {
PrivilegeLevel::NotPrivileged => RunasCommand::new(uninstall_path).show(false).status()?,
_ => StdCommand::new(uninstall_path)
.creation_flags(0x08000000)
.status()?,
};
let status = match level {
PrivilegeLevel::NotPrivileged => RunasCommand::new(install_path).show(false).status()?,
_ => StdCommand::new(install_path)
@ -178,73 +152,41 @@ pub async fn install_service() -> Result<()> {
};
if !status.success() {
bail!(
let error = format!(
"failed to install service with status {}",
status.code().unwrap()
);
service_state.last_error = Some(error.clone());
service_state.save()?;
bail!(error);
}
// 记录安装信息并保存
service_state.record_install();
service_state.last_error = None;
service_state.save()?;
Ok(())
}
#[cfg(target_os = "windows")]
pub async fn reinstall_service() -> Result<()> {
logging!(info, Type::Service, true, "reinstall service");
// 获取当前服务状态
let mut service_state = ServiceState::get();
// 检查是否允许重装
if !service_state.can_reinstall() {
logging!(
warn,
Type::Service,
true,
"service reinstall rejected: cooldown period or max attempts reached"
);
bail!("Service reinstallation is rate limited. Please try again later.");
}
// 先卸载服务
if let Err(err) = uninstall_service().await {
logging!(
warn,
Type::Service,
true,
"failed to uninstall service: {}",
err
);
}
// 再安装服务
match install_service().await {
Ok(_) => {
// 记录安装信息并保存
service_state.record_install();
service_state.last_error = None;
service_state.save()?;
Ok(())
}
Err(err) => {
let error = format!("failed to install service: {}", err);
service_state.last_error = Some(error.clone());
service_state.save()?;
bail!(error)
}
}
}
#[cfg(target_os = "linux")]
pub async fn uninstall_service() -> Result<()> {
logging!(info, Type::Service, true, "uninstall service");
pub async fn reinstall_service() -> Result<()> {
log::info!(target:"app", "reinstall service");
use users::get_effective_uid;
let install_path = tauri::utils::platform::current_exe()?.with_file_name("install-service");
let uninstall_path = tauri::utils::platform::current_exe()?.with_file_name("uninstall-service");
if !install_path.exists() {
bail!(format!("installer not found: {install_path:?}"));
}
if !uninstall_path.exists() {
bail!(format!("uninstaller not found: {uninstall_path:?}"));
}
let install_shell: String = install_path.to_string_lossy().replace(" ", "\\ ");
let uninstall_shell: String = uninstall_path.to_string_lossy().replace(" ", "\\ ");
let elevator = crate::utils::help::linux_elevator();
@ -256,38 +198,8 @@ pub async fn uninstall_service() -> Result<()> {
.arg(uninstall_shell)
.status()?,
};
logging!(
info,
Type::Service,
true,
"uninstall status code:{}",
status.code().unwrap()
);
log::info!(target:"app", "status code:{}", status.code().unwrap());
if !status.success() {
bail!(
"failed to uninstall service with status {}",
status.code().unwrap()
);
}
Ok(())
}
#[cfg(target_os = "linux")]
pub async fn install_service() -> Result<()> {
logging!(info, Type::Service, true, "install service");
use users::get_effective_uid;
let install_path = tauri::utils::platform::current_exe()?.with_file_name("install-service");
if !install_path.exists() {
bail!(format!("installer not found: {install_path:?}"));
}
let install_shell: String = install_path.to_string_lossy().replace(" ", "\\ ");
let elevator = crate::utils::help::linux_elevator();
let status = match get_effective_uid() {
0 => StdCommand::new(install_shell).status()?,
_ => StdCommand::new(elevator.clone())
@ -296,13 +208,6 @@ pub async fn install_service() -> Result<()> {
.arg(install_shell)
.status()?,
};
logging!(
info,
Type::Service,
true,
"install status code:{}",
status.code().unwrap()
);
if !status.success() {
bail!(
@ -314,110 +219,44 @@ pub async fn install_service() -> Result<()> {
Ok(())
}
#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
pub async fn reinstall_service() -> Result<()> {
logging!(info, Type::Service, true, "reinstall service");
// 获取当前服务状态
let mut service_state = ServiceState::get();
// 检查是否允许重装
if !service_state.can_reinstall() {
logging!(
warn,
Type::Service,
true,
"service reinstall rejected: cooldown period or max attempts reached"
);
bail!("Service reinstallation is rate limited. Please try again later.");
}
// 先卸载服务
if let Err(err) = uninstall_service().await {
logging!(
warn,
Type::Service,
true,
"failed to uninstall service: {}",
err
);
}
// 再安装服务
match install_service().await {
Ok(_) => {
// 记录安装信息并保存
service_state.record_install();
service_state.last_error = None;
service_state.save()?;
Ok(())
}
Err(err) => {
let error = format!("failed to install service: {}", err);
service_state.last_error = Some(error.clone());
service_state.save()?;
bail!(error)
}
}
}
#[cfg(target_os = "macos")]
pub async fn uninstall_service() -> Result<()> {
use crate::utils::i18n::t;
logging!(info, Type::Service, true, "uninstall service");
let binary_path = dirs::service_path()?;
let uninstall_path = binary_path.with_file_name("uninstall-service");
if !uninstall_path.exists() {
bail!(format!("uninstaller not found: {uninstall_path:?}"));
}
let uninstall_shell: String = uninstall_path.to_string_lossy().into_owned();
let prompt = t("Service Administrator Prompt");
let command = format!(
r#"do shell script "sudo '{uninstall_shell}'" with administrator privileges with prompt "{prompt}""#
);
logging!(debug, Type::Service, true, "uninstall command: {}", command);
let status = StdCommand::new("osascript")
.args(vec!["-e", &command])
.status()?;
if !status.success() {
bail!(
"failed to uninstall service with status {}",
status.code().unwrap()
);
}
Ok(())
}
#[cfg(target_os = "macos")]
pub async fn install_service() -> Result<()> {
use crate::utils::i18n::t;
logging!(info, Type::Service, true, "install service");
log::info!(target:"app", "reinstall service");
let binary_path = dirs::service_path()?;
let install_path = binary_path.with_file_name("install-service");
let uninstall_path = binary_path.with_file_name("uninstall-service");
if !install_path.exists() {
bail!(format!("installer not found: {install_path:?}"));
}
let install_shell: String = install_path.to_string_lossy().into_owned();
if !uninstall_path.exists() {
bail!(format!("uninstaller not found: {uninstall_path:?}"));
}
let install_shell: String = install_path.to_string_lossy().into_owned();
let uninstall_shell: String = uninstall_path.to_string_lossy().into_owned();
// 获取提示文本,如果 i18n 失败则使用硬编码默认值
let prompt = crate::utils::i18n::t("Service Administrator Prompt");
let prompt = if prompt == "Service Administrator Prompt" {
if Config::verge().latest().language.as_deref() == Some("zh")
|| Config::verge().latest().language.is_none()
{
"Clash Verge 需要使用管理员权限来重新安装系统服务"
} else {
"Clash Verge needs administrator privileges to reinstall the system service"
}
} else {
&prompt
};
let prompt = t("Service Administrator Prompt");
let command = format!(
r#"do shell script "sudo '{install_shell}'" with administrator privileges with prompt "{prompt}""#
r#"do shell script "sudo '{uninstall_shell}' && sudo '{install_shell}'" with administrator privileges with prompt "{prompt}""#
);
logging!(debug, Type::Service, true, "install command: {}", command);
log::debug!(target: "app", "command: {}", command);
let status = StdCommand::new("osascript")
.args(vec!["-e", &command])
@ -429,57 +268,9 @@ pub async fn install_service() -> Result<()> {
status.code().unwrap()
);
}
Ok(())
}
#[cfg(target_os = "macos")]
pub async fn reinstall_service() -> Result<()> {
logging!(info, Type::Service, true, "reinstall service");
// 获取当前服务状态
let mut service_state = ServiceState::get();
// 检查是否允许重装
if !service_state.can_reinstall() {
logging!(
warn,
Type::Service,
true,
"service reinstall rejected: cooldown period or max attempts reached"
);
bail!("Service reinstallation is rate limited. Please try again later.");
}
// 先卸载服务
if let Err(err) = uninstall_service().await {
logging!(
warn,
Type::Service,
true,
"failed to uninstall service: {}",
err
);
}
// 再安装服务
match install_service().await {
Ok(_) => {
// 记录安装信息并保存
service_state.record_install();
service_state.last_error = None;
service_state.save()?;
Ok(())
}
Err(err) => {
let error = format!("failed to install service: {}", err);
service_state.last_error = Some(error.clone());
service_state.save()?;
bail!(error)
}
}
}
/// check the windows service status
pub async fn check_service() -> Result<JsonResponse> {
let url = format!("{SERVICE_URL}/get_clash");
@ -523,7 +314,7 @@ pub async fn check_service_version() -> Result<String> {
pub async fn check_service_needs_reinstall() -> bool {
// 获取当前服务状态
let service_state = ServiceState::get();
// 首先检查是否在冷却期或超过重装次数限制
if !service_state.can_reinstall() {
log::info!(target: "app", "service reinstall check: in cooldown period or max attempts reached");
@ -535,21 +326,21 @@ pub async fn check_service_needs_reinstall() -> bool {
Ok(version) => {
// 打印更详细的日志,方便排查问题
log::info!(target: "app", "服务版本检测:当前={}, 要求={}", version, REQUIRED_SERVICE_VERSION);
let needs_reinstall = version != REQUIRED_SERVICE_VERSION;
if needs_reinstall {
log::warn!(target: "app", "发现服务版本不匹配,需要重装! 当前={}, 要求={}",
log::warn!(target: "app", "发现服务版本不匹配,需要重装! 当前={}, 要求={}",
version, REQUIRED_SERVICE_VERSION);
// 打印版本字符串的原始字节,确认没有隐藏字符
log::debug!(target: "app", "当前版本字节: {:?}", version.as_bytes());
log::debug!(target: "app", "要求版本字节: {:?}", REQUIRED_SERVICE_VERSION.as_bytes());
} else {
log::info!(target: "app", "服务版本匹配,无需重装");
}
needs_reinstall
}
},
Err(err) => {
// 检查服务是否可用如果可用但版本检查失败可能只是版本API有问题
match is_service_running().await {
@ -580,6 +371,8 @@ pub(super) async fn start_with_existing_service(config_file: &PathBuf) -> Result
let config_dir = dirs::app_home_dir()?;
let config_dir = dirs::path_to_str(&config_dir)?;
#[cfg(target_os = "linux")]
let config_dir = &(config_dir.replace("/verge-mihomo", "") + "/resources");
let log_path = dirs::service_log_file()?;
let log_path = dirs::path_to_str(&log_path)?;
@ -615,9 +408,9 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
// 先检查服务版本,不受冷却期限制
let version_check = match check_service_version().await {
Ok(version) => {
log::info!(target: "app", "检测到服务版本: {}, 要求版本: {}",
log::info!(target: "app", "检测到服务版本: {}, 要求版本: {}",
version, REQUIRED_SERVICE_VERSION);
// 通过字节比较确保完全匹配
if version.as_bytes() != REQUIRED_SERVICE_VERSION.as_bytes() {
log::warn!(target: "app", "服务版本不匹配,需要重装");
@ -626,7 +419,7 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
log::info!(target: "app", "服务版本匹配");
true // 版本匹配
}
}
},
Err(err) => {
log::warn!(target: "app", "无法获取服务版本: {}", err);
false // 无法获取版本
@ -641,11 +434,11 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
return start_with_existing_service(config_file).await;
}
}
// 强制执行版本检查,如果版本不匹配则重装
if !version_check {
log::info!(target: "app", "服务版本不匹配,尝试重装");
// 获取服务状态,检查是否可以重装
let service_state = ServiceState::get();
if !service_state.can_reinstall() {
@ -658,22 +451,22 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
bail!("服务版本不匹配且无法重装,启动失败");
}
}
// 尝试重装
log::info!(target: "app", "开始重装服务");
if let Err(err) = reinstall_service().await {
log::warn!(target: "app", "服务重装失败: {}", err);
// 尝试使用现有服务
log::info!(target: "app", "尝试使用现有服务");
return start_with_existing_service(config_file).await;
}
// 重装成功,尝试启动
log::info!(target: "app", "服务重装成功,尝试启动");
return start_with_existing_service(config_file).await;
}
// 检查服务状态
match check_service().await {
Ok(_) => {
@ -682,22 +475,22 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
if let Ok(()) = start_with_existing_service(config_file).await {
return Ok(());
}
}
},
Err(err) => {
log::warn!(target: "app", "服务检查失败: {}", err);
}
}
// 服务不可用或启动失败,检查是否需要重装
if check_service_needs_reinstall().await {
log::info!(target: "app", "服务需要重装");
// 尝试重装
if let Err(err) = reinstall_service().await {
log::warn!(target: "app", "服务重装失败: {}", err);
bail!("Failed to reinstall service: {}", err);
}
// 重装后再次尝试启动
log::info!(target: "app", "服务重装完成,尝试启动核心");
start_with_existing_service(config_file).await
@ -728,22 +521,12 @@ pub async fn is_service_running() -> Result<bool> {
// 检查服务状态码和消息
if resp.code == 0 && resp.msg == "ok" && resp.data.is_some() {
logging!(debug, Type::Service, "Service is running");
Ok(true)
} else {
logging!(debug, Type::Service, "Service is not running");
Ok(false)
}
}
pub async fn is_service_available() -> Result<()> {
let resp = check_service().await?;
if resp.code == 0 && resp.msg == "ok" && resp.data.is_some() {
logging!(debug, Type::Service, "Service is available");
}
Ok(())
}
/// 强制重装服务用于UI中的修复服务按钮
pub async fn force_reinstall_service() -> Result<()> {
log::info!(target: "app", "用户请求强制重装服务");
@ -751,15 +534,15 @@ pub async fn force_reinstall_service() -> Result<()> {
// 创建默认服务状态(重置所有限制)
let service_state = ServiceState::default();
service_state.save()?;
log::info!(target: "app", "已重置服务状态,开始执行重装");
// 执行重装
match reinstall_service().await {
Ok(()) => {
log::info!(target: "app", "服务重装成功");
Ok(())
}
},
Err(err) => {
log::error!(target: "app", "强制重装服务失败: {}", err);
bail!("强制重装服务失败: {}", err)

View File

@ -1,8 +1,7 @@
use crate::{
config::{Config, IVerge},
core::handle::Handle,
logging_error,
utils::logging::Type,
log_err,
};
use anyhow::Result;
use once_cell::sync::OnceCell;
@ -221,9 +220,9 @@ impl Sysopt {
let enable = enable.unwrap_or(false);
let app_handle = Handle::global().app_handle().unwrap();
let autostart_manager = app_handle.autolaunch();
log::info!(target: "app", "Setting auto launch to: {}", enable);
match enable {
true => {
let result = autostart_manager.enable();
@ -232,8 +231,8 @@ impl Sysopt {
} else {
log::info!(target: "app", "Auto launch enabled successfully");
}
logging_error!(Type::System, true, result);
}
log_err!(result)
},
false => {
let result = autostart_manager.disable();
if let Err(ref e) = result {
@ -241,8 +240,8 @@ impl Sysopt {
} else {
log::info!(target: "app", "Auto launch disabled successfully");
}
logging_error!(Type::System, true, result);
}
log_err!(result)
},
};
Ok(())
@ -252,12 +251,12 @@ impl Sysopt {
pub fn get_launch_status(&self) -> Result<bool> {
let app_handle = Handle::global().app_handle().unwrap();
let autostart_manager = app_handle.autolaunch();
match autostart_manager.is_enabled() {
Ok(status) => {
log::info!(target: "app", "Auto launch status: {}", status);
Ok(status)
}
},
Err(e) => {
log::error!(target: "app", "Failed to get auto launch status: {}", e);
Err(anyhow::anyhow!("Failed to get auto launch status: {}", e))
@ -324,7 +323,7 @@ impl Sysopt {
enable: true,
url: format!("http://127.0.0.1:{pac_port}/commands/pac"),
};
logging_error!(Type::System, true, autoproxy.set_auto_proxy());
log_err!(autoproxy.set_auto_proxy());
} else {
let sysproxy = Sysproxy {
enable: true,
@ -333,7 +332,7 @@ impl Sysopt {
bypass: get_bypass(),
};
logging_error!(Type::System, true, sysproxy.set_system_proxy());
log_err!(sysproxy.set_system_proxy());
}
}

View File

@ -1,6 +1,4 @@
use crate::{
config::Config, core::CoreManager, feat, logging, logging_error, utils::logging::Type,
};
use crate::{config::Config, core::CoreManager, feat};
use anyhow::{Context, Result};
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder};
use once_cell::sync::OnceCell;
@ -56,18 +54,18 @@ impl Timer {
)
.is_err()
{
logging!(debug, Type::Timer, "Timer already initialized, skipping...");
log::debug!(target: "app", "Timer already initialized, skipping...");
return Ok(());
}
logging!(info, Type::Timer, true, "Initializing timer...");
log::info!(target: "app", "Initializing timer...");
// Initialize timer tasks
if let Err(e) = self.refresh() {
// Reset initialization flag on error
self.initialized
.store(false, std::sync::atomic::Ordering::SeqCst);
logging_error!(Type::Timer, false, "Failed to initialize timer: {}", e);
log::error!(target: "app", "Failed to initialize timer: {}", e);
return Err(e);
}
@ -100,15 +98,15 @@ impl Timer {
for uid in profiles_to_update {
if let Some(task) = timer_map.get(&uid) {
logging!(info, Type::Timer, "Advancing task for uid: {}", uid);
log::info!(target: "app", "Advancing task for uid: {}", uid);
if let Err(e) = delay_timer.advance_task(task.task_id) {
logging!(warn, Type::Timer, "Failed to advance task {}: {}", uid, e);
log::warn!(target: "app", "Failed to advance task {}: {}", uid, e);
}
}
}
}
logging!(info, Type::Timer, "Timer initialization completed");
log::info!(target: "app", "Timer initialization completed");
Ok(())
}
@ -118,16 +116,11 @@ impl Timer {
let diff_map = self.gen_diff();
if diff_map.is_empty() {
logging!(debug, Type::Timer, "No timer changes needed");
log::debug!(target: "app", "No timer changes needed");
return Ok(());
}
logging!(
info,
Type::Timer,
"Refreshing {} timer tasks",
diff_map.len()
);
log::info!(target: "app", "Refreshing {} timer tasks", diff_map.len());
// Apply changes while holding locks
let mut timer_map = self.timer_map.write();
@ -138,16 +131,9 @@ impl Timer {
DiffFlag::Del(tid) => {
timer_map.remove(&uid);
if let Err(e) = delay_timer.remove_task(tid) {
logging!(
warn,
Type::Timer,
"Failed to remove task {} for uid {}: {}",
tid,
uid,
e
);
log::warn!(target: "app", "Failed to remove task {} for uid {}: {}", tid, uid, e);
} else {
logging!(debug, Type::Timer, "Removed task {} for uid {}", tid, uid);
log::debug!(target: "app", "Removed task {} for uid {}", tid, uid);
}
}
DiffFlag::Add(tid, interval) => {
@ -160,23 +146,16 @@ impl Timer {
timer_map.insert(uid.clone(), task);
if let Err(e) = self.add_task(&mut delay_timer, uid.clone(), tid, interval) {
logging_error!(Type::Timer, "Failed to add task for uid {}: {}", uid, e);
log::error!(target: "app", "Failed to add task for uid {}: {}", uid, e);
timer_map.remove(&uid); // Rollback on failure
} else {
logging!(debug, Type::Timer, "Added task {} for uid {}", tid, uid);
log::debug!(target: "app", "Added task {} for uid {}", tid, uid);
}
}
DiffFlag::Mod(tid, interval) => {
// Remove old task first
if let Err(e) = delay_timer.remove_task(tid) {
logging!(
warn,
Type::Timer,
"Failed to remove old task {} for uid {}: {}",
tid,
uid,
e
);
log::warn!(target: "app", "Failed to remove old task {} for uid {}: {}", tid, uid, e);
}
// Then add the new one
@ -189,10 +168,10 @@ impl Timer {
timer_map.insert(uid.clone(), task);
if let Err(e) = self.add_task(&mut delay_timer, uid.clone(), tid, interval) {
logging_error!(Type::Timer, "Failed to update task for uid {}: {}", uid, e);
log::error!(target: "app", "Failed to update task for uid {}: {}", uid, e);
timer_map.remove(&uid); // Rollback on failure
} else {
logging!(debug, Type::Timer, "Updated task {} for uid {}", tid, uid);
log::debug!(target: "app", "Updated task {} for uid {}", tid, uid);
}
}
}
@ -271,14 +250,7 @@ impl Timer {
tid: TaskID,
minutes: u64,
) -> Result<()> {
logging!(
info,
Type::Timer,
"Adding task: uid={}, id={}, interval={}min",
uid,
tid,
minutes
);
log::info!(target: "app", "Adding task: uid={}, id={}, interval={}min", uid, tid, minutes);
// Create a task with reasonable retries and backoff
let task = TaskBuilder::default()
@ -303,7 +275,7 @@ impl Timer {
/// Async task with better error handling and logging
async fn async_task(uid: String) {
let task_start = std::time::Instant::now();
logging!(info, Type::Timer, "Running timer task for profile: {}", uid);
log::info!(target: "app", "Running timer task for profile: {}", uid);
// Update profile
let profile_result = feat::update_profile(uid.clone(), None).await;
@ -314,26 +286,23 @@ impl Timer {
match CoreManager::global().update_config().await {
Ok(_) => {
let duration = task_start.elapsed().as_millis();
logging!(
info,
Type::Timer,
log::info!(
target: "app",
"Timer task completed successfully for uid: {} (took {}ms)",
uid,
duration
uid, duration
);
}
Err(e) => {
logging_error!(
Type::Timer,
log::error!(
target: "app",
"Failed to refresh config after profile update for uid {}: {}",
uid,
e
uid, e
);
}
}
}
Err(e) => {
logging_error!(Type::Timer, "Failed to update profile uid {}: {}", uid, e);
log::error!(target: "app", "Failed to update profile uid {}: {}", uid, e);
}
}
}

View File

@ -8,7 +8,7 @@ use crate::{
feat,
module::{lightweight::entry_lightweight_mode, mihomo::Rate},
resolve,
utils::{dirs::find_target_icons, i18n::t, logging::Type, resolve::VERSION},
utils::{dirs, i18n::t, resolve::VERSION},
};
use anyhow::Result;
@ -20,7 +20,10 @@ use parking_lot::Mutex;
use parking_lot::RwLock;
#[cfg(target_os = "macos")]
pub use speed_rate::{SpeedRate, Traffic};
use std::fs;
#[cfg(target_os = "macos")]
use std::collections::hash_map::DefaultHasher;
#[cfg(target_os = "macos")]
use std::hash::{Hash, Hasher};
#[cfg(target_os = "macos")]
use std::sync::Arc;
use tauri::{
@ -32,124 +35,19 @@ use tauri::{
use tokio::sync::broadcast;
use super::handle;
#[derive(Clone)]
struct TrayState {}
#[cfg(target_os = "macos")]
pub struct Tray {
pub speed_rate: Arc<Mutex<Option<SpeedRate>>>,
shutdown_tx: Arc<RwLock<Option<broadcast::Sender<()>>>>,
is_subscribed: Arc<RwLock<bool>>,
pub icon_hash: Arc<Mutex<Option<u64>>>,
pub icon_cache: Arc<Mutex<Option<Vec<u8>>>>,
pub rate_cache: Arc<Mutex<Option<Rate>>>,
}
#[cfg(not(target_os = "macos"))]
pub struct Tray {}
impl TrayState {
pub fn get_common_tray_icon() -> (bool, Vec<u8>) {
let verge = Config::verge().latest().clone();
let is_common_tray_icon = verge.common_tray_icon.unwrap_or(false);
if is_common_tray_icon {
if let Some(common_icon_path) = find_target_icons("common").unwrap() {
let icon_data = fs::read(common_icon_path).unwrap();
return (true, icon_data);
}
}
#[cfg(target_os = "macos")]
{
let tray_icon_colorful = verge.tray_icon.unwrap_or("monochrome".to_string());
if tray_icon_colorful == "monochrome" {
(
false,
include_bytes!("../../../icons/tray-icon-mono.ico").to_vec(),
)
} else {
(
false,
include_bytes!("../../../icons/tray-icon.ico").to_vec(),
)
}
}
#[cfg(not(target_os = "macos"))]
{
(
false,
include_bytes!("../../../icons/tray-icon.ico").to_vec(),
)
}
}
pub fn get_sysproxy_tray_icon() -> (bool, Vec<u8>) {
let verge = Config::verge().latest().clone();
let is_sysproxy_tray_icon = verge.sysproxy_tray_icon.unwrap_or(false);
if is_sysproxy_tray_icon {
if let Some(sysproxy_icon_path) = find_target_icons("sysproxy").unwrap() {
let icon_data = fs::read(sysproxy_icon_path).unwrap();
return (true, icon_data);
}
}
#[cfg(target_os = "macos")]
{
let tray_icon_colorful = verge.tray_icon.clone().unwrap_or("monochrome".to_string());
if tray_icon_colorful == "monochrome" {
(
false,
include_bytes!("../../../icons/tray-icon-sys-mono.ico").to_vec(),
)
} else {
(
false,
include_bytes!("../../../icons/tray-icon-sys.ico").to_vec(),
)
}
}
#[cfg(not(target_os = "macos"))]
{
(
false,
include_bytes!("../../../icons/tray-icon-sys.ico").to_vec(),
)
}
}
pub fn get_tun_tray_icon() -> (bool, Vec<u8>) {
let verge = Config::verge().latest().clone();
let is_tun_tray_icon = verge.tun_tray_icon.unwrap_or(false);
if is_tun_tray_icon {
if let Some(tun_icon_path) = find_target_icons("tun").unwrap() {
let icon_data = fs::read(tun_icon_path).unwrap();
return (true, icon_data);
}
}
#[cfg(target_os = "macos")]
{
let tray_icon_colorful = verge.tray_icon.clone().unwrap_or("monochrome".to_string());
if tray_icon_colorful == "monochrome" {
(
false,
include_bytes!("../../../icons/tray-icon-tun-mono.ico").to_vec(),
)
} else {
(
false,
include_bytes!("../../../icons/tray-icon-tun.ico").to_vec(),
)
}
}
#[cfg(not(target_os = "macos"))]
{
(
false,
include_bytes!("../../../icons/tray-icon-tun.ico").to_vec(),
)
}
}
}
impl Tray {
pub fn global() -> &'static Tray {
static TRAY: OnceCell<Tray> = OnceCell::new();
@ -159,6 +57,8 @@ impl Tray {
speed_rate: Arc::new(Mutex::new(None)),
shutdown_tx: Arc::new(RwLock::new(None)),
is_subscribed: Arc::new(RwLock::new(false)),
icon_hash: Arc::new(Mutex::new(None)),
icon_cache: Arc::new(Mutex::new(None)),
rate_cache: Arc::new(Mutex::new(None)),
});
@ -205,7 +105,7 @@ impl Tray {
match tray_event.as_str() {
"system_proxy" => feat::toggle_system_proxy(),
"tun_mode" => feat::toggle_tun_mode(None),
"main_window" => resolve::create_window(true),
"main_window" => resolve::create_window(),
_ => {}
}
}
@ -259,29 +159,107 @@ impl Tray {
}
/// 更新托盘图标
#[allow(unused_variables)]
pub fn update_icon(&self, rate: Option<Rate>) -> Result<()> {
let app_handle = handle::Handle::global().app_handle().unwrap();
let verge = Config::verge().latest().clone();
let system_mode = verge.enable_system_proxy.as_ref().unwrap_or(&false);
let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
let app_handle = handle::Handle::global().app_handle().unwrap();
let common_tray_icon = verge.common_tray_icon.as_ref().unwrap_or(&false);
let sysproxy_tray_icon = verge.sysproxy_tray_icon.as_ref().unwrap_or(&false);
let tun_tray_icon = verge.tun_tray_icon.as_ref().unwrap_or(&false);
let tray = app_handle.tray_by_id("main").unwrap();
let (is_custom_icon, icon_bytes) = match (*system_mode, *tun_mode) {
(true, true) => TrayState::get_tun_tray_icon(),
(true, false) => TrayState::get_sysproxy_tray_icon(),
(false, true) => TrayState::get_tun_tray_icon(),
(false, false) => TrayState::get_common_tray_icon(),
#[cfg(target_os = "macos")]
let tray_icon = verge.tray_icon.clone().unwrap_or("monochrome".to_string());
let icon_bytes = if *system_proxy && !*tun_mode {
#[cfg(target_os = "macos")]
let mut icon = match tray_icon.as_str() {
"colorful" => include_bytes!("../../../icons/tray-icon-sys.ico").to_vec(),
_ => include_bytes!("../../../icons/tray-icon-sys-mono.ico").to_vec(),
};
#[cfg(not(target_os = "macos"))]
let mut icon = include_bytes!("../../../icons/tray-icon-sys.ico").to_vec();
if *sysproxy_tray_icon {
let icon_dir_path = dirs::app_home_dir()?.join("icons");
let png_path = icon_dir_path.join("sysproxy.png");
let ico_path = icon_dir_path.join("sysproxy.ico");
if ico_path.exists() {
icon = std::fs::read(ico_path).unwrap();
} else if png_path.exists() {
icon = std::fs::read(png_path).unwrap();
}
}
icon
} else if *tun_mode {
#[cfg(target_os = "macos")]
let mut icon = match tray_icon.as_str() {
"colorful" => include_bytes!("../../../icons/tray-icon-tun.ico").to_vec(),
_ => include_bytes!("../../../icons/tray-icon-tun-mono.ico").to_vec(),
};
#[cfg(not(target_os = "macos"))]
let mut icon = include_bytes!("../../../icons/tray-icon-tun.ico").to_vec();
if *tun_tray_icon {
let icon_dir_path = dirs::app_home_dir()?.join("icons");
let png_path = icon_dir_path.join("tun.png");
let ico_path = icon_dir_path.join("tun.ico");
if ico_path.exists() {
icon = std::fs::read(ico_path).unwrap();
} else if png_path.exists() {
icon = std::fs::read(png_path).unwrap();
}
}
icon
} else {
#[cfg(target_os = "macos")]
let mut icon = match tray_icon.as_str() {
"colorful" => include_bytes!("../../../icons/tray-icon.ico").to_vec(),
_ => include_bytes!("../../../icons/tray-icon-mono.ico").to_vec(),
};
#[cfg(not(target_os = "macos"))]
let mut icon = include_bytes!("../../../icons/tray-icon.ico").to_vec();
if *common_tray_icon {
let icon_dir_path = dirs::app_home_dir()?.join("icons");
let png_path = icon_dir_path.join("common.png");
let ico_path = icon_dir_path.join("common.ico");
if ico_path.exists() {
icon = std::fs::read(ico_path).unwrap();
} else if png_path.exists() {
icon = std::fs::read(png_path).unwrap();
}
}
icon
};
#[cfg(target_os = "macos")]
{
let enable_tray_speed = verge.enable_tray_speed.unwrap_or(true);
let enable_tray_icon = verge.enable_tray_icon.unwrap_or(true);
let colorful = verge.tray_icon.clone().unwrap_or("monochrome".to_string());
let is_colorful = colorful == "colorful";
let is_colorful = tray_icon == "colorful";
if !enable_tray_speed {
let _ = tray.set_icon(Some(tauri::image::Image::from_bytes(&icon_bytes)?));
let icon_hash = {
let mut hasher = DefaultHasher::new();
icon_bytes.clone().hash(&mut hasher);
hasher.finish()
};
let mut icon_hash_guard = self.icon_hash.lock();
let mut icon_bytes_guard = self.icon_cache.lock();
if *icon_hash_guard != Some(icon_hash) {
*icon_hash_guard = Some(icon_hash);
*icon_bytes_guard = Some(icon_bytes.clone());
}
if !enable_tray_speed || (!enable_tray_speed && !enable_tray_icon) {
let _ = tray.set_icon(Some(tauri::image::Image::from_bytes(
&(*icon_bytes_guard).clone().unwrap(),
)?));
let _ = tray.set_icon_as_template(!is_colorful);
return Ok(());
}
@ -302,15 +280,16 @@ impl Tray {
*rate_guard = rate;
let bytes = if enable_tray_icon {
Some(icon_bytes)
Some(icon_bytes_guard.as_ref().unwrap())
} else {
None
};
let rate = rate_guard.as_ref();
let rate_bytes = SpeedRate::add_speed_text(is_custom_icon, bytes, rate).unwrap();
let rate_bytes = SpeedRate::add_speed_text(bytes, rate).unwrap();
let _ = tray.set_icon(Some(tauri::image::Image::from_bytes(&rate_bytes)?));
let _ = tray.set_icon_as_template(!is_custom_icon && !is_colorful);
let _ = tray.set_icon_as_template(!is_colorful);
}
Ok(())
}
@ -675,17 +654,18 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
println!("change mode to: {}", mode);
feat::change_clash_mode(mode.into());
}
"open_window" => resolve::create_window(true),
"open_window" => resolve::create_window(),
"system_proxy" => feat::toggle_system_proxy(),
"tun_mode" => feat::toggle_tun_mode(None),
"copy_env" => feat::copy_clash_env(),
"open_app_dir" => crate::logging_error!(Type::Cmd, true, cmd::open_app_dir()),
"open_core_dir" => crate::logging_error!(Type::Cmd, true, cmd::open_core_dir()),
"open_logs_dir" => crate::logging_error!(Type::Cmd, true, cmd::open_logs_dir()),
"open_app_dir" => crate::log_err!(cmd::open_app_dir()),
"open_core_dir" => crate::log_err!(cmd::open_core_dir()),
"open_logs_dir" => crate::log_err!(cmd::open_logs_dir()),
"restart_clash" => feat::restart_clash_core(),
"restart_app" => feat::restart_app(),
"entry_lightweight_mode" => entry_lightweight_mode(),
"quit" => {
println!("quit");
feat::quit(Some(0));
}
id if id.starts_with("profiles_") => {

View File

@ -76,16 +76,15 @@ impl SpeedRate {
}
// 分离图标加载和速率渲染
pub fn add_speed_text(
is_custom_icon: bool,
icon_bytes: Option<Vec<u8>>,
rate: Option<&Rate>,
pub fn add_speed_text<'a>(
icon_bytes: Option<&'a Vec<u8>>,
rate: Option<&'a Rate>,
) -> Result<Vec<u8>> {
let rate = rate.unwrap_or(&Rate { up: 0, down: 0 });
let (mut icon_width, mut icon_height) = (0, 256);
let icon_image = if let Some(bytes) = icon_bytes.clone() {
let icon_image = image::load_from_memory(&bytes)?;
let icon_image = if let Some(bytes) = icon_bytes {
let icon_image = image::load_from_memory(bytes)?;
icon_width = icon_image.width();
icon_height = icon_image.height();
icon_image
@ -94,17 +93,23 @@ impl SpeedRate {
image::DynamicImage::new_rgba8(0, 0)
};
let total_width = match (is_custom_icon, icon_bytes.is_some()) {
(true, true) => 510,
(true, false) => 740,
(false, false) => 740,
(false, true) => icon_width + 740,
// 判断是否为彩色图标
let is_colorful = if let Some(bytes) = icon_bytes {
!crate::utils::help::is_monochrome_image_from_bytes(bytes).unwrap_or(false)
} else {
false
};
// println!(
// "icon_height: {}, icon_wight: {}, total_width: {}",
// icon_height, icon_width, total_width
// );
// 增加文本宽度和间距
let total_width = if icon_bytes.is_some() {
if icon_width < 580 {
icon_width + 580
} else {
icon_width
}
} else {
580
};
// 创建新的透明画布
let mut combined_image = RgbaImage::new(total_width, icon_height);
@ -119,28 +124,18 @@ impl SpeedRate {
}
}
let is_colorful = if let Some(bytes) = icon_bytes.clone() {
!crate::utils::help::is_monochrome_image_from_bytes(&bytes).unwrap_or(false)
} else {
false
};
// 选择文本颜色
let (text_color, shadow_color) = if is_colorful {
(
Rgba([144u8, 144u8, 144u8, 255u8]),
// Rgba([255u8, 255u8, 255u8, 128u8]),
Rgba([0u8, 0u8, 0u8, 128u8]),
)
// (
// Rgba([160u8, 160u8, 160u8, 255u8]),
// // Rgba([255u8, 255u8, 255u8, 128u8]),
// Rgba([0u8, 0u8, 0u8, 255u8]),
// )
} else {
// 彩色图标使用黑色文本和轻微白色阴影
(
Rgba([255u8, 255u8, 255u8, 255u8]),
Rgba([0u8, 0u8, 0u8, 128u8]),
Rgba([0u8, 0u8, 0u8, 160u8]),
)
} else {
// 单色图标使用白色文本和轻微黑色阴影
(
Rgba([255u8, 255u8, 255u8, 255u8]),
Rgba([0u8, 0u8, 0u8, 120u8]),
)
};
// 减小字体大小以适应文本区域
@ -150,30 +145,17 @@ impl SpeedRate {
let scale = ab_glyph::PxScale::from(font_size);
// 使用更简洁的速率格式
let up_text = format!("{}", format_bytes_speed(rate.up));
let down_text = format!("{}", format_bytes_speed(rate.down));
// For test rate display
// let down_text = format!("↓ {}", format_bytes_speed(102 * 1020 * 1024));
let up_text = format_bytes_speed(rate.up);
let down_text = format_bytes_speed(rate.down);
// 计算文本位置,确保垂直间距合适
// 修改文本位置为居右显示
// 计算右对齐的文本位置
// let up_text_width = imageproc::drawing::text_size(scale, &font, &up_text).0 as u32;
// let down_text_width = imageproc::drawing::text_size(scale, &font, &down_text).0 as u32;
// let up_text_x = total_width - up_text_width;
// let down_text_x = total_width - down_text_width;
let up_text_width = imageproc::drawing::text_size(scale, &font, &up_text).0 as u32;
let down_text_width = imageproc::drawing::text_size(scale, &font, &down_text).0 as u32;
// 计算左对齐的文本位置
let (up_text_x, down_text_x) = {
if is_custom_icon || icon_bytes.is_some() {
let text_left_offset = 30;
let left_begin = icon_width + text_left_offset;
(left_begin, left_begin)
} else {
(icon_width, icon_width)
}
};
// 计算右对齐的文本位置
let up_text_x = total_width - up_text_width;
let down_text_x = total_width - down_text_width;
// 优化垂直位置,使速率显示的高度和上下间距正好等于图标大小
let text_height = font_size as i32;

View File

@ -1 +0,0 @@
pub mod service;

View File

@ -1 +0,0 @@

View File

@ -1,8 +1,8 @@
use crate::{
config::{Config, IVerge},
core::backup,
logging_error,
utils::{dirs::app_home_dir, logging::Type},
log_err,
utils::dirs::app_home_dir,
};
use anyhow::Result;
use reqwest_dav::list_cmd::ListFile;
@ -69,9 +69,8 @@ pub async fn restore_webdav_backup(filename: String) -> Result<()> {
// extract zip file
let mut zip = zip::ZipArchive::new(fs::File::open(backup_storage_path.clone())?)?;
zip.extract(app_home_dir()?)?;
logging_error!(
Type::Backup,
true,
log_err!(
super::patch_verge(
IVerge {
webdav_url,

View File

@ -1,9 +1,9 @@
use crate::{
config::Config,
core::{handle, tray, CoreManager},
logging_error,
log_err,
module::mihomo::MihomoManager,
utils::{logging::Type, resolve},
utils::resolve,
};
use serde_yaml::{Mapping, Value};
use tauri::Manager;
@ -28,31 +28,12 @@ pub fn restart_clash_core() {
pub fn restart_app() {
tauri::async_runtime::spawn_blocking(|| {
tauri::async_runtime::block_on(async {
logging_error!(Type::Core, true, CoreManager::global().stop_core().await);
resolve::resolve_reset_async().await;
let app_handle = handle::Handle::global().app_handle().unwrap();
std::thread::sleep(std::time::Duration::from_secs(1));
tauri::process::restart(&app_handle.env());
log_err!(CoreManager::global().stop_core().await);
});
});
}
fn after_change_clash_mode() {
tauri::async_runtime::spawn(async {
match MihomoManager::global().get_connections().await {
Ok(connections) => {
if let Some(connections_array) = connections["connections"].as_array() {
for connection in connections_array {
if let Some(id) = connection["id"].as_str() {
let _ = MihomoManager::global().delete_connection(id).await;
}
}
}
}
Err(err) => {
log::error!(target: "app", "Failed to get connections: {}", err);
}
}
resolve::resolve_reset();
let app_handle = handle::Handle::global().app_handle().unwrap();
std::thread::sleep(std::time::Duration::from_secs(1));
tauri::process::restart(&app_handle.env());
});
}
@ -66,6 +47,8 @@ pub fn change_clash_mode(mode: String) {
});
tauri::async_runtime::spawn(async move {
log::debug!(target: "app", "change clash mode to {mode}");
CoreManager::global().ensure_running_core().await;
match MihomoManager::global().patch_configs(json_value).await {
Ok(_) => {
// 更新订阅
@ -73,16 +56,8 @@ pub fn change_clash_mode(mode: String) {
if Config::clash().data().save_config().is_ok() {
handle::Handle::refresh_clash();
logging_error!(Type::Tray, true, tray::Tray::global().update_menu());
logging_error!(Type::Tray, true, tray::Tray::global().update_icon(None));
}
let is_auto_close_connection = Config::verge()
.data()
.auto_close_connection
.unwrap_or(false);
if is_auto_close_connection {
after_change_clash_mode();
log_err!(tray::Tray::global().update_menu());
log_err!(tray::Tray::global().update_icon(None));
}
}
Err(err) => println!("{err}"),
@ -92,6 +67,7 @@ pub fn change_clash_mode(mode: String) {
/// Test connection delay to a URL
pub async fn test_delay(url: String) -> anyhow::Result<u32> {
CoreManager::global().ensure_running_core().await;
use tokio::time::{Duration, Instant};
let mut builder = reqwest::ClientBuilder::new().use_rustls_tls().no_proxy();

View File

@ -1,9 +1,8 @@
use crate::{
config::{Config, IVerge},
core::{handle, hotkey, sysopt, tray, CoreManager},
logging_error,
log_err,
module::lightweight,
utils::logging::Type,
};
use anyhow::Result;
use serde_yaml::Mapping;
@ -19,8 +18,8 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> {
CoreManager::global().restart_core().await?;
} else {
if patch.get("mode").is_some() {
logging_error!(Type::Tray, true, tray::Tray::global().update_menu());
logging_error!(Type::Tray, true, tray::Tray::global().update_icon(None));
log_err!(tray::Tray::global().update_menu());
log_err!(tray::Tray::global().update_icon(None));
}
Config::runtime().latest().patch_config(patch);
CoreManager::global().update_config().await?;

View File

@ -82,11 +82,3 @@ pub async fn update_profile(uid: String, option: Option<PrfOption>) -> Result<()
Ok(())
}
/// 增强配置
pub async fn enhance_profiles() -> Result<()> {
crate::core::CoreManager::global()
.update_config()
.await
.map(|_| ())
}

View File

@ -28,19 +28,6 @@ pub fn toggle_system_proxy() {
/// Toggle TUN mode on/off
pub fn toggle_tun_mode(not_save_file: Option<bool>) {
// tauri::async_runtime::spawn(async move {
// logging!(
// info,
// Type::Service,
// true,
// "Toggle TUN mode need install service"
// );
// if is_service_available().await.is_err() {
// logging_error!(Type::Service, true, install_service().await);
// }
// logging_error!(Type::Core, true, CoreManager::global().restart_core().await);
// });
let enable = Config::verge().data().enable_tun_mode;
let enable = enable.unwrap_or(false);

View File

@ -6,6 +6,8 @@ use crate::{
module::mihomo::MihomoManager,
utils::resolve,
};
use tauri::Manager;
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
/// Open or close the dashboard window
#[allow(dead_code)]
@ -45,10 +47,40 @@ pub fn open_or_close_dashboard() {
} else {
println!("No existing window found, creating new window");
log::info!(target: "app", "No existing window found, creating new window");
resolve::create_window(true);
resolve::create_window();
}
}
/// Setup window state monitor to save window position and size in real-time
pub fn setup_window_state_monitor(app_handle: &tauri::AppHandle) {
let window = app_handle.get_webview_window("main").unwrap();
let app_handle_clone = app_handle.clone();
// 监听窗口移动事件
let app_handle_move = app_handle_clone.clone();
window.on_window_event(move |event| {
match event {
// 窗口移动时保存状态
tauri::WindowEvent::Moved(_) => {
let _ = app_handle_move.save_window_state(StateFlags::all());
}
// 窗口调整大小时保存状态
tauri::WindowEvent::Resized(_) => {
let _ = app_handle_move.save_window_state(StateFlags::all());
}
// 其他可能改变窗口状态的事件
tauri::WindowEvent::ScaleFactorChanged { .. } => {
let _ = app_handle_move.save_window_state(StateFlags::all());
}
// 窗口关闭时保存
tauri::WindowEvent::CloseRequested { .. } => {
let _ = app_handle_move.save_window_state(StateFlags::all());
}
_ => {}
}
});
}
/// 优化的应用退出函数
pub fn quit(code: Option<i32>) {
log::debug!(target: "app", "启动退出流程");

View File

@ -2,7 +2,6 @@ mod cmd;
mod config;
mod core;
mod enhance;
mod error;
mod feat;
mod module;
mod utils;
@ -17,7 +16,6 @@ use tauri::AppHandle;
use tauri::Manager;
use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_deep_link::DeepLinkExt;
use utils::logging::Type;
/// A global singleton handle to the application.
pub struct AppHandleManager {
@ -112,6 +110,7 @@ pub fn run() {
.plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_shell::init())
@ -121,13 +120,13 @@ pub fn run() {
#[cfg(any(target_os = "linux", all(debug_assertions, windows)))]
{
use tauri_plugin_deep_link::DeepLinkExt;
logging_error!(Type::System, true, app.deep_link().register_all());
log_err!(app.deep_link().register_all());
}
app.deep_link().on_open_url(|event| {
tauri::async_runtime::spawn(async move {
if let Some(url) = event.urls().first() {
logging_error!(Type::Setup, true, resolve_scheme(url.to_string()).await);
log_err!(resolve_scheme(url.to_string()).await);
}
});
});
@ -152,14 +151,10 @@ pub fn run() {
cmd::restart_app,
// 添加新的命令
cmd::get_running_mode,
cmd::install_service,
cmd::repair_service,
cmd::get_app_uptime,
cmd::get_auto_launch_status,
cmd::is_admin,
// service 管理
cmd::install_service,
cmd::uninstall_service,
cmd::reinstall_service,
cmd::repair_service,
// clash
cmd::get_clash_info,
cmd::patch_clash_config,
@ -282,25 +277,13 @@ pub fn run() {
tauri::WindowEvent::Focused(true) => {
#[cfg(target_os = "macos")]
{
logging_error!(
Type::Hotkey,
true,
hotkey::Hotkey::global().register("CMD+Q", "quit")
);
logging_error!(
Type::Hotkey,
true,
hotkey::Hotkey::global().register("CMD+W", "hide")
);
log_err!(hotkey::Hotkey::global().register("CMD+Q", "quit"));
log_err!(hotkey::Hotkey::global().register("CMD+W", "hide"));
}
#[cfg(not(target_os = "macos"))]
{
logging_error!(
Type::Hotkey,
true,
hotkey::Hotkey::global().register("Control+Q", "quit")
);
log_err!(hotkey::Hotkey::global().register("Control+Q", "quit"));
};
{
let is_enable_global_hotkey = Config::verge()
@ -308,31 +291,19 @@ pub fn run() {
.enable_global_hotkey
.unwrap_or(true);
if !is_enable_global_hotkey {
logging_error!(Type::Hotkey, true, hotkey::Hotkey::global().init())
log_err!(hotkey::Hotkey::global().init())
}
}
}
tauri::WindowEvent::Focused(false) => {
#[cfg(target_os = "macos")]
{
logging_error!(
Type::Hotkey,
true,
hotkey::Hotkey::global().unregister("CMD+Q")
);
logging_error!(
Type::Hotkey,
true,
hotkey::Hotkey::global().unregister("CMD+W")
);
log_err!(hotkey::Hotkey::global().unregister("CMD+Q"));
log_err!(hotkey::Hotkey::global().unregister("CMD+W"));
}
#[cfg(not(target_os = "macos"))]
{
logging_error!(
Type::Hotkey,
true,
hotkey::Hotkey::global().unregister("Control+Q")
);
log_err!(hotkey::Hotkey::global().unregister("Control+Q"));
};
{
let is_enable_global_hotkey = Config::verge()
@ -340,32 +311,20 @@ pub fn run() {
.enable_global_hotkey
.unwrap_or(true);
if !is_enable_global_hotkey {
logging_error!(Type::Hotkey, true, hotkey::Hotkey::global().reset())
log_err!(hotkey::Hotkey::global().reset())
}
}
}
tauri::WindowEvent::Destroyed => {
#[cfg(target_os = "macos")]
{
logging_error!(
Type::Hotkey,
true,
hotkey::Hotkey::global().unregister("CMD+Q")
);
logging_error!(
Type::Hotkey,
true,
hotkey::Hotkey::global().unregister("CMD+W")
);
log_err!(hotkey::Hotkey::global().unregister("CMD+Q"));
log_err!(hotkey::Hotkey::global().unregister("CMD+W"));
}
#[cfg(not(target_os = "macos"))]
{
logging_error!(
Type::Hotkey,
true,
hotkey::Hotkey::global().unregister("Control+Q")
);
log_err!(hotkey::Hotkey::global().unregister("Control+Q"));
};
}
_ => {}

View File

@ -5,22 +5,21 @@ use tauri::{Listener, Manager};
use crate::{
config::Config,
core::{handle, timer::Timer},
log_err, logging,
utils::logging::Type,
AppHandleManager,
log_err, AppHandleManager,
};
const LIGHT_WEIGHT_TASK_UID: &str = "light_weight_task";
pub fn enable_auto_light_weight_mode() {
Timer::global().init().unwrap();
logging!(info, Type::Lightweight, true, "开启自动轻量模式");
println!("[lightweight_mode] 开启自动轻量模式");
log::info!(target: "app", "[lightweight_mode] 开启自动轻量模式");
setup_window_close_listener();
setup_webview_focus_listener();
}
pub fn disable_auto_light_weight_mode() {
logging!(info, Type::Lightweight, true, "关闭自动轻量模式");
println!("[lightweight_mode] 关闭自动轻量模式");
log::info!(target: "app", "[lightweight_mode] 关闭自动轻量模式");
let _ = cancel_light_weight_timer();
cancel_window_close_listener();
}
@ -35,7 +34,8 @@ pub fn entry_lightweight_mode() {
}
#[cfg(target_os = "macos")]
AppHandleManager::global().set_activation_policy_accessory();
logging!(info, Type::Lightweight, true, "轻量模式已开启");
println!("[lightweight_mode] 轻量模式已开启");
log::info!(target: "app", "[lightweight_mode] 轻量模式已开启");
}
let _ = cancel_light_weight_timer();
}
@ -44,12 +44,8 @@ fn setup_window_close_listener() -> u32 {
if let Some(window) = handle::Handle::global().get_window() {
let handler = window.listen("tauri://close-requested", move |_event| {
let _ = setup_light_weight_timer();
logging!(
info,
Type::Lightweight,
true,
"监听到关闭请求,开始轻量模式计时"
);
println!("[lightweight_mode] 监听到关闭请求,开始轻量模式计时");
log::info!(target: "app", "[lightweight_mode] 监听到关闭请求,开始轻量模式计时");
});
return handler;
}
@ -60,12 +56,8 @@ fn setup_webview_focus_listener() -> u32 {
if let Some(window) = handle::Handle::global().get_window() {
let handler = window.listen("tauri://focus", move |_event| {
log_err!(cancel_light_weight_timer());
logging!(
info,
Type::Lightweight,
true,
"监听到窗口获得焦点,取消轻量模式计时"
);
println!("[lightweight_mode] 监听到窗口获得焦点,取消轻量模式计时");
log::info!(target: "app", "[lightweight_mode] 监听到窗口获得焦点,取消轻量模式计时");
});
return handler;
}
@ -75,7 +67,8 @@ fn setup_webview_focus_listener() -> u32 {
fn cancel_window_close_listener() {
if let Some(window) = handle::Handle::global().get_window() {
window.unlisten(setup_window_close_listener());
logging!(info, Type::Lightweight, true, "取消了窗口关闭监听");
println!("[lightweight_mode] 取消了窗口关闭监听");
log::info!(target: "app", "[lightweight_mode] 取消了窗口关闭监听");
}
}
@ -92,6 +85,7 @@ fn setup_light_weight_timer() -> Result<()> {
let once_by_minutes = Config::verge()
.latest()
.auto_light_weight_minutes
.clone()
.unwrap_or(10);
let task = TaskBuilder::default()
@ -99,14 +93,17 @@ fn setup_light_weight_timer() -> Result<()> {
.set_maximum_parallel_runnable_num(1)
.set_frequency_once_by_minutes(once_by_minutes)
.spawn_async_routine(move || async move {
logging!(info, Type::Timer, true, "计时器到期,开始进入轻量模式");
println!("[lightweight_mode] 计时器到期,开始进入轻量模式");
log::info!(target: "app",
"[lightweight_mode] 计时器到期,开始进入轻量模式"
);
entry_lightweight_mode();
})
.context("failed to create timer task")?;
.context("failed to create light weight timer task")?;
delay_timer
.add_task(task)
.context("failed to add timer task")?;
.context("failed to add light weight timer task")?;
let timer_task = crate::core::timer::TimerTask {
task_id,
@ -116,11 +113,12 @@ fn setup_light_weight_timer() -> Result<()> {
timer_map.insert(LIGHT_WEIGHT_TASK_UID.to_string(), timer_task);
logging!(
info,
Type::Timer,
true,
"计时器已设置,{} 分钟后将自动进入轻量模式",
println!(
"[lightweight_mode] 轻量模式计时器已设置,{} 分钟后将自动进入轻量模式",
once_by_minutes
);
log::info!(target: "app",
"[lightweight_mode] 轻量模式计时器已设置,{} 分钟后将自动进入轻量模式",
once_by_minutes
);
@ -134,8 +132,9 @@ fn cancel_light_weight_timer() -> Result<()> {
if let Some(task) = timer_map.remove(LIGHT_WEIGHT_TASK_UID) {
delay_timer
.remove_task(task.task_id)
.context("failed to remove timer task")?;
logging!(info, Type::Timer, true, "计时器已取消");
.context("failed to remove light weight timer task")?;
println!("[lightweight_mode] 轻量模式计时器已取消");
log::info!(target: "app", "[lightweight_mode] 轻量模式计时器已取消");
}
Ok(())

View File

@ -1,3 +1,3 @@
pub mod lightweight;
pub mod mihomo;
pub mod sysinfo;
pub mod lightweight;

View File

@ -1,7 +1,4 @@
use crate::{
cmd::system,
core::{handle, CoreManager},
};
use crate::core::{handle, CoreManager};
use std::fmt::{self, Debug, Formatter};
use sysinfo::System;
@ -12,15 +9,14 @@ pub struct PlatformSpecification {
system_arch: String,
verge_version: String,
running_mode: String,
is_admin: bool,
}
impl Debug for PlatformSpecification {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"System Name: {}\nSystem Version: {}\nSystem kernel Version: {}\nSystem Arch: {}\nVerge Version: {}\nRunning Mode: {}\nIs Admin: {}",
self.system_name, self.system_version, self.system_kernel_version, self.system_arch, self.verge_version, self.running_mode, self.is_admin
"System Name: {}\nSystem Version: {}\nSystem kernel Version: {}\nSystem Arch: {}\nVerge Version: {}\nRunning Mode: {}",
self.system_name, self.system_version, self.system_kernel_version, self.system_arch, self.verge_version, self.running_mode
)
}
}
@ -36,10 +32,16 @@ impl PlatformSpecification {
let config = handler.config();
let verge_version = config.version.clone().unwrap_or("Null".into());
// 使用默认值避免在同步上下文中执行异步操作
let running_mode = "NotRunning".to_string();
let is_admin = system::is_admin().unwrap_or_default();
// Get running mode asynchronously
let running_mode = tokio::task::block_in_place(|| {
tokio::runtime::Handle::current().block_on(async {
match CoreManager::global().get_running_mode().await {
crate::core::RunningMode::Service => "Service".to_string(),
crate::core::RunningMode::Sidecar => "Sidecar".to_string(),
crate::core::RunningMode::NotRunning => "Not Running".to_string(),
}
})
});
Self {
system_name,
@ -48,17 +50,6 @@ impl PlatformSpecification {
system_arch,
verge_version,
running_mode,
is_admin,
}
}
// 异步方法来获取完整的系统信息
pub async fn new_async() -> Self {
let mut info = Self::new();
let running_mode = CoreManager::global().get_running_mode().await;
info.running_mode = running_mode.to_string();
info
}
}

View File

@ -77,36 +77,6 @@ pub fn app_profiles_dir() -> Result<PathBuf> {
Ok(app_home_dir()?.join("profiles"))
}
/// icons dir
pub fn app_icons_dir() -> Result<PathBuf> {
Ok(app_home_dir()?.join("icons"))
}
pub fn find_target_icons(target: &str) -> Result<Option<String>> {
let icons_dir = app_icons_dir()?;
let mut matching_files = Vec::new();
for entry in fs::read_dir(icons_dir)? {
let entry = entry?;
let path = entry.path();
if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) {
if file_name.starts_with(target)
&& (file_name.ends_with(".ico") || file_name.ends_with(".png"))
{
matching_files.push(path);
}
}
}
if matching_files.is_empty() {
Ok(None)
} else {
let first = path_to_str(matching_files.first().unwrap())?;
Ok(Some(first.to_string()))
}
}
/// logs dir
pub fn app_logs_dir() -> Result<PathBuf> {
Ok(app_home_dir()?.join("logs"))

View File

@ -1,8 +1,8 @@
use crate::{enhance::seq::SeqMap, logging, utils::logging::Type};
use crate::enhance::seq::SeqMap;
use anyhow::{anyhow, bail, Context, Result};
use nanoid::nanoid;
use serde::{de::DeserializeOwned, Serialize};
use serde_yaml::Mapping;
use serde_yaml::{Mapping, Value};
use std::{fs, path::PathBuf, str::FromStr};
/// read data from yaml as struct T
@ -22,41 +22,19 @@ pub fn read_yaml<T: DeserializeOwned>(path: &PathBuf) -> Result<T> {
})
}
/// read mapping from yaml
/// read mapping from yaml fix #165
pub fn read_mapping(path: &PathBuf) -> Result<Mapping> {
if !path.exists() {
bail!("file not found \"{}\"", path.display());
}
let mut val: Value = read_yaml(path)?;
val.apply_merge()
.with_context(|| format!("failed to apply merge \"{}\"", path.display()))?;
let yaml_str = fs::read_to_string(path)
.with_context(|| format!("failed to read the file \"{}\"", path.display()))?;
// YAML语法检查
match serde_yaml::from_str::<serde_yaml::Value>(&yaml_str) {
Ok(mut val) => {
val.apply_merge()
.with_context(|| format!("failed to apply merge \"{}\"", path.display()))?;
Ok(val
.as_mapping()
.ok_or(anyhow!(
"failed to transform to yaml mapping \"{}\"",
path.display()
))?
.to_owned())
}
Err(err) => {
let error_msg = format!("YAML syntax error in {}: {}", path.display(), err);
logging!(error, Type::Config, true, "{}", error_msg);
crate::core::handle::Handle::notice_message(
"config_validate::yaml_syntax_error",
&error_msg,
);
bail!("YAML syntax error: {}", err)
}
}
Ok(val
.as_mapping()
.ok_or(anyhow!(
"failed to transform to yaml mapping \"{}\"",
path.display()
))?
.to_owned())
}
/// read mapping from yaml fix #165
@ -158,6 +136,52 @@ pub fn linux_elevator() -> String {
}
}
#[macro_export]
macro_rules! error {
($result: expr) => {
log::error!(target: "app", "{}", $result);
};
}
#[macro_export]
macro_rules! log_err {
($result: expr) => {
if let Err(err) = $result {
log::error!(target: "app", "{err}");
}
};
($result: expr, $err_str: expr) => {
if let Err(_) = $result {
log::error!(target: "app", "{}", $err_str);
}
};
}
#[macro_export]
macro_rules! trace_err {
($result: expr, $err_str: expr) => {
if let Err(err) = $result {
log::trace!(target: "app", "{}, err {}", $err_str, err);
}
}
}
/// wrap the anyhow error
/// transform the error to String
#[macro_export]
macro_rules! wrap_err {
($stat: expr) => {
match $stat {
Ok(a) => Ok(a),
Err(err) => {
log::error!(target: "app", "{}", err.to_string());
Err(format!("{}", err.to_string()))
}
}
};
}
/// return the string literal error
#[macro_export]
macro_rules! ret_err {
@ -189,16 +213,15 @@ macro_rules! t {
/// ```
#[cfg(target_os = "macos")]
pub fn format_bytes_speed(speed: u64) -> String {
const UNITS: [&str; 4] = ["B", "KB", "MB", "GB"];
let mut size = speed as f64;
let mut unit_index = 0;
while size >= 1000.0 && unit_index < UNITS.len() - 1 {
size /= 1024.0;
unit_index += 1;
if speed < 1024 {
format!("{}B/s", speed)
} else if speed < 1024 * 1024 {
format!("{:.1}KB/s", speed as f64 / 1024.0)
} else if speed < 1024 * 1024 * 1024 {
format!("{:.1}MB/s", speed as f64 / 1024.0 / 1024.0)
} else {
format!("{:.1}GB/s", speed as f64 / 1024.0 / 1024.0 / 1024.0)
}
format!("{:.1}{}/s", size, UNITS[unit_index])
}
#[cfg(target_os = "macos")]

View File

@ -177,7 +177,6 @@ fn init_dns_config() -> Result<()> {
(
"default-nameserver".into(),
Value::Sequence(vec![
Value::String("system".into()),
Value::String("223.6.6.6".into()),
Value::String("8.8.8.8".into()),
]),
@ -190,7 +189,14 @@ fn init_dns_config() -> Result<()> {
Value::String("https://dns.alidns.com/dns-query".into()),
]),
),
("fallback".into(), Value::Sequence(vec![])),
(
"fallback".into(),
Value::Sequence(vec![
Value::String("https://dns.alidns.com/dns-query".into()),
Value::String("https://dns.google/dns-query".into()),
Value::String("https://cloudflare-dns.com/dns-query".into()),
]),
),
(
"nameserver-policy".into(),
Value::Mapping(serde_yaml::Mapping::new()),
@ -200,7 +206,6 @@ fn init_dns_config() -> Result<()> {
Value::Sequence(vec![
Value::String("https://doh.pub/dns-query".into()),
Value::String("https://dns.alidns.com/dns-query".into()),
Value::String("tls://223.5.5.5".into()),
]),
),
("direct-nameserver".into(), Value::Sequence(vec![])),
@ -295,11 +300,15 @@ pub fn init_config() -> Result<()> {
/// after tauri setup
pub fn init_resources() -> Result<()> {
let app_dir = dirs::app_home_dir()?;
let test_dir = app_dir.join("test");
let res_dir = dirs::app_resources_dir()?;
if !app_dir.exists() {
let _ = fs::create_dir_all(&app_dir);
}
if !test_dir.exists() {
let _ = fs::create_dir_all(&test_dir);
}
if !res_dir.exists() {
let _ = fs::create_dir_all(&res_dir);
}
@ -311,6 +320,7 @@ pub fn init_resources() -> Result<()> {
for file in file_list.iter() {
let src_path = res_dir.join(file);
let dest_path = app_dir.join(file);
let test_dest_path = test_dir.join(file);
log::debug!(target: "app", "src_path: {src_path:?}, dest_path: {dest_path:?}");
let handle_copy = |dest: &PathBuf| {
@ -322,6 +332,9 @@ pub fn init_resources() -> Result<()> {
};
};
if src_path.exists() && !test_dest_path.exists() {
handle_copy(&test_dest_path);
}
if src_path.exists() && !dest_path.exists() {
handle_copy(&dest_path);
continue;

View File

@ -1,139 +0,0 @@
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Type {
Cmd,
Core,
Config,
Setup,
System,
Service,
Hotkey,
Window,
Tray,
Timer,
Frontend,
Backup,
Lightweight,
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Type::Cmd => write!(f, "[Cmd]"),
Type::Core => write!(f, "[Core]"),
Type::Config => write!(f, "[Config]"),
Type::Setup => write!(f, "[Setup]"),
Type::System => write!(f, "[System]"),
Type::Service => write!(f, "[Service]"),
Type::Hotkey => write!(f, "[Hotkey]"),
Type::Window => write!(f, "[Window]"),
Type::Tray => write!(f, "[Tray]"),
Type::Timer => write!(f, "[Timer]"),
Type::Frontend => write!(f, "[Frontend]"),
Type::Backup => write!(f, "[Backup]"),
Type::Lightweight => write!(f, "[Lightweight]"),
}
}
}
#[macro_export]
macro_rules! error {
($result: expr) => {
log::error!(target: "app", "{}", $result);
};
}
#[macro_export]
macro_rules! log_err {
($result: expr) => {
if let Err(err) = $result {
log::error!(target: "app", "{err}");
}
};
($result: expr, $err_str: expr) => {
if let Err(_) = $result {
log::error!(target: "app", "{}", $err_str);
}
};
}
#[macro_export]
macro_rules! trace_err {
($result: expr, $err_str: expr) => {
if let Err(err) = $result {
log::trace!(target: "app", "{}, err {}", $err_str, err);
}
}
}
/// wrap the anyhow error
/// transform the error to String
#[macro_export]
macro_rules! wrap_err {
($stat: expr) => {
match $stat {
Ok(a) => Ok(a),
Err(err) => {
log::error!(target: "app", "{}", err.to_string());
Err(format!("{}", err.to_string()))
}
}
};
}
#[macro_export]
macro_rules! logging {
// 带 println 的版本(支持格式化参数)
($level:ident, $type:expr, true, $($arg:tt)*) => {
println!("{} {}", $type, format_args!($($arg)*));
log::$level!(target: "app", "{} {}", $type, format_args!($($arg)*));
};
// 带 println 的版本(使用 false 明确不打印)
($level:ident, $type:expr, false, $($arg:tt)*) => {
log::$level!(target: "app", "{} {}", $type, format_args!($($arg)*));
};
// 不带 print 参数的版本(默认不打印)
($level:ident, $type:expr, $($arg:tt)*) => {
log::$level!(target: "app", "{} {}", $type, format_args!($($arg)*));
};
}
#[macro_export]
macro_rules! logging_error {
// 1. 处理 Result<T, E>,带打印控制
($type:expr, $print:expr, $expr:expr) => {
match $expr {
Ok(_) => {},
Err(err) => {
if $print {
println!("[{}] Error: {}", $type, err);
}
log::error!(target: "app", "[{}] {}", $type, err);
}
}
};
// 2. 处理 Result<T, E>,默认不打印
($type:expr, $expr:expr) => {
if let Err(err) = $expr {
log::error!(target: "app", "[{}] {}", $type, err);
}
};
// 3. 处理格式化字符串,带打印控制
($type:expr, $print:expr, $fmt:literal $(, $arg:expr)*) => {
if $print {
println!("[{}] {}", $type, format_args!($fmt $(, $arg)*));
}
log::error!(target: "app", "[{}] {}", $type, format_args!($fmt $(, $arg)*));
};
// 4. 处理格式化字符串,不带 bool 时,默认 `false`
($type:expr, $fmt:literal $(, $arg:expr)*) => {
logging_error!($type, false, $fmt $(, $arg)*);
};
}

View File

@ -3,7 +3,6 @@ pub mod error;
pub mod help;
pub mod i18n;
pub mod init;
pub mod logging;
pub mod resolve;
pub mod server;
pub mod tmpl;

View File

@ -1,12 +1,7 @@
#[cfg(target_os = "macos")]
use crate::AppHandleManager;
use crate::{
config::{Config, IVerge, PrfItem},
core::*,
logging, logging_error,
module::lightweight,
utils::{error, init, logging::Type, server},
wrap_err,
config::{Config, IVerge, PrfItem}, core::*, log_err, module::lightweight, utils::{error, init, server}, wrap_err
};
use anyhow::{bail, Result};
use once_cell::sync::OnceCell;
@ -15,9 +10,10 @@ use serde_yaml::Mapping;
use std::net::TcpListener;
use tauri::{App, Manager};
use tauri::Url;
use url::Url;
//#[cfg(not(target_os = "linux"))]
// use window_shadows::set_shadow;
use tauri_plugin_notification::NotificationExt;
pub static VERSION: OnceCell<String> = OnceCell::new();
@ -51,98 +47,106 @@ pub async fn resolve_setup(app: &mut App) {
handle::Handle::global().init(app.app_handle());
VERSION.get_or_init(|| version.clone());
logging_error!(Type::Config, true, init::init_config());
logging_error!(Type::Setup, true, init::init_resources());
logging_error!(Type::Setup, true, init::init_scheme());
logging_error!(Type::Setup, true, init::startup_script().await);
log_err!(init::init_config());
log_err!(init::init_resources());
log_err!(init::init_scheme());
log_err!(init::startup_script().await);
// 处理随机端口
logging_error!(Type::System, true, resolve_random_port_config());
log_err!(resolve_random_port_config());
// 启动核心
logging!(trace, Type::Config, true, "Initial config");
logging_error!(Type::Config, true, Config::init_config().await);
log::trace!(target:"app", "init config");
log_err!(Config::init_config().await);
logging!(trace, Type::Core, "Starting CoreManager");
logging_error!(Type::Core, true, CoreManager::global().init().await);
if service::check_service().await.is_err() {
match service::reinstall_service().await {
Ok(_) => {
log::info!(target:"app", "install service susccess.");
#[cfg(not(target_os = "macos"))]
std::thread::sleep(std::time::Duration::from_millis(1000));
#[cfg(target_os = "macos")]
{
let mut service_runing = false;
for _ in 0..40 {
if service::check_service().await.is_ok() {
service_runing = true;
break;
} else {
log::warn!(target: "app", "service not runing, sleep 500ms and check again.");
std::thread::sleep(std::time::Duration::from_millis(500));
}
}
if !service_runing {
log::warn!(target: "app", "service not running, will fallback to user mode");
}
}
}
Err(e) => {
log::warn!(target: "app", "failed to install service: {e:?}, will fallback to user mode");
}
}
}
log::trace!(target: "app", "launch core");
log_err!(CoreManager::global().init().await);
// setup a simple http server for singleton
log::trace!(target: "app", "launch embed server");
server::embed_server();
log::trace!(target: "app", "Initial system tray");
logging_error!(Type::Tray, true, tray::Tray::global().init());
logging_error!(Type::Tray, true, tray::Tray::global().create_systray(app));
log::trace!(target: "app", "init system tray");
log_err!(tray::Tray::global().init());
log_err!(tray::Tray::global().create_systray(app));
logging_error!(
Type::System,
true,
sysopt::Sysopt::global().update_sysproxy().await
);
logging_error!(
Type::System,
true,
sysopt::Sysopt::global().init_guard_sysproxy()
);
log_err!(sysopt::Sysopt::global().update_sysproxy().await);
log_err!(sysopt::Sysopt::global().init_guard_sysproxy());
let is_silent_start = { Config::verge().data().enable_silent_start }.unwrap_or(false);
create_window(!is_silent_start);
// 初始化热键
log::trace!(target: "app", "init hotkeys");
log_err!(hotkey::Hotkey::global().init());
logging_error!(Type::System, true, timer::Timer::global().init());
let silent_start = { Config::verge().data().enable_silent_start };
if !silent_start.unwrap_or(false) {
create_window();
}
log_err!(tray::Tray::global().update_part());
log_err!(timer::Timer::global().init());
let enable_auto_light_weight_mode = { Config::verge().data().enable_auto_light_weight_mode };
if enable_auto_light_weight_mode.unwrap_or(false) {
lightweight::enable_auto_light_weight_mode();
}
logging_error!(Type::Tray, true, tray::Tray::global().update_part());
// 初始化热键
logging!(trace, Type::System, true, "Initial hotkeys");
logging_error!(Type::System, true, hotkey::Hotkey::global().init());
}
/// reset system proxy (异步版)
pub async fn resolve_reset_async() {
#[cfg(target_os = "macos")]
logging!(info, Type::Tray, true, "Unsubscribing from traffic updates");
#[cfg(target_os = "macos")]
tray::Tray::global().unsubscribe_traffic();
/// reset system proxy
pub fn resolve_reset() {
tauri::async_runtime::block_on(async move {
#[cfg(target_os = "macos")]
tray::Tray::global().unsubscribe_traffic();
logging_error!(
Type::System,
true,
sysopt::Sysopt::global().reset_sysproxy().await
);
logging_error!(Type::Core, true, CoreManager::global().stop_core().await);
#[cfg(target_os = "macos")]
{
logging!(info, Type::System, true, "Restoring system DNS settings");
log_err!(sysopt::Sysopt::global().reset_sysproxy().await);
log_err!(CoreManager::global().stop_core().await);
#[cfg(target_os = "macos")]
restore_public_dns().await;
}
});
}
/// create main window
pub fn create_window(is_showup: bool) {
logging!(info, Type::Window, true, "Creating window");
pub fn create_window() {
println!("Starting to create window");
log::info!(target: "app", "Starting to create window");
let app_handle = handle::Handle::global().app_handle().unwrap();
#[cfg(target_os = "macos")]
AppHandleManager::global().set_activation_policy_regular();
if let Some(window) = handle::Handle::global().get_window() {
logging!(
info,
Type::Window,
true,
"Found existing window, attempting to restore visibility"
);
println!("Found existing window, trying to show it");
log::info!(target: "app", "Found existing window, trying to show it");
if window.is_minimized().unwrap_or(false) {
logging!(
info,
Type::Window,
true,
"Window is minimized, restoring window state"
);
println!("Window is minimized, unminimizing");
log::info!(target: "app", "Window is minimized, unminimizing");
let _ = window.unminimize();
}
let _ = window.show();
@ -150,7 +154,8 @@ pub fn create_window(is_showup: bool) {
return;
}
logging!(info, Type::Window, true, "Creating new application window");
println!("Creating new window");
log::info!(target: "app", "Creating new window");
#[cfg(target_os = "windows")]
let window = tauri::WebviewWindowBuilder::new(
@ -196,46 +201,17 @@ pub fn create_window(is_showup: bool) {
match window {
Ok(window) => {
logging!(info, Type::Window, true, "Window created successfully");
if is_showup {
println!("is showup");
let _ = window.show();
let _ = window.set_focus();
} else {
let _ = window.hide();
#[cfg(target_os = "macos")]
AppHandleManager::global().set_activation_policy_accessory();
}
println!("Window created successfully, attempting to show");
log::info!(target: "app", "Window created successfully, attempting to show");
let _ = window.show();
let _ = window.set_focus();
// 设置窗口状态监控,实时保存窗口位置和大小
// crate::feat::setup_window_state_monitor(&app_handle);
// 标记前端UI已准备就绪向前端发送启动完成事件
let app_handle_clone = app_handle.clone();
tauri::async_runtime::spawn(async move {
use tauri::Emitter;
logging!(
info,
Type::Window,
true,
"标记前端UI已准备就绪开始处理启动错误队列"
);
handle::Handle::global().mark_startup_completed();
if let Some(window) = app_handle_clone.get_webview_window("main") {
let _ = window.emit("verge://startup-completed", ());
}
});
crate::feat::setup_window_state_monitor(&app_handle);
}
Err(e) => {
logging!(
error,
Type::Window,
true,
"Failed to create window: {:?}",
e
);
println!("Failed to create window: {:?}", e);
log::error!(target: "app", "Failed to create window: {:?}", e);
}
}
}
@ -243,6 +219,8 @@ pub fn create_window(is_showup: bool) {
pub async fn resolve_scheme(param: String) -> Result<()> {
log::info!(target:"app", "received deep link: {}", param);
let app_handle = handle::Handle::global().app_handle().unwrap();
let param_str = if param.starts_with("[") && param.len() > 4 {
param
.get(2..param.len() - 2)
@ -265,32 +243,41 @@ pub async fn resolve_scheme(param: String) -> Result<()> {
.find(|(key, _)| key == "name")
.map(|(_, value)| value.into_owned());
// 通过直接获取查询部分并解析特定参数来避免 URL 转义问题
let url_param = if let Some(query) = link_parsed.query() {
let prefix = "url=";
if let Some(pos) = query.find(prefix) {
let raw_url = &query[pos + prefix.len()..];
Some(percent_decode_str(raw_url).decode_utf8_lossy().to_string())
} else {
None
}
} else {
None
};
let encode_url = link_parsed
.query_pairs()
.find(|(key, _)| key == "url")
.map(|(_, value)| value.into_owned());
match url_param {
match encode_url {
Some(url) => {
log::info!(target:"app", "decoded subscription url: {}", url);
let url = percent_decode_str(url.as_ref())
.decode_utf8_lossy()
.to_string();
create_window(false);
create_window();
match PrfItem::from_url(url.as_ref(), name, None, None).await {
Ok(item) => {
let uid = item.uid.clone().unwrap();
let _ = wrap_err!(Config::profiles().data().append_item(item));
handle::Handle::notice_message("import_sub_url::ok", uid);
app_handle
.notification()
.builder()
.title("Clash Verge")
.body("Import profile success")
.show()
.unwrap();
}
Err(e) => {
handle::Handle::notice_message("import_sub_url::error", e.to_string());
app_handle
.notification()
.builder()
.title("Clash Verge")
.body(format!("Import profile failed: {e}"))
.show()
.unwrap();
}
}
}

View File

@ -3,8 +3,7 @@ extern crate warp;
use super::resolve;
use crate::{
config::{Config, IVerge, DEFAULT_PAC},
logging_error,
utils::logging::Type,
log_err,
};
use anyhow::{bail, Result};
use port_scanner::local_port_available;
@ -49,7 +48,7 @@ pub fn embed_server() {
tauri::async_runtime::spawn(async move {
let visible = warp::path!("commands" / "visible").map(move || {
resolve::create_window(false);
resolve::create_window();
"ok"
});
@ -70,11 +69,7 @@ pub fn embed_server() {
.unwrap_or_default()
});
async fn scheme_handler(query: QueryParam) -> Result<impl warp::Reply, Infallible> {
logging_error!(
Type::Setup,
true,
resolve::resolve_scheme(query.param).await
);
log_err!(resolve::resolve_scheme(query.param).await);
Ok("ok")
}

View File

@ -6,9 +6,9 @@ edition = "2024"
debug = []
[dependencies]
reqwest = { version = "0.12.15", features = ["json"] }
serde = { version = "1.0.219", features = ["derive"] }
reqwest = { version = "0.12.12", features = ["json"] }
serde = { version = "1.0.218", features = ["derive"] }
serde_json = "1.0.140"
[dev-dependencies]
tokio = { version = "1.44.1", features = ["rt", "macros"] }
tokio = { version = "1.43.0", features = ["rt", "macros"] }

View File

@ -140,23 +140,4 @@ impl MihomoManager {
let response = self.send_request(Method::GET, url, None).await?;
Ok(response)
}
pub async fn get_connections(&self) -> Result<serde_json::Value, String> {
let url = format!("{}/connections", self.mihomo_server);
let response = self.send_request(Method::GET, url, None).await?;
Ok(response)
}
pub async fn delete_connection(&self, id: &str) -> Result<(), String> {
let url = format!("{}/connections/{}", self.mihomo_server, id);
let response = self.send_request(Method::DELETE, url, None).await?;
if response["code"] == 204 {
Ok(())
} else {
Err(response["message"]
.as_str()
.unwrap_or("unknown error")
.to_string())
}
}
}

View File

@ -1,5 +1,5 @@
{
"version": "2.2.3",
"version": "2.2.1",
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"bundle": {
"active": true,
@ -11,15 +11,9 @@
"icons/icon.icns",
"icons/icon.ico"
],
"resources": [
"resources",
"resources/locales/*"
],
"resources": ["resources", "resources/locales/*"],
"publisher": "Clash Verge Rev",
"externalBin": [
"sidecar/verge-mihomo",
"sidecar/verge-mihomo-alpha"
],
"externalBin": ["sidecar/verge-mihomo", "sidecar/verge-mihomo-alpha"],
"copyright": "GNU General Public License v3.0",
"category": "DeveloperTool",
"shortDescription": "Clash Verge Rev",
@ -36,42 +30,24 @@
"plugins": {
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEQyOEMyRjBCQkVGOUJEREYKUldUZnZmbStDeStNMHU5Mmo1N24xQXZwSVRYbXA2NUpzZE5oVzlqeS9Bc0t6RVV4MmtwVjBZaHgK",
"endpoints": [
"https://download.clashverge.dev/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-proxy.json",
"https://gh-proxy.com/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-proxy.json",
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update.json",
"https://download.clashverge.dev/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater-alpha/update-alpha-proxy.json",
"https://gh-proxy.com/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater-alpha/update-alpha-proxy.json",
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater-alpha/update-alpha.json"
],
"windows": {
"installMode": "basicUi"
}
},
"deep-link": {
"desktop": {
"schemes": [
"clash",
"clash-verge"
]
"schemes": ["clash", "clash-verge"]
}
}
},
"app": {
"security": {
"capabilities": [
"desktop-capability",
"migrated"
],
"capabilities": ["desktop-capability", "migrated"],
"assetProtocol": {
"scope": [
"$APPDATA/**",
"$RESOURCE/../**",
"**"
],
"scope": ["$APPDATA/**", "$RESOURCE/../**", "**"],
"enable": true
},
"csp": null
}
}
}
}

View File

@ -1,13 +0,0 @@
import { AppDataProvider } from "./providers/app-data-provider";
import React from "react";
import Layout from "./pages/_layout";
function App() {
return (
<AppDataProvider>
<Layout />
</AppDataProvider>
);
}
export default App;

View File

@ -1,10 +1,13 @@
import { useTranslation } from "react-i18next";
import { Typography, Stack, Divider } from "@mui/material";
import { DeveloperBoardOutlined } from "@mui/icons-material";
import { useClashInfo } from "@/hooks/use-clash";
import { useClash } from "@/hooks/use-clash";
import { EnhancedCard } from "./enhanced-card";
import { useMemo } from "react";
import { useAppData } from "@/providers/app-data-provider";
import useSWR from "swr";
import { getRules } from "@/services/api";
import { getAppUptime, getSystemProxy } from "@/services/cmds";
import { useMemo, useState, useEffect } from "react";
// 将毫秒转换为时:分:秒格式的函数
const formatUptime = (uptimeMs: number) => {
@ -16,15 +19,37 @@ const formatUptime = (uptimeMs: number) => {
export const ClashInfoCard = () => {
const { t } = useTranslation();
const { clashInfo } = useClashInfo();
const { version: clashVersion } = useClash();
const { clashConfig, sysproxy, rules, uptime } = useAppData();
const [sysproxy, setSysproxy] = useState<{ server: string; enable: boolean; bypass: string } | null>(null);
const [rules, setRules] = useState<any[]>([]);
// 使用SWR获取应用运行时间降低更新频率
const { data: uptimeMs = 0 } = useSWR(
"appUptime",
getAppUptime,
{
refreshInterval: 1000,
revalidateOnFocus: false,
dedupingInterval: 1000,
},
);
// 在组件加载时获取系统代理信息和规则数据
useEffect(() => {
// 获取系统代理信息
getSystemProxy().then(setSysproxy);
// 获取规则数据
getRules().then(setRules).catch(() => setRules([]));
}, []);
// 使用useMemo缓存格式化后的uptime避免频繁计算
const formattedUptime = useMemo(() => formatUptime(uptime), [uptime]);
const uptime = useMemo(() => formatUptime(uptimeMs), [uptimeMs]);
// 使用备忘录组件内容,减少重新渲染
const cardContent = useMemo(() => {
if (!clashConfig) return null;
if (!clashInfo) return null;
return (
<Stack spacing={1.5}>
@ -51,7 +76,7 @@ export const ClashInfoCard = () => {
{t("Mixed Port")}
</Typography>
<Typography variant="body2" fontWeight="medium">
{clashConfig["mixed-port"] || "-"}
{clashInfo.mixed_port || "-"}
</Typography>
</Stack>
<Divider />
@ -60,7 +85,7 @@ export const ClashInfoCard = () => {
{t("Uptime")}
</Typography>
<Typography variant="body2" fontWeight="medium">
{formattedUptime}
{uptime}
</Typography>
</Stack>
<Divider />
@ -74,7 +99,7 @@ export const ClashInfoCard = () => {
</Stack>
</Stack>
);
}, [clashConfig, clashVersion, t, formattedUptime, rules.length, sysproxy]);
}, [clashInfo, clashVersion, t, uptime, rules.length, sysproxy]);
return (
<EnhancedCard

View File

@ -1,7 +1,8 @@
import { useTranslation } from "react-i18next";
import { Box, Typography, Paper, Stack, Fade } from "@mui/material";
import { useLockFn } from "ahooks";
import { closeAllConnections } from "@/services/api";
import useSWR from "swr";
import { closeAllConnections, getClashConfig } from "@/services/api";
import { patchClashMode } from "@/services/cmds";
import { useVerge } from "@/hooks/use-verge";
import {
@ -10,12 +11,22 @@ import {
DirectionsRounded,
} from "@mui/icons-material";
import { useMemo } from "react";
import { useAppData } from "@/providers/app-data-provider";
export const ClashModeCard = () => {
const { t } = useTranslation();
const { verge } = useVerge();
const { clashConfig, refreshProxy } = useAppData();
// 获取当前Clash配置
const { data: clashConfig, mutate: mutateClash } = useSWR(
"getClashConfig",
getClashConfig,
{
revalidateOnFocus: false,
revalidateIfStale: true,
dedupingInterval: 1000,
errorRetryInterval: 5000
}
);
// 支持的模式列表
const modeList = useMemo(() => ["rule", "global", "direct"] as const, []);
@ -39,8 +50,7 @@ export const ClashModeCard = () => {
try {
await patchClashMode(mode);
// 使用共享的刷新方法
refreshProxy();
mutateClash();
} catch (error) {
console.error("Failed to change mode:", error);
}

View File

@ -13,7 +13,7 @@ import {
SelectChangeEvent,
Tooltip,
} from "@mui/material";
import { useEffect, useState, useMemo, useCallback } from "react";
import { useEffect, useState, useMemo, useCallback, useRef } from "react";
import {
SignalWifi4Bar as SignalStrong,
SignalWifi3Bar as SignalGood,
@ -24,11 +24,16 @@ import {
ChevronRight,
} from "@mui/icons-material";
import { useNavigate } from "react-router-dom";
import { useCurrentProxy } from "@/hooks/use-current-proxy";
import { EnhancedCard } from "@/components/home/enhanced-card";
import { updateProxy, deleteConnection } from "@/services/api";
import {
getProxies,
updateProxy,
getConnections,
deleteConnection,
} from "@/services/api";
import delayManager from "@/services/delay";
import { useVerge } from "@/hooks/use-verge";
import { useAppData } from "@/providers/app-data-provider";
// 本地存储的键名
const STORAGE_KEY_GROUP = "clash-verge-selected-proxy-group";
@ -87,16 +92,21 @@ function debounce(fn: Function, ms = 100) {
export const CurrentProxyCard = () => {
const { t } = useTranslation();
const { currentProxy, primaryGroupName, mode, refreshProxy } =
useCurrentProxy();
const navigate = useNavigate();
const theme = useTheme();
const { verge } = useVerge();
const { proxies, connections, clashConfig, refreshProxy } = useAppData();
// 判断模式
const mode = clashConfig?.mode?.toLowerCase() || "rule";
const isGlobalMode = mode === "global";
const isDirectMode = mode === "direct";
// 使用 useRef 存储最后一次刷新时间和是否正在刷新
const lastRefreshRef = useRef<number>(0);
const isRefreshingRef = useRef<boolean>(false);
const pendingRefreshRef = useRef<boolean>(false);
// 定义状态类型
type ProxyState = {
proxyData: {
@ -129,32 +139,6 @@ export const CurrentProxyCard = () => {
// 初始化选择的组
useEffect(() => {
if (!proxies) return;
// 提取primaryGroupName
const getPrimaryGroupName = () => {
if (!proxies?.groups?.length) return "";
// 查找主要的代理组(优先级:包含关键词 > 第一个非GLOBAL组
const primaryKeywords = [
"auto",
"select",
"proxy",
"节点选择",
"自动选择",
];
const primaryGroup =
proxies.groups.find((group: { name: string }) =>
primaryKeywords.some((keyword) =>
group.name.toLowerCase().includes(keyword.toLowerCase()),
),
) || proxies.groups.filter((g: { name: string }) => g.name !== "GLOBAL")[0];
return primaryGroup?.name || "";
};
const primaryGroupName = getPrimaryGroupName();
// 根据模式确定初始组
if (isGlobalMode) {
setState((prev) => ({
@ -182,85 +166,153 @@ export const CurrentProxyCard = () => {
},
}));
}
}, [isGlobalMode, isDirectMode, proxies]);
}, [isGlobalMode, isDirectMode, primaryGroupName]);
// 监听代理数据变化,更新状态
useEffect(() => {
if (!proxies) return;
// 使用函数式更新确保状态更新的原子性
setState((prev) => {
// 过滤和格式化组
const filteredGroups = proxies.groups
.filter((g: { name: string }) => g.name !== "DIRECT" && g.name !== "REJECT")
.map((g: { name: string; now: string; all: Array<{ name: string }> }) => ({
name: g.name,
now: g.now || "",
all: g.all.map((p: { name: string }) => p.name),
}));
let newProxy = "";
let newDisplayProxy = null;
let newGroup = prev.selection.group;
// 根据模式确定新代理
if (isDirectMode) {
newGroup = "DIRECT";
newProxy = "DIRECT";
newDisplayProxy = proxies.records?.DIRECT || null;
} else if (isGlobalMode && proxies.global) {
newGroup = "GLOBAL";
newProxy = proxies.global.now || "";
newDisplayProxy = proxies.records?.[newProxy] || null;
} else {
// 普通模式 - 检查当前选择的组是否存在
const currentGroup = filteredGroups.find(
(g: { name: string }) => g.name === prev.selection.group,
);
// 如果当前组不存在或为空,自动选择第一个组
if (!currentGroup && filteredGroups.length > 0) {
newGroup = filteredGroups[0].name;
const firstGroup = filteredGroups[0];
newProxy = firstGroup.now;
newDisplayProxy = proxies.records?.[newProxy] || null;
// 保存到本地存储
if (!isGlobalMode && !isDirectMode) {
localStorage.setItem(STORAGE_KEY_GROUP, newGroup);
if (newProxy) {
localStorage.setItem(STORAGE_KEY_PROXY, newProxy);
}
}
} else if (currentGroup) {
// 使用当前组的代理
newProxy = currentGroup.now;
newDisplayProxy = proxies.records?.[newProxy] || null;
}
// 带锁的代理数据获取函数,防止并发请求
const fetchProxyData = useCallback(
async (force = false) => {
// 防止重复请求
if (isRefreshingRef.current) {
pendingRefreshRef.current = true;
return;
}
// 返回新状态
return {
proxyData: {
groups: filteredGroups,
records: proxies.records || {},
globalProxy: proxies.global?.now || "",
directProxy: proxies.records?.DIRECT || null,
},
selection: {
group: newGroup,
proxy: newProxy,
},
displayProxy: newDisplayProxy,
};
});
}, [proxies, isGlobalMode, isDirectMode]);
// 检查刷新间隔
const now = Date.now();
if (!force && now - lastRefreshRef.current < 1000) {
return;
}
// 使用防抖包装状态更新,避免快速连续更新,增加防抖时间
isRefreshingRef.current = true;
lastRefreshRef.current = now;
try {
const data = await getProxies();
// 过滤和格式化组
const filteredGroups = data.groups
.filter((g) => g.name !== "DIRECT" && g.name !== "REJECT")
.map((g) => ({
name: g.name,
now: g.now || "",
all: g.all.map((p) => p.name),
}));
// 使用函数式更新确保状态更新的原子性
setState((prev) => {
let newProxy = "";
let newDisplayProxy = null;
let newGroup = prev.selection.group;
// 根据模式确定新代理
if (isDirectMode) {
newGroup = "DIRECT";
newProxy = "DIRECT";
newDisplayProxy = data.records?.DIRECT || null;
} else if (isGlobalMode && data.global) {
newGroup = "GLOBAL";
newProxy = data.global.now || "";
newDisplayProxy = data.records?.[newProxy] || null;
} else {
// 普通模式 - 检查当前选择的组是否存在
const currentGroup = filteredGroups.find(
(g) => g.name === prev.selection.group,
);
// 如果当前组不存在或为空,自动选择第一个组
if (!currentGroup && filteredGroups.length > 0) {
newGroup = filteredGroups[0].name;
const firstGroup = filteredGroups[0];
newProxy = firstGroup.now;
newDisplayProxy = data.records?.[newProxy] || null;
// 保存到本地存储
if (!isGlobalMode && !isDirectMode) {
localStorage.setItem(STORAGE_KEY_GROUP, newGroup);
if (newProxy) {
localStorage.setItem(STORAGE_KEY_PROXY, newProxy);
}
}
} else if (currentGroup) {
// 使用当前组的代理
newProxy = currentGroup.now;
newDisplayProxy = data.records?.[newProxy] || null;
}
}
// 返回新状态
return {
proxyData: {
groups: filteredGroups,
records: data.records || {},
globalProxy: data.global?.now || "",
directProxy: data.records?.DIRECT || null,
},
selection: {
group: newGroup,
proxy: newProxy,
},
displayProxy: newDisplayProxy,
};
});
} catch (error) {
console.error("获取代理信息失败", error);
} finally {
isRefreshingRef.current = false;
// 处理待处理的刷新请求
if (pendingRefreshRef.current) {
pendingRefreshRef.current = false;
setTimeout(() => fetchProxyData(), 100);
}
}
},
[isGlobalMode, isDirectMode],
);
// 响应 currentProxy 变化
useEffect(() => {
if (
currentProxy &&
(!state.displayProxy || currentProxy.name !== state.displayProxy.name)
) {
fetchProxyData(true);
}
}, [currentProxy, fetchProxyData, state.displayProxy]);
// 监听模式变化mode变化时刷新
useEffect(() => {
fetchProxyData(true);
}, [mode, fetchProxyData]);
// 计算要显示的代理选项 - 使用 useMemo 优化
const proxyOptions = useMemo(() => {
if (isDirectMode) {
return [{ name: "DIRECT" }];
}
if (isGlobalMode && state.proxyData.records) {
// 全局模式下的选项
return Object.keys(state.proxyData.records)
.filter((name) => name !== "DIRECT" && name !== "REJECT")
.map((name) => ({ name }));
}
// 普通模式
const group = state.proxyData.groups.find(
(g) => g.name === state.selection.group,
);
if (group) {
return group.all.map((name) => ({ name }));
}
return [];
}, [isDirectMode, isGlobalMode, state.proxyData, state.selection.group]);
// 使用防抖包装状态更新,避免快速连续更新
const debouncedSetState = useCallback(
debounce((updateFn: (prev: ProxyState) => ProxyState) => {
setState(updateFn);
}, 300),
}, 50),
[],
);
@ -276,7 +328,7 @@ export const CurrentProxyCard = () => {
// 获取该组当前选中的代理
setState((prev) => {
const group = prev.proxyData.groups.find((g: { name: string }) => g.name === newGroup);
const group = prev.proxyData.groups.find((g) => g.name === newGroup);
if (group) {
return {
...prev,
@ -329,17 +381,20 @@ export const CurrentProxyCard = () => {
// 自动关闭连接设置
if (verge?.auto_close_connection && previousProxy) {
connections.data.forEach((conn: any) => {
if (conn.chains.includes(previousProxy)) {
deleteConnection(conn.id);
}
getConnections().then(({ connections }) => {
connections.forEach((conn) => {
if (conn.chains.includes(previousProxy)) {
deleteConnection(conn.id);
}
});
});
}
// 延长刷新延迟时间
// 刷新代理信息,使用较短的延迟
setTimeout(() => {
refreshProxy();
}, 500);
fetchProxyData(true);
}, 200);
} catch (error) {
console.error("更新代理失败", error);
}
@ -351,8 +406,8 @@ export const CurrentProxyCard = () => {
state.selection,
verge?.auto_close_connection,
refreshProxy,
fetchProxyData,
debouncedSetState,
connections.data,
],
);
@ -362,14 +417,11 @@ export const CurrentProxyCard = () => {
}, [navigate]);
// 获取要显示的代理节点
const currentProxy = useMemo(() => {
// 从state中获取当前代理信息
return state.displayProxy;
}, [state.displayProxy]);
const proxyToDisplay = state.displayProxy || currentProxy;
// 获取当前节点的延迟
const currentDelay = currentProxy
? delayManager.getDelayFix(currentProxy, state.selection.group)
const currentDelay = proxyToDisplay
? delayManager.getDelayFix(proxyToDisplay, state.selection.group)
: -1;
// 获取信号图标
@ -399,45 +451,23 @@ export const CurrentProxyCard = () => {
[state.proxyData.records, state.selection.group],
);
// 计算要显示的代理选项 - 使用 useMemo 优化
const proxyOptions = useMemo(() => {
if (isDirectMode) {
return [{ name: "DIRECT" }];
}
if (isGlobalMode && state.proxyData.records) {
// 全局模式下的选项
return Object.keys(state.proxyData.records)
.filter((name) => name !== "DIRECT" && name !== "REJECT")
.map((name) => ({ name }));
}
// 普通模式
const group = state.proxyData.groups.find(
(g: { name: string }) => g.name === state.selection.group,
);
if (group) {
return group.all.map((name) => ({ name }));
}
return [];
}, [isDirectMode, isGlobalMode, state.proxyData, state.selection.group]);
return (
<EnhancedCard
title={t("Current Node")}
icon={
<Tooltip
title={
currentProxy
proxyToDisplay
? `${signalInfo.text}: ${delayManager.formatDelay(currentDelay)}`
: "无代理节点"
}
>
<Box sx={{ color: signalInfo.color }}>
{currentProxy ? signalInfo.icon : <SignalNone color="disabled" />}
{proxyToDisplay ? signalInfo.icon : <SignalNone color="disabled" />}
</Box>
</Tooltip>
}
iconColor={currentProxy ? "primary" : undefined}
iconColor={proxyToDisplay ? "primary" : undefined}
action={
<Button
variant="outlined"
@ -450,7 +480,7 @@ export const CurrentProxyCard = () => {
</Button>
}
>
{currentProxy ? (
{proxyToDisplay ? (
<Box>
{/* 代理节点信息显示 */}
<Box
@ -467,7 +497,7 @@ export const CurrentProxyCard = () => {
>
<Box>
<Typography variant="body1" fontWeight="medium">
{currentProxy.name}
{proxyToDisplay.name}
</Typography>
<Box
@ -478,7 +508,7 @@ export const CurrentProxyCard = () => {
color="text.secondary"
sx={{ mr: 1 }}
>
{currentProxy.type}
{proxyToDisplay.type}
</Typography>
{isGlobalMode && (
<Chip
@ -497,26 +527,26 @@ export const CurrentProxyCard = () => {
/>
)}
{/* 节点特性 */}
{currentProxy.udp && (
{proxyToDisplay.udp && (
<Chip size="small" label="UDP" variant="outlined" />
)}
{currentProxy.tfo && (
{proxyToDisplay.tfo && (
<Chip size="small" label="TFO" variant="outlined" />
)}
{currentProxy.xudp && (
{proxyToDisplay.xudp && (
<Chip size="small" label="XUDP" variant="outlined" />
)}
{currentProxy.mptcp && (
{proxyToDisplay.mptcp && (
<Chip size="small" label="MPTCP" variant="outlined" />
)}
{currentProxy.smux && (
{proxyToDisplay.smux && (
<Chip size="small" label="SMUX" variant="outlined" />
)}
</Box>
</Box>
{/* 显示延迟 */}
{currentProxy && !isDirectMode && (
{proxyToDisplay && !isDirectMode && (
<Chip
size="small"
label={delayManager.formatDelay(currentDelay)}
@ -567,14 +597,14 @@ export const CurrentProxyCard = () => {
>
{isDirectMode
? null
: proxyOptions.map((proxy, index) => {
: proxyOptions.map((proxy) => {
const delayValue = delayManager.getDelayFix(
state.proxyData.records[proxy.name],
state.selection.group,
);
return (
<MenuItem
key={`${proxy.name}-${index}`}
key={proxy.name}
value={proxy.name}
sx={{
display: "flex",

View File

@ -39,35 +39,69 @@ export interface EnhancedTrafficGraphRef {
// 时间范围类型
type TimeRange = 1 | 5 | 10; // 分钟
// 数据点类型
// 创建一个明确的类型
type DataPoint = ITrafficItem & { name: string; timestamp: number };
// 控制帧率的工具函数
const FPS_LIMIT = 1; // 限制为1fps因为数据每秒才更新一次
const FRAME_MIN_TIME = 1000 / FPS_LIMIT; // 每帧最小时间间隔即1000ms
// 全局存储流量数据历史记录
declare global {
interface Window {
trafficHistoryData?: DataPoint[];
trafficHistoryStyle?: "line" | "area";
trafficHistoryTimeRange?: TimeRange;
}
}
// 初始化全局存储
if (typeof window !== "undefined" && !window.trafficHistoryData) {
window.trafficHistoryData = [];
window.trafficHistoryStyle = "area";
window.trafficHistoryTimeRange = 10;
}
/**
*
* Recharts 线
*/
export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
(props, ref) => {
const theme = useTheme();
const { t } = useTranslation();
// 基础状态
const [timeRange, setTimeRange] = useState<TimeRange>(10);
const [chartStyle, setChartStyle] = useState<"line" | "area">("area");
// 从全局变量恢复状态
const [timeRange, setTimeRange] = useState<TimeRange>(
window.trafficHistoryTimeRange || 10
);
const [chartStyle, setChartStyle] = useState<"line" | "area">(
window.trafficHistoryStyle || "area"
);
// 使用useRef存储数据避免不必要的重渲染
const dataBufferRef = useRef<DataPoint[]>([]);
// 只为渲染目的的状态
const [displayData, setDisplayData] = useState<DataPoint[]>([]);
// 数据缓冲区
const dataBufferRef = useRef<DataPoint[]>([]);
// 帧率控制
const lastUpdateTimeRef = useRef<number>(0);
const pendingUpdateRef = useRef<boolean>(false);
const rafIdRef = useRef<number | null>(null);
// 根据时间范围计算保留的数据点数量
const getMaxPointsByTimeRange = useCallback(
(minutes: TimeRange): number => minutes * 60,
[]
(minutes: TimeRange): number => {
// 使用更低的采样率来减少点的数量每2秒一个点而不是每秒一个点
return minutes * 30; // 每分钟30个点(每2秒1个点)
},
[],
);
// 最大数据点数量
// 最大数据点数量 - 基于选择的时间范围
const MAX_BUFFER_SIZE = useMemo(
() => getMaxPointsByTimeRange(10),
[getMaxPointsByTimeRange]
[getMaxPointsByTimeRange],
);
// 颜色配置
@ -79,50 +113,149 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
tooltip: theme.palette.background.paper,
text: theme.palette.text.primary,
}),
[theme]
[theme],
);
// 切换时间范围
const handleTimeRangeClick = useCallback(() => {
setTimeRange((prevRange) => {
// 在1、5、10分钟之间循环切换
return prevRange === 1 ? 5 : prevRange === 5 ? 10 : 1;
const newRange = prevRange === 1 ? 5 : prevRange === 5 ? 10 : 1;
window.trafficHistoryTimeRange = newRange; // 保存到全局
return newRange;
});
}, []);
// 初始化数据缓冲区
useEffect(() => {
// 创建初始空数据
const now = Date.now();
const tenMinutesAgo = now - 10 * 60 * 1000;
const initialBuffer = Array.from(
{ length: MAX_BUFFER_SIZE },
(_, index) => {
const pointTime =
tenMinutesAgo + index * ((10 * 60 * 1000) / MAX_BUFFER_SIZE);
const date = new Date(pointTime);
return {
up: 0,
down: 0,
timestamp: pointTime,
name: date.toLocaleTimeString("en-US", {
hour12: false,
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}),
};
let initialBuffer: DataPoint[] = [];
// 如果全局有保存的数据,优先使用
if (window.trafficHistoryData && window.trafficHistoryData.length > 0) {
initialBuffer = [...window.trafficHistoryData];
// 确保数据长度符合要求
if (initialBuffer.length > MAX_BUFFER_SIZE) {
initialBuffer = initialBuffer.slice(-MAX_BUFFER_SIZE);
} else if (initialBuffer.length < MAX_BUFFER_SIZE) {
// 如果历史数据不足,则在前面补充空数据
const now = Date.now();
const oldestTimestamp = initialBuffer.length > 0
? initialBuffer[0].timestamp
: now - 10 * 60 * 1000;
const additionalPoints = MAX_BUFFER_SIZE - initialBuffer.length;
const timeInterval = initialBuffer.length > 0
? (initialBuffer[0].timestamp - (now - 10 * 60 * 1000)) / additionalPoints
: (10 * 60 * 1000) / MAX_BUFFER_SIZE;
const emptyPrefix: DataPoint[] = Array.from(
{ length: additionalPoints },
(_, index) => {
const pointTime = oldestTimestamp - (additionalPoints - index) * timeInterval;
const date = new Date(pointTime);
return {
up: 0,
down: 0,
timestamp: pointTime,
name: date.toLocaleTimeString("en-US", {
hour12: false,
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}),
};
}
);
initialBuffer = [...emptyPrefix, ...initialBuffer];
}
);
} else {
// 没有历史数据时,创建空的初始缓冲区
const now = Date.now();
const tenMinutesAgo = now - 10 * 60 * 1000;
initialBuffer = Array.from(
{ length: MAX_BUFFER_SIZE },
(_, index) => {
const pointTime =
tenMinutesAgo + index * ((10 * 60 * 1000) / MAX_BUFFER_SIZE);
const date = new Date(pointTime);
return {
up: 0,
down: 0,
timestamp: pointTime,
name: date.toLocaleTimeString("en-US", {
hour12: false,
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}),
};
}
);
}
dataBufferRef.current = initialBuffer;
window.trafficHistoryData = initialBuffer; // 保存到全局
// 更新显示数据
const pointsToShow = getMaxPointsByTimeRange(timeRange);
setDisplayData(initialBuffer.slice(-pointsToShow));
}, [MAX_BUFFER_SIZE, getMaxPointsByTimeRange]);
// 清理函数,取消任何未完成的动画帧
return () => {
if (rafIdRef.current !== null) {
cancelAnimationFrame(rafIdRef.current);
rafIdRef.current = null;
}
};
}, [MAX_BUFFER_SIZE, getMaxPointsByTimeRange, timeRange]);
// 处理数据更新并控制帧率的函数
const updateDisplayData = useCallback(() => {
if (pendingUpdateRef.current) {
pendingUpdateRef.current = false;
// 根据当前时间范围计算需要显示的点数
const pointsToShow = getMaxPointsByTimeRange(timeRange);
// 从缓冲区中获取最新的数据点
const newDisplayData = dataBufferRef.current.slice(-pointsToShow);
setDisplayData(newDisplayData);
}
rafIdRef.current = null;
}, [timeRange, getMaxPointsByTimeRange]);
// 节流更新函数
const throttledUpdateData = useCallback(() => {
pendingUpdateRef.current = true;
const now = performance.now();
const timeSinceLastUpdate = now - lastUpdateTimeRef.current;
if (rafIdRef.current === null) {
if (timeSinceLastUpdate >= FRAME_MIN_TIME) {
// 如果距离上次更新已经超过最小帧时间,立即更新
lastUpdateTimeRef.current = now;
rafIdRef.current = requestAnimationFrame(updateDisplayData);
} else {
// 否则,在适当的时间进行更新
const timeToWait = FRAME_MIN_TIME - timeSinceLastUpdate;
setTimeout(() => {
lastUpdateTimeRef.current = performance.now();
rafIdRef.current = requestAnimationFrame(updateDisplayData);
}, timeToWait);
}
}
}, [updateDisplayData]);
// 监听时间范围变化,更新显示数据
useEffect(() => {
throttledUpdateData();
}, [timeRange, throttledUpdateData]);
// 添加数据点方法
const appendData = useCallback((data: ITrafficItem) => {
@ -148,24 +281,24 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
timestamp: timestamp,
};
// 更新缓冲区,保持原数组大小
// 更新ref保持原数组大小
const newBuffer = [...dataBufferRef.current.slice(1), newPoint];
dataBufferRef.current = newBuffer;
// 更新显示数据
const pointsToShow = getMaxPointsByTimeRange(timeRange);
setDisplayData(newBuffer.slice(-pointsToShow));
}, [timeRange, getMaxPointsByTimeRange]);
// 监听时间范围变化,更新显示数据
useEffect(() => {
const pointsToShow = getMaxPointsByTimeRange(timeRange);
setDisplayData(dataBufferRef.current.slice(-pointsToShow));
}, [timeRange, getMaxPointsByTimeRange]);
// 保存到全局变量
window.trafficHistoryData = newBuffer;
// 使用节流更新显示数据
throttledUpdateData();
}, [throttledUpdateData]);
// 切换图表样式
const toggleStyle = useCallback(() => {
setChartStyle((prev) => prev === "line" ? "area" : "line");
setChartStyle((prev) => {
const newStyle = prev === "line" ? "area" : "line";
window.trafficHistoryStyle = newStyle; // 保存到全局
return newStyle;
});
}, []);
// 暴露方法给父组件
@ -175,12 +308,13 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
appendData,
toggleStyle,
}),
[appendData, toggleStyle]
[appendData, toggleStyle],
);
// 格式化工具提示内容
const formatTooltip = useCallback((value: number, name: string, props: any) => {
const [num, unit] = parseTraffic(value);
// 使用props.dataKey判断是上传还是下载
return [`${num} ${unit}/s`, props?.dataKey === "up" ? t("Upload") : t("Download")];
}, [t]);
@ -193,6 +327,7 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
// 格式化X轴标签
const formatXLabel = useCallback((value: string) => {
if (!value) return "";
// 只显示小时和分钟
const parts = value.split(":");
return `${parts[0]}:${parts[1]}`;
}, []);
@ -205,7 +340,7 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
// 共享图表配置
const chartConfig = useMemo(() => ({
data: displayData,
margin: { top: 20, right: 10, left: 0, bottom: -10 },
margin: { top: 10, right: 20, left: 0, bottom: 0 },
}), [displayData]);
// 共享的线条/区域配置
@ -217,7 +352,7 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
isAnimationActive: false, // 禁用动画以减少CPU使用
}), []);
// 曲线类型
// 曲线类型 - 使用线性曲线避免错位
const curveType = "monotone";
return (
@ -228,138 +363,186 @@ export const EnhancedTrafficGraph = memo(forwardRef<EnhancedTrafficGraphRef>(
position: "relative",
bgcolor: "action.hover",
borderRadius: 1,
padding: 1,
cursor: "pointer",
}}
onClick={toggleStyle}
>
<ResponsiveContainer width="100%" height="100%">
{/* 根据chartStyle动态选择图表类型 */}
{(() => {
// 创建共享的图表组件
const commonChartComponents = (
<>
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke={colors.grid} opacity={0.3} />
<XAxis
dataKey="name"
tick={{ fontSize: 10, fill: colors.text }}
tickLine={{ stroke: colors.grid }}
axisLine={{ stroke: colors.grid }}
interval="preserveStart"
tickFormatter={formatXLabel}
minTickGap={30}
/>
<YAxis
tickFormatter={formatYAxis}
tick={{ fontSize: 10, fill: colors.text }}
tickLine={{ stroke: colors.grid }}
axisLine={{ stroke: colors.grid }}
width={44}
domain={[0, "auto"]}
padding={{ top: 8, bottom: 0 }}
/>
<Tooltip
formatter={formatTooltip}
labelFormatter={(label) => `${t("Time")}: ${label}`}
contentStyle={{
backgroundColor: colors.tooltip,
borderColor: colors.grid,
borderRadius: 4,
}}
itemStyle={{ color: colors.text }}
isAnimationActive={false}
/>
{/* 可点击的时间范围标签 */}
<text
x="1%"
y="11%"
textAnchor="start"
fill={theme.palette.text.secondary}
fontSize={11}
fontWeight="bold"
onClick={handleTimeRangeClick}
style={{ cursor: "pointer" }}
>
{getTimeRangeText()}
</text>
{/* 上传标签 - 右上角 */}
<text
x="99%"
y="11%"
textAnchor="end"
fill={colors.up}
fontSize={12}
fontWeight="bold"
onClick={toggleStyle}
style={{ cursor: "pointer" }}
>
{t("Upload")}
</text>
{chartStyle === "line" ? (
<LineChart {...chartConfig}>
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke={colors.grid} opacity={0.3} />
<XAxis
dataKey="name"
tick={{ fontSize: 10, fill: colors.text }}
tickLine={{ stroke: colors.grid }}
axisLine={{ stroke: colors.grid }}
interval="preserveStart"
tickFormatter={formatXLabel}
minTickGap={30}
/>
<YAxis
tickFormatter={formatYAxis}
tick={{ fontSize: 10, fill: colors.text }}
tickLine={{ stroke: colors.grid }}
axisLine={{ stroke: colors.grid }}
width={40}
domain={[0, "auto"]}
/>
<Tooltip
formatter={formatTooltip}
labelFormatter={(label) => `${t("Time")}: ${label}`}
contentStyle={{
backgroundColor: colors.tooltip,
borderColor: colors.grid,
borderRadius: 4,
}}
itemStyle={{ color: colors.text }}
isAnimationActive={false}
/>
<Line
type={curveType}
{...commonLineProps}
dataKey="up"
name={t("Upload")}
stroke={colors.up}
/>
<Line
type={curveType}
{...commonLineProps}
dataKey="down"
name={t("Download")}
stroke={colors.down}
/>
{/* 可点击的时间范围标签 */}
<text
x="1%"
y="6%"
textAnchor="start"
fill={theme.palette.text.secondary}
fontSize={11}
fontWeight="bold"
onClick={handleTimeRangeClick}
style={{ cursor: "pointer" }}
>
{getTimeRangeText()}
</text>
{/* 上传标签 - 右上角 */}
<text
x="98%"
y="7%"
textAnchor="end"
fill={colors.up}
fontSize={12}
fontWeight="bold"
>
{t("Upload")}
</text>
{/* 下载标签 - 右上角下方 */}
<text
x="99%"
y="19%"
textAnchor="end"
fill={colors.down}
fontSize={12}
fontWeight="bold"
onClick={toggleStyle}
style={{ cursor: "pointer" }}
>
{t("Download")}
</text>
</>
);
{/* 下载标签 - 右上角下方 */}
<text
x="98%"
y="16%"
textAnchor="end"
fill={colors.down}
fontSize={12}
fontWeight="bold"
>
{t("Download")}
</text>
</LineChart>
) : (
<AreaChart {...chartConfig}>
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke={colors.grid} opacity={0.3} />
<XAxis
dataKey="name"
tick={{ fontSize: 10, fill: colors.text }}
tickLine={{ stroke: colors.grid }}
axisLine={{ stroke: colors.grid }}
interval="preserveStart"
tickFormatter={formatXLabel}
minTickGap={30}
/>
<YAxis
tickFormatter={formatYAxis}
tick={{ fontSize: 10, fill: colors.text }}
tickLine={{ stroke: colors.grid }}
axisLine={{ stroke: colors.grid }}
width={40}
domain={[0, "auto"]}
padding={{ top: 10, bottom: 0 }}
/>
<Tooltip
formatter={formatTooltip}
labelFormatter={(label) => `${t("Time")}: ${label}`}
contentStyle={{
backgroundColor: colors.tooltip,
borderColor: colors.grid,
borderRadius: 4,
}}
itemStyle={{ color: colors.text }}
isAnimationActive={false}
/>
<Area
type={curveType}
{...commonLineProps}
dataKey="up"
name={t("Upload")}
stroke={colors.up}
fill={colors.up}
fillOpacity={0.2}
/>
<Area
type={curveType}
{...commonLineProps}
dataKey="down"
name={t("Download")}
stroke={colors.down}
fill={colors.down}
fillOpacity={0.3}
/>
{/* 可点击的时间范围标签 */}
<text
x="1%"
y="6%"
textAnchor="start"
fill={theme.palette.text.secondary}
fontSize={11}
fontWeight="bold"
onClick={handleTimeRangeClick}
style={{ cursor: "pointer" }}
>
{getTimeRangeText()}
</text>
{/* 上传标签 - 右上角 */}
<text
x="98%"
y="7%"
textAnchor="end"
fill={colors.up}
fontSize={12}
fontWeight="bold"
>
{t("Upload")}
</text>
// 根据chartStyle返回相应的图表类型
if (chartStyle === "line") {
return (
<LineChart {...chartConfig}>
{commonChartComponents}
<Line
type={curveType}
{...commonLineProps}
dataKey="up"
name={t("Upload")}
stroke={colors.up}
/>
<Line
type={curveType}
{...commonLineProps}
dataKey="down"
name={t("Download")}
stroke={colors.down}
/>
</LineChart>
);
} else {
return (
<AreaChart {...chartConfig}>
{commonChartComponents}
<Area
type={curveType}
{...commonLineProps}
dataKey="up"
name={t("Upload")}
stroke={colors.up}
fill={colors.up}
fillOpacity={0.2}
/>
<Area
type={curveType}
{...commonLineProps}
dataKey="down"
name={t("Download")}
stroke={colors.down}
fill={colors.down}
fillOpacity={0.3}
/>
</AreaChart>
);
}
})()}
{/* 下载标签 - 右上角下方 */}
<text
x="98%"
y="16%"
textAnchor="end"
fill={colors.down}
fontSize={12}
fontWeight="bold"
>
{t("Download")}
</text>
</AreaChart>
)}
</ResponsiveContainer>
</Box>
);

View File

@ -28,7 +28,6 @@ import { createAuthSockette } from "@/utils/websocket";
import parseTraffic from "@/utils/parse-traffic";
import { getConnections, isDebugEnabled, gc } from "@/services/api";
import { ReactNode } from "react";
import { useAppData } from "@/providers/app-data-provider";
interface MemoryUsage {
inuse: number;
@ -158,13 +157,11 @@ export const EnhancedTrafficStats = () => {
const pageVisible = useVisibility();
const [isDebug, setIsDebug] = useState(false);
// 使用AppDataProvider
const { connections, uptime } = useAppData();
// 使用单一状态对象减少状态更新次数
const [stats, setStats] = useState({
traffic: { up: 0, down: 0 },
memory: { inuse: 0, oslimit: undefined as number | undefined },
connections: { uploadTotal: 0, downloadTotal: 0, activeConnections: 0 },
});
// 创建一个标记来追踪最后更新时间,用于节流
@ -179,6 +176,36 @@ export const EnhancedTrafficStats = () => {
memory: null as ReturnType<typeof createAuthSockette> | null,
});
// 获取连接数据
const fetchConnections = useCallback(async () => {
if (!pageVisible) return;
try {
const connections = await getConnections();
if (connections) {
setStats(prev => ({
...prev,
connections: {
uploadTotal: connections.uploadTotal || 0,
downloadTotal: connections.downloadTotal || 0,
activeConnections: connections.connections ? connections.connections.length : 0,
}
}));
}
} catch (err) {
console.error("Failed to fetch connections:", err);
}
}, [pageVisible]);
// 定期更新连接数据
useEffect(() => {
if (!pageVisible) return;
fetchConnections();
const intervalId = setInterval(fetchConnections, CONNECTIONS_UPDATE_INTERVAL);
return () => clearInterval(intervalId);
}, [pageVisible, fetchConnections]);
// 检查是否支持调试
useEffect(() => {
isDebugEnabled().then((flag) => setIsDebug(flag));
@ -301,14 +328,14 @@ export const EnhancedTrafficStats = () => {
const [up, upUnit] = parseTraffic(stats.traffic.up);
const [down, downUnit] = parseTraffic(stats.traffic.down);
const [inuse, inuseUnit] = parseTraffic(stats.memory.inuse);
const [uploadTotal, uploadTotalUnit] = parseTraffic(connections.uploadTotal);
const [downloadTotal, downloadTotalUnit] = parseTraffic(connections.downloadTotal);
const [uploadTotal, uploadTotalUnit] = parseTraffic(stats.connections.uploadTotal);
const [downloadTotal, downloadTotalUnit] = parseTraffic(stats.connections.downloadTotal);
return {
up, upUnit, down, downUnit, inuse, inuseUnit,
uploadTotal, uploadTotalUnit, downloadTotal, downloadTotalUnit
};
}, [stats, connections.uploadTotal, connections.downloadTotal]);
}, [stats]);
// 渲染流量图表 - 使用useMemo缓存渲染结果
const trafficGraphComponent = useMemo(() => {
@ -371,7 +398,7 @@ export const EnhancedTrafficStats = () => {
{
icon: <LinkRounded fontSize="small" />,
title: t("Active Connections"),
value: connections.count,
value: stats.connections.activeConnections,
unit: "",
color: "success" as const,
},
@ -397,16 +424,14 @@ export const EnhancedTrafficStats = () => {
color: "error" as const,
onClick: isDebug ? handleGarbageCollection : undefined,
},
], [t, parsedData, connections.count, isDebug, handleGarbageCollection]);
], [t, parsedData, stats.connections.activeConnections, isDebug, handleGarbageCollection]);
return (
<Grid container spacing={1} columns={{ xs: 8, sm: 8, md: 12 }}>
{trafficGraph && (
<Grid size={12}>
{/* 流量图表区域 */}
{trafficGraphComponent}
</Grid>
)}
<Grid size={12}>
{/* 流量图表区域 */}
{trafficGraphComponent}
</Grid>
{/* 统计卡片区域 */}
{statCards.map((card, index) => (
<Grid key={index} size={4}>

View File

@ -27,7 +27,6 @@ import { openWebUrl, updateProfile } from "@/services/cmds";
import { useLockFn } from "ahooks";
import { Notice } from "@/components/base";
import { EnhancedCard } from "./enhanced-card";
import { useAppData } from "@/providers/app-data-provider";
// 定义旋转动画
const round = keyframes`
@ -271,7 +270,6 @@ const EmptyProfile = ({ onClick }: { onClick: () => void }) => {
export const HomeProfileCard = ({ current, onProfileUpdated }: HomeProfileCardProps) => {
const { t } = useTranslation();
const navigate = useNavigate();
const { refreshAll } = useAppData();
// 更新当前订阅
const [updating, setUpdating] = useState(false);
@ -284,9 +282,6 @@ export const HomeProfileCard = ({ current, onProfileUpdated }: HomeProfileCardPr
await updateProfile(current.uid);
Notice.success(t("Update subscription successfully"));
onProfileUpdated?.();
// 刷新首页数据
refreshAll();
} catch (err: any) {
Notice.error(err?.message || err.toString());
} finally {

View File

@ -18,8 +18,13 @@ import {
HelpOutlineRounded,
SvgIconComponent,
} from "@mui/icons-material";
import useSWR from "swr";
import {
getSystemProxy,
getAutotemProxy,
getRunningMode,
} from "@/services/cmds";
import { useVerge } from "@/hooks/use-verge";
import { useSystemState } from "@/hooks/use-system-state";
const LOCAL_STORAGE_TAB_KEY = "clash-verge-proxy-active-tab";
@ -32,64 +37,71 @@ interface TabButtonProps {
}
// 抽取Tab组件以减少重复代码
const TabButton: FC<TabButtonProps> = memo(
({ isActive, onClick, icon: Icon, label, hasIndicator = false }) => (
<Paper
elevation={isActive ? 2 : 0}
onClick={onClick}
sx={{
cursor: "pointer",
px: 2,
py: 1,
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 1,
bgcolor: isActive ? "primary.main" : "background.paper",
color: isActive ? "primary.contrastText" : "text.primary",
borderRadius: 1.5,
flex: 1,
maxWidth: 160,
transition: "all 0.2s ease-in-out",
position: "relative",
"&:hover": {
transform: "translateY(-1px)",
boxShadow: 1,
},
"&:after": isActive
? {
content: '""',
position: "absolute",
bottom: -9,
left: "50%",
width: 2,
height: 9,
bgcolor: "primary.main",
transform: "translateX(-50%)",
}
: {},
}}
const TabButton: FC<TabButtonProps> = memo(({
isActive,
onClick,
icon: Icon,
label,
hasIndicator = false
}) => (
<Paper
elevation={isActive ? 2 : 0}
onClick={onClick}
sx={{
cursor: "pointer",
px: 2,
py: 1,
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 1,
bgcolor: isActive ? "primary.main" : "background.paper",
color: isActive ? "primary.contrastText" : "text.primary",
borderRadius: 1.5,
flex: 1,
maxWidth: 160,
transition: "all 0.2s ease-in-out",
position: "relative",
"&:hover": {
transform: "translateY(-1px)",
boxShadow: 1,
},
"&:after": isActive
? {
content: '""',
position: "absolute",
bottom: -9,
left: "50%",
width: 2,
height: 9,
bgcolor: "primary.main",
transform: "translateX(-50%)",
}
: {},
}}
>
<Icon fontSize="small" />
<Typography
variant="body2"
sx={{ fontWeight: isActive ? 600 : 400 }}
>
<Icon fontSize="small" />
<Typography variant="body2" sx={{ fontWeight: isActive ? 600 : 400 }}>
{label}
</Typography>
{hasIndicator && (
<Box
sx={{
width: 8,
height: 8,
borderRadius: "50%",
bgcolor: isActive ? "#fff" : "success.main",
position: "absolute",
top: 8,
right: 8,
}}
/>
)}
</Paper>
),
);
{label}
</Typography>
{hasIndicator && (
<Box
sx={{
width: 8,
height: 8,
borderRadius: "50%",
bgcolor: isActive ? "#fff" : "success.main",
position: "absolute",
top: 8,
right: 8,
}}
/>
)}
</Paper>
));
interface TabDescriptionProps {
description: string;
@ -97,57 +109,56 @@ interface TabDescriptionProps {
}
// 抽取描述文本组件
const TabDescription: FC<TabDescriptionProps> = memo(
({ description, tooltipTitle }) => (
<Fade in={true} timeout={200}>
<Typography
variant="caption"
component="div"
sx={{
width: "95%",
textAlign: "center",
color: "text.secondary",
p: 0.8,
borderRadius: 1,
borderColor: "primary.main",
borderWidth: 1,
borderStyle: "solid",
backgroundColor: "background.paper",
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 0.5,
wordBreak: "break-word",
hyphens: "auto",
}}
>
{description}
<Tooltip title={tooltipTitle}>
<HelpOutlineRounded
sx={{ fontSize: 14, opacity: 0.7, flexShrink: 0 }}
/>
</Tooltip>
</Typography>
</Fade>
),
);
const TabDescription: FC<TabDescriptionProps> = memo(({ description, tooltipTitle }) => (
<Fade in={true} timeout={200}>
<Typography
variant="caption"
component="div"
sx={{
width: "95%",
textAlign: "center",
color: "text.secondary",
p: 0.8,
borderRadius: 1,
borderColor: "primary.main",
borderWidth: 1,
borderStyle: "solid",
backgroundColor: "background.paper",
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 0.5,
wordBreak: "break-word",
hyphens: "auto",
}}
>
{description}
<Tooltip title={tooltipTitle}>
<HelpOutlineRounded
sx={{ fontSize: 14, opacity: 0.7, flexShrink: 0 }}
/>
</Tooltip>
</Typography>
</Fade>
));
export const ProxyTunCard: FC = () => {
const { t } = useTranslation();
const theme = useTheme();
const [activeTab, setActiveTab] = useState<string>(
() => localStorage.getItem(LOCAL_STORAGE_TAB_KEY) || "system",
const [activeTab, setActiveTab] = useState<string>(() =>
localStorage.getItem(LOCAL_STORAGE_TAB_KEY) || "system"
);
// 获取代理状态信息
const { data: sysproxy } = useSWR("getSystemProxy", getSystemProxy);
const { data: runningMode } = useSWR("getRunningMode", getRunningMode);
const { verge } = useVerge();
const { isSidecarMode, isAdminMode } = useSystemState();
// 从verge配置中获取开关状态
const { enable_system_proxy, enable_tun_mode } = verge ?? {};
// 判断Tun模式是否可用 - 当处于服务模式或管理员模式时可用
const isTunAvailable = !isSidecarMode || isAdminMode;
// 是否以sidecar模式运行
const isSidecarMode = runningMode === "sidecar";
// 处理错误
const handleError = (err: Error) => {
@ -167,19 +178,19 @@ export const ProxyTunCard: FC = () => {
text: enable_system_proxy
? t("System Proxy Enabled")
: t("System Proxy Disabled"),
tooltip: t("System Proxy Info"),
tooltip: t("System Proxy Info")
};
} else {
return {
text: !isTunAvailable
text: isSidecarMode
? t("TUN Mode Service Required")
: enable_tun_mode
? t("TUN Mode Enabled")
: t("TUN Mode Disabled"),
tooltip: t("TUN Mode Intercept Info"),
tooltip: t("TUN Mode Intercept Info")
};
}
}, [activeTab, enable_system_proxy, enable_tun_mode, isTunAvailable, t]);
}, [activeTab, enable_system_proxy, enable_tun_mode, isSidecarMode, t]);
return (
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
@ -206,7 +217,7 @@ export const ProxyTunCard: FC = () => {
onClick={() => handleTabChange("tun")}
icon={TroubleshootRounded}
label={t("Tun Mode")}
hasIndicator={enable_tun_mode && isTunAvailable}
hasIndicator={enable_tun_mode && !isSidecarMode}
/>
</Stack>

View File

@ -1,30 +1,21 @@
import { useTranslation } from "react-i18next";
import { Typography, Stack, Divider, Chip, IconButton, Tooltip } from "@mui/material";
import {
InfoOutlined,
SettingsOutlined,
WarningOutlined,
AdminPanelSettingsOutlined,
DnsOutlined,
ExtensionOutlined
} from "@mui/icons-material";
import { Typography, Stack, Divider, Chip, IconButton } from "@mui/material";
import { InfoOutlined, SettingsOutlined } from "@mui/icons-material";
import { useVerge } from "@/hooks/use-verge";
import { EnhancedCard } from "./enhanced-card";
import useSWR from "swr";
import { getSystemInfo, installService } from "@/services/cmds";
import { getRunningMode, getSystemInfo, installService } from "@/services/cmds";
import { useNavigate } from "react-router-dom";
import { version as appVersion } from "@root/package.json";
import { useCallback, useEffect, useMemo, useState } from "react";
import { check as checkUpdate } from "@tauri-apps/plugin-updater";
import { useLockFn } from "ahooks";
import { Notice } from "@/components/base";
import { useSystemState } from "@/hooks/use-system-state";
export const SystemInfoCard = () => {
const { t } = useTranslation();
const { verge, patchVerge } = useVerge();
const navigate = useNavigate();
const { isAdminMode, isSidecarMode, mutateRunningMode } = useSystemState();
// 系统信息状态
const [systemState, setSystemState] = useState({
@ -32,6 +23,16 @@ export const SystemInfoCard = () => {
lastCheckUpdate: "-",
});
// 获取运行模式
const { data: runningMode = "sidecar", mutate: mutateRunningMode } = useSWR(
"getRunningMode",
getRunningMode,
{ suspense: false, revalidateOnFocus: false }
);
// 是否以sidecar模式运行
const isSidecarMode = runningMode === "sidecar";
// 初始化系统信息
useEffect(() => {
// 获取系统信息
@ -41,10 +42,7 @@ export const SystemInfoCard = () => {
if (lines.length > 0) {
const sysName = lines[0].split(": ")[1] || "";
const sysVersion = lines[1].split(": ")[1] || "";
setSystemState((prev) => ({
...prev,
osInfo: `${sysName} ${sysVersion}`,
}));
setSystemState(prev => ({ ...prev, osInfo: `${sysName} ${sysVersion}` }));
}
})
.catch(console.error);
@ -55,9 +53,9 @@ export const SystemInfoCard = () => {
try {
const timestamp = parseInt(lastCheck, 10);
if (!isNaN(timestamp)) {
setSystemState((prev) => ({
...prev,
lastCheckUpdate: new Date(timestamp).toLocaleString(),
setSystemState(prev => ({
...prev,
lastCheckUpdate: new Date(timestamp).toLocaleString()
}));
}
} catch (e) {
@ -67,11 +65,11 @@ export const SystemInfoCard = () => {
// 如果启用了自动检查更新但没有记录,设置当前时间并延迟检查
const now = Date.now();
localStorage.setItem("last_check_update", now.toString());
setSystemState((prev) => ({
...prev,
lastCheckUpdate: new Date(now).toLocaleString(),
setSystemState(prev => ({
...prev,
lastCheckUpdate: new Date(now).toLocaleString()
}));
setTimeout(() => {
if (verge?.auto_check_update) {
checkUpdate().catch(console.error);
@ -86,9 +84,9 @@ export const SystemInfoCard = () => {
async () => {
const now = Date.now();
localStorage.setItem("last_check_update", now.toString());
setSystemState((prev) => ({
...prev,
lastCheckUpdate: new Date(now).toLocaleString(),
setSystemState(prev => ({
...prev,
lastCheckUpdate: new Date(now).toLocaleString()
}));
return await checkUpdate();
},
@ -96,7 +94,7 @@ export const SystemInfoCard = () => {
revalidateOnFocus: false,
refreshInterval: 24 * 60 * 60 * 1000, // 每天检查一次
dedupingInterval: 60 * 60 * 1000, // 1小时内不重复检查
},
}
);
// 导航到设置页面
@ -106,13 +104,13 @@ export const SystemInfoCard = () => {
// 切换自启动状态
const toggleAutoLaunch = useCallback(async () => {
if (!verge || isAdminMode) return;
if (!verge) return;
try {
await patchVerge({ enable_auto_launch: !verge.enable_auto_launch });
} catch (err) {
console.error("切换开机自启动状态失败:", err);
}
}, [verge, patchVerge, isAdminMode]);
}, [verge, patchVerge]);
// 安装系统服务
const onInstallService = useLockFn(async () => {
@ -120,20 +118,18 @@ export const SystemInfoCard = () => {
Notice.info(t("Installing Service..."), 1000);
await installService();
Notice.success(t("Service Installed Successfully"), 2000);
await mutateRunningMode();
await mutateRunningMode();
} catch (err: any) {
Notice.error(err.message || err.toString(), 3000);
}
});
// 点击运行模式处理,Sidecar或纯管理员模式允许安装服务
// 点击运行模式处理
const handleRunningModeClick = useCallback(() => {
if (isSidecarMode || (isAdminMode && isSidecarMode)) {
if (isSidecarMode) {
onInstallService();
}
}, [isSidecarMode, isAdminMode, onInstallService]);
}, [isSidecarMode, onInstallService]);
// 检查更新
const onCheckUpdate = useLockFn(async () => {
@ -151,82 +147,16 @@ export const SystemInfoCard = () => {
});
// 是否启用自启动
const autoLaunchEnabled = useMemo(
() => verge?.enable_auto_launch || false,
[verge],
);
const autoLaunchEnabled = useMemo(() => verge?.enable_auto_launch || false, [verge]);
// 运行模式样式
const runningModeStyle = useMemo(
() => ({
// Sidecar或纯管理员模式允许安装服务
cursor: (isSidecarMode || (isAdminMode && isSidecarMode)) ? "pointer" : "default",
textDecoration: (isSidecarMode || (isAdminMode && isSidecarMode)) ? "underline" : "none",
display: "flex",
alignItems: "center",
gap: 0.5,
"&:hover": {
opacity: (isSidecarMode || (isAdminMode && isSidecarMode)) ? 0.7 : 1,
},
}),
[isSidecarMode, isAdminMode],
);
// 获取模式图标和文本
const getModeIcon = () => {
if (isAdminMode) {
// 判断是否为组合模式(管理员+服务)
if (!isSidecarMode) {
return (
<>
<AdminPanelSettingsOutlined
sx={{ color: "primary.main", fontSize: 16 }}
titleAccess={t("Administrator Mode")}
/>
<DnsOutlined
sx={{ color: "success.main", fontSize: 16, ml: 0.5 }}
titleAccess={t("Service Mode")}
/>
</>
);
}
return (
<AdminPanelSettingsOutlined
sx={{ color: "primary.main", fontSize: 16 }}
titleAccess={t("Administrator Mode")}
/>
);
} else if (isSidecarMode) {
return (
<ExtensionOutlined
sx={{ color: "info.main", fontSize: 16 }}
titleAccess={t("Sidecar Mode")}
/>
);
} else {
return (
<DnsOutlined
sx={{ color: "success.main", fontSize: 16 }}
titleAccess={t("Service Mode")}
/>
);
}
};
// 获取模式文本
const getModeText = () => {
if (isAdminMode) {
// 判断是否同时处于服务模式
if (!isSidecarMode) {
return t("Administrator + Service Mode");
}
return t("Administrator Mode");
} else if (isSidecarMode) {
return t("Sidecar Mode");
} else {
return t("Service Mode");
}
};
const runningModeStyle = useMemo(() => ({
cursor: isSidecarMode ? "pointer" : "default",
textDecoration: isSidecarMode ? "underline" : "none",
"&:hover": {
opacity: isSidecarMode ? 0.7 : 1,
},
}), [isSidecarMode]);
// 只有当verge存在时才渲染内容
if (!verge) return null;
@ -252,29 +182,21 @@ export const SystemInfoCard = () => {
</Typography>
</Stack>
<Divider />
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack direction="row" justifyContent="space-between">
<Typography variant="body2" color="text.secondary">
{t("Auto Launch")}
</Typography>
<Stack direction="row" spacing={1} alignItems="center">
{isAdminMode && (
<Tooltip title={t("Administrator mode does not support auto launch")}>
<WarningOutlined sx={{ color: "warning.main", fontSize: 20 }} />
</Tooltip>
)}
<Chip
size="small"
label={autoLaunchEnabled ? t("Enabled") : t("Disabled")}
color={autoLaunchEnabled ? "success" : "default"}
variant={autoLaunchEnabled ? "filled" : "outlined"}
onClick={toggleAutoLaunch}
disabled={isAdminMode}
sx={{ cursor: isAdminMode ? "not-allowed" : "pointer" }}
/>
</Stack>
<Chip
size="small"
label={autoLaunchEnabled ? t("Enabled") : t("Disabled")}
color={autoLaunchEnabled ? "success" : "default"}
variant={autoLaunchEnabled ? "filled" : "outlined"}
onClick={toggleAutoLaunch}
sx={{ cursor: "pointer" }}
/>
</Stack>
<Divider />
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack direction="row" justifyContent="space-between">
<Typography variant="body2" color="text.secondary">
{t("Running Mode")}
</Typography>
@ -284,8 +206,7 @@ export const SystemInfoCard = () => {
onClick={handleRunningModeClick}
sx={runningModeStyle}
>
{getModeIcon()}
{getModeText()}
{isSidecarMode ? t("Sidecar Mode") : t("Service Mode")}
</Typography>
</Stack>
<Divider />

View File

@ -179,21 +179,17 @@ export const ProfileItem = (props: Props) => {
/// 0 不使用任何代理
/// 1 使用订阅好的代理
/// 2 至少使用一个代理,根据订阅,如果没订阅,默认使用系统代理
const onUpdate = useLockFn(async (type: 0 | 1 | 2): Promise<void> => {
const onUpdate = useLockFn(async (type: 0 | 1 | 2) => {
setAnchorEl(null);
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: true }));
// 存储原始设置以便回退后恢复
const originalOptions = {
with_proxy: itemData.option?.with_proxy,
self_proxy: itemData.option?.self_proxy
};
// 根据类型设置初始更新选项
const option: Partial<IProfileOption> = {};
if (type === 0) {
option.with_proxy = false;
option.self_proxy = false;
} else if (type === 1) {
// nothing
} else if (type === 2) {
if (itemData.option?.self_proxy) {
option.with_proxy = false;
@ -205,31 +201,14 @@ export const ProfileItem = (props: Props) => {
}
try {
// 尝试正常更新
await updateProfile(itemData.uid, option);
Notice.success(t("Update subscription successfully"));
mutate("getProfiles");
} catch (err: any) {
// 更新失败,尝试使用自身代理
const errmsg = err?.message || err.toString();
Notice.info(t("Update failed, retrying with Clash proxy..."));
try {
await updateProfile(itemData.uid, {
with_proxy: false,
self_proxy: true
});
Notice.success(t("Update with Clash proxy successfully"));
await updateProfile(itemData.uid, originalOptions);
mutate("getProfiles");
} catch (retryErr: any) {
const retryErrmsg = retryErr?.message || retryErr.toString();
Notice.error(
`${t("Update failed even with Clash proxy")}: ${retryErrmsg.replace(/error sending request for url (\S+?): /, "")}`,
);
}
Notice.error(
errmsg.replace(/error sending request for url (\S+?): /, ""),
);
} finally {
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: false }));
}

View File

@ -88,13 +88,11 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
formIns.handleSubmit(async (form) => {
setLoading(true);
try {
// 基本验证
if (!form.type) throw new Error("`Type` should not be null");
if (form.type === "remote" && !form.url) {
throw new Error("The URL should not be null");
}
// 处理表单数据
if (form.option?.update_interval) {
form.option.update_interval = +form.option.update_interval;
} else {
@ -103,72 +101,25 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
if (form.option?.user_agent === "") {
delete form.option.user_agent;
}
const name = form.name || `${form.type} file`;
const item = { ...form, name };
const isRemote = form.type === "remote";
// 保存原始代理设置以便回退成功后恢复
const originalOptions = {
with_proxy: form.option?.with_proxy,
self_proxy: form.option?.self_proxy
};
// 执行创建或更新操作,本地配置不需要回退机制
if (!isRemote) {
if (openType === "new") {
await createProfile(item, fileDataRef.current);
} else {
if (!form.uid) throw new Error("UID not found");
await patchProfile(form.uid, item);
}
} else {
// 远程配置使用回退机制
try {
// 尝试正常操作
if (openType === "new") {
await createProfile(item, fileDataRef.current);
} else {
if (!form.uid) throw new Error("UID not found");
await patchProfile(form.uid, item);
}
} catch (err) {
// 首次创建/更新失败,尝试使用自身代理
Notice.info(t("Profile creation failed, retrying with Clash proxy..."));
// 使用自身代理的配置
const retryItem = {
...item,
option: {
...item.option,
with_proxy: false,
self_proxy: true
}
};
// 使用自身代理再次尝试
if (openType === "new") {
await createProfile(retryItem, fileDataRef.current);
} else {
if (!form.uid) throw new Error("UID not found");
await patchProfile(form.uid, retryItem);
// 编辑模式下恢复原始代理设置
await patchProfile(form.uid, { option: originalOptions });
}
Notice.success(t("Profile creation succeeded with Clash proxy"));
}
// 创建
if (openType === "new") {
await createProfile(item, fileDataRef.current);
}
// 编辑
else {
if (!form.uid) throw new Error("UID not found");
await patchProfile(form.uid, item);
}
// 成功后的操作
setOpen(false);
setLoading(false);
setTimeout(() => formIns.reset(), 500);
fileDataRef.current = null;
props.onChange();
} catch (err: any) {
Notice.error(err.message || err.toString());
} finally {
setLoading(false);
}
})

View File

@ -1,46 +1,195 @@
import dayjs from "dayjs";
import useSWR, { mutate } from "swr";
import { useState } from "react";
import {
import {
Button,
Box,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
IconButton,
List,
ListItem,
ListItemText,
styled,
Box,
alpha,
Typography,
Divider,
LinearProgress,
alpha,
styled,
useTheme
keyframes,
} from "@mui/material";
import { RefreshRounded } from "@mui/icons-material";
import { useTranslation } from "react-i18next";
import { useLockFn } from "ahooks";
import { proxyProviderUpdate } from "@/services/api";
import { useAppData } from "@/providers/app-data-provider";
import { Notice } from "@/components/base";
import { StorageOutlined, RefreshRounded } from "@mui/icons-material";
import dayjs from "dayjs";
import { getProxyProviders, proxyProviderUpdate } from "@/services/api";
import { BaseDialog } from "../base";
import parseTraffic from "@/utils/parse-traffic";
// 定义代理提供者类型
interface ProxyProviderItem {
name?: string;
proxies: any[];
updatedAt: number;
vehicleType: string;
subscriptionInfo?: {
Upload: number;
Download: number;
Total: number;
Expire: number;
};
}
const round = keyframes`
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
`;
// 样式化组件 - 类型框
export const ProviderButton = () => {
const { t } = useTranslation();
const { data } = useSWR("getProxyProviders", getProxyProviders);
const [open, setOpen] = useState(false);
const hasProvider = Object.keys(data || {}).length > 0;
const [updating, setUpdating] = useState(
Object.keys(data || {}).map(() => false),
);
const setUpdatingAt = (status: boolean, index: number) => {
setUpdating((prev) => {
const next = [...prev];
next[index] = status;
return next;
});
};
const handleUpdate = async (key: string, index: number) => {
setUpdatingAt(true, index);
proxyProviderUpdate(key)
.then(async () => {
setUpdatingAt(false, index);
await mutate("getProxies");
await mutate("getProxyProviders");
})
.catch(async () => {
setUpdatingAt(false, index);
await mutate("getProxies");
await mutate("getProxyProviders");
});
};
if (!hasProvider) return null;
return (
<>
<Button
size="small"
variant="outlined"
sx={{ textTransform: "capitalize" }}
onClick={() => setOpen(true)}
>
{t("Proxy Provider")}
</Button>
<BaseDialog
open={open}
title={
<Box display="flex" justifyContent="space-between" gap={1}>
<Typography variant="h6">{t("Proxy Provider")}</Typography>
<Button
variant="contained"
size="small"
onClick={async () => {
Object.entries(data || {}).forEach(async ([key], index) => {
await handleUpdate(key, index);
});
}}
>
{t("Update All")}
</Button>
</Box>
}
contentSx={{ width: 400 }}
disableOk
cancelBtn={t("Close")}
onClose={() => setOpen(false)}
onCancel={() => setOpen(false)}
>
<List sx={{ py: 0, minHeight: 250 }}>
{Object.entries(data || {}).map(([key, item], index) => {
const time = dayjs(item.updatedAt);
const sub = item.subscriptionInfo;
const hasSubInfo = !!sub;
const upload = sub?.Upload || 0;
const download = sub?.Download || 0;
const total = sub?.Total || 0;
const expire = sub?.Expire || 0;
const progress = Math.min(
Math.round(((download + upload) * 100) / (total + 0.01)) + 1,
100,
);
return (
<>
<ListItem
sx={{
p: 0,
borderRadius: "10px",
border: "solid 2px var(--divider-color)",
mb: 1,
}}
key={key}
>
<ListItemText
sx={{ px: 1 }}
primary={
<>
<Typography
variant="h6"
component="span"
noWrap
title={key}
>
{key}
</Typography>
<TypeBox component="span" sx={{ marginLeft: "8px" }}>
{item.proxies.length}
</TypeBox>
</>
}
secondary={
<>
<StyledTypeBox component="span">
{item.vehicleType}
</StyledTypeBox>
<StyledTypeBox component="span">
{t("Update At")} {time.fromNow()}
</StyledTypeBox>
{hasSubInfo && (
<>
<Box sx={{ ...boxStyle, fontSize: 14 }}>
<span title="Used / Total">
{parseTraffic(upload + download)} /{" "}
{parseTraffic(total)}
</span>
<span title="Expire Time">
{parseExpire(expire)}
</span>
</Box>
<LinearProgress
variant="determinate"
value={progress}
style={{ opacity: total > 0 ? 1 : 0 }}
/>
</>
)}
</>
}
/>
<Divider orientation="vertical" flexItem />
<IconButton
size="small"
color="inherit"
title={`${t("Update")}${t("Proxy Provider")}`}
onClick={() => handleUpdate(key, index)}
sx={{
animation: updating[index]
? `1s linear infinite ${round}`
: "none",
}}
>
<RefreshRounded />
</IconButton>
</ListItem>
</>
);
})}
</List>
</BaseDialog>
</>
);
};
const TypeBox = styled(Box)<{ component?: React.ElementType }>(({ theme }) => ({
display: "inline-block",
border: "1px solid #ccc",
@ -53,271 +202,28 @@ const TypeBox = styled(Box)<{ component?: React.ElementType }>(({ theme }) => ({
lineHeight: 1.25,
}));
// 解析过期时间
const parseExpire = (expire?: number) => {
const StyledTypeBox = styled(Box)<{ component?: React.ElementType }>(
({ theme }) => ({
display: "inline-block",
border: "1px solid #ccc",
borderColor: alpha(theme.palette.primary.main, 0.5),
color: alpha(theme.palette.primary.main, 0.8),
borderRadius: 4,
fontSize: 10,
marginRight: "4px",
padding: "0 2px",
lineHeight: 1.25,
}),
);
const boxStyle = {
height: 26,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
};
function parseExpire(expire?: number) {
if (!expire) return "-";
return dayjs(expire * 1000).format("YYYY-MM-DD");
};
export const ProviderButton = () => {
const { t } = useTranslation();
const theme = useTheme();
const [open, setOpen] = useState(false);
const { proxyProviders, refreshProxy, refreshProxyProviders } = useAppData();
const [updating, setUpdating] = useState<Record<string, boolean>>({});
// 检查是否有提供者
const hasProviders = Object.keys(proxyProviders || {}).length > 0;
// 更新单个代理提供者
const updateProvider = useLockFn(async (name: string) => {
try {
// 设置更新状态
setUpdating(prev => ({ ...prev, [name]: true }));
await proxyProviderUpdate(name);
// 刷新数据
await refreshProxy();
await refreshProxyProviders();
Notice.success(`${name} 更新成功`);
} catch (err: any) {
Notice.error(`${name} 更新失败: ${err?.message || err.toString()}`);
} finally {
// 清除更新状态
setUpdating(prev => ({ ...prev, [name]: false }));
}
});
// 更新所有代理提供者
const updateAllProviders = useLockFn(async () => {
try {
// 获取所有provider的名称
const allProviders = Object.keys(proxyProviders || {});
if (allProviders.length === 0) {
Notice.info("没有可更新的代理提供者");
return;
}
// 设置所有provider为更新中状态
const newUpdating = allProviders.reduce((acc, key) => {
acc[key] = true;
return acc;
}, {} as Record<string, boolean>);
setUpdating(newUpdating);
// 改为串行逐个更新所有provider
for (const name of allProviders) {
try {
await proxyProviderUpdate(name);
// 每个更新完成后更新状态
setUpdating(prev => ({ ...prev, [name]: false }));
} catch (err) {
console.error(`更新 ${name} 失败`, err);
// 继续执行下一个,不中断整体流程
}
}
// 刷新数据
await refreshProxy();
await refreshProxyProviders();
Notice.success("全部代理提供者更新成功");
} catch (err: any) {
Notice.error(`更新失败: ${err?.message || err.toString()}`);
} finally {
// 清除所有更新状态
setUpdating({});
}
});
const handleClose = () => {
setOpen(false);
};
if (!hasProviders) return null;
return (
<>
<Button
variant="outlined"
size="small"
startIcon={<StorageOutlined />}
onClick={() => setOpen(true)}
sx={{ mr: 1 }}
>
{t("Proxy Provider")}
</Button>
<Dialog
open={open}
onClose={handleClose}
maxWidth="sm"
fullWidth
>
<DialogTitle>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="h6">{t("Proxy Provider")}</Typography>
<Box>
<Button
variant="contained"
size="small"
onClick={updateAllProviders}
>
{t("Update All")}
</Button>
</Box>
</Box>
</DialogTitle>
<DialogContent>
<List sx={{ py: 0, minHeight: 250 }}>
{Object.entries(proxyProviders || {}).map(([key, item]) => {
const provider = item as ProxyProviderItem;
const time = dayjs(provider.updatedAt);
const isUpdating = updating[key];
// 订阅信息
const sub = provider.subscriptionInfo;
const hasSubInfo = !!sub;
const upload = sub?.Upload || 0;
const download = sub?.Download || 0;
const total = sub?.Total || 0;
const expire = sub?.Expire || 0;
// 流量使用进度
const progress = total > 0
? Math.min(Math.round(((download + upload) * 100) / total) + 1, 100)
: 0;
return (
<ListItem
key={key}
sx={[
{
p: 0,
mb: "8px",
borderRadius: 2,
overflow: "hidden",
transition: "all 0.2s"
},
({ palette: { mode, primary } }) => {
const bgcolor = mode === "light" ? "#ffffff" : "#24252f";
const hoverColor = mode === "light"
? alpha(primary.main, 0.1)
: alpha(primary.main, 0.2);
return {
backgroundColor: bgcolor,
"&:hover": {
backgroundColor: hoverColor,
}
};
}
]}
>
<ListItemText
sx={{ px: 2, py: 1 }}
primary={
<Box sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}>
<Typography
variant="subtitle1"
component="div"
noWrap
title={key}
sx={{ display: "flex", alignItems: "center" }}
>
<span style={{ marginRight: "8px" }}>{key}</span>
<TypeBox component="span">
{provider.proxies.length}
</TypeBox>
<TypeBox component="span">
{provider.vehicleType}
</TypeBox>
</Typography>
<Typography variant="body2" color="text.secondary" noWrap>
<small>{t("Update At")}: </small>{time.fromNow()}
</Typography>
</Box>
}
secondary={
<>
{/* 订阅信息 */}
{hasSubInfo && (
<>
<Box sx={{
mb: 1,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}>
<span title={t("Used / Total") as string}>
{parseTraffic(upload + download)} / {parseTraffic(total)}
</span>
<span title={t("Expire Time") as string}>
{parseExpire(expire)}
</span>
</Box>
{/* 进度条 */}
<LinearProgress
variant="determinate"
value={progress}
sx={{
height: 6,
borderRadius: 3,
opacity: total > 0 ? 1 : 0,
}}
/>
</>
)}
</>
}
/>
<Divider orientation="vertical" flexItem />
<Box sx={{
width: 40,
display: "flex",
justifyContent: "center",
alignItems: "center"
}}>
<IconButton
size="small"
color="primary"
onClick={(e) => {
updateProvider(key);
}}
disabled={isUpdating}
sx={{
animation: isUpdating ? "spin 1s linear infinite" : "none",
"@keyframes spin": {
"0%": { transform: "rotate(0deg)" },
"100%": { transform: "rotate(360deg)" }
}
}}
title={t("Update Provider") as string}
>
<RefreshRounded />
</IconButton>
</Box>
</ListItem>
);
})}
</List>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} variant="outlined">
{t("Close")}
</Button>
</DialogActions>
</Dialog>
</>
);
};
}

View File

@ -5,8 +5,6 @@ import {
ListItemButton,
Typography,
styled,
Chip,
Tooltip,
} from "@mui/material";
import {
ExpandLessRounded,
@ -23,19 +21,17 @@ import { useThemeMode } from "@/services/states";
import { useEffect, useMemo, useState } from "react";
import { convertFileSrc } from "@tauri-apps/api/core";
import { downloadIconCache } from "@/services/cmds";
import { useTranslation } from "react-i18next";
interface RenderProps {
item: IRenderItem;
indent: boolean;
onLocation: (group: IRenderItem["group"]) => void;
onLocation: (group: IProxyGroupItem) => void;
onCheckAll: (groupName: string) => void;
onHeadState: (groupName: string, patch: Partial<HeadState>) => void;
onChangeProxy: (group: IRenderItem["group"], proxy: IRenderItem["proxy"] & { name: string }) => void;
onChangeProxy: (group: IProxyGroupItem, proxy: IProxyItem) => void;
}
export const ProxyRender = (props: RenderProps) => {
const { t } = useTranslation();
const { indent, item, onLocation, onCheckAll, onHeadState, onChangeProxy } =
props;
const { type, group, headState, proxy, proxyCol } = item;
@ -127,20 +123,7 @@ export const ProxyRender = (props: RenderProps) => {
},
}}
/>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Tooltip title={t("Proxy Count")} arrow>
<Chip
size="small"
label={`${group.all.length}`}
sx={{
mr: 1,
backgroundColor: (theme) => alpha(theme.palette.primary.main, 0.1),
color: (theme) => theme.palette.primary.main,
}}
/>
</Tooltip>
{headState?.open ? <ExpandLessRounded /> : <ExpandMoreRounded />}
</Box>
{headState?.open ? <ExpandLessRounded /> : <ExpandMoreRounded />}
</ListItemButton>
);
}

View File

@ -1,4 +1,6 @@
import useSWR from "swr";
import { useEffect, useMemo, useCallback } from "react";
import { getProxies } from "@/services/api";
import { useVerge } from "@/hooks/use-verge";
import { filterSort } from "./use-filter-sort";
import { useWindowWidth } from "./use-window-width";
@ -7,52 +9,12 @@ import {
DEFAULT_STATE,
type HeadState,
} from "./use-head-state";
import { useAppData } from "@/providers/app-data-provider";
// 定义代理项接口
interface IProxyItem {
name: string;
type: string;
udp: boolean;
xudp: boolean;
tfo: boolean;
mptcp: boolean;
smux: boolean;
history: {
time: string;
delay: number;
}[];
provider?: string;
testUrl?: string;
[key: string]: any; // 添加索引签名以适应其他可能的属性
}
// 代理组类型
type ProxyGroup = {
name: string;
type: string;
udp: boolean;
xudp: boolean;
tfo: boolean;
mptcp: boolean;
smux: boolean;
history: {
time: string;
delay: number;
}[];
now: string;
all: IProxyItem[];
hidden?: boolean;
icon?: string;
testUrl?: string;
provider?: string;
};
export interface IRenderItem {
// 组 | head | item | empty | item col
type: 0 | 1 | 2 | 3 | 4;
key: string;
group: ProxyGroup;
group: IProxyGroupItem;
proxy?: IProxyItem;
col?: number;
proxyCol?: IProxyItem[];
@ -89,8 +51,16 @@ const groupProxies = <T = any>(list: T[], size: number): T[][] => {
};
export const useRenderList = (mode: string) => {
// 使用全局数据提供者
const { proxies: proxiesData, refreshProxy } = useAppData();
const { data: proxiesData, mutate: mutateProxies } = useSWR(
"getProxies",
getProxies,
{
refreshInterval: 2000,
revalidateOnFocus: false,
revalidateOnReconnect: true,
},
);
const { verge } = useVerge();
const { width } = useWindowWidth();
const [headStates, setHeadState] = useHeadStateNew();
@ -110,9 +80,9 @@ export const useRenderList = (mode: string) => {
(mode === "rule" && !groups.length) ||
(mode === "global" && proxies.length < 2)
) {
setTimeout(() => refreshProxy(), 500);
setTimeout(() => mutateProxies(), 500);
}
}, [proxiesData, mode, refreshProxy]);
}, [proxiesData, mode, mutateProxies]);
// 处理渲染列表
const renderList: IRenderItem[] = useMemo(() => {
@ -124,7 +94,7 @@ export const useRenderList = (mode: string) => {
? proxiesData.groups
: [proxiesData.global!];
const retList = renderGroups.flatMap((group: ProxyGroup) => {
const retList = renderGroups.flatMap((group) => {
const headState = headStates[group.name] || DEFAULT_STATE;
const ret: IRenderItem[] = [
{
@ -161,9 +131,9 @@ export const useRenderList = (mode: string) => {
});
} else if (col > 1) {
return ret.concat(
groupProxies(proxies, col).map((proxyCol, colIndex) => ({
groupProxies(proxies, col).map((proxyCol) => ({
type: 4,
key: `col-${group.name}-${proxyCol[0].name}-${colIndex}`,
key: `col-${group.name}-${proxyCol[0].name}`,
group,
headState,
col,
@ -188,12 +158,12 @@ export const useRenderList = (mode: string) => {
});
if (!useRule) return retList.slice(1);
return retList.filter((item: IRenderItem) => !item.group.hidden);
return retList.filter((item) => !item.group.hidden);
}, [headStates, proxiesData, mode, col]);
return {
renderList,
onProxies: refreshProxy,
onProxies: mutateProxies,
onHeadState: setHeadState,
currentColumns: col,
};

View File

@ -1,39 +1,170 @@
import dayjs from "dayjs";
import useSWR, { mutate } from "swr";
import { useState } from "react";
import {
Button,
Box,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
import {
Button,
IconButton,
List,
ListItem,
ListItemText,
Typography,
Divider,
alpha,
styled,
useTheme
Box,
alpha,
Divider,
keyframes,
} from "@mui/material";
import { RefreshRounded } from "@mui/icons-material";
import { useTranslation } from "react-i18next";
import { useLockFn } from "ahooks";
import { ruleProviderUpdate } from "@/services/api";
import { Notice } from "@/components/base";
import { StorageOutlined, RefreshRounded } from "@mui/icons-material";
import { useAppData } from "@/providers/app-data-provider";
import dayjs from "dayjs";
import { getRuleProviders, ruleProviderUpdate } from "@/services/api";
import { BaseDialog } from "../base";
// 定义规则提供者类型
interface RuleProviderItem {
behavior: string;
ruleCount: number;
updatedAt: number;
vehicleType: string;
}
const round = keyframes`
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
`;
// 辅助组件 - 类型框
const TypeBox = styled(Box)<{ component?: React.ElementType }>(({ theme }) => ({
export const ProviderButton = () => {
const { t } = useTranslation();
const { data } = useSWR("getRuleProviders", getRuleProviders);
const [open, setOpen] = useState(false);
const hasProvider = Object.keys(data || {}).length > 0;
const [updating, setUpdating] = useState(
Object.keys(data || {}).map(() => false),
);
const setUpdatingAt = (status: boolean, index: number) => {
setUpdating((prev) => {
const next = [...prev];
next[index] = status;
return next;
});
};
const handleUpdate = async (key: string, index: number) => {
setUpdatingAt(true, index);
ruleProviderUpdate(key)
.then(async () => {
setUpdatingAt(false, index);
await mutate("getRules");
await mutate("getRuleProviders");
})
.catch(async () => {
setUpdatingAt(false, index);
await mutate("getRules");
await mutate("getRuleProviders");
});
};
if (!hasProvider) return null;
return (
<>
<Button
size="small"
variant="outlined"
sx={{ textTransform: "capitalize" }}
onClick={() => setOpen(true)}
>
{t("Rule Provider")}
</Button>
<BaseDialog
open={open}
title={
<Box display="flex" justifyContent="space-between" gap={1}>
<Typography variant="h6">{t("Rule Provider")}</Typography>
<Button
variant="contained"
size="small"
onClick={async () => {
Object.entries(data || {}).forEach(async ([key], index) => {
await handleUpdate(key, index);
});
}}
>
{t("Update All")}
</Button>
</Box>
}
contentSx={{ width: 400 }}
disableOk
cancelBtn={t("Close")}
onClose={() => setOpen(false)}
onCancel={() => setOpen(false)}
>
<List sx={{ py: 0, minHeight: 250 }}>
{Object.entries(data || {}).map(([key, item], index) => {
const time = dayjs(item.updatedAt);
return (
<>
<ListItem
sx={{
p: 0,
borderRadius: "10px",
border: "solid 2px var(--divider-color)",
mb: 1,
}}
key={key}
>
<ListItemText
sx={{ px: 1 }}
primary={
<>
<Typography
variant="h6"
component="span"
noWrap
title={key}
>
{key}
</Typography>
<TypeBox component="span" sx={{ marginLeft: "8px" }}>
{item.ruleCount}
</TypeBox>
</>
}
secondary={
<>
<StyledTypeBox component="span">
{item.vehicleType}
</StyledTypeBox>
<StyledTypeBox component="span">
{item.behavior}
</StyledTypeBox>
<StyledTypeBox component="span">
{t("Update At")} {time.fromNow()}
</StyledTypeBox>
</>
}
/>
<Divider orientation="vertical" flexItem />
<IconButton
size="small"
color="inherit"
title={`${t("Update")}${t("Rule Provider")}`}
onClick={() => handleUpdate(key, index)}
sx={{
animation: updating[index]
? `1s linear infinite ${round}`
: "none",
}}
>
<RefreshRounded />
</IconButton>
</ListItem>
</>
);
})}
</List>
</BaseDialog>
</>
);
};
const TypeBox = styled(Box, {
shouldForwardProp: (prop) => prop !== "component",
})<{ component?: React.ElementType }>(({ theme }) => ({
display: "inline-block",
border: "1px solid #ccc",
borderColor: alpha(theme.palette.secondary.main, 0.5),
@ -45,222 +176,16 @@ const TypeBox = styled(Box)<{ component?: React.ElementType }>(({ theme }) => ({
lineHeight: 1.25,
}));
export const ProviderButton = () => {
const { t } = useTranslation();
const theme = useTheme();
const [open, setOpen] = useState(false);
const { ruleProviders, refreshRules, refreshRuleProviders } = useAppData();
const [updating, setUpdating] = useState<Record<string, boolean>>({});
// 检查是否有提供者
const hasProviders = Object.keys(ruleProviders || {}).length > 0;
// 更新单个规则提供者
const updateProvider = useLockFn(async (name: string) => {
try {
// 设置更新状态
setUpdating(prev => ({ ...prev, [name]: true }));
await ruleProviderUpdate(name);
// 刷新数据
await refreshRules();
await refreshRuleProviders();
Notice.success(`${name} 更新成功`);
} catch (err: any) {
Notice.error(`${name} 更新失败: ${err?.message || err.toString()}`);
} finally {
// 清除更新状态
setUpdating(prev => ({ ...prev, [name]: false }));
}
});
// 更新所有规则提供者
const updateAllProviders = useLockFn(async () => {
try {
// 获取所有provider的名称
const allProviders = Object.keys(ruleProviders || {});
if (allProviders.length === 0) {
Notice.info("没有可更新的规则提供者");
return;
}
// 设置所有provider为更新中状态
const newUpdating = allProviders.reduce((acc, key) => {
acc[key] = true;
return acc;
}, {} as Record<string, boolean>);
setUpdating(newUpdating);
// 改为串行逐个更新所有provider
for (const name of allProviders) {
try {
await ruleProviderUpdate(name);
// 每个更新完成后更新状态
setUpdating(prev => ({ ...prev, [name]: false }));
} catch (err) {
console.error(`更新 ${name} 失败`, err);
// 继续执行下一个,不中断整体流程
}
}
// 刷新数据
await refreshRules();
await refreshRuleProviders();
Notice.success("全部规则提供者更新成功");
} catch (err: any) {
Notice.error(`更新失败: ${err?.message || err.toString()}`);
} finally {
// 清除所有更新状态
setUpdating({});
}
});
const handleClose = () => {
setOpen(false);
};
if (!hasProviders) return null;
return (
<>
<Button
variant="outlined"
size="small"
startIcon={<StorageOutlined />}
onClick={() => setOpen(true)}
>
{t("Rule Provider")}
</Button>
<Dialog
open={open}
onClose={handleClose}
maxWidth="sm"
fullWidth
>
<DialogTitle>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="h6">{t("Rule Providers")}</Typography>
<Button
variant="contained"
size="small"
onClick={updateAllProviders}
>
{t("Update All")}
</Button>
</Box>
</DialogTitle>
<DialogContent>
<List sx={{ py: 0, minHeight: 250 }}>
{Object.entries(ruleProviders || {}).map(([key, item]) => {
const provider = item as RuleProviderItem;
const time = dayjs(provider.updatedAt);
const isUpdating = updating[key];
return (
<ListItem
key={key}
sx={[
{
p: 0,
mb: "8px",
borderRadius: 2,
overflow: "hidden",
transition: "all 0.2s"
},
({ palette: { mode, primary } }) => {
const bgcolor = mode === "light" ? "#ffffff" : "#24252f";
const hoverColor = mode === "light"
? alpha(primary.main, 0.1)
: alpha(primary.main, 0.2);
return {
backgroundColor: bgcolor,
"&:hover": {
backgroundColor: hoverColor,
borderColor: alpha(primary.main, 0.3)
}
};
}
]}
>
<ListItemText
sx={{ px: 2, py: 1 }}
primary={
<Box sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}>
<Typography
variant="subtitle1"
component="div"
noWrap
title={key}
sx={{ display: "flex", alignItems: "center" }}
>
<span style={{ marginRight: "8px" }}>{key}</span>
<TypeBox component="span">
{provider.ruleCount}
</TypeBox>
</Typography>
<Typography variant="body2" color="text.secondary" noWrap>
<small>{t("Update At")}: </small>{time.fromNow()}
</Typography>
</Box>
}
secondary={
<Box sx={{ display: "flex" }}>
<TypeBox component="span">
{provider.vehicleType}
</TypeBox>
<TypeBox component="span">
{provider.behavior}
</TypeBox>
</Box>
}
/>
<Divider orientation="vertical" flexItem />
<Box sx={{
width: 40,
display: "flex",
justifyContent: "center",
alignItems: "center"
}}>
<IconButton
size="small"
color="primary"
onClick={() => updateProvider(key)}
disabled={isUpdating}
sx={{
animation: isUpdating ? "spin 1s linear infinite" : "none",
"@keyframes spin": {
"0%": { transform: "rotate(0deg)" },
"100%": { transform: "rotate(360deg)" }
}
}}
title={t("Update Provider") as string}
>
<RefreshRounded />
</IconButton>
</Box>
</ListItem>
);
})}
</List>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} variant="outlined">
{t("Close")}
</Button>
</DialogActions>
</Dialog>
</>
);
};
const StyledTypeBox = styled(Box, {
shouldForwardProp: (prop) => prop !== "component",
})<{ component?: React.ElementType }>(({ theme }) => ({
display: "inline-block",
border: "1px solid #ccc",
borderColor: alpha(theme.palette.primary.main, 0.5),
color: alpha(theme.palette.primary.main, 0.8),
borderRadius: 4,
fontSize: 10,
marginRight: "4px",
padding: "0 2px",
lineHeight: 1.25,
}));

View File

@ -57,18 +57,21 @@ const DEFAULT_DNS_CONFIG = {
"*.msftncsi.com",
"www.msftconnecttest.com",
],
"default-nameserver": ["system", "223.6.6.6", "8.8.8.8"],
"default-nameserver": ["223.6.6.6", "8.8.8.8"],
nameserver: [
"8.8.8.8",
"https://doh.pub/dns-query",
"https://dns.alidns.com/dns-query",
],
fallback: [],
fallback: [
"https://dns.alidns.com/dns-query",
"https://dns.google/dns-query",
"https://cloudflare-dns.com/dns-query",
],
"nameserver-policy": {},
"proxy-server-nameserver": [
"https://doh.pub/dns-query",
"https://dns.alidns.com/dns-query",
"tls://223.5.5.5"
],
"direct-nameserver": [],
"direct-nameserver-follow-policy": false,
@ -95,12 +98,12 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
fakeIpFilterMode: "blacklist" | "whitelist";
preferH3: boolean;
respectRules: boolean;
useHosts: boolean;
useSystemHosts: boolean;
fakeIpFilter: string;
nameserver: string;
fallback: string;
defaultNameserver: string;
useHosts: boolean;
useSystemHosts: boolean;
proxyServerNameserver: string;
directNameserver: string;
directNameserverFollowPolicy: boolean;
@ -117,12 +120,12 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
fakeIpFilterMode: DEFAULT_DNS_CONFIG["fake-ip-filter-mode"],
preferH3: DEFAULT_DNS_CONFIG["prefer-h3"],
respectRules: DEFAULT_DNS_CONFIG["respect-rules"],
useHosts: DEFAULT_DNS_CONFIG["use-hosts"],
useSystemHosts: DEFAULT_DNS_CONFIG["use-system-hosts"],
fakeIpFilter: DEFAULT_DNS_CONFIG["fake-ip-filter"].join(", "),
defaultNameserver: DEFAULT_DNS_CONFIG["default-nameserver"].join(", "),
nameserver: DEFAULT_DNS_CONFIG.nameserver.join(", "),
fallback: DEFAULT_DNS_CONFIG.fallback.join(", "),
useHosts: DEFAULT_DNS_CONFIG["use-hosts"],
useSystemHosts: DEFAULT_DNS_CONFIG["use-system-hosts"],
proxyServerNameserver:
DEFAULT_DNS_CONFIG["proxy-server-nameserver"]?.join(", ") || "",
directNameserver: DEFAULT_DNS_CONFIG["direct-nameserver"]?.join(", ") || "",
@ -206,9 +209,6 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
preferH3: config["prefer-h3"] ?? DEFAULT_DNS_CONFIG["prefer-h3"],
respectRules:
config["respect-rules"] ?? DEFAULT_DNS_CONFIG["respect-rules"],
useHosts: config["use-hosts"] ?? DEFAULT_DNS_CONFIG["use-hosts"],
useSystemHosts:
config["use-system-hosts"] ?? DEFAULT_DNS_CONFIG["use-system-hosts"],
fakeIpFilter:
config["fake-ip-filter"]?.join(", ") ??
DEFAULT_DNS_CONFIG["fake-ip-filter"].join(", "),
@ -220,6 +220,9 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
defaultNameserver:
config["default-nameserver"]?.join(", ") ??
DEFAULT_DNS_CONFIG["default-nameserver"].join(", "),
useHosts: config["use-hosts"] ?? DEFAULT_DNS_CONFIG["use-hosts"],
useSystemHosts:
config["use-system-hosts"] ?? DEFAULT_DNS_CONFIG["use-system-hosts"],
proxyServerNameserver:
config["proxy-server-nameserver"]?.join(", ") ??
(DEFAULT_DNS_CONFIG["proxy-server-nameserver"]?.join(", ") || ""),
@ -256,12 +259,12 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
fakeIpFilterMode: DEFAULT_DNS_CONFIG["fake-ip-filter-mode"],
preferH3: DEFAULT_DNS_CONFIG["prefer-h3"],
respectRules: DEFAULT_DNS_CONFIG["respect-rules"],
useHosts: DEFAULT_DNS_CONFIG["use-hosts"],
useSystemHosts: DEFAULT_DNS_CONFIG["use-system-hosts"],
fakeIpFilter: DEFAULT_DNS_CONFIG["fake-ip-filter"].join(", "),
defaultNameserver: DEFAULT_DNS_CONFIG["default-nameserver"].join(", "),
nameserver: DEFAULT_DNS_CONFIG.nameserver.join(", "),
fallback: DEFAULT_DNS_CONFIG.fallback.join(", "),
useHosts: DEFAULT_DNS_CONFIG["use-hosts"],
useSystemHosts: DEFAULT_DNS_CONFIG["use-system-hosts"],
proxyServerNameserver:
DEFAULT_DNS_CONFIG["proxy-server-nameserver"]?.join(", ") || "",
directNameserver:
@ -327,10 +330,6 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
preferH3: dnsConfig["prefer-h3"] ?? DEFAULT_DNS_CONFIG["prefer-h3"],
respectRules:
dnsConfig["respect-rules"] ?? DEFAULT_DNS_CONFIG["respect-rules"],
useHosts: dnsConfig["use-hosts"] ?? DEFAULT_DNS_CONFIG["use-hosts"],
useSystemHosts:
dnsConfig["use-system-hosts"] ??
DEFAULT_DNS_CONFIG["use-system-hosts"],
fakeIpFilter:
dnsConfig["fake-ip-filter"]?.join(", ") ??
DEFAULT_DNS_CONFIG["fake-ip-filter"].join(", "),
@ -343,6 +342,10 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
fallback:
dnsConfig.fallback?.join(", ") ??
DEFAULT_DNS_CONFIG.fallback.join(", "),
useHosts: dnsConfig["use-hosts"] ?? DEFAULT_DNS_CONFIG["use-hosts"],
useSystemHosts:
dnsConfig["use-system-hosts"] ??
DEFAULT_DNS_CONFIG["use-system-hosts"],
proxyServerNameserver:
dnsConfig["proxy-server-nameserver"]?.join(", ") ??
(DEFAULT_DNS_CONFIG["proxy-server-nameserver"]?.join(", ") || ""),
@ -455,12 +458,12 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
"fake-ip-filter-mode": values.fakeIpFilterMode,
"prefer-h3": values.preferH3,
"respect-rules": values.respectRules,
"use-hosts": values.useHosts,
"use-system-hosts": values.useSystemHosts,
"fake-ip-filter": parseList(values.fakeIpFilter),
"default-nameserver": parseList(values.defaultNameserver),
nameserver: parseList(values.nameserver),
"direct-nameserver-follow-policy": values.directNameserverFollowPolicy,
fallback: parseList(values.fallback),
"use-hosts": values.useHosts,
"use-system-hosts": values.useSystemHosts,
"fallback-filter": {
geoip: values.fallbackGeoip,
"geoip-code": values.fallbackGeoipCode,
@ -469,16 +472,13 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
},
};
// 只在有值时添加其他可选字段
if (values.fallback) {
dnsConfig["fallback"] = parseList(values.fallback);
}
// 只在有nameserverPolicy时添加
const policy = parseNameserverPolicy(values.nameserverPolicy);
if (Object.keys(policy).length > 0) {
dnsConfig["nameserver-policy"] = policy;
}
// 只在有值时添加其他可选字段
if (values.proxyServerNameserver) {
dnsConfig["proxy-server-nameserver"] = parseList(
values.proxyServerNameserver,
@ -489,6 +489,9 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
dnsConfig["direct-nameserver"] = parseList(values.directNameserver);
}
dnsConfig["direct-nameserver-follow-policy"] =
values.directNameserverFollowPolicy;
return dnsConfig;
};
@ -573,7 +576,7 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
open={open}
title={
<Box display="flex" justifyContent="space-between" alignItems="center">
{t("DNS Overwrite")}
{t("DNS Settings")}
<Box display="flex" alignItems="center" gap={1}>
<Button
variant="outlined"
@ -752,7 +755,7 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
size="small"
value={values.defaultNameserver}
onChange={handleChange("defaultNameserver")}
placeholder="system,223.6.6.6, 8.8.8.8"
placeholder="223.6.6.6, 8.8.8.8"
/>
</Item>

View File

@ -5,6 +5,7 @@ import {
SettingsRounded,
ShuffleRounded,
LanRounded,
DnsRounded,
} from "@mui/icons-material";
import { DialogRef, Notice, Switch } from "@/components/base";
import { useClash } from "@/hooks/use-clash";
@ -48,16 +49,7 @@ const SettingClash = ({ onError }: Props) => {
const { enable_random_port = false, verge_mixed_port } = verge ?? {};
// 独立跟踪DNS设置开关状态
const [dnsSettingsEnabled, setDnsSettingsEnabled] = useState(() => {
// 尝试从localStorage获取之前保存的状态
const savedState = localStorage.getItem("dns_settings_enabled");
if (savedState !== null) {
return savedState === "true";
}
// 如果没有保存的状态则从verge配置中获取
return verge?.enable_dns_settings ?? false;
});
const [dnsSettingsEnabled, setDnsSettingsEnabled] = useState(false);
const { addListener } = useListen();
const webRef = useRef<DialogRef>(null);
@ -67,6 +59,12 @@ const SettingClash = ({ onError }: Props) => {
const networkRef = useRef<DialogRef>(null);
const dnsRef = useRef<DialogRef>(null);
// 初始化时从verge配置中加载DNS设置开关状态
useEffect(() => {
const dnsSettingsState = verge?.enable_dns_settings ?? false;
setDnsSettingsEnabled(dnsSettingsState);
}, [verge]);
const onSwitchFormat = (_e: any, value: boolean) => value;
const onChangeData = (patch: Partial<IConfigData>) => {
mutateClash((old) => ({ ...(old! || {}), ...patch }), false);
@ -86,21 +84,15 @@ const SettingClash = ({ onError }: Props) => {
// 实现DNS设置开关处理函数
const handleDnsToggle = useLockFn(async (enable: boolean) => {
try {
// 立即更新UI状态
setDnsSettingsEnabled(enable);
// 保存到localStorage用于记住用户的选择
localStorage.setItem("dns_settings_enabled", String(enable));
// 更新verge配置
await patchVerge({ enable_dns_settings: enable });
await invoke("apply_dns_config", { apply: enable });
setTimeout(() => {
mutateClash();
}, 500); // 延迟500ms确保后端完成处理
} catch (err: any) {
// 如果出错,恢复原始状态
setDnsSettingsEnabled(!enable);
localStorage.setItem("dns_settings_enabled", String(!enable));
Notice.error(err.message || err.toString());
setDnsSettingsEnabled(!enable);
await patchVerge({ enable_dns_settings: !enable }).catch(() => {
// 忽略恢复状态时的错误
});
@ -151,6 +143,7 @@ const SettingClash = ({ onError }: Props) => {
/>
}
>
{/* 使用独立状态不再依赖dns?.enable */}
<Switch
edge="end"
checked={dnsSettingsEnabled}

View File

@ -18,12 +18,12 @@ import { TooltipIcon } from "@/components/base/base-tooltip-icon";
import {
getSystemProxy,
getAutotemProxy,
getRunningMode,
installService,
getAutoLaunchStatus,
} from "@/services/cmds";
import { useLockFn } from "ahooks";
import { Button, Tooltip } from "@mui/material";
import { useSystemState } from "@/hooks/use-system-state";
import { Box, Button, Tooltip } from "@mui/material";
interface Props {
onError?: (err: Error) => void;
@ -36,29 +36,23 @@ const SettingSystem = ({ onError }: Props) => {
const { data: sysproxy } = useSWR("getSystemProxy", getSystemProxy);
const { data: autoproxy } = useSWR("getAutotemProxy", getAutotemProxy);
const { data: autoLaunchEnabled } = useSWR(
"getAutoLaunchStatus",
getAutoLaunchStatus,
{ revalidateOnFocus: false }
const { data: runningMode, mutate: mutateRunningMode } = useSWR(
"getRunningMode",
getRunningMode,
);
const { isAdminMode, isSidecarMode, mutateRunningMode } = useSystemState();
// 判断Tun模式是否可用 - 当处于服务模式或管理员模式时可用
const isTunAvailable = !isSidecarMode || isAdminMode;
const { data: autoLaunchEnabled } = useSWR("getAutoLaunchStatus", getAutoLaunchStatus);
// 当实际自启动状态与配置不同步时更新配置
useEffect(() => {
if (
autoLaunchEnabled !== undefined &&
verge &&
verge.enable_auto_launch !== autoLaunchEnabled
) {
if (autoLaunchEnabled !== undefined && verge && verge.enable_auto_launch !== autoLaunchEnabled) {
// 静默更新配置不触发UI刷新
mutateVerge({ ...verge, enable_auto_launch: autoLaunchEnabled }, false);
}
}, [autoLaunchEnabled]);
// 是否以sidecar模式运行
const isSidecarMode = runningMode === "sidecar";
const sysproxyRef = useRef<DialogRef>(null);
const tunRef = useRef<DialogRef>(null);
@ -109,12 +103,12 @@ const SettingSystem = ({ onError }: Props) => {
icon={SettingsRounded}
onClick={() => tunRef.current?.open()}
/>
{isSidecarMode && !isAdminMode && (
{isSidecarMode && (
<Tooltip title={t("TUN requires Service Mode")}>
<WarningRounded sx={{ color: "warning.main", mr: 1 }} />
</Tooltip>
)}
{isSidecarMode && !isAdminMode && (
{isSidecarMode && (
<Tooltip title={t("Install Service")}>
<Button
variant="outlined"
@ -136,20 +130,20 @@ const SettingSystem = ({ onError }: Props) => {
onCatch={onError}
onFormat={onSwitchFormat}
onChange={(e) => {
// 当在sidecar模式下且非管理员模式时禁用切换
if (isSidecarMode && !isAdminMode) return;
// 当在sidecar模式下禁用切换
if (isSidecarMode) return;
onChangeData({ enable_tun_mode: e });
}}
onGuard={(e) => {
// 当在sidecar模式下且非管理员模式时禁用切换
if (isSidecarMode && !isAdminMode) {
// 当在sidecar模式下禁用切换
if (isSidecarMode) {
Notice.error(t("TUN requires Service Mode"), 2000);
return Promise.reject(new Error(t("TUN requires Service Mode")));
}
return patchVerge({ enable_tun_mode: e });
}}
>
<Switch edge="end" disabled={isSidecarMode && !isAdminMode} />
<Switch edge="end" disabled={isSidecarMode} />
</GuardState>
</SettingItem>
<SettingItem
@ -190,32 +184,14 @@ const SettingSystem = ({ onError }: Props) => {
</GuardState>
</SettingItem>
<SettingItem
label={t("Auto Launch")}
extra={
isAdminMode && (
<Tooltip title={t("Administrator mode does not support auto launch")}>
<WarningRounded sx={{ color: "warning.main", mr: 1 }} />
</Tooltip>
)
}
>
<SettingItem label={t("Auto Launch")}>
<GuardState
value={enable_auto_launch ?? false}
valueProps="checked"
onCatch={onError}
onFormat={onSwitchFormat}
onChange={(e) => {
// 在管理员模式下禁用更改
if (isAdminMode) return;
onChangeData({ enable_auto_launch: e });
}}
onChange={(e) => onChangeData({ enable_auto_launch: e })}
onGuard={async (e) => {
if (isAdminMode) {
Notice.error(t("Administrator mode does not support auto launch"), 2000);
return Promise.reject(new Error(t("Administrator mode does not support auto launch")));
}
try {
// 在应用更改之前先触发UI更新让用户立即看到反馈
onChangeData({ enable_auto_launch: e });
@ -230,7 +206,7 @@ const SettingSystem = ({ onError }: Props) => {
}
}}
>
<Switch edge="end" disabled={isAdminMode} />
<Switch edge="end" />
</GuardState>
</SettingItem>

View File

@ -52,7 +52,7 @@ const ProxyControlSwitches = ({ label, onError }: ProxySwitchProps) => {
);
// 是否以sidecar模式运行
const isSidecarMode = runningMode === "Sidecar";
const isSidecarMode = runningMode === "sidecar";
const sysproxyRef = useRef<DialogRef>(null);
const tunRef = useRef<DialogRef>(null);
@ -138,7 +138,7 @@ const ProxyControlSwitches = ({ label, onError }: ProxySwitchProps) => {
{t("System Proxy")}
</Typography>
{/* <Typography variant="caption" color="text.secondary">
{sysproxy?.enable
{sysproxy?.enable
? t("Proxy is active")
: t("Enable this for most users")
}

View File

@ -1,25 +1,32 @@
import useSWR from "swr";
import { useMemo } from "react";
import { useAppData } from "@/providers/app-data-provider";
// 定义代理组类型
interface ProxyGroup {
name: string;
now: string;
}
import { getProxies } from "@/services/api";
import { getClashConfig } from "@/services/api";
// 获取当前代理节点信息的自定义Hook
export const useCurrentProxy = () => {
// 从AppDataProvider获取数据
const { proxies, clashConfig, refreshProxy } = useAppData();
// 获取代理信息
const { data: proxiesData, mutate: mutateProxies } = useSWR(
"getProxies",
getProxies,
{
refreshInterval: 2000,
revalidateOnFocus: false,
revalidateOnReconnect: true,
},
);
// 获取当前Clash配置包含模式信息
const { data: clashConfig } = useSWR("getClashConfig", getClashConfig);
// 获取当前模式
const currentMode = clashConfig?.mode?.toLowerCase() || "rule";
// 获取当前代理节点信息
const currentProxyInfo = useMemo(() => {
if (!proxies) return { currentProxy: null, primaryGroupName: null };
if (!proxiesData) return { currentProxy: null, primaryGroupName: null };
const { global, groups, records } = proxies;
const { global, groups, records } = proxiesData;
// 默认信息
let primaryGroupName = "GLOBAL";
@ -36,11 +43,11 @@ export const useCurrentProxy = () => {
"自动选择",
];
const primaryGroup =
groups.find((group: ProxyGroup) =>
groups.find((group) =>
primaryKeywords.some((keyword) =>
group.name.toLowerCase().includes(keyword.toLowerCase()),
),
) || groups.filter((g: ProxyGroup) => g.name !== "GLOBAL")[0];
) || groups.filter((g) => g.name !== "GLOBAL")[0];
if (primaryGroup) {
primaryGroupName = primaryGroup.name;
@ -64,12 +71,12 @@ export const useCurrentProxy = () => {
};
return { currentProxy, primaryGroupName };
}, [proxies, currentMode]);
}, [proxiesData, currentMode]);
return {
currentProxy: currentProxyInfo.currentProxy,
primaryGroupName: currentProxyInfo.primaryGroupName,
mode: currentMode,
refreshProxy,
refreshProxy: mutateProxies,
};
};

View File

@ -1,29 +0,0 @@
import useSWR from "swr";
import { getRunningMode, isAdmin } from "@/services/cmds";
/**
* hook
*
*/
export function useSystemState() {
// 获取运行模式
const { data: runningMode = "Sidecar", mutate: mutateRunningMode } =
useSWR("getRunningMode", getRunningMode, {
suspense: false,
revalidateOnFocus: false
});
// 获取管理员状态
const { data: isAdminMode = false } =
useSWR("isAdmin", isAdmin, {
suspense: false,
revalidateOnFocus: false
});
return {
runningMode,
isAdminMode,
isSidecarMode: runningMode === "Sidecar",
mutateRunningMode
};
}

View File

@ -27,7 +27,6 @@
"Proxies": "Proxies",
"Proxy Groups": "Proxy Groups",
"Proxy Provider": "Proxy Provider",
"Proxy Count": "Proxy Count",
"Update All": "Update All",
"Update At": "Update At",
"rule": "rule",
@ -202,8 +201,6 @@
"Tun Mode": "Tun Mode",
"TUN requires Service Mode": "TUN mode requires install service",
"Install Service": "Install Service",
"Install Service failed": "Install Service failed",
"Restart Core failed": "Restart Core failed",
"Reset to Default": "Reset to Default",
"Tun Mode Info": "Tun (Virtual NIC) mode: Captures all system traffic, when enabled, there is no need to enable system proxy.",
"System Proxy Enabled": "System proxy is enabled, your applications will access the network through the proxy",
@ -255,7 +252,6 @@
"PAC Script Content": "PAC Script Content",
"PAC URL": "PAC URL: ",
"Auto Launch": "Auto Launch",
"Administrator mode does not support auto launch": "Administrator mode does not support auto launch",
"Silent Start": "Silent Start",
"Silent Start Info": "Start the program in background mode without displaying the panel",
"TG Channel": "Telegram Channel",
@ -492,9 +488,8 @@
"Validation Failed": "Validation Failed",
"Service Administrator Prompt": "Clash Verge requires administrator privileges to reinstall the system service",
"DNS Settings": "DNS Settings",
"DNS settings saved": "DNS settings saved",
"DNS Overwrite": "DNS Overwrite",
"DNS Settings Warning": "If you are not familiar with these settings, please do not modify them and keep DNS Overwrite enabled",
"DNS Settings Warning": "If you are not familiar with these settings, please do not modify them and keep DNS Settings enabled",
"Enable DNS": "Enable DNS",
"DNS Listen": "DNS Listen",
"Enhanced Mode": "Enhanced Mode",
@ -555,19 +550,9 @@
"OS Info": "OS Info",
"Running Mode": "Running Mode",
"Sidecar Mode": "User Mode",
"Administrator Mode": "Administrator Mode",
"Administrator + Service Mode": "Admin + Service Mode",
"Last Check Update": "Last Check Update",
"Click to import subscription": "Click to import subscription",
"Update subscription successfully": "Update subscription successfully",
"Update failed, retrying with Clash proxy...": "Update failed, retrying with Clash proxy...",
"Update with Clash proxy successfully": "Update with Clash proxy successfully",
"Update failed even with Clash proxy": "Update failed even with Clash proxy",
"Profile creation failed, retrying with Clash proxy...": "Profile creation failed, retrying with Clash proxy...",
"Profile creation succeeded with Clash proxy": "Profile creation succeeded with Clash proxy",
"Import failed, retrying with Clash proxy...": "Import failed, retrying with Clash proxy...",
"Profile Imported with Clash proxy": "Profile Imported with Clash proxy",
"Import failed even with Clash proxy": "Import failed even with Clash proxy",
"Current Node": "Current Node",
"No active proxy node": "No active proxy node",
"Network Settings": "Network Settings",

View File

@ -27,7 +27,6 @@
"Proxies": "代理",
"Proxy Groups": "代理组",
"Proxy Provider": "代理集合",
"Proxy Count": "节点数量",
"Update All": "更新全部",
"Update At": "更新于",
"rule": "规则",
@ -202,8 +201,6 @@
"Tun Mode": "虚拟网卡模式",
"TUN requires Service Mode": "TUN 模式需要安装服务",
"Install Service": "安装服务",
"Install Service failed": "安装服务失败",
"Restart Core failed": "重启核心失败",
"Reset to Default": "重置为默认值",
"Tun Mode Info": "TUN虚拟网卡模式接管系统所有流量启用时无须打开系统代理",
"System Proxy Enabled": "系统代理已启用,您的应用将通过代理访问网络",
@ -255,7 +252,6 @@
"PAC Script Content": "PAC 脚本内容",
"PAC URL": "PAC 地址:",
"Auto Launch": "开机自启",
"Administrator mode does not support auto launch": "管理员模式不支持开机自启",
"Silent Start": "静默启动",
"Silent Start Info": "程序启动时以后台模式运行,不显示程序面板",
"TG Channel": "Telegram 频道",
@ -357,6 +353,7 @@
"clash_mode_direct": "直连模式",
"toggle_system_proxy": "打开/关闭系统代理",
"toggle_tun_mode": "打开/关闭 TUN 模式",
"toggle_lightweight_mode": "进入轻量模式",
"entry_lightweight_mode": "进入轻量模式",
"Backup Setting": "备份设置",
"Backup Setting Info": "支持 WebDAV 备份配置文件",
@ -490,9 +487,8 @@
"Validate Merge File": "验证覆写文件",
"Validation Success": "验证成功",
"Validation Failed": "验证失败",
"Service Administrator Prompt": "Clash Verge 需要管理员权限安装系统服务",
"Service Administrator Prompt": "Clash Verge 需要使用管理员权限来重新安装系统服务",
"DNS Settings": "DNS 设置",
"DNS settings saved": "DNS 设置已保存",
"DNS Overwrite": "DNS 覆写",
"DNS Settings Warning": "如果你不清楚这里的设置请不要修改,并保持 DNS 覆写开启",
"Enable DNS": "启用 DNS",
@ -555,19 +551,9 @@
"OS Info": "操作系统信息",
"Running Mode": "运行模式",
"Sidecar Mode": "用户模式",
"Administrator Mode": "管理员模式",
"Administrator + Service Mode": "管理员 + 服务模式",
"Last Check Update": "最后检查更新",
"Click to import subscription": "点击导入订阅",
"Update subscription successfully": "订阅更新成功",
"Update failed, retrying with Clash proxy...": "订阅更新失败,尝试使用 Clash 代理更新",
"Update with Clash proxy successfully": "使用 Clash 代理更新成功",
"Update failed even with Clash proxy": "使用 Clash 代理更新也失败",
"Profile creation failed, retrying with Clash proxy...": "订阅创建失败,尝试使用 Clash 代理创建",
"Profile creation succeeded with Clash proxy": "使用 Clash 代理创建订阅成功",
"Import failed, retrying with Clash proxy...": "订阅导入失败,尝试使用 Clash 代理导入",
"Profile Imported with Clash proxy": "使用 Clash 代理导入订阅成功",
"Import failed even with Clash proxy": "使用 Clash 代理导入订阅也失败",
"Current Node": "当前节点",
"No active proxy node": "暂无激活的代理节点",
"Network Settings": "网络设置",

View File

@ -19,7 +19,6 @@ import {
ThemeModeProvider,
UpdateStateProvider,
} from "./services/states";
import { AppDataProvider } from "./providers/app-data-provider";
const mainElementId = "root";
const container = document.getElementById(mainElementId);
@ -52,11 +51,9 @@ createRoot(container).render(
<React.StrictMode>
<ComposeContextProvider contexts={contexts}>
<BaseErrorBoundary>
<AppDataProvider>
<BrowserRouter>
<Layout />
</BrowserRouter>
</AppDataProvider>
<BrowserRouter>
<Layout />
</BrowserRouter>
</BaseErrorBoundary>
</ComposeContextProvider>
</React.StrictMode>

View File

@ -249,8 +249,8 @@ const Layout = () => {
? {
borderRadius: "8px",
border: "1px solid var(--divider-color)",
width: "calc(100vw - 4px)",
height: "calc(100vh - 4px)",
width: "calc(100vw - 0px)",
height: "calc(100vh - 0px)",
}
: {},
]}

View File

@ -11,6 +11,7 @@ import {
} from "@mui/icons-material";
import { closeAllConnections } from "@/services/api";
import { useConnectionSetting } from "@/services/states";
import { useClashInfo } from "@/hooks/use-clash";
import { BaseEmpty, BasePage } from "@/components/base";
import { ConnectionItem } from "@/components/connection/connection-item";
import { ConnectionTable } from "@/components/connection/connection-table";
@ -24,9 +25,10 @@ import {
type SearchState,
} from "@/components/base/base-search-box";
import { BaseStyledSelect } from "@/components/base/base-styled-select";
import useSWRSubscription from "swr/subscription";
import { createSockette, createAuthSockette } from "@/utils/websocket";
import { useTheme } from "@mui/material/styles";
import { useVisibility } from "@/hooks/use-visibility";
import { useAppData } from "@/providers/app-data-provider";
const initConn: IConnections = {
uploadTotal: 0,
@ -38,14 +40,12 @@ type OrderFunc = (list: IConnectionsItem[]) => IConnectionsItem[];
const ConnectionsPage = () => {
const { t } = useTranslation();
const { clashInfo } = useClashInfo();
const pageVisible = useVisibility();
const theme = useTheme();
const isDark = theme.palette.mode === "dark";
const [match, setMatch] = useState(() => (_: string) => true);
const [curOrderOpt, setOrderOpt] = useState("Default");
// 使用全局数据
const { connections } = useAppData();
const [setting, setSetting] = useConnectionSetting();
@ -66,37 +66,99 @@ const ConnectionsPage = () => {
const [isPaused, setIsPaused] = useState(false);
const [frozenData, setFrozenData] = useState<IConnections | null>(null);
// 使用全局连接数据
const displayData = useMemo(() => {
if (!pageVisible) return initConn;
if (isPaused) {
return frozenData ?? {
uploadTotal: connections.uploadTotal,
downloadTotal: connections.downloadTotal,
connections: connections.data
const { data: connData = initConn } = useSWRSubscription<
IConnections,
any,
"getClashConnections" | null
>(
clashInfo && pageVisible ? "getClashConnections" : null,
(_key, { next }) => {
const { server = "", secret = "" } = clashInfo!;
if (!server) {
console.warn("[Connections] 服务器地址为空,无法建立连接");
next(null, initConn);
return () => {};
}
console.log(`[Connections] 正在连接: ${server}/connections`);
// 设置较长的超时时间,确保连接可以建立
const s = createAuthSockette(`${server}/connections`, secret, {
timeout: 8000, // 8秒超时
onmessage(event) {
const data = JSON.parse(event.data) as IConnections;
next(null, (old = initConn) => {
const oldConn = old.connections;
const maxLen = data.connections?.length;
const connections: IConnectionsItem[] = [];
const rest = (data.connections || []).filter((each) => {
const index = oldConn.findIndex((o) => o.id === each.id);
if (index >= 0 && index < maxLen) {
const old = oldConn[index];
each.curUpload = each.upload - old.upload;
each.curDownload = each.download - old.download;
connections[index] = each;
return false;
}
return true;
});
for (let i = 0; i < maxLen; ++i) {
if (!connections[i] && rest.length > 0) {
connections[i] = rest.shift()!;
connections[i].curUpload = 0;
connections[i].curDownload = 0;
}
}
return { ...data, connections };
});
},
onerror(event) {
console.error("[Connections] WebSocket 连接错误", event);
// 报告错误但提供空数据避免UI崩溃
next(null, initConn);
},
onclose(event) {
console.log("[Connections] WebSocket 连接关闭", event);
},
onopen(event) {
console.log("[Connections] WebSocket 连接已建立");
},
});
return () => {
console.log("[Connections] 清理WebSocket连接");
try {
s.close();
} catch (e) {
console.error("[Connections] 关闭连接时出错", e);
}
};
}
return {
uploadTotal: connections.uploadTotal,
downloadTotal: connections.downloadTotal,
connections: connections.data
};
}, [isPaused, frozenData, connections, pageVisible]);
},
);
const displayData = useMemo(() => {
return isPaused ? (frozenData ?? connData) : connData;
}, [isPaused, frozenData, connData]);
const [filterConn] = useMemo(() => {
const orderFunc = orderOpts[curOrderOpt];
let conns = displayData.connections.filter((conn) => {
let connections = displayData.connections.filter((conn) => {
const { host, destinationIP, process } = conn.metadata;
return (
match(host || "") || match(destinationIP || "") || match(process || "")
);
});
if (orderFunc) conns = orderFunc(conns);
if (orderFunc) connections = orderFunc(connections);
return [conns];
return [connections];
}, [displayData, match, curOrderOpt]);
const onCloseAll = useLockFn(closeAllConnections);
@ -110,17 +172,13 @@ const ConnectionsPage = () => {
const handlePauseToggle = useCallback(() => {
setIsPaused((prev) => {
if (!prev) {
setFrozenData({
uploadTotal: connections.uploadTotal,
downloadTotal: connections.downloadTotal,
connections: connections.data
});
setFrozenData(connData);
} else {
setFrozenData(null);
}
return !prev;
});
}, [connections]);
}, [connData]);
return (
<BasePage

View File

@ -203,7 +203,7 @@ const HomeSettingsDialog = ({
);
};
export const HomePage = () => {
const HomePage = () => {
const { t } = useTranslation();
const { verge } = useVerge();
const { current, mutateProfiles } = useProfiles();
@ -322,6 +322,7 @@ export const HomePage = () => {
title={t("Traffic Stats")}
icon={<SpeedOutlined />}
iconColor="secondary"
minHeight={280}
>
<EnhancedTrafficStats />
</EnhancedCard>
@ -394,4 +395,4 @@ const ClashModeEnhancedCard = () => {
);
};
export default HomePage;
export default HomePage;

View File

@ -142,36 +142,15 @@ const ProfilePage = () => {
setLoading(true);
try {
// 尝试正常导入
await importProfile(url);
Notice.success(t("Profile Imported Successfully"));
setUrl("");
setLoading(false);
mutateProfiles();
await onEnhance(false);
} catch (err: any) {
// 首次导入失败,尝试使用自身代理
const errmsg = err.message || err.toString();
Notice.info(t("Import failed, retrying with Clash proxy..."));
try {
// 使用自身代理尝试导入
await importProfile(url, {
with_proxy: false,
self_proxy: true
});
// 回退导入成功
Notice.success(t("Profile Imported with Clash proxy"));
setUrl("");
mutateProfiles();
await onEnhance(false);
} catch (retryErr: any) {
// 回退导入也失败
const retryErrmsg = retryErr?.message || retryErr.toString();
Notice.error(
`${t("Import failed even with Clash proxy")}: ${retryErrmsg}`,
);
}
Notice.error(err.message || err.toString());
setLoading(false);
} finally {
setDisabled(false);
setLoading(false);

View File

@ -1,27 +1,28 @@
import useSWR from "swr";
import { useState, useMemo, useRef } from "react";
import { useTranslation } from "react-i18next";
import { Virtuoso, VirtuosoHandle } from "react-virtuoso";
import { Box } from "@mui/material";
import { getRules } from "@/services/api";
import { BaseEmpty, BasePage } from "@/components/base";
import RuleItem from "@/components/rule/rule-item";
import { ProviderButton } from "@/components/rule/provider-button";
import { BaseSearchBox } from "@/components/base/base-search-box";
import { useTheme } from "@mui/material/styles";
import { ScrollTopButton } from "@/components/layout/scroll-top-button";
import { useAppData } from "@/providers/app-data-provider";
const RulesPage = () => {
const { t } = useTranslation();
const { rules = [] } = useAppData();
const { data = [] } = useSWR("getRules", getRules);
const theme = useTheme();
const isDark = theme.palette.mode === "dark";
const [match, setMatch] = useState(() => (_: string) => true);
const virtuosoRef = useRef<VirtuosoHandle>(null);
const [showScrollTop, setShowScrollTop] = useState(false);
const filteredRules = useMemo(() => {
return rules.filter((item) => match(item.payload));
}, [rules, match]);
const rules = useMemo(() => {
return data.filter((item) => match(item.payload));
}, [data, match]);
const scrollToTop = () => {
virtuosoRef.current?.scrollTo({
@ -63,11 +64,11 @@ const RulesPage = () => {
<BaseSearchBox onSearch={(match) => setMatch(() => match)} />
</Box>
{filteredRules.length > 0 ? (
{rules.length > 0 ? (
<>
<Virtuoso
ref={virtuosoRef}
data={filteredRules}
data={rules}
style={{
flex: 1,
}}

View File

@ -1,299 +0,0 @@
import { createContext, useContext, useMemo } from "react";
import useSWR from "swr";
import useSWRSubscription from "swr/subscription";
import { getProxies, getConnections, getRules, getClashConfig, getProxyProviders, getRuleProviders } from "@/services/api";
import { getSystemProxy, getRunningMode, getAppUptime } from "@/services/cmds";
import { useClashInfo } from "@/hooks/use-clash";
import { createAuthSockette } from "@/utils/websocket";
import { useVisibility } from "@/hooks/use-visibility";
// 定义AppDataContext类型 - 使用宽松类型
interface AppDataContextType {
proxies: any;
clashConfig: any;
rules: any[];
sysproxy: any;
runningMode?: string;
uptime: number;
proxyProviders: any;
ruleProviders: any;
connections: {
data: any[];
count: number;
uploadTotal: number;
downloadTotal: number;
};
traffic: {up: number; down: number};
memory: {inuse: number};
refreshProxy: () => Promise<any>;
refreshClashConfig: () => Promise<any>;
refreshRules: () => Promise<any>;
refreshSysproxy: () => Promise<any>;
refreshProxyProviders: () => Promise<any>;
refreshRuleProviders: () => Promise<any>;
refreshAll: () => Promise<any>;
}
// 创建上下文
const AppDataContext = createContext<AppDataContextType | null>(null);
// 全局数据提供者组件
export const AppDataProvider = ({ children }: { children: React.ReactNode }) => {
const { clashInfo } = useClashInfo();
const pageVisible = useVisibility();
// 基础数据 - 中频率更新 (5秒)
const { data: proxiesData, mutate: refreshProxy } = useSWR(
"getProxies",
getProxies,
{
refreshInterval: 5000,
revalidateOnFocus: false,
suspense: false,
errorRetryCount: 3
}
);
const { data: clashConfig, mutate: refreshClashConfig } = useSWR(
"getClashConfig",
getClashConfig,
{
refreshInterval: 5000,
revalidateOnFocus: false,
suspense: false,
errorRetryCount: 3
}
);
// 提供者数据
const { data: proxyProviders, mutate: refreshProxyProviders } = useSWR(
"getProxyProviders",
getProxyProviders,
{
revalidateOnFocus: false,
suspense: false,
errorRetryCount: 3
}
);
const { data: ruleProviders, mutate: refreshRuleProviders } = useSWR(
"getRuleProviders",
getRuleProviders,
{
revalidateOnFocus: false,
suspense: false,
errorRetryCount: 3
}
);
// 低频率更新数据
const { data: rulesData, mutate: refreshRules } = useSWR(
"getRules",
getRules,
{
revalidateOnFocus: false,
suspense: false,
errorRetryCount: 3
}
);
const { data: sysproxy, mutate: refreshSysproxy } = useSWR(
"getSystemProxy",
getSystemProxy,
{
revalidateOnFocus: false,
suspense: false,
errorRetryCount: 3
}
);
const { data: runningMode } = useSWR(
"getRunningMode",
getRunningMode,
{
revalidateOnFocus: false,
suspense: false,
errorRetryCount: 3
}
);
// 高频率更新数据 (1秒)
const { data: uptimeData } = useSWR(
"appUptime",
getAppUptime,
{
refreshInterval: 1000,
revalidateOnFocus: false,
suspense: false
}
);
// 连接数据 - 使用WebSocket实时更新
const { data: connectionsData = { connections: [], uploadTotal: 0, downloadTotal: 0 } } =
useSWRSubscription(
clashInfo && pageVisible ? "connections" : null,
(_key, { next }) => {
if (!clashInfo || !pageVisible) return () => {};
const { server = "", secret = "" } = clashInfo;
if (!server) return () => {};
const socket = createAuthSockette(`${server}/connections`, secret, {
timeout: 5000,
onmessage(event) {
try {
const data = JSON.parse(event.data);
// 处理连接数据,计算当前上传下载速度
next(null, (prev: any = { connections: [], uploadTotal: 0, downloadTotal: 0 }) => {
const oldConns = prev.connections || [];
const newConns = data.connections || [];
// 计算当前速度
const processedConns = newConns.map((conn: any) => {
const oldConn = oldConns.find((old: any) => old.id === conn.id);
if (oldConn) {
return {
...conn,
curUpload: conn.upload - oldConn.upload,
curDownload: conn.download - oldConn.download
};
}
return { ...conn, curUpload: 0, curDownload: 0 };
});
return {
...data,
connections: processedConns
};
});
} catch (err) {
console.error("[Connections] 解析数据错误:", err);
}
},
onerror() {
next(null, { connections: [], uploadTotal: 0, downloadTotal: 0 });
}
});
return () => socket.close();
}
);
// 流量和内存数据 - 通过WebSocket获取实时流量数据
const { data: trafficData = { up: 0, down: 0 } } = useSWRSubscription(
clashInfo && pageVisible ? "traffic" : null,
(_key, { next }) => {
if (!clashInfo || !pageVisible) return () => {};
const { server = "", secret = "" } = clashInfo;
if (!server) return () => {};
const socket = createAuthSockette(`${server}/traffic`, secret, {
onmessage(event) {
try {
const data = JSON.parse(event.data);
next(null, data);
} catch (err) {
console.error("[Traffic] 解析数据错误:", err);
}
}
});
return () => socket.close();
}
);
const { data: memoryData = { inuse: 0 } } = useSWRSubscription(
clashInfo && pageVisible ? "memory" : null,
(_key, { next }) => {
if (!clashInfo || !pageVisible) return () => {};
const { server = "", secret = "" } = clashInfo;
if (!server) return () => {};
const socket = createAuthSockette(`${server}/memory`, secret, {
onmessage(event) {
try {
const data = JSON.parse(event.data);
next(null, data);
} catch (err) {
console.error("[Memory] 解析数据错误:", err);
}
}
});
return () => socket.close();
}
);
// 提供统一的刷新方法
const refreshAll = async () => {
await Promise.all([
refreshProxy(),
refreshClashConfig(),
refreshRules(),
refreshSysproxy(),
refreshProxyProviders(),
refreshRuleProviders()
]);
};
// 聚合所有数据
const value = useMemo(() => ({
// 数据
proxies: proxiesData,
clashConfig,
rules: rulesData || [],
sysproxy,
runningMode,
uptime: uptimeData || 0,
// 提供者数据
proxyProviders: proxyProviders || {},
ruleProviders: ruleProviders || {},
// 连接数据
connections: {
data: connectionsData.connections || [],
count: connectionsData.connections?.length || 0,
uploadTotal: connectionsData.uploadTotal || 0,
downloadTotal: connectionsData.downloadTotal || 0
},
// 实时流量数据
traffic: trafficData,
memory: memoryData,
// 刷新方法
refreshProxy,
refreshClashConfig,
refreshRules,
refreshSysproxy,
refreshProxyProviders,
refreshRuleProviders,
refreshAll
}), [
proxiesData, clashConfig, rulesData, sysproxy,
runningMode, uptimeData, connectionsData,
trafficData, memoryData, proxyProviders, ruleProviders,
refreshProxy, refreshClashConfig, refreshRules, refreshSysproxy,
refreshProxyProviders, refreshRuleProviders
]);
return (
<AppDataContext.Provider value={value}>
{children}
</AppDataContext.Provider>
);
};
// 自定义Hook访问全局数据
export const useAppData = () => {
const context = useContext(AppDataContext);
if (!context) {
throw new Error("useAppData必须在AppDataProvider内使用");
}
return context;
};

View File

@ -3,9 +3,13 @@ import { getClashInfo } from "./cmds";
import { invoke } from "@tauri-apps/api/core";
import { useLockFn } from "ahooks";
let instancePromise: Promise<AxiosInstance> = null!;
let axiosIns: AxiosInstance = null!;
/// initialize some information
/// enable force update axiosIns
export const getAxios = async (force: boolean = false) => {
if (axiosIns && !force) return axiosIns;
async function getInstancePromise() {
let server = "";
let secret = "";
@ -22,22 +26,13 @@ async function getInstancePromise() {
if (info?.secret) secret = info?.secret;
} catch {}
const axiosIns = axios.create({
axiosIns = axios.create({
baseURL: `http://${server}`,
headers: secret ? { Authorization: `Bearer ${secret}` } : {},
timeout: 15000,
});
axiosIns.interceptors.response.use((r) => r.data);
return axiosIns;
}
/// initialize some information
/// enable force update axiosIns
export const getAxios = async (force: boolean = false) => {
if (!instancePromise || force) {
instancePromise = getInstancePromise();
}
return instancePromise;
};
/// Get Version

View File

@ -1,3 +1,4 @@
import dayjs from "dayjs";
import { invoke } from "@tauri-apps/api/core";
import { Notice } from "@/components/base";
@ -36,10 +37,10 @@ export async function saveProfileFile(index: string, fileData: string) {
return invoke<void>("save_profile_file", { index, fileData });
}
export async function importProfile(url: string, option?: IProfileOption) {
export async function importProfile(url: string) {
return invoke<void>("import_profile", {
url,
option: option || { with_proxy: true },
option: { with_proxy: true },
});
}
@ -319,39 +320,15 @@ export const getAppUptime = async () => {
return invoke<number>("get_app_uptime");
};
// 安装系统服务
// 安装/重装系统服务
export const installService = async () => {
return invoke<void>("install_service");
};
// 卸载系统服务
export const uninstallService = async () => {
return invoke<void>("uninstall_service");
};
// 重装系统服务
export const reinstallService = async () => {
return invoke<void>("reinstall_service");
};
// 修复系统服务
export const repairService = async () => {
return invoke<void>("repair_service");
};
export const entry_lightweight_mode = async () => {
return invoke<void>("entry_lightweight_mode");
};
}
export const exit_lightweight_mode = async () => {
return invoke<void>("exit_lightweight_mode");
};
export const isAdmin = async () => {
try {
return await invoke<boolean>("is_admin");
} catch (error) {
console.error("检查管理员权限失败:", error);
return false;
}
};
}

View File

@ -36,96 +36,11 @@ export default defineConfig({
entry: "monaco-yaml/yaml.worker",
},
],
globalAPI: false,
}),
],
build: {
outDir: "../dist",
emptyOutDir: true,
target: "es2020",
minify: "terser",
chunkSizeWarningLimit: 4000,
reportCompressedSize: false,
sourcemap: false,
cssCodeSplit: true,
cssMinify: true,
rollupOptions: {
treeshake: {
preset: "recommended",
moduleSideEffects: (id) => !/\.css$/.test(id),
tryCatchDeoptimization: false,
},
output: {
compact: true,
experimentalMinChunkSize: 30000,
dynamicImportInCjs: true,
manualChunks(id) {
if (id.includes("node_modules")) {
// Monaco Editor should be a separate chunk
if (id.includes("monaco-editor")) return "monaco-editor";
// React-related libraries (react, react-dom, react-router-dom, etc.)
if (
id.includes("react") ||
id.includes("react-dom") ||
id.includes("react-router-dom") ||
id.includes("react-transition-group") ||
id.includes("react-error-boundary") ||
id.includes("react-hook-form") ||
id.includes("react-markdown") ||
id.includes("react-virtuoso")
) {
return "react";
}
// Utilities chunk: group commonly used utility libraries
if (
id.includes("axios") ||
id.includes("lodash-es") ||
id.includes("dayjs") ||
id.includes("js-base64") ||
id.includes("js-yaml") ||
id.includes("cli-color") ||
id.includes("nanoid")
) {
return "utils";
}
// Tauri-related plugins: grouping together Tauri plugins
if (
id.includes("@tauri-apps/api") ||
id.includes("@tauri-apps/plugin-clipboard-manager") ||
id.includes("@tauri-apps/plugin-dialog") ||
id.includes("@tauri-apps/plugin-fs") ||
id.includes("@tauri-apps/plugin-global-shortcut") ||
id.includes("@tauri-apps/plugin-notification") ||
id.includes("@tauri-apps/plugin-process") ||
id.includes("@tauri-apps/plugin-shell") ||
id.includes("@tauri-apps/plugin-updater")
) {
return "tauri-plugins";
}
// Material UI libraries (grouped together)
if (
id.includes("@mui/material") ||
id.includes("@mui/icons-material") ||
id.includes("@mui/lab") ||
id.includes("@mui/x-data-grid")
) {
return "mui";
}
// Small vendor packages
const pkg = id.match(/node_modules\/([^\/]+)/)?.[1];
if (pkg && pkg.length < 8) return "small-vendors";
// Large vendor packages
return "large-vendor";
}
},
},
},
},
resolve: {
alias: {