diff --git a/src/components/BugScreenshotDialog.tsx b/src/components/BugScreenshotDialog.tsx new file mode 100644 index 0000000..123a080 --- /dev/null +++ b/src/components/BugScreenshotDialog.tsx @@ -0,0 +1,91 @@ +import { IpcClient } from "@/ipc/ipc_client"; +import { Dialog, DialogTitle } from "@radix-ui/react-dialog"; +import { DialogContent, DialogHeader } from "./ui/dialog"; +import { Button } from "./ui/button"; +import { BugIcon, Camera } from "lucide-react"; +import { useState } from "react"; +import { ScreenshotSuccessDialog } from "./ScreenshotSuccessDialog"; + +interface BugScreenshotDialogProps { + isOpen: boolean; + onClose: () => void; + handleReportBug: () => Promise; + isLoading: boolean; +} +export function BugScreenshotDialog({ + isOpen, + onClose, + handleReportBug, + isLoading, +}: BugScreenshotDialogProps) { + const [isScreenshotSuccessOpen, setIsScreenshotSuccessOpen] = useState(false); + const [screenshotError, setScreenshotError] = useState(null); + + const handleReportBugWithScreenshot = async () => { + setScreenshotError(null); + onClose(); + setTimeout(async () => { + try { + await IpcClient.getInstance().takeScreenshot(); + setIsScreenshotSuccessOpen(true); + } catch (error) { + setScreenshotError( + error instanceof Error ? error.message : "Failed to take screenshot", + ); + } + }, 200); // Small delay for dialog to close + }; + + return ( + + + + Take a screenshot? + +
+
+ +

+ You'll get better and faster responses if you do this! +

+
+
+ +

+ We'll still try to respond but might not be able to help as much. +

+
+ {screenshotError && ( +

+ Failed to take screenshot: {screenshotError} +

+ )} +
+
+ setIsScreenshotSuccessOpen(false)} + handleReportBug={handleReportBug} + isLoading={isLoading} + /> +
+ ); +} diff --git a/src/components/HelpDialog.tsx b/src/components/HelpDialog.tsx index 91bdffc..9bc07af 100644 --- a/src/components/HelpDialog.tsx +++ b/src/components/HelpDialog.tsx @@ -25,6 +25,7 @@ import { ChatLogsData } from "@/ipc/ipc_types"; import { showError } from "@/lib/toast"; import { HelpBotDialog } from "./HelpBotDialog"; import { useSettings } from "@/hooks/useSettings"; +import { BugScreenshotDialog } from "./BugScreenshotDialog"; interface HelpDialogProps { isOpen: boolean; @@ -39,6 +40,7 @@ export function HelpDialog({ isOpen, onClose }: HelpDialogProps) { const [uploadComplete, setUploadComplete] = useState(false); const [sessionId, setSessionId] = useState(""); const [isHelpBotOpen, setIsHelpBotOpen] = useState(false); + const [isBugScreenshotOpen, setIsBugScreenshotOpen] = useState(false); const selectedChatId = useAtomValue(selectedChatIdAtom); const { settings } = useSettings(); @@ -91,6 +93,9 @@ Issues that do not meet these requirements will be closed and may need to be res ## Actual Behavior (required) +## Screenshot (Optional) + + ## System Information - Dyad Version: ${debugInfo.dyadVersion} - Platform: ${debugInfo.platform} @@ -427,7 +432,10 @@ Session ID: ${sessionId}
+ + + ); +} diff --git a/src/ipc/handlers/debug_handlers.ts b/src/ipc/handlers/debug_handlers.ts index b02eb50..c74ebfa 100644 --- a/src/ipc/handlers/debug_handlers.ts +++ b/src/ipc/handlers/debug_handlers.ts @@ -1,4 +1,4 @@ -import { ipcMain } from "electron"; +import { BrowserWindow, clipboard, ipcMain } from "electron"; import { platform, arch } from "os"; import { SystemDebugInfo, ChatLogsData } from "../ipc_types"; import { readSettings } from "../../main/settings"; @@ -196,6 +196,20 @@ export function registerDebugHandlers() { ); console.log("Registered debug IPC handlers"); + + ipcMain.handle("take-screenshot", async () => { + const win = BrowserWindow.getFocusedWindow(); + if (!win) throw new Error("No focused window to capture"); + + // Capture the window's current contents as a NativeImage + const image = await win.capturePage(); + // Validate image + if (!image || image.isEmpty()) { + throw new Error("Failed to capture screenshot"); + } + // Write the image to the clipboard + clipboard.writeImage(image); + }); } function serializeModelForDebug(model: LargeLanguageModel): string { diff --git a/src/ipc/ipc_client.ts b/src/ipc/ipc_client.ts index 7ae20dd..9dc53ff 100644 --- a/src/ipc/ipc_client.ts +++ b/src/ipc/ipc_client.ts @@ -1320,6 +1320,10 @@ export class IpcClient { }); } + public async takeScreenshot(): Promise { + await this.ipcRenderer.invoke("take-screenshot"); + } + public cancelHelpChat(sessionId: string): void { this.ipcRenderer.invoke("help:chat:cancel", sessionId).catch(() => {}); } diff --git a/src/preload.ts b/src/preload.ts index 9e931c7..7cee4d2 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -121,6 +121,8 @@ const validInvokeChannels = [ "mcp:set-tool-consent", // MCP consent response from renderer to main "mcp:tool-consent-response", + // Help + "take-screenshot", // Help bot "help:chat:start", "help:chat:cancel",