|
<script lang="ts"> |
|
import { onMount } from "svelte"; |
|
import { ProgressBarRound, ArrowLeft } from "carbon-icons-svelte"; |
|
|
|
interface Scene { |
|
name: string; |
|
url: string; |
|
thumbnail: string; |
|
} |
|
|
|
interface Config { |
|
Model: string; |
|
Space: string; |
|
Paper: string; |
|
} |
|
|
|
export let modelName: string; |
|
export let onBack: () => void; |
|
export let onSceneClick: (scene: Scene) => void; |
|
|
|
let scenes: Scene[] = []; |
|
let config: Config; |
|
|
|
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; |
|
}, []); |
|
|
|
const configUrl = `https://huggingface.co/datasets/dylanebert/3d-arena/resolve/main/outputs/${modelName}/config.json`; |
|
const configResponse = await fetch(configUrl); |
|
config = (await configResponse.json()) as Config; |
|
|
|
scenes = [...scenes]; |
|
} |
|
|
|
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">{modelName}</h2> |
|
</button> |
|
<div class="desktop-spacer" /> |
|
</div> |
|
|
|
<div class="model-details"> |
|
{#if config && (config.Model || config.Space || config.Paper)} |
|
<div class="config-container"> |
|
{#if 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} |
|
<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} |
|
<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> |
|
{/if} |
|
|
|
{#if scenes.length > 0} |
|
<div class="grid"> |
|
{#each scenes as scene} |
|
<button class="grid-item" on:click={() => onSceneClick(scene)}> |
|
<img src={scene.thumbnail} alt={scene.name} class="thumbnail" /> |
|
<div class="title">{scene.name}</div> |
|
</button> |
|
{/each} |
|
</div> |
|
{:else} |
|
<div class="loading-container"> |
|
<ProgressBarRound class="loading-icon" /> |
|
<div class="loading-text">Loading...</div> |
|
</div> |
|
{/if} |
|
</div> |
|
|