ostp/ostp-gui/src/index.html

386 lines
18 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OSTP</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" />
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="app-root">
<!-- Eagle watermark — behind everything, every screen -->
<div class="watermark" aria-hidden="true">
<img src="assets/logo.svg" alt="" />
</div>
<!-- ── HOME SCREEN ──────────────────────────────────────── -->
<div id="home-screen" class="screen active">
<header class="topbar">
<div class="brand">
<div class="brand-dot" id="brand-dot"></div>
<span class="brand-name">OSTP</span>
</div>
<div class="topbar-right">
<button id="btn-auto-connect" class="icon-btn" aria-label="Auto" title="Auto-connect">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
</svg>
</button>
<button id="btn-go-settings" class="icon-btn" aria-label="Settings">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
</svg>
</button>
</div>
</header>
<!-- Center stage -->
<main class="stage">
<!-- Orbit rings (animated when connecting/connected) -->
<div class="orbit-wrap" id="orbit-wrap">
<div class="orbit orbit-1"></div>
<div class="orbit orbit-2"></div>
<div class="orbit orbit-3"></div>
<!-- Power button -->
<button id="btn-connect" class="power-btn" aria-label="Connect / Disconnect">
<div class="power-icon">
<svg width="46" height="46" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<path d="M18.36 6.64a9 9 0 1 1-12.73 0"/>
<line x1="12" y1="2" x2="12" y2="12"/>
</svg>
</div>
</button>
</div>
<!-- Status text -->
<div class="status-block">
<div id="status-text" class="status-label">Disconnected</div>
<div id="uptime-text" class="status-sub">Tap to protect your traffic</div>
</div>
<!-- Error banner -->
<div id="error-banner" class="error-banner hidden"></div>
<!-- Connection info (visible when connected) -->
<div id="connection-info" class="connection-info hidden">
<div class="server-badge">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="2" width="20" height="8" rx="2"/>
<rect x="2" y="14" width="20" height="8" rx="2"/>
<line x1="6" y1="6" x2="6.01" y2="6"/>
<line x1="6" y1="18" x2="6.01" y2="18"/>
</svg>
<span id="server-badge-text"></span>
</div>
<!-- Live RTT + speeds -->
<div class="live-stats">
<div class="live-stat">
<span class="live-stat-label">RTT</span>
<span id="live-rtt" class="live-stat-value">--</span>
</div>
<div class="live-stat-sep"></div>
<div class="live-stat">
<span class="live-stat-label"></span>
<span id="live-down-speed" class="live-stat-value">0 B/s</span>
</div>
<div class="live-stat-sep"></div>
<div class="live-stat">
<span class="live-stat-label"></span>
<span id="live-up-speed" class="live-stat-value">0 B/s</span>
</div>
</div>
</div>
</main>
<!-- Total traffic bar -->
<footer class="metrics-bar">
<div class="metric">
<div class="metric-icon down-icon">
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 5v14M19 12l-7 7-7-7"/>
</svg>
</div>
<div class="metric-body">
<span class="metric-label">Download</span>
<span id="metric-down" class="metric-value">0 B</span>
</div>
</div>
<div class="metric-sep"></div>
<div class="metric">
<div class="metric-icon up-icon">
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 19V5M5 12l7-7 7 7"/>
</svg>
</div>
<div class="metric-body">
<span class="metric-label">Upload</span>
<span id="metric-up" class="metric-value">0 B</span>
</div>
</div>
</footer>
</div>
<!-- ── SETTINGS SCREEN ──────────────────────────────────── -->
<div id="settings-screen" class="screen">
<header class="topbar">
<button id="btn-back" class="icon-btn" aria-label="Back">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M19 12H5M12 19l-7-7 7-7"/>
</svg>
</button>
<span class="topbar-title">Profiles</span>
<button id="btn-add-profile" class="icon-btn add-btn" aria-label="Add profile">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="12" y1="5" x2="12" y2="19"/>
<line x1="5" y1="12" x2="19" y2="12"/>
</svg>
</button>
</header>
<div class="settings-body">
<!-- Profile list -->
<div id="profile-list" class="profile-list">
<div id="profile-empty" class="profile-empty">
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="16"/>
<line x1="8" y1="12" x2="16" y2="12"/>
</svg>
<p>No profiles yet.<br/>Tap <strong>+</strong> to add one.</p>
</div>
</div>
<!-- Client settings -->
<div class="section-divider"><span>Client Settings</span></div>
<div class="client-settings-card">
<div class="toggle-row">
<div class="toggle-text">
<span class="toggle-name">TUN Mode</span>
<span class="toggle-hint">Route all system traffic</span>
</div>
<label class="toggle">
<input type="checkbox" id="in-tun-mode" />
<span class="toggle-track"><span class="toggle-thumb"></span></span>
</label>
</div>
<div class="toggle-row sub-row" id="group-kill-switch" style="display:none;">
<div class="toggle-text">
<span class="toggle-name">Kill Switch</span>
<span class="toggle-hint">Block traffic if VPN drops</span>
</div>
<label class="toggle">
<input type="checkbox" id="in-kill-switch" />
<span class="toggle-track"><span class="toggle-thumb"></span></span>
</label>
</div>
<div class="toggle-row">
<div class="toggle-text">
<span class="toggle-name">Multiplexing</span>
<span class="toggle-hint">Multiple streams over one connection</span>
</div>
<label class="toggle">
<input type="checkbox" id="in-mux-mode" />
<span class="toggle-track"><span class="toggle-thumb"></span></span>
</label>
</div>
<div class="inline-field sub-row" id="group-mux-sessions" style="display:none;">
<span class="field-label">Sessions</span>
<input id="in-mux-sessions" class="field-input compact" type="number" placeholder="2" min="1" max="8" />
</div>
<div class="inline-field">
<span class="field-label">MTU</span>
<input id="in-mtu" class="field-input compact" type="number" placeholder="1350" />
</div>
<div class="inline-field">
<span class="field-label">DNS</span>
<input id="in-dns" class="field-input compact" type="text" placeholder="1.1.1.1" />
</div>
<div class="inline-field">
<span class="field-label">Local Proxy</span>
<input id="in-socks" class="field-input compact" type="text" placeholder="127.0.0.1:1088" />
</div>
<div class="section-divider-mini"><span>Exceptions / Routing</span></div>
<div class="field-group" style="padding: 10px 14px; margin-bottom: 0;">
<label class="field-label" for="in-ex-domains">Excluded Domains</label>
<textarea id="in-ex-domains" class="field-input mono" placeholder="google.com, mycompany.internal" rows="2" spellcheck="false"></textarea>
</div>
<div class="field-group" style="padding: 0 14px 10px; margin-bottom: 0;">
<label class="field-label" for="in-ex-ips">Excluded IPs / Subnets</label>
<textarea id="in-ex-ips" class="field-input mono" placeholder="192.168.1.0/24, 10.0.0.1" rows="2" spellcheck="false"></textarea>
</div>
<div class="field-group" style="padding: 0 14px 10px; margin-bottom: 0; border-bottom: 1px solid rgba(255,255,255,0.04);">
<label class="field-label" for="in-ex-procs">Excluded Processes</label>
<textarea id="in-ex-procs" class="field-input mono" placeholder="chrome.exe, spotify.exe" rows="2" spellcheck="false"></textarea>
</div>
<div class="section-divider-mini"><span>Application</span></div>
<div class="toggle-row" style="border-top:none;">
<div class="toggle-text">
<span class="toggle-name">Auto-connect</span>
<span class="toggle-hint">Connect on startup</span>
</div>
<label class="toggle">
<input type="checkbox" id="in-autoconnect" />
<span class="toggle-track"><span class="toggle-thumb"></span></span>
</label>
</div>
<div class="toggle-row" style="border-top:none;">
<div class="toggle-text">
<span class="toggle-name">Launch at Startup</span>
<span class="toggle-hint">Start with Windows</span>
</div>
<label class="toggle">
<input type="checkbox" id="in-launch-startup" />
<span class="toggle-track"><span class="toggle-thumb"></span></span>
</label>
</div>
<div class="toggle-row" style="border-top:none;">
<div class="toggle-text">
<span class="toggle-name">Debug Logs</span>
<span class="toggle-hint">Verbose output to .log file</span>
</div>
<label class="toggle">
<input type="checkbox" id="in-debug" />
<span class="toggle-track"><span class="toggle-thumb"></span></span>
</label>
</div>
</div>
</div>
</div>
<!-- ── ADD PROFILE DROPDOWN ─────────────────────────────── -->
<div id="add-menu" class="add-menu hidden">
<button id="add-from-link" class="add-menu-item">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
From link
</button>
<button id="add-from-clipboard" class="add-menu-item">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
From clipboard
</button>
<button id="add-manually" class="add-menu-item">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
Manually
</button>
</div>
<!-- ── LINK INPUT MODAL ─────────────────────────────────── -->
<div id="link-modal" class="modal-overlay hidden">
<div class="modal-content compact">
<h3 class="modal-title">Paste link</h3>
<div class="field-group">
<input id="link-input" class="field-input mono" type="text" placeholder="ostp://key@host:port" spellcheck="false" />
</div>
<div class="modal-actions">
<button id="btn-link-cancel" class="btn secondary">Cancel</button>
<button id="btn-link-import" class="btn primary">Import</button>
</div>
</div>
</div>
<!-- ── PROFILE EDITOR MODAL ─────────────────────────────── -->
<div id="profile-modal" class="modal-overlay hidden">
<div class="modal-content">
<h3 class="modal-title" id="profile-modal-title">New Profile</h3>
<div class="field-group">
<label class="field-label" for="pm-name">Name</label>
<input id="pm-name" class="field-input" type="text" placeholder="My Server" />
</div>
<div class="field-group">
<label class="field-label" for="pm-server">Server</label>
<input id="pm-server" class="field-input mono" type="text" placeholder="host:port" spellcheck="false" />
</div>
<div class="field-group">
<label class="field-label" for="pm-key">Access Key</label>
<div class="input-wrap">
<input id="pm-key" class="field-input mono has-icon" type="password" placeholder="Secure access key" spellcheck="false" />
<button class="peek-btn" id="btn-peek-pm" tabindex="-1" aria-label="Show key">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
</button>
</div>
</div>
<div class="field-group">
<label class="field-label" for="pm-transport">Transport</label>
<select id="pm-transport" class="field-input">
<option value="udp">UDP (Default)</option>
<option value="uot">TCP (UoT)</option>
</select>
</div>
<div class="modal-actions">
<button id="btn-profile-cancel" class="btn secondary">Cancel</button>
<button id="btn-profile-delete" class="btn danger" style="display:none;">Delete</button>
<button id="btn-profile-save" class="btn primary">Save</button>
</div>
</div>
</div>
<!-- ── SHARE MODAL ──────────────────────────────────────── -->
<div id="share-modal" class="modal-overlay hidden">
<div class="modal-content">
<h3 class="modal-title">Share Profile</h3>
<p class="modal-text">QR generated locally — the key never leaves this device.</p>
<div id="share-qr" class="share-qr"></div>
<input id="share-link" class="field-input mono" type="text" readonly />
<div class="modal-actions">
<button id="btn-share-close" class="btn secondary">Close</button>
<button id="btn-share-copy" class="btn primary">Copy link</button>
</div>
</div>
</div>
<!-- ── WINTUN MODAL ─────────────────────────────────────── -->
<div id="wintun-modal" class="modal-overlay hidden">
<div class="modal-content">
<h3 class="modal-title">Wintun Driver Missing</h3>
<p class="modal-text">TUN mode requires the Wintun network driver.</p>
<ol class="modal-steps">
<li>Download <strong>wintun.zip</strong> from wintun.net</li>
<li>Extract <code>amd64\wintun.dll</code></li>
<li>Place it here: <code id="wintun-install-path">...</code></li>
<li>Restart the connection</li>
</ol>
<div class="modal-actions">
<button id="btn-wintun-cancel" class="btn secondary">Cancel</button>
<a id="btn-wintun-open" href="https://www.wintun.net" target="_blank" class="btn primary">Open wintun.net ↗</a>
</div>
</div>
</div>
<!-- Toast notification -->
<div id="toast" class="toast" role="status" aria-live="polite"></div>
</div>
<script type="module" src="main.js"></script>
</body>
</html>