Skip to content

Commit 6f1c7c2

Browse files
authored
Filepicker improvements
See MD file in filepicker module folder.
2 parents 75d4658 + 15cb14b commit 6f1c7c2

File tree

8 files changed

+3508
-17
lines changed

8 files changed

+3508
-17
lines changed

Filepicker-Improvements.md

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
# Filepicker Module Analysis and Improvement Recommendations
2+
3+
## Executive Summary
4+
5+
This document analyzes the filepicker implementation in tldw_chatbook and provides actionable recommendations for improvements. The analysis covers the current dual-implementation architecture (third-party `textual_fspicker` and custom `file_picker_dialog.py` wrapper), identifies pain points, and suggests both easy wins and more comprehensive improvements.
6+
7+
## Current Architecture Overview
8+
9+
### 1. Third-Party Component (`textual_fspicker`)
10+
- **Location**: `tldw_chatbook/Third_Party/textual_fspicker/`
11+
- **Components**:
12+
- `FileOpen`: File opening dialog
13+
- `FileSave`: File saving dialog
14+
- `DirectoryNavigation`: Core navigation widget
15+
- `DriveNavigation`: Windows drive selection
16+
- `Filters`: File type filtering system
17+
18+
### 2. Custom Wrapper (`file_picker_dialog.py`)
19+
- **Location**: `tldw_chatbook/Widgets/file_picker_dialog.py`
20+
- **Purpose**: Evaluation-specific file picker dialogs
21+
- **Classes**:
22+
- `EvalFilePickerDialog`: Base modal dialog
23+
- `TaskFilePickerDialog`: Task file selection
24+
- `DatasetFilePickerDialog`: Dataset file selection
25+
- `ExportFilePickerDialog`: Result export
26+
- `QuickPickerWidget`: Inline file selection
27+
28+
## Identified Issues and Improvements
29+
30+
### 🏆 Easy Wins (Low effort, high impact)
31+
32+
#### 1. **Add Keyboard Shortcuts**
33+
**Issue**: No keyboard shortcuts for common actions
34+
**Solution**: Add bindings for:
35+
```python
36+
BINDINGS = [
37+
Binding("ctrl+h", "toggle_hidden", "Toggle hidden files"),
38+
Binding("ctrl+l", "focus_path_input", "Edit path directly"),
39+
Binding("ctrl+d", "bookmark_current", "Bookmark directory"),
40+
Binding("f5", "refresh", "Refresh directory"),
41+
Binding("tab", "toggle_focus", "Switch focus"),
42+
]
43+
```
44+
**Effort**: 2-3 hours
45+
**Impact**: Significant UX improvement
46+
47+
#### 2. **Recent Files/Directories List**
48+
**Issue**: Users must navigate from scratch each time
49+
**Solution**: Add a recent locations dropdown
50+
```python
51+
class RecentLocations:
52+
def __init__(self, max_items: int = 10):
53+
self.recent: List[Path] = []
54+
self.load_from_config()
55+
56+
def add(self, path: Path):
57+
if path in self.recent:
58+
self.recent.remove(path)
59+
self.recent.insert(0, path)
60+
self.recent = self.recent[:self.max_items]
61+
self.save_to_config()
62+
```
63+
**Effort**: 3-4 hours
64+
**Impact**: Major time-saver for users
65+
66+
#### 3. **Improve Path Display**
67+
**Issue**: Current path display can be unclear/truncated
68+
**Solution**: Add breadcrumb navigation
69+
```python
70+
class PathBreadcrumbs(Horizontal):
71+
"""Clickable breadcrumb path navigation"""
72+
def render_path(self, path: Path):
73+
parts = path.parts
74+
for i, part in enumerate(parts):
75+
partial_path = Path(*parts[:i+1])
76+
yield Button(part, classes="breadcrumb-button")
77+
if i < len(parts) - 1:
78+
yield Label("/", classes="breadcrumb-separator")
79+
```
80+
**Effort**: 4-5 hours
81+
**Impact**: Better navigation clarity
82+
83+
#### 4. **File Preview Panel**
84+
**Issue**: No way to preview file contents
85+
**Solution**: Add optional preview pane for text/image files
86+
```python
87+
class FilePreview(Container):
88+
def update_preview(self, file_path: Path):
89+
if file_path.suffix in ['.txt', '.json', '.yaml', '.md']:
90+
self.show_text_preview(file_path)
91+
elif file_path.suffix in ['.png', '.jpg', '.webp']:
92+
self.show_image_preview(file_path)
93+
```
94+
**Effort**: 4-5 hours
95+
**Impact**: Reduces wrong file selections
96+
97+
#### 5. **Search Within Directory**
98+
**Issue**: No search functionality for large directories
99+
**Solution**: Add search input with real-time filtering
100+
```python
101+
@on(Input.Changed, "#search-input")
102+
def filter_entries(self, event: Input.Changed):
103+
search_term = event.value.lower()
104+
for entry in self.directory_entries:
105+
entry.visible = search_term in entry.name.lower()
106+
```
107+
**Effort**: 2-3 hours
108+
**Impact**: Much faster file finding
109+
110+
### 🔨 Medium Improvements (Moderate effort, good impact)
111+
112+
#### 6. **Multi-file Selection**
113+
**Issue**: Can only select one file at a time
114+
**Solution**: Add checkbox mode for batch operations
115+
```python
116+
class MultiSelectFileOpen(FileOpen):
117+
selected_files: reactive[Set[Path]] = reactive(set())
118+
119+
def toggle_selection(self, path: Path):
120+
if path in self.selected_files:
121+
self.selected_files.discard(path)
122+
else:
123+
self.selected_files.add(path)
124+
```
125+
**Effort**: 8-10 hours
126+
**Impact**: Enables batch processing workflows
127+
128+
#### 7. **Bookmarks/Favorites System**
129+
**Issue**: No way to save frequently used locations
130+
**Solution**: Add persistent bookmarks sidebar
131+
```python
132+
class BookmarksPanel(Container):
133+
def compose(self) -> ComposeResult:
134+
yield ListView(id="bookmarks-list")
135+
yield Button("+ Add Current", id="add-bookmark")
136+
137+
def load_bookmarks(self) -> List[Bookmark]:
138+
return self.app.config.get("filepicker.bookmarks", [])
139+
```
140+
**Effort**: 6-8 hours
141+
**Impact**: Significant workflow improvement
142+
143+
#### 8. **Performance: Lazy Loading**
144+
**Issue**: Large directories load slowly
145+
**Solution**: Implement virtual scrolling with batch loading
146+
```python
147+
class LazyDirectoryNavigation(DirectoryNavigation):
148+
BATCH_SIZE = 100
149+
150+
async def load_entries_batch(self, start: int, end: int):
151+
# Load only visible entries + buffer
152+
entries = self.all_entries[start:end]
153+
await self.update_display(entries)
154+
```
155+
**Effort**: 10-12 hours
156+
**Impact**: Much better performance for large dirs
157+
158+
### 🚀 Major Improvements (Higher effort, transformative impact)
159+
160+
#### 9. **Unified Filepicker Architecture**
161+
**Issue**: Dual implementation creates maintenance burden
162+
**Solution**: Create single, extensible filepicker system
163+
```python
164+
class UnifiedFilePicker:
165+
"""Single filepicker with plugin system for different contexts"""
166+
def register_filter_preset(self, name: str, filters: Filters):
167+
self.filter_presets[name] = filters
168+
169+
def register_validator(self, name: str, validator: Callable):
170+
self.validators[name] = validator
171+
```
172+
**Effort**: 20-30 hours
173+
**Impact**: Cleaner codebase, easier to extend
174+
175+
#### 10. **Drag and Drop Support**
176+
**Issue**: No modern drag/drop functionality
177+
**Solution**: Implement drop zones for file selection
178+
```python
179+
class DropZoneFilePicker(FilePicker):
180+
def on_drop(self, event: DropEvent):
181+
for file_path in event.files:
182+
self.add_selected_file(file_path)
183+
```
184+
**Effort**: 15-20 hours
185+
**Impact**: Modern UX expected by users
186+
187+
## Implementation Priority Matrix
188+
189+
### Phase 1: Quick Wins (1-2 weeks)
190+
1. ✅ Keyboard shortcuts
191+
2. ✅ Recent files list
192+
3. ✅ Search within directory
193+
4. ✅ Improve path display
194+
195+
### Phase 2: Enhanced UX (2-3 weeks)
196+
5. ✅ File preview panel
197+
6. ✅ Bookmarks system
198+
7. ✅ Basic multi-selection
199+
200+
### Phase 3: Performance & Architecture (4-6 weeks)
201+
8. ✅ Lazy loading implementation
202+
9. ✅ Unified architecture refactor
203+
10. ✅ Drag and drop support
204+
205+
## Code Quality Improvements
206+
207+
### Type Hints Enhancement
208+
```python
209+
# Before
210+
def handle_file_selected(self, event):
211+
self.selected_file = str(event.path)
212+
213+
# After
214+
def handle_file_selected(self, event: FileOpen.FileSelected) -> None:
215+
self.selected_file: str = str(event.path)
216+
```
217+
218+
### Error Handling Improvements
219+
```python
220+
# Add specific exception types
221+
class FilePickerError(Exception):
222+
"""Base exception for filepicker errors"""
223+
224+
class InvalidPathError(FilePickerError):
225+
"""Raised when path is invalid or inaccessible"""
226+
227+
class FilterError(FilePickerError):
228+
"""Raised when file filter pattern is invalid"""
229+
```
230+
231+
### Consistent Event System
232+
```python
233+
# Define standard events
234+
class FilePickerEvents:
235+
class FileSelected(Message):
236+
path: Path
237+
238+
class DirectoryChanged(Message):
239+
path: Path
240+
241+
class FilterChanged(Message):
242+
filter: Filter
243+
```
244+
245+
## Testing Recommendations
246+
247+
1. **Unit Tests**: Test each component in isolation
248+
2. **Integration Tests**: Test dialog workflows
249+
3. **Performance Tests**: Benchmark large directory handling
250+
4. **Accessibility Tests**: Ensure keyboard navigation works
251+
252+
## Migration Strategy
253+
254+
1. **Backward Compatibility**: Keep existing APIs working
255+
2. **Feature Flags**: Roll out new features gradually
256+
3. **Documentation**: Update examples and guides
257+
4. **Deprecation Path**: Clear timeline for old API removal
258+
259+
## Conclusion
260+
261+
The filepicker module has significant room for improvement. Starting with the easy wins will provide immediate user benefits while building toward a more comprehensive overhaul. The recommended phased approach balances quick improvements with long-term architectural benefits.
262+
263+
### Next Steps
264+
1. Review and prioritize recommendations with team
265+
2. Create detailed implementation tickets
266+
3. Begin with Phase 1 easy wins
267+
4. Gather user feedback after each phase
268+
5. Iterate based on real-world usage patterns

0 commit comments

Comments
 (0)