force color scheme, fix particles
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
f3f39f7fccc129027b9c5d5a0e84e0c874c280b6- Parents
-
09d4e1c - Tree
f6badd4
f3f39f7
f3f39f7fccc129027b9c5d5a0e84e0c874c280b609d4e1c
f6badd4| Status | File | + | - |
|---|---|---|---|
| M |
frontend/package-lock.json
|
1 | 1 |
| M |
frontend/src/components/Game.tsx
|
8 | 27 |
| M |
frontend/src/components/TreeVisualizer.tsx
|
32 | 22 |
frontend/package-lock.jsonmodified@@ -2536,7 +2536,7 @@ | |||
| 2536 | "node_modules/call-bound": { | 2536 | "node_modules/call-bound": { |
| 2537 | "version": "1.0.4", | 2537 | "version": "1.0.4", |
| 2538 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", | 2538 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", |
| 2539 | - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", | 2539 | + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0joshKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", |
| 2540 | "dev": true, | 2540 | "dev": true, |
| 2541 | "license": "MIT", | 2541 | "license": "MIT", |
| 2542 | "dependencies": { | 2542 | "dependencies": { |
frontend/src/components/Game.tsxmodified@@ -44,19 +44,6 @@ const Game: React.FC = () => { | |||
| 44 | const terminalRef = useRef<HTMLDivElement>(null); | 44 | const terminalRef = useRef<HTMLDivElement>(null); |
| 45 | const inputRef = useRef<HTMLInputElement>(null); | 45 | const inputRef = useRef<HTMLInputElement>(null); |
| 46 | 46 | ||
| 47 | - // Detect system color scheme preference | ||
| 48 | - useEffect(() => { | ||
| 49 | - const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); | ||
| 50 | - setIsDarkMode(mediaQuery.matches); | ||
| 51 | - | ||
| 52 | - const handleChange = (e: MediaQueryListEvent) => { | ||
| 53 | - setIsDarkMode(e.matches); | ||
| 54 | - }; | ||
| 55 | - | ||
| 56 | - mediaQuery.addEventListener('change', handleChange); | ||
| 57 | - return () => mediaQuery.removeEventListener('change', handleChange); | ||
| 58 | - }, []); | ||
| 59 | - | ||
| 60 | // Auto-scroll terminal to bottom | 47 | // Auto-scroll terminal to bottom |
| 61 | useEffect(() => { | 48 | useEffect(() => { |
| 62 | if (terminalRef.current) { | 49 | if (terminalRef.current) { |
@@ -334,23 +321,17 @@ const Game: React.FC = () => { | |||
| 334 | return `rotate(${angle}deg)`; | 321 | return `rotate(${angle}deg)`; |
| 335 | }; | 322 | }; |
| 336 | 323 | ||
| 337 | - // Terminal color scheme based on dark/light mode | 324 | + // Terminal color scheme - always dark mode |
| 338 | - const terminalColors = isDarkMode ? { | 325 | + const terminalColors = { |
| 339 | frame: 'bg-stone-200 border-stone-300', | 326 | frame: 'bg-stone-200 border-stone-300', |
| 340 | header: 'bg-stone-300 border-stone-400', | 327 | header: 'bg-stone-300 border-stone-400', |
| 341 | headerText: 'text-stone-900', | 328 | headerText: 'text-stone-900', |
| 342 | content: 'bg-black', | 329 | content: 'bg-black', |
| 343 | closeButton: 'text-stone-700 hover:text-stone-900' | 330 | closeButton: 'text-stone-700 hover:text-stone-900' |
| 344 | - } : { | ||
| 345 | - frame: 'bg-blue-900 border-blue-800', | ||
| 346 | - header: 'bg-blue-800 border-blue-700', | ||
| 347 | - headerText: 'text-blue-100', | ||
| 348 | - content: 'bg-black', | ||
| 349 | - closeButton: 'text-blue-300 hover:text-white' | ||
| 350 | }; | 331 | }; |
| 351 | 332 | ||
| 352 | - // Canvas background color based on dark/light mode | 333 | + // Canvas background color - always dark mode |
| 353 | - const canvasBackground = isDarkMode ? 'bg-gray-900' : 'bg-stone-100'; | 334 | + const canvasBackground = 'bg-gray-900'; |
| 354 | 335 | ||
| 355 | if (gameState.loading) { | 336 | if (gameState.loading) { |
| 356 | return ( | 337 | return ( |
@@ -716,19 +697,19 @@ const Game: React.FC = () => { | |||
| 716 | )} | 697 | )} |
| 717 | 698 | ||
| 718 | {/* Bottom Game Bar */} | 699 | {/* Bottom Game Bar */} |
| 719 | - <div className={`absolute bottom-0 left-0 right-0 ${isDarkMode ? 'bg-slate-800/90' : 'bg-blue-50/90'} backdrop-blur-sm border-t ${isDarkMode ? 'border-slate-700' : 'border-blue-200'} p-3 z-20`}> | 700 | + <div className="absolute bottom-0 left-0 right-0 bg-slate-800/90 backdrop-blur-sm border-t border-slate-700 p-3 z-20"> |
| 720 | <div className="max-w-7xl mx-auto flex justify-between items-center px-4"> | 701 | <div className="max-w-7xl mx-auto flex justify-between items-center px-4"> |
| 721 | <div className="flex items-center gap-4"> | 702 | <div className="flex items-center gap-4"> |
| 722 | - <h1 className={`text-2xl font-bold ${isDarkMode ? 'text-white' : 'text-gray-900'}`}><span className='font-terminal bg-gray-200 dark:bg-gray-500 text-red-900 dark:text-red-400 px-0.5 py-0 rounded'>bash</span> amole</h1> | 703 | + <h1 className="text-2xl font-bold text-white"><span className='font-terminal bg-gray-200 dark:bg-gray-500 text-red-900 dark:text-red-400 px-0.5 py-0 rounded'>bash</span> amole</h1> |
| 723 | </div> | 704 | </div> |
| 724 | 705 | ||
| 725 | <div className="flex items-center gap-3"> | 706 | <div className="flex items-center gap-3"> |
| 726 | - <div className={`text-xs ${isDarkMode ? 'text-slate-400' : 'text-blue-700'}`}> | 707 | + <div className="text-xs text-slate-400"> |
| 727 | click adjacent nodes or use the terminal | 708 | click adjacent nodes or use the terminal |
| 728 | </div> | 709 | </div> |
| 729 | <button | 710 | <button |
| 730 | onClick={startNewGame} | 711 | onClick={startNewGame} |
| 731 | - className={`px-3 py-1.5 ${isDarkMode ? 'bg-slate-700 hover:bg-slate-600' : 'bg-blue-200 hover:bg-blue-300'} ${isDarkMode ? 'text-white' : 'text-blue-900'} text-sm rounded transition`} | 712 | + className="px-3 py-1.5 bg-slate-700 hover:bg-slate-600 text-white text-sm rounded transition" |
| 732 | > | 713 | > |
| 733 | New Game | 714 | New Game |
| 734 | </button> | 715 | </button> |
frontend/src/components/TreeVisualizer.tsxmodified@@ -116,7 +116,7 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({ | |||
| 116 | viewBoxMultiplier: 2.5, | 116 | viewBoxMultiplier: 2.5, |
| 117 | minHeight: 1200, | 117 | minHeight: 1200, |
| 118 | background: { | 118 | background: { |
| 119 | - color: isDarkMode ? '#0F172A' : '#FEF3C7', | 119 | + color: '#0F172A', // Always dark mode color |
| 120 | opacity: 1 | 120 | opacity: 1 |
| 121 | } | 121 | } |
| 122 | }; | 122 | }; |
@@ -126,9 +126,9 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({ | |||
| 126 | opacity: 0.6, | 126 | opacity: 0.6, |
| 127 | dashArray: '5,5', | 127 | dashArray: '5,5', |
| 128 | colors: { | 128 | colors: { |
| 129 | - default: isDarkMode ? '#475569' : '#92400E', | 129 | + default: '#475569', // Always dark mode colors |
| 130 | - hover: isDarkMode ? '#64748B' : '#DC2626', | 130 | + hover: '#64748B', |
| 131 | - adjacent: isDarkMode ? '#3B82F6' : '#2563EB' | 131 | + adjacent: '#3B82F6' |
| 132 | } | 132 | } |
| 133 | }; | 133 | }; |
| 134 | 134 | ||
@@ -137,12 +137,12 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({ | |||
| 137 | fontWeight: { base: '600', player: '800' }, | 137 | fontWeight: { base: '600', player: '800' }, |
| 138 | offset: { parent: -38, leaf: 44 }, | 138 | offset: { parent: -38, leaf: 44 }, |
| 139 | colors: { | 139 | colors: { |
| 140 | - player: isDarkMode ? '#93C5FD' : '#1E40AF', | 140 | + player: '#93C5FD', // Always dark mode colors |
| 141 | - regular: isDarkMode ? '#E5E7EB' : '#451A03', | 141 | + regular: '#E5E7EB', |
| 142 | mole: '#DC2626' | 142 | mole: '#DC2626' |
| 143 | }, | 143 | }, |
| 144 | background: { | 144 | background: { |
| 145 | - fill: isDarkMode ? 'rgba(15, 23, 42, 0.9)' : 'rgba(254, 243, 199, 0.9)', | 145 | + fill: 'rgba(15, 23, 42, 0.9)', // Always dark mode background |
| 146 | padding: { x: 8, y: 4 }, | 146 | padding: { x: 8, y: 4 }, |
| 147 | radius: 4 | 147 | radius: 4 |
| 148 | } | 148 | } |
@@ -238,38 +238,48 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({ | |||
| 238 | feMerge2.append('feMergeNode').attr('in', 'offsetblur'); | 238 | feMerge2.append('feMergeNode').attr('in', 'offsetblur'); |
| 239 | feMerge2.append('feMergeNode').attr('in', 'SourceGraphic'); | 239 | feMerge2.append('feMergeNode').attr('in', 'SourceGraphic'); |
| 240 | 240 | ||
| 241 | - // Quirky background with floating particles | 241 | + // Create a background group that won't be affected by zoom |
| 242 | - svg.append('rect') | 242 | + const bgGroup = svg.append('g').attr('class', 'background-group'); |
| 243 | - .attr('width', '100%') | 243 | + |
| 244 | - .attr('height', '100%') | 244 | + // Quirky background that covers the entire viewport |
| 245 | + bgGroup.append('rect') | ||
| 246 | + .attr('x', -width) | ||
| 247 | + .attr('y', -height) | ||
| 248 | + .attr('width', width * 3) | ||
| 249 | + .attr('height', height * 3) | ||
| 245 | .attr('fill', LAYOUT_CONFIG.background.color) | 250 | .attr('fill', LAYOUT_CONFIG.background.color) |
| 246 | .style('opacity', LAYOUT_CONFIG.background.opacity); | 251 | .style('opacity', LAYOUT_CONFIG.background.opacity); |
| 247 | 252 | ||
| 248 | - // Add floating background particles | 253 | + // Add floating background particles across a larger area |
| 249 | - const particlesGroup = svg.append('g').attr('class', 'particles'); | 254 | + const particlesGroup = bgGroup.append('g').attr('class', 'particles'); |
| 255 | + | ||
| 256 | + // Create particles across a much larger area to ensure coverage | ||
| 257 | + const particleCount = PARTICLE_CONFIG.count * 5; | ||
| 250 | 258 | ||
| 251 | - for (let i = 0; i < PARTICLE_CONFIG.count; i++) { | 259 | + for (let i = 0; i < particleCount; i++) { |
| 260 | + const startX = (Math.random() - 0.5) * width * 3; | ||
| 252 | const particle = particlesGroup.append('circle') | 261 | const particle = particlesGroup.append('circle') |
| 253 | - .attr('cx', Math.random() * width) | 262 | + .attr('cx', startX) |
| 254 | - .attr('cy', Math.random() * height) | 263 | + .attr('cy', Math.random() * height * 3 - height) |
| 255 | .attr('r', Math.random() * (PARTICLE_CONFIG.size.max - PARTICLE_CONFIG.size.min) + PARTICLE_CONFIG.size.min) | 264 | .attr('r', Math.random() * (PARTICLE_CONFIG.size.max - PARTICLE_CONFIG.size.min) + PARTICLE_CONFIG.size.min) |
| 256 | .attr('fill', PARTICLE_CONFIG.colors[Math.floor(Math.random() * PARTICLE_CONFIG.colors.length)]) | 265 | .attr('fill', PARTICLE_CONFIG.colors[Math.floor(Math.random() * PARTICLE_CONFIG.colors.length)]) |
| 257 | .attr('opacity', 0.3); | 266 | .attr('opacity', 0.3); |
| 258 | 267 | ||
| 259 | // Animate particles floating | 268 | // Animate particles floating |
| 269 | + const duration = Math.random() * (PARTICLE_CONFIG.speed.max - PARTICLE_CONFIG.speed.min) + PARTICLE_CONFIG.speed.min; | ||
| 260 | particle | 270 | particle |
| 261 | .transition() | 271 | .transition() |
| 262 | - .duration(Math.random() * (PARTICLE_CONFIG.speed.max - PARTICLE_CONFIG.speed.min) + PARTICLE_CONFIG.speed.min) | 272 | + .duration(duration) |
| 263 | .ease(d3.easeLinear) | 273 | .ease(d3.easeLinear) |
| 264 | - .attr('cy', -20) | 274 | + .attr('cy', -height) |
| 265 | .on('end', function repeat() { | 275 | .on('end', function repeat() { |
| 266 | d3.select(this) | 276 | d3.select(this) |
| 267 | - .attr('cy', height + 20) | 277 | + .attr('cy', height * 2) |
| 268 | - .attr('cx', Math.random() * width) | 278 | + .attr('cx', (Math.random() - 0.5) * width * 3) |
| 269 | .transition() | 279 | .transition() |
| 270 | - .duration(Math.random() * (PARTICLE_CONFIG.speed.max - PARTICLE_CONFIG.speed.min) + PARTICLE_CONFIG.speed.min) | 280 | + .duration(duration) |
| 271 | .ease(d3.easeLinear) | 281 | .ease(d3.easeLinear) |
| 272 | - .attr('cy', -20) | 282 | + .attr('cy', -height) |
| 273 | .on('end', repeat); | 283 | .on('end', repeat); |
| 274 | }); | 284 | }); |
| 275 | } | 285 | } |