ManageModal.svelte 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. <script lang="ts">
  2. import { toast } from 'svelte-sonner';
  3. import dayjs from 'dayjs';
  4. import { getContext, createEventDispatcher } from 'svelte';
  5. const dispatch = createEventDispatcher();
  6. import Modal from '$lib/components/common/Modal.svelte';
  7. import AddMemoryModal from './AddMemoryModal.svelte';
  8. import { deleteMemoriesByUserId, deleteMemoryById, getMemories } from '$lib/apis/memories';
  9. import Tooltip from '$lib/components/common/Tooltip.svelte';
  10. import { error } from '@sveltejs/kit';
  11. import EditMemoryModal from './EditMemoryModal.svelte';
  12. import localizedFormat from 'dayjs/plugin/localizedFormat';
  13. import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
  14. const i18n = getContext('i18n');
  15. dayjs.extend(localizedFormat);
  16. export let show = false;
  17. let memories = [];
  18. let loading = true;
  19. let showAddMemoryModal = false;
  20. let showEditMemoryModal = false;
  21. let selectedMemory = null;
  22. let showClearConfirmDialog = false;
  23. let onClearConfirmed = async () => {
  24. const res = await deleteMemoriesByUserId(localStorage.token).catch((error) => {
  25. toast.error(`${error}`);
  26. return null;
  27. });
  28. if (res && memories.length > 0) {
  29. toast.success($i18n.t('Memory cleared successfully'));
  30. memories = [];
  31. }
  32. showClearConfirmDialog = false;
  33. };
  34. $: if (show && memories.length === 0 && loading) {
  35. (async () => {
  36. memories = await getMemories(localStorage.token);
  37. loading = false;
  38. })();
  39. }
  40. </script>
  41. <Modal size="xl" bind:show>
  42. <div>
  43. <div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-1">
  44. <div class=" text-lg font-medium self-center">{$i18n.t('Memory')}</div>
  45. <button
  46. class="self-center"
  47. on:click={() => {
  48. show = false;
  49. }}
  50. >
  51. <svg
  52. xmlns="http://www.w3.org/2000/svg"
  53. viewBox="0 0 20 20"
  54. fill="currentColor"
  55. class="w-5 h-5"
  56. >
  57. <path
  58. d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
  59. />
  60. </svg>
  61. </button>
  62. </div>
  63. <div class="flex flex-col w-full px-5 pb-5 dark:text-gray-200">
  64. <div
  65. class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6 h-[28rem] max-h-screen outline outline-1 rounded-xl outline-gray-100 dark:outline-gray-800 mb-4 mt-1"
  66. >
  67. {#if memories.length > 0}
  68. <div class="text-left text-sm w-full mb-4 overflow-y-scroll">
  69. <div class="relative overflow-x-auto">
  70. <table class="w-full text-sm text-left text-gray-600 dark:text-gray-400 table-auto">
  71. <thead
  72. class="text-xs text-gray-700 uppercase bg-transparent dark:text-gray-200 border-b-2 dark:border-gray-850"
  73. >
  74. <tr>
  75. <th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
  76. <th scope="col" class="px-3 py-2 hidden md:flex">
  77. {$i18n.t('Last Modified')}
  78. </th>
  79. <th scope="col" class="px-3 py-2 text-right" />
  80. </tr>
  81. </thead>
  82. <tbody>
  83. {#each memories as memory}
  84. <tr class="border-b dark:border-gray-850 items-center">
  85. <td class="px-3 py-1">
  86. <div class="line-clamp-1">
  87. {memory.content}
  88. </div>
  89. </td>
  90. <td class=" px-3 py-1 hidden md:flex h-[2.5rem]">
  91. <div class="my-auto whitespace-nowrap">
  92. {dayjs(memory.updated_at * 1000).format('LLL')}
  93. </div>
  94. </td>
  95. <td class="px-3 py-1">
  96. <div class="flex justify-end w-full">
  97. <Tooltip content="Edit">
  98. <button
  99. class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
  100. on:click={() => {
  101. selectedMemory = memory;
  102. showEditMemoryModal = true;
  103. }}
  104. >
  105. <svg
  106. xmlns="http://www.w3.org/2000/svg"
  107. fill="none"
  108. viewBox="0 0 24 24"
  109. stroke-width="1.5"
  110. stroke="currentColor"
  111. class="w-4 h-4 s-FoVA_WMOgxUD"
  112. ><path
  113. stroke-linecap="round"
  114. stroke-linejoin="round"
  115. d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
  116. class="s-FoVA_WMOgxUD"
  117. /></svg
  118. >
  119. </button>
  120. </Tooltip>
  121. <Tooltip content="Delete">
  122. <button
  123. class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
  124. on:click={async () => {
  125. const res = await deleteMemoryById(
  126. localStorage.token,
  127. memory.id
  128. ).catch((error) => {
  129. toast.error(`${error}`);
  130. return null;
  131. });
  132. if (res) {
  133. toast.success($i18n.t('Memory deleted successfully'));
  134. memories = await getMemories(localStorage.token);
  135. }
  136. }}
  137. >
  138. <svg
  139. xmlns="http://www.w3.org/2000/svg"
  140. fill="none"
  141. viewBox="0 0 24 24"
  142. stroke-width="1.5"
  143. stroke="currentColor"
  144. class="w-4 h-4"
  145. >
  146. <path
  147. stroke-linecap="round"
  148. stroke-linejoin="round"
  149. d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
  150. />
  151. </svg>
  152. </button>
  153. </Tooltip>
  154. </div>
  155. </td>
  156. </tr>
  157. {/each}
  158. </tbody>
  159. </table>
  160. </div>
  161. </div>
  162. {:else}
  163. <div class="text-center flex h-full text-sm w-full">
  164. <div class=" my-auto pb-10 px-4 w-full text-gray-500">
  165. {$i18n.t('Memories accessible by LLMs will be shown here.')}
  166. </div>
  167. </div>
  168. {/if}
  169. </div>
  170. <div class="flex text-sm font-medium gap-1.5">
  171. <button
  172. class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-300 dark:outline-gray-800 rounded-3xl"
  173. on:click={() => {
  174. showAddMemoryModal = true;
  175. }}>{$i18n.t('Add Memory')}</button
  176. >
  177. <button
  178. class=" px-3.5 py-1.5 font-medium text-red-500 hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-red-300 dark:outline-red-800 rounded-3xl"
  179. on:click={() => {
  180. if (memories.length > 0) {
  181. showClearConfirmDialog = true;
  182. } else {
  183. toast.error($i18n.t('No memories to clear'));
  184. }
  185. }}>{$i18n.t('Clear memory')}</button
  186. >
  187. </div>
  188. </div>
  189. </div>
  190. </Modal>
  191. <ConfirmDialog
  192. title={$i18n.t('Clear Memory')}
  193. message={$i18n.t('Are you sure you want to clear all memories? This action cannot be undone.')}
  194. show={showClearConfirmDialog}
  195. on:confirm={onClearConfirmed}
  196. on:cancel={() => {
  197. showClearConfirmDialog = false;
  198. }}
  199. />
  200. <AddMemoryModal
  201. bind:show={showAddMemoryModal}
  202. on:save={async () => {
  203. memories = await getMemories(localStorage.token);
  204. }}
  205. />
  206. <EditMemoryModal
  207. bind:show={showEditMemoryModal}
  208. memory={selectedMemory}
  209. on:save={async () => {
  210. memories = await getMemories(localStorage.token);
  211. }}
  212. />