Spaces:
Sleeping
Sleeping
File size: 3,289 Bytes
db66673 fe3e1db db66673 f7c8859 4e74aba fe3e1db 4aa9c36 db66673 5dfba10 db66673 955bb3b db66673 2961a5b db66673 2961a5b db66673 2961a5b 4aa9c36 2961a5b db66673 2961a5b db66673 4e74aba db66673 4e74aba db66673 2961a5b 5dfba10 2961a5b 955bb3b 755e7a5 fe3e1db 755e7a5 2961a5b 5dfba10 2961a5b 955bb3b 755e7a5 fe3e1db 755e7a5 2961a5b db66673 2961a5b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | <script lang="ts">
import { Plus, Info, ChevronLeft } from '@lucide/svelte';
import * as Command from '$lib/components/ui/command/';
import { Button } from '$lib/components/ui/button/';
import { modelsState } from '$lib/state/models.svelte';
import { MAX_TRENDING_MODELS } from '$lib';
import Spinner from '../loading/Spinner.svelte';
import ModelImageInput from './ModelImageInput.svelte';
import { mode } from 'mode-watcher';
interface Props {
onSelect?: (model: string) => void;
excludeIds?: string[];
}
let { onSelect, excludeIds = [] }: Props = $props();
let open = $state(false);
let search = $state('');
const filteredModels = $derived(
modelsState.models.filter((m) => {
const matchesSearch = !search || m.id.toLowerCase().includes(search.toLowerCase());
const notExcluded = !excludeIds.includes(m.id);
return matchesSearch && notExcluded;
})
);
let trendingModels = $derived(filteredModels.slice(0, MAX_TRENDING_MODELS));
let otherModels = $derived(filteredModels.slice(MAX_TRENDING_MODELS));
</script>
<Button
variant={mode.current === 'dark' ? 'secondary' : 'outline'}
size="icon-sm"
class="!shadow-none!"
onclick={() => {
open = true;
search = '';
}}
>
<Plus />
</Button>
{#if excludeIds.length === 0}
<p
class="relative ml-1 flex items-center gap-1 rounded-md bg-gray-500/10 px-2.5 py-2 text-xs text-gray-600"
>
<ChevronLeft
class="absolute top-1/2 -left-2 size-3 -translate-y-1/2 fill-gray-500/10 text-gray-500/10"
/>
<Info class="size-3" />
Please select at least one model.
</p>
{/if}
<Command.Dialog bind:open shouldFilter={false}>
<Command.Input bind:value={search} placeholder="Search models..." />
<Command.List>
{#if modelsState.loading}
<Command.Loading class="flex items-center justify-center gap-2 p-3 text-muted-foreground/70">
<Spinner className="size-4" />
Loading models...
</Command.Loading>
{:else if modelsState.error}
<Command.Empty>{modelsState.error ?? 'No models found'}</Command.Empty>
{:else if filteredModels.length === 0}
<Command.Empty>No results found.</Command.Empty>
{:else}
<Command.Group heading="🔥 Trending">
{#each trendingModels as model (model.id)}
<Command.Item
onSelect={() => {
onSelect?.(model.id);
open = false;
}}
>
<img
src={`https://huggingface.co/api/avatars/${model.owned_by}`}
alt=""
class="size-4 rounded-full"
/>
<span>{model.id.split('/').pop() ?? model.id}</span>
{#if model.architecture?.input_modalities?.includes('image')}
<ModelImageInput />
{/if}
</Command.Item>
{/each}
<Command.Separator />
</Command.Group>
<Command.Group heading="Other models">
{#each otherModels as model (model.id)}
<Command.Item
onSelect={() => {
onSelect?.(model.id);
open = false;
}}
>
<img
src={`https://huggingface.co/api/avatars/${model.owned_by}`}
alt=""
class="size-4 rounded-full"
/>
<span>{model.id.split('/').pop() ?? model.id}</span>
{#if model.architecture?.input_modalities?.includes('image')}
<ModelImageInput />
{/if}
</Command.Item>
{/each}
</Command.Group>
{/if}
</Command.List>
</Command.Dialog>
|