From 9da6d0f90a332c007313451ec6adf0ee8fcda107 Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Tue, 26 May 2026 23:06:36 -0700 Subject: [PATCH] Split out panels.py into multiple separate source files. --- borgmatic/actions/browse/app.py | 14 +- borgmatic/actions/browse/archives_list.py | 36 ++++ borgmatic/actions/browse/bindings.py | 23 +++ borgmatic/actions/browse/carousel.py | 26 +-- .../browse/configuration_files_list.py | 28 +++ .../browse/{panels.py => directory_list.py} | 161 ++---------------- borgmatic/actions/browse/file_preview.py | 57 +++++++ borgmatic/actions/browse/loading.py | 1 + borgmatic/actions/browse/logs.py | 7 + borgmatic/actions/browse/repositories_list.py | 28 +++ 10 files changed, 220 insertions(+), 161 deletions(-) create mode 100644 borgmatic/actions/browse/archives_list.py create mode 100644 borgmatic/actions/browse/bindings.py create mode 100644 borgmatic/actions/browse/configuration_files_list.py rename borgmatic/actions/browse/{panels.py => directory_list.py} (51%) create mode 100644 borgmatic/actions/browse/file_preview.py create mode 100644 borgmatic/actions/browse/repositories_list.py diff --git a/borgmatic/actions/browse/app.py b/borgmatic/actions/browse/app.py index 70a2e4ee..fd270ba4 100644 --- a/borgmatic/actions/browse/app.py +++ b/borgmatic/actions/browse/app.py @@ -5,7 +5,9 @@ import textual.binding import textual.widgets import borgmatic.actions.browse.carousel +import borgmatic.actions.browse.configuration_files_list import borgmatic.actions.browse.logs +import borgmatic.actions.browse.repositories_list class Browse_app(textual.app.App): @@ -52,14 +54,20 @@ class Browse_app(textual.app.App): ''' yield textual.widgets.Header() yield borgmatic.actions.browse.carousel.Carousel( - [borgmatic.actions.browse.panels.Configuration_files_list(self.configs)] + [ + borgmatic.actions.browse.configuration_files_list.Configuration_files_list( + self.configs + ) + ] if len(self.configs) > 1 else [ - borgmatic.actions.browse.panels.Repositories_list(next(iter(self.configs.values()))) + borgmatic.actions.browse.repositories_list.Repositories_list( + next(iter(self.configs.values())) + ) ] ) - logs_panel = borgmatic.actions.browse.panels.Logs() + logs_panel = borgmatic.actions.browse.logs.Logs() yield logs_panel yield textual.widgets.Footer() diff --git a/borgmatic/actions/browse/archives_list.py b/borgmatic/actions/browse/archives_list.py new file mode 100644 index 00000000..01c0febf --- /dev/null +++ b/borgmatic/actions/browse/archives_list.py @@ -0,0 +1,36 @@ +import contextlib +import os + +import textual.binding +import textual.widgets + +import borgmatic.actions.browse.bindings +import borgmatic.actions.browse.loading +import borgmatic.actions.browse.paths +import borgmatic.actions.browse.workers + + +class Archives_list(textual.widgets.OptionList): + BINDINGS = borgmatic.actions.browse.bindings.OPTION_LIST_BINDINGS + + def __init__(self, config, repository): + self.config = config + self.repository = repository + + super().__init__(classes='panel') + self.border_title = '📚 archives' + self.highlighted_option_changed = False + + timer = borgmatic.actions.browse.loading.add_inline_loading_indicator(self) + + borgmatic.actions.browse.workers.add_repository_archives( + self.app, + archives_list=self, + config=self.config, + repository=self.repository, + timer=timer, + ) + + def on_option_list_option_highlighted(self, event): + if self.highlighted not in {None, 0}: + self.highlighted_option_changed = True diff --git a/borgmatic/actions/browse/bindings.py b/borgmatic/actions/browse/bindings.py new file mode 100644 index 00000000..f42422c5 --- /dev/null +++ b/borgmatic/actions/browse/bindings.py @@ -0,0 +1,23 @@ +import textual.binding +import textual.widgets + + +OPTION_LIST_BINDINGS = ( + *textual.widgets.OptionList.BINDINGS, + textual.binding.Binding( + key='up,k', action='cursor_up', description='scroll up', show=True, priority=True + ), + textual.binding.Binding( + key='down,j', action='cursor_down', description='scroll down', show=True, priority=True + ), + textual.binding.Binding( + key='pageup', action='page_up', description='page up', show=True, priority=True + ), + textual.binding.Binding( + key='pagedown', action='page_down', description='page down', show=True, priority=True + ), + textual.binding.Binding( + key='enter', action='select', description='select', show=True, priority=True + ), + textual.binding.Binding(key='right,l', action='select', description='select', show=False), +) diff --git a/borgmatic/actions/browse/carousel.py b/borgmatic/actions/browse/carousel.py index aa9a8b11..2b3f2580 100644 --- a/borgmatic/actions/browse/carousel.py +++ b/borgmatic/actions/browse/carousel.py @@ -3,8 +3,12 @@ import os import textual.binding import textual.containers -import borgmatic.actions.browse.panels +import borgmatic.actions.browse.archives_list +import borgmatic.actions.browse.directory_list +import borgmatic.actions.browse.configuration_files_list +import borgmatic.actions.browse.file_preview import borgmatic.actions.browse.paths +import borgmatic.actions.browse.repositories_list def make_next_panel(focused_panel, option_id): @@ -16,22 +20,24 @@ def make_next_panel(focused_panel, option_id): If the particular option ID on the focused panel doesn't have a supported next panel, then return None. ''' - if isinstance(focused_panel, borgmatic.actions.browse.panels.Configuration_files_list): - return borgmatic.actions.browse.panels.Repositories_list( + if isinstance( + focused_panel, borgmatic.actions.browse.configuration_files_list.Configuration_files_list + ): + return borgmatic.actions.browse.repositories_list.Repositories_list( config=focused_panel.configs[option_id] ) - if isinstance(focused_panel, borgmatic.actions.browse.panels.Repositories_list): - return borgmatic.actions.browse.panels.Archives_list( + if isinstance(focused_panel, borgmatic.actions.browse.repositories_list.Repositories_list): + return borgmatic.actions.browse.archives_list.Archives_list( config=focused_panel.config, repository=focused_panel.repositories[option_id] ) - if isinstance(focused_panel, borgmatic.actions.browse.panels.Archives_list): - return borgmatic.actions.browse.panels.Directory_list( + if isinstance(focused_panel, borgmatic.actions.browse.archives_list.Archives_list): + return borgmatic.actions.browse.directory_list.Directory_list( config=focused_panel.config, repository=focused_panel.repository, archive_name=option_id ) - if isinstance(focused_panel, borgmatic.actions.browse.panels.Directory_list): + if isinstance(focused_panel, borgmatic.actions.browse.directory_list.Directory_list): option = focused_panel.get_option(option_id) if option.prompt.startswith( @@ -39,7 +45,7 @@ def make_next_panel(focused_panel, option_id): borgmatic.actions.browse.paths.Path_type.DIRECTORY.value ] ): - return borgmatic.actions.browse.panels.Directory_list( + return borgmatic.actions.browse.directory_list.Directory_list( focused_panel.config, focused_panel.repository, focused_panel.archive_name, @@ -51,7 +57,7 @@ def make_next_panel(focused_panel, option_id): borgmatic.actions.browse.paths.Path_type.FILE.value ] ): - return borgmatic.actions.browse.panels.File_preview( + return borgmatic.actions.browse.file_preview.File_preview( focused_panel.config, focused_panel.repository, focused_panel.archive_name, diff --git a/borgmatic/actions/browse/configuration_files_list.py b/borgmatic/actions/browse/configuration_files_list.py new file mode 100644 index 00000000..4d7d1a2f --- /dev/null +++ b/borgmatic/actions/browse/configuration_files_list.py @@ -0,0 +1,28 @@ +import contextlib +import os + +import textual.binding +import textual.widgets + +import borgmatic.actions.browse.bindings +import borgmatic.actions.browse.loading +import borgmatic.actions.browse.paths +import borgmatic.actions.browse.workers + + +class Configuration_files_list(textual.widgets.OptionList): + BINDINGS = borgmatic.actions.browse.bindings.OPTION_LIST_BINDINGS + + def __init__(self, configs): + self.configs = configs + home_directory = os.path.expanduser('~') + + super().__init__( + *( + textual.widgets.option_list.Option(unexpanded_path, id=config_path) + for config_path in configs + for unexpanded_path in (config_path.replace(home_directory, '~'),) + ), + classes='panel', + ) + self.border_title = 'configuration files' diff --git a/borgmatic/actions/browse/panels.py b/borgmatic/actions/browse/directory_list.py similarity index 51% rename from borgmatic/actions/browse/panels.py rename to borgmatic/actions/browse/directory_list.py index a81a8b5e..4b2a0cf3 100644 --- a/borgmatic/actions/browse/panels.py +++ b/borgmatic/actions/browse/directory_list.py @@ -1,101 +1,15 @@ import contextlib -import logging import os import textual.binding import textual.widgets +import borgmatic.actions.browse.bindings import borgmatic.actions.browse.loading import borgmatic.actions.browse.paths import borgmatic.actions.browse.workers -logger = logging.getLogger('__name__') - - -OPTION_LIST_BINDINGS = ( - *textual.widgets.OptionList.BINDINGS, - textual.binding.Binding( - key='up,k', action='cursor_up', description='scroll up', show=True, priority=True - ), - textual.binding.Binding( - key='down,j', action='cursor_down', description='scroll down', show=True, priority=True - ), - textual.binding.Binding( - key='pageup', action='page_up', description='page up', show=True, priority=True - ), - textual.binding.Binding( - key='pagedown', action='page_down', description='page down', show=True, priority=True - ), - textual.binding.Binding( - key='enter', action='select', description='select', show=True, priority=True - ), - textual.binding.Binding(key='right,l', action='select', description='select', show=False), -) - - -class Configuration_files_list(textual.widgets.OptionList): - BINDINGS = OPTION_LIST_BINDINGS - - def __init__(self, configs): - self.configs = configs - home_directory = os.path.expanduser('~') - - super().__init__( - *( - textual.widgets.option_list.Option(unexpanded_path, id=config_path) - for config_path in configs - for unexpanded_path in (config_path.replace(home_directory, '~'),) - ), - classes='panel', - ) - self.border_title = 'configuration files' - - -class Repositories_list(textual.widgets.OptionList): - BINDINGS = OPTION_LIST_BINDINGS - - def __init__(self, config): - self.config = config - self.repositories = config['repositories'] - - super().__init__( - *( - textual.widgets.option_list.Option(label, id=index) - for index, repository in enumerate(self.repositories) - for label in (repository.get('label', repository.get('path')),) - ), - classes='panel', - ) - self.border_title = '📦 repositories' - - -class Archives_list(textual.widgets.OptionList): - BINDINGS = OPTION_LIST_BINDINGS - - def __init__(self, config, repository): - self.config = config - self.repository = repository - - super().__init__(classes='panel') - self.border_title = '📚 archives' - self.highlighted_option_changed = False - - timer = borgmatic.actions.browse.loading.add_inline_loading_indicator(self) - - borgmatic.actions.browse.workers.add_repository_archives( - self.app, - archives_list=self, - config=self.config, - repository=self.repository, - timer=timer, - ) - - def on_option_list_option_highlighted(self, event): - if self.highlighted not in {None, 0}: - self.highlighted_option_changed = True - - def get_relative_archive_path_components(archive_path, current_directory_path_components): ''' Given an Archive_path instance and a tuple of path components for the currently browsed @@ -110,11 +24,14 @@ def get_relative_archive_path_components(archive_path, current_directory_path_co # If the loaded path doesn't match this directory list's own path, then we don't care about # it for purposes of displaying this particular directory. - if tuple(archive_path_components[: len(current_directory_path_components)]) != current_directory_path_components: + if ( + tuple(archive_path_components[: len(current_directory_path_components)]) + != current_directory_path_components + ): return None # Strip off the portion of the archive path that matches the directory list's own path. - return archive_path_components[len(current_directory_path_components):] + return archive_path_components[len(current_directory_path_components) :] def make_directory_list_option(archive_path, archive_path_components): @@ -144,10 +61,12 @@ def add_archive_paths( *( make_directory_list_option(archive_path, archive_path_components) for archive_path in archive_paths - for archive_path_components in (get_relative_archive_path_components( - archive_path, - directory_list.path_components, - ),) + for archive_path_components in ( + get_relative_archive_path_components( + archive_path, + directory_list.path_components, + ), + ) if archive_path_components if not archive_path_components[0] in directory_list._id_to_option ), @@ -168,7 +87,7 @@ def add_archive_paths( class Directory_list(textual.widgets.OptionList): - BINDINGS = OPTION_LIST_BINDINGS + BINDINGS = borgmatic.actions.browse.bindings.OPTION_LIST_BINDINGS def __init__(self, config, repository, archive_name, path_loaded=None, path_components=None): self.config = config @@ -244,57 +163,3 @@ class Directory_list(textual.widgets.OptionList): def on_option_list_option_highlighted(self, event): if self.highlighted not in {None, 0}: self.highlighted_option_changed = True - - -class File_preview(textual.widgets.RichLog): - BINDINGS = [ - *textual.widgets.RichLog.BINDINGS, - textual.binding.Binding( - key='up,k', action='scroll_up', description='scroll up', show=True, priority=True - ), - textual.binding.Binding( - key='down,j', action='scroll_down', description='scroll down', show=True, priority=True - ), - textual.binding.Binding( - key='pageup', action='page_up', description='page up', show=True, priority=True - ), - textual.binding.Binding( - key='pagedown', action='page_down', description='page down', show=True, priority=True - ), - ] - - def __init__(self, config, repository, archive_name, file_path): - self.config = config - self.repository = repository - self.archive_name = archive_name - self.file_path = file_path - - super().__init__(classes='panel') - self.border_title = ' '.join( - ( - borgmatic.actions.browse.paths.PATH_TYPE_ICONS[ - borgmatic.actions.browse.paths.Path_type.FILE.value - ], - self.file_path, - 'preview', - ) - ) - self.auto_scroll = False - - timer = borgmatic.actions.browse.loading.add_inline_loading_indicator(self) - - borgmatic.actions.browse.workers.load_file_preview( - self.app, - file_preview=self, - config=self.config, - repository=self.repository, - archive_name=self.archive_name, - file_path=self.file_path, - timer=timer, - ) - - -class Logs(textual.widgets.RichLog): - def __init__(self): - super().__init__(markup=True, id='logs', classes='panel') - self.border_title = '🪵 logs' diff --git a/borgmatic/actions/browse/file_preview.py b/borgmatic/actions/browse/file_preview.py new file mode 100644 index 00000000..3d0ac8e3 --- /dev/null +++ b/borgmatic/actions/browse/file_preview.py @@ -0,0 +1,57 @@ +import contextlib +import os + +import textual.binding +import textual.widgets + +import borgmatic.actions.browse.loading +import borgmatic.actions.browse.paths +import borgmatic.actions.browse.workers + + +class File_preview(textual.widgets.RichLog): + BINDINGS = [ + *textual.widgets.RichLog.BINDINGS, + textual.binding.Binding( + key='up,k', action='scroll_up', description='scroll up', show=True, priority=True + ), + textual.binding.Binding( + key='down,j', action='scroll_down', description='scroll down', show=True, priority=True + ), + textual.binding.Binding( + key='pageup', action='page_up', description='page up', show=True, priority=True + ), + textual.binding.Binding( + key='pagedown', action='page_down', description='page down', show=True, priority=True + ), + ] + + def __init__(self, config, repository, archive_name, file_path): + self.config = config + self.repository = repository + self.archive_name = archive_name + self.file_path = file_path + + super().__init__(classes='panel') + self.border_title = ' '.join( + ( + borgmatic.actions.browse.paths.PATH_TYPE_ICONS[ + borgmatic.actions.browse.paths.Path_type.FILE.value + ], + self.file_path, + 'preview', + ) + ) + self.auto_scroll = False + + timer = borgmatic.actions.browse.loading.add_inline_loading_indicator(self) + + borgmatic.actions.browse.workers.load_file_preview( + self.app, + file_preview=self, + config=self.config, + repository=self.repository, + archive_name=self.archive_name, + file_path=self.file_path, + timer=timer, + ) diff --git a/borgmatic/actions/browse/loading.py b/borgmatic/actions/browse/loading.py index c086375a..18d140a4 100644 --- a/borgmatic/actions/browse/loading.py +++ b/borgmatic/actions/browse/loading.py @@ -8,6 +8,7 @@ LOADING_DOT_INTERVAL_SECONDS = 0.3 import logging + logger = logging.getLogger('__name__') diff --git a/borgmatic/actions/browse/logs.py b/borgmatic/actions/browse/logs.py index 3e59de39..a7f8280e 100644 --- a/borgmatic/actions/browse/logs.py +++ b/borgmatic/actions/browse/logs.py @@ -2,6 +2,7 @@ import contextlib import logging import textual._context +import textual.widgets import textual.worker import borgmatic.logger @@ -68,3 +69,9 @@ def log_to_widget(logs_widget): if isinstance(handler, borgmatic.logger.Multi_stream_handler) ) logger.removeHandler(console_handler) + + +class Logs(textual.widgets.RichLog): + def __init__(self): + super().__init__(markup=True, id='logs', classes='panel') + self.border_title = '🪵 logs' diff --git a/borgmatic/actions/browse/repositories_list.py b/borgmatic/actions/browse/repositories_list.py new file mode 100644 index 00000000..086d4408 --- /dev/null +++ b/borgmatic/actions/browse/repositories_list.py @@ -0,0 +1,28 @@ +import contextlib +import os + +import textual.binding +import textual.widgets + +import borgmatic.actions.browse.bindings +import borgmatic.actions.browse.loading +import borgmatic.actions.browse.paths +import borgmatic.actions.browse.workers + + +class Repositories_list(textual.widgets.OptionList): + BINDINGS = borgmatic.actions.browse.bindings.OPTION_LIST_BINDINGS + + def __init__(self, config): + self.config = config + self.repositories = config['repositories'] + + super().__init__( + *( + textual.widgets.option_list.Option(label, id=index) + for index, repository in enumerate(self.repositories) + for label in (repository.get('label', repository.get('path')),) + ), + classes='panel', + ) + self.border_title = '📦 repositories'