feat(app): retain Explorer across layout and appearance changes

Keep one workspace Explorer host and destination-aware navigation so folding, theme updates and compact sheets preserve the current panel. Scope pull request discovery and timeline demand to active surfaces.
This commit is contained in:
Mohamed Boudra
2026-10-19 16:57:57 +02:00
parent 49954e1e22
commit a264f41d4a
71 changed files with 2365 additions and 1748 deletions
+1 -2
View File
@@ -1427,10 +1427,9 @@ function useDiffTabNavigation({
openWorkspacePullRequest({
isCompact: isMobile,
workspaceKey: persistenceKey,
checkout: { serverId, cwd, isGit: true },
destination: pullRequestOpenLocation,
});
}, [cwd, isMobile, persistenceKey, pullRequestOpenLocation, serverId]);
}, [isMobile, persistenceKey, pullRequestOpenLocation]);
return {
openDiff,
openCommit,
@@ -0,0 +1,27 @@
import { createContext, useContext, type ReactNode } from "react";
import type { DaemonClient } from "@getpaseo/client/internal/daemon-client";
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
export interface PullRequestConnection {
client: Pick<DaemonClient, "checkoutPrStatus" | "pullRequestTimeline"> | null;
isConnected: boolean;
}
const ConnectionContext = createContext<((serverId: string) => PullRequestConnection) | null>(null);
/** Supplies the PR read connection, shared by discovery, rail status and timeline content. */
export function PullRequestConnectionProvider({
connection,
children,
}: {
connection: (serverId: string) => PullRequestConnection;
children: ReactNode;
}) {
return <ConnectionContext value={connection}>{children}</ConnectionContext>;
}
export function usePullRequestConnection(serverId: string): PullRequestConnection {
const supplied = useContext(ConnectionContext);
const client = useHostRuntimeClient(serverId);
const isConnected = useHostRuntimeIsConnected(serverId);
return supplied ? supplied(serverId) : { client, isConnected };
}
@@ -406,7 +406,7 @@ describe("selectPrPaneState", () => {
expect(state.data?.forgeSpecific).toEqual({ ...gitlabFacts, mergeStatus: null });
});
it("returns null data when the consumer disabled timeline rendering", () => {
it("keeps cached content when retained activity disables timeline demand", () => {
const state = selectPrPaneState({
...baseSelectInput,
status: prStatus(),
@@ -414,7 +414,7 @@ describe("selectPrPaneState", () => {
timelinePayload: timelinePayload(),
});
expect(state.data).toBeNull();
expect(state.data).not.toBeNull();
expect(state.prNumber).toBe(42);
});
@@ -1,7 +1,7 @@
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
import { usePullRequestConnection } from "@/git/pull-request-connection";
import type {
CheckoutPrStatusResponse,
PullRequestTimelineResponse,
@@ -20,7 +20,7 @@ type PullRequestTimeline = PullRequestTimelineResponse["payload"];
export interface UsePrPaneDataOptions {
serverId: string;
cwd: string;
enabled?: boolean;
enabled: boolean;
timelineEnabled?: boolean;
}
@@ -166,7 +166,7 @@ export interface SelectPrPaneStateInput {
export function selectPrPaneState(input: SelectPrPaneStateInput): UsePrPaneDataResult {
const identity = extractPrRepoIdentity(input.status);
const data =
identity.prNumber === null || !input.timelineEnabled
identity.prNumber === null
? null
: mapPrPaneData(input.status, input.timelinePayload, undefined, input.forge ?? "github");
const statusRefreshing = input.statusIsFetching && !input.statusIsLoading;
@@ -198,12 +198,11 @@ export function selectPrPaneState(input: SelectPrPaneStateInput): UsePrPaneDataR
export function usePrPaneData({
serverId,
cwd,
enabled = true,
enabled,
timelineEnabled = enabled,
}: UsePrPaneDataOptions): UsePrPaneDataResult {
const { t } = useTranslation();
const daemonClient = useHostRuntimeClient(serverId);
const isConnected = useHostRuntimeIsConnected(serverId);
const { client: daemonClient, isConnected } = usePullRequestConnection(serverId);
const checkoutPrStatus = useCheckoutPrStatusQuery({ serverId, cwd, enabled });
const status = checkoutPrStatus.status;
const identity = extractPrRepoIdentity(status);
+3 -5
View File
@@ -1,6 +1,6 @@
import { useQuery } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
import { usePullRequestConnection } from "@/git/pull-request-connection";
import { checkoutPrStatusQueryKey } from "@/git/query-keys";
import { normalizeForge } from "@/git/forge";
import { selectPrHintFromStatus, type PrHint } from "@/git/pr-hint";
@@ -25,8 +25,7 @@ export function useCheckoutPrStatusQuery({
enabled = true,
}: UseCheckoutPrStatusQueryOptions) {
const { t } = useTranslation();
const client = useHostRuntimeClient(serverId);
const isConnected = useHostRuntimeIsConnected(serverId);
const { client, isConnected } = usePullRequestConnection(serverId);
const query = useQuery({
queryKey: checkoutPrStatusQueryKey(serverId, cwd),
@@ -67,8 +66,7 @@ export function useWorkspacePrHint({
enabled = true,
}: UseCheckoutPrStatusQueryOptions): PrHint | null {
const { t } = useTranslation();
const client = useHostRuntimeClient(serverId);
const isConnected = useHostRuntimeIsConnected(serverId);
const { client, isConnected } = usePullRequestConnection(serverId);
const query = useQuery<CheckoutPrStatusPayload, Error, PrHint | null>({
queryKey: checkoutPrStatusQueryKey(serverId, cwd),