fix(frontend,application): lot utilisateur #315 — détachement Paramètres projet, couleurs projet fiables, restauration conversation custom CLI, images post-aller-retour projet, retrait menus Projects/Project Context (QA verte)
- détachement: le panneau Paramètres du projet devient détachable (mapping historique panel=context → projectSettings dans parseViewWindowParams, DETACHABLE/ViewPanelBody alignés, ProjectsView ne court-circuitant plus le refus); Retry d'échec de détachement préservé - couleurs projet: projectTheme publie idea:project-theme-changed localement + BroadcastChannel inter-webviews (fenêtres OS détachées), garde de révision par projet dans ProjectsView/ViewWindow contre les lectures settings en course — plus de flash/retour au bleu par défaut - conversation custom CLI: transcript_restore_expected ne considère plus l'absence de scrollback volatile comme une restauration échouée quand la session rouvre en SessionPlan::None (continuation vide valide); CustomAgentChatView n'affiche plus le faux « conversation liée n'a pas pu être restaurée » - images: conservation des images de conversation après changement de projet puis retour - menus Panneaux: retrait des entrées Projects et Project Context (panelOrder WebWorkspace, création/switch via tab-bar +, contexte projet fusionné dans Paramètres du projet) - QA verte: tests frontend ciblés window/ViewWindow/ProjectSettingsPanel/ProjectsView.detach/projects/CustomAgentChatView/WebDesktopWorkspace + cargo test -p application lifecycle transcript_restore_expected; fichiers .ideai/* (tickets 308-315, index/counter, agents/test, chat-drafts, system-permissions, project-settings.json, attachments/) et scripts/ hors périmètre préservés en worktree; aucun push
This commit is contained in:
@ -29,6 +29,18 @@ describe("TauriWindowGateway (#23)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("maps project settings to the backend's historical context window id", async () => {
|
||||
const gw = new TauriWindowGateway();
|
||||
await gw.openViewWindow("projectSettings");
|
||||
expect(invoke).toHaveBeenLastCalledWith("open_view_window", {
|
||||
panel: "context",
|
||||
});
|
||||
await gw.closeViewWindow("projectSettings");
|
||||
expect(invoke).toHaveBeenLastCalledWith("close_view_window", {
|
||||
panel: "context",
|
||||
});
|
||||
});
|
||||
|
||||
it("opens plugin layout windows through the backend command", async () => {
|
||||
invoke.mockResolvedValueOnce({
|
||||
label: "view-plugin-layout",
|
||||
@ -98,6 +110,11 @@ describe("TauriWindowGateway (#23)", () => {
|
||||
expect(handler).toHaveBeenCalledTimes(1);
|
||||
expect(handler).toHaveBeenCalledWith({ panel: "git" });
|
||||
|
||||
raw?.({
|
||||
payload: { kind: "closed", panel: "context", label: "view-context" },
|
||||
});
|
||||
expect(handler).toHaveBeenLastCalledWith({ panel: "projectSettings" });
|
||||
|
||||
off();
|
||||
expect(unlisten).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -25,6 +25,17 @@ import type {
|
||||
/** The single Tauri event carrying detached-window lifecycle transitions. */
|
||||
const VIEW_WINDOW_LIFECYCLE = "view-window://lifecycle";
|
||||
|
||||
// The backend keeps the historical `context` window identity. Project context
|
||||
// now lives inside `projectSettings`, so the adapter owns the compatibility
|
||||
// alias while the UI consistently uses the current panel id.
|
||||
function toBackendPanel(panel: string): string {
|
||||
return panel === "projectSettings" ? "context" : panel;
|
||||
}
|
||||
|
||||
function fromBackendPanel(panel: string): string {
|
||||
return panel === "context" ? "projectSettings" : panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backend lifecycle payload (discriminated on `kind`). Ticket #47 dropped the
|
||||
* `projectId` field — a detached window is panel-only and follows the focused
|
||||
@ -40,7 +51,7 @@ export class TauriWindowGateway implements WindowGateway {
|
||||
async openViewWindow(panel: string): Promise<void> {
|
||||
// Panel-only window (#47): no project id — the window follows the focused
|
||||
// project published on `focused-project://changed`.
|
||||
await invoke("open_view_window", { panel });
|
||||
await invoke("open_view_window", { panel: toBackendPanel(panel) });
|
||||
}
|
||||
|
||||
async openPluginLayoutWindow(
|
||||
@ -52,13 +63,17 @@ export class TauriWindowGateway implements WindowGateway {
|
||||
}
|
||||
|
||||
async closeViewWindow(panel: string): Promise<void> {
|
||||
await invoke("close_view_window", { panel });
|
||||
await invoke("close_view_window", { panel: toBackendPanel(panel) });
|
||||
}
|
||||
|
||||
async listOpenViewWindows(): Promise<ViewWindowSnapshot[]> {
|
||||
// Backend serialises the snapshots in camelCase (panel/label/visible), so the
|
||||
// wire shape matches the port type directly.
|
||||
return invoke<ViewWindowSnapshot[]>("list_open_view_windows");
|
||||
const snapshots = await invoke<ViewWindowSnapshot[]>("list_open_view_windows");
|
||||
return snapshots.map((snapshot) => ({
|
||||
...snapshot,
|
||||
panel: fromBackendPanel(snapshot.panel),
|
||||
}));
|
||||
}
|
||||
|
||||
async onViewWindowClosed(
|
||||
@ -70,7 +85,7 @@ export class TauriWindowGateway implements WindowGateway {
|
||||
VIEW_WINDOW_LIFECYCLE,
|
||||
(e) => {
|
||||
if (e.payload.kind !== "closed") return;
|
||||
handler({ panel: e.payload.panel });
|
||||
handler({ panel: fromBackendPanel(e.payload.panel) });
|
||||
},
|
||||
);
|
||||
return unlisten;
|
||||
|
||||
Reference in New Issue
Block a user