mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-03 02:23:51 +00:00
fix: keep the composer's + and Integrations buttons visible at larger UI Scale on mobile (#30374)
On a phone with UI Scale at 1.2x or higher, the model selector in the chat composer covers the Integrations button, so it cannot be seen or tapped. From 1.3x it covers the + button too, which leaves no way to attach files or open integrations. The model selector's width cap is rem based, so it grows with UI Scale, and the left button group could shrink to nothing, so the selector took the space first. The + and Integrations buttons now sit in a group that keeps its width, and only the toggled chips stay in the scrolling strip. When space runs out the model name truncates, and chips scroll as before. Measured in the real app at a 360px mobile viewport: before, Integrations is covered at 1.2x and both buttons at 1.3x and 1.5x; after, both are tappable at every scale, with the model name truncated (114px at 1.3x). Button positions at 1x and on desktop are unchanged to the pixel, with and without chips. Fixes #29989
This commit is contained in:
parent
a046e07044
commit
e6e9fc9721
1 changed files with 251 additions and 253 deletions
|
|
@ -2223,7 +2223,7 @@
|
|||
</div>
|
||||
|
||||
<div class=" flex justify-between mt-0.5 mb-2 mx-0.5 max-w-full" dir="ltr">
|
||||
<div class="ml-1 self-end flex items-center flex-1 min-w-0">
|
||||
<div class="ml-1 self-end flex items-center">
|
||||
<InputMenu
|
||||
bind:files
|
||||
selectedModels={selectedModelIds}
|
||||
|
|
@ -2298,279 +2298,277 @@
|
|||
<div
|
||||
class="flex self-center w-[0.0625rem] h-4 mx-1 bg-gray-200/50 dark:bg-gray-800/50 shrink-0"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<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
|
||||
oauthRedirectHandler={(tool: {
|
||||
id: string;
|
||||
serverId: string;
|
||||
authType?: string | null;
|
||||
}) => oauthRedirectHandler(tool, chatInputDraft)}
|
||||
{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
|
||||
oauthRedirectHandler={(tool: {
|
||||
id: string;
|
||||
serverId: string;
|
||||
authType?: string | null;
|
||||
}) => oauthRedirectHandler(tool, chatInputDraft)}
|
||||
{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();
|
||||
}}
|
||||
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-[1.875rem] 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}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 items-center min-w-0 overflow-x-auto scrollbar-none">
|
||||
{#if selectedModelIds.length === 1 && $models.find((m) => m.id === selectedModelIds[0])?.has_user_valves}
|
||||
<div class="ml-1 flex gap-1.5 shrink-0">
|
||||
<Tooltip content={$i18n.t('Valves')} placement="top">
|
||||
<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-[1.875rem] flex justify-center items-center outline-hidden focus:outline-hidden shrink-0"
|
||||
aria-label={$i18n.t('Integrations')}
|
||||
id="model-valves-button"
|
||||
class="bg-transparent hover:bg-gray-100 text-gray-700 dark:text-white dark:hover:bg-gray-800 rounded-full size-[1.875rem] flex justify-center items-center outline-hidden focus:outline-hidden"
|
||||
on:click={() => {
|
||||
selectedValvesType = 'function';
|
||||
selectedValvesItemId = selectedModelIds[0]?.split('.')[0];
|
||||
showValvesModal = true;
|
||||
}}
|
||||
>
|
||||
<Component className="size-4.5" strokeWidth="1.5" />
|
||||
<Knobs className="size-4" strokeWidth="1.5" />
|
||||
</button>
|
||||
</IntegrationsMenu>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/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={$i18n.t('Available Tools')}
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showTools = !showTools;
|
||||
}}
|
||||
>
|
||||
<Wrench className="size-4" strokeWidth="1.75" />
|
||||
|
||||
<span class="text-sm">
|
||||
{(selectedToolIds ?? []).length}
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{#if selectedModelIds.length === 1 && $models.find((m) => m.id === selectedModelIds[0])?.has_user_valves}
|
||||
<div class="ml-1 flex gap-1.5 shrink-0">
|
||||
<Tooltip content={$i18n.t('Valves')} placement="top">
|
||||
<button
|
||||
type="button"
|
||||
id="model-valves-button"
|
||||
class="bg-transparent hover:bg-gray-100 text-gray-700 dark:text-white dark:hover:bg-gray-800 rounded-full size-[1.875rem] flex justify-center items-center outline-hidden focus:outline-hidden"
|
||||
on:click={() => {
|
||||
selectedValvesType = 'function';
|
||||
selectedValvesItemId = selectedModelIds[0]?.split('.')[0];
|
||||
showValvesModal = true;
|
||||
}}
|
||||
>
|
||||
<Knobs className="size-4" strokeWidth="1.5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{#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={$i18n.t('Available Skills')}
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showSkills = !showSkills;
|
||||
}}
|
||||
>
|
||||
<Cube className="size-4" strokeWidth="1.75" />
|
||||
|
||||
<span class="text-sm">
|
||||
{(selectedSkillIds ?? []).length}
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
<div class="ml-1 flex gap-1.5 shrink-0">
|
||||
{#if (selectedToolIds ?? []).length > 0}
|
||||
{#each selectedFilterIds as filterId (filterId)}
|
||||
{@const filter = toggleFilters.find((f) => f.id === filterId)}
|
||||
{#if filter}
|
||||
<Tooltip
|
||||
content={$i18n.t('{{COUNT}} Available Tools', {
|
||||
COUNT: (selectedToolIds ?? []).length
|
||||
})}
|
||||
content={resolveLocalizedFunction(
|
||||
filter,
|
||||
$localizedFunctions,
|
||||
$i18n.language
|
||||
)}
|
||||
placement="top"
|
||||
>
|
||||
<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={$i18n.t('Available Tools')}
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showTools = !showTools;
|
||||
}}
|
||||
>
|
||||
<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={$i18n.t('Available Skills')}
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showSkills = !showSkills;
|
||||
}}
|
||||
>
|
||||
<Cube 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={resolveLocalizedFunction(
|
||||
filter,
|
||||
$localizedFunctions,
|
||||
$i18n.language
|
||||
)}
|
||||
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-[0.375rem] 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={resolveLocalizedFunction(
|
||||
filter,
|
||||
$localizedFunctions,
|
||||
$i18n.language
|
||||
)}
|
||||
/>
|
||||
</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 && showWebSearchButton}
|
||||
<Tooltip content={$i18n.t('Web Search')} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() => (webSearchEnabled = !webSearchEnabled)}
|
||||
type="button"
|
||||
class="group p-[0.375rem] 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 && showImageGenerationButton}
|
||||
<Tooltip content={$i18n.t('Image')} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() =>
|
||||
(imageGenerationEnabled = !imageGenerationEnabled)}
|
||||
type="button"
|
||||
class="group p-[0.375rem] 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 && showCodeInterpreterButton}
|
||||
<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-[0.375rem] 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}
|
||||
|
||||
{#each pendingOAuthTools as pendingTool (pendingTool.id)}
|
||||
<Tooltip content={$i18n.t('Click to connect')} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() => {
|
||||
oauthRedirectHandler(pendingTool, chatInputDraft);
|
||||
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 px-2 py-[0.3125rem] 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"
|
||||
class="group p-[0.375rem] 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"
|
||||
>
|
||||
<Wrench className="size-3.5" strokeWidth="1.75" />
|
||||
<span class="truncate">{pendingTool.name}</span>
|
||||
{#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={resolveLocalizedFunction(
|
||||
filter,
|
||||
$localizedFunctions,
|
||||
$i18n.language
|
||||
)}
|
||||
/>
|
||||
</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>
|
||||
{/each}
|
||||
|
||||
<!-- Terminal Server Selector -->
|
||||
{#if showTerminalSelector}
|
||||
<TerminalMenu
|
||||
bind:show={showTerminalMenu}
|
||||
disabled={generating ||
|
||||
(!!history?.currentId &&
|
||||
history.messages[history.currentId]?.done != true)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{#if webSearchEnabled && showWebSearchButton}
|
||||
<Tooltip content={$i18n.t('Web Search')} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() => (webSearchEnabled = !webSearchEnabled)}
|
||||
type="button"
|
||||
class="group p-[0.375rem] 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 && showImageGenerationButton}
|
||||
<Tooltip content={$i18n.t('Image')} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() =>
|
||||
(imageGenerationEnabled = !imageGenerationEnabled)}
|
||||
type="button"
|
||||
class="group p-[0.375rem] 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 && showCodeInterpreterButton}
|
||||
<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-[0.375rem] 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}
|
||||
|
||||
{#each pendingOAuthTools as pendingTool (pendingTool.id)}
|
||||
<Tooltip content={$i18n.t('Click to connect')} placement="top">
|
||||
<button
|
||||
on:click|preventDefault={() => {
|
||||
oauthRedirectHandler(pendingTool, chatInputDraft);
|
||||
}}
|
||||
type="button"
|
||||
class="group px-2 py-[0.3125rem] 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}
|
||||
|
||||
<!-- Terminal Server Selector -->
|
||||
{#if showTerminalSelector}
|
||||
<TerminalMenu
|
||||
bind:show={showTerminalMenu}
|
||||
disabled={generating ||
|
||||
(!!history?.currentId &&
|
||||
history.messages[history.currentId]?.done != true)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue