refactor(file-explorer): add deep linking, path navigation, diff preview, and Codex session persistence
This commit is contained in:
@@ -14,17 +14,20 @@ import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||
import { BottomSheetModal } from "@gorhom/bottom-sheet";
|
||||
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
|
||||
import { ChevronDown } from "lucide-react-native";
|
||||
import { useRouter } from "expo-router";
|
||||
import {
|
||||
AssistantMessage,
|
||||
UserMessage,
|
||||
ActivityLog,
|
||||
ToolCall,
|
||||
AgentThoughtMessage,
|
||||
type InlinePathTarget,
|
||||
} from "./message";
|
||||
import { ToolCallBottomSheet } from "./tool-call-bottom-sheet";
|
||||
import type { StreamItem } from "@/types/stream";
|
||||
import type { SelectedToolCall, PendingPermission } from "@/types/shared";
|
||||
import type { Agent } from "@/contexts/session-context";
|
||||
import { useSession } from "@/contexts/session-context";
|
||||
|
||||
export interface AgentStreamViewProps {
|
||||
agentId: string;
|
||||
@@ -52,6 +55,8 @@ export function AgentStreamView({
|
||||
const isProgrammaticScrollRef = useRef(false);
|
||||
const isNearBottomRef = useRef(true);
|
||||
const isUserScrollingRef = useRef(false);
|
||||
const router = useRouter();
|
||||
const { requestDirectoryListing, requestFilePreview } = useSession();
|
||||
|
||||
useEffect(() => {
|
||||
hasScrolledInitially.current = false;
|
||||
@@ -71,6 +76,40 @@ export function AgentStreamView({
|
||||
setSelectedToolCall(null);
|
||||
}
|
||||
|
||||
const handleInlinePathPress = useCallback(
|
||||
(target: InlinePathTarget) => {
|
||||
if (!target.path) {
|
||||
return;
|
||||
}
|
||||
|
||||
const normalized = normalizeInlinePath(target.path);
|
||||
if (!normalized) {
|
||||
return;
|
||||
}
|
||||
|
||||
requestDirectoryListing(agentId, normalized.directory);
|
||||
if (normalized.file) {
|
||||
requestFilePreview(agentId, normalized.file);
|
||||
}
|
||||
|
||||
router.push({
|
||||
pathname: "/file-explorer",
|
||||
params: {
|
||||
agentId,
|
||||
path: normalized.directory,
|
||||
...(normalized.file ? { file: normalized.file } : {}),
|
||||
...(target.lineStart !== undefined
|
||||
? { lineStart: String(target.lineStart) }
|
||||
: {}),
|
||||
...(target.lineEnd !== undefined
|
||||
? { lineEnd: String(target.lineEnd) }
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
},
|
||||
[agentId, requestDirectoryListing, requestFilePreview, router]
|
||||
);
|
||||
|
||||
const handleScroll = useCallback(
|
||||
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
||||
const { contentOffset } = event.nativeEvent;
|
||||
@@ -179,6 +218,7 @@ export function AgentStreamView({
|
||||
<AssistantMessage
|
||||
message={item.text}
|
||||
timestamp={item.timestamp.getTime()}
|
||||
onInlinePathPress={handleInlinePathPress}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -324,6 +364,48 @@ export function AgentStreamView({
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeInlinePath(rawPath: string):
|
||||
| { directory: string; file?: string }
|
||||
| null {
|
||||
if (!rawPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let value = rawPath.trim();
|
||||
value = value.replace(/^['"`]/, "").replace(/['"`]$/, "");
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let normalized = value.replace(/\\/g, "/");
|
||||
normalized = normalized.replace(/\/{2,}/g, "/");
|
||||
|
||||
if (normalized.startsWith("./")) {
|
||||
normalized = normalized.slice(2) || ".";
|
||||
}
|
||||
|
||||
if (!normalized.length) {
|
||||
normalized = ".";
|
||||
}
|
||||
|
||||
if (normalized === ".") {
|
||||
return { directory: "." };
|
||||
}
|
||||
|
||||
if (normalized.endsWith("/")) {
|
||||
const dir = normalized.replace(/\/+$/, "");
|
||||
return { directory: dir.length > 0 ? dir : "." };
|
||||
}
|
||||
|
||||
const lastSlash = normalized.lastIndexOf("/");
|
||||
const directory = lastSlash >= 0 ? normalized.slice(0, lastSlash) : ".";
|
||||
|
||||
return {
|
||||
directory: directory.length > 0 ? directory : ".",
|
||||
file: normalized,
|
||||
};
|
||||
}
|
||||
|
||||
// Permission Request Card Component
|
||||
function PermissionRequestCard({
|
||||
permission,
|
||||
|
||||
Reference in New Issue
Block a user