Spaces:
Sleeping
Sleeping
Download server/static/index.html from ArshPathan/material_forge_env: direct link, hf CLI and curl.
- Browser
- Download file 20.2 kB
-
https://huggingface.co/spaces/ArshPathan/material_forge_env/resolve/main/server/static/index.html
- Command line
-
hf download hf://spaces/ArshPathan/material_forge_env/server/static/index.html
-
curl -L -o index.html https://huggingface.co/spaces/ArshPathan/material_forge_env/resolve/main/server/static/index.html
20.2 kB
| <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×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> | |