Skip to content

Commit 17dd321

Browse files
feat(ui): render MCP Apps in dashboard widgets
1 parent 7ed78c7 commit 17dd321

12 files changed

Lines changed: 1168 additions & 10 deletions

File tree

ui/src/components/board/board-view.test.ts

Lines changed: 378 additions & 0 deletions
Large diffs are not rendered by default.

ui/src/components/board/board-view.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import type {
2525
} from "../../lib/board/view-types.ts";
2626
import { OpenClawLightDomElement } from "../../lit/openclaw-element.ts";
2727
import "../../styles/board.css";
28+
import { McpAppUnmountGate } from "../mcp-app-unmount.ts";
2829
import "../web-awesome-tabs.ts";
2930
import "../web-awesome.ts";
3031
import type { BoardWidgetCellCallbacks } from "./board-widget-cell.ts";
@@ -85,6 +86,7 @@ class OpenClawBoardView extends OpenClawLightDomElement {
8586
private mutationRequestId = 0;
8687
private stableCellOrder = new Map<string, number>();
8788
private stableCellOrderSequence = 0;
89+
private readonly mcpAppUnmountGate = new McpAppUnmountGate(this);
8890

8991
override willUpdate(changed: PropertyValues<this>): void {
9092
if (changed.has("snapshot")) {
@@ -207,6 +209,12 @@ class OpenClawBoardView extends OpenClawLightDomElement {
207209
frameLoadFailed: async (name) => {
208210
await this.callbacks?.frameLoadFailed?.(name);
209211
},
212+
widgetAppView: async (name, revision, refresh) => {
213+
if (!this.callbacks?.widgetAppView) {
214+
return { status: "stale", error: t("board.widget.appUnavailable") };
215+
}
216+
return await this.callbacks.widgetAppView(name, revision, refresh);
217+
},
210218
};
211219

212220
private beginGesture(
@@ -592,7 +600,7 @@ class OpenClawBoardView extends OpenClawLightDomElement {
592600
const activeTab = this.activeTab(tabs);
593601
const activeTabId = activeTab?.tabId ?? this.activeTabId;
594602
const widgets = activeTab ? orderedWidgets(snapshot, activeTab.tabId) : [];
595-
return html`
603+
const rendered = html`
596604
<section class="board-view" aria-label=${t("board.label")}>
597605
${this.renderTabs(tabs, activeTabId)} ${this.renderGrid(widgets, tabs, snapshot.sessionKey)}
598606
${this.actionError
@@ -610,6 +618,18 @@ class OpenClawBoardView extends OpenClawLightDomElement {
610618
</div>
611619
</section>
612620
`;
621+
const nextMcpAppKeys = new Set(
622+
widgets
623+
.filter((widget) => widget.contentKind === "mcp-app")
624+
.map((widget) => `${snapshot.sessionKey}\0${widget.name}`),
625+
);
626+
return this.mcpAppUnmountGate.render(JSON.stringify([...nextMcpAppKeys]), rendered, () =>
627+
[...this.querySelectorAll("openclaw-board-widget-cell")].filter(
628+
(cell) =>
629+
cell.widget?.contentKind === "mcp-app" &&
630+
!nextMcpAppKeys.has(`${cell.sessionKey}\0${cell.widget.name}`),
631+
),
632+
);
613633
}
614634
}
615635

0 commit comments

Comments
 (0)