Files
Git-Manager-Gui/DEV_GUIDE.md
T
2026-10-22 09:13:21 +00:00

18 KiB

Entwicklerhandbuch — Git Manager Explorer Pro

Dieses Dokument richtet sich an Entwickler, die an Git Manager Explorer Pro beitragen oder das Projekt erweitern möchten.


Inhaltsverzeichnis

  1. Projektstruktur
  2. Setup & Abhängigkeiten
  3. Entwicklung starten
  4. Wichtige Module
  5. Neue Features hinzufügen
  6. Build & Release
  7. Debugging & Testing
  8. Code-Konventionen
  9. Häufige Aufgaben

Projektstruktur

git-manager-gui/
├── main.js                 # Electron Main Process — Fenster, Menü, IPC
├── preload.js             # Preload Script — sichere Electron API für Renderer
├── renderer/              # Frontend (React/Vanilla JS)
│   ├── App.jsx            # React-Komponente (optional, optional genutzt)
│   ├── Settings.jsx       # Settings-Modal
│   ├── index.html         # HTML Entry Point
│   ├── index.js           # Renderer Init
│   ├── renderer.js        # Hauptlogik: UI, Grid, Editor, API-Calls
│   ├── style.css          # Styling
│   └── modules/           # Feature-Module
│       ├── editor.js      # Editor-Funktion (Syntax Highlighting, etc.)
│       ├── gitea.js       # Gitea API Wrapper
│       ├── github.js      # GitHub API Wrapper
│       ├── progress.js    # Progress-Bar & Ladeindikatoren
│       ├── state.js       # State Management (Favoriten, Verlauf, etc.)
│       └── ui.js          # UI Helpers (Dialoge, Toast, etc.)
├── src/                   # Backend (Node.js Main Process)
│   ├── git/              
│   │   ├── gitHandler.js  # Git CLI Wrapper (Commits, Tags, etc.)
│   │   └── apiHandler.js  # Gitea/GitHub API HTTP Client
│   ├── backup/
│   │   ├── BackupManager.js    # Backup/Restore Logik
│   │   ├── BackupProvider.js   # Provider Interface
│   │   └── LocalProvider.js    # Lokale Datei-Provider
│   └── utils/
│       └── helpers.js     # Allgemeine Hilfsfunktionen
├── __tests__/
│   └── gitHandler.test.js # Jest Unit Tests
├── updater.js            # App-Update-Logik
├── package.json          # Dependencies & Scripts
├── HANDBUCH.html         # Benutzerhandbuch (HTML)
└── README.md             # Projekt-Übersicht


Setup & Abhängigkeiten

Voraussetzungen

  • Node.js 16+ (empfohlen: LTS)
  • npm 7+
  • Git (für Git-Operationen)
  • Visual Studio Code (optional, empfohlen)

Installation

# Repository klonen
git clone https://github.com/dein-username/git-manager-gui.git
cd git-manager-gui

# Dependencies installieren
npm install

# App starten
npm start

Wichtige Dependencies

Package Verwendung
electron Desktop-Framework
sqlite3 Lokale Datenspeicherung (Favoriten, Verlauf)
axios HTTP-Requests zu Gitea/GitHub APIs
crypto Verschlüsselung von Credentials
jest Testing Framework

Entwicklung starten

Dev-Server / Hot Reload

Es gibt aktuell keinen automatischen Hot Reload. Nach Code-Änderungen:

# App neu starten
npm start

# Oder im laufenden Fenster: DevTools öffnen
# Strg+Shift+I → Reload (Strg+R)

DevTools debuggen

// In main.js, beim Erstellen des Fensters:
mainWindow.webContents.openDevTools();

// Oder per Shortcut im laufenden Fenster:
// Strg+Shift+I

Logs anschauen

# Logs der letzten Session anschauen:
# Windows: %APPDATA%\git-manager-gui\logs\
# oder direkt im app data folder

# Mit Winston logger (falls implementiert):
npm run logs

Wichtige Module

renderer/renderer.js — Hauptlogik

Das Herzstück der App. Enthält:

  • Globale State-Variablen: favorites, recentRepos, currentState, pinnedRepos
  • Grid-Rendering: loadRepos(), loadGiteaRepos(), loadGithubRepos()
  • Kontext-Menü: showRepoContextMenu()
  • Datei-Explorer: loadRepoContents(), renderExplorer()
  • Editor: openFileEditor(), mit Speichern & Syntax-Highlighting
  • Favoriten/Verlauf: addToFavorites(), addToRecent()

Wichtige Funktionen:

// Repository-Liste laden
loadRepos() → loadGiteaRepos() oder loadGithubRepos()

// Repository öffnen (Datei-Explorer)
loadRepoContents(owner, repo, path)

// Datei bearbeiten
openFileEditor(owner, repo, path, content)

// Favoriten hinzufügen
async addToFavorites(owner, repo, cloneUrl, platform)

// Settings speichern
async saveSettings(settings)

src/git/gitHandler.js — Git-Operationen

Wrapper um Git CLI. Beispiele:

// Commits einsehen
const commits = await getCommitLog(repoPath, branch);

// Tags auflisten
const tags = await getTagList(repoPath);

// Branch wechseln
await checkoutBranch(repoPath, branchName);

// Lokales Repo klonen
await cloneRepository(cloneUrl, targetPath);

src/git/apiHandler.js — API-Calls

HTTP-Wrapper für Gitea/GitHub APIs.

// Gitea Repositories auflisten
const repos = await getGiteaRepos(url, token, username);

// GitHub Repositories
const repos = await getGithubRepos(token);

// Repository erstellen
await createGiteaRepo(url, token, { name, description, private });

// Datei speichern
await updateGiteaFile(url, token, owner, repo, path, content, message);

renderer/modules/state.js — State Management

Persistiert Favoriten, Verlauf und Settings.

// State laden/speichern
loadState() → returns { favorites, recent, pinnedRepos }
saveState(state) → speichert lokal

// Helpers
addFavorite(repo)
removeFavorite(repo)
addRecentRepo(repo)
clearRecent()

Neue Features hinzufügen

Beispiel: Neuer API-Endpoint integrieren

Schritt 1: API-Wrapper in src/git/apiHandler.js ergänzen

/**
 * Gitea Webhook auflisten
 */
async function getGiteaWebhooks(url, token, owner, repo) {
    const path = `/api/v1/repos/${owner}/${repo}/hooks`;
    return axios.get(`${url}${path}`, {
        headers: { 'Authorization': `token ${token}` }
    }).then(r => r.data);
}

module.exports = { getGiteaWebhooks, /* ... other functions */ };

Schritt 2: IPC-Handler in main.js ergänzen

// In ipcMain.handle() Bereich:
ipcMain.handle('getGiteaWebhooks', async (event, { url, token, owner, repo }) => {
    try {
        const webhooks = await apiHandler.getGiteaWebhooks(url, token, owner, repo);
        return { ok: true, webhooks };
    } catch (err) {
        return { ok: false, error: err.message };
    }
});

Schritt 3: UI in renderer/renderer.js hinzufügen

// Button im Repo-Kontextmenü:
if (option === 'webhooks') {
    const webhooks = await window.electronAPI.getGiteaWebhooks({
        url: currentState.giteaUrl,
        token: credentials.giteaToken,
        owner,
        repo
    });
    if (webhooks.ok) {
        showWebhookList(webhooks.webhooks);
    } else {
        showError('Webhooks laden fehlgeschlagen: ' + webhooks.error);
    }
}

Build & Release

Production Build

# Electron App als .exe packagen
npm run build

# Output: dist/git-manager-gui-Setup-2.x.x.exe

Abbrechbare Vorgänge

Uploads, Ordner-Downloads und Batch-Clones laufen über ein gemeinsames Abbruch-System:

  • main.js: beginCancellableUpload() legt einen AbortController an, endCancellableUpload() räumt auf (immer im finally). Der IPC-Kanal cancel-upload bricht alle laufenden Vorgänge ab.
  • Das signal wird durchgereicht an simple-git (abort), execGit/runGitAsync (signal), runLimited und uploadItemsViaGiteaApi (axios signal).
  • Ein Abbruch ist kein Fehler: Er läuft als UPLOAD_CANCELLED durch die Kette (createUploadCancelledError, isUploadCancelledError, throwIfUploadCancelled in src/git/apiHandler.js), löst keinen API-Ausweichweg aus und kommt als { ok: false, cancelled: true } im Renderer an.
  • Neuer abbrechbarer Handler: beginCancellableUpload() am Anfang, signal weiterreichen, im catch isUploadCancelledError(e) abfangen, im finally endCancellableUpload(). Im Renderer showProgress(..., { cancellable: true }).
  • Unter Windows beendet spawn(signal) nur git.exe; execGit beendet deshalb den ganzen Prozessbaum (taskkill /T /F) und kehrt sofort zurück.

Pause, Sperre, Benachrichtigung, Release-Assistent

  • Pause: src/features/pauseGate.js (createPauseGate). In main.js gibt es genau ein transferGate. Kontrollpunkte rufen await transferGate.wait(signal) auf (in runLimited, vor dem Staging der Git-Wege, zwischen den Paketen von uploadItemsViaGiteaApi über waitIfPaused, vor jedem Batch-Clone). Ein Abbruch löst Wartende sofort aus. Neue langlaufende Schritte brauchen einen eigenen Kontrollpunkt.
  • Zugangsdaten-Sperre: src/features/uploadBlocklist.js nutzt die Dateinamen-Regeln aus secretScan.js. removeBlockedInPlace() und blockedSecretReason() in main.js sind in allen Upload-Handlern eingebaut, die Ergebnisse tragen blockedFiles. Neuer Upload-Weg: dort ebenfalls aufrufen. Einstellung uploadBlockSecrets.
  • Benachrichtigung: src/features/transferNotice.js baut den Text aus dem Verlaufseintrag, maybeNotifyTransfer() in main.js zeigt ihn nur, wenn das Hauptfenster nicht vorn ist (Einstellung notifyTransfers).
  • Release-Assistent: src/features/releaseAssist.js (Version aus Dateiname, Versionsvergleich, Tag-Prüfung, Release-Text mit Prüfsummen). IPC inspect-release-file und prepare-release, Oberfläche in showCreateReleaseModal(). Das Prüfsummen-Format in buildReleaseBody muss zu extractChecksumFromReleaseBody in updater.js passen.

Startanimation

Nach dem Vorbild des AeroMC-Launchers (AeroMC-Launcher/src/startanimation.js):

  1. createWindow() berechnet Position und Größe (centeredBounds) und legt über begin() zuerst ein randloses, durchsichtiges Fenster mit renderer/splash.html an: das Logo dreht sich (CSS rotateY), darunter der Schriftzug.
  2. Das Hauptfenster entsteht danach mit denselben Maßen, verborgen, und lädt.
  3. play() wartet auf did-finish-load (höchstens 12 s) und mindestens 1,6 s Drehzeit. Dann zeigt es das Hauptfenster unsichtbar (Deckkraft 0, Mausereignisse aus), wartet auf die gezeichnete Oberfläche (Repo-Liste oder höchstens 1,5 s), fotografiert es mit capturePage() und lässt aufbauen() in splash.js das Bild von der Mitte aus aufziehen.
  4. Am Ende übernimmt das echte Fenster an derselben Stelle. Fehler und Zeitüberschreitungen zeigen das Hauptfenster immer trotzdem.

Stolperstein: setIgnoreMouseEvents(true) am Startfenster darf erst nach dem Zeigen und leicht verzögert aufgerufen werden (IGNORE_MOUSE_DELAY_MS). Davor aufgerufen wird das durchsichtige Fenster unter Windows weiß. scripts/ui-smoke-splash.js prüft das über eine Bildschirmaufnahme (Farbe an einer leeren Stelle muss der des Desktops entsprechen) und darf deshalb die Hardware-Beschleunigung nicht abschalten.

Beim Start mit --hidden und bei abgeschalteter Einstellung showSplash gibt es die Animation nicht. scripts/ui-smoke-splash.js prüft sie mit einem Ersatz-Hauptfenster. Im Entwicklungsmodus (electron .) startet die App wegen der vielen node_modules-Dateien deutlich langsamer als das gepackte Programm; die Animation selbst wird dadurch nicht langsamer.

Oberflächen-Testlauf

npm run test:ui (scripts/ui-smoke.js) startet die App mit leerem eigenen APPDATA, steuert sie über den Debug-Port und prüft Toolbar-Layout bei drei Breiten, Fortschrittsbalken, Pause, Verlauf, Sperre, Release-Prüfung und Vorlagen. scripts/ui-smoke-tray.js prüft das Tray-Menüfenster in echtem Electron. Der Lauf berührt weder echte Zugangsdaten noch einen Server und dauert gut eine Minute.

Tray-Menü

Das Tray-Symbol öffnet kein natives Menü, sondern ein eigenes kleines Fenster, denn das native Electron-Menü zeichnet unter Windows keine Hover-Markierung:

  • src/features/trayMenu.js: Inhalt (buildTrayTemplate, serializeTemplate, runAction, trayTooltip), ohne Electron und damit testbar. Neuer Eintrag: dort mit id ergänzen, die Aktion in trayTemplate() in main.js verdrahten.
  • src/features/trayPopup.js: das Fenster (frameless, transparent, schließt bei Fokusverlust und Esc). Es wird 2 Sekunden nach dem Start vorgeladen, weil der erste Renderer-Start mehrere Sekunden dauert.
  • renderer/tray-menu.html, renderer/tray-menu.js, tray-menu-preload.js: Darstellung (hell/dunkel nach Windows-Design, Pfeiltasten und Enter) und die vier IPC-Kanäle.
  • Das Fenster ist von der Zoom-Einstellung ausgenommen und wird beim Beenden zerstört, sonst hielte das versteckte Fenster die App am Leben.

Upload-Verlauf und Wiederholen

  • src/features/uploadHistory.js (buildEntry, filterEntries, createUploadHistory): speichert die letzten 100 Uploads in upload-history.json im userData-Ordner. main.js umhüllt dafür ipcMain.handle für die Kanäle in UPLOAD_HISTORY_CHANNELS, neue Upload-Kanäle dort eintragen.
  • Teilfehler: buildPartialUploadError hängt die fehlgeschlagenen Einträge an den Fehler (failedItems). Die Handler geben sie als retryItems zurück, der Renderer sendet sie über den Parameter items erneut (retryFailedUploads).
  • .gitignore-Vorlagen: src/features/gitignoreTemplates.js.

Version erhöhen

  1. Version in package.json updaten: "version": "2.2.1"
  2. Changelog aktualisieren (optional)
  3. Build ausführen
  4. GitHub Release erstellen mit der .exe

Debugging & Testing

Unit Tests schreiben

Jest ist konfiguriert. Test-Datei Vorlage:

// __tests__/myFeature.test.js
const { myFunction } = require('../src/git/gitHandler');

describe('Git Handler', () => {
    it('should parse commit log', () => {
        const result = myFunction(testData);
        expect(result.length).toBe(3);
        expect(result[0].sha).toMatch(/^[a-f0-9]{7}/);
    });
});

Tests starten:

npm test

# Mit Coverage:
npm test -- --coverage

# Watch Mode:
npm test -- --watch

Debugging im DevTools

// Im renderer.js oder irgendwo im Renderer:
console.log('Debug Info:', data);

// Öffne DevTools: Strg+Shift+I
// Console Tab → alle Logs sichtbar

Main Process debuggen

// In main.js:
console.log('Main Process Log:', data);

// Logs erscheinen in der Terminal-Console
// wenn die App mit npm start gestartet wurde

Code-Konventionen

Variablen & Funktionen

// camelCase für Variablen und Funktionen
const repoName = "mein-projekt";
function loadRepositories() { }

// SCREAMING_SNAKE_CASE für Konstanten
const MAX_RETRIES = 5;
const DEFAULT_TIMEOUT = 3000;

// Präfixe für Boolean-Variablen
const isPrivate = true;
const hasCredentials = false;
const shouldRetry = true;

Async/Await

// Immer try-catch für async Funktionen
async function loadData() {
    try {
        const data = await fetchFromAPI();
        return { ok: true, data };
    } catch (error) {
        console.error('Error:', error);
        return { ok: false, error: error.message };
    }
}

Kommentare

// Kurze Inline-Kommentare über Code
const delay = 1000; // Millisekunden

/**
 * Längere Beschreibung vor Funktion
 * @param {string} repoName - Name des Repositories
 * @returns {Promise<Object>} Result mit { ok, data, error }
 */
async function loadRepository(repoName) {
    // Implementierung
}

Fehlerbehandlung

// Immer aussagekräftige Fehlermeldungen
if (!token) {
    return { ok: false, error: 'missing-token' };
}

// Oder sprechend für Benutzer:
if (!token) {
    return { ok: false, error: 'Gitea Token nicht gespeichert. Bitte Settings öffnen.' };
}

Häufige Aufgaben

Neue Einstellung hinzufügen

  1. In Settings.jsx UI-Control hinzufügen

    <label>Neue Option</label>
    <input type="checkbox" onChange={(e) => setSetting('newOption', e.target.checked)} />
    
  2. In renderer.js speichern

    async function saveSettings(settings) {
        const result = await window.electronAPI.saveSettings(settings);
        // ...
    }
    
  3. In main.js Handler

    ipcMain.handle('saveSettings', async (event, settings) => {
        // Validation und Speicherung
        return { ok: true };
    });
    

Neuer Button in der Toolbar

  1. In index.html Button hinzufügen

    <button id="btnMyFeature" title="Mein Feature">🎯 Feature</button>
    
  2. In renderer.js Event-Handler

    if ($('btnMyFeature')) {
        $('btnMyFeature').onclick = () => {
            myFeatureHandler();
        };
    }
    

Neue Kontextmenü-Option

  1. In showRepoContextMenu() Item hinzufügen

    items.push({
        label: '🎯 Meine Aktion',
        click: () => myActionHandler(owner, repo)
    });
    
  2. Handler implementieren

    function myActionHandler(owner, repo) {
        // Logik...
    }
    

Neue API-Methode aufrufen

  1. In src/git/apiHandler.js ergänzen
  2. In main.js IPC-Handler anlegen
  3. In renderer.js aufrufen:
    const result = await window.electronAPI.myNewMethod({ param1, param2 });
    

Troubleshooting für Entwickler

App startet nicht

# Node/npm Version prüfen
node --version  # sollte >= 16
npm --version   # sollte >= 7

# Dependencies neu installieren
rm -r node_modules
npm install
npm start

Fehler: "Cannot find module"

# Dependencies aktualisieren
npm install

# Cache löschen
npm cache clean --force

Axios/API-Calls schlagen fehl

  • URL/Token in Settings prüfen
  • Gitea-Server online? curl https://git.example.com/api/v1/user
  • Token noch gültig? Im Gitea-Interface regenerieren
  • Firewall/VPN blockiert?

Datei-Upload funktioniert nicht

  • Lokale Temp-Pfade prüfen (in main.js)
  • Git-Auth konfiguriert?
  • Ausreichend Festplatte?

Ressourcen


Lizenz & Beitragen

Dieses Projekt ist unter der MIT-Lizenz lizenziert.
Für Beiträge bitte einen Pull Request einreichen mit aussagekräftiger Beschreibung.


Letzte Aktualisierung: 13. Mai 2026 | Ersteller: M_Viper