Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
<script lang="ts"> | |
import { base } from "$app/paths"; | |
import type { ToolLogoColor, ToolLogoIcon } from "$lib/types/Tool"; | |
import { debounce } from "$lib/utils/debounce"; | |
import { onMount } from "svelte"; | |
import ToolLogo from "./ToolLogo.svelte"; | |
import CarbonClose from "~icons/carbon/close"; | |
interface ToolSuggestion { | |
_id: string; | |
displayName: string; | |
createdByName: string; | |
color: ToolLogoColor; | |
icon: ToolLogoIcon; | |
} | |
interface Props { | |
toolIds?: string[]; | |
} | |
let { toolIds = $bindable([]) }: Props = $props(); | |
let selectedValues: ToolSuggestion[] = $state([]); | |
onMount(async () => { | |
selectedValues = await Promise.all( | |
toolIds.map(async (id) => await fetch(`${base}/api/tools/${id}`).then((res) => res.json())) | |
); | |
await fetchSuggestions(""); | |
}); | |
let inputValue = $state(""); | |
let maxValues = 3; | |
let suggestions: ToolSuggestion[] = $state([]); | |
async function fetchSuggestions(query: string) { | |
suggestions = (await fetch(`${base}/api/tools/search?q=${query}`).then((res) => | |
res.json() | |
)) satisfies ToolSuggestion[]; | |
} | |
const debouncedFetch = debounce((query: string) => fetchSuggestions(query), 300); | |
function addValue(value: ToolSuggestion) { | |
if (selectedValues.length < maxValues && !selectedValues.includes(value)) { | |
selectedValues = [...selectedValues, value]; | |
toolIds = [...toolIds, value._id]; | |
inputValue = ""; | |
suggestions = []; | |
} | |
} | |
function removeValue(id: ToolSuggestion["_id"]) { | |
selectedValues = selectedValues.filter((v) => v._id !== id); | |
toolIds = selectedValues.map((value) => value._id); | |
} | |
</script> | |
{#if selectedValues.length > 0} | |
<div class="flex flex-wrap items-center justify-center gap-2"> | |
{#each selectedValues as value} | |
<div | |
class="flex items-center justify-center space-x-2 rounded border border-gray-300 bg-gray-200 px-2 py-1" | |
> | |
{#key value.color + value.icon} | |
<ToolLogo color={value.color} icon={value.icon} size="sm" /> | |
{/key} | |
<div class="flex flex-col items-center justify-center py-1"> | |
<a | |
href={`${base}/tools/${value._id}`} | |
target="_blank" | |
class="line-clamp-1 truncate font-semibold text-blue-600 hover:underline" | |
>{value.displayName}</a | |
> | |
{#if value.createdByName} | |
<p class="text-center text-xs text-gray-500"> | |
Created by | |
<a class="underline" href="{base}/tools?user={value.createdByName}" target="_blank" | |
>{value.createdByName}</a | |
> | |
</p> | |
{:else} | |
<p class="text-center text-xs text-gray-500">Official HuggingChat tool</p> | |
{/if} | |
</div> | |
<button | |
onclick={(e) => { | |
e.preventDefault(); | |
e.stopPropagation(); | |
removeValue(value._id); | |
}} | |
class="text-lg text-gray-600" | |
> | |
<CarbonClose /> | |
</button> | |
</div> | |
{/each} | |
</div> | |
{/if} | |
{#if selectedValues.length < maxValues} | |
<div class="group relative block"> | |
<input | |
type="text" | |
bind:value={inputValue} | |
oninput={(ev) => { | |
inputValue = ev.currentTarget.value; | |
debouncedFetch(inputValue); | |
}} | |
disabled={selectedValues.length >= maxValues} | |
class="w-full rounded border border-gray-200 bg-gray-100 px-3 py-2" | |
class:opacity-50={selectedValues.length >= maxValues} | |
class:bg-gray-100={selectedValues.length >= maxValues} | |
placeholder="Type to search tools..." | |
tabindex="0" | |
/> | |
{#if suggestions.length > 0} | |
<div | |
class="invisible absolute z-10 mt-1 w-full rounded border border-gray-300 bg-white shadow-lg group-focus-within:visible" | |
tabindex="-1" | |
> | |
{#if inputValue === ""} | |
<p class="px-3 py-2 text-left text-xs text-gray-500"> | |
Start typing to search for tools... | |
</p> | |
{:else} | |
{#each suggestions as suggestion} | |
<button | |
onclick={(e) => { | |
e.preventDefault(); | |
e.stopPropagation(); | |
addValue(suggestion); | |
}} | |
class="w-full cursor-pointer px-3 py-2 text-left hover:bg-blue-500 hover:text-white" | |
tabindex="0" | |
> | |
{suggestion.displayName} | |
{#if suggestion.createdByName} | |
<span class="text-xs text-gray-500"> by {suggestion.createdByName}</span> | |
{/if} | |
</button> | |
{/each} | |
{/if} | |
</div> | |
{/if} | |
</div> | |
{/if} | |