refactor: move workspace tab navigation logic to prepareWorkspaceTab

This commit is contained in:
Mohamed Boudra
2026-03-21 15:21:02 +07:00
parent 78fc1fe862
commit 48c3308c31
17 changed files with 184 additions and 390 deletions
@@ -1,7 +1,6 @@
import { describe, expect, it } from "vitest";
import type { Agent } from "@/stores/session-store";
import {
canOpenAgentTabFromRoute,
deriveWorkspaceAgentVisibility,
shouldPruneWorkspaceAgentTab,
workspaceAgentVisibilityEqual,
@@ -89,18 +88,6 @@ describe("workspace agent visibility", () => {
expect(result.knownAgentIds.has("other-workspace-agent")).toBe(false);
});
it("allows explicit route open for archived agent once agents are hydrated", () => {
const knownAgentIds = new Set(["archived-agent"]);
expect(
canOpenAgentTabFromRoute({
agentId: "archived-agent",
agentsHydrated: true,
knownAgentIds,
})
).toBe(true);
});
it("does not prune archived agent tabs when knownAgentIds contains the agent", () => {
const knownAgentIds = new Set(["archived-agent"]);
@@ -57,20 +57,6 @@ function setsEqual(a: Set<string>, b: Set<string>): boolean {
return true;
}
export function canOpenAgentTabFromRoute(input: {
agentId: string;
agentsHydrated: boolean;
knownAgentIds: Set<string>;
}): boolean {
if (!input.agentId.trim()) {
return false;
}
if (!input.agentsHydrated) {
return true;
}
return input.knownAgentIds.has(input.agentId);
}
export function shouldPruneWorkspaceAgentTab(input: {
agentId: string;
agentsHydrated: boolean;
@@ -64,8 +64,6 @@ import { useKeyboardActionHandler } from "@/hooks/use-keyboard-action-handler";
import type { KeyboardActionDefinition } from "@/keyboard/keyboard-action-dispatcher";
import { useCreateFlowStore } from "@/stores/create-flow-store";
import {
buildWorkspaceOpenIntentParam,
type WorkspaceOpenIntent,
decodeWorkspaceIdFromPathSegment,
} from "@/utils/host-routes";
import { normalizeWorkspaceIdentity } from "@/utils/workspace-identity";
@@ -103,7 +101,6 @@ import {
} from "@/screens/workspace/workspace-agent-visibility";
import {
deriveWorkspacePaneState,
type WorkspaceDerivedTab,
} from "@/screens/workspace/workspace-pane-state";
import {
buildWorkspacePaneContentModel,
@@ -125,7 +122,6 @@ const EMPTY_PINNED_AGENT_IDS = new Set<string>();
type WorkspaceScreenProps = {
serverId: string;
workspaceId: string;
openIntent?: WorkspaceOpenIntent | null;
};
function trimNonEmpty(value: string | null | undefined): string | null {
@@ -144,49 +140,6 @@ function decodeSegment(value: string): string {
}
}
function buildOpenIntentKey(input: {
serverId: string;
workspaceId: string;
openIntent?: WorkspaceOpenIntent | null;
}): string | null {
if (!input.openIntent) {
return null;
}
const openParam = buildWorkspaceOpenIntentParam(input.openIntent);
if (!openParam) {
return null;
}
return `${input.serverId}:${input.workspaceId}:${openParam}`;
}
function openIntentMatchesActiveTab(input: {
openIntent?: WorkspaceOpenIntent | null;
activeTab: WorkspaceDerivedTab | null;
}): boolean {
const { openIntent, activeTab } = input;
if (!openIntent || !activeTab) {
return false;
}
const target = activeTab.descriptor.target;
if (openIntent.kind !== target.kind) {
return false;
}
if (openIntent.kind === "agent" && target.kind === "agent") {
return target.agentId === openIntent.agentId;
}
if (openIntent.kind === "terminal" && target.kind === "terminal") {
return target.terminalId === openIntent.terminalId;
}
if (openIntent.kind === "file" && target.kind === "file") {
return target.path === openIntent.path;
}
if (openIntent.kind === "draft" && target.kind === "draft") {
return openIntent.draftId === "new" || target.draftId === openIntent.draftId;
}
return false;
}
function getFallbackTabOptionLabel(tab: WorkspaceTabDescriptor): string {
if (tab.target.kind === "draft") {
return "New Agent";
@@ -507,7 +460,6 @@ const MobileWorkspaceTabSwitcher = memo(function MobileWorkspaceTabSwitcher({
export function WorkspaceScreen({
serverId,
workspaceId,
openIntent,
}: WorkspaceScreenProps) {
const isFocused = useIsFocused();
@@ -520,7 +472,6 @@ export function WorkspaceScreen({
<WorkspaceScreenContent
serverId={serverId}
workspaceId={workspaceId}
openIntent={openIntent}
/>
</ExplorerSidebarAnimationProvider>
);
@@ -529,7 +480,6 @@ export function WorkspaceScreen({
function WorkspaceScreenContent({
serverId,
workspaceId,
openIntent,
}: WorkspaceScreenProps) {
const { theme } = useUnistyles();
const insets = useSafeAreaInsets();
@@ -811,7 +761,6 @@ function WorkspaceScreenContent({
const openWorkspaceTab = useWorkspaceLayoutStore((state) => state.openTab);
const focusWorkspaceTab = useWorkspaceLayoutStore((state) => state.focusTab);
const closeWorkspaceTab = useWorkspaceLayoutStore((state) => state.closeTab);
const pinWorkspaceAgent = useWorkspaceLayoutStore((state) => state.pinAgent);
const unpinWorkspaceAgent = useWorkspaceLayoutStore((state) => state.unpinAgent);
const retargetWorkspaceTab = useWorkspaceLayoutStore((state) => state.retargetTab);
const splitWorkspacePane = useWorkspaceLayoutStore((state) => state.splitPane);
@@ -826,33 +775,7 @@ function WorkspaceScreenContent({
: EMPTY_PINNED_AGENT_IDS
);
const pendingByDraftId = useCreateFlowStore((state) => state.pendingByDraftId);
const consumedOpenIntentsRef = useRef(new Set<string>());
const pendingCloseTabIdsRef = useRef(new Set<string>());
const [resolvedOpenIntentKey, setResolvedOpenIntentKey] = useState<string | null>(null);
const currentOpenIntentKey = useMemo(
() =>
buildOpenIntentKey({
serverId: normalizedServerId,
workspaceId: normalizedWorkspaceId,
openIntent,
}),
[normalizedServerId, normalizedWorkspaceId, openIntent]
);
const currentOpenIntentPinnedAgentId = useMemo(
function getCurrentOpenIntentPinnedAgentId() {
if (!openIntent || openIntent.kind !== "agent" || !hasHydratedAgents) {
return null;
}
if (!workspaceAgentVisibility.knownAgentIds.has(openIntent.agentId)) {
return null;
}
if (workspaceAgentVisibility.activeAgentIds.has(openIntent.agentId)) {
return null;
}
return openIntent.agentId;
},
[hasHydratedAgents, openIntent, workspaceAgentVisibility]
);
const closeWorkspaceTabWithCleanup = useCallback(
function closeWorkspaceTabWithCleanup(input: {
tabId: string;
@@ -949,93 +872,6 @@ function WorkspaceScreenContent({
[ensureWorkspaceTab, focusWorkspaceTab, openWorkspaceTab, persistenceKey]
);
useEffect(() => {
if (!currentOpenIntentKey) {
if (resolvedOpenIntentKey !== null) {
setResolvedOpenIntentKey(null);
}
return;
}
if (resolvedOpenIntentKey === currentOpenIntentKey) {
return;
}
}, [currentOpenIntentKey, resolvedOpenIntentKey]);
useEffect(() => {
if (!openIntent || !persistenceKey) {
return;
}
if (!currentOpenIntentKey) {
return;
}
if (currentOpenIntentPinnedAgentId) {
pinWorkspaceAgent(persistenceKey, currentOpenIntentPinnedAgentId);
}
const intentKey = currentOpenIntentKey;
if (consumedOpenIntentsRef.current.has(intentKey)) {
if (resolvedOpenIntentKey !== intentKey) {
setResolvedOpenIntentKey(intentKey);
}
return;
}
consumedOpenIntentsRef.current.add(intentKey);
if (openIntent.kind === "draft") {
const draftId = openIntent.draftId.trim();
const tabId = openWorkspaceDraftTab({
...(draftId === "new" ? {} : { draftId }),
focus: true,
});
return;
}
const target: WorkspaceTabTarget =
openIntent.kind === "agent"
? { kind: "agent", agentId: openIntent.agentId }
: openIntent.kind === "terminal"
? { kind: "terminal", terminalId: openIntent.terminalId }
: { kind: "file", path: openIntent.path };
const tabId = openWorkspaceTab(persistenceKey, target);
if (tabId) {
focusWorkspaceTab(persistenceKey, tabId);
}
}, [
currentOpenIntentPinnedAgentId,
currentOpenIntentKey,
focusWorkspaceTab,
openIntent,
openWorkspaceDraftTab,
openWorkspaceTab,
pinWorkspaceAgent,
persistenceKey,
]);
const unresolvedOpenIntent = currentOpenIntentKey && resolvedOpenIntentKey !== currentOpenIntentKey
? openIntent
: null;
const resolvedPaneTabState = useMemo(
() =>
deriveWorkspacePaneState({
layout: workspaceLayout,
tabs: uiTabs,
preferredTarget:
unresolvedOpenIntent?.kind === "agent"
? { kind: "agent", agentId: unresolvedOpenIntent.agentId }
: unresolvedOpenIntent?.kind === "terminal"
? { kind: "terminal", terminalId: unresolvedOpenIntent.terminalId }
: unresolvedOpenIntent?.kind === "draft"
? { kind: "draft", draftId: unresolvedOpenIntent.draftId }
: unresolvedOpenIntent?.kind === "file"
? { kind: "file", path: unresolvedOpenIntent.path }
: null,
}),
[uiTabs, unresolvedOpenIntent, workspaceLayout]
);
useEffect(() => {
if (!normalizedServerId || !normalizedWorkspaceId || !persistenceKey) {
return;
@@ -1077,12 +913,10 @@ function WorkspaceScreenContent({
canPruneAgentTabs &&
tab.target.kind === "agent" &&
!pinnedAgentIds.has(tab.target.agentId) &&
tab.target.agentId !== currentOpenIntentPinnedAgentId &&
shouldPruneWorkspaceAgentTab({
agentId: tab.target.agentId,
agentsHydrated: hasHydratedAgents,
knownAgentIds: workspaceAgentVisibility.knownAgentIds,
activeAgentIds: workspaceAgentVisibility.activeAgentIds,
})
) {
closeWorkspaceTabWithCleanup({ tabId: tab.tabId, target: tab.target });
@@ -1097,7 +931,6 @@ function WorkspaceScreenContent({
}
}, [
closeWorkspaceTabWithCleanup,
currentOpenIntentPinnedAgentId,
ensureWorkspaceTab,
hasHydratedAgents,
pendingByDraftId,
@@ -1109,25 +942,8 @@ function WorkspaceScreenContent({
workspaceAgentVisibility,
]);
const activeTabId = resolvedPaneTabState.activeTabId;
const activeTab = resolvedPaneTabState.activeTab;
useEffect(() => {
if (!openIntent || !currentOpenIntentKey) {
return;
}
if (resolvedOpenIntentKey === currentOpenIntentKey) {
return;
}
if (
openIntentMatchesActiveTab({
openIntent,
activeTab,
})
) {
setResolvedOpenIntentKey(currentOpenIntentKey);
}
}, [activeTab, currentOpenIntentKey, openIntent, resolvedOpenIntentKey]);
const activeTabId = focusedPaneTabState.activeTabId;
const activeTab = focusedPaneTabState.activeTab;
useEffect(() => {
if (!activeTabId || !persistenceKey) {
@@ -1137,8 +953,8 @@ function WorkspaceScreenContent({
}, [activeTabId, focusWorkspaceTab, persistenceKey]);
const tabs = useMemo<WorkspaceTabDescriptor[]>(
() => resolvedPaneTabState.tabs.map((tab) => tab.descriptor),
[resolvedPaneTabState.tabs]
() => focusedPaneTabState.tabs.map((tab) => tab.descriptor),
[focusedPaneTabState.tabs]
);
const navigateToTabId = useCallback(
@@ -1156,10 +972,6 @@ function WorkspaceScreenContent({
if (!persistenceKey) {
return;
}
if (openIntent) {
emptyWorkspaceSeedRef.current = null;
return;
}
if (workspaceAgentVisibility.activeAgentIds.size > 0 || terminals.length > 0) {
emptyWorkspaceSeedRef.current = null;
return;
@@ -1177,7 +989,6 @@ function WorkspaceScreenContent({
}, [
normalizedServerId,
normalizedWorkspaceId,
openIntent,
openWorkspaceDraftTab,
persistenceKey,
terminals.length,