Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
<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> |