@@ -119,228 +81,44 @@ export default function Home() {
+
+ {/* Search Results & Code Preview */}
-
-
- {fileMatches.map((match, index) => (
- {
- const url = createPathWithQueryParams(
- `http://localhost:3000/api/source`,
- [pathQueryParamName, match.FileName],
- [repoQueryParamName, match.Repo]
- );
+ {
+ 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);
- });
- }}
- />
- ))}
-
-
-
+ // @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);
+ });
+ }}
+ />
+
+
+
+ setIsCodePanelOpen(false)}
+ keymapType="default"
+ />
-
- {isCodePanelOpen && (
-
- setIsCodePanelOpen(false)}
- keymapType="default"
- />
-
- )}
);
}
-
-interface CodeEditorProps {
- code: string;
- filepath: string;
- keymapType: "default" | "vim";
- onClose: () => void;
-}
-
-const CodeEditor = ({
- code,
- filepath,
- keymapType,
- onClose,
-}: CodeEditorProps) => {
- const { theme: _theme, systemTheme } = useTheme();
- const theme = useMemo(() => {
- if (_theme === "system") {
- return systemTheme ?? "light";
- }
-
- return _theme ?? "light";
- }, [_theme]);
-
- const [gutterWidth, setGutterWidth] = useState(0);
- const gutterWidthPlugin = useMemo(() => {
- return ViewPlugin.fromClass(class {
- width: number = 0;
- constructor(view: EditorView) {
- this.measureWidth(view)
- }
- update(update: ViewUpdate) {
- if (update.geometryChanged) this.measureWidth(update.view)
- }
- measureWidth(view: EditorView) {
- let gutter = view.scrollDOM.querySelector('.cm-gutters') as HTMLElement
- if (gutter) this.width = gutter.offsetWidth
- }
- });
- }, []);
-
- return (
-
-
-
- {
- const width = update.view.plugin(gutterWidthPlugin)?.width;
- if (width) {
- setGutterWidth(width);
- }
- })
- ]}
- />
-
-
-
- )
-}
-
-interface SearchBarProps {
- query: string;
- numResults: number;
- onLoadingChange: (isLoading: boolean) => void;
- onQueryChange: (query: string) => void;
- onSearchResult: (result?: ZoekResult) => void,
-}
-
-const SearchBar = ({
- query,
- numResults,
- onLoadingChange,
- onQueryChange,
- onSearchResult,
-}: SearchBarProps) => {
- const SEARCH_DEBOUNCE_MS = 200;
-
- // @todo : we should probably be cancelling any running requests
- const search = useDebouncedCallback((query: string) => {
- if (query === "") {
- onSearchResult(undefined);
- return;
- }
- console.log('making query...');
-
- onLoadingChange(true);
- fetch(`http://localhost:3000/api/search?query=${query}&numResults=${numResults}`)
- .then(response => response.json())
- .then(({ data }: { data: ZoekSearchResponse }) => {
- onSearchResult(data.result);
- })
- // @todo : error handling
- .catch(error => {
- console.error('Error:', error);
- }).finally(() => {
- console.log('done making query');
- onLoadingChange(false);
- });
- }, SEARCH_DEBOUNCE_MS);
-
- useEffect(() => {
- search(query);
- }, [query]);
-
- return (
-
{
- const query = e.target.value;
- onQueryChange(query);
- }}
- />
- )
-}
-
-interface FileMatchProps {
- match: ZoekFileMatch;
- onOpenFile: () => void;
-}
-
-const FileMatch = ({
- match,
- onOpenFile,
-}: FileMatchProps) => {
-
- return (
-
-
- {match.Repo} · {match.FileName}
-
- {match.Matches.map((match, index) => {
- const fragment = match.Fragments[0];
-
- return (
-
{
- onOpenFile();
- }}
- >
-
{match.LineNum}: {fragment.Pre}{fragment.Match}{fragment.Post}
-
-
- );
- })}
-
- );
-}
diff --git a/src/app/searchBar.tsx b/src/app/searchBar.tsx
new file mode 100644
index 00000000..e1acb43d
--- /dev/null
+++ b/src/app/searchBar.tsx
@@ -0,0 +1,63 @@
+'use client';
+
+import { Input } from "@/components/ui/input";
+import { ZoektResult, ZoektSearchResponse } from "@/lib/types";
+import { useEffect } from "react";
+import { useDebouncedCallback } from "use-debounce";
+
+interface SearchBarProps {
+ query: string;
+ numResults: number;
+ onLoadingChange: (isLoading: boolean) => void;
+ onQueryChange: (query: string) => void;
+ onSearchResult: (result?: ZoektResult) => void,
+}
+
+export const SearchBar = ({
+ query,
+ numResults,
+ onLoadingChange,
+ onQueryChange,
+ onSearchResult,
+}: SearchBarProps) => {
+ const SEARCH_DEBOUNCE_MS = 200;
+
+ // @todo : we should probably be cancelling any running requests
+ const search = useDebouncedCallback((query: string) => {
+ if (query === "") {
+ onSearchResult(undefined);
+ return;
+ }
+ console.log('making query...');
+
+ onLoadingChange(true);
+ fetch(`http://localhost:3000/api/search?query=${query}&numResults=${numResults}`)
+ .then(response => response.json())
+ .then(({ data }: { data: ZoektSearchResponse }) => {
+ onSearchResult(data.result);
+ })
+ // @todo : error handling
+ .catch(error => {
+ console.error('Error:', error);
+ }).finally(() => {
+ console.log('done making query');
+ onLoadingChange(false);
+ });
+ }, SEARCH_DEBOUNCE_MS);
+
+ useEffect(() => {
+ search(query);
+ }, [query]);
+
+ return (
+
{
+ const query = e.target.value;
+ onQueryChange(query);
+ }}
+ />
+ )
+}
\ No newline at end of file
diff --git a/src/app/searchResults.tsx b/src/app/searchResults.tsx
new file mode 100644
index 00000000..2c67cb87
--- /dev/null
+++ b/src/app/searchResults.tsx
@@ -0,0 +1,68 @@
+'use client';
+
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { Separator } from "@/components/ui/separator";
+import { ZoektFileMatch } from "@/lib/types";
+import { Scrollbar } from "@radix-ui/react-scroll-area";
+
+interface SearchResultsProps {
+ fileMatches: ZoektFileMatch[];
+ onOpenFileMatch: (match: ZoektFileMatch) => void;
+}
+
+export const SearchResults = ({
+ fileMatches,
+ onOpenFileMatch,
+}: SearchResultsProps) => {
+ return (
+
+
+ {fileMatches.map((match, index) => (
+ {
+ onOpenFileMatch(match);
+ }}
+ />
+ ))}
+
+
+
+ )
+}
+
+interface FileMatchProps {
+ match: ZoektFileMatch;
+ onOpenFile: () => void;
+}
+
+const FileMatch = ({
+ match,
+ onOpenFile,
+}: FileMatchProps) => {
+
+ return (
+
+
+ {match.Repo} · {match.FileName}
+
+ {match.Matches.map((match, index) => {
+ const fragment = match.Fragments[0];
+
+ return (
+
{
+ onOpenFile();
+ }}
+ >
+
{match.LineNum}: {fragment.Pre}{fragment.Match}{fragment.Post}
+
+
+ );
+ })}
+
+ );
+}
diff --git a/src/lib/api.ts b/src/lib/api.ts
deleted file mode 100644
index a2855732..00000000
--- a/src/lib/api.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-export const pathQueryParamName = "path";
-export const repoQueryParamName = "repo";
-
-export type GetSourceResponse = {
- content: string;
-}
diff --git a/src/lib/types.ts b/src/lib/types.ts
new file mode 100644
index 00000000..2a8e2430
--- /dev/null
+++ b/src/lib/types.ts
@@ -0,0 +1,40 @@
+
+
+export const pathQueryParamName = "path";
+export const repoQueryParamName = "repo";
+
+export type GetSourceResponse = {
+ content: string;
+}
+
+export interface ZoektMatch {
+ URL: string,
+ FileName: string,
+ LineNum: number,
+ Fragments: {
+ Pre: string,
+ Match: string,
+ Post: string
+ }[]
+}
+
+export interface ZoektFileMatch {
+ FileName: string,
+ Repo: string,
+ Language: string,
+ Matches: ZoektMatch[],
+ URL: string,
+}
+
+export interface ZoektResult {
+ QueryStr: string,
+ FileMatches: ZoektFileMatch[] | null,
+ Stats: {
+ // Duration in nanoseconds
+ Duration: number,
+ }
+}
+
+export interface ZoektSearchResponse {
+ result: ZoektResult,
+}
\ No newline at end of file