- 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
105 lines
3.0 KiB
TypeScript
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;
|
|
}
|