feat: expose plugin settings UI API

This commit is contained in:
mirivlad 2026-06-29 04:23:06 +08:00
parent 4387c6ab19
commit 380f90c5ec
9 changed files with 48 additions and 4 deletions

View File

@ -69,6 +69,9 @@ export interface VerstakPluginAPI {
write(name: string, data: PluginDataJSON): Promise<void>;
};
};
ui: {
openSettings(panelId?: string): Promise<void>;
};
capabilities: {
has(capability: string): Promise<boolean>;
get(capability: string): Promise<{

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"version":3,"file":"plugin-api.js","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E;AAC9E,gDAAgD;AAsKhD,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;AACtF,CAAC"}
{"version":3,"file":"plugin-api.js","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E;AAC9E,gDAAgD;AA0KhD,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;AACtF,CAAC"}

View File

@ -1 +1 @@
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,KAAK,EAAwB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAQ3E,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,4BAA4B,CAAC;CAC9C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAetF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CASnF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,SAAgB,EAAE,OAAO,GAAE,oBAAyB,GAAG,gBAAgB,CAkTlH;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAgCxF;AAGD,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,EAAE,EAAE,CAAC"}
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,KAAK,EAAwB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAQ3E,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,4BAA4B,CAAC;CAC9C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAetF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CASnF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,SAAgB,EAAE,OAAO,GAAE,oBAAyB,GAAG,gBAAgB,CA6TlH;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAgCxF;AAGD,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,EAAE,EAAE,CAAC"}

11
dist/test-utils.js vendored
View File

@ -126,6 +126,17 @@ export function createMockPluginAPI(pluginId = 'test.plugin', options = {}) {
}),
},
},
ui: {
openSettings: vi.fn(async (panelId) => {
const event = {
name: 'ui.openSettings',
pluginId,
payload: { pluginId, panelId: panelId || '' },
timestamp: new Date().toISOString(),
};
(eventHandlers.get('ui.openSettings') || []).slice().forEach((handler) => handler(event));
}),
},
capabilities: {
has: vi.fn(async () => false),
get: vi.fn(async (name) => ({ available: false, name })),

File diff suppressed because one or more lines are too long

View File

@ -14,6 +14,7 @@ describe('VerstakPluginAPI contract', () => {
expect(typeof api.settings.write).toBe('function');
expect(typeof api.storage.data.read).toBe('function');
expect(typeof api.storage.data.write).toBe('function');
expect(typeof api.ui.openSettings).toBe('function');
expect(typeof api.capabilities.list).toBe('function');
expect(typeof api.commands.register).toBe('function');
expect(typeof api.commands.execute).toBe('function');
@ -211,6 +212,20 @@ describe('VerstakPluginAPI contract', () => {
await expect(api.storage.data.read('missing')).resolves.toEqual({});
});
test('ui openSettings publishes the requested settings target in the mock API namespace', async () => {
const api = createMockPluginAPI('ui.plugin');
const received: unknown[] = [];
const unsubscribe = await api.events.subscribe('ui.openSettings', (event) => {
received.push(event.payload);
});
await api.ui.openSettings('ui.plugin.settings');
expect(received).toEqual([{ pluginId: 'ui.plugin', panelId: 'ui.plugin.settings' }]);
unsubscribe();
});
test('commands register, execute, and unregister', async () => {
const api = createMockPluginAPI('cmd.plugin');

View File

@ -104,6 +104,10 @@ export interface VerstakPluginAPI {
};
};
ui: {
openSettings(panelId?: string): Promise<void>;
};
capabilities: {
has(capability: string): Promise<boolean>;
get(capability: string): Promise<{ available: boolean; name?: string; pluginId?: string; status?: string }>;

View File

@ -138,6 +138,17 @@ export function createMockPluginAPI(pluginId = 'test.plugin', options: MockPlugi
}),
},
},
ui: {
openSettings: vi.fn(async (panelId?: string) => {
const event = {
name: 'ui.openSettings',
pluginId,
payload: { pluginId, panelId: panelId || '' },
timestamp: new Date().toISOString(),
};
(eventHandlers.get('ui.openSettings') || []).slice().forEach((handler) => handler(event));
}),
},
capabilities: {
has: vi.fn(async () => false),
get: vi.fn(async (name: string) => ({ available: false, name })),