|
<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-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> |
|
|