@@ -21,7 +21,17 @@ import {
2121 websocketConnectionsAtom ,
2222 workspacesAtom ,
2323} from "@yaakapp-internal/models" ;
24+ import type { TreeHandle , TreeItemProps , TreeNode , TreeProps } from "@yaakapp-internal/ui" ;
25+ import {
26+ Icon ,
27+ InlineCode ,
28+ isSelectedFamily ,
29+ LoadingIcon ,
30+ selectedIdsFamily ,
31+ Tree ,
32+ } from "@yaakapp-internal/ui" ;
2433import classNames from "classnames" ;
34+ import { fuzzyMatch } from "fuzzbunny" ;
2535import { atom , useAtomValue } from "jotai" ;
2636import { atomFamily , selectAtom } from "jotai/utils" ;
2737import { memo , useCallback , useEffect , useMemo , useRef } from "react" ;
@@ -42,6 +52,7 @@ import { sendAnyHttpRequest } from "../hooks/useSendAnyHttpRequest";
4252import { useSidebarHidden } from "../hooks/useSidebarHidden" ;
4353import { getWebsocketRequestActions } from "../hooks/useWebsocketRequestActions" ;
4454import { deepEqualAtom } from "../lib/atoms" ;
55+ import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage" ;
4556import { deleteModelWithConfirm } from "../lib/deleteModelWithConfirm" ;
4657import { jotaiStore } from "../lib/jotai" ;
4758import { resolvedModelName } from "../lib/resolvedModelName" ;
@@ -54,19 +65,9 @@ import { filter } from "./core/Editor/filter/extension";
5465import { evaluate , parseQuery } from "./core/Editor/filter/query" ;
5566import { HttpMethodTag } from "./core/HttpMethodTag" ;
5667import { HttpStatusTag } from "./core/HttpStatusTag" ;
57- import {
58- Icon ,
59- LoadingIcon ,
60- Tree ,
61- isSelectedFamily ,
62- selectedIdsFamily ,
63- InlineCode ,
64- } from "@yaakapp-internal/ui" ;
65- import type { TreeNode , TreeHandle , TreeProps , TreeItemProps } from "@yaakapp-internal/ui" ;
6668import { IconButton } from "./core/IconButton" ;
6769import type { InputHandle } from "./core/Input" ;
6870import { Input } from "./core/Input" ;
69- import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage" ;
7071import { GitDropdown } from "./git/GitDropdown" ;
7172
7273const collapsedFamily = atomFamily ( ( treeId : string ) => {
@@ -75,6 +76,7 @@ const collapsedFamily = atomFamily((treeId: string) => {
7576} ) ;
7677
7778type SidebarModel = Workspace | Folder | HttpRequest | GrpcRequest | WebsocketRequest ;
79+
7880function isSidebarLeafModel ( m : AnyModel ) : boolean {
7981 const modelMap : Record < Exclude < SidebarModel [ "model" ] , "workspace" > , null > = {
8082 http_request : null ,
@@ -854,7 +856,9 @@ const SidebarInnerItem = memo(function SidebarInnerItem({
854856
855857 return (
856858 < div className = "flex items-center gap-2 min-w-0 h-full w-full text-left" >
857- < div className = "truncate" > { resolvedModelName ( item ) } </ div >
859+ < div className = "truncate" >
860+ < HighlightedName name = { resolvedModelName ( item ) } />
861+ </ div >
858862 { response != null && (
859863 < div className = "ml-auto" >
860864 { response . state !== "closed" ? (
@@ -868,6 +872,42 @@ const SidebarInnerItem = memo(function SidebarInnerItem({
868872 ) ;
869873} ) ;
870874
875+ const bareTermNeedleAtom = selectAtom ( sidebarFilterAtom , ( filter ) => {
876+ const text = filter . text . trim ( ) ;
877+ if ( ! text ) return "" ;
878+ const bareTerms = text . match ( / (?: ^ | \s ) (? ! - ) (? ! [ " ] ) ( [ A - Z a - z 0 - 9 _ \- . / ] + ) (? ! [ " ] ? : ) / g) ;
879+ if ( ! bareTerms ) return "" ;
880+ return bareTerms
881+ . map ( ( t ) => t . trim ( ) )
882+ . filter ( ( t ) => {
883+ if ( ! t || t . startsWith ( "-" ) || t . startsWith ( '"' ) ) return false ;
884+ if ( / ^ ( A N D | O R | N O T ) $ / i. test ( t ) ) return false ;
885+ if ( t . includes ( ":" ) ) return false ;
886+ return true ;
887+ } )
888+ . join ( " " ) ;
889+ } ) ;
890+
891+ function HighlightedName ( { name } : { name : string } ) {
892+ const needle = useAtomValue ( bareTermNeedleAtom ) ;
893+ if ( ! needle ) return < > { name } </ > ;
894+ const match = fuzzyMatch ( name , needle ) ;
895+ if ( ! match ) return < > { name } </ > ;
896+ return (
897+ < >
898+ { match . highlights . map ( ( segment , i ) =>
899+ i % 2 === 1 ? (
900+ < mark key = { i } className = "bg-transparent text-notice font-semibold" >
901+ { segment }
902+ </ mark >
903+ ) : (
904+ < span key = { i } > { segment } </ span >
905+ ) ,
906+ ) }
907+ </ >
908+ ) ;
909+ }
910+
871911function getItemFields ( node : TreeNode < SidebarModel > ) : Record < string , string > {
872912 const item = node . item ;
873913
0 commit comments