Summary
chat_app.R has a standing TODO to "Support a standalone register_slash_command() that works outside the returned environment (e.g., so callers don't have to thread the return value)." This proposes exactly that.
Motivation
Slash commands are currently only reachable through chat_server()'s $slash_command() method. That has two friction points:
- You must thread the returned object around. To register a command from anywhere other than right next to the
chat_server() call, you have to capture and pass its return value.
- Fully custom chat loops can't use slash commands at all. The Get started vignette says as much: "Slash commands are currently available only through
chat_server(), not when building a fully custom chat loop with chat_ui() and chat_append() directly." Apps that drive chat_ui() with their own server logic (custom streaming, an external agent loop) can't offer the official palette.
Proposal
Export a free function that mirrors $slash_command() but is callable anywhere with just the chat id and session:
register_slash_command(
id, name, description, handler, ...,
echo = NULL, force = FALSE,
session = getDefaultReactiveDomain()
)
- Same
handler semantics as $slash_command() (0/1-arg, or NULL for client-side handling via shiny:chat-slash-command); a 1-arg handler receives a ContentSlashCommand.
- The command registry lives in
session$userData keyed by the namespaced id (the same idiom already used for bookmark info in chat_restore.R), and the dispatch + sync observers are set up once, lazily — so no return value needs threading.
- The slash machinery is factored into an internal helper that
chat_server() reuses, so both paths share a single implementation and can't drift.
Follows shinychat's argument conventions (id first, session last, matching chat_append() / chat_clear() / update_chat_user_input()).
Open question
R's $slash_command() returns an unregister closure; I've kept that for register_slash_command(). Would you prefer a separate remove_slash_command(id, name, session) for parity with the Python Chat.remove_slash_command() method, or is returning the unregister function the right R idiom here? Happy to adjust.
I have a draft PR ready that implements this as a pure refactor (no behavior change to chat_server()), with standalone tests and a clean R CMD check.
Summary
chat_app.Rhas a standingTODOto "Support a standaloneregister_slash_command()that works outside the returned environment (e.g., so callers don't have to thread the return value)." This proposes exactly that.Motivation
Slash commands are currently only reachable through
chat_server()'s$slash_command()method. That has two friction points:chat_server()call, you have to capture and pass its return value.chat_server(), not when building a fully custom chat loop withchat_ui()andchat_append()directly." Apps that drivechat_ui()with their own server logic (custom streaming, an external agent loop) can't offer the official palette.Proposal
Export a free function that mirrors
$slash_command()but is callable anywhere with just the chatidandsession:handlersemantics as$slash_command()(0/1-arg, orNULLfor client-side handling viashiny:chat-slash-command); a 1-arg handler receives aContentSlashCommand.session$userDatakeyed by the namespacedid(the same idiom already used for bookmark info inchat_restore.R), and the dispatch + sync observers are set up once, lazily — so no return value needs threading.chat_server()reuses, so both paths share a single implementation and can't drift.Follows shinychat's argument conventions (
idfirst,sessionlast, matchingchat_append()/chat_clear()/update_chat_user_input()).Open question
R's
$slash_command()returns an unregister closure; I've kept that forregister_slash_command(). Would you prefer a separateremove_slash_command(id, name, session)for parity with the PythonChat.remove_slash_command()method, or is returning the unregister function the right R idiom here? Happy to adjust.I have a draft PR ready that implements this as a pure refactor (no behavior change to
chat_server()), with standalone tests and a cleanR CMD check.