From 8826c1dc7acd6262da721c628bd1f0fbd5ff1674 Mon Sep 17 00:00:00 2001
From: Antoine Beyeler <49431240+abey79@users.noreply.github.com>
Date: Tue, 17 Oct 2023 16:44:18 +0200
Subject: [PATCH] Add warning in the Quick Start guides about Safari breaking
Copy to Clipboard (#3898)
### What
For security reason, Safari doesn't let use copy text from our Web
viewer, see:
- https://github.com/emilk/egui/issues/3480
This PR detects if we are running on Safari and adds a warning to the
top of the Quick Start guides.
### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested [demo.rerun.io](https://demo.rerun.io/pr/3898) (if
applicable)
* [x] The PR title and labels are set such as to maximize their
usefulness for the next release's CHANGELOG
- [PR Build Summary](https://build.rerun.io/pr/3898)
- [Docs
preview](https://rerun.io/preview/063e12defa92f5249e22c771c4fe0454231420cc/docs)
- [Examples
preview](https://rerun.io/preview/063e12defa92f5249e22c771c4fe0454231420cc/examples)
- [Recent benchmark results](https://ref.rerun.io/dev/bench/)
- [Wasm size tracking](https://ref.rerun.io/dev/sizes/)
---------
Co-authored-by: Emil Ernerfeldt
---
Cargo.lock | 1 -
crates/re_viewer/Cargo.toml | 1 -
.../data/quick_start_guides/python_native.md | 4 +
.../data/quick_start_guides/rust_native.md | 4 +
.../src/ui/welcome_screen/welcome_page.rs | 100 ++++++++++++++----
5 files changed, 85 insertions(+), 25 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index ffde5eef641e..a711ad56fc90 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4840,7 +4840,6 @@ dependencies = [
"thiserror",
"time",
"wasm-bindgen-futures",
- "web-sys",
"web-time",
"wgpu",
]
diff --git a/crates/re_viewer/Cargo.toml b/crates/re_viewer/Cargo.toml
index 497b1a7b3812..05515ab455c5 100644
--- a/crates/re_viewer/Cargo.toml
+++ b/crates/re_viewer/Cargo.toml
@@ -99,7 +99,6 @@ wgpu.workspace = true
# web dependencies:
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen-futures.workspace = true
-web-sys = { workspace = true, features = ["Window"] }
[build-dependencies]
re_build_tools.workspace = true
diff --git a/crates/re_viewer/data/quick_start_guides/python_native.md b/crates/re_viewer/data/quick_start_guides/python_native.md
index 6c33bde8ba82..53d094d41050 100644
--- a/crates/re_viewer/data/quick_start_guides/python_native.md
+++ b/crates/re_viewer/data/quick_start_guides/python_native.md
@@ -1,5 +1,7 @@
## Python Quick Start
+${SAFARI_WARNING}
+
### Installing the Rerun SDK
The Rerun SDK is available on [PyPI](https://pypi.org/) under the
@@ -31,3 +33,5 @@ Instead of a pre-packaged demo, you can log your own data. Copy and paste the fo
```python
${EXAMPLE_CODE}
```
+
+${HOW_DOES_IT_WORK}
diff --git a/crates/re_viewer/data/quick_start_guides/rust_native.md b/crates/re_viewer/data/quick_start_guides/rust_native.md
index 6fc3b86d96a3..687f7726ffd0 100644
--- a/crates/re_viewer/data/quick_start_guides/rust_native.md
+++ b/crates/re_viewer/data/quick_start_guides/rust_native.md
@@ -1,5 +1,7 @@
## Rust Quick Start
+${SAFARI_WARNING}
+
### Installing Rerun
To use the Rerun SDK in your project, you need the [rerun crate](https://crates.io/crates/rerun) which you can add with `cargo add rerun`.
@@ -29,3 +31,5 @@ cargo run
Once everything finishes compiling, you will see the points in this viewer:
![Demo recording](https://static.rerun.io/intro_rust_result/cc780eb9bf014d8b1a68fac174b654931f92e14f/768w.png)
+
+${HOW_DOES_IT_WORK}
diff --git a/crates/re_viewer/src/ui/welcome_screen/welcome_page.rs b/crates/re_viewer/src/ui/welcome_screen/welcome_page.rs
index cdab927e19a1..a6a4b6508270 100644
--- a/crates/re_viewer/src/ui/welcome_screen/welcome_page.rs
+++ b/crates/re_viewer/src/ui/welcome_screen/welcome_page.rs
@@ -1,6 +1,5 @@
use super::{large_text_button, status_strings, url_large_text_button, WelcomeScreenResponse};
use egui::{NumExt, Ui};
-use itertools::Itertools;
use re_data_store::StoreDb;
use re_log_types::{
DataRow, EntityPath, LogMsg, RowId, StoreId, StoreInfo, StoreKind, StoreSource, Time, TimePoint,
@@ -8,9 +7,12 @@ use re_log_types::{
use re_smart_channel::ReceiveSet;
use re_ui::UICommandSender;
use re_viewer_context::{SystemCommand, SystemCommandSender};
+use std::collections::HashMap;
const SPACE_VIEWS_HELP: &str = "https://www.rerun.io/docs/getting-started/viewer-walkthrough";
+const HOW_DOES_IT_WORK: &str = include_str!("../../../data/quick_start_guides/how_does_it_work.md");
+
/// Show the welcome page.
///
/// Return `true` if the user wants to switch to the example page.
@@ -66,15 +68,18 @@ fn onboarding_content_ui(
if large_text_button(ui, "C++").clicked() {
open_quick_start(
command_sender,
+ include_str!("../../../data/quick_start_guides/cpp_native.md"),
[
- include_str!("../../../data/quick_start_guides/cpp_native.md"),
- include_str!(
- "../../../data/quick_start_guides/how_does_it_work.md"
+ (
+ "EXAMPLE_CODE",
+ include_str!(
+ "../../../data/quick_start_guides/quick_start_connect.cpp"
+ ),
),
- ],
- include_str!(
- "../../../data/quick_start_guides/quick_start_connect.cpp"
- ),
+ ("HOW_DOES_IT_WORK", HOW_DOES_IT_WORK),
+ ("SAFARI_WARNING", safari_warning()),
+ ]
+ .into(),
"C++ Quick Start",
"cpp_quick_start",
);
@@ -83,11 +88,18 @@ fn onboarding_content_ui(
if large_text_button(ui, "Python").clicked() {
open_quick_start(
command_sender,
+ include_str!("../../../data/quick_start_guides/python_native.md"),
[
- include_str!("../../../data/quick_start_guides/python_native.md"),
- include_str!("../../../data/quick_start_guides/how_does_it_work.md"),
- ],
- include_str!("../../../data/quick_start_guides/quick_start_connect.py"),
+ (
+ "EXAMPLE_CODE",
+ include_str!(
+ "../../../data/quick_start_guides/quick_start_connect.py"
+ ),
+ ),
+ ("HOW_DOES_IT_WORK", HOW_DOES_IT_WORK),
+ ("SAFARI_WARNING", safari_warning()),
+ ]
+ .into(),
"Python Quick Start",
"python_quick_start",
);
@@ -95,11 +107,18 @@ fn onboarding_content_ui(
if large_text_button(ui, "Rust").clicked() {
open_quick_start(
command_sender,
+ include_str!("../../../data/quick_start_guides/rust_native.md"),
[
- include_str!("../../../data/quick_start_guides/rust_native.md"),
- include_str!("../../../data/quick_start_guides/how_does_it_work.md"),
- ],
- include_str!("../../../data/quick_start_guides/quick_start_connect.rs"),
+ (
+ "EXAMPLE_CODE",
+ include_str!(
+ "../../../data/quick_start_guides/quick_start_connect.rs"
+ ),
+ ),
+ ("HOW_DOES_IT_WORK", HOW_DOES_IT_WORK),
+ ("SAFARI_WARNING", safari_warning()),
+ ]
+ .into(),
"Rust Quick Start",
"rust_quick_start",
);
@@ -276,17 +295,20 @@ fn image_banner(ui: &mut egui::Ui, icon: &re_ui::Icon, column_width: f32, max_im
/// Open a Quick Start recording
///
-/// The `parts` are joined with newlines to form the markdown, and the spacial tag
-/// `"${EXAMPLE_CODE}"` is replaced with the content of th `example_code` variable.
-fn open_quick_start<'a>(
+/// The markdown content may contain placeholders in the form of `${NAME}`. These will be replaced
+/// with the corresponding value from the `placeholder_content` hash map.
+fn open_quick_start(
command_sender: &re_viewer_context::CommandSender,
- parts: impl IntoIterator- ,
- example_code: &str,
+ markdown: &str,
+ placeholder_content: HashMap<&'static str, &'static str>,
app_id: &str,
entity_path: &str,
) {
- let mut markdown = parts.into_iter().join("\n");
- markdown = markdown.replace("${EXAMPLE_CODE}", example_code);
+ let mut markdown = markdown.to_owned();
+
+ for (key, value) in placeholder_content {
+ markdown = markdown.replace(format!("${{{key}}}").as_str(), value);
+ }
let res = open_markdown_recording(command_sender, markdown.as_str(), app_id, entity_path);
if let Err(err) = res {
@@ -324,3 +346,35 @@ fn open_markdown_recording(
Ok(())
}
+
+/// The User-Agent of the user's browser.
+fn user_agent() -> Option {
+ #[cfg(target_arch = "wasm32")]
+ return eframe::web::user_agent();
+
+ #[cfg(not(target_arch = "wasm32"))]
+ None
+}
+
+/// Are we running on Safari?
+fn safari_warning() -> &'static str {
+ // Note that this implementation is very naive and might return false positives. This is ok for
+ // the purpose of displaying a "can't copy" warning in the Quick Start guide, but this detection
+ // is likely not suitable for pretty much anything else.
+ //
+ // See this page for more information on User Agent sniffing (and why/how to avoid it):
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
+
+ let is_safari = user_agent().is_some_and(|user_agent| {
+ user_agent.contains("Safari")
+ && !user_agent.contains("Chrome")
+ && !user_agent.contains("Chromium")
+ });
+
+ if is_safari {
+ "**Note**: This browser appears to be Safari. If you are unable to copy the code, please \
+ try a different browser (see [this issue](https://github.com/emilk/egui/issues/3480))."
+ } else {
+ ""
+ }
+}