Skip to content
9 changes: 1 addition & 8 deletions packages/web/src/app/runs.$runId.files.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import { createFileRoute } from "@tanstack/react-router";
import { z } from "zod";
import { FilesPage } from "@/routes/files-page";

const filesSearchSchema = z.object({
scrollTo: z.string().optional(),
});

export const Route = createFileRoute("/runs/$runId/files")({
validateSearch: (search) => filesSearchSchema.parse(search),
component: FilesRoute,
});

function FilesRoute() {
const { runId } = Route.useParams();
const { scrollTo } = Route.useSearch();
return <FilesPage runId={runId} scrollTo={scrollTo} />;
return <FilesPage runId={runId} />;
}
2 changes: 1 addition & 1 deletion packages/web/src/app/runs.$runId.index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function ChaptersRoute() {
<div className="@container">
<div className="grid grid-cols-1 gap-6 @4xl:grid-cols-[minmax(0,2fr)_minmax(0,3fr)]">
<div className="scrollbar-thin min-w-0 @4xl:sticky @4xl:top-[var(--content-top)] @4xl:max-h-[calc(var(--main-height)_-_var(--content-top))] @4xl:overflow-y-auto @4xl:pr-4 @4xl:pb-6">
<PrologueSection prologue={prologue} runId={runId} />
<PrologueSection prologue={prologue} />
</div>
<div className="min-w-0">
<ChaptersIndexPage
Expand Down
3 changes: 3 additions & 0 deletions packages/web/src/components/chapter/chapter-navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export function ChapterNavigator({
<Link
to="/runs/$runId/chapters/$chapterNumber"
params={{ runId, chapterNumber: String(prevChapter.order) }}
resetScroll={false}
className="inline-flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
>
<ChevronLeft className="size-4" />
Expand Down Expand Up @@ -105,6 +106,7 @@ export function ChapterNavigator({
<Link
to="/runs/$runId/chapters/$chapterNumber"
params={{ runId, chapterNumber: String(ch.order) }}
resetScroll={false}
className={cn("cursor-pointer", isActive && "bg-accent")}
>
<StatusBadge
Expand Down Expand Up @@ -146,6 +148,7 @@ export function ChapterNavigator({
<Link
to="/runs/$runId/chapters/$chapterNumber"
params={{ runId, chapterNumber: String(nextChapter.order) }}
resetScroll={false}
className="inline-flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
>
<ChevronRight className="size-4" />
Expand Down
51 changes: 29 additions & 22 deletions packages/web/src/components/chapter/file-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ import {
} from "lucide-react";
import type { MouseEvent } from "react";
import { useCallback } from "react";
import { ShortcutLabel } from "@/components/keyboard/shortcut-label";
import { LineCounts } from "@/components/shared/line-counts";
import { ShortcutTooltip } from "@/components/shared/shortcut-tooltip";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { FILE_STATUS, type PullRequestFile } from "@/lib/diff-types";
import { FILE_STATUS_ICONS, FILE_STATUS_LABELS, FILE_STATUS_TEXT_COLORS } from "@/lib/file-status";
import { SHORTCUT_KEY } from "@/lib/keyboard-shortcuts";
import { useIsMac } from "@/lib/use-is-mac";
import { useShortcut } from "@/lib/use-shortcut";
import { cn } from "@/lib/utils";

function CopyableFilename({
Expand Down Expand Up @@ -71,6 +75,7 @@ export function FileHeader({
}: FileHeaderProps) {
const isMac = useIsMac();
const altLabel = isMac ? "⌥" : "Alt";
const { label: collapseShortcutLabel } = useShortcut(SHORTCUT_KEY.TOGGLE_FILE_COLLAPSED);

const copyPath = useCallback(
(path: string, label: string) => {
Expand Down Expand Up @@ -146,7 +151,10 @@ export function FileHeader({
</button>
</TooltipTrigger>
<TooltipContent side="top" className="text-center">
<p>{isCollapsed ? "Expand file" : "Collapse file"}</p>
<p className="flex items-center justify-center gap-1">
{isCollapsed ? "Expand file" : "Collapse file"}
<ShortcutLabel label={collapseShortcutLabel} />
</p>
<p className="text-muted-foreground">
{altLabel}-click to {isCollapsed ? "expand" : "collapse"} all files
</p>
Expand Down Expand Up @@ -213,27 +221,26 @@ export function FileHeader({
className="relative z-10 shrink-0"
/>
{onToggleViewed && (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={(e) => {
e.stopPropagation();
onToggleViewed();
}}
className={cn(
"relative z-10 flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors hover:bg-accent",
isViewed
? "text-green-600 hover:text-green-700 dark:text-green-500 dark:hover:text-green-400"
: "text-muted-foreground hover:text-foreground",
)}
aria-label={isViewed ? "Mark file as unviewed" : "Mark file as viewed"}
>
{isViewed ? <CircleCheck className="size-3.5" /> : <Circle className="size-3.5" />}
</button>
</TooltipTrigger>
<TooltipContent>{isViewed ? "Mark as unviewed" : "Mark as viewed"}</TooltipContent>
</Tooltip>
<ShortcutTooltip
shortcutKey={SHORTCUT_KEY.MARK_FILE_AS_VIEWED}
label={isViewed ? "Mark as unviewed" : "Mark as viewed"}
>
<button
type="button"
onClick={(e) => {
e.stopPropagation();
onToggleViewed();
}}
className={cn(
"relative z-10 flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors hover:bg-accent",
isViewed
? "text-green-600 hover:text-green-700 dark:text-green-500 dark:hover:text-green-400"
: "text-muted-foreground hover:text-foreground",
)}
>
{isViewed ? <CircleCheck className="size-3.5" /> : <Circle className="size-3.5" />}
</button>
</ShortcutTooltip>
)}
{handleCommentClick && (
<Tooltip>
Expand Down
22 changes: 20 additions & 2 deletions packages/web/src/components/files/file-diff-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { PierreDiffViewer } from "@/components/chapter/pierre-diff-viewer";
import { findRenderedDiffLine } from "@/components/chapter/rendered-line-target";
import type { AnnotatedLineRef, DiffSide, LineRef } from "@/lib/diff-types";
import type { FileDiffEntry } from "@/lib/parse-diff";
import { cn } from "@/lib/utils";

export interface FileDiffListHandle {
scrollToFile: (filePath: string) => void;
Expand Down Expand Up @@ -41,14 +42,24 @@ interface FileDiffListProps {
onToggleViewed?: (path: string) => void;
collapseState: CollapseState;
chapterOverlay?: ChapterOverlayProps;
/** The keyboard-focused file, outlined to mark it as the active diff. */
focusedFilePath?: string;
}

const FILE_TOP_PADDING = 16;
const SCROLL_TO_LINE_POLL_MS = 100;
const SCROLL_TO_LINE_TIMEOUT_MS = 3000;

export const FileDiffList = forwardRef<FileDiffListHandle, FileDiffListProps>(function FileDiffList(
{ entries, emptyMessage, viewedPathSet, onToggleViewed, collapseState, chapterOverlay },
{
entries,
emptyMessage,
viewedPathSet,
onToggleViewed,
collapseState,
chapterOverlay,
focusedFilePath,
},
ref,
) {
const scrollRequestRef = useRef(0);
Expand Down Expand Up @@ -197,6 +208,7 @@ export const FileDiffList = forwardRef<FileDiffListHandle, FileDiffListProps>(fu
key={entry.file.path}
entry={entry}
isViewed={viewedPathSet?.has(entry.file.path) ?? false}
isFocused={entry.file.path === focusedFilePath}
onToggleViewed={onToggleViewed}
collapseState={collapseState}
chapterOverlay={chapterOverlay}
Expand All @@ -209,6 +221,7 @@ export const FileDiffList = forwardRef<FileDiffListHandle, FileDiffListProps>(fu
interface FileDiffSectionProps {
entry: FileDiffEntry;
isViewed: boolean;
isFocused: boolean;
onToggleViewed?: (path: string) => void;
collapseState: CollapseState;
chapterOverlay?: ChapterOverlayProps;
Expand All @@ -217,6 +230,7 @@ interface FileDiffSectionProps {
function FileDiffSection({
entry,
isViewed,
isFocused,
onToggleViewed,
collapseState,
chapterOverlay,
Expand All @@ -239,7 +253,11 @@ function FileDiffSection({
}, [onToggleViewed, file.path]);

return (
<div id={`file-${file.path}`}>
<div
id={`file-${file.path}`}
data-focused-file={isFocused ? "true" : undefined}
className={cn("rounded-lg", isFocused && "outline-2 outline-primary/70")}
>
<FileHeader
file={file}
isCollapsed={isCollapsed}
Expand Down
19 changes: 6 additions & 13 deletions packages/web/src/components/prologue/prologue-section.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { FocusArea, FocusAreaSeverity, Prologue } from "@stagereview/types/prologue";
import { FOCUS_AREA_SEVERITY } from "@stagereview/types/prologue";
import { Link } from "@tanstack/react-router";
import { AlertTriangle } from "lucide-react";
import { cn } from "@/lib/utils";

Expand All @@ -18,7 +17,7 @@ function getFileName(filePath: string): string {
return filePath.split("/").pop() ?? filePath;
}

function PrologueDisplay({ prologue, runId }: { prologue: Prologue; runId: string }) {
function PrologueDisplay({ prologue }: { prologue: Prologue }) {
const concerns = getConcerns(prologue.focusAreas);

return (
Expand Down Expand Up @@ -81,14 +80,9 @@ function PrologueDisplay({ prologue, runId }: { prologue: Prologue; runId: strin
/>
<span className="min-w-0 truncate">{area.title}</span>
{area.locations[0] && (
<Link
to="/runs/$runId/files"
params={{ runId }}
search={{ scrollTo: area.locations[0] }}
className="ml-auto shrink-0 text-xs text-muted-foreground transition-colors hover:text-foreground"
>
{getFileName(area.locations[0])} &rarr;
</Link>
<span className="ml-auto shrink-0 text-xs text-muted-foreground">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Removed clickable file-location navigation from the prologue "Review Focus" section and replaced it with plain text. The previous <Link> navigated to /runs/$runId/files?scrollTo=<location>, letting users jump directly to the relevant file. The new <span> just displays the filename inertly — no navigation, no click target, no arrow affordance. This removes a user-facing path from a prologue focus-area item to its corresponding file in the diff view. The PR description discusses a new file-focus model but does not describe removing this prologue-to-file navigation, and no replacement navigation mechanism is introduced in this diff.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/web/src/components/prologue/prologue-section.tsx, line 83:

<comment>Removed clickable file-location navigation from the prologue "Review Focus" section and replaced it with plain text. The previous `<Link>` navigated to `/runs/$runId/files?scrollTo=<location>`, letting users jump directly to the relevant file. The new `<span>` just displays the filename inertly — no navigation, no click target, no arrow affordance. This removes a user-facing path from a prologue focus-area item to its corresponding file in the diff view. The PR description discusses a new file-focus model but does not describe removing this prologue-to-file navigation, and no replacement navigation mechanism is introduced in this diff.</comment>

<file context>
@@ -81,14 +80,9 @@ function PrologueDisplay({ prologue, runId }: { prologue: Prologue; runId: strin
-										>
-											{getFileName(area.locations[0])} &rarr;
-										</Link>
+										<span className="ml-auto shrink-0 text-xs text-muted-foreground">
+											{getFileName(area.locations[0])}
+										</span>
</file context>

{getFileName(area.locations[0])}
</span>
)}
</span>
<p className="mt-0.5 ml-6 text-xs text-muted-foreground">{area.description}</p>
Expand All @@ -103,10 +97,9 @@ function PrologueDisplay({ prologue, runId }: { prologue: Prologue; runId: strin

interface PrologueSectionProps {
prologue: Prologue | null | undefined;
runId: string;
}

export function PrologueSection({ prologue, runId }: PrologueSectionProps) {
export function PrologueSection({ prologue }: PrologueSectionProps) {
if (!prologue) return null;
return <PrologueDisplay prologue={prologue} runId={runId} />;
return <PrologueDisplay prologue={prologue} />;
}
Loading