| <script lang="ts"> | |
| import { onMount } from "svelte"; | |
| import { ProgressBarRound, ArrowLeft } from "carbon-icons-svelte"; | |
| import { getConfig } from "./utils/getConfig"; | |
| import type { Config } from "./utils/Config"; | |
| interface Scene { | |
| name: string; | |
| url: string; | |
| thumbnail: string; | |
| } | |
| export let modelName: string; | |
| export let onBack: () => void; | |
| export let onSceneClick: (scene: Scene) => void; | |
| let scenes: Scene[] = []; | |
| let config: Config; | |
| let displayName: string; | |
| async function fetchScenes() { | |
| scenes = []; | |
| const url = `https://huggingface.co/api/datasets/dylanebert/3d-arena`; | |
| const response = await fetch(url); | |
| const responseData = await response.json(); | |
| const directory = `outputs/${modelName}`; | |
| const extensions = ["obj", "glb", "ply", "splat"]; | |
| scenes = responseData.siblings | |
| .filter((scene: any) => { | |
| const fileExtension = scene.rfilename.split(".").pop(); | |
| return scene.rfilename.startsWith(directory) && extensions.includes(fileExtension); | |
| }) | |
| .reduce((acc: Scene[], scene: any) => { | |
| const name = scene.rfilename.split("/").pop().split(".").slice(0, -1).join("."); | |
| const url = `https://huggingface.co/datasets/dylanebert/3d-arena/resolve/main/${scene.rfilename}`; | |
| const thumbnail = url.replace(/\.[^.]+$/, ".png"); | |
| acc.push({ name, url, thumbnail }); | |
| return acc; | |
| }, []); | |
| config = await getConfig(modelName); | |
| displayName = config.DisplayName || modelName; | |
| scenes = [...scenes]; | |
| } | |
| function isValidUrl(url: string | undefined): boolean { | |
| if (!url) return false; | |
| try { | |
| new URL(url); | |
| return true; | |
| } catch (error) { | |
| return false; | |
| } | |
| } | |
| onMount(fetchScenes); | |
| </script> | |
| <div class="header"> | |
| <div class="back" aria-label="Back" aria-hidden="true" on:click={onBack}> | |
| <ArrowLeft size={24} /> | |
| </div> | |
| <div class="spacer" /> | |
| <button class="title-button" on:click={fetchScenes}> | |
| <h2 class="muted">{displayName || "Loading..."}</h2> | |
| </button> | |
| <div class="desktop-spacer" /> | |
| </div> | |
| <div class="model-details"> | |
| {#if config && (isValidUrl(config.Model) || isValidUrl(config.Space) || isValidUrl(config.Paper))} | |
| <div class="config-container"> | |
| {#if config.Model && isValidUrl(config.Model)} | |
| <div class="config-item"> | |
| <span class="config-label">Model:</span> | |
| <a class="muted" href={config.Model} target="_blank"> | |
| {config.Model.replace("https://huggingface.co/", "")} | |
| </a> | |
| </div> | |
| {/if} | |
| {#if config.Space && isValidUrl(config.Space)} | |
| <div class="config-item"> | |
| <span class="config-label">Space:</span> | |
| <a class="muted" href={config.Space} target="_blank"> | |
| {config.Space.replace("https://huggingface.co/spaces/", "")} | |
| </a> | |
| </div> | |
| {/if} | |
| {#if config.Paper && isValidUrl(config.Paper)} | |
| <div class="config-item"> | |
| <span class="config-label">Paper:</span> | |
| <a class="muted" href={config.Paper} target="_blank"> | |
| {config.Paper.replace("https://huggingface.co/papers/", "").replace( | |
| "https://arxiv.org/abs/", | |
| "", | |
| )} | |
| </a> | |
| </div> | |
| {/if} | |
| </div> | |
| {:else} | |
| <div class="config-container"> | |
| <div class="config-item"> | |
| <span class="config-label">Model:</span> | |
| <span class="muted">Anonymous</span> | |
| </div> | |
| </div> | |
| {/if} | |
| {#if scenes.length > 0} | |
| <div class="grid"> | |
| {#each scenes as scene} | |
| <button class="grid-item" on:click={() => onSceneClick(scene)}> | |
| <img loading="lazy" src={scene.thumbnail} alt={scene.name} class="thumbnail" /> | |
| <div class="title">{scene.name.length > 16 ? `${scene.name.slice(0, 16)}...` : scene.name}</div> | |
| </button> | |
| {/each} | |
| </div> | |
| {:else} | |
| <div class="loading-container"> | |
| <ProgressBarRound class="loading-icon" /> | |
| <div class="loading-text">Loading...</div> | |
| </div> | |
| {/if} | |
| </div> | |