| | <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); |
| | } |
| | } |
| | } |
| | }); |
| | |
| | |
| | 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-full' |
| | : '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> |
| |
|