nsarrazin's picture
nsarrazin HF Staff
feat(tools): add early access disclaimer
942fdc7 unverified
raw
history blame
9.67 kB
<script lang="ts">
import type { PageData } from "./$types";
import { env as envPublic } from "$env/dynamic/public";
import { isHuggingChat } from "$lib/utils/isHuggingChat";
import { goto } from "$app/navigation";
import { base } from "$app/paths";
import { page } from "$app/stores";
import CarbonAdd from "~icons/carbon/add";
import CarbonHelpFilled from "~icons/carbon/help-filled";
import CarbonClose from "~icons/carbon/close";
import CarbonArrowUpRight from "~icons/carbon/arrow-up-right";
import CarbonEarthAmerica from "~icons/carbon/earth-americas-filled";
import CarbonSearch from "~icons/carbon/search";
import Pagination from "$lib/components/Pagination.svelte";
import { getHref } from "$lib/utils/getHref";
import { debounce } from "$lib/utils/debounce";
import { isDesktop } from "$lib/utils/isDesktop";
import { SortKey } from "$lib/types/Assistant";
import ToolLogo from "$lib/components/ToolLogo.svelte";
export let data: PageData;
$: tools = data.tools.filter((t) =>
activeOnly ? data.settings.tools.some((toolId) => toolId === t._id.toString()) : true
);
$: toolsCreator = $page.url.searchParams.get("user");
$: createdByMe = data.user?.username && data.user.username === toolsCreator;
$: activeOnly = $page.url.searchParams.get("active") === "true";
const SEARCH_DEBOUNCE_DELAY = 400;
let filterInputEl: HTMLInputElement;
let filterValue = data.query;
let isFilterInPorgress = false;
let sortValue = data.sort as SortKey;
const resetFilter = () => {
filterValue = "";
isFilterInPorgress = false;
};
const filterOnName = debounce(async (value: string) => {
filterValue = value;
if (isFilterInPorgress) {
return;
}
isFilterInPorgress = true;
const newUrl = getHref($page.url, {
newKeys: { q: value },
existingKeys: { behaviour: "delete", keys: ["p"] },
});
await goto(newUrl);
if (isDesktop(window)) {
setTimeout(() => filterInputEl.focus(), 0);
}
isFilterInPorgress = false;
// there was a new filter query before server returned response
if (filterValue !== value) {
filterOnName(filterValue);
}
}, SEARCH_DEBOUNCE_DELAY);
const sortTools = () => {
const newUrl = getHref($page.url, {
newKeys: { sort: sortValue },
existingKeys: { behaviour: "delete", keys: ["p"] },
});
goto(newUrl);
};
const goToActiveUrl = () => {
return getHref($page.url, {
newKeys: { active: "true" },
existingKeys: { behaviour: "delete_except", keys: ["active", "sort"] },
});
};
const goToCommunity = () => {
return getHref($page.url, {
existingKeys: { behaviour: "delete_except", keys: ["sort", "q"] },
});
};
</script>
<svelte:head>
{#if isHuggingChat}
<title>HuggingChat - Tools</title>
<meta property="og:title" content="HuggingChat - Tools" />
<meta property="og:type" content="link" />
<meta property="og:description" content="Browse HuggingChat tools made by the community." />
<meta
property="og:image"
content="{envPublic.PUBLIC_ORIGIN ||
$page.url.origin}{base}/{envPublic.PUBLIC_APP_ASSETS}/tools-thumbnail.png"
/>
<meta property="og:url" content={$page.url.href} />
{/if}
</svelte:head>
<div class="scrollbar-custom mr-1 h-full overflow-y-auto py-12 max-sm:pt-8 md:py-24">
<div class="pt-42 mx-auto flex flex-col px-5 xl:w-[60rem] 2xl:w-[64rem]">
<div class="flex items-center">
<h1 class="text-2xl font-bold">Tools</h1>
{#if isHuggingChat}
<div class="5 ml-1.5 rounded-lg text-xxs uppercase text-gray-500 dark:text-gray-500">
beta
</div>
<a
href="https://huggingface.co/spaces/huggingchat/chat-ui/discussions/357"
class="ml-auto dark:text-gray-400 dark:hover:text-gray-300"
target="_blank"
>
<CarbonHelpFilled />
</a>
{/if}
</div>
<h3 class="text-gray-500">Popular tools made by the community</h3>
<h4 class="mt-2 w-fit text-purple-700 dark:text-purple-300">
This feature is in <span
class="rounded-lg bg-purple-100 px-2 py-1 font-semibold dark:bg-purple-800/50"
>early access</span
>. Only team members can see it and use it for now. Feel free to share feedback on it
internally!
</h4>
<div class="ml-auto mt-6 flex justify-between gap-2 max-sm:flex-col sm:items-center">
<a
href={`${base}/tools/new`}
class="flex items-center gap-1 whitespace-nowrap rounded-lg border bg-white py-1 pl-1.5 pr-2.5 shadow-sm hover:bg-gray-50 hover:shadow-none dark:border-gray-600 dark:bg-gray-700 dark:hover:bg-gray-700"
>
<CarbonAdd />Create new tool
</a>
</div>
<div class="mt-7 flex flex-wrap items-center gap-x-2 gap-y-3 text-sm">
{#if toolsCreator && !createdByMe}
<div
class="flex items-center gap-1.5 rounded-full border border-gray-300 bg-gray-50 px-3 py-1 dark:border-gray-600 dark:bg-gray-700 dark:text-white"
>
{toolsCreator}'s tools
<a
href={getHref($page.url, {
existingKeys: { behaviour: "delete", keys: ["user", "modelId", "p", "q"] },
})}
on:click={resetFilter}
class="group"
><CarbonClose
class="text-xs group-hover:text-gray-800 dark:group-hover:text-gray-300"
/></a
>
</div>
{#if isHuggingChat}
<a
href="https://hf.co/{toolsCreator}"
target="_blank"
class="ml-auto flex items-center text-xs text-gray-500 underline hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-300"
><CarbonArrowUpRight class="mr-1 flex-none text-[0.58rem]" target="_blank" />View {toolsCreator}
on HF</a
>
{/if}
{:else}
<a
href={goToActiveUrl()}
class="flex items-center gap-1.5 rounded-full border px-3 py-1 {activeOnly
? 'border-gray-300 bg-gray-50 dark:border-gray-600 dark:bg-gray-700 dark:text-white'
: 'border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-300'}"
>
<CarbonEarthAmerica class="text-xs" />
Active ({$page.data.settings?.tools?.length})
</a>
<a
href={goToCommunity()}
class="flex items-center gap-1.5 rounded-full border px-3 py-1 {!activeOnly &&
!toolsCreator
? 'border-gray-300 bg-gray-50 dark:border-gray-600 dark:bg-gray-700 dark:text-white'
: 'border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-300'}"
>
<CarbonEarthAmerica class="text-xs" />
Community
</a>
{#if data.user?.username}
<a
href={getHref($page.url, {
newKeys: { user: data.user.username },
existingKeys: { behaviour: "delete", keys: ["modelId", "p", "q", "active"] },
})}
on:click={resetFilter}
class="flex items-center gap-1.5 truncate rounded-full border px-3 py-1 {toolsCreator &&
createdByMe
? 'border-gray-300 bg-gray-50 dark:border-gray-600 dark:bg-gray-700 dark:text-white'
: 'border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-300'}"
>{data.user.username}
</a>
{/if}
{/if}
<div
class="relative ml-auto flex h-[30px] w-40 items-center rounded-full border px-2 has-[:focus]:border-gray-400 dark:border-gray-600 sm:w-64"
>
<CarbonSearch class="pointer-events-none absolute left-2 text-xs text-gray-400" />
<input
class="h-[30px] w-full bg-transparent pl-5 focus:outline-none"
placeholder="Filter by name"
value={filterValue}
on:input={(e) => filterOnName(e.currentTarget.value)}
bind:this={filterInputEl}
maxlength="150"
type="search"
/>
</div>
<select
bind:value={sortValue}
on:change={sortTools}
class="rounded-lg border border-gray-300 bg-gray-50 px-2 py-1 text-sm text-gray-900 focus:border-blue-700 focus:ring-blue-700 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400"
>
<option value={SortKey.TRENDING}>{SortKey.TRENDING}</option>
<option value={SortKey.POPULAR}>{SortKey.POPULAR}</option>
</select>
</div>
<div class="mt-8 grid grid-cols-1 gap-3 sm:gap-5 lg:grid-cols-2">
{#each tools as tool}
<a
href="{base}/tools/{tool._id.toString()}"
class="relative flex flex-row items-center gap-4 overflow-hidden text-balance rounded-xl border bg-gray-50/50 px-4 text-center shadow hover:bg-gray-50 hover:shadow-inner dark:border-gray-800/70 dark:bg-gray-950/20 dark:hover:bg-gray-950/40 max-sm:px-4 sm:h-24"
>
<ToolLogo color={tool.color} icon={tool.icon} />
<div class="flex w-full flex-col items-start text-left">
<span class="font-bold">
<span class="w-full overflow-clip">
{tool.displayName}
</span>
{#if ($page.data.settings?.tools ?? []).includes(tool._id.toString())}
<!-- active badge -->
<span
class="inline-flex items-center rounded-full bg-blue-800 px-2 py-0.5 text-xs font-semibold text-white"
>Active</span
>
{/if}
</span>
<span class="font-mono text-xs text-gray-400">
{tool.baseUrl ?? "Internal tool"}
</span>
{#if tool.createdByName}
<p class=" pt-2 text-xs text-gray-400 dark:text-gray-500">
Added by <a
class="hover:underline"
href="{base}/tools?user={tool.createdByName}"
on:click|stopPropagation
>
{tool.createdByName}
</a>
<span class="text-gray-300"></span>
{tool.useCount} runs
</p>
{:else}
<p class="pt-2 text-xs text-gray-400 dark:text-gray-500">HuggingChat official tool</p>
{/if}
</div>
</a>
{:else}
No tools found
{/each}
</div>
<Pagination
classNames="w-full flex justify-center mt-14 mb-4"
numItemsPerPage={data.numItemsPerPage}
numTotalItems={data.numTotalItems}
/>
</div>
</div>