fix(dsh-plugin): add PiP close button and sniff observation thumbnail media type

The Document PiP window had no close/return entry once popped out —
users could not dismiss it from inside the window. Add an onClosePip
callback and render a close button in the PiP header; clicking it
calls pipWindow.close() and the content returns to its carrier.

The observation thumbnail publisher hardcoded mediaType as "image/png"
regardless of the actual capture bytes. Reuse the existing
sniffImageMediaType helper (exported from image.ts) so the HTTP
thumbnail endpoint declares the correct Content-Type when Chromium
returns JPEG captures.

Closes #153 (Bug 2 fully; Bug 1 was fixed in #135, this covers the
remaining observation-thumbnail path).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
drakezhang
2026-09-02 16:14:24 +08:00
co-authored by Cursor
parent 49744233f7
commit 51473c01ff
6 changed files with 58 additions and 2 deletions
@@ -298,6 +298,33 @@ describe("ObservationOverlay", () => {
expect(close).toHaveBeenCalledTimes(1);
});
it("shows a close button inside the PiP window that closes it", async () => {
const h = makeHarness([BUSY]);
const pipDoc = document.implementation.createHTMLDocument("pip");
const close = vi.fn();
const listeners = new Map<string, (() => void)[]>();
const requestWindow = vi.fn(async () => {
return {
document: pipDoc,
addEventListener: (name: string, fn: () => void) => {
listeners.set(name, [...(listeners.get(name) ?? []), fn]);
},
close,
} as unknown as Window;
});
(window as unknown as Record<string, unknown>).documentPictureInPicture = { requestWindow };
render(<ObservationOverlay store={h.store} />);
const popout = await screen.findByRole("button", { name: /Pop out/ });
fireEvent.click(popout);
await waitFor(() => expect(pipDoc.body.textContent).toContain("s1"));
const closeBtn = pipDoc.body.querySelector<HTMLButtonElement>(
'button[aria-label="Close mini window"]',
);
expect(closeBtn).not.toBeNull();
closeBtn!.click();
expect(close).toHaveBeenCalledTimes(1);
});
it("renders the strip for two sessions and pins focus on click", async () => {
const older: SessionObservation = {
sessionId: "s1",