Spaces:
Running
Running
File size: 5,779 Bytes
dc7c195 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
import React, { useState, useEffect } from 'react';
import { createRoot } from 'react-dom/client';
import html2canvas from 'html2canvas';
const transformerImageDataUri = '/0_WGCl24jT_rWfgZWL.png';
const App = () => {
// Component definitions for the Transformer architecture
const components = [
{ id: 'inputs', label: 'Inputs', side: 'encoder' },
{ id: 'input-embedding', label: 'Input Embedding', side: 'encoder' },
{ id: 'positional-encoding', label: 'Positional Encoding', side: 'encoder' },
{ id: 'multi-head-attention', label: 'Multi-Head Attention', side: 'encoder' },
{ id: 'add-norm', label: 'Add & Norm', side: 'encoder' },
{ id: 'feedforward', label: 'Feedforward', side: 'encoder' },
{ id: 'outputs', label: 'Outputs (shifted right)', side: 'decoder' },
{ id: 'output-embedding', label: 'Output Embedding', side: 'decoder' },
{ id: 'decoder-positional-encoding', label: 'Positional Encoding', side: 'decoder' },
{ id: 'masked-multi-head-attention', label: 'Masked Multi-Head Attention', side: 'decoder' },
{ id: 'decoder-add-norm', label: 'Add & Norm', side: 'decoder' },
{ id: 'cross-attention', label: 'Cross Attention', side: 'decoder' },
{ id: 'decoder-feedforward', label: 'Feedforward', side: 'decoder' },
{ id: 'linear', label: 'Linear', side: 'decoder' },
{ id: 'softmax', label: 'Softmax', side: 'decoder' },
{ id: 'output-probabilities', label: 'Output Probabilities', side: 'decoder' },
];
const [notes, setNotes] = useState<{ [key: string]: string }>({});
const [showToast, setShowToast] = useState(false);
const [name, setName] = useState('');
// Load notes from localStorage on initial render
useEffect(() => {
try {
const savedNotes = localStorage.getItem('transformer-notes');
if (savedNotes) {
setNotes(JSON.parse(savedNotes));
}
} catch (error) {
console.error("Failed to load notes from localStorage", error);
}
}, []);
const handleNoteChange = (id: string, value: string) => {
setNotes((prev: { [key: string]: string }) => ({ ...prev, [id]: value }));
};
const handleSaveNotes = async () => {
const appContainer = document.querySelector('.app-container') as HTMLElement;
if (!appContainer) return;
try {
const canvas = await html2canvas(appContainer, { backgroundColor: '#121212' });
const image = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = image;
link.download = 'transformer-notes.png';
link.click();
} catch (error) {
console.error('Failed to capture screenshot', error);
}
};
const encoderComponents = components.filter(c => c.side === 'encoder');
const decoderComponents = components.filter(c => c.side === 'decoder');
const renderNoteColumn = (title: string, componentsToRender: { id: string; label: string; side: string }[]) => (
<div className="notes-column">
<h2 className="column-title">{title}</h2>
{componentsToRender.map(comp => (
<div className="note-block" key={comp.id}>
<label className="note-label" htmlFor={comp.id}>{comp.label}</label>
<textarea
id={comp.id}
aria-label={`Notes for ${comp.label}`}
value={notes[comp.id] || ''}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => handleNoteChange(comp.id, e.target.value)}
placeholder={`Explain ${comp.label}...`}
/>
</div>
))}
</div>
);
return (
<div className="app-container">
<header className="header">
<h1>Interactive Transformer Architecture</h1>
<p>An interactive diagram of the Transformer model from "Attention Is All You Need". Add your notes in the text boxes to explain each part. Your notes will be saved locally in your browser.</p>
</header>
<div className="save-button-container" style={{ flexDirection: 'column', alignItems: 'center', gap: '1rem' }}>
<input
type="text"
className="name-input"
placeholder="Enter your name..."
value={name}
onChange={e => setName(e.target.value)}
style={{ padding: '10px', borderRadius: '6px', border: '1px solid #333', width: '250px', fontSize: '1rem', marginBottom: '0.5rem' }}
/>
<button className="save-button" onClick={handleSaveNotes}>
Save Notes
</button>
</div>
<div className="architecture-container">
{renderNoteColumn('Encoder', encoderComponents)}
<div className="image-column">
<img
src={transformerImageDataUri}
alt="Transformer Architecture Diagram"
className="architecture-image"
/>
</div>
{renderNoteColumn('Decoder', decoderComponents)}
</div>
<div className={`toast ${showToast ? 'show' : ''}`}>
Notes saved successfully!
</div>
</div>
);
};
const container = document.getElementById('root');
if (container) {
const root = createRoot(container);
root.render(<App />);
}
|