Compare commits

...
729 Commits
Author SHA1 Message Date
oomeow d0917dae73 fix: core always run by sidecar mode after reinstall service 2025-12-06 13:27:24 +08:00
Tunglies f383a18f77 fix(release): add Rust target installation step for cross-compilation 2025-12-06 12:28:55 +08:00
Tunglies f8035710df fix(release): update Rust toolchain to version 1.91.0 and adjust installation steps 2025-12-06 12:11:45 +08:00
Tunglies 4e34eb156b chore: update version to 2.4.4-rc 2025-12-06 11:52:04 +08:00
Tunglies c997fbbeed chore: update Cargo.lock 2025-12-06 11:48:00 +08:00
Tunglies 54ba9fc98d fix(changelog): remove obsolete entry and clarify i18n support details 2025-12-06 11:44:07 +08:00
Slinetrac eb7d22f610 refactor(debug): simplify toggle flow and unify flag handling 2025-12-06 11:36:47 +08:00
Tunglies 90b1c6e153 refactor(signal): migrate signal to handling async in windows and remove windows_sys dependency (#5741)
* refactor(signal): migrate signal to handling async in windows and remove signal-hook dependency

* refactor(signal): enhance Windows signal handling with improved cleanup logic

* refactor(signal): improve exit handling in run function for async compatibility
2025-12-06 11:33:49 +08:00
Slinetrac 5797bb7f8c docs: add JSDoc for showNotice API 2025-12-06 11:16:26 +08:00
Tunglies 2ca8e6716d style: adjust rustfmt max_width to 120 2025-12-06 10:31:31 +08:00
Tunglies c888703b05 fix(service): enhance version check error handling for compatibility with older service versions 2025-12-06 10:26:03 +08:00
Tunglies cd6885f115 Revert "ci(dev): add --profile fast-release argument to Tauri build step"
This reverts commit 7ae2414067.
2025-12-06 09:28:41 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 643df7c15d chore(deps): update dependency react-i18next to v16.4.0 (#5739)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-06 09:20:09 +08:00
❤是纱雾酱哟~ ff0de29f17 Fixes traffic parsing for small values (#5737)
- Unified handler to IEC standard.
- Change handler to parse number in a unified style, even with number less than 1000.

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>
2025-12-06 09:00:04 +08:00
Tunglies 7ae2414067 ci(dev): add --profile fast-release argument to Tauri build step 2025-12-06 08:14:33 +08:00
Tunglies d28075221c refactor(signal): migrate signal to handling async in unix and remove signal-hook dependency 2025-12-06 06:41:47 +08:00
Slinetrac d0c8f7fcc1 docs: Changelog.md 2025-12-05 13:55:32 +08:00
Slinetrac 28f8bb9199 fix(tray): remove checkmark from proxy group entries 2025-12-05 13:46:22 +08:00
Slinetrac 19a31450e5 fix(nav): use ListItemButton as drag handle for navigation items 2025-12-05 13:21:46 +08:00
Tunglies a0fd24bb90 refactor(sysopt): replace Mutex with RwLock for inner_proxy management 2025-12-04 23:47:34 +08:00
Tunglies f2ad62e446 chore: update clash_verge_service_ipc to version 2.0.24 , improve service prompts and improve service need reinstall process 2025-12-04 23:04:03 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 7aeaab6e5c chore(deps): update npm dependencies to v19.2.1 (#5711)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-04 15:01:51 +08:00
Sline afee21dae4 refactor: unify Mihomo WS subscription with shared hook (#5719)
* refactor: unify Mihomo WS subscription with shared hook

* refactor: relocate clash log hook and streamline services

* docs: Changelog.md
2025-12-04 14:58:03 +08:00
Slinetrac 3cf51de850 refactor: reorganize component and hook structure; remove unused modules 2025-12-04 12:51:22 +08:00
Slinetrac 4f8ed63cf0 refactor(use-clash): centralize patch typing and port validation logic 2025-12-04 09:30:24 +08:00
Tunglies 135d14b170 chore: allow renovate automerge patch version and allow checks cargo workspace 2025-12-04 06:27:17 +08:00
Tunglies 762c991035 Revert "chore: update renovate configuration to enhance dependency management and grouping"
This reverts commit 2af7ddfdef.
2025-12-04 06:13:35 +08:00
Tunglies 357069505a chore(deps): bump clash_verge_service_ipc version to 2.0.23 2025-12-04 00:04:27 +08:00
Tunglies 2af7ddfdef chore: update renovate configuration to enhance dependency management and grouping 2025-12-03 23:57:53 +08:00
Tunglies bd24a3f0ed chore: update Changelog for WebView2 Runtime upgrade to 133.0.3065.92 2025-12-03 22:08:30 +08:00
Tunglies 127501ce49 chore(deps): bump fixed WebView2 Runtime from 109.0.1518.78 to 133.0.3065.92 2025-12-03 22:05:43 +08:00
Tunglies 666878e288 refactor(release): enhance Rust caching strategy and update file paths 2025-12-03 22:00:19 +08:00
oomeowandTunglies 9388b73c1f fix: upgrade mihomo core failed (#5646)
* fix: upgrade mihomo core failed

* fix: upgrade core failed when run core by service

* fix: app freeze when restart core on windows

* deps: bump clash-verge-rev-ipc version lock

---------

Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2025-12-03 21:48:30 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 29cf57d4b9 chore(deps): update npm dependencies (#5706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 21:27:23 +08:00
wonfen 176ad1a741 revert: macOS skip codesign 2025-12-03 17:51:24 +08:00
Slinetrac 56ad05cbf9 refactor(main): extract keyboard shortcuts to utils 2025-12-03 13:15:21 +08:00
Slinetrac 1727d0b6d4 refactor(preload): unify preload logic into preload.ts 2025-12-03 12:51:46 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> cbc8782bf6 chore(deps): update npm dependencies (#5701)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 12:23:50 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 76eb063f7d chore(deps): update cargo dependencies (#5699)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 12:18:43 +08:00
Slinetrac edf6e7cc42 chore: improve i18n 2025-12-02 18:55:59 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 3a5ce1dd19 chore(deps): update dependency typescript-eslint to ^8.48.1 (#5698)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-02 18:29:40 +08:00
Tunglies 550a7e0bb9 fix: restart core or change can not remember proxy select in Global mode with sidecar #5466 2025-12-02 16:25:25 +08:00
Slinetrac 1393828078 docs: Changelog.md 2025-12-02 11:23:00 +08:00
Slinetrac 32a5044026 fix(updater): compute real download progress and stabilize progress bar states 2025-12-02 11:06:10 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> bfa7e1a2e6 chore(deps): update dependency @eslint-react/eslint-plugin to ^2.3.10 (#5693)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-02 10:36:14 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 9f151594ff chore(deps): update dependency i18next to ^25.7.1 (#5692)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-02 09:36:19 +08:00
Tunglies a611f7d8a7 fix: switch reqwest client to use rustls-tls for improved security #5559 2025-12-01 22:22:26 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> d5f1af91f1 chore(deps): update dependency i18next to ^25.7.0 (#5691)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 21:54:56 +08:00
Sline 008234988c fix: ensure early CSS injection by preloading Verge config (#5690)
* fix(theme): background css injection

* fix: ensure early CSS injection by preloading Verge config
2025-12-01 21:45:01 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 9056e8a298 chore(deps): update dependency vite to ^7.2.6 (#5682)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 19:32:21 +08:00
Tunglies e6a4da8064 chore: update Mihomo(Meta) kernel version to v1.19.17 and improve warning formatting 2025-12-01 16:30:36 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 8f10f98db1 chore(deps): update dependency react-virtuoso to ^4.16.1 (#5675)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 07:45:20 +08:00
Tunglies a1a9c2b7bb refactor: optimize reference handling in profile and i18n modules 2025-11-30 23:00:16 +08:00
Tunglies 6897ead070 perf: change patch_config parameter from Mapping to &Mapping for efficiency 2025-11-30 20:44:21 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> ae8c302bf4 chore(deps): update cargo dependencies (#5670)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 19:48:44 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> aab8c8b3a1 chore(deps): update npm dependencies (#5671)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 19:47:19 +08:00
Tunglies dca9b51b4b chore: macOS skip codesign 2025-11-30 16:16:16 +08:00
Tunglies a14f76eaf8 chore: comment out Apple signing credentials in dev workflow 2025-11-30 15:12:06 +08:00
Tunglies 3e4bdf2d64 chore: comment out Apple signing credentials in autobuild workflow 2025-11-30 14:38:31 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> b03dcd71f4 chore(deps): update dependency prettier to ^3.7.3 (#5663)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 08:48:14 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 25a3c2524f chore(deps): update github actions (#5660)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-29 20:44:46 +08:00
Slinetrac 882f1a2a4a Revert "feat: imporve the layout of proxies page (#5647)"
This reverts commit 8c1caf8555.
2025-11-29 20:40:45 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> bd2d86fd8f chore(deps): update github actions (#5661)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-29 20:37:21 +08:00
Tunglies 271550205b chore: update autobuild action version, let renovate manage ci version 2025-11-29 20:28:41 +08:00
Slinetrac 45d4f0e89c chore: remove meta-json-schema #5656 #5478 2025-11-29 15:10:41 +08:00
Slinetrac 1d41cf691b fix: wake window correctly on first silent startup 2025-11-29 14:10:55 +08:00
DengHanjie 8c1caf8555 feat: imporve the layout of proxies page (#5647)
* feat: imporve the layout of proxies page to avoid navigator overlapping items (issue #5556)

* feat(proxy): remove left padding
2025-11-29 11:08:58 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 0fa77441f5 chore(deps): update npm dependencies (#5648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-29 10:04:40 +08:00
oomeow 6456c597ed perf: spawn clean task (#5595)
* perf: spawn task

* perf: spawn task to save draft date

* perf: store proxy client

* chore: update

* perf: reduce handle quit on macOS

* chore: clippy

* docs: update Changelog.md

* chore: update
2025-11-28 21:12:34 +08:00
Sline 683667f8ae fix(ui): prevent light flash on dark startup (#5637)
* fix(ui): prevent light flash on dark startup

* fix(window): apply initial dark/light theme to prevent white flash

* refactor(boot): optimize config fetch and theme fallback

* fix: system theme detection

* fix(window): remove black flash before loader by aligning initial paint with theme
2025-11-28 15:15:17 +08:00
Slinetrac 34aa26813f docs: Changelog.md 2025-11-28 11:05:49 +08:00
Slinetrac 99dda5496e fix(i18n): prevent zh flash and normalize language caching #5632 2025-11-28 10:38:42 +08:00
oomeow 9ce5d27d6e Revert "chore: frontend format check"
This reverts commit 90b850154d.
2025-11-27 21:50:25 +08:00
oomeow 90b850154d chore: frontend format check 2025-11-27 21:08:04 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> aed3060a11 chore(deps): update dependency prettier to ^3.7.1 (#5631)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-27 20:46:33 +08:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Slinetrac
3cd6e88c37 chore(deps): update dependency prettier to ^3.7.0 (#5625)
* chore(deps): update dependency prettier to ^3.7.0

* style: prettier

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-11-27 17:25:34 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> c2c628e8e0 chore(deps): update dependency @eslint-react/eslint-plugin to ^2.3.9 (#5613)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-27 10:06:25 +08:00
Slinetrac a572708fd8 fix(search): prevent crash on invalid regex input in search box
Closes #5609
2025-11-26 18:40:22 +08:00
Slinetrac cbd8c89097 fix(connection-table): set boxSizing to border-box for header and cells
Closes #5611
2025-11-26 17:49:17 +08:00
Slinetrac affdfc045e fix(backup-config-viewer): add top margin to WebDAV address field to prevent floating label from overlapping dialog title
Closes #5608
2025-11-26 17:13:05 +08:00
Tunglies adb969d370 fix(ip-info-card): update IP detection to refresh only when necessary 2025-11-26 14:41:12 +08:00
Tunglies 22e2e751a2 refactor(profile): update get_profiles to return SharedBox and optimize clone usage 2025-11-26 11:27:02 +08:00
Tunglies 2e6e9a0db4 refactor(use_seq): optimize group handling by removing unnecessary cloning 2025-11-26 11:27:01 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 643e15b752 chore(deps): update dependency react-virtuoso to ^4.15.0 (#5600)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 11:10:52 +08:00
Tunglies ecc272aa20 feat: integrate tauri-plugin-clipboard-manager and add system info commands (#5593) 2025-11-25 16:58:25 +08:00
Slinetrac d3dc40e788 docs: Changelog.md 2025-11-25 11:51:00 +08:00
Sline b83ef994f8 refactor(traffic-monitor): Move traffic sampling/compression to a dedicated Web Worker (#5588)
* refactor(traffic-monitor): Move traffic sampling/compression to a dedicated Web Worker

* refactor(traffic-monitor): introduce shared sampler, safe fallback, and decoupled range handling

* refactor(traffic-monitor): add optional subscribe flag and fix range handling

* fix(traffic-monitor): age out idle samples and prevent ref-count updates after unmount
2025-11-25 11:43:45 +08:00
Sline caca8b2bc4 refactor: debugLog for frontend (#5587) 2025-11-25 10:01:04 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 64727ad660 chore(deps): update dependency typescript-eslint to ^8.48.0 (#5583)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-25 08:49:32 +08:00
AetherWingandTunglies 6b3f5eea16 fix: correct flag emoji for ISO alpha-3 region code (#5557)
* fix: correct flag emoji for ISO alpha-3 region code

* fix: use rust_iso3166 to convert ISO3 region code

* fix: validate ISO country code when generating flag emoji

* fix: correct icon encoding for unlock test in specific regions

---------

Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2025-11-24 20:40:32 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 38b306a438 chore(deps): update dependency @types/react to v19.2.7 (#5577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-24 20:12:49 +08:00
Tunglies e24bb8f9cc chore: update cargo lock 2025-11-24 20:06:06 +08:00
Slinetrac 0c00a39754 docs: Changelog.md 2025-11-24 16:22:14 +08:00
Sline 8e8182f707 refactor(app-data): split monolithic context into focused SWR hooks (#5576)
* refactor(app-data): split monolithic context into focused SWR hooks

* refactor(swr): unify polling and consolidate proxy/config/provider data flow
2025-11-24 16:18:31 +08:00
Tunglies 871881c460 fix(resolve): implement resolve_done state management and refactor timer logic 2025-11-24 13:52:08 +08:00
Tunglies cbd1fa44d7 fix(reqwest): add native-tls feature to reqwest dependency #5559 2025-11-24 13:25:00 +08:00
Tunglies ad6fe696c0 fix: resolve proxy button and highlight state desynchronization; fix sidebar navigation issues 2025-11-23 20:01:54 +08:00
Tunglies 44adf55975 fix(proxy): sync system proxy switch state with background highlight #5240, #5439, #5421, #4651, #4536
Fixed race condition where button state and background color would desync
during rapid toggles or slow backend responses.

Root causes:
- Button used actualState while background used configState
- toggleSystemProxy returned immediately via setTimeout(0), releasing lock
  before backend operation completed
- 100ms delay in updateProxyStatus caused visual flash

Changes:
- Unified state source: both button and background now use actualState
- Made toggleSystemProxy truly async with proper await chain
- Wrapped toggle function with useLockFn to serialize operations
- Reduced state refresh delay from 100ms to 50ms
- Fixed GuardState lock release timing for async operations
- Added proper error rollback with state refresh on failure

Resolves issue where proxy toggle button would show as off while
background remained green, or vice versa.
2025-11-23 12:17:52 +08:00
Tunglies ddd24eb3ac fix: prevent drag listeners from blocking navigation clicks in sidebar #5567
Separated dnd-kit drag handlers (attributes/listeners) from ListItemButton
to ListItemIcon only. Previously, long-press or rapid clicks on nav items
would trigger drag detection and cancel the click event, causing the item
to visually highlight but fail to navigate.

Changes:
- Moved sortable attributes/listeners to icon elements only
- ListItemButton now handles pure navigation clicks without interference
- Drag handle cursor ('grab') remains on icons when sortable is enabled

Fixes issue where sidebar tabs would "go dark" without actual route change.
2025-11-23 11:47:27 +08:00
oomeowandTunglies cbab199f80 fix: failed to receive shutdown signal on windows (#5533)
* fix: receive shutdown signal failed on windows

* docs: update Changelog.md

* chore: update

* fix: use tokio runtime to handle shutdown signal

* docs: update Changelog.md

* fix: move tauri dependency to the correct section in Cargo.toml

* fix: remove unused exit handling code in run function

* fix(clash-verge-signal): use global runtime to avoid tokio runtime panic on unix

* chore: update tauri-plugin-mihomo deps

* fix: handle macOS exit event to ensure proper application termination

---------

Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2025-11-22 22:36:00 +08:00
Slinetrac 687530a9cd fix(sysproxy): remove redundant 172.29.0.0/16 entry #4351
Closes #4351
2025-11-22 19:05:57 +08:00
Sline 4ff186497c feat(tun): auto-redirect GUI support for linux (#5565)
* feat(tun): auto-redirect GUI support for linux

* refactor(tun-viewer): unify Linux-only auto-redirect row layout
2025-11-22 18:58:57 +08:00
Slinetrac 45020fceda refactor(proxies): reorder chain-mode toggle for immediate UI feedback 2025-11-22 17:06:35 +08:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Slinetrac
51760a5b95 chore(deps): update npm dependencies (#5561)
* chore(deps): update npm dependencies

* fix: types

* chore: replace vite-plugin-monaco-editor-esm with utils/monaco

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-11-22 16:33:34 +08:00
Tunglies a271ba6ce5 feat: add update_proxy_chain_config method with detailed documentation for proxy and proxy-group configuration 2025-11-22 16:30:25 +08:00
Tunglies 82bed4910e perf: refactor IRuntime to use HashSet for exists_keys and improve related functions performance 2025-11-22 16:25:50 +08:00
Tunglies c82cefe80e feat: add clash-verge-types crate and integrate IRuntime struct 2025-11-22 15:33:24 +08:00
Slinetrac d110e462aa Revert "chore(deps): update npm dependencies (#5558)"
This reverts commit ddcd4a8192.
2025-11-22 13:49:17 +08:00
Slinetrac 1d9179ba7e Revert "fix: update vite-plugin-monaco-editor to version 1.1.0"
This reverts commit dd52522905.
2025-11-22 13:49:14 +08:00
Tunglies dd52522905 fix: update vite-plugin-monaco-editor to version 1.1.0 2025-11-22 12:01:34 +08:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Tunglies
ddcd4a8192 chore(deps): update npm dependencies (#5558)
* chore(deps): update npm dependencies

* fix: correct monaco typescript reference in editor viewer

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2025-11-22 08:39:08 +08:00
Slinetrac c38dd5da14 fix(unlock): normalize and dedupe unlock items 2025-11-21 20:22:57 +08:00
Tunglies 803f749063 Revert "chore(deps): update npm dependencies (#5554)"
This reverts commit da0106b1e8.
2025-11-21 18:13:22 +08:00
Slinetrac 192b15a02d docs: CONTRIBUTING.md 2025-11-21 14:36:04 +08:00
Slinetrac a5a791a7f8 fix(ci): pnpm cache 2025-11-21 13:47:30 +08:00
Sline 02d0a7e61f chore(ci): add frontend-check.yml (#5555)
* chore(ci): add frontend-check.yml

* test

* Revert "test"

This reverts commit 24c4cf270c.
2025-11-21 13:28:54 +08:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Slinetrac
da0106b1e8 chore(deps): update npm dependencies (#5554)
* chore(deps): update npm dependencies

* fix: types

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-11-21 12:36:17 +08:00
Tunglies ee9e9182dc perf: update current_* methods to return references instead of owned Strings and decrease unnecessary arc clone 2025-11-21 12:17:35 +08:00
Tunglies 6a62009485 Revert "chore(deps): update dependency monaco-editor to ^0.55.1 (#5548)"
This reverts commit 22241f8f60.
2025-11-21 12:11:45 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 22241f8f60 chore(deps): update dependency monaco-editor to ^0.55.1 (#5548)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-21 10:08:49 +08:00
Tunglies a1f8ab7308 refactor: update function signatures to use references for Mapping and String 2025-11-20 22:50:36 +08:00
Tunglies 5a677fbbeb refactor: update function signatures to use references for Mapping and String 2025-11-20 21:49:16 +08:00
Slinetrac fcc1d4f9b5 chore: remove vitest 2025-11-20 21:42:11 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> e3da884bde chore(deps): update dependency vitest to ^4.0.12 (#5547)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 20:54:01 +08:00
Tunglies 8e3273a32c refactor: simplify sysproxy usage in network commands 2025-11-20 19:50:25 +08:00
Slinetrac 0827829cc5 docs: Changelog.md 2025-11-20 17:52:20 +08:00
Sline bf06cbc87d refactor: replace @mui/x-data-grid with @tanstack/react-table (#5544) 2025-11-20 17:41:34 +08:00
Tunglies 7e373ff39c fix(mihomo): unix can not connect mihomo ipc after reload config #5532 2025-11-20 17:02:57 +08:00
Tunglies 87e3125e67 fix: resolve issue with application not remembering user behavior on restart or shutdown 2025-11-20 16:07:05 +08:00
Tunglies 131cd6c3b8 fix(config): restart and quit application or shutdown system can not remember user's behavior #5347, #5359, #5430 2025-11-20 16:03:43 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> e42ae0e8c5 chore(deps): update dependency vite to ^7.2.4 (#5541)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 15:54:09 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> c765016063 chore(deps): update dependency vite to ^7.2.3 (#5540)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 15:48:52 +08:00
Slinetrac 614a2cf00b style: prettier 2025-11-20 15:27:42 +08:00
Slinetrac be97a27ba3 fix: Monaco syntax highlighting broken by CSS injection #5440 2025-11-20 15:16:25 +08:00
Tunglies b3dc48d07e fix(tests): suppress clippy expect warnings in enhance function
fix(timer): improve task removal logic in add_task method
refactor(notification): drop binding after emitting event
chore(Cargo): add lints section to Cargo.toml
2025-11-20 14:59:49 +08:00
Tunglies 4c8673fcb2 fix(timer): optimize lock handling in add_task method 2025-11-20 14:50:53 +08:00
Slinetrac 51a7b4fe75 docs: Changelog.md 2025-11-20 13:29:45 +08:00
Slinetrac 2bb9f648ba fix(app-data-provider): dashboard state sync 2025-11-20 13:23:50 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> c3fe2d31ef chore(deps): update npm dependencies (#5535)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 12:35:17 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 4617ec0997 chore(deps): update dependency glob to v13 (#5534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 12:33:59 +08:00
Tunglies d8090277af refactor: replace singleton_lazy with singleton macro across multiple modules 2025-11-19 22:40:52 +08:00
Tunglies 108a05ce96 refactor: replace CommandChildGuard with CommandChild and simplify related methods 2025-11-19 22:11:22 +08:00
SlineandTunglies d808e59156 fix(enhance): prevent crash when deleted proxies remain in manual groups (#5522)
* feat(enhance): cleanup stale proxies and policies before final config

* feat(enhance): preserve provider-backed groups when sanitizing proxies

* docs: Changelog.md

* refactor: to_owned

---------

Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2025-11-19 18:59:20 +08:00
Tunglies ba3cd9b006 refactor: simplify IVerge struct by deriving Default and removing manual implementation 2025-11-19 18:40:53 +08:00
Tunglies aad5bb812c chore: add removeUnusedCommands to build configuration 2025-11-19 18:13:35 +08:00
Slinetrac 6ab597db8f fix: prevent filename from overlapping with action icons in backup records #5525
Closes #5525
2025-11-19 17:29:11 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 85302144b2 chore(deps): update npm dependencies (#5528)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-19 17:11:05 +08:00
Slinetrac ba90c6a1e1 docs: Changelog.md 2025-11-19 17:09:54 +08:00
Sline f439e93a2b feat(hotkey): add global reactivate_profiles shortcut (#5527)
* feat(hotkey): add global reactivate_profiles shortcut

* feat(profile): expose validation state for reactivation shortcuts
2025-11-19 17:06:23 +08:00
Slinetrac 94b07b51d6 chore: rename Youtube to YouTube
Closes #5526
2025-11-19 17:04:19 +08:00
oomeow 9e3bc6db8c chore: update tauri-plugin-mihomo deps 2025-11-18 22:02:14 +08:00
Tunglies 531307ebe1 fix(workflows): update pnpm cache key format and node version in autobuild and dev workflows 2025-11-18 20:05:49 +08:00
Tunglies df4c69e9cc fix(workflows): enable caching for all crates in autobuild, dev, and lint-clippy workflows 2025-11-18 19:55:30 +08:00
Tunglies 9ef4362b23 fix(autobuild): update file paths for deb, rpm, and nsis bundles 2025-11-18 19:40:34 +08:00
Tunglies 3ec7c6d2b8 feat(profiles): add profile preview structure and update profile menu handling 2025-11-18 19:08:07 +08:00
Slinetrac ec7f912eae fix(home-profile-card): use profiles label for home shortcut 2025-11-18 18:50:53 +08:00
Slinetrac e7812396df feat(logs): support reverse chronological order #5513
Closes #5513
2025-11-18 17:39:56 +08:00
Slinetrac 4fa8b1f118 chore(types): refine monaco plugin default export typing 2025-11-18 16:51:41 +08:00
Tunglies af9b1b777e feat(app_init): enhance IPC pool configuration with new connection limits and reject policy 2025-11-18 16:41:41 +08:00
Tunglies 096b1b0d81 fix(ci): update Rust cache configuration and artifact paths in dev workflow 2025-11-18 16:33:13 +08:00
Tunglies 7a81a673f3 chore(ci): update Rust cache configuration in autobuild and lint-clippy workflows for improved efficiency 2025-11-18 16:14:30 +08:00
Sline 38980b1249 chore(deps): bump pnpm10 (#5511)
* chore(deps): bump pnpm10

* fix: keep React and router in a single chunk to avoid runtime crash
2025-11-18 16:01:34 +08:00
Tunglies b34e4cf551 Revert "fix: update Rust cache keys in autobuild and lint-clippy workflows for consistency"
This reverts commit 9df0998f3e.
2025-11-18 15:53:04 +08:00
Tunglies 8339fabb17 feat(sysinfo): add tauri-plugin-clash-verge-sysinfo for system information retrieval (#5510)
* feat(sysinfo): add tauri-plugin-clash-verge-sysinfo for system information retrieval

* feat(sysinfo): add tauri-plugin-clash-verge-sysinfo for system information retrieval

* fix(service): import Manager trait for app handle in linux_running_as_root function
2025-11-18 15:48:48 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> ca35994ccb chore(deps): update npm dependencies (#5502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-18 13:33:45 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 92d3a59d0d chore(deps): update rust crate open to 5.3.3 (#5501)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-18 13:32:18 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 4f6ceec0d5 chore(deps): update dependency glob to v12 (#5503)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-18 13:31:17 +08:00
Tunglies 9df0998f3e fix: update Rust cache keys in autobuild and lint-clippy workflows for consistency 2025-11-18 10:11:32 +08:00
Tunglies e672d19622 feat(signal): add clash-verge-signal crate and integrate signal handling (#5500)
* feat(signal): add clash-verge-signal crate and integrate signal handling

* fix: clippy error on type complexity
2025-11-18 10:04:21 +08:00
16478d314b fix: install service (#5479)
* fix: compatible older service version when install service

* docs: update Changelog.md

* chore: check service available

* chore: wait for service server is running

* fix: update changelog to reflect compatibility with legacy service mode upgrades

---------

Co-authored-by: Tunglies <tunglies.dev@outlook.com>
Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2025-11-18 09:52:48 +08:00
Slinetrac a5de24a545 fix(webdav): prevent dialog refresh loop and stabilize busy-state handling 2025-11-17 18:16:13 +08:00
Tunglies 35ae5cdba1 fix: update caching keys in workflow files to use glob pattern for Cargo.lock 2025-11-17 13:02:44 +08:00
Tunglies d1a099b480 fix: update file paths in release script for fixed WebView2 setup 2025-11-17 12:57:34 +08:00
Tunglies eb1768e212 fix: update file paths in autobuild script for WebView2 setup 2025-11-17 12:47:01 +08:00
Tunglies 056af768e5 feat: initialize workspace with clash-verge-draft and clash-verge-logging crates (#5489)
- Add Cargo.toml for workspace management, including dependencies and profiles.
- Create clash-verge-draft crate with basic structure, including a benchmark for Draft functionality.
- Implement Draft management with shared state and asynchronous modifications.
- Add tests for Draft functionality to ensure correctness.
- Create clash-verge-logging crate for logging utilities with structured log types and macros.
- Update src-tauri to use new crates and remove unnecessary configurations.
- Refactor existing code to utilize the new Draft and logging functionalities.
2025-11-17 11:51:50 +08:00
Tunglies 0866b93175 feat(logging): introduce clash-verge-logging crate for management (#5486)
* feat(logging): introduce clash-verge-logging crate for management

- Added a new crate `clash-verge-logging` with dependencies on `log`, `tokio`, `compact_str`, and `flexi_logger`.
- Implemented logging types and macros for structured logging across the application.
- Replaced existing logging imports with the new `clash_verge_logging` crate in various modules.
- Updated logging functionality to support different logging types and error handling.
- Refactored code to improve logging consistency and maintainability.

* fix(logging): update import paths for clash_verge_logging in linux.rs and dns.rs

* fix(logging): update import statement for clash_verge_logging in windows.rs
2025-11-17 10:42:57 +08:00
Slinetrac 635f63e9e5 feat: explicit paste button 2025-11-17 10:07:38 +08:00
Slinetrac f315a98afd fix: monaco editor line count limit 2025-11-17 07:41:37 +08:00
oomeow b0089d162b fix: dynamically modify pac content (#5468)
* fix: dynamically modify pac content

* docs: update Changelog.md
2025-11-16 18:53:43 +08:00
Slinetrac e1fc9547d8 docs: Changelog.md 2025-11-16 16:05:25 +08:00
Slinetrac cfd4532440 fix(home/traffic): ensure traffic chart stretches on Windows maximize 2025-11-16 15:42:14 +08:00
Tunglies 1eb4a0d834 fix(macos): remove tproxy-port from config on macOS #5439, #5397, #5372 2025-11-16 15:38:39 +08:00
Su Guoyu 29d8df40b9 fix(linux): retry with sudo when pkexec execution failed (#5469) 2025-11-16 14:52:13 +08:00
Tunglies dbb4877be6 refactor(Draft): management as crate (#5470)
* feat: implement draft functionality with apply and discard methods, and add benchmarks and tests

* Refactor Draft management and integrate Tokio for asynchronous operations

- Introduced a new `IVerge` struct for configuration management.
- Updated `Draft` struct to use `Arc<RwLock>` for better concurrency handling.
- Added asynchronous editing capabilities to `Draft` using Tokio.
- Replaced synchronous editing methods with asynchronous counterparts.
- Updated benchmark tests to reflect changes in the `Draft` API.
- Removed redundant draft utility module and integrated its functionality into the main `Draft` implementation.
- Adjusted tests to validate new behavior and ensure correctness of the `Draft` management flow.
2025-11-16 00:33:21 +08:00
Tunglies e66b8e7894 feat(timer): better subscription update timer after core startup 2025-11-15 21:18:35 +08:00
TungliesandSlinetrac 8654bad6b0 refactor: migrate proxy setting guard to sysproxy-rs crate (#5287)
* Refactor proxy management: Remove EventDrivenProxyManager and async_proxy_query

- Removed the EventDrivenProxyManager and its related event-driven proxy management logic.
- Replaced usages of AsyncProxyQuery with direct calls to sysproxy for fetching system and auto proxy configurations.
- Cleaned up constants by removing unused default proxy host.
- Updated sysopt to eliminate calls to the removed EventDrivenProxyManager.
- Adjusted logging and proxy state management to reflect the removal of event-driven architecture.
- Simplified the core module structure by removing unnecessary module imports.

* refactor: remove bypass module for cleaner network configuration

* feat: update sysproxy to version 0.4.0 and add guard feature; refactor sysopt for improved proxy management

* feat(windows/sysproxy): unify guard-type handling and auto-restore drift

* refactor(config): remove commented-out code for SysProxy update flag

---------

Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-11-15 20:51:36 +08:00
Slinetrac e021e45e9a fix(backup): correctly restore CSS injection from verge.yaml backups 2025-11-15 20:20:27 +08:00
Slinetrac 272c2f3467 Revert "fix(editor): restore syntax highlighting for JS/CSS in Monaco editor"
This reverts commit 55ac25b1a6.
vite-plugin-monaco-editor-esm has handled it
2025-11-15 16:46:11 +08:00
Slinetrac 0b641992e7 refactor(async-proxy): remove unnecessary Result wrappers from Windows registry helpers 2025-11-15 16:37:53 +08:00
Slinetrac 55ac25b1a6 fix(editor): restore syntax highlighting for JS/CSS in Monaco editor 2025-11-15 15:51:03 +08:00
Tunglies 40db928436 fix: suppress error from deep link registration in setup_plugins 2025-11-15 12:34:06 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 2eb5b5802d chore(deps): update dependency @types/react to v19.2.5 (#5465)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-15 10:10:12 +08:00
Tunglies 7f2bced0d4 fix: remove unnecessary exit statement from pre-push script 2025-11-15 08:58:52 +08:00
Tunglies a6cb903fe6 refactor: simplify function return types and remove unnecessary wraps 2025-11-15 08:56:58 +08:00
Tunglies 2b0ba4dc95 refactor: streamline config handling and logging mechanisms 2025-11-15 02:46:34 +08:00
Tunglies f6f288f02b fix: prevent save yaml return too early to failed get runtime config 2025-11-15 01:09:22 +08:00
wonfen c4e558d377 refactor: replace isahc with reqwest (#5463) 2025-11-14 23:10:56 +08:00
Tunglies 8190432903 fix: update download links for Windows and macOS installers
I hate tauri-action update :(
2025-11-14 21:59:27 +08:00
oomeow 20cb3e49fd chore: don't record websocket logs 2025-11-14 20:23:25 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 4fb8611e55 chore(deps): update npm dependencies (#5462)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-14 19:38:06 +08:00
Tunglies e41f910063 feat: add infoPlist configuration for macOS build 2025-11-14 17:30:27 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 4e800d165b chore(deps): update npm dependencies (#5455)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-14 09:59:10 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> f1bd56ea13 chore(deps): update cargo dependencies (#5453)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-13 23:17:55 +08:00
Slinetrac dbd46f2b76 fix: ruleEditor i18n keys 2025-11-13 21:45:04 +08:00
oomeow 18b3db46b0 chore: update tauri-plugin-mihomo deps 2025-11-13 20:24:24 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 0e2e919a58 chore(deps): update dependency @mui/x-data-grid to ^8.18.0 (#5452)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-13 19:53:41 +08:00
oomeow 1125dc0562 feat: support closed connections (#5244)
* feat: support closed connections

* fix: clear closed connections

* feat: show footer

* feat: show closed connection detail

* docs: update Changelog.md

* chore: update
2025-11-13 19:50:23 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> d73036a3b6 chore(deps): update npm dependencies (#5441)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-13 10:20:51 +08:00
Tunglies 9c24b3089c fix(editor): update editorRef initialization and handleChange logic for better type safety #5442 2025-11-13 09:42:03 +08:00
Tunglies d0d35da2f1 fix(editor): update theme mode for MonacoEditor to use 'light' instead of 'vs' in multiple components #5440 2025-11-13 08:39:10 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 3db69bd816 chore(deps): update npm dependencies (#5431)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-12 18:47:40 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 3fab4ce34a chore(deps): update dependency @types/react-dom to v19.2.3 (#5424)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-12 14:13:43 +08:00
Sline 2a2de29224 feat: tray menu enhance (#5412)
* feat(tray): move outbound modes into a dedicated submenu

* refactor: Enable 'Show Proxy Groups Inline' by default

* refactor(tray): move `copyEnv` to `more` submenu
2025-11-12 14:10:20 +08:00
Slinetrac 2ea22feb95 fix(tray): refresh icon with latest draft before apply() commits 2025-11-12 11:41:28 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> a5f2b163f1 chore(deps): update dependency @types/node to ^24.10.1 (#5420)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-12 10:55:48 +08:00
Tunglies f3a3d0e8b0 refactor: update update_icon method to require a non-optional verge reference and adjust related calls (#5417) 2025-11-12 07:36:24 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 92a3f0cae3 chore(deps): update dependency @types/react to v19.2.3 (#5416)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-12 01:26:04 +08:00
Slinetrac d18c303e12 fix(tun): set linux DEFAULT_STACK to gvisor 2025-11-12 00:39:46 +08:00
Slinetrac b40136a790 chore: Changelog.md 2025-11-12 00:19:50 +08:00
Slinetrac 645b92bc28 fix(tun): remove Linux-only block that forced tun.stack to mixed 2025-11-12 00:08:17 +08:00
Sline afb3842776 feat(canvas): add adaptive FPS and pause rendering on window blur (#5409) 2025-11-11 20:15:07 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> e02f23e0aa chore(deps): update npm dependencies (#5400)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-11 16:38:08 +08:00
Tunglies 6e9c6e60bb chore: update sysproxy-rs 2025-11-11 08:53:14 +08:00
Tunglies 600019f980 chore: update tauri-plugin-mihomo 2025-11-11 08:24:23 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 2d9eef834b chore(deps): update dependency @eslint-react/eslint-plugin to ^2.3.4 (#5394)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 22:38:29 +08:00
Tunglies 33f575ed4d refactor: update icon handling in tray methods to accept IVerge reference and improve logging #5379 (#5387)
* refactor: update icon handling in tray methods to accept IVerge reference and improve logging #5379

* refactor: simplify icon retrieval logic in Tray implementation
2025-11-10 19:06:37 +08:00
Slinetrac 7d42d5cb0f feat: add proxy chain icon 2025-11-10 14:30:47 +08:00
Sline 838e401796 feat(auto-backup): implement centralized auto-backup manager and UI (#5374)
* feat(auto-backup): implement centralized auto-backup manager and UI

- Introduced AutoBackupManager to handle verge settings, run a background scheduler, debounce change-driven backups, and trim auto-labeled archives (keeps 20); wired into startup and config refresh hooks
  (src-tauri/src/module/auto_backup.rs:28-209, src-tauri/src/utils/resolve/mod.rs:64-136, src-tauri/src/feat/config.rs:102-238)

- Extended verge schema and backup helpers so scheduled/change-based settings persist, create_local_backup can rename archives, and profile/global-extend mutations now trigger backups
  (src-tauri/src/config/verge.rs:162-536, src/types/types.d.ts:857-859, src-tauri/src/feat/backup.rs:125-189, src-tauri/src/cmd/profile.rs:66-476, src-tauri/src/cmd/save_profile.rs:21-82)

- Added Auto Backup settings panel in backup dialog with dual toggles + interval selector; localized new strings across all locales
  (src/components/setting/mods/auto-backup-settings.tsx:1-138, src/components/setting/mods/backup-viewer.tsx:28-309, src/locales/en/settings.json:312-326 and mirrored entries)

- Regenerated typed i18n resources for strong typing in React
  (src/types/generated/i18n-keys.ts, src/types/generated/i18n-resources.ts)

* refactor(setting/backup): restructure backup dialog for consistent layout

* refactor(ui): unify settings dialog style

* fix(backup): only trigger auto-backup on valid saves & restore restarts app safely

* fix(backup): scrub console.log leak and rewire WebDAV dialog to actually probe server

* refactor: rename SubscriptionChange to ProfileChange

* chore: update i18n

* chore: WebDAV i18n improvements

* refactor(backup): error handling

* refactor(auto-backup): wrap scheduler startup with maybe_start_runner

* refactor: remove the redundant throw in handleExport

* feat(backup-history-viewer): improve WebDAV handling and UI fallback

* feat(auto-backup): trigger backups on all profile edits & improve interval input UX

* refactor: use InputAdornment

* docs: Changelog.md
2025-11-10 13:49:14 +08:00
Cactus 78d5cb5eca i18n: remove proxy chain icon due to design inconsistency (#5377)
style: prettier
2025-11-10 12:40:59 +08:00
Tunglies b308251022 refactor: replace async_runtime::block_on with AsyncHandler::block_on and add inline annotations 2025-11-10 09:30:55 +08:00
Tunglies 49e5a557ca refactor: add inline annotations to CommandChildGuard methods 2025-11-10 09:23:30 +08:00
Tunglies e66a4f6aca perf: some inline magic for Draft
This would improve 8-14% CPU for all API
2025-11-10 07:18:50 +08:00
Tunglies 204bfa36e5 refactor: reorganize imports and remove unused axios instance code 2025-11-10 02:12:16 +08:00
Slinetrac a8fe5a71f7 fix: use Manager as _ 2025-11-09 23:46:23 +08:00
Slinetrac c03e10b5dc fix: macOS clippy lint 2025-11-09 23:36:39 +08:00
Slinetrac 0dcdd7fed6 fix: clippy lint 2025-11-09 23:23:03 +08:00
Tunglies 221a1e89eb chore: update Changelog for v2.4.4 with Mihomo(Meta) kernel upgrade and additional improvements 2025-11-09 22:38:55 +08:00
Tunglies 4eeb883464 refactor: update imports to use as _ for unused identifiers across multiple files 2025-11-09 22:15:37 +08:00
Tunglies e6c8f762db refactor: replace cloned() with clone() for improved performance in multiple files 2025-11-09 22:07:12 +08:00
Tunglies 7f267fa727 fix: update configuration data access from latest_arc to data_arc in Clash and WebDAV commands and init windows 2025-11-09 21:26:11 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> f5160ef277 chore(deps): update dependency @tauri-apps/cli to v2.9.4 (#5375)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-09 21:01:49 +08:00
Tunglies 5f2ac77923 chore: update Rust toolchain to version 1.91.0 in autobuild workflow
fix: update Rust toolchain action to use master branch for consistency

feat: add support for Windows ARM64 Gnullvm in development workflow

refactor: remove Windows ARM64 Gnullvm support and ensure Rust target is added for the pinned toolchain

feat: add Rust target installation step for pinned toolchain in autobuild workflow

fix: remove unnecessary blank lines in autobuild workflow
2025-11-09 20:53:19 +08:00
Tunglies 3e81dcd78f refactor: simplify icon file search logic in find_target_icons function
Try with 1.91.0 new API
2025-11-09 16:59:43 +08:00
Tunglies f644036e08 chore: update Rust toolchain to version 1.91 and adjust edition in rustfmt configuration 2025-11-09 16:31:37 +08:00
Tunglies c763fdd233 fix: update configuration data access from latest_arc to data_arc for TUN and system proxy settings 2025-11-09 16:05:32 +08:00
Tunglies 6026f432da fix: update method call to fetch configuration data for global hotkey settings 2025-11-09 16:02:21 +08:00
Tunglies d193460649 refactor: replace inline log fetching with dedicated script for better maintainability 2025-11-09 15:46:40 +08:00
Tunglies 02ff2d3c2e chore: update dependencies in Cargo.toml and Cargo.lock for improved performance and features 2025-11-09 15:35:22 +08:00
oomeow 72544ccb54 fix: missing app_log_max_size and app_log_max_count when saving misc settings 2025-11-09 13:32:59 +08:00
oomeow ba1b9796e8 fix: Tun disable failed due to too short timeout 2025-11-09 13:09:32 +08:00
SlinetracandLiMoon 2f7b3bb9a0 chore(i18n): update zhtw translations
Co-authored-by: LiMoon <azhe9335@gmail.com>
2025-11-09 10:51:21 +08:00
Slinetrac 5c323fc40f docs: move Changelog.history.md to docs 2025-11-09 10:15:21 +08:00
Tunglies d3d47d448a docs: update Changelog.md to include backend memory and performance optimizations 2025-11-09 02:52:22 +08:00
Tunglies 9c5cda793d refactor: streamline UI readiness handling by replacing RwLock with AtomicU8 and updating related functions 2025-11-09 02:47:48 +08:00
Tunglies 243f5b1b96 refactor: replace OnceCell with AtomicBool for UI readiness state 2025-11-09 02:29:50 +08:00
Tunglies a57ffba8ba refactor: replace RwLock with AtomicBool for is_exiting and emergency_mode in Handle and NotificationSystem 2025-11-09 02:23:35 +08:00
Tunglies 8f080389fe feat: add Changelog.md and update scripts to reference it
- Created a new Changelog.md file to document version updates and changes.
- Updated the updatelog.mjs script to parse Changelog.md instead of UPDATELOG.md.
- Modified updater-fixed-webview2.mjs to use the new Changelog.md for update notes.
2025-11-09 01:09:10 +08:00
Slinetrac 2a13696d06 docs: UPDATELOG.md 2025-11-09 00:21:03 +08:00
XiaoBuHalyandSlinetrac af4463fef1 feat(connections): enable persistent column visibility and ordering #5235 (#5249)
* feat(connections): enable persistent column visibility and ordering

fix(connections): smooth column manager access in table view

fix(connections): integrate column manager into column menu

- Move the Connections column manager trigger into the DataGrid column menu, reusing existing i18n strings
- Clean up the page toolbar by removing the standalone button and lifting dialog state to the parent page

fix(connections): unify column order handling and resolve lint warnings

fix(connections): unify column order handling and enforce 0 ESLint warnings

- extract reconcileColumnOrder helper and reuse it across:
  - initial normalization (useEffect)
  - manager drag order handler (handleManagerOrderChange)
- derive columnOptions directly from the computed, sorted columns to keep a single source of truth and prevent dialog/grid drift
- connections.tsx: remove direct setState in useEffect; gate dialog open with `isTableLayout && isColumnManagerOpen`; clean up unused imports
- connection-column-manager.tsx: replace deprecated `primaryTypographyProps` with `slotProps`
- run full lint; project now has 0 warnings on main configuration

* feat(connection-table): safeguard column visibility

---------

Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-11-09 00:15:01 +08:00
Slinerenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>rozan
c8aa72186e chore: i18n (#5276)
* chore: notice i18n

* feat: add script to clean up unused i18n keys

* chore: cleanup i18n keys

* refactor(i18n/proxies): migrate proxies UI to structured locale keys

* chore: i18n for rule module

* chore: i18n for profile module

* chore: i18n for connections module

* chore: i18n for settings module

* chore: i18n for verge settings

* chore: i18n for theme settings

* chore: i18n for theme

* chore(i18n): components.home.*

* chore(i18n): remove unused i18n keys

* chore(i18n): components.profile.*

* chore(i18n): components.connection

* chore(i18n): pages.logs.*

* chore(i18n): pages.*.provider

* chore(i18n): components.settings.externalCors.*

* chore(i18n): components.settings.clash.*

* chore(i18n): components.settings.liteMode.*

* chore(i18n): components.settings.backup.*

* chore(i18n): components.settings.clash.port.*

* chore(i18n): components.settings.misc.*

* chore(i18n): components.settings.update.*

* chore(i18n): components.settings.sysproxy.*

* chore(i18n): components.settings.sysproxy.*

* chore(i18n): pages.profiles.notices/components.providers.notices

* refactor(notice): unify showNotice usage

* refactor(notice): add typed showNotice shortcuts, centralize defaults, and simplify subscriptions

* refactor: unify showNotice usage

* refactor(notice): unify showNotice API

* refactor(notice): unify showNotice usage

* chore(i18n): components.test.*

* chore(i18n): components.settings.dns.*

* chore(i18n): components.home.clashInfo.*

* chore(i18n): components.home.systemInfo.*

* chore(i18n): components.home.ipInfo/traffic.*

* chore(i18n): navigation.*

* refactor(i18n): remove pages.* namespace and migrate route texts under module-level page keys

* chore(i18n): common.*

* chore(i18n): common.*

* fix: change error handling in patch_profiles_config to return false when a switch is in progress

* fix: improve error handling in patch_profiles_config to prevent requests during profile switching

* fix: change error handling in patch_profiles_config to return false when a switch is in progress

fix: ensure CURRENT_SWITCHING_PROFILE is reset after config updates in perform_config_update and patch_profiles_config

* chore(i18n): restructure root-level locale keys into namespaces

* chore(i18n): add missing i18n keys

* docs: i18n guide

* chore: adjust i18n

* refactor(i18n): align UI actions and status labels with common keys

* refactor(i18n): unify two-name locale namespaces

* refactor(i18n/components): unify locale keys and update component references

* chore(i18n): add shared and entities namespaces to all locale files

* refactor(i18n): consolidate shared and entity namespaces across features

* chore(deps): update npm dependencies to ^7.3.5 (#5310)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* refactor(i18n): migrate shared editor modes and consolidate entities namespaces

* tmp

* refactor(i18n): flatten locales and move theme/validation strings

* docs: CONTRIBUTING_i18n.md

* refactor(i18n): restructure feedback and profile namespaces for better organization

* refactor(i18n): unify settings locale structure and update references

* refactor(i18n): reorganize locale keys for home, proxies, rules, connections, logs, unlock, and tests

* refactor(i18n/feedback/layout): unify shared toasts & normalize layout namespace

* refactor(i18n): centralize common UI strings in shared

* refactor(i18n): flatten headers and unify locale schema

* refactor(i18n): consolidate duplicate per-feature translations into shared namespace

* refactor(i18n): split locales into per-namespace files

* style: lint

* refactor(i18n): unify unlock UI translations under tests namespace

* feat(i18n): add type-checked translation keys

* style: eslint import order

* feat(i18n): replace ad-hoc loader with rust-i18n backend bundles

* chore(prebuild): remove locale-copy step

* fix(i18n, notice): propagate runtime params and update cleanup script path

* fix(i18n,notice): make locale formatting idempotent and guard early notice translations

* fix(i18n): resolve locale aliases and match OS codes correctly

* fix(unlock): use i18next-compatible double-brace interpolation in failure notice

* fix(i18n): route unlock error notices through translation keys

* fix(i18n): i18n types

* feat(i18n): localize upgrade notice for Clash core viewer

* fix(notice): ensure runtime overrides apply to prefix translations

* chore(i18n): replace literal notices with translation keys

* chore(i18n): types

* chore(i18n): regen typings before formatting to keep keys in sync

* chore(i18n): simply labels

* chore(i18n): adjust translation

* chore: remove eslint-plugin-i18next

* chore(i18n): add/refine Korean translations across frontend scopes and Rust backend (#5341)

* chore(i18n): translate settings.json (missed in previous pass) (#5343)

* chore(i18n): add/refine Korean translations across frontend scopes and Rust backend

* chore(i18n): add/refine Korean translations across frontend scopes and Rust backend

* fix(i18n-tauri): quote placeholder-leading value in ko.yml to prevent rust_i18n parse panic

* chore(i18n): translate settings.json (forgot to include previously)

---------

Co-authored-by: rozan <34974262+thelojan@users.noreply.github.com>
2025-11-08 19:40:38 +08:00
Tunglies c1a2e99f63 feat: migrate from react-monaco-editor to @monaco-editor/react and update editor components (#5263)
* feat: migrate from react-monaco-editor to @monaco-editor/react and update editor components

* chore: add acknowledgments for contributors in update log

* feat: enhance frontend information editing component for improved performance
2025-11-08 19:32:26 +08:00
Tunglies 9d874e5c6f chore: update version to 2.4.4 in package.json, Cargo.toml, Cargo.lock, tauri.conf.json, and add new changelog section 2025-11-08 19:16:51 +08:00
Tunglies fa74e79f40 fix: update version from 2.4.3-rc to 2.4.3 in package.json, Cargo.toml, Cargo.lock, and tauri.conf.json 2025-11-08 19:10:29 +08:00
Tunglies 6017423a49 chore: update version to 2.4.3-rc and enhance release notes 2025-11-08 18:31:22 +08:00
Tunglies 538cba5a33 fix: return correct type in get_profiles function 2025-11-08 17:07:48 +08:00
oomeow 76bf91ff8a fix: import profile panic (#5346)
* fix: import profile panic

docs: update UPDATELOG.md

chore: update

* perf: spawn import profile
2025-11-07 22:46:30 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 758c21e656 chore(deps): update dependency vitest to ^4.0.8 (#5349)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-07 21:51:09 +08:00
Slinetrac 5759bed687 refactor(linux): replace users crate with libc::geteuid for root detection
Closes #5333
2025-11-07 18:58:13 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 9cc2f44379 chore(deps): update dependency vite to ^7.2.2 (#5342)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-07 16:22:28 +08:00
rozanandSlinetrac aa6a1c8650 add Korean README and update language selectors (#5338)
* docs(i18n): add Korean README and update language selectors

* style: prettier

---------

Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-11-07 12:06:48 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 5b3dbaaec6 chore(deps): update dependency i18next to ^25.6.1 (#5331)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-07 10:49:11 +08:00
oomeow 81e35e2ead chore: optimize (#5319)
* chore: optimize and reduce spawn

* chore: optimize
2025-11-07 06:06:09 +08:00
Tunglies 19b3899a1b refactor: improve notification handling and avoid backend loop with empty messages 2025-11-07 00:54:16 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 333440535b chore(deps): update dependency vite to ^7.2.1 (#5324)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-06 14:17:21 +08:00
Tunglies 409b16b49f refactor: streamline admin check logic and improve get_running_mode return type (#5325) 2025-11-06 13:37:11 +08:00
Tunglies 9a1465ec4d refactor: replace AtomicI64 with Instant for app start time tracking and simplify uptime calculation 2025-11-06 12:59:05 +08:00
Tunglies 04fc9640c6 refactor: move handle_window_destroyed function under macOS conditional compilation 2025-11-06 11:31:15 +08:00
Slinetrac 966693c9cb fix: clippy lint 2025-11-06 11:21:12 +08:00
Tunglies 363fa98891 refactor: enhance YouTube Premium check logic and streamline response handling 2025-11-06 11:03:35 +08:00
Tunglies a0f7fb7952 refactor: update linting rules and optimize directory handling in Unix 2025-11-06 10:52:14 +08:00
Tunglies 5a8e83cd49 refactor: change function definitions to const for improved performance and clarity 2025-11-06 10:47:25 +08:00
Tunglies aaf3ebe547 refactor: enhance error handling in Timer and update server argument collection 2025-11-06 10:42:21 +08:00
Tunglies 651513c826 refactor: optimize error message handling and improve cloning in various functions 2025-11-06 10:26:40 +08:00
Tunglies 69a706b438 refactor: streamline error handling and resource management in various modules 2025-11-06 10:19:06 +08:00
Tunglies 671ac2ebed refactor: replace type references with Self in various structs and methods for consistency 2025-11-06 10:19:06 +08:00
Tunglies 70236f781c refactor: simplify uid assignment in PrfItem from_merge and from_script methods 2025-11-06 10:19:05 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> ccffc0a4fa chore(deps): update npm dependencies (#5321)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-06 09:36:30 +08:00
Tunglies 21de5cbb0b refactor: remove unnecessary emit calls in switch_proxy_node and enhance profile switch notifications 2025-11-06 00:30:43 +08:00
Tunglies 6eaf999240 refactor: optimize worker loop to use try_recv for non-blocking event processing #5318 2025-11-05 22:59:22 +08:00
Tunglies 1bd2bde096 refactor: streamline lightweight mode initialization and logging levels 2025-11-05 18:57:04 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> cf89ac381a chore(deps): update npm dependencies (#5317)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-05 18:47:43 +08:00
Tunglies ea41e71f72 refactor: replace EncryptionGuard with with_encryption for better async context handling 2025-11-05 16:56:04 +08:00
Tunglies 8c3e9c9ea9 feat: implement encryption guard for secure data handling in YAML operations 2025-11-05 15:05:19 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 7145096729 chore(deps): update npm dependencies (#5311)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-05 12:13:07 +08:00
Tunglies 3e2f605e77 fix: improve error handling and logging in various modules 2025-11-05 02:11:43 +08:00
Tunglies 28483ff9db fix: simplify error handling in toggle_proxy_profile by logging errors directly 2025-11-04 23:05:03 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 5c207a8a7d chore(deps): update npm dependencies to ^7.3.5 (#5310)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 23:05:03 +08:00
Tunglies ab136e463f fix: change error handling in patch_profiles_config to return false when a switch is in progress
fix: improve error handling in patch_profiles_config to prevent requests during profile switching

fix: change error handling in patch_profiles_config to return false when a switch is in progress

fix: ensure CURRENT_SWITCHING_PROFILE is reset after config updates in perform_config_update and patch_profiles_config
2025-11-04 23:05:01 +08:00
Tunglies 97769cf93a feat: add methods to retrieve current subscription details in PrfItem and refactor profile handling in IProfiles 2025-11-04 12:03:03 +08:00
Tunglies 0306f73841 fix: replace update_tray_display with update_menu for consistency across tray updates 2025-11-04 12:03:03 +08:00
Tunglies 0a03867352 fix: optimize ownership handling in patch macro for better performance 2025-11-04 12:03:03 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 28a78464e1 chore(deps): update npm dependencies (#5303)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 12:00:47 +08:00
Tunglies bae584b1ab perf: optimize profile handle memory usage 2025-11-04 10:05:17 +08:00
Tunglies a5e6a35dea fix: update IVerge struct to skip serializing optional fields and adjust types for latency timeout and proxy layout column 2025-11-04 08:16:02 +08:00
Tunglies b86ceb26f6 fix: streamline verge configuration fetching and patching functions 2025-11-04 08:01:33 +08:00
Tunglies b70d45b66a fix: update profile handling to apply and discard changes correctly 2025-11-04 07:58:14 +08:00
Tunglies ebd7f457d2 fix: improve error handling in port settings save function #4707 2025-11-04 07:17:14 +08:00
Tunglies 72d94c62f4 Revert "modify the port detection range to prevent the use of system ports (#3587)"
This reverts commit d3dbc11b1b.
2025-11-04 07:11:44 +08:00
Tunglies fe757ed984 docs(autobuild): Assets URL correction for rpm package 2025-11-04 06:23:53 +08:00
Tunglies 2287ea5f0b Refactor configuration access to use latest_arc() instead of latest_ref()
- Updated multiple instances in the codebase to replace calls to latest_ref() with latest_arc() for improved performance and memory management.
- This change affects various modules including validate, enhance, feat (backup, clash, config, profile, proxy, window), utils (draft, i18n, init, network, resolve, server).
- Ensured that all references to configuration data are now using the new arc-based approach to enhance concurrency and reduce cloning overhead.

refactor: update imports to explicitly include ClashInfo and Config in command files
2025-11-04 06:06:20 +08:00
Slinetrac 48a19f99e2 docs: UPDATELOG.md 2025-11-03 20:54:46 +08:00
❤是纱雾酱哟~ 1003118468 docs(release): Assets URL correction (#5294)
- Fix mis-spelling of assets URL to Linux RPM package
2025-11-03 20:45:06 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 940717712a chore(deps): update dependency @types/node to ^24.10.0 (#5292)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 20:44:58 +08:00
Tunglies 4babc72754 feat: integrate arc-swap for improved concurrency in CoreManager and Hotkey(onMac) management 2025-11-03 12:32:36 +08:00
Tunglies 54039c2448 fix: correct download URL for ARM64 Windows setup in autobuild.yml 2025-11-03 09:24:27 +08:00
Tunglies 5d0b18eecd docs: update contributor acknowledgment in UPDATELOG.md 2025-11-03 09:20:52 +08:00
Tunglies 8f4c0e823b refactor: remove update semaphore from CoreManager and related config update logic 2025-11-03 08:53:55 +08:00
Tunglies 501def6695 refactor: update patch_config method to accept a reference to IProfiles 2025-11-03 08:46:37 +08:00
Tunglies dce349586c refactor: simplify profile retrieval and remove unused template method 2025-11-03 03:17:33 +08:00
Slinetrac ed08fadb5a fix: refresh_lightweight_tray_state #5285 2025-11-02 23:42:26 +08:00
Tunglies 34cac0fa3a feat: improve subscription update method and enhance error handling for profile updates 2025-11-02 22:41:51 +08:00
oomeow d4cb16f4ff perf: select proxy (#5284)
* perf: improve select proxy for group

* chore: update
2025-11-02 22:33:50 +08:00
Tunglies 36d1a3878f refactor: remove retry notice for Clash proxy updates from notification handlers and localization files 2025-11-02 20:13:09 +08:00
Tunglies fb5bf72fb9 feat: add function to retrieve profile name by UID and improve error logging for profile updates 2025-11-02 20:07:47 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 85244a8f86 chore(deps): update dependency @eslint-react/eslint-plugin to ^2.3.1 (#5281)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-02 18:27:58 +08:00
Tunglies 4a7859bdae refactor: replace hardcoded DNS config filename with constant reference (#5280)
* refactor: replace hardcoded DNS config filename with constant reference

* refactor: remove redundant import of constants in IClashTemp template method

* refactor: add conditional compilation for DEFAULT_REDIR based on OS

* refactor: simplify default TPROXY port handling and remove unused trace_err macro

* refactor: simplify default TPROXY port fallback logic
2025-11-01 22:50:19 +08:00
Tunglies c0f9920531 refactor: remove orphaned process cleanup functionality
It might breaks mihomo starting.

Due to potentiall process name processing, permissions verifing, permissions and safty FORCE KILL, find process faillure.
2025-11-01 22:11:16 +08:00
Tunglies d3d32006c3 feat: add logging check to pre-commit and CI workflow 2025-11-01 21:22:41 +08:00
Tunglies fb260fb33d Refactor logging to use a centralized logging utility across the application (#5277)
- Replaced direct log calls with a new logging macro that includes a logging type for better categorization.
- Updated logging in various modules including `merge.rs`, `mod.rs`, `tun.rs`, `clash.rs`, `profile.rs`, `proxy.rs`, `window.rs`, `lightweight.rs`, `guard.rs`, `autostart.rs`, `dirs.rs`, `dns.rs`, `scheme.rs`, `server.rs`, and `window_manager.rs`.
- Introduced logging types such as `Core`, `Network`, `ProxyMode`, `Window`, `Lightweight`, `Service`, and `File` to enhance log clarity and filtering.
2025-11-01 20:47:01 +08:00
Slinetrac 50567d9b97 refactor(profiles): remove import verification and simplify post-import refresh 2025-11-01 20:09:00 +08:00
Tunglies 9370a56337 refactor: reduce clone operation (#5268)
* refactor: optimize item handling and improve profile management

* refactor: update IVerge references to use references instead of owned values

* refactor: update patch_verge to use data_ref for improved data handling

* refactor: move handle_copy function to improve resource initialization logic

* refactor: update profile handling to use references for improved memory efficiency

* refactor: simplify get_item method and update profile item retrieval to use string slices

* refactor: update profile validation and patching to use references for improved performance

* refactor: update profile functions to use references for improved performance and memory efficiency

* refactor: update profile patching functions to use references for improved memory efficiency

* refactor: simplify merge function in PrfOption to enhance readability

* refactor: update change_core function to accept a reference for improved memory efficiency

* refactor: update PrfItem and profile functions to use references for improved memory efficiency

* refactor: update resolve_scheme function to accept a reference for improved memory efficiency

* refactor: update resolve_scheme function to accept a string slice for improved flexibility

* refactor: simplify update_profile parameters and logic
2025-11-01 20:03:56 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 73e53eb33f chore(deps): update npm dependencies (#5278)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-01 19:42:34 +08:00
Slinetrac 30d1655e07 docs: UPDATELOG.md 2025-11-01 19:25:04 +08:00
Slinetrac 9dc50da167 fix: profile auto refresh #5274 2025-11-01 19:24:54 +08:00
Tunglies b3b8eeb577 refactor: convert file operations to async using tokio fs (#5267)
* refactor: convert file operations to async using tokio fs

* refactor: integrate AsyncHandler for file operations in backup processes
2025-11-01 19:24:52 +08:00
Sline 413f29e22a fix: linux theme sync (#5273) 2025-11-01 19:24:47 +08:00
Tunglies ae319279ae chore(deps): update cc, clash_verge_logger, and version-compare to latest versions 2025-11-01 10:15:12 +08:00
Tunglies c0e111e756 fix: resolve macOS lightweight mode exit synchronization issues and improve logging levels #5241 2025-11-01 10:09:51 +08:00
Slinetrac 52545a626c chore(lint): enforce no warnings in pre hooks 2025-11-01 09:49:52 +08:00
Tunglies 518875acde refactor: update draft handling and improve benchmark structure 2025-10-31 23:31:04 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> b672dd7055 chore(deps): update dependency sass to ^1.93.3 (#5265)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-31 22:54:52 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 804641425b chore(deps): update dependency vitest to ^4.0.6 (#5264)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-31 22:51:12 +08:00
Tunglies d3386908ff fix: improve caching strategy for autobuild jobs 2025-10-31 19:48:36 +08:00
Slinetrac 59e7095b0f chore: up sysproxy git hash 2025-10-31 19:20:07 +08:00
Tunglies 8fc8eb1789 chore: add acknowledgments for contributors in update log 2025-10-31 18:15:46 +08:00
Slinetrac 0f1537ef48 chore: up Cargo.lock 2025-10-31 17:36:33 +08:00
oomeowandTunglies 8c734a5a35 fix: disable tun mode menu on tray when tun mode is unavailable (#4975)
* fix: check if service installed when toggle tun mode on tray

* chore: cargo fmt

* fix: auto disable tun mode

* docs: update UPDATELOG.md

* fix: init Tun mode status

* chore: update

* feat: disable tun mode tray menu when tun mode is unavailable

* fix: restart core when uninstall service is canceled

* chore: remove check notification when toggle tun mode

* chore: fix updatelog

---------

Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2025-10-31 17:31:40 +08:00
Tunglies 5187712a71 chore(deps): remove zustand and update vite-plugin-monaco-editor to esm version 2025-10-31 16:55:52 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 7b7fa2239b chore(deps): update dependency dayjs to v1.11.19 (#5261)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-31 13:36:35 +08:00
Slinetrac 85ff296912 chore(deps): update deps 2025-10-31 11:28:14 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 5e7adf76ca chore(deps): update npm dependencies (#5258)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-31 10:46:42 +08:00
Slinetrac d094d3885c chore: move CONTRIBUTING_i18n.md to /docs 2025-10-31 10:41:48 +08:00
Tunglies 648c93c066 chore(i18n): update localization files sorting and add i18n contribution guidline
- Add i18n contribution guidline
- Chinese (zh.json): fix TPROXY port missing translation
2025-10-31 05:15:47 +08:00
Tunglies 1e9df69ffc fix: remove unused dependencies from Cargo.toml and Cargo.lock 2025-10-31 00:55:50 +08:00
Tunglies ef35752d84 fix: specify version for sysproxy dependency in Cargo.toml 2025-10-31 00:33:11 +08:00
Tunglies ffb7400a22 fix: add updateCargoLock to postUpdateOptions in renovate.json 2025-10-31 00:11:55 +08:00
oomeow 4d5f1f4327 fix: incorrect proxies route 2025-10-30 20:28:56 +08:00
Tunglies 999830aaf5 fix: correct download link for ARM64 Windows setup in autobuild workflow 2025-10-30 20:18:23 +08:00
Tunglies 6d7efbbf28 fix: reorder import statements and enhance normalizeDetailsTags function 2025-10-30 20:08:57 +08:00
Tunglies a869dbb441 Revert "refactor: profile switch (#5197)"
This reverts commit c2dcd86722.
2025-10-30 18:11:04 +08:00
Tunglies 928f226d10 fix: update clash_verge_service_ipc version to 2.0.21 2025-10-30 18:02:24 +08:00
Tunglies c27ad3fdcb feat: add log opening functionality in tray menu and update localization 2025-10-30 17:34:41 +08:00
Sline c2dcd86722 refactor: profile switch (#5197)
* refactor: proxy refresh

* fix(proxy-store): properly hydrate and filter backend provider snapshots

* fix(proxy-store): add monotonic fetch guard and event bridge cleanup

* fix(proxy-store): tweak fetch sequencing guard to prevent snapshot invalidation from wiping fast responses

* docs: UPDATELOG.md

* fix(proxy-snapshot, proxy-groups): restore last-selected proxy and group info

* fix(proxy): merge static and provider entries in snapshot; fix Virtuoso viewport height

* fix(proxy-groups): restrict reduced-height viewport to chain-mode column

* refactor(profiles): introduce a state machine

* refactor:replace state machine with reducer

* refactor:introduce a profile switch worker

* refactor: hooked up a backend-driven profile switch flow

* refactor(profile-switch): serialize switches with async queue and enrich frontend events

* feat(profiles): centralize profile switching with reducer/driver queue to fix stuck UI on rapid toggles

* chore: translate comments and log messages to English to avoid encoding issues

* refactor: migrate backend queue to SwitchDriver actor

* fix(profile): unify error string types in validation helper

* refactor(profile): make switch driver fully async and handle panics safely

* refactor(cmd): move switch-validation helper into new profile_switch module

* refactor(profile): modularize switch logic into profile_switch.rs

* refactor(profile_switch): modularize switch handler

- Break monolithic switch handler into proper module hierarchy
- Move shared globals, constants, and SwitchScope guard to state.rs
- Isolate queue orchestration and async task spawning in driver.rs
- Consolidate switch pipeline and config patching in workflow.rs
- Extract request pre-checks/YAML validation into validation.rs

* refactor(profile_switch): centralize state management and add cancellation flow

- Introduced SwitchManager in state.rs to unify mutex, sequencing, and SwitchScope handling.
- Added SwitchCancellation and SwitchRequest wrappers to encapsulate cancel tokens and notifications.
- Updated driver to allocate task IDs via SwitchManager, cancel old tokens, and queue next jobs in order.
- Updated workflow to check cancellation and sequence at each phase, replacing global flags with manager APIs.

* feat(profile_switch): integrate explicit state machine for profile switching

- workflow.rs:24 now delegates each switch to SwitchStateMachine, passing an owned SwitchRequest.
  Queue cancellation and state-sequence checks are centralized inside the machine instead of scattered guards.
- workflow.rs:176 replaces the old helper with `SwitchStateMachine::new(manager(), None, profiles).run().await`,
  ensuring manual profile patches follow the same workflow (locking, validation, rollback) as queued switches.
- workflow.rs:180 & 275 expose `validate_profile_yaml` and `restore_previous_profile` for reuse inside the state machine.

- workflow/state_machine.rs:1 introduces a dedicated state machine module.
  It manages global mutex acquisition, request/cancellation state, YAML validation, draft patching,
  `CoreManager::update_config`, failure rollback, and tray/notification side-effects.
  Transitions check for cancellations and stale sequences; completions release guards via `SwitchScope` drop.

* refactor(profile-switch): integrate stage-aware panic handling

- src-tauri/src/cmd/profile_switch/workflow/state_machine.rs:1
  Defines SwitchStage and SwitchPanicInfo as crate-visible, wraps each transition in with_stage(...) with catch_unwind, and propagates CmdResult<bool> to distinguish validation failures from panics while keeping cancellation semantics.

- src-tauri/src/cmd/profile_switch/workflow.rs:25
  Updates run_switch_job to return Result<bool, SwitchPanicInfo>, routing timeout, validation, config, and stage panic cases separately. Reuses SwitchPanicInfo for logging/UI notifications; patch_profiles_config maps state-machine panics into user-facing error strings.

- src-tauri/src/cmd/profile_switch/driver.rs:1
  Adds SwitchJobOutcome to unify workflow results: normal completions carry bool, and panics propagate SwitchPanicInfo. The driver loop now logs panics explicitly and uses AssertUnwindSafe(...).catch_unwind() to guard setup-phase panics.

* refactor(profile-switch): add watchdog, heartbeat, and async timeout guards

- Introduce SwitchHeartbeat for stage tracking and timing; log stage transitions with elapsed durations.
- Add watchdog in driver to cancel stalled switches (5s heartbeat timeout).
- Wrap blocking ops (Config::apply, tray updates, profiles_save_file_safe, etc.) with time::timeout to prevent async stalls.
- Improve logs for stage transitions and watchdog timeouts to clarify cancellation points.

* refactor(profile-switch): async post-switch tasks, early lock release, and spawn_blocking for IO

* feat(profile-switch): track cleanup and coordinate pipeline

- Add explicit cleanup tracking in the driver (`cleanup_profiles` map + `CleanupDone` messages) to know when background post-switch work is still running before starting a new workflow. (driver.rs:29-50)
- Update `handle_enqueue` to detect “cleanup in progress”: same-profile retries are short-circuited; other requests collapse the pending queue, cancelling old tokens so only the latest intent survives. (driver.rs:176-247)
- Rework scheduling helpers: `start_next_job` refuses to start while cleanup is outstanding; discarded requests release cancellation tokens; cleanup completion explicitly restarts the pipeline. (driver.rs:258-442)

* feat(profile-switch): unify post-switch cleanup handling

- workflow.rs (25-427) returns `SwitchWorkflowResult` (success + CleanupHandle) or `SwitchWorkflowError`.
  All failure/timeout paths stash post-switch work into a single CleanupHandle.
  Cleanup helpers (`notify_profile_switch_finished` and `close_connections_after_switch`) run inside that task for proper lifetime handling.

- driver.rs (29-439) propagates CleanupHandle through `SwitchJobOutcome`, spawns a bridge to wait for completion, and blocks `start_next_job` until done.
  Direct driver-side panics now schedule failure cleanup via the shared helper.

* tmp

* Revert "tmp"

This reverts commit e582cf4a65.

* refactor: queue frontend events through async dispatcher

* refactor: queue frontend switch/proxy events and throttle notices

* chore: frontend debug log

* fix: re-enable only ProfileSwitchFinished events - keep others suppressed for crash isolation

- Re-enabled only ProfileSwitchFinished events; RefreshClash, RefreshProxy, and ProfileChanged remain suppressed (they log suppression messages)
- Allows frontend to receive task completion notifications for UI feedback while crash isolation continues
- src-tauri/src/core/handle.rs now only suppresses notify_profile_changed
- Serialized emitter, frontend logging bridge, and other diagnostics unchanged

* refactor: refreshClashData

* refactor(proxy): stabilize proxy switch pipeline and rendering

- Add coalescing buffer in notification.rs to emit only the latest proxies-updated snapshot
- Replace nextTick with queueMicrotask in asyncQueue.ts for same-frame hydration
- Hide auto-generated GLOBAL snapshot and preserve optional metadata in proxy-snapshot.ts
- Introduce stable proxy rendering state in AppDataProvider (proxyTargetProfileId, proxyDisplayProfileId, isProxyRefreshPending)
- Update proxy page to fade content during refresh and overlay status banner instead of showing incomplete snapshot

* refactor(profiles): move manual activating logic to reducer for deterministic queue tracking

* refactor: replace proxy-data event bridge with pure polling and simplify proxy store

- Replaced the proxy-data event bridge with pure polling: AppDataProvider now fetches the initial snapshot and drives refreshes from the polled switchStatus, removing verge://refresh-* listeners (src/providers/app-data-provider.tsx).
- Simplified proxy-store by dropping the proxies-updated listener queue and unused payload/normalizer helpers; relies on SWR/provider fetch path + calcuProxies for live updates (src/stores/proxy-store.ts).
- Trimmed layout-level event wiring to keep only notice/show/hide subscriptions, removing obsolete refresh listeners (src/pages/_layout/useLayoutEvents.ts).

* refactor(proxy): streamline proxies-updated handling and store event flow

- AppDataProvider now treats `proxies-updated` as the fast path: the listener
  calls `applyLiveProxyPayload` immediately and schedules only a single fallback
  `fetchLiveProxies` ~600 ms later (replacing the old 0/250/1000/2000 cascade).
  Expensive provider/rule refreshes run in parallel via `Promise.allSettled`, and
  the multi-stage queue on profile updates completion was removed
  (src/providers/app-data-provider.tsx).

- Rebuilt proxy-store to support the event flow: restored `setLive`, provider
  normalization, and an animation-frame + async queue that applies payloads without
  blocking. Exposed `applyLiveProxyPayload` so providers can push events directly
  into the store (src/stores/proxy-store.ts).

* refactor: switch delay

* refactor(app-data-provider): trigger getProfileSwitchStatus revalidation on profile-switch-finished

- AppDataProvider now listens to `profile-switch-finished` and calls `mutate("getProfileSwitchStatus")` to immediately update state and unlock buttons (src/providers/app-data-provider.tsx).
- Retain existing detailed timing logs for monitoring other stages.
- Frontend success notifications remain instant; background refreshes continue asynchronously.

* fix(profiles): prevent duplicate toast on page remount

* refactor(profile-switch): make active switches preemptible and prevent queue piling

- Add notify mechanism to SwitchCancellation to await cancellation without busy-waiting (state.rs:82)
- Collapse pending queue to a single entry in the driver; cancel in-flight task on newer request (driver.rs:232)
- Update handle_update_core to watch cancel token and 30s timeout; release locks, discard draft, and exit early if canceled (state_machine.rs:301)
- Providers revalidate status immediately on profile-switch-finished events (app-data-provider.tsx:208)

* refactor(core): make core reload phase controllable, reduce 0xcfffffff risk

- CoreManager::apply_config now calls `reload_config_with_retry`, each attempt waits up to 5s, retries 3 times; on failure, returns error with duration logged and triggers core restart if needed (src-tauri/src/core/manager/config.rs:175, 205)
- `reload_config_with_retry` logs attempt info on timeout or error; if error is a Mihomo connection issue, fallback to original restart logic (src-tauri/src/core/manager/config.rs:211)
- `reload_config_once` retains original Mihomo call for retry wrapper usage (src-tauri/src/core/manager/config.rs:247)

* chore(frontend-logs): downgrade routine event logs from info to debug

- Logs like `emit_via_app entering spawn_blocking`, `Async emit…`, `Buffered proxies…` are now debug-level (src-tauri/src/core/notification.rs:155, :265, :309…)
- Genuine warnings/errors (failures/timeouts) remain at warn/error
- Core stage logs remain info to keep backend tracking visible

* refactor(frontend-emit): make emit_via_app fire-and-forget async

- `emit_via_app` now a regular function; spawns with `tokio::spawn` and logs a warn if `emit_to` fails, caller returns immediately (src-tauri/src/core/notification.rs:269)
- Removed `.await` at Async emit and flush_proxies calls; only record dispatch duration and warn on failure (src-tauri/src/core/notification.rs:211, :329)

* refactor(ui): restructure profile switch for event-driven speed + polling stability

- Backend
  - SwitchManager maintains a lightweight event queue: added `event_sequence`, `recent_events`, and `SwitchResultEvent`; provides `push_event` / `events_after` (state.rs)
  - `handle_completion` pushes events on success/failure and keeps `last_result` (driver.rs) for frontend incremental fetch
  - New Tauri command `get_profile_switch_events(after_sequence)` exposes `events_after` (profile_switch/mod.rs → profile.rs → lib.rs)
- Notification system
  - `NotificationSystem::process_event` only logs debug, disables WebView `emit_to`, fixes 0xcfffffff
  - Related emit/buffer functions now safe no-op, removed unused structures and warnings (notification.rs)
- Frontend
  - services/cmds.ts defines `SwitchResultEvent` and `getProfileSwitchEvents`
  - `AppDataProvider` holds `switchEventSeqRef`, polls incremental events every 0.25s (busy) / 1s (idle); each event triggers:
      - immediate `globalMutate("getProfiles")` to refresh current profile
      - background refresh of proxies/providers/rules via `Promise.allSettled` (failures logged, non-blocking)
      - forced `mutateSwitchStatus` to correct state
  - original switchStatus effect calls `handleSwitchResult` as fallback; other toast/activation logic handled in profiles.tsx
- Commands / API cleanup
  - removed `pub use profile_switch::*;` in cmd::mod.rs to avoid conflicts; frontend uses new command polling

* refactor(frontend): optimize profile switch with optimistic updates

* refactor(profile-switch): switch to event-driven flow with Profile Store

- SwitchManager pushes events; frontend polls get_profile_switch_events
- Zustand store handles optimistic profiles; AppDataProvider applies updates and background-fetches
- UI flicker removed

* fix(app-data): re-hook profile store updates during switch hydration

* fix(notification): restore frontend event dispatch and non-blocking emits

* fix(app-data-provider): restore proxy refresh and seed snapshot after refactor

* fix: ensure switch completion events are received and handle proxies-updated

* fix(app-data-provider): dedupe switch results by taskId and fix stale profile state

* fix(profile-switch): ensure patch_profiles_config_by_profile_index waits for real completion and handle join failures in apply_config_with_timeout

* docs: UPDATELOG.md

* chore: add necessary comments

* fix(core): always dispatch async proxy snapshot after RefreshClash event

* fix(proxy-store, provider): handle pending snapshots and proxy profiles

- Added pending snapshot tracking in proxy-store so `lastAppliedFetchId` no longer jumps on seed. Profile adoption is deferred until a qualifying fetch completes. Exposed `clearPendingProfile` for rollback support.
- Cleared pending snapshot state whenever live payloads apply or the store resets, preventing stale optimistic profile IDs after failures.
- In provider integration, subscribed to the pending proxy profile and fed it into target-profile derivation. Cleared it on failed switch results so hydration can advance and UI status remains accurate.

* fix(proxy): re-hook tray refresh events into proxy refresh queue

- Reattached listen("verge://refresh-proxy-config", …) at src/providers/app-data-provider.tsx:402 and registered it for cleanup.
- Added matching window fallback handler at src/providers/app-data-provider.tsx:430 so in-app dispatches share the same refresh path.

* fix(proxy-snapshot/proxy-groups): address review findings on snapshot placeholders

- src/utils/proxy-snapshot.ts:72-95 now derives snapshot group members solely from proxy-groups.proxies, so provider ids under `use` no longer generate placeholder proxy items.
- src/components/proxy/proxy-groups.tsx:665-677 lets the hydration overlay capture pointer events (and shows a wait cursor) so users can’t interact with snapshot-only placeholders before live data is ready.

* fix(profile-switch): preserve queued requests and avoid stale connection teardown

- Keep earlier queued switches intact by dropping the blanket “collapse” call: after removing duplicates for the same profile, new requests are simply appended, leaving other profiles pending (driver.rs:376). Resolves queue-loss scenario.
- Gate connection cleanup on real successes so cancelled/stale runs no longer tear down Mihomo connections; success handler now skips close_connections_after_switch when success == false (workflow.rs:419).

* fix(profile-switch, layout): improve profile validation and restore backend refresh

- Hardened profile validation using `tokio::fs` with a 5s timeout and offloading YAML parsing to `AsyncHandler::spawn_blocking`, preventing slow disks or malformed files from freezing the runtime (src-tauri/src/cmd/profile_switch/validation.rs:9, 71).
- Restored backend-triggered refresh handling by listening for `verge://refresh-clash-config` / `verge://refresh-verge-config` and invoking shared refresh services so SWR caches stay in sync with core events (src/pages/_layout/useLayoutEvents.ts:6, 45, 55).

* feat(profile-switch): handle cancellations for superseded requests

- Added a `cancelled` flag and constructor so superseded requests publish an explicit cancellation instead of a failure (src-tauri/src/cmd/profile_switch/state.rs:249, src-tauri/src/cmd/profile_switch/driver.rs:482)
- Updated the profile switch effect to log cancellations as info, retain the shared `mutate` call, and skip emitting error toasts while still refreshing follow-up work (src/pages/profiles.tsx:554, src/pages/profiles.tsx:581)
- Exposed the new flag on the TypeScript contract to keep downstream consumers type-safe (src/services/cmds.ts:20)

* fix(profiles): wrap logging payload for Tauri frontend_log

* fix(profile-switch): add rollback and error propagation for failed persistence

- Added rollback on apply failure so Mihomo restores to the previous profile
  before exiting the success path early (state_machine.rs:474).
- Reworked persist_profiles_with_timeout to surface timeout/join/save errors,
  convert them into CmdResult failures, and trigger rollback + error propagation
  when persistence fails (state_machine.rs:703).

* fix(profile-switch): prevent mid-finalize reentrancy and lingering tasks

* fix(profile-switch): preserve pending queue and surface discarded switches

* fix(profile-switch): avoid draining Mihomo sockets on failed/cancelled switches

* fix(app-data-provider): restore backend-driven refresh and reattach fallbacks

* fix(profile-switch): queue concurrent updates and add bounded wait/backoff

* fix(proxy): trigger live refresh on app start for proxy snapshot

* refactor(profile-switch): split flow into layers and centralize async cleanup

- Introduced `SwitchDriver` to encapsulate queue and driver logic while keeping the public Tauri command API.
- Added workflow/cleanup helpers for notification dispatch and Mihomo connection draining, re-exported for API consistency.
- Replaced monolithic state machine with `core.rs`, `context.rs`, and `stages.rs`, plus a thin `mod.rs` re-export layer; stage methods are now individually testable.
- Removed legacy `workflow/state_machine.rs` and adjusted visibility on re-exported types/constants to ensure compilation.
2025-10-30 17:29:15 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> af79bcd1cf chore(deps): update dependency react-i18next to v16.2.2 (#5251)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 17:12:20 +08:00
Tunglies 2d73afdff2 style: update UPDATELOG.md using details and summary 2025-10-30 17:00:00 +08:00
e7a9f8f755 add support x-oss-meta-subscription-userinfo (#5234)
* add support x-oss-meta-subscription-userinfo

* Update prfitem.rs

match any subscription-userinfo

* Update prfitem.rs

改为 ends_with 更好

* feat(config): enforce stricter header match for subscription usage

---------

Co-authored-by: i18n <i18n.site@gmail.com>
Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-10-30 11:24:40 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> d209238009 chore(deps): update npm dependencies (#5245)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 10:24:35 +08:00
Sukka dfcdb33e58 chore: use vite-swc-react (#5246) 2025-10-30 10:19:29 +08:00
Tunglies 37359ffc27 fix: add check for allow_auto_update in timer task filtering 2025-10-30 01:40:43 +08:00
oomeow fb09e6c85d fix: notification can not notify frontend (#5243) 2025-10-30 00:34:45 +08:00
oomeow d10665091b chore: update eslint ignorePattern 2025-10-29 21:16:18 +08:00
Tunglies d8b0e9929c fix: include Mihomo-go122 by default for macOS 10.15+ to resolve Intel architecture compatibility issues 2025-10-29 21:14:02 +08:00
Tunglies 73323edf06 chore(deps): update clash_verge_service_ipc to version 2.0.20
Reduce memory usage, avoid duplicated clients
2025-10-29 20:35:45 +08:00
Tunglies f4de4738f1 refactor(logger): replace ClashLogger with CLASH_LOGGER and update log handling; improve log retrieval and management 2025-10-29 17:58:02 +08:00
Tunglies 2e9f6dd174 fix: prevent service duplicate start_core and early-return after stop_core; fix start failures
Update clash_verge_service_ipc version to 2.0.18
2025-10-29 16:09:19 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> e928089a77 chore(deps): update npm dependencies (#5231)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-29 08:29:47 +08:00
Tunglies f41998284a fix(verge_patch): add tray_inline_proxy_groups handling to update flags and refresh tray 2025-10-29 02:41:07 +08:00
Tunglies 9375674c91 refactor(validate): simplify validation process management and remove unused code 2025-10-28 19:36:17 +08:00
Tunglies 2a7ccb5bde refactor(core): optimize RunningMode handling and improve state management 2025-10-28 19:16:42 +08:00
Tunglies 2af0af0837 refactor(tray): comment out enable_tray_icon references for future removal #5161
Since network speed display in Tray on menu has been removed
2025-10-28 14:37:57 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 0fcf168b08 chore(deps): update dependency axios to ^1.13.0 (#5225)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-28 10:47:36 +08:00
Tunglies f39436f1d0 refactor(i18n): optimize translation handling with Arc<str> for better memory efficiency
refactor(tray): change menu text storage to use Arc<str> for improved performance
refactor(service): utilize SmartString for error messages to enhance memory management
2025-10-28 00:26:20 +08:00
❤是纱雾酱哟~ a9eb512f20 docs(autobuild): update download links for release assets (#5224)
- To match those in actual "Assets" section

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>
2025-10-28 00:00:01 +08:00
Tunglies 713162ca37 perf(i18n): change TRANSLATIONS type to use Box<Value> for better memory management
This reduce memory usage from 72 to 48
2025-10-27 23:17:40 +08:00
Tunglies 87168b6ce0 perf(tray): improve menu handling localization support
refactor(tray): replace string literals with MenuIds for menu event handling
2025-10-27 23:08:05 +08:00
SlinetracandLiMoon 2ee8d164fd chore(i18n): upload zhtw.json
Co-authored-by: LiMoon <azhe9335@gmail.com>
2025-10-27 21:39:45 +08:00
Tunglies c736796380 feat(clippy): cognitive-complexity rule (#5215)
* feat(config): enhance configuration initialization and validation process

* refactor(profile): streamline profile update logic and enhance error handling

* refactor(config): simplify profile item checks and streamline update flag processing

* refactor(disney_plus): add cognitive complexity allowance for check_disney_plus function

* refactor(enhance): restructure configuration and profile item handling for improved clarity and maintainability

* refactor(tray): add cognitive complexity allowance for create_tray_menu function

* refactor(config): add cognitive complexity allowance for patch_config function

* refactor(profiles): simplify item removal logic by introducing take_item_file_by_uid helper function

* refactor(profile): add new validation logic for profile configuration syntax

* refactor(profiles): improve formatting and readability of take_item_file_by_uid function

* refactor(cargo): change cognitive complexity level from warn to deny

* refactor(cargo): ensure cognitive complexity is denied in Cargo.toml

* refactor(i18n): clean up imports and improve code readability
refactor(proxy): simplify system proxy toggle logic
refactor(service): remove unnecessary `as_str()` conversion in error handling
refactor(tray): modularize tray menu creation for better maintainability

* refactor(tray): update menu item text handling to use references for improved performance
2025-10-27 20:55:51 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 6df1e137f3 chore(deps): update dependency vitest to ^4.0.4 (#5221)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 20:40:13 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 5a29508407 chore(deps): update cargo dependencies (#5217)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 20:39:00 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 45c68424f0 chore(deps): update npm dependencies (#5218)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 20:38:17 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 9426fc1b1c chore(deps): update dependency react-i18next to v16.2.1 (#5216)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 18:34:00 +08:00
Slinetrac c234d1dc16 fix(groups-editor): persist deletions/restorations and normalize YAML
Prevent state resets while editing groups so deletions/restorations persist instead of being overwritten.
Ensure YAML is normalized and the latest visual state is saved.

- Add `normalizeDeleteSeq` to handle legacy `{name: ...}` entries and `buildGroupsYaml` for consistent serialization.
- Guard reassigning `deleteSeq` unless normalized value changes to avoid effect loops.
- Normalize proxy deletions and deduplicate policy names without extra backend writes.
- Split “on open” effect from proxy-policy refresh; toggling delete no longer triggers `fetchContent()`.
- Write composed YAML in `handleSave`, keep `currData`/`prevData` aligned, and provide accurate payloads to `onSave`.
2025-10-27 16:20:47 +08:00
11035db307 feat: add signal handling for graceful shutdown on Windows and Unix (#5023)
* feat: add signal handling for graceful shutdown on Windows and Unix

Co-authored-by: oomeow <oomeow@outlook.com>

* chore: update Cargo.lock

* fix(windows): restore shutdown hook build by enabling missing Win32 APIs and removing stray tracing call

Includes the required windows-sys feature expansions and replaces a leftover tracing reference so the Windows shutdown hook builds successfully.

* fix: add deprecation warnings for encrypt_data and decrypt_data functions

---------

Co-authored-by: oomeow <oomeow@outlook.com>
Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-10-27 14:02:27 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> d2614396da chore(deps): update npm dependencies (#5212)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 13:06:15 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> a2bbb69b73 chore(deps): update cargo dependencies (#5210)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 08:30:10 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> b23b2a95c5 chore(deps): update npm dependencies (#5211)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 08:29:39 +08:00
❤是纱雾酱哟~ 8fa9bcc650 Refactor(zhtw): Refines terminology for consistency (#5189)
* Refactor(zhtw): Refines terminology for consistency

- Updates: "連接" -> "連線"
- Updates: "高級" -> "進階"
- Updates: "局域網連接" -> "區域網路連線"

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>

* chore(i18n): Refine Traditional Chinese translations

- Improves the quality and consistency to match Traditional Chinese localized expressions.

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>

* chore(i18n): Refines Traditional Chinese translations

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>

---------

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>
2025-10-26 14:01:56 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> e544203ca0 chore(deps): update dependency commander to ^14.0.2 (#5205)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-25 21:19:05 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> ed9eed226d chore(deps): update npm dependencies (#5198)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-25 09:59:54 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 806769b307 chore(deps): update npm dependencies (#5185)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-24 09:55:18 +08:00
Tunglies d291fc5c64 chore(dependencies): update clash_verge_logger source to specific commit 2025-10-24 00:08:10 +08:00
Tunglies bf45e487f9 feat(tauri-plugin-mihomo): configure IPC pool settings with connection limits and health checks 2025-10-23 22:32:23 +08:00
Tunglies 99ef0e51fc feat(init_logger): improve log level retrieval from environment variable 2025-10-23 22:13:43 +08:00
oomeow cc2dc66d5f chore: update tauri-plugin-mihomo dep 2025-10-23 20:45:34 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 001c11913a chore(deps): update dependency vite to ^7.1.12 (#5180)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 16:25:38 +08:00
Tunglies f061bce2a1 refactor(window_manager): simplify window state handling and improve method organization 2025-10-23 16:21:51 +08:00
oomeowandTunglies d7859b07a6 fix: parse hotkey (#5167)
* fix: incorrectly parse hotkey

* refactor: parse hotkey

* fix: panic on linux

* chore: update

* chore: update style

* fix: register hotkey error on windows

* chore: update style

---------

Co-authored-by: Tunglies <tunglies.dev@outlook.com>
2025-10-23 15:54:48 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 585963e751 chore(deps): update dependency lint-staged to ^16.2.6 (#5175)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 13:18:51 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> d84b762ef3 chore(deps): update dependency vitest to v4 (#5176)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 13:18:30 +08:00
Sline 8657cedca0 feat: add configurable hover jump navigator delay (#5178)
* fix: hover options

* feat: add configurable hover jump navigator delay

- Added `hover_jump_navigator_delay` to Verge config defaults, patch flow, and response payload for persistent app-wide settings.
- Made proxy navigator respect configurable delay via `DEFAULT_HOVER_DELAY` and new `hoverDelay` prop.
- Threaded stored delay through proxy list so hover scrolling uses Verge-configured value.
- Added "Hover Jump Navigator Delay" control in Layout settings with clamped numeric input, tooltip, and toggle-aware disabling.
- Localized new labels in English, Simplified Chinese, and Traditional Chinese.
- Extended frontend Verge config type to include delay field for type-safe access.

* docs: UPDATELOG.md
2025-10-23 13:14:01 +08:00
Tunglies 9ea9704bbf refactor(sysopt): replace Arc<TokioMutex> with AtomicBool for sysproxy state management 2025-10-23 00:53:57 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 302677aed0 chore(deps): update dependency react-i18next to v16.1.5 (#5171)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-22 22:01:37 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 1415df1d23 chore(deps): update cargo dependencies (#5169)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-22 21:59:53 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> b7d2bc7c74 chore(deps): update dependency @tauri-apps/cli to v2.9.1 (#5170)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-22 21:59:31 +08:00
Sline 4f7d069f19 feat(ui): add left menu lock/unlock with reorder mode and context menu (#5168)
* feat: free menu

* feat(ui): add left menu lock/unlock with reorder mode and context menu

* docs: UPDATELOG.md
2025-10-22 21:39:12 +08:00
Sline 3bedf7ec35 feat(backup): add dns_config into backup (#5166) 2025-10-22 19:52:44 +08:00
Sline c4c37bf291 feat(tray): close all connections (#5165) 2025-10-22 19:28:00 +08:00
Tunglies 815a865265 perf(tray): ignore unnecessary tray icon events to reduce refreshes 2025-10-22 18:49:28 +08:00
Tunglies 2d2167e048 refactor: replace unwrap_or with unwrap_or_else for improved error handling (#5163)
In Rust, the `or` and `or_else` methods have distinct behavioral differences. The `or` method always eagerly evaluates its argument and executes any associated function calls. This can lead to unnecessary performance costs—especially in expensive operations like string processing or file handling—and may even trigger unintended side effects.

In contrast, `or_else` evaluates its closure lazily, only when necessary. Introducing a Clippy lint to disallow `or` sacrifices a bit of code simplicity but ensures predictable behavior and enforces lazy evaluation for better performance.
2025-10-22 17:33:55 +08:00
Tunglies a05ea64bcd perf: utilize smartstring for string handling (#5149)
* perf: utilize smartstring for string handling

- Updated various modules to replace standard String with smartstring::alias::String for improved performance and memory efficiency.
- Adjusted string manipulations and conversions throughout the codebase to ensure compatibility with the new smartstring type.
- Enhanced readability and maintainability by using `.into()` for conversions where applicable.
- Ensured that all instances of string handling in configuration, logging, and network management leverage the benefits of smartstring.

* fix: replace wrap_err with stringify_err for better error handling in UWP tool invocation

* refactor: update import path for StringifyErr and adjust string handling in sysopt

* fix: correct import path for CmdResult in UWP module

* fix: update argument type for execute_sysproxy_command to use std::string::String

* fix: add missing CmdResult import in UWP platform module

* fix: improve string handling and error messaging across multiple files

* style: format code for improved readability and consistency across multiple files

* fix: remove unused file
2025-10-22 16:25:44 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> fe96a7030a chore(deps): update rust crate boa_engine to 0.21.0 (#5159)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-22 09:11:57 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 9050e56cdb chore(deps): update npm dependencies (#5158)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-22 09:02:32 +08:00
Tunglies b813cbdfc8 fix: update clash_verge_service_ipc to version 2.0.17 2025-10-22 01:51:11 +08:00
Slinetrac 95aee6ec81 chore: better pre-hooks 2025-10-22 00:08:16 +08:00
Slinetrac 4d2c1b4dc2 fix: resolve clippy lint in linux utils 2025-10-21 23:20:27 +08:00
Tunglies d9fdf261d1 fix: optimize process ID collection and remove unnecessary async handler in window destruction
fix: remove unnecessary condition check in process name matching
2025-10-21 23:14:57 +08:00
Slinetrac a8b17926ed refactor: adjust MIME detection to merge duplicates and follow Freedesktop standard
- Honor Freedesktop precedence when locating mimeapps.list
- Replace per-scheme HashSet with index-tracking HashMap
- Merge duplicate handler entries instead of discarding them
- Ensure all schemes exist using the new tracking structure
2025-10-21 22:53:47 +08:00
Tunglies afb049ca17 fix: simplify conditional checks and improve async handler usage across multiple files (#5156)
* fix: simplify conditional checks and improve async handler usage across multiple files

* fix: add missing AsyncHandler import in find_processes_by_name function

* fix: remove redundant AsyncHandler import in find_processes_by_name function
2025-10-21 22:39:32 +08:00
Sline 9c9aefe4cd fix: MIME config (#5154)
* fix: MIME config #2487

* fix: path

* refactor: enhance logic
2025-10-21 22:02:41 +08:00
Tunglies e7a4415d1f ci: improve clippy lint workflow to handle manual triggers and src-tauri changes 2025-10-21 18:10:44 +08:00
xmk23333 ef3f8e1839 style: clean up whitespace and improve code formatting across multiple files 2025-10-21 17:53:02 +08:00
xmk23333 0e933597f5 refactor: streamline SWR configuration and improve error handling in AppDataProvider 2025-10-21 17:51:12 +08:00
Slinetrac bafe2ae164 fix: home card save 2025-10-21 15:10:48 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 6d93e21bc7 chore(deps): update dependency react-i18next to v16.1.3 (#5150)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 13:35:33 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 91fb0d9ffa chore(deps): update npm dependencies (#5147)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 13:31:52 +08:00
Tunglies 8fc72814c8 build: optimize release profile for better performance
Upstream Tauri fixed the stack overflow in the invoke handler
(https://github.com/tauri-apps/tauri/pull/14170) in v2.9.0,
so we can safely use opt-level 3 now.
2025-10-21 00:19:27 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> a5d3d6fc50 chore(deps): update dependency @tauri-apps/cli to v2.9.0 (#5145)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 23:57:28 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> b6d51d6fe4 chore(deps): update cargo dependencies (#5141)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 23:57:19 +08:00
oomeow a5c00ecb12 fix: faile to reset sysproxy (#5139) 2025-10-20 23:09:13 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 91e12798e4 chore(deps): update dependency @tauri-apps/api to v2.9.0 (#5142)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 22:19:00 +08:00
Sline 9ee4b067d0 fix(current-proxy-card): stabilize match rule lookup and dependencies (#5138)
- memoize policy name normalization and include it in hook deps
- guard MATCH rule checks against partial controller data
- register MATCH policy groups when rebuilding selector selectors
2025-10-20 17:59:21 +08:00
xmk23333 366deb2756 Merge branch 'dev' of https://github.com/clash-verge-rev/clash-verge-rev into dev 2025-10-20 16:48:41 +08:00
xmk23333 98778fe6a3 refactor: improve code formatting and enhance logging consistency across core and event handling modules 2025-10-20 16:47:26 +08:00
xmk23333 b9dd62e2e6 refactor: enhance error handling and logging in core components and server initialization 2025-10-20 16:34:38 +08:00
Tunglies a1dcdd04a7 fix: improve TUN mode handling logic to prevent unnecessary state changes #5122 (#5124) 2025-10-20 16:09:29 +08:00
Slinetrac 8ebf915330 docs: README.md i18n 2025-10-20 15:56:38 +08:00
AltZedandSline 5281449e26 Translate readme to russian (#5136)
* Add Russian translation README_ru.md and update README.md

* add language badges for Chinese and Russian README

* add language badges for Chinese and Russian README

* Update README.md

Co-authored-by: Sline <realakayuki@gmail.com>

* Update README_ru.md

---------

Co-authored-by: Sline <realakayuki@gmail.com>
2025-10-20 15:33:52 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> b2f0bf2f69 chore(deps): update dependency vite to ^7.1.11 (#5134)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 13:44:30 +08:00
xmk23333 d6bba4f68c Merge branch 'dev' of https://github.com/clash-verge-rev/clash-verge-rev into dev 2025-10-20 13:27:57 +08:00
xmk23333 278ab30d40 refactor: improve code formatting and enhance logging in resolve_setup_async function 2025-10-20 13:27:01 +08:00
xmk23333 786c981fe0 refactor: improve error handling in AppDataProvider and enhance configuration update logic in CoreManager 2025-10-20 13:26:24 +08:00
xmk23333 b77cc012e1 refactor: streamline app initialization and enhance WebSocket cleanup logic 2025-10-20 13:15:51 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> dc31ec524b chore(deps): update dependency eslint-plugin-unused-imports to ^4.3.0 (#5132)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 12:54:57 +08:00
Slinetrac a0ef64cda8 docs: UPDATELOG.md 2025-10-19 18:43:20 +08:00
oomeow 7be790c6fb chore: remove unused file and improve traffic monitor 2025-10-19 16:34:45 +08:00
Slinetrac fbe6cefbdb fix: hover jump navigator 2025-10-19 16:00:45 +08:00
Tunglies a60cab989d feat: add tracing support to logger initialization (#5120) 2025-10-18 22:57:51 +08:00
Tunglies 385ffafc67 fix: WindowProvider awful performance (#5119) 2025-10-18 22:49:55 +08:00
Tunglies 462b11d96a feat: implement caching for version and file hash to optimize prebuild process 2025-10-18 20:48:53 +08:00
oomeow 96ce529b16 refactor: react router (#5073)
* refactor: react router

* chore: update

* fix: router

* refactor: generate router children by navItems

* chore: set start page when create window

* docs: update UPDATELOG.md
2025-10-18 20:25:31 +08:00
Tunglies 8e20b1b0a0 feat: enhance profile update logic to include auto-update option handling 2025-10-18 17:40:55 +08:00
Tunglies c2d7bf296a feat: add allow auto update option for profiles and update UI components 2025-10-18 17:04:03 +08:00
Slinetrac 98725bbecf fix: TS errors 2025-10-18 16:11:42 +08:00
Sline c465000178 fix: update fallback (#5115)
* fix: update fallback

* test: introduce Vitest and add semver helper tests

* chore: merge vitest config into vite
2025-10-18 15:51:34 +08:00
Slinetrac 3d09cf0666 chore: remove unused imports 2025-10-18 14:51:27 +08:00
Tunglies 70770b3c13 refactor: optimize TUN mode cleanup process and improve task execution flow 2025-10-18 14:26:56 +08:00
Tunglies 0cdb9a05ce refactor: remove immediate window hide on quit for improved cleanup flow 2025-10-18 14:01:22 +08:00
Tunglies c97c4cbd41 refactor: optimize async setup flow and add config verification 2025-10-18 14:01:21 +08:00
Sline fecae38c63 refactor: Linux environment detection logic (#5108)
* fix: wayland framebuffer

* refactor(utils): move linux env heuristics into platform helper

* refactor(linux): let DMABUF override helper use resolved decision

* fix: clippy

* fix: clippy

* feat: NVIDIA detection

* fix: clippy
2025-10-18 12:13:00 +08:00
Tunglies 210c12a74e feat: implement CoreConfigValidator for configuration validation and enhance logging types (#5112) 2025-10-18 10:57:57 +08:00
Sline a1c0a09423 refactor(core): elegant retry (#5113) 2025-10-18 10:12:36 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> fc99f24802 chore(deps): update npm dependencies (#5111)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-18 10:10:18 +08:00
Tunglies 28bcdc3706 feat: enhance ProxyControlSwitches with Tun Mode functionality and state management 2025-10-18 00:00:20 +08:00
wonfen 859d09ff8c fix: undefined is not an object on connection table 2025-10-17 21:48:58 +08:00
Sline 10f155da78 refactor: retry with backoff (#5104)
* Reapply "refactor: retry with backoff"

This reverts commit 4e31dc8728.

* fix: clippy

* fix: clippy
2025-10-17 20:43:06 +08:00
Slinetrac 4e31dc8728 Revert "refactor: retry with backoff"
This reverts commit 0b63bebb6c.
2025-10-17 20:14:15 +08:00
Tunglies 98a52c5c33 fix: remove dead code from event-driven proxy and handle modules (#5103)
* refactor: remove dead code from event-driven proxy and handle modules

* refactor: remove dead code for set_activation_policy_prohibited function
2025-10-17 20:12:03 +08:00
Slinetrac 0b63bebb6c refactor: retry with backoff 2025-10-17 19:48:50 +08:00
Tunglies bccde5ef6d feat(locales): add "App Log Max Size" and "App Log Max Count" entries to English and Chinese localization files 2025-10-17 19:20:51 +08:00
Slinetrac 215ba4da63 perf(delay): cache latency updates and smooth proxy list refresh
- track delay as structured updates with TTL-backed cache
- batch listener notifications to avoid render storms during checks
- surface cached latency in proxy items for quicker, steadier UI feedback
2025-10-17 18:27:21 +08:00
Slinetrac 886d1a551a refactor: useLayoutEffect 2025-10-17 15:11:36 +08:00
Slinetrac d05bcc17f7 fix: untested and testing sort 2025-10-17 15:01:38 +08:00
Slinetrac c63584daca fix: timeout sort 2025-10-17 14:51:33 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> c2f59ffc02 chore(deps): update dependency @mui/x-data-grid to ^8.14.1 (#5093)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-17 07:41:57 +08:00
oomeow f90361f8e4 chore: temporarily allow clippy to pass 2025-10-16 20:22:46 +08:00
Slinetrac 67d254236d fix(profile): verify saved proxy exists before activation
- check selector-like groups’ all list before applying saved nodes
- warn and fall back when a stored proxy disappeared from the group
- keep existing auto-switch flow for matching nodes to avoid regressions
2025-10-16 19:29:19 +08:00
Slinetrac fd5bddeb80 fix(backup): prevent immediate deletion before confirmation dialog 2025-10-16 16:54:25 +08:00
Slinetrac 4835d68222 docs: UPDATELOG.md 2025-10-16 16:17:13 +08:00
Slinetrac fe78e2d5cd fix(windows): wait for service readiness before enabling TUN 2025-10-16 15:09:17 +08:00
Slinetrac e73217ad5f fix(core): restart core when config reload fails
- add retry path that restarts Mihomo on connection-related reload errors
- guard runtime config state by discarding on repeated failures and returning rich errors
2025-10-16 14:30:57 +08:00
Slinetrac 88cde5d99d fix(proxy): place timeout nodes at the end when sorting by latency 2025-10-16 11:51:57 +08:00
Slinetrac 41bc0e62a1 fix(home): scope cached proxy selection to active profile to prevent fallback reset on profile switch
Previously, Home card used global localStorage keys for selected proxy/group
(`clash-verge-selected-proxy(-group)`), causing cached selections from other
profiles to override the current one and reset the fallback to Direct when switching.

Now the cache keys are namespaced per profile, with migration of legacy values
and unified helpers (src/components/home/current-proxy-card.tsx:110–147),
ensuring each profile restores its own proxy state independently.
2025-10-16 09:39:57 +08:00
Slinetrac b05799cfae fix: clippy warnings 2025-10-16 09:01:16 +08:00
oomeow 592e7f846d fix: incorrect delay status 2025-10-15 23:44:38 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> bcd54bf995 chore(deps): update rust crate gethostname to 1.1.0 (#5065)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-15 19:02:22 +08:00
Sline 0b4403b67b refactor: frontend (#5068)
* refactor: setting components

* refactor: frontend

* fix: settings router
2025-10-15 18:57:44 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> a591ee1efc chore(deps): update dependency eslint-plugin-react-refresh to ^0.4.24 (#5070)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-15 18:20:00 +08:00
Slinetrac ef9ccafe61 refactor: proxy components 2025-10-15 09:00:03 +08:00
Tunglies e6b7d512fb feat: implement draft management system for concurrent editing and committing of data 2025-10-15 08:32:52 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 6113be3b6c chore(deps): update rust crate getrandom to 0.3.4 (#5064)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-15 08:22:56 +08:00
Tunglies 7fab8eeaf6 feat: add "Prefer System Titlebar" localization to English and Chinese JSON files 2025-10-15 07:24:36 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 15d5113729 chore(deps): update npm dependencies (#5060)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-14 23:52:56 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> e5eaff37a4 chore(deps): update rust crate tokio to 1.48.0 (#5063)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-14 23:50:37 +08:00
Slinetrac 4f2633a62b refactor: profile components 2025-10-14 23:45:12 +08:00
Slinetrac 5d114806f7 refactor: layout and log components 2025-10-14 22:13:14 +08:00
Slinetrac 06dc7a6ef4 refactor: home components 2025-10-14 21:57:33 +08:00
Slinetrac 8dbe3f8c48 refactor: connection components 2025-10-14 21:33:36 +08:00
Slinetrac 778d506be7 refactor: common components 2025-10-14 21:22:48 +08:00
Slinetrac 65cf6c387b refactor: base components 2025-10-14 21:13:10 +08:00
Slinetrac 2e3174baa7 feat(proxy-groups, current-proxy-card): auto-refresh delay sorting
- proxy-groups: recalculate active group head and reapply delay sort after tests so list reorders automatically when "按延迟排序" is active.
- current-proxy-card: add delaySortRefresh trigger after auto/manual latency checks to immediately refresh selector and proxy list ordering.
- current-proxy-card: listen for delaySortRefresh to keep displayed delay chips and option ordering aligned with latest measurements.
2025-10-14 20:25:52 +08:00
Tunglies 7c71d07ad2 refactor: convert synchronous file operations to asynchronous for improved performance (#5059)
* refactor: convert synchronous file operations to asynchronous for improved performance

* fix: update copy_icon_file to use asynchronous directory creation

* refactor: remove unnecessary variable assignments in shortcut management functions
2025-10-14 19:55:22 +08:00
Slinetrac 8760ed17dc feat(current-proxy-card): add toggle for automatic delay detection 2025-10-14 19:27:41 +08:00
Tunglies 02b44d83af chore: replace pre-commit hook with cargo alias 2025-10-14 18:21:25 +08:00
Tunglies bb2059c76f fix: resolve issue with file deletion during subscription removal 2025-10-14 17:56:38 +08:00
Sline f541464ff4 feat: tray enhance (#5058)
* feat: proxy group sorting for tray

* feat(tray): add inline proxy groups toggle
2025-10-14 17:03:37 +08:00
Slinetrac 98527d5038 feat: import profiles with enter 2025-10-14 15:54:26 +08:00
Slinetrac 2ba2f4d42c refactor: use logging 2025-10-14 15:10:06 +08:00
Sline 51b08be87e feat: local backup (#5054)
* feat: local backup

* refactor(backup): make local backup helpers synchronous and clean up redundant checks

- Converted local backup helpers to synchronous functions to remove unused async warnings and align command signatures.
- Updated list/delete/export commands to call the sync feature functions directly without awaits while preserving behavior.
- Simplified destination directory creation to always ensure parent folders exist without redundant checks, satisfying Clippy.
2025-10-14 14:52:04 +08:00
Tunglies 4dd811330b feat: add Clippy alias commands and improve build process for Clippy integration (#5055)
* feat: add Clippy alias commands and improve build process for Clippy integration

* fix(lint-clippy): update Clippy run command to use working directory for src-tauri
2025-10-14 14:43:03 +08:00
Tunglies 76ca24086b fix: specify type for mode and host variables to improve clarity and type safety (#5052)
* fix: specify type for mode and host variables to improve clarity and type safety

* fix: specify types for pac_url and host variables to enhance type safety

* fix: change type of pac_url from Url to String for consistency in handling PAC output
2025-10-14 13:16:11 +08:00
Tunglies 3d96a575c0 refactor: streamline profile import logic and enhance error handling (#5051) 2025-10-14 12:39:22 +08:00
Slinetrac db091f5d2e feat(current-proxy-card): add automatic delay checks 2025-10-14 12:27:12 +08:00
Slinetrac baebce4aad fix(connection-table): patch DataGrid event handling to prevent Safari crash
- Ensure api.publishEvent is patched only once, retrying until the API is ready.
- Normalize missing event objects for Safari to avoid crashes.
- Restore the original handler and clear timers on unmount to keep the grid stable.
2025-10-14 11:52:51 +08:00
Tunglies fefc5c23fd fix: simplify error handling in change_clash_core and validate_dns_config functions 2025-10-14 11:48:53 +08:00
Tunglies 924e7d1022 Refactor string handling to use into() instead of to_string() for improved performance and consistency across the codebase. This change affects various modules including app.rs, clash.rs, config.rs, core.rs, service.rs, and others, ensuring that string conversions are streamlined and more idiomatic. 2025-10-14 09:26:20 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 44eb781060 chore(deps): update npm dependencies (#5045)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-14 09:02:49 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 3bd981d47b chore(deps): update cargo dependencies (#5048)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-14 08:56:51 +08:00
Slinetrac 1d725b8bde feat: add claude, spotify and tiktok into unlock checker 2025-10-13 23:55:40 +08:00
Moon 15a0c30ccb fix: linux border render (#5046) 2025-10-13 22:42:25 +08:00
Slinetrac 537a3000b6 chore: rm success notice 2025-10-13 19:04:40 +08:00
Sline 965ee9844d refactor(unlock): restructure media unlock checker (#5044)
- Split the monolithic unlock checker into a module tree (mod.rs:9–133), wiring service-specific tasks while keeping exported Tauri commands untouched.
- Centralize shared data and helpers in types.rs (1–40) and utils.rs (1–21) for reusable timestamp and emoji logic.
- Move each provider’s logic into its own file (bilibili.rs, disney_plus.rs, netflix.rs, etc.), preserving behavior and making future additions or fixes localized.
2025-10-13 18:56:15 +08:00
Tunglies fa39cfc41b fix: reorganize imports in logging.rs for clarity and consistency 2025-10-13 13:10:55 +08:00
Tunglies 902c8fcaf2 fix: update clash_verge_service_ipc to version 2.0.16 and improve log handling 2025-10-13 11:39:16 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 5fb770c113 chore(deps): update dependency @eslint-react/eslint-plugin to ^2.1.1 (#5040)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 11:24:43 +08:00
Tunglies ca3fa869d5 Squashed commit of the following:
commit 2a9f2f20e9c6d88c2f96fd40589740e1f236f64a
Author: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date:   Mon Oct 13 11:07:31 2025 +0800

    fix: improve message handling in CommandEvent logging with CompactString

commit c77fc18accefeaf471594035d61bd13e235c87d6
Author: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date:   Mon Oct 13 10:47:16 2025 +0800

    fix: optimize shared writer locking in CommandEvent handling

commit d5286ee5f1612f17b7a97eead84d430669816d98
Author: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date:   Mon Oct 13 10:30:19 2025 +0800

    feat: integrate CompactString for improved logging and dependency management

commit 951fb2b120ce159c00dc57d43c5a519990f34cee
Author: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date:   Mon Oct 13 09:39:29 2025 +0800

    refactor: remove write_sidecar_log function and streamline logging in CommandEvent handling

commit fd48d66c55a2c62fd32741fd3c65cc06d4cc693f
Author: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date:   Mon Oct 13 09:38:05 2025 +0800

    Revert "refactor(core): stabilize 'static backing for sidecar logging"

    This reverts commit fe7eb59f18.
2025-10-13 11:08:44 +08:00
Slinetrac 51ba1d1e34 refactor(nsis): use tauri v2 installer template 2025-10-13 10:30:03 +08:00
Slinetrac 7e3804bf34 chore: issue template 2025-10-13 08:46:04 +08:00
oomeow 55e8582ee4 chore: bump clash_verge_service_ipc to 2.0.15 2025-10-13 00:09:12 +08:00
oomeow 0e9595f255 feat: add get clash logs from service
chore: log message
2025-10-13 00:02:46 +08:00
Sline 19246ac616 fix(profile): fix false failure notice after successful import (#5038)
- normalize profile urls so matching ignores casing/trailing slashes
- capture baseline profile state and confirm landing before showing success
- reuse shared success handler for normal and clash proxy retries
2025-10-12 23:21:32 +08:00
oomeowandSlinetrac b91087e175 feat: support for reopen app via desktop shortcuts (#5037)
* fix: singleton check

* docs: update UPDATELOG.md

---------

Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-10-12 22:55:40 +08:00
Slinetrac 7789d0bd5c Revert "chore: update ipc crate version"
This reverts commit 8e5c150a4f.
2025-10-12 21:26:45 +08:00
oomeow 1875e1b513 fix: incorrect maximize status on custom windows controller (#5033)
* fix: windows controller

* chore: update style

* chore: update style
2025-10-12 20:51:25 +08:00
Slinetrac 8e5c150a4f chore: update ipc crate version 2025-10-12 16:18:17 +08:00
oomeow 85f4afe2a1 perf: reduce reset scroller position 2025-10-12 15:09:20 +08:00
oomeow 0b8b3c5a1a feat: set clash log level default to info 2025-10-12 13:18:26 +08:00
oomeow 5ce95d74a9 chore: cleanup 2025-10-12 13:03:42 +08:00
Tunglies d531432f4a fix: improve Service connection method and permissions for Windows and Unix 2025-10-11 23:41:24 +08:00
Tunglies 4f1d61a56e Revert "fix: improve Service connection method and permissions for Windows and Unix"
This reverts commit 601e99f0b5.

Revert "refactor: clash-verge-rev-service-ipc (#4841)"

This reverts commit 5370bd45ed.
2025-10-11 21:21:23 +08:00
oomeow 121b8c433b chore: update tauri-plugin-mihomo dep 2025-10-11 20:57:30 +08:00
Sline 3d2507430b fix(shutdown): mark shutdown as exiting to stop background tasks (#5024)
* fix(shutdown): mark shutdown as exiting to stop background tasks

- lib.rs:570 → Flag app as exiting on ExitRequested, notify proxy guard, start cleanup immediately, with fallback in Exit event
- tray/mod.rs:190 → Add unified exit checks around tray init/updates to prevent UI recreation during shutdown
- event_driven_proxy.rs:252 → Ensure proxy guard skips all restore/re-enable work (including sysproxy.exe calls) once exit flag is set

* fix(shutdown): refine exit handling and proxy guard notifications

* fix(shutdown): add guard to run shutdown routine only once per lifecycle
2025-10-11 16:49:47 +08:00
Tunglies 601e99f0b5 fix: improve Service connection method and permissions for Windows and Unix 2025-10-11 15:40:59 +08:00
Tunglies 5370bd45ed refactor: clash-verge-rev-service-ipc (#4841)
* feat: update service installation scripts and IPC integration

- Updated `Cargo.toml` to use version 2.0.8 of `clash_verge_service_ipc` with "client" feature.
- Renamed service installation and uninstallation scripts in `post-install.sh` and `pre-remove.sh`.
- Removed `service_ipc` module and refactored IPC handling in `service.rs` to use the new `clash_verge_service_ipc` directly.
- Adjusted service version checking and core management to align with the new IPC structure.
- Simplified directory checks in `dirs.rs` and updated logging configurations in `init.rs`.
- Updated Linux configuration file to reflect new script names.
- Enhanced service installer hook to manage state more effectively.

* refactor: simplify ClashConfig instantiation and remove unused service log file function

* feat: update clash_verge_service_ipc to version 2.0.9 and enhance service initialization logging

* chore: update clash_verge_service_ipc to version 2.0.10 and refactor async service manager initialization

* fix: update clash_verge_service_ipc to version 2.0.11 and improve service manager initialization

* fix: increase sleep duration for socket readiness check to improve stability

* fix: update clash_verge_service_ipc to version 2.0.12 and kode-bridge to version 0.3.4; refactor service management and IPC path checks

* fix: update clash_verge_service_ipc to version 2.0.13; refactor service connection and initialization logic
2025-10-11 15:35:26 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 1246a66b35 chore(deps): update dependency lint-staged to ^16.2.4 (#5021)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-11 15:17:53 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 7d6fb54783 chore(deps): update npm dependencies (#5015)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-11 13:14:56 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 79d14f1d51 chore(deps): update rust crate regex to 1.12.1 (#5017)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-11 13:12:27 +08:00
Tunglies 59dd71ebaa refactor: simplify logging calls by removing unnecessary parameters 2025-10-10 13:46:26 +08:00
Tunglies ea319e951c fix: update clippy command to include all features and targets 2025-10-10 13:05:59 +08:00
Tunglies 8c0af66ca9 Refactor logging macros to remove print control parameter
- Updated logging macros to eliminate the boolean parameter for print control, simplifying the logging calls throughout the codebase.
- Adjusted all logging calls in various modules (lib.rs, lightweight.rs, help.rs, init.rs, logging.rs, resolve/mod.rs, resolve/scheme.rs, resolve/ui.rs, resolve/window.rs, server.rs, singleton.rs, window_manager.rs) to reflect the new macro structure.
- Ensured consistent logging behavior across the application by standardizing the logging format.
2025-10-10 13:05:37 +08:00
Sline a4d94c8bc9 refactor: enhance compositor detection logic (#5007)
* refactor: enhance compositor detection logic

* docs: UPDATELOG.md
2025-10-10 10:13:24 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> ea8ca1b739 chore(deps): update dependency @mui/x-data-grid to ^8.14.0 (#5004)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-10 08:38:22 +08:00
Slinetrac e4f1bab8fb Reapply "fix: windows title bar only"
This reverts commit 7e05b8f13b.
2025-10-10 08:25:08 +08:00
Tunglies 7e05b8f13b Revert "fix: windows title bar and refactor old code (#4988)" this breaks UI page switch function
This reverts commit 03ab2410cc.
2025-10-10 07:38:52 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 0a771bd67a chore(deps): update rust crate libc to 0.2.177 (#5003)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 22:24:19 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 2798e930ac chore(deps): update rust crate zip to v6 (#5000)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 22:17:45 +08:00
oomeow 1357913f8b fix: restart app failed (#4974)
* fix: failed to restart app

* chore: cleanup

* chore: cargo fmt

* chore: use AsyncHandler

* chore: clippy

* chore: update
2025-10-09 20:08:25 +08:00
❤是纱雾酱哟~ 14b990ad9f Revert "build(tauri): add pkexec dependency for Linux packages (#4833)" (#4996)
- This reverts commit b608a38
- This may reopen issue #4831
- Issue #4992 may still remain unresolved

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>
2025-10-09 18:45:02 +08:00
Slinetrac 58bd2032f3 refactor(sidecar): inline Record construction for safer logging
- Build Record inline so fmt::Arguments temporary only lives through writer.write, avoiding dropped-temporary borrow.
- Add explicit 'static annotation on leaked string before reboxing for clarity.
2025-10-09 18:41:09 +08:00
Slinetrac fe7eb59f18 refactor(core): stabilize 'static backing for sidecar logging
Introduced `write_sidecar_log` to prevent temporary `format_args!` values
from dropping early.

- src-tauri/src/core/core.rs:60 — adds `write_sidecar_log`, which temporarily
  leaks the message into a `Box<str>`, builds the `Record`, writes it, then
  immediately reclaims the boxed string. The `unsafe` block is limited to
  `Box::from_raw` needed to undo `Box::leak`.
- src-tauri/src/core/core.rs:794, 802, 806 — all three sidecar events now route
  through this helper, reusing the returned string for the in-memory log and
  avoiding extra UTF-8 decoding.
2025-10-09 16:46:11 +08:00
Sline e3cd16189b fix: linux app theme (#4997) 2025-10-09 16:03:28 +08:00
Sline bd9db1b4f7 fix: linux webkit error (#4995)
* fix: linux webkit error

* docs: UPDATELOG.md
2025-10-09 15:44:11 +08:00
Sline 5db4677ff8 fix: linux tun timeout (#4993)
* fix: linux tun timeout

* docs: UPDATELOG.md
2025-10-09 15:09:17 +08:00
Slinetrac 44280b23e4 Merge remote-tracking branch 'origin/dev' into dev 2025-10-09 14:05:37 +08:00
Slinetrac 7cfc31b6e5 docs: UPDATELOG.md 2025-10-09 14:05:05 +08:00
Sline c7cd47fbdc fix: silent start (#4990) 2025-10-09 14:02:27 +08:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Slinetrac
a9d91a09c4 chore(deps): update dependency eslint-plugin-react-hooks to v7 (#4987)
* chore(deps): update dependency eslint-plugin-react-hooks to v7

* style: format

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-10-09 12:58:14 +08:00
Slinetrac d18b98304b fix: windows title bar only 2025-10-09 12:34:36 +08:00
Slinetrac a80bc10719 Revert "fix: windows title bar and refactor old code (#4988)"
This reverts commit 03ab2410cc.
2025-10-09 12:31:54 +08:00
Slinetrac 0f34d63b6d docs: up UPDATELOG.md 2025-10-09 11:44:52 +08:00
Tunglies 02c271dfb2 feat: update tauri-plugin-mihomo to version 0.1.1 with new source reference 2025-10-09 11:32:09 +08:00
Sline 03ab2410cc fix: windows title bar and refactor old code (#4988) 2025-10-09 10:53:20 +08:00
Slinetrac f5c2b2a23d chore: light hook 2025-10-09 10:29:20 +08:00
Tunglies 4417fe6cd9 feat: update tray tooltip to include reassembled version format #4727 2025-10-09 05:53:49 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 57c031a8f8 chore(deps): update dependency react-router-dom to v7.9.4 (#4985)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 05:10:48 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> a9733d9746 chore(deps): update rust crate flexi_logger to 0.31.7 (#4986)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 05:10:32 +08:00
❤是纱雾酱哟~ 936764e6ce feat: Enable git hooks with husky (#4984)
* build(deps): Adds husky for Git hooks

- Integrates the husky package as a development dependency.
- Enables the configuration and enforcement of pre-commit and pre-push Git hooks.
- Improves code quality and consistency by automating checks before commits.

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>

* feat: Add Husky prepare hook

- Automatically installs Git hooks for developers
- Ensures consistent code quality checks before commits or pushes
- Streamlines the developer setup process

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>

* ci: Avoid installing Git Hooks on GitHub Workflows

- Adds `HUSKY: 0` environment variable to all workflow definitions.
- Prevents local development hooks from executing in CI, which can cause unnecessary failures or overhead.
- See https://typicode.github.io/husky/how-to.html#ci-server-and-docker

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>

---------

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>
2025-10-09 05:10:03 +08:00
Sline 9750cd3ce6 fix: multi-monitor-crash (#4980) 2025-10-08 22:02:17 +08:00
Tunglies bfd1274a8c feat: Implement custom window controls and titlebar management (#4919)
- Added WindowControls component for managing window actions (minimize, maximize, close) based on the operating system.
- Integrated window decoration toggle functionality to allow users to prefer system titlebar.
- Updated layout styles to accommodate new titlebar and window controls.
- Refactored layout components to utilize new window management hooks.
- Enhanced layout viewer to include a switch for enabling/disabling window decorations.
- Improved overall window management by introducing useWindow and useWindowDecorations hooks for better state handling.
2025-10-08 20:23:26 +08:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Slinetrac
f195b3bccf chore(deps): update rust crate flexi_logger to 0.31.6 (#4979)
* chore(deps): update rust crate flexi_logger to 0.31.6

* chore: up lock

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-10-08 19:14:00 +08:00
Tunglies 5376d50cfb feat: add clash_verge_logger and clash_verge_service_ipc dependencies; refactor logging and process management 2025-10-08 18:06:11 +08:00
Slinetrac 0b6681436a style: lint 2025-10-08 14:19:07 +08:00
oomeow 7fc238c27b refactor: invock mihomo api by use tauri-plugin-mihomo (#4926)
* feat: add tauri-plugin-mihomo

* refactor: invock mihomo api by use tauri-plugin-mihomo

* chore: todo

* chore: update

* chore: update

* chore: update

* chore: update

* fix: incorrect delay status and update pretty config

* chore: update

* chore: remove cache

* chore: update

* chore: update

* fix: app freezed when change group proxy

* chore: update

* chore: update

* chore: add rustfmt.toml to tauri-plugin-mihomo

* chore: happy clippy

* refactor: connect mihomo websocket

* chore: update

* chore: update

* fix: parse bigint to number

* chore: update

* Revert "fix: parse bigint to number"

This reverts commit 74c006522e.

* chore: use number instead of bigint

* chore: cleanup

* fix: rule data not refresh when switch profile

* chore: update

* chore: cleanup

* chore: update

* fix: traffic graph data display

* feat: add ipc connection pool

* chore: update

* chore: clippy

* fix: incorrect delay status

* fix: typo

* fix: empty proxies tray menu

* chore: clippy

* chore: import tauri-plugin-mihomo by using git repo

* chore: cleanup

* fix: mihomo api

* fix: incorrect delay status

* chore: update tauri-plugin-mihomo dep

chore: update
2025-10-08 12:32:40 +08:00
Sline 72aa56007c feat(ui): implement profiles batch select and i18n (#4972)
* feat(ui): implement profiles batch select and i18n

* refactor: adjust button position and icon

* style: lint fmt
2025-10-08 12:02:55 +08:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Slinetrac
2bc720534d chore(deps): update rust crate flexi_logger to 0.31.5 (#4965)
* chore(deps): update rust crate flexi_logger to 0.31.5

* chore: up lock

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-10-08 08:34:41 +08:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Slinetrac
1e88f95b43 chore(deps): update dependency lint-staged to v16 (#4968)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-10-08 08:34:03 +08:00
Slinetrac 9d47bc66c4 chore: unify hook behavior to prevent cross-platform CI failures 2025-10-08 08:12:16 +08:00
Slinetrac 10f250b7e7 Revert "fix(windows): show UAC prompt for TUN service install/uninstall (#4959)"
This reverts commit c05395c258.
2025-10-08 07:47:05 +08:00
oomeow f492580864 chore: pretty lint-staged.config.js 2025-10-07 18:50:18 +08:00
Slinetrac 86b4712beb chore: more friendly lint 2025-10-07 18:28:32 +08:00
Slinetrac 0d12103085 chore: add lint-staged and edit pre-commit 2025-10-07 18:02:37 +08:00
Sline bf4e1a3270 feat: url test button for proxy card and type safety (#4964)
* feat: url test button for proxy card and type safety

* fix: resolve ESLint hook dependency error in current-proxy-card.tsx
2025-10-07 16:39:22 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 3f1f53434c chore(deps): update npm dependencies (#4962)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 16:38:32 +08:00
Sline d3477159a8 fix: improve Linux tray support and add --no-tray option (#4958) 2025-10-07 10:02:11 +08:00
Sline c05395c258 fix(windows): show UAC prompt for TUN service install/uninstall (#4959) 2025-10-07 10:01:35 +08:00
Sline d25eb49bfe fix(tray): resolve "Restart App" failure on Windows (#4960)
* fix(tray): resolve "Restart App" failure on Windows

* style: rm useless comment
2025-10-07 10:01:15 +08:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Slinetrac
f3f8ea0481 chore(deps): update cargo dependencies (#4899)
* chore(deps): update cargo dependencies

* chore: up lock

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Slinetrac <realakayuki@gmail.com>
2025-10-07 08:22:35 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 0af971b08a chore(deps): update npm dependencies (#4955)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 07:53:24 +08:00
Sline a7fa63f054 fix: implement proper error handling for "Restart App" failures (#4951)
* fix: implement proper error handling for "Restart App" failures

* fix: make clippy happy
2025-10-07 07:32:01 +08:00
Sline b20b30baad refactor(config): move verify_config_initialization with backoff retry (#4952) 2025-10-07 07:31:35 +08:00
Sline cb15a38cb3 fix: restore periodic proxy guard checks removed in event-driven migration (#4954)
* fix: restore periodic proxy guard checks removed in event-driven migration

* style: cargo fmt
2025-10-07 07:18:07 +08:00
Sline 39673af46f fix: ensure frontend sync after profile create/delete (#4956) 2025-10-07 07:17:21 +08:00
wonfen c6afbf6ee8 chore: resolve deprecation warnings and add missing translations 2025-10-06 23:40:12 +08:00
Tunglies abb0df59df refactor: simplify auto proxy disabling logic in clean_async function 2025-10-06 18:26:01 +08:00
Sline b0decf824e fix(webdav/app): reset client on errors and improve app restart (#4941)
* fix(webdav/app): reset client on errors and improve app restart

* refactor: rm unused function
2025-10-06 16:54:35 +08:00
Sline 5ec5fdcfc7 fix(init): ensure runtime config is ready before core manager startup (#4942)
* fix(init): ensure runtime config is ready before core manager startup

* refactor: simplify verify_config_initialization function
2025-10-06 16:51:47 +08:00
wonfen f9bc739c51 feat: add system proxy cleanup on system shutdown & prevent DLL errors 2025-10-06 11:19:23 +08:00
Tunglies a1b3f267de feat: enhance exit handling to prevent initialization and event processing during application exit 2025-10-05 18:07:47 +08:00
wonfen dbcad24093 refactor: Uses tokio Command with CREATE_NO_WINDOW flag to avoid DLL initialization issues during shutdown 2025-10-05 11:11:14 +08:00
Tunglies 1176f8c863 feat: refactor app data provider and context for improved data management and performance 2025-10-04 21:20:31 +08:00
oomeowandTunglies 90b98f695b fix: app freeze when core run by service mode and open app window (#4922)
* fix: app freeze when core run by service mode

* chore: update

* chore: update UPDATELOG

---------

Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2025-10-04 20:44:49 +08:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Tunglies
600b0b52f4 chore(deps): update npm dependencies (#4939)
* chore(deps): update npm dependencies

* Refactor components to use function syntax instead of forwardRef for better type handling and clarity. Updated imports and adjusted prop types accordingly across multiple viewer components including TrafficGraph, ProfileViewer, BackupViewer, ClashCoreViewer, ControllerViewer, DnsViewer, LiteModeViewer, NetworkInterfaceViewer, ThemeViewer, TunViewer, UpdateViewer, and WebUIViewer.

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2025-10-04 20:26:10 +08:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Tunglies
8b3bc18ea8 chore(deps): update dependency eslint-plugin-react-hooks to v6 (#4940)
* chore(deps): update dependency eslint-plugin-react-hooks to v6

* fix: update ESLint configuration to use correct imports and recommended settings

* chore: clean up unused code and improve readability across components

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2025-10-04 19:42:34 +08:00
Tunglies 9a9c9a2da1 feat: enhance versioning script to include latest Tauri commit hash in autobuild and deploytest tags 2025-10-04 17:03:05 +08:00
oomeow c5023b4505 ci: use ubuntu-22.04 on dev bundle workflow 2025-10-02 12:46:56 +08:00
oomeow 18b79d3693 ci: add linux dev bundle 2025-10-02 12:33:48 +08:00
oomeow 982c8b4df2 fix: drag and drop (#4924) 2025-10-02 12:32:55 +08:00
Tunglies 8a4f2de887 Revert "Refactor components to remove forwardRef and simplify props handling"
This reverts commit 1cd013fb94.
2025-09-30 18:13:02 +08:00
Tunglies 14288568bf feat: optimize backend i18n resource usage and improve language loading 2025-09-30 15:22:08 +08:00
Tunglies 1cd013fb94 Refactor components to remove forwardRef and simplify props handling
- Updated multiple components to remove the use of forwardRef, simplifying the props structure.
- Adjusted imports and component definitions accordingly.
- Ensured consistent handling of refs and props across various viewer components.
- Improved readability and maintainability of the codebase.
2025-09-30 14:26:40 +08:00
Sukka 0c88568cd7 chore: make eslint happy (part 1) (#4890) 2025-09-30 14:19:49 +08:00
Tunglies ecdeadfe1e feat: enhance CI workflows with paths filtering for Rust and web changes 2025-09-30 03:35:38 +08:00
Junkai W. d86bdea127 feat: add Quick navigation bar in the rule mode agent group (#4889) 2025-09-29 11:51:53 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 40f0e1bb19 chore(deps): update dependency @types/react to v19.1.15 (#4888)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-28 19:55:08 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 78496312ec chore(deps): update npm dependencies (#4857)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-28 19:54:58 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 3e23609b68 chore(deps): update cargo dependencies (#4842)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-28 19:51:25 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 8488a92026 chore(deps): update npm dependencies (#4843)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-28 19:51:10 +08:00
wonfen 1b4691d0ac chore: update log 2025-09-27 18:40:59 +08:00
wonfen fae2c27648 refactor: remove duplicate and inconsistent isTunAvailable definitions 2025-09-26 14:08:32 +08:00
wonfen 7a14e90802 feat: unify TUN mode availability checks across components 2025-09-26 14:00:57 +08:00
Tunglies c8c79d9baa feat: group all GitHub Actions updates into a single PR 2025-09-25 19:24:26 +08:00
Tunglies a2d33c5447 fix: update rust-toolchain action to use master branch for consistency 2025-09-25 19:21:06 +08:00
Sukka fb5d5a7d37 chore(eslint): replace eslint-plugin-react w/ eslint-react (#4844)
* chore(eslint): replace `eslint-plugin-react` w/ `eslint-react`

* chore(eslint): replace `eslint-plugin-import` w/ `import-x`
2025-09-24 13:25:22 +08:00
❤是纱雾酱哟~andTunglies b608a389c5 build(tauri): add pkexec dependency for linux packages (#4833)
* build(tauri): add pkexec dependency for linux packages

- Include pkexec in deb package dependencies
- Include pkexec in rpm package dependencies
- Update dependency arrays formatting in tauri linux config

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>

* refactor(tauri): Prettify configurations according to suggestions

- Format `src-tauri/tauri.linux.conf.json` using Prettier

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>

* feat: add pkexec dependency for Linux .deb and .rpm packaging

---------

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>
Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2025-09-24 08:42:14 +08:00
Hank 9de90200f5 fix: main thread block by sidecar launching. (#4795)
* fix: main thread block by sidecar launching.

#4791

* Refactor async runtime spawn to use AsyncHandler::spawn

for unify debugging.

* Fix compile error on non-windows platform

by remove Windows-specific configuration import.
2025-09-24 08:38:06 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 860f154d54 chore(deps): update cargo dependencies (#4796)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-23 14:02:43 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> f7d4040ac7 chore(deps): update npm dependencies (#4783)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-23 14:02:37 +08:00
Thomas b3075cce24 recommit #4765 overwritten by #4815 (#4834) 2025-09-23 13:59:41 +08:00
Junkai W. 909c4028f1 添加链式代理下规则适配 2025-09-22 18:16:17 +08:00
Tunglies 620922f82e feat: add configurable log size and count options in settings 2025-09-22 16:31:38 +08:00
Tunglies 22e1d329cb fix: breaks the behavior of entering lightweight mode from tray menu #4815 2025-09-21 21:34:47 +08:00
Tunglies 33fdcc38b5 Revert "还原轻量模式的进入和退出行为 (#4817)"
This reverts commit 8f88270cdf.
2025-09-21 21:33:01 +08:00
infinite-illusion fef2728a7c remove incorrect setupCloseListener from _layout.tsx (#4818) 2025-09-21 15:05:37 +08:00
Junkai W. 8f88270cdf 还原轻量模式的进入和退出行为 (#4817)
同时把轻量模式系统托盘id改为lightweight_mode
2025-09-21 15:02:55 +08:00
Junkai W. 3493580236 修复win下系统托盘代理组与gui顺序不一致 (#4815) 2025-09-21 09:45:26 +08:00
Tunglies 7f2729fd0f fix: restore LogLineFilter import for proper logging functionality 2025-09-21 01:59:02 +08:00
Tunglies b8c82320d3 refactor: enhance logging system with NoExternModule for better log filtering 2025-09-21 01:31:08 +08:00
Tunglies 94ec25c193 feat: add logging for ClashVergeRev version in async setup 2025-09-20 23:24:43 +08:00
Tunglies 2366530622 refactor: change lto option to "thin" for improved build performance 2025-09-20 22:54:52 +08:00
Tunglies 1cf8e2384e fix: Cargo.toml dependencies option (#4805)
* chore: update cargo lock

* refactor: streamline dependency declarations in Cargo.toml

* refactor: reorder import statements and comment out Cargo.toml version update

* fix: enable Cargo version update in release script and update tauri-plugin-devtools dependency
2025-09-20 15:15:24 +08:00
Tunglies d9a5c11d6a refactor: improve code readability and consistency in proxy-chain and uri-parser utilities
refactor: add keys to icons in routers for improved rendering and performance
refactor: optimize RegExp polyfill by using Object.prototype.hasOwnProperty.call
refactor: reorder imports in chain-proxy-provider for consistency
refactor: remove unused "obfs-opts" property from IProxySnellConfig interface

refactor: reorganize imports and enhance refresh logic in app data provider

refactor: re-enable prop-types linting for better type safety in BaseDialog component

refactor: update dependencies in effect hooks for improved stability and performance
2025-09-20 11:19:36 +08:00
Tunglies 7811714f89 refactor: enhance logging system and add new development commands (#4803)
* refactor: enhance logging system and add new development commands

* refactor: add cfg-if dependency and improve logging configuration
2025-09-20 00:04:46 +08:00
Tunglies e869da8d4c refactor: remove unused HTTP-specific structs to streamline service code 2025-09-19 00:04:51 +08:00
Tunglies e414b49879 Refactor imports across multiple components for consistency and clarity
- Reorganized import statements in various components to ensure consistent ordering and grouping.
- Removed unnecessary imports and added missing ones where applicable.
- Improved readability and maintainability of the codebase by standardizing import styles.
2025-09-19 00:01:04 +08:00
Tunglies 627119bb22 Refactor imports and improve code organization across multiple components and hooks
- Consolidated and reordered imports in various files for better readability and maintainability.
- Removed unused imports and ensured consistent import styles.
- Enhanced the structure of components by grouping related imports together.
- Updated the layout and organization of hooks to streamline functionality.
- Improved the overall code quality by following best practices in import management.
2025-09-18 23:34:38 +08:00
Tunglies 74ade3ee41 refactor: update ESLint configuration and improve lint command with cache 2025-09-18 23:13:13 +08:00
Tunglies 324628dd3d refactor: replace 'let' with 'const' for better variable scoping and immutability 2025-09-18 23:07:18 +08:00
Tunglies 9d96ac0f6a feat: Integrate HTTP plugin and update IP detection to use fetch API #4712 2025-09-18 19:35:14 +08:00
Tunglies 409571f54b refactor: remove unused notification permission hook and related code 2025-09-18 19:13:23 +08:00
TianHua Liu a995a13163 chore: use jsx-runtime presets of eslint-plugin-react (#4794) 2025-09-18 19:04:12 +08:00
Tunglies 7848d6b1de refactor: window handle usage (#4788)
* refactor: Remove unused UI reset function and streamline window creation logic

* refactor: Remove debug print statements and streamline lightweight mode initialization

* fix: Ensure tray status refresh during silent startup and lightweight mode entry is independent of window creation

* refactor: Simplify window creation process and remove debug print statements
2025-09-18 10:22:43 +08:00
Tunglies 5d2e114b4d fix: Update tray menu event handling to toggle lightweight mode based on current state #4785 2025-09-18 00:46:49 +08:00
Tunglies c207516b47 refactor: clash-verge-service management (#4674)
* refactor: clash-verge-service management

* fix: correct service state checks in ProxyControlSwitches component
refactor: improve logging in service state update functions

* fix: add missing async handler for Windows and adjust logging import for macOS

* fix: streamline logging imports and add missing async handler for Windows

* refactor: remove unused useServiceStateSync hook and update imports in _layout

* refactor: remove unused useServiceStateSync import and clean up code in ProxyControlSwitches and _layout

* refactor: simplify service status checks and reduce wait time in useServiceInstaller hook

* refactor: remove unnecessary logging statements in service checks and IPC connection

* refactor: extract SwitchRow component for better code organization and readability

* refactor: enhance service state management and update related mutations in layout

* refactor: streamline core stopping logic and improve IPC connection logging

* refactor: consolidate service uninstallation logic and improve error handling

* fix: simplify conditional statements in CoreManager and service functions

* feat: add backoff dependency and implement retry strategy for IPC requests

* refactor: remove redundant Windows conditional and improve error handling in IPC tests

* test: improve error handling in IPC tests for message signing and verification

* fix: adjust IPC backoff retry parameters

* refactor: Remove service state tracking and related logic from service management

* feat: Enhance service status handling with logging and running mode updates

* fix: Improve service status handling with enhanced error logging

* fix: Ensure proper handling of service operations with error propagation

* refactor: Simplify service operation execution and enhance service status handling

* fix: Improve error message formatting in service operation execution and simplify service status retrieval

* refactor: Replace Cache with CacheProxy in multiple modules and update CacheEntry to be generic

* fix: Remove unnecessary success message from config validation

* refactor: Comment out logging statements in service version check and IPC request handling
2025-09-17 22:59:02 +08:00
Tunglies 6724f1ae35 feat: Implement caching mechanism with Cache struct and update related commands 2025-09-17 19:37:42 +08:00
Tunglies 1787d5372e fix: Update OS_PLATFORM definition to reflect the current platform 2025-09-17 16:10:22 +08:00
Tunglies 4c41144dd0 fix: Update error message to include details of accumulated startup errors 2025-09-17 16:10:01 +08:00
Tunglies 27636c848f fix: update changelog to reflect removal of hidden groups in tray node switching #4765 2025-09-17 13:39:38 +08:00
Tunglies 8060d699f0 fix: enhance prebuild script to support shorthand for force update #4777 2025-09-17 13:36:31 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> f36f31a636 chore(deps): update npm dependencies (#4686)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-17 12:45:57 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> d300fac3d9 chore(deps): update cargo dependencies (#4687)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-17 12:45:50 +08:00
Thomas 8c2262dd95 stop showing hidden groups in the tray menu (#4765)
托盘菜单不显示隐藏代理组
2025-09-17 12:45:35 +08:00
wonfen c438e916ca perf: remove system-level unlock test timeout notice 2025-09-15 22:16:13 +08:00
ZShab NibaandTunglies 0855bd4896 fix: Fix icon to RGBA/sRGB Colorspace (#4753)
* fix: Fixed icon pixel size

* fix: fix icon to RGBA/sRGB Colorspace

* chore: update UPDATELOG

---------

Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2025-09-15 12:29:30 +08:00
Junkai W. f2073a2f83 Add Func 链式代理 (#4624)
* 添加链式代理gui和语言支持
在Iruntime中添跟新链式代理配置方法
同时添加了cmd

* 修复读取运行时代理链配置文件bug

* t

* 完成链式代理配置构造

* 修复获取链式代理运行时配置的bug

* 完整的链式代理功能
2025-09-15 07:44:54 +08:00
Tunglies a1f468202f Revert "fix: Fixed icon pixel size (#4698)"
revert due to runtime error [[Setup]] Error: failed to process image: Format error decoding Ico: The PNG is not in RGBA format!

This reverts commit a24bf4042c.
2025-09-15 00:56:59 +08:00
ZShab Niba a24bf4042c fix: Fixed icon pixel size (#4698) 2025-09-13 10:56:07 +08:00
❤是纱雾酱哟~ 15d22b4bf6 chore(issue template): disable blank issue template in GitHub config (#4731)
- Prevent users from creating issue without a template
- Always use a template for guiding users to provide necessary information for us

Signed-off-by: Dragon1573 <49941141+Dragon1573@users.noreply.github.com>
2025-09-13 10:11:40 +08:00
Tunglies 231d264652 fix: update alpha version description in README 2025-09-12 18:45:40 +08:00
Tunglies 67ac353fd5 chore: update cron schedule for auto build workflow 2025-09-12 18:43:13 +08:00
Tunglies 1c5534ad36 fix: remove deprecated updater for alpha endpoints in tauri configuration 2025-09-11 17:14:57 +08:00
Tunglies 251678493c edition 2024 (#4702)
* feat: update Cargo.toml for 2024 edition and optimize release profiles

* feat: refactor environment variable settings for Linux and improve code organization

* Refactor conditional statements to use `&&` for improved readability

- Updated multiple files to combine nested `if let` statements using `&&` for better clarity and conciseness.
- This change enhances the readability of the code by reducing indentation levels and making the conditions more straightforward.
- Affected files include: media_unlock_checker.rs, profile.rs, clash.rs, profiles.rs, async_proxy_query.rs, core.rs, handle.rs, hotkey.rs, service.rs, timer.rs, tray/mod.rs, merge.rs, seq.rs, config.rs, proxy.rs, window.rs, general.rs, dirs.rs, i18n.rs, init.rs, network.rs, and window.rs in the resolve module.

* refactor: streamline conditional checks using `&&` for improved readability

* fix: update release profile settings for panic behavior and optimization

* fix: adjust optimization level in Cargo.toml and reorder imports in lightweight.rs
2025-09-10 09:49:06 +08:00
Tunglies ccbffa14f0 fix: replace toggle with show for main window in lightweight mode exit #4697 2025-09-09 21:25:38 +08:00
Tunglies dfc1f736af fix: resolve from lightweight cause crash (#4682)
* refactor: streamline lightweight mode handling and improve window management

* refactor: replace mutex-based window creation lock with atomic operations for improved performance

* refactor: remove startup completed event handling and simplify initialization logic

* refactor: remove conditional compilation for emit_update_event function

* refactor: simplify return statements and clean up commented code in lightweight and window manager modules

* refactor: streamline lightweight mode handling by consolidating window management calls

* refactor: prevent unnecessary window toggle when exiting lightweight mode

* refactor: reorder imports for consistency in lightweight module

* refactor: move macOS specific logging_error import for clarity
2025-09-09 18:50:24 +08:00
Tunglies c54d89a465 feat: add support for Windows ARM64 in development workflow 2025-09-09 16:55:46 +08:00
Tunglies 55b95a1985 Revert "feat: update Cargo.toml for 2024 edition and optimize release profiles (#4681)"
This reverts commit 31e3104c7f.
2025-09-08 21:48:09 +08:00
Tunglies 31e3104c7f feat: update Cargo.toml for 2024 edition and optimize release profiles (#4681)
* feat: update Cargo.toml for 2024 edition and optimize release profiles

* feat: refactor environment variable settings for Linux and improve code organization

* Refactor conditional statements to use `&&` for improved readability

- Updated multiple files to combine nested `if let` statements using `&&` for better clarity and conciseness.
- This change enhances the readability of the code by reducing indentation levels and making the conditions more straightforward.
- Affected files include: media_unlock_checker.rs, profile.rs, clash.rs, profiles.rs, async_proxy_query.rs, core.rs, handle.rs, hotkey.rs, service.rs, timer.rs, tray/mod.rs, merge.rs, seq.rs, config.rs, proxy.rs, window.rs, general.rs, dirs.rs, i18n.rs, init.rs, network.rs, and window.rs in the resolve module.

* refactor: streamline conditional checks using `&&` for improved readability
2025-09-08 13:57:32 +08:00
wonfen 58a0089b19 fix: workflow file name 2025-09-07 15:24:14 +08:00
Tunglies 043ed4cb31 feat: add known issues section and update lightweight mode handling 2025-09-07 13:00:51 +08:00
Tunglies f64c01044c feat: bump version to 2.4.3 and update changelog for macOS intel Mihomo compatibility 2025-09-07 12:06:33 +08:00
Tunglies 5dca724017 feat: update Vite configuration for improved chunking and build options 2025-09-06 21:24:46 +08:00
Tunglies 579f9bd1f8 feat: add path and process dependencies; remove unused SCSS preprocessor options 2025-09-06 21:04:58 +08:00
Tunglies 7c9104a5b9 feat: optimize home page loading with lazy loading and improve card rendering logic 2025-09-06 20:38:33 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 14d1531469 chore(deps): update npm dependencies (#4567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-06 14:11:30 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 74e1e92607 chore(deps): update rust crate zip to v5 (#4653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-06 14:11:09 +08:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> f7a56c0eb3 chore(deps): update cargo dependencies (#4586)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-06 14:10:35 +08:00
Tunglies e2fa76332a fix: remove unused ProxyRequestCache import and improve proxy update error handling 2025-09-06 14:10:09 +08:00
Tunglies 0daa8720cd feat: implement i18n lazy loading optimization
🚀 Performance improvements:
- Replace static language imports with dynamic imports
- Load only current language on startup instead of all 13 languages
- Implement on-demand loading when switching languages

📦 Bundle optimization:
- Reduce initial bundle size by avoiding preloading all language files
- Add resource caching to prevent reloading same language
- Support all 13 languages: en, ru, zh, fa, tt, id, ar, ko, tr, de, es, jp, zhtw

🔧 Technical changes:
- Convert i18n.ts to use dynamic import() for language resources
- Add async initializeLanguage() for app startup
- Create useI18n hook for language management with loading states
- Update main.tsx for async language initialization
- Fix language display labels in settings dropdown
- Maintain backward compatibility with existing language system

✅ Fixed issues:
- Resolve infinite loop in React components
- Fix missing language labels in settings UI
- Prevent circular dependencies in language loading
- Add proper error handling and fallback mechanisms
2025-09-06 14:05:36 +08:00
Tunglies f70b8b1213 Revert "fix: auto-detect KDE/Plasma and switch to X11 as backend to fix titlebar button freeze (#4523)"
This reverts commit d58c0a7df5.
2025-09-06 11:59:33 +08:00
wonfen feb3dfbe86 chore: use UTF+8 time to build 2025-09-05 11:21:34 +08:00
Tunglies f38e4a6cac fix: refactor proxy fetching to use command methods and improve error handling 2025-09-04 15:32:46 +08:00
Tunglies 893188d693 fix: add basic authorization header support for URL parsing in NetworkManager #4618 2025-09-03 01:07:45 +08:00
Tunglies b989aeb7b0 refactor: clean up imports and remove unused initialization in NetworkManager 2025-09-02 23:48:27 +08:00
Tunglies 40f87c834d fix: update ClientConfig settings for improved connection management 2025-09-02 23:21:05 +08:00
Tunglies 0bb9cb5097 fix: enhance startup speed and fix connection issues during initialization 2025-09-02 23:10:02 +08:00
Tunglies b51797e238 fix: update logging types and clean up ProxyRequestCache usage 2025-09-02 22:19:22 +08:00
Tunglies 926c095409 perf: update ProxyRequestCache to use boxed CacheEntry for improved memory management 2025-09-02 19:09:44 +08:00
Tunglies 0c65f8ebad fix: remove macOS specific conditional compilation for logging_error import 2025-09-02 18:36:13 +08:00
Tunglies 63f4295063 fix: update required service version to 1.1.2 2025-09-02 16:09:21 +08:00
Tunglies d2b38a8a3c fix: optimize async handler usage in singleton checks and resource initialization #4576, #4590, #4609 2025-09-02 13:37:14 +08:00
Tunglies 45ddb15d56 fix: remove redundant service stop call in CoreManager and clean up unused Mutex import 2025-09-02 12:12:19 +08:00
Tunglies 7aef9d2a5a fix: resolve lightweight mode state detection issues and improve logging #3814 2025-09-02 08:00:53 +08:00
wonfen 45fdebeaca style: simplify and improve proxy settings UI 2025-09-01 13:57:04 +08:00
wonfen 0ea875f7f7 fix: unify homepage node selection 2025-09-01 11:30:27 +08:00
Guanghui Qinandhiaoxui 1b54c9fc1b fix rpm/deb package naming issues (#4582)
* fix arm64 deb package name

* fix rpm package naming issue

* fix rpm package naming issue

---------

Co-authored-by: hiaoxui <hiaoxui@gmail.com>
2025-09-01 01:12:40 +08:00
Tunglies 89f3adcbef fix: add redirect policy to HTTP client builder 2025-09-01 00:45:39 +08:00
Tunglies b13fef5ad9 fix: add missing allow(dead_code) attribute to block_on method 2025-08-31 16:22:35 +08:00
Tunglies 9110955b63 fix: streamline service availability checks and improve logging for core startup 2025-08-31 16:19:31 +08:00
Tunglies 4508d062f1 chore: bump version to 2.4.2 2025-08-31 15:53:50 +08:00
Junkai W. bea0dde074 Win 下添加代理节点的系统托盘 (#4562)
* add proxy memu in tray

* 添加win下系统托盘 节点
代理->代理组->nodes
同时添加了对应gui同步

* 添加win 系统托盘显示代理节点
且gui和托盘刷新机制

* rust format

* 添加 win下系统托盘节点延迟

* Squashed commit of the following:

commit 44caaa62c5
Merge: 1916e539 3939741a
Author: Junkai W. <129588175+Be-Forever223@users.noreply.github.com>
Date:   Sat Aug 30 02:37:07 2025 +0800

    Merge branch 'dev' into dev

commit 3939741a06
Author: Tunglies <tunglies.dev@outlook.com>
Date:   Sat Aug 30 02:24:47 2025 +0800

    refactor: migrate from serde_yaml to serde_yaml_ng for improved YAML handling (#4568)

    * refactor: migrate from serde_yaml to serde_yaml_ng for improved YAML handling

    * refactor: format code for better readability in DNS configuration

commit f86a1816e0
Author: Tunglies <tunglies.dev@outlook.com>
Date:   Sat Aug 30 02:15:34 2025 +0800

    chore(deps): update sysinfo to 0.37.0 and zip to 4.5.0 in Cargo.toml (#4564)

    * chore(deps): update sysinfo to 0.37.0 and zip to 4.5.0 in Cargo.toml

    * chore(deps): remove libnghttp2-sys dependency and update isahc features in Cargo.toml

    * chore(deps): remove sysinfo and zip from ignoreDeps in renovate.json

commit 9cbd8b4529
Author: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date:   Sat Aug 30 01:30:48 2025 +0800

    feat: add x86 OpenSSL installation step for macOS in workflows

commit 5dea73fc2a
Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Date:   Sat Aug 30 01:21:53 2025 +0800

    chore(deps): update npm dependencies (#4542)

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

commit 01af1bea23
Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Date:   Sat Aug 30 01:21:46 2025 +0800

    chore(deps): update rust crate reqwest_dav to 0.2.2 (#4554)

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

commit 1227e86134
Author: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date:   Sat Aug 30 01:12:03 2025 +0800

    Remove unnecessary "rustls-tls" feature from reqwest dependency in Cargo.toml

commit c6a6ea48dd
Author: Tunglies <tunglies.dev@outlook.com>
Date:   Fri Aug 29 23:51:09 2025 +0800

    refactor: enhance async initialization and streamline setup process (#4560)

    * feat: Implement DNS management for macOS

    - Added `set_public_dns` and `restore_public_dns` functions in `dns.rs` to manage system DNS settings.
    - Introduced `resolve` module to encapsulate DNS and scheme resolution functionalities.
    - Implemented `resolve_scheme` function in `scheme.rs` to handle deep links and profile imports.
    - Created UI readiness management in `ui.rs` to track and update UI loading states.
    - Developed window management logic in `window.rs` to handle window creation and visibility.
    - Added initial loading overlay script in `window_script.rs` for better user experience during startup.
    - Updated server handling in `server.rs` to integrate new resolve functionalities.
    - Refactored window creation calls in `window_manager.rs` to use the new window management logic.

    * refactor: streamline asynchronous handling in config and resolve setup

    * Revert "refactor: streamline asynchronous handling in config and resolve setup"

    This reverts commit 23d7dc86d5.

    * fix: optimize asynchronous memory handling

    * fix: enhance task logging by adding size check for special cases

    * refactor: enhance async initialization and streamline setup process

    * refactor: optimize async setup by consolidating initialization tasks

    * chore: update changelog for Mihomo(Meta) kernel upgrade to v1.19.13

    * fix: improve startup phase initialization performance

    * refactor: optimize file read/write performance to reduce application wait time

    * refactor: simplify app instance exit logic and adjust system proxy guard initialization

    * refactor: change resolve_setup_async to synchronous execution for improved performance

    * refactor: update resolve_setup_async to accept AppHandle for improved initialization flow

    * refactor: remove unnecessary initialization of portable flag in run function

    * refactor: consolidate async initialization tasks into a single blocking call for improved execution flow

    * refactor: optimize resolve_setup_async by restructuring async tasks for improved concurrency

    * refactor: streamline resolve_setup_async and embed_server for improved async handling

    * refactor: separate synchronous and asynchronous setup functions for improved clarity

    * refactor: simplify async notification handling and remove redundant network manager initialization

    * refactor: enhance async handling in proxy request cache and window creation logic

    * refactor: improve code formatting and readability in ProxyRequestCache

    * refactor: adjust singleton check timeout and optimize trace size conditions

    * refactor: update TRACE_SPECIAL_SIZE to include additional size condition

    * refactor: update kode-bridge dependency to version 0.2.1-rc2

    * refactor: replace RwLock with AtomicBool for UI readiness and implement event-driven monitoring

    * refactor: convert async functions to synchronous for window management

    * Update src-tauri/src/utils/resolve/window.rs

    * fix: handle missing app_handle in create_window function

    * Update src-tauri/src/module/lightweight.rs

* format
2025-08-31 14:20:57 +08:00
wonfen 3e674b186f fix: refine release workflow 2025-08-31 08:49:30 +08:00
551 changed files with 57931 additions and 36034 deletions
+4
View File
@@ -3,3 +3,7 @@ linker = "aarch64-linux-gnu-gcc"
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
[alias]
clippy-all = "clippy --all-targets --all-features -- -D warnings"
clippy-only = "clippy --all-targets --features clippy -- -D warnings"
+6 -6
View File
@@ -13,8 +13,8 @@ body:
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. 请 **务必** 尝试 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本,确定问题是否仍然存在
4. 请 **务必** 查看 [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) 版本更新日志
5. 请 **务必** 尝试 [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) 版本,确定问题是否仍然存在
6. 请 **务必** 按照模板规范详细描述问题以及尝试更新 Alpha 版本,否则issue将会被直接关闭
## Before submitting the issue, please make sure of the following checklist:
@@ -22,8 +22,8 @@ body:
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 check out [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version update log
5. Please be sure to try the [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version to ensure that the problem still exists
4. Please be sure to check out [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) version update log
5. Please be sure to try the [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) version to ensure that the problem still exists
6. Please describe the problem in detail according to the template specification and try to update the Alpha version, otherwise the issue will be closed
- type: textarea
@@ -35,8 +35,8 @@ body:
required: true
- type: textarea
attributes:
label: 软件版本 / Verge Version
description: 请提供Verge的具体版本,如果是alpha版本,请注明下载时间(精确到小时分钟) / Please provide the specific version of Verge. If it is an alpha version, please indicate the download time (accurate to hours and minutes)
label: 软件版本 / CVR Version
description: 请提供 CVR 的具体版本,如果是 AutoBuild 版本,请注明下载时间(精确到小时分钟) / Please provide the specific version of CVR. If it is an AutoBuild version, please indicate the download time (accurate to hours and minutes)
render: text
validations:
required: true
+1
View File
@@ -1,3 +1,4 @@
blank_issues_enabled: false
contact_links:
- name: 讨论交流 / Communication
url: https://t.me/clash_verge_rev
+2 -2
View File
@@ -12,13 +12,13 @@ body:
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) 版本测试,确保该功能还未实现
4. 请 **务必** 先下载 [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) 版本测试,确保该功能还未实现
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
4. Please be sure to download the [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) 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
+2 -2
View File
@@ -52,7 +52,7 @@ body:
- type: input
id: verge-version
attributes:
label: 软件版本 / Verge Version
description: 请提供你使用的 Verge 具体版本 / Please provide the specific version of Verge you are using
label: 软件版本 / CVR Version
description: 请提供你使用的 CVR 具体版本 / Please provide the specific version of CVR you are using
validations:
required: true
+19 -18
View File
@@ -25,6 +25,7 @@ env:
TAG_CHANNEL: Alpha
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
HUSKY: 0
concurrency:
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
@@ -34,7 +35,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Check tag and package.json version
id: check_tag
@@ -63,7 +64,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Delete Old Alpha Tags Except Latest
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -179,23 +180,23 @@ jobs:
needs: delete_old_assets
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Fetch UPDATE logs
id: fetch_update_logs
run: |
if [ -f "UPDATELOG.md" ]; then
UPDATE_LOGS=$(awk '/^## v/{if(flag) exit; flag=1} flag' UPDATELOG.md)
if [ -f "Changelog.md" ]; then
UPDATE_LOGS=$(awk '/^## v/{if(flag) exit; flag=1} flag' Changelog.md)
if [ -n "$UPDATE_LOGS" ]; then
echo "Found update logs"
echo "UPDATE_LOGS<<EOF" >> $GITHUB_ENV
echo "$UPDATE_LOGS" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
else
echo "No update sections found in UPDATELOG.md"
echo "No update sections found in Changelog.md"
fi
else
echo "UPDATELOG.md file not found"
echo "Changelog.md file not found"
fi
shell: bash
@@ -274,7 +275,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
@@ -304,9 +305,9 @@ jobs:
echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -359,7 +360,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
@@ -374,9 +375,9 @@ jobs:
save-if: false
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -490,7 +491,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
@@ -502,9 +503,9 @@ jobs:
save-if: false
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -521,8 +522,8 @@ jobs:
- 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
invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/133.0.3065.92/Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ matrix.arch }}.cab
Expand .\Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ 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
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 2
+109 -74
View File
@@ -3,14 +3,15 @@ name: Auto Build
on:
workflow_dispatch:
schedule:
# UTC+8 0,6,12,18
- cron: "0 16,22,4,10 * * *"
# UTC+8 12:00, 18:00 -> UTC 4:00, 10:00
- cron: "0 4,10 * * *"
permissions: write-all
env:
TAG_NAME: autobuild
TAG_CHANNEL: AutoBuild
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
HUSKY: 0
concurrency:
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
@@ -30,35 +31,22 @@ jobs:
if: ${{ needs.check_commit.outputs.should_run == 'true' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Fetch UPDATE logs
id: fetch_update_logs
run: |
if [ -f "UPDATELOG.md" ]; then
UPDATE_LOGS=$(awk '/^## v/{if(flag) exit; flag=1} flag' UPDATELOG.md)
if [ -n "$UPDATE_LOGS" ]; then
echo "Found update logs"
echo "UPDATE_LOGS<<EOF" >> $GITHUB_ENV
echo "$UPDATE_LOGS" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
else
echo "No update sections found in UPDATELOG.md"
fi
else
echo "UPDATELOG.md file not found"
fi
run: bash ./scripts/extract_update_logs.sh
shell: bash
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v4.2.0
name: Install pnpm
with:
run_install: false
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- name: Install dependencies
run: pnpm install --frozen-lockfile
@@ -99,10 +87,10 @@ jobs:
### Linux
#### DEB包(Debian系) 使用 apt ./路径 安装
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
#### RPM包(Redhat系) 使用 dnf ./路径 安装
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhfp.rpm)
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)
### FAQ
- [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
@@ -121,7 +109,7 @@ jobs:
body_path: release.txt
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
generate_release_notes: true
generate_release_notes: false
clean_old_assets:
name: Clean Old Release Assets
@@ -154,10 +142,13 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.91.0"
targets: ${{ matrix.target }}
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
@@ -165,10 +156,13 @@ jobs:
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
cache-all-crates: true
save-if: ${{ github.ref == 'refs/heads/dev' }}
shared-key: autobuild-shared
prefix-key: "v1-rust"
key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
workspaces: |
. -> target
cache-all-crates: true
cache-workspace-crates: true
- name: Install dependencies (ubuntu only)
if: matrix.os == 'ubuntu-22.04'
@@ -185,17 +179,25 @@ jobs:
echo "OPENSSL_LIB_DIR=$(brew --prefix openssl@3)/lib" >> $GITHUB_ENV
echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v4.2.0
name: Install pnpm
with:
run_install: false
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
cache: "pnpm"
- name: Pnpm Cache
uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: "pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
restore-keys: |
pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}
- name: Pnpm install and check
run: |
pnpm i
@@ -204,6 +206,13 @@ jobs:
- name: Release ${{ env.TAG_CHANNEL }} Version
run: pnpm release-version autobuild-latest
- name: Add Rust Target
run: |
# Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
rustup target list --installed
echo "Rust target ${{ matrix.target }} installed."
- name: Tauri build for Windows-macOS-Linux
uses: tauri-apps/tauri-action@v0
env:
@@ -244,10 +253,13 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.91.0"
targets: ${{ matrix.target }}
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
@@ -255,22 +267,33 @@ jobs:
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
cache-all-crates: true
save-if: ${{ github.ref == 'refs/heads/dev' }}
shared-key: autobuild-shared
prefix-key: "v1-rust"
key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
workspaces: |
. -> target
cache-all-crates: true
cache-workspace-crates: true
- name: Install pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v4.2.0
with:
run_install: false
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
cache: "pnpm"
- name: Pnpm Cache
uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: "pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
restore-keys: |
pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}
- name: Pnpm install and check
run: |
pnpm i
@@ -328,6 +351,13 @@ jobs:
gcc-arm-linux-gnueabihf \
g++-arm-linux-gnueabihf
- name: Add Rust Target
run: |
# Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
rustup target list --installed
echo "Rust target ${{ matrix.target }} installed."
- name: Tauri Build for Linux
run: |
export PKG_CONFIG_ALLOW_CROSS=1
@@ -359,8 +389,8 @@ jobs:
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
files: |
src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
src-tauri/target/${{ matrix.target }}/release/bundle/rpm/*.rpm
target/${{ matrix.target }}/release/bundle/deb/*.deb
target/${{ matrix.target }}/release/bundle/rpm/*.rpm
autobuild-x86-arm-windows_webview2:
name: Autobuild x86 and ARM Windows with WebView2
@@ -379,7 +409,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
@@ -387,22 +417,33 @@ jobs:
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
cache-all-crates: true
save-if: ${{ github.ref == 'refs/heads/dev' }}
shared-key: autobuild-shared
prefix-key: "v1-rust"
key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
workspaces: |
. -> target
cache-all-crates: true
cache-workspace-crates: true
- name: Install pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v4.2.0
with:
run_install: false
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
cache: "pnpm"
- name: Pnpm Cache
uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: "pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
restore-keys: |
pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}
- name: Pnpm install and check
run: |
pnpm i
@@ -413,11 +454,18 @@ jobs:
- 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
invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/133.0.3065.92/Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ matrix.arch }}.cab
Expand .\Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ 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: Add Rust Target
run: |
# Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
rustup target list --installed
echo "Rust target ${{ matrix.target }} installed."
- name: Tauri build for Windows
id: build
uses: tauri-apps/tauri-action@v0
@@ -433,19 +481,19 @@ jobs:
- name: Rename
run: |
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
$files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.exe$", "_fixed_webview2-setup.exe"
Rename-Item $file.FullName $newName
}
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
$files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.nsis\.zip$", "_fixed_webview2-setup.nsis.zip"
Rename-Item $file.FullName $newName
}
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
$files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.exe\.sig$", "_fixed_webview2-setup.exe.sig"
Rename-Item $file.FullName $newName
@@ -458,7 +506,7 @@ jobs:
name: "Clash Verge Rev ${{ env.TAG_CHANNEL }}"
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*setup*
files: target/${{ matrix.target }}/release/bundle/nsis/*setup*
- name: Portable Bundle
run: pnpm portable-fixed-webview2 ${{ matrix.target }} --${{ env.TAG_NAME }}
@@ -477,32 +525,19 @@ jobs:
]
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Fetch UPDATE logs
id: fetch_update_logs
run: |
if [ -f "UPDATELOG.md" ]; then
UPDATE_LOGS=$(awk '/^## v/{if(flag) exit; flag=1} flag' UPDATELOG.md)
if [ -n "$UPDATE_LOGS" ]; then
echo "Found update logs"
echo "UPDATE_LOGS<<EOF" >> $GITHUB_ENV
echo "$UPDATE_LOGS" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
else
echo "No update sections found in UPDATELOG.md"
fi
else
echo "UPDATELOG.md file not found"
fi
run: bash ./scripts/extract_update_logs.sh
shell: bash
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v4.2.0
name: Install pnpm
with:
run_install: false
@@ -547,10 +582,10 @@ jobs:
### Linux
#### DEB包(Debian系) 使用 apt ./路径 安装
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
#### RPM包(Redhat系) 使用 dnf ./路径 安装
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhfp.rpm)
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)
### FAQ
- [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
@@ -53,7 +53,7 @@ jobs:
autobuild_version: ${{ steps.check.outputs.autobuild_version }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 50
@@ -104,8 +104,8 @@ jobs:
# Generate autobuild version using autobuild-latest format
CURRENT_BASE_VERSION=$(echo "$CURRENT_VERSION" | sed -E 's/-(alpha|beta|rc)(\.[0-9]+)?//g' | sed -E 's/\+[a-zA-Z0-9.-]+//g')
MONTH=$(date +%m)
DAY=$(date +%d)
MONTH=$(TZ=Asia/Shanghai date +%m)
DAY=$(TZ=Asia/Shanghai date +%d)
AUTOBUILD_VERSION="${CURRENT_BASE_VERSION}+autobuild.${MONTH}${DAY}.${LAST_TAURI_COMMIT}"
echo "🏷️ Autobuild version: $AUTOBUILD_VERSION"
+5 -5
View File
@@ -42,7 +42,7 @@ jobs:
autobuild_version: ${{ steps.check.outputs.autobuild_version }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 50
@@ -56,7 +56,7 @@ jobs:
echo "🔍 Finding last commit with Tauri-related changes..."
# Define patterns for Tauri-related files
TAURI_PATTERNS="src/ src-tauri/src src-tauri/Cargo.toml src-tauri/Cargo.lock src-tauri/tauri.*.conf.json src-tauri/build.rs src-tauri/capabilities"
TAURI_PATTERNS="src/ src-tauri/src src-tauri/Cargo.toml Cargo.lock src-tauri/tauri.*.conf.json src-tauri/build.rs src-tauri/capabilities"
# Get the last commit that changed any of these patterns (excluding build artifacts)
LAST_TAURI_COMMIT=""
@@ -88,8 +88,8 @@ jobs:
# Generate autobuild version for consistency
CURRENT_BASE_VERSION=$(echo "$CURRENT_VERSION" | sed -E 's/-(alpha|beta|rc)(\.[0-9]+)?//g' | sed -E 's/\+[a-zA-Z0-9.-]+//g')
MONTH=$(date +%m)
DAY=$(date +%d)
MONTH=$(TZ=Asia/Shanghai date +%m)
DAY=$(TZ=Asia/Shanghai date +%d)
AUTOBUILD_VERSION="${CURRENT_BASE_VERSION}+autobuild.${MONTH}${DAY}.${LAST_TAURI_COMMIT}"
echo "🏷️ Current autobuild version: $AUTOBUILD_VERSION"
@@ -105,7 +105,7 @@ jobs:
needs: check_current_version
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Clean old assets from release
env:
+6 -3
View File
@@ -9,6 +9,9 @@ on:
permissions:
contents: read
env:
HUSKY: 0
jobs:
cargo-check:
# Treat all Rust compiler warnings as errors
@@ -27,7 +30,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
@@ -38,9 +41,9 @@ jobs:
run: rustup target add ${{ matrix.target }}
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "20"
node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
+68 -18
View File
@@ -13,6 +13,16 @@ on:
required: false
type: boolean
default: true
run_windows_arm64:
description: "运行 Windows ARM64"
required: false
type: boolean
default: true
run_linux_amd64:
description: "运行 Linux amd64"
required: false
type: boolean
default: true
permissions: write-all
env:
@@ -20,6 +30,7 @@ env:
TAG_CHANNEL: DeployTest
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
HUSKY: 0
concurrency:
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
@@ -40,6 +51,16 @@ jobs:
bundle: dmg
id: macos-aarch64
input: run_macos_aarch64
- os: windows-latest
target: aarch64-pc-windows-msvc
bundle: nsis
id: windows-arm64
input: run_windows_arm64
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
bundle: deb
id: linux-amd64
input: run_linux_amd64
runs-on: ${{ matrix.os }}
steps:
@@ -49,24 +70,28 @@ jobs:
- name: Checkout Repository
if: github.event.inputs[matrix.input] == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Rust Stable
if: github.event.inputs[matrix.input] == 'true'
uses: dtolnay/rust-toolchain@stable
- name: Add Rust Target
if: github.event.inputs[matrix.input] == 'true'
run: rustup target add ${{ matrix.target }}
uses: dtolnay/rust-toolchain@1.91.0
- name: Rust Cache
if: github.event.inputs[matrix.input] == 'true'
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
save-if: false
save-if: ${{ github.ref == 'refs/heads/dev' }}
prefix-key: "v1-rust"
key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
workspaces: |
. -> target
cache-all-crates: true
shared-key: autobuild-shared
cache-workspace-crates: true
- name: Install dependencies (ubuntu only)
if: matrix.os == 'ubuntu-22.04' && github.event.inputs[matrix.input] == 'true'
run: |
sudo apt-get update
sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -76,11 +101,20 @@ jobs:
- name: Install Node
if: github.event.inputs[matrix.input] == 'true'
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "20"
node-version: "24.11.1"
cache: "pnpm"
- name: Pnpm Cache
uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: "pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
restore-keys: |
pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}
lookup-only: true
- name: Pnpm install and check
if: github.event.inputs[matrix.input] == 'true'
run: |
@@ -91,6 +125,14 @@ jobs:
if: github.event.inputs[matrix.input] == 'true'
run: pnpm release-version ${{ env.TAG_NAME }}
- name: Add Rust Target
if: github.event.inputs[matrix.input] == 'true'
run: |
# Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
rustup target list --installed
echo "Rust target ${{ matrix.target }} installed."
- name: Tauri build
if: github.event.inputs[matrix.input] == 'true'
uses: tauri-apps/tauri-action@v0
@@ -109,18 +151,26 @@ jobs:
tauriScript: pnpm
args: --target ${{ matrix.target }} -b ${{ matrix.bundle }}
- name: Upload Artifacts
- name: Upload Artifacts (macOS)
if: matrix.os == 'macos-latest' && github.event.inputs[matrix.input] == 'true'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ matrix.target }}
path: src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg
path: target/${{ matrix.target }}/release/bundle/dmg/*.dmg
if-no-files-found: error
- name: Upload Artifacts
- name: Upload Artifacts (Windows)
if: matrix.os == 'windows-latest' && github.event.inputs[matrix.input] == 'true'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ matrix.target }}
path: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*.exe
path: target/${{ matrix.target }}/release/bundle/nsis/*.exe
if-no-files-found: error
- name: Upload Artifacts (Linux)
if: matrix.os == 'ubuntu-22.04' && github.event.inputs[matrix.input] == 'true'
uses: actions/upload-artifact@v5
with:
name: ${{ matrix.target }}
path: target/${{ matrix.target }}/release/bundle/deb/*.deb
if-no-files-found: error
+75
View File
@@ -0,0 +1,75 @@
name: Frontend Check
on:
pull_request:
workflow_dispatch:
env:
HUSKY: 0
jobs:
frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check frontend changes
id: check_frontend
uses: dorny/paths-filter@v3
with:
filters: |
frontend:
- 'src/**'
- '**/*.js'
- '**/*.ts'
- '**/*.tsx'
- '**/*.css'
- '**/*.scss'
- '**/*.json'
- '**/*.md'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'eslint.config.ts'
- 'tsconfig.json'
- 'vite.config.*'
- name: Skip if no frontend changes
if: steps.check_frontend.outputs.frontend != 'true'
run: echo "No frontend changes, skipping frontend checks."
- name: Install pnpm
if: steps.check_frontend.outputs.frontend == 'true'
uses: pnpm/action-setup@v4
with:
run_install: false
- uses: actions/setup-node@v6
if: steps.check_frontend.outputs.frontend == 'true'
with:
node-version: "24.11.1"
cache: "pnpm"
- name: Restore pnpm cache
if: steps.check_frontend.outputs.frontend == 'true'
uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: "pnpm-shared-stable-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}"
restore-keys: |
pnpm-shared-stable-${{ runner.os }}-
- run: pnpm install --frozen-lockfile
if: steps.check_frontend.outputs.frontend == 'true'
- name: Run Prettier check
if: steps.check_frontend.outputs.frontend == 'true'
run: pnpm format:check
- name: Run ESLint
if: steps.check_frontend.outputs.frontend == 'true'
run: pnpm lint
- name: Run TypeScript typecheck
if: steps.check_frontend.outputs.frontend == 'true'
run: pnpm typecheck
+45 -31
View File
@@ -3,6 +3,8 @@ name: Clippy Lint
on:
pull_request:
workflow_dispatch:
env:
HUSKY: 0
jobs:
clippy:
@@ -19,11 +21,36 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Check src-tauri changes
if: github.event_name != 'workflow_dispatch'
id: check_changes
uses: dorny/paths-filter@v3
with:
filters: |
rust:
- 'src-tauri/**'
- name: Skip if src-tauri not changed
if: github.event_name != 'workflow_dispatch' && steps.check_changes.outputs.rust != 'true'
run: echo "No src-tauri changes, skipping clippy lint."
- name: Continue if src-tauri changed
if: github.event_name != 'workflow_dispatch' && steps.check_changes.outputs.rust == 'true'
run: echo "src-tauri changed, running clippy lint."
- name: Manual trigger - always run
if: github.event_name == 'workflow_dispatch'
run: |
echo "Manual trigger detected: skipping changes check and running clippy."
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
components: clippy
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
@@ -31,10 +58,13 @@ jobs:
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
save-if: false
cache-all-crates: false
shared-key: autobuild-shared
save-if: ${{ github.ref == 'refs/heads/dev' }}
prefix-key: "v1-rust"
key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
workspaces: |
. -> target
cache-all-crates: true
cache-workspace-crates: true
- name: Install dependencies (ubuntu only)
if: matrix.os == 'ubuntu-22.04'
@@ -42,29 +72,13 @@ jobs:
sudo apt-get update
sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
run_install: false
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "pnpm"
- name: Pnpm install and check
run: |
pnpm i
pnpm run prebuild ${{ matrix.target }}
# This workflow runs linting using cargo clippy.
# Note: If the web build step is skipped,
# cargo clippy will fail to run due to missing web dist in the Tauri environment.
- name: Build Web Assets
run: pnpm run web:build
env:
NODE_OPTIONS: "--max_old_space_size=4096"
- name: Run Clippy
run: cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets --all-features -- -D warnings
working-directory: ./src-tauri
run: cargo clippy-all
- name: Run Logging Check
working-directory: ./src-tauri
shell: bash
run: |
cargo install --git https://github.com/clash-verge-rev/clash-verge-logging-check.git
clash-verge-logging-check
+108 -75
View File
@@ -5,12 +5,14 @@ on:
# ! 不再使用 workflow_dispatch 触发。
# workflow_dispatch:
push:
# -rc tag 时预览发布, 跳过 telegram 通知、跳过 winget 提交、跳过 latest.json 文件更新
tags:
- "v*.*.*"
permissions: write-all
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
HUSKY: 0
concurrency:
# only allow per workflow per commit (and not pr) to run at a time
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
@@ -22,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -67,24 +69,11 @@ jobs:
needs: [release, release-for-linux-arm, release-for-fixed-webview2]
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Fetch UPDATE logs
id: fetch_update_logs
run: |
if [ -f "UPDATELOG.md" ]; then
UPDATE_LOGS=$(awk '/^## v/{if(flag) exit; flag=1} flag' UPDATELOG.md)
if [ -n "$UPDATE_LOGS" ]; then
echo "Found update logs"
echo "UPDATE_LOGS<<EOF" >> $GITHUB_ENV
echo "$UPDATE_LOGS" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
else
echo "No update sections found in UPDATELOG.md"
fi
else
echo "UPDATELOG.md file not found"
fi
run: bash ./scripts/extract_update_logs.sh
shell: bash
- name: Set Env
@@ -122,16 +111,17 @@ jobs:
### Linux
#### DEB包(Debian系) 使用 apt ./路径 安装
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
#### RPM包(Redhat系) 使用 dnf ./路径 安装
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhfp.rpm)
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)
### FAQ
- [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
### 稳定机场VPN推荐
- [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6)
Created at ${{ env.BUILDTIME }}.
EOF
@@ -142,7 +132,7 @@ jobs:
name: "Clash Verge Rev ${{ env.TAG_NAME }}"
body_path: release.txt
draft: false
prerelease: false
prerelease: ${{ contains(github.ref_name, '-rc') }}
token: ${{ secrets.GITHUB_TOKEN }}
# generate_release_notes: true
@@ -167,10 +157,13 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.91.0"
targets: ${{ matrix.target }}
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
@@ -178,8 +171,13 @@ jobs:
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
save-if: false
save-if: ${{ github.ref == 'refs/heads/dev' }}
prefix-key: "v1-rust"
key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
workspaces: |
. -> target
cache-all-crates: true
cache-workspace-crates: true
- name: Install dependencies (ubuntu only)
if: matrix.os == 'ubuntu-22.04'
@@ -197,9 +195,9 @@ jobs:
echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -211,8 +209,16 @@ jobs:
pnpm i
pnpm run prebuild ${{ matrix.target }}
- name: Add Rust Target
run: |
# Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
rustup target list --installed
echo "Rust target ${{ matrix.target }} installed."
- name: Tauri build
uses: tauri-apps/tauri-action@v0
# 上游 5.24 修改了 latest.json 的生成逻辑,且依赖 tauri-plugin-update 2.10.0 暂未发布,故锁定在 0.5.23 版本
uses: tauri-apps/tauri-action@v0.6.0
env:
NODE_OPTIONS: "--max_old_space_size=4096"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -229,7 +235,7 @@ jobs:
releaseName: "Clash Verge Rev ${{ github.ref_name }}"
releaseBody: "Draft release, will be updated later."
releaseDraft: true
prerelease: false
prerelease: ${{ contains(github.ref_name, '-rc') }}
tauriScript: pnpm
args: --target ${{ matrix.target }}
includeUpdaterJson: true
@@ -250,10 +256,13 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.91.0"
targets: ${{ matrix.target }}
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
@@ -261,13 +270,18 @@ jobs:
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
save-if: false
save-if: ${{ github.ref == 'refs/heads/dev' }}
prefix-key: "v1-rust"
key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
workspaces: |
. -> target
cache-all-crates: true
cache-workspace-crates: true
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -323,6 +337,13 @@ jobs:
gcc-arm-linux-gnueabihf \
g++-arm-linux-gnueabihf
- name: Add Rust Target
run: |
# Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
rustup target list --installed
echo "Rust target ${{ matrix.target }} installed."
- name: Build for Linux
run: |
export PKG_CONFIG_ALLOW_CROSS=1
@@ -353,9 +374,10 @@ jobs:
name: "Clash Verge Rev v${{env.VERSION}}"
body: "See release notes for detailed changelog."
token: ${{ secrets.GITHUB_TOKEN }}
prerelease: ${{ contains(github.ref_name, '-rc') }}
files: |
src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
src-tauri/target/${{ matrix.target }}/release/bundle/rpm/*.rpm
target/${{ matrix.target }}/release/bundle/deb/*.deb
target/${{ matrix.target }}/release/bundle/rpm/*.rpm
release-for-fixed-webview2:
name: Release Build for Fixed WebView2
@@ -373,7 +395,13 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.91.0"
targets: ${{ matrix.target }}
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
@@ -381,13 +409,18 @@ jobs:
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
save-if: false
save-if: ${{ github.ref == 'refs/heads/dev' }}
prefix-key: "v1-rust"
key: "rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}"
workspaces: |
. -> target
cache-all-crates: true
cache-workspace-crates: true
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -401,14 +434,21 @@ jobs:
- 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
invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/133.0.3065.92/Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ matrix.arch }}.cab
Expand .\Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ 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: Add Rust Target
run: |
# Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
rustup target list --installed
echo "Rust target ${{ matrix.target }} installed."
- name: Tauri build
id: build
uses: tauri-apps/tauri-action@v0
uses: tauri-apps/tauri-action@v0.6.0
env:
NODE_OPTIONS: "--max_old_space_size=4096"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -420,19 +460,19 @@ jobs:
- name: Rename
run: |
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
$files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.exe$", "_fixed_webview2-setup.exe"
Rename-Item $file.FullName $newName
}
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
$files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.nsis\.zip$", "_fixed_webview2-setup.nsis.zip"
Rename-Item $file.FullName $newName
}
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
$files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
foreach ($file in $files) {
$newName = $file.Name -replace "-setup\.exe\.sig$", "_fixed_webview2-setup.exe.sig"
Rename-Item $file.FullName $newName
@@ -445,7 +485,8 @@ jobs:
name: "Clash Verge Rev v${{steps.build.outputs.appVersion}}"
body: "See release notes for detailed changelog."
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*setup*
prerelease: ${{ contains(github.ref_name, '-rc') }}
files: target/${{ matrix.target }}/release/bundle/nsis/*setup*
- name: Portable Bundle
run: pnpm portable-fixed-webview2 ${{ matrix.target }}
@@ -453,17 +494,18 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-update:
if: ${{ !contains(github.ref_name, '-rc') }}
name: Release Update
runs-on: ubuntu-latest
needs: [release, release-for-linux-arm]
needs: [update_tag]
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -479,16 +521,17 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-update-for-fixed-webview2:
if: ${{ !contains(github.ref_name, '-rc') }}
runs-on: ubuntu-latest
needs: [release-for-fixed-webview2]
needs: [update_tag]
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -504,12 +547,13 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
submit-to-winget:
if: ${{ !contains(github.ref_name, '-rc') }}
name: Submit to Winget
runs-on: ubuntu-latest
needs: [update_tag, release-update]
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Get Version
@@ -527,6 +571,7 @@ jobs:
token: ${{ secrets.WINGET_TOKEN }}
notify-telegram:
if: ${{ !contains(github.ref_name, '-rc') }}
name: Notify Telegram
runs-on: ubuntu-latest
needs:
@@ -538,30 +583,17 @@ jobs:
]
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Fetch UPDATE logs
id: fetch_update_logs
run: |
if [ -f "UPDATELOG.md" ]; then
UPDATE_LOGS=$(awk '/^## v/{if(flag) exit; flag=1} flag' UPDATELOG.md)
if [ -n "$UPDATE_LOGS" ]; then
echo "Found update logs"
echo "UPDATE_LOGS<<EOF" >> $GITHUB_ENV
echo "$UPDATE_LOGS" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
else
echo "No update sections found in UPDATELOG.md"
fi
else
echo "UPDATELOG.md file not found"
fi
run: bash ./scripts/extract_update_logs.sh
shell: bash
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -605,16 +637,17 @@ jobs:
### Linux
#### DEB包(Debian系) 使用 apt ./路径 安装
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
#### RPM包(Redhat系) 使用 dnf ./路径 安装
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhfp.rpm)
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)
### FAQ
- [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
### 稳定机场VPN推荐
- [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6)
Created at ${{ env.BUILDTIME }}.
EOF
@@ -7,32 +7,38 @@ name: Check Formatting
on:
pull_request:
env:
HUSKY: 0
jobs:
rustfmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Check Rust changes
id: check_rust
uses: dorny/paths-filter@v3
with:
filters: |
rust:
- 'src-tauri/**'
- '**/*.rs'
- name: Skip if no Rust changes
if: steps.check_rust.outputs.rust != 'true'
run: echo "No Rust changes, skipping rustfmt."
- name: install Rust stable and rustfmt
if: steps.check_rust.outputs.rust == 'true'
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: run cargo fmt
if: steps.check_rust.outputs.rust == 'true'
run: cargo fmt --manifest-path ./src-tauri/Cargo.toml --all -- --check
prettier:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "lts/*"
- run: corepack enable
- run: pnpm install --frozen-lockfile
- run: pnpm format:check
# taplo:
# name: taplo (.toml files)
# runs-on: ubuntu-latest
+9 -6
View File
@@ -2,17 +2,20 @@ name: Updater CI
on: workflow_dispatch
permissions: write-all
env:
HUSKY: 0
jobs:
release-update:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -31,12 +34,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22"
node-version: "24.11.1"
- uses: pnpm/action-setup@v4
name: Install pnpm
+2
View File
@@ -10,3 +10,5 @@ scripts/_env.sh
.tool-versions
.idea
.old
.eslintcache
target
+46 -15
View File
@@ -1,26 +1,57 @@
#!/bin/bash
set -euo pipefail
#pnpm pretty-quick --staged
ROOT_DIR="$(git rev-parse --show-toplevel)"
cd "$ROOT_DIR"
if git diff --cached --name-only | grep -q '^src/'; then
pnpm format:check
if [ $? -ne 0 ]; then
echo "Code format check failed in src/. Please fix formatting issues."
exit 1
fi
if ! command -v pnpm >/dev/null 2>&1; then
echo "❌ pnpm is required for pre-commit checks."
exit 1
fi
if git diff --cached --name-only | grep -q '^src-tauri/'; then
LOCALE_DIFF="$(git diff --cached --name-only --diff-filter=ACMR | grep -E '^src/locales/' || true)"
if [ -n "$LOCALE_DIFF" ]; then
echo "[pre-commit] Locale changes detected. Regenerating i18n types..."
pnpm i18n:types
if [ -d src/types/generated ]; then
echo "[pre-commit] Staging regenerated i18n type artifacts..."
git add src/types/generated
fi
fi
echo "[pre-commit] Running pnpm format before lint..."
pnpm format
echo "[pre-commit] Running lint-staged for JS/TS files..."
pnpm exec lint-staged
RUST_FILES="$(git diff --cached --name-only --diff-filter=ACMR | grep -E '^src-tauri/.*\.rs$' || true)"
if [ -n "$RUST_FILES" ]; then
echo "[pre-commit] Formatting Rust changes with cargo fmt..."
(
cd src-tauri
cargo fmt
if [ $? -ne 0 ]; then
echo "rustfmt failed to format the code. Please fix the issues and try again."
exit 1
)
while IFS= read -r file; do
[ -n "$file" ] && git add "$file"
done <<< "$RUST_FILES"
echo "[pre-commit] Linting Rust changes with cargo clippy..."
(
cd src-tauri
cargo clippy-all
if ! command -v clash-verge-logging-check >/dev/null 2>&1; then
echo "[pre-commit] Installing clash-verge-logging-check..."
cargo install --git https://github.com/clash-verge-rev/clash-verge-logging-check.git
fi
cd ..
clash-verge-logging-check
)
fi
#git add .
TS_FILES="$(git diff --cached --name-only --diff-filter=ACMR | grep -E '\.(ts|tsx)$' || true)"
if [ -n "$TS_FILES" ]; then
echo "[pre-commit] Running TypeScript type check..."
pnpm typecheck
fi
# 允许提交
exit 0
echo "[pre-commit] All checks completed successfully."
+33 -25
View File
@@ -1,34 +1,42 @@
#!/bin/bash
set -euo pipefail
# $1: remote name (e.g., origin)
# $2: remote url (e.g., git@github.com:clash-verge-rev/clash-verge-rev.git)
remote_name="${1:-origin}"
remote_url="${2:-unknown}"
if git diff --cached --name-only | grep -q '^src-tauri/'; then
cargo clippy --manifest-path ./src-tauri/Cargo.toml
if [ $? -ne 0 ]; then
echo "Clippy found issues in src-tauri. Please fix them before pushing."
exit 1
fi
ROOT_DIR="$(git rev-parse --show-toplevel)"
cd "$ROOT_DIR"
if ! command -v pnpm >/dev/null 2>&1; then
echo "❌ pnpm is required for pre-push checks."
exit 1
fi
echo "[pre-push] Preparing to push to '$remote_name' ($remote_url). Running full validation..."
# Only run format check if the remote exists and is the main repo
remote_name="$1"
if git remote get-url "$remote_name" >/dev/null 2>&1; then
remote_url=$(git remote get-url "$remote_name")
if [[ "$remote_url" =~ github\.com[:/]+clash-verge-rev/clash-verge-rev(\.git)?$ ]]; then
echo "[pre-push] Detected push to clash-verge-rev/clash-verge-rev ($remote_url)"
echo "[pre-push] Running pnpm format:check..."
pnpm format:check
if [ $? -ne 0 ]; then
echo "❌ Code format check failed. Please fix formatting before pushing."
exit 1
fi
else
echo "[pre-push] Not pushing to target repo. Skipping format check."
fi
echo "[pre-push] Checking Prettier formatting..."
pnpm format:check
echo "[pre-push] Running ESLint..."
pnpm lint
echo "[pre-push] Running TypeScript type checking..."
pnpm typecheck
if command -v cargo >/dev/null 2>&1; then
echo "[pre-push] Verifying Rust formatting..."
(
cd src-tauri
cargo fmt --check
)
echo "[pre-push] Running cargo clippy..."
(
cd src-tauri
cargo clippy-all
)
else
echo "[pre-push] Remote $remote_name does not exist. Skipping format check."
echo "[pre-push] ⚠️ cargo not found; skipping Rust checks."
fi
exit 0
echo "[pre-push] All checks passed."
+3 -1
View File
@@ -1,8 +1,10 @@
# README.md
# UPDATELOG.md
# Changelog.md
# CONTRIBUTING.md
pnpm-lock.yaml
src-tauri/target/
src-tauri/gen/
target
+1 -1
View File
@@ -11,6 +11,6 @@
"arrowParens": "always",
"proseWrap": "preserve",
"htmlWhitespaceSensitivity": "css",
"endOfLine": "lf",
"endOfLine": "auto",
"embeddedLanguageFormatting": "auto"
}
+63 -75
View File
@@ -1,144 +1,132 @@
# 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.
Thank you for your interest in contributing to **Clash Verge Rev**! This guide provides instructions to help you set up your development environment and start contributing effectively.
## Internationalization (i18n)
We welcome translations and improvements to existing locales. For details on contributing translations, please see [CONTRIBUTING_i18n.md](docs/CONTRIBUTING_i18n.md).
## 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:
Before contributing, you need to set up your development environment. Follow the steps below carefully.
### Prerequisites
1. **Install Rust and Node.js**: Our project requires both Rust and Node.js. Please follow the instructions provided [here](https://tauri.app/start/prerequisites/) to install them on your system.
1. **Install Rust and Node.js**
Our project requires both Rust and Node.js. Follow the official installation instructions [here](https://tauri.app/start/prerequisites/).
### Setup for Windows Users
### Windows Users
> [!NOTE]
> **If you are using a Windows ARM device, you additionally need to install [LLVM](https://github.com/llvm/llvm-project/releases) (including clang) and set the environment variable.**
>
> Because the `ring` crate is compiled based on `clang` under Windows ARM.
> [!NOTE]
> **Windows ARM users must also install [LLVM](https://github.com/llvm/llvm-project/releases) (including clang) and set the corresponding environment variables.**
> The `ring` crate depends on `clang` when building on Windows ARM.
If you're a Windows user, you may need to perform some additional steps:
Additional steps for Windows:
- 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
- Ensure Rust and Node.js are added to your system `PATH`.
When you setup `Rust` environment, Only use toolchain with `Windows MSVC` , to change settings follow command:
- Install the GNU `patch` tool.
```shell
- Use the MSVC toolchain for Rust:
```bash
rustup target add x86_64-pc-windows-msvc
rustup set default-host x86_64-pc-windows-msvc
```
### Install Node.js Package
### Install Node.js Package Manager
After installing Rust and Node.js, install the necessary Node.js and Node Package Manager:
Enable `corepack`:
```shell
npm install pnpm -g
```bash
corepack enable
```
### Install Dependencies
### Install Project Dependencies
Install node packages
Node.js dependencies:
```shell
```bash
pnpm install
```
Install apt packages ONLY for Ubuntu
Ubuntu-only system packages:
```shell
apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
```bash
sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
```
### Download the Mihomo Core Binary
### Download the Mihomo Core Binary (Automatic)
You have two options for downloading the clash binary:
- Automatically download it via the provided script:
```shell
pnpm run prebuild
# Use '--force' to force update to the latest version
# pnpm run prebuild --force
```
- Manually download it from the [Mihomo release](https://github.com/MetaCubeX/mihomo/releases). After downloading, rename the binary according to the [Tauri configuration](https://tauri.app/v1/api/config#bundleconfig.externalbin).
```bash
pnpm run prebuild
pnpm run prebuild --force # Re-download and overwrite Mihomo core and service binaries
```
### 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
```bash
pnpm dev # Standard
pnpm dev:diff # If an app instance already exists
pnpm dev:tauri # Run Tauri development mode
```
### Build the Project
To build this project:
Standard build:
```shell
```bash
pnpm build
```
For a faster build, use the following command
Fast build for testing:
```shell
```bash
pnpm build:fast
```
This uses Rust's fast-release profile which significantly reduces compilation time by disabling optimization and LTO. The resulting binary will be larger and less performant than the standard build, but it's useful for testing changes quickly.
### Clean Build
The `Artifacts` will display in the `log` in the Terminal.
### Build clean
To clean rust build:
```shell
```bash
pnpm clean
```
### Portable Version (Windows Only)
To package portable version after the build:
```shell
```bash
pnpm portable
```
## Contributing Your Changes
#### Before commit your changes
### Before Committing
If you changed the rust code, it's recommanded to execute code style formatting and quailty checks.
1. Code quailty checks
**Code quality checks:**
```bash
# For rust backend
$ clash-verge-rev: pnpm clippy
# For frontend (not yet).
# Rust backend
cargo clippy-all
# Frontend
pnpm lint
```
2. Code style formatting
**Code formatting:**
```bash
# For rust backend
$ clash-verge-rev: cd src-tauri
$ clash-verge-rev/src-tauri: cargo fmt
# For frontend
$ clash-verge-rev: pnpm format:check
$ clash-verge-rev: pnpm format
# Rust backend
cargo fmt
# Frontend
pnpm format
```
Once you have made your changes:
### Submitting 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!
2. Create a new branch for your feature or bug fix.
3. Commit your changes with clear messages.
4. Push your branch and submit a pull request.
We appreciate your contributions and look forward to your participation!
+2213 -1549
View File
File diff suppressed because it is too large Load Diff
+134
View File
@@ -0,0 +1,134 @@
[workspace]
members = [
"src-tauri",
"crates/clash-verge-draft",
"crates/clash-verge-logging",
"crates/clash-verge-signal",
"crates/tauri-plugin-clash-verge-sysinfo",
"crates/clash-verge-types",
]
resolver = "2"
[workspace.package]
edition = "2024"
rust-version = "1.91"
[profile.release]
panic = "abort"
codegen-units = 1
lto = "thin"
opt-level = 3
debug = false
strip = true
overflow-checks = false
rpath = false
[profile.dev]
incremental = true
codegen-units = 64
opt-level = 0
debug = true
strip = "none"
overflow-checks = true
lto = false
rpath = false
[profile.fast-release]
inherits = "release"
codegen-units = 64
incremental = true
lto = false
opt-level = 0
debug = true
strip = false
[workspace.dependencies]
clash-verge-draft = { path = "crates/clash-verge-draft" }
clash-verge-logging = { path = "crates/clash-verge-logging" }
clash-verge-signal = { path = "crates/clash-verge-signal" }
clash-verge-types = { path = "crates/clash-verge-types" }
tauri-plugin-clash-verge-sysinfo = { path = "crates/tauri-plugin-clash-verge-sysinfo" }
tauri = { version = "2.9.4" }
tauri-plugin-clipboard-manager = "2.3.2"
parking_lot = { version = "0.12.5", features = ["hardware-lock-elision"] }
anyhow = "1.0.100"
criterion = { version = "0.7.0", features = ["async_tokio"] }
tokio = { version = "1.48.0", features = [
"rt-multi-thread",
"macros",
"time",
"sync",
] }
flexi_logger = "0.31.7"
log = "0.4.29"
smartstring = { version = "1.0.1" }
compact_str = { version = "0.9.0", features = ["serde"] }
serde = { version = "1.0.228" }
serde_json = { version = "1.0.145" }
serde_yaml_ng = { version = "0.10.0" }
# *** For Windows platform only ***
deelevate = "0.2.0"
# *********************************
[workspace.lints.clippy]
correctness = { level = "deny", priority = -1 }
suspicious = { level = "deny", priority = -1 }
unwrap_used = "warn"
expect_used = "warn"
panic = "deny"
unimplemented = "deny"
todo = "warn"
dbg_macro = "warn"
clone_on_ref_ptr = "warn"
rc_clone_in_vec_init = "warn"
large_stack_arrays = "warn"
large_const_arrays = "warn"
async_yields_async = "deny"
mutex_atomic = "deny"
mutex_integer = "deny"
rc_mutex = "deny"
unused_async = "deny"
await_holding_lock = "deny"
large_futures = "deny"
future_not_send = "deny"
redundant_else = "deny"
needless_continue = "deny"
needless_raw_string_hashes = "deny"
or_fun_call = "deny"
cognitive_complexity = "deny"
useless_let_if_seq = "deny"
use_self = "deny"
tuple_array_conversions = "deny"
trait_duplication_in_bounds = "deny"
suspicious_operation_groupings = "deny"
string_lit_as_bytes = "deny"
significant_drop_tightening = "deny"
significant_drop_in_scrutinee = "deny"
redundant_clone = "deny"
# option_if_let_else = "deny" // 过于激进,暂时不开启
needless_pass_by_ref_mut = "deny"
needless_collect = "deny"
missing_const_for_fn = "deny"
iter_with_drain = "deny"
iter_on_single_items = "deny"
iter_on_empty_collections = "deny"
# fallible_impl_from = "deny" // 过于激进,暂时不开启
equatable_if_let = "deny"
collection_is_never_read = "deny"
branches_sharing_code = "deny"
pathbuf_init_then_push = "deny"
option_as_ref_cloned = "deny"
large_types_passed_by_value = "deny"
# implicit_clone = "deny" // 可能会造成额外开销,暂时不开启
expl_impl_clone_on_copy = "deny"
copy_iterator = "deny"
cloned_instead_of_copied = "deny"
# self_only_used_in_recursion = "deny" // Since 1.92.0
unnecessary_self_imports = "deny"
unused_trait_names = "deny"
wildcard_imports = "deny"
unnecessary_wraps = "deny"
+287
View File
@@ -0,0 +1,287 @@
## v2.4.4
- **Mihomo(Meta) 内核升级至 v1.19.17**
### 🐞 修复问题
- Linux 无法切换 TUN 堆栈
- macOS service 启动项显示名称(试验性修改)
- macOS 非预期 Tproxy 端口设置
- 流量图缩放异常
- PAC 自动代理脚本内容无法动态调整
- 兼容从旧版服务模式升级
- Monaco 编辑器的行数上限
- 已删除节点在手动分组中导致配置无法加载
- 仪表盘与托盘状态不同步
- 彻底修复 macOS 连接页面显示异常
- windows 端监听关机信号失败
- 修复代理按钮和高亮状态不同步
- 修复侧边栏可能的未能正确跳转
- 修复解锁测试部分地区图标编码不正确
- 修复 IP 检测切页后强制刷新,改为仅在必要时更新
- 修复在搜索框输入不完整正则直接崩溃
- 修复创建窗口时在非简体中文环境或深色主题下的短暂闪烁
- 修复更新时加载进度条异常
- 升级内核失败导致内核不可用问题
- 修复 macOS 在安装和卸载服务时提示与操作不匹配
- 修复菜单排序模式拖拽异常
- 修复托盘菜单代理组前的异常勾选状态
<details>
<summary><strong> ✨ 新增功能 </strong></summary>
- 支持连接页面各个项目的排序
- 实现可选的自动备份
- 连接页面支持查看已关闭的连接(最近最多 500 个已关闭连接)
- 日志页面支持按时间倒序
- 增加「重新激活订阅」的全局快捷键
- WebView2 Runtime 修复构建升级到 133.0.3065.92
</details>
<details>
<summary><strong> 🚀 优化改进 </strong></summary>
- 网络请求改为使用 rustls,提升 TLS 兼容性
- rustls 避免因服务器证书链配置问题或较新 TLS 要求导致订阅无法导入
- 替换前端信息编辑组件,提供更好性能
- 优化后端内存和性能表现
- 防止退出时可能的禁用 TUN 失败
- 全新 i18n 支持方式
- 优化备份设置布局
- 优化流量图性能表现,实现动态 FPS 和窗口失焦自动暂停
- 性能优化系统状态获取
- 优化托盘菜单当前订阅检测逻辑
- 优化连接页面表格渲染
- 优化链式代理 UI 反馈
- 优化重启应用的资源清理逻辑
- 优化前端数据刷新
- 优化流量采样和数据处理
- 优化应用重启/退出时的资源清理性能, 大幅缩短执行时间
- 优化前端 WebSocket 连接机制
- 改进旧版 Service 需要重新安装检测流程
- 优化 macOS, Linux 和 Windows 系统信号处理
</details>
## v2.4.3
**发行代号:澜**
代号释义:澜象征平稳与融合,本次版本聚焦稳定性、兼容性、性能与体验优化,全面提升整体可靠性。
特别感谢 @Slinetrac, @oomeow, @Lythrilla, @Dragon1573 的出色贡献
### 🐞 修复问题
- 优化服务模式重装逻辑,避免不必要的重复检查
- 修复轻量模式退出无响应的问题
- 修复托盘轻量模式支持退出/进入
- 修复静默启动和自动进入轻量模式时,托盘状态刷新不再依赖窗口创建流程
- macOS Tun/系统代理 模式下图标大小不统一
- 托盘节点切换不再显示隐藏组
- 修复前端 IP 检测无法使用 ipapi, ipsb 提供商
- 修复MacOS 下 Tun开启后 系统代理无法打开的问题
- 修复服务模式启动时,修改、生成配置文件或重启内核可能导致页面卡死的问题
- 修复 Webdav 恢复备份不重启
- 修复 Linux 开机后无法正常代理需要手动设置
- 修复增加订阅或导入订阅文件时订阅页面无更新
- 修复系统代理守卫功能不工作
- 修复 KDE + Wayland 下多屏显示 UI 异常
- 修复 Windows 深色模式下首次启动客户端标题栏颜色异常
- 修复静默启动不加载完整 WebView 的问题
- 修复 Linux WebKit 网络进程的崩溃
- 修复无法导入订阅
- 修复实际导入成功但显示导入失败的问题
- 修复服务不可用时,自动关闭 Tun 模式导致应用卡死问题
- 修复删除订阅时未能实际删除相关文件
- 修复 macOS 连接界面显示异常
- 修复规则配置项在不同配置文件间全局共享导致切换被重置的问题
- 修复 Linux Wayland 下部分 GPU 可能出现的 UI 渲染问题
- 修复自动更新使版本回退的问题
- 修复首页自定义卡片在切换轻量模式时失效
- 修复悬浮跳转导航失效
- 修复小键盘热键映射错误
- 修复前端无法及时刷新操作状态
- 修复 macOS 从 Dock 栏退出轻量模式状态不同步
- 修复 Linux 系统主题切换不生效
- 修复 `允许自动更新` 字段使手动订阅刷新失效
- 修复轻量模式托盘状态不同步
- 修复一键导入订阅导致应用卡死崩溃的问题
<details>
<summary><strong> ✨ 新增功能 </strong></summary>
- **Mihomo(Meta) 内核升级至 v1.19.15**
- 支持前端修改日志(最大文件大小、最大保留数量)
- 新增链式代理图形化设置功能
- 新增系统标题栏与程序标题栏切换 (设置-页面设置-倾向系统标题栏)
- 监听关机事件,自动关闭系统代理
- 主界面“当前节点”卡片新增“延迟测试”按钮
- 新增批量选择配置文件功能
- Windows / Linux / MacOS 监听关机信号,优雅恢复网络设置
- 新增本地备份功能
- 主界面“当前节点”卡片新增自动延迟检测开关(默认关闭)
- 允许独立控制订阅自动更新
- 托盘 `更多` 中新增 `关闭所有连接` 按钮
- 新增左侧菜单栏的排序功能(右键点击左侧菜单栏)
- 托盘 `打开目录` 中新增 `应用日志` 和 `内核日志`
</details>
<details>
<summary><strong> 🚀 优化改进 </strong></summary>
- 重构并简化服务模式启动检测流程,消除重复检测
- 重构并简化窗口创建流程
- 重构日志系统,单个日志默认最大 10 MB
- 优化前端资源占用
- 改进 macos 下系统代理设置的方法
- 优化 TUN 模式可用性的判断
- 移除流媒体检测的系统级提示(使用软件内通知)
- 优化后端 i18n 资源占用
- 改进 Linux 托盘支持并添加 `--no-tray` 选项
- Linux 现在在新生成的配置中默认将 TUN 栈恢复为 mixed 模式
- 为代理延迟测试的 URL 设置增加了保护以及添加了安全的备用 URL
- 更新了 Wayland 合成器检测逻辑,从而在 Hyprland 会话中保留原生 Wayland 后端
- 改进 Windows 和 Unix 的 服务连接方式以及权限,避免无法连接服务或内核
- 修改内核默认日志级别为 Info
- 支持通过桌面快捷方式重新打开应用
- 支持订阅界面输入链接后回车导入
- 选择按延迟排序时每次延迟测试自动刷新节点顺序
- 配置重载失败时自动重启核心
- 启用 TUN 前等待服务就绪
- 卸载 TUN 时会先关闭
- 优化应用启动页
- 优化首页当前节点对MATCH规则的支持
- 允许在 `界面设置` 修改 `悬浮跳转导航延迟`
- 添加热键绑定错误的提示信息
- 在 macOS 10.15 及更高版本默认包含 Mihomo-go122,以解决 Intel 架构 Mac 无法运行内核的问题
- Tun 模式不可用时,禁用系统托盘的 Tun 模式菜单
- 改进订阅更新方式,仍失败需打开订阅设置 `允许危险证书`
- 允许设置 Mihomo 端口范围 1000(含) - 65536(含)
</details>
## v2.4.2
### ✨ 新增功能
- 增加托盘节点选择
### 🚀 性能优化
- 优化前端首页加载速度
- 优化前端未使用 i18n 文件缓存
- 优化后端内存占用
- 优化后端启动速度
### 🐞 修复问题
- 修复首页节点切换失效的问题
- 修复和优化服务检查流程
- 修复2.4.1引入的订阅地址重定向报错问题
- 修复 rpm/deb 包名称问题
- 修复托盘轻量模式状态检测异常
- 修复通过 scheme 导入订阅崩溃
- 修复单例检测实效
- 修复启动阶段可能导致的无法连接内核
- 修复导入订阅无法 Auth Basic
### 👙 界面样式
- 简化和改进代理设置样式
## v2.4.1
### 🏆 重大改进
- **应用响应速度提升**:采用全新异步处理架构,大幅提升应用响应速度和稳定性
### ✨ 新增功能
- **Mihomo(Meta) 内核升级至 v1.19.13**
### 🚀 性能优化
- 优化热键响应速度,提升快捷键操作体验
- 改进服务管理响应性,减少系统服务操作等待时间
- 提升文件和配置处理性能
- 优化任务管理和日志记录效率
- 优化异步内存管理,减少内存占用并提升多任务处理效率
- 优化启动阶段初始化性能
### 🐞 修复问题
- 修复应用在某些操作中可能出现的响应延迟问题
- 修复任务管理中的潜在并发问题
- 修复通过托盘重启应用无法恢复
- 修复订阅在某些情况下无法导入
- 修复无法新建订阅时使用远程链接
- 修复卸载服务后的 tun 开关状态问题
- 修复页面快速切换订阅时导致崩溃
- 修复丢失工作目录时无法恢复环境
- 修复从轻量模式恢复导致崩溃
### 👙 界面样式
- 统一代理设置样式
### 🗑️ 移除内容
- 移除启动阶段自动清理过期订阅
## v2.4.0
**发行代号:融**
代号释义: 「融」象征融合与贯通,寓意新版本通过全新 IPC 通信机制 将系统各部分紧密衔接,打破壁垒,实现更高效的 数据流通与全面性能优化。
### 🏆 重大改进
- **核心通信架构升级**:采用全新通信机制,提升应用性能和稳定性
- **流量监控系统重构**:全新的流量监控界面,支持更丰富的数据展示
- **数据缓存优化**:改进配置和节点数据缓存,提升响应速度
### ✨ 新增功能
- **Mihomo(Meta) 内核升级至 v1.19.12**
- 新增版本信息复制按钮
- 增强型流量监控,支持更详细的数据分析
- 新增流量图表多种显示模式
- 新增强制刷新配置和节点缓存功能
- 首页流量统计支持查看刻度线详情
### 🚀 性能优化
- 全面提升数据传输和处理效率
- 优化内存使用,减少系统资源消耗
- 改进流量图表渲染性能
- 优化配置和节点刷新策略,从5秒延长到60秒
- 改进数据缓存机制,减少重复请求
- 优化异步程序性能
### 🐞 修复问题
- 修复系统代理状态检测和显示不一致问题
- 修复系统主题窗口颜色不一致问题
- 修复特殊字符 URL 处理问题
- 修复配置修改后缓存不同步问题
- 修复 Windows 安装器自启设置问题
- 修复 macOS 下 Dock 图标恢复窗口问题
- 修复 linux 下 KDE/Plasma 异常标题栏按钮
- 修复架构升级后节点测速功能异常
- 修复架构升级后流量统计功能异常
- 修复架构升级后日志功能异常
- 修复外部控制器跨域配置保存问题
- 修复首页端口显示不一致问题
- 修复首页流量统计刻度线显示问题
- 修复日志页面按钮功能混淆问题
- 修复日志等级设置保存问题
- 修复日志等级异常过滤
- 修复清理日志天数功能异常
- 修复偶发性启动卡死问题
- 修复首页虚拟网卡开关在管理模式下的状态问题
### 🔧 技术改进
- 统一使用新的内核通信方式
- 新增外部控制器配置界面
- 改进跨平台兼容性支持
+15 -5
View File
@@ -9,6 +9,16 @@
A Clash Meta GUI based on <a href="https://github.com/tauri-apps/tauri">Tauri</a>.
</h3>
<p align="center">
Languages:
<a href="./README.md">简体中文</a> ·
<a href="./docs/README_en.md">English</a> ·
<a href="./docs/README_es.md">Español</a> ·
<a href="./docs/README_ru.md">Русский</a> ·
<a href="./docs/README_ja.md">日本語</a> ·
<a href="./docs/README_ko.md">한국어</a>
</p>
## Preview
| Dark | Light |
@@ -23,11 +33,11 @@ Supports Windows (x64/x86), Linux (x64/arm64) and macOS 10.15+ (intel/apple).
#### 我应当怎样选择发行版
| 版本 | 特征 | 链接 |
| :-------- | :--------------------------------------- | :------------------------------------------------------------------------------------- |
| Stable | 正式版,高可靠性,适合日常使用。 | [Release](https://github.com/clash-verge-rev/clash-verge-rev/releases) |
| Alpha | 早期测试版,功能未完善,可能存在缺陷。 | [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) |
| AutoBuild | 滚动更新版,持续集成更新,适合开发测试。 | [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) |
| 版本 | 特征 | 链接 |
| :---------- | :--------------------------------------- | :------------------------------------------------------------------------------------- |
| Stable | 正式版,高可靠性,适合日常使用。 | [Release](https://github.com/clash-verge-rev/clash-verge-rev/releases) |
| Alpha(废弃) | 测试发布流程。 | [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) |
| AutoBuild | 滚动更新版,适合测试反馈,可能存在缺陷。 | [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) |
#### 安装说明和常见问题,请到 [文档页](https://clash-verge-rev.github.io/) 查看
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "clash-verge-draft"
version = "0.1.0"
edition = "2024"
[[bench]]
name = "draft_bench"
path = "bench/benche_me.rs"
harness = false
[dependencies]
parking_lot = { workspace = true }
anyhow = { workspace = true }
[dev-dependencies]
criterion = { workspace = true }
tokio = { workspace = true }
+119
View File
@@ -0,0 +1,119 @@
use criterion::{Criterion, criterion_group, criterion_main};
use std::hint::black_box;
use std::process;
use tokio::runtime::Runtime;
use clash_verge_draft::Draft;
#[derive(Default, Clone, Debug)]
struct IVerge {
enable_auto_launch: Option<bool>,
enable_tun_mode: Option<bool>,
}
fn make_draft() -> Draft<IVerge> {
let verge = IVerge {
enable_auto_launch: Some(true),
enable_tun_mode: Some(false),
};
Draft::new(verge)
}
pub fn bench_draft(c: &mut Criterion) {
let rt = Runtime::new().unwrap_or_else(|e| {
eprintln!("Tokio runtime init failed: {e}");
process::exit(1);
});
let mut group = c.benchmark_group("draft");
group.sample_size(100);
group.warm_up_time(std::time::Duration::from_millis(300));
group.measurement_time(std::time::Duration::from_secs(1));
group.bench_function("data_mut", |b| {
b.iter(|| {
let draft = black_box(make_draft());
draft.edit_draft(|d| d.enable_tun_mode = Some(true));
black_box(&draft.latest_arc().enable_tun_mode);
});
});
group.bench_function("draft_mut_first", |b| {
b.iter(|| {
let draft = black_box(make_draft());
draft.edit_draft(|d| d.enable_auto_launch = Some(false));
let latest = draft.latest_arc();
black_box(&latest.enable_auto_launch);
});
});
group.bench_function("draft_mut_existing", |b| {
b.iter(|| {
let draft = black_box(make_draft());
{
draft.edit_draft(|d| {
d.enable_tun_mode = Some(true);
});
let latest1 = draft.latest_arc();
black_box(&latest1.enable_tun_mode);
}
draft.edit_draft(|d| {
d.enable_tun_mode = Some(false);
});
let latest2 = draft.latest_arc();
black_box(&latest2.enable_tun_mode);
});
});
group.bench_function("latest_arc", |b| {
b.iter(|| {
let draft = black_box(make_draft());
let latest = draft.latest_arc();
black_box(&latest.enable_auto_launch);
});
});
group.bench_function("apply", |b| {
b.iter(|| {
let draft = black_box(make_draft());
{
draft.edit_draft(|d| {
d.enable_auto_launch = Some(false);
});
}
draft.apply();
black_box(&draft);
});
});
group.bench_function("discard", |b| {
b.iter(|| {
let draft = black_box(make_draft());
{
draft.edit_draft(|d| {
d.enable_auto_launch = Some(false);
});
}
draft.discard();
black_box(&draft);
});
});
group.bench_function("with_data_modify_async", |b| {
b.to_async(&rt).iter(|| async {
let draft = black_box(make_draft());
let _: Result<(), anyhow::Error> = draft
.with_data_modify::<_, _, _>(|mut box_data| async move {
box_data.enable_auto_launch =
Some(!box_data.enable_auto_launch.unwrap_or(false));
Ok((box_data, ()))
})
.await;
});
});
group.finish();
}
criterion_group!(benches, bench_draft);
criterion_main!(benches);
+102
View File
@@ -0,0 +1,102 @@
use parking_lot::RwLock;
use std::sync::Arc;
pub type SharedBox<T> = Arc<Box<T>>;
type DraftInner<T> = (SharedBox<T>, Option<SharedBox<T>>);
/// Draft 管理:committed 与 optional draft 都以 Arc<Box<T>> 存储,
// (committed_snapshot, optional_draft_snapshot)
#[derive(Debug, Clone)]
pub struct Draft<T: Clone> {
inner: Arc<RwLock<DraftInner<T>>>,
}
impl<T: Clone> Draft<T> {
#[inline]
pub fn new(data: T) -> Self {
Self {
inner: Arc::new(RwLock::new((Arc::new(Box::new(data)), None))),
}
}
/// 以 Arc<Box<T>> 的形式获取当前“已提交(正式)”数据的快照(零拷贝,仅 clone Arc)
#[inline]
pub fn data_arc(&self) -> SharedBox<T> {
let guard = self.inner.read();
Arc::clone(&guard.0)
}
/// 获取当前(草稿若存在则返回草稿,否则返回已提交)的快照
/// 这也是零拷贝:只 clone Arc,不 clone T
#[inline]
pub fn latest_arc(&self) -> SharedBox<T> {
let guard = self.inner.read();
guard.1.clone().unwrap_or_else(|| Arc::clone(&guard.0))
}
/// 通过闭包以可变方式编辑草稿(在闭包中我们给出 &mut T)
/// - 延迟拷贝:如果只有这一个 Arc 引用,则直接修改,不会克隆 T;
/// - 若草稿被其他读者共享,Arc::make_mut 会做一次 T.clone(最小必要拷贝)。
#[inline]
pub fn edit_draft<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
// 先获得写锁以创建或取出草稿 Arc 的可变引用位置
let mut guard = self.inner.write();
let mut draft_arc = if guard.1.is_none() {
Arc::clone(&guard.0)
} else {
#[allow(clippy::unwrap_used)]
guard.1.take().unwrap()
};
drop(guard);
// Arc::make_mut: 如果只有一个引用则返回可变引用;否则会克隆底层 Box<T>(要求 T: Clone)
let boxed = Arc::make_mut(&mut draft_arc); // &mut Box<T>
// 对 Box<T> 解引用得到 &mut T
let result = f(&mut **boxed);
// 恢复修改后的草稿 Arc
self.inner.write().1 = Some(draft_arc);
result
}
/// 将草稿提交到已提交位置(替换),并清除草稿
#[inline]
pub fn apply(&self) {
let mut guard = self.inner.write();
if let Some(d) = guard.1.take() {
guard.0 = d;
}
}
/// 丢弃草稿(如果存在)
#[inline]
pub fn discard(&self) {
let mut guard = self.inner.write();
guard.1 = None;
}
/// 异步地以拥有 Box<T> 的方式修改已提交数据:将克隆一次已提交数据到本地,
/// 异步闭包返回新的 Box<T>(替换已提交数据)和业务返回值 R。
#[inline]
pub async fn with_data_modify<F, Fut, R>(&self, f: F) -> Result<R, anyhow::Error>
where
T: Send + Sync + 'static,
F: FnOnce(Box<T>) -> Fut + Send,
Fut: std::future::Future<Output = Result<(Box<T>, R), anyhow::Error>> + Send,
{
// 读取已提交快照(cheap Arc clone, 然后得到 Box<T> 所有权 via clone)
// 注意:为了让闭包接收 Box<T> 所有权,我们需要 clone 底层 T(不可避免)
let local: Box<T> = {
let guard = self.inner.read();
// 将 Arc<Box<T>> 的 Box<T> clone 出来(会调用 T: Clone)
(*guard.0).clone()
};
let (new_local, res) = f(local).await?;
// 将新的 Box<T> 放到已提交位置(包进 Arc)
self.inner.write().0 = Arc::new(new_local);
Ok(res)
}
}
+269
View File
@@ -0,0 +1,269 @@
#[cfg(test)]
mod tests {
use anyhow::anyhow;
use clash_verge_draft::Draft;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
#[derive(Clone, Debug, Default, PartialEq)]
struct IVerge {
enable_auto_launch: Option<bool>,
enable_tun_mode: Option<bool>,
}
// Minimal single-threaded executor for immediately-ready futures
fn block_on_ready<F: Future>(fut: F) -> F::Output {
fn no_op_raw_waker() -> RawWaker {
fn clone(_: *const ()) -> RawWaker {
no_op_raw_waker()
}
fn wake(_: *const ()) {}
fn wake_by_ref(_: *const ()) {}
fn drop(_: *const ()) {}
static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
RawWaker::new(std::ptr::null(), &VTABLE)
}
let waker = unsafe { Waker::from_raw(no_op_raw_waker()) };
let mut cx = Context::from_waker(&waker);
let mut fut = Box::pin(fut);
loop {
match Pin::as_mut(&mut fut).poll(&mut cx) {
Poll::Ready(v) => return v,
Poll::Pending => std::thread::yield_now(),
}
}
}
#[test]
fn test_draft_basic_flow() {
let verge = IVerge {
enable_auto_launch: Some(true),
enable_tun_mode: Some(false),
};
let draft = Draft::new(verge);
// 读取正式数据(data_arc)
{
let data = draft.data_arc();
assert_eq!(data.enable_auto_launch, Some(true));
assert_eq!(data.enable_tun_mode, Some(false));
}
// 修改草稿(使用 edit_draft)
draft.edit_draft(|d| {
d.enable_auto_launch = Some(false);
d.enable_tun_mode = Some(true);
});
// 正式数据未变
{
let data = draft.data_arc();
assert_eq!(data.enable_auto_launch, Some(true));
assert_eq!(data.enable_tun_mode, Some(false));
}
// 草稿已变
{
let latest = draft.latest_arc();
assert_eq!(latest.enable_auto_launch, Some(false));
assert_eq!(latest.enable_tun_mode, Some(true));
}
// 提交草稿
draft.apply();
// 正式数据已更新
{
let data = draft.data_arc();
assert_eq!(data.enable_auto_launch, Some(false));
assert_eq!(data.enable_tun_mode, Some(true));
}
// 新一轮草稿并修改
draft.edit_draft(|d| {
d.enable_auto_launch = Some(true);
});
{
let latest = draft.latest_arc();
assert_eq!(latest.enable_auto_launch, Some(true));
assert_eq!(latest.enable_tun_mode, Some(true));
}
// 丢弃草稿
draft.discard();
// 丢弃后再次创建草稿,会从已提交重新 clone
{
draft.edit_draft(|d| {
// 原 committed 是 enable_auto_launch = Some(false)
assert_eq!(d.enable_auto_launch, Some(false));
// 再修改一下
d.enable_tun_mode = Some(false);
});
// 草稿中值已修改,但正式数据仍是 apply 后的值
let data = draft.data_arc();
assert_eq!(data.enable_auto_launch, Some(false));
assert_eq!(data.enable_tun_mode, Some(true));
}
}
#[test]
fn test_arc_pointer_behavior_on_edit_and_apply() {
let draft = Draft::new(IVerge {
enable_auto_launch: Some(true),
enable_tun_mode: Some(false),
});
// 初始 latest == committed
let committed = draft.data_arc();
let latest = draft.latest_arc();
assert!(std::sync::Arc::ptr_eq(&committed, &latest));
// 第一次 edit:由于与 committed 共享,Arc::make_mut 会克隆
draft.edit_draft(|d| d.enable_tun_mode = Some(true));
let committed_after_first_edit = draft.data_arc();
let draft_after_first_edit = draft.latest_arc();
assert!(!std::sync::Arc::ptr_eq(
&committed_after_first_edit,
&draft_after_first_edit
));
// 提交会把 committed 指向草稿的 Arc
let prev_draft_ptr = std::sync::Arc::as_ptr(&draft_after_first_edit);
draft.apply();
let committed_after_apply = draft.data_arc();
assert_eq!(
std::sync::Arc::as_ptr(&committed_after_apply),
prev_draft_ptr
);
// 第二次编辑:此时草稿唯一持有(无其它引用),不应再克隆
// 获取草稿 Arc 的指针并立即丢弃本地引用,避免增加 strong_count
draft.edit_draft(|d| d.enable_auto_launch = Some(false));
let latest1 = draft.latest_arc();
let latest1_ptr = std::sync::Arc::as_ptr(&latest1);
drop(latest1); // 确保只有 Draft 内部持有草稿 Arc
// 再次编辑(unique,Arc::make_mut 不应克隆)
draft.edit_draft(|d| d.enable_tun_mode = Some(false));
let latest2 = draft.latest_arc();
let latest2_ptr = std::sync::Arc::as_ptr(&latest2);
assert_eq!(latest1_ptr, latest2_ptr, "Unique edit should not clone Arc");
assert_eq!(latest2.enable_auto_launch, Some(false));
assert_eq!(latest2.enable_tun_mode, Some(false));
}
#[test]
fn test_discard_restores_latest_to_committed() {
let draft = Draft::new(IVerge {
enable_auto_launch: Some(false),
enable_tun_mode: Some(false),
});
// 创建草稿并修改
draft.edit_draft(|d| d.enable_auto_launch = Some(true));
let committed = draft.data_arc();
let latest = draft.latest_arc();
assert!(!std::sync::Arc::ptr_eq(&committed, &latest));
// 丢弃草稿后 latest 应回到 committed
draft.discard();
let committed2 = draft.data_arc();
let latest2 = draft.latest_arc();
assert!(std::sync::Arc::ptr_eq(&committed2, &latest2));
assert_eq!(latest2.enable_auto_launch, Some(false));
}
#[test]
fn test_edit_draft_returns_closure_result() {
let draft = Draft::new(IVerge::default());
let ret = draft.edit_draft(|d| {
d.enable_tun_mode = Some(true);
123usize
});
assert_eq!(ret, 123);
let latest = draft.latest_arc();
assert_eq!(latest.enable_tun_mode, Some(true));
}
#[test]
fn test_with_data_modify_ok_and_replaces_committed() {
let draft = Draft::new(IVerge {
enable_auto_launch: Some(false),
enable_tun_mode: Some(false),
});
// 使用 with_data_modify 异步(立即就绪)地更新 committed
let res = block_on_ready(draft.with_data_modify(|mut v| async move {
v.enable_auto_launch = Some(true);
Ok((Box::new(*v), "done")) // Dereference v to get Box<T>
}));
assert_eq!(
{
#[allow(clippy::unwrap_used)]
res.unwrap()
},
"done"
);
let committed = draft.data_arc();
assert_eq!(committed.enable_auto_launch, Some(true));
assert_eq!(committed.enable_tun_mode, Some(false));
}
#[test]
fn test_with_data_modify_error_propagation() {
let draft = Draft::new(IVerge::default());
#[allow(clippy::unwrap_used)]
let err = block_on_ready(draft.with_data_modify(|v| async move {
drop(v);
Err::<(Box<IVerge>, ()), _>(anyhow!("boom"))
}))
.unwrap_err();
assert_eq!(format!("{err}"), "boom");
}
#[test]
fn test_with_data_modify_does_not_touch_existing_draft() {
let draft = Draft::new(IVerge {
enable_auto_launch: Some(false),
enable_tun_mode: Some(false),
});
// 创建草稿并修改
draft.edit_draft(|d| {
d.enable_auto_launch = Some(true);
d.enable_tun_mode = Some(true);
});
let draft_before = draft.latest_arc();
let draft_before_ptr = std::sync::Arc::as_ptr(&draft_before);
// 同时通过 with_data_modify 修改 committed
#[allow(clippy::unwrap_used)]
block_on_ready(draft.with_data_modify(|mut v| async move {
v.enable_auto_launch = Some(false); // 与草稿不同
Ok((Box::new(*v), ())) // Dereference v to get Box<T>
}))
.unwrap();
// 草稿应保持不变
let draft_after = draft.latest_arc();
assert_eq!(
std::sync::Arc::as_ptr(&draft_after),
draft_before_ptr,
"Existing draft should not be replaced by with_data_modify"
);
assert_eq!(draft_after.enable_auto_launch, Some(true));
assert_eq!(draft_after.enable_tun_mode, Some(true));
// 丢弃草稿后 latest == committed,且 committed 为异步修改结果
draft.discard();
let latest = draft.latest_arc();
assert_eq!(latest.enable_auto_launch, Some(false));
assert_eq!(latest.enable_tun_mode, Some(false));
}
}
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "clash-verge-logging"
version = "0.1.0"
edition = "2024"
[dependencies]
log = { workspace = true }
tokio = { workspace = true }
compact_str = { workspace = true }
flexi_logger = { workspace = true }
[features]
default = []
tauri-dev = []
+139
View File
@@ -0,0 +1,139 @@
use compact_str::CompactString;
use flexi_logger::DeferredNow;
#[cfg(not(feature = "tauri-dev"))]
use flexi_logger::filter::LogLineFilter;
use flexi_logger::writers::FileLogWriter;
use flexi_logger::writers::LogWriter as _;
use log::Level;
use log::Record;
use std::{fmt, sync::Arc};
use tokio::sync::{Mutex, MutexGuard};
pub type SharedWriter = Arc<Mutex<FileLogWriter>>;
#[derive(Debug, PartialEq, Eq)]
pub enum Type {
Cmd,
Core,
Config,
Setup,
System,
SystemSignal,
Service,
Hotkey,
Window,
Tray,
Timer,
Frontend,
Backup,
File,
Lightweight,
Network,
ProxyMode,
Validate,
ClashVergeRev,
}
impl fmt::Display for Type {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Cmd => write!(f, "[Cmd]"),
Self::Core => write!(f, "[Core]"),
Self::Config => write!(f, "[Config]"),
Self::Setup => write!(f, "[Setup]"),
Self::System => write!(f, "[System]"),
Self::SystemSignal => write!(f, "[SysSignal]"),
Self::Service => write!(f, "[Service]"),
Self::Hotkey => write!(f, "[Hotkey]"),
Self::Window => write!(f, "[Window]"),
Self::Tray => write!(f, "[Tray]"),
Self::Timer => write!(f, "[Timer]"),
Self::Frontend => write!(f, "[Frontend]"),
Self::Backup => write!(f, "[Backup]"),
Self::File => write!(f, "[File]"),
Self::Lightweight => write!(f, "[Lightweight]"),
Self::Network => write!(f, "[Network]"),
Self::ProxyMode => write!(f, "[ProxMode]"),
Self::Validate => write!(f, "[Validate]"),
Self::ClashVergeRev => write!(f, "[ClashVergeRev]"),
}
}
}
#[macro_export]
macro_rules! logging {
// 不带 print 参数的版本(默认不打印)
($level:ident, $type:expr, $($arg:tt)*) => {
log::$level!(target: "app", "{} {}", $type, format_args!($($arg)*))
};
}
#[macro_export]
macro_rules! logging_error {
// Handle Result<T, E>
($type:expr, $expr:expr) => {
if let Err(err) = $expr {
log::error!(target: "app", "[{}] {}", $type, err);
}
};
// Handle formatted message: always print to stdout and log as error
($type:expr, $fmt:literal $(, $arg:expr)*) => {
log::error!(target: "app", "[{}] {}", $type, format_args!($fmt $(, $arg)*));
};
}
#[inline]
pub fn write_sidecar_log(
writer: MutexGuard<'_, FileLogWriter>,
now: &mut DeferredNow,
level: Level,
message: &CompactString,
) {
let args = format_args!("{}", message);
let record = Record::builder()
.args(args)
.level(level)
.target("sidecar")
.build();
let _ = writer.write(now, &record);
}
#[cfg(not(feature = "tauri-dev"))]
pub struct NoModuleFilter<'a>(pub &'a [&'a str]);
#[cfg(not(feature = "tauri-dev"))]
impl<'a> NoModuleFilter<'a> {
#[inline]
pub fn filter(&self, record: &Record) -> bool {
if let Some(module) = record.module_path() {
for blocked in self.0 {
if module.len() >= blocked.len()
&& module.as_bytes()[..blocked.len()] == blocked.as_bytes()[..]
{
return false;
}
}
}
true
}
}
#[cfg(not(feature = "tauri-dev"))]
impl<'a> LogLineFilter for NoModuleFilter<'a> {
#[inline]
fn write(
&self,
now: &mut DeferredNow,
record: &Record,
writer: &dyn flexi_logger::filter::LogLineWriter,
) -> std::io::Result<()> {
if !self.filter(record) {
return Ok(());
}
writer.write(now, record)
}
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "clash-verge-signal"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
[dependencies]
clash-verge-logging = { workspace = true }
log = { workspace = true }
tokio = { workspace = true }
[lints]
workspace = true
+35
View File
@@ -0,0 +1,35 @@
use std::sync::OnceLock;
use clash_verge_logging::{Type, logging};
#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;
pub(crate) static RUNTIME: OnceLock<Option<tokio::runtime::Runtime>> = OnceLock::new();
pub fn register<F, Fut>(f: F)
where
F: Fn() -> Fut + Send + Sync + 'static,
Fut: Future + Send + 'static,
{
RUNTIME.get_or_init(|| match tokio::runtime::Runtime::new() {
Ok(rt) => Some(rt),
Err(e) => {
logging!(
info,
Type::SystemSignal,
"register shutdown signal failed, create tokio runtime error: {}",
e
);
None
}
});
#[cfg(unix)]
unix::register(f);
#[cfg(windows)]
windows::register(f);
}
+94
View File
@@ -0,0 +1,94 @@
use std::sync::atomic::{AtomicBool, Ordering};
use clash_verge_logging::{Type, logging};
use tokio::signal::unix::{SignalKind, signal};
use crate::RUNTIME;
static IS_CLEANING_UP: AtomicBool = AtomicBool::new(false);
pub fn register<F, Fut>(f: F)
where
F: Fn() -> Fut + Send + Sync + 'static,
Fut: Future + Send + 'static,
{
if let Some(Some(rt)) = RUNTIME.get() {
rt.spawn(async move {
let mut sigterm = match signal(SignalKind::terminate()) {
Ok(s) => s,
Err(e) => {
logging!(
error,
Type::SystemSignal,
"Failed to register SIGTERM: {}",
e
);
return;
}
};
let mut sigint = match signal(SignalKind::interrupt()) {
Ok(s) => s,
Err(e) => {
logging!(
error,
Type::SystemSignal,
"Failed to register SIGINT: {}",
e
);
return;
}
};
let mut sighup = match signal(SignalKind::hangup()) {
Ok(s) => s,
Err(e) => {
logging!(
error,
Type::SystemSignal,
"Failed to register SIGHUP: {}",
e
);
return;
}
};
loop {
let signal_name;
tokio::select! {
_ = sigterm.recv() => {
signal_name = "SIGTERM";
}
_ = sigint.recv() => {
signal_name = "SIGINT";
}
_ = sighup.recv() => {
signal_name = "SIGHUP";
}
else => {
break;
}
}
if IS_CLEANING_UP.load(Ordering::SeqCst) {
logging!(
info,
Type::SystemSignal,
"Already shutting down, ignoring repeated signal: {}",
signal_name
);
continue;
}
IS_CLEANING_UP.store(true, Ordering::SeqCst);
logging!(info, Type::SystemSignal, "Caught signal {}", signal_name);
f().await;
}
});
} else {
logging!(
error,
Type::SystemSignal,
"register shutdown signal failed, RUNTIME is not available"
);
}
}
+114
View File
@@ -0,0 +1,114 @@
use std::sync::atomic::{AtomicBool, Ordering};
use clash_verge_logging::{Type, logging};
use tokio::signal::windows;
use crate::RUNTIME;
static IS_CLEANING_UP: AtomicBool = AtomicBool::new(false);
pub fn register<F, Fut>(f: F)
where
F: Fn() -> Fut + Send + Sync + 'static,
Fut: Future + Send + 'static,
{
if let Some(Some(rt)) = RUNTIME.get() {
rt.spawn(async move {
let mut ctrl_c = match windows::ctrl_c() {
Ok(s) => s,
Err(e) => {
logging!(
error,
Type::SystemSignal,
"Failed to register Ctrl+C: {}",
e
);
return;
}
};
let mut ctrl_close = match windows::ctrl_close() {
Ok(s) => s,
Err(e) => {
logging!(
error,
Type::SystemSignal,
"Failed to register Ctrl+Close: {}",
e
);
return;
}
};
let mut ctrl_shutdown = match windows::ctrl_shutdown() {
Ok(s) => s,
Err(e) => {
logging!(
error,
Type::SystemSignal,
"Failed to register Ctrl+Shutdown: {}",
e
);
return;
}
};
let mut ctrl_logoff = match windows::ctrl_logoff() {
Ok(s) => s,
Err(e) => {
logging!(
error,
Type::SystemSignal,
"Failed to register Ctrl+Logoff: {}",
e
);
return;
}
};
loop {
let signal_name;
tokio::select! {
_ = ctrl_c.recv() => {
signal_name = "Ctrl+C";
}
_ = ctrl_close.recv() => {
signal_name = "Ctrl+Close";
}
_ = ctrl_shutdown.recv() => {
signal_name = "Ctrl+Shutdown";
}
_ = ctrl_logoff.recv() => {
signal_name = "Ctrl+Logoff";
}
}
if IS_CLEANING_UP.load(Ordering::SeqCst) {
logging!(
info,
Type::SystemSignal,
"Already shutting down, ignoring repeated signal: {}",
signal_name
);
continue;
}
IS_CLEANING_UP.store(true, Ordering::SeqCst);
logging!(
info,
Type::SystemSignal,
"Caught Windows signal: {}",
signal_name
);
f().await;
}
});
} else {
logging!(
error,
Type::SystemSignal,
"register shutdown signal failed, RUNTIME is not available"
);
}
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "clash-verge-types"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
[dependencies]
serde = { workspace = true }
serde_yaml_ng = { workspace = true }
smartstring = { workspace = true }
[lints]
workspace = true
+1
View File
@@ -0,0 +1 @@
pub mod runtime;
+152
View File
@@ -0,0 +1,152 @@
use serde_yaml_ng::{Mapping, Value};
use smartstring::alias::String;
use std::collections::{HashMap, HashSet};
const PATCH_CONFIG_INNER: [&str; 4] = ["allow-lan", "ipv6", "log-level", "unified-delay"];
#[derive(Default, Clone)]
pub struct IRuntime {
pub config: Option<Mapping>,
// 记录在订阅中(包括merge和script生成的)出现过的keys
// 这些keys不一定都生效
pub exists_keys: HashSet<String>,
// TODO 或许可以用 FixMap 来存储以提升效率
pub chain_logs: HashMap<String, Vec<(String, String)>>,
}
impl IRuntime {
#[inline]
pub fn new() -> Self {
Self::default()
}
// 这里只更改 allow-lan | ipv6 | log-level | tun
#[inline]
pub fn patch_config(&mut self, patch: &Mapping) {
let config = if let Some(config) = self.config.as_mut() {
config
} else {
return;
};
for key in PATCH_CONFIG_INNER.iter() {
if let Some(value) = patch.get(key) {
config.insert((*key).into(), value.clone());
}
}
let patch_tun = patch.get("tun");
if let Some(patch_tun_value) = patch_tun {
let mut tun = config
.get("tun")
.and_then(|val| val.as_mapping())
.cloned()
.unwrap_or_else(Mapping::new);
if let Some(patch_tun_mapping) = patch_tun_value.as_mapping() {
for key in use_keys(patch_tun_mapping) {
if let Some(value) = patch_tun_mapping.get(key.as_str()) {
tun.insert(Value::from(key.as_str()), value.clone());
}
}
}
config.insert("tun".into(), Value::from(tun));
}
}
/// 更新链式代理配置
///
/// 该函数更新 `proxies` 和 `proxy-groups` 配置,并处理链式代理的修改或(传入 None )删除。
///
/// 配置示例:
///
/// ```json
/// {
/// "proxies": [
/// {
/// "name": "入口节点",
/// "type": "xxx",
/// "server": "xxx",
/// "port": "xxx",
/// "ports": "xxx",
/// "password": "xxx",
/// "skip-cert-verify": "xxx"
/// },
/// {
/// "name": "hop_node_1_xxxx",
/// "type": "xxx",
/// "server": "xxx",
/// "port": "xxx",
/// "ports": "xxx",
/// "password": "xxx",
/// "skip-cert-verify": "xxx",
/// "dialer-proxy": "入口节点"
/// },
/// {
/// "name": "出口节点",
/// "type": "xxx",
/// "server": "xxx",
/// "port": "xxx",
/// "ports": "xxx",
/// "password": "xxx",
/// "skip-cert-verify": "xxx",
/// "dialer-proxy": "hop_node_1_xxxx"
/// }
/// ],
/// "proxy-groups": [
/// {
/// "name": "proxy_chain",
/// "type": "select",
/// "proxies": ["出口节点"]
/// }
/// ]
/// }
/// ```
#[inline]
pub fn update_proxy_chain_config(&mut self, proxy_chain_config: Option<Value>) {
let config = if let Some(config) = self.config.as_mut() {
config
} else {
return;
};
if let Some(Value::Sequence(proxies)) = config.get_mut("proxies") {
proxies.iter_mut().for_each(|proxy| {
if let Some(proxy) = proxy.as_mapping_mut()
&& proxy.get("dialer-proxy").is_some()
{
proxy.remove("dialer-proxy");
}
});
}
if let Some(Value::Sequence(dialer_proxies)) = proxy_chain_config
&& let Some(Value::Sequence(proxies)) = config.get_mut("proxies")
{
for (i, dialer_proxy) in dialer_proxies.iter().enumerate() {
if let Some(Value::Mapping(proxy)) = proxies
.iter_mut()
.find(|proxy| proxy.get("name") == Some(dialer_proxy))
&& i != 0
&& let Some(dialer_proxy) = dialer_proxies.get(i - 1)
{
proxy.insert("dialer-proxy".into(), dialer_proxy.to_owned());
}
}
}
}
}
// TODO 完整迁移 enhance 行为后移除
#[inline]
fn use_keys<'a>(config: &'a Mapping) -> impl Iterator<Item = String> + 'a {
config
.iter()
.filter_map(|(key, _)| key.as_str())
.map(|s: &str| {
let mut s: String = s.into();
s.make_ascii_lowercase();
s
})
}
@@ -0,0 +1,20 @@
[package]
name = "tauri-plugin-clash-verge-sysinfo"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
[dependencies]
tauri = { workspace = true }
tauri-plugin-clipboard-manager = { workspace = true }
parking_lot = { workspace = true }
sysinfo = { version = "0.37.2", features = ["network", "system"] }
[target.'cfg(not(windows))'.dependencies]
libc = "0.2.178"
[target.'cfg(windows)'.dependencies]
deelevate = { workspace = true }
[lints]
workspace = true
@@ -0,0 +1,39 @@
use parking_lot::RwLock;
use tauri::{AppHandle, Runtime, State, command};
use tauri_plugin_clipboard_manager::{ClipboardExt as _, Error};
use crate::Platform;
// TODO 迁移,让新的结构体允许通过 tauri command 正确使用 structure.field 方式获取信息
#[command]
pub fn get_system_info(state: State<'_, RwLock<Platform>>) -> Result<String, Error> {
Ok(state.inner().read().to_string())
}
/// 获取应用的运行时间(毫秒)
#[command]
pub fn get_app_uptime(state: State<'_, RwLock<Platform>>) -> Result<u128, Error> {
Ok(state
.inner()
.read()
.appinfo
.app_startup_time
.elapsed()
.as_millis())
}
/// 检查应用是否以管理员身份运行
#[command]
pub fn app_is_admin(state: State<'_, RwLock<Platform>>) -> Result<bool, Error> {
Ok(state.inner().read().appinfo.app_is_admin)
}
#[command]
pub fn export_diagnostic_info<R: Runtime>(
app_handle: AppHandle<R>,
state: State<'_, RwLock<Platform>>,
) -> Result<(), Error> {
let info = state.inner().read().to_string();
let clipboard = app_handle.clipboard();
clipboard.write_text(info)
}
@@ -0,0 +1,165 @@
use std::{
fmt::{Debug, Display},
time::Instant,
};
pub mod commands;
#[cfg(windows)]
use deelevate::{PrivilegeLevel, Token};
use parking_lot::RwLock;
use sysinfo::{Networks, System};
use tauri::{
Manager as _, Runtime,
plugin::{Builder, TauriPlugin},
};
pub struct SysInfo {
system_name: String,
system_version: String,
system_kernel_version: String,
system_arch: String,
}
impl Default for SysInfo {
#[inline]
fn default() -> Self {
let system_name = System::name().unwrap_or_else(|| "Null".into());
let system_version = System::long_os_version().unwrap_or_else(|| "Null".into());
let system_kernel_version = System::kernel_version().unwrap_or_else(|| "Null".into());
let system_arch = System::cpu_arch();
Self {
system_name,
system_version,
system_kernel_version,
system_arch,
}
}
}
pub struct AppInfo {
app_version: String,
app_core_mode: String,
pub app_startup_time: Instant,
pub app_is_admin: bool,
}
impl Default for AppInfo {
#[inline]
fn default() -> Self {
let app_version = "0.0.0".into();
let app_core_mode = "NotRunning".into();
let app_is_admin = false;
let app_startup_time = Instant::now();
Self {
app_version,
app_core_mode,
app_startup_time,
app_is_admin,
}
}
}
#[derive(Default)]
pub struct Platform {
pub sysinfo: SysInfo,
pub appinfo: AppInfo,
}
impl Debug for Platform {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Platform")
.field("system_name", &self.sysinfo.system_name)
.field("system_version", &self.sysinfo.system_version)
.field("system_kernel_version", &self.sysinfo.system_kernel_version)
.field("system_arch", &self.sysinfo.system_arch)
.field("app_version", &self.appinfo.app_version)
.field("app_core_mode", &self.appinfo.app_core_mode)
.field("app_is_admin", &self.appinfo.app_is_admin)
.finish()
}
}
impl Display for Platform {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"System Name: {}\nSystem Version: {}\nSystem kernel Version: {}\nSystem Arch: {}\nVerge Version: {}\nRunning Mode: {}\nIs Admin: {}",
self.sysinfo.system_name,
self.sysinfo.system_version,
self.sysinfo.system_kernel_version,
self.sysinfo.system_arch,
self.appinfo.app_version,
self.appinfo.app_core_mode,
self.appinfo.app_is_admin
)
}
}
impl Platform {
#[inline]
fn new() -> Self {
Self::default()
}
}
#[inline]
fn is_binary_admin() -> bool {
#[cfg(not(windows))]
unsafe {
libc::geteuid() == 0
}
#[cfg(windows)]
Token::with_current_process()
.and_then(|token| token.privilege_level())
.map(|level| level != PrivilegeLevel::NotPrivileged)
.unwrap_or(false)
}
#[inline]
pub fn list_network_interfaces() -> Vec<String> {
let mut networks = Networks::new();
networks.refresh(false);
networks.keys().map(|name| name.to_owned()).collect()
}
#[inline]
pub fn set_app_core_mode<R: Runtime>(app: &tauri::AppHandle<R>, mode: impl Into<String>) {
let platform_spec = app.state::<RwLock<Platform>>();
let mut spec = platform_spec.write();
spec.appinfo.app_core_mode = mode.into();
}
#[inline]
pub fn is_current_app_handle_admin<R: Runtime>(app: &tauri::AppHandle<R>) -> bool {
let platform_spec = app.state::<RwLock<Platform>>();
let spec = platform_spec.read();
spec.appinfo.app_is_admin
}
#[inline]
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::<R>::new("clash_verge_sysinfo")
// TODO 现在 crate 还不是真正的 tauri 插件,必须由主 lib 自行注册
// TODO 从 clash-verge 中迁移获取系统信息的 commnand 并实现优雅 structure.field 访问
// .invoke_handler(tauri::generate_handler![
// commands::get_system_info,
// commands::get_app_uptime,
// commands::app_is_admin,
// commands::export_diagnostic_info,
// ])
.setup(move |app, _api| {
let app_version = app.package_info().version.to_string();
let is_admin = is_binary_admin();
let mut platform_spec = Platform::new();
platform_spec.appinfo.app_version = app_version;
platform_spec.appinfo.app_is_admin = is_admin;
app.manage(RwLock::new(platform_spec));
Ok(())
})
.build()
}
-4
View File
@@ -1,4 +0,0 @@
files:
- source: /src/locales/en.json
translation: /src/locales
multilingual: 1
+79
View File
@@ -0,0 +1,79 @@
# CONTRIBUTING — i18n
Thanks for helping localize Clash Verge Rev. This guide reflects the current architecture, where the React frontend and the Tauri backend keep their translation bundles separate. Follow the steps below to keep both sides in sync without stepping on each other.
## Quick workflow
- Update the language folder under `src/locales/<lang>/`; use `src/locales/en/` as the canonical reference for keys and intent.
- Run `pnpm format:i18n` to align structure and `pnpm i18n:types` to refresh generated typings.
- If you touch backend copy, edit the matching YAML file in `src-tauri/locales/<lang>.yml`.
- Preview UI changes with `pnpm dev` (desktop shell) or `pnpm web:dev` (web only).
- Keep PRs focused and add screenshots whenever layout could be affected by text length.
## Frontend locale structure
Each locale folder mirrors the namespaces under `src/locales/en/`:
```
src/locales/
en/
connections.json
home.json
shared.json
...
index.ts
zh/
...
```
- JSON files map to namespaces (for example `home.json` → `home.*`). Keep keys scoped to the file they belong to.
- `shared.json` stores reusable vocabulary (buttons, validations, etc.); feature-specific wording should live in the relevant namespace.
- `index.ts` re-exports a `resources` object that aggregates the namespace JSON files. When adding or removing namespaces, mirror the pattern from `src/locales/en/index.ts`.
- Frontend bundles are lazy-loaded by `src/services/i18n.ts`. Only languages listed in `supportedLanguages` are fetched at runtime, so append new codes there when you add a locale.
Because backend translations now live in their own directory, you no longer need to run `pnpm prebuild` just to sync locales—the frontend folder is the sole source of truth for web bundles.
## Tooling for frontend contributors
- `pnpm format:i18n` → `node scripts/cleanup-unused-i18n.mjs --align --apply`. It aligns key ordering, removes unused entries, and keeps all locales in lock-step with English.
- `pnpm node scripts/cleanup-unused-i18n.mjs` (without flags) performs a dry-run audit. Use it to inspect missing or extra keys before committing.
- `pnpm i18n:types` regenerates `src/types/generated/i18n-keys.ts` and `src/types/generated/i18n-resources.ts`, ensuring TypeScript catches invalid key usage.
- For dynamic keys that the analyzer cannot statically detect, add explicit references in code or update the script whitelist to avoid false positives.
## Backend (Tauri) locale bundles
Native UI strings (tray menu, notifications, dialogs) use `rust-i18n` with YAML bundles stored in `src-tauri/locales/<lang>.yml`. These files are completely independent from the frontend JSON modules.
- Keep `en.yml` semantically aligned with the Simplified Chinese baseline (`zh.yml`). Other locales may temporarily copy English if no translation is available yet.
- When a backend feature introduces new strings, update every YAML file to keep the key set consistent. Missing keys fall back to the default language (`zh`), so catching gaps early avoids mixed-language output.
- Rust code resolves the active language through `src-tauri/src/utils/i18n.rs`. No additional build step is required after editing YAML files; `tauri dev` and `tauri build` pick them up automatically.
## Adding a new language
1. Duplicate `src/locales/en/` into `src/locales/<new-lang>/` and translate the JSON files while preserving key structure.
2. Update the locale’s `index.ts` to import every namespace. Matching the English file is the easiest way to avoid missing exports.
3. Append the language code to `supportedLanguages` in `src/services/i18n.ts`.
4. If the backend should expose the language, create `src-tauri/locales/<new-lang>.yml` and translate the keys used in existing YAML files.
5. Adjust `crowdin.yml` if the locale requires a special mapping for Crowdin.
6. Run `pnpm format:i18n`, `pnpm i18n:types`, and (optionally) `pnpm node scripts/cleanup-unused-i18n.mjs` in dry-run mode to confirm structure.
## Authoring guidelines
- **Reuse shared vocabulary** before introducing new phrases—check `shared.json` for common actions, statuses, and labels.
- **Prefer semantic keys** (`systemProxy`, `updateInterval`, `autoRefresh`) over positional ones (`item1`, `dialogTitle2`).
- **Document placeholders** using `{{placeholder}}` and ensure components supply the required values.
- **Group keys by UI responsibility** inside each namespace (`page`, `sections`, `forms`, `actions`, `tooltips`, `notifications`, `errors`, `tables`, `statuses`, etc.).
- **Keep strings concise** to avoid layout issues. If a translation needs more context, leave a PR note so reviewers can verify the UI.
## Testing & QA
- Launch the desktop shell with `pnpm dev` (or `pnpm web:dev`) and navigate through the affected views to confirm translations load and layouts behave.
- Run `pnpm test` if you touched code that consumes translations or adjusts formatting logic.
- For backend changes, trigger the relevant tray actions or notifications to verify the updated copy.
- Note any remaining untranslated sections or layout concerns in your PR description so maintainers can follow up.
## Feedback & support
- File an issue for missing context, tooling bugs, or localization gaps so we can track them.
- PRs that touch UI should include screenshots or GIFs whenever text length may affect layout.
- Mention the commands you ran (formatting, type generation, tests) in the PR checklist. If you need extra context or review help, request it via a PR comment.
@@ -1,99 +1,3 @@
## v2.4.1
### 🏆 重大改进
- **应用响应速度提升**:采用全新异步处理架构,大幅提升应用响应速度和稳定性
### ✨ 新增功能
- **Mihomo(Meta) 内核升级至 v1.19.13**
### 🚀 性能优化
- 优化热键响应速度,提升快捷键操作体验
- 改进服务管理响应性,减少系统服务操作等待时间
- 提升文件和配置处理性能
- 优化任务管理和日志记录效率
- 优化异步内存管理,减少内存占用并提升多任务处理效率
- 优化启动阶段初始化性能
### 🐞 修复问题
- 修复应用在某些操作中可能出现的响应延迟问题
- 修复任务管理中的潜在并发问题
- 修复通过托盘重启应用无法恢复
- 修复订阅在某些情况下无法导入
- 修复无法新建订阅时使用远程链接
- 修复卸载服务后的 tun 开关状态问题
- 修复页面快速切换订阅时导致崩溃
- 修复丢失工作目录时无法恢复环境
- 修复从轻量模式恢复导致崩溃
### 👙 界面样式
- 统一代理设置样式
### 🗑️ 移除内容
- 移除启动阶段自动清理过期订阅
## v2.4.0
**发行代号:融**
代号释义: 「融」象征融合与贯通,寓意新版本通过全新 IPC 通信机制 将系统各部分紧密衔接,打破壁垒,实现更高效的 数据流通与全面性能优化。
### 🏆 重大改进
- **核心通信架构升级**:采用全新通信机制,提升应用性能和稳定性
- **流量监控系统重构**:全新的流量监控界面,支持更丰富的数据展示
- **数据缓存优化**:改进配置和节点数据缓存,提升响应速度
### ✨ 新增功能
- **Mihomo(Meta) 内核升级至 v1.19.12**
- 新增版本信息复制按钮
- 增强型流量监控,支持更详细的数据分析
- 新增流量图表多种显示模式
- 新增强制刷新配置和节点缓存功能
- 首页流量统计支持查看刻度线详情
### 🚀 性能优化
- 全面提升数据传输和处理效率
- 优化内存使用,减少系统资源消耗
- 改进流量图表渲染性能
- 优化配置和节点刷新策略,从5秒延长到60秒
- 改进数据缓存机制,减少重复请求
- 优化异步程序性能
### 🐞 修复问题
- 修复系统代理状态检测和显示不一致问题
- 修复系统主题窗口颜色不一致问题
- 修复特殊字符 URL 处理问题
- 修复配置修改后缓存不同步问题
- 修复 Windows 安装器自启设置问题
- 修复 macOS 下 Dock 图标恢复窗口问题
- 修复 linux 下 KDE/Plasma 异常标题栏按钮
- 修复架构升级后节点测速功能异常
- 修复架构升级后流量统计功能异常
- 修复架构升级后日志功能异常
- 修复外部控制器跨域配置保存问题
- 修复首页端口显示不一致问题
- 修复首页流量统计刻度线显示问题
- 修复日志页面按钮功能混淆问题
- 修复日志等级设置保存问题
- 修复日志等级异常过滤
- 修复清理日志天数功能异常
- 修复偶发性启动卡死问题
- 修复首页虚拟网卡开关在管理模式下的状态问题
### 🔧 技术改进
- 统一使用新的内核通信方式
- 新增外部控制器配置界面
- 改进跨平台兼容性支持
## v2.3.2
### 🐞 修复问题
+125
View File
@@ -0,0 +1,125 @@
<h1 align="center">
<img src="../src-tauri/icons/icon.png" alt="Clash" width="128" />
<br>
Continuation of <a href="https://github.com/zzzgydi/clash-verge">Clash Verge</a>
<br>
</h1>
<h3 align="center">
A Clash Meta GUI built with <a href="https://github.com/tauri-apps/tauri">Tauri</a>.
</h3>
<p align="center">
Languages:
<a href="../README.md">简体中文</a> ·
<a href="./README_en.md">English</a> ·
<a href="./README_es.md">Español</a> ·
<a href="./README_ru.md">Русский</a> ·
<a href="./README_ja.md">日本語</a> ·
<a href="./README_ko.md">한국어</a>
</p>
## Preview
| Dark | Light |
| ----------------------------------- | ------------------------------------- |
| ![Dark Preview](./preview_dark.png) | ![Light Preview](./preview_light.png) |
## Install
Visit the [Release page](https://github.com/clash-verge-rev/clash-verge-rev/releases) to download the installer that matches your platform.<br>
We provide packages for Windows (x64/x86), Linux (x64/arm64), and macOS 10.15+ (Intel/Apple).
#### Choosing a Release Channel
| Channel | Description | Link |
| :---------- | :-------------------------------------------------------------------- | :------------------------------------------------------------------------------------- |
| Stable | Official builds with high reliability, ideal for daily use. | [Release](https://github.com/clash-verge-rev/clash-verge-rev/releases) |
| Alpha (EOL) | Legacy builds used to validate the publish pipeline. | [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) |
| AutoBuild | Rolling builds for testing and feedback. Expect experimental changes. | [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) |
#### Installation Guides & FAQ
Read the [project documentation](https://clash-verge-rev.github.io/) for install steps, troubleshooting, and frequently asked questions.
---
### Telegram Channel
Join [@clash_verge_rev](https://t.me/clash_verge_re) for update announcements.
## Promotion
#### [Doggygo VPN — Performance-oriented global accelerator](https://verge.dginv.click/#/register?code=oaxsAGo6)
- High-performance overseas network service with free trials, discounted plans, streaming unlocks, and first-class Hysteria protocol support.
- Register through the exclusive Clash Verge link to get a 3-day trial with 1 GB of traffic per day: [Sign up](https://verge.dginv.click/#/register?code=oaxsAGo6)
- Exclusive 20% off coupon for Clash Verge users: `verge20` (limited to 500 uses)
- Discounted bundle from ¥15.8 per month for 160 GB, plus an additional 20% off for yearly billing
- Operated by an overseas team with reliable service and up to 50% revenue share
- Load-balanced clusters with high-speed dedicated routes (compatible with legacy clients), exceptionally low latency, smooth 4K playback
- First global provider to support the `Hysteria2` protocol—perfect fit for the Clash Verge client
- Supports streaming services and ChatGPT access
- Official site: [https://狗狗加速.com](https://verge.dginv.click/#/register?code=oaxsAGo6)
#### Build Infrastructure Sponsor — [YXVM Dedicated Servers](https://yxvm.com/aff.php?aff=827)
Our builds and releases run on YXVM dedicated servers that deliver premium resources, strong performance, and high-speed networking. If downloads feel fast and usage feels snappy, it is thanks to robust hardware.
🧩 Highlights of YXVM Dedicated Servers:
- 🌎 Optimized global routes for dramatically faster downloads
- 🔧 Bare-metal resources instead of shared VPS capacity for maximum performance
- 🧠 Great for proxy workloads, hosting web/CDN services, CI/CD pipelines, or any high-load tasks
- 💡 Ready to use instantly with multiple datacenter options, including CN2 and IEPL
- 📦 The configuration used by this project is on sale—feel free to get the same setup
- 🎯 Want the same build environment? [Order a YXVM server today](https://yxvm.com/aff.php?aff=827)
## Features
- Built on high-performance Rust with the Tauri 2 framework
- Ships with the embedded [Clash.Meta (mihomo)](https://github.com/MetaCubeX/mihomo) core and supports switching to the `Alpha` channel
- Clean, polished UI with theme color controls, proxy group/tray icons, and `CSS Injection`
- Enhanced profile management (Merge and Script helpers) with configuration syntax hints
- System proxy controls, guard mode, and `TUN` (virtual network adapter) support
- Visual editors for nodes and rules
- WebDAV-based backup and sync for configurations
### FAQ
See the [FAQ page](https://clash-verge-rev.github.io/faq/windows.html) for platform-specific guidance.
### Donation
[Support Clash Verge Rev development](https://github.com/sponsors/clash-verge-rev)
## Development
See [CONTRIBUTING.md](../CONTRIBUTING.md) for detailed contribution guidelines.
After installing all **Tauri** prerequisites, run the development shell with:
```shell
pnpm i
pnpm run prebuild
pnpm dev
```
## Contributions
Issues and pull requests are welcome!
## Acknowledgement
Clash Verge Rev builds on or draws inspiration from these projects:
- [zzzgydi/clash-verge](https://github.com/zzzgydi/clash-verge): A Tauri-based Clash GUI for Windows, macOS, and Linux.
- [tauri-apps/tauri](https://github.com/tauri-apps/tauri): Build smaller, faster, more secure desktop apps with a web frontend.
- [Dreamacro/clash](https://github.com/Dreamacro/clash): A rule-based tunnel written in Go.
- [MetaCubeX/mihomo](https://github.com/MetaCubeX/mihomo): A rule-based tunnel written in Go.
- [Fndroid/clash_for_windows_pkg](https://github.com/Fndroid/clash_for_windows_pkg): A Clash GUI for Windows and macOS.
- [vitejs/vite](https://github.com/vitejs/vite): Next-generation frontend tooling with blazing-fast DX.
## License
GPL-3.0 License. See the [license file](../LICENSE) for details.
+125
View File
@@ -0,0 +1,125 @@
<h1 align="center">
<img src="../src-tauri/icons/icon.png" alt="Clash" width="128" />
<br>
Continuación de <a href="https://github.com/zzzgydi/clash-verge">Clash Verge</a>
<br>
</h1>
<h3 align="center">
Una interfaz gráfica para Clash Meta construida con <a href="https://github.com/tauri-apps/tauri">Tauri</a>.
</h3>
<p align="center">
Idiomas:
<a href="../README.md">简体中文</a> ·
<a href="./README_en.md">English</a> ·
<a href="./README_es.md">Español</a> ·
<a href="./README_ru.md">Русский</a> ·
<a href="./README_ja.md">日本語</a> ·
<a href="./README_ko.md">한국어</a>
</p>
## Vista previa
| Oscuro | Claro |
| ----------------------------------- | ----------------------------------- |
| ![Vista oscura](./preview_dark.png) | ![Vista clara](./preview_light.png) |
## Instalación
Visita la [página de lanzamientos](https://github.com/clash-verge-rev/clash-verge-rev/releases) y descarga el instalador que corresponda a tu plataforma.<br>
Ofrecemos paquetes para Windows (x64/x86), Linux (x64/arm64) y macOS 10.15+ (Intel/Apple).
#### Cómo elegir el canal de lanzamiento
| Canal | Descripción | Enlace |
| :---------- | :----------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- |
| Stable | Compilaciones oficiales de alta fiabilidad; ideales para el uso diario. | [Release](https://github.com/clash-verge-rev/clash-verge-rev/releases) |
| Alpha (EOL) | Compilaciones heredadas usadas para validar el flujo de publicación. | [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) |
| AutoBuild | Compilaciones continuas para pruebas y retroalimentación. Espera cambios beta. | [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) |
#### Guías de instalación y preguntas frecuentes
Consulta la [documentación del proyecto](https://clash-verge-rev.github.io/) para encontrar los pasos de instalación, solución de problemas y preguntas frecuentes.
---
### Canal de Telegram
Únete a [@clash_verge_rev](https://t.me/clash_verge_re) para enterarte de las novedades.
## Promociones
#### [Doggygo VPN — Acelerador global orientado al rendimiento](https://verge.dginv.click/#/register?code=oaxsAGo6)
- Servicio internacional de alto rendimiento con prueba gratuita, planes con descuento, desbloqueo de streaming y soporte de protocolo Hysteria de primera clase.
- Regístrate mediante el enlace exclusivo de Clash Verge y obtén una prueba de 3 días con 1 GB de tráfico diario: [Regístrate](https://verge.dginv.click/#/register?code=oaxsAGo6)
- Cupón exclusivo de 20% de descuento para usuarios de Clash Verge: `verge20` (limitado a 500 usos)
- Plan promocional desde ¥15.8 al mes con 160 GB, más 20% de descuento adicional por pago anual
- Equipo ubicado en el extranjero para un servicio confiable, con hasta 50% de comisión compartida
- Clústeres balanceados con rutas dedicadas de alta velocidad (compatibles con clientes antiguos), latencia extremadamente baja, reproducción 4K sin interrupciones
- Primer proveedor global que soporta el protocolo `Hysteria2`, ideal para el cliente Clash Verge
- Desbloquea servicios de streaming y acceso a ChatGPT
- Sitio oficial: [https://狗狗加速.com](https://verge.dginv.click/#/register?code=oaxsAGo6)
#### Patrocinador de la infraestructura de compilación — [Servidores dedicados YXVM](https://yxvm.com/aff.php?aff=827)
Las compilaciones y lanzamientos del proyecto se ejecutan en servidores dedicados de YXVM, que proporcionan recursos premium, alto rendimiento y redes de alta velocidad. Si las descargas son rápidas y el uso es fluido, es gracias a este hardware robusto.
🧩 Ventajas de los servidores dedicados YXVM:
- 🌎 Rutas globales optimizadas para descargas significativamente más rápidas
- 🔧 Recursos bare-metal, en lugar de VPS compartidos, para obtener el máximo rendimiento
- 🧠 Ideales para proxys, alojamiento de sitios web/CDN, pipelines de CI/CD o cualquier carga elevada
- 💡 Listos para usar al instante, con múltiples centros de datos disponibles (incluidos CN2 e IEPL)
- 📦 La misma configuración utilizada por este proyecto está disponible para su compra
- 🎯 ¿Quieres el mismo entorno de compilación? [Solicita un servidor YXVM hoy](https://yxvm.com/aff.php?aff=827)
## Funciones
- Basado en Rust de alto rendimiento y en el framework Tauri 2
- Incluye el núcleo integrado [Clash.Meta (mihomo)](https://github.com/MetaCubeX/mihomo) y permite cambiar al canal `Alpha`
- Interfaz limpia y elegante con controles de color de tema, iconos de grupos proxy/bandeja y `CSS Injection`
- Gestión avanzada de perfiles (herramientas Merge y Script) con sugerencias de sintaxis para configuraciones
- Control del proxy del sistema, modo guardián y soporte para `TUN` (adaptador de red virtual)
- Editores visuales para nodos y reglas
- Copias de seguridad y sincronización mediante WebDAV
### Preguntas frecuentes
Visita la [página de FAQ](https://clash-verge-rev.github.io/faq/windows.html) para obtener instrucciones específicas por plataforma.
### Donaciones
[Apoya el desarrollo de Clash Verge Rev](https://github.com/sponsors/clash-verge-rev)
## Desarrollo
Consulta [CONTRIBUTING.md](../CONTRIBUTING.md) para conocer las pautas de contribución.
Después de instalar todos los requisitos de **Tauri**, ejecuta el entorno de desarrollo con:
```shell
pnpm i
pnpm run prebuild
pnpm dev
```
## Contribuciones
Se agradecen los issues y pull requests.
## Agradecimientos
Clash Verge Rev se basa en, o se inspira en, los siguientes proyectos:
- [zzzgydi/clash-verge](https://github.com/zzzgydi/clash-verge): Interfaz gráfica para Clash basada en Tauri. Compatible con Windows, macOS y Linux.
- [tauri-apps/tauri](https://github.com/tauri-apps/tauri): Construye aplicaciones de escritorio más pequeñas, rápidas y seguras con un frontend web.
- [Dreamacro/clash](https://github.com/Dreamacro/clash): Túnel basado en reglas escrito en Go.
- [MetaCubeX/mihomo](https://github.com/MetaCubeX/mihomo): Túnel basado en reglas escrito en Go.
- [Fndroid/clash_for_windows_pkg](https://github.com/Fndroid/clash_for_windows_pkg): Interfaz de Clash para Windows y macOS.
- [vitejs/vite](https://github.com/vitejs/vite): Herramientas de frontend de nueva generación con una experiencia rapidísima.
## Licencia
Licencia GPL-3.0. Consulta el [archivo de licencia](../LICENSE) para más detalles.
+125
View File
@@ -0,0 +1,125 @@
<h1 align="center">
<img src="../src-tauri/icons/icon.png" alt="Clash" width="128" />
<br>
<a href="https://github.com/zzzgydi/clash-verge">Clash Verge</a> の継続プロジェクト
<br>
</h1>
<h3 align="center">
<a href="https://github.com/tauri-apps/tauri">Tauri</a> で構築された Clash Meta GUI。
</h3>
<p align="center">
言語:
<a href="../README.md">简体中文</a> ·
<a href="./README_en.md">English</a> ·
<a href="./README_es.md">Español</a> ·
<a href="./README_ru.md">Русский</a> ·
<a href="./README_ja.md">日本語</a> ·
<a href="./README_ko.md">한국어</a>
</p>
## プレビュー
| ダーク | ライト |
| --------------------------------------- | ---------------------------------------- |
| ![ダークプレビュー](./preview_dark.png) | ![ライトプレビュー](./preview_light.png) |
## インストール
[リリースページ](https://github.com/clash-verge-rev/clash-verge-rev/releases) から、ご利用のプラットフォームに対応したインストーラーをダウンロードしてください。<br>
Windows (x64/x86)、Linux (x64/arm64)、macOS 10.15+ (Intel/Apple) をサポートしています。
#### リリースチャンネルの選び方
| チャンネル | 説明 | リンク |
| :---------- | :--------------------------------------------------------------- | :------------------------------------------------------------------------------------- |
| Stable | 安定版。信頼性が高く、日常利用に最適です。 | [Release](https://github.com/clash-verge-rev/clash-verge-rev/releases) |
| Alpha (EOL) | 公開フローの検証に使用した旧テスト版。 | [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) |
| AutoBuild | 継続的に更新されるテスト版。フィードバックや新機能検証向けです。 | [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) |
#### インストール手順と FAQ
詳しい導入手順やトラブルシュートは [ドキュメントサイト](https://clash-verge-rev.github.io/) を参照してください。
---
### Telegram チャンネル
更新情報は [@clash_verge_rev](https://t.me/clash_verge_re) をフォローしてください。
## プロモーション
#### [Doggygo VPN — 高性能グローバルアクセラレータ](https://verge.dginv.click/#/register?code=oaxsAGo6)
- 無料トライアル、割引プラン、ストリーミング解放、世界初の Hysteria プロトコル対応を備えた高性能海外ネットワークサービス。
- Clash Verge 専用リンクから登録すると、3 日間・1 日 1 GB の無料体験が利用できます。 [登録はこちら](https://verge.dginv.click/#/register?code=oaxsAGo6)
- Clash Verge 利用者限定 20% オフクーポン: `verge20`(先着 500 名)
- 月額 15.8 元で 160 GB を利用できるプラン、年額契約ならさらに 20% オフ
- 海外チーム運営による高信頼サービス、収益シェアは最大 50%
- 負荷分散クラスタと高速専用回線(旧クライアント互換)、極低レイテンシで 4K も快適
- 世界初の `Hysteria2` プロトコル対応。Clash Verge クライアントとの相性抜群
- ストリーミングおよび ChatGPT の利用にも対応
- 公式サイト: [https://狗狗加速.com](https://verge.dginv.click/#/register?code=oaxsAGo6)
#### ビルド環境スポンサー — [YXVM 専用サーバー](https://yxvm.com/aff.php?aff=827)
本プロジェクトのビルドとリリースは、YXVM の専用サーバーによって支えられています。高速ダウンロードや快適な操作性は、強力なハードウェアがあってこそです。
🧩 YXVM 専用サーバーの特長:
- 🌎 最適化されたグローバル回線で圧倒的なダウンロード速度
- 🔧 VPS とは異なるベアメタル資源で最高性能を発揮
- 🧠 プロキシ運用、Web/CDN ホスティング、CI/CD など高負荷ワークロードに最適
- 💡 複数データセンターから即時利用可能。CN2 や IEPL も選択可
- 📦 本プロジェクトが使用している構成も販売中。同じ環境を入手できます
- 🎯 同じビルド体験をしたい方は [今すぐ YXVM サーバーを注文](https://yxvm.com/aff.php?aff=827)
## 機能
- 高性能な Rust と Tauri 2 フレームワークに基づくデスクトップアプリ
- 組み込みの [Clash.Meta (mihomo)](https://github.com/MetaCubeX/mihomo) コアを搭載し、`Alpha` チャンネルへの切り替えも可能
- テーマカラーやプロキシグループ/トレイアイコン、`CSS Injection` をカスタマイズできる洗練された UI
- 設定ファイルの管理および拡張(Merge・Script 支援)、構成シンタックスヒントを提供
- システムプロキシ制御、ガード機能、`TUN`(仮想ネットワークアダプタ)モード
- ノードとルールのビジュアルエディタ
- WebDAV による設定のバックアップと同期
### FAQ
プラットフォーム別の案内は [FAQ ページ](https://clash-verge-rev.github.io/faq/windows.html) を参照してください。
### 寄付
[Clash Verge Rev の開発を支援する](https://github.com/sponsors/clash-verge-rev)
## 開発
詳細な貢献ガイドは [CONTRIBUTING.md](../CONTRIBUTING.md) をご覧ください。
**Tauri** の前提条件を整えたら、以下のコマンドで開発サーバーを起動できます:
```shell
pnpm i
pnpm run prebuild
pnpm dev
```
## コントリビューション
Issue や Pull Request を歓迎します。
## 謝辞
Clash Verge Rev は、以下のプロジェクトに影響を受けています。
- [zzzgydi/clash-verge](https://github.com/zzzgydi/clash-verge): Tauri ベースの Clash GUI。Windows / macOS / Linux に対応。
- [tauri-apps/tauri](https://github.com/tauri-apps/tauri): Web フロントエンドで小型・高速・安全なデスクトップアプリを構築するためのフレームワーク。
- [Dreamacro/clash](https://github.com/Dreamacro/clash): Go 製のルールベーストンネル。
- [MetaCubeX/mihomo](https://github.com/MetaCubeX/mihomo): Go 製のルールベーストンネル。
- [Fndroid/clash_for_windows_pkg](https://github.com/Fndroid/clash_for_windows_pkg): Windows / macOS 向けの Clash GUI。
- [vitejs/vite](https://github.com/vitejs/vite): 次世代のフロントエンドツール群。高速な開発体験を提供。
## ライセンス
GPL-3.0 ライセンス。詳細は [LICENSE](../LICENSE) を参照してください。
+125
View File
@@ -0,0 +1,125 @@
<h1 align="center">
<img src="../src-tauri/icons/icon.png" alt="Clash" width="128" />
<br>
<a href="https://github.com/zzzgydi/clash-verge">Clash Verge</a>의 후속 프로젝트
<br>
</h1>
<h3 align="center">
<a href="https://github.com/tauri-apps/tauri">Tauri</a>로 제작된 Clash Meta GUI.
</h3>
<p align="center">
언어:
<a href="../README.md">简体中文</a> ·
<a href="./README_en.md">English</a> ·
<a href="./README_es.md">Español</a> ·
<a href="./README_ru.md">Русский</a> ·
<a href="./README_ja.md">日本語</a> ·
<a href="./README_ko.md">한국어</a>
</p>
## 미리보기
| 다크 | 라이트 |
| ------------------------------------ | --------------------------------------- |
| ![다크 미리보기](./preview_dark.png) | ![라이트 미리보기](./preview_light.png) |
## 설치
[릴리스 페이지](https://github.com/clash-verge-rev/clash-verge-rev/releases)에서 사용 중인 플랫폼에 맞는 설치 프로그램을 다운로드하세요.<br>
Windows (x64/x86), Linux (x64/arm64), macOS 10.15+ (Intel/Apple)을 지원합니다.
#### 릴리스 채널 선택
| 채널 | 설명 | 링크 |
| :---------- | :----------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- |
| Stable | 안정 릴리스. 신뢰성이 높아 일상 사용에 적합합니다. | [Release](https://github.com/clash-verge-rev/clash-verge-rev/releases) |
| Alpha (EOL) | 퍼블리시 파이프라인 검증에 사용되었던 구 테스트 채널입니다. | [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) |
| AutoBuild | 롤링 빌드 채널. 테스트와 피드백 용도로 권장되며, 실험적인 변경이 포함될 수 있습니다. | [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) |
#### 설치 가이드 및 FAQ
설치 방법, 트러블슈팅, 자주 묻는 질문은 [프로젝트 문서](https://clash-verge-rev.github.io/)를 참고하세요.
---
### 텔레그램 채널
업데이트 공지는 [@clash_verge_rev](https://t.me/clash_verge_re)에서 확인하세요.
## 프로모션
#### [Doggygo VPN — 고성능 글로벌 가속기](https://verge.dginv.click/#/register?code=oaxsAGo6)
- 무료 체험, 할인 요금제, 스트리밍 해제, 선도적인 Hysteria 프로토콜 지원을 갖춘 고성능 해외 네트워크 서비스
- Clash Verge 전용 초대 링크로 가입 시 3일간 매일 1GB 무료 체험 제공: [가입하기](https://verge.dginv.click/#/register?code=oaxsAGo6)
- Clash Verge 전용 20% 할인 코드: `verge20` (선착순 500회)
- 월 15.8위안부터 160GB 제공, 연간 결제 시 추가 20% 할인
- 해외 팀 운영, 높은 신뢰성, 최대 50% 커미션
- 로드밸런싱 클러스터, 고속 전용 회선(구 클라이언트 호환), 매우 낮은 지연, 4K도 쾌적
- 세계 최초 `Hysteria2` 프로토콜 지원 — Clash Verge 클라이언트와 최적의 궁합
- 스트리밍 및 ChatGPT 접근 지원
- 공식 사이트: [https://狗狗加速.com](https://verge.dginv.click/#/register?code=oaxsAGo6)
#### 빌드 인프라 스폰서 — [YXVM 전용 서버](https://yxvm.com/aff.php?aff=827)
본 프로젝트의 빌드 및 릴리스는 YXVM 전용 서버에서 구동됩니다. 빠른 다운로드와 경쾌한 사용감은 탄탄한 하드웨어 덕분입니다.
🧩 YXVM 전용 서버 하이라이트:
- 🌎 최적화된 글로벌 라우팅으로 대폭 빨라진 다운로드
- 🔧 공유 VPS가 아닌 베어메탈 자원으로 최대 성능 제공
- 🧠 프록시 워크로드, Web/CDN 호스팅, CI/CD, 고부하 작업에 적합
- 💡 CN2 / IEPL 등 다양한 데이터센터 옵션, 즉시 사용 가능
- 📦 본 프로젝트가 사용하는 구성도 판매 중 — 동일한 환경을 사용할 수 있습니다
- 🎯 동일한 빌드 환경이 필요하다면 [지금 YXVM 서버 주문](https://yxvm.com/aff.php?aff=827)
## 기능
- 고성능 Rust와 Tauri 2 프레임워크 기반 데스크톱 앱
- 내장 [Clash.Meta (mihomo)](https://github.com/MetaCubeX/mihomo) 코어, `Alpha` 채널 전환 지원
- 테마 색상, 프록시 그룹/트레이 아이콘, `CSS Injection` 등 세련된 UI 커스터마이징
- 프로필 관리(병합 및 스크립트 보조), 구성 문법 힌트 제공
- 시스템 프록시 제어, 가드 모드, `TUN`(가상 네트워크 어댑터) 지원
- 노드/규칙 시각 편집기
- WebDAV 기반 설정 백업 및 동기화
### FAQ
플랫폼별 가이드는 [FAQ 페이지](https://clash-verge-rev.github.io/faq/windows.html)에서 확인하세요.
### 후원
[Clash Verge Rev 개발 후원](https://github.com/sponsors/clash-verge-rev)
## 개발
자세한 기여 가이드는 [CONTRIBUTING.md](../CONTRIBUTING.md)를 참고하세요.
**Tauri** 필수 구성 요소를 설치한 뒤 아래 명령으로 개발 서버를 실행합니다:
```shell
pnpm i
pnpm run prebuild
pnpm dev
```
## 기여
Issue와 Pull Request를 환영합니다!
## 감사의 말
Clash Verge Rev는 다음 프로젝트에 기반하거나 영향을 받았습니다:
- [zzzgydi/clash-verge](https://github.com/zzzgydi/clash-verge): Windows / macOS / Linux용 Tauri 기반 Clash GUI
- [tauri-apps/tauri](https://github.com/tauri-apps/tauri): 웹 프론트엔드로 더 작고 빠르고 안전한 데스크톱 앱을 빌드
- [Dreamacro/clash](https://github.com/Dreamacro/clash): Go로 작성된 규칙 기반 터널
- [MetaCubeX/mihomo](https://github.com/MetaCubeX/mihomo): Go로 작성된 규칙 기반 터널
- [Fndroid/clash_for_windows_pkg](https://github.com/Fndroid/clash_for_windows_pkg): Windows / macOS용 Clash GUI
- [vitejs/vite](https://github.com/vitejs/vite): 차세대 프론트엔드 툴링, 매우 빠른 DX
## 라이선스
GPL-3.0 라이선스. 자세한 내용은 [LICENSE](../LICENSE)를 참고하세요.
+121
View File
@@ -0,0 +1,121 @@
<h1 align="center">
<img src="../src-tauri/icons/icon.png" alt="Clash" width="128" />
<br>
Continuation of <a href="https://github.com/zzzgydi/clash-verge">Clash Verge</a>
<br>
</h1>
<h3 align="center">
Clash Meta GUI базируется на <a href="https://github.com/tauri-apps/tauri">Tauri</a>.
</h3>
<p align="center">
Языки:
<a href="../README.md">简体中文</a> ·
<a href="./README_en.md">English</a> ·
<a href="./README_es.md">Español</a> ·
<a href="./README_ru.md">Русский</a> ·
<a href="./README_ja.md">日本語</a> ·
<a href="./README_ko.md">한국어</a>
</p>
## Предпросмотр
| Тёмная тема | Светлая тема |
| ---------------------------------- | ------------------------------------ |
| ![Тёмная тема](./preview_dark.png) | ![Светлая тема](./preview_light.png) |
## Установка
Пожалуйста, перейдите на страницу релизов, чтобы скачать соответствующий установочный пакет: [Страница релизов](https://github.com/clash-verge-rev/clash-verge-rev/releases)<br>
Перейти на [Страницу релизов](https://github.com/clash-verge-rev/clash-verge-rev/releases) to download the corresponding installation package<br>
Поддержка Windows (x64/x86), Linux (x64/arm64) и macOS 10.15+ (intel/apple).
#### Как выбрать дистрибутив?
| Версия | Характеристики | Ссылка |
| :-------------------- | :------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------- |
| Stable | Официальный релиз, высокая надежность, подходит для повседневного использования. | [Release](https://github.com/clash-verge-rev/clash-verge-rev/releases) |
| Alpha(неиспользуемый) | Тестирование процесса публикации. | [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) |
| AutoBuild | Версия с постоянным обновлением, подходящая для тестирования и обратной связи. Может содержать дефекты. | [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) |
#### Инструкции по установке и ответы на часто задаваемые вопросы можно найти на [странице документации](https://clash-verge-rev.github.io/)
---
### TG канал: [@clash_verge_rev](https://t.me/clash_verge_re)
## Продвижение
#### [Doggygo VPN —— технический VPN-сервис (айрпорт)](https://verge.dginv.click/#/register?code=oaxsAGo6)
- Высокопроизводительный иностранный VPN-сервис (айрпорт) с бесплатным пробным периодом, выгодными тарифами, возможностью разблокировки потокового ТВ и первым в мире поддержкой протокола Hysteria.
- Зарегистрируйтесь по эксклюзивной ссылке Clash Verge и получите 3 дня бесплатного использования, 1 Гб трафика в день: [регистрация](https://verge.dginv.click/#/register?code=oaxsAGo6)
- Эксклюзивный промо-код на скидку 20% для Clash Verge: verge20 (только 500 штук)
- Специальный тарифный план всего за 15,8 юаней в месяц, 160 Гб трафика, скидка 20% при оплате за год
- Команда за рубежом, без риска побега, до 50% кэшбэка
- Архитектура с балансировкойнагрузки, высокоскоростная выделенная линия (совместима со старыми клиентами), чрезвычайно низкая задержка, без проблем в часы пик, 4K видео загружается мгновенно
- Первый в мире VPN-сервис (айрпорт), поддерживающий протокол Hysteria, теперь доступен более быстрый протокол `Hysteria2` (лучшее сочетание с клиентом Clash Verge)
- Разблокировка потоковые сервисы и ChatGPT
- Официальный сайт: [https://狗狗加速.com](https://verge.dginv.click/#/register?code=oaxsAGo6)
#### Среда сборки и публикации этого проекта полностью поддерживается выделенным сервером [YXVM](https://yxvm.com/aff.php?aff=827)
Благодарим вас за предоставление надежной бэкэнд-среды с эксклюзивными ресурсами, высокой производительностью и высокоскоростной сетью. Если вы считаете, что загрузка файлов происходит достаточно быстро, а использование — достаточно плавно, то это потому, что мы используем серверы высшего уровня!
🧩 Преимущества выделенного сервера YXVM:
- 🌎 Премиум-сеть с оптимизацией обратного пути для молниеносной скорости загрузки
- 🔧 Выделенные физические серверные ресурсы, не имеющие аналогов среди VPS, обеспечивающие максимальную производительность
- 🧠 Идеально подходит для прокси, хостинга веб-сайтов/CDN-сайтов, рабочих процессов CI/CD или любых приложений с высокой нагрузкой
- 💡 Поддержка использования сразу после включения, выбор нескольких дата-центров, CN2 / IEPL на выбор
- 📦 Эта конфигурация в настоящее время доступна для покупки — не стесняйтесь заказывать ту же модель!
- 🎯 Хотите попробовать такую же сборку? [Закажите выделенный сервер YXVM прямо сейчас!](https://yxvm.com/aff.php?aff=827)
## Фичи
- Основан на произвоительном Rust и фреймворке Tauri 2
- Имеет встроенное ядро [Clash.Meta(mihomo)](https://github.com/MetaCubeX/mihomo) и поддерживает переключение на ядро версии `Alpha`.
- Чистый и эстетичный пользовательский интерфейс, поддержка настраиваемых цветов темы, значков прокси-группы/системного трея и `CSS Injection`。
- Управление и расширение конфигурационными файлами (Merge и Script), подсказки по синтаксису конфигурационных файлов.
- Режим системного прокси и защита, `TUN (Tunneled Network Interface)` режим.
- Визуальное редактирование узлов и правил
- Резервное копирование и синхронизация конфигурации WebDAV
### FAQ
Смотрите [Страница часто задаваемых вопросов](https://clash-verge-rev.github.io/faq/windows.html)
### Донат
[Поддержите развитие Clash Verge Rev](https://github.com/sponsors/clash-verge-rev)
## Разработка
Дополнительные сведения смотреть в файле [CONTRIBUTING.md](../CONTRIBUTING.md).
Для запуска сервера разработки выполните следующие команды после установки всех необходимых компонентов для **Tauri**:
```shell
pnpm i
pnpm run prebuild
pnpm dev
```
## Вклад
Обращения и запросы на PR приветствуются!
## Благодарность
Clash Verge rev был основан на этих проектах или вдохновлен ими, и так далее:
- [zzzgydi/clash-verge](https://github.com/zzzgydi/clash-verge): Графический интерфейс Clash на основе tauri. Поддерживает Windows, macOS и Linux.
- [tauri-apps/tauri](https://github.com/tauri-apps/tauri): Создавайте более компактные, быстрые и безопасные настольные приложения с веб-интерфейсом.
- [Dreamacro/clash](https://github.com/Dreamacro/clash): Правило-ориентированный туннель на Go.
- [MetaCubeX/mihomo](https://github.com/MetaCubeX/mihomo): Правило-ориентированный туннель на Go.
- [Fndroid/clash_for_windows_pkg](https://github.com/Fndroid/clash_for_windows_pkg): Графический интерфейс пользователя для Windows/macOS на основе Clash.
- [vitejs/vite](https://github.com/vitejs/vite): Инструменты нового поколения для фронтенда. Они быстрые!
## Лицензия
GPL-3.0 License. Подробности смотрите в [Лицензии](../LICENSE).
+138 -8
View File
@@ -1,16 +1,146 @@
import js from "@eslint/js";
import eslintJS from "@eslint/js";
import eslintReact from "@eslint-react/eslint-plugin";
import { defineConfig } from "eslint/config";
import configPrettier from "eslint-config-prettier";
import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript";
import pluginImportX from "eslint-plugin-import-x";
import pluginPrettier from "eslint-plugin-prettier";
import pluginReactHooks from "eslint-plugin-react-hooks";
import pluginReactRefresh from "eslint-plugin-react-refresh";
import pluginUnusedImports from "eslint-plugin-unused-imports";
import globals from "globals";
import tseslint from "typescript-eslint";
import pluginReact from "eslint-plugin-react";
import { defineConfig } from "eslint/config";
export default defineConfig([
{
files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
plugins: { js },
extends: ["js/recommended"],
languageOptions: { globals: globals.browser },
plugins: {
js: eslintJS,
// @ts-expect-error -- https://github.com/typescript-eslint/typescript-eslint/issues/11543
"react-hooks": pluginReactHooks,
// @ts-expect-error -- https://github.com/un-ts/eslint-plugin-import-x/issues/421
"import-x": pluginImportX,
"react-refresh": pluginReactRefresh,
"unused-imports": pluginUnusedImports,
prettier: pluginPrettier,
},
extends: [
eslintJS.configs.recommended,
tseslint.configs.recommended,
eslintReact.configs["recommended-typescript"],
configPrettier,
],
languageOptions: {
globals: globals.browser,
},
settings: {
react: {
version: "detect",
},
"import-x/resolver-next": [
createTypeScriptImportResolver({
project: "./tsconfig.json",
}),
],
},
rules: {
// React
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error",
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
"@eslint-react/no-forward-ref": "off",
// React performance and production quality rules
"@eslint-react/no-array-index-key": "warn",
"@eslint-react/no-children-count": "error",
"@eslint-react/no-children-for-each": "error",
"@eslint-react/no-children-map": "error",
"@eslint-react/no-children-only": "error",
"@eslint-react/no-children-prop": "error",
"@eslint-react/no-children-to-array": "error",
"@eslint-react/no-class-component": "error",
"@eslint-react/no-clone-element": "error",
"@eslint-react/no-create-ref": "error",
"@eslint-react/no-default-props": "error",
"@eslint-react/no-direct-mutation-state": "error",
"@eslint-react/no-implicit-key": "error",
"@eslint-react/no-prop-types": "error",
"@eslint-react/no-set-state-in-component-did-mount": "error",
"@eslint-react/no-set-state-in-component-did-update": "error",
"@eslint-react/no-set-state-in-component-will-update": "error",
"@eslint-react/no-string-refs": "error",
"@eslint-react/no-unstable-context-value": "warn",
"@eslint-react/no-unstable-default-props": "warn",
"@eslint-react/no-unused-class-component-members": "error",
"@eslint-react/no-unused-state": "error",
"@eslint-react/no-useless-fragment": "warn",
"@eslint-react/prefer-destructuring-assignment": "warn",
// TypeScript
"@typescript-eslint/no-explicit-any": "off",
// unused-imports 代替 no-unused-vars
"@typescript-eslint/no-unused-vars": "off",
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"warn",
{
vars: "all",
varsIgnorePattern: "^_",
args: "after-used",
argsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^ignore",
},
],
// Import
"import-x/no-unresolved": "error",
"import-x/order": [
"warn",
{
groups: [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index",
],
"newlines-between": "always",
alphabetize: {
order: "asc",
caseInsensitive: true,
},
},
],
// 其他常见
"prefer-const": "warn",
"no-case-declarations": "error",
"no-fallthrough": "error",
"no-empty": ["warn", { allowEmptyCatch: true }],
// Prettier 格式化问题
"prettier/prettier": "warn",
},
},
{
files: ["scripts/**/*.{js,mjs,cjs}", "scripts-workflow/**/*.{js,mjs,cjs}"],
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
},
},
tseslint.configs.recommended,
pluginReact.configs.flat.recommended,
]);
+81 -62
View File
@@ -1,11 +1,13 @@
{
"name": "clash-verge",
"version": "2.4.1",
"version": "2.4.4-rc",
"license": "GPL-3.0-only",
"scripts": {
"dev": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev",
"dev:diff": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev",
"dev:trace": "cross-env RUST_BACKTRACE=1 RUSTFLAGS=\"--cfg tokio_unstable\" tauri dev -f verge-dev tokio-trace",
"prepare": "husky || true",
"dev": "cross-env RUST_BACKTRACE=full tauri dev -f verge-dev",
"dev:diff": "cross-env RUST_BACKTRACE=full tauri dev -f verge-dev",
"dev:trace": "cross-env RUST_BACKTRACE=full RUSTFLAGS=\"--cfg tokio_unstable\" tauri dev -f verge-dev tokio-trace",
"dev:tauri": "cross-env RUST_BACKTRACE=full tauri dev -f tauri-dev",
"build": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tauri build",
"build:fast": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tauri build -- --profile fast-release",
"tauri": "tauri",
@@ -22,11 +24,13 @@
"release:autobuild": "pnpm release-version autobuild",
"release:deploytest": "pnpm release-version deploytest",
"publish-version": "node scripts/publish-version.mjs",
"fmt": "cargo fmt --manifest-path ./src-tauri/Cargo.toml",
"clippy": "cargo clippy --manifest-path ./src-tauri/Cargo.toml",
"lint": "eslint src",
"lint": "eslint -c eslint.config.ts --max-warnings=0 --cache --cache-location .eslintcache src",
"lint:fix": "eslint -c eslint.config.ts --max-warnings=0 --cache --cache-location .eslintcache --fix src",
"format": "prettier --write .",
"format:check": "prettier --check ."
"format:check": "prettier --check .",
"format:i18n": "node scripts/cleanup-unused-i18n.mjs --align --apply",
"i18n:types": "node scripts/generate-i18n-keys.mjs",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
@@ -35,76 +39,91 @@
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@juggle/resize-observer": "^3.4.0",
"@mui/icons-material": "^7.3.1",
"@mui/lab": "7.0.0-beta.16",
"@mui/material": "^7.3.1",
"@mui/x-data-grid": "^8.11.0",
"@tauri-apps/api": "2.8.0",
"@tauri-apps/plugin-clipboard-manager": "^2.3.0",
"@tauri-apps/plugin-dialog": "^2.3.3",
"@tauri-apps/plugin-fs": "^2.4.2",
"@tauri-apps/plugin-notification": "^2.3.1",
"@tauri-apps/plugin-process": "^2.3.0",
"@tauri-apps/plugin-shell": "2.3.1",
"@monaco-editor/react": "^4.7.0",
"@mui/icons-material": "^7.3.6",
"@mui/lab": "7.0.0-beta.17",
"@mui/material": "^7.3.6",
"@tanstack/react-table": "^8.21.3",
"@tanstack/react-virtual": "^3.13.12",
"@tauri-apps/api": "2.9.1",
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
"@tauri-apps/plugin-dialog": "^2.4.2",
"@tauri-apps/plugin-fs": "^2.4.4",
"@tauri-apps/plugin-http": "~2.5.4",
"@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-shell": "2.3.3",
"@tauri-apps/plugin-updater": "2.9.0",
"@types/json-schema": "^7.0.15",
"ahooks": "^3.9.4",
"axios": "^1.11.0",
"cli-color": "^2.0.4",
"dayjs": "1.11.16",
"ahooks": "^3.9.6",
"axios": "^1.13.2",
"dayjs": "1.11.19",
"foxact": "^0.2.49",
"glob": "^11.0.3",
"i18next": "^25.4.2",
"js-yaml": "^4.1.0",
"json-schema": "^0.4.0",
"i18next": "^25.7.1",
"js-yaml": "^4.1.1",
"lodash-es": "^4.17.21",
"monaco-editor": "^0.52.2",
"monaco-editor": "^0.55.1",
"monaco-yaml": "^5.4.0",
"nanoid": "^5.1.5",
"react": "19.1.1",
"react-dom": "19.1.1",
"nanoid": "^5.1.6",
"react": "19.2.1",
"react-dom": "19.2.1",
"react-error-boundary": "6.0.0",
"react-hook-form": "^7.62.0",
"react-i18next": "15.7.3",
"react-hook-form": "^7.67.0",
"react-i18next": "16.4.0",
"react-markdown": "10.1.0",
"react-monaco-editor": "0.59.0",
"react-router-dom": "7.8.2",
"react-virtuoso": "^4.14.0",
"sockette": "^2.0.6",
"swr": "^2.3.6",
"tar": "^7.4.3",
"types-pac": "^1.0.3",
"zustand": "^5.0.8"
"react-router": "^7.10.0",
"react-virtuoso": "^4.16.1",
"swr": "^2.3.7",
"tauri-plugin-mihomo-api": "git+https://github.com/clash-verge-rev/tauri-plugin-mihomo#main",
"types-pac": "^1.0.3"
},
"devDependencies": {
"@actions/github": "^6.0.1",
"@eslint/js": "^9.34.0",
"@tauri-apps/cli": "2.8.3",
"@eslint-react/eslint-plugin": "^2.3.12",
"@eslint/js": "^9.39.1",
"@tauri-apps/cli": "2.9.5",
"@types/js-yaml": "^4.0.9",
"@types/lodash-es": "^4.17.12",
"@types/react": "19.1.12",
"@types/react-dom": "19.1.9",
"@types/node": "^24.10.1",
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"@vitejs/plugin-legacy": "^7.2.1",
"@vitejs/plugin-react": "5.0.2",
"@vitejs/plugin-react-swc": "^4.2.2",
"adm-zip": "^0.5.16",
"commander": "^14.0.0",
"cross-env": "^10.0.0",
"eslint": "^9.34.0",
"eslint-plugin-react": "^7.37.5",
"globals": "^16.3.0",
"cli-color": "^2.0.4",
"commander": "^14.0.2",
"cross-env": "^10.1.0",
"eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"eslint-plugin-unused-imports": "^4.3.0",
"glob": "^13.0.0",
"globals": "^16.5.0",
"https-proxy-agent": "^7.0.6",
"jiti": "^2.5.1",
"meta-json-schema": "^1.19.12",
"husky": "^9.1.7",
"jiti": "^2.6.1",
"lint-staged": "^16.2.7",
"node-fetch": "^3.3.2",
"prettier": "^3.6.2",
"sass": "^1.91.0",
"terser": "^5.43.1",
"typescript": "^5.9.2",
"typescript-eslint": "^8.41.0",
"vite": "^7.1.3",
"vite-plugin-monaco-editor": "^1.1.0",
"prettier": "^3.7.4",
"sass": "^1.94.2",
"tar": "^7.5.2",
"terser": "^5.44.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.48.1",
"vite": "^7.2.6",
"vite-plugin-svgr": "^4.5.0"
},
"lint-staged": {
"*.{ts,tsx,js,jsx}": [
"eslint --fix --max-warnings=0",
"prettier --write"
],
"*.{css,scss,json,md}": [
"prettier --write"
]
},
"type": "module",
"packageManager": "pnpm@9.13.2"
"packageManager": "pnpm@10.22.0"
}
+2312 -3305
View File
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
onlyBuiltDependencies:
- "@parcel/watcher"
- "@swc/core"
- core-js
- es5-ext
- esbuild
- unrs-resolver
-42
View File
@@ -1,42 +0,0 @@
{
"extends": ["config:recommended", ":disableDependencyDashboard"],
"baseBranches": ["dev"],
"enabledManagers": ["cargo", "npm"],
"labels": ["dependencies"],
"ignorePaths": [
"**/node_modules/**",
"**/bower_components/**",
"**/vendor/**",
"**/__tests__/**",
"**/test/**",
"**/tests/**",
"**/__fixtures__/**",
"**/crate/**",
"shared/**"
],
"rangeStrategy": "bump",
"packageRules": [
{
"semanticCommitType": "chore",
"matchPackageNames": ["*"]
},
{
"description": "Disable node/pnpm version updates",
"matchPackageNames": ["node", "pnpm"],
"matchDepTypes": ["engines", "packageManager"],
"enabled": false
},
{
"description": "Group all cargo dependencies into a single PR",
"matchManagers": ["cargo"],
"groupName": "cargo dependencies"
},
{
"description": "Group all npm dependencies into a single PR",
"matchManagers": ["npm"],
"groupName": "npm dependencies"
}
],
"postUpdateOptions": ["pnpmDedupe"],
"ignoreDeps": ["criterion"]
}
+50
View File
@@ -0,0 +1,50 @@
{
extends: ["config:recommended", ":disableDependencyDashboard"],
baseBranches: ["dev"],
enabledManagers: ["cargo", "npm", "github-actions"],
labels: ["dependencies"],
ignorePaths: [
"**/node_modules/**",
"**/bower_components/**",
"**/vendor/**",
"**/__tests__/**",
"**/test/**",
"**/tests/**",
"**/__fixtures__/**",
"shared/**",
],
rangeStrategy: "replace",
packageRules: [
{
matchUpdateTypes: ["patch"],
automerge: true,
},
{
semanticCommitType: "chore",
matchPackageNames: ["*"],
},
{
description: "Disable node/pnpm version updates",
matchPackageNames: ["node", "pnpm"],
matchDepTypes: ["engines", "packageManager"],
enabled: false,
},
{
description: "Group all cargo dependencies into a single PR",
matchManagers: ["cargo"],
groupName: "cargo dependencies",
},
{
description: "Group all npm dependencies into a single PR",
matchManagers: ["npm"],
groupName: "npm dependencies",
},
{
description: "Group all GitHub Actions updates into a single PR",
matchManagers: ["github-actions"],
groupName: "github actions",
},
],
postUpdateOptions: ["pnpmDedupe", "updateCargoLock"],
ignoreDeps: ["criterion"],
}
-102
View File
@@ -1,102 +0,0 @@
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const LOCALES_DIR = path.resolve(__dirname, "../src/locales");
const SRC_DIRS = [
path.resolve(__dirname, "../src"),
path.resolve(__dirname, "../src-tauri"),
];
const exts = [".js", ".ts", ".tsx", ".jsx", ".vue", ".rs"];
// 递归获取所有文件
function getAllFiles(dir, exts) {
let files = [];
fs.readdirSync(dir).forEach((file) => {
const full = path.join(dir, file);
if (fs.statSync(full).isDirectory()) {
files = files.concat(getAllFiles(full, exts));
} else if (exts.includes(path.extname(full))) {
files.push(full);
}
});
return files;
}
// 读取所有源码内容为一个大字符串
function getAllSourceContent() {
const files = SRC_DIRS.flatMap((dir) => getAllFiles(dir, exts));
return files.map((f) => fs.readFileSync(f, "utf8")).join("\n");
}
// 白名单 key,不检查这些 key 是否被使用
const WHITELIST_KEYS = [
"theme.light",
"theme.dark",
"theme.system",
"Already Using Latest Core Version",
];
// 主流程
function processI18nFile(i18nPath, lang, allSource) {
const i18n = JSON.parse(fs.readFileSync(i18nPath, "utf8"));
const keys = Object.keys(i18n);
const used = {};
const unused = [];
let checked = 0;
const total = keys.length;
keys.forEach((key) => {
if (WHITELIST_KEYS.includes(key)) {
used[key] = i18n[key];
} else {
// 只查找一次
const regex = new RegExp(`["'\`]${key}["'\`]`);
if (regex.test(allSource)) {
used[key] = i18n[key];
} else {
unused.push(key);
}
}
checked++;
if (checked % 20 === 0 || checked === total) {
const percent = ((checked / total) * 100).toFixed(1);
process.stdout.write(
`\r[${lang}] Progress: ${checked}/${total} (${percent}%)`,
);
if (checked === total) process.stdout.write("\n");
}
});
// 输出未使用的 key
console.log(`\n[${lang}] Unused keys:`, unused);
// 备份原文件
const oldPath = i18nPath + ".old";
fs.renameSync(i18nPath, oldPath);
// 写入精简后的 i18n 文件(保留原文件名)
fs.writeFileSync(i18nPath, JSON.stringify(used, null, 2), "utf8");
console.log(
`[${lang}] Cleaned i18n file written to src/locales/${path.basename(i18nPath)}`,
);
console.log(`[${lang}] Original file backed up as ${path.basename(oldPath)}`);
}
function main() {
// 支持 zhtw.json、zh-tw.json、zh_CN.json 等
const files = fs
.readdirSync(LOCALES_DIR)
.filter((f) => /^[a-z0-9\-_]+\.json$/i.test(f) && !f.endsWith(".old"));
const allSource = getAllSourceContent();
files.forEach((file) => {
const lang = path.basename(file, ".json");
processI18nFile(path.join(LOCALES_DIR, file), lang, allSource);
});
}
main();
File diff suppressed because it is too large Load Diff
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
#
# extract_update_logs.sh
# 从 Changelog.md 提取最新版本 (## v...) 的更新内容
# 并输出到屏幕或写入环境变量文件(如 GitHub Actions)
set -euo pipefail
CHANGELOG_FILE="Changelog.md"
if [[ ! -f "$CHANGELOG_FILE" ]]; then
echo "❌ 文件不存在: $CHANGELOG_FILE" >&2
exit 1
fi
# 提取从第一个 '## v' 开始到下一个 '## v' 前的内容
UPDATE_LOGS=$(awk '
/^## v/ {
if (found) exit;
found=1
}
found
' "$CHANGELOG_FILE")
if [[ -z "$UPDATE_LOGS" ]]; then
echo "⚠️ 未找到更新日志内容"
exit 0
fi
echo "✅ 提取到的最新版本日志内容如下:"
echo "----------------------------------------"
echo "$UPDATE_LOGS"
echo "----------------------------------------"
# 如果在 GitHub Actions 环境中(GITHUB_ENV 已定义)
if [[ -n "${GITHUB_ENV:-}" ]]; then
{
echo "UPDATE_LOGS<<EOF"
echo "$UPDATE_LOGS"
echo "EOF"
} >> "$GITHUB_ENV"
echo "✅ 已写入 GitHub 环境变量 UPDATE_LOGS"
fi
+1 -1
View File
@@ -1,7 +1,7 @@
import { exec } from "child_process";
import { promisify } from "util";
import fs from "fs/promises";
import path from "path";
import { promisify } from "util";
/**
* 为Alpha版本重命名版本号
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/env node
import { promises as fs } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const ROOT_DIR = path.resolve(__dirname, "..");
const LOCALE_DIR = path.resolve(ROOT_DIR, "src/locales/en");
const KEY_OUTPUT = path.resolve(ROOT_DIR, "src/types/generated/i18n-keys.ts");
const RESOURCE_OUTPUT = path.resolve(
ROOT_DIR,
"src/types/generated/i18n-resources.ts",
);
const isPlainObject = (value) =>
typeof value === "object" && value !== null && !Array.isArray(value);
const flattenKeys = (data, prefix = "") => {
const keys = [];
for (const [key, value] of Object.entries(data)) {
const nextPrefix = prefix ? `${prefix}.${key}` : key;
if (isPlainObject(value)) {
keys.push(...flattenKeys(value, nextPrefix));
} else {
keys.push(nextPrefix);
}
}
return keys;
};
const buildType = (data, indent = 0) => {
if (!isPlainObject(data)) {
return "string";
}
const entries = Object.entries(data).sort(([a], [b]) => a.localeCompare(b));
const pad = " ".repeat(indent);
const inner = entries
.map(([key, value]) => {
const typeStr = buildType(value, indent + 2);
return `${" ".repeat(indent + 2)}${JSON.stringify(key)}: ${typeStr};`;
})
.join("\n");
return entries.length
? `{
${inner}
${pad}}`
: "{}";
};
const loadNamespaceJson = async () => {
const dirents = await fs.readdir(LOCALE_DIR, { withFileTypes: true });
const namespaces = [];
for (const dirent of dirents) {
if (!dirent.isFile() || !dirent.name.endsWith(".json")) continue;
const name = dirent.name.replace(/\.json$/, "");
const filePath = path.join(LOCALE_DIR, dirent.name);
const raw = await fs.readFile(filePath, "utf8");
const json = JSON.parse(raw);
namespaces.push({ name, json });
}
namespaces.sort((a, b) => a.name.localeCompare(b.name));
return namespaces;
};
const buildKeysFile = (keys) => {
const arrayLiteral = keys.map((key) => ` "${key}"`).join(",\n");
return `// This file is auto-generated by scripts/generate-i18n-keys.mjs\n// Do not edit this file manually.\n\nexport const translationKeys = [\n${arrayLiteral}\n] as const;\n\nexport type TranslationKey = typeof translationKeys[number];\n`;
};
const buildResourcesFile = (namespaces) => {
const namespaceEntries = namespaces
.map(({ name, json }) => {
const typeStr = buildType(json, 4);
return ` ${JSON.stringify(name)}: ${typeStr};`;
})
.join("\n");
return `// This file is auto-generated by scripts/generate-i18n-keys.mjs\n// Do not edit this file manually.\n\nexport interface TranslationResources {\n translation: {\n${namespaceEntries}\n };\n}\n`;
};
const main = async () => {
const namespaces = await loadNamespaceJson();
const keys = namespaces.flatMap(({ name, json }) => flattenKeys(json, name));
const keysContent = buildKeysFile(keys);
const resourcesContent = buildResourcesFile(namespaces);
await fs.mkdir(path.dirname(KEY_OUTPUT), { recursive: true });
await fs.writeFile(KEY_OUTPUT, keysContent, "utf8");
await fs.writeFile(RESOURCE_OUTPUT, resourcesContent, "utf8");
console.log(`Generated ${keys.length} translation keys.`);
};
main().catch((error) => {
console.error("Failed to generate i18n metadata:", error);
process.exitCode = 1;
});
+8 -7
View File
@@ -1,9 +1,10 @@
import fs from "fs";
import fsp from "fs/promises";
import path from "path";
import AdmZip from "adm-zip";
import { createRequire } from "module";
import { getOctokit, context } from "@actions/github";
import path from "path";
import { context, getOctokit } from "@actions/github";
import AdmZip from "adm-zip";
const target = process.argv.slice(2)[0];
const alpha = process.argv.slice(2)[1];
@@ -49,9 +50,9 @@ async function resolvePortable() {
zip.addLocalFolder(
path.join(
releaseDir,
`Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${arch}`,
`Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${arch}`,
),
`Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${arch}`,
`Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${arch}`,
);
zip.addLocalFolder(configDir, ".config");
@@ -79,11 +80,11 @@ async function resolvePortable() {
tag,
});
let assets = release.assets.filter((x) => {
const assets = release.assets.filter((x) => {
return x.name === zipFile;
});
if (assets.length > 0) {
let id = assets[0].id;
const id = assets[0].id;
await github.rest.repos.deleteReleaseAsset({
...options,
asset_id: id,
+4 -3
View File
@@ -1,8 +1,9 @@
import fs from "fs";
import path from "path";
import AdmZip from "adm-zip";
import { createRequire } from "module";
import fsp from "fs/promises";
import { createRequire } from "module";
import path from "path";
import AdmZip from "adm-zip";
const target = process.argv.slice(2)[0];
const ARCH_MAP = {
+399 -247
View File
@@ -1,18 +1,32 @@
import { execSync } from "child_process";
import { createHash } from "crypto";
import fs from "fs";
import fsp from "fs/promises";
import zlib from "zlib";
import { extract } from "tar";
import path from "path";
import zlib from "zlib";
import AdmZip from "adm-zip";
import fetch from "node-fetch";
import { HttpsProxyAgent } from "https-proxy-agent";
import { execSync } from "child_process";
import { log_info, log_debug, log_error, log_success } from "./utils.mjs";
import { glob } from "glob";
import { HttpsProxyAgent } from "https-proxy-agent";
import fetch from "node-fetch";
import { extract } from "tar";
import { log_debug, log_error, log_info, log_success } from "./utils.mjs";
/**
* Prebuild script with optimization features:
* 1. Skip downloading mihomo core if it already exists (unless --force is used)
* 2. Cache version information for 1 hour to avoid repeated version checks
* 3. Use file hash to detect changes and skip unnecessary chmod/copy operations
* 4. Use --force or -f flag to force re-download and update all resources
*
*/
const cwd = process.cwd();
const TEMP_DIR = path.join(cwd, "node_modules/.verge");
const FORCE = process.argv.includes("--force");
const FORCE = process.argv.includes("--force") || process.argv.includes("-f");
const VERSION_CACHE_FILE = path.join(TEMP_DIR, ".version_cache.json");
const HASH_CACHE_FILE = path.join(TEMP_DIR, ".hash_cache.json");
const PLATFORM_MAP = {
"x86_64-pc-windows-msvc": "win32",
@@ -43,7 +57,7 @@ const ARCH_MAP = {
const arg1 = process.argv.slice(2)[0];
const arg2 = process.argv.slice(2)[1];
const target = arg1 === "--force" ? arg2 : arg1;
const target = arg1 === "--force" || arg1 === "-f" ? arg2 : arg1;
const { platform, arch } = target
? { platform: PLATFORM_MAP[target], arch: ARCH_MAP[target] }
: process;
@@ -54,65 +68,119 @@ const SIDECAR_HOST = target
.toString()
.match(/(?<=host: ).+(?=\s*)/g)[0];
/* ======= clash meta alpha======= */
// =======================
// Version Cache
// =======================
async function loadVersionCache() {
try {
if (fs.existsSync(VERSION_CACHE_FILE)) {
const data = await fsp.readFile(VERSION_CACHE_FILE, "utf-8");
return JSON.parse(data);
}
} catch (err) {
log_debug("Failed to load version cache:", err.message);
}
return {};
}
async function saveVersionCache(cache) {
try {
await fsp.mkdir(TEMP_DIR, { recursive: true });
await fsp.writeFile(VERSION_CACHE_FILE, JSON.stringify(cache, null, 2));
log_debug("Version cache saved");
} catch (err) {
log_debug("Failed to save version cache:", err.message);
}
}
async function getCachedVersion(key) {
const cache = await loadVersionCache();
const cached = cache[key];
if (cached && Date.now() - cached.timestamp < 3600000) {
log_info(`Using cached version for ${key}: ${cached.version}`);
return cached.version;
}
return null;
}
async function setCachedVersion(key, version) {
const cache = await loadVersionCache();
cache[key] = { version, timestamp: Date.now() };
await saveVersionCache(cache);
}
// =======================
// Hash Cache & File Hash
// =======================
async function calculateFileHash(filePath) {
try {
const fileBuffer = await fsp.readFile(filePath);
const hashSum = createHash("sha256");
hashSum.update(fileBuffer);
return hashSum.digest("hex");
} catch (ignoreErr) {
return null;
}
}
async function loadHashCache() {
try {
if (fs.existsSync(HASH_CACHE_FILE)) {
const data = await fsp.readFile(HASH_CACHE_FILE, "utf-8");
return JSON.parse(data);
}
} catch (err) {
log_debug("Failed to load hash cache:", err.message);
}
return {};
}
async function saveHashCache(cache) {
try {
await fsp.mkdir(TEMP_DIR, { recursive: true });
await fsp.writeFile(HASH_CACHE_FILE, JSON.stringify(cache, null, 2));
log_debug("Hash cache saved");
} catch (err) {
log_debug("Failed to save hash cache:", err.message);
}
}
async function hasFileChanged(filePath, targetPath) {
if (FORCE) return true;
if (!fs.existsSync(targetPath)) return true;
const hashCache = await loadHashCache();
const sourceHash = await calculateFileHash(filePath);
const targetHash = await calculateFileHash(targetPath);
if (!sourceHash || !targetHash) return true;
const cacheKey = targetPath;
const cachedHash = hashCache[cacheKey];
if (cachedHash === sourceHash && sourceHash === targetHash) {
return false;
}
return true;
}
async function updateHashCache(targetPath) {
const hashCache = await loadHashCache();
const hash = await calculateFileHash(targetPath);
if (hash) {
hashCache[targetPath] = hash;
await saveHashCache(hashCache);
}
}
// =======================
// Meta maps (stable & alpha)
// =======================
const META_ALPHA_VERSION_URL =
"https://github.com/MetaCubeX/mihomo/releases/download/Prerelease-Alpha/version.txt";
const META_ALPHA_URL_PREFIX = `https://github.com/MetaCubeX/mihomo/releases/download/Prerelease-Alpha`;
let META_ALPHA_VERSION;
const META_ALPHA_MAP = {
"win32-x64": "mihomo-windows-amd64-v2",
"win32-ia32": "mihomo-windows-386",
"win32-arm64": "mihomo-windows-arm64",
"darwin-x64": "mihomo-darwin-amd64-v2",
"darwin-arm64": "mihomo-darwin-arm64",
"linux-x64": "mihomo-linux-amd64-v2",
"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 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 = new HttpsProxyAgent(httpProxy);
}
try {
const response = await fetch(META_ALPHA_VERSION_URL, {
...options,
method: "GET",
});
let v = await response.text();
META_ALPHA_VERSION = v.trim(); // Trim to remove extra whitespaces
log_info(`Latest alpha version: ${META_ALPHA_VERSION}`);
} catch (error) {
log_error("Error fetching latest alpha version:", error.message);
process.exit(1);
}
}
/* ======= clash meta stable ======= */
const META_VERSION_URL =
"https://github.com/MetaCubeX/mihomo/releases/latest/download/version.txt";
const META_URL_PREFIX = `https://github.com/MetaCubeX/mihomo/releases/download`;
let META_VERSION;
const META_MAP = {
const META_ALPHA_MAP = {
"win32-x64": "mihomo-windows-amd64-v2",
"win32-ia32": "mihomo-windows-386",
"win32-arm64": "mihomo-windows-arm64",
"darwin-x64": "mihomo-darwin-amd64-v2",
"darwin-arm64": "mihomo-darwin-arm64",
"darwin-x64": "mihomo-darwin-amd64-v1-go122",
"darwin-arm64": "mihomo-darwin-arm64-go122",
"linux-x64": "mihomo-linux-amd64-v2",
"linux-ia32": "mihomo-linux-386",
"linux-arm64": "mihomo-linux-arm64",
@@ -121,65 +189,116 @@ const META_MAP = {
"linux-loong64": "mihomo-linux-loong64",
};
// Fetch the latest release version from the version.txt file
async function getLatestReleaseVersion() {
const options = {};
const META_MAP = {
"win32-x64": "mihomo-windows-amd64-v2",
"win32-ia32": "mihomo-windows-386",
"win32-arm64": "mihomo-windows-arm64",
"darwin-x64": "mihomo-darwin-amd64-v2-go122",
"darwin-arm64": "mihomo-darwin-arm64-go122",
"linux-x64": "mihomo-linux-amd64-v2",
"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 latest versions
// =======================
async function getLatestAlphaVersion() {
if (!FORCE) {
const cached = await getCachedVersion("META_ALPHA_VERSION");
if (cached) {
META_ALPHA_VERSION = cached;
return;
}
}
const options = {};
const httpProxy =
process.env.HTTP_PROXY ||
process.env.http_proxy ||
process.env.HTTPS_PROXY ||
process.env.https_proxy;
if (httpProxy) options.agent = new HttpsProxyAgent(httpProxy);
if (httpProxy) {
options.agent = new HttpsProxyAgent(httpProxy);
try {
const response = await fetch(META_ALPHA_VERSION_URL, {
...options,
method: "GET",
});
if (!response.ok)
throw new Error(
`Failed to fetch ${META_ALPHA_VERSION_URL}: ${response.status}`,
);
META_ALPHA_VERSION = (await response.text()).trim();
log_info(`Latest alpha version: ${META_ALPHA_VERSION}`);
await setCachedVersion("META_ALPHA_VERSION", META_ALPHA_VERSION);
} catch (err) {
log_error("Error fetching latest alpha version:", err.message);
process.exit(1);
}
}
async function getLatestReleaseVersion() {
if (!FORCE) {
const cached = await getCachedVersion("META_VERSION");
if (cached) {
META_VERSION = cached;
return;
}
}
const options = {};
const httpProxy =
process.env.HTTP_PROXY ||
process.env.http_proxy ||
process.env.HTTPS_PROXY ||
process.env.https_proxy;
if (httpProxy) options.agent = new HttpsProxyAgent(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
if (!response.ok)
throw new Error(
`Failed to fetch ${META_VERSION_URL}: ${response.status}`,
);
META_VERSION = (await response.text()).trim();
log_info(`Latest release version: ${META_VERSION}`);
} catch (error) {
log_error("Error fetching latest release version:", error.message);
await setCachedVersion("META_VERSION", META_VERSION);
} catch (err) {
log_error("Error fetching latest release version:", err.message);
process.exit(1);
}
}
/*
* check available
*/
// =======================
// Validate availability
// =======================
if (!META_MAP[`${platform}-${arch}`]) {
throw new Error(
`clash meta alpha unsupported platform "${platform}-${arch}"`,
);
throw new Error(`clash meta unsupported platform "${platform}-${arch}"`);
}
if (!META_ALPHA_MAP[`${platform}-${arch}`]) {
throw new Error(
`clash meta alpha unsupported platform "${platform}-${arch}"`,
);
}
/**
* core info
*/
// =======================
// Build meta objects
// =======================
function clashMetaAlpha() {
const name = META_ALPHA_MAP[`${platform}-${arch}`];
const isWin = platform === "win32";
const urlExt = isWin ? "zip" : "gz";
const downloadURL = `${META_ALPHA_URL_PREFIX}/${name}-${META_ALPHA_VERSION}.${urlExt}`;
const exeFile = `${name}${isWin ? ".exe" : ""}`;
const zipFile = `${name}-${META_ALPHA_VERSION}.${urlExt}`;
return {
name: "verge-mihomo-alpha",
targetFile: `verge-mihomo-alpha-${SIDECAR_HOST}${isWin ? ".exe" : ""}`,
exeFile,
zipFile,
downloadURL,
exeFile: `${name}${isWin ? ".exe" : ""}`,
zipFile: `${name}-${META_ALPHA_VERSION}.${urlExt}`,
downloadURL: `${META_ALPHA_URL_PREFIX}/${name}-${META_ALPHA_VERSION}.${urlExt}`,
};
}
@@ -187,35 +306,83 @@ function clashMeta() {
const name = META_MAP[`${platform}-${arch}`];
const isWin = platform === "win32";
const urlExt = isWin ? "zip" : "gz";
const downloadURL = `${META_URL_PREFIX}/${META_VERSION}/${name}-${META_VERSION}.${urlExt}`;
const exeFile = `${name}${isWin ? ".exe" : ""}`;
const zipFile = `${name}-${META_VERSION}.${urlExt}`;
return {
name: "verge-mihomo",
targetFile: `verge-mihomo-${SIDECAR_HOST}${isWin ? ".exe" : ""}`,
exeFile,
zipFile,
downloadURL,
exeFile: `${name}${isWin ? ".exe" : ""}`,
zipFile: `${name}-${META_VERSION}.${urlExt}`,
downloadURL: `${META_URL_PREFIX}/${META_VERSION}/${name}-${META_VERSION}.${urlExt}`,
};
}
/**
* download sidecar and rename
*/
// =======================
// download helper (增强:status + magic bytes)
// =======================
async function downloadFile(url, outPath) {
const options = {};
const httpProxy =
process.env.HTTP_PROXY ||
process.env.http_proxy ||
process.env.HTTPS_PROXY ||
process.env.https_proxy;
if (httpProxy) options.agent = new HttpsProxyAgent(httpProxy);
const response = await fetch(url, {
...options,
method: "GET",
headers: { "Content-Type": "application/octet-stream" },
});
if (!response.ok) {
const body = await response.text().catch(() => "");
// 将 body 写到文件以便排查(可通过临时目录查看)
await fsp.mkdir(path.dirname(outPath), { recursive: true });
await fsp.writeFile(outPath, body);
throw new Error(`Failed to download ${url}: status ${response.status}`);
}
const buf = Buffer.from(await response.arrayBuffer());
await fsp.mkdir(path.dirname(outPath), { recursive: true });
// 简单 magic 字节检查
if (url.endsWith(".gz") || url.endsWith(".tgz")) {
if (!(buf[0] === 0x1f && buf[1] === 0x8b)) {
await fsp.writeFile(outPath, buf);
throw new Error(
`Downloaded file for ${url} is not a valid gzip (magic mismatch).`,
);
}
} else if (url.endsWith(".zip")) {
if (!(buf[0] === 0x50 && buf[1] === 0x4b)) {
await fsp.writeFile(outPath, buf);
throw new Error(
`Downloaded file for ${url} is not a valid zip (magic mismatch).`,
);
}
}
await fsp.writeFile(outPath, buf);
log_success(`download finished: ${url}`);
}
// =======================
// resolveSidecar (支持 zip / tgz / gz)
// =======================
async function resolveSidecar(binInfo) {
const { name, targetFile, zipFile, exeFile, downloadURL } = binInfo;
const sidecarDir = path.join(cwd, "src-tauri", "sidecar");
const sidecarPath = path.join(sidecarDir, targetFile);
await fsp.mkdir(sidecarDir, { recursive: true });
if (!FORCE && fs.existsSync(sidecarPath)) return;
if (!FORCE && fs.existsSync(sidecarPath)) {
log_success(`"${name}" already exists, skipping download`);
return;
}
const tempDir = path.join(TEMP_DIR, name);
const tempZip = path.join(tempDir, zipFile);
const tempExe = path.join(tempDir, exeFile);
await fsp.mkdir(tempDir, { recursive: true });
try {
if (!fs.existsSync(tempZip)) {
await downloadFile(downloadURL, tempZip);
@@ -224,140 +391,118 @@ async function resolveSidecar(binInfo) {
if (zipFile.endsWith(".zip")) {
const zip = new AdmZip(tempZip);
zip.getEntries().forEach((entry) => {
log_debug(`"${name}" entry name`, entry.entryName);
log_debug(`"${name}" entry: ${entry.entryName}`);
});
zip.extractAllTo(tempDir, true);
await fsp.rename(tempExe, sidecarPath);
// 尝试按 exeFile 重命名,否则找第一个可执行文件
if (fs.existsSync(tempExe)) {
await fsp.rename(tempExe, sidecarPath);
} else {
// 搜索候选
const files = await fsp.readdir(tempDir);
const candidate = files.find(
(f) =>
f === path.basename(exeFile) ||
f.endsWith(".exe") ||
!f.includes("."),
);
if (!candidate)
throw new Error(`Expected binary not found in ${tempDir}`);
await fsp.rename(path.join(tempDir, candidate), sidecarPath);
}
if (platform !== "win32") execSync(`chmod 755 ${sidecarPath}`);
log_success(`unzip finished: "${name}"`);
} else if (zipFile.endsWith(".tgz")) {
// tgz
await fsp.mkdir(tempDir, { recursive: true });
await extract({
cwd: tempDir,
file: tempZip,
//strip: 1, // 可能需要根据实际的 .tgz 文件结构调整
});
await extract({ cwd: tempDir, file: tempZip });
const files = await fsp.readdir(tempDir);
log_debug(`"${name}" files in tempDir:`, files);
const extractedFile = files.find((file) => file.startsWith("虚空终端-"));
if (extractedFile) {
const extractedFilePath = path.join(tempDir, extractedFile);
await fsp.rename(extractedFilePath, sidecarPath);
log_success(`"${name}" file renamed to "${sidecarPath}"`);
execSync(`chmod 755 ${sidecarPath}`);
log_success(`chmod binary finished: "${name}"`);
} else {
throw new Error(`Expected file not found in ${tempDir}`);
}
log_debug(`"${name}" extracted files:`, files);
// 优先寻找给定 exeFile 或已知前缀
let extracted = files.find(
(f) =>
f === path.basename(exeFile) ||
f.startsWith("虚空终端-") ||
!f.includes("."),
);
if (!extracted) extracted = files[0];
if (!extracted) throw new Error(`Expected file not found in ${tempDir}`);
await fsp.rename(path.join(tempDir, extracted), sidecarPath);
execSync(`chmod 755 ${sidecarPath}`);
log_success(`tgz processed: "${name}"`);
} else {
// gz
// .gz
const readStream = fs.createReadStream(tempZip);
const writeStream = fs.createWriteStream(sidecarPath);
await new Promise((resolve, reject) => {
const onError = (error) => {
log_error(`"${name}" gz failed:`, error.message);
reject(error);
};
readStream
.pipe(zlib.createGunzip().on("error", onError))
.pipe(zlib.createGunzip())
.on("error", (e) => {
log_error(`gunzip error for ${name}:`, e.message);
reject(e);
})
.pipe(writeStream)
.on("finish", () => {
execSync(`chmod 755 ${sidecarPath}`);
log_success(`chmod binary finished: "${name}"`);
if (platform !== "win32") execSync(`chmod 755 ${sidecarPath}`);
resolve();
})
.on("error", onError);
.on("error", (e) => {
log_error(`write stream error for ${name}:`, e.message);
reject(e);
});
});
log_success(`gz binary processed: "${name}"`);
}
} catch (err) {
// 需要删除文件
await fsp.rm(sidecarPath, { recursive: true, force: true });
throw err;
} finally {
// delete temp dir
await fsp.rm(tempDir, { recursive: true, force: true });
}
}
const resolveSetDnsScript = () =>
resolveResource({
file: "set_dns.sh",
localPath: path.join(cwd, "scripts/set_dns.sh"),
});
const resolveUnSetDnsScript = () =>
resolveResource({
file: "unset_dns.sh",
localPath: path.join(cwd, "scripts/unset_dns.sh"),
});
/**
* download the file to the resources dir
*/
async function resolveResource(binInfo) {
const { file, downloadURL, localPath } = binInfo;
const resDir = path.join(cwd, "src-tauri/resources");
const targetPath = path.join(resDir, file);
if (!FORCE && fs.existsSync(targetPath)) return;
if (!FORCE && fs.existsSync(targetPath) && !downloadURL && !localPath) {
log_success(`"${file}" already exists, skipping`);
return;
}
if (downloadURL) {
if (!FORCE && fs.existsSync(targetPath)) {
log_success(`"${file}" already exists, skipping download`);
return;
}
await fsp.mkdir(resDir, { recursive: true });
await downloadFile(downloadURL, targetPath);
await updateHashCache(targetPath);
}
if (localPath) {
await fs.copyFile(localPath, targetPath, (err) => {
if (err) {
console.error("Error copying file:", err);
} else {
console.log("File was copied successfully");
}
});
log_debug(`copy file finished: "${localPath}"`);
if (!(await hasFileChanged(localPath, targetPath))) {
return;
}
await fsp.mkdir(resDir, { recursive: true });
await fsp.copyFile(localPath, targetPath);
await updateHashCache(targetPath);
log_success(`Copied file: ${file}`);
}
log_success(`${file} finished`);
}
/**
* download file and save to `path`
*/ async function downloadFile(url, path) {
const options = {};
const httpProxy =
process.env.HTTP_PROXY ||
process.env.http_proxy ||
process.env.HTTPS_PROXY ||
process.env.https_proxy;
if (httpProxy) {
options.agent = new HttpsProxyAgent(httpProxy);
}
const response = await fetch(url, {
...options,
method: "GET",
headers: { "Content-Type": "application/octet-stream" },
});
const buffer = await response.arrayBuffer();
await fsp.writeFile(path, new Uint8Array(buffer));
log_success(`download finished: ${url}`);
}
// SimpleSC.dll
// SimpleSC.dll (win plugin)
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 pluginDir = path.join(process.env.APPDATA || "", "Local/NSIS");
const pluginPath = path.join(pluginDir, "SimpleSC.dll");
await fsp.mkdir(pluginDir, { recursive: true });
await fsp.mkdir(tempDir, { recursive: true });
@@ -367,95 +512,95 @@ const resolvePlugin = async () => {
await downloadFile(url, tempZip);
}
const zip = new AdmZip(tempZip);
zip.getEntries().forEach((entry) => {
log_debug(`"SimpleSC" entry name`, entry.entryName);
});
zip
.getEntries()
.forEach((entry) => log_debug(`"SimpleSC" entry`, entry.entryName));
zip.extractAllTo(tempDir, true);
await fsp.cp(tempDll, pluginPath, { recursive: true, force: true });
log_success(`unzip finished: "SimpleSC"`);
if (fs.existsSync(tempDll)) {
await fsp.cp(tempDll, pluginPath, { recursive: true, force: true });
log_success(`unzip finished: "SimpleSC"`);
} else {
// 如果 dll 名称不同,尝试找到 dll
const files = await fsp.readdir(tempDir);
const dll = files.find((f) => f.toLowerCase().endsWith(".dll"));
if (dll) {
await fsp.cp(path.join(tempDir, dll), pluginPath, {
recursive: true,
force: true,
});
log_success(`unzip finished: "SimpleSC" (found ${dll})`);
} else {
throw new Error("SimpleSC.dll not found in zip");
}
}
} finally {
await fsp.rm(tempDir, { recursive: true, force: true });
}
};
// service chmod
// service chmod (保留并使用 glob)
const resolveServicePermission = async () => {
const serviceExecutables = [
"clash-verge-service*",
"install-service*",
"uninstall-service*",
"clash-verge-service-install*",
"clash-verge-service-uninstall*",
];
const resDir = path.join(cwd, "src-tauri/resources");
for (let f of serviceExecutables) {
// 使用glob模块来处理通配符
const hashCache = await loadHashCache();
let hasChanges = false;
for (const f of serviceExecutables) {
const files = glob.sync(path.join(resDir, f));
for (let filePath of files) {
for (const filePath of files) {
if (fs.existsSync(filePath)) {
execSync(`chmod 755 ${filePath}`);
log_success(`chmod finished: "${filePath}"`);
const currentHash = await calculateFileHash(filePath);
const cacheKey = `${filePath}_chmod`;
if (!FORCE && hashCache[cacheKey] === currentHash) {
continue;
}
try {
execSync(`chmod 755 ${filePath}`);
log_success(`chmod finished: "${filePath}"`);
} catch (e) {
log_error(`chmod failed for ${filePath}:`, e.message);
}
hashCache[cacheKey] = currentHash;
hasChanges = true;
}
}
}
if (hasChanges) {
await saveHashCache(hashCache);
}
};
// 在 resolveResource 函数后添加新函数
async function resolveLocales() {
const srcLocalesDir = path.join(cwd, "src/locales");
const targetLocalesDir = path.join(cwd, "src-tauri/resources/locales");
try {
// 确保目标目录存在
await fsp.mkdir(targetLocalesDir, { recursive: true });
// 读取所有语言文件
const files = await fsp.readdir(srcLocalesDir);
// 复制每个文件
for (const file of files) {
const srcPath = path.join(srcLocalesDir, file);
const targetPath = path.join(targetLocalesDir, file);
await fsp.copyFile(srcPath, targetPath);
log_success(`Copied locale file: ${file}`);
}
log_success("All locale files copied successfully");
} catch (err) {
log_error("Error copying locale files:", err.message);
throw err;
}
}
/**
* main
*/
const SERVICE_URL = `https://github.com/clash-verge-rev/clash-verge-service/releases/download/${SIDECAR_HOST}`;
// =======================
// Other resource resolvers (service, mmdb, geosite, geoip, enableLoopback, sysproxy)
// =======================
const SERVICE_URL = `https://github.com/clash-verge-rev/clash-verge-service-ipc/releases/download/${SIDECAR_HOST}`;
const resolveService = () => {
let ext = platform === "win32" ? ".exe" : "";
let suffix = platform === "linux" ? "-" + SIDECAR_HOST : "";
resolveResource({
const ext = platform === "win32" ? ".exe" : "";
const suffix = platform === "linux" ? "-" + SIDECAR_HOST : "";
return resolveResource({
file: "clash-verge-service" + suffix + ext,
downloadURL: `${SERVICE_URL}/clash-verge-service${ext}`,
});
};
const resolveInstall = () => {
let ext = platform === "win32" ? ".exe" : "";
let suffix = platform === "linux" ? "-" + SIDECAR_HOST : "";
resolveResource({
file: "install-service" + suffix + ext,
downloadURL: `${SERVICE_URL}/install-service${ext}`,
const ext = platform === "win32" ? ".exe" : "";
const suffix = platform === "linux" ? "-" + SIDECAR_HOST : "";
return resolveResource({
file: "clash-verge-service-install" + suffix + ext,
downloadURL: `${SERVICE_URL}/clash-verge-service-install${ext}`,
});
};
const resolveUninstall = () => {
let ext = platform === "win32" ? ".exe" : "";
let suffix = platform === "linux" ? "-" + SIDECAR_HOST : "";
resolveResource({
file: "uninstall-service" + suffix + ext,
downloadURL: `${SERVICE_URL}/uninstall-service${ext}`,
const ext = platform === "win32" ? ".exe" : "";
const suffix = platform === "linux" ? "-" + SIDECAR_HOST : "";
return resolveResource({
file: "clash-verge-service-uninstall" + suffix + ext,
downloadURL: `${SERVICE_URL}/clash-verge-service-uninstall${ext}`,
});
};
@@ -479,15 +624,27 @@ const resolveEnableLoopback = () =>
file: "enableLoopback.exe",
downloadURL: `https://github.com/Kuingsmile/uwp-tool/releases/download/latest/enableLoopback.exe`,
});
const resolveWinSysproxy = () =>
resolveResource({
file: "sysproxy.exe",
downloadURL: `https://github.com/clash-verge-rev/sysproxy/releases/download/${arch}/sysproxy.exe`,
});
const resolveSetDnsScript = () =>
resolveResource({
file: "set_dns.sh",
localPath: path.join(cwd, "scripts/set_dns.sh"),
});
const resolveUnSetDnsScript = () =>
resolveResource({
file: "unset_dns.sh",
localPath: path.join(cwd, "scripts/unset_dns.sh"),
});
// =======================
// Tasks
// =======================
const tasks = [
// { name: "clash", func: resolveClash, retry: 5 },
{
name: "verge-mihomo-alpha",
func: () =>
@@ -537,11 +694,6 @@ const tasks = [
retry: 5,
macosOnly: true,
},
{
name: "locales",
func: resolveLocales,
retry: 2,
},
];
async function runTask() {
+34 -13
View File
@@ -29,10 +29,11 @@
* Errors are logged and the process exits with code 1 on failure.
*/
import { execSync } from "child_process";
import fs from "fs/promises";
import path from "path";
import { program } from "commander";
import { execSync } from "child_process";
/**
* 获取当前 git 短 commit hash
@@ -58,26 +59,44 @@ function getLatestTauriCommit() {
)
.toString()
.trim();
return execSync(`git rev-parse --short ${fullHash}`).toString().trim();
} catch {
const shortHash = execSync(`git rev-parse --short ${fullHash}`)
.toString()
.trim();
console.log(`[INFO]: Latest Tauri-related commit: ${shortHash}`);
return shortHash;
} catch (error) {
console.warn(
"[WARN]: Failed to get latest Tauri commit, fallback to current git short commit",
);
console.warn(`[WARN]: Error details: ${error.message}`);
return getGitShortCommit();
}
}
/**
* 生成短时间戳(格式:YYMMDD)或带 commit(格式:YYMMDD.cc39b27)
* 生成短时间戳(格式:MMDD)或带 commit(格式:MMDD.cc39b27)
* 使用 Asia/Shanghai 时区
* @param {boolean} withCommit 是否带 commit
* @param {boolean} useTauriCommit 是否使用 Tauri 相关的 commit(仅当 withCommit 为 true 时有效)
* @returns {string}
*/
function generateShortTimestamp(withCommit = false) {
function generateShortTimestamp(withCommit = false, useTauriCommit = false) {
const now = new Date();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
const formatter = new Intl.DateTimeFormat("en-CA", {
timeZone: "Asia/Shanghai",
month: "2-digit",
day: "2-digit",
});
const parts = formatter.formatToParts(now);
const month = parts.find((part) => part.type === "month").value;
const day = parts.find((part) => part.type === "day").value;
if (withCommit) {
const gitShort = getGitShortCommit();
const gitShort = useTauriCommit
? getLatestTauriCommit()
: getGitShortCommit();
return `${month}${day}.${gitShort}`;
}
return `${month}${day}`;
@@ -255,15 +274,17 @@ async function main(versionArg) {
const baseVersion = getBaseVersion(currentVersion);
if (versionArg.toLowerCase() === "autobuild") {
// 格式: 2.3.0+autobuild.250613.cc39b27
newVersion = `${baseVersion}+autobuild.${generateShortTimestamp(true)}`;
// 格式: 2.3.0+autobuild.1004.cc39b27
// 使用 Tauri 相关的最新 commit hash
newVersion = `${baseVersion}+autobuild.${generateShortTimestamp(true, true)}`;
} else if (versionArg.toLowerCase() === "autobuild-latest") {
// 格式: 2.3.0+autobuild.0614.a1b2c3d (使用最新 Tauri 提交)
// 格式: 2.3.0+autobuild.1004.a1b2c3d (使用最新 Tauri 提交)
const latestTauriCommit = getLatestTauriCommit();
newVersion = `${baseVersion}+autobuild.${generateShortTimestamp()}.${latestTauriCommit}`;
} else if (versionArg.toLowerCase() === "deploytest") {
// 格式: 2.3.0+deploytest.250613.cc39b27
newVersion = `${baseVersion}+deploytest.${generateShortTimestamp(true)}`;
// 格式: 2.3.0+deploytest.1004.cc39b27
// 使用 Tauri 相关的最新 commit hash
newVersion = `${baseVersion}+deploytest.${generateShortTimestamp(true, true)}`;
} else {
newVersion = `${baseVersion}-${versionArg.toLowerCase()}`;
}
+18 -3
View File
@@ -1,6 +1,8 @@
import axios from "axios";
import { readFileSync } from "fs";
import { log_success, log_error, log_info } from "./utils.mjs";
import axios from "axios";
import { log_error, log_info, log_success } from "./utils.mjs";
const CHAT_ID_RELEASE = "@clash_verge_re"; // 正式发布频道
const CHAT_ID_TEST = "@vergetest"; // 测试频道
@@ -71,11 +73,24 @@ async function sendTelegramNotification() {
.join("\n");
}
function normalizeDetailsTags(content) {
return content
.replace(
/<summary>\s*<strong>\s*(.*?)\s*<\/strong>\s*<\/summary>/g,
"\n<b>$1</b>\n",
)
.replace(/<summary>\s*(.*?)\s*<\/summary>/g, "\n<b>$1</b>\n")
.replace(/<\/?details>/g, "")
.replace(/<\/?strong>/g, (m) => (m === "</strong>" ? "</b>" : "<b>"))
.replace(/<br\s*\/?>/g, "\n");
}
releaseContent = normalizeDetailsTags(releaseContent);
const formattedContent = convertMarkdownToTelegramHTML(releaseContent);
const releaseTitle = isAutobuild ? "滚动更新版发布" : "正式发布";
const encodedVersion = encodeURIComponent(version);
const content = `<b>🎉 <a href="https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/v${encodedVersion}">Clash Verge Rev v${version}</a> ${releaseTitle}</b>\n\n${formattedContent}`;
const content = `<b>🎉 <a href="https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild">Clash Verge Rev v${version}</a> ${releaseTitle}</b>\n\n${formattedContent}`;
// 发送到 Telegram
try {
+7 -7
View File
@@ -2,9 +2,9 @@ import fs from "fs";
import fsp from "fs/promises";
import path from "path";
const UPDATE_LOG = "UPDATELOG.md";
const UPDATE_LOG = "Changelog.md";
// parse the UPDATELOG.md
// parse the Changelog.md
export async function resolveUpdateLog(tag) {
const cwd = process.cwd();
@@ -14,7 +14,7 @@ export async function resolveUpdateLog(tag) {
const file = path.join(cwd, UPDATE_LOG);
if (!fs.existsSync(file)) {
throw new Error("could not found UPDATELOG.md");
throw new Error("could not found Changelog.md");
}
const data = await fsp.readFile(file, "utf-8");
@@ -38,7 +38,7 @@ export async function resolveUpdateLog(tag) {
});
if (!map[tag]) {
throw new Error(`could not found "${tag}" in UPDATELOG.md`);
throw new Error(`could not found "${tag}" in Changelog.md`);
}
return map[tag].join("\n").trim();
@@ -49,7 +49,7 @@ export async function resolveUpdateLogDefault() {
const file = path.join(cwd, UPDATE_LOG);
if (!fs.existsSync(file)) {
throw new Error("could not found UPDATELOG.md");
throw new Error("could not found Changelog.md");
}
const data = await fsp.readFile(file, "utf-8");
@@ -58,7 +58,7 @@ export async function resolveUpdateLogDefault() {
const reEnd = /^---/;
let isCapturing = false;
let content = [];
const content = [];
let firstTag = "";
for (const line of data.split("\n")) {
@@ -77,7 +77,7 @@ export async function resolveUpdateLogDefault() {
}
if (!firstTag) {
throw new Error("could not found any version tag in UPDATELOG.md");
throw new Error("could not found any version tag in Changelog.md");
}
return content.join("\n").trim();
+4 -3
View File
@@ -1,5 +1,6 @@
import { context, getOctokit } from "@actions/github";
import fetch from "node-fetch";
import { getOctokit, context } from "@actions/github";
import { resolveUpdateLog } from "./updatelog.mjs";
const UPDATE_TAG_NAME = "updater";
@@ -35,7 +36,7 @@ async function resolveUpdater() {
const updateData = {
name: tag.name,
notes: await resolveUpdateLog(tag.name), // use updatelog.md
notes: await resolveUpdateLog(tag.name), // use Changelog.md
pub_date: new Date().toISOString(),
platforms: {
"windows-x86_64": { signature: "", url: "" },
@@ -113,7 +114,7 @@ async function resolveUpdater() {
});
// delete the old assets
for (let asset of updateRelease.assets) {
for (const asset of updateRelease.assets) {
if (asset.name === UPDATE_JSON_FILE) {
await github.rest.repos.deleteReleaseAsset({
...options,
+3 -2
View File
@@ -1,5 +1,6 @@
import fetch from "node-fetch";
import { getOctokit, context } from "@actions/github";
import fetch from "node-fetch";
import { resolveUpdateLog, resolveUpdateLogDefault } from "./updatelog.mjs";
// Add stable update JSON filenames
@@ -259,7 +260,7 @@ async function processRelease(github, options, tag, isAlpha) {
const proxyFile = isAlpha ? ALPHA_UPDATE_JSON_PROXY : UPDATE_JSON_PROXY;
// Delete existing assets with these names
for (let asset of updateRelease.assets) {
for (const asset of updateRelease.assets) {
if (asset.name === jsonFile) {
await github.rest.repos.deleteReleaseAsset({
...options,
+2 -1
View File
@@ -1 +1,2 @@
avoid-breaking-exported-api = true
avoid-breaking-exported-api = true
cognitive-complexity-threshold = 25
+79 -146
View File
@@ -1,92 +1,109 @@
[package]
name = "clash-verge"
version = "2.4.1"
version = "2.4.4-rc"
description = "clash verge"
authors = ["zzzgydi", "Tunglies", "wonfen", "MystiPanda"]
license = "GPL-3.0-only"
repository = "https://github.com/clash-verge-rev/clash-verge-rev.git"
default-run = "clash-verge"
edition = "2021"
build = "build.rs"
edition = { workspace = true }
rust-version = { workspace = true }
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]
verge-dev = ["clash_verge_logger/color"]
tauri-dev = ["clash-verge-logging/tauri-dev"]
tokio-trace = ["console-subscriber"]
clippy = ["tauri/test"]
tracing = []
[package.metadata.bundle]
identifier = "io.github.clash-verge-rev.clash-verge-rev"
[build-dependencies]
tauri-build = { version = "2.4.0", features = [] }
tauri-build = { version = "2.5.3", features = [] }
[dependencies]
warp = { version = "0.4.2", features = ["server"] }
anyhow = "1.0.99"
dirs = "6.0"
open = "5.3.2"
log = "0.4.27"
dunce = "1.0.5"
log4rs = "1.3.0"
nanoid = "0.4"
chrono = "0.4.41"
sysinfo = { version = "0.37.0", features = ["network", "system"] }
boa_engine = "0.20.0"
serde_json = "1.0.143"
serde_yaml_ng = "0.10.0"
once_cell = "1.21.3"
port_scanner = "0.1.5"
delay_timer = "0.11.6"
parking_lot = "0.12.4"
percent-encoding = "2.3.2"
tokio = { version = "1.47.1", features = [
"rt-multi-thread",
"macros",
"time",
"sync",
] }
serde = { version = "1.0.219", features = ["derive"] }
reqwest = { version = "0.12.23", features = ["json", "cookies"] }
regex = "1.11.2"
sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs" }
tauri = { version = "2.8.4", features = [
clash-verge-draft = { workspace = true }
clash-verge-logging = { workspace = true }
clash-verge-signal = { workspace = true }
clash-verge-types = { workspace = true }
tauri-plugin-clash-verge-sysinfo = { workspace = true }
tauri-plugin-clipboard-manager = { workspace = true }
tauri = { workspace = true, features = [
"protocol-asset",
"devtools",
"tray-icon",
"image-ico",
"image-png",
] }
parking_lot = { workspace = true }
anyhow = { workspace = true }
tokio = { workspace = true }
compact_str = { workspace = true }
flexi_logger = { workspace = true }
log = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
serde_yaml_ng = { workspace = true }
smartstring = { workspace = true, features = ["serde"] }
warp = { version = "0.4.2", features = ["server"] }
open = "5.3.3"
dunce = "1.0.5"
nanoid = "0.4"
chrono = "0.4.42"
boa_engine = "0.21.0"
once_cell = { version = "1.21.3", features = ["parking_lot"] }
port_scanner = "0.1.5"
delay_timer = "0.11.6"
percent-encoding = "2.3.2"
reqwest = { version = "0.12.24", features = ["json", "cookies", "rustls-tls"] }
regex = "1.12.2"
sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs", features = [
"guard",
] }
network-interface = { version = "2.0.3", features = ["serde"] }
tauri-plugin-shell = "2.3.1"
tauri-plugin-dialog = "2.3.3"
tauri-plugin-fs = "2.4.2"
tauri-plugin-process = "2.3.0"
tauri-plugin-clipboard-manager = "2.3.0"
tauri-plugin-deep-link = "2.4.2"
tauri-plugin-devtools = "2.0.1"
tauri-plugin-window-state = "2.4.0"
zip = "4.5.0"
tauri-plugin-shell = "2.3.3"
tauri-plugin-dialog = "2.4.2"
tauri-plugin-fs = "2.4.4"
tauri-plugin-process = "2.3.1"
tauri-plugin-deep-link = "2.4.5"
tauri-plugin-window-state = "2.4.1"
zip = "6.0.0"
reqwest_dav = "0.2.2"
aes-gcm = { version = "0.10.3", features = ["std"] }
base64 = "0.22.1"
getrandom = "0.3.3"
getrandom = "0.3.4"
futures = "0.3.31"
sys-locale = "0.3.2"
libc = "0.2.175"
gethostname = "1.0.2"
hmac = "0.12.1"
sha2 = "0.10.9"
hex = "0.4.3"
gethostname = "1.1.0"
scopeguard = "1.2.0"
kode-bridge = "0.2.1-rc2"
dashmap = "6.1.0"
tauri-plugin-notification = "2.3.1"
console-subscriber = { version = "0.4.1", optional = true }
tauri-plugin-notification = "2.3.3"
tokio-stream = "0.1.17"
isahc = { version = "1.7.2", default-features = false, features = [
"text-decoding",
"parking_lot",
] }
backoff = { version = "0.4.0", features = ["tokio"] }
tauri-plugin-http = "2.5.4"
console-subscriber = { version = "0.5.0", optional = true }
tauri-plugin-devtools = { version = "2.0.1" }
tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo" }
clash_verge_logger = { git = "https://github.com/clash-verge-rev/clash-verge-logger" }
async-trait = "0.1.89"
clash_verge_service_ipc = { version = "2.0.24", features = [
"client",
], git = "https://github.com/clash-verge-rev/clash-verge-service-ipc" }
arc-swap = "1.7.1"
rust-i18n = "3.1.5"
rust_iso3166 = "0.1.14"
dark-light = "2.0.0"
[target.'cfg(windows)'.dependencies]
deelevate = { workspace = true }
runas = "=1.2.0"
deelevate = "0.2.0"
winreg = "0.55.0"
winapi = { version = "0.3.9", features = [
"winbase",
@@ -102,97 +119,13 @@ winapi = { version = "0.3.9", features = [
"winreg",
] }
[target.'cfg(target_os = "linux")'.dependencies]
users = "0.11.0"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-autostart = "2.5.0"
tauri-plugin-global-shortcut = "2.3.0"
tauri-plugin-autostart = "2.5.1"
tauri-plugin-global-shortcut = "2.3.1"
tauri-plugin-updater = "2.9.0"
[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]
verge-dev = []
tokio-trace = ["console-subscriber"]
[profile.release]
panic = "abort"
codegen-units = 1
lto = true
opt-level = "s"
strip = true
[profile.dev]
incremental = true
codegen-units = 256 # 增加编译单元,提升编译速度
opt-level = 0 # 禁用优化,进一步提升编译速度
debug = true # 保留调试信息
strip = false # 不剥离符号,保留调试信息
[profile.fast-release]
inherits = "release" # 继承 release 的配置
panic = "abort" # 与 release 相同
codegen-units = 256 # 增加编译单元,提升编译速度
lto = false # 禁用 LTO,提升编译速度
opt-level = 0 # 禁用优化,大幅提升编译速度
debug = true # 保留调试信息
strip = false # 不剥离符号,保留调试信息
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[dev-dependencies]
criterion = "0.7.0"
criterion = { workspace = true }
[lints.clippy]
# Core categories - most important for code safety and correctness
correctness = { level = "deny", priority = -1 }
suspicious = { level = "deny", priority = -1 }
# Critical safety lints - warn for now due to extensive existing usage
unwrap_used = "warn"
expect_used = "warn"
panic = "deny"
unimplemented = "deny"
# Development quality lints
todo = "warn"
dbg_macro = "warn"
#print_stdout = "warn"
#print_stderr = "warn"
# Performance lints for proxy application
clone_on_ref_ptr = "warn"
rc_clone_in_vec_init = "warn"
large_stack_arrays = "warn"
large_const_arrays = "warn"
# Security lints
#integer_division = "warn"
#lossy_float_literal = "warn"
#default_numeric_fallback = "warn"
# Mutex and async lints - strict control
async_yields_async = "deny" # Prevents missing await in async blocks
mutex_atomic = "deny" # Use atomics instead of Mutex<bool/int>
mutex_integer = "deny" # Use AtomicInt instead of Mutex<int>
rc_mutex = "deny" # Single-threaded Rc with Mutex is wrong
unused_async = "deny" # Too many false positives in Tauri/framework code
await_holding_lock = "deny"
large_futures = "deny"
future_not_send = "deny"
# Common style improvements
redundant_else = "deny" # Too many in existing code
needless_continue = "deny" # Too many in existing code
needless_raw_string_hashes = "deny" # Too many in existing code
# Disable noisy categories for existing codebase but keep them available
#style = { level = "allow", priority = -1 }
#complexity = { level = "allow", priority = -1 }
#perf = { level = "allow", priority = -1 }
#pedantic = { level = "allow", priority = -1 }
#nursery = { level = "allow", priority = -1 }
#restriction = { level = "allow", priority = -1 }
[lints]
workspace = true
-91
View File
@@ -1,91 +0,0 @@
use criterion::{criterion_group, criterion_main, Criterion};
use std::hint::black_box;
// 业务模型 & Draft
use app_lib::config::Draft as DraftNew;
use app_lib::config::IVerge;
// fn bench_apply_old(c: &mut Criterion) {
// c.bench_function("apply_draft_old", |b| {
// b.iter(|| {
// let verge = Box::new(IVerge {
// enable_auto_launch: Some(true),
// enable_tun_mode: Some(false),
// ..Default::default()
// });
// let draft = DraftOld::from(black_box(verge));
// {
// let mut d = draft.draft_mut();
// d.enable_auto_launch = Some(false);
// }
// let _ = draft.apply();
// });
// });
// }
// fn bench_discard_old(c: &mut Criterion) {
// c.bench_function("discard_draft_old", |b| {
// b.iter(|| {
// let verge = Box::new(IVerge::default());
// let draft = DraftOld::from(black_box(verge));
// {
// let mut d = draft.draft_mut();
// d.enable_auto_launch = Some(false);
// }
// let _ = draft.discard();
// });
// });
// }
/// 基准:修改草稿并 apply()
fn bench_apply_new(c: &mut Criterion) {
c.bench_function("apply_draft_new", |b| {
b.iter(|| {
let verge = Box::new(IVerge {
enable_auto_launch: Some(true),
enable_tun_mode: Some(false),
..Default::default()
});
let draft = DraftNew::from(black_box(verge));
{
let mut d = draft.draft_mut();
d.enable_auto_launch = Some(false);
}
let _ = draft.apply();
});
});
}
/// 基准:修改草稿并 discard()
fn bench_discard_new(c: &mut Criterion) {
c.bench_function("discard_draft_new", |b| {
b.iter(|| {
let verge = Box::new(IVerge::default());
let draft = DraftNew::from(black_box(verge));
{
let mut d = draft.draft_mut();
d.enable_auto_launch = Some(false);
}
let _ = draft.discard();
});
});
}
criterion_group!(
benches,
// bench_apply_old,
// bench_discard_old,
bench_apply_new,
bench_discard_new
);
criterion_main!(benches);
+7 -1
View File
@@ -1,3 +1,9 @@
fn main() {
tauri_build::build()
#[cfg(feature = "clippy")]
{
println!("cargo:warning=Skipping tauri_build during Clippy");
}
#[cfg(not(feature = "clippy"))]
tauri_build::build();
}
+8 -1
View File
@@ -18,6 +18,13 @@
"autostart:allow-disable",
"autostart:allow-is-enabled",
"core:window:allow-set-theme",
"notification:default"
"notification:default",
"http:default",
"http:allow-fetch",
{
"identifier": "http:default",
"allow": [{ "url": "https://*/*" }, { "url": "http://*/*" }]
},
"mihomo:default"
]
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 41 KiB

+59
View File
@@ -0,0 +1,59 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
profilesReactivated:
title: Profiles
body: Profile Reactivated.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
outboundModes: Outbound Modes
rule: Rule
direct: Direct
global: Global
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+59
View File
@@ -0,0 +1,59 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
profilesReactivated:
title: Profiles
body: Profile Reactivated.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
outboundModes: Outbound Modes
rule: Regel
direct: Direkt
global: Global
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+61
View File
@@ -0,0 +1,61 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
profilesReactivated:
title: Profiles
body: Profile Reactivated.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminInstallPrompt: Installing the service requires administrator privileges.
adminUninstallPrompt: Uninstalling the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
outboundModes: Outbound Modes
rule: Rule
direct: Direct
global: Global
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+59
View File
@@ -0,0 +1,59 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
profilesReactivated:
title: Profiles
body: Profile Reactivated.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
outboundModes: Outbound Modes
rule: Regla
direct: Directo
global: Global
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+59
View File
@@ -0,0 +1,59 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
profilesReactivated:
title: Profiles
body: Profile Reactivated.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
outboundModes: Outbound Modes
rule: Rule
direct: Direct
global: Global
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+59
View File
@@ -0,0 +1,59 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
profilesReactivated:
title: Profiles
body: Profile Reactivated.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
outboundModes: Outbound Modes
rule: Aturan
direct: Langsung
global: Global
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+59
View File
@@ -0,0 +1,59 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
profilesReactivated:
title: Profiles
body: Profile Reactivated.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
outboundModes: Outbound Modes
rule: ルール
direct: ダイレクト
global: グローバル
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+59
View File
@@ -0,0 +1,59 @@
_version: 1
notifications:
dashboardToggled:
title: 대시보드
body: 대시보드 표시 상태가 업데이트되었습니다.
clashModeChanged:
title: 모드 전환
body: "{mode}(으)로 전환되었습니다."
systemProxyToggled:
title: 시스템 프록시
body: 시스템 프록시 상태가 업데이트되었습니다.
tunModeToggled:
title: TUN 모드
body: TUN 모드 상태가 업데이트되었습니다.
lightweightModeEntered:
title: 경량 모드
body: 경량 모드에 진입했습니다.
profilesReactivated:
title: Profiles
body: Profile Reactivated.
appQuit:
title: 곧 종료
body: Clash Verge가 곧 종료됩니다.
appHidden:
title: 앱이 숨겨짐
body: Clash Verge가 백그라운드에서 실행 중입니다.
service:
adminPrompt: 서비스를 설치하려면 관리자 권한이 필요합니다.
tray:
dashboard: 대시보드
ruleMode: 규칙 모드
globalMode: 전역 모드
directMode: 직접 모드
outboundModes: Outbound Modes
rule: 규칙
direct: 직접
global: 글로벌
profiles: 프로필
proxies: 프록시
systemProxy: 시스템 프록시
tunMode: TUN 모드
closeAllConnections: 모든 연결 닫기
lightweightMode: 경량 모드
copyEnv: 환경 변수 복사
confDir: 구성 디렉터리
coreDir: 코어 디렉터리
logsDir: 로그 디렉터리
openDir: 디렉터리 열기
appLog: 애플리케이션 로그
coreLog: 코어 로그
restartClash: Clash 코어 재시작
restartApp: 애플리케이션 재시작
vergeVersion: Verge 버전
more: 더 보기
exit: 종료
tooltip:
systemProxy: 시스템 프록시
tun: TUN
profile: 프로필
+59
View File
@@ -0,0 +1,59 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
profilesReactivated:
title: Profiles
body: Profile Reactivated.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
outboundModes: Outbound Modes
rule: Правило
direct: Прямой
global: Глобальный
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+59
View File
@@ -0,0 +1,59 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
profilesReactivated:
title: Profiles
body: Profile Reactivated.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
outboundModes: Outbound Modes
rule: Kural
direct: Doğrudan
global: Küresel
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+59
View File
@@ -0,0 +1,59 @@
_version: 1
notifications:
dashboardToggled:
title: Dashboard
body: Dashboard visibility has been updated.
clashModeChanged:
title: Mode Switch
body: Switched to {mode}.
systemProxyToggled:
title: System Proxy
body: System proxy status has been updated.
tunModeToggled:
title: TUN Mode
body: TUN mode status has been updated.
lightweightModeEntered:
title: Lightweight Mode
body: Entered lightweight mode.
profilesReactivated:
title: Profiles
body: Profile Reactivated.
appQuit:
title: About to Exit
body: Clash Verge is about to exit.
appHidden:
title: Application Hidden
body: Clash Verge is running in the background.
service:
adminPrompt: Installing the service requires administrator privileges.
tray:
dashboard: Dashboard
ruleMode: Rule Mode
globalMode: Global Mode
directMode: Direct Mode
outboundModes: Outbound Modes
rule: Rule
direct: Direct
global: Global
profiles: Profiles
proxies: Proxies
systemProxy: System Proxy
tunMode: TUN Mode
closeAllConnections: Close All Connections
lightweightMode: Lightweight Mode
copyEnv: Copy Environment Variables
confDir: Configuration Directory
coreDir: Core Directory
logsDir: Log Directory
openDir: Open Directory
appLog: Application Log
coreLog: Core Log
restartClash: Restart Clash Core
restartApp: Restart Application
vergeVersion: Verge Version
more: More
exit: Exit
tooltip:
systemProxy: System Proxy
tun: TUN
profile: Profile
+60
View File
@@ -0,0 +1,60 @@
_version: 1
notifications:
dashboardToggled:
title: 仪表板
body: 仪表板显示状态已更新。
clashModeChanged:
title: 模式切换
body: 已切换至 {mode}。
systemProxyToggled:
title: 系统代理
body: 系统代理状态已更新。
tunModeToggled:
title: TUN 模式
body: TUN 模式状态已更新。
lightweightModeEntered:
title: 轻量模式
body: 已进入轻量模式。
profilesReactivated:
title: 订阅
body: 订阅已激活。
appQuit:
title: 即将退出
body: Clash Verge 即将退出。
appHidden:
title: 应用已隐藏
body: Clash Verge 正在后台运行。
service:
adminInstallPrompt: 安装服务需要管理员权限
adminUninstallPrompt: 写在服务需要管理员权限
tray:
dashboard: 仪表板
ruleMode: 规则模式
globalMode: 全局模式
directMode: 直连模式
outboundModes: 出站模式
rule: 规则
direct: 直连
global: 全局
profiles: 订阅
proxies: 代理
systemProxy: 系统代理
tunMode: TUN 模式
closeAllConnections: 关闭所有连接
lightweightMode: 轻量模式
copyEnv: 复制环境变量
confDir: 配置目录
coreDir: 内核目录
logsDir: 日志目录
openDir: 打开目录
appLog: 应用日志
coreLog: 内核日志
restartClash: 重启 Clash 内核
restartApp: 重启应用
vergeVersion: Verge 版本
more: 更多
exit: 退出
tooltip:
systemProxy: 系统代理
tun: TUN
profile: 订阅
+60
View File
@@ -0,0 +1,60 @@
_version: 1
notifications:
dashboardToggled:
title: 儀表板
body: 儀表板顯示狀態已更新。
clashModeChanged:
title: 模式切換
body: 已切換至 {mode}。
systemProxyToggled:
title: 系統代理
body: 系統代理狀態已更新。
tunModeToggled:
title: 虛擬網路介面卡模式
body: 已更新虛擬網路介面卡模式狀態。
lightweightModeEntered:
title: 輕量模式
body: 已進入輕量模式。
profilesReactivated:
title: 訂閱
body: 訂閱已啟用。
appQuit:
title: 即將退出
body: Clash Verge 即將退出。
appHidden:
title: 應用已隱藏
body: Clash Verge 正在背景執行。
service:
adminInstallPrompt: 安裝服務需要管理員權限
adminUninstallPrompt: 卸载服務需要管理員權限
tray:
dashboard: 儀表板
ruleMode: 規則模式
globalMode: 全域模式
directMode: 直連模式
outboundModes: 出站模式
rule: 規則
direct: 直連
global: 全域
profiles: 訂閱
proxies: 代理
systemProxy: 系統代理
tunMode: 虛擬網路介面卡模式
closeAllConnections: 關閉所有連線
lightweightMode: 輕量模式
copyEnv: 複製環境變數
confDir: 設定目錄
coreDir: 核心目錄
logsDir: 日誌目錄
openDir: 開啟目錄
appLog: 應用程式日誌
coreLog: 核心日誌
restartClash: 重新啟動 Clash 核心
restartApp: 重新啟動應用程式
vergeVersion: Verge 版本
more: 更多
exit: 離開
tooltip:
systemProxy: 系統代理
tun: 虛擬網路介面卡
profile: 訂閱
+2 -2
View File
@@ -1,4 +1,4 @@
#!/bin/bash
chmod +x /usr/bin/install-service
chmod +x /usr/bin/uninstall-service
chmod +x /usr/bin/clash-verge-service-install
chmod +x /usr/bin/clash-verge-service-uninstall
chmod +x /usr/bin/clash-verge-service
+1 -1
View File
@@ -1,2 +1,2 @@
#!/bin/bash
/usr/bin/uninstall-service
/usr/bin/clash-verge-service-uninstall
+10
View File
@@ -0,0 +1,10 @@
<?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>AssociatedBundleIdentifiers</key>
<array>
<string>io.github.clash-verge-rev.clash-verge-rev.service</string>
</array>
</dict>
</plist>
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
[toolchain]
channel = "1.91.0"
components = ["rustfmt", "clippy"]
+2 -2
View File
@@ -1,4 +1,4 @@
max_width = 100
max_width = 120
hard_tabs = false
tab_spaces = 4
newline_style = "Auto"
@@ -6,7 +6,7 @@ use_small_heuristics = "Default"
reorder_imports = true
reorder_modules = true
remove_nested_parens = true
edition = "2021"
edition = "2024"
merge_derives = true
use_try_shorthand = false
use_field_init_shorthand = false
+92 -93
View File
@@ -1,37 +1,58 @@
use super::CmdResult;
use crate::core::sysopt::Sysopt;
use crate::utils::resolve::ui::{self, UiReadyStage};
use crate::{
feat, logging,
utils::{dirs, logging::Type},
wrap_err,
cmd::StringifyErr as _,
feat,
utils::dirs::{self, PathBufExec as _},
};
use tauri::{AppHandle, Manager};
use clash_verge_logging::{Type, logging};
use smartstring::alias::String;
use std::path::Path;
use tauri::{AppHandle, Manager as _};
use tokio::fs;
use tokio::io::AsyncWriteExt as _;
/// 打开应用程序所在目录
#[tauri::command]
pub async fn open_app_dir() -> CmdResult<()> {
let app_dir = wrap_err!(dirs::app_home_dir())?;
wrap_err!(open::that(app_dir))
let app_dir = dirs::app_home_dir().stringify_err()?;
open::that(app_dir).stringify_err()
}
/// 打开核心所在目录
#[tauri::command]
pub async fn open_core_dir() -> CmdResult<()> {
let core_dir = wrap_err!(tauri::utils::platform::current_exe())?;
let core_dir = tauri::utils::platform::current_exe().stringify_err()?;
let core_dir = core_dir.parent().ok_or("failed to get core dir")?;
wrap_err!(open::that(core_dir))
open::that(core_dir).stringify_err()
}
/// 打开日志目录
#[tauri::command]
pub async fn open_logs_dir() -> CmdResult<()> {
let log_dir = wrap_err!(dirs::app_logs_dir())?;
wrap_err!(open::that(log_dir))
let log_dir = dirs::app_logs_dir().stringify_err()?;
open::that(log_dir).stringify_err()
}
/// 打开网页链接
#[tauri::command]
pub fn open_web_url(url: String) -> CmdResult<()> {
wrap_err!(open::that(url))
open::that(url.as_str()).stringify_err()
}
// TODO 后续可以为前端提供接口,当前作为托盘菜单使用
/// 打开 Verge 最新日志
#[tauri::command]
pub async fn open_app_log() -> CmdResult<()> {
open::that(dirs::app_latest_log().stringify_err()?).stringify_err()
}
// TODO 后续可以为前端提供接口,当前作为托盘菜单使用
/// 打开 Clash 最新日志
#[tauri::command]
pub async fn open_core_log() -> CmdResult<()> {
open::that(dirs::clash_latest_log().stringify_err()?).stringify_err()
}
/// 打开/关闭开发者工具
@@ -61,43 +82,40 @@ pub async fn restart_app() -> CmdResult<()> {
/// 获取便携版标识
#[tauri::command]
pub fn get_portable_flag() -> CmdResult<bool> {
Ok(*dirs::PORTABLE_FLAG.get().unwrap_or(&false))
pub fn get_portable_flag() -> bool {
*dirs::PORTABLE_FLAG.get().unwrap_or(&false)
}
/// 获取应用目录
#[tauri::command]
pub fn get_app_dir() -> CmdResult<String> {
let app_home_dir = wrap_err!(dirs::app_home_dir())?
.to_string_lossy()
.to_string();
let app_home_dir = dirs::app_home_dir().stringify_err()?.to_string_lossy().into();
Ok(app_home_dir)
}
/// 获取当前自启动状态
#[tauri::command]
pub fn get_auto_launch_status() -> CmdResult<bool> {
use crate::core::sysopt::Sysopt;
wrap_err!(Sysopt::global().get_launch_status())
Sysopt::global().get_launch_status().stringify_err()
}
/// 下载图标缓存
#[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);
let icon_cache_dir = dirs::app_home_dir().stringify_err()?.join("icons").join("cache");
let icon_path = icon_cache_dir.join(name.as_str());
if icon_path.exists() {
return Ok(icon_path.to_string_lossy().to_string());
return Ok(icon_path.to_string_lossy().into());
}
if !icon_cache_dir.exists() {
let _ = std::fs::create_dir_all(&icon_cache_dir);
let _ = fs::create_dir_all(&icon_cache_dir).await;
}
let temp_path = icon_cache_dir.join(format!("{}.downloading", &name));
let temp_path = icon_cache_dir.join(format!("{}.downloading", name.as_str()));
let response = wrap_err!(reqwest::get(&url).await)?;
let response = reqwest::get(url.as_str()).await.stringify_err()?;
let content_type = response
.headers()
@@ -107,46 +125,44 @@ pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String>
let is_image = content_type.starts_with("image/");
let content = wrap_err!(response.bytes().await)?;
let content = response.bytes().await.stringify_err()?;
let is_html = content.len() > 15
&& (content.starts_with(b"<!DOCTYPE html")
|| content.starts_with(b"<html")
|| content.starts_with(b"<?xml"));
&& (content.starts_with(b"<!DOCTYPE html") || content.starts_with(b"<html") || content.starts_with(b"<?xml"));
if is_image && !is_html {
{
let mut file = match std::fs::File::create(&temp_path) {
let mut file = match fs::File::create(&temp_path).await {
Ok(file) => file,
Err(_) => {
if icon_path.exists() {
return Ok(icon_path.to_string_lossy().to_string());
return Ok(icon_path.to_string_lossy().into());
}
return Err("Failed to create temporary file".into());
}
};
wrap_err!(std::io::copy(&mut content.as_ref(), &mut file))?;
file.write_all(content.as_ref()).await.stringify_err()?;
file.flush().await.stringify_err()?;
}
if !icon_path.exists() {
match std::fs::rename(&temp_path, &icon_path) {
match fs::rename(&temp_path, &icon_path).await {
Ok(_) => {}
Err(_) => {
let _ = std::fs::remove_file(&temp_path);
let _ = temp_path.remove_if_exists().await;
if icon_path.exists() {
return Ok(icon_path.to_string_lossy().to_string());
return Ok(icon_path.to_string_lossy().into());
}
}
}
} else {
let _ = std::fs::remove_file(&temp_path);
let _ = temp_path.remove_if_exists().await;
}
Ok(icon_path.to_string_lossy().to_string())
Ok(icon_path.to_string_lossy().into())
} else {
let _ = std::fs::remove_file(&temp_path);
Err(format!("下载的内容不是有效图片: {url}"))
let _ = temp_path.remove_if_exists().await;
Err(format!("下载的内容不是有效图片: {}", url.as_str()).into())
}
}
@@ -159,87 +175,70 @@ pub struct IconInfo {
/// 复制图标文件
#[tauri::command]
pub fn copy_icon_file(path: String, icon_info: IconInfo) -> CmdResult<String> {
use std::{fs, path::Path};
pub async fn copy_icon_file(path: String, icon_info: IconInfo) -> CmdResult<String> {
let file_path = Path::new(path.as_str());
let file_path = Path::new(&path);
let icon_dir = wrap_err!(dirs::app_home_dir())?.join("icons");
let icon_dir = dirs::app_home_dir().stringify_err()?.join("icons");
if !icon_dir.exists() {
let _ = fs::create_dir_all(&icon_dir);
let _ = fs::create_dir_all(&icon_dir).await;
}
let ext = match file_path.extension() {
Some(e) => e.to_string_lossy().to_string(),
None => "ico".to_string(),
let ext: String = match file_path.extension() {
Some(e) => e.to_string_lossy().into(),
None => "ico".into(),
};
let dest_path = icon_dir.join(format!(
"{0}-{1}.{ext}",
icon_info.name, icon_info.current_t
icon_info.name.as_str(),
icon_info.current_t.as_str()
));
if file_path.exists() {
if icon_info.previous_t.trim() != "" {
fs::remove_file(
icon_dir.join(format!("{0}-{1}.png", icon_info.name, icon_info.previous_t)),
)
.unwrap_or_default();
fs::remove_file(
icon_dir.join(format!("{0}-{1}.ico", icon_info.name, icon_info.previous_t)),
)
.unwrap_or_default();
icon_dir
.join(format!(
"{0}-{1}.png",
icon_info.name.as_str(),
icon_info.previous_t.as_str()
))
.remove_if_exists()
.await
.unwrap_or_default();
icon_dir
.join(format!(
"{0}-{1}.ico",
icon_info.name.as_str(),
icon_info.previous_t.as_str()
))
.remove_if_exists()
.await
.unwrap_or_default();
}
logging!(
info,
Type::Cmd,
true,
"Copying icon file path: {:?} -> file dist: {:?}",
path,
dest_path
);
match fs::copy(file_path, &dest_path) {
Ok(_) => Ok(dest_path.to_string_lossy().to_string()),
Err(err) => Err(err.to_string()),
match fs::copy(file_path, &dest_path).await {
Ok(_) => Ok(dest_path.to_string_lossy().into()),
Err(err) => Err(err.to_string().into()),
}
} else {
Err("file not found".to_string())
Err("file not found".into())
}
}
/// 通知UI已准备就绪
#[tauri::command]
pub fn notify_ui_ready() -> CmdResult<()> {
log::info!(target: "app", "前端UI已准备就绪");
crate::utils::resolve::ui::mark_ui_ready();
Ok(())
pub fn notify_ui_ready() {
logging!(info, Type::Cmd, "前端UI已准备就绪");
ui::mark_ui_ready();
}
/// UI加载阶段
#[tauri::command]
pub fn update_ui_stage(stage: String) -> CmdResult<()> {
log::info!(target: "app", "UI加载阶段更新: {stage}");
use crate::utils::resolve::ui::UiReadyStage;
let stage_enum = match stage.as_str() {
"NotStarted" => UiReadyStage::NotStarted,
"Loading" => UiReadyStage::Loading,
"DomReady" => UiReadyStage::DomReady,
"ResourcesLoaded" => UiReadyStage::ResourcesLoaded,
"Ready" => UiReadyStage::Ready,
_ => {
log::warn!(target: "app", "未知的UI加载阶段: {stage}");
return Err(format!("未知的UI加载阶段: {stage}"));
}
};
crate::utils::resolve::ui::update_ui_ready_stage(stage_enum);
Ok(())
}
/// 重置UI就绪状态
#[tauri::command]
pub fn reset_ui_ready_state() -> CmdResult<()> {
log::info!(target: "app", "重置UI就绪状态");
crate::utils::resolve::ui::reset_ui_ready();
Ok(())
pub fn update_ui_stage(stage: UiReadyStage) {
logging!(info, Type::Cmd, "UI加载阶段更新: {:?}", &stage);
ui::update_ui_ready_stage(stage);
}
+34
View File
@@ -0,0 +1,34 @@
use super::CmdResult;
use crate::{cmd::StringifyErr as _, feat};
use feat::LocalBackupFile;
use smartstring::alias::String;
/// Create a local backup
#[tauri::command]
pub async fn create_local_backup() -> CmdResult<()> {
feat::create_local_backup().await.stringify_err()
}
/// List local backups
#[tauri::command]
pub async fn list_local_backup() -> CmdResult<Vec<LocalBackupFile>> {
feat::list_local_backup().await.stringify_err()
}
/// Delete local backup
#[tauri::command]
pub async fn delete_local_backup(filename: String) -> CmdResult<()> {
feat::delete_local_backup(filename).await.stringify_err()
}
/// Restore local backup
#[tauri::command]
pub async fn restore_local_backup(filename: String) -> CmdResult<()> {
feat::restore_local_backup(filename).await.stringify_err()
}
/// Export local backup to a user selected destination
#[tauri::command]
pub async fn export_local_backup(filename: String, destination: String) -> CmdResult<()> {
feat::export_local_backup(filename, destination).await.stringify_err()
}
+64 -436
View File
@@ -1,21 +1,17 @@
use super::CmdResult;
use crate::feat;
use crate::utils::dirs;
use crate::{
config::Config,
core::{handle, CoreManager},
};
use crate::{
config::*,
feat,
ipc::{self, IpcManager},
logging,
state::proxy::ProxyRequestCache,
utils::logging::Type,
wrap_err,
cmd::StringifyErr as _,
config::{ClashInfo, Config},
constants,
core::{CoreManager, handle, validate::CoreConfigValidator},
};
use clash_verge_logging::{Type, logging, logging_error};
use compact_str::CompactString;
use serde_yaml_ng::Mapping;
use std::time::Duration;
const CONFIG_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
use smartstring::alias::String;
use tokio::fs;
/// 复制Clash环境变量
#[tauri::command]
@@ -27,13 +23,13 @@ pub async fn copy_clash_env() -> CmdResult {
/// 获取Clash信息
#[tauri::command]
pub async fn get_clash_info() -> CmdResult<ClashInfo> {
Ok(Config::clash().await.latest_ref().get_client_info())
Ok(Config::clash().await.data_arc().get_client_info())
}
/// 修改Clash配置
#[tauri::command]
pub async fn patch_clash_config(payload: Mapping) -> CmdResult {
wrap_err!(feat::patch_clash(payload).await)
feat::patch_clash(&payload).await.stringify_err()
}
/// 修改Clash模式
@@ -48,35 +44,30 @@ pub async fn patch_clash_mode(payload: String) -> CmdResult {
pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>> {
logging!(info, Type::Config, "changing core to {clash_core}");
match CoreManager::global()
.change_core(Some(clash_core.clone()))
.await
{
match CoreManager::global().change_core(&clash_core).await {
Ok(_) => {
logging_error!(Type::Core, Config::profiles().await.latest_arc().save_file().await);
// 切换内核后重启内核
match CoreManager::global().restart_core().await {
Ok(_) => {
logging!(
info,
Type::Core,
"core changed and restarted to {clash_core}"
);
handle::Handle::notice_message("config_core::change_success", &clash_core);
logging!(info, Type::Core, "core changed and restarted to {clash_core}");
handle::Handle::notice_message("config_core::change_success", clash_core);
handle::Handle::refresh_clash();
Ok(None)
}
Err(err) => {
let error_msg = format!("Core changed but failed to restart: {err}");
let error_msg: String = format!("Core changed but failed to restart: {err}").into();
handle::Handle::notice_message("config_core::change_error", error_msg.clone());
logging!(error, Type::Core, "{error_msg}");
handle::Handle::notice_message("config_core::change_error", &error_msg);
Ok(Some(error_msg))
}
}
}
Err(err) => {
let error_msg = err.to_string();
let error_msg: String = err;
logging!(error, Type::Core, "failed to change core: {error_msg}");
handle::Handle::notice_message("config_core::change_error", &error_msg);
handle::Handle::notice_message("config_core::change_error", error_msg.clone());
Ok(Some(error_msg))
}
}
@@ -85,7 +76,7 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>>
/// 启动核心
#[tauri::command]
pub async fn start_core() -> CmdResult {
let result = wrap_err!(CoreManager::global().start_core().await);
let result = CoreManager::global().start_core().await.stringify_err();
if result.is_ok() {
handle::Handle::refresh_clash();
}
@@ -95,7 +86,8 @@ pub async fn start_core() -> CmdResult {
/// 关闭核心
#[tauri::command]
pub async fn stop_core() -> CmdResult {
let result = wrap_err!(CoreManager::global().stop_core().await);
logging_error!(Type::Core, Config::profiles().await.latest_arc().save_file().await);
let result = CoreManager::global().stop_core().await.stringify_err();
if result.is_ok() {
handle::Handle::refresh_clash();
}
@@ -105,34 +97,21 @@ pub async fn stop_core() -> CmdResult {
/// 重启核心
#[tauri::command]
pub async fn restart_core() -> CmdResult {
let result = wrap_err!(CoreManager::global().restart_core().await);
logging_error!(Type::Core, Config::profiles().await.latest_arc().save_file().await);
let result = CoreManager::global().restart_core().await.stringify_err();
if result.is_ok() {
handle::Handle::refresh_clash();
}
result
}
/// 获取代理延迟
#[tauri::command]
pub async fn clash_api_get_proxy_delay(
name: String,
url: Option<String>,
timeout: i32,
) -> CmdResult<serde_json::Value> {
wrap_err!(
IpcManager::global()
.test_proxy_delay(&name, url, timeout)
.await
)
}
/// 测试URL延迟
#[tauri::command]
pub async fn test_delay(url: String) -> CmdResult<u32> {
let result = match feat::test_delay(url).await {
Ok(delay) => delay,
Err(e) => {
log::error!(target: "app", "{}", e);
logging!(error, Type::Cmd, "{}", e);
10000u32
}
};
@@ -147,15 +126,11 @@ pub async fn save_dns_config(dns_config: Mapping) -> CmdResult {
use tokio::fs;
// 获取DNS配置文件路径
let dns_path = dirs::app_home_dir()
.map_err(|e| e.to_string())?
.join("dns_config.yaml");
let dns_path = dirs::app_home_dir().stringify_err()?.join(constants::files::DNS_CONFIG);
// 保存DNS配置到文件
let yaml_str = serde_yaml_ng::to_string(&dns_config).map_err(|e| e.to_string())?;
fs::write(&dns_path, yaml_str)
.await
.map_err(|e| e.to_string())?;
let yaml_str = serde_yaml_ng::to_string(&dns_config).stringify_err()?;
fs::write(&dns_path, yaml_str).await.stringify_err()?;
logging!(info, Type::Config, "DNS config saved to {dns_path:?}");
Ok(())
@@ -164,34 +139,23 @@ pub async fn save_dns_config(dns_config: Mapping) -> CmdResult {
/// 应用或撤销DNS配置
#[tauri::command]
pub async fn apply_dns_config(apply: bool) -> CmdResult {
use crate::{
config::Config,
core::{handle, CoreManager},
utils::dirs,
};
if apply {
// 读取DNS配置文件
let dns_path = dirs::app_home_dir()
.map_err(|e| e.to_string())?
.join("dns_config.yaml");
let dns_path = dirs::app_home_dir().stringify_err()?.join(constants::files::DNS_CONFIG);
if !dns_path.exists() {
logging!(warn, Type::Config, "DNS config file not found");
return Err("DNS config file not found".into());
}
let dns_yaml = tokio::fs::read_to_string(&dns_path).await.map_err(|e| {
let dns_yaml = fs::read_to_string(&dns_path).await.stringify_err_log(|e| {
logging!(error, Type::Config, "Failed to read DNS config: {e}");
e.to_string()
})?;
// 解析DNS配置
let patch_config =
serde_yaml_ng::from_str::<serde_yaml_ng::Mapping>(&dns_yaml).map_err(|e| {
logging!(error, Type::Config, "Failed to parse DNS config: {e}");
e.to_string()
})?;
let patch_config = serde_yaml_ng::from_str::<serde_yaml_ng::Mapping>(&dns_yaml).stringify_err_log(|e| {
logging!(error, Type::Config, "Failed to parse DNS config: {e}");
})?;
logging!(info, Type::Config, "Applying DNS config from file");
@@ -200,56 +164,41 @@ pub async fn apply_dns_config(apply: bool) -> CmdResult {
patch.insert("dns".into(), patch_config.into());
// 应用DNS配置到运行时配置
Config::runtime().await.draft_mut().patch_config(patch);
Config::runtime().await.edit_draft(|d| {
d.patch_config(&patch);
});
// 重新生成配置
Config::generate().await.map_err(|err| {
logging!(
error,
Type::Config,
"Failed to regenerate config with DNS: {err}"
);
"Failed to regenerate config with DNS".to_string()
Config::generate().await.stringify_err_log(|err| {
let err = format!("Failed to regenerate config with DNS: {err}");
logging!(error, Type::Config, "{err}");
})?;
// 应用新配置
CoreManager::global().update_config().await.map_err(|err| {
logging!(
error,
Type::Config,
"Failed to apply config with DNS: {err}"
);
"Failed to apply config with DNS".to_string()
CoreManager::global().update_config().await.stringify_err_log(|err| {
let err = format!("Failed to apply config with DNS: {err}");
logging!(error, Type::Config, "{err}");
})?;
logging!(info, Type::Config, "DNS config successfully applied");
handle::Handle::refresh_clash();
} else {
// 当关闭DNS设置时,重新生成配置(不加载DNS配置文件)
logging!(
info,
Type::Config,
"DNS settings disabled, regenerating config"
);
logging!(info, Type::Config, "DNS settings disabled, regenerating config");
Config::generate().await.map_err(|err| {
logging!(error, Type::Config, "Failed to regenerate config: {err}");
"Failed to regenerate config".to_string()
Config::generate().await.stringify_err_log(|err| {
let err = format!("Failed to regenerate config: {err}");
logging!(error, Type::Config, "{err}");
})?;
CoreManager::global().update_config().await.map_err(|err| {
logging!(
error,
Type::Config,
"Failed to apply regenerated config: {err}"
);
"Failed to apply regenerated config".to_string()
CoreManager::global().update_config().await.stringify_err_log(|err| {
let err = format!("Failed to apply regenerated config: {err}");
logging!(error, Type::Config, "{err}");
})?;
logging!(info, Type::Config, "Config regenerated successfully");
handle::Handle::refresh_clash();
}
handle::Handle::refresh_clash();
Ok(())
}
@@ -258,9 +207,7 @@ pub async fn apply_dns_config(apply: bool) -> CmdResult {
pub fn check_dns_config_exists() -> CmdResult<bool> {
use crate::utils::dirs;
let dns_path = dirs::app_home_dir()
.map_err(|e| e.to_string())?
.join("dns_config.yaml");
let dns_path = dirs::app_home_dir().stringify_err()?.join(constants::files::DNS_CONFIG);
Ok(dns_path.exists())
}
@@ -271,353 +218,34 @@ pub async fn get_dns_config_content() -> CmdResult<String> {
use crate::utils::dirs;
use tokio::fs;
let dns_path = dirs::app_home_dir()
.map_err(|e| e.to_string())?
.join("dns_config.yaml");
let dns_path = dirs::app_home_dir().stringify_err()?.join(constants::files::DNS_CONFIG);
if !fs::try_exists(&dns_path).await.map_err(|e| e.to_string())? {
if !fs::try_exists(&dns_path).await.stringify_err()? {
return Err("DNS config file not found".into());
}
let content = fs::read_to_string(&dns_path)
.await
.map_err(|e| e.to_string())?;
let content = fs::read_to_string(&dns_path).await.stringify_err()?.into();
Ok(content)
}
/// 验证DNS配置文件
#[tauri::command]
pub async fn validate_dns_config() -> CmdResult<(bool, String)> {
use crate::{core::CoreManager, utils::dirs};
let app_dir = dirs::app_home_dir().map_err(|e| e.to_string())?;
let dns_path = app_dir.join("dns_config.yaml");
let app_dir = dirs::app_home_dir().stringify_err()?;
let dns_path = app_dir.join(constants::files::DNS_CONFIG);
let dns_path_str = dns_path.to_str().unwrap_or_default();
if !dns_path.exists() {
return Ok((false, "DNS config file not found".to_string()));
return Ok((false, "DNS config file not found".into()));
}
match CoreManager::global()
.validate_config_file(dns_path_str, None)
CoreConfigValidator::validate_config_file(dns_path_str, None)
.await
{
Ok(result) => Ok(result),
Err(e) => Err(e.to_string()),
}
.stringify_err()
}
/// 获取Clash版本信息
#[tauri::command]
pub async fn get_clash_version() -> CmdResult<serde_json::Value> {
wrap_err!(IpcManager::global().get_version().await)
}
/// 获取Clash配置
#[tauri::command]
pub async fn get_clash_config() -> CmdResult<serde_json::Value> {
let manager = IpcManager::global();
let cache = ProxyRequestCache::global();
let key = ProxyRequestCache::make_key("clash_config", "default");
let value = cache
.get_or_fetch(key, CONFIG_REFRESH_INTERVAL, || async {
manager.get_config().await.unwrap_or_else(|e| {
logging!(error, Type::Cmd, "Failed to fetch clash config: {e}");
serde_json::Value::Object(serde_json::Map::new())
})
})
.await;
Ok((*value).clone())
}
/// 强制刷新Clash配置缓存
#[tauri::command]
pub async fn force_refresh_clash_config() -> CmdResult<serde_json::Value> {
let cache = ProxyRequestCache::global();
let key = ProxyRequestCache::make_key("clash_config", "default");
cache.map.remove(&key);
get_clash_config().await
}
/// 更新地理数据
#[tauri::command]
pub async fn update_geo_data() -> CmdResult {
wrap_err!(IpcManager::global().update_geo_data().await)
}
/// 升级Clash核心
#[tauri::command]
pub async fn upgrade_clash_core() -> CmdResult {
wrap_err!(IpcManager::global().upgrade_core().await)
}
/// 获取规则
#[tauri::command]
pub async fn get_clash_rules() -> CmdResult<serde_json::Value> {
wrap_err!(IpcManager::global().get_rules().await)
}
/// 更新代理选择
#[tauri::command]
pub async fn update_proxy_choice(group: String, proxy: String) -> CmdResult {
wrap_err!(IpcManager::global().update_proxy(&group, &proxy).await)
}
/// 获取代理提供者
#[tauri::command]
pub async fn get_proxy_providers() -> CmdResult<serde_json::Value> {
wrap_err!(IpcManager::global().get_providers_proxies().await)
}
/// 获取规则提供者
#[tauri::command]
pub async fn get_rule_providers() -> CmdResult<serde_json::Value> {
wrap_err!(IpcManager::global().get_rule_providers().await)
}
/// 代理提供者健康检查
#[tauri::command]
pub async fn proxy_provider_health_check(name: String) -> CmdResult {
wrap_err!(
IpcManager::global()
.proxy_provider_health_check(&name)
.await
)
}
/// 更新代理提供者
#[tauri::command]
pub async fn update_proxy_provider(name: String) -> CmdResult {
wrap_err!(IpcManager::global().update_proxy_provider(&name).await)
}
/// 更新规则提供者
#[tauri::command]
pub async fn update_rule_provider(name: String) -> CmdResult {
wrap_err!(IpcManager::global().update_rule_provider(&name).await)
}
/// 获取连接
#[tauri::command]
pub async fn get_clash_connections() -> CmdResult<serde_json::Value> {
wrap_err!(IpcManager::global().get_connections().await)
}
/// 删除连接
#[tauri::command]
pub async fn delete_clash_connection(id: String) -> CmdResult {
wrap_err!(IpcManager::global().delete_connection(&id).await)
}
/// 关闭所有连接
#[tauri::command]
pub async fn close_all_clash_connections() -> CmdResult {
wrap_err!(IpcManager::global().close_all_connections().await)
}
/// 获取流量数据 (使用新的IPC流式监控)
#[tauri::command]
pub async fn get_traffic_data() -> CmdResult<serde_json::Value> {
let traffic = crate::ipc::get_current_traffic().await;
let result = serde_json::json!({
"up": traffic.total_up,
"down": traffic.total_down,
"up_rate": traffic.up_rate,
"down_rate": traffic.down_rate,
"last_updated": traffic.last_updated.elapsed().as_secs()
});
Ok(result)
}
/// 获取内存数据 (使用新的IPC流式监控)
#[tauri::command]
pub async fn get_memory_data() -> CmdResult<serde_json::Value> {
let memory = crate::ipc::get_current_memory().await;
let usage_percent = if memory.oslimit > 0 {
(memory.inuse as f64 / memory.oslimit as f64) * 100.0
} else {
0.0
};
let result = serde_json::json!({
"inuse": memory.inuse,
"oslimit": memory.oslimit,
"usage_percent": usage_percent,
"last_updated": memory.last_updated.elapsed().as_secs()
});
Ok(result)
}
/// 启动流量监控服务 (IPC流式监控自动启动,此函数为兼容性保留)
#[tauri::command]
pub async fn start_traffic_service() -> CmdResult {
logging!(trace, Type::Ipc, "启动流量监控服务 (IPC流式监控)");
// 新的IPC监控在首次访问时自动启动
// 触发一次访问以确保监控器已初始化
let _ = crate::ipc::get_current_traffic().await;
let _ = crate::ipc::get_current_memory().await;
logging!(info, Type::Ipc, "IPC流式监控已激活");
Ok(())
}
/// 停止流量监控服务 (IPC流式监控无需显式停止,此函数为兼容性保留)
#[tauri::command]
pub async fn stop_traffic_service() -> CmdResult {
logging!(trace, Type::Ipc, "停止流量监控服务请求 (IPC流式监控)");
// 新的IPC监控是持久的,无需显式停止
logging!(info, Type::Ipc, "IPC流式监控继续运行");
Ok(())
}
/// 获取格式化的流量数据 (包含单位,便于前端显示)
#[tauri::command]
pub async fn get_formatted_traffic_data() -> CmdResult<serde_json::Value> {
logging!(trace, Type::Ipc, "获取格式化流量数据");
let (up_rate, down_rate, total_up, total_down, is_fresh) =
crate::ipc::get_formatted_traffic().await;
let result = serde_json::json!({
"up_rate_formatted": up_rate,
"down_rate_formatted": down_rate,
"total_up_formatted": total_up,
"total_down_formatted": total_down,
"is_fresh": is_fresh
});
logging!(
debug,
Type::Ipc,
"格式化流量数据: ↑{up_rate}/s ↓{down_rate}/s (总计: ↑{total_up} ↓{total_down})"
);
Ok(result)
}
/// 获取格式化的内存数据 (包含单位,便于前端显示)
#[tauri::command]
pub async fn get_formatted_memory_data() -> CmdResult<serde_json::Value> {
logging!(info, Type::Ipc, "获取格式化内存数据");
let (inuse, oslimit, usage_percent, is_fresh) = crate::ipc::get_formatted_memory().await;
let result = serde_json::json!({
"inuse_formatted": inuse,
"oslimit_formatted": oslimit,
"usage_percent": usage_percent,
"is_fresh": is_fresh
});
logging!(
debug,
Type::Ipc,
"格式化内存数据: {inuse} / {oslimit} ({usage_percent:.1}%)"
);
Ok(result)
}
/// 获取系统监控概览 (流量+内存,便于前端一次性获取所有状态)
#[tauri::command]
pub async fn get_system_monitor_overview() -> CmdResult<serde_json::Value> {
logging!(debug, Type::Ipc, "获取系统监控概览");
// 并发获取流量和内存数据
let (traffic, memory) = tokio::join!(
crate::ipc::get_current_traffic(),
crate::ipc::get_current_memory()
);
let (traffic_formatted, memory_formatted) = tokio::join!(
crate::ipc::get_formatted_traffic(),
crate::ipc::get_formatted_memory()
);
let traffic_is_fresh = traffic.last_updated.elapsed().as_secs() < 5;
let memory_is_fresh = memory.last_updated.elapsed().as_secs() < 10;
let result = serde_json::json!({
"traffic": {
"raw": {
"up": traffic.total_up,
"down": traffic.total_down,
"up_rate": traffic.up_rate,
"down_rate": traffic.down_rate
},
"formatted": {
"up_rate": traffic_formatted.0,
"down_rate": traffic_formatted.1,
"total_up": traffic_formatted.2,
"total_down": traffic_formatted.3
},
"is_fresh": traffic_is_fresh
},
"memory": {
"raw": {
"inuse": memory.inuse,
"oslimit": memory.oslimit,
"usage_percent": if memory.oslimit > 0 {
(memory.inuse as f64 / memory.oslimit as f64) * 100.0
} else {
0.0
}
},
"formatted": {
"inuse": memory_formatted.0,
"oslimit": memory_formatted.1,
"usage_percent": memory_formatted.2
},
"is_fresh": memory_is_fresh
},
"overall_status": if traffic_is_fresh && memory_is_fresh { "healthy" } else { "stale" }
});
Ok(result)
}
/// 获取代理组延迟
#[tauri::command]
pub async fn get_group_proxy_delays(
group_name: String,
url: Option<String>,
timeout: Option<i32>,
) -> CmdResult<serde_json::Value> {
wrap_err!(
IpcManager::global()
.get_group_proxy_delays(&group_name, url, timeout.unwrap_or(10000))
.await
)
}
/// 检查调试是否启用
#[tauri::command]
pub async fn is_clash_debug_enabled() -> CmdResult<bool> {
match IpcManager::global().is_debug_enabled().await {
Ok(enabled) => Ok(enabled),
Err(_) => Ok(false),
}
}
/// 垃圾回收
#[tauri::command]
pub async fn clash_gc() -> CmdResult {
wrap_err!(IpcManager::global().gc().await)
}
/// 获取日志 (使用新的流式实现)
#[tauri::command]
pub async fn get_clash_logs() -> CmdResult<serde_json::Value> {
Ok(ipc::get_logs_json().await)
}
/// 启动日志监控
#[tauri::command]
pub async fn start_logs_monitoring(level: Option<String>) -> CmdResult {
ipc::start_logs_monitoring(level).await;
Ok(())
}
/// 停止日志监控
#[tauri::command]
pub async fn stop_logs_monitoring() -> CmdResult {
ipc::stop_logs_monitoring().await;
Ok(())
}
/// 清除日志
#[tauri::command]
pub async fn clear_logs() -> CmdResult {
ipc::clear_logs().await;
Ok(())
pub async fn get_clash_logs() -> CmdResult<Vec<CompactString>> {
let logs = CoreManager::global().get_clash_logs().await.unwrap_or_default();
Ok(logs)
}
File diff suppressed because it is too large Load Diff

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