ChatControls.svelte 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  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. >
  137. {#if $showControls}
  138. <div class="pr-4 pb-8 flex max-h-full min-h-full">
  139. <div
  140. class="w-full {$showOverview && !$showCallOverlay
  141. ? ' '
  142. : '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"
  143. >
  144. {#if $showCallOverlay}
  145. <div class="w-full h-full flex justify-center">
  146. <CallOverlay
  147. bind:files
  148. {submitPrompt}
  149. {stopResponse}
  150. {modelId}
  151. {chatId}
  152. {eventTarget}
  153. on:close={() => {
  154. showControls.set(false);
  155. }}
  156. />
  157. </div>
  158. {:else if $showOverview}
  159. <Overview
  160. {history}
  161. on:nodeclick={(e) => {
  162. if (e.detail.node.data.message.favorite) {
  163. history.messages[e.detail.node.data.message.id].favorite = true;
  164. } else {
  165. history.messages[e.detail.node.data.message.id].favorite = null;
  166. }
  167. showMessage(e.detail.node.data.message);
  168. }}
  169. on:close={() => {
  170. showControls.set(false);
  171. }}
  172. />
  173. {:else}
  174. <Controls
  175. on:close={() => {
  176. showControls.set(false);
  177. }}
  178. {models}
  179. bind:chatFiles
  180. bind:params
  181. />
  182. {/if}
  183. </div>
  184. </div>
  185. {/if}
  186. </Pane>
  187. {/if}
  188. </SvelteFlowProvider>