'use client'; import { useState, useEffect, useRef } from 'react'; import { createPortal } from 'react-dom'; import { HelpCircle, X } from 'lucide-react'; import { analytics } from '@/lib/analytics'; // The "?" help modal - shows keyboard shortcuts and how to use the app export function HowItWorks() { const [isOpen, setIsOpen] = useState(false); const [isClosing, setIsClosing] = useState(false); const [mounted, setMounted] = useState(false); const triggerRef = useRef(null); const handleOpen = () => { setIsClosing(false); setIsOpen(true); analytics.helpOpened(); }; const handleClose = () => { setIsClosing(true); analytics.helpClosed(); // Wait for exit animation to finish setTimeout(() => { setIsOpen(false); setIsClosing(false); }, 200); }; useEffect(() => { // eslint-disable-next-line react-hooks/set-state-in-effect setMounted(true); }, []); // Lock body scroll when modal is open useEffect(() => { if (isOpen) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = ''; } return () => { document.body.style.overflow = ''; }; }, [isOpen]); // Global keyboard shortcut: ? to toggle modal useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Ignore if typing in input if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return; // Skip if Ctrl/Alt/Meta are pressed (Shift is allowed for ?) if (e.ctrlKey || e.altKey || e.metaKey) return; if (e.key === '?' || (e.shiftKey && e.key === '/')) { e.preventDefault(); if (isOpen) { handleClose(); } else { handleOpen(); } } // Close on Escape if (e.key === 'Escape' && isOpen) { handleClose(); } }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); }, [isOpen]); const modal = ( <> {/* Backdrop with blur */}
{/* Modal - AccessGuide style: rectangular with left border accent */}
{/* Header */}

Help

{/* Content */}
{/* Shortcuts - AccessGuide style */}

Keyboard Shortcuts

{[ ['↑↓←→', 'Navigate through apps'], ['hjkl', 'Vim-style navigation'], ['Space', 'Select or deselect app'], ['/', 'Focus search box'], ['y', 'Copy install command'], ['d', 'Download install script'], ['c', 'Clear all selections'], ['t', 'Toggle light/dark theme'], ['Tab', 'Preview current selection'], ['Esc', 'Close this modal'], ['?', 'Show this help'], ['1 / 2', 'Switch AUR helper (yay/paru)'], ].map(([key, desc]) => (
{key} {desc}
))}
{/* Getting Started - AccessGuide style */}

Getting Started

  1. 1. Pick your distro — Select your Linux distribution from the dropdown at the top. This determines which package manager commands TuxMate generates for you.
  2. 2. Select apps — Browse the categories and click on apps to add them to your selection. Selected apps are highlighted. Use keyboard shortcuts to navigate faster.
  3. 3. Copy or download — Copy the generated install command to your clipboard, or download a complete shell script. Downloaded scripts include error handling and can install multiple apps at once.
  4. 4. Run in terminal — Open your terminal, paste the command (Ctrl+Shift+V), and press Enter. The script will handle the rest.
{/* Notes - AccessGuide style */}

Good to Know

  • Greyed out apps aren't available in your distro's official repositories. Try switching to Flatpak or Snap in the dropdown, or hover the info icon next to the app for alternative installation methods.
  • Arch Linux users — Some packages come from the AUR. TuxMate uses yay or paru as the AUR helper. Press 1 or 2 anytime to switch between them.
  • Homebrew users — Works on both macOS and Linux. GUI apps (Casks) are macOS-only and will be automatically skipped on Linux. The script handles this check for you.
  • Auto-save — Your app selections are saved automatically in your browser. Come back anytime and your selections will still be there.
  • Script Safety — Downloaded scripts are robust and idempotent. They include error handling, network retries, and system checks. Run them with bash tuxmate-*.sh to safely install your selection.
); return ( <> {isOpen && mounted && typeof document !== 'undefined' && createPortal(modal, document.body)} ); }