'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; } export const CodePreviewPanel = ({ fileMatch, onClose, selectedMatchIndex, onSelectedMatchIndexChange, repoUrlTemplates, }: CodePreviewPanelProps) => { const { data: file } = useQuery({ queryKey: ["source", fileMatch?.FileName, fileMatch?.Repository, fileMatch?.Branches], queryFn: async (): Promise => { if (!fileMatch) { return undefined; } // 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]; // 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; } 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, revision: branch ?? "HEAD", }; }); }, enabled: fileMatch !== undefined, }); return ( ) }