Spaces:
Running
Running
Here is the proposal: A Modular, Tag-less Quality-Control & Selection System for Protein Production in E. coli
e75b376 verified | // Scrollytelling functionality | |
| class ScrollytellingEngine { | |
| constructor() { | |
| this.scenes = document.querySelectorAll('.scene'); | |
| this.progressBar = document.querySelector('.progress-bar'); | |
| this.currentScene = 0; | |
| this.init(); | |
| } | |
| init() { | |
| // Set up intersection observer for scene detection | |
| this.setupIntersectionObserver(); | |
| // Set up scroll progress tracking | |
| this.setupScrollProgress(); | |
| // Initialize first scene | |
| this.activateScene(0); | |
| } | |
| setupIntersectionObserver() { | |
| const options = { | |
| root: null, | |
| rootMargin: '0px', | |
| threshold: this.buildThresholdList() | |
| }; | |
| this.observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| const sceneIndex = parseInt(entry.target.dataset.scene); | |
| this.activateScene(sceneIndex); | |
| } | |
| }); | |
| }, options); | |
| this.scenes.forEach(scene => { | |
| this.observer.observe(scene); | |
| }); | |
| } | |
| buildThresholdList() { | |
| const thresholds = []; | |
| const numSteps = 20; | |
| for (let i = 1.0; i <= numSteps; i++) { | |
| const ratio = i / numSteps; | |
| thresholds.push(ratio); | |
| } | |
| thresholds.push(0); | |
| return thresholds; | |
| } | |
| setupScrollProgress() { | |
| window.addEventListener('scroll', () => { | |
| const scrollTop = window.pageYOffset || document.documentElement.scrollTop; | |
| const scrollHeight = document.documentElement.scrollHeight - window.innerHeight; | |
| const scrollProgress = (scrollTop / scrollHeight) * 100; | |
| if (this.progressBar) { | |
| this.progressBar.style.width = `${scrollProgress}%`; | |
| } | |
| }); | |
| } | |
| activateScene(index) { | |
| // Update all scenes | |
| this.scenes.forEach((scene, i) => { | |
| if (i === index) { | |
| scene.classList.add('active'); | |
| } else { | |
| scene.classList.remove('active'); | |
| } | |
| }); | |
| this.currentScene = index; | |
| this.updateVisualization(index); | |
| } | |
| updateVisualization(sceneIndex) { | |
| const visualizationContainer = document.querySelector('.sticky > div'); | |
| if (!visualizationContainer) return; | |
| // Update visualization based on current scene | |
| switch(sceneIndex) { | |
| case 0: | |
| visualizationContainer.innerHTML = this.createScene0Visualization(); | |
| break; | |
| case 1: | |
| visualizationContainer.innerHTML = this.createScene1Visualization(); | |
| break; | |
| case 2: | |
| visualizationContainer.innerHTML = this.createScene2Visualization(); | |
| break; | |
| case 3: | |
| visualizationContainer.innerHTML = this.createScene3Visualization(); | |
| break; | |
| case 4: | |
| visualizationContainer.innerHTML = this.createScene4Visualization(); | |
| break; | |
| case 5: | |
| visualizationContainer.innerHTML = this.createScene5Visualization(); | |
| break; | |
| case 6: | |
| visualizationContainer.innerHTML = this.createScene6Visualization(); | |
| break; | |
| case 7: | |
| visualizationContainer.innerHTML = this.createScene7Visualization(); | |
| break; | |
| case 8: | |
| visualizationContainer.innerHTML = this.createScene8Visualization(); | |
| break; | |
| default: | |
| visualizationContainer.innerHTML = this.createDefaultVisualization(); | |
| } | |
| } | |
| createScene0Visualization() { | |
| return ` | |
| <div class="w-full max-w-lg bg-gray-800 rounded-lg p-6 shadow-xl"> | |
| <h3 class="text-lg font-semibold text-blue-400 mb-4">Cheater Dynamics</h3> | |
| <svg viewBox="0 0 400 300" class="system-diagram"> | |
| <rect x="50" y="50" width="300" height="200" fill="#1e293b" rx="8" /> | |
| <circle cx="100" cy="150" r="20" fill="#10b981" class="good-cell" /> | |
| <circle cx="200" cy="150" r="20" fill="#ef4444" class="cheater-cell" /> | |
| <text x="100" y="190" fill="#d1d5db" text-anchor="middle" font-size="12">Good Cell</text> | |
| <text x="200" y="190" fill="#d1d5db" text-anchor="middle" font-size="12">Cheater</text> | |
| <text x="200" y="240" fill="#60a5fa" text-anchor="middle" font-size="14" font-weight="bold">Type-2 Cheaters: Transcribe but fail to translate</text> | |
| </svg> | |
| </div> | |
| `; | |
| } | |
| createScene1Visualization() { | |
| return ` | |
| <div class="w-full max-w-lg bg-gray-800 rounded-lg p-6 shadow-xl"> | |
| <h3 class="text-lg font-semibold text-blue-400 mb-4">Constraints & Requirements</h3> | |
| <svg viewBox="0 0 400 300" class="system-diagram"> | |
| <rect x="50" y="50" width="300" height="200" fill="#1e293b" rx="8" /> | |
| <text x="200" y="100" fill="#60a5fa" text-anchor="middle" font-size="16" font-weight="bold">No Tags Allowed</text> | |
| <circle cx="150" cy="150" r="15" fill="#ef4444" /> | |
| <circle cx="250" cy="150" r="15" fill="#ef4444" /> | |
| <text x="150" y="180" fill="#d1d5db" text-anchor="middle" font-size="12">His-tag</text> | |
| <text x="250" y="180" fill="#d1d5db" text-anchor="middle" font-size="12">Intein</text> | |
| <text x="200" y="240" fill="#60a5fa" text-anchor="middle" font-size="14" font-weight="bold">Pristine Product, Universal QC</text> | |
| </svg> | |
| </div> | |
| `; | |
| } | |
| createScene2Visualization() { | |
| return ` | |
| <div class="w-full max-w-lg bg-gray-800 rounded-lg p-6 shadow-xl"> | |
| <h3 class="text-lg font-semibold text-blue-400 mb-4">Dual Stress Sensors</h3> | |
| <svg viewBox="0 0 400 300" class="system-diagram"> | |
| <rect x="50" y="50" width="300" height="200" fill="#1e293b" rx="8" /> | |
| <circle cx="120" cy="120" r="15" fill="#f59e0b" class="sensor-active" /> | |
| <circle cx="280" cy="120" r="15" fill="#f59e0b" class="sensor-active" /> | |
| <text x="120" y="180" fill="#d1d5db" text-anchor="middle" font-size="12">σ32 Sensor</text> | |
| <text x="280" y="180" fill="#d1d5db" text-anchor="middle" font-size="12">σE/Cpx Sensor</text> | |
| <text x="200" y="240" fill="#60a5fa" text-anchor="middle" font-size="14" font-weight="bold">Native Stress Pathways</text> | |
| </svg> | |
| </div> | |
| `; | |
| } | |
| createScene3Visualization() { | |
| return ` | |
| <div class="w-full max-w-lg bg-gray-800 rounded-lg p-6 shadow-xl"> | |
| <h3 class="text-lg font-semibold text-blue-400 mb-4">Production Gate Logic</h3> | |
| <svg viewBox="0 0 400 300" class="system-diagram"> | |
| <rect x="50" y="50" width="300" height="200" fill="#1e293b" rx="8" /> | |
| <text x="200" y="120" fill="#60a5fa" text-anchor="middle" font-size="16" font-weight="bold">(Scyt ∨ Senv) ∧ PM</text> | |
| <rect x="180" y="140" width="40" height="20" fill="#8b5cf6" class="gate-active" rx="4" /> | |
| <text x="200" y="240" fill="#60a5fa" text-anchor="middle" font-size="14" font-weight="bold">Stress OR + ProductionMode AND</text> | |
| </svg> | |
| </div> | |
| `; | |
| } | |
| createScene4Visualization() { | |
| return ` | |
| <div class="w-full max-w-lg bg-gray-800 rounded-lg p-6 shadow-xl"> | |
| <h3 class="text-lg font-semibold text-blue-400 mb-4">Actuator & Latch</h3> | |
| <svg viewBox="0 0 400 300" class="system-diagram"> | |
| <rect x="50" y="50" width="300" height="200" fill="#1e293b" rx="8" /> | |
| <path d="M 100 150 L 150 150 L 150 130 L 180 150 L 150 170 L 150 150" fill="#dc2626" class="latch-flipped" /> | |
| <text x="200" y="240" fill="#60a5fa" text-anchor="middle" font-size="14" font-weight="bold">Flp-ssrA + FRT-Toxin</text> | |
| </svg> | |
| </div> | |
| `; | |
| } | |
| createScene5Visualization() { | |
| return ` | |
| <div class="w-full max-w-lg bg-gray-800 rounded-lg p-6 shadow-xl"> | |
| <h3 class="text-lg font-semibold text-blue-400 mb-4">Translation Certificate</h3> | |
| <svg viewBox="0 0 400 300" class="system-diagram"> | |
| <rect x="50" y="50" width="300" height="200" fill="#1e293b" rx="8" /> | |
| <path d="M 120 140 L 160 140 L 160 160 L 120 160 Z" fill="#059669" class="certificate-issued" /> | |
| <text x="200" y="240" fill="#60a5fa" text-anchor="middle" font-size="14" font-weight="bold">Ribosomal Re-initiation</text> | |
| </svg> | |
| </div> | |
| `; | |
| } | |
| createScene6Visualization() { | |
| return ` | |
| <div class="w-full max-w-lg bg-gray-800 rounded-lg p-6 shadow-xl"> | |
| <h3 class="text-lg font-semibold text-blue-400 mb-4">Dual-Plug Antidote</h3> | |
| <svg viewBox="0 0 400 300" class="system-diagram"> | |
| <rect x="50" y="50" width="300" height="200" fill="#1e293b" rx="8" /> | |
| <circle cx="120" cy="120" r="15" fill="#3b82f6" class="antidote-active" /> | |
| <circle cx="280" cy="120" r="15" fill="#3b82f6" class="antidote-active" /> | |
| <text x="120" y="180" fill="#d1d5db" text-anchor="middle" font-size="12">Repressor</text> | |
| <text x="280" y="180" fill="#d1d5db" text-anchor="middle" font-size="12">sRNA</text> | |
| <text x="200" y="240" fill="#60a5fa" text-anchor="middle" font-size="14" font-weight="bold">Transcriptional + Translational Suppression</text> | |
| </svg> | |
| </div> | |
| `; | |
| } | |
| createScene7Visualization() { | |
| return ` | |
| <div class="w-full max-w-lg bg-gray-800 rounded-lg p-6 shadow-xl"> | |
| <h3 class="text-lg font-semibold text-blue-400 mb-4">System Partitioning</h3> | |
| <svg viewBox="0 0 400 300" class="system-diagram"> | |
| <rect x="50" y="50" width="300" height="200" fill="#1e293b" rx="8" /> | |
| <rect x="80" y="80" width="120" height="40" fill="#1e40af" rx="4" /> | |
| <rect x="220" y="80" width="120" height="40" fill="#1e40af" rx="4" /> | |
| <text x="140" y="100" fill="#ffffff" text-anchor="middle" font-size="12">Genome Core</text> | |
| <rect x="220" y="140" width="120" height="40" fill="#3730a3" rx="4" /> | |
| <text x="280" y="120" fill="#ffffff" text-anchor="middle" font-size="12">Plasmid App</text> | |
| <text x="200" y="240" fill="#60a5fa" text-anchor="middle" font-size="14" font-weight="bold">Stable Decision Logic + Swappable Components</text> | |
| </svg> | |
| </div> | |
| `; | |
| } | |
| createScene8Visualization() { | |
| return ` | |
| <div class="w-full max-w-lg bg-gray-800 rounded-lg p-6 shadow-xl"> | |
| <h3 class="text-lg font-semibold text-blue-400 mb-4">Performance Metrics</h3> | |
| <svg viewBox="0 0 400 300" class="system-diagram"> | |
| <rect x="50" y="50" width="300" height="200" fill="#1e293b" rx="8" /> | |
| <rect x="120" y="100" width="160" height="80" fill="#1e293b" rx="8" /> | |
| <text x="200" y="120" fill="#10b981" text-anchor="middle" font-size="16" font-weight="bold">≥10× Enrichment</text> | |
| <text x="200" y="160" fill="#60a5fa" text-anchor="middle" font-size="14">Target Enrichment</text> | |
| <text x="200" y="200" fill="#ef4444" text-anchor="middle" font-size="14">False Commit Risk ≤0.1%</text> | |
| </svg> | |
| </div> | |
| `; | |
| } | |
| createDefaultVisualization() { | |
| return ` | |
| <div class="w-full max-w-lg bg-gray-800 rounded-lg p-6 shadow-xl"> | |
| <h3 class="text-lg font-semibold text-blue-400 mb-4">System Overview</h3> | |
| <svg viewBox="0 0 400 300" class="system-diagram"> | |
| <rect x="50" y="50" width="300" height="200" fill="#1e293b" rx="8" /> | |
| <text x="200" y="150" fill="#60a5fa" text-anchor="middle" dominant-baseline="middle" font-size="16" font-weight="bold">Synthetic Biology QC System</text> | |
| </svg> | |
| </div> | |
| `; | |
| } | |
| } | |
| // Initialize when DOM is loaded | |
| document.addEventListener('DOMContentLoaded', () => { | |
| new ScrollytellingEngine(); | |
| // Set up reduced motion support | |
| if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { | |
| document.body.classList.add('reduced-motion'); | |
| } | |
| }); |