mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
fix(ui): lazy load usage dashboard
This commit is contained in:
@@ -50,6 +50,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Release/CI/E2E: reset shared Crabbox pnpm hydrate state before installs so stale `/var/tmp` stores cannot leave `pnpm install` spinning after completion.
|
||||
- Release/CI/E2E: print heartbeat progress during centralized Docker builds while keeping successful build logs quiet.
|
||||
- Release/CI/E2E: avoid heartbeat-tail delays in Docker E2E log wrappers while reporting captured log bytes during long runs.
|
||||
- Control UI: lazy-load the usage view so the initial app bundle stays below the chunk warning threshold.
|
||||
- Build: render independent CLI startup metadata help snapshots concurrently to cut cold build-all metadata time.
|
||||
- Plugins: stop timed-out package-boundary prep steps by process group so descendant TypeScript/helper processes do not survive local check cleanup.
|
||||
- Control UI: serve static assets asynchronously after safe-open checks so large UI files do not block Gateway request handling.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { renderUsageTab } from "./app-render-usage-tab.ts";
|
||||
import type { AppViewState } from "./app-view-state.ts";
|
||||
import type { LazyView } from "./lazy-view.ts";
|
||||
import type { UsageProps } from "./views/usageTypes.ts";
|
||||
|
||||
const loadUsageMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
@@ -15,9 +16,17 @@ vi.mock("./controllers/usage.ts", async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./views/usage.ts", () => ({
|
||||
renderUsage: renderUsageMock,
|
||||
}));
|
||||
type UsageViewModule = typeof import("./views/usage.ts");
|
||||
|
||||
function createLoadedUsageView(): LazyView<UsageViewModule> {
|
||||
return {
|
||||
read: () => ({ renderUsage: renderUsageMock }) as unknown as UsageViewModule,
|
||||
retry: () => {},
|
||||
error: () => undefined,
|
||||
hasError: () => false,
|
||||
pending: () => false,
|
||||
};
|
||||
}
|
||||
|
||||
function createState(overrides: Partial<AppViewState> = {}): AppViewState {
|
||||
return {
|
||||
@@ -52,7 +61,7 @@ describe("renderUsageTab", () => {
|
||||
});
|
||||
|
||||
it("passes configured agents to the usage view", () => {
|
||||
renderUsageTab(createState());
|
||||
renderUsageTab(createState(), createLoadedUsageView());
|
||||
|
||||
expect(renderUsageMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@@ -64,7 +73,7 @@ describe("renderUsageTab", () => {
|
||||
it("reloads usage when selecting an agent scope", () => {
|
||||
const state = createState();
|
||||
|
||||
renderUsageTab(state);
|
||||
renderUsageTab(state, createLoadedUsageView());
|
||||
expect(renderUsageMock).toHaveBeenCalled();
|
||||
const props = renderUsageMock.mock.calls[0]?.[0];
|
||||
if (!props) {
|
||||
|
||||
@@ -2,7 +2,11 @@ import { nothing } from "lit";
|
||||
import type { AppViewState } from "./app-view-state.ts";
|
||||
import type { UsageState } from "./controllers/usage.ts";
|
||||
import { loadUsage, loadSessionTimeSeries, loadSessionLogs } from "./controllers/usage.ts";
|
||||
import { renderUsage } from "./views/usage.ts";
|
||||
import type { LazyView } from "./lazy-view.ts";
|
||||
import { renderLazyView } from "./lazy-view.ts";
|
||||
import type { UsageColumnId } from "./views/usageTypes.ts";
|
||||
|
||||
type UsageViewModule = typeof import("./views/usage.ts");
|
||||
|
||||
type UsageCacheStatus = NonNullable<NonNullable<UsageState["usageResult"]>["cacheStatus"]>;
|
||||
|
||||
@@ -40,12 +44,12 @@ const debouncedLoadUsage = (state: UsageState) => {
|
||||
usageDateDebounceTimeout = window.setTimeout(() => void loadUsage(state), 400);
|
||||
};
|
||||
|
||||
export function renderUsageTab(state: AppViewState) {
|
||||
export function renderUsageTab(state: AppViewState, usageView: LazyView<UsageViewModule>) {
|
||||
if (state.tab !== "usage") {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return renderUsage({
|
||||
return renderLazyView(usageView, ({ renderUsage }) => renderUsage({
|
||||
data: {
|
||||
loading: state.usageLoading,
|
||||
error: state.usageError,
|
||||
@@ -79,7 +83,7 @@ export function renderUsageTab(state: AppViewState) {
|
||||
sessionSortDir: state.usageSessionSortDir,
|
||||
recentSessions: state.usageRecentSessions,
|
||||
sessionsTab: state.usageSessionsTab,
|
||||
visibleColumns: state.usageVisibleColumns as import("./views/usage.ts").UsageColumnId[],
|
||||
visibleColumns: state.usageVisibleColumns as UsageColumnId[],
|
||||
contextExpanded: state.usageContextExpanded,
|
||||
headerPinned: state.usageHeaderPinned,
|
||||
},
|
||||
@@ -333,5 +337,5 @@ export function renderUsageTab(state: AppViewState) {
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -531,6 +531,7 @@ const lazySkillWorkshop = createLazyView(
|
||||
notifyLazyViewChanged,
|
||||
);
|
||||
const lazySkills = createLazyView(() => import("./views/skills.ts"), notifyLazyViewChanged);
|
||||
const lazyUsage = createLazyView(() => import("./views/usage.ts"), notifyLazyViewChanged);
|
||||
const lazyWorkboard = createLazyView(() => import("./views/workboard.ts"), notifyLazyViewChanged);
|
||||
|
||||
type ChatWorkspaceFilesState = {
|
||||
@@ -2642,7 +2643,7 @@ export function renderApp(state: AppViewState) {
|
||||
});
|
||||
})
|
||||
: nothing}
|
||||
${renderUsageTab(state)}
|
||||
${renderUsageTab(state, lazyUsage)}
|
||||
${state.tab === "cron" ? renderCronQuickCreateForTab(state, requestHostUpdate) : nothing}
|
||||
${state.tab === "cron"
|
||||
? renderLazyView(lazyCron, (m) =>
|
||||
|
||||
Reference in New Issue
Block a user