























@@ -220,6 +220,8 @@ export class OpenClawApp extends LitElement {
220220 @state() realtimeTalkTranscript: string | null = null;
221221private realtimeTalkSession: RealtimeTalkSession | null = null;
222222 @state() chatManualRefreshInFlight = false;
223+ @state() chatMobileControlsOpen = false;
224+private chatMobileControlsTrigger: HTMLElement | null = null;
223225 @state() navDrawerOpen = false;
224226225227onSlashAction?: (action: string) => void;
@@ -578,6 +580,23 @@ export class OpenClawApp extends LitElement {
578580}
579581}
580582};
583+private chatMobileControlsKeydownHandler = (e: KeyboardEvent) => {
584+if (e.key !== "Escape" || !this.chatMobileControlsOpen) {
585+return;
586+}
587+e.preventDefault();
588+this.setChatMobileControlsOpen(false, { restoreFocus: true });
589+};
590+private chatMobileControlsPointerdownHandler = (e: Event) => {
591+if (!this.chatMobileControlsOpen) {
592+return;
593+}
594+const wrapper = this.querySelector(".chat-mobile-controls-wrapper");
595+if (wrapper && e.composedPath().includes(wrapper)) {
596+return;
597+}
598+this.setChatMobileControlsOpen(false);
599+};
581600582601createRenderRoot() {
583602return this;
@@ -603,6 +622,8 @@ export class OpenClawApp extends LitElement {
603622}
604623};
605624document.addEventListener("keydown", this.globalKeydownHandler);
625+document.addEventListener("keydown", this.chatMobileControlsKeydownHandler);
626+document.addEventListener("pointerdown", this.chatMobileControlsPointerdownHandler);
606627handleConnected(this as unknown as Parameters<typeof handleConnected>[0]);
607628void this.initWebPushState();
608629}
@@ -613,12 +634,19 @@ export class OpenClawApp extends LitElement {
613634614635disconnectedCallback() {
615636document.removeEventListener("keydown", this.globalKeydownHandler);
637+document.removeEventListener("keydown", this.chatMobileControlsKeydownHandler);
638+document.removeEventListener("pointerdown", this.chatMobileControlsPointerdownHandler);
639+this.chatMobileControlsTrigger = null;
616640handleDisconnected(this as unknown as Parameters<typeof handleDisconnected>[0]);
617641super.disconnectedCallback();
618642}
619643620644protected updated(changed: Map<PropertyKey, unknown>) {
621645handleUpdated(this as unknown as Parameters<typeof handleUpdated>[0], changed);
646+// Some render callbacks assign tab directly while preparing nested panel state.
647+if (changed.has("tab") && this.tab !== "chat" && this.chatMobileControlsOpen) {
648+this.setChatMobileControlsOpen(false);
649+}
622650if (!changed.has("sessionKey") || this.agentsPanel !== "tools") {
623651return;
624652}
@@ -693,9 +721,35 @@ export class OpenClawApp extends LitElement {
693721694722setTab(next: Tab) {
695723setTabInternal(this as unknown as Parameters<typeof setTabInternal>[0], next);
724+if (next !== "chat") {
725+this.setChatMobileControlsOpen(false);
726+}
696727this.navDrawerOpen = false;
697728}
698729730+setChatMobileControlsOpen(
731+open: boolean,
732+options?: { trigger?: HTMLElement | null; restoreFocus?: boolean },
733+) {
734+if (open) {
735+this.chatMobileControlsTrigger = options?.trigger ?? this.chatMobileControlsTrigger;
736+this.chatMobileControlsOpen = true;
737+return;
738+}
739+740+const focusTarget = options?.restoreFocus ? this.chatMobileControlsTrigger : null;
741+this.chatMobileControlsOpen = false;
742+this.chatMobileControlsTrigger = null;
743+if (!(focusTarget instanceof HTMLElement) || !focusTarget.isConnected) {
744+return;
745+}
746+requestAnimationFrame(() => {
747+if (focusTarget.isConnected) {
748+focusTarget.focus();
749+}
750+});
751+}
752+699753setTheme(next: ThemeName, context?: Parameters<typeof setThemeInternal>[2]) {
700754setThemeInternal(this as unknown as Parameters<typeof setThemeInternal>[0], next, context);
701755this.themeOrder = this.buildThemeOrder(next);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。