Skip to content

Files

Latest commit

Jun 19, 2025
67f75fa Β· Jun 19, 2025

History

History
268 lines (224 loc) Β· 8.71 KB

Filepicker-Improvements.md

File metadata and controls

268 lines (224 loc) Β· 8.71 KB

Filepicker Module Analysis and Improvement Recommendations

Executive Summary

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.

Current Architecture Overview

1. Third-Party Component (textual_fspicker)

  • Location: tldw_chatbook/Third_Party/textual_fspicker/
  • Components:
    • FileOpen: File opening dialog
    • FileSave: File saving dialog
    • DirectoryNavigation: Core navigation widget
    • DriveNavigation: Windows drive selection
    • Filters: File type filtering system

2. Custom Wrapper (file_picker_dialog.py)

  • Location: tldw_chatbook/Widgets/file_picker_dialog.py
  • Purpose: Evaluation-specific file picker dialogs
  • Classes:
    • EvalFilePickerDialog: Base modal dialog
    • TaskFilePickerDialog: Task file selection
    • DatasetFilePickerDialog: Dataset file selection
    • ExportFilePickerDialog: Result export
    • QuickPickerWidget: Inline file selection

Identified Issues and Improvements

πŸ† Easy Wins (Low effort, high impact)

1. Add Keyboard Shortcuts

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

2. Recent Files/Directories List

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

3. Improve Path Display

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

4. File Preview Panel

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

5. Search Within Directory

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

πŸ”¨ Medium Improvements (Moderate effort, good impact)

6. Multi-file Selection

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

7. Bookmarks/Favorites System

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

8. Performance: Lazy Loading

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

πŸš€ Major Improvements (Higher effort, transformative impact)

9. Unified Filepicker Architecture

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

10. Drag and Drop Support

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

Implementation Priority Matrix

Phase 1: Quick Wins (1-2 weeks)

  1. βœ… Keyboard shortcuts
  2. βœ… Recent files list
  3. βœ… Search within directory
  4. βœ… Improve path display

Phase 2: Enhanced UX (2-3 weeks)

  1. βœ… File preview panel
  2. βœ… Bookmarks system
  3. βœ… Basic multi-selection

Phase 3: Performance & Architecture (4-6 weeks)

  1. βœ… Lazy loading implementation
  2. βœ… Unified architecture refactor
  3. βœ… Drag and drop support

Code Quality Improvements

Type Hints Enhancement

# 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)

Error Handling Improvements

# 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"""

Consistent Event System

# Define standard events
class FilePickerEvents:
    class FileSelected(Message):
        path: Path
        
    class DirectoryChanged(Message):
        path: Path
        
    class FilterChanged(Message):
        filter: Filter

Testing Recommendations

  1. Unit Tests: Test each component in isolation
  2. Integration Tests: Test dialog workflows
  3. Performance Tests: Benchmark large directory handling
  4. Accessibility Tests: Ensure keyboard navigation works

Migration Strategy

  1. Backward Compatibility: Keep existing APIs working
  2. Feature Flags: Roll out new features gradually
  3. Documentation: Update examples and guides
  4. Deprecation Path: Clear timeline for old API removal

Conclusion

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.

Next Steps

  1. Review and prioritize recommendations with team
  2. Create detailed implementation tickets
  3. Begin with Phase 1 easy wins
  4. Gather user feedback after each phase
  5. Iterate based on real-world usage patterns