Compare commits

...
350 Commits
Author SHA1 Message Date
MystiPanda 9c22f7ce69 fix: depend error 2024-05-08 13:24:34 +08:00
MystiPanda 05fa916915 Release 1.6.2 2024-05-08 13:14:00 +08:00
MystiPanda 9a593d7a61 feat: support upgrade for release core 2024-05-08 12:48:27 +08:00
MystiPanda 30017eeaa0 docs: Update FAQ URL 2024-05-08 11:44:57 +08:00
MystiPanda 9b03cacc94 ci: update ci 2024-05-08 11:12:06 +08:00
MystiPanda 557970cf54 refactor: remove prepend and append for provider 2024-05-08 00:22:14 +08:00
MystiPanda d90dac60ef feat: deep merge
#983
2024-05-08 00:13:32 +08:00
MystiPanda b4b2e67260 build: Add portable for fixed webview2 2024-05-06 19:13:08 +08:00
MystiPanda bcd5e935e7 ci: debug ci 2024-05-06 16:16:47 +08:00
MystiPanda b322627609 ci: fix ci script 2024-05-06 15:30:00 +08:00
MystiPanda 09861095ff ci: update release body 2024-05-06 15:15:07 +08:00
MystiPanda 47e567fb40 docs: update url 2024-05-06 15:05:56 +08:00
MystiPanda 5bb30ad28f build: try support fixed webview2 runtime 2024-05-06 14:18:12 +08:00
MystiPanda a46bbf05ec ci: try resupport x86 2024-05-06 13:04:29 +08:00
dongchengjie 04a4705dbd fix: non-ascii character secret causes controller link error
https://github.com/clash-verge-rev/clash-verge-rev/issues/973#issuecomment-2094839700
2024-05-05 23:04:33 +08:00
MystiPanda 3271c96531 fix: enhance when change tun config 2024-05-04 16:12:10 +08:00
dongchengjie 05fa6b9aba chore: profile template typo 2024-05-04 14:24:11 +08:00
MystiPanda 7edd7f27cb fix: try to set env 2024-05-03 18:00:55 +08:00
dongchengjie 4a6b12eda1 fix: #974 2024-05-03 12:50:04 +08:00
MystiPanda a354e1a0eb chore: update locale 2024-05-02 23:44:09 +08:00
dongchengjie c2c419522a chore: add service mode locale 2024-05-02 22:16:05 +08:00
MystiPanda 7a3cc7d242 feat: Support drag and drop local files 2024-05-02 20:41:43 +08:00
MystiPanda bd6f02f6af fix: change script execution path 2024-05-01 11:39:09 +08:00
MystiPanda bd33728fd7 Release 1.6.1 2024-04-30 23:43:51 +08:00
MystiPanda 8400ee8a3d fix: MacOS tray click 2024-04-30 23:21:25 +08:00
lxk955 80ee7aef8e feat: Support for using regular expressions on the log page #707 (#959) 2024-04-30 22:59:42 +08:00
MystiPanda 41762be3a5 fix: service install path 2024-04-30 20:20:39 +08:00
MystiPanda 605dda7b76 build: Restore core version 2024-04-30 09:42:25 +08:00
dongchengjie 8d6e3b5a58 fixup: wrong version 2024-04-30 02:13:24 +08:00
dongchengjie 53920fc368 chore: update pnpm-lock.yaml (#952)
* chore: update schema version

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

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

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

* chore: tauri.conf.json json schema

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

* feat: fixed proxy indicator

* fix: try to fix traffic websocket no longer updating

* fixup: group delay test use defined url

* feat: connections sorted by start by default

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

* fix: editor no hints and add yaml support

* feat: quick suggestions

* chore: use monaco-editor

* chore: update schema url

* chore: change default merge config content

* fix: load schema via npm

* feat: runtime config viewer style auto adjust

* feat: adjust fixed proxy style

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

* chore: switch version

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

* feat: fixed proxy indicator

* fix: try to fix traffic websocket no longer updating

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

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

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

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

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

* chore: cleanup

* chore: adjust content style
2024-03-29 08:20:31 +08:00
wonfen 2cf7a048cf Release 1.5.11 2024-03-28 12:06:41 +08:00
MystiPanda 3dbb71a076 feat: Support Persian
#715
2024-03-21 20:51:33 +08:00
MystiPanda ddf6760543 fix: Duplicate icon display error
#719
2024-03-21 20:29:16 +08:00
MystiPanda a6fa323f33 fix: The update button cannot be clicked
#716
2024-03-21 19:56:30 +08:00
MystiPanda 2df8f1acdb fix: Check if the install directory is empty when uninstall 2024-03-21 15:24:12 +08:00
MystiPanda 74a3290abc chore: fix typo 2024-03-21 14:09:59 +08:00
MystiPanda ab306d21f3 Release 1.5.10 2024-03-21 13:47:39 +08:00
MystiPanda 1bd7795851 chore: Change a little 2024-03-21 12:55:22 +08:00
MystiPanda 302c142346 build: Update depends 2024-03-21 11:53:25 +08:00
MystiPanda 7c254c9b45 fix: a little 2024-03-21 11:43:16 +08:00
MystiPanda b1c458359d feat: Confirm before deletion
#703
2024-03-21 11:39:01 +08:00
MystiPanda 2d925c62f5 feat: Support config redir port and tproxy port 2024-03-21 10:54:56 +08:00
MystiPanda ef4711987f chore: limit port config
#699
2024-03-20 21:23:10 +08:00
MystiPanda 8910dfaecf fix: Limit icon width
#697
2024-03-20 20:38:45 +08:00
MystiPanda e8b2e79c7d fix: service logs are not cleared
#695
2024-03-20 20:31:00 +08:00
MystiPanda 333ee1e1f7 docs: Improve the issue template 2024-03-20 18:06:02 +08:00
MystiPanda ba013bbe96 feat: Optimize Linux tray menu 2024-03-18 11:11:18 +08:00
MystiPanda 636e66fe7a Revert "fix: remove activation policy" 2024-03-18 09:53:48 +08:00
MystiPanda 372a45e667 Release 1.5.9 2024-03-17 11:25:25 +08:00
MystiPanda ab6b796ce2 fix: drag error
#643
2024-03-16 20:37:39 +08:00
MystiPanda 71e6c02897 fix: drag error 2024-03-16 17:06:59 +08:00
MystiPanda 3edeaa7038 fix: Try to fix touch drag
#456
2024-03-16 10:54:19 +08:00
MystiPanda f2f75d4015 build: Update to Tauri 1.6.1 2024-03-16 00:03:34 +08:00
MystiPanda 69adade738 ci: update alpha script 2024-03-15 21:52:56 +08:00
MystiPanda 1bd88828e4 ci: remove 2024-03-15 21:42:55 +08:00
MystiPanda 977fcbe6cd feat: Try to support more architecture 2024-03-15 21:14:05 +08:00
MystiPanda aa76a5c436 refactor: Try to migrate to boa_engine
#634
2024-03-15 19:58:22 +08:00
MystiPanda ea5fad428d fix: Avoid empty user-agent 2024-03-15 18:27:24 +08:00
MystiPanda 0e1e27b35a feat: Try to cache remote images
#603
2024-03-15 16:43:39 +08:00
screw-hand d41f67a156 fix: mac setting theme font-famil not working (#632) 2024-03-15 15:17:05 +08:00
MystiPanda 6f10dec808 fix: remove activation policy
#592
2024-03-15 12:44:25 +08:00
xkww3n 9b79da2cdf fix: Set REJECT-DROP policy the same text color as REJECT (#622) 2024-03-14 18:54:26 +08:00
MystiPanda c11b8519f1 Release 1.5.8 2024-03-13 14:17:06 +08:00
wonfen b9e23a0b59 Sytle: A little tweak 2024-03-13 14:09:08 +08:00
MystiPanda 75d41b6fe5 feat: Add border-radius for window on linux 2024-03-13 13:58:29 +08:00
Amnesiash 4256590735 update profile ui (#594) 2024-03-13 10:53:39 +08:00
MystiPanda 462fb05ea8 fix: Try to fix #577 again 2024-03-11 22:52:29 +08:00
MystiPanda 812dbfd836 fix: script 2024-03-11 22:10:20 +08:00
MystiPanda cfca837777 fix: typo 2024-03-11 22:05:56 +08:00
MystiPanda ac5fb1948a fix: Try to fix #577 2024-03-11 22:03:45 +08:00
MystiPanda 22d8b73625 feat: Allow open devtools 2024-03-11 20:19:21 +08:00
MystiPanda edde40c298 docs: Update Readme 2024-03-11 18:38:58 +08:00
MystiPanda 50ade92238 Release 1.5.7 2024-03-11 17:09:01 +08:00
MystiPanda b598d00aef fix: display error 2024-03-11 16:18:39 +08:00
MystiPanda 97bee17e4e chore: Change IP 2024-03-11 16:05:09 +08:00
MystiPanda 6aa5c79332 fix: a little 2024-03-11 15:02:41 +08:00
MystiPanda 7c5ce756f9 fix: Change DNS for MacOS Tun Mode
#568
2024-03-11 14:55:00 +08:00
MystiPanda 43d53a9ffa fix: default value 2024-03-11 13:17:45 +08:00
MystiPanda e6589bee5b fix: Try to fix touch drag
#456
2024-03-11 12:36:20 +08:00
MystiPanda 9f94cad615 feat: Allow to control whether auto check update 2024-03-11 12:17:46 +08:00
MystiPanda 7778d9f773 style: fix a little 2024-03-10 23:56:04 +08:00
MystiPanda 564bd55147 style: fix styles 2024-03-10 23:47:12 +08:00
MystiPanda a7ff806522 fix styles 2024-03-10 22:13:25 +08:00
MystiPanda ac3b074d66 chore: proxy group header height 2024-03-10 21:56:49 +08:00
MystiPanda da949c2604 chore: adjust proxy group font style 2024-03-10 21:52:40 +08:00
MystiPanda 20a9775dc6 chore: Adjust Profile Style 2024-03-10 21:37:52 +08:00
MystiPanda 8ec19e058f chore: Add Default value 2024-03-10 21:15:22 +08:00
MystiPanda a5c4549722 style: Adjust icon color 2024-03-10 20:48:13 +08:00
AmnesiashandMystiPanda c7d302aa16 style: Adjust colorful icon (#558)
Co-authored-by: MystiPanda <mystipanda@proton.me>
2024-03-10 20:37:00 +08:00
MystiPanda 77ac565ff3 chore: i18n 2024-03-10 20:33:27 +08:00
MystiPanda 2e6f834a50 chore: Update Icon 2024-03-10 20:14:51 +08:00
MystiPanda 5b044cca9e feat: Add option to control menu icon 2024-03-10 19:54:47 +08:00
MystiPanda 2ab5623809 chore: Adjust styles 2024-03-10 13:24:38 +08:00
MystiPanda 2d07f0d77c Revert "chore: Adjust secondary text style (#545)"
This reverts commit 8ba7f9f702.
2024-03-10 13:08:09 +08:00
Amnesiash 8ba7f9f702 chore: Adjust secondary text style (#545) 2024-03-10 12:54:34 +08:00
MystiPanda 460ac7a86b style: Adjust delay fontSize
#544
2024-03-10 12:51:53 +08:00
MystiPanda f93a1ab3eb docs: update preview 2024-03-10 12:32:08 +08:00
MystiPanda 6b02696aa8 chore: Change Default Font 2024-03-10 12:23:11 +08:00
MystiPanda 25f20d6a85 chore: delete debug output 2024-03-10 11:47:55 +08:00
MystiPanda 8387964bae Release 1.5.6 2024-03-10 11:43:37 +08:00
MystiPanda 926278617f feat: Add default webui
#530
2024-03-10 11:41:22 +08:00
MystiPanda 1933737a0c Adjust styles 2024-03-10 11:21:17 +08:00
MystiPanda b3e5288bde Adjust styles 2024-03-10 11:12:54 +08:00
wonfen ed6e966b2f Sytle: UI improvement & Update Readme 2024-03-10 07:00:24 +08:00
MystiPanda 9315fe36b6 fix: fix a little 2024-03-10 00:41:18 +08:00
black23eep 41fbf5ba36 Update layout-traffic.tsx (#528) 2024-03-10 00:35:26 +08:00
MystiPanda 40db481453 chore: update 2024-03-10 00:34:22 +08:00
MystiPanda 1b5e295744 fix: fontSize and some styles 2024-03-10 00:22:22 +08:00
Amnesiash b42a52f7f6 fix settings.svg (#526) 2024-03-10 00:04:06 +08:00
MystiPanda 8268df84d0 chore: center 2024-03-09 23:38:03 +08:00
Charles eb6fa5f1f1 tweak(ui): menu icon use svg component (#524) 2024-03-09 23:13:08 +08:00
MystiPanda 025c8856ed fix: img path error 2024-03-09 22:43:53 +08:00
MystiPanda ed421445e9 Release 1.5.5 2024-03-09 22:02:36 +08:00
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
175 changed files with 13878 additions and 5800 deletions
-33
View File
@@ -1,33 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ""
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Information**
- OS: [e.g. macOS]
- Clash Verge Version: [e.g. 1.3.4]
- Clash Core: [e.g. Clash or Clash Meta]
**Additional context**
Add any other context about the problem here.
+56
View File
@@ -0,0 +1,56 @@
name: 问题反馈 / Bug report
title: "[BUG] "
description: 反馈你遇到的问题 / Report the issue you are experiencing
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
## 在提交问题之前,请确认以下事项:
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide/term.html) 以及 [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似issue,否则请在已有的issue下进行讨论
3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索
4. 请 **务必** 先下载 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本测试,确保问题依然存在
5. 请 **务必** 按照模板规范详细描述问题,否则issue将会被关闭
## Before submitting the issue, please make sure of the following checklist:
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide/term.html) and [FAQ](https://clash-verge-rev.github.io/faq/windows.html)
2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue
3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search
4. Please be sure to download the [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version for testing to ensure that the problem still exists
5. Please describe the problem in detail according to the template specification, otherwise the issue will be closed
- type: textarea
id: description
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: 操作系统 / OS
options:
- Windows
- Linux
- MacOS
validations:
required: true
- type: input
attributes:
label: 操作系统版本 / OS Version
description: 请提供你的操作系统版本,Linux请额外提供桌面环境及窗口系统 / Please provide your OS version, for Linux, please also provide the desktop environment and window system
validations:
required: true
- type: textarea
attributes:
label: 日志 / Log
description: 请提供完整或相关部分的Debug日志 / Please provide the complete or relevant part of the Debug log
validations:
required: true
+4
View File
@@ -0,0 +1,4 @@
contact_links:
- name: 讨论交流 / Communication
url: https://t.me/clash_verge_rev
about: 在 Telegram 群组中与其他用户讨论交流 / Communicate with other users in the Telegram group
-20
View File
@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[Feature]"
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
@@ -0,0 +1,35 @@
name: 功能请求 / Feature request
title: "[Feature] "
description: 提出你的功能请求 / Propose your feature request
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
## 在提交问题之前,请确认以下事项:
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide/term.html) 确认软件不存在类似的功能
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似issue,否则请在已有的issue下进行讨论
3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索
4. 请 **务必** 先下载 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本测试,确保该功能还未实现
5. 请 **务必** 按照模板规范详细描述问题,否则issue将会被关闭
## Before submitting the issue, please make sure of the following checklist:
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide/term.html) to confirm that the software does not have similar functions
2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue
3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search
4. Please be sure to download the [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version for testing to ensure that the function has not been implemented
5. Please describe the problem in detail according to the template specification, otherwise the issue will be closed
- type: textarea
id: description
attributes:
label: 功能描述 / Feature description
description: 详细清晰地描述你的功能请求 / A clear and concise description of what the feature is
validations:
required: true
- type: textarea
attributes:
label: 使用场景 / Use case
description: 请描述你的功能请求的使用场景 / Please describe the use case of your feature request
validations:
required: true
+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
+53
View File
@@ -0,0 +1,53 @@
#!/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" = "i686-unknown-linux-gnu" ]; then
dpkg --add-architecture i386
apt-get update
apt-get install -y libstdc++6:i386 libgdk-pixbuf2.0-dev:i386 libatomic1:i386 gcc-multilib g++-multilib libwebkit2gtk-4.0-dev:i386 libssl-dev:i386 libgtk-3-dev:i386 librsvg2-dev:i386 patchelf:i386 libayatana-appindicator3-dev:i386
export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig/:$PKG_CONFIG_PATH
export PKG_CONFIG_SYSROOT_DIR=/
elif [ "$INPUT_TARGET" = "aarch64-unknown-linux-gnu" ]; then
dpkg --add-architecture arm64
apt-get update
apt-get install -y libncurses6:arm64 libtinfo6:arm64 linux-libc-dev:arm64 libncursesw6:arm64 libssl3:arm64 libcups2:arm64
apt-get install -y --no-install-recommends g++-aarch64-linux-gnu libc6-dev-arm64-cross libwebkit2gtk-4.0-dev:arm64 libgtk-3-dev:arm64 patchelf:arm64 librsvg2-dev:arm64 libayatana-appindicator3-dev:arm64
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc
export CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
export PKG_CONFIG_ALLOW_CROSS=1
elif [ "$INPUT_TARGET" = "armv7-unknown-linux-gnueabihf" ]; then
dpkg --add-architecture armhf
apt-get update
apt-get install -y libncurses6:armhf libtinfo6:armhf linux-libc-dev:armhf libncursesw6:armhf libssl3:armhf libcups2:armhf
apt-get install -y --no-install-recommends g++-arm-linux-gnueabihf libc6-dev-armhf-cross libwebkit2gtk-4.0-dev:armhf libgtk-3-dev:armhf patchelf:armhf librsvg2-dev:armhf libayatana-appindicator3-dev:armhf
export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
export CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc
export CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++
export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig
export PKG_CONFIG_ALLOW_CROSS=1
elif [ "$INPUT_TARGET" = "riscv64gc-unknown-linux-gnu" ]; then
dpkg --add-architecture riscv64
apt-get update
apt-get install -y libncurses6:riscv64 libtinfo6:riscv64 linux-libc-dev:riscv64 libncursesw6:riscv64 libssl3:riscv64 libcups2:riscv64
apt-get install -y --no-install-recommends g++-riscv64-linux-gnu libc6-dev-riscv64-cross libwebkit2gtk-4.0-dev:riscv64 libgtk-3-dev:riscv64 patchelf:riscv64 librsvg2-dev:riscv64 libayatana-appindicator3-dev:riscv64
export CARGO_TARGET_RISCV64_UNKNOWN_LINUX_GNU_LINKER=riscv64-linux-gnu-gcc
export CC_riscv64_unknown_linux_gnu=riscv64-linux-gnu-gcc
export CXX_riscv64_unknown_linux_gnu=riscv64-linux-gnu-g++
export PKG_CONFIG_PATH=/usr/lib/riscv64-linux-gnu/pkgconfig
export PKG_CONFIG_ALLOW_CROSS=1
else
echo "Unknown target: $INPUT_TARGET" && exit 1
fi
bash .github/build-for-linux/build.sh
+282
View File
@@ -0,0 +1,282 @@
name: Alpha Build
on:
workflow_dispatch:
push:
branches: [main]
tags-ignore: [updater, alpha]
permissions: write-all
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
jobs:
alpha:
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: windows-latest
target: i686-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: 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@v3
name: Install pnpm
with:
version: 9
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: alpha
releaseName: "Clash Verge Rev Alpha"
releaseBody: "More new features are now supported."
releaseDraft: false
prerelease: true
tauriScript: pnpm
args: --target ${{ matrix.target }}
- name: Portable Bundle
if: matrix.os == 'windows-latest'
run: pnpm portable ${{ matrix.target }} --alpha
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
alpha-for-linux:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
target: i686-unknown-linux-gnu
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
- os: ubuntu-latest
target: armv7-unknown-linux-gnueabihf
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
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
- name: Upload Release
if: startsWith(matrix.target, 'x86_64')
uses: softprops/action-gh-release@v2
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@v2
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
alpha-for-fixed-webview2:
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
target: x86_64-pc-windows-msvc
arch: x64
- os: windows-latest
target: i686-pc-windows-msvc
arch: x86
- os: windows-latest
target: aarch64-pc-windows-msvc
arch: arm64
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
- uses: pnpm/action-setup@v3
name: Install pnpm
with:
version: 9
run_install: false
- name: Pnpm install and check
run: |
pnpm i
pnpm check ${{ matrix.target }}
- name: Download WebView2 Runtime
run: |
invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/109.0.1518.78/Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab
Expand .\Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -F:* ./src-tauri
Remove-Item .\src-tauri\tauri.windows.conf.json
Rename-Item .\src-tauri\webview2.${{ matrix.arch }}.json tauri.windows.conf.json
- name: Tauri build
id: build
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tauriScript: pnpm
args: --target ${{ matrix.target }}
- name: Rename
run: |
Rename-Item '.\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\Clash Verge_${{steps.build.outputs.appVersion}}_${{ matrix.arch }}-setup.exe' 'Clash Verge_${{steps.build.outputs.appVersion}}_${{ matrix.arch }}_fixed_webview2-setup.exe'
Rename-Item '.\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\Clash Verge_${{steps.build.outputs.appVersion}}_${{ matrix.arch }}-setup.nsis.zip' 'Clash Verge_${{steps.build.outputs.appVersion}}_${{ matrix.arch }}_fixed_webview2-setup.nsis.zip'
Rename-Item '.\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\Clash Verge_${{steps.build.outputs.appVersion}}_${{ matrix.arch }}-setup.nsis.zip.sig' 'Clash Verge_${{steps.build.outputs.appVersion}}_${{ matrix.arch }}_fixed_webview2-setup.nsis.zip.sig'
- name: Upload Release
uses: softprops/action-gh-release@v2
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/nsis/*setup*
- name: Portable Bundle
run: pnpm portable-fixed-webview2 ${{ matrix.target }} --alpha
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
update_tag:
name: Update tag
runs-on: ubuntu-latest
needs: [alpha, alpha-for-linux, alpha-for-fixed-webview2]
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'
## 我应该下载哪个版本?
### MacOS (提示文件损坏或开发者无法验证请查看下面FAQ)
- MacOS intel芯片: x64.dmg
- MacOS apple M芯片: aarch64.dmg
### Linux
- Linux 64位: amd64.AppImage/amd64.deb
- Linux 32位: i386.deb
- Linux arm64架构: arm64.deb
- Linux armv7架构: armhf.deb
### Windows (Win7 用户请查看下面FAQ手动替换内核使用)
#### 正常版本(推荐)
- 64位: x64-setup.exe
- 32位: x86-setup.exe
- arm64架构: arm64-setup.exe
#### 便携版(不推荐使用,无法自动更新)
- 64位: x64_portable.zip
- 32位: x86_portable.zip
- arm64架构: arm64_portable.zip
#### 内置Webview2版(体积较大,仅在企业版系统或Win7无法安装webview2时使用)
- 64位: x64_fixed_webview2-setup.exe
- 32位: x86_fixed_webview2-setup.exe
- arm64架构: arm64_fixed_webview2-setup.exe
### FAQ
- [FAQ](https://clash-verge-rev.github.io/faq/windows.html)
Created at ${{ env.BUILDTIME }}.
EOF
- name: Upload Release
uses: softprops/action-gh-release@v2
with:
tag_name: alpha
name: "Clash Verge Rev Alpha"
body_path: release.txt
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
generate_release_notes: true
-101
View File
@@ -1,101 +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 }}
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
-97
View File
@@ -1,97 +0,0 @@
name: Dev Build
on:
workflow_dispatch:
push:
branches:
- dev
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
jobs:
release:
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
- 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: Delete current release assets
if: startsWith(matrix.os, 'ubuntu-')
uses: mknejp/delete-release-assets@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: dev
fail-if-no-assets: false
fail-if-no-release: false
assets: |
*.zip
*.gz
*.AppImage
*.deb
*.dmg
*.msi
*.sig
*.exe
*.json
- 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 openssl
- name: Pnpm install and check
run: |
pnpm i
pnpm 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: dev
releaseName: "Clash Verge Dev"
releaseBody: "More new features are now supported."
releaseDraft: false
prerelease: true
tauriScript: pnpm
args: -f default-meta
- name: Portable Bundle
if: matrix.os == 'windows-latest'
run: |
pnpm portable
env:
TAG_NAME: dev
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
VITE_WIN_PORTABLE: 1
-106
View File
@@ -1,106 +0,0 @@
name: Meta CI
on:
workflow_dispatch:
push:
tags:
- v**
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
jobs:
release:
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
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: Delete current release assets
if: matrix.os == 'ubuntu-latest'
uses: mknejp/delete-release-assets@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: meta
fail-if-no-assets: false
fail-if-no-release: false
assets: |
*.zip
*.gz
*.AppImage
*.deb
*.dmg
*.msi
*.sig
- 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 openssl
- 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: meta
releaseName: "Clash Verge Meta"
releaseBody: ""
releaseDraft: false
prerelease: true
args: -f default-meta
- name: Portable Bundle
if: matrix.os == 'windows-latest'
run: |
yarn build -f default-meta
yarn run portable
env:
TAG_NAME: meta
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
VITE_WIN_PORTABLE: 1
+166 -95
View File
@@ -2,9 +2,6 @@ name: Release Build
on:
workflow_dispatch:
push:
tags:
- v**
permissions: write-all
env:
CARGO_INCREMENTAL: 0
@@ -20,18 +17,12 @@ jobs:
target: x86_64-pc-windows-msvc
- os: windows-latest
target: i686-pc-windows-msvc
# - os: windows-latest
# target: aarch64-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
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
# - os: ubuntu-latest
# target: armv7-unknown-linux-gnueabihf
runs-on: ${{ matrix.os }}
steps:
@@ -42,8 +33,7 @@ jobs:
uses: dtolnay/rust-toolchain@stable
- name: Add Rust Target
run: |
rustup target add ${{ matrix.target }}
run: rustup target add ${{ matrix.target }}
- name: Rust Cache
uses: Swatinem/rust-cache@v2
@@ -55,49 +45,18 @@ jobs:
with:
node-version: "20"
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v3
name: Install pnpm
with:
version: 8
version: 9
run_install: false
- name: Install Dependencies (Ubuntu Only)
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.target,'x86_64')
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libayatana-appindicator3-dev librsvg2-dev patchelf openssl
- name: Install Dependencies (Ubuntu Only)
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.target,'aarch64')
run: |
sed 's/mirror+file:\/etc\/apt\/apt-mirrors.txt/[arch-=amd64] http:\/\/ports.ubuntu.com\/ubuntu-ports\//g' /etc/apt/sources.list | sudo tee /etc/apt/sources.list.d/ports.list
sudo sed -i 's/mirror+file/[arch=amd64] mirror+file/g' /etc/apt/sources.list
cat /etc/apt/sources.list
cat /etc/apt/sources.list.d/ports.list
sudo dpkg --add-architecture arm64
sudo apt-get update
sudo apt-get install -y libncurses6:arm64 libtinfo6:arm64 linux-libc-dev:arm64 libncursesw6:arm64 libssl3:arm64 libcups2:arm64
sudo 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
- name: Install Dependencies (Ubuntu Only)
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.target,'armv7')
run: |
sed 's/mirror+file:\/etc\/apt\/apt-mirrors.txt/[arch-=amd64] http:\/\/ports.ubuntu.com\/ubuntu-ports\//g' /etc/apt/sources.list | sudo tee /etc/apt/sources.list.d/ports.list
sudo sed -i 's/mirror+file/[arch=amd64] mirror+file/g' /etc/apt/sources.list
cat /etc/apt/sources.list
cat /etc/apt/sources.list.d/ports.list
sudo dpkg --add-architecture armhf
sudo apt-get update
sudo apt-get install -y libncurses6:armhf libtinfo6:armhf linux-libc-dev:armhf libncursesw6:armhf libssl3:armhf libcups2:armhf
sudo 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
- name: Pnpm install and check
run: |
pnpm i
pnpm check ${{ matrix.target }}
- name: Tauri build
if: startsWith(matrix.os, 'windows') || startsWith(matrix.os,'macos') || startsWith(matrix.target,'x86_64')
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -105,69 +64,155 @@ jobs:
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tagName: v__VERSION__
releaseName: "Clash Verge v__VERSION__"
releaseName: "Clash Verge Rev v__VERSION__"
releaseBody: "More new features are now supported."
releaseDraft: false
prerelease: false
tauriScript: pnpm
args: -f default-meta --target ${{ matrix.target }}
- name: Tauri build (Ubuntu Arm64)
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.target,'aarch64')
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 }}
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc
CXX_aarch64_unknown_linux_gnu: aarch64-linux-gnu-g++
PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig
PKG_CONFIG_ALLOW_CROSS: 1
with:
tagName: v__VERSION__
releaseName: "Clash Verge v__VERSION__"
releaseBody: "More new features are now supported."
releaseDraft: false
prerelease: false
tauriScript: pnpm
args: -f default-meta --target ${{ matrix.target }} -b deb,updater
- name: Tauri build (Ubuntu Armv7)
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.target,'armv7')
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 }}
CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc
CC_armv7_unknown_linux_gnueabihf: arm-linux-gnueabihf-gcc
CXX_armv7_unknown_linux_gnueabihf: arm-linux-gnueabihf-g++
PKG_CONFIG_PATH: /usr/lib/arm-linux-gnueabihf/pkgconfig
PKG_CONFIG_ALLOW_CROSS: 1
with:
tagName: v__VERSION__
releaseName: "Clash Verge v__VERSION__"
releaseBody: "More new features are now supported."
releaseDraft: false
prerelease: false
tauriScript: pnpm
args: -f default-meta --target ${{ matrix.target }} -b deb,updater
args: --target ${{ matrix.target }}
- name: Portable Bundle
if: matrix.os == 'windows-latest'
run: pnpm portable ${{ matrix.target }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-for-linux:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
target: i686-unknown-linux-gnu
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
- os: ubuntu-latest
target: armv7-unknown-linux-gnueabihf
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: |
pnpm portable
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@v2
with:
tag_name: v${{env.VERSION}}
name: "Clash Verge Rev v${{env.VERSION}}"
body: "More new features are now supported."
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/${{ matrix.target }}/release/bundle/appimage/*.AppImage*
- name: Upload Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{env.VERSION}}
name: "Clash Verge Rev v${{env.VERSION}}"
body: "More new features are now supported."
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
release-for-fixed-webview2:
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
target: x86_64-pc-windows-msvc
arch: x64
- os: windows-latest
target: i686-pc-windows-msvc
arch: x86
- os: windows-latest
target: aarch64-pc-windows-msvc
arch: arm64
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
- uses: pnpm/action-setup@v3
name: Install pnpm
with:
version: 9
run_install: false
- name: Pnpm install and check
run: |
pnpm i
pnpm check ${{ matrix.target }}
- name: Download WebView2 Runtime
run: |
invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/109.0.1518.78/Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab
Expand .\Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -F:* ./src-tauri
Remove-Item .\src-tauri\tauri.windows.conf.json
Rename-Item .\src-tauri\webview2.${{ matrix.arch }}.json tauri.windows.conf.json
- name: Tauri build
id: build
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
VITE_WIN_PORTABLE: 1
with:
tauriScript: pnpm
args: --target ${{ matrix.target }}
- name: Rename
run: |
Rename-Item '.\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\Clash Verge_${{steps.build.outputs.appVersion}}_${{ matrix.arch }}-setup.exe' 'Clash Verge_${{steps.build.outputs.appVersion}}_${{ matrix.arch }}_fixed_webview2-setup.exe'
Rename-Item '.\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\Clash Verge_${{steps.build.outputs.appVersion}}_${{ matrix.arch }}-setup.nsis.zip' 'Clash Verge_${{steps.build.outputs.appVersion}}_${{ matrix.arch }}_fixed_webview2-setup.nsis.zip'
Rename-Item '.\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\Clash Verge_${{steps.build.outputs.appVersion}}_${{ matrix.arch }}-setup.nsis.zip.sig' 'Clash Verge_${{steps.build.outputs.appVersion}}_${{ matrix.arch }}_fixed_webview2-setup.nsis.zip.sig'
- name: Upload Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{steps.build.outputs.appVersion}}
name: "Clash Verge Rev v${{steps.build.outputs.appVersion}}"
body: "More new features are now supported."
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*setup*
- name: Portable Bundle
run: pnpm portable-fixed-webview2 ${{ matrix.target }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-update:
needs: [release]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
needs: [release, release-for-linux]
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -180,7 +225,7 @@ jobs:
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
version: 9
run_install: false
- name: Pnpm install
@@ -190,3 +235,29 @@ jobs:
run: pnpm updater
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-update-for-fixed-webview2:
runs-on: ubuntu-latest
needs: [release-for-fixed-webview2]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 9
run_install: false
- name: Pnpm install
run: pnpm i
- name: Release updater file
run: pnpm updater-fixed-webview2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-75
View File
@@ -1,75 +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 }}
steps:
- name: System Version
run: |
echo ${{ github.event.inputs.os }}
- name: Checkout repository
uses: actions/checkout@v4
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "16"
cache: "yarn"
- 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 libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
- name: Yarn install and check
run: |
yarn install --network-timeout 1000000 --frozen-lockfile
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: alpha
releaseName: "Clash Verge Alpha"
releaseBody: "Alpha Version (include debug)"
releaseDraft: false
includeUpdaterJson: false
+27 -2
View File
@@ -10,14 +10,14 @@ jobs:
uses: actions/checkout@v3
- name: Install Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: "20"
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
version: 9
run_install: false
- name: Pnpm install
@@ -27,3 +27,28 @@ jobs:
run: pnpm updater
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-update-for-fixed-webview2:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 9
run_install: false
- name: Pnpm install
run: pnpm i
- name: Release updater file
run: pnpm updater-fixed-webview2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+2
View File
@@ -1,4 +1,5 @@
node_modules
.pnpm-store
.DS_Store
dist
dist-ssr
@@ -6,3 +7,4 @@ dist-ssr
update.json
scripts/_env.sh
.vscode
.tool-versions
+1
View File
@@ -0,0 +1 @@
nodejs 21.7.1
+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!
+26 -61
View File
@@ -1,23 +1,29 @@
<h1 align="center">
<img src="./src/assets/image/logo.png" alt="Clash" width="128" />
<br>
Continuation of Clash Verge
Continuation of <a href="https://github.com/zzzgydi/clash-verge">Clash Verge</a>
<br>
</h1>
<h3 align="center">
A Clash Meta 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
## Preview
- 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://github.com/clash-verge-rev/clash-verge-rev/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.
![preview](./docs/preview.png)
#### TG Group: [@clash_verge_rev](https://t.me/clash_verge_rev)
## Install
请到发布页面下载对应的安装包:[Release page](https://github.com/clash-verge-rev/clash-verge-rev/releases)<br>
Go to the [release page](https://github.com/clash-verge-rev/clash-verge-rev/releases) to download the corresponding installation package<br>
Supports Windows (x64/x86), Linux (x64/arm64) and macOS 10.15+ (intel/apple).
### 安装说明和常见问题,请到[文档页](https://clash-verge-rev.github.io/)查看:[Doc](https://clash-verge-rev.github.io/)
---
### TG Group: [@clash_verge_rev](https://t.me/clash_verge_rev)
## Promotion
@@ -33,70 +39,30 @@ A Clash Meta GUI based on <a href="https://github.com/tauri-apps/tauri">tauri</a
- 解锁流媒体及 ChatGPT
- 官网:https://狗狗加速.com
## Preview
## Features
![preview](./docs/preview.gif)
## Install
Download from [release](https://github.com/clash-verge-rev/clash-verge-rev/releases). Supports Windows x64, Linux x86_64 and macOS 11+
- [Windows x64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.0/Clash.Verge_1.4.0_x64_zh-CN.msi)
- [macOS intel](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.0/Clash.Verge_1.4.0_x64.dmg)
- [macOS arm](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.0/Clash.Verge_1.4.0_aarch64.dmg)
- [Linux AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.0/clash-verge_1.4.0_amd64.AppImage)
- [Linux deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.0/clash-verge_1.4.0_amd64.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.
- Since the clash core has been removed. The project no longer maintains the clash core, but only the Clash Meta core.
- Profiles management and enhancement (by yaml and Javascript). [Doc](https://clash-verge-rev.github.io)
- Improved UI and supports custom theme color.
- Built-in support [Clash.Meta(mihomo)](https://github.com/MetaCubeX/mihomo) core.
- System proxy setting and guard.
### 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`
Refer to [Doc FAQ Page](https://clash-verge-rev.github.io/faq/windows.html)
## Development
You should install Rust and Nodejs, see [here](https://tauri.app/v1/guides/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
pnpm i
```
Then download the clash binary... Or you can download it from [clash meta release](https://github.com/MetaCubeX/Clash.Meta/releases) and rename it according to [tauri config](https://tauri.studio/docs/api/config/#tauri.bundle.externalBin).
```shell
# force update to latest version
# pnpm run check --force
pnpm run check
```
Then run
```shell
pnpm dev
# run it in another way if app instance exists
pnpm dev:diff
```
Or you can build it
```shell
pnpm build
```
## Todos
> This keng is a little big...
## Disclaimer
This is a learning project for Rust practice.
## Contributions
Issue and PR welcome!
@@ -105,11 +71,10 @@ Issue and PR welcome!
Clash Verge rev was based on or inspired by these projects and so on:
- [keiko233/clash-nyanpasu](https://github.com/keiko233/clash-nyanpasu): A Clash Verge variant.
- [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!
+391
View File
@@ -1,8 +1,399 @@
## v1.6.2
### Features
- 支持本地文件拖拽导入
- 重新支持 32 位 CPU
- 新增内置 Webview2 版本
- 优化 Merge 逻辑,支持深度合并
- 删除 Merge 配置中的 append/prepend-provider 字段
- 支持更新稳定版内核
### Bugs Fixes
- MacOS DNS 还原失败
- CMD 环境变量格式错误
- Linux 下与 N 卡的兼容性问题
- 修改 Tun 设置不立即生效
## v1.6.1
### Features
- 鼠标悬浮显示当前订阅的名称 [#938](https://github.com/clash-verge-rev/clash-verge-rev/pull/938)
- 日志过滤支持正则表达式 [#959](https://github.com/clash-verge-rev/clash-verge-rev/pull/959)
- 更新 Clash 内核到 1.18.4
### Bugs Fixes
- 修复 Linux KDE 环境下系统代理无法开启的问题
- 窗口最大化图标调整 [#924](https://github.com/clash-verge-rev/clash-verge-rev/pull/924)
- 修改 MacOS 托盘点击行为(左键菜单,右键点击事件)
- 修复 MacOS 服务模式安装失败的问题
---
## v1.6.0
### Features
- Meta(mihomo)内核回退 1.18.1(当前新版内核 hy2 协议有 bug,等修复后更新)
- 多处界面细节调整 [#724](https://github.com/clash-verge-rev/clash-verge-rev/pull/724) [#799](https://github.com/clash-verge-rev/clash-verge-rev/pull/799) [#900](https://github.com/clash-verge-rev/clash-verge-rev/pull/900) [#901](https://github.com/clash-verge-rev/clash-verge-rev/pull/901)
- Linux 下新增服务模式
- 新增订阅卡片右键可以打开机场首页
- url-test 支持手动选择、节点组 fixed 节点使用角标展示 [#840](https://github.com/clash-verge-rev/clash-verge-rev/pull/840)
- Clash 配置、Merge 配置提供 JSON Schema 语法支持、连接界面调整 [#887](https://github.com/clash-verge-rev/clash-verge-rev/pull/887)
- 修改 Merge 配置文件默认内容 [#889](https://github.com/clash-verge-rev/clash-verge-rev/pull/889)
- 修改 tun 模式默认 mtu 为 1500,老版本升级,需在 tun 模式设置下“重置为默认值”。
- 使用 npm 安装 meta-json-schema [#895](https://github.com/clash-verge-rev/clash-verge-rev/pull/895)
- 更新部分翻译 [#904](https://github.com/clash-verge-rev/clash-verge-rev/pull/904)
- 支持 ico 格式的任务栏图标
### Bugs Fixes
- 修复 Linux KDE 环境下系统代理无法开启的问题
- 修复延迟检测动画问题
- 窗口最大化图标调整 [#816](https://github.com/clash-verge-rev/clash-verge-rev/pull/816)
- 修复 Windows 某些情况下无法安装服务模式 [#822](https://github.com/clash-verge-rev/clash-verge-rev/pull/822)
- UI 细节修复 [#821](https://github.com/clash-verge-rev/clash-verge-rev/pull/821)
- 修复使用默认编辑器打开配置文件
- 修复内核文件在特定目录也可以更新的问题 [#857](https://github.com/clash-verge-rev/clash-verge-rev/pull/857)
- 修复服务模式的安装目录问题
- 修复删除配置文件的“更新间隔”出现的问题 [#907](https://github.com/clash-verge-rev/clash-verge-rev/issues/907)
### 已知问题(历史遗留问题,暂未找到有效解决方案)
- MacOS M 芯片下服务模式无法安装;临时解决方案:在内核 ⚙️ 下,手动授权,再打开 tun 模式。
- MacOS 下如果删除过网络配置,会导致无法正常打开系统代理;临时解决方案:使用浏览器代理插件或手动配置系统代理。
- Window 拨号连接下无法正确识别并打开系统代理;临时解决方案:使用浏览器代理插件或使用 tun 模式。
---
## v1.5.11
### Features
- Meta(mihomo)内核更新 1.18.2
### Bugs Fixes
- 升级图标无法点击的问题
- 卸载时检查安装目录是否为空
- 代理界面图标重合的问题
---
## v1.5.10
### Features
- 优化 Linux 托盘菜单显示
- 添加透明代理端口设置
- 删除订阅前确认
### Bugs Fixes
- 删除 MacOS 程序坞图标
- Windows 下 service 日志没有清理
- MacOS 无法开启系统代理
---
## v1.5.9
### Features
- 缓存代理组图标
- 使用`boa_engine` 代替 `rquickjs`
- 支持 Linux armv7
### Bugs Fixes
- Windows 首次安装无法点击
- Windows 触摸屏无法拖动
- 规则列表 `REJECT-DROP` 颜色
- MacOS Dock 栏不显示图标
- MacOS 自定义字体无效
- 避免使用空 UA 拉取订阅
---
## v1.5.8
### Features
- 优化 UI 细节
- Linux 绘制窗口圆角
- 开放 DevTools
### Bugs Fixes
- 修复 MacOS 下开启 Tun 内核崩溃的问题
---
## v1.5.7
### Features
- 优化 UI 各种细节
- 提供菜单栏图标样式切换选项(单色/彩色/禁用)
- 添加自动检查更新开关
- MacOS 开启 Tun 模式自动修改 DNS
- 调整可拖动区域(尝试修复触摸屏无法拖动的问题)
---
## v1.5.6
### Features
- 全新专属 Verge rev UI 界面 (by @Amnesiash) 及细节调整
- 提供允许无效证书的开关
- 删除不必要的快捷键
- Provider 更新添加动画
- Merge 支持 Provider
- 更换订阅框的粘贴按钮,删除默认的"Remote File" Profile 名称
- 链接菜单添加节点显示
### 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
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 576 KiB

+37 -34
View File
@@ -1,75 +1,78 @@
{
"name": "clash-verge",
"version": "1.4.2",
"license": "GPL-3.0",
"version": "1.6.2",
"license": "GPL-3.0-only",
"scripts": {
"dev": "tauri dev -f default-meta",
"dev": "tauri dev",
"dev:diff": "tauri dev -f verge-dev",
"build": "tauri build -f default-meta",
"build": "tauri build",
"tauri": "tauri",
"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",
"updater-fixed-webview2": "node scripts/updater-fixed-webview2.mjs",
"portable": "node scripts/portable.mjs",
"portable-fixed-webview2": "node scripts/portable-fixed-webview2.mjs",
"prepare": "husky install"
},
"dependencies": {
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@juggle/resize-observer": "^3.4.0",
"@mui/icons-material": "^5.14.19",
"@mui/icons-material": "^5.15.16",
"@mui/lab": "5.0.0-alpha.149",
"@mui/material": "^5.14.19",
"@mui/x-data-grid": "^6.18.2",
"@tauri-apps/api": "^1.5.1",
"ahooks": "^3.7.8",
"axios": "^1.6.2",
"@mui/material": "^5.15.16",
"@mui/x-data-grid": "^6.19.11",
"@tauri-apps/api": "^1.5.4",
"@types/json-schema": "^7.0.15",
"ahooks": "^3.7.11",
"axios": "^1.6.8",
"dayjs": "1.11.5",
"i18next": "^23.7.7",
"i18next": "^23.11.3",
"lodash-es": "^4.17.21",
"monaco-editor": "^0.34.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"meta-json-schema": "1.18.4-beta4",
"monaco-editor": "^0.47.0",
"monaco-yaml": "^5.1.1",
"nanoid": "^5.0.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-error-boundary": "^3.1.4",
"react-hook-form": "^7.48.2",
"react-hook-form": "^7.51.4",
"react-i18next": "^13.5.0",
"react-router-dom": "^6.20.0",
"react-markdown": "^9.0.1",
"react-router-dom": "^6.23.0",
"react-transition-group": "^4.4.5",
"react-virtuoso": "^4.6.2",
"react-virtuoso": "^4.7.10",
"recoil": "^0.7.7",
"snarkdown": "^2.0.0",
"swr": "^1.3.0",
"tar": "^6.2.0"
"tar": "^6.2.1"
},
"devDependencies": {
"@actions/github": "^5.1.1",
"@tauri-apps/cli": "^1.5.6",
"@tauri-apps/cli": "^1.5.13",
"@types/fs-extra": "^9.0.13",
"@types/js-cookie": "^3.0.6",
"@types/lodash": "^4.14.202",
"@types/lodash-es": "^4.17.12",
"@types/react": "^18.2.39",
"@types/react-dom": "^18.2.17",
"@types/react-transition-group": "^4.4.9",
"@vitejs/plugin-react": "^4.2.0",
"adm-zip": "^0.5.10",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"@types/react-transition-group": "^4.4.10",
"@vitejs/plugin-react": "^4.2.1",
"adm-zip": "^0.5.12",
"cross-env": "^7.0.3",
"fs-extra": "^11.2.0",
"https-proxy-agent": "^5.0.1",
"husky": "^7.0.4",
"node-fetch": "^3.3.2",
"prettier": "^2.8.8",
"pretty-quick": "^3.1.3",
"sass": "^1.69.5",
"typescript": "^5.3.2",
"vite": "^4.5.0",
"pretty-quick": "^3.3.1",
"sass": "^1.77.0",
"typescript": "^5.4.5",
"vite": "^5.2.11",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-svgr": "^4.2.0"
},
+3461 -1964
View File
File diff suppressed because it is too large Load Diff
-119
View File
@@ -1,119 +0,0 @@
/**
* Build and upload assets
* for macOS(aarch)
*/
import fs from "fs-extra";
import path from "path";
import { exit } from "process";
import { execSync } from "child_process";
import { createRequire } from "module";
import { getOctokit, context } from "@actions/github";
// to `meta` tag
const META = process.argv.includes("--meta");
// to `alpha` tag
const ALPHA = process.argv.includes("--alpha");
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 tag = META ? "meta" : ALPHA ? "alpha" : `v${version}`;
const buildCmd = META ? `pnpm build -f default-meta` : `pnpm build`;
console.log(`[INFO]: Upload to tag "${tag}"`);
console.log(`[INFO]: Building app. "${buildCmd}"`);
execSync(buildCmd);
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,
});
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);
}
+165 -30
View File
@@ -18,8 +18,11 @@ const PLATFORM_MAP = {
"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",
"riscv64gc-unknown-linux-gnu": "linux",
"loongarch64-unknown-linux-gnu": "linux",
};
const ARCH_MAP = {
"x86_64-pc-windows-msvc": "x64",
@@ -28,8 +31,11 @@ const ARCH_MAP = {
"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",
"riscv64gc-unknown-linux-gnu": "riscv64",
"loongarch64-unknown-linux-gnu": "loong64",
};
const arg1 = process.argv.slice(2)[0];
@@ -46,50 +52,99 @@ const SIDECAR_HOST = target
.match(/(?<=host: ).+(?=\s*)/g)[0];
/* ======= clash meta alpha======= */
const VERSION_URL =
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",
"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",
"linux-x64": "mihomo-linux-amd64-compatible",
"linux-ia32": "mihomo-linux-386",
"linux-arm64": "mihomo-linux-arm64",
"linux-arm": "mihomo-linux-armv7",
"linux-riscv64": "mihomo-linux-riscv64",
"linux-loong64": "mihomo-linux-loong64",
};
// Fetch the latest release version from the version.txt file
async function getLatestVersion() {
// 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(VERSION_URL, { method: "GET" });
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 release version: ${META_ALPHA_VERSION}`);
console.log(`Latest alpha version: ${META_ALPHA_VERSION}`);
} catch (error) {
console.error("Error fetching latest release version:", error.message);
console.error("Error fetching latest alpha version:", error.message);
process.exit(1);
}
}
/* ======= clash meta stable ======= */
const META_URL_PREFIX = `https://github.com/MetaCubeX/mihomo/releases/download`;
let META_VERSION = "v1.17.0";
const META_VERSION_URL =
"https://github.com/MetaCubeX/mihomo/releases/latest/download/version.txt";
const META_URL_PREFIX = `https://github.com/clash-verge-rev/mihomo/releases/download`;
let META_VERSION;
const META_MAP = {
"win32-x64": "mihomo-windows-amd64",
"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",
"linux-x64": "mihomo-linux-amd64-compatible",
"linux-ia32": "mihomo-linux-386",
"linux-arm64": "mihomo-linux-arm64",
"linux-arm": "mihomo-linux-armv7",
"linux-riscv64": "mihomo-linux-riscv64",
"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
*/
@@ -266,26 +321,94 @@ 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);
}
};
// service chmod
const resolveServicePermission = async () => {
const serviceExecutables = [
"clash-verge-service",
"install-service",
"uninstall-service",
];
const resDir = path.join(cwd, "src-tauri/resources");
for (let f of serviceExecutables) {
const targetPath = path.join(resDir, f);
if (await fs.pathExists(targetPath)) {
execSync(`chmod 755 ${targetPath}`);
console.log(`[INFO]: "${targetPath}" chmod finished`);
}
}
};
/**
* main
*/
const SERVICE_URL =
"https://github.com/zzzgydi/clash-verge-service/releases/download/latest";
const resolveService = () =>
const SERVICE_URL = `https://github.com/clash-verge-rev/clash-verge-service/releases/download/${SIDECAR_HOST}`;
const resolveService = () => {
let ext = platform === "win32" ? ".exe" : "";
resolveResource({
file: "clash-verge-service.exe",
downloadURL: `${SERVICE_URL}/clash-verge-service.exe`,
file: "clash-verge-service" + ext,
downloadURL: `${SERVICE_URL}/clash-verge-service${ext}`,
});
const resolveInstall = () =>
};
const resolveInstall = () => {
let ext = platform === "win32" ? ".exe" : "";
resolveResource({
file: "install-service.exe",
downloadURL: `${SERVICE_URL}/install-service.exe`,
file: "install-service" + ext,
downloadURL: `${SERVICE_URL}/install-service${ext}`,
});
const resolveUninstall = () =>
};
const resolveUninstall = () => {
let ext = platform === "win32" ? ".exe" : "";
resolveResource({
file: "uninstall-service.exe",
downloadURL: `${SERVICE_URL}/uninstall-service.exe`,
file: "uninstall-service" + ext,
downloadURL: `${SERVICE_URL}/uninstall-service${ext}`,
});
};
const resolveSetDnsScript = () =>
resolveResource({
file: "set_dns.sh",
downloadURL: `https://github.com/clash-verge-rev/set-dns-script/releases/download/script/set_dns.sh`,
});
const resolveUnSetDnsScript = () =>
resolveResource({
file: "unset_dns.sh",
downloadURL: `https://github.com/clash-verge-rev/set-dns-script/releases/download/script/unset_dns.sh`,
});
const resolveMmdb = () =>
resolveResource({
@@ -312,18 +435,22 @@ const tasks = [
// { name: "clash", func: resolveClash, retry: 5 },
{
name: "clash-meta-alpha",
func: () => getLatestVersion().then(() => resolveSidecar(clashMetaAlpha())),
func: () =>
getLatestAlphaVersion().then(() => resolveSidecar(clashMetaAlpha())),
retry: 5,
},
{
name: "clash-meta",
func: () => resolveSidecar(clashMeta()),
func: () =>
getLatestReleaseVersion().then(() => resolveSidecar(clashMeta())),
retry: 5,
},
// { name: "wintun", func: resolveWintun, 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: "plugin", func: resolvePlugin, retry: 5, winOnly: true },
{ name: "service", func: resolveService, retry: 5 },
{ name: "install", func: resolveInstall, retry: 5 },
{ name: "uninstall", func: resolveUninstall, retry: 5 },
{ name: "set_dns_script", func: resolveSetDnsScript, retry: 5 },
{ name: "unset_dns_script", func: resolveUnSetDnsScript, retry: 5 },
{ name: "mmdb", func: resolveMmdb, retry: 5 },
{ name: "geosite", func: resolveGeosite, retry: 5 },
{ name: "geoip", func: resolveGeoIP, retry: 5 },
@@ -333,12 +460,20 @@ const tasks = [
retry: 5,
winOnly: true,
},
{
name: "service_chmod",
func: resolveServicePermission,
retry: 1,
unixOnly: true,
},
];
async function runTask() {
const task = tasks.shift();
if (!task) return;
if (task.winOnly && process.platform !== "win32") return runTask();
if (task.winOnly && platform !== "win32") return runTask();
if (task.linuxOnly && platform !== "linux") return runTask();
if (task.unixOnly && platform === "win32") return runTask();
for (let i = 0; i < task.retry; i++) {
try {
+100
View File
@@ -0,0 +1,100 @@
import fs from "fs-extra";
import path from "path";
import AdmZip from "adm-zip";
import { createRequire } from "module";
import { getOctokit, context } from "@actions/github";
const target = process.argv.slice(2)[0];
const alpha = process.argv.slice(2)[1];
const ARCH_MAP = {
"x86_64-pc-windows-msvc": "x64",
"i686-pc-windows-msvc": "x86",
"aarch64-pc-windows-msvc": "arm64",
};
const PROCESS_MAP = {
x64: "x64",
ia32: "x86",
arm64: "arm64",
};
const arch = target ? ARCH_MAP[target] : PROCESS_MAP[process.arch];
/// Script for ci
/// 打包绿色版/便携版 (only Windows)
async function resolvePortable() {
if (process.platform !== "win32") return;
const releaseDir = target
? `./src-tauri/target/${target}/release`
: `./src-tauri/target/release`;
const configDir = path.join(releaseDir, ".config");
if (!(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-meta.exe"));
zip.addLocalFile(path.join(releaseDir, "clash-meta-alpha.exe"));
zip.addLocalFolder(path.join(releaseDir, "resources"), "resources");
zip.addLocalFolder(
path.join(
releaseDir,
`Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${arch}`
),
`Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${arch}`
);
zip.addLocalFolder(configDir, ".config");
const require = createRequire(import.meta.url);
const packageJson = require("../package.json");
const { version } = packageJson;
const zipFile = `Clash.Verge_${version}_${arch}_fixed_webview2_portable.zip`;
zip.writeZip(zipFile);
console.log("[INFO]: create portable zip successfully");
// push release assets
if (process.env.GITHUB_TOKEN === undefined) {
throw new Error("GITHUB_TOKEN is required");
}
const options = { owner: context.repo.owner, repo: context.repo.repo };
const github = getOctokit(process.env.GITHUB_TOKEN);
const tag = alpha ? "alpha" : process.env.TAG_NAME || `v${version}`;
console.log("[INFO]: upload to ", tag);
const { data: release } = await github.rest.repos.getReleaseByTag({
...options,
tag,
});
let assets = release.assets.filter((x) => {
return x.name === zipFile;
});
if (assets.length > 0) {
let id = assets[0].id;
await github.rest.repos.deleteReleaseAsset({
...options,
asset_id: id,
});
}
console.log(release.name);
await github.rest.repos.uploadReleaseAsset({
...options,
release_id: release.id,
name: zipFile,
data: zip.toBuffer(),
});
}
resolvePortable().catch(console.error);
+39 -6
View File
@@ -4,29 +4,51 @@ import AdmZip from "adm-zip";
import { createRequire } from "module";
import { getOctokit, context } from "@actions/github";
const target = process.argv.slice(2)[0];
const alpha = process.argv.slice(2)[1];
const ARCH_MAP = {
"x86_64-pc-windows-msvc": "x64",
"i686-pc-windows-msvc": "x86",
"aarch64-pc-windows-msvc": "arm64",
};
const PROCESS_MAP = {
x64: "x64",
ia32: "x86",
arm64: "arm64",
};
const arch = target ? ARCH_MAP[target] : PROCESS_MAP[process.arch];
/// Script for ci
/// 打包绿色版/便携版 (only Windows)
async function resolvePortable() {
if (process.platform !== "win32") return;
const releaseDir = "./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}_portable.zip`;
zip.writeZip(zipFile);
console.log("[INFO]: create portable zip successfully");
@@ -38,14 +60,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();
+154
View File
@@ -0,0 +1,154 @@
import fetch from "node-fetch";
import { getOctokit, context } from "@actions/github";
import { resolveUpdateLog } from "./updatelog.mjs";
const UPDATE_TAG_NAME = "updater";
const UPDATE_JSON_FILE = "update-fixed-webview2.json";
const UPDATE_JSON_PROXY = "update-fixed-webview2-proxy.json";
/// generate update.json
/// upload to update tag's release asset
async function resolveUpdater() {
if (process.env.GITHUB_TOKEN === undefined) {
throw new Error("GITHUB_TOKEN is required");
}
const options = { owner: context.repo.owner, repo: context.repo.repo };
const github = getOctokit(process.env.GITHUB_TOKEN);
const { data: tags } = await github.rest.repos.listTags({
...options,
per_page: 10,
page: 1,
});
// get the latest publish tag
const tag = tags.find((t) => t.name.startsWith("v"));
console.log(tag);
console.log();
const { data: latestRelease } = await github.rest.repos.getReleaseByTag({
...options,
tag: tag.name,
});
const updateData = {
name: tag.name,
notes: await resolveUpdateLog(tag.name), // use updatelog.md
pub_date: new Date().toISOString(),
platforms: {
"windows-x86_64": { signature: "", url: "" },
"windows-aarch64": { signature: "", url: "" },
"windows-x86": { signature: "", url: "" },
},
};
const promises = latestRelease.assets.map(async (asset) => {
const { name, browser_download_url } = asset;
// win64 url
if (name.endsWith("x64_fixed_webview2-setup.nsis.zip")) {
updateData.platforms["windows-x86_64"].url = browser_download_url;
}
// win64 signature
if (name.endsWith("x64_fixed_webview2-setup.nsis.zip.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["windows-x86_64"].signature = sig;
}
// win32 url
if (name.endsWith("x86_fixed_webview2-setup.nsis.zip")) {
updateData.platforms["windows-x86"].url = browser_download_url;
}
// win32 signature
if (name.endsWith("x86_fixed_webview2-setup.nsis.zip.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["windows-x86"].signature = sig;
}
// win arm url
if (name.endsWith("arm64_fixed_webview2-setup.nsis.zip")) {
updateData.platforms["windows-aarch64"].url = browser_download_url;
}
// win arm signature
if (name.endsWith("arm64_fixed_webview2-setup.nsis.zip.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["windows-aarch64"].signature = sig;
}
});
await Promise.allSettled(promises);
console.log(updateData);
// maybe should test the signature as well
// delete the null field
Object.entries(updateData.platforms).forEach(([key, value]) => {
if (!value.url) {
console.log(`[Error]: failed to parse release for "${key}"`);
delete updateData.platforms[key];
}
});
// 生成一个代理github的更新文件
// 使用 https://hub.fastgit.xyz/ 做github资源的加速
const updateDataNew = JSON.parse(JSON.stringify(updateData));
Object.entries(updateDataNew.platforms).forEach(([key, value]) => {
if (value.url) {
updateDataNew.platforms[key].url =
"https://mirror.ghproxy.com/" + value.url;
} else {
console.log(`[Error]: updateDataNew.platforms.${key} is null`);
}
});
// update the update.json
const { data: updateRelease } = await github.rest.repos.getReleaseByTag({
...options,
tag: UPDATE_TAG_NAME,
});
// delete the old assets
for (let asset of updateRelease.assets) {
if (asset.name === UPDATE_JSON_FILE) {
await github.rest.repos.deleteReleaseAsset({
...options,
asset_id: asset.id,
});
}
if (asset.name === UPDATE_JSON_PROXY) {
await github.rest.repos
.deleteReleaseAsset({ ...options, asset_id: asset.id })
.catch(console.error); // do not break the pipeline
}
}
// upload new assets
await github.rest.repos.uploadReleaseAsset({
...options,
release_id: updateRelease.id,
name: UPDATE_JSON_FILE,
data: JSON.stringify(updateData, null, 2),
});
await github.rest.repos.uploadReleaseAsset({
...options,
release_id: updateRelease.id,
name: UPDATE_JSON_PROXY,
data: JSON.stringify(updateDataNew, null, 2),
});
}
// get the signature file content
async function getSignature(url) {
const response = await fetch(url, {
method: "GET",
headers: { "Content-Type": "application/octet-stream" },
});
return response.text();
}
resolveUpdater().catch(console.error);
+28 -28
View File
@@ -45,8 +45,12 @@ async function resolveUpdater() {
"darwin-intel": { signature: "", url: "" },
"darwin-x86_64": { signature: "", url: "" },
"linux-x86_64": { signature: "", url: "" },
"linux-x86": { signature: "", url: "" },
"linux-aarch64": { signature: "", url: "" },
"linux-armv7": { signature: "", url: "" },
"windows-x86_64": { signature: "", url: "" },
"windows-aarch64": { signature: "", url: "" },
"windows-x86": { signature: "", url: "" },
},
};
@@ -54,41 +58,35 @@ async function resolveUpdater() {
const { name, browser_download_url } = asset;
// win64 url
if (
name.endsWith(".msi.zip") &&
name.includes("en-US") &&
name.includes("x64")
) {
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") &&
name.includes("x64")
) {
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;
}
// win32 url
if (
name.endsWith(".msi.zip") &&
name.includes("en-US") &&
name.includes("x86")
) {
updateData.platforms["windows-i686"].url = browser_download_url;
if (name.endsWith("x64-setup.nsis.zip")) {
updateData.platforms["windows-x86"].url = browser_download_url;
}
// win32 signature
if (
name.endsWith(".msi.zip.sig") &&
name.includes("en-US") &&
name.includes("x86")
) {
if (name.endsWith("x64-setup.nsis.zip.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["windows-i686"].signature = sig;
updateData.platforms["windows-x86"].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)
@@ -115,22 +113,24 @@ 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;
updateData.platforms["linux-x86"].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;
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;
updateData.platforms["linux-x86"].url = browser_download_url;
// 暂时使用x64版本的url和sig,使得可以检查更新,但aarch64版本还不支持构建appimage
updateData.platforms["linux-aarch64"].signature = sig;
// updateData.platforms["linux-armv7"].signature = sig;
updateData.platforms["linux-armv7"].signature = sig;
}
});
+2115 -875
View File
File diff suppressed because it is too large Load Diff
+17 -25
View File
@@ -1,9 +1,9 @@
[package]
name = "clash-verge"
version = "1.4.2"
version = "1.6.2"
description = "clash verge"
authors = ["zzzgydi"]
license = "GPL-3.0"
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"
@@ -14,52 +14,44 @@ tauri-build = { version = "1", features = [] }
[dependencies]
warp = "0.3"
which = "5.0.0"
anyhow = "1.0"
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.29"
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
rquickjs = "0.3"
sysinfo = "0.30"
boa_engine = "0.18"
serde_json = "1.0"
serde_yaml = "0.9"
auto-launch = "0.5"
once_cell = "1.18"
port_scanner = "0.1.5"
delay_timer = "0.11"
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", "rustls-tls"] }
tauri = { version = "1.5", features = ["clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all"] }
window-vibrancy = { version = "0.4.3" }
window-shadows = { version = "0.2" }
reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
auto-launch = { git="https://github.com/zzzgydi/auto-launch", branch = "main" }
tauri = { version = "1.6", features = [ "fs-read-file", "fs-exists", "path-all", "protocol-asset", "dialog-open", "notification-all", "icon-png", "icon-ico", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all", "devtools"] }
[target.'cfg(windows)'.dependencies]
runas = "=1.1.0"
runas = "=1.2.0"
deelevate = "0.2.0"
winreg = { version = "0.52", features = ["transactions"] }
windows-sys = { version = "0.52", features = ["Win32_System_LibraryLoader", "Win32_System_SystemInformation"] }
[target.'cfg(windows)'.dependencies.tauri]
features = ["global-shortcut-all", "icon-png", "process-all", "shell-all", "system-tray", "updater", "window-all"]
[target.'cfg(linux)'.dependencies.tauri]
features = ["global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all", "native-tls-vendored", "reqwest-native-tls-vendored"]
winreg = "0.52.0"
[target.'cfg(target_os = "linux")'.dependencies]
users = "0.11.0"
#openssl
[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]
verge-dev = []
default-meta = []
[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

Before

Width:  |  Height:  |  Size: 1.3 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

+96 -31
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, Manager};
type CmdResult<T = ()> = Result<T, String>;
#[tauri::command]
@@ -65,6 +65,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult {
match CoreManager::global().update_config().await {
Ok(_) => {
handle::Handle::refresh_clash();
let _ = handle::Handle::update_systray_part();
Config::profiles().apply();
wrap_err!(Config::profiles().data().save_file())?;
Ok(())
@@ -85,7 +86,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
@@ -98,7 +99,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]
@@ -219,7 +220,7 @@ pub fn open_app_dir() -> CmdResult<()> {
#[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(format!("failed to get core dir"))?;
let core_dir = core_dir.parent().ok_or("failed to get core dir")?;
wrap_err!(open::that(core_dir))
}
@@ -249,49 +250,113 @@ pub mod uwp {
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).await {
match clash_api::get_proxy_delay(name, url, timeout).await {
Ok(res) => Ok(res),
Err(err) => Err(format!("{}", err.to_string())),
Err(err) => Err(err.to_string()),
}
}
#[cfg(windows)]
#[tauri::command]
pub fn get_portable_flag() -> CmdResult<bool> {
Ok(*dirs::PORTABLE_FLAG.get().unwrap_or(&false))
}
#[tauri::command]
pub async fn test_delay(url: String) -> CmdResult<u32> {
Ok(feat::test_delay(url).await.unwrap_or(10000u32))
}
#[tauri::command]
pub fn get_app_dir() -> CmdResult<String> {
let app_home_dir = wrap_err!(dirs::app_home_dir())?
.to_string_lossy()
.to_string();
Ok(app_home_dir)
}
#[tauri::command]
pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String> {
let icon_cache_dir = wrap_err!(dirs::app_home_dir())?.join("icons").join("cache");
let icon_path = icon_cache_dir.join(name);
if !icon_cache_dir.exists() {
let _ = std::fs::create_dir_all(&icon_cache_dir);
}
if !icon_path.exists() {
let response = wrap_err!(reqwest::get(url).await)?;
let mut file = wrap_err!(std::fs::File::create(&icon_path))?;
let content = wrap_err!(response.bytes().await)?;
wrap_err!(std::io::copy(&mut content.as_ref(), &mut file))?;
}
Ok(icon_path.to_string_lossy().to_string())
}
#[tauri::command]
pub fn copy_icon_file(path: String, name: String) -> CmdResult<String> {
let file_path = std::path::Path::new(&path);
let icon_dir = wrap_err!(dirs::app_home_dir())?.join("icons");
if !icon_dir.exists() {
let _ = std::fs::create_dir_all(&icon_dir);
}
let ext = match file_path.extension() {
Some(e) => e.to_string_lossy().to_string(),
None => "ico".to_string(),
};
let png_dest_path = icon_dir.join(format!("{name}.png"));
let ico_dest_path = icon_dir.join(format!("{name}.ico"));
let dest_path = icon_dir.join(format!("{name}.{ext}"));
if file_path.exists() {
std::fs::remove_file(png_dest_path).unwrap_or_default();
std::fs::remove_file(ico_dest_path).unwrap_or_default();
match std::fs::copy(file_path, &dest_path) {
Ok(_) => Ok(dest_path.to_string_lossy().to_string()),
Err(err) => Err(err.to_string()),
}
} else {
return Err("file not found".to_string());
}
}
#[tauri::command]
pub fn open_devtools(app_handle: tauri::AppHandle) {
if let Some(window) = app_handle.get_window("main") {
if !window.is_devtools_open() {
window.open_devtools();
} else {
window.close_devtools();
}
}
}
#[tauri::command]
pub fn exit_app(app_handle: tauri::AppHandle) {
let _ = resolve::save_window_size_position(&app_handle, true);
resolve::resolve_reset();
api::process::kill_children();
app_handle.exit(0);
std::process::exit(0);
}
pub mod service {
use super::*;
use crate::core::win_service;
use crate::core::service;
#[tauri::command]
pub async fn check_service() -> CmdResult<win_service::JsonResponse> {
wrap_err!(win_service::check_service().await)
pub async fn check_service() -> CmdResult<service::JsonResponse> {
wrap_err!(service::check_service().await)
}
#[tauri::command]
pub async fn install_service() -> CmdResult {
wrap_err!(win_service::install_service().await)
wrap_err!(service::install_service().await)
}
#[tauri::command]
pub async fn uninstall_service() -> CmdResult {
wrap_err!(win_service::uninstall_service().await)
}
}
#[cfg(not(windows))]
pub mod service {
use super::*;
#[tauri::command]
pub async fn check_service() -> CmdResult {
Ok(())
}
#[tauri::command]
pub async fn install_service() -> CmdResult {
Ok(())
}
#[tauri::command]
pub async fn uninstall_service() -> CmdResult {
Ok(())
wrap_err!(service::uninstall_service().await)
}
}
+131 -38
View File
@@ -12,50 +12,66 @@ 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();
map.insert(
"mixed-port".into(),
match cfg!(feature = "default-meta") {
false => 7890.into(),
true => 7898.into(),
},
);
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(), 1500.into());
#[cfg(not(target_os = "windows"))]
map.insert("redir-port".into(), 7895.into());
#[cfg(target_os = "linux")]
map.insert("tproxy-port".into(), 7896.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(),
match cfg!(feature = "default-meta") {
false => "127.0.0.1:9090".into(),
true => "127.0.0.1:9098".into(),
},
);
map.insert("external-controller".into(), "127.0.0.1:9097".into());
map.insert("secret".into(), "".into());
#[cfg(feature = "default-meta")]
map.insert("unified-delay".into(), true.into());
#[cfg(feature = "default-meta")]
map.insert("tcp-concurrent".into(), true.into());
// map.insert("ipv6".into(), false.into());
map.insert("tun".into(), tun.into());
Self(map)
}
fn guard(mut config: Mapping) -> Mapping {
let port = Self::guard_mixed_port(&config);
#[cfg(not(target_os = "windows"))]
let redir_port = Self::guard_redir_port(&config);
#[cfg(target_os = "linux")]
let tproxy_port = Self::guard_tproxy_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());
#[cfg(not(target_os = "windows"))]
config.insert("redir-port".into(), redir_port.into());
#[cfg(target_os = "linux")]
config.insert("tproxy-port".into(), tproxy_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
}
@@ -78,12 +94,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()),
@@ -92,6 +120,37 @@ impl IClashTemp {
}),
}
}
#[cfg(not(target_os = "windows"))]
pub fn guard_redir_port(config: &Mapping) -> u16 {
let mut port = config
.get("redir-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(7895);
if port == 0 {
port = 7895;
}
port
}
#[cfg(target_os = "linux")]
pub fn guard_tproxy_port(config: &Mapping) -> u16 {
let mut port = config
.get("tproxy-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(7896);
if port == 0 {
port = 7896;
}
port
}
pub fn guard_mixed_port(config: &Mapping) -> u16 {
let mut port = config
@@ -101,9 +160,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
}
@@ -115,7 +204,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(),
};
@@ -126,7 +215,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 {
@@ -138,7 +227,7 @@ impl IClashTemp {
}
socket.to_string()
}
Err(_) => "127.0.0.1:9090".into(),
Err(_) => "127.0.0.1:9097".into(),
}
}
}
@@ -146,6 +235,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,
@@ -165,7 +256,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,
}
@@ -173,12 +266,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"),
@@ -187,7 +280,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!(
@@ -212,7 +305,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")
);
}
+86 -59
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>,
@@ -46,6 +46,10 @@ pub struct PrfItem {
#[serde(skip_serializing_if = "Option::is_none")]
pub option: Option<PrfOption>,
/// profile web page url
#[serde(skip_serializing_if = "Option::is_none")]
pub home: Option<String>,
/// the file data
#[serde(skip)]
pub file_data: Option<String>,
@@ -59,10 +63,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 +88,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 +103,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 +145,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}\""),
}
}
@@ -172,6 +165,7 @@ impl PrfItem {
selected: None,
extra: None,
option: None,
home: None,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(file_data.unwrap_or(tmpl::ITEM_LOCAL.into())),
})
@@ -188,7 +182,9 @@ 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().use_rustls_tls().no_proxy();
@@ -213,26 +209,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?;
@@ -248,12 +245,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,
@@ -262,20 +258,48 @@ 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()),
),
};
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,
},
};
// 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 home = match header.get("profile-web-page-url") {
Some(value) => {
let str_value = value.to_str().unwrap_or("");
Some(str_value.to_string())
},
None => None,
};
@@ -306,6 +330,7 @@ impl PrfItem {
selected: None,
extra,
option,
home,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(data.into()),
})
@@ -327,6 +352,7 @@ impl PrfItem {
selected: None,
extra: None,
option: None,
home: None,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(tmpl::ITEM_MERGE.into()),
})
@@ -345,6 +371,7 @@ impl PrfItem {
desc: Some(desc),
file: Some(file),
url: None,
home: None,
selected: None,
extra: None,
option: None,
+15 -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| {
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,12 +52,6 @@ impl IProfiles {
pub fn template() -> Self {
Self {
valid: Some(vec![
"dns".into(),
"sub-rules".into(),
"unified-delay".into(),
"tcp-concurrent".into(),
]),
items: Some(vec![]),
..Self::default()
}
@@ -93,10 +84,6 @@ impl IProfiles {
self.chain = Some(chain);
}
if let Some(valid) = patch.valid {
self.valid = Some(valid);
}
Ok(())
}
@@ -152,17 +139,19 @@ impl IProfiles {
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(vec![]);
let mut items = self.items.take().unwrap_or_default();
let mut old_index = None;
let mut new_index = None;
for i in 0..items.len() {
for (i, _) in items.iter().enumerate() {
if items[i].uid == Some(active_id.clone()) {
old_index = Some(i);
}
@@ -182,7 +171,7 @@ impl IProfiles {
/// 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 each in items.iter_mut() {
if each.uid == Some(uid.clone()) {
@@ -222,7 +211,7 @@ impl IProfiles {
if each.uid == some_uid {
each.extra = item.extra;
each.updated = item.updated;
each.home = item.home;
// save the file data
// move the field value after save
if let Some(file_data) = item.file_data.take() {
@@ -255,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;
@@ -267,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,
};
@@ -299,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}\"");
}
+17 -3
View File
@@ -1,7 +1,7 @@
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>,
@@ -16,7 +16,7 @@ impl IRuntime {
Self::default()
}
// 这里只更改 allow-lan | ipv6 | log-level
// 这里只更改 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"]
@@ -26,6 +26,20 @@ impl IRuntime {
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));
}
}
}
+92 -20
View File
@@ -19,19 +19,38 @@ pub struct IVerge {
/// `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>,
/// menu icon
pub menu_icon: Option<String>,
/// 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>,
@@ -74,11 +93,14 @@ pub struct IVerge {
/// 切换代理时自动关闭连接
pub auto_close_connection: Option<bool>,
/// 是否自动检查更新
pub auto_check_update: Option<bool>,
/// 默认的延迟测试连接
pub default_latency_test: Option<String>,
/// 支持关闭字段过滤,避免meta的新字段都被过滤掉,默认为真
pub enable_clash_fields: Option<bool>,
/// 默认的延迟测试超时时间
pub default_latency_timeout: Option<i32>,
/// 是否使用内部的脚本支持,默认为真
pub enable_builtin_enhanced: Option<bool>,
@@ -86,6 +108,9 @@ pub struct IVerge {
/// 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>,
@@ -94,11 +119,33 @@ pub struct IVerge {
#[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
/// verge 的各种 port 用于覆盖 clash 的各种 port
#[cfg(not(target_os = "windows"))]
pub verge_redir_port: Option<u16>,
#[cfg(target_os = "linux")]
pub verge_tproxy_port: Option<u16>,
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)]
@@ -130,28 +177,37 @@ impl IVerge {
pub fn template() -> Self {
Self {
clash_core: match cfg!(feature = "default-meta") {
false => Some("clash".into()),
true => Some("clash-meta".into()),
},
language: match cfg!(feature = "default-meta") {
false => Some("en".into()),
true => Some("zh".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),
menu_icon: Some("monochrome".into()),
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(7890),
#[cfg(not(target_os = "windows"))]
verge_redir_port: Some(7895),
#[cfg(target_os = "linux")]
verge_tproxy_port: Some(7896),
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),
auto_check_update: Some(true),
enable_builtin_enhanced: Some(true),
enable_clash_fields: Some(true),
auto_log_clean: Some(3),
..Self::default()
}
@@ -176,17 +232,30 @@ 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!(menu_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);
#[cfg(not(target_os = "windows"))]
patch!(verge_redir_port);
#[cfg(target_os = "linux")]
patch!(verge_tproxy_port);
patch!(verge_mixed_port);
patch!(verge_socks_port);
patch!(verge_port);
patch!(enable_system_proxy);
patch!(enable_proxy_guard);
patch!(system_proxy_bypass);
@@ -198,12 +267,15 @@ impl IVerge {
patch!(hotkeys);
patch!(auto_close_connection);
patch!(auto_check_update);
patch!(default_latency_test);
patch!(default_latency_timeout);
patch!(enable_builtin_enhanced);
patch!(proxy_layout_column);
patch!(enable_clash_fields);
patch!(test_list);
patch!(auto_log_clean);
patch!(window_size_position);
patch!(window_is_maximized);
}
/// 在初始化前尝试拿到单例端口的值
+12 -8
View File
@@ -20,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}\"")
}
}
@@ -44,7 +44,11 @@ pub struct DelayRes {
/// GET /proxies/{name}/delay
/// 获取代理延迟
pub async fn get_proxy_delay(name: String, test_url: Option<String>) -> Result<DelayRes> {
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");
@@ -57,7 +61,7 @@ pub async fn get_proxy_delay(name: String, test_url: Option<String>) -> Result<D
let builder = client
.get(&url)
.headers(headers)
.query(&[("timeout", "10000"), ("url", &test_url)]);
.query(&[("timeout", &format!("{timeout}")), ("url", &test_url)]);
let response = builder.send().await?;
Ok(response.json::<DelayRes>().await?)
@@ -83,12 +87,12 @@ fn clash_client_info() -> Result<(String, HeaderMap)> {
/// 缩短clash的日志
pub fn parse_log(log: String) -> String {
if log.starts_with("time=") && log.len() > 33 {
return (&log[33..]).to_owned();
return (log[33..]).to_owned();
}
if log.len() > 9 {
return (&log[9..]).to_owned();
return (log[9..]).to_owned();
}
return log;
log
}
/// 缩短clash -t的错误输出
@@ -105,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();
}
}
@@ -113,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
+28 -36
View File
@@ -1,3 +1,4 @@
use super::service;
use super::{clash_api, logger::Logger};
use crate::log_err;
use crate::{config::*, utils::dirs};
@@ -5,7 +6,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 +36,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 {
@@ -68,7 +69,7 @@ impl CoreManager {
if !output.status.success() {
let error = clash_api::parse_check_output(output.stdout.clone());
let error = match error.len() > 0 {
let error = match !error.is_empty() {
true => error,
false => output.stdout.clone(),
};
@@ -93,10 +94,9 @@ impl CoreManager {
None => false,
};
#[cfg(target_os = "windows")]
if *self.use_service_mode.lock() {
log::debug!(target: "app", "stop the core by service");
log_err!(super::win_service::stop_core_by_service().await);
log_err!(service::stop_core_by_service().await);
should_kill = true;
}
@@ -105,32 +105,27 @@ impl CoreManager {
sleep(Duration::from_millis(500)).await;
}
#[cfg(target_os = "windows")]
{
use super::win_service;
// 服务模式
let enable = { Config::verge().latest().enable_service_mode };
let enable = enable.unwrap_or(false);
// 服务模式
let enable = { Config::verge().latest().enable_service_mode.clone() };
let enable = enable.unwrap_or(false);
*self.use_service_mode.lock() = enable;
*self.use_service_mode.lock() = enable;
if enable {
// 服务模式启动失败就直接运行sidecar
log::debug!(target: "app", "try to run core in service mode");
if enable {
// 服务模式启动失败就直接运行sidecar
log::debug!(target: "app", "try to run core in service mode");
match (|| async {
win_service::check_service().await?;
win_service::run_core_by_service(&config_path).await
})()
.await
{
Ok(_) => return Ok(()),
Err(err) => {
// 修改这个值,免得stop出错
*self.use_service_mode.lock() = false;
log::error!(target: "app", "{err}");
}
match (|| async {
service::check_service().await?;
service::run_core_by_service(&config_path).await
})()
.await
{
Ok(_) => return Ok(()),
Err(err) => {
// 修改这个值,免得stop出错
*self.use_service_mode.lock() = false;
log::error!(target: "app", "{err}");
}
}
}
@@ -144,10 +139,9 @@ impl CoreManager {
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-alpha" => 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],
};
@@ -206,7 +200,6 @@ impl CoreManager {
/// 重启内核
pub fn recover_core(&'static self) -> Result<()> {
// 服务模式不管
#[cfg(target_os = "windows")]
if *self.use_service_mode.lock() {
return Ok(());
}
@@ -239,11 +232,10 @@ impl CoreManager {
/// 停止核心运行
pub fn stop_core(&self) -> Result<()> {
#[cfg(target_os = "windows")]
if *self.use_service_mode.lock() {
log::debug!(target: "app", "stop the core by service");
tauri::async_runtime::block_on(async move {
log_err!(super::win_service::stop_core_by_service().await);
log_err!(service::stop_core_by_service().await);
});
return Ok(());
}
@@ -259,7 +251,7 @@ 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; 3] = ["clash", "clash-meta", "clash-meta-alpha"];
const CLASH_CORES: [&str; 2] = ["clash-meta", "clash-meta-alpha"];
if !CLASH_CORES.contains(&clash_core.as_str()) {
bail!("invalid clash core name \"{clash_core}\"");
+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() {
+5 -10
View File
@@ -65,17 +65,12 @@ impl Hotkey {
}
let f = match func.trim() {
"open_dashboard" => || feat::open_dashboard(),
"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}\""),
};
@@ -86,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(())
}
@@ -110,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| {
+1 -36
View File
@@ -1,5 +1,3 @@
use std::borrow::Cow;
/// 给clash内核的tun模式授权
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn grant_permission(core: String) -> anyhow::Result<()> {
@@ -13,9 +11,6 @@ pub fn grant_permission(core: String) -> anyhow::Result<()> {
#[cfg(target_os = "macos")]
let output = {
// the path of clash /Applications/Clash Verge.app/Contents/MacOS/clash
// https://apple.stackexchange.com/questions/82967/problem-with-empty-spaces-when-executing-shell-commands-in-applescript
// let path = escape(&path);
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"#);
@@ -27,7 +22,7 @@ pub fn grant_permission(core: String) -> anyhow::Result<()> {
#[cfg(target_os = "linux")]
let output = {
let path = path.replace(' ', "\\ "); // 避免路径中有空格
let shell = format!("setcap cap_net_bind_service,cap_net_admin=+ep {path}");
let shell = format!("setcap cap_net_bind_service,cap_net_admin,cap_dac_override=+ep {path}");
let sudo = match Command::new("which").arg("pkexec").output() {
Ok(output) => {
@@ -50,33 +45,3 @@ pub fn grant_permission(core: String) -> anyhow::Result<()> {
anyhow::bail!("{stderr}");
}
}
#[allow(unused)]
pub fn escape<'a>(text: &'a str) -> Cow<'a, str> {
let bytes = text.as_bytes();
let mut owned = None;
for pos in 0..bytes.len() {
let special = match bytes[pos] {
b' ' => Some(b' '),
_ => None,
};
if let Some(s) = special {
if owned.is_none() {
owned = Some(bytes[0..pos].to_owned());
}
owned.as_mut().unwrap().push(b'\\');
owned.as_mut().unwrap().push(b'\\');
owned.as_mut().unwrap().push(s);
} else if let Some(owned) = owned.as_mut() {
owned.push(bytes[pos]);
}
}
if let Some(owned) = owned {
unsafe { Cow::Owned(String::from_utf8_unchecked(owned)) }
} else {
unsafe { Cow::Borrowed(std::str::from_utf8_unchecked(bytes)) }
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ pub mod manager;
pub mod sysopt;
pub mod timer;
pub mod tray;
pub mod win_service;
pub mod service;
pub mod win_uwp;
pub use self::core::*;
@@ -1,18 +1,15 @@
#![cfg(target_os = "windows")]
use crate::config::Config;
use crate::utils::dirs;
use anyhow::{bail, Context, Result};
use deelevate::{PrivilegeLevel, Token};
use runas::Command as RunasCommand;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::os::windows::process::CommandExt;
use std::path::PathBuf;
use std::time::Duration;
use std::{env::current_exe, process::Command as StdCommand};
use tokio::time::sleep;
// Windows only
const SERVICE_URL: &str = "http://127.0.0.1:33211";
#[derive(Debug, Deserialize, Serialize, Clone)]
@@ -32,7 +29,13 @@ pub struct JsonResponse {
/// Install the Clash Verge Service
/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程
///
#[cfg(target_os = "windows")]
pub async fn install_service() -> Result<()> {
use deelevate::{PrivilegeLevel, Token};
use runas::Command as RunasCommand;
use std::os::windows::process::CommandExt;
let binary_path = dirs::service_path()?;
let install_path = binary_path.with_file_name("install-service.exe");
@@ -60,9 +63,69 @@ pub async fn install_service() -> Result<()> {
Ok(())
}
#[cfg(target_os = "linux")]
pub async fn install_service() -> Result<()> {
use users::get_effective_uid;
let binary_path = dirs::service_path()?;
let installer_path = binary_path.with_file_name("install-service");
if !installer_path.exists() {
bail!("installer not found");
}
let elevator = crate::utils::unix_helper::linux_elevator();
let status = match get_effective_uid() {
0 => StdCommand::new(installer_path).status()?,
_ => StdCommand::new(elevator)
.arg("sh")
.arg("-c")
.arg(installer_path)
.status()?,
};
if !status.success() {
bail!(
"failed to install service with status {}",
status.code().unwrap()
);
}
Ok(())
}
#[cfg(target_os = "macos")]
pub async fn install_service() -> Result<()> {
let binary_path = dirs::service_path()?;
let installer_path = binary_path.with_file_name("install-service");
if !installer_path.exists() {
bail!("installer not found");
}
let shell = installer_path.to_string_lossy().replace(" ", "\\\\ ");
let command = format!(r#"do shell script "{shell}" with administrator privileges"#);
let status = StdCommand::new("osascript")
.args(vec!["-e", &command])
.status()?;
if !status.success() {
bail!(
"failed to install service with status {}",
status.code().unwrap()
);
}
Ok(())
}
/// Uninstall the Clash Verge Service
/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程
#[cfg(target_os = "windows")]
pub async fn uninstall_service() -> Result<()> {
use deelevate::{PrivilegeLevel, Token};
use runas::Command as RunasCommand;
use std::os::windows::process::CommandExt;
let binary_path = dirs::service_path()?;
let uninstall_path = binary_path.with_file_name("uninstall-service.exe");
@@ -90,6 +153,63 @@ pub async fn uninstall_service() -> Result<()> {
Ok(())
}
#[cfg(target_os = "linux")]
pub async fn uninstall_service() -> Result<()> {
use users::get_effective_uid;
let binary_path = dirs::service_path()?;
let uninstaller_path = binary_path.with_file_name("uninstall-service");
if !uninstaller_path.exists() {
bail!("uninstaller not found");
}
let elevator = crate::utils::unix_helper::linux_elevator();
let status = match get_effective_uid() {
0 => StdCommand::new(uninstaller_path).status()?,
_ => StdCommand::new(elevator)
.arg("sh")
.arg("-c")
.arg(uninstaller_path)
.status()?,
};
if !status.success() {
bail!(
"failed to install service with status {}",
status.code().unwrap()
);
}
Ok(())
}
#[cfg(target_os = "macos")]
pub async fn uninstall_service() -> Result<()> {
let binary_path = dirs::service_path()?;
let uninstaller_path = binary_path.with_file_name("uninstall-service");
if !uninstaller_path.exists() {
bail!("uninstaller not found");
}
let shell = uninstaller_path.to_string_lossy().replace(" ", "\\\\ ");
let command = format!(r#"do shell script "{shell}" with administrator privileges"#);
let status = StdCommand::new("osascript")
.args(vec!["-e", &command])
.status()?;
if !status.success() {
bail!(
"failed to install service with status {}",
status.code().unwrap()
);
}
Ok(())
}
/// check the windows service status
pub async fn check_service() -> Result<JsonResponse> {
let url = format!("{SERVICE_URL}/get_clash");
@@ -119,7 +239,8 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
let clash_core = { Config::verge().latest().clash_core.clone() };
let clash_core = clash_core.unwrap_or("clash".into());
let clash_bin = format!("{clash_core}.exe");
let bin_ext = if cfg!(windows) { ".exe" } else { "" };
let clash_bin = format!("{clash_core}{bin_ext}");
let bin_path = current_exe()?.with_file_name(clash_bin);
let bin_path = dirs::path_to_str(&bin_path)?;
+16 -34
View File
@@ -3,9 +3,10 @@ use anyhow::{anyhow, Result};
use auto_launch::{AutoLaunch, AutoLaunchBuilder};
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use std::env::current_exe;
use std::sync::Arc;
use sysproxy::Sysproxy;
use tauri::{async_runtime::Mutex as TokioMutex, utils::platform::current_exe};
use tauri::async_runtime::Mutex as TokioMutex;
pub struct Sysopt {
/// current system proxy setting
@@ -53,7 +54,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(),
)
};
@@ -66,7 +67,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;
@@ -93,7 +94,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(),
)
};
@@ -102,6 +103,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);
@@ -142,11 +149,8 @@ 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_exe = dunce::canonicalize(app_exe)?;
let app_name = app_exe
.file_stem()
.and_then(|f| f.to_str())
@@ -198,28 +202,6 @@ impl Sysopt {
.set_app_path(&app_path)
.build()?;
// 避免在开发时将自启动关了
#[cfg(feature = "verge-dev")]
if !enable {
return Ok(());
}
#[cfg(target_os = "macos")]
{
if enable && !auto.is_enabled().unwrap_or(false) {
// 避免重复设置登录项
let _ = auto.disable();
auto.enable()?;
} else if !enable {
let _ = auto.disable();
}
}
#[cfg(not(target_os = "macos"))]
if enable {
auto.enable()?;
}
*self.auto_launch.lock() = Some(auto);
Ok(())
@@ -233,7 +215,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();
@@ -271,9 +253,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(),
)
};
+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(())
}
+142 -47
View File
@@ -1,4 +1,9 @@
use crate::{cmds, 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,
@@ -41,10 +46,6 @@ impl Tray {
"direct_mode",
t!("Direct Mode", "直连模式"),
))
.add_item(CustomMenuItem::new(
"script_mode",
t!("Script Mode", "脚本模式"),
))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new(
"system_proxy",
@@ -52,16 +53,8 @@ impl Tray {
))
.add_item(CustomMenuItem::new("tun_mode", t!("TUN Mode", "Tun 模式")))
.add_item(CustomMenuItem::new(
"copy_env_sh",
t!("Copy Env (sh)", "复制环境变量(sh)"),
))
.add_item(CustomMenuItem::new(
"copy_env_cmd",
t!("Copy Env (CMD)", "复制环境变量(CMD)"),
))
.add_item(CustomMenuItem::new(
"copy_env_ps",
t!("Copy Env (PS)", "复制环境变量(PS)"),
"copy_env",
t!("Copy Env", "复制环境变量"),
))
.add_submenu(SystemTraySubmenu::new(
t!("Open Dir", "打开目录"),
@@ -136,30 +129,130 @@ 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");
#[cfg(target_os = "linux")]
match mode.as_str() {
"rule" => {
let _ = tray
.get_item("rule_mode")
.set_title(t!("Rule Mode ✔", "规则模式 ✔"));
let _ = tray
.get_item("global_mode")
.set_title(t!("Global Mode", "全局模式"));
let _ = tray
.get_item("direct_mode")
.set_title(t!("Direct Mode", "直连模式"));
}
"global" => {
let _ = tray
.get_item("rule_mode")
.set_title(t!("Rule Mode", "规则模式"));
let _ = tray
.get_item("global_mode")
.set_title(t!("Global Mode ✔", "全局模式 ✔"));
let _ = tray
.get_item("direct_mode")
.set_title(t!("Direct Mode", "直连模式"));
}
"direct" => {
let _ = tray
.get_item("rule_mode")
.set_title(t!("Rule Mode", "规则模式"));
let _ = tray
.get_item("global_mode")
.set_title(t!("Global Mode", "全局模式"));
let _ = tray
.get_item("direct_mode")
.set_title(t!("Direct Mode ✔", "直连模式 ✔"));
}
_ => {}
}
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);
#[cfg(target_os = "windows")]
{
let mut indication_icon = if *system_proxy {
include_bytes!("../../icons/win-tray-icon-activated.png").to_vec()
} else {
include_bytes!("../../icons/win-tray-icon.png").to_vec()
};
if *tun_mode {
indication_icon = include_bytes!("../../icons/win-tray-icon-tun.png").to_vec();
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 icon_dir_path = dirs::app_home_dir()?.join("icons");
let png_path = icon_dir_path.join("sysproxy.png");
let ico_path = icon_dir_path.join("sysproxy.ico");
if ico_path.exists() {
icon = std::fs::read(ico_path).unwrap();
} else if png_path.exists() {
icon = std::fs::read(png_path).unwrap();
}
}
icon
} else {
#[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 icon_dir_path = dirs::app_home_dir()?.join("icons");
let png_path = icon_dir_path.join("common.png");
let ico_path = icon_dir_path.join("common.ico");
if ico_path.exists() {
icon = std::fs::read(ico_path).unwrap();
} else if png_path.exists() {
icon = std::fs::read(png_path).unwrap();
}
}
icon
};
let _ = tray.set_icon(tauri::Icon::Raw(indication_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 icon_dir_path = dirs::app_home_dir()?.join("icons");
let png_path = icon_dir_path.join("tun.png");
let ico_path = icon_dir_path.join("tun.ico");
if ico_path.exists() {
icon = std::fs::read(ico_path).unwrap();
} else if png_path.exists() {
icon = std::fs::read(png_path).unwrap();
}
}
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);
#[cfg(target_os = "linux")]
{
if *system_proxy {
let _ = tray
.get_item("system_proxy")
.set_title(t!("System Proxy ✔", "系统代理 ✔"));
} else {
let _ = tray
.get_item("system_proxy")
.set_title(t!("System Proxy", "系统代理"));
}
if *tun_mode {
let _ = tray
.get_item("tun_mode")
.set_title(t!("TUN Mode ✔", "Tun 模式 ✔"));
} else {
let _ = tray
.get_item("tun_mode")
.set_title(t!("TUN Mode", "Tun 模式"));
}
}
let switch_map = {
let mut map = std::collections::HashMap::new();
@@ -168,19 +261,30 @@ impl Tray {
map
};
#[cfg(not(target_os = "linux"))]
let mut current_profile_name = "None".to_string();
let profiles = Config::profiles();
let profiles = profiles.latest();
if let Some(current_profile_uid) = profiles.get_current() {
let current_profile = profiles.get_item(&current_profile_uid);
current_profile_name = match &current_profile.unwrap().name {
Some(profile_name) => profile_name.to_string(),
None => current_profile_name,
};
};
let _ = tray.set_tooltip(&format!(
"Clash Verge {version}\n{}: {}\n{}: {}",
"Clash Verge {version}\n{}: {}\n{}: {}\n{}: {}",
t!("System Proxy", "系统代理"),
switch_map[system_proxy],
t!("TUN Mode", "Tun 模式"),
switch_map[tun_mode]
switch_map[tun_mode],
t!("Curent Profile", "当前订阅"),
current_profile_name
));
Ok(())
}
pub fn on_left_click(app_handle: &AppHandle) {
pub fn on_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() {
@@ -193,35 +297,26 @@ impl Tray {
pub fn on_system_tray_event(app_handle: &AppHandle, event: SystemTrayEvent) {
match event {
#[cfg(not(target_os = "linux"))]
SystemTrayEvent::LeftClick { .. } => Tray::on_left_click(app_handle),
#[cfg(not(target_os = "macos"))]
SystemTrayEvent::LeftClick { .. } => Tray::on_click(app_handle),
#[cfg(target_os = "macos")]
SystemTrayEvent::RightClick { .. } => Tray::on_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_sh" => feat::copy_clash_env(app_handle, "sh"),
#[cfg(target_os = "windows")]
"copy_env_cmd" => feat::copy_clash_env(app_handle, "cmd"),
#[cfg(target_os = "windows")]
"copy_env_ps" => feat::copy_clash_env(app_handle, "ps"),
"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" => {
let _ = resolve::save_window_size_position(app_handle, true);
"quit" => cmds::exit_app(app_handle.clone()),
resolve::resolve_reset();
api::process::kill_children();
app_handle.exit(0);
std::process::exit(0);
}
_ => {}
},
_ => {}
+4 -5
View File
@@ -7,8 +7,8 @@ use runas::Command as RunasCommand;
use std::process::Command as StdCommand;
pub async fn invoke_uwptools() -> Result<()> {
let binary_path = dirs::service_path()?;
let tool_path = binary_path.with_file_name("enableLoopback.exe");
let resource_dir = dirs::app_resources_dir()?;
let tool_path = resource_dir.join("enableLoopback.exe");
if !tool_path.exists() {
bail!("enableLoopback exe not found");
@@ -17,10 +17,9 @@ pub async fn invoke_uwptools() -> Result<()> {
let token = Token::with_current_process()?;
let level = token.privilege_level()?;
match level {
match level {
PrivilegeLevel::NotPrivileged => RunasCommand::new(tool_path).status()?,
_ => StdCommand::new(tool_path)
.status()?,
_ => StdCommand::new(tool_path).status()?,
};
Ok(())
+4 -4
View File
@@ -1,6 +1,6 @@
function main(params) {
if (params.mode === "script") {
params.mode = "rule";
function main(config) {
if (config.mode === "script") {
config.mode = "rule";
}
return params;
return config;
}
@@ -1,10 +1,10 @@
function main(params) {
if (Array.isArray(params.proxies)) {
params.proxies.forEach((p, i) => {
function main(config) {
if (Array.isArray(config.proxies)) {
config.proxies.forEach((p, i) => {
if (p.type === "hysteria" && typeof p.alpn === "string") {
params.proxies[i].alpn = [p.alpn];
config.proxies[i].alpn = [p.alpn];
}
});
}
return params;
return config;
}
+34 -95
View File
@@ -1,11 +1,13 @@
use serde_yaml::{Mapping, Value};
use std::collections::HashSet;
pub const HANDLE_FIELDS: [&str; 9] = [
pub const HANDLE_FIELDS: [&str; 11] = [
"mode",
"port",
"socks-port",
"redir-port",
"tproxy-port",
"mixed-port",
"socks-port",
"port",
"allow-lan",
"log-level",
"ipv6",
@@ -15,71 +17,13 @@ pub const HANDLE_FIELDS: [&str; 9] = [
pub const DEFAULT_FIELDS: [&str; 5] = [
"proxies",
"proxy-groups",
"proxy-providers",
"rules",
"proxy-groups",
"rule-providers",
"rules",
];
pub const OTHERS_FIELDS: [&str; 31] = [
"dns",
"tun",
"ebpf",
"hosts",
"script",
"profile",
"payload",
"tunnels",
"auto-redir",
"experimental",
"interface-name",
"routing-mark",
"redir-port",
"tproxy-port",
"iptables",
"external-ui",
"bind-address",
"authentication",
"tls", // meta
"sniffer", // meta
"geox-url", // meta
"listeners", // meta
"sub-rules", // meta
"geodata-mode", // meta
"unified-delay", // meta
"tcp-concurrent", // meta
"enable-process", // meta
"find-process-mode", // meta
"skip-auth-prefixes", // meta
"external-controller-tls", // meta
"global-client-fingerprint", // 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>, enable: bool) -> Mapping {
if !enable {
return config;
}
pub fn use_filter(config: Mapping, filter: &Vec<String>) -> Mapping {
let mut ret = Mapping::new();
for (key, value) in config.into_iter() {
@@ -105,40 +49,35 @@ pub fn use_lowercase(config: Mapping) -> Mapping {
ret
}
pub fn use_sort(config: Mapping, enable_filter: bool) -> 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();
if !enable_filter {
let supported_keys: HashSet<&str> = HANDLE_FIELDS
.into_iter()
.chain(OTHERS_FIELDS)
.chain(DEFAULT_FIELDS)
.collect();
let config_keys: HashSet<&str> = config
.keys()
.filter_map(|e| e.as_str())
.into_iter()
.collect();
config_keys.difference(&supported_keys).for_each(|&key| {
let key = Value::from(key);
config.get(&key).map(|value| {
ret.insert(key, value.clone());
});
});
}
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
}
@@ -150,7 +89,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()
}
+23 -9
View File
@@ -10,16 +10,28 @@ const MERGE_FIELDS: [&str; 6] = [
"append-proxy-groups",
];
pub fn use_merge(merge: Mapping, mut config: Mapping) -> Mapping {
// 直接覆盖原字段
use_lowercase(merge.clone())
.into_iter()
.for_each(|(key, value)| {
config.insert(key, value);
});
fn deep_merge(a: &mut Value, b: &Value) {
match (a, b) {
(&mut Value::Mapping(ref mut a), &Value::Mapping(ref b)) => {
for (k, v) in b {
deep_merge(a.entry(k.clone()).or_insert(Value::Null), v);
}
}
(a, b) => *a = b.clone(),
}
}
pub fn use_merge(merge: Mapping, config: Mapping) -> Mapping {
let mut config = Value::from(config);
let mut merge_without_append = use_lowercase(merge.clone());
for key in MERGE_FIELDS {
merge_without_append.remove(key).unwrap_or_default();
}
deep_merge(&mut config, &Value::from(merge_without_append));
let mut config = config.as_mapping().unwrap().clone();
let merge_list = MERGE_FIELDS.iter().map(|s| s.to_string());
let merge = use_filter(merge, &merge_list.collect(), true);
let merge = use_filter(merge, &merge_list.collect());
["rules", "proxies", "proxy-groups"]
.iter()
@@ -48,7 +60,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
}
+25 -25
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::*;
@@ -23,50 +22,43 @@ pub fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
// config.yaml 的订阅
let clash_config = { Config::clash().latest().0.clone() };
let (clash_core, enable_tun, enable_builtin, enable_filter) = {
let (clash_core, enable_tun, enable_builtin) = {
let verge = Config::verge();
let verge = verge.latest();
(
verge.clash_core.clone(),
verge.enable_tun_mode.clone().unwrap_or(false),
verge.enable_builtin_enhanced.clone().unwrap_or(true),
verge.enable_clash_fields.clone().unwrap_or(true),
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, enable_filter);
// 处理用户的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, enable_filter);
}
ChainType::Script(script) => {
let mut logs = vec![];
@@ -74,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, enable_filter);
config = res_config;
logs.extend(res_logs);
}
Err(err) => logs.push(("exception".into(), err.to_string())),
@@ -86,11 +78,20 @@ 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 {
ChainItem::builtin()
@@ -103,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, enable_filter);
config = res_config;
}
Err(err) => {
log::error!(target: "app", "builtin script error `{err}`");
@@ -114,12 +115,11 @@ pub fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
});
}
config = use_filter(config, &clash_fields, enable_filter);
config = use_tun(config, enable_tun);
config = use_sort(config, enable_filter);
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)
+44 -31
View File
@@ -1,62 +1,75 @@
use super::use_lowercase;
use anyhow::Result;
use anyhow::{Error, 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 boa_engine::{native_function::NativeFunction, Context, JsValue, Source};
use std::sync::{Arc, Mutex};
let mut context = Context::default();
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({
unsafe {
let _ = context.register_global_builtin_callable(
"__verge_log__".into(),
2,
NativeFunction::from_closure(
move |_: &JsValue, args: &[JsValue], context: &mut Context| {
let level = args.get(0).unwrap().to_string(context)?;
let level = level.to_std_string().unwrap();
let data = args.get(1).unwrap().to_string(context)?;
let data = data.to_std_string().unwrap();
let mut out = copy_outputs.lock().unwrap();
out.push((level, data));
Ok(JsValue::undefined())
},
),
);
}
let _ = context.eval(Source::from_bytes(
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 config = use_lowercase(config.clone());
let config_str = serde_json::to_string(&config)?;
let code = format!(
r#"try{{
let code = format!(
r#"try{{
{script};
JSON.stringify(main({config_str})||'')
}} catch(err) {{
`__error_flag__ ${{err.toString()}}`
}}"#
);
let result: String = ctx.eval(code.as_str())?;
);
if let Ok(result) = context.eval(Source::from_bytes(code.as_str())) {
if !result.is_string() {
anyhow::bail!("main function should return object");
}
let result = result.to_string(&mut context).unwrap();
let result = result.to_std_string().unwrap();
if result.starts_with("__error_flag__") {
anyhow::bail!(result[15..].to_owned());
}
if result == "\"\"" {
anyhow::bail!("main function should return object");
}
return 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()))
let res: Result<Mapping, Error> = Ok(serde_json::from_str::<Mapping>(result.as_str())?);
let mut out = outputs.lock().unwrap();
match res {
Ok(config) => Ok((use_lowercase(config), out.to_vec())),
Err(err) => {
out.push(("exception".into(), err.to_string()));
Ok((config, out.to_vec()))
}
}
} else {
anyhow::bail!("main function should return object");
}
}
+52 -6
View File
@@ -30,18 +30,64 @@ 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);
if enable {
#[cfg(target_os = "macos")]
{
use crate::utils::dirs;
use tauri::api::process::Command;
log::info!(target: "app", "try to set system dns");
let resource_dir = dirs::app_resources_dir().unwrap();
let script = resource_dir.join("set_dns.sh");
let script = script.to_string_lossy();
match Command::new("bash")
.args([script])
.current_dir(resource_dir)
.status()
{
Ok(status) => {
if status.success() {
log::info!(target: "app", "set system dns successfully");
} else {
let code = status.code().unwrap_or(-1);
log::error!(target: "app", "set system dns failed: {code}");
}
}
Err(err) => {
log::error!(target: "app", "set system dns failed: {err}");
}
}
}
use_dns_for_tun(config)
} else {
#[cfg(target_os = "macos")]
{
use crate::utils::dirs;
use tauri::api::process::Command;
log::info!(target: "app", "try to unset system dns");
let resource_dir = dirs::app_resources_dir().unwrap();
let script = resource_dir.join("unset_dns.sh");
let script = script.to_string_lossy();
match Command::new("bash")
.args([script])
.current_dir(resource_dir)
.status()
{
Ok(status) => {
if status.success() {
log::info!(target: "app", "unset system dns successfully");
} else {
let code = status.code().unwrap_or(-1);
log::error!(target: "app", "unset system dns failed: {code}");
}
}
Err(err) => {
log::error!(target: "app", "unset system dns failed: {err}");
}
}
}
config
}
}
+97 -90
View File
@@ -10,13 +10,19 @@ use crate::log_err;
use crate::utils::resolve;
use anyhow::{bail, Result};
use serde_yaml::{Mapping, Value};
use tauri::{AppHandle, ClipboardManager};
use tauri::{AppHandle, ClipboardManager, Manager};
// 打开面板
pub fn open_dashboard() {
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);
}
}
@@ -62,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 {
@@ -78,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 {
@@ -126,52 +102,26 @@ 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的订阅
pub async fn patch_clash(patch: Mapping) -> Result<()> {
Config::clash().draft().patch_config(patch.clone());
match {
let redir_port = patch.get("redir-port");
let tproxy_port = patch.get("tproxy-port");
let mixed_port = patch.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.clone().unwrap()
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");
@@ -181,7 +131,11 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> {
};
// 激活订阅
if mixed_port.is_some()
if redir_port.is_some()
|| tproxy_port.is_some()
|| mixed_port.is_some()
|| socks_port.is_some()
|| port.is_some()
|| patch.get("secret").is_some()
|| patch.get("external-controller").is_some()
{
@@ -225,31 +179,27 @@ 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")]
{
let service_mode = patch.enable_service_mode;
let service_mode = patch.enable_service_mode;
if service_mode.is_some() {
log::debug!(target: "app", "change service mode to {}", service_mode.unwrap());
if service_mode.is_some() {
log::debug!(target: "app", "change service mode to {}", service_mode.unwrap());
Config::generate()?;
CoreManager::global().run_core().await?;
} else if tun_mode.is_some() {
update_core_config().await?;
}
}
#[cfg(not(target_os = "windows"))]
if tun_mode.is_some() {
Config::generate()?;
CoreManager::global().run_core().await?;
} else if tun_mode.is_some() {
update_core_config().await?;
}
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();
}
@@ -264,7 +214,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()?;
}
@@ -337,21 +292,73 @@ async fn update_core_config() -> Result<()> {
}
/// copy env variable
pub fn copy_clash_env(app_handle: &AppHandle, option: &str) {
let port = { Config::verge().latest().verge_mixed_port.unwrap_or(7890) };
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 cmd: String = format!("set http_proxy={http_proxy}\r\nset 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();
match option {
"sh" => cliboard.write_text(sh).unwrap_or_default(),
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(),
"ps" => cliboard.write_text(ps).unwrap_or_default(),
_ => log::error!(target: "app", "copy_clash_env: Invalid option! {option}"),
"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)
}
}
+19 -23
View File
@@ -20,12 +20,18 @@ fn main() -> std::io::Result<()> {
return Ok(());
}
#[cfg(target_os = "linux")]
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
crate::log_err!(init::init_config());
#[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
@@ -34,6 +40,7 @@ fn main() -> std::io::Result<()> {
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,
@@ -50,6 +57,12 @@ fn main() -> std::io::Result<()> {
// verge
cmds::get_verge_config,
cmds::patch_verge_config,
cmds::test_delay,
cmds::get_app_dir,
cmds::copy_icon_file,
cmds::download_icon_cache,
cmds::open_devtools,
cmds::exit_app,
// cmds::update_hotkeys,
// profile
cmds::get_profiles,
@@ -100,38 +113,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();
let _ = resolve::save_window_size_position(&app_handle, true);
app_handle.get_window("main").map(|win| {
let _ = win.hide();
});
tauri::WindowEvent::Destroyed => {
let _ = resolve::save_window_size_position(app_handle, true);
}
_ => {}
}
}
}
#[cfg(not(target_os = "macos"))]
tauri::RunEvent::WindowEvent { label, event, .. } => {
if label == "main" {
match event {
tauri::WindowEvent::CloseRequested { .. } => {
let _ = resolve::save_window_size_position(&app_handle, true);
let _ = resolve::save_window_size_position(app_handle, true);
}
tauri::WindowEvent::Moved(_) | tauri::WindowEvent::Resized(_) => {
let _ = resolve::save_window_size_position(&app_handle, false);
let _ = resolve::save_window_size_position(app_handle, false);
}
_ => {}
}
+42 -76
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.2.0";
/// 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,35 +88,20 @@ pub fn profiles_path() -> Result<PathBuf> {
Ok(app_home_dir()?.join(PROFILE_YAML))
}
#[allow(unused)]
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> {
Ok(app_home_dir()?.join("clash.pid"))
}
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"))
}
#[cfg(not(target_os = "windows"))]
pub fn service_path() -> Result<PathBuf> {
Ok(app_resources_dir()?.join("clash-verge-service"))
}
#[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)]
pub fn service_log_file() -> Result<PathBuf> {
use chrono::Local;
+41 -25
View File
@@ -3,6 +3,10 @@ use nanoid::nanoid;
use serde::{de::DeserializeOwned, Serialize};
use serde_yaml::{Mapping, Value};
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,7 +14,7 @@ pub fn read_yaml<T: DeserializeOwned>(path: &PathBuf) -> Result<T> {
bail!("file not found \"{}\"", path.display());
}
let yaml_str = fs::read_to_string(&path)
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).with_context(|| {
@@ -67,32 +71,44 @@ 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<()> {
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";
// use vscode first
if let Err(err) = open::with(&path, code) {
log::error!(target: "app", "failed to open file with VScode `{err}`");
// default open
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)
}
};
Ok(())
}
@@ -156,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);
}
+101 -14
View File
@@ -8,8 +8,9 @@ use log4rs::append::file::FileAppender;
use log4rs::config::{Appender, Logger, Root};
use log4rs::encode::pattern::PatternEncoder;
use std::fs::{self, DirEntry};
use std::path::PathBuf;
use std::str::FromStr;
use tauri::PackageInfo;
use tauri::api::process::Command;
/// initialize this instance's log file
fn init_log() -> Result<()> {
@@ -80,7 +81,7 @@ pub fn delete_log() -> Result<()> {
let auto_log_clean = {
let verge = Config::verge();
let verge = verge.data();
verge.auto_log_clean.clone().unwrap_or(0)
verge.auto_log_clean.unwrap_or(0)
};
let day = match auto_log_clean {
@@ -116,7 +117,10 @@ pub fn delete_log() -> Result<()> {
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 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 {
@@ -128,22 +132,22 @@ pub fn delete_log() -> Result<()> {
Ok(())
};
for file in fs::read_dir(&log_dir)? {
if let Ok(file) = file {
let _ = process_file(file);
}
for file in fs::read_dir(&log_dir)?.flatten() {
let _ = process_file(file);
}
let service_log_dir = log_dir.join("service");
for file in fs::read_dir(&service_log_dir)?.flatten() {
let _ = process_file(file);
}
Ok(())
}
/// Initialize all the config files
/// before tauri setup
pub fn init_config() -> Result<()> {
#[cfg(target_os = "windows")]
unsafe {
let _ = dirs::init_portable_flag();
}
let _ = dirs::init_portable_flag();
let _ = init_log();
let _ = delete_log();
@@ -185,9 +189,9 @@ pub fn init_config() -> Result<()> {
/// initialize app resources
/// after tauri setup
pub fn init_resources(package_info: &PackageInfo) -> Result<()> {
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);
@@ -241,3 +245,86 @@ pub fn init_resources(package_info: &PackageInfo) -> Result<()> {
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 = "powershell";
}
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 -1
View File
@@ -4,4 +4,4 @@ pub mod init;
pub mod resolve;
pub mod server;
pub mod tmpl;
// mod winhelp;
pub mod unix_helper;
+113 -73
View File
@@ -1,10 +1,20 @@
use crate::config::IVerge;
use crate::{config::Config, 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") {
@@ -27,11 +37,13 @@ pub fn find_unused_port() -> Result<u16> {
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);
@@ -73,7 +85,7 @@ pub fn resolve_setup(app: &mut App) {
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());
}
@@ -84,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
@@ -107,6 +126,7 @@ pub fn create_window(app_handle: &AppHandle) {
tauri::WindowUrl::App("index.html".into()),
)
.title("Clash Verge")
.visible(false)
.fullscreen(false)
.min_inner_size(600.0, 520.0);
@@ -135,75 +155,70 @@ pub fn create_window(app_handle: &AppHandle) {
}
}
};
#[cfg(target_os = "windows")]
{
use std::time::Duration;
use tokio::time::sleep;
use window_shadows::set_shadow;
match builder
.decorations(false)
.transparent(true)
.visible(false)
.build()
{
Ok(win) => {
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).try_into()?
|| pos.y < -200
|| pos.y > (size.height - 200).try_into()?
{
center = true;
}
Ok(center)
})();
if center.unwrap_or(true) {
trace_err!(win.center(), "set win center");
}
log::trace!("try to create window");
let app_handle = app_handle.clone();
// 加点延迟避免界面闪一下
tauri::async_runtime::spawn(async move {
sleep(Duration::from_millis(888)).await;
if let Some(window) = app_handle.get_window("main") {
trace_err!(set_shadow(&window, true), "set win shadow");
trace_err!(window.show(), "set win visible");
trace_err!(window.unminimize(), "set win unminimize");
trace_err!(window.set_focus(), "set win focus");
} else {
log::error!(target: "app", "failed to create window, get_window is None")
}
});
}
Err(err) => log::error!(target: "app", "failed to create window, {err}"),
}
}
let window = builder
.decorations(false)
.additional_browser_args("--enable-features=msWebView2EnableDraggableRegions --disable-features=OverscrollHistoryNavigation,msExperimentalScrolling")
.transparent(true)
.visible(false)
.build();
#[cfg(target_os = "macos")]
crate::log_err!(builder
let window = builder
.decorations(true)
.hidden_title(true)
.title_bar_style(tauri::TitleBarStyle::Overlay)
.build());
.build();
#[cfg(target_os = "linux")]
crate::log_err!(builder.decorations(true).transparent(false).build());
let window = builder.decorations(false).transparent(true).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"))?;
@@ -213,14 +228,39 @@ pub fn save_window_size_position(app_handle: &AppHandle, save_to_file: bool) ->
let size = size.to_logical::<f64>(scale);
let pos = win.outer_position()?;
let pos = pos.to_logical::<f64>(scale);
let verge = Config::verge();
let mut verge = verge.latest();
verge.window_size_position = Some(vec![size.width, size.height, pos.x, pos.y]);
if save_to_file {
verge.save_file()?;
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;
});
}
+15 -16
View File
@@ -1,36 +1,35 @@
///! Some config file template
//! Some config file template
/// template for new a profile item
pub const ITEM_LOCAL: &str = "# Profile Template for clash verge
pub const ITEM_LOCAL: &str = "# Profile Template for Clash Verge
proxies:
proxies: []
proxy-groups:
proxy-groups: []
rules:
rules: []
";
/// enhanced profile
pub const ITEM_MERGE: &str = "# Merge Template for clash verge
# The `Merge` format used to enhance profile
pub const ITEM_MERGE: &str = "# Profile Enhancement Merge Template for Clash Verge
prepend-rules:
prepend-rules: []
prepend-proxies:
prepend-proxies: []
prepend-proxy-groups:
prepend-proxy-groups: []
append-rules:
append-rules: []
append-proxies:
append-proxies: []
append-proxy-groups:
append-proxy-groups: []
";
/// enhanced profile
pub const ITEM_SCRIPT: &str = "// Define the `main` function
pub const ITEM_SCRIPT: &str = "// Define main function (script entry)
function main(params) {
return params;
function main(config) {
return config;
}
";
+14
View File
@@ -0,0 +1,14 @@
#[cfg(target_os = "linux")]
pub fn linux_elevator() -> &'static str {
use std::process::Command;
match Command::new("which").arg("pkexec").output() {
Ok(output) => {
if output.stdout.is_empty() {
"sudo"
} else {
"pkexec"
}
}
Err(_) => "sudo",
}
}
-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());
}
+25 -28
View File
@@ -1,7 +1,8 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"package": {
"productName": "Clash Verge",
"version": "1.4.2"
"version": "1.6.2"
},
"build": {
"distDir": "../dist",
@@ -10,14 +11,9 @@
"beforeBuildCommand": "pnpm run web:build"
},
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.ico",
"iconAsTemplate": true
},
"bundle": {
"active": true,
"targets": "all",
"identifier": "moe.elaina.clash.verge",
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
@@ -29,26 +25,8 @@
"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": ["openssl"]
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"wix": {
"language": ["zh-CN", "en-US", "ru-RU"]
}
}
"shortDescription": "A Clash Meta GUI based on tauri.",
"longDescription": "A Clash Meta GUI based on tauri."
},
"updater": {
"active": true,
@@ -74,11 +52,30 @@
},
"clipboard": {
"all": true
},
"notification": {
"all": true
},
"dialog": {
"all": false,
"open": true
},
"protocol": {
"asset": true,
"assetScope": ["$APPDATA/**", "$RESOURCE/../**"]
},
"path": {
"all": true
},
"fs": {
"exists": true,
"readFile": true,
"scope": ["$APPDATA/**", "$RESOURCE/../**", "**"]
}
},
"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';"
}
}
}
+16
View File
@@ -0,0 +1,16 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.png"
},
"bundle": {
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"targets": ["deb", "appimage", "updater"],
"deb": {
"depends": ["openssl"],
"desktopTemplate": "./template/clash-verge.desktop"
}
}
}
}
+20
View File
@@ -0,0 +1,20 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": {
"systemTray": {
"iconPath": "icons/mac-tray-icon.png",
"iconAsTemplate": true
},
"bundle": {
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"targets": ["app", "dmg", "updater"],
"macOS": {
"frameworks": [],
"minimumSystemVersion": "10.15",
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
}
}
}
}
+29
View File
@@ -0,0 +1,29 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.png"
},
"bundle": {
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"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;
+852
View File
@@ -0,0 +1,852 @@
; 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
Function un.isDirectoryEmpty
Exch $0
Push $1
Push $2
StrCpy $2 0
FindFirst $1 $2 "$0\*.*"
loop:
StrCmp $2 "" done
StrCmp $2 "." next
StrCmp $2 ".." next
StrCpy $0 0
goto done
next:
FindNext $1 $2
goto loop
done:
FindClose $1
StrCmp $2 "" 0 +2
StrCpy $0 1
Pop $2
Pop $1
Exch $0
FunctionEnd
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 "$INSTDIR\resources"
; Delete external binaries
{{#each binaries}}
Delete "$INSTDIR\\{{this}}"
{{/each}}
; Delete uninstaller
Delete "$INSTDIR\uninstall.exe"
; Remove InstallDir
Push "$INSTDIR"
Call un.isDirectoryEmpty
Pop $0
${If} $0 == 1
RMDir /R /REBOOTOK "$INSTDIR"
${Else}
MessageBox MB_OK "Install Directory is not Empty, Please remove it manually."
${EndIf}
; 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
+38
View File
@@ -0,0 +1,38 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.png"
},
"bundle": {
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"targets": ["nsis", "updater"],
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"webviewInstallMode": {
"type": "fixedRuntime",
"path": "./Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.arm64/"
},
"nsis": {
"displayLanguageSelector": true,
"installerIcon": "icons/icon.ico",
"languages": ["SimpChinese", "English"],
"license": "../LICENSE",
"installMode": "perMachine",
"template": "./template/installer.nsi"
}
}
},
"updater": {
"active": true,
"dialog": false,
"endpoints": [
"https://mirror.ghproxy.com/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-fixed-webview2-proxy.json",
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-fixed-webview2.json"
],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEQyOEMyRjBCQkVGOUJEREYKUldUZnZmbStDeStNMHU5Mmo1N24xQXZwSVRYbXA2NUpzZE5oVzlqeS9Bc0t6RVV4MmtwVjBZaHgK"
}
}
}
+38
View File
@@ -0,0 +1,38 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.png"
},
"bundle": {
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"targets": ["nsis", "updater"],
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"webviewInstallMode": {
"type": "fixedRuntime",
"path": "./Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.x64/"
},
"nsis": {
"displayLanguageSelector": true,
"installerIcon": "icons/icon.ico",
"languages": ["SimpChinese", "English"],
"license": "../LICENSE",
"installMode": "perMachine",
"template": "./template/installer.nsi"
}
}
},
"updater": {
"active": true,
"dialog": false,
"endpoints": [
"https://mirror.ghproxy.com/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-fixed-webview2-proxy.json",
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-fixed-webview2.json"
],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEQyOEMyRjBCQkVGOUJEREYKUldUZnZmbStDeStNMHU5Mmo1N24xQXZwSVRYbXA2NUpzZE5oVzlqeS9Bc0t6RVV4MmtwVjBZaHgK"
}
}
}
+38
View File
@@ -0,0 +1,38 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.png"
},
"bundle": {
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"targets": ["nsis", "updater"],
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"webviewInstallMode": {
"type": "fixedRuntime",
"path": "./Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.x86/"
},
"nsis": {
"displayLanguageSelector": true,
"installerIcon": "icons/icon.ico",
"languages": ["SimpChinese", "English"],
"license": "../LICENSE",
"installMode": "perMachine",
"template": "./template/installer.nsi"
}
}
},
"updater": {
"active": true,
"dialog": false,
"endpoints": [
"https://mirror.ghproxy.com/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-fixed-webview2-proxy.json",
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-fixed-webview2.json"
],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEQyOEMyRjBCQkVGOUJEREYKUldUZnZmbStDeStNMHU5Mmo1N24xQXZwSVRYbXA2NUpzZE5oVzlqeS9Bc0t6RVV4MmtwVjBZaHgK"
}
}
}
+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_971_118)"/>
<path d="M17.9917 9.66675C13.3917 9.66675 9.66669 13.4001 9.66669 18.0001C9.66669 22.6001 13.3917 26.3334 17.9917 26.3334C22.6 26.3334 26.3334 22.6001 26.3334 18.0001C26.3334 13.4001 22.6 9.66675 17.9917 9.66675ZM23.7667 14.6667H21.3084C21.0417 13.6251 20.6584 12.6251 20.1584 11.7001C21.6917 12.2251 22.9667 13.2917 23.7667 14.6667ZM18 11.3667C18.6917 12.3667 19.2334 13.4751 19.5917 14.6667H16.4084C16.7667 13.4751 17.3084 12.3667 18 11.3667ZM11.55 19.6667C11.4167 19.1334 11.3334 18.5751 11.3334 18.0001C11.3334 17.4251 11.4167 16.8667 11.55 16.3334H14.3667C14.3 16.8834 14.25 17.4334 14.25 18.0001C14.25 18.5667 14.3 19.1167 14.3667 19.6667H11.55ZM12.2334 21.3334H14.6917C14.9584 22.3751 15.3417 23.3751 15.8417 24.3001C14.3084 23.7751 13.0334 22.7167 12.2334 21.3334ZM14.6917 14.6667H12.2334C13.0334 13.2834 14.3084 12.2251 15.8417 11.7001C15.3417 12.6251 14.9584 13.6251 14.6917 14.6667ZM18 24.6334C17.3084 23.6334 16.7667 22.5251 16.4084 21.3334H19.5917C19.2334 22.5251 18.6917 23.6334 18 24.6334ZM19.95 19.6667H16.05C15.975 19.1167 15.9167 18.5667 15.9167 18.0001C15.9167 17.4334 15.975 16.8751 16.05 16.3334H19.95C20.025 16.8751 20.0834 17.4334 20.0834 18.0001C20.0834 18.5667 20.025 19.1167 19.95 19.6667ZM20.1584 24.3001C20.6584 23.3751 21.0417 22.3751 21.3084 21.3334H23.7667C22.9667 22.7084 21.6917 23.7751 20.1584 24.3001ZM21.6334 19.6667C21.7 19.1167 21.75 18.5667 21.75 18.0001C21.75 17.4334 21.7 16.8834 21.6334 16.3334H24.45C24.5834 16.8667 24.6667 17.4251 24.6667 18.0001C24.6667 18.5751 24.5834 19.1334 24.45 19.6667H21.6334Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_971_118" 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: 1.9 KiB

+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_971_127)"/>
<path d="M18.8334 22.1667H12.1667C11.7084 22.1667 11.3334 22.5417 11.3334 23.0001C11.3334 23.4584 11.7084 23.8334 12.1667 23.8334H18.8334C19.2917 23.8334 19.6667 23.4584 19.6667 23.0001C19.6667 22.5417 19.2917 22.1667 18.8334 22.1667ZM23.8334 15.5001H12.1667C11.7084 15.5001 11.3334 15.8751 11.3334 16.3334C11.3334 16.7917 11.7084 17.1667 12.1667 17.1667H23.8334C24.2917 17.1667 24.6667 16.7917 24.6667 16.3334C24.6667 15.8751 24.2917 15.5001 23.8334 15.5001ZM12.1667 20.5001H23.8334C24.2917 20.5001 24.6667 20.1251 24.6667 19.6667C24.6667 19.2084 24.2917 18.8334 23.8334 18.8334H12.1667C11.7084 18.8334 11.3334 19.2084 11.3334 19.6667C11.3334 20.1251 11.7084 20.5001 12.1667 20.5001ZM11.3334 13.0001C11.3334 13.4584 11.7084 13.8334 12.1667 13.8334H23.8334C24.2917 13.8334 24.6667 13.4584 24.6667 13.0001C24.6667 12.5417 24.2917 12.1667 23.8334 12.1667H12.1667C11.7084 12.1667 11.3334 12.5417 11.3334 13.0001Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_971_127" 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: 1.3 KiB

+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_971_113)"/>
<path d="M23.8333 18.8333H12.1667C11.25 18.8333 10.5 19.5833 10.5 20.5V23.8333C10.5 24.75 11.25 25.5 12.1667 25.5H23.8333C24.75 25.5 25.5 24.75 25.5 23.8333V20.5C25.5 19.5833 24.75 18.8333 23.8333 18.8333ZM13.8333 23.8333C12.9167 23.8333 12.1667 23.0833 12.1667 22.1667C12.1667 21.25 12.9167 20.5 13.8333 20.5C14.75 20.5 15.5 21.25 15.5 22.1667C15.5 23.0833 14.75 23.8333 13.8333 23.8333ZM23.8333 10.5H12.1667C11.25 10.5 10.5 11.25 10.5 12.1667V15.5C10.5 16.4167 11.25 17.1667 12.1667 17.1667H23.8333C24.75 17.1667 25.5 16.4167 25.5 15.5V12.1667C25.5 11.25 24.75 10.5 23.8333 10.5ZM13.8333 15.5C12.9167 15.5 12.1667 14.75 12.1667 13.8333C12.1667 12.9167 12.9167 12.1667 13.8333 12.1667C14.75 12.1667 15.5 12.9167 15.5 13.8333C15.5 14.75 14.75 15.5 13.8333 15.5Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_971_113" x1="31" y1="27.5" x2="6.5" y2="7" gradientUnits="userSpaceOnUse">
<stop stop-color="#6038CB"/>
<stop offset="1" stop-color="#704ADC"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+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_971_108)"/>
<path d="M9.7167 16.3834C10.1417 16.8084 10.8167 16.85 11.275 16.4667C15.1667 13.2667 20.8167 13.2667 24.7167 16.4583C25.1834 16.8417 25.8667 16.8084 26.2917 16.3834C26.7834 15.8917 26.75 15.075 26.2084 14.6333C21.45 10.7417 14.5667 10.7417 9.80003 14.6333C9.25836 15.0667 9.2167 15.8834 9.7167 16.3834ZM16.1834 22.85L17.4084 24.075C17.7334 24.4 18.2584 24.4 18.5834 24.075L19.8084 22.85C20.2 22.4583 20.1167 21.7833 19.6167 21.525C18.6 21 17.3834 21 16.3584 21.525C15.8834 21.7833 15.7917 22.4583 16.1834 22.85ZM13.075 19.7417C13.4834 20.15 14.125 20.1917 14.6 19.85C16.6334 18.4084 19.3667 18.4084 21.4 19.85C21.875 20.1834 22.5167 20.15 22.925 19.7417L22.9334 19.7333C23.4334 19.2333 23.4 18.3834 22.825 17.975C19.9584 15.9 16.05 15.9 13.175 17.975C12.6 18.3917 12.5667 19.2333 13.075 19.7417Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_971_108" x1="31" y1="27.5" x2="6.5" y2="7" gradientUnits="userSpaceOnUse">
<stop stop-color="#21B2CB"/>
<stop offset="1" stop-color="#3EC5D2"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+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_971_122)"/>
<path d="M15.5 24.6666C15.5 25.125 15.875 25.5 16.3333 25.5C16.7917 25.5 17.1667 25.125 17.1667 24.6666V22.1666C17.775 20.0166 19.725 19.275 21.475 19.6666L20.7416 20.4C20.4166 20.725 20.4166 21.25 20.7416 21.575C21.0666 21.9 21.5917 21.9 21.9167 21.575L24.075 19.4166C24.4 19.0916 24.4 18.5666 24.075 18.2416L21.9167 16.0833C21.8396 16.006 21.748 15.9447 21.6472 15.9029C21.5464 15.8611 21.4383 15.8396 21.3291 15.8396C21.22 15.8396 21.1119 15.8611 21.0111 15.9029C20.9103 15.9447 20.8187 16.006 20.7416 16.0833C20.4166 16.4083 20.4166 16.9333 20.7416 17.2583L21.475 18C20.2166 17.725 18.3667 18.0666 17.1667 19.1333V13.6916L17.9 14.425C18.225 14.75 18.75 14.75 19.075 14.425C19.4 14.1 19.4 13.575 19.075 13.25L16.9167 11.0916C16.8396 11.0144 16.748 10.9531 16.6472 10.9113C16.5464 10.8694 16.4383 10.8479 16.3292 10.8479C16.22 10.8479 16.1119 10.8694 16.0111 10.9113C15.9103 10.9531 15.8187 11.0144 15.7417 11.0916L13.5917 13.2416C13.2667 13.5666 13.2667 14.0916 13.5917 14.4166C13.9167 14.7416 14.4417 14.7416 14.7667 14.4166L15.5 13.6916V24.6666Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_971_122" x1="31" y1="27.5" x2="6.5" y2="7" gradientUnits="userSpaceOnUse">
<stop stop-color="#FB4293"/>
<stop offset="1" stop-color="#F957A1"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+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_971_137)"/>
<path d="M24.25 18.0001C24.25 17.8084 24.2416 17.6251 24.225 17.4334L25.775 16.2584C26.1083 16.0084 26.2 15.5417 25.9916 15.1751L24.4333 12.4834C24.225 12.1167 23.775 11.9667 23.3916 12.1334L21.6 12.8917C21.2916 12.6751 20.9666 12.4834 20.625 12.3251L20.3833 10.4001C20.3333 9.98341 19.975 9.66675 19.5583 9.66675H16.45C16.025 9.66675 15.6666 9.98341 15.6166 10.4001L15.375 12.3251C15.0333 12.4834 14.7083 12.6751 14.4 12.8917L12.6083 12.1334C12.225 11.9667 11.775 12.1167 11.5666 12.4834L10.0083 15.1834C9.79997 15.5501 9.89163 16.0084 10.225 16.2667L11.775 17.4417C11.7583 17.6251 11.75 17.8084 11.75 18.0001C11.75 18.1917 11.7583 18.3751 11.775 18.5667L10.225 19.7417C9.89163 19.9917 9.79997 20.4584 10.0083 20.8251L11.5666 23.5167C11.775 23.8834 12.225 24.0334 12.6083 23.8667L14.4 23.1084C14.7083 23.3251 15.0333 23.5167 15.375 23.6751L15.6166 25.6001C15.6666 26.0167 16.025 26.3334 16.4416 26.3334H19.55C19.9666 26.3334 20.325 26.0167 20.375 25.6001L20.6166 23.6751C20.9583 23.5167 21.2833 23.3251 21.5916 23.1084L23.3833 23.8667C23.7666 24.0334 24.2166 23.8834 24.425 23.5167L25.9833 20.8251C26.1916 20.4584 26.1 20.0001 25.7666 19.7417L24.2166 18.5667C24.2416 18.3751 24.25 18.1917 24.25 18.0001ZM18.0333 20.9167C16.425 20.9167 15.1166 19.6084 15.1166 18.0001C15.1166 16.3917 16.425 15.0834 18.0333 15.0834C19.6416 15.0834 20.95 16.3917 20.95 18.0001C20.95 19.6084 19.6416 20.9167 18.0333 20.9167Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_971_137" 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: 1.8 KiB

+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_971_132)"/>
<path d="M18 17.1668C17.0834 17.1668 16.3334 17.9168 16.3334 18.8335C16.3334 19.7502 17.0834 20.5002 18 20.5002C18.9167 20.5002 19.6667 19.7502 19.6667 18.8335C19.6667 17.9168 18.9167 17.1668 18 17.1668ZM23 18.8335C23 15.8668 20.4084 13.5002 17.375 13.8752C15.1917 14.1418 13.3917 15.8835 13.0584 18.0585C12.7834 19.8502 13.4667 21.4835 14.6667 22.5585C15.0667 22.9168 15.6917 22.8335 15.9667 22.3668L15.975 22.3585C16.175 22.0085 16.0917 21.5835 15.7917 21.3085C14.9334 20.5335 14.4667 19.3335 14.775 18.0252C15.05 16.8418 16.0084 15.8835 17.1917 15.6002C19.375 15.0752 21.3334 16.7252 21.3334 18.8335C21.3334 19.8168 20.9 20.6918 20.225 21.3002C19.925 21.5668 19.8334 22.0002 20.0334 22.3502L20.0417 22.3585C20.3 22.8002 20.9 22.9335 21.2917 22.5918C22.3334 21.6752 23 20.3335 23 18.8335ZM17.025 10.5585C13.175 10.9918 10.0667 14.1668 9.70838 18.0252C9.41671 21.1085 10.8084 23.8752 13.0584 25.5335C13.4584 25.8252 14.025 25.7002 14.275 25.2752C14.4834 24.9168 14.3917 24.4502 14.0584 24.2002C12.1584 22.7918 11.0167 20.4085 11.425 17.7835C11.875 14.8668 14.3084 12.5418 17.2417 12.2168C21.2584 11.7585 24.6667 14.9002 24.6667 18.8335C24.6667 21.0418 23.5917 22.9835 21.9417 24.2002C21.6084 24.4502 21.5167 24.9085 21.725 25.2752C21.975 25.7085 22.5417 25.8252 22.9417 25.5335C25 24.0168 26.3334 21.5835 26.3334 18.8335C26.3334 13.9085 22.0584 9.98349 17.025 10.5585Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_971_132" x1="31" y1="27.5" x2="6.5" y2="7" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFA800"/>
<stop offset="1" stop-color="#FFAC4B"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.7 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

-21
View File
@@ -1,21 +0,0 @@
.page-enter {
opacity: 0;
clip-path: inset(0 100% 0 0); /* 完全隐藏内容 */
}
.page-enter-active {
opacity: 1;
clip-path: inset(0 0 0 0); /* 逐渐显示整个内容 */
transition: opacity 300ms, clip-path 300ms ease-in-out;
}
.page-exit {
opacity: 1;
clip-path: inset(0 0 0 0); /* 完全显示内容 */
}
.page-exit-active {
opacity: 0;
clip-path: inset(0 100% 0 0); /* 逐渐隐藏内容 */
transition: opacity 300ms, clip-path 300ms ease-in-out;
}
+13 -11
View File
@@ -13,10 +13,10 @@ body {
:root {
--primary-main: #5b5c9d;
--text-primary: #637381;
--text-primary: #1f1f1f;
--selection-color: #f5f5f5;
--scroller-color: #90939980;
--background-color: #ffffff;
--scroller-color: #8c8c8c;
--background-color: #f5f5f5;
--background-color-alpha: rgba(24, 103, 192, 0.1);
--border-radius: 8px;
}
@@ -27,14 +27,17 @@ body {
}
*::-webkit-scrollbar {
width: 6px;
height: 6px;
width: 8px;
height: 8px;
background: transparent;
}
*::-webkit-scrollbar-thumb {
border-radius: 6px;
background-color: var(--scroller-color);
}
*::-webkit-scrollbar-corner {
background-color: transparent;
}
body {
overflow: hidden;
@@ -42,14 +45,13 @@ body {
@import "./layout.scss";
@import "./page.scss";
@import "./anime.scss";
@import "./font.scss";
@media (prefers-color-scheme: dark) {
:root {
background-color: rgba(18, 18, 18, 1);
}
}
// @media (prefers-color-scheme: dark) {
// :root {
// background-color: rgba(18, 18, 18, 1);
// }
// }
.user-none {
user-select: none;
+59 -31
View File
@@ -5,33 +5,45 @@
overflow: hidden;
&__left {
flex: 1 0 15%;
flex: 1 0 200px;
display: flex;
height: 100%;
max-width: 225px;
min-width: 125px;
padding: 16px 0 8px;
position: relative;
width: 100%;
// max-width: 225px;
// min-width: 225px;
// padding: 16px 0 8px;
padding: 0px 0px 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;
background-color: var(--background-color-alpha);
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,
@@ -39,17 +51,19 @@
width: 100%;
height: 100%;
pointer-events: none;
fill: var(--primary-main);
// fill: var(--primary-main);
#bg {
fill: var(--background-color);
}
// #bg {
// fill: var(--background-color);
// }
}
.the-newbtn {
position: absolute;
right: 10px;
bottom: 0px;
top: 15px;
border-radius: 8px;
padding: 2px 4px;
transform: scale(0.8);
}
}
@@ -58,6 +72,7 @@
flex: 1 1 80%;
overflow-y: auto;
margin-bottom: 0px;
padding-top: 4px;
}
.the-traffic {
@@ -65,25 +80,31 @@
> div {
margin: 0 auto;
padding: 0px 20px;
}
}
}
&__right {
position: relative;
flex: 1 1 75%;
flex: 1 1 100%;
height: 100%;
background-color: var(--background-color-alpha);
// background-color: var(--background-color-alpha);
.the-bar {
position: absolute;
top: 2px;
right: 8px;
// position: absolute;
// top: 0px;
// right: 0px;
height: 36px;
display: flex;
align-items: center;
// align-items: center;
justify-content: end;
box-sizing: border-box;
z-index: 2;
.the-dragbar {
margin-top: 5px;
app-region: drag;
}
}
.the-content {
@@ -100,15 +121,22 @@
.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;
margin-top: 10px;
margin-left: 10px;
padding-top: 30px;
padding-left: 10px;
padding-right: 20px;
padding-bottom: 16px;
}
.layout__right .the-content {
top: 30px;
top: 24px;
}
}
}
@@ -119,7 +147,7 @@
padding-top: 24px;
}
.layout__right .the-content {
top: 20px;
top: 24px;
}
}
}

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