sourcebot/packages/web/src/app/search/components/codePreviewPanel/index.tsx

99 lines
3.9 KiB
TypeScript
Raw Normal View History

'use client';
import { fetchFileSource } from "@/app/api/(client)/client";
import { base64Decode } from "@/lib/utils";
import { useQuery } from "@tanstack/react-query";
import { CodePreview, CodePreviewFile } from "./codePreview";
import { SearchResultFile } from "@/lib/types";
interface CodePreviewPanelProps {
fileMatch?: SearchResultFile;
onClose: () => void;
selectedMatchIndex: number;
onSelectedMatchIndexChange: (index: number) => void;
repoUrlTemplates: Record<string, string>;
}
export const CodePreviewPanel = ({
fileMatch,
onClose,
selectedMatchIndex,
onSelectedMatchIndexChange,
repoUrlTemplates,
}: CodePreviewPanelProps) => {
const { data: file } = useQuery({
2024-11-07 02:28:10 +00:00
queryKey: ["source", fileMatch?.FileName, fileMatch?.Repository, fileMatch?.Branches],
queryFn: async (): Promise<CodePreviewFile | undefined> => {
if (!fileMatch) {
return undefined;
}
2024-11-07 02:28:10 +00:00
// If there are multiple branches pointing to the same revision of this file, it doesn't
// matter which branch we use here, so use the first one.
const branch = fileMatch.Branches && fileMatch.Branches.length > 0 ? fileMatch.Branches[0] : undefined;
return fetchFileSource({
fileName: fileMatch.FileName,
repository: fileMatch.Repository,
branch,
})
.then(({ source }) => {
const link = (() => {
const template = repoUrlTemplates[fileMatch.Repository];
2025-01-15 00:50:38 +00:00
// This is a hacky parser for templates generated by
// the go text/template package. Example template:
// {{URLJoinPath "https://github.com/sourcebot-dev/sourcebot" "blob" .Version .Path}}
// @see: https://pkg.go.dev/text/template
if (!template || !template.match(/^{{URLJoinPath\s.*}}(\?.+)?$/)) {
return undefined;
}
2025-01-15 00:50:38 +00:00
const url =
template.substring("{{URLJoinPath ".length,template.indexOf("}}"))
.replace(".Version", branch ?? "HEAD")
.replace(".Path", fileMatch.FileName)
.split(" ")
.map((part) => {
// remove wrapping quotes
if (part.startsWith("\"")) part = part.substring(1);
if (part.endsWith("\"")) part = part.substring(0, part.length - 1);
return part;
})
.join("/");
const optionalQueryParams = template.substring(template.indexOf("}}") + 2);
return url + optionalQueryParams;
})();
const decodedSource = base64Decode(source);
// Filter out filename matches
const filteredMatches = fileMatch.ChunkMatches.filter((match) => {
return !match.FileName;
});
return {
content: decodedSource,
filepath: fileMatch.FileName,
matches: filteredMatches,
link: link,
language: fileMatch.Language,
2025-01-07 18:27:42 +00:00
revision: branch ?? "HEAD",
};
});
},
enabled: fileMatch !== undefined,
});
return (
<CodePreview
file={file}
2025-01-07 18:27:42 +00:00
repoName={fileMatch?.Repository}
onClose={onClose}
selectedMatchIndex={selectedMatchIndex}
onSelectedMatchIndexChange={onSelectedMatchIndexChange}
/>
)
}