Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
different ways (see #1845 for more details):
* By adding a `.suggestion` CSS class to an HTML element (e.g., `<span class="suggestion">A suggestion</span>`)
* Add a `data-suggestion` attribute to an HTML element, and set the value to the input suggestion text (e.g., `<span data-suggestion="Suggestion value">Suggestion link</span>`)
* To auto-submit the suggestion when clicked by the user, include the `.submit` class or the `data-suggestion-submit="true"` attribute on the HTML element.
* To auto-submit the suggestion when clicked by the user, include the `.submit` class or the `data-suggestion-submit="true"` attribute on the HTML element. Alternatively, use Cmd/Ctrl + click to auto-submit any suggestion or Alt/Opt + click to apply any suggestion to the chat input without submitting.

* Added a new `.add_sass_layer_file()` method to `ui.Theme` that supports reading a Sass file with layer boundary comments, e.g. `/*-- scss:defaults --*/`. This format [is supported by Quarto](https://quarto.org/docs/output-formats/html-themes-more.html#bootstrap-bootswatch-layering) and makes it easier to store Sass rules and declarations that need to be woven into Shiny's Sass Bootstrap files. (#1790)

Expand Down
22 changes: 15 additions & 7 deletions js/chat/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,23 +410,31 @@ class ChatContainer extends LightElement {
}
}

#onInputSuggestionClick(e: Event): void {
this.#applySuggestion(e);
#onInputSuggestionClick(e: MouseEvent): void {
this.#onInputSuggestionEvent(e);
}

#onInputSuggestionKeydown(e: KeyboardEvent): void {
const isEnter = e.key === "Enter" || e.key === " ";
if (!isEnter) return;
const isEnterOrSpace = e.key === "Enter" || e.key === " ";
if (!isEnterOrSpace) return;

this.#applySuggestion(e);
this.#onInputSuggestionEvent(e);
}

#applySuggestion(e: Event | KeyboardEvent): void {
#onInputSuggestionEvent(e: MouseEvent | KeyboardEvent): void {
const { suggestion, submit } = this.#getSuggestion(e.target);
if (!suggestion) return;

e.preventDefault();
this.input.setInputValue(suggestion, { submit, focus: !submit });
// Cmd/Ctrl + (event) = force submitting
// Alt/Opt + (event) = force setting without submitting
const shouldSubmit =
e.metaKey || e.ctrlKey ? true : e.altKey ? false : submit;

this.input.setInputValue(suggestion, {
submit: shouldSubmit,
focus: !shouldSubmit,
});
}

#getSuggestion(x: EventTarget | null): {
Expand Down
6 changes: 3 additions & 3 deletions shiny/www/py-shiny/chat/chat.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions shiny/www/py-shiny/chat/chat.js.map

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,36 @@ def test_validate_chat_input_suggestion(page: Page, local_app: ShinyAppProc) ->
chat.expect_user_input("")
expect(chat.loc_input_button).to_be_disabled()
expect(chat.loc_input).not_to_be_focused()

# Reset chat
chat.set_user_input("reset")
chat.send_user_input()
chat.expect_latest_message("You said: reset")

# Test keyboard modifiers - Alt + event = set but do not submit
fourth.click(modifiers=["Alt"])
chat.expect_user_input("this suggestion will auto-submit")
chat.expect_latest_message("You said: reset")

fifth.focus()
page.keyboard.press("Alt+Enter")
chat.expect_user_input("another suggestion")
chat.expect_latest_message("You said: reset")

# Reset chat
chat.send_user_input()
chat.expect_user_input("")

# Test keyboard modifiers - Cmd/Ctrl + event = submit the suggestion
first.click(modifiers=["ControlOrMeta"])
chat.expect_latest_message("You said: 1st input suggestion")
chat.expect_user_input("")
expect(chat.loc_input_button).to_be_disabled()
expect(chat.loc_input).not_to_be_focused()

second.focus()
page.keyboard.press("ControlOrMeta+Enter")
chat.expect_latest_message("You said: The actual suggestion")
chat.expect_user_input("")
expect(chat.loc_input_button).to_be_disabled()
expect(chat.loc_input).not_to_be_focused()
Loading