Compare commits

...

7 Commits

Author SHA1 Message Date
renovate[bot]
a4c537541e
chore(deps): update rust crate rust-i18n to v4 (#6784)
* chore(deps): update rust crate rust-i18n to v4

* fix: migrate rust-i18n to v4 with Cow-first zero-copy approach

- Adapt to v4 breaking changes: available_locales!() returns Vec<Cow<'static, str>>
- Cache locales in LazyLock<Vec<Cow<'static, str>>> to avoid repeated Vec alloc + sort
- Propagate Cow<'static, str> through resolve/current/system_language APIs
- Fix t! macro args branch: into_owned() + Cow::Owned for type correctness
- Eliminate double resolve in sync_locale (skip redundant set_locale indirection)
- Replace .to_string() with .into_owned() / Cow passthrough in updater.rs

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
2026-04-12 11:11:16 +00:00
renovate[bot]
9e32fba13e
chore(deps): update github actions (#6774)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-12 10:15:18 +00:00
renovate[bot]
7a5c314d89
chore(deps): update npm dependencies to v19.2.5 (#6762)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-12 10:15:00 +00:00
HuangTao
c358b917d6
为项目添加 github 的 Provenance 机制 (#6633)
* ci: add github provenance attestations

* ci: disable updater metadata in dev workflow

* ci: add provenance smoke test workflow

* build: fallback to alpha release assets api

* ci: remove signing env from dev workflow

* ci: disable updater artifacts in linux dev validation

* ci: support alpha manual trigger tag input

* ci: remove provenance validation scaffolding

* ci: drop redundant provenance job permissions

* ci: limit provenance to release workflow
2026-04-12 09:50:44 +00:00
Tunglies
749b6c9e30
feat(script): convert script execution to async and add timeout handling 2026-04-12 11:20:28 +08:00
Tunglies
e6a88cf9c9
refactor: improve service manager initialization by reducing lock duration 2026-04-12 11:15:34 +08:00
Tunglies
0f41f1bc8d
refactor: optimize deep_merge function using iterative stack approach 2026-04-12 11:13:17 +08:00
18 changed files with 291 additions and 233 deletions

View File

@ -38,7 +38,7 @@ jobs:
run: bash ./scripts/extract_update_logs.sh
shell: bash
- uses: pnpm/action-setup@v5.0.0
- uses: pnpm/action-setup@v6.0.0
name: Install pnpm
with:
run_install: false
@ -102,7 +102,7 @@ jobs:
EOF
- name: Upload Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ env.TAG_NAME }}
name: 'Clash Verge Rev ${{ env.TAG_CHANNEL }}'
@ -179,7 +179,7 @@ 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@v5.0.0
- uses: pnpm/action-setup@v6.0.0
name: Install pnpm
with:
run_install: false
@ -278,7 +278,7 @@ jobs:
cache-workspace-crates: true
- name: Install pnpm
uses: pnpm/action-setup@v5.0.0
uses: pnpm/action-setup@v6.0.0
with:
run_install: false
@ -379,7 +379,7 @@ jobs:
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
- name: Upload Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ env.TAG_NAME }}
name: 'Clash Verge Rev ${{ env.TAG_CHANNEL }}'
@ -423,7 +423,7 @@ jobs:
cache-workspace-crates: true
- name: Install pnpm
uses: pnpm/action-setup@v5.0.0
uses: pnpm/action-setup@v6.0.0
with:
run_install: false
@ -497,7 +497,7 @@ jobs:
}
- name: Upload Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ env.TAG_NAME }}
name: 'Clash Verge Rev ${{ env.TAG_CHANNEL }}'
@ -534,7 +534,7 @@ jobs:
with:
node-version: '24.14.1'
- uses: pnpm/action-setup@v5.0.0
- uses: pnpm/action-setup@v6.0.0
name: Install pnpm
with:
run_install: false

View File

@ -45,7 +45,7 @@ jobs:
with:
node-version: '24.14.1'
- uses: pnpm/action-setup@v5
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false

View File

@ -93,7 +93,7 @@ jobs:
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@v5
- uses: pnpm/action-setup@v6
name: Install pnpm
if: github.event.inputs[matrix.input] == 'true'
with:

View File

@ -40,7 +40,7 @@ jobs:
- name: Install pnpm
if: steps.check_frontend.outputs.frontend == 'true'
uses: pnpm/action-setup@v5
uses: pnpm/action-setup@v6
with:
run_install: false

View File

@ -82,7 +82,7 @@ jobs:
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@ -104,7 +104,7 @@ jobs:
sparse-checkout-cone-mode: true
fetch-depth: 1
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
GH_AW_WORKFLOW_FILE: "pr-ai-slop-review.lock.yml"
with:
@ -115,7 +115,7 @@ jobs:
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@ -186,7 +186,7 @@ jobs:
GH_AW_PROMPT_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
@ -196,7 +196,7 @@ jobs:
const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_GITHUB_ACTOR: ${{ github.actor }}
@ -305,7 +305,7 @@ jobs:
id: checkout-pr
if: |
github.event.pull_request || github.event.issue.pull_request
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
with:
@ -653,7 +653,7 @@ jobs:
bash ${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@ -677,7 +677,7 @@ jobs:
- name: Ingest agent output
id: collect_output
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
@ -692,7 +692,7 @@ jobs:
await main();
- name: Parse agent logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
@ -703,7 +703,7 @@ jobs:
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@ -776,7 +776,7 @@ jobs:
ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- name: Setup threat detection
if: always() && steps.detection_guard.outputs.run_detection == 'true'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
WORKFLOW_NAME: "PR AI Slop Review"
WORKFLOW_DESCRIPTION: "Reviews incoming pull requests for missing issue linkage and high-confidence\nsigns of one-shot AI-generated changes, then posts a maintainer-focused\ncomment when the risk is high enough to warrant follow-up."
@ -832,7 +832,7 @@ jobs:
- name: Parse threat detection results
id: parse_detection_results
if: always() && steps.detection_guard.outputs.run_detection == 'true'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
@ -906,7 +906,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV"
- name: Process No-Op Messages
id: noop
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
GH_AW_NOOP_MAX: "1"
@ -920,7 +920,7 @@ jobs:
await main();
- name: Record Missing Tool
id: missing_tool
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
GH_AW_WORKFLOW_NAME: "PR AI Slop Review"
@ -934,7 +934,7 @@ jobs:
- name: Handle Agent Failure
id: handle_agent_failure
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
GH_AW_WORKFLOW_NAME: "PR AI Slop Review"
@ -957,7 +957,7 @@ jobs:
await main();
- name: Handle No-Op Message
id: handle_noop_message
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
GH_AW_WORKFLOW_NAME: "PR AI Slop Review"
@ -1025,7 +1025,7 @@ jobs:
echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
- name: Process Safe Outputs
id: process_safe_outputs
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"

View File

@ -126,7 +126,7 @@ jobs:
EOF
- name: Upload Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ env.TAG_NAME }}
name: 'Clash Verge Rev ${{ env.TAG_NAME }}'
@ -199,7 +199,7 @@ jobs:
with:
node-version: '24.14.1'
- uses: pnpm/action-setup@v5
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
@ -240,6 +240,26 @@ jobs:
args: --target ${{ matrix.target }}
includeUpdaterJson: true
- name: Attest Windows bundles
if: matrix.os == 'windows-latest'
uses: actions/attest-build-provenance@v4
with:
subject-path: target/${{ matrix.target }}/release/bundle/nsis/*setup*
- name: Attest macOS bundles
if: matrix.os == 'macos-latest'
uses: actions/attest-build-provenance@v4
with:
subject-path: target/${{ matrix.target }}/release/bundle/dmg/*.dmg
- name: Attest Linux bundles
if: matrix.os == 'ubuntu-22.04'
uses: actions/attest-build-provenance@v4
with:
subject-path: |
target/${{ matrix.target }}/release/bundle/deb/*.deb
target/${{ matrix.target }}/release/bundle/rpm/*.rpm
release-for-linux-arm:
name: Release Build for Linux ARM
needs: [check_tag_version]
@ -284,7 +304,7 @@ jobs:
node-version: '24.14.1'
- name: Install pnpm
uses: pnpm/action-setup@v5
uses: pnpm/action-setup@v6
with:
run_install: false
@ -367,8 +387,15 @@ jobs:
echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
- name: Attest Linux bundles
uses: actions/attest-build-provenance@v4
with:
subject-path: |
target/${{ matrix.target }}/release/bundle/deb/*.deb
target/${{ matrix.target }}/release/bundle/rpm/*.rpm
- name: Upload Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
tag_name: v${{env.VERSION}}
name: 'Clash Verge Rev v${{env.VERSION}}'
@ -422,7 +449,7 @@ jobs:
with:
node-version: '24.14.1'
- uses: pnpm/action-setup@v5
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
@ -478,8 +505,13 @@ jobs:
Rename-Item $file.FullName $newName
}
- name: Attest Windows bundles
uses: actions/attest-build-provenance@v4
with:
subject-path: target/${{ matrix.target }}/release/bundle/nsis/*setup*
- name: Upload Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
tag_name: v${{steps.build.outputs.appVersion}}
name: 'Clash Verge Rev v${{steps.build.outputs.appVersion}}'
@ -507,7 +539,7 @@ jobs:
with:
node-version: '24.14.1'
- uses: pnpm/action-setup@v5
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
@ -533,7 +565,7 @@ jobs:
with:
node-version: '24.14.1'
- uses: pnpm/action-setup@v5
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
@ -595,7 +627,7 @@ jobs:
with:
node-version: '24.14.1'
- uses: pnpm/action-setup@v5
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false

View File

@ -36,7 +36,7 @@ jobs:
with:
node-version: '24.14.1'
- uses: pnpm/action-setup@v5
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false

View File

@ -17,7 +17,7 @@ jobs:
with:
node-version: '24.14.1'
- uses: pnpm/action-setup@v5
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
@ -41,7 +41,7 @@ jobs:
with:
node-version: '24.14.1'
- uses: pnpm/action-setup@v5
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false

15
Cargo.lock generated
View File

@ -6261,12 +6261,11 @@ dependencies = [
[[package]]
name = "rust-i18n"
version = "3.1.5"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fda2551fdfaf6cc5ee283adc15e157047b92ae6535cf80f6d4962d05717dc332"
checksum = "21031bf5e6f2c0ae745d831791c403608e99a8bd3776c7e5e5535acd70c3b7ba"
dependencies = [
"globwalk",
"once_cell",
"regex",
"rust-i18n-macro",
"rust-i18n-support",
@ -6275,12 +6274,11 @@ dependencies = [
[[package]]
name = "rust-i18n-macro"
version = "3.1.5"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22baf7d7f56656d23ebe24f6bb57a5d40d2bce2a5f1c503e692b5b2fa450f965"
checksum = "51fe5295763b358606f7ca26a564e20f4469775a57ec1f09431249a33849ff52"
dependencies = [
"glob",
"once_cell",
"proc-macro2",
"quote",
"rust-i18n-support",
@ -6292,9 +6290,9 @@ dependencies = [
[[package]]
name = "rust-i18n-support"
version = "3.1.5"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940ed4f52bba4c0152056d771e563b7133ad9607d4384af016a134b58d758f19"
checksum = "69bcc115c8eea2803aa3d85362e339776f4988a0349f2f475af572e497443f6f"
dependencies = [
"arc-swap",
"base62",
@ -6302,7 +6300,6 @@ dependencies = [
"itertools 0.11.0",
"lazy_static",
"normpath",
"once_cell",
"proc-macro2",
"regex",
"serde",

View File

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
rust-i18n = "3.1.5"
rust-i18n = "4.0.0"
sys-locale = "0.3.2"
[lints]

View File

@ -1,8 +1,12 @@
use rust_i18n::i18n;
use std::borrow::Cow;
use std::sync::LazyLock;
const DEFAULT_LANGUAGE: &str = "zh";
i18n!("locales", fallback = "zh");
static SUPPORTED_LOCALES: LazyLock<Vec<Cow<'static, str>>> = LazyLock::new(|| rust_i18n::available_locales!());
#[inline]
fn locale_alias(locale: &str) -> Option<&'static str> {
match locale {
@ -14,54 +18,51 @@ fn locale_alias(locale: &str) -> Option<&'static str> {
}
#[inline]
fn resolve_supported_language(language: &str) -> Option<&'static str> {
fn resolve_supported_language(language: &str) -> Option<Cow<'static, str>> {
if language.is_empty() {
return None;
}
let normalized = language.to_lowercase().replace('_', "-");
let segments: Vec<&str> = normalized.split('-').collect();
let supported = rust_i18n::available_locales!();
for i in (1..=segments.len()).rev() {
let prefix = segments[..i].join("-");
if let Some(alias) = locale_alias(&prefix)
&& let Some(&found) = supported.iter().find(|&&l| l.eq_ignore_ascii_case(alias))
&& let Some(found) = SUPPORTED_LOCALES.iter().find(|l| l.eq_ignore_ascii_case(alias))
{
return Some(found);
return Some(found.clone());
}
if let Some(&found) = supported.iter().find(|&&l| l.eq_ignore_ascii_case(&prefix)) {
return Some(found);
if let Some(found) = SUPPORTED_LOCALES.iter().find(|l| l.eq_ignore_ascii_case(&prefix)) {
return Some(found.clone());
}
}
None
}
#[inline]
fn current_language(language: Option<&str>) -> &str {
fn current_language(language: Option<&str>) -> Cow<'static, str> {
language
.as_ref()
.filter(|lang| !lang.is_empty())
.and_then(|lang| resolve_supported_language(lang))
.and_then(resolve_supported_language)
.unwrap_or_else(system_language)
}
#[inline]
pub fn system_language() -> &'static str {
pub fn system_language() -> Cow<'static, str> {
sys_locale::get_locale()
.as_deref()
.and_then(resolve_supported_language)
.unwrap_or(DEFAULT_LANGUAGE)
.unwrap_or(Cow::Borrowed(DEFAULT_LANGUAGE))
}
#[inline]
pub fn sync_locale(language: Option<&str>) {
let language = current_language(language);
set_locale(language);
rust_i18n::set_locale(&current_language(language));
}
#[inline]
pub fn set_locale(language: &str) {
let lang = resolve_supported_language(language).unwrap_or(DEFAULT_LANGUAGE);
rust_i18n::set_locale(lang);
let lang = resolve_supported_language(language).unwrap_or(Cow::Borrowed(DEFAULT_LANGUAGE));
rust_i18n::set_locale(&lang);
}
#[inline]
@ -76,11 +77,11 @@ macro_rules! t {
};
($key:expr, $($arg_name:ident = $arg_value:expr),*) => {
{
let mut _text = $crate::translate(&$key);
let mut _text = $crate::translate(&$key).into_owned();
$(
_text = _text.replace(&format!("{{{}}}", stringify!($arg_name)), &$arg_value);
)*
_text
::std::borrow::Cow::<'static, str>::Owned(_text)
}
};
}
@ -91,13 +92,13 @@ mod test {
#[test]
fn test_resolve_supported_language() {
assert_eq!(resolve_supported_language("en"), Some("en"));
assert_eq!(resolve_supported_language("en-US"), Some("en"));
assert_eq!(resolve_supported_language("zh"), Some("zh"));
assert_eq!(resolve_supported_language("zh-CN"), Some("zh"));
assert_eq!(resolve_supported_language("zh-Hant"), Some("zhtw"));
assert_eq!(resolve_supported_language("jp"), Some("jp"));
assert_eq!(resolve_supported_language("ja-JP"), Some("jp"));
assert_eq!(resolve_supported_language("en").as_deref(), Some("en"));
assert_eq!(resolve_supported_language("en-US").as_deref(), Some("en"));
assert_eq!(resolve_supported_language("zh").as_deref(), Some("zh"));
assert_eq!(resolve_supported_language("zh-CN").as_deref(), Some("zh"));
assert_eq!(resolve_supported_language("zh-Hant").as_deref(), Some("zhtw"));
assert_eq!(resolve_supported_language("jp").as_deref(), Some("jp"));
assert_eq!(resolve_supported_language("ja-JP").as_deref(), Some("jp"));
assert_eq!(resolve_supported_language("fr"), None);
}
}

View File

@ -67,8 +67,8 @@
"monaco-editor": "^0.55.1",
"monaco-yaml": "^5.4.1",
"nanoid": "^5.1.7",
"react": "19.2.4",
"react-dom": "19.2.4",
"react": "19.2.5",
"react-dom": "19.2.5",
"react-error-boundary": "6.1.1",
"react-hook-form": "^7.72.0",
"react-i18next": "17.0.2",

246
pnpm-lock.yaml generated
View File

@ -10,43 +10,43 @@ importers:
dependencies:
'@dnd-kit/core':
specifier: ^6.3.1
version: 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
version: 6.3.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
'@dnd-kit/sortable':
specifier: ^10.0.0
version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5)
'@dnd-kit/utilities':
specifier: ^3.2.2
version: 3.2.2(react@19.2.4)
version: 3.2.2(react@19.2.5)
'@emotion/react':
specifier: ^11.14.0
version: 11.14.0(@types/react@19.2.14)(react@19.2.4)
version: 11.14.0(@types/react@19.2.14)(react@19.2.5)
'@emotion/styled':
specifier: ^11.14.1
version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)
version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)
'@juggle/resize-observer':
specifier: ^3.4.0
version: 3.4.0
'@monaco-editor/react':
specifier: ^4.7.0
version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
'@mui/icons-material':
specifier: ^7.3.9
version: 7.3.9(@mui/material@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)
version: 7.3.9(@mui/material@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)
'@mui/lab':
specifier: 7.0.0-beta.17
version: 7.0.0-beta.17(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@mui/material@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
version: 7.0.0-beta.17(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@mui/material@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
'@mui/material':
specifier: ^7.3.9
version: 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
version: 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
'@tanstack/react-query':
specifier: ^5.96.1
version: 5.96.2(react@19.2.4)
version: 5.96.2(react@19.2.5)
'@tanstack/react-table':
specifier: ^8.21.3
version: 8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
version: 8.21.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
'@tanstack/react-virtual':
specifier: ^3.13.23
version: 3.13.23(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
version: 3.13.23(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
'@tauri-apps/api':
specifier: 2.10.1
version: 2.10.1
@ -73,7 +73,7 @@ importers:
version: 2.10.1
ahooks:
specifier: ^3.9.6
version: 3.9.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
version: 3.9.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
cidr-block:
specifier: ^2.3.0
version: 2.3.0
@ -82,7 +82,7 @@ importers:
version: 1.11.20
foxact:
specifier: ^0.3.0
version: 0.3.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
version: 0.3.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
foxts:
specifier: ^5.3.0
version: 5.4.0
@ -108,26 +108,26 @@ importers:
specifier: ^5.1.7
version: 5.1.7
react:
specifier: 19.2.4
version: 19.2.4
specifier: 19.2.5
version: 19.2.5
react-dom:
specifier: 19.2.4
version: 19.2.4(react@19.2.4)
specifier: 19.2.5
version: 19.2.5(react@19.2.5)
react-error-boundary:
specifier: 6.1.1
version: 6.1.1(react@19.2.4)
version: 6.1.1(react@19.2.5)
react-hook-form:
specifier: ^7.72.0
version: 7.72.1(react@19.2.4)
version: 7.72.1(react@19.2.5)
react-i18next:
specifier: 17.0.2
version: 17.0.2(i18next@26.0.3(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2)
version: 17.0.2(i18next@26.0.3(typescript@6.0.2))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@6.0.2)
react-markdown:
specifier: 10.1.0
version: 10.1.0(@types/react@19.2.14)(react@19.2.4)
version: 10.1.0(@types/react@19.2.14)(react@19.2.5)
react-router:
specifier: ^7.13.1
version: 7.14.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
version: 7.14.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
rehype-raw:
specifier: ^7.0.0
version: 7.0.0
@ -3101,10 +3101,10 @@ packages:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
react-dom@19.2.4:
resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==}
react-dom@19.2.5:
resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==}
peerDependencies:
react: ^19.2.4
react: ^19.2.5
react-error-boundary@6.1.1:
resolution: {integrity: sha512-BrYwPOdXi5mqkk5lw+Uvt0ThHx32rCt3BkukS4X23A2AIWDPSGX6iaWTc0y9TU/mHDA/6qOSGel+B2ERkOvD1w==}
@ -3164,8 +3164,8 @@ packages:
react: '>=16.6.0'
react-dom: '>=16.6.0'
react@19.2.4:
resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==}
react@19.2.5:
resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==}
engines: {node: '>=0.10.0'}
readdirp@4.1.2:
@ -4331,29 +4331,29 @@ snapshots:
'@biomejs/cli-win32-x64@2.4.10':
optional: true
'@dnd-kit/accessibility@3.1.1(react@19.2.4)':
'@dnd-kit/accessibility@3.1.1(react@19.2.5)':
dependencies:
react: 19.2.4
react: 19.2.5
tslib: 2.8.1
'@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
'@dnd-kit/core@6.3.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
dependencies:
'@dnd-kit/accessibility': 3.1.1(react@19.2.4)
'@dnd-kit/utilities': 3.2.2(react@19.2.4)
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
'@dnd-kit/accessibility': 3.1.1(react@19.2.5)
'@dnd-kit/utilities': 3.2.2(react@19.2.5)
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
tslib: 2.8.1
'@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)':
'@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5)':
dependencies:
'@dnd-kit/core': 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@dnd-kit/utilities': 3.2.2(react@19.2.4)
react: 19.2.4
'@dnd-kit/core': 6.3.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
'@dnd-kit/utilities': 3.2.2(react@19.2.5)
react: 19.2.5
tslib: 2.8.1
'@dnd-kit/utilities@3.2.2(react@19.2.4)':
'@dnd-kit/utilities@3.2.2(react@19.2.5)':
dependencies:
react: 19.2.4
react: 19.2.5
tslib: 2.8.1
'@emnapi/core@1.9.2':
@ -4404,17 +4404,17 @@ snapshots:
'@emotion/memoize@0.9.0': {}
'@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4)':
'@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5)':
dependencies:
'@babel/runtime': 7.29.2
'@emotion/babel-plugin': 11.13.5
'@emotion/cache': 11.14.0
'@emotion/serialize': 1.3.3
'@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.4)
'@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.5)
'@emotion/utils': 1.4.2
'@emotion/weak-memoize': 0.4.0
hoist-non-react-statics: 3.3.2
react: 19.2.4
react: 19.2.5
optionalDependencies:
'@types/react': 19.2.14
transitivePeerDependencies:
@ -4430,16 +4430,16 @@ snapshots:
'@emotion/sheet@1.4.0': {}
'@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)':
'@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)':
dependencies:
'@babel/runtime': 7.29.2
'@emotion/babel-plugin': 11.13.5
'@emotion/is-prop-valid': 1.4.0
'@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.4)
'@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.5)
'@emotion/serialize': 1.3.3
'@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.4)
'@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.5)
'@emotion/utils': 1.4.2
react: 19.2.4
react: 19.2.5
optionalDependencies:
'@types/react': 19.2.14
transitivePeerDependencies:
@ -4447,9 +4447,9 @@ snapshots:
'@emotion/unitless@0.10.0': {}
'@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.4)':
'@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.5)':
dependencies:
react: 19.2.4
react: 19.2.5
'@emotion/utils@1.4.2': {}
@ -4617,70 +4617,70 @@ snapshots:
dependencies:
state-local: 1.0.7
'@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
'@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
dependencies:
'@monaco-editor/loader': 1.7.0
monaco-editor: 0.55.1
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
'@mui/core-downloads-tracker@7.3.9': {}
'@mui/icons-material@7.3.9(@mui/material@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)':
'@mui/icons-material@7.3.9(@mui/material@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)':
dependencies:
'@babel/runtime': 7.29.2
'@mui/material': 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
react: 19.2.4
'@mui/material': 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
react: 19.2.5
optionalDependencies:
'@types/react': 19.2.14
'@mui/lab@7.0.0-beta.17(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@mui/material@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
'@mui/lab@7.0.0-beta.17(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@mui/material@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
dependencies:
'@babel/runtime': 7.29.2
'@mui/material': 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@mui/system': 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)
'@mui/material': 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
'@mui/system': 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)
'@mui/types': 7.4.12(@types/react@19.2.14)
'@mui/utils': 7.3.9(@types/react@19.2.14)(react@19.2.4)
'@mui/utils': 7.3.9(@types/react@19.2.14)(react@19.2.5)
clsx: 2.1.1
prop-types: 15.8.1
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
optionalDependencies:
'@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.4)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)
'@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.5)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)
'@types/react': 19.2.14
'@mui/material@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
'@mui/material@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
dependencies:
'@babel/runtime': 7.29.2
'@mui/core-downloads-tracker': 7.3.9
'@mui/system': 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)
'@mui/system': 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)
'@mui/types': 7.4.12(@types/react@19.2.14)
'@mui/utils': 7.3.9(@types/react@19.2.14)(react@19.2.4)
'@mui/utils': 7.3.9(@types/react@19.2.14)(react@19.2.5)
'@popperjs/core': 2.11.8
'@types/react-transition-group': 4.4.12(@types/react@19.2.14)
clsx: 2.1.1
csstype: 3.2.3
prop-types: 15.8.1
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
react-is: 19.2.4
react-transition-group: 4.4.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
react-transition-group: 4.4.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
optionalDependencies:
'@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.4)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)
'@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.5)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)
'@types/react': 19.2.14
'@mui/private-theming@7.3.9(@types/react@19.2.14)(react@19.2.4)':
'@mui/private-theming@7.3.9(@types/react@19.2.14)(react@19.2.5)':
dependencies:
'@babel/runtime': 7.29.2
'@mui/utils': 7.3.9(@types/react@19.2.14)(react@19.2.4)
'@mui/utils': 7.3.9(@types/react@19.2.14)(react@19.2.5)
prop-types: 15.8.1
react: 19.2.4
react: 19.2.5
optionalDependencies:
'@types/react': 19.2.14
'@mui/styled-engine@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)':
'@mui/styled-engine@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5)':
dependencies:
'@babel/runtime': 7.29.2
'@emotion/cache': 11.14.0
@ -4688,25 +4688,25 @@ snapshots:
'@emotion/sheet': 1.4.0
csstype: 3.2.3
prop-types: 15.8.1
react: 19.2.4
react: 19.2.5
optionalDependencies:
'@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.4)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)
'@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.5)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)
'@mui/system@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)':
'@mui/system@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)':
dependencies:
'@babel/runtime': 7.29.2
'@mui/private-theming': 7.3.9(@types/react@19.2.14)(react@19.2.4)
'@mui/styled-engine': 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)
'@mui/private-theming': 7.3.9(@types/react@19.2.14)(react@19.2.5)
'@mui/styled-engine': 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5)
'@mui/types': 7.4.12(@types/react@19.2.14)
'@mui/utils': 7.3.9(@types/react@19.2.14)(react@19.2.4)
'@mui/utils': 7.3.9(@types/react@19.2.14)(react@19.2.5)
clsx: 2.1.1
csstype: 3.2.3
prop-types: 15.8.1
react: 19.2.4
react: 19.2.5
optionalDependencies:
'@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.4)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)
'@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.5)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)
'@types/react': 19.2.14
'@mui/types@7.4.12(@types/react@19.2.14)':
@ -4715,14 +4715,14 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.14
'@mui/utils@7.3.9(@types/react@19.2.14)(react@19.2.4)':
'@mui/utils@7.3.9(@types/react@19.2.14)(react@19.2.5)':
dependencies:
'@babel/runtime': 7.29.2
'@mui/types': 7.4.12(@types/react@19.2.14)
'@types/prop-types': 15.7.15
clsx: 2.1.1
prop-types: 15.8.1
react: 19.2.4
react: 19.2.5
react-is: 19.2.4
optionalDependencies:
'@types/react': 19.2.14
@ -4992,22 +4992,22 @@ snapshots:
'@tanstack/query-core@5.96.2': {}
'@tanstack/react-query@5.96.2(react@19.2.4)':
'@tanstack/react-query@5.96.2(react@19.2.5)':
dependencies:
'@tanstack/query-core': 5.96.2
react: 19.2.4
react: 19.2.5
'@tanstack/react-table@8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
'@tanstack/react-table@8.21.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
dependencies:
'@tanstack/table-core': 8.21.3
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
'@tanstack/react-virtual@3.13.23(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
'@tanstack/react-virtual@3.13.23(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
dependencies:
'@tanstack/virtual-core': 3.13.23
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
'@tanstack/table-core@8.21.3': {}
@ -5344,7 +5344,7 @@ snapshots:
agent-base@9.0.0: {}
ahooks@3.9.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
ahooks@3.9.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
dependencies:
'@babel/runtime': 7.29.2
'@types/js-cookie': 3.0.6
@ -5352,8 +5352,8 @@ snapshots:
intersection-observer: 0.12.2
js-cookie: 3.0.5
lodash: 4.18.1
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
react-fast-compare: 3.2.2
resize-observer-polyfill: 1.5.1
screenfull: 5.2.0
@ -5977,14 +5977,14 @@ snapshots:
dependencies:
fetch-blob: 3.2.0
foxact@0.3.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
foxact@0.3.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
dependencies:
client-only: 0.0.1
event-target-bus: 1.0.0
server-only: 0.0.1
optionalDependencies:
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
foxts@5.4.0: {}
@ -6763,37 +6763,37 @@ snapshots:
punycode@2.3.1: {}
react-dom@19.2.4(react@19.2.4):
react-dom@19.2.5(react@19.2.5):
dependencies:
react: 19.2.4
react: 19.2.5
scheduler: 0.27.0
react-error-boundary@6.1.1(react@19.2.4):
react-error-boundary@6.1.1(react@19.2.5):
dependencies:
react: 19.2.4
react: 19.2.5
react-fast-compare@3.2.2: {}
react-hook-form@7.72.1(react@19.2.4):
react-hook-form@7.72.1(react@19.2.5):
dependencies:
react: 19.2.4
react: 19.2.5
react-i18next@17.0.2(i18next@26.0.3(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2):
react-i18next@17.0.2(i18next@26.0.3(typescript@6.0.2))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@6.0.2):
dependencies:
'@babel/runtime': 7.29.2
html-parse-stringify: 3.0.1
i18next: 26.0.3(typescript@6.0.2)
react: 19.2.4
use-sync-external-store: 1.6.0(react@19.2.4)
react: 19.2.5
use-sync-external-store: 1.6.0(react@19.2.5)
optionalDependencies:
react-dom: 19.2.4(react@19.2.4)
react-dom: 19.2.5(react@19.2.5)
typescript: 6.0.2
react-is@16.13.1: {}
react-is@19.2.4: {}
react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.4):
react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.5):
dependencies:
'@types/hast': 3.0.4
'@types/mdast': 4.0.4
@ -6802,7 +6802,7 @@ snapshots:
hast-util-to-jsx-runtime: 2.3.6
html-url-attributes: 3.0.1
mdast-util-to-hast: 13.2.1
react: 19.2.4
react: 19.2.5
remark-parse: 11.0.0
remark-rehype: 11.1.2
unified: 11.0.5
@ -6811,24 +6811,24 @@ snapshots:
transitivePeerDependencies:
- supports-color
react-router@7.14.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
react-router@7.14.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
dependencies:
cookie: 1.1.1
react: 19.2.4
react: 19.2.5
set-cookie-parser: 2.7.2
optionalDependencies:
react-dom: 19.2.4(react@19.2.4)
react-dom: 19.2.5(react@19.2.5)
react-transition-group@4.4.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
react-transition-group@4.4.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
dependencies:
'@babel/runtime': 7.29.2
dom-helpers: 5.2.1
loose-envify: 1.4.0
prop-types: 15.8.1
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
react@19.2.4: {}
react@19.2.5: {}
readdirp@4.1.2: {}
@ -7171,9 +7171,9 @@ snapshots:
dependencies:
punycode: 2.3.1
use-sync-external-store@1.6.0(react@19.2.4):
use-sync-external-store@1.6.0(react@19.2.5):
dependencies:
react: 19.2.4
react: 19.2.5
validator@13.15.35: {}

View File

@ -294,10 +294,10 @@ impl SilentUpdater {
async fn ask_user_to_install(app_handle: &tauri::AppHandle, version: &str) -> bool {
use tauri_plugin_dialog::{DialogExt as _, MessageDialogButtons, MessageDialogKind};
let title = clash_verge_i18n::t!("notifications.updateReady.title").to_string();
let title = clash_verge_i18n::t!("notifications.updateReady.title");
let body = clash_verge_i18n::t!("notifications.updateReady.body").replace("{version}", version);
let install_now = clash_verge_i18n::t!("notifications.updateReady.installNow").to_string();
let later = clash_verge_i18n::t!("notifications.updateReady.later").to_string();
let install_now = clash_verge_i18n::t!("notifications.updateReady.installNow").into_owned();
let later = clash_verge_i18n::t!("notifications.updateReady.later").into_owned();
let (tx, rx) = tokio::sync::oneshot::channel();

View File

@ -4,15 +4,22 @@ use super::use_lowercase;
use serde_yaml_ng::{self, Mapping, Value};
fn deep_merge(a: &mut Value, b: Value) {
let mut stack: Vec<(*mut Value, Value)> = vec![(a as *mut Value, b)];
while let Some((a_ptr, b)) = stack.pop() {
let a = unsafe { &mut *a_ptr };
match (a, b) {
(&mut Value::Mapping(ref mut a), Value::Mapping(b)) => {
for (k, v) in b {
deep_merge(a.entry(k).or_insert(Value::Null), v);
(Value::Mapping(a_map), Value::Mapping(b_map)) => {
for (k, v) in b_map {
let child = a_map.entry(k).or_insert(Value::Null);
stack.push((child as *mut Value, v));
}
}
(a, b) => *a = b,
}
}
}
pub fn use_merge(merge: &Mapping, config: Mapping) -> Mapping {
let mut config = Value::from(config);

View File

@ -303,7 +303,7 @@ async fn collect_profile_items() -> ProfileItems {
}
}
fn process_global_items(
async fn process_global_items(
mut config: Mapping,
global_merge: ChainItem,
global_script: ChainItem,
@ -319,7 +319,7 @@ fn process_global_items(
if let ChainType::Script(script) = global_script.data {
let mut logs = vec![];
match use_script(script, &config, profile_name) {
match use_script(script, config.clone(), profile_name.clone()).await {
Ok((res_config, res_logs)) => {
exists_keys.extend(use_keys(&res_config));
config = res_config;
@ -334,7 +334,7 @@ fn process_global_items(
}
#[allow(clippy::too_many_arguments)]
fn process_profile_items(
async fn process_profile_items(
mut config: Mapping,
mut exists_keys: Vec<String>,
mut result_map: HashMap<String, ResultLog>,
@ -364,7 +364,7 @@ fn process_profile_items(
if let ChainType::Script(script) = script_item.data {
let mut logs = vec![];
match use_script(script, &config, profile_name) {
match use_script(script, config.clone(), profile_name.clone()).await {
Ok((res_config, res_logs)) => {
exists_keys.extend(use_keys(&res_config));
config = res_config;
@ -455,16 +455,17 @@ async fn merge_default_config(
config
}
fn apply_builtin_scripts(mut config: Mapping, clash_core: Option<String>, enable_builtin: bool) -> Mapping {
async fn apply_builtin_scripts(mut config: Mapping, clash_core: Option<String>, enable_builtin: bool) -> Mapping {
if enable_builtin {
ChainItem::builtin()
let items: Vec<_> = ChainItem::builtin()
.into_iter()
.filter(|(s, _)| s.is_support(clash_core.as_ref()))
.map(|(_, c)| c)
.for_each(|item| {
.collect();
for item in items {
logging!(debug, Type::Core, "run builtin script {}", item.uid);
if let ChainType::Script(script) = item.data {
match use_script(script, &config, &String::from("")) {
match use_script(script, config.clone(), String::from("")).await {
Ok((res_config, _)) => {
config = res_config;
}
@ -473,7 +474,7 @@ fn apply_builtin_scripts(mut config: Mapping, clash_core: Option<String>, enable
}
}
}
});
}
}
config
@ -621,7 +622,8 @@ pub async fn enhance() -> (Mapping, HashSet<String>, HashMap<String, ResultLog>)
let profile_name = profile.profile_name;
// process globals
let (config, exists_keys, result_map) = process_global_items(config, global_merge, global_script, &profile_name);
let (config, exists_keys, result_map) =
process_global_items(config, global_merge, global_script, &profile_name).await;
// process profile-specific items
let (config, exists_keys, result_map) = process_profile_items(
@ -634,7 +636,8 @@ pub async fn enhance() -> (Mapping, HashSet<String>, HashMap<String, ResultLog>)
merge_item,
script_item,
&profile_name,
);
)
.await;
// merge default clash config
let config = merge_default_config(
@ -650,7 +653,7 @@ pub async fn enhance() -> (Mapping, HashSet<String>, HashMap<String, ResultLog>)
.await;
// builtin scripts
let mut config = apply_builtin_scripts(config, clash_core, enable_builtin);
let mut config = apply_builtin_scripts(config, clash_core, enable_builtin).await;
config = cleanup_proxy_groups(config);

View File

@ -1,3 +1,5 @@
use crate::process::AsyncHandler;
use super::use_lowercase;
use anyhow::{Error, Result};
use boa_engine::{Context, JsString, JsValue, Source, native_function::NativeFunction};
@ -10,11 +12,25 @@ use std::sync::Arc;
const MAX_OUTPUTS: usize = 1000;
const MAX_OUTPUT_SIZE: usize = 1024 * 1024; // 1MB
const MAX_JSON_SIZE: usize = 10 * 1024 * 1024; // 10MB
const MAX_LOOP_ITERATIONS: u64 = 10_000_000;
const SCRIPT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
// TODO 使用引用改进上下相关处理,避免不必要 Clone
pub fn use_script(script: String, config: &Mapping, name: &String) -> Result<(Mapping, Vec<(String, String)>)> {
pub async fn use_script(script: String, config: Mapping, name: String) -> Result<(Mapping, Vec<(String, String)>)> {
let handle = AsyncHandler::spawn_blocking(move || use_script_sync(script, &config, &name));
match tokio::time::timeout(SCRIPT_TIMEOUT, handle).await {
Ok(Ok(result)) => result,
Ok(Err(join_err)) => Err(anyhow::anyhow!("script task panicked: {join_err}")),
Err(_elapsed) => Err(anyhow::anyhow!("script execution timed out after {:?}", SCRIPT_TIMEOUT)),
}
}
fn use_script_sync(script: String, config: &Mapping, name: &String) -> Result<(Mapping, Vec<(String, String)>)> {
let mut context = Context::default();
context
.runtime_limits_mut()
.set_loop_iteration_limit(MAX_LOOP_ITERATIONS);
let outputs = Arc::new(Mutex::new(vec![]));
let total_size = Arc::new(Mutex::new(0usize));
@ -189,7 +205,7 @@ fn test_script() {
let config = &serde_yaml_ng::from_str(config).expect("Failed to parse test config YAML");
let (config, results) =
use_script(script.into(), config, &String::from("")).expect("Script execution should succeed in test");
use_script_sync(script.into(), config, &String::from("")).expect("Script execution should succeed in test");
let _ = serde_yaml_ng::to_string(&config).expect("Failed to serialize config to YAML");
let yaml_config_size = std::mem::size_of_val(&config);
@ -243,7 +259,7 @@ fn test_memory_limits() {
#[allow(clippy::expect_used)]
let config = &serde_yaml_ng::from_str("test: value").expect("Failed to parse test YAML");
let result = use_script(script.into(), config, &String::from(""));
let result = use_script_sync(script.into(), config, &String::from(""));
// 应该失败或被限制
assert!(result.is_ok()); // 会被限制但不会 panic
}

View File

@ -178,9 +178,11 @@ pub(super) async fn init_service_manager() {
if !is_service_ipc_path_exists() {
return;
}
if SERVICE_MANAGER.lock().await.init().await.is_ok() {
logging_error!(Type::Setup, SERVICE_MANAGER.lock().await.refresh().await);
let mut manager = SERVICE_MANAGER.lock().await;
if manager.init().await.is_ok() {
logging_error!(Type::Setup, manager.refresh().await);
}
drop(manager);
}
pub(super) async fn init_core_manager() {