| | <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 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 |
| | aria-roledescription="model-item" |
| | aria-label={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} |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | {`${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 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() |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | </div> |
| | {/if} |
| | {/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.owned_by === 'ollama' && item.model.ollama?.expires_at && new Date(item.model.ollama?.expires_at * 1000) > new Date()} |
| | |
| | {`${$i18n.t('Eject')} |
| | |
| | |
| | |
| | |
| | {(e) => { |
| | e.preventDefault(); |
| | e.stopPropagation(); |
| | unloadModelHandler(item.value); |
| | } |
| | |
| | |
| | |
| | </Tooltip> |
| | {/if} |
| |
|
| | <ModelItemMenu |
| | bind:show={showMenu} |
| | model={item.model} |
| | {pinModelHandler} |
| | 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> |
| |
|