/ concept-collection / ans-visualizer
Sign in
concept-collection / ans-visualizer
ans-visualizer / src / App.tsx
153 lines · 5.0 KBBlameHistoryRaw
1import { GitHub, ArrowDownward } from '@mui/icons-material';
2import { Box, Button, Container, createTheme, CssBaseline, IconButton, Paper, ThemeProvider, Tooltip } from '@mui/material';
3import 'katex/dist/katex.min.css';
4import { useEffect, useRef, useState } from 'react';
5import ReactMarkdown from 'react-markdown';
6import rehypeKatex from 'rehype-katex';
7import remarkMath from 'remark-math';
8import FrequencyInput from './components/FrequencyInput';
9import StateGrid from './components/StateGrid';
10import explanationText from './explanation.md?raw';
11import type { Symbol } from './types';
12import { getDefaultColors } from './utils/ansLogic';
14const theme = createTheme({
15 palette: {
16 mode: 'light',
17 primary: {
18 main: '#1976d2',
19 },
20 },
21});
23function App() {
24 const [symbols, setSymbols] = useState<Symbol[]>([]);
25 const visualizationRef = useRef<HTMLDivElement>(null);
27 useEffect(() => {
28 // Initialize with default symbols: A-1, B-1, C-1
29 const defaultColors = getDefaultColors();
30 const defaultSymbols: Symbol[] = [
31 { name: 'A', frequency: 3, color: defaultColors[0] },
32 { name: 'B', frequency: 2, color: defaultColors[1] },
33 { name: 'C', frequency: 1, color: defaultColors[2] },
34 ];
35 setSymbols(defaultSymbols);
36 }, []);
38 const scrollToVisualization = () => {
39 visualizationRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
40 };
42 return (
43 <ThemeProvider theme={theme}>
44 <CssBaseline />
45 <Tooltip title="View source on GitHub">
46 <IconButton
47 component="a"
48 href="https://github.com/visposts/ans-visualizer"
49 target="_blank"
50 rel="noopener noreferrer"
51 sx={{
52 position: 'fixed',
53 top: 16,
54 right: 16,
55 bgcolor: 'background.paper',
56 boxShadow: 2,
57 '&:hover': {
58 bgcolor: 'action.hover',
59 },
60 zIndex: 1000,
61 }}
62 >
63 <GitHub />
64 </IconButton>
65 </Tooltip>
66 <Container maxWidth="xl" sx={{
67 py: 2,
68 height: '100vh',
69 display: 'flex',
70 flexDirection: 'column',
71 overflow: { xs: 'auto', md: 'hidden' }, // Single scroll on mobile, no scroll on desktop
72 }}>
73 <Box sx={{
74 display: 'flex',
75 flexDirection: { xs: 'column', md: 'row' },
76 gap: 3,
77 flex: { xs: '0 1 auto', md: 1 }, // Allow shrinking on mobile, fixed on desktop
78 overflow: { xs: 'visible', md: 'hidden' }, // Visible on mobile (parent scrolls), hidden on desktop
79 minHeight: { xs: 'auto', md: 0 },
80 }}>
81 {/* Left Column - Explanation */}
82 <Box sx={{
83 flex: 1,
84 display: 'flex',
85 flexDirection: 'column',
86 minHeight: { xs: 'auto', md: 0 },
87 overflow: { xs: 'visible', md: 'auto' }, // No scroll on mobile (parent scrolls), independent scroll on desktop
88 }}>
89 <Paper sx={{
90 p: 2,
91 flex: { xs: '0 0 auto', md: '1 1 auto' },
92 overflow: { xs: 'visible', md: 'auto' },
93 display: 'flex',
94 flexDirection: 'column',
95 }}>
96 {/* Jump to Visualization Button - Only visible on mobile (single column) */}
97 <Box sx={{
98 display: { xs: 'flex', md: 'none' },
99 justifyContent: 'center',
100 mb: 2,
101 }}>
102 <Button
103 variant="contained"
104 color="primary"
105 size="large"
106 endIcon={<ArrowDownward />}
107 onClick={scrollToVisualization}
108 sx={{
109 boxShadow: 3,
110 '&:hover': {
111 boxShadow: 6,
112 },
113 }}
114 >
115 Jump to Visualization
116 </Button>
117 </Box>
119 <ReactMarkdown
120 remarkPlugins={[remarkMath]}
121 rehypePlugins={[rehypeKatex]}
122 >
123 {explanationText}
124 </ReactMarkdown>
125 </Paper>
126 </Box>
128 {/* Right Column - Interactive Controls */}
129 <Box
130 ref={visualizationRef}
131 sx={{
132 flex: 1,
133 display: 'flex',
134 flexDirection: 'column',
135 minHeight: { xs: 'auto', md: 0 },
136 overflow: { xs: 'visible', md: 'auto' }, // No scroll on mobile (parent scrolls), independent scroll on desktop
137 gap: 3,
138 pb: { xs: 2, md: 0 }, // Add bottom padding on mobile for better spacing
139 }}
140 >
141 <FrequencyInput symbols={symbols} onSymbolsChange={setSymbols} />
143 <Box sx={{ flex: 1, minHeight: 0 }}>
144 <StateGrid symbols={symbols} />
145 </Box>
146 </Box>
147 </Box>
148 </Container>
149 </ThemeProvider>
150 );
153export default App;
moveopenescclose