fix(ui): keep mobile session actions reachable (#126124)

* fix(ui): keep mobile session actions reachable

Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>

* test(ui): use placement startup capability

Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>

---------

Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
ClawSweeper
2026-08-18 19:46:34 -07:00
committed by GitHub
parent 2a469c1cb2
commit bf420e92c6
5 changed files with 363 additions and 169 deletions
+45 -32
View File
@@ -7,6 +7,13 @@ import { syncDropdownItemRadio } from "./web-awesome.ts";
type SessionOwnerAssignment = Pick<SessionOwnerOption, "type" | "id">;
type SessionOwnerMenuParams = {
ownerOptions: readonly SessionOwnerOption[];
currentOwnerId: string | null;
disabled: boolean;
disabledReason?: string;
};
export function sessionOwnerAssignmentFromMenuValue(value: string): SessionOwnerAssignment | null {
if (!value.startsWith("assign-owner:")) {
return null;
@@ -16,13 +23,43 @@ export function sessionOwnerAssignmentFromMenuValue(value: string): SessionOwner
return (type === "human" || type === "agent") && id ? { type, id } : null;
}
export function renderSessionOwnerAssignmentMenu(params: {
ownerOptions: readonly SessionOwnerOption[];
selfOwner: SessionOwnerOption | null;
currentOwnerId: string | null;
disabled: boolean;
disabledReason?: string;
}) {
export function renderSessionOwnerAssignmentOptions(
params: SessionOwnerMenuParams,
inline = false,
) {
const title = params.disabledReason ?? nothing;
return params.ownerOptions.map((owner) => {
const checked = owner.id === params.currentOwnerId;
return html`
<wa-dropdown-item
slot=${inline ? nothing : "submenu"}
class="session-menu__item"
value=${`assign-owner:${owner.type}:${encodeURIComponent(owner.id)}`}
role="menuitemradio"
aria-checked=${String(checked)}
${ref((element) => syncDropdownItemRadio(element, checked))}
?disabled=${params.disabled || checked}
title=${title}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true"
>${renderSessionOwnerMenuAvatar(owner)}</span
>
<span class="session-menu__text">${owner.label ?? owner.id}</span>
${checked
? html`<span slot="details" class="session-menu__check" aria-hidden="true"
>${icons.check}</span
>`
: nothing}
</wa-dropdown-item>
`;
});
}
export function renderSessionOwnerAssignmentMenu(
params: SessionOwnerMenuParams & {
selfOwner: SessionOwnerOption | null;
},
) {
const title = params.disabledReason ?? nothing;
return html`
${params.selfOwner
@@ -44,31 +81,7 @@ export function renderSessionOwnerAssignmentMenu(params: {
>
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.users}</span>
<span class="session-menu__text">${t("sessionsView.assignTo")}</span>
${params.ownerOptions.map((owner) => {
const checked = owner.id === params.currentOwnerId;
return html`
<wa-dropdown-item
slot="submenu"
class="session-menu__item"
value=${`assign-owner:${owner.type}:${encodeURIComponent(owner.id)}`}
role="menuitemradio"
aria-checked=${String(checked)}
${ref((element) => syncDropdownItemRadio(element, checked))}
?disabled=${params.disabled || checked}
title=${title}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true"
>${renderSessionOwnerMenuAvatar(owner)}</span
>
<span class="session-menu__text">${owner.label ?? owner.id}</span>
${checked
? html`<span slot="details" class="session-menu__check" aria-hidden="true"
>${icons.check}</span
>`
: nothing}
</wa-dropdown-item>
`;
})}
${renderSessionOwnerAssignmentOptions(params)}
</wa-dropdown-item>`
: nothing}
`;
@@ -207,22 +207,22 @@ suite.define(() => {
const app = document.querySelector("openclaw-app") as HTMLElement & {
runtime?: {
context: {
cloudStartup: {
placementStartup: {
get: (sessionKey: string) => { error?: string; phase: string } | null;
subscribe: (listener: () => void) => () => void;
};
};
};
};
const cloudStartup = app.runtime?.context.cloudStartup;
if (!cloudStartup) {
reject(new Error("cloud startup runtime unavailable"));
const placementStartup = app.runtime?.context.placementStartup;
if (!placementStartup) {
reject(new Error("session placement startup unavailable"));
return;
}
let settled = false;
const subscription: { stop?: () => void } = {};
const resolveFailed = () => {
const status = cloudStartup.get(key);
const status = placementStartup.get(key);
if (settled || status?.phase !== "failed") {
return;
}
@@ -230,7 +230,7 @@ suite.define(() => {
subscription.stop?.();
resolve(status.error ?? "");
};
subscription.stop = cloudStartup.subscribe(resolveFailed);
subscription.stop = placementStartup.subscribe(resolveFailed);
if (settled) {
subscription.stop();
} else {
@@ -273,10 +273,15 @@ describe("chat header session menu", () => {
]);
});
it("renders quick actions directly in the compact menu", async () => {
it("drills into compact menu groups without rendering side flyouts", async () => {
const showTasks = vi.fn();
const onSettingsChange = vi.fn<(patch: Partial<UiSettings>) => void>();
const onAction = vi.fn<(action: HeaderMenuAction) => void>();
const ada = { type: "human", id: "profile-ada", label: "Ada" } as const;
const research = { type: "agent", id: "research:one", label: "Research" } as const;
const menu = await mountMenu({
compact: true,
worktreePath: "/work/openclaw",
panelActions: [
{
id: "background-tasks",
@@ -286,15 +291,71 @@ describe("chat header session menu", () => {
onActivate: showTasks,
},
],
layoutActions: [
{
id: "split-right",
label: "Split right",
icon: icons.panelRightOpen,
onActivate: vi.fn(),
},
],
ownerOptions: [ada, research],
selfOwner: ada,
currentOwnerId: research.id,
onSettingsChange,
onAction,
});
expect(menu.querySelector(".session-menu__section-label")?.textContent?.trim()).toBe("Panels");
const rootLabels = Array.from(
menu.querySelectorAll<MenuItemElement>(":scope > wa-dropdown > wa-dropdown-item"),
).map(itemLabel);
expect(rootLabels).toEqual([
"Open in",
"Panels",
"Layout",
"Rename…",
"Assign to…",
"View",
"Fork",
"Continue in terminal…",
"Archive session",
"Delete…",
]);
expect(menu.querySelector("[slot='submenu']")).toBeNull();
select(menu, "compact:open-view");
await menu.updateComplete;
expect(
Array.from(
menu.querySelectorAll<MenuItemElement>(":scope > wa-dropdown > wa-dropdown-item"),
).map(itemLabel),
).toEqual(["Back", "Reasoning", "Tool calls", "Keep commentary"]);
expect(menu.querySelector("[slot='submenu']")).toBeNull();
select(menu, "view:reasoning");
expect(onSettingsChange).toHaveBeenCalledWith({ chatShowThinking: false });
select(menu, "compact:back");
await menu.updateComplete;
select(menu, "compact:open-panels");
await menu.updateComplete;
const action = item(menu, "Show background tasks");
expect(action.getAttribute("slot")).toBeNull();
expect(action.querySelector('[slot="details"]')?.textContent?.trim()).toBe("2");
select(menu, "quick:panels:background-tasks");
expect(showTasks).toHaveBeenCalledOnce();
select(menu, "compact:open-assign-owner");
await menu.updateComplete;
expect(
Array.from(
menu.querySelectorAll<MenuItemElement>(":scope > wa-dropdown > wa-dropdown-item"),
).map(itemLabel),
).toEqual(["Back", "Ada", "Research"]);
select(menu, "assign-owner:human:profile-ada");
expect(onAction).toHaveBeenCalledWith({
kind: "assign-owner",
owner: { type: "human", id: "profile-ada" },
});
});
it("pins and disables onboarding view preferences", async () => {
@@ -1,11 +1,12 @@
import { html, nothing, type TemplateResult } from "lit";
import { property } from "lit/decorators.js";
import { property, state } from "lit/decorators.js";
import type { UiSettings } from "../../../app/settings.ts";
import { icons } from "../../../components/icons.ts";
import { activateMenuShortcut, menuShortcutHint } from "../../../components/menu-shortcuts.ts";
import type { SessionOwnerOption } from "../../../components/session-owner-chip.ts";
import {
renderSessionOwnerAssignmentMenu,
renderSessionOwnerAssignmentOptions,
sessionOwnerAssignmentFromMenuValue,
} from "../../../components/session-owner-menu.ts";
import { t } from "../../../i18n/index.ts";
@@ -33,6 +34,17 @@ export type HeaderMenuQuickAction = {
const EMPTY_SETTINGS = {} as UiSettings;
type CompactMenuView = "root" | "open-in" | "panels" | "layout" | "assign-owner" | "view";
const COMPACT_MENU_VIEW_BY_VALUE: Record<string, CompactMenuView> = {
"compact:back": "root",
"compact:open-assign-owner": "assign-owner",
"compact:open-layout": "layout",
"compact:open-open-in": "open-in",
"compact:open-panels": "panels",
"compact:open-view": "view",
};
class ChatHeaderSessionMenu extends OpenClawLightDomElement {
@property({ attribute: false }) sessionLabel = "";
@property({ attribute: false }) worktreePath: string | null = null;
@@ -56,6 +68,7 @@ class ChatHeaderSessionMenu extends OpenClawLightDomElement {
@property({ attribute: false }) onOpen: () => void = () => {};
@property({ attribute: false }) onSettingsChange: (patch: Partial<UiSettings>) => void = () => {};
@property({ attribute: false }) onAction: (action: HeaderMenuAction) => void = () => {};
@state() private compactView: CompactMenuView = "root";
private actionDisabled(kind: HeaderMenuActionKind, extra = false): boolean {
return extra || Boolean(this.actionDisabledReasons[kind]);
@@ -70,6 +83,15 @@ class ChatHeaderSessionMenu extends OpenClawLightDomElement {
if (!value) {
return;
}
const compactView = COMPACT_MENU_VIEW_BY_VALUE[value];
if (compactView) {
event.preventDefault();
this.compactView = compactView;
void this.updateComplete.then(() => {
this.querySelector<HTMLElement>("wa-dropdown-item:not([disabled])")?.focus();
});
return;
}
if (value.startsWith("quick:")) {
const [, group, id] = value.split(":");
const actions = group === "panels" ? this.panelActions : this.layoutActions;
@@ -124,30 +146,33 @@ class ChatHeaderSessionMenu extends OpenClawLightDomElement {
}
};
private renderEditorSubmenu() {
private renderEditorSubmenu(inline = false) {
return EDITOR_IDS.map(
(editor) => html`
<wa-dropdown-item slot="submenu" class="session-menu__item" value=${`open-in:${editor}`}>
<wa-dropdown-item
slot=${inline ? nothing : "submenu"}
class="session-menu__item"
value=${`open-in:${editor}`}
>
<span class="session-menu__text">${EDITOR_LABELS[editor]}</span>
</wa-dropdown-item>
`,
);
}
private renderQuickActions(group: "panels" | "layout", actions: HeaderMenuQuickAction[]) {
if (actions.length === 0) {
return nothing;
}
const label = t(group === "panels" ? "chat.sessionHeader.panels" : "chat.sessionHeader.layout");
const icon = group === "panels" ? icons.panelRightOpen : icons.columns2;
const items = actions.map((action) => {
private renderQuickActionItems(
group: "panels" | "layout",
actions: HeaderMenuQuickAction[],
inline = false,
) {
return actions.map((action) => {
const detail =
typeof action.badge === "number" && action.badge > 0
? html`<span slot="details" class="session-menu__sub">${action.badge}</span>`
: nothing;
return html`
<wa-dropdown-item
slot=${this.compact ? nothing : "submenu"}
slot=${inline ? nothing : "submenu"}
class="session-menu__item"
value=${`quick:${group}:${action.id}`}
type=${action.active === undefined ? nothing : "checkbox"}
@@ -159,30 +184,55 @@ class ChatHeaderSessionMenu extends OpenClawLightDomElement {
</wa-dropdown-item>
`;
});
}
private renderCompactNavigationItem(
view: Exclude<CompactMenuView, "root">,
label: string,
icon: TemplateResult,
disabled = false,
) {
return html`
<wa-dropdown-item
class="session-menu__item"
value=${`compact:open-${view}`}
?disabled=${disabled}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icon}</span>
<span class="session-menu__text">${label}</span>
<span slot="details" class="session-menu__icon session-menu__chevron" aria-hidden="true"
>${icons.chevronRight}</span
>
</wa-dropdown-item>
`;
}
private renderQuickActions(group: "panels" | "layout", actions: HeaderMenuQuickAction[]) {
if (actions.length === 0) {
return nothing;
}
const label = t(group === "panels" ? "chat.sessionHeader.panels" : "chat.sessionHeader.layout");
const icon = group === "panels" ? icons.panelRightOpen : icons.columns2;
if (this.compact) {
return html`
<div class="session-menu__section-label">${label}</div>
${items}
<div class="session-menu__separator" role="separator"></div>
`;
return this.renderCompactNavigationItem(group, label, icon);
}
return html`
<wa-dropdown-item class="session-menu__item">
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icon}</span>
<span class="session-menu__text">${label}</span>
${items}
${this.renderQuickActionItems(group, actions)}
</wa-dropdown-item>
`;
}
private renderViewSubmenu() {
private renderViewSubmenu(inline = false) {
const showThinking = this.onboarding ? false : this.settings.chatShowThinking;
const showToolCalls = this.onboarding ? true : this.settings.chatShowToolCalls;
const persistCommentary = this.settings.chatPersistCommentary !== false;
const disabledTitle = this.onboarding ? t("chat.onboardingDisabled") : nothing;
const item = (value: string, label: string, checked: boolean) => html`
<wa-dropdown-item
slot="submenu"
slot=${inline ? nothing : "submenu"}
class="session-menu__item"
type="checkbox"
value=${`view:${value}`}
@@ -198,13 +248,177 @@ class ChatHeaderSessionMenu extends OpenClawLightDomElement {
${item("tool-calls", t("chat.view.toolCalls"), showToolCalls)}
${item("commentary", t("chat.view.commentary"), persistCommentary)}
${this.preferencesBrowserOnly
? html`<div slot="submenu" class="session-menu__info" role="note">
? html`<div slot=${inline ? nothing : "submenu"} class="session-menu__info" role="note">
${t("quickSettings.personal.browserOnly")}
</div>`
: nothing}
`;
}
private compactOwnerOptions(): readonly SessionOwnerOption[] {
if (!this.selfOwner || this.ownerOptions.some((owner) => owner.id === this.selfOwner?.id)) {
return this.ownerOptions;
}
return [this.selfOwner, ...this.ownerOptions];
}
private renderCompactView() {
const back = html`
<wa-dropdown-item class="session-menu__item session-menu__back" value="compact:back">
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.arrowLeft}</span>
<span class="session-menu__text">${t("common.back")}</span>
</wa-dropdown-item>
<div class="session-menu__separator" role="separator"></div>
`;
const body =
this.compactView === "open-in"
? this.renderEditorSubmenu(true)
: this.compactView === "panels"
? this.renderQuickActionItems("panels", this.panelActions, true)
: this.compactView === "layout"
? this.renderQuickActionItems("layout", this.layoutActions, true)
: this.compactView === "assign-owner"
? renderSessionOwnerAssignmentOptions(
{
ownerOptions: this.compactOwnerOptions(),
currentOwnerId: this.currentOwnerId,
disabled: this.actionDisabled("assign-owner"),
disabledReason: this.actionDisabledReasons["assign-owner"],
},
true,
)
: this.renderViewSubmenu(true);
return html`${back}${body}`;
}
private renderRootView() {
return html`
${this.worktreePath
? html`
${this.compact
? this.renderCompactNavigationItem(
"open-in",
t("sessionsView.openInEditorMenu"),
icons.externalLink,
)
: html`<wa-dropdown-item class="session-menu__item">
<span slot="icon" class="session-menu__icon" aria-hidden="true"
>${icons.externalLink}</span
>
<span class="session-menu__text">${t("sessionsView.openInEditorMenu")}</span>
${this.renderEditorSubmenu()}
</wa-dropdown-item>`}
<div class="session-menu__separator" role="separator"></div>
`
: nothing}
${this.renderQuickActions("panels", this.panelActions)}
${this.renderQuickActions("layout", this.layoutActions)}
<wa-dropdown-item
class="session-menu__item"
value="rename"
data-shortcut="r"
aria-keyshortcuts="R"
?disabled=${this.actionDisabled("rename")}
title=${this.actionTitle("rename")}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.edit}</span>
<span class="session-menu__text">${t("sessionsView.renameSessionMenu")}</span>
${menuShortcutHint("r")}
</wa-dropdown-item>
${this.compact
? this.compactOwnerOptions().length > 0
? this.renderCompactNavigationItem(
"assign-owner",
t("sessionsView.assignTo"),
icons.users,
this.actionDisabled("assign-owner"),
)
: nothing
: renderSessionOwnerAssignmentMenu({
ownerOptions: this.ownerOptions,
selfOwner: this.selfOwner,
currentOwnerId: this.currentOwnerId,
disabled: this.actionDisabled("assign-owner"),
disabledReason: this.actionDisabledReasons["assign-owner"],
})}
${this.compact
? this.renderCompactNavigationItem("view", t("chat.view.menu"), icons.eye)
: html`<wa-dropdown-item class="session-menu__item">
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.eye}</span>
<span class="session-menu__text">${t("chat.view.menu")}</span>
${this.renderViewSubmenu()}
</wa-dropdown-item>`}
<wa-dropdown-item
class="session-menu__item"
value="fork"
data-shortcut="f"
aria-keyshortcuts="F"
?disabled=${this.actionDisabled("fork", this.forkDisabled)}
title=${this.actionTitle("fork")}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.copy}</span>
<span class="session-menu__text"
>${t(
this.forkFromLastCompleted
? "sessionsView.forkFromLastCompleted"
: "sessionsView.forkSession",
)}</span
>
${menuShortcutHint("f")}
</wa-dropdown-item>
<wa-dropdown-item
class="session-menu__item"
value="continue-in-terminal"
?disabled=${this.actionDisabled("continue-in-terminal")}
title=${this.actionTitle("continue-in-terminal")}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.terminal}</span>
<span class="session-menu__text">${t("chat.sessionHeader.continueInTerminal.action")}</span>
</wa-dropdown-item>
<div class="session-menu__separator" role="separator"></div>
<wa-dropdown-item
class="session-menu__item"
value="toggle-archived"
data-shortcut="a"
aria-keyshortcuts="A"
?disabled=${this.actionDisabled("toggle-archived", !this.archived && !this.archiveAllowed)}
title=${this.actionTitle("toggle-archived")}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true"
>${this.archived ? icons.archiveRestore : icons.archive}</span
>
<span class="session-menu__text"
>${this.archived
? t("sessionsView.restoreSession")
: t("sessionsView.archiveSession")}</span
>
${menuShortcutHint("a")}
</wa-dropdown-item>
<wa-dropdown-item
class="session-menu__item session-menu__item--destructive"
value="delete"
variant="danger"
data-shortcut="d"
aria-keyshortcuts="D"
?disabled=${this.actionDisabled("delete", !this.deleteAllowed)}
title=${this.actionTitle("delete")}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.trash}</span>
<span class="session-menu__text">${t("sessionsView.deleteSessionMenu")}</span>
${menuShortcutHint("d")}
</wa-dropdown-item>
`;
}
private readonly handleShow = () => {
this.compactView = "root";
this.onOpen();
};
private readonly handleAfterHide = () => {
this.compactView = "root";
};
override render() {
const menuLabel = t("chat.sidebar.sessionMenu", { session: this.sessionLabel });
return html`
@@ -213,7 +427,8 @@ class ChatHeaderSessionMenu extends OpenClawLightDomElement {
placement="bottom-end"
aria-label=${menuLabel}
@keydown=${(event: KeyboardEvent) => activateMenuShortcut(this, event)}
@wa-show=${this.onOpen}
@wa-show=${this.handleShow}
@wa-after-hide=${this.handleAfterHide}
@wa-select=${this.handleSelect}
>
<button
@@ -225,108 +440,9 @@ class ChatHeaderSessionMenu extends OpenClawLightDomElement {
>
${icons.moreHorizontal}
</button>
${this.worktreePath
? html`
<wa-dropdown-item class="session-menu__item">
<span slot="icon" class="session-menu__icon" aria-hidden="true"
>${icons.externalLink}</span
>
<span class="session-menu__text">${t("sessionsView.openInEditorMenu")}</span>
${this.renderEditorSubmenu()}
</wa-dropdown-item>
<div class="session-menu__separator" role="separator"></div>
`
: nothing}
${this.renderQuickActions("panels", this.panelActions)}
${this.renderQuickActions("layout", this.layoutActions)}
<wa-dropdown-item
class="session-menu__item"
value="rename"
data-shortcut="r"
aria-keyshortcuts="R"
?disabled=${this.actionDisabled("rename")}
title=${this.actionTitle("rename")}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.edit}</span>
<span class="session-menu__text">${t("sessionsView.renameSessionMenu")}</span>
${menuShortcutHint("r")}
</wa-dropdown-item>
${renderSessionOwnerAssignmentMenu({
ownerOptions: this.ownerOptions,
selfOwner: this.selfOwner,
currentOwnerId: this.currentOwnerId,
disabled: this.actionDisabled("assign-owner"),
disabledReason: this.actionDisabledReasons["assign-owner"],
})}
<wa-dropdown-item class="session-menu__item">
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.eye}</span>
<span class="session-menu__text">${t("chat.view.menu")}</span>
${this.renderViewSubmenu()}
</wa-dropdown-item>
<wa-dropdown-item
class="session-menu__item"
value="fork"
data-shortcut="f"
aria-keyshortcuts="F"
?disabled=${this.actionDisabled("fork", this.forkDisabled)}
title=${this.actionTitle("fork")}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.copy}</span>
<span class="session-menu__text"
>${t(
this.forkFromLastCompleted
? "sessionsView.forkFromLastCompleted"
: "sessionsView.forkSession",
)}</span
>
${menuShortcutHint("f")}
</wa-dropdown-item>
<wa-dropdown-item
class="session-menu__item"
value="continue-in-terminal"
?disabled=${this.actionDisabled("continue-in-terminal")}
title=${this.actionTitle("continue-in-terminal")}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.terminal}</span>
<span class="session-menu__text"
>${t("chat.sessionHeader.continueInTerminal.action")}</span
>
</wa-dropdown-item>
<div class="session-menu__separator" role="separator"></div>
<wa-dropdown-item
class="session-menu__item"
value="toggle-archived"
data-shortcut="a"
aria-keyshortcuts="A"
?disabled=${this.actionDisabled(
"toggle-archived",
!this.archived && !this.archiveAllowed,
)}
title=${this.actionTitle("toggle-archived")}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true"
>${this.archived ? icons.archiveRestore : icons.archive}</span
>
<span class="session-menu__text"
>${this.archived
? t("sessionsView.restoreSession")
: t("sessionsView.archiveSession")}</span
>
${menuShortcutHint("a")}
</wa-dropdown-item>
<wa-dropdown-item
class="session-menu__item session-menu__item--destructive"
value="delete"
variant="danger"
data-shortcut="d"
aria-keyshortcuts="D"
?disabled=${this.actionDisabled("delete", !this.deleteAllowed)}
title=${this.actionTitle("delete")}
>
<span slot="icon" class="session-menu__icon" aria-hidden="true">${icons.trash}</span>
<span class="session-menu__text">${t("sessionsView.deleteSessionMenu")}</span>
${menuShortcutHint("d")}
</wa-dropdown-item>
${this.compact && this.compactView !== "root"
? this.renderCompactView()
: this.renderRootView()}
</wa-dropdown>
`;
}
+9 -5
View File
@@ -724,11 +724,15 @@ openclaw-chat-pane {
min-height: 44px;
}
.chat-header-session-menu--compact .session-menu__section-label {
padding: 4px 8px 2px;
color: var(--muted);
font-size: var(--control-ui-text-xs);
font-weight: 650;
.chat-header-session-menu--compact .session-menu__back {
z-index: 1;
position: sticky;
top: 0;
background: var(--wa-color-surface-raised);
}
.chat-header-session-menu--compact .session-menu__chevron {
display: inline-flex;
}
.chat-header-session-menu--compact::part(menu) {