calvesca's picture
Upload folder using huggingface_hub
0bd62e5 verified
<script lang="ts">
import MetaTags from "$lib/components/MetaTags.svelte";
import { page } from "$app/stores";
import { onMount, tick } from "svelte";
import type { ComponentData } from "./utils";
import { clickOutside } from "./utils";
const API = "https://gradio-custom-component-gallery-backend.hf.space/";
let components: ComponentData[] = [];
let selection: string = "";
let link_copied = false;
let selected_component: ComponentData | null = null;
let components_length: number = 0;
const COLOR_SETS = [
"from-red-50 via-red-100 to-red-50",
"from-green-50 via-green-100 to-green-50",
"from-yellow-50 via-yellow-100 to-yellow-50",
"from-pink-50 via-pink-100 to-pink-50",
"from-blue-50 via-blue-100 to-blue-50",
"from-purple-50 via-purple-100 to-purple-50"
];
let color_mapping: { [key: string]: string } = {};
function define_colors(components: ComponentData[]) {
let counter = 0;
for (const component of components) {
if (counter >= COLOR_SETS.length) {
counter = 0;
}
if (!(component.id in color_mapping)) {
color_mapping[component.id] = COLOR_SETS[counter];
}
component.background_color = color_mapping[component.id];
counter++;
}
}
const handle_box_click = (component: ComponentData) => {
selected_component = component;
};
async function fetch_components(selection: string[] = []) {
components = await fetch(
`${API}components?name_or_tags=${selection.join(",")}`
)
.then((response) => response.json())
.catch((error) => `Error: ${error}`);
define_colors(components);
if (!components_length) {
components_length = components.length;
}
components = components.sort((a, b) => b["likes"] - a["likes"]);
const id = $page.url.searchParams.get("id");
selected_component =
components.find((component) => component.id === id) ?? null;
}
onMount(fetch_components);
async function handle_keypress(e: KeyboardEvent): Promise<void> {
await tick();
e.preventDefault();
fetch_components(selection.split(","));
}
function copy_link(id: string) {
const url = $page.url;
url.searchParams.set("id", id);
const link = url.toString();
navigator.clipboard.writeText(link).then(() => {
window.setTimeout(() => {
link_copied = false;
}, 1000);
});
}
</script>
<MetaTags
title="Gradio Custom Components Gallery"
url={$page.url.pathname}
canonical={$page.url.pathname}
description="Search through a gallery of custom components."
/>
<div class="container mx-auto px-4 relative pt-8 mb-0">
<input
type="text"
class="w-full border border-gray-200 p-1 rounded-md outline-none text-center text-lg mb-1 focus:placeholder-transparent focus:shadow-none focus:border-orange-500 focus:ring-0"
placeholder="What are you looking for?"
autocomplete="off"
on:keyup={handle_keypress}
bind:value={selection}
/>
<div class="text-gray-600 mb-0 mx-auto w-fit text-sm">
Search through {components_length} components by name, keyword or description.
<a
class="link text-gray-600"
href="https://www.gradio.app/guides/five-minute-guide"
>Read more about Custom Components.</a
>
</div>
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6 !m-0 !mt-8">
{#each components as component (component.id)}
<div
on:click={(event) => {
handle_box_click(component);
event.stopPropagation();
}}
class=" cursor-pointer px-3 pt-3 h-40 group font:thin relative rounded-xl shadow-sm transform hover:scale-[1.02] hover:shadow-alternate transition bg-gradient-to-tr {component.background_color}"
>
<h2
class="text-md font-semibold text-gray-700 max-w-full truncate py-1"
>
{component.name.startsWith("gradio_")
? component.name.slice(7)
: component.name}
</h2>
{#if component.likes}
<p
class="text-sm font-light py-1"
style="position: absolute; top: 5%; right: 5%"
>
<span
class="bg-white p-1 rounded-md text-gray-700 inline-flex align-middle"
>
<svg
class="mr-1 self-center"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
focusable="false"
role="img"
width="1em"
height="1em"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 32 32"
fill="currentColor"
><path
d="M22.45,6a5.47,5.47,0,0,1,3.91,1.64,5.7,5.7,0,0,1,0,8L16,26.13,5.64,15.64a5.7,5.7,0,0,1,0-8,5.48,5.48,0,0,1,7.82,0L16,10.24l2.53-2.58A5.44,5.44,0,0,1,22.45,6m0-2a7.47,7.47,0,0,0-5.34,2.24L16,7.36,14.89,6.24a7.49,7.49,0,0,0-10.68,0,7.72,7.72,0,0,0,0,10.82L16,29,27.79,17.06a7.72,7.72,0,0,0,0-10.82A7.49,7.49,0,0,0,22.45,4Z"
></path></svg
>
<p class="">{component.likes ? component.likes : ""}</p>
</span>
</p>
{/if}
<p class="description text-md font-light py-1">
{component.description}
</p>
<p
class="text-sm font-light py-1"
style="position: absolute; bottom: 5%; left: 5%"
>
<span class="bg-white p-1 rounded-md text-gray-700">
@{component.author}
</span>
</p>
{#if component.template && component.template != "Fallback"}
<p
class="text-sm font-light py-1"
style="position: absolute; bottom: 5%; right: 5%"
>
<span class="bg-white p-1 rounded-md text-gray-700">
{component.template}
</span>
</p>
{/if}
</div>
{/each}
</div>
</div>
{#each components as component (component.id)}
<div
class="details-panel open border border-gray-200 shadow-xl rounded-xl bg-white p-5"
class:hidden={!(selected_component == component)}
class:flex={selected_component == component}
use:clickOutside={() => {
selected_component = null;
}}
>
<div class="self-end mr-8 flex">
{#if !link_copied}
<button
on:click={(e) => {
link_copied = true;
copy_link(component.id);
}}
class="rounded-md w-fit px-3.5 py-1 text-sm font-semibold text-white bg-orange-300 hover:drop-shadow-sm mr-4"
>
Share
</button>
{:else}
<span
class="rounded-md w-fit px-3.5 py-1 text-sm font-semibold text-white bg-orange-300 hover:drop-shadow-sm mr-4"
>
Link copied to clipboard!
</span>
{/if}
<a
href={`https://huggingface.co/spaces/${component.id}`}
target="_blank"
class="rounded-md w-fit px-3.5 py-1 text-sm font-semibold text-white bg-orange-300 hover:drop-shadow-sm"
>
Go to Space <span aria-hidden="true">→</span>
</a>
</div>
<iframe
src={`https://${component.subdomain}.hf.space?__theme=light`}
height="100%"
width="100%"
></iframe>
</div>
{/each}
<style>
.close-button {
position: absolute;
top: 0;
right: 5;
width: var(--size-1);
color: var(--body-text-color);
}
.details-panel {
position: fixed;
top: 5%;
right: 5%;
height: 90%;
width: 90%;
z-index: 1000;
flex-direction: column;
}
.description {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
</style>