Commit
·
5679415
1
Parent(s):
160a21b
support alternative display names
Browse files- src/routes/+page.svelte +2 -3
- src/routes/Leaderboard.svelte +11 -3
- src/routes/ModelDetails.svelte +9 -12
- src/routes/Viewer.svelte +11 -3
- src/routes/Vote.svelte +11 -2
- src/routes/utils/Config.ts +6 -0
- src/routes/utils/getConfig.ts +8 -0
src/routes/+page.svelte
CHANGED
|
@@ -12,14 +12,14 @@
|
|
| 12 |
}
|
| 13 |
|
| 14 |
let currentView: "Leaderboard" | "Vote" | "ModelDetails" | "Viewer" | "About" = "Vote";
|
| 15 |
-
let selectedEntry: { name: string
|
| 16 |
let selectedScene: Scene | null = null;
|
| 17 |
|
| 18 |
function goHome() {
|
| 19 |
window.location.href = "/";
|
| 20 |
}
|
| 21 |
|
| 22 |
-
function showModelDetails(entry: { name: string
|
| 23 |
selectedEntry = entry;
|
| 24 |
currentView = "ModelDetails";
|
| 25 |
}
|
|
@@ -55,7 +55,6 @@
|
|
| 55 |
{:else if currentView === "ModelDetails" && selectedEntry}
|
| 56 |
<ModelDetails
|
| 57 |
modelName={selectedEntry.name}
|
| 58 |
-
modelPath={selectedEntry.path}
|
| 59 |
onBack={() => (currentView = "Leaderboard")}
|
| 60 |
onSceneClick={showScene}
|
| 61 |
/>
|
|
|
|
| 12 |
}
|
| 13 |
|
| 14 |
let currentView: "Leaderboard" | "Vote" | "ModelDetails" | "Viewer" | "About" = "Vote";
|
| 15 |
+
let selectedEntry: { name: string } | null = null;
|
| 16 |
let selectedScene: Scene | null = null;
|
| 17 |
|
| 18 |
function goHome() {
|
| 19 |
window.location.href = "/";
|
| 20 |
}
|
| 21 |
|
| 22 |
+
function showModelDetails(entry: { name: string }) {
|
| 23 |
selectedEntry = entry;
|
| 24 |
currentView = "ModelDetails";
|
| 25 |
}
|
|
|
|
| 55 |
{:else if currentView === "ModelDetails" && selectedEntry}
|
| 56 |
<ModelDetails
|
| 57 |
modelName={selectedEntry.name}
|
|
|
|
| 58 |
onBack={() => (currentView = "Leaderboard")}
|
| 59 |
onSceneClick={showScene}
|
| 60 |
/>
|
src/routes/Leaderboard.svelte
CHANGED
|
@@ -1,12 +1,14 @@
|
|
| 1 |
<script lang="ts">
|
| 2 |
import { onMount } from "svelte";
|
| 3 |
import { ProgressBarRound } from "carbon-icons-svelte";
|
|
|
|
| 4 |
|
| 5 |
interface Entry {
|
| 6 |
name: string;
|
| 7 |
rank: number;
|
| 8 |
score: number;
|
| 9 |
votes: number;
|
|
|
|
| 10 |
}
|
| 11 |
|
| 12 |
export let onEntryClick: (entry: Entry) => void;
|
|
@@ -23,9 +25,15 @@
|
|
| 23 |
},
|
| 24 |
});
|
| 25 |
const data = (await response.json()) as Entry[];
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
-
leaderboard =
|
| 29 |
};
|
| 30 |
|
| 31 |
onMount(async () => {
|
|
@@ -39,7 +47,7 @@
|
|
| 39 |
<button class="grid-item" on:click={() => onEntryClick(entry)}>
|
| 40 |
<img src={`${baseUrl}/${entry.name}/thumbnail.png`} alt={entry.name} class="thumbnail" />
|
| 41 |
<div class="ranking">{entry.rank}</div>
|
| 42 |
-
<div class="title">{entry.
|
| 43 |
<div class="score-container">
|
| 44 |
<div class="score">
|
| 45 |
<span class="label">Score:</span>
|
|
|
|
| 1 |
<script lang="ts">
|
| 2 |
import { onMount } from "svelte";
|
| 3 |
import { ProgressBarRound } from "carbon-icons-svelte";
|
| 4 |
+
import { getConfig } from "./utils/getConfig";
|
| 5 |
|
| 6 |
interface Entry {
|
| 7 |
name: string;
|
| 8 |
rank: number;
|
| 9 |
score: number;
|
| 10 |
votes: number;
|
| 11 |
+
displayName?: string;
|
| 12 |
}
|
| 13 |
|
| 14 |
export let onEntryClick: (entry: Entry) => void;
|
|
|
|
| 25 |
},
|
| 26 |
});
|
| 27 |
const data = (await response.json()) as Entry[];
|
| 28 |
+
const entriesWithDisplayNames = await Promise.all(
|
| 29 |
+
data.map(async (entry) => {
|
| 30 |
+
const config = await getConfig(entry.name);
|
| 31 |
+
return { ...entry, displayName: config.DisplayName || entry.name };
|
| 32 |
+
}),
|
| 33 |
+
);
|
| 34 |
+
entriesWithDisplayNames.sort((a, b) => a.rank - b.rank);
|
| 35 |
|
| 36 |
+
leaderboard = entriesWithDisplayNames;
|
| 37 |
};
|
| 38 |
|
| 39 |
onMount(async () => {
|
|
|
|
| 47 |
<button class="grid-item" on:click={() => onEntryClick(entry)}>
|
| 48 |
<img src={`${baseUrl}/${entry.name}/thumbnail.png`} alt={entry.name} class="thumbnail" />
|
| 49 |
<div class="ranking">{entry.rank}</div>
|
| 50 |
+
<div class="title">{entry.displayName}</div>
|
| 51 |
<div class="score-container">
|
| 52 |
<div class="score">
|
| 53 |
<span class="label">Score:</span>
|
src/routes/ModelDetails.svelte
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
<script lang="ts">
|
| 2 |
import { onMount } from "svelte";
|
| 3 |
import { ProgressBarRound, ArrowLeft } from "carbon-icons-svelte";
|
|
|
|
|
|
|
| 4 |
|
| 5 |
interface Scene {
|
| 6 |
name: string;
|
|
@@ -8,18 +10,13 @@
|
|
| 8 |
thumbnail: string;
|
| 9 |
}
|
| 10 |
|
| 11 |
-
interface Config {
|
| 12 |
-
Model: string;
|
| 13 |
-
Space: string;
|
| 14 |
-
Paper: string;
|
| 15 |
-
}
|
| 16 |
-
|
| 17 |
export let modelName: string;
|
| 18 |
export let onBack: () => void;
|
| 19 |
export let onSceneClick: (scene: Scene) => void;
|
| 20 |
|
| 21 |
let scenes: Scene[] = [];
|
| 22 |
let config: Config;
|
|
|
|
| 23 |
|
| 24 |
async function fetchScenes() {
|
| 25 |
scenes = [];
|
|
@@ -44,14 +41,14 @@
|
|
| 44 |
return acc;
|
| 45 |
}, []);
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
config = (await configResponse.json()) as Config;
|
| 50 |
|
| 51 |
scenes = [...scenes];
|
| 52 |
}
|
| 53 |
|
| 54 |
-
function isValidUrl(url: string): boolean {
|
|
|
|
| 55 |
try {
|
| 56 |
new URL(url);
|
| 57 |
return true;
|
|
@@ -69,7 +66,7 @@
|
|
| 69 |
</div>
|
| 70 |
<div class="spacer" />
|
| 71 |
<button class="title-button" on:click={fetchScenes}>
|
| 72 |
-
<h2 class="muted">{
|
| 73 |
</button>
|
| 74 |
<div class="desktop-spacer" />
|
| 75 |
</div>
|
|
@@ -99,7 +96,7 @@
|
|
| 99 |
<a class="muted" href={config.Paper} target="_blank">
|
| 100 |
{config.Paper.replace("https://huggingface.co/papers/", "").replace(
|
| 101 |
"https://arxiv.org/abs/",
|
| 102 |
-
""
|
| 103 |
)}
|
| 104 |
</a>
|
| 105 |
</div>
|
|
|
|
| 1 |
<script lang="ts">
|
| 2 |
import { onMount } from "svelte";
|
| 3 |
import { ProgressBarRound, ArrowLeft } from "carbon-icons-svelte";
|
| 4 |
+
import { getConfig } from "./utils/getConfig";
|
| 5 |
+
import type { Config } from "./utils/Config";
|
| 6 |
|
| 7 |
interface Scene {
|
| 8 |
name: string;
|
|
|
|
| 10 |
thumbnail: string;
|
| 11 |
}
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
export let modelName: string;
|
| 14 |
export let onBack: () => void;
|
| 15 |
export let onSceneClick: (scene: Scene) => void;
|
| 16 |
|
| 17 |
let scenes: Scene[] = [];
|
| 18 |
let config: Config;
|
| 19 |
+
let displayName: string;
|
| 20 |
|
| 21 |
async function fetchScenes() {
|
| 22 |
scenes = [];
|
|
|
|
| 41 |
return acc;
|
| 42 |
}, []);
|
| 43 |
|
| 44 |
+
config = await getConfig(modelName);
|
| 45 |
+
displayName = config.DisplayName || modelName;
|
|
|
|
| 46 |
|
| 47 |
scenes = [...scenes];
|
| 48 |
}
|
| 49 |
|
| 50 |
+
function isValidUrl(url: string | undefined): boolean {
|
| 51 |
+
if (!url) return false;
|
| 52 |
try {
|
| 53 |
new URL(url);
|
| 54 |
return true;
|
|
|
|
| 66 |
</div>
|
| 67 |
<div class="spacer" />
|
| 68 |
<button class="title-button" on:click={fetchScenes}>
|
| 69 |
+
<h2 class="muted">{displayName || "Loading..."}</h2>
|
| 70 |
</button>
|
| 71 |
<div class="desktop-spacer" />
|
| 72 |
</div>
|
|
|
|
| 96 |
<a class="muted" href={config.Paper} target="_blank">
|
| 97 |
{config.Paper.replace("https://huggingface.co/papers/", "").replace(
|
| 98 |
"https://arxiv.org/abs/",
|
| 99 |
+
"",
|
| 100 |
)}
|
| 101 |
</a>
|
| 102 |
</div>
|
src/routes/Viewer.svelte
CHANGED
|
@@ -3,6 +3,8 @@
|
|
| 3 |
import type { IViewer } from "./viewers/IViewer";
|
| 4 |
import { createViewer } from "./viewers/ViewerFactory";
|
| 5 |
import { ArrowLeft, Cube, WatsonHealth3DPrintMesh } from "carbon-icons-svelte";
|
|
|
|
|
|
|
| 6 |
|
| 7 |
interface Scene {
|
| 8 |
name: string;
|
|
@@ -20,9 +22,11 @@
|
|
| 20 |
let loadingBarFill: HTMLDivElement;
|
| 21 |
|
| 22 |
let viewer: IViewer;
|
|
|
|
| 23 |
|
| 24 |
async function loadScene() {
|
| 25 |
overlay.style.display = "flex";
|
|
|
|
| 26 |
viewer = await createViewer(scene.url, canvas, (progress) => {
|
| 27 |
loadingBarFill.style.width = `${progress * 100}%`;
|
| 28 |
});
|
|
@@ -78,9 +82,13 @@
|
|
| 78 |
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
| 79 |
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
| 80 |
<h2>
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
</h2>
|
| 85 |
</button>
|
| 86 |
<div class="desktop-spacer" />
|
|
|
|
| 3 |
import type { IViewer } from "./viewers/IViewer";
|
| 4 |
import { createViewer } from "./viewers/ViewerFactory";
|
| 5 |
import { ArrowLeft, Cube, WatsonHealth3DPrintMesh } from "carbon-icons-svelte";
|
| 6 |
+
import type { Config } from "./utils/Config";
|
| 7 |
+
import { getConfig } from "./utils/getConfig";
|
| 8 |
|
| 9 |
interface Scene {
|
| 10 |
name: string;
|
|
|
|
| 22 |
let loadingBarFill: HTMLDivElement;
|
| 23 |
|
| 24 |
let viewer: IViewer;
|
| 25 |
+
let displayName: string;
|
| 26 |
|
| 27 |
async function loadScene() {
|
| 28 |
overlay.style.display = "flex";
|
| 29 |
+
displayName = (await getConfig(modelName)).DisplayName || modelName;
|
| 30 |
viewer = await createViewer(scene.url, canvas, (progress) => {
|
| 31 |
loadingBarFill.style.width = `${progress * 100}%`;
|
| 32 |
});
|
|
|
|
| 82 |
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
| 83 |
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
| 84 |
<h2>
|
| 85 |
+
{#if displayName}
|
| 86 |
+
<span class="muted" on:click={onBack}>{displayName}/</span>{scene.name.length > 16
|
| 87 |
+
? `${scene.name.slice(0, 16)}...`
|
| 88 |
+
: scene.name}
|
| 89 |
+
{:else}
|
| 90 |
+
Loading...
|
| 91 |
+
{/if}
|
| 92 |
</h2>
|
| 93 |
</button>
|
| 94 |
<div class="desktop-spacer" />
|
src/routes/Vote.svelte
CHANGED
|
@@ -3,14 +3,17 @@
|
|
| 3 |
import type { IViewer } from "./viewers/IViewer";
|
| 4 |
import { createViewer } from "./viewers/ViewerFactory";
|
| 5 |
import { Cube, WatsonHealth3DPrintMesh } from "carbon-icons-svelte";
|
|
|
|
| 6 |
|
| 7 |
interface Data {
|
| 8 |
input: string;
|
| 9 |
input_path: string;
|
| 10 |
model1: string;
|
| 11 |
model1_path: string;
|
|
|
|
| 12 |
model2: string;
|
| 13 |
model2_path: string;
|
|
|
|
| 14 |
}
|
| 15 |
|
| 16 |
let viewerA: IViewer;
|
|
@@ -78,6 +81,12 @@
|
|
| 78 |
const model1_path = `${baseUrl}${data.model1_path}`;
|
| 79 |
const model2_path = `${baseUrl}${data.model2_path}`;
|
| 80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
try {
|
| 82 |
[viewerA, viewerB] = await Promise.all([
|
| 83 |
createViewer(model1_path, canvasA, (progress) => {
|
|
@@ -214,7 +223,7 @@
|
|
| 214 |
<div bind:this={loadingBarFillA} class="loading-bar-fill" />
|
| 215 |
</div>
|
| 216 |
</div>
|
| 217 |
-
<div bind:this={voteOverlayA} class="vote-overlay">{data.
|
| 218 |
<canvas bind:this={canvasA} class="viewer-canvas" id="canvas1"> </canvas>
|
| 219 |
<div class="stats">
|
| 220 |
{#if viewerA}
|
|
@@ -253,7 +262,7 @@
|
|
| 253 |
<div bind:this={loadingBarFillB} class="loading-bar-fill" />
|
| 254 |
</div>
|
| 255 |
</div>
|
| 256 |
-
<div bind:this={voteOverlayB} class="vote-overlay">{data.
|
| 257 |
<canvas bind:this={canvasB} class="viewer-canvas" id="canvas2"></canvas>
|
| 258 |
<div class="stats">
|
| 259 |
{#if viewerB}
|
|
|
|
| 3 |
import type { IViewer } from "./viewers/IViewer";
|
| 4 |
import { createViewer } from "./viewers/ViewerFactory";
|
| 5 |
import { Cube, WatsonHealth3DPrintMesh } from "carbon-icons-svelte";
|
| 6 |
+
import { getConfig } from "./utils/getConfig";
|
| 7 |
|
| 8 |
interface Data {
|
| 9 |
input: string;
|
| 10 |
input_path: string;
|
| 11 |
model1: string;
|
| 12 |
model1_path: string;
|
| 13 |
+
model1_displayName: string;
|
| 14 |
model2: string;
|
| 15 |
model2_path: string;
|
| 16 |
+
model2_displayName: string;
|
| 17 |
}
|
| 18 |
|
| 19 |
let viewerA: IViewer;
|
|
|
|
| 81 |
const model1_path = `${baseUrl}${data.model1_path}`;
|
| 82 |
const model2_path = `${baseUrl}${data.model2_path}`;
|
| 83 |
|
| 84 |
+
const config1 = await getConfig(data.model1);
|
| 85 |
+
const config2 = await getConfig(data.model2);
|
| 86 |
+
|
| 87 |
+
data.model1_displayName = config1.DisplayName || data.model1;
|
| 88 |
+
data.model2_displayName = config2.DisplayName || data.model2;
|
| 89 |
+
|
| 90 |
try {
|
| 91 |
[viewerA, viewerB] = await Promise.all([
|
| 92 |
createViewer(model1_path, canvasA, (progress) => {
|
|
|
|
| 223 |
<div bind:this={loadingBarFillA} class="loading-bar-fill" />
|
| 224 |
</div>
|
| 225 |
</div>
|
| 226 |
+
<div bind:this={voteOverlayA} class="vote-overlay">{data.model1_displayName}</div>
|
| 227 |
<canvas bind:this={canvasA} class="viewer-canvas" id="canvas1"> </canvas>
|
| 228 |
<div class="stats">
|
| 229 |
{#if viewerA}
|
|
|
|
| 262 |
<div bind:this={loadingBarFillB} class="loading-bar-fill" />
|
| 263 |
</div>
|
| 264 |
</div>
|
| 265 |
+
<div bind:this={voteOverlayB} class="vote-overlay">{data.model2_displayName}</div>
|
| 266 |
<canvas bind:this={canvasB} class="viewer-canvas" id="canvas2"></canvas>
|
| 267 |
<div class="stats">
|
| 268 |
{#if viewerB}
|
src/routes/utils/Config.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export interface Config {
|
| 2 |
+
Model?: string;
|
| 3 |
+
Space?: string;
|
| 4 |
+
Paper?: string;
|
| 5 |
+
DisplayName?: string;
|
| 6 |
+
}
|
src/routes/utils/getConfig.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { Config } from "./Config";
|
| 2 |
+
|
| 3 |
+
export async function getConfig(modelName: string): Promise<Config> {
|
| 4 |
+
const configUrl = `https://huggingface.co/datasets/dylanebert/3d-arena/resolve/main/outputs/${modelName}/config.json`;
|
| 5 |
+
const configResponse = await fetch(configUrl);
|
| 6 |
+
const config = await configResponse.json();
|
| 7 |
+
return config;
|
| 8 |
+
}
|