浏览代码

enh: model selector tag filter

Timothy Jaeryang Baek 2 月之前
父节点
当前提交
5f89c1b137
共有 1 个文件被更改,包括 68 次插入10 次删除
  1. 68 10
      src/lib/components/chat/ModelSelector/Selector.svelte

+ 68 - 10
src/lib/components/chat/ModelSelector/Selector.svelte

@@ -52,12 +52,17 @@
 	export let className = 'w-[32rem]';
 	export let className = 'w-[32rem]';
 	export let triggerClassName = 'text-lg';
 	export let triggerClassName = 'text-lg';
 
 
+	let tagsContainerElement;
+
 	let show = false;
 	let show = false;
+	let tags = [];
 
 
 	let selectedModel = '';
 	let selectedModel = '';
 	$: selectedModel = items.find((item) => item.value === value) ?? '';
 	$: selectedModel = items.find((item) => item.value === value) ?? '';
 
 
 	let searchValue = '';
 	let searchValue = '';
+	let selectedTag = '';
+
 	let ollamaVersion = null;
 	let ollamaVersion = null;
 
 
 	let selectedModelIdx = 0;
 	let selectedModelIdx = 0;
@@ -79,10 +84,23 @@
 	);
 	);
 
 
 	$: filteredItems = searchValue
 	$: filteredItems = searchValue
-		? fuse.search(searchValue).map((e) => {
-				return e.item;
-			})
-		: items;
+		? fuse
+				.search(searchValue)
+				.map((e) => {
+					return e.item;
+				})
+				.filter((item) => {
+					if (selectedTag === '') {
+						return true;
+					}
+					return item.model?.info?.meta?.tags?.map((tag) => tag.name).includes(selectedTag);
+				})
+		: items.filter((item) => {
+				if (selectedTag === '') {
+					return true;
+				}
+				return item.model?.info?.meta?.tags?.map((tag) => tag.name).includes(selectedTag);
+			});
 
 
 	const pullModelHandler = async () => {
 	const pullModelHandler = async () => {
 		const sanitizedModelTag = searchValue.trim().replace(/^ollama\s+(run|pull)\s+/, '');
 		const sanitizedModelTag = searchValue.trim().replace(/^ollama\s+(run|pull)\s+/, '');
@@ -214,6 +232,11 @@
 
 
 	onMount(async () => {
 	onMount(async () => {
 		ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false);
 		ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false);
+
+		if (items) {
+			tags = items.flatMap((item) => item.model?.info?.meta?.tags ?? []);
+			tags = [...new Set(tags)].map((tag) => tag.name).sort();
+		}
 	});
 	});
 
 
 	const cancelModelPullHandler = async (model: string) => {
 	const cancelModelPullHandler = async (model: string) => {
@@ -298,10 +321,43 @@
 					/>
 					/>
 				</div>
 				</div>
 
 
-				<hr class="border-gray-100 dark:border-gray-850" />
+				<hr class="border-gray-100 dark:border-gray-800" />
 			{/if}
 			{/if}
 
 
-			<div class="px-3 my-2 max-h-64 overflow-y-auto scrollbar-hidden group">
+			<div class="px-3 my-2 max-h-64 overflow-y-auto scrollbar-hidden group relative">
+				{#if tags}
+					<div class=" flex w-full sticky">
+						<div
+							class="flex gap-1 scrollbar-none overflow-x-auto w-fit text-center text-sm font-medium rounded-full bg-transparent px-1.5 pb-0.5"
+							bind:this={tagsContainerElement}
+						>
+							<button
+								class="min-w-fit outline-none p-1.5 {selectedTag === ''
+									? ''
+									: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+								on:click={() => {
+									selectedTag = '';
+								}}
+							>
+								{$i18n.t('All')}
+							</button>
+
+							{#each tags as tag}
+								<button
+									class="min-w-fit outline-none p-1.5 {selectedTag === tag
+										? ''
+										: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+									on:click={() => {
+										selectedTag = tag;
+									}}
+								>
+									{tag}
+								</button>
+							{/each}
+						</div>
+					</div>
+				{/if}
+
 				{#each filteredItems as item, index}
 				{#each filteredItems as item, index}
 					<button
 					<button
 						aria-label="model-item"
 						aria-label="model-item"
@@ -441,11 +497,13 @@
 								{/if}
 								{/if}
 
 
 								{#if !$mobile && (item?.model?.info?.meta?.tags ?? []).length > 0}
 								{#if !$mobile && (item?.model?.info?.meta?.tags ?? []).length > 0}
-									<div class="flex gap-0.5 self-center items-center h-full translate-y-[0.5px]">
+									<div
+										class="flex gap-0.5 self-center items-center h-full translate-y-[0.5px] overflow-x-auto scrollbar-none"
+									>
 										{#each item.model?.info?.meta.tags as tag}
 										{#each item.model?.info?.meta.tags as tag}
-											<Tooltip content={tag.name}>
+											<Tooltip content={tag.name} className="flex-shrink-0">
 												<div
 												<div
-													class=" text-xs font-bold px-1 rounded-sm uppercase line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
+													class=" text-xs font-bold px-1 rounded-sm uppercase bg-gray-500/20 text-gray-700 dark:text-gray-200"
 												>
 												>
 													{tag.name}
 													{tag.name}
 												</div>
 												</div>
@@ -575,7 +633,7 @@
 			</div>
 			</div>
 
 
 			{#if showTemporaryChatControl}
 			{#if showTemporaryChatControl}
-				<hr class="border-gray-100 dark:border-gray-850" />
+				<hr class="border-gray-100 dark:border-gray-800" />
 
 
 				<div class="flex items-center mx-2 my-2">
 				<div class="flex items-center mx-2 my-2">
 					<button
 					<button