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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ Newest first. `Unreleased` is what is on `main` and not yet tagged.

## Unreleased

### The desktop setup's question box waits for a composed character before it asks

Enter confirms a character being typed through an input method (Japanese, Chinese, Korean). On the
final setup screen that Enter also sent the question, with its last character still unconfirmed. The
box now waits for the character to be confirmed, the way a chat composer does, and an ordinary Enter
still asks.
### Revoking a grant with a blank ref or Bot is refused instead of reported as done

`DELETE /api/plugins/grants` checked its query params with truthiness, and a query param is
Expand Down
35 changes: 34 additions & 1 deletion desktop/src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { afterAll, afterEach, beforeAll, expect, mock, test } from "bun:test";
import { GlobalRegistrator } from "@happy-dom/global-registrator";
import { act, cleanup, render, waitFor } from "@testing-library/react";
import {
act,
cleanup,
fireEvent,
render,
waitFor,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { StrictMode } from "react";

Expand Down Expand Up @@ -1870,3 +1876,30 @@ test("Start credential failures do not expose a restore action", async () => {
),
).toEqual([]);
});

test("the Enter that finishes a composed character does not ask the Bot", async () => {
useCompatibleEndpointSetup({});
const previous = invokeHandler;
invokeHandler = async (command, args) => {
if (command === "ask_the_bot") return "42";
return previous(command, args);
};
const view = await enterCompatibleEndpoint("https://models.example/v1");
await userEvent.click(view.getByRole("button", { name: "Continue" }));
await userEvent.click(view.getByRole("button", { name: "Start OpenBot" }));
const question = await view.findByLabelText("Your question");
// Keys land on the focused field.
await userEvent.click(question);

// Japanese, Chinese and Korean are typed through an input method, where Enter confirms the
// character being built. Chromium marks that keydown `isComposing`; the macOS WebKit webview
// sends it after compositionend with key code 229. Neither should send the question.
await act(async () => {
fireEvent.keyDown(question, { key: "Enter", isComposing: true });
fireEvent.keyDown(question, { key: "Enter", keyCode: 229 });
});

expect(invokeCalls.filter((call) => call.command === "ask_the_bot")).toEqual(
[],
);
});
11 changes: 10 additions & 1 deletion desktop/src/Ask.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,16 @@ export function Ask({
}}
disabled={asking}
onKeyDown={(event) => {
if (event.key === "Enter" && !asking) {
// Enter also confirms a character being composed through an input method (Japanese,
// Chinese, Korean). Asking on that Enter would send the question with its last character
// still unconfirmed. Chromium marks that keydown `isComposing`; the macOS WebKit webview
// instead sends it after compositionend with key code 229. Wait for either.
if (
event.key === "Enter" &&
!asking &&
!event.nativeEvent.isComposing &&
event.nativeEvent.keyCode !== 229
) {
ask();
}
}}
Expand Down