//! Blob-layout migration strip: a thin, cancellable progress row above the footer. //! //! Shown only while the background migration is relocating blobs from the legacy //! flat store root into hash-prefix shards. Deliberately a strip and not a modal or //! a full-screen mode: the migration auto-starts at vault open rather than being //! asked for, and the library stays completely usable while it runs, because reads //! resolve both layouts. Seizing the window for it would be the wrong trade. //! //! Cancelling is honest here. The sweep is resumable and records nothing until a //! pass verifies the root is clean, so stopping defers the remainder to the next //! open instead of abandoning or half-applying it. use egui; use super::theme; use crate::state::BrowserState; /// Draw the migration strip. No-op when no migration is running. pub fn draw_layout_strip(ui: &mut egui::Ui, state: &mut BrowserState) { let Some(progress) = state.layout_migration else { return; }; ui.add_space(theme::space::hair()); ui.horizontal(|ui| { ui.label( egui::RichText::new("Optimising storage layout") .small() .color(theme::content_secondary()), ); ui.label( egui::RichText::new(format!("{} / {} files", progress.completed, progress.total)) .small() .color(theme::content_muted()), ); // The button is laid out first from the right so the bar takes the // remaining width instead of pushing the button off the row. ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { if ui .small_button("Pause") .on_hover_text("Stop for now. Resumes the next time this vault opens.") .clicked() { if let Err(e) = state.backend.cancel_layout_migration() { tracing::warn!("failed to cancel layout migration: {e}"); } // Clear the strip immediately rather than waiting for the // worker's terminal event: the click has to feel like it did // something, and the Complete event that follows sets the status // line and would clear this anyway. state.layout_migration = None; } ui.add( egui::ProgressBar::new(progress.fraction()) .desired_height(theme::space::peer()) .show_percentage(), ); }); }); ui.add_space(theme::space::hair()); }