mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-12 12:25:20 +00:00
refac
This commit is contained in:
parent
ca853ca465
commit
96b8aaf83f
2 changed files with 216 additions and 226 deletions
|
|
@ -1739,11 +1739,11 @@
|
||||||
<div
|
<div
|
||||||
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"
|
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"
|
||||||
>
|
>
|
||||||
<Component className="size-4.5" strokeWidth="1.75" />
|
<Component className="size-4.5" strokeWidth="1.5" />
|
||||||
</div>
|
</div>
|
||||||
</OptionsMenu>
|
</OptionsMenu>
|
||||||
|
|
||||||
<div class="ml-1 flex gap-1.5">
|
<div class="ml-0.5 flex gap-1.5">
|
||||||
{#if showToolsButton}
|
{#if showToolsButton}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={$i18n.t('{{COUNT}} Available Tools', {
|
content={$i18n.t('{{COUNT}} Available Tools', {
|
||||||
|
|
@ -1751,7 +1751,7 @@
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="translate-y-[0.5px] flex gap-1 items-center text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200 rounded-lg p-1 self-center transition"
|
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"
|
aria-label="Available Tools"
|
||||||
type="button"
|
type="button"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
|
|
@ -1760,7 +1760,7 @@
|
||||||
>
|
>
|
||||||
<Wrench className="size-4" strokeWidth="1.75" />
|
<Wrench className="size-4" strokeWidth="1.75" />
|
||||||
|
|
||||||
<span class="text-sm font-medium text-gray-600 dark:text-gray-300">
|
<span class="text-sm">
|
||||||
{toolServers.length + selectedToolIds.length}
|
{toolServers.length + selectedToolIds.length}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -1770,7 +1770,7 @@
|
||||||
{#each selectedFilterIds as filterId}
|
{#each selectedFilterIds as filterId}
|
||||||
{@const filter = toggleFilters.find((f) => f.id === filterId)}
|
{@const filter = toggleFilters.find((f) => f.id === filterId)}
|
||||||
{#if filter}
|
{#if filter}
|
||||||
<Tooltip content={filter?.description} placement="top">
|
<Tooltip content={filter?.name} placement="top">
|
||||||
<button
|
<button
|
||||||
on:click|preventDefault={() => {
|
on:click|preventDefault={() => {
|
||||||
selectedFilterIds = selectedFilterIds.filter(
|
selectedFilterIds = selectedFilterIds.filter(
|
||||||
|
|
@ -1778,7 +1778,7 @@
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
type="button"
|
type="button"
|
||||||
class="group px-2 @xl:px-2.5 py-2 flex gap-1.5 items-center text-sm rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden hover:bg-gray-50 dark:hover:bg-gray-800 {selectedFilterIds.includes(
|
class="group px-2 py-2 flex gap-1.5 items-center text-sm rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden hover:bg-gray-50 dark:hover:bg-gray-800 {selectedFilterIds.includes(
|
||||||
filterId
|
filterId
|
||||||
)
|
)
|
||||||
? 'text-sky-500 dark:text-sky-300 bg-sky-50 dark:bg-sky-200/5'
|
? 'text-sky-500 dark:text-sky-300 bg-sky-50 dark:bg-sky-200/5'
|
||||||
|
|
@ -1798,11 +1798,6 @@
|
||||||
{:else}
|
{:else}
|
||||||
<Sparkles className="size-4" strokeWidth="1.75" />
|
<Sparkles className="size-4" strokeWidth="1.75" />
|
||||||
{/if}
|
{/if}
|
||||||
<span
|
|
||||||
class="hidden @xl:block whitespace-nowrap text-ellipsis leading-none normal-case pr-0.5"
|
|
||||||
>{filter?.name}</span
|
|
||||||
>
|
|
||||||
|
|
||||||
<div class="hidden group-hover:block">
|
<div class="hidden group-hover:block">
|
||||||
<XMark className="size-4" strokeWidth="1.75" />
|
<XMark className="size-4" strokeWidth="1.75" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1812,21 +1807,16 @@
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
{#if webSearchEnabled}
|
{#if webSearchEnabled}
|
||||||
<Tooltip content={$i18n.t('Search the internet')} placement="top">
|
<Tooltip content={$i18n.t('Web Search')} placement="top">
|
||||||
<button
|
<button
|
||||||
on:click|preventDefault={() => (webSearchEnabled = !webSearchEnabled)}
|
on:click|preventDefault={() => (webSearchEnabled = !webSearchEnabled)}
|
||||||
type="button"
|
type="button"
|
||||||
class="group px-2 @xl:px-2.5 py-2 flex gap-1.5 items-center text-sm rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden hover:bg-gray-50 dark:hover:bg-gray-800 {webSearchEnabled ||
|
class="group px-2 py-2 flex gap-1.5 items-center text-sm rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden hover:bg-gray-50 dark:hover:bg-gray-800 {webSearchEnabled ||
|
||||||
($settings?.webSearch ?? false) === 'always'
|
($settings?.webSearch ?? false) === 'always'
|
||||||
? ' text-sky-500 dark:text-sky-300 bg-sky-50 dark:bg-sky-200/5'
|
? ' text-sky-500 dark:text-sky-300 bg-sky-50 dark:bg-sky-200/5'
|
||||||
: 'bg-transparent text-gray-600 dark:text-gray-300 '}"
|
: 'bg-transparent text-gray-600 dark:text-gray-300 '}"
|
||||||
>
|
>
|
||||||
<GlobeAlt className="size-4" strokeWidth="1.75" />
|
<GlobeAlt className="size-4" strokeWidth="1.75" />
|
||||||
<span
|
|
||||||
class="hidden @xl:block whitespace-nowrap text-ellipsis leading-none normal-case pr-0.5"
|
|
||||||
>{$i18n.t('Web Search')}</span
|
|
||||||
>
|
|
||||||
|
|
||||||
<div class="hidden group-hover:block">
|
<div class="hidden group-hover:block">
|
||||||
<XMark className="size-4" strokeWidth="1.75" />
|
<XMark className="size-4" strokeWidth="1.75" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1835,22 +1825,16 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if imageGenerationEnabled}
|
{#if imageGenerationEnabled}
|
||||||
<Tooltip content={$i18n.t('Generate an image')} placement="top">
|
<Tooltip content={$i18n.t('Image')} placement="top">
|
||||||
<button
|
<button
|
||||||
on:click|preventDefault={() =>
|
on:click|preventDefault={() =>
|
||||||
(imageGenerationEnabled = !imageGenerationEnabled)}
|
(imageGenerationEnabled = !imageGenerationEnabled)}
|
||||||
type="button"
|
type="button"
|
||||||
class="group px-2 @xl:px-2.5 py-2 flex gap-1.5 items-center text-sm rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden hover:bg-gray-50 dark:hover:bg-gray-800 {imageGenerationEnabled
|
class="group px-2 py-2 flex gap-1.5 items-center text-sm rounded-full transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden hover:bg-gray-50 dark:hover:bg-gray-800 {imageGenerationEnabled
|
||||||
? ' text-sky-500 dark:text-sky-300 bg-sky-50 dark:bg-sky-200/5'
|
? ' text-sky-500 dark:text-sky-300 bg-sky-50 dark:bg-sky-200/5'
|
||||||
: 'bg-transparent text-gray-600 dark:text-gray-300 '}"
|
: 'bg-transparent text-gray-600 dark:text-gray-300 '}"
|
||||||
>
|
>
|
||||||
<Photo className="size-4" strokeWidth="1.75" />
|
<Photo className="size-4" strokeWidth="1.75" />
|
||||||
|
|
||||||
<span
|
|
||||||
class="hidden @xl:block whitespace-nowrap text-ellipsis leading-none normal-case pr-0.5"
|
|
||||||
>{$i18n.t('Image')}</span
|
|
||||||
>
|
|
||||||
|
|
||||||
<div class="hidden group-hover:block">
|
<div class="hidden group-hover:block">
|
||||||
<XMark className="size-4" strokeWidth="1.75" />
|
<XMark className="size-4" strokeWidth="1.75" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1859,7 +1843,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if codeInterpreterEnabled}
|
{#if codeInterpreterEnabled}
|
||||||
<Tooltip content={$i18n.t('Execute code for analysis')} placement="top">
|
<Tooltip content={$i18n.t('Code Interpreter')} placement="top">
|
||||||
<button
|
<button
|
||||||
aria-label={codeInterpreterEnabled
|
aria-label={codeInterpreterEnabled
|
||||||
? $i18n.t('Disable Code Interpreter')
|
? $i18n.t('Disable Code Interpreter')
|
||||||
|
|
@ -1868,7 +1852,7 @@
|
||||||
on:click|preventDefault={() =>
|
on:click|preventDefault={() =>
|
||||||
(codeInterpreterEnabled = !codeInterpreterEnabled)}
|
(codeInterpreterEnabled = !codeInterpreterEnabled)}
|
||||||
type="button"
|
type="button"
|
||||||
class=" group px-2 @xl:px-2.5 py-2 flex gap-1.5 items-center text-sm transition-colors duration-300 max-w-full overflow-hidden hover:bg-gray-50 dark:hover:bg-gray-800 {codeInterpreterEnabled
|
class=" group px-2 py-2 flex gap-1.5 items-center text-sm transition-colors duration-300 max-w-full overflow-hidden hover:bg-gray-50 dark:hover:bg-gray-800 {codeInterpreterEnabled
|
||||||
? ' text-sky-500 dark:text-sky-300 bg-sky-50 dark:bg-sky-200/5'
|
? ' text-sky-500 dark:text-sky-300 bg-sky-50 dark:bg-sky-200/5'
|
||||||
: 'bg-transparent text-gray-600 dark:text-gray-300 '} {($settings?.highContrastMode ??
|
: 'bg-transparent text-gray-600 dark:text-gray-300 '} {($settings?.highContrastMode ??
|
||||||
false)
|
false)
|
||||||
|
|
@ -1876,10 +1860,6 @@
|
||||||
: 'focus:outline-hidden rounded-full'}"
|
: 'focus:outline-hidden rounded-full'}"
|
||||||
>
|
>
|
||||||
<Terminal className="size-3.5" strokeWidth="2" />
|
<Terminal className="size-3.5" strokeWidth="2" />
|
||||||
<span
|
|
||||||
class="hidden @xl:block whitespace-nowrap text-ellipsis leading-none normal-case pr-0.5"
|
|
||||||
>{$i18n.t('Code Interpreter')}</span
|
|
||||||
>
|
|
||||||
|
|
||||||
<div class="hidden group-hover:block">
|
<div class="hidden group-hover:block">
|
||||||
<XMark className="size-4" strokeWidth="1.75" />
|
<XMark className="size-4" strokeWidth="1.75" />
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
import GlobeAlt from '$lib/components/icons/GlobeAlt.svelte';
|
import GlobeAlt from '$lib/components/icons/GlobeAlt.svelte';
|
||||||
import Photo from '$lib/components/icons/Photo.svelte';
|
import Photo from '$lib/components/icons/Photo.svelte';
|
||||||
import Terminal from '$lib/components/icons/Terminal.svelte';
|
import Terminal from '$lib/components/icons/Terminal.svelte';
|
||||||
|
import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
|
||||||
|
import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
|
@ -78,52 +80,230 @@
|
||||||
<slot />
|
<slot />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
|
<!-- class="w-full max-w-[240px] rounded-2xl px-1 py-1 border border-gray-100 dark:border-gray-850 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg transition max-h-96 overflow-y-auto scrollbar-thin" -->
|
||||||
|
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
<DropdownMenu.Content
|
<DropdownMenu.Content
|
||||||
class="w-full max-w-[240px] rounded-2xl px-1 py-1 border border-gray-100 dark:border-gray-850 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg transition"
|
class="w-full max-w-[240px] rounded-2xl px-1 py-1 border border-gray-100 dark:border-gray-850 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg max-h-72 overflow-y-auto scrollbar-thin"
|
||||||
sideOffset={4}
|
sideOffset={4}
|
||||||
alignOffset={-6}
|
alignOffset={-6}
|
||||||
side="bottom"
|
side="bottom"
|
||||||
align="start"
|
align="start"
|
||||||
transition={flyAndScale}
|
transition={flyAndScale}
|
||||||
>
|
>
|
||||||
{#if toggleFilters && toggleFilters.length > 0}
|
{#if tools}
|
||||||
{#each toggleFilters.sort( (a, b) => a.name.localeCompare( b.name, undefined, { sensitivity: 'base' } ) ) as filter, filterIdx (filter.id)}
|
<button
|
||||||
<Tooltip content={filter?.description} placement="top">
|
class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800"
|
||||||
|
on:click={() => {
|
||||||
|
showAllTools = !showAllTools;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{#if !showAllTools}
|
||||||
|
<Wrench />
|
||||||
|
|
||||||
|
<div class="flex items-center w-full justify-between">
|
||||||
|
<div>
|
||||||
|
{$i18n.t('Tools')}
|
||||||
|
<span class="ml-0.5 text-gray-500">{Object.keys(tools).length}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-gray-500">
|
||||||
|
<ChevronRight />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<ChevronLeft />
|
||||||
|
|
||||||
|
<div class="flex items-center w-full justify-between">
|
||||||
|
<div>
|
||||||
|
{$i18n.t('Tools')}
|
||||||
|
<span class="ml-0.5 text-gray-500">{Object.keys(tools).length}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<div class="py-4">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if showAllTools}
|
||||||
|
{#each Object.keys(tools) as toolId}
|
||||||
|
<button
|
||||||
|
class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800"
|
||||||
|
on:click={() => {
|
||||||
|
tools[toolId].enabled = !tools[toolId].enabled;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="flex-1 truncate">
|
||||||
|
<Tooltip
|
||||||
|
content={tools[toolId]?.description ?? ''}
|
||||||
|
placement="top-start"
|
||||||
|
className="flex flex-1 gap-2 items-center"
|
||||||
|
>
|
||||||
|
<div class="shrink-0">
|
||||||
|
<Wrench />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" truncate">{tools[toolId].name}</div>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" shrink-0">
|
||||||
|
<Switch
|
||||||
|
state={tools[toolId].enabled}
|
||||||
|
on:change={async (e) => {
|
||||||
|
const state = e.detail;
|
||||||
|
await tick();
|
||||||
|
if (state) {
|
||||||
|
selectedToolIds = [...selectedToolIds, toolId];
|
||||||
|
} else {
|
||||||
|
selectedToolIds = selectedToolIds.filter((id) => id !== toolId);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
{:else}
|
||||||
|
{#if toggleFilters && toggleFilters.length > 0}
|
||||||
|
{#each toggleFilters.sort( (a, b) => a.name.localeCompare( b.name, undefined, { sensitivity: 'base' } ) ) as filter, filterIdx (filter.id)}
|
||||||
|
<Tooltip content={filter?.description} placement="top-start">
|
||||||
|
<button
|
||||||
|
class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800"
|
||||||
|
on:click={() => {
|
||||||
|
if (selectedFilterIds.includes(filter.id)) {
|
||||||
|
selectedFilterIds = selectedFilterIds.filter((id) => id !== filter.id);
|
||||||
|
} else {
|
||||||
|
selectedFilterIds = [...selectedFilterIds, filter.id];
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="flex-1 truncate">
|
||||||
|
<div class="flex flex-1 gap-2 items-center">
|
||||||
|
<div class="shrink-0">
|
||||||
|
{#if filter?.icon}
|
||||||
|
<div class="size-4 items-center flex justify-center">
|
||||||
|
<img
|
||||||
|
src={filter.icon}
|
||||||
|
class="size-3.5 {filter.icon.includes('svg')
|
||||||
|
? 'dark:invert-[80%]'
|
||||||
|
: ''}"
|
||||||
|
style="fill: currentColor;"
|
||||||
|
alt={filter.name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<Sparkles className="size-4" strokeWidth="1.75" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" truncate">{filter?.name}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" shrink-0">
|
||||||
|
<Switch
|
||||||
|
state={selectedFilterIds.includes(filter.id)}
|
||||||
|
on:change={async (e) => {
|
||||||
|
const state = e.detail;
|
||||||
|
await tick();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if showWebSearchButton}
|
||||||
|
<Tooltip content={$i18n.t('Search the internet')} placement="top-start">
|
||||||
<button
|
<button
|
||||||
class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800"
|
class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
if (selectedFilterIds.includes(filter.id)) {
|
webSearchEnabled = !webSearchEnabled;
|
||||||
selectedFilterIds = selectedFilterIds.filter((id) => id !== filter.id);
|
|
||||||
} else {
|
|
||||||
selectedFilterIds = [...selectedFilterIds, filter.id];
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex-1 truncate">
|
<div class="flex-1 truncate">
|
||||||
<div class="flex flex-1 gap-2 items-center">
|
<div class="flex flex-1 gap-2 items-center">
|
||||||
<div class="shrink-0">
|
<div class="shrink-0">
|
||||||
{#if filter?.icon}
|
<GlobeAlt />
|
||||||
<div class="size-4 items-center flex justify-center">
|
|
||||||
<img
|
|
||||||
src={filter.icon}
|
|
||||||
class="size-3.5 {filter.icon.includes('svg') ? 'dark:invert-[80%]' : ''}"
|
|
||||||
style="fill: currentColor;"
|
|
||||||
alt={filter.name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<Sparkles className="size-4" strokeWidth="1.75" />
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" truncate">{filter?.name}</div>
|
<div class=" truncate">{$i18n.t('Web Search')}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" shrink-0">
|
<div class=" shrink-0">
|
||||||
<Switch
|
<Switch
|
||||||
state={selectedFilterIds.includes(filter.id)}
|
state={webSearchEnabled}
|
||||||
|
on:change={async (e) => {
|
||||||
|
const state = e.detail;
|
||||||
|
await tick();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if showImageGenerationButton}
|
||||||
|
<Tooltip content={$i18n.t('Generate an image')} placement="top-start">
|
||||||
|
<button
|
||||||
|
class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800"
|
||||||
|
on:click={() => {
|
||||||
|
imageGenerationEnabled = !imageGenerationEnabled;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="flex-1 truncate">
|
||||||
|
<div class="flex flex-1 gap-2 items-center">
|
||||||
|
<div class="shrink-0">
|
||||||
|
<Photo className="size-4" strokeWidth="1.5" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" truncate">{$i18n.t('Image')}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" shrink-0">
|
||||||
|
<Switch
|
||||||
|
state={imageGenerationEnabled}
|
||||||
|
on:change={async (e) => {
|
||||||
|
const state = e.detail;
|
||||||
|
await tick();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if showCodeInterpreterButton}
|
||||||
|
<Tooltip content={$i18n.t('Execute code for analysis')} placement="top-start">
|
||||||
|
<button
|
||||||
|
class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800"
|
||||||
|
aria-pressed={codeInterpreterEnabled}
|
||||||
|
aria-label={codeInterpreterEnabled
|
||||||
|
? $i18n.t('Disable Code Interpreter')
|
||||||
|
: $i18n.t('Enable Code Interpreter')}
|
||||||
|
on:click={() => {
|
||||||
|
codeInterpreterEnabled = !codeInterpreterEnabled;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="flex-1 truncate">
|
||||||
|
<div class="flex flex-1 gap-2 items-center">
|
||||||
|
<div class="shrink-0">
|
||||||
|
<Terminal className="size-3.5" strokeWidth="1.75" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" truncate">{$i18n.t('Code Interpreter')}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" shrink-0">
|
||||||
|
<Switch
|
||||||
|
state={codeInterpreterEnabled}
|
||||||
on:change={async (e) => {
|
on:change={async (e) => {
|
||||||
const state = e.detail;
|
const state = e.detail;
|
||||||
await tick();
|
await tick();
|
||||||
|
|
@ -132,177 +312,7 @@
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if showWebSearchButton}
|
|
||||||
<Tooltip content={$i18n.t('Search the internet')} placement="top-start">
|
|
||||||
<button
|
|
||||||
class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800"
|
|
||||||
on:click={() => {
|
|
||||||
webSearchEnabled = !webSearchEnabled;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div class="flex-1 truncate">
|
|
||||||
<div class="flex flex-1 gap-2 items-center">
|
|
||||||
<div class="shrink-0">
|
|
||||||
<GlobeAlt />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class=" truncate">{$i18n.t('Web Search')}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class=" shrink-0">
|
|
||||||
<Switch
|
|
||||||
state={webSearchEnabled}
|
|
||||||
on:change={async (e) => {
|
|
||||||
const state = e.detail;
|
|
||||||
await tick();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if showImageGenerationButton}
|
|
||||||
<Tooltip content={$i18n.t('Generate an image')} placement="top-start">
|
|
||||||
<button
|
|
||||||
class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800"
|
|
||||||
on:click={() => {
|
|
||||||
imageGenerationEnabled = !imageGenerationEnabled;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div class="flex-1 truncate">
|
|
||||||
<div class="flex flex-1 gap-2 items-center">
|
|
||||||
<div class="shrink-0">
|
|
||||||
<Photo className="size-4" strokeWidth="1.5" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class=" truncate">{$i18n.t('Image')}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class=" shrink-0">
|
|
||||||
<Switch
|
|
||||||
state={imageGenerationEnabled}
|
|
||||||
on:change={async (e) => {
|
|
||||||
const state = e.detail;
|
|
||||||
await tick();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if showCodeInterpreterButton}
|
|
||||||
<Tooltip content={$i18n.t('Execute code for analysis')} placement="top-start">
|
|
||||||
<button
|
|
||||||
class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800"
|
|
||||||
aria-pressed={codeInterpreterEnabled}
|
|
||||||
aria-label={codeInterpreterEnabled
|
|
||||||
? $i18n.t('Disable Code Interpreter')
|
|
||||||
: $i18n.t('Enable Code Interpreter')}
|
|
||||||
on:click={() => {
|
|
||||||
codeInterpreterEnabled = !codeInterpreterEnabled;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div class="flex-1 truncate">
|
|
||||||
<div class="flex flex-1 gap-2 items-center">
|
|
||||||
<div class="shrink-0">
|
|
||||||
<Terminal className="size-3.5" strokeWidth="1.75" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class=" truncate">{$i18n.t('Code Interpreter')}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class=" shrink-0">
|
|
||||||
<Switch
|
|
||||||
state={codeInterpreterEnabled}
|
|
||||||
on:change={async (e) => {
|
|
||||||
const state = e.detail;
|
|
||||||
await tick();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if tools}
|
|
||||||
<hr class="my-1 border-gray-50 dark:border-gray-850" />
|
|
||||||
|
|
||||||
{#if Object.keys(tools).length > 0}
|
|
||||||
<div class="{showAllTools ? ' max-h-96' : 'max-h-28'} overflow-y-auto scrollbar-thin">
|
|
||||||
{#each Object.keys(tools) as toolId}
|
|
||||||
<button
|
|
||||||
class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800"
|
|
||||||
on:click={() => {
|
|
||||||
tools[toolId].enabled = !tools[toolId].enabled;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div class="flex-1 truncate">
|
|
||||||
<Tooltip
|
|
||||||
content={tools[toolId]?.description ?? ''}
|
|
||||||
placement="top-start"
|
|
||||||
className="flex flex-1 gap-2 items-center"
|
|
||||||
>
|
|
||||||
<div class="shrink-0">
|
|
||||||
<Wrench />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class=" truncate">{tools[toolId].name}</div>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class=" shrink-0">
|
|
||||||
<Switch
|
|
||||||
state={tools[toolId].enabled}
|
|
||||||
on:change={async (e) => {
|
|
||||||
const state = e.detail;
|
|
||||||
await tick();
|
|
||||||
if (state) {
|
|
||||||
selectedToolIds = [...selectedToolIds, toolId];
|
|
||||||
} else {
|
|
||||||
selectedToolIds = selectedToolIds.filter((id) => id !== toolId);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{#if Object.keys(tools).length > 3}
|
|
||||||
<button
|
|
||||||
class="flex w-full justify-center items-center text-sm font-medium cursor-pointer rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800"
|
|
||||||
on:click={() => {
|
|
||||||
showAllTools = !showAllTools;
|
|
||||||
}}
|
|
||||||
title={showAllTools ? $i18n.t('Show Less') : $i18n.t('Show All')}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="2.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="size-3 transition-transform duration-200 {showAllTools
|
|
||||||
? 'rotate-180'
|
|
||||||
: ''} text-gray-300 dark:text-gray-600"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
|
||||||
<div class="py-4">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue