This commit is contained in:
Timothy Jaeryang Baek
2026-08-24 18:14:30 -04:00
parent 6cb2449ab7
commit 7a533d0d5b
2 changed files with 158 additions and 130 deletions
+16 -8
View File
@@ -773,6 +773,14 @@
'terminal',
modelCapabilitiesById
);
$: hasDirectToolServerAccess =
$_user?.role === 'admin' || ($_user?.permissions?.features?.direct_tool_servers ?? true);
$: showTerminalSelector =
terminalCapableModels.length > 0 &&
(($terminalServers ?? []).some((t) => t.id) ||
(hasDirectToolServerAccess &&
(($terminalServers ?? []).some((t) => !t.id) ||
($settings?.terminalServers ?? []).some((s) => s.url))));
let toggleFilters = [];
$: toggleFilters = (atSelectedModel?.id ? [atSelectedModel.id] : selectedModels)
@@ -2482,14 +2490,14 @@
</Tooltip>
{/each}
{#if !history?.currentId || history.messages[history.currentId]?.done == true}
<!-- Terminal Server Selector -->
{@const hasDirectToolServerAccess =
$_user?.role === 'admin' ||
($_user?.permissions?.features?.direct_tool_servers ?? true)}
{#if terminalCapableModels.length > 0 && (($terminalServers ?? []).some((t) => t.id) || (hasDirectToolServerAccess && (($terminalServers ?? []).some((t) => !t.id) || ($settings?.terminalServers ?? []).some((s) => s.url))))}
<TerminalMenu bind:show={showTerminalMenu} />
{/if}
<!-- Terminal Server Selector -->
{#if showTerminalSelector}
<TerminalMenu
bind:show={showTerminalMenu}
disabled={generating ||
(!!history?.currentId &&
history.messages[history.currentId]?.done != true)}
/>
{/if}
</div>
</div>
@@ -21,6 +21,7 @@
const i18n = getContext('i18n');
export let show = false;
export let disabled = false;
const chatContext = (terminal: any) => terminal?.contexts?.chat ?? {};
@@ -117,106 +118,56 @@
selectedDirectTerminal?.name ||
selectedDirectTerminal?.url?.replace(/^https?:\/\//, '') ||
$i18n.t('Terminal');
$: if (disabled && show) show = false;
$: triggerClass = `flex items-center gap-1.5 translate-y-[1px] text-[0.8125rem] transition rounded-lg ${
disabled
? 'p-1 cursor-not-allowed opacity-40 text-gray-400 dark:text-gray-600'
: `text-gray-600 hover:bg-gray-50/40 hover:text-gray-700 dark:text-gray-300 dark:hover:bg-gray-800/40 dark:hover:text-gray-200 cursor-pointer ${
$selectedTerminalId && selectedLabel ? ' p-1 ' : ' p-1 opacity-50'
}`
}`;
</script>
<div class="flex items-center translate-x-0.5">
<Dropdown bind:show align="start">
{#if disabled}
<Tooltip content={$i18n.t('Terminal')} placement="top">
<button
type="button"
class="flex items-center gap-1.5 translate-y-[1px] text-[0.8125rem] text-gray-600 hover:bg-gray-50/40 hover:text-gray-700 dark:text-gray-300 dark:hover:bg-gray-800/40 dark:hover:text-gray-200 transition rounded-lg cursor-pointer {$selectedTerminalId &&
selectedLabel
? ' p-1 '
: ' p-1 opacity-50'}"
>
<button type="button" disabled aria-disabled="true" class={triggerClass}>
<Cloud className="size-3.5" strokeWidth="2" />
{#if $selectedTerminalId && selectedLabel}
<span class="truncate text-[0.8125rem] max-w-[6.25rem] sm:max-w-[9.375rem]">{selectedLabel}</span>
<span class="truncate text-[0.8125rem] max-w-[6.25rem] sm:max-w-[9.375rem]"
>{selectedLabel}</span
>
{/if}
</button>
</Tooltip>
{:else}
<Dropdown bind:show align="start">
<Tooltip content={$i18n.t('Terminal')} placement="top">
<button type="button" class={triggerClass}>
<Cloud className="size-3.5" strokeWidth="2" />
<div slot="content">
<DropdownMenu
className="min-w-56 max-w-56 max-h-72 overflow-y-auto overflow-x-hidden scrollbar-thin"
>
<!-- Direct terminals (gated by permission) -->
{#if directTerminals.length > 0 && ($user?.role === 'admin' || ($user?.permissions?.features?.direct_tool_servers ?? true))}
<div class="flex items-center justify-between px-3 py-1">
<span
class="text-[0.625rem] font-normal text-gray-400 dark:text-gray-500 uppercase tracking-wider"
{#if $selectedTerminalId && selectedLabel}
<span class="truncate text-[0.8125rem] max-w-[6.25rem] sm:max-w-[9.375rem]"
>{selectedLabel}</span
>
{$i18n.t('Direct')}
</span>
<Tooltip content={$i18n.t('Add Terminal')} placement="top">
<button
type="button"
aria-label={$i18n.t('Add Terminal')}
class="p-0.5 rounded-md text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition"
on:click|stopPropagation={() => {
show = false;
showSettings.set('tools');
}}
{/if}
</button>
</Tooltip>
<div slot="content">
<DropdownMenu
className="min-w-56 max-w-56 max-h-72 overflow-y-auto overflow-x-hidden scrollbar-thin"
>
<!-- Direct terminals (gated by permission) -->
{#if directTerminals.length > 0 && ($user?.role === 'admin' || ($user?.permissions?.features?.direct_tool_servers ?? true))}
<div class="flex items-center justify-between px-3 py-1">
<span
class="text-[0.625rem] font-normal text-gray-400 dark:text-gray-500 uppercase tracking-wider"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="size-3.5"
>
<path
d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
/>
</svg>
</button>
</Tooltip>
</div>
{#each directTerminals as terminal}
<button
type="button"
class="flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl {$selectedTerminalId ===
terminal.url
? 'bg-gray-50/40 dark:bg-gray-800/40'
: 'hover:bg-gray-50/40 dark:hover:bg-gray-800/40'}"
on:click={() => selectDirect(terminal)}
>
<div class="flex flex-1 gap-2 items-center truncate">
<Cloud className="size-3.5 shrink-0" strokeWidth="2" />
<span class="truncate"
>{terminal.name || terminal.url.replace(/^https?:\/\//, '')}</span
>
</div>
{#if $selectedTerminalId === terminal.url}
<div class="shrink-0 text-emerald-600 dark:text-emerald-400">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="size-3.5"
>
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd"
/>
</svg>
</div>
{/if}
</button>
{/each}
{/if}
<!-- System terminals -->
{#if systemTerminals.length > 0}
<div class="flex items-center justify-between px-3 py-1">
<span
class="text-[0.625rem] font-normal text-gray-400 dark:text-gray-500 uppercase tracking-wider"
>
{$i18n.t('System')}
</span>
{#if $user?.role === 'admin'}
{$i18n.t('Direct')}
</span>
<Tooltip content={$i18n.t('Add Terminal')} placement="top">
<button
type="button"
@@ -224,7 +175,7 @@
class="p-0.5 rounded-md text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition"
on:click|stopPropagation={() => {
show = false;
showSettings.set('admin:integrations');
showSettings.set('tools');
}}
>
<svg
@@ -239,42 +190,111 @@
</svg>
</button>
</Tooltip>
{/if}
</div>
</div>
{#each systemTerminals as terminal}
<button
type="button"
class="flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl {$selectedTerminalId ===
terminal.id
? 'bg-gray-50/40 dark:bg-gray-800/40'
: 'hover:bg-gray-50/40 dark:hover:bg-gray-800/40'}"
on:click={() => selectSystem(terminal)}
>
<div class="flex flex-1 gap-2 items-center truncate">
<Cloud className="size-3.5 shrink-0" strokeWidth="2" />
<span class="truncate">{terminal.name || $i18n.t('Terminal')}</span>
</div>
{#if $selectedTerminalId === terminal.id}
<div class="shrink-0 text-emerald-600 dark:text-emerald-400">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="size-3.5"
{#each directTerminals as terminal}
<button
type="button"
class="flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl {$selectedTerminalId ===
terminal.url
? 'bg-gray-50/40 dark:bg-gray-800/40'
: 'hover:bg-gray-50/40 dark:hover:bg-gray-800/40'}"
on:click={() => selectDirect(terminal)}
>
<div class="flex flex-1 gap-2 items-center truncate">
<Cloud className="size-3.5 shrink-0" strokeWidth="2" />
<span class="truncate"
>{terminal.name || terminal.url.replace(/^https?:\/\//, '')}</span
>
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd"
/>
</svg>
</div>
{#if $selectedTerminalId === terminal.url}
<div class="shrink-0 text-emerald-600 dark:text-emerald-400">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="size-3.5"
>
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd"
/>
</svg>
</div>
{/if}
</button>
{/each}
{/if}
<!-- System terminals -->
{#if systemTerminals.length > 0}
<div class="flex items-center justify-between px-3 py-1">
<span
class="text-[0.625rem] font-normal text-gray-400 dark:text-gray-500 uppercase tracking-wider"
>
{$i18n.t('System')}
</span>
{#if $user?.role === 'admin'}
<Tooltip content={$i18n.t('Add Terminal')} placement="top">
<button
type="button"
aria-label={$i18n.t('Add Terminal')}
class="p-0.5 rounded-md text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition"
on:click|stopPropagation={() => {
show = false;
showSettings.set('admin:integrations');
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="size-3.5"
>
<path
d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
/>
</svg>
</button>
</Tooltip>
{/if}
</button>
{/each}
{/if}
</DropdownMenu>
</div>
</Dropdown>
</div>
{#each systemTerminals as terminal}
<button
type="button"
class="flex w-full justify-between gap-2 items-center h-[1.6875rem] px-2 text-[0.8125rem] font-normal cursor-pointer rounded-xl {$selectedTerminalId ===
terminal.id
? 'bg-gray-50/40 dark:bg-gray-800/40'
: 'hover:bg-gray-50/40 dark:hover:bg-gray-800/40'}"
on:click={() => selectSystem(terminal)}
>
<div class="flex flex-1 gap-2 items-center truncate">
<Cloud className="size-3.5 shrink-0" strokeWidth="2" />
<span class="truncate">{terminal.name || $i18n.t('Terminal')}</span>
</div>
{#if $selectedTerminalId === terminal.id}
<div class="shrink-0 text-emerald-600 dark:text-emerald-400">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="size-3.5"
>
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd"
/>
</svg>
</div>
{/if}
</button>
{/each}
{/if}
</DropdownMenu>
</div>
</Dropdown>
{/if}
</div>