mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(ui): repair control-ui type drift
This commit is contained in:
@@ -315,6 +315,10 @@ describe("isCronSessionKey", () => {
|
||||
|
||||
describe("resolveSessionOptionGroups", () => {
|
||||
const sessions: SessionsListResult = {
|
||||
ts: 0,
|
||||
path: "",
|
||||
count: 3,
|
||||
defaults: { model: null, contextTokens: null },
|
||||
sessions: [
|
||||
row({ key: "agent:main:main" }),
|
||||
row({ key: "agent:main:cron:daily" }),
|
||||
|
||||
@@ -277,9 +277,10 @@ function resolveAssistantAvatarUrl(state: AppViewState): string | undefined {
|
||||
}
|
||||
|
||||
export function renderApp(state: AppViewState) {
|
||||
const updatableState = state as AppViewState & { requestUpdate?: () => void };
|
||||
const requestHostUpdate =
|
||||
typeof (state as { requestUpdate?: unknown }).requestUpdate === "function"
|
||||
? () => (state as { requestUpdate: () => void }).requestUpdate()
|
||||
typeof updatableState.requestUpdate === "function"
|
||||
? () => updatableState.requestUpdate?.()
|
||||
: undefined;
|
||||
|
||||
// Gate: require successful gateway connection before showing the dashboard.
|
||||
@@ -757,6 +758,7 @@ export function renderApp(state: AppViewState) {
|
||||
error: state.cronError,
|
||||
busy: state.cronBusy,
|
||||
form: state.cronForm,
|
||||
editingJobId: state.cronEditingJobId,
|
||||
channels: state.channelsSnapshot?.channelMeta?.length
|
||||
? state.channelsSnapshot.channelMeta.map((entry) => entry.id)
|
||||
: (state.channelsSnapshot?.channelOrder ?? []),
|
||||
|
||||
@@ -34,8 +34,6 @@ const createHost = (tab: Tab): SettingsHost => ({
|
||||
eventLog: [],
|
||||
eventLogBuffer: [],
|
||||
basePath: "",
|
||||
themeMedia: null,
|
||||
themeMediaHandler: null,
|
||||
logsPollInterval: null,
|
||||
debugPollInterval: null,
|
||||
systemThemeCleanup: null,
|
||||
|
||||
@@ -204,7 +204,8 @@ describe("loadToolsCatalog", () => {
|
||||
expect(state.toolsCatalogResult).toEqual(replacementPayload);
|
||||
expect(state.toolsCatalogLoading).toBe(false);
|
||||
|
||||
resolveMain?.({
|
||||
expect(resolveMain).not.toBeNull();
|
||||
resolveMain!({
|
||||
agentId: "main",
|
||||
profiles: [{ id: "full", label: "Full" }],
|
||||
groups: [],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { UiSettings } from "./storage.ts";
|
||||
|
||||
function createStorageMock(): Storage {
|
||||
const store = new Map<string, string>();
|
||||
@@ -62,7 +63,7 @@ function expectedGatewayUrl(basePath: string): string {
|
||||
return `${proto}://${location.host}${basePath}`;
|
||||
}
|
||||
|
||||
function createSettings(overrides: Record<string, unknown> = {}) {
|
||||
function createSettings(overrides: Partial<UiSettings> = {}): UiSettings {
|
||||
return {
|
||||
gatewayUrl: "wss://gateway.example:8443/openclaw",
|
||||
token: "",
|
||||
|
||||
@@ -410,6 +410,15 @@ export type {
|
||||
SessionUsageTimeSeries,
|
||||
} from "./usage-types.ts";
|
||||
|
||||
export type CronRunStatus = "ok" | "error" | "skipped";
|
||||
export type CronDeliveryStatus = "delivered" | "not-delivered" | "unknown" | "not-requested";
|
||||
export type CronJobsEnabledFilter = "all" | "enabled" | "disabled";
|
||||
export type CronJobsSortBy = "nextRunAtMs" | "updatedAtMs" | "name";
|
||||
export type CronRunScope = "job" | "all";
|
||||
export type CronRunsStatusValue = CronRunStatus;
|
||||
export type CronRunsStatusFilter = "all" | CronRunStatus;
|
||||
export type CronSortDir = "asc" | "desc";
|
||||
|
||||
export type CronSchedule =
|
||||
| { kind: "at"; at: string }
|
||||
| { kind: "every"; everyMs: number; anchorMs?: number }
|
||||
@@ -424,9 +433,15 @@ export type CronPayload =
|
||||
kind: "agentTurn";
|
||||
message: string;
|
||||
model?: string;
|
||||
fallbacks?: string[];
|
||||
thinking?: string;
|
||||
timeoutSeconds?: number;
|
||||
allowUnsafeExternalContent?: boolean;
|
||||
lightContext?: boolean;
|
||||
deliver?: boolean;
|
||||
channel?: string;
|
||||
to?: string;
|
||||
bestEffortDeliver?: boolean;
|
||||
};
|
||||
|
||||
export type CronDelivery = {
|
||||
@@ -458,9 +473,15 @@ export type CronJobState = {
|
||||
nextRunAtMs?: number;
|
||||
runningAtMs?: number;
|
||||
lastRunAtMs?: number;
|
||||
lastStatus?: "ok" | "error" | "skipped";
|
||||
lastRunStatus?: CronRunStatus;
|
||||
lastStatus?: CronRunStatus;
|
||||
lastError?: string;
|
||||
lastErrorReason?: string;
|
||||
lastDurationMs?: number;
|
||||
consecutiveErrors?: number;
|
||||
lastDelivered?: boolean;
|
||||
lastDeliveryStatus?: CronDeliveryStatus;
|
||||
lastDeliveryError?: string;
|
||||
lastFailureAlertAtMs?: number;
|
||||
};
|
||||
|
||||
@@ -484,12 +505,46 @@ export type CronStatus = {
|
||||
export type CronRunLogEntry = {
|
||||
ts: number;
|
||||
jobId: string;
|
||||
status: "ok" | "error" | "skipped";
|
||||
action?: "finished";
|
||||
status?: CronRunStatus;
|
||||
durationMs?: number;
|
||||
error?: string;
|
||||
summary?: string;
|
||||
delivered?: boolean;
|
||||
deliveryStatus?: CronDeliveryStatus;
|
||||
deliveryError?: string;
|
||||
sessionId?: string;
|
||||
sessionKey?: string;
|
||||
runAtMs?: number;
|
||||
nextRunAtMs?: number;
|
||||
model?: string;
|
||||
provider?: string;
|
||||
usage?: {
|
||||
input_tokens?: number;
|
||||
output_tokens?: number;
|
||||
total_tokens?: number;
|
||||
cache_read_tokens?: number;
|
||||
cache_write_tokens?: number;
|
||||
};
|
||||
jobName?: string;
|
||||
};
|
||||
|
||||
export type CronJobsListResult = {
|
||||
jobs: CronJob[];
|
||||
total?: number;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
nextOffset?: number | null;
|
||||
hasMore?: boolean;
|
||||
};
|
||||
|
||||
export type CronRunsResult = {
|
||||
entries: CronRunLogEntry[];
|
||||
total?: number;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
nextOffset?: number | null;
|
||||
hasMore?: boolean;
|
||||
};
|
||||
|
||||
export type SkillsStatusConfigCheck = {
|
||||
|
||||
@@ -40,12 +40,15 @@ function createProps(overrides: Partial<ChatProps> = {}): ChatProps {
|
||||
focusMode: false,
|
||||
assistantName: "OpenClaw",
|
||||
assistantAvatar: null,
|
||||
agentsList: null,
|
||||
currentAgentId: "main",
|
||||
onRefresh: () => undefined,
|
||||
onToggleFocusMode: () => undefined,
|
||||
onDraftChange: () => undefined,
|
||||
onSend: () => undefined,
|
||||
onQueueRemove: () => undefined,
|
||||
onNewSession: () => undefined,
|
||||
onAgentChange: () => undefined,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
@@ -190,18 +193,17 @@ describe("chat view", () => {
|
||||
createProps({
|
||||
canAbort: true,
|
||||
onAbort,
|
||||
stream: "in-flight",
|
||||
}),
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
const stopButton = Array.from(container.querySelectorAll("button")).find(
|
||||
(btn) => btn.textContent?.trim() === "Stop",
|
||||
);
|
||||
expect(stopButton).not.toBeUndefined();
|
||||
const stopButton = container.querySelector<HTMLButtonElement>('button[title="Stop"]');
|
||||
expect(stopButton).not.toBeNull();
|
||||
stopButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
expect(onAbort).toHaveBeenCalledTimes(1);
|
||||
expect(container.textContent).not.toContain("New session");
|
||||
expect(container.querySelector('button[title="New session"]')).toBeNull();
|
||||
});
|
||||
|
||||
it("shows a new session button when aborting is unavailable", () => {
|
||||
@@ -217,13 +219,13 @@ describe("chat view", () => {
|
||||
container,
|
||||
);
|
||||
|
||||
const newSessionButton = Array.from(container.querySelectorAll("button")).find(
|
||||
(btn) => btn.textContent?.trim() === "New session",
|
||||
const newSessionButton = container.querySelector<HTMLButtonElement>(
|
||||
'button[title="New session"]',
|
||||
);
|
||||
expect(newSessionButton).not.toBeUndefined();
|
||||
expect(newSessionButton).not.toBeNull();
|
||||
newSessionButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
expect(onNewSession).toHaveBeenCalledTimes(1);
|
||||
expect(container.textContent).not.toContain("Stop");
|
||||
expect(container.querySelector('button[title="Stop"]')).toBeNull();
|
||||
});
|
||||
|
||||
it("shows sender labels from sanitized gateway messages instead of generic You", () => {
|
||||
|
||||
@@ -1234,6 +1234,20 @@ export function renderChat(props: ChatProps) {
|
||||
|
||||
<div class="agent-chat__toolbar-right">
|
||||
${nothing /* search hidden for now */}
|
||||
${
|
||||
canAbort
|
||||
? nothing
|
||||
: html`
|
||||
<button
|
||||
class="btn-ghost"
|
||||
@click=${props.onNewSession}
|
||||
title="New session"
|
||||
aria-label="New session"
|
||||
>
|
||||
${icons.plus}
|
||||
</button>
|
||||
`
|
||||
}
|
||||
<button class="btn-ghost" @click=${() => exportMarkdown(props)} title="Export" ?disabled=${props.messages.length === 0}>
|
||||
${icons.download}
|
||||
</button>
|
||||
|
||||
@@ -360,7 +360,9 @@ export function renderCron(props: CronProps) {
|
||||
props.runsScope === "all"
|
||||
? t("cron.jobList.allJobs")
|
||||
: (selectedJob?.name ?? props.runsJobId ?? t("cron.jobList.selectJob"));
|
||||
const runs = props.runs;
|
||||
const runs = props.runs.toSorted((a, b) =>
|
||||
props.runsSortDir === "asc" ? a.ts - b.ts : b.ts - a.ts,
|
||||
);
|
||||
const runStatusOptions = getRunStatusOptions();
|
||||
const runDeliveryOptions = getRunDeliveryOptions();
|
||||
const selectedStatusLabels = runStatusOptions
|
||||
@@ -1569,7 +1571,7 @@ function renderJob(job: CronJob, props: CronProps) {
|
||||
?disabled=${props.busy}
|
||||
@click=${(event: Event) => {
|
||||
event.stopPropagation();
|
||||
selectAnd(() => props.onLoadRuns(job.id));
|
||||
props.onLoadRuns(job.id);
|
||||
}}
|
||||
>
|
||||
${t("cron.jobList.history")}
|
||||
|
||||
@@ -23,7 +23,18 @@ function buildProps(result: SessionsListResult): SessionsProps {
|
||||
includeGlobal: false,
|
||||
includeUnknown: false,
|
||||
basePath: "",
|
||||
searchQuery: "",
|
||||
sortColumn: "updated",
|
||||
sortDir: "desc",
|
||||
page: 0,
|
||||
pageSize: 25,
|
||||
actionsOpenKey: null,
|
||||
onFiltersChange: () => undefined,
|
||||
onSearchChange: () => undefined,
|
||||
onSortChange: () => undefined,
|
||||
onPageChange: () => undefined,
|
||||
onPageSizeChange: () => undefined,
|
||||
onActionsOpenChange: () => undefined,
|
||||
onRefresh: () => undefined,
|
||||
onPatch: () => undefined,
|
||||
onDelete: () => undefined,
|
||||
|
||||
Reference in New Issue
Block a user