diff --git a/crates/re_ui/src/command.rs b/crates/re_ui/src/command.rs index fb8424f5fc43..22fe42c951c0 100644 --- a/crates/re_ui/src/command.rs +++ b/crates/re_ui/src/command.rs @@ -119,7 +119,7 @@ impl UICommand { Self::ResetViewer => ( "Reset Viewer", - "Reset the Viewer to how it looked the first time you ran it", + "Reset the Viewer to how it looked the first time you ran it, forgetting all stored blueprints and UI state", ), #[cfg(not(target_arch = "wasm32"))] diff --git a/crates/re_viewer/Cargo.toml b/crates/re_viewer/Cargo.toml index 749db3cd6c08..5eb8fb025b16 100644 --- a/crates/re_viewer/Cargo.toml +++ b/crates/re_viewer/Cargo.toml @@ -113,6 +113,7 @@ web-sys = { workspace = true, features = [ 'Url', 'UrlSearchParams', 'Window', + "Storage", ] } diff --git a/crates/re_viewer/src/app.rs b/crates/re_viewer/src/app.rs index 303f1794190b..117b74a34285 100644 --- a/crates/re_viewer/src/app.rs +++ b/crates/re_viewer/src/app.rs @@ -374,7 +374,7 @@ impl App { // By clearing the blueprint it will be re-populated with the defaults // at the beginning of the next frame. re_log::debug!("Reset blueprint"); - store_hub.clear_blueprint(); + store_hub.clear_current_blueprint(); } SystemCommand::UpdateBlueprint(blueprint_id, updates) => { // We only want to update the blueprint if the "inspect blueprint timeline" mode is @@ -1048,12 +1048,17 @@ impl App { /// Reset the viewer to how it looked the first time you ran it. fn reset(&mut self, store_hub: &mut StoreHub, egui_ctx: &egui::Context) { self.state = Default::default(); - store_hub.clear_blueprint(); - // Keep the style: + store_hub.clear_all_blueprints(); + + // Reset egui, but keep the style: let style = egui_ctx.style(); egui_ctx.memory_mut(|mem| *mem = Default::default()); egui_ctx.set_style((*style).clone()); + + if let Err(err) = crate::reset_viewer_persistence() { + re_log::warn!("Failed to reset viewer: {err}"); + } } pub fn recording_db(&self) -> Option<&EntityDb> { diff --git a/crates/re_viewer/src/lib.rs b/crates/re_viewer/src/lib.rs index dd325eba9612..d7f194780a73 100644 --- a/crates/re_viewer/src/lib.rs +++ b/crates/re_viewer/src/lib.rs @@ -234,3 +234,51 @@ pub fn wake_up_ui_thread_on_each_msg( .unwrap(); new_rx } + +/// Reset the viewer state as stored on disk and local storage, +/// keeping only the analytics state. +#[allow(clippy::unnecessary_wraps)] // wasm only +pub fn reset_viewer_persistence() -> anyhow::Result<()> { + #[cfg(not(target_arch = "wasm32"))] + { + let Some(data_dir) = eframe::storage_dir(native::APP_ID) else { + anyhow::bail!("Failed to figure out where Rerun stores its data.") + }; + + // Note: `remove_dir_all` fails if the directory doesn't exist. + if data_dir.exists() { + // Keep analytics, because it is used to uniquely identify users over time. + let analytics_file_path = data_dir.join("analytics.json"); + let analytics = std::fs::read(&analytics_file_path); + + if let Err(err) = std::fs::remove_dir_all(&data_dir) { + anyhow::bail!("Failed to remove {data_dir:?}: {err}"); + } else { + re_log::info!("Cleared {data_dir:?}."); + } + + if let Ok(analytics) = analytics { + // Restore analytics.json: + std::fs::create_dir(&data_dir).ok(); + std::fs::write(&analytics_file_path, analytics).ok(); + } + } else { + re_log::info!("Rerun state was already cleared."); + } + } + #[cfg(target_arch = "wasm32")] + { + // TODO(emilk): eframe should have an API for this. + if let Some(storage) = web_sys::window() + .and_then(|w| w.local_storage().ok()) + .flatten() + { + storage.delete("egui_memory_ron").ok(); + storage.delete(eframe::APP_KEY).ok(); + } + + // TODO(#2579): implement web-storage for blueprints as well, and clear it here + } + + Ok(()) +} diff --git a/crates/re_viewer/src/store_hub.rs b/crates/re_viewer/src/store_hub.rs index 2fc9710a87e6..5cb6ff9c6f8b 100644 --- a/crates/re_viewer/src/store_hub.rs +++ b/crates/re_viewer/src/store_hub.rs @@ -186,7 +186,7 @@ impl StoreHub { } /// Clear the current blueprint - pub fn clear_blueprint(&mut self) { + pub fn clear_current_blueprint(&mut self) { if let Some(app_id) = &self.selected_application_id { if let Some(blueprint_id) = self.blueprint_by_app_id.remove(app_id) { self.store_bundle.remove(&blueprint_id); @@ -194,6 +194,13 @@ impl StoreHub { } } + /// Forgets all blueprints + pub fn clear_all_blueprints(&mut self) { + for (_app_id, blueprint_id) in self.blueprint_by_app_id.drain() { + self.store_bundle.remove(&blueprint_id); + } + } + /// Insert a new recording into the [`StoreHub`]. /// /// Note that the recording is not automatically made active. Use [`StoreHub::set_recording_id`] diff --git a/crates/rerun/src/run.rs b/crates/rerun/src/run.rs index 8274093477db..c571bd2b4b1c 100644 --- a/crates/rerun/src/run.rs +++ b/crates/rerun/src/run.rs @@ -381,7 +381,7 @@ where Command::Print(print_command) => print_command.run(), #[cfg(feature = "native_viewer")] - Command::Reset => reset_viewer(), + Command::Reset => re_viewer::reset_viewer_persistence(), } } else { run_impl(build_info, call_source, args).await @@ -880,34 +880,3 @@ fn parse_max_latency(max_latency: Option<&String>) -> f32 { .unwrap_or_else(|err| panic!("Failed to parse max_latency ({max_latency:?}): {err}")) }) } - -#[cfg(feature = "native_viewer")] -fn reset_viewer() -> anyhow::Result<()> { - let Some(data_dir) = re_viewer::external::eframe::storage_dir(re_viewer::native::APP_ID) else { - anyhow::bail!("Failed to figure out where Rerun stores its data.") - }; - - // Note: `remove_dir_all` fails if the directory doesn't exist. - if data_dir.exists() { - // Keep analytics, because it is used to uniquely identify users over time. - let analytics_file_path = data_dir.join("analytics.json"); - let analytics = std::fs::read(&analytics_file_path); - - if let Err(err) = std::fs::remove_dir_all(&data_dir) { - anyhow::bail!("Failed to remove {data_dir:?}: {err}"); - } else { - eprintln!("Cleared {data_dir:?}."); - } - - if let Ok(analytics) = analytics { - // Restore analytics.json: - std::fs::create_dir(&data_dir).ok(); - std::fs::write(&analytics_file_path, analytics).ok(); - } - - Ok(()) - } else { - eprintln!("Rerun state was already cleared."); - Ok(()) - } -}