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>