From d767b082aa6d2c5aa72c3024784828e1ad8f6c1b Mon Sep 17 00:00:00 2001 From: bkellam Date: Tue, 22 Oct 2024 12:52:14 -0400 Subject: [PATCH 1/9] Fix overflow issues --- packages/web/src/app/globals.css | 8 ++++++ .../codePreviewPanel/codePreview.tsx | 23 +++++++++++---- .../searchResultsPanel/fileMatchContainer.tsx | 28 +++++++++++-------- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/packages/web/src/app/globals.css b/packages/web/src/app/globals.css index 7153c1ee..d1ff0cbb 100644 --- a/packages/web/src/app/globals.css +++ b/packages/web/src/app/globals.css @@ -85,4 +85,12 @@ .cm-editor .cm-searchMatch-selected { border: solid; +} + +.truncate-start { + direction: rtl; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } \ No newline at end of file diff --git a/packages/web/src/app/search/components/codePreviewPanel/codePreview.tsx b/packages/web/src/app/search/components/codePreviewPanel/codePreview.tsx index 35f764da..3aa6fb34 100644 --- a/packages/web/src/app/search/components/codePreviewPanel/codePreview.tsx +++ b/packages/web/src/app/search/components/codePreviewPanel/codePreview.tsx @@ -43,8 +43,8 @@ export const CodePreview = ({ }: CodePreviewProps) => { const editorRef = useRef(null); - const [ keymapType ] = useKeymapType(); - const { theme } = useThemeNormalized(); + const [keymapType] = useKeymapType(); + const { theme } = useThemeNormalized(); const [gutterWidth, setGutterWidth] = useState(0); const keymapExtension = useExtensionWithDependency( @@ -109,7 +109,9 @@ export const CodePreview = ({ return (
-
+
+ + {/* Gutter icon */}
+
+ + {/* File path */} +
{ if (file?.link) { window.open(file.link, "_blank"); } }} + title={file?.filepath} > {file?.filepath}
-
+ +
+ {/* Match selector */} {file && file.matches.length > 0 && ( <>

{`${selectedMatchIndex + 1} of ${ranges.length}`}

@@ -154,6 +163,8 @@ export const CodePreview = ({ )} + + {/* Close button */}
- {/* Search Results & Code Preview */} - - - {isLoading ? ( -
- -

Searching...

-
- ) : fileMatches.length > 0 ? ( - - { - setSelectedFile(fileMatch); - }} - onMatchIndexChanged={(matchIndex) => { - setSelectedMatchIndex(matchIndex); - }} - /> - {isMoreResultsButtonVisible && ( -
- - Load more results - -
- )} - -
- ) : ( -
-

No results found

-
- )} -
- - -
+ {isLoading ? ( +
+ +

Searching...

+
+ ) : ( + + )}
); } + +interface PanelGroupProps { + fileMatches: SearchResultFile[]; + isMoreResultsButtonVisible?: boolean; + onLoadMoreResults: () => void; +} + +const PanelGroup = ({ + fileMatches, + isMoreResultsButtonVisible, + onLoadMoreResults, +}: PanelGroupProps) => { + const [selectedMatchIndex, setSelectedMatchIndex] = useState(0); + const [selectedFile, setSelectedFile] = useState(undefined); + + const codePreviewPanelRef = useRef(null); + useEffect(() => { + if (selectedFile) { + codePreviewPanelRef.current?.expand(); + } else { + codePreviewPanelRef.current?.collapse(); + } + }, [selectedFile]); + + return ( + + {/* ~~ Filter panel ~~ */} + + + + + + {/* ~~ Search results ~~ */} + + {fileMatches.length > 0 ? ( + + { + setSelectedFile(fileMatch); + }} + onMatchIndexChanged={(matchIndex) => { + setSelectedMatchIndex(matchIndex); + }} + /> + {isMoreResultsButtonVisible && ( +
+ + Load more results + +
+ )} + +
+ ) : ( +
+

No results found

+
+ )} +
+ + + {/* ~~ Code preview ~~ */} + + setSelectedFile(undefined)} + selectedMatchIndex={selectedMatchIndex} + onSelectedMatchIndexChange={setSelectedMatchIndex} + /> + +
+ ) +} \ No newline at end of file From d374b1f2180622417413c5b128592644c0ddf1ba Mon Sep 17 00:00:00 2001 From: bkellam Date: Wed, 23 Oct 2024 10:37:34 -0400 Subject: [PATCH 3/9] Wip on repository filtering --- packages/web/src/app/repositoryCarousel.tsx | 2 +- .../src/app/search/components/filterPanel.tsx | 97 ------------------- .../search/components/filterPanel/entry.tsx | 66 +++++++++++++ .../search/components/filterPanel/index.tsx | 97 +++++++++++++++++++ .../searchResultsPanel/fileMatchContainer.tsx | 2 +- packages/web/src/app/search/page.tsx | 10 +- packages/web/src/lib/utils.ts | 4 +- 7 files changed, 174 insertions(+), 104 deletions(-) delete mode 100644 packages/web/src/app/search/components/filterPanel.tsx create mode 100644 packages/web/src/app/search/components/filterPanel/entry.tsx create mode 100644 packages/web/src/app/search/components/filterPanel/index.tsx diff --git a/packages/web/src/app/repositoryCarousel.tsx b/packages/web/src/app/repositoryCarousel.tsx index f67aea0f..0969ff81 100644 --- a/packages/web/src/app/repositoryCarousel.tsx +++ b/packages/web/src/app/repositoryCarousel.tsx @@ -64,7 +64,7 @@ const RepositoryBadge = ({ repoIcon: {info.costHostName}, repoName: info.repoName, repoLink: info.repoLink, diff --git a/packages/web/src/app/search/components/filterPanel.tsx b/packages/web/src/app/search/components/filterPanel.tsx deleted file mode 100644 index d27ed8fc..00000000 --- a/packages/web/src/app/search/components/filterPanel.tsx +++ /dev/null @@ -1,97 +0,0 @@ -'use client'; - -import { Input } from "@/components/ui/input"; -import { SearchResultFile } from "@/lib/types"; -import { getRepoCodeHostInfo } from "@/lib/utils"; -import { useCallback, useEffect, useState } from "react"; -import Image from "next/image"; -import { FileIcon } from "@radix-ui/react-icons"; -import clsx from "clsx"; - -interface FilePanelProps { - fileMatches: SearchResultFile[]; -} - -type Entry = { - icon?: string; - iconAltText?: string; - count: number; - isSelected: boolean; -} - -export const FilterPanel = ({ - fileMatches, -}: FilePanelProps) => { - const [repos, setRepos] = useState>({}); - - useEffect(() => { - const _repos = fileMatches - .map((fileMatch) => fileMatch.Repository) - .reduce((repos, repoName) => { - if (!repos[repoName]) { - const info = getRepoCodeHostInfo(repoName); - repos[repoName] = { - icon: info?.icon, - iconAltText: info?.costHostName, - count: 0, - isSelected: false, - }; - } - repos[repoName].count += 1; - return repos; - }, {} as Record); - - setRepos(_repos); - }, [fileMatches, setRepos]); - - const onEntryClicked = useCallback((name: string) => { - setRepos((repos) => ({ - ...repos, - [name]: { - ...repos[name], - isSelected: !repos[name].isSelected, - }, - })); - }, []); - - return ( -
-

Filter Results

- -
-

By Repository

- -
- {Object.entries(repos).map(([name, { count, icon, iconAltText, isSelected }]) => ( -
onEntryClicked(name)} - > -
- {icon ? ( - {iconAltText - ) : ( - - )} -

{name}

-
-

{count}

-
- ))} -
-
-
- ) -} \ No newline at end of file diff --git a/packages/web/src/app/search/components/filterPanel/entry.tsx b/packages/web/src/app/search/components/filterPanel/entry.tsx new file mode 100644 index 00000000..c837b80b --- /dev/null +++ b/packages/web/src/app/search/components/filterPanel/entry.tsx @@ -0,0 +1,66 @@ +'use client'; + +import { FileIcon } from "@radix-ui/react-icons"; +import clsx from "clsx"; +import Image from "next/image"; + +export type Entry = { + displayName: string; + count: number; + isSelected: boolean; + icon?: string; + iconAltText?: string; + iconClassName?: string; +} + +interface EntryProps { + entry: Entry, + onClicked: () => void +} + +export const Entry = ({ + entry: { + isSelected, + icon, + iconAltText, + iconClassName, + displayName, + count, + }, + onClicked, +}: EntryProps) => { + + return ( +
onClicked()} + > +
+ {icon ? ( + {iconAltText + ) : ( + + )} +

{displayName}

+
+

{count}

+
+ ); +} + +export const compareEntries = (a: Entry, b: Entry) => { + if (a.isSelected !== b.isSelected) { + return a.isSelected ? 1 : -1; + } + + return a.count - b.count; +} \ No newline at end of file diff --git a/packages/web/src/app/search/components/filterPanel/index.tsx b/packages/web/src/app/search/components/filterPanel/index.tsx new file mode 100644 index 00000000..618ada25 --- /dev/null +++ b/packages/web/src/app/search/components/filterPanel/index.tsx @@ -0,0 +1,97 @@ +'use client'; + +import { Input } from "@/components/ui/input"; +import { SearchResultFile } from "@/lib/types"; +import { getRepoCodeHostInfo } from "@/lib/utils"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { compareEntries, Entry } from "./entry"; + +interface FilePanelProps { + matches: SearchResultFile[]; + onFilterChanged: (filteredMatches: SearchResultFile[]) => void, +} + +export const FilterPanel = ({ + matches, + onFilterChanged, +}: FilePanelProps) => { + const [repos, setRepos] = useState>({}); + + useEffect(() => { + const _repos = matches + .map((fileMatch) => fileMatch.Repository) + .reduce((repos, repoId) => { + if (!repos[repoId]) { + const info = getRepoCodeHostInfo(repoId); + repos[repoId] = { + displayName: info?.repoName ?? repoId, + count: 0, + isSelected: false, + icon: info?.icon, + iconAltText: info?.costHostName, + iconClassName: info?.iconClassName, + }; + } + repos[repoId].count += 1; + return repos; + }, {} as Record); + + setRepos(_repos); + }, [matches, setRepos]); + + const onEntryClicked = useCallback((name: string) => { + setRepos((repos) => ({ + ...repos, + [name]: { + ...repos[name], + isSelected: !repos[name].isSelected, + }, + })); + }, []); + + const filteredMatches = useMemo(() => { + const selectedRepos = new Set( + Object.entries(repos) + .filter(([_, { isSelected }]) => isSelected) + .map(([name]) => name) + ); + + if (selectedRepos.size === 0) { + return matches; + } + + return matches.filter((match) => { + return selectedRepos.has(match.Repository); + }); + }, [matches, repos]); + + useEffect(() => { + onFilterChanged(filteredMatches); + }, [filteredMatches]); + + return ( +
+

Filter Results

+ + {/* Repos filter */} +
+

By Repository

+ + +
+ {Object.entries(repos) + .sort(([_, entryA], [__, entryB]) => compareEntries(entryB, entryA)) + .map(([name, entry]) => ( + onEntryClicked(name) } + /> + ))} +
+
+
+ ) +} diff --git a/packages/web/src/app/search/components/searchResultsPanel/fileMatchContainer.tsx b/packages/web/src/app/search/components/searchResultsPanel/fileMatchContainer.tsx index e1de9147..02951f49 100644 --- a/packages/web/src/app/search/components/searchResultsPanel/fileMatchContainer.tsx +++ b/packages/web/src/app/search/components/searchResultsPanel/fileMatchContainer.tsx @@ -63,7 +63,7 @@ export const FileMatchContainer = ({ repoIcon: {info.costHostName} } } diff --git a/packages/web/src/app/search/page.tsx b/packages/web/src/app/search/page.tsx index 7ef393d4..09316ac3 100644 --- a/packages/web/src/app/search/page.tsx +++ b/packages/web/src/app/search/page.tsx @@ -201,6 +201,7 @@ const PanelGroup = ({ }: PanelGroupProps) => { const [selectedMatchIndex, setSelectedMatchIndex] = useState(0); const [selectedFile, setSelectedFile] = useState(undefined); + const [filteredFileMatches, setFilteredFileMatches] = useState(fileMatches); const codePreviewPanelRef = useRef(null); useEffect(() => { @@ -225,7 +226,10 @@ const PanelGroup = ({ order={1} > { + setFilteredFileMatches(filteredFileMatches) + }} /> - {fileMatches.length > 0 ? ( + {filteredFileMatches.length > 0 ? ( { setSelectedFile(fileMatch); }} diff --git a/packages/web/src/lib/utils.ts b/packages/web/src/lib/utils.ts index c04def13..38846d5f 100644 --- a/packages/web/src/lib/utils.ts +++ b/packages/web/src/lib/utils.ts @@ -35,7 +35,7 @@ type CodeHostInfo = { costHostName: string; repoLink: string; icon: string; - iconClassname?: string; + iconClassName?: string; } export const getRepoCodeHostInfo = (repoName: string): CodeHostInfo | undefined => { @@ -46,7 +46,7 @@ export const getRepoCodeHostInfo = (repoName: string): CodeHostInfo | undefined costHostName: "GitHub", repoLink: `https://${repoName}`, icon: githubLogo, - iconClassname: "dark:invert", + iconClassName: "dark:invert", } } From c9a849c933e8ee3783a79060531a380a0e411307 Mon Sep 17 00:00:00 2001 From: bkellam Date: Wed, 23 Oct 2024 11:03:39 -0400 Subject: [PATCH 4/9] Added filtering --- .../search/components/filterPanel/index.tsx | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/packages/web/src/app/search/components/filterPanel/index.tsx b/packages/web/src/app/search/components/filterPanel/index.tsx index 618ada25..deecd723 100644 --- a/packages/web/src/app/search/components/filterPanel/index.tsx +++ b/packages/web/src/app/search/components/filterPanel/index.tsx @@ -5,6 +5,7 @@ import { SearchResultFile } from "@/lib/types"; import { getRepoCodeHostInfo } from "@/lib/utils"; import { useCallback, useEffect, useMemo, useState } from "react"; import { compareEntries, Entry } from "./entry"; +import { ScrollArea } from "@/components/ui/scroll-area"; interface FilePanelProps { matches: SearchResultFile[]; @@ -16,15 +17,16 @@ export const FilterPanel = ({ onFilterChanged, }: FilePanelProps) => { const [repos, setRepos] = useState>({}); + const [searchFilter, setSearchFilter] = useState(""); useEffect(() => { const _repos = matches - .map((fileMatch) => fileMatch.Repository) - .reduce((repos, repoId) => { - if (!repos[repoId]) { - const info = getRepoCodeHostInfo(repoId); - repos[repoId] = { - displayName: info?.repoName ?? repoId, + .map((fileMatch) => fileMatch['Repository']) + .reduce((repos, key) => { + if (!repos[key]) { + const info = getRepoCodeHostInfo(key); + repos[key] = { + displayName: info?.repoName ?? key, count: 0, isSelected: false, icon: info?.icon, @@ -32,19 +34,19 @@ export const FilterPanel = ({ iconClassName: info?.iconClassName, }; } - repos[repoId].count += 1; + repos[key].count += 1; return repos; }, {} as Record); setRepos(_repos); }, [matches, setRepos]); - const onEntryClicked = useCallback((name: string) => { + const onEntryClicked = useCallback((key: string) => { setRepos((repos) => ({ ...repos, - [name]: { - ...repos[name], - isSelected: !repos[name].isSelected, + [key]: { + ...repos[key], + isSelected: !repos[key].isSelected, }, })); }, []); @@ -75,23 +77,32 @@ export const FilterPanel = ({ {/* Repos filter */}
-

By Repository

+

By Repository

setSearchFilter(event.target.value)} /> -
- {Object.entries(repos) - .sort(([_, entryA], [__, entryB]) => compareEntries(entryB, entryA)) - .map(([name, entry]) => ( - onEntryClicked(name) } - /> - ))} -
+ +
+ {Object.entries(repos) + .sort(([_, entryA], [__, entryB]) => compareEntries(entryB, entryA)) + // @todo: replace with fuzzy find + .filter(([_, { displayName }]) => displayName.startsWith(searchFilter)) + .map(([key, entry]) => ( + onEntryClicked(key) } + /> + ))} +
+
) -} +} \ No newline at end of file From b7f746e9dba445e67a0d6a45e09c6101291ab1e0 Mon Sep 17 00:00:00 2001 From: bkellam Date: Wed, 23 Oct 2024 11:59:24 -0400 Subject: [PATCH 5/9] Add language filtering --- .../search/components/filterPanel/entry.tsx | 4 +- .../search/components/filterPanel/filter.tsx | 52 ++++++ .../search/components/filterPanel/index.tsx | 167 +++++++++++------- 3 files changed, 155 insertions(+), 68 deletions(-) create mode 100644 packages/web/src/app/search/components/filterPanel/filter.tsx diff --git a/packages/web/src/app/search/components/filterPanel/entry.tsx b/packages/web/src/app/search/components/filterPanel/entry.tsx index c837b80b..e6bc21f1 100644 --- a/packages/web/src/app/search/components/filterPanel/entry.tsx +++ b/packages/web/src/app/search/components/filterPanel/entry.tsx @@ -1,6 +1,6 @@ 'use client'; -import { FileIcon } from "@radix-ui/react-icons"; +import { QuestionMarkCircledIcon } from "@radix-ui/react-icons"; import clsx from "clsx"; import Image from "next/image"; @@ -48,7 +48,7 @@ export const Entry = ({ className={`w-4 h-4 flex-shrink-0 ${iconClassName}`} /> ) : ( - + )}

{displayName}

diff --git a/packages/web/src/app/search/components/filterPanel/filter.tsx b/packages/web/src/app/search/components/filterPanel/filter.tsx new file mode 100644 index 00000000..0d3a5caa --- /dev/null +++ b/packages/web/src/app/search/components/filterPanel/filter.tsx @@ -0,0 +1,52 @@ +'use client'; + +import { useState } from "react"; +import { compareEntries, Entry } from "./entry"; +import { Input } from "@/components/ui/input"; +import { ScrollArea } from "@/components/ui/scroll-area"; + +interface FilterProps { + title: string, + searchPlaceholder: string, + entries: Record, + onEntryClicked: (key: string) => void, +} + +export const Filter = ({ + title, + searchPlaceholder, + entries, + onEntryClicked, +}: FilterProps) => { + const [searchFilter, setSearchFilter] = useState(""); + + return ( +
+

{title}

+ setSearchFilter(event.target.value)} + /> + + +
+ {Object.entries(entries) + .sort(([_, entryA], [__, entryB]) => compareEntries(entryB, entryA)) + // @todo: replace with fuzzy find + .filter(([_, { displayName }]) => displayName.startsWith(searchFilter)) + .map(([key, entry]) => ( + onEntryClicked(key)} + /> + ))} +
+
+
+ ) +} \ No newline at end of file diff --git a/packages/web/src/app/search/components/filterPanel/index.tsx b/packages/web/src/app/search/components/filterPanel/index.tsx index deecd723..91a152ff 100644 --- a/packages/web/src/app/search/components/filterPanel/index.tsx +++ b/packages/web/src/app/search/components/filterPanel/index.tsx @@ -1,11 +1,10 @@ 'use client'; -import { Input } from "@/components/ui/input"; import { SearchResultFile } from "@/lib/types"; import { getRepoCodeHostInfo } from "@/lib/utils"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { compareEntries, Entry } from "./entry"; -import { ScrollArea } from "@/components/ui/scroll-area"; +import { SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; +import { Entry } from "./entry"; +import { Filter } from "./filter"; interface FilePanelProps { matches: SearchResultFile[]; @@ -17,92 +16,128 @@ export const FilterPanel = ({ onFilterChanged, }: FilePanelProps) => { const [repos, setRepos] = useState>({}); - const [searchFilter, setSearchFilter] = useState(""); + const [languages, setLanguages] = useState>({}); useEffect(() => { - const _repos = matches - .map((fileMatch) => fileMatch['Repository']) - .reduce((repos, key) => { - if (!repos[key]) { - const info = getRepoCodeHostInfo(key); - repos[key] = { - displayName: info?.repoName ?? key, - count: 0, - isSelected: false, - icon: info?.icon, - iconAltText: info?.costHostName, - iconClassName: info?.iconClassName, - }; - } - repos[key].count += 1; - return repos; - }, {} as Record); - + const _repos = aggregateMatches( + "Repository", + matches, + (key) => { + const info = getRepoCodeHostInfo(key); + return { + displayName: info?.repoName ?? key, + count: 0, + isSelected: false, + icon: info?.icon, + iconAltText: info?.costHostName, + iconClassName: info?.iconClassName, + }; + } + ); + setRepos(_repos); }, [matches, setRepos]); - const onEntryClicked = useCallback((key: string) => { - setRepos((repos) => ({ - ...repos, + useEffect(() => { + const _languages = aggregateMatches( + "Language", + matches, + (key) => { + // @todo: Get language icons + return { + displayName: key, + count: 0, + isSelected: false, + } + } + ) + + setLanguages(_languages); + }, [matches, setLanguages]); + + const onEntryClicked = useCallback(( + key: string, + setter: (value: SetStateAction>) => void, + ) => { + setter((values) => ({ + ...values, [key]: { - ...repos[key], - isSelected: !repos[key].isSelected, + ...values[key], + isSelected: !values[key].isSelected, }, })); }, []); - const filteredMatches = useMemo(() => { + useEffect(() => { const selectedRepos = new Set( Object.entries(repos) .filter(([_, { isSelected }]) => isSelected) - .map(([name]) => name) + .map(([key]) => key) ); - if (selectedRepos.size === 0) { - return matches; - } + const selectedLanguages = new Set( + Object.entries(languages) + .filter(([_, { isSelected }]) => isSelected) + .map(([key]) => key) + ); - return matches.filter((match) => { - return selectedRepos.has(match.Repository); - }); - }, [matches, repos]); + const filteredMatches = matches.filter((match) => + ( + (selectedRepos.size === 0 ? true : selectedRepos.has(match.Repository)) && + (selectedLanguages.size === 0 ? true : selectedLanguages.has(match.Language)) + ) + ); - useEffect(() => { onFilterChanged(filteredMatches); - }, [filteredMatches]); + }, [matches, repos, languages]); return (

Filter Results

- {/* Repos filter */} -
-

By Repository

- setSearchFilter(event.target.value)} - /> + onEntryClicked(key, setRepos)} + /> - -
- {Object.entries(repos) - .sort(([_, entryA], [__, entryB]) => compareEntries(entryB, entryA)) - // @todo: replace with fuzzy find - .filter(([_, { displayName }]) => displayName.startsWith(searchFilter)) - .map(([key, entry]) => ( - onEntryClicked(key) } - /> - ))} -
-
-
+ onEntryClicked(key, setLanguages)} + />
) +} + +/* Aggregates `matches` by the given `propName`. The result is a record + * of `Entry` objects, where the key is the aggregated `propName` and + * the value is the entry created by `createEntry`. Example: + * + * "repo1": { + * "count": 22, + * ... + * }, + * "repo2": { + * "count": 9, + * ... + * } + */ +const aggregateMatches = ( + propName: 'Repository' | 'Language', + matches: SearchResultFile[], + createEntry: (key: string) => Entry +) => { + return matches + .map((match) => match[propName]) + .filter((key) => key.length > 0) + .reduce((aggregation, key) => { + if (!aggregation[key]) { + aggregation[key] = createEntry(key); + } + aggregation[key].count += 1; + return aggregation; + }, {} as Record) } \ No newline at end of file From 14351b97321cc9d45ecdd5d4ddc4aa836a2175b3 Mon Sep 17 00:00:00 2001 From: bkellam Date: Wed, 23 Oct 2024 14:07:57 -0700 Subject: [PATCH 6/9] nit --- packages/web/src/app/search/components/filterPanel/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/app/search/components/filterPanel/index.tsx b/packages/web/src/app/search/components/filterPanel/index.tsx index 91a152ff..fd53a939 100644 --- a/packages/web/src/app/search/components/filterPanel/index.tsx +++ b/packages/web/src/app/search/components/filterPanel/index.tsx @@ -2,7 +2,7 @@ import { SearchResultFile } from "@/lib/types"; import { getRepoCodeHostInfo } from "@/lib/utils"; -import { SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; +import { SetStateAction, useCallback, useEffect, useState } from "react"; import { Entry } from "./entry"; import { Filter } from "./filter"; From a97209aa1049abdf002420ba571fe14a3a91737e Mon Sep 17 00:00:00 2001 From: bkellam Date: Thu, 24 Oct 2024 10:55:09 -0700 Subject: [PATCH 7/9] Language icons --- .../public/languages/file_type_assembly.svg | 1 + .../web/public/languages/file_type_c3.svg | 1 + .../web/public/languages/file_type_cpp3.svg | 1 + .../public/languages/file_type_csharp2.svg | 1 + .../web/public/languages/file_type_css.svg | 1 + .../public/languages/file_type_dartlang.svg | 1 + .../web/public/languages/file_type_go.svg | 1 + .../public/languages/file_type_haskell.svg | 1 + .../web/public/languages/file_type_html.svg | 1 + .../web/public/languages/file_type_java.svg | 1 + .../languages/file_type_js_official.svg | 1 + .../web/public/languages/file_type_json.svg | 1 + .../web/public/languages/file_type_julia.svg | 1 + .../web/public/languages/file_type_kotlin.svg | 1 + .../web/public/languages/file_type_lua.svg | 1 + .../public/languages/file_type_markdown.svg | 1 + .../web/public/languages/file_type_matlab.svg | 1 + .../public/languages/file_type_objectivec.svg | 1 + .../web/public/languages/file_type_ocaml.svg | 1 + .../web/public/languages/file_type_perl.svg | 1 + .../web/public/languages/file_type_php3.svg | 1 + .../public/languages/file_type_powershell.svg | 1 + .../web/public/languages/file_type_python.svg | 1 + packages/web/public/languages/file_type_r.svg | 1 + .../web/public/languages/file_type_ruby.svg | 1 + .../web/public/languages/file_type_rust.svg | 1 + .../web/public/languages/file_type_shell.svg | 1 + .../web/public/languages/file_type_swift.svg | 1 + .../web/public/languages/file_type_tex.svg | 1 + .../web/public/languages/file_type_text.svg | 1 + .../file_type_typescript_official.svg | 1 + .../web/public/languages/file_type_yaml.svg | 1 + .../web/public/languages/file_type_zig.svg | 1 + .../search/components/filterPanel/index.tsx | 4 +- .../components/filterPanel/languageIcons.ts | 110 ++++++++++++++++++ packages/web/tsconfig.json | 3 +- 36 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 packages/web/public/languages/file_type_assembly.svg create mode 100644 packages/web/public/languages/file_type_c3.svg create mode 100644 packages/web/public/languages/file_type_cpp3.svg create mode 100644 packages/web/public/languages/file_type_csharp2.svg create mode 100644 packages/web/public/languages/file_type_css.svg create mode 100644 packages/web/public/languages/file_type_dartlang.svg create mode 100644 packages/web/public/languages/file_type_go.svg create mode 100644 packages/web/public/languages/file_type_haskell.svg create mode 100644 packages/web/public/languages/file_type_html.svg create mode 100644 packages/web/public/languages/file_type_java.svg create mode 100644 packages/web/public/languages/file_type_js_official.svg create mode 100644 packages/web/public/languages/file_type_json.svg create mode 100644 packages/web/public/languages/file_type_julia.svg create mode 100644 packages/web/public/languages/file_type_kotlin.svg create mode 100644 packages/web/public/languages/file_type_lua.svg create mode 100644 packages/web/public/languages/file_type_markdown.svg create mode 100644 packages/web/public/languages/file_type_matlab.svg create mode 100644 packages/web/public/languages/file_type_objectivec.svg create mode 100644 packages/web/public/languages/file_type_ocaml.svg create mode 100644 packages/web/public/languages/file_type_perl.svg create mode 100644 packages/web/public/languages/file_type_php3.svg create mode 100644 packages/web/public/languages/file_type_powershell.svg create mode 100644 packages/web/public/languages/file_type_python.svg create mode 100644 packages/web/public/languages/file_type_r.svg create mode 100644 packages/web/public/languages/file_type_ruby.svg create mode 100644 packages/web/public/languages/file_type_rust.svg create mode 100644 packages/web/public/languages/file_type_shell.svg create mode 100644 packages/web/public/languages/file_type_swift.svg create mode 100644 packages/web/public/languages/file_type_tex.svg create mode 100644 packages/web/public/languages/file_type_text.svg create mode 100644 packages/web/public/languages/file_type_typescript_official.svg create mode 100644 packages/web/public/languages/file_type_yaml.svg create mode 100644 packages/web/public/languages/file_type_zig.svg create mode 100644 packages/web/src/app/search/components/filterPanel/languageIcons.ts diff --git a/packages/web/public/languages/file_type_assembly.svg b/packages/web/public/languages/file_type_assembly.svg new file mode 100644 index 00000000..4c4584b5 --- /dev/null +++ b/packages/web/public/languages/file_type_assembly.svg @@ -0,0 +1 @@ +file_type_assembly \ No newline at end of file diff --git a/packages/web/public/languages/file_type_c3.svg b/packages/web/public/languages/file_type_c3.svg new file mode 100644 index 00000000..2d8ac394 --- /dev/null +++ b/packages/web/public/languages/file_type_c3.svg @@ -0,0 +1 @@ +file_type_c3 \ No newline at end of file diff --git a/packages/web/public/languages/file_type_cpp3.svg b/packages/web/public/languages/file_type_cpp3.svg new file mode 100644 index 00000000..b999f2ea --- /dev/null +++ b/packages/web/public/languages/file_type_cpp3.svg @@ -0,0 +1 @@ +file_type_cpp3 \ No newline at end of file diff --git a/packages/web/public/languages/file_type_csharp2.svg b/packages/web/public/languages/file_type_csharp2.svg new file mode 100644 index 00000000..882bf4a3 --- /dev/null +++ b/packages/web/public/languages/file_type_csharp2.svg @@ -0,0 +1 @@ +file_type_csharp2 \ No newline at end of file diff --git a/packages/web/public/languages/file_type_css.svg b/packages/web/public/languages/file_type_css.svg new file mode 100644 index 00000000..3d8959a0 --- /dev/null +++ b/packages/web/public/languages/file_type_css.svg @@ -0,0 +1 @@ +file_type_css \ No newline at end of file diff --git a/packages/web/public/languages/file_type_dartlang.svg b/packages/web/public/languages/file_type_dartlang.svg new file mode 100644 index 00000000..0b258097 --- /dev/null +++ b/packages/web/public/languages/file_type_dartlang.svg @@ -0,0 +1 @@ +file_type_dartlang \ No newline at end of file diff --git a/packages/web/public/languages/file_type_go.svg b/packages/web/public/languages/file_type_go.svg new file mode 100644 index 00000000..05a1baa1 --- /dev/null +++ b/packages/web/public/languages/file_type_go.svg @@ -0,0 +1 @@ +file_type_go \ No newline at end of file diff --git a/packages/web/public/languages/file_type_haskell.svg b/packages/web/public/languages/file_type_haskell.svg new file mode 100644 index 00000000..f818dac4 --- /dev/null +++ b/packages/web/public/languages/file_type_haskell.svg @@ -0,0 +1 @@ +file_type_haskell \ No newline at end of file diff --git a/packages/web/public/languages/file_type_html.svg b/packages/web/public/languages/file_type_html.svg new file mode 100644 index 00000000..a0152d86 --- /dev/null +++ b/packages/web/public/languages/file_type_html.svg @@ -0,0 +1 @@ +file_type_html \ No newline at end of file diff --git a/packages/web/public/languages/file_type_java.svg b/packages/web/public/languages/file_type_java.svg new file mode 100644 index 00000000..14121c58 --- /dev/null +++ b/packages/web/public/languages/file_type_java.svg @@ -0,0 +1 @@ +file_type_java \ No newline at end of file diff --git a/packages/web/public/languages/file_type_js_official.svg b/packages/web/public/languages/file_type_js_official.svg new file mode 100644 index 00000000..bcfade41 --- /dev/null +++ b/packages/web/public/languages/file_type_js_official.svg @@ -0,0 +1 @@ +file_type_js_official \ No newline at end of file diff --git a/packages/web/public/languages/file_type_json.svg b/packages/web/public/languages/file_type_json.svg new file mode 100644 index 00000000..26c39ba7 --- /dev/null +++ b/packages/web/public/languages/file_type_json.svg @@ -0,0 +1 @@ +file_type_json \ No newline at end of file diff --git a/packages/web/public/languages/file_type_julia.svg b/packages/web/public/languages/file_type_julia.svg new file mode 100644 index 00000000..49343a27 --- /dev/null +++ b/packages/web/public/languages/file_type_julia.svg @@ -0,0 +1 @@ +file_type_julia \ No newline at end of file diff --git a/packages/web/public/languages/file_type_kotlin.svg b/packages/web/public/languages/file_type_kotlin.svg new file mode 100644 index 00000000..4b0961cb --- /dev/null +++ b/packages/web/public/languages/file_type_kotlin.svg @@ -0,0 +1 @@ +file_type_kotlin \ No newline at end of file diff --git a/packages/web/public/languages/file_type_lua.svg b/packages/web/public/languages/file_type_lua.svg new file mode 100644 index 00000000..44f3fa08 --- /dev/null +++ b/packages/web/public/languages/file_type_lua.svg @@ -0,0 +1 @@ +file_type_lua \ No newline at end of file diff --git a/packages/web/public/languages/file_type_markdown.svg b/packages/web/public/languages/file_type_markdown.svg new file mode 100644 index 00000000..c5b32a6f --- /dev/null +++ b/packages/web/public/languages/file_type_markdown.svg @@ -0,0 +1 @@ +file_type_markdown \ No newline at end of file diff --git a/packages/web/public/languages/file_type_matlab.svg b/packages/web/public/languages/file_type_matlab.svg new file mode 100644 index 00000000..0b5e3755 --- /dev/null +++ b/packages/web/public/languages/file_type_matlab.svg @@ -0,0 +1 @@ +file_type_matlab \ No newline at end of file diff --git a/packages/web/public/languages/file_type_objectivec.svg b/packages/web/public/languages/file_type_objectivec.svg new file mode 100644 index 00000000..fe0a61be --- /dev/null +++ b/packages/web/public/languages/file_type_objectivec.svg @@ -0,0 +1 @@ +file_type_objectivec \ No newline at end of file diff --git a/packages/web/public/languages/file_type_ocaml.svg b/packages/web/public/languages/file_type_ocaml.svg new file mode 100644 index 00000000..8e5d8e9a --- /dev/null +++ b/packages/web/public/languages/file_type_ocaml.svg @@ -0,0 +1 @@ +file_type_ocaml \ No newline at end of file diff --git a/packages/web/public/languages/file_type_perl.svg b/packages/web/public/languages/file_type_perl.svg new file mode 100644 index 00000000..8b8be680 --- /dev/null +++ b/packages/web/public/languages/file_type_perl.svg @@ -0,0 +1 @@ +file_type_perl \ No newline at end of file diff --git a/packages/web/public/languages/file_type_php3.svg b/packages/web/public/languages/file_type_php3.svg new file mode 100644 index 00000000..aaed635e --- /dev/null +++ b/packages/web/public/languages/file_type_php3.svg @@ -0,0 +1 @@ +file_type_php3 \ No newline at end of file diff --git a/packages/web/public/languages/file_type_powershell.svg b/packages/web/public/languages/file_type_powershell.svg new file mode 100644 index 00000000..05c95b31 --- /dev/null +++ b/packages/web/public/languages/file_type_powershell.svg @@ -0,0 +1 @@ +file_type_powershell \ No newline at end of file diff --git a/packages/web/public/languages/file_type_python.svg b/packages/web/public/languages/file_type_python.svg new file mode 100644 index 00000000..677f2165 --- /dev/null +++ b/packages/web/public/languages/file_type_python.svg @@ -0,0 +1 @@ +file_type_python \ No newline at end of file diff --git a/packages/web/public/languages/file_type_r.svg b/packages/web/public/languages/file_type_r.svg new file mode 100644 index 00000000..28f49c5e --- /dev/null +++ b/packages/web/public/languages/file_type_r.svg @@ -0,0 +1 @@ +file_type_r \ No newline at end of file diff --git a/packages/web/public/languages/file_type_ruby.svg b/packages/web/public/languages/file_type_ruby.svg new file mode 100644 index 00000000..9443db1f --- /dev/null +++ b/packages/web/public/languages/file_type_ruby.svg @@ -0,0 +1 @@ +file_type_ruby \ No newline at end of file diff --git a/packages/web/public/languages/file_type_rust.svg b/packages/web/public/languages/file_type_rust.svg new file mode 100644 index 00000000..327fd299 --- /dev/null +++ b/packages/web/public/languages/file_type_rust.svg @@ -0,0 +1 @@ +file_type_rust \ No newline at end of file diff --git a/packages/web/public/languages/file_type_shell.svg b/packages/web/public/languages/file_type_shell.svg new file mode 100644 index 00000000..17d38213 --- /dev/null +++ b/packages/web/public/languages/file_type_shell.svg @@ -0,0 +1 @@ +file_type_shell \ No newline at end of file diff --git a/packages/web/public/languages/file_type_swift.svg b/packages/web/public/languages/file_type_swift.svg new file mode 100644 index 00000000..c232d1f7 --- /dev/null +++ b/packages/web/public/languages/file_type_swift.svg @@ -0,0 +1 @@ +file_type_swift \ No newline at end of file diff --git a/packages/web/public/languages/file_type_tex.svg b/packages/web/public/languages/file_type_tex.svg new file mode 100644 index 00000000..952a2dec --- /dev/null +++ b/packages/web/public/languages/file_type_tex.svg @@ -0,0 +1 @@ +file_type_tex \ No newline at end of file diff --git a/packages/web/public/languages/file_type_text.svg b/packages/web/public/languages/file_type_text.svg new file mode 100644 index 00000000..a5562edd --- /dev/null +++ b/packages/web/public/languages/file_type_text.svg @@ -0,0 +1 @@ +file_type_text \ No newline at end of file diff --git a/packages/web/public/languages/file_type_typescript_official.svg b/packages/web/public/languages/file_type_typescript_official.svg new file mode 100644 index 00000000..bac7e33c --- /dev/null +++ b/packages/web/public/languages/file_type_typescript_official.svg @@ -0,0 +1 @@ +file_type_typescript_official \ No newline at end of file diff --git a/packages/web/public/languages/file_type_yaml.svg b/packages/web/public/languages/file_type_yaml.svg new file mode 100644 index 00000000..601979d5 --- /dev/null +++ b/packages/web/public/languages/file_type_yaml.svg @@ -0,0 +1 @@ +file_type_yaml \ No newline at end of file diff --git a/packages/web/public/languages/file_type_zig.svg b/packages/web/public/languages/file_type_zig.svg new file mode 100644 index 00000000..7e954652 --- /dev/null +++ b/packages/web/public/languages/file_type_zig.svg @@ -0,0 +1 @@ +file_type_zig \ No newline at end of file diff --git a/packages/web/src/app/search/components/filterPanel/index.tsx b/packages/web/src/app/search/components/filterPanel/index.tsx index fd53a939..9ec54db9 100644 --- a/packages/web/src/app/search/components/filterPanel/index.tsx +++ b/packages/web/src/app/search/components/filterPanel/index.tsx @@ -5,6 +5,7 @@ import { getRepoCodeHostInfo } from "@/lib/utils"; import { SetStateAction, useCallback, useEffect, useState } from "react"; import { Entry } from "./entry"; import { Filter } from "./filter"; +import { getLanguageIcon } from "./languageIcons"; interface FilePanelProps { matches: SearchResultFile[]; @@ -48,7 +49,8 @@ export const FilterPanel = ({ displayName: key, count: 0, isSelected: false, - } + icon: getLanguageIcon(key), + } satisfies Entry; } ) diff --git a/packages/web/src/app/search/components/filterPanel/languageIcons.ts b/packages/web/src/app/search/components/filterPanel/languageIcons.ts new file mode 100644 index 00000000..c9e2156f --- /dev/null +++ b/packages/web/src/app/search/components/filterPanel/languageIcons.ts @@ -0,0 +1,110 @@ +import JavaScriptIcon from "@/public/languages/file_type_js_official.svg"; +import TypeScriptIcon from "@/public/languages/file_type_typescript_official.svg"; +import GoIcon from "@/public/languages/file_type_go.svg"; +import MarkdownIcon from "@/public/languages/file_type_markdown.svg"; +import CIcon from "@/public/languages/file_type_c3.svg"; +import CppIcon from "@/public/languages/file_type_cpp3.svg"; +import CSharpIcon from "@/public/languages/file_type_csharp2.svg"; +import CSSIcon from "@/public/languages/file_type_css.svg"; +import HTMLIcon from "@/public/languages/file_type_html.svg"; +import JavaIcon from "@/public/languages/file_type_java.svg"; +import JSONIcon from "@/public/languages/file_type_json.svg"; +import PythonIcon from "@/public/languages/file_type_python.svg"; +import RubyIcon from "@/public/languages/file_type_ruby.svg"; +import RustIcon from "@/public/languages/file_type_rust.svg"; +import YAMLIcon from "@/public/languages/file_type_yaml.svg"; +import KotlinIcon from "@/public/languages/file_type_kotlin.svg"; +import SwiftIcon from "@/public/languages/file_type_swift.svg"; +import PHPIcon from "@/public/languages/file_type_php3.svg"; +import RIcon from "@/public/languages/file_type_r.svg"; +import MatlabIcon from "@/public/languages/file_type_matlab.svg"; +import ObjectiveCIcon from "@/public/languages/file_type_objectivec.svg"; +import LuaIcon from "@/public/languages/file_type_lua.svg"; +import DartIcon from "@/public/languages/file_type_dartlang.svg"; +import HaskellIcon from "@/public/languages/file_type_haskell.svg"; +import PerlIcon from "@/public/languages/file_type_perl.svg"; +import ShellIcon from "@/public/languages/file_type_shell.svg"; +import ZigIcon from "@/public/languages/file_type_zig.svg"; +import JuliaIcon from "@/public/languages/file_type_julia.svg"; +import OcamlIcon from "@/public/languages/file_type_ocaml.svg"; +import TextIcon from "@/public/languages/file_type_text.svg"; +import PowershellIcon from "@/public/languages/file_type_powershell.svg"; +import TexIcon from "@/public/languages/file_type_tex.svg"; +import AssemblyIcon from "@/public/languages/file_type_assembly.svg"; + +export const getLanguageIcon = (language: string) => { + switch (language.toLowerCase()) { + case "tsx": + case "typescript": + return TypeScriptIcon; + case "jsx": + case "javascript": + return JavaScriptIcon; + case "go": + return GoIcon; + case "markdown": + return MarkdownIcon; + case "c": + return CIcon; + case "c++": + return CppIcon; + case "python": + return PythonIcon; + case "c#": + return CSharpIcon; + case "html": + return HTMLIcon; + case "css": + return CSSIcon; + case "java": + return JavaIcon; + case "json with comments": + case "json": + return JSONIcon; + case "ruby": + return RubyIcon; + case "rust": + return RustIcon; + case "yaml": + return YAMLIcon; + case "kotlin": + return KotlinIcon; + case "swift": + return SwiftIcon; + case "php": + return PHPIcon; + case "r": + return RIcon; + case "matlab": + return MatlabIcon; + case "objective-c": + return ObjectiveCIcon; + case "lua": + return LuaIcon; + case "dart": + return DartIcon; + case "haskell": + return HaskellIcon; + case "perl": + return PerlIcon; + case "makefile": + case "shell": + return ShellIcon; + case "zig": + return ZigIcon; + case "julia": + return JuliaIcon; + case "ocaml": + return OcamlIcon; + case "text": + return TextIcon; + case "powershell": + return PowershellIcon; + case "tex": + return TexIcon; + case "assembly": + return AssemblyIcon; + default: + return null; + } +} diff --git a/packages/web/tsconfig.json b/packages/web/tsconfig.json index 7b285893..6b2f2e65 100644 --- a/packages/web/tsconfig.json +++ b/packages/web/tsconfig.json @@ -18,7 +18,8 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "@/public/*": ["./public/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], From 9adfeddacbe4e65df8f0671ec27414697a638fd1 Mon Sep 17 00:00:00 2001 From: bkellam Date: Thu, 24 Oct 2024 12:18:42 -0700 Subject: [PATCH 8/9] fuzzy find --- packages/web/package.json | 1 + .../search/components/filterPanel/entry.tsx | 1 + .../search/components/filterPanel/filter.tsx | 30 ++++++++++++++----- .../search/components/filterPanel/index.tsx | 6 ++-- yarn.lock | 5 ++++ 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/packages/web/package.json b/packages/web/package.json index ae7cb054..cc523f8e 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -45,6 +45,7 @@ "embla-carousel-auto-scroll": "^8.3.0", "embla-carousel-react": "^8.3.0", "escape-string-regexp": "^5.0.0", + "fuse.js": "^7.0.0", "http-status-codes": "^2.3.0", "lucide-react": "^0.435.0", "next": "14.2.10", diff --git a/packages/web/src/app/search/components/filterPanel/entry.tsx b/packages/web/src/app/search/components/filterPanel/entry.tsx index e6bc21f1..ff67d4cf 100644 --- a/packages/web/src/app/search/components/filterPanel/entry.tsx +++ b/packages/web/src/app/search/components/filterPanel/entry.tsx @@ -5,6 +5,7 @@ import clsx from "clsx"; import Image from "next/image"; export type Entry = { + key: string; displayName: string; count: number; isSelected: boolean; diff --git a/packages/web/src/app/search/components/filterPanel/filter.tsx b/packages/web/src/app/search/components/filterPanel/filter.tsx index 0d3a5caa..0faacbca 100644 --- a/packages/web/src/app/search/components/filterPanel/filter.tsx +++ b/packages/web/src/app/search/components/filterPanel/filter.tsx @@ -1,14 +1,15 @@ 'use client'; -import { useState } from "react"; +import { useMemo, useState } from "react"; import { compareEntries, Entry } from "./entry"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; +import Fuse from "fuse.js"; interface FilterProps { title: string, searchPlaceholder: string, - entries: Record, + entries: Entry[], onEntryClicked: (key: string) => void, } @@ -20,6 +21,20 @@ export const Filter = ({ }: FilterProps) => { const [searchFilter, setSearchFilter] = useState(""); + const filteredEntries = useMemo(() => { + if (searchFilter === "") { + return entries; + } + + const fuse = new Fuse(entries, { + keys: ["displayName"], + threshold: 0.3, + }); + + const result = fuse.search(searchFilter); + return result.map((result) => result.item); + }, [entries, searchFilter]); + return (

{title}

@@ -35,14 +50,13 @@ export const Filter = ({
- {Object.entries(entries) - .sort(([_, entryA], [__, entryB]) => compareEntries(entryB, entryA)) - // @todo: replace with fuzzy find - .filter(([_, { displayName }]) => displayName.startsWith(searchFilter)) - .map(([key, entry]) => ( + {filteredEntries + .sort((entryA, entryB) => compareEntries(entryB, entryA)) + .map((entry) => ( onEntryClicked(key)} + onClicked={() => onEntryClicked(entry.key)} /> ))}
diff --git a/packages/web/src/app/search/components/filterPanel/index.tsx b/packages/web/src/app/search/components/filterPanel/index.tsx index 9ec54db9..1a220fe3 100644 --- a/packages/web/src/app/search/components/filterPanel/index.tsx +++ b/packages/web/src/app/search/components/filterPanel/index.tsx @@ -26,6 +26,7 @@ export const FilterPanel = ({ (key) => { const info = getRepoCodeHostInfo(key); return { + key, displayName: info?.repoName ?? key, count: 0, isSelected: false, @@ -46,6 +47,7 @@ export const FilterPanel = ({ (key) => { // @todo: Get language icons return { + key, displayName: key, count: 0, isSelected: false, @@ -100,14 +102,14 @@ export const FilterPanel = ({ onEntryClicked(key, setRepos)} /> onEntryClicked(key, setLanguages)} />
diff --git a/yarn.lock b/yarn.lock index df394cff..6ffb2bc5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2739,6 +2739,11 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +fuse.js@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-7.0.0.tgz#6573c9fcd4c8268e403b4fc7d7131ffcf99a9eb2" + integrity sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q== + get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" From a8a8bc36cea04d4988eceb1bb50bd62e60d2517e Mon Sep 17 00:00:00 2001 From: bkellam Date: Sat, 26 Oct 2024 09:59:58 -0700 Subject: [PATCH 9/9] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52970136..0a45d299 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Added filtering panel for filtering results by repository and by language. ([#48](https://github.com/sourcebot-dev/sourcebot/pull/48)) + ## [2.1.1] - 2024-10-25 ### Fixed