diff --git a/pkg-r/NAMESPACE b/pkg-r/NAMESPACE index 56116f09..5bb459a5 100644 --- a/pkg-r/NAMESPACE +++ b/pkg-r/NAMESPACE @@ -10,6 +10,7 @@ export(chat_mod_ui) export(chat_ui) export(markdown_stream) export(output_markdown_stream) +export(update_chat_user_input) if (getRversion() < "4.3.0") importFrom("S7", "@") import(S7) import(rlang) diff --git a/pkg-r/NEWS.md b/pkg-r/NEWS.md index 941e15b1..7b9d9d60 100644 --- a/pkg-r/NEWS.md +++ b/pkg-r/NEWS.md @@ -1,8 +1,11 @@ # shinychat (development version) -## New features and improvements +## New features * Added `chat_enable_bookmarking()` which adds Shiny bookmarking hooks to save and restore the `{ellmer}` chat client. (#28) +* Added `update_chat_user_input()` for programmatically updating the user input of a chat UI element. (#78) + +## Improvements * `chat_app()` now correctly restores the chat client state when refreshing the app, e.g. by reloading the page. (#71) diff --git a/pkg-r/R/chat.R b/pkg-r/R/chat.R index 8683a701..8237e8a9 100644 --- a/pkg-r/R/chat.R +++ b/pkg-r/R/chat.R @@ -541,3 +541,83 @@ chat_clear <- function(id, session = getDefaultReactiveDomain()) { ) ) } + + +#' Update the user input of a chat control +#' +#' @param id The ID of the chat element +#' @param ... Currently unused, but reserved for future use. +#' @param value The value to set the user input to. If `NULL`, the input will not be updated. +#' @param placeholder The placeholder text for the user input +#' @param submit Whether to automatically submit the text for the user. Requires `value`. +#' @param focus Whether to move focus to the input element. Requires `value`. +#' @param session The Shiny session object +#' +#' @export +#' @examplesIf interactive() +#' library(shiny) +#' library(bslib) +#' library(shinychat) +#' +#' ui <- page_fillable( +#' chat_ui("chat"), +#' layout_columns( +#' fill = FALSE, +#' actionButton("update_placeholder", "Update placeholder"), +#' actionButton("update_value", "Update user input") +#' ) +#' ) +#' +#' server <- function(input, output, session) { +#' observeEvent(input$update_placeholder, { +#' update_chat_user_input("chat", placeholder = "New placeholder text") +#' }) +#' +#' observeEvent(input$update_value, { +#' update_chat_user_input("chat", value = "New user input", focus = TRUE) +#' }) +#' +#' observeEvent(input$chat_user_input, { +#' response <- paste0("You said: ", input$chat_user_input) +#' chat_append("chat", response) +#' }) +#' } +#' +#' shinyApp(ui, server) + +update_chat_user_input <- function( + id, + ..., + value = NULL, + placeholder = NULL, + submit = FALSE, + focus = FALSE, + session = getDefaultReactiveDomain() +) { + rlang::check_dots_empty() + check_active_session(session) + + if (is.null(value) && (submit || focus)) { + rlang::abort( + "An input `value` must be provided when `submit` or `focus` are `TRUE`." + ) + } + + vals <- drop_nulls( + list( + value = value, + placeholder = placeholder, + submit = submit, + focus = focus + ) + ) + + session$sendCustomMessage( + "shinyChatMessage", + list( + id = resolve_id(id, session), + handler = "shiny-chat-update-user-input", + obj = vals + ) + ) +} diff --git a/pkg-r/R/utils.R b/pkg-r/R/utils.R index e1373871..ca4a09d9 100644 --- a/pkg-r/R/utils.R +++ b/pkg-r/R/utils.R @@ -18,3 +18,8 @@ strip_ansi <- function(text) { ansi_pattern <- "(\x1B|\x033)\\[[0-9;?=<>]*[@-~]" gsub(ansi_pattern, "", text) } + + +drop_nulls <- function(x) { + x[!vapply(x, is.null, FUN.VALUE = logical(1))] +} diff --git a/pkg-r/man/update_chat_user_input.Rd b/pkg-r/man/update_chat_user_input.Rd new file mode 100644 index 00000000..828a546f --- /dev/null +++ b/pkg-r/man/update_chat_user_input.Rd @@ -0,0 +1,67 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/chat.R +\name{update_chat_user_input} +\alias{update_chat_user_input} +\title{Update the user input of a chat control} +\usage{ +update_chat_user_input( + id, + ..., + value = NULL, + placeholder = NULL, + submit = FALSE, + focus = FALSE, + session = getDefaultReactiveDomain() +) +} +\arguments{ +\item{id}{The ID of the chat element} + +\item{...}{Currently unused, but reserved for future use.} + +\item{value}{The value to set the user input to. If \code{NULL}, the input will not be updated.} + +\item{placeholder}{The placeholder text for the user input} + +\item{submit}{Whether to automatically submit the text for the user. Requires \code{value}.} + +\item{focus}{Whether to move focus to the input element. Requires \code{value}.} + +\item{session}{The Shiny session object} +} +\description{ +Update the user input of a chat control +} +\examples{ +\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +library(shiny) +library(bslib) +library(shinychat) + +ui <- page_fillable( + chat_ui("chat"), + layout_columns( + fill = FALSE, + actionButton("update_placeholder", "Update placeholder"), + actionButton("update_value", "Update user input") + ) +) + +server <- function(input, output, session) { + observeEvent(input$update_placeholder, { + update_chat_user_input("chat", placeholder = "New placeholder text") + }) + + observeEvent(input$update_value, { + update_chat_user_input("chat", value = "New user input", focus = TRUE) + }) + + observeEvent(input$chat_user_input, { + response <- paste0("You said: ", input$chat_user_input) + chat_append("chat", response) + }) +} + +shinyApp(ui, server) +\dontshow{\}) # examplesIf} +}