From 8b95cebef4448d9f530f139c7cfa2c85a7bf794a Mon Sep 17 00:00:00 2001 From: Robbie Byrd Date: Fri, 10 Jul 2026 03:09:26 +0000 Subject: [PATCH] Feedback updates --- .../src/Applications/Feedback/Feedback.tsx | 4 +-- .../Applications/Feedback/useFeedback.test.ts | 36 +++++++++++++++++++ .../src/Applications/Feedback/useFeedback.ts | 8 +++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/Applications/Feedback/Feedback.tsx b/packages/frontend/src/Applications/Feedback/Feedback.tsx index b7d9f993..b6c03658 100644 --- a/packages/frontend/src/Applications/Feedback/Feedback.tsx +++ b/packages/frontend/src/Applications/Feedback/Feedback.tsx @@ -10,10 +10,10 @@ import type React from "react"; import { useCallback, useMemo, useState } from "react"; import { FeedbackForm } from "./FeedbackForm"; import { FeedbackSuccess } from "./FeedbackSuccess"; -import { useFeedback } from "./useFeedback"; +import { FEEDBACK_APP_ID, useFeedback } from "./useFeedback"; import type { FeedbackFields } from "./useFeedback"; -const appId = "Feedback.app"; +const appId = FEEDBACK_APP_ID; const appName = "Feedback"; // This app's own icon, registered into the shared registry at diff --git a/packages/frontend/src/Applications/Feedback/useFeedback.test.ts b/packages/frontend/src/Applications/Feedback/useFeedback.test.ts index c842b02b..f91cd2c2 100644 --- a/packages/frontend/src/Applications/Feedback/useFeedback.test.ts +++ b/packages/frontend/src/Applications/Feedback/useFeedback.test.ts @@ -92,6 +92,42 @@ describe("useFeedback", () => { document.body.removeChild(root); }); + + it("ignoreElements skips the Feedback app's own windows so they don't appear in the shot", async () => { + const mockCanvas = { + toBlob: (cb: (b: Blob | null) => void) => + cb(new Blob(["png"], { type: "image/png" })), + } as unknown as HTMLCanvasElement; + mockHtml2canvas.mockResolvedValue(mockCanvas); + + const root = document.createElement("div"); + root.id = "root"; + document.body.appendChild(root); + + const { result } = renderHook(() => useFeedback()); + await act(async () => { + await result.current.captureScreenshot(); + }); + + const { ignoreElements } = mockHtml2canvas.mock.calls[0][1] as { + ignoreElements: (el: Element) => boolean; + }; + + // Classicy renders each window with DOM id `${appId}_${windowId}`. + const mainWindow = document.createElement("div"); + mainWindow.id = "Feedback.app_feedback_main"; + expect(ignoreElements(mainWindow)).toBe(true); + + const aboutWindow = document.createElement("div"); + aboutWindow.id = "Feedback.app_about"; + expect(ignoreElements(aboutWindow)).toBe(true); + + const otherAppWindow = document.createElement("div"); + otherAppWindow.id = "RadioScanner.app_radioscanner_main"; + expect(ignoreElements(otherAppWindow)).toBe(false); + + document.body.removeChild(root); + }); }); describe("submit", () => { diff --git a/packages/frontend/src/Applications/Feedback/useFeedback.ts b/packages/frontend/src/Applications/Feedback/useFeedback.ts index 6379bbf5..d14d2286 100644 --- a/packages/frontend/src/Applications/Feedback/useFeedback.ts +++ b/packages/frontend/src/Applications/Feedback/useFeedback.ts @@ -2,6 +2,11 @@ import html2canvas from "html2canvas-pro"; import { useCallback, useState } from "react"; import { getSessionURL } from "../../openreplay"; +// Also the `id` of in Feedback.tsx. Classicy gives every window +// a DOM id of `${appId}_${windowId}`, which is how captureScreenshot excludes +// this app's own windows from the shot. +export const FEEDBACK_APP_ID = "Feedback.app"; + export interface FeedbackFields { name: string; email: string; @@ -27,6 +32,9 @@ export function useFeedback(): { const canvas = await html2canvas(root, { useCORS: true, ignoreElements: (el) => { + // html2canvas re-renders the DOM rather than grabbing the real + // screen, so skipping this app's windows is what "hides" them. + if (el.id.startsWith(`${FEEDBACK_APP_ID}_`)) return true; if (el.tagName !== "IFRAME") return false; try { return new URL((el as HTMLIFrameElement).src).origin !== window.location.origin;