Easy ein Windows Tool wie „ComPortAnzeiger“ mit Antigravity erstellen

Ob in der Arduino IDE oder VS Code oder sonstige Entwicklung Umgebungen, wo kann ich das angesteckte Board ansprechen. Ach wäre es nicht toll, wenn ich die USB Anschluss Nummer (COM-Port) beim verbinden, direkt sehen könnte?

Welche meiner installierten Anwendungsprogramme unter Windows 11 sollten mal Upgedatet werden?

Wie kann ich als Ausbilder, bildhafter ich im Netzwerkunterricht, die 7 Schichten im OSI erklären?

Du brauchst schnell mal eine Erweiterung für Windows oder interaktive Darstellung? Da haben wir was für sie! Tada…

Google Antigravity macht es sehr leicht möglich. (Tatsächlich!)

Antigravity ist eine KI-gestützte Entwicklungsplattform, die autonome Agenten einsetzt, um Softwareprojekte zu planen, zu programmieren, zu testen und zu dokumentieren. Sie wurde von Google DeepMind entwickelt als AI-native Entwicklungsumgebungs-App, die klassische Coding-Tools erweitert, indem sie KI-Agenten in den Mittelpunkt stellt. Anders als herkömmliche Assistenten, die nur Codezeilen ergänzen, kann Antigravity komplette Aufgabenpakete autonom bearbeiten – von der Planung über die Umsetzung bis hin zur Überprüfung im Browser.

Klingt nach wenig Aufwand und schnellem Ergebnis 🙂
Download der App: https://antigravity.google/

Hier 4 Anwendungsbeispiele die zeigt wie wenig das Projekt beschrieben werden muss und wie effektiv das Ergebnis war:

Beispiel 1 – Serielle Schnittstellen Anzeige

Anforderung:

Erstelle eine Lösung die im Task Manager die sobald ein Arduino oder esp32 angesteckt wird, den verwendeten COM Port anzeigt und wenn wieder abgeklemmt dann „kein COM Port wird verwendet“ angezeigt sein

Erzeugt wurden im Ordner C:\Users\prilchen\OneDrive\Dokumente\antigravity\ die Datei ComPortMonitor.ps1

Zum lesen anklicken
param(
    [switch]$Hidden = $true
)

if ($Hidden) {
    # Verstecke das Konsolenfenster
    $code = @"
    using System;
    using System.Runtime.InteropServices;
    public class Win32 {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        
        [DllImport("kernel32.dll")]
        public static extern IntPtr GetConsoleWindow();
    }
"@
    try {
        Add-Type -TypeDefinition $code -ErrorAction SilentlyContinue
        [Win32]::ShowWindow([Win32]::GetConsoleWindow(), 0) | Out-Null
    } catch {}
}

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# Erstelle das System-Tray-Icon
$icon = New-Object System.Windows.Forms.NotifyIcon
$icon.Icon = [System.Drawing.SystemIcons]::Information
$icon.Visible = $true
$icon.Text = "Initialisiere COM Port Monitor..."

# Kontextmenü (Rechtsklick) hinzufügen, um das Skript beenden zu können
$contextMenu = New-Object System.Windows.Forms.ContextMenu
$menuItemExit = New-Object System.Windows.Forms.MenuItem
$menuItemExit.Text = "Beenden"
$menuItemExit.add_Click({
    $timer.Stop()
    $icon.Visible = $false
    [System.Windows.Forms.Application]::Exit()
})
$contextMenu.MenuItems.Add($menuItemExit)
$icon.ContextMenu = $contextMenu

# Timer für die Überprüfung der COM Ports (jede Sekunde)
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000

$script:currentPortNames = @()
$script:firstRun = $true

$timer.add_Tick({
    # Hole aktuelle COM Ports (sehr schnell, keine hohe CPU Last)
    $newPortNames = [System.IO.Ports.SerialPort]::GetPortNames() | Sort-Object
    
    $changed = $false
    if ($newPortNames.Count -ne $script:currentPortNames.Count) {
        $changed = $true
    } else {
        for ($i=0; $i -lt $newPortNames.Count; $i++) {
            if ($newPortNames[$i] -ne $script:currentPortNames[$i]) {
                $changed = $true
                break
            }
        }
    }
    
    if ($changed -or $script:firstRun) {
        $script:currentPortNames = $newPortNames
        
        if ($newPortNames.Count -gt 0) {
            # Wenn Ports vorhanden sind, hole die freundlichen Namen über WMI (z.B. "Arduino Uno (COM3)")
            $wmiPorts = Get-CimInstance Win32_PnPEntity | Where-Object { $_.Name -match "\((COM\d+)\)" }
            $textParts = @()
            foreach ($port in $newPortNames) {
                $friendlyName = $port
                if ($wmiPorts) {
                    foreach ($wmi in $wmiPorts) {
                        if ($wmi.Name -match "\($port\)") {
                            $friendlyName = $wmi.Name
                            break
                        }
                    }
                }
                $textParts += $friendlyName
            }
            
            $text = $textParts -join ", "
            # NotifyIcon.Text ist auf 63 Zeichen limitiert
            if ($text.Length -gt 63) {
                $text = $text.Substring(0, 60) + "..."
            }
            $icon.Text = $text
            
            # Wenn es sich geändert hat und es nicht der erste Start ist, zeige eine Benachrichtigung (Balloon Tip)
            if (-not $script:firstRun) {
                $icon.ShowBalloonTip(3000, "ESP32/Arduino verbunden", ($textParts -join "`n"), [System.Windows.Forms.ToolTipIcon]::Info)
            }
        } else {
            $icon.Text = "kein COM Port wird verwendet"
            
            if (-not $script:firstRun) {
                $icon.ShowBalloonTip(3000, "Gerät getrennt", "kein COM Port wird verwendet", [System.Windows.Forms.ToolTipIcon]::Warning)
            }
        }
        
        $script:firstRun = $false
    }
})

$timer.Start()
# Starte die Windows Forms Anwendungsschleife, damit das Icon aktiv bleibt
[System.Windows.Forms.Application]::Run()

und damit ein jeder das sofort starten kann, diese StartMonitor.bat noch dazu:

@echo off
REM Startet den COM Port Monitor im Hintergrund
powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0ComPortMonitor.ps1"

Ergebnis:

In Windows unten rechts, bei der Uhr taucht nun immer wenn ein Microcontroller angeschlossen wird, nun die gewünschte Info auf. 🙂


2tes Beispiel – Update Checker

Anforderung:

Erstelle eine Win11 App die beim Start prüft, welche der installierten Programme upgegraded werden können.

Antwort:

Ich habe einen Implementierungsplan für die Windows 11 App erstellt. Bitte sieh dir den Plan an (implementation_plan.md) und teile mir mit, ob du mit dem Technologie-Stack (C# WPF mit nativem Win11 Design) einverstanden bist und beantworte kurz die offenen Fragen.

implementation_plan.md:

Bist du mit C# und WPF (Windows Presentation Foundation) in Kombination mit der Bibliothek WPF-UI (für das native Windows 11 Fluent Design mit Mica-Effekt) einverstanden?
Die App wird im Hintergrund den Windows Package Manager (winget) nutzen, um die Updates abzufragen. Ist das in Ordnung?
Hast du .NET 8 SDK auf deinem System installiert? (Falls nicht, müssen wir es installieren oder eine andere Technologie wie Python oder eine Web-App mit Tauri wählen).

Sobald du das Go gibst, fange ich mit der Erstellung der App an!

Erzeugte ps1-Datei (Zum Anzeigen anklicken)
import customtkinter as ctk
import subprocess
import threading
import sys

# Setzt das Erscheinungsbild der GUI:
# - "System": folgt dem Windows‑11‑Design (Dark/Light Mode)
# - "blue": Windows‑typischer Akzentfarbton
ctk.set_appearance_mode("System")
ctk.set_default_color_theme("blue")


class App(ctk.CTk):
    def __init__(self):
        super().__init__()

        # Grundlegende Fensterkonfiguration
        self.title("Windows 11 Upgrade Checker")
        self.geometry("850x550")
        self.minsize(800, 500)

        # Grid‑Layout für dynamische Größenanpassung
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(0, weight=1)

        # -------------------------
        # HEADER-BEREICH
        # -------------------------
        self.header_frame = ctk.CTkFrame(self, fg_color="transparent")
        self.header_frame.grid(row=0, column=0, padx=20, pady=(20, 10), sticky="ew")

        # Titel
        self.title_label = ctk.CTkLabel(
            self.header_frame,
            text="Verfügbare Updates",
            font=ctk.CTkFont(family="Segoe UI Variable Display", size=24, weight="bold")
        )
        self.title_label.pack(side="left")

        # Aktualisieren‑Button → lädt Updates neu
        self.refresh_btn = ctk.CTkButton(
            self.header_frame,
            text="Aktualisieren",
            width=120,
            command=self.load_updates_async
        )
        self.refresh_btn.pack(side="right")

        # -------------------------
        # LADEN-ANZEIGE
        # -------------------------
        self.loading_label = ctk.CTkLabel(
            self,
            text="Suche nach Updates (winget wird ausgeführt)...",
            font=ctk.CTkFont(family="Segoe UI Variable", size=16)
        )
        self.loading_label.grid(row=1, column=0, padx=20, pady=20)

        # Scrollbarer Bereich für die Update-Liste
        self.scroll_frame = ctk.CTkScrollableFrame(self)

        # Liste der gefundenen Updates
        self.update_list = []

        # Beim Start sofort Updates laden
        self.load_updates_async()

    # ---------------------------------------------------------
    # Asynchrones Laden der Updates (damit GUI nicht blockiert)
    # ---------------------------------------------------------
    def load_updates_async(self):
        # Scrollbereich verstecken, Ladeanzeige zeigen
        self.scroll_frame.grid_forget()
        self.loading_label.grid(row=1, column=0, padx=20, pady=20)
        self.refresh_btn.configure(state="disabled")

        # Alte Einträge entfernen
        for widget in self.scroll_frame.winfo_children():
            widget.destroy()

        # winget-Aufruf in separatem Thread starten
        thread = threading.Thread(target=self.fetch_winget_updates)
        thread.start()

    # ---------------------------------------------------------
    # winget upgrade ausführen und Ausgabe parsen
    # ---------------------------------------------------------
    def fetch_winget_updates(self):
        try:
            # Unter Windows: verhindert das Öffnen eines extra Konsolenfensters
            startupinfo = None
            if sys.platform == "win32":
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

            # winget upgrade ausführen
            result = subprocess.run(
                ["winget", "upgrade"],
                capture_output=True,
                text=True,
                encoding='utf-8',
                errors='ignore',
                startupinfo=startupinfo
            )

            output = result.stdout

            # Ausgabe parsen
            apps = self.parse_winget_output(output)

            # UI‑Update im Hauptthread
            self.after(0, self.display_updates, apps)

        except Exception as e:
            self.after(0, self.display_error, str(e))

    # ---------------------------------------------------------
    # winget‑Textausgabe in strukturierte App‑Objekte umwandeln
    # ---------------------------------------------------------
    def parse_winget_output(self, output):
        lines = output.split('\n')
        header_line = None
        header_idx = -1

        # Header suchen (Name | ID | Version | Verfügbar | Quelle)
        for i, line in enumerate(lines):
            if line.startswith('Name') and 'ID' in line and 'Version' in line:
                header_line = line
                header_idx = i
                break

        # Keine Updates gefunden oder Format unbekannt
        if not header_line:
            return []

        # Spaltenpositionen bestimmen
        id_idx = header_line.find('ID')
        version_idx = header_line.find('Version')
        avail_idx = header_line.find('Verfügbar')
        if avail_idx == -1:
            avail_idx = header_line.find('Available')

        source_idx = header_line.find('Quelle')
        if source_idx == -1:
            source_idx = header_line.find('Source')

        apps = []

        # Ab Zeile nach dem Header parsen
        for line in lines[header_idx + 2:]:
            if not line.strip() or line.startswith('-'):
                continue

            # Zusammenfassungszeilen überspringen
            if any(keyword in line for keyword in ['Aktualisierung', 'Paket', 'packages', 'updates', 'Mindestens']):
                continue

            # Spalten extrahieren
            name = line[:id_idx].strip()
            app_id = line[id_idx:version_idx].strip()
            version = line[version_idx:avail_idx].strip()

            if source_idx != -1:
                available = line[avail_idx:source_idx].strip()
            else:
                available = line[avail_idx:].strip()

            # Unicode‑Ellipsen entfernen
            name = name.rstrip('…')

            if name and app_id:
                apps.append({
                    'name': name,
                    'id': app_id,
                    'version': version,
                    'available': available
                })

        return apps

    # ---------------------------------------------------------
    # Updates in der GUI anzeigen
    # ---------------------------------------------------------
    def display_updates(self, apps):
        self.loading_label.grid_forget()
        self.scroll_frame.grid(row=1, column=0, padx=20, pady=(0, 20), sticky="nsew")
        self.refresh_btn.configure(state="normal")

        # Keine Updates gefunden
        if not apps:
            no_updates = ctk.CTkLabel(
                self.scroll_frame,
                text="Dein System ist auf dem neuesten Stand! 🎉",
                font=ctk.CTkFont(size=16)
            )
            no_updates.pack(pady=40)
            return

        # Tabellenkopf
        header_frm = ctk.CTkFrame(self.scroll_frame, fg_color="transparent")
        header_frm.pack(fill="x", padx=10, pady=5)

        ctk.CTkLabel(header_frm, text="Name", font=ctk.CTkFont(weight="bold"), width=250, anchor="w").pack(side="left")
        ctk.CTkLabel(header_frm, text="Installiert", font=ctk.CTkFont(weight="bold"), width=100, anchor="w").pack(side="left", padx=10)
        ctk.CTkLabel(header_frm, text="Verfügbar", font=ctk.CTkFont(weight="bold"), width=100, anchor="w").pack(side="left", padx=10)

        # Jede App als Zeile anzeigen
        for app in apps:
            item_frm = ctk.CTkFrame(self.scroll_frame)
            item_frm.pack(fill="x", padx=5, pady=5)

            # Name
            name_lbl = ctk.CTkLabel(item_frm, text=app['name'], width=250, anchor="w")
            name_lbl.pack(side="left", padx=10, pady=10)

            # Installierte Version
            cur_lbl = ctk.CTkLabel(item_frm, text=app['version'], width=100, anchor="w", text_color="gray")
            cur_lbl.pack(side="left", padx=10, pady=10)

            # Verfügbare Version
            new_lbl = ctk.CTkLabel(item_frm, text=app['available'], width=100, anchor="w", text_color="#2da44e")
            new_lbl.pack(side="left", padx=10, pady=10)

            # Update‑Button → öffnet CMD‑Fenster
            update_btn = ctk.CTkButton(
                item_frm,
                text="Update",
                width=80,
                command=lambda a_id=app['id']: self.run_update(a_id)
            )
            update_btn.pack(side="right", padx=15, pady=10)

    # ---------------------------------------------------------
    # Fehleranzeige
    # ---------------------------------------------------------
    def display_error(self, err_msg):
        self.loading_label.grid_forget()
        self.scroll_frame.grid(row=1, column=0, padx=20, pady=(0, 20), sticky="nsew")
        self.refresh_btn.configure(state="normal")

        err_lbl = ctk.CTkLabel(
            self.scroll_frame,
            text=f"Fehler beim Abrufen der Updates:\n{err_msg}",
            text_color="red"
        )
        err_lbl.pack(pady=40)

    # ---------------------------------------------------------
    # Update ausführen → neues CMD‑Fenster mit winget upgrade
    # ---------------------------------------------------------
    def run_update(self, app_id):
        command = f'winget upgrade --id {app_id} --accept-source-agreements'
        subprocess.Popen(f'start cmd /c "{command} & pause"', shell=True)


# Start der Anwendung
if __name__ == "__main__":
    app = App()
    app.mainloop()

Main.py erstellen und das Script hinein kopieren.
Es wird Python unter Windows benötigt.
Wer das noch installieren muss, hier die Anleitung:
ESP32 Firmware sichern und wiederherstellen mit ESPTool

Erzeugte Batch zum einfachen starten, Start.bat erzeugen und dies hier hinein kopieren:

@echo off
cd /d "%~dp0"
start "" ".\venv\Scripts\pythonw.exe" "main.py"

Ergebnis:

Beim Klick auf einer Update Aufforderung einer Anwendung, geht eine separate Eingabeaufforderung auf. Ein Winget Befehl führt das update dann zuverlässig aus!

Alternativ geht es auch, um schnell mal alles zu aktualisieren, in der Eingabeausforderung mit Befehl: winget update –all 😉


Beispiel 3:

Anforderung:

Ich brauche für die Ausbildung eine grafische Darstellung beim versenden einer email. Die schritt für schritt das verschicken in den 7 OSI Schichten zeigt. Also smtp, utf8 oder kodierung, schicht id, tcp, ip-adressen, mac-adressen und binäre werte

Erzeugt diesen HTML-Code: (anklicken zum lesen)
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OSI-Modell: E-Mail Versand</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=Fira+Code:wght@400;600&display=swap" rel="stylesheet">
    <style>
        :root {
            --bg-color: #0f172a;
            --text-color: #f8fafc;
            --accent-color: #3b82f6;
            --accent-hover: #2563eb;
            --glass-bg: rgba(255, 255, 255, 0.05);
            --glass-border: rgba(255, 255, 255, 0.1);
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: 'Inter', sans-serif;
            background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 100%);
            color: var(--text-color);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            overflow-x: hidden;
        }

        header {
            text-align: center;
            padding: 2rem;
            background: var(--glass-bg);
            border-bottom: 1px solid var(--glass-border);
            backdrop-filter: blur(10px);
        }

        header h1 {
            font-weight: 700;
            font-size: 2.5rem;
            background: linear-gradient(to right, #60a5fa, #c084fc);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            margin-bottom: 0.5rem;
        }

        .container {
            display: flex;
            flex: 1;
            padding: 2rem;
            gap: 2rem;
            max-width: 1400px;
            margin: 0 auto;
            width: 100%;
        }

        .sidebar {
            flex: 0 0 300px;
            display: flex;
            flex-direction: column;
            gap: 1rem;
        }

        .layer-btn {
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            color: var(--text-color);
            padding: 1rem;
            border-radius: 12px;
            cursor: pointer;
            text-align: left;
            transition: all 0.3s ease;
            position: relative;
            overflow: hidden;
        }

        .layer-btn.active {
            background: rgba(59, 130, 246, 0.2);
            border-color: var(--accent-color);
            box-shadow: 0 0 20px rgba(59, 130, 246, 0.3);
            transform: translateX(10px);
        }

        .layer-btn:hover:not(.active) {
            background: rgba(255, 255, 255, 0.1);
        }

        .layer-number {
            font-weight: 700;
            color: #94a3b8;
            font-size: 0.875rem;
            margin-bottom: 0.25rem;
        }

        .active .layer-number {
            color: #60a5fa;
        }

        .layer-name {
            font-weight: 600;
            font-size: 1.125rem;
        }

        .main-content {
            flex: 1;
            display: flex;
            flex-direction: column;
            gap: 2rem;
        }

        .info-panel {
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            border-radius: 16px;
            padding: 2rem;
            backdrop-filter: blur(10px);
            min-height: 250px;
        }

        .info-panel h2 {
            margin-bottom: 1rem;
            color: #c084fc;
        }

        .info-panel p {
            line-height: 1.6;
            color: #cbd5e1;
            font-size: 1.1rem;
        }

        .detail-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 1rem;
            margin-top: 1.5rem;
        }

        .detail-item {
            background: rgba(0, 0, 0, 0.2);
            padding: 1rem;
            border-radius: 8px;
            border: 1px solid rgba(255, 255, 255, 0.05);
        }

        .detail-label {
            font-size: 0.8rem;
            color: #94a3b8;
            text-transform: uppercase;
            letter-spacing: 0.05em;
            margin-bottom: 0.5rem;
        }

        .detail-value {
            font-family: 'Fira Code', monospace;
            font-size: 1rem;
            color: #34d399;
            word-break: break-all;
        }

        .packet-visualization {
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            border-radius: 16px;
            padding: 2rem;
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            overflow-x: auto;
            backdrop-filter: blur(10px);
            min-height: 180px;
        }

        .packet-container {
            display: flex;
            flex-direction: row;
            align-items: stretch;
            gap: 0.5rem;
        }

        .packet-chunk {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            padding: 1.5rem;
            border-radius: 8px;
            font-family: 'Fira Code', monospace;
            font-size: 1.1rem;
            font-weight: 700;
            color: #fff;
            opacity: 0;
            transform: translateY(-20px);
            animation: dropIn 0.3s forwards ease-out;
            min-width: 120px;
            text-align: center;
            box-shadow: 0 4px 12px rgba(0,0,0,0.4);
            border: 1px solid rgba(255, 255, 255, 0.2);
        }

        @keyframes dropIn {
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .controls {
            display: flex;
            justify-content: space-between;
            margin-top: auto;
        }

        button.nav-btn {
            background: var(--accent-color);
            color: white;
            border: none;
            padding: 1rem 2.5rem;
            border-radius: 8px;
            font-size: 1.1rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s;
            box-shadow: 0 4px 6px rgba(0,0,0,0.2);
        }

        button.nav-btn:hover:not(:disabled) {
            background: var(--accent-hover);
            transform: translateY(-2px);
            box-shadow: 0 6px 12px rgba(0,0,0,0.3);
        }

        button.nav-btn:disabled {
            background: #475569;
            cursor: not-allowed;
            opacity: 0.5;
            box-shadow: none;
            transform: none;
        }

        /* Layer specific colors */
        .color-7 { background: linear-gradient(135deg, #ec4899, #be185d); }
        .color-6 { background: linear-gradient(135deg, #d946ef, #a21caf); }
        .color-5 { background: linear-gradient(135deg, #8b5cf6, #6d28d9); }
        .color-4 { background: linear-gradient(135deg, #6366f1, #4338ca); }
        .color-3 { background: linear-gradient(135deg, #3b82f6, #1d4ed8); }
        .color-2 { background: linear-gradient(135deg, #0ea5e9, #0369a1); }
        .color-1 { background: linear-gradient(135deg, #14b8a6, #0f766e); }
        
        .binary-stream {
            font-family: 'Fira Code', monospace;
            font-size: 1.2rem;
            color: #34d399;
            letter-spacing: 3px;
            word-break: break-all;
            line-height: 1.8;
            max-height: 150px;
            overflow: hidden;
            position: relative;
            text-align: center;
        }

        .pulse-text {
            animation: pulse 1.5s infinite;
        }

        @keyframes pulse {
            0% { opacity: 0.6; }
            50% { opacity: 1; text-shadow: 0 0 10px #34d399; }
            100% { opacity: 0.6; }
        }
    </style>
</head>
<body>

    <header>
        <h1>OSI-Modell: E-Mail Versand</h1>
        <p>Schritt-für-Schritt Darstellung der Netzwerkschichten (Top-Down)</p>
    </header>

    <div class="container">
        <div class="sidebar" id="sidebar">
            <!-- Dynamically populated -->
        </div>

        <div class="main-content">
            <div class="info-panel" id="infoPanel">
                <!-- Dynamically populated -->
            </div>

            <div class="packet-visualization">
                <div class="packet-container" id="packetContainer">
                    <!-- Dynamically populated -->
                </div>
            </div>

            <div class="controls">
                <button class="nav-btn" id="prevBtn" onclick="prevLayer()">Zurück</button>
                <button class="nav-btn" id="nextBtn" onclick="nextLayer()">Weiter (Nächste Schicht)</button>
            </div>
        </div>
    </div>

    <script>
        const layers = [
            {
                number: 7,
                name: "Anwendungsschicht (Application)",
                title: "Schicht 7: E-Mail wird verfasst",
                desc: "Der Benutzer schreibt eine E-Mail im E-Mail-Client. Das Simple Mail Transfer Protocol (SMTP) wird verwendet, um die Nachricht zu formatieren und für den Versand vorzubereiten.",
                details: {
                    "Protokoll": "SMTP",
                    "Aktion": "MAIL FROM, RCPT TO, DATA",
                    "Nutzdaten": "\"Hallo, wie geht's?\""
                },
                packetLabel: "DATA",
                packetColor: "color-7"
            },
            {
                number: 6,
                name: "Darstellungsschicht (Presentation)",
                title: "Schicht 6: Formatierung & Kodierung",
                desc: "Die Nachricht wird in ein einheitliches Format übersetzt. Der Text wird z.B. in UTF-8 kodiert. Hier könnte auch Verschlüsselung (TLS) oder Komprimierung stattfinden.",
                details: {
                    "Kodierung": "UTF-8",
                    "MIME-Type": "text/plain",
                    "Konvertiert": "48 61 6c 6c 6f..."
                },
                packetLabel: "UTF8",
                packetColor: "color-6"
            },
            {
                number: 5,
                name: "Sitzungsschicht (Session)",
                title: "Schicht 5: Sitzungsverwaltung",
                desc: "Die Verbindung (Sitzung) zwischen Sender und Empfänger-Server wird logisch aufgebaut und aufrechterhalten. Eine eindeutige Schicht-ID (Session-ID) wird zugewiesen.",
                details: {
                    "Schicht-ID (Session)": "0x8A9B7C2F",
                    "Status": "Established",
                    "Kontrolle": "Dialog Control"
                },
                packetLabel: "SESS",
                packetColor: "color-5"
            },
            {
                number: 4,
                name: "Transportschicht (Transport)",
                title: "Schicht 4: TCP Segmentierung",
                desc: "Die Daten werden zuverlässig übertragen. Ein TCP-Header wird hinzugefügt. Da es sich um SMTP handelt, ist der Ziel-Port 25 (oder 587/465).",
                details: {
                    "Protokoll": "TCP",
                    "Source-Port": "54321 (Dynamisch)",
                    "Dest-Port": "25 (SMTP)",
                    "Sequenz-Nr": "1001"
                },
                packetLabel: "TCP",
                packetColor: "color-4"
            },
            {
                number: 3,
                name: "Vermittlungsschicht (Network)",
                title: "Schicht 3: IP Adressierung (Pakete)",
                desc: "Das TCP-Segment wird in ein IP-Paket gepackt. Hier werden die logischen IP-Adressen von Sender und Empfänger hinzugefügt, damit das Paket geroutet werden kann.",
                details: {
                    "Protokoll": "IPv4 / IPv6",
                    "Source-IP": "192.168.1.10",
                    "Dest-IP": "203.0.113.50",
                    "TTL": "64"
                },
                packetLabel: "IP",
                packetColor: "color-3"
            },
            {
                number: 2,
                name: "Sicherungsschicht (Data Link)",
                title: "Schicht 2: MAC Adressierung (Frames)",
                desc: "Das IP-Paket wird in einen Ethernet-Frame gepackt. Die physikalischen MAC-Adressen der Netzwerkkarten (Sender und nächster Router) werden als Header hinzugefügt, plus ein Trailer zur Fehlerprüfung.",
                details: {
                    "Protokoll": "Ethernet II",
                    "Source-MAC": "AA:BB:CC:DD:EE:FF",
                    "Dest-MAC": "11:22:33:44:55:66",
                    "FCS (Trailer)": "0x3A4B5C6D"
                },
                packetLabel: "MAC",
                packetColor: "color-2"
            },
            {
                number: 1,
                name: "Bitübertragungsschicht (Physical)",
                title: "Schicht 1: Binäre Übertragung",
                desc: "Der gesamte Frame wird in eine Folge von Nullen und Einsen (Bits) umgewandelt und als elektrisches Signal, Lichtsignal oder Funkwelle über das physikalische Medium gesendet.",
                details: {
                    "Medium": "Kupfer (CAT6) / WLAN",
                    "Signal": "Elektrische Impulse",
                    "Binäre Werte": "01001000 01100101 01101100..."
                },
                packetLabel: "BITS",
                packetColor: "color-1"
            }
        ];

        let currentStep = 0;

        function init() {
            renderSidebar();
            updateView();
        }

        function renderSidebar() {
            const sidebar = document.getElementById('sidebar');
            sidebar.innerHTML = '';
            layers.forEach((layer, index) => {
                const btn = document.createElement('div');
                btn.className = `layer-btn ${index === currentStep ? 'active' : ''}`;
                btn.onclick = () => {
                    currentStep = index;
                    updateView();
                };
                btn.innerHTML = `
                    <div class="layer-number">Schicht ${layer.number}</div>
                    <div class="layer-name">${layer.name}</div>
                `;
                sidebar.appendChild(btn);
            });
        }

        function updateView() {
            // Update sidebar active state
            document.querySelectorAll('.layer-btn').forEach((btn, idx) => {
                btn.className = `layer-btn ${idx === currentStep ? 'active' : ''}`;
            });

            const layer = layers[currentStep];

            // Update info panel
            let detailsHtml = '';
            for (const [key, value] of Object.entries(layer.details)) {
                detailsHtml += `
                    <div class="detail-item">
                        <div class="detail-label">${key}</div>
                        <div class="detail-value">${value}</div>
                    </div>
                `;
            }

            const infoPanel = document.getElementById('infoPanel');
            infoPanel.innerHTML = `
                <h2>${layer.title}</h2>
                <p>${layer.desc}</p>
                <div class="detail-grid">
                    ${detailsHtml}
                </div>
            `;

            // Update packet visualization
            const packetContainer = document.getElementById('packetContainer');
            
            if (layer.number === 1) {
                // Physical layer - show binary stream
                packetContainer.innerHTML = `
                    <div class="binary-stream pulse-text">
                        01001000 01100101 01101100 01101100<br>
                        01101111 00100000 01010111 01101111<br>
                        01110010 01101100 01100100 00100001<br>
                        <br>... SIGNAL WIRD ÜBERTRAGEN ...
                    </div>
                `;
            } else {
                // Show stacked headers
                packetContainer.innerHTML = '';
                
                // Construct the packet from currentStep down to 0
                // This appends Headers to the left of the Data (as expected)
                for (let i = currentStep; i >= 0; i--) {
                    const l = layers[i];
                    const chunk = document.createElement('div');
                    chunk.className = `packet-chunk ${l.packetColor}`;
                    
                    // Simple animation delay for the newest layer added
                    if (i === currentStep && currentStep > 0) {
                        chunk.style.animation = 'dropIn 0.4s forwards cubic-bezier(0.175, 0.885, 0.32, 1.275)';
                    } else {
                        chunk.style.opacity = '1';
                        chunk.style.transform = 'translateY(0)';
                        chunk.style.animation = 'none';
                    }

                    chunk.innerHTML = `
                        <div style="font-size: 0.75rem; margin-bottom: 8px; opacity: 0.8;">Schicht ${l.number}</div>
                        <div>${l.packetLabel}</div>
                    `;
                    packetContainer.appendChild(chunk);
                }
                
                // Add Trailer if Data Link Layer (Schicht 2)
                if (layer.number === 2) {
                     const trailer = document.createElement('div');
                     trailer.className = `packet-chunk color-2`;
                     trailer.style.animation = 'dropIn 0.4s forwards cubic-bezier(0.175, 0.885, 0.32, 1.275)';
                     trailer.innerHTML = `
                        <div style="font-size: 0.75rem; margin-bottom: 8px; opacity: 0.8;">Trailer</div>
                        <div>FCS</div>
                     `;
                     packetContainer.appendChild(trailer);
                }
            }

            // Update buttons
            document.getElementById('prevBtn').disabled = currentStep === 0;
            document.getElementById('nextBtn').disabled = currentStep === layers.length - 1;
            
            if (currentStep === layers.length - 1) {
                document.getElementById('nextBtn').innerText = "Versand abgeschlossen";
            } else {
                document.getElementById('nextBtn').innerText = "Weiter (Nächste Schicht)";
            }
        }

        function nextLayer() {
            if (currentStep < layers.length - 1) {
                currentStep++;
                updateView();
            }
        }

        function prevLayer() {
            if (currentStep > 0) {
                currentStep--;
                updateView();
            }
        }

        // Initialize
        init();
    </script>
</body>
</html>

Ergebnis:

🔍 Live‑Ansicht öffnen

Beispiel 4:

bitte auch als weiters Beispiel, das abrufen der webseite „https://prilchen.de“ in den 7 osi schichten zeigt

Erzeugt diesen HTML-Code: (anklicken zum lesen)
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OSI-Modell: Webseite abrufen</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=Fira+Code:wght@400;600&display=swap" rel="stylesheet">
    <style>
        :root {
            --bg-color: #0f172a;
            --text-color: #f8fafc;
            --accent-color: #3b82f6;
            --accent-hover: #2563eb;
            --glass-bg: rgba(255, 255, 255, 0.05);
            --glass-border: rgba(255, 255, 255, 0.1);
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: 'Inter', sans-serif;
            background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 100%);
            color: var(--text-color);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            overflow-x: hidden;
        }

        header {
            text-align: center;
            padding: 2rem;
            background: var(--glass-bg);
            border-bottom: 1px solid var(--glass-border);
            backdrop-filter: blur(10px);
        }

        header h1 {
            font-weight: 700;
            font-size: 2.5rem;
            background: linear-gradient(to right, #60a5fa, #34d399);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            margin-bottom: 0.5rem;
        }

        .container {
            display: flex;
            flex: 1;
            padding: 2rem;
            gap: 2rem;
            max-width: 1400px;
            margin: 0 auto;
            width: 100%;
        }

        .sidebar {
            flex: 0 0 300px;
            display: flex;
            flex-direction: column;
            gap: 1rem;
        }

        .layer-btn {
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            color: var(--text-color);
            padding: 1rem;
            border-radius: 12px;
            cursor: pointer;
            text-align: left;
            transition: all 0.3s ease;
            position: relative;
            overflow: hidden;
        }

        .layer-btn.active {
            background: rgba(59, 130, 246, 0.2);
            border-color: var(--accent-color);
            box-shadow: 0 0 20px rgba(59, 130, 246, 0.3);
            transform: translateX(10px);
        }

        .layer-btn:hover:not(.active) {
            background: rgba(255, 255, 255, 0.1);
        }

        .layer-number {
            font-weight: 700;
            color: #94a3b8;
            font-size: 0.875rem;
            margin-bottom: 0.25rem;
        }

        .active .layer-number {
            color: #60a5fa;
        }

        .layer-name {
            font-weight: 600;
            font-size: 1.125rem;
        }

        .main-content {
            flex: 1;
            display: flex;
            flex-direction: column;
            gap: 2rem;
        }

        .info-panel {
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            border-radius: 16px;
            padding: 2rem;
            backdrop-filter: blur(10px);
            min-height: 250px;
        }

        .info-panel h2 {
            margin-bottom: 1rem;
            color: #34d399;
        }

        .info-panel p {
            line-height: 1.6;
            color: #cbd5e1;
            font-size: 1.1rem;
        }

        .detail-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 1rem;
            margin-top: 1.5rem;
        }

        .detail-item {
            background: rgba(0, 0, 0, 0.2);
            padding: 1rem;
            border-radius: 8px;
            border: 1px solid rgba(255, 255, 255, 0.05);
        }

        .detail-label {
            font-size: 0.8rem;
            color: #94a3b8;
            text-transform: uppercase;
            letter-spacing: 0.05em;
            margin-bottom: 0.5rem;
        }

        .detail-value {
            font-family: 'Fira Code', monospace;
            font-size: 1rem;
            color: #34d399;
            word-break: break-all;
        }

        .packet-visualization {
            background: var(--glass-bg);
            border: 1px solid var(--glass-border);
            border-radius: 16px;
            padding: 2rem;
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            overflow-x: auto;
            backdrop-filter: blur(10px);
            min-height: 180px;
        }

        .packet-container {
            display: flex;
            flex-direction: row;
            align-items: stretch;
            gap: 0.5rem;
        }

        .packet-chunk {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            padding: 1.5rem;
            border-radius: 8px;
            font-family: 'Fira Code', monospace;
            font-size: 1.1rem;
            font-weight: 700;
            color: #fff;
            opacity: 0;
            transform: translateY(-20px);
            animation: dropIn 0.3s forwards ease-out;
            min-width: 120px;
            text-align: center;
            box-shadow: 0 4px 12px rgba(0,0,0,0.4);
            border: 1px solid rgba(255, 255, 255, 0.2);
        }

        @keyframes dropIn {
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .controls {
            display: flex;
            justify-content: space-between;
            margin-top: auto;
        }

        button.nav-btn {
            background: var(--accent-color);
            color: white;
            border: none;
            padding: 1rem 2.5rem;
            border-radius: 8px;
            font-size: 1.1rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s;
            box-shadow: 0 4px 6px rgba(0,0,0,0.2);
        }

        button.nav-btn:hover:not(:disabled) {
            background: var(--accent-hover);
            transform: translateY(-2px);
            box-shadow: 0 6px 12px rgba(0,0,0,0.3);
        }

        button.nav-btn:disabled {
            background: #475569;
            cursor: not-allowed;
            opacity: 0.5;
            box-shadow: none;
            transform: none;
        }

        /* Layer specific colors */
        .color-7 { background: linear-gradient(135deg, #ec4899, #be185d); }
        .color-6 { background: linear-gradient(135deg, #d946ef, #a21caf); }
        .color-5 { background: linear-gradient(135deg, #8b5cf6, #6d28d9); }
        .color-4 { background: linear-gradient(135deg, #6366f1, #4338ca); }
        .color-3 { background: linear-gradient(135deg, #3b82f6, #1d4ed8); }
        .color-2 { background: linear-gradient(135deg, #0ea5e9, #0369a1); }
        .color-1 { background: linear-gradient(135deg, #14b8a6, #0f766e); }
        
        .binary-stream {
            font-family: 'Fira Code', monospace;
            font-size: 1.2rem;
            color: #34d399;
            letter-spacing: 3px;
            word-break: break-all;
            line-height: 1.8;
            max-height: 150px;
            overflow: hidden;
            position: relative;
            text-align: center;
        }

        .pulse-text {
            animation: pulse 1.5s infinite;
        }

        @keyframes pulse {
            0% { opacity: 0.6; }
            50% { opacity: 1; text-shadow: 0 0 10px #34d399; }
            100% { opacity: 0.6; }
        }
    </style>
</head>
<body>

    <header>
        <h1>OSI-Modell: Abruf von https://prilchen.de</h1>
        <p>Schritt-für-Schritt Darstellung des Webseiten-Aufrufs (Top-Down)</p>
    </header>

    <div class="container">
        <div class="sidebar" id="sidebar">
            <!-- Dynamically populated -->
        </div>

        <div class="main-content">
            <div class="info-panel" id="infoPanel">
                <!-- Dynamically populated -->
            </div>

            <div class="packet-visualization">
                <div class="packet-container" id="packetContainer">
                    <!-- Dynamically populated -->
                </div>
            </div>

            <div class="controls">
                <button class="nav-btn" id="prevBtn" onclick="prevLayer()">Zurück</button>
                <button class="nav-btn" id="nextBtn" onclick="nextLayer()">Weiter (Nächste Schicht)</button>
            </div>
        </div>
    </div>

    <script>
        const layers = [
            {
                number: 7,
                name: "Anwendungsschicht (Application)",
                title: "Schicht 7: HTTP GET Request",
                desc: "Der Webbrowser möchte die Webseite 'https://prilchen.de' abrufen. Das Protokoll HTTP (meist Version 1.1, 2 oder 3) formuliert eine Anfrage an den Webserver.",
                details: {
                    "Protokoll": "HTTP",
                    "Aktion": "GET / HTTP/1.1",
                    "Host": "prilchen.de"
                },
                packetLabel: "HTTP",
                packetColor: "color-7"
            },
            {
                number: 6,
                name: "Darstellungsschicht (Presentation)",
                title: "Schicht 6: TLS Verschlüsselung",
                desc: "Da die Webseite über HTTPS (sicher) aufgerufen wird, müssen die Daten verschlüsselt werden (TLS). Der HTTP-Klartext wird in unverständlichen Ciphertext umgewandelt.",
                details: {
                    "Verschlüsselung": "TLS 1.3 (AES-256)",
                    "Format": "Ciphertext",
                    "Status": "Encrypted Payload"
                },
                packetLabel: "TLS",
                packetColor: "color-6"
            },
            {
                number: 5,
                name: "Sitzungsschicht (Session)",
                title: "Schicht 5: TLS-Sitzungsverwaltung",
                desc: "Die sichere Sitzung mit dem Server von prilchen.de wird etabliert und aufrechterhalten. Eine TLS Session-ID wird generiert, um spätere Verbindungen zu beschleunigen (Session Resumption).",
                details: {
                    "Schicht-ID (Session)": "0xF9A2B4C1",
                    "Status": "TLS Handshake OK",
                    "Keep-Alive": "Aktiv"
                },
                packetLabel: "SESS",
                packetColor: "color-5"
            },
            {
                number: 4,
                name: "Transportschicht (Transport)",
                title: "Schicht 4: TCP Segmentierung",
                desc: "Der verschlüsselte Datenstrom wird in Segmente unterteilt. Ein TCP-Header wird angehängt. Der Ziel-Port für sicheren Web-Traffic (HTTPS) ist immer Port 443.",
                details: {
                    "Protokoll": "TCP",
                    "Source-Port": "60123 (Dynamisch)",
                    "Dest-Port": "443 (HTTPS)",
                    "Sequenz-Nr": "54321"
                },
                packetLabel: "TCP",
                packetColor: "color-4"
            },
            {
                number: 3,
                name: "Vermittlungsschicht (Network)",
                title: "Schicht 3: IP Adressierung",
                desc: "Das TCP-Segment wird in ein IP-Paket verpackt. Der Browser hat zuvor über DNS die IP-Adresse von prilchen.de ermittelt und trägt diese als Ziel-IP ein.",
                details: {
                    "Protokoll": "IPv4 / IPv6",
                    "Source-IP": "192.168.1.10 (Lokal)",
                    "Dest-IP": "85.13.149.33 (prilchen.de)",
                    "TTL": "128"
                },
                packetLabel: "IP",
                packetColor: "color-3"
            },
            {
                number: 2,
                name: "Sicherungsschicht (Data Link)",
                title: "Schicht 2: MAC Adressierung",
                desc: "Das IP-Paket wird in einen Frame (z.B. Ethernet oder WLAN) verpackt. Die MAC-Adresse deines lokalen Routers (Standardgateway) wird als Ziel-MAC eingetragen, damit das Paket dein Heimnetzwerk verlassen kann.",
                details: {
                    "Protokoll": "Ethernet II / WLAN",
                    "Source-MAC": "AA:BB:CC:DD:EE:FF",
                    "Dest-MAC": "99:88:77:66:55:44 (Router)",
                    "FCS (Trailer)": "0x4C5D6E7F"
                },
                packetLabel: "MAC",
                packetColor: "color-2"
            },
            {
                number: 1,
                name: "Bitübertragungsschicht (Physical)",
                title: "Schicht 1: Binäre Übertragung",
                desc: "Der gesamte Frame wird in eine Folge von Nullen und Einsen (Bits) umgewandelt und als physikalisches Signal (z.B. Funkwellen im WLAN oder elektrische Impulse im Netzwerkkabel) zum Router gesendet.",
                details: {
                    "Medium": "WLAN (Radio) / Kupfer",
                    "Signal": "Modulierte Welle",
                    "Binäre Werte": "10101010 11001100 00110011..."
                },
                packetLabel: "BITS",
                packetColor: "color-1"
            }
        ];

        let currentStep = 0;

        function init() {
            renderSidebar();
            updateView();
        }

        function renderSidebar() {
            const sidebar = document.getElementById('sidebar');
            sidebar.innerHTML = '';
            layers.forEach((layer, index) => {
                const btn = document.createElement('div');
                btn.className = `layer-btn ${index === currentStep ? 'active' : ''}`;
                btn.onclick = () => {
                    currentStep = index;
                    updateView();
                };
                btn.innerHTML = `
                    <div class="layer-number">Schicht ${layer.number}</div>
                    <div class="layer-name">${layer.name}</div>
                `;
                sidebar.appendChild(btn);
            });
        }

        function updateView() {
            // Update sidebar active state
            document.querySelectorAll('.layer-btn').forEach((btn, idx) => {
                btn.className = `layer-btn ${idx === currentStep ? 'active' : ''}`;
            });

            const layer = layers[currentStep];

            // Update info panel
            let detailsHtml = '';
            for (const [key, value] of Object.entries(layer.details)) {
                detailsHtml += `
                    <div class="detail-item">
                        <div class="detail-label">${key}</div>
                        <div class="detail-value">${value}</div>
                    </div>
                `;
            }

            const infoPanel = document.getElementById('infoPanel');
            infoPanel.innerHTML = `
                <h2>${layer.title}</h2>
                <p>${layer.desc}</p>
                <div class="detail-grid">
                    ${detailsHtml}
                </div>
            `;

            // Update packet visualization
            const packetContainer = document.getElementById('packetContainer');
            
            if (layer.number === 1) {
                // Physical layer - show binary stream
                packetContainer.innerHTML = `
                    <div class="binary-stream pulse-text">
                        10101010 11001100 00110011 11110000<br>
                        00001111 10101010 01010101 11001100<br>
                        11111111 00000000 10101010 10101010<br>
                        <br>... SIGNAL WIRD ÜBERTRAGEN ...
                    </div>
                `;
            } else {
                // Show stacked headers
                packetContainer.innerHTML = '';
                
                // Construct the packet from currentStep down to 0
                for (let i = currentStep; i >= 0; i--) {
                    const l = layers[i];
                    const chunk = document.createElement('div');
                    chunk.className = `packet-chunk ${l.packetColor}`;
                    
                    // Simple animation delay for the newest layer added
                    if (i === currentStep && currentStep > 0) {
                        chunk.style.animation = 'dropIn 0.4s forwards cubic-bezier(0.175, 0.885, 0.32, 1.275)';
                    } else {
                        chunk.style.opacity = '1';
                        chunk.style.transform = 'translateY(0)';
                        chunk.style.animation = 'none';
                    }

                    chunk.innerHTML = `
                        <div style="font-size: 0.75rem; margin-bottom: 8px; opacity: 0.8;">Schicht ${l.number}</div>
                        <div>${l.packetLabel}</div>
                    `;
                    packetContainer.appendChild(chunk);
                }
                
                // Add Trailer if Data Link Layer (Schicht 2)
                if (layer.number === 2) {
                     const trailer = document.createElement('div');
                     trailer.className = `packet-chunk color-2`;
                     trailer.style.animation = 'dropIn 0.4s forwards cubic-bezier(0.175, 0.885, 0.32, 1.275)';
                     trailer.innerHTML = `
                        <div style="font-size: 0.75rem; margin-bottom: 8px; opacity: 0.8;">Trailer</div>
                        <div>FCS</div>
                     `;
                     packetContainer.appendChild(trailer);
                }
            }

            // Update buttons
            document.getElementById('prevBtn').disabled = currentStep === 0;
            document.getElementById('nextBtn').disabled = currentStep === layers.length - 1;
            
            if (currentStep === layers.length - 1) {
                document.getElementById('nextBtn').innerText = "Abruf gestartet";
            } else {
                document.getElementById('nextBtn').innerText = "Weiter (Nächste Schicht)";
            }
        }

        function nextLayer() {
            if (currentStep < layers.length - 1) {
                currentStep++;
                updateView();
            }
        }

        function prevLayer() {
            if (currentStep > 0) {
                currentStep--;
                updateView();
            }
        }

        // Initialize
        init();
    </script>
</body>
</html>


Ergebnis:

🔍 Live‑Ansicht öffnen

Jede einzelne Schichten wird nun näher beschrieben. Super anwendbar für Ausbildung und interessierte!


Fazit:

Die Seite zeigt, wie Antigravity schnell funktionale Windows‑Tools und technische Visualisierungen erzeugt – von COM‑Port‑Monitoring über Update‑Checker bis hin zu OSI‑Schicht‑Darstellungen. Wenig aufwand und überzeugendes Ergebnis.
Für mich ein echter Mehrwert.

Youtube Video:

, ,
Datenschutz-Übersicht

Diese Website verwendet Cookies, damit wir dir die bestmögliche Benutzerkomfort bieten können. Cookie-Informationen werden in deinem Browser gespeichert und führen Funktionen aus, wie das Wiedererkennen von dir, wenn du auf unsere Website zurückkehrst und hilft uns zu verstehen, welche Abschnitte der Website für dich am interessantesten und nützlichsten sind.