From 10c1f5c17ebde80c2dc3087d425393a4097910ba Mon Sep 17 00:00:00 2001
From: Antoine Beyeler <49431240+abey79@users.noreply.github.com>
Date: Tue, 26 Nov 2024 09:50:08 +0100
Subject: [PATCH] Introduce `RecordingUri` component (#8210)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
### What
This PR introduces a new `RecordingUri` component (which contains just a
string) and assorted UI, which present the URI as a link that opens said
component. Also introduce a new `FileSource::Uri` to account for this
new way of opening recordings.
Note: displaying links is disabled in wasm build, because
`DataSource::from_uri()` is not available there. I'm actually not quite
sure what the story is to load remote recordings from the web viewer 🤷🏻
---
Cargo.lock | 3 +
crates/store/re_log_types/src/lib.rs | 10 +-
.../re_types/definitions/rerun/components.fbs | 1 +
.../rerun/components/recording_uri.fbs | 11 ++
.../re_types/src/components/.gitattributes | 1 +
crates/store/re_types/src/components/mod.rs | 3 +
.../re_types/src/components/recording_uri.rs | 104 ++++++++++++++++++
.../src/components/recording_uri_ext.rs | 8 ++
crates/viewer/re_component_ui/Cargo.toml | 3 +
crates/viewer/re_component_ui/src/lib.rs | 3 +
.../re_component_ui/src/recording_uri.rs | 50 +++++++++
crates/viewer/re_viewer/src/reflection/mod.rs | 8 ++
.../re_viewer/src/viewer_analytics/event.rs | 1 +
docs/content/reference/types/components.md | 1 +
.../reference/types/components/.gitattributes | 1 +
.../types/components/recording_uri.md | 22 ++++
.../content/reference/types/datatypes/utf8.md | 1 +
lychee.toml | 1 +
rerun_cpp/src/rerun/components.hpp | 1 +
rerun_cpp/src/rerun/components/.gitattributes | 1 +
.../src/rerun/components/recording_uri.hpp | 76 +++++++++++++
.../rerun_sdk/rerun/components/.gitattributes | 1 +
.../rerun_sdk/rerun/components/__init__.py | 3 +
.../rerun/components/recording_uri.py | 32 ++++++
.../check_all_components_ui.py | 6 +
25 files changed, 349 insertions(+), 3 deletions(-)
create mode 100644 crates/store/re_types/definitions/rerun/components/recording_uri.fbs
create mode 100644 crates/store/re_types/src/components/recording_uri.rs
create mode 100644 crates/store/re_types/src/components/recording_uri_ext.rs
create mode 100644 crates/viewer/re_component_ui/src/recording_uri.rs
create mode 100644 docs/content/reference/types/components/recording_uri.md
create mode 100644 rerun_cpp/src/rerun/components/recording_uri.hpp
create mode 100644 rerun_py/rerun_sdk/rerun/components/recording_uri.py
diff --git a/Cargo.lock b/Cargo.lock
index 4785e53ca5c7..cf7855a4b885 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5390,8 +5390,11 @@ dependencies = [
"egui",
"egui_extras",
"egui_plot",
+ "re_data_source",
"re_data_ui",
"re_format",
+ "re_log",
+ "re_log_types",
"re_tracing",
"re_types",
"re_types_blueprint",
diff --git a/crates/store/re_log_types/src/lib.rs b/crates/store/re_log_types/src/lib.rs
index 5f25a628e376..5c3f52b88b87 100644
--- a/crates/store/re_log_types/src/lib.rs
+++ b/crates/store/re_log_types/src/lib.rs
@@ -410,6 +410,9 @@ impl std::fmt::Display for PythonVersion {
pub enum FileSource {
Cli,
+ /// The user clicked on a recording URI in the viewer.
+ Uri,
+
DragAndDrop {
/// The [`ApplicationId`] that the viewer heuristically recommends should be used when loading
/// this data source, based on the surrounding context.
@@ -463,7 +466,7 @@ impl FileSource {
recommended_application_id,
..
} => recommended_application_id.as_ref(),
- Self::Cli | Self::Sdk => None,
+ Self::Cli | Self::Uri | Self::Sdk => None,
}
}
@@ -478,7 +481,7 @@ impl FileSource {
recommended_recording_id,
..
} => recommended_recording_id.as_ref(),
- Self::Cli | Self::Sdk => None,
+ Self::Cli | Self::Uri | Self::Sdk => None,
}
}
@@ -491,7 +494,7 @@ impl FileSource {
| Self::DragAndDrop {
force_store_info, ..
} => *force_store_info,
- Self::Cli | Self::Sdk => false,
+ Self::Cli | Self::Uri | Self::Sdk => false,
}
}
}
@@ -538,6 +541,7 @@ impl std::fmt::Display for StoreSource {
Self::RustSdk { rustc_version, .. } => write!(f, "Rust SDK (rustc {rustc_version})"),
Self::File { file_source, .. } => match file_source {
FileSource::Cli => write!(f, "File via CLI"),
+ FileSource::Uri => write!(f, "File via URI"),
FileSource::DragAndDrop { .. } => write!(f, "File via drag-and-drop"),
FileSource::FileDialog { .. } => write!(f, "File via file dialog"),
FileSource::Sdk => write!(f, "File via SDK"),
diff --git a/crates/store/re_types/definitions/rerun/components.fbs b/crates/store/re_types/definitions/rerun/components.fbs
index ca625053e29f..57a3c7a1b851 100644
--- a/crates/store/re_types/definitions/rerun/components.fbs
+++ b/crates/store/re_types/definitions/rerun/components.fbs
@@ -41,6 +41,7 @@ include "./components/position2d.fbs";
include "./components/position3d.fbs";
include "./components/radius.fbs";
include "./components/range1d.fbs";
+include "./components/recording_uri.fbs";
include "./components/resolution.fbs";
include "./components/rotation_axis_angle.fbs";
include "./components/rotation_quat.fbs";
diff --git a/crates/store/re_types/definitions/rerun/components/recording_uri.fbs b/crates/store/re_types/definitions/rerun/components/recording_uri.fbs
new file mode 100644
index 000000000000..e27204f5251d
--- /dev/null
+++ b/crates/store/re_types/definitions/rerun/components/recording_uri.fbs
@@ -0,0 +1,11 @@
+namespace rerun.components;
+
+// ---
+
+/// A recording URI (Uniform Resource Identifier).
+table RecordingUri (
+ "attr.rust.derive": "PartialEq, Eq, PartialOrd, Ord, Hash",
+ "attr.docs.unreleased"
+) {
+ recording_uri: rerun.datatypes.Utf8 (order: 100);
+}
diff --git a/crates/store/re_types/src/components/.gitattributes b/crates/store/re_types/src/components/.gitattributes
index 0a17e84f4293..f45278457d5c 100644
--- a/crates/store/re_types/src/components/.gitattributes
+++ b/crates/store/re_types/src/components/.gitattributes
@@ -47,6 +47,7 @@ position2d.rs linguist-generated=true
position3d.rs linguist-generated=true
radius.rs linguist-generated=true
range1d.rs linguist-generated=true
+recording_uri.rs linguist-generated=true
resolution.rs linguist-generated=true
rotation_axis_angle.rs linguist-generated=true
rotation_quat.rs linguist-generated=true
diff --git a/crates/store/re_types/src/components/mod.rs b/crates/store/re_types/src/components/mod.rs
index 76ca71fd8475..44993beca085 100644
--- a/crates/store/re_types/src/components/mod.rs
+++ b/crates/store/re_types/src/components/mod.rs
@@ -80,6 +80,8 @@ mod radius;
mod radius_ext;
mod range1d;
mod range1d_ext;
+mod recording_uri;
+mod recording_uri_ext;
mod resolution;
mod resolution_ext;
mod rotation_axis_angle;
@@ -168,6 +170,7 @@ pub use self::position2d::Position2D;
pub use self::position3d::Position3D;
pub use self::radius::Radius;
pub use self::range1d::Range1D;
+pub use self::recording_uri::RecordingUri;
pub use self::resolution::Resolution;
pub use self::rotation_axis_angle::RotationAxisAngle;
pub use self::rotation_quat::RotationQuat;
diff --git a/crates/store/re_types/src/components/recording_uri.rs b/crates/store/re_types/src/components/recording_uri.rs
new file mode 100644
index 000000000000..9f3c09c9693e
--- /dev/null
+++ b/crates/store/re_types/src/components/recording_uri.rs
@@ -0,0 +1,104 @@
+// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/rust/api.rs
+// Based on "crates/store/re_types/definitions/rerun/components/recording_uri.fbs".
+
+#![allow(unused_imports)]
+#![allow(unused_parens)]
+#![allow(clippy::clone_on_copy)]
+#![allow(clippy::cloned_instead_of_copied)]
+#![allow(clippy::map_flatten)]
+#![allow(clippy::needless_question_mark)]
+#![allow(clippy::new_without_default)]
+#![allow(clippy::redundant_closure)]
+#![allow(clippy::too_many_arguments)]
+#![allow(clippy::too_many_lines)]
+
+use ::re_types_core::external::arrow2;
+use ::re_types_core::ComponentName;
+use ::re_types_core::SerializationResult;
+use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch};
+use ::re_types_core::{DeserializationError, DeserializationResult};
+
+/// **Component**: A recording URI (Uniform Resource Identifier).
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct RecordingUri(pub crate::datatypes::Utf8);
+
+impl ::re_types_core::SizeBytes for RecordingUri {
+ #[inline]
+ fn heap_size_bytes(&self) -> u64 {
+ self.0.heap_size_bytes()
+ }
+
+ #[inline]
+ fn is_pod() -> bool {
+ ::is_pod()
+ }
+}
+
+impl> From for RecordingUri {
+ fn from(v: T) -> Self {
+ Self(v.into())
+ }
+}
+
+impl std::borrow::Borrow for RecordingUri {
+ #[inline]
+ fn borrow(&self) -> &crate::datatypes::Utf8 {
+ &self.0
+ }
+}
+
+impl std::ops::Deref for RecordingUri {
+ type Target = crate::datatypes::Utf8;
+
+ #[inline]
+ fn deref(&self) -> &crate::datatypes::Utf8 {
+ &self.0
+ }
+}
+
+impl std::ops::DerefMut for RecordingUri {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut crate::datatypes::Utf8 {
+ &mut self.0
+ }
+}
+
+::re_types_core::macros::impl_into_cow!(RecordingUri);
+
+impl ::re_types_core::Loggable for RecordingUri {
+ #[inline]
+ fn arrow_datatype() -> arrow::datatypes::DataType {
+ crate::datatypes::Utf8::arrow_datatype()
+ }
+
+ fn to_arrow_opt<'a>(
+ data: impl IntoIterator- >>>,
+ ) -> SerializationResult
+ where
+ Self: Clone + 'a,
+ {
+ crate::datatypes::Utf8::to_arrow_opt(data.into_iter().map(|datum| {
+ datum.map(|datum| match datum.into() {
+ ::std::borrow::Cow::Borrowed(datum) => ::std::borrow::Cow::Borrowed(&datum.0),
+ ::std::borrow::Cow::Owned(datum) => ::std::borrow::Cow::Owned(datum.0),
+ })
+ }))
+ }
+
+ fn from_arrow2_opt(
+ arrow_data: &dyn arrow2::array::Array,
+ ) -> DeserializationResult>>
+ where
+ Self: Sized,
+ {
+ crate::datatypes::Utf8::from_arrow2_opt(arrow_data)
+ .map(|v| v.into_iter().map(|v| v.map(Self)).collect())
+ }
+}
+
+impl ::re_types_core::Component for RecordingUri {
+ #[inline]
+ fn name() -> ComponentName {
+ "rerun.components.RecordingUri".into()
+ }
+}
diff --git a/crates/store/re_types/src/components/recording_uri_ext.rs b/crates/store/re_types/src/components/recording_uri_ext.rs
new file mode 100644
index 000000000000..b3b65916a310
--- /dev/null
+++ b/crates/store/re_types/src/components/recording_uri_ext.rs
@@ -0,0 +1,8 @@
+use super::RecordingUri;
+
+impl RecordingUri {
+ /// Return the Recording URI contained in this component.
+ pub fn uri(&self) -> &str {
+ self.0.as_str()
+ }
+}
diff --git a/crates/viewer/re_component_ui/Cargo.toml b/crates/viewer/re_component_ui/Cargo.toml
index 51f7ea4d25c9..c6a0f3329325 100644
--- a/crates/viewer/re_component_ui/Cargo.toml
+++ b/crates/viewer/re_component_ui/Cargo.toml
@@ -20,7 +20,10 @@ all-features = true
[dependencies]
re_data_ui.workspace = true # Needed for `item_ui`.
+re_data_source.workspace = true
re_format.workspace = true
+re_log.workspace = true
+re_log_types.workspace = true
re_tracing.workspace = true
re_types = { workspace = true, features = [
"egui_plot", # Needed to draw marker shapes.
diff --git a/crates/viewer/re_component_ui/src/lib.rs b/crates/viewer/re_component_ui/src/lib.rs
index f5ac978d76f7..10dd4ad28647 100644
--- a/crates/viewer/re_component_ui/src/lib.rs
+++ b/crates/viewer/re_component_ui/src/lib.rs
@@ -15,6 +15,7 @@ mod map_provider;
mod marker_shape;
mod pinhole;
mod radius;
+mod recording_uri;
mod resolution;
mod response_utils;
mod timeline;
@@ -158,6 +159,8 @@ pub fn create_component_ui_registry() -> re_viewer_context::ComponentUiRegistry
registry.add_singleline_edit_or_view(pinhole::singleline_view_pinhole);
registry.add_multiline_edit_or_view(pinhole::multiline_view_pinhole);
+ registry.add_singleline_edit_or_view(recording_uri::singleline_view_recording_uri);
+
line_strip::register_linestrip_component_ui(&mut registry);
geo_line_string::register_geo_line_string_component_ui(&mut registry);
diff --git a/crates/viewer/re_component_ui/src/recording_uri.rs b/crates/viewer/re_component_ui/src/recording_uri.rs
new file mode 100644
index 000000000000..465c6d543bfd
--- /dev/null
+++ b/crates/viewer/re_component_ui/src/recording_uri.rs
@@ -0,0 +1,50 @@
+use re_types::components::RecordingUri;
+use re_viewer_context::{MaybeMutRef, ViewerContext};
+
+pub fn singleline_view_recording_uri(
+ _ctx: &ViewerContext<'_>,
+ ui: &mut egui::Ui,
+ value: &mut MaybeMutRef<'_, RecordingUri>,
+) -> egui::Response {
+ let value = value.as_ref();
+
+ #[cfg(not(target_arch = "wasm32"))]
+ {
+ use re_viewer_context::{SystemCommand, SystemCommandSender};
+
+ let response = ui
+ .scope(|ui| {
+ if ui.style().wrap_mode.is_none() {
+ ui.style_mut().wrap_mode = Some(if ui.is_sizing_pass() {
+ egui::TextWrapMode::Extend
+ } else {
+ egui::TextWrapMode::Truncate
+ });
+ }
+
+ ui.link(value.uri())
+ })
+ .inner;
+
+ if response.clicked() {
+ let data_source = re_data_source::DataSource::from_uri(
+ re_log_types::FileSource::Uri,
+ value.uri().to_owned(),
+ );
+
+ match data_source.stream(None) {
+ Ok(rx) => _ctx
+ .command_sender
+ .send_system(SystemCommand::AddReceiver(rx)),
+ Err(err) => re_log::warn!("Could not open recording URI: {err}"),
+ }
+ }
+
+ response
+ }
+
+ #[cfg(target_arch = "wasm32")]
+ {
+ re_viewer_context::UiLayout::List.label(ui, value.uri())
+ }
+}
diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs
index d8ac61f325a1..5e0bc0622d33 100644
--- a/crates/viewer/re_viewer/src/reflection/mod.rs
+++ b/crates/viewer/re_viewer/src/reflection/mod.rs
@@ -680,6 +680,14 @@ fn generate_component_reflection() -> Result::name(),
+ ComponentReflection {
+ docstring_md: "A recording URI (Uniform Resource Identifier).",
+ custom_placeholder: None,
+ datatype: RecordingUri::arrow2_datatype(),
+ },
+ ),
(
::name(),
ComponentReflection {
diff --git a/crates/viewer/re_viewer/src/viewer_analytics/event.rs b/crates/viewer/re_viewer/src/viewer_analytics/event.rs
index 0e2b7d821496..6c845c6114ca 100644
--- a/crates/viewer/re_viewer/src/viewer_analytics/event.rs
+++ b/crates/viewer/re_viewer/src/viewer_analytics/event.rs
@@ -75,6 +75,7 @@ pub fn open_recording(
S::RustSdk { .. } => "rust_sdk".to_owned(),
S::File { file_source } => match file_source {
re_log_types::FileSource::Cli => "file_cli".to_owned(),
+ re_log_types::FileSource::Uri => "file_uri".to_owned(),
re_log_types::FileSource::DragAndDrop { .. } => "file_drag_and_drop".to_owned(),
re_log_types::FileSource::FileDialog { .. } => "file_dialog".to_owned(),
re_log_types::FileSource::Sdk => "file_sdk".to_owned(),
diff --git a/docs/content/reference/types/components.md b/docs/content/reference/types/components.md
index e95094cca0c5..84907d1b19b5 100644
--- a/docs/content/reference/types/components.md
+++ b/docs/content/reference/types/components.md
@@ -59,6 +59,7 @@ on [Entities and Components](../../concepts/entity-component.md).
* [`Position3D`](components/position3d.md): A position in 3D space.
* [`Radius`](components/radius.md): The radius of something, e.g. a point.
* [`Range1D`](components/range1d.md): A 1D range, specifying a lower and upper bound.
+* [`RecordingUri`](components/recording_uri.md): A recording URI (Uniform Resource Identifier).
* [`Resolution`](components/resolution.md): Pixel resolution width & height, e.g. of a camera sensor.
* [`RotationAxisAngle`](components/rotation_axis_angle.md): 3D rotation represented by a rotation around a given axis.
* [`RotationQuat`](components/rotation_quat.md): A 3D rotation expressed as a quaternion.
diff --git a/docs/content/reference/types/components/.gitattributes b/docs/content/reference/types/components/.gitattributes
index 7d03d1206dc9..4cb988eea78b 100644
--- a/docs/content/reference/types/components/.gitattributes
+++ b/docs/content/reference/types/components/.gitattributes
@@ -47,6 +47,7 @@ position2d.md linguist-generated=true
position3d.md linguist-generated=true
radius.md linguist-generated=true
range1d.md linguist-generated=true
+recording_uri.md linguist-generated=true
resolution.md linguist-generated=true
rotation_axis_angle.md linguist-generated=true
rotation_quat.md linguist-generated=true
diff --git a/docs/content/reference/types/components/recording_uri.md b/docs/content/reference/types/components/recording_uri.md
new file mode 100644
index 000000000000..bcfe34c05aff
--- /dev/null
+++ b/docs/content/reference/types/components/recording_uri.md
@@ -0,0 +1,22 @@
+---
+title: "RecordingUri"
+---
+
+
+A recording URI (Uniform Resource Identifier).
+
+## Rerun datatype
+[`Utf8`](../datatypes/utf8.md)
+
+
+## Arrow datatype
+```
+utf8
+```
+
+## API reference links
+ * 🌊 [C++ API docs for `RecordingUri`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1components_1_1RecordingUri.html?speculative-link)
+ * 🐍 [Python API docs for `RecordingUri`](https://ref.rerun.io/docs/python/stable/common/components?speculative-link#rerun.components.RecordingUri)
+ * 🦀 [Rust API docs for `RecordingUri`](https://docs.rs/rerun/latest/rerun/components/struct.RecordingUri.html?speculative-link)
+
+
diff --git a/docs/content/reference/types/datatypes/utf8.md b/docs/content/reference/types/datatypes/utf8.md
index 1c8ec70eca28..d7f5aa99ea30 100644
--- a/docs/content/reference/types/datatypes/utf8.md
+++ b/docs/content/reference/types/datatypes/utf8.md
@@ -23,6 +23,7 @@ utf8
* [`GraphNode`](../components/graph_node.md?speculative-link)
* [`MediaType`](../components/media_type.md)
* [`Name`](../components/name.md)
+* [`RecordingUri`](../components/recording_uri.md?speculative-link)
* [`TextLogLevel`](../components/text_log_level.md)
* [`Text`](../components/text.md)
* [`Utf8Pair`](../datatypes/utf8pair.md?speculative-link)
diff --git a/lychee.toml b/lychee.toml
index 127e5ebea33f..ef6fe68e3b38 100644
--- a/lychee.toml
+++ b/lychee.toml
@@ -113,6 +113,7 @@ exclude = [
'https://static.rerun.io/my_screenshot/',
'https://your-hosted-asset-url.com/widget.js',
'file:///path/to/file',
+ 'rerun://localhost:51234/recording/some-recording-id',
# Link fragments and data links in examples.
'https://raw.githubusercontent.com/googlefonts/noto-emoji/', # URL fragment.
diff --git a/rerun_cpp/src/rerun/components.hpp b/rerun_cpp/src/rerun/components.hpp
index fd37481c6293..57eab0f62570 100644
--- a/rerun_cpp/src/rerun/components.hpp
+++ b/rerun_cpp/src/rerun/components.hpp
@@ -48,6 +48,7 @@
#include "components/position3d.hpp"
#include "components/radius.hpp"
#include "components/range1d.hpp"
+#include "components/recording_uri.hpp"
#include "components/resolution.hpp"
#include "components/rotation_axis_angle.hpp"
#include "components/rotation_quat.hpp"
diff --git a/rerun_cpp/src/rerun/components/.gitattributes b/rerun_cpp/src/rerun/components/.gitattributes
index 011cb60f9609..312f4f62e67b 100644
--- a/rerun_cpp/src/rerun/components/.gitattributes
+++ b/rerun_cpp/src/rerun/components/.gitattributes
@@ -57,6 +57,7 @@ position2d.hpp linguist-generated=true
position3d.hpp linguist-generated=true
radius.hpp linguist-generated=true
range1d.hpp linguist-generated=true
+recording_uri.hpp linguist-generated=true
resolution.hpp linguist-generated=true
rotation_axis_angle.hpp linguist-generated=true
rotation_quat.hpp linguist-generated=true
diff --git a/rerun_cpp/src/rerun/components/recording_uri.hpp b/rerun_cpp/src/rerun/components/recording_uri.hpp
new file mode 100644
index 000000000000..147e91eafd7a
--- /dev/null
+++ b/rerun_cpp/src/rerun/components/recording_uri.hpp
@@ -0,0 +1,76 @@
+// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs
+// Based on "crates/store/re_types/definitions/rerun/components/recording_uri.fbs".
+
+#pragma once
+
+#include "../datatypes/utf8.hpp"
+#include "../result.hpp"
+
+#include
+#include
+#include
+#include
+
+namespace rerun::components {
+ /// **Component**: A recording URI (Uniform Resource Identifier).
+ struct RecordingUri {
+ rerun::datatypes::Utf8 recording_uri;
+
+ public:
+ RecordingUri() = default;
+
+ RecordingUri(rerun::datatypes::Utf8 recording_uri_)
+ : recording_uri(std::move(recording_uri_)) {}
+
+ RecordingUri& operator=(rerun::datatypes::Utf8 recording_uri_) {
+ recording_uri = std::move(recording_uri_);
+ return *this;
+ }
+
+ RecordingUri(std::string value_) : recording_uri(std::move(value_)) {}
+
+ RecordingUri& operator=(std::string value_) {
+ recording_uri = std::move(value_);
+ return *this;
+ }
+
+ /// Cast to the underlying Utf8 datatype
+ operator rerun::datatypes::Utf8() const {
+ return recording_uri;
+ }
+ };
+} // namespace rerun::components
+
+namespace rerun {
+ static_assert(sizeof(rerun::datatypes::Utf8) == sizeof(components::RecordingUri));
+
+ /// \private
+ template <>
+ struct Loggable {
+ static constexpr const char Name[] = "rerun.components.RecordingUri";
+
+ /// Returns the arrow data type this type corresponds to.
+ static const std::shared_ptr& arrow_datatype() {
+ return Loggable::arrow_datatype();
+ }
+
+ /// Serializes an array of `rerun::components::RecordingUri` into an arrow array.
+ static Result> to_arrow(
+ const components::RecordingUri* instances, size_t num_instances
+ ) {
+ if (num_instances == 0) {
+ return Loggable::to_arrow(nullptr, 0);
+ } else if (instances == nullptr) {
+ return rerun::Error(
+ ErrorCode::UnexpectedNullArgument,
+ "Passed array instances is null when num_elements> 0."
+ );
+ } else {
+ return Loggable::to_arrow(
+ &instances->recording_uri,
+ num_instances
+ );
+ }
+ }
+ };
+} // namespace rerun
diff --git a/rerun_py/rerun_sdk/rerun/components/.gitattributes b/rerun_py/rerun_sdk/rerun/components/.gitattributes
index 8f860f91817f..d9db90a272aa 100644
--- a/rerun_py/rerun_sdk/rerun/components/.gitattributes
+++ b/rerun_py/rerun_sdk/rerun/components/.gitattributes
@@ -48,6 +48,7 @@ position2d.py linguist-generated=true
position3d.py linguist-generated=true
radius.py linguist-generated=true
range1d.py linguist-generated=true
+recording_uri.py linguist-generated=true
resolution.py linguist-generated=true
rotation_axis_angle.py linguist-generated=true
rotation_quat.py linguist-generated=true
diff --git a/rerun_py/rerun_sdk/rerun/components/__init__.py b/rerun_py/rerun_sdk/rerun/components/__init__.py
index 10ec22321fd2..0b3141a8eb38 100644
--- a/rerun_py/rerun_sdk/rerun/components/__init__.py
+++ b/rerun_py/rerun_sdk/rerun/components/__init__.py
@@ -63,6 +63,7 @@
from .position3d import Position3D, Position3DBatch
from .radius import Radius, RadiusBatch
from .range1d import Range1D, Range1DBatch
+from .recording_uri import RecordingUri, RecordingUriBatch
from .resolution import Resolution, ResolutionBatch
from .rotation_axis_angle import RotationAxisAngle, RotationAxisAngleBatch
from .rotation_quat import RotationQuat, RotationQuatBatch
@@ -205,6 +206,8 @@
"RadiusBatch",
"Range1D",
"Range1DBatch",
+ "RecordingUri",
+ "RecordingUriBatch",
"Resolution",
"ResolutionBatch",
"RotationAxisAngle",
diff --git a/rerun_py/rerun_sdk/rerun/components/recording_uri.py b/rerun_py/rerun_sdk/rerun/components/recording_uri.py
new file mode 100644
index 000000000000..52220d7f92d7
--- /dev/null
+++ b/rerun_py/rerun_sdk/rerun/components/recording_uri.py
@@ -0,0 +1,32 @@
+# DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/python/mod.rs
+# Based on "crates/store/re_types/definitions/rerun/components/recording_uri.fbs".
+
+# You can extend this class by creating a "RecordingUriExt" class in "recording_uri_ext.py".
+
+from __future__ import annotations
+
+from .. import datatypes
+from .._baseclasses import (
+ ComponentBatchMixin,
+ ComponentMixin,
+)
+
+__all__ = ["RecordingUri", "RecordingUriBatch"]
+
+
+class RecordingUri(datatypes.Utf8, ComponentMixin):
+ """**Component**: A recording URI (Uniform Resource Identifier)."""
+
+ _BATCH_TYPE = None
+ # You can define your own __init__ function as a member of RecordingUriExt in recording_uri_ext.py
+
+ # Note: there are no fields here because RecordingUri delegates to datatypes.Utf8
+ pass
+
+
+class RecordingUriBatch(datatypes.Utf8Batch, ComponentBatchMixin):
+ _COMPONENT_NAME: str = "rerun.components.RecordingUri"
+
+
+# This is patched in late to avoid circular dependencies.
+RecordingUri._BATCH_TYPE = RecordingUriBatch # type: ignore[assignment]
diff --git a/tests/python/release_checklist/check_all_components_ui.py b/tests/python/release_checklist/check_all_components_ui.py
index 705f91b3b3b5..cd9cab6cfecc 100644
--- a/tests/python/release_checklist/check_all_components_ui.py
+++ b/tests/python/release_checklist/check_all_components_ui.py
@@ -195,6 +195,12 @@ def alternatives(self) -> list[Any] | None:
"Position3DBatch": TestCase(batch=[(0, 3, 4), (1, 4, 5), (2, 5, 6)]),
"RadiusBatch": TestCase(batch=[4.5, 5, 6, 7]),
"Range1DBatch": TestCase((0, 5)),
+ "RecordingUriBatch": TestCase(
+ batch=[
+ rr.components.RecordingUri("file:///path/to/file"),
+ rr.components.RecordingUri("rerun://localhost:51234/recording/some-recording-id"),
+ ]
+ ),
"ResolutionBatch": TestCase((1920, 1080)),
"RotationAxisAngleBatch": TestCase(
rr.datatypes.RotationAxisAngle(axis=(1, 0, 0), angle=rr.datatypes.Angle(rad=math.pi))