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.
- Location:
tldw_chatbook/Third_Party/textual_fspicker/
- Components:
FileOpen
: File opening dialogFileSave
: File saving dialogDirectoryNavigation
: Core navigation widgetDriveNavigation
: Windows drive selectionFilters
: File type filtering system
- Location:
tldw_chatbook/Widgets/file_picker_dialog.py
- Purpose: Evaluation-specific file picker dialogs
- Classes:
EvalFilePickerDialog
: Base modal dialogTaskFilePickerDialog
: Task file selectionDatasetFilePickerDialog
: Dataset file selectionExportFilePickerDialog
: Result exportQuickPickerWidget
: Inline file selection
Issue: No keyboard shortcuts for common actions Solution: Add bindings for:
BINDINGS = [
Binding("ctrl+h", "toggle_hidden", "Toggle hidden files"),
Binding("ctrl+l", "focus_path_input", "Edit path directly"),
Binding("ctrl+d", "bookmark_current", "Bookmark directory"),
Binding("f5", "refresh", "Refresh directory"),
Binding("tab", "toggle_focus", "Switch focus"),
]
Effort: 2-3 hours Impact: Significant UX improvement
Issue: Users must navigate from scratch each time Solution: Add a recent locations dropdown
class RecentLocations:
def __init__(self, max_items: int = 10):
self.recent: List[Path] = []
self.load_from_config()
def add(self, path: Path):
if path in self.recent:
self.recent.remove(path)
self.recent.insert(0, path)
self.recent = self.recent[:self.max_items]
self.save_to_config()
Effort: 3-4 hours Impact: Major time-saver for users
Issue: Current path display can be unclear/truncated Solution: Add breadcrumb navigation
class PathBreadcrumbs(Horizontal):
"""Clickable breadcrumb path navigation"""
def render_path(self, path: Path):
parts = path.parts
for i, part in enumerate(parts):
partial_path = Path(*parts[:i+1])
yield Button(part, classes="breadcrumb-button")
if i < len(parts) - 1:
yield Label("/", classes="breadcrumb-separator")
Effort: 4-5 hours Impact: Better navigation clarity
Issue: No way to preview file contents Solution: Add optional preview pane for text/image files
class FilePreview(Container):
def update_preview(self, file_path: Path):
if file_path.suffix in ['.txt', '.json', '.yaml', '.md']:
self.show_text_preview(file_path)
elif file_path.suffix in ['.png', '.jpg', '.webp']:
self.show_image_preview(file_path)
Effort: 4-5 hours Impact: Reduces wrong file selections
Issue: No search functionality for large directories Solution: Add search input with real-time filtering
@on(Input.Changed, "#search-input")
def filter_entries(self, event: Input.Changed):
search_term = event.value.lower()
for entry in self.directory_entries:
entry.visible = search_term in entry.name.lower()
Effort: 2-3 hours Impact: Much faster file finding
Issue: Can only select one file at a time Solution: Add checkbox mode for batch operations
class MultiSelectFileOpen(FileOpen):
selected_files: reactive[Set[Path]] = reactive(set())
def toggle_selection(self, path: Path):
if path in self.selected_files:
self.selected_files.discard(path)
else:
self.selected_files.add(path)
Effort: 8-10 hours Impact: Enables batch processing workflows
Issue: No way to save frequently used locations Solution: Add persistent bookmarks sidebar
class BookmarksPanel(Container):
def compose(self) -> ComposeResult:
yield ListView(id="bookmarks-list")
yield Button("+ Add Current", id="add-bookmark")
def load_bookmarks(self) -> List[Bookmark]:
return self.app.config.get("filepicker.bookmarks", [])
Effort: 6-8 hours Impact: Significant workflow improvement
Issue: Large directories load slowly Solution: Implement virtual scrolling with batch loading
class LazyDirectoryNavigation(DirectoryNavigation):
BATCH_SIZE = 100
async def load_entries_batch(self, start: int, end: int):
# Load only visible entries + buffer
entries = self.all_entries[start:end]
await self.update_display(entries)
Effort: 10-12 hours Impact: Much better performance for large dirs
Issue: Dual implementation creates maintenance burden Solution: Create single, extensible filepicker system
class UnifiedFilePicker:
"""Single filepicker with plugin system for different contexts"""
def register_filter_preset(self, name: str, filters: Filters):
self.filter_presets[name] = filters
def register_validator(self, name: str, validator: Callable):
self.validators[name] = validator
Effort: 20-30 hours Impact: Cleaner codebase, easier to extend
Issue: No modern drag/drop functionality Solution: Implement drop zones for file selection
class DropZoneFilePicker(FilePicker):
def on_drop(self, event: DropEvent):
for file_path in event.files:
self.add_selected_file(file_path)
Effort: 15-20 hours Impact: Modern UX expected by users
- β Keyboard shortcuts
- β Recent files list
- β Search within directory
- β Improve path display
- β File preview panel
- β Bookmarks system
- β Basic multi-selection
- β Lazy loading implementation
- β Unified architecture refactor
- β Drag and drop support
# Before
def handle_file_selected(self, event):
self.selected_file = str(event.path)
# After
def handle_file_selected(self, event: FileOpen.FileSelected) -> None:
self.selected_file: str = str(event.path)
# Add specific exception types
class FilePickerError(Exception):
"""Base exception for filepicker errors"""
class InvalidPathError(FilePickerError):
"""Raised when path is invalid or inaccessible"""
class FilterError(FilePickerError):
"""Raised when file filter pattern is invalid"""
# Define standard events
class FilePickerEvents:
class FileSelected(Message):
path: Path
class DirectoryChanged(Message):
path: Path
class FilterChanged(Message):
filter: Filter
- Unit Tests: Test each component in isolation
- Integration Tests: Test dialog workflows
- Performance Tests: Benchmark large directory handling
- Accessibility Tests: Ensure keyboard navigation works
- Backward Compatibility: Keep existing APIs working
- Feature Flags: Roll out new features gradually
- Documentation: Update examples and guides
- Deprecation Path: Clear timeline for old API removal
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.
- Review and prioritize recommendations with team
- Create detailed implementation tickets
- Begin with Phase 1 easy wins
- Gather user feedback after each phase
- Iterate based on real-world usage patterns