Skip to content

Commit fe800c7

Browse files
committed
mvp feature complete!
1 parent b5052c0 commit fe800c7

File tree

2 files changed

+114
-1
lines changed

2 files changed

+114
-1
lines changed

app/tui/app.py

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,56 @@ def action_close(self) -> None:
275275
self.dismiss()
276276

277277

278+
class SaveFileModal(ModalScreen):
279+
"""Modal to choose save filename before downloading."""
280+
281+
BINDINGS = [("escape", "cancel", "Cancel")]
282+
283+
def __init__(self, default_filename: str, file_path: str, layer_idx: int):
284+
super().__init__()
285+
self.default_filename = default_filename
286+
self.file_path = file_path
287+
self.layer_idx = layer_idx
288+
289+
def compose(self) -> ComposeResult:
290+
with Vertical(id="save-file-dialog"):
291+
yield Label("Save File As", id="save-file-title")
292+
yield Label(f"Source: {self.file_path} (Layer {self.layer_idx})", id="save-file-source")
293+
yield Input(value=self.default_filename, id="save-filename-input", placeholder="Enter filename...")
294+
with Horizontal(id="save-file-buttons"):
295+
yield Button("Save", id="btn-confirm-save", variant="primary")
296+
yield Button("Cancel", id="btn-cancel-save", variant="warning")
297+
298+
def on_button_pressed(self, event: Button.Pressed) -> None:
299+
if event.button.id == "btn-confirm-save":
300+
filename_input = self.query_one("#save-filename-input", Input)
301+
filename = filename_input.value.strip()
302+
if filename:
303+
self.dismiss(result={
304+
"filename": filename,
305+
"path": self.file_path,
306+
"layer": self.layer_idx
307+
})
308+
else:
309+
self.notify("Please enter a filename", severity="warning")
310+
else:
311+
self.dismiss(result=None)
312+
313+
def on_input_submitted(self, event: Input.Submitted) -> None:
314+
"""Handle Enter key in the filename input."""
315+
if event.input.id == "save-filename-input":
316+
filename = event.value.strip()
317+
if filename:
318+
self.dismiss(result={
319+
"filename": filename,
320+
"path": self.file_path,
321+
"layer": self.layer_idx
322+
})
323+
324+
def action_cancel(self) -> None:
325+
self.dismiss(result=None)
326+
327+
278328
def parse_slug(slug: str) -> tuple[str, str]:
279329
"""Extract namespace and repo from slug.
280330
@@ -629,7 +679,30 @@ def _on_file_action_chosen(self, result: dict | None) -> None:
629679
if action == "view":
630680
self.carve_file_as_text(file_path, layer, filename)
631681
elif action == "save":
632-
self.carve_file_download(file_path, layer, filename)
682+
# Generate a unique default filename with layer number
683+
# Split filename into name and extension
684+
if "." in filename:
685+
name_parts = filename.rsplit(".", 1)
686+
default_filename = f"{name_parts[0]}_L{layer}.{name_parts[1]}"
687+
else:
688+
default_filename = f"{filename}_L{layer}"
689+
690+
# Show save filename modal
691+
self.push_screen(
692+
SaveFileModal(default_filename, file_path, layer),
693+
callback=self._on_save_filename_chosen
694+
)
695+
696+
def _on_save_filename_chosen(self, result: dict | None) -> None:
697+
"""Handle save filename modal result."""
698+
if result is None:
699+
return
700+
701+
filename = result.get("filename")
702+
file_path = result.get("path")
703+
layer = result.get("layer")
704+
705+
self.carve_file_download(file_path, layer, filename)
633706

634707
@work(exclusive=True, group="carve")
635708
async def carve_file_as_text(self, file_path: str, layer: int, filename: str) -> None:

app/tui/styles.tcss

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,43 @@ TextViewerModal {
212212
margin-top: 1;
213213
align: center middle;
214214
}
215+
216+
/* Save File Modal */
217+
SaveFileModal {
218+
align: center middle;
219+
}
220+
221+
#save-file-dialog {
222+
width: 70;
223+
height: auto;
224+
padding: 1 2;
225+
background: $surface;
226+
border: thick $primary;
227+
}
228+
229+
#save-file-title {
230+
text-align: center;
231+
text-style: bold;
232+
margin-bottom: 1;
233+
}
234+
235+
#save-file-source {
236+
text-align: center;
237+
color: $text-muted;
238+
margin-bottom: 1;
239+
}
240+
241+
#save-filename-input {
242+
width: 100%;
243+
margin-bottom: 1;
244+
}
245+
246+
#save-file-buttons {
247+
width: 100%;
248+
height: auto;
249+
align: center middle;
250+
}
251+
252+
#save-file-buttons Button {
253+
margin: 0 1;
254+
}

0 commit comments

Comments
 (0)