|
@@ -90,14 +90,49 @@
|
|
});
|
|
});
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ 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) => {
|
|
const uploadFileHandler = async (file, fullContext: boolean = false) => {
|
|
if ($_user?.role !== 'admin' && !($_user?.permissions?.chat?.file_upload ?? true)) {
|
|
if ($_user?.role !== 'admin' && !($_user?.permissions?.chat?.file_upload ?? true)) {
|
|
toast.error($i18n.t('You do not have permission to upload files.'));
|
|
toast.error($i18n.t('You do not have permission to upload files.'));
|
|
return null;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
- console.log(file);
|
|
|
|
-
|
|
|
|
const tempItemId = uuidv4();
|
|
const tempItemId = uuidv4();
|
|
const fileItem = {
|
|
const fileItem = {
|
|
type: 'file',
|
|
type: 'file',
|
|
@@ -359,64 +394,13 @@
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
{/if}
|
|
|
|
|
|
- <div class=" flex">
|
|
|
|
- <div class=" ml-0.5 self-end mb-1.5 flex space-x-1">
|
|
|
|
- <InputMenu
|
|
|
|
- bind:webSearchEnabled
|
|
|
|
- bind:selectedToolIds
|
|
|
|
- tools={$tools.reduce((a, e, i, arr) => {
|
|
|
|
- if (availableToolIds.includes(e.id) || ($_user?.role ?? 'user') === 'admin') {
|
|
|
|
- a[e.id] = {
|
|
|
|
- name: e.name,
|
|
|
|
- description: e.meta.description,
|
|
|
|
- enabled: false
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
- return a;
|
|
|
|
- }, {})}
|
|
|
|
- uploadFilesHandler={() => {
|
|
|
|
- filesInputElement.click();
|
|
|
|
- }}
|
|
|
|
- uploadGoogleDriveHandler={async () => {
|
|
|
|
- try {
|
|
|
|
- const fileData = await createPicker();
|
|
|
|
- if (fileData) {
|
|
|
|
- const file = new File([fileData.blob], fileData.name, {
|
|
|
|
- type: fileData.blob.type
|
|
|
|
- });
|
|
|
|
- await uploadFileHandler(file);
|
|
|
|
- } else {
|
|
|
|
- console.log('No file was selected from Google Drive');
|
|
|
|
- }
|
|
|
|
- } catch (error) {
|
|
|
|
- console.error('Google Drive Error:', error);
|
|
|
|
- toast.error(
|
|
|
|
- $i18n.t('Error accessing Google Drive: {{error}}', {
|
|
|
|
- error: error.message
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- }}
|
|
|
|
- onClose={async () => {
|
|
|
|
- await tick();
|
|
|
|
-
|
|
|
|
- const chatInput = document.getElementById('chat-input');
|
|
|
|
- chatInput?.focus();
|
|
|
|
- }}
|
|
|
|
- >
|
|
|
|
- <button
|
|
|
|
- class="bg-gray-50 hover:bg-gray-100 text-gray-800 dark:bg-gray-850 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-2 outline-none focus:outline-none"
|
|
|
|
- type="button"
|
|
|
|
- aria-label="More"
|
|
|
|
- >
|
|
|
|
- <svg
|
|
|
|
- xmlns="http://www.w3.org/2000/svg"
|
|
|
|
- viewBox="0 0 16 16"
|
|
|
|
- fill="currentColor"
|
|
|
|
- class="size-5"
|
|
|
|
- >
|
|
|
|
- <path
|
|
|
|
- d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
|
|
|
|
|
|
+ {#if webSearchEnabled}
|
|
|
|
+ <div class="flex items-center justify-between w-full">
|
|
|
|
+ <div class="flex items-center gap-2.5 text-sm dark:text-gray-500">
|
|
|
|
+ <div class="pl-1">
|
|
|
|
+ <span class="relative flex size-2">
|
|
|
|
+ <span
|
|
|
|
+ class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
|
|
/>
|
|
/>
|
|
<span class="relative inline-flex rounded-full size-2 bg-green-500" />
|
|
<span class="relative inline-flex rounded-full size-2 bg-green-500" />
|
|
</span>
|
|
</span>
|
|
@@ -646,6 +630,7 @@
|
|
);
|
|
);
|
|
}
|
|
}
|
|
}}
|
|
}}
|
|
|
|
+
|
|
onClose={async () => {
|
|
onClose={async () => {
|
|
await tick();
|
|
await tick();
|
|
|
|
|