This document covers the frontend architecture, file structure, and development guidelines for the GGWP Launcher embedded UI.
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
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 | 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 | ✅ |
┌─────────────────┐
│ Application │
│ Startup │
└────────┬────────┘
│
▼
┌─────────────────┐ ┌─────────────────┐
│ Check Remote │────▶│ loader.html │
│ UI Available │ │ (splash screen)│
└────────┬────────┘ └─────────────────┘
│
┌────┴────┐
│ │
▼ ▼
┌────────┐ ┌────────────┐
│ Remote │ │ Local │
│ UI │ │ Embedded │
└────────┘ └─────┬──────┘
│
┌───────┴───────┐
│ │
▼ ▼
┌──────────┐ ┌──────────────┐
│index.html│ │ offline.html │
│ (online) │ │ (no network)│
└──────────┘ └──────────────┘
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 } |
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'
};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;
}┌─────────────────────────────────────────────┐
│ .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 │
└─────────────────────────────────────────────┘
Use data-i18n attribute for automatic translation:
<button data-i18n="button.launch">START GAME</button>Translations are loaded from Rust via GetTranslations command.
// Get translated string
const text = t("status.downloading");
// With parameters
const text = t("ui.error_prefix", { error: errorMessage });resources/locales/en.json- Englishresources/locales/tr.json- Turkishresources/locales/de.json- Germanresources/locales/es.json- Spanishresources/locales/fr.json- Frenchresources/locales/pt.json- Portuguese
In debug builds, the UI supports hot reload:
-
Set environment variable:
$env:GGWP_UI_DEV_DIR = "resources/ui" $env:GGWP_UI_HOT_RELOAD = "1"
-
Run the application in debug mode:
cargo run
-
Edit files in
resources/ui/- changes auto-reload
The titlebar includes a debug button (gear icon) that simulates download progress for testing UI states.
Press F12 in the application window to open WebView2 DevTools for debugging.
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.
# Release build with embedded UI
cargo build --releaseThe UI files are automatically included - no separate build step required.
| Key | Action |
|---|---|
Ctrl+R |
Reload UI |
F5 |
Retry connection (offline mode) |
Escape |
Exit application (offline mode) |
- ✅ Use CSS variables for colors
- ✅ Use
data-i18nfor translatable text - ✅ Handle IPC errors gracefully
- ✅ Show loading states during async operations
- ✅ Use semantic HTML elements
- ✅ Test offline fallback behavior
- ❌ Add external CDN dependencies
- ❌ Use inline styles (use CSS classes)
- ❌ Hardcode strings (use i18n)
- ❌ Ignore IPC timeout handling
- ❌ Block UI during long operations
- Check WebView2 runtime is installed
- Verify files exist in
resources/ui/ - Check console for decryption errors
- Ensure
window.__LAUNCHER__is defined - Check
window.ipc.postMessageis available - Verify bridge.js is loaded in init script
- Check
styles.cssis properly embedded - Verify CSS syntax errors in DevTools
- Ensure CSS variables are defined in
:root
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
- Content Security: All UI content is embedded and encrypted
- URL Validation: External URLs are validated before opening
- IPC Sanitization: All IPC messages are JSON-encoded to prevent injection
- No External Requests: UI doesn't make direct HTTP requests (uses Rust IPC)
| 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 |
/doc/QUICKSTART.md- Getting started guide/resources/bridge.js- IPC bridge implementation/src/ui/webview.rs- Rust WebView integration/src/ui/bridge.rs- IPC command handlers