mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 03:45:46 -06:00
d170459c70
* refactor(ui): consolidate dock panel layout
* refactor(ui): centralize realtime camera lifecycle
* refactor(ui): centralize async gateway scopes
* refactor(ui): centralize stream auto-follow
* refactor(ui): centralize dropdown menu lifecycle
* Revert "refactor(ui): centralize async gateway scopes"
This reverts commit 04f120b0e2.
* fix(ui): align controller refactor checks
104 lines
3.7 KiB
TypeScript
104 lines
3.7 KiB
TypeScript
import { html } from "lit";
|
|
import { property } from "lit/decorators.js";
|
|
import { t } from "../i18n/index.ts";
|
|
import { OpenClawLightDomElement } from "../lit/openclaw-element.ts";
|
|
import { DropdownMenuController } from "./dropdown-menu-controller.ts";
|
|
import { icons } from "./icons.ts";
|
|
import { promoteToPopoverTopLayer } from "./menu-surface.ts";
|
|
import "./web-awesome.ts";
|
|
|
|
export type CatalogSessionMenuAction = "viewer" | "terminal";
|
|
|
|
class CatalogSessionMenu extends OpenClawLightDomElement {
|
|
@property({ attribute: false }) x = 0;
|
|
@property({ attribute: false }) y = 0;
|
|
@property({ attribute: false }) trigger: HTMLElement | null = null;
|
|
@property({ attribute: false }) lastActive = "";
|
|
@property({ attribute: false }) terminalDisabled = false;
|
|
@property({ attribute: false }) onAction: (action: CatalogSessionMenuAction) => void = () => {};
|
|
@property({ attribute: false }) onClose: () => void = () => {};
|
|
readonly menuLifecycle = new DropdownMenuController(this, {
|
|
getTrigger: () => this.trigger,
|
|
onClose: () => this.onClose(),
|
|
});
|
|
|
|
override connectedCallback() {
|
|
super.connectedCallback();
|
|
promoteToPopoverTopLayer(this);
|
|
}
|
|
|
|
private run(action: CatalogSessionMenuAction) {
|
|
// Dispatch while the controller still owns the menu snapshot; close clears it synchronously.
|
|
this.onAction(action);
|
|
this.onClose();
|
|
}
|
|
|
|
private readonly handleSelect = (
|
|
event: CustomEvent<{ item: { value?: CatalogSessionMenuAction } }>,
|
|
) => {
|
|
event.preventDefault();
|
|
const action = event.detail.item.value;
|
|
if (action) {
|
|
this.run(action);
|
|
}
|
|
};
|
|
|
|
private readonly handleAfterHide = (event: Event) => {
|
|
if (event.currentTarget instanceof Node && event.currentTarget.isConnected) {
|
|
this.onClose();
|
|
}
|
|
};
|
|
|
|
override render() {
|
|
const menuWidth = 240;
|
|
const menuMaxHeight = 140;
|
|
const x = Math.max(8, Math.min(this.x, window.innerWidth - menuWidth - 8));
|
|
const y = Math.max(8, Math.min(this.y, window.innerHeight - menuMaxHeight - 8));
|
|
const menuLabel = t("chat.catalog.sessionMenu");
|
|
return html`
|
|
<wa-dropdown
|
|
class="session-menu"
|
|
.open=${true}
|
|
placement="bottom-start"
|
|
.distance=${0}
|
|
aria-label=${menuLabel}
|
|
@wa-select=${this.handleSelect}
|
|
@wa-after-hide=${this.handleAfterHide}
|
|
>
|
|
<button
|
|
slot="trigger"
|
|
type="button"
|
|
tabindex="-1"
|
|
aria-hidden="true"
|
|
aria-label=${menuLabel}
|
|
style="position: fixed; left: ${x}px; top: ${y}px; width: 1px; height: 1px; opacity: 0; pointer-events: none;"
|
|
></button>
|
|
${this.lastActive
|
|
? html`<div class="session-menu__info">
|
|
${t("sessionsView.lastActive", { time: this.lastActive })}
|
|
</div>`
|
|
: ""}
|
|
<wa-dropdown-item class="session-menu__item" value="viewer">
|
|
<span slot="icon" class="session-menu__icon" aria-hidden="true"
|
|
>${icons.messageSquare}</span
|
|
>
|
|
<span class="session-menu__text">${t("chat.catalog.openInOpenClaw")}</span>
|
|
</wa-dropdown-item>
|
|
<wa-dropdown-item
|
|
class="session-menu__item"
|
|
value="terminal"
|
|
title=${this.terminalDisabled ? t("chat.catalog.terminalUnavailable") : ""}
|
|
?disabled=${this.terminalDisabled}
|
|
>
|
|
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.terminal}</span>
|
|
<span class="session-menu__text">${t("chat.catalog.openInTerminal")}</span>
|
|
</wa-dropdown-item>
|
|
</wa-dropdown>
|
|
`;
|
|
}
|
|
}
|
|
|
|
if (!customElements.get("openclaw-catalog-session-menu")) {
|
|
customElements.define("openclaw-catalog-session-menu", CatalogSessionMenu);
|
|
}
|