ArshPathan's picture
Upload folder using huggingface_hub
cd470c1
Raw History Blame Contribute Delete
20.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MaterialForge — Discovery Lab</title>
<!-- Typography -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<!-- Styling -->
<link rel="stylesheet" href="/playground/css/theme.css">
<link rel="stylesheet" href="/playground/css/components.css">
<link rel="stylesheet" href="/playground/css/playground.css">
<link rel="stylesheet" href="/playground/css/docs.css">
<!-- Charts.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<!-- Global Loading Overlay -->
<div id="loading-overlay" class="loading-overlay">
<div class="spinner"></div>
<div style="font-size: 11px; font-weight: 700; letter-spacing: 2px; color: var(--text-secondary);">IGNITING DISCOVERY ENGINE...</div>
</div>
<header>
<div class="logo">
<div class="logo-icon">Mf</div>
<div>
<div class="logo-text">MaterialForge</div>
<div class="logo-sub">OPENENV LAB v3.2</div>
</div>
</div>
<nav class="nav-tabs">
<button class="nav-tab active" data-tab="observation">Laboratory</button>
<button class="nav-tab" data-tab="analytics">Analytics</button>
<button class="nav-tab" data-tab="docs">Documentation</button>
</nav>
<div class="header-status">
<div id="status-dot" class="status-dot"></div>
<div id="status-text">DISCONNECTED</div>
</div>
</header>
<div class="playground-layout">
<!-- KPI Ribbon (GridMind Style) -->
<div class="kpi-ribbon">
<div class="kpi-item">
<div class="kpi-label">Cycle / Max</div>
<div id="kpi-step" class="kpi-value accent-blue">0 / 0</div>
</div>
<div class="kpi-item">
<div class="kpi-label">Current Reward</div>
<div id="kpi-reward" class="kpi-value accent-green">0.0000</div>
</div>
<div class="kpi-item">
<div class="kpi-label">Optimal Record</div>
<div id="kpi-best" class="kpi-value">0.0000</div>
</div>
<div class="kpi-item">
<div class="kpi-label">Lattice Order</div>
<div id="kpi-order" class="kpi-value accent-yellow">0.000</div>
</div>
<div class="kpi-item">
<div class="kpi-label">Atom Variance</div>
<div id="kpi-atoms" class="kpi-value">0</div>
</div>
<div class="kpi-item">
<div class="kpi-label">Budget Usage</div>
<div id="kpi-cost" class="kpi-value">0 / 0</div>
</div>
</div>
<!-- MAIN APP CONTAINER -->
<main style="flex: 1; display: flex; overflow: hidden;">
<!-- TAB: LABORATORY -->
<div id="tab-observation" class="tab-content active">
<div class="lab-grid">
<!-- Central Visualization Area -->
<div class="lab-main">
<!-- Lattice Canvas -->
<div class="card canvas-card">
<div class="card-header">
<div class="card-title">Atomic Lattice Grid</div>
<div id="episode-id" style="font-size: 9px; font-family: var(--mono); color: var(--text-muted);"></div>
</div>
<canvas id="lattice-canvas" width="380" height="380"></canvas>
</div>
<!-- Alignment Metrics -->
<div class="card metrics-card">
<div class="card-header">
<div class="card-title">Physical Alignment</div>
</div>
<div class="prop-list">
<!-- Hardness -->
<div class="prop-row">
<div class="prop-header"><span>HARDNESS</span><span id="pv-hardness">0 / 0</span></div>
<div class="prop-bar-container">
<div id="pt-hardness" class="prop-bar-target"></div>
<div id="pb-hardness" class="prop-bar-fill" style="background: var(--atom-A); width: 0%;"></div>
</div>
</div>
<!-- Conductivity -->
<div class="prop-row">
<div class="prop-header"><span>CONDUCTIVITY</span><span id="pv-conductivity">0 / 0</span></div>
<div class="prop-bar-container">
<div id="pt-conductivity" class="prop-bar-target"></div>
<div id="pb-conductivity" class="prop-bar-fill" style="background: var(--atom-B); width: 0%;"></div>
</div>
</div>
<!-- Thermal -->
<div class="prop-row">
<div class="prop-header"><span>THERMAL RES.</span><span id="pv-thermal_resistance">0 / 0</span></div>
<div class="prop-bar-container">
<div id="pt-thermal_resistance" class="prop-bar-target"></div>
<div id="pb-thermal_resistance" class="prop-bar-fill" style="background: var(--atom-C); width: 0%;"></div>
</div>
</div>
<!-- Elasticity -->
<div class="prop-row">
<div class="prop-header"><span>ELASTICITY</span><span id="pv-elasticity">0 / 0</span></div>
<div class="prop-bar-container">
<div id="pt-elasticity" class="prop-bar-target"></div>
<div id="pb-elasticity" class="prop-bar-fill" style="background: var(--atom-P); width: 0%;"></div>
</div>
</div>
</div>
<!-- Structural Bonuses -->
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 8px;">
<div class="control-item">
<div class="control-label">Stability: <span id="sc-stability" style="color:var(--text-primary);">0.000</span></div>
<div class="prop-bar-container" style="height:4px;"><div id="sb-stability" class="prop-bar-fill" style="background:var(--accent2); width:0%;"></div></div>
</div>
<div class="control-item">
<div class="control-label">Symmetry: <span id="sc-order" style="color:var(--text-primary);">0.000</span></div>
<div class="prop-bar-container" style="height:4px;"><div id="sb-order" class="prop-bar-fill" style="background:var(--accent3); width:0%;"></div></div>
</div>
</div>
</div>
<!-- Reward History Chart -->
<div class="card chart-card-full">
<div class="card-header">
<div class="card-title">Reward Convergence Timeline</div>
</div>
<div style="flex: 1; min-height: 0;">
<canvas id="reward-chart"></canvas>
</div>
</div>
</div>
<!-- Interaction Sidebar -->
<aside class="lab-sidebar">
<!-- Setup Panel -->
<div class="card">
<div class="card-title">Protocols</div>
<div class="control-item">
<div class="control-label">Material Scenario</div>
<select id="sel-scenario">
<option value="">Full Random Discovery</option>
<option value="diamond-like">Diamond-like (Hardness Focus)</option>
<option value="conductor">Conductor (Electricity Focus)</option>
<option value="heat-shield">Heat Shield (Thermal Focus)</option>
</select>
</div>
<button id="btn-reset" class="btn btn-primary">RE-INITIALIZE ENVIRONMENT</button>
</div>
<!-- Action Panel -->
<div class="card" style="flex: 1; display:flex; flex-direction:column;">
<div class="card-title">Intervention</div>
<div class="control-item">
<div class="control-label">Species Selection</div>
<div class="atom-selector">
<div class="atom-option selected" data-atom="A">
<div class="atom-symbol" style="color: var(--atom-A);">A</div>
<div class="atom-name">Metal</div>
</div>
<div class="atom-option" data-atom="B">
<div class="atom-symbol" style="color: var(--atom-B);">B</div>
<div class="atom-name">Conductor</div>
</div>
<div class="atom-option" data-atom="C">
<div class="atom-symbol" style="color: var(--atom-C);">C</div>
<div class="atom-name">Ceramic</div>
</div>
<div class="atom-option" data-atom="P">
<div class="atom-symbol" style="color: var(--atom-P);">P</div>
<div class="atom-name">Polymer</div>
</div>
</div>
</div>
<!-- Log Console -->
<div class="control-item" style="flex:1; display:flex; flex-direction:column; margin-top:12px;">
<div class="control-label">Telemetry Log</div>
<div id="log-box" class="log-box"></div>
</div>
<div style="font-size: 10px; color: var(--text-muted); margin-top: 8px; text-align: center;">
Left-Click: Place / Right-Click: Remove
</div>
</div>
</aside>
</div>
</div>
<!-- TAB: ANALYTICS -->
<div id="tab-analytics" class="tab-content">
<div style="display: flex; justify-content: space-between; align-items: flex-end;">
<div>
<h1 style="font-size: 28px; font-weight: 700;">Synthesis Performance</h1>
<p style="color: var(--text-secondary); margin-top: 4px;">Historical benchmark metadata for the current laboratory session.</p>
</div>
<button class="btn" style="width: auto;" onclick="window.MF_APP.refreshAnalytics()">Refresh Data</button>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 32px;">
<div class="card" style="height: 400px;">
<div class="card-title">Phase Distribution</div>
<div style="flex:1; min-height:0;"><canvas id="phase-pie"></canvas></div>
</div>
<div class="card" style="height: 400px;">
<div class="card-title">Success Rate by Difficulty</div>
<div style="flex:1; min-height:0;"><canvas id="difficulty-bar"></canvas></div>
</div>
</div>
</div>
<!-- TAB: DOCUMENTATION -->
<div id="tab-docs" class="tab-content">
<div style="max-width: 900px; margin: 0 auto;">
<h1 style="font-size: 42px; font-weight: 800; letter-spacing: -1px; margin-bottom: 32px;">Technical Integration</h1>
<section style="margin-bottom: 48px;">
<h2 style="font-size: 20px; font-weight: 700; color: var(--accent); margin-bottom: 16px; text-transform: uppercase;">Overview</h2>
<p style="color: var(--text-secondary); line-height: 1.7; font-size: 16px;">
MaterialForge is a reinforcement learning benchmark focused on inverse materials discovery. Agents are tasked with
positioning atomic species in an 8&times;8 crystalline lattice to achieve a set of target macro-physical properties.
</p>
</section>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 32px; margin-bottom: 48px;">
<div class="card">
<div class="card-title">REST API</div>
<code style="font-family: var(--mono); font-size: 13px; color: var(--accent2);">POST /reset</code>
<code style="font-family: var(--mono); font-size: 13px; color: var(--accent2);">POST /step</code>
</div>
<div class="card">
<div class="card-title">Websocket</div>
<code style="font-family: var(--mono); font-size: 13px; color: var(--accent);">ws://{host}/ws</code>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- Application Logic -->
<script type="module">
import { LabAPI } from '/playground/js/modules/api.js';
import { LatticeRenderer } from '/playground/js/modules/renderer.js';
import { TelemetryUI } from '/playground/js/modules/ui.js';
class MaterialForgeApp {
constructor() {
this.selectedAtom = 'A';
this.bestReward = 0;
this.state = { currentObs: null };
// Modules
this.api = new LabAPI(window.location.origin);
this.ui = new TelemetryUI();
const colors = {
A: getComputedStyle(document.documentElement).getPropertyValue('--atom-A').trim(),
B: getComputedStyle(document.documentElement).getPropertyValue('--atom-B').trim(),
C: getComputedStyle(document.documentElement).getPropertyValue('--atom-C').trim(),
P: getComputedStyle(document.documentElement).getPropertyValue('--atom-P').trim(),
'.': '#000'
};
this.renderer = new LatticeRenderer(document.getElementById('lattice-canvas'), colors);
this.init();
}
async init() {
this.setupCharts();
this.bindEvents();
try {
await this.api.connect('/ws');
this.ui.setStatus(true);
this.ui.log('Discovery link established.', 'success');
this.api.onMessage((msg) => {
if (msg.type === 'observation') {
const obs = msg.data.observation;
const reward = msg.data.reward;
this.state.currentObs = obs;
this.bestReward = Math.max(this.bestReward, reward);
this.ui.updateKPIs(obs, reward, this.bestReward);
this.ui.updatePropertyBars(obs);
this.renderer.draw(obs.grid);
this.updateRewardChart(reward);
}
});
this.doReset();
} catch (err) {
this.ui.setStatus(false);
this.ui.log('Failed to connect to engine.', 'error');
}
setTimeout(() => document.getElementById('loading-overlay').classList.add('hidden'), 1000);
}
setupCharts() {
const ctx = document.getElementById('reward-chart').getContext('2d');
this.rewardChart = new Chart(ctx, {
type: 'line',
data: { labels: [], datasets: [{
data: [],
borderColor: '#4285f4',
backgroundColor: 'rgba(66, 133, 244, 0.1)',
fill: true,
tension: 0.4,
pointRadius: 0,
borderWidth: 2
}]},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
y: { grid: { color: 'rgba(255,255,255,0.05)' }, ticks: { color: '#64748b' } },
x: { display: false }
}
}
});
}
updateRewardChart(reward) {
this.rewardChart.data.labels.push('');
this.rewardChart.data.datasets[0].data.push(reward);
if (this.rewardChart.data.labels.length > 60) {
this.rewardChart.data.labels.shift();
this.rewardChart.data.datasets[0].data.shift();
}
this.rewardChart.update('none');
}
bindEvents() {
// Tabs
document.querySelectorAll('.nav-tab').forEach(btn => {
btn.onclick = () => {
const target = btn.dataset.tab;
document.querySelectorAll('.nav-tab').forEach(b => b.classList.toggle('active', b === btn));
document.querySelectorAll('.tab-content').forEach(c => {
const isActive = c.id === `tab-${target}`;
c.classList.toggle('active', isActive);
c.style.display = isActive ? 'flex' : 'none'; // Explicitly manage display
});
if (target === 'analytics') this.renderAnalytics();
};
});
// Atoms
document.querySelectorAll('.atom-option').forEach(btn => {
btn.onclick = () => {
document.querySelectorAll('.atom-option').forEach(b => b.classList.remove('selected'));
btn.classList.add('selected');
this.selectedAtom = btn.dataset.atom;
};
});
// Reset
document.getElementById('btn-reset').onclick = () => this.doReset();
// Canvas Interaction
const canvas = document.getElementById('lattice-canvas');
canvas.onclick = (e) => this.sendAction(e, 'place');
canvas.oncontextmenu = (e) => {
e.preventDefault();
this.sendAction(e, 'remove');
};
}
doReset() {
const scenario = document.getElementById('sel-scenario').value;
this.bestReward = 0;
this.rewardChart.data.labels = [];
this.rewardChart.data.datasets[0].data = [];
this.rewardChart.update();
this.api.send('reset', { scenario_name: scenario || undefined });
this.ui.log(`Environment reset: ${scenario || 'Random'}`, 'info');
}
sendAction(e, type) {
const rect = e.target.getBoundingClientRect();
const scaleX = e.target.width / rect.width;
const scaleY = e.target.height / rect.height;
const col = Math.floor(((e.clientX - rect.left) * scaleX) / (e.target.width / 8));
const row = Math.floor(((e.clientY - rect.top) * scaleY) / (e.target.height / 8));
this.api.send('step', {
action_type: type,
row, col,
atom: type === 'place' ? this.selectedAtom : undefined
});
}
refreshAnalytics() {
// Mock data for analytics demonstration matching the GridMind look
this.renderAnalytics();
}
renderAnalytics() {
const pCtx = document.getElementById('phase-pie').getContext('2d');
if (this.phaseChart) this.phaseChart.destroy();
this.phaseChart = new Chart(pCtx, {
type: 'doughnut',
data: {
labels: ['Crystalline', 'Polycrystalline', 'Amorphous'],
datasets: [{
data: [45, 30, 25],
backgroundColor: ['#4285f4', '#34a853', '#fbbc05'],
borderWidth: 0
}]
},
options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom', labels: { color: '#94a3b8' } } } }
});
const dCtx = document.getElementById('difficulty-bar').getContext('2d');
if (this.diffChart) this.diffChart.destroy();
this.diffChart = new Chart(dCtx, {
type: 'bar',
data: {
labels: ['Easy', 'Medium', 'Hard'],
datasets: [{
label: 'Avg Reward',
data: [0.85, 0.72, 0.58],
backgroundColor: '#4285f4',
borderRadius: 4
}]
},
options: { responsive: true, maintainAspectRatio: false, scales: { y: { grid: { color: 'rgba(255,255,255,0.05)' } } } }
});
}
}
window.MF_APP = new MaterialForgeApp();
</script>
</body>
</html>