From e98f2323817678436626e330dc6e54eb9a155c06 Mon Sep 17 00:00:00 2001 From: Jonathan McPherson Date: Thu, 11 Dec 2025 13:24:10 -0800 Subject: [PATCH 1/6] send profvis output to editor --- crates/amalthea/src/comm/connections_comm.rs | 7 ++- crates/amalthea/src/comm/ui_comm.rs | 47 ++++++++++++++++++- crates/ark/src/connections/r_connection.rs | 1 + crates/ark/src/interface.rs | 2 +- .../ark/src/modules/positron/html_widgets.R | 6 +-- crates/ark/src/modules/positron/options.R | 11 ++++- crates/ark/src/ui/events.rs | 1 + crates/ark/src/viewer.rs | 21 ++++++--- crates/ark/tests/connections.rs | 1 + 9 files changed, 82 insertions(+), 15 deletions(-) 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/interface.rs b/crates/ark/src/interface.rs index b72894eae..3d6564345 100644 --- a/crates/ark/src/interface.rs +++ b/crates/ark/src/interface.rs @@ -337,7 +337,7 @@ impl PendingInputs { let input = if harp::get_option_bool("keep.source") { _srcfile = Some(SrcFile::new_virtual_empty_filename(input.into())); - harp::ParseInput::SrcFile(&_srcfile.unwrap()) + harp::ParseInput::SrcFile(_srcfile.as_ref().unwrap()) } else { harp::ParseInput::Text(input) }; diff --git a/crates/ark/src/modules/positron/html_widgets.R b/crates/ark/src/modules/positron/html_widgets.R index f68cef4ef..1d203cf99 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..146f10750 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,12 @@ 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 HTML content to a temporary file + tmp_file <- htmltools::html_print(x, 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..d87ab381e 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'" + ); + 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, } } From 0c4af7ff7b723780b4ca1980b6168da39d049a0b Mon Sep 17 00:00:00 2001 From: Jonathan Date: Fri, 12 Dec 2025 16:03:21 -0800 Subject: [PATCH 2/6] Update crates/ark/src/modules/positron/html_widgets.R Co-authored-by: Lionel Henry --- crates/ark/src/modules/positron/html_widgets.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ark/src/modules/positron/html_widgets.R b/crates/ark/src/modules/positron/html_widgets.R index 1d203cf99..7cd957aa8 100644 --- a/crates/ark/src/modules/positron/html_widgets.R +++ b/crates/ark/src/modules/positron/html_widgets.R @@ -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. - destination <- if (isTRUE(x$sizingPolicy$knitr$figure)) 'plot' else 'viewer' + 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) From 5aea33fcb7afdeb7e9aa04a7b6672855f2a0c91b Mon Sep 17 00:00:00 2001 From: Jonathan McPherson Date: Fri, 12 Dec 2025 16:06:31 -0800 Subject: [PATCH 3/6] revert srcfile change --- crates/ark/src/interface.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ark/src/interface.rs b/crates/ark/src/interface.rs index 3d6564345..b72894eae 100644 --- a/crates/ark/src/interface.rs +++ b/crates/ark/src/interface.rs @@ -337,7 +337,7 @@ impl PendingInputs { let input = if harp::get_option_bool("keep.source") { _srcfile = Some(SrcFile::new_virtual_empty_filename(input.into())); - harp::ParseInput::SrcFile(_srcfile.as_ref().unwrap()) + harp::ParseInput::SrcFile(&_srcfile.unwrap()) } else { harp::ParseInput::Text(input) }; From d28e119a5565e9ce28249d6466d935bae5a25afb Mon Sep 17 00:00:00 2001 From: Jonathan Date: Mon, 15 Dec 2025 15:54:35 -0800 Subject: [PATCH 4/6] Update crates/ark/src/viewer.rs Co-authored-by: Davis Vaughan --- crates/ark/src/viewer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ark/src/viewer.rs b/crates/ark/src/viewer.rs index d87ab381e..2a99e5838 100644 --- a/crates/ark/src/viewer.rs +++ b/crates/ark/src/viewer.rs @@ -80,7 +80,7 @@ pub unsafe extern "C-unwind" fn ps_html_viewer( 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'" + "`destination` must be one of 'plot', 'editor', or 'viewer', using 'viewer' as a fallback." ); ShowHtmlFileDestination::Viewer }), From 9877008a279b15ddbbc5e6bc1e4b07c9fff88ec3 Mon Sep 17 00:00:00 2001 From: Jonathan McPherson Date: Mon, 15 Dec 2025 16:05:23 -0800 Subject: [PATCH 5/6] clarify htmltools dependency --- crates/ark/src/modules/positron/options.R | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ark/src/modules/positron/options.R b/crates/ark/src/modules/positron/options.R index 146f10750..d135e41f1 100644 --- a/crates/ark/src/modules/positron/options.R +++ b/crates/ark/src/modules/positron/options.R @@ -40,6 +40,7 @@ options(shiny.launch.browser = function(url) { # Show Profvis output in the viewer options(profvis.print = function(x) { # Render the HTML content to a temporary file + # (htmltools is a profvis dependency so it's guaranteed to be available) tmp_file <- htmltools::html_print(x, viewer = NULL) # Pass the file to the viewer From db02e6fa3269d14e797d958dd1286c00fd95c7d8 Mon Sep 17 00:00:00 2001 From: Jonathan McPherson Date: Mon, 15 Dec 2025 17:10:04 -0800 Subject: [PATCH 6/6] enforce standalone mode --- crates/ark/src/modules/positron/options.R | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/ark/src/modules/positron/options.R b/crates/ark/src/modules/positron/options.R index d135e41f1..cdd6e6d0a 100644 --- a/crates/ark/src/modules/positron/options.R +++ b/crates/ark/src/modules/positron/options.R @@ -39,9 +39,12 @@ options(shiny.launch.browser = function(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 - # (htmltools is a profvis dependency so it's guaranteed to be available) - tmp_file <- htmltools::html_print(x, viewer = NULL) + 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")