fix(windows): fenêtres de panneau détachées suivent le projet en focus côté UI (#47)
Partie frontend. Ajoute un adaptateur focusedProject et un port dédié : la ViewWindow détachée n'est plus liée à un project_id figé, elle s'abonne à l'event focused-project émis par la fenêtre principale et affiche le panneau du projet courant. ProjectsView propage le focus ; le détachement crée une fenêtre panel-only. Couvert par les tests window/ViewWindow/focusedProject/ ProjectsView.focus. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -937,40 +937,84 @@ export interface TicketGateway {
|
||||
* Payload of the OS window-lifecycle event that fires when a detached view
|
||||
* window closes (ticket #23). Lets the main window re-toggle the view's
|
||||
* placement out of `"detached"`.
|
||||
*
|
||||
* Ticket #47: a detached window is now **panel-only** and follows the main
|
||||
* window's focused project rather than embedding a project id, so the close
|
||||
* event no longer carries a `projectId` — the placement is keyed by `panel`.
|
||||
*/
|
||||
export interface ViewWindowClosed {
|
||||
/** The detached panel id (a `PanelId`, kept as `string` at the port seam). */
|
||||
panel: string;
|
||||
/** The project the detached window was showing. */
|
||||
projectId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaching a View into its own OS window (ticket #23).
|
||||
* Detaching a View into its own OS window (ticket #23, reworked in #47).
|
||||
*
|
||||
* The backend owns the Tauri `WebviewWindow` lifecycle (create/focus/close,
|
||||
* anti-duplicate registry). This port is the UI's only door to it — components
|
||||
* never call `invoke()` directly. `panel` is a `PanelId`-shaped string; it is
|
||||
* typed as `string` here so ports need not depend on the `features/` layer.
|
||||
*
|
||||
* A detached window is **panel-only**: it no longer carries a project id and
|
||||
* instead follows the focused project published through
|
||||
* {@link FocusedProjectGateway}. Windows are therefore keyed by `panel` alone.
|
||||
*/
|
||||
export interface WindowGateway {
|
||||
/**
|
||||
* Opens — or focuses, if already open — a separate OS window rendering only
|
||||
* `panel` for `projectId` (the backend's `open_view_window` command).
|
||||
* `panel` (the backend's `open_view_window` command). The window follows the
|
||||
* main window's focused project; no project id is passed.
|
||||
*/
|
||||
openViewWindow(panel: string, projectId: string): Promise<void>;
|
||||
/** Closes the detached window for `panel`/`projectId` if one is open. */
|
||||
closeViewWindow(panel: string, projectId: string): Promise<void>;
|
||||
openViewWindow(panel: string): Promise<void>;
|
||||
/** Closes the detached window for `panel` if one is open. */
|
||||
closeViewWindow(panel: string): Promise<void>;
|
||||
/**
|
||||
* Subscribes to detached-window close events (OS close or programmatic). The
|
||||
* handler receives the `{ panel, projectId }` that closed. Returns an
|
||||
* unsubscribe.
|
||||
* handler receives the `{ panel }` that closed. Returns an unsubscribe.
|
||||
*/
|
||||
onViewWindowClosed(
|
||||
handler: (event: ViewWindowClosed) => void,
|
||||
): Promise<Unsubscribe>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A focused-project snapshot (ticket #47). Mirrors the backend
|
||||
* `FocusedProjectDto` — the minimal identity a panel-only window needs to mount
|
||||
* a project-scoped view (id for the read-models, name for chrome, root as the
|
||||
* agents panel's cwd base).
|
||||
*/
|
||||
export interface FocusedProject {
|
||||
id: string;
|
||||
name: string;
|
||||
root: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The focused-project channel between the main window and detached panel-only
|
||||
* windows (ticket #47).
|
||||
*
|
||||
* The main window is the single writer: it publishes the currently active
|
||||
* project (or `null` when none is open) via {@link setFocusedProject}. Detached
|
||||
* windows are readers: they read the current focus at mount
|
||||
* ({@link getFocusedProject}) then track changes via
|
||||
* {@link onFocusedProjectChanged}. This decouples a restored panel window from
|
||||
* any stale embedded project id — with no focus it shows an "open a project"
|
||||
* shell and mounts no project-scoped panel.
|
||||
*/
|
||||
export interface FocusedProjectGateway {
|
||||
/** Publishes the focused project (or `null` when no project is active). */
|
||||
setFocusedProject(project: FocusedProject | null): Promise<void>;
|
||||
/** Reads the current focused project; `null` when none is focused. */
|
||||
getFocusedProject(): Promise<FocusedProject | null>;
|
||||
/**
|
||||
* Subscribes to focused-project changes. The handler receives the new focus
|
||||
* (or `null`). Returns an unsubscribe.
|
||||
*/
|
||||
onFocusedProjectChanged(
|
||||
handler: (project: FocusedProject | null) => void,
|
||||
): Promise<Unsubscribe>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Thin UI-preferences port (ticket #29). Persists small, per-surface pieces of
|
||||
* UI state (like the ticket filters) so they survive a restart / reopen. This is
|
||||
@ -1018,5 +1062,6 @@ export interface Gateways {
|
||||
conversation: ConversationGateway;
|
||||
ticket: TicketGateway;
|
||||
window: WindowGateway;
|
||||
focusedProject: FocusedProjectGateway;
|
||||
uiPreferences: UiPreferencesGateway;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user