- Replace boolean/string validation results with structured ValidationOutcome values across config validation, profile switching, DNS validation, and script validation. Callers can now distinguish valid, invalid, skipped, and busy states, with invalid results carrying an error kind and message.
- Route core config updates through structured outcomes and checked forced updates. Generation and validation failures now discard runtime drafts and surface validation failures instead of relying on empty success messages.
- Preserve existing frontend ergonomics by keeping boolean service wrappers where callers only need success, while direct DNS and script validation consumers receive ValidationOutcome. Failed profile editor saves reload the backend-restored file content and synchronize Monaco models so reopened editors show the restored state.
- Bump clash_verge_service_ipc to 2.3.0 and refresh the lockfile for its updated dependency graph.
- Re-check the running service version after install/start so a stopped old 2.2.0 service is reinstalled before use.
- Resolve the latest clash-verge-service-ipc release tag with the existing version cache.
- Download the per-target service archive and extract service, install, and uninstall binaries to their existing platform-specific destinations.
- Preserve mihomo/resource download behavior and allow scripts/*.mjs through ESLint project service for hook linting.
Mitigates RUSTSEC-2026-0098: Incorrect URI name constraint validation.
- Update rustls-webpki from 0.103.11 to 0.103.13.
- Fixes a flaw where URI name constraints in certificates were
incorrectly accepted, potentially allowing unauthorized names.
See: https://rustsec.org/advisories/RUSTSEC-2026-0098
Address RUSTSEC-2026-0103: Use-After-Free and Double Free in
IntoIter::drop when element drop panics.
- Upgrade thin-vec from 0.2.15 to 0.2.16.
- Mitigate potential memory corruption during panic unwinding.
See: https://rustsec.org/advisories/RUSTSEC-2026-0103
WebSocket hooks (use-connection-data, use-log-data)
- Single-pass merge for connection snapshots; reuse previous row reference
when upload/download are unchanged so downstream memos can bail out
- Single-allocation cap on closedConnections (no double concat+slice)
- Single-allocation clamp in appendLogs
- Cache dayjs().format result within each 50ms log flush window
Connection table (connection-table.tsx)
- Decouple 5s relative-time tick from columnDefs via shared module-level
tickStore + memoised RelativeTimeCell so the tick no longer rebuilds
the table
- RowComponent memo with custom comparator (relies on stable row reference)
- Lift sx objects to module scope
- Stable onShowDetail via ref
App data provider (app-data-provider, app-data-context)
- Split monolithic context into six sub-contexts (Proxies/Rules/ClashConfig/
System/Uptime/Refreshers); 3s uptime ticks no longer invalidate unrelated
consumers
- useAppData adapter aggregates sub-contexts; consumer files unchanged
- useStableFn helper stabilises all refresh* callback identities
- Extract useCtx helper to remove repeated null-check boilerplate
Canvas traffic graph (enhanced-canvas-traffic-graph, use-traffic-monitor)
- Lift Y-axis scale (max/min) to drawGraph layer; calculateY allocations
per frame: ~600 -> 1
- Document visibility gate stops RAF and the setNow interval when the
window is hidden; appendData unaffected
- Reuse the existing visibilitychange listener instead of adding a second
one; isDocumentVisibleRef guards animate() inside the frame
- Convert forEach arrow shorthands to block bodies
- Drop unnecessary state-setter deps from a useCallback
Proxy groups (proxy-groups, proxy-render, use-render-list)
- Narrow proxyColItemsMemo dep: headState -> headState.showType
- Per-group fingerprint cache + prevListRef in use-render-list; steady-state
refetch ticks become O(0) and the list reference stays stable when nothing
changed
- useStableCallback wraps handleCheckAll / handleLocation
- Extract shared delay state logic from ProxyItem and ProxyItemMini
into `useProxyDelayState` hook (src/hooks/use-proxy-delay-state.ts);
JSX display thresholds preserved per-component.
- Deduplicate rAF/setTimeout/Promise scheduling in DelayManager via
a private `scheduleOnNextFrame` helper.
- Drop no-op `useMemo(() => MODES, [])` in proxies page.
- Replace `any` on `setClashLog` prev params and layout-traffic sx
objects with proper types (`pre!`, `Pick<BoxProps|...>`).
The MUI v9 migration moved boxStyle layout props into sx, but the memory
usage Box had a separate sx that overwrote it. Merge boxStyle.sx like the
upload/download rows already do.
Transitioned panic strategy from 'abort' to 'unwind' and integrated a
global panic hook into the logging framework.
This change allows the application to capture critical failure metadata—
including specific file locations, line numbers, and panic payloads—
persisting them to local logs before exit.
Note: We prioritize troubleshooting capability and long-term stability
at this stage. Reverting to 'panic = abort', `debug = false`,
`strip = true`, `remove split-debuginfo` for peak performance and
minimal binary size will only be considered once the project reaches
a mature state with near-zero community-reported crashes.
Backend:
- Move resolve_done() from sync setup() to async task after futures::join!
so Timer waits for actual init completion instead of firing immediately
- Replace std::thread::sleep(50ms) with tokio::time::sleep in async context
- Remove duplicate refresh_tray_menu in tray_init (keep post-join call only)
- Delete dead code reset_resolve_done (process restarts, static is destroyed)
- Rename create_window(is_show) → create_window(should_create) for clarity
Frontend:
- Remove duplicate verge://refresh-clash-config listener from AppDataProvider
(useLayoutEvents handles it via invalidateQueries — single consumer path)
- Stabilize useEffect deps with useRef for TQ refetch references
- Simplify AppDataProvider event listener setup (profile-changed + proxy only)
- Change TQ_MIHOMO retryDelay from fixed 2000ms to exponential backoff
(200ms → 400ms → 800ms, cap 3s) so core-dependent queries retry faster
- Expose isCoreDataPending from AppDataProvider to distinguish between
data still loading vs actual errors
- ClashModeCard: show placeholder instead of "communication error" while
core data is pending
- CurrentProxyCard: show empty space instead of "no active node" while
core data is pending
- 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.)
- 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
- 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
Preserve SWR cache in onConnected to avoid replacing accumulated logs
with kernel buffer on reconnect. Add KeepAlive for the logs page so
its DOM stays mounted across route changes, removing the visible blank
window when navigating back.
- use-system-state: convert module-level `disablingTunMode` to useRef
to isolate state per hook instance, fix no-op clearTimeout, add
proper effect cleanup
- use-profiles: convert forEach to for..of so selectNodeForGroup is
properly awaited, remove fire-and-forget setTimeout around mutate
- use-clash: add useLockFn to patchInfo for concurrency safety
- Remove unused `dns_settings_enabled` localStorage writes in
setting-clash.tsx — state is sourced from verge config, these
writes were never read anywhere.
- Replace hardcoded `'last_check_update'` localStorage read in
system-info-card.tsx with exported `readLastCheckTime()` from
the useUpdate hook, keeping the key in a single source of truth.
The settings page "Check for updates" did not update the homepage
"last check update time" because each page managed the timestamp
independently. Centralizes the timestamp in the useUpdate hook
via SWR + localStorage so both pages share a single data source.
Closes https://github.com/clash-verge-rev/clash-verge-rev/issues/6605#issuecomment-4147144987
The profile-update-completed event handler was missing a mutate('getProfiles')
call, causing the "X time ago" display to show stale timestamps after
backend timer auto-updates.
The onCheckUpdate handler never persisted the timestamp to localStorage
or dispatched it to component state, so clicking "Last Check Update"
would report the result but leave the displayed time stale.
- Consolidate 3 near-identical icon getter functions into load_icon/default_icon with IconKind enum
- Merge two platform-gated update_icon implementations into one
- Extract update_menu_and_icon to eliminate duplicate combo in feat/config.rs and feat/clash.rs