sourcebot/src/app/page.tsx

125 lines
5.5 KiB
TypeScript
Raw Normal View History

'use client';
2024-08-28 00:04:50 +00:00
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable";
2024-08-26 00:57:34 +00:00
import { Separator } from "@/components/ui/separator";
import { useNonEmptyQueryParam } from "@/hooks/useNonEmptyQueryParam";
import { GetSourceResponse, pathQueryParamName, repoQueryParamName, ZoektFileMatch } from "@/lib/types";
2024-08-28 00:04:50 +00:00
import { createPathWithQueryParams } from "@/lib/utils";
import { SymbolIcon } from "@radix-ui/react-icons";
2024-08-26 00:57:34 +00:00
import Image from "next/image";
import { useRouter } from "next/navigation";
import { useState } from "react";
2024-08-27 03:54:15 +00:00
import logoDark from "../../public/sb_logo_dark.png";
2024-08-28 00:04:50 +00:00
import logoLight from "../../public/sb_logo_light.png";
import { CodePreview } from "./codePreview";
import { SearchBar } from "./searchBar";
import { SearchResults } from "./searchResults";
2024-08-27 03:54:15 +00:00
import { ThemeSelectorButton } from "./themeSelectorButton";
2024-08-23 20:54:13 +00:00
export default function Home() {
const router = useRouter();
const defaultQuery = useNonEmptyQueryParam("query") ?? "";
const defaultNumResults = useNonEmptyQueryParam("numResults");
2024-08-26 00:57:34 +00:00
const [query, setQuery] = useState(defaultQuery);
const [numResults, _setNumResults] = useState(defaultNumResults && !isNaN(Number(defaultNumResults)) ? Number(defaultNumResults) : 100);
2024-08-23 20:54:13 +00:00
2024-08-26 04:30:09 +00:00
const [isCodePanelOpen, setIsCodePanelOpen] = useState(false);
const [code, setCode] = useState("");
const [filepath, setFilepath] = useState("");
2024-08-26 00:57:34 +00:00
const [fileMatches, setFileMatches] = useState<ZoektFileMatch[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [searchDurationMs, setSearchDurationMs] = useState(0);
// @todo: We need to be able to handle the case when the user navigates backwards / forwards.
// Currently we do not re-query.
2024-08-23 20:54:13 +00:00
return (
2024-08-26 00:57:34 +00:00
<main className="h-screen overflow-hidden">
{/* TopBar */}
2024-08-27 03:54:15 +00:00
<div className="sticky top-0 left-0 right-0 z-10">
<div className="flex flex-row justify-between items-center py-1 px-2 gap-4">
<div className="grow flex flex-row gap-4 items-center">
<Image
src={logoDark}
className="h-12 w-auto hidden dark:block"
alt={"Sourcebot logo"}
/>
<Image
src={logoLight}
className="h-12 w-auto block dark:hidden"
alt={"Sourcebot logo"}
/>
<SearchBar
query={query}
numResults={numResults}
onQueryChange={(query) => setQuery(query)}
onLoadingChange={(isLoading) => setIsLoading(isLoading)}
onSearchResult={(result) => {
if (result) {
setFileMatches(result.FileMatches ?? []);
setSearchDurationMs(Math.round(result.Stats.Duration / 1000000));
}
2024-08-27 03:54:15 +00:00
router.push(`?query=${query}&numResults=${numResults}`);
}}
/>
{isLoading && (
<SymbolIcon className="h-4 w-4 animate-spin" />
)}
</div>
<ThemeSelectorButton />
2024-08-25 05:09:31 +00:00
</div>
<Separator />
<div className="bg-accent p-2">
<p className="text-sm font-medium">Results for: {fileMatches.length} files in {searchDurationMs} ms</p>
</div>
<Separator />
</div>
{/* Search Results & Code Preview */}
2024-08-25 06:01:46 +00:00
<ResizablePanelGroup direction="horizontal">
2024-08-26 00:57:34 +00:00
<ResizablePanel minSize={20}>
<SearchResults
fileMatches={fileMatches}
onOpenFileMatch={(match) => {
const url = createPathWithQueryParams(
`http://localhost:3000/api/source`,
[pathQueryParamName, match.FileName],
[repoQueryParamName, match.Repo]
);
// @todo : this query should definitely be cached s.t., when the user is switching between files,
// we aren't re-fetching the same file.
fetch(url)
.then(response => response.json())
.then((body: GetSourceResponse) => {
setIsCodePanelOpen(true);
setCode(body.content);
setFilepath(match.FileName);
});
}}
/>
</ResizablePanel>
<ResizableHandle withHandle={isCodePanelOpen} />
<ResizablePanel
minSize={20}
hidden={!isCodePanelOpen}
>
<CodePreview
code={code}
filepath={filepath}
onClose={() => setIsCodePanelOpen(false)}
keymapType="default"
/>
2024-08-25 06:01:46 +00:00
</ResizablePanel>
</ResizablePanelGroup>
</main>
);
}