Skip to content

feat(r): standalone register_slash_command() - #274

Open
kaipingyang wants to merge 1 commit into
posit-dev:mainfrom
kaipingyang:feat/standalone-register-slash-command
Open

feat(r): standalone register_slash_command()#274
kaipingyang wants to merge 1 commit into
posit-dev:mainfrom
kaipingyang:feat/standalone-register-slash-command

Conversation

@kaipingyang

Copy link
Copy Markdown

Fixes the standing TODO in chat_app.R for a standalone register_slash_command(). See #273 for motivation and the proposed API.

What this does

Factors the slash-command registration machinery out of chat_server() into:

  • register_slash_command(id, name, description, handler, ..., echo = NULL, force = FALSE, session = getDefaultReactiveDomain()) — exported. A free function you can call from anywhere with just the chat id and session; no need to thread chat_server()'s return value, and it works with a fully custom chat_ui() + chat_append() loop (no chat_server() required).
  • slash_commands_registry(id, session) — internal. Get-or-create the per-(session, id) command registry, wiring the dispatch + sync observers exactly once. State lives in session$userData keyed by the namespaced id, matching the chat_restore.R idiom.

chat_server()'s $slash_command() is now a thin wrapper over register_slash_command(), so both paths share one implementation. This is a pure refactor — no behavior change to the existing chat_server() API.

Semantics (unchanged from $slash_command())

  • handler: 0-/1-arg function, or NULL for client-side handling via the cancelable shiny:chat-slash-command event. A 1-arg handler receives a ContentSlashCommand.
  • echo defaults to TRUE when a handler is supplied, FALSE otherwise.
  • force overwrites an existing command; returns an unregister function.
  • Argument order follows chat_append() / chat_clear() (id first, session last).

Test plan

  • Existing chat_server slash tests still pass (updated the 3 that reached into the old closure to read the registry from its new location).
  • New register_slash_command() tests: 0-/1-arg dispatch, ContentSlashCommand payload, validation, echo defaults, force, unregister, single-registry-per-(session, id).
  • Full suite green (devtools::test(), 516 tests, 0 failures).
  • R CMD check: 0 errors, 0 warnings (remaining NOTES are pre-existing and unrelated).
  • Verified end-to-end in a real headless browser: the palette renders and commands dispatch for both chat_server() and standalone register_slash_command(), including inside a Shiny module, the 0-arg path, the client-side NULL-handler + preventDefault() path, and the content@textchat_append() pattern.

Open question

I kept the unregister-closure return (R idiom). If you'd rather have a remove_slash_command() for parity with Python's Chat.remove_slash_command(), I'm happy to add it.

Factor the slash-command registration machinery out of chat_server() into an
exported register_slash_command(id, ...) plus an internal
slash_commands_registry() that both paths share. This fills the existing TODO
in chat_app.R for a standalone entry point that works outside chat_server()'s
returned environment, so callers don't have to thread the return value and can
add the official palette to a chat_ui() driven by fully custom server logic.

The registry lives in session$userData keyed by the namespaced id (the same
idiom as chat_restore.R); the dispatch + sync observers are set up once, lazily.
chat_server()'s $slash_command() becomes a thin wrapper over the same code.

Adds standalone tests alongside the existing chat_server slash tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant