diff --git a/crates/amalthea/src/comm/connections_comm.rs b/crates/amalthea/src/comm/connections_comm.rs index 3120e3d45..9bdaba064 100644 --- a/crates/amalthea/src/comm/connections_comm.rs +++ b/crates/amalthea/src/comm/connections_comm.rs @@ -18,7 +18,12 @@ pub struct ObjectSchema { pub name: String, /// The object type (table, catalog, schema) - pub kind: String + pub kind: String, + + /// Indicates if the object has children that can be listed. This property + /// is optional and when omitted, it is assumed that the object may have + /// children unless its kind is 'field'. + pub has_children: Option } /// FieldSchema in Schemas diff --git a/crates/amalthea/src/comm/ui_comm.rs b/crates/amalthea/src/comm/ui_comm.rs index 09dbc2a02..f4b14b8e7 100644 --- a/crates/amalthea/src/comm/ui_comm.rs +++ b/crates/amalthea/src/comm/ui_comm.rs @@ -98,6 +98,17 @@ pub struct Range { pub end: Position } +/// Source information for preview content +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct PreviewSource { + /// The type of source that opened the preview + #[serde(rename = "type")] + pub preview_source_type: PreviewSourceType, + + /// The ID of the source (session_id or terminal process ID) + pub id: String +} + /// Possible values for Kind in OpenEditor #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, strum_macros::Display, strum_macros::EnumString)] pub enum OpenEditorKind { @@ -110,6 +121,34 @@ pub enum OpenEditorKind { Uri } +/// Possible values for Destination in ShowHtmlFile +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, strum_macros::Display, strum_macros::EnumString)] +pub enum ShowHtmlFileDestination { + #[serde(rename = "plot")] + #[strum(to_string = "plot")] + Plot, + + #[serde(rename = "viewer")] + #[strum(to_string = "viewer")] + Viewer, + + #[serde(rename = "editor")] + #[strum(to_string = "editor")] + Editor +} + +/// Possible values for Type in PreviewSource +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, strum_macros::Display, strum_macros::EnumString)] +pub enum PreviewSourceType { + #[serde(rename = "runtime")] + #[strum(to_string = "runtime")] + Runtime, + + #[serde(rename = "terminal")] + #[strum(to_string = "terminal")] + Terminal +} + /// Parameters for the DidChangePlotsRenderSettings method. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct DidChangePlotsRenderSettingsParams { @@ -305,6 +344,9 @@ pub struct ModifyEditorSelectionsParams { pub struct ShowUrlParams { /// The URL to display pub url: String, + + /// Optional source information for the URL + pub source: Option, } /// Parameters for the ShowHtmlFile method. @@ -317,8 +359,9 @@ pub struct ShowHtmlFileParams { /// superseded by the title in the HTML file. pub title: String, - /// Whether the HTML file is a plot-like object - pub is_plot: bool, + /// Where the file should be shown in Positron: as an interactive plot, in + /// the viewer pane, or in a new editor tab. + pub destination: ShowHtmlFileDestination, /// The desired height of the HTML viewer, in pixels. The special value 0 /// indicates that no particular height is desired, and -1 indicates that diff --git a/crates/ark/src/connections/r_connection.rs b/crates/ark/src/connections/r_connection.rs index 09fc2bac2..bb30f50ec 100644 --- a/crates/ark/src/connections/r_connection.rs +++ b/crates/ark/src/connections/r_connection.rs @@ -126,6 +126,7 @@ impl RConnection { .map(|(name, kind)| ObjectSchema { name: name.clone(), kind: kind.clone(), + has_children: None, }) .collect::>(); diff --git a/crates/ark/src/modules/positron/html_widgets.R b/crates/ark/src/modules/positron/html_widgets.R index f68cef4ef..7cd957aa8 100644 --- a/crates/ark/src/modules/positron/html_widgets.R +++ b/crates/ark/src/modules/positron/html_widgets.R @@ -1,7 +1,7 @@ # # html_widgets.R # -# Copyright (C) 2023-2024 Posit Software, PBC. All rights reserved. +# Copyright (C) 2023-2025 Posit Software, PBC. All rights reserved. # # #' @export @@ -14,7 +14,7 @@ tmp_file <- htmltools::html_print(rendered, viewer = NULL) # Guess whether this is a plot-like widget based on its sizing policy. - is_plot <- isTRUE(x$sizingPolicy$knitr$figure) + destination <- if (isTRUE(x$sizingPolicy$knitr$figure)) "plot" else "viewer" # Derive the height of the viewer pane from the sizing policy of the widget. height <- .ps.validate.viewer.height(x$sizingPolicy$viewer$paneHeight) @@ -30,7 +30,7 @@ # Pass the widget to the viewer. Positron will assemble the final HTML # document from these components. - .ps.Call("ps_html_viewer", tmp_file, label, height, is_plot) + .ps.Call("ps_html_viewer", tmp_file, label, height, destination) } #' @export diff --git a/crates/ark/src/modules/positron/options.R b/crates/ark/src/modules/positron/options.R index cd25d3504..cdd6e6d0a 100644 --- a/crates/ark/src/modules/positron/options.R +++ b/crates/ark/src/modules/positron/options.R @@ -1,7 +1,7 @@ # # options.R # -# Copyright (C) 2023-2024 Posit Software, PBC. All rights reserved. +# Copyright (C) 2023-2025 Posit Software, PBC. All rights reserved. # # @@ -36,3 +36,16 @@ options(plumber.docs.callback = function(url) { options(shiny.launch.browser = function(url) { .ps.ui.showUrl(url) }) + +# Show Profvis output in the viewer +options(profvis.print = function(x) { + # Render the widget to a tag list to create standalone HTML output. + # (htmltools is a Profvis dependency so it's guaranteed to be available) + rendered <- htmltools::as.tags(x, standalone = TRUE) + + # Render the HTML content to a temporary file + tmp_file <- htmltools::html_print(rendered, viewer = NULL) + + # Pass the file to the viewer + .ps.Call("ps_html_viewer", tmp_file, "R Profile", -1L, "editor") +}) diff --git a/crates/ark/src/ui/events.rs b/crates/ark/src/ui/events.rs index 9aa47f96f..10cc37860 100644 --- a/crates/ark/src/ui/events.rs +++ b/crates/ark/src/ui/events.rs @@ -108,6 +108,7 @@ pub unsafe extern "C-unwind" fn ps_ui_set_selection_ranges(ranges: SEXP) -> anyh pub fn send_show_url_event(url: &str) -> anyhow::Result<()> { let params = ShowUrlParams { url: url.to_string(), + source: None, }; let event = UiFrontendEvent::ShowUrl(params); diff --git a/crates/ark/src/viewer.rs b/crates/ark/src/viewer.rs index cc060bec6..2a99e5838 100644 --- a/crates/ark/src/viewer.rs +++ b/crates/ark/src/viewer.rs @@ -5,6 +5,7 @@ // // +use amalthea::comm::ui_comm::ShowHtmlFileDestination; use amalthea::comm::ui_comm::ShowHtmlFileParams; use amalthea::comm::ui_comm::UiFrontendEvent; use amalthea::socket::iopub::IOPubMessage; @@ -53,7 +54,7 @@ pub unsafe extern "C-unwind" fn ps_html_viewer( url: SEXP, label: SEXP, height: SEXP, - is_plot: SEXP, + destination: SEXP, ) -> anyhow::Result { // Convert url to a string; note that we are only passed URLs that // correspond to files in the temporary directory. @@ -76,12 +77,18 @@ pub unsafe extern "C-unwind" fn ps_html_viewer( } }, SessionMode::Console => { - let is_plot = RObject::view(is_plot).to::(); - let is_plot = match is_plot { - Ok(is_plot) => is_plot, + let destination = match RObject::view(destination).to::() { + Ok(s) => s.parse::().unwrap_or_else(|_| { + log::warn!( + "`destination` must be one of 'plot', 'editor', or 'viewer', using 'viewer' as a fallback." + ); + ShowHtmlFileDestination::Viewer + }), Err(err) => { - log::warn!("Can't convert `is_plot` into a bool, using `false` as a fallback: {err:?}"); - false + log::warn!( + "Can't convert `destination` to a string, using 'viewer' as a fallback: {err}" + ); + ShowHtmlFileDestination::Viewer }, }; @@ -98,7 +105,7 @@ pub unsafe extern "C-unwind" fn ps_html_viewer( path, title: label, height, - is_plot, + destination, }; let event = UiFrontendEvent::ShowHtmlFile(params); diff --git a/crates/ark/tests/connections.rs b/crates/ark/tests/connections.rs index 2d663ddfa..0331c4135 100644 --- a/crates/ark/tests/connections.rs +++ b/crates/ark/tests/connections.rs @@ -70,6 +70,7 @@ fn obj(name: &str, kind: &str) -> ObjectSchema { ObjectSchema { name: String::from(name), kind: String::from(kind), + has_children: None, } }