Ship Paseo-equivalent provider snapshot, AgentComposerBar, ACP dispatch rewrite with streaming/persist, permission prompts, and agent commands. Follow-up: pane-scoped chat resolution, CoderMessageList tool timeline, WS user-delta replace, and inference orphan tool_call stripping. Archive openspec v2-2; update CHANGELOG and CURRENT. Co-authored-by: Cursor <cursoragent@cursor.com>
50 lines
1.3 KiB
TypeScript
50 lines
1.3 KiB
TypeScript
import { useEffect, useSyncExternalStore } from 'react';
|
|
import { api } from '@/api/client';
|
|
import type { ProviderSnapshotEntry } from '@/api/types';
|
|
|
|
let cached: ProviderSnapshotEntry[] | null = null;
|
|
let inflight: Promise<ProviderSnapshotEntry[]> | null = null;
|
|
const listeners = new Set<() => void>();
|
|
|
|
function notify(): void {
|
|
for (const fn of listeners) fn();
|
|
}
|
|
|
|
function subscribe(fn: () => void): () => void {
|
|
listeners.add(fn);
|
|
return () => listeners.delete(fn);
|
|
}
|
|
|
|
function getSnapshot(): ProviderSnapshotEntry[] | null {
|
|
return cached;
|
|
}
|
|
|
|
async function doFetch(cwd?: string): Promise<ProviderSnapshotEntry[]> {
|
|
const data = await api.coder.snapshot(cwd);
|
|
cached = data;
|
|
inflight = null;
|
|
notify();
|
|
return data;
|
|
}
|
|
|
|
function ensureLoaded(cwd?: string): void {
|
|
if (cached || inflight) return;
|
|
inflight = doFetch(cwd).catch((err) => {
|
|
inflight = null;
|
|
console.error('provider snapshot fetch failed:', err);
|
|
return [];
|
|
});
|
|
}
|
|
|
|
export function refreshProviderSnapshot(cwd?: string): Promise<ProviderSnapshotEntry[]> {
|
|
cached = null;
|
|
inflight = null;
|
|
return doFetch(cwd);
|
|
}
|
|
|
|
export function useProviderSnapshot(cwd?: string): ProviderSnapshotEntry[] | null {
|
|
const entries = useSyncExternalStore(subscribe, getSnapshot);
|
|
useEffect(() => { ensureLoaded(cwd); }, [cwd]);
|
|
return entries;
|
|
}
|