@@ -2,11 +2,10 @@ import React from "react";
22import { cn } from "@/common/lib/utils" ;
33import type { FrontendWorkspaceMetadata } from "@/common/types/workspace" ;
44import { useWorkspaceContext } from "@/browser/contexts/WorkspaceContext" ;
5- import { Trash2 , ChevronDown , ChevronRight , Search } from "lucide-react" ;
5+ import { Trash2 , Search } from "lucide-react" ;
66import { ArchiveIcon , ArchiveRestoreIcon } from "./icons/ArchiveIcon" ;
77import { Tooltip , TooltipTrigger , TooltipContent } from "./ui/tooltip" ;
88import { RuntimeBadge } from "./RuntimeBadge" ;
9- import { usePersistedState } from "@/browser/hooks/usePersistedState" ;
109
1110interface ArchivedWorkspacesProps {
1211 projectPath : string ;
@@ -70,7 +69,6 @@ export const ArchivedWorkspaces: React.FC<ArchivedWorkspacesProps> = ({
7069 onWorkspacesChanged,
7170} ) => {
7271 const { unarchiveWorkspace, removeWorkspace, setSelectedWorkspace } = useWorkspaceContext ( ) ;
73- const [ expanded , setExpanded ] = usePersistedState ( "archivedWorkspacesExpanded" , true ) ;
7472 const [ searchQuery , setSearchQuery ] = React . useState ( "" ) ;
7573 const [ processingIds , setProcessingIds ] = React . useState < Set < string > > ( new Set ( ) ) ;
7674 const [ deleteConfirmId , setDeleteConfirmId ] = React . useState < string | null > ( null ) ;
@@ -136,140 +134,131 @@ export const ArchivedWorkspaces: React.FC<ArchivedWorkspacesProps> = ({
136134
137135 return (
138136 < div className = "border-border rounded-lg border" >
139- < button
140- onClick = { ( ) => setExpanded ( ( prev ) => ! prev ) }
141- className = "hover:bg-hover flex w-full items-center gap-2 rounded-t-lg px-4 py-3 text-left transition-colors"
142- >
137+ { /* Header - not collapsible */ }
138+ < div className = "flex items-center gap-2 px-4 py-3" >
143139 < ArchiveIcon className = "text-muted h-4 w-4" />
144140 < span className = "text-foreground flex-1 font-medium" >
145141 Archived Workspaces ({ workspaces . length } )
146142 </ span >
147- { expanded ? (
148- < ChevronDown className = "text-muted h-4 w-4" />
149- ) : (
150- < ChevronRight className = "text-muted h-4 w-4" />
151- ) }
152- </ button >
143+ </ div >
153144
154- { expanded && (
155- < div className = "border-border border-t" >
156- { /* Search input */ }
157- { workspaces . length > 3 && (
158- < div className = "border-border border-b px-4 py-2" >
159- < div className = "relative" >
160- < Search className = "text-muted pointer-events-none absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" />
161- < input
162- type = "text"
163- placeholder = "Search archived workspaces..."
164- value = { searchQuery }
165- onChange = { ( e ) => setSearchQuery ( e . target . value ) }
166- className = "bg-bg-dark placeholder:text-muted text-foreground w-full rounded border border-transparent py-1.5 pl-8 pr-3 text-sm focus:border-border-light focus:outline-none"
167- />
168- </ div >
145+ < div className = "border-border border-t" >
146+ { /* Search input */ }
147+ { workspaces . length > 3 && (
148+ < div className = "border-border border-b px-4 py-2" >
149+ < div className = "relative" >
150+ < Search className = "text-muted pointer-events-none absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" />
151+ < input
152+ type = "text"
153+ placeholder = "Search archived workspaces..."
154+ value = { searchQuery }
155+ onChange = { ( e ) => setSearchQuery ( e . target . value ) }
156+ className = "bg-bg-dark placeholder:text-muted text-foreground w-full rounded border border-transparent py-1.5 pl-8 pr-3 text-sm focus:border-border-light focus:outline-none"
157+ />
169158 </ div >
170- ) }
159+ </ div >
160+ ) }
171161
172- { /* Timeline grouped list - no inner scroll, parent handles overflow */ }
173- < div >
174- { filteredWorkspaces . length === 0 ? (
175- < div className = "text-muted px-4 py-6 text-center text-sm" >
176- No workspaces match "{ searchQuery } "
177- </ div >
178- ) : (
179- Array . from ( groupedWorkspaces . entries ( ) ) . map ( ( [ period , periodWorkspaces ] ) => (
180- < div key = { period } >
181- { /* Period header */ }
182- < div className = "bg-bg-dark text-muted px-4 py-1.5 text-xs font-medium" >
183- { period }
184- </ div >
185- { /* Workspaces in this period */ }
186- { periodWorkspaces . map ( ( workspace ) => {
187- const isProcessing = processingIds . has ( workspace . id ) ;
188- const isDeleting = deleteConfirmId === workspace . id ;
189- const displayTitle = workspace . title ?? workspace . name ;
162+ { /* Timeline grouped list */ }
163+ < div >
164+ { filteredWorkspaces . length === 0 ? (
165+ < div className = "text-muted px-4 py-6 text-center text-sm" >
166+ No workspaces match "{ searchQuery } "
167+ </ div >
168+ ) : (
169+ Array . from ( groupedWorkspaces . entries ( ) ) . map ( ( [ period , periodWorkspaces ] ) => (
170+ < div key = { period } >
171+ { /* Period header */ }
172+ < div className = "bg-bg-dark text-muted px-4 py-1.5 text-xs font-medium" >
173+ { period }
174+ </ div >
175+ { /* Workspaces in this period */ }
176+ { periodWorkspaces . map ( ( workspace ) => {
177+ const isProcessing = processingIds . has ( workspace . id ) ;
178+ const isDeleting = deleteConfirmId === workspace . id ;
179+ const displayTitle = workspace . title ?? workspace . name ;
190180
191- return (
192- < div
193- key = { workspace . id }
194- className = { cn (
195- "border-border flex items-center gap-3 border-b px-4 py-2.5 last:border-b-0" ,
196- isProcessing && "opacity-50"
197- ) }
198- >
199- < RuntimeBadge runtimeConfig = { workspace . runtimeConfig } isWorking = { false } />
200- < div className = "min-w-0 flex-1" >
201- < div className = "text-foreground truncate text-sm font-medium" >
202- { displayTitle }
203- </ div >
204- { workspace . archivedAt && (
205- < div className = "text-muted text-xs" >
206- { new Date ( workspace . archivedAt ) . toLocaleString ( undefined , {
207- month : "short" ,
208- day : "numeric" ,
209- hour : "numeric" ,
210- minute : "2-digit" ,
211- } ) }
212- </ div >
213- ) }
181+ return (
182+ < div
183+ key = { workspace . id }
184+ className = { cn (
185+ "border-border flex items-center gap-3 border-b px-4 py-2.5 last:border-b-0" ,
186+ isProcessing && "opacity-50"
187+ ) }
188+ >
189+ < RuntimeBadge runtimeConfig = { workspace . runtimeConfig } isWorking = { false } />
190+ < div className = "min-w-0 flex-1" >
191+ < div className = "text-foreground truncate text-sm font-medium" >
192+ { displayTitle }
214193 </ div >
215-
216- { isDeleting ? (
217- < div className = "flex items-center gap-2" >
218- < span className = "text-muted text-xs" > Delete?</ span >
219- < button
220- onClick = { ( ) => void handleDelete ( workspace . id ) }
221- disabled = { isProcessing }
222- className = "rounded bg-red-600 px-2 py-1 text-xs text-white hover:bg-red-700 disabled:opacity-50"
223- >
224- Yes
225- </ button >
226- < button
227- onClick = { ( ) => setDeleteConfirmId ( null ) }
228- disabled = { isProcessing }
229- className = "text-muted hover:text-foreground text-xs disabled:opacity-50"
230- >
231- No
232- </ button >
233- </ div >
234- ) : (
235- < div className = "flex items-center gap-1" >
236- < Tooltip >
237- < TooltipTrigger asChild >
238- < button
239- onClick = { ( ) => void handleUnarchive ( workspace . id ) }
240- disabled = { isProcessing }
241- className = "text-muted hover:text-foreground rounded p-1.5 transition-colors hover:bg-white/10 disabled:opacity-50"
242- aria-label = { `Restore workspace ${ displayTitle } ` }
243- >
244- < ArchiveRestoreIcon className = "h-4 w-4" />
245- </ button >
246- </ TooltipTrigger >
247- < TooltipContent > Restore to sidebar</ TooltipContent >
248- </ Tooltip >
249- < Tooltip >
250- < TooltipTrigger asChild >
251- < button
252- onClick = { ( ) => setDeleteConfirmId ( workspace . id ) }
253- disabled = { isProcessing }
254- className = "text-muted rounded p-1.5 transition-colors hover:bg-white/10 hover:text-red-400 disabled:opacity-50"
255- aria-label = { `Delete workspace ${ displayTitle } ` }
256- >
257- < Trash2 className = "h-4 w-4" />
258- </ button >
259- </ TooltipTrigger >
260- < TooltipContent > Delete permanently</ TooltipContent >
261- </ Tooltip >
194+ { workspace . archivedAt && (
195+ < div className = "text-muted text-xs" >
196+ { new Date ( workspace . archivedAt ) . toLocaleString ( undefined , {
197+ month : "short" ,
198+ day : "numeric" ,
199+ hour : "numeric" ,
200+ minute : "2-digit" ,
201+ } ) }
262202 </ div >
263203 ) }
264204 </ div >
265- ) ;
266- } ) }
267- </ div >
268- ) )
269- ) }
270- </ div >
205+
206+ { isDeleting ? (
207+ < div className = "flex items-center gap-2" >
208+ < span className = "text-muted text-xs" > Delete?</ span >
209+ < button
210+ onClick = { ( ) => void handleDelete ( workspace . id ) }
211+ disabled = { isProcessing }
212+ className = "rounded bg-red-600 px-2 py-1 text-xs text-white hover:bg-red-700 disabled:opacity-50"
213+ >
214+ Yes
215+ </ button >
216+ < button
217+ onClick = { ( ) => setDeleteConfirmId ( null ) }
218+ disabled = { isProcessing }
219+ className = "text-muted hover:text-foreground text-xs disabled:opacity-50"
220+ >
221+ No
222+ </ button >
223+ </ div >
224+ ) : (
225+ < div className = "flex items-center gap-1" >
226+ < Tooltip >
227+ < TooltipTrigger asChild >
228+ < button
229+ onClick = { ( ) => void handleUnarchive ( workspace . id ) }
230+ disabled = { isProcessing }
231+ className = "text-muted hover:text-foreground rounded p-1.5 transition-colors hover:bg-white/10 disabled:opacity-50"
232+ aria-label = { `Restore workspace ${ displayTitle } ` }
233+ >
234+ < ArchiveRestoreIcon className = "h-4 w-4" />
235+ </ button >
236+ </ TooltipTrigger >
237+ < TooltipContent > Restore to sidebar</ TooltipContent >
238+ </ Tooltip >
239+ < Tooltip >
240+ < TooltipTrigger asChild >
241+ < button
242+ onClick = { ( ) => setDeleteConfirmId ( workspace . id ) }
243+ disabled = { isProcessing }
244+ className = "text-muted rounded p-1.5 transition-colors hover:bg-white/10 hover:text-red-400 disabled:opacity-50"
245+ aria-label = { `Delete workspace ${ displayTitle } ` }
246+ >
247+ < Trash2 className = "h-4 w-4" />
248+ </ button >
249+ </ TooltipTrigger >
250+ < TooltipContent > Delete permanently</ TooltipContent >
251+ </ Tooltip >
252+ </ div >
253+ ) }
254+ </ div >
255+ ) ;
256+ } ) }
257+ </ div >
258+ ) )
259+ ) }
271260 </ div >
272- ) }
261+ </ div >
273262 </ div >
274263 ) ;
275264} ;
0 commit comments