| <script lang="ts"> |
| import Fuse from 'fuse.js'; |
| |
| import { createEventDispatcher, onMount } from 'svelte'; |
| import { tick, getContext } from 'svelte'; |
| |
| import { models } from '$lib/stores'; |
| |
| const i18n = getContext('i18n'); |
| |
| const dispatch = createEventDispatcher(); |
| |
| export let command = ''; |
| |
| let selectedIdx = 0; |
| let filteredItems = []; |
| |
| let fuse = new Fuse( |
| $models |
| .filter((model) => !model?.info?.meta?.hidden) |
| .map((model) => { |
| const _item = { |
| ...model, |
| modelName: model?.name, |
| tags: model?.info?.meta?.tags?.map((tag) => tag.name).join(' '), |
| desc: model?.info?.meta?.description |
| }; |
| return _item; |
| }), |
| { |
| keys: ['value', 'tags', 'modelName'], |
| threshold: 0.3 |
| } |
| ); |
| |
| $: filteredItems = command.slice(1) |
| ? fuse.search(command).map((e) => { |
| return e.item; |
| }) |
| : $models.filter((model) => !model?.info?.meta?.hidden); |
| |
| $: if (command) { |
| selectedIdx = 0; |
| } |
| |
| export const selectUp = () => { |
| selectedIdx = Math.max(0, selectedIdx - 1); |
| }; |
| |
| export const selectDown = () => { |
| selectedIdx = Math.min(selectedIdx + 1, filteredItems.length - 1); |
| }; |
| |
| const confirmSelect = async (model) => { |
| command = ''; |
| dispatch('select', model); |
| }; |
| |
| onMount(async () => { |
| await tick(); |
| const chatInputElement = document.getElementById('chat-input'); |
| await tick(); |
| chatInputElement?.focus(); |
| await tick(); |
| }); |
| </script> |
|
|
| {#if filteredItems.length > 0} |
| <div |
| id="commands-container" |
| class="pl-3 pr-14 mb-3 text-left w-full absolute bottom-0 left-0 right-0 z-10" |
| > |
| <div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850"> |
| <div |
| class="max-h-60 flex flex-col w-full rounded-xl bg-white dark:bg-gray-900 dark:text-gray-100" |
| > |
| <div class="m-1 overflow-y-auto p-1 rounded-r-lg space-y-0.5 scrollbar-hidden"> |
| {#each filteredItems as model, modelIdx} |
| <button |
| class="px-3 py-1.5 rounded-xl w-full text-left {modelIdx === selectedIdx |
| ? 'bg-gray-50 dark:bg-gray-850 selected-command-option-button' |
| : ''}" |
| type="button" |
| on:click={() => { |
| confirmSelect(model); |
| }} |
| on:mousemove={() => { |
| selectedIdx = modelIdx; |
| }} |
| on:focus={() => {}} |
| > |
| <div class="flex font-medium text-black dark:text-gray-100 line-clamp-1"> |
| <img |
| src={model?.info?.meta?.profile_image_url ?? '/static/favicon.png'} |
| alt={model?.name ?? model.id} |
| class="rounded-full size-6 items-center mr-2" |
| /> |
| {model.name} |
| </div> |
| </button> |
| {/each} |
| </div> |
| </div> |
| </div> |
| </div> |
| {/if} |
|
|