diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx
index 00ddd9b73..b8d1c9aab 100644
--- a/apps/web/src/components/Sidebar.tsx
+++ b/apps/web/src/components/Sidebar.tsx
@@ -374,8 +374,7 @@ export default function Sidebar() {
const isOnSubPage =
pathname === "/settings" ||
pathname === "/pr-review" ||
- pathname === "/merge-conflicts" ||
- pathname === "/file-view";
+ pathname === "/merge-conflicts";
const { settings: appSettings, updateSettings } = useAppSettings();
const { resolvedTheme } = useTheme();
const { handleNewThread } = useHandleNewThread();
diff --git a/apps/web/src/components/merge-conflicts/MergeConflictShell.tsx b/apps/web/src/components/merge-conflicts/MergeConflictShell.tsx
index 68112ed8d..063681459 100644
--- a/apps/web/src/components/merge-conflicts/MergeConflictShell.tsx
+++ b/apps/web/src/components/merge-conflicts/MergeConflictShell.tsx
@@ -20,8 +20,9 @@ import {
ShieldCheckIcon,
WorkflowIcon,
} from "lucide-react";
-import { useEffect, useMemo, useState } from "react";
+import { useCallback, useEffect, useMemo, useState } from "react";
+import { useCodeViewerStore } from "~/codeViewerStore";
import { openInPreferredEditor } from "~/editorPreferences";
import { useCopyToClipboard } from "~/hooks/useCopyToClipboard";
import { useLocalStorage } from "~/hooks/useLocalStorage";
@@ -41,6 +42,7 @@ import { cn } from "~/lib/utils";
import { ensureNativeApi } from "~/nativeApi";
import { parsePullRequestReference } from "~/pullRequestReference";
import { findProjectMatchingPullRequestReference } from "~/pullRequestProjectMatch";
+import { useStore } from "~/store";
import type { Project } from "~/types";
import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert";
import { toastManager } from "~/components/ui/toast";
@@ -246,7 +248,26 @@ function MergeConflictGuidanceRail({
status: "done" | "active" | "todo" | "blocked";
}>;
}) {
- const navigateToFileView = useNavigate();
+ const navigate = useNavigate();
+ const threads = useStore((s) => s.threads);
+ const openCodeViewer = useCodeViewerStore((s) => s.open);
+
+ const navigateToLatestThread = useCallback(
+ () => {
+ openCodeViewer();
+ const sorted = [...threads].sort((a, b) =>
+ (b.updatedAt ?? b.createdAt).localeCompare(a.updatedAt ?? a.createdAt),
+ );
+ const latest = sorted[0];
+ if (latest) {
+ void navigate({ to: "/$threadId", params: { threadId: latest.id } });
+ } else {
+ void navigate({ to: "/" });
+ }
+ },
+ [navigate, openCodeViewer, threads],
+ );
+
return (
@@ -355,18 +376,10 @@ function MergeConflictGuidanceRail({
className="cursor-pointer rounded-2xl border border-border/70 bg-muted/24 p-3 transition-colors hover:border-border hover:bg-muted/40"
role="button"
tabIndex={0}
- onClick={() =>
- void navigateToFileView({
- to: "/file-view",
- search: { cwd: project.cwd },
- })
- }
+ onClick={navigateToLatestThread}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
- void navigateToFileView({
- to: "/file-view",
- search: { cwd: project.cwd },
- });
+ navigateToLatestThread();
}
}}
>
@@ -377,18 +390,10 @@ function MergeConflictGuidanceRail({
className="cursor-pointer rounded-2xl border border-border/70 bg-muted/24 p-3 transition-colors hover:border-border hover:bg-muted/40"
role="button"
tabIndex={0}
- onClick={() =>
- void navigateToFileView({
- to: "/file-view",
- search: { cwd: preparedWorkspace?.cwd ?? project.cwd },
- })
- }
+ onClick={navigateToLatestThread}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
- void navigateToFileView({
- to: "/file-view",
- search: { cwd: preparedWorkspace?.cwd ?? project.cwd },
- });
+ navigateToLatestThread();
}
}}
>
diff --git a/apps/web/src/hooks/useFileViewNavigation.ts b/apps/web/src/hooks/useFileViewNavigation.ts
index 2479c7abe..bcd636d4d 100644
--- a/apps/web/src/hooks/useFileViewNavigation.ts
+++ b/apps/web/src/hooks/useFileViewNavigation.ts
@@ -1,19 +1,38 @@
-import { useNavigate } from "@tanstack/react-router";
+import { useNavigate, useParams } from "@tanstack/react-router";
import { useCallback } from "react";
import { useCodeViewerStore } from "~/codeViewerStore";
+import { useStore } from "~/store";
+/**
+ * Opens a file in the code-viewer side panel of the active thread.
+ * If the caller is not on a thread page, navigates to the most recent thread first.
+ */
export function useFileViewNavigation() {
const navigate = useNavigate();
const openFile = useCodeViewerStore((s) => s.openFile);
+ const threadId = useParams({
+ strict: false,
+ select: (params) => (params as Record
).threadId ?? null,
+ });
+ const threads = useStore((s) => s.threads);
return useCallback(
(cwd: string, relativePath: string) => {
openFile(cwd, relativePath);
- void navigate({
- to: "/file-view",
- search: { cwd, path: relativePath },
- });
+ // If not already on a thread page, navigate to the most recent thread
+ // so the code-viewer inline sidebar is visible.
+ if (!threadId) {
+ const sorted = [...threads].sort((a, b) =>
+ (b.updatedAt ?? b.createdAt).localeCompare(a.updatedAt ?? a.createdAt),
+ );
+ const latest = sorted[0];
+ if (latest) {
+ void navigate({ to: "/$threadId", params: { threadId: latest.id } });
+ } else {
+ void navigate({ to: "/" });
+ }
+ }
},
- [navigate, openFile],
+ [navigate, openFile, threadId, threads],
);
}
diff --git a/apps/web/src/routes/_chat.file-view.tsx b/apps/web/src/routes/_chat.file-view.tsx
index 44e7128f9..03acbfc98 100644
--- a/apps/web/src/routes/_chat.file-view.tsx
+++ b/apps/web/src/routes/_chat.file-view.tsx
@@ -1,28 +1,46 @@
-import { createFileRoute } from "@tanstack/react-router";
-import { FileCodeIcon } from "lucide-react";
+import { createFileRoute, useNavigate } from "@tanstack/react-router";
+import { useEffect } from "react";
-import { FileViewShell } from "~/components/file-view/FileViewShell";
-import { ProjectSubpageShell } from "~/components/review/ProjectSubpageShell";
+import { useCodeViewerStore } from "~/codeViewerStore";
+import { useStore } from "~/store";
export interface FileViewSearch {
cwd?: string;
path?: string;
}
-function FileViewRouteView() {
+/**
+ * Legacy route — the standalone file-view page has been removed.
+ * If a file was requested via search params, open it in the code-viewer
+ * side panel and redirect to the most recent thread.
+ */
+function FileViewRouteRedirect() {
const { cwd, path } = Route.useSearch();
+ const openFile = useCodeViewerStore((s) => s.openFile);
+ const navigate = useNavigate();
+ const threads = useStore((s) => s.threads);
- return (
-
- {({ project }) => (
-
- )}
-
- );
+ useEffect(() => {
+ // Open the requested file in the side-panel store
+ if (cwd && path) {
+ openFile(cwd, path);
+ }
+
+ // Navigate to the most recent thread (or home)
+ const sorted = [...threads].sort((a, b) =>
+ (b.updatedAt ?? b.createdAt).localeCompare(a.updatedAt ?? a.createdAt),
+ );
+ const latest = sorted[0];
+ if (latest) {
+ void navigate({ to: "/$threadId", params: { threadId: latest.id }, replace: true });
+ } else {
+ void navigate({ to: "/", replace: true });
+ }
+ // Only run on mount
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return null;
}
export const Route = createFileRoute("/_chat/file-view")({
@@ -38,5 +56,5 @@ export const Route = createFileRoute("/_chat/file-view")({
return validatedSearch;
},
- component: FileViewRouteView,
+ component: FileViewRouteRedirect,
});