fix assistant focus and settings

This commit is contained in:
ajnart committed 2026-08-14 11:32:11 +02:00
1 parent b7fdff9f7f
commit fc5ba7d888
6 files changed
+145 -74

No files matched your search

@@ -16,6 +16,7 @@ import {
MessagePrimitive,
ThreadPrimitive,
useAui,
useAuiEvent,
useLocalRuntime,
} from "@assistant-ui/react";
import { LexicalComposerInput } from "@assistant-ui/react-lexical";
@@ -118,14 +119,25 @@ const SurfaceProbe = ({ id, onReady }: SurfaceProbeProps) => {
} as ComponentProps<typeof LexicalComposerInput>);
};
const LexicalRunFocusProbe = () => {
useAuiEvent("thread.runStart", () => {
queueMicrotask(() => {
document.querySelector<HTMLElement>('[data-testid="input-b"] [contenteditable="true"]')?.focus();
});
});
return null;
};
const SurfaceTestAssistant = ({
onReady,
onRuntimeReady,
allowImages = true,
simulateRunFocus = false,
}: {
onReady: SurfaceProbeProps["onReady"];
onRuntimeReady: (runtime: AssistantRuntime) => void;
allowImages?: boolean;
simulateRunFocus?: boolean;
}) => {
const runtime = useLocalRuntime(noOpAdapter, {
adapters: { attachments: createAttachmentAdapter(allowImages ? "image/png,text/plain" : "text/plain") },
@@ -145,10 +157,14 @@ const SurfaceTestAssistant = ({
{ surfaceId: "surface-b" },
createElement(SurfaceProbe, { id: "b", onReady }),
),
simulateRunFocus ? createElement(LexicalRunFocusProbe) : null,
);
};
const renderSurfaceTestAssistant = async ({ allowImages = true }: { allowImages?: boolean } = {}) => {
const renderSurfaceTestAssistant = async ({
allowImages = true,
simulateRunFocus = false,
}: { allowImages?: boolean; simulateRunFocus?: boolean } = {}) => {
const container = document.createElement("div");
containers.push(container);
document.body.append(container);
@@ -160,6 +176,7 @@ const renderSurfaceTestAssistant = async ({ allowImages = true }: { allowImages?
root.render(
createElement(SurfaceTestAssistant, {
allowImages,
simulateRunFocus,
onReady: (id, aui) => clients.set(id, aui),
onRuntimeReady: (runtime) => {
sharedRuntime = runtime;
@@ -334,6 +351,35 @@ describe("AssistantRuntimeProviderWithTools", () => {
await act(async () => root.unmount());
});
test("keeps focus and dashboard scroll on the surface that started the run", async () => {
const { container, root, a } = await renderSurfaceTestAssistant({ simulateRunFocus: true });
const panelInput = getRequiredComposerInput(container, "input-a");
const widgetInput = getRequiredComposerInput(container, "input-b");
const scrollContainer = container;
scrollContainer.scrollTop = 120;
const focusWidget = widgetInput.focus.bind(widgetInput);
const widgetFocus = vi.spyOn(widgetInput, "focus").mockImplementation((options) => {
// jsdom does not implement native focus scrolling, so reproduce the dashboard jump that a
// browser performs when the off-screen widget composer receives programmatic focus.
scrollContainer.scrollTop = 640;
focusWidget(options);
});
panelInput.focus();
act(() => {
a.composer().setText("Send from the floating panel");
a.composer().send();
});
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 0));
});
expect(widgetFocus).toHaveBeenCalled();
expect(document.activeElement).toBe(panelInput);
expect(scrollContainer.scrollTop).toBe(120);
await act(async () => root.unmount());
});
test("adds pasted images only to the active surface composer", async () => {
const { container, root, a, b } = await renderSurfaceTestAssistant();
const target = getRequiredComposerInput(container, "input-a");