diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index 800059055..349946a43 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -36,6 +36,7 @@ import RichTextInput from '../common/RichTextInput.svelte'; import { generateAutoCompletion } from '$lib/apis'; import { error, text } from '@sveltejs/kit'; + import Image from '../common/Image.svelte'; const i18n = getContext('i18n'); @@ -88,6 +89,43 @@ }); }; + const screenCaptureHandler = async () => { + try { + // Request screen media + const mediaStream = await navigator.mediaDevices.getDisplayMedia({ + video: { cursor: 'never' }, + audio: false + }); + // Once the user selects a screen, temporarily create a video element + const video = document.createElement('video'); + video.srcObject = mediaStream; + // Ensure the video loads without affecting user experience or tab switching + await video.play(); + // Set up the canvas to match the video dimensions + const canvas = document.createElement('canvas'); + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + // Grab a single frame from the video stream using the canvas + const context = canvas.getContext('2d'); + context.drawImage(video, 0, 0, canvas.width, canvas.height); + // Stop all video tracks (stop screen sharing) after capturing the image + mediaStream.getTracks().forEach((track) => track.stop()); + + // bring back focus to this current tab, so that the user can see the screen capture + window.focus(); + + // Convert the canvas to a Base64 image URL + const imageUrl = canvas.toDataURL('image/png'); + // Add the captured image to the files array to render it + files = [...files, { type: 'image', url: imageUrl }]; + // Clean memory: Clear video srcObject + video.srcObject = null; + } catch (error) { + // Handle any errors (e.g., user cancels screen sharing) + console.error('Error capturing screen:', error); + } + }; + const uploadFileHandler = async (file, fullContext: boolean = false) => { if ($_user?.role !== 'admin' && !($_user?.permissions?.chat?.file_upload ?? true)) { toast.error($i18n.t('You do not have permission to upload files.')); @@ -471,10 +509,10 @@ {#if file.type === 'image'}
- input {#if atSelectedModel ? visionCapableModels.length === 0 : selectedModels.length !== visionCapableModels.length} { filesInputElement.click(); }} diff --git a/src/lib/components/chat/MessageInput/InputMenu.svelte b/src/lib/components/chat/MessageInput/InputMenu.svelte index ffdb5897d..3159c6145 100644 --- a/src/lib/components/chat/MessageInput/InputMenu.svelte +++ b/src/lib/components/chat/MessageInput/InputMenu.svelte @@ -3,7 +3,7 @@ import { flyAndScale } from '$lib/utils/transitions'; import { getContext, onMount, tick } from 'svelte'; - import { config, user, tools as _tools } from '$lib/stores'; + import { config, user, tools as _tools, mobile } from '$lib/stores'; import { getTools } from '$lib/apis/tools'; import Dropdown from '$lib/components/common/Dropdown.svelte'; @@ -12,9 +12,11 @@ import Switch from '$lib/components/common/Switch.svelte'; import GlobeAltSolid from '$lib/components/icons/GlobeAltSolid.svelte'; import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte'; + import CameraSolid from '$lib/components/icons/CameraSolid.svelte'; const i18n = getContext('i18n'); + export let screenCaptureHandler: Function; export let uploadFilesHandler: Function; export let selectedToolIds: string[] = []; @@ -127,6 +129,18 @@
{/if} + {#if !$mobile} + { + screenCaptureHandler(); + }} + > + +
{$i18n.t('Capture')}
+
+ {/if} + { diff --git a/src/lib/components/icons/CameraSolid.svelte b/src/lib/components/icons/CameraSolid.svelte new file mode 100644 index 000000000..f74edb73e --- /dev/null +++ b/src/lib/components/icons/CameraSolid.svelte @@ -0,0 +1,12 @@ + + + + + +