Skip to content

Commit 9ece301

Browse files
committed
feat(desktop): add remove-test-files selection action with trash icon
1 parent 0e3f482 commit 9ece301

3 files changed

Lines changed: 70 additions & 1 deletion

File tree

apps/desktop/src/App.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
22
import './App.css'
3-
import { ChevronsDown, ChevronsUp, CheckSquare, Square, Sun, Moon, Folder, FolderGit2, ListChecks, Copy, ArrowLeftRight } from 'lucide-react'
3+
import { ChevronsDown, ChevronsUp, CheckSquare, Square, Sun, Moon, Folder, FolderGit2, ListChecks, Copy, ArrowLeftRight, Trash2 } from 'lucide-react'
44
import { FileTreeView, PreviewModal, GitHubStarIconButton, BugIconButton } from '@gitcontext/ui'
55
import { type FileDiffStatus, isBinaryPath, MAX_CONCURRENT_READS } from '@gitcontext/core'
66
import { readText, writeText } from '@tauri-apps/plugin-clipboard-manager'
@@ -159,6 +159,7 @@ function AppContent() {
159159
selectAll,
160160
deselectAll,
161161
addSelectedPaths,
162+
removeSelectedPathsByPredicate,
162163
revealPath,
163164
} = useFileTree(setAppStatus)
164165

@@ -394,6 +395,10 @@ function AppContent() {
394395
}
395396
}, [currentDir, fileTree, statusByPath, addSelectedPaths])
396397

398+
const handleRemoveTestPathsFromSelection = useCallback(() => {
399+
removeSelectedPathsByPredicate((path) => path.toLowerCase().includes('test'))
400+
}, [removeSelectedPathsByPredicate])
401+
397402
// Calculate file tree tokens
398403
useEffect(() => {
399404
if (!includeFileTree || !fileTree || selectedPaths.size === 0) {
@@ -578,6 +583,7 @@ function AppContent() {
578583
<button onClick={() => selectAll(treeFilter)} className="btn btn-ghost btn-icon" title="Select All" disabled={!fileTree}><CheckSquare size={14} /></button>
579584
<button onClick={() => deselectAll(treeFilter)} className="btn btn-ghost btn-icon" title="Deselect All" disabled={!fileTree}><Square size={14} /></button>
580585
<button onClick={() => void handleBatchSelectFromClipboard()} className="btn btn-ghost btn-icon" title="Batch Select from Clipboard" disabled={!fileTree || !currentDir}><ListChecks size={14} /></button>
586+
<button onClick={handleRemoveTestPathsFromSelection} className="btn btn-ghost btn-icon" title="Remove selected test files" disabled={!fileTree || selectedPaths.size === 0}><Trash2 size={14} /></button>
581587
</div>
582588
<label className="tree-filter-checkbox">
583589
<input type="checkbox" checked={showChangedOnly} onChange={(e) => setShowChangedOnly(e.target.checked)} />
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { describe, it, expect } from 'vitest'
2+
import { act, renderHook } from '@testing-library/react'
3+
import { useFileTree } from './useFileTree'
4+
5+
describe('useFileTree selection helpers', () => {
6+
it('addSelectedPaths keeps existing and adds new unique paths', () => {
7+
const { result } = renderHook(() => useFileTree())
8+
9+
act(() => {
10+
result.current.toggleSelect('src/app.ts')
11+
result.current.addSelectedPaths(['src/test/a.test.ts', 'src/app.ts'])
12+
})
13+
14+
expect(Array.from(result.current.selectedPaths).sort()).toEqual([
15+
'src/app.ts',
16+
'src/test/a.test.ts',
17+
])
18+
})
19+
20+
it('removeSelectedPathsByPredicate removes only matching test paths (case-insensitive)', () => {
21+
const { result } = renderHook(() => useFileTree())
22+
23+
act(() => {
24+
result.current.addSelectedPaths([
25+
'src/app.ts',
26+
'src/test/foo.ts',
27+
'unit/MyTest.spec.ts',
28+
'docs/guide.md',
29+
])
30+
result.current.removeSelectedPathsByPredicate((p) => p.toLowerCase().includes('test'))
31+
})
32+
33+
expect(Array.from(result.current.selectedPaths).sort()).toEqual([
34+
'docs/guide.md',
35+
'src/app.ts',
36+
])
37+
})
38+
39+
it('removeSelectedPathsByPredicate is a no-op when nothing matches', () => {
40+
const { result } = renderHook(() => useFileTree())
41+
42+
act(() => {
43+
result.current.addSelectedPaths(['src/app.ts', 'docs/guide.md'])
44+
result.current.removeSelectedPathsByPredicate((p) => p.toLowerCase().includes('test'))
45+
})
46+
47+
expect(Array.from(result.current.selectedPaths).sort()).toEqual([
48+
'docs/guide.md',
49+
'src/app.ts',
50+
])
51+
})
52+
})

apps/desktop/src/hooks/useFileTree.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,16 @@ export function useFileTree(setAppStatus?: (s: AppStatus) => void) {
290290
})
291291
}, [])
292292

293+
const removeSelectedPathsByPredicate = useCallback((predicate: (path: string) => boolean) => {
294+
setSelectedPaths((prev) => {
295+
const next = new Set(prev)
296+
for (const path of prev) {
297+
if (predicate(path)) next.delete(path)
298+
}
299+
return next
300+
})
301+
}, [])
302+
293303
const revealPath = useCallback((path: string) => {
294304
if (!fileTree) return
295305

@@ -349,6 +359,7 @@ export function useFileTree(setAppStatus?: (s: AppStatus) => void) {
349359
selectAll,
350360
deselectAll,
351361
addSelectedPaths,
362+
removeSelectedPathsByPredicate,
352363
revealPath,
353364
}
354365
}

0 commit comments

Comments
 (0)