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.
This commit is contained in:
Peter Steinberger
2026-08-12 08:00:21 -07:00
committed by GitHub
parent f9f602a0df
commit 557a8aeab0
7 changed files with 15 additions and 83 deletions
-12
View File
@@ -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);
});
});
-2
View File
@@ -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);
-15
View File
@@ -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
@@ -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;
@@ -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);
}
@@ -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;
};
-1
View File
@@ -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",