diff --git a/CHANGELOG.md b/CHANGELOG.md index 9064f5dad..fa3ef3efe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Docs: https://docs.clawd.bot ### Fixes - Doctor: warn when gateway.mode is unset with configure/config guidance. +- UI: refresh debug panel on route-driven tab changes. (#1373) Thanks @yazinsai. ## 2026.1.21 diff --git a/src/cli/update-cli.test.ts b/src/cli/update-cli.test.ts index 395789bdd..25fe5b1d1 100644 --- a/src/cli/update-cli.test.ts +++ b/src/cli/update-cli.test.ts @@ -273,7 +273,7 @@ describe("update-cli", () => { try { await fs.writeFile( path.join(tempDir, "package.json"), - JSON.stringify({ name: "clawdbot", version: "2026.1.18-1" }), + JSON.stringify({ name: "clawdbot", version: "1.0.0" }), "utf-8", ); diff --git a/ui/src/ui/app-lifecycle.ts b/ui/src/ui/app-lifecycle.ts index 817151d5f..71af9d202 100644 --- a/ui/src/ui/app-lifecycle.ts +++ b/ui/src/ui/app-lifecycle.ts @@ -14,6 +14,8 @@ import { startNodesPolling, stopLogsPolling, stopNodesPolling, + startDebugPolling, + stopDebugPolling, } from "./app-polling"; type LifecycleHost = { @@ -52,6 +54,9 @@ export function handleConnected(host: LifecycleHost) { if (host.tab === "logs") { startLogsPolling(host as unknown as Parameters[0]); } + if (host.tab === "debug") { + startDebugPolling(host as unknown as Parameters[0]); + } } export function handleFirstUpdated(host: LifecycleHost) { @@ -62,6 +67,7 @@ export function handleDisconnected(host: LifecycleHost) { window.removeEventListener("popstate", host.popStateHandler); stopNodesPolling(host as unknown as Parameters[0]); stopLogsPolling(host as unknown as Parameters[0]); + stopDebugPolling(host as unknown as Parameters[0]); detachThemeListener( host as unknown as Parameters[0], ); diff --git a/ui/src/ui/app-polling.ts b/ui/src/ui/app-polling.ts index 53d7b2296..b18113c5a 100644 --- a/ui/src/ui/app-polling.ts +++ b/ui/src/ui/app-polling.ts @@ -1,10 +1,12 @@ import { loadLogs } from "./controllers/logs"; import { loadNodes } from "./controllers/nodes"; +import { loadDebug } from "./controllers/debug"; import type { ClawdbotApp } from "./app"; type PollingHost = { nodesPollInterval: number | null; logsPollInterval: number | null; + debugPollInterval: number | null; tab: string; }; @@ -35,3 +37,17 @@ export function stopLogsPolling(host: PollingHost) { clearInterval(host.logsPollInterval); host.logsPollInterval = null; } + +export function startDebugPolling(host: PollingHost) { + if (host.debugPollInterval != null) return; + host.debugPollInterval = window.setInterval(() => { + if (host.tab !== "debug") return; + void loadDebug(host as unknown as ClawdbotApp); + }, 3000); +} + +export function stopDebugPolling(host: PollingHost) { + if (host.debugPollInterval == null) return; + clearInterval(host.debugPollInterval); + host.debugPollInterval = null; +} diff --git a/ui/src/ui/app-settings.test.ts b/ui/src/ui/app-settings.test.ts new file mode 100644 index 000000000..be06d7d8b --- /dev/null +++ b/ui/src/ui/app-settings.test.ts @@ -0,0 +1,91 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import type { Tab } from "./navigation"; + +type SettingsHost = Parameters[0]; + +const createHost = (tab: Tab): SettingsHost => ({ + settings: { + gatewayUrl: "", + token: "", + sessionKey: "main", + lastActiveSessionKey: "main", + theme: "system", + chatFocusMode: false, + chatShowThinking: true, + splitRatio: 0.6, + navCollapsed: false, + navGroupsCollapsed: {}, + }, + theme: "system", + themeResolved: "dark", + applySessionKey: "main", + sessionKey: "main", + tab, + connected: false, + chatHasAutoScrolled: false, + logsAtBottom: false, + eventLog: [], + eventLogBuffer: [], + basePath: "", + themeMedia: null, + themeMediaHandler: null, +}); + +describe("setTabFromRoute", () => { + beforeEach(() => { + vi.resetModules(); + }); + + it("starts and stops log polling based on the tab", async () => { + const startLogsPolling = vi.fn(); + const stopLogsPolling = vi.fn(); + const startDebugPolling = vi.fn(); + const stopDebugPolling = vi.fn(); + + vi.doMock("./app-polling", () => ({ + startLogsPolling, + stopLogsPolling, + startDebugPolling, + stopDebugPolling, + })); + + const { setTabFromRoute } = await import("./app-settings"); + const host = createHost("chat"); + + setTabFromRoute(host, "logs"); + expect(startLogsPolling).toHaveBeenCalledTimes(1); + expect(stopLogsPolling).not.toHaveBeenCalled(); + expect(startDebugPolling).not.toHaveBeenCalled(); + expect(stopDebugPolling).toHaveBeenCalledTimes(1); + + setTabFromRoute(host, "chat"); + expect(stopLogsPolling).toHaveBeenCalledTimes(1); + }); + + it("starts and stops debug polling based on the tab", async () => { + const startLogsPolling = vi.fn(); + const stopLogsPolling = vi.fn(); + const startDebugPolling = vi.fn(); + const stopDebugPolling = vi.fn(); + + vi.doMock("./app-polling", () => ({ + startLogsPolling, + stopLogsPolling, + startDebugPolling, + stopDebugPolling, + })); + + const { setTabFromRoute } = await import("./app-settings"); + const host = createHost("chat"); + + setTabFromRoute(host, "debug"); + expect(startDebugPolling).toHaveBeenCalledTimes(1); + expect(stopDebugPolling).not.toHaveBeenCalled(); + expect(startLogsPolling).not.toHaveBeenCalled(); + expect(stopLogsPolling).toHaveBeenCalledTimes(1); + + setTabFromRoute(host, "chat"); + expect(stopDebugPolling).toHaveBeenCalledTimes(1); + }); +}); diff --git a/ui/src/ui/app-settings.ts b/ui/src/ui/app-settings.ts index f9139afbd..0cceff4cb 100644 --- a/ui/src/ui/app-settings.ts +++ b/ui/src/ui/app-settings.ts @@ -14,7 +14,7 @@ import { saveSettings, type UiSettings } from "./storage"; import { resolveTheme, type ResolvedTheme, type ThemeMode } from "./theme"; import { startThemeTransition, type ThemeTransitionContext } from "./theme-transition"; import { scheduleChatScroll, scheduleLogsScroll } from "./app-scroll"; -import { startLogsPolling, stopLogsPolling } from "./app-polling"; +import { startLogsPolling, stopLogsPolling, startDebugPolling, stopDebugPolling } from "./app-polling"; import { refreshChat } from "./app-chat"; import type { ClawdbotApp } from "./app"; @@ -116,6 +116,9 @@ export function setTab(host: SettingsHost, next: Tab) { if (next === "logs") startLogsPolling(host as unknown as Parameters[0]); else stopLogsPolling(host as unknown as Parameters[0]); + if (next === "debug") + startDebugPolling(host as unknown as Parameters[0]); + else stopDebugPolling(host as unknown as Parameters[0]); void refreshActiveTab(host); syncUrlWithTab(host, next, false); } @@ -261,6 +264,9 @@ export function setTabFromRoute(host: SettingsHost, next: Tab) { if (next === "logs") startLogsPolling(host as unknown as Parameters[0]); else stopLogsPolling(host as unknown as Parameters[0]); + if (next === "debug") + startDebugPolling(host as unknown as Parameters[0]); + else stopDebugPolling(host as unknown as Parameters[0]); if (host.connected) void refreshActiveTab(host); } diff --git a/ui/src/ui/app.ts b/ui/src/ui/app.ts index f0e6a6c51..9ae886048 100644 --- a/ui/src/ui/app.ts +++ b/ui/src/ui/app.ts @@ -226,6 +226,7 @@ export class ClawdbotApp extends LitElement { private chatUserNearBottom = true; private nodesPollInterval: number | null = null; private logsPollInterval: number | null = null; + private debugPollInterval: number | null = null; private logsScrollFrame: number | null = null; private toolStreamById = new Map(); private toolStreamOrder: string[] = [];