# Mudabbir Desktop Launcher

A lightweight desktop app (~15MB) that lets non-developers run Mudabbir with zero terminal usage. Download, double-click, done.

## What it does

1. **First run** — Detects Python 3.11+ on your system (or downloads it on Windows), creates a virtual environment, and installs Mudabbir via pip. A progress window shows each step.
2. **Every run** — Starts the Mudabbir server on localhost, opens your browser to the dashboard, and sits in your system tray for quick access.

```
┌─────────────────────────────────┐
│  Double-click Mudabbir.app     │
│          ↓                      │
│  Python 3.11+ found?            │
│    yes → create venv            │
│    no  → download (Windows)     │
│          ↓                      │
│  pip install mudabbir          │
│          ↓                      │
│  Start server → open browser    │
│          ↓                      │
│  System tray icon 🧠            │
│  (Start/Stop/Update/Quit)       │
└─────────────────────────────────┘
```

## Modules

| File                  | Purpose                                                 |
| --------------------- | ------------------------------------------------------- |
| `__main__.py`         | Entry point — orchestrates bootstrap, server, tray      |
| `bootstrap.py`        | Python detection, venv creation, pip install            |
| `server.py`           | Server process lifecycle, PID management, health checks |
| `tray.py`             | System tray icon with dynamic menu (via pystray)        |
| `splash.py`           | First-run tkinter progress window                       |
| `updater.py`          | PyPI version check and one-click upgrade                |
| `build/build.py`      | PyInstaller build script                                |
| `build/launcher.spec` | PyInstaller spec file (folder mode, platform-specific)  |

## Running from source

No build needed to test the launcher locally:

```bash
# From the repo root
cd /path/to/mudabbir

# Run the launcher module directly
PYTHONPATH=. python -m installer.launcher

# With options
PYTHONPATH=. python -m installer.launcher --no-browser --no-tray --port 9999
```

### CLI Options

| Flag            | Description                                                                  |
| --------------- | ---------------------------------------------------------------------------- |
| `--no-browser`  | Don't auto-open the browser                                                  |
| `--no-tray`     | Run headless (no system tray icon, Ctrl+C to stop)                           |
| `--port PORT`   | Override the dashboard port (default: 8888)                                  |
| `--extras LIST` | Comma-separated pip extras, e.g. `telegram,discord` (default: `recommended`) |
| `--reset`       | Delete the venv and reinstall from scratch                                   |
| `--dev`         | Install from the `dev` branch instead of PyPI (shortcut for `--branch dev`)  |
| `--branch NAME` | Install from a specific git branch (e.g. `--branch feat/new-thing`)          |
| `--local PATH`  | Install from a local directory in editable mode                              |

## Dev / Branch Testing

Test unreleased code without publishing to PyPI. Useful for QA before a release.

> **Prerequisite (all platforms):** `git` must be installed and on your PATH for `--dev` and `--branch` to work. The `--local` flag does not require git.

### Install from the dev branch

```bash
# macOS / Linux
PYTHONPATH=. python -m installer.launcher --dev --no-tray

# Windows (PowerShell)
$env:PYTHONPATH = "."
python -m installer.launcher --dev --no-tray
```

This installs `mudabbir` from `git+https://github.com/Ahmed5754/Mudabbir.git@dev` instead of PyPI.

### Install from any branch

```bash
PYTHONPATH=. python -m installer.launcher --branch feat/new-feature --no-tray
```

### Install from a local checkout (editable)

```bash
# Code changes in the repo are reflected immediately — no reinstall needed
PYTHONPATH=. python -m installer.launcher --local /path/to/mudabbir --no-tray
```

### Force a clean reinstall from dev

```bash
PYTHONPATH=. python -m installer.launcher --dev --reset --no-tray
```

### How dev mode works

- A marker file `~/.mudabbir/.dev-mode` is created when using `--dev`, `--branch`, or `--local`
- While this marker exists, the **updater** skips PyPI version checks and instead offers to re-pull from the configured branch
- To switch back to PyPI releases, use `--reset` without `--dev`:
  ```bash
  PYTHONPATH=. python -m installer.launcher --reset --no-tray
  ```

## Building

### Prerequisites

```bash
pip install pyinstaller pystray Pillow
```

### Build for your platform

```bash
python installer/launcher/build/build.py
```

Output goes to `dist/launcher/`.

### macOS — create .dmg

```bash
# After build.py finishes:
hdiutil create -volname Mudabbir \
  -srcfolder dist/launcher/Mudabbir.app \
  -ov -format UDZO \
  dist/launcher/Mudabbir.dmg
```

### Windows — create installer

Option A: Use [Inno Setup](https://jrsoftware.org/isinfo.php) (free) — point it at `dist\launcher\Mudabbir\`.

Option B: Zip the folder for a portable build:

```powershell
Compress-Archive -Path dist\launcher\Mudabbir -DestinationPath dist\launcher\Mudabbir-portable.zip
```

### Automated builds (CI)

The GitHub Actions workflow `.github/workflows/build-launcher.yml` builds for:

- **macOS (Apple Silicon)** — `.dmg`
- **macOS (Intel)** — `.dmg`
- **Windows** — `.exe` installer (via Inno Setup)

Triggered on:

- **Release published** — artifacts are attached to the GitHub release
- **Manual dispatch** — artifacts are uploaded as workflow artifacts

## How it works

### Bootstrap (`bootstrap.py`)

1. Searches for Python 3.11+ in common locations (`python3`, `python3.12`, `python3.11`, etc.)
2. On Windows, if no Python found, downloads the [Python embeddable package](https://www.python.org/downloads/) (~15MB, no admin needed)
3. Creates a venv at `~/.mudabbir/venv/`
4. Runs `pip install mudabbir[recommended]` inside the venv

### Server management (`server.py`)

- Starts Mudabbir as a subprocess: `{venv}/bin/python -m mudabbir --port {port}`
- Writes PID to `~/.mudabbir/launcher.pid`
- Health check via HTTP GET to `http://127.0.0.1:{port}/`
- Graceful shutdown: SIGTERM → wait → SIGKILL
- Auto-finds a free port if the default (8888) is occupied

### System tray (`tray.py`)

- Cross-platform via [pystray](https://github.com/moses-palmer/pystray)
- Menu: Open Dashboard, Start/Stop Server, Restart, Check for Updates, Quit
- Checks PyPI for updates every 6 hours
- Shows desktop notifications for available updates

### Updates (`updater.py`)

- **PyPI mode (default):** Fetches `https://pypi.org/pypi/mudabbir/json` for the latest version, compares with installed, applies via `pip install --upgrade mudabbir`
- **Dev mode (`--dev` / `--branch`):** Skips PyPI checks, always offers "re-pull from branch", applies via `pip install --force-reinstall` from the git URL

## File locations

| Path                             | Purpose                                                              |
| -------------------------------- | -------------------------------------------------------------------- |
| `~/.mudabbir/venv/`             | Virtual environment with mudabbir installed                         |
| `~/.mudabbir/config.json`       | Mudabbir configuration                                              |
| `~/.mudabbir/launcher.pid`      | Server process PID                                                   |
| `~/.mudabbir/logs/launcher.log` | Launcher log file                                                    |
| `~/.mudabbir/.dev-mode`         | Dev mode marker (present when `--dev`/`--branch`/`--local` was used) |
| `~/.mudabbir/python/`           | Embedded Python (Windows only)                                       |

## Tests

53 unit tests covering bootstrap, server, and updater:

```bash
PYTHONPATH=. uv run pytest tests/test_launcher_bootstrap.py tests/test_launcher_server.py tests/test_launcher_updater.py -v
```

## Architecture decisions

**Why a thin launcher instead of bundling everything?**
Mudabbir with all dependencies is 150-300MB. The launcher itself is ~15MB. By keeping mudabbir in a pip-managed venv, users get standard `pip install --upgrade` updates instead of downloading a new 300MB app every time.

**Why PyInstaller folder mode?**
One-file mode extracts to a temp dir on every launch (slow startup). Folder mode starts instantly and is easier to debug.

**Why tkinter for the splash?**
It's built into Python — no extra dependency. The splash only appears once (first install), so it doesn't need to be fancy.
