General.svelte 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. <script lang="ts">
  2. import { toast } from 'svelte-sonner';
  3. import { createEventDispatcher, onMount } from 'svelte';
  4. const dispatch = createEventDispatcher();
  5. import { models, user } from '$lib/stores';
  6. import AdvancedParams from './Advanced/AdvancedParams.svelte';
  7. export let saveSettings: Function;
  8. export let getModels: Function;
  9. // General
  10. let themes = ['dark', 'light', 'rose-pine dark', 'rose-pine-dawn light'];
  11. let theme = 'dark';
  12. let notificationEnabled = false;
  13. let system = '';
  14. let showAdvanced = false;
  15. const toggleNotification = async () => {
  16. const permission = await Notification.requestPermission();
  17. if (permission === 'granted') {
  18. notificationEnabled = !notificationEnabled;
  19. saveSettings({ notificationEnabled: notificationEnabled });
  20. } else {
  21. toast.error(
  22. 'Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.'
  23. );
  24. }
  25. };
  26. // Advanced
  27. let requestFormat = '';
  28. let keepAlive = null;
  29. let options = {
  30. // Advanced
  31. seed: 0,
  32. temperature: '',
  33. repeat_penalty: '',
  34. repeat_last_n: '',
  35. mirostat: '',
  36. mirostat_eta: '',
  37. mirostat_tau: '',
  38. top_k: '',
  39. top_p: '',
  40. stop: '',
  41. tfs_z: '',
  42. num_ctx: '',
  43. num_predict: ''
  44. };
  45. const toggleRequestFormat = async () => {
  46. if (requestFormat === '') {
  47. requestFormat = 'json';
  48. } else {
  49. requestFormat = '';
  50. }
  51. saveSettings({ requestFormat: requestFormat !== '' ? requestFormat : undefined });
  52. };
  53. onMount(async () => {
  54. let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
  55. theme = localStorage.theme ?? 'dark';
  56. notificationEnabled = settings.notificationEnabled ?? false;
  57. system = settings.system ?? '';
  58. requestFormat = settings.requestFormat ?? '';
  59. keepAlive = settings.keepAlive ?? null;
  60. options.seed = settings.seed ?? 0;
  61. options.temperature = settings.temperature ?? '';
  62. options.repeat_penalty = settings.repeat_penalty ?? '';
  63. options.top_k = settings.top_k ?? '';
  64. options.top_p = settings.top_p ?? '';
  65. options.num_ctx = settings.num_ctx ?? '';
  66. options = { ...options, ...settings.options };
  67. options.stop = (settings?.options?.stop ?? []).join(',');
  68. });
  69. </script>
  70. <div class="flex flex-col h-full justify-between text-sm">
  71. <div class=" pr-1.5 overflow-y-scroll max-h-[20.5rem]">
  72. <div class="">
  73. <div class=" mb-1 text-sm font-medium">WebUI Settings</div>
  74. <div class=" py-0.5 flex w-full justify-between">
  75. <div class=" self-center text-xs font-medium">Theme</div>
  76. <div class="flex items-center relative">
  77. <div class=" absolute right-16">
  78. {#if theme === 'dark'}
  79. <svg
  80. xmlns="http://www.w3.org/2000/svg"
  81. viewBox="0 0 20 20"
  82. fill="currentColor"
  83. class="w-4 h-4"
  84. >
  85. <path
  86. fill-rule="evenodd"
  87. d="M7.455 2.004a.75.75 0 01.26.77 7 7 0 009.958 7.967.75.75 0 011.067.853A8.5 8.5 0 116.647 1.921a.75.75 0 01.808.083z"
  88. clip-rule="evenodd"
  89. />
  90. </svg>
  91. {:else if theme === 'light'}
  92. <svg
  93. xmlns="http://www.w3.org/2000/svg"
  94. viewBox="0 0 20 20"
  95. fill="currentColor"
  96. class="w-4 h-4 self-center"
  97. >
  98. <path
  99. d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
  100. />
  101. </svg>
  102. {/if}
  103. </div>
  104. <select
  105. class="w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
  106. bind:value={theme}
  107. placeholder="Select a theme"
  108. on:change={(e) => {
  109. localStorage.theme = theme;
  110. themes
  111. .filter((e) => e !== theme)
  112. .forEach((e) => {
  113. e.split(' ').forEach((e) => {
  114. document.documentElement.classList.remove(e);
  115. });
  116. });
  117. theme.split(' ').forEach((e) => {
  118. document.documentElement.classList.add(e);
  119. });
  120. console.log(theme);
  121. }}
  122. >
  123. <option value="dark">Dark</option>
  124. <option value="light">Light</option>
  125. <option value="rose-pine dark">Rosé Pine</option>
  126. <option value="rose-pine-dawn light">Rosé Pine Dawn</option>
  127. </select>
  128. </div>
  129. </div>
  130. <div>
  131. <div class=" py-0.5 flex w-full justify-between">
  132. <div class=" self-center text-xs font-medium">Notification</div>
  133. <button
  134. class="p-1 px-3 text-xs flex rounded transition"
  135. on:click={() => {
  136. toggleNotification();
  137. }}
  138. type="button"
  139. >
  140. {#if notificationEnabled === true}
  141. <span class="ml-2 self-center">On</span>
  142. {:else}
  143. <span class="ml-2 self-center">Off</span>
  144. {/if}
  145. </button>
  146. </div>
  147. </div>
  148. </div>
  149. <hr class=" dark:border-gray-700 my-3" />
  150. <div>
  151. <div class=" my-2.5 text-sm font-medium">System Prompt</div>
  152. <textarea
  153. bind:value={system}
  154. class="w-full rounded-lg p-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
  155. rows="4"
  156. />
  157. </div>
  158. <div class="mt-2 space-y-3 pr-1.5">
  159. <div class="flex justify-between items-center text-sm">
  160. <div class=" font-medium">Advanced Parameters</div>
  161. <button
  162. class=" text-xs font-medium text-gray-500"
  163. type="button"
  164. on:click={() => {
  165. showAdvanced = !showAdvanced;
  166. }}>{showAdvanced ? 'Hide' : 'Show'}</button
  167. >
  168. </div>
  169. {#if showAdvanced}
  170. <AdvancedParams bind:options />
  171. <hr class=" dark:border-gray-700" />
  172. <div class=" py-1 w-full justify-between">
  173. <div class="flex w-full justify-between">
  174. <div class=" self-center text-xs font-medium">Keep Alive</div>
  175. <button
  176. class="p-1 px-3 text-xs flex rounded transition"
  177. type="button"
  178. on:click={() => {
  179. keepAlive = keepAlive === null ? '5m' : null;
  180. }}
  181. >
  182. {#if keepAlive === null}
  183. <span class="ml-2 self-center"> Default </span>
  184. {:else}
  185. <span class="ml-2 self-center"> Custom </span>
  186. {/if}
  187. </button>
  188. </div>
  189. {#if keepAlive !== null}
  190. <div class="flex mt-1 space-x-2">
  191. <input
  192. class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
  193. type="text"
  194. placeholder={`e.g.) "30s","10m". Valid time units are "s", "m", "h".`}
  195. bind:value={keepAlive}
  196. />
  197. </div>
  198. {/if}
  199. </div>
  200. <div>
  201. <div class=" py-1 flex w-full justify-between">
  202. <div class=" self-center text-sm font-medium">Request Mode</div>
  203. <button
  204. class="p-1 px-3 text-xs flex rounded transition"
  205. on:click={() => {
  206. toggleRequestFormat();
  207. }}
  208. >
  209. {#if requestFormat === ''}
  210. <span class="ml-2 self-center"> Default </span>
  211. {:else if requestFormat === 'json'}
  212. <!-- <svg
  213. xmlns="http://www.w3.org/2000/svg"
  214. viewBox="0 0 20 20"
  215. fill="currentColor"
  216. class="w-4 h-4 self-center"
  217. >
  218. <path
  219. d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
  220. />
  221. </svg> -->
  222. <span class="ml-2 self-center"> JSON </span>
  223. {/if}
  224. </button>
  225. </div>
  226. </div>
  227. {/if}
  228. </div>
  229. </div>
  230. <div class="flex justify-end pt-3 text-sm font-medium">
  231. <button
  232. class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
  233. on:click={() => {
  234. saveSettings({
  235. system: system !== '' ? system : undefined,
  236. options: {
  237. seed: (options.seed !== 0 ? options.seed : undefined) ?? undefined,
  238. stop: options.stop !== '' ? options.stop.split(',').filter((e) => e) : undefined,
  239. temperature: options.temperature !== '' ? options.temperature : undefined,
  240. repeat_penalty: options.repeat_penalty !== '' ? options.repeat_penalty : undefined,
  241. repeat_last_n: options.repeat_last_n !== '' ? options.repeat_last_n : undefined,
  242. mirostat: options.mirostat !== '' ? options.mirostat : undefined,
  243. mirostat_eta: options.mirostat_eta !== '' ? options.mirostat_eta : undefined,
  244. mirostat_tau: options.mirostat_tau !== '' ? options.mirostat_tau : undefined,
  245. top_k: options.top_k !== '' ? options.top_k : undefined,
  246. top_p: options.top_p !== '' ? options.top_p : undefined,
  247. tfs_z: options.tfs_z !== '' ? options.tfs_z : undefined,
  248. num_ctx: options.num_ctx !== '' ? options.num_ctx : undefined,
  249. num_predict: options.num_predict !== '' ? options.num_predict : undefined
  250. },
  251. keepAlive: keepAlive ? (isNaN(keepAlive) ? keepAlive : parseInt(keepAlive)) : undefined
  252. });
  253. dispatch('save');
  254. }}
  255. >
  256. Save
  257. </button>
  258. </div>
  259. </div>