# Contributing to TuxMate

## Quick Navigation

1.  [Project Overview](#project-overview)
2.  [Development Workflow](#development-workflow)
    *   [Initial Setup](#initial-setup)
    *   [Running with Docker](#running-with-docker)
    *   [Quality Assurance](#quality-assurance)
    *   [Verifying Install Scripts](#verifying-install-scripts)
3.  [Adding Applications](#adding-applications)
    *   [Research Protocol](#1-mandatory-research-protocol)
    *   [Entry Structure](#2-entry-structure)
    *   [Unavailable Reason](#3-unavailable-reason-guidelines)
    *   [Platform Rules](#4-platform-specific-rules)
        *   [Arch Linux](#arch-linux)
        *   [NixOS](#nixos)
        *   [Ubuntu/Debian](#ubuntudebian)
        *   [Flatpak](#flatpak)
        *   [Snap](#snap)
        *   [Homebrew](#homebrew)
    *   [Universal Targets](#universal-targets-npm--script)
    *   [Icon System](#5-icon-system)
    *   [Valid Categories](#6-valid-categories)
4.  [Adding Distributions](#adding-distributions)
5.  [Pull Request Checklist](#pull-request-checklist)
    *   [Core Principles](#core-principles)
    *   [Verification Steps](#verification-steps)
6.  [Templates](#templates)
    *   [Pull Request](#pull-request-template)
    *   [Bug Report](#issue-template-bug-report)

---

## Project Overview

*   `src/lib/apps/*.json`: Main registry for applications (split by category).
*   `src/lib/data.ts`: Main registry for distributions, categories, and Typescript types.
*   `src/lib/aur-packages.json`: Whitelist for AUR packages that lack standard suffixes.
*   `src/lib/nix-unfree.json`: Registry for unfree Nix packages.
*   `src/lib/verified-flatpaks.json`: Auto-generated list of verified Flathub apps. **Do not edit.**
*   `src/lib/verified-snaps.json`: Manual whitelist of verified Snap publishers.

---

## Development Workflow

### Initial Setup

```bash
git clone https://github.com/abusoww/tuxmate.git
cd tuxmate
npm install
npm run dev
```
The app will be available at `http://localhost:3000`.

<a id="running-with-docker"></a>
<details>
<summary>Running with Docker</summary>

You can run the full application using the official Docker image:

```bash
docker run -p 3000:80 ghcr.io/abusoww/tuxmate:latest
```
</details>

<a id="quality-assurance"></a>
<details>
<summary>Quality Assurance</summary>

Always run these before pushing:
*   `npm run lint`: Check for code style issues.
*   `npm run test`: Run unit test suite.
*   `npm run build`: Verify production build.
</details>

<a id="verifying-install-scripts"></a>
<details>
<summary>Verifying Install Scripts</summary>

To verify that the *scripts generated by TuxMate* work correctly, run them in a clean container environment. This prevents messing up your local system.

1.  Generate a script in the TuxMate UI (Dev mode).
2.  Copy the script.
3.  Run the corresponding distro container commands below and paste the script.

```bash
# Arch Linux
docker run -it --rm archlinux:latest bash -c "pacman -Sy && bash"

# Ubuntu
docker run -it --rm ubuntu:latest bash -c "apt update && bash"

# Fedora
docker run -it --rm fedora:latest bash -c "dnf check-update; bash"
```
</details>

---

## Adding Applications

All applications are defined in category-specific JSON files within [`src/lib/apps/`](src/lib/apps/).

### 1. Mandatory Research Protocol

**You MUST verify every package on these official sources before submitting:**

| Source | Scope | URL |
| :--- | :--- | :--- |
| **Repology** | Global Index | [repology.org](https://repology.org/) |
| **Arch Linux** | Official Repos | [archlinux.org/packages](https://archlinux.org/packages/) |
| **AUR** | User Repo | [aur.archlinux.org](https://aur.archlinux.org/) |
| **Debian** | Official Repos | [packages.debian.org](https://packages.debian.org/) |
| **Ubuntu** | Official Repos | [packages.ubuntu.com](https://packages.ubuntu.com/) |
| **Fedora** | Official Repos | [packages.fedoraproject.org](https://packages.fedoraproject.org/) |
| **OpenSUSE** | Official Repos | [software.opensuse.org](https://software.opensuse.org/) |
| **Nix** | Nixpkgs | [search.nixos.org](https://search.nixos.org/packages) |
| **Flathub** | Flatpaks | [flathub.org](https://flathub.org/) |
| **Snapcraft** | Snaps | [snapcraft.io](https://snapcraft.io/) |
| **Homebrew** | CLI & Casks | [formulae.brew.sh](https://formulae.brew.sh/) |

### 2. Entry Structure

```json
{
  "id": "app-id",                      // Unique, lowercase, kebab-case
  "name": "App Name",                  // Official display name
  "description": "Short description",  // Max ~60 characters
  "category": "Category",              // Must match valid categories
  "icon": {                            // See Icon System section
    "type": "iconify",
    "set": "simple-icons",
    "name": "python",
    "color": "#3776AB"
  },
  "targets": {
    "ubuntu": "exact-package-name",    // apt package (official repos ONLY)
    "arch": "exact-package-name",      // pacman OR AUR package name
    "flatpak": "com.vendor.AppId",     // FULL Flatpak App ID (reverse DNS)
    "snap": "snap-name",               // Add --classic if needed
    "homebrew": "formula-name",        // Formula (CLI) or '--cask name' (GUI)
    "npm": "@scope/package-name",      // Global npm install (universal fallback)
    "script": "curl -fsSL ... | bash"  // Custom install script (universal fallback)
  },
  "note": "Context for universal targets",  // Shown on hover for universal-fallback apps
  "unavailableReason": "Markdown install instructions"
}
```

> [!NOTE]
> **Priority system**: Native distro targets (e.g., `arch`, `ubuntu`) always take precedence over universal targets (`npm`, `script`). If an app has both `arch: "ollama"` and `script: "curl ..."`, the script is only used when the user selects a distro where no native package exists.

### 3. Unavailable Reason Guidelines

This field renders Markdown when a target is missing. It serves as the manual fallback instruction.

**Requirements:**
1.  **Actionable**: Avoid "Not available". State the alternative (e.g., "Install via Flatpak").
2.  **Linked**: Hard-link the solution (Flathub page, upstream .deb URL, or Wiki guide).
3.  **Format**: Use standard Markdown for links `[text](url)` and code `` `cmd` ``.

**Examples:**

| Status | Message | Why? |
| :--- | :--- | :--- |
| ❌ **Bad** | `'Not available.'` | Dead end. No solution provided. |
| ❌ **Bad** | `'Download from website.'` | Dead end. No solution provided. |
| ✅ **Good** | `'Not in official repos. Use the [Flatpak version](https://flathub.org/apps/com.spotify.Client) instead.'` | Directs to the preferred supported alternative. |
| ✅ **Good** | ```'Arch requires [multilib](https://wiki.archlinux.org/title/Official_repositories#multilib) enabled: uncomment `[multilib]` in `/etc/pacman.conf`, run `sudo pacman -Syu`, then `sudo pacman -S steam`.'``` | Exact steps to enable the required repo. |

### 4. Platform Specific Rules

#### Arch Linux
*   **Official Packages**: Use the package name directly if found in `core` or `extra` (e.g., `firefox`).
*   **Automatic AUR Detection**: `src/lib/aur.ts` automatically detects suffixes `-bin`, `-git`, or `-appimage`. Use the name directly (e.g., `brave-bin`).
*   **Manual AUR Detection**: For AUR packages without suffixes (e.g., `google-chrome`), **you must add the name to** `src/lib/aur-packages.json`.
*   Prefer `-bin` suffix packages in AUR (pre-built, faster install)

#### NixOS
Nixpkgs requires explicit user consent for unfree software.
1.  Check the license on [search.nixos.org](https://search.nixos.org/packages).
2.  If the license is unfree, add it to `src/lib/nix-unfree.json` if missing.
3.  Add the package to the appropriate JSON file in `src/lib/apps/` normally.

#### Ubuntu/Debian
**Strict Repository Policy**: The generation scripts do **not** enable extra repositories (like PPAs or `non-free` by default). Packages must be available in the standard enabled repositories.

*   **Allowed**:
    *   **Ubuntu**: Main, Restricted
    *   **Debian**: Main
*   **Prohibited**:
    *   PPAs (Personal Package Archives).
    *   External `.deb` URLs.
    *   Packages requiring manual `sources.list` modification (unless detailed in `unavailableReason`).

#### Flatpak
*   **ID Format**: Always use the full **Application ID** (reverse-DNS style).
    *   ✅ Correct: `org.mozilla.firefox`
    *   ❌ Wrong: `firefox`
*   **Verification**: Find the exact ID at the bottom of the app's [Flathub page](https://flathub.org/).
*   **Note**: `verified-flatpaks.json` is auto-generated; do not edit it manually.

#### Snap
*   **Classic Confinement**: If the snap requires classic confinement (access to host system files), append `--classic`.
    *   Example: `code --classic`
    *   Check the install command on [snapcraft.io](https://snapcraft.io/) to confirm.
*   **Verification**: If the publisher has a **"Verified" badge** on [Snapcraft](https://snapcraft.io/):
    *   Add the **publisher name** to `src/lib/verified-snaps.json`.
    *   This enables the "Verified" badge in the TuxMate UI.

#### Homebrew
Homebrew (macOS/Linux) has two package types. Check [formulae.brew.sh](https://formulae.brew.sh/) to find the correct one.

*   **Formula**: Standard CLI tools and libraries.
    *   Usage: Use the package name directly.
    *   Example: `'wget'`, `'node'`, `'python@3.12'`
*   **Cask**: GUI applications and large binaries (macOS only).
    *   Usage: Prefix with `--cask ` (note the space).
    *   Example: `'--cask firefox'`, `'--cask visual-studio-code'`
*   **Validation**:
    *   Run `brew search <name>` locally to confirm type.
    *   We skip `--cask` targets on Linux installs automatically.

#### Universal Targets (npm & script)

Universal targets provide cross-distro installation via package managers or custom scripts. They serve as **fallbacks** — only used when no native distro target exists for the selected distribution.

*   **`npm`**: Install via `npm install -g`. Requires Node.js runtime on the system.
*   **`script`**: Raw shell command (typically a `curl | bash` installer). Runs directly.

> [!IMPORTANT]
> **Native targets always take priority.** If an app defines `arch: "ollama"` alongside `script: "curl ..."`, the script target is completely ignored when Arch is selected. The fallback only activates for distros without a native package.

**When to use each:**

| Target | Use Case | Example |
| :--- | :--- | :--- |
| `npm` | CLI tools distributed via npmjs.com | `"npm": "@google/gemini-cli"` |
| `script` | Apps with official install scripts | `"script": "curl -fsSL https://ollama.com/install.sh \| sh"` |

**Real examples from the codebase:**

```json
// Ollama: native on arch/fedora/nix/homebrew, falls back to script on ubuntu/debian
{
  "id": "ollama",
  "targets": {
    "fedora": "ollama",
    "arch": "ollama",
    "nix": "ollama",
    "homebrew": "ollama",
    "script": "curl -fsSL https://ollama.com/install.sh | sh"
  },
  "note": "Falls back to official installer (ollama.com/install.sh) on distros without a native package."
}

// Gemini CLI: npm-only (plus homebrew)
{
  "id": "gemini-cli",
  "targets": {
    "npm": "@google/gemini-cli",
    "homebrew": "gemini-cli"
  },
  "note": "Requires Node.js runtime. Installed globally via npm where native packages are unavailable."
}
```

**The `note` field:**
*   Used to explain the universal target behavior to users (shown on hover).
*   **Required** when using `npm` or `script` targets.
*   Keep concise and professional. Examples:
    *   ✅ `"Falls back to official installer (ollama.com/install.sh) on distros without a native package."`
    *   ✅ `"Requires Node.js runtime. Installed globally via npm where native packages are unavailable."`
    *   ❌ `"Installed globally via npm."` *(too terse, doesn't explain when or why)*

**Script safety rules:**
1.  Only use **official** installer scripts from the app's own domain.
2.  Always use `curl -fsSL` flags (fail silently on errors, follow redirects, show errors).
3.  Never combine multiple pipes or add `sudo` — the install script handles privileges itself.
4.  Verify the script URL is stable and maintained by the upstream project.

### 5. Icon System

Every app needs an icon! Our JSON format makes it super simple to add icons using [Iconify](https://iconify.design/). 

We store icons as structured objects in the JSON. You just need to provide the set, name, and color:
```json
"icon": {
  "type": "iconify",
  "set": "simple-icons",
  "name": "python",
  "color": "#3776AB"
}
```

#### The 3 Main Icon Sets We Use:
1.  **Simple Icons** (`"set": "simple-icons"`)
    *   Used for major brands and single-color logos (like Discord or Python).
    *   *You must provide a hex `"color"` for these.*
2.  **Logos** (`"set": "logos"`)
    *   Used when an app has a multi-colored, official logo. 
    *   *You don't need a `"color"` for these.*
3.  **Material Design** (`"set": "mdi"`)
    *   Used for generic utilities (like a terminal or a magnifying glass).
    *   *You must provide a hex `"color"` for these.*

#### Can't find it on Iconify?
If an app's icon isn't on Iconify, you can use a direct link to an image (SVG preferred, or a high-res PNG).
Just change the `"type"` to `"url"`:
```json
"icon": {
  "type": "url",
  "url": "https://raw.githubusercontent.com/..."
}
```
*   ✅ **DO:** Use a stable link (like the app's official GitHub repo raw image, or Wikimedia).
*   ❌ **DON'T:** Use temporary image hosts (like Imgur) or hotlink-protected websites.

### 6. Valid Categories

Use **exactly** one of these:
*   Web Browsers • Communication • Media • Creative • Gaming • Office
*   Dev: Languages • Dev: Editors • Dev: Tools
*   Terminal • CLI Tools • AI Tools • VPN & Network • Security • File Sharing • System

---

## Adding Distributions

Adding a new distribution involves three main steps:

### 1. Register the Distribution
Edit [`src/lib/data.ts`](src/lib/data.ts):
1.  Add the new ID to the `DistroId` type definition.
2.  Add a new object to the `distros` array with:
    *   `id`: unique identifier.
    *   `name`: Display name.
    *   `iconUrl`: Icon for the distro selector.
    *   `color`: Brand color.
    *   `installPrefix`: The default command prefix (e.g., `sudo dnf install -y`).

### 2. Create the Generator Script
Create a new file `src/lib/scripts/<distroId>.ts`. This file must export a function (e.g., `generateFedoraScript`) that takes `PackageInfo[]` and returns the generated shell script string.
*   Use helpers from `shared.ts` like `generateAsciiHeader` and `PackageInfo`.
*   Implement logic to check if a package is already installed.
*   Implement the installation command loop using `with_retry` for robustness.
*   See `src/lib/scripts/fedora.ts` or `ubuntu.ts` for clean examples.

### 3. Integrate the Generator
1.  Export your new function in [`src/lib/scripts/index.ts`](src/lib/scripts/index.ts).
2.  Import it in [`src/lib/generateInstallScript.ts`](src/lib/generateInstallScript.ts).
3.  Add a case for your `distroId` in the `switch` statement inside `generateInstallScript`.
4.  Also add the simple one-liner command logic in `generateSimpleCommand` within the same file.

---

## 🔀 Pull Request Checklist

### Core Principles

> [!IMPORTANT]
> **Your PR will be rejected if you violate these rules:**
>
> 1.  **Verify Everything**: Submit only verified package names. Guessing is prohibited.
> 2.  **Official First**: Use official repository packages over third-party options.
> 3.  **No Unofficial Repos**: Do not include PPAs, COPRs, or unofficial repositories.
> 4.  **Full IDs**: Use full IDs for Flatpaks (e.g., `org.mozilla.firefox`).
> 5.  **Strict Casing**: Package names are case-sensitive.
> 6.  **Link Integrity**: Ensure all links in `unavailableReason` are direct and working.

### Verification Steps
**Verify before submitting:**
- [ ] Package names verified on official search pages (Repology, Arch, etc).
- [ ] Case sensitivity checked (especially openSUSE).
- [ ] Arch packages verified (Official vs AUR).
- [ ] No PPAs used for Debian/Ubuntu; Main/Universe only.
- [ ] Flatpak IDs are full reverse-DNS style.
- [ ] Snap `--classic` flag verification.
- [ ] Nix unfree packages added to JSON.
- [ ] Homebrew Casks prefixed correctly.
- [ ] Universal targets: `npm`/`script` only used as fallbacks, `note` field provided.
- [ ] Script URLs verified as official, stable endpoints.
- [ ] `npm run lint` & `npm run test` passed.

---

## 📝 Templates

### Pull Request Template

```markdown
## Summary
Brief description of changes.

## Changes
| App Name | Category | Sources |
|----------|----------|---------|
| Example  | Dev: Tool| apt, pacman |

## Verification
> Package names verified against official sources.

| Source | Link |
|--------|------|
| Repology | [Link](...) |
| Arch | [Link](...) |
| ... | ... |

## Testing
- [ ] `npm run dev` working
- [ ] `npm run build` passed
- [ ] `npm run test` passed
- [ ] `npm run lint` passed


## Screenshots (if applicable)

<!-- Add screenshots for UI changes -->

```

### Issue Template (Bug Report)

```markdown
## 🐛 Bug Report

**Environment**:
- OS: [e.g. Arch Linux]
- Browser: [e.g. Firefox 120]

**Steps to Reproduce**:
1. ...
2. ...

**Details**:


**Logs/Screenshots**:
[Paste console logs or attach screenshots]
```
