From 557a8aeab03fdc55cacdb366f2d0bdbcc6f1784b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 12 Aug 2026 08:00:21 -0700 Subject: [PATCH] refactor(ui): delete the orphaned custodian panel toggle contract (#122663) Removing the Ask OpenClaw button from the chat workspace rail (#122507) left CUSTODIAN_PANEL_TOGGLE_EVENT with no production dispatcher, so its event constant, detail type, panel listener, handleToggleRequest, toggle(), the shell's deferred-load handler and its forwarding delegate were all dead. The floating panel keeps its real open path: app-shell-navigation raises minimizeRequestId when the operator leaves the /custodian route mid-conversation and the panel opens itself when the store has a real user turn. Preloading is unchanged and still gated on openclaw.chat, so the panel mounts exactly when it could be available. Tests move onto that surviving path rather than being deleted; the unused custodian.panel.toggle string goes with the contract. Production LOC: -64, none added. --- ui/src/app/app-host.test.ts | 12 ------ ui/src/app/app-host.ts | 2 - ui/src/app/app-shell-chrome.ts | 15 ------- .../custodian/custodian-panel.test.ts | 22 ++++++---- .../components/custodian/custodian-panel.ts | 40 ------------------- ui/src/components/panel-toggle-contract.ts | 6 --- ui/src/i18n/locales/en.ts | 1 - 7 files changed, 15 insertions(+), 83 deletions(-) diff --git a/ui/src/app/app-host.test.ts b/ui/src/app/app-host.test.ts index 6e6d31af9714..f50a80504366 100644 --- a/ui/src/app/app-host.test.ts +++ b/ui/src/app/app-host.test.ts @@ -11,7 +11,6 @@ import { } from "../components/command-palette-contract.ts"; import { BROWSER_PANEL_TOGGLE_EVENT, - CUSTODIAN_PANEL_TOGGLE_EVENT, TERMINAL_PANEL_TOGGLE_EVENT, UI_COMMAND_EVENT, } from "../components/panel-toggle-contract.ts"; @@ -87,9 +86,7 @@ type TestOptionalCustomElement = { type ShellLazySurfaceState = ShellKeyboardState & { browserPanelElement: TestOptionalCustomElement; commandPaletteElement: TestOptionalCustomElement; - custodianPanelElement: TestOptionalCustomElement; handleDeferredBrowserToggle: (event: Event) => void; - handleDeferredCustodianToggle: (event: Event) => void; handleDeferredTerminalToggle: (event: Event) => void; terminalPanelElement: TestOptionalCustomElement; }; @@ -869,14 +866,11 @@ describe("OpenClaw shell keyboard shortcuts", () => { it("delivers first panel toggles after their lazy modules load", async () => { const terminalElement = createLazyElementSpec("terminal panel"); const browserElement = createLazyElementSpec("browser panel"); - const custodianElement = createLazyElementSpec("custodian panel"); const terminalToggle = vi.fn(); const browserToggle = vi.fn(); - const custodianToggle = vi.fn(); const shell = document.createElement("openclaw-app-shell") as unknown as ShellLazySurfaceState; shell.terminalPanelElement = terminalElement; shell.browserPanelElement = browserElement; - shell.custodianPanelElement = custodianElement; shell.runtime = { context: { gateway: { @@ -904,9 +898,6 @@ describe("OpenClaw shell keyboard shortcuts", () => { if (selector === browserElement.tagName) { return { handleToggleRequest: browserToggle }; } - if (selector === custodianElement.tagName) { - return { handleToggleRequest: custodianToggle }; - } return null; }, }); @@ -914,16 +905,13 @@ describe("OpenClaw shell keyboard shortcuts", () => { detail: { dock: "right", open: true }, }); const browserEvent = new CustomEvent(BROWSER_PANEL_TOGGLE_EVENT); - const custodianEvent = new CustomEvent(CUSTODIAN_PANEL_TOGGLE_EVENT); shell.handleDeferredTerminalToggle(terminalEvent); shell.handleDeferredBrowserToggle(browserEvent); - shell.handleDeferredCustodianToggle(custodianEvent); await vi.waitFor(() => { expect(terminalToggle).toHaveBeenCalledWith(terminalEvent); expect(browserToggle).toHaveBeenCalledWith(browserEvent); - expect(custodianToggle).toHaveBeenCalledWith(custodianEvent); }); }); diff --git a/ui/src/app/app-host.ts b/ui/src/app/app-host.ts index c6da27c0b722..5cb3e026eac7 100644 --- a/ui/src/app/app-host.ts +++ b/ui/src/app/app-host.ts @@ -477,8 +477,6 @@ class OpenClawShell this.shellChrome.handleDeferredTerminalToggle(event); readonly handleDeferredBrowserToggle = (event: Event) => this.shellChrome.handleDeferredBrowserToggle(event); - readonly handleDeferredCustodianToggle = (event: Event) => - this.shellChrome.handleDeferredCustodianToggle(event); readonly handleCommandPaletteSlashCommand = (command: string) => this.shellChrome.handleCommandPaletteSlashCommand(command); diff --git a/ui/src/app/app-shell-chrome.ts b/ui/src/app/app-shell-chrome.ts index d8980a7a553c..6b695c4a825b 100644 --- a/ui/src/app/app-shell-chrome.ts +++ b/ui/src/app/app-shell-chrome.ts @@ -12,7 +12,6 @@ import { import type { OpenClawModalDialog } from "../components/modal-dialog.ts"; import { BROWSER_PANEL_TOGGLE_EVENT, - CUSTODIAN_PANEL_TOGGLE_EVENT, DESKTOP_PANEL_TOGGLE_EVENT, isTerminalPanelShortcut, TERMINAL_PANEL_TOGGLE_EVENT, @@ -72,7 +71,6 @@ export interface ShellChromeHost extends HTMLElement { readonly terminalPanelElement: OptionalCustomElement; readonly browserPanelElement: OptionalCustomElement; readonly desktopPanelElement: OptionalCustomElement; - readonly custodianPanelElement: OptionalCustomElement; readonly execApprovalElement: OptionalCustomElement; readonly commandPalette: CommandPaletteElement | undefined; readonly approvalOverlay: (HTMLElement & { show(): void }) | undefined; @@ -117,7 +115,6 @@ export class ShellChromeOwner { window.addEventListener(TERMINAL_PANEL_TOGGLE_EVENT, this.handleDeferredTerminalToggle); window.addEventListener(BROWSER_PANEL_TOGGLE_EVENT, this.handleDeferredBrowserToggle); window.addEventListener(DESKTOP_PANEL_TOGGLE_EVENT, this.handleDeferredDesktopToggle); - window.addEventListener(CUSTODIAN_PANEL_TOGGLE_EVENT, this.handleDeferredCustodianToggle); } disconnect(): void { @@ -138,7 +135,6 @@ export class ShellChromeOwner { window.removeEventListener(TERMINAL_PANEL_TOGGLE_EVENT, this.handleDeferredTerminalToggle); window.removeEventListener(BROWSER_PANEL_TOGGLE_EVENT, this.handleDeferredBrowserToggle); window.removeEventListener(DESKTOP_PANEL_TOGGLE_EVENT, this.handleDeferredDesktopToggle); - window.removeEventListener(CUSTODIAN_PANEL_TOGGLE_EVENT, this.handleDeferredCustodianToggle); } toggleNavigationSurface(trigger?: HTMLElement): void { @@ -513,17 +509,6 @@ export class ShellChromeOwner { this.deliverPanelEventAfterLoad(host.desktopPanelElement, event); }; - readonly handleDeferredCustodianToggle = (event: Event): void => { - const host = this.host; - if (isOptionalElementDefined(host.custodianPanelElement)) { - return; - } - const snapshot = host.context?.gateway?.snapshot; - if (snapshot && isGatewayMethodAdvertised(snapshot, "openclaw.chat") === true) { - this.deliverPanelEventAfterLoad(host.custodianPanelElement, event); - } - }; - readonly handleCommandPaletteSlashCommand = (command: string): void => { const host = this.host; const chatHandler = host.commandPaletteTarget?.owner.isConnected diff --git a/ui/src/components/custodian/custodian-panel.test.ts b/ui/src/components/custodian/custodian-panel.test.ts index 1c13100e91be..e350f7249c9a 100644 --- a/ui/src/components/custodian/custodian-panel.test.ts +++ b/ui/src/components/custodian/custodian-panel.test.ts @@ -5,7 +5,6 @@ import { createContext } from "../../pages/custodian/custodian-page.test-harness import { CustodianSessionStore } from "../../pages/custodian/custodian-session-store.ts"; import { createApplicationContextProvider } from "../../test-helpers/application-context.ts"; import { createStorageMock } from "../../test-helpers/storage.ts"; -import { CUSTODIAN_PANEL_TOGGLE_EVENT } from "../panel-toggle-contract.ts"; import "./custodian-panel.ts"; type TestCustodianPanel = HTMLElement & { @@ -88,17 +87,23 @@ describe("custodian panel", () => { expect(panel.custodianPanelOpen).toBe(true); }); - it("suppresses the dock on the full page and ignores explicit toggles there", async () => { - const { panel } = await mountPanel(); + it("hides and restores the dock across full-page suppression", async () => { + const { panel, store } = await mountPanel(); + store.messages = [ + { id: 1, role: "user", text: "Check this system", at: 1, question: null, step: null }, + ]; - window.dispatchEvent(new CustomEvent(CUSTODIAN_PANEL_TOGGLE_EVENT, { detail: { open: true } })); + panel.suppressed = false; + panel.minimizeRequestId = 1; + await panel.updateComplete; + expect(panel.custodianPanelOpen).toBe(true); + + panel.suppressed = true; await panel.updateComplete; expect(panel.custodianPanelOpen).toBe(false); panel.suppressed = false; await panel.updateComplete; - window.dispatchEvent(new CustomEvent(CUSTODIAN_PANEL_TOGGLE_EVENT, { detail: { open: true } })); - await panel.updateComplete; expect(panel.custodianPanelOpen).toBe(true); panel.suppressed = true; @@ -155,8 +160,11 @@ describe("custodian panel", () => { it("updates the panel mascot mood with shared sending state", async () => { const { panel, store } = await mountPanel(); + store.messages = [ + { id: 1, role: "user", text: "Check this system", at: 1, question: null, step: null }, + ]; panel.suppressed = false; - window.dispatchEvent(new CustomEvent(CUSTODIAN_PANEL_TOGGLE_EVENT, { detail: { open: true } })); + panel.minimizeRequestId = 1; await panel.updateComplete; store.sending = true; diff --git a/ui/src/components/custodian/custodian-panel.ts b/ui/src/components/custodian/custodian-panel.ts index 96349f84ec27..02076252c335 100644 --- a/ui/src/components/custodian/custodian-panel.ts +++ b/ui/src/components/custodian/custodian-panel.ts @@ -10,10 +10,6 @@ import { import { DockLayoutController } from "../dock-layout-controller.ts"; import { createDockPanelLayout, type DockPanelSide } from "../dock-panel-layout.ts"; import { icons } from "../icons.ts"; -import { - CUSTODIAN_PANEL_TOGGLE_EVENT, - type CustodianPanelToggleDetail, -} from "../panel-toggle-contract.ts"; import "../../pages/custodian/custodian-surface.ts"; import "../../styles/custodian-panel.css"; @@ -40,7 +36,6 @@ export class OpenClawCustodianPanel extends OpenClawLightDomElement { reservationPrefix: "custodian", isAvailable: () => this.available, }); - private readonly onToggleRequest = (event: Event) => this.handleToggleRequest(event); private handledMinimizeRequestId = 0; private subscribedStore: CustodianSessionStore | null = null; private storeCleanup: (() => void) | null = null; @@ -48,12 +43,10 @@ export class OpenClawCustodianPanel extends OpenClawLightDomElement { override connectedCallback(): void { super.connectedCallback(); this.subscribeToStore(); - window.addEventListener(CUSTODIAN_PANEL_TOGGLE_EVENT, this.onToggleRequest); this.dockLayout.setSuppressed(this.suppressed); } override disconnectedCallback(): void { - window.removeEventListener(CUSTODIAN_PANEL_TOGGLE_EVENT, this.onToggleRequest); this.storeCleanup?.(); this.storeCleanup = null; this.subscribedStore = null; @@ -94,39 +87,6 @@ export class OpenClawCustodianPanel extends OpenClawLightDomElement { this.storeCleanup = this.store.subscribe(() => this.requestUpdate()); } - toggle(): void { - if (!this.available || this.suppressed) { - return; - } - if (this.dockLayout.open) { - this.dockLayout.setOpen(false); - } else { - this.dockLayout.setOpen(true); - } - } - - handleToggleRequest(event: Event): void { - const detail = - event instanceof CustomEvent && typeof event.detail === "object" && event.detail !== null - ? (event.detail as CustodianPanelToggleDetail) - : null; - if (detail?.dock === "right" || detail?.dock === "bottom") { - this.dockLayout.setDock(detail.dock, false); - } - if (detail?.open === false) { - this.dockLayout.setOpen(false); - return; - } - if (detail?.open === true) { - if (!this.available || this.suppressed) { - return; - } - this.dockLayout.setOpen(true); - return; - } - this.toggle(); - } - private setDock(dock: CustodianDock): void { this.dockLayout.setDock(dock); } diff --git a/ui/src/components/panel-toggle-contract.ts b/ui/src/components/panel-toggle-contract.ts index c1e22ee7c86a..7196f7e80639 100644 --- a/ui/src/components/panel-toggle-contract.ts +++ b/ui/src/components/panel-toggle-contract.ts @@ -3,7 +3,6 @@ import type { UiCommandParams } from "@openclaw/gateway-protocol"; export const TERMINAL_PANEL_TOGGLE_EVENT = "openclaw:terminal-toggle"; export const BROWSER_PANEL_TOGGLE_EVENT = "openclaw:browser-toggle"; export const DESKTOP_PANEL_TOGGLE_EVENT = "openclaw:desktop-toggle"; -export const CUSTODIAN_PANEL_TOGGLE_EVENT = "openclaw:custodian-toggle"; export const UI_COMMAND_EVENT = "openclaw:ui-command"; export type UiCommandDetail = UiCommandParams; @@ -31,11 +30,6 @@ export type DesktopPanelToggleDetail = { environmentId?: string; }; -export type CustodianPanelToggleDetail = { - dock?: "bottom" | "right"; - open?: boolean; -}; - export type PanelToggleElement = HTMLElement & { handleToggleRequest: (event: Event) => void; }; diff --git a/ui/src/i18n/locales/en.ts b/ui/src/i18n/locales/en.ts index 64a786302f75..d9c987c29673 100644 --- a/ui/src/i18n/locales/en.ts +++ b/ui/src/i18n/locales/en.ts @@ -2358,7 +2358,6 @@ export const en: TranslationMap = { unsupportedGateway: "Update the Gateway to continue setup with OpenClaw.", panel: { title: "OpenClaw", - toggle: "Ask OpenClaw", close: "Close Ask OpenClaw", resize: "Resize Ask OpenClaw", dockBottom: "Dock Ask OpenClaw at bottom",