ProxyControlSwitches unconditionally mounted both SysproxyViewer and
TunViewer while its switch rows are mutually exclusive, so every
instance carried a dialog that could never open, each firing
getEmbeddedServerPort or a clash query subscription on mount. Mount
each viewer under the same condition as the switch row that opens it.
SettingSystem also mounted its own SysproxyViewer and TunViewer whose
refs were never opened there; the reachable entries are the
ProxyControlSwitches rows, so the dead pair is removed. Viewer mounts
across the app drop from eight (setting-system: two own + two per
instance, proxy-tun-card: two) to three (one per rendered switch
variant).
sysproxy, misc and tunnels viewers duplicated their form default
literals in useState and open(), and the two copies in misc-viewer had
already diverged (app log max size, app log max count, auto log clean).
Extract a buildInitialValues factory per viewer and unify on the open()
values, which are the only ones users could ever see.
GuardState applies the optimistic onChange write before onGuard runs,
so repeating the same onChangeData patch inside the guard handler
duplicated every cache write and subscriber notification for the auto
launch switch.
The mount effect fired refreshRules and refreshRuleProviders
unconditionally and again under the page-visible flag, double-firing
both queries on mount and on every hide-to-show transition. Gate the
refresh on visibility once so each trigger performs exactly one
revalidation.
Drop the handwritten IProxyProviderItem and IRuleProviderItem
interfaces: IProxyProviderItem is a stale duplicate of the generated
ProxyProviderView contract, and neither has any references left in
the tree.
Keep IProxyItem and IProxyGroupItem: IConfigData still declares
'proxy-groups'?: IProxyGroupItem[], which builds on IProxyItem.
Remove the never-read _revalidate argument of mutateVerge and its 16
call-site placeholders, seven never-passed props of the split chip
editor, the unconsumed refreshDecorated context member, the now-orphaned
toggleFullscreen control, the unused translate argument of the notice
message handler, the reserved-but-unset ServiceConfig.timeout field and
unreachable branches in parseNoticeExtras.
Drop state that can only hold its initial value (profiles page disabled
flag, traffic graph currentFPS), a write-only window controls ref with
its unconsumed imperative handle, a no-op appendData ref method, an
unreachable gradient branch, an invisible fadeIn shell, constant-true
subexpressions in the system info card, an empty click handler and a
pointless useMemo.
Add six liveness rules to the frontend guardrails covering dead state,
unused exposed surface, unreachable fallback paths, duplicate side
effects per interaction, unreachable mounts and unbounded resource
lifecycles. These target defect classes that the existing CI gates
(knip, eslint, arch:check) cannot detect mechanically.
`@scope`'s `to` bound excludes the boundary elements themselves, so rules
targeting `.view-lines` never matched. Move the bound to its children
with `> *`: the container becomes styleable, its internals stay excluded.
The paths-filter output in lint-clippy.yml only gated two echo steps;
checkout, toolchain setup and clippy itself ran unconditionally, so
frontend-only PRs still burned a 3-runner matrix on a full clippy build
plus the logging-check tool install.
- lint-clippy/rustfmt/frontend-check: move path filters to workflow
level so irrelevant PRs spawn no runner at all
- split contract-parity into its own workflow; its trigger set spans
both sides and workflow paths cannot vary per job
- clippy paths now cover crates/** and workspace Cargo files, which the
old src-tauri/** filter missed
dev has no required status checks, so path-filtered workflows cannot
get stuck in "Expected".
* fix(proxies): render configured groups in global mode
Global mode previously only rendered the synthetic GLOBAL group and
sliced off its header, which flattened every user-defined group into
a non-expandable proxy row. Configured groups could no longer be
opened or inspected. Render the same group list as rule/script mode
so every group keeps its own header and expand toggle.
Fixes#8376
* style: keep useRule assignment on one line
* fix & changelog
* fix: hover when global proxy
---------
Co-authored-by: susu-Sugar <214122492+susu-Sugar@users.noreply.github.com>
Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
tsrs (Rust port of the TypeScript 7 checker) replaces tsc --noEmit in
the typecheck script, web:build, and CI. Measured on this repository:
~0.4s vs ~3.5s wall time and ~280 MB vs ~750 MB peak RSS, with
identical zero-diagnostic results on the current tree. The typescript
package stays installed: typescript-eslint projectService and
scripts/cleanup-unused-i18n.mjs consume its compiler API.
Support the dns-mode field introduced in mihomo v1.19.33. A
button group below the stack selector offers
Auto/Disabled/Native/Hijack; Auto writes an empty string, which
the core treats as the legacy behavior. Adding dns-mode to
GUI_KEYS makes the value GUI-authoritative once saved, matching
the other TUN dialog fields.
* chore(deps): update tauri
* fix(deps): pin rust-ini to 0.21.1 for tauri-plugin-deep-link 2.6.2
tauri-plugin-deep-link 2.6.2 constructs ini::ParseOption with an
exhaustive struct literal, which fails to compile against rust-ini
0.21.2+ (new required fields enabled_indented_mutiline_value and
enabled_preserve_key_leading_whitespace, E0063 on linux). Upstream
fixed it in tauri-apps/plugins-workspace#3711 with ..Default::default()
but no release contains the fix yet, so downgrade rust-ini in Cargo.lock
until deep-link 2.6.3 ships.
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
This reverts commit cb751052a4.
Remove the tray latency test and its dashboard URL persistence and cache
changes because the two entry points have diverging behavior.
Preserve the subsequent IPC binding migration and remove generated fields,
input projections, and locale entries that depended on the reverted feature.
The WebdriverIO Tauri service pairs with the Tauri stack it drives, so
its updates belong with the coordinated Tauri npm and Cargo upgrades
rather than the generic npm group.
Add a packageRule that pulls @tauri-apps/* packages and tauri/tauri-*
crates out of the generic npm and cargo groups into one shared "tauri"
group, so frontend and backend halves of a Tauri upgrade are validated
together.
Pin tauri-plugin-mihomo by rev instead of branch so Renovate tracks it:
branch-pinned git dependencies are skipped with skipReason
git-dependency and never produce updates. With the rev pin the plugin
joins the tauri group via digest updates. The rev is the commit already
locked in Cargo.lock, so resolution is unchanged.
The notice handler table was rebuilt on every notice event; build it
once at module scope. withLoading returned a new state even when the
loading set was unchanged, re-rendering store consumers for nothing.
The switching flag was released by a defer registered inside
perform_config_update, so a panic during the write-lock wait or the
draft edit could leave it stuck set. Register the defer right after the
CAS. Skip the generic validation notice for busy switch outcomes; the
frontend already shows its own busy toast.
patchProfiles entered the mutate funnel without errorNotice:false, so a
failed switch toasted twice. On busy the cached current profile was left
untouched, so a discarded switch draft could poison later clicks on the
target profile. Refetch on every non-valid outcome.
- Bump `gh-aw` from 0.89.21 to 0.92.0
- Switch from `pull_request_target` to `pull_request` for CI security
- Set `permissions.copilot-requests` to `none` for suppressing compilation info
Badge values for freshly expanded rows seed from the cached proxy
view. Results that reach the core without a frontend event (tray
group test, provider health checks) are absent from that snapshot
until the next poll, so measured delays only appeared after another
interaction such as switching nodes forced a refetch. Route head
state patches through a wrapper that revalidates the view on
open transitions, matching how the group test already refreshes.
Every traffic refresh rebuilt the attributed-string dictionary (font,
color, paragraph style, baseline number, dict) and ran a CoreText layout
via NSAttributedString::size() just to size the status item, although
both depend only on the button height and the glyph advance.
The dictionary and the monospaced per-glyph advance (measured once
through the same layout engine with a "0" probe) are now cached keyed by
button height, and the width is computed as longest-line length times
the advance. The speed charset is pure ASCII on a monospaced font, so
the arithmetic width matched the layout width exactly (max deviation
0.000pt over 2000 realistic samples).
Standalone A/B over 43,200 updates (12h at 1 Hz), same hot path:
| variant | mean | p95 | phys_footprint after |
| --- | ---: | ---: | ---: |
| rebuild + layout (old) | 6.89us | 7.8us | 19.8MB |
| cached attrs | 4.71us | 5.0us | 7.8MB |
| cached + arithmetic width | 0.27us | 0.3us | 6.0MB |
The retained-memory delta is malloc-zone fragmentation from the churn,
which this removes at the source.
The cap of 8 was a conservative pick from 8ccb0b9a; that commit's own
setup benchmark had the 4-worker / 8-blocking row as the best config:
639.3ms avg / 645.5ms max setup time and 22.1 avg threads, ahead of the
adopted 8-worker row (650.1ms / 662.4ms, 26.0 threads).
Startup parallelism is unaffected in practice: the setup join! polls
cooperatively on one task, and CPU work (validation, script eval, YAML
parse) runs on the spawn_blocking pool, which keeps its 8-thread cap
through the existing 2x invariant. A 12h live session showed the 8 idle
workers accumulating under 0.5s CPU total, so four parked stacks
(~0.4MB dirty) are pure overhead.
* feat!: remove signal crate
* fix: clippy
* feat(signal): limit the shutdown signal crate to Linux
Upstream now covers termination and session-end events on Windows and
macOS, but Linux has no equivalent, so pre-shutdown cleanup there still
depends on listening for SIGTERM/SIGINT/SIGHUP (#7827). Restore
clash-verge-signal behind a target_os = "linux" gate instead of dropping
it: the dependency lives in a linux-only target section, the crate root
carries an English Linux-only rationale and compiles to nothing
elsewhere, and the Windows backend is gone.
register() now takes the quit flow's unit future: the latch serializes
in-flight cleanups and releases after each attempt, leaving the
exit-or-cancel decision to the quit flow itself.
Verified with cargo check and clippy for aarch64-unknown-linux-gnu, and
cargo check on the host where the crate compiles empty.
tauri-cli's get_binaries() adds every file in src-tauri/src/bin/ as a
bundle binary, and the deb/rpm packagers copy each one to /usr/bin, so
the dev-only bindings generator ended up as /usr/bin/frontend-contract
(1.0 MB) in release packages and broke downstream repackaging (Copr:
"Installed but unpackaged file(s) found"). There is no bundler option
to exclude a binary, so move the generator to an example target, which
cargo keeps dev-only and get_binaries() never scans, and invoke it via
'cargo run --example'.
Ref https://github.com/clash-verge-rev/clash-verge-rev/pull/8131#issuecomment-6137907930