lora-studio / src /routes /models /+layout.svelte
enzostvs's picture
enzostvs HF staff
try to fix metadata
608f763
<script lang="ts">
import { browser } from "$app/environment";
import InfiniteScroll from "svelte-infinite-scroll";
import { page } from "$app/stores";
import { onMount } from "svelte";
import { goto } from "$app/navigation";
import Button from "$lib/components/Button.svelte";
import Card from "$lib/components/models/Card.svelte";
import Input from "$lib/components/fields/Input.svelte";
import Radio from "$lib/components/fields/Radio.svelte";
import { MODELS_FILTER_OPTIONS } from "$lib/utils/index.js";
import GoTop from "$lib/components/GoTop.svelte";
import Drawer from "$lib/components/models/drawer/Drawer.svelte";
import type { ModelCard } from "$lib/type";
import { userStore } from "$lib/stores/use-user";
let data: {
models: ModelCard[],
total_items: number,
} = {
models: [],
total_items: 0,
}
let form: Record<string, string> = {
filter: $page.url.searchParams.get('filter') ?? "hotest",
search: $page.url.searchParams.get('search') ?? "",
base_model: $page.url.searchParams.get('base_model') ?? "",
page: "0"
}
let submitModelDialog = false;
onMount(() => {
refetch(false);
});
$: elementScroll = browser ? document?.getElementById('app') : undefined;
const handleFetchMore = async () => {
form.page = (Number(form.page) + 1).toString();
refetch(true);
}
const handleChangeFilter = async (filter: string) => {
form.filter = filter;
form.page = "0";
$page.url.searchParams.set('filter', filter);
await goto(`?${$page.url.searchParams.toString()}`);
refetch(false);
}
const handleChangeBaseModel = async (event: any) => {
const base_model = event.target.value
form.base_model = base_model;
$page.url.searchParams.set('base_model', base_model);
await goto(`?${$page.url.searchParams.toString()}`);
refetch(false);
}
let timeout: any;
const handleChangeSearch = async (search: string) => {
clearTimeout(timeout);
form.search = search;
form.page = "0";
timeout = setTimeout(async () => {
if (search === "") $page.url.searchParams.delete('search');
else $page.url.searchParams.set('search', search);
await goto(`?${$page.url.searchParams.toString()}`);
refetch(false);
}, 500);
}
const refetch = async (add: boolean) => {
const request = await fetch(`/api/models?${new URLSearchParams({...form })}`);
const response = await request.json();
if (add) data.models = [...data.models, ...response.cards] as any[];
else {
data.models = response.cards;
data.total_items = response.total_items;
}
}
</script>
<main class="px-6 py-10 lg:px-10 lg:py-12 relative">
<Drawer onSearch={handleChangeSearch} />
<h1 class="text-white font-semibold text-2xl">
Explore Models ({data?.total_items ?? 0})
</h1>
<div class="bg-cyan-500/20 max-w-xl border-2 border-cyan-500/30 w-full p-3 text-white rounded-lg mt-4">
<p class="font-semibold">FLUX LoRAs are here! ✨</p>
<p class="opacity-70 text-sm">
We are excited to announce the arrival of the first FLUX models on LoRA Studio.
</p>
</div>
<div class="flex items-start sm:items-center justify-between mt-5 flex-col sm:flex-row gap-5 sm:justify-between">
<Radio
options={[
...MODELS_FILTER_OPTIONS,
...($userStore?.is_admin ? [
{
label: "Staff only",
value: "staff_only",
icon: "lets-icons:view-hide-fill",
iconColor: "text-yellow-500"
}
] : [])
]}
value="{form.filter}"
onChange={handleChangeFilter}
/>
<div class="items-center justify-end gap-5 hidden lg:flex">
<Button href="https://huggingface.co/new/stable-diffusion-lora" target="_blank" icon="ic:round-plus" theme="dark" size="lg">Upload</Button>
<Button
icon="octicon:upload-16"
theme="blue"
target="_blank"
href="https://huggingface.co/spaces/multimodalart/civitai-to-hf"
size="lg"
>
Migrate
</Button>
</div>
<div class="items-center justify-end gap-3 flex lg:hidden">
<Button href="https://huggingface.co/new/stable-diffusion-lora" target="_blank" icon="ic:round-plus" theme="dark" size="md">Create</Button>
<Button
icon="octicon:upload-16"
theme="blue"
target="_blank"
href="https://huggingface.co/spaces/multimodalart/civitai-to-hf"
size="md"
>
Migrate
</Button>
</div>
</div>
<div class="mt-5 max-w-sm flex items-center justify-start gap-5 w-full">
<Input value={form.search} className="lg:min-w-[300px]" placeholder="Filter by model name" onChange={handleChangeSearch} />
<div class="flex flex-col items-start justify-center gap-1.5">
<p class="text-xs text-white/60 whitespace-nowrap flex items-center justify-start gap-2">
Filter by
<span class="relative">
<span class="w-2 h-2 rounded-full block bg-cyan-500"></span>
<span class="w-2 h-2 rounded-full block bg-cyan-500 animate-ping absolute top-0 left-0"></span>
</span>
</p>
<select value={form.base_model} class="text-white bg-transparent outline-none cursor-pointer" on:change={handleChangeBaseModel}>
<option value="">
All models
</option>
<option value="flux">
Flux
</option>
<option value="sd3">
Stable Diffusion 3
</option>
<option value="sdxl">
Stable Diffusion XL
</option>
<option value="sd1">
Stable Diffusion 1
</option>
</select>
</div>
</div>
<div class="mx-auto grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 2xl:grid-cols-4 gap-5 mt-8 lg:mt-10">
{#each data.models as card (card.id)}
<Card card={card} />
{/each}
{#if data.models.length === 0}
<p class="text-neutral-400 text-left w-full">No models found</p>
{/if}
<InfiniteScroll
elementScroll="{elementScroll ?? undefined}"
threshold={100}
hasMore={data.total_items > data.models.length}
on:loadMore={handleFetchMore}
/>
<GoTop />
</div>
<slot />
</main>