Merge branch 'main' into read-electron-migration-skills

# Conflicts:
#	packages/app/src/app/_layout.tsx
#	packages/app/src/components/left-sidebar.tsx
This commit is contained in:
Mohamed Boudra
2026-03-21 01:52:25 +07:00
36 changed files with 2169 additions and 1769 deletions
File diff suppressed because it is too large Load Diff
@@ -33,7 +33,7 @@ import { buildWorkingDirectorySuggestions } from '@/utils/working-directory-sugg
import { useExplorerOpenGesture } from '@/hooks/use-explorer-open-gesture'
import { useSessionStore } from '@/stores/session-store'
import { generateDraftId } from '@/stores/draft-keys'
import { getHostRuntimeStore, useHostRuntimeSession } from '@/runtime/host-runtime'
import { getHostRuntimeStore, useHostRuntimeClient, useHostRuntimeIsConnected } from '@/runtime/host-runtime'
import { ExplorerSidebarAnimationProvider } from '@/contexts/explorer-sidebar-animation-context'
import { usePanelStore, type ExplorerCheckoutContext } from '@/stores/panel-store'
import { MAX_CONTENT_WIDTH } from '@/constants/layout'
@@ -317,9 +317,8 @@ function DraftAgentScreenContent({
return collectAgentWorkingDirectorySuggestions([...liveSources, ...fetchedSources])
}, [allAgents, sessionAgents])
const { client: runtimeClient, isConnected: isHostOnline } = useHostRuntimeSession(
selectedServerId ?? ''
)
const runtimeClient = useHostRuntimeClient(selectedServerId ?? '')
const isHostOnline = useHostRuntimeIsConnected(selectedServerId ?? '')
const sessionClient = runtimeClient
const trimmedWorkingDir = workingDir.trim()
const shouldInspectRepo = trimmedWorkingDir.length > 0
+7 -5
View File
@@ -24,7 +24,9 @@ import { useSessionStore } from "@/stores/session-store";
import {
getHostRuntimeStore,
isHostRuntimeConnected,
useHostRuntimeSession,
useHostRuntimeClient,
useHostRuntimeIsConnected,
useHostRuntimeSnapshot,
} from "@/runtime/host-runtime";
import { AddHostMethodModal } from "@/components/add-host-method-modal";
import { AddHostModal } from "@/components/add-host-modal";
@@ -993,9 +995,9 @@ function HostDetailModal({
const connections = host?.connections ?? [];
// Restart logic (moved from DaemonCard)
const { snapshot: runtimeSnapshot, client: runtimeClient, isConnected } = useHostRuntimeSession(
host?.serverId ?? ""
);
const runtimeSnapshot = useHostRuntimeSnapshot(host?.serverId ?? "");
const runtimeClient = useHostRuntimeClient(host?.serverId ?? "");
const isConnected = useHostRuntimeIsConnected(host?.serverId ?? "");
const runtime = getHostRuntimeStore();
const daemonClient = runtimeClient;
const daemonVersion = useSessionStore((state) => host ? (state.sessions[host.serverId]?.serverInfo?.version ?? null) : null);
@@ -1390,7 +1392,7 @@ function DaemonCard({
onOpenSettings,
}: DaemonCardProps) {
const { theme } = useUnistyles();
const { snapshot } = useHostRuntimeSession(daemon.serverId);
const snapshot = useHostRuntimeSnapshot(daemon.serverId);
const connectionStatus = snapshot?.connectionStatus ?? "connecting";
const activeConnection = snapshot?.activeConnection ?? null;
const lastError = snapshot?.lastError ?? null;
@@ -4,6 +4,7 @@ import {
canOpenAgentTabFromRoute,
deriveWorkspaceAgentVisibility,
shouldPruneWorkspaceAgentTab,
workspaceAgentVisibilityEqual,
} from "@/screens/workspace/workspace-agent-visibility";
function makeAgent(input: {
@@ -53,7 +54,7 @@ function makeAgent(input: {
}
describe("workspace agent visibility", () => {
it("keeps archived agents hidden from visible list but present in workspace lookup", () => {
it("keeps archived agents out of activeAgentIds but present in knownAgentIds", () => {
const workspaceId = "/repo/worktree";
const visible = makeAgent({
id: "visible-agent",
@@ -82,87 +83,46 @@ describe("workspace agent visibility", () => {
workspaceId,
});
expect(result.visibleAgents.map((agent) => agent.id)).toEqual(["visible-agent"]);
expect(result.lookupById.has("visible-agent")).toBe(true);
expect(result.lookupById.has("archived-agent")).toBe(true);
expect(result.lookupById.has("other-workspace-agent")).toBe(false);
expect(result.activeAgentIds).toEqual(new Set(["visible-agent"]));
expect(result.knownAgentIds.has("visible-agent")).toBe(true);
expect(result.knownAgentIds.has("archived-agent")).toBe(true);
expect(result.knownAgentIds.has("other-workspace-agent")).toBe(false);
});
it("allows explicit route open for archived agent once agents are hydrated", () => {
const archivedAgent = makeAgent({
id: "archived-agent",
cwd: "/repo/worktree",
archivedAt: new Date("2026-03-04T00:01:00.000Z"),
});
const lookup = new Map<string, Agent>([[archivedAgent.id, archivedAgent]]);
const knownAgentIds = new Set(["archived-agent"]);
expect(
canOpenAgentTabFromRoute({
agentId: "archived-agent",
agentsHydrated: true,
workspaceAgentLookup: lookup,
knownAgentIds,
})
).toBe(true);
});
it("does not prune archived agent tabs when the workspace lookup still contains the agent", () => {
const archivedAgent = makeAgent({
id: "archived-agent",
cwd: "/repo/worktree",
archivedAt: new Date("2026-03-04T00:01:00.000Z"),
});
const lookup = new Map<string, Agent>([[archivedAgent.id, archivedAgent]]);
it("does not prune archived agent tabs when knownAgentIds contains the agent", () => {
const knownAgentIds = new Set(["archived-agent"]);
expect(
shouldPruneWorkspaceAgentTab({
agentId: "archived-agent",
agentsHydrated: true,
workspaceAgentLookup: lookup,
knownAgentIds,
})
).toBe(false);
});
it("prunes agent tabs once agents are hydrated and the agent is missing from the workspace lookup", () => {
it("prunes agent tabs once agents are hydrated and the agent is missing from knownAgentIds", () => {
expect(
shouldPruneWorkspaceAgentTab({
agentId: "missing-agent",
agentsHydrated: true,
workspaceAgentLookup: new Map<string, Agent>(),
knownAgentIds: new Set<string>(),
})
).toBe(true);
});
it("sorts same-createdAt agents by lastActivityAt descending", () => {
const workspaceId = "/repo/worktree";
const createdAt = new Date("2026-03-04T00:00:00.000Z");
const newerActivity = makeAgent({
id: "newer-activity",
cwd: workspaceId,
createdAt,
lastActivityAt: new Date("2026-03-04T00:05:00.000Z"),
});
const olderActivity = makeAgent({
id: "older-activity",
cwd: workspaceId,
createdAt,
lastActivityAt: new Date("2026-03-04T00:01:00.000Z"),
});
const sessionAgents = new Map<string, Agent>([
[olderActivity.id, olderActivity],
[newerActivity.id, newerActivity],
]);
const result = deriveWorkspaceAgentVisibility({
sessionAgents,
workspaceId,
});
expect(result.visibleAgents.map((agent) => agent.id)).toEqual([
"newer-activity",
"older-activity",
]);
});
it("matches workspace agents when cwd and route workspace differ only by trailing slash", () => {
const sessionAgents = new Map<string, Agent>([
[
@@ -179,7 +139,33 @@ describe("workspace agent visibility", () => {
workspaceId: "/Users/moboudra/.paseo/worktrees/1luy0po7/normal-squid",
});
expect(result.visibleAgents.map((agent) => agent.id)).toEqual(["slash-agent"]);
expect(result.lookupById.has("slash-agent")).toBe(true);
expect(result.activeAgentIds).toEqual(new Set(["slash-agent"]));
expect(result.knownAgentIds.has("slash-agent")).toBe(true);
});
describe("workspaceAgentVisibilityEqual", () => {
it("returns true for identical sets", () => {
const a = { activeAgentIds: new Set(["a", "b"]), knownAgentIds: new Set(["a", "b", "c"]) };
const b = { activeAgentIds: new Set(["a", "b"]), knownAgentIds: new Set(["a", "b", "c"]) };
expect(workspaceAgentVisibilityEqual(a, b)).toBe(true);
});
it("returns false when activeAgentIds differ", () => {
const a = { activeAgentIds: new Set(["a"]), knownAgentIds: new Set(["a"]) };
const b = { activeAgentIds: new Set(["b"]), knownAgentIds: new Set(["a"]) };
expect(workspaceAgentVisibilityEqual(a, b)).toBe(false);
});
it("returns false when knownAgentIds differ", () => {
const a = { activeAgentIds: new Set(["a"]), knownAgentIds: new Set(["a"]) };
const b = { activeAgentIds: new Set(["a"]), knownAgentIds: new Set(["a", "b"]) };
expect(workspaceAgentVisibilityEqual(a, b)).toBe(false);
});
it("returns true for empty sets", () => {
const a = { activeAgentIds: new Set<string>(), knownAgentIds: new Set<string>() };
const b = { activeAgentIds: new Set<string>(), knownAgentIds: new Set<string>() };
expect(workspaceAgentVisibilityEqual(a, b)).toBe(true);
});
});
});
@@ -1,68 +1,66 @@
import type { Agent } from "@/stores/session-store";
import { normalizeWorkspaceIdentity } from "@/utils/workspace-identity";
function sortAgentsByCreatedAtDescending(agents: Agent[]): Agent[] {
const sorted = [...agents];
sorted.sort((left, right) => {
const createdAtDelta = right.createdAt.getTime() - left.createdAt.getTime();
if (createdAtDelta !== 0) {
return createdAtDelta;
}
return right.lastActivityAt.getTime() - left.lastActivityAt.getTime();
});
return sorted;
}
function trimNonEmpty(value: string | null | undefined): string | null {
if (!value) {
return null;
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : null;
}
function normalizeWorkspaceId(value: string | null | undefined): string {
return normalizeWorkspaceIdentity(value) ?? "";
}
export interface WorkspaceAgentVisibility {
activeAgentIds: Set<string>;
knownAgentIds: Set<string>;
}
export function deriveWorkspaceAgentVisibility(input: {
sessionAgents: Map<string, Agent> | undefined;
workspaceId: string;
}): {
visibleAgents: Agent[];
lookupById: Map<string, Agent>;
} {
}): WorkspaceAgentVisibility {
const { sessionAgents, workspaceId } = input;
const normalizedWorkspaceId = normalizeWorkspaceId(workspaceId);
if (!sessionAgents || !workspaceId) {
return {
visibleAgents: [],
lookupById: new Map<string, Agent>(),
activeAgentIds: new Set<string>(),
knownAgentIds: new Set<string>(),
};
}
const lookupById = new Map<string, Agent>();
const visible: Agent[] = [];
const activeAgentIds = new Set<string>();
const knownAgentIds = new Set<string>();
for (const agent of sessionAgents.values()) {
if (normalizeWorkspaceId(agent.cwd) !== normalizedWorkspaceId) {
continue;
}
lookupById.set(agent.id, agent);
knownAgentIds.add(agent.id);
if (!agent.archivedAt) {
visible.push(agent);
activeAgentIds.add(agent.id);
}
}
return {
visibleAgents: sortAgentsByCreatedAtDescending(visible),
lookupById,
};
return { activeAgentIds, knownAgentIds };
}
export function workspaceAgentVisibilityEqual(
a: WorkspaceAgentVisibility,
b: WorkspaceAgentVisibility,
): boolean {
return setsEqual(a.activeAgentIds, b.activeAgentIds) && setsEqual(a.knownAgentIds, b.knownAgentIds);
}
function setsEqual(a: Set<string>, b: Set<string>): boolean {
if (a.size !== b.size) {
return false;
}
for (const item of a) {
if (!b.has(item)) {
return false;
}
}
return true;
}
export function canOpenAgentTabFromRoute(input: {
agentId: string;
agentsHydrated: boolean;
workspaceAgentLookup: Map<string, Agent>;
knownAgentIds: Set<string>;
}): boolean {
if (!input.agentId.trim()) {
return false;
@@ -70,13 +68,13 @@ export function canOpenAgentTabFromRoute(input: {
if (!input.agentsHydrated) {
return true;
}
return input.workspaceAgentLookup.has(input.agentId);
return input.knownAgentIds.has(input.agentId);
}
export function shouldPruneWorkspaceAgentTab(input: {
agentId: string;
agentsHydrated: boolean;
workspaceAgentLookup: Map<string, Agent>;
knownAgentIds: Set<string>;
}): boolean {
if (!input.agentId.trim()) {
return false;
@@ -84,5 +82,5 @@ export function shouldPruneWorkspaceAgentTab(input: {
if (!input.agentsHydrated) {
return false;
}
return !input.workspaceAgentLookup.has(input.agentId);
return !input.knownAgentIds.has(input.agentId);
}
@@ -7,7 +7,7 @@ import { AgentStreamView } from '@/components/agent-stream-view'
import type { ImageAttachment } from '@/components/message-input'
import { useAgentFormState } from '@/hooks/use-agent-form-state'
import { useDraftAgentCreateFlow } from '@/hooks/use-draft-agent-create-flow'
import { useHostRuntimeSession } from '@/runtime/host-runtime'
import { useHostRuntimeClient, useHostRuntimeIsConnected } from '@/runtime/host-runtime'
import type { Agent } from '@/stores/session-store'
import { encodeImages } from '@/utils/encode-images'
import type { AgentCapabilityFlags, AgentSessionConfig } from '@server/server/agent/agent-sdk-types'
@@ -40,7 +40,8 @@ export function WorkspaceDraftAgentTab({
onCreated,
onOpenWorkspaceFile,
}: WorkspaceDraftAgentTabProps) {
const { client, isConnected } = useHostRuntimeSession(serverId)
const client = useHostRuntimeClient(serverId)
const isConnected = useHostRuntimeIsConnected(serverId)
const addImagesRef = useRef<((images: ImageAttachment[]) => void) | null>(null)
const {
@@ -15,6 +15,7 @@ export interface BuildWorkspacePaneContentModelInput {
tab: WorkspaceTabDescriptor;
normalizedServerId: string;
normalizedWorkspaceId: string;
isPaneFocused: boolean;
onOpenTab: (target: WorkspaceTabDescriptor["target"]) => void;
onCloseCurrentTab: () => void;
onRetargetCurrentTab: (target: WorkspaceTabDescriptor["target"]) => void;
@@ -25,6 +26,7 @@ export function buildWorkspacePaneContentModel({
tab,
normalizedServerId,
normalizedWorkspaceId,
isPaneFocused,
onOpenTab,
onCloseCurrentTab,
onRetargetCurrentTab,
@@ -40,6 +42,7 @@ export function buildWorkspacePaneContentModel({
serverId: normalizedServerId,
workspaceId: normalizedWorkspaceId,
tabId: tab.tabId,
isPaneFocused,
target: tab.target,
openTab: onOpenTab,
closeCurrentTab: onCloseCurrentTab,
@@ -1,4 +1,5 @@
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useStoreWithEqualityFn } from "zustand/traditional";
import { useIsFocused } from "@react-navigation/native";
import {
ActivityIndicator,
@@ -70,7 +71,7 @@ import {
normalizeWorkspaceTabTarget,
workspaceTabTargetsEqual,
} from "@/utils/workspace-tab-identity";
import { useHostRuntimeSession } from "@/runtime/host-runtime";
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
import { useWorkspaceTerminalSessionRetention } from "@/terminal/hooks/use-workspace-terminal-session-retention";
import {
checkoutStatusQueryKey,
@@ -96,6 +97,7 @@ import {
import {
deriveWorkspaceAgentVisibility,
shouldPruneWorkspaceAgentTab,
workspaceAgentVisibilityEqual,
} from "@/screens/workspace/workspace-agent-visibility";
import {
deriveWorkspacePaneState,
@@ -279,6 +281,25 @@ function ResolvedMobileActiveTabTrigger({
);
}
function WorkspaceDocumentTitleEffect({
label,
titleState,
}: {
label: string;
titleState: "ready" | "loading";
}) {
useEffect(() => {
if (Platform.OS !== "web" || typeof document === "undefined") {
return;
}
const resolvedLabel = label.trim();
document.title =
titleState === "loading" ? "Loading..." : resolvedLabel || "Workspace";
}, [label, titleState]);
return null;
}
function MobileWorkspaceTabOption({
tab,
tabIndex,
@@ -519,6 +540,12 @@ export function WorkspaceScreen({
workspaceId,
openIntent,
}: WorkspaceScreenProps) {
const isFocused = useIsFocused();
if (!isFocused) {
return <View style={{ flex: 1 }} />;
}
return (
<ExplorerSidebarAnimationProvider>
<WorkspaceScreenContent
@@ -539,7 +566,6 @@ function WorkspaceScreenContent({
const isDarkMode = useColorScheme() === "dark";
const mainBackgroundColor = isDarkMode ? theme.colors.surface1 : theme.colors.surface0;
const toast = useToast();
const isScreenFocused = useIsFocused();
const isMobile =
UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm";
@@ -555,20 +581,18 @@ function WorkspaceScreenContent({
});
const queryClient = useQueryClient();
const { client, isConnected } = useHostRuntimeSession(normalizedServerId);
const client = useHostRuntimeClient(normalizedServerId);
const isConnected = useHostRuntimeIsConnected(normalizedServerId);
const sessionAgents = useSessionStore(
(state) => state.sessions[normalizedServerId]?.agents
);
const workspaceAgentVisibility = useMemo(
() =>
const workspaceAgentVisibility = useStoreWithEqualityFn(
useSessionStore,
(state) =>
deriveWorkspaceAgentVisibility({
sessionAgents,
sessionAgents: state.sessions[normalizedServerId]?.agents,
workspaceId: normalizedWorkspaceId,
}),
[normalizedWorkspaceId, sessionAgents]
workspaceAgentVisibilityEqual,
);
const workspaceAgents = workspaceAgentVisibility.visibleAgents;
const terminalsQueryKey = useMemo(
() => ["terminals", normalizedServerId, normalizedWorkspaceId] as const,
@@ -798,8 +822,6 @@ function WorkspaceScreenContent({
return () => handler.remove();
}, [closeToAgent, isExplorerOpen]);
const agentsById = workspaceAgentVisibility.lookupById;
const persistenceKey = useMemo(
() =>
buildWorkspaceTabPersistenceKey({
@@ -828,7 +850,6 @@ function WorkspaceScreenContent({
const reorderWorkspaceTabsInPane = useWorkspaceLayoutStore((state) => state.reorderTabsInPane);
const pendingByDraftId = useCreateFlowStore((state) => state.pendingByDraftId);
const consumedOpenIntentsRef = useRef(new Set<string>());
const reopenedArchivedOpenIntentsRef = useRef(new Set<string>());
const pendingCloseTabIdsRef = useRef(new Set<string>());
const [resolvedOpenIntentKey, setResolvedOpenIntentKey] = useState<string | null>(null);
const currentOpenIntentKey = useMemo(
@@ -849,6 +870,24 @@ function WorkspaceScreenContent({
}),
[uiTabs, workspaceLayout]
);
const setFocusedAgentId = useSessionStore((state) => state.setFocusedAgentId);
const focusedPaneAgentId = useMemo(() => {
const target = focusedPaneTabState.activeTab?.descriptor.target;
if (target?.kind !== "agent") {
return null;
}
return target.agentId;
}, [focusedPaneTabState.activeTab]);
useEffect(() => {
setFocusedAgentId(normalizedServerId, focusedPaneAgentId);
}, [focusedPaneAgentId, normalizedServerId, setFocusedAgentId]);
useEffect(() => {
return () => {
setFocusedAgentId(normalizedServerId, null);
};
}, [normalizedServerId, setFocusedAgentId]);
const ensureWorkspaceTab = useCallback(
function ensureWorkspaceTab(target: WorkspaceTabTarget): string | null {
@@ -914,43 +953,6 @@ function WorkspaceScreenContent({
}
}, [currentOpenIntentKey, resolvedOpenIntentKey]);
useEffect(() => {
if (!client || !isConnected || openIntent?.kind !== "agent" || !currentOpenIntentKey) {
return;
}
const agentId = openIntent.agentId.trim();
if (!agentId) {
return;
}
if (reopenedArchivedOpenIntentsRef.current.has(currentOpenIntentKey)) {
return;
}
const agent = agentsById.get(agentId) ?? null;
if (!agent?.archivedAt) {
return;
}
reopenedArchivedOpenIntentsRef.current.add(currentOpenIntentKey);
void client.refreshAgent(agentId).catch((error) => {
console.warn("[WorkspaceScreen] Failed to reopen archived agent", {
serverId: normalizedServerId,
workspaceId: normalizedWorkspaceId,
agentId,
error,
});
});
}, [
agentsById,
client,
currentOpenIntentKey,
isConnected,
normalizedServerId,
normalizedWorkspaceId,
openIntent,
]);
useEffect(() => {
if (!openIntent || !persistenceKey) {
return;
@@ -1032,12 +1034,12 @@ function WorkspaceScreenContent({
return pending?.serverId === normalizedServerId && pending.lifecycle === "active";
});
for (const agent of workspaceAgents) {
for (const agentId of workspaceAgentVisibility.activeAgentIds) {
const representedByTarget = uiTabs.some(
(tab) => tab.target.kind === "agent" && tab.target.agentId === agent.id
(tab) => tab.target.kind === "agent" && tab.target.agentId === agentId
);
const representedByDeterministicTabId = uiTabs.some(
(tab) => tab.tabId === `agent_${agent.id}`
(tab) => tab.tabId === `agent_${agentId}`
);
if (
hasActivePendingDraftCreateInWorkspace &&
@@ -1046,7 +1048,7 @@ function WorkspaceScreenContent({
) {
continue;
}
ensureWorkspaceTab({ kind: "agent", agentId: agent.id });
ensureWorkspaceTab({ kind: "agent", agentId });
}
for (const terminal of terminals) {
ensureWorkspaceTab({ kind: "terminal", terminalId: terminal.id });
@@ -1061,7 +1063,7 @@ function WorkspaceScreenContent({
shouldPruneWorkspaceAgentTab({
agentId: tab.target.agentId,
agentsHydrated: hasHydratedAgents,
workspaceAgentLookup: agentsById,
knownAgentIds: workspaceAgentVisibility.knownAgentIds,
})
) {
closeWorkspaceTab(persistenceKey, tab.tabId);
@@ -1083,7 +1085,7 @@ function WorkspaceScreenContent({
terminals,
terminalsQuery.isSuccess,
uiTabs,
workspaceAgents,
workspaceAgentVisibility,
]);
const activeTabId = resolvedPaneTabState.activeTabId;
@@ -1137,7 +1139,7 @@ function WorkspaceScreenContent({
emptyWorkspaceSeedRef.current = null;
return;
}
if (workspaceAgents.length > 0 || terminals.length > 0) {
if (workspaceAgentVisibility.activeAgentIds.size > 0 || terminals.length > 0) {
emptyWorkspaceSeedRef.current = null;
return;
}
@@ -1159,7 +1161,7 @@ function WorkspaceScreenContent({
persistenceKey,
terminals.length,
tabs.length,
workspaceAgents.length,
workspaceAgentVisibility.activeAgentIds.size,
]);
const handleOpenFileFromExplorer = useCallback(
@@ -1442,7 +1444,7 @@ function WorkspaceScreenContent({
const handleCopyResumeCommand = useCallback(
async (agentId: string) => {
if (!agentId) return;
const agent = sessionAgents?.get(agentId) ?? null;
const agent = useSessionStore.getState().sessions[normalizedServerId]?.agents?.get(agentId) ?? null;
const providerSessionId =
agent?.runtimeInfo?.sessionId ?? agent?.persistence?.sessionId ?? null;
if (!agent || !providerSessionId) {
@@ -1467,7 +1469,7 @@ function WorkspaceScreenContent({
toast.error("Copy failed");
}
},
[sessionAgents, toast]
[normalizedServerId, toast]
);
const handleCopyWorkspacePath = useCallback(async () => {
@@ -1773,7 +1775,7 @@ function WorkspaceScreenContent({
] as const,
enabled: Boolean(normalizedServerId && normalizedWorkspaceId),
priority: 100,
isActive: () => isScreenFocused,
isActive: () => true,
handle: handleWorkspaceTabAction,
});
@@ -1794,21 +1796,29 @@ function WorkspaceScreenContent({
] as const,
enabled: Boolean(normalizedServerId && normalizedWorkspaceId),
priority: 100,
isActive: () => isScreenFocused,
isActive: () => true,
handle: handleWorkspacePaneAction,
});
const activeTabDescriptor = activeTab?.descriptor ?? null;
useEffect(() => {
if (Platform.OS !== "web" || typeof document === "undefined" || activeTabDescriptor) {
return;
}
document.title = "Workspace";
}, [activeTabDescriptor]);
const buildPaneContentModel = useCallback(
(input: {
tab: WorkspaceTabDescriptor;
paneId?: string | null;
isPaneFocused: boolean;
focusPaneBeforeOpen?: boolean;
}) =>
buildWorkspacePaneContentModel({
tab: input.tab,
normalizedServerId,
normalizedWorkspaceId,
isPaneFocused: input.isPaneFocused,
onOpenTab: (target) => {
if (!persistenceKey) {
return;
@@ -1855,6 +1865,7 @@ function WorkspaceScreenContent({
? buildPaneContentModel({
tab: activeTabDescriptor,
paneId: focusedPaneTabState.pane?.id ?? null,
isPaneFocused: true,
focusPaneBeforeOpen: false,
})
: null,
@@ -1885,6 +1896,20 @@ function WorkspaceScreenContent({
return (
<View style={[styles.container, { backgroundColor: mainBackgroundColor }]}>
{Platform.OS === "web" && activeTabDescriptor ? (
<WorkspaceTabPresentationResolver
tab={activeTabDescriptor}
serverId={normalizedServerId}
workspaceId={normalizedWorkspaceId}
>
{(presentation) => (
<WorkspaceDocumentTitleEffect
label={presentation.label}
titleState={presentation.titleState}
/>
)}
</WorkspaceTabPresentationResolver>
) : null}
<View style={styles.threePaneRow}>
<View style={styles.centerColumn}>
<ScreenHeader
@@ -2115,13 +2140,14 @@ function WorkspaceScreenContent({
onCloseOtherTabs={handleCloseOtherTabsInPane}
onSelectNewTabOption={handleSelectNewTabOption}
newTabAgentOptionId={NEW_TAB_AGENT_OPTION_ID}
buildPaneContentModel={({ paneId, tab }) =>
buildPaneContentModel({
tab,
paneId,
focusPaneBeforeOpen: true,
})
}
buildPaneContentModel={({ paneId, tab, isPaneFocused }) =>
buildPaneContentModel({
tab,
paneId,
isPaneFocused,
focusPaneBeforeOpen: true,
})
}
onFocusPane={(paneId) => {
focusWorkspacePane(persistenceKey, paneId);
}}