style things stable
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
4b1ad8a8adc4cef3aef9962c23c14e13a5bd4728- Parents
-
703cd03 - Tree
4754a43
4b1ad8a
4b1ad8a8adc4cef3aef9962c23c14e13a5bd4728703cd03
4754a43| Status | File | + | - |
|---|---|---|---|
| D |
frontend/public/file.svg
|
0 | 1 |
| D |
frontend/public/globe.svg
|
0 | 1 |
| A |
frontend/public/mole.png
|
bin | |
| D |
frontend/public/next.svg
|
0 | 1 |
| A |
frontend/public/player.png
|
bin | |
| M |
frontend/public/player.svg
|
7 | 5 |
| D |
frontend/public/vercel.svg
|
0 | 1 |
| D |
frontend/public/window.svg
|
0 | 1 |
| M |
frontend/src/components/Game.tsx
|
67 | 37 |
| M |
frontend/src/components/TreeVisualizer.tsx
|
25 | 8 |
frontend/public/file.svgdeleted@@ -1,1 +0,0 @@ | ||
| 1 | -<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg> | |
frontend/public/globe.svgdeleted@@ -1,1 +0,0 @@ | ||
| 1 | -<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg> | |
frontend/public/mole.pngaddedfrontend/public/next.svgdeleted@@ -1,1 +0,0 @@ | ||
| 1 | -<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg> | |
frontend/public/player.pngaddedfrontend/public/player.svgmodified@@ -1,5 +1,7 @@ | ||
| 1 | -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
| 2 | - <circle cx="12" cy="8" r="3" fill="#3B82F6" stroke="#1E40AF" stroke-width="1.5"/> | |
| 3 | - <path d="M12 14C8 14 5 17 5 21H19C19 17 16 14 12 14Z" fill="#3B82F6" stroke="#1E40AF" stroke-width="1.5"/> | |
| 4 | - <rect x="11" y="11" width="2" height="3" fill="#1E40AF"/> | |
| 5 | -</svg> | |
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<svg width="1200pt" height="1200pt" version="1.1" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg"> | |
| 3 | + <path d="m450 468.75h-37.5c0 6.6992-3.5742 12.887-9.375 16.238-5.8008 3.3477-12.949 3.3477-18.75 0-5.8008-3.3516-9.375-9.5391-9.375-16.238h-37.5c0 20.098 10.723 38.664 28.125 48.715 17.402 10.047 38.848 10.047 56.25 0 17.402-10.051 28.125-28.617 28.125-48.715z"/> | |
| 4 | + <path d="m581.25 525c14.914-0.015625 29.211-5.9492 39.758-16.492 10.543-10.547 16.477-24.844 16.492-39.758h-37.5c0 6.6992-3.5742 12.887-9.375 16.238-5.8008 3.3477-12.949 3.3477-18.75 0-5.8008-3.3516-9.375-9.5391-9.375-16.238h-37.5c0.015625 14.914 5.9492 29.211 16.492 39.758 10.547 10.543 24.844 16.477 39.758 16.492z"/> | |
| 5 | + <path d="m431.25 243.75c0 14.918 5.9258 29.227 16.477 39.773 10.547 10.551 24.855 16.477 39.773 16.477s29.227-5.9258 39.773-16.477c10.551-10.547 16.477-24.855 16.477-39.773s-5.9258-29.227-16.477-39.773c-10.547-10.551-24.855-16.477-39.773-16.477-14.914 0.015625-29.211 5.9492-39.758 16.492-10.543 10.547-16.477 24.844-16.492 39.758zm75 0c0 4.9727-1.9766 9.7422-5.4922 13.258-3.5156 3.5156-8.2852 5.4922-13.258 5.4922s-9.7422-1.9766-13.258-5.4922c-3.5156-3.5156-5.4922-8.2852-5.4922-13.258s1.9766-9.7422 5.4922-13.258c3.5156-3.5156 8.2852-5.4922 13.258-5.4922 4.9727 0.003906 9.7383 1.9844 13.254 5.4961 3.5117 3.5156 5.4922 8.2812 5.4961 13.254z"/> | |
| 6 | + <path d="m926.36 787.34-7.457-7.457c-55.133-55.328-126.26-91.93-203.32-104.63-2.6367-0.43359-5.3359-0.29688-7.918 0.40234 3.2188-12.328 4.8438-25.016 4.8359-37.754v-75.402h38.23c9.75-0.011719 19.094-3.8867 25.988-10.781s10.77-16.238 10.781-25.988v-75.73c6.5 0 12.535-3.3633 15.949-8.8945 3.418-5.5273 3.7266-12.43 0.82031-18.242l-35.52-71.039v-33.074c0.1875-61.207-19.773-120.77-56.805-169.51-33.336-43.902-78.734-77.145-130.65-95.668-0.63281-4.4609-2.8594-8.5469-6.2617-11.5-3.4062-2.9531-7.7656-4.5781-12.273-4.5742h-150.47c-4.5078-0.007812-8.8633 1.6172-12.27 4.5664-3.4062 2.9531-5.6328 7.0352-6.2695 11.496-51.926 18.52-97.336 51.758-130.68 95.66-37.039 48.738-57.004 108.31-56.82 169.53v33.074l-35.52 71.039c-2.9062 5.8125-2.5977 12.715 0.82031 18.242 3.4141 5.5312 9.4492 8.8945 15.949 8.8945v75.73c0.011719 9.75 3.8867 19.094 10.781 25.988s16.238 10.77 25.988 10.781h38.23v75.402c0.042969 39.66 15.82 77.688 43.867 105.73 28.043 28.047 66.07 43.824 105.73 43.867h0.40234v37.5h-75v37.5h86.496c17.754 15.016 40.254 23.254 63.504 23.254s45.75-8.2383 63.504-23.254h161.12c13.117 0.015625 25.82 4.5781 35.945 12.914l-249.59 249.59h-179.73v-131.25c0-4.9727-1.9766-9.7422-5.4922-13.258-3.5156-3.5156-8.2852-5.4922-13.258-5.4922h-93.75v-55.875c0.015625-15.012 5.9883-29.406 16.605-40.02 10.613-10.617 25.008-16.59 40.02-16.605h37.125v-37.5h-37.125c-24.953 0.027344-48.879 9.9531-66.523 27.602-17.648 17.645-27.574 41.57-27.602 66.523v224.62c0 4.9727 1.9766 9.7422 5.4922 13.258 3.5156 3.5156 8.2852 5.4922 13.258 5.4922h600c4.9727 0 9.7422-1.9766 13.258-5.4922 3.5156-3.5156 5.4922-8.2852 5.4922-13.258v-56.25h-37.5v37.5h-104.73l104.73-104.73v29.734h37.5v-67.234l75-75 14.145 14.148c19.84 19.828 41.059 38.227 63.492 55.062l42.363 31.773c4.0195 3.0156 9.0781 4.2891 14.047 3.5391 4.9648-0.74609 9.4219-3.457 12.375-7.5234 2.9531-4.0625 4.1484-9.1445 3.3242-14.098-12.703-77.066-49.305-148.19-104.63-203.32zm-708.53-374.84 18.75-37.5h63.414v-37.5h-56.25v-18.75c0-6.293 0.25391-12.543 0.72266-18.75h124.45c6.6719 13.98 15.754 26.672 26.832 37.5h-58.258v37.5h400.91l18.75 37.5zm388.22-112.5h124.45c0.46875 6.207 0.72266 12.457 0.72266 18.75v18.75h-152c11.078-10.828 20.16-23.52 26.832-37.5zm-24.809-56.25c0 24.863-9.8789 48.711-27.457 66.293-17.582 17.578-41.43 27.457-66.293 27.457s-48.711-9.8789-66.293-27.457c-17.578-17.582-27.457-41.43-27.457-66.293s9.8789-48.711 27.457-66.293c17.582-17.578 41.43-27.457 66.293-27.457 24.855 0.027344 48.684 9.9141 66.262 27.488 17.574 17.578 27.461 41.406 27.488 66.262zm-281.25 206.25h375v103.12l-57.188 42.891c-37.227-21.988-79.68-33.562-122.91-33.516h-14.797c-43.23-0.046875-85.68 11.531-122.9 33.516l-57.199-42.891zm450 75h-37.5v-75h37.5zm-67.969-353.14c20.414 26.898 34.961 57.773 42.707 90.641h-107.34c5.8828-40.43-7.4727-81.285-36.094-110.44l0.13281-58.238c39.793 16.746 74.484 43.66 100.6 78.039zm-138.07-96.859-0.11328 50.234c-35.645-16.977-77.043-16.977-112.69 0l-0.097656-50.234zm-251 96.844c26.125-34.383 60.828-61.293 100.63-78.031l0.11328 58.238-0.003906 0.003907c-28.625 29.152-41.98 70.012-36.098 110.45h-107.34c7.7383-32.871 22.285-63.754 42.699-90.656zm-67.969 353.16v-75h37.5v75zm75 112.9v-37.902l45 33.75c3.1016 2.3281 6.8516 3.6367 10.73 3.7422 3.8789 0.10937 7.6953-0.98828 10.922-3.1406 33.578-22.434 73.066-34.391 113.45-34.352h14.797c40.383-0.039062 79.871 11.918 113.45 34.352 3.2266 2.1523 7.043 3.25 10.922 3.1406 3.8789-0.10547 7.6289-1.4141 10.73-3.7422l45-33.75v37.902c-0.035156 29.719-11.855 58.211-32.871 79.227s-49.508 32.836-79.227 32.871h-150.8c-29.719-0.035156-58.211-11.855-79.227-32.871s-32.836-49.508-32.871-79.227zm225 197.46c-10.711 8.3633-23.91 12.906-37.5 12.906s-26.789-4.543-37.5-12.906v-47.863h75zm187.12-10.363h-149.62v-37.5h0.40234c27.887-0.027344 55.215-7.8438 78.902-22.566 23.684-14.723 42.789-35.766 55.16-60.762 0.18359 0.27344 0.33203 0.56641 0.53125 0.82812l31.773 42.363h-0.003906c16.84 22.438 35.242 43.652 55.078 63.492l14.141 14.145-23.816 23.816c-17.23-15.324-39.484-23.797-62.543-23.816zm-430.88 187.5v37.5h-75v-37.5zm-75 75h75v37.5h-75zm404.73 37.5h-59.469l273.48-273.48 29.734 29.734zm370.4-178.02c-21.016-15.77-40.891-33.008-59.477-51.582l-111.05-111.06c-18.578-18.582-35.812-38.457-51.582-59.473l-0.39844-0.53125c50.363 16.422 96.121 44.547 133.51 82.074l7.457 7.457c37.527 37.391 65.652 83.145 82.074 133.51z"/> | |
| 7 | +</svg> | |
frontend/public/vercel.svgdeleted@@ -1,1 +0,0 @@ | ||
| 1 | -<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg> | |
frontend/public/window.svgdeleted@@ -1,1 +0,0 @@ | ||
| 1 | -<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg> | |
frontend/src/components/Game.tsxmodified@@ -31,10 +31,24 @@ const Game: React.FC = () => { | ||
| 31 | 31 | const [hints, setHints] = useState<string[]>([]); |
| 32 | 32 | const [terminalMinimized, setTerminalMinimized] = useState(true); |
| 33 | 33 | const [hasPlayedIntro, setHasPlayedIntro] = useState(false); |
| 34 | + const [isDarkMode, setIsDarkMode] = useState(true); | |
| 34 | 35 | |
| 35 | 36 | const terminalRef = useRef<HTMLDivElement>(null); |
| 36 | 37 | const inputRef = useRef<HTMLInputElement>(null); |
| 37 | 38 | |
| 39 | + // Detect system color scheme preference | |
| 40 | + useEffect(() => { | |
| 41 | + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); | |
| 42 | + setIsDarkMode(mediaQuery.matches); | |
| 43 | + | |
| 44 | + const handleChange = (e: MediaQueryListEvent) => { | |
| 45 | + setIsDarkMode(e.matches); | |
| 46 | + }; | |
| 47 | + | |
| 48 | + mediaQuery.addEventListener('change', handleChange); | |
| 49 | + return () => mediaQuery.removeEventListener('change', handleChange); | |
| 50 | + }, []); | |
| 51 | + | |
| 38 | 52 | // Auto-scroll terminal to bottom |
| 39 | 53 | useEffect(() => { |
| 40 | 54 | if (terminalRef.current) { |
@@ -180,8 +194,6 @@ const Game: React.FC = () => { | ||
| 180 | 194 | return treeData; |
| 181 | 195 | }; |
| 182 | 196 | |
| 183 | - | |
| 184 | - | |
| 185 | 197 | // Handle node click in visualizer |
| 186 | 198 | const handleNodeClick = (path: string) => { |
| 187 | 199 | executeCommand(`cd ${path}`); |
@@ -203,12 +215,30 @@ const Game: React.FC = () => { | ||
| 203 | 215 | } |
| 204 | 216 | }, [gameState.tree, hasPlayedIntro]); |
| 205 | 217 | |
| 218 | + // Terminal color scheme based on dark/light mode | |
| 219 | + const terminalColors = isDarkMode ? { | |
| 220 | + frame: 'bg-stone-200 border-stone-300', | |
| 221 | + header: 'bg-stone-300 border-stone-400', | |
| 222 | + headerText: 'text-stone-900', | |
| 223 | + content: 'bg-black', | |
| 224 | + closeButton: 'text-stone-700 hover:text-stone-900' | |
| 225 | + } : { | |
| 226 | + frame: 'bg-blue-900 border-blue-800', | |
| 227 | + header: 'bg-blue-800 border-blue-700', | |
| 228 | + headerText: 'text-blue-100', | |
| 229 | + content: 'bg-black', | |
| 230 | + closeButton: 'text-blue-300 hover:text-white' | |
| 231 | + }; | |
| 232 | + | |
| 233 | + // Canvas background color based on dark/light mode | |
| 234 | + const canvasBackground = isDarkMode ? 'bg-gray-900' : 'bg-stone-100'; | |
| 235 | + | |
| 206 | 236 | if (gameState.loading) { |
| 207 | 237 | return ( |
| 208 | - <div className="flex items-center justify-center min-h-screen bg-gray-900 text-white"> | |
| 238 | + <div className={`flex items-center justify-center min-h-screen ${canvasBackground} text-gray-900 dark:text-white`}> | |
| 209 | 239 | <div className="text-center"> |
| 210 | 240 | <div className="text-2xl mb-4">Loading Bashamole...</div> |
| 211 | - <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-white mx-auto"></div> | |
| 241 | + <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-current mx-auto"></div> | |
| 212 | 242 | </div> |
| 213 | 243 | </div> |
| 214 | 244 | ); |
@@ -216,9 +246,9 @@ const Game: React.FC = () => { | ||
| 216 | 246 | |
| 217 | 247 | if (gameState.error) { |
| 218 | 248 | return ( |
| 219 | - <div className="flex items-center justify-center min-h-screen bg-gray-900 text-white"> | |
| 249 | + <div className={`flex items-center justify-center min-h-screen ${canvasBackground} text-gray-900 dark:text-white`}> | |
| 220 | 250 | <div className="text-center"> |
| 221 | - <div className="text-red-400 mb-4">{gameState.error}</div> | |
| 251 | + <div className="text-red-600 dark:text-red-400 mb-4">{gameState.error}</div> | |
| 222 | 252 | <button |
| 223 | 253 | onClick={startNewGame} |
| 224 | 254 | className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition" |
@@ -232,10 +262,10 @@ const Game: React.FC = () => { | ||
| 232 | 262 | |
| 233 | 263 | if (!gameState.tree) { |
| 234 | 264 | return ( |
| 235 | - <div className="flex items-center justify-center min-h-screen bg-gray-900 text-white"> | |
| 265 | + <div className={`flex items-center justify-center min-h-screen ${canvasBackground} text-gray-900 dark:text-white`}> | |
| 236 | 266 | <div className="text-center"> |
| 237 | 267 | <h1 className="text-4xl font-bold mb-4">Bashamole</h1> |
| 238 | - <p className="text-gray-400 mb-8">Hunt the mole in the Unix filesystem!</p> | |
| 268 | + <p className="text-gray-600 dark:text-gray-400 mb-8">Hunt the mole in the Unix filesystem!</p> | |
| 239 | 269 | <button |
| 240 | 270 | onClick={startNewGame} |
| 241 | 271 | className="px-8 py-4 bg-green-600 text-white rounded-lg hover:bg-green-700 text-xl transition transform hover:scale-105" |
@@ -248,34 +278,35 @@ const Game: React.FC = () => { | ||
| 248 | 278 | } |
| 249 | 279 | |
| 250 | 280 | return ( |
| 251 | - <div className="relative min-h-screen bg-gray-900 text-white overflow-hidden"> | |
| 281 | + <div className={`relative min-h-screen ${canvasBackground} overflow-hidden`}> | |
| 252 | 282 | {/* Tree Canvas - Full Screen Background */} |
| 253 | - <div className="absolute inset-0 bg-gray-900"> | |
| 283 | + <div className={`absolute inset-0 ${canvasBackground}`}> | |
| 254 | 284 | <TreeVisualizer |
| 255 | 285 | treeData={gameState.tree.tree_data} |
| 256 | 286 | playerLocation={gameState.tree.player_location} |
| 257 | 287 | onNodeClick={handleNodeClick} |
| 258 | 288 | playIntro={!hasPlayedIntro} |
| 289 | + isDarkMode={isDarkMode} | |
| 259 | 290 | /> |
| 260 | 291 | </div> |
| 261 | 292 | |
| 262 | 293 | {/* Floating Terminal - Top Left */} |
| 263 | - <div className={`absolute top-4 left-4 bg-gray-900 rounded-lg shadow-2xl border border-gray-800 transition-all duration-300 z-30 ${ | |
| 294 | + <div className={`absolute top-4 left-4 ${terminalColors.frame} rounded-lg shadow-2xl border transition-all duration-300 z-30 ${ | |
| 264 | 295 | terminalMinimized ? 'w-80' : 'w-[700px]' |
| 265 | 296 | }`}> |
| 266 | 297 | {/* Terminal Header */} |
| 267 | - <div className="flex items-center justify-between bg-gray-800 px-4 py-2 rounded-t-lg border-b border-gray-700"> | |
| 298 | + <div className={`flex items-center justify-between ${terminalColors.header} px-4 py-2 rounded-t-lg border-b`}> | |
| 268 | 299 | <div className="flex items-center gap-2"> |
| 269 | 300 | <div className="flex gap-1.5"> |
| 270 | 301 | <div className="w-3 h-3 bg-red-500 rounded-full"></div> |
| 271 | 302 | <div className="w-3 h-3 bg-yellow-500 rounded-full"></div> |
| 272 | 303 | <div className="w-3 h-3 bg-green-500 rounded-full"></div> |
| 273 | 304 | </div> |
| 274 | - <h3 className="text-sm font-medium text-gray-300 ml-2">bash</h3> | |
| 305 | + <h3 className={`text-sm font-medium ${terminalColors.headerText} ml-2`}>bash</h3> | |
| 275 | 306 | </div> |
| 276 | 307 | <button |
| 277 | 308 | onClick={() => setTerminalMinimized(!terminalMinimized)} |
| 278 | - className="text-gray-400 hover:text-white transition" | |
| 309 | + className={`${terminalColors.closeButton} transition`} | |
| 279 | 310 | > |
| 280 | 311 | {terminalMinimized ? '▼' : '▲'} |
| 281 | 312 | </button> |
@@ -285,12 +316,12 @@ const Game: React.FC = () => { | ||
| 285 | 316 | {!terminalMinimized && ( |
| 286 | 317 | <div |
| 287 | 318 | ref={terminalRef} |
| 288 | - className="bg-black p-4 font-mono text-base h-[350px] overflow-y-auto scrollbar-thin scrollbar-thumb-gray-700" | |
| 319 | + className={`${terminalColors.content} p-4 font-mono text-base h-[350px] overflow-y-auto scrollbar-thin scrollbar-thumb-gray-700`} | |
| 289 | 320 | onClick={() => inputRef.current?.focus()} |
| 290 | 321 | > |
| 291 | 322 | {commandHistory.map((entry, index) => ( |
| 292 | 323 | <div key={index} className="mb-1"> |
| 293 | - <div className="flex items-start"> | |
| 324 | + <div className="flex items-start font-mono"> | |
| 294 | 325 | <span className="text-green-400">groundskeeper@molehill</span> |
| 295 | 326 | <span className="text-gray-400 mx-1">::</span> |
| 296 | 327 | <span className="text-blue-400">{entry.command.startsWith('Hunt started!') ? '~' : gameState.tree?.player_location || '~'}</span> |
@@ -300,7 +331,7 @@ const Game: React.FC = () => { | ||
| 300 | 331 | </span> |
| 301 | 332 | </div> |
| 302 | 333 | {entry.output && ( |
| 303 | - <div className={`${entry.success ? 'text-gray-300' : 'text-red-400'} ml-0 mt-1`}> | |
| 334 | + <div className={`${entry.success ? 'text-gray-300' : 'text-red-400'} ml-0 mt-1 font-mono whitespace-pre-wrap`}> | |
| 304 | 335 | {entry.output.split('\n').map((line, i) => ( |
| 305 | 336 | <div key={i}>{line}</div> |
| 306 | 337 | ))} |
@@ -310,13 +341,22 @@ const Game: React.FC = () => { | ||
| 310 | 341 | ))} |
| 311 | 342 | |
| 312 | 343 | {/* Current input line */} |
| 313 | - <div className="flex items-start"> | |
| 344 | + <div className="flex items-start font-mono"> | |
| 314 | 345 | <span className="text-green-400">groundskeeper@molehill</span> |
| 315 | 346 | <span className="text-gray-400 mx-1">::</span> |
| 316 | 347 | <span className="text-blue-400">{gameState.tree?.player_location || '~'}</span> |
| 317 | 348 | <span className="text-gray-400 ml-1">$</span> |
| 318 | 349 | <div className="flex-1 ml-2"> |
| 319 | - <div className="relative"> | |
| 350 | + <div className="relative inline-block"> | |
| 351 | + <span className="text-gray-300 font-mono">{command}</span> | |
| 352 | + <span | |
| 353 | + className="text-gray-300 font-mono" | |
| 354 | + style={{ | |
| 355 | + animation: 'blink 1s step-end infinite' | |
| 356 | + }} | |
| 357 | + > | |
| 358 | + _ | |
| 359 | + </span> | |
| 320 | 360 | <input |
| 321 | 361 | ref={inputRef} |
| 322 | 362 | type="text" |
@@ -329,7 +369,7 @@ const Game: React.FC = () => { | ||
| 329 | 369 | } |
| 330 | 370 | }} |
| 331 | 371 | disabled={executing || gameState.tree?.is_completed} |
| 332 | - className="w-full bg-transparent text-gray-300 outline-none caret-transparent" | |
| 372 | + className="absolute inset-0 w-full bg-transparent text-transparent outline-none caret-transparent font-mono" | |
| 333 | 373 | placeholder="" |
| 334 | 374 | autoFocus |
| 335 | 375 | spellCheck={false} |
@@ -337,16 +377,6 @@ const Game: React.FC = () => { | ||
| 337 | 377 | autoCorrect="off" |
| 338 | 378 | autoCapitalize="off" |
| 339 | 379 | /> |
| 340 | - {/* Blinking cursor */} | |
| 341 | - <span | |
| 342 | - className="absolute text-gray-300 pointer-events-none" | |
| 343 | - style={{ | |
| 344 | - left: `${command.length * 0.6}em`, | |
| 345 | - animation: 'blink 1s step-end infinite' | |
| 346 | - }} | |
| 347 | - > | |
| 348 | - _ | |
| 349 | - </span> | |
| 350 | 380 | </div> |
| 351 | 381 | </div> |
| 352 | 382 | </div> |
@@ -371,18 +401,18 @@ const Game: React.FC = () => { | ||
| 371 | 401 | )} |
| 372 | 402 | |
| 373 | 403 | {/* Bottom Game Bar */} |
| 374 | - <div className="absolute bottom-0 left-0 right-0 bg-gray-900/90 backdrop-blur-sm border-t border-gray-700 p-4 z-20"> | |
| 404 | + <div className={`absolute bottom-0 left-0 right-0 ${isDarkMode ? 'bg-gray-900/90' : 'bg-white/90'} backdrop-blur-sm border-t ${isDarkMode ? 'border-gray-700' : 'border-gray-300'} p-4 z-20`}> | |
| 375 | 405 | <div className="max-w-7xl mx-auto flex justify-between items-center"> |
| 376 | 406 | <div className="flex items-center gap-4"> |
| 377 | - <h1 className="text-2xl font-bold">Bashamole</h1> | |
| 378 | - <div className="text-sm text-gray-400"> | |
| 379 | - Location: <span className="font-mono text-blue-400">{gameState.tree.player_location}</span> | |
| 407 | + <h1 className={`text-2xl font-bold ${isDarkMode ? 'text-white' : 'text-gray-900'}`}>Bashamole</h1> | |
| 408 | + <div className={`text-sm ${isDarkMode ? 'text-gray-400' : 'text-gray-600'}`}> | |
| 409 | + Location: <span className="font-mono text-blue-600 dark:text-blue-400">{gameState.tree.player_location}</span> | |
| 380 | 410 | </div> |
| 381 | 411 | </div> |
| 382 | 412 | |
| 383 | 413 | <div className="flex items-center gap-3"> |
| 384 | 414 | {gameState.tree.is_completed ? ( |
| 385 | - <div className="text-green-400 font-bold animate-pulse"> | |
| 415 | + <div className="text-green-600 dark:text-green-400 font-bold animate-pulse"> | |
| 386 | 416 | You found the mole! |
| 387 | 417 | </div> |
| 388 | 418 | ) : ( |
@@ -393,14 +423,14 @@ const Game: React.FC = () => { | ||
| 393 | 423 | > |
| 394 | 424 | Get Hint |
| 395 | 425 | </button> |
| 396 | - <div className="text-xs text-gray-500"> | |
| 426 | + <div className={`text-xs ${isDarkMode ? 'text-gray-500' : 'text-gray-600'}`}> | |
| 397 | 427 | Click nodes or use terminal |
| 398 | 428 | </div> |
| 399 | 429 | </> |
| 400 | 430 | )} |
| 401 | 431 | <button |
| 402 | 432 | onClick={startNewGame} |
| 403 | - className="px-3 py-1.5 bg-gray-700 text-white text-sm rounded hover:bg-gray-600 transition" | |
| 433 | + className={`px-3 py-1.5 ${isDarkMode ? 'bg-gray-700 hover:bg-gray-600' : 'bg-gray-300 hover:bg-gray-400'} text-white dark:text-white text-gray-900 text-sm rounded transition`} | |
| 404 | 434 | > |
| 405 | 435 | New Game |
| 406 | 436 | </button> |
frontend/src/components/TreeVisualizer.tsxmodified@@ -10,6 +10,7 @@ interface TreeVisualizerProps { | ||
| 10 | 10 | playerLocation: string; |
| 11 | 11 | onNodeClick?: (path: string) => void; |
| 12 | 12 | playIntro?: boolean; |
| 13 | + isDarkMode?: boolean; | |
| 13 | 14 | } |
| 14 | 15 | |
| 15 | 16 | const TreeVisualizer: React.FC<TreeVisualizerProps> = ({ |
@@ -17,6 +18,7 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({ | ||
| 17 | 18 | playerLocation, |
| 18 | 19 | onNodeClick, |
| 19 | 20 | playIntro = true, |
| 21 | + isDarkMode = true, | |
| 20 | 22 | }) => { |
| 21 | 23 | const svgRef = useRef<SVGSVGElement>(null); |
| 22 | 24 | const containerRef = useRef<HTMLDivElement>(null); |
@@ -67,8 +69,14 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({ | ||
| 67 | 69 | margin: { top: 100, right: 150, bottom: 100, left: 150 }, |
| 68 | 70 | viewBoxMultiplier: 2, |
| 69 | 71 | minHeight: 1200, |
| 70 | - grid: { size: 40, strokeColor: '#1f2937' }, | |
| 71 | - background: { color: '#111827', opacity: 0.95 } | |
| 72 | + grid: { | |
| 73 | + size: 40, | |
| 74 | + strokeColor: isDarkMode ? '#1f2937' : '#d6d3d1' | |
| 75 | + }, | |
| 76 | + background: { | |
| 77 | + color: isDarkMode ? '#111827' : '#f5f5f4', | |
| 78 | + opacity: isDarkMode ? 0.95 : 1 | |
| 79 | + } | |
| 72 | 80 | }; |
| 73 | 81 | |
| 74 | 82 | const ZOOM_CONFIG = { |
@@ -82,8 +90,14 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({ | ||
| 82 | 90 | const LINK_CONFIG = { |
| 83 | 91 | gradient: { |
| 84 | 92 | id: 'link-gradient-v', |
| 85 | - start: { color: '#4B5563', opacity: 0.6 }, | |
| 86 | - end: { color: '#6B7280', opacity: 0.3 } | |
| 93 | + start: { | |
| 94 | + color: isDarkMode ? '#4B5563' : '#78716c', | |
| 95 | + opacity: isDarkMode ? 0.6 : 0.4 | |
| 96 | + }, | |
| 97 | + end: { | |
| 98 | + color: isDarkMode ? '#6B7280' : '#a8a29e', | |
| 99 | + opacity: isDarkMode ? 0.3 : 0.2 | |
| 100 | + } | |
| 87 | 101 | }, |
| 88 | 102 | strokeWidth: 2, |
| 89 | 103 | opacity: 0.8 |
@@ -93,8 +107,11 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({ | ||
| 93 | 107 | fontSize: 14, |
| 94 | 108 | fontWeight: { base: '500', player: '700' }, |
| 95 | 109 | offset: { parent: -32, leaf: 40 }, |
| 96 | - colors: { player: '#93C5FD', regular: '#E5E7EB' }, | |
| 97 | - textShadow: '0 0 4px rgba(0,0,0,0.8)' | |
| 110 | + colors: { | |
| 111 | + player: isDarkMode ? '#93C5FD' : '#1e40af', | |
| 112 | + regular: isDarkMode ? '#E5E7EB' : '#44403c' | |
| 113 | + }, | |
| 114 | + textShadow: isDarkMode ? '0 0 4px rgba(0,0,0,0.8)' : '0 0 4px rgba(255,255,255,0.8)' | |
| 98 | 115 | }; |
| 99 | 116 | |
| 100 | 117 | const GLOW_CONFIG = { |
@@ -175,7 +192,7 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({ | ||
| 175 | 192 | .attr('width', '100%') |
| 176 | 193 | .attr('height', '100%') |
| 177 | 194 | .attr('fill', 'url(#grid)') |
| 178 | - .style('opacity', 0.3); | |
| 195 | + .style('opacity', isDarkMode ? 0.3 : 0.1); | |
| 179 | 196 | |
| 180 | 197 | const g = svg |
| 181 | 198 | .append('g') |
@@ -486,7 +503,7 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({ | ||
| 486 | 503 | } |
| 487 | 504 | } |
| 488 | 505 | |
| 489 | - }, [treeData, playerLocation, onNodeClick, playIntro]); | |
| 506 | + }, [treeData, playerLocation, onNodeClick, playIntro, isDarkMode]); | |
| 490 | 507 | |
| 491 | 508 | return ( |
| 492 | 509 | <div ref={containerRef} className="w-full h-full"> |