Skip to content
This repository was archived by the owner on Jan 9, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 36 additions & 30 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,46 @@ import { Glossary } from "./components/Glossary";
import { Samples } from "./components/Samples";
import { Metrics } from "./components/Metrics";
import { BASE } from "./site";
import { MetricsProvider } from "./context/Metrics";
import { SampleManifestProvider } from "./context/SampleManifest";

function App() {
return (
<BrowserRouter basename={BASE}>
<div id="header">
<img src={BASE + "/granitecode.svg"} id="headerLogo" />
<div id="headerTitle">granite-completebench</div>
<NavLink className="headerLink" to="/">
About
</NavLink>
<NavLink className="headerLink" to="/metrics">
Metrics
</NavLink>
<NavLink className="headerLink" to="/samples">
Samples
</NavLink>
<NavLink className="headerLink" to="/glossary">
Glossary
</NavLink>
<a
className="headerLink"
href="https://github.com/Granite-Code/granite-completebench"
>
GitHub
</a>
</div>
<div id="main">
<Routes>
<Route path="/" element={<About />} />
<Route path="/metrics" element={<Metrics />} />
<Route path="/samples" element={<Samples />} />
<Route path="/glossary" element={<Glossary />} />
</Routes>
</div>
<MetricsProvider>
<SampleManifestProvider>
<div id="header">
<img src={BASE + "/granitecode.svg"} id="headerLogo" />
<div id="headerTitle">granite-completebench</div>
<NavLink className="headerLink" to="/">
About
</NavLink>
<NavLink className="headerLink" to="/metrics">
Metrics
</NavLink>
<NavLink className="headerLink" to="/samples">
Samples
</NavLink>
<NavLink className="headerLink" to="/glossary">
Glossary
</NavLink>
<a
className="headerLink"
href="https://github.com/Granite-Code/granite-completebench"
>
GitHub
</a>
</div>
<div id="main">
<Routes>
<Route path="/" element={<About />} />
<Route path="/metrics" element={<Metrics />} />
<Route path="/samples" element={<Samples />} />
<Route path="/glossary" element={<Glossary />} />
</Routes>
</div>
</SampleManifestProvider>
</MetricsProvider>
</BrowserRouter>
);
}
Expand Down
23 changes: 5 additions & 18 deletions web/src/components/Metrics.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useEffect, useState } from "react";
import { MetricsStore, fetchMetrics } from "../utils/fetchMetrics";
import { MetricsTable } from "./MetricsTable";
import { useEffect } from "react";
import { useSearchParams } from "react-router";
import { MetricsTable } from "./MetricsTable";
import { useMetrics } from "../context/Metrics";

function validatedPostProcessor(
searchParams: URLSearchParams,
store: MetricsStore,
store: ReturnType<typeof useMetrics>,
) {
if (store.postprocessors.length == 0) {
return "";
Expand All @@ -24,20 +24,7 @@ function validatedPostProcessor(

export function Metrics() {
const [searchParams, setSearchParams] = useSearchParams();
const [store, setStore] = useState<MetricsStore>(new MetricsStore());

useEffect(() => {
async function loadMetrics() {
try {
const newStore = await fetchMetrics();
setStore(newStore);
} catch (error) {
console.log("Error loading metrics", error);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add an error?: string field to MetricsStore and Manifest so that the web page can display an error rather than just being stuck in a loading state.

Copy link
Contributor Author

@owtaylor owtaylor Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And please make the pages display the errors rather than content if the error fields are set

}
}

loadMetrics();
}, []);
const store = useMetrics();

useEffect(() => {
if (store.postprocessors.length == 0) {
Expand Down
58 changes: 7 additions & 51 deletions web/src/components/Samples.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
import { useEffect, useState } from "react";
import { useState, useEffect } from "react";
import { useSearchParams } from "react-router";
import SampleDisplay from "./SampleDisplay";
import { Dropdown } from "./Dropdown";
import { fetchSamples } from "../utils/fetchSamples";
import { BASE } from "../site";
import type { Sample } from "../types";
import SampleSelector from "./SampleSelector";

interface Manifest {
models: string[];
languages: string[];
templates: string[];
postprocessors: string[];
}

// Default empty options to use before loading
const emptyOptions: string[] = [];
import { useSampleManifest } from "../context/SampleManifest";

function validateSearchParams(
searchParams: URLSearchParams,
manifest: Manifest,
manifest: ReturnType<typeof useSampleManifest>,
) {
let newSearchParams: URLSearchParams | undefined;

Expand All @@ -39,49 +29,19 @@ function validateSearchParams(

export function Samples() {
const [searchParams, setSearchParams] = useSearchParams();
const [manifest, setManifest] = useState<Manifest>({
models: emptyOptions,
languages: emptyOptions,
templates: emptyOptions,
postprocessors: emptyOptions,
});
const [isLoading, setIsLoading] = useState(true);
const [isInitialized, setIsInitialized] = useState(false);
const [error, setError] = useState<string | null>(null);
const [samples, setSamples] = useState<Sample[]>([]);
const [current, setCurrent] = useState<number | undefined>();

// Load manifest.json
useEffect(() => {
async function loadManifest() {
try {
const response = await fetch(BASE + "/samples/manifest.json");

if (!response.ok) {
throw new Error(`Failed to load manifest: ${response.statusText}`);
}

const data = await response.json();
setManifest(data);
setIsLoading(false);
} catch (err) {
console.error("Error loading manifest:", err);
setError(err instanceof Error ? err.message : "Failed to load options");
setIsLoading(false);
}
}

loadManifest();
}, []);
const manifest = useSampleManifest();

useEffect(() => {
if (isLoading || isInitialized) return;
if (!manifest || isInitialized) return;

setSearchParams(validateSearchParams(searchParams, manifest), {
replace: true,
});
setIsInitialized(true);
}, [isLoading, isInitialized, manifest, searchParams, setSearchParams]);
}, [manifest, isInitialized, searchParams, setSearchParams]);

useEffect(() => {
if (!isInitialized) return;
Expand Down Expand Up @@ -115,11 +75,7 @@ export function Samples() {
}, [isInitialized, searchParams, current, setCurrent, samples, manifest]);

if (!isInitialized || samples.length == 0 || current === undefined) {
return <div>Loading options...</div>;
}

if (error) {
return <div>Error: {error}</div>;
return <div>Loading samples...</div>;
}

return (
Expand Down
33 changes: 29 additions & 4 deletions web/src/utils/fetchMetrics.ts → web/src/context/Metrics.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createContext, useContext, useEffect, useState } from "react";
import { BASE } from "../site";

export const METRIC_DESCRIPTIONS: { [K in MetricName]: string } = {
Expand Down Expand Up @@ -105,8 +106,32 @@ export class MetricsStore {
}
}

export async function fetchMetrics() {
const store = new MetricsStore();
await store.load();
return store;
const MetricsContext = createContext<MetricsStore | undefined>(undefined);

export function MetricsProvider({ children }: { children: React.ReactNode }) {
const [metrics, setMetrics] = useState<MetricsStore>();

useEffect(() => {
const loadMetrics = async () => {
const store = new MetricsStore();
await store.load();
setMetrics(store);
};

loadMetrics();
}, []);

return (
<MetricsContext.Provider value={metrics}>
{children}
</MetricsContext.Provider>
);
}

export function useMetrics() {
const context = useContext(MetricsContext);
if (context === undefined) {
throw new Error("useMetrics must be used within a MetricsProvider");
}
return context;
}
68 changes: 68 additions & 0 deletions web/src/context/SampleManifest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { createContext, useContext, useEffect, useState } from "react";
import { BASE } from "../site";

interface Manifest {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename this to SampleManifest

models: string[];
languages: string[];
templates: string[];
postprocessors: string[];
}

const emptyManifest: Manifest = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no point to this since useSampleManifest() returns Manifest | undefined - we can just use undefined.

models: [],
languages: [],
templates: [],
postprocessors: [],
};

const SampleManifestContext = createContext<Manifest | undefined>(undefined);

export function SampleManifestProvider({
children,
}: {
children: React.ReactNode;
}) {
const [manifest, setManifest] = useState<Manifest>(emptyManifest);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
async function loadManifest() {
try {
const response = await fetch(BASE + "/samples/manifest.json");

if (!response.ok) {
throw new Error(`Failed to load manifest: ${response.statusText}`);
}

const data = await response.json();
setManifest(data);
} catch (err) {
console.error("Error loading manifest:", err);
setError(err instanceof Error ? err.message : "Failed to load options");
}
}

loadManifest();
}, []);

if (error) {
// You might want to handle this differently depending on your needs
console.error(error);
}

return (
<SampleManifestContext.Provider value={manifest}>
{children}
</SampleManifestContext.Provider>
);
}

export function useSampleManifest() {
const context = useContext(SampleManifestContext);
if (context === undefined) {
throw new Error(
"useSampleManifest must be used within a SampleManifestProvider",
);
}
return context;
}