Spaces:
Paused
Paused
| <script> | |
| import { onDestroy, onMount, tick, getContext, createEventDispatcher } from 'svelte'; | |
| const i18n = getContext('i18n'); | |
| const dispatch = createEventDispatcher(); | |
| import Markdown from './Markdown.svelte'; | |
| import { chatId, mobile, settings, showArtifacts, showControls, showOverview } from '$lib/stores'; | |
| import FloatingButtons from '../ContentRenderer/FloatingButtons.svelte'; | |
| import { createMessagesList } from '$lib/utils'; | |
| export let id; | |
| export let content; | |
| export let history; | |
| export let model = null; | |
| export let sources = null; | |
| export let save = false; | |
| export let floatingButtons = true; | |
| export let onSourceClick = () => {}; | |
| export let onTaskClick = () => {}; | |
| export let onAddMessages = () => {}; | |
| let contentContainerElement; | |
| let floatingButtonsElement; | |
| const updateButtonPosition = (event) => { | |
| const buttonsContainerElement = document.getElementById(`floating-buttons-${id}`); | |
| if ( | |
| !contentContainerElement?.contains(event.target) && | |
| !buttonsContainerElement?.contains(event.target) | |
| ) { | |
| closeFloatingButtons(); | |
| return; | |
| } | |
| setTimeout(async () => { | |
| await tick(); | |
| if (!contentContainerElement?.contains(event.target)) return; | |
| let selection = window.getSelection(); | |
| if (selection.toString().trim().length > 0) { | |
| const range = selection.getRangeAt(0); | |
| const rect = range.getBoundingClientRect(); | |
| const parentRect = contentContainerElement.getBoundingClientRect(); | |
| // Adjust based on parent rect | |
| const top = rect.bottom - parentRect.top; | |
| const left = rect.left - parentRect.left; | |
| if (buttonsContainerElement) { | |
| buttonsContainerElement.style.display = 'block'; | |
| // Calculate space available on the right | |
| const spaceOnRight = parentRect.width - left; | |
| let halfScreenWidth = $mobile ? window.innerWidth / 2 : window.innerWidth / 3; | |
| if (spaceOnRight < halfScreenWidth) { | |
| const right = parentRect.right - rect.right; | |
| buttonsContainerElement.style.right = `${right}px`; | |
| buttonsContainerElement.style.left = 'auto'; // Reset left | |
| } else { | |
| // Enough space, position using 'left' | |
| buttonsContainerElement.style.left = `${left}px`; | |
| buttonsContainerElement.style.right = 'auto'; // Reset right | |
| } | |
| buttonsContainerElement.style.top = `${top + 5}px`; // +5 to add some spacing | |
| } | |
| } else { | |
| closeFloatingButtons(); | |
| } | |
| }, 0); | |
| }; | |
| const closeFloatingButtons = () => { | |
| const buttonsContainerElement = document.getElementById(`floating-buttons-${id}`); | |
| if (buttonsContainerElement) { | |
| buttonsContainerElement.style.display = 'none'; | |
| } | |
| if (floatingButtonsElement) { | |
| // check if closeHandler is defined | |
| if (typeof floatingButtonsElement?.closeHandler === 'function') { | |
| // call the closeHandler function | |
| floatingButtonsElement?.closeHandler(); | |
| } | |
| } | |
| }; | |
| const keydownHandler = (e) => { | |
| if (e.key === 'Escape') { | |
| closeFloatingButtons(); | |
| } | |
| }; | |
| onMount(() => { | |
| if (floatingButtons) { | |
| contentContainerElement?.addEventListener('mouseup', updateButtonPosition); | |
| document.addEventListener('mouseup', updateButtonPosition); | |
| document.addEventListener('keydown', keydownHandler); | |
| } | |
| }); | |
| onDestroy(() => { | |
| if (floatingButtons) { | |
| contentContainerElement?.removeEventListener('mouseup', updateButtonPosition); | |
| document.removeEventListener('mouseup', updateButtonPosition); | |
| document.removeEventListener('keydown', keydownHandler); | |
| } | |
| }); | |
| </script> | |
| <div bind:this={contentContainerElement}> | |
| <Markdown | |
| {id} | |
| {content} | |
| {model} | |
| {save} | |
| sourceIds={(sources ?? []).reduce((acc, s) => { | |
| let ids = []; | |
| s.document.forEach((document, index) => { | |
| if (model?.info?.meta?.capabilities?.citations == false) { | |
| ids.push('N/A'); | |
| return ids; | |
| } | |
| const metadata = s.metadata?.[index]; | |
| const id = metadata?.source ?? 'N/A'; | |
| if (metadata?.name) { | |
| ids.push(metadata.name); | |
| return ids; | |
| } | |
| if (id.startsWith('http://') || id.startsWith('https://')) { | |
| ids.push(id); | |
| } else { | |
| ids.push(s?.source?.name ?? id); | |
| } | |
| return ids; | |
| }); | |
| acc = [...acc, ...ids]; | |
| // remove duplicates | |
| return acc.filter((item, index) => acc.indexOf(item) === index); | |
| }, [])} | |
| {onSourceClick} | |
| {onTaskClick} | |
| onUpdate={(value) => { | |
| dispatch('update', value); | |
| }} | |
| onCode={(value) => { | |
| const { lang, code } = value; | |
| if ( | |
| ($settings?.detectArtifacts ?? true) && | |
| (['html', 'svg'].includes(lang) || (lang === 'xml' && code.includes('svg'))) && | |
| !$mobile && | |
| $chatId | |
| ) { | |
| showArtifacts.set(true); | |
| showControls.set(true); | |
| } | |
| }} | |
| /> | |
| </div> | |
| {#if floatingButtons && model} | |
| <FloatingButtons | |
| bind:this={floatingButtonsElement} | |
| {id} | |
| model={model?.id} | |
| messages={createMessagesList(history, id)} | |
| onAdd={({ modelId, parentId, messages }) => { | |
| console.log(modelId, parentId, messages); | |
| onAddMessages({ modelId, parentId, messages }); | |
| closeFloatingButtons(); | |
| }} | |
| /> | |
| {/if} | |