Compare commits

...
526 Commits
Author SHA1 Message Date
MystiPanda ed421445e9 Release 1.5.5 2024-03-09 22:02:36 +08:00
AmnesiashandMystiPanda 0cda07106b refactor: Upgrade to the new UI (#521)
Co-authored-by: MystiPanda <mystipanda@proton.me>
2024-03-09 21:37:21 +08:00
wonfen f335941b62 UI: change paste icon, delete default profile name 2024-03-09 02:34:33 +08:00
Pylogmon f0719f8bde feat: Merge Providers (#508) 2024-03-08 11:37:52 +08:00
MystiPanda 27fe28661c feat: Add animation for provider update 2024-03-02 13:52:48 +08:00
MystiPanda bb13b12de6 chore: fix style 2024-02-28 18:25:50 +08:00
MystiPanda 1117e28b2e feat: Try to support touch drag 2024-02-27 23:41:52 +08:00
MystiPanda 0f48873c25 chore: Remove unnecessary hotkey 2024-02-27 11:18:52 +08:00
MystiPanda faf61b4af3 fix: Image display failed on Linux 2024-02-26 13:31:51 +08:00
Cyenoch e29ce93d32 Feat: Provide a switch for allowing invalid certificates (#450) 2024-02-25 16:07:06 +08:00
MystiPanda 38ab68492e chore: change style 2024-02-24 15:39:29 +08:00
MystiPanda 53b1576e5f Release 1.5.4 2024-02-24 14:00:01 +08:00
MystiPanda 2835e79973 feat: Support Open/Close Dashboard Hotkey
#439
2024-02-24 13:39:30 +08:00
MystiPanda 8e4d7e989b feat: Show current proxy for group node
#444
2024-02-24 13:09:53 +08:00
MystiPanda 603c6b826c feat: allow disable group icon 2024-02-24 12:38:17 +08:00
MystiPanda 51bcc77cb3 refactor: Optimize implementation of Custom tray icon 2024-02-24 11:25:22 +08:00
MystiPanda a30d07b924 feat: Support Custom Tray Icon 2024-02-24 00:52:21 +08:00
MystiPanda 11faa5fe39 Release 1.5.3 2024-02-22 00:23:56 +08:00
MystiPanda ce8383b26f feat: add reset button 2024-02-22 00:19:45 +08:00
MystiPanda bd8f07c90a fix: Do not set autolaunch at init
#423 #424
2024-02-21 23:50:50 +08:00
MystiPanda fee077bebd chore: rm -m arg 2024-02-21 23:38:01 +08:00
MystiPanda 558f4d93ba chore: change default value of dns hijack 2024-02-21 16:40:47 +08:00
MystiPanda e77d126349 chore: change default value of strict route 2024-02-21 11:32:51 +08:00
MystiPanda cd8667d6de chore: fix placeholder 2024-02-21 11:13:28 +08:00
MystiPanda 8a2d06d010 chore: Add Translation 2024-02-21 11:06:32 +08:00
MystiPanda 92741fc733 feat: Disable system stack when service mode is turned off 2024-02-21 10:52:03 +08:00
MystiPanda 1cff162649 fix: Config data display error
#417
2024-02-21 10:02:28 +08:00
MystiPanda f59be465ea Release 1.5.2 2024-02-21 00:04:11 +08:00
MystiPanda 27305317ce chore: Auto Update config.yml 2024-02-20 23:54:02 +08:00
MystiPanda 8378320e50 feat: Support Tun Config (#416) 2024-02-20 23:27:03 +08:00
MystiPanda 86a69952db chore: Update patch file 2024-02-20 18:39:36 +08:00
MystiPanda e212ebfdb9 fix: Allow program run with administrator to start at startup 2024-02-20 18:35:39 +08:00
MystiPanda f235125d48 chore: add tooltip for tun mode 2024-02-19 18:10:10 +08:00
MystiPanda 8efa815eb9 feat: Support custom delay timeout (#397) 2024-02-18 11:11:22 +08:00
MystiPanda e54d42576b build: Try restart windows service after install (#395) 2024-02-18 10:19:54 +08:00
MystiPanda 1e18539862 fix: Merge profile unexpect behavior 2024-02-17 15:58:52 +08:00
MystiPanda 673fdf4721 ci: Update Release Note 2024-02-15 20:05:00 +08:00
MystiPanda 57d6bfba51 ci: change tag 2024-02-15 19:45:37 +08:00
MystiPanda 8fa23aecda chore: update ci script 2024-02-15 19:14:35 +08:00
MystiPanda f9c10533e8 chore: Change CI Name 2024-02-15 18:45:13 +08:00
MystiPanda c891c3723e ci: Support alpha package 2024-02-15 18:43:20 +08:00
MystiPanda a0625ef2e7 chore: remove 32bit package 2024-02-15 18:22:15 +08:00
MystiPanda b2774abb54 chore: default show proxy detials 2024-02-15 18:17:21 +08:00
wonfen fa089bbc40 doc: remove 32bit package 2024-02-12 02:25:18 +08:00
MystiPanda 8437d1763f fix: build error 2024-02-11 20:51:00 +08:00
MystiPanda 0532b0f9df Release v1.5.1 2024-02-11 20:32:25 +08:00
MystiPanda 5f8eb853f0 style: clear script 2024-02-11 20:03:14 +08:00
MystiPanda 9125a85382 fix: Custom GLOBAL group display error 2024-02-11 18:27:27 +08:00
MystiPanda 5b3dc44c72 feat: Show proxies count for provider 2024-02-10 13:13:27 +08:00
MystiPanda ee5147f111 feat: Save window maximize state 2024-02-10 12:51:30 +08:00
MystiPanda f67137fd5e style: change config name
#350
2024-02-08 10:23:54 +08:00
MystiPanda b67db4a896 fix: Script profile invalid
#347
2024-02-06 08:55:44 +08:00
MystiPanda 067018828c Release 1.5.0 2024-02-06 00:41:12 +08:00
MystiPanda e990530ada refactor: Remove clash field filter 2024-02-05 17:28:36 +08:00
wonfen 45f863a72a chore: enable clash field TLS fingerprint 2024-02-04 13:07:25 +08:00
MystiPanda 519febbeb2 fix: Try to fix traffic parse error
#337
2024-02-04 10:24:37 +08:00
wonfen 854b1a4caf chore: update changelog 2024-02-03 16:14:26 +08:00
MystiPanda 08eb95e73a fix: Copy Env Type Select Error 2024-02-02 17:57:56 +08:00
MystiPanda 66f8fbf08c chore: typo 2024-02-02 17:47:28 +08:00
MystiPanda 86a30bbb29 chore: Update Changelog 2024-02-02 17:34:15 +08:00
MystiPanda 966c453c27 fix: prevent_exit 2024-02-02 17:26:31 +08:00
MystiPanda c7bcaf0193 Release 1.4.11 2024-02-02 16:56:39 +08:00
MystiPanda 0ac91e36a0 fix: exit_app event 2024-02-02 16:32:19 +08:00
MystiPanda 6f1299ce9e chore: fix styles 2024-02-02 16:15:23 +08:00
MystiPanda 83ca29d649 chore: Remove prevent_close 2024-02-02 15:57:03 +08:00
MystiPanda d0206044dc fix: Fix Nisi Error 2024-02-02 15:53:28 +08:00
ycjcl868 d4623e4175 chore: unused clash core (#284) 2024-01-23 11:22:15 +08:00
MystiPanda 68ef03377d chore: fix style 2024-01-21 13:49:07 +08:00
MystiPanda d3f9d3d033 revert: some style 2024-01-21 13:49:07 +08:00
wonfen a6ccd04471 Sytle: UI improvement 2024-01-20 16:05:01 +08:00
Lai Zn 0fd8174e77 fix: Solve the confliction issues on auto updater failure (#273) 2024-01-20 13:03:48 +08:00
MystiPanda ebc63f479a revert: Support both registry and api for windows sysproxy 2024-01-20 12:16:46 +08:00
MystiPanda 570c39c20a build: Update lock file 2024-01-18 22:34:14 +08:00
MystiPanda 1d57257cf5 Release v1.4.10 2024-01-18 22:26:23 +08:00
MystiPanda 82d7baee0b feat: Add exit button on setting page 2024-01-18 22:19:14 +08:00
MystiPanda 1d91e1f1f7 build: Update Depends 2024-01-18 16:26:38 +08:00
MystiPanda 15e8894614 feat: Support Custom Start Page 2024-01-18 15:37:02 +08:00
Lai Zn 0ae96918e9 feat: Use url path name as fallback subscription name (#255) 2024-01-18 14:36:37 +08:00
MystiPanda e970880059 feat: Show SubInfo for Proxy Provider
#211
2024-01-18 14:26:57 +08:00
Lai Zn 69ae86aba8 fix: make port change set to system proxy immediately (#256) 2024-01-18 09:37:46 +08:00
MystiPanda 8142e1be49 fix: use nanoid to compatible with old devices 2024-01-18 01:16:39 +08:00
MystiPanda cbc7612451 feat: Optimizing Provider Support 2024-01-18 01:02:30 +08:00
Kiri 09e6a7b7cc feat: Add support for loong64 (#246) 2024-01-17 22:30:51 +08:00
Lai Zn 082e35668a docs: Add guidelines for windows development (#250) 2024-01-17 19:02:39 +08:00
MystiPanda c9e78c837b fix: Do not use proxy when test on tun mode 2024-01-17 18:08:09 +08:00
MystiPanda 2d171db672 Release v1.4.9 2024-01-17 15:43:15 +08:00
MystiPanda a8b11abec8 feat: Support Startup Script 2024-01-17 15:06:16 +08:00
MystiPanda b08333dccd feat: Support proxy group icon 2024-01-17 13:32:56 +08:00
MystiPanda 98bad48971 fix: Fix connection table sort error
#108
2024-01-17 12:54:54 +08:00
MystiPanda 53375bb536 chore: Update I18n 2024-01-17 11:30:19 +08:00
MystiPanda 88798093e1 fix: csp error 2024-01-17 11:08:14 +08:00
MystiPanda 45a28751af feat: Add Test Page 2024-01-17 11:02:17 +08:00
MystiPanda 1670c44464 fix: fix column width 2024-01-16 19:02:38 +08:00
MystiPanda a286ac85dc Release v1.4.8 2024-01-16 15:36:04 +08:00
MystiPanda 1606adc0ab feat: Support both registry and api for windows sysproxy 2024-01-16 15:11:53 +08:00
MystiPanda b4ce557d92 fix: Can not use specify update time when create profile 2024-01-16 10:29:04 +08:00
MystiPanda 2ba7fff8d4 refactor: rm theme blur 2024-01-15 17:13:55 +08:00
MystiPanda 98efc93610 Revert Use Tauri Http Api 2024-01-15 10:18:04 +08:00
MystiPanda 2680507eae Revert Use Tauri Websocket 2024-01-15 10:17:00 +08:00
MystiPanda df72392ad2 fix: Fix connections sort issue and add total traffic info 2024-01-15 00:46:19 +08:00
MystiPanda a6acb15a00 refactor: Use Tauri Http API 2024-01-14 19:35:03 +08:00
MystiPanda ba7242a815 refactor: Use Tauri WebSocket 2024-01-14 17:30:18 +08:00
MystiPanda 99740c1324 fix patch error 2024-01-11 14:47:25 +08:00
MystiPanda c04b20d1fa Release v1.4.7 2024-01-11 13:19:05 +08:00
MystiPanda e127067878 feat: Disable updater for portable 2024-01-11 12:44:30 +08:00
MystiPanda 6f03b72368 feat: Support hide group
#214
2024-01-11 12:34:05 +08:00
Morris Li 7bd3ee9340 Fix expired Tauri domain (#222) 2024-01-11 10:17:56 +08:00
MystiPanda c94db606e5 build: Use old sysproxy 2024-01-10 19:24:14 +08:00
MystiPanda 4d2474226b refactor: cargo clippy 2024-01-10 17:36:35 +08:00
MystiPanda 1ffc4f538b refactor: Optimizing the implementation of Linux URL Scheme registration 2024-01-10 16:34:35 +08:00
MystiPanda 6702ac957b fix: resolve scheme error 2024-01-10 15:37:40 +08:00
MystiPanda ba42b2e77d fix: linux build error 2024-01-10 15:22:08 +08:00
MystiPanda fba18ca40a feat: Support URL Scheme for MacOS 2024-01-10 14:04:06 +08:00
MystiPanda 3a2a7a1476 feat: Support URL Scheme for Linux 2024-01-10 13:03:34 +08:00
MystiPanda 3c6d6b90bc build: fix macos build script 2024-01-09 22:49:11 +08:00
MystiPanda 47dc9ee304 fix patch error 2024-01-09 22:25:25 +08:00
MystiPanda 0045bf206c Release v1.4.6 2024-01-09 22:17:29 +08:00
MystiPanda 669a1a6953 feat: Support URL Scheme for Windows
#165
2024-01-09 21:57:06 +08:00
MystiPanda e28452cc7b feat: Optimize control button style 2024-01-09 16:04:56 +08:00
MystiPanda 5ceac03db1 feat: Add pin button 2024-01-09 15:13:59 +08:00
MystiPanda 96215e5950 fix: Fix some compile error 2024-01-09 14:52:43 +08:00
wonfen 7a030b9224 Style: UI improvement & 1.4.6 ready 2024-01-09 13:57:53 +08:00
wonfen e743478a4d chore: UI adjustment 2024-01-08 19:32:21 +08:00
MystiPanda bc29c80d44 chore: Use Latest and compatible core 2023-12-23 12:09:19 +08:00
MystiPanda 82b8a474d7 fix: Cargo clippy 2023-12-21 16:49:21 +08:00
MystiPanda 99851b297d fix: Get filename error
#165
2023-12-19 19:52:13 +08:00
MystiPanda e3a500e12c fix: portable flag 2023-12-15 21:39:34 +08:00
MystiPanda 378666e3cc chore: update service url 2023-12-15 20:38:17 +08:00
MystiPanda b65ad1ebd7 fix: user-agent version error 2023-12-15 15:18:01 +08:00
MystiPanda 933a821b5f fix: Subinfo parse error 2023-12-15 11:35:10 +08:00
MystiPanda 159337ddbd Release 1.4.5 2023-12-14 17:40:48 +08:00
MystiPanda 2e25a4333a fix: Window control button icon issue
#136
2023-12-14 14:59:55 +08:00
MystiPanda 64fafb0795 chore: Remove unsafe function 2023-12-14 13:56:51 +08:00
MystiPanda 5927e0bb3b fix: icon size 2023-12-14 13:20:01 +08:00
MystiPanda e21596db83 feat: Update MacOS tray icon 2023-12-14 13:11:46 +08:00
MystiPanda 8a608c3c3e chore: Optimize service path 2023-12-14 13:03:52 +08:00
MystiPanda 76f9db8516 chore: Optimize upgrade process 2023-12-14 11:24:26 +08:00
MystiPanda 90f4809b7c chore: default enable clash field filter 2023-12-14 10:49:58 +08:00
MystiPanda 3ff1af9fd6 chore: Delete unnecessary drag area 2023-12-13 11:09:19 +08:00
MystiPanda 711dd520f7 chore: Change service path 2023-12-13 10:59:07 +08:00
MystiPanda 99503c836a fix: Save wrong window size 2023-12-12 16:42:19 +08:00
MystiPanda 2c8bc51862 Revert "fix: Change PID file path"
This reverts commit d64bdf02de.
2023-12-12 15:59:00 +08:00
MystiPanda 5e7aa15232 fix: Can't switch env type 2023-12-11 10:51:59 +08:00
MystiPanda d0761869b6 build: fix update issue 2023-12-10 22:24:46 +08:00
MystiPanda bb99b89228 build: fix patch 2023-12-10 21:40:57 +08:00
MystiPanda 7b88beb0b5 v1.4.4 2023-12-10 21:36:22 +08:00
MystiPanda 82c630bd0e feat: Support windows aarch64 (#112) 2023-12-10 20:45:27 +08:00
MystiPanda bb4f11e1b8 feat: Patch for windows aarch64 2023-12-10 19:50:01 +08:00
MystiPanda 3182d20b7c build: Update Depends 2023-12-10 17:14:48 +08:00
MystiPanda a6f15e2474 chore: Hide script mode for alpha core 2023-12-10 16:31:55 +08:00
MystiPanda 0c3f03680c Support upgrade alpha core 2023-12-10 15:57:10 +08:00
MystiPanda a66e8dfc9f feat: Support update geodata
#37
2023-12-10 15:22:04 +08:00
zclkkk 556232aac4 chore: Allow user to choose where to install (#110) 2023-12-10 09:50:43 +08:00
MystiPanda 7fcfaadd91 chore: Remove script mode 2023-12-09 17:04:03 +08:00
MystiPanda 18d388e1e2 docs: Update ISSUE_TEMPLATE 2023-12-09 12:00:01 +08:00
MystiPanda 7de6622d74 feat: Support different tray icon for macos 2023-12-09 11:22:02 +08:00
MystiPanda 9af1f90990 feat: Support different tray icon for linux 2023-12-09 11:03:19 +08:00
Pylogmon 15ab43963d feat: Optimize copy environment variable logic (#106) 2023-12-08 22:16:42 +08:00
MystiPanda 6b7465a4b0 perf: Improves window creation speed 2023-12-08 13:10:35 +08:00
MystiPanda d64bdf02de fix: Change PID file path
#99
2023-12-08 11:59:40 +08:00
MystiPanda 4a2c94993a docs: Update README 2023-12-07 16:49:59 +08:00
MystiPanda 86c318d86b feat: Add AppImage for x86 linux 2023-12-07 16:38:39 +08:00
MystiPanda bffc1ad41d ci: fix build script 2023-12-07 16:17:34 +08:00
MystiPanda 2a685c116f chore: Fix build error 2023-12-07 16:02:29 +08:00
MystiPanda 0e271d2924 ci: Fix Linux CI Script 2023-12-07 15:57:53 +08:00
MystiPanda 7127c4d590 ci: Fix Linux Build Script 2023-12-07 15:34:49 +08:00
MystiPanda a82929996b chore: change default port to 7897 2023-12-07 14:52:14 +08:00
MystiPanda 3f01f52c7b refactor: fix depends 2023-12-07 14:44:44 +08:00
MystiPanda 9c94494622 refactor: Remove unnecessary changes 2023-12-07 13:43:53 +08:00
MystiPanda c8a508f35c Revert "chore: change default port to 7897"
This reverts commit 4ec73ef1c3.
2023-12-07 13:32:49 +08:00
wonfen 4ec73ef1c3 chore: change default port to 7897 2023-12-06 03:37:16 +08:00
MystiPanda 3465b79e5b docs: Update README 2023-12-05 17:02:54 +08:00
MystiPanda 726a0a33ae docs: UPDATELOG 2023-12-05 16:47:42 +08:00
MystiPanda ba05810d80 Release 1.4.3 2023-12-05 16:28:50 +08:00
MystiPanda 10bb53e7de chore: fix appid 2023-12-05 15:41:13 +08:00
MystiPanda dd8a083546 refactor: Change app_home to standard locations of app_data 2023-12-05 15:39:44 +08:00
MystiPanda 84131a71c9 fix: Stop core before install update 2023-12-05 15:01:15 +08:00
MystiPanda 4909a11896 docs: Update README 2023-12-05 13:33:23 +08:00
MystiPanda 4069357b81 refactor: Remove page animation 2023-12-05 13:30:55 +08:00
MystiPanda e6e87bcc40 refactor: Change Tun Icon Color 2023-12-05 13:23:54 +08:00
MystiPanda 7ba22045e8 build: Dependency downgrade 2023-12-05 12:55:20 +08:00
Pylogmon d4040b61c4 refactor: Change Portable Config Path (#66) 2023-12-05 12:52:26 +08:00
Pylogmon 7534f8fc37 refactor: Hide Clash Field Option when Disable Filter (#63) 2023-12-05 08:34:57 +08:00
MystiPanda 7ced009e92 Update README.md 2023-12-04 14:51:46 +08:00
MystiPanda cc13c71e40 ci: fix updater 2023-12-04 13:51:46 +08:00
MystiPanda d2f09209a7 chore: Use Nsis 2023-12-04 13:45:28 +08:00
Pylogmon a241b04d99 fix: Open File (#56) 2023-12-04 12:15:12 +08:00
Pylogmon 4f35d907cb fix: Get Profile Filename (#54) 2023-12-04 12:15:01 +08:00
MystiPanda 9e50d144a3 chore: change linux meta core to compatible 2023-12-04 11:38:54 +08:00
wonfen 8514bb48c4 chore: change windows meta core to compatible 2023-12-04 06:00:31 +08:00
WhizPanda 3ae633a2a1 fix: alpha core can't display memory 2023-12-03 18:55:38 +08:00
WhizPanda 2799cb9118 style: fix icon size 2023-12-03 18:28:41 +08:00
WhizPanda d20346b6ac docs: Update README.md 2023-12-03 16:42:49 +08:00
Pylogmon 2f00666a68 style: improve drag icon style (#51) 2023-12-03 16:27:13 +08:00
WhizPanda 80f4570093 ci: Fix Portable Script 2023-12-03 15:36:39 +08:00
wonfen 3f0a2ba48f docs: update readme 2023-12-03 15:25:33 +08:00
WhizPanda fc90094e8a feat: Support Both Stable and Alpha Version (#47) 2023-12-03 14:26:03 +08:00
wonfen 20d580ade8 release: 1.4.2, tweak UI, fix emoji on mac 2023-12-03 14:01:53 +08:00
WhizPanda 290a024a9e chore: Adjust style 2023-12-02 23:20:04 +08:00
WhizPanda 64a283c3a6 chore: Replace Repo Name 2023-12-02 23:04:04 +08:00
WhizPanda 3e93f0ecbc build: Update Depends 2023-12-02 22:36:04 +08:00
WhizPanda b12bcc1c49 style: Improve Style 2023-12-02 16:23:53 +08:00
WhizPanda 05d7313dcc feat: Support New Clash Field
#46
2023-12-02 15:18:54 +08:00
WhizPanda c0b6e549f0 build: Update Cargo Depends 2023-12-01 15:52:49 +08:00
WhizPanda 31f9bf219e build: Update Depends 2023-12-01 14:49:32 +08:00
WhizPanda 4906ca7059 feat: support random mixed port 2023-12-01 12:56:18 +08:00
WhizPanda b963a7a0e5 fix: Fix Updater Script 2023-12-01 11:19:49 +08:00
WhizPanda 5d6dadda76 Remove unnecessary conditions 2023-12-01 11:11:20 +08:00
Pylogmon 77f69fd223 feat: Add Windows x86 and Linux armv7 Support (#44)
* feat: Add Windows x86 and Linux armv7 Support

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

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

* Add Russian support

* Minor update

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

* runfix: Add children type to FC components

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

* use package from source
2023-01-18 23:36:18 +08:00
GyDi bba03d14d4 fix: compatible with UTF8 BOM, close #283 2023-01-17 19:51:02 +08:00
GyDi 23b728a762 feat: adjust macOS window style 2023-01-16 22:57:53 +08:00
GyDi 819c5207d2 fix: use selected proxy after profile changed 2023-01-15 21:33:03 +08:00
GyDi 311358544e fix: error log 2023-01-15 19:11:51 +08:00
GyDi 4480ecc96d chore: ci 2023-01-14 22:22:18 +08:00
GyDi 6c5f70a205 v1.2.2 2023-01-14 22:01:28 +08:00
GyDi 99adfb4a9e chore: update log 2023-01-14 21:56:27 +08:00
GyDi 7909cf4067 fix: adjust fields order 2023-01-14 14:57:55 +08:00
GyDi 780ab20aeb fix: add meta fields 2023-01-14 14:51:29 +08:00
GyDi 73119bb7c5 chore: ci 2023-01-14 12:20:17 +08:00
GyDi f20f0f064e chore: update clash meta 2023-01-14 12:16:09 +08:00
GyDi 1b44ae098c fix: add os platform value 2023-01-14 12:07:31 +08:00
GyDi 453c230716 feat: recover core after panic, close #353 2023-01-14 11:45:47 +08:00
GyDi 439d885ee1 chore: allow unused 2023-01-13 23:11:48 +08:00
GyDi 43dee3ef76 fix: reconnect traffic websocket 2023-01-13 23:02:45 +08:00
GyDi c71ba6ff8d chore: update deps 2023-01-13 21:29:21 +08:00
GyDi fb7a36eb73 feat: use decorations in Linux, close #354 2023-01-12 00:42:33 +08:00
GyDi e7f294a065 fix: parse bytes precision, close #334 2023-01-11 14:05:49 +08:00
GyDi d5037f180e fix: trigger new profile dialog, close #356 2023-01-11 13:45:16 +08:00
GyDi e90158809a fix: parse log cause panic 2023-01-11 13:30:14 +08:00
GyDi 0cb802ed9a chore: update clash meta 2023-01-09 21:55:03 +08:00
GyDi d0b47204f4 v1.2.1 2022-12-23 22:44:27 +08:00
GyDi 351cb391e5 chore: update log 2022-12-23 22:44:00 +08:00
GyDi 051be927cd fix: avoid setting login item repeatedly, close #326 2022-12-23 22:39:27 +08:00
GyDi 8bad2c2113 fix: adjust code 2022-12-15 21:54:48 +08:00
GyDi 2bcf6fb3eb fix: adjust delay check concurrency 2022-12-15 12:23:57 +08:00
GyDi d1ba0ed2b2 chore: adjust code 2022-12-15 12:22:20 +08:00
GyDi 6e421e60c5 fix: change default column to auto 2022-12-14 16:56:33 +08:00
GyDi 8385050804 fix: change default app version 2022-12-14 16:15:53 +08:00
GyDi bfe4f08232 fix: adjust rule ui 2022-12-14 15:59:41 +08:00
GyDi 132f914b0d fix: adjust log ui 2022-12-14 15:49:05 +08:00
GyDi 97d82b03ab fix: keep delay data 2022-12-14 15:29:02 +08:00
GyDi f06fa3f9b7 fix: use list item button 2022-12-14 15:16:49 +08:00
GyDi 6337788a22 fix: proxy item style 2022-12-14 15:15:44 +08:00
GyDi 024db4358b feat: auto proxy layout column 2022-12-14 15:07:51 +08:00
GyDi 173f35487e chore: fix ci 2022-12-13 18:13:28 +08:00
GyDi 74cbe82dd1 chore: fix ci 2022-12-13 18:11:43 +08:00
GyDi a8c30d30a9 chore: fix ci 2022-12-13 18:02:23 +08:00
GyDi 1b7a52d5af chore: alpha ci 2022-12-13 17:44:46 +08:00
GyDi e5109789bf chore: alpha ci 2022-12-13 17:41:53 +08:00
GyDi 4d2b35e09d feat: support to change proxy layout column 2022-12-13 17:34:39 +08:00
GyDi 5c5177ec57 feat: support to open core dir 2022-12-13 00:44:24 +08:00
GyDi d93b00cd15 chore: update deps 2022-12-12 00:00:45 +08:00
MoeShin 39ade59174 fix: Virtuoso no work in legacy browsers (#318) 2022-12-08 10:47:42 +08:00
GyDi 0309c815b9 chore: update deps 2022-12-04 20:54:43 +08:00
GyDi ce613098db fix: adjust ui 2022-12-04 00:45:39 +08:00
GyDi ab34044196 feat: profile page ui 2022-11-28 22:29:58 +08:00
GyDi 17f724748f chore: deps 2022-11-28 22:27:23 +08:00
GyDi f3a917b5e7 chore: update clash version 2022-11-26 13:25:52 +08:00
GyDi 376011ea08 fix: refresh websocket 2022-11-25 22:22:57 +08:00
GyDi 2ce7624c14 fix: adjust ui 2022-11-24 18:24:34 +08:00
GyDi c62dddd5b9 feat: save some fields in the runtime config, close #292 2022-11-24 17:52:25 +08:00
GyDi 490ba9f140 chore: alpha ci 2022-11-24 11:14:03 +08:00
GyDi f76890cc56 fix: parse bytes base 1024 2022-11-24 11:11:31 +08:00
GyDi e1c8f1fed9 fix: add clash fields 2022-11-24 10:46:21 +08:00
GyDi 6e19a4ab8b chore: ci 2022-11-24 10:41:03 +08:00
GyDi b156523a7f chore: ci 2022-11-24 10:35:22 +08:00
GyDi d20b745ae5 fix: direct mode hide proxies 2022-11-24 10:35:09 +08:00
GyDi c51e9e6b2c feat: add meta feature 2022-11-24 10:26:41 +08:00
GyDi 1a31fa9067 fix: profile can not edit 2022-11-24 10:26:25 +08:00
GyDi 558b8499af chore: ci 2022-11-23 23:46:32 +08:00
188 changed files with 16154 additions and 6973 deletions
+45
View File
@@ -0,0 +1,45 @@
name: Bug report
description: Create a report to help us improve
title: "[BUG]"
labels: ["bug"]
body:
- type: textarea
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: To Reproduce
description: Steps to reproduce the behavior.
validations:
required: true
- type: dropdown
attributes:
label: Platform
options:
- Windows
- Linux
- MacOS
validations:
required: true
- type: input
attributes:
label: System Version
placeholder: "e.g. macOS 10.15.7"
validations:
required: true
- type: input
attributes:
label: Software Version
placeholder: "e.g. 1.4.3"
validations:
required: true
- type: textarea
attributes:
label: Log
description: "Log file content or screenshot"
- type: textarea
attributes:
label: Additional Information
@@ -0,0 +1,27 @@
name: Feature request
description: Suggest an idea for this project
title: "[Feature]"
labels: ["enhancement"]
body:
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
+4
View File
@@ -0,0 +1,4 @@
FROM rust:buster
COPY entrypoint.sh /entrypoint.sh
RUN chmod a+x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
+14
View File
@@ -0,0 +1,14 @@
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
@@ -0,0 +1,8 @@
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
+37
View File
@@ -0,0 +1,37 @@
#!/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 libssl-dev:arm64 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 libssl-dev:armhf 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
else
echo "Unknown target: $INPUT_TARGET" && exit 1
fi
bash .github/build-for-linux/build.sh
+129 -47
View File
@@ -1,34 +1,51 @@
name: Alpha CI
name: Alpha Build
on:
workflow_dispatch:
inputs:
debug:
type: boolean
default: false
push:
branches: [main]
tags-ignore: [updater, alpha]
permissions: write-all
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
jobs:
release:
alpha:
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
if: startsWith(github.repository, 'zzzgydi')
steps:
- name: Checkout repository
uses: actions/checkout@v2
include:
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: windows-latest
target: aarch64-pc-windows-msvc
- os: macos-latest
target: aarch64-apple-darwin
- os: macos-latest
target: x86_64-apple-darwin
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Apply Patch
if: matrix.target == 'aarch64-pc-windows-msvc'
run: |
git config --global user.email "clash-verge-rev@github.io"
git config --global user.name "clash-verge-rev"
git am patches/support-windows-aarch64.patch
- name: Init Submodule
if: matrix.target == 'aarch64-pc-windows-msvc'
run: git submodule update --init --recursive
- 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
@@ -36,33 +53,20 @@ jobs:
workspaces: src-tauri
- name: Install Node
uses: actions/setup-node@v1
uses: actions/setup-node@v4
with:
node-version: 16
node-version: "20"
- name: Install Dependencies (ubuntu only)
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
- name: Get yarn cache dir path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn Cache
uses: actions/cache@v2
id: yarn-cache
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
version: 8
run_install: false
- name: Yarn install and check
- name: Pnpm install and check
run: |
yarn install --network-timeout 1000000
yarn run check
pnpm i
pnpm check ${{ matrix.target }}
- name: Tauri build
uses: tauri-apps/tauri-action@v0
@@ -72,16 +76,94 @@ jobs:
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tagName: alpha
releaseName: "Clash Verge Alpha"
releaseBody: "Alpha Version (include debug)"
releaseName: "Clash Verge Rev Alpha"
releaseBody: "More new features are now supported."
releaseDraft: false
includeDebug: ${{ github.event.inputs.debug }}
prerelease: true
tauriScript: pnpm
args: --target ${{ matrix.target }}
- name: Portable Bundle
if: matrix.os == 'windows-latest'
run: |
yarn run portable
run: pnpm portable ${{ matrix.target }} --alpha
env:
TAG_NAME: alpha
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
alpha-for-linux:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
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 }}
with:
target: ${{ matrix.target }}
- 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: Upload Release
if: startsWith(matrix.target, 'x86_64')
uses: softprops/action-gh-release@v1
with:
tag_name: alpha
name: "Clash Verge Rev Alpha"
body: "More new features are now supported."
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/${{ matrix.target }}/release/bundle/appimage/*.AppImage*
- name: Upload Release
uses: softprops/action-gh-release@v1
with:
tag_name: alpha
name: "Clash Verge Rev Alpha"
body: "More new features are now supported."
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]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set Env
run: |
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
shell: bash
- name: Update Tag
uses: richardsimko/update-tag@v1
with:
tag_name: alpha
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
cat > release.txt << 'EOF'
## Clash Verge Rev Alpha
Created at ${{ env.BUILDTIME }}.
EOF
- 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 }}
generate_release_notes: true
-122
View File
@@ -1,122 +0,0 @@
name: Release CI
on:
push:
tags:
- v**
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
jobs:
release:
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
if: |
startsWith(github.repository, 'zzzgydi') &&
startsWith(github.ref, 'refs/tags/v')
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install Node
uses: actions/setup-node@v1
with:
node-version: 16
- name: Install Dependencies (ubuntu only)
if: startsWith(matrix.os, 'ubuntu-')
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
- name: Get yarn cache dir path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn Cache
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Yarn install and check
run: |
yarn install --network-timeout 1000000
yarn run check
- name: Tauri build
uses: tauri-apps/tauri-action@v0
# enable cache even though failed
# continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tagName: v__VERSION__
releaseName: "Clash Verge v__VERSION__"
releaseBody: "More new features are now supported."
releaseDraft: false
prerelease: true
- name: Portable Bundle
if: matrix.os == 'windows-latest'
# rebuild with env settings
run: |
yarn build
yarn run portable
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
VITE_WIN_PORTABLE: 1
release-update:
needs: release
runs-on: macos-latest
if: |
startsWith(github.repository, 'zzzgydi') &&
startsWith(github.ref, 'refs/tags/v')
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Get yarn cache dir path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn Cache
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Yarn install
run: yarn install
- name: Release updater file
run: yarn run updater
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-102
View File
@@ -1,102 +0,0 @@
name: Compatible CI
on:
workflow_dispatch:
push:
tags:
- v**
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
jobs:
build:
strategy:
fail-fast: false
matrix:
targets:
- tag: macOS-10.15
os: macos-10.15
- tag: Ubuntu18
os: ubuntu-18.04
- tag: Ubuntu22
os: ubuntu-22.04
runs-on: ${{ matrix.targets.os }}
if: startsWith(github.repository, 'zzzgydi')
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install Node
uses: actions/setup-node@v1
with:
node-version: 16
# - name: Install Dependencies (ubuntu18 only)
# if: matrix.targets.os == 'ubuntu-18.04'
# run: |
# sudo apt-get update
# sudo apt-get install -y libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libappindicator3-dev librsvg2-dev libayatana-appindicator3-dev
- name: Install Dependencies (ubuntu22 only)
if: startsWith(matrix.targets.os, 'ubuntu-')
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
- name: Get yarn cache dir path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn Cache
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Yarn install and check
run: |
yarn install --network-timeout 1000000
yarn run check
- name: Tauri build
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tagName: ${{ matrix.targets.tag }}
releaseName: "Compatible For ${{ matrix.targets.tag }}"
releaseBody: "More new features are now supported."
releaseDraft: false
prerelease: false
# - name: Portable Bundle
# if: matrix.os == 'windows-latest'
# # rebuild with env settings
# run: |
# yarn build
# yarn run portable
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
# TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
# VITE_WIN_PORTABLE: 1
+159
View File
@@ -0,0 +1,159 @@
name: Release Build
on:
workflow_dispatch:
permissions: write-all
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
jobs:
release:
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: windows-latest
target: aarch64-pc-windows-msvc
- os: macos-latest
target: aarch64-apple-darwin
- os: macos-latest
target: x86_64-apple-darwin
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Apply Patch
if: matrix.target == 'aarch64-pc-windows-msvc'
run: |
git config --global user.email "clash-verge-rev@github.io"
git config --global user.name "clash-verge-rev"
git am patches/support-windows-aarch64.patch
- name: Init Submodule
if: matrix.target == 'aarch64-pc-windows-msvc'
run: git submodule update --init --recursive
- 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
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
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:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
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:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
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 }}
with:
target: ${{ matrix.target }}
- 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: Upload Release
if: startsWith(matrix.target, 'x86_64')
uses: softprops/action-gh-release@v1
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*
- name: Upload Release
uses: softprops/action-gh-release@v1
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/deb/*.deb
release-update:
runs-on: ubuntu-latest
needs: [release, release-for-linux]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: "20"
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false
- name: Pnpm install
run: pnpm i
- name: Release updater file
run: pnpm updater
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-86
View File
@@ -1,86 +0,0 @@
name: Test CI
on:
workflow_dispatch:
inputs:
os:
description: "Runs on OS"
required: true
default: windows-latest
type: choice
options:
- windows-latest
- ubuntu-latest
- macos-latest
- ubuntu-18.04
- ubuntu-20.04
- ubuntu-22.04
- macos-10.15
- macos-11
- macos-12
- windows-2019
- windows-2022
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
jobs:
release:
runs-on: ${{ github.event.inputs.os }}
if: startsWith(github.repository, 'zzzgydi')
steps:
- name: System Version
run: |
echo ${{ github.event.inputs.os }}
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install Node
uses: actions/setup-node@v1
with:
node-version: 16
- name: Install Dependencies (ubuntu only)
if: startsWith(github.event.inputs.os, 'ubuntu-')
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
- name: Get yarn cache dir path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn Cache
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Yarn install and check
run: |
yarn install --network-timeout 1000000
yarn run check
- name: Tauri build
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
+15 -18
View File
@@ -1,32 +1,29 @@
name: Updater CI
on: workflow_dispatch
permissions: write-all
jobs:
release-update:
runs-on: macos-latest
if: startsWith(github.repository, 'zzzgydi')
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Get yarn cache dir path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn Cache
uses: actions/cache@v2
id: yarn-cache
- name: Install Node
uses: actions/setup-node@v3
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
node-version: "20"
- name: Yarn install
run: yarn install
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false
- name: Pnpm install
run: pnpm i
- name: Release updater file
run: yarn run updater
run: pnpm updater
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn pretty-quick --staged
pnpm pretty-quick --staged
+67
View File
@@ -0,0 +1,67 @@
# CONTRIBUTING
Thank you for your interest in contributing to Clash Verge Rev! This document provides guidelines and instructions to help you set up your development environment and start contributing.
## Development Setup
Before you start contributing to the project, you need to set up your development environment. Here are the steps you need to follow:
### Prerequisites
1. **Install Rust and Node.js**: Our project requires both Rust and Node.js. Please follow the instructions provided [here](https://tauri.app/v1/guides/getting-started/prerequisites) to install them on your system.
### Setup for Windows Users
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:
```shell
pnpm i
```
### Download the Clash Binary
You have two options for downloading the clash binary:
- Automatically download it via the provided script:
```shell
pnpm run check
# 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).
### Run the Development Server
To run the development server, use the following command:
```shell
pnpm dev
# If an app instance already exists, use a different command
pnpm dev:diff
```
### Build the Project
If you want to build the project, use:
```shell
pnpm build
```
## Contributing Your Changes
Once you have made your changes:
1. Fork the repository.
2. Create a new branch for your feature or bug fix.
3. Commit your changes with clear and concise commit messages.
4. Push your branch to your fork and submit a pull request to our repository.
We appreciate your contributions and look forward to your active participation in our project!
+56 -55
View File
@@ -1,82 +1,82 @@
<h1 align="center">
<img src="./src/assets/image/logo.png" alt="Clash" width="128" />
<br>
Clash Verge
Continuation of <a href="https://github.com/zzzgydi/clash-verge">Clash Verge</a>
<br>
</h1>
<h3 align="center">
A <a href="https://github.com/Dreamacro/clash">Clash</a> GUI based on <a href="https://github.com/tauri-apps/tauri">tauri</a>.
A Clash Meta GUI based on <a href="https://github.com/tauri-apps/tauri">Tauri</a>.
</h3>
## Features
- Full `clash` config supported, Partial `clash premium` config supported.
- Profiles management and enhancement (by yaml and Javascript). [Doc](https://github.com/zzzgydi/clash-verge/wiki/%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97)
- Simple UI and supports custom theme color.
- Built-in support [Clash.Meta](https://github.com/MetaCubeX/Clash.Meta) core.
- System proxy setting and guard.
## Install
Download from [release](https://github.com/zzzgydi/clash-verge/releases). Supports Windows x64, Linux x86_64 and macOS 11+
Click on the corresponding link below to download the installation package. Supports Windows (x64/x86), Linux (x64/arm64) and macOS 10.15+ (intel/apple).
[[Windows x64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.5.5/Clash.Verge_1.5.5_x64-setup.exe)]
[[Windows arm64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.5.5/Clash.Verge_1.5.5_arm64-setup.exe)]
[[macOS intel](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.5.5/Clash.Verge_1.5.5_x64.dmg)]
[[macOS apple](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.5.5/Clash.Verge_1.5.5_aarch64.dmg)]
[[Linux x64 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.5.5/clash-verge_1.5.5_amd64.AppImage)]
[[Linux x64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.5.5/clash-verge_1.5.5_amd64.deb)]
[[Linux arm64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.5.5/clash-verge_1.5.5_arm64.deb)]
Or you can build it yourself. Supports Windows, Linux and macOS 10.15+
Notes: If you could not start the app on Windows, please check that you have [Webview2](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section) installed.
## 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)
- Simple UI and supports custom theme color.
- Built-in support [Clash.Meta(mihomo)](https://github.com/MetaCubeX/mihomo) core.
- System proxy setting and guard.
#### TG Group: [@clash_verge_rev](https://t.me/clash_verge_rev)
## Promotion
[狗狗加速 —— 技术流机场 Doggygo VPN](https://狗狗加速.com)
- 高性能海外机场,免费试用,优惠套餐,解锁流媒体,全球首家支持 Hysteria 协议。
- 使用 Clash Verge 专属邀请链接注册送 3 天,每天 1G 流量免费试用:https://verge.狗狗加速.com/#/register?code=oaxsAGo6
- Clash Verge 专属 8 折优惠码: verge20 (仅有 500 份)
- 优惠套餐每月仅需 15.8 元,160G 流量,年付 8 折
- 海外团队,无跑路风险,高达 50% 返佣
- 集群负载均衡设计,高速专线(兼容老客户端),极低延迟,无视晚高峰,4K 秒开
- 全球首家 Hysteria 协议机场,现已上线更快的 `Hysteria2` 协议(Clash Verge 客户端最佳搭配)
- 解锁流媒体及 ChatGPT
- 官网:https://狗狗加速.com
## Preview
![preview](./docs/preview.gif)
### FAQ
#### 1. **macOS** "Clash Verge" is damaged and can't be opened
open the terminal and run `sudo xattr -r -d com.apple.quarantine /Applications/Clash\ Verge.app`
## Development
You should install Rust and Nodejs, see [here](https://tauri.studio/docs/getting-started/prerequisites) for more details. Then install Nodejs packages.
See [CONTRIBUTING.md](./CONTRIBUTING.md) for more details.
To run the development server, execute the following commands after all prerequisites for **Tauri** are installed:
```shell
yarn install
```
Then download the clash binary... Or you can download it from [clash premium release](https://github.com/Dreamacro/clash/releases/tag/premium) and rename it according to [tauri config](https://tauri.studio/docs/api/config/#tauri.bundle.externalBin).
```shell
yarn run check
```
Then run
```shell
yarn dev
```
Or you can build it
```shell
yarn build
pnpm i
pnpm run check
pnpm dev
```
## Todos
> This keng is a little big...
## Screenshots
<div align="center">
<img src="./docs/demo1.png" alt="demo1" width="32%" />
<img src="./docs/demo2.png" alt="demo2" width="32%" />
<img src="./docs/demo3.png" alt="demo3" width="32%" />
<img src="./docs/demo4.png" alt="demo4" width="32%" />
<img src="./docs/demo5.png" alt="demo5" width="32%" />
<img src="./docs/demo6.png" alt="demo6" width="32%" />
</div>
### Custom Theme
<div align="center">
<img src="./docs/color1.png" alt="demo1" width="16%" />
<img src="./docs/color2.png" alt="demo2" width="16%" />
<img src="./docs/color3.png" alt="demo3" width="16%" />
<img src="./docs/color4.png" alt="demo4" width="16%" />
<img src="./docs/color5.png" alt="demo5" width="16%" />
<img src="./docs/color6.png" alt="demo6" width="16%" />
</div>
## Disclaimer
This is a learning project for Rust practice.
@@ -87,11 +87,12 @@ Issue and PR welcome!
## Acknowledgement
Clash Verge was based on or inspired by these projects and so on:
Clash Verge rev was based on or inspired by these projects and so on:
- [zzzgydi/clash-verge](https://github.com/zzzgydi/clash-verge): A Clash GUI based on tauri. Supports Windows, macOS and Linux.
- [tauri-apps/tauri](https://github.com/tauri-apps/tauri): Build smaller, faster, and more secure desktop applications with a web frontend.
- [Dreamacro/clash](https://github.com/Dreamacro/clash): A rule-based tunnel in Go.
- [MetaCubeX/Clash.Meta](https://github.com/MetaCubeX/Clash.Meta): A rule-based tunnel in Go.
- [MetaCubeX/mihomo](https://github.com/MetaCubeX/mihomo): A rule-based tunnel in Go.
- [Fndroid/clash_for_windows_pkg](https://github.com/Fndroid/clash_for_windows_pkg): A Windows/macOS GUI based on Clash.
- [vitejs/vite](https://github.com/vitejs/vite): Next generation frontend tooling. It's fast!
+483
View File
@@ -1,3 +1,486 @@
## v1.5.5
### Features
- 全新 UI 界面 (by @Amnesiash)
- 提供允许无效证书的开关
- 删除不必要的快捷键
- Provider 更新添加动画
- Merge 支持 Provider
### Bugs Fixes
- Linux 下图片显示错误
---
## v1.5.4
### Features
- 支持自定义托盘图标
- 支持禁用代理组图标
- 代理组显示当前代理
- 修改 `打开面板` 快捷键为`打开/关闭面板`
---
## v1.5.3
### Features
- Tun 设置添加重置按钮
### Bugs Fixes
- Tun 设置项显示错误的问题
- 修改一些默认值
- 启动时不更改启动项设置
---
## v1.5.2
### Features
- 支持自定义延迟测试超时时间
- 优化 Tun 相关设置
### Bugs Fixes
- Merge 操作出错
- 安装后重启服务
- 修复管理员权限启动时开机启动失效的问题
---
## v1.5.1
### Features
- 保存窗口最大化状态
- Proxy Provider 显示数量
- 不再提供 32 位安装包(因为 32 位经常出现各种奇怪问题,比如 tun 模式无法开启;现在系统也几乎没有 32 位了)
### Bugs Fixes
- 优化设置项名称
- 自定义 GLOBAL 代理组时代理组显示错误的问题
---
## v1.5.0
### Features
- 删除 Clash 字段过滤功能
- 添加 socks 端口和 http 端口设置
- 升级内核到 1.18.1
### Bugs Fixes
- 修复 32 位版本无法显示流量信息的问题
---
## v1.4.11
### Break Changes
- 此版本更改了 Windows 安装包安装模式,需要卸载后手动安装,否则无法安装到正确位置
### Features
- 优化了系统代理开启的代码,解决了稀有场景下代理开启卡顿的问题
- 添加 MacOS 下的 debug 日志,以便日后调试稀有场景下 MacOS 下无法开启系统代理的问题
- MacOS 关闭 GUI 时同步杀除后台 GUI [#306](https://github.com/clash-verge-rev/clash-verge-rev/issues/306)
### Bugs Fixes
- 解决自动更新时文件占用问题
- 解决稀有场景下系统代理开启失败的问题
- 删除冗余内核代码
---
## v1.4.10
### Features
- 设置中添加退出按钮
- 支持自定义软件启动页
- 在 Proxy Provider 页面展示订阅信息
- 优化 Provider 支持
### Bugs Fixes
- 更改端口时立即重设系统代理
- 网站测试超时错误
---
## v1.4.9
### Features
- 支持启动时运行脚本
- 支持代理组显示图标
- 新增测试页面
### Bugs Fixes
- 连接页面时间排序错误
- 连接页面表格宽度优化
---
## v1.4.8
### Features
- 连接页面总流量显示
### Bugs Fixes
- 连接页面数据排序错误
- 新建订阅时设置更新间隔无效
- Windows 拨号网络无法设置系统代理
- Windows 开启/关闭系统代理延迟(使用注册表即可)
- 删除无效的背景模糊选项
---
## v1.4.7
### Features
- Windows 便携版禁用应用内更新
- 支持代理组 Hidden 选项
- 支持 URL Scheme(MacOS & Linux)
---
## v1.4.6
### Features
- 更新 Clash Meta(mihomo) 内核到 v1.18.0
- 支持 URL Scheme(暂时仅支持 Windows)
- 添加窗口置顶按钮
- UI 优化调整
### Bugs Fixes
- 修复一些编译错误
- 获取订阅名称错误
- 订阅信息解析错误
---
## v1.4.5
### Features
- 更新 MacOS 托盘图标样式(@gxx2778 贡献)
### Bugs Fixes
- Windows 下更新时无法覆盖`clash-verge-service.exe`的问题(需要卸载重装一次服务,下次更新生效)
- 窗口最大化按钮变化问题
- 窗口尺寸保存错误问题
- 复制环境变量类型无法切换问题
- 某些情况下闪退的问题
- 某些订阅无法导入的问题
---
## v1.4.4
### Features
- 支持 Windows aarch64(arm64) 版本
- 支持一键更新 GeoData
- 支持一键更新 Alpha 内核
- MacOS 支持在系统代理时显示不同的托盘图标
- Linux 支持在系统代理时显示不同的托盘图标
- 优化复制环境变量逻辑
### Bugs Fixes
- 修改 PID 文件的路径
### Performance
- 优化创建窗口的速度
---
## v1.4.3
### Break Changes
- 更改配置文件路径到标准目录(可以保证卸载时没有残留)
- 更改 appid 为 `io.github.clash-verge-rev.clash-verge-rev`
- 建议卸载旧版本后再安装新版本,该版本安装后不会使用旧版配置文件,你可以手动将旧版配置文件迁移到新版配置文件目录下
### Features
- 移除页面切换动画
- 更改 Tun 模式托盘图标颜色
- Portable 版本默认使用当前目录作为配置文件目录
- 禁用 Clash 字段过滤时隐藏 Clash 字段选项
- 优化拖拽时光标样式
### Bugs Fixes
- 修复 windows 下更新时没有关闭内核导致的更新失败的问题
- 修复打开文件报错的问题
- 修复 url 导入时无法获取中文配置名称的问题
- 修复 alpha 内核无法显示内存信息的问题
---
## v1.4.2
### Features
- update clash meta core to mihomo 1.17.0
- support both clash meta stable release and prerelease-alpha release
- fixed the problem of not being able to set the system proxy when there is a dial-up link on windows system [#833](https://github.com/zzzgydi/clash-verge/issues/833)
- support new clash field
- support random mixed port
- add windows x86 and linux armv7 support
- support disable tray click event
- add download progress for updater
- support drag to reorder the profile
- embed emoji fonts
- update depends
- improve UI style
---
## v1.4.1
### Features
- update clash meta core to newest 虚空终端(2023.11.23)
- delete clash core UI
- improve UI
- change Logo to original
---
## v1.4.0
### Features
- update clash meta core to newest 虚空终端
- delete clash core, no longer maintain
- merge Clash nyanpasu changes
- remove delay display different color
- use Meta Country.mmdb
- update dependencies
- small changes here and there
---
## v1.3.8
### Features
- update clash meta core
- add default valid keys
- adjust the delay display interval and color
### Bug Fixes
- fix connections page undefined exception
---
## v1.3.7
### Features
- update clash and clash meta core
- profiles page add paste button
- subscriptions url textfield use multi lines
- set min window size
- add check for updates buttons
- add open dashboard to the hotkey list
### Bug Fixes
- fix profiles page undefined exception
---
## v1.3.6
### Features
- add russian translation
- support to show connection detail
- support clash meta memory usage display
- support proxy provider update ui
- update geo data file from meta repo
- adjust setting page
### Bug Fixes
- center the window when it is out of screen
- use `sudo` when `pkexec` not found (Linux)
- reconnect websocket when window focus
### Notes
- The current version of the Linux installation package is built by Ubuntu 20.04 (Github Action).
---
## v1.3.5
### Features
- update clash core
### Bug Fixes
- fix blurry system tray icon (Windows)
- fix v1.3.4 wintun.dll not found (Windows)
- fix v1.3.4 clash core not found (macOS, Linux)
---
## v1.3.4
### Features
- update clash and clash meta core
- optimize traffic graph high CPU usage when window hidden
- use polkit to elevate permission (Linux)
- support app log level setting
- support copy environment variable
- overwrite resource file according to file modified
- save window size and position
### Bug Fixes
- remove fallback group select status
- enable context menu on editable element (Windows)
---
## v1.3.3
### Features
- update clash and clash meta core
- show tray icon variants in different system proxy status (Windows)
- close all connections when mode changed
### Bug Fixes
- encode controller secret into uri
- error boundary for each page
---
## v1.3.2
### Features
- update clash and clash meta core
### Bug Fixes
- fix import url issue
- fix profile undefined issue
---
## v1.3.1
### Features
- update clash and clash meta core
### Bug Fixes
- fix open url issue
- fix appimage path panic
- fix grant root permission in macOS
- fix linux system proxy default bypass
---
## v1.3.0
### Features
- update clash and clash meta
- support opening dir on tray
- support updating all profiles with one click
- support granting root permission to clash core(Linux, macOS)
- support enable/disable clash fields filter, feel free to experience the latest features of Clash Meta
### Bug Fixes
- deb add openssl depend(Linux)
- fix the AppImage auto launch path(Linux)
- fix get the default network service(macOS)
- remove the esc key listener in macOS, cmd+w instead(macOS)
- fix infinite retry when websocket error
---
## v1.2.3
### Features
- update clash
- adjust macOS window style
- profile supports UTF8 with BOM
### Bug Fixes
- fix selected proxy
- fix error log
---
## v1.2.2
### Features
- update clash meta
- recover clash core after panic
- use system window decorations(Linux)
### Bug Fixes
- flush system proxy settings(Windows)
- fix parse log panic
- fix ui bug
---
## v1.2.1
### Features
- update clash version
- proxy groups support multi columns
- optimize ui
### Bug Fixes
- fix ui websocket connection
- adjust delay check concurrency
- avoid setting login item repeatedly(macOS)
---
## v1.2.0
### Features
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 MiB

+46 -37
View File
@@ -1,7 +1,7 @@
{
"name": "clash-verge",
"version": "1.2.0",
"license": "GPL-3.0",
"version": "1.5.5",
"license": "GPL-3.0-only",
"scripts": {
"dev": "tauri dev",
"dev:diff": "tauri dev -f verge-dev",
@@ -10,57 +10,66 @@
"web:dev": "vite",
"web:build": "tsc && vite build",
"web:serve": "vite preview",
"aarch": "node scripts/aarch.mjs",
"check": "node scripts/check.mjs",
"updater": "node scripts/updater.mjs",
"publish": "node scripts/publish.mjs",
"portable": "node scripts/portable.mjs",
"prepare": "husky install"
},
"dependencies": {
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@mui/icons-material": "^5.10.9",
"@mui/material": "^5.10.13",
"@mui/x-data-grid": "^5.17.11",
"@tauri-apps/api": "^1.2.0",
"ahooks": "^3.7.2",
"axios": "^1.1.3",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@juggle/resize-observer": "^3.4.0",
"@mui/icons-material": "^5.15.5",
"@mui/lab": "5.0.0-alpha.149",
"@mui/material": "^5.15.5",
"@mui/x-data-grid": "^6.18.7",
"@tauri-apps/api": "^1.5.3",
"ahooks": "^3.7.8",
"axios": "^1.6.5",
"dayjs": "1.11.5",
"i18next": "^22.0.4",
"i18next": "^23.7.16",
"lodash-es": "^4.17.21",
"monaco-editor": "^0.34.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"nanoid": "^5.0.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^3.1.4",
"react-i18next": "^12.0.0",
"react-router-dom": "^6.4.3",
"react-virtuoso": "^3.1.3",
"recoil": "^0.7.6",
"react-hook-form": "^7.49.3",
"react-i18next": "^13.5.0",
"react-router-dom": "^6.21.2",
"react-transition-group": "^4.4.5",
"react-virtuoso": "^4.6.2",
"recoil": "^0.7.7",
"snarkdown": "^2.0.0",
"swr": "^1.3.0"
"swr": "^1.3.0",
"tar": "^6.2.0"
},
"devDependencies": {
"@actions/github": "^5.0.3",
"@tauri-apps/cli": "^1.2.0",
"@actions/github": "^5.1.1",
"@tauri-apps/cli": "^1.5.9",
"@types/fs-extra": "^9.0.13",
"@types/js-cookie": "^3.0.2",
"@types/lodash": "^4.14.180",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@vitejs/plugin-react": "^2.0.1",
"adm-zip": "^0.5.9",
"@types/js-cookie": "^3.0.6",
"@types/lodash-es": "^4.17.12",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"@types/react-transition-group": "^4.4.10",
"@vitejs/plugin-react": "^4.2.1",
"adm-zip": "^0.5.10",
"cross-env": "^7.0.3",
"fs-extra": "^10.0.0",
"fs-extra": "^11.2.0",
"https-proxy-agent": "^5.0.1",
"husky": "^7.0.0",
"node-fetch": "^3.2.6",
"prettier": "^2.7.1",
"pretty-quick": "^3.1.3",
"sass": "^1.54.0",
"typescript": "^4.7.4",
"vite": "^3.0.9",
"husky": "^7.0.4",
"node-fetch": "^3.3.2",
"prettier": "^2.8.8",
"pretty-quick": "^3.3.1",
"sass": "^1.70.0",
"typescript": "^5.3.3",
"vite": "^5.0.11",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-svgr": "^2.2.1"
"vite-plugin-svgr": "^4.2.0"
},
"prettier": {
"tabWidth": 2,
+195
View File
@@ -0,0 +1,195 @@
From 871c9a6d1ed014c93da2436a437df03734e9f76c Mon Sep 17 00:00:00 2001
From: MystiPanda <mystipanda@proton.me>
Date: Sun, 10 Dec 2023 19:47:45 +0800
Subject: [PATCH] feat: Support windows aarch64
---
.gitmodules | 3 +
src-tauri/Cargo.toml | 2 +-
src-tauri/quick-rs | 1 +
src-tauri/src/enhance/script.rs | 130 +++++++++++++++++++-------------
4 files changed, 81 insertions(+), 55 deletions(-)
create mode 100644 .gitmodules
create mode 160000 src-tauri/quick-rs
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..2eda7e4
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "src-tauri/quick-rs"]
+ path = src-tauri/quick-rs
+ url = https://github.com/clash-verge-rev/quick-rs.git
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 2f1a3be..d67f6ed 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -25,7 +25,6 @@ log4rs = "1"
nanoid = "0.4"
chrono = "0.4"
sysinfo = "0.30"
-rquickjs = "0.3" # 高版本不支持 Linux aarch64
serde_json = "1.0"
serde_yaml = "0.9"
once_cell = "1.18"
@@ -33,6 +32,7 @@ port_scanner = "0.1.5"
delay_timer = "0.11.5"
parking_lot = "0.12"
percent-encoding = "2.3.1"
+quick-rs = { path = "quick-rs" }
window-shadows = { version = "0.2" }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
diff --git a/src-tauri/quick-rs b/src-tauri/quick-rs
new file mode 160000
index 0000000..78277c4
--- /dev/null
+++ b/src-tauri/quick-rs
@@ -0,0 +1 @@
+Subproject commit 78277c4509c64f18c0fc5c9f2b84671de7c83343
diff --git a/src-tauri/src/enhance/script.rs b/src-tauri/src/enhance/script.rs
index 30a922f..d47dc33 100644
--- a/src-tauri/src/enhance/script.rs
+++ b/src-tauri/src/enhance/script.rs
@@ -3,61 +3,83 @@ use anyhow::Result;
use serde_yaml::Mapping;
pub fn use_script(script: String, config: Mapping) -> Result<(Mapping, Vec<(String, String)>)> {
- use rquickjs::{function::Func, Context, Runtime};
- use std::sync::{Arc, Mutex};
-
- let runtime = Runtime::new().unwrap();
- let context = Context::full(&runtime).unwrap();
- let outputs = Arc::new(Mutex::new(vec![]));
-
- let copy_outputs = outputs.clone();
- let result = context.with(|ctx| -> Result<Mapping> {
- ctx.globals().set(
- "__verge_log__",
- Func::from(move |level: String, data: String| {
- let mut out = copy_outputs.lock().unwrap();
- out.push((level, data));
- }),
- )?;
-
- ctx.eval(
- r#"var console = Object.freeze({
- log(data){__verge_log__("log",JSON.stringify(data))},
- info(data){__verge_log__("info",JSON.stringify(data))},
- error(data){__verge_log__("error",JSON.stringify(data))},
- debug(data){__verge_log__("debug",JSON.stringify(data))},
- });"#,
- )?;
-
- let config = use_lowercase(config.clone());
- let config_str = serde_json::to_string(&config)?;
-
- let code = format!(
- r#"try{{
+ use quick_rs::{context::Context, function::Function, module::Module, runtime::Runtime};
+
+ let config = use_lowercase(config.clone());
+ let config_str = serde_json::to_string(&config)?;
+
+ let runtime = Runtime::new();
+ let context = Context::from(&runtime);
+
+ let code = format!(
+ r#"
+ let output = [];
+
+ function __verge_log__(type, data) {{
+ output.push([type, data]);
+ }}
+
+ var console = Object.freeze({{
+ log(data) {{ __verge_log__("log", JSON.stringify(data)) }},
+ info(data) {{ __verge_log__("info", JSON.stringify(data)) }},
+ error(data) {{ __verge_log__("error", JSON.stringify(data)) }},
+ debug(data) {{ __verge_log__("debug", JSON.stringify(data)) }},
+ }});
+
{script};
- JSON.stringify(main({config_str})||'')
- }} catch(err) {{
- `__error_flag__ ${{err.toString()}}`
- }}"#
- );
- let result: String = ctx.eval(code.as_str())?;
- if result.starts_with("__error_flag__") {
- anyhow::bail!(result[15..].to_owned());
- }
- if result == "\"\"" {
- anyhow::bail!("main function should return object");
- }
- Ok(serde_json::from_str::<Mapping>(result.as_str())?)
- });
-
- let mut out = outputs.lock().unwrap();
- match result {
- Ok(config) => Ok((use_lowercase(config), out.to_vec())),
- Err(err) => {
- out.push(("exception".into(), err.to_string()));
- Ok((config, out.to_vec()))
- }
- }
+
+ export function _main(){{
+ try{{
+ let result = JSON.stringify(main({config_str})||"");
+ return JSON.stringify({{result, output}});
+ }} catch(err) {{
+ output.push(["exception", err.toString()]);
+ return JSON.stringify({{result: "__error__", output}});
+ }}
+ }}
+ "#
+ );
+ let value = context.eval_module(&code, "_main")?;
+ let module = Module::new(value)?;
+ let value = module.get("_main")?;
+ let function = Function::new(value)?;
+ let value = function.call(vec![])?;
+ let result = serde_json::from_str::<serde_json::Value>(&value.to_string()?)?;
+ result
+ .as_object()
+ .map(|obj| {
+ let result = obj.get("result").unwrap().as_str().unwrap();
+ let output = obj.get("output").unwrap();
+
+ let mut out = output
+ .as_array()
+ .unwrap()
+ .iter()
+ .map(|item| {
+ let item = item.as_array().unwrap();
+ (
+ item[0].as_str().unwrap().into(),
+ item[1].as_str().unwrap().into(),
+ )
+ })
+ .collect::<Vec<_>>();
+ if result.is_empty() {
+ anyhow::bail!("main function should return object");
+ }
+ if result == "__error__" {
+ return Ok((config, out.to_vec()));
+ }
+ let result = serde_json::from_str::<Mapping>(result);
+
+ match result {
+ Ok(config) => Ok((use_lowercase(config), out.to_vec())),
+ Err(err) => {
+ out.push(("exception".into(), err.to_string()));
+ Ok((config, out.to_vec()))
+ }
+ }
+ })
+ .unwrap_or_else(|| anyhow::bail!("Unknown result"))
}
#[test]
--
2.43.0.windows.1
+4152
View File
File diff suppressed because it is too large Load Diff
-104
View File
@@ -1,104 +0,0 @@
/**
* Build and upload assets for macOS(aarch)
*/
import fs from "fs-extra";
import path from "path";
import { exit } from "process";
import { createRequire } from "module";
import { getOctokit, context } from "@actions/github";
const require = createRequire(import.meta.url);
async function resolve() {
if (!process.env.GITHUB_TOKEN) {
throw new Error("GITHUB_TOKEN is required");
}
if (!process.env.GITHUB_REPOSITORY) {
throw new Error("GITHUB_REPOSITORY is required");
}
if (!process.env.TAURI_PRIVATE_KEY) {
throw new Error("TAURI_PRIVATE_KEY is required");
}
if (!process.env.TAURI_KEY_PASSWORD) {
throw new Error("TAURI_KEY_PASSWORD is required");
}
const { version } = require("../package.json");
const cwd = process.cwd();
const bundlePath = path.join(cwd, "src-tauri/target/release/bundle");
const join = (p) => path.join(bundlePath, p);
const appPathList = [
join("macos/Clash Verge.aarch64.app.tar.gz"),
join("macos/Clash Verge.aarch64.app.tar.gz.sig"),
];
for (const appPath of appPathList) {
if (fs.pathExistsSync(appPath)) {
fs.removeSync(appPath);
}
}
fs.copyFileSync(join("macos/Clash Verge.app.tar.gz"), appPathList[0]);
fs.copyFileSync(join("macos/Clash Verge.app.tar.gz.sig"), appPathList[1]);
const options = { owner: context.repo.owner, repo: context.repo.repo };
const github = getOctokit(process.env.GITHUB_TOKEN);
const { data: release } = await github.rest.repos.getReleaseByTag({
...options,
tag: `v${version}`,
});
if (!release.id) throw new Error("failed to find the release");
await uploadAssets(release.id, [
join(`dmg/Clash Verge_${version}_aarch64.dmg`),
...appPathList,
]);
}
// From tauri-apps/tauri-action
// https://github.com/tauri-apps/tauri-action/blob/dev/packages/action/src/upload-release-assets.ts
async function uploadAssets(releaseId, assets) {
const github = getOctokit(process.env.GITHUB_TOKEN);
// Determine content-length for header to upload asset
const contentLength = (filePath) => fs.statSync(filePath).size;
for (const assetPath of assets) {
const headers = {
"content-type": "application/zip",
"content-length": contentLength(assetPath),
};
const ext = path.extname(assetPath);
const filename = path.basename(assetPath).replace(ext, "");
const assetName = path.dirname(assetPath).includes(`target${path.sep}debug`)
? `${filename}-debug${ext}`
: `${filename}${ext}`;
console.log(`[INFO]: Uploading ${assetName}...`);
try {
await github.rest.repos.uploadReleaseAsset({
headers,
name: assetName,
data: fs.readFileSync(assetPath),
owner: context.repo.owner,
repo: context.repo.repo,
release_id: releaseId,
});
} catch (error) {
console.log(error.message);
}
}
}
if (process.platform === "darwin" && process.arch === "arm64") {
resolve();
} else {
console.error("invalid");
exit(1);
}
+267 -113
View File
@@ -1,5 +1,6 @@
import fs from "fs-extra";
import zlib from "zlib";
import tar from "tar";
import path from "path";
import AdmZip from "adm-zip";
import fetch from "node-fetch";
@@ -10,59 +11,165 @@ const cwd = process.cwd();
const TEMP_DIR = path.join(cwd, "node_modules/.verge");
const FORCE = process.argv.includes("--force");
const SIDECAR_HOST = execSync("rustc -vV")
.toString()
.match(/(?<=host: ).+(?=\s*)/g)[0];
/* ======= clash ======= */
const CLASH_URL_PREFIX =
"https://github.com/Dreamacro/clash/releases/download/premium/";
const CLASH_LATEST_DATE = "2022.08.26";
const CLASH_MAP = {
"win32-x64": "clash-windows-amd64",
"darwin-x64": "clash-darwin-amd64",
"darwin-arm64": "clash-darwin-arm64",
"linux-x64": "clash-linux-amd64",
"linux-arm64": "clash-linux-armv8",
const PLATFORM_MAP = {
"x86_64-pc-windows-msvc": "win32",
"i686-pc-windows-msvc": "win32",
"aarch64-pc-windows-msvc": "win32",
"x86_64-apple-darwin": "darwin",
"aarch64-apple-darwin": "darwin",
"x86_64-unknown-linux-gnu": "linux",
"i686-unknown-linux-gnu": "linux",
"aarch64-unknown-linux-gnu": "linux",
"armv7-unknown-linux-gnueabihf": "linux",
"loongarch64-unknown-linux-gnu": "linux",
};
const ARCH_MAP = {
"x86_64-pc-windows-msvc": "x64",
"i686-pc-windows-msvc": "ia32",
"aarch64-pc-windows-msvc": "arm64",
"x86_64-apple-darwin": "x64",
"aarch64-apple-darwin": "arm64",
"x86_64-unknown-linux-gnu": "x64",
"i686-unknown-linux-gnu": "ia32",
"aarch64-unknown-linux-gnu": "arm64",
"armv7-unknown-linux-gnueabihf": "arm",
"loongarch64-unknown-linux-gnu": "loong64",
};
/* ======= clash meta ======= */
const META_URL_PREFIX = `https://github.com/MetaCubeX/Clash.Meta/releases/download/`;
const META_VERSION = "v1.13.2";
const arg1 = process.argv.slice(2)[0];
const arg2 = process.argv.slice(2)[1];
const target = arg1 === "--force" ? arg2 : arg1;
const { platform, arch } = target
? { platform: PLATFORM_MAP[target], arch: ARCH_MAP[target] }
: process;
const SIDECAR_HOST = target
? target
: execSync("rustc -vV")
.toString()
.match(/(?<=host: ).+(?=\s*)/g)[0];
/* ======= clash meta alpha======= */
const META_ALPHA_VERSION_URL =
"https://github.com/MetaCubeX/mihomo/releases/download/Prerelease-Alpha/version.txt";
const META_ALPHA_URL_PREFIX = `https://github.com/MetaCubeX/mihomo/releases/download/Prerelease-Alpha`;
let META_ALPHA_VERSION;
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-arm64": "mihomo-darwin-arm64",
"linux-x64": "mihomo-linux-amd64-compatible",
"linux-ia32": "mihomo-linux-386",
"linux-arm64": "mihomo-linux-arm64",
"linux-arm": "mihomo-linux-armv7",
"linux-loong64": "mihomo-linux-loong64",
};
// Fetch the latest alpha release version from the version.txt file
async function getLatestAlphaVersion() {
const options = {};
const httpProxy =
process.env.HTTP_PROXY ||
process.env.http_proxy ||
process.env.HTTPS_PROXY ||
process.env.https_proxy;
if (httpProxy) {
options.agent = proxyAgent(httpProxy);
}
try {
const response = await fetch(META_ALPHA_VERSION_URL, {
...options,
method: "GET",
});
let v = await response.text();
META_ALPHA_VERSION = v.trim(); // Trim to remove extra whitespaces
console.log(`Latest alpha version: ${META_ALPHA_VERSION}`);
} catch (error) {
console.error("Error fetching latest alpha version:", error.message);
process.exit(1);
}
}
/* ======= clash meta stable ======= */
const META_VERSION_URL =
"https://github.com/MetaCubeX/mihomo/releases/latest/download/version.txt";
const META_URL_PREFIX = `https://github.com/MetaCubeX/mihomo/releases/download`;
let META_VERSION;
const META_MAP = {
"win32-x64": "Clash.Meta-windows-amd64-compatible",
"darwin-x64": "Clash.Meta-darwin-amd64",
"darwin-arm64": "Clash.Meta-darwin-arm64",
"linux-x64": "Clash.Meta-linux-amd64-compatible",
"linux-arm64": "Clash.Meta-linux-arm64",
"win32-x64": "mihomo-windows-amd64-compatible",
"win32-ia32": "mihomo-windows-386",
"win32-arm64": "mihomo-windows-arm64",
"darwin-x64": "mihomo-darwin-amd64",
"darwin-arm64": "mihomo-darwin-arm64",
"linux-x64": "mihomo-linux-amd64-compatible",
"linux-ia32": "mihomo-linux-386",
"linux-arm64": "mihomo-linux-arm64",
"linux-arm": "mihomo-linux-armv7",
"linux-loong64": "mihomo-linux-loong64",
};
/**
// Fetch the latest release version from the version.txt file
async function getLatestReleaseVersion() {
const options = {};
const httpProxy =
process.env.HTTP_PROXY ||
process.env.http_proxy ||
process.env.HTTPS_PROXY ||
process.env.https_proxy;
if (httpProxy) {
options.agent = proxyAgent(httpProxy);
}
try {
const response = await fetch(META_VERSION_URL, {
...options,
method: "GET",
});
let v = await response.text();
META_VERSION = v.trim(); // Trim to remove extra whitespaces
console.log(`Latest release version: ${META_VERSION}`);
} catch (error) {
console.error("Error fetching latest release version:", error.message);
process.exit(1);
}
}
/*
* check available
*/
const { platform, arch } = process;
if (!CLASH_MAP[`${platform}-${arch}`]) {
throw new Error(`clash unsupport platform "${platform}-${arch}"`);
}
if (!META_MAP[`${platform}-${arch}`]) {
throw new Error(`clash meta unsupport platform "${platform}-${arch}"`);
throw new Error(
`clash meta alpha unsupported platform "${platform}-${arch}"`
);
}
function clash() {
const name = CLASH_MAP[`${platform}-${arch}`];
if (!META_ALPHA_MAP[`${platform}-${arch}`]) {
throw new Error(
`clash meta alpha unsupported platform "${platform}-${arch}"`
);
}
/**
* core info
*/
function clashMetaAlpha() {
const name = META_ALPHA_MAP[`${platform}-${arch}`];
const isWin = platform === "win32";
const urlExt = isWin ? "zip" : "gz";
const downloadURL = `${CLASH_URL_PREFIX}${name}-${CLASH_LATEST_DATE}.${urlExt}`;
const downloadURL = `${META_ALPHA_URL_PREFIX}/${name}-${META_ALPHA_VERSION}.${urlExt}`;
const exeFile = `${name}${isWin ? ".exe" : ""}`;
const zipFile = `${name}.${urlExt}`;
const zipFile = `${name}-${META_ALPHA_VERSION}.${urlExt}`;
return {
name: "clash",
targetFile: `clash-${SIDECAR_HOST}${isWin ? ".exe" : ""}`,
name: "clash-meta-alpha",
targetFile: `clash-meta-alpha-${SIDECAR_HOST}${isWin ? ".exe" : ""}`,
exeFile,
zipFile,
downloadURL,
@@ -73,7 +180,7 @@ function clashMeta() {
const name = META_MAP[`${platform}-${arch}`];
const isWin = platform === "win32";
const urlExt = isWin ? "zip" : "gz";
const downloadURL = `${META_URL_PREFIX}${META_VERSION}/${name}-${META_VERSION}.${urlExt}`;
const downloadURL = `${META_URL_PREFIX}/${META_VERSION}/${name}-${META_VERSION}.${urlExt}`;
const exeFile = `${name}${isWin ? ".exe" : ""}`;
const zipFile = `${name}-${META_VERSION}.${urlExt}`;
@@ -85,7 +192,6 @@ function clashMeta() {
downloadURL,
};
}
/**
* download sidecar and rename
*/
@@ -103,75 +209,68 @@ async function resolveSidecar(binInfo) {
const tempExe = path.join(tempDir, exeFile);
await fs.mkdirp(tempDir);
if (!(await fs.pathExists(tempZip))) await downloadFile(downloadURL, tempZip);
try {
if (!(await fs.pathExists(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);
});
zip.extractAllTo(tempDir, true);
await fs.rename(tempExe, sidecarPath);
console.log(`[INFO]: ${name} unzip finished`);
} else {
// gz
const readStream = fs.createReadStream(tempZip);
const writeStream = fs.createWriteStream(sidecarPath);
readStream
.pipe(zlib.createGunzip())
.pipe(writeStream)
.on("finish", () => {
console.log(`[INFO]: ${name} gunzip finished`);
execSync(`chmod 755 ${sidecarPath}`);
console.log(`[INFO]: ${name} chmod binary finished`);
})
.on("error", (error) => {
console.error(`[ERROR]: ${name} gz failed`, error.message);
throw error;
if (zipFile.endsWith(".zip")) {
const zip = new AdmZip(tempZip);
zip.getEntries().forEach((entry) => {
console.log(`[DEBUG]: "${name}" entry name`, entry.entryName);
});
zip.extractAllTo(tempDir, true);
await fs.rename(tempExe, sidecarPath);
console.log(`[INFO]: "${name}" unzip finished`);
} else if (zipFile.endsWith(".tgz")) {
// tgz
await fs.mkdirp(tempDir);
await tar.extract({
cwd: tempDir,
file: tempZip,
//strip: 1, // 可能需要根据实际的 .tgz 文件结构调整
});
const files = await fs.readdir(tempDir);
console.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}"`);
execSync(`chmod 755 ${sidecarPath}`);
console.log(`[INFO]: "${name}" chmod binary finished`);
} else {
throw new Error(`Expected file not found in ${tempDir}`);
}
} else {
// gz
const readStream = fs.createReadStream(tempZip);
const writeStream = fs.createWriteStream(sidecarPath);
await new Promise((resolve, reject) => {
const onError = (error) => {
console.error(`[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`);
resolve();
})
.on("error", onError);
});
}
} catch (err) {
// 需要删除文件
await fs.remove(sidecarPath);
throw err;
} finally {
// delete temp dir
await fs.remove(tempDir);
}
// delete temp dir
await fs.remove(tempDir);
}
/**
* only Windows
* get the wintun.dll (not required)
*/
async function resolveWintun() {
const { platform } = process;
if (platform !== "win32") return;
const url = "https://www.wintun.net/builds/wintun-0.14.1.zip";
const tempDir = path.join(TEMP_DIR, "wintun");
const tempZip = path.join(tempDir, "wintun.zip");
const wintunPath = path.join(tempDir, "wintun/bin/amd64/wintun.dll");
const targetPath = path.join(cwd, "src-tauri/resources", "wintun.dll");
if (!FORCE && (await fs.pathExists(targetPath))) return;
await fs.mkdirp(tempDir);
if (!(await fs.pathExists(tempZip))) {
await downloadFile(url, tempZip);
}
// unzip
const zip = new AdmZip(tempZip);
zip.extractAllTo(tempDir, true);
if (!(await fs.pathExists(wintunPath))) {
throw new Error(`path not found "${wintunPath}"`);
}
await fs.rename(wintunPath, targetPath);
await fs.remove(tempDir);
console.log(`[INFO]: resolve wintun.dll finished`);
}
/**
@@ -218,11 +317,43 @@ async function downloadFile(url, path) {
console.log(`[INFO]: download finished "${url}"`);
}
// SimpleSC.dll
const resolvePlugin = async () => {
const url =
"https://nsis.sourceforge.io/mediawiki/images/e/ef/NSIS_Simple_Service_Plugin_Unicode_1.30.zip";
const tempDir = path.join(TEMP_DIR, "SimpleSC");
const tempZip = path.join(
tempDir,
"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;
try {
if (!(await fs.pathExists(tempZip))) {
await downloadFile(url, tempZip);
}
const zip = new AdmZip(tempZip);
zip.getEntries().forEach((entry) => {
console.log(`[DEBUG]: "SimpleSC" entry name`, entry.entryName);
});
zip.extractAllTo(tempDir, true);
await fs.copyFile(tempDll, pluginPath);
console.log(`[INFO]: "SimpleSC" unzip finished`);
} finally {
await fs.remove(tempDir);
}
};
/**
* main
*/
const SERVICE_URL =
"https://github.com/zzzgydi/clash-verge-service/releases/download/latest";
const SERVICE_URL = `https://github.com/clash-verge-rev/clash-verge-service/releases/download/${SIDECAR_HOST}`;
const resolveService = () =>
resolveResource({
@@ -242,29 +373,51 @@ const resolveUninstall = () =>
const resolveMmdb = () =>
resolveResource({
file: "Country.mmdb",
downloadURL: `https://github.com/Dreamacro/maxmind-geoip/releases/download/20221112/Country.mmdb`,
downloadURL: `https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/country.mmdb`,
});
const resolveGeosite = () =>
resolveResource({
file: "geosite.dat",
downloadURL: `https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat`,
downloadURL: `https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat`,
});
const resolveGeoIP = () =>
resolveResource({
file: "geoip.dat",
downloadURL: `https://github.com/Loyalsoldier/geoip/releases/latest/download/geoip.dat`,
downloadURL: `https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat`,
});
const resolveEnableLoopback = () =>
resolveResource({
file: "enableLoopback.exe",
downloadURL: `https://github.com/Kuingsmile/uwp-tool/releases/download/latest/enableLoopback.exe`,
});
const tasks = [
{ name: "clash", func: () => resolveSidecar(clash()), retry: 5 },
{ name: "clash-meta", func: () => resolveSidecar(clashMeta()), retry: 5 },
{ name: "wintun", func: resolveWintun, retry: 5, winOnly: true },
// { name: "clash", func: resolveClash, retry: 5 },
{
name: "clash-meta-alpha",
func: () =>
getLatestAlphaVersion().then(() => resolveSidecar(clashMetaAlpha())),
retry: 5,
},
{
name: "clash-meta",
func: () =>
getLatestReleaseVersion().then(() => resolveSidecar(clashMeta())),
retry: 5,
},
{ name: "plugin", func: resolvePlugin, retry: 5, winOnly: true },
{ name: "service", func: resolveService, retry: 5, winOnly: true },
{ name: "install", func: resolveInstall, retry: 5, winOnly: true },
{ name: "uninstall", func: resolveUninstall, retry: 5, winOnly: true },
{ name: "mmdb", func: resolveMmdb, retry: 5 },
{ name: "geosite", func: resolveGeosite, retry: 5 },
{ name: "geoip", func: resolveGeoIP, retry: 5 },
{
name: "enableLoopback",
func: resolveEnableLoopback,
retry: 5,
winOnly: true,
},
];
async function runTask() {
@@ -277,7 +430,8 @@ async function runTask() {
await task.func();
break;
} catch (err) {
console.error(`[ERROR]: task::${task.name} try ${i} == `, err.message);
console.error(`[ERROR]: task::${task.name} try ${i} ==`, err.message);
if (i === task.retry - 1) throw err;
}
}
return runTask();
+32 -6
View File
@@ -4,29 +4,44 @@ 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",
"aarch64-pc-windows-msvc": "arm64",
};
/// Script for ci
/// 打包绿色版/便携版 (only Windows)
async function resolvePortable() {
if (process.platform !== "win32") return;
const releaseDir = "./src-tauri/target/release";
const releaseDir = target
? `./src-tauri/target/${target}/release`
: `./src-tauri/target/release`;
const configDir = path.join(releaseDir, ".config");
if (!(await fs.pathExists(releaseDir))) {
throw new Error("could not found the release dir");
}
await fs.mkdir(configDir);
await fs.createFile(path.join(configDir, "PORTABLE"));
const zip = new AdmZip();
zip.addLocalFile(path.join(releaseDir, "Clash Verge.exe"));
zip.addLocalFile(path.join(releaseDir, "clash.exe"));
zip.addLocalFile(path.join(releaseDir, "clash-meta.exe"));
zip.addLocalFile(path.join(releaseDir, "clash-meta-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}_x64_portable.zip`;
const zipFile = `Clash.Verge_${version}_${ARCH_MAP[target]}_portable.zip`;
zip.writeZip(zipFile);
console.log("[INFO]: create portable zip successfully");
@@ -38,14 +53,25 @@ async function resolvePortable() {
const options = { owner: context.repo.owner, repo: context.repo.repo };
const github = getOctokit(process.env.GITHUB_TOKEN);
console.log("[INFO]: upload to ", process.env.TAG_NAME || `v${version}`);
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: process.env.TAG_NAME || `v${version}`,
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({
-53
View File
@@ -1,53 +0,0 @@
import fs from "fs-extra";
import { createRequire } from "module";
import { execSync } from "child_process";
import { resolveUpdateLog } from "./updatelog.mjs";
const require = createRequire(import.meta.url);
// publish
async function resolvePublish() {
const flag = process.argv[2] ?? "patch";
const packageJson = require("../package.json");
const tauriJson = require("../src-tauri/tauri.conf.json");
let [a, b, c] = packageJson.version.split(".").map(Number);
if (flag === "major") {
a += 1;
b = 0;
c = 0;
} else if (flag === "minor") {
b += 1;
c = 0;
} else if (flag === "patch") {
c += 1;
} else throw new Error(`invalid flag "${flag}"`);
const nextVersion = `${a}.${b}.${c}`;
packageJson.version = nextVersion;
tauriJson.package.version = nextVersion;
// 发布更新前先写更新日志
const nextTag = `v${nextVersion}`;
await resolveUpdateLog(nextTag);
await fs.writeFile(
"./package.json",
JSON.stringify(packageJson, undefined, 2)
);
await fs.writeFile(
"./src-tauri/tauri.conf.json",
JSON.stringify(tauriJson, undefined, 2)
);
execSync("git add ./package.json");
execSync("git add ./src-tauri/tauri.conf.json");
execSync(`git commit -m "v${nextVersion}"`);
execSync(`git tag -a v${nextVersion} -m "v${nextVersion}"`);
execSync(`git push`);
execSync(`git push origin v${nextVersion}`);
console.log(`Publish Successfully...`);
}
resolvePublish();
+30 -11
View File
@@ -43,9 +43,12 @@ async function resolveUpdater() {
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-i686": { signature: "", url: "" }, // no supported
"windows-aarch64": { signature: "", url: "" },
},
};
@@ -53,27 +56,39 @@ async function resolveUpdater() {
const { name, browser_download_url } = asset;
// win64 url
if (name.endsWith(".msi.zip") && name.includes("en-US")) {
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(".msi.zip.sig") && name.includes("en-US")) {
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;
}
// 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)
@@ -86,16 +101,22 @@ async function resolveUpdater() {
updateData.platforms["darwin-aarch64"].signature = sig;
}
// linux url
if (name.endsWith(".AppImage.tar.gz")) {
// 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 signature
if (name.endsWith(".AppImage.tar.gz.sig")) {
// 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;
}
});
@@ -117,10 +138,8 @@ async function resolveUpdater() {
Object.entries(updateDataNew.platforms).forEach(([key, value]) => {
if (value.url) {
updateDataNew.platforms[key].url = value.url.replace(
"https://github.com/",
"https://hub.fastgit.xyz/"
);
updateDataNew.platforms[key].url =
"https://mirror.ghproxy.com/" + value.url;
} else {
console.log(`[Error]: updateDataNew.platforms.${key} is null`);
}
+2317 -1310
View File
File diff suppressed because it is too large Load Diff
+29 -29
View File
@@ -1,10 +1,10 @@
[package]
name = "clash-verge"
version = "0.1.0"
version = "1.5.5"
description = "clash verge"
authors = ["zzzgydi"]
license = "GPL-3.0"
repository = "https://github.com/zzzgydi/clash-verge.git"
authors = ["zzzgydi", "wonfen", "MystiPanda"]
license = "GPL-3.0-only"
repository = "https://github.com/clash-verge-rev/clash-verge-rev.git"
default-run = "clash-verge"
edition = "2021"
build = "build.rs"
@@ -14,45 +14,45 @@ tauri-build = { version = "1", features = [] }
[dependencies]
warp = "0.3"
which = "4.2.2"
which = "6.0.0"
anyhow = "1.0"
dirs = "4.0.0"
open = "3.0.3"
log = "0.4.14"
ctrlc = "3.2.3"
dunce = "1.0.2"
log4rs = "1.0.0"
nanoid = "0.4.0"
chrono = "0.4.19"
sysinfo = "0.26.2"
sysproxy = "0.1"
rquickjs = "0.1.7"
dirs = "5.0"
open = "5.0"
log = "0.4"
ctrlc = "3.4"
dunce = "1.0"
log4rs = "1"
nanoid = "0.4"
chrono = "0.4"
sysinfo = "0.30"
rquickjs = "0.3" # 高版本不支持 Linux aarch64
serde_json = "1.0"
serde_yaml = "0.9"
auto-launch = "0.4"
once_cell = "1.14.0"
once_cell = "1.18"
port_scanner = "0.1.5"
delay_timer = "0.11.1"
parking_lot = "0.12.0"
delay_timer = "0.11.5"
parking_lot = "0.12"
percent-encoding = "2.3.1"
window-shadows = { version = "0.2" }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.11", features = ["json"] }
tauri = { version = "1.1.1", features = ["global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all"] }
tauri-runtime-wry = { version = "0.12" }
window-vibrancy = { version = "0.3.0" }
window-shadows = { version = "0.2.0" }
reqwest = { version = "0.11", 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.5", features = [ "path-all", "protocol-asset", "dialog-open", "notification-all", "icon-png", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all"] }
[target.'cfg(windows)'.dependencies]
runas = "0.2.1"
runas = "=1.0.0" # 高版本会返回错误 Status
deelevate = "0.2.0"
winreg = { version = "0.10", features = ["transactions"] }
windows-sys = { version = "0.36", features = ["Win32_System_LibraryLoader", "Win32_System_SystemInformation"] }
winreg = "0.52.0"
[target.'cfg(target_os = "linux")'.dependencies]
#openssl
[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]
verge-dev = []
debug-yml = []
[profile.release]
panic = "abort"
+17
View File
@@ -0,0 +1,17 @@
<?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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

+104 -4
View File
@@ -2,14 +2,14 @@ use crate::{
config::*,
core::*,
feat,
utils::{dirs, help},
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;
type CmdResult<T = ()> = Result<T, String>;
#[tauri::command]
@@ -30,6 +30,11 @@ pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult
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)?;
@@ -80,7 +85,7 @@ pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult {
}
#[tauri::command]
pub fn view_profile(index: String) -> CmdResult {
pub fn view_profile(app_handle: tauri::AppHandle, index: String) -> CmdResult {
let file = {
wrap_err!(Config::profiles().latest().get_item(&index))?
.file
@@ -93,7 +98,7 @@ pub fn view_profile(index: String) -> CmdResult {
ret_err!("the file not found");
}
wrap_err!(help::open_file(path))
wrap_err!(help::open_file(app_handle, path))
}
#[tauri::command]
@@ -175,6 +180,15 @@ 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> {
@@ -202,6 +216,13 @@ pub fn open_app_dir() -> CmdResult<()> {
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())?;
@@ -213,6 +234,75 @@ 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 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 dest_path = icon_dir.join(name);
if file_path.exists() {
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 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);
}
#[cfg(windows)]
pub mod service {
use super::*;
@@ -251,3 +341,13 @@ pub mod service {
Ok(())
}
}
#[cfg(not(windows))]
pub mod uwp {
use super::*;
#[tauri::command]
pub async fn invoke_uwp_tool() -> CmdResult {
Ok(())
}
}
+88 -19
View File
@@ -12,33 +12,56 @@ 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)) {
Ok(map) => Self(Self::guard(map)),
Ok(mut map) => {
template.0.keys().for_each(|key| {
if !map.contains_key(key) {
map.insert(key.clone(), template.0.get(key).unwrap().clone());
}
});
Self(Self::guard(map))
}
Err(err) => {
log::error!(target: "app", "{err}");
Self::template()
template
}
}
}
pub fn template() -> Self {
let mut map = Mapping::new();
let mut tun = Mapping::new();
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(), 9000.into());
map.insert("mixed-port".into(), 7890.into());
map.insert("mixed-port".into(), 7897.into());
map.insert("socks-port".into(), 7898.into());
map.insert("port".into(), 7899.into());
map.insert("log-level".into(), "info".into());
map.insert("allow-lan".into(), false.into());
map.insert("mode".into(), "rule".into());
map.insert("external-controller".into(), "127.0.0.1:9090".into());
map.insert("external-controller".into(), "127.0.0.1:9097".into());
map.insert("secret".into(), "".into());
map.insert("tun".into(), tun.into());
Self(map)
}
fn guard(mut config: Mapping) -> Mapping {
let port = Self::guard_mixed_port(&config);
let mixed_port = Self::guard_mixed_port(&config);
let socks_port = Self::guard_socks_port(&config);
let port = Self::guard_port(&config);
let ctrl = Self::guard_server_ctrl(&config);
config.insert("mixed-port".into(), port.into());
config.insert("mixed-port".into(), mixed_port.into());
config.insert("socks-port".into(), socks_port.into());
config.insert("port".into(), port.into());
config.insert("external-controller".into(), ctrl.into());
config
}
@@ -61,12 +84,24 @@ impl IClashTemp {
Self::guard_mixed_port(&self.0)
}
#[allow(unused)]
pub fn get_socks_port(&self) -> u16 {
Self::guard_socks_port(&self.0)
}
#[allow(unused)]
pub fn get_port(&self) -> u16 {
Self::guard_port(&self.0)
}
pub fn get_client_info(&self) -> ClashInfo {
let config = &self.0;
ClashInfo {
port: Self::guard_mixed_port(&config),
server: Self::guard_client_ctrl(&config),
mixed_port: Self::guard_mixed_port(config),
socks_port: Self::guard_socks_port(config),
port: Self::guard_port(config),
server: Self::guard_client_ctrl(config),
secret: config.get("secret").and_then(|value| match value {
Value::String(val_str) => Some(val_str.clone()),
Value::Bool(val_bool) => Some(val_bool.to_string()),
@@ -84,9 +119,39 @@ impl IClashTemp {
Value::Number(val_num) => val_num.as_u64().map(|u| u as u16),
_ => None,
})
.unwrap_or(7890);
.unwrap_or(7897);
if port == 0 {
port = 7890;
port = 7897;
}
port
}
pub fn guard_socks_port(config: &Mapping) -> u16 {
let mut port = config
.get("socks-port")
.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(7898);
if port == 0 {
port = 7898;
}
port
}
pub fn guard_port(config: &Mapping) -> u16 {
let mut port = config
.get("port")
.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(7899);
if port == 0 {
port = 7899;
}
port
}
@@ -98,7 +163,7 @@ impl IClashTemp {
Some(val_str) => {
let val_str = val_str.trim();
let val = match val_str.starts_with(":") {
let val = match val_str.starts_with(':') {
true => format!("127.0.0.1{val_str}"),
false => val_str.to_owned(),
};
@@ -109,7 +174,7 @@ impl IClashTemp {
}
None => None,
})
.unwrap_or("127.0.0.1:9090".into())
.unwrap_or("127.0.0.1:9097".into())
}
pub fn guard_client_ctrl(config: &Mapping) -> String {
@@ -121,7 +186,7 @@ impl IClashTemp {
}
socket.to_string()
}
Err(_) => "127.0.0.1:9090".into(),
Err(_) => "127.0.0.1:9097".into(),
}
}
}
@@ -129,6 +194,8 @@ impl IClashTemp {
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct ClashInfo {
/// clash core port
pub mixed_port: u16,
pub socks_port: u16,
pub port: u16,
/// same as `external-controller`
pub server: String,
@@ -148,7 +215,9 @@ fn test_clash_info() {
fn get_result<S: Into<String>>(port: u16, server: S) -> ClashInfo {
ClashInfo {
port,
mixed_port: port,
socks_port: 7898,
port: 7899,
server: server.into(),
secret: None,
}
@@ -156,12 +225,12 @@ fn test_clash_info() {
assert_eq!(
IClashTemp(IClashTemp::guard(Mapping::new())).get_client_info(),
get_result(7890, "127.0.0.1:9090")
get_result(7897, "127.0.0.1:9097")
);
assert_eq!(get_case("", ""), get_result(7890, "127.0.0.1:9090"));
assert_eq!(get_case("", ""), get_result(7897, "127.0.0.1:9097"));
assert_eq!(get_case(65537, ""), get_result(1, "127.0.0.1:9090"));
assert_eq!(get_case(65537, ""), get_result(1, "127.0.0.1:9097"));
assert_eq!(
get_case(8888, "127.0.0.1:8888"),
@@ -170,7 +239,7 @@ fn test_clash_info() {
assert_eq!(
get_case(8888, " :98888 "),
get_result(8888, "127.0.0.1:9090")
get_result(8888, "127.0.0.1:9097")
);
assert_eq!(
@@ -195,7 +264,7 @@ fn test_clash_info() {
assert_eq!(
get_case(8888, "192.168.1.1:80800"),
get_result(8888, "127.0.0.1:9090")
get_result(8888, "127.0.0.1:9097")
);
}
+3 -3
View File
@@ -45,7 +45,7 @@ impl Config {
Self::global().runtime_config.clone()
}
/// 初始化配置
/// 初始化订阅
pub fn init_config() -> Result<()> {
crate::log_err!(Self::generate());
if let Err(err) = Self::generate_file(ConfigType::Run) {
@@ -64,7 +64,7 @@ impl Config {
Ok(())
}
/// 将配置丢到对应的文件中
/// 将订阅丢到对应的文件中
pub fn generate_file(typ: ConfigType) -> Result<PathBuf> {
let path = match typ {
ConfigType::Run => dirs::app_home_dir()?.join(RUNTIME_CONFIG),
@@ -82,7 +82,7 @@ impl Config {
Ok(path)
}
/// 生成配置存好
/// 生成订阅存好
pub fn generate() -> Result<()> {
let (config, exists_keys, logs) = enhance::enhance();
+1 -1
View File
@@ -26,7 +26,7 @@ macro_rules! draft_define {
}
pub fn draft(&self) -> MappedMutexGuard<$id> {
MutexGuard::map(self.inner.lock(), |mut inner| {
MutexGuard::map(self.inner.lock(), |inner| {
if inner.1.is_none() {
inner.1 = Some(inner.0.clone());
}
+82 -65
View File
@@ -1,4 +1,4 @@
use crate::utils::{dirs, help, tmpl};
use crate::utils::{dirs, help, resolve::VERSION, tmpl};
use anyhow::{bail, Context, Result};
use reqwest::StatusCode;
use serde::{Deserialize, Serialize};
@@ -8,7 +8,7 @@ use sysproxy::Sysproxy;
use super::Config;
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct PrfItem {
pub uid: Option<String>,
@@ -59,10 +59,10 @@ pub struct PrfSelected {
#[derive(Default, Debug, Clone, Copy, Deserialize, Serialize)]
pub struct PrfExtra {
pub upload: usize,
pub download: usize,
pub total: usize,
pub expire: usize,
pub upload: u64,
pub download: u64,
pub total: u64,
pub expire: u64,
}
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
@@ -84,6 +84,12 @@ pub struct PrfOption {
#[serde(skip_serializing_if = "Option::is_none")]
pub update_interval: Option<u64>,
/// for `remote` profile
/// disable certificate validation
/// default is `false`
#[serde(skip_serializing_if = "Option::is_none")]
pub danger_accept_invalid_certs: Option<bool>,
}
impl PrfOption {
@@ -93,28 +99,11 @@ 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.update_interval = b.update_interval.or(a.update_interval);
Some(a)
}
t @ _ => t.0.or(t.1),
}
}
}
impl Default for PrfItem {
fn default() -> Self {
PrfItem {
uid: None,
itype: None,
name: None,
desc: None,
file: None,
url: None,
selected: None,
extra: None,
updated: None,
option: None,
file_data: None,
t => t.0.or(t.1),
}
}
}
@@ -152,7 +141,7 @@ impl PrfItem {
let desc = item.desc.unwrap_or("".into());
PrfItem::from_script(name, desc)
}
typ @ _ => bail!("invalid profile item type \"{typ}\""),
typ => bail!("invalid profile item type \"{typ}\""),
}
}
@@ -188,13 +177,18 @@ impl 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 user_agent = opt_ref.map_or(None, |o| o.user_agent.clone());
let accept_invalid_certs = opt_ref.map_or(false, |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 builder = reqwest::ClientBuilder::new().no_proxy();
let mut builder = reqwest::ClientBuilder::new().use_rustls_tls().no_proxy();
// 使用软件自己的代理
if self_proxy {
let port = Config::clash().data().get_mixed_port();
let port = Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
let proxy_scheme = format!("http://127.0.0.1:{port}");
@@ -210,26 +204,27 @@ impl PrfItem {
}
// 使用系统代理
else if with_proxy {
match Sysproxy::get_system_proxy() {
Ok(p @ Sysproxy { enable: true, .. }) => {
let proxy_scheme = format!("http://{}:{}", p.host, p.port);
if let Ok(p @ Sysproxy { enable: true, .. }) = Sysproxy::get_system_proxy() {
let proxy_scheme = format!("http://{}:{}", p.host, p.port);
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
_ => {}
};
if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
}
}
let version = unsafe { dirs::APP_VERSION };
let version = format!("clash-verge/{version}");
let version = match VERSION.get() {
Some(v) => format!("clash-verge/v{}", v),
None => "clash-verge/unknown".to_string(),
};
builder = builder.danger_accept_invalid_certs(accept_invalid_certs);
builder = builder.user_agent(user_agent.unwrap_or(version));
let resp = builder.build()?.get(url).send().await?;
@@ -245,12 +240,11 @@ impl PrfItem {
let extra = match header.get("Subscription-Userinfo") {
Some(value) => {
let sub_info = value.to_str().unwrap_or("");
Some(PrfExtra {
upload: help::parse_str(sub_info, "upload=").unwrap_or(0),
download: help::parse_str(sub_info, "download=").unwrap_or(0),
total: help::parse_str(sub_info, "total=").unwrap_or(0),
expire: help::parse_str(sub_info, "expire=").unwrap_or(0),
upload: help::parse_str(sub_info, "upload").unwrap_or(0),
download: help::parse_str(sub_info, "download").unwrap_or(0),
total: help::parse_str(sub_info, "total").unwrap_or(0),
expire: help::parse_str(sub_info, "expire").unwrap_or(0),
})
}
None => None,
@@ -259,22 +253,42 @@ impl PrfItem {
// parse the Content-Disposition
let filename = match header.get("Content-Disposition") {
Some(value) => {
let filename = value.to_str().unwrap_or("");
help::parse_str::<String>(filename, "filename=")
let filename = format!("{value:?}");
let filename = filename.trim_matches('"');
match help::parse_str::<String>(filename, "filename*") {
Some(filename) => {
let iter = percent_encoding::percent_decode(filename.as_bytes());
let filename = iter.decode_utf8().unwrap_or_default();
filename.split("''").last().map(|s| s.to_string())
}
None => match help::parse_str::<String>(filename, "filename") {
Some(filename) => {
let filename = filename.trim_matches('"');
Some(filename.to_string())
}
None => None,
},
}
}
None => None,
None => Some(
crate::utils::help::get_last_part_and_decode(url).unwrap_or("Remote File".into()),
),
};
// parse the profile-update-interval
let option = 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()
}),
Err(_) => None,
let option = match update_interval {
Some(val) => Some(PrfOption {
update_interval: Some(val),
..PrfOption::default()
}),
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()
}),
Err(_) => None,
},
None => None,
},
None => None,
};
let uid = help::get_uid("r");
@@ -282,8 +296,11 @@ impl PrfItem {
let name = name.unwrap_or(filename.unwrap_or("Remote File".into()));
let data = resp.text_with_charset("utf-8").await?;
// process the charset "UTF-8 with BOM"
let data = data.trim_start_matches('\u{feff}');
// check the data whether the valid yaml format
let yaml = serde_yaml::from_str::<Mapping>(&data) //
let yaml = serde_yaml::from_str::<Mapping>(data)
.context("the remote profile data is invalid yaml")?;
if !yaml.contains_key("proxies") && !yaml.contains_key("proxy-providers") {
@@ -301,7 +318,7 @@ impl PrfItem {
extra,
option,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(data),
file_data: Some(data.into()),
})
}
+44 -26
View File
@@ -14,9 +14,6 @@ pub struct IProfiles {
/// same as PrfConfig.chain
pub chain: Option<Vec<String>>,
/// record valid fields for clash
pub valid: Option<Vec<String>>,
/// profile list
pub items: Option<Vec<PrfItem>>,
}
@@ -37,13 +34,13 @@ impl IProfiles {
profiles.items = Some(vec![]);
}
// compatible with the old old old version
profiles.items.as_mut().map(|items| {
for mut item in items.iter_mut() {
if let Some(items) = profiles.items.as_mut() {
for item in items.iter_mut() {
if item.uid.is_none() {
item.uid = Some(help::get_uid("d"));
}
}
});
}
profiles
}
Err(err) => {
@@ -55,7 +52,6 @@ impl IProfiles {
pub fn template() -> Self {
Self {
valid: Some(vec!["dns".into()]),
items: Some(vec![]),
..Self::default()
}
@@ -88,10 +84,6 @@ impl IProfiles {
self.chain = Some(chain);
}
if let Some(valid) = patch.valid {
self.valid = Some(valid);
}
Ok(())
}
@@ -138,24 +130,50 @@ impl IProfiles {
let path = dirs::app_profiles_dir()?.join(&file);
fs::File::create(path)
.context(format!("failed to create file \"{}\"", file))?
.with_context(|| format!("failed to create file \"{}\"", file))?
.write(file_data.as_bytes())
.context(format!("failed to write to file \"{}\"", file))?;
.with_context(|| format!("failed to write to file \"{}\"", file))?;
}
if self.items.is_none() {
self.items = Some(vec![]);
}
self.items.as_mut().map(|items| items.push(item));
if let Some(items) = self.items.as_mut() {
items.push(item)
}
self.save_file()
}
/// reorder items
pub fn reorder(&mut self, active_id: String, over_id: String) -> Result<()> {
let mut items = self.items.take().unwrap_or_default();
let mut old_index = None;
let mut new_index = None;
for (i, _) in items.iter().enumerate() {
if items[i].uid == Some(active_id.clone()) {
old_index = Some(i);
}
if items[i].uid == Some(over_id.clone()) {
new_index = Some(i);
}
}
if old_index.is_none() || new_index.is_none() {
return Ok(());
}
let item = items.remove(old_index.unwrap());
items.insert(new_index.unwrap(), item);
self.items = Some(items);
self.save_file()
}
/// update the item value
pub fn patch_item(&mut self, uid: String, item: PrfItem) -> Result<()> {
let mut items = self.items.take().unwrap_or(vec![]);
let mut items = self.items.take().unwrap_or_default();
for mut each in items.iter_mut() {
for each in items.iter_mut() {
if each.uid == Some(uid.clone()) {
patch!(each, item, itype);
patch!(each, item, name);
@@ -189,7 +207,7 @@ impl IProfiles {
if let Some(items) = self.items.as_mut() {
let some_uid = Some(uid.clone());
for mut each in items.iter_mut() {
for each in items.iter_mut() {
if each.uid == some_uid {
each.extra = item.extra;
each.updated = item.updated;
@@ -207,9 +225,9 @@ impl IProfiles {
let path = dirs::app_profiles_dir()?.join(&file);
fs::File::create(path)
.context(format!("failed to create file \"{}\"", file))?
.with_context(|| format!("failed to create file \"{}\"", file))?
.write(file_data.as_bytes())
.context(format!("failed to write to file \"{}\"", file))?;
.with_context(|| format!("failed to write to file \"{}\"", file))?;
}
break;
@@ -226,11 +244,11 @@ impl IProfiles {
let current = self.current.as_ref().unwrap_or(&uid);
let current = current.clone();
let mut items = self.items.take().unwrap_or(vec![]);
let mut items = self.items.take().unwrap_or_default();
let mut index = None;
// get the index
for i in 0..items.len() {
for (i, _) in items.iter().enumerate() {
if items[i].uid == Some(uid.clone()) {
index = Some(i);
break;
@@ -238,19 +256,19 @@ impl IProfiles {
}
if let Some(index) = index {
items.remove(index).file.map(|file| {
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.len() > 0 {
self.current = match !items.is_empty() {
true => items[0].uid.clone(),
false => None,
};
@@ -261,7 +279,7 @@ impl IProfiles {
Ok(current == uid)
}
/// 获取current指向的配置内容
/// 获取current指向的订阅内容
pub fn current_mapping(&self) -> Result<Mapping> {
match (self.current.as_ref(), self.items.as_ref()) {
(Some(current), Some(items)) => {
@@ -270,7 +288,7 @@ impl IProfiles {
Some(file) => dirs::app_profiles_dir()?.join(file),
None => bail!("failed to get the file field"),
};
return Ok(help::read_merge_mapping(&file_path)?);
return help::read_merge_mapping(&file_path);
}
bail!("failed to find the current profile \"uid:{current}\"");
}
+30 -3
View File
@@ -1,11 +1,11 @@
use crate::enhance::field::use_keys;
use serde::{Deserialize, Serialize};
use serde_yaml::Mapping;
use serde_yaml::{Mapping, Value};
use std::collections::HashMap;
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct IRuntime {
pub config: Option<Mapping>,
// 记录在配置中(包括merge和script生成的)出现过的keys
// 记录在订阅中(包括merge和script生成的)出现过的keys
// 这些keys不一定都生效
pub exists_keys: Vec<String>,
pub chain_logs: HashMap<String, Vec<(String, String)>>,
@@ -15,4 +15,31 @@ impl IRuntime {
pub fn new() -> Self {
Self::default()
}
// 这里只更改 allow-lan | ipv6 | log-level | tun
pub fn patch_config(&mut self, patch: Mapping) {
if let Some(config) = self.config.as_mut() {
["allow-lan", "ipv6", "log-level"]
.into_iter()
.for_each(|key| {
if let Some(value) = patch.get(key).to_owned() {
config.insert(key.into(), value.clone());
}
});
let tun = config.get("tun");
let mut tun = tun.map_or(Mapping::new(), |val| {
val.as_mapping().cloned().unwrap_or(Mapping::new())
});
let patch_tun = patch.get("tun");
let patch_tun = patch_tun.map_or(Mapping::new(), |val| {
val.as_mapping().cloned().unwrap_or(Mapping::new())
});
use_keys(&patch_tun).into_iter().for_each(|key| {
if let Some(value) = patch_tun.get(&key).to_owned() {
tun.insert(key.into(), value.clone());
}
});
config.insert("tun".into(), Value::from(tun));
}
}
}
+123 -7
View File
@@ -1,5 +1,6 @@
use crate::utils::{dirs, help};
use anyhow::Result;
use log::LevelFilter;
use serde::{Deserialize, Serialize};
/// ### `verge.yaml` schema
@@ -8,19 +9,45 @@ pub struct IVerge {
/// app listening port for app singleton
pub app_singleton_port: Option<u16>,
/// app log level
/// silent | error | warn | info | debug | trace
pub app_log_level: Option<String>,
// i18n
pub language: Option<String>,
/// `light` or `dark` or `system`
pub theme_mode: Option<String>,
/// enable blur mode
/// maybe be able to set the alpha
pub theme_blur: Option<bool>,
/// tray click event
pub tray_event: Option<String>,
/// copy env type
pub env_type: Option<String>,
/// start page
pub start_page: Option<String>,
/// startup script path
pub startup_script: Option<String>,
/// enable traffic graph default is true
pub traffic_graph: Option<bool>,
/// show memory info (only for Clash Meta)
pub enable_memory_usage: Option<bool>,
/// enable group icon
pub enable_group_icon: Option<bool>,
/// common tray icon
pub common_tray_icon: Option<bool>,
/// sysproxy tray icon
pub sysproxy_tray_icon: Option<bool>,
/// tun tray icon
pub tun_tray_icon: Option<bool>,
/// clash tun mode
pub enable_tun_mode: Option<bool>,
@@ -66,8 +93,47 @@ pub struct IVerge {
/// 默认的延迟测试连接
pub default_latency_test: Option<String>,
/// 默认的延迟测试超时时间
pub default_latency_timeout: Option<i32>,
/// 是否使用内部的脚本支持,默认为真
pub enable_builtin_enhanced: Option<bool>,
/// proxy 页面布局 列数
pub proxy_layout_column: Option<i32>,
/// 测试网站列表
pub test_list: Option<Vec<IVergeTestItem>>,
/// 日志清理
/// 0: 不清理; 1: 7天; 2: 30天; 3: 90天
pub auto_log_clean: Option<i32>,
/// window size and position
#[serde(skip_serializing_if = "Option::is_none")]
pub window_size_position: Option<Vec<f64>>,
/// window size and position
#[serde(skip_serializing_if = "Option::is_none")]
pub window_is_maximized: Option<bool>,
/// 是否启用随机端口
pub enable_random_port: Option<bool>,
/// verge mixed port 用于覆盖 clash 的 mixed port
pub verge_mixed_port: Option<u16>,
pub verge_socks_port: Option<u16>,
pub verge_port: Option<u16>,
}
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct IVergeTestItem {
pub uid: Option<String>,
pub name: Option<String>,
pub icon: Option<String>,
pub url: Option<String>,
}
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
@@ -99,18 +165,32 @@ impl IVerge {
pub fn template() -> Self {
Self {
clash_core: Some("clash".into()),
language: Some("en".into()),
clash_core: Some("clash-meta".into()),
language: Some("zh".into()),
theme_mode: Some("system".into()),
theme_blur: Some(false),
#[cfg(not(target_os = "windows"))]
env_type: Some("bash".into()),
#[cfg(target_os = "windows")]
env_type: Some("powershell".into()),
start_page: Some("/".into()),
traffic_graph: Some(true),
enable_memory_usage: Some(true),
enable_group_icon: Some(true),
common_tray_icon: Some(false),
sysproxy_tray_icon: Some(false),
tun_tray_icon: Some(false),
enable_auto_launch: Some(false),
enable_silent_start: Some(false),
enable_system_proxy: Some(false),
enable_random_port: Some(false),
verge_mixed_port: Some(7897),
verge_socks_port: Some(7898),
verge_port: Some(7899),
enable_proxy_guard: Some(false),
proxy_guard_duration: Some(30),
auto_close_connection: Some(true),
enable_builtin_enhanced: Some(true),
auto_log_clean: Some(3),
..Self::default()
}
}
@@ -131,15 +211,28 @@ impl IVerge {
};
}
patch!(app_log_level);
patch!(language);
patch!(theme_mode);
patch!(theme_blur);
patch!(tray_event);
patch!(env_type);
patch!(start_page);
patch!(startup_script);
patch!(traffic_graph);
patch!(enable_memory_usage);
patch!(enable_group_icon);
patch!(common_tray_icon);
patch!(sysproxy_tray_icon);
patch!(tun_tray_icon);
patch!(enable_tun_mode);
patch!(enable_service_mode);
patch!(enable_auto_launch);
patch!(enable_silent_start);
patch!(enable_random_port);
patch!(verge_mixed_port);
patch!(verge_socks_port);
patch!(verge_port);
patch!(enable_system_proxy);
patch!(enable_proxy_guard);
patch!(system_proxy_bypass);
@@ -152,7 +245,13 @@ impl IVerge {
patch!(auto_close_connection);
patch!(default_latency_test);
patch!(default_latency_timeout);
patch!(enable_builtin_enhanced);
patch!(proxy_layout_column);
patch!(test_list);
patch!(auto_log_clean);
patch!(window_size_position);
patch!(window_is_maximized);
}
/// 在初始化前尝试拿到单例端口的值
@@ -167,4 +266,21 @@ impl IVerge {
Err(_) => SERVER_PORT, // 这里就不log错误了
}
}
/// 获取日志等级
pub fn get_log_level(&self) -> LevelFilter {
if let Some(level) = self.app_log_level.as_ref() {
match level.to_lowercase().as_str() {
"silent" => LevelFilter::Off,
"error" => LevelFilter::Error,
"warn" => LevelFilter::Warn,
"info" => LevelFilter::Info,
"debug" => LevelFilter::Debug,
"trace" => LevelFilter::Trace,
_ => LevelFilter::Info,
}
} else {
LevelFilter::Info
}
}
}
+40 -6
View File
@@ -1,6 +1,7 @@
use crate::config::Config;
use anyhow::{bail, Result};
use reqwest::header::HeaderMap;
use serde::{Deserialize, Serialize};
use serde_yaml::Mapping;
use std::collections::HashMap;
@@ -19,7 +20,7 @@ pub async fn put_configs(path: &str) -> Result<()> {
match response.status().as_u16() {
204 => Ok(()),
status @ _ => {
status => {
bail!("failed to put configs with status \"{status}\"")
}
}
@@ -36,6 +37,36 @@ pub async fn patch_configs(config: &Mapping) -> Result<()> {
Ok(())
}
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct DelayRes {
delay: u64,
}
/// GET /proxies/{name}/delay
/// 获取代理延迟
pub async fn get_proxy_delay(
name: String,
test_url: Option<String>,
timeout: i32,
) -> Result<DelayRes> {
let (url, headers) = clash_client_info()?;
let url = format!("{url}/proxies/{name}/delay");
let default_url = "http://1.1.1.1";
let test_url = test_url
.map(|s| if s.is_empty() { default_url.into() } else { s })
.unwrap_or(default_url.into());
let client = reqwest::ClientBuilder::new().no_proxy().build()?;
let builder = client
.get(&url)
.headers(headers)
.query(&[("timeout", &format!("{timeout}")), ("url", &test_url)]);
let response = builder.send().await?;
Ok(response.json::<DelayRes>().await?)
}
/// 根据clash info获取clash服务地址和请求头
fn clash_client_info() -> Result<(String, HeaderMap)> {
let client = { Config::clash().data().get_client_info() };
@@ -55,10 +86,13 @@ fn clash_client_info() -> Result<(String, HeaderMap)> {
/// 缩短clash的日志
pub fn parse_log(log: String) -> String {
if log.starts_with("time=") {
return (&log[33..]).to_owned();
if log.starts_with("time=") && log.len() > 33 {
return (log[33..]).to_owned();
}
(&log[9..]).to_owned()
if log.len() > 9 {
return (log[9..]).to_owned();
}
log
}
/// 缩短clash -t的错误输出
@@ -75,7 +109,7 @@ pub fn parse_check_output(log: String) -> String {
};
if mr > m {
return (&log[e..mr]).to_owned();
return (log[e..mr]).to_owned();
}
}
@@ -83,7 +117,7 @@ pub fn parse_check_output(log: String) -> String {
let r = log.find("path=").or(Some(log.len()));
if let (Some(l), Some(r)) = (l, r) {
return (&log[(l + 6)..(r - 1)]).to_owned();
return (log[(l + 6)..(r - 1)]).to_owned();
}
log
+62 -16
View File
@@ -5,7 +5,7 @@ use anyhow::{bail, Context, Result};
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use std::{fs, io::Write, sync::Arc, time::Duration};
use sysinfo::{Pid, PidExt, ProcessExt, System, SystemExt};
use sysinfo::{Pid, System};
use tauri::api::process::{Command, CommandChild, CommandEvent};
use tokio::time::sleep;
@@ -35,12 +35,12 @@ impl CoreManager {
.map(|pid| {
let mut system = System::new();
system.refresh_all();
system.process(Pid::from_u32(pid)).map(|proc| {
if let Some(proc) = system.process(Pid::from_u32(pid)) {
if proc.name().contains("clash") {
log::debug!(target: "app", "kill old clash process");
proc.kill();
}
});
}
});
tauri::async_runtime::spawn(async {
@@ -51,7 +51,7 @@ impl CoreManager {
Ok(())
}
/// 检查配置是否正确
/// 检查订阅是否正确
pub fn check_config(&self) -> Result<()> {
let config_path = Config::generate_file(ConfigType::Check)?;
let config_path = dirs::path_to_str(&config_path)?;
@@ -68,6 +68,10 @@ impl CoreManager {
if !output.status.success() {
let error = clash_api::parse_check_output(output.stdout.clone());
let error = match !error.is_empty() {
true => error,
false => output.stdout.clone(),
};
Logger::global().set_log(output.stdout);
bail!("{error}");
}
@@ -106,7 +110,7 @@ impl CoreManager {
use super::win_service;
// 服务模式
let enable = { Config::verge().latest().enable_service_mode.clone() };
let enable = { Config::verge().latest().enable_service_mode };
let enable = enable.unwrap_or(false);
*self.use_service_mode.lock() = enable;
@@ -136,12 +140,13 @@ impl CoreManager {
let clash_core = { Config::verge().latest().clash_core.clone() };
let clash_core = clash_core.unwrap_or("clash".into());
let is_clash = clash_core == "clash";
let config_path = dirs::path_to_str(&config_path)?;
// fix #212
let args = match clash_core.as_str() {
"clash-meta" => vec!["-m", "-d", app_dir, "-f", config_path],
"clash-meta" => vec!["-d", app_dir, "-f", config_path],
"clash-meta-alpha" => vec!["-d", app_dir, "-f", config_path],
_ => vec!["-d", app_dir, "-f", config_path],
};
@@ -161,18 +166,23 @@ impl CoreManager {
let mut sidecar = self.sidecar.lock();
*sidecar = Some(cmd_child);
drop(sidecar);
tauri::async_runtime::spawn(async move {
while let Some(event) = rx.recv().await {
match event {
CommandEvent::Stdout(line) => {
let stdout = clash_api::parse_log(line.clone());
log::info!(target: "app", "[clash]: {stdout}");
if is_clash {
let stdout = clash_api::parse_log(line.clone());
log::info!(target: "app", "[clash]: {stdout}");
} else {
log::info!(target: "app", "[clash]: {line}");
};
Logger::global().set_log(line);
}
CommandEvent::Stderr(err) => {
let stdout = clash_api::parse_log(err.clone());
log::error!(target: "app", "[clash]: {stdout}");
// let stdout = clash_api::parse_log(err.clone());
log::error!(target: "app", "[clash]: {err}");
Logger::global().set_log(err);
}
CommandEvent::Error(err) => {
@@ -181,6 +191,7 @@ impl CoreManager {
}
CommandEvent::Terminated(_) => {
log::info!(target: "app", "clash core terminated");
let _ = CoreManager::global().recover_core();
break;
}
_ => {}
@@ -191,6 +202,40 @@ impl CoreManager {
Ok(())
}
/// 重启内核
pub fn recover_core(&'static self) -> Result<()> {
// 服务模式不管
#[cfg(target_os = "windows")]
if *self.use_service_mode.lock() {
return Ok(());
}
// 清空原来的sidecar值
if let Some(sidecar) = self.sidecar.lock().take() {
let _ = sidecar.kill();
}
tauri::async_runtime::spawn(async move {
// 6秒之后再查看服务是否正常 (时间随便搞的)
// terminated 可能是切换内核 (切换内核已经有500ms的延迟)
sleep(Duration::from_millis(6666)).await;
if self.sidecar.lock().is_none() {
log::info!(target: "app", "recover clash core");
// 重新启动app
if let Err(err) = self.run_core().await {
log::error!(target: "app", "failed to recover clash core");
log::error!(target: "app", "{err}");
let _ = self.recover_core();
}
}
});
Ok(())
}
/// 停止核心运行
pub fn stop_core(&self) -> Result<()> {
#[cfg(target_os = "windows")]
@@ -213,8 +258,9 @@ impl CoreManager {
/// 切换核心
pub async fn change_core(&self, clash_core: Option<String>) -> Result<()> {
let clash_core = clash_core.ok_or(anyhow::anyhow!("clash core is null"))?;
const CLASH_CORES: [&str; 2] = ["clash-meta", "clash-meta-alpha"];
if &clash_core != "clash" && &clash_core != "clash-meta" {
if !CLASH_CORES.contains(&clash_core.as_str()) {
bail!("invalid clash core name \"{clash_core}\"");
}
@@ -222,7 +268,7 @@ impl CoreManager {
Config::verge().draft().clash_core = Some(clash_core);
// 更新配置
// 更新订阅
Config::generate()?;
self.check_config()?;
@@ -250,13 +296,13 @@ impl CoreManager {
pub async fn update_config(&self) -> Result<()> {
log::debug!(target: "app", "try to update clash config");
// 更新配置
// 更新订阅
Config::generate()?;
// 检查配置是否正常
// 检查订阅是否正常
self.check_config()?;
// 更新运行时配置
// 更新运行时订阅
let path = Config::generate_file(ConfigType::Run)?;
let path = dirs::path_to_str(&path)?;
+1 -1
View File
@@ -28,7 +28,7 @@ impl Handle {
self.app_handle
.lock()
.as_ref()
.map_or(None, |a| a.get_window("main"))
.and_then(|a| a.get_window("main"))
}
pub fn refresh_clash() {
+6 -26
View File
@@ -4,7 +4,6 @@ use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use std::{collections::HashMap, sync::Arc};
use tauri::{AppHandle, GlobalShortcutManager};
use tauri_runtime_wry::wry::application::accelerator::Accelerator;
pub struct Hotkey {
current: Arc<Mutex<Vec<String>>>, // 保存当前的热键设置
@@ -35,7 +34,7 @@ impl Hotkey {
match (key, func) {
(Some(key), Some(func)) => {
log_err!(Self::check_key(key).and_then(|_| self.register(key, func)));
log_err!(self.register(key, func));
}
_ => {
let key = key.unwrap_or("None");
@@ -50,16 +49,6 @@ impl Hotkey {
Ok(())
}
/// 检查一个键是否合法
fn check_key(hotkey: &str) -> Result<()> {
// fix #287
// tauri的这几个方法全部有Result expect,会panic,先检测一遍避免挂了
if hotkey.parse::<Accelerator>().is_err() {
bail!("invalid hotkey `{hotkey}`");
}
Ok(())
}
fn get_manager(&self) -> Result<impl GlobalShortcutManager> {
let app_handle = self.app_handle.lock();
if app_handle.is_none() {
@@ -76,16 +65,12 @@ impl Hotkey {
}
let f = match func.trim() {
"open_or_close_dashboard" => feat::open_or_close_dashboard,
"clash_mode_rule" => || feat::change_clash_mode("rule".into()),
"clash_mode_global" => || feat::change_clash_mode("global".into()),
"clash_mode_direct" => || feat::change_clash_mode("direct".into()),
"clash_mode_script" => || feat::change_clash_mode("script".into()),
"toggle_system_proxy" => || feat::toggle_system_proxy(),
"enable_system_proxy" => || feat::enable_system_proxy(),
"disable_system_proxy" => || feat::disable_system_proxy(),
"toggle_tun_mode" => || feat::toggle_tun_mode(),
"enable_tun_mode" => || feat::enable_tun_mode(),
"disable_tun_mode" => || feat::disable_tun_mode(),
"toggle_system_proxy" => feat::toggle_system_proxy,
"toggle_tun_mode" => feat::toggle_tun_mode,
_ => bail!("invalid function \"{func}\""),
};
@@ -96,7 +81,7 @@ impl Hotkey {
}
fn unregister(&self, hotkey: &str) -> Result<()> {
self.get_manager()?.unregister(&hotkey)?;
self.get_manager()?.unregister(hotkey)?;
log::info!(target: "app", "unregister hotkey {hotkey}");
Ok(())
}
@@ -108,11 +93,6 @@ impl Hotkey {
let (del, add) = Self::get_diff(old_map, new_map);
// 先检查一遍所有新的热键是不是可以用的
for (hotkey, _) in add.iter() {
Self::check_key(hotkey)?;
}
del.iter().for_each(|key| {
let _ = self.unregister(key);
});
@@ -125,7 +105,7 @@ impl Hotkey {
Ok(())
}
fn get_map_from_vec<'a>(hotkeys: &'a Vec<String>) -> HashMap<&'a str, &'a str> {
fn get_map_from_vec(hotkeys: &Vec<String>) -> HashMap<&str, &str> {
let mut map = HashMap::new();
hotkeys.iter().for_each(|hotkey| {
+47
View File
@@ -0,0 +1,47 @@
/// 给clash内核的tun模式授权
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn grant_permission(core: String) -> anyhow::Result<()> {
use std::process::Command;
use tauri::utils::platform::current_exe;
let path = current_exe()?.with_file_name(core).canonicalize()?;
let path = path.display().to_string();
log::debug!("grant_permission path: {path}");
#[cfg(target_os = "macos")]
let output = {
let path = path.replace(' ', "\\\\ ");
let shell = format!("chown root:admin {path}\nchmod +sx {path}");
let command = format!(r#"do shell script "{shell}" with administrator privileges"#);
Command::new("osascript")
.args(vec!["-e", &command])
.output()?
};
#[cfg(target_os = "linux")]
let output = {
let path = path.replace(' ', "\\ "); // 避免路径中有空格
let shell = format!("setcap cap_net_bind_service,cap_net_admin=+ep {path}");
let sudo = match Command::new("which").arg("pkexec").output() {
Ok(output) => {
if output.stdout.is_empty() {
"sudo"
} else {
"pkexec"
}
}
Err(_) => "sudo",
};
Command::new(sudo).arg("sh").arg("-c").arg(shell).output()?
};
if output.status.success() {
Ok(())
} else {
let stderr = std::str::from_utf8(&output.stderr).unwrap_or("");
anyhow::bail!("{stderr}");
}
}
+2
View File
@@ -3,9 +3,11 @@ mod core;
pub mod handle;
pub mod hotkey;
pub mod logger;
pub mod manager;
pub mod sysopt;
pub mod timer;
pub mod tray;
pub mod win_service;
pub mod win_uwp;
pub use self::core::*;
+45 -28
View File
@@ -23,11 +23,12 @@ pub struct Sysopt {
}
#[cfg(target_os = "windows")]
static DEFAULT_BYPASS: &str = "localhost;127.*;192.168.*;<local>";
static DEFAULT_BYPASS: &str = "localhost;127.*;192.168.*;10.*;172.16.*;<local>";
#[cfg(target_os = "linux")]
static DEFAULT_BYPASS: &str = "localhost,127.0.0.1/8,::1";
static DEFAULT_BYPASS: &str = "localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,::1";
#[cfg(target_os = "macos")]
static DEFAULT_BYPASS: &str = "127.0.0.1,localhost,<local>";
static DEFAULT_BYPASS: &str =
"127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,localhost,*.local,*.crashlytics.com,<local>";
impl Sysopt {
pub fn global() -> &'static Sysopt {
@@ -43,13 +44,16 @@ impl Sysopt {
/// init the sysproxy
pub fn init_sysproxy(&self) -> Result<()> {
let port = { Config::clash().latest().get_mixed_port() };
let port = Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
let (enable, bypass) = {
let verge = Config::verge();
let verge = verge.latest();
(
verge.enable_system_proxy.clone().unwrap_or(false),
verge.enable_system_proxy.unwrap_or(false),
verge.system_proxy_bypass.clone(),
)
};
@@ -62,7 +66,7 @@ impl Sysopt {
};
if enable {
let old = Sysproxy::get_system_proxy().map_or(None, |p| Some(p));
let old = Sysproxy::get_system_proxy().ok();
current.set_system_proxy()?;
*self.old_sysproxy.lock() = old;
@@ -89,7 +93,7 @@ impl Sysopt {
let verge = Config::verge();
let verge = verge.latest();
(
verge.enable_system_proxy.clone().unwrap_or(false),
verge.enable_system_proxy.unwrap_or(false),
verge.system_proxy_bypass.clone(),
)
};
@@ -98,6 +102,12 @@ impl Sysopt {
sysproxy.enable = enable;
sysproxy.bypass = bypass.unwrap_or(DEFAULT_BYPASS.into());
let port = Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
sysproxy.port = port;
sysproxy.set_system_proxy()?;
*cur_sysproxy = Some(sysproxy);
@@ -138,9 +148,6 @@ impl Sysopt {
/// init the auto launch
pub fn init_launch(&self) -> Result<()> {
let enable = { Config::verge().latest().enable_auto_launch.clone() };
let enable = enable.unwrap_or(false);
let app_exe = current_exe()?;
let app_exe = dunce::canonicalize(app_exe)?;
let app_name = app_exe
@@ -171,24 +178,29 @@ impl Sysopt {
})()
.unwrap_or(app_path);
// fix #403
#[cfg(target_os = "linux")]
let app_path = {
use crate::core::handle::Handle;
use tauri::Manager;
let handle = Handle::global();
match handle.app_handle.lock().as_ref() {
Some(app_handle) => {
let appimage = app_handle.env().appimage;
appimage
.and_then(|p| p.to_str().map(|s| s.to_string()))
.unwrap_or(app_path)
}
None => app_path,
}
};
let auto = AutoLaunchBuilder::new()
.set_app_name(app_name)
.set_app_path(&app_path)
.build()?;
// 避免在开发时将自启动关了
#[cfg(feature = "verge-dev")]
if !enable {
return Ok(());
}
// macos每次启动都更新登录项,避免重复设置登录项
#[cfg(target_os = "macos")]
let _ = auto.disable();
if enable {
auto.enable()?;
}
*self.auto_launch.lock() = Some(auto);
Ok(())
@@ -202,7 +214,7 @@ impl Sysopt {
drop(auto_launch);
return self.init_launch();
}
let enable = { Config::verge().latest().enable_auto_launch.clone() };
let enable = { Config::verge().latest().enable_auto_launch };
let enable = enable.unwrap_or(false);
let auto_launch = auto_launch.as_ref().unwrap();
@@ -240,9 +252,9 @@ impl Sysopt {
let verge = Config::verge();
let verge = verge.latest();
(
verge.enable_system_proxy.clone().unwrap_or(false),
verge.enable_proxy_guard.clone().unwrap_or(false),
verge.proxy_guard_duration.clone().unwrap_or(10),
verge.enable_system_proxy.unwrap_or(false),
verge.enable_proxy_guard.unwrap_or(false),
verge.proxy_guard_duration.unwrap_or(10),
verge.system_proxy_bypass.clone(),
)
};
@@ -257,7 +269,12 @@ impl Sysopt {
log::debug!(target: "app", "try to guard the system proxy");
let port = { Config::clash().latest().get_mixed_port() };
let port = {
Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port())
};
let sysproxy = Sysproxy {
enable: true,
+2 -2
View File
@@ -40,7 +40,7 @@ impl Timer {
let timer_map = self.timer_map.lock();
let delay_timer = self.delay_timer.lock();
Config::profiles().latest().get_items().map(|items| {
if let Some(items) = Config::profiles().latest().get_items() {
items
.iter()
.filter_map(|item| {
@@ -61,7 +61,7 @@ impl Timer {
}
}
})
});
}
Ok(())
}
+169 -60
View File
@@ -1,4 +1,9 @@
use crate::{config::Config, feat, utils::resolve};
use crate::{
cmds,
config::Config,
feat,
utils::{dirs, resolve},
};
use anyhow::Result;
use tauri::{
api, AppHandle, CustomMenuItem, Manager, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem,
@@ -13,53 +18,77 @@ impl Tray {
let version = app_handle.package_info().version.to_string();
if zh {
SystemTrayMenu::new()
.add_item(CustomMenuItem::new("open_window", "打开面板"))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new("rule_mode", "规则模式"))
.add_item(CustomMenuItem::new("global_mode", "全局模式"))
.add_item(CustomMenuItem::new("direct_mode", "直连模式"))
.add_item(CustomMenuItem::new("script_mode", "脚本模式"))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new("system_proxy", "系统代理"))
.add_item(CustomMenuItem::new("tun_mode", "TUN 模式"))
.add_submenu(SystemTraySubmenu::new(
"更多",
SystemTrayMenu::new()
.add_item(CustomMenuItem::new("restart_clash", "重启 Clash"))
.add_item(CustomMenuItem::new("restart_app", "重启应用"))
.add_item(
CustomMenuItem::new("app_version", format!("Version {version}"))
.disabled(),
),
))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new("quit", "退出").accelerator("CmdOrControl+Q"))
} else {
SystemTrayMenu::new()
.add_item(CustomMenuItem::new("open_window", "Dashboard"))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new("rule_mode", "Rule Mode"))
.add_item(CustomMenuItem::new("global_mode", "Global Mode"))
.add_item(CustomMenuItem::new("direct_mode", "Direct Mode"))
.add_item(CustomMenuItem::new("script_mode", "Script Mode"))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new("system_proxy", "System Proxy"))
.add_item(CustomMenuItem::new("tun_mode", "Tun Mode"))
.add_submenu(SystemTraySubmenu::new(
"More",
SystemTrayMenu::new()
.add_item(CustomMenuItem::new("restart_clash", "Restart Clash"))
.add_item(CustomMenuItem::new("restart_app", "Restart App"))
.add_item(
CustomMenuItem::new("app_version", format!("Version {version}"))
.disabled(),
),
))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new("quit", "Quit").accelerator("CmdOrControl+Q"))
macro_rules! t {
($en: expr, $zh: expr) => {
if zh {
$zh
} else {
$en
}
};
}
SystemTrayMenu::new()
.add_item(CustomMenuItem::new(
"open_window",
t!("Dashboard", "打开面板"),
))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new(
"rule_mode",
t!("Rule Mode", "规则模式"),
))
.add_item(CustomMenuItem::new(
"global_mode",
t!("Global Mode", "全局模式"),
))
.add_item(CustomMenuItem::new(
"direct_mode",
t!("Direct Mode", "直连模式"),
))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new(
"system_proxy",
t!("System Proxy", "系统代理"),
))
.add_item(CustomMenuItem::new("tun_mode", t!("TUN Mode", "Tun 模式")))
.add_item(CustomMenuItem::new(
"copy_env",
t!("Copy Env", "复制环境变量"),
))
.add_submenu(SystemTraySubmenu::new(
t!("Open Dir", "打开目录"),
SystemTrayMenu::new()
.add_item(CustomMenuItem::new(
"open_app_dir",
t!("App Dir", "应用目录"),
))
.add_item(CustomMenuItem::new(
"open_core_dir",
t!("Core Dir", "内核目录"),
))
.add_item(CustomMenuItem::new(
"open_logs_dir",
t!("Logs Dir", "日志目录"),
)),
))
.add_submenu(SystemTraySubmenu::new(
t!("More", "更多"),
SystemTrayMenu::new()
.add_item(CustomMenuItem::new(
"restart_clash",
t!("Restart Clash", "重启 Clash"),
))
.add_item(CustomMenuItem::new(
"restart_app",
t!("Restart App", "重启应用"),
))
.add_item(
CustomMenuItem::new("app_version", format!("Version {version}")).disabled(),
),
))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new("quit", t!("Quit", "退出")).accelerator("CmdOrControl+Q"))
}
pub fn update_systray(app_handle: &AppHandle) -> Result<()> {
@@ -71,6 +100,20 @@ impl Tray {
}
pub fn update_part(app_handle: &AppHandle) -> Result<()> {
let zh = { Config::verge().latest().language == Some("zh".into()) };
let version = app_handle.package_info().version.to_string();
macro_rules! t {
($en: expr, $zh: expr) => {
if zh {
$zh
} else {
$en
}
};
}
let mode = {
Config::clash()
.latest()
@@ -86,44 +129,110 @@ impl Tray {
let _ = tray.get_item("rule_mode").set_selected(mode == "rule");
let _ = tray.get_item("global_mode").set_selected(mode == "global");
let _ = tray.get_item("direct_mode").set_selected(mode == "direct");
let _ = tray.get_item("script_mode").set_selected(mode == "script");
let verge = Config::verge();
let verge = verge.latest();
let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
let common_tray_icon = verge.common_tray_icon.as_ref().unwrap_or(&false);
let sysproxy_tray_icon = verge.sysproxy_tray_icon.as_ref().unwrap_or(&false);
let tun_tray_icon = verge.tun_tray_icon.as_ref().unwrap_or(&false);
let mut indication_icon = if *system_proxy {
#[cfg(not(target_os = "macos"))]
let mut icon = include_bytes!("../../icons/tray-icon-sys.png").to_vec();
#[cfg(target_os = "macos")]
let mut icon = include_bytes!("../../icons/mac-tray-icon-sys.png").to_vec();
if *sysproxy_tray_icon {
let path = dirs::app_home_dir()?.join("icons").join("sysproxy.png");
if path.exists() {
icon = std::fs::read(path).unwrap();
}
}
icon
} else {
#[cfg(not(target_os = "macos"))]
let mut icon = include_bytes!("../../icons/tray-icon.png").to_vec();
#[cfg(target_os = "macos")]
let mut icon = include_bytes!("../../icons/mac-tray-icon.png").to_vec();
if *common_tray_icon {
let path = dirs::app_home_dir()?.join("icons").join("common.png");
if path.exists() {
icon = std::fs::read(path).unwrap();
}
}
icon
};
if *tun_mode {
#[cfg(not(target_os = "macos"))]
let mut icon = include_bytes!("../../icons/tray-icon-tun.png").to_vec();
#[cfg(target_os = "macos")]
let mut icon = include_bytes!("../../icons/mac-tray-icon-tun.png").to_vec();
if *tun_tray_icon {
let path = dirs::app_home_dir()?.join("icons").join("tun.png");
if path.exists() {
icon = std::fs::read(path).unwrap();
}
}
indication_icon = icon
}
let _ = tray.set_icon(tauri::Icon::Raw(indication_icon));
let _ = tray.get_item("system_proxy").set_selected(*system_proxy);
let _ = tray.get_item("tun_mode").set_selected(*tun_mode);
let switch_map = {
let mut map = std::collections::HashMap::new();
map.insert(true, "on");
map.insert(false, "off");
map
};
let _ = tray.set_tooltip(&format!(
"Clash Verge {version}\n{}: {}\n{}: {}",
t!("System Proxy", "系统代理"),
switch_map[system_proxy],
t!("TUN Mode", "Tun 模式"),
switch_map[tun_mode]
));
Ok(())
}
pub fn on_left_click(app_handle: &AppHandle) {
let tray_event = { Config::verge().latest().tray_event.clone() };
let tray_event = tray_event.unwrap_or("main_window".into());
match tray_event.as_str() {
"system_proxy" => feat::toggle_system_proxy(),
"tun_mode" => feat::toggle_tun_mode(),
"main_window" => resolve::create_window(app_handle),
_ => {}
}
}
pub fn on_system_tray_event(app_handle: &AppHandle, event: SystemTrayEvent) {
match event {
SystemTrayEvent::LeftClick { .. } => Tray::on_left_click(app_handle),
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
mode @ ("rule_mode" | "global_mode" | "direct_mode" | "script_mode") => {
mode @ ("rule_mode" | "global_mode" | "direct_mode") => {
let mode = &mode[0..mode.len() - 5];
feat::change_clash_mode(mode.into());
}
"open_window" => resolve::create_window(app_handle),
"system_proxy" => feat::toggle_system_proxy(),
"tun_mode" => feat::toggle_tun_mode(),
"copy_env" => feat::copy_clash_env(app_handle),
"open_app_dir" => crate::log_err!(cmds::open_app_dir()),
"open_core_dir" => crate::log_err!(cmds::open_core_dir()),
"open_logs_dir" => crate::log_err!(cmds::open_logs_dir()),
"restart_clash" => feat::restart_clash_core(),
"restart_app" => api::process::restart(&app_handle.env()),
"quit" => {
resolve::resolve_reset();
api::process::kill_children();
app_handle.exit(0);
std::process::exit(0);
}
"quit" => cmds::exit_app(app_handle.clone()),
_ => {}
},
#[cfg(target_os = "windows")]
SystemTrayEvent::LeftClick { .. } => {
resolve::create_window(app_handle);
}
_ => {}
}
}
+26
View File
@@ -0,0 +1,26 @@
#![cfg(target_os = "windows")]
use crate::utils::dirs;
use anyhow::{bail, Result};
use deelevate::{PrivilegeLevel, Token};
use runas::Command as RunasCommand;
use std::process::Command as StdCommand;
pub async fn invoke_uwptools() -> Result<()> {
let resource_dir = dirs::app_resources_dir()?;
let tool_path = resource_dir.join("enableLoopback.exe");
if !tool_path.exists() {
bail!("enableLoopback exe not found");
}
let token = Token::with_current_process()?;
let level = token.privilege_level()?;
match level {
PrivilegeLevel::NotPrivileged => RunasCommand::new(tool_path).status()?,
_ => StdCommand::new(tool_path).status()?,
};
Ok(())
}
+12
View File
@@ -21,6 +21,7 @@ pub enum ChainType {
pub enum ChainSupport {
Clash,
ClashMeta,
ClashMetaAlpha,
All,
}
@@ -60,9 +61,19 @@ impl ChainItem {
let hy_alpn =
ChainItem::to_script("verge_hy_alpn", include_str!("./builtin/meta_hy_alpn.js"));
// meta 的一些处理
let meta_guard_alpha =
ChainItem::to_script("verge_meta_guard", include_str!("./builtin/meta_guard.js"));
// meta 1.13.2 alpn string 转 数组
let hy_alpn_alpha =
ChainItem::to_script("verge_hy_alpn", include_str!("./builtin/meta_hy_alpn.js"));
vec![
(ChainSupport::ClashMeta, hy_alpn),
(ChainSupport::ClashMeta, meta_guard),
(ChainSupport::ClashMetaAlpha, hy_alpn_alpha),
(ChainSupport::ClashMetaAlpha, meta_guard_alpha),
]
}
@@ -81,6 +92,7 @@ impl ChainSupport {
(ChainSupport::All, _) => true,
(ChainSupport::Clash, "clash") => true,
(ChainSupport::ClashMeta, "clash-meta") => true,
(ChainSupport::ClashMetaAlpha, "clash-meta-alpha") => true,
_ => false,
},
None => true,
+36 -62
View File
@@ -1,69 +1,26 @@
use serde_yaml::{Mapping, Value};
use std::collections::HashSet;
pub const HANDLE_FIELDS: [&str; 9] = [
"port",
"socks-port",
"mixed-port",
"mode",
"ipv6",
"log-level",
"mixed-port",
"socks-port",
"port",
"allow-lan",
"external-controller",
"log-level",
"ipv6",
"secret",
"external-controller",
];
pub const DEFAULT_FIELDS: [&str; 5] = [
"proxies",
"proxy-groups",
"rules",
"proxy-providers",
"proxy-groups",
"rule-providers",
"rules",
];
pub const OTHERS_FIELDS: [&str; 21] = [
"tun",
"dns",
"ebpf",
"hosts",
"script",
"profile",
"payload",
"auto-redir",
"experimental",
"interface-name",
"routing-mark",
"redir-port",
"tproxy-port",
"iptables",
"external-ui",
"bind-address",
"authentication",
"sniffer", // meta
"sub-rules", // meta
"geodata-mode", // meta
"tcp-concurrent", // meta
];
pub fn use_clash_fields() -> Vec<String> {
DEFAULT_FIELDS
.into_iter()
.chain(HANDLE_FIELDS)
.chain(OTHERS_FIELDS)
.map(|s| s.to_string())
.collect()
}
pub fn use_valid_fields(mut valid: Vec<String>) -> Vec<String> {
let others = Vec::from(OTHERS_FIELDS);
valid.iter_mut().for_each(|s| s.make_ascii_lowercase());
valid
.into_iter()
.filter(|s| others.contains(&s.as_str()))
.chain(DEFAULT_FIELDS.iter().map(|s| s.to_string()))
.collect()
}
pub fn use_filter(config: Mapping, filter: &Vec<String>) -> Mapping {
let mut ret = Mapping::new();
@@ -92,17 +49,34 @@ pub fn use_lowercase(config: Mapping) -> Mapping {
pub fn use_sort(config: Mapping) -> Mapping {
let mut ret = Mapping::new();
HANDLE_FIELDS.into_iter().for_each(|key| {
let key = Value::from(key);
if let Some(value) = config.get(&key) {
ret.insert(key, value.clone());
}
});
HANDLE_FIELDS
let supported_keys: HashSet<&str> = HANDLE_FIELDS.into_iter().chain(DEFAULT_FIELDS).collect();
let config_keys: HashSet<&str> = config
.keys()
.filter_map(|e| e.as_str())
.into_iter()
.chain(OTHERS_FIELDS)
.chain(DEFAULT_FIELDS)
.for_each(|key| {
let key = Value::from(key);
config.get(&key).map(|value| {
ret.insert(key, value.clone());
});
});
.collect();
config_keys.difference(&supported_keys).for_each(|&key| {
let key = Value::from(key);
if let Some(value) = config.get(&key) {
ret.insert(key, value.clone());
}
});
DEFAULT_FIELDS.into_iter().for_each(|key| {
let key = Value::from(key);
if let Some(value) = config.get(&key) {
ret.insert(key, value.clone());
}
});
ret
}
@@ -113,7 +87,7 @@ pub fn use_keys(config: &Mapping) -> Vec<String> {
.map(|s| {
let mut s = s.to_string();
s.make_ascii_lowercase();
return s;
s
})
.collect()
}
+42 -3
View File
@@ -1,12 +1,15 @@
use super::{use_filter, use_lowercase};
use serde_yaml::{self, Mapping, Sequence, Value};
#[allow(unused)]
const MERGE_FIELDS: [&str; 6] = [
const MERGE_FIELDS: [&str; 10] = [
"prepend-rules",
"append-rules",
"prepend-rule-providers",
"append-rule-providers",
"prepend-proxies",
"append-proxies",
"prepend-proxy-providers",
"append-proxy-providers",
"prepend-proxy-groups",
"append-proxy-groups",
];
@@ -15,6 +18,7 @@ pub fn use_merge(merge: Mapping, mut config: Mapping) -> Mapping {
// 直接覆盖原字段
use_lowercase(merge.clone())
.into_iter()
.filter(|(key, _)| !MERGE_FIELDS.contains(&key.as_str().unwrap_or_default()))
.for_each(|(key, value)| {
config.insert(key, value);
});
@@ -22,6 +26,39 @@ pub fn use_merge(merge: Mapping, mut config: Mapping) -> Mapping {
let merge_list = MERGE_FIELDS.iter().map(|s| s.to_string());
let merge = use_filter(merge, &merge_list.collect());
["rule-providers", "proxy-providers"]
.iter()
.for_each(|key_str| {
let key_val = Value::from(key_str.to_string());
let mut map = Mapping::default();
map = config.get(&key_val).map_or(map.clone(), |val| {
val.as_mapping().map_or(map, |v| v.clone())
});
let pre_key = Value::from(format!("prepend-{key_str}"));
let post_key = Value::from(format!("append-{key_str}"));
if let Some(pre_val) = merge.get(&pre_key) {
if pre_val.is_mapping() {
let mut pre_val = pre_val.as_mapping().unwrap().clone();
pre_val.extend(map);
map = pre_val;
}
}
if let Some(post_val) = merge.get(&post_key) {
if post_val.is_mapping() {
map.extend(post_val.as_mapping().unwrap().clone());
}
}
if !map.is_empty() {
config.insert(key_val, Value::from(map));
}
});
["rules", "proxies", "proxy-groups"]
.iter()
.for_each(|key_str| {
@@ -49,7 +86,9 @@ pub fn use_merge(merge: Mapping, mut config: Mapping) -> Mapping {
}
}
config.insert(key_val, Value::from(list));
if !list.is_empty() {
config.insert(key_val, Value::from(list));
}
});
config
}
+28 -27
View File
@@ -1,12 +1,11 @@
mod chain;
mod field;
pub mod field;
mod merge;
mod script;
mod tun;
pub(self) use self::field::*;
use self::chain::*;
use self::field::*;
use self::merge::*;
use self::script::*;
use self::tun::*;
@@ -18,54 +17,48 @@ use std::collections::HashSet;
type ResultLog = Vec<(String, String)>;
/// Enhance mode
/// 返回最终配置、该配置包含的键、和script执行的结果
/// 返回最终订阅、该订阅包含的键、和script执行的结果
pub fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
// config.yaml 的配置
// config.yaml 的订阅
let clash_config = { Config::clash().latest().0.clone() };
let (clash_core, tun_mode, enable_builtin) = {
let (clash_core, enable_tun, enable_builtin) = {
let verge = Config::verge();
let verge = verge.latest();
(
verge.clash_core.clone(),
verge.enable_tun_mode.clone(),
verge.enable_builtin_enhanced.clone(),
verge.enable_tun_mode.unwrap_or(false),
verge.enable_builtin_enhanced.unwrap_or(true),
)
};
// 从profiles里拿东西
let (mut config, chain, valid) = {
let (mut config, chain) = {
let profiles = Config::profiles();
let profiles = profiles.latest();
let current = profiles.current_mapping().unwrap_or(Mapping::new());
let current = profiles.current_mapping().unwrap_or_default();
let chain = match profiles.chain.as_ref() {
Some(chain) => chain
.iter()
.filter_map(|uid| profiles.get_item(uid).ok())
.filter_map(|item| <Option<ChainItem>>::from(item))
.filter_map(<Option<ChainItem>>::from)
.collect::<Vec<ChainItem>>(),
None => vec![],
};
let valid = profiles.valid.clone().unwrap_or(vec![]);
(current, chain, valid)
(current, chain)
};
let mut result_map = HashMap::new(); // 保存脚本日志
let mut exists_keys = use_keys(&config); // 保存出现过的keys
let valid = use_valid_fields(valid);
config = use_filter(config, &valid);
// 处理用户的profile
chain.into_iter().for_each(|item| match item.data {
ChainType::Merge(merge) => {
exists_keys.extend(use_keys(&merge));
config = use_merge(merge, config.to_owned());
config = use_filter(config.to_owned(), &valid);
}
ChainType::Script(script) => {
let mut logs = vec![];
@@ -73,7 +66,7 @@ pub fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
match use_script(script, config.to_owned()) {
Ok((res_config, res_logs)) => {
exists_keys.extend(use_keys(&res_config));
config = use_filter(res_config, &valid);
config = res_config;
logs.extend(res_logs);
}
Err(err) => logs.push(("exception".into(), err.to_string())),
@@ -85,13 +78,22 @@ pub fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
// 合并默认的config
for (key, value) in clash_config.into_iter() {
config.insert(key, value);
if key.as_str() == Some("tun") {
let mut tun = config.get_mut("tun").map_or(Mapping::new(), |val| {
val.as_mapping().cloned().unwrap_or(Mapping::new())
});
let patch_tun = value.as_mapping().cloned().unwrap_or(Mapping::new());
for (key, value) in patch_tun.into_iter() {
tun.insert(key, value);
}
config.insert("tun".into(), tun.into());
} else {
config.insert(key, value);
}
}
let clash_fields = use_clash_fields();
// 内建脚本最后跑
if enable_builtin.unwrap_or(true) {
if enable_builtin {
ChainItem::builtin()
.into_iter()
.filter(|(s, _)| s.is_support(clash_core.as_ref()))
@@ -102,7 +104,7 @@ pub fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
match item.data {
ChainType::Script(script) => match use_script(script, config.to_owned()) {
Ok((res_config, _)) => {
config = use_filter(res_config, &clash_fields);
config = res_config;
}
Err(err) => {
log::error!(target: "app", "builtin script error `{err}`");
@@ -113,12 +115,11 @@ pub fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
});
}
config = use_filter(config, &clash_fields);
config = use_tun(config, tun_mode.unwrap_or(false));
config = use_tun(config, enable_tun);
config = use_sort(config);
let mut exists_set = HashSet::new();
exists_set.extend(exists_keys.into_iter().filter(|s| clash_fields.contains(s)));
exists_set.extend(exists_keys.into_iter());
exists_keys = exists_set.into_iter().collect();
(config, exists_keys, result_map)
+2 -2
View File
@@ -3,7 +3,7 @@ use anyhow::Result;
use serde_yaml::Mapping;
pub fn use_script(script: String, config: Mapping) -> Result<(Mapping, Vec<(String, String)>)> {
use rquickjs::{Context, Func, Runtime};
use rquickjs::{function::Func, Context, Runtime};
use std::sync::{Arc, Mutex};
let runtime = Runtime::new().unwrap();
@@ -47,7 +47,7 @@ pub fn use_script(script: String, config: Mapping) -> Result<(Mapping, Vec<(Stri
if result == "\"\"" {
anyhow::bail!("main function should return object");
}
return Ok(serde_json::from_str::<Mapping>(result.as_str())?);
Ok(serde_json::from_str::<Mapping>(result.as_str())?)
});
let mut out = outputs.lock().unwrap();
-6
View File
@@ -30,12 +30,6 @@ pub fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
});
revise!(tun_val, "enable", enable);
if enable {
append!(tun_val, "stack", "gvisor");
append!(tun_val, "dns-hijack", vec!["any:53"]);
append!(tun_val, "auto-route", true);
append!(tun_val, "auto-detect-interface", true);
}
revise!(config, "tun", tun_val);
+122 -73
View File
@@ -7,8 +7,25 @@
use crate::config::*;
use crate::core::*;
use crate::log_err;
use crate::utils::resolve;
use anyhow::{bail, Result};
use serde_yaml::{Mapping, Value};
use tauri::{AppHandle, ClipboardManager, Manager};
// 打开面板
pub fn open_or_close_dashboard() {
let handle = handle::Handle::global();
let app_handle = handle.app_handle.lock();
if let Some(app_handle) = app_handle.as_ref() {
if let Some(window) = app_handle.get_window("main") {
if let Ok(true) = window.is_focused() {
let _ = window.close();
return;
}
}
resolve::create_window(app_handle);
}
}
// 重启clash
pub fn restart_clash_core() {
@@ -36,7 +53,7 @@ pub fn change_clash_mode(mode: String) {
match clash_api::patch_configs(&mapping).await {
Ok(_) => {
// 更新配置
// 更新订阅
Config::clash().data().patch_config(mapping);
if Config::clash().data().save_config().is_ok() {
@@ -51,7 +68,7 @@ pub fn change_clash_mode(mode: String) {
// 切换系统代理
pub fn toggle_system_proxy() {
let enable = Config::verge().draft().enable_system_proxy.clone();
let enable = Config::verge().draft().enable_system_proxy;
let enable = enable.unwrap_or(false);
tauri::async_runtime::spawn(async move {
@@ -67,39 +84,9 @@ pub fn toggle_system_proxy() {
});
}
// 打开系统代理
pub fn enable_system_proxy() {
tauri::async_runtime::spawn(async {
match patch_verge(IVerge {
enable_system_proxy: Some(true),
..IVerge::default()
})
.await
{
Ok(_) => handle::Handle::refresh_verge(),
Err(err) => log::error!(target: "app", "{err}"),
}
});
}
// 关闭系统代理
pub fn disable_system_proxy() {
tauri::async_runtime::spawn(async {
match patch_verge(IVerge {
enable_system_proxy: Some(false),
..IVerge::default()
})
.await
{
Ok(_) => handle::Handle::refresh_verge(),
Err(err) => log::error!(target: "app", "{err}"),
}
});
}
// 切换tun模式
pub fn toggle_tun_mode() {
let enable = Config::verge().data().enable_tun_mode.clone();
let enable = Config::verge().data().enable_tun_mode;
let enable = enable.unwrap_or(false);
tauri::async_runtime::spawn(async move {
@@ -115,47 +102,24 @@ pub fn toggle_tun_mode() {
});
}
// 打开tun模式
pub fn enable_tun_mode() {
tauri::async_runtime::spawn(async {
match patch_verge(IVerge {
enable_tun_mode: Some(true),
..IVerge::default()
})
.await
{
Ok(_) => handle::Handle::refresh_verge(),
Err(err) => log::error!(target: "app", "{err}"),
}
});
}
// 关闭tun模式
pub fn disable_tun_mode() {
tauri::async_runtime::spawn(async {
match patch_verge(IVerge {
enable_tun_mode: Some(false),
..IVerge::default()
})
.await
{
Ok(_) => handle::Handle::refresh_verge(),
Err(err) => log::error!(target: "app", "{err}"),
}
});
}
/// 修改clash的配置
/// 修改clash的订阅
pub async fn patch_clash(patch: Mapping) -> Result<()> {
Config::clash().draft().patch_config(patch.clone());
match {
let mixed_port = patch.get("mixed-port");
if mixed_port.is_some() {
let changed = mixed_port != Config::clash().data().0.get("mixed-port");
let socks_port = patch.get("socks-port");
let port = patch.get("port");
let enable_random_port = Config::verge().latest().enable_random_port.unwrap_or(false);
if mixed_port.is_some() && !enable_random_port {
let changed = mixed_port.unwrap()
!= Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
// 检查端口占用
if changed {
if let Some(port) = mixed_port.clone().unwrap().as_u64() {
if let Some(port) = mixed_port.unwrap().as_u64() {
if !port_scanner::local_port_available(port as u16) {
Config::clash().discard();
bail!("port already in use");
@@ -164,8 +128,10 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> {
}
};
// 激活配置
// 激活订阅
if mixed_port.is_some()
|| socks_port.is_some()
|| port.is_some()
|| patch.get("secret").is_some()
|| patch.get("external-controller").is_some()
{
@@ -183,6 +149,8 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> {
log_err!(handle::Handle::update_systray_part());
}
Config::runtime().latest().patch_config(patch);
<Result<()>>::Ok(())
} {
Ok(()) => {
@@ -197,7 +165,7 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> {
}
}
/// 修改verge的配置
/// 修改verge的订阅
/// 一般都是一个个的修改
pub async fn patch_verge(patch: IVerge) -> Result<()> {
Config::verge().draft().patch_config(patch.clone());
@@ -207,6 +175,10 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
let system_proxy = patch.enable_system_proxy;
let proxy_bypass = patch.system_proxy_bypass;
let language = patch.language;
let port = patch.verge_mixed_port;
let common_tray_icon = patch.common_tray_icon;
let sysproxy_tray_icon = patch.sysproxy_tray_icon;
let tun_tray_icon = patch.tun_tray_icon;
match {
#[cfg(target_os = "windows")]
@@ -231,7 +203,7 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
if auto_launch.is_some() {
sysopt::Sysopt::global().update_launch()?;
}
if system_proxy.is_some() || proxy_bypass.is_some() {
if system_proxy.is_some() || proxy_bypass.is_some() || port.is_some() {
sysopt::Sysopt::global().update_sysproxy()?;
sysopt::Sysopt::global().guard_proxy();
}
@@ -246,7 +218,12 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
if language.is_some() {
handle::Handle::update_systray()?;
} else if system_proxy.or(tun_mode).is_some() {
} else if system_proxy.is_some()
|| tun_mode.is_some()
|| common_tray_icon.is_some()
|| sysproxy_tray_icon.is_some()
|| tun_tray_icon.is_some()
{
handle::Handle::update_systray_part()?;
}
@@ -265,7 +242,7 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
}
/// 更新某个profile
/// 如果更新当前配置就激活配置
/// 如果更新当前订阅就激活订阅
pub async fn update_profile(uid: String, option: Option<PrfOption>) -> Result<()> {
let url_opt = {
let profiles = Config::profiles();
@@ -303,7 +280,7 @@ pub async fn update_profile(uid: String, option: Option<PrfOption>) -> Result<()
Ok(())
}
/// 更新配置
/// 更新订阅
async fn update_core_config() -> Result<()> {
match CoreManager::global().update_config().await {
Ok(_) => {
@@ -317,3 +294,75 @@ async fn update_core_config() -> Result<()> {
}
}
}
/// copy env variable
pub fn copy_clash_env(app_handle: &AppHandle) {
let port = { Config::verge().latest().verge_mixed_port.unwrap_or(7897) };
let http_proxy = format!("http://127.0.0.1:{}", port);
let socks5_proxy = format!("socks5://127.0.0.1:{}", port);
let sh =
format!("export https_proxy={http_proxy} http_proxy={http_proxy} all_proxy={socks5_proxy}");
let cmd: String = format!("set http_proxy={http_proxy} \n set https_proxy={http_proxy}");
let ps: String = format!("$env:HTTP_PROXY=\"{http_proxy}\"; $env:HTTPS_PROXY=\"{http_proxy}\"");
let mut cliboard = app_handle.clipboard_manager();
let env_type = { Config::verge().latest().env_type.clone() };
let env_type = match env_type {
Some(env_type) => env_type,
None => {
#[cfg(not(target_os = "windows"))]
let default = "bash";
#[cfg(target_os = "windows")]
let default = "powershell";
default.to_string()
}
};
match env_type.as_str() {
"bash" => cliboard.write_text(sh).unwrap_or_default(),
"cmd" => cliboard.write_text(cmd).unwrap_or_default(),
"powershell" => cliboard.write_text(ps).unwrap_or_default(),
_ => log::error!(target: "app", "copy_clash_env: Invalid env type! {env_type}"),
};
}
pub async fn test_delay(url: String) -> Result<u32> {
use tokio::time::{Duration, Instant};
let mut builder = reqwest::ClientBuilder::new().use_rustls_tls().no_proxy();
let port = Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
let tun_mode = Config::verge().latest().enable_tun_mode.unwrap_or(false);
let proxy_scheme = format!("http://127.0.0.1:{port}");
if !tun_mode {
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
}
let request = builder
.timeout(Duration::from_millis(10000))
.build()?
.get(url).header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0");
let start = Instant::now();
let response = request.send().await?;
if response.status().is_success() {
let delay = start.elapsed().as_millis() as u32;
Ok(delay)
} else {
Ok(10000u32)
}
}
+24 -11
View File
@@ -25,7 +25,10 @@ fn main() -> std::io::Result<()> {
#[allow(unused_mut)]
let mut builder = tauri::Builder::default()
.system_tray(SystemTray::new())
.setup(|app| Ok(resolve::resolve_setup(app)))
.setup(|app| {
resolve::resolve_setup(app);
Ok(())
})
.on_system_tray_event(core::tray::Tray::on_system_tray_event)
.invoke_handler(tauri::generate_handler![
// common
@@ -33,8 +36,11 @@ fn main() -> std::io::Result<()> {
cmds::open_app_dir,
cmds::open_logs_dir,
cmds::open_web_url,
cmds::open_core_dir,
cmds::get_portable_flag,
// cmds::kill_sidecar,
cmds::restart_sidecar,
cmds::grant_permission,
// clash
cmds::get_clash_info,
cmds::get_clash_logs,
@@ -44,9 +50,14 @@ fn main() -> std::io::Result<()> {
cmds::get_runtime_yaml,
cmds::get_runtime_exists,
cmds::get_runtime_logs,
cmds::uwp::invoke_uwp_tool,
// verge
cmds::get_verge_config,
cmds::patch_verge_config,
cmds::test_delay,
cmds::get_app_dir,
cmds::copy_icon_file,
cmds::exit_app,
// cmds::update_hotkeys,
// profile
cmds::get_profiles,
@@ -56,6 +67,7 @@ fn main() -> std::io::Result<()> {
cmds::patch_profile,
cmds::create_profile,
cmds::import_profile,
cmds::reorder_profile,
cmds::update_profile,
cmds::delete_profile,
cmds::read_profile_file,
@@ -64,6 +76,8 @@ fn main() -> std::io::Result<()> {
cmds::service::check_service,
cmds::service::install_service,
cmds::service::uninstall_service,
// clash api
cmds::clash_api_get_proxy_delay
]);
#[cfg(target_os = "macos")]
@@ -94,22 +108,21 @@ fn main() -> std::io::Result<()> {
tauri::RunEvent::ExitRequested { api, .. } => {
api.prevent_exit();
}
tauri::RunEvent::Exit => {
tauri::RunEvent::Updater(tauri::UpdaterEvent::Downloaded) => {
resolve::resolve_reset();
api::process::kill_children();
app_handle.exit(0);
}
#[cfg(target_os = "macos")]
tauri::RunEvent::WindowEvent { label, event, .. } => {
use tauri::Manager;
if label == "main" {
match event {
tauri::WindowEvent::CloseRequested { api, .. } => {
api.prevent_close();
app_handle.get_window("main").map(|win| {
let _ = win.hide();
});
tauri::WindowEvent::Destroyed => {
let _ = resolve::save_window_size_position(app_handle, true);
}
tauri::WindowEvent::CloseRequested { .. } => {
let _ = resolve::save_window_size_position(app_handle, true);
}
tauri::WindowEvent::Moved(_) | tauri::WindowEvent::Resized(_) => {
let _ = resolve::save_window_size_position(app_handle, false);
}
_ => {}
}
+38 -75
View File
@@ -1,88 +1,69 @@
use crate::core::handle;
use anyhow::Result;
use once_cell::sync::OnceCell;
use std::path::PathBuf;
use tauri::{
api::path::{home_dir, resource_dir},
Env, PackageInfo,
api::path::{data_dir, resource_dir},
Env,
};
#[cfg(not(feature = "verge-dev"))]
static APP_DIR: &str = "clash-verge";
pub static APP_ID: &str = "io.github.clash-verge-rev.clash-verge-rev";
#[cfg(feature = "verge-dev")]
static APP_DIR: &str = "clash-verge-dev";
pub static APP_ID: &str = "io.github.clash-verge-rev.clash-verge-rev.dev";
pub static PORTABLE_FLAG: OnceCell<bool> = OnceCell::new();
static CLASH_CONFIG: &str = "config.yaml";
static VERGE_CONFIG: &str = "verge.yaml";
static PROFILE_YAML: &str = "profiles.yaml";
static mut RESOURCE_DIR: Option<PathBuf> = None;
/// portable flag
#[allow(unused)]
static mut PORTABLE_FLAG: bool = false;
pub static mut APP_VERSION: &str = "v1.1.2";
/// initialize portable flag
#[cfg(target_os = "windows")]
pub unsafe fn init_portable_flag() -> Result<()> {
/// init portable flag
pub fn init_portable_flag() -> Result<()> {
use tauri::utils::platform::current_exe;
let exe = current_exe()?;
if let Some(dir) = exe.parent() {
let app_exe = current_exe()?;
if let Some(dir) = app_exe.parent() {
let dir = PathBuf::from(dir).join(".config/PORTABLE");
if dir.exists() {
PORTABLE_FLAG = true;
PORTABLE_FLAG.get_or_init(|| true);
}
}
PORTABLE_FLAG.get_or_init(|| false);
Ok(())
}
/// get the verge app home dir
pub fn app_home_dir() -> Result<PathBuf> {
#[cfg(target_os = "windows")]
unsafe {
use tauri::utils::platform::current_exe;
use tauri::utils::platform::current_exe;
if !PORTABLE_FLAG {
Ok(home_dir()
.ok_or(anyhow::anyhow!("failed to get app home dir"))?
.join(".config")
.join(APP_DIR))
} else {
let app_exe = current_exe()?;
let app_exe = dunce::canonicalize(app_exe)?;
let app_dir = app_exe
.parent()
.ok_or(anyhow::anyhow!("failed to get the portable app dir"))?;
Ok(PathBuf::from(app_dir).join(".config").join(APP_DIR))
}
let flag = PORTABLE_FLAG.get().unwrap_or(&false);
if *flag {
let app_exe = current_exe()?;
let app_exe = dunce::canonicalize(app_exe)?;
let app_dir = app_exe
.parent()
.ok_or(anyhow::anyhow!("failed to get the portable app dir"))?;
return Ok(PathBuf::from(app_dir).join(".config").join(APP_ID));
}
#[cfg(not(target_os = "windows"))]
Ok(home_dir()
.ok_or(anyhow::anyhow!("failed to get the app home dir"))?
.join(".config")
.join(APP_DIR))
Ok(data_dir()
.ok_or(anyhow::anyhow!("failed to get app home dir"))?
.join(APP_ID))
}
/// get the resources dir
pub fn app_resources_dir(package_info: &PackageInfo) -> Result<PathBuf> {
let res_dir = resource_dir(package_info, &Env::default())
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?
.join("resources");
unsafe {
RESOURCE_DIR = Some(res_dir.clone());
let ver = package_info.version.to_string();
let ver_str = format!("v{ver}");
APP_VERSION = Box::leak(Box::new(ver_str));
}
Ok(res_dir)
pub fn app_resources_dir() -> Result<PathBuf> {
let handle = handle::Handle::global();
let app_handle = handle.app_handle.lock();
if let Some(app_handle) = app_handle.as_ref() {
let res_dir = resource_dir(app_handle.package_info(), &Env::default())
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?
.join("resources");
return Ok(res_dir);
};
Err(anyhow::anyhow!("failed to get the resource dir"))
}
/// profiles dir
@@ -107,31 +88,13 @@ pub fn profiles_path() -> Result<PathBuf> {
Ok(app_home_dir()?.join(PROFILE_YAML))
}
pub fn app_res_dir() -> Result<PathBuf> {
unsafe {
Ok(RESOURCE_DIR
.clone()
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?)
}
}
pub fn clash_pid_path() -> Result<PathBuf> {
unsafe {
Ok(RESOURCE_DIR
.clone()
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?
.join("clash.pid"))
}
Ok(app_home_dir()?.join("clash.pid"))
}
#[cfg(windows)]
pub fn service_path() -> Result<PathBuf> {
unsafe {
let res_dir = RESOURCE_DIR
.clone()
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?;
Ok(res_dir.join("clash-verge-service.exe"))
}
Ok(app_resources_dir()?.join("clash-verge-service.exe"))
}
#[cfg(windows)]
+64 -47
View File
@@ -2,7 +2,11 @@ use anyhow::{anyhow, bail, Context, Result};
use nanoid::nanoid;
use serde::{de::DeserializeOwned, Serialize};
use serde_yaml::{Mapping, Value};
use std::{fs, path::PathBuf, process::Command, str::FromStr};
use std::{fs, path::PathBuf, str::FromStr};
use tauri::{
api::shell::{open, Program},
Manager,
};
/// read data from yaml as struct T
pub fn read_yaml<T: DeserializeOwned>(path: &PathBuf) -> Result<T> {
@@ -10,20 +14,22 @@ pub fn read_yaml<T: DeserializeOwned>(path: &PathBuf) -> Result<T> {
bail!("file not found \"{}\"", path.display());
}
let yaml_str = fs::read_to_string(&path)
.context(format!("failed to read the file \"{}\"", path.display()))?;
let yaml_str = fs::read_to_string(path)
.with_context(|| format!("failed to read the file \"{}\"", path.display()))?;
serde_yaml::from_str::<T>(&yaml_str).context(format!(
"failed to read the file with yaml format \"{}\"",
path.display()
))
serde_yaml::from_str::<T>(&yaml_str).with_context(|| {
format!(
"failed to read the file with yaml format \"{}\"",
path.display()
)
})
}
/// read mapping from yaml fix #165
pub fn read_merge_mapping(path: &PathBuf) -> Result<Mapping> {
let mut val: Value = read_yaml(path)?;
val.apply_merge()
.context(format!("failed to apply merge \"{}\"", path.display()))?;
.with_context(|| format!("failed to apply merge \"{}\"", path.display()))?;
Ok(val
.as_mapping()
@@ -45,7 +51,8 @@ pub fn save_yaml<T: Serialize>(path: &PathBuf, data: &T, prefix: Option<&str>) -
};
let path_str = path.as_os_str().to_string_lossy().to_string();
fs::write(path, yaml_str.as_bytes()).context(format!("failed to save file \"{path_str}\""))
fs::write(path, yaml_str.as_bytes())
.with_context(|| format!("failed to save file \"{path_str}\""))
}
const ALPHABET: [char; 62] = [
@@ -64,44 +71,45 @@ pub fn get_uid(prefix: &str) -> String {
/// parse the string
/// xxx=123123; => 123123
pub fn parse_str<T: FromStr>(target: &str, key: &str) -> Option<T> {
target.find(key).and_then(|idx| {
let idx = idx + key.len();
let value = &target[idx..];
match value.split(';').nth(0) {
Some(value) => value.trim().parse(),
None => value.trim().parse(),
target.split(';').map(str::trim).find_map(|s| {
let mut parts = s.splitn(2, '=');
match (parts.next(), parts.next()) {
(Some(k), Some(v)) if k == key => v.parse::<T>().ok(),
_ => None,
}
.ok()
})
}
/// get the last part of the url, if not found, return empty string
pub fn get_last_part_and_decode(url: &str) -> Option<String> {
let path = url.split('?').next().unwrap_or(""); // Splits URL and takes the path part
let segments: Vec<&str> = path.split('/').collect();
let last_segment = segments.last()?;
Some(
percent_encoding::percent_decode_str(last_segment)
.decode_utf8_lossy()
.to_string(),
)
}
/// open file
/// use vscode by default
pub fn open_file(path: PathBuf) -> Result<()> {
// use vscode first
if let Ok(code) = which::which("code") {
let mut command = Command::new(&code);
pub fn open_file(app: tauri::AppHandle, path: PathBuf) -> Result<()> {
#[cfg(target_os = "macos")]
let code = "Visual Studio Code";
#[cfg(not(target_os = "macos"))]
let code = "code";
#[cfg(target_os = "windows")]
{
use std::os::windows::process::CommandExt;
if let Err(err) = command.creation_flags(0x08000000).arg(&path).spawn() {
log::error!(target: "app", "failed to open with VScode `{err}`");
open::that(path)?;
}
let _ = match Program::from_str(code) {
Ok(code) => open(&app.shell_scope(), path.to_string_lossy(), Some(code)),
Err(err) => {
log::error!(target: "app", "Can't find VScode `{err}`");
// default open
open(&app.shell_scope(), path.to_string_lossy(), None)
}
};
#[cfg(not(target_os = "windows"))]
if let Err(err) = command.arg(&path).spawn() {
log::error!(target: "app", "failed to open with VScode `{err}`");
open::that(path)?;
}
return Ok(());
}
open::that(path)?;
Ok(())
}
@@ -127,6 +135,15 @@ macro_rules! log_err {
};
}
#[macro_export]
macro_rules! trace_err {
($result: expr, $err_str: expr) => {
if let Err(err) = $result {
log::trace!(target: "app", "{}, err {}", $err_str, err);
}
}
}
/// wrap the anyhow error
/// transform the error to String
#[macro_export]
@@ -155,17 +172,17 @@ fn test_parse_value() {
let test_1 = "upload=111; download=2222; total=3333; expire=444";
let test_2 = "attachment; filename=Clash.yaml";
assert_eq!(parse_str::<usize>(test_1, "upload=").unwrap(), 111);
assert_eq!(parse_str::<usize>(test_1, "download=").unwrap(), 2222);
assert_eq!(parse_str::<usize>(test_1, "total=").unwrap(), 3333);
assert_eq!(parse_str::<usize>(test_1, "expire=").unwrap(), 444);
assert_eq!(parse_str::<usize>(test_1, "upload").unwrap(), 111);
assert_eq!(parse_str::<usize>(test_1, "download").unwrap(), 2222);
assert_eq!(parse_str::<usize>(test_1, "total").unwrap(), 3333);
assert_eq!(parse_str::<usize>(test_1, "expire").unwrap(), 444);
assert_eq!(
parse_str::<String>(test_2, "filename=").unwrap(),
parse_str::<String>(test_2, "filename").unwrap(),
format!("Clash.yaml")
);
assert_eq!(parse_str::<usize>(test_1, "aaa="), None);
assert_eq!(parse_str::<usize>(test_1, "upload1="), None);
assert_eq!(parse_str::<usize>(test_1, "expire1="), None);
assert_eq!(parse_str::<usize>(test_2, "attachment="), None);
assert_eq!(parse_str::<usize>(test_1, "aaa"), None);
assert_eq!(parse_str::<usize>(test_1, "upload1"), None);
assert_eq!(parse_str::<usize>(test_1, "expire1"), None);
assert_eq!(parse_str::<usize>(test_2, "attachment"), None);
}
+236 -34
View File
@@ -1,14 +1,16 @@
use crate::config::*;
use crate::utils::{dirs, help};
use anyhow::Result;
use chrono::Local;
use chrono::{Local, TimeZone};
use log::LevelFilter;
use log4rs::append::console::ConsoleAppender;
use log4rs::append::file::FileAppender;
use log4rs::config::{Appender, Config, Logger, Root};
use log4rs::config::{Appender, Logger, Root};
use log4rs::encode::pattern::PatternEncoder;
use std::fs;
use tauri::PackageInfo;
use std::fs::{self, DirEntry};
use std::path::PathBuf;
use std::str::FromStr;
use tauri::api::process::Command;
/// initialize this instance's log file
fn init_log() -> Result<()> {
@@ -17,49 +19,131 @@ fn init_log() -> Result<()> {
let _ = fs::create_dir_all(&log_dir);
}
let log_level = Config::verge().data().get_log_level();
if log_level == LevelFilter::Off {
return Ok(());
}
let local_time = Local::now().format("%Y-%m-%d-%H%M").to_string();
let log_file = format!("{}.log", local_time);
let log_file = log_dir.join(log_file);
#[cfg(feature = "verge-dev")]
let time_format = "{d(%Y-%m-%d %H:%M:%S)} {l} - {M} {m}{n}";
#[cfg(not(feature = "verge-dev"))]
let time_format = "{d(%Y-%m-%d %H:%M:%S)} {l} - {m}{n}";
let log_pattern = match log_level {
LevelFilter::Trace => "{d(%Y-%m-%d %H:%M:%S)} {l} [{M}] - {m}{n}",
_ => "{d(%Y-%m-%d %H:%M:%S)} {l} - {m}{n}",
};
let encode = Box::new(PatternEncoder::new(time_format));
let encode = Box::new(PatternEncoder::new(log_pattern));
let stdout = ConsoleAppender::builder().encoder(encode.clone()).build();
let tofile = FileAppender::builder().encoder(encode).build(log_file)?;
#[cfg(feature = "verge-dev")]
let level = LevelFilter::Debug;
#[cfg(not(feature = "verge-dev"))]
let level = LevelFilter::Info;
let mut logger_builder = Logger::builder();
let mut root_builder = Root::builder();
let config = Config::builder()
let log_more = log_level == LevelFilter::Trace || log_level == LevelFilter::Debug;
#[cfg(feature = "verge-dev")]
{
logger_builder = logger_builder.appenders(["file", "stdout"]);
if log_more {
root_builder = root_builder.appenders(["file", "stdout"]);
} else {
root_builder = root_builder.appenders(["stdout"]);
}
}
#[cfg(not(feature = "verge-dev"))]
{
logger_builder = logger_builder.appenders(["file"]);
if log_more {
root_builder = root_builder.appenders(["file"]);
}
}
let (config, _) = log4rs::config::Config::builder()
.appender(Appender::builder().build("stdout", Box::new(stdout)))
.appender(Appender::builder().build("file", Box::new(tofile)))
.logger(
Logger::builder()
.appenders(["file", "stdout"])
.additive(false)
.build("app", level),
)
.build(Root::builder().appender("stdout").build(LevelFilter::Info))?;
.logger(logger_builder.additive(false).build("app", log_level))
.build_lossy(root_builder.build(log_level));
log4rs::init_config(config)?;
Ok(())
}
/// Initialize all the files from resources
pub fn init_config() -> Result<()> {
#[cfg(target_os = "windows")]
unsafe {
let _ = dirs::init_portable_flag();
/// 删除log文件
pub fn delete_log() -> Result<()> {
let log_dir = dirs::app_logs_dir()?;
if !log_dir.exists() {
return Ok(());
}
let auto_log_clean = {
let verge = Config::verge();
let verge = verge.data();
verge.auto_log_clean.unwrap_or(0)
};
let day = match auto_log_clean {
1 => 7,
2 => 30,
3 => 90,
_ => return Ok(()),
};
log::debug!(target: "app", "try to delete log files, day: {day}");
// %Y-%m-%d to NaiveDateTime
let parse_time_str = |s: &str| {
let sa: Vec<&str> = s.split('-').collect();
if sa.len() != 4 {
return Err(anyhow::anyhow!("invalid time str"));
}
let year = i32::from_str(sa[0])?;
let month = u32::from_str(sa[1])?;
let day = u32::from_str(sa[2])?;
let time = chrono::NaiveDate::from_ymd_opt(year, month, day)
.ok_or(anyhow::anyhow!("invalid time str"))?
.and_hms_opt(0, 0, 0)
.ok_or(anyhow::anyhow!("invalid time str"))?;
Ok(time)
};
let process_file = |file: DirEntry| -> Result<()> {
let file_name = file.file_name();
let file_name = file_name.to_str().unwrap_or_default();
if file_name.ends_with(".log") {
let now = Local::now();
let created_time = parse_time_str(&file_name[0..file_name.len() - 4])?;
let file_time = Local
.from_local_datetime(&created_time)
.single()
.ok_or(anyhow::anyhow!("invalid local datetime"))?;
let duration = now.signed_duration_since(file_time);
if duration.num_days() > day {
let file_path = file.path();
let _ = fs::remove_file(file_path);
log::info!(target: "app", "delete log file: {file_name}");
}
}
Ok(())
};
for file in fs::read_dir(&log_dir)?.flatten() {
let _ = process_file(file);
}
Ok(())
}
/// Initialize all the config files
/// before tauri setup
pub fn init_config() -> Result<()> {
let _ = dirs::init_portable_flag();
let _ = init_log();
let _ = delete_log();
crate::log_err!(dirs::app_home_dir().map(|app_dir| {
if !app_dir.exists() {
@@ -75,7 +159,7 @@ pub fn init_config() -> Result<()> {
crate::log_err!(dirs::clash_path().map(|path| {
if !path.exists() {
help::save_yaml(&path, &IClashTemp::template().0, Some("# Clash Verge"))?;
help::save_yaml(&path, &IClashTemp::template().0, Some("# Clash Vergeasu"))?;
}
<Result<()>>::Ok(())
}));
@@ -97,10 +181,11 @@ pub fn init_config() -> Result<()> {
Ok(())
}
/// initialize app
pub fn init_resources(package_info: &PackageInfo) -> Result<()> {
/// initialize app resources
/// after tauri setup
pub fn init_resources() -> Result<()> {
let app_dir = dirs::app_home_dir()?;
let res_dir = dirs::app_resources_dir(package_info)?;
let res_dir = dirs::app_resources_dir()?;
if !app_dir.exists() {
let _ = fs::create_dir_all(&app_dir);
@@ -109,14 +194,131 @@ pub fn init_resources(package_info: &PackageInfo) -> Result<()> {
let _ = fs::create_dir_all(&res_dir);
}
#[cfg(target_os = "windows")]
let file_list = ["Country.mmdb", "geoip.dat", "geosite.dat"];
#[cfg(not(target_os = "windows"))]
let file_list = ["Country.mmdb", "geoip.dat", "geosite.dat"];
// copy the resource file
for file in ["Country.mmdb", "geoip.dat", "geosite.dat", "wintun.dll"].iter() {
// if the source file is newer than the destination file, copy it over
for file in file_list.iter() {
let src_path = res_dir.join(file);
let target_path = app_dir.join(file);
if src_path.exists() {
let _ = fs::copy(src_path, target_path);
let dest_path = app_dir.join(file);
let handle_copy = || {
match fs::copy(&src_path, &dest_path) {
Ok(_) => log::debug!(target: "app", "resources copied '{file}'"),
Err(err) => {
log::error!(target: "app", "failed to copy resources '{file}', {err}")
}
};
};
if src_path.exists() && !dest_path.exists() {
handle_copy();
continue;
}
let src_modified = fs::metadata(&src_path).and_then(|m| m.modified());
let dest_modified = fs::metadata(&dest_path).and_then(|m| m.modified());
match (src_modified, dest_modified) {
(Ok(src_modified), Ok(dest_modified)) => {
if src_modified > dest_modified {
handle_copy();
} else {
log::debug!(target: "app", "skipping resource copy '{file}'");
}
}
_ => {
log::debug!(target: "app", "failed to get modified '{file}'");
handle_copy();
}
};
}
Ok(())
}
/// initialize url scheme
#[cfg(target_os = "windows")]
pub fn init_scheme() -> Result<()> {
use tauri::utils::platform::current_exe;
use winreg::enums::*;
use winreg::RegKey;
let app_exe = current_exe()?;
let app_exe = dunce::canonicalize(app_exe)?;
let app_exe = app_exe.to_string_lossy().into_owned();
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let (clash, _) = hkcu.create_subkey("Software\\Classes\\Clash")?;
clash.set_value("", &"Clash Verge")?;
clash.set_value("URL Protocol", &"Clash Verge URL Scheme Protocol")?;
let (default_icon, _) = hkcu.create_subkey("Software\\Classes\\Clash\\DefaultIcon")?;
default_icon.set_value("", &app_exe)?;
let (command, _) = hkcu.create_subkey("Software\\Classes\\Clash\\Shell\\Open\\Command")?;
command.set_value("", &format!("{app_exe} \"%1\""))?;
Ok(())
}
#[cfg(target_os = "linux")]
pub fn init_scheme() -> Result<()> {
let output = std::process::Command::new("xdg-mime")
.arg("default")
.arg("clash-verge.desktop")
.arg("x-scheme-handler/clash")
.output()?;
if !output.status.success() {
return Err(anyhow::anyhow!(
"failed to set clash scheme, {}",
String::from_utf8_lossy(&output.stderr)
));
}
Ok(())
}
#[cfg(target_os = "macos")]
pub fn init_scheme() -> Result<()> {
Ok(())
}
pub fn startup_script() -> Result<()> {
let path = {
let verge = Config::verge();
let verge = verge.latest();
verge.startup_script.clone().unwrap_or("".to_string())
};
if !path.is_empty() {
let mut shell = "";
if path.ends_with(".sh") {
shell = "bash";
}
if path.ends_with(".ps1") {
shell = "powershell";
}
if path.ends_with(".bat") {
shell = "cmd";
}
if shell.is_empty() {
return Err(anyhow::anyhow!("unsupported script: {path}"));
}
let current_dir = PathBuf::from(path.clone());
if !current_dir.exists() {
return Err(anyhow::anyhow!("script not found: {path}"));
}
let current_dir = current_dir.parent();
match current_dir {
Some(dir) => {
let _ = Command::new(shell)
.current_dir(dir.to_path_buf())
.args(&[path])
.output()?;
}
None => {
let _ = Command::new(shell).args(&[path]).output()?;
}
}
}
Ok(())
}
-1
View File
@@ -4,4 +4,3 @@ pub mod init;
pub mod resolve;
pub mod server;
pub mod tmpl;
// mod winhelp;
+212 -49
View File
@@ -1,27 +1,91 @@
use crate::config::Config;
use crate::log_err;
use crate::{core::*, utils::init, utils::server};
use crate::config::{IVerge, PrfOption};
use crate::{
config::{Config, PrfItem},
core::*,
utils::init,
utils::server,
};
use crate::{log_err, trace_err};
use anyhow::Result;
use once_cell::sync::OnceCell;
use serde_yaml::Mapping;
use std::net::TcpListener;
use tauri::api::notification;
use tauri::{App, AppHandle, Manager};
use window_shadows::set_shadow;
pub static VERSION: OnceCell<String> = OnceCell::new();
pub fn find_unused_port() -> Result<u16> {
match TcpListener::bind("127.0.0.1:0") {
Ok(listener) => {
let port = listener.local_addr()?.port();
Ok(port)
}
Err(_) => {
let port = Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
log::warn!(target: "app", "use default port: {}", port);
Ok(port)
}
}
}
/// handle something when start app
pub fn resolve_setup(app: &mut App) {
#[cfg(target_os = "macos")]
app.set_activation_policy(tauri::ActivationPolicy::Accessory);
let version = app.package_info().version.to_string();
handle::Handle::global().init(app.app_handle());
VERSION.get_or_init(|| version.clone());
log_err!(init::init_resources(app.package_info()));
log_err!(init::init_resources());
log_err!(init::init_scheme());
log_err!(init::startup_script());
// 处理随机端口
let enable_random_port = Config::verge().latest().enable_random_port.unwrap_or(false);
let mut port = Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
if enable_random_port {
port = find_unused_port().unwrap_or(
Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port()),
);
}
Config::verge().data().patch_config(IVerge {
verge_mixed_port: Some(port),
..IVerge::default()
});
let _ = Config::verge().data().save_file();
let mut mapping = Mapping::new();
mapping.insert("mixed-port".into(), port.into());
Config::clash().data().patch_config(mapping);
let _ = Config::clash().data().save_config();
// 启动核心
log::trace!("init config");
log_err!(Config::init_config());
log::trace!("launch core");
log_err!(CoreManager::global().init());
// setup a simple http server for singleton
log::trace!("launch embed server");
server::embed_server(app.app_handle());
log::trace!("init system tray");
log_err!(tray::Tray::update_systray(&app.app_handle()));
let silent_start = { Config::verge().data().enable_silent_start.clone() };
let silent_start = { Config::verge().data().enable_silent_start };
if !silent_start.unwrap_or(false) {
create_window(&app.app_handle());
}
@@ -32,6 +96,13 @@ pub fn resolve_setup(app: &mut App) {
log_err!(handle::Handle::update_systray_part());
log_err!(hotkey::Hotkey::global().init(app.app_handle()));
log_err!(timer::Timer::global().init());
let argvs: Vec<String> = std::env::args().collect();
if argvs.len() > 1 {
tauri::async_runtime::block_on(async {
resolve_scheme(argvs[1].to_owned()).await;
});
}
}
/// reset system proxy
@@ -43,63 +114,155 @@ pub fn resolve_reset() {
/// create main window
pub fn create_window(app_handle: &AppHandle) {
if let Some(window) = app_handle.get_window("main") {
let _ = window.unminimize();
let _ = window.show();
let _ = window.set_focus();
trace_err!(window.unminimize(), "set win unminimize");
trace_err!(window.show(), "set win visible");
trace_err!(window.set_focus(), "set win focus");
return;
}
let builder = tauri::window::WindowBuilder::new(
let mut builder = tauri::window::WindowBuilder::new(
app_handle,
"main".to_string(),
tauri::WindowUrl::App("index.html".into()),
)
.title("Clash Verge")
.center()
.visible(false)
.fullscreen(false)
.min_inner_size(600.0, 520.0);
#[cfg(target_os = "windows")]
{
use std::time::Duration;
use tokio::time::sleep;
use window_shadows::set_shadow;
match builder
.decorations(false)
.transparent(true)
.inner_size(800.0, 636.0)
.visible(false)
.build()
{
Ok(_) => {
let app_handle = app_handle.clone();
if let Some(window) = app_handle.get_window("main") {
let _ = set_shadow(&window, true);
}
tauri::async_runtime::spawn(async move {
sleep(Duration::from_secs(1)).await;
if let Some(window) = app_handle.get_window("main") {
let _ = window.show();
let _ = window.unminimize();
let _ = window.set_focus();
}
});
}
Err(err) => log::error!(target: "app", "{err}"),
match Config::verge().latest().window_size_position.clone() {
Some(size_pos) if size_pos.len() == 4 => {
let size = (size_pos[0], size_pos[1]);
let pos = (size_pos[2], size_pos[3]);
let w = size.0.clamp(600.0, f64::INFINITY);
let h = size.1.clamp(520.0, f64::INFINITY);
builder = builder.inner_size(w, h).position(pos.0, pos.1);
}
}
_ => {
#[cfg(target_os = "windows")]
{
builder = builder
.additional_browser_args("--enable-features=msWebView2EnableDraggableRegions")
.inner_size(800.0, 636.0)
.center();
}
#[cfg(target_os = "macos")]
crate::log_err!(builder.decorations(true).inner_size(800.0, 642.0).build());
#[cfg(target_os = "macos")]
{
builder = builder.inner_size(800.0, 642.0).center();
}
#[cfg(target_os = "linux")]
crate::log_err!(builder
#[cfg(target_os = "linux")]
{
builder = builder.inner_size(800.0, 642.0).center();
}
}
};
#[cfg(target_os = "windows")]
let window = builder
.decorations(false)
.transparent(true)
.inner_size(800.0, 636.0)
.build());
.visible(false)
.build();
#[cfg(target_os = "macos")]
let window = builder
.decorations(true)
.hidden_title(true)
.title_bar_style(tauri::TitleBarStyle::Overlay)
.build();
#[cfg(target_os = "linux")]
let window = builder.decorations(true).transparent(false).build();
match window {
Ok(win) => {
let is_maximized = Config::verge()
.latest()
.window_is_maximized
.unwrap_or(false);
log::trace!("try to calculate the monitor size");
let center = (|| -> Result<bool> {
let mut center = false;
let monitor = win.current_monitor()?.ok_or(anyhow::anyhow!(""))?;
let size = monitor.size();
let pos = win.outer_position()?;
if pos.x < -400
|| pos.x > (size.width - 200) as i32
|| pos.y < -200
|| pos.y > (size.height - 200) as i32
{
center = true;
}
Ok(center)
})();
if center.unwrap_or(true) {
trace_err!(win.center(), "set win center");
}
trace_err!(set_shadow(&win, true), "set win shadow");
if is_maximized {
trace_err!(win.maximize(), "set win maximize");
}
}
Err(_) => {
log::error!("failed to create window");
return;
}
}
}
/// save window size and position
pub fn save_window_size_position(app_handle: &AppHandle, save_to_file: bool) -> Result<()> {
let verge = Config::verge();
let mut verge = verge.latest();
if save_to_file {
verge.save_file()?;
}
let win = app_handle
.get_window("main")
.ok_or(anyhow::anyhow!("failed to get window"))?;
let scale = win.scale_factor()?;
let size = win.inner_size()?;
let size = size.to_logical::<f64>(scale);
let pos = win.outer_position()?;
let pos = pos.to_logical::<f64>(scale);
let is_maximized = win.is_maximized()?;
verge.window_is_maximized = Some(is_maximized);
if !is_maximized && size.width >= 600.0 && size.height >= 520.0 {
verge.window_size_position = Some(vec![size.width, size.height, pos.x, pos.y]);
}
Ok(())
}
pub async fn resolve_scheme(param: String) {
let url = param
.trim_start_matches("clash://install-config/?url=")
.trim_start_matches("clash://install-config?url=");
let option = PrfOption {
user_agent: None,
with_proxy: Some(true),
self_proxy: None,
danger_accept_invalid_certs: None,
update_interval: None,
};
if let Ok(item) = PrfItem::from_url(url, None, None, Some(option)).await {
if Config::profiles().data().append_item(item).is_ok() {
notification::Notification::new(crate::utils::dirs::APP_ID)
.title("Clash Verge")
.body("Import profile success")
.show()
.unwrap();
};
} else {
notification::Notification::new(crate::utils::dirs::APP_ID)
.title("Clash Verge")
.body("Import profile failed")
.show()
.unwrap();
log::error!("failed to parse url: {}", url);
}
}
+39 -5
View File
@@ -4,19 +4,42 @@ use super::resolve;
use crate::config::IVerge;
use anyhow::{bail, Result};
use port_scanner::local_port_available;
use std::convert::Infallible;
use tauri::AppHandle;
use warp::Filter;
#[derive(serde::Deserialize, Debug)]
struct QueryParam {
param: String,
}
/// check whether there is already exists
pub fn check_singleton() -> Result<()> {
let port = IVerge::get_singleton_port();
if !local_port_available(port) {
tauri::async_runtime::block_on(async {
let url = format!("http://127.0.0.1:{port}/commands/visible");
let resp = reqwest::get(url).await?.text().await?;
let resp = reqwest::get(format!("http://127.0.0.1:{port}/commands/ping"))
.await?
.text()
.await?;
if &resp == "ok" {
let argvs: Vec<String> = std::env::args().collect();
if argvs.len() > 1 {
let param = argvs[1].as_str();
reqwest::get(format!(
"http://127.0.0.1:{port}/commands/scheme?param={param}"
))
.await?
.text()
.await?;
} else {
reqwest::get(format!("http://127.0.0.1:{port}/commands/visible"))
.await?
.text()
.await?;
}
bail!("app exists");
}
@@ -34,11 +57,22 @@ pub fn embed_server(app_handle: AppHandle) {
let port = IVerge::get_singleton_port();
tauri::async_runtime::spawn(async move {
let commands = warp::path!("commands" / "visible").map(move || {
let ping = warp::path!("commands" / "ping").map(move || "ok");
let visible = warp::path!("commands" / "visible").map(move || {
resolve::create_window(&app_handle);
format!("ok")
"ok"
});
warp::serve(commands).bind(([127, 0, 0, 1], port)).await;
let scheme = warp::path!("commands" / "scheme")
.and(warp::query::<QueryParam>())
.and_then(scheme_handler);
async fn scheme_handler(query: QueryParam) -> Result<impl warp::Reply, Infallible> {
resolve::resolve_scheme(query.param).await;
Ok("ok")
}
let commands = ping.or(visible).or(scheme);
warp::serve(commands).run(([127, 0, 0, 1], port)).await;
});
}
+9 -1
View File
@@ -1,4 +1,4 @@
///! Some config file template
//! Some config file template
/// template for new a profile item
pub const ITEM_LOCAL: &str = "# Profile Template for clash verge
@@ -16,14 +16,22 @@ pub const ITEM_MERGE: &str = "# Merge Template for clash verge
prepend-rules:
prepend-rule-providers:
prepend-proxies:
prepend-proxy-providers:
prepend-proxy-groups:
append-rules:
append-rule-providers:
append-proxies:
append-proxy-providers:
append-proxy-groups:
";
-69
View File
@@ -1,69 +0,0 @@
#![cfg(target_os = "windows")]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
//!
//! From https://github.com/tauri-apps/window-vibrancy/blob/dev/src/windows.rs
//!
use windows_sys::Win32::{
Foundation::*,
System::{LibraryLoader::*, SystemInformation::*},
};
fn get_function_impl(library: &str, function: &str) -> Option<FARPROC> {
assert_eq!(library.chars().last(), Some('\0'));
assert_eq!(function.chars().last(), Some('\0'));
let module = unsafe { LoadLibraryA(library.as_ptr()) };
if module == 0 {
return None;
}
Some(unsafe { GetProcAddress(module, function.as_ptr()) })
}
macro_rules! get_function {
($lib:expr, $func:ident) => {
get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0')).map(|f| unsafe {
std::mem::transmute::<::windows_sys::Win32::Foundation::FARPROC, $func>(f)
})
};
}
/// Returns a tuple of (major, minor, buildnumber)
fn get_windows_ver() -> Option<(u32, u32, u32)> {
type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> i32;
let handle = get_function!("ntdll.dll", RtlGetVersion);
if let Some(rtl_get_version) = handle {
unsafe {
let mut vi = OSVERSIONINFOW {
dwOSVersionInfoSize: 0,
dwMajorVersion: 0,
dwMinorVersion: 0,
dwBuildNumber: 0,
dwPlatformId: 0,
szCSDVersion: [0; 128],
};
let status = (rtl_get_version)(&mut vi as _);
if status >= 0 {
Some((vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber))
} else {
None
}
}
} else {
None
}
}
pub fn is_win11() -> bool {
let v = get_windows_ver().unwrap_or_default();
v.2 >= 22000
}
#[test]
fn test_version() {
dbg!(get_windows_ver().unwrap_or_default());
}
+28 -34
View File
@@ -1,23 +1,18 @@
{
"package": {
"productName": "Clash Verge",
"version": "1.2.0"
"version": "1.5.5"
},
"build": {
"distDir": "../dist",
"devPath": "http://localhost:3000/",
"beforeDevCommand": "yarn run web:dev",
"beforeBuildCommand": "yarn run web:build"
"beforeDevCommand": "pnpm run web:dev",
"beforeBuildCommand": "pnpm run web:build"
},
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.ico",
"iconAsTemplate": true
},
"bundle": {
"active": true,
"targets": "all",
"identifier": "top.gydi.clashverge",
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
@@ -26,38 +21,20 @@
"icons/icon.ico"
],
"resources": ["resources"],
"externalBin": ["sidecar/clash", "sidecar/clash-meta"],
"externalBin": ["sidecar/clash-meta", "sidecar/clash-meta-alpha"],
"copyright": "© 2022 zzzgydi All Rights Reserved",
"category": "DeveloperTool",
"shortDescription": "A Clash GUI based on tauri.",
"longDescription": "A Clash GUI based on tauri.",
"deb": {
"depends": []
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"wix": {
"language": ["zh-CN", "en-US"]
}
}
"shortDescription": "A Clash Meta GUI based on tauri.",
"longDescription": "A Clash Meta GUI based on tauri."
},
"updater": {
"active": true,
"endpoints": [
"https://github.com/zzzgydi/clash-verge/releases/download/updater/update.json",
"https://hub.fastgit.xyz/zzzgydi/clash-verge/releases/download/updater/update-proxy.json"
"https://mirror.ghproxy.com/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-proxy.json",
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update.json"
],
"dialog": false,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDExNUFBNTBBN0FDNEFBRTUKUldUbHFzUjZDcVZhRVRJM25NS3NkSFlFVElxUkNZMzZ6bHUwRVJjb2F3alJXVzRaeDdSaTA2YWYK"
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEQyOEMyRjBCQkVGOUJEREYKUldUZnZmbStDeStNMHU5Mmo1N24xQXZwSVRYbXA2NUpzZE5oVzlqeS9Bc0t6RVV4MmtwVjBZaHgK"
},
"allowlist": {
"shell": {
@@ -71,11 +48,28 @@
},
"globalShortcut": {
"all": true
},
"clipboard": {
"all": true
},
"notification": {
"all": true
},
"dialog": {
"all": false,
"open": true
},
"protocol": {
"asset": true,
"assetScope": ["$APPDATA/**", "$RESOURCE/../**"]
},
"path": {
"all": true
}
},
"windows": [],
"security": {
"csp": "script-src 'unsafe-eval' 'self'; default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; img-src data: 'self';"
"csp": "script-src 'unsafe-eval' 'self'; default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; img-src asset: http: https: data: 'self';"
}
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.png"
},
"bundle": {
"targets": ["deb", "appimage", "updater"],
"deb": {
"depends": ["openssl"],
"desktopTemplate": "./template/clash-verge.desktop"
}
}
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"tauri": {
"systemTray": {
"iconPath": "icons/mac-tray-icon.png",
"iconAsTemplate": true
},
"bundle": {
"targets": ["app", "dmg", "updater"],
"macOS": {
"frameworks": [],
"minimumSystemVersion": "10.15",
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
}
}
}
}
+27
View File
@@ -0,0 +1,27 @@
{
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.png"
},
"bundle": {
"targets": ["nsis", "updater"],
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"webviewInstallMode": {
"type": "embedBootstrapper",
"silent": true
},
"nsis": {
"displayLanguageSelector": true,
"installerIcon": "icons/icon.ico",
"languages": ["SimpChinese", "English"],
"license": "../LICENSE",
"installMode": "perMachine",
"template": "./template/installer.nsi"
}
}
}
}
}
+9
View File
@@ -0,0 +1,9 @@
[Desktop Entry]
Categories={{{categories}}}
Comment={{{comment}}}
Exec={{{exec}}} %u
Icon={{{icon}}}
Name={{{name}}}
Terminal=false
Type=Application
MimeType=x-scheme-handler/clash;
+827
View File
@@ -0,0 +1,827 @@
; 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.
Unicode true
; Set the compression algorithm. Default is LZMA.
!if "{{compression}}" == ""
SetCompressor /SOLID lzma
!else
SetCompressor /SOLID "{{compression}}"
!endif
!include MUI2.nsh
!include FileFunc.nsh
!include x64.nsh
!include WordFunc.nsh
!include "LogicLib.nsh"
!include "StrFunc.nsh"
!addplugindir "$%AppData%\Local\NSIS\"
${StrCase}
${StrLoc}
!define MANUFACTURER "{{manufacturer}}"
!define PRODUCTNAME "{{product_name}}"
!define VERSION "{{version}}"
!define VERSIONWITHBUILD "{{version_with_build}}"
!define SHORTDESCRIPTION "{{short_description}}"
!define INSTALLMODE "{{install_mode}}"
!define LICENSE "{{license}}"
!define INSTALLERICON "{{installer_icon}}"
!define SIDEBARIMAGE "{{sidebar_image}}"
!define HEADERIMAGE "{{header_image}}"
!define MAINBINARYNAME "{{main_binary_name}}"
!define MAINBINARYSRCPATH "{{main_binary_path}}"
!define BUNDLEID "{{bundle_id}}"
!define COPYRIGHT "{{copyright}}"
!define OUTFILE "{{out_file}}"
!define ARCH "{{arch}}"
!define PLUGINSPATH "{{additional_plugins_path}}"
!define ALLOWDOWNGRADES "{{allow_downgrades}}"
!define DISPLAYLANGUAGESELECTOR "{{display_language_selector}}"
!define INSTALLWEBVIEW2MODE "{{install_webview2_mode}}"
!define WEBVIEW2INSTALLERARGS "{{webview2_installer_args}}"
!define WEBVIEW2BOOTSTRAPPERPATH "{{webview2_bootstrapper_path}}"
!define WEBVIEW2INSTALLERPATH "{{webview2_installer_path}}"
!define UNINSTKEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCTNAME}"
!define MANUPRODUCTKEY "Software\${MANUFACTURER}\${PRODUCTNAME}"
!define UNINSTALLERSIGNCOMMAND "{{uninstaller_sign_cmd}}"
!define ESTIMATEDSIZE "{{estimated_size}}"
Name "${PRODUCTNAME}"
BrandingText "${COPYRIGHT}"
OutFile "${OUTFILE}"
VIProductVersion "${VERSIONWITHBUILD}"
VIAddVersionKey "ProductName" "${PRODUCTNAME}"
VIAddVersionKey "FileDescription" "${SHORTDESCRIPTION}"
VIAddVersionKey "LegalCopyright" "${COPYRIGHT}"
VIAddVersionKey "FileVersion" "${VERSION}"
VIAddVersionKey "ProductVersion" "${VERSION}"
; Plugins path, currently exists for linux only
!if "${PLUGINSPATH}" != ""
!addplugindir "${PLUGINSPATH}"
!endif
!if "${UNINSTALLERSIGNCOMMAND}" != ""
!uninstfinalize '${UNINSTALLERSIGNCOMMAND}'
!endif
; Handle install mode, `perUser`, `perMachine` or `both`
!if "${INSTALLMODE}" == "perMachine"
RequestExecutionLevel highest
!endif
!if "${INSTALLMODE}" == "currentUser"
RequestExecutionLevel user
!endif
!if "${INSTALLMODE}" == "both"
!define MULTIUSER_MUI
!define MULTIUSER_INSTALLMODE_INSTDIR "${PRODUCTNAME}"
!define MULTIUSER_INSTALLMODE_COMMANDLINE
!if "${ARCH}" == "x64"
!define MULTIUSER_USE_PROGRAMFILES64
!else if "${ARCH}" == "arm64"
!define MULTIUSER_USE_PROGRAMFILES64
!endif
!define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY "${UNINSTKEY}"
!define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME "CurrentUser"
!define MULTIUSER_INSTALLMODEPAGE_SHOWUSERNAME
!define MULTIUSER_INSTALLMODE_FUNCTION RestorePreviousInstallLocation
!define MULTIUSER_EXECUTIONLEVEL Highest
!include MultiUser.nsh
!endif
; installer icon
!if "${INSTALLERICON}" != ""
!define MUI_ICON "${INSTALLERICON}"
!endif
; installer sidebar image
!if "${SIDEBARIMAGE}" != ""
!define MUI_WELCOMEFINISHPAGE_BITMAP "${SIDEBARIMAGE}"
!endif
; installer header image
!if "${HEADERIMAGE}" != ""
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_BITMAP "${HEADERIMAGE}"
!endif
; Define registry key to store installer language
!define MUI_LANGDLL_REGISTRY_ROOT "HKCU"
!define MUI_LANGDLL_REGISTRY_KEY "${MANUPRODUCTKEY}"
!define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language"
; Installer pages, must be ordered as they appear
; 1. Welcome Page
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
!insertmacro MUI_PAGE_WELCOME
; 2. License Page (if defined)
!if "${LICENSE}" != ""
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
!insertmacro MUI_PAGE_LICENSE "${LICENSE}"
!endif
; 3. Install mode (if it is set to `both`)
!if "${INSTALLMODE}" == "both"
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
!insertmacro MULTIUSER_PAGE_INSTALLMODE
!endif
; 4. Custom page to ask user if he wants to reinstall/uninstall
; only if a previous installtion was detected
Var ReinstallPageCheck
Page custom PageReinstall PageLeaveReinstall
Function PageReinstall
; Uninstall previous WiX installation if exists.
;
; A WiX installer stores the isntallation info in registry
; using a UUID and so we have to loop through all keys under
; `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall`
; and check if `DisplayName` and `Publisher` keys match ${PRODUCTNAME} and ${MANUFACTURER}
;
; This has a potentional issue that there maybe another installation that matches
; our ${PRODUCTNAME} and ${MANUFACTURER} but wasn't installed by our WiX installer,
; 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
IntOp $0 $0 + 1
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" "DisplayName"
ReadRegStr $R1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" "Publisher"
StrCmp "$R0$R1" "${PRODUCTNAME}${MANUFACTURER}" 0 wix_loop
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" "UninstallString"
${StrCase} $R1 $R0 "L"
${StrLoc} $R0 $R1 "msiexec" ">"
StrCmp $R0 0 0 wix_done
StrCpy $R7 "wix"
StrCpy $R6 "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1"
Goto compare_version
wix_done:
; Check if there is an existing installation, if not, abort the reinstall page
ReadRegStr $R0 SHCTX "${UNINSTKEY}" ""
ReadRegStr $R1 SHCTX "${UNINSTKEY}" "UninstallString"
${IfThen} "$R0$R1" == "" ${|} Abort ${|}
; Compare this installar version with the existing installation
; and modify the messages presented to the user accordingly
compare_version:
StrCpy $R4 "$(older)"
${If} $R7 == "wix"
ReadRegStr $R0 HKLM "$R6" "DisplayVersion"
${Else}
ReadRegStr $R0 SHCTX "${UNINSTKEY}" "DisplayVersion"
${EndIf}
${IfThen} $R0 == "" ${|} StrCpy $R4 "$(unknown)" ${|}
nsis_tauri_utils::SemverCompare "${VERSION}" $R0
Pop $R0
; Reinstalling the same version
${If} $R0 == 0
StrCpy $R1 "$(alreadyInstalledLong)"
StrCpy $R2 "$(addOrReinstall)"
StrCpy $R3 "$(uninstallApp)"
!insertmacro MUI_HEADER_TEXT "$(alreadyInstalled)" "$(chooseMaintenanceOption)"
StrCpy $R5 "2"
; Upgrading
${ElseIf} $R0 == 1
StrCpy $R1 "$(olderOrUnknownVersionInstalled)"
StrCpy $R2 "$(uninstallBeforeInstalling)"
StrCpy $R3 "$(dontUninstall)"
!insertmacro MUI_HEADER_TEXT "$(alreadyInstalled)" "$(choowHowToInstall)"
StrCpy $R5 "1"
; Downgrading
${ElseIf} $R0 == -1
StrCpy $R1 "$(newerVersionInstalled)"
StrCpy $R2 "$(uninstallBeforeInstalling)"
!if "${ALLOWDOWNGRADES}" == "true"
StrCpy $R3 "$(dontUninstall)"
!else
StrCpy $R3 "$(dontUninstallDowngrade)"
!endif
!insertmacro MUI_HEADER_TEXT "$(alreadyInstalled)" "$(choowHowToInstall)"
StrCpy $R5 "1"
${Else}
Abort
${EndIf}
Call SkipIfPassive
nsDialogs::Create 1018
Pop $R4
${IfThen} $(^RTL) == 1 ${|} nsDialogs::SetRTL $(^RTL) ${|}
${NSD_CreateLabel} 0 0 100% 24u $R1
Pop $R1
${NSD_CreateRadioButton} 30u 50u -30u 8u $R2
Pop $R2
${NSD_OnClick} $R2 PageReinstallUpdateSelection
${NSD_CreateRadioButton} 30u 70u -30u 8u $R3
Pop $R3
; disable this radio button if downgrading and downgrades are disabled
!if "${ALLOWDOWNGRADES}" == "false"
${IfThen} $R0 == -1 ${|} EnableWindow $R3 0 ${|}
!endif
${NSD_OnClick} $R3 PageReinstallUpdateSelection
; Check the first radio button if this the first time
; we enter this page or if the second button wasn't
; selected the last time we were on this page
${If} $ReinstallPageCheck != 2
SendMessage $R2 ${BM_SETCHECK} ${BST_CHECKED} 0
${Else}
SendMessage $R3 ${BM_SETCHECK} ${BST_CHECKED} 0
${EndIf}
${NSD_SetFocus} $R2
nsDialogs::Show
FunctionEnd
Function PageReinstallUpdateSelection
${NSD_GetState} $R2 $R1
${If} $R1 == ${BST_CHECKED}
StrCpy $ReinstallPageCheck 1
${Else}
StrCpy $ReinstallPageCheck 2
${EndIf}
FunctionEnd
Function PageLeaveReinstall
${NSD_GetState} $R2 $R1
; $R5 holds whether we are reinstalling the same version or not
; $R5 == "1" -> different versions
; $R5 == "2" -> same version
;
; $R1 holds the radio buttons state. its meaning is dependant on the context
StrCmp $R5 "1" 0 +2 ; Existing install is not the same version?
StrCmp $R1 "1" reinst_uninstall reinst_done ; $R1 == "1", then user chose to uninstall existing version, otherwise skip uninstalling
StrCmp $R1 "1" reinst_done ; Same version? skip uninstalling
reinst_uninstall:
HideWindow
ClearErrors
${If} $R7 == "wix"
ReadRegStr $R1 HKLM "$R6" "UninstallString"
ExecWait '$R1' $0
${Else}
ReadRegStr $4 SHCTX "${MANUPRODUCTKEY}" ""
ReadRegStr $R1 SHCTX "${UNINSTKEY}" "UninstallString"
ExecWait '$R1 /P _?=$4' $0
${EndIf}
BringToFront
${IfThen} ${Errors} ${|} StrCpy $0 2 ${|} ; ExecWait failed, set fake exit code
${If} $0 <> 0
${OrIf} ${FileExists} "$INSTDIR\${MAINBINARYNAME}.exe"
${If} $0 = 1 ; User aborted uninstaller?
StrCmp $R5 "2" 0 +2 ; Is the existing install the same version?
Quit ; ...yes, already installed, we are done
Abort
${EndIf}
MessageBox MB_ICONEXCLAMATION "$(unableToUninstall)"
Abort
${Else}
StrCpy $0 $R1 1
${IfThen} $0 == '"' ${|} StrCpy $R1 $R1 -1 1 ${|} ; Strip quotes from UninstallString
Delete $R1
RMDir $INSTDIR
${EndIf}
reinst_done:
FunctionEnd
; 5. Choose install directoy page
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
!insertmacro MUI_PAGE_DIRECTORY
; 6. Start menu shortcut page
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
Var AppStartMenuFolder
!insertmacro MUI_PAGE_STARTMENU Application $AppStartMenuFolder
; 7. Installation page
!insertmacro MUI_PAGE_INSTFILES
; 8. Finish page
;
; Don't auto jump to finish page after installation page,
; because the installation page has useful info that can be used debug any issues with the installer.
!define MUI_FINISHPAGE_NOAUTOCLOSE
; Use show readme button in the finish page as a button create a desktop shortcut
!define MUI_FINISHPAGE_SHOWREADME
!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_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
!insertmacro MUI_PAGE_FINISH
; Uninstaller Pages
; 1. Confirm uninstall page
Var DeleteAppDataCheckbox
Var DeleteAppDataCheckboxState
!define /ifndef WS_EX_LAYOUTRTL 0x00400000
!define MUI_PAGE_CUSTOMFUNCTION_SHOW un.ConfirmShow
Function un.ConfirmShow
FindWindow $1 "#32770" "" $HWNDPARENT ; Find inner dialog
${If} $(^RTL) == 1
System::Call 'USER32::CreateWindowEx(i${__NSD_CheckBox_EXSTYLE}|${WS_EX_LAYOUTRTL},t"${__NSD_CheckBox_CLASS}",t "$(deleteAppData)",i${__NSD_CheckBox_STYLE},i 50,i 100,i 400, i 25,i$1,i0,i0,i0)i.s'
${Else}
System::Call 'USER32::CreateWindowEx(i${__NSD_CheckBox_EXSTYLE},t"${__NSD_CheckBox_CLASS}",t "$(deleteAppData)",i${__NSD_CheckBox_STYLE},i 0,i 100,i 400, i 25,i$1,i0,i0,i0)i.s'
${EndIf}
Pop $DeleteAppDataCheckbox
SendMessage $HWNDPARENT ${WM_GETFONT} 0 0 $1
SendMessage $DeleteAppDataCheckbox ${WM_SETFONT} $1 1
FunctionEnd
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE un.ConfirmLeave
Function un.ConfirmLeave
SendMessage $DeleteAppDataCheckbox ${BM_GETCHECK} 0 0 $DeleteAppDataCheckboxState
FunctionEnd
!insertmacro MUI_UNPAGE_CONFIRM
; 2. Uninstalling Page
!insertmacro MUI_UNPAGE_INSTFILES
;Languages
{{#each languages}}
!insertmacro MUI_LANGUAGE "{{this}}"
{{/each}}
!insertmacro MUI_RESERVEFILE_LANGDLL
{{#each language_files}}
!include "{{this}}"
{{/each}}
!macro SetContext
!if "${INSTALLMODE}" == "currentUser"
SetShellVarContext current
!else if "${INSTALLMODE}" == "perMachine"
SetShellVarContext all
!endif
${If} ${RunningX64}
!if "${ARCH}" == "x64"
SetRegView 64
!else if "${ARCH}" == "arm64"
SetRegView 64
!else
SetRegView 32
!endif
${EndIf}
!macroend
Var PassiveMode
Function .onInit
${GetOptions} $CMDLINE "/P" $PassiveMode
IfErrors +2 0
StrCpy $PassiveMode 1
!if "${DISPLAYLANGUAGESELECTOR}" == "true"
!insertmacro MUI_LANGDLL_DISPLAY
!endif
!insertmacro SetContext
${If} $INSTDIR == ""
; Set default install location
!if "${INSTALLMODE}" == "perMachine"
${If} ${RunningX64}
!if "${ARCH}" == "x64"
StrCpy $INSTDIR "$PROGRAMFILES64\${PRODUCTNAME}"
!else if "${ARCH}" == "arm64"
StrCpy $INSTDIR "$PROGRAMFILES64\${PRODUCTNAME}"
!else
StrCpy $INSTDIR "$PROGRAMFILES\${PRODUCTNAME}"
!endif
${Else}
StrCpy $INSTDIR "$PROGRAMFILES\${PRODUCTNAME}"
${EndIf}
!else if "${INSTALLMODE}" == "currentUser"
StrCpy $INSTDIR "$LOCALAPPDATA\${PRODUCTNAME}"
!endif
Call RestorePreviousInstallLocation
${EndIf}
!if "${INSTALLMODE}" == "both"
!insertmacro MULTIUSER_INIT
!endif
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
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}
; Check if clash-meta-alpha.exe is running
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}
; Check if clash-meta.exe is running
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}
!macroend
!macro StartVergeService
; Check if the service exists
SimpleSC::ExistsService "clash_verge_service"
Pop $0 ; 0:service exists;other: service not exists
; Service exists
${If} $0 == 0
Push $0
; Check if the service is running
SimpleSC::ServiceIsRunning "clash_verge_service"
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
Pop $1 ; returns 1 (service is running) - returns 0 (service is not running)
${If} $0 == 0
Push $0
${If} $1 == 0
DetailPrint "Restart Clash Verge Service..."
SimpleSC::StartService "clash_verge_service" "" 30
${EndIf}
${ElseIf} $0 != 0
Push $0
SimpleSC::GetErrorMessage
Pop $0
MessageBox MB_OK|MB_ICONSTOP "Check Service Status Error ($0)"
${EndIf}
${EndIf}
!macroend
!macro RemoveVergeService
; Check if the service exists
SimpleSC::ExistsService "clash_verge_service"
Pop $0 ; 0:service exists;other: service not exists
; Service exists
${If} $0 == 0
Push $0
; Check if the service is running
SimpleSC::ServiceIsRunning "clash_verge_service"
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
Pop $1 ; returns 1 (service is running) - returns 0 (service is not running)
${If} $0 == 0
Push $0
${If} $1 == 1
DetailPrint "Stop Clash Verge Service..."
SimpleSC::StopService "clash_verge_service" 1 30
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
${If} $0 == 0
DetailPrint "Removing Clash Verge Service..."
SimpleSC::RemoveService "clash_verge_service"
${ElseIf} $0 != 0
Push $0
SimpleSC::GetErrorMessage
Pop $0
MessageBox MB_OK|MB_ICONSTOP "Clash Verge Service Stop Error ($0)"
${EndIf}
${ElseIf} $1 == 0
DetailPrint "Removing Clash Verge Service..."
SimpleSC::RemoveService "clash_verge_service"
${EndIf}
${ElseIf} $0 != 0
Push $0
SimpleSC::GetErrorMessage
Pop $0
MessageBox MB_OK|MB_ICONSTOP "Check Service Status Error ($0)"
${EndIf}
${EndIf}
!macroend
Section EarlyChecks
; Abort silent installer if downgrades is disabled
!if "${ALLOWDOWNGRADES}" == "false"
IfSilent 0 silent_downgrades_done
; If downgrading
${If} $R0 == -1
System::Call 'kernel32::AttachConsole(i -1)i.r0'
${If} $0 != 0
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color
FileWrite $0 "$(silentDowngrades)"
${EndIf}
Abort
${EndIf}
silent_downgrades_done:
!endif
SectionEnd
Section WebView2
; Check if Webview2 is already installed and skip this section
${If} ${RunningX64}
ReadRegStr $4 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
${Else}
ReadRegStr $4 HKLM "SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
${EndIf}
ReadRegStr $5 HKCU "SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
StrCmp $4 "" 0 webview2_done
StrCmp $5 "" 0 webview2_done
; Webview2 install modes
!if "${INSTALLWEBVIEW2MODE}" == "downloadBootstrapper"
Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe"
DetailPrint "$(webview2Downloading)"
nsis_tauri_utils::download "https://go.microsoft.com/fwlink/p/?LinkId=2124703" "$TEMP\MicrosoftEdgeWebview2Setup.exe"
Pop $0
${If} $0 == 0
DetailPrint "$(webview2DownloadSuccess)"
${Else}
DetailPrint "$(webview2DownloadError)"
Abort "$(webview2AbortError)"
${EndIf}
StrCpy $6 "$TEMP\MicrosoftEdgeWebview2Setup.exe"
Goto install_webview2
!endif
!if "${INSTALLWEBVIEW2MODE}" == "embedBootstrapper"
Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe"
File "/oname=$TEMP\MicrosoftEdgeWebview2Setup.exe" "${WEBVIEW2BOOTSTRAPPERPATH}"
DetailPrint "$(installingWebview2)"
StrCpy $6 "$TEMP\MicrosoftEdgeWebview2Setup.exe"
Goto install_webview2
!endif
!if "${INSTALLWEBVIEW2MODE}" == "offlineInstaller"
Delete "$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe"
File "/oname=$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe" "${WEBVIEW2INSTALLERPATH}"
DetailPrint "$(installingWebview2)"
StrCpy $6 "$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe"
Goto install_webview2
!endif
Goto webview2_done
install_webview2:
DetailPrint "$(installingWebview2)"
; $6 holds the path to the webview2 installer
ExecWait "$6 ${WEBVIEW2INSTALLERARGS} /install" $1
${If} $1 == 0
DetailPrint "$(webview2InstallSuccess)"
${Else}
DetailPrint "$(webview2InstallError)"
Abort "$(webview2AbortError)"
${EndIf}
webview2_done:
SectionEnd
!macro CheckIfAppIsRunning
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::FindProcessCurrentUser "${MAINBINARYNAME}.exe"
!else
nsis_tauri_utils::FindProcess "${MAINBINARYNAME}.exe"
!endif
Pop $R0
${If} $R0 = 0
IfSilent kill 0
${IfThen} $PassiveMode != 1 ${|} MessageBox MB_OKCANCEL "$(appRunningOkKill)" IDOK kill IDCANCEL cancel ${|}
kill:
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::KillProcessCurrentUser "${MAINBINARYNAME}.exe"
!else
nsis_tauri_utils::KillProcess "${MAINBINARYNAME}.exe"
!endif
Pop $R0
Sleep 500
${If} $R0 = 0
Goto app_check_done
${Else}
IfSilent silent ui
silent:
System::Call 'kernel32::AttachConsole(i -1)i.r0'
${If} $0 != 0
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color
FileWrite $0 "$(appRunning)$\n"
${EndIf}
Abort
ui:
Abort "$(failedToKillApp)"
${EndIf}
cancel:
Abort "$(appRunning)"
${EndIf}
app_check_done:
!macroend
Section Install
SetOutPath $INSTDIR
!insertmacro CheckIfAppIsRunning
!insertmacro CheckAllVergeProcesses
; Copy main executable
File "${MAINBINARYSRCPATH}"
; Copy resources
{{#each resources_dirs}}
CreateDirectory "$INSTDIR\\{{this}}"
{{/each}}
{{#each resources}}
File /a "/oname={{this.[1]}}" "{{@key}}"
{{/each}}
; Copy external binaries
{{#each binaries}}
File /a "/oname={{this}}" "{{@key}}"
{{/each}}
!insertmacro StartVergeService
; Create uninstaller
WriteUninstaller "$INSTDIR\uninstall.exe"
; Save $INSTDIR in registry for future installations
WriteRegStr SHCTX "${MANUPRODUCTKEY}" "" $INSTDIR
!if "${INSTALLMODE}" == "both"
; Save install mode to be selected by default for the next installation such as updating
; or when uninstalling
WriteRegStr SHCTX "${UNINSTKEY}" $MultiUser.InstallMode 1
!endif
; Registry information for add/remove programs
WriteRegStr SHCTX "${UNINSTKEY}" "DisplayName" "${PRODUCTNAME}"
WriteRegStr SHCTX "${UNINSTKEY}" "DisplayIcon" "$\"$INSTDIR\${MAINBINARYNAME}.exe$\""
WriteRegStr SHCTX "${UNINSTKEY}" "DisplayVersion" "${VERSION}"
WriteRegStr SHCTX "${UNINSTKEY}" "Publisher" "${MANUFACTURER}"
WriteRegStr SHCTX "${UNINSTKEY}" "InstallLocation" "$\"$INSTDIR$\""
WriteRegStr SHCTX "${UNINSTKEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegDWORD SHCTX "${UNINSTKEY}" "NoModify" "1"
WriteRegDWORD SHCTX "${UNINSTKEY}" "NoRepair" "1"
WriteRegDWORD SHCTX "${UNINSTKEY}" "EstimatedSize" "${ESTIMATEDSIZE}"
; Create start menu shortcut (GUI)
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
Call CreateStartMenuShortcut
!insertmacro MUI_STARTMENU_WRITE_END
; Create shortcuts for silent and passive installers, which
; can be disabled by passing `/NS` flag
; GUI installer has buttons for users to control creating them
IfSilent check_ns_flag 0
${IfThen} $PassiveMode == 1 ${|} Goto check_ns_flag ${|}
Goto shortcuts_done
check_ns_flag:
${GetOptions} $CMDLINE "/NS" $R0
IfErrors 0 shortcuts_done
Call CreateDesktopShortcut
Call CreateStartMenuShortcut
shortcuts_done:
; Auto close this page for passive mode
${IfThen} $PassiveMode == 1 ${|} SetAutoClose true ${|}
SectionEnd
Function .onInstSuccess
; Check for `/R` flag only in silent and passive installers because
; GUI installer has a toggle for the user to (re)start the app
IfSilent check_r_flag 0
${IfThen} $PassiveMode == 1 ${|} Goto check_r_flag ${|}
Goto run_done
check_r_flag:
${GetOptions} $CMDLINE "/R" $R0
IfErrors run_done 0
Exec '"$INSTDIR\${MAINBINARYNAME}.exe"'
run_done:
FunctionEnd
Function un.onInit
!insertmacro SetContext
!if "${INSTALLMODE}" == "both"
!insertmacro MULTIUSER_UNINIT
!endif
!insertmacro MUI_UNGETLANGUAGE
FunctionEnd
Section Uninstall
!insertmacro CheckIfAppIsRunning
!insertmacro CheckAllVergeProcesses
!insertmacro RemoveVergeService
; Delete the app directory and its content from disk
; Copy main executable
Delete "$INSTDIR\${MAINBINARYNAME}.exe"
; Delete resources
{{#each resources}}
Delete "$INSTDIR\\{{this.[1]}}"
{{/each}}
; Delete external binaries
{{#each binaries}}
Delete "$INSTDIR\\{{this}}"
{{/each}}
; Delete uninstaller
Delete "$INSTDIR\uninstall.exe"
${If} $DeleteAppDataCheckboxState == 1
RMDir /R /REBOOTOK "$INSTDIR"
${Else}
{{#each resources_ancestors}}
RMDir /REBOOTOK "$INSTDIR\\{{this}}"
{{/each}}
RMDir "$INSTDIR"
${EndIf}
; Remove start menu shortcut
!insertmacro MUI_STARTMENU_GETFOLDER Application $AppStartMenuFolder
Delete "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk"
RMDir "$SMPROGRAMS\$AppStartMenuFolder"
; Remove desktop shortcuts
Delete "$DESKTOP\${MAINBINARYNAME}.lnk"
; Remove registry information for add/remove programs
!if "${INSTALLMODE}" == "both"
DeleteRegKey SHCTX "${UNINSTKEY}"
!else if "${INSTALLMODE}" == "perMachine"
DeleteRegKey HKLM "${UNINSTKEY}"
!else
DeleteRegKey HKCU "${UNINSTKEY}"
!endif
DeleteRegValue HKCU "${MANUPRODUCTKEY}" "Installer Language"
; Delete app data
${If} $DeleteAppDataCheckboxState == 1
SetShellVarContext current
RmDir /r "$APPDATA\${BUNDLEID}"
RmDir /r "$LOCALAPPDATA\${BUNDLEID}"
${EndIf}
${GetOptions} $CMDLINE "/P" $R0
IfErrors +2 0
SetAutoClose true
SectionEnd
Function RestorePreviousInstallLocation
ReadRegStr $4 SHCTX "${MANUPRODUCTKEY}" ""
StrCmp $4 "" +2 0
StrCpy $INSTDIR $4
FunctionEnd
Function SkipIfPassive
${IfThen} $PassiveMode == 1 ${|} Abort ${|}
FunctionEnd
Function CreateDesktopShortcut
CreateShortcut "$DESKTOP\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
ApplicationID::Set "$DESKTOP\${MAINBINARYNAME}.lnk" "${BUNDLEID}"
FunctionEnd
Function CreateStartMenuShortcut
CreateDirectory "$SMPROGRAMS\$AppStartMenuFolder"
CreateShortcut "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
ApplicationID::Set "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "${BUNDLEID}"
FunctionEnd
Binary file not shown.
+10
View File
@@ -0,0 +1,10 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="36" height="36" rx="18" fill="url(#paint0_linear_290_83)"/>
<path d="M26.1791 19.6042C25.8714 21.1676 25.1224 22.6103 24.0208 23.7615C22.9191 24.9128 21.5109 25.7246 19.9625 26.1008C20.3875 25.5992 20.7558 24.9492 21.0616 24.2117C21.3658 23.4783 21.62 22.6275 21.8083 21.695C23.2058 21.335 24.4716 20.7817 25.5333 20.0742C25.7566 19.925 25.9725 19.7683 26.1791 19.6042ZM9.82082 19.6042C10.0275 19.7683 10.2433 19.925 10.4666 20.0742C11.5275 20.7817 12.7933 21.335 14.1916 21.695C14.3791 22.6275 14.6333 23.4783 14.9383 24.2117C15.2433 24.9483 15.6116 25.5992 16.0366 26.1008C14.4884 25.7244 13.0803 24.9126 11.9788 23.7613C10.8773 22.6101 10.1285 21.1675 9.82082 19.6042ZM20.0225 22.0375C19.8927 22.5611 19.7254 23.0747 19.5216 23.5742C19.2325 24.2725 18.9141 24.7867 18.6125 25.1125C18.3041 25.4458 18.0925 25.5 18 25.5C17.9075 25.5 17.6966 25.4458 17.3875 25.1125C17.0858 24.7875 16.7675 24.2725 16.4783 23.5742C16.2746 23.0747 16.1072 22.5611 15.9775 22.0375C16.6341 22.1225 17.3108 22.1667 18 22.1667C18.6762 22.1671 19.3518 22.1239 20.0225 22.0375ZM20.4691 16.9408C20.5344 18.0629 20.4947 19.1886 20.3508 20.3033C19.6033 20.4317 18.815 20.5 18 20.5C17.2119 20.5009 16.4253 20.4351 15.6483 20.3033C15.5023 19.1888 15.463 18.0629 15.5308 16.9408C16.3191 17.0883 17.1475 17.1667 18 17.1667C18.8282 17.1676 19.6548 17.092 20.4691 16.9408ZM25.555 14.4792C25.9383 15.3008 26.1925 16.1958 26.2883 17.1367C25.8708 17.6975 25.3075 18.2217 24.6083 18.6867C23.8833 19.17 23.0283 19.5783 22.0758 19.8842C22.1845 18.7637 22.1962 17.6359 22.1108 16.5133C22.8976 16.2518 23.6486 15.8932 24.3466 15.4458C24.7812 15.1654 25.186 14.8416 25.555 14.4792ZM10.445 14.4792C10.8033 14.835 11.2116 15.1583 11.6533 15.4458C12.3512 15.8934 13.1023 16.252 13.8891 16.5133C13.804 17.6359 13.8154 18.7636 13.9233 19.8842C12.9708 19.5783 12.1158 19.1708 11.3908 18.6875C10.6925 18.2217 10.1291 17.6975 9.71082 17.1367C9.80748 16.195 10.0608 15.3008 10.4441 14.4783L10.445 14.4792ZM18 10.5C18.0916 10.5 18.3033 10.555 18.6125 10.8883C18.9141 11.2133 19.2325 11.7283 19.5216 12.4267C19.8466 13.2125 20.1166 14.1833 20.29 15.2775C19.5716 15.4217 18.8025 15.5 18 15.5C17.2309 15.5015 16.4636 15.4269 15.7091 15.2775C15.8825 14.1833 16.1525 13.2125 16.4783 12.4275C16.7675 11.7283 17.0858 11.2142 17.3866 10.8883C17.6966 10.555 17.9075 10.5 18 10.5ZM19.9625 9.89917C21.8406 10.3553 23.5019 11.4496 24.6625 12.995C24.3075 13.4034 23.8951 13.7582 23.4383 14.0483C22.9554 14.3584 22.4408 14.6162 21.9033 14.8175C21.7108 13.6833 21.42 12.655 21.0616 11.7892C20.7558 11.0517 20.3883 10.4008 19.9625 9.89917ZM16.0375 9.89917C15.6125 10.4008 15.2441 11.0517 14.9383 11.7892C14.58 12.6558 14.2883 13.6833 14.0966 14.8175C13.5592 14.6162 13.0446 14.3584 12.5616 14.0483C12.1046 13.7583 11.6919 13.4035 11.3366 12.995C12.4974 11.4494 14.1591 10.3551 16.0375 9.89917Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_290_83" x1="31" y1="27.5" x2="6.5" y2="7" gradientUnits="userSpaceOnUse">
<stop stop-color="#009038"/>
<stop offset="1" stop-color="#1CA350"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

+21
View File
@@ -0,0 +1,21 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="36" height="36" rx="18" fill="url(#paint0_linear_289_53)"/>
<rect width="36" height="36" rx="18" fill="url(#paint1_linear_289_53)"/>
<mask id="mask0_289_53" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="20" height="20">
<path d="M28 8V28H8V8H28ZM18.4942 27.3817L18.485 27.3833L18.4258 27.4125L18.4092 27.4158L18.3975 27.4125L18.3383 27.3833C18.33 27.38 18.3225 27.3825 18.3183 27.3875L18.315 27.3958L18.3008 27.7525L18.305 27.7692L18.3133 27.78L18.4 27.8417L18.4125 27.845L18.4225 27.8417L18.5092 27.78L18.5192 27.7667L18.5225 27.7525L18.5083 27.3967C18.5067 27.3883 18.5008 27.3825 18.4942 27.3817ZM18.715 27.2875L18.7042 27.2892L18.55 27.3667L18.5417 27.375L18.5392 27.3842L18.5542 27.7425L18.5583 27.7525L18.565 27.7583L18.7325 27.8358C18.7425 27.8392 18.7517 27.8358 18.7567 27.8292L18.76 27.8175L18.7317 27.3058C18.7292 27.2958 18.7233 27.2892 18.715 27.2875ZM18.1192 27.2892C18.1154 27.2874 18.1111 27.287 18.1071 27.2879C18.103 27.2888 18.0994 27.291 18.0967 27.2942L18.0917 27.3058L18.0633 27.8175C18.0633 27.8275 18.0692 27.8342 18.0775 27.8375L18.09 27.8358L18.2575 27.7583L18.2658 27.7517L18.2692 27.7425L18.2833 27.3842L18.2808 27.3742L18.2725 27.3658L18.1192 27.2892Z" fill="white"/>
</mask>
<g mask="url(#mask0_289_53)">
<path d="M15.9167 9.66669C15.5852 9.66669 15.2672 9.79838 15.0328 10.0328C14.7984 10.2672 14.6667 10.5852 14.6667 10.9167V11.75C14.6667 12.0815 14.7984 12.3995 15.0328 12.6339C15.2672 12.8683 15.5852 13 15.9167 13H20.0834C20.4149 13 20.7328 12.8683 20.9672 12.6339C21.2017 12.3995 21.3334 12.0815 21.3334 11.75V10.9167C21.3334 10.5852 21.2017 10.2672 20.9672 10.0328C20.7328 9.79838 20.4149 9.66669 20.0834 9.66669H15.9167Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.4167 11.3642C12.3683 11.4225 11.7067 11.5892 11.2325 12.0642C10.5 12.7967 10.5 13.975 10.5 16.3317V21.3317C10.5 23.6892 10.5 24.8675 11.2325 25.6C11.9642 26.3317 13.1433 26.3317 15.5 26.3317H20.5C22.8567 26.3317 24.0358 26.3317 24.7675 25.6C25.5 24.8667 25.5 23.6892 25.5 21.3317V16.3317C25.5 13.975 25.5 12.7967 24.7675 12.0642C24.2933 11.5892 23.6317 11.4225 22.5833 11.3642V11.75C22.5833 12.4131 22.3199 13.049 21.8511 13.5178C21.3823 13.9866 20.7464 14.25 20.0833 14.25H15.9167C15.2536 14.25 14.6177 13.9866 14.1489 13.5178C13.6801 13.049 13.4167 12.4131 13.4167 11.75V11.3642ZM13.8333 16.125C13.6676 16.125 13.5086 16.1909 13.3914 16.3081C13.2742 16.4253 13.2083 16.5843 13.2083 16.75C13.2083 16.9158 13.2742 17.0748 13.3914 17.192C13.5086 17.3092 13.6676 17.375 13.8333 17.375H14.25C14.4158 17.375 14.5747 17.3092 14.6919 17.192C14.8092 17.0748 14.875 16.9158 14.875 16.75C14.875 16.5843 14.8092 16.4253 14.6919 16.3081C14.5747 16.1909 14.4158 16.125 14.25 16.125H13.8333ZM16.75 16.125C16.5842 16.125 16.4253 16.1909 16.3081 16.3081C16.1908 16.4253 16.125 16.5843 16.125 16.75C16.125 16.9158 16.1908 17.0748 16.3081 17.192C16.4253 17.3092 16.5842 17.375 16.75 17.375H22.1667C22.3324 17.375 22.4914 17.3092 22.6086 17.192C22.7258 17.0748 22.7917 16.9158 22.7917 16.75C22.7917 16.5843 22.7258 16.4253 22.6086 16.3081C22.4914 16.1909 22.3324 16.125 22.1667 16.125H16.75ZM13.8333 19.0417C13.6676 19.0417 13.5086 19.1075 13.3914 19.2248C13.2742 19.342 13.2083 19.5009 13.2083 19.6667C13.2083 19.8325 13.2742 19.9914 13.3914 20.1086C13.5086 20.2258 13.6676 20.2917 13.8333 20.2917H14.25C14.4158 20.2917 14.5747 20.2258 14.6919 20.1086C14.8092 19.9914 14.875 19.8325 14.875 19.6667C14.875 19.5009 14.8092 19.342 14.6919 19.2248C14.5747 19.1075 14.4158 19.0417 14.25 19.0417H13.8333ZM16.75 19.0417C16.5842 19.0417 16.4253 19.1075 16.3081 19.2248C16.1908 19.342 16.125 19.5009 16.125 19.6667C16.125 19.8325 16.1908 19.9914 16.3081 20.1086C16.4253 20.2258 16.5842 20.2917 16.75 20.2917H22.1667C22.3324 20.2917 22.4914 20.2258 22.6086 20.1086C22.7258 19.9914 22.7917 19.8325 22.7917 19.6667C22.7917 19.5009 22.7258 19.342 22.6086 19.2248C22.4914 19.1075 22.3324 19.0417 22.1667 19.0417H16.75ZM13.8333 21.9584C13.6676 21.9584 13.5086 22.0242 13.3914 22.1414C13.2742 22.2586 13.2083 22.4176 13.2083 22.5834C13.2083 22.7491 13.2742 22.9081 13.3914 23.0253C13.5086 23.1425 13.6676 23.2084 13.8333 23.2084H14.25C14.4158 23.2084 14.5747 23.1425 14.6919 23.0253C14.8092 22.9081 14.875 22.7491 14.875 22.5834C14.875 22.4176 14.8092 22.2586 14.6919 22.1414C14.5747 22.0242 14.4158 21.9584 14.25 21.9584H13.8333ZM16.75 21.9584C16.5842 21.9584 16.4253 22.0242 16.3081 22.1414C16.1908 22.2586 16.125 22.4176 16.125 22.5834C16.125 22.7491 16.1908 22.9081 16.3081 23.0253C16.4253 23.1425 16.5842 23.2084 16.75 23.2084H22.1667C22.3324 23.2084 22.4914 23.1425 22.6086 23.0253C22.7258 22.9081 22.7917 22.7491 22.7917 22.5834C22.7917 22.4176 22.7258 22.2586 22.6086 22.1414C22.4914 22.0242 22.3324 21.9584 22.1667 21.9584H16.75Z" fill="white"/>
</g>
<defs>
<linearGradient id="paint0_linear_289_53" x1="6" y1="6.5" x2="29.5" y2="30.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#56718E"/>
<stop offset="1" stop-color="#4B6683"/>
</linearGradient>
<linearGradient id="paint1_linear_289_53" x1="6" y1="6.5" x2="29.5" y2="30.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#E96038"/>
<stop offset="1" stop-color="#E1451D"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

+15
View File
@@ -0,0 +1,15 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="36" height="36" rx="18" fill="url(#paint0_linear_293_142)"/>
<g clip-path="url(#clip0_293_142)">
<path d="M24.5167 13C24.7587 13.0001 24.9978 13.0529 25.2173 13.1547C25.4369 13.2566 25.6316 13.405 25.788 13.5897C25.9444 13.7744 26.0587 13.9909 26.1229 14.2243C26.1872 14.4576 26.1998 14.7021 26.16 14.9408L24.7717 23.2742C24.7068 23.6633 24.5059 24.0168 24.2049 24.2718C23.9038 24.5268 23.5221 24.6667 23.1275 24.6667H12.8725C12.478 24.6667 12.0962 24.5268 11.7952 24.2718C11.4941 24.0168 11.2933 23.6633 11.2284 23.2742L9.84003 14.9408C9.80022 14.7021 9.81287 14.4576 9.87711 14.2243C9.94135 13.9909 10.0556 13.7744 10.212 13.5897C10.3684 13.405 10.5632 13.2566 10.7827 13.1547C11.0023 13.0529 11.2413 13.0001 11.4834 13H24.5167ZM23 10.5C23.221 10.5 23.433 10.5878 23.5893 10.7441C23.7456 10.9004 23.8334 11.1123 23.8334 11.3333C23.8334 11.5543 23.7456 11.7663 23.5893 11.9226C23.433 12.0789 23.221 12.1667 23 12.1667H13C12.779 12.1667 12.5671 12.0789 12.4108 11.9226C12.2545 11.7663 12.1667 11.5543 12.1667 11.3333C12.1667 11.1123 12.2545 10.9004 12.4108 10.7441C12.5671 10.5878 12.779 10.5 13 10.5H23Z" fill="white"/>
</g>
<defs>
<linearGradient id="paint0_linear_293_142" x1="31" y1="27.5" x2="6.5" y2="7" gradientUnits="userSpaceOnUse">
<stop stop-color="#6038CB"/>
<stop offset="1" stop-color="#704ADC"/>
</linearGradient>
<clipPath id="clip0_293_142">
<rect width="20" height="20" fill="white" transform="translate(8 8)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+15
View File
@@ -0,0 +1,15 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="36" height="36" rx="18" fill="url(#paint0_linear_294_169)"/>
<g clip-path="url(#clip0_294_169)">
<path d="M18 22.1667C18.221 22.1667 18.433 22.2545 18.5893 22.4107C18.7455 22.567 18.8333 22.779 18.8333 23C18.8333 23.221 18.7455 23.433 18.5893 23.5893C18.433 23.7455 18.221 23.8333 18 23.8333C17.779 23.8333 17.567 23.7455 17.4107 23.5893C17.2545 23.433 17.1667 23.221 17.1667 23C17.1667 22.779 17.2545 22.567 17.4107 22.4107C17.567 22.2545 17.779 22.1667 18 22.1667ZM18 18.8333C19.15 18.8333 20.1933 19.3 20.9467 20.0533C21.103 20.2097 21.1909 20.4218 21.1909 20.6429C21.1909 20.8641 21.103 21.0761 20.9467 21.2325C20.7903 21.3889 20.5782 21.4767 20.3571 21.4767C20.136 21.4767 19.9239 21.3889 19.7675 21.2325C19.5357 20.9999 19.2603 20.8154 18.9569 20.6897C18.6536 20.564 18.3284 20.4995 18 20.5C17.3092 20.5 16.6858 20.7792 16.2325 21.2325C16.0761 21.3889 15.8641 21.4767 15.6429 21.4767C15.4218 21.4767 15.2097 21.3889 15.0533 21.2325C14.897 21.0761 14.8091 20.8641 14.8091 20.6429C14.8091 20.4218 14.897 20.2097 15.0533 20.0533C15.44 19.666 15.8993 19.3588 16.405 19.1494C16.9107 18.9401 17.4527 18.8326 18 18.8333ZM18 15.5C18.9851 15.4989 19.9607 15.6923 20.8708 16.0693C21.7809 16.4463 22.6076 16.9993 23.3033 17.6967C23.4551 17.8538 23.5391 18.0643 23.5372 18.2828C23.5353 18.5013 23.4477 18.7103 23.2932 18.8648C23.1387 19.0194 22.9297 19.107 22.7112 19.1089C22.4927 19.1108 22.2822 19.0268 22.125 18.875C21.584 18.3324 20.941 17.9022 20.2331 17.609C19.5251 17.3158 18.7662 17.1655 18 17.1667C17.2338 17.1655 16.4749 17.3158 15.7669 17.609C15.059 17.9022 14.416 18.3324 13.875 18.875C13.7178 19.0268 13.5073 19.1108 13.2888 19.1089C13.0703 19.107 12.8613 19.0194 12.7068 18.8648C12.5523 18.7103 12.4647 18.5013 12.4628 18.2828C12.4609 18.0643 12.5449 17.8538 12.6967 17.6967C13.3924 16.9993 14.2191 16.4463 15.1292 16.0693C16.0393 15.6923 17.0149 15.4989 18 15.5ZM18 12.1667C20.9917 12.1667 23.7 13.38 25.66 15.34C25.8118 15.4972 25.8958 15.7077 25.8939 15.9262C25.892 16.1447 25.8044 16.3537 25.6499 16.5082C25.4953 16.6627 25.2863 16.7503 25.0678 16.7522C24.8493 16.7541 24.6388 16.6701 24.4817 16.5183C23.6315 15.6657 22.6212 14.9896 21.5089 14.5288C20.3965 14.068 19.204 13.8316 18 13.8333C16.796 13.8315 15.6035 14.0679 14.4911 14.5286C13.3787 14.9894 12.3684 15.6656 11.5183 16.5183C11.3612 16.6701 11.1507 16.7541 10.9322 16.7522C10.7137 16.7503 10.5047 16.6627 10.3502 16.5082C10.1957 16.3537 10.108 16.1447 10.1061 15.9262C10.1042 15.7077 10.1882 15.4972 10.34 15.34C11.3449 14.3326 12.5389 13.5337 13.8534 12.9891C15.1679 12.4445 16.5771 12.165 18 12.1667Z" fill="white"/>
</g>
<defs>
<linearGradient id="paint0_linear_294_169" x1="31" y1="27.5" x2="6.5" y2="7" gradientUnits="userSpaceOnUse">
<stop stop-color="#21B2CB"/>
<stop offset="1" stop-color="#3EC5D2"/>
</linearGradient>
<clipPath id="clip0_294_169">
<rect width="20" height="20" fill="white" transform="translate(8 8)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

+15
View File
@@ -0,0 +1,15 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="36" height="36" rx="18" fill="url(#paint0_linear_294_182)"/>
<g clip-path="url(#clip0_294_182)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.7759 10.6333C24.307 10.1647 23.6713 9.90137 23.0084 9.90137C22.3454 9.90137 21.7097 10.1647 21.2409 10.6333L20.6517 11.2233L24.7767 15.3483L25.365 14.7592C25.5972 14.527 25.7814 14.2514 25.9071 13.9481C26.0327 13.6447 26.0974 13.3196 26.0974 12.9913C26.0974 12.6629 26.0327 12.3378 25.9071 12.0344C25.7814 11.7311 25.5972 11.4555 25.365 11.2233L24.7759 10.6333ZM23.5975 16.5267L19.4725 12.4017L11.8975 19.9775C11.7318 20.1433 11.616 20.3524 11.5634 20.5808L10.7059 24.2933C10.6739 24.4312 10.6776 24.5748 10.7165 24.7109C10.7554 24.8469 10.8284 24.9707 10.9284 25.0708C11.0284 25.1708 11.1523 25.2437 11.2883 25.2827C11.4243 25.3216 11.568 25.3253 11.7059 25.2933L15.4192 24.4367C15.6474 24.3839 15.8561 24.2681 16.0217 24.1025L23.5975 16.5267Z" fill="white"/>
</g>
<defs>
<linearGradient id="paint0_linear_294_182" x1="31" y1="27.5" x2="6.5" y2="7" gradientUnits="userSpaceOnUse">
<stop stop-color="#FB4293"/>
<stop offset="1" stop-color="#F957A1"/>
</linearGradient>
<clipPath id="clip0_294_182">
<rect width="20" height="20" fill="white" transform="translate(8 8)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+15
View File
@@ -0,0 +1,15 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="36" height="36" rx="18" fill="url(#paint0_linear_289_60)"/>
<rect width="36" height="36" rx="18" fill="url(#paint1_linear_289_60)"/>
<path d="M20.0083 9.91083C20.8155 10.1108 21.5881 10.431 22.3 10.8608C22.4427 10.947 22.5563 11.0739 22.6262 11.2253C22.696 11.3767 22.7188 11.5455 22.6916 11.71C22.5975 12.2842 22.74 12.7233 23.0083 12.9917C23.2766 13.26 23.7166 13.4025 24.29 13.3083C24.4545 13.2812 24.6233 13.304 24.7747 13.3738C24.9261 13.4437 25.053 13.5573 25.1391 13.7C25.569 14.4119 25.8893 15.1844 26.0891 15.9917C26.1294 16.1536 26.1201 16.3239 26.0624 16.4805C26.0048 16.6371 25.9015 16.7728 25.7658 16.87C25.2933 17.2092 25.0825 17.62 25.0825 18C25.0825 18.38 25.2933 18.7917 25.7658 19.1308C25.9013 19.228 26.0045 19.3635 26.0622 19.52C26.1198 19.6764 26.1292 19.8465 26.0891 20.0083C25.8892 20.8156 25.569 21.5881 25.1391 22.3C25.053 22.4427 24.9261 22.5564 24.7747 22.6262C24.6233 22.696 24.4545 22.7188 24.29 22.6917C23.7158 22.5975 23.2766 22.7408 23.0083 23.0083C22.74 23.2767 22.5975 23.7167 22.6916 24.29C22.719 24.4546 22.6962 24.6236 22.6264 24.7752C22.5566 24.9267 22.4429 25.0538 22.3 25.14C21.5881 25.5698 20.8155 25.8901 20.0083 26.09C19.8464 26.1302 19.6761 26.1209 19.5195 26.0633C19.3629 26.0056 19.2272 25.9023 19.13 25.7667C18.7908 25.2933 18.38 25.0833 18 25.0833C17.62 25.0833 17.2083 25.2933 16.8691 25.7667C16.772 25.9022 16.6365 26.0054 16.48 26.063C16.3236 26.1207 16.1535 26.1301 15.9916 26.09C15.1844 25.8901 14.4119 25.5698 13.7 25.14C13.5571 25.0538 13.4434 24.9267 13.3736 24.7752C13.3037 24.6236 13.281 24.4546 13.3083 24.29C13.4025 23.7167 13.2591 23.2775 12.9916 23.0092C12.7225 22.7408 12.2833 22.5975 11.71 22.6925C11.5454 22.7198 11.3763 22.6971 11.2248 22.6273C11.0733 22.5574 10.9462 22.4437 10.86 22.3008C10.43 21.5884 10.1098 20.8153 9.90998 20.0075C9.86992 19.8457 9.87931 19.6756 9.93695 19.5191C9.99459 19.3627 10.0978 19.2271 10.2333 19.13C10.7066 18.7917 10.9166 18.38 10.9166 18C10.9166 17.6208 10.7066 17.2083 10.2333 16.87C10.0978 16.7729 9.99459 16.6373 9.93695 16.4809C9.87931 16.3245 9.86992 16.1543 9.90998 15.9925C10.1098 15.185 10.4301 14.4122 10.86 13.7C10.9462 13.5571 11.0733 13.4434 11.2248 13.3736C11.3763 13.3037 11.5454 13.281 11.71 13.3083C12.2833 13.4025 12.7225 13.26 12.9908 12.9917C13.2591 12.7233 13.4025 12.2833 13.3075 11.71C13.2803 11.5455 13.3032 11.3767 13.373 11.2253C13.4428 11.0739 13.5564 10.947 13.6991 10.8608C14.4113 10.4309 15.1841 10.1107 15.9916 9.91083C16.1535 9.87077 16.3236 9.88017 16.48 9.93781C16.6365 9.99545 16.772 10.0987 16.8691 10.2342C17.2083 10.7067 17.62 10.9175 18 10.9175C18.3791 10.9175 18.7916 10.7067 19.13 10.2342C19.2271 10.0987 19.3627 9.99545 19.5191 9.93781C19.6755 9.88017 19.8465 9.87077 20.0083 9.91083ZM18 13.8333C16.8949 13.8333 15.8351 14.2723 15.0537 15.0537C14.2723 15.8351 13.8333 16.8949 13.8333 18C13.8333 19.1051 14.2723 20.1649 15.0537 20.9463C15.8351 21.7277 16.8949 22.1667 18 22.1667C19.105 22.1667 20.1649 21.7277 20.9463 20.9463C21.7277 20.1649 22.1666 19.1051 22.1666 18C22.1666 16.8949 21.7277 15.8351 20.9463 15.0537C20.1649 14.2723 19.105 13.8333 18 13.8333ZM18 15.5C18.663 15.5 19.2989 15.7634 19.7677 16.2322C20.2366 16.7011 20.5 17.337 20.5 18C20.5 18.663 20.2366 19.2989 19.7677 19.7678C19.2989 20.2366 18.663 20.5 18 20.5C17.3369 20.5 16.7011 20.2366 16.2322 19.7678C15.7634 19.2989 15.5 18.663 15.5 18C15.5 17.337 15.7634 16.7011 16.2322 16.2322C16.7011 15.7634 17.3369 15.5 18 15.5Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_289_60" x1="0" y1="0" x2="36" y2="36" gradientUnits="userSpaceOnUse">
<stop stop-color="#E95E36"/>
<stop offset="1" stop-color="#E0421D"/>
</linearGradient>
<linearGradient id="paint1_linear_289_60" x1="6" y1="6.5" x2="29.5" y2="30.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#56718E"/>
<stop offset="1" stop-color="#4B6683"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

+15
View File
@@ -0,0 +1,15 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="36" height="36" rx="18" fill="url(#paint0_linear_293_156)"/>
<g clip-path="url(#clip0_293_156)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M18 11.3334C16.4977 11.3327 15.0392 11.8395 13.861 12.7715C12.6828 13.7036 11.854 15.0063 11.5088 16.4684C11.1637 17.9305 11.3225 19.4663 11.9595 20.8269C12.5965 22.1874 13.6744 23.2929 15.0184 23.9642C15.2161 24.0632 15.3663 24.2367 15.4361 24.4465C15.5059 24.6563 15.4895 24.8852 15.3904 25.0829C15.2914 25.2806 15.1179 25.4309 14.9081 25.5007C14.6983 25.5705 14.4694 25.554 14.2717 25.455C12.8875 24.7626 11.7236 23.6984 10.9102 22.3816C10.0969 21.0649 9.66628 19.5477 9.66669 18C9.66669 13.3975 13.3975 9.66669 18 9.66669C22.6025 9.66669 26.3334 13.3975 26.3334 18C26.3338 19.5477 25.9032 21.0649 25.0898 22.3816C24.2765 23.6984 23.1125 24.7626 21.7284 25.455C21.6305 25.504 21.5239 25.5333 21.4147 25.5411C21.3055 25.549 21.1958 25.5352 21.0919 25.5007C20.988 25.4661 20.892 25.4115 20.8092 25.3398C20.7265 25.2681 20.6586 25.1808 20.6096 25.0829C20.5606 24.985 20.5313 24.8785 20.5235 24.7693C20.5156 24.6601 20.5294 24.5504 20.5639 24.4465C20.5985 24.3426 20.6532 24.2466 20.7248 24.1638C20.7965 24.081 20.8838 24.0132 20.9817 23.9642C22.3257 23.2929 23.4036 22.1874 24.0406 20.8269C24.6776 19.4663 24.8364 17.9305 24.4912 16.4684C24.1461 15.0063 23.3172 13.7036 22.139 12.7715C20.9608 11.8395 19.5023 11.3327 18 11.3334ZM18 14.6667C17.2487 14.6662 16.5193 14.9195 15.93 15.3855C15.3407 15.8516 14.9262 16.503 14.7536 17.2342C14.581 17.9654 14.6604 18.7335 14.9791 19.4139C15.2978 20.0943 15.837 20.647 16.5092 20.9825C16.7017 21.0844 16.8466 21.2575 16.9131 21.4649C16.9795 21.6723 16.9621 21.8974 16.8647 22.0921C16.7672 22.2869 16.5974 22.4357 16.3915 22.5068C16.1857 22.5779 15.9602 22.5656 15.7634 22.4725C14.7561 21.9686 13.9485 21.1394 13.4714 20.1192C12.9942 19.0991 12.8754 17.9477 13.1343 16.8516C13.3932 15.7555 14.0146 14.7789 14.8977 14.08C15.7809 13.3811 16.8742 13.0009 18.0004 13.0009C19.1267 13.0009 20.22 13.3811 21.1031 14.08C21.9863 14.7789 22.6077 15.7555 22.8666 16.8516C23.1254 17.9477 23.0067 19.0991 22.5295 20.1192C22.0524 21.1394 21.2448 21.9686 20.2375 22.4725C20.0402 22.5695 19.8125 22.5846 19.6041 22.5144C19.3957 22.4443 19.2235 22.2946 19.1249 22.098C19.0264 21.9014 19.0096 21.6738 19.0781 21.4649C19.1467 21.2559 19.295 21.0826 19.4909 20.9825C20.1631 20.647 20.7023 20.0943 21.0209 19.4139C21.3396 18.7335 21.4191 17.9654 21.2465 17.2342C21.0739 16.503 20.6593 15.8516 20.07 15.3855C19.4808 14.9195 18.7513 14.6662 18 14.6667ZM16.3334 18C16.3334 17.558 16.5089 17.1341 16.8215 16.8215C17.1341 16.5089 17.558 16.3334 18 16.3334C18.442 16.3334 18.866 16.5089 19.1785 16.8215C19.4911 17.1341 19.6667 17.558 19.6667 18C19.6667 18.442 19.4911 18.866 19.1785 19.1785C18.866 19.4911 18.442 19.6667 18 19.6667C17.558 19.6667 17.1341 19.4911 16.8215 19.1785C16.5089 18.866 16.3334 18.442 16.3334 18Z" fill="white"/>
</g>
<defs>
<linearGradient id="paint0_linear_293_156" x1="31" y1="27.5" x2="6.5" y2="7" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFA800"/>
<stop offset="1" stop-color="#FFAC4B"/>
</linearGradient>
<clipPath id="clip0_293_156">
<rect width="20" height="20" fill="white" transform="translate(8 8)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+18
View File
@@ -0,0 +1,18 @@
<svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400"
height="400" viewBox="0, 0, 400,400">
<defs>
<linearGradient id="Gradient2" x1="0" x2="-0.1" y1="0" y2="1">
<stop offset="0%" stop-color="#5b5c9d" />
<stop offset="80%" stop-color="rgb(63, 64, 109)" />
<stop offset="100%" stop-color="rgb(63, 64, 109)" />
</linearGradient>
</defs>
<g id="svgg">
<path id="path0"
d="M118.124 39.858 C 116.645 40.182,114.562 42.930,113.283 46.247 C 111.908 49.812,110.887 54.542,107.205 74.400 C 106.736 76.930,105.926 81.160,105.404 83.800 C 104.527 88.237,103.654 92.889,102.177 101.000 C 101.856 102.760,101.413 105.100,101.192 106.200 C 100.754 108.375,100.784 108.199,98.436 122.200 C 96.675 132.702,95.947 137.215,94.984 143.600 C 94.636 145.910,94.098 149.420,93.789 151.400 C 92.649 158.697,91.961 164.413,92.180 164.768 C 92.304 164.968,92.223 165.192,92.000 165.266 C 91.777 165.340,96.852 165.347,103.277 165.282 C 116.520 165.149,115.721 165.379,116.358 161.519 C 122.839 122.279,179.334 121.580,186.617 160.650 C 186.981 162.602,187.463 164.425,187.689 164.700 C 188.328 165.481,229.717 165.423,230.367 164.640 C 230.622 164.332,231.013 162.937,231.235 161.540 C 237.531 121.949,295.239 121.807,301.620 161.367 C 302.274 165.421,301.855 165.252,310.962 165.117 C 315.273 165.052,318.861 165.090,318.935 165.200 C 319.009 165.310,319.005 165.152,318.927 164.848 C 318.849 164.545,318.519 162.610,318.193 160.548 C 317.867 158.487,317.330 155.135,317.000 153.100 C 316.162 147.934,315.312 142.663,314.823 139.600 C 314.595 138.170,314.226 136.010,314.004 134.800 C 313.781 133.590,312.423 125.400,310.985 116.600 C 304.278 75.545,304.008 74.156,298.145 50.400 C 295.961 41.553,294.621 39.584,290.800 39.611 C 287.927 39.631,283.053 43.395,279.420 48.400 C 277.509 51.032,261.566 79.109,256.960 87.953 C 253.991 93.654,253.647 93.789,245.600 92.402 C 218.757 87.774,194.454 87.780,170.478 92.420 C 162.868 93.893,163.987 94.596,157.579 84.306 C 133.879 46.247,126.566 38.009,118.124 39.858 M147.277 134.807 C 123.365 138.118,111.083 165.918,124.948 185.349 C 140.543 207.202,173.704 202.274,182.466 176.800 C 190.000 154.900,170.471 131.596,147.277 134.807 M262.051 134.810 C 235.780 138.440,224.730 170.720,243.356 189.422 C 264.398 210.548,299.874 195.275,298.689 165.600 C 297.927 146.523,280.892 132.207,262.051 134.810 M157.600 153.840 C 163.092 156.341,166.343 161.914,165.902 168.074 C 164.875 182.425,145.082 186.221,138.715 173.287 C 132.759 161.189,145.324 148.250,157.600 153.840 M273.193 153.597 C 279.788 156.926,283.287 165.159,280.844 171.600 C 275.229 186.406,253.594 183.910,252.135 168.287 C 251.125 157.467,263.609 148.758,273.193 153.597 M91.200 168.809 C 91.200 169.522,90.939 171.836,90.621 173.952 C 89.933 178.523,87.567 196.170,85.788 210.000 C 85.462 212.530,85.012 215.860,84.786 217.400 C 84.561 218.940,84.191 221.820,83.964 223.800 C 83.737 225.780,83.288 229.560,82.965 232.200 C 81.886 241.026,80.172 255.664,79.404 262.600 C 78.867 267.450,78.532 270.381,76.987 283.800 C 76.251 290.187,75.405 297.881,74.568 305.800 C 74.220 309.100,73.789 313.150,73.613 314.800 C 73.436 316.450,73.063 320.230,72.784 323.200 C 72.504 326.170,72.169 328.723,72.038 328.874 C 71.701 329.262,59.638 327.033,54.028 325.546 C 34.668 320.412,26.096 301.951,35.625 285.911 C 38.026 281.869,41.515 278.587,49.795 272.581 C 58.081 266.570,59.262 265.247,59.510 261.702 C 59.969 255.136,50.677 252.070,40.551 255.447 C -6.127 271.014,-3.894 337.227,43.806 351.951 C 50.541 354.030,58.050 355.239,67.760 355.807 C 72.410 356.079,75.202 356.542,82.181 358.199 C 88.822 359.777,100.215 360.425,156.000 362.398 C 170.725 362.918,255.192 362.921,267.760 362.401 C 273.062 362.182,281.900 361.820,287.400 361.597 C 302.851 360.972,326.558 359.339,333.200 358.444 C 343.055 357.116,343.889 354.966,341.839 336.200 C 341.502 333.120,340.948 327.900,340.607 324.600 C 340.266 321.300,339.709 315.990,339.370 312.800 C 339.030 309.610,338.499 304.570,338.189 301.600 C 337.022 290.393,335.650 278.160,334.622 269.800 C 333.105 257.460,332.638 254.027,330.408 238.800 C 329.716 234.070,328.983 229.030,328.781 227.600 C 328.579 226.170,328.231 223.920,328.008 222.600 C 327.785 221.280,326.695 214.260,325.587 207.000 C 324.478 199.740,323.124 191.010,322.578 187.600 C 322.032 184.190,321.325 179.780,321.007 177.800 C 320.689 175.820,320.203 172.711,319.925 170.892 C 319.508 168.155,319.315 167.635,318.810 167.885 C 318.475 168.051,314.780 168.279,310.600 168.393 C 301.870 168.631,302.159 168.535,301.810 171.300 C 299.468 189.825,283.036 203.371,264.230 202.279 C 247.552 201.310,233.532 188.465,231.203 172.020 C 230.651 168.125,232.337 168.400,209.000 168.400 C 185.703 168.400,187.432 168.114,186.814 172.064 C 180.639 211.474,123.556 212.329,116.559 173.117 C 115.684 168.218,116.787 168.629,103.881 168.400 C 97.676 168.290,92.285 168.046,91.900 167.857 C 91.307 167.567,91.200 167.712,91.200 168.809 M211.015 197.632 C 214.247 200.942,215.394 201.357,220.447 201.050 C 225.236 200.759,225.846 201.071,224.310 203.023 C 221.899 206.089,216.333 205.843,210.779 202.425 C 208.312 200.907,207.846 200.906,205.600 202.405 C 200.180 206.022,193.561 206.136,191.600 202.647 C 190.824 201.267,191.555 200.665,193.600 201.001 C 199.643 201.993,201.561 201.450,204.900 197.805 C 207.534 194.929,208.352 194.906,211.015 197.632 "
stroke="none" fill="url(#Gradient2)" fill-rule="evenodd"></path>
<path id="path1"
d="M148.000 131.622 C 132.020 133.140,119.114 145.293,116.377 161.400 C 115.675 165.535,116.697 165.239,103.579 165.112 C 97.252 165.050,92.159 165.135,92.261 165.300 C 92.364 165.465,92.279 165.600,92.074 165.600 C 91.391 165.600,91.684 167.617,92.424 168.013 C 92.839 168.235,97.730 168.400,103.897 168.400 C 116.551 168.400,115.666 168.070,116.562 173.117 C 123.522 212.326,180.639 211.470,186.814 172.064 C 187.432 168.114,185.703 168.400,209.000 168.400 C 232.337 168.400,230.651 168.125,231.203 172.020 C 233.532 188.465,247.552 201.310,264.230 202.279 C 283.039 203.371,299.468 189.826,301.811 171.294 C 302.169 168.458,302.345 168.400,310.600 168.400 C 318.988 168.400,319.609 168.159,318.852 165.200 C 318.824 165.090,315.273 165.052,310.961 165.117 C 301.855 165.252,302.274 165.421,301.620 161.367 C 295.239 121.807,237.531 121.949,231.235 161.540 C 231.013 162.937,230.622 164.332,230.367 164.640 C 229.717 165.423,188.328 165.481,187.689 164.700 C 187.463 164.425,186.981 162.602,186.617 160.650 C 183.194 142.288,166.648 129.850,148.000 131.622 M159.886 135.622 C 183.880 141.759,192.035 172.033,174.435 189.636 C 156.404 207.671,125.892 198.962,119.944 174.084 C 114.368 150.761,136.343 129.601,159.886 135.622 M274.658 135.615 C 299.082 141.861,307.061 172.817,288.734 190.222 C 267.975 209.936,234.188 195.420,234.219 166.800 C 234.241 145.502,253.935 130.315,274.658 135.615 M148.542 152.793 C 139.499 154.706,134.598 164.925,138.715 173.287 C 145.082 186.221,164.875 182.425,165.902 168.074 C 166.591 158.446,157.916 150.811,148.542 152.793 M262.091 153.188 C 253.922 156.127,249.883 165.408,253.408 173.139 C 257.835 182.850,271.593 184.491,278.201 176.097 C 287.389 164.426,275.974 148.192,262.091 153.188 M204.900 197.805 C 201.561 201.450,199.643 201.993,193.600 201.001 C 191.555 200.665,190.824 201.267,191.600 202.647 C 193.561 206.136,200.180 206.022,205.600 202.405 C 207.846 200.906,208.312 200.907,210.779 202.425 C 216.333 205.843,221.899 206.089,224.310 203.023 C 225.846 201.071,225.236 200.759,220.447 201.050 C 215.394 201.357,214.247 200.942,211.015 197.632 C 208.352 194.906,207.534 194.929,204.900 197.805 "
stroke="none" fill="#ffffff" fill-rule="evenodd"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

+24 -17
View File
@@ -1,18 +1,25 @@
<svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400"
height="400" viewBox="0, 0, 400,400">
<defs>
<linearGradient id="Gradient2" x1="0" x2="-0.1" y1="0" y2="1">
<stop offset="0%" stop-color="#5b5c9d" />
<stop offset="80%" stop-color="rgb(63, 64, 109)" />
<stop offset="100%" stop-color="rgb(63, 64, 109)" />
</linearGradient>
</defs>
<g id="svgg">
<path id="path0"
d="M118.124 39.858 C 116.645 40.182,114.562 42.930,113.283 46.247 C 111.908 49.812,110.887 54.542,107.205 74.400 C 106.736 76.930,105.926 81.160,105.404 83.800 C 104.527 88.237,103.654 92.889,102.177 101.000 C 101.856 102.760,101.413 105.100,101.192 106.200 C 100.754 108.375,100.784 108.199,98.436 122.200 C 96.675 132.702,95.947 137.215,94.984 143.600 C 94.636 145.910,94.098 149.420,93.789 151.400 C 92.649 158.697,91.961 164.413,92.180 164.768 C 92.304 164.968,92.223 165.192,92.000 165.266 C 91.777 165.340,96.852 165.347,103.277 165.282 C 116.520 165.149,115.721 165.379,116.358 161.519 C 122.839 122.279,179.334 121.580,186.617 160.650 C 186.981 162.602,187.463 164.425,187.689 164.700 C 188.328 165.481,229.717 165.423,230.367 164.640 C 230.622 164.332,231.013 162.937,231.235 161.540 C 237.531 121.949,295.239 121.807,301.620 161.367 C 302.274 165.421,301.855 165.252,310.962 165.117 C 315.273 165.052,318.861 165.090,318.935 165.200 C 319.009 165.310,319.005 165.152,318.927 164.848 C 318.849 164.545,318.519 162.610,318.193 160.548 C 317.867 158.487,317.330 155.135,317.000 153.100 C 316.162 147.934,315.312 142.663,314.823 139.600 C 314.595 138.170,314.226 136.010,314.004 134.800 C 313.781 133.590,312.423 125.400,310.985 116.600 C 304.278 75.545,304.008 74.156,298.145 50.400 C 295.961 41.553,294.621 39.584,290.800 39.611 C 287.927 39.631,283.053 43.395,279.420 48.400 C 277.509 51.032,261.566 79.109,256.960 87.953 C 253.991 93.654,253.647 93.789,245.600 92.402 C 218.757 87.774,194.454 87.780,170.478 92.420 C 162.868 93.893,163.987 94.596,157.579 84.306 C 133.879 46.247,126.566 38.009,118.124 39.858 M147.277 134.807 C 123.365 138.118,111.083 165.918,124.948 185.349 C 140.543 207.202,173.704 202.274,182.466 176.800 C 190.000 154.900,170.471 131.596,147.277 134.807 M262.051 134.810 C 235.780 138.440,224.730 170.720,243.356 189.422 C 264.398 210.548,299.874 195.275,298.689 165.600 C 297.927 146.523,280.892 132.207,262.051 134.810 M157.600 153.840 C 163.092 156.341,166.343 161.914,165.902 168.074 C 164.875 182.425,145.082 186.221,138.715 173.287 C 132.759 161.189,145.324 148.250,157.600 153.840 M273.193 153.597 C 279.788 156.926,283.287 165.159,280.844 171.600 C 275.229 186.406,253.594 183.910,252.135 168.287 C 251.125 157.467,263.609 148.758,273.193 153.597 M91.200 168.809 C 91.200 169.522,90.939 171.836,90.621 173.952 C 89.933 178.523,87.567 196.170,85.788 210.000 C 85.462 212.530,85.012 215.860,84.786 217.400 C 84.561 218.940,84.191 221.820,83.964 223.800 C 83.737 225.780,83.288 229.560,82.965 232.200 C 81.886 241.026,80.172 255.664,79.404 262.600 C 78.867 267.450,78.532 270.381,76.987 283.800 C 76.251 290.187,75.405 297.881,74.568 305.800 C 74.220 309.100,73.789 313.150,73.613 314.800 C 73.436 316.450,73.063 320.230,72.784 323.200 C 72.504 326.170,72.169 328.723,72.038 328.874 C 71.701 329.262,59.638 327.033,54.028 325.546 C 34.668 320.412,26.096 301.951,35.625 285.911 C 38.026 281.869,41.515 278.587,49.795 272.581 C 58.081 266.570,59.262 265.247,59.510 261.702 C 59.969 255.136,50.677 252.070,40.551 255.447 C -6.127 271.014,-3.894 337.227,43.806 351.951 C 50.541 354.030,58.050 355.239,67.760 355.807 C 72.410 356.079,75.202 356.542,82.181 358.199 C 88.822 359.777,100.215 360.425,156.000 362.398 C 170.725 362.918,255.192 362.921,267.760 362.401 C 273.062 362.182,281.900 361.820,287.400 361.597 C 302.851 360.972,326.558 359.339,333.200 358.444 C 343.055 357.116,343.889 354.966,341.839 336.200 C 341.502 333.120,340.948 327.900,340.607 324.600 C 340.266 321.300,339.709 315.990,339.370 312.800 C 339.030 309.610,338.499 304.570,338.189 301.600 C 337.022 290.393,335.650 278.160,334.622 269.800 C 333.105 257.460,332.638 254.027,330.408 238.800 C 329.716 234.070,328.983 229.030,328.781 227.600 C 328.579 226.170,328.231 223.920,328.008 222.600 C 327.785 221.280,326.695 214.260,325.587 207.000 C 324.478 199.740,323.124 191.010,322.578 187.600 C 322.032 184.190,321.325 179.780,321.007 177.800 C 320.689 175.820,320.203 172.711,319.925 170.892 C 319.508 168.155,319.315 167.635,318.810 167.885 C 318.475 168.051,314.780 168.279,310.600 168.393 C 301.870 168.631,302.159 168.535,301.810 171.300 C 299.468 189.825,283.036 203.371,264.230 202.279 C 247.552 201.310,233.532 188.465,231.203 172.020 C 230.651 168.125,232.337 168.400,209.000 168.400 C 185.703 168.400,187.432 168.114,186.814 172.064 C 180.639 211.474,123.556 212.329,116.559 173.117 C 115.684 168.218,116.787 168.629,103.881 168.400 C 97.676 168.290,92.285 168.046,91.900 167.857 C 91.307 167.567,91.200 167.712,91.200 168.809 M211.015 197.632 C 214.247 200.942,215.394 201.357,220.447 201.050 C 225.236 200.759,225.846 201.071,224.310 203.023 C 221.899 206.089,216.333 205.843,210.779 202.425 C 208.312 200.907,207.846 200.906,205.600 202.405 C 200.180 206.022,193.561 206.136,191.600 202.647 C 190.824 201.267,191.555 200.665,193.600 201.001 C 199.643 201.993,201.561 201.450,204.900 197.805 C 207.534 194.929,208.352 194.906,211.015 197.632 "
stroke="none" fill="url(#Gradient2)" fill-rule="evenodd"></path>
<path id="path1"
d="M148.000 131.622 C 132.020 133.140,119.114 145.293,116.377 161.400 C 115.675 165.535,116.697 165.239,103.579 165.112 C 97.252 165.050,92.159 165.135,92.261 165.300 C 92.364 165.465,92.279 165.600,92.074 165.600 C 91.391 165.600,91.684 167.617,92.424 168.013 C 92.839 168.235,97.730 168.400,103.897 168.400 C 116.551 168.400,115.666 168.070,116.562 173.117 C 123.522 212.326,180.639 211.470,186.814 172.064 C 187.432 168.114,185.703 168.400,209.000 168.400 C 232.337 168.400,230.651 168.125,231.203 172.020 C 233.532 188.465,247.552 201.310,264.230 202.279 C 283.039 203.371,299.468 189.826,301.811 171.294 C 302.169 168.458,302.345 168.400,310.600 168.400 C 318.988 168.400,319.609 168.159,318.852 165.200 C 318.824 165.090,315.273 165.052,310.961 165.117 C 301.855 165.252,302.274 165.421,301.620 161.367 C 295.239 121.807,237.531 121.949,231.235 161.540 C 231.013 162.937,230.622 164.332,230.367 164.640 C 229.717 165.423,188.328 165.481,187.689 164.700 C 187.463 164.425,186.981 162.602,186.617 160.650 C 183.194 142.288,166.648 129.850,148.000 131.622 M159.886 135.622 C 183.880 141.759,192.035 172.033,174.435 189.636 C 156.404 207.671,125.892 198.962,119.944 174.084 C 114.368 150.761,136.343 129.601,159.886 135.622 M274.658 135.615 C 299.082 141.861,307.061 172.817,288.734 190.222 C 267.975 209.936,234.188 195.420,234.219 166.800 C 234.241 145.502,253.935 130.315,274.658 135.615 M148.542 152.793 C 139.499 154.706,134.598 164.925,138.715 173.287 C 145.082 186.221,164.875 182.425,165.902 168.074 C 166.591 158.446,157.916 150.811,148.542 152.793 M262.091 153.188 C 253.922 156.127,249.883 165.408,253.408 173.139 C 257.835 182.850,271.593 184.491,278.201 176.097 C 287.389 164.426,275.974 148.192,262.091 153.188 M204.900 197.805 C 201.561 201.450,199.643 201.993,193.600 201.001 C 191.555 200.665,190.824 201.267,191.600 202.647 C 193.561 206.136,200.180 206.022,205.600 202.405 C 207.846 200.906,208.312 200.907,210.779 202.425 C 216.333 205.843,221.899 206.089,224.310 203.023 C 225.846 201.071,225.236 200.759,220.447 201.050 C 215.394 201.357,214.247 200.942,211.015 197.632 C 208.352 194.906,207.534 194.929,204.900 197.805 "
stroke="none" fill="#ffffff" fill-rule="evenodd"></path>
</g>
<svg width="157" height="28" viewBox="0 0 157 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="logo" clip-path="url(#clip0_189_16)">
<g id="&#228;&#187;&#163;&#231;&#144;&#134;&#231;&#187;&#132;">
<path id="Vector" d="M155.711 21.92C155.205 22.2266 154.611 22.4533 153.931 22.6C153.265 22.7466 152.618 22.82 151.991 22.82C149.858 22.82 148.238 22.3133 147.131 21.3C146.038 20.2866 145.491 18.8667 145.491 17.04C145.491 15.2267 145.978 13.84 146.951 12.88C147.938 11.9066 149.278 11.42 150.971 11.42C152.651 11.42 153.978 11.9066 154.951 12.88C155.938 13.84 156.431 15.2267 156.431 17.04C156.431 17.24 156.418 17.5266 156.391 17.9H148.611C148.905 19.62 150.031 20.48 151.991 20.48C153.365 20.48 154.605 20.0733 155.711 19.26V21.92ZM153.411 15.66C153.238 14.9933 152.938 14.5 152.511 14.18C152.085 13.86 151.571 13.7 150.971 13.7C150.411 13.7 149.931 13.8666 149.531 14.2C149.145 14.5333 148.871 15.02 148.711 15.66H153.411Z" fill="black" fill-opacity="0.88"/>
<path id="Vector_2" d="M138.386 27.06C137.572 27.06 136.752 27.0067 135.926 26.9C135.112 26.7933 134.379 26.64 133.726 26.44V23.82C135.152 24.14 136.579 24.3 138.006 24.3C138.912 24.3 139.599 24.1 140.066 23.7C140.532 23.3 140.766 22.7467 140.766 22.04C140.032 22.52 139.159 22.76 138.146 22.76C137.159 22.76 136.292 22.52 135.546 22.04C134.799 21.56 134.226 20.9 133.826 20.06C133.426 19.2067 133.226 18.2267 133.226 17.12C133.226 16.0133 133.426 15.0333 133.826 14.18C134.239 13.3133 134.812 12.64 135.546 12.16C136.292 11.68 137.159 11.44 138.146 11.44C139.066 11.44 139.939 11.7467 140.766 12.36V11.7H143.886V22.46C143.886 25.5267 142.052 27.06 138.386 27.06ZM136.286 17.12C136.286 17.7333 136.406 18.2933 136.646 18.8C136.899 19.3067 137.226 19.7133 137.626 20.02C138.039 20.3133 138.472 20.46 138.926 20.46C139.259 20.46 139.632 20.3867 140.046 20.24C140.459 20.08 140.812 19.7533 141.106 19.26L141.206 18.86V15.16C140.926 14.5867 140.566 14.2133 140.126 14.04C139.686 13.8533 139.286 13.76 138.926 13.76C138.472 13.76 138.039 13.9067 137.626 14.2C137.226 14.4933 136.899 14.9 136.646 15.42C136.406 15.9267 136.286 16.4933 136.286 17.12Z" fill="black" fill-opacity="0.88"/>
<path id="Vector_3" d="M129.052 11.7V12.86C129.399 12.4867 129.812 12.2 130.292 12C130.786 11.7867 131.272 11.68 131.752 11.68C132.086 11.68 132.399 11.7267 132.692 11.82V14.28C132.319 14.1867 131.966 14.14 131.632 14.14C131.126 14.14 130.646 14.2267 130.192 14.4C129.739 14.56 129.359 14.82 129.052 15.18V22.5H125.852V11.7H129.052Z" fill="black" fill-opacity="0.88"/>
<path id="Vector_4" d="M123.524 21.92C123.017 22.2266 122.424 22.4533 121.744 22.6C121.077 22.7466 120.43 22.82 119.804 22.82C117.67 22.82 116.05 22.3133 114.944 21.3C113.85 20.2866 113.304 18.8667 113.304 17.04C113.304 15.2267 113.79 13.84 114.764 12.88C115.75 11.9066 117.09 11.42 118.784 11.42C120.464 11.42 121.79 11.9066 122.764 12.88C123.75 13.84 124.244 15.2267 124.244 17.04C124.244 17.24 124.23 17.5266 124.204 17.9H116.424C116.717 19.62 117.844 20.48 119.804 20.48C121.177 20.48 122.417 20.0733 123.524 19.26V21.92ZM121.224 15.66C121.05 14.9933 120.75 14.5 120.324 14.18C119.897 13.86 119.384 13.7 118.784 13.7C118.224 13.7 117.744 13.8666 117.344 14.2C116.957 14.5333 116.684 15.02 116.524 15.66H121.224Z" fill="black" fill-opacity="0.88"/>
<path id="Vector_5" d="M99.3491 8.5H102.929L106.289 20.28H106.369L109.729 8.5H113.309L108.989 22.5H103.669L99.3491 8.5Z" fill="black" fill-opacity="0.88"/>
<path id="Vector_6" d="M86.2201 7.12V12.86C86.7001 12.46 87.2335 12.1533 87.8201 11.94C88.4068 11.7133 89.0001 11.6 89.6001 11.6C90.6268 11.6 91.4401 11.9 92.0401 12.5C92.6535 13.1 92.9601 13.88 92.9601 14.84V22.5H89.7601V15.38C89.7601 14.9933 89.6001 14.68 89.2801 14.44C88.9601 14.1867 88.5801 14.06 88.1401 14.06C87.8868 14.06 87.5735 14.14 87.2001 14.3C86.8268 14.46 86.5001 14.66 86.2201 14.9V22.5H83.0201V7.12H86.2201Z" fill="black" fill-opacity="0.88"/>
<path id="Vector_7" d="M77.7898 22.74C76.9765 22.78 76.1498 22.7133 75.3098 22.54C74.4832 22.38 73.7632 22.1267 73.1498 21.78V18.9C73.6965 19.3 74.3498 19.6333 75.1098 19.9C75.8832 20.1533 76.5632 20.26 77.1498 20.22C77.5632 20.1933 77.8632 20.1067 78.0498 19.96C78.2365 19.8 78.3432 19.64 78.3698 19.48C78.4365 19.0667 78.3365 18.7533 78.0698 18.54C77.8032 18.3267 77.3032 18.12 76.5698 17.92C75.7565 17.6933 75.1098 17.4333 74.6298 17.14C74.1498 16.8333 73.7832 16.4933 73.5298 16.12C73.2898 15.7333 73.1698 15.2467 73.1698 14.66C73.1698 14.0467 73.3298 13.5 73.6498 13.02C73.9698 12.5267 74.4765 12.14 75.1698 11.86C75.8632 11.58 76.6098 11.44 77.4098 11.44C78.0098 11.44 78.6165 11.5 79.2298 11.62C79.8432 11.7267 80.3565 11.8733 80.7698 12.06V14.64C80.3432 14.4267 79.8365 14.24 79.2498 14.08C78.6765 13.9067 78.1765 13.82 77.7498 13.82C76.8432 13.82 76.3498 14.06 76.2698 14.54C76.2298 14.82 76.3565 15.0667 76.6498 15.28C76.9565 15.4933 77.3898 15.6867 77.9498 15.86C78.7498 16.1133 79.4032 16.3733 79.9098 16.64C80.4298 16.9067 80.8365 17.2533 81.1298 17.68C81.4232 18.1067 81.5698 18.6533 81.5698 19.32C81.5698 20.3067 81.2165 21.1133 80.5098 21.74C79.8032 22.3533 78.8965 22.6867 77.7898 22.74Z" fill="black" fill-opacity="0.88"/>
<path id="Vector_8" d="M65.667 22.76C64.6537 22.76 63.767 22.52 63.007 22.04C62.2603 21.56 61.6803 20.9 61.267 20.06C60.867 19.22 60.667 18.24 60.667 17.12C60.667 16.0133 60.867 15.0333 61.267 14.18C61.6803 13.3133 62.2537 12.64 62.987 12.16C63.7337 11.68 64.6003 11.44 65.587 11.44C66.0403 11.44 66.4937 11.5267 66.947 11.7C67.4137 11.86 67.847 12.0867 68.247 12.38V11.7H71.447V19.98C71.447 21.06 71.5803 21.9 71.847 22.5H68.847C68.7537 22.3133 68.6737 22.0733 68.607 21.78C67.7803 22.4333 66.8003 22.76 65.667 22.76ZM63.727 17.12C63.727 17.7333 63.847 18.2933 64.087 18.8C64.3403 19.3067 64.667 19.7133 65.067 20.02C65.4803 20.3133 65.9137 20.46 66.367 20.46C66.687 20.46 67.047 20.3933 67.447 20.26C67.847 20.1133 68.1937 19.8067 68.487 19.34V14.86C68.1937 14.3933 67.847 14.0933 67.447 13.96C67.0603 13.8267 66.7003 13.76 66.367 13.76C65.9137 13.76 65.4803 13.9067 65.067 14.2C64.667 14.4933 64.3403 14.9 64.087 15.42C63.847 15.9267 63.727 16.4933 63.727 17.12Z" fill="black" fill-opacity="0.88"/>
<path id="Vector_9" d="M58.3398 22.66C57.1398 22.66 56.2332 22.3267 55.6198 21.66C55.0065 20.98 54.6998 19.9867 54.6998 18.68V7.12H57.8998V17.88C57.8998 18.4133 57.9398 18.82 58.0198 19.1C58.0998 19.38 58.2332 19.5933 58.4198 19.74C58.5665 19.86 58.7265 19.94 58.8998 19.98C59.0865 20.0067 59.4065 20.02 59.8598 20.02V22.66H58.3398Z" fill="black" fill-opacity="0.88"/>
<path id="Vector_10" d="M48.6679 22.76C47.1479 22.76 45.7946 22.48 44.6079 21.92C43.4346 21.36 42.5079 20.5467 41.8279 19.48C41.1613 18.4133 40.8279 17.1467 40.8279 15.68C40.8279 14.1333 41.1546 12.8067 41.8079 11.7C42.4613 10.58 43.3746 9.72666 44.5479 9.13999C45.7213 8.53999 47.0613 8.23999 48.5679 8.23999C49.3146 8.23999 50.0679 8.31332 50.8279 8.45999C51.5879 8.60666 52.2146 8.79332 52.7079 9.01999V11.5C51.3613 11.0067 50.1079 10.76 48.9479 10.76C47.5613 10.76 46.4346 11.1667 45.5679 11.98C44.7146 12.7933 44.2879 14.0267 44.2879 15.68C44.2879 16.56 44.5013 17.34 44.9279 18.02C45.3546 18.6867 45.9146 19.2067 46.6079 19.58C47.3013 19.9533 48.0479 20.1667 48.8479 20.22L49.2879 20.24C49.9013 20.24 50.5079 20.1533 51.1079 19.98C51.7079 19.8067 52.2413 19.5733 52.7079 19.28V22.04C52.1346 22.3067 51.5213 22.4933 50.8679 22.6C50.2279 22.7067 49.4946 22.76 48.6679 22.76Z" fill="black" fill-opacity="0.88"/>
</g>
<g id="svgg">
<path id="path0" fill-rule="evenodd" clip-rule="evenodd" d="M9.45822 0.677484C9.3398 0.703426 9.17301 0.923458 9.0706 1.18905C8.96051 1.4745 8.87876 1.85323 8.58394 3.44325C8.54639 3.64583 8.48153 3.98453 8.43973 4.19591C8.36951 4.55118 8.29961 4.92366 8.18135 5.57311C8.15565 5.71403 8.12018 5.9014 8.10248 5.98947C8.06741 6.16362 8.06981 6.14953 7.88181 7.27059C7.7408 8.11148 7.68251 8.47284 7.60541 8.98408C7.57754 9.16904 7.53446 9.45009 7.50972 9.60863C7.41844 10.1929 7.36336 10.6506 7.38089 10.679C7.39082 10.695 7.38433 10.7129 7.36648 10.7189C7.34862 10.7248 7.75498 10.7254 8.26943 10.7202C9.32979 10.7095 9.26581 10.7279 9.31682 10.4189C9.83575 7.27691 14.3593 7.22095 14.9424 10.3493C14.9716 10.5056 15.0102 10.6515 15.0283 10.6736C15.0794 10.7361 18.3935 10.7314 18.4455 10.6687C18.4659 10.6441 18.4972 10.5324 18.515 10.4205C19.0191 7.25049 23.6398 7.23912 24.1507 10.4067C24.2031 10.7313 24.1695 10.7178 24.8987 10.7069C25.2439 10.7017 25.5312 10.7048 25.5371 10.7136C25.543 10.7224 25.5427 10.7097 25.5365 10.6854C25.5302 10.6611 25.5038 10.5062 25.4777 10.3411C25.4516 10.1761 25.4086 9.90769 25.3822 9.74475C25.3151 9.3311 25.247 8.90906 25.2079 8.6638C25.1896 8.5493 25.1601 8.37635 25.1423 8.27947C25.1244 8.18258 25.0157 7.52681 24.9006 6.8222C24.3635 3.53493 24.3419 3.42372 23.8725 1.52158C23.6976 0.813202 23.5903 0.655544 23.2844 0.657706C23.0543 0.659308 22.6641 0.96069 22.3732 1.36144C22.2201 1.57218 20.9436 3.8203 20.5748 4.52844C20.3371 4.98492 20.3095 4.99573 19.6652 4.88467C17.5159 4.51411 15.5699 4.51459 13.6502 4.88611C13.0409 5.00405 13.1305 5.06034 12.6174 4.23642C10.7197 1.18905 10.1342 0.529434 9.45822 0.677484ZM11.7925 8.28003C9.87787 8.54514 8.89445 10.7711 10.0046 12.3269C11.2533 14.0767 13.9085 13.6821 14.6101 11.6424C15.2133 9.88887 13.6496 8.02292 11.7925 8.28003ZM20.9824 8.28027C18.8789 8.57092 17.9941 11.1556 19.4855 12.653C21.1704 14.3446 24.0109 13.1217 23.916 10.7456C23.855 9.21813 22.491 8.07185 20.9824 8.28027ZM12.6191 9.804C13.0588 10.0043 13.3191 10.4505 13.2838 10.9437C13.2016 12.0928 11.6167 12.3967 11.1069 11.3611C10.63 10.3924 11.6361 9.35641 12.6191 9.804ZM21.8746 9.78454C22.4026 10.0511 22.6828 10.7103 22.4872 11.226C22.0376 12.4115 20.3053 12.2117 20.1885 10.9608C20.1076 10.0944 21.1072 9.39708 21.8746 9.78454ZM7.30242 11.0026C7.30242 11.0597 7.28152 11.2449 7.25606 11.4144C7.20097 11.7804 7.01153 13.1933 6.86908 14.3007C6.84298 14.5033 6.80695 14.7699 6.78885 14.8932C6.77084 15.0165 6.74121 15.2471 6.72304 15.4057C6.70486 15.5642 6.66891 15.8669 6.64305 16.0783C6.55665 16.785 6.41941 17.957 6.35792 18.5124C6.31492 18.9007 6.2881 19.1354 6.16439 20.2099C6.10546 20.7213 6.03772 21.3373 5.9707 21.9714C5.94284 22.2356 5.90833 22.5599 5.89423 22.692C5.88006 22.8241 5.8502 23.1268 5.82786 23.3646C5.80544 23.6024 5.77861 23.8068 5.76813 23.8189C5.74114 23.85 4.77526 23.6715 4.32607 23.5525C2.77592 23.1414 2.08956 21.6632 2.85254 20.3789C3.04479 20.0552 3.32416 19.7925 3.98713 19.3116C4.65059 18.8303 4.74515 18.7243 4.76501 18.4405C4.80176 17.9147 4.05775 17.6693 3.24697 17.9396C-0.49053 19.1861 -0.311734 24.4878 3.5076 25.6667C4.04687 25.8332 4.64811 25.93 5.42559 25.9754C5.79791 25.9972 6.02147 26.0343 6.58027 26.167C7.11202 26.2933 8.02425 26.3452 12.4909 26.5032C13.67 26.5448 20.4332 26.5451 21.4395 26.5034C21.8641 26.4859 22.5717 26.4569 23.0121 26.4391C24.2493 26.389 26.1475 26.2583 26.6793 26.1866C27.4684 26.0803 27.5352 25.9081 27.371 24.4055C27.3441 24.1589 27.2997 23.7409 27.2724 23.4767C27.2451 23.2125 27.2005 22.7873 27.1733 22.5319C27.1461 22.2765 27.1036 21.8729 27.0788 21.6351C26.9853 20.7378 26.8755 19.7583 26.7932 19.0889C26.6717 18.1008 26.6343 17.8259 26.4558 16.6067C26.4003 16.228 26.3417 15.8244 26.3255 15.7099C26.3093 15.5954 26.2814 15.4153 26.2636 15.3096C26.2457 15.2039 26.1585 14.6418 26.0697 14.0605C25.9809 13.4792 25.8725 12.7802 25.8288 12.5072C25.7851 12.2341 25.7285 11.881 25.703 11.7225C25.6776 11.5639 25.6386 11.315 25.6164 11.1693C25.583 10.9502 25.5675 10.9086 25.5271 10.9286C25.5003 10.9419 25.2044 10.9601 24.8697 10.9693C24.1707 10.9883 24.1939 10.9806 24.1659 11.202C23.9784 12.6853 22.6627 13.7699 21.1569 13.6825C19.8215 13.6049 18.6989 12.5764 18.5124 11.2597C18.4682 10.9478 18.6032 10.9698 16.7346 10.9698C14.8693 10.9698 15.0077 10.9469 14.9582 11.2632C14.4638 14.4187 9.89316 14.4872 9.33291 11.3475C9.26285 10.9552 9.35117 10.9881 8.31779 10.9698C7.82095 10.961 7.3893 10.9415 7.35847 10.9263C7.31099 10.9031 7.30242 10.9147 7.30242 11.0026ZM16.896 13.3104C17.1548 13.5754 17.2466 13.6087 17.6512 13.5841C18.0347 13.5608 18.0835 13.5858 17.9605 13.7421C17.7675 13.9876 17.3218 13.9679 16.8771 13.6942C16.6796 13.5726 16.6422 13.5726 16.4624 13.6926C16.0284 13.9822 15.4984 13.9913 15.3414 13.712C15.2793 13.6015 15.3378 13.5533 15.5016 13.5802C15.9854 13.6596 16.139 13.6161 16.4064 13.3243C16.6173 13.094 16.6828 13.0921 16.896 13.3104Z" fill="#40416F"/>
<path id="path1" fill-rule="evenodd" clip-rule="evenodd" d="M11.8504 8.025C10.5709 8.14655 9.53748 9.11964 9.31833 10.4093C9.26212 10.7404 9.34395 10.7167 8.29359 10.7065C7.78699 10.7016 7.3792 10.7084 7.38736 10.7216C7.39561 10.7348 7.38881 10.7456 7.37239 10.7456C7.3177 10.7456 7.34116 10.9071 7.40042 10.9388C7.43364 10.9566 7.82527 10.9698 8.31906 10.9698C9.33226 10.9698 9.2614 10.9434 9.33314 11.3475C9.89043 14.487 14.4638 14.4184 14.9582 11.2632C15.0077 10.9469 14.8692 10.9698 16.7346 10.9698C18.6032 10.9698 18.4682 10.9478 18.5124 11.2597C18.6989 12.5764 19.8215 13.6049 21.1569 13.6825C22.6629 13.7699 23.9784 12.6854 24.166 11.2015C24.1947 10.9745 24.2087 10.9698 24.8697 10.9698C25.5413 10.9698 25.5911 10.9505 25.5305 10.7136C25.5282 10.7048 25.2439 10.7017 24.8986 10.7069C24.1695 10.7177 24.2031 10.7313 24.1507 10.4067C23.6398 7.23912 19.0191 7.25049 18.515 10.4205C18.4972 10.5324 18.4659 10.6441 18.4455 10.6687C18.3934 10.7314 15.0794 10.7361 15.0283 10.6736C15.0102 10.6515 14.9716 10.5056 14.9424 10.3493C14.6683 8.87903 13.3435 7.88312 11.8504 8.025ZM12.8021 8.34528C14.7233 8.83667 15.3762 11.2607 13.967 12.6702C12.5233 14.1142 10.0802 13.4169 9.60394 11.4249C9.15747 9.55746 10.917 7.86318 12.8021 8.34528ZM21.9918 8.34472C23.9475 8.84484 24.5864 11.3235 23.1189 12.7171C21.4567 14.2956 18.7514 13.1333 18.7539 10.8417C18.7557 9.13637 20.3326 7.92035 21.9918 8.34472ZM11.8938 9.72016C11.1697 9.87333 10.7773 10.6916 11.1069 11.3611C11.6167 12.3967 13.2016 12.0928 13.2838 10.9437C13.339 10.1728 12.6443 9.56146 11.8938 9.72016ZM20.9856 9.75179C20.3315 9.98711 20.0081 10.7302 20.2904 11.3493C20.6448 12.1268 21.7464 12.2582 22.2755 11.5861C23.0112 10.6516 22.0972 9.35176 20.9856 9.75179ZM16.4063 13.3243C16.139 13.6161 15.9854 13.6596 15.5016 13.5802C15.3378 13.5533 15.2793 13.6015 15.3414 13.712C15.4984 13.9913 16.0284 13.9822 16.4624 13.6926C16.6422 13.5726 16.6795 13.5726 16.8771 13.6942C17.3218 13.9679 17.7674 13.9876 17.9605 13.7421C18.0835 13.5858 18.0346 13.5608 17.6512 13.5841C17.2466 13.6087 17.1548 13.5754 16.896 13.3104C16.6827 13.0921 16.6172 13.094 16.4063 13.3243Z" fill="white"/>
</g>
</g>
<defs>
<clipPath id="clip0_189_16">
<rect width="157" height="27" fill="white" transform="translate(0 0.5)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 14 KiB

+20
View File
@@ -0,0 +1,20 @@
<svg width="157" height="27" viewBox="0 0 157 27" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_745_54)">
<path d="M155.711 21.42C155.205 21.7266 154.611 21.9533 153.931 22.1C153.265 22.2466 152.618 22.32 151.991 22.32C149.858 22.32 148.238 21.8133 147.131 20.8C146.038 19.7866 145.491 18.3667 145.491 16.54C145.491 14.7267 145.978 13.34 146.951 12.38C147.938 11.4066 149.278 10.92 150.971 10.92C152.651 10.92 153.978 11.4066 154.951 12.38C155.938 13.34 156.431 14.7267 156.431 16.54C156.431 16.74 156.418 17.0266 156.391 17.4H148.611C148.905 19.12 150.031 19.98 151.991 19.98C153.365 19.98 154.605 19.5733 155.711 18.76V21.42ZM153.411 15.16C153.238 14.4933 152.938 14 152.511 13.68C152.085 13.36 151.571 13.2 150.971 13.2C150.411 13.2 149.931 13.3666 149.531 13.7C149.145 14.0333 148.871 14.52 148.711 15.16H153.411Z" fill="white"/>
<path d="M138.386 26.56C137.572 26.56 136.752 26.5067 135.926 26.4C135.112 26.2933 134.379 26.14 133.726 25.94V23.32C135.152 23.64 136.579 23.8 138.006 23.8C138.912 23.8 139.599 23.6 140.066 23.2C140.532 22.8 140.766 22.2467 140.766 21.54C140.032 22.02 139.159 22.26 138.146 22.26C137.159 22.26 136.292 22.02 135.546 21.54C134.799 21.06 134.226 20.4 133.826 19.56C133.426 18.7067 133.226 17.7267 133.226 16.62C133.226 15.5133 133.426 14.5333 133.826 13.68C134.239 12.8133 134.812 12.14 135.546 11.66C136.292 11.18 137.159 10.94 138.146 10.94C139.066 10.94 139.939 11.2467 140.766 11.86V11.2H143.886V21.96C143.886 25.0267 142.052 26.56 138.386 26.56ZM136.286 16.62C136.286 17.2333 136.406 17.7933 136.646 18.3C136.899 18.8067 137.226 19.2133 137.626 19.52C138.039 19.8133 138.472 19.96 138.926 19.96C139.259 19.96 139.632 19.8867 140.046 19.74C140.459 19.58 140.812 19.2533 141.106 18.76L141.206 18.36V14.66C140.926 14.0867 140.566 13.7133 140.126 13.54C139.686 13.3533 139.286 13.26 138.926 13.26C138.472 13.26 138.039 13.4067 137.626 13.7C137.226 13.9933 136.899 14.4 136.646 14.92C136.406 15.4267 136.286 15.9933 136.286 16.62Z" fill="white"/>
<path d="M129.052 11.2V12.36C129.399 11.9867 129.812 11.7 130.292 11.5C130.786 11.2867 131.272 11.18 131.752 11.18C132.086 11.18 132.399 11.2267 132.692 11.32V13.78C132.319 13.6867 131.966 13.64 131.632 13.64C131.126 13.64 130.646 13.7267 130.192 13.9C129.739 14.06 129.359 14.32 129.052 14.68V22H125.852V11.2H129.052Z" fill="white"/>
<path d="M123.524 21.42C123.017 21.7266 122.424 21.9533 121.744 22.1C121.077 22.2466 120.43 22.32 119.804 22.32C117.67 22.32 116.05 21.8133 114.944 20.8C113.85 19.7866 113.304 18.3667 113.304 16.54C113.304 14.7267 113.79 13.34 114.764 12.38C115.75 11.4066 117.09 10.92 118.784 10.92C120.464 10.92 121.79 11.4066 122.764 12.38C123.75 13.34 124.244 14.7267 124.244 16.54C124.244 16.74 124.23 17.0266 124.204 17.4H116.424C116.717 19.12 117.844 19.98 119.804 19.98C121.177 19.98 122.417 19.5733 123.524 18.76V21.42ZM121.224 15.16C121.05 14.4933 120.75 14 120.324 13.68C119.897 13.36 119.384 13.2 118.784 13.2C118.224 13.2 117.744 13.3666 117.344 13.7C116.957 14.0333 116.684 14.52 116.524 15.16H121.224Z" fill="white"/>
<path d="M99.3491 8H102.929L106.289 19.78H106.369L109.729 8H113.309L108.989 22H103.669L99.3491 8Z" fill="white"/>
<path d="M86.2201 6.62V12.36C86.7001 11.96 87.2335 11.6533 87.8201 11.44C88.4068 11.2133 89.0001 11.1 89.6001 11.1C90.6268 11.1 91.4401 11.4 92.0401 12C92.6535 12.6 92.9601 13.38 92.9601 14.34V22H89.7601V14.88C89.7601 14.4933 89.6001 14.18 89.2801 13.94C88.9601 13.6867 88.5801 13.56 88.1401 13.56C87.8868 13.56 87.5735 13.64 87.2001 13.8C86.8268 13.96 86.5001 14.16 86.2201 14.4V22H83.0201V6.62H86.2201Z" fill="white"/>
<path d="M77.7898 22.24C76.9765 22.28 76.1498 22.2133 75.3098 22.04C74.4832 21.88 73.7632 21.6267 73.1498 21.28V18.4C73.6965 18.8 74.3498 19.1333 75.1098 19.4C75.8832 19.6533 76.5632 19.76 77.1498 19.72C77.5632 19.6933 77.8632 19.6067 78.0498 19.46C78.2365 19.3 78.3432 19.14 78.3698 18.98C78.4365 18.5667 78.3365 18.2533 78.0698 18.04C77.8032 17.8267 77.3032 17.62 76.5698 17.42C75.7565 17.1933 75.1098 16.9333 74.6298 16.64C74.1498 16.3333 73.7832 15.9933 73.5298 15.62C73.2898 15.2333 73.1698 14.7467 73.1698 14.16C73.1698 13.5467 73.3298 13 73.6498 12.52C73.9698 12.0267 74.4765 11.64 75.1698 11.36C75.8632 11.08 76.6098 10.94 77.4098 10.94C78.0098 10.94 78.6165 11 79.2298 11.12C79.8432 11.2267 80.3565 11.3733 80.7698 11.56V14.14C80.3432 13.9267 79.8365 13.74 79.2498 13.58C78.6765 13.4067 78.1765 13.32 77.7498 13.32C76.8432 13.32 76.3498 13.56 76.2698 14.04C76.2298 14.32 76.3565 14.5667 76.6498 14.78C76.9565 14.9933 77.3898 15.1867 77.9498 15.36C78.7498 15.6133 79.4032 15.8733 79.9098 16.14C80.4298 16.4067 80.8365 16.7533 81.1298 17.18C81.4232 17.6067 81.5698 18.1533 81.5698 18.82C81.5698 19.8067 81.2165 20.6133 80.5098 21.24C79.8032 21.8533 78.8965 22.1867 77.7898 22.24Z" fill="white"/>
<path d="M65.667 22.26C64.6537 22.26 63.767 22.02 63.007 21.54C62.2603 21.06 61.6803 20.4 61.267 19.56C60.867 18.72 60.667 17.74 60.667 16.62C60.667 15.5133 60.867 14.5333 61.267 13.68C61.6803 12.8133 62.2537 12.14 62.987 11.66C63.7337 11.18 64.6003 10.94 65.587 10.94C66.0403 10.94 66.4937 11.0267 66.947 11.2C67.4137 11.36 67.847 11.5867 68.247 11.88V11.2H71.447V19.48C71.447 20.56 71.5803 21.4 71.847 22H68.847C68.7537 21.8133 68.6737 21.5733 68.607 21.28C67.7803 21.9333 66.8003 22.26 65.667 22.26ZM63.727 16.62C63.727 17.2333 63.847 17.7933 64.087 18.3C64.3403 18.8067 64.667 19.2133 65.067 19.52C65.4803 19.8133 65.9137 19.96 66.367 19.96C66.687 19.96 67.047 19.8933 67.447 19.76C67.847 19.6133 68.1937 19.3067 68.487 18.84V14.36C68.1937 13.8933 67.847 13.5933 67.447 13.46C67.0603 13.3267 66.7003 13.26 66.367 13.26C65.9137 13.26 65.4803 13.4067 65.067 13.7C64.667 13.9933 64.3403 14.4 64.087 14.92C63.847 15.4267 63.727 15.9933 63.727 16.62Z" fill="white"/>
<path d="M58.3398 22.16C57.1398 22.16 56.2332 21.8267 55.6198 21.16C55.0065 20.48 54.6998 19.4867 54.6998 18.18V6.62H57.8998V17.38C57.8998 17.9133 57.9398 18.32 58.0198 18.6C58.0998 18.88 58.2332 19.0933 58.4198 19.24C58.5665 19.36 58.7265 19.44 58.8998 19.48C59.0865 19.5067 59.4065 19.52 59.8598 19.52V22.16H58.3398Z" fill="white"/>
<path d="M48.6679 22.26C47.1479 22.26 45.7946 21.98 44.6079 21.42C43.4346 20.86 42.5079 20.0467 41.8279 18.98C41.1613 17.9133 40.8279 16.6467 40.8279 15.18C40.8279 13.6333 41.1546 12.3067 41.8079 11.2C42.4613 10.08 43.3746 9.22666 44.5479 8.63999C45.7213 8.03999 47.0613 7.73999 48.5679 7.73999C49.3146 7.73999 50.0679 7.81332 50.8279 7.95999C51.5879 8.10666 52.2146 8.29332 52.7079 8.51999V11C51.3613 10.5067 50.1079 10.26 48.9479 10.26C47.5613 10.26 46.4346 10.6667 45.5679 11.48C44.7146 12.2933 44.2879 13.5267 44.2879 15.18C44.2879 16.06 44.5013 16.84 44.9279 17.52C45.3546 18.1867 45.9146 18.7067 46.6079 19.08C47.3013 19.4533 48.0479 19.6667 48.8479 19.72L49.2879 19.74C49.9013 19.74 50.5079 19.6533 51.1079 19.48C51.7079 19.3067 52.2413 19.0733 52.7079 18.78V21.54C52.1346 21.8067 51.5213 21.9933 50.8679 22.1C50.2279 22.2067 49.4946 22.26 48.6679 22.26Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.45822 0.177484C9.3398 0.203426 9.17301 0.423458 9.0706 0.689049C8.96051 0.974498 8.87876 1.35323 8.58394 2.94325C8.54639 3.14583 8.48153 3.48453 8.43973 3.69591C8.36951 4.05118 8.29961 4.42366 8.18135 5.07311C8.15565 5.21403 8.12018 5.4014 8.10248 5.48947C8.06741 5.66362 8.06981 5.64953 7.88181 6.77059C7.7408 7.61148 7.68251 7.97284 7.60541 8.48408C7.57754 8.66904 7.53446 8.95009 7.50972 9.10863C7.41844 9.69289 7.36336 10.1506 7.38089 10.179C7.39082 10.195 7.38433 10.2129 7.36648 10.2189C7.34862 10.2248 7.75498 10.2254 8.26943 10.2202C9.32979 10.2095 9.26581 10.2279 9.31682 9.91885C9.83575 6.77691 14.3593 6.72095 14.9424 9.84927C14.9716 10.0056 15.0102 10.1515 15.0283 10.1736C15.0794 10.2361 18.3935 10.2314 18.4455 10.1687C18.4659 10.1441 18.4972 10.0324 18.515 9.92053C19.0191 6.75049 23.6398 6.73912 24.1507 9.90668C24.2031 10.2313 24.1695 10.2178 24.8987 10.2069C25.2439 10.2017 25.5312 10.2048 25.5371 10.2136C25.543 10.2224 25.5427 10.2097 25.5365 10.1854C25.5302 10.1611 25.5038 10.0062 25.4777 9.8411C25.4516 9.67608 25.4086 9.40769 25.3822 9.24475C25.3151 8.8311 25.247 8.40906 25.2079 8.1638C25.1896 8.0493 25.1601 7.87635 25.1423 7.77947C25.1244 7.68258 25.0157 7.02681 24.9006 6.3222C24.3635 3.03493 24.3419 2.92372 23.8725 1.02158C23.6976 0.313202 23.5903 0.155544 23.2844 0.157706C23.0543 0.159308 22.6641 0.46069 22.3732 0.86144C22.2201 1.07218 20.9436 3.3203 20.5748 4.02844C20.3371 4.48492 20.3095 4.49573 19.6652 4.38467C17.5159 4.01411 15.5699 4.01459 13.6502 4.38611C13.0409 4.50405 13.1305 4.56034 12.6174 3.73642C10.7197 0.689049 10.1342 0.0294346 9.45822 0.177484ZM11.7925 7.78003C9.87787 8.04514 8.89445 10.2711 10.0046 11.8269C11.2533 13.5767 13.9085 13.1821 14.6101 11.1424C15.2133 9.38887 13.6496 7.52292 11.7925 7.78003ZM20.9824 7.78027C18.8789 8.07092 17.9941 10.6556 19.4855 12.153C21.1704 13.8446 24.0109 12.6217 23.916 10.2456C23.855 8.71813 22.491 7.57185 20.9824 7.78027ZM12.6191 9.304C13.0588 9.50425 13.3191 9.95048 13.2838 10.4437C13.2016 11.5928 11.6167 11.8967 11.1069 10.8611C10.63 9.89243 11.6361 8.85641 12.6191 9.304ZM21.8746 9.28454C22.4026 9.55109 22.6828 10.2103 22.4872 10.726C22.0376 11.9115 20.3053 11.7117 20.1885 10.4608C20.1076 9.59441 21.1072 8.89708 21.8746 9.28454ZM7.30242 10.5026C7.30242 10.5597 7.28152 10.7449 7.25606 10.9144C7.20097 11.2804 7.01153 12.6933 6.86908 13.8007C6.84298 14.0033 6.80695 14.2699 6.78885 14.3932C6.77084 14.5165 6.74121 14.7471 6.72304 14.9057C6.70486 15.0642 6.66891 15.3669 6.64305 15.5783C6.55665 16.285 6.41941 17.457 6.35792 18.0124C6.31492 18.4007 6.2881 18.6354 6.16439 19.7099C6.10546 20.2213 6.03772 20.8373 5.9707 21.4714C5.94284 21.7356 5.90833 22.0599 5.89423 22.192C5.88006 22.3241 5.8502 22.6268 5.82786 22.8646C5.80544 23.1024 5.77861 23.3068 5.76813 23.3189C5.74114 23.35 4.77526 23.1715 4.32607 23.0525C2.77592 22.6414 2.08956 21.1632 2.85254 19.8789C3.04479 19.5552 3.32416 19.2925 3.98713 18.8116C4.65059 18.3303 4.74515 18.2243 4.76501 17.9405C4.80176 17.4147 4.05775 17.1693 3.24697 17.4396C-0.490531 18.6861 -0.311735 23.9878 3.5076 25.1667C4.04687 25.3332 4.64811 25.43 5.42559 25.4754C5.79791 25.4972 6.02147 25.5343 6.58027 25.667C7.11202 25.7933 8.02425 25.8452 12.4909 26.0032C13.67 26.0448 20.4332 26.0451 21.4395 26.0034C21.8641 25.9859 22.5717 25.9569 23.0121 25.9391C24.2493 25.889 26.1475 25.7583 26.6793 25.6866C27.4684 25.5803 27.5352 25.4081 27.371 23.9055C27.3441 23.6589 27.2997 23.2409 27.2724 22.9767C27.2451 22.7125 27.2005 22.2873 27.1733 22.0319C27.1461 21.7765 27.1036 21.3729 27.0788 21.1351C26.9853 20.2378 26.8755 19.2583 26.7932 18.5889C26.6717 17.6008 26.6343 17.3259 26.4558 16.1067C26.4003 15.728 26.3417 15.3244 26.3255 15.2099C26.3093 15.0954 26.2814 14.9153 26.2636 14.8096C26.2457 14.7039 26.1585 14.1418 26.0697 13.5605C25.9809 12.9792 25.8725 12.2802 25.8288 12.0072C25.7851 11.7341 25.7285 11.381 25.703 11.2225C25.6776 11.0639 25.6386 10.815 25.6164 10.6693C25.583 10.4502 25.5675 10.4086 25.5271 10.4286C25.5003 10.4419 25.2044 10.4601 24.8697 10.4693C24.1707 10.4883 24.1939 10.4806 24.1659 10.702C23.9784 12.1853 22.6627 13.2699 21.1569 13.1825C19.8215 13.1049 18.6989 12.0764 18.5124 10.7597C18.4682 10.4478 18.6032 10.4698 16.7346 10.4698C14.8693 10.4698 15.0077 10.4469 14.9582 10.7632C14.4638 13.9187 9.89316 13.9872 9.33291 10.8475C9.26285 10.4552 9.35117 10.4881 8.31779 10.4698C7.82095 10.461 7.3893 10.4415 7.35847 10.4263C7.31099 10.4031 7.30242 10.4147 7.30242 10.5026ZM16.896 12.8104C17.1548 13.0754 17.2466 13.1087 17.6512 13.0841C18.0347 13.0608 18.0835 13.0858 17.9605 13.2421C17.7675 13.4876 17.3218 13.4679 16.8771 13.1942C16.6796 13.0726 16.6422 13.0726 16.4624 13.1926C16.0284 13.4822 15.4984 13.4913 15.3414 13.212C15.2793 13.1015 15.3378 13.0533 15.5016 13.0802C15.9854 13.1596 16.139 13.1161 16.4064 12.8243C16.6173 12.594 16.6828 12.5921 16.896 12.8104Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_745_54">
<rect width="157" height="27" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

+4
View File
@@ -0,0 +1,4 @@
@font-face {
font-family: "twemoji mozilla";
src: url("../fonts/Twemoji.Mozilla.ttf");
}
+16 -4
View File
@@ -13,9 +13,12 @@ body {
:root {
--primary-main: #5b5c9d;
--text-primary: #637381;
--text-primary: #1f1f1f;
--selection-color: #f5f5f5;
--scroller-color: #90939980;
--scroller-color: #8c8c8c;
--background-color: #f5f5f5;
--background-color-alpha: rgba(24, 103, 192, 0.1);
--border-radius: 8px;
}
::selection {
@@ -24,8 +27,8 @@ body {
}
*::-webkit-scrollbar {
width: 6px;
height: 6px;
width: 8px;
height: 8px;
background: transparent;
}
*::-webkit-scrollbar-thumb {
@@ -33,8 +36,13 @@ body {
background-color: var(--scroller-color);
}
body {
overflow: hidden;
}
@import "./layout.scss";
@import "./page.scss";
@import "./font.scss";
@media (prefers-color-scheme: dark) {
:root {
@@ -48,3 +56,7 @@ body {
-moz-user-select: none;
-ms-user-select: none;
}
[data-windrag] {
app-region: drag;
}
+57 -23
View File
@@ -5,31 +5,44 @@
overflow: hidden;
&__left {
flex: 1 0 25%;
flex: 1 0 228px;
display: flex;
height: 100%;
max-width: 225px;
min-width: 125px;
width: 100%;
// max-width: 225px;
// min-width: 225px;
padding: 16px 0 8px;
// position: relative;
flex-direction: column;
align-self: stretch;
box-sizing: border-box;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
overflow: hidden;
border-right: 1px solid var(--divider-color);
// background-color: var(--background-color-alpha);
$maxLogo: 100px;
// $maxLogo: 100px;
.the-logo {
position: relative;
flex: 0 1 $maxLogo;
width: 100%;
max-width: $maxLogo + 32px;
max-height: $maxLogo;
margin: 0 auto;
padding: 0 16px;
text-align: center;
flex: 1 0 58px;
// width: 100%;
display: flex;
height: 100%;
padding: 0px 20px;
flex-direction: column;
justify-content: center;
align-items: flex-start;
align-self: stretch;
border-bottom: 1px solid var(--divider-color);
// max-width: $maxLogo + 32px;
// max-height: $maxLogo;
// margin: 0 auto;
// padding: 0 auto;
// text-align: center;
box-sizing: border-box;
img,
@@ -37,12 +50,19 @@
width: 100%;
height: 100%;
pointer-events: none;
// fill: var(--primary-main);
// #bg {
// fill: var(--background-color);
// }
}
.the-newbtn {
position: absolute;
right: 10px;
bottom: 0px;
top: 0px;
border-radius: 8px;
padding: 2px 4px;
transform: scale(0.8);
}
}
@@ -50,7 +70,7 @@
.the-menu {
flex: 1 1 80%;
overflow-y: auto;
margin-bottom: 8px;
margin-bottom: 0px;
}
.the-traffic {
@@ -58,20 +78,22 @@
> div {
margin: 0 auto;
// padding: 0 20px;
}
}
}
&__right {
position: relative;
flex: 1 1 75%;
flex: 1 1 100%;
height: 100%;
// background-color: var(--background-color-alpha);
.the-bar {
position: absolute;
top: 2px;
right: 8px;
height: 36px;
top: 0px;
right: 0px;
height: 24px;
display: flex;
align-items: center;
box-sizing: border-box;
@@ -83,7 +105,7 @@
top: 0;
left: 0;
right: 2px;
bottom: 10px;
bottom: 0px;
}
}
}
@@ -92,15 +114,27 @@
.windows,
.unknown {
&.layout {
$maxLogo: 115px;
.layout__left {
padding-top: 24px;
}
.layout__left .the-logo {
flex: 0 1 $maxLogo;
max-width: $maxLogo + 32px;
max-height: $maxLogo;
flex: 1 0 58px;
}
.layout__right .the-content {
top: 30px;
top: 24px;
}
}
}
.macos {
&.layout {
.layout__left {
padding-top: 24px;
}
.layout__right .the-content {
top: 24px;
}
}
}

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