data-sourcerer 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data_sourcerer-0.1.0.dist-info/METADATA +52 -0
- data_sourcerer-0.1.0.dist-info/RECORD +95 -0
- data_sourcerer-0.1.0.dist-info/WHEEL +5 -0
- data_sourcerer-0.1.0.dist-info/entry_points.txt +2 -0
- data_sourcerer-0.1.0.dist-info/licenses/LICENSE +21 -0
- data_sourcerer-0.1.0.dist-info/top_level.txt +1 -0
- sourcerer/__init__.py +15 -0
- sourcerer/domain/__init__.py +13 -0
- sourcerer/domain/access_credentials/__init__.py +7 -0
- sourcerer/domain/access_credentials/entities.py +53 -0
- sourcerer/domain/access_credentials/exceptions.py +17 -0
- sourcerer/domain/access_credentials/repositories.py +84 -0
- sourcerer/domain/access_credentials/services.py +91 -0
- sourcerer/domain/file_system/__init__.py +7 -0
- sourcerer/domain/file_system/entities.py +70 -0
- sourcerer/domain/file_system/exceptions.py +17 -0
- sourcerer/domain/file_system/services.py +64 -0
- sourcerer/domain/shared/__init__.py +6 -0
- sourcerer/domain/shared/entities.py +18 -0
- sourcerer/domain/storage_provider/__init__.py +7 -0
- sourcerer/domain/storage_provider/entities.py +86 -0
- sourcerer/domain/storage_provider/exceptions.py +17 -0
- sourcerer/domain/storage_provider/services.py +130 -0
- sourcerer/infrastructure/__init__.py +13 -0
- sourcerer/infrastructure/access_credentials/__init__.py +7 -0
- sourcerer/infrastructure/access_credentials/exceptions.py +16 -0
- sourcerer/infrastructure/access_credentials/registry.py +120 -0
- sourcerer/infrastructure/access_credentials/repositories.py +119 -0
- sourcerer/infrastructure/access_credentials/services.py +396 -0
- sourcerer/infrastructure/db/__init__.py +6 -0
- sourcerer/infrastructure/db/config.py +73 -0
- sourcerer/infrastructure/db/models.py +47 -0
- sourcerer/infrastructure/file_system/__init__.py +7 -0
- sourcerer/infrastructure/file_system/exceptions.py +89 -0
- sourcerer/infrastructure/file_system/services.py +147 -0
- sourcerer/infrastructure/storage_provider/__init__.py +7 -0
- sourcerer/infrastructure/storage_provider/exceptions.py +78 -0
- sourcerer/infrastructure/storage_provider/registry.py +84 -0
- sourcerer/infrastructure/storage_provider/services.py +509 -0
- sourcerer/infrastructure/utils.py +106 -0
- sourcerer/presentation/__init__.py +12 -0
- sourcerer/presentation/app.py +36 -0
- sourcerer/presentation/di_container.py +46 -0
- sourcerer/presentation/screens/__init__.py +0 -0
- sourcerer/presentation/screens/critical_error/__init__.py +0 -0
- sourcerer/presentation/screens/critical_error/main.py +78 -0
- sourcerer/presentation/screens/critical_error/styles.tcss +41 -0
- sourcerer/presentation/screens/file_system_finder/main.py +248 -0
- sourcerer/presentation/screens/file_system_finder/styles.tcss +44 -0
- sourcerer/presentation/screens/file_system_finder/widgets/__init__.py +0 -0
- sourcerer/presentation/screens/file_system_finder/widgets/file_system_navigator.py +810 -0
- sourcerer/presentation/screens/main/__init__.py +0 -0
- sourcerer/presentation/screens/main/main.py +469 -0
- sourcerer/presentation/screens/main/messages/__init__.py +0 -0
- sourcerer/presentation/screens/main/messages/delete_request.py +12 -0
- sourcerer/presentation/screens/main/messages/download_request.py +12 -0
- sourcerer/presentation/screens/main/messages/preview_request.py +10 -0
- sourcerer/presentation/screens/main/messages/resizing_rule.py +21 -0
- sourcerer/presentation/screens/main/messages/select_storage_item.py +11 -0
- sourcerer/presentation/screens/main/messages/uncheck_files_request.py +8 -0
- sourcerer/presentation/screens/main/messages/upload_request.py +10 -0
- sourcerer/presentation/screens/main/mixins/__init__.py +0 -0
- sourcerer/presentation/screens/main/mixins/resize_containers_watcher_mixin.py +144 -0
- sourcerer/presentation/screens/main/styles.tcss +32 -0
- sourcerer/presentation/screens/main/widgets/__init__.py +0 -0
- sourcerer/presentation/screens/main/widgets/gradient.py +45 -0
- sourcerer/presentation/screens/main/widgets/resizing_rule.py +67 -0
- sourcerer/presentation/screens/main/widgets/storage_content.py +691 -0
- sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py +143 -0
- sourcerer/presentation/screens/preview_content/__init__.py +0 -0
- sourcerer/presentation/screens/preview_content/main.py +59 -0
- sourcerer/presentation/screens/preview_content/styles.tcss +26 -0
- sourcerer/presentation/screens/provider_creds_list/__init__.py +0 -0
- sourcerer/presentation/screens/provider_creds_list/main.py +164 -0
- sourcerer/presentation/screens/provider_creds_list/styles.tcss +65 -0
- sourcerer/presentation/screens/provider_creds_registration/__init__.py +0 -0
- sourcerer/presentation/screens/provider_creds_registration/main.py +264 -0
- sourcerer/presentation/screens/provider_creds_registration/styles.tcss +42 -0
- sourcerer/presentation/screens/question/__init__.py +0 -0
- sourcerer/presentation/screens/question/main.py +31 -0
- sourcerer/presentation/screens/question/styles.tcss +33 -0
- sourcerer/presentation/screens/shared/__init__.py +0 -0
- sourcerer/presentation/screens/shared/containers.py +13 -0
- sourcerer/presentation/screens/shared/widgets/__init__.py +0 -0
- sourcerer/presentation/screens/shared/widgets/button.py +54 -0
- sourcerer/presentation/screens/shared/widgets/labeled_input.py +80 -0
- sourcerer/presentation/screens/storage_action_progress/__init__.py +0 -0
- sourcerer/presentation/screens/storage_action_progress/main.py +476 -0
- sourcerer/presentation/screens/storage_action_progress/styles.tcss +43 -0
- sourcerer/presentation/settings.py +31 -0
- sourcerer/presentation/themes/__init__.py +0 -0
- sourcerer/presentation/themes/github_dark.py +21 -0
- sourcerer/presentation/utils.py +69 -0
- sourcerer/settings.py +72 -0
- sourcerer/utils.py +32 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
"""
|
2
|
+
Dependency injection container configuration for the Sourcerer application.
|
3
|
+
|
4
|
+
This module defines the dependency injection container that manages the application's
|
5
|
+
dependencies and their lifecycle. It provides a centralized way to configure
|
6
|
+
and access services, repositories, and other components throughout the application.
|
7
|
+
"""
|
8
|
+
|
9
|
+
from pathlib import Path
|
10
|
+
|
11
|
+
from dependency_injector import containers, providers
|
12
|
+
|
13
|
+
from sourcerer.infrastructure.access_credentials.repositories import (
|
14
|
+
SQLAlchemyCredentialsRepository,
|
15
|
+
)
|
16
|
+
from sourcerer.infrastructure.db.config import Database
|
17
|
+
from sourcerer.infrastructure.file_system.services import FileSystemService
|
18
|
+
from sourcerer.settings import APP_DIR, DB_NAME
|
19
|
+
|
20
|
+
DB_URL = f"sqlite:////{APP_DIR}/{DB_NAME}"
|
21
|
+
|
22
|
+
|
23
|
+
class DiContainer(containers.DeclarativeContainer):
|
24
|
+
"""
|
25
|
+
Dependency injection container for the Sourcerer application.
|
26
|
+
|
27
|
+
This container manages the application's dependencies including:
|
28
|
+
- Database configuration and connection
|
29
|
+
- Session factory for database operations
|
30
|
+
- Credentials repository for managing access credentials
|
31
|
+
- File system service for local file operations
|
32
|
+
|
33
|
+
The container uses the dependency_injector library to provide
|
34
|
+
a clean way to manage dependencies and their lifecycle.
|
35
|
+
"""
|
36
|
+
|
37
|
+
config = providers.Configuration()
|
38
|
+
|
39
|
+
db = providers.Singleton(Database, db_url=DB_URL)
|
40
|
+
session_factory = providers.Factory(Database.session_factory, db=db)
|
41
|
+
|
42
|
+
credentials_repository = providers.Factory(
|
43
|
+
SQLAlchemyCredentialsRepository, session_factory
|
44
|
+
)
|
45
|
+
|
46
|
+
file_system_service = providers.Factory(FileSystemService, Path.home())
|
File without changes
|
File without changes
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import platform
|
2
|
+
import sys
|
3
|
+
import webbrowser
|
4
|
+
|
5
|
+
from textual import on
|
6
|
+
from textual.app import ComposeResult
|
7
|
+
from textual.containers import Horizontal, Container
|
8
|
+
from textual.screen import ModalScreen
|
9
|
+
from textual.widgets import Label, RichLog
|
10
|
+
from textual.css.query import NoMatches
|
11
|
+
|
12
|
+
from sourcerer import __version__
|
13
|
+
from sourcerer.presentation.screens.shared.widgets.button import Button
|
14
|
+
|
15
|
+
|
16
|
+
class CriticalErrorScreen(ModalScreen[bool]):
|
17
|
+
"""Screen with a parameter."""
|
18
|
+
|
19
|
+
CSS_PATH = "styles.tcss"
|
20
|
+
|
21
|
+
def __init__(self, error: str, traceback: str) -> None:
|
22
|
+
self.error = error
|
23
|
+
self.traceback = traceback
|
24
|
+
super().__init__()
|
25
|
+
|
26
|
+
def compose(self) -> ComposeResult:
|
27
|
+
with Container(id="CriticalErrorScreen"):
|
28
|
+
yield Label(self.error)
|
29
|
+
yield RichLog(highlight=True, markup=True)
|
30
|
+
with Horizontal():
|
31
|
+
yield Button("Report", name="report")
|
32
|
+
yield Button("Dismiss", name="dismiss")
|
33
|
+
|
34
|
+
|
35
|
+
def on_mount(self) -> None:
|
36
|
+
"""
|
37
|
+
Called when the screen is mounted.
|
38
|
+
"""
|
39
|
+
self.query_one("#CriticalErrorScreen").border_title = "Critical Error"
|
40
|
+
try:
|
41
|
+
text_log = self.query_one(RichLog)
|
42
|
+
except NoMatches:
|
43
|
+
return
|
44
|
+
|
45
|
+
text_log.write(self.traceback)
|
46
|
+
|
47
|
+
@on(Button.Click)
|
48
|
+
def on_button_click(self, event: Button.Click) -> None:
|
49
|
+
"""
|
50
|
+
Handle button click events.
|
51
|
+
"""
|
52
|
+
if event.action == "report":
|
53
|
+
webbrowser.open(self._build_github_issue_url(), new=0, autoraise=True)
|
54
|
+
elif event.action == "dismiss":
|
55
|
+
self.dismiss() # type: ignore
|
56
|
+
|
57
|
+
def _build_github_issue_url(self) -> str:
|
58
|
+
"""
|
59
|
+
Build the GitHub issue URL.
|
60
|
+
"""
|
61
|
+
|
62
|
+
error_body = f"""
|
63
|
+
Operating System: {platform.system()}({platform.release()})
|
64
|
+
Python Version: {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}
|
65
|
+
Sourcerer Version: {__version__}
|
66
|
+
Steps to Reproduce:
|
67
|
+
…
|
68
|
+
…
|
69
|
+
…
|
70
|
+
Traceback:
|
71
|
+
```{self.traceback}```
|
72
|
+
"""
|
73
|
+
|
74
|
+
return (
|
75
|
+
f"https://github.com/the-impact-craft/sourcerer/issues/new?"
|
76
|
+
f"title=Runtime issue:{self.error}&"
|
77
|
+
f"body={error_body}"
|
78
|
+
)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
CriticalErrorScreen {
|
2
|
+
align: center middle;
|
3
|
+
content-align: center top;
|
4
|
+
|
5
|
+
|
6
|
+
& > Container {
|
7
|
+
padding: 1 2 0 2;
|
8
|
+
margin: 0 0;
|
9
|
+
width: 75%;
|
10
|
+
height: 25;
|
11
|
+
border: solid $text-secondary;
|
12
|
+
border-title-color: $text-secondary;
|
13
|
+
|
14
|
+
background: $background-lighten-1;
|
15
|
+
|
16
|
+
& > Label {
|
17
|
+
margin: 1;
|
18
|
+
text-align: center;
|
19
|
+
column-span: 2;
|
20
|
+
width: auto;
|
21
|
+
}
|
22
|
+
|
23
|
+
& > RichLog {
|
24
|
+
height: 16;
|
25
|
+
}
|
26
|
+
|
27
|
+
& > Horizontal {
|
28
|
+
align: center bottom;
|
29
|
+
|
30
|
+
& > Button {
|
31
|
+
width: auto;
|
32
|
+
margin-right: 2;
|
33
|
+
color: $text-secondary;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
}
|
39
|
+
|
40
|
+
|
41
|
+
|
@@ -0,0 +1,248 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import List, Callable
|
4
|
+
|
5
|
+
from dependency_injector.wiring import Provide
|
6
|
+
from textual import on
|
7
|
+
from textual.app import ComposeResult
|
8
|
+
from textual.binding import Binding
|
9
|
+
from textual.containers import Horizontal, Container
|
10
|
+
from textual.css.query import NoMatches
|
11
|
+
from textual.reactive import reactive
|
12
|
+
from textual.screen import ModalScreen
|
13
|
+
from textual.widgets import Static
|
14
|
+
|
15
|
+
from sourcerer.infrastructure.file_system.services import FileSystemService
|
16
|
+
from sourcerer.presentation.di_container import DiContainer
|
17
|
+
from sourcerer.presentation.screens.file_system_finder.widgets.file_system_navigator import (
|
18
|
+
FileSystemNavigator,
|
19
|
+
)
|
20
|
+
from sourcerer.presentation.screens.shared.widgets.button import Button
|
21
|
+
|
22
|
+
|
23
|
+
@dataclass
|
24
|
+
class FileSystemSelectionValidationRule:
|
25
|
+
action: Callable[[Path], bool]
|
26
|
+
error_message: str
|
27
|
+
|
28
|
+
|
29
|
+
class FileSystemNavigationModal(ModalScreen):
|
30
|
+
CONTAINER_ID = "file_system_view_container"
|
31
|
+
CSS_PATH = "styles.tcss"
|
32
|
+
|
33
|
+
BINDINGS = [
|
34
|
+
Binding("escape", "app.pop_screen", "Pop screen"),
|
35
|
+
]
|
36
|
+
|
37
|
+
active_path: reactive[Path] = reactive(Path())
|
38
|
+
|
39
|
+
def __init__(
|
40
|
+
self,
|
41
|
+
file_system_service: FileSystemService = Provide[
|
42
|
+
DiContainer.file_system_service
|
43
|
+
],
|
44
|
+
validation_rules: List[FileSystemSelectionValidationRule] | None = None,
|
45
|
+
*args,
|
46
|
+
**kwargs,
|
47
|
+
):
|
48
|
+
"""
|
49
|
+
Initialize a FileSystemNavigationModal with file system navigation configuration.
|
50
|
+
|
51
|
+
Arguments:
|
52
|
+
file_system_service (FileSystemService, optional): Service for file system operations.
|
53
|
+
Defaults to dependency injection from DiContainer.
|
54
|
+
work_dir (Path, optional): Working directory for file navigation.
|
55
|
+
Defaults to dependency injection from DiContainer configuration.
|
56
|
+
validation_rules (List[FileSystemSelectionValidationRule], optional): Rules for validating file selections.
|
57
|
+
Defaults to an empty list if not provided.
|
58
|
+
*args: Variable positional arguments passed to parent class constructor.
|
59
|
+
**kwargs: Variable keyword arguments passed to parent class constructor.
|
60
|
+
|
61
|
+
Raises:
|
62
|
+
TypeError: If validation rules are not instances of FileSystemSelectionValidationRule,
|
63
|
+
or if file_system_service is not a FileSystemService or work_dir is not a Path.
|
64
|
+
|
65
|
+
Notes:
|
66
|
+
- Validates input types before setting instance attributes
|
67
|
+
- Logs an error and raises TypeError for invalid input types
|
68
|
+
- Ensures type safety for file system navigation configuration
|
69
|
+
"""
|
70
|
+
super().__init__(*args, **kwargs)
|
71
|
+
|
72
|
+
if not validation_rules:
|
73
|
+
validation_rules = []
|
74
|
+
|
75
|
+
if not all(
|
76
|
+
isinstance(rule, FileSystemSelectionValidationRule)
|
77
|
+
for rule in validation_rules
|
78
|
+
):
|
79
|
+
self.log.error("Invalid validation rules provided")
|
80
|
+
raise TypeError(
|
81
|
+
"Each validation rule must be an instance of FileSystemSelectionValidationRule"
|
82
|
+
)
|
83
|
+
|
84
|
+
self.file_system_service = file_system_service
|
85
|
+
self.work_dir = file_system_service.work_dir
|
86
|
+
self.validation_rules = validation_rules
|
87
|
+
|
88
|
+
def compose(self) -> ComposeResult:
|
89
|
+
"""
|
90
|
+
Compose the user interface for the file system navigation modal.
|
91
|
+
|
92
|
+
This method sets up the modal's layout with three primary components:
|
93
|
+
1. A static label to display the current active path
|
94
|
+
2. A file system navigator for browsing and selecting files/directories
|
95
|
+
3. Control buttons for closing or applying the current selection
|
96
|
+
|
97
|
+
Returns:
|
98
|
+
ComposeResult: A generator yielding the modal's UI components, including a path label,
|
99
|
+
file system navigator, and control buttons.
|
100
|
+
|
101
|
+
Components:
|
102
|
+
- Static label with ID "active-path" for displaying the current path
|
103
|
+
- FileSystemNavigator configured with the current working directory and file system service
|
104
|
+
- Horizontal layout of "Close" and "Apply" buttons with the ID "controls"
|
105
|
+
"""
|
106
|
+
with Container(id=self.CONTAINER_ID):
|
107
|
+
yield Static("", id="active-path")
|
108
|
+
yield FileSystemNavigator(
|
109
|
+
work_dir=self.work_dir, file_system_service=self.file_system_service
|
110
|
+
)
|
111
|
+
yield Horizontal(
|
112
|
+
Button("Close", name="close", id="close", classes="button"),
|
113
|
+
Button("Apply", name="apply", id="apply", classes="button"),
|
114
|
+
id="controls",
|
115
|
+
)
|
116
|
+
|
117
|
+
@on(Button.Click)
|
118
|
+
def on_button_click(self, event: Button.Click):
|
119
|
+
"""
|
120
|
+
Handle button click events for the file system navigation modal.
|
121
|
+
This method is triggered when the user clicks either the "Close" or "Apply" button.
|
122
|
+
It determines the action to take based on the button clicked.
|
123
|
+
|
124
|
+
Args:
|
125
|
+
event (Button.Click): The event containing the button that was clicked.
|
126
|
+
"""
|
127
|
+
if event.action == "close":
|
128
|
+
self.on_close()
|
129
|
+
else:
|
130
|
+
self.on_apply()
|
131
|
+
|
132
|
+
@on(FileSystemNavigator.ActivePathChanged)
|
133
|
+
def on_active_path_changed(self, event: FileSystemNavigator.ActivePathChanged):
|
134
|
+
"""
|
135
|
+
Update the active path label when the file system navigator's active path changes.
|
136
|
+
|
137
|
+
This method is triggered by the `ActivePathChanged` event from the file system navigator. It updates
|
138
|
+
the displayed path label and sets the `active_path` attribute of the modal.
|
139
|
+
|
140
|
+
Arguments:
|
141
|
+
event (FileSystemNavigator.ActivePathChanged): Event containing the newly selected path
|
142
|
+
|
143
|
+
Behavior:
|
144
|
+
- Silently returns if the event path is empty
|
145
|
+
- Attempts to find the active path label with ID "active-path"
|
146
|
+
- If label is not found, silently returns
|
147
|
+
- Calculates the relative path from the working directory
|
148
|
+
- Appends "/" to the label if the path is a directory
|
149
|
+
- Updates the label with the calculated path
|
150
|
+
- Sets the `active_path` attribute to the selected path
|
151
|
+
|
152
|
+
Exceptions:
|
153
|
+
- Handles `NoMatches` exception if the active path label cannot be found
|
154
|
+
"""
|
155
|
+
if not event.path:
|
156
|
+
return
|
157
|
+
try:
|
158
|
+
active_path_label: Static = self.query_one("#active-path") # type: ignore
|
159
|
+
except NoMatches:
|
160
|
+
return
|
161
|
+
label = str(event.path.relative_to(self.work_dir))
|
162
|
+
if event.path.is_dir():
|
163
|
+
label += "/"
|
164
|
+
active_path_label.update(label)
|
165
|
+
self.active_path = event.path
|
166
|
+
|
167
|
+
@on(FileSystemNavigator.ActivePathFileDoubleClicked)
|
168
|
+
def on_path_double_clicked(
|
169
|
+
self, event: FileSystemNavigator.ActivePathFileDoubleClicked
|
170
|
+
):
|
171
|
+
"""
|
172
|
+
Handle the event when a file is double-clicked in the file system navigator.
|
173
|
+
|
174
|
+
Validates the selected file path and either dismisses the modal with the selected path or displays an error
|
175
|
+
notification.
|
176
|
+
|
177
|
+
Arguments:
|
178
|
+
event (FileSystemNavigator.ActivePathFileDoubleClicked): The event containing the double-clicked file path.
|
179
|
+
|
180
|
+
Behavior:
|
181
|
+
- If no path is provided, the method returns without action.
|
182
|
+
- Validates the path using the `validate_path` method.
|
183
|
+
- If the path is invalid, displays an error notification with validation details.
|
184
|
+
- If the path is valid, dismisses the modal and returns the selected path.
|
185
|
+
"""
|
186
|
+
if not event.path:
|
187
|
+
return
|
188
|
+
valid_status, details = self.validate_path(event.path)
|
189
|
+
if not valid_status:
|
190
|
+
self.notify(details, severity="error")
|
191
|
+
else:
|
192
|
+
self.dismiss(event.path)
|
193
|
+
|
194
|
+
def validate_path(self, path: Path) -> tuple[bool, str]:
|
195
|
+
"""
|
196
|
+
Validate the given path against a set of predefined validation rules.
|
197
|
+
|
198
|
+
Iterates through the validation rules and checks each rule's action against the provided path.
|
199
|
+
If any rule fails or raises an exception, the path is considered invalid.
|
200
|
+
|
201
|
+
Arguments:
|
202
|
+
path (Path): The file system path to validate.
|
203
|
+
|
204
|
+
Returns:
|
205
|
+
tuple[bool, str]: A tuple containing:
|
206
|
+
- A boolean indicating whether the path is valid (True) or invalid (False)
|
207
|
+
- A string with error details if validation fails, otherwise an empty string
|
208
|
+
|
209
|
+
Notes:
|
210
|
+
- If no validation rules are defined, the path is considered valid by default.
|
211
|
+
- Stops validation on the first failed rule and returns its error message.
|
212
|
+
- Catches and handles any exceptions raised during rule validation.
|
213
|
+
"""
|
214
|
+
for rule in self.validation_rules or []:
|
215
|
+
try:
|
216
|
+
if not rule.action(path):
|
217
|
+
return False, rule.error_message
|
218
|
+
except Exception:
|
219
|
+
return False, rule.error_message
|
220
|
+
return True, ""
|
221
|
+
|
222
|
+
def on_close(self):
|
223
|
+
"""
|
224
|
+
Dismiss the file system navigation modal without selecting a path.
|
225
|
+
|
226
|
+
This method is triggered when the user clicks the close button or uses the designated close action.
|
227
|
+
It immediately closes the modal screen and returns None, indicating no file was selected.
|
228
|
+
|
229
|
+
"""
|
230
|
+
self.dismiss(None)
|
231
|
+
|
232
|
+
def on_apply(self):
|
233
|
+
"""
|
234
|
+
Handle the apply action in the file system navigation modal.
|
235
|
+
|
236
|
+
Validates the currently selected path and either dismisses the modal with the selected path
|
237
|
+
or displays an error notification if the path is invalid.
|
238
|
+
|
239
|
+
Side Effects:
|
240
|
+
- Calls `self.validate_path()` to check path validity
|
241
|
+
- Displays an error notification if path is invalid
|
242
|
+
- Dismisses the modal with the selected path if valid
|
243
|
+
"""
|
244
|
+
valid_status, details = self.validate_path(self.active_path)
|
245
|
+
if not valid_status:
|
246
|
+
self.notify(details, severity="error")
|
247
|
+
else:
|
248
|
+
self.dismiss(self.active_path)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
FileSystemNavigationModal {
|
3
|
+
align: center middle;
|
4
|
+
content-align: center top;
|
5
|
+
|
6
|
+
& > #file_system_view_container {
|
7
|
+
padding: 1 2 0 2;
|
8
|
+
margin: 0 0;
|
9
|
+
width: 90;
|
10
|
+
height: auto;
|
11
|
+
background: $surface;
|
12
|
+
|
13
|
+
& > #active-path {
|
14
|
+
margin-bottom: 1;
|
15
|
+
width: 1fr;
|
16
|
+
border: solid $block-cursor-blurred-background;
|
17
|
+
|
18
|
+
}
|
19
|
+
|
20
|
+
& > #controls {
|
21
|
+
align: center middle;
|
22
|
+
content-align: center top;
|
23
|
+
padding: 1 0 0 0;
|
24
|
+
height: 4;
|
25
|
+
width: 100%;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
.dir-listing-folder {
|
30
|
+
padding: 0 0 0 0;
|
31
|
+
height: auto;
|
32
|
+
layout: grid;
|
33
|
+
|
34
|
+
& > .folder-name {
|
35
|
+
content-align: left middle;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
.dir-listing-file {
|
40
|
+
padding: 0 0 0 0;
|
41
|
+
height: auto;
|
42
|
+
|
43
|
+
}
|
44
|
+
}
|
File without changes
|