Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capture screen like similar to Claude #1604

Merged
merged 11 commits into from
Jan 8, 2025
27 changes: 27 additions & 0 deletions src/lib/components/chat/ChatInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import { goto } from "$app/navigation";
import { base } from "$app/paths";
import IconAdd from "~icons/carbon/add";
import { captureScreen } from "$lib/utils/screenshot";
import IconScreenshot from "../icons/IconScreenshot.svelte";

export let files: File[] = [];
export let mimeTypes: string[] = [];
Expand Down Expand Up @@ -250,6 +252,31 @@
</label>
</HoverTooltip>
</form>
{#if mimeTypes.includes("image/*")}
<HoverTooltip
label="Capture screenshot"
position="top"
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden"
>
<button
class="base-tool"
on:click|preventDefault={async () => {
const screenshot = await captureScreen();

// Convert base64 to blob
const base64Response = await fetch(screenshot);
const blob = await base64Response.blob();

// Create a File object from the blob
const file = new File([blob], "screenshot.png", { type: "image/png" });

files = [...files, file];
}}
>
<IconScreenshot classNames="text-xl" />
</button>
</HoverTooltip>
{/if}
{/if}
{#if modelHasTools}
{#each extraTools as tool}
Expand Down
20 changes: 20 additions & 0 deletions src/lib/components/icons/IconScreenshot.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script lang="ts">
export let classNames = "";
</script>

<svg
class={classNames}
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
focusable="false"
role="img"
width="1em"
height="1em"
fill="currentColor"
viewBox="0 0 32 32"
><path
fill-rule="evenodd"
clip-rule="evenodd"
d="M5.98 6.01h20.04v16.1H5.98V6.02Zm-2.1 0c0-1.16.94-2.1 2.1-2.1h20.04c1.16 0 2.1.94 2.1 2.1v16.1a2.1 2.1 0 0 1-2.1 2.11H5.98a2.1 2.1 0 0 1-2.1-2.1V6.02Zm5.7 1.65a2.1 2.1 0 0 0-2.1 2.1v2.61a1.05 1.05 0 0 0 2.1 0v-2.6h2.96a1.05 1.05 0 1 0 0-2.11H9.58ZM24.41 18.4a2.1 2.1 0 0 1-2.1 2.1h-2.95a1.05 1.05 0 1 1 0-2.1h2.95v-2.61a1.05 1.05 0 0 1 2.1 0v2.6ZM10.1 25.9a1.05 1.05 0 1 0 0 2.1H22.3a1.05 1.05 0 1 0 0-2.1H10.1Z"
/>
</svg>
43 changes: 43 additions & 0 deletions src/lib/utils/screenshot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export async function captureScreen(): Promise<string> {
let stream: MediaStream | undefined;
try {
// This will show the native browser dialog for screen capture
stream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio: false,
});

// Create a canvas element to capture the screenshot
const canvas = document.createElement("canvas");
const video = document.createElement("video");

// Wait for the video to load metadata
await new Promise((resolve) => {
video.onloadedmetadata = () => {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
video.play();
resolve(null);
};
if (stream) {
video.srcObject = stream;
} else {
throw Error("No stream available");
}
});

// Draw the video frame to canvas
const context = canvas.getContext("2d");
context?.drawImage(video, 0, 0, canvas.width, canvas.height);
// Convert to base64
return canvas.toDataURL("image/png");
} catch (error) {
console.error("Error capturing screenshot:", error);
throw error;
} finally {
// Stop all tracks
if (stream) {
stream.getTracks().forEach((track) => track.stop());
}
}
}
nsarrazin marked this conversation as resolved.
Show resolved Hide resolved
Loading