feat(settings): add third-party integrations dashboard
Add a Settings → Integrations page for installing and managing the three supported OpenCode provider plugins (Claude Code, Command Code, Cursor), with search, i18n, and plugin-registry status wiring. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
This commit is contained in:
committed by
Cursor Agent
co-authored by
Serhii Dziupin
parent
3f266232f9
commit
af380081c2
@@ -292,12 +292,24 @@ describe('usePluginsStore', () => {
|
||||
test('loadRegistryInfo skips empty specs and clears loading flag', async () => {
|
||||
usePluginsStore.setState({ isLoadingRegistry: true });
|
||||
|
||||
await usePluginsStore.getState().loadRegistryInfo({ specs: [] });
|
||||
const result = await usePluginsStore.getState().loadRegistryInfo({ specs: [] });
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(fetchCalls).toHaveLength(0);
|
||||
expect(usePluginsStore.getState().isLoadingRegistry).toBe(false);
|
||||
});
|
||||
|
||||
test('loadRegistryInfo reports a failed request without clearing prior registry data', async () => {
|
||||
usePluginsStore.setState({ registryInfo: { [entry.spec]: registryOk } });
|
||||
queueFetchResponses([jsonResponse({ error: 'registry unavailable' }, { status: 500 })]);
|
||||
|
||||
const result = await usePluginsStore.getState().loadRegistryInfo({ specs: [entry.spec] });
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(usePluginsStore.getState().registryInfo).toEqual({ [entry.spec]: registryOk });
|
||||
expect(usePluginsStore.getState().isLoadingRegistry).toBe(false);
|
||||
});
|
||||
|
||||
test('loadPlugins success triggers registry load without blocking result', async () => {
|
||||
queueFetchResponses([jsonResponse(pluginListPayload), jsonResponse({ results: [registryOk] })]);
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ export interface PluginsStore {
|
||||
setSelected: (id: string | null) => void;
|
||||
setDraft: (draft: PluginDraft | null) => void;
|
||||
loadPlugins: (options?: { force?: boolean }) => Promise<boolean>;
|
||||
loadRegistryInfo: (opts?: { specs?: string[]; force?: boolean }) => Promise<void>;
|
||||
loadRegistryInfo: (opts?: { specs?: string[]; force?: boolean }) => Promise<boolean>;
|
||||
updateToLatest: (id: string) => Promise<PluginMutationResult>;
|
||||
createEntry: (input: { spec: string; options?: Record<string, unknown>; scope: PluginScope }) => Promise<PluginMutationResult>;
|
||||
updateEntry: (id: string, input: { spec?: string; options?: Record<string, unknown> }) => Promise<PluginMutationResult>;
|
||||
@@ -207,7 +207,7 @@ export const usePluginsStore = create<PluginsStore>()(
|
||||
const specs = dedupeSpecs(opts?.specs ?? get().entries.map((entry) => entry.spec));
|
||||
if (specs.length === 0) {
|
||||
set({ isLoadingRegistry: false });
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
set({ isLoadingRegistry: true });
|
||||
@@ -227,9 +227,11 @@ export const usePluginsStore = create<PluginsStore>()(
|
||||
}
|
||||
}
|
||||
set({ registryInfo: nextRegistryInfo, isLoadingRegistry: false });
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[PluginsStore] Failed to load plugin registry info:', error);
|
||||
set({ isLoadingRegistry: false });
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user