feat(layout): supporte la création de layouts plugins (customPluginLayout)

Étend le flux de création de layout backend (DTO, usecases, store) et le
frontend (sélecteur, adaptateurs, LayoutTabs/LayoutGrid) pour permettre
d'ouvrir un layout déclaré par un plugin installé (ex. Android Health),
sans passer par le message bloquant "extension backend pas encore livrée".

Ticket #141 — QA vert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-03 18:34:21 +02:00
parent 168f93df78
commit 45617992ef
29 changed files with 907 additions and 99 deletions

View File

@ -27,6 +27,7 @@ import type {
LayoutList,
LayoutOperation,
LayoutTree,
PluginLayoutOrigin,
LocalModelServerConfig,
ModelServerCommandPreview,
Memory,
@ -958,6 +959,7 @@ interface MockLayoutEntry {
id: string;
name: string;
kind: LayoutKind;
pluginOrigin?: PluginLayoutOrigin | null;
tree: LayoutTree;
}
@ -1028,14 +1030,38 @@ export class MockLayoutGateway implements LayoutGateway {
async listLayouts(projectId: string): Promise<LayoutList> {
const ps = this.getProjectLayouts(projectId);
const layouts: LayoutInfo[] = ps.layouts.map((l) => ({ id: l.id, name: l.name, kind: l.kind }));
const layouts: LayoutInfo[] = ps.layouts.map((l) => ({
id: l.id,
name: l.name,
kind: l.kind,
pluginOrigin: l.pluginOrigin,
}));
return { layouts, activeId: ps.activeId };
}
async createLayout(projectId: string, name: string, kind: LayoutKind = "terminal"): Promise<{ layoutId: string }> {
async createLayout(
projectId: string,
name: string,
kind: LayoutKind = "terminal",
pluginOrigin?: PluginLayoutOrigin,
): Promise<{ layoutId: string }> {
const ps = this.getProjectLayouts(projectId);
const layoutId = `layout-${Math.random().toString(36).slice(2, 10)}`;
ps.layouts.push({ id: layoutId, name, kind, tree: singleLeafTree() });
const tree =
kind === "plugin" && pluginOrigin
? {
root: {
type: "customPluginLayout" as const,
node: {
id: `plugin-cell-${Math.random().toString(36).slice(2, 10)}`,
pluginId: pluginOrigin.pluginId,
layoutType: pluginOrigin.layoutType,
state: null,
},
},
}
: singleLeafTree();
ps.layouts.push({ id: layoutId, name, kind, pluginOrigin: pluginOrigin ?? null, tree });
return { layoutId };
}