zeroed-some/bashamole / 0bb9031

Browse files

modal for fhs info

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
0bb90310bc69b39b5b8f92d60b655168fb7858a7
Parents
5d19b36
Tree
102844e

4 changed files

StatusFile+-
M backend/apps/trees/views.py 25 0
M frontend/src/app/globals.css 6 1
M frontend/src/components/Game.tsx 62 23
M frontend/src/lib/api.ts 15 0
backend/apps/trees/views.pymodified
@@ -17,6 +17,31 @@ class FileSystemTreeViewSet(viewsets.ModelViewSet):
1717
     queryset = FileSystemTree.objects.all()
1818
     serializer_class = FileSystemTreeSerializer
1919
     
20
+    @action(detail=False, methods=['get'])
21
+    def fhs_reference(self, request):
22
+        """Get FHS directory reference"""
23
+        fhs_dirs = [
24
+            {"path": "/bin", "name": "bin", "desc": "Essential command binaries"},
25
+            {"path": "/boot", "name": "boot", "desc": "Static files of the boot loader"},
26
+            {"path": "/dev", "name": "dev", "desc": "Device files"},
27
+            {"path": "/etc", "name": "etc", "desc": "Host-specific system configuration"},
28
+            {"path": "/home", "name": "home", "desc": "User home directories"},
29
+            {"path": "/lib", "name": "lib", "desc": "Essential shared libraries and kernel modules"},
30
+            {"path": "/media", "name": "media", "desc": "Mount points for removable media"},
31
+            {"path": "/mnt", "name": "mnt", "desc": "Mount point for temporarily mounted filesystems"},
32
+            {"path": "/opt", "name": "opt", "desc": "Add-on application software packages"},
33
+            {"path": "/proc", "name": "proc", "desc": "Virtual filesystem for process information"},
34
+            {"path": "/root", "name": "root", "desc": "Home directory for the root user"},
35
+            {"path": "/run", "name": "run", "desc": "Data relevant to running processes"},
36
+            {"path": "/sbin", "name": "sbin", "desc": "Essential system binaries"},
37
+            {"path": "/srv", "name": "srv", "desc": "Data for services provided by this system"},
38
+            {"path": "/sys", "name": "sys", "desc": "Virtual filesystem for system information"},
39
+            {"path": "/tmp", "name": "tmp", "desc": "Temporary files"},
40
+            {"path": "/usr", "name": "usr", "desc": "Secondary hierarchy"},
41
+            {"path": "/var", "name": "var", "desc": "Variable data"},
42
+        ]
43
+        return Response({"directories": fhs_dirs})
44
+    
2045
     @action(detail=False, methods=['post'])
2146
     def create_game(self, request):
2247
         """Create a new game with a generated filesystem tree"""
frontend/src/app/globals.cssmodified
@@ -27,7 +27,12 @@ body {
2727
 
2828
 /* Custom terminal font class */
2929
 .font-terminal {
30
-  font-family: var(--font-terminal, 'Space Mono', 'Courier New', monospace);
30
+  font-family: var(--font-terminal, 'Anonymous Pro', 'Courier New', monospace);
31
+}
32
+
33
+/* Hide Next.js dev indicator */
34
+[data-nextjs-portal] {
35
+  display: none !important;
3136
 }
3237
 
3338
 /* Custom scrollbar for terminal */
frontend/src/components/Game.tsxmodified
@@ -3,7 +3,7 @@
33
 // src/components/Game.tsx
44
 import React, { useState, useEffect, useRef } from 'react';
55
 import TreeVisualizer from './TreeVisualizer';
6
-import { gameApi, FileSystemTree } from '@/lib/api';
6
+import { gameApi, FileSystemTree, FHSDirectory } from '@/lib/api';
77
 
88
 interface CommandHistoryEntry {
99
   command: string;
@@ -29,6 +29,8 @@ const Game: React.FC = () => {
2929
   const [executing, setExecuting] = useState(false);
3030
   const [showHints, setShowHints] = useState(false);
3131
   const [hints, setHints] = useState<string[]>([]);
32
+  const [showFHS, setShowFHS] = useState(false);
33
+  const [fhsDirs, setFhsDirs] = useState<FHSDirectory[]>([]);
3234
   const [terminalMinimized, setTerminalMinimized] = useState(true);
3335
   const [hasPlayedIntro, setHasPlayedIntro] = useState(false);
3436
   const [isDarkMode, setIsDarkMode] = useState(true);
@@ -123,6 +125,17 @@ const Game: React.FC = () => {
123125
     }
124126
   };
125127
 
128
+  // Get FHS reference
129
+  const getFHSReference = async () => {
130
+    try {
131
+      const response = await gameApi.getFHSReference();
132
+      setFhsDirs(response.directories);
133
+      setShowFHS(true);
134
+    } catch (error) {
135
+      console.error('Failed to get FHS reference', error);
136
+    }
137
+  };
138
+
126139
   // Execute a command
127140
   const executeCommand = async (cmd: string) => {
128141
     if (!gameState.tree || !cmd.trim() || executing) return;
@@ -298,9 +311,25 @@ const Game: React.FC = () => {
298311
         <div className={`flex items-center justify-between ${terminalColors.header} px-4 py-2 rounded-t-lg border-b`}>
299312
           <div className="flex items-center gap-2">
300313
             <div className="flex gap-1.5">
301
-              <div className="w-3 h-3 bg-red-500 rounded-full"></div>
302
-              <div className="w-3 h-3 bg-yellow-500 rounded-full"></div>
303
-              <div className="w-3 h-3 bg-green-500 rounded-full"></div>
314
+              <div className="w-3.5 h-3.5 bg-red-500 rounded-full"></div>
315
+              {gameState.tree && !gameState.tree.is_completed ? (
316
+                <button
317
+                  onClick={getHints}
318
+                  className="w-3.5 h-3.5 bg-yellow-500 hover:bg-yellow-400 rounded-full flex items-center justify-center transition-colors relative"
319
+                  title="Get Hint"
320
+                >
321
+                  <span className="text-[9px] font-bold text-gray-900 absolute">?</span>
322
+                </button>
323
+              ) : (
324
+                <div className="w-3.5 h-3.5 bg-yellow-500 rounded-full"></div>
325
+              )}
326
+              <button
327
+                onClick={getFHSReference}
328
+                className="w-3.5 h-3.5 bg-green-500 hover:bg-green-400 rounded-full flex items-center justify-center transition-colors relative"
329
+                title="FHS Directory Reference"
330
+              >
331
+                <span className="text-[9px] font-bold text-gray-900 absolute">/</span>
332
+              </button>
304333
             </div>
305334
             <h3 className={`text-sm font-medium ${terminalColors.headerText} ml-2`}>bash</h3>
306335
           </div>
@@ -384,9 +413,9 @@ const Game: React.FC = () => {
384413
         )}
385414
       </div>
386415
 
387
-      {/* Hints Popup */}
416
+      {/* Hints Popup - Now positioned relative to terminal */}
388417
       {showHints && hints.length > 0 && (
389
-        <div className="absolute top-20 left-1/2 transform -translate-x-1/2 bg-yellow-900/95 backdrop-blur-sm border border-yellow-600 rounded-lg p-4 max-w-md z-30 shadow-2xl">
418
+        <div className="absolute top-16 left-4 bg-yellow-900/95 backdrop-blur-sm border border-yellow-600 rounded-lg p-4 max-w-md z-40 shadow-2xl">
390419
           <button
391420
             onClick={() => setShowHints(false)}
392421
             className="absolute top-2 right-2 text-yellow-400 hover:text-yellow-300"
@@ -400,14 +429,32 @@ const Game: React.FC = () => {
400429
         </div>
401430
       )}
402431
 
403
-      {/* Bottom Game Bar */}
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`}>
432
+      {/* FHS Reference Modal */}
433
+      {showFHS && (
434
+        <div className="absolute top-16 left-4 bg-green-900/95 backdrop-blur-sm border border-green-600 rounded-lg p-6 max-w-2xl z-40 shadow-2xl">
435
+          <button
436
+            onClick={() => setShowFHS(false)}
437
+            className="absolute top-3 right-3 text-green-400 hover:text-green-300"
438
+          >
439
+            ✕
440
+          </button>
441
+          <h3 className="text-green-400 font-bold mb-4 text-lg">Filesystem Hierarchy Standard (FHS)</h3>
442
+          <div className="grid grid-cols-1 gap-2 max-h-96 overflow-y-auto">
443
+            {fhsDirs.map((dir, index) => (
444
+              <div key={index} className="flex items-start gap-3">
445
+                <code className="text-green-300 font-terminal text-sm font-bold min-w-[80px]">{dir.path}</code>
446
+                <span className="text-green-200 text-sm">{dir.desc}</span>
447
+              </div>
448
+            ))}
449
+          </div>
450
+        </div>
451
+      )}
452
+
453
+      {/* Bottom Game Bar - Updated with blue shade */}
454
+      <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-4 z-20`}>
405455
         <div className="max-w-7xl mx-auto flex justify-between items-center">
406456
           <div className="flex items-center gap-4">
407457
             <h1 className={`text-2xl font-bold ${isDarkMode ? 'text-white' : 'text-gray-900'}`}><span className='font-terminal bg-gray-200 dark:bg-gray-700 text-red-900 dark:text-red-400 px-1 py-0 rounded'>bash</span>amole</h1>
408
-            {/* <div className={`text-sm ${isDarkMode ? 'text-gray-400' : 'text-gray-600'}`}>
409
-              Location: <span className="font-terminal text-blue-600 dark:text-blue-400">{gameState.tree.player_location}</span>
410
-            </div> */}
411458
           </div>
412459
           
413460
           <div className="flex items-center gap-3">
@@ -416,21 +463,13 @@ const Game: React.FC = () => {
416463
                 You found a mole!
417464
               </div>
418465
             ) : (
419
-              <>
420
-                <button
421
-                  onClick={getHints}
422
-                  className="px-3 py-1.5 bg-yellow-600 text-white text-sm rounded hover:bg-yellow-700 transition"
423
-                >
424
-                  Get Hint
425
-                </button>
426
-                <div className={`text-xs ${isDarkMode ? 'text-gray-500' : 'text-gray-600'}`}>
427
-                  Click nodes or use terminal
428
-                </div>
429
-              </>
466
+              <div className={`text-xs ${isDarkMode ? 'text-slate-400' : 'text-blue-700'}`}>
467
+                Click nodes or use terminal
468
+              </div>
430469
             )}
431470
             <button
432471
               onClick={startNewGame}
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`}
472
+              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`}
434473
             >
435474
               New Game
436475
             </button>
frontend/src/lib/api.tsmodified
@@ -50,6 +50,16 @@ export interface HintResponse {
5050
   hints: string[];
5151
 }
5252
 
53
+export interface FHSDirectory {
54
+  path: string;
55
+  name: string;
56
+  desc: string;
57
+}
58
+
59
+export interface FHSReferenceResponse {
60
+  directories: FHSDirectory[];
61
+}
62
+
5363
 export const gameApi = {
5464
   createGame: async (playerName: string = 'Anonymous'): Promise<GameCreationResponse> => {
5565
     const response = await api.post('/trees/filesystem-trees/create_game/', {
@@ -81,4 +91,9 @@ export const gameApi = {
8191
     const response = await api.get(`/trees/filesystem-trees/${treeId}/current_directory/`);
8292
     return response.data;
8393
   },
94
+
95
+  getFHSReference: async (): Promise<FHSReferenceResponse> => {
96
+    const response = await api.get('/trees/filesystem-trees/fhs_reference/');
97
+    return response.data;
98
+  },
8499
 };