harisyammnv nsarrazin HF Staff victor HF Staff commited on
Commit
e442c7f
·
unverified ·
1 Parent(s): 514377e

Capture screen like similar to Claude (#1604)

Browse files

* 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 <[email protected]>

* fix: capitalization

* fix: stop tracks in finally block

* fix: revert chatwindow changes that shouldnt be there

* fix: tooltip classes

---------

Co-authored-by: Nathan Sarrazin <[email protected]>
Co-authored-by: Victor Muštar <[email protected]>

src/lib/components/chat/ChatInput.svelte CHANGED
@@ -21,6 +21,8 @@
21
  import { goto } from "$app/navigation";
22
  import { base } from "$app/paths";
23
  import IconAdd from "~icons/carbon/add";
 
 
24
 
25
  export let files: File[] = [];
26
  export let mimeTypes: string[] = [];
@@ -250,6 +252,31 @@
250
  </label>
251
  </HoverTooltip>
252
  </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  {/if}
254
  {#if modelHasTools}
255
  {#each extraTools as tool}
 
21
  import { goto } from "$app/navigation";
22
  import { base } from "$app/paths";
23
  import IconAdd from "~icons/carbon/add";
24
+ import { captureScreen } from "$lib/utils/screenshot";
25
+ import IconScreenshot from "../icons/IconScreenshot.svelte";
26
 
27
  export let files: File[] = [];
28
  export let mimeTypes: string[] = [];
 
252
  </label>
253
  </HoverTooltip>
254
  </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}
280
  {/if}
281
  {#if modelHasTools}
282
  {#each extraTools as tool}
src/lib/components/icons/IconScreenshot.svelte ADDED
@@ -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 ADDED
@@ -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
+ }