feat(dialogs): reopen file pickers in the last-used directory

Every rfd dialog result funnels through sys::handle_path, which now
records the picked file's folder (persisted to the config dir), and all
pickers are built via sys::file_dialog(), seeded with that folder.
Save-As keeps the current tab's own directory as the first choice and
falls back to the remembered one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-07-21 09:27:44 +03:00
commit e78ad7884d
7 changed files with 78 additions and 15 deletions

View file

@ -39,7 +39,11 @@ pub async fn read_clipboard_text() -> Option<String> {
/// (a follow-up).
#[cfg(not(target_arch = "wasm32"))]
pub fn handle_path(h: &rfd::FileHandle) -> std::path::PathBuf {
h.path().to_path_buf()
let p = h.path().to_path_buf();
// Every dialog result funnels through here — remember its folder so the
// next dialog opens where the user left off.
crate::config::remember_dialog_dir(&p);
p
}
#[cfg(target_arch = "wasm32")]
@ -47,6 +51,23 @@ pub fn handle_path(h: &rfd::FileHandle) -> std::path::PathBuf {
std::path::PathBuf::from(h.file_name())
}
/// New async file dialog seeded with the last directory a dialog was used in.
/// All pickers should start from this instead of `rfd::AsyncFileDialog::new()`.
#[cfg(not(target_arch = "wasm32"))]
pub fn file_dialog() -> rfd::AsyncFileDialog {
let dlg = rfd::AsyncFileDialog::new();
match crate::config::last_dialog_dir() {
Some(dir) => dlg.set_directory(dir),
None => dlg,
}
}
/// Web: no filesystem paths, nothing to remember.
#[cfg(target_arch = "wasm32")]
pub fn file_dialog() -> rfd::AsyncFileDialog {
rfd::AsyncFileDialog::new()
}
/// Trigger a browser download of `bytes` as `name`. Builds a Blob, points a
/// hidden `<a download>` at it and clicks it programmatically — because this
/// runs inside the Save button's click (a user gesture), the file downloads