<script lang="ts"> import { SvelteFlowProvider } from '@xyflow/svelte'; import { slide } from 'svelte/transition'; import { Pane, PaneResizer } from 'paneforge'; import { onDestroy, onMount, tick } from 'svelte'; import { mobile, showControls, showCallOverlay, showOverview, showArtifacts } from '$lib/stores'; import Modal from '../common/Modal.svelte'; import Controls from './Controls/Controls.svelte'; import CallOverlay from './MessageInput/CallOverlay.svelte'; import Drawer from '../common/Drawer.svelte'; import Overview from './Overview.svelte'; import EllipsisVertical from '../icons/EllipsisVertical.svelte'; import Artifacts from './Artifacts.svelte'; import { min } from '@floating-ui/utils'; export let history; export let models = []; export let chatId = null; export let chatFiles = []; export let params = {}; export let eventTarget: EventTarget; export let submitPrompt: Function; export let stopResponse: Function; export let showMessage: Function; export let files; export let modelId; export let pane; let mediaQuery; let largeScreen = false; let dragged = false; let minSize = 0; export const openPane = () => { if (parseInt(localStorage?.chatControlsSize)) { pane.resize(parseInt(localStorage?.chatControlsSize)); } else { pane.resize(minSize); } }; const handleMediaQuery = async (e) => { if (e.matches) { largeScreen = true; if ($showCallOverlay) { showCallOverlay.set(false); await tick(); showCallOverlay.set(true); } } else { largeScreen = false; if ($showCallOverlay) { showCallOverlay.set(false); await tick(); showCallOverlay.set(true); } pane = null; } }; const onMouseDown = (event) => { dragged = true; }; const onMouseUp = (event) => { dragged = false; }; onMount(() => { // listen to resize 1024px mediaQuery = window.matchMedia('(min-width: 1024px)'); mediaQuery.addEventListener('change', handleMediaQuery); handleMediaQuery(mediaQuery); // Select the container element you want to observe const container = document.getElementById('chat-container'); // initialize the minSize based on the container width minSize = Math.floor((350 / container.clientWidth) * 100); // Create a new ResizeObserver instance const resizeObserver = new ResizeObserver((entries) => { for (let entry of entries) { const width = entry.contentRect.width; // calculate the percentage of 200px const percentage = (350 / width) * 100; // set the minSize to the percentage, must be an integer minSize = Math.floor(percentage); if ($showControls) { if (pane && pane.isExpanded() && pane.getSize() < minSize) { pane.resize(minSize); } } } }); // Start observing the container's size changes resizeObserver.observe(container); document.addEventListener('mousedown', onMouseDown); document.addEventListener('mouseup', onMouseUp); }); onDestroy(() => { showControls.set(false); mediaQuery.removeEventListener('change', handleMediaQuery); document.removeEventListener('mousedown', onMouseDown); document.removeEventListener('mouseup', onMouseUp); }); const closeHandler = () => { showControls.set(false); showOverview.set(false); showArtifacts.set(false); if ($showCallOverlay) { showCallOverlay.set(false); } }; $: if (!chatId) { closeHandler(); } </script> <SvelteFlowProvider> {#if !largeScreen} {#if $showControls} <Drawer show={$showControls} on:close={() => { showControls.set(false); }} > <div class=" {$showCallOverlay || $showOverview || $showArtifacts ? ' h-screen w-screen' : 'px-6 py-4'} h-full" > {#if $showCallOverlay} <div class=" h-full max-h-[100dvh] bg-white text-gray-700 dark:bg-black dark:text-gray-300 flex justify-center" > <CallOverlay bind:files {submitPrompt} {stopResponse} {modelId} {chatId} {eventTarget} on:close={() => { showControls.set(false); }} /> </div> {:else if $showArtifacts} <Artifacts {history} /> {:else if $showOverview} <Overview {history} on:nodeclick={(e) => { showMessage(e.detail.node.data.message); }} on:close={() => { showControls.set(false); }} /> {:else} <Controls on:close={() => { showControls.set(false); }} {models} bind:chatFiles bind:params /> {/if} </div> </Drawer> {/if} {:else} <!-- if $showControls --> {#if $showControls} <PaneResizer class="relative flex w-2 items-center justify-center bg-background group"> <div class="z-10 flex h-7 w-5 items-center justify-center rounded-sm"> <EllipsisVertical className="size-4 invisible group-hover:visible" /> </div> </PaneResizer> {/if} <Pane bind:pane defaultSize={0} onResize={(size) => { console.log('size', size, minSize); if ($showControls && pane.isExpanded()) { if (size < minSize) { pane.resize(minSize); } if (size < minSize) { localStorage.chatControlsSize = 0; } else { localStorage.chatControlsSize = size; } } }} onCollapse={() => { showControls.set(false); }} collapsible={true} class="pt-8" > {#if $showControls} <div class="pr-4 pb-8 flex max-h-full min-h-full"> <div class="w-full {($showOverview || $showArtifacts) && !$showCallOverlay ? ' ' : 'px-4 py-4 bg-white dark:shadow-lg dark:bg-gray-850 border border-gray-50 dark:border-gray-850'} rounded-xl z-40 pointer-events-auto overflow-y-auto scrollbar-hidden" > {#if $showCallOverlay} <div class="w-full h-full flex justify-center"> <CallOverlay bind:files {submitPrompt} {stopResponse} {modelId} {chatId} {eventTarget} on:close={() => { showControls.set(false); }} /> </div> {:else if $showArtifacts} <Artifacts {history} overlay={dragged} /> {:else if $showOverview} <Overview {history} on:nodeclick={(e) => { if (e.detail.node.data.message.favorite) { history.messages[e.detail.node.data.message.id].favorite = true; } else { history.messages[e.detail.node.data.message.id].favorite = null; } showMessage(e.detail.node.data.message); }} on:close={() => { showControls.set(false); }} /> {:else} <Controls on:close={() => { showControls.set(false); }} {models} bind:chatFiles bind:params /> {/if} </div> </div> {/if} </Pane> {/if} </SvelteFlowProvider>