feat: add interactive installer with GPU auto-detection (#88)

* feat: add interactive installer with GPU auto-detection

This commit enhances the Vocalinux installation experience with:

- GPU auto-detection: Automatically detects NVIDIA GPU presence
  and selects CPU-only Whisper when no GPU is present
- New --interactive flag: Friendly guided installation for new users
  with 4-step flow (system info, engine choice, variant, models)
- Beautiful welcome message: Clear post-install instructions
  with next steps, tips, and help links
- Website updates: New interactive install section at top

Changes:
- install.sh: Add detect_nvidia_gpu(), run_interactive_install(),
  print_welcome_message() functions
- install.sh: Add INTERACTIVE_MODE, HAS_NVIDIA_GPU variables
- install.sh: Add --interactive|-i command-line flag
- install.sh: Auto-detect GPU in non-interactive mode
- web/src/app/page.tsx: Add interactiveInstallCommand section
- README.md: Add interactive install section at top

The installer now saves ~2GB of bandwidth for users without
NVIDIA GPUs by automatically selecting CPU-only PyTorch.

* fix: clarify website install options - remove duplicate 'Recommended' label

* fix: change CPU-only install icon color to cyan

* fix: polish interactive installer output

* chore: register typescript eslint plugin
This commit is contained in:
Jatin K Malik
2026-02-04 20:28:48 -08:00
committed by GitHub
parent 22ab96cea7
commit d140146490
4 changed files with 313 additions and 71 deletions
+17 -1
View File
@@ -48,13 +48,29 @@
## 🚀 Quick Install
### One-liner Installation (Recommended)
### Interactive Install (Recommended for New Users)
Let the installer guide you through the setup with smart hardware detection:
```bash
curl -fsSL https://raw.githubusercontent.com/jatinkrmalik/vocalinux/main/install.sh | bash -s -- --tag=v0.4.1-alpha --interactive
```
The interactive installer will:
- **Auto-detect your hardware** (GPU, RAM)
- **Recommend the best configuration** for your system
- **Guide you through each step** with clear explanations
- **Show you how to launch and use** Vocalinux after installation
### One-liner Installation (Quick Install)
```bash
curl -fsSL https://raw.githubusercontent.com/jatinkrmalik/vocalinux/main/install.sh | bash -s -- --tag=v0.4.1-alpha
```
> **Note**: Installs v0.4.1-alpha. For the most recent version, check [GitHub Releases](https://github.com/jatinkrmalik/vocalinux/releases).
>
> **GPU Detection**: The installer now automatically detects NVIDIA GPUs and uses CPU-only PyTorch when no GPU is present, saving you ~2GB of download!
This will:
- Clone the repository to `~/.local/share/vocalinux-install`
+248 -57
View File
@@ -31,6 +31,10 @@ WITH_WHISPER="no"
WHISPER_CPU="no"
NO_WHISPER_EXPLICIT="no"
NON_INTERACTIVE="no"
INTERACTIVE_MODE="no"
HAS_NVIDIA_GPU="unknown"
GPU_NAME=""
GPU_MEMORY=""
# Detect if running non-interactively (e.g., via curl | bash)
if [ ! -t 0 ]; then
@@ -70,6 +74,10 @@ while [[ $# -gt 0 ]]; do
NON_INTERACTIVE="yes" # Skip the prompt
shift
;;
--interactive|-i)
INTERACTIVE_MODE="yes"
shift
;;
--tag=*)
INSTALL_TAG="${1#*=}"
shift
@@ -82,14 +90,15 @@ while [[ $# -gt 0 ]]; do
echo "Vocalinux Installer"
echo "Usage: $0 [options]"
echo "Options:"
echo " --interactive, -i Guided interactive installation with recommendations"
echo " --dev Install in development mode with all dev dependencies"
echo " --test Run tests after installation"
echo " --venv-dir=PATH Specify custom virtual environment directory (default: venv)"
echo " --skip-models Skip downloading VOSK models during installation"
echo " --with-whisper Install Whisper AI support with GPU/CUDA (included by default)"
echo " --skip-models Skip downloading speech models during installation"
echo " --with-whisper Install Whisper AI with GPU/CUDA (included by default)"
echo " --whisper-cpu Install Whisper with CPU-only PyTorch (smaller download, works on low-RAM)"
echo " --no-whisper VOSK-only install (skips Whisper entirely, uses VOSK as default)"
echo " --tag=TAG Install specific release tag (default: v0.3.0-alpha)"
echo " --tag=TAG Install specific release tag (default: v0.4.1-alpha)"
echo " -y, --yes Non-interactive mode (accept defaults)"
echo " --help Show this help message"
exit 0
@@ -209,6 +218,150 @@ check_ubuntu_version() {
return 0
}
# Detect NVIDIA GPU presence
detect_nvidia_gpu() {
# Check if nvidia-smi command exists and can successfully query GPU
if command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi >/dev/null 2>&1; then
# Extract GPU information for user feedback
GPU_NAME=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -n1)
GPU_MEMORY=$(nvidia-smi --query-gpu=memory.total --format=csv,noheader 2>/dev/null | head -n1)
HAS_NVIDIA_GPU="yes"
return 0
else
HAS_NVIDIA_GPU="no"
return 1
fi
}
# Print section header for interactive mode
clear_screen() {
if [ -t 1 ] && command -v clear >/dev/null 2>&1 && [ -n "${TERM:-}" ]; then
clear >/dev/null 2>&1 || true
fi
}
print_header() {
local title="$1"
echo ""
echo "============================================================"
echo " $title"
echo "============================================================"
}
# Function to run interactive guided installation
run_interactive_install() {
clear_screen
cat << "EOF"
Vocalinux - Interactive Installation Guide
==========================================
EOF
echo "Welcome! This guided installation will help you set up Vocalinux"
echo "with the best options for your system."
echo ""
echo "Both speech engines are 100% offline, local, and private."
echo "Your voice data never leaves your computer."
echo ""
# Step 1: Detect and display system info
print_header "Step 1: Your System"
echo "Detected: $DISTRO_NAME $DISTRO_VERSION"
detect_nvidia_gpu
if [[ "$HAS_NVIDIA_GPU" == "yes" ]]; then
echo "GPU: $GPU_NAME ($GPU_MEMORY)"
RECOMMENDED_ENGINE="whisper-gpu"
RECOMMENDED_LABEL="Whisper AI with GPU acceleration"
else
echo "GPU: None detected (or no NVIDIA drivers)"
RECOMMENDED_ENGINE="whisper-cpu"
RECOMMENDED_LABEL="Whisper AI (CPU-only)"
fi
echo ""
# Step 2: Choose speech engine
print_header "Step 2: Speech Recognition Engine"
echo "Vocalinux supports two speech recognition engines:"
echo ""
echo " 1. Whisper AI (recommended)"
echo " - Highest accuracy available"
echo " - 99+ languages supported"
echo " - Created by OpenAI"
echo ""
echo " 2. VOSK"
echo " - Lightweight and fast"
echo " - Works on older/low-RAM systems"
echo " - 20+ languages supported"
echo ""
read -p "Choose your speech engine [1-2] (default: 1): " ENGINE_CHOICE
ENGINE_CHOICE=${ENGINE_CHOICE:-1}
if [[ "$ENGINE_CHOICE" == "2" ]]; then
WITH_WHISPER="no"
NO_WHISPER_EXPLICIT="yes"
SELECTED_ENGINE="VOSK"
else
WITH_WHISPER="yes"
SELECTED_ENGINE="Whisper AI"
# Step 3: Whisper variant (if Whisper selected)
print_header "Step 3: Whisper Installation Type"
echo "How would you like to install Whisper?"
echo ""
echo " 1. GPU version (~2.3GB) - For NVIDIA GPUs"
echo " 2. CPU-only (~200MB) - For any system"
echo ""
if [[ "$HAS_NVIDIA_GPU" == "yes" ]]; then
DEFAULT_WHISPER="1"
echo " -> Recommended: Option 1 (GPU detected)"
else
DEFAULT_WHISPER="2"
echo " -> Recommended: Option 2 (no GPU detected)"
fi
echo ""
read -p "Choose installation type [1-2] (default: $DEFAULT_WHISPER): " WHISPER_CHOICE
WHISPER_CHOICE=${WHISPER_CHOICE:-$DEFAULT_WHISPER}
if [[ "$WHISPER_CHOICE" == "2" ]]; then
WHISPER_CPU="yes"
SELECTED_VARIANT="Whisper AI (CPU-only)"
else
SELECTED_VARIANT="Whisper AI (GPU)"
fi
fi
# Step 4: Download models now or later?
print_header "Step 4: Model Download"
echo "Speech recognition models need to be downloaded."
echo ""
echo " 1. Download now (recommended) - Faster first run"
echo " 2. Download later - Smaller install, models download on first use"
echo ""
read -p "Download models now? [1-2] (default: 1): " MODELS_CHOICE
MODELS_CHOICE=${MODELS_CHOICE:-1}
if [[ "$MODELS_CHOICE" == "2" ]]; then
SKIP_MODELS="yes"
fi
# Summary
print_header "Installation Summary"
echo "Speech engine: $SELECTED_ENGINE${SELECTED_VARIANT:+ ($SELECTED_VARIANT)}"
echo "Models: $([[ "$SKIP_MODELS" == "yes" ]] && echo "Download on first use" || echo "Download now")"
echo ""
read -p "Press Enter to continue, or Ctrl+C to cancel..."
echo ""
# Set non-interactive to skip the old prompt
NON_INTERACTIVE="yes"
}
# Detect distribution
detect_distro
@@ -240,6 +393,37 @@ else
fi
fi
# Check if interactive mode is requested
if [[ "$INTERACTIVE_MODE" == "yes" ]]; then
# Check if we have a TTY (required for interactive mode)
if [ ! -t 0 ]; then
print_error "Interactive mode requires a terminal (TTY)."
print_error "Please run from a terminal, or use quick install mode:"
print_error " curl -fsSL https://raw.githubusercontent.com/jatinkrmalik/vocalinux/main/install.sh | bash"
exit 1
fi
# Run interactive installation
run_interactive_install
fi
# Auto-detect GPU for non-interactive mode when no explicit Whisper choice
# This helps users who run curl | bash without any flags
if [[ "$NON_INTERACTIVE" == "yes" ]] && [[ "$WITH_WHISPER" == "no" ]] && [[ "$NO_WHISPER_EXPLICIT" == "no" ]] && [[ "$INTERACTIVE_MODE" == "no" ]]; then
detect_nvidia_gpu
if [[ "$HAS_NVIDIA_GPU" == "yes" ]]; then
WITH_WHISPER="yes"
print_info "NVIDIA GPU detected: $GPU_NAME"
print_info "Installing Whisper with GPU support..."
else
WITH_WHISPER="yes"
WHISPER_CPU="yes"
print_info "No NVIDIA GPU detected"
print_info "Installing Whisper with CPU-only PyTorch (~200MB)..."
fi
echo ""
fi
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
@@ -1233,66 +1417,73 @@ verify_installation() {
return $ISSUES
}
# Function to print installation summary
print_installation_summary() {
# Function to print beautiful welcome message
print_welcome_message() {
local ISSUES=$1
echo
echo "=============================="
echo " INSTALLATION SUMMARY"
echo "=============================="
echo
clear_screen
cat << "EOF"
Vocalinux - Installation Complete
================================
EOF
# Success or warning message
if [ "$ISSUES" -eq 0 ]; then
print_success "Installation completed successfully with no issues!"
print_success "Vocalinux has been installed successfully!"
else
print_warning "Installation completed with $ISSUES potential issue(s)."
print_warning "The application may still work, but some features might be limited."
print_warning "Installation complete with $ISSUES minor issue(s)"
print_warning "The application should still work normally."
fi
echo
print_info "Installation details:"
print_info "- Virtual environment: $VENV_DIR"
print_info "- Desktop entry: $DESKTOP_DIR/vocalinux.desktop"
print_info "- Icons: $ICON_DIR"
print_info "- Configuration: $CONFIG_DIR"
print_info "- Data directory: $DATA_DIR"
echo ""
echo "============================================================"
echo " Getting Started"
echo "============================================================"
echo ""
echo "1. Launch Vocalinux from your app menu"
echo " Look for 'Vocalinux' in your application launcher."
echo ""
echo " Or from terminal:"
echo " vocalinux"
echo ""
echo "2. Find Vocalinux in your system tray (top bar)"
echo " - Click the icon to access settings"
echo " - Right-click for options (Quit, Settings, etc.)"
echo ""
echo "3. Start dictating!"
echo " Double-tap Ctrl anywhere to start/stop dictation"
echo ""
echo "============================================================"
echo " First Run Tips"
echo "============================================================"
echo ""
echo "- Speak clearly and at a natural pace"
echo "- Use voice commands: 'period', 'comma', 'new line', 'delete that'"
echo "- Both engines are 100% offline - your privacy is protected"
echo ""
echo "============================================================"
echo " Need Help?"
echo "============================================================"
echo ""
echo "- Report issues: https://github.com/jatinkrmalik/vocalinux/issues"
echo "- Documentation: https://github.com/jatinkrmalik/vocalinux"
echo "- Star us on GitHub: https://github.com/jatinkrmalik/vocalinux"
echo ""
# Check if Whisper model was installed
if [ -f "$DATA_DIR/models/whisper/tiny.pt" ]; then
print_info "- Whisper tiny model: $DATA_DIR/models/whisper/tiny.pt (default engine)"
elif python -c "import whisper" 2>/dev/null; then
print_info "- Whisper tiny model: Will be downloaded on first run"
fi
# Check if VOSK model was installed
if [ "$SKIP_MODELS" = "no" ] && [ -d "$DATA_DIR/models/vosk-model-small-en-us-0.15" ]; then
print_info "- VOSK small model: $DATA_DIR/models/vosk-model-small-en-us-0.15 (fallback)"
elif [ "$SKIP_MODELS" = "yes" ]; then
print_info "- VOSK models: Will be downloaded on first run (--skip-models used)"
else
print_info "- VOSK models: Will be downloaded on first run (installation failed)"
fi
echo
print_info "To activate the virtual environment in the future, run:"
print_info " source $ACTIVATION_SCRIPT"
print_info "You can then launch Vocalinux by running 'vocalinux'"
# If venv is in a standard location, mention direct launch option
if [[ "$VENV_DIR" == "$HOME/.local/share/vocalinux/venv" ]]; then
echo
print_info "Or run directly with:"
print_info " $VENV_DIR/bin/vocalinux"
fi
echo
print_info "For more information, see: https://github.com/jatinkrmalik/vocalinux"
if [ "$ISSUES" -gt 0 ]; then
echo
print_warning "If you encounter any problems, please report them at:"
print_warning "https://github.com/jatinkrmalik/vocalinux/issues"
# Installation details (optional, for debugging)
if [[ "$VERBOSE" == "yes" ]]; then
echo "============================================================"
echo " Installation Details"
echo "============================================================"
echo ""
echo "Virtual environment: $VENV_DIR"
echo "Desktop entry: $DESKTOP_DIR/vocalinux.desktop"
echo "Configuration: $CONFIG_DIR"
echo "Data directory: $DATA_DIR"
echo ""
fi
}
@@ -1300,7 +1491,7 @@ print_installation_summary() {
verify_installation
INSTALL_ISSUES=$?
# Print installation summary
print_installation_summary $INSTALL_ISSUES
# Print welcome message
print_welcome_message $INSTALL_ISSUES
print_success "Installation process completed!"
+3 -1
View File
@@ -1,6 +1,8 @@
{
"extends": ["next/core-web-vitals"],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"rules": {
"react/no-unescaped-entities": "off"
}
}
}
+45 -12
View File
@@ -40,6 +40,10 @@ import { useInView } from "react-intersection-observer";
// The one-liner install command (split into three lines for display)
// Downloads install.sh from main (which has --tag support) and passes the latest release tag
const getInstallCommands = (latestRelease: string) => ({
interactiveInstallCommand: `curl \\
-fsSL https://raw.githubusercontent.com/jatinkrmalik/vocalinux/main/install.sh \\
| bash -s -- --tag=${latestRelease} --interactive`,
oneClickInstallCommand: `curl \\
-fsSL https://raw.githubusercontent.com/jatinkrmalik/vocalinux/main/install.sh \\
| bash -s -- --tag=${latestRelease}`,
@@ -185,7 +189,7 @@ export default function HomePage() {
// Get install commands based on the latest release
const installCommands = getInstallCommands(latestRelease);
const { oneClickInstallCommand, oneClickInstallWhisperCpu, oneClickInstallNoWhisper, uninstallCommand } = installCommands;
const { interactiveInstallCommand, oneClickInstallCommand, oneClickInstallWhisperCpu, oneClickInstallNoWhisper, uninstallCommand } = installCommands;
return (
<main className="min-h-screen bg-gradient-to-b from-zinc-50 to-white dark:from-zinc-900 dark:to-zinc-950">
@@ -598,16 +602,45 @@ export default function HomePage() {
<FadeInSection delay={0.1}>
<div className="bg-white dark:bg-zinc-800 rounded-2xl shadow-xl overflow-hidden border border-zinc-200 dark:border-zinc-700 min-w-0">
<div className="p-6 sm:p-8">
{/* Interactive Install - NEW PRIMARY OPTION */}
<div className="mb-8 bg-gradient-to-r from-purple-500/10 to-blue-500/10 rounded-xl p-6 -mx-6 -mt-8 sm:-mx-8 sm:-mt-8 border border-purple-500/20">
<div className="flex items-center gap-3 mb-4">
<div className="bg-purple-500/10 p-2 rounded-lg">
<Sparkles className="h-5 w-5 text-purple-500" />
</div>
<div className="flex-1">
<h3 className="text-xl font-semibold">Interactive Install (Recommended)</h3>
<p className="text-sm text-muted-foreground">
Guided installation with smart hardware detection
</p>
</div>
<CopyButton text={interactiveInstallCommand} />
</div>
<div className="overflow-x-auto">
<CodeBlock
language="bash"
className="rounded-lg text-sm sm:text-base"
customStyle={{ margin: 0, maxWidth: "100%", overflowX: "auto" }}
wrapLongLines={false}
>
{interactiveInstallCommand}
</CodeBlock>
</div>
<p className="text-xs text-muted-foreground mt-3">
Detects your GPU and recommends the best option. Prompts for your preferences.
</p>
</div>
{/* Primary install option */}
<div className="mb-8">
<div className="flex items-center gap-3 mb-4">
<div className="bg-green-500/10 p-2 rounded-lg">
<Sparkles className="h-5 w-5 text-green-500" />
<div className="bg-blue-500/10 p-2 rounded-lg">
<Zap className="h-5 w-5 text-blue-500" />
</div>
<div className="flex-1">
<h3 className="text-xl font-semibold">Recommended: Full Install</h3>
<h3 className="text-xl font-semibold">Quick Install (Auto-detect)</h3>
<p className="text-sm text-muted-foreground">
Includes Whisper AI for best accuracy (~5-10 min)
Fastest for power users - no prompts (~3-10 min)
</p>
</div>
<CopyButton text={oneClickInstallCommand} />
@@ -627,13 +660,13 @@ export default function HomePage() {
{/* Alternative install - CPU only */}
<div className="border-t border-zinc-200 dark:border-zinc-700 pt-6">
<div className="flex items-center gap-3 mb-4">
<div className="bg-blue-500/10 p-2 rounded-lg">
<Cpu className="h-5 w-5 text-blue-500" />
<div className="bg-cyan-500/10 p-2 rounded-lg">
<Cpu className="h-5 w-5 text-cyan-500" />
</div>
<div className="flex-1">
<h3 className="text-lg font-semibold">Whisper CPU-only</h3>
<h3 className="text-lg font-semibold">CPU-only Install</h3>
<p className="text-sm text-muted-foreground">
Smaller download (~200MB vs ~2.3GB) - no NVIDIA GPU needed (~3-4 min)
Smaller download (~200MB vs ~2.3GB) - for systems without NVIDIA GPU (~3-4 min)
</p>
</div>
<CopyButton text={oneClickInstallWhisperCpu} />
@@ -653,11 +686,11 @@ export default function HomePage() {
{/* Alternative install - VOSK only */}
<div className="border-t border-zinc-200 dark:border-zinc-700 pt-6">
<div className="flex items-center gap-3 mb-4">
<div className="bg-green-500/10 p-2 rounded-lg">
<Zap className="h-5 w-5 text-green-500" />
<div className="bg-orange-500/10 p-2 rounded-lg">
<Zap className="h-5 w-5 text-orange-500" />
</div>
<div className="flex-1">
<h3 className="text-lg font-semibold">VOSK only (lightest)</h3>
<h3 className="text-lg font-semibold">VOSK Only (Lightweight)</h3>
<p className="text-sm text-muted-foreground">
For low-RAM systems (8GB or less) - skips Whisper entirely (~2-3 min)
</p>