refac
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
|
||||
export let showSetDefault = true;
|
||||
export let triggerClassName = 'text-lg';
|
||||
export let className = undefined;
|
||||
|
||||
let compareModels = selectedModels.length > 1;
|
||||
|
||||
@@ -69,6 +70,7 @@
|
||||
model: model
|
||||
}))}
|
||||
{pinModelHandler}
|
||||
{className}
|
||||
{triggerClassName}
|
||||
{showSetDefault}
|
||||
onSetDefault={saveDefaultModel}
|
||||
|
||||
@@ -52,17 +52,17 @@
|
||||
role="option"
|
||||
aria-selected={isSelected}
|
||||
aria-label={$i18n.t('Select {{modelName}} model', { modelName: item.label })}
|
||||
class="flex group/item w-full text-left font-medium line-clamp-1 select-none items-center rounded-button py-[7px] pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-hidden transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl cursor-pointer data-highlighted:bg-muted {index ===
|
||||
class="group/item flex h-8 w-full cursor-pointer select-none items-center rounded-xl px-2 text-left text-[13px] font-normal text-gray-700 outline-hidden transition-colors duration-75 hover:bg-gray-50/40 dark:text-gray-100 dark:hover:bg-gray-800/40 {index ===
|
||||
selectedModelIdx && !compareEnabled
|
||||
? 'bg-gray-100 dark:bg-gray-800 group-hover:bg-transparent'
|
||||
: ''} {isSelected ? 'bg-gray-100 dark:bg-gray-800 group-hover:bg-transparent' : ''}"
|
||||
? 'bg-gray-50/70 dark:bg-gray-800/60'
|
||||
: ''} {isSelected ? 'bg-gray-50/70 dark:bg-gray-800/60' : ''}"
|
||||
data-arrow-selected={index === selectedModelIdx}
|
||||
data-value={item.value}
|
||||
on:click={() => {
|
||||
onClick();
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-col flex-1 gap-1.5">
|
||||
<div class="flex flex-1 flex-col gap-1.5 overflow-hidden">
|
||||
<!-- {#if (item?.model?.tags ?? []).length > 0}
|
||||
<div
|
||||
class="flex gap-0.5 self-center items-start h-full w-full translate-y-[0.5px] overflow-x-auto scrollbar-none"
|
||||
@@ -79,13 +79,13 @@
|
||||
</div>
|
||||
{/if} -->
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center gap-2 overflow-hidden">
|
||||
<div class="flex items-center min-w-fit">
|
||||
<Tooltip content={$user?.role === 'admin' ? (item?.value ?? '') : ''} placement="top-start">
|
||||
<img
|
||||
src={`${WEBUI_API_BASE_URL}/models/model/profile/image?id=${item.model.id}&lang=${$i18n.language}`}
|
||||
alt={$i18n.t('{{modelName}} profile image', { modelName: item.label })}
|
||||
class="rounded-full size-5 flex items-center"
|
||||
class="flex size-4 items-center rounded-full"
|
||||
loading="lazy"
|
||||
on:error={(e) => {
|
||||
e.currentTarget.src = '/favicon.png';
|
||||
@@ -94,7 +94,7 @@
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="flex min-w-0 items-center">
|
||||
<Tooltip content={`${item.label} (${item.value})`} placement="top-start">
|
||||
<div class="line-clamp-1">
|
||||
{item.label}
|
||||
@@ -102,7 +102,7 @@
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class=" shrink-0 flex items-center gap-2">
|
||||
<div class="flex shrink-0 items-center gap-1.5">
|
||||
{#if item.model.owned_by === 'ollama'}
|
||||
{#if (item.model.ollama?.details?.parameter_size ?? '') !== ''}
|
||||
<div class="flex items-center translate-y-[0.5px]">
|
||||
@@ -118,7 +118,7 @@
|
||||
}`}
|
||||
className="self-end"
|
||||
>
|
||||
<span class=" text-xs font-medium text-gray-600 dark:text-gray-400 line-clamp-1"
|
||||
<span class="line-clamp-1 text-[11px] font-normal text-gray-500 dark:text-gray-400"
|
||||
>{item.model.ollama?.details?.parameter_size ?? ''}</span
|
||||
>
|
||||
</Tooltip>
|
||||
@@ -127,7 +127,7 @@
|
||||
{/if}
|
||||
|
||||
{#if item.model.loaded}
|
||||
<div class="flex items-center translate-y-[0.5px] px-0.5">
|
||||
<div class="flex items-center px-0.5">
|
||||
<Tooltip
|
||||
content={item.model.ollama?.expires_at &&
|
||||
new Date(item.model.ollama?.expires_at * 1000) > new Date()
|
||||
@@ -138,11 +138,11 @@
|
||||
className="self-end"
|
||||
>
|
||||
<div class=" flex items-center">
|
||||
<span class="relative flex size-2">
|
||||
<span class="relative flex size-1.5">
|
||||
<span
|
||||
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
|
||||
/>
|
||||
<span class="relative inline-flex rounded-full size-2 bg-green-500" />
|
||||
<span class="relative inline-flex size-1.5 rounded-full bg-green-500" />
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
@@ -240,7 +240,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ml-auto pl-2 pr-1 flex items-center gap-1.5 shrink-0">
|
||||
<div class="ml-auto flex shrink-0 items-center gap-1.5 pl-2">
|
||||
{#if !selectionOnly && $user?.role === 'admin' && item.model.loaded}
|
||||
<Tooltip
|
||||
content={`${$i18n.t('Eject')}`}
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
import Switch from '$lib/components/common/Switch.svelte';
|
||||
import ChatBubbleOval from '$lib/components/icons/ChatBubbleOval.svelte';
|
||||
import Keyframes from '$lib/components/icons/Keyframes.svelte';
|
||||
import TagSelector from '$lib/components/workspace/common/TagSelector.svelte';
|
||||
|
||||
import ModelItem from './ModelItem.svelte';
|
||||
|
||||
@@ -61,15 +63,13 @@
|
||||
[key: string]: any;
|
||||
}[] = [];
|
||||
|
||||
export let className = 'w-[32rem]';
|
||||
export let className = 'w-[22rem]';
|
||||
export let triggerClassName = 'text-lg';
|
||||
export let showSetDefault = false;
|
||||
export let onSetDefault: () => Promise<void> | void = () => {};
|
||||
|
||||
export let pinModelHandler: (modelId: string) => void = () => {};
|
||||
|
||||
let tagsContainerElement;
|
||||
|
||||
let show = false;
|
||||
let triggerElement: HTMLElement | null = null;
|
||||
let contentElement: HTMLElement | null = null;
|
||||
@@ -112,7 +112,8 @@
|
||||
const target = e.target as Node;
|
||||
if (
|
||||
(triggerElement && triggerElement.contains(target)) ||
|
||||
(contentElement && contentElement.contains(target))
|
||||
(contentElement && contentElement.contains(target)) ||
|
||||
((target as HTMLElement).closest?.('.model-selector-child-menu') ?? false)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -146,6 +147,8 @@
|
||||
|
||||
let selectedTag = '';
|
||||
let selectedConnectionType = '';
|
||||
let selectedFilter = '';
|
||||
let modelFilterItems = [];
|
||||
|
||||
let ollamaVersion = null;
|
||||
let selectedModelIdx = 0;
|
||||
@@ -243,6 +246,38 @@
|
||||
resetView();
|
||||
}
|
||||
|
||||
$: modelFilterItems = [
|
||||
...(items.find((item) => item.model?.connection_type === 'local')
|
||||
? [{ value: 'connection:local', label: $i18n.t('Local') }]
|
||||
: []),
|
||||
...(items.find((item) => item.model?.connection_type === 'external')
|
||||
? [{ value: 'connection:external', label: $i18n.t('External') }]
|
||||
: []),
|
||||
...(items.find((item) => item.model?.direct)
|
||||
? [{ value: 'connection:direct', label: $i18n.t('Direct') }]
|
||||
: []),
|
||||
...tags.map((tag) => ({ value: `tag:${tag}`, label: tag }))
|
||||
];
|
||||
|
||||
$: selectedFilter = selectedConnectionType
|
||||
? `connection:${selectedConnectionType}`
|
||||
: selectedTag
|
||||
? `tag:${selectedTag}`
|
||||
: '';
|
||||
|
||||
const setModelFilter = (filterValue: string) => {
|
||||
if (!filterValue) {
|
||||
selectedConnectionType = '';
|
||||
selectedTag = '';
|
||||
} else if (filterValue.startsWith('connection:')) {
|
||||
selectedConnectionType = filterValue.replace('connection:', '');
|
||||
selectedTag = '';
|
||||
} else if (filterValue.startsWith('tag:')) {
|
||||
selectedConnectionType = '';
|
||||
selectedTag = filterValue.replace('tag:', '');
|
||||
}
|
||||
};
|
||||
|
||||
const resetView = async () => {
|
||||
await tick();
|
||||
|
||||
@@ -527,7 +562,7 @@
|
||||
deleteModelTarget = null;
|
||||
};
|
||||
|
||||
const ITEM_HEIGHT = 39;
|
||||
const ITEM_HEIGHT = 32;
|
||||
const OVERSCAN = 10;
|
||||
|
||||
let listScrollTop = 0;
|
||||
@@ -536,7 +571,7 @@
|
||||
$: visibleStart = Math.max(0, Math.floor(listScrollTop / ITEM_HEIGHT) - OVERSCAN);
|
||||
$: visibleEnd = Math.min(
|
||||
filteredItems.length,
|
||||
Math.ceil((listScrollTop + 256) / ITEM_HEIGHT) + OVERSCAN
|
||||
Math.ceil((listScrollTop + 288) / ITEM_HEIGHT) + OVERSCAN
|
||||
);
|
||||
</script>
|
||||
|
||||
@@ -603,18 +638,18 @@
|
||||
<div
|
||||
class="z-40 {$mobile
|
||||
? `w-full`
|
||||
: `${className}`} max-w-[calc(100vw-1rem)] justify-start rounded-2xl bg-white dark:bg-gray-850 dark:text-white shadow-lg outline-hidden"
|
||||
: `${className}`} max-w-[calc(100vw-1rem)] justify-start rounded-xl border border-gray-100 bg-white p-0.5 shadow-lg outline-hidden dark:border-gray-800 dark:bg-gray-850 dark:text-white"
|
||||
transition:flyAndScale
|
||||
>
|
||||
<slot>
|
||||
{#if searchEnabled}
|
||||
<div class="flex items-center gap-2 px-3.5 pt-3 mb-1.5">
|
||||
<Search className="size-4" strokeWidth="2.5" />
|
||||
<div class="my-0.5 flex h-[1.6875rem] items-center gap-2">
|
||||
<Search className="ml-2 size-3.5 shrink-0" strokeWidth="2" />
|
||||
|
||||
<input
|
||||
id="model-search-input"
|
||||
bind:value={searchValue}
|
||||
class="w-full text-sm bg-transparent outline-hidden"
|
||||
class="w-full bg-transparent text-[13px] font-normal outline-hidden placeholder:text-gray-400 dark:placeholder:text-gray-500"
|
||||
placeholder={searchPlaceholder}
|
||||
autocomplete="off"
|
||||
aria-label={$i18n.t('Search In Models')}
|
||||
@@ -642,124 +677,44 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if multipleEnabled}
|
||||
<button
|
||||
type="button"
|
||||
class="shrink-0 rounded-full px-2 py-1 text-[11px] font-medium leading-none transition-colors duration-100 {compareEnabled
|
||||
? 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-200'
|
||||
: 'text-gray-400 hover:bg-gray-50 hover:text-gray-600 dark:text-gray-500 dark:hover:bg-gray-800/40 dark:hover:text-gray-300'}"
|
||||
aria-pressed={compareEnabled}
|
||||
on:click={() => {
|
||||
setCompareEnabled(!compareEnabled);
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Compare')}
|
||||
</button>
|
||||
{#if modelFilterItems.length > 0 || multipleEnabled}
|
||||
<div class="flex min-w-0 shrink-0 items-center gap-0.5">
|
||||
{#if multipleEnabled}
|
||||
<Tooltip content={$i18n.t('Compare')}>
|
||||
<button
|
||||
type="button"
|
||||
class="flex size-[1.375rem] shrink-0 items-center justify-center rounded-lg transition-colors duration-100 {compareEnabled
|
||||
? 'bg-gray-100/60 text-gray-700 hover:bg-gray-100/60 dark:bg-gray-800/40 dark:text-gray-300 dark:hover:bg-gray-800/40'
|
||||
: 'text-gray-500 hover:bg-gray-50/40 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-800/40 dark:hover:text-gray-200'}"
|
||||
aria-label={$i18n.t('Compare')}
|
||||
aria-pressed={compareEnabled}
|
||||
on:click={() => {
|
||||
setCompareEnabled(!compareEnabled);
|
||||
}}
|
||||
>
|
||||
<Keyframes className="size-3" strokeWidth="2" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{#if modelFilterItems.length > 0}
|
||||
<TagSelector
|
||||
bind:value={selectedFilter}
|
||||
placeholder={$i18n.t('All')}
|
||||
align="end"
|
||||
items={modelFilterItems}
|
||||
triggerClass="relative flex h-[1.375rem] max-w-32 items-center gap-0.5 rounded-xl bg-transparent px-1.5 text-[11px] font-normal text-gray-400 transition-colors duration-100 hover:bg-gray-50/40 hover:text-gray-600 dark:text-gray-500 dark:hover:bg-gray-800/40 dark:hover:text-gray-300"
|
||||
itemClass="flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] capitalize hover:bg-gray-50/40 hover:text-gray-900 dark:hover:bg-gray-800/40 dark:hover:text-gray-100"
|
||||
contentClass="min-w-36 model-selector-child-menu"
|
||||
onChange={setModelFilter}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="px-2">
|
||||
{#if tags && items.filter((item) => includeHidden || !(item.model?.info?.meta?.hidden ?? false)).length > 0}
|
||||
<div
|
||||
class=" flex w-full bg-white dark:bg-gray-850 overflow-x-auto scrollbar-none font-[450] mb-0.5"
|
||||
on:wheel={(e) => {
|
||||
if (e.deltaY !== 0) {
|
||||
e.preventDefault();
|
||||
e.currentTarget.scrollLeft += e.deltaY;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="flex gap-1 w-fit text-center text-sm rounded-full bg-transparent px-1.5 whitespace-nowrap"
|
||||
bind:this={tagsContainerElement}
|
||||
>
|
||||
{#if items.find((item) => item.model?.connection_type === 'local') || items.find((item) => item.model?.connection_type === 'external') || items.find((item) => item.model?.direct) || tags.length > 0}
|
||||
<button
|
||||
class="min-w-fit outline-none px-1.5 py-0.5 {selectedTag === '' &&
|
||||
selectedConnectionType === ''
|
||||
? ''
|
||||
: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition capitalize"
|
||||
aria-pressed={selectedTag === '' && selectedConnectionType === ''}
|
||||
on:click={() => {
|
||||
selectedConnectionType = '';
|
||||
selectedTag = '';
|
||||
}}
|
||||
>
|
||||
{$i18n.t('All')}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if items.find((item) => item.model?.connection_type === 'local')}
|
||||
<button
|
||||
class="min-w-fit outline-none px-1.5 py-0.5 {selectedConnectionType ===
|
||||
'local'
|
||||
? ''
|
||||
: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition capitalize"
|
||||
aria-pressed={selectedConnectionType === 'local'}
|
||||
on:click={() => {
|
||||
selectedTag = '';
|
||||
selectedConnectionType = 'local';
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Local')}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if items.find((item) => item.model?.connection_type === 'external')}
|
||||
<button
|
||||
class="min-w-fit outline-none px-1.5 py-0.5 {selectedConnectionType ===
|
||||
'external'
|
||||
? ''
|
||||
: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition capitalize"
|
||||
aria-pressed={selectedConnectionType === 'external'}
|
||||
on:click={() => {
|
||||
selectedTag = '';
|
||||
selectedConnectionType = 'external';
|
||||
}}
|
||||
>
|
||||
{$i18n.t('External')}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if items.find((item) => item.model?.direct)}
|
||||
<button
|
||||
class="min-w-fit outline-none px-1.5 py-0.5 {selectedConnectionType ===
|
||||
'direct'
|
||||
? ''
|
||||
: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition capitalize"
|
||||
aria-pressed={selectedConnectionType === 'direct'}
|
||||
on:click={() => {
|
||||
selectedTag = '';
|
||||
selectedConnectionType = 'direct';
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Direct')}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#each tags as tag}
|
||||
<Tooltip content={tag}>
|
||||
<button
|
||||
class="min-w-fit outline-none px-1.5 py-0.5 {selectedTag === tag
|
||||
? ''
|
||||
: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition capitalize"
|
||||
aria-pressed={selectedTag === tag}
|
||||
on:click={() => {
|
||||
selectedConnectionType = '';
|
||||
selectedTag = tag;
|
||||
}}
|
||||
>
|
||||
{tag.length > 16 ? `${tag.slice(0, 16)}...` : tag}
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="px-2 group relative">
|
||||
<div class="group relative">
|
||||
{#if filteredItems.length === 0}
|
||||
{#if items.length === 0 && $user?.role === 'admin'}
|
||||
<div class="flex flex-col items-start justify-center py-6 px-4 text-start">
|
||||
@@ -781,7 +736,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="">
|
||||
<div class="block px-3 py-2 text-sm text-gray-700 dark:text-gray-100">
|
||||
<div class="block px-2 py-1 text-[13px] text-gray-700 dark:text-gray-100">
|
||||
{$i18n.t('No results found')}
|
||||
</div>
|
||||
</div>
|
||||
@@ -789,7 +744,7 @@
|
||||
{:else}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="max-h-64 overflow-y-auto"
|
||||
class="max-h-72 overflow-y-auto"
|
||||
role="listbox"
|
||||
aria-label={$i18n.t('Available models')}
|
||||
bind:this={listContainer}
|
||||
@@ -828,7 +783,7 @@
|
||||
placement="top-start"
|
||||
>
|
||||
<button
|
||||
class="flex w-full font-medium line-clamp-1 select-none items-center rounded-button py-[7px] pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-hidden transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl cursor-pointer data-highlighted:bg-muted"
|
||||
class="flex h-[1.6875rem] w-full cursor-pointer select-none items-center rounded-xl px-2 text-[13px] font-normal text-gray-700 outline-hidden transition-colors duration-75 hover:bg-gray-50/40 dark:text-gray-100 dark:hover:bg-gray-800/40"
|
||||
on:click={() => {
|
||||
pullModelHandler();
|
||||
}}
|
||||
@@ -844,7 +799,7 @@
|
||||
|
||||
{#each selectionOnly ? [] : Object.keys($MODEL_DOWNLOAD_POOL) as model}
|
||||
<div
|
||||
class="flex w-full justify-between font-medium select-none rounded-button py-[7px] pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-hidden transition-all duration-75 rounded-xl cursor-pointer data-highlighted:bg-muted"
|
||||
class="flex min-h-[1.6875rem] w-full cursor-pointer select-none justify-between rounded-xl px-2 text-[13px] font-normal text-gray-700 outline-hidden transition-colors duration-75 dark:text-gray-100"
|
||||
>
|
||||
<div class="flex">
|
||||
<div class="mr-2.5 translate-y-0.5">
|
||||
@@ -906,21 +861,23 @@
|
||||
</div>
|
||||
|
||||
{#if showSetDefault}
|
||||
<div class="px-3 pb-2 pt-1 text-left">
|
||||
<div class="flex items-center justify-end px-2 py-1 leading-none">
|
||||
<button
|
||||
type="button"
|
||||
class="text-[0.7rem] font-normal text-gray-500 underline-offset-2 transition-colors duration-100 hover:text-gray-700 hover:underline dark:text-gray-500 dark:hover:text-gray-300"
|
||||
class="text-[0.65rem] font-normal leading-none text-gray-500 underline-offset-2 transition-colors duration-100 hover:text-gray-700 hover:underline dark:text-gray-500 dark:hover:text-gray-300"
|
||||
on:click|stopPropagation={setDefaultHandler}
|
||||
>
|
||||
{$i18n.t('Set as default')}
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="pb-2"></div>
|
||||
<div class="pb-1"></div>
|
||||
{/if}
|
||||
|
||||
<div class="hidden w-[42rem]" />
|
||||
<div class="hidden w-[32rem]" />
|
||||
<div class="hidden w-[28rem]" />
|
||||
<div class="hidden w-[24rem]" />
|
||||
<div class="hidden w-[22rem]" />
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
: 'invisible'} bg-linear-to-b via-40% to-97% from-white/90 via-white/50 to-transparent dark:from-gray-900/90 dark:via-gray-900/50 dark:to-transparent pointer-events-none absolute inset-0 -bottom-10 z-[-1]"
|
||||
></div>
|
||||
|
||||
<div class=" flex max-w-full w-full mx-auto px-1.5 md:px-2 pt-0.5 bg-transparent">
|
||||
<div class=" flex max-w-full w-full mx-auto px-1.5 md:px-2 bg-transparent">
|
||||
<div class="flex items-center w-full max-w-full">
|
||||
{#if $mobile && !$showSidebar}
|
||||
<div
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
localStorage.setItem(`${id}-folder-state`, `${state}`);
|
||||
}}
|
||||
>
|
||||
<div class="flex items-center justify-between h-8 w-full pl-3.5 pr-1.5 shrink-0">
|
||||
<div class="flex items-center justify-between h-6 w-full pl-3.5 pr-1.5 shrink-0">
|
||||
<button
|
||||
type="button"
|
||||
class="group flex flex-1 h-full items-center gap-1 text-left text-xs text-gray-400 hover:text-gray-500 dark:text-gray-500 dark:hover:text-gray-400 transition-colors duration-100 {buttonClassName}"
|
||||
|
||||
@@ -655,7 +655,6 @@
|
||||
placeholder={$i18n.t('Select a base model (e.g. llama3, gpt-4o)')}
|
||||
searchPlaceholder={$i18n.t('Search a model')}
|
||||
items={getBaseModelItems($models)}
|
||||
className="w-[32rem]"
|
||||
triggerClassName="text-sm"
|
||||
selectionOnly
|
||||
includeHidden={$user?.role === 'admin'}
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
export let placeholder = $i18n.t('Tag');
|
||||
export let align = 'start';
|
||||
export let onChange: (value: string) => void = () => {};
|
||||
export let triggerClass =
|
||||
'relative h-8 w-full flex items-center gap-0.5 px-1.5 py-1.5 bg-transparent rounded-xl text-[13px] font-normal text-gray-700 transition hover:text-gray-900 dark:text-gray-200 dark:hover:text-gray-100';
|
||||
export let itemClass =
|
||||
'flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] capitalize hover:bg-gray-50/40 hover:text-gray-900 dark:hover:bg-gray-800/40 dark:hover:text-gray-100';
|
||||
export let contentClass = 'min-w-[170px]';
|
||||
|
||||
export let items = [];
|
||||
</script>
|
||||
@@ -21,16 +26,17 @@
|
||||
{items}
|
||||
{placeholder}
|
||||
{align}
|
||||
triggerClass="relative h-8 w-full flex items-center gap-0.5 px-1.5 py-1.5 bg-transparent rounded-xl text-[13px] font-normal text-gray-700 transition hover:text-gray-900 dark:text-gray-200 dark:hover:text-gray-100"
|
||||
itemClass="flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] capitalize hover:bg-gray-50/40 hover:text-gray-900 dark:hover:bg-gray-800/40 dark:hover:text-gray-100"
|
||||
{triggerClass}
|
||||
{itemClass}
|
||||
{contentClass}
|
||||
onChange={() => onChange(value)}
|
||||
>
|
||||
<svelte:fragment slot="trigger" let:selectedLabel>
|
||||
<div
|
||||
class="inline-flex h-input w-full outline-hidden bg-transparent truncate placeholder-gray-400 focus:outline-hidden capitalize"
|
||||
class="inline-flex h-input min-w-0 flex-1 outline-hidden bg-transparent truncate placeholder-gray-400 focus:outline-hidden capitalize"
|
||||
>
|
||||
{#if value}
|
||||
{value}
|
||||
{selectedLabel}
|
||||
{:else}
|
||||
{placeholder}
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user