Images.svelte 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <script lang="ts">
  2. import { toast } from 'svelte-sonner';
  3. import { createEventDispatcher, onMount } from 'svelte';
  4. import { config, user } from '$lib/stores';
  5. import {
  6. getAUTOMATIC1111Url,
  7. getImageGenerationModels,
  8. getDefaultImageGenerationModel,
  9. updateDefaultImageGenerationModel,
  10. getImageSize,
  11. getImageGenerationConfig,
  12. updateImageGenerationConfig,
  13. updateAUTOMATIC1111Url,
  14. updateImageSize,
  15. getImageSteps,
  16. updateImageSteps
  17. } from '$lib/apis/images';
  18. import { getBackendConfig } from '$lib/apis';
  19. const dispatch = createEventDispatcher();
  20. export let saveSettings: Function;
  21. let loading = false;
  22. let imageGenerationEngine = '';
  23. let enableImageGeneration = false;
  24. let AUTOMATIC1111_BASE_URL = '';
  25. let selectedModel = '';
  26. let models = null;
  27. let imageSize = '';
  28. let steps = 50;
  29. const getModels = async () => {
  30. models = await getImageGenerationModels(localStorage.token).catch((error) => {
  31. toast.error(error);
  32. return null;
  33. });
  34. selectedModel = await getDefaultImageGenerationModel(localStorage.token).catch((error) => {
  35. return '';
  36. });
  37. };
  38. const updateAUTOMATIC1111UrlHandler = async () => {
  39. const res = await updateAUTOMATIC1111Url(localStorage.token, AUTOMATIC1111_BASE_URL).catch(
  40. (error) => {
  41. toast.error(error);
  42. return null;
  43. }
  44. );
  45. if (res) {
  46. AUTOMATIC1111_BASE_URL = res;
  47. await getModels();
  48. if (models) {
  49. toast.success('Server connection verified');
  50. }
  51. } else {
  52. AUTOMATIC1111_BASE_URL = await getAUTOMATIC1111Url(localStorage.token);
  53. }
  54. };
  55. const updateImageGeneration = async () => {
  56. const res = await updateImageGenerationConfig(
  57. localStorage.token,
  58. imageGenerationEngine,
  59. enableImageGeneration
  60. ).catch((error) => {
  61. toast.error(error);
  62. return null;
  63. });
  64. if (res) {
  65. imageGenerationEngine = res.engine;
  66. enableImageGeneration = res.enabled;
  67. }
  68. if (enableImageGeneration) {
  69. config.set(await getBackendConfig(localStorage.token));
  70. getModels();
  71. }
  72. };
  73. onMount(async () => {
  74. if ($user.role === 'admin') {
  75. const res = await getImageGenerationConfig(localStorage.token).catch((error) => {
  76. toast.error(error);
  77. return null;
  78. });
  79. if (res) {
  80. imageGenerationEngine = res.engine;
  81. enableImageGeneration = res.enabled;
  82. }
  83. AUTOMATIC1111_BASE_URL = await getAUTOMATIC1111Url(localStorage.token);
  84. imageSize = await getImageSize(localStorage.token);
  85. steps = await getImageSteps(localStorage.token);
  86. if (enableImageGeneration) {
  87. getModels();
  88. }
  89. }
  90. });
  91. </script>
  92. <form
  93. class="flex flex-col h-full justify-between space-y-3 text-sm"
  94. on:submit|preventDefault={async () => {
  95. loading = true;
  96. await updateDefaultImageGenerationModel(localStorage.token, selectedModel);
  97. await updateImageSize(localStorage.token, imageSize).catch((error) => {
  98. toast.error(error);
  99. return null;
  100. });
  101. await updateImageSteps(localStorage.token, steps).catch((error) => {
  102. toast.error(error);
  103. return null;
  104. });
  105. dispatch('save');
  106. loading = false;
  107. }}
  108. >
  109. <div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[20.5rem]">
  110. <div>
  111. <div class=" mb-1 text-sm font-medium">Image Settings</div>
  112. <div class=" py-0.5 flex w-full justify-between">
  113. <div class=" self-center text-xs font-medium">Image Generation Engine</div>
  114. <div class="flex items-center relative">
  115. <select
  116. class="w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
  117. bind:value={imageGenerationEngine}
  118. placeholder="Select a mode"
  119. on:change={async () => {
  120. await updateImageGeneration();
  121. }}
  122. >
  123. <option value="">Default (Automatic1111)</option>
  124. <option value="openai">Open AI (Dall-E)</option>
  125. </select>
  126. </div>
  127. </div>
  128. <div>
  129. <div class=" py-0.5 flex w-full justify-between">
  130. <div class=" self-center text-xs font-medium">Image Generation (Experimental)</div>
  131. <button
  132. class="p-1 px-3 text-xs flex rounded transition"
  133. on:click={() => {
  134. if (imageGenerationEngine === '' && AUTOMATIC1111_BASE_URL === '') {
  135. toast.error('AUTOMATIC1111 Base URL is required.');
  136. } else {
  137. enableImageGeneration = !enableImageGeneration;
  138. updateImageGeneration();
  139. }
  140. }}
  141. type="button"
  142. >
  143. {#if enableImageGeneration === true}
  144. <span class="ml-2 self-center">On</span>
  145. {:else}
  146. <span class="ml-2 self-center">Off</span>
  147. {/if}
  148. </button>
  149. </div>
  150. </div>
  151. </div>
  152. {#if imageGenerationEngine === ''}
  153. <hr class=" dark:border-gray-700" />
  154. <div class=" mb-2.5 text-sm font-medium">AUTOMATIC1111 Base URL</div>
  155. <div class="flex w-full">
  156. <div class="flex-1 mr-2">
  157. <input
  158. class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
  159. placeholder="Enter URL (e.g. http://127.0.0.1:7860/)"
  160. bind:value={AUTOMATIC1111_BASE_URL}
  161. />
  162. </div>
  163. <button
  164. class="px-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-700 rounded transition"
  165. type="button"
  166. on:click={() => {
  167. // updateOllamaAPIUrlHandler();
  168. updateAUTOMATIC1111UrlHandler();
  169. }}
  170. >
  171. <svg
  172. xmlns="http://www.w3.org/2000/svg"
  173. viewBox="0 0 20 20"
  174. fill="currentColor"
  175. class="w-4 h-4"
  176. >
  177. <path
  178. fill-rule="evenodd"
  179. d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
  180. clip-rule="evenodd"
  181. />
  182. </svg>
  183. </button>
  184. </div>
  185. <div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
  186. Include `--api` flag when running stable-diffusion-webui
  187. <a
  188. class=" text-gray-300 font-medium"
  189. href="https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/3734"
  190. target="_blank"
  191. >
  192. (e.g. `sh webui.sh --api`)
  193. </a>
  194. </div>
  195. {/if}
  196. {#if enableImageGeneration}
  197. <hr class=" dark:border-gray-700" />
  198. <div>
  199. <div class=" mb-2.5 text-sm font-medium">Set Default Model</div>
  200. <div class="flex w-full">
  201. <div class="flex-1 mr-2">
  202. <select
  203. class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
  204. bind:value={selectedModel}
  205. placeholder="Select a model"
  206. >
  207. {#if !selectedModel}
  208. <option value="" disabled selected>Select a model</option>
  209. {/if}
  210. {#each models ?? [] as model}
  211. <option value={model.id} class="bg-gray-100 dark:bg-gray-700">{model.name}</option>
  212. {/each}
  213. </select>
  214. </div>
  215. </div>
  216. </div>
  217. <div>
  218. <div class=" mb-2.5 text-sm font-medium">Set Image Size</div>
  219. <div class="flex w-full">
  220. <div class="flex-1 mr-2">
  221. <input
  222. class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
  223. placeholder="Enter Image Size (e.g. 512x512)"
  224. bind:value={imageSize}
  225. />
  226. </div>
  227. </div>
  228. </div>
  229. <div>
  230. <div class=" mb-2.5 text-sm font-medium">Set Steps</div>
  231. <div class="flex w-full">
  232. <div class="flex-1 mr-2">
  233. <input
  234. class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
  235. placeholder="Enter Number of Steps (e.g. 50)"
  236. bind:value={steps}
  237. />
  238. </div>
  239. </div>
  240. </div>
  241. {/if}
  242. </div>
  243. <div class="flex justify-end pt-3 text-sm font-medium">
  244. <button
  245. class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded flex flex-row space-x-1 items-center {loading
  246. ? ' cursor-not-allowed'
  247. : ''}"
  248. type="submit"
  249. disabled={loading}
  250. >
  251. Save
  252. {#if loading}
  253. <div class="ml-2 self-center">
  254. <svg
  255. class=" w-4 h-4"
  256. viewBox="0 0 24 24"
  257. fill="currentColor"
  258. xmlns="http://www.w3.org/2000/svg"
  259. ><style>
  260. .spinner_ajPY {
  261. transform-origin: center;
  262. animation: spinner_AtaB 0.75s infinite linear;
  263. }
  264. @keyframes spinner_AtaB {
  265. 100% {
  266. transform: rotate(360deg);
  267. }
  268. }
  269. </style><path
  270. d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
  271. opacity=".25"
  272. /><path
  273. d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
  274. class="spinner_ajPY"
  275. /></svg
  276. >
  277. </div>
  278. {/if}
  279. </button>
  280. </div>
  281. </form>