Skip to content

Commit 9421e37

Browse files
committed
feat: added similar search functionality in desktop app
1 parent ca273b7 commit 9421e37

2 files changed

Lines changed: 195 additions & 164 deletions

File tree

desktop/frontend/src/components/BrowserToolbar.tsx

Lines changed: 82 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,32 @@
1-
import {
2-
ChevronUp,
3-
ClipboardPaste,
4-
FolderPlus,
5-
FolderUp,
6-
Loader2,
7-
RefreshCw,
8-
Upload
9-
} from "lucide-react";
10-
import { useEffect, useState } from "react";
1+
import { ChevronUp, ClipboardPaste, RefreshCw, Search, X } from "lucide-react";
112

123
interface Props {
134
currentPath: string;
145
parentPath: string;
156
deviceRootPath: string;
167
loading: boolean;
17-
uploading: boolean;
188
disabled: boolean;
199
onNavigate: (path: string) => void;
20-
onUploadFiles: () => void;
21-
onUploadFolder: () => void;
22-
onCreateFolderClick: () => void;
2310
onShareClipboard: () => void;
11+
searchQuery: string;
12+
setSearchQuery: (query: string) => void;
13+
isSearchActive: boolean;
14+
setIsSearchActive: (active: boolean) => void;
2415
}
2516

2617
export function BrowserToolbar({
2718
currentPath,
2819
parentPath,
2920
deviceRootPath,
3021
loading,
31-
uploading,
3222
disabled,
3323
onNavigate,
34-
onUploadFiles,
35-
onUploadFolder,
36-
onCreateFolderClick,
37-
onShareClipboard
24+
onShareClipboard,
25+
searchQuery,
26+
setSearchQuery,
27+
isSearchActive,
28+
setIsSearchActive,
3829
}: Props) {
39-
const [showFolderMenu, setShowFolderMenu] = useState(false);
40-
41-
// Close dropdown on outside click
42-
useEffect(() => {
43-
const handleClickOutside = () => setShowFolderMenu(false);
44-
if (showFolderMenu) document.addEventListener("click", handleClickOutside);
45-
return () => document.removeEventListener("click", handleClickOutside);
46-
}, [showFolderMenu]);
47-
4830
// ── Path Calculations ──
4931
const normPath = currentPath ? currentPath.replace(/\\/g, "/") : "";
5032
const normRoot = deviceRootPath ? deviceRootPath.replace(/\\/g, "/") : "";
@@ -72,7 +54,7 @@ export function BrowserToolbar({
7254
};
7355

7456
return (
75-
<div className="flex items-center gap-2 px-4 py-2.5 border-b border-[#1e2535] bg-surface/60 shrink-0">
57+
<div className="flex items-center gap-2 px-4 py-2.5 border-b border-[#1e2535] bg-surface/60 shrink-0 h-12">
7658
<button
7759
onClick={() => onNavigate(parentPath || "/")}
7860
disabled={!canGoUp || disabled}
@@ -81,52 +63,75 @@ export function BrowserToolbar({
8163
<ChevronUp size={16} />
8264
</button>
8365

84-
{/* ── Breadcrumbs ── */}
85-
<div className="flex-1 flex items-center gap-1 overflow-x-auto hide-scrollbar min-w-0">
86-
<button
87-
onClick={() => onNavigate("/")}
88-
className="text-[11px] font-mono text-[#3d4d63] hover:text-accent transition-colors shrink-0 px-1 py-0.5 rounded hover:bg-accent/8"
89-
>
90-
/
91-
</button>
66+
{/* ── DYNAMIC AREA: Search Input OR Breadcrumbs ── */}
67+
{isSearchActive ? (
68+
<div className="flex-1 flex items-center gap-2 px-2 bg-bg-base/50 border border-accent/30 rounded-lg h-full">
69+
<Search size={14} className="text-accent shrink-0" />
70+
<input
71+
autoFocus
72+
type="text"
73+
value={searchQuery}
74+
onChange={(e) => setSearchQuery(e.target.value)}
75+
placeholder="Search locally..."
76+
className="flex-1 bg-transparent border-none outline-none text-[13px] text-[#dde4f0] placeholder-[#3d4d63] min-w-0"
77+
/>
78+
<button
79+
onClick={() => {
80+
setSearchQuery("");
81+
setIsSearchActive(false);
82+
}}
83+
className="p-1 text-[#8090a8] hover:text-[#dde4f0] rounded"
84+
>
85+
<X size={14} />
86+
</button>
87+
</div>
88+
) : (
89+
<div className="flex-1 flex items-center gap-1 overflow-x-auto hide-scrollbar min-w-0">
90+
<button
91+
onClick={() => onNavigate("/")}
92+
className="text-[11px] font-mono text-[#3d4d63] hover:text-accent transition-colors shrink-0 px-1 py-0.5 rounded hover:bg-accent/8"
93+
>
94+
/
95+
</button>
9296

93-
{showEllipsis && (
94-
<span className="flex items-center gap-1 shrink-0">
95-
<span className="text-[#1e2535] text-[11px]">/</span>
96-
<span className="text-[11px] font-mono px-1 py-0.5 text-[#8090a8] select-none">
97-
...
97+
{showEllipsis && (
98+
<span className="flex items-center gap-1 shrink-0">
99+
<span className="text-[#1e2535] text-[11px]">/</span>
100+
<span className="text-[11px] font-mono px-1 py-0.5 text-[#8090a8] select-none">
101+
...
102+
</span>
98103
</span>
99-
</span>
100-
)}
101-
102-
{visibleSegments.map((seg, i) => {
103-
const originalIndex =
104-
pathSegments.length - visibleSegments.length + i;
105-
const relativeSegPath =
106-
"/" + pathSegments.slice(0, originalIndex + 1).join("/");
107-
const absoluteSegPath = buildAbsolutePath(relativeSegPath);
108-
const isLast = originalIndex === pathSegments.length - 1;
104+
)}
109105

110-
return (
111-
<span
112-
key={originalIndex}
113-
className="flex items-center gap-1 shrink-0 min-w-0"
114-
>
115-
<span className="text-[#1e2535] text-[11px]">/</span>
116-
<button
117-
onClick={() => !isLast && onNavigate(absoluteSegPath)}
118-
title={seg}
119-
className={`
120-
text-[11px] font-mono px-1 py-0.5 rounded transition-colors truncate max-w-30
121-
${isLast ? "text-[#dde4f0] cursor-default" : "text-[#3d4d63] hover:text-accent hover:bg-accent/8 cursor-pointer"}
122-
`}
106+
{visibleSegments.map((seg, i) => {
107+
const originalIndex =
108+
pathSegments.length - visibleSegments.length + i;
109+
const relativeSegPath =
110+
"/" + pathSegments.slice(0, originalIndex + 1).join("/");
111+
const absoluteSegPath = buildAbsolutePath(relativeSegPath);
112+
const isLast = originalIndex === pathSegments.length - 1;
113+
114+
return (
115+
<span
116+
key={originalIndex}
117+
className="flex items-center gap-1 shrink-0 min-w-0"
123118
>
124-
{seg}
125-
</button>
126-
</span>
127-
);
128-
})}
129-
</div>
119+
<span className="text-[#1e2535] text-[11px]">/</span>
120+
<button
121+
onClick={() => !isLast && onNavigate(absoluteSegPath)}
122+
title={seg}
123+
className={`
124+
text-[11px] font-mono px-1 py-0.5 rounded transition-colors truncate max-w-30
125+
${isLast ? "text-[#dde4f0] cursor-default" : "text-[#3d4d63] hover:text-accent hover:bg-accent/8 cursor-pointer"}
126+
`}
127+
>
128+
{seg}
129+
</button>
130+
</span>
131+
);
132+
})}
133+
</div>
134+
)}
130135

131136
{/* ── Actions ── */}
132137
<div className="flex items-center gap-1.5 shrink-0">
@@ -144,55 +149,19 @@ export function BrowserToolbar({
144149
disabled={disabled}
145150
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-[12px] font-semibold text-[#a78bfa] bg-[#a78bfa]/8 border border-[#a78bfa]/25 hover:bg-[#a78bfa]/15 hover:border-[#a78bfa]/40 disabled:opacity-40 transition-all"
146151
>
147-
<ClipboardPaste size={12} /> Share Clipboard
152+
<ClipboardPaste size={12} /> Share
148153
</button>
149154

150-
<button
151-
onClick={onUploadFiles}
152-
disabled={disabled}
153-
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-[12px] font-semibold text-accent bg-accent/8 border border-accent/25 hover:bg-accent/15 hover:border-accent/40 disabled:opacity-40 transition-all"
154-
>
155-
{uploading ? (
156-
<Loader2 size={12} className="animate-spin" />
157-
) : (
158-
<Upload size={12} />
159-
)}{" "}
160-
Files
161-
</button>
162-
163-
<div className="relative" onClick={(e) => e.stopPropagation()}>
155+
{/* ── Search Trigger Button ── */}
156+
{!isSearchActive && (
164157
<button
165-
onClick={() => setShowFolderMenu(!showFolderMenu)}
158+
onClick={() => setIsSearchActive(true)}
166159
disabled={disabled}
167160
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-[12px] font-semibold text-[#00c9a7] bg-[#00c9a7]/8 border border-[#00c9a7]/25 hover:bg-[#00c9a7]/15 hover:border-[#00c9a7]/40 disabled:opacity-40 transition-all"
168161
>
169-
<FolderUp size={12} /> Folder
162+
<Search size={12} /> Search
170163
</button>
171-
172-
{showFolderMenu && (
173-
<div className="absolute top-full right-0 mt-1.5 w-44 bg-panel border border-[#1e2535] rounded-xl shadow-2xl py-1.5 px-1.5 z-50 animate-in fade-in zoom-in-95 duration-100">
174-
<button
175-
onClick={() => {
176-
setShowFolderMenu(false);
177-
onUploadFolder();
178-
}}
179-
className="w-full rounded-lg flex items-center gap-2.5 px-3 py-2 text-[12px] font-medium text-[#dde4f0] hover:bg-[#00c9a7]/10 hover:text-[#00c9a7] transition-colors"
180-
>
181-
<FolderUp size={13} /> Upload Folder
182-
</button>
183-
<div className="h-px w-full bg-[#1e2535] my-1" />
184-
<button
185-
onClick={() => {
186-
setShowFolderMenu(false);
187-
onCreateFolderClick();
188-
}}
189-
className="w-full rounded-lg flex items-center gap-2.5 px-3 py-2 text-[12px] font-medium text-[#dde4f0] hover:bg-accent/10 hover:text-accent transition-colors"
190-
>
191-
<FolderPlus size={13} /> Create Folder
192-
</button>
193-
</div>
194-
)}
195-
</div>
164+
)}
196165
</div>
197166
</div>
198167
);

0 commit comments

Comments
 (0)