Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved repository query performance by adding db indices. [#526](https://github.com/sourcebot-dev/sourcebot/pull/526)
- Improved repository query performance by removing JOIN on `Connection` table. [#527](https://github.com/sourcebot-dev/sourcebot/pull/527)
- Changed repo carousel and repo list links to redirect to the file browser. [#528](https://github.com/sourcebot-dev/sourcebot/pull/528)
- Changed file headers, files/directories in file tree, and reference list buttons into links. [#532](https://github.com/sourcebot-dev/sourcebot/pull/532)

## [4.7.1] - 2025-09-19

Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
'use client';

import { useCallback, useRef } from "react";
import { useRef } from "react";
import { FileTreeItem } from "@/features/fileTree/actions";
import { FileTreeItemComponent } from "@/features/fileTree/components/fileTreeItemComponent";
import { useBrowseNavigation } from "../../hooks/useBrowseNavigation";
import { getBrowsePath } from "../../hooks/useBrowseNavigation";
import { ScrollArea } from "@/components/ui/scroll-area";
import { useBrowseParams } from "../../hooks/useBrowseParams";
import { useDomain } from "@/hooks/useDomain";

interface PureTreePreviewPanelProps {
items: FileTreeItem[];
}

export const PureTreePreviewPanel = ({ items }: PureTreePreviewPanelProps) => {
const { repoName, revisionName } = useBrowseParams();
const { navigateToPath } = useBrowseNavigation();
const scrollAreaRef = useRef<HTMLDivElement>(null);

const onNodeClicked = useCallback((node: FileTreeItem) => {
navigateToPath({
repoName: repoName,
revisionName: revisionName,
path: node.path,
pathType: node.type === 'tree' ? 'tree' : 'blob',
});
}, [navigateToPath, repoName, revisionName]);

const domain = useDomain();

return (
<ScrollArea
className="flex flex-col p-0.5"
Expand All @@ -37,8 +29,14 @@ export const PureTreePreviewPanel = ({ items }: PureTreePreviewPanelProps) => {
isActive={false}
depth={0}
isCollapseChevronVisible={false}
onClick={() => onNodeClicked(item)}
parentRef={scrollAreaRef}
href={getBrowsePath({
repoName,
revisionName,
path: item.path,
pathType: item.type === 'tree' ? 'tree' : 'blob',
domain,
})}
/>
))}
</ScrollArea>
Expand Down
55 changes: 31 additions & 24 deletions packages/web/src/app/[domain]/components/pathHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { cn, getCodeHostInfoForRepo } from "@/lib/utils";
import { LaptopIcon } from "@radix-ui/react-icons";
import Image from "next/image";
import { useBrowseNavigation } from "../browse/hooks/useBrowseNavigation";
import { getBrowsePath } from "../browse/hooks/useBrowseNavigation";
import { ChevronRight, MoreHorizontal } from "lucide-react";
import { useCallback, useState, useMemo, useRef, useEffect } from "react";
import { useToast } from "@/components/hooks/use-toast";
Expand All @@ -15,6 +15,8 @@ import {
} from "@/components/ui/dropdown-menu";
import { VscodeFileIcon } from "@/app/components/vscodeFileIcon";
import { CopyIconButton } from "./copyIconButton";
import Link from "next/link";
import { useDomain } from "@/hooks/useDomain";

interface FileHeaderProps {
path: string;
Expand Down Expand Up @@ -64,11 +66,11 @@ export const PathHeader = ({
webUrl: repo.webUrl,
});

const { navigateToPath } = useBrowseNavigation();
const { toast } = useToast();
const containerRef = useRef<HTMLDivElement>(null);
const breadcrumbsRef = useRef<HTMLDivElement>(null);
const [visibleSegmentCount, setVisibleSegmentCount] = useState<number | null>(null);
const domain = useDomain();

// Create breadcrumb segments from file path
const breadcrumbSegments = useMemo(() => {
Expand Down Expand Up @@ -179,16 +181,6 @@ export const PathHeader = ({
return true;
}, [path, toast]);

const onBreadcrumbClick = useCallback((segment: BreadcrumbSegment) => {
navigateToPath({
repoName: repo.name,
path: segment.fullPath,
pathType: segment.isLastSegment ? pathType : 'tree',
revisionName: branchDisplayName,
});
}, [repo.name, branchDisplayName, navigateToPath, pathType]);


const renderSegmentWithHighlight = (segment: BreadcrumbSegment) => {
if (!segment.highlightRange) {
return segment.name;
Expand Down Expand Up @@ -224,17 +216,18 @@ export const PathHeader = ({
</>
)}

<div
<Link
className={cn("font-medium cursor-pointer hover:underline", repoNameClassName)}
onClick={() => navigateToPath({
href={getBrowsePath({
repoName: repo.name,
path: '',
path: '/',
pathType: 'tree',
revisionName: branchDisplayName,
domain,
})}
>
{info?.displayName}
</div>
</Link>
{branchDisplayName && (
<p
className="text-xs font-semibold text-gray-500 dark:text-gray-400 mt-[3px] flex items-center gap-0.5"
Expand Down Expand Up @@ -263,13 +256,21 @@ export const PathHeader = ({
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="min-w-[200px]">
{hiddenSegments.map((segment) => (
<DropdownMenuItem
<Link
href={getBrowsePath({
repoName: repo.name,
path: segment.fullPath,
pathType: segment.isLastSegment ? pathType : 'tree',
revisionName: branchDisplayName,
domain,
})}
className="font-mono text-sm hover:cursor cursor-pointer"
key={segment.fullPath}
onClick={() => onBreadcrumbClick(segment)}
className="font-mono text-sm cursor-pointer"
>
{renderSegmentWithHighlight(segment)}
</DropdownMenuItem>
<DropdownMenuItem className="hover:cursor cursor-pointer">
{renderSegmentWithHighlight(segment)}
</DropdownMenuItem>
</Link>
))}
</DropdownMenuContent>
</DropdownMenu>
Expand All @@ -281,14 +282,20 @@ export const PathHeader = ({
{(isFileIconVisible && index === visibleSegments.length - 1) && (
<VscodeFileIcon fileName={segment.name} className="h-4 w-4 mr-1" />
)}
<span
<Link
className={cn(
"font-mono text-sm truncate cursor-pointer hover:underline",
)}
onClick={() => onBreadcrumbClick(segment)}
href={getBrowsePath({
repoName: repo.name,
path: segment.fullPath,
pathType: segment.isLastSegment ? pathType : 'tree',
revisionName: branchDisplayName,
domain,
})}
>
{renderSegmentWithHighlight(segment)}
</span>
</Link>
{index < visibleSegments.length - 1 && (
<ChevronRight className="h-3 w-3 mx-0.5 text-muted-foreground flex-shrink-0" />
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,47 @@
'use client';

import { useCallback } from "react";
import { SearchResultFile, SearchResultChunk } from "@/features/search/types";
import { LightweightCodeHighlighter } from "@/app/[domain]/components/lightweightCodeHighlighter";
import Link from "next/link";
import { getBrowsePath } from "@/app/[domain]/browse/hooks/useBrowseNavigation";
import { useDomain } from "@/hooks/useDomain";


interface FileMatchProps {
match: SearchResultChunk;
file: SearchResultFile;
onOpen: (startLineNumber: number, endLineNumber: number, isCtrlKeyPressed: boolean) => void;
}

export const FileMatch = ({
match,
file,
onOpen: _onOpen,
}: FileMatchProps) => {
const onOpen = useCallback((isCtrlKeyPressed: boolean) => {
const startLineNumber = match.contentStart.lineNumber;
const endLineNumber = match.content.trimEnd().split('\n').length + startLineNumber - 1;

_onOpen(startLineNumber, endLineNumber, isCtrlKeyPressed);
}, [match.content, match.contentStart.lineNumber, _onOpen]);
const domain = useDomain();

// If it's just the title, don't show a code preview
if (match.matchRanges.length === 0) {
return null;
}

return (
<div
<Link
tabIndex={0}
className="cursor-pointer focus:ring-inset focus:ring-4 bg-background hover:bg-editor-lineHighlight"
onKeyDown={(e) => {
if (e.key !== "Enter") {
return;
href={getBrowsePath({
repoName: file.repository,
revisionName: file.branches?.[0] ?? 'HEAD',
path: file.fileName.text,
pathType: 'blob',
domain,
highlightRange: {
start: {
lineNumber: match.contentStart.lineNumber,
},
end: {
lineNumber: match.content.trimEnd().split('\n').length + match.contentStart.lineNumber - 1,
}
}

onOpen(e.metaKey || e.ctrlKey);
}}
onClick={(e) => {
onOpen(e.metaKey || e.ctrlKey);
}}
})}
title="open file: click, open file preview: cmd/ctrl + click"
>
<LightweightCodeHighlighter
Expand All @@ -53,6 +53,6 @@ export const FileMatch = ({
>
{match.content}
</LightweightCodeHighlighter>
</div>
</Link>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { useMemo } from "react";
import { FileMatch } from "./fileMatch";
import { RepositoryInfo, SearchResultFile } from "@/features/search/types";
import { Button } from "@/components/ui/button";
import { useBrowseNavigation } from "@/app/[domain]/browse/hooks/useBrowseNavigation";

export const MAX_MATCHES_TO_PREVIEW = 3;

Expand All @@ -33,7 +32,6 @@ export const FileMatchContainer = ({
const matchCount = useMemo(() => {
return file.chunks.length;
}, [file]);
const { navigateToPath } = useBrowseNavigation();

const matches = useMemo(() => {
const sortedMatches = file.chunks.sort((a, b) => {
Expand Down Expand Up @@ -123,29 +121,6 @@ export const FileMatchContainer = ({
<FileMatch
match={match}
file={file}
onOpen={(startLineNumber, endLineNumber, isCtrlKeyPressed) => {
if (isCtrlKeyPressed) {
const matchIndex = matches.slice(0, index).reduce((acc, match) => {
return acc + match.matchRanges.length;
}, 0);
onOpenFilePreview(matchIndex);
} else {
navigateToPath({
repoName: file.repository,
revisionName: file.branches?.[0] ?? 'HEAD',
path: file.fileName.text,
pathType: 'blob',
highlightRange: {
start: {
lineNumber: startLineNumber,
},
end: {
lineNumber: endLineNumber,
}
}
});
}
}}
/>
{(index !== matches.length - 1 || isMoreContentButtonVisible) && (
<Separator className="bg-accent" />
Expand Down
Loading