Files
openclaw/ui/src/components/app-sidebar-base.ts
T
2026-08-02 18:32:19 +01:00

122 lines
5.6 KiB
TypeScript

import { consume } from "@lit/context";
import { property } from "lit/decorators.js";
import type { UpdateAvailable } from "../api/types.ts";
import { DEFAULT_SIDEBAR_ENTRIES, type NavigationRouteId } from "../app-navigation.ts";
import type { RouteId } from "../app-route-paths.ts";
import { selectApplicationSession } from "../app/agent-selection.ts";
import {
applicationContext,
type ApplicationContext,
type ApplicationNavigationOptions,
} from "../app/context.ts";
import type { CatalogOpenTarget } from "../app/settings.ts";
import type { ThemeMode } from "../app/theme.ts";
import { readSessionMethodAccess, type SessionMethodAccess } from "../lib/session-method-access.ts";
import { prepareSessionNavigationHandoff } from "../lib/sessions/navigation-handoff.ts";
import { SESSION_NAVIGATION_KEY_PARAM } from "../lib/sessions/route-navigation.ts";
import { parseAgentSessionKey } from "../lib/sessions/session-key.ts";
import { OpenClawLightDomContentsElement } from "../lit/openclaw-element.ts";
import type { NewSessionTarget } from "../pages/new-session/location.ts";
import type { SidebarWorkboardBoard, SidebarWorkboardRenderers } from "./app-sidebar-workboard.ts";
/** Stable custom-element inputs. Behavior is layered in focused sidebar modules. */
export abstract class AppSidebarBase extends OpenClawLightDomContentsElement {
@property({ attribute: false }) basePath = "";
@property({ attribute: false }) activeRouteId?: NavigationRouteId;
@property({ attribute: false }) activePluginTabId = "";
@property({ attribute: false }) activeWorkboardBoardId = "";
@property({ attribute: false }) enabledRouteIds?: readonly NavigationRouteId[];
@property({ attribute: false }) connected = false;
@property({ attribute: false }) offline = false;
@property({ attribute: false }) outboxCountForSession: (sessionKey: string) => number = () => 0;
@property({ attribute: false }) terminalAvailable = false;
@property({ attribute: false }) catalogOpenTarget: CatalogOpenTarget = "viewer";
@property({ attribute: false }) canPairDevice = false;
@property({ attribute: false }) sessionKey = "";
@property({ attribute: false }) sidebarEntries: readonly string[] = DEFAULT_SIDEBAR_ENTRIES;
@property({ attribute: false }) workboardBoards: readonly SidebarWorkboardBoard[] = [];
@property({ attribute: false }) workboardBoardsReady = false;
@property({ attribute: false }) workboardRenderers?: SidebarWorkboardRenderers;
@property({ attribute: false }) sidebarLiveActivity = true;
/** Agents surfaced first in the chip quick switcher when many exist. */
@property({ attribute: false }) pinnedAgentIds: readonly string[] = [];
@property({ attribute: false }) themeMode: ThemeMode = "system";
@property({ attribute: false }) lobsterPetVisits = true;
@property({ attribute: false }) lobsterPetSounds = false;
@property({ attribute: false }) gatewayVersion: string | null = null;
@property({ attribute: false }) devGitBranch: string | null = null;
@property({ attribute: false }) updateAvailable: UpdateAvailable | null = null;
@property({ attribute: false }) updateRunning = false;
@property({ attribute: false }) onUpdate: () => void = () => undefined;
@property({ attribute: false }) onOpenApprovals?: () => void;
@property({ attribute: false }) onRetryConnect?: () => void;
@property({ attribute: false }) onOpenNewSession?: (
agentId: string,
target?: NewSessionTarget,
) => void;
/** Agent id of the in-flight new-session draft; renders the draft row. */
@property({ attribute: false }) draftSessionAgentId = "";
@property({ attribute: false }) onUpdateSidebarEntries?: (entries: string[]) => void;
@property({ attribute: false }) onPairMobile?: () => void;
@property({ attribute: false })
onNavigate?: (routeId: NavigationRouteId, options?: ApplicationNavigationOptions) => void;
@property({ attribute: false }) onPreloadRoute?: (routeId: NavigationRouteId) => Promise<void>;
@consume({ context: applicationContext, subscribe: true })
protected context?: ApplicationContext<RouteId>;
protected setApplicationSession(sessionKey: string, fallbackAgentId?: string): void {
const context = this.context;
if (!context) {
return;
}
selectApplicationSession({
selection: context.agentSelection,
gateway: context.gateway,
sessionKey,
agentId: parseAgentSessionKey(sessionKey)?.agentId ?? fallbackAgentId,
});
}
prepareSessionNavigation(sessionKey: string, pathname: string): void {
if (this.context) {
prepareSessionNavigationHandoff(this.context.gateway, pathname, sessionKey);
}
}
protected bindLiteralSession(
sessionKey: string,
fallbackAgentId: string,
options: ApplicationNavigationOptions,
): void {
if (!new URLSearchParams(options.search ?? "").has(SESSION_NAVIGATION_KEY_PARAM)) {
this.setApplicationSession(sessionKey, fallbackAgentId);
}
}
readNewSessionAccess(): SessionMethodAccess {
return readSessionMethodAccess(this.connected ? this.context?.gateway.snapshot : null, {
method: "sessions.create",
params: {},
});
}
readSessionMutationAccess(request: {
method: string;
params?: unknown;
requiredScope?: "operator.write" | "operator.admin";
}): SessionMethodAccess {
return readSessionMethodAccess(this.connected ? this.context?.gateway.snapshot : null, request);
}
requestOpenNewSession(agentId: string, target?: NewSessionTarget): void {
if (this.readNewSessionAccess().allowed) {
if (target) {
this.onOpenNewSession?.(agentId, target);
} else {
this.onOpenNewSession?.(agentId);
}
}
}
}