<script lang="ts">
  import { onMount, tick } from "svelte";
  import {
    AlertTriangle,
    BookOpen,
    Box,
    Check,
    ChevronRight,
    CircleHelp,
    Clipboard,
    Cloud,
    Command,
    GitBranch,
    GitCommitHorizontal,
    Home,
    Keyboard,
    Library,
    Lightbulb,
    ListChecks,
    Search,
    Sparkles,
    Wrench,
    X,
  } from "@lucide/svelte";

  interface CommandExample {
    command: string;
    description: string;
  }

  interface HelpSection {
    id: string;
    title: string;
    summary: string;
    steps?: string[];
    commands?: CommandExample[];
    note?: string;
  }

  interface HelpCategory {
    id: string;
    label: string;
    description: string;
    sections: HelpSection[];
  }

  interface Props {
    language: "en" | "de";
    onClose: () => void;
  }

  const deCategories: HelpCategory[] = [
    {
      id: "start",
      label: "Erste Schritte",
      description: "Repository öffnen, Oberfläche verstehen und den ersten Commit erstellen.",
      sections: [
        {
          id: "start-workflow",
          title: "Dein erster Gitty-Workflow",
          summary: "Vom Repository bis zum veröffentlichten Commit in fünf klaren Schritten.",
          steps: [
            "Öffne ein vorhandenes Repository oder klone ein Projekt über die Repository-Verwaltung.",
            "Bearbeite deine Dateien. Gitty zeigt Änderungen im Arbeitsverzeichnis automatisch an.",
            "Prüfe den Diff und stage einzelne Dateien, Zeilen oder alle passenden Änderungen.",
            "Schreibe eine aussagekräftige Commit-Nachricht und erstelle den Commit.",
            "Nutze Fetch, Pull und Push, um deinen Stand mit dem Remote-Repository abzugleichen.",
          ],
        },
        {
          id: "start-layout",
          title: "Die Oberfläche im Überblick",
          summary: "Links findest du Branches, Stashes und Dateien. In der Mitte prüfst und commitest du Änderungen; rechts siehst du Verlauf und Dateihistorie.",
          note: "Fast alle Bereiche lassen sich über die Trennlinien in der Größe anpassen oder über ihren Kopf einklappen.",
        },
        {
          id: "start-safety",
          title: "Sicher arbeiten",
          summary: "Prüfe vor Commit, Pull, Rebase oder Verwerfen immer den aktuellen Branch und die betroffenen Dateien. Gitty fragt bei destruktiven Aktionen nochmals nach.",
        },
      ],
    },
    {
      id: "app",
      label: "Arbeiten mit Gitty",
      description: "Die wichtigsten Funktionen der App Schritt für Schritt.",
      sections: [
        {
          id: "app-changes",
          title: "Änderungen prüfen und stagen",
          summary: "Wähle eine geänderte Datei, lies den Diff und verschiebe gezielt Änderungen in den Staging-Bereich.",
          steps: [
            "Unstaged enthält noch nicht vorbereitete Änderungen; Staged enthält den nächsten Commit-Inhalt.",
            "Öffne eine Datei, um hinzugefügte und entfernte Zeilen zu prüfen.",
            "Stage ganze Dateien oder nutze die Zeilen-/Hunk-Aktionen für kleinere, saubere Commits.",
            "Verwerfen entfernt lokale Änderungen. Diese Aktion lässt sich nicht immer rückgängig machen.",
          ],
        },
        {
          id: "app-history",
          title: "Verlauf, Suche und Vergleich",
          summary: "Der Commit-Verlauf zeigt Branches und Merges. Mit der globalen Suche findest du die Einführung von Code oder den Verlauf einer Datei.",
          steps: [
            "Klappe einen Commit auf, um seine Dateien zu sehen.",
            "Vergleiche zwei Commits über Compare in der Titelleiste.",
            "Nutze Reflog, um auch verschobene oder nicht mehr sichtbare Referenzen wiederzufinden.",
          ],
        },
        {
          id: "app-stash",
          title: "Zwischenstände mit Stash sichern",
          summary: "Ein Stash parkt unvollständige Änderungen, ohne einen Commit zu erzeugen. Apply behält den Stash, Pop wendet ihn an und entfernt ihn.",
        },
      ],
    },
    {
      id: "basics",
      label: "Git-Grundlagen",
      description: "Alltägliche Befehle für Status, Änderungen, Commits und Wiederherstellung.",
      sections: [
        {
          id: "basics-inspect",
          title: "Repository prüfen",
          summary: "Diese Befehle verändern nichts und eignen sich immer als erster Blick auf den aktuellen Zustand.",
          commands: [
            { command: "git status", description: "Arbeitsverzeichnis und Staging-Bereich anzeigen" },
            { command: "git diff", description: "Noch nicht gestagte Änderungen anzeigen" },
            { command: "git diff --staged", description: "Inhalt des nächsten Commits anzeigen" },
            { command: "git log --oneline --graph --decorate --all", description: "Kompakten Branch- und Commit-Verlauf anzeigen" },
          ],
        },
        {
          id: "basics-commit",
          title: "Änderungen speichern",
          summary: "Stage nur zusammengehörige Änderungen und beschreibe im Commit, warum die Änderung nötig ist.",
          commands: [
            { command: "git add <datei>", description: "Eine Datei für den Commit vormerken" },
            { command: "git add -p", description: "Änderungen interaktiv und abschnittsweise stagen" },
            { command: "git commit -m \"Kurze Beschreibung\"", description: "Einen Commit erstellen" },
            { command: "git commit --amend", description: "Den letzten lokalen Commit ergänzen oder umbenennen" },
          ],
        },
        {
          id: "basics-undo",
          title: "Änderungen rückgängig machen",
          summary: "Restore arbeitet am Arbeitsverzeichnis oder Staging-Bereich; Revert erzeugt einen neuen Gegen-Commit und ist für veröffentlichte Historie sicherer.",
          commands: [
            { command: "git restore <datei>", description: "Lokale, noch nicht gestagte Änderung verwerfen" },
            { command: "git restore --staged <datei>", description: "Datei aus dem Staging-Bereich entfernen" },
            { command: "git revert <commit>", description: "Wirkung eines Commits durch neuen Commit umkehren" },
          ],
          note: "Vorsicht: git reset --hard verwirft lokale Änderungen. Verwende den Befehl nur, wenn du den Verlust bewusst akzeptierst.",
        },
      ],
    },
    {
      id: "branches",
      label: "Branches & Merges",
      description: "Parallele Arbeit organisieren, zusammenführen und aufräumen.",
      sections: [
        {
          id: "branches-manage",
          title: "Branches verwalten",
          summary: "Ein Branch ist ein beweglicher Zeiger auf eine Commit-Linie. Erstelle für jede getrennte Aufgabe einen eigenen Branch.",
          commands: [
            { command: "git switch -c feature/meine-aenderung", description: "Neuen Branch erstellen und wechseln" },
            { command: "git switch main", description: "Zu einem vorhandenen Branch wechseln" },
            { command: "git branch -vv", description: "Lokale Branches mit Upstream-Status anzeigen" },
            { command: "git branch -d <branch>", description: "Bereits zusammengeführten Branch löschen" },
          ],
        },
        {
          id: "branches-integrate",
          title: "Merge oder Rebase?",
          summary: "Merge bewahrt die tatsächliche Verzweigung. Rebase setzt lokale Commits auf eine neue Basis und erzeugt eine lineare Historie.",
          commands: [
            { command: "git merge <branch>", description: "Einen Branch in den aktuellen Branch integrieren" },
            { command: "git rebase main", description: "Lokale Commits auf den aktuellen Stand von main setzen" },
            { command: "git rebase --continue", description: "Rebase nach gelöstem Konflikt fortsetzen" },
            { command: "git rebase --abort", description: "Rebase abbrechen und Ausgangszustand wiederherstellen" },
          ],
          note: "Rebase keine Commits, an denen andere bereits weiterarbeiten. Das Umschreiben veröffentlichter Historie verursacht unnötige Konflikte.",
        },
      ],
    },
    {
      id: "remote",
      label: "Remote & Sync",
      description: "Änderungen sicher mit GitHub, GitLab oder anderen Remotes austauschen.",
      sections: [
        {
          id: "remote-sync",
          title: "Fetch, Pull und Push",
          summary: "Fetch lädt Referenzen ohne deine Dateien zu ändern. Pull integriert Remote-Änderungen. Push veröffentlicht deine Commits.",
          commands: [
            { command: "git fetch --all --prune", description: "Remote-Stände laden und entfernte Referenzen aufräumen" },
            { command: "git pull --rebase", description: "Remote-Änderungen laden und lokale Commits darauf neu abspielen" },
            { command: "git push -u origin <branch>", description: "Branch erstmals veröffentlichen und Upstream setzen" },
            { command: "git remote -v", description: "Konfigurierte Remote-Adressen anzeigen" },
          ],
        },
        {
          id: "remote-ahead",
          title: "Ahead und Behind verstehen",
          summary: "Ahead bedeutet: lokale Commits wurden noch nicht gepusht. Behind bedeutet: im Remote liegen neue Commits, die lokal fehlen. Beides gleichzeitig weist auf auseinanderlaufende Historien hin.",
        },
      ],
    },
    {
      id: "troubleshooting",
      label: "Probleme lösen",
      description: "Konflikte, verlorene Commits und typische Fehlermeldungen verstehen.",
      sections: [
        {
          id: "trouble-conflicts",
          title: "Merge-Konflikte lösen",
          summary: "Ein Konflikt entsteht, wenn Git Änderungen nicht eindeutig kombinieren kann. Gitty bietet dafür einen eigenen Resolve-Dialog.",
          steps: [
            "Öffne jede Konfliktdatei und vergleiche Current, Incoming und das kombinierte Ergebnis.",
            "Übernimm eine Seite oder bearbeite den Zielinhalt manuell.",
            "Markiere die Datei als gelöst und prüfe anschließend den vollständigen Diff.",
            "Führe Merge, Rebase oder Cherry-pick fort – oder brich die Operation vollständig ab.",
          ],
          commands: [
            { command: "git status", description: "Konfliktdateien und laufende Operation anzeigen" },
            { command: "git merge --abort", description: "Laufenden Merge abbrechen" },
            { command: "git cherry-pick --abort", description: "Laufenden Cherry-pick abbrechen" },
          ],
        },
        {
          id: "trouble-reflog",
          title: "Verlorene Commits wiederfinden",
          summary: "Reflog protokolliert lokale Bewegungen von HEAD und Branches. Kopiere den Hash des gesuchten Eintrags und erstelle daraus einen Sicherungs-Branch.",
          commands: [
            { command: "git reflog", description: "Lokale Referenzbewegungen anzeigen" },
            { command: "git branch recovery/<name> <commit>", description: "Gefundenen Commit dauerhaft über Branch sichern" },
          ],
        },
      ],
    },
    {
      id: "shortcuts",
      label: "Tastenkürzel",
      description: "Gitty schneller und ohne Maus bedienen.",
      sections: [
        {
          id: "shortcuts-main",
          title: "Globale Bedienung",
          summary: "Die Hilfe ist überall erreichbar. Dialoge lassen sich konsistent schließen und Suchfelder direkt fokussieren.",
          commands: [
            { command: "Ctrl + /", description: "Diese Hilfe öffnen" },
            { command: "Escape", description: "Aktuelles Overlay oder Dialogfenster schließen" },
            { command: "Tab / Shift + Tab", description: "Zwischen Bedienelementen wechseln" },
            { command: "Enter / Leertaste", description: "Fokussierte Aktion ausführen" },
          ],
          note: "Auf macOS kannst du für Ctrl in der Regel die Command-Taste verwenden.",
        },
      ],
    },
  ];

  const enCategories: HelpCategory[] = [
    {
      id: "start",
      label: "Getting started",
      description: "Open a repository, understand the interface, and create your first commit.",
      sections: [
        {
          id: "start-workflow",
          title: "Your first Gitty workflow",
          summary: "From repository to published commit in five clear steps.",
          steps: [
            "Open an existing repository or clone a project from Repository Management.",
            "Edit your files. Gitty automatically displays working-tree changes.",
            "Review the diff and stage individual files, lines, or all related changes.",
            "Write a meaningful commit message and create the commit.",
            "Use Fetch, Pull, and Push to synchronize with the remote repository.",
          ],
        },
        {
          id: "start-layout",
          title: "Interface overview",
          summary: "Branches, stashes, and files are on the left. Review and commit changes in the center; history and file history are on the right.",
          note: "Most areas can be resized using their dividers or collapsed from their header.",
        },
        {
          id: "start-safety",
          title: "Work safely",
          summary: "Before committing, pulling, rebasing, or discarding, always check the current branch and affected files. Gitty confirms destructive actions.",
        },
      ],
    },
    {
      id: "app",
      label: "Working with Gitty",
      description: "The app's most important features, explained step by step.",
      sections: [
        {
          id: "app-changes",
          title: "Review and stage changes",
          summary: "Select a changed file, review its diff, and move changes into the staging area.",
          steps: [
            "Unstaged contains changes not yet prepared; Staged contains the next commit.",
            "Open a file to review added and removed lines.",
            "Stage whole files or use line and hunk actions for focused commits.",
            "Discard removes local changes and cannot always be undone.",
          ],
        },
        {
          id: "app-history",
          title: "History, search, and comparison",
          summary: "Commit History shows branches and merges. Global Search finds where code was introduced or displays a file's history.",
          steps: [
            "Expand a commit to inspect its files.",
            "Compare two commits with Compare in the title bar.",
            "Use Reflog to recover moved or otherwise hidden references.",
          ],
        },
        {
          id: "app-stash",
          title: "Save temporary work with Stash",
          summary: "A stash parks unfinished changes without creating a commit. Apply keeps the stash; Pop applies and removes it.",
        },
      ],
    },
    {
      id: "basics",
      label: "Git basics",
      description: "Everyday commands for status, changes, commits, and recovery.",
      sections: [
        {
          id: "basics-inspect",
          title: "Inspect a repository",
          summary: "These commands do not change anything and are always a good first look at the current state.",
          commands: [
            { command: "git status", description: "Show the working tree and staging area" },
            { command: "git diff", description: "Show changes that have not been staged" },
            { command: "git diff --staged", description: "Show the contents of the next commit" },
            { command: "git log --oneline --graph --decorate --all", description: "Show a compact branch and commit graph" },
          ],
        },
        {
          id: "basics-commit",
          title: "Save changes",
          summary: "Stage only related changes and explain why the change is needed in the commit message.",
          commands: [
            { command: "git add <file>", description: "Stage one file" },
            { command: "git add -p", description: "Stage changes interactively by hunk" },
            { command: "git commit -m \"Short description\"", description: "Create a commit" },
            { command: "git commit --amend", description: "Update or rename the latest local commit" },
          ],
        },
        {
          id: "basics-undo",
          title: "Undo changes",
          summary: "Restore changes the working tree or staging area. Revert creates a new inverse commit and is safer for published history.",
          commands: [
            { command: "git restore <file>", description: "Discard an unstaged local change" },
            { command: "git restore --staged <file>", description: "Remove a file from the staging area" },
            { command: "git revert <commit>", description: "Undo a commit through a new commit" },
          ],
          note: "Caution: git reset --hard discards local changes. Use it only when that data loss is intentional.",
        },
      ],
    },
    {
      id: "branches",
      label: "Branches & merges",
      description: "Organize parallel work, integrate it, and clean up afterward.",
      sections: [
        {
          id: "branches-manage",
          title: "Manage branches",
          summary: "A branch is a movable pointer to a commit line. Create a separate branch for each independent task.",
          commands: [
            { command: "git switch -c feature/my-change", description: "Create and switch to a new branch" },
            { command: "git switch main", description: "Switch to an existing branch" },
            { command: "git branch -vv", description: "Show local branches and upstream status" },
            { command: "git branch -d <branch>", description: "Delete a branch that has already been merged" },
          ],
        },
        {
          id: "branches-integrate",
          title: "Merge or rebase?",
          summary: "Merge preserves the actual branch structure. Rebase moves local commits onto a new base for a linear history.",
          commands: [
            { command: "git merge <branch>", description: "Integrate a branch into the current branch" },
            { command: "git rebase main", description: "Move local commits onto the latest main" },
            { command: "git rebase --continue", description: "Continue after resolving a conflict" },
            { command: "git rebase --abort", description: "Abort and restore the original state" },
          ],
          note: "Do not rebase commits other people are already using. Rewriting published history creates avoidable conflicts.",
        },
      ],
    },
    {
      id: "remote",
      label: "Remote & sync",
      description: "Exchange changes safely with GitHub, GitLab, or other remotes.",
      sections: [
        {
          id: "remote-sync",
          title: "Fetch, pull, and push",
          summary: "Fetch downloads references without changing files. Pull integrates remote changes. Push publishes your commits.",
          commands: [
            { command: "git fetch --all --prune", description: "Download remote state and remove stale references" },
            { command: "git pull --rebase", description: "Download changes and replay local commits on top" },
            { command: "git push -u origin <branch>", description: "Publish a branch and set its upstream" },
            { command: "git remote -v", description: "Show configured remote URLs" },
          ],
        },
        {
          id: "remote-ahead",
          title: "Understanding Ahead and Behind",
          summary: "Ahead means local commits have not been pushed. Behind means remote commits are missing locally. Both at once means the histories have diverged.",
        },
      ],
    },
    {
      id: "troubleshooting",
      label: "Troubleshooting",
      description: "Resolve conflicts, recover lost commits, and understand common errors.",
      sections: [
        {
          id: "trouble-conflicts",
          title: "Resolve merge conflicts",
          summary: "A conflict occurs when Git cannot combine changes unambiguously. Gitty provides a dedicated Resolve dialog.",
          steps: [
            "Open each conflicted file and compare Current, Incoming, and the combined result.",
            "Accept one side or edit the final content manually.",
            "Mark the file resolved and review the complete diff.",
            "Continue the merge, rebase, or cherry-pick—or abort the operation.",
          ],
          commands: [
            { command: "git status", description: "Show conflicts and the active operation" },
            { command: "git merge --abort", description: "Abort the current merge" },
            { command: "git cherry-pick --abort", description: "Abort the current cherry-pick" },
          ],
        },
        {
          id: "trouble-reflog",
          title: "Recover lost commits",
          summary: "Reflog records local movements of HEAD and branches. Copy the desired hash and create a recovery branch from it.",
          commands: [
            { command: "git reflog", description: "Show local reference movements" },
            { command: "git branch recovery/<name> <commit>", description: "Preserve the recovered commit with a branch" },
          ],
        },
      ],
    },
    {
      id: "shortcuts",
      label: "Keyboard shortcuts",
      description: "Use Gitty quickly without reaching for the mouse.",
      sections: [
        {
          id: "shortcuts-main",
          title: "Global controls",
          summary: "Help is available everywhere. Dialogs close consistently and search fields receive focus automatically.",
          commands: [
            { command: "Ctrl + /", description: "Open this help center" },
            { command: "Escape", description: "Close the current overlay or dialog" },
            { command: "Tab / Shift + Tab", description: "Move between controls" },
            { command: "Enter / Space", description: "Activate the focused control" },
          ],
          note: "On macOS, you can generally use Command instead of Ctrl.",
        },
      ],
    },
  ];

  deCategories.splice(deCategories.findIndex((category) => category.id === "remote") + 1, 0, {
    id: "lfs",
    label: "Git LFS",
    description: "Große Binärdateien tracken, Objekte synchronisieren und bestehende Repositories sicher umstellen.",
    sections: [
      {
        id: "lfs-overview",
        title: "Was Git LFS macht",
        summary: "Git LFS ersetzt große Dateien im Git-Verlauf durch kleine Zeigerdateien. Die eigentlichen Inhalte liegen im LFS-Speicher des Remotes und werden beim Checkout oder Pull passend geladen.",
        steps: [
          "Nutze LFS vor allem für große Binärdateien wie PSD-, Video-, Audio-, Modell- oder Archivdateien, die Git nicht sinnvoll als Text-Diff verwalten kann.",
          "Gitty liefert die Git-LFS-Erweiterung in Desktop-Installern mit und zeigt Version, Filter sowie Pre-push-Hook im LFS-Dialog an.",
          "Die LFS-Regeln stehen in .gitattributes und gehören deshalb wie normaler Quellcode in das Repository.",
        ],
      },
      {
        id: "lfs-setup",
        title: "Git LFS in Gitty einrichten",
        summary: "Die Einrichtung gilt für das aktuell geöffnete Repository und verändert keine globalen Git-Einstellungen.",
        steps: [
          "Öffne im Repository das Menü Synchronisieren und wähle Git LFS.",
          "Klicke auf LFS aktivieren, damit Gitty die lokalen Filter und den Pre-push-Hook einrichtet.",
          "Füge ein Muster wie *.psd, Assets/** oder video.mp4 hinzu. Lockable markiert Dateien, die über einen kompatiblen LFS-Server gesperrt werden können.",
          "Stage und committe anschließend .gitattributes zusammen mit den gewünschten Dateien.",
        ],
        commands: [
          { command: "git lfs install --local", description: "LFS nur im aktuellen Repository aktivieren" },
          { command: "git lfs track \"*.psd\"", description: "Ein Dateimuster über Git LFS verwalten" },
          { command: "git add .gitattributes", description: "Die erzeugten Tracking-Regeln stagen" },
        ],
      },
      {
        id: "lfs-sync",
        title: "LFS-Objekte synchronisieren",
        summary: "Clone und Pull prüfen in Gitty automatisch auf LFS und laden benötigte Objekte mit demselben Remote und denselben Zugangsdaten. Ein zweiter Pull ist nicht nötig.",
        steps: [
          "Neue Klone aus der Oberfläche sowie über --clone aktivieren LFS lokal und laden die Objekte von origin, bevor das Repository geöffnet wird.",
          "Normales Push nutzt den LFS-Pre-push-Hook und lädt neue LFS-Objekte vor den Git-Referenzen hoch.",
          "Objekte laden im LFS-Dialog ist ein manueller Reparatur- oder Aktualisierungsschritt, falls lokale Inhalte fehlen.",
          "Cache bereinigen entfernt sicher nicht mehr benötigte lokale Objekte; aktuell verwendete und noch nicht gepushte Inhalte bleiben erhalten.",
        ],
        commands: [
          { command: "git lfs status", description: "LFS-Zustand und ausstehende Änderungen prüfen" },
          { command: "git lfs pull", description: "Benötigte LFS-Objekte manuell laden" },
          { command: "git lfs prune", description: "Nicht mehr benötigte lokale LFS-Objekte bereinigen" },
          { command: "git lfs lock <datei>", description: "Eine als lockable markierte Datei auf einem kompatiblen Remote sperren" },
        ],
      },
      {
        id: "lfs-migrate",
        title: "Bestehende Dateien umstellen",
        summary: "Ein neu hinzugefügtes Tracking-Muster schreibt vorhandene Commits nicht rückwirkend um. Aktuelle Dateien lassen sich neu normalisieren; eine vollständige Migration verändert dagegen die Historie.",
        commands: [
          { command: "git add --renormalize .", description: "Aktuelle Dateien erneut durch die neuen LFS-Regeln führen" },
          { command: "git lfs migrate import --include=\"*.psd\" --everything", description: "Passende Dateien in der gesamten Historie nach LFS migrieren" },
        ],
        note: "Vorsicht: git lfs migrate import schreibt Commit-Hashes um. Stimme die Migration mit allen Beteiligten ab, erstelle vorher ein Backup und rechne bei bereits veröffentlichten Branches mit einem koordinierten Force-Push.",
      },
    ],
  });

  enCategories.splice(enCategories.findIndex((category) => category.id === "remote") + 1, 0, {
    id: "lfs",
    label: "Git LFS",
    description: "Track large binary files, synchronize objects, and migrate existing repositories safely.",
    sections: [
      {
        id: "lfs-overview",
        title: "What Git LFS does",
        summary: "Git LFS replaces large files in Git history with small pointer files. The actual content is stored in the remote's LFS storage and downloaded for the relevant checkout or pull.",
        steps: [
          "Use LFS mainly for large binary files such as PSDs, videos, audio, models, or archives that Git cannot usefully manage as text diffs.",
          "Gitty bundles the Git LFS extension in desktop installers and displays its version, filters, and pre-push hook in the LFS dialog.",
          "LFS rules live in .gitattributes, so commit them to the repository like regular source code.",
        ],
      },
      {
        id: "lfs-setup",
        title: "Set up Git LFS in Gitty",
        summary: "Setup applies to the currently open repository and does not change global Git settings.",
        steps: [
          "Open the Sync menu in the repository and select Git LFS.",
          "Select Activate LFS so Gitty configures the local filters and pre-push hook.",
          "Add a pattern such as *.psd, Assets/**, or video.mp4. Lockable marks files that can be locked through a compatible LFS server.",
          "Stage and commit .gitattributes together with the files you want to track.",
        ],
        commands: [
          { command: "git lfs install --local", description: "Activate LFS only in the current repository" },
          { command: "git lfs track \"*.psd\"", description: "Manage a file pattern through Git LFS" },
          { command: "git add .gitattributes", description: "Stage the generated tracking rules" },
        ],
      },
      {
        id: "lfs-sync",
        title: "Synchronize LFS objects",
        summary: "Clone and pull automatically check for LFS in Gitty and download required objects with the same remote and credentials. A second pull is not necessary.",
        steps: [
          "Fresh clones from the UI and --clone activate LFS locally and download objects from origin before the repository is opened.",
          "A normal push uses the LFS pre-push hook to upload new LFS objects before Git references are published.",
          "Pull objects in the LFS dialog is a manual repair or refresh action when local content is missing.",
          "Prune cache safely removes unused local objects while retaining current and unpushed content.",
        ],
        commands: [
          { command: "git lfs status", description: "Inspect LFS state and pending changes" },
          { command: "git lfs pull", description: "Download required LFS objects manually" },
          { command: "git lfs prune", description: "Remove unused local LFS objects" },
          { command: "git lfs lock <file>", description: "Lock a lockable file on a compatible remote" },
        ],
      },
      {
        id: "lfs-migrate",
        title: "Migrate existing files",
        summary: "Adding a tracking pattern does not rewrite existing commits. Current files can be renormalized, while a complete migration changes repository history.",
        commands: [
          { command: "git add --renormalize .", description: "Run current files through the new LFS rules again" },
          { command: "git lfs migrate import --include=\"*.psd\" --everything", description: "Move matching files to LFS throughout repository history" },
        ],
        note: "Caution: git lfs migrate import rewrites commit hashes. Coordinate the migration with every contributor, create a backup first, and expect a coordinated force push for published branches.",
      },
    ],
  });

  // Extended handbook chapters. Keeping these additions next to the shared data makes
  // it straightforward to compare the German and English coverage section by section.
  deCategories.find((category) => category.id === "start")?.sections.push(
    {
      id: "start-model",
      title: "Das Git-Grundmodell verstehen",
      summary: "Git speichert keine fortlaufende Liste einzelner Dateiänderungen, sondern verknüpfte Schnappschüsse deines Projekts. HEAD zeigt auf deinen aktuellen Commit; der Branch-Name bewegt sich beim Commit mit.",
      steps: [
        "Arbeitsverzeichnis: Hier bearbeitest du echte Dateien. Änderungen sind noch nicht Teil eines Commits.",
        "Staging-Bereich: Hier stellst du exakt den Inhalt des nächsten Commits zusammen.",
        "Lokales Repository: Commits, Branches und Tags liegen zunächst nur auf deinem Rechner.",
        "Remote-Repository: Push veröffentlicht lokale Commits; Fetch lädt fremde Referenzen; Pull lädt und integriert.",
      ],
      note: "Der Staging-Bereich ist kein zusätzlicher Ordner. Er ist ein Git-Schnappschuss, den Gitty als „Staged“ darstellt.",
    },
    {
      id: "start-before-work",
      title: "Checkliste vor jeder Aufgabe",
      summary: "Ein kurzer Zustandscheck verhindert die meisten versehentlichen Commits und komplizierten Konflikte.",
      commands: [
        { command: "git status --short --branch", description: "Branch, Upstream und Änderungen kompakt prüfen" },
        { command: "git fetch --prune", description: "Remote-Stand aktualisieren, ohne Dateien zu verändern" },
        { command: "git log --oneline --decorate -10", description: "Die letzten zehn Commits und Referenzen prüfen" },
      ],
      steps: [
        "Prüfe, ob du auf dem richtigen Branch bist.",
        "Sichere oder stash unvollständige Änderungen, bevor du den Branch wechselst.",
        "Hole Remote-Informationen mit Fetch und entscheide erst danach über Pull, Rebase oder Merge.",
      ],
    },
  );

  deCategories.find((category) => category.id === "app")?.sections.push(
    {
      id: "app-repositories",
      title: "Repositories und Tabs verwalten",
      summary: "Die Repository-Verwaltung bündelt offene, zuletzt verwendete und favorisierte Projekte. Jedes offene Repository erhält einen eigenen Tab mit Branch- und Änderungsstatus.",
      steps: [
        "Browse öffnet ein bestehendes lokales Repository; Clone lädt ein Remote-Repository in einen neuen Ordner.",
        "Markiere häufig verwendete Projekte als Favorit, damit sie unabhängig von der Verlaufsliste sichtbar bleiben.",
        "Wechsle über die Tabs zwischen Projekten. Gitty merkt sich Status, Größen und ausgewählte Bereiche pro Sitzung.",
        "Öffne das Tab-Kontextmenü, um ein Repository aus der aktuellen Arbeitsfläche zu entfernen, ohne Dateien zu löschen.",
      ],
    },
    {
      id: "app-commit-detail",
      title: "Saubere Commits in Gitty erstellen",
      summary: "Ein guter Commit enthält genau eine logisch zusammengehörige Änderung und lässt sich unabhängig erklären, prüfen und notfalls zurücknehmen.",
      steps: [
        "Prüfe zuerst Unstaged und den vollständigen Diff jeder betroffenen Datei.",
        "Stage nur passende Dateien, Hunks oder Zeilen. Tests und Implementierung dürfen zusammengehören; zufällige Formatierungen meist nicht.",
        "Lies anschließend ausschließlich den Staged-Diff – genau dieser Inhalt wird committed.",
        "Formuliere eine kurze, imperative Betreffzeile, zum Beispiel „Handle expired credentials“.",
        "Nutze Amend nur, solange der letzte Commit noch nicht von anderen verwendet wird.",
      ],
      note: "Wenn du im Staged-Diff etwas Überraschendes siehst, entferne es wieder aus dem Staging-Bereich. Ein Commit ist der falsche Ort für „wird schon passen“.",
    },
    {
      id: "app-branches-tags",
      title: "Branches und Tags in der App",
      summary: "Das Branch-Panel zeigt lokale und Remote-Branches sowie Ahead/Behind. Über das Kontextmenü kannst du wechseln, erstellen, umbenennen, löschen, mergen oder rebasen.",
      steps: [
        "Erstelle einen Branch vom aktuellen HEAD oder gezielt von einem Commit im Verlauf.",
        "Ein Checkout/Switch aktualisiert Arbeitsverzeichnis und HEAD. Sichere inkompatible lokale Änderungen vorher.",
        "Tags markieren feste Commits, typischerweise Releases. Ein Tag bewegt sich nicht automatisch weiter.",
        "Remote-Branches sind zunächst Referenzen. Erstelle beim Wechsel einen lokalen Tracking-Branch.",
      ],
    },
    {
      id: "app-history-tools",
      title: "History, Dateiverlauf, Blame und Restore",
      summary: "Gitty verbindet Commit-Graph, Dateiverlauf und Wiederherstellung, damit du Ursache und Entwicklung einer Änderung nachvollziehen kannst.",
      steps: [
        "Wähle eine Datei im Explorer, um ihren eigenen Verlauf unabhängig vom Gesamtprojekt zu sehen.",
        "Blame ordnet jeder aktuellen Zeile den letzten verändernden Commit zu. Nutze es als Einstieg, nicht als Schuldzuweisung.",
        "Compare zeigt Unterschiede zwischen zwei beliebigen Commits oder Branch-Spitzen.",
        "Restore from commit übernimmt eine ältere Dateiversion ins Arbeitsverzeichnis. Prüfe und committe das Ergebnis anschließend normal.",
      ],
    },
    {
      id: "app-git-notes",
      title: "Commits mit Git Notes ergänzen",
      summary: "Git Notes speichern zusätzliche Informationen zu einem Commit, ohne dessen Hash oder die Historie zu verändern. Sie eignen sich etwa für Review-Hinweise, Ticket-Kontext, Build-IDs oder Freigabestatus.",
      steps: [
        "Öffne im Commit-Verlauf über das Notiz-Symbol oder das Kontextmenü die Commit-Notiz.",
        "Schreibe oder bearbeite die Notiz und speichere sie. Commits mit einer Notiz sind im Verlauf markiert; beim Überfahren der Markierung erscheint eine Vorschau.",
        "Löschen entfernt nur die Notiz. Der zugehörige Commit und seine Dateien bleiben unverändert.",
        "Gitty lädt Git Notes im Hintergrund vom bevorzugten Remote. Nutze im Dialog Vom Remote laden, um sie bei Bedarf gezielt zu aktualisieren.",
        "Nutze Zum Remote senden, um lokale Notizen zu veröffentlichen. Ein normaler Branch-Push überträgt Git Notes nicht automatisch.",
      ],
      commands: [
        { command: "git notes show <commit>", description: "Notiz eines Commits in der Kommandozeile anzeigen" },
        { command: "git notes add <commit>", description: "Notiz zu einem Commit hinzufügen oder im Editor verfassen" },
        { command: "git fetch <remote> refs/notes/commits:refs/notes/commits", description: "Commit-Notizen gezielt vom Remote laden" },
        { command: "git push <remote> refs/notes/commits", description: "Lokale Commit-Notizen zum Remote senden" },
      ],
      note: "Git Notes liegen standardmäßig unter refs/notes/commits und werden getrennt von Branches synchronisiert. Prüfe vor einem Push, ob der Ziel-Remote diese Referenz akzeptiert.",
    },
    {
      id: "app-search",
      title: "Code-Ursprung mit Global Search finden",
      summary: "Die Code-Suche untersucht die Commit-Historie und findet, in welchem Commit eine Zeichenfolge oder Funktion eingeführt wurde. Die Dateisuche verbindet Pfadsuche mit Dateihistorie.",
      steps: [
        "Suche nach einem stabilen, möglichst eindeutigen Ausschnitt statt nach einer häufigen Einzelzeile.",
        "Aktiviere Groß-/Kleinschreibung nur, wenn sie die Treffermenge sinnvoll reduziert.",
        "Öffne einen Treffer als Diff, um die Einführung im Kontext des gesamten Commits zu prüfen.",
        "Bei Umbenennungen zeigt Gitty alten und neuen Pfad, soweit Git sie aus der Ähnlichkeit ableiten kann.",
      ],
    },
    {
      id: "app-operations",
      title: "Laufende Git-Operationen sicher beenden",
      summary: "Während Rebase oder Cherry-pick zeigt Gitty einen speziellen Status. Löse alle Konflikte und entscheide dann bewusst zwischen Continue und Abort.",
      steps: [
        "Resolve öffnet jede Konfliktdatei mit Current, Incoming und editierbarem Zielinhalt.",
        "Markiere erst nach inhaltlicher Prüfung als gelöst; „keine Konfliktmarker mehr“ bedeutet nicht automatisch „fachlich richtig“.",
        "Continue verarbeitet den nächsten Commit und kann weitere Konflikte erzeugen.",
        "Abort stellt den Zustand vor Beginn der gesamten Operation wieder her.",
      ],
    },
    {
      id: "app-credentials-settings",
      title: "Zugangsdaten, AI und Einstellungen",
      summary: "Gitty fragt Zugangsdaten erst bei einer authentifizierten Remote-Aktion ab. App-Theme, Sprache und anonyme Analytics liegen in den Einstellungen; AI-Anbieter werden separat konfiguriert.",
      steps: [
        "Verwende für HTTPS-Remotes ein persönliches Zugriffstoken statt des Account-Passworts.",
        "Begrenze Token-Rechte und Laufzeit auf das tatsächlich benötigte Minimum.",
        "AI-generierte Commit-Texte sind Vorschläge: Prüfe Inhalt, sensible Daten und tatsächlichen Staged-Diff.",
        "Gitty sendet über Analytics keine Pfade, Remotes, Branches, Commit-Texte, Dateinamen, Diffs, Zugangsdaten oder Code.",
      ],
    },
  );

  deCategories.find((category) => category.id === "basics")?.sections.push(
    {
      id: "basics-config",
      title: "Identität und Konfiguration",
      summary: "Git schreibt Name und E-Mail in jeden Commit. Globale Werte gelten für alle Repositories; lokale Werte überschreiben sie nur im aktuellen Projekt.",
      commands: [
        { command: "git config --global user.name \"Ada Lovelace\"", description: "Globalen Anzeigenamen setzen" },
        { command: "git config --global user.email \"ada@example.com\"", description: "Globale Commit-E-Mail setzen" },
        { command: "git config --list --show-origin", description: "Wirksame Einstellungen und Quelldateien anzeigen" },
        { command: "git config user.email \"work@example.com\"", description: "E-Mail nur für das aktuelle Repository setzen" },
      ],
    },
    {
      id: "basics-ignore",
      title: ".gitignore richtig verwenden",
      summary: ".gitignore verhindert, dass neue, noch ungetrackte Dateien vorgeschlagen werden. Bereits getrackte Dateien werden dadurch nicht automatisch entfernt.",
      commands: [
        { command: "git check-ignore -v <datei>", description: "Zeigen, welche Ignore-Regel auf eine Datei wirkt" },
        { command: "git rm --cached <datei>", description: "Datei nur aus Git entfernen, lokal aber behalten" },
        { command: "git status --ignored", description: "Auch ignorierte Dateien anzeigen" },
      ],
      note: "Committe niemals Secrets. .gitignore verhindert zukünftiges Tracking, entfernt aber keine Geheimnisse aus bereits vorhandenen Commits.",
    },
    {
      id: "basics-show",
      title: "Commits und Objekte untersuchen",
      summary: "Hashes identifizieren Git-Objekte. Meist reichen die ersten eindeutigen Zeichen; Referenzen wie HEAD~1 oder main sind lesbare Zeiger auf Commits.",
      commands: [
        { command: "git show <commit>", description: "Metadaten und Patch eines Commits anzeigen" },
        { command: "git show <commit>:<pfad>", description: "Dateiinhalt aus einem bestimmten Commit ausgeben" },
        { command: "git diff <von>..<bis>", description: "Zwei Zustände direkt vergleichen" },
        { command: "git log --follow -- <datei>", description: "Dateiverlauf über Umbenennungen hinweg verfolgen" },
      ],
    },
  );

  deCategories.find((category) => category.id === "app")?.sections.push(
    {
      id: "app-line-staging",
      title: "Änderungen zeilenweise stagen",
      summary: "Im Patch-Dialog kannst du nicht nur vollständige Dateien oder Hunks, sondern einzelne hinzugefügte und entfernte Zeilen für den nächsten Commit auswählen.",
      steps: [
        "Öffne im Bereich Unstaged den Patch einer geänderten Textdatei.",
        "Klicke auf die Auswahlfläche vor einer grünen oder roten Zeile. Mit Shift-Klick markierst du einen zusammenhängenden Bereich.",
        "Die Auswahlleiste zeigt die Anzahl der gewählten Zeilen. Stage selected übernimmt nur diese Auswahl in den Staging-Bereich.",
        "Öffne denselben Dialog unter Staged und nutze Unstage selected, wenn einzelne Zeilen wieder zurück ins Arbeitsverzeichnis sollen.",
        "Prüfe danach den Staged-Diff. Er ist die verbindliche Vorschau des nächsten Commits.",
      ],
      note: "Bei einer geänderten Zeile stellt Git intern meist eine entfernte und eine hinzugefügte Zeile dar. Wähle beide Seiten, wenn die vollständige Ersetzung in denselben Commit gehört.",
    },
    {
      id: "app-line-discard",
      title: "Hunks und einzelne Zeilen verwerfen",
      summary: "Discard selected verwirft nur die markierten Zeilen; Discard Hunk verwirft den vollständigen Abschnitt. Gitty zeigt vor dem Löschen eine Sicherheitsabfrage.",
      steps: [
        "Kontrolliere, ob der Dialog Staged changes oder Unstaged changes anzeigt.",
        "Markiere exakt die Zeilen, die nicht behalten werden sollen.",
        "Lies in der Sicherheitsabfrage Pfad und Umfang und bestätige erst danach.",
        "Aktualisiere den Patch, falls Dateien während des geöffneten Dialogs extern verändert wurden.",
      ],
      note: "Vorsicht: Verwerfen kann nicht zuverlässig rückgängig gemacht werden. Erstelle bei wichtigen Zwischenständen zuerst einen Commit oder Stash.",
    },
    {
      id: "app-package-updates",
      title: "Gitty unter Arch Linux installieren und aktualisieren",
      summary: "Das AUR-Paket gitty-desktop lädt das öffentliche Gitea-Release und baut Gitty lokal aus dem Quellcode.",
      commands: [
        { command: "yay -S gitty-desktop", description: "Gitty mit yay aus dem AUR bauen, installieren oder aktualisieren" },
        { command: "paru -S gitty-desktop", description: "Gitty alternativ mit paru bauen, installieren oder aktualisieren" },
        { command: "git clone https://aur.archlinux.org/gitty-desktop.git && cd gitty-desktop && makepkg -si", description: "AUR-Paket ohne AUR-Helfer prüfen und manuell bauen" },
        { command: "pacman -Qi gitty-desktop", description: "Installierte Version und Paketinformationen anzeigen" },
      ],
      steps: [
        "Das PKGBUILD lädt das öffentliche Quellarchiv des jeweiligen Gitea-Tags herunter.",
        "Die Pipeline aktualisiert Version, Prüfsumme und .SRCINFO im AUR-Paket gitty-desktop.",
        "AUR-Helfer erkennen neue Versionen und erzeugen daraus lokal ein normales Pacman-Paket.",
        "Ohne AUR-Helfer kannst du das AUR-Git-Repository klonen, die Dateien prüfen und makepkg -si ausführen.",
      ],
      note: "AUR-Pakete werden von Nutzern gepflegt. Prüfe PKGBUILD und .SRCINFO vor der Installation, besonders nach größeren Änderungen.",
    },
  );

  deCategories.find((category) => category.id === "branches")?.sections.push(
    {
      id: "branches-worktrees-concept",
      title: "Was ein Worktree ist",
      summary: "Ein Repository besitzt eine gemeinsame Git-Datenbank, kann aber mehrere Arbeitsordner haben. Jeder Worktree hat eigene ausgecheckte Dateien und normalerweise einen eigenen Branch; Commits und Referenzen sind sofort in allen Worktrees sichtbar.",
      steps: [
        "Das Haupt-Worktree ist der Ordner, den du ursprünglich als Repository geöffnet hast.",
        "Ein zusätzlicher Worktree ist kein vollständiger Clone: Git-Objekte und Historie werden gemeinsam genutzt, nur Arbeitsordner und Index sind getrennt.",
        "Änderungen in Dateien bleiben im jeweiligen Worktree. Ein dort erstellter Commit ist dagegen sofort Teil desselben Repositorys.",
        "Worktrees eignen sich besonders für parallele Features, dringende Hotfixes, Reviews oder längere Builds auf einem zweiten Branch.",
        "Für vollständig unabhängige Remotes, Konfigurationen oder Experimente ist ein eigener Clone weiterhin die passendere Wahl.",
      ],
      note: "Ein Branch kann normalerweise nicht gleichzeitig in zwei Worktrees ausgecheckt werden. Das verhindert, dass zwei Ordner denselben Branch widersprüchlich verändern.",
    },
    {
      id: "branches-worktrees",
      title: "Mehrere Branches mit Worktrees parallel öffnen",
      summary: "Ein Worktree checkt einen weiteren Branch in einen eigenen Ordner aus. So kannst du an mehreren Aufgaben arbeiten, ohne Branches im Hauptordner umzuschalten oder lokale Änderungen zu stashen.",
      steps: [
        "Öffne im Branch-Panel unter Tags den Reiter Worktrees.",
        "Wähle Existing branch für einen vorhandenen lokalen Branch, New branch für einen neuen Branch oder Detached für einen festen Commit ohne Branch.",
        "Lege einen leeren Zielordner fest und erstelle den Worktree. Open tab öffnet ihn anschließend als eigenen Repository-Tab.",
        "Move verschiebt einen nicht aktiven Worktree. Lock schützt ihn vor versehentlichem Prune oder Entfernen, etwa auf einem externen Laufwerk.",
        "Remove löscht den Worktree-Ordner und seine Registrierung, behält aber den Branch. Prune entfernt nur veraltete Registrierungen nicht mehr vorhandener Ordner.",
      ],
      commands: [
        { command: "git worktree list", description: "Alle registrierten Worktrees anzeigen" },
        { command: "git worktree add ../projekt-fix fix/login", description: "Vorhandenen Branch in neuem Ordner auschecken" },
        { command: "git worktree add -b feature/name ../projekt-feature main", description: "Neuen Branch und Worktree von main erstellen" },
        { command: "git worktree prune --dry-run", description: "Veraltete Registrierungen vor dem Aufräumen prüfen" },
      ],
      note: "Ein Branch kann normalerweise nur in einem Worktree gleichzeitig ausgecheckt sein. Ein Worktree mit lokalen Änderungen sollte vor Remove committed, gestasht oder bewusst erzwungen entfernt werden.",
    },
  );

  deCategories.find((category) => category.id === "branches")?.sections.push(
    {
      id: "branches-strategy",
      title: "Eine einfache Branch-Strategie",
      summary: "Kurze, fokussierte Branches reduzieren Konflikte. Aktualisiere sie regelmäßig und integriere sie nach Review möglichst schnell.",
      steps: [
        "Starte vom aktuellen main und gib dem Branch einen beschreibenden Namen wie feature/help-search.",
        "Committe kleine, nachvollziehbare Einheiten und pushe den Branch als Sicherung und für Review.",
        "Synchronisiere vor Abschluss mit dem aktuellen Ziel-Branch und löse Konflikte im eigenen Branch.",
        "Merge nach bestandenem Review und lösche den kurzlebigen Branch lokal sowie remote.",
      ],
    },
    {
      id: "branches-cherry-pick",
      title: "Cherry-pick gezielt einsetzen",
      summary: "Cherry-pick kopiert die Änderung eines vorhandenen Commits als neuen Commit auf den aktuellen Branch. Das ist praktisch für einzelne Fixes, ersetzt aber keine normale Branch-Integration.",
      commands: [
        { command: "git cherry-pick <commit>", description: "Einen Commit auf den aktuellen Branch kopieren" },
        { command: "git cherry-pick --no-commit <commit>", description: "Änderung übernehmen, aber vor dem Commit weiter bearbeiten" },
        { command: "git cherry-pick --continue", description: "Nach Konfliktlösung fortsetzen" },
        { command: "git cherry-pick --abort", description: "Gesamten Cherry-pick abbrechen" },
      ],
    },
    {
      id: "branches-interactive-rebase",
      title: "Interactive Rebase",
      summary: "Vor dem Veröffentlichen kannst du lokale Commits neu ordnen, umbenennen, zusammenfassen oder entfernen. Gitty bietet dafür einen visuellen Rebase-Plan.",
      steps: [
        "Pick behält einen Commit, Reword ändert seine Nachricht, Squash/Fixup kombiniert ihn mit dem vorherigen Commit, Drop entfernt ihn.",
        "Ordne Abhängigkeiten so, dass jeder Zwischenschritt möglichst baubar und verständlich bleibt.",
        "Prüfe nach dem Rebase Tests, Commit-Reihenfolge und finalen Diff gegen den Ziel-Branch.",
      ],
      note: "Interactive Rebase erzeugt neue Commit-Hashes. Verwende ihn bevorzugt für deine eigenen, noch nicht gemeinsam genutzten Commits.",
    },
    {
      id: "branches-tags",
      title: "Releases mit Tags markieren",
      summary: "Ein annotierter Tag speichert zusätzlich Autor, Datum und Nachricht und eignet sich deshalb besser für Releases als ein einfacher Lightweight-Tag.",
      commands: [
        { command: "git tag -a v1.2.0 -m \"Release 1.2.0\"", description: "Annotierten Release-Tag erstellen" },
        { command: "git show v1.2.0", description: "Tag und zugehörigen Commit prüfen" },
        { command: "git push origin v1.2.0", description: "Einen bestimmten Tag veröffentlichen" },
        { command: "git push origin --tags", description: "Alle noch fehlenden lokalen Tags veröffentlichen" },
      ],
    },
  );

  deCategories.find((category) => category.id === "remote")?.sections.push(
    {
      id: "remote-tracking",
      title: "Tracking-Branches und Upstream",
      summary: "Der Upstream verbindet einen lokalen Branch mit seiner Remote-Referenz. Dadurch wissen Pull, Push und Ahead/Behind, welche beiden Linien verglichen werden.",
      commands: [
        { command: "git branch --show-current", description: "Aktuellen lokalen Branch anzeigen" },
        { command: "git branch -u origin/<branch>", description: "Upstream für den aktuellen Branch setzen" },
        { command: "git branch -vv", description: "Upstream und Ahead/Behind aller lokalen Branches anzeigen" },
        { command: "git push -u origin HEAD", description: "Aktuellen Branch veröffentlichen und Upstream setzen" },
      ],
    },
    {
      id: "remote-safe-pull",
      title: "Sicher synchronisieren",
      summary: "Fetch ist immer der kontrollierteste erste Schritt. Danach kannst du den Unterschied prüfen und bewusst Merge oder Rebase wählen.",
      commands: [
        { command: "git fetch origin", description: "Remote-Informationen laden, ohne den lokalen Branch zu ändern" },
        { command: "git log --oneline HEAD..@{upstream}", description: "Commits anzeigen, die lokal noch fehlen" },
        { command: "git log --oneline @{upstream}..HEAD", description: "Noch nicht veröffentlichte lokale Commits anzeigen" },
        { command: "git diff HEAD...@{upstream}", description: "Änderungen seit dem gemeinsamen Ausgangspunkt vergleichen" },
      ],
    },
    {
      id: "remote-force",
      title: "Force Push verstehen",
      summary: "Nach einem Rebase stimmt die lokale Historie nicht mehr mit dem Remote überein. --force-with-lease überschreibt nur, wenn niemand den Remote-Branch seit deinem letzten Fetch verändert hat.",
      commands: [
        { command: "git push --force-with-lease", description: "Rebaseten Branch mit Schutz vor fremden neuen Commits aktualisieren" },
      ],
      note: "Verwende niemals blind --force auf gemeinsam genutzten Branches. Bevorzuge --force-with-lease und stimme das Umschreiben der Historie im Team ab.",
    },
  );

  deCategories.find((category) => category.id === "troubleshooting")?.sections.push(
    {
      id: "trouble-undo-map",
      title: "Restore, Reset und Revert unterscheiden",
      summary: "Die drei Befehle lösen verschiedene Probleme: Restore betrifft Dateien, Reset verschiebt Branch/Index, Revert macht veröffentlichte Änderungen durch einen neuen Commit rückgängig.",
      commands: [
        { command: "git restore <datei>", description: "Nicht gestagte Dateiänderungen verwerfen" },
        { command: "git restore --staged <datei>", description: "Staging rückgängig machen, Dateiänderung behalten" },
        { command: "git reset --soft HEAD~1", description: "Letzten lokalen Commit entfernen, alles gestaged behalten" },
        { command: "git revert <commit>", description: "Veröffentlichten Commit sicher durch Gegen-Commit umkehren" },
      ],
    },
    {
      id: "trouble-errors",
      title: "Typische Fehlermeldungen",
      summary: "Git-Fehler beschreiben meist den blockierenden Zustand. Prüfe zuerst status, Branch, Upstream und laufende Operationen, bevor du Befehle wiederholst.",
      steps: [
        "non-fast-forward: Im Remote existieren Commits, die lokal fehlen. Fetch, vergleichen und integrieren.",
        "detached HEAD: Du bist direkt auf einem Commit. Erstelle einen Branch, wenn du neue Arbeit behalten willst.",
        "pathspec did not match: Pfad oder Branch-Name ist falsch oder lokal noch nicht vorhanden. Prüfe Schreibweise und Fetch-Stand.",
        "local changes would be overwritten: Committe, stash oder verwerfe die genannten Änderungen vor Checkout/Pull.",
        "not a git repository: Aktueller Ordner liegt außerhalb eines Repositorys oder .git fehlt.",
      ],
    },
    {
      id: "trouble-diagnose",
      title: "Diagnose ohne weitere Schäden",
      summary: "Bevor du Reset, Clean oder Force verwendest, sichere den aktuellen Zustand und sammle lesende Informationen.",
      commands: [
        { command: "git status", description: "Aktuellen Zustand und Handlungsanweisungen anzeigen" },
        { command: "git diff && git diff --staged", description: "Ungesicherte und gestagte Änderungen vollständig prüfen" },
        { command: "git branch backup/before-recovery", description: "Aktuellen Commit mit einem Sicherungs-Branch verankern" },
        { command: "git stash push -u -m \"backup before recovery\"", description: "Auch ungetrackte lokale Arbeit vorübergehend sichern" },
      ],
      note: "git clean -fd und git reset --hard können nicht getrackte beziehungsweise lokale Daten endgültig löschen. Nutze zuerst Vorschau, Backup-Branch oder Stash.",
    },
  );

  deCategories.push(
    {
      id: "workflows",
      label: "Praxis-Workflows",
      description: "Bewährte Rezepte für typische Aufgaben vom Feature bis zum Hotfix.",
      sections: [
        {
          id: "workflow-feature",
          title: "Feature-Branch von Anfang bis Ende",
          summary: "Dieser Ablauf hält den Branch aktuell, den Commit-Verlauf verständlich und die Integration überschaubar.",
          commands: [
            { command: "git switch main && git pull --ff-only", description: "Aktuellen, unveränderten Ausgangspunkt herstellen" },
            { command: "git switch -c feature/<name>", description: "Neuen Feature-Branch erstellen" },
            { command: "git push -u origin HEAD", description: "Branch veröffentlichen und Upstream setzen" },
            { command: "git fetch origin && git rebase origin/main", description: "Vor Review auf aktuellen main setzen" },
          ],
          steps: [
            "Arbeite in kleinen Commits und prüfe vor jedem Commit den Staged-Diff.",
            "Pushe regelmäßig als Sicherung und für Zusammenarbeit.",
            "Führe Tests nach der letzten Synchronisierung aus.",
            "Erstelle Review/PR, integriere nach Freigabe und lösche den Branch.",
          ],
        },
        {
          id: "workflow-hotfix",
          title: "Einzelnen Fix übernehmen",
          summary: "Wenn ein bereits vorhandener Fix gezielt in einen Release-Branch muss, ist Cherry-pick oft präziser als ein vollständiger Merge.",
          commands: [
            { command: "git switch release/<version>", description: "Ziel-Branch wechseln" },
            { command: "git pull --ff-only", description: "Sicherstellen, dass der Ziel-Branch aktuell ist" },
            { command: "git cherry-pick -x <fix-commit>", description: "Fix übernehmen und Herkunft in der Nachricht dokumentieren" },
          ],
          note: "Prüfe, ob der Fix von früheren Commits abhängt. Ein technisch erfolgreicher Cherry-pick kann fachlich unvollständig sein.",
        },
        {
          id: "workflow-clean-commit",
          title: "Gemischte Änderungen in saubere Commits teilen",
          summary: "Du musst nicht alles committen, was gerade geändert ist. Staging nach Hunk oder Zeile trennt Refactoring, Fix und Dokumentation.",
          commands: [
            { command: "git add -p", description: "Änderungen abschnittsweise auswählen" },
            { command: "git diff --staged", description: "Ersten Commit-Inhalt prüfen" },
            { command: "git commit", description: "Ersten logischen Commit erstellen" },
            { command: "git add -p && git commit", description: "Mit dem nächsten Themenblock fortfahren" },
          ],
        },
      ],
    },
    {
      id: "reference",
      label: "Referenz & Glossar",
      description: "Kompakte Befehlsübersicht und zentrale Git-Begriffe zum Nachschlagen.",
      sections: [
        {
          id: "reference-daily",
          title: "Tägliche Kurzreferenz",
          summary: "Die häufigsten sicheren Befehle für Orientierung, Änderung, Commit und Synchronisierung.",
          commands: [
            { command: "git status", description: "Zustand prüfen" },
            { command: "git diff", description: "Lokale Änderungen lesen" },
            { command: "git add -p", description: "Gezielt stagen" },
            { command: "git diff --staged", description: "Commit-Inhalt prüfen" },
            { command: "git commit", description: "Commit erstellen" },
            { command: "git fetch --prune", description: "Remote-Stand aktualisieren" },
            { command: "git push", description: "Lokale Commits veröffentlichen" },
          ],
        },
        {
          id: "reference-glossary",
          title: "Git-Glossar",
          summary: "HEAD ist der aktuelle Checkout. Branches und Tags sind Referenzen auf Commits. origin ist nur der übliche Name eines Remotes. Upstream ist die zugeordnete Remote-Referenz eines lokalen Branches.",
          steps: [
            "Commit: Unveränderlicher Projekt-Schnappschuss mit Eltern, Autor, Zeit und Nachricht.",
            "Index/Staging: Vorbereiteter Schnappschuss für den nächsten Commit.",
            "Working tree: Ausgecheckte Dateien, die du gerade bearbeitest.",
            "Remote: Benannte Verbindung zu einem anderen Repository, nicht automatisch „die Cloud“.",
            "Fast-forward: Branch-Zeiger kann ohne Merge-Commit direkt nach vorn bewegt werden.",
            "Detached HEAD: HEAD zeigt direkt auf einen Commit statt auf einen lokalen Branch.",
          ],
        },
        {
          id: "reference-safety",
          title: "Gefahrenstufen von Git-Befehlen",
          summary: "Lesende Befehle wie status, log, show und diff sind unkritisch. Restore, Reset, Clean, Rebase und Force Push verändern oder löschen Zustand und verdienen eine zusätzliche Prüfung.",
          steps: [
            "Sicher lesend: status, log, show, diff, branch, remote -v, reflog.",
            "Lokal verändernd: add, restore, commit, stash, switch, merge, rebase.",
            "Potenziell datenlöschend: reset --hard, clean -fd, branch -D.",
            "Teamweit riskant: push --force, veröffentlichte Commits rebasen oder Tags verschieben.",
          ],
          note: "Wenn du unsicher bist: Stoppe, erstelle einen Backup-Branch und prüfe git status sowie git reflog. Git belohnt kleine, nachvollziehbare Schritte.",
        },
      ],
    },
  );

  enCategories.find((category) => category.id === "start")?.sections.push(
    {
      id: "start-model",
      title: "Understand Git's core model",
      summary: "Git stores linked snapshots of your project rather than a running list of individual file edits. HEAD points to the current commit; the branch name moves forward when you commit.",
      steps: [
        "Working tree: The real files you edit. Changes are not part of a commit yet.",
        "Staging area: The exact snapshot you are preparing for the next commit.",
        "Local repository: Commits, branches, and tags initially exist only on your machine.",
        "Remote repository: Push publishes commits, Fetch downloads references, and Pull downloads and integrates.",
      ],
      note: "The staging area is not another folder. It is a Git snapshot that Gitty presents as “Staged”.",
    },
    {
      id: "start-before-work",
      title: "Checklist before every task",
      summary: "A short state check prevents most accidental commits and complicated conflicts.",
      commands: [
        { command: "git status --short --branch", description: "Check branch, upstream, and changes concisely" },
        { command: "git fetch --prune", description: "Refresh remote state without changing files" },
        { command: "git log --oneline --decorate -10", description: "Review the latest ten commits and references" },
      ],
      steps: [
        "Confirm that you are on the correct branch.",
        "Commit or stash unfinished changes before switching branches.",
        "Fetch remote information, then choose deliberately between Pull, Rebase, and Merge.",
      ],
    },
  );

  enCategories.find((category) => category.id === "app")?.sections.push(
    {
      id: "app-repositories",
      title: "Manage repositories and tabs",
      summary: "Repository Management groups open, recent, and favorite projects. Every open repository gets a tab with its branch and change status.",
      steps: [
        "Browse opens an existing local repository; Clone downloads a remote repository into a new folder.",
        "Favorite frequently used projects so they remain visible independently of recent history.",
        "Switch between projects with tabs. Gitty keeps useful repository state available during the session.",
        "Use the tab context menu to remove a repository from the workspace without deleting its files.",
      ],
    },
    {
      id: "app-commit-detail",
      title: "Create clean commits in Gitty",
      summary: "A good commit contains one logical change and can be explained, reviewed, and reverted independently.",
      steps: [
        "Review Unstaged and the complete diff of every affected file.",
        "Stage only related files, hunks, or lines. Tests and implementation may belong together; unrelated formatting usually does not.",
        "Review the Staged diff by itself—this is exactly what will be committed.",
        "Write a short imperative subject, for example “Handle expired credentials”.",
        "Use Amend only while nobody else depends on the latest commit.",
      ],
      note: "If the Staged diff contains a surprise, unstage it. A commit is the wrong place for “it will probably be fine”.",
    },
    {
      id: "app-branches-tags",
      title: "Branches and tags in the app",
      summary: "The Branch panel shows local and remote branches plus Ahead/Behind. Its context menu supports switch, create, rename, delete, merge, and rebase actions.",
      steps: [
        "Create a branch from the current HEAD or from a specific commit in History.",
        "Checkout/Switch updates the working tree and HEAD. Save incompatible local changes first.",
        "Tags mark fixed commits, usually releases. A tag does not move forward automatically.",
        "Remote branches are references. Switching creates a local tracking branch when needed.",
      ],
    },
    {
      id: "app-history-tools",
      title: "History, file history, Blame, and Restore",
      summary: "Gitty connects the commit graph, file history, and restoration tools so you can understand how and why a change evolved.",
      steps: [
        "Select a file in Explorer to view its history separately from the project history.",
        "Blame links every current line to its latest modifying commit. Use it as a starting point, not as an accusation.",
        "Compare shows the difference between any two commits or branch tips.",
        "Restore from commit writes an older file version into the working tree. Review and commit the result normally.",
      ],
    },
    {
      id: "app-git-notes",
      title: "Add context to commits with Git Notes",
      summary: "Git Notes attach additional information to a commit without changing its hash or rewriting history. They are useful for review findings, ticket context, build IDs, or approval status.",
      steps: [
        "Open the commit note from the note icon or the commit context menu in History.",
        "Write or edit the note and save it. Commits with a note are marked in History, and hovering over the marker shows a preview.",
        "Deleting removes only the note. The associated commit and its files remain unchanged.",
        "Gitty fetches Git Notes from the preferred remote in the background. Use Fetch from remote in the dialog to refresh them explicitly when needed.",
        "Use Push to remote to publish local notes. A regular branch push does not transfer Git Notes automatically.",
      ],
      commands: [
        { command: "git notes show <commit>", description: "Show a commit's note on the command line" },
        { command: "git notes add <commit>", description: "Add a note to a commit or compose it in an editor" },
        { command: "git fetch <remote> refs/notes/commits:refs/notes/commits", description: "Fetch commit notes explicitly from a remote" },
        { command: "git push <remote> refs/notes/commits", description: "Push local commit notes to a remote" },
      ],
      note: "Git Notes are stored under refs/notes/commits by default and synchronize separately from branches. Before pushing, make sure the destination remote accepts this reference.",
    },
    {
      id: "app-search",
      title: "Find code origins with Global Search",
      summary: "Code Search examines commit history to find where text or a function was introduced. File Search combines path search with file history.",
      steps: [
        "Search for a stable, distinctive excerpt rather than a common single line.",
        "Enable case sensitivity only when it meaningfully reduces results.",
        "Open a result as a diff to review the introduction in the full commit context.",
        "For renames, Gitty shows old and new paths when Git can infer the similarity.",
      ],
    },
    {
      id: "app-operations",
      title: "Finish active Git operations safely",
      summary: "During Rebase or Cherry-pick, Gitty displays a dedicated state. Resolve every conflict, then choose deliberately between Continue and Abort.",
      steps: [
        "Resolve opens each conflict with Current, Incoming, and editable result content.",
        "Mark a file resolved only after reviewing its meaning; removing conflict markers is not enough.",
        "Continue processes the next commit and may reveal additional conflicts.",
        "Abort restores the state from before the entire operation began.",
      ],
    },
    {
      id: "app-credentials-settings",
      title: "Credentials, AI, and settings",
      summary: "Gitty requests credentials only for authenticated remote actions. Theme, language, and anonymous analytics live in Settings; AI providers are configured separately.",
      steps: [
        "Use a personal access token instead of the account password for HTTPS remotes.",
        "Limit token permissions and lifetime to the minimum required.",
        "AI-generated commit messages are suggestions: verify content, sensitive data, and the actual Staged diff.",
        "Analytics never sends repository paths, remotes, branches, commit messages, file names, diffs, credentials, or code.",
      ],
    },
  );

  enCategories.find((category) => category.id === "basics")?.sections.push(
    {
      id: "basics-config",
      title: "Identity and configuration",
      summary: "Git writes your name and email into every commit. Global values apply to all repositories; local values override them only in the current project.",
      commands: [
        { command: "git config --global user.name \"Ada Lovelace\"", description: "Set the global display name" },
        { command: "git config --global user.email \"ada@example.com\"", description: "Set the global commit email" },
        { command: "git config --list --show-origin", description: "Show effective settings and their source files" },
        { command: "git config user.email \"work@example.com\"", description: "Set email only for the current repository" },
      ],
    },
    {
      id: "basics-ignore",
      title: "Use .gitignore correctly",
      summary: ".gitignore prevents new untracked files from being suggested. It does not automatically remove files that Git already tracks.",
      commands: [
        { command: "git check-ignore -v <file>", description: "Show which ignore rule applies to a file" },
        { command: "git rm --cached <file>", description: "Remove a file from Git while keeping it locally" },
        { command: "git status --ignored", description: "Include ignored files in status output" },
      ],
      note: "Never commit secrets. .gitignore prevents future tracking but does not remove secrets from existing commits.",
    },
    {
      id: "basics-show",
      title: "Inspect commits and objects",
      summary: "Hashes identify Git objects. The first unique characters are usually enough; references such as HEAD~1 or main are readable pointers to commits.",
      commands: [
        { command: "git show <commit>", description: "Show a commit's metadata and patch" },
        { command: "git show <commit>:<path>", description: "Print a file from a specific commit" },
        { command: "git diff <from>..<to>", description: "Compare two states directly" },
        { command: "git log --follow -- <file>", description: "Follow file history across renames" },
      ],
    },
  );

  enCategories.find((category) => category.id === "app")?.sections.push(
    {
      id: "app-line-staging",
      title: "Stage changes line by line",
      summary: "The patch dialog lets you prepare individual added and deleted lines instead of staging an entire file or hunk.",
      steps: [
        "Open the patch of a changed text file in Unstaged.",
        "Click the selector in front of a green or red line. Shift-click selects a continuous range.",
        "The selection bar shows the number of selected lines. Stage selected moves only that selection into Staged.",
        "Open the same dialog under Staged and use Unstage selected to return individual lines to the working tree.",
        "Review the Staged diff afterward. It is the authoritative preview of the next commit.",
      ],
      note: "Git usually represents one edited line as one deletion plus one addition. Select both sides when the complete replacement belongs in the same commit.",
    },
    {
      id: "app-line-discard",
      title: "Discard hunks or individual lines",
      summary: "Discard selected removes only selected lines; Discard Hunk removes the complete section. Gitty shows a confirmation before deleting changes.",
      steps: [
        "Check whether the dialog currently shows Staged changes or Unstaged changes.",
        "Select exactly the lines that should not be kept.",
        "Review the file path and scope in the confirmation, then confirm.",
        "Refresh the patch if another application changed the file while the dialog was open.",
      ],
      note: "Caution: discarded work cannot always be recovered. Commit or stash important intermediate work first.",
    },
    {
      id: "app-package-updates",
      title: "Install and update Gitty on Arch Linux",
      summary: "The gitty-desktop AUR package downloads the public Gitea release and builds Gitty locally from source.",
      commands: [
        { command: "yay -S gitty-desktop", description: "Build, install, or update Gitty from the AUR with yay" },
        { command: "paru -S gitty-desktop", description: "Alternatively build, install, or update Gitty with paru" },
        { command: "git clone https://aur.archlinux.org/gitty-desktop.git && cd gitty-desktop && makepkg -si", description: "Inspect and build the AUR package without an AUR helper" },
        { command: "pacman -Qi gitty-desktop", description: "Show the installed version and package information" },
      ],
      steps: [
        "PKGBUILD downloads the public source archive for the corresponding Gitea tag.",
        "The pipeline updates the version, checksum, and .SRCINFO in the gitty-desktop AUR package.",
        "AUR helpers detect new versions and turn the recipe into a regular Pacman package locally.",
        "Without an AUR helper, clone the AUR Git repository, inspect its files, and run makepkg -si.",
      ],
      note: "AUR packages are user-maintained. Review PKGBUILD and .SRCINFO before installing, especially after major changes.",
    },
  );

  enCategories.find((category) => category.id === "branches")?.sections.push(
    {
      id: "branches-worktrees-concept",
      title: "What a worktree is",
      summary: "One repository has a shared Git database but can have multiple working folders. Each worktree has its own checked-out files and usually its own branch; commits and references are immediately visible from every worktree.",
      steps: [
        "The main worktree is the folder you originally opened as the repository.",
        "An additional worktree is not a full clone: Git objects and history are shared while the working folder and index remain separate.",
        "File changes stay inside their worktree. A commit created there immediately becomes part of the shared repository.",
        "Worktrees are useful for parallel features, urgent hotfixes, reviews, or long-running builds on another branch.",
        "Use a separate clone when you need independent remotes, configuration, or isolated experiments.",
      ],
      note: "A branch normally cannot be checked out in two worktrees at once. This prevents two folders from changing the same branch inconsistently.",
    },
    {
      id: "branches-worktrees",
      title: "Open multiple branches in parallel with worktrees",
      summary: "A worktree checks out another branch into a separate folder. This lets you work on multiple tasks without switching the main folder or stashing local changes.",
      steps: [
        "Open Worktrees below Tags in the Branch panel.",
        "Choose Existing branch for a local branch, New branch to create one, or Detached for a fixed commit without a branch.",
        "Choose an empty destination folder and create the worktree. Open tab then opens it as a separate repository tab.",
        "Move relocates an inactive worktree. Lock protects it from accidental pruning or removal, for example while stored on an external drive.",
        "Remove deletes the worktree folder and registration but keeps its branch. Prune removes only stale registrations for missing folders.",
      ],
      commands: [
        { command: "git worktree list", description: "Show all registered worktrees" },
        { command: "git worktree add ../project-fix fix/login", description: "Check out an existing branch in a new folder" },
        { command: "git worktree add -b feature/name ../project-feature main", description: "Create a branch and worktree from main" },
        { command: "git worktree prune --dry-run", description: "Preview stale registrations before pruning" },
      ],
      note: "A branch can normally be checked out in only one worktree at a time. Commit, stash, or deliberately force removal of local changes before removing a worktree.",
    },
  );

  enCategories.find((category) => category.id === "branches")?.sections.push(
    {
      id: "branches-strategy",
      title: "A simple branch strategy",
      summary: "Short, focused branches reduce conflicts. Update them regularly and integrate them soon after review.",
      steps: [
        "Start from an up-to-date main and use a descriptive name such as feature/help-search.",
        "Commit small understandable units and push the branch for backup and review.",
        "Synchronize with the target branch before completion and resolve conflicts on your branch.",
        "Merge after review and delete the short-lived branch locally and remotely.",
      ],
    },
    {
      id: "branches-cherry-pick",
      title: "Use Cherry-pick deliberately",
      summary: "Cherry-pick copies the change from an existing commit as a new commit on the current branch. It is useful for individual fixes but not a replacement for normal branch integration.",
      commands: [
        { command: "git cherry-pick <commit>", description: "Copy one commit onto the current branch" },
        { command: "git cherry-pick --no-commit <commit>", description: "Apply the change but edit it before committing" },
        { command: "git cherry-pick --continue", description: "Continue after conflict resolution" },
        { command: "git cherry-pick --abort", description: "Abort the complete cherry-pick" },
      ],
    },
    {
      id: "branches-interactive-rebase",
      title: "Interactive Rebase",
      summary: "Before publishing, you can reorder, rename, combine, or remove local commits. Gitty provides a visual rebase plan.",
      steps: [
        "Pick keeps a commit, Reword changes its message, Squash/Fixup combines it with the previous commit, and Drop removes it.",
        "Order dependencies so each intermediate step remains as understandable and buildable as possible.",
        "After rebasing, run tests and review commit order and the final diff against the target branch.",
      ],
      note: "Interactive Rebase creates new commit hashes. Prefer it for your own commits that are not yet shared.",
    },
    {
      id: "branches-tags",
      title: "Mark releases with tags",
      summary: "An annotated tag also stores author, date, and message, making it better for releases than a lightweight tag.",
      commands: [
        { command: "git tag -a v1.2.0 -m \"Release 1.2.0\"", description: "Create an annotated release tag" },
        { command: "git show v1.2.0", description: "Inspect the tag and referenced commit" },
        { command: "git push origin v1.2.0", description: "Publish a specific tag" },
        { command: "git push origin --tags", description: "Publish all missing local tags" },
      ],
    },
  );

  enCategories.find((category) => category.id === "remote")?.sections.push(
    {
      id: "remote-tracking",
      title: "Tracking branches and upstream",
      summary: "An upstream connects a local branch with its remote reference. This tells Pull, Push, and Ahead/Behind which two histories to compare.",
      commands: [
        { command: "git branch --show-current", description: "Show the current local branch" },
        { command: "git branch -u origin/<branch>", description: "Set the upstream of the current branch" },
        { command: "git branch -vv", description: "Show upstream and Ahead/Behind for local branches" },
        { command: "git push -u origin HEAD", description: "Publish the current branch and set its upstream" },
      ],
    },
    {
      id: "remote-safe-pull",
      title: "Synchronize safely",
      summary: "Fetch is the most controlled first step. You can then inspect the difference and deliberately choose Merge or Rebase.",
      commands: [
        { command: "git fetch origin", description: "Download remote information without changing the local branch" },
        { command: "git log --oneline HEAD..@{upstream}", description: "Show commits that are still missing locally" },
        { command: "git log --oneline @{upstream}..HEAD", description: "Show unpublished local commits" },
        { command: "git diff HEAD...@{upstream}", description: "Compare changes since the common ancestor" },
      ],
    },
    {
      id: "remote-force",
      title: "Understand Force Push",
      summary: "After rebasing, local history no longer matches the remote. --force-with-lease overwrites only if nobody changed the remote branch since your latest Fetch.",
      commands: [
        { command: "git push --force-with-lease", description: "Update a rebased branch while protecting others' new commits" },
      ],
      note: "Never use --force blindly on shared branches. Prefer --force-with-lease and coordinate history rewrites with the team.",
    },
  );

  enCategories.find((category) => category.id === "troubleshooting")?.sections.push(
    {
      id: "trouble-undo-map",
      title: "Distinguish Restore, Reset, and Revert",
      summary: "The commands solve different problems: Restore changes files, Reset moves a branch or the index, and Revert undoes published changes through a new commit.",
      commands: [
        { command: "git restore <file>", description: "Discard unstaged file changes" },
        { command: "git restore --staged <file>", description: "Undo staging while keeping the file change" },
        { command: "git reset --soft HEAD~1", description: "Remove the latest local commit and keep everything staged" },
        { command: "git revert <commit>", description: "Safely undo a published commit with an inverse commit" },
      ],
    },
    {
      id: "trouble-errors",
      title: "Common error messages",
      summary: "Git errors usually describe the blocking state. Check status, branch, upstream, and active operations before repeating commands.",
      steps: [
        "non-fast-forward: The remote contains commits missing locally. Fetch, compare, and integrate them.",
        "detached HEAD: You are directly on a commit. Create a branch if you want to keep new work.",
        "pathspec did not match: The path or branch name is wrong or not available locally. Check spelling and Fetch state.",
        "local changes would be overwritten: Commit, stash, or discard the listed changes before Checkout or Pull.",
        "not a git repository: The current folder is outside a repository or its .git data is missing.",
      ],
    },
    {
      id: "trouble-diagnose",
      title: "Diagnose without causing more damage",
      summary: "Before using Reset, Clean, or Force, preserve the current state and collect read-only information.",
      commands: [
        { command: "git status", description: "Show the current state and Git's suggested next steps" },
        { command: "git diff && git diff --staged", description: "Review unstaged and staged changes completely" },
        { command: "git branch backup/before-recovery", description: "Anchor the current commit with a backup branch" },
        { command: "git stash push -u -m \"backup before recovery\"", description: "Temporarily protect tracked and untracked work" },
      ],
      note: "git clean -fd and git reset --hard can permanently remove untracked or local data. Prefer a preview, backup branch, or stash first.",
    },
  );

  enCategories.push(
    {
      id: "workflows",
      label: "Practical workflows",
      description: "Reliable recipes for common tasks from feature work to hotfixes.",
      sections: [
        {
          id: "workflow-feature",
          title: "Feature branch from start to finish",
          summary: "This workflow keeps your branch current, the commit history understandable, and integration manageable.",
          commands: [
            { command: "git switch main && git pull --ff-only", description: "Establish a current, unchanged starting point" },
            { command: "git switch -c feature/<name>", description: "Create a new feature branch" },
            { command: "git push -u origin HEAD", description: "Publish the branch and set its upstream" },
            { command: "git fetch origin && git rebase origin/main", description: "Move onto the latest main before review" },
          ],
          steps: [
            "Work in small commits and review the Staged diff before every commit.",
            "Push regularly for backup and collaboration.",
            "Run tests after the final synchronization.",
            "Open a review or PR, integrate after approval, and delete the branch.",
          ],
        },
        {
          id: "workflow-hotfix",
          title: "Apply one isolated fix",
          summary: "When an existing fix must be applied to a release branch, Cherry-pick is often more precise than merging a complete branch.",
          commands: [
            { command: "git switch release/<version>", description: "Switch to the target branch" },
            { command: "git pull --ff-only", description: "Ensure the target branch is current" },
            { command: "git cherry-pick -x <fix-commit>", description: "Apply the fix and record its origin in the message" },
          ],
          note: "Check whether the fix depends on earlier commits. A technically successful Cherry-pick can still be functionally incomplete.",
        },
        {
          id: "workflow-clean-commit",
          title: "Split mixed changes into clean commits",
          summary: "You do not have to commit everything that is currently changed. Hunk or line staging separates refactoring, fixes, and documentation.",
          commands: [
            { command: "git add -p", description: "Select changes hunk by hunk" },
            { command: "git diff --staged", description: "Review the first commit's content" },
            { command: "git commit", description: "Create the first logical commit" },
            { command: "git add -p && git commit", description: "Continue with the next topic" },
          ],
        },
      ],
    },
    {
      id: "reference",
      label: "Reference & glossary",
      description: "A compact command overview and the core Git terms in one place.",
      sections: [
        {
          id: "reference-daily",
          title: "Daily quick reference",
          summary: "The most common safe commands for orientation, changes, commits, and synchronization.",
          commands: [
            { command: "git status", description: "Check state" },
            { command: "git diff", description: "Read local changes" },
            { command: "git add -p", description: "Stage selectively" },
            { command: "git diff --staged", description: "Review commit contents" },
            { command: "git commit", description: "Create a commit" },
            { command: "git fetch --prune", description: "Refresh remote state" },
            { command: "git push", description: "Publish local commits" },
          ],
        },
        {
          id: "reference-glossary",
          title: "Git glossary",
          summary: "HEAD is the current checkout. Branches and tags are references to commits. origin is only the conventional name of a remote. Upstream is the remote reference assigned to a local branch.",
          steps: [
            "Commit: Immutable project snapshot with parents, author, time, and message.",
            "Index/Staging: Prepared snapshot for the next commit.",
            "Working tree: Checked-out files you are currently editing.",
            "Remote: Named connection to another repository, not necessarily “the cloud”.",
            "Fast-forward: A branch pointer can move forward without a merge commit.",
            "Detached HEAD: HEAD points directly to a commit instead of a local branch.",
          ],
        },
        {
          id: "reference-safety",
          title: "Risk levels of Git commands",
          summary: "Read-only commands such as status, log, show, and diff are harmless. Restore, Reset, Clean, Rebase, and Force Push change or remove state and deserve an extra check.",
          steps: [
            "Safe and read-only: status, log, show, diff, branch, remote -v, reflog.",
            "Locally modifying: add, restore, commit, stash, switch, merge, rebase.",
            "Potentially destructive: reset --hard, clean -fd, branch -D.",
            "Team-wide risk: push --force, rebasing published commits, or moving tags.",
          ],
          note: "When unsure, stop, create a backup branch, and inspect git status and git reflog. Git rewards small, understandable steps.",
        },
      ],
    },
  );

  deCategories.splice(1, 0, {
    id: "changelog",
    label: "Neu in Gitty",
    description: "Änderungen seit der letzten veröffentlichten Version und wichtige Neuerungen früherer Releases.",
    sections: [
      {
        id: "changelog-2026-8-6",
        title: "Version 2026.8.6",
        summary: "Dieses Wartungsrelease stabilisiert Git-LFS-Workflows vom Tracking über Clone und Pull bis zum Push großer Dateien nach Azure DevOps.",
        steps: [
          "LFS-Muster aus der .gitattributes im Repository-Stamm bleiben im LFS-Dialog sichtbar, auch wenn die Datei noch ungetrackt ist oder zuvor durch eine Ignore-Regel ausgeblendet wurde.",
          "Beim Aktivieren von Git LFS und beim Hinzufügen eines Tracking-Musters stellt Gitty sicher, dass die .gitattributes nicht ignoriert wird. Nur wenn nötig, wird die gezielte Ausnahme !/.gitattributes am Ende der .gitignore ergänzt.",
          "Clone und Pull verwenden für erkannte LFS-Repositories denselben Remote und dieselben Zugangsdaten auch zum Laden der LFS-Objekte. Neue Klone aktivieren Filter und Pre-Push-Hook automatisch.",
          "Wenn Azure DevOps einen großen LFS-Upload über HTTP/2 mit HTTP 413 ablehnt, wiederholt Gitty den Push einmal mit einer nur für diesen Befehl geltenden HTTP/1.1-Konfiguration. Globale und Repository-Einstellungen bleiben unverändert.",
          "LFS-, Größen- und andere allgemeine Push-Fehler werden nicht mehr als Non-Fast-Forward verwechselt. Der unnötige Ablauf „Pull vor Push“ mit anschließendem „Push after pull“ erscheint nur noch bei einem tatsächlichen veralteten lokalen Branch.",
          "Der Tauri-Debug-Launcher entfernt ausschließlich bekannte nicht routende Test-Proxys aus dem Gitty-Unterprozess. Echte Benutzer- und Unternehmens-Proxys bleiben erhalten, sodass Remote- und LFS-Abläufe auch im Debug-Build testbar sind.",
        ],
        note: "Die HTTP/1.1-Wiederholung greift nur nach einem LFS-Fehler 413. Änderungen an .gitattributes und .gitignore bleiben normale Repository-Änderungen und müssen committed und gepusht werden.",
      },
      {
        id: "changelog-2026-8-5",
        title: "Version 2026.8.5",
        summary: "Dieses Release integriert Git LFS direkt in Gitty und macht den Staging-Bereich bei vielen geänderten Dateien deutlich übersichtlicher.",
        steps: [
          "Git LFS ist direkt über das Synchronisierungsmenü erreichbar. Gitty prüft die verfügbare Erweiterung, die Repository-Konfiguration und den Pre-Push-Hook und zeigt an, ob Git LFS mit Gitty gebündelt oder systemweit installiert ist.",
          "LFS-Muster lassen sich hinzufügen, als Lockable markieren und wieder entfernen. Der Dialog zeigt außerdem die LFS-Dateien des aktuellen Checkouts, lädt fehlende Objekte und bereinigt nicht mehr benötigte Cache-Objekte.",
          "Nach einem erfolgreichen Clone oder Pull erkennt Gitty LFS-Repositories automatisch und lädt die benötigten LFS-Objekte mit demselben Remote und denselben Zugangsdaten. Neue Klone aktivieren LFS außerdem lokal, sodass kein zweiter manueller Pull erforderlich ist.",
          "Unstaged und Staged stehen jetzt gleich breit nebeneinander, scrollen unabhängig voneinander und verwenden eindeutige Pfeile für Stage und Unstage. Bei schmalen Fenstern wechselt die Darstellung automatisch untereinander.",
          "Der mittig angeordnete List-/Tree-Umschalter zeigt Änderungen entweder als kompakte Liste oder gruppiert sie in beiden Bereichen nach aufklappbaren Ordnern.",
          "Über das neu gestaltete Kontextmenü einer Datei oder eines Ordners lassen sich gezielt einzelne Dateien oder alle Änderungen im Ordner stagen, unstagen oder in einem eigenen Stash sichern. Neue und ungetrackte Inhalte können im Changes-Bereich und im File Explorer als exakte Datei, kompletter Ordner oder Dateiendungs-Muster in die .gitignore übernommen werden; die Ordneroption erscheint nur beim Rechtsklick auf einen Ordner. Bereits getrackte Dateien und Ordner lassen sich mit „Stop tracking“ aus dem Git-Index entfernen, bleiben aber auf der Festplatte erhalten. Dateiname, übergeordneter Pfad und Anzahl der betroffenen Dateien sind dabei klar voneinander getrennt.",
          "Repositories können beim Start über --repo PATH oder --repo=PATH direkt geöffnet werden. Mit clone REMOTE ZIEL, --clone REMOTE ZIEL oder --clone=REMOTE ZIEL klont Gitty ein Remote-Repository in den exakt angegebenen lokalen Ordner und öffnet es anschließend. Relative Pfade werden gegen das aktuelle Arbeitsverzeichnis aufgelöst; der Aufruf wird auch an eine bereits laufende Gitty-Instanz weitergegeben.",
          "Quadratische Bedienelemente und Flächen vereinheitlichen das Erscheinungsbild; runde Statuspunkte, Avatare und charakteristische Branch-Markierungen bleiben erhalten.",
        ],
        note: "Von Git LFS erzeugte Änderungen an .gitattributes gehören zum Repository und müssen wie jede andere Änderung committed werden. Bereits vorhandene Git-Historie wird durch neue Tracking-Muster nicht rückwirkend umgeschrieben.",
      },
      {
        id: "changelog-2026-8-4",
        title: "Version 2026.8.4",
        summary: "Dieses Release vereinfacht die Verwaltung zusammengehöriger Branches und sorgt für eine einheitlichere, klarere Oberfläche.",
        steps: [
          "Lokale und verschachtelte Remote-Branch-Ordner lassen sich über ihr Kontextmenü gesammelt löschen. Der oberste Remote-Ordner wie origin ist geschützt. Der aktuell ausgecheckte Branch bleibt erhalten und einzelne Fehler werden nach Abschluss verständlich aufgeführt.",
          "Der Compare-Auswahldialog ist vollständig auf Deutsch verfügbar und orientiert sich bei Feldern, Gruppen, Typografie und Dialogflächen am Styling der externen Tools.",
          "Die Schließen-Schaltflächen der Repository-Tabs sind quadratisch und haben ausgewogenere Abstände sowie deutlichere Hover- und Tastaturfokus-Zustände.",
        ],
        note: "Der oberste Remote-Ordner wie origin kann nicht gesammelt gelöscht werden. Seine Unterordner können weiterhin gezielt verwaltet werden.",
      },
      {
        id: "changelog-2026-8-3",
        title: "Version 2026.8.3",
        summary: "Dieses Release verbindet Gitty enger mit deinen Entwicklungswerkzeugen und macht Branches, Historie und Vergleiche deutlich leistungsfähiger.",
        steps: [
          "Externe Tools: Editor, Diff-Tool, Merge-Tool, Terminal und Dateimanager lassen sich in den neu gestalteten Einstellungen erkennen, auswählen und individuell konfigurieren.",
          "VS Code, JetBrains-IDEs, Beyond Compare und weitere unterstützte Programme werden in einem eigenen Fenster geöffnet; dokumentierte Ergebnis-Codes werden beim Schließen korrekt behandelt.",
          "Git Notes: Commits erhalten lokale Notizen, ohne ihre Historie umzuschreiben. Notizen lassen sich bearbeiten, löschen sowie gezielt vom Remote abrufen oder dorthin übertragen.",
          "Vollständiger Branch-Vergleich: Lokale Branches, Remote-Branches und Commits können direkt ausgewählt und dateiweise im Side-by-Side-Diff verglichen werden.",
          "Remote-Branches lassen sich im Kontextmenü sicher umbenennen. Gitty schützt dabei vorhandene Ziel-Branches und zwischenzeitlich geänderte Remote-Stände.",
          "Der überarbeitete Commit-Graph zeigt Branches kompakter, reduziert überladene Commit-Zeilen und blendet zusätzliche Flag-Details beim Darüberfahren ein.",
          "Noch nicht veröffentlichte Branches sind als „Nur lokal“ erkennbar – in der Werkzeugleiste, Repository-Übersicht, Statuszeile und direkt an der Branch-Flag. Die erste Push-Aktion heißt passend „Veröffentlichen“.",
          "Branch-Sichtbarkeit, feinere Graph-Verbindungen und ein kleineres Mindestmaß des Verlaufsbereichs verbessern die Übersicht bei großen Repositories.",
          "Die neue Befehlspalette öffnet Aktionen, Dateien und Commits schneller; asynchrone Git-Befehle halten Gitty auch bei langsameren Operationen reaktionsfähig.",
        ],
        note: "Der Branch-Vergleich zeigt die vollständig festgeschriebenen Zustände der beiden Branch-Spitzen. Nicht commitete Änderungen im Arbeitsverzeichnis sind nicht enthalten.",
      },
      {
        id: "changelog-2026-8-2",
        title: "Version 2026.8.2",
        summary: "Dieses Release stabilisiert die Darstellung komplexer Verläufe und verbessert die Veröffentlichung neuer Gitty-Versionen.",
        steps: [
          "Branch-Farben bleiben über Eltern-Lanes hinweg stabil, sodass sich Linien in längeren und verzweigten Historien leichter verfolgen lassen.",
          "Release-Artefakte werden automatisch und ohne doppelte Dateien an das passende Gitea-Release angehängt.",
          "Beim Beenden der Anwendung wird die Telemetrie zuverlässiger abgeschlossen.",
        ],
      },
      {
        id: "changelog-2026-8-1",
        title: "Version 2026.8.1",
        summary: "Dieses Release macht große Commit-Verläufe und die Historie einzelner Dateien leichter zugänglich und verbessert die Paketverteilung.",
        steps: [
          "Die Commit-Historie lädt ältere Einträge seitenweise nach und ist nicht mehr auf die erste Ergebnismenge begrenzt.",
          "Die Dateihistorie öffnet sich aus dem Explorer-Kontextmenü in einem eigenen, größeren Dialog statt in einem dauerhaft belegten Seitenbereich.",
          "Dialoge reagieren konsistenter auf die Escape-Taste.",
          "Windows- und Ubuntu-Releases sowie der AUR-Paketablauf wurden erweitert und robuster gemacht.",
          "Die Arch-Linux-Anleitung verwendet jetzt das AUR-Paket gitty-desktop; SSH-Einrichtung, Zeitlimits und Wiederholungsversuche wurden verbessert.",
        ],
      },
      {
        id: "changelog-2026-07-22",
        title: "Version 2026.07.22",
        summary: "Dieses Release erweitert Gitty um eine AI-gestützte Aufteilung gestagter Änderungen in logisch getrennte Commits.",
        steps: [
          "AI-Commit-Aufteilung: Der gestagte Diff wird analysiert und als geordneter Plan aus mehreren logisch zusammengehörenden Commits vorgeschlagen.",
          "Für jede Gruppe wird automatisch eine editierbare Conventional-Commit-Nachricht erzeugt.",
          "Dateien können vor dem Commit zwischen den vorgeschlagenen Gruppen verschoben werden.",
          "Mit „Commit all“ werden alle bestätigten Gruppen sicher und der Reihe nach committed.",
          "Der Dialog erklärt leere Gruppen oder fehlende Nachrichten und schützt vor einem zwischenzeitlich veränderten Staging-Bereich.",
          "„Commit all“ reagiert wieder zuverlässig und bricht nicht mehr beim Kopieren des reaktiven Dialogzustands ab.",
          "Ein geschlossenes aktives Repository kann nicht mehr durch einen verspäteten Status- oder Fetch-Request erneut geöffnet werden.",
        ],
        note: "Die AI-Commit-Aufteilung unterstützt OpenAI, Anthropic und eigene OpenAI-kompatible Endpunkte. In Paketdateien erscheint diese Version als 2026.7.22.",
      },
      {
        id: "changelog-2026-07-21",
        title: "Version 2026.07.21",
        summary: "Dieses Release bündelt paralleles Arbeiten mit Worktrees, präzisere Commits und die neue Arch-Linux-Verteilung.",
        steps: [
          "Worktree-Verwaltung: zusätzliche Arbeitsordner erstellen, öffnen, verschieben, sperren, entsperren, reparieren, entfernen und veraltete Registrierungen aufräumen.",
          "Worktrees sind direkt über den neuen Reiter unter Tags erreichbar; Branches lassen sich außerdem aus ihrem Kontextmenü in einem Worktree öffnen.",
          "Zeilenweises Staging: einzelne Ergänzungen und Löschungen auswählen, per Shift-Klick Bereiche markieren sowie ausgewählte Zeilen stagen, unstagen oder verwerfen.",
          "Sicherere Dialoge: verständlichere Branch-Löschabfrage und weichgezeichneter Hintergrund bei geöffneten Dialogen.",
          "Arch-Linux-Pakete: automatisierter Build aus dem PKGBUILD, Veröffentlichung von .pkg.tar.zst und Repository-Datenbank für Pacman auf dem CDN.",
          "Robustere Remote-Aktionen, zentrale Fehlermeldungen und strukturierte, datensparsame Telemetrie.",
          "Erweiterte zweisprachige Hilfe mit Worktree-, Pacman- und Line-Staging-Anleitungen.",
        ],
        note: "In Paketdateien kann dieselbe Version als 2026.7.21 erscheinen, weil Paketmanager numerische Versionssegmente ohne führende Null verwenden.",
      },
      {
        id: "changelog-2026-7-20",
        title: "Version 2026.7.20",
        summary: "Der letzte veröffentlichte Stand konzentrierte sich auf produktiveres Arbeiten, bessere Orientierung und einen stabileren Paket-Build.",
        steps: [
          "Vor dem Commit kann eine AI-gestützte Codeprüfung den Staged-Diff analysieren.",
          "Automatische Aktualisierung hält Repository-Status und Arbeitsbereich auf Wunsch aktuell.",
          "Repository-Aktionsleiste, Statusdarstellung, Tabs und Diff-Ansicht wurden übersichtlicher gestaltet.",
          "Die integrierte Hilfe wurde um ausführliche deutsche Git-Dokumentation ergänzt.",
          "PKGBUILD und Build-Skripte wurden für die Arch-Linux-Verteilung vorbereitet.",
        ],
      },
    ],
  });

  enCategories.splice(1, 0, {
    id: "changelog",
    label: "What's new",
    description: "Changes since the latest published version and notable additions from earlier releases.",
    sections: [
      {
        id: "changelog-2026-8-6",
        title: "Version 2026.8.6",
        summary: "This maintenance release stabilizes Git LFS workflows from tracking through clone and pull to pushing large files to Azure DevOps.",
        steps: [
          "LFS patterns from the root .gitattributes remain visible in the LFS dialog even while the file is untracked or was previously hidden by an ignore rule.",
          "When Git LFS is activated or a tracking pattern is added, Gitty ensures that .gitattributes is not ignored. Only when required, the scoped !/.gitattributes exception is appended to .gitignore.",
          "Clone and pull use the same remote and credentials to download LFS objects for detected LFS repositories. Fresh clones also activate the filters and pre-push hook automatically.",
          "When Azure DevOps rejects a large LFS upload over HTTP/2 with HTTP 413, Gitty retries the push once with an HTTP/1.1 setting scoped to that command. Global and repository settings remain unchanged.",
          "LFS, size, and other generic push failures are no longer mistaken for non-fast-forward rejections. The Pull before push and Push after pull flow is now offered only when the local branch is genuinely behind its remote.",
          "The Tauri debug launcher removes only known non-routing test proxies from the Gitty child process. Real user and company proxies are preserved, keeping remote and LFS workflows testable in debug builds.",
        ],
        note: "The HTTP/1.1 retry runs only after an LFS HTTP 413 failure. Changes to .gitattributes and .gitignore remain ordinary repository changes that must be committed and pushed.",
      },
      {
        id: "changelog-2026-8-5",
        title: "Version 2026.8.5",
        summary: "This release integrates Git LFS directly into Gitty and makes the staging area much easier to navigate when many files have changed.",
        steps: [
          "Git LFS is available directly from the Sync menu. Gitty checks the available extension, repository configuration, and pre-push hook, and reports whether Git LFS is bundled with Gitty or installed system-wide.",
          "LFS patterns can be added, marked as Lockable, and removed again. The dialog also lists LFS files in the current checkout, downloads missing objects, and prunes unused cache objects.",
          "After a successful clone or pull, Gitty automatically detects LFS repositories and downloads the required LFS objects with the same remote and credentials. Fresh clones also activate LFS locally, so a second manual pull is no longer required.",
          "Unstaged and Staged now sit side by side at equal width, scroll independently, and use clear arrows for Stage and Unstage. Narrow windows automatically fall back to a vertical layout.",
          "The centered List/Tree switch presents changes either as a compact list or groups them into collapsible folders in both areas.",
          "The redesigned file and folder context menu can stage, unstage, or save only that file or the folder's complete set of changes in a dedicated stash. In Changes and the File Explorer, new and untracked items can be added to .gitignore as an exact file, a complete folder, or an extension-wide pattern; the folder option only appears for folder selections. Tracked files and folders can be removed from the Git index with Stop tracking while remaining on disk. The selected name, parent path, and affected file count are now clearly separated.",
          "Repositories can be opened directly at startup with --repo PATH or --repo=PATH. With clone REMOTE TARGET, --clone REMOTE TARGET, or --clone=REMOTE TARGET, Gitty clones a remote into the exact local folder and opens it afterward. Relative paths are resolved against the current working directory, and requests are forwarded to an already-running Gitty instance.",
          "Square controls and surfaces make the interface more consistent while circular status markers, avatars, and characteristic branch shapes remain intact.",
        ],
        note: "Changes to .gitattributes created by Git LFS belong to the repository and must be committed like any other change. New tracking patterns do not rewrite existing Git history retroactively.",
      },
      {
        id: "changelog-2026-8-4",
        title: "Version 2026.8.4",
        summary: "This release simplifies managing related branches and makes the interface more consistent and easier to read.",
        steps: [
          "Local and nested remote branch folders can be deleted in one action from their context menu. The top-level remote folder such as origin is protected. The currently checked-out branch is kept, and individual failures are summarized after processing.",
          "The Compare selector is fully localized in German and now follows the external-tool selectors for fields, groups, typography, and dialog surfaces.",
          "Repository-tab close buttons are square and have more balanced spacing and clearer hover and keyboard-focus states.",
        ],
        note: "The top-level remote folder such as origin cannot be deleted in bulk. Its nested folders can still be managed selectively.",
      },
      {
        id: "changelog-2026-8-3",
        title: "Version 2026.8.3",
        summary: "This release connects Gitty more closely with your development tools and makes branches, history, and comparisons substantially more capable.",
        steps: [
          "External tools: editors, diff tools, merge tools, terminals, and file managers can be detected, selected, and customized in the redesigned settings.",
          "VS Code, JetBrains IDEs, Beyond Compare, and other supported applications open in a separate window; documented result codes are handled correctly when they close.",
          "Git Notes: attach local notes to commits without rewriting history. Notes can be edited, deleted, fetched from a remote, or pushed explicitly.",
          "Complete branch comparison: choose local branches, remote branches, or commits and inspect every changed file in a side-by-side diff.",
          "Remote branches can be renamed safely from the context menu. Gitty protects existing destination branches and remote branches that changed after the last fetch.",
          "The redesigned commit graph presents branches more compactly, reduces crowded commit rows, and reveals additional flag details on hover.",
          "Unpublished branches are clearly marked as Local only in the toolbar, repository summary, status bar, and on the graph flag. Their first push is labeled Publish.",
          "Branch visibility controls, refined graph connectors, and a smaller minimum history width improve navigation in large repositories.",
          "The new command palette opens actions, files, and commits faster; asynchronous Git commands keep Gitty responsive during slower operations.",
        ],
        note: "Branch comparison uses the fully committed state at each branch tip. Uncommitted working-tree changes are not included.",
      },
      {
        id: "changelog-2026-8-2",
        title: "Version 2026.8.2",
        summary: "This release stabilizes complex history rendering and improves publication of new Gitty versions.",
        steps: [
          "Branch colors remain stable across parent lanes, making longer and branching histories easier to follow.",
          "Release artifacts are attached to the matching Gitea release automatically without uploading duplicates.",
          "Telemetry cleanup completes more reliably while the application is shutting down.",
        ],
      },
      {
        id: "changelog-2026-8-1",
        title: "Version 2026.8.1",
        summary: "This release makes large commit histories and individual file histories easier to access and improves package distribution.",
        steps: [
          "Commit history loads older entries page by page instead of stopping after the initial result set.",
          "File history opens from the explorer context menu in a dedicated larger dialog instead of occupying a permanent workspace panel.",
          "Dialogs respond more consistently to the Escape key.",
          "Windows and Ubuntu publishing plus the AUR package workflow were expanded and made more robust.",
          "The Arch Linux guide now uses the gitty-desktop AUR package; SSH setup, timeouts, and retry handling were improved.",
        ],
      },
      {
        id: "changelog-2026-07-22",
        title: "Version 2026.07.22",
        summary: "This release adds AI-assisted splitting of staged changes into separate logical commits.",
        steps: [
          "AI commit splitting analyzes the staged diff and proposes an ordered plan of logically related commits.",
          "Every group receives an automatically generated, editable Conventional Commit message.",
          "Files can be moved between proposed groups before committing.",
          "Commit all safely creates every accepted group in sequence.",
          "The dialog explains empty groups or missing messages and protects against a staging area that changed after planning.",
          "Commit all now responds reliably instead of failing while copying reactive dialog state.",
          "Closing the active repository no longer lets a delayed status or fetch request reopen the closed tab.",
        ],
        note: "AI commit splitting supports OpenAI, Anthropic, and custom OpenAI-compatible endpoints. Package metadata represents this version as 2026.7.22.",
      },
      {
        id: "changelog-2026-07-21",
        title: "Version 2026.07.21",
        summary: "This release combines parallel worktree workflows, more precise commits, and the new Arch Linux distribution.",
        steps: [
          "Worktree management: create, open, move, lock, unlock, repair, remove, and prune additional working folders.",
          "Worktrees are available from the new entry below Tags; branches can also be opened in a worktree from their context menu.",
          "Line-level staging: select additions and deletions, Shift-click ranges, and stage, unstage, or discard selected lines.",
          "Safer dialogs: a clearer branch deletion confirmation and a blurred background while dialogs are open.",
          "Arch Linux packages: automated PKGBUILD builds plus publication of .pkg.tar.zst and the Pacman repository database to the CDN.",
          "More robust remote operations, centralized error messages, and structured privacy-conscious telemetry.",
          "Expanded bilingual help for worktrees, Pacman installation, and line-level staging.",
        ],
        note: "Package metadata may represent the same release as 2026.7.21 because package managers use numeric version segments without leading zeroes.",
      },
      {
        id: "changelog-2026-7-20",
        title: "Version 2026.7.20",
        summary: "The latest published version focused on productivity, clearer navigation, and a more stable package build.",
        steps: [
          "AI-assisted pre-commit review can analyze the staged diff before committing.",
          "Optional automatic refresh keeps repository status and the workspace current.",
          "The repository action bar, status presentation, tabs, and diff view became easier to scan.",
          "The built-in help gained comprehensive German Git documentation.",
          "PKGBUILD and build scripts prepared the Arch Linux distribution workflow.",
        ],
      },
    ],
  });

  let { language = "en", onClose = () => {} }: Props = $props();
  const isGerman = $derived(language === "de");
  const categories = $derived(isGerman ? deCategories : enCategories);
  let selectedCategoryId = $state("start");
  let searchQuery = $state("");
  let copiedCommand = $state("");
  let searchInput: HTMLInputElement;
  let contentElement: HTMLElement;
  let copyTimer: ReturnType<typeof setTimeout> | undefined;

  const normalizedQuery = $derived(searchQuery.trim().toLocaleLowerCase(language));
  const selectedCategory = $derived(categories.find((category) => category.id === selectedCategoryId) ?? categories[0]);
  const visibleGroups = $derived.by(() => {
    if (!normalizedQuery) return [{ category: selectedCategory, sections: selectedCategory.sections }];
    return categories.flatMap((category) => {
      const sections = category.sections.filter((section) => sectionSearchText(category, section).includes(normalizedQuery));
      return sections.length > 0 ? [{ category, sections }] : [];
    });
  });
  const resultCount = $derived(visibleGroups.reduce((sum, group) => sum + group.sections.length, 0));

  $effect(() => {
    normalizedQuery;
    selectedCategoryId;
    if (contentElement) contentElement.scrollTop = 0;
  });

  onMount(() => {
    void tick().then(() => searchInput?.focus());
    return () => {
      if (copyTimer) clearTimeout(copyTimer);
    };
  });

  function sectionSearchText(category: HelpCategory, section: HelpSection): string {
    return [
      category.label,
      category.description,
      section.title,
      section.summary,
      ...(section.steps ?? []),
      ...(section.commands?.flatMap((entry) => [entry.command, entry.description]) ?? []),
      section.note ?? "",
    ].join(" ").toLocaleLowerCase(language);
  }

  function selectCategory(id: string) {
    selectedCategoryId = id;
    searchQuery = "";
  }

  function isWarningNote(note: string): boolean {
    return /^(Vorsicht|Rebase|Caution|Do not)/.test(note);
  }

  async function copyCommand(command: string) {
    try {
      await navigator.clipboard.writeText(command);
      copiedCommand = command;
      if (copyTimer) clearTimeout(copyTimer);
      copyTimer = setTimeout(() => { copiedCommand = ""; }, 1800);
    } catch {
      copiedCommand = "";
    }
  }

  function handleBackdropClick(event: MouseEvent) {
    if (event.target === event.currentTarget) onClose();
  }
</script>

<div class="help-backdrop" role="presentation" onclick={handleBackdropClick}>
  <div class="help-overlay" role="dialog" aria-modal="true" aria-labelledby="help-title">
    <header class="help-header">
      <div class="help-title-wrap">
        <span class="help-mark"><CircleHelp size={19} aria-hidden="true" /></span>
        <div>
          <h2 id="help-title">{isGerman ? "Gitty Hilfe" : "Gitty Help"}</h2>
          <p>{isGerman ? "App-Anleitung und Git-Wissen an einem Ort" : "App guidance and Git knowledge in one place"}</p>
        </div>
      </div>

      <label class="help-search">
        <span class="help-search-icon"><Search size={17} aria-hidden="true" /></span>
        <input bind:this={searchInput} bind:value={searchQuery} type="search" placeholder={isGerman ? "Hilfe und Git-Befehle durchsuchen …" : "Search help and Git commands …"} aria-label={isGerman ? "Hilfe durchsuchen" : "Search help"} />
        <kbd>Ctrl /</kbd>
      </label>

      <button class="help-close" type="button" onclick={onClose} title={isGerman ? "Hilfe schließen" : "Close help"} aria-label={isGerman ? "Hilfe schließen" : "Close help"}>
        <X size={19} aria-hidden="true" />
      </button>
    </header>

    <div class="help-layout">
      <nav class="help-nav" aria-label={isGerman ? "Hilfethemen" : "Help topics"}>
        <p class="help-nav-label">{isGerman ? "Themen" : "Topics"}</p>
        {#each categories as category}
          <button
            type="button"
            class:active={!normalizedQuery && category.id === selectedCategoryId}
            onclick={() => selectCategory(category.id)}
          >
            <span class="help-nav-icon">
              {#if category.id === "start"}<Home size={17} aria-hidden="true" />
              {:else if category.id === "changelog"}<Sparkles size={17} aria-hidden="true" />
              {:else if category.id === "app"}<BookOpen size={17} aria-hidden="true" />
              {:else if category.id === "basics"}<GitCommitHorizontal size={17} aria-hidden="true" />
              {:else if category.id === "branches"}<GitBranch size={17} aria-hidden="true" />
              {:else if category.id === "remote"}<Cloud size={17} aria-hidden="true" />
              {:else if category.id === "lfs"}<Box size={17} aria-hidden="true" />
              {:else if category.id === "troubleshooting"}<Wrench size={17} aria-hidden="true" />
              {:else if category.id === "workflows"}<ListChecks size={17} aria-hidden="true" />
              {:else if category.id === "reference"}<Library size={17} aria-hidden="true" />
              {:else}<Keyboard size={17} aria-hidden="true" />{/if}
            </span>
            <span>{category.label}</span>
            <span class="help-nav-chevron"><ChevronRight size={14} aria-hidden="true" /></span>
          </button>
        {/each}

        <div class="help-nav-tip">
          <span class="help-tip-icon"><Lightbulb size={16} aria-hidden="true" /></span>
          <span>{isGerman ? "Suche auch nach Befehlen wie" : "Try commands such as"} <code>rebase</code>, <code>lfs</code> {isGerman ? "oder" : "or"} <code>reflog</code>.</span>
        </div>
      </nav>

      <main class="help-content" bind:this={contentElement}>
        {#if normalizedQuery}
          <header class="help-result-header">
            <div>
              <span>{isGerman ? "Suchergebnisse" : "Search results"}</span>
              <h3>{resultCount} {isGerman ? "Treffer für" : resultCount === 1 ? "result for" : "results for"} „{searchQuery.trim()}“</h3>
            </div>
            <button type="button" onclick={() => { searchQuery = ""; }}>{isGerman ? "Suche löschen" : "Clear search"}</button>
          </header>
        {/if}

        {#if visibleGroups.length === 0}
          <div class="help-empty">
            <span class="help-empty-icon"><Search size={28} aria-hidden="true" /></span>
            <h3>{isGerman ? "Kein Hilfethema gefunden" : "No help topic found"}</h3>
            <p>{isGerman ? "Versuche einen allgemeineren Begriff wie „Commit“, „Branch“, „Remote“ oder „Konflikt“." : "Try a broader term such as “commit”, “branch”, “remote”, or “conflict”."}</p>
            <button class="btn-secondary" type="button" onclick={() => { searchQuery = ""; }}>{isGerman ? "Alle Themen anzeigen" : "Show all topics"}</button>
          </div>
        {:else}
          {#each visibleGroups as group}
            <div class="help-group">
              <header class="help-group-header">
                <span>{normalizedQuery ? group.category.label : isGerman ? "Gitty Handbuch" : "Gitty handbook"}</span>
                <h3>{normalizedQuery ? group.category.label : group.category.label}</h3>
                <p>{group.category.description}</p>
              </header>

              {#each group.sections as section, sectionIndex}
                <article class="help-section" id={section.id}>
                  <div class="help-section-number">{String(sectionIndex + 1).padStart(2, "0")}</div>
                  <div class="help-section-body">
                    <h4>{section.title}</h4>
                    <p>{section.summary}</p>

                    {#if section.steps}
                      <ol class="help-steps">
                        {#each section.steps as step}
                          <li><span>{step}</span></li>
                        {/each}
                      </ol>
                    {/if}

                    {#if section.commands}
                      <div class="help-commands">
                        {#each section.commands as entry}
                          <div class="help-command-row">
                            <code>{entry.command}</code>
                            <span>{entry.description}</span>
                            <button type="button" onclick={() => copyCommand(entry.command)} aria-label={(isGerman ? "Befehl kopieren: " : "Copy command: ") + entry.command} title={isGerman ? "Befehl kopieren" : "Copy command"}>
                              {#if copiedCommand === entry.command}
                                <Check size={15} aria-hidden="true" /><span>{isGerman ? "Kopiert" : "Copied"}</span>
                              {:else}
                                <Clipboard size={15} aria-hidden="true" /><span>{isGerman ? "Kopieren" : "Copy"}</span>
                              {/if}
                            </button>
                          </div>
                        {/each}
                      </div>
                    {/if}

                    {#if section.note}
                      <div class="help-note" class:warning={isWarningNote(section.note)}>
                        {#if isWarningNote(section.note)}
                          <span class="help-note-icon"><AlertTriangle size={16} aria-hidden="true" /></span>
                        {:else}
                          <span class="help-note-icon"><Lightbulb size={16} aria-hidden="true" /></span>
                        {/if}
                        <span>{section.note}</span>
                      </div>
                    {/if}
                  </div>
                </article>
              {/each}
            </div>
          {/each}
        {/if}
      </main>
    </div>

    <footer class="help-footer">
      <span><Command size={14} aria-hidden="true" /> <kbd>Ctrl</kbd><kbd>/</kbd> {isGerman ? "Hilfe öffnen" : "open help"}</span>
      <span><kbd>Esc</kbd> {isGerman ? "schließen" : "close"}</span>
    </footer>
  </div>
</div>

<style>
  .help-backdrop {
    position: fixed;
    inset: var(--app-titlebar-height, 42px) 0 0;
    z-index: 70;
    display: grid;
    place-items: center;
    padding: 24px;
    background: rgba(3, 7, 13, 0.82);
    backdrop-filter: blur(3px);
  }

  .help-overlay {
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto;
    width: min(1240px, 100%);
    height: min(820px, 100%);
    overflow: hidden;
    border: 1px solid var(--color-border);
    border-radius: 12px;
    color: var(--color-ink);
    background: var(--app-dialog-bg);
    box-shadow: var(--app-dialog-shadow);
  }

  .help-header {
    display: grid;
    grid-template-columns: minmax(220px, 0.72fr) minmax(320px, 1.2fr) minmax(44px, 0.72fr);
    align-items: center;
    gap: 22px;
    min-height: 74px;
    padding: 12px 16px 12px 20px;
    border-bottom: 1px solid var(--color-border);
    background: var(--app-dialog-chrome);
  }

  .help-title-wrap { display: flex; align-items: center; gap: 11px; min-width: 0; }
  .help-mark { display: grid; place-items: center; width: 34px; height: 34px; border: 1px solid rgba(90, 140, 248, 0.28); border-radius: 8px; color: var(--color-accent); background: rgba(90, 140, 248, 0.09); }
  .help-title-wrap h2 { margin: 0; font-size: 17px; line-height: 1.2; }
  .help-title-wrap p { margin: 3px 0 0; overflow: hidden; color: var(--color-ink-faint); font-size: 10.5px; text-overflow: ellipsis; white-space: nowrap; }

  .help-search { position: relative; display: flex; align-items: center; min-width: 0; }
  .help-search-icon { position: absolute; left: 12px; display: grid; color: var(--color-ink-faint); pointer-events: none; }
  .help-search input { width: 100%; height: 40px; padding: 0 68px 0 39px; border-color: var(--color-border-input); border-radius: 8px; background: var(--app-input-bg); color: var(--color-ink); font-size: 12.5px; }
  .help-search kbd { position: absolute; right: 8px; }
  .help-close { justify-self: end; width: 36px; min-height: 36px; padding: 0; border-color: transparent; border-radius: 7px; background: transparent; color: var(--color-ink-muted); }

  .help-layout { display: grid; grid-template-columns: 250px minmax(0, 1fr); min-height: 0; }
  .help-nav { display: flex; flex-direction: column; min-height: 0; padding: 14px 10px 12px; border-right: 1px solid var(--color-border); background: var(--color-surface-dim); }
  .help-nav-label { margin: 0 10px 8px; color: var(--color-ink-faint); font-size: 9.5px; font-weight: 800; letter-spacing: 0.09em; text-transform: uppercase; }
  .help-nav > button { display: grid; grid-template-columns: 28px minmax(0, 1fr) auto; align-items: center; gap: 8px; width: 100%; min-height: 42px; padding: 5px 9px; border-color: transparent; border-radius: 7px; background: transparent; color: var(--color-ink-muted); font-size: 11.5px; font-weight: 650; text-align: left; }
  .help-nav > button:hover { background: var(--color-surface-hover); color: var(--color-ink); }
  .help-nav > button.active { border-color: rgba(90, 140, 248, 0.22); background: rgba(90, 140, 248, 0.12); color: var(--color-accent); }
  .help-nav-icon { display: grid; place-items: center; color: currentColor; }
  .help-nav-chevron { display: grid; color: var(--color-ink-faint); }
  .help-nav-tip { display: flex; align-items: flex-start; gap: 8px; margin: auto 6px 0; padding: 11px; border-top: 1px solid var(--color-border-subtle); color: var(--color-ink-faint); font-size: 10.5px; line-height: 1.5; }
  .help-tip-icon { flex: 0 0 auto; display: grid; margin-top: 2px; color: #d8a13d; }
  .help-nav-tip code { color: var(--color-ink-muted); font-family: var(--font-mono); }

  .help-content { min-width: 0; min-height: 0; padding: 0 34px 48px; overflow: auto; outline: none; scroll-behavior: smooth; }
  .help-group { max-width: 850px; margin: 0 auto; }
  .help-group + .help-group { margin-top: 22px; border-top: 1px solid var(--color-border); }
  .help-group-header { padding: 34px 0 26px; border-bottom: 1px solid var(--color-border); }
  .help-group-header > span, .help-result-header span { color: var(--color-accent); font-size: 9.5px; font-weight: 800; letter-spacing: 0.11em; text-transform: uppercase; }
  .help-group-header h3 { margin: 7px 0 8px; font-size: clamp(24px, 3vw, 32px); letter-spacing: -0.025em; }
  .help-group-header p { max-width: 650px; margin: 0; color: var(--color-ink-muted); font-size: 13px; line-height: 1.55; }

  .help-result-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; max-width: 850px; margin: 0 auto; padding: 22px 0 0; }
  .help-result-header h3 { margin: 4px 0 0; font-size: 17px; }
  .help-result-header button { min-height: 30px; padding: 0 10px; border-color: var(--color-border-subtle); background: transparent; color: var(--color-ink-muted); font-size: 10.5px; }

  .help-section { display: grid; grid-template-columns: 42px minmax(0, 1fr); gap: 2px; padding: 26px 0; border-bottom: 1px solid var(--color-border-subtle); }
  .help-section-number { padding-top: 3px; color: var(--color-ink-faint); font-family: var(--font-mono); font-size: 10px; }
  .help-section-body h4 { margin: 0 0 7px; font-size: 16px; }
  .help-section-body > p { margin: 0; color: var(--color-ink-muted); font-size: 12.5px; line-height: 1.6; }
  .help-steps { display: grid; gap: 10px; margin: 18px 0 0; padding: 0; list-style: none; counter-reset: help-step; }
  .help-steps li { display: grid; grid-template-columns: 24px minmax(0, 1fr); align-items: start; gap: 9px; color: var(--color-ink-muted); font-size: 12px; line-height: 1.5; counter-increment: help-step; }
  .help-steps li::before { content: counter(help-step); display: grid; place-items: center; width: 22px; height: 22px; border: 1px solid rgba(90, 140, 248, 0.35); border-radius: 50%; color: var(--color-accent); font-family: var(--font-mono); font-size: 9px; font-weight: 800; }

  .help-commands { display: grid; gap: 1px; margin-top: 17px; overflow: hidden; border: 1px solid var(--color-border); border-radius: 8px; background: var(--color-border-subtle); }
  .help-command-row { display: grid; grid-template-columns: minmax(210px, 0.9fr) minmax(190px, 1.2fr) auto; align-items: center; gap: 14px; min-height: 47px; padding: 6px 7px 6px 13px; background: var(--code-surface); }
  .help-command-row:hover { background: var(--code-hover-bg); }
  .help-command-row > code { overflow: hidden; color: var(--color-accent); font-family: var(--font-mono); font-size: 11.5px; text-overflow: ellipsis; white-space: nowrap; }
  .help-command-row > span { color: var(--color-ink-faint); font-size: 10.5px; line-height: 1.35; }
  .help-command-row button { display: inline-flex; align-items: center; gap: 6px; min-width: 83px; min-height: 31px; padding: 0 9px; border-color: var(--color-border-subtle); border-radius: 6px; background: var(--color-surface-raised); color: var(--color-ink-muted); font-size: 10px; }
  .help-command-row button:hover { border-color: rgba(90, 140, 248, 0.32); color: var(--color-accent); }

  .help-note { display: flex; align-items: flex-start; gap: 9px; margin-top: 15px; padding: 11px 12px; border: 1px solid rgba(90, 140, 248, 0.2); border-radius: 7px; background: rgba(90, 140, 248, 0.07); color: var(--color-ink-muted); font-size: 11px; line-height: 1.5; }
  .help-note-icon { flex: 0 0 auto; display: grid; margin-top: 1px; color: var(--color-accent); }
  .help-note.warning { border-color: rgba(224, 160, 64, 0.24); background: rgba(224, 160, 64, 0.07); }
  .help-note.warning .help-note-icon { color: #d8a13d; }

  .help-empty { display: grid; justify-items: center; max-width: 520px; margin: 90px auto 0; text-align: center; }
  .help-empty-icon { display: grid; color: var(--color-ink-faint); }
  .help-empty h3 { margin: 14px 0 5px; font-size: 18px; }
  .help-empty p { margin: 0 0 18px; color: var(--color-ink-muted); font-size: 12px; line-height: 1.55; }

  .help-footer { display: flex; align-items: center; justify-content: space-between; min-height: 38px; padding: 0 16px 0 20px; border-top: 1px solid var(--color-border); background: var(--app-dialog-chrome); color: var(--color-ink-faint); font-size: 9.5px; }
  .help-footer span { display: inline-flex; align-items: center; gap: 5px; }
  kbd { display: inline-grid; place-items: center; min-width: 20px; height: 21px; padding: 0 5px; border: 1px solid var(--color-border-input); border-radius: 5px; background: var(--color-surface-raised); color: var(--color-ink-muted); box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.04); font-family: var(--font-mono); font-size: 9px; }

  @media (max-width: 820px) {
    .help-backdrop { padding: 10px; }
    .help-header { grid-template-columns: minmax(0, 1fr) auto; gap: 10px; }
    .help-title-wrap { display: none; }
    .help-layout { grid-template-columns: 190px minmax(0, 1fr); }
    .help-content { padding-inline: 20px; }
    .help-command-row { grid-template-columns: minmax(0, 1fr) auto; gap: 5px 10px; }
    .help-command-row > span { grid-column: 1; }
    .help-command-row button { grid-column: 2; grid-row: 1 / 3; }
  }

  @media (max-width: 600px) {
    .help-layout { grid-template-columns: 1fr; }
    .help-nav { flex-direction: row; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--color-border); }
    .help-nav-label, .help-nav-tip, .help-nav-chevron { display: none; }
    .help-nav > button { flex: 0 0 auto; width: auto; grid-template-columns: 24px auto; }
    .help-content { padding-inline: 15px; }
    .help-section { grid-template-columns: 1fr; }
    .help-section-number { display: none; }
    .help-footer { display: none; }
  }
</style>
