mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-31 03:02:08 +08:00
daadb126e077c9a00cabbdf2df66e3828ffa2a22
31
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
22947632c0 |
refactor: Rewrite and improve sysproxy logic (#7744)
* fix(cmd): preserve anyhow context in coded errors Use alternate error formatting so coded failures retain the full anyhow context chain without changing their wire format. * fix(macos): skip redundant proxy disable writes When both proxy modes are off, read macOS state first and skip networksetup if the OS is already clean. Update the guard target only after a successful write. * feat(proxy): classify system proxy failures Attach stable, actionable proxy failure codes to anyhow chains while preserving the original diagnostic causes. * feat(cmd): return structured command failures Replace encoded error strings with serializable code and detail fields, and update frontend notice consumers to handle the structured shape. * fix(scripts): use the js-yaml namespace import Import js-yaml through its ESM namespace so the i18n cleanup script can access load and dump. * feat(proxy): localize classified proxy failures Map privilege and direct-fallback codes to localized explanations, and let mapped causes outrank generic operation keys in notices. * fix(ui): bound notice text and render failure details Limit resolved notice text without truncating clipboard content, and normalize structured failures embedded in translation parameters. * feat(proxy): persist unseen proxy failures Store unresolved classified failures by code, notify the frontend when the table changes, and show each failure sequence once when a visible window can receive it. * feat(proxy): report tray and hotkey toggle failures Record classified failures from tray and hotkey proxy toggles so they appear when a visible window is available. * fix(ui): restore the proxy switch after failed toggles Refresh frontend proxy state after a failed toggle so the switch reflects the rolled-back configuration. * feat(proxy): stop the guard after repeated failures Report guard outcomes, stop after the failure threshold, and persist the classified failure for the frontend. * fix(macos): preserve proxy during service handoff Stop a sidecar being handed to the service without clearing proxy state that the new owner must inherit. * feat(macos): classify misplaced sidecar state Distinguish a refused sidecar proxy write from a machine that still needs the privileged service installed. * feat(ui): recover misplaced sidecar notices Offer a restart action for macOS proxy failures caused by a ready service while the core still runs as a sidecar. * feat(ui): install the service from proxy notices Let privilege notices install the macOS service, restart the core, and report the runtime mode that was actually reached. * feat(cmd): attach requested proxy operations Carry the requested proxy direction with structured command failures when the originating command knows it. * feat(sysproxy): drain guards before OS access Wait for the local proxy guard to stop before OS reads, service handoff, or final writes, and refuse transitions when it does not drain. * feat(sysproxy): recover partial proxy writes Use write progress to choose whether to restore the old guard or compensate with a disabled proxy state, while preserving actionable errors. * feat(sysproxy): skip already-applied proxy writes Compare macOS proxy snapshots with global, PAC, and disabled targets so matching state avoids redundant privileged writes. * feat(proxy): report background proxy failures Persist and surface classified proxy failures from lifecycle paths that have no direct caller awaiting the result. * feat(ui): move proxy recovery into a dialog Guide users through installing or switching to the service after a refused proxy write, and suppress duplicate toast reports. * fix(ui): close proxy dialog after service recovery Dismiss the recovery dialog once the core reaches service mode, even when the original proxy request remains pending. * feat(ui): retry proxy request after service recovery Replay the requested proxy direction after the core reaches service mode, while leaving incidental restores unchanged. * fix(proxy): refresh state after successful apply Publish the proxy refresh from the shared apply path after releasing the lifecycle lock, and remove the tray-specific duplicate. * feat(ui): show proxy recovery progress Display installation, restart, and proxy-apply stages while keeping the primary action spinner visible during recovery. * feat(ui): route unavailable TUN through service recovery Let the TUN switch request service recovery and restore its requested state after the core restarts in service mode. * fix(ui): avoid duplicate TUN recovery errors Roll back deferred TUN toggles without reporting an error, and publish configuration refreshes from the transaction commit path. * chore: update changelog & pinlock * fix(core): resolve platform-specific clippy warnings * fix(core): detect Windows wildcard TCP conflicts Windows permits a specific exclusive bind beside an existing non-exclusive wildcard listener. Consult the system TCP listener table so the probe reports that overlap as a conflict. |
||
|
|
cefcb150c9 |
fix: core runstate review/improve (#7617)
* fix(core): reopen PAC when a start attempt never happens `save_proxy_ports` closes the PAC endpoint before it stages a candidate, because the endpoint resolves the Mixed Port through the draft layer and would otherwise answer with a port nothing is listening on. Most ways out of that function are a rejection, though: the candidate is refused and the Core keeps serving on its old ports, having never been stopped. PAC is re-derived only when the Running Mode changes, and that is exactly what did not happen — so the endpoint stayed shut for the rest of the session, and every browser on PAC silently went direct. Give `core_starting` the counterpart it was missing. `core_start_settled` re-derives PAC from the Running Mode rather than restoring a saved flag, which makes it correct on all three endings — a start that succeeded, one that was abandoned, one that failed and left the Core down — and therefore safe to run from a guard on every path. * fix(config): refuse overlapping draft transactions instead of losing an edit A layer holds exactly one draft, so a second writer's `edit_draft` overwrites the first writer's staged value in place. `DraftTransaction` nonetheless rolled back by calling `discard` on the shared layer, which discarded whatever was staged there — including a change another transaction was about to commit. The other transaction then found no draft, and `apply` on an absent draft does nothing at all. It saved the committed value, returned Ok, and its side effect had already happened: auto-launch registered with the OS while the config that describes it says otherwise, and no log line either way. "Roll back only my edit" cannot be implemented over a single slot, so enforce the exclusivity the type already assumed. Beginning a transaction claims every layer and refuses rather than interleaving, which is the same shape as `try_start_config_update` and `begin_operation` elsewhere. `patch_verge` is the caller that matters: it holds its draft across `process_terminated_flags`, which restarts the Core and can take seconds. The TUN reconciliation runs on every Run State transition and can now lose that race, so it treats a busy layer as "later" rather than as a failure worth telling the user about. * fix(core): reconcile TUN availability when the setting itself changes Moving the TUN availability guard from the frontend into Rust also moved what triggers it. The old effect depended on `enable_tun_mode`; the new reconciliation is spawned from `RealEnv::publish`, so it only runs on a Run State transition. Those two sets are not the same, and the setting itself falls in the gap. Switching TUN on from the global hotkey is the case that reaches it. That path is not gated on availability — unlike the tray item and the settings switch, both of which read `tun_capable` — and the flags a TUN patch raises are answered by a Core reload, which changes no Run State at all. So on a machine with no Service and no elevation the setting stayed on: tray icon, settings switch and `enable_tun_mode` in verge.yaml all reported TUN as enabled while nothing carried its traffic, until some unrelated core lifecycle transition happened to reconcile it. Reconcile after any patch that touched the setting. The reconciliation writes configuration itself, so it goes through the plain apply rather than back through the patch-and-reconcile form; the absence of a cycle is then a property of the call graph rather than of a runtime early-return. `toggle_tun_mode` now reads the setting back instead of returning what it asked for, so the hotkey cannot report TUN as on moments before the notice saying it was turned off. * fix(core): an action already under way is not a decision to put to the user The legacy status could not distinguish a requested privileged operation from one already running, because readers blocked until the operation finished and so never observed the request at all. Run State pushes every transition instead, and `service_needs_attention` treated any pending action as a question for the user. So an install started from the settings page or the home card raised the migration dialog on top of it, and an uninstall raised one offering to reinstall the Service being removed. Both offer a button that can only fail, since the privileged-operation slot is already taken. An operation in flight is a report of progress. The dialog the user is already driving keeps itself open through its own loading state. `tun_should_be_disabled` is unaffected: it already refused to act while an operation was in flight, so the term this adds is redundant there. * fix(core): let the snapshot decide whether the Run State has settled `settled` read the operation slot twice: once to decide whether to wait, then again inside the snapshot it returned. An operation claiming the slot between those two reads was described rather than waited for. `prepare_startup` reads this. A start racing an install would see the requested install, flatten it to `InstallRequired`, and decide `Wait` — leaving the core stopped where waiting for the install to finish would have told it whether the Service came back. Read the slot once, inside the snapshot, so there is no window rather than a second check closing it. The rewrite that introduced this also fixed a lost wakeup here, and that fix stands: the waiter is still registered before the state is read. * fix(ui): re-read the Run State once its listener is live Replacing the poll with a push left a gap. `listen` resolves asynchronously, so between the first `get_runtime_state` and the subscription being registered there is a window, and a transition landing in it is not queued anywhere — it is simply missed. The next event is whenever the backend happens to move again, which during startup can be a long time; the 30-second safety net was all that stood behind it, where the old code polled every two seconds precisely through startup. The Run State is the one thing here that arrives only by event, so it is the one thing a missed event leaves stale. Re-read it once every listener is live, which closes the gap without going back to polling for it. `onSubscribed` fires even when a registration failed, because a resync is most needed when a subscription is missing, and not at all after teardown. * docs(config): state what makes the PAC endpoint's draft read safe `MixedPort::desired` reads the draft layer, so the PAC endpoint can name a port the Core has not moved to yet. That is only safe because the one path staging a listener port closes PAC across the change. Nothing said so at either end, and the two are in different modules. * fix(core): ask again when staging goes silent rather than assume it failed The Service commits the generation before it answers, so a reply that never arrives is not evidence that nothing was written: config.yaml can already hold the new configuration while this side concludes the staging never happened. The Core keeps running the previous configuration, so nothing looks wrong -- until the Core dies and the Service's watchdog restarts it in the same generation from whatever config.yaml says, quietly making live a configuration the app reported as failed. Silence is the one answer that has to be resolved rather than acted on, so ask once more before falling back. It is cheap exactly where it matters: staging plans against the manifest it wrote, so a bundle that already landed copies nothing and answers in milliseconds, and the Service's lifecycle lock makes a second ask wait for a first one still in flight instead of interleaving with it. A deadline keeps that wait off the caller, and a second silence goes where the first one used to. |
||
|
|
c90de1355e |
refactor: core runstate (#7602)
* refactor(core): introduce core::runstate as the owner of Run State
"Is the Service available?" had five caches and three predicates, and
prepare_startup consulted all three within thirty lines. This lands the
foundation for collapsing that, with no behaviour change: the old owners
now delegate into one store.
The state model splits observation from intent. Five of ServiceStatus's
ten variants were never observations — cmd/service.rs passed
ServiceStatus::InstallRequired as the "which operation do you want"
argument — which is why narrowing to ServiceInstallState had to be lossy.
ServiceHealth now records what we saw, PendingAction records what the
session asked for, and ServiceStatus::from_run_state flattens the pair
for call sites that still expect the single slot.
Reads differ only in freshness: state() is cached, settled() waits for an
in-flight privileged operation, probe() forces IPC. What an answer means
lives on the snapshot (service_usable, tun_capable), so callers stop
writing their own formulas — the tray now asks tun_capable() instead of
recomputing "admin or ready".
Effects sit behind RunStateEnv with two adapters, RealEnv and FakeEnv.
That retires the seven tests that hand-built a private ServiceManager
struct literal, and makes the probe and detection paths testable for the
first time.
Two distinctions are now explicit rather than incidental:
- A single failed probe is not an observation and leaves confirmed
health intact; exhausting a poll's whole retry budget is one and
records unavailable. Both are tested, protecting
|
||
|
|
e599a9d02c | fix(service): avoid sticky availability probes | ||
|
|
7796259b03 |
feat: automatic mixed port fallback (#7579)
* feat: automatically select an available mixed proxy port * fix: coordinate listener port changes * fix: detect Windows listener port conflicts |
||
|
|
bce83d5c6f | chore: update i18n | ||
|
|
304f5bade9 |
perf(proxy): consolidate proxy data into a single IPC (#7542)
* feat(proxy): build deterministic proxy view * feat(proxy): expose ordered proxy view command * feat(proxy): add proxy view client contract * fix(lint): allow Node test files * feat(proxy): switch UI to ordered proxy view * fix(proxy): harden ordered proxy view consumers * fix(proxy): compare chain duplicates by current id * fix(proxy): release mihomo guard after fetch * fix(proxy): preserve semantic selections across refresh * fix(proxy): block global chain without GLOBAL * fix(proxy): handle proxy view edge states |
||
|
|
b9da0875cd | refactor: optimize swr data refresh paths | ||
|
|
5a96fdd9a2 |
fix: harden home clash-mode card against /configs read failures
Recover from controller read/switch failures on the mode card instead of showing a bare "communication error": - fall back to the tolerant get_clash_mode command (then runtime config) when the live /configs mode is unavailable, - optimistically reflect the clicked mode and write it through to the getClashConfig cache so a failed refetch can't flash back the old value, - surface patch failures via showNotice on both the home card and the proxies page (patchClashMode can now reject), and - revalidate getClashMode/getRuntimeConfig on refresh-clash-config so a once-empty fallback can recover. |
||
|
|
a00df6c93d | fix: refresh rules data when clash config is reloaded (#7299) | ||
|
|
c18c23963d |
refactor: frontend (#7097)
* refactor: remove unused window show/hide event listeners * refactor: deduplicate profile-changed event handling * refactor: remove unused setupCloseListener from use-listen * refactor: remove unused removeAllListeners from use-listen * fix(backend): dedupe profile changed notification on import * fix: avoid duplicate timer update completed event * refactor(frontend): simplify proxy switch fallback path |
||
|
|
ec82b69786 |
refactor: eliminate startup flicker — defer window show until overlay renders
- Remove Rust-side `eval(INITIAL_LOADING_OVERLAY)` that prematurely dismissed the overlay before React/MUI theme was ready - Defer `window.show()` from Rust `activate_window` to an inline `<script>` in index.html, executed after the themed overlay is in DOM - Remove `useAppInitialization` hook (duplicate of `useLoadingOverlay` with no themeReady gate) - Simplify overlay to pure theme-colored background — no spinner or loading text — so fast startup feels instant - Simplify `hideInitialOverlay` API and reduce overlay fade to 0.2s - Clean up unused CSS variables (spinner-track, spinner-top, etc.) |
||
|
|
04ce3d1772 | refactor: remove unused UI notification functions and streamline initialization logic | ||
|
|
b8fbabae04 |
fix: frontend memory leaks — Monaco dispose, TQ cache eviction, useEffect cleanup
- Dispose Monaco editor instances on dialog close to prevent cycle leak - Replace gcTime: Infinity with finite TTLs and evict orphaned subscription queryKeys - Add missing useEffect cleanup for timers, move setTimeout out of useMemo |
||
|
|
5da9f99698 |
fix: prevent TUN from being falsely disabled during startup
- Add 10s startup grace period before TUN auto-disable logic activates; service IPC may not be ready when the frontend first queries, causing a transient isServiceOk=false that incorrectly persists - Replace placeholderData (which set isLoading=false with stale data) with a proper isStartingUp guard; query now polls every 2s during startup to catch service readiness quickly - Add 'getSystemState' to refresh-verge-config invalidation keys to fix key mismatch that prevented event-driven refetches from working |
||
|
|
a73fafaf9f |
refactor: migrate SWR to TanStack Query v5 (#6713)
Replace swr with @tanstack/react-query v5 across all hooks, providers, and components. Introduce singleton QueryClient, WS subscription pattern via useQuery+useEffect, and enforce component-layer cache access contract. |
||
|
|
c672a6fef3 |
refactor: lint (#6511)
* refactor: lint * chore: remove eslint-plugin/config-prettier |
||
|
|
d91e19e166 | chore: reorganize frontend files | ||
|
|
a9e6391417 | feat(layout/nav): add reset-to-default menu order action | ||
|
|
721929a2a1 | refactor(layout/nav): extract nav menu order logic into hook | ||
|
|
bae65a523a | chore: rename files to kebab-case and update imports | ||
|
|
3cf51de850 | refactor: reorganize component and hook structure; remove unused modules | ||
|
|
f439e93a2b |
feat(hotkey): add global reactivate_profiles shortcut (#5527)
* feat(hotkey): add global reactivate_profiles shortcut * feat(profile): expose validation state for reactivation shortcuts |
||
|
|
204bfa36e5 | refactor: reorganize imports and remove unused axios instance code | ||
|
|
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> |
||
|
|
19b3899a1b | refactor: improve notification handling and avoid backend loop with empty messages | ||
|
|
34cac0fa3a | feat: improve subscription update method and enhance error handling for profile updates | ||
|
|
36d1a3878f | refactor: remove retry notice for Clash proxy updates from notification handlers and localization files | ||
|
|
a869dbb441 |
Revert "refactor: profile switch (#5197)"
This reverts commit
|
||
|
|
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
|
||
|
|
b77cc012e1 | refactor: streamline app initialization and enhance WebSocket cleanup logic |