WIP: workspace execution refactor + notification routing + setup store updates
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import { Text, View } from "react-native";
|
||||
import { FileText } from "lucide-react-native";
|
||||
import invariant from "tiny-invariant";
|
||||
import { FilePane } from "@/components/file-pane";
|
||||
import { usePaneContext } from "@/panels/pane-context";
|
||||
import type { PanelRegistration } from "@/panels/panel-registry";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { resolveWorkspaceExecutionAuthority } from "@/utils/workspace-execution";
|
||||
|
||||
function useFilePanelDescriptor(target: { kind: "file"; path: string }) {
|
||||
const fileName = target.path.split("/").filter(Boolean).pop() ?? target.path;
|
||||
@@ -17,8 +20,27 @@ function useFilePanelDescriptor(target: { kind: "file"; path: string }) {
|
||||
|
||||
function FilePanel() {
|
||||
const { serverId, workspaceId, target } = usePaneContext();
|
||||
const authority = useSessionStore((state) =>
|
||||
resolveWorkspaceExecutionAuthority({
|
||||
workspaces: state.sessions[serverId]?.workspaces,
|
||||
workspaceId,
|
||||
}),
|
||||
);
|
||||
invariant(target.kind === "file", "FilePanel requires file target");
|
||||
return <FilePane serverId={serverId} workspaceRoot={workspaceId} filePath={target.path} />;
|
||||
if (!authority) {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: "center", justifyContent: "center", padding: 16 }}>
|
||||
<Text>Workspace execution directory not found.</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<FilePane
|
||||
serverId={serverId}
|
||||
workspaceRoot={authority.workspaceDirectory}
|
||||
filePath={target.path}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const filePanelRegistration: PanelRegistration<"file"> = {
|
||||
|
||||
@@ -19,6 +19,10 @@ import { useProviderRecency } from "@/stores/provider-recency-store";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { normalizeAgentSnapshot } from "@/utils/agent-snapshots";
|
||||
import { toErrorMessage } from "@/utils/error-messages";
|
||||
import {
|
||||
getWorkspaceExecutionAuthority,
|
||||
requireWorkspaceRecordId,
|
||||
} from "@/utils/workspace-execution";
|
||||
|
||||
const MAX_VISIBLE_PROVIDER_TILES = 4;
|
||||
|
||||
@@ -36,11 +40,11 @@ function LauncherPanel() {
|
||||
const { serverId, workspaceId, target, retargetCurrentTab, isPaneFocused } = usePaneContext();
|
||||
const client = useHostRuntimeClient(serverId);
|
||||
const isConnected = useHostRuntimeIsConnected(serverId);
|
||||
const workspaceDirectory = useSessionStore(
|
||||
(state) =>
|
||||
state.sessions[serverId]?.workspaces.get(workspaceId)?.workspaceDirectory ??
|
||||
state.sessions[serverId]?.workspaces.get(workspaceId)?.projectRootPath,
|
||||
);
|
||||
const workspaces = useSessionStore((state) => state.sessions[serverId]?.workspaces);
|
||||
const workspaceAuthority = getWorkspaceExecutionAuthority({ workspaces, workspaceId });
|
||||
const workspaceDirectory = workspaceAuthority.ok
|
||||
? workspaceAuthority.authority.workspaceDirectory
|
||||
: null;
|
||||
const { providers, recordUsage } = useProviderRecency();
|
||||
const setAgents = useSessionStore((state) => state.setAgents);
|
||||
const [pendingAction, setPendingAction] = useState<string | null>(null);
|
||||
@@ -62,6 +66,11 @@ function LauncherPanel() {
|
||||
setErrorMessage(!workspaceDirectory ? "Workspace directory not found" : "Host is not connected");
|
||||
return;
|
||||
}
|
||||
if (!workspaceAuthority.ok) {
|
||||
setErrorMessage(workspaceAuthority.message);
|
||||
return;
|
||||
}
|
||||
const persistedWorkspaceId = requireWorkspaceRecordId(workspaceAuthority.authority.workspaceId);
|
||||
|
||||
setPendingAction(providerId);
|
||||
setErrorMessage(null);
|
||||
@@ -70,6 +79,7 @@ function LauncherPanel() {
|
||||
const agent = await client.createAgent({
|
||||
provider: providerId,
|
||||
cwd: workspaceDirectory,
|
||||
workspaceId: persistedWorkspaceId,
|
||||
terminal: true,
|
||||
});
|
||||
recordUsage(providerId);
|
||||
@@ -87,7 +97,16 @@ function LauncherPanel() {
|
||||
setPendingAction((current) => (current === providerId ? null : current));
|
||||
}
|
||||
},
|
||||
[client, isConnected, recordUsage, retargetCurrentTab, serverId, setAgents, workspaceDirectory],
|
||||
[
|
||||
client,
|
||||
isConnected,
|
||||
recordUsage,
|
||||
retargetCurrentTab,
|
||||
serverId,
|
||||
setAgents,
|
||||
workspaceAuthority,
|
||||
workspaceDirectory,
|
||||
],
|
||||
);
|
||||
|
||||
const openDraftTab = useCallback(() => {
|
||||
@@ -131,7 +150,9 @@ function LauncherPanel() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.content, styles.loadingContent]}>
|
||||
<ActivityIndicator />
|
||||
<Text style={styles.errorText}>
|
||||
{workspaceAuthority.ok ? "Workspace execution directory not found." : workspaceAuthority.message}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Terminal } from "lucide-react-native";
|
||||
import { View } from "react-native";
|
||||
import { Text, View } from "react-native";
|
||||
import { useIsFocused } from "@react-navigation/native";
|
||||
import invariant from "tiny-invariant";
|
||||
import type { ListTerminalsResponse } from "@server/shared/messages";
|
||||
@@ -8,6 +8,7 @@ import { TerminalPane } from "@/components/terminal-pane";
|
||||
import { usePaneContext } from "@/panels/pane-context";
|
||||
import type { PanelDescriptor, PanelRegistration } from "@/panels/panel-registry";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { getWorkspaceExecutionAuthority } from "@/utils/workspace-execution";
|
||||
|
||||
type ListTerminalsPayload = ListTerminalsResponse["payload"];
|
||||
|
||||
@@ -24,18 +25,22 @@ function useTerminalPanelDescriptor(
|
||||
context: { serverId: string; workspaceId: string },
|
||||
): PanelDescriptor {
|
||||
const client = useSessionStore((state) => state.sessions[context.serverId]?.client ?? null);
|
||||
const workspaceDirectory = useSessionStore(
|
||||
(state) =>
|
||||
state.sessions[context.serverId]?.workspaces.get(context.workspaceId)?.workspaceDirectory ??
|
||||
state.sessions[context.serverId]?.workspaces.get(context.workspaceId)?.projectRootPath ??
|
||||
null,
|
||||
);
|
||||
const workspaces = useSessionStore((state) => state.sessions[context.serverId]?.workspaces);
|
||||
const workspaceAuthority = getWorkspaceExecutionAuthority({
|
||||
workspaces,
|
||||
workspaceId: context.workspaceId,
|
||||
});
|
||||
const workspaceDirectory = workspaceAuthority.ok
|
||||
? workspaceAuthority.authority.workspaceDirectory
|
||||
: null;
|
||||
const terminalsQuery = useQuery({
|
||||
queryKey: ["terminals", context.serverId, workspaceDirectory] as const,
|
||||
enabled: Boolean(client && workspaceDirectory),
|
||||
queryFn: async (): Promise<ListTerminalsPayload> => {
|
||||
if (!client || !workspaceDirectory) {
|
||||
return { cwd: workspaceDirectory ?? "", terminals: [], requestId: "missing-client" };
|
||||
throw new Error(
|
||||
workspaceAuthority.ok ? "Workspace execution directory not found" : workspaceAuthority.message,
|
||||
);
|
||||
}
|
||||
return client.listTerminals(workspaceDirectory);
|
||||
},
|
||||
@@ -56,18 +61,27 @@ function useTerminalPanelDescriptor(
|
||||
function TerminalPanel() {
|
||||
const isFocused = useIsFocused();
|
||||
const { serverId, workspaceId, target, isPaneFocused } = usePaneContext();
|
||||
const workspaceDirectory = useSessionStore(
|
||||
(state) =>
|
||||
state.sessions[serverId]?.workspaces.get(workspaceId)?.workspaceDirectory ??
|
||||
state.sessions[serverId]?.workspaces.get(workspaceId)?.projectRootPath ??
|
||||
null,
|
||||
);
|
||||
const workspaces = useSessionStore((state) => state.sessions[serverId]?.workspaces);
|
||||
const workspaceAuthority = getWorkspaceExecutionAuthority({ workspaces, workspaceId });
|
||||
const workspaceDirectory = workspaceAuthority.ok
|
||||
? workspaceAuthority.authority.workspaceDirectory
|
||||
: null;
|
||||
invariant(target.kind === "terminal", "TerminalPanel requires terminal target");
|
||||
|
||||
if (!isFocused || !workspaceDirectory) {
|
||||
if (!isFocused) {
|
||||
return <View style={{ flex: 1 }} />;
|
||||
}
|
||||
|
||||
if (!workspaceDirectory) {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: "center", justifyContent: "center", padding: 16 }}>
|
||||
<Text>
|
||||
{workspaceAuthority.ok ? "Workspace execution directory not found." : workspaceAuthority.message}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TerminalPane
|
||||
serverId={serverId}
|
||||
|
||||
Reference in New Issue
Block a user