joeddav's picture
Add WIP disclaimer banner
133beb7
import { useEffect, useMemo, useState } from 'react'
import './App.css'
import { ClusterMap } from './components/ClusterMap'
import { ControlsPanel } from './components/ControlsPanel'
import { analyzeCluster } from './lib/trainingClusterModel'
import { getScenarioConfig, getViewOptions } from './lib/viewOptions'
import { buildWorkbenchViewModel } from './lib/workbenchPresenter'
import { type WorkbenchConfig } from './lib/workbench'
function App() {
const viewOptions = getViewOptions()
const [config, setConfig] = useState<WorkbenchConfig>(() =>
getScenarioConfig(viewOptions.scenario),
)
const [expandedView, setExpandedView] = useState<'cluster' | null>(null)
const analysis = useMemo(
() => analyzeCluster(config.model, config.training, config.cluster, config.parallelism),
[config],
)
const viewModel = useMemo(
() => buildWorkbenchViewModel(config, analysis),
[analysis, config],
)
useEffect(() => {
if (!expandedView) {
return undefined
}
const previousOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
setExpandedView(null)
}
}
window.addEventListener('keydown', handleKeyDown)
return () => {
document.body.style.overflow = previousOverflow
window.removeEventListener('keydown', handleKeyDown)
}
}, [expandedView])
const handleConfigChange = (nextConfig: WorkbenchConfig) => {
setConfig(nextConfig)
}
const handleReset = () => {
setConfig(getScenarioConfig(viewOptions.scenario))
}
const clusterView = (
<section className="map-panel">
<div className="topology-header">
<div>
<p className="mini-label">Live cluster topology</p>
<h2>GPU fabric map</h2>
</div>
<div className="topology-header-actions">
<button
type="button"
className="scene-button"
onClick={() => setExpandedView('cluster')}
>
open full screen
</button>
</div>
</div>
<ClusterMap
viewModel={viewModel}
debugEnabled={viewOptions.debug}
snapshotMode={viewOptions.snapshot}
linkedFocus={null}
/>
</section>
)
return (
<div className="workbench-shell">
<header className="app-topbar">
<div className="title-block">
<p className="mini-label">Illustrated training cluster</p>
<h1>[WIP] Parallelism workbench</h1>
<p className="title-copy">{viewModel.subheadline}</p>
</div>
<div className="status-banner status-banner-wip" data-testid="wip-banner">
<strong>Work in progress</strong>
<span>This demo may contain bugs, rough edges, and logical errors in the training model.</span>
</div>
{!analysis.feasible ? (
<div className="status-banner status-banner-danger" data-testid="infeasible-banner">
<strong>Infeasible configuration</strong>
<span>{analysis.infeasibilityReason}</span>
</div>
) : null}
<section className="summary-strip" aria-label="simulation summary">
<div className="summary-card summary-card-wide">
<span>Scenario</span>
<strong>{viewModel.headline}</strong>
<p>
{config.cluster.numNodes} {config.cluster.nodeLabel ?? 'nodes'} 路 {config.cluster.gpuType.name}
{' 路 '}
{config.model.numLayers} layers 路 hidden {config.model.hiddenDim.toLocaleString()}
</p>
</div>
<div className="summary-card">
<span>Throughput</span>
<strong>{viewModel.summary.throughputLabel}</strong>
<p>{viewModel.summary.throughputNote}</p>
</div>
<div className="summary-card">
<span>Active GPUs</span>
<strong>{viewModel.summary.gpuLabel}</strong>
<p>{viewModel.summary.gpuNote}</p>
</div>
<div className="summary-card">
<span>Interconnect</span>
<strong>{viewModel.summary.interconnectLabel}</strong>
<p>{viewModel.summary.interconnectNote}</p>
</div>
<div className="summary-card">
<span>Bottleneck</span>
<strong>{viewModel.summary.bottleneckLabel}</strong>
<p>{viewModel.summary.bottleneckNote}</p>
</div>
</section>
</header>
<ControlsPanel
config={config}
onChange={handleConfigChange}
onReset={handleReset}
viewModel={viewModel}
/>
<main className="analysis-stack">
{expandedView !== 'cluster' ? clusterView : null}
<section className="side-card">
<div className="side-header">
<p className="mini-label">Run breakdown</p>
<h3>{config.cluster.gpuType.name}</h3>
</div>
<div className="facts-grid">
{viewModel.facts.map((fact) => (
<div key={fact.label} className="fact-row">
<span>{fact.label}</span>
<strong>{fact.value}</strong>
</div>
))}
</div>
<div className="warning-list" aria-live="polite">
{viewModel.warnings.map((warning) => (
<div key={warning} className="warning-pill">
{warning}
</div>
))}
</div>
</section>
</main>
{expandedView ? (
<div
className="fullscreen-overlay"
role="dialog"
aria-modal="true"
onClick={(event) => {
if (event.target === event.currentTarget) {
setExpandedView(null)
}
}}
>
<div className="fullscreen-shell">
<div className="fullscreen-toolbar">
<div>
<p className="mini-label">Expanded view</p>
<h2>GPU fabric map</h2>
</div>
<button
type="button"
className="scene-button"
onClick={() => setExpandedView(null)}
>
close full screen
</button>
</div>
<div className="fullscreen-content">
{clusterView}
</div>
</div>
</div>
) : null}
</div>
)
}
export default App