Files
cs249r_book/tinytorch/site/extra/community/index.html

892 lines
32 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI History Landscape</title>
<link rel="icon" href="assets/flame.svg" type="image/svg+xml">
<style>
body {
margin: 0;
overflow-x: hidden;
background-color: #eeeee6;
font-family: 'Courier New', Courier, monospace;
}
#canvas-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
z-index: 1;
}
#scroll-track {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 25000vh;
z-index: 2;
pointer-events: none;
}
/* --- Hero Title Section --- */
#hero-title {
position: fixed;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 20;
text-align: center;
pointer-events: none;
transition: opacity 0.2s ease-out, transform 0.2s ease-out;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.hero-logo {
height: 180px;
filter: drop-shadow(0 4px 6px rgba(255, 102, 0, 0.2));
}
.subtitle {
font-family: 'Courier New', Courier, monospace;
font-size: 1.75rem;
color: #555;
letter-spacing: 3px;
margin-top: 12px;
background: rgba(255, 255, 255, 0.6);
padding: 8px 20px;
border-radius: 30px;
backdrop-filter: blur(2px);
}
/* --- Hamburger Menu, Create Account, Sidebar Styles moved to layout.js --- */
/* --- Markers --- */
#markers-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 5;
overflow: hidden;
}
.marker {
position: absolute;
transform: translate(-50%, -100%);
text-align: center;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: auto;
}
.marker-content {
background: rgba(255, 255, 255, 0.95);
padding: 8px 12px;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
border: 1px solid rgba(255, 102, 0, 0.2);
display: flex;
flex-direction: column;
align-items: center;
}
.marker-year {
font-size: 0.8rem;
font-weight: bold;
color: #ff6600;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 2px;
}
.marker-label {
font-size: 1.1rem;
font-weight: 800;
color: #222;
white-space: nowrap;
margin-bottom: 2px;
}
.marker-author {
font-size: 0.75rem;
color: #666;
font-style: italic;
max-width: 200px;
white-space: normal;
line-height: 1.1;
}
.marker.past .marker-content {
background: rgba(240, 240, 240, 0.85);
border-color: #ddd;
}
.marker.past .marker-year {
color: #999;
}
.marker.past .marker-label {
color: #aaa;
}
.marker.past .marker-author {
color: #bbb;
}
/* --- Generic Card Styles --- */
.feed-card {
position: fixed;
width: 300px;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border: 1px solid rgba(0,0,0,0.1);
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
z-index: 100;
transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
overflow: hidden;
display: flex;
flex-direction: column;
}
/* Content Styling */
.card-header {
padding: 12px 15px;
font-family: 'Verdana', sans-serif;
font-weight: bold;
font-size: 0.9rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.feed-list {
padding: 0;
margin: 0;
list-style: none;
}
.feed-item {
padding: 12px 15px;
border-bottom: 1px solid #f0f0f0;
font-family: 'Verdana', sans-serif;
font-size: 0.8rem;
display: flex;
justify-content: space-between;
align-items: center;
background: white;
line-height: 1.4;
}
.feed-item:last-child {
border-bottom: none;
}
.feed-item a {
text-decoration: none;
color: inherit;
display: block;
width: 100%;
}
/* --- 1. The Journey Card (Right) --- */
#process-card {
top: 45vh;
right: 30px;
left: auto; /* Ensure no conflict */
transform: translateX(150%);
max-height: 50vh;
overflow-y: auto;
}
#process-card.visible {
transform: translateX(0);
}
#process-card .card-header {
background: transparent;
color: #333;
border-bottom: 1px solid rgba(0,0,0,0.1);
}
#process-card .feed-item {
justify-content: flex-start;
gap: 15px;
min-height: 50px;
opacity: 1;
transform: translateX(0);
transition: all 0.3s ease;
border-left: 4px solid transparent;
}
#process-card .feed-item.active-step {
background: #f0f4f8;
box-shadow: 0 4px 15px rgba(0,0,0,0.15);
transform: scale(1.03) translateX(5px);
border-left: 4px solid #2a323a;
color: #2a323a;
font-weight: bold;
z-index: 10;
}
#process-card .feed-item::before {
content: '';
width: 16px;
height: 16px;
border-radius: 50%;
border: 2px solid #ccc;
flex-shrink: 0;
box-sizing: border-box;
transition: all 0.3s ease;
}
#process-card .feed-item.active-step::before {
background-color: #ff6600; /* Orange core for the flame */
border-color: #ff9900;
animation: glow 1.5s ease-in-out infinite alternate;
}
@keyframes glow {
from {
box-shadow: 0 0 4px #ff9900, 0 0 8px #ff6600;
}
to {
box-shadow: 0 0 8px #ff9900, 0 0 16px #ff6600;
}
}
/* --- 2. News Feed Card (Left) --- */
#news-card {
top: 30vh;
left: 30px;
right: auto;
transform: translateX(-150%);
}
#news-card.visible {
transform: translateX(0);
}
#news-card .card-header {
background: #dcedc8;
color: #33691e;
border-bottom: 1px solid #c5e1a5;
}
/* --- 3. Submission Feed Card (Left - Appears after News) --- */
#submission-card {
display: flex;
top: 30vh; /* Same vertical position as News for continuity */
left: 30px;
right: auto;
transform: translateX(-150%);
}
#submission-card.visible {
transform: translateX(0);
}
#submission-card .card-header {
background: #dcedc8; /* Same Green as News */
color: #33691e;
border-bottom: 1px solid #c5e1a5;
}
/* Submission Specifics */
.sub-user { font-weight: bold; color: #003366; }
.sub-module { color: #666; font-style: italic; text-align: right; max-width: 120px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* News Specifics */
.news-link {
text-decoration: none;
color: #333;
display: flex;
flex-direction: column;
width: 100%;
}
.news-link:hover .news-title { color: #33691e; text-decoration: underline; }
.news-date { font-size: 0.7em; color: #999; margin-bottom: 2px; text-transform: uppercase; }
.news-title { font-weight: bold; font-size: 0.85rem; line-height: 1.2; }
.ui-overlay {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
z-index: 10;
color: #333;
text-align: center;
background: transparent;
padding: 0;
border-radius: 0;
box-shadow: none;
pointer-events: none;
backdrop-filter: none;
font-weight: bold;
border: none;
}
</style>
<!-- Load Three.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
</head>
<body>
<!-- Layout elements (Menu, Sidebar, Login) are injected by layout.js -->
<div id="hero-title">
<img src="../_static/logos/logo-tinytorch-transparent.png" alt="TinyTorch" class="hero-logo">
<div class="subtitle">Builder Community</div>
</div>
<!-- 1. The Journey/Process Card -->
<!--
<div id="process-card" class="feed-card">
<div class="card-header">
<span>The Journey</span>
<span style="font-size:0.7em; opacity:0.8;">GUIDE</span>
</div>
<ul class="feed-list process-list">
<li class="feed-item"><a href="?action=join">Create Account & Track Progress</a></li>
<li class="feed-item"><a href="dashboard.html">View and interact with others during your TinyTorch journey</a></li>
<li class="feed-item"><a href="community.html">View the global TinyTorch community</a></li>
<li class="feed-item"><a href="?action=profile">Update Your Profile</a></li>
<li class="feed-item">Connect with Global Builders</li>
<li class="feed-item">Join Hackathons & Earn Badges</li>
<li class="feed-item">Innovate in the AI Space</li>
</ul>
</div>
-->
<!-- 2. News Feed Card (Left) -->
<!--
<div id="news-card" class="feed-card">
<div class="card-header">
<span>Recent News</span>
<span style="font-size:0.7em; opacity:0.8;">UPDATES</span>
</div>
<ul class="feed-list" id="news-list">
</ul>
</div>
-->
<!-- 3. Submission Feed Card (Left - Appears after News) -->
<div id="submission-card" class="feed-card">
<div class="card-header">
<span>Recent Submissions</span>
<span style="font-size:0.7em; opacity:0.8;">LIVE</span>
</div>
<ul class="feed-list" id="submission-list">
<!-- Items injected by JS -->
</ul>
</div>
<div id="canvas-container"></div>
<div id="scroll-track"></div>
<div id="markers-container"></div>
<div class="ui-overlay">
<div class="caret-up"></div>
<div style="font-size: 10px; letter-spacing: 2px; margin-top: 8px;">SCROLL</div>
</div>
<style>
.caret-up {
width: 24px;
height: 24px;
border-left: 4px solid #333;
border-top: 4px solid #333;
transform: rotate(45deg);
margin: 0 auto;
animation: bounce 2s infinite;
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {transform: rotate(45deg) translate(0, 0);}
40% {transform: rotate(45deg) translate(-5px, -5px);}
60% {transform: rotate(45deg) translate(-3px, -3px);}
}
</style>
<script type="module" src="app.js"></script>
<script type="module">
import { ArxivService } from './arxiv_service.js';
import { DotismScene } from './modules/dotism.js';
// --- Configuration & Data ---
const arxiv = new ArxivService();
const MILESTONES = [
{ year: 1958, name: "Perceptron", authors: "Frank Rosenblatt", z: -20 },
{ year: 1969, name: "XOR Problem", authors: "Minsky & Papert", z: -50 },
{ year: 1986, name: "Backprop / MLP", authors: "Rumelhart, Hinton, Williams", z: -90 },
{ year: 1997, name: "Deep Blue", authors: "Feng-hsiung Hsu, Campbell", z: -130 },
{ year: 1997, name: "LSTM", authors: "Hochreiter & Schmidhuber", z: -140 },
{ year: 1998, name: "LeNet (CNN)", authors: "Yann LeCun et al.", z: -160 },
{ year: 2012, name: "AlexNet", authors: "Krizhevsky, Sutskever, Hinton", z: -200 },
{ year: 2013, name: "DQN (Deep RL)", authors: "Mnih et al. (DeepMind)", z: -215 },
{ year: 2014, name: "GANs", authors: "Ian Goodfellow et al.", z: -230 },
{ year: 2015, name: "ResNet", authors: "Kaiming He et al.", z: -245 },
{ year: 2016, name: "AlphaGo", authors: "Silver, Hassabis (DeepMind)", z: -260 },
{ year: 2017, name: "Transformer", authors: "Vaswani, Shazeer et al.", z: -280 },
{ year: 2018, name: "BERT", authors: "Devlin et al. (Google)", z: -295 },
{ year: 2018, name: "GPT-1", authors: "Radford et al. (OpenAI)", z: -310 },
{ year: 2020, name: "GPT-3", authors: "Brown et al. (OpenAI)", z: -330 },
{ year: 2022, name: "ChatGPT", authors: "OpenAI", z: -360 },
{ year: 2022, name: "Stable Diffusion", authors: "Rombach et al. (CompVis)", z: -375 },
{ year: 2023, name: "Mistral 7B", authors: "Mistral AI", z: -400 },
{ year: 2023, name: "I-JEPA (World Model)", authors: "LeCun et al. (Meta)", z: -420 },
{ year: 2023, name: "Phi-2", authors: "Microsoft Research", z: -440 },
{ year: 2024, name: "Llama 3", authors: "Meta AI", z: -460 },
{ year: 2024, name: "DeepSeek-V2", authors: "DeepSeek AI", z: -480 }
];
let flyOverY = 0;
let targetFlyOverY = 0;
const container = document.getElementById('canvas-container');
const scene = new THREE.Scene();
const initialBgColor = new THREE.Color(0xeeeee6);
scene.background = initialBgColor;
scene.fog = new THREE.Fog(initialBgColor, 20, 140);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 20);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
container.appendChild(renderer.domElement);
// --- Markers ---
const markersContainer = document.getElementById('markers-container');
const markerElements = [];
MILESTONES.forEach(m => {
const div = document.createElement('div');
div.className = 'marker';
div.innerHTML = `
<div class="marker-content">
<div class="marker-year">${m.year}</div>
<div class="marker-label">${m.name}</div>
<div class="marker-author">${m.authors}</div>
</div>
`;
markersContainer.appendChild(div);
markerElements.push({ data: m, element: div });
});
// Initialize Dotism Scene
DotismScene.init({
scene,
camera,
renderer
});
const heroTitle = document.getElementById('hero-title');
// Sky Cycle Colors
const skyColors = [
{ pos: 0, color: new THREE.Color(0xeeeee6), dramatic: 1.0 },
{ pos: 150, color: new THREE.Color(0x8a929a), dramatic: 0.8 },
{ pos: 300, color: new THREE.Color(0x5a626a), dramatic: 0.3 },
{ pos: 450, color: new THREE.Color(0x2a323a), dramatic: 0.1 },
{ pos: 550, color: new THREE.Color(0x5a626a), dramatic: 0.3 },
{ pos: 700, color: new THREE.Color(0xeeeee6), dramatic: 1.0 }
];
function getSkyState(y) {
const cycle = 700;
const modY = y % cycle;
for(let i=0; i<skyColors.length-1; i++) {
const curr = skyColors[i];
const next = skyColors[i+1];
if (modY >= curr.pos && modY < next.pos) {
const t = (modY - curr.pos) / (next.pos - curr.pos);
return {
color: curr.color.clone().lerp(next.color, t),
dramatic: lerp(t, curr.dramatic, next.dramatic)
};
}
}
return { color: skyColors[0].color, dramatic: skyColors[0].dramatic };
}
function lerp(t, a, b) { return a + t * (b - a); }
// Initialize ArXiv
const MARKER_SCALE = 8;
let lastZ = MILESTONES[MILESTONES.length - 1].z;
const Z_INTERVAL = 30; // Spacing for new papers
let nextPaperZ = lastZ - Z_INTERVAL;
let spawningPapers = true;
function updateScene() {
flyOverY += (targetFlyOverY - flyOverY) * 0.08;
// Move camera through the dot landscape (matching dotism.html behavior)
camera.position.z = 20 - (flyOverY * 4);
camera.position.x = Math.sin(flyOverY * 0.2) * 2;
camera.position.y = 5 + Math.cos(flyOverY * 0.12) * 1;
camera.lookAt(0, 0, camera.position.z - 50);
// Ensure camera matrices are updated before projection in updateMarkers
camera.updateMatrixWorld();
// --- ArXiv Paper Spawning Logic ---
const cameraZ = camera.position.z;
const lastMilestoneWorldZ = lastZ * MARKER_SCALE;
if (spawningPapers && lastMilestoneWorldZ > cameraZ - 300) {
const paper = arxiv.papers.shift();
if (paper) {
const newM = {
year: paper.year,
name: paper.name,
authors: paper.authors,
z: nextPaperZ
};
MILESTONES.push(newM);
const div = document.createElement('div');
div.className = 'marker';
div.innerHTML = `
<div class="marker-content" style="border-color: #00ccff;">
<div class="marker-year" style="color: #00ccff;">LATEST RESEARCH</div>
<div class="marker-label" style="font-size: 0.9rem; white-space: normal; max-width: 250px;">${newM.name}</div>
<div class="marker-author">${newM.authors}</div>
</div>
`;
markersContainer.appendChild(div);
markerElements.push({ data: newM, element: div });
lastZ = nextPaperZ;
nextPaperZ -= Z_INTERVAL;
} else {
arxiv.getNextPaper();
}
}
const skyState = getSkyState(flyOverY);
scene.background.copy(skyState.color);
// Update Dotism Scene
DotismScene.update(skyState);
// Update marker positions for the dotism camera movement
updateMarkers();
}
function getPathX(z) {
return Math.sin(z * 0.1) * 8 + Math.cos(z * 0.03) * 12;
}
function updateMarkers() {
const tempV = new THREE.Vector3();
markerElements.forEach(item => {
const m = item.data;
const el = item.element;
// Markers are stationary in world space, scaled to match scroll
const worldZ = m.z * MARKER_SCALE;
const wX = getPathX(worldZ * 0.005); // Use lower frequency for path scaling
const simplex = DotismScene.getSimplex();
const h1 = simplex.noise2D(wX * 0.01, worldZ * 0.01) * 25;
const h2 = simplex.noise2D(wX * 0.03, worldZ * 0.03) * 8;
const baseHeight = h1 + h2 - 10;
const wY = baseHeight + 2.5;
tempV.set(wX, wY, worldZ);
tempV.project(camera);
const screenX = (tempV.x * 0.5 + 0.5) * window.innerWidth;
const screenY = (-tempV.y * 0.5 + 0.5) * window.innerHeight;
// Past check based on camera position
if (worldZ > camera.position.z) el.classList.add('past');
else el.classList.remove('past');
// Fade in as marker approaches, fade out as it passes behind
const viewDistance = 150;
const fadeInStart = camera.position.z - viewDistance;
const fadeInEnd = camera.position.z - 60;
const fadeOutStart = camera.position.z + 10;
const fadeOutEnd = camera.position.z + 40;
let opacity = 1;
if (worldZ > fadeOutStart) {
opacity = Math.max(0, 1 - (worldZ - fadeOutStart) / (fadeOutEnd - fadeOutStart));
} else if (worldZ < fadeInStart) {
opacity = Math.max(0, (worldZ - fadeInStart) / (fadeInEnd - fadeInStart));
}
// Global fade for hero section
let globalFade = (flyOverY * 4 - 150) / 50;
if (globalFade < 0) globalFade = 0;
if (globalFade > 1) globalFade = 1;
opacity *= globalFade;
el.style.opacity = opacity;
el.style.left = `${screenX}px`;
el.style.top = `${screenY}px`;
// Hide if too far behind or ahead in projection
if (tempV.z < -1 || tempV.z > 1) el.style.opacity = 0;
});
}
function animate() {
requestAnimationFrame(animate);
updateScene();
renderer.render(scene, camera);
}
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
DotismScene.resize();
});
window.addEventListener('scroll', () => {
const scrollY = window.scrollY;
targetFlyOverY = scrollY * 0.25;
// Hero Fade Out
const startFade = 100;
const endFade = 600;
let opacity = 1;
if (scrollY > startFade) {
opacity = 1 - (scrollY - startFade) / (endFade - startFade);
if (opacity < 0) opacity = 0;
}
heroTitle.style.opacity = opacity;
if (opacity > 0) {
const lift = Math.min(scrollY * 0.2, 100);
heroTitle.style.transform = `translate(-50%, calc(-50% - ${lift}px))`;
}
// Timeline Fade In (Start after Hero starts fading, fully visible after Hero is gone)
const tlStart = 400;
const tlEnd = 900;
let tlOpacity = 0;
if (scrollY > tlStart) {
tlOpacity = (scrollY - tlStart) / (tlEnd - tlStart);
if (tlOpacity > 1) tlOpacity = 1;
}
// Update materials (timeline lines removed)
// historyMat.opacity = tlOpacity;
// futureMat.opacity = 0.6 * tlOpacity;
// glowMat.opacity = 0.8 * tlOpacity;
// Show card logic
const processCard = document.getElementById('process-card');
const submissionCard = document.getElementById('submission-card');
const newsCard = document.getElementById('news-card');
// Process: Show 50px - 700px
if (processCard) {
if (scrollY > 50 && scrollY < 700) {
processCard.classList.add('visible');
// Highlight Logic
const start = 50;
const end = 700;
const range = end - start;
const progress = (scrollY - start) / range;
const items = processCard.querySelectorAll('.feed-item');
const activeIndex = Math.floor(progress * items.length);
items.forEach((item, index) => {
if (index === activeIndex) {
item.classList.add('active-step');
} else {
item.classList.remove('active-step');
}
});
} else {
processCard.classList.remove('visible');
}
}
// News: Show 700px - 1500px (Immediate after Process, Shortened Duration)
if (newsCard) {
if (scrollY >= 700 && scrollY < 1500) {
newsCard.classList.add('visible');
} else {
newsCard.classList.remove('visible');
}
}
// Submissions: Show 1500px - 3500px (Immediate after News)
// if (scrollY >= 1500 && scrollY < 3500) {
// submissionCard.classList.add('visible');
// } else {
// submissionCard.classList.remove('visible');
// }
});
animate();
// --- Feed Logic ---
const cardList = document.getElementById('submission-list');
const newsList = document.getElementById('news-list');
let lastSubmissionData = null;
function addSubmissionRow(user, mod) {
const li = document.createElement('li');
li.className = 'feed-item';
li.innerHTML = `
<span class="sub-user">${user}</span>
<span class="sub-module">${mod}</span>
`;
cardList.appendChild(li);
}
// --- News Logic (Tiny Torch Specific) ---
const NEWS_ITEMS = [
{ date: "Dec 17", title: "MLSYSBOOK 10k GitHub Stars Celebration via Edge AI Foundation", link: "#" },
{ date: "Dec 9", title: "Tiny Torch official launch", link: "#" },
{ date: "Dec 8", title: "Tiny Torch merged with MLSYSBOOK.ai", link: "#" },
{ date: "Dec 1", title: "Tiny Torch Community is born", link: "#" }
];
function populateNews() {
if (!newsList) return;
newsList.innerHTML = '';
NEWS_ITEMS.forEach(item => {
const li = document.createElement('li');
li.className = 'feed-item';
li.innerHTML = `
<a href="${item.link}" target="_blank" class="news-link">
<span class="news-date">${item.date}</span>
<span class="news-title">${item.title}</span>
</a>
`;
newsList.appendChild(li);
});
}
populateNews();
// --- Real Data Integration ---
const API_URL = "https://zrvmjrxhokwwmjacyhpq.supabase.co/functions/v1/get_recent_submitted";
// Map DB IDs to UI Strings (must match src/ folder names)
const MODULE_MAP = {
"01": "01_tensor",
"02": "02_activations",
"03": "03_layers",
"04": "04_losses",
"05": "05_dataloader",
"06": "06_autograd",
"07": "07_optimizers",
"08": "08_training",
"09": "09_convolutions",
"10": "10_tokenization",
"11": "11_embeddings",
"12": "12_attention",
"13": "13_transformers",
"14": "14_profiling",
"15": "15_quantization",
"16": "16_compression",
"17": "17_acceleration",
"18": "18_memoization",
"19": "19_benchmarking",
"20": "20_capstone"
};
async function fetchRealSubmissions() {
try {
// Fetch top 5 recent submissions
const response = await fetch(`${API_URL}?limit=5`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
processRealData(data);
} catch (error) {
console.error("Error fetching submissions:", error);
}
}
function processRealData(data) {
// Check if data changed to avoid redraws
const currentHash = JSON.stringify(data);
if (currentHash === lastSubmissionData) return;
lastSubmissionData = currentHash;
// Clear the existing list
cardList.innerHTML = '';
data.forEach(row => {
const username = row.username || "Anonymous";
// Get the last completed module ID (most recent)
const lastId = row.completed_ids && row.completed_ids.length > 0
? row.completed_ids[row.completed_ids.length - 1]
: 1;
// Format ID to match map keys (e.g. 1 -> "01")
const idKey = String(lastId).padStart(2, '0');
// Lookup or fallback
const modName = MODULE_MAP[idKey] || `Module ${lastId}`;
addSubmissionRow(username, modName);
});
}
// Start Polling
// fetchRealSubmissions(); // Initial fetch
// setInterval(fetchRealSubmissions, 10000); // Poll every 10 seconds
</script>
<script>
document.addEventListener('DOMContentLoaded', () => {
console.log("Welcome to v1.2, Kai says HI!")
const processList = document.querySelector('#process-card .process-list');
if (processList) {
processList.addEventListener('click', (e) => {
const link = e.target.closest('a');
if (!link) return;
e.preventDefault();
const href = link.getAttribute('href');
if (!href || href === '#') return;
// Re-implement getBasePath logic from modules/config.js
let basePath = '';
const hostname = window.location.hostname;
if (hostname === 'mlsysbook.ai') {
basePath = '/tinytorch/community';
} else if (hostname === 'tinytorch.ai' || (hostname === 'localhost' && window.location.port === '8000')) {
basePath = '/community';
} else if (hostname === 'harvard-edge.github.io') {
basePath = '/cs249r_book_dev/tinytorch/community';
}
if (href.startsWith('?')) {
// It's a modal action, reload the page with the param
window.location.href = window.location.pathname + href;
} else {
// It's a page navigation
window.location.href = basePath ? `${basePath}/${href}` : href;
}
});
}
});
</script>
</body>
</html>