mirror of
https://github.com/open-webui/open-webui.git
synced 2026-07-25 01:36:29 -05:00
refac
This commit is contained in:
@@ -2977,12 +2977,13 @@
|
||||
const loaded = chat?.chat ?? {};
|
||||
if (equal(params, loaded.params ?? {}) && equal(chatFiles, loaded.files ?? [])) return;
|
||||
|
||||
const res = await updateChatById(localStorage.token, $chatId, { params, files: chatFiles }).catch(
|
||||
(err) => {
|
||||
console.error('[controls autosave]', err);
|
||||
return null;
|
||||
}
|
||||
);
|
||||
const res = await updateChatById(localStorage.token, $chatId, {
|
||||
params,
|
||||
files: chatFiles
|
||||
}).catch((err) => {
|
||||
console.error('[controls autosave]', err);
|
||||
return null;
|
||||
});
|
||||
// Refresh the dedupe baseline so a later revert still saves.
|
||||
if (res) chat = res;
|
||||
};
|
||||
|
||||
@@ -1755,43 +1755,43 @@
|
||||
|
||||
<div class="flex flex-1 items-center min-w-0 overflow-x-auto scrollbar-none">
|
||||
{#if showWebSearchButton || showImageGenerationButton || showCodeInterpreterButton || showToolsButton || showSkillsButton || (toggleFilters && toggleFilters.length > 0)}
|
||||
<IntegrationsMenu
|
||||
selectedModels={selectedModelIds}
|
||||
{toggleFilters}
|
||||
{showWebSearchButton}
|
||||
{showImageGenerationButton}
|
||||
{showCodeInterpreterButton}
|
||||
bind:selectedToolIds
|
||||
bind:selectedSkillIds
|
||||
bind:selectedFilterIds
|
||||
bind:webSearchEnabled
|
||||
bind:imageGenerationEnabled
|
||||
bind:codeInterpreterEnabled
|
||||
{onWebSearchToggle}
|
||||
closeOnOutsideClick={integrationsMenuCloseOnOutsideClick}
|
||||
onShowValves={(e) => {
|
||||
const { type, id } = e;
|
||||
selectedValvesType = type;
|
||||
selectedValvesItemId = id;
|
||||
showValvesModal = true;
|
||||
integrationsMenuCloseOnOutsideClick = false;
|
||||
}}
|
||||
onClose={async () => {
|
||||
await tick();
|
||||
<IntegrationsMenu
|
||||
selectedModels={selectedModelIds}
|
||||
{toggleFilters}
|
||||
{showWebSearchButton}
|
||||
{showImageGenerationButton}
|
||||
{showCodeInterpreterButton}
|
||||
bind:selectedToolIds
|
||||
bind:selectedSkillIds
|
||||
bind:selectedFilterIds
|
||||
bind:webSearchEnabled
|
||||
bind:imageGenerationEnabled
|
||||
bind:codeInterpreterEnabled
|
||||
{onWebSearchToggle}
|
||||
closeOnOutsideClick={integrationsMenuCloseOnOutsideClick}
|
||||
onShowValves={(e) => {
|
||||
const { type, id } = e;
|
||||
selectedValvesType = type;
|
||||
selectedValvesItemId = id;
|
||||
showValvesModal = true;
|
||||
integrationsMenuCloseOnOutsideClick = false;
|
||||
}}
|
||||
onClose={async () => {
|
||||
await tick();
|
||||
|
||||
const chatInput = document.getElementById('chat-input');
|
||||
chatInput?.focus();
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
id="integration-menu-button"
|
||||
class="bg-transparent hover:bg-gray-100 text-gray-700 dark:text-white dark:hover:bg-gray-800 rounded-full size-8 flex justify-center items-center outline-hidden focus:outline-hidden shrink-0"
|
||||
aria-label={$i18n.t('Integrations')}
|
||||
const chatInput = document.getElementById('chat-input');
|
||||
chatInput?.focus();
|
||||
}}
|
||||
>
|
||||
<Component className="size-4.5" strokeWidth="1.5" />
|
||||
</button>
|
||||
</IntegrationsMenu>
|
||||
<button
|
||||
type="button"
|
||||
id="integration-menu-button"
|
||||
class="bg-transparent hover:bg-gray-100 text-gray-700 dark:text-white dark:hover:bg-gray-800 rounded-full size-8 flex justify-center items-center outline-hidden focus:outline-hidden shrink-0"
|
||||
aria-label={$i18n.t('Integrations')}
|
||||
>
|
||||
<Component className="size-4.5" strokeWidth="1.5" />
|
||||
</button>
|
||||
</IntegrationsMenu>
|
||||
{/if}
|
||||
|
||||
{#if selectedModelIds.length === 1 && $models.find((m) => m.id === selectedModelIds[0])?.has_user_valves}
|
||||
@@ -1814,190 +1814,190 @@
|
||||
{/if}
|
||||
|
||||
<div class="ml-1 flex gap-1.5 shrink-0">
|
||||
{#if (selectedToolIds ?? []).length > 0}
|
||||
<Tooltip
|
||||
content={$i18n.t('{{COUNT}} Available Tools', {
|
||||
COUNT: (selectedToolIds ?? []).length
|
||||
})}
|
||||
>
|
||||
<button
|
||||
class="translate-y-[0.5px] px-1 flex gap-1 items-center text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200 rounded-lg self-center transition"
|
||||
aria-label="Available Tools"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showTools = !showTools;
|
||||
}}
|
||||
{#if (selectedToolIds ?? []).length > 0}
|
||||
<Tooltip
|
||||
content={$i18n.t('{{COUNT}} Available Tools', {
|
||||
COUNT: (selectedToolIds ?? []).length
|
||||
})}
|
||||
>
|
||||
<Wrench className="size-4" strokeWidth="1.75" />
|
||||
|
||||
<span class="text-sm">
|
||||
{(selectedToolIds ?? []).length}
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{#if (selectedSkillIds ?? []).length > 0}
|
||||
<Tooltip
|
||||
content={$i18n.t('{{COUNT}} Available Skills', {
|
||||
COUNT: (selectedSkillIds ?? []).length
|
||||
})}
|
||||
>
|
||||
<button
|
||||
class="translate-y-[0.5px] px-1 flex gap-1 items-center text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200 rounded-lg self-center transition"
|
||||
aria-label="Available Skills"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showSkills = !showSkills;
|
||||
}}
|
||||
>
|
||||
<Keyframes className="size-4" strokeWidth="1.75" />
|
||||
|
||||
<span class="text-sm">
|
||||
{(selectedSkillIds ?? []).length}
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{#each selectedFilterIds as filterId (filterId)}
|
||||
{@const filter = toggleFilters.find((f) => f.id === filterId)}
|
||||
{#if filter}
|
||||
<Tooltip content={filter?.name} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() => {
|
||||
if (
|
||||
filter?.has_user_valves &&
|
||||
($_user?.role === 'admin' ||
|
||||
($_user?.permissions?.chat?.valves ?? true))
|
||||
) {
|
||||
selectedValvesType = 'function';
|
||||
selectedValvesItemId = filterId;
|
||||
showValvesModal = true;
|
||||
} else {
|
||||
selectedFilterIds = selectedFilterIds.filter(
|
||||
(id) => id !== filterId
|
||||
);
|
||||
}
|
||||
}}
|
||||
class="translate-y-[0.5px] px-1 flex gap-1 items-center text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200 rounded-lg self-center transition"
|
||||
aria-label="Available Tools"
|
||||
type="button"
|
||||
class="group p-[7px] flex gap-1.5 items-center text-sm rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden {selectedFilterIds.includes(
|
||||
filterId
|
||||
)
|
||||
? 'text-sky-500 dark:text-sky-300 bg-sky-50 hover:bg-sky-100 dark:bg-sky-400/10 dark:hover:bg-sky-600/10 border border-sky-200/40 dark:border-sky-500/20'
|
||||
: 'bg-transparent text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 '} capitalize"
|
||||
on:click={() => {
|
||||
showTools = !showTools;
|
||||
}}
|
||||
>
|
||||
{#if filter?.icon}
|
||||
<div class="size-4 items-center flex justify-center">
|
||||
<img
|
||||
src={filter.icon}
|
||||
class="size-3.5 {filter.icon.includes('data:image/svg')
|
||||
? 'dark:invert-[80%]'
|
||||
: ''}"
|
||||
style="fill: currentColor;"
|
||||
alt={filter.name}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<Sparkles className="size-4" strokeWidth="1.75" />
|
||||
{/if}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="hidden group-hover:block"
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
selectedFilterIds = selectedFilterIds.filter(
|
||||
(id) => id !== filterId
|
||||
);
|
||||
<Wrench className="size-4" strokeWidth="1.75" />
|
||||
|
||||
<span class="text-sm">
|
||||
{(selectedToolIds ?? []).length}
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{#if (selectedSkillIds ?? []).length > 0}
|
||||
<Tooltip
|
||||
content={$i18n.t('{{COUNT}} Available Skills', {
|
||||
COUNT: (selectedSkillIds ?? []).length
|
||||
})}
|
||||
>
|
||||
<button
|
||||
class="translate-y-[0.5px] px-1 flex gap-1 items-center text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200 rounded-lg self-center transition"
|
||||
aria-label="Available Skills"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showSkills = !showSkills;
|
||||
}}
|
||||
>
|
||||
<Keyframes className="size-4" strokeWidth="1.75" />
|
||||
|
||||
<span class="text-sm">
|
||||
{(selectedSkillIds ?? []).length}
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{#each selectedFilterIds as filterId (filterId)}
|
||||
{@const filter = toggleFilters.find((f) => f.id === filterId)}
|
||||
{#if filter}
|
||||
<Tooltip content={filter?.name} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() => {
|
||||
if (
|
||||
filter?.has_user_valves &&
|
||||
($_user?.role === 'admin' ||
|
||||
($_user?.permissions?.chat?.valves ?? true))
|
||||
) {
|
||||
selectedValvesType = 'function';
|
||||
selectedValvesItemId = filterId;
|
||||
showValvesModal = true;
|
||||
} else {
|
||||
selectedFilterIds = selectedFilterIds.filter(
|
||||
(id) => id !== filterId
|
||||
);
|
||||
}
|
||||
}}
|
||||
type="button"
|
||||
class="group p-[7px] flex gap-1.5 items-center text-sm rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden {selectedFilterIds.includes(
|
||||
filterId
|
||||
)
|
||||
? 'text-sky-500 dark:text-sky-300 bg-sky-50 hover:bg-sky-100 dark:bg-sky-400/10 dark:hover:bg-sky-600/10 border border-sky-200/40 dark:border-sky-500/20'
|
||||
: 'bg-transparent text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 '} capitalize"
|
||||
>
|
||||
{#if filter?.icon}
|
||||
<div class="size-4 items-center flex justify-center">
|
||||
<img
|
||||
src={filter.icon}
|
||||
class="size-3.5 {filter.icon.includes('data:image/svg')
|
||||
? 'dark:invert-[80%]'
|
||||
: ''}"
|
||||
style="fill: currentColor;"
|
||||
alt={filter.name}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<Sparkles className="size-4" strokeWidth="1.75" />
|
||||
{/if}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="hidden group-hover:block"
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
selectedFilterIds = selectedFilterIds.filter(
|
||||
(id) => id !== filterId
|
||||
);
|
||||
}}
|
||||
>
|
||||
<XMark className="size-4" strokeWidth="1.75" />
|
||||
</div>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
{#if webSearchEnabled}
|
||||
<Tooltip content={$i18n.t('Web Search')} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() => (webSearchEnabled = !webSearchEnabled)}
|
||||
type="button"
|
||||
class="group p-[7px] flex gap-1.5 items-center text-sm rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden {webSearchEnabled ||
|
||||
($settings?.webSearch ?? false) === 'always'
|
||||
? ' text-sky-500 dark:text-sky-300 bg-sky-50 hover:bg-sky-100 dark:bg-sky-400/10 dark:hover:bg-sky-600/10 border border-sky-200/40 dark:border-sky-500/20'
|
||||
: 'bg-transparent text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 '}"
|
||||
>
|
||||
<GlobeAlt className="size-4" strokeWidth="1.75" />
|
||||
<div class="hidden group-hover:block">
|
||||
<XMark className="size-4" strokeWidth="1.75" />
|
||||
</div>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
{#if webSearchEnabled}
|
||||
<Tooltip content={$i18n.t('Web Search')} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() => (webSearchEnabled = !webSearchEnabled)}
|
||||
type="button"
|
||||
class="group p-[7px] flex gap-1.5 items-center text-sm rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden {webSearchEnabled ||
|
||||
($settings?.webSearch ?? false) === 'always'
|
||||
? ' text-sky-500 dark:text-sky-300 bg-sky-50 hover:bg-sky-100 dark:bg-sky-400/10 dark:hover:bg-sky-600/10 border border-sky-200/40 dark:border-sky-500/20'
|
||||
: 'bg-transparent text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 '}"
|
||||
>
|
||||
<GlobeAlt className="size-4" strokeWidth="1.75" />
|
||||
<div class="hidden group-hover:block">
|
||||
<XMark className="size-4" strokeWidth="1.75" />
|
||||
</div>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
{#if imageGenerationEnabled}
|
||||
<Tooltip content={$i18n.t('Image')} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() =>
|
||||
(imageGenerationEnabled = !imageGenerationEnabled)}
|
||||
type="button"
|
||||
class="group p-[7px] flex gap-1.5 items-center text-sm rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden {imageGenerationEnabled
|
||||
? ' text-sky-500 dark:text-sky-300 bg-sky-50 hover:bg-sky-100 dark:bg-sky-400/10 dark:hover:bg-sky-700/10 border border-sky-200/40 dark:border-sky-500/20'
|
||||
: 'bg-transparent text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 '}"
|
||||
>
|
||||
<Photo className="size-4" strokeWidth="1.75" />
|
||||
<div class="hidden group-hover:block">
|
||||
<XMark className="size-4" strokeWidth="1.75" />
|
||||
</div>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{#if imageGenerationEnabled}
|
||||
<Tooltip content={$i18n.t('Image')} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() =>
|
||||
(imageGenerationEnabled = !imageGenerationEnabled)}
|
||||
type="button"
|
||||
class="group p-[7px] flex gap-1.5 items-center text-sm rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden {imageGenerationEnabled
|
||||
? ' text-sky-500 dark:text-sky-300 bg-sky-50 hover:bg-sky-100 dark:bg-sky-400/10 dark:hover:bg-sky-700/10 border border-sky-200/40 dark:border-sky-500/20'
|
||||
: 'bg-transparent text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 '}"
|
||||
>
|
||||
<Photo className="size-4" strokeWidth="1.75" />
|
||||
<div class="hidden group-hover:block">
|
||||
<XMark className="size-4" strokeWidth="1.75" />
|
||||
</div>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
{#if codeInterpreterEnabled}
|
||||
<Tooltip content={$i18n.t('Code Interpreter')} placement="top">
|
||||
<button
|
||||
aria-label={codeInterpreterEnabled
|
||||
? $i18n.t('Disable Code Interpreter')
|
||||
: $i18n.t('Enable Code Interpreter')}
|
||||
aria-pressed={codeInterpreterEnabled}
|
||||
on:click|preventDefault={() =>
|
||||
(codeInterpreterEnabled = !codeInterpreterEnabled)}
|
||||
type="button"
|
||||
class=" group p-[7px] flex gap-1.5 items-center text-sm transition-colors duration-300 max-w-full overflow-hidden {codeInterpreterEnabled
|
||||
? ' text-sky-500 dark:text-sky-300 bg-sky-50 hover:bg-sky-100 dark:bg-sky-400/10 dark:hover:bg-sky-700/10 border border-sky-200/40 dark:border-sky-500/20'
|
||||
: 'bg-transparent text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 '} {($settings?.highContrastMode ??
|
||||
false)
|
||||
? 'm-1'
|
||||
: 'focus:outline-hidden rounded-full'}"
|
||||
>
|
||||
<Terminal className="size-3.5" strokeWidth="2" />
|
||||
|
||||
{#if codeInterpreterEnabled}
|
||||
<Tooltip content={$i18n.t('Code Interpreter')} placement="top">
|
||||
<button
|
||||
aria-label={codeInterpreterEnabled
|
||||
? $i18n.t('Disable Code Interpreter')
|
||||
: $i18n.t('Enable Code Interpreter')}
|
||||
aria-pressed={codeInterpreterEnabled}
|
||||
on:click|preventDefault={() =>
|
||||
(codeInterpreterEnabled = !codeInterpreterEnabled)}
|
||||
type="button"
|
||||
class=" group p-[7px] flex gap-1.5 items-center text-sm transition-colors duration-300 max-w-full overflow-hidden {codeInterpreterEnabled
|
||||
? ' text-sky-500 dark:text-sky-300 bg-sky-50 hover:bg-sky-100 dark:bg-sky-400/10 dark:hover:bg-sky-700/10 border border-sky-200/40 dark:border-sky-500/20'
|
||||
: 'bg-transparent text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 '} {($settings?.highContrastMode ??
|
||||
false)
|
||||
? 'm-1'
|
||||
: 'focus:outline-hidden rounded-full'}"
|
||||
>
|
||||
<Terminal className="size-3.5" strokeWidth="2" />
|
||||
<div class="hidden group-hover:block">
|
||||
<XMark className="size-4" strokeWidth="1.75" />
|
||||
</div>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
<div class="hidden group-hover:block">
|
||||
<XMark className="size-4" strokeWidth="1.75" />
|
||||
</div>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{#each pendingOAuthTools as pendingTool (pendingTool.id)}
|
||||
<Tooltip content={$i18n.t('Click to connect')} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() => {
|
||||
initiateOAuthRedirect(pendingTool);
|
||||
}}
|
||||
type="button"
|
||||
class="group px-2 py-[5px] flex gap-1.5 items-center text-xs rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden
|
||||
{#each pendingOAuthTools as pendingTool (pendingTool.id)}
|
||||
<Tooltip content={$i18n.t('Click to connect')} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() => {
|
||||
initiateOAuthRedirect(pendingTool);
|
||||
}}
|
||||
type="button"
|
||||
class="group px-2 py-[5px] flex gap-1.5 items-center text-xs rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden
|
||||
text-amber-600 dark:text-amber-400 bg-amber-50 hover:bg-amber-100 dark:bg-amber-400/10 dark:hover:bg-amber-600/10 border border-amber-200/40 dark:border-amber-500/20"
|
||||
>
|
||||
<Wrench className="size-3.5" strokeWidth="1.75" />
|
||||
<span class="truncate">{pendingTool.name}</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/each}
|
||||
</div>
|
||||
>
|
||||
<Wrench className="size-3.5" strokeWidth="1.75" />
|
||||
<span class="truncate">{pendingTool.name}</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -254,8 +254,6 @@
|
||||
return allModels;
|
||||
};
|
||||
|
||||
|
||||
|
||||
const enableAllHandler = async () => {
|
||||
const allModels = await fetchAllWorkspaceModels();
|
||||
const modelsToEnable = allModels.filter((m) => !(m.is_active ?? true));
|
||||
|
||||
Reference in New Issue
Block a user