refactor(site/src/pages/AgentsPage): break AgentChatPage circular dep (#25287)

`AgentChatPageView.tsx` imported `getPersistedSidebarTabId` /
`savePersistedSidebarTabId` from `AgentChatPage.tsx`, which already
imports `AgentChatPageView`, closing a cycle that `pnpm run
lint:circular-deps`
reports but doesn't fail on (dpdm defaults to exit code 0; the script
is missing `--exit-code circular:1`).

Move the three sidebar-tab localStorage helpers and the key prefix into
`utils/sidebarTabStorage.ts` alongside `draftStorage.ts` and the other
per-chat storage modules. Pure code move, no behavior change.

After this change, `pnpm run lint:circular-deps` reports zero cycles.

---------

Signed-off-by: Thomas Kosiewski <tk@coder.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Thomas Kosiewski
2026-05-13 17:13:32 +02:00
committed by GitHub
co-authored by Claude Opus 4.7
parent 36200b625e
commit b9b8d763e3
6 changed files with 44 additions and 55 deletions
@@ -3,14 +3,10 @@ import { createRef } from "react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ChatQueuedMessage } from "#/api/typesGenerated";
import {
clearPersistedSidebarTabId,
draftInputStorageKeyPrefix,
getPersistedDraftInputValue,
getPersistedSidebarTabId,
lastActiveSidebarTabStorageKeyPrefix,
restoreOptimisticRequestSnapshot,
runPromoteQueuedMessage,
savePersistedSidebarTabId,
submitEditAndScroll,
useConversationEditingState,
waitForPendingChatSettingsSyncs,
@@ -18,6 +14,12 @@ import {
import type { ChatMessageInputRef } from "./components/AgentChatInput";
import { createChatStore } from "./components/ChatConversation/chatStore";
import type { PendingAttachment } from "./components/ChatPageContent";
import {
clearPersistedSidebarTabId,
getPersistedSidebarTabId,
lastActiveSidebarTabStorageKeyPrefix,
savePersistedSidebarTabId,
} from "./utils/sidebarTabStorage";
type MockChatInputHandle = {
handle: ChatMessageInputRef;
@@ -103,8 +103,6 @@ export const RIGHT_PANEL_OPEN_KEY = "agents.right-panel-open";
const lastModelConfigIDStorageKey = "agents.last-model-config-id";
/** @internal Exported for testing. */
export const draftInputStorageKeyPrefix = "agents.draft-input.";
/** @internal localStorage key prefix for the per-chat active sidebar tab. Exported for testing. */
export const lastActiveSidebarTabStorageKeyPrefix = "agents.last-active-tab.";
const clearChatPlanMode = "" satisfies ChatPlanModeOrClear;
@@ -126,49 +124,6 @@ export function getPersistedDraftInputValue(
).text;
}
/**
* Read the persisted active sidebar tab ID for a given chat. Returns
* `null` when no value is stored or the chat ID is missing.
*/
export function getPersistedSidebarTabId(
chatID: string | undefined,
): string | null {
if (!chatID) {
return null;
}
return localStorage.getItem(
`${lastActiveSidebarTabStorageKeyPrefix}${chatID}`,
);
}
/**
* Persist the active sidebar tab ID for a given chat so it can be
* restored across session switches. No-op when the chat ID is missing.
*/
export function savePersistedSidebarTabId(
chatID: string | undefined,
tabID: string,
): void {
if (!chatID) {
return;
}
localStorage.setItem(
`${lastActiveSidebarTabStorageKeyPrefix}${chatID}`,
tabID,
);
}
/**
* Remove the persisted active sidebar tab ID for a given chat. Called
* when a chat is archived so a future unarchive starts fresh.
*/
export function clearPersistedSidebarTabId(chatID: string | undefined): void {
if (!chatID) {
return;
}
localStorage.removeItem(`${lastActiveSidebarTabStorageKeyPrefix}${chatID}`);
}
/** @internal Exported for testing. */
export const restoreOptimisticRequestSnapshot = (
store: Pick<
@@ -16,7 +16,6 @@ import {
withProxyProvider,
withWebSocket,
} from "#/testHelpers/storybook";
import { lastActiveSidebarTabStorageKeyPrefix } from "./AgentChatPage";
import {
AgentChatPageLoadingView,
AgentChatPageNotFoundView,
@@ -28,6 +27,7 @@ import {
useChatSelector,
} from "./components/ChatConversation/chatStore";
import type { ModelSelectorOption } from "./components/ChatElements";
import { lastActiveSidebarTabStorageKeyPrefix } from "./utils/sidebarTabStorage";
import type { ChatDetailError } from "./utils/usageLimitMessage";
// ---------------------------------------------------------------------------
@@ -18,10 +18,6 @@ import type {
} from "#/api/typesGenerated";
import { cn } from "#/utils/cn";
import { pageTitle } from "#/utils/page";
import {
getPersistedSidebarTabId,
savePersistedSidebarTabId,
} from "./AgentChatPage";
import {
AgentChatInput,
type ChatMessageInputRef,
@@ -46,6 +42,10 @@ import { getWorkspaceStatus, StatusIcon } from "./components/StatusIcon";
import { TerminalPanel } from "./components/TerminalPanel";
import { ChatWorkspaceContext } from "./context/ChatWorkspaceContext";
import { chatWidthClass, useChatFullWidth } from "./hooks/useChatFullWidth";
import {
getPersistedSidebarTabId,
savePersistedSidebarTabId,
} from "./utils/sidebarTabStorage";
import type { ChatDetailError } from "./utils/usageLimitMessage";
type ChatStoreHandle = ReturnType<typeof useChatStore>["store"];
+1 -1
View File
@@ -40,7 +40,6 @@ import { ConfirmDialog } from "#/components/Dialogs/ConfirmDialog/ConfirmDialog"
import { DeleteDialog } from "#/components/Dialogs/DeleteDialog/DeleteDialog";
import { useAuthenticated } from "#/hooks/useAuthenticated";
import { createReconnectingWebSocket } from "#/utils/reconnectingWebSocket";
import { clearPersistedSidebarTabId } from "./AgentChatPage";
import { AgentsPageView } from "./AgentsPageView";
import { emptyInputStorageKey } from "./components/AgentCreateForm";
import { useAgentsPageKeybindings } from "./hooks/useAgentsPageKeybindings";
@@ -53,6 +52,7 @@ import {
} from "./utils/agentWorkspaceUtils";
import { maybePlayChime } from "./utils/chime";
import { getModelOptionsFromConfigs } from "./utils/modelOptions";
import { clearPersistedSidebarTabId } from "./utils/sidebarTabStorage";
import {
type ChatDetailError,
chatDetailErrorsEqual,
@@ -0,0 +1,32 @@
export const lastActiveSidebarTabStorageKeyPrefix = "agents.last-active-tab.";
export function getPersistedSidebarTabId(
chatID: string | undefined,
): string | null {
if (!chatID) {
return null;
}
return localStorage.getItem(
`${lastActiveSidebarTabStorageKeyPrefix}${chatID}`,
);
}
export function savePersistedSidebarTabId(
chatID: string | undefined,
tabID: string,
): void {
if (!chatID) {
return;
}
localStorage.setItem(
`${lastActiveSidebarTabStorageKeyPrefix}${chatID}`,
tabID,
);
}
export function clearPersistedSidebarTabId(chatID: string | undefined): void {
if (!chatID) {
return;
}
localStorage.removeItem(`${lastActiveSidebarTabStorageKeyPrefix}${chatID}`);
}