Files
EasyCLIProxyAPI/src/coreRuntime.tsx
T
lzt404 9cf3878b28 perf: reduce background overhead and improve UI responsiveness
- Move blocking usage queries and core lifecycle operations to background
  workers, limit concurrency, and prevent overlapping core operations
- Coalesce statistics refreshes and consolidate category aggregations,
  reducing unfiltered analysis queries from 11 to 4
- Cache bundled model prices, reuse management API connections, and reduce
  redundant checks in the usage collector
- Throttle download progress and guide positioning updates, and avoid
  redundant renders when core status is unchanged
- Bound and coalesce configuration watcher events, and reuse a single
  model catalog refresh worker
- Stop scheduling remaining health checks when the dialog closes and
  limit backend health-check concurrency
- Replace overlapping OAuth polling with sequential requests, retry
  backoff, timeouts, and stale callback protection
- Add regression tests and a statistics benchmark script, and move
  performance verification documentation to agent_docs
2026-09-06 01:28:58 +08:00

105 lines
3.0 KiB
TypeScript

import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
export type CoreStatus = {
installed: boolean;
running: boolean;
starting: boolean;
managed: boolean;
processId: number | null;
currentVersion: string | null;
installDir: string;
binaryPath: string | null;
message: string;
};
type CoreRuntimeContextValue = {
status: CoreStatus | null;
statusError: string;
refreshStatus: () => Promise<void>;
publishStatus: (status: CoreStatus | null) => void;
};
const CoreRuntimeContext = createContext<CoreRuntimeContextValue | null>(null);
export function CoreRuntimeProvider({ children }: { children: ReactNode }) {
const [status, setStatus] = useState<CoreStatus | null>(null);
const [statusError, setStatusError] = useState('');
const refreshRequest = useRef<Promise<void> | null>(null);
const statusRevision = useRef(0);
const publishStatus = useCallback((nextStatus: CoreStatus | null) => {
++statusRevision.current;
setStatus((previous) => JSON.stringify(previous) === JSON.stringify(nextStatus) ? previous : nextStatus);
if (nextStatus) {
setStatusError('');
}
}, []);
const refreshStatus = useCallback(() => {
if (refreshRequest.current) return refreshRequest.current;
const revision = statusRevision.current;
refreshRequest.current = invoke<CoreStatus>('get_core_status')
.then((nextStatus) => {
if (revision === statusRevision.current) publishStatus(nextStatus);
})
.catch((error) => {
if (revision !== statusRevision.current) return;
setStatus(null);
setStatusError(String(error));
})
.finally(() => { refreshRequest.current = null; });
return refreshRequest.current;
}, [publishStatus]);
useEffect(() => {
let disposed = false;
let unlisten: (() => void) | null = null;
listen<CoreStatus>('core-status-changed', (event) => {
if (!disposed) {
publishStatus(event.payload);
}
}).then((stop) => {
if (disposed) {
stop();
} else {
unlisten = stop;
}
}).catch((error) => {
if (!disposed) {
setStatusError(String(error));
}
});
void refreshStatus();
const timer = window.setInterval(() => {
if (!document.hidden) {
void refreshStatus();
}
}, 10_000);
return () => {
disposed = true;
unlisten?.();
window.clearInterval(timer);
};
}, [publishStatus, refreshStatus]);
const value = useMemo(
() => ({ status, statusError, refreshStatus, publishStatus }),
[publishStatus, refreshStatus, status, statusError],
);
return <CoreRuntimeContext.Provider value={value}>{children}</CoreRuntimeContext.Provider>;
}
export function useCoreRuntime() {
const context = useContext(CoreRuntimeContext);
if (!context) {
throw new Error('useCoreRuntime 必须在 CoreRuntimeProvider 内使用');
}
return context;
}