Update files
This commit is contained in:
@@ -70,6 +70,7 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { buildHostAgentDraftRoute } from "@/utils/host-routes";
|
||||
|
||||
const DROPDOWN_WIDTH = 220;
|
||||
const EMPTY_STREAM_ITEMS: StreamItem[] = [];
|
||||
@@ -124,7 +125,11 @@ export function AgentReadyScreen({
|
||||
targetMs: 300,
|
||||
});
|
||||
}
|
||||
router.replace("/agent" as any);
|
||||
if (targetServerId) {
|
||||
router.replace(buildHostAgentDraftRoute(targetServerId) as any);
|
||||
return;
|
||||
}
|
||||
router.replace("/" as any);
|
||||
}, [resolvedAgentId, resolvedServerId, router]);
|
||||
|
||||
const focusServerId = resolvedServerId;
|
||||
|
||||
@@ -14,7 +14,7 @@ import { StyleSheet, UnistylesRuntime, useUnistyles } from "react-native-unistyl
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller";
|
||||
import Animated, { useAnimatedStyle, useSharedValue } from "react-native-reanimated";
|
||||
import { Folder, GitBranch, Menu, Monitor, PanelLeft } from "lucide-react-native";
|
||||
import { Folder, GitBranch, Menu, PanelLeft } from "lucide-react-native";
|
||||
import { HeaderToggleButton } from "@/components/headers/header-toggle-button";
|
||||
import { AgentInputArea } from "@/components/agent-input-area";
|
||||
import { AgentStreamView } from "@/components/agent-stream-view";
|
||||
@@ -29,7 +29,6 @@ import {
|
||||
} from "@/hooks/use-checkout-status-query";
|
||||
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
|
||||
import { useDaemonRegistry } from "@/contexts/daemon-registry-context";
|
||||
import { formatConnectionStatus } from "@/utils/daemons";
|
||||
import { buildBranchComboOptions, normalizeBranchOptionName } from "@/utils/branch-suggestions";
|
||||
import { shortenPath } from "@/utils/shorten-path";
|
||||
import { usePanelStore } from "@/stores/panel-store";
|
||||
@@ -47,6 +46,7 @@ import type {
|
||||
AgentSessionConfig,
|
||||
} from "@server/server/agent/agent-sdk-types";
|
||||
import { AGENT_PROVIDER_DEFINITIONS } from "@server/server/agent/provider-manifest";
|
||||
import { buildHostAgentDetailRoute } from "@/utils/host-routes";
|
||||
|
||||
const DRAFT_AGENT_ID = "__new_agent__";
|
||||
const EMPTY_PENDING_PERMISSIONS = new Map();
|
||||
@@ -112,11 +112,13 @@ type DraftAgentParams = {
|
||||
type DraftAgentScreenProps = {
|
||||
isVisible?: boolean;
|
||||
onCreateFlowActiveChange?: (active: boolean) => void;
|
||||
forcedServerId?: string;
|
||||
};
|
||||
|
||||
export function DraftAgentScreen({
|
||||
isVisible = true,
|
||||
onCreateFlowActiveChange,
|
||||
forcedServerId,
|
||||
}: DraftAgentScreenProps = {}) {
|
||||
const { theme } = useUnistyles();
|
||||
const router = useRouter();
|
||||
@@ -144,7 +146,11 @@ export function DraftAgentScreen({
|
||||
};
|
||||
});
|
||||
|
||||
const resolvedServerId = getParamValue(params.serverId);
|
||||
const forcedServerIdParam = forcedServerId?.trim();
|
||||
const resolvedServerId =
|
||||
forcedServerIdParam && forcedServerIdParam.length > 0
|
||||
? forcedServerIdParam
|
||||
: getParamValue(params.serverId);
|
||||
const resolvedProvider = getValidProvider(getParamValue(params.provider));
|
||||
const resolvedMode = getValidMode(resolvedProvider, getParamValue(params.modeId));
|
||||
const resolvedModel = getParamValue(params.model);
|
||||
@@ -215,25 +221,9 @@ export function DraftAgentScreen({
|
||||
isCreateFlow: true,
|
||||
onlineServerIds,
|
||||
});
|
||||
const daemonLabelByServerId = useMemo(() => {
|
||||
const map = new Map<string, string>();
|
||||
for (const daemon of daemons) {
|
||||
const label = daemon.label?.trim();
|
||||
map.set(daemon.serverId, label && label.length > 0 ? label : daemon.serverId);
|
||||
}
|
||||
return map;
|
||||
}, [daemons]);
|
||||
const hostEntry = selectedServerId
|
||||
? connectionStates.get(selectedServerId)
|
||||
: undefined;
|
||||
const selectedDaemonLabel = selectedServerId
|
||||
? daemonLabelByServerId.get(selectedServerId)
|
||||
: null;
|
||||
const hostLabel =
|
||||
selectedDaemonLabel ??
|
||||
hostEntry?.daemon.label ??
|
||||
selectedServerId ??
|
||||
"Select host";
|
||||
const isMobile =
|
||||
UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm";
|
||||
const isSidebarOpen = isMobile ? mobileView === "agent-list" : desktopAgentListOpen;
|
||||
@@ -242,7 +232,6 @@ export function DraftAgentScreen({
|
||||
? theme.colors.foreground
|
||||
: theme.colors.foregroundMuted;
|
||||
|
||||
const [isHostOpen, setIsHostOpen] = useState(false);
|
||||
const [worktreeMode, setWorktreeMode] = useState<"none" | "create" | "attach">("none");
|
||||
const [baseBranch, setBaseBranch] = useState("");
|
||||
const [worktreeSlug, setWorktreeSlug] = useState("");
|
||||
@@ -252,7 +241,6 @@ export function DraftAgentScreen({
|
||||
const [isBranchOpen, setIsBranchOpen] = useState(false);
|
||||
const [branchSearchQuery, setBranchSearchQuery] = useState("");
|
||||
const [debouncedBranchSearchQuery, setDebouncedBranchSearchQuery] = useState("");
|
||||
const hostAnchorRef = useRef<View>(null);
|
||||
const workingDirAnchorRef = useRef<View>(null);
|
||||
const worktreeAnchorRef = useRef<View>(null);
|
||||
const branchAnchorRef = useRef<View>(null);
|
||||
@@ -670,17 +658,6 @@ export function DraftAgentScreen({
|
||||
worktreeMode === "create"
|
||||
? "Create new worktree"
|
||||
: selectedWorktreeLabel || "Select worktree";
|
||||
const hostOptions = useMemo(
|
||||
() =>
|
||||
daemons.map((daemon) => ({
|
||||
id: daemon.serverId,
|
||||
label: daemonLabelByServerId.get(daemon.serverId) ?? daemon.serverId,
|
||||
description: formatConnectionStatus(
|
||||
connectionStates.get(daemon.serverId)?.status ?? "idle"
|
||||
),
|
||||
})),
|
||||
[connectionStates, daemonLabelByServerId, daemons]
|
||||
);
|
||||
const worktreeComboOptions = useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -930,7 +907,9 @@ export function DraftAgentScreen({
|
||||
const agentId = (result as { id?: string })?.id;
|
||||
if (agentId && selectedServerId) {
|
||||
updatePendingAgentId(agentId);
|
||||
router.replace(`/agent/${selectedServerId}/${agentId}` as any);
|
||||
router.replace(
|
||||
buildHostAgentDetailRoute(selectedServerId, agentId) as any
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1031,7 +1010,7 @@ export function DraftAgentScreen({
|
||||
<View style={isMobile ? styles.stackedSelectorGroup : styles.topSelectorRow}>
|
||||
<FormSelectTrigger
|
||||
controlRef={workingDirAnchorRef}
|
||||
containerStyle={isMobile ? styles.fullSelector : styles.topSelectorPrimary}
|
||||
containerStyle={styles.fullSelector}
|
||||
label="Working directory"
|
||||
value={displayWorkingDir}
|
||||
placeholder="/path/to/project"
|
||||
@@ -1040,16 +1019,6 @@ export function DraftAgentScreen({
|
||||
showLabel={false}
|
||||
valueEllipsizeMode="middle"
|
||||
/>
|
||||
<FormSelectTrigger
|
||||
controlRef={hostAnchorRef}
|
||||
containerStyle={isMobile ? styles.fullSelector : styles.topSelectorSecondary}
|
||||
label="Host"
|
||||
value={hostLabel}
|
||||
placeholder="Select host"
|
||||
onPress={() => setIsHostOpen(true)}
|
||||
icon={<Monitor size={16} color={theme.colors.foregroundMuted} />}
|
||||
showLabel={false}
|
||||
/>
|
||||
</View>
|
||||
{isDirectoryNotExists && (
|
||||
<View style={styles.warningContainer}>
|
||||
@@ -1117,17 +1086,6 @@ export function DraftAgentScreen({
|
||||
{attachWorktreeError ? <Text style={styles.errorInlineText}>{attachWorktreeError}</Text> : null}
|
||||
{worktreeOptionsError ? <Text style={styles.errorInlineText}>{worktreeOptionsError}</Text> : null}
|
||||
</View>
|
||||
<Combobox
|
||||
options={hostOptions}
|
||||
value={selectedServerId ?? ""}
|
||||
onSelect={(serverId) => setSelectedServerIdFromUser(serverId)}
|
||||
title="Host"
|
||||
searchPlaceholder="Search hosts..."
|
||||
open={isHostOpen}
|
||||
onOpenChange={setIsHostOpen}
|
||||
anchorRef={hostAnchorRef}
|
||||
/>
|
||||
|
||||
<Combobox
|
||||
options={worktreeComboOptions}
|
||||
value={
|
||||
|
||||
@@ -7,6 +7,10 @@ import { BackHeader } from "@/components/headers/back-header";
|
||||
import { useSessionDirectory } from "@/hooks/use-session-directory";
|
||||
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
|
||||
import type { Agent } from "@/contexts/session-context";
|
||||
import {
|
||||
buildHostAgentDetailRoute,
|
||||
buildHostAgentDraftRoute,
|
||||
} from "@/utils/host-routes";
|
||||
|
||||
type AgentMatch = {
|
||||
serverId: string;
|
||||
@@ -50,16 +54,21 @@ export function LegacyAgentIdScreen({ agentId }: { agentId: string }) {
|
||||
return;
|
||||
}
|
||||
const match = matches[0];
|
||||
router.replace(`/agent/${match.serverId}/${match.agent.id}` as any);
|
||||
router.replace(buildHostAgentDetailRoute(match.serverId, match.agent.id) as any);
|
||||
}, [isRedirecting, matches, router]);
|
||||
|
||||
const handleGoDraft = useCallback(() => {
|
||||
router.replace("/agent" as any);
|
||||
}, [router]);
|
||||
const firstMatchServerId = matches[0]?.serverId ?? null;
|
||||
if (firstMatchServerId) {
|
||||
router.replace(buildHostAgentDraftRoute(firstMatchServerId) as any);
|
||||
return;
|
||||
}
|
||||
router.replace("/" as any);
|
||||
}, [matches, router]);
|
||||
|
||||
const handleSelectMatch = useCallback(
|
||||
(match: AgentMatch) => {
|
||||
router.replace(`/agent/${match.serverId}/${match.agent.id}` as any);
|
||||
router.replace(buildHostAgentDetailRoute(match.serverId, match.agent.id) as any);
|
||||
},
|
||||
[router]
|
||||
);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { useMemo, useState, useCallback, useEffect } from "react";
|
||||
import { View } from "react-native";
|
||||
import { StyleSheet } from "react-native-unistyles";
|
||||
import { BackHeader } from "@/components/headers/back-header";
|
||||
import { AgentList } from "@/components/agent-list";
|
||||
import { useAllAgentsList } from "@/hooks/use-all-agents-list";
|
||||
import { buildHostAgentDraftRoute } from "@/utils/host-routes";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export function AgentsScreen({ serverId }: { serverId: string }) {
|
||||
const { agents, isRevalidating, refreshAll } = useAllAgentsList({
|
||||
serverId,
|
||||
});
|
||||
|
||||
// Track user-initiated refresh to avoid showing spinner on background revalidation
|
||||
const [isManualRefresh, setIsManualRefresh] = useState(false);
|
||||
|
||||
const handleRefresh = useCallback(() => {
|
||||
setIsManualRefresh(true);
|
||||
refreshAll();
|
||||
}, [refreshAll]);
|
||||
|
||||
// Reset manual refresh flag when revalidation completes
|
||||
useEffect(() => {
|
||||
if (!isRevalidating && isManualRefresh) {
|
||||
setIsManualRefresh(false);
|
||||
}
|
||||
}, [isRevalidating, isManualRefresh]);
|
||||
|
||||
const sortedAgents = useMemo(() => {
|
||||
return [...agents].sort((a, b) => {
|
||||
if (a.requiresAttention && !b.requiresAttention) return -1;
|
||||
if (!a.requiresAttention && b.requiresAttention) return 1;
|
||||
return 0;
|
||||
});
|
||||
}, [agents]);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<BackHeader
|
||||
title="All agents"
|
||||
onBack={() => router.replace(buildHostAgentDraftRoute(serverId) as any)}
|
||||
/>
|
||||
<AgentList
|
||||
agents={sortedAgents}
|
||||
showCheckoutInfo={false}
|
||||
isRefreshing={isManualRefresh && isRevalidating}
|
||||
onRefresh={handleRefresh}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create((theme) => ({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.colors.surface0,
|
||||
},
|
||||
}));
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user