mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-13 21:05:19 +00:00
refac: images
This commit is contained in:
parent
314cac0113
commit
72900cd686
12 changed files with 660 additions and 477 deletions
|
|
@ -210,7 +210,7 @@
|
|||
<div class=" space-y-3 overflow-y-scroll scrollbar-hidden h-full">
|
||||
<div class="flex flex-col gap-3">
|
||||
<div>
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Speech-to-Text')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Speech-to-Text')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
@ -498,7 +498,7 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Text-to-Speech')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Text-to-Speech')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
{#if config}
|
||||
<div>
|
||||
<div class="mb-3.5">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
@ -164,7 +164,7 @@
|
|||
</div>
|
||||
|
||||
<div class="mb-3.5">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Code Interpreter')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Code Interpreter')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@
|
|||
<div class=" overflow-y-scroll scrollbar-hidden h-full">
|
||||
{#if ENABLE_OPENAI_API !== null && ENABLE_OLLAMA_API !== null && connectionsConfig !== null}
|
||||
<div class="mb-3.5">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@
|
|||
<div class=" space-y-2.5 overflow-y-scroll scrollbar-hidden h-full pr-1.5">
|
||||
<div class="">
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
@ -914,7 +914,7 @@
|
|||
|
||||
{#if !RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL}
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Embedding')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Embedding')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
@ -1089,7 +1089,7 @@
|
|||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Retrieval')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Retrieval')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
@ -1332,7 +1332,7 @@
|
|||
{/if}
|
||||
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Files')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Files')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
@ -1444,7 +1444,7 @@
|
|||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Integration')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Integration')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
@ -1464,7 +1464,7 @@
|
|||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Danger Zone')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Danger Zone')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@
|
|||
{#if evaluationConfig !== null}
|
||||
<div class="">
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
@ -119,7 +119,7 @@
|
|||
|
||||
{#if evaluationConfig.ENABLE_EVALUATION_ARENA_MODELS}
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium flex justify-between items-center">
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium flex justify-between items-center">
|
||||
<div>
|
||||
{$i18n.t('Manage')}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -118,11 +118,11 @@
|
|||
updateHandler();
|
||||
}}
|
||||
>
|
||||
<div class="mt-0.5 space-y-3 overflow-y-scroll scrollbar-hidden h-full">
|
||||
<div class="space-y-3 overflow-y-scroll scrollbar-hidden h-full">
|
||||
{#if adminConfig !== null}
|
||||
<div class="">
|
||||
<div class="mb-3.5">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
@ -280,7 +280,7 @@
|
|||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Authentication')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Authentication')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
@ -637,7 +637,7 @@
|
|||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Features')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Features')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
import Switch from '$lib/components/common/Switch.svelte';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
import Textarea from '$lib/components/common/Textarea.svelte';
|
||||
import CodeEditorModal from '$lib/components/common/CodeEditorModal.svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
let models = null;
|
||||
let config = null;
|
||||
|
||||
let showComfyUIWorkflowEditor = false;
|
||||
let requiredWorkflowNodes = [
|
||||
{
|
||||
type: 'prompt',
|
||||
|
|
@ -68,24 +70,48 @@
|
|||
};
|
||||
|
||||
const updateConfigHandler = async () => {
|
||||
const res = await updateConfig(localStorage.token, config)
|
||||
.catch((error) => {
|
||||
toast.error(`${error}`);
|
||||
if (
|
||||
config.IMAGE_GENERATION_ENGINE === 'automatic1111' &&
|
||||
config.AUTOMATIC1111_BASE_URL === ''
|
||||
) {
|
||||
toast.error($i18n.t('AUTOMATIC1111 Base URL is required.'));
|
||||
config.ENABLE_IMAGE_GENERATION = false;
|
||||
|
||||
return null;
|
||||
})
|
||||
.catch((error) => {
|
||||
} else if (config.IMAGE_GENERATION_ENGINE === 'comfyui' && config.COMFYUI_BASE_URL === '') {
|
||||
toast.error($i18n.t('ComfyUI Base URL is required.'));
|
||||
config.ENABLE_IMAGE_GENERATION = false;
|
||||
|
||||
return null;
|
||||
} else if (config.IMAGE_GENERATION_ENGINE === 'openai' && config.OPENAI_API_KEY === '') {
|
||||
toast.error($i18n.t('OpenAI API Key is required.'));
|
||||
config.ENABLE_IMAGE_GENERATION = false;
|
||||
|
||||
return null;
|
||||
} else if (config.IMAGE_GENERATION_ENGINE === 'gemini' && config.GEMINI_API_KEY === '') {
|
||||
toast.error($i18n.t('Gemini API Key is required.'));
|
||||
config.ENABLE_IMAGE_GENERATION = false;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const res = await updateConfig(localStorage.token, config).catch((error) => {
|
||||
toast.error(`${error}`);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
config = res;
|
||||
}
|
||||
|
||||
if (config.enabled) {
|
||||
if (config.ENABLE_IMAGE_GENERATION) {
|
||||
backendConfig.set(await getBackendConfig());
|
||||
getModels();
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const validateJSON = (json) => {
|
||||
|
|
@ -121,15 +147,11 @@
|
|||
});
|
||||
}
|
||||
|
||||
await updateConfig(localStorage.token, config).catch((error) => {
|
||||
toast.error(`${error}`);
|
||||
loading = false;
|
||||
return null;
|
||||
});
|
||||
|
||||
getModels();
|
||||
|
||||
const res = await updateConfigHandler();
|
||||
if (res) {
|
||||
dispatch('save');
|
||||
}
|
||||
|
||||
loading = false;
|
||||
};
|
||||
|
||||
|
|
@ -179,76 +201,49 @@
|
|||
<div class=" space-y-3 overflow-y-scroll scrollbar-hidden pr-2">
|
||||
{#if config}
|
||||
<div>
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Create Image')}</div>
|
||||
<div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Create Image')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
<div>
|
||||
<div class=" py-1 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">
|
||||
{$i18n.t('Image Generation (Experimental)')}
|
||||
</div>
|
||||
|
||||
<div class="px-1">
|
||||
<Switch
|
||||
bind:state={config.ENABLE_IMAGE_GENERATION}
|
||||
on:change={(e) => {
|
||||
const enabled = e.detail;
|
||||
|
||||
if (enabled) {
|
||||
if (
|
||||
config.IMAGE_GENERATION_ENGINE === 'automatic1111' &&
|
||||
config.AUTOMATIC1111_BASE_URL === ''
|
||||
) {
|
||||
toast.error($i18n.t('AUTOMATIC1111 Base URL is required.'));
|
||||
config.ENABLE_IMAGE_GENERATION = false;
|
||||
} else if (
|
||||
config.IMAGE_GENERATION_ENGINE === 'comfyui' &&
|
||||
config.COMFYUI_BASE_URL === ''
|
||||
) {
|
||||
toast.error($i18n.t('ComfyUI Base URL is required.'));
|
||||
config.ENABLE_IMAGE_GENERATION = false;
|
||||
} else if (
|
||||
config.IMAGE_GENERATION_ENGINE === 'openai' &&
|
||||
config.OPENAI_API_KEY === ''
|
||||
) {
|
||||
toast.error($i18n.t('OpenAI API Key is required.'));
|
||||
config.ENABLE_IMAGE_GENERATION = false;
|
||||
} else if (
|
||||
config.IMAGE_GENERATION_ENGINE === 'gemini' &&
|
||||
config.GEMINI_API_KEY === ''
|
||||
) {
|
||||
toast.error($i18n.t('Gemini API Key is required.'));
|
||||
config.ENABLE_IMAGE_GENERATION = false;
|
||||
}
|
||||
}
|
||||
|
||||
updateConfigHandler();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2">
|
||||
<div class="">
|
||||
{$i18n.t('Image Generation')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Switch bind:state={config.ENABLE_IMAGE_GENERATION} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if config.ENABLE_IMAGE_GENERATION}
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2">
|
||||
<div class="">
|
||||
{$i18n.t('Image Prompt Generation')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if config.enabled}
|
||||
<div class=" py-1 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Image Prompt Generation')}</div>
|
||||
<div class="px-1">
|
||||
<Switch bind:state={config.ENABLE_IMAGE_PROMPT_GENERATION} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class=" py-1 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Image Generation Engine')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2">
|
||||
<div class="">
|
||||
{$i18n.t('Image Generation Engine')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<select
|
||||
class=" dark:bg-gray-900 w-fit pr-8 cursor-pointer rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
|
||||
class=" dark:bg-gray-900 w-fit pr-8 cursor-pointer rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
|
||||
bind:value={config.IMAGE_GENERATION_ENGINE}
|
||||
placeholder={$i18n.t('Select Engine')}
|
||||
on:change={async () => {
|
||||
updateConfigHandler();
|
||||
}}
|
||||
>
|
||||
<option value="openai">{$i18n.t('Default (Open AI)')}</option>
|
||||
<option value="comfyui">{$i18n.t('ComfyUI')}</option>
|
||||
|
|
@ -257,23 +252,158 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class=" border-gray-100 dark:border-gray-850" />
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
{#if (config?.IMAGE_GENERATION_ENGINE ?? 'automatic1111') === 'automatic1111'}
|
||||
<div>
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('AUTOMATIC1111 Base URL')}</div>
|
||||
{#if config.ENABLE_IMAGE_GENERATION}
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2">
|
||||
<div class="">
|
||||
{$i18n.t('Default Model')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tooltip content={$i18n.t('Enter Model ID')} placement="top-start">
|
||||
<input
|
||||
list="model-list"
|
||||
class=" rounded-lg text-right text-sm bg-transparent outline-hidden"
|
||||
bind:value={config.IMAGE_GENERATION_MODEL}
|
||||
placeholder={$i18n.t('Select a model')}
|
||||
required
|
||||
/>
|
||||
|
||||
<datalist id="model-list">
|
||||
{#each models ?? [] as model}
|
||||
<option value={model.id}>{model.name}</option>
|
||||
{/each}
|
||||
</datalist>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2">
|
||||
<div class="">
|
||||
{$i18n.t('Image Size')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tooltip content={$i18n.t('Enter Image Size (e.g. 512x512)')} placement="top-start">
|
||||
<input
|
||||
class=" rounded-lg text-right text-sm bg-transparent outline-hidden"
|
||||
placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
|
||||
bind:value={config.IMAGE_SIZE}
|
||||
required
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if ['comfyui', 'automatic1111', ''].includes(config?.IMAGE_GENERATION_ENGINE)}
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2">
|
||||
<div class="">
|
||||
{$i18n.t('Steps')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tooltip
|
||||
content={$i18n.t('Enter Number of Steps (e.g. 50)')}
|
||||
placement="top-start"
|
||||
>
|
||||
<input
|
||||
class=" rounded-lg text-right text-sm bg-transparent outline-hidden"
|
||||
placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')}
|
||||
bind:value={config.IMAGE_STEPS}
|
||||
required
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if config?.IMAGE_GENERATION_ENGINE === 'openai'}
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2 shrink-0">
|
||||
<div class="">
|
||||
{$i18n.t('OpenAI API Base URL')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full text-sm bg-transparent outline-hidden text-right"
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
bind:value={config.IMAGES_OPENAI_API_BASE_URL}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2 shrink-0">
|
||||
<div class="">
|
||||
{$i18n.t('OpenAI API Key')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<SensitiveInput
|
||||
inputClassName="text-right w-full"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
bind:value={config.IMAGES_OPENAI_API_KEY}
|
||||
required={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2 shrink-0">
|
||||
<div class="">
|
||||
{$i18n.t('OpenAI API Version')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full text-sm bg-transparent outline-hidden text-right"
|
||||
placeholder={$i18n.t('API Version')}
|
||||
bind:value={config.IMAGES_OPENAI_API_VERSION}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if (config?.IMAGE_GENERATION_ENGINE ?? 'automatic1111') === 'automatic1111'}
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2 shrink-0">
|
||||
<div class="">
|
||||
{$i18n.t('AUTOMATIC1111 Base URL')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
||||
class="w-full text-sm bg-transparent outline-hidden text-right"
|
||||
placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
|
||||
bind:value={config.AUTOMATIC1111_BASE_URL}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
class="px-2.5 bg-gray-50 hover:bg-gray-100 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
|
||||
class=" rounded-lg transition"
|
||||
type="button"
|
||||
aria-label="verify connection"
|
||||
on:click={async () => {
|
||||
|
|
@ -302,8 +432,9 @@
|
|||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
<div class="mt-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
{$i18n.t('Include `--api` flag when running stable-diffusion-webui')}
|
||||
<a
|
||||
class=" text-gray-300 font-medium"
|
||||
|
|
@ -315,17 +446,27 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2 text-sm font-medium">
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2 shrink-0">
|
||||
<div class="">
|
||||
{$i18n.t('AUTOMATIC1111 Api Auth String')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<SensitiveInput
|
||||
inputClassName="text-right w-full"
|
||||
placeholder={$i18n.t('Enter api auth string (e.g. username:password)')}
|
||||
bind:value={config.AUTOMATIC1111_API_AUTH}
|
||||
required={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
<div class="mt-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
{$i18n.t('Include `--api-auth` flag when running stable-diffusion-webui')}
|
||||
<a
|
||||
class=" text-gray-300 font-medium"
|
||||
|
|
@ -339,13 +480,18 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
<div class="w-full">
|
||||
<div class=" mb-1.5 text-xs font-medium">{$i18n.t('Additional Parameters')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2 shrink-0">
|
||||
<div class="">
|
||||
{$i18n.t('Additional Parameters')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-1.5 flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<Textarea
|
||||
className="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
||||
className="w-full rounded-lg py-2 px-3 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
||||
bind:value={config.AUTOMATIC1111_PARAMS}
|
||||
placeholder={$i18n.t('Enter additional parameters in JSON format')}
|
||||
minSize={100}
|
||||
|
|
@ -353,20 +499,25 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if config?.IMAGE_GENERATION_ENGINE === 'comfyui'}
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2 shrink-0">
|
||||
<div class="">
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('ComfyUI Base URL')}</div>
|
||||
{$i18n.t('ComfyUI Base URL')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
||||
class="w-full text-sm bg-transparent outline-hidden text-right"
|
||||
placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
|
||||
bind:value={config.COMFYUI_BASE_URL}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
class="px-2.5 bg-gray-50 hover:bg-gray-100 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
|
||||
class=" rounded-lg transition"
|
||||
type="button"
|
||||
aria-label="verify connection"
|
||||
on:click={async () => {
|
||||
|
|
@ -396,12 +547,20 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2 shrink-0">
|
||||
<div class="">
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('ComfyUI API Key')}</div>
|
||||
{$i18n.t('ComfyUI API Key')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<div class="flex-1">
|
||||
<SensitiveInput
|
||||
inputClassName="text-right w-full"
|
||||
placeholder={$i18n.t('sk-1234')}
|
||||
bind:value={config.COMFYUI_API_KEY}
|
||||
required={false}
|
||||
|
|
@ -409,21 +568,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('ComfyUI Workflow')}</div>
|
||||
|
||||
{#if config.COMFYUI_WORKFLOW}
|
||||
<Textarea
|
||||
class="w-full rounded-lg mb-1 py-2 px-4 text-xs bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden disabled:text-gray-600 resize-none"
|
||||
rows="10"
|
||||
bind:value={config.COMFYUI_WORKFLOW}
|
||||
required
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<div class="mb-2.5">
|
||||
<input
|
||||
id="upload-comfyui-workflow-input"
|
||||
hidden
|
||||
|
|
@ -441,42 +588,93 @@
|
|||
reader.readAsText(file);
|
||||
}}
|
||||
/>
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2 shrink-0">
|
||||
<div class="">
|
||||
{$i18n.t('ComfyUI Workflow')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2 justify-end flex gap-1">
|
||||
{#if config.COMFYUI_WORKFLOW}
|
||||
<button
|
||||
class="w-full text-sm font-medium py-2 bg-transparent hover:bg-gray-50 border border-dashed border-gray-50 dark:border-gray-850 dark:hover:bg-gray-850 text-center rounded-xl"
|
||||
class="text-xs text-gray-700 dark:text-gray-400 underline"
|
||||
type="button"
|
||||
aria-label={$i18n.t('Edit workflow.json content')}
|
||||
on:click={() => {
|
||||
// open code editor modal
|
||||
showComfyUIWorkflowEditor = true;
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Edit')}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<Tooltip content={$i18n.t('Click here to upload a workflow.json file.')}>
|
||||
<button
|
||||
class="text-xs text-gray-700 dark:text-gray-400 underline"
|
||||
type="button"
|
||||
aria-label={$i18n.t('Click here to upload a workflow.json file.')}
|
||||
on:click={() => {
|
||||
document.getElementById('upload-comfyui-workflow-input')?.click();
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Click here to upload a workflow.json file.')}
|
||||
{$i18n.t('Upload')}
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
<div class="mt-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
<CodeEditorModal
|
||||
bind:show={showComfyUIWorkflowEditor}
|
||||
value={config.COMFYUI_WORKFLOW}
|
||||
lang="json"
|
||||
onChange={(e) => {
|
||||
config.COMFYUI_WORKFLOW = e;
|
||||
}}
|
||||
onSave={() => {
|
||||
console.log('Saved');
|
||||
}}
|
||||
/>
|
||||
<!-- {#if config.COMFYUI_WORKFLOW}
|
||||
<Textarea
|
||||
class="w-full rounded-lg my-1 py-2 px-3 text-xs bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden disabled:text-gray-600 resize-none"
|
||||
rows="10"
|
||||
bind:value={config.COMFYUI_WORKFLOW}
|
||||
required
|
||||
/>
|
||||
{/if} -->
|
||||
{$i18n.t('Make sure to export a workflow.json file as API format from ComfyUI.')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if config.COMFYUI_WORKFLOW}
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2 shrink-0">
|
||||
<div class="">
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('ComfyUI Workflow Nodes')}</div>
|
||||
{$i18n.t('ComfyUI Workflow Nodes')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-xs flex flex-col gap-1.5">
|
||||
<div class="mt-1 text-xs flex flex-col gap-1.5">
|
||||
{#each requiredWorkflowNodes as node}
|
||||
<div class="flex w-full items-center">
|
||||
<div class="flex w-full flex-col">
|
||||
<div class="shrink-0">
|
||||
<div
|
||||
class=" capitalize line-clamp-1 font-medium px-3 py-1 w-20 text-center bg-green-500/10 text-green-700 dark:text-green-200"
|
||||
>
|
||||
<div class=" capitalize line-clamp-1 w-20 text-gray-400 dark:text-gray-500">
|
||||
{node.type}{node.type === 'prompt' ? '*' : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex mt-0.5 items-center">
|
||||
<div class="">
|
||||
<Tooltip content={$i18n.t('Input Key (e.g. text, unet_name, steps)')}>
|
||||
<input
|
||||
class="py-1 px-3 w-24 text-xs text-center bg-transparent outline-hidden border-r border-gray-50 dark:border-gray-850"
|
||||
class="py-1 w-24 text-xs bg-transparent outline-hidden"
|
||||
placeholder={$i18n.t('Key')}
|
||||
bind:value={node.key}
|
||||
required
|
||||
|
|
@ -484,152 +682,74 @@
|
|||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="px-2 text-gray-400 dark:text-gray-500">:</div>
|
||||
|
||||
<div class="w-full">
|
||||
<Tooltip
|
||||
content={$i18n.t('Comma separated Node Ids (e.g. 1 or 1,2)')}
|
||||
placement="top-start"
|
||||
>
|
||||
<input
|
||||
class="w-full py-1 px-4 text-xs bg-transparent outline-hidden"
|
||||
class="w-full py-1 text-xs bg-transparent outline-hidden"
|
||||
placeholder={$i18n.t('Node Ids')}
|
||||
bind:value={node.node_ids}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="mt-2 text-xs text-right text-gray-400 dark:text-gray-500">
|
||||
<div class="mt-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
{$i18n.t('*Prompt node ID(s) are required for image generation')}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{:else if config?.IMAGE_GENERATION_ENGINE === 'openai'}
|
||||
<div>
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('OpenAI API Config')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
bind:value={config.IMAGES_OPENAI_API_BASE_URL}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('API Key')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<SensitiveInput
|
||||
placeholder={$i18n.t('API Key')}
|
||||
bind:value={config.IMAGES_OPENAI_API_KEY}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('API Version')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
||||
placeholder={$i18n.t('API Version')}
|
||||
bind:value={config.IMAGES_OPENAI_API_VERSION}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if config?.IMAGE_GENERATION_ENGINE === 'gemini'}
|
||||
<div>
|
||||
<div class=" mb-1.5 text-sm font-medium">{$i18n.t('Gemini API Config')}</div>
|
||||
|
||||
<div class="flex gap-2 mb-1">
|
||||
<input
|
||||
class="flex-1 w-full text-sm bg-transparent outline-none"
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
bind:value={config.IMAGES_GEMINI_API_BASE_URL}
|
||||
required
|
||||
/>
|
||||
|
||||
<SensitiveInput
|
||||
placeholder={$i18n.t('API Key')}
|
||||
bind:value={config.IMAGES_GEMINI_API_KEY}
|
||||
/>
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2 shrink-0">
|
||||
<div class="">
|
||||
{$i18n.t('Gemini Base URL')}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if config?.ENABLE_IMAGE_GENERATION}
|
||||
<hr class=" border-gray-100 dark:border-gray-850" />
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Default Model')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<Tooltip content={$i18n.t('Enter Model ID')} placement="top-start">
|
||||
<input
|
||||
list="model-list"
|
||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
||||
bind:value={config.IMAGE_GENERATION_MODEL}
|
||||
placeholder={$i18n.t('Select a model')}
|
||||
required
|
||||
class="w-full text-sm bg-transparent outline-hidden text-right"
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
bind:value={config.IMAGES_GEMINI_API_BASE_URL}
|
||||
/>
|
||||
|
||||
<datalist id="model-list">
|
||||
{#each models ?? [] as model}
|
||||
<option value={model.id}>{model.name}</option>
|
||||
{/each}
|
||||
</datalist>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Image Size')}</div>
|
||||
<div class="mb-2.5">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="text-xs pr-2 shrink-0">
|
||||
<div class="">
|
||||
{$i18n.t('Gemini API Key')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<Tooltip content={$i18n.t('Enter Image Size (e.g. 512x512)')} placement="top-start">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
||||
placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
|
||||
bind:value={config.IMAGE_SIZE}
|
||||
required
|
||||
<div class="flex-1">
|
||||
<SensitiveInput
|
||||
inputClassName="text-right w-full"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
bind:value={config.IMAGES_GEMINI_API_KEY}
|
||||
required={true}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if ['comfyui', 'automatic1111', ''].includes(config?.IMAGE_GENERATION_ENGINE)}
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Steps')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<Tooltip content={$i18n.t('Enter Number of Steps (e.g. 50)')} placement="top-start">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
||||
placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')}
|
||||
bind:value={config.IMAGE_STEPS}
|
||||
required
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@
|
|||
>
|
||||
<div class=" overflow-y-scroll scrollbar-hidden h-full pr-1.5">
|
||||
<div class="mb-3.5">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Tasks')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Tasks')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
@ -384,7 +384,7 @@
|
|||
</div>
|
||||
|
||||
<div class="mb-3.5">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('UI')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('UI')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
{#if servers !== null}
|
||||
<div class="">
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@
|
|||
{#if webConfig}
|
||||
<div class="">
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
@ -724,7 +724,7 @@
|
|||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class=" mb-2.5 text-base font-medium">{$i18n.t('Loader')}</div>
|
||||
<div class=" mt-0.5 mb-2.5 text-base font-medium">{$i18n.t('Loader')}</div>
|
||||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
|
|
|
|||
65
src/lib/components/common/CodeEditorModal.svelte
Normal file
65
src/lib/components/common/CodeEditorModal.svelte
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<script lang="ts">
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
import CodeEditor from './CodeEditor.svelte';
|
||||
import Drawer from './Drawer.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let {
|
||||
show = $bindable(),
|
||||
value = $bindable(),
|
||||
lang = 'python',
|
||||
onChange = () => {},
|
||||
onSave = () => {}
|
||||
} = $props();
|
||||
|
||||
let boilerplate = ``;
|
||||
|
||||
let codeEditor = $state(null);
|
||||
let _content = $state(value);
|
||||
|
||||
$effect(() => {
|
||||
if (_content) {
|
||||
value = _content;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<Drawer bind:show>
|
||||
<div class="flex h-full flex-col">
|
||||
<div
|
||||
class=" sticky top-0 z-30 flex justify-between bg-white px-4.5 pt-3 pb-3 dark:bg-gray-900 dark:text-gray-100"
|
||||
>
|
||||
<div class=" font-primary self-center text-lg font-medium">
|
||||
{$i18n.t('Code Editor')}
|
||||
</div>
|
||||
<button
|
||||
class="self-center"
|
||||
aria-label="Close"
|
||||
onclick={() => {
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="h-5 w-5"
|
||||
>
|
||||
<path
|
||||
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"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex h-full w-full flex-1 flex-col md:flex-row md:space-x-4 dark:text-gray-200 overflow-y-auto"
|
||||
>
|
||||
<div class=" flex h-full w-full flex-col sm:flex-row sm:justify-center sm:space-x-6">
|
||||
<CodeEditor bind:this={codeEditor} {value} {boilerplate} {lang} {onChange} {onSave} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
import { onDestroy, onMount } from 'svelte';
|
||||
import { flyAndScale } from '$lib/utils/transitions';
|
||||
import { fade, fly, slide } from 'svelte/transition';
|
||||
import { isApp } from '$lib/stores';
|
||||
|
||||
export let show = false;
|
||||
export let className = '';
|
||||
|
|
@ -54,19 +53,17 @@
|
|||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
|
||||
{#if show}
|
||||
<div
|
||||
bind:this={modalElement}
|
||||
class="modal fixed right-0 {$isApp
|
||||
? ' ml-[4.5rem] max-w-[calc(100%-4.5rem)]'
|
||||
: ''} left-0 bottom-0 bg-black/60 w-full h-screen max-h-[100dvh] flex justify-center z-999 overflow-hidden overscroll-contain"
|
||||
class="modal fixed right-0 bottom-0 left-0 z-999 flex h-screen max-h-[100dvh] w-full justify-center overflow-hidden overscroll-contain bg-black/60"
|
||||
in:fly={{ y: 100, duration: 100 }}
|
||||
on:mousedown={() => {
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class=" mt-auto w-full bg-gray-50 dark:bg-gray-900 dark:text-gray-100 {className} max-h-[100dvh] overflow-y-auto scrollbar-hidden"
|
||||
class=" mt-auto w-full bg-gray-50 dark:bg-gray-900 dark:text-gray-100 {className} scrollbar-hidden max-h-[100dvh] overflow-y-auto"
|
||||
on:mousedown={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
|
|
@ -74,6 +71,7 @@
|
|||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.modal-content {
|
||||
|
|
|
|||
Loading…
Reference in a new issue