Skip to content

Commit 79c26f2

Browse files
harisyammnvnsarrazingary149
authored
Capture screen like similar to Claude (#1604)
* feat: capture screen * feat: make it work with latest tool UI * fix: add screenshot icon * Update src/lib/components/icons/iconScreenshot.svelte Co-authored-by: Victor Muštar <victor.mustar@gmail.com> * fix: capitalization * fix: stop tracks in finally block * fix: revert chatwindow changes that shouldnt be there * fix: tooltip classes --------- Co-authored-by: Nathan Sarrazin <sarrazin.nathan@gmail.com> Co-authored-by: Victor Muštar <victor.mustar@gmail.com>
1 parent f48d079 commit 79c26f2

File tree

3 files changed

+90
-0
lines changed

3 files changed

+90
-0
lines changed

src/lib/components/chat/ChatInput.svelte

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import { goto } from "$app/navigation";
2222
import { base } from "$app/paths";
2323
import IconAdd from "~icons/carbon/add";
24+
import { captureScreen } from "$lib/utils/screenshot";
25+
import IconScreenshot from "../icons/IconScreenshot.svelte";
2426
2527
export let files: File[] = [];
2628
export let mimeTypes: string[] = [];
@@ -250,6 +252,31 @@
250252
</label>
251253
</HoverTooltip>
252254
</form>
255+
{#if mimeTypes.includes("image/*")}
256+
<HoverTooltip
257+
label="Capture screenshot"
258+
position="top"
259+
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden"
260+
>
261+
<button
262+
class="base-tool"
263+
on:click|preventDefault={async () => {
264+
const screenshot = await captureScreen();
265+
266+
// Convert base64 to blob
267+
const base64Response = await fetch(screenshot);
268+
const blob = await base64Response.blob();
269+
270+
// Create a File object from the blob
271+
const file = new File([blob], "screenshot.png", { type: "image/png" });
272+
273+
files = [...files, file];
274+
}}
275+
>
276+
<IconScreenshot classNames="text-xl" />
277+
</button>
278+
</HoverTooltip>
279+
{/if}
253280
{/if}
254281
{#if modelHasTools}
255282
{#each extraTools as tool}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script lang="ts">
2+
export let classNames = "";
3+
</script>
4+
5+
<svg
6+
class={classNames}
7+
xmlns="http://www.w3.org/2000/svg"
8+
aria-hidden="true"
9+
focusable="false"
10+
role="img"
11+
width="1em"
12+
height="1em"
13+
fill="currentColor"
14+
viewBox="0 0 32 32"
15+
><path
16+
fill-rule="evenodd"
17+
clip-rule="evenodd"
18+
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"
19+
/>
20+
</svg>

src/lib/utils/screenshot.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
export async function captureScreen(): Promise<string> {
2+
let stream: MediaStream | undefined;
3+
try {
4+
// This will show the native browser dialog for screen capture
5+
stream = await navigator.mediaDevices.getDisplayMedia({
6+
video: true,
7+
audio: false,
8+
});
9+
10+
// Create a canvas element to capture the screenshot
11+
const canvas = document.createElement("canvas");
12+
const video = document.createElement("video");
13+
14+
// Wait for the video to load metadata
15+
await new Promise((resolve) => {
16+
video.onloadedmetadata = () => {
17+
canvas.width = video.videoWidth;
18+
canvas.height = video.videoHeight;
19+
video.play();
20+
resolve(null);
21+
};
22+
if (stream) {
23+
video.srcObject = stream;
24+
} else {
25+
throw Error("No stream available");
26+
}
27+
});
28+
29+
// Draw the video frame to canvas
30+
const context = canvas.getContext("2d");
31+
context?.drawImage(video, 0, 0, canvas.width, canvas.height);
32+
// Convert to base64
33+
return canvas.toDataURL("image/png");
34+
} catch (error) {
35+
console.error("Error capturing screenshot:", error);
36+
throw error;
37+
} finally {
38+
// Stop all tracks
39+
if (stream) {
40+
stream.getTracks().forEach((track) => track.stop());
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)