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.
This prevents the runtime from spawning an excessive number of threads on
high-end CPUs, reducing memory footprint and context switching.
Introduces a dynamic cap for worker threads (max 16) and scales
blocking threads proportionally. Includes thread naming for diagnostics.
* fix(tun): restart core when tun mode is toggled to true on linux
* fix(changelog): add note for Linux TUN not taking effect immediately
---------
Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
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