| <script lang="ts"> |
| import { marked } from 'marked'; |
| |
| import { getContext, tick } from 'svelte'; |
| import dayjs from '$lib/dayjs'; |
| |
| import { mobile, settings, user } from '$lib/stores'; |
| import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants'; |
| |
| import Tooltip from '$lib/components/common/Tooltip.svelte'; |
| import { copyToClipboard, sanitizeResponseContent } from '$lib/utils'; |
| import ArrowUpTray from '$lib/components/icons/ArrowUpTray.svelte'; |
| import Check from '$lib/components/icons/Check.svelte'; |
| import ModelItemMenu from './ModelItemMenu.svelte'; |
| import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte'; |
| import { toast } from 'svelte-sonner'; |
| import Tag from '$lib/components/icons/Tag.svelte'; |
| import Label from '$lib/components/icons/Label.svelte'; |
| |
| const i18n = getContext('i18n'); |
| |
| export let selectedModelIdx: number = -1; |
| export let item: any = {}; |
| export let index: number = -1; |
| export let value: string = ''; |
| |
| export let unloadModelHandler: (modelValue: string) => void = () => {}; |
| export let pinModelHandler: (modelId: string) => void = () => {}; |
| export let deleteModelHandler: (model: any) => void = () => {}; |
| |
| export let onClick: () => void = () => {}; |
| |
| const copyLinkHandler = async (model) => { |
| const baseUrl = window.location.origin; |
| const res = await copyToClipboard(`${baseUrl}/?model=${encodeURIComponent(model.id)}`); |
| |
| if (res) { |
| toast.success($i18n.t('Copied link to clipboard')); |
| } else { |
| toast.error($i18n.t('Failed to copy link')); |
| } |
| }; |
| |
| let showMenu = false; |
| </script> |
|
|
| <button |
| role="option" |
| aria-selected={value === item.value} |
| 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-2 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 === |
| selectedModelIdx |
| ? 'bg-gray-100 dark:bg-gray-800 group-hover:bg-transparent' |
| : ''}" |
| data-arrow-selected={index === selectedModelIdx} |
| data-value={item.value} |
| on:click={() => { |
| onClick(); |
| }} |
| > |
| <div class="flex flex-col flex-1 gap-1.5"> |
| {#if (item?.model?.tags ?? []).length > 0} |
| |
| |
| |
| {#each item.model?.tags.sort((a, b) => a.name.localeCompare(b.name)) as tag} |
| {tag.name} |
| |
| |
| |
| {tag.name} |
| |
| </Tooltip> |
| {/each} |
| </div> |
| {/if} --> |
|
|
| |
| |
| {$user?.role === 'admin' ? (item?.value ?? '') : ''} |
| |
| {`${WEBUI_API_BASE_URL}{item.model.id}{$i18n.language} |
| {$i18n.t('{{modelName}{ modelName: item.label } |
| |
| |
| {(e) => { |
| e.currentTarget.src = '/favicon.png'; |
| } |
| |
| |
| |
|
|
| |
| {`${item.label}{item.value} |
| |
| {item.label} |
| |
| |
| </div> |
|
|
| |
| {#if item.model.owned_by === 'ollama'} |
| {#if (item.model.ollama?.details?.parameter_size ?? '') !== ''} |
| |
| |
| {`${ |
| item.model.ollama?.details?.quantization_level |
| ? item.model.ollama?.details?.quantization_level + ' ' |
| : '' |
| }{ |
| item.model.ollama?.size |
| ? `(${(item.model.ollama?.size / 1024 ** 3).toFixed(1)} |
| |
| |
| |
| |
| |
| {item.model.ollama?.details?.parameter_size ?? ''} |
| |
| |
| |
| {/if} |
| {/if} |
|
|
| {#if item.model.loaded} |
| |
| |
| {item.model.ollama?.expires_at && |
| new Date(item.model.ollama?.expires_at * 1000) > new Date() |
| ? `${$i18n.t('Unloads {{FROM_NOW}{ |
| FROM_NOW: dayjs(item.model.ollama?.expires_at * 1000).fromNow() |
| } |
| {$i18n.t('Loaded')} |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| </div> |
| {/if} |
|
|
| <!-- {JSON.stringify(item.info)} --> |
|
|
| {#if (item?.model?.tags ?? []).length > 0} |
| {#key item.model.id} |
| |
| |
| {#each item.model?.tags.sort((a, b) => a.name.localeCompare(b.name)) as tag} |
| {tag.name} |
| |
| {tag.name} |
| |
| |
| {/each} |
| |
|
|
| |
| |
| |
| </Tooltip> |
| {/key} |
| {/if} |
|
|
| {#if item.model?.direct} |
| <Tooltip content={`${$i18n.t('Direct')}`}> |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| </div> |
| </Tooltip> |
| {:else if item.model.connection_type === 'external'} |
| <Tooltip content={`${$i18n.t('External')}`}> |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| </div> |
| </Tooltip> |
| {/if} |
|
|
| {#if item.model?.info?.meta?.description} |
| <Tooltip |
| content={`${marked.parse( |
| sanitizeResponseContent(item.model?.info?.meta?.description).replaceAll('\n', '<br>') |
| )}`} |
| > |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| </div> |
| </Tooltip> |
| {/if} |
| </div> |
| </div> |
| </div> |
|
|
| |
| {#if $user?.role === 'admin' && item.model.loaded} |
| |
| {`${$i18n.t('Eject')} |
| |
| |
| |
| |
| {$i18n.t('Eject model')} |
| {(e) => { |
| e.preventDefault(); |
| e.stopPropagation(); |
| unloadModelHandler(item.value); |
| } |
| |
| |
| |
| </Tooltip> |
| {/if} |
|
|
| <ModelItemMenu |
| bind:show={showMenu} |
| model={item.model} |
| {pinModelHandler} |
| {deleteModelHandler} |
| copyLinkHandler={() => { |
| copyLinkHandler(item.model); |
| }} |
| > |
| <button |
| aria-label={`${$i18n.t('More Options')}`} |
| class="flex" |
| on:click={(e) => { |
| e.preventDefault(); |
| e.stopPropagation(); |
| showMenu = !showMenu; |
| }} |
| > |
| <EllipsisHorizontal /> |
| </button> |
| </ModelItemMenu> |
|
|
| {#if value === item.value} |
| |
| |
| |
| {/if} |
| </div> |
| </button> |
|
|