ChatControls.svelte 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. <script lang="ts">
  2. import { SvelteFlowProvider } from '@xyflow/svelte';
  3. import { slide } from 'svelte/transition';
  4. import { onDestroy, onMount, tick } from 'svelte';
  5. import { mobile, showControls, showCallOverlay, showOverview } from '$lib/stores';
  6. import Modal from '../common/Modal.svelte';
  7. import Controls from './Controls/Controls.svelte';
  8. import CallOverlay from './MessageInput/CallOverlay.svelte';
  9. import Drawer from '../common/Drawer.svelte';
  10. import Overview from './Overview.svelte';
  11. import { Pane, PaneResizer } from 'paneforge';
  12. import EllipsisVertical from '../icons/EllipsisVertical.svelte';
  13. import { get } from 'svelte/store';
  14. export let history;
  15. export let models = [];
  16. export let chatId = null;
  17. export let chatFiles = [];
  18. export let params = {};
  19. export let eventTarget: EventTarget;
  20. export let submitPrompt: Function;
  21. export let stopResponse: Function;
  22. export let showMessage: Function;
  23. export let files;
  24. export let modelId;
  25. export let pane;
  26. let largeScreen = false;
  27. onMount(() => {
  28. // listen to resize 1024px
  29. const mediaQuery = window.matchMedia('(min-width: 1024px)');
  30. const handleMediaQuery = async (e) => {
  31. if (e.matches) {
  32. largeScreen = true;
  33. if ($showCallOverlay) {
  34. showCallOverlay.set(false);
  35. await tick();
  36. showCallOverlay.set(true);
  37. }
  38. } else {
  39. largeScreen = false;
  40. if ($showCallOverlay) {
  41. showCallOverlay.set(false);
  42. await tick();
  43. showCallOverlay.set(true);
  44. }
  45. pane = null;
  46. }
  47. };
  48. mediaQuery.addEventListener('change', handleMediaQuery);
  49. handleMediaQuery(mediaQuery);
  50. return () => {
  51. mediaQuery.removeEventListener('change', handleMediaQuery);
  52. };
  53. });
  54. onDestroy(() => {
  55. showControls.set(false);
  56. });
  57. $: if (!chatId) {
  58. showOverview.set(false);
  59. }
  60. </script>
  61. <SvelteFlowProvider>
  62. {#if !largeScreen}
  63. {#if $showControls}
  64. <Drawer
  65. show={$showControls}
  66. on:close={() => {
  67. showControls.set(false);
  68. }}
  69. >
  70. <div
  71. class=" {$showCallOverlay || $showOverview ? ' h-screen w-screen' : 'px-6 py-4'} h-full"
  72. >
  73. {#if $showCallOverlay}
  74. <div
  75. class=" h-full max-h-[100dvh] bg-white text-gray-700 dark:bg-black dark:text-gray-300 flex justify-center"
  76. >
  77. <CallOverlay
  78. bind:files
  79. {submitPrompt}
  80. {stopResponse}
  81. {modelId}
  82. {chatId}
  83. {eventTarget}
  84. on:close={() => {
  85. showControls.set(false);
  86. }}
  87. />
  88. </div>
  89. {:else if $showOverview}
  90. <Overview
  91. {history}
  92. on:nodeclick={(e) => {
  93. showMessage(e.detail.node.data.message);
  94. }}
  95. on:close={() => {
  96. showControls.set(false);
  97. }}
  98. />
  99. {:else}
  100. <Controls
  101. on:close={() => {
  102. showControls.set(false);
  103. }}
  104. {models}
  105. bind:chatFiles
  106. bind:params
  107. />
  108. {/if}
  109. </div>
  110. </Drawer>
  111. {/if}
  112. {:else}
  113. <!-- if $showControls -->
  114. <PaneResizer class="relative flex w-2 items-center justify-center bg-background group">
  115. <div class="z-10 flex h-7 w-5 items-center justify-center rounded-sm">
  116. <EllipsisVertical className="size-4 invisible group-hover:visible" />
  117. </div>
  118. </PaneResizer>
  119. <Pane
  120. bind:pane
  121. defaultSize={$showControls
  122. ? parseInt(localStorage?.chatControlsSize ?? '30')
  123. ? parseInt(localStorage?.chatControlsSize ?? '30')
  124. : 30
  125. : 0}
  126. onResize={(size) => {
  127. if (size === 0) {
  128. showControls.set(false);
  129. } else {
  130. if (!$showControls) {
  131. showControls.set(true);
  132. }
  133. localStorage.chatControlsSize = size;
  134. }
  135. }}
  136. class="pt-8"
  137. >
  138. {#if $showControls}
  139. <div class="pr-4 pb-8 flex max-h-full min-h-full">
  140. <div
  141. class="w-full {$showOverview && !$showCallOverlay
  142. ? ' '
  143. : 'px-5 py-4 bg-white dark:shadow-lg dark:bg-gray-850 border border-gray-50 dark:border-gray-800'} rounded-lg z-50 pointer-events-auto overflow-y-auto scrollbar-hidden"
  144. >
  145. {#if $showCallOverlay}
  146. <div class="w-full h-full flex justify-center">
  147. <CallOverlay
  148. bind:files
  149. {submitPrompt}
  150. {stopResponse}
  151. {modelId}
  152. {chatId}
  153. {eventTarget}
  154. on:close={() => {
  155. showControls.set(false);
  156. }}
  157. />
  158. </div>
  159. {:else if $showOverview}
  160. <Overview
  161. {history}
  162. on:nodeclick={(e) => {
  163. if (e.detail.node.data.message.favorite) {
  164. history.messages[e.detail.node.data.message.id].favorite = true;
  165. } else {
  166. history.messages[e.detail.node.data.message.id].favorite = null;
  167. }
  168. showMessage(e.detail.node.data.message);
  169. }}
  170. on:close={() => {
  171. showControls.set(false);
  172. }}
  173. />
  174. {:else}
  175. <Controls
  176. on:close={() => {
  177. showControls.set(false);
  178. }}
  179. {models}
  180. bind:chatFiles
  181. bind:params
  182. />
  183. {/if}
  184. </div>
  185. </div>
  186. {/if}
  187. </Pane>
  188. {/if}
  189. </SvelteFlowProvider>