FolderModal.svelte 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <script lang="ts">
  2. import { getContext, createEventDispatcher, onMount, tick } from 'svelte';
  3. import Spinner from '$lib/components/common/Spinner.svelte';
  4. import Modal from '$lib/components/common/Modal.svelte';
  5. import XMark from '$lib/components/icons/XMark.svelte';
  6. import { toast } from 'svelte-sonner';
  7. import { page } from '$app/stores';
  8. import { goto } from '$app/navigation';
  9. import { user } from '$lib/stores';
  10. import Textarea from '$lib/components/common/Textarea.svelte';
  11. import Knowledge from '$lib/components/workspace/Models/Knowledge.svelte';
  12. const i18n = getContext('i18n');
  13. export let show = false;
  14. export let onSubmit: Function = (e) => {};
  15. export let edit = false;
  16. export let folder = null;
  17. let name = '';
  18. let data = {
  19. system_prompt: '',
  20. files: []
  21. };
  22. let loading = false;
  23. const submitHandler = async () => {
  24. loading = true;
  25. if ((data?.files ?? []).some((file) => file.status === 'uploading')) {
  26. toast.error($i18n.t('Please wait until all files are uploaded.'));
  27. loading = false;
  28. return;
  29. }
  30. await onSubmit({
  31. name,
  32. data
  33. });
  34. show = false;
  35. loading = false;
  36. };
  37. const init = () => {
  38. name = folder.name;
  39. data = folder.data || {
  40. system_prompt: '',
  41. files: []
  42. };
  43. };
  44. const focusInput = async () => {
  45. await tick();
  46. const input = document.getElementById('folder-name') as HTMLInputElement;
  47. if (input) {
  48. input.focus();
  49. input.select();
  50. }
  51. };
  52. $: if (show) {
  53. focusInput();
  54. }
  55. $: if (folder) {
  56. init();
  57. }
  58. $: if (!show && !edit) {
  59. name = '';
  60. data = {
  61. system_prompt: '',
  62. files: []
  63. };
  64. }
  65. </script>
  66. <Modal size="md" bind:show>
  67. <div>
  68. <div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-1">
  69. <div class=" text-lg font-medium self-center">
  70. {#if edit}
  71. {$i18n.t('Edit Folder')}
  72. {:else}
  73. {$i18n.t('Create Folder')}
  74. {/if}
  75. </div>
  76. <button
  77. class="self-center"
  78. on:click={() => {
  79. show = false;
  80. }}
  81. >
  82. <XMark className={'size-5'} />
  83. </button>
  84. </div>
  85. <div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
  86. <div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
  87. <form
  88. class="flex flex-col w-full"
  89. on:submit|preventDefault={() => {
  90. submitHandler();
  91. }}
  92. >
  93. <div class="flex flex-col w-full mt-1">
  94. <div class=" mb-1 text-xs text-gray-500">{$i18n.t('Folder Name')}</div>
  95. <div class="flex-1">
  96. <input
  97. id="folder-name"
  98. class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden"
  99. type="text"
  100. bind:value={name}
  101. placeholder={$i18n.t('Enter folder name')}
  102. autocomplete="off"
  103. />
  104. </div>
  105. </div>
  106. <hr class=" border-gray-50 dark:border-gray-850 my-2.5 w-full" />
  107. {#if $user?.role === 'admin' || ($user?.permissions.chat?.system_prompt ?? true)}
  108. <div class="my-1">
  109. <div class="mb-2 text-xs text-gray-500">{$i18n.t('System Prompt')}</div>
  110. <div>
  111. <Textarea
  112. className=" text-sm w-full bg-transparent outline-hidden "
  113. placeholder={$i18n.t(
  114. 'Write your model system prompt content here\ne.g.) You are Mario from Super Mario Bros, acting as an assistant.'
  115. )}
  116. maxSize={200}
  117. bind:value={data.system_prompt}
  118. />
  119. </div>
  120. </div>
  121. {/if}
  122. <div class="my-2">
  123. <Knowledge bind:selectedItems={data.files}>
  124. <div slot="label">
  125. <div class="flex w-full justify-between">
  126. <div class=" mb-2 text-xs text-gray-500">
  127. {$i18n.t('Knowledge')}
  128. </div>
  129. </div>
  130. </div>
  131. </Knowledge>
  132. </div>
  133. <div class="flex justify-end pt-3 text-sm font-medium gap-1.5">
  134. <button
  135. class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-950 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {loading
  136. ? ' cursor-not-allowed'
  137. : ''}"
  138. type="submit"
  139. disabled={loading}
  140. >
  141. {$i18n.t('Save')}
  142. {#if loading}
  143. <div class="ml-2 self-center">
  144. <Spinner />
  145. </div>
  146. {/if}
  147. </button>
  148. </div>
  149. </form>
  150. </div>
  151. </div>
  152. </div>
  153. </Modal>