Skip to content

Latest commit

 

History

History
397 lines (306 loc) · 11.6 KB

File metadata and controls

397 lines (306 loc) · 11.6 KB

GGWP Launcher Frontend UI

This document covers the frontend architecture, file structure, and development guidelines for the GGWP Launcher embedded UI.

Overview

The UI is a single-page application built with vanilla HTML, CSS, and JavaScript. It runs inside a WebView2 (Windows) component and communicates with the Rust backend via an IPC bridge.

Key Design Principles:

  • No external dependencies (no npm, no bundlers)
  • Embedded at compile time via include_crypt! macro
  • Supports hot-reload in debug builds
  • Fully translatable via i18n system
  • Offline-capable with graceful degradation

File Structure

resources/ui/
├── index.html      # Main application UI
├── app.js          # Application logic & state management
├── styles.css      # All styling (CSS variables + components)
├── loader.html     # Splash screen during remote UI loading
├── offline.html    # Fallback UI when offline
├── logo.png        # Application logo (titlebar & branding)
└── icon.ico        # Windows icon file

File Descriptions

File Purpose Size Embedded
index.html Main Launcher interface with titlebar, news slider, changelog, and download controls ~11KB
app.js State management, IPC communication, event handlers, UI updates ~39KB
styles.css Complete styling with CSS variables for theming ~18KB
loader.html Loading splash shown while checking remote UI availability ~6KB
offline.html Full offline fallback UI with retry functionality ~17KB
logo.png PNG logo displayed in titlebar ~9KB
icon.ico Windows application icon ~4KB

Architecture

UI Loading Flow

┌─────────────────┐
│  Application    │
│    Startup      │
└────────┬────────┘
         │
         ▼
┌─────────────────┐     ┌─────────────────┐
│  Check Remote   │────▶│   loader.html   │
│   UI Available  │     │  (splash screen)│
└────────┬────────┘     └─────────────────┘
         │
    ┌────┴────┐
    │         │
    ▼         ▼
┌────────┐  ┌────────────┐
│ Remote │  │   Local    │
│   UI   │  │ Embedded   │
└────────┘  └─────┬──────┘
                  │
         ┌───────┴───────┐
         │               │
         ▼               ▼
   ┌──────────┐   ┌──────────────┐
   │index.html│   │ offline.html │
   │ (online) │   │  (no network)│
   └──────────┘   └──────────────┘

IPC Communication

The UI communicates with Rust via window.__LAUNCHER__:

// Send command to Rust
const result = await window.__LAUNCHER__.invoke("CommandName", { payload });

// Listen for events from Rust
window.__LAUNCHER__.on("EventName", (data) => {
    // Handle event
});

Available Commands:

Command Description
CheckManifest Check for updates
StartDownload Begin downloading files
PauseDownload Pause active download
ResumeDownload Resume paused download
LaunchGame Start the game executable
GetState Get current application state
GetTranslations Load i18n translations
SetLanguage Change UI language
OpenExternalUrl Open URL in browser
GetUiConfig Get UI configuration
GetManifestInfo Get manifest details
CheckConnection Test internet connectivity

Events from Rust:

Event Data
StateChanged { state: string }
DownloadProgress { total_bytes, downloaded_bytes, download_speed, eta_seconds }
DownloadComplete {}
VerificationProgress { current, total, file_name }
Error { message, suggestion?, error_code? }
LanguageChanged { lang, translations }
ManifestInfo { version, maintenance, news, splash }

State Management

The application uses a simple state object in app.js:

const AppState = {
    STARTUP: 'startup',
    READY: 'ready',
    EULA_PENDING: 'eula_pending',
    CHECKING: 'checking_manifest',
    UPDATE_AVAILABLE: 'update_available',
    DOWNLOADING: 'downloading',
    VERIFYING: 'verifying',
    APPLYING: 'patching',
    LAUNCH_READY: 'launch_ready',
    LAUNCHING: 'launching',
    ERROR: 'error'
};

const state = {
    current: AppState.READY,
    isDownloading: false,
    currentSlideIndex: 0,
    currentTab: 'news',
    currentLang: 'en',
    translations: {},
    newsItems: [],
    changelogItems: [],
    links: {},
    serverOnline: false,
    version: '1.0.0'
};

Styling System

CSS Variables

All colors and values are defined as CSS variables for easy theming:

:root {
    /* Backgrounds */
    --bg-primary: #0a0a0f;
    --bg-secondary: #0f0f18;
    --bg-card: #14141f;
    --bg-hover: #1a1a28;
    
    /* Accent Colors */
    --accent-primary: #7c5cff;
    --accent-secondary: #9d7cff;
    
    /* Text */
    --text-primary: #e8e8f0;
    --text-secondary: #7a7a8c;
    --text-muted: #4a4a5c;
    
    /* Status Colors */
    --danger: #ff5c5c;
    --warning: #ffb347;
    --success: #5cdb95;
    
    /* Transitions */
    --transition-fast: 0.15s ease;
    --transition-normal: 0.25s ease;
}

Component Structure

┌─────────────────────────────────────────────┐
│ .titlebar                                    │
│  ├── .titlebar-left (logo + status)         │
│  └── .titlebar-controls (buttons)           │
├─────────────────────────────────────────────┤
│ .content-header                              │
│  ├── .content-tabs (News | Changelog)       │
│  └── .nav-group (external links)            │
├─────────────────────────────────────────────┤
│ .main-content                                │
│  ├── #news-panel                            │
│  │    └── .news-slider                      │
│  └── #changelog-panel                       │
│       └── .changelog-list                   │
├─────────────────────────────────────────────┤
│ .footer                                      │
│  └── .download-row                          │
│       ├── .progress-column                  │
│       └── .action-buttons                   │
└─────────────────────────────────────────────┘

Internationalization (i18n)

Translation Keys

Use data-i18n attribute for automatic translation:

<button data-i18n="button.launch">START GAME</button>

Translations are loaded from Rust via GetTranslations command.

Using Translations in JavaScript

// Get translated string
const text = t("status.downloading");

// With parameters
const text = t("ui.error_prefix", { error: errorMessage });

Available Translation Files

  • resources/locales/en.json - English
  • resources/locales/tr.json - Turkish
  • resources/locales/de.json - German
  • resources/locales/es.json - Spanish
  • resources/locales/fr.json - French
  • resources/locales/pt.json - Portuguese

Development

Hot Reload (Debug Builds)

In debug builds, the UI supports hot reload:

  1. Set environment variable:

    $env:GGWP_UI_DEV_DIR = "resources/ui"
    $env:GGWP_UI_HOT_RELOAD = "1"
  2. Run the application in debug mode:

    cargo run
  3. Edit files in resources/ui/ - changes auto-reload

Debug Button

The titlebar includes a debug button (gear icon) that simulates download progress for testing UI states.

Browser DevTools

Press F12 in the application window to open WebView2 DevTools for debugging.


Build & Embedding

The UI files are embedded into the binary at compile time using the include_crypt! macro:

// In src/ui/webview.rs
static UI_INDEX_HTML: EncryptedFile = include_crypt!("resources/ui/index.html");
static UI_STYLES_CSS: EncryptedFile = include_crypt!("resources/ui/styles.css");
static UI_APP_JS: EncryptedFile = include_crypt!("resources/ui/app.js");
static UI_LOADER_HTML: EncryptedFile = include_crypt!("resources/ui/loader.html");
static UI_OFFLINE_HTML: EncryptedFile = include_crypt!("resources/ui/offline.html");

Files are encrypted at build time and decrypted at runtime for added security.

Building for Production

# Release build with embedded UI
cargo build --release

The UI files are automatically included - no separate build step required.


Keyboard Shortcuts

Key Action
Ctrl+R Reload UI
F5 Retry connection (offline mode)
Escape Exit application (offline mode)

Best Practices

Do's

  • ✅ Use CSS variables for colors
  • ✅ Use data-i18n for translatable text
  • ✅ Handle IPC errors gracefully
  • ✅ Show loading states during async operations
  • ✅ Use semantic HTML elements
  • ✅ Test offline fallback behavior

Don'ts

  • ❌ Add external CDN dependencies
  • ❌ Use inline styles (use CSS classes)
  • ❌ Hardcode strings (use i18n)
  • ❌ Ignore IPC timeout handling
  • ❌ Block UI during long operations

Troubleshooting

UI Not Loading

  1. Check WebView2 runtime is installed
  2. Verify files exist in resources/ui/
  3. Check console for decryption errors

IPC Not Working

  1. Ensure window.__LAUNCHER__ is defined
  2. Check window.ipc.postMessage is available
  3. Verify bridge.js is loaded in init script

Styles Not Applied

  1. Check styles.css is properly embedded
  2. Verify CSS syntax errors in DevTools
  3. Ensure CSS variables are defined in :root

File Dependencies

index.html
├── styles.css (linked)
├── app.js (script)
└── logo.png (image)

loader.html
└── (self-contained, inline styles)

offline.html
└── (self-contained, inline styles)

bridge.js (injected by Rust)
└── Provides window.__LAUNCHER__ API

Security Considerations

  1. Content Security: All UI content is embedded and encrypted
  2. URL Validation: External URLs are validated before opening
  3. IPC Sanitization: All IPC messages are JSON-encoded to prevent injection
  4. No External Requests: UI doesn't make direct HTTP requests (uses Rust IPC)

Version History

Version Changes
1.0.0 Initial release with news slider, changelog, download UI
1.1.0 Added offline fallback, improved i18n support
1.2.0 Production hardening, IPC timeout handling, error boundaries

Related Documentation