R/init-addin.R

Defines functions .chores_app .init_addin

Documented in .init_addin

#' Run the chores addin
#'
#' @description
#' The chores addin allows users to interactively select a chore helper to
#' interface with the current selection. **This function is not
#' intended to be interfaced with in regular usage of the package.**
#' To launch the chores addin in RStudio, navigate to `Addins > Chores`
#' and/or register the addin with a shortcut via
#' `Tools > Modify Keyboard Shortcuts > Search "Chores"`--we suggest `Ctrl+Alt+C`
#' (or `Ctrl+Cmd+C` on macOS).
#'
#' @returns
#' `NULL`, invisibly. Called for the side effect of launching the chores addin
#' and interfacing with selected text.
#'
#' @examples
#' if (interactive()) {
#'   .init_addin()
#' }
#' @export
.init_addin <- function() {
  if (is.null(fetch_chores_chat())) {
    return(invisible())
  }

  load_chores_directory()

  # suppress "Listening on..." message and rethrow errors with new context
  try_fetch(
    suppressMessages(helper_fn_name <- .chores_app()),
    error = function(cnd) {
      cli::cli_abort(conditionMessage(cnd), call = NULL)
    }
  )

  if (is.null(helper_fn_name) || identical(helper_fn_name, ".helper_rs_")) {
    return(invisible())
  }

  # call the binding associated with the chosen helper
  try_fetch(
    helper_fn <- env_get(chores_env(), helper_fn_name),
    error = function(e) {
      cli::cli_abort("Unable to locate the requested helper.", call = NULL)
    }
  )

  do.call(helper_fn, args = list())

  invisible()
}

.chores_app <- function() {
  helper_choices <- list_helpers()

  ui <- miniUI::miniPage(
    miniUI::miniContentPanel(
      shiny::selectizeInput(
        "helper",
        "Select a helper:",
        choices = helper_choices,
        multiple = FALSE,
        options = list(
          create = FALSE,
          placeholder = 'Type to filter or select a helper',
          onDropdownOpen = I("function($dropdown) {this.clear();}"),
          onBlur = I("function() {this.clear();}"),
          score = I(
            "function(search) {
                                           return function(item) {
                                             const text = (item.value || item.text || '').toLowerCase();
                                             const searchLower = search.toLowerCase();
                                             if (text.startsWith(searchLower)) return 1;
                                             if (text.includes(searchLower)) return 0.5;
                                             return 0;
                                           };
                                         }"
          )
        )
      ),
      shiny::verbatimTextOutput("result"),
      shiny::tags$script(shiny::HTML(
        "
        $(document).on('keyup', function(e) {
          if(e.key == 'Enter'){
            Shiny.setInputValue('done', true, {priority: 'event'});
          }
        });
        $(document).ready(function() {
          setTimeout(function() {
            $('.selectize-input input').focus();
            
            // clicking an option in the selectize should submit the selection
            $('select#helper').on('change', function() {
              if (this.value) {
                Shiny.setInputValue('done', true, {priority: 'event'});
              }
            });
          }, 100);
        });
      "
      )),
      shiny::tags$div(
        style = "position: absolute; bottom: 10px; left: 0; right: 0; color: #999;",
        "Press [Enter] to submit."
      )
    )
  )

  server <- function(input, output, session) {
    shiny::observeEvent(input$done, {
      shiny::stopApp(returnValue = paste0(".helper_rs_", input$helper))
    })
    shiny::onStop(function() {
      shiny::stopApp(returnValue = NULL)
    })
  }

  viewer <- shiny::dialogViewer("Chore helper", width = 300, height = 10)
  shiny::runGadget(ui, server, viewer = viewer)
}

Try the chores package in your browser

Any scripts or data that you put into this service are public.

chores documentation built on June 8, 2025, 11:19 a.m.