Skip to content

Instantly share code, notes, and snippets.

@maxim-ge
Created January 20, 2026 16:24
Show Gist options
  • Select an option

  • Save maxim-ge/8aa3c371dd51a1754a3dace4e6329ff9 to your computer and use it in GitHub Desktop.

Select an option

Save maxim-ge/8aa3c371dd51a1754a3dace4e6329ff9 to your computer and use it in GitHub Desktop.
explorer-design.md

File Explorer - Design Document

Overview

A web-based file explorer that enables users to navigate, browse, and manage files and directories on the local filesystem. The application provides a responsive interface for directory navigation, file previews (images and PDFs), file operations (create, delete, rename, move), search functionality, and breadcrumb navigation. Built with React and Tailwind CSS frontend communicating with a Go backend that serves the local filesystem.

System Configuration

Runtime:

  • Node.js 22.x LTS+ must be installed on the system for frontend development
    • Installation: winget install OpenJS.NodeJS.LTS or https://nodejs.org/
  • Go 1.22+ must be installed on the system for backend development
    • Installation: winget install GoLang.Go or https://go.dev/dl/

Project Configuration

Frontend Dependencies:

  • package.json: React 19.x must be added for UI framework
    • npm install react@19 react-dom@19
  • package.json: Tailwind CSS 4.x must be added for styling
    • npm install -D tailwindcss@4 postcss autoprefixer
  • package.json: React Router 7.x must be added for client-side routing
    • npm install react-router-dom@7
  • package.json: React PDF must be added for PDF preview functionality
    • npm install react-pdf pdfjs-dist
  • package.json: Lucide React must be added for icon components
    • npm install lucide-react
  • package.json: Vite must be added as build tool and dev server
    • npm install -D vite @vitejs/plugin-react

Frontend Configuration:

Backend Dependencies:

  • go.mod: Go module initialization for backend
    • go mod init github.com/maxim-ge/seeai0/examples/explorer
  • go.mod: Chi router must be added for HTTP routing (lightweight alternative)
    • go get -u github.com/go-chi/chi/v5
  • go.mod: Chi CORS middleware must be added for cross-origin requests
    • go get -u github.com/go-chi/cors

Project Structure:

  • frontend/: React frontend application directory
    • Manual creation
  • backend/: Go backend application directory
    • Manual creation

Architecture

Feature Components

Utility Components

  • apiClient: object

    • HTTP client wrapper for backend API calls with error handling
    • Used by: useFileExplorer, useFileOperations
  • pathUtils: object

    • Path manipulation utilities (join, normalize, parent directory)
    • Used by: BreadcrumbNav, useFileExplorer
  • fileTypeDetector: object

    • File type detection from extensions (image, PDF, text, directory)
    • Used by: FileList, FilePreview
  • formatUtils: object

    • File size formatting and date formatting utilities
    • Used by: FileList
  • PathValidator: struct

    • Path validation and sanitization to prevent directory traversal attacks
    • Used by: FileService
  • ErrorHandler: struct

    • Centralized error handling and HTTP error response formatting
    • Used by: FileHandler

External Components

  • [fetch: Web API](browser built-in)
    • Browser fetch API for HTTP requests
    • Used by: apiClient
  • react-pdf: library
    • PDF rendering library for preview functionality
    • Used by: FilePreview
  • lucide-react: library
    • Icon library for UI elements
    • Used by: FileList, BreadcrumbNav, FileOperationsMenu, SearchBar
  • [os: package](Go standard library)
    • Operating system file operations
    • Used by: FileSystemRepository
  • [path/filepath: package](Go standard library)
    • File path manipulation utilities
    • Used by: FileService, PathValidator
  • [net/http: package](Go standard library)
    • HTTP server and request handling
    • Used by: Server, FileHandler
  • chi: package
    • HTTP router for RESTful API
    • Used by: Server

Data Structure

Frontend State:

  • FileExplorerState: interface
    • State with currentPath (string), files (FileItem[]), selectedFile (FileItem | null), isLoading (bool), error (string | null)
  • FileItem: interface
    • File metadata with name (string), path (string), isDirectory (bool), size (number), modifiedAt (ISO8601), extension (string)
  • FileOperationState: interface
    • Operation state with isProcessing (bool), operationType (enum: CREATE|DELETE|RENAME|MOVE), error (string | null)

API Contracts:

Domain Models:

  • FileEntry: struct
    • Domain model with path (string), name (string), isDirectory (bool), size (int64), modTime (time.Time), permissions (os.FileMode)

Flows

Flow 1: User Navigates to Directory

User clicks on a directory to navigate into it and view its contents.

sequenceDiagram
    actor 👤User
    participant 🎯FileList
    participant 🎯useFileExplorer
    participant 🔧apiClient
    participant 📦FileHandler
    participant 📦FileService
    participant 📦FileSystemRepository

    👤User->>🎯FileList: clicks directory item
    🎯FileList->>🎯useFileExplorer: navigateToDirectory(path)
    🎯useFileExplorer->>🎯useFileExplorer: setLoading(true)
    🎯useFileExplorer->>🔧apiClient: GET /api/files?path={path}
    🔧apiClient->>📦FileHandler: handleListFiles(request)
    📦FileHandler->>📦FileService: listFiles(path)
    📦FileService->>📦PathValidator: validatePath(path)

    alt path is invalid or unsafe
        📦PathValidator-->>📦FileService: validation error
        📦FileService-->>📦FileHandler: error response
        📦FileHandler-->>🔧apiClient: 400 Bad Request
        🔧apiClient-->>🎯useFileExplorer: error
        🎯useFileExplorer-->>🎯FileList: displays error message
    else path is valid
        📦PathValidator-->>📦FileService: validation success
        📦FileService->>📦FileSystemRepository: readDirectory(path)
        📦FileSystemRepository-->>📦FileService: FileEntry[]
        📦FileService-->>📦FileHandler: ListFilesResponse
        📦FileHandler-->>🔧apiClient: 200 OK with files
        🔧apiClient-->>🎯useFileExplorer: files data
        🎯useFileExplorer->>🎯useFileExplorer: updateState(files, path)
        🎯useFileExplorer-->>🎯FileList: renders file list
    end
Loading

Flow 2: User Previews File

User clicks on an image or PDF file to preview its content.

sequenceDiagram
    actor 👤User
    participant 🎯FileList
    participant 🎯FilePreview
    participant 🔧apiClient
    participant 📦FileHandler
    participant 📦FileSystemRepository

    👤User->>🎯FileList: clicks file (image/PDF)
    🎯FileList->>🎯FilePreview: openPreview(fileItem)
    🎯FilePreview->>🔧apiClient: GET /api/files/content?path={path}
    🔧apiClient->>📦FileHandler: handleGetFileContent(request)
    📦FileHandler->>📦FileSystemRepository: readFile(path)

    alt file not found or access denied
        📦FileSystemRepository-->>📦FileHandler: error
        📦FileHandler-->>🔧apiClient: 404 Not Found or 403 Forbidden
        🔧apiClient-->>🎯FilePreview: error state
        🎯FilePreview-->>👤User: displays error message
    else file read success
        📦FileSystemRepository-->>📦FileHandler: file content (binary)
        📦FileHandler-->>🔧apiClient: 200 OK with content + Content-Type
        🔧apiClient-->>🎯FilePreview: file data

        alt file is image
            🎯FilePreview->>🎯FilePreview: renders <img> with blob URL
        else file is PDF
            🎯FilePreview->>📦react-pdf: render PDF document
            📦react-pdf-->>🎯FilePreview: displays PDF viewer
        end

        🎯FilePreview-->>👤User: shows preview modal
    end
Loading

Flow 3: User Deletes File

User deletes a file or directory through the context menu.

sequenceDiagram
    actor 👤User
    participant 🎯FileOperationsMenu
    participant 🎯useFileOperations
    participant 🔧apiClient
    participant 📦FileHandler
    participant 📦FileService
    participant 📦FileSystemRepository
    participant 🎯useFileExplorer

    👤User->>🎯FileOperationsMenu: right-clicks file, selects "Delete"
    🎯FileOperationsMenu->>🎯FileOperationsMenu: shows confirmation dialog
    👤User->>🎯FileOperationsMenu: confirms deletion
    🎯FileOperationsMenu->>🎯useFileOperations: deleteFile(path)
    🎯useFileOperations->>🎯useFileOperations: setProcessing(true)
    🎯useFileOperations->>🔧apiClient: DELETE /api/files with path
    🔧apiClient->>📦FileHandler: handleDeleteFile(request)
    📦FileHandler->>📦FileService: deleteFile(path)
    📦FileService->>📦FileSystemRepository: remove(path)

    alt deletion fails
        📦FileSystemRepository-->>📦FileService: error (permission denied, not found)
        📦FileService-->>📦FileHandler: error response
        📦FileHandler-->>🔧apiClient: 403 Forbidden or 404 Not Found
        🔧apiClient-->>🎯useFileOperations: error
        🎯useFileOperations-->>🎯FileOperationsMenu: displays error message
    else deletion succeeds
        📦FileSystemRepository-->>📦FileService: success
        📦FileService-->>📦FileHandler: success response
        📦FileHandler-->>🔧apiClient: 200 OK
        🔧apiClient-->>🎯useFileOperations: success
        🎯useFileOperations->>🎯useFileExplorer: refreshCurrentDirectory()
        🎯useFileExplorer-->>🎯FileOperationsMenu: updates file list
        🎯FileOperationsMenu-->>👤User: shows success notification
    end
Loading

Flow 4: User Searches for Files

User enters search query to filter files in current directory.

sequenceDiagram
    actor 👤User
    participant 🎯SearchBar
    participant 🎯useFileExplorer
    participant 🔧apiClient
    participant 📦FileHandler
    participant 📦FileService
    participant 📦FileSystemRepository

    👤User->>🎯SearchBar: types search query
    🎯SearchBar->>🎯SearchBar: debounce input (300ms)
    🎯SearchBar->>🎯useFileExplorer: searchFiles(query, currentPath)
    🎯useFileExplorer->>🔧apiClient: GET /api/files/search?path={path}&query={query}
    🔧apiClient->>📦FileHandler: handleSearchFiles(request)
    📦FileHandler->>📦FileService: searchFiles(path, query)
    📦FileService->>📦FileSystemRepository: readDirectory(path)
    📦FileSystemRepository-->>📦FileService: FileEntry[]
    📦FileService->>📦FileService: filterByName(entries, query)
    📦FileService-->>📦FileHandler: filtered results
    📦FileHandler-->>🔧apiClient: 200 OK with filtered files
    🔧apiClient-->>🎯useFileExplorer: search results
    🎯useFileExplorer-->>🎯SearchBar: updates file list with results
    🎯SearchBar-->>👤User: displays filtered files
Loading

Flow 5: User Creates New Directory

User creates a new directory through the file operations menu.

sequenceDiagram
    actor 👤User
    participant 🎯FileOperationsMenu
    participant 🎯useFileOperations
    participant 🔧apiClient
    participant 📦FileHandler
    participant 📦FileService
    participant 📦FileSystemRepository
    participant 🎯useFileExplorer

    👤User->>🎯FileOperationsMenu: clicks "New Folder" button
    🎯FileOperationsMenu->>🎯FileOperationsMenu: shows input dialog
    👤User->>🎯FileOperationsMenu: enters folder name
    🎯FileOperationsMenu->>🎯useFileOperations: createDirectory(path, name)
    🎯useFileOperations->>🔧apiClient: POST /api/files with path, isDirectory=true
    🔧apiClient->>📦FileHandler: handleCreateFile(request)
    📦FileHandler->>📦FileService: createDirectory(path)
    📦FileService->>📦PathValidator: validatePath(path)

    alt path invalid or already exists
        📦PathValidator-->>📦FileService: validation error
        📦FileService-->>📦FileHandler: error response
        📦FileHandler-->>🔧apiClient: 400 Bad Request or 409 Conflict
        🔧apiClient-->>🎯useFileOperations: error
        🎯useFileOperations-->>🎯FileOperationsMenu: displays error message
    else path valid
        📦PathValidator-->>📦FileService: validation success
        📦FileService->>📦FileSystemRepository: createDirectory(path)
        📦FileSystemRepository-->>📦FileService: success
        📦FileService-->>📦FileHandler: success response
        📦FileHandler-->>🔧apiClient: 201 Created
        🔧apiClient-->>🎯useFileOperations: success
        🎯useFileOperations->>🎯useFileExplorer: refreshCurrentDirectory()
        🎯useFileExplorer-->>🎯FileOperationsMenu: updates file list
        🎯FileOperationsMenu-->>👤User: shows new folder in list
    end
Loading

Key Data Flows

Data Flow 1: File Metadata Transformation

FileSystemRepository (os.FileInfo) -> FileEntry domain model
  -> FileService (validates, enriches with MIME type)
  -> FileInfo DTO (JSON serialization)
  -> apiClient (HTTP response)
  -> FileItem interface (frontend state)
  -> FileList component (renders UI)

Data Flow 2: File Content Streaming

User clicks file -> FilePreview requests content
  -> apiClient GET /api/files/content?path={path}
  -> FileHandler streams file content
  -> FileSystemRepository reads file as binary
  -> HTTP response with Content-Type header
  -> Browser receives blob
  -> FilePreview creates object URL
  -> Renders in <img> or react-pdf component

Data Flow 3: Search Query Processing

User input "report" -> SearchBar (debounced)
  -> useFileExplorer.searchFiles(query)
  -> apiClient GET /api/files/search
  -> FileService.searchFiles(path, query)
  -> FileSystemRepository.readDirectory()
  -> Filter entries by name.contains(query, case-insensitive)
  -> Return filtered FileInfo[]
  -> Update FileExplorerState.files
  -> FileList re-renders with filtered results

UI Structure

FileExplorerPage
├── Header
│   ├── Logo
│   └── SearchBar
│       └── SearchIcon (lucide-react)
├── BreadcrumbNav
│   ├── HomeIcon (lucide-react)
│   ├── PathSegment (clickable)
│   └── ChevronRight (separator)
├── Toolbar
│   ├── NewFolderButton
│   ├── UploadButton
│   └── ViewToggle (grid/list)
├── FileList
│   ├── FileItem (grid or list layout)
│   │   ├── FileIcon (lucide-react: Folder, FileText, Image, FileCode)
│   │   ├── FileName
│   │   ├── FileSize
│   │   └── ModifiedDate
│   └── EmptyState (when no files)
├── FileOperationsMenu (context menu)
│   ├── RenameOption
│   ├── DeleteOption
│   ├── MoveOption
│   └── DownloadOption
└── FilePreview (modal)
    ├── PreviewHeader
    │   ├── FileName
    │   └── CloseButton
    ├── PreviewContent
    │   ├── ImagePreview (<img>)
    │   └── PDFPreview (react-pdf Document + Page)
    └── PreviewFooter
        ├── DownloadButton
        └── PageNavigation (for PDFs)

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment