Compare commits

...
968 Commits
Author SHA1 Message Date
wonfen 2bbcdb1d95 Release 2.2.2 2025-03-25 14:11:40 +08:00
wonfen 646df28d1c feat: improve mihomo core and service keep-alive and reinstallation logic 2025-03-25 06:41:00 +08:00
Tunglies f5f2ee1f95 chore: rename updater alpha release json names 2025-03-25 01:54:44 +08:00
Tunglies 0842c7343d chore: updater channel logic 2025-03-25 01:49:51 +08:00
Tunglies 20e93ecc8e Revert "chore: updater channel combined"
This reverts commit aeaab10fcb.
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 46af096cb1.
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 1d5471e70d.
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 ff8426d95e.
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 b31cbda615.
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 4382df7951.
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 d2c4a44297.
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
zhaoyuanandzymouse 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 18c7ed1ccc.
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
downerand周晓亮 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 e3c52781f3.
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 3df4718709.
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 b8cb48aead.
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 4b7ffa1465.
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 40ff3fd4bf.
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 e14ddbe23c.
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 38eb3123be.
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 a0f9fb90ee.
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 69b65d06a4.
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 7e361cb3f1.
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
312 changed files with 41699 additions and 12425 deletions
+5
View File
@@ -0,0 +1,5 @@
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
+17 -8
View File
@@ -2,29 +2,38 @@ name: 问题反馈 / Bug report
title: "[BUG] "
description: 反馈你遇到的问题 / Report the issue you are experiencing
labels: ["bug"]
type: "Bug"
body:
- type: markdown
attributes:
value: |
## 在提交问题之前,请确认以下事项:
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide.html) 以及 [常见问题](https://clash-verge-rev.github.io/faq/install/)
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide/term.html) 以及 [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似issue,否则请在已有的issue下进行讨论
3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索
4. 请 **务必** 先下载 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本测试,确保问题依然存在
5. 请 **务必** 按照模板规范详细描述问题,否则issue将会被关闭
4. 请 **务必** 查看 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本更新日志
5. 请 **务必** 尝试 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本,确定问题是否仍然存在
6. 请 **务必** 按照模板规范详细描述问题以及尝试更新 Alpha 版本,否则issue将会被直接关闭
## Before submitting the issue, please make sure of the following checklist:
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide.html) and [FAQ](https://clash-verge-rev.github.io/faq/install/)
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide/term.html) and [FAQ](https://clash-verge-rev.github.io/faq/windows.html)
2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue
3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search
4. Please be sure to download the [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version for testing to ensure that the problem still exists
5. Please describe the problem in detail according to the template specification, otherwise the issue will be closed
4. Please be sure to check out [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version update log
5. Please be sure to try the [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version to ensure that the problem still exists
6. Please describe the problem in detail according to the template specification and try to update the Alpha version, otherwise the issue will be closed
- type: textarea
id: description
attributes:
label: 问题描述 / Describe the bug
description: 详细清晰地描述你遇到的问题 / A clear and concise description of what the bug is
description: 详细清晰地描述你遇到的问题,并配合截图 / Describe the problem you encountered in detail and clearly, and provide screenshots
validations:
required: true
- type: textarea
attributes:
label: 软件版本 / Verge Version
description: 请提供Verge的具体版本,如果是alpha版本,请注明下载时间(精确到小时分钟) / Please provide the specific version of Verge. If it is an alpha version, please indicate the download time (accurate to hours and minutes)
validations:
required: true
- type: textarea
@@ -51,6 +60,6 @@ body:
- type: textarea
attributes:
label: 日志 / Log
description: 请提供完整或相关部分的Debug日志 / Please provide the complete or relevant part of the Debug log
description: 请提供完整或相关部分的Debug日志(请在“软件左侧菜单”->“设置”->“日志等级”调整到debug,Verge错误请把“杂项设置”->“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
+14 -2
View File
@@ -2,19 +2,20 @@ name: 功能请求 / Feature request
title: "[Feature] "
description: 提出你的功能请求 / Propose your feature request
labels: ["enhancement"]
type: "Feature"
body:
- type: markdown
attributes:
value: |
## 在提交问题之前,请确认以下事项:
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide.html) 确认软件不存在类似的功能
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide/term.html) 确认软件不存在类似的功能
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似issue,否则请在已有的issue下进行讨论
3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索
4. 请 **务必** 先下载 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本测试,确保该功能还未实现
5. 请 **务必** 按照模板规范详细描述问题,否则issue将会被关闭
## Before submitting the issue, please make sure of the following checklist:
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide.html) to confirm that the software does not have similar functions
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide/term.html) to confirm that the software does not have similar functions
2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue
3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search
4. Please be sure to download the [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version for testing to ensure that the function has not been implemented
@@ -33,3 +34,14 @@ body:
description: 请描述你的功能请求的使用场景 / Please describe the use case of your feature request
validations:
required: true
- type: checkboxes
id: os-labels
attributes:
label: 适用系统 / Target OS
description: 请选择该功能适用的操作系统(至少选择一个) / Please select the operating system(s) for this feature request (select at least one)
options:
- label: windows
- label: macos
- label: linux
validations:
required: true
-4
View File
@@ -1,4 +0,0 @@
FROM rust:buster
COPY entrypoint.sh /entrypoint.sh
RUN chmod a+x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
-14
View File
@@ -1,14 +0,0 @@
name: "Build for Linux"
branding:
icon: user-check
color: gray-dark
inputs:
target:
required: true
description: "Rust Target"
runs:
using: "docker"
image: "Dockerfile"
args:
- ${{ inputs.target }}
-8
View File
@@ -1,8 +0,0 @@
pnpm install
pnpm check $INPUT_TARGET
sed -i "s/#openssl/openssl={version=\"0.10\",features=[\"vendored\"]}/g" src-tauri/Cargo.toml
if [ "$INPUT_TARGET" = "x86_64-unknown-linux-gnu" ]; then
pnpm build --target $INPUT_TARGET
else
pnpm build --target $INPUT_TARGET -b deb
fi
-47
View File
@@ -1,47 +0,0 @@
#!/bin/bash
wget https://nodejs.org/dist/v20.10.0/node-v20.10.0-linux-x64.tar.xz
tar -Jxvf ./node-v20.10.0-linux-x64.tar.xz
export PATH=$(pwd)/node-v20.10.0-linux-x64/bin:$PATH
npm install pnpm -g
rustup target add "$INPUT_TARGET"
if [ "$INPUT_TARGET" = "x86_64-unknown-linux-gnu" ]; then
apt-get update
apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev patchelf
elif [ "$INPUT_TARGET" = "aarch64-unknown-linux-gnu" ]; then
dpkg --add-architecture arm64
apt-get update
apt-get install -y libncurses6:arm64 libtinfo6:arm64 linux-libc-dev:arm64 libncursesw6:arm64 libssl3:arm64 libcups2:arm64
apt-get install -y --no-install-recommends g++-aarch64-linux-gnu libc6-dev-arm64-cross libwebkit2gtk-4.0-dev:arm64 libgtk-3-dev:arm64 patchelf:arm64 librsvg2-dev:arm64 libayatana-appindicator3-dev:arm64
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc
export CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
export PKG_CONFIG_ALLOW_CROSS=1
elif [ "$INPUT_TARGET" = "armv7-unknown-linux-gnueabihf" ]; then
dpkg --add-architecture armhf
apt-get update
apt-get install -y libncurses6:armhf libtinfo6:armhf linux-libc-dev:armhf libncursesw6:armhf libssl3:armhf libcups2:armhf
apt-get install -y --no-install-recommends g++-arm-linux-gnueabihf libc6-dev-armhf-cross libwebkit2gtk-4.0-dev:armhf libgtk-3-dev:armhf patchelf:armhf librsvg2-dev:armhf libayatana-appindicator3-dev:armhf
export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
export CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc
export CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++
export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig
export PKG_CONFIG_ALLOW_CROSS=1
elif [ "$INPUT_TARGET" = "riscv64gc-unknown-linux-gnu" ]; then
dpkg --add-architecture riscv64
apt-get update
apt-get install -y libncurses6:riscv64 libtinfo6:riscv64 linux-libc-dev:riscv64 libncursesw6:riscv64 libssl3:riscv64 libcups2:riscv64
apt-get install -y --no-install-recommends g++-riscv64-linux-gnu libc6-dev-riscv64-cross libwebkit2gtk-4.0-dev:riscv64 libgtk-3-dev:riscv64 patchelf:riscv64 librsvg2-dev:riscv64 libayatana-appindicator3-dev:riscv64
export CARGO_TARGET_RISCV64_UNKNOWN_LINUX_GNU_LINKER=riscv64-linux-gnu-gcc
export CC_riscv64_unknown_linux_gnu=riscv64-linux-gnu-gcc
export CXX_riscv64_unknown_linux_gnu=riscv64-linux-gnu-g++
export PKG_CONFIG_PATH=/usr/lib/riscv64-linux-gnu/pkgconfig
export PKG_CONFIG_ALLOW_CROSS=1
else
echo "Unknown target: $INPUT_TARGET" && exit 1
fi
bash .github/build-for-linux/build.sh
+386 -95
View File
@@ -2,16 +2,197 @@ name: Alpha Build
on:
workflow_dispatch:
push:
branches: [main]
tags-ignore: [updater, alpha]
schedule:
# UTC+8 0,6,12,18
- cron: "0 16,22,4,10 * * *"
permissions: write-all
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
concurrency:
# only allow per workflow per commit (and not pr) to run at a time
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
check_commit:
runs-on: ubuntu-latest
outputs:
should_run: ${{ steps.check.outputs.should_run }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Check if version changed
id: check
run: |
# For manual workflow_dispatch, always run
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "should_run=true" >> $GITHUB_OUTPUT
exit 0
fi
# Store current version from package.json
CURRENT_VERSION=$(cat package.json | jq -r '.version')
echo "Current version: $CURRENT_VERSION"
# Get the previous commit's package.json version
git checkout HEAD~1 package.json
PREVIOUS_VERSION=$(cat package.json | jq -r '.version')
echo "Previous version: $PREVIOUS_VERSION"
# Reset back to current commit
git checkout HEAD package.json
# Check if version changed
if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then
echo "Version changed from $PREVIOUS_VERSION to $CURRENT_VERSION"
echo "should_run=true" >> $GITHUB_OUTPUT
else
echo "Version unchanged: $CURRENT_VERSION"
echo "should_run=false" >> $GITHUB_OUTPUT
fi
delete_old_assets:
needs: check_commit
if: ${{ needs.check_commit.outputs.should_run == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Delete Old Alpha Release Assets
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const releaseTag = 'alpha';
try {
// Get the release by tag name
const { data: release } = await github.rest.repos.getReleaseByTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag: releaseTag
});
console.log(`Found release with ID: ${release.id}`);
// Delete each asset
if (release.assets && release.assets.length > 0) {
console.log(`Deleting ${release.assets.length} assets`);
for (const asset of release.assets) {
console.log(`Deleting asset: ${asset.name} (${asset.id})`);
await github.rest.repos.deleteReleaseAsset({
owner: context.repo.owner,
repo: context.repo.repo,
asset_id: asset.id
});
}
console.log('All assets deleted successfully');
} else {
console.log('No assets found to delete');
}
} catch (error) {
if (error.status === 404) {
console.log('Release not found, nothing to delete');
} else {
console.error('Error:', error);
throw error;
}
}
update_tag:
name: Update tag
runs-on: ubuntu-latest
needs: delete_old_assets
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Fetch Alpha update logs
id: fetch_alpha_logs
run: |
# Check if UPDATELOG.md exists
if [ -f "UPDATELOG.md" ]; then
# Extract the section starting with ## and containing -alpha until the next ## or end of file
# ALPHA_LOGS=$(awk '/^## .*-alpha/{flag=1; print; next} /^## /{flag=0} flag' UPDATELOG.md)
ALPHA_LOGS=$(awk '/^## v/{if(flag) exit; flag=1} flag' UPDATELOG.md)
if [ -n "$ALPHA_LOGS" ]; then
echo "Found alpha update logs"
echo "ALPHA_LOGS<<EOF" >> $GITHUB_ENV
echo "$ALPHA_LOGS" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
else
echo "No alpha sections found in UPDATELOG.md"
fi
else
echo "UPDATELOG.md file not found"
fi
shell: bash
- name: Set Env
run: |
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
shell: bash
- run: |
# 检查 ALPHA_LOGS 是否存在,如果不存在则使用默认消息
if [ -z "$ALPHA_LOGS" ]; then
echo "No alpha logs found, using default message"
ALPHA_LOGS="More new features are now supported. Check for detailed changelog soon."
else
echo "Using found alpha logs"
fi
# 生成 release.txt 文件
cat > release.txt << EOF
$ALPHA_LOGS
## 我应该下载哪个版本?
### MacOS
- MacOS intel芯片: x64.dmg
- MacOS apple M芯片: aarch64.dmg
### Linux
- Linux 64位: amd64.deb/amd64.rpm
- Linux arm64 architecture: arm64.deb/aarch64.rpm
- Linux armv7架构: armhf.deb/armhfp.rpm
### Windows (不再支持Win7)
#### 正常版本(推荐)
- 64位: x64-setup.exe
- arm64架构: arm64-setup.exe
#### 便携版问题很多不再提供
#### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用)
- 64位: x64_fixed_webview2-setup.exe
- arm64架构: arm64_fixed_webview2-setup.exe
### FAQ
- [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
### 稳定机场VPN推荐
- [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6)
Created at ${{ env.BUILDTIME }}.
EOF
- name: Upload Release
uses: softprops/action-gh-release@v2
with:
tag_name: alpha
name: "Clash Verge Rev Alpha"
body_path: release.txt
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
generate_release_notes: true
alpha:
needs: update_tag
strategy:
fail-fast: false
matrix:
@@ -24,6 +205,8 @@ jobs:
target: aarch64-apple-darwin
- os: macos-latest
target: x86_64-apple-darwin
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.os }}
steps:
@@ -40,16 +223,23 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
cache-all-crates: true
cache-on-failure: true
- name: Install dependencies (ubuntu only)
if: matrix.os == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: "22"
- uses: pnpm/action-setup@v3
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9
run_install: false
- name: Pnpm install and check
@@ -57,12 +247,22 @@ jobs:
pnpm i
pnpm check ${{ matrix.target }}
- name: Release Alpha Version
run: pnpm release-alpha-version
- name: Tauri build
uses: tauri-apps/tauri-action@v0
env:
NODE_OPTIONS: "--max_old_space_size=4096"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
tagName: alpha
releaseName: "Clash Verge Rev Alpha"
@@ -72,37 +272,112 @@ jobs:
tauriScript: pnpm
args: --target ${{ matrix.target }}
- name: Portable Bundle
if: matrix.os == 'windows-latest'
run: pnpm portable ${{ matrix.target }} --alpha
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
alpha-for-linux:
alpha-for-linux-arm:
needs: update_tag
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
- os: ubuntu-22.04
target: aarch64-unknown-linux-gnu
- os: ubuntu-latest
arch: arm64
- os: ubuntu-22.04
target: armv7-unknown-linux-gnueabihf
arch: armhf
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Build for Linux
uses: ./.github/build-for-linux
env:
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
target: ${{ matrix.target }}
workspaces: src-tauri
cache-all-crates: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
run_install: false
- name: Pnpm install and check
run: |
pnpm i
pnpm check ${{ matrix.target }}
- name: Release Alpha Version
run: pnpm release-alpha-version
- name: "Setup for linux"
run: |-
sudo ls -lR /etc/apt/
cat > /tmp/sources.list << EOF
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy main multiverse universe restricted
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-security main multiverse universe restricted
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-updates main multiverse universe restricted
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-backports main multiverse universe restricted
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy main multiverse universe restricted
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main multiverse universe restricted
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main multiverse universe restricted
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main multiverse universe restricted
EOF
sudo mv /etc/apt/sources.list /etc/apt/sources.list.default
sudo mv /tmp/sources.list /etc/apt/sources.list
sudo dpkg --add-architecture ${{ matrix.arch }}
sudo apt update
sudo apt install -y \
libxslt1.1:${{ matrix.arch }} \
libwebkit2gtk-4.1-dev:${{ matrix.arch }} \
libayatana-appindicator3-dev:${{ matrix.arch }} \
libssl-dev:${{ matrix.arch }} \
patchelf:${{ matrix.arch }} \
librsvg2-dev:${{ matrix.arch }}
- name: "Install aarch64 tools"
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt install -y \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu
- name: "Install armv7 tools"
if: matrix.target == 'armv7-unknown-linux-gnueabihf'
run: |
sudo apt install -y \
gcc-arm-linux-gnueabihf \
g++-arm-linux-gnueabihf
- name: Build for Linux
run: |
export PKG_CONFIG_ALLOW_CROSS=1
if [ "${{ matrix.target }}" == "aarch64-unknown-linux-gnu" ]; then
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/:$PKG_CONFIG_PATH
export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/
elif [ "${{ matrix.target }}" == "armv7-unknown-linux-gnueabihf" ]; then
export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig/:$PKG_CONFIG_PATH
export PKG_CONFIG_SYSROOT_DIR=/usr/arm-linux-gnueabihf/
fi
pnpm build --target ${{ matrix.target }}
env:
NODE_OPTIONS: "--max_old_space_size=4096"
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: Get Version
run: |
@@ -111,95 +386,111 @@ jobs:
echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
- run: |
cat > release.txt << 'EOF'
### 我应该下载哪个版本?
- Windows x86_64架构: x64-setup.exe (不支持win7)
- Windows arm64架构: arm64-setup.exe
- MacOS intel芯片: x64.dmg
- MacOS apple M芯片: aarch64.dmg (提示文件损坏看下面FAQ)
- Linux x64架构: amd64.AppImage/amd64.deb
- Linux arm64架构: arm64.deb
- Linux armv7架构: armhf.deb
- Windows 便携板 x86_64架构: x64_portable.zip (不推荐使用,无法自动更新)
- Windows 便携板 arm64架构: arm64_portable.zip (不推荐使用,无法自动更新)
### FAQ
- [FAQ](https://clash-verge-rev.github.io/faq/install/)
Created at ${{ env.BUILDTIME }}.
EOF
- name: Upload Release
if: startsWith(matrix.target, 'x86_64')
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
tag_name: alpha
name: "Clash Verge Rev Alpha"
body_path: release.txt
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/${{ matrix.target }}/release/bundle/appimage/*.AppImage*
files: |
src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
src-tauri/target/${{ matrix.target }}/release/bundle/rpm/*.rpm
- name: Upload Release
uses: softprops/action-gh-release@v1
with:
tag_name: alpha
name: "Clash Verge Rev Alpha"
body_path: release.txt
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
update_tag:
name: Update tag
runs-on: ubuntu-latest
needs: [alpha, alpha-for-linux]
alpha-for-fixed-webview2:
needs: update_tag
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
target: x86_64-pc-windows-msvc
arch: x64
- os: windows-latest
target: aarch64-pc-windows-msvc
arch: arm64
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set Env
run: |
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
shell: bash
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
- name: Update Tag
uses: richardsimko/update-tag@v1
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
tag_name: alpha
workspaces: src-tauri
cache-all-crates: true
cache-on-failure: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "22"
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
- name: Pnpm install and check
run: |
pnpm i
pnpm check ${{ matrix.target }}
- name: Release Alpha Version
run: pnpm release-alpha-version
- name: Download WebView2 Runtime
run: |
invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/109.0.1518.78/Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab
Expand .\Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -F:* ./src-tauri
Remove-Item .\src-tauri\tauri.windows.conf.json
Rename-Item .\src-tauri\webview2.${{ matrix.arch }}.json tauri.windows.conf.json
- name: Tauri build
id: build
uses: tauri-apps/tauri-action@v0
env:
NODE_OPTIONS: "--max_old_space_size=4096"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tauriScript: pnpm
args: --target ${{ matrix.target }}
- run: |
cat > release.txt << 'EOF'
### 我应该下载哪个版本?
- name: Rename
run: |
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.exe$", "_fixed_webview2-setup.exe"
Rename-Item $file.FullName $newName
}
- Windows x86_64架构: x64-setup.exe (不支持win7)
- Windows arm64架构: arm64-setup.exe
- MacOS intel芯片: x64.dmg
- MacOS apple M芯片: aarch64.dmg (提示文件损坏看下面FAQ)
- Linux x64架构: amd64.AppImage/amd64.deb
- Linux arm64架构: arm64.deb
- Linux armv7架构: armhf.deb
- Windows 便携板 x86_64架构: x64_portable.zip (不推荐使用,无法自动更新)
- Windows 便携板 arm64架构: arm64_portable.zip (不推荐使用,无法自动更新)
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.nsis\.zip$", "_fixed_webview2-setup.nsis.zip"
Rename-Item $file.FullName $newName
}
### FAQ
- [FAQ](https://clash-verge-rev.github.io/faq/install/)
Created at ${{ env.BUILDTIME }}.
EOF
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.exe\.sig$", "_fixed_webview2-setup.exe.sig"
Rename-Item $file.FullName $newName
}
- name: Upload Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
tag_name: alpha
name: "Clash Verge Rev Alpha"
body_path: release.txt
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
generate_release_notes: true
files: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*setup*
- name: Portable Bundle
run: pnpm portable-fixed-webview2 ${{ matrix.target }} --alpha
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+94
View File
@@ -0,0 +1,94 @@
name: Development Test
on:
workflow_dispatch:
permissions: write-all
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
concurrency:
# only allow per workflow per commit (and not pr) to run at a time
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
dev:
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
target: x86_64-pc-windows-msvc
bundle: nsis
- os: macos-latest
target: aarch64-apple-darwin
bundle: dmg
- os: macos-latest
target: x86_64-apple-darwin
bundle: dmg
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
cache-all-crates: true
cache-on-failure: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
- name: Pnpm install and check
run: |
pnpm i
pnpm check ${{ matrix.target }}
- name: Tauri build
uses: tauri-apps/tauri-action@v0
env:
NODE_OPTIONS: "--max_old_space_size=4096"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
tauriScript: pnpm
args: --target ${{ matrix.target }} -b ${{ matrix.bundle }}
- name: Upload Artifacts
if: matrix.os == 'macos-latest'
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.target }}
path: src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg
if-no-files-found: error
- name: Upload Artifacts
if: matrix.os == 'windows-latest'
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.target }}
path: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*.exe
if-no-files-found: error
+261 -39
View File
@@ -6,6 +6,10 @@ permissions: write-all
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
concurrency:
# only allow per workflow per commit (and not pr) to run at a time
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
release:
@@ -21,6 +25,8 @@ jobs:
target: aarch64-apple-darwin
- os: macos-latest
target: x86_64-apple-darwin
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.os }}
steps:
@@ -37,16 +43,23 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
cache-all-crates: true
cache-on-failure: true
- name: Install dependencies (ubuntu only)
if: matrix.os == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: "22"
- uses: pnpm/action-setup@v3
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9
run_install: false
- name: Pnpm install and check
@@ -57,91 +70,253 @@ jobs:
- name: Tauri build
uses: tauri-apps/tauri-action@v0
env:
NODE_OPTIONS: "--max_old_space_size=4096"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
tagName: v__VERSION__
releaseName: "Clash Verge Rev v__VERSION__"
releaseBody: "More new features are now supported."
releaseDraft: false
prerelease: false
tauriScript: pnpm
args: --target ${{ matrix.target }}
- name: Portable Bundle
if: matrix.os == 'windows-latest'
run: pnpm portable ${{ matrix.target }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
release-for-linux:
release-for-linux-arm:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
- os: ubuntu-22.04
target: aarch64-unknown-linux-gnu
- os: ubuntu-latest
arch: arm64
- os: ubuntu-22.04
target: armv7-unknown-linux-gnueabihf
arch: armhf
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Build for Linux
uses: ./.github/build-for-linux
env:
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
target: ${{ matrix.target }}
workspaces: src-tauri
cache-all-crates: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
run_install: false
- name: Pnpm install and check
run: |
pnpm i
pnpm check ${{ matrix.target }}
- name: "Setup for linux"
run: |-
sudo ls -lR /etc/apt/
cat > /tmp/sources.list << EOF
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy main multiverse universe restricted
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-security main multiverse universe restricted
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-updates main multiverse universe restricted
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-backports main multiverse universe restricted
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy main multiverse universe restricted
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main multiverse universe restricted
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main multiverse universe restricted
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main multiverse universe restricted
EOF
sudo mv /etc/apt/sources.list /etc/apt/sources.list.default
sudo mv /tmp/sources.list /etc/apt/sources.list
sudo dpkg --add-architecture ${{ matrix.arch }}
sudo apt update
sudo apt install -y \
libxslt1.1:${{ matrix.arch }} \
libwebkit2gtk-4.1-dev:${{ matrix.arch }} \
libayatana-appindicator3-dev:${{ matrix.arch }} \
libssl-dev:${{ matrix.arch }} \
patchelf:${{ matrix.arch }} \
librsvg2-dev:${{ matrix.arch }}
- name: "Install aarch64 tools"
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt install -y \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu
- name: "Install armv7 tools"
if: matrix.target == 'armv7-unknown-linux-gnueabihf'
run: |
sudo apt install -y \
gcc-arm-linux-gnueabihf \
g++-arm-linux-gnueabihf
- name: Build for Linux
run: |
export PKG_CONFIG_ALLOW_CROSS=1
if [ "${{ matrix.target }}" == "aarch64-unknown-linux-gnu" ]; then
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/:$PKG_CONFIG_PATH
export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/
elif [ "${{ matrix.target }}" == "armv7-unknown-linux-gnueabihf" ]; then
export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig/:$PKG_CONFIG_PATH
export PKG_CONFIG_SYSROOT_DIR=/usr/arm-linux-gnueabihf/
fi
pnpm build --target ${{ matrix.target }}
env:
NODE_OPTIONS: "--max_old_space_size=4096"
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: Get Version
run: |
sudo apt-get update
sudo apt-get install jq
echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
- name: Upload Release
if: startsWith(matrix.target, 'x86_64')
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
tag_name: v${{env.VERSION}}
name: "Clash Verge Rev v${{env.VERSION}}"
body: "More new features are now supported."
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/${{ matrix.target }}/release/bundle/appimage/*.AppImage*
files: |
src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
src-tauri/target/${{ matrix.target }}/release/bundle/rpm/*.rpm
release-for-fixed-webview2:
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
target: x86_64-pc-windows-msvc
arch: x64
- os: windows-latest
target: aarch64-pc-windows-msvc
arch: arm64
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
cache-all-crates: true
cache-on-failure: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "22"
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
- name: Pnpm install and check
run: |
pnpm i
pnpm check ${{ matrix.target }}
- name: Download WebView2 Runtime
run: |
invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/109.0.1518.78/Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab
Expand .\Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -F:* ./src-tauri
Remove-Item .\src-tauri\tauri.windows.conf.json
Rename-Item .\src-tauri\webview2.${{ matrix.arch }}.json tauri.windows.conf.json
- name: Tauri build
id: build
uses: tauri-apps/tauri-action@v0
env:
NODE_OPTIONS: "--max_old_space_size=4096"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tauriScript: pnpm
args: --target ${{ matrix.target }}
- name: Rename
run: |
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.exe$", "_fixed_webview2-setup.exe"
Rename-Item $file.FullName $newName
}
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.nsis\.zip$", "_fixed_webview2-setup.nsis.zip"
Rename-Item $file.FullName $newName
}
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.exe\.sig$", "_fixed_webview2-setup.exe.sig"
Rename-Item $file.FullName $newName
}
- name: Upload Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
tag_name: v${{env.VERSION}}
name: "Clash Verge Rev v${{env.VERSION}}"
tag_name: v${{steps.build.outputs.appVersion}}
name: "Clash Verge Rev v${{steps.build.outputs.appVersion}}"
body: "More new features are now supported."
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
files: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*setup*
- name: Portable Bundle
run: pnpm portable-fixed-webview2 ${{ matrix.target }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-update:
runs-on: ubuntu-latest
needs: [release, release-for-linux]
needs: [release, release-for-linux-arm]
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: "22"
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 8
run_install: false
- name: Pnpm install
@@ -151,3 +326,50 @@ jobs:
run: pnpm updater
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-update-for-fixed-webview2:
runs-on: ubuntu-latest
needs: [release-for-fixed-webview2]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "22"
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
- name: Pnpm install
run: pnpm i
- name: Release updater file
run: pnpm updater-fixed-webview2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
submit-to-winget:
runs-on: ubuntu-latest
needs: [release-update]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get Version
run: |
sudo apt-get update
sudo apt-get install jq
echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
- name: Submit to Winget
uses: vedantmgoyal9/winget-releaser@main
with:
identifier: ClashVergeRev.ClashVergeRev
version: ${{env.VERSION}}
release-tag: v${{env.VERSION}}
installers-regex: '_(arm64|x64|x86)-setup\.exe$'
token: ${{ secrets.WINGET_TOKEN }}
+28 -5
View File
@@ -7,17 +7,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: "22"
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 8
run_install: false
- name: Pnpm install
@@ -27,3 +26,27 @@ jobs:
run: pnpm updater
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-update-for-fixed-webview2:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "22"
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
- name: Pnpm install
run: pnpm i
- name: Release updater file
run: pnpm updater-fixed-webview2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+2 -1
View File
@@ -7,4 +7,5 @@ dist-ssr
update.json
scripts/_env.sh
.vscode
.tool-versions
.tool-versions
.idea
+15 -3
View File
@@ -1,4 +1,16 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
#!/bin/bash
pnpm pretty-quick --staged
#pnpm pretty-quick --staged
# 运行 clippy fmt
#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
#git add .
# 允许提交
exit 0
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
# 运行 clippy
#cargo clippy --manifest-path ./src-tauri/Cargo.toml --fix
# 如果 clippy 失败,阻止 push
#if [ $? -ne 0 ]; then
# echo "Clippy found issues in sub_crate. Please fix them before pushing."
# exit 1
#fi
# 允许 push
exit 0
+46 -7
View File
@@ -17,15 +17,28 @@ If you're a Windows user, you may need to perform some additional steps:
- Make sure to add Rust and Node.js to your system's PATH. This is usually done during the installation process, but you can verify and manually add them if necessary.
- The gnu `patch` tool should be installed
### Install Node.js Packages
After installing Rust and Node.js, install the necessary Node.js packages:
When you setup `Rust` environment, Only use toolchain with `Windows MSVC` , to change settings follow command:
```shell
pnpm i
rustup target add x86_64-pc-windows-msvc
rustup set default-host x86_64-pc-windows-msvc
```
### Download the Clash Binary
### Install Node.js Package
After installing Rust and Node.js, install the necessary Node.js and Node Package Manager:
```shell
npm install pnpm -g
```
### Install Dependencies
```shell
pnpm install
```
### Download the Mihomo Core Binary
You have two options for downloading the clash binary:
@@ -35,7 +48,7 @@ You have two options for downloading the clash binary:
# Use '--force' to force update to the latest version
# pnpm run check --force
```
- Manually download it from the [Clash Meta release](https://github.com/MetaCubeX/Clash.Meta/releases). After downloading, rename the binary according to the [Tauri configuration](https://tauri.app/v1/api/config#bundleconfig.externalbin).
- Manually download it from the [Mihomo release](https://github.com/MetaCubeX/mihomo/releases). After downloading, rename the binary according to the [Tauri configuration](https://tauri.app/v1/api/config#bundleconfig.externalbin).
### Run the Development Server
@@ -49,12 +62,38 @@ pnpm dev:diff
### Build the Project
If you want to build the project, use:
To build this project:
```shell
pnpm build
```
For a faster build, use the following command
```shell
pnpm build:fast
```
This uses Rust's fast-release profile which significantly reduces compilation time by disabling optimization and LTO. The resulting binary will be larger and less performant than the standard build, but it's useful for testing changes quickly.
The `Artifacts` will display in the `log` in the Terminal.
### Build clean
To clean rust build:
```shell
pnpm clean
```
### Portable Version (Windows Only)
To package portable version after the build:
```shell
pnpm portable
```
## Contributing Your Changes
Once you have made your changes:
+20 -12
View File
@@ -1,5 +1,5 @@
<h1 align="center">
<img src="./src/assets/image/logo.png" alt="Clash" width="128" />
<img src="./src-tauri/icons/icon.png" alt="Clash" width="128" />
<br>
Continuation of <a href="https://github.com/zzzgydi/clash-verge">Clash Verge</a>
<br>
@@ -11,7 +11,9 @@ A Clash Meta GUI based on <a href="https://github.com/tauri-apps/tauri">Tauri</a
## Preview
![preview](./docs/preview.png)
| Dark | Light |
| -------------------------------- | --------------------------------- |
| ![预览](./docs/preview_dark.png) | ![预览](./docs/preview_light.png) |
## Install
@@ -23,33 +25,39 @@ Supports Windows (x64/x86), Linux (x64/arm64) and macOS 10.15+ (intel/apple).
---
### TG Group: [@clash_verge_rev](https://t.me/clash_verge_rev)
### TG 频道: [@clash_verge_rev](https://t.me/clash_verge_re)
## Promotion
[狗狗加速 —— 技术流机场 Doggygo VPN](https://狗狗加速.com)
[狗狗加速 —— 技术流机场 Doggygo VPN](https://verge.dginv.click/#/register?code=oaxsAGo6)
- 高性能海外机场,免费试用,优惠套餐,解锁流媒体,全球首家支持 Hysteria 协议。
- 使用 Clash Verge 专属邀请链接注册送 3 天,每天 1G 流量免费试用:https://verge.狗狗加速.com/#/register?code=oaxsAGo6
- 使用 Clash Verge 专属邀请链接注册送 3 天,每天 1G 流量免费试用:[点此注册](https://verge.dginv.click/#/register?code=oaxsAGo6)
- Clash Verge 专属 8 折优惠码: verge20 (仅有 500 份)
- 优惠套餐每月仅需 15.8 元,160G 流量,年付 8 折
- 海外团队,无跑路风险,高达 50% 返佣
- 集群负载均衡设计,高速专线(兼容老客户端),极低延迟,无视晚高峰,4K 秒开
- 全球首家 Hysteria 协议机场,现已上线更快的 `Hysteria2` 协议(Clash Verge 客户端最佳搭配)
- 解锁流媒体及 ChatGPT
- 官网:https://狗狗加速.com
- 官网:[https://狗狗加速.com](https://verge.dginv.click/#/register?code=oaxsAGo6)
## Features
- Since the clash core has been removed. The project no longer maintains the clash core, but only the Clash Meta core.
- Profiles management and enhancement (by yaml and Javascript). [Doc](https://clash-verge-rev.github.io)
- Improved UI and supports custom theme color.
- Built-in support [Clash.Meta(mihomo)](https://github.com/MetaCubeX/mihomo) core.
- System proxy setting and guard.
- 基于性能强劲的 Rust 和 Tauri 2 框架
- 内置[Clash.Meta(mihomo)](https://github.com/MetaCubeX/mihomo)内核,并支持切换 `Alpha` 版本内核。
- 简洁美观的用户界面,支持自定义主题颜色、代理组/托盘图标以及 `CSS Injection`。
- 配置文件管理和增强(Merge 和 Script),配置文件语法提示。
- 系统代理和守卫、`TUN(虚拟网卡)` 模式。
- 可视化节点和规则编辑
- WebDav 配置备份和同步
### FAQ
Refer to [Doc FAQ Page](https://clash-verge-rev.github.io/faq/install/)
Refer to [Doc FAQ Page](https://clash-verge-rev.github.io/faq/windows.html)
### Donation
[捐助Clash Verge Rev的开发](https://github.com/sponsors/clash-verge-rev)
## Development
+640
View File
@@ -1,3 +1,643 @@
## v2.2.2
**发行代号:拓**
感谢 Tunglies 对 Verge 后端重构,性能优化做出的重大贡献!
代号释义: 本次发布在功能上的大幅扩展。新首页设计为用户带来全新交互体验,DNS 覆写功能增强网络控制能力,解锁测试页面助力内容访问自由度提升,轻量模式提供灵活使用选择。此外,macOS 应用菜单集成、sidecar 模式、诊断信息导出等新特性进一步丰富了软件的适用场景。这些新增功能显著拓宽了 Clash Verge 的功能边界,为用户提供了更强大的工具和可能性。
#### 已知问题
- 仅在Ubuntu 22.04/24.04,Fedora 41 **Gnome桌面环境** 做过简单测试,不保证其他其他Linux发行版可用,将在未来做进一步适配和调优
### 2.2.2 相对于 2.2.1(已下架不在提供)
#### 修复了:
- 弹黑框的问题(原因是服务崩溃触发重装机制)
- MacOS进入轻量模式以后影藏Dock图标
- 增加轻量模式缺失的tray翻译
- Linux下的窗口边框被削掉的问题
#### 新增了:
- 加强服务检测和重装逻辑
- 增强内核与服务保活机制
- 增加服务模式下的僵尸进程清理机制
- 新增当服务模式多次尝试失败后自动回退至用户空间模式
### 2.2.1 相对于 2.2.0(已下架不在提供)
#### 修复了:
1. **首页**
- 修复 Direct 模式首页无法渲染
- 修复 首页启用轻量模式导致 ClashVergeRev 从托盘退出
- 修复 系统代理标识判断不准的问题
- 修复 系统代理地址错误的问题
- 代理模式“多余的切换动画”
2. **系统**
- 修复 MacOS 无法使用快捷键粘贴/选择/复制订阅地址。
- 修复 代理端口设置同步问题。
- 修复 Linux 无法与 Mihomo 核心 和 ClashVergeRev 服务通信
3. **界面**
- 修复 连接详情卡没有跟随主题色
4. **轻量模式**
- 修复 MacOS 轻量模式下 Dock 栏图标无法隐藏。
#### 新增了:
1. **首页**
- 首页文本过长自动截断
2. **轻量模式**
- 新增托盘进入轻量模式支持
- 新增进入轻量模式快捷键支持
3. **系统**
- 在 ClashVergeRev 对 Mihomo 进行操作时,总是尝试确保两者运行
- 服务器模式下启动mihomo内核的时候查找并停止其他已经存在的内核进程,防止内核假死等问题带来的通信失败
4. **托盘**
- 新增 MacOS 启用托盘速率显示时,可选隐藏托盘图标显示
---
## 2.2.0(已下架不在提供)
#### 新增功能
1. **首页**
- 新增首页功能,默认启动页面改为首页。
- 首页流量图卡片显示上传/下载名称。
- 首页支持轻量模式切换。
- 流量统计数据持久保存。
- 限制首页配置文件卡片URL长度。
2. **DNS 设置与覆写**
- 新增 DNS 覆写功能。
- 默认启用 DNS 覆写。
3. **解锁测试**
- 新增解锁测试页面。
4. **轻量模式**
- 新增轻量模式及设置。
- 添加自动轻量模式定时器。
5. **系统支持**
- Mihomo(meta)内核升级 1.19.3
- macOS 支持 CMD+W 关闭窗口。
- 新增 macOS 应用菜单。
- 添加 macOS 安装服务时候的管理员权限提示。
- 新增 sidecar(用户空间启动内核) 模式。
6. **其他**
- 增强延迟测试日志和错误处理。
- 添加诊断信息导出。
- 新增代理命令。
#### 修复
1. **系统**
- 修复 Windows 热键崩溃。
- 修复 macOS 无框标题。
- 修复 macOS 静默启动崩溃。
- 修复 macOS tray图标错位到左上角的问题。
- 修复 Windows/Linux 运行时崩溃。
- 修复 Win10 阴影和边框问题。
- 修复 升级或重装后开机自启状态检测和同步问题。
2. **构建**
- 修复构建失败问题。
#### 优化
1. **性能**
- 重构后端,巨幅性能优化。
- 优化首页组件性能。
- 优化流量图表资源使用。
- 提升代理组列表滚动性能。
- 加快应用退出速度。
- 加快进入轻量模式速度。
- 优化小数值速度更新。
- 增加请求超时至 60 秒。
- 修复代理节点选择同步。
- 优化修改verge配置性能。
2. **重构**
- 重构后端,巨幅性能优化。
- 优化定时器管理。
- 重构 MihomoManager 处理流量。
- 优化 WebSocket 连接。
3. **其他**
- 更新依赖。
- 默认 TUN 堆栈改为 gvisor。
---
## v2.1.2
**发行代号:臻**
代号释义: 千锤百炼臻至善,集性能跃升、功能拓展、交互焕新于一体,彰显持续打磨、全方位优化的迭代精神。
感谢 Tychristine 对社区群组管理做出的重大贡献!
##### 2.1.2相对2.1.1(已下架不在提供)更新了:
- 无法更新和签名验证失败的问题(该死的CDN缓存)
- 设置菜单区分Verge基本设置和高级设置
- 增加v2 Updater的更多功能和权限
- 退出Verge后Tun代理状态仍保留的问题
##### 2.1.1相对2.1.0(已下架不在提供)更新了:
- 检测所需的Clash Verge Service版本(杀毒软件误报可能与此有关,因为检测和安装新版本Service需管理员权限)
- MacOS下支持彩色托盘图标和更好速率显示(感谢Tunglies)
- 文件类型判断不准导致脚本检测报错的问题
- 打开Win下的阴影(Win10因底层兼容性问题,可能圆角和边框显示不太完美)
- 边框去白边
- 修复Linux下编译问题
- 修复热键无法关闭面板的问题
##### 2.1.0 - 发行代号:臻
### 功能新增
- 新增窗口状态实时监控与自动保存功能
- 增强核心配置变更时的验证与错误处理机制
- 支持通过环境变量`CLASH_VERGE_REV_IP`自定义复制IP地址
- 添加连接表列宽持久化设置与进程过滤功能
- 新增代理组首字母导航与动态滚动定位功能
- 实现连接追踪暂停/恢复功能
- 支持从托盘菜单快速切换代理配置
- 添加轻量级模式开关选项
- 允许用户自定义TUN模式增强类型和FakeIP范围
- 新增系统代理状态指示器
- 增加Alpha版本自动重命名逻辑
- 优化字母导航工具提示与防抖交互机制
### 性能优化
- 重构代理列表渲染逻辑,提升布局计算效率
- 优化代理数据更新机制,采用乐观UI策略
- 改进虚拟列表渲染性能(Virtuoso)
- 提升主窗口Clash模式切换速度(感谢Tunglies)
- 加速内核关闭流程并优化管理逻辑
- 优化节点延迟刷新速率
- 改进托盘网速显示更新逻辑
- 提升配置验证错误信息的可读性
- 重构服务架构,优化代码组织结构(感谢Tunglies)
- 优化内核启动时的配置验证流程
### 问题修复
- 修复删除节点时关联组信息残留问题
- 解决菜单切换异常与重复勾选问题
- 修正连接页流量计算错误
- 修复Windows圆角显示异常问题
- 解决控制台废弃API警告
- 修复全局热键空值导致的崩溃
- 修复Alpha版本Windows打包重命名问题
- 修复MacOS端口切换崩溃问题
- 解决Linux持续集成更新器问题
- 修复静默启动后热键失效问题
- 修正TypeScript代理组类型定义
- 修复Windows托盘图标空白问题
- 优化远程目标地址显示(替换旧版IP展示)
### 交互体验
- 统一多平台托盘图标点击行为
- 优化代理列表滚动流畅度
- 改进日志搜索功能与数据管理
- 重构热键管理逻辑,修复托盘冻结问题
- 优化托盘网速显示样式
- 增强字母导航工具提示的动态响应
### 国际化
- 新增配置检查多语言支持
- 添加轻量级模式多语言文本
- 完善多语言翻译内容
### 维护更新
- 将默认TUN协议栈改为gVisor
- 更新Node.js运行版本
- 移除自动生成更新器文件
- 清理废弃代码与未使用组件
- 禁用工作流自动Alpha标签更新
- 更新依赖库版本
- 添加MacOS格式转换函数专项测试
- 优化开发模式日志输出
### 安全增强
- 强化应用启动时的配置验证机制
- 改进脚本验证与异常处理流程
- 修复编译警告(移除无用导入)
---
## v2.0.3
### Notice
- !!使用出现异常的,打开设置-->配置目录 备份 后 删除所有文件 尝试是否正常!!
- 历时3个月的紧密开发与严格测试稳定版2.0.0终于发布了:巨量改进与性能、稳定性提升,目前Clash Verge Rev已经有了比肩cfw的健壮性;而且更强大易用!
- 由于更改了服务安装逻辑,每次更新安装需要输入系统密码卸载老版本服务和安装新版本服务,以后可以丝滑使用tun(虚拟网卡)模式
### 2.0.3相对于2.0.2改进修复了:
1. 修复VLess-URL识别网络类型错误 f400f90 #2126
2. 新增系统代理绕过文本校验 c71e18e
3. 修复脚本编辑器UI显示不正确 6197249 #2267
4. 修复Shift热键无效 589324b #2278
5. 新增nushell环境变量复制 d233a84
6. 修复全局扩展脚本无法覆写DNS d22b37c #2235
7. 切换到系统代理相对于稳定的版本 38745d4
8. 修改fake-ip-range网段 0e3b631
9. 修复窗口隐藏后WebSocket未断开连接,减小内存风险 b42d13f
10. 改进系统代理绕过设置 c5c840d
11. 修复i18n翻译文本缺失 b149084
12. 修复双击托盘图标打开面板 f839d3b #2346
13. 修复Windows10窗口白色边框 4f6ca40 #2425
14. 修复Windows窗口状态恢复 4f6ca40
15. 改进保存配置文件自动重启Mihomo内核 0669f7a
16. 改进更新托盘图标性能 d9291d4
17. 修复保存配置后代理列表未更新 542baf9 #2460
18. 新增MacOS托盘显示实时速率,可在"界面设置"中关闭 1b2f1b6
19. 新增托盘菜单显示已设置的快捷键 eeff4d4
20. 新增重载配置文件错误响应"400"时显示更多错误信息 c5989d2 #2492
21. 修复GUI代理状态与菜单显示不一致 13b63b5 #2502
22. 新增默认语言跟随系统语言(无语言支持即为英语),添加了阿拉伯语、印尼语、鞑靼语支持 9655f77 #2940
### Features
- Meta(mihomo)内核升级 1.19.1
- 增加更多语言和托盘语言跟随
- MacOS增加状态栏速率显示
- 托盘显示快捷键
- 重载配置文件错误响应"400"时显示更多错误信息
- 改进保存配置文件自动重启Mihomo内核
### Performance
- 改进更新托盘图标性能
- 窗口隐藏后WebSocket断开连接
---
## v2.0.2
### Notice
- !!使用出现异常的,打开设置-->配置目录 备份 后 删除所有文件 尝试是否正常!!
- 历时3个月的紧密开发与严格测试稳定版2.0.0终于发布了:巨量改进与性能、稳定性提升,目前Clash Verge Rev已经有了比肩cfw的健壮性;而且更强大易用!
- 由于更改了服务安装逻辑,Mac/Linux 首次安装需要输入系统密码卸载和安装服务,以后可以丝滑使用 tun(虚拟网卡)模式
- 因 Tauri 2.0 底层 bug,关闭窗口后保留webview进程,优点是再次打开面板更快,缺点是内存使用略有增加
### 2.0.2相对于2.0.1改进了:
- MacOS 下自定义图标可以支持彩色、单色切换
- 修正了 Linux 下多个内核僵尸进程的问题
- 修正了 DNS ipv6 强制覆盖的逻辑
- 修改了 MacOS tun 模式下覆盖设置 dns 字段的问题
- 修正了 MacOS tray 图标不会随代理模式更改的问题
- 静默启动下重复运行会出现多个实例的bug
- 安装的时候自动删除历史残留启动项
- Tun模式默认是还用内核推荐的 mixed 堆栈
- 改进了默认窗口大小(启动软件窗口不会那么小了)
- 改进了 WebDAV 备份超时时间机制
- 测试菜单添加滚动条
- 改进和修正了 Tun 模式下对设置的覆盖逻辑
- 修复了打开配置出错的问题
- 修复了配置文件无法拖拽添加的问题
- 改善了浅色模式的对比度
### 2.0.1相对于2.0.0改进了:
- 无法从 2.0rc和2.0.0 升级的问题(已经安装了2.0版本的需手动下载安装)
- MacOS 系统下少有的无法安装服务,无法启动的问题,目前更健壮了
- 当系统中没有 yaml 编辑器的情况下,打开文件程序崩溃的问题
- Windows 应用内升级和覆盖安装不会删除老执行文件的问题
- 修改优化了 mac 下 fakeip 段和 dns
- 测试菜单 svg 图标格式检查
- 应用内升级重复安装 vs runtime 的问题
- 修复外部控制下密码有特殊字符认证出错的问题
- 修复恢复 Webdav 备份设置后, Webdav 设置丢失的问题
- 代理页面增加快速回到顶部的按钮
### Breaking changes
- 重大框架升级:使用 Tauri 2.0(巨量改进与性能提升)
- 出现 bug 到 issues 中提出;以后不再接受1.x版本的bug反馈。
- 强烈建议完全删除 1.x 老版本再安装此版本 !!使用出现异常的,打开设置-->配置目录 备份 后 删除所有文件 尝试是否正常!!
### Features
- Meta(mihomo)内核升级 1.18.10
- Win 下的系统代理替换为 Shadowsocks/CFW/v2rayN 等成熟的 sysproxy.exe 方案,解决拨号/VPN 环境下无法设置系统代理的问题
- 服务模式改进为启动软件时自动安装,TUN 模式可自由开启不再限制于服务模式
- Mac 下可用 URL Scheme 导入订阅
- 可使用 Ctrl(cmd)+Q 快捷键退出程序
- 成功导入订阅的提示消息
- 能自动选中新导入的订阅
- 日志加入颜色区分
- 改进多处文本表述
- 加入图标 svg 格式检测
- 增加更多 app 调试日志
- 添加 MacOS 下白色桌面的 tray 黑色配色(但会代理系统代理、tun 模式图标失效的问题)
- 增加 Webdav 备份功能
- 添加统一延迟的设置开关
- 添加 Windows 下自动检测并下载 vc runtime 的功能
- 支持显示 mux 和 mptcp 的节点标识
- 延迟测试连接更换 http 的 cp.cloudflare.com/generate_204 (关闭统一延迟的情况下延迟测试结果会有所增加)
- 重构日志记录逻辑,可以收集和筛选所有日志类型了(之前无法记录debug的日志类型)
### Performance
- 优化及重构内核启动管理逻辑
- 优化 TUN 启动逻辑
- 重构和优化 app_handle
- 重构系统代理绕过逻辑
- 移除无用的 PID 创建逻辑
- 优化系统 DNS 设置逻辑
- 后端实现窗口控制
- 重构 MacOS 下的 DNS 设置逻辑
### Bugs Fixes
- 修复已有多个订阅导入新订阅会跳选订阅的问题
- 修复多个 Linux 下的 bug, Tun 模式在 Linux 下目前工作正常
- 修复 Linux wayland 下任务栏图标缺失的问题
- 修复 Linux KDE 桌面环境无法启动的问题
- 移除多余退出变量和钩子
- 修复 MacOS 下 tray 菜单重启 app 失效的问题
- 修复某些特定配置文件载入失败的问题
- 修复 MacOS 下 tun 模式 fakeip 不生效的问题
- 修复 Linux 下 关闭 tun 模式文件报错的问题
- 修复快捷键设置的相关 bug
- 修复 Win 下点左键菜单闪现的问题(Mac 下的操作逻辑相反,默认情况下不管点左/右键均会打开菜单,闪现不属于 bug)
### Known issues
- Windows 下窗口大小无法记忆(等待上游修复)
- Webdav 备份因为安全性和兼容性问题,暂不支持跨平台配置同步
---
## v1.7.7
### Bugs Fixes
- 修复导入订阅没有自动重载(不显示节点)的问题
- 英语状态下修复 Windows 工具栏提示文本超过限制的问题
---
## v1.7.6
### Notice
- Clash Verge Rev 目前已进入稳定周期,日后更新将着重于 bug 修复与内核常规升级
### Features
- Meta(mihomo)内核升级 1.18.7
- 界面细节调整
- 优化服务模式安装逻辑
- 移除无用的 console log
- 能自动选择第一个订阅
### Bugs Fixes
- 修复服务模式安装问题
- 修复 Mac 下的代理绕过 CIDR 写法过滤
- 修复 32 位升级 URL
- 修复不同分组 URL 测试地址配置无效的问题
- 修复 Web UI 下的一处 hostname 参数
---
## v1.7.5
### Features
- 展示局域网 IP 地址信息
- 在设置页面直接复制环境变量
- 优化服务模式安装逻辑
### Performance
- 优化切换订阅速度
- 优化更改端口速度
### Bugs Fixes
- 调整 MacOS 托盘图标大小
- Trojan URI 解析错误
- 卡片拖动显示层级错误
- 代理绕过格式检查错误
- MacOS 下编辑器最大化失败
- MacOS 服务安装失败
- 更改窗口大小导致闪退的问题
---
## v1.7.3
### Features
- 支持可视化编辑订阅代理组
- 支持可视化编辑订阅节点
- 支持可视化编辑订阅规则
- 扩展脚本支持订阅名称参数 `function main(config, profileName)`
### Bugs Fixes
- 代理绕过格式检查错误
---
## v1.7.2
### Break Changes
- 更新后请务必重新导入所有订阅,包括 Remote 和 Local
- 此版本重构了 Merge/Script,更新前请先备份好自定义 Merge 和 Script(更新并不会删除配置文件,但是旧版 Merge 和 Script 在更新后无法从前端访问,备份以防万一)
- Merge 改名为 `扩展配置`,分为 `全局扩展配置` 和 `订阅扩展配置`,全局扩展配置对所有订阅生效,订阅扩展配置只对关联的订阅生效
- Script 改名为 `扩展脚本`,同样分为 `全局扩展脚本` 和 `订阅扩展脚本`
- 订阅扩展配置在订阅右键菜单里进入
- 执行优先级为: 全局扩展配置 -> 全局扩展脚本 -> 订阅扩展配置 ->订阅扩展脚本
- 扩展配置删除了 `prepend/append` 能力,请使用 右键订阅 -> `编辑规则`/`编辑节点`/`编辑代理组` 来代替
- MacOS 用户更新后请重新安装服务模式
### Features
- 升级内核到 1.18.6
- 移除内核授权,改为服务模式实现
- 自动填充本地订阅名称
- 添加重大更新处理逻辑
- 订阅单独指定扩展配置/脚本(需要重新导入订阅)
- 添加可视化规则编辑器(需要重新导入订阅)
- 编辑器新增工具栏按钮(格式化、最大化/最小化)
- WEBUI 使用最新版 metacubex,并解决无法自动登陆问问题
- 禁用部分 Webview2 快捷键
- 热键配置新增连接符 + 号
- 新增部分悬浮提示按钮,用于解释说明
- 当日志等级为`Debug`时(更改需重启软件生效),支持点击内存主动内存回收(绿色文字)
- 设置页面右上角新增 TG 频道链接
- 各种细节优化和界面性能优化
### Bugs Fixes
- 修复代理绕过格式检查
- 通过进程名称关闭进程
- 退出软件时恢复 DNS 设置
- 修复创建本地订阅时更新间隔无法保存
- 连接页面列宽无法调整
---
## v1.7.1
### Break Changes
- 更新后请务必重新导入所有订阅,包括 Remote 和 Local
- 此版本重构了 Merge/Script,更新前请先备份好自定义 Merge 和 Script(更新并不会删除配置文件,但是旧版 Merge 和 Script 在更新后无法从前端访问,备份以防万一)
- Merge 改名为 `扩展配置`,分为 `全局扩展配置` 和 `订阅扩展配置`,全局扩展配置对所有订阅生效,订阅扩展配置只对关联的订阅生效
- Script 改名为 `扩展脚本`,同样分为 `全局扩展脚本` 和 `订阅扩展脚本`
- 订阅扩展配置在订阅右键菜单里进入
- 执行优先级为: 全局扩展配置 -> 全局扩展脚本 -> 订阅扩展配置 ->订阅扩展脚本
- 扩展配置删除了 `prepend/append` 能力,请使用 右键订阅 -> `编辑规则`/`编辑节点`/`编辑代理组` 来代替
- MacOS 用户更新后请重新安装服务模式
### Features
- 升级内核到 1.18.6
- 移除内核授权,改为服务模式实现
- 自动填充本地订阅名称
- 添加重大更新处理逻辑
- 订阅单独指定扩展配置/脚本(需要重新导入订阅)
- 添加可视化规则编辑器(需要重新导入订阅)
- 编辑器新增工具栏按钮(格式化、最大化/最小化)
- WEBUI 使用最新版 metacubex,并解决无法自动登陆问问题
- 禁用部分 Webview2 快捷键
- 热键配置新增连接符 + 号
- 新增部分悬浮提示按钮,用于解释说明
- 当日志等级为`Debug`时(更改需重启软件生效),支持点击内存主动内存回收(绿色文字)
- 设置页面右上角新增 TG 频道链接
- 各种细节优化和界面性能优化
### Bugs Fixes
- 修复代理绕过格式检查
- 通过进程名称关闭进程
- 退出软件时恢复 DNS 设置
- 修复创建本地订阅时更新间隔无法保存
- 连接页面列宽无法调整
---
## v1.7.0
### Break Changes
- 此版本重构了 Merge/Script,更新前请先备份好自定义 Merge 和 Script(更新并不会删除配置文件,但是旧版 Merge 和 Script 在更新后无法从前端访问,备份以防万一)
- Merge 改名为 `扩展配置`,分为 `全局扩展配置` 和 `订阅扩展配置`,全局扩展配置对所有订阅生效,订阅扩展配置只对关联的订阅生效
- Script 改名为 `扩展脚本`,同样分为 `全局扩展脚本` 和 `订阅扩展脚本`
- 执行优先级为: 全局扩展配置 -> 全局扩展脚本 -> 订阅扩展配置 ->订阅扩展脚本
- MacOS 用户更新后请重新安装服务模式
### Features
- 移除内核授权,改为服务模式实现
- 自动填充本地订阅名称
- 添加重大更新处理逻辑
- 订阅单独指定扩展配置/脚本(需要重新导入订阅)
- 添加可视化规则编辑器(需要重新导入订阅)
- 编辑器新增工具栏按钮(格式化、最大化/最小化)
- WEBUI 使用最新版 metacubex,并解决无法自动登陆问问题
- 禁用部分 Webview2 快捷键
- 热键配置新增连接符 + 号
- 新增部分悬浮提示按钮,用于解释说明
- 当日志等级为`Debug`时(更改需重启软件生效),支持点击内存主动内存回收(绿色文字)
- 设置页面右上角新增 TG 频道链接
### Bugs Fixes
- 修复代理绕过格式检查
- 通过进程名称关闭进程
- 退出软件时恢复 DNS 设置
- 修复创建本地订阅时更新间隔无法保存
- 连接页面列宽无法调整
---
## v1.6.6
### Features
- MacOS 应用签名
- 删除 AppImage
- 应用更新对话框添加下载按钮
- 设置系统代理绕过时保留默认值
- 系统代理绕过设置输入格式检查
### Bugs Fixes
- MacOS 代理组图标无法显示
- RPM 包依赖缺失
---
## v1.6.5
### Features
- 添加 RPM 包支持
- 优化细节
### Bugs Fixes
- MacOS 10.15 编辑器空白的问题
- MacOS 低版本启动白屏的问题
---
## v1.6.4
### Features
- 系统代理支持 PAC 模式
- 允许关闭不使用的端口
- 使用新的应用图标
- MacOS 支持切换托盘图标单色/彩色模式
- CSS 注入支持通过编辑器编辑
- 优化代理组列表性能
- 优化流量图显性能
- 支持波斯语
### Bugs Fixes
- Kill 内核后 Tun 开启缓慢的问题
- 代理绕过为空时使用默认值
- 无法读取剪切板内容
- Windows 下覆盖安装无法内核占用问题
---
## v1.6.2
### Features
- 支持本地文件拖拽导入
- 重新支持 32 位 CPU
- 新增内置 Webview2 版本
- 优化 Merge 逻辑,支持深度合并
- 删除 Merge 配置中的 append/prepend-provider 字段
- 支持更新稳定版内核
### Bugs Fixes
- MacOS DNS 还原失败
- CMD 环境变量格式错误
- Linux 下与 N 卡的兼容性问题
- 修改 Tun 设置不立即生效
---
## v1.6.1
### Features
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

+78 -49
View File
@@ -1,83 +1,112 @@
{
"name": "clash-verge",
"version": "1.6.1",
"version": "2.2.2",
"license": "GPL-3.0-only",
"scripts": {
"dev": "tauri dev",
"dev:diff": "tauri dev -f verge-dev",
"build": "tauri build",
"dev": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev -- --profile fast-dev",
"dev:diff": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev -- --profile fast-dev",
"build": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tauri build",
"build:fast": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tauri build -- --profile fast-release",
"tauri": "tauri",
"web:dev": "vite",
"web:build": "tsc && vite build",
"web:build": "tsc --noEmit && vite build",
"web:serve": "vite preview",
"check": "node scripts/check.mjs",
"updater": "node scripts/updater.mjs",
"updater-fixed-webview2": "node scripts/updater-fixed-webview2.mjs",
"portable": "node scripts/portable.mjs",
"prepare": "husky install"
"portable-fixed-webview2": "node scripts/portable-fixed-webview2.mjs",
"fix-alpha-version": "node scripts/fix-alpha_version.mjs",
"release-alpha-version": "node scripts/release-alpha_version.mjs",
"prepare": "husky",
"clean": "cd ./src-tauri && cargo clean && cd -"
},
"dependencies": {
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@juggle/resize-observer": "^3.4.0",
"@mui/icons-material": "^5.15.15",
"@mui/lab": "5.0.0-alpha.149",
"@mui/material": "^5.15.15",
"@mui/x-data-grid": "^6.19.11",
"@tauri-apps/api": "^1.5.4",
"@mui/icons-material": "^6.4.8",
"@mui/lab": "6.0.0-beta.25",
"@mui/material": "^6.4.8",
"@mui/x-data-grid": "^7.28.0",
"@tauri-apps/api": "2.2.0",
"@tauri-apps/plugin-clipboard-manager": "^2.2.2",
"@tauri-apps/plugin-dialog": "^2.2.0",
"@tauri-apps/plugin-fs": "^2.2.0",
"@tauri-apps/plugin-global-shortcut": "^2.2.0",
"@tauri-apps/plugin-notification": "^2.2.2",
"@tauri-apps/plugin-process": "^2.2.0",
"@tauri-apps/plugin-shell": "2.2.0",
"@tauri-apps/plugin-updater": "2.3.0",
"@types/d3-shape": "^3.1.7",
"@types/json-schema": "^7.0.15",
"ahooks": "^3.7.11",
"axios": "^1.6.8",
"dayjs": "1.11.5",
"i18next": "^23.11.2",
"ahooks": "^3.8.4",
"axios": "^1.8.3",
"cli-color": "^2.0.4",
"d3-shape": "^3.2.0",
"dayjs": "1.11.13",
"foxact": "^0.2.44",
"glob": "^11.0.1",
"i18next": "^24.2.3",
"js-base64": "^3.7.7",
"js-yaml": "^4.1.0",
"lodash-es": "^4.17.21",
"meta-json-schema": "1.18.4-beta2",
"monaco-editor": "^0.47.0",
"monaco-yaml": "^5.1.1",
"nanoid": "^5.0.7",
"monaco-editor": "^0.52.2",
"monaco-yaml": "^5.3.1",
"nanoid": "^5.1.5",
"peggy": "^4.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-error-boundary": "^3.1.4",
"react-hook-form": "^7.51.3",
"react-error-boundary": "^4.1.2",
"react-hook-form": "^7.54.2",
"react-i18next": "^13.5.0",
"react-markdown": "^9.0.1",
"react-router-dom": "^6.23.0",
"react-markdown": "^9.1.0",
"react-monaco-editor": "^0.56.2",
"react-router-dom": "^6.30.0",
"react-transition-group": "^4.4.5",
"react-virtuoso": "^4.7.10",
"recoil": "^0.7.7",
"swr": "^1.3.0",
"tar": "^6.2.1"
"react-virtuoso": "^4.12.5",
"recharts": "^2.15.1",
"sockette": "^2.0.6",
"swr": "^2.3.3",
"tar": "^7.4.3",
"types-pac": "^1.0.3",
"zustand": "^5.0.3"
},
"devDependencies": {
"@actions/github": "^5.1.1",
"@tauri-apps/cli": "^1.5.12",
"@types/fs-extra": "^9.0.13",
"@actions/github": "^6.0.0",
"@tauri-apps/cli": "2.2.7",
"@types/js-cookie": "^3.0.6",
"@types/js-yaml": "^4.0.9",
"@types/lodash-es": "^4.17.12",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"@types/react-transition-group": "^4.4.10",
"@vitejs/plugin-react": "^4.2.1",
"adm-zip": "^0.5.12",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@types/react-transition-group": "^4.4.12",
"@vitejs/plugin-legacy": "^6.0.2",
"@vitejs/plugin-react": "^4.3.4",
"adm-zip": "^0.5.16",
"cross-env": "^7.0.3",
"fs-extra": "^11.2.0",
"https-proxy-agent": "^5.0.1",
"husky": "^7.0.4",
"https-proxy-agent": "^7.0.6",
"husky": "^9.1.7",
"meta-json-schema": "^1.19.3",
"node-fetch": "^3.3.2",
"prettier": "^2.8.8",
"pretty-quick": "^3.3.1",
"sass": "^1.75.0",
"typescript": "^5.4.5",
"vite": "^5.2.10",
"prettier": "^3.5.3",
"pretty-quick": "^4.1.1",
"sass": "^1.86.0",
"terser": "^5.39.0",
"typescript": "^5.8.2",
"vite": "^6.2.2",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-svgr": "^4.2.0"
"vite-plugin-svgr": "^4.3.0"
},
"prettier": {
"tabWidth": 2,
"semi": true,
"singleQuote": false,
"endOfLine": "lf"
}
},
"type": "module",
"packageManager": "pnpm@9.13.2"
}
+4408 -3722
View File
File diff suppressed because it is too large Load Diff
+162 -86
View File
@@ -1,11 +1,14 @@
import fs from "fs-extra";
import fs from "fs";
import fsp from "fs/promises";
import zlib from "zlib";
import tar from "tar";
import { extract } from "tar";
import path from "path";
import AdmZip from "adm-zip";
import fetch from "node-fetch";
import proxyAgent from "https-proxy-agent";
import { HttpsProxyAgent } from "https-proxy-agent";
import { execSync } from "child_process";
import { log_info, log_debug, log_error, log_success } from "./utils.mjs";
import { glob } from "glob";
const cwd = process.cwd();
const TEMP_DIR = path.join(cwd, "node_modules/.verge");
@@ -61,7 +64,7 @@ const META_ALPHA_MAP = {
"win32-x64": "mihomo-windows-amd64-compatible",
"win32-ia32": "mihomo-windows-386",
"win32-arm64": "mihomo-windows-arm64",
"darwin-x64": "mihomo-darwin-amd64",
"darwin-x64": "mihomo-darwin-amd64-compatible",
"darwin-arm64": "mihomo-darwin-arm64",
"linux-x64": "mihomo-linux-amd64-compatible",
"linux-ia32": "mihomo-linux-386",
@@ -82,7 +85,7 @@ async function getLatestAlphaVersion() {
process.env.https_proxy;
if (httpProxy) {
options.agent = proxyAgent(httpProxy);
options.agent = new HttpsProxyAgent(httpProxy);
}
try {
const response = await fetch(META_ALPHA_VERSION_URL, {
@@ -91,9 +94,9 @@ async function getLatestAlphaVersion() {
});
let v = await response.text();
META_ALPHA_VERSION = v.trim(); // Trim to remove extra whitespaces
console.log(`Latest alpha version: ${META_ALPHA_VERSION}`);
log_info(`Latest alpha version: ${META_ALPHA_VERSION}`);
} catch (error) {
console.error("Error fetching latest alpha version:", error.message);
log_error("Error fetching latest alpha version:", error.message);
process.exit(1);
}
}
@@ -108,7 +111,7 @@ const META_MAP = {
"win32-x64": "mihomo-windows-amd64-compatible",
"win32-ia32": "mihomo-windows-386",
"win32-arm64": "mihomo-windows-arm64",
"darwin-x64": "mihomo-darwin-amd64",
"darwin-x64": "mihomo-darwin-amd64-compatible",
"darwin-arm64": "mihomo-darwin-arm64",
"linux-x64": "mihomo-linux-amd64-compatible",
"linux-ia32": "mihomo-linux-386",
@@ -129,7 +132,7 @@ async function getLatestReleaseVersion() {
process.env.https_proxy;
if (httpProxy) {
options.agent = proxyAgent(httpProxy);
options.agent = new HttpsProxyAgent(httpProxy);
}
try {
const response = await fetch(META_VERSION_URL, {
@@ -138,9 +141,9 @@ async function getLatestReleaseVersion() {
});
let v = await response.text();
META_VERSION = v.trim(); // Trim to remove extra whitespaces
console.log(`Latest release version: ${META_VERSION}`);
log_info(`Latest release version: ${META_VERSION}`);
} catch (error) {
console.error("Error fetching latest release version:", error.message);
log_error("Error fetching latest release version:", error.message);
process.exit(1);
}
}
@@ -150,13 +153,13 @@ async function getLatestReleaseVersion() {
*/
if (!META_MAP[`${platform}-${arch}`]) {
throw new Error(
`clash meta alpha unsupported platform "${platform}-${arch}"`
`clash meta alpha unsupported platform "${platform}-${arch}"`,
);
}
if (!META_ALPHA_MAP[`${platform}-${arch}`]) {
throw new Error(
`clash meta alpha unsupported platform "${platform}-${arch}"`
`clash meta alpha unsupported platform "${platform}-${arch}"`,
);
}
@@ -172,8 +175,8 @@ function clashMetaAlpha() {
const zipFile = `${name}-${META_ALPHA_VERSION}.${urlExt}`;
return {
name: "clash-meta-alpha",
targetFile: `clash-meta-alpha-${SIDECAR_HOST}${isWin ? ".exe" : ""}`,
name: "verge-mihomo-alpha",
targetFile: `verge-mihomo-alpha-${SIDECAR_HOST}${isWin ? ".exe" : ""}`,
exeFile,
zipFile,
downloadURL,
@@ -189,8 +192,8 @@ function clashMeta() {
const zipFile = `${name}-${META_VERSION}.${urlExt}`;
return {
name: "clash-meta",
targetFile: `clash-meta-${SIDECAR_HOST}${isWin ? ".exe" : ""}`,
name: "verge-mihomo",
targetFile: `verge-mihomo-${SIDECAR_HOST}${isWin ? ".exe" : ""}`,
exeFile,
zipFile,
downloadURL,
@@ -205,44 +208,44 @@ async function resolveSidecar(binInfo) {
const sidecarDir = path.join(cwd, "src-tauri", "sidecar");
const sidecarPath = path.join(sidecarDir, targetFile);
await fs.mkdirp(sidecarDir);
if (!FORCE && (await fs.pathExists(sidecarPath))) return;
await fsp.mkdir(sidecarDir, { recursive: true });
if (!FORCE && fs.existsSync(sidecarPath)) return;
const tempDir = path.join(TEMP_DIR, name);
const tempZip = path.join(tempDir, zipFile);
const tempExe = path.join(tempDir, exeFile);
await fs.mkdirp(tempDir);
await fsp.mkdir(tempDir, { recursive: true });
try {
if (!(await fs.pathExists(tempZip))) {
if (!fs.existsSync(tempZip)) {
await downloadFile(downloadURL, tempZip);
}
if (zipFile.endsWith(".zip")) {
const zip = new AdmZip(tempZip);
zip.getEntries().forEach((entry) => {
console.log(`[DEBUG]: "${name}" entry name`, entry.entryName);
log_debug(`"${name}" entry name`, entry.entryName);
});
zip.extractAllTo(tempDir, true);
await fs.rename(tempExe, sidecarPath);
console.log(`[INFO]: "${name}" unzip finished`);
await fsp.rename(tempExe, sidecarPath);
log_success(`unzip finished: "${name}"`);
} else if (zipFile.endsWith(".tgz")) {
// tgz
await fs.mkdirp(tempDir);
await tar.extract({
await fsp.mkdir(tempDir, { recursive: true });
await extract({
cwd: tempDir,
file: tempZip,
//strip: 1, // 可能需要根据实际的 .tgz 文件结构调整
});
const files = await fs.readdir(tempDir);
console.log(`[DEBUG]: "${name}" files in tempDir:`, files);
const files = await fsp.readdir(tempDir);
log_debug(`"${name}" files in tempDir:`, files);
const extractedFile = files.find((file) => file.startsWith("虚空终端-"));
if (extractedFile) {
const extractedFilePath = path.join(tempDir, extractedFile);
await fs.rename(extractedFilePath, sidecarPath);
console.log(`[INFO]: "${name}" file renamed to "${sidecarPath}"`);
await fsp.rename(extractedFilePath, sidecarPath);
log_success(`"${name}" file renamed to "${sidecarPath}"`);
execSync(`chmod 755 ${sidecarPath}`);
console.log(`[INFO]: "${name}" chmod binary finished`);
log_success(`chmod binary finished: "${name}"`);
} else {
throw new Error(`Expected file not found in ${tempDir}`);
}
@@ -252,16 +255,15 @@ async function resolveSidecar(binInfo) {
const writeStream = fs.createWriteStream(sidecarPath);
await new Promise((resolve, reject) => {
const onError = (error) => {
console.error(`[ERROR]: "${name}" gz failed:`, error.message);
log_error(`"${name}" gz failed:`, error.message);
reject(error);
};
readStream
.pipe(zlib.createGunzip().on("error", onError))
.pipe(writeStream)
.on("finish", () => {
console.log(`[INFO]: "${name}" gunzip finished`);
execSync(`chmod 755 ${sidecarPath}`);
console.log(`[INFO]: "${name}" chmod binary finished`);
log_success(`chmod binary finished: "${name}"`);
resolve();
})
.on("error", onError);
@@ -269,35 +271,58 @@ async function resolveSidecar(binInfo) {
}
} catch (err) {
// 需要删除文件
await fs.remove(sidecarPath);
await fsp.rm(sidecarPath, { recursive: true, force: true });
throw err;
} finally {
// delete temp dir
await fs.remove(tempDir);
await fsp.rm(tempDir, { recursive: true, force: true });
}
}
const resolveSetDnsScript = () =>
resolveResource({
file: "set_dns.sh",
localPath: path.join(cwd, "scripts/set_dns.sh"),
});
const resolveUnSetDnsScript = () =>
resolveResource({
file: "unset_dns.sh",
localPath: path.join(cwd, "scripts/unset_dns.sh"),
});
/**
* download the file to the resources dir
*/
async function resolveResource(binInfo) {
const { file, downloadURL } = binInfo;
const { file, downloadURL, localPath } = binInfo;
const resDir = path.join(cwd, "src-tauri/resources");
const targetPath = path.join(resDir, file);
if (!FORCE && (await fs.pathExists(targetPath))) return;
if (!FORCE && fs.existsSync(targetPath)) return;
await fs.mkdirp(resDir);
await downloadFile(downloadURL, targetPath);
if (downloadURL) {
await fsp.mkdir(resDir, { recursive: true });
await downloadFile(downloadURL, targetPath);
}
console.log(`[INFO]: ${file} finished`);
if (localPath) {
await fs.copyFile(localPath, targetPath, (err) => {
if (err) {
console.error("Error copying file:", err);
} else {
console.log("File was copied successfully");
}
});
log_debug(`copy file finished: "${localPath}"`);
}
log_success(`${file} finished`);
}
/**
* download file and save to `path`
*/
async function downloadFile(url, path) {
*/ async function downloadFile(url, path) {
const options = {};
const httpProxy =
@@ -307,7 +332,7 @@ async function downloadFile(url, path) {
process.env.https_proxy;
if (httpProxy) {
options.agent = proxyAgent(httpProxy);
options.agent = new HttpsProxyAgent(httpProxy);
}
const response = await fetch(url, {
@@ -316,9 +341,9 @@ async function downloadFile(url, path) {
headers: { "Content-Type": "application/octet-stream" },
});
const buffer = await response.arrayBuffer();
await fs.writeFile(path, new Uint8Array(buffer));
await fsp.writeFile(path, new Uint8Array(buffer));
console.log(`[INFO]: download finished "${url}"`);
log_success(`download finished: ${url}`);
}
// SimpleSC.dll
@@ -329,87 +354,111 @@ const resolvePlugin = async () => {
const tempDir = path.join(TEMP_DIR, "SimpleSC");
const tempZip = path.join(
tempDir,
"NSIS_Simple_Service_Plugin_Unicode_1.30.zip"
"NSIS_Simple_Service_Plugin_Unicode_1.30.zip",
);
const tempDll = path.join(tempDir, "SimpleSC.dll");
const pluginDir = path.join(process.env.APPDATA, "Local/NSIS");
const pluginPath = path.join(pluginDir, "SimpleSC.dll");
await fs.mkdirp(pluginDir);
await fs.mkdirp(tempDir);
if (!FORCE && (await fs.pathExists(pluginPath))) return;
await fsp.mkdir(pluginDir, { recursive: true });
await fsp.mkdir(tempDir, { recursive: true });
if (!FORCE && fs.existsSync(pluginPath)) return;
try {
if (!(await fs.pathExists(tempZip))) {
if (!fs.existsSync(tempZip)) {
await downloadFile(url, tempZip);
}
const zip = new AdmZip(tempZip);
zip.getEntries().forEach((entry) => {
console.log(`[DEBUG]: "SimpleSC" entry name`, entry.entryName);
log_debug(`"SimpleSC" entry name`, entry.entryName);
});
zip.extractAllTo(tempDir, true);
await fs.copyFile(tempDll, pluginPath);
console.log(`[INFO]: "SimpleSC" unzip finished`);
await fsp.cp(tempDll, pluginPath, { recursive: true, force: true });
log_success(`unzip finished: "SimpleSC"`);
} finally {
await fs.remove(tempDir);
await fsp.rm(tempDir, { recursive: true, force: true });
}
};
// service chmod
const resolveServicePermission = async () => {
const serviceExecutables = [
"clash-verge-service",
"install-service",
"uninstall-service",
"clash-verge-service*",
"install-service*",
"uninstall-service*",
];
const resDir = path.join(cwd, "src-tauri/resources");
for (let f of serviceExecutables) {
const targetPath = path.join(resDir, f);
if (await fs.pathExists(targetPath)) {
execSync(`chmod 755 ${targetPath}`);
console.log(`[INFO]: "${targetPath}" chmod finished`);
// 使用glob模块来处理通配符
const files = glob.sync(path.join(resDir, f));
for (let filePath of files) {
if (fs.existsSync(filePath)) {
execSync(`chmod 755 ${filePath}`);
log_success(`chmod finished: "${filePath}"`);
}
}
}
};
// 在 resolveResource 函数后添加新函数
async function resolveLocales() {
const srcLocalesDir = path.join(cwd, "src/locales");
const targetLocalesDir = path.join(cwd, "src-tauri/resources/locales");
try {
// 确保目标目录存在
await fsp.mkdir(targetLocalesDir, { recursive: true });
// 读取所有语言文件
const files = await fsp.readdir(srcLocalesDir);
// 复制每个文件
for (const file of files) {
const srcPath = path.join(srcLocalesDir, file);
const targetPath = path.join(targetLocalesDir, file);
await fsp.copyFile(srcPath, targetPath);
log_success(`Copied locale file: ${file}`);
}
log_success("All locale files copied successfully");
} catch (err) {
log_error("Error copying locale files:", err.message);
throw err;
}
}
/**
* main
*/
const SERVICE_URL = `https://github.com/clash-verge-rev/clash-verge-service/releases/download/${SIDECAR_HOST}`;
const resolveService = () => {
let ext = platform === "win32" ? ".exe" : "";
let suffix = platform === "linux" ? "-" + SIDECAR_HOST : "";
resolveResource({
file: "clash-verge-service" + ext,
file: "clash-verge-service" + suffix + ext,
downloadURL: `${SERVICE_URL}/clash-verge-service${ext}`,
});
};
const resolveInstall = () => {
let ext = platform === "win32" ? ".exe" : "";
let suffix = platform === "linux" ? "-" + SIDECAR_HOST : "";
resolveResource({
file: "install-service" + ext,
file: "install-service" + suffix + ext,
downloadURL: `${SERVICE_URL}/install-service${ext}`,
});
};
const resolveUninstall = () => {
let ext = platform === "win32" ? ".exe" : "";
let suffix = platform === "linux" ? "-" + SIDECAR_HOST : "";
resolveResource({
file: "uninstall-service" + ext,
file: "uninstall-service" + suffix + ext,
downloadURL: `${SERVICE_URL}/uninstall-service${ext}`,
});
};
const resolveSetDnsScript = () =>
resolveResource({
file: "set_dns.sh",
downloadURL: `https://github.com/clash-verge-rev/set-dns-script/releases/download/script/set_dns.sh`,
});
const resolveUnSetDnsScript = () =>
resolveResource({
file: "unset_dns.sh",
downloadURL: `https://github.com/clash-verge-rev/set-dns-script/releases/download/script/unset_dns.sh`,
});
const resolveMmdb = () =>
resolveResource({
file: "Country.mmdb",
@@ -431,16 +480,22 @@ const resolveEnableLoopback = () =>
downloadURL: `https://github.com/Kuingsmile/uwp-tool/releases/download/latest/enableLoopback.exe`,
});
const resolveWinSysproxy = () =>
resolveResource({
file: "sysproxy.exe",
downloadURL: `https://github.com/clash-verge-rev/sysproxy/releases/download/${arch}/sysproxy.exe`,
});
const tasks = [
// { name: "clash", func: resolveClash, retry: 5 },
{
name: "clash-meta-alpha",
name: "verge-mihomo-alpha",
func: () =>
getLatestAlphaVersion().then(() => resolveSidecar(clashMetaAlpha())),
retry: 5,
},
{
name: "clash-meta",
name: "verge-mihomo",
func: () =>
getLatestReleaseVersion().then(() => resolveSidecar(clashMeta())),
retry: 5,
@@ -449,8 +504,6 @@ const tasks = [
{ name: "service", func: resolveService, retry: 5 },
{ name: "install", func: resolveInstall, retry: 5 },
{ name: "uninstall", func: resolveUninstall, retry: 5 },
{ name: "set_dns_script", func: resolveSetDnsScript, retry: 5 },
{ name: "unset_dns_script", func: resolveUnSetDnsScript, retry: 5 },
{ name: "mmdb", func: resolveMmdb, retry: 5 },
{ name: "geosite", func: resolveGeosite, retry: 5 },
{ name: "geoip", func: resolveGeoIP, retry: 5 },
@@ -463,24 +516,48 @@ const tasks = [
{
name: "service_chmod",
func: resolveServicePermission,
retry: 1,
unixOnly: true,
retry: 5,
unixOnly: platform === "linux" || platform === "darwin",
},
{
name: "windows-sysproxy",
func: resolveWinSysproxy,
retry: 5,
winOnly: true,
},
{
name: "set_dns_script",
func: resolveSetDnsScript,
retry: 5,
macosOnly: true,
},
{
name: "unset_dns_script",
func: resolveUnSetDnsScript,
retry: 5,
macosOnly: true,
},
{
name: "locales",
func: resolveLocales,
retry: 2,
},
];
async function runTask() {
const task = tasks.shift();
if (!task) return;
if (task.winOnly && platform !== "win32") return runTask();
if (task.linuxOnly && platform !== "linux") return runTask();
if (task.unixOnly && platform === "win32") return runTask();
if (task.winOnly && platform !== "win32") return runTask();
if (task.macosOnly && platform !== "darwin") return runTask();
if (task.linuxOnly && platform !== "linux") return runTask();
for (let i = 0; i < task.retry; i++) {
try {
await task.func();
break;
} catch (err) {
console.error(`[ERROR]: task::${task.name} try ${i} ==`, err.message);
log_error(`task::${task.name} try ${i} ==`, err.message);
if (i === task.retry - 1) throw err;
}
}
@@ -488,4 +565,3 @@ async function runTask() {
}
runTask();
runTask();
+56
View File
@@ -0,0 +1,56 @@
import { exec } from "child_process";
import { promisify } from "util";
import fs from "fs/promises";
import path from "path";
/**
* 为Alpha版本重命名版本号
*/
const execPromise = promisify(exec);
/**
* 标准输出HEAD hash
*/
async function getLatestCommitHash() {
try {
const { stdout } = await execPromise("git rev-parse HEAD");
const commitHash = stdout.trim();
// 格式化,只截取前7位字符
const formathash = commitHash.substring(0, 7);
console.log(`Found the latest commit hash code: ${commitHash}`);
return formathash;
} catch (error) {
console.error("pnpm run fix-alpha-version ERROR", error);
}
}
/**
* @param string 传入格式化后的hash
* 将新的版本号写入文件 package.json
*/
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);
// 获取键值替换
let result = packageJson.version.replace("alpha", newVersion);
console.log("[INFO]: Current version is: ", result);
packageJson.version = result;
// 写入版本号
await fs.writeFile(
packageJsonPath,
JSON.stringify(packageJson, null, 2),
"utf8",
);
console.log(`[INFO]: Alpha version update to: ${newVersion}`);
} catch (error) {
console.error("pnpm run fix-alpha-version ERROR", error);
}
}
const newVersion = await getLatestCommitHash();
updatePackageVersion(newVersion).catch(console.error);
+103
View File
@@ -0,0 +1,103 @@
import fs from "fs";
import fsp from "fs/promises";
import path from "path";
import AdmZip from "adm-zip";
import { createRequire } from "module";
import { getOctokit, context } from "@actions/github";
const target = process.argv.slice(2)[0];
const alpha = process.argv.slice(2)[1];
const ARCH_MAP = {
"x86_64-pc-windows-msvc": "x64",
"i686-pc-windows-msvc": "x86",
"aarch64-pc-windows-msvc": "arm64",
};
const PROCESS_MAP = {
x64: "x64",
ia32: "x86",
arm64: "arm64",
};
const arch = target ? ARCH_MAP[target] : PROCESS_MAP[process.arch];
/// Script for ci
/// 打包绿色版/便携版 (only Windows)
async function resolvePortable() {
if (process.platform !== "win32") return;
const releaseDir = target
? `./src-tauri/target/${target}/release`
: `./src-tauri/target/release`;
const configDir = path.join(releaseDir, ".config");
if (!fs.existsSync(releaseDir)) {
throw new Error("could not found the release dir");
}
await fsp.mkdir(configDir, { recursive: true });
if (!fs.existsSync(path.join(configDir, "PORTABLE"))) {
await fsp.writeFile(path.join(configDir, "PORTABLE"), "");
}
const zip = new AdmZip();
zip.addLocalFile(path.join(releaseDir, "Clash Verge.exe"));
zip.addLocalFile(path.join(releaseDir, "verge-mihomo.exe"));
zip.addLocalFile(path.join(releaseDir, "verge-mihomo-alpha.exe"));
zip.addLocalFolder(path.join(releaseDir, "resources"), "resources");
zip.addLocalFolder(
path.join(
releaseDir,
`Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${arch}`,
),
`Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${arch}`,
);
zip.addLocalFolder(configDir, ".config");
const require = createRequire(import.meta.url);
const packageJson = require("../package.json");
const { version } = packageJson;
const zipFile = `Clash.Verge_${version}_${arch}_fixed_webview2_portable.zip`;
zip.writeZip(zipFile);
console.log("[INFO]: create portable zip successfully");
// push release assets
if (process.env.GITHUB_TOKEN === undefined) {
throw new Error("GITHUB_TOKEN is required");
}
const options = { owner: context.repo.owner, repo: context.repo.repo };
const github = getOctokit(process.env.GITHUB_TOKEN);
const tag = alpha ? "alpha" : process.env.TAG_NAME || `v${version}`;
console.log("[INFO]: upload to ", tag);
const { data: release } = await github.rest.repos.getReleaseByTag({
...options,
tag,
});
let assets = release.assets.filter((x) => {
return x.name === zipFile;
});
if (assets.length > 0) {
let id = assets[0].id;
await github.rest.repos.deleteReleaseAsset({
...options,
asset_id: id,
});
}
console.log(release.name);
await github.rest.repos.uploadReleaseAsset({
...options,
release_id: release.id,
name: zipFile,
data: zip.toBuffer(),
});
}
resolvePortable().catch(console.error);
+16 -49
View File
@@ -1,17 +1,20 @@
import fs from "fs-extra";
import fs from "fs";
import path from "path";
import AdmZip from "adm-zip";
import { createRequire } from "module";
import { getOctokit, context } from "@actions/github";
import fsp from "fs/promises";
const target = process.argv.slice(2)[0];
const alpha = process.argv.slice(2)[1];
const ARCH_MAP = {
"x86_64-pc-windows-msvc": "x64",
"aarch64-pc-windows-msvc": "arm64",
};
const PROCESS_MAP = {
x64: "x64",
arm64: "arm64",
};
const arch = target ? ARCH_MAP[target] : PROCESS_MAP[process.arch];
/// Script for ci
/// 打包绿色版/便携版 (only Windows)
async function resolvePortable() {
@@ -22,64 +25,28 @@ async function resolvePortable() {
: `./src-tauri/target/release`;
const configDir = path.join(releaseDir, ".config");
if (!(await fs.pathExists(releaseDir))) {
if (!fs.existsSync(releaseDir)) {
throw new Error("could not found the release dir");
}
await fs.mkdir(configDir);
await fs.createFile(path.join(configDir, "PORTABLE"));
await fsp.mkdir(configDir, { recursive: true });
if (!fs.existsSync(path.join(configDir, "PORTABLE"))) {
await fsp.writeFile(path.join(configDir, "PORTABLE"), "");
}
const zip = new AdmZip();
zip.addLocalFile(path.join(releaseDir, "Clash Verge.exe"));
zip.addLocalFile(path.join(releaseDir, "clash-meta.exe"));
zip.addLocalFile(path.join(releaseDir, "clash-meta-alpha.exe"));
zip.addLocalFile(path.join(releaseDir, "clash-verge.exe"));
zip.addLocalFile(path.join(releaseDir, "verge-mihomo.exe"));
zip.addLocalFile(path.join(releaseDir, "verge-mihomo-alpha.exe"));
zip.addLocalFolder(path.join(releaseDir, "resources"), "resources");
zip.addLocalFolder(configDir, ".config");
const require = createRequire(import.meta.url);
const packageJson = require("../package.json");
const { version } = packageJson;
const zipFile = `Clash.Verge_${version}_${ARCH_MAP[target]}_portable.zip`;
const zipFile = `Clash.Verge_${version}_${arch}_portable.zip`;
zip.writeZip(zipFile);
console.log("[INFO]: create portable zip successfully");
// push release assets
if (process.env.GITHUB_TOKEN === undefined) {
throw new Error("GITHUB_TOKEN is required");
}
const options = { owner: context.repo.owner, repo: context.repo.repo };
const github = getOctokit(process.env.GITHUB_TOKEN);
const tag = alpha ? "alpha" : process.env.TAG_NAME || `v${version}`;
console.log("[INFO]: upload to ", tag);
const { data: release } = await github.rest.repos.getReleaseByTag({
...options,
tag,
});
let assets = release.assets.filter((x) => {
return x.name === zipFile;
});
if (assets.length > 0) {
let id = assets[0].id;
await github.rest.repos.deleteReleaseAsset({
...options,
asset_id: id,
});
}
console.log(release.name);
await github.rest.repos.uploadReleaseAsset({
...options,
release_id: release.id,
name: zipFile,
data: zip.toBuffer(),
});
}
resolvePortable().catch(console.error);
+96
View File
@@ -0,0 +1,96 @@
import fs from "fs/promises";
import path from "path";
/**
* 更新 package.json 文件中的版本号
*/
async function updatePackageVersion() {
const _dirname = process.cwd();
const packageJsonPath = path.join(_dirname, "package.json");
try {
const data = await fs.readFile(packageJsonPath, "utf8");
const packageJson = JSON.parse(data);
let result = packageJson.version;
if (!result.includes("alpha")) {
result = `${result}-alpha`;
}
console.log("[INFO]: Current package.json version is: ", result);
packageJson.version = result;
await fs.writeFile(
packageJsonPath,
JSON.stringify(packageJson, null, 2),
"utf8",
);
console.log(`[INFO]: package.json version updated to: ${result}`);
} catch (error) {
console.error("Error updating package.json version:", error);
}
}
/**
* 更新 Cargo.toml 文件中的版本号
*/
async function updateCargoVersion() {
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 updatedLines = lines.map((line) => {
if (line.startsWith("version =")) {
const versionMatch = line.match(/version\s*=\s*"([^"]+)"/);
if (versionMatch && !versionMatch[1].includes("alpha")) {
const newVersion = `${versionMatch[1]}-alpha`;
return line.replace(versionMatch[1], newVersion);
}
}
return line;
});
await fs.writeFile(cargoTomlPath, updatedLines.join("\n"), "utf8");
} catch (error) {
console.error("Error updating Cargo.toml version:", error);
}
}
/**
* 更新 tauri.conf.json 文件中的版本号
*/
async function updateTauriConfigVersion() {
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);
let version = tauriConfig.version;
if (!version.includes("alpha")) {
version = `${version}-alpha`;
}
console.log("[INFO]: Current tauri.conf.json version is: ", version);
tauriConfig.version = version;
await fs.writeFile(
tauriConfigPath,
JSON.stringify(tauriConfig, null, 2),
"utf8",
);
console.log(`[INFO]: tauri.conf.json version updated to: ${version}`);
} catch (error) {
console.error("Error updating tauri.conf.json version:", error);
}
}
/**
* 主函数,依次更新所有文件的版本号
*/
async function main() {
await updatePackageVersion();
await updateCargoVersion();
await updateTauriConfigVersion();
}
main().catch(console.error);
+66
View File
@@ -0,0 +1,66 @@
#!/bin/bash
# 验证IPv4地址格式
function is_valid_ipv4() {
local ip=$1
local IFS='.'
local -a octets
[[ ! $ip =~ ^([0-9]+\.){3}[0-9]+$ ]] && return 1
read -r -a octets <<<"$ip"
[ "${#octets[@]}" -ne 4 ] && return 1
for octet in "${octets[@]}"; do
if ! [[ "$octet" =~ ^[0-9]+$ ]] || ((octet < 0 || octet > 255)); then
return 1
fi
done
return 0
}
# 验证IPv6地址格式
function is_valid_ipv6() {
local ip=$1
if [[ ! $ip =~ ^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$ ]] &&
[[ ! $ip =~ ^(([0-9a-fA-F]{0,4}:){0,7}:|(:[0-9a-fA-F]{0,4}:){0,6}:[0-9a-fA-F]{0,4})$ ]]; then
return 1
fi
return 0
}
# 验证IP地址是否为有效的IPv4或IPv6
function is_valid_ip() {
is_valid_ipv4 "$1" || is_valid_ipv6 "$1"
}
# 检查参数
[ $# -lt 1 ] && echo "Usage: $0 <IP address>" && exit 1
! is_valid_ip "$1" && echo "$1 is not a valid IP address." && exit 1
# 获取网络接口和硬件端口
nic=$(route -n get default | grep "interface" | awk '{print $2}')
hardware_port=$(networksetup -listallhardwareports | awk -v dev="$nic" '
/Hardware Port:/{port=$0; gsub("Hardware Port: ", "", port)}
/Device: /{if ($2 == dev) {print port; exit}}
')
# 获取当前DNS设置
original_dns=$(networksetup -getdnsservers "$hardware_port")
# 检查当前DNS设置是否有效
is_valid_dns=false
for ip in $original_dns; do
ip=$(echo "$ip" | tr -d '[:space:]')
if [ -n "$ip" ] && (is_valid_ipv4 "$ip" || is_valid_ipv6 "$ip"); then
is_valid_dns=true
break
fi
done
# 更新DNS设置
if [ "$is_valid_dns" = false ]; then
echo "empty" >.original_dns.txt
else
echo "$original_dns" >.original_dns.txt
fi
networksetup -setdnsservers "$hardware_port" "$1"
+20
View File
@@ -0,0 +1,20 @@
#!/bin/bash
nic=$(route -n get default | grep "interface" | awk '{print $2}')
hardware_port=$(networksetup -listallhardwareports | awk -v dev="$nic" '
/Hardware Port:/{
port=$0; gsub("Hardware Port: ", "", port)
}
/Device: /{
if ($2 == dev) {
print port;
exit
}
}
')
if [ -f .original_dns.txt ]; then
original_dns=$(cat .original_dns.txt)
networksetup -setdnsservers "$hardware_port" $original_dns
rm -rf .original_dns.txt
fi
+43 -3
View File
@@ -1,4 +1,5 @@
import fs from "fs-extra";
import fs from "fs";
import fsp from "fs/promises";
import path from "path";
const UPDATE_LOG = "UPDATELOG.md";
@@ -12,11 +13,11 @@ export async function resolveUpdateLog(tag) {
const file = path.join(cwd, UPDATE_LOG);
if (!(await fs.pathExists(file))) {
if (!fs.existsSync(file)) {
throw new Error("could not found UPDATELOG.md");
}
const data = await fs.readFile(file).then((d) => d.toString("utf8"));
const data = await fsp.readFile(file, "utf-8");
const map = {};
let p = "";
@@ -42,3 +43,42 @@ export async function resolveUpdateLog(tag) {
return map[tag].join("\n").trim();
}
export async function resolveUpdateLogDefault() {
const cwd = process.cwd();
const file = path.join(cwd, UPDATE_LOG);
if (!fs.existsSync(file)) {
throw new Error("could not found UPDATELOG.md");
}
const data = await fsp.readFile(file, "utf-8");
const reTitle = /^## v[\d\.]+/;
const reEnd = /^---/;
let isCapturing = false;
let content = [];
let firstTag = "";
for (const line of data.split("\n")) {
if (reTitle.test(line) && !isCapturing) {
isCapturing = true;
firstTag = line.slice(3).trim();
continue;
}
if (isCapturing) {
if (reEnd.test(line)) {
break;
}
content.push(line);
}
}
if (!firstTag) {
throw new Error("could not found any version tag in UPDATELOG.md");
}
return content.join("\n").trim();
}
+157
View File
@@ -0,0 +1,157 @@
import fetch from "node-fetch";
import { getOctokit, context } from "@actions/github";
import { resolveUpdateLog } from "./updatelog.mjs";
const UPDATE_TAG_NAME = "updater";
const UPDATE_JSON_FILE = "update-fixed-webview2.json";
const UPDATE_JSON_PROXY = "update-fixed-webview2-proxy.json";
/// generate update.json
/// upload to update tag's release asset
async function resolveUpdater() {
if (process.env.GITHUB_TOKEN === undefined) {
throw new Error("GITHUB_TOKEN is required");
}
const options = { owner: context.repo.owner, repo: context.repo.repo };
const github = getOctokit(process.env.GITHUB_TOKEN);
const { data: tags } = await github.rest.repos.listTags({
...options,
per_page: 10,
page: 1,
});
// get the latest publish tag
const tag = tags.find((t) => t.name.startsWith("v"));
console.log(tag);
console.log();
const { data: latestRelease } = await github.rest.repos.getReleaseByTag({
...options,
tag: tag.name,
});
const updateData = {
name: tag.name,
notes: await resolveUpdateLog(tag.name), // use updatelog.md
pub_date: new Date().toISOString(),
platforms: {
"windows-x86_64": { signature: "", url: "" },
"windows-aarch64": { signature: "", url: "" },
"windows-x86": { signature: "", url: "" },
"windows-i686": { signature: "", url: "" },
},
};
const promises = latestRelease.assets.map(async (asset) => {
const { name, browser_download_url } = asset;
// win64 url
if (name.endsWith("x64_fixed_webview2-setup.nsis.zip")) {
updateData.platforms["windows-x86_64"].url = browser_download_url;
}
// win64 signature
if (name.endsWith("x64_fixed_webview2-setup.nsis.zip.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["windows-x86_64"].signature = sig;
}
// win32 url
if (name.endsWith("x86_fixed_webview2-setup.nsis.zip")) {
updateData.platforms["windows-x86"].url = browser_download_url;
updateData.platforms["windows-i686"].url = browser_download_url;
}
// win32 signature
if (name.endsWith("x86_fixed_webview2-setup.nsis.zip.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_fixed_webview2-setup.nsis.zip")) {
updateData.platforms["windows-aarch64"].url = browser_download_url;
}
// win arm signature
if (name.endsWith("arm64_fixed_webview2-setup.nsis.zip.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["windows-aarch64"].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];
}
});
// 生成一个代理github的更新文件
// 使用 https://hub.fastgit.xyz/ 做github资源的加速
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`);
}
});
// update the update.json
const { data: updateRelease } = await github.rest.repos.getReleaseByTag({
...options,
tag: UPDATE_TAG_NAME,
});
// delete the old assets
for (let asset of updateRelease.assets) {
if (asset.name === UPDATE_JSON_FILE) {
await github.rest.repos.deleteReleaseAsset({
...options,
asset_id: asset.id,
});
}
if (asset.name === UPDATE_JSON_PROXY) {
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: UPDATE_JSON_FILE,
data: JSON.stringify(updateData, null, 2),
});
await github.rest.repos.uploadReleaseAsset({
...options,
release_id: updateRelease.id,
name: UPDATE_JSON_PROXY,
data: JSON.stringify(updateDataNew, null, 2),
});
}
// get the signature file content
async function getSignature(url) {
const response = await fetch(url, {
method: "GET",
headers: { "Content-Type": "application/octet-stream" },
});
return response.text();
}
resolveUpdater().catch(console.error);
+285 -158
View File
@@ -1,10 +1,15 @@
import fetch from "node-fetch";
import { getOctokit, context } from "@actions/github";
import { resolveUpdateLog } from "./updatelog.mjs";
import { resolveUpdateLog, resolveUpdateLogDefault } from "./updatelog.mjs";
// Add stable update JSON filenames
const UPDATE_TAG_NAME = "updater";
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";
/// generate update.json
/// upload to update tag's release asset
@@ -16,171 +21,293 @@ async function resolveUpdater() {
const options = { owner: context.repo.owner, repo: context.repo.repo };
const github = getOctokit(process.env.GITHUB_TOKEN);
const { data: tags } = await github.rest.repos.listTags({
...options,
per_page: 10,
page: 1,
});
// Fetch all tags using pagination
let allTags = [];
let page = 1;
const perPage = 100;
// get the latest publish tag
const tag = tags.find((t) => t.name.startsWith("v"));
while (true) {
const { data: pageTags } = await github.rest.repos.listTags({
...options,
per_page: perPage,
page: page,
});
console.log(tag);
console.log();
allTags = allTags.concat(pageTags);
const { data: latestRelease } = await github.rest.repos.getReleaseByTag({
...options,
tag: tag.name,
});
const updateData = {
name: tag.name,
notes: await resolveUpdateLog(tag.name), // use updatelog.md
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-aarch64": { signature: "", url: "" },
"linux-armv7": { signature: "", url: "" },
"windows-x86_64": { signature: "", url: "" },
"windows-aarch64": { signature: "", url: "" },
},
};
const promises = latestRelease.assets.map(async (asset) => {
const { name, browser_download_url } = asset;
// win64 url
if (name.endsWith("x64-setup.nsis.zip")) {
updateData.platforms.win64.url = browser_download_url;
updateData.platforms["windows-x86_64"].url = browser_download_url;
}
// win64 signature
if (name.endsWith("x64-setup.nsis.zip.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms.win64.signature = sig;
updateData.platforms["windows-x86_64"].signature = sig;
// Break if we received fewer tags than requested (last page)
if (pageTags.length < perPage) {
break;
}
// win arm url
if (name.endsWith("arm64-setup.nsis.zip")) {
updateData.platforms["windows-aarch64"].url = browser_download_url;
}
// win arm signature
if (name.endsWith("arm64-setup.nsis.zip.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;
}
// darwin signature (aarch)
if (name.endsWith("aarch64.app.tar.gz.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["darwin-aarch64"].signature = sig;
}
// linux x64 url
if (name.endsWith("amd64.AppImage.tar.gz")) {
updateData.platforms.linux.url = browser_download_url;
updateData.platforms["linux-x86_64"].url = browser_download_url;
// 暂时使用x64版本的url和sig,使得可以检查更新,但aarch64版本还不支持构建appimage
updateData.platforms["linux-aarch64"].url = browser_download_url;
updateData.platforms["linux-armv7"].url = browser_download_url;
}
// linux x64 signature
if (name.endsWith("amd64.AppImage.tar.gz.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms.linux.signature = sig;
updateData.platforms["linux-x86_64"].signature = sig;
// 暂时使用x64版本的url和sig,使得可以检查更新,但aarch64版本还不支持构建appimage
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];
}
});
// 生成一个代理github的更新文件
// 使用 https://hub.fastgit.xyz/ 做github资源的加速
const updateDataNew = JSON.parse(JSON.stringify(updateData));
Object.entries(updateDataNew.platforms).forEach(([key, value]) => {
if (value.url) {
updateDataNew.platforms[key].url =
"https://mirror.ghproxy.com/" + value.url;
} else {
console.log(`[Error]: updateDataNew.platforms.${key} is null`);
}
});
// update the update.json
const { data: updateRelease } = await github.rest.repos.getReleaseByTag({
...options,
tag: UPDATE_TAG_NAME,
});
// delete the old assets
for (let asset of updateRelease.assets) {
if (asset.name === UPDATE_JSON_FILE) {
await github.rest.repos.deleteReleaseAsset({
...options,
asset_id: asset.id,
});
}
if (asset.name === UPDATE_JSON_PROXY) {
await github.rest.repos
.deleteReleaseAsset({ ...options, asset_id: asset.id })
.catch(console.error); // do not break the pipeline
}
page++;
}
// upload new assets
await github.rest.repos.uploadReleaseAsset({
...options,
release_id: updateRelease.id,
name: UPDATE_JSON_FILE,
data: JSON.stringify(updateData, null, 2),
});
const tags = allTags;
console.log(`Retrieved ${tags.length} tags in total`);
await github.rest.repos.uploadReleaseAsset({
...options,
release_id: updateRelease.id,
name: UPDATE_JSON_PROXY,
data: JSON.stringify(updateDataNew, null, 2),
});
// More flexible tag detection with regex patterns
const stableTagRegex = /^v\d+\.\d+\.\d+$/; // Matches vX.Y.Z format
// const preReleaseRegex = /^v\d+\.\d+\.\d+-(alpha|beta|rc|pre)/i; // Matches vX.Y.Z-alpha/beta/rc format
const preReleaseRegex = /^(alpha|beta|rc|pre)$/i; // Matches exact alpha/beta/rc/pre tags
// Get the latest stable tag and pre-release tag
const stableTag = tags.find((t) => stableTagRegex.test(t.name));
const preReleaseTag = tags.find((t) => preReleaseRegex.test(t.name));
console.log("All tags:", tags.map((t) => t.name).join(", "));
console.log("Stable tag:", stableTag ? stableTag.name : "None found");
console.log(
"Pre-release tag:",
preReleaseTag ? preReleaseTag.name : "None found",
);
console.log();
// Process stable release
if (stableTag) {
await processRelease(github, options, stableTag, false);
}
// Process pre-release if found
if (preReleaseTag) {
await processRelease(github, options, preReleaseTag, true);
}
}
// Process a release (stable or alpha) and generate update files
async function processRelease(github, options, tag, isAlpha) {
if (!tag) return;
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,
);
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({
...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) {
console.error(
`Failed to process ${isAlpha ? "alpha" : "stable"} release:`,
error.message,
);
}
} 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,
);
}
}
}
// get the signature file content
+11
View File
@@ -0,0 +1,11 @@
import clc from "cli-color";
export const log_success = (msg, ...optionalParams) =>
console.log(clc.green(msg), ...optionalParams);
export const log_error = (msg, ...optionalParams) =>
console.log(clc.red(msg), ...optionalParams);
export const log_info = (msg, ...optionalParams) =>
console.log(clc.bgBlue(msg), ...optionalParams);
var debugMsg = clc.xterm(245);
export const log_debug = (msg, ...optionalParams) =>
console.log(debugMsg(msg), ...optionalParams);
+1
View File
@@ -0,0 +1 @@
avoid-breaking-exported-api = true
+2
View File
@@ -1,6 +1,8 @@
# Generated by Cargo
# will have compiled files and executables
/target/
gen/
WixTools
resources
sidecar
+4494 -1815
View File
File diff suppressed because it is too large Load Diff
Regular → Executable
+94 -17
View File
@@ -1,6 +1,6 @@
[package]
name = "clash-verge"
version = "1.6.1"
version = "2.2.2"
description = "clash verge"
authors = ["zzzgydi", "wonfen", "MystiPanda"]
license = "GPL-3.0-only"
@@ -9,43 +9,86 @@ default-run = "clash-verge"
edition = "2021"
build = "build.rs"
[package.metadata.bundle]
identifier = "io.github.clash-verge-rev.clash-verge-rev"
[build-dependencies]
tauri-build = { version = "1", features = [] }
tauri-build = { version = "2.0.6", features = [] }
[dependencies]
warp = "0.3"
anyhow = "1.0"
dirs = "5.0"
open = "5.0"
anyhow = "1.0.97"
dirs = "6.0"
open = "5.1"
log = "0.4"
dunce = "1.0"
log4rs = "1"
nanoid = "0.4"
chrono = "0.4"
sysinfo = "0.30"
boa_engine = "0.18"
chrono = "0.4.40"
sysinfo = "0.33.1"
boa_engine = "0.20.0"
serde_json = "1.0"
serde_yaml = "0.9"
once_cell = "1.18"
once_cell = "1.20.3"
port_scanner = "0.1.5"
delay_timer = "0.11.5"
delay_timer = "0.11.6"
parking_lot = "0.12"
percent-encoding = "2.3.1"
window-shadows = { version = "0.2" }
tokio = { version = "1", features = ["full"] }
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"] }
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
auto-launch = { git="https://github.com/zzzgydi/auto-launch", branch = "main" }
tauri = { version = "1.6", features = [ "fs-read-file", "fs-exists", "path-all", "protocol-asset", "dialog-open", "notification-all", "icon-png", "icon-ico", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all", "devtools"] }
reqwest = { version = "0.12", features = ["json", "rustls-tls", "cookies"] }
regex = "1.10.5"
sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs", rev = "3d748b5" }
image = "0.25.5"
imageproc = "0.25.0"
rusttype = "0.9"
tauri = { version = "2.3.1", features = [
"protocol-asset",
"devtools",
"tray-icon",
"image-ico",
"image-png",
] }
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.1"
tauri-plugin-deep-link = "2.2.0"
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.1"
tokio-tungstenite = "0.26.2"
futures = "0.3"
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"
[target.'cfg(windows)'.dependencies]
runas = "=1.2.0"
deelevate = "0.2.0"
winreg = "0.52.0"
winreg = "0.55.0"
url = "2.5.4"
[target.'cfg(target_os = "linux")'.dependencies]
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.5.1"
tauri-plugin-window-state = "2.2.1"
#openssl
[features]
@@ -58,3 +101,37 @@ panic = "abort"
codegen-units = 1
lto = true
opt-level = "s"
strip = true
[profile.dev]
incremental = true
[profile.fast-release]
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 # 不剥离符号,保留调试信息
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[dev-dependencies]
env_logger = "0.11.0"
mockito = "1.7.0"
tempfile = "3.17.1"
[workspace]
members = [
"src_crates/crate_mihomo_api",]
-17
View File
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>Clash Verge</string>
<key>CFBundleURLSchemes</key>
<array>
<string>clash</string>
</array>
</dict>
</array>
</dict>
</plist>
BIN
View File
Binary file not shown.
+21
View File
@@ -0,0 +1,21 @@
{
"identifier": "desktop-capability",
"platforms": ["macOS", "windows", "linux"],
"webviews": ["main"],
"windows": ["main"],
"permissions": [
"global-shortcut:default",
"updater:default",
"dialog:default",
"dialog:allow-ask",
"dialog:allow-message",
"updater:default",
"updater:allow-check",
"updater:allow-download-and-install",
"process:allow-restart",
"deep-link:default",
"window-state:default",
"window-state:default",
"autostart:default"
]
}
+85
View File
@@ -0,0 +1,85 @@
{
"identifier": "migrated",
"description": "permissions that were migrated from v1",
"local": true,
"windows": ["main"],
"permissions": [
"core:default",
"fs:allow-read-file",
"fs:allow-exists",
{
"identifier": "fs:scope",
"allow": ["$APPDATA/**", "$RESOURCE/../**", "**"]
},
"fs:allow-write-file",
{
"identifier": "fs:scope",
"allow": ["$APPDATA/**", "$RESOURCE/../**", "**"]
},
"fs:allow-app-read",
"fs:allow-app-read-recursive",
"fs:allow-appcache-read",
"fs:allow-appcache-read-recursive",
"fs:allow-appconfig-read",
"fs:allow-appconfig-read-recursive",
"core:window:allow-create",
"core:window:allow-center",
"core:window:allow-request-user-attention",
"core:window:allow-set-resizable",
"core:window:allow-set-maximizable",
"core:window:allow-set-minimizable",
"core:window:allow-set-closable",
"core:window:allow-set-title",
"core:window:allow-maximize",
"core:window:allow-unmaximize",
"core:window:allow-minimize",
"core:window:allow-unminimize",
"core:window:allow-show",
"core:window:allow-hide",
"core:window:allow-close",
"core:window:allow-set-decorations",
"core:window:allow-set-always-on-top",
"core:window:allow-set-content-protected",
"core:window:allow-set-size",
"core:window:allow-set-min-size",
"core:window:allow-set-max-size",
"core:window:allow-set-position",
"core:window:allow-set-fullscreen",
"core:window:allow-set-focus",
"core:window:allow-set-icon",
"core:window:allow-set-skip-taskbar",
"core:window:allow-set-cursor-grab",
"core:window:allow-set-cursor-visible",
"core:window:allow-set-cursor-icon",
"core:window:allow-set-cursor-position",
"core:window:allow-set-ignore-cursor-events",
"core:window:allow-start-dragging",
"core:window:allow-maximize",
"core:window:allow-toggle-maximize",
"core:window:allow-unmaximize",
"core:window:allow-minimize",
"core:window:allow-unminimize",
"core:window:allow-set-maximizable",
"core:window:allow-set-minimizable",
"core:webview:allow-print",
"shell:allow-execute",
"shell:allow-open",
"shell:allow-kill",
"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",
"global-shortcut:allow-unregister",
"global-shortcut:allow-unregister-all",
"process:allow-restart",
"process:allow-exit",
"clipboard-manager:allow-read-text",
"clipboard-manager:allow-write-text",
"shell:default",
"dialog:default",
"notification:default"
]
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

@@ -2,8 +2,9 @@
Categories={{{categories}}}
Comment={{{comment}}}
Exec={{{exec}}} %u
StartupWMClass={{{exec}}}
Icon={{{icon}}}
Name={{{name}}}
Terminal=false
Type=Application
MimeType=x-scheme-handler/clash;
MimeType=x-scheme-handler/clash;
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
chmod +x /usr/bin/install-service
chmod +x /usr/bin/uninstall-service
chmod +x /usr/bin/clash-verge-service
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
/usr/bin/uninstall-service
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<false/>
<key>com.apple.security.application-groups</key>
<array>
<string>io.github.clash-verge-rev.clash-verge-rev</string>
</array>
<key>com.apple.security.inherit</key>
<true/>
</dict>
</plist>
@@ -1,5 +1,6 @@
; This file is copied from https://github.com/tauri-apps/tauri/blob/tauri-v1.5/tooling/bundler/src/bundle/windows/templates/installer.nsi
; and edit to fit the needs of the project. the latest tauri 2.x has a different base nsi script.
RequestExecutionLevel admin
Unicode true
; Set the compression algorithm. Default is LZMA.
@@ -13,8 +14,11 @@ Unicode true
!include FileFunc.nsh
!include x64.nsh
!include WordFunc.nsh
!include "LogicLib.nsh"
!include "StrFunc.nsh"
!include "Win\COM.nsh"
!include "Win\Propkey.nsh"
!include "WinVer.nsh"
!include "LogicLib.nsh"
!addplugindir "$%AppData%\Local\NSIS\"
${StrCase}
${StrLoc}
@@ -149,7 +153,6 @@ Function PageReinstall
; however, this should be fine since the user will have to confirm the uninstallation
; and they can chose to abort it if doesn't make sense.
StrCpy $0 0
wix_loop:
EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" $0
StrCmp $1 "" wix_done ; Exit loop if there is no more keys to loop on
@@ -323,10 +326,15 @@ Var AppStartMenuFolder
!define MUI_FINISHPAGE_SHOWREADME_TEXT "$(createDesktop)"
!define MUI_FINISHPAGE_SHOWREADME_FUNCTION CreateDesktopShortcut
; Show run app after installation.
!define MUI_FINISHPAGE_RUN "$INSTDIR\${MAINBINARYNAME}.exe"
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION RunMainBinary
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
!insertmacro MUI_PAGE_FINISH
Function RunMainBinary
nsis_tauri_utils::RunAsUser "$INSTDIR\${MAINBINARYNAME}.exe" ""
FunctionEnd
; Uninstaller Pages
; 1. Confirm uninstall page
Var DeleteAppDataCheckbox
@@ -420,55 +428,85 @@ Function .onInit
FunctionEnd
!macro CheckAllVergeProcesses
; Check if Clash Verge.exe is running
nsis_tauri_utils::FindProcess "Clash Verge.exe"
${If} $R0 != 0
; Kill the process
DetailPrint "Kill Clash Verge.exe..."
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::KillProcessCurrentUser "Clash Verge.exe"
!else
nsis_tauri_utils::KillProcess "Clash Verge.exe"
!endif
${EndIf}
; Check if clash-verge-service.exe is running
; Check if clash-verge-service.exe is running
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::FindProcessCurrentUser "clash-verge-service.exe"
!else
nsis_tauri_utils::FindProcess "clash-verge-service.exe"
${If} $R0 != 0
; Kill the process
DetailPrint "Kill clash-verge-service.exe..."
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::KillProcessCurrentUser "clash-verge-service.exe"
!else
nsis_tauri_utils::KillProcess "clash-verge-service.exe"
!endif
${EndIf}
!endif
Pop $R0
${If} $R0 = 0
DetailPrint "Kill clash-verge-service.exe..."
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::KillProcessCurrentUser "clash-verge-service.exe"
!else
nsis_tauri_utils::KillProcess "clash-verge-service.exe"
!endif
${EndIf}
; Check if clash-meta-alpha.exe is running
; Check if verge-mihomo-alpha.exe is running
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::FindProcessCurrentUser "verge-mihomo-alpha.exe"
!else
nsis_tauri_utils::FindProcess "verge-mihomo-alpha.exe"
!endif
Pop $R0
${If} $R0 = 0
DetailPrint "Kill verge-mihomo-alpha.exe..."
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::KillProcessCurrentUser "verge-mihomo-alpha.exe"
!else
nsis_tauri_utils::KillProcess "verge-mihomo-alpha.exe"
!endif
${EndIf}
; Check if verge-mihomo.exe is running
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::FindProcessCurrentUser "verge-mihomo.exe"
!else
nsis_tauri_utils::FindProcess "verge-mihomo.exe"
!endif
Pop $R0
${If} $R0 = 0
DetailPrint "Kill verge-mihomo.exe..."
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::KillProcessCurrentUser "verge-mihomo.exe"
!else
nsis_tauri_utils::KillProcess "verge-mihomo.exe"
!endif
${EndIf}
; Check if clash-meta-alpha.exe is running
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::FindProcessCurrentUser "clash-meta-alpha.exe"
!else
nsis_tauri_utils::FindProcess "clash-meta-alpha.exe"
${If} $R0 != 0
; Kill the process
DetailPrint "Kill clash-meta-alpha.exe..."
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::KillProcessCurrentUser "clash-meta-alpha.exe"
!else
nsis_tauri_utils::KillProcess "clash-meta-alpha.exe"
!endif
${EndIf}
!endif
Pop $R0
${If} $R0 = 0
DetailPrint "Kill clash-meta-alpha.exe..."
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::KillProcessCurrentUser "clash-meta-alpha.exe"
!else
nsis_tauri_utils::KillProcess "clash-meta-alpha.exe"
!endif
${EndIf}
; Check if clash-meta.exe is running
; Check if clash-meta.exe is running
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::FindProcessCurrentUser "clash-meta.exe"
!else
nsis_tauri_utils::FindProcess "clash-meta.exe"
${If} $R0 != 0
; Kill the process
DetailPrint "Kill clash-meta.exe..."
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::KillProcessCurrentUser "clash-meta.exe"
!else
nsis_tauri_utils::KillProcess "clash-meta.exe"
!endif
${EndIf}
!endif
Pop $R0
${If} $R0 = 0
DetailPrint "Kill clash-meta.exe..."
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::KillProcessCurrentUser "clash-meta.exe"
!else
nsis_tauri_utils::KillProcess "clash-meta.exe"
!endif
${EndIf}
!macroend
!macro StartVergeService
@@ -571,7 +609,7 @@ Section WebView2
!if "${INSTALLWEBVIEW2MODE}" == "downloadBootstrapper"
Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe"
DetailPrint "$(webview2Downloading)"
nsis_tauri_utils::download "https://go.microsoft.com/fwlink/p/?LinkId=2124703" "$TEMP\MicrosoftEdgeWebview2Setup.exe"
NSISdl::download "https://go.microsoft.com/fwlink/p/?LinkId=2124703" "$TEMP\MicrosoftEdgeWebview2Setup.exe"
Pop $0
${If} $0 == 0
DetailPrint "$(webview2DownloadSuccess)"
@@ -653,11 +691,114 @@ SectionEnd
app_check_done:
!macroend
Var VC_REDIST_URL
Var VC_REDIST_EXE
Section CheckAndInstallVSRuntime
; 检查是否已安装 Visual C++ Redistributable
${If} ${IsNativeARM64}
StrCpy $VC_REDIST_URL "https://aka.ms/vs/17/release/vc_redist.arm64.exe"
StrCpy $VC_REDIST_EXE "vc_redist.arm64.exe"
; 检查关键DLL
IfFileExists "$SYSDIR\vcruntime140.dll" 0 checkInstall
IfFileExists "$SYSDIR\msvcp140.dll" Done checkInstall
${ElseIf} ${RunningX64}
StrCpy $VC_REDIST_URL "https://aka.ms/vs/17/release/vc_redist.x64.exe"
StrCpy $VC_REDIST_EXE "vc_redist.x64.exe"
; 检查关键DLL
IfFileExists "$SYSDIR\vcruntime140.dll" 0 checkInstall
IfFileExists "$SYSDIR\msvcp140.dll" Done checkInstall
${Else}
StrCpy $VC_REDIST_URL "https://aka.ms/vs/17/release/vc_redist.x86.exe"
StrCpy $VC_REDIST_EXE "vc_redist.x86.exe"
; 检查关键DLL
IfFileExists "$SYSDIR\vcruntime140.dll" 0 checkInstall
IfFileExists "$SYSDIR\msvcp140.dll" Done checkInstall
${EndIf}
checkInstall:
; 检查注册表
${If} ${RunningX64}
SetRegView 64
ReadRegDword $R0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\${ARCH}" "Installed"
${If} $R0 == "1"
Goto Done
${EndIf}
${Else}
ReadRegDword $R0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x86" "Installed"
${If} $R0 == "1"
Goto Done
${EndIf}
${EndIf}
; 如果没有安装,则下载并安装
DetailPrint "正在下载 Visual C++ Redistributable..."
nsisdl::download "$VC_REDIST_URL" "$TEMP\$VC_REDIST_EXE"
Pop $0
${If} $0 == "success"
DetailPrint "正在安装 Visual C++ Redistributable..."
ExecWait '"$TEMP\$VC_REDIST_EXE" /quiet /norestart' $0
${If} $0 == 0
DetailPrint "Visual C++ Redistributable 安装成功"
${Else}
DetailPrint "Visual C++ Redistributable 安装失败"
${EndIf}
Delete "$TEMP\$VC_REDIST_EXE"
${Else}
DetailPrint "Visual C++ Redistributable 下载失败"
${EndIf}
Done:
SectionEnd
Section Install
SetOutPath $INSTDIR
nsExec::Exec 'netsh int tcp res'
!insertmacro CheckIfAppIsRunning
!insertmacro CheckAllVergeProcesses
; 清理自启动注册表项
DetailPrint "Cleaning auto-launch registry entries..."
StrCpy $R1 "Software\Microsoft\Windows\CurrentVersion\Run"
SetRegView 64
; 清理旧版本的注册表项 (Clash Verge)
ReadRegStr $R2 HKCU "$R1" "Clash Verge"
${If} $R2 != ""
DeleteRegValue HKCU "$R1" "Clash Verge"
${EndIf}
ReadRegStr $R2 HKLM "$R1" "Clash Verge"
${If} $R2 != ""
DeleteRegValue HKLM "$R1" "Clash Verge"
${EndIf}
; 清理新版本的注册表项 (clash-verge)
ReadRegStr $R2 HKCU "$R1" "clash-verge"
${If} $R2 != ""
DeleteRegValue HKCU "$R1" "clash-verge"
${EndIf}
ReadRegStr $R2 HKLM "$R1" "clash-verge"
${If} $R2 != ""
DeleteRegValue HKLM "$R1" "clash-verge"
${EndIf}
; Delete old files before installation
; Delete clash-verge.desktop
IfFileExists "$INSTDIR\Clash Verge.exe" 0 +2
Delete "$INSTDIR\Clash Verge.exe"
; Copy main executable
File "${MAINBINARYSRCPATH}"
@@ -730,7 +871,8 @@ Function .onInstSuccess
check_r_flag:
${GetOptions} $CMDLINE "/R" $R0
IfErrors run_done 0
Exec '"$INSTDIR\${MAINBINARYNAME}.exe"'
${GetOptions} $CMDLINE "/ARGS" $R0
nsis_tauri_utils::RunAsUser "$INSTDIR\${MAINBINARYNAME}.exe" "$R0"
run_done:
FunctionEnd
@@ -743,35 +885,69 @@ Function un.onInit
!insertmacro MUI_UNGETLANGUAGE
FunctionEnd
Function un.isDirectoryEmpty
Exch $0
Push $1
Push $2
StrCpy $2 0
FindFirst $1 $2 "$0\*.*"
loop:
StrCmp $2 "" done
StrCmp $2 "." next
StrCmp $2 ".." next
StrCpy $0 0
goto done
next:
FindNext $1 $2
goto loop
done:
FindClose $1
StrCmp $2 "" 0 +2
StrCpy $0 1
Pop $2
Pop $1
Exch $0
FunctionEnd
!macro DeleteAppUserModelId
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_DestinationList} ${IID_ICustomDestinationList} r1 ""
${If} $1 P<> 0
${ICustomDestinationList::DeleteList} $1 '("${BUNDLEID}")'
${IUnknown::Release} $1 ""
${EndIf}
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_ApplicationDestinations} ${IID_IApplicationDestinations} r1 ""
${If} $1 P<> 0
${IApplicationDestinations::SetAppID} $1 '("${BUNDLEID}")i.r0'
${If} $0 >= 0
${IApplicationDestinations::RemoveAllDestinations} $1 ''
${EndIf}
${IUnknown::Release} $1 ""
${EndIf}
!macroend
; From https://stackoverflow.com/a/42816728/16993372
!macro UnpinShortcut shortcut
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_StartMenuPin} ${IID_IStartMenuPinnedList} r0 ""
${If} $0 P<> 0
System::Call 'SHELL32::SHCreateItemFromParsingName(ws, p0, g "${IID_IShellItem}", *p0r1)' "${shortcut}"
${If} $1 P<> 0
${IStartMenuPinnedList::RemoveFromList} $0 '(r1)'
${IUnknown::Release} $1 ""
${EndIf}
${IUnknown::Release} $0 ""
${EndIf}
!macroend
Section Uninstall
!insertmacro CheckIfAppIsRunning
!insertmacro CheckAllVergeProcesses
!insertmacro RemoveVergeService
; 清理自启动注册表项
DetailPrint "Cleaning auto-launch registry entries..."
StrCpy $R1 "Software\Microsoft\Windows\CurrentVersion\Run"
SetRegView 64
; 清理旧版本的注册表项 (Clash Verge)
ReadRegStr $R2 HKCU "$R1" "Clash Verge"
${If} $R2 != ""
DeleteRegValue HKCU "$R1" "Clash Verge"
${EndIf}
ReadRegStr $R2 HKLM "$R1" "Clash Verge"
${If} $R2 != ""
DeleteRegValue HKLM "$R1" "Clash Verge"
${EndIf}
; 清理新版本的注册表项 (clash-verge)
ReadRegStr $R2 HKCU "$R1" "clash-verge"
${If} $R2 != ""
DeleteRegValue HKCU "$R1" "clash-verge"
${EndIf}
ReadRegStr $R2 HKLM "$R1" "clash-verge"
${If} $R2 != ""
DeleteRegValue HKLM "$R1" "clash-verge"
${EndIf}
; Delete the app directory and its content from disk
; Copy main executable
Delete "$INSTDIR\${MAINBINARYNAME}.exe"
@@ -786,18 +962,21 @@ Section Uninstall
Delete "$INSTDIR\\{{this}}"
{{/each}}
; Delete clash-verge.desktop
IfFileExists "$INSTDIR\Clash Verge.exe" 0 +2
Delete "$INSTDIR\Clash Verge.exe"
; Delete uninstaller
Delete "$INSTDIR\uninstall.exe"
; Remove InstallDir
Push "$INSTDIR"
Call un.isDirectoryEmpty
Pop $0
${If} $0 == 1
RMDir /R /REBOOTOK "$INSTDIR"
${Else}
MessageBox MB_OK "Install Directory is not Empty, Please remove it manually."
${EndIf}
{{#each resources_ancestors}}
RMDir /REBOOTOK "$INSTDIR\\{{this}}"
{{/each}}
RMDir "$INSTDIR"
!insertmacro DeleteAppUserModelId
!insertmacro UnpinShortcut "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk"
!insertmacro UnpinShortcut "$DESKTOP\${MAINBINARYNAME}.lnk"
; Remove start menu shortcut
!insertmacro MUI_STARTMENU_GETFOLDER Application $AppStartMenuFolder
@@ -840,13 +1019,39 @@ Function SkipIfPassive
${IfThen} $PassiveMode == 1 ${|} Abort ${|}
FunctionEnd
!macro SetLnkAppUserModelId shortcut
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_ShellLink} ${IID_IShellLink} r0 ""
${If} $0 P<> 0
${IUnknown::QueryInterface} $0 '("${IID_IPersistFile}",.r1)'
${If} $1 P<> 0
${IPersistFile::Load} $1 '("${shortcut}", ${STGM_READWRITE})'
${IUnknown::QueryInterface} $0 '("${IID_IPropertyStore}",.r2)'
${If} $2 P<> 0
System::Call 'Oleaut32::SysAllocString(w "${BUNDLEID}") i.r3'
System::Call '*${SYSSTRUCT_PROPERTYKEY}(${PKEY_AppUserModel_ID})p.r4'
System::Call '*${SYSSTRUCT_PROPVARIANT}(${VT_BSTR},,&i4 $3)p.r5'
${IPropertyStore::SetValue} $2 '($4,$5)'
System::Call 'Oleaut32::SysFreeString($3)'
System::Free $4
System::Free $5
${IPropertyStore::Commit} $2 ""
${IUnknown::Release} $2 ""
${IPersistFile::Save} $1 '("${shortcut}",1)'
${EndIf}
${IUnknown::Release} $1 ""
${EndIf}
${IUnknown::Release} $0 ""
${EndIf}
!macroend
Function CreateDesktopShortcut
CreateShortcut "$DESKTOP\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
ApplicationID::Set "$DESKTOP\${MAINBINARYNAME}.lnk" "${BUNDLEID}"
!insertmacro SetLnkAppUserModelId "$DESKTOP\${MAINBINARYNAME}.lnk"
FunctionEnd
Function CreateStartMenuShortcut
CreateDirectory "$SMPROGRAMS\$AppStartMenuFolder"
CreateShortcut "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
ApplicationID::Set "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "${BUNDLEID}"
!insertmacro SetLnkAppUserModelId "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk"
FunctionEnd
+205
View File
@@ -0,0 +1,205 @@
use super::CmdResult;
use crate::{feat, utils::dirs, wrap_err};
use tauri::Manager;
/// 打开应用程序所在目录
#[tauri::command]
pub fn open_app_dir() -> CmdResult<()> {
let app_dir = wrap_err!(dirs::app_home_dir())?;
wrap_err!(open::that(app_dir))
}
/// 打开核心所在目录
#[tauri::command]
pub fn open_core_dir() -> CmdResult<()> {
let core_dir = wrap_err!(tauri::utils::platform::current_exe())?;
let core_dir = core_dir.parent().ok_or("failed to get core dir")?;
wrap_err!(open::that(core_dir))
}
/// 打开日志目录
#[tauri::command]
pub fn open_logs_dir() -> CmdResult<()> {
let log_dir = wrap_err!(dirs::app_logs_dir())?;
wrap_err!(open::that(log_dir))
}
/// 打开网页链接
#[tauri::command]
pub fn open_web_url(url: String) -> CmdResult<()> {
wrap_err!(open::that(url))
}
/// 打开/关闭开发者工具
#[tauri::command]
pub fn open_devtools(app_handle: tauri::AppHandle) {
if let Some(window) = app_handle.get_webview_window("main") {
if !window.is_devtools_open() {
window.open_devtools();
} else {
window.close_devtools();
}
}
}
/// 退出应用
#[tauri::command]
pub fn exit_app() {
feat::quit(Some(0));
}
/// 重启应用
#[tauri::command]
pub async fn restart_app() -> CmdResult<()> {
feat::restart_app();
Ok(())
}
/// 获取便携版标识
#[tauri::command]
pub fn get_portable_flag() -> CmdResult<bool> {
Ok(*dirs::PORTABLE_FLAG.get().unwrap_or(&false))
}
/// 获取应用目录
#[tauri::command]
pub fn get_app_dir() -> CmdResult<String> {
let app_home_dir = wrap_err!(dirs::app_home_dir())?
.to_string_lossy()
.to_string();
Ok(app_home_dir)
}
/// 获取当前自启动状态
#[tauri::command]
pub fn get_auto_launch_status() -> CmdResult<bool> {
use crate::core::sysopt::Sysopt;
wrap_err!(Sysopt::global().get_launch_status())
}
/// 下载图标缓存
#[tauri::command]
pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String> {
let icon_cache_dir = wrap_err!(dirs::app_home_dir())?.join("icons").join("cache");
let icon_path = icon_cache_dir.join(&name);
// 如果文件已存在,直接返回路径
if icon_path.exists() {
return Ok(icon_path.to_string_lossy().to_string());
}
// 确保缓存目录存在
if !icon_cache_dir.exists() {
let _ = std::fs::create_dir_all(&icon_cache_dir);
}
// 使用临时文件名来下载
let temp_path = icon_cache_dir.join(format!("{}.downloading", &name));
// 下载文件到临时位置
let response = wrap_err!(reqwest::get(&url).await)?;
// 检查内容类型是否为图片
let content_type = response
.headers()
.get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.unwrap_or("");
let is_image = content_type.starts_with("image/");
// 获取响应内容
let content = wrap_err!(response.bytes().await)?;
// 检查内容是否为HTML (针对CDN错误页面)
let is_html = content.len() > 15
&& (content.starts_with(b"<!DOCTYPE html")
|| content.starts_with(b"<html")
|| content.starts_with(b"<?xml"));
// 只有当内容确实是图片时才保存
if is_image && !is_html {
{
let mut file = match std::fs::File::create(&temp_path) {
Ok(file) => file,
Err(_) => {
if icon_path.exists() {
return Ok(icon_path.to_string_lossy().to_string());
} else {
return Err("Failed to create temporary file".into());
}
}
};
wrap_err!(std::io::copy(&mut content.as_ref(), &mut file))?;
}
// 再次检查目标文件是否已存在,避免重命名覆盖其他线程已完成的文件
if !icon_path.exists() {
match std::fs::rename(&temp_path, &icon_path) {
Ok(_) => {}
Err(_) => {
let _ = std::fs::remove_file(&temp_path);
if icon_path.exists() {
return Ok(icon_path.to_string_lossy().to_string());
}
}
}
} else {
let _ = std::fs::remove_file(&temp_path);
}
Ok(icon_path.to_string_lossy().to_string())
} else {
let _ = std::fs::remove_file(&temp_path);
Err(format!("下载的内容不是有效图片: {}", url))
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct IconInfo {
name: String,
previous_t: String,
current_t: String,
}
/// 复制图标文件
#[tauri::command]
pub fn copy_icon_file(path: String, icon_info: IconInfo) -> CmdResult<String> {
use std::{fs, path::Path};
let file_path = Path::new(&path);
let icon_dir = wrap_err!(dirs::app_home_dir())?.join("icons");
if !icon_dir.exists() {
let _ = fs::create_dir_all(&icon_dir);
}
let ext = match file_path.extension() {
Some(e) => e.to_string_lossy().to_string(),
None => "ico".to_string(),
};
let dest_path = icon_dir.join(format!(
"{0}-{1}.{ext}",
icon_info.name, icon_info.current_t
));
if file_path.exists() {
if icon_info.previous_t.trim() != "" {
fs::remove_file(
icon_dir.join(format!("{0}-{1}.png", icon_info.name, icon_info.previous_t)),
)
.unwrap_or_default();
fs::remove_file(
icon_dir.join(format!("{0}-{1}.ico", icon_info.name, icon_info.previous_t)),
)
.unwrap_or_default();
}
match fs::copy(file_path, &dest_path) {
Ok(_) => Ok(dest_path.to_string_lossy().to_string()),
Err(err) => Err(err.to_string()),
}
} else {
Err("file not found".to_string())
}
}
+223
View File
@@ -0,0 +1,223 @@
use super::CmdResult;
use crate::{config::*, core::*, feat, module::mihomo::MihomoManager, wrap_err};
use serde_yaml::Mapping;
/// 复制Clash环境变量
#[tauri::command]
pub fn copy_clash_env() -> CmdResult {
feat::copy_clash_env();
Ok(())
}
/// 获取Clash信息
#[tauri::command]
pub fn get_clash_info() -> CmdResult<ClashInfo> {
Ok(Config::clash().latest().get_client_info())
}
/// 修改Clash配置
#[tauri::command]
pub async fn patch_clash_config(payload: Mapping) -> CmdResult {
wrap_err!(feat::patch_clash(payload).await)
}
/// 修改Clash模式
#[tauri::command]
pub async fn patch_clash_mode(payload: String) -> CmdResult {
feat::change_clash_mode(payload);
Ok(())
}
/// 切换Clash核心
#[tauri::command]
pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>> {
log::info!(target: "app", "changing core to {clash_core}");
match CoreManager::global()
.change_core(Some(clash_core.clone()))
.await
{
Ok(_) => {
log::info!(target: "app", "core changed to {clash_core}");
handle::Handle::notice_message("config_core::change_success", &clash_core);
handle::Handle::refresh_clash();
Ok(None)
}
Err(err) => {
let error_msg = err.to_string();
log::error!(target: "app", "failed to change core: {error_msg}");
handle::Handle::notice_message("config_core::change_error", &error_msg);
Ok(Some(error_msg))
}
}
}
/// 重启核心
#[tauri::command]
pub async fn restart_core() -> CmdResult {
wrap_err!(CoreManager::global().restart_core().await)
}
/// 获取代理延迟
#[tauri::command]
pub async fn clash_api_get_proxy_delay(
name: String,
url: Option<String>,
timeout: i32,
) -> CmdResult<serde_json::Value> {
MihomoManager::global()
.test_proxy_delay(&name, url, timeout)
.await
}
/// 测试URL延迟
#[tauri::command]
pub async fn test_delay(url: String) -> CmdResult<u32> {
Ok(feat::test_delay(url).await.unwrap_or(10000u32))
}
/// 保存DNS配置到单独文件
#[tauri::command]
pub async fn save_dns_config(dns_config: Mapping) -> CmdResult {
use crate::utils::dirs;
use serde_yaml;
use std::fs;
// 获取DNS配置文件路径
let dns_path = dirs::app_home_dir()
.map_err(|e| e.to_string())?
.join("dns_config.yaml");
// 保存DNS配置到文件
let yaml_str = serde_yaml::to_string(&dns_config).map_err(|e| e.to_string())?;
fs::write(&dns_path, yaml_str).map_err(|e| e.to_string())?;
log::info!(target: "app", "DNS config saved to {:?}", dns_path);
Ok(())
}
/// 应用或撤销DNS配置
#[tauri::command]
pub fn apply_dns_config(apply: bool) -> CmdResult {
use crate::{
config::Config,
core::{handle, CoreManager},
utils::dirs,
};
use tauri::async_runtime;
// 使用spawn来处理异步操作
async_runtime::spawn(async move {
if apply {
// 读取DNS配置文件
let dns_path = match dirs::app_home_dir() {
Ok(path) => path.join("dns_config.yaml"),
Err(e) => {
log::error!(target: "app", "Failed to get home dir: {}", e);
return;
}
};
if !dns_path.exists() {
log::warn!(target: "app", "DNS config file not found");
return;
}
let dns_yaml = match std::fs::read_to_string(&dns_path) {
Ok(content) => content,
Err(e) => {
log::error!(target: "app", "Failed to read DNS config: {}", e);
return;
}
};
// 解析DNS配置并创建patch
let patch_config = match serde_yaml::from_str::<serde_yaml::Mapping>(&dns_yaml) {
Ok(config) => {
let mut patch = serde_yaml::Mapping::new();
patch.insert("dns".into(), config.into());
patch
}
Err(e) => {
log::error!(target: "app", "Failed to parse DNS config: {}", e);
return;
}
};
log::info!(target: "app", "Applying DNS config from file");
// 重新生成配置,确保DNS配置被正确应用
// 这里不调用patch_clash以避免将DNS配置写入config.yaml
Config::runtime()
.latest()
.patch_config(patch_config.clone());
// 首先重新生成配置
if let Err(err) = Config::generate().await {
log::error!(target: "app", "Failed to regenerate config with DNS: {}", err);
return;
}
// 然后应用新配置
if let Err(err) = CoreManager::global().update_config().await {
log::error!(target: "app", "Failed to apply config with DNS: {}", err);
} else {
log::info!(target: "app", "DNS config successfully applied");
handle::Handle::refresh_clash();
}
} else {
// 当关闭DNS设置时,不需要对配置进行任何修改
// 直接重新生成配置,让enhance函数自动跳过DNS配置的加载
log::info!(target: "app", "DNS settings disabled, regenerating config");
// 重新生成配置
if let Err(err) = Config::generate().await {
log::error!(target: "app", "Failed to regenerate config: {}", err);
return;
}
// 应用新配置
match CoreManager::global().update_config().await {
Ok(_) => {
log::info!(target: "app", "Config regenerated successfully");
handle::Handle::refresh_clash();
}
Err(err) => {
log::error!(target: "app", "Failed to apply regenerated config: {}", err);
}
}
}
});
Ok(())
}
/// 检查DNS配置文件是否存在
#[tauri::command]
pub fn check_dns_config_exists() -> CmdResult<bool> {
use crate::utils::dirs;
let dns_path = dirs::app_home_dir()
.map_err(|e| e.to_string())?
.join("dns_config.yaml");
Ok(dns_path.exists())
}
/// 获取DNS配置文件内容
#[tauri::command]
pub async fn get_dns_config_content() -> CmdResult<String> {
use crate::utils::dirs;
use std::fs;
let dns_path = dirs::app_home_dir()
.map_err(|e| e.to_string())?
.join("dns_config.yaml");
if !dns_path.exists() {
return Err("DNS config file not found".into());
}
let content = fs::read_to_string(&dns_path).map_err(|e| e.to_string())?;
Ok(content)
}
+8
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)
}
+9
View File
@@ -0,0 +1,9 @@
use crate::module::lightweight;
use super::CmdResult;
#[tauri::command]
pub async fn entry_lightweight_mode() -> CmdResult {
lightweight::entry_lightweight_mode();
Ok(())
}
File diff suppressed because it is too large Load Diff
+38
View File
@@ -0,0 +1,38 @@
use anyhow::Result;
// Common result type used by command functions
pub type CmdResult<T = ()> = Result<T, String>;
// Command modules
pub mod app;
pub mod clash;
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 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 core::*;
pub use media_unlock_checker::*;
pub use network::*;
pub use profile::*;
pub use proxy::*;
pub use runtime::*;
pub use save_profile::*;
pub use system::*;
pub use uwp::*;
pub use validate::*;
pub use verge::*;
pub use webdav::*;
pub use lighteweight::*;
+63
View File
@@ -0,0 +1,63 @@
use super::CmdResult;
use crate::wrap_err;
use network_interface::NetworkInterface;
use serde_yaml::Mapping;
use sysproxy::{Autoproxy, Sysproxy};
/// get the system proxy
#[tauri::command]
pub fn get_sys_proxy() -> CmdResult<Mapping> {
let current = wrap_err!(Sysproxy::get_system_proxy())?;
let mut map = Mapping::new();
map.insert("enable".into(), current.enable.into());
map.insert(
"server".into(),
format!("{}:{}", current.host, current.port).into(),
);
map.insert("bypass".into(), current.bypass.into());
Ok(map)
}
/// get the system proxy
#[tauri::command]
pub fn get_auto_proxy() -> CmdResult<Mapping> {
let current = wrap_err!(Autoproxy::get_auto_proxy())?;
let mut map = Mapping::new();
map.insert("enable".into(), current.enable.into());
map.insert("url".into(), current.url.into());
Ok(map)
}
/// 获取网络接口列表
#[tauri::command]
pub fn get_network_interfaces() -> Vec<String> {
use sysinfo::Networks;
let mut result = Vec::new();
let networks = Networks::new_with_refreshed_list();
for (interface_name, _) in &networks {
result.push(interface_name.clone());
}
result
}
/// 获取网络接口详细信息
#[tauri::command]
pub fn get_network_interfaces_info() -> CmdResult<Vec<NetworkInterface>> {
use network_interface::{NetworkInterface, NetworkInterfaceConfig};
let names = get_network_interfaces();
let interfaces = wrap_err!(NetworkInterface::show())?;
let mut result = Vec::new();
for interface in interfaces {
if names.contains(&interface.name) {
result.push(interface);
}
}
Ok(result)
}
+177
View File
@@ -0,0 +1,177 @@
use super::CmdResult;
use crate::{
config::*,
core::*,
feat, log_err, ret_err,
utils::{dirs, help},
wrap_err,
};
/// 获取配置文件列表
#[tauri::command]
pub fn get_profiles() -> CmdResult<IProfiles> {
let _ = tray::Tray::global().update_menu();
Ok(Config::profiles().data().clone())
}
/// 增强配置文件
#[tauri::command]
pub async fn enhance_profiles() -> CmdResult {
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(())
}
}
}
/// 导入配置文件
#[tauri::command]
pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult {
let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?;
wrap_err!(Config::profiles().data().append_item(item))
}
/// 重新排序配置文件
#[tauri::command]
pub async fn reorder_profile(active_id: String, over_id: String) -> CmdResult {
wrap_err!(Config::profiles().data().reorder(active_id, over_id))
}
/// 创建配置文件
#[tauri::command]
pub async fn create_profile(item: PrfItem, file_data: Option<String>) -> CmdResult {
let item = wrap_err!(PrfItem::from(item, file_data).await)?;
wrap_err!(Config::profiles().data().append_item(item))
}
/// 更新配置文件
#[tauri::command]
pub async fn update_profile(index: String, option: Option<PrfOption>) -> CmdResult {
wrap_err!(feat::update_profile(index, option).await)
}
/// 删除配置文件
#[tauri::command]
pub async fn delete_profile(index: String) -> CmdResult {
let should_update = wrap_err!({ Config::profiles().data().delete_item(index) })?;
if should_update {
wrap_err!(CoreManager::global().update_config().await)?;
handle::Handle::refresh_clash();
}
Ok(())
}
/// 修改profiles的配置
#[tauri::command]
pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
println!("[cmd配置patch] 开始修改配置文件");
// 保存当前配置,以便在验证失败时恢复
let current_profile = Config::profiles().latest().current.clone();
println!("[cmd配置patch] 当前配置: {:?}", current_profile);
// 更新profiles配置
println!("[cmd配置patch] 正在更新配置草稿");
wrap_err!({ Config::profiles().draft().patch_config(profiles) })?;
// 更新配置并进行验证
match CoreManager::global().update_config().await {
Ok((true, _)) => {
println!("[cmd配置patch] 配置更新成功");
handle::Handle::refresh_clash();
let _ = tray::Tray::global().update_tooltip();
Config::profiles().apply();
wrap_err!(Config::profiles().data().save_file())?;
Ok(true)
}
Ok((false, error_msg)) => {
println!("[cmd配置patch] 配置验证失败: {}", error_msg);
Config::profiles().discard();
// 如果验证失败,恢复到之前的配置
if let Some(prev_profile) = current_profile {
println!("[cmd配置patch] 尝试恢复到之前的配置: {}", prev_profile);
let restore_profiles = IProfiles {
current: Some(prev_profile),
items: None,
};
// 静默恢复,不触发验证
wrap_err!({ Config::profiles().draft().patch_config(restore_profiles) })?;
Config::profiles().apply();
wrap_err!(Config::profiles().data().save_file())?;
println!("[cmd配置patch] 成功恢复到之前的配置");
}
// 发送验证错误通知
handle::Handle::notice_message("config_validate::error", &error_msg);
Ok(false)
}
Err(e) => {
println!("[cmd配置patch] 更新过程发生错误: {}", e);
Config::profiles().discard();
handle::Handle::notice_message("config_validate::boot_error", e.to_string());
Ok(false)
}
}
}
/// 根据profile name修改profiles
#[tauri::command]
pub async fn patch_profiles_config_by_profile_index(
_app_handle: tauri::AppHandle,
profile_index: String,
) -> CmdResult<bool> {
let profiles = IProfiles {
current: Some(profile_index),
items: None,
};
patch_profiles_config(profiles).await
}
/// 修改某个profile item的
#[tauri::command]
pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult {
wrap_err!(Config::profiles().data().patch_item(index, profile))?;
Ok(())
}
/// 查看配置文件
#[tauri::command]
pub fn view_profile(app_handle: tauri::AppHandle, index: String) -> CmdResult {
let file = {
wrap_err!(Config::profiles().latest().get_item(&index))?
.file
.clone()
.ok_or("the file field is null")
}?;
let path = wrap_err!(dirs::app_profiles_dir())?.join(file);
if !path.exists() {
ret_err!("the file not found");
}
wrap_err!(help::open_file(app_handle, path))
}
/// 读取配置文件内容
#[tauri::command]
pub fn read_profile_file(index: String) -> CmdResult<String> {
let profiles = Config::profiles();
let profiles = profiles.latest();
let item = wrap_err!(profiles.get_item(&index))?;
let data = wrap_err!(item.read_file())?;
Ok(data)
}
+26
View File
@@ -0,0 +1,26 @@
use super::CmdResult;
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();
let proxies = mannager
.refresh_proxies()
.await
.map(|_| 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();
let providers = mannager
.refresh_providers_proxies()
.await
.map(|_| mannager.get_providers_proxies())
.or_else(|_| Ok(mannager.get_providers_proxies()));
providers
}
+36
View File
@@ -0,0 +1,36 @@
use super::CmdResult;
use crate::{config::*, wrap_err};
use anyhow::Context;
use serde_yaml::Mapping;
use std::collections::HashMap;
/// 获取运行时配置
#[tauri::command]
pub fn get_runtime_config() -> CmdResult<Option<Mapping>> {
Ok(Config::runtime().latest().config.clone())
}
/// 获取运行时YAML配置
#[tauri::command]
pub fn get_runtime_yaml() -> CmdResult<String> {
let runtime = Config::runtime();
let runtime = runtime.latest();
let config = runtime.config.as_ref();
wrap_err!(config
.ok_or(anyhow::anyhow!("failed to parse config to yaml file"))
.and_then(
|config| serde_yaml::to_string(config).context("failed to convert config to yaml")
))
}
/// 获取运行时存在的键
#[tauri::command]
pub fn get_runtime_exists() -> CmdResult<Vec<String>> {
Ok(Config::runtime().latest().exists_keys.clone())
}
/// 获取运行时日志
#[tauri::command]
pub fn get_runtime_logs() -> CmdResult<HashMap<String, Vec<(String, String)>>> {
Ok(Config::runtime().latest().chain_logs.clone())
}
+116
View File
@@ -0,0 +1,116 @@
use super::CmdResult;
use crate::{config::*, core::*, utils::dirs, wrap_err};
use std::fs;
/// 保存profiles的配置
#[tauri::command]
pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdResult {
if file_data.is_none() {
return Ok(());
}
// 在异步操作前完成所有文件操作
let (file_path, original_content, is_merge_file) = {
let profiles = Config::profiles();
let profiles_guard = profiles.latest();
let item = wrap_err!(profiles_guard.get_item(&index))?;
// 确定是否为merge类型文件
let is_merge = item.itype.as_ref().is_some_and(|t| t == "merge");
let content = wrap_err!(item.read_file())?;
let path = item.file.clone().ok_or("file field is null")?;
let profiles_dir = wrap_err!(dirs::app_profiles_dir())?;
(profiles_dir.join(path), content, is_merge)
};
// 保存新的配置文件
wrap_err!(fs::write(&file_path, file_data.clone().unwrap()))?;
let file_path_str = file_path.to_string_lossy().to_string();
println!(
"[cmd配置save] 开始验证配置文件: {}, 是否为merge文件: {}",
file_path_str, is_merge_file
);
// 对于 merge 文件,只进行语法验证,不进行后续内核验证
if is_merge_file {
println!("[cmd配置save] 检测到merge文件,只进行语法验证");
match CoreManager::global()
.validate_config_file(&file_path_str, Some(true))
.await
{
Ok((true, _)) => {
println!("[cmd配置save] merge文件语法验证通过");
// 成功后尝试更新整体配置
if let Err(e) = CoreManager::global().update_config().await {
println!("[cmd配置save] 更新整体配置时发生错误: {}", e);
log::warn!(target: "app", "更新整体配置时发生错误: {}", e);
}
return Ok(());
}
Ok((false, error_msg)) => {
println!("[cmd配置save] merge文件语法验证失败: {}", error_msg);
// 恢复原始配置文件
wrap_err!(fs::write(&file_path, original_content))?;
// 发送合并文件专用错误通知
let result = (false, error_msg.clone());
crate::cmd::validate::handle_yaml_validation_notice(&result, "合并配置文件");
return Ok(());
}
Err(e) => {
println!("[cmd配置save] 验证过程发生错误: {}", e);
// 恢复原始配置文件
wrap_err!(fs::write(&file_path, original_content))?;
return Err(e.to_string());
}
}
}
// 非merge文件使用完整验证流程
match CoreManager::global()
.validate_config_file(&file_path_str, None)
.await
{
Ok((true, _)) => {
println!("[cmd配置save] 验证成功");
Ok(())
}
Ok((false, error_msg)) => {
println!("[cmd配置save] 验证失败: {}", error_msg);
// 恢复原始配置文件
wrap_err!(fs::write(&file_path, original_content))?;
// 智能判断错误类型
let is_script_error = file_path_str.ends_with(".js")
|| error_msg.contains("Script syntax error")
|| error_msg.contains("Script must contain a main function")
|| error_msg.contains("Failed to read script file");
if error_msg.contains("YAML syntax error")
|| error_msg.contains("Failed to read file:")
|| (!file_path_str.ends_with(".js") && !is_script_error)
{
// 普通YAML错误使用YAML通知处理
println!("[cmd配置save] YAML配置文件验证失败,发送通知");
let result = (false, error_msg.clone());
crate::cmd::validate::handle_yaml_validation_notice(&result, "YAML配置文件");
} else if is_script_error {
// 脚本错误使用专门的通知处理
println!("[cmd配置save] 脚本文件验证失败,发送通知");
let result = (false, error_msg.clone());
crate::cmd::validate::handle_script_validation_notice(&result, "脚本文件");
} else {
// 普通配置错误使用一般通知
println!("[cmd配置save] 其他类型验证失败,发送一般通知");
handle::Handle::notice_message("config_validate::error", &error_msg);
}
Ok(())
}
Err(e) => {
println!("[cmd配置save] 验证过程发生错误: {}", e);
// 恢复原始配置文件
wrap_err!(fs::write(&file_path, original_content))?;
Err(e.to_string())
}
}
}
+71
View File
@@ -0,0 +1,71 @@
use super::CmdResult;
use crate::{
core::{self, handle, service, CoreManager},
module::sysinfo::PlatformSpecification,
wrap_err,
};
use once_cell::sync::Lazy;
use std::{
sync::atomic::{AtomicI64, Ordering},
time::{SystemTime, UNIX_EPOCH},
};
use tauri_plugin_clipboard_manager::ClipboardExt;
// 存储应用启动时间的全局变量
static APP_START_TIME: Lazy<AtomicI64> = Lazy::new(|| {
// 获取当前系统时间,转换为毫秒级时间戳
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as i64;
AtomicI64::new(now)
});
#[tauri::command]
pub async fn export_diagnostic_info() -> CmdResult<()> {
let sysinfo = PlatformSpecification::new();
let info = format!("{:?}", sysinfo);
let app_handle = handle::Handle::global().app_handle().unwrap();
let cliboard = app_handle.clipboard();
if cliboard.write_text(info).is_err() {
log::error!(target: "app", "Failed to write to clipboard");
}
Ok(())
}
#[tauri::command]
pub async fn get_system_info() -> CmdResult<String> {
let sysinfo = PlatformSpecification::new();
let info = format!("{:?}", sysinfo);
Ok(info)
}
/// 获取当前内核运行模式
#[tauri::command]
pub async fn get_running_mode() -> Result<String, 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)
}
/// 获取应用的运行时间(毫秒)
#[tauri::command]
pub fn get_app_uptime() -> CmdResult<i64> {
let start_time = APP_START_TIME.load(Ordering::Relaxed);
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as i64;
Ok(now - start_time)
}
+28
View File
@@ -0,0 +1,28 @@
use super::CmdResult;
/// Platform-specific implementation for UWP functionality
#[cfg(windows)]
mod platform {
use super::CmdResult;
use crate::{core::win_uwp, wrap_err};
pub async fn invoke_uwp_tool() -> CmdResult {
wrap_err!(win_uwp::invoke_uwptools().await)
}
}
/// Stub implementation for non-Windows platforms
#[cfg(not(windows))]
mod platform {
use super::CmdResult;
pub async fn invoke_uwp_tool() -> CmdResult {
Ok(())
}
}
/// Command exposed to Tauri
#[tauri::command]
pub async fn invoke_uwp_tool() -> CmdResult {
platform::invoke_uwp_tool().await
}
+104
View File
@@ -0,0 +1,104 @@
use super::CmdResult;
use crate::core::*;
/// 发送脚本验证通知消息
#[tauri::command]
pub async fn script_validate_notice(status: String, msg: String) -> CmdResult {
handle::Handle::notice_message(&status, &msg);
Ok(())
}
/// 处理脚本验证相关的所有消息通知
/// 统一通知接口,保持消息类型一致性
pub fn handle_script_validation_notice(result: &(bool, String), file_type: &str) {
if !result.0 {
let error_msg = &result.1;
// 根据错误消息内容判断错误类型
let status = if error_msg.starts_with("File not found:") {
"config_validate::file_not_found"
} else if error_msg.starts_with("Failed to read script file:") {
"config_validate::script_error"
} else if error_msg.starts_with("Script syntax error:") {
"config_validate::script_syntax_error"
} else if error_msg == "Script must contain a main function" {
"config_validate::script_missing_main"
} else {
// 如果是其他类型错误,作为一般脚本错误处理
"config_validate::script_error"
};
log::warn!(target: "app", "{} 验证失败: {}", file_type, error_msg);
handle::Handle::notice_message(status, error_msg);
}
}
/// 验证指定脚本文件
#[tauri::command]
pub async fn validate_script_file(file_path: String) -> CmdResult<bool> {
log::info!(target: "app", "验证脚本文件: {}", file_path);
match CoreManager::global()
.validate_config_file(&file_path, None)
.await
{
Ok(result) => {
handle_script_validation_notice(&result, "脚本文件");
Ok(result.0) // 返回验证结果布尔值
}
Err(e) => {
let error_msg = e.to_string();
log::error!(target: "app", "验证脚本文件过程发生错误: {}", error_msg);
handle::Handle::notice_message("config_validate::process_terminated", &error_msg);
Ok(false)
}
}
}
/// 处理YAML验证相关的所有消息通知
/// 统一通知接口,保持消息类型一致性
pub fn handle_yaml_validation_notice(result: &(bool, String), file_type: &str) {
if !result.0 {
let error_msg = &result.1;
println!("[通知] 处理{}验证错误: {}", file_type, error_msg);
// 检查是否为merge文件
let is_merge_file = file_type.contains("合并");
// 根据错误消息内容判断错误类型
let status = if error_msg.starts_with("File not found:") {
"config_validate::file_not_found"
} else if error_msg.starts_with("Failed to read file:") {
"config_validate::yaml_read_error"
} else if error_msg.starts_with("YAML syntax error:") {
if is_merge_file {
"config_validate::merge_syntax_error"
} else {
"config_validate::yaml_syntax_error"
}
} else if error_msg.contains("mapping values are not allowed") {
if is_merge_file {
"config_validate::merge_mapping_error"
} else {
"config_validate::yaml_mapping_error"
}
} else if error_msg.contains("did not find expected key") {
if is_merge_file {
"config_validate::merge_key_error"
} else {
"config_validate::yaml_key_error"
}
} else {
// 如果是其他类型错误,根据文件类型作为一般错误处理
if is_merge_file {
"config_validate::merge_error"
} else {
"config_validate::yaml_error"
}
};
log::warn!(target: "app", "{} 验证失败: {}", file_type, error_msg);
println!("[通知] 发送通知: status={}, msg={}", status, error_msg);
handle::Handle::notice_message(status, error_msg);
}
}
+16
View File
@@ -0,0 +1,16 @@
use super::CmdResult;
use crate::{config::*, feat, wrap_err};
/// 获取Verge配置
#[tauri::command]
pub fn get_verge_config() -> CmdResult<IVergeResponse> {
let verge = Config::verge();
let verge_data = verge.data().clone();
Ok(IVergeResponse::from(verge_data))
}
/// 修改Verge配置
#[tauri::command]
pub async fn patch_verge_config(payload: IVerge) -> CmdResult {
wrap_err!(feat::patch_verge(payload, false).await)
}
+46
View File
@@ -0,0 +1,46 @@
use super::CmdResult;
use crate::{config::*, core, feat, wrap_err};
use reqwest_dav::list_cmd::ListFile;
/// 保存 WebDAV 配置
#[tauri::command]
pub async fn save_webdav_config(url: String, username: String, password: String) -> CmdResult<()> {
let patch = IVerge {
webdav_url: Some(url),
webdav_username: Some(username),
webdav_password: Some(password),
..IVerge::default()
};
Config::verge().draft().patch_config(patch.clone());
Config::verge().apply();
Config::verge()
.data()
.save_file()
.map_err(|err| err.to_string())?;
core::backup::WebDavClient::global().reset();
Ok(())
}
/// 创建 WebDAV 备份并上传
#[tauri::command]
pub async fn create_webdav_backup() -> CmdResult<()> {
wrap_err!(feat::create_backup_and_upload_webdav().await)
}
/// 列出 WebDAV 上的备份文件
#[tauri::command]
pub async fn list_webdav_backup() -> CmdResult<Vec<ListFile>> {
wrap_err!(feat::list_wevdav_backup().await)
}
/// 删除 WebDAV 上的备份文件
#[tauri::command]
pub async fn delete_webdav_backup(filename: String) -> CmdResult<()> {
wrap_err!(feat::delete_webdav_backup(filename).await)
}
/// 从 WebDAV 恢复备份文件
#[tauri::command]
pub async fn restore_webdav_backup(filename: String) -> CmdResult<()> {
wrap_err!(feat::restore_webdav_backup(filename).await)
}
-371
View File
@@ -1,371 +0,0 @@
use crate::{
config::*,
core::*,
feat,
utils::{dirs, help, resolve},
};
use crate::{ret_err, wrap_err};
use anyhow::{Context, Result};
use serde_yaml::Mapping;
use std::collections::{HashMap, VecDeque};
use sysproxy::Sysproxy;
use tauri::{api, Manager};
type CmdResult<T = ()> = Result<T, String>;
#[tauri::command]
pub fn get_profiles() -> CmdResult<IProfiles> {
Ok(Config::profiles().data().clone())
}
#[tauri::command]
pub async fn enhance_profiles() -> CmdResult {
wrap_err!(CoreManager::global().update_config().await)?;
handle::Handle::refresh_clash();
Ok(())
}
#[tauri::command]
pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult {
let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?;
wrap_err!(Config::profiles().data().append_item(item))
}
#[tauri::command]
pub async fn reorder_profile(active_id: String, over_id: String) -> CmdResult {
wrap_err!(Config::profiles().data().reorder(active_id, over_id))
}
#[tauri::command]
pub async fn create_profile(item: PrfItem, file_data: Option<String>) -> CmdResult {
let item = wrap_err!(PrfItem::from(item, file_data).await)?;
wrap_err!(Config::profiles().data().append_item(item))
}
#[tauri::command]
pub async fn update_profile(index: String, option: Option<PrfOption>) -> CmdResult {
wrap_err!(feat::update_profile(index, option).await)
}
#[tauri::command]
pub async fn delete_profile(index: String) -> CmdResult {
let should_update = wrap_err!({ Config::profiles().data().delete_item(index) })?;
if should_update {
wrap_err!(CoreManager::global().update_config().await)?;
handle::Handle::refresh_clash();
}
Ok(())
}
/// 修改profiles的
#[tauri::command]
pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult {
wrap_err!({ Config::profiles().draft().patch_config(profiles) })?;
match CoreManager::global().update_config().await {
Ok(_) => {
handle::Handle::refresh_clash();
let _ = handle::Handle::update_systray_part();
Config::profiles().apply();
wrap_err!(Config::profiles().data().save_file())?;
Ok(())
}
Err(err) => {
Config::profiles().discard();
log::error!(target: "app", "{err}");
Err(format!("{err}"))
}
}
}
/// 修改某个profile item的
#[tauri::command]
pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult {
wrap_err!(Config::profiles().data().patch_item(index, profile))?;
wrap_err!(timer::Timer::global().refresh())
}
#[tauri::command]
pub fn view_profile(app_handle: tauri::AppHandle, index: String) -> CmdResult {
let file = {
wrap_err!(Config::profiles().latest().get_item(&index))?
.file
.clone()
.ok_or("the file field is null")
}?;
let path = wrap_err!(dirs::app_profiles_dir())?.join(file);
if !path.exists() {
ret_err!("the file not found");
}
wrap_err!(help::open_file(app_handle, path))
}
#[tauri::command]
pub fn read_profile_file(index: String) -> CmdResult<String> {
let profiles = Config::profiles();
let profiles = profiles.latest();
let item = wrap_err!(profiles.get_item(&index))?;
let data = wrap_err!(item.read_file())?;
Ok(data)
}
#[tauri::command]
pub fn save_profile_file(index: String, file_data: Option<String>) -> CmdResult {
if file_data.is_none() {
return Ok(());
}
let profiles = Config::profiles();
let profiles = profiles.latest();
let item = wrap_err!(profiles.get_item(&index))?;
wrap_err!(item.save_file(file_data.unwrap()))
}
#[tauri::command]
pub fn get_clash_info() -> CmdResult<ClashInfo> {
Ok(Config::clash().latest().get_client_info())
}
#[tauri::command]
pub fn get_runtime_config() -> CmdResult<Option<Mapping>> {
Ok(Config::runtime().latest().config.clone())
}
#[tauri::command]
pub fn get_runtime_yaml() -> CmdResult<String> {
let runtime = Config::runtime();
let runtime = runtime.latest();
let config = runtime.config.as_ref();
wrap_err!(config
.ok_or(anyhow::anyhow!("failed to parse config to yaml file"))
.and_then(
|config| serde_yaml::to_string(config).context("failed to convert config to yaml")
))
}
#[tauri::command]
pub fn get_runtime_exists() -> CmdResult<Vec<String>> {
Ok(Config::runtime().latest().exists_keys.clone())
}
#[tauri::command]
pub fn get_runtime_logs() -> CmdResult<HashMap<String, Vec<(String, String)>>> {
Ok(Config::runtime().latest().chain_logs.clone())
}
#[tauri::command]
pub async fn patch_clash_config(payload: Mapping) -> CmdResult {
wrap_err!(feat::patch_clash(payload).await)
}
#[tauri::command]
pub fn get_verge_config() -> CmdResult<IVerge> {
Ok(Config::verge().data().clone())
}
#[tauri::command]
pub async fn patch_verge_config(payload: IVerge) -> CmdResult {
wrap_err!(feat::patch_verge(payload).await)
}
#[tauri::command]
pub async fn change_clash_core(clash_core: Option<String>) -> CmdResult {
wrap_err!(CoreManager::global().change_core(clash_core).await)
}
/// restart the sidecar
#[tauri::command]
pub async fn restart_sidecar() -> CmdResult {
wrap_err!(CoreManager::global().run_core().await)
}
#[tauri::command]
pub fn grant_permission(_core: String) -> CmdResult {
#[cfg(any(target_os = "macos", target_os = "linux"))]
return wrap_err!(manager::grant_permission(_core));
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
return Err("Unsupported target".into());
}
/// get the system proxy
#[tauri::command]
pub fn get_sys_proxy() -> CmdResult<Mapping> {
let current = wrap_err!(Sysproxy::get_system_proxy())?;
let mut map = Mapping::new();
map.insert("enable".into(), current.enable.into());
map.insert(
"server".into(),
format!("{}:{}", current.host, current.port).into(),
);
map.insert("bypass".into(), current.bypass.into());
Ok(map)
}
#[tauri::command]
pub fn get_clash_logs() -> CmdResult<VecDeque<String>> {
Ok(logger::Logger::global().get_log())
}
#[tauri::command]
pub fn open_app_dir() -> CmdResult<()> {
let app_dir = wrap_err!(dirs::app_home_dir())?;
wrap_err!(open::that(app_dir))
}
#[tauri::command]
pub fn open_core_dir() -> CmdResult<()> {
let core_dir = wrap_err!(tauri::utils::platform::current_exe())?;
let core_dir = core_dir.parent().ok_or("failed to get core dir")?;
wrap_err!(open::that(core_dir))
}
#[tauri::command]
pub fn open_logs_dir() -> CmdResult<()> {
let log_dir = wrap_err!(dirs::app_logs_dir())?;
wrap_err!(open::that(log_dir))
}
#[tauri::command]
pub fn open_web_url(url: String) -> CmdResult<()> {
wrap_err!(open::that(url))
}
#[cfg(windows)]
pub mod uwp {
use super::*;
use crate::core::win_uwp;
#[tauri::command]
pub async fn invoke_uwp_tool() -> CmdResult {
wrap_err!(win_uwp::invoke_uwptools().await)
}
}
#[tauri::command]
pub async fn clash_api_get_proxy_delay(
name: String,
url: Option<String>,
timeout: i32,
) -> CmdResult<clash_api::DelayRes> {
match clash_api::get_proxy_delay(name, url, timeout).await {
Ok(res) => Ok(res),
Err(err) => Err(err.to_string()),
}
}
#[tauri::command]
pub fn get_portable_flag() -> CmdResult<bool> {
Ok(*dirs::PORTABLE_FLAG.get().unwrap_or(&false))
}
#[tauri::command]
pub async fn test_delay(url: String) -> CmdResult<u32> {
Ok(feat::test_delay(url).await.unwrap_or(10000u32))
}
#[tauri::command]
pub fn get_app_dir() -> CmdResult<String> {
let app_home_dir = wrap_err!(dirs::app_home_dir())?
.to_string_lossy()
.to_string();
Ok(app_home_dir)
}
#[tauri::command]
pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String> {
let icon_cache_dir = wrap_err!(dirs::app_home_dir())?.join("icons").join("cache");
let icon_path = icon_cache_dir.join(name);
if !icon_cache_dir.exists() {
let _ = std::fs::create_dir_all(&icon_cache_dir);
}
if !icon_path.exists() {
let response = wrap_err!(reqwest::get(url).await)?;
let mut file = wrap_err!(std::fs::File::create(&icon_path))?;
let content = wrap_err!(response.bytes().await)?;
wrap_err!(std::io::copy(&mut content.as_ref(), &mut file))?;
}
Ok(icon_path.to_string_lossy().to_string())
}
#[tauri::command]
pub fn copy_icon_file(path: String, name: String) -> CmdResult<String> {
let file_path = std::path::Path::new(&path);
let icon_dir = wrap_err!(dirs::app_home_dir())?.join("icons");
if !icon_dir.exists() {
let _ = std::fs::create_dir_all(&icon_dir);
}
let ext = match file_path.extension() {
Some(e) => e.to_string_lossy().to_string(),
None => "ico".to_string(),
};
let png_dest_path = icon_dir.join(format!("{name}.png"));
let ico_dest_path = icon_dir.join(format!("{name}.ico"));
let dest_path = icon_dir.join(format!("{name}.{ext}"));
if file_path.exists() {
std::fs::remove_file(png_dest_path).unwrap_or_default();
std::fs::remove_file(ico_dest_path).unwrap_or_default();
match std::fs::copy(file_path, &dest_path) {
Ok(_) => Ok(dest_path.to_string_lossy().to_string()),
Err(err) => Err(err.to_string()),
}
} else {
return Err("file not found".to_string());
}
}
#[tauri::command]
pub fn open_devtools(app_handle: tauri::AppHandle) {
if let Some(window) = app_handle.get_window("main") {
if !window.is_devtools_open() {
window.open_devtools();
} else {
window.close_devtools();
}
}
}
#[tauri::command]
pub fn exit_app(app_handle: tauri::AppHandle) {
let _ = resolve::save_window_size_position(&app_handle, true);
resolve::resolve_reset();
api::process::kill_children();
app_handle.exit(0);
std::process::exit(0);
}
pub mod service {
use super::*;
use crate::core::service;
#[tauri::command]
pub async fn check_service() -> CmdResult<service::JsonResponse> {
wrap_err!(service::check_service().await)
}
#[tauri::command]
pub async fn install_service() -> CmdResult {
wrap_err!(service::install_service().await)
}
#[tauri::command]
pub async fn uninstall_service() -> CmdResult {
wrap_err!(service::uninstall_service().await)
}
}
#[cfg(not(windows))]
pub mod uwp {
use super::*;
#[tauri::command]
pub async fn invoke_uwp_tool() -> CmdResult {
Ok(())
}
}
+13 -6
View File
@@ -13,7 +13,7 @@ pub struct IClashTemp(pub Mapping);
impl IClashTemp {
pub fn new() -> Self {
let template = Self::template();
match dirs::clash_path().and_then(|path| help::read_merge_mapping(&path)) {
match dirs::clash_path().and_then(|path| help::read_mapping(&path)) {
Ok(mut map) => {
template.0.keys().for_each(|key| {
if !map.contains_key(key) {
@@ -32,13 +32,13 @@ impl IClashTemp {
pub fn template() -> Self {
let mut map = Mapping::new();
let mut tun = Mapping::new();
tun.insert("enable".into(), false.into());
tun.insert("stack".into(), "gvisor".into());
tun.insert("device".into(), "Meta".into());
tun.insert("auto-route".into(), true.into());
tun.insert("strict-route".into(), false.into());
tun.insert("auto-detect-interface".into(), true.into());
tun.insert("dns-hijack".into(), vec!["any:53"].into());
tun.insert("mtu".into(), 1500.into());
#[cfg(not(target_os = "windows"))]
map.insert("redir-port".into(), 7895.into());
#[cfg(target_os = "linux")]
@@ -50,9 +50,13 @@ impl IClashTemp {
map.insert("allow-lan".into(), false.into());
map.insert("mode".into(), "rule".into());
map.insert("external-controller".into(), "127.0.0.1:9097".into());
let mut cors_map = Mapping::new();
cors_map.insert("allow-private-network".into(), true.into());
cors_map.insert("allow-origins".into(), vec!["*"].into());
map.insert("secret".into(), "".into());
map.insert("tun".into(), tun.into());
map.insert("external-controller-cors".into(), cors_map.into());
map.insert("unified-delay".into(), true.into());
Self(map)
}
@@ -153,17 +157,20 @@ impl IClashTemp {
}
pub fn guard_mixed_port(config: &Mapping) -> u16 {
let mut port = config
.get("mixed-port")
let raw_value = config.get("mixed-port");
let mut port = raw_value
.and_then(|value| match value {
Value::String(val_str) => val_str.parse().ok(),
Value::Number(val_num) => val_num.as_u64().map(|u| u as u16),
_ => None,
})
.unwrap_or(7897);
if port == 0 {
port = 7897;
}
port
}
+74 -18
View File
@@ -1,11 +1,14 @@
use super::{Draft, IClashTemp, IProfiles, IRuntime, IVerge};
use crate::{
config::PrfItem,
core::{handle, CoreManager},
enhance,
utils::{dirs, help},
};
use anyhow::{anyhow, Result};
use once_cell::sync::OnceCell;
use std::{env::temp_dir, path::PathBuf};
use std::path::PathBuf;
use tokio::time::{sleep, Duration};
pub const RUNTIME_CONFIG: &str = "clash-verge.yaml";
pub const CHECK_CONFIG: &str = "clash-verge-check.yaml";
@@ -46,21 +49,74 @@ impl Config {
}
/// 初始化订阅
pub fn init_config() -> Result<()> {
crate::log_err!(Self::generate());
if let Err(err) = Self::generate_file(ConfigType::Run) {
log::error!(target: "app", "{err}");
let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG);
// 如果不存在就将默认的clash文件拿过来
if !runtime_path.exists() {
help::save_yaml(
&runtime_path,
&Config::clash().latest().0,
Some("# Clash Verge Runtime"),
)?;
}
pub async fn init_config() -> Result<()> {
if Self::profiles()
.data()
.get_item(&"Merge".to_string())
.is_err()
{
let merge_item = PrfItem::from_merge(Some("Merge".to_string()))?;
Self::profiles().data().append_item(merge_item.clone())?;
}
if Self::profiles()
.data()
.get_item(&"Script".to_string())
.is_err()
{
let script_item = PrfItem::from_script(Some("Script".to_string()))?;
Self::profiles().data().append_item(script_item.clone())?;
}
// 生成运行时配置
crate::log_err!(Self::generate().await);
// 生成运行时配置文件并验证
let config_result = Self::generate_file(ConfigType::Run);
let validation_result = if config_result.is_ok() {
// 验证配置文件
println!("[首次启动] 开始验证配置");
match CoreManager::global().validate_config().await {
Ok((is_valid, error_msg)) => {
if !is_valid {
println!(
"[首次启动] 配置验证失败,使用默认最小配置启动: {}",
error_msg
);
CoreManager::global()
.use_default_config("config_validate::boot_error", &error_msg)
.await?;
Some(("config_validate::boot_error", error_msg))
} else {
println!("[首次启动] 配置验证成功");
Some(("config_validate::success", String::new()))
}
}
Err(err) => {
println!("[首次启动] 验证进程执行失败: {}", err);
CoreManager::global()
.use_default_config("config_validate::process_terminated", "")
.await?;
Some(("config_validate::process_terminated", String::new()))
}
}
} else {
println!("[首次启动] 生成配置文件失败,使用默认配置");
CoreManager::global()
.use_default_config("config_validate::error", "")
.await?;
Some(("config_validate::error", String::new()))
};
// 在单独的任务中发送通知
if let Some((msg_type, msg_content)) = validation_result {
tauri::async_runtime::spawn(async move {
sleep(Duration::from_secs(2)).await;
handle::Handle::notice_message(msg_type, &msg_content);
});
}
Ok(())
}
@@ -68,7 +124,7 @@ impl Config {
pub fn generate_file(typ: ConfigType) -> Result<PathBuf> {
let path = match typ {
ConfigType::Run => dirs::app_home_dir()?.join(RUNTIME_CONFIG),
ConfigType::Check => temp_dir().join(CHECK_CONFIG),
ConfigType::Check => dirs::app_home_dir()?.join(CHECK_CONFIG),
};
let runtime = Config::runtime();
@@ -83,8 +139,8 @@ impl Config {
}
/// 生成订阅存好
pub fn generate() -> Result<()> {
let (config, exists_keys, logs) = enhance::enhance();
pub async fn generate() -> Result<()> {
let (config, exists_keys, logs) = enhance::enhance().await;
*Config::runtime().draft() = IRuntime {
config: Some(config),
+95
View File
@@ -0,0 +1,95 @@
use crate::utils::dirs::get_encryption_key;
use aes_gcm::{
aead::{Aead, KeyInit},
Aes256Gcm, Key,
};
use base64::{engine::general_purpose::STANDARD, Engine};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
const NONCE_LENGTH: usize = 12;
/// Encrypt data
pub fn encrypt_data(data: &str) -> Result<String, Box<dyn std::error::Error>> {
let encryption_key = get_encryption_key()?;
let key = Key::<Aes256Gcm>::from_slice(&encryption_key);
let cipher = Aes256Gcm::new(key);
// Generate random nonce
let mut nonce = vec![0u8; NONCE_LENGTH];
getrandom::fill(&mut nonce)?;
// Encrypt data
let ciphertext = cipher
.encrypt(nonce.as_slice().into(), data.as_bytes())
.map_err(|e| format!("Encryption failed: {}", e))?;
// Concatenate nonce and ciphertext and encode them in base64
let mut combined = nonce;
combined.extend(ciphertext);
Ok(STANDARD.encode(combined))
}
/// Decrypt data
pub fn decrypt_data(encrypted: &str) -> Result<String, Box<dyn std::error::Error>> {
let encryption_key = get_encryption_key()?;
let key = Key::<Aes256Gcm>::from_slice(&encryption_key);
let cipher = Aes256Gcm::new(key);
// Decode from base64
let data = STANDARD.decode(encrypted)?;
if data.len() < NONCE_LENGTH {
return Err("Invalid encrypted data".into());
}
// Separate nonce and ciphertext
let (nonce, ciphertext) = data.split_at(NONCE_LENGTH);
// Decrypt data
let plaintext = cipher
.decrypt(nonce.into(), ciphertext)
.map_err(|e| format!("Decryption failed: {}", e))?;
String::from_utf8(plaintext).map_err(|e| e.into())
}
/// Serialize encrypted function
pub fn serialize_encrypted<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: Serialize,
S: Serializer,
{
// 如果序列化失败,返回 None
let json = match serde_json::to_string(value) {
Ok(j) => j,
Err(_) => return serializer.serialize_none(),
};
// 如果加密失败,返回 None
match encrypt_data(&json) {
Ok(encrypted) => serializer.serialize_str(&encrypted),
Err(_) => serializer.serialize_none(),
}
}
/// Deserialize decrypted function
pub fn deserialize_encrypted<'a, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: for<'de> Deserialize<'de> + Default,
D: Deserializer<'a>,
{
// 如果反序列化字符串失败,返回默认值
let encrypted = match String::deserialize(deserializer) {
Ok(s) => s,
Err(_) => return Ok(T::default()),
};
// 如果解密失败,返回默认值
let decrypted_string = match decrypt_data(&encrypted) {
Ok(data) => data,
Err(_) => return Ok(T::default()),
};
// 如果 JSON 解析失败,返回默认值
match serde_json::from_str(&decrypted_string) {
Ok(value) => Ok(value),
Err(_) => Ok(T::default()),
}
}
+10 -7
View File
@@ -1,15 +1,18 @@
mod clash;
#[allow(clippy::module_inception)]
mod config;
mod draft;
mod encrypt;
mod prfitem;
mod profiles;
mod runtime;
mod verge;
pub use self::clash::*;
pub use self::config::*;
pub use self::draft::*;
pub use self::prfitem::*;
pub use self::profiles::*;
pub use self::runtime::*;
pub use self::verge::*;
pub use self::{
clash::*, config::*, draft::*, encrypt::*, prfitem::*, profiles::*, runtime::*, verge::*,
};
pub const DEFAULT_PAC: &str = r#"function FindProxyForURL(url, host) {
return "PROXY 127.0.0.1:%mixed-port%; SOCKS5 127.0.0.1:%mixed-port%; DIRECT;";
}
"#;
+204 -44
View File
@@ -94,6 +94,16 @@ pub struct PrfOption {
/// default is `false`
#[serde(skip_serializing_if = "Option::is_none")]
pub danger_accept_invalid_certs: Option<bool>,
pub merge: Option<String>,
pub script: Option<String>,
pub rules: Option<String>,
pub proxies: Option<String>,
pub groups: Option<String>,
}
impl PrfOption {
@@ -103,8 +113,15 @@ impl PrfOption {
a.user_agent = b.user_agent.or(a.user_agent);
a.with_proxy = b.with_proxy.or(a.with_proxy);
a.self_proxy = b.self_proxy.or(a.self_proxy);
a.danger_accept_invalid_certs = b.danger_accept_invalid_certs.or(a.danger_accept_invalid_certs);
a.danger_accept_invalid_certs = b
.danger_accept_invalid_certs
.or(a.danger_accept_invalid_certs);
a.update_interval = b.update_interval.or(a.update_interval);
a.merge = b.merge.or(a.merge);
a.script = b.script.or(a.script);
a.rules = b.rules.or(a.rules);
a.proxies = b.proxies.or(a.proxies);
a.groups = b.groups.or(a.groups);
Some(a)
}
t => t.0.or(t.1),
@@ -133,17 +150,7 @@ impl PrfItem {
"local" => {
let name = item.name.unwrap_or("Local File".into());
let desc = item.desc.unwrap_or("".into());
PrfItem::from_local(name, desc, file_data)
}
"merge" => {
let name = item.name.unwrap_or("Merge".into());
let desc = item.desc.unwrap_or("".into());
PrfItem::from_merge(name, desc)
}
"script" => {
let name = item.name.unwrap_or("Script".into());
let desc = item.desc.unwrap_or("".into());
PrfItem::from_script(name, desc)
PrfItem::from_local(name, desc, file_data, item.option)
}
typ => bail!("invalid profile item type \"{typ}\""),
}
@@ -151,10 +158,49 @@ impl PrfItem {
/// ## Local type
/// create a new item from name/desc
pub fn from_local(name: String, desc: String, file_data: Option<String>) -> Result<PrfItem> {
let uid = help::get_uid("l");
pub fn from_local(
name: String,
desc: String,
file_data: Option<String>,
option: Option<PrfOption>,
) -> Result<PrfItem> {
let uid = help::get_uid("L");
let file = format!("{uid}.yaml");
let opt_ref = option.as_ref();
let update_interval = opt_ref.and_then(|o| o.update_interval);
let mut merge = opt_ref.and_then(|o| o.merge.clone());
let mut script = opt_ref.and_then(|o| o.script.clone());
let mut rules = opt_ref.and_then(|o| o.rules.clone());
let mut proxies = opt_ref.and_then(|o| o.proxies.clone());
let mut groups = opt_ref.and_then(|o| o.groups.clone());
if merge.is_none() {
let merge_item = PrfItem::from_merge(None)?;
Config::profiles().data().append_item(merge_item.clone())?;
merge = merge_item.uid;
}
if script.is_none() {
let script_item = PrfItem::from_script(None)?;
Config::profiles().data().append_item(script_item.clone())?;
script = script_item.uid;
}
if rules.is_none() {
let rules_item = PrfItem::from_rules()?;
Config::profiles().data().append_item(rules_item.clone())?;
rules = rules_item.uid;
}
if proxies.is_none() {
let proxies_item = PrfItem::from_proxies()?;
Config::profiles()
.data()
.append_item(proxies_item.clone())?;
proxies = proxies_item.uid;
}
if groups.is_none() {
let groups_item = PrfItem::from_groups()?;
Config::profiles().data().append_item(groups_item.clone())?;
groups = groups_item.uid;
}
Ok(PrfItem {
uid: Some(uid),
itype: Some("local".into()),
@@ -164,7 +210,15 @@ impl PrfItem {
url: None,
selected: None,
extra: None,
option: None,
option: Some(PrfOption {
update_interval,
merge,
script,
rules,
proxies,
groups,
..PrfOption::default()
}),
home: None,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(file_data.unwrap_or(tmpl::ITEM_LOCAL.into())),
@@ -180,12 +234,17 @@ impl PrfItem {
option: Option<PrfOption>,
) -> Result<PrfItem> {
let opt_ref = option.as_ref();
let with_proxy = opt_ref.map_or(false, |o| o.with_proxy.unwrap_or(false));
let self_proxy = opt_ref.map_or(false, |o| o.self_proxy.unwrap_or(false));
let accept_invalid_certs = opt_ref.map_or(false, |o| o.danger_accept_invalid_certs.unwrap_or(false));
let with_proxy = opt_ref.is_some_and(|o| o.with_proxy.unwrap_or(false));
let self_proxy = opt_ref.is_some_and(|o| o.self_proxy.unwrap_or(false));
let accept_invalid_certs =
opt_ref.is_some_and(|o| o.danger_accept_invalid_certs.unwrap_or(false));
let user_agent = opt_ref.and_then(|o| o.user_agent.clone());
let update_interval = opt_ref.and_then(|o| o.update_interval);
let mut merge = opt_ref.and_then(|o| o.merge.clone());
let mut script = opt_ref.and_then(|o| o.script.clone());
let mut rules = opt_ref.and_then(|o| o.rules.clone());
let mut proxies = opt_ref.and_then(|o| o.proxies.clone());
let mut groups = opt_ref.and_then(|o| o.groups.clone());
let mut builder = reqwest::ClientBuilder::new().use_rustls_tls().no_proxy();
// 使用软件自己的代理
@@ -279,17 +338,11 @@ impl PrfItem {
crate::utils::help::get_last_part_and_decode(url).unwrap_or("Remote File".into()),
),
};
let option = match update_interval {
Some(val) => Some(PrfOption {
update_interval: Some(val),
..PrfOption::default()
}),
let update_interval = match update_interval {
Some(val) => Some(val),
None => match header.get("profile-update-interval") {
Some(value) => match value.to_str().unwrap_or("").parse::<u64>() {
Ok(val) => Some(PrfOption {
update_interval: Some(val * 60), // hour -> min
..PrfOption::default()
}),
Ok(val) => Some(val * 60), // hour -> min
Err(_) => None,
},
None => None,
@@ -300,11 +353,11 @@ impl PrfItem {
Some(value) => {
let str_value = value.to_str().unwrap_or("");
Some(str_value.to_string())
},
}
None => None,
};
let uid = help::get_uid("r");
let uid = help::get_uid("R");
let file = format!("{uid}.yaml");
let name = name.unwrap_or(filename.unwrap_or("Remote File".into()));
let data = resp.text_with_charset("utf-8").await?;
@@ -320,6 +373,34 @@ impl PrfItem {
bail!("profile does not contain `proxies` or `proxy-providers`");
}
if merge.is_none() {
let merge_item = PrfItem::from_merge(None)?;
Config::profiles().data().append_item(merge_item.clone())?;
merge = merge_item.uid;
}
if script.is_none() {
let script_item = PrfItem::from_script(None)?;
Config::profiles().data().append_item(script_item.clone())?;
script = script_item.uid;
}
if rules.is_none() {
let rules_item = PrfItem::from_rules()?;
Config::profiles().data().append_item(rules_item.clone())?;
rules = rules_item.uid;
}
if proxies.is_none() {
let proxies_item = PrfItem::from_proxies()?;
Config::profiles()
.data()
.append_item(proxies_item.clone())?;
proxies = proxies_item.uid;
}
if groups.is_none() {
let groups_item = PrfItem::from_groups()?;
Config::profiles().data().append_item(groups_item.clone())?;
groups = groups_item.uid;
}
Ok(PrfItem {
uid: Some(uid),
itype: Some("remote".into()),
@@ -329,7 +410,15 @@ impl PrfItem {
url: Some(url.into()),
selected: None,
extra,
option,
option: Some(PrfOption {
update_interval,
merge,
script,
rules,
proxies,
groups,
..PrfOption::default()
}),
home,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(data.into()),
@@ -338,15 +427,20 @@ impl PrfItem {
/// ## Merge type (enhance)
/// create the enhanced item by using `merge` rule
pub fn from_merge(name: String, desc: String) -> Result<PrfItem> {
let uid = help::get_uid("m");
let file = format!("{uid}.yaml");
pub fn from_merge(uid: Option<String>) -> Result<PrfItem> {
let mut id = help::get_uid("m");
let mut template = tmpl::ITEM_MERGE_EMPTY.into();
if let Some(uid) = uid {
id = uid;
template = tmpl::ITEM_MERGE.into();
}
let file = format!("{id}.yaml");
Ok(PrfItem {
uid: Some(uid),
uid: Some(id),
itype: Some("merge".into()),
name: Some(name),
desc: Some(desc),
name: None,
desc: None,
file: Some(file),
url: None,
selected: None,
@@ -354,21 +448,24 @@ impl PrfItem {
option: None,
home: None,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(tmpl::ITEM_MERGE.into()),
file_data: Some(template),
})
}
/// ## Script type (enhance)
/// create the enhanced item by using javascript quick.js
pub fn from_script(name: String, desc: String) -> Result<PrfItem> {
let uid = help::get_uid("s");
let file = format!("{uid}.js"); // js ext
pub fn from_script(uid: Option<String>) -> Result<PrfItem> {
let mut id = help::get_uid("s");
if let Some(uid) = uid {
id = uid;
}
let file = format!("{id}.js"); // js ext
Ok(PrfItem {
uid: Some(uid),
uid: Some(id),
itype: Some("script".into()),
name: Some(name),
desc: Some(desc),
name: None,
desc: None,
file: Some(file),
url: None,
home: None,
@@ -380,6 +477,69 @@ impl PrfItem {
})
}
/// ## Rules type (enhance)
pub fn from_rules() -> Result<PrfItem> {
let uid = help::get_uid("r");
let file = format!("{uid}.yaml"); // yaml ext
Ok(PrfItem {
uid: Some(uid),
itype: Some("rules".into()),
name: None,
desc: None,
file: Some(file),
url: None,
home: None,
selected: None,
extra: None,
option: None,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(tmpl::ITEM_RULES.into()),
})
}
/// ## Proxies type (enhance)
pub fn from_proxies() -> Result<PrfItem> {
let uid = help::get_uid("p");
let file = format!("{uid}.yaml"); // yaml ext
Ok(PrfItem {
uid: Some(uid),
itype: Some("proxies".into()),
name: None,
desc: None,
file: Some(file),
url: None,
home: None,
selected: None,
extra: None,
option: None,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(tmpl::ITEM_PROXIES.into()),
})
}
/// ## Groups type (enhance)
pub fn from_groups() -> Result<PrfItem> {
let uid = help::get_uid("g");
let file = format!("{uid}.yaml"); // yaml ext
Ok(PrfItem {
uid: Some(uid),
itype: Some("groups".into()),
name: None,
desc: None,
file: Some(file),
url: None,
home: None,
selected: None,
extra: None,
option: None,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(tmpl::ITEM_GROUPS.into()),
})
}
/// get the file data
pub fn read_file(&self) -> Result<String> {
if self.file.is_none() {
+207 -17
View File
@@ -1,4 +1,4 @@
use super::prfitem::PrfItem;
use super::{prfitem::PrfItem, PrfOption};
use crate::utils::{dirs, help};
use anyhow::{bail, Context, Result};
use serde::{Deserialize, Serialize};
@@ -11,9 +11,6 @@ pub struct IProfiles {
/// same as PrfConfig.current
pub current: Option<String>,
/// same as PrfConfig.chain
pub chain: Option<Vec<String>>,
/// profile list
pub items: Option<Vec<PrfItem>>,
}
@@ -74,16 +71,11 @@ impl IProfiles {
if let Some(current) = patch.current {
let items = self.items.as_ref().unwrap();
let some_uid = Some(current);
if items.iter().any(|e| e.uid == some_uid) {
self.current = some_uid;
}
}
if let Some(chain) = patch.chain {
self.chain = Some(chain);
}
Ok(())
}
@@ -118,6 +110,7 @@ impl IProfiles {
if item.uid.is_none() {
bail!("the uid should not be null");
}
let uid = item.uid.clone();
// save the file data
// move the field value after save
@@ -135,6 +128,12 @@ impl IProfiles {
.with_context(|| format!("failed to write to file \"{}\"", file))?;
}
if self.current.is_none()
&& (item.itype == Some("remote".to_string()) || item.itype == Some("local".to_string()))
{
self.current = uid;
}
if self.items.is_none() {
self.items = Some(vec![]);
}
@@ -142,6 +141,7 @@ impl IProfiles {
if let Some(items) = self.items.as_mut() {
items.push(item)
}
self.save_file()
}
@@ -212,6 +212,7 @@ impl IProfiles {
each.extra = item.extra;
each.updated = item.updated;
each.home = item.home;
each.option = PrfOption::merge(each.option.clone(), item.option);
// save the file data
// move the field value after save
if let Some(file_data) = item.file_data.take() {
@@ -243,9 +244,19 @@ impl IProfiles {
pub fn delete_item(&mut self, uid: String) -> Result<bool> {
let current = self.current.as_ref().unwrap_or(&uid);
let current = current.clone();
let item = self.get_item(&uid)?;
let merge_uid = item.option.as_ref().and_then(|e| e.merge.clone());
let script_uid = item.option.as_ref().and_then(|e| e.script.clone());
let rules_uid = item.option.as_ref().and_then(|e| e.rules.clone());
let proxies_uid = item.option.as_ref().and_then(|e| e.proxies.clone());
let groups_uid = item.option.as_ref().and_then(|e| e.groups.clone());
let mut items = self.items.take().unwrap_or_default();
let mut index = None;
let mut merge_index = None;
let mut script_index = None;
let mut rules_index = None;
let mut proxies_index = None;
let mut groups_index = None;
// get the index
for (i, _) in items.iter().enumerate() {
@@ -254,7 +265,6 @@ impl IProfiles {
break;
}
}
if let Some(index) = index {
if let Some(file) = items.remove(index).file {
let _ = dirs::app_profiles_dir().map(|path| {
@@ -265,13 +275,102 @@ impl IProfiles {
});
}
}
// get the merge index
for (i, _) in items.iter().enumerate() {
if items[i].uid == merge_uid {
merge_index = Some(i);
break;
}
}
if let Some(index) = merge_index {
if let Some(file) = items.remove(index).file {
let _ = dirs::app_profiles_dir().map(|path| {
let path = path.join(file);
if path.exists() {
let _ = fs::remove_file(path);
}
});
}
}
// get the script index
for (i, _) in items.iter().enumerate() {
if items[i].uid == script_uid {
script_index = Some(i);
break;
}
}
if let Some(index) = script_index {
if let Some(file) = items.remove(index).file {
let _ = dirs::app_profiles_dir().map(|path| {
let path = path.join(file);
if path.exists() {
let _ = fs::remove_file(path);
}
});
}
}
// get the rules index
for (i, _) in items.iter().enumerate() {
if items[i].uid == rules_uid {
rules_index = Some(i);
break;
}
}
if let Some(index) = rules_index {
if let Some(file) = items.remove(index).file {
let _ = dirs::app_profiles_dir().map(|path| {
let path = path.join(file);
if path.exists() {
let _ = fs::remove_file(path);
}
});
}
}
// get the proxies index
for (i, _) in items.iter().enumerate() {
if items[i].uid == proxies_uid {
proxies_index = Some(i);
break;
}
}
if let Some(index) = proxies_index {
if let Some(file) = items.remove(index).file {
let _ = dirs::app_profiles_dir().map(|path| {
let path = path.join(file);
if path.exists() {
let _ = fs::remove_file(path);
}
});
}
}
// get the groups index
for (i, _) in items.iter().enumerate() {
if items[i].uid == groups_uid {
groups_index = Some(i);
break;
}
}
if let Some(index) = groups_index {
if let Some(file) = items.remove(index).file {
let _ = dirs::app_profiles_dir().map(|path| {
let path = path.join(file);
if path.exists() {
let _ = fs::remove_file(path);
}
});
}
}
// delete the original uid
if current == uid {
self.current = match !items.is_empty() {
true => items[0].uid.clone(),
false => None,
};
self.current = None;
for item in items.iter() {
if item.itype == Some("remote".to_string())
|| item.itype == Some("local".to_string())
{
self.current = item.uid.clone();
break;
}
}
}
self.items = Some(items);
@@ -288,11 +387,102 @@ impl IProfiles {
Some(file) => dirs::app_profiles_dir()?.join(file),
None => bail!("failed to get the file field"),
};
return help::read_merge_mapping(&file_path);
return help::read_mapping(&file_path);
}
bail!("failed to find the current profile \"uid:{current}\"");
}
_ => Ok(Mapping::new()),
}
}
/// 获取current指向的订阅的merge
pub fn current_merge(&self) -> Option<String> {
match (self.current.as_ref(), self.items.as_ref()) {
(Some(current), Some(items)) => {
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
let merge = item.option.as_ref().and_then(|e| e.merge.clone());
return merge;
}
None
}
_ => None,
}
}
/// 获取current指向的订阅的script
pub fn current_script(&self) -> Option<String> {
match (self.current.as_ref(), self.items.as_ref()) {
(Some(current), Some(items)) => {
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
let script = item.option.as_ref().and_then(|e| e.script.clone());
return script;
}
None
}
_ => None,
}
}
/// 获取current指向的订阅的rules
pub fn current_rules(&self) -> Option<String> {
match (self.current.as_ref(), self.items.as_ref()) {
(Some(current), Some(items)) => {
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
let rules = item.option.as_ref().and_then(|e| e.rules.clone());
return rules;
}
None
}
_ => None,
}
}
/// 获取current指向的订阅的proxies
pub fn current_proxies(&self) -> Option<String> {
match (self.current.as_ref(), self.items.as_ref()) {
(Some(current), Some(items)) => {
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
let proxies = item.option.as_ref().and_then(|e| e.proxies.clone());
return proxies;
}
None
}
_ => None,
}
}
/// 获取current指向的订阅的groups
pub fn current_groups(&self) -> Option<String> {
match (self.current.as_ref(), self.items.as_ref()) {
(Some(current), Some(items)) => {
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
let groups = item.option.as_ref().and_then(|e| e.groups.clone());
return groups;
}
None
}
_ => None,
}
}
/// 判断profile是否是current指向的
pub fn is_current_profile_index(&self, index: String) -> bool {
self.current == Some(index)
}
/// 获取所有的profiles(uid,名称)
pub fn all_profile_uid_and_name(&self) -> Option<Vec<(String, String)>> {
self.items.as_ref().map(|items| {
items
.iter()
.filter_map(|e| {
if let (Some(uid), Some(name)) = (e.uid.clone(), e.name.clone()) {
Some((uid, name))
} else {
None
}
})
.collect()
})
}
}

Some files were not shown because too many files have changed in this diff Show More