Files
Strix/webui/web/index.html
T
eduard256 8bf92e6598 Add mock mode for web UI development and testing
- Add mock data module with simulated camera search and stream discovery
- Enable mock mode via ?mock=true URL parameter
- Show MOCK MODE indicator when enabled
- Remove statistics cards from discovery screen, keep only progress bar
- Mock mode works independently from Go backend for easier UI testing
2025-11-21 22:40:38 +03:00

345 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="theme-color" content="#0a0a0f">
<title>Strix - Camera Stream Discovery</title>
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div id="app">
<!-- Screen 1: Initial Address Input -->
<div id="screen-address" class="screen active">
<div class="container">
<div class="hero">
<svg class="logo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="800px" width="800px" version="1.1" id="Layer_1" viewBox="0 0 512.001 512.001" xml:space="preserve">
<g>
<path style="fill:#7E57C2;" d="M124.477,378.183L9.347,495.779c21.628,21.628,56.695,21.628,78.324,0L119,464.45 c21.628,21.628,56.696,21.628,78.324,0l28.375-28.375C187.373,426.203,151.825,405.106,124.477,378.183z"/>
<path style="fill:#7E57C2;" d="M447.27,55.383h-55.383V177.22c0.002-40.997,22.277-76.788,55.383-95.939 c16.293-9.425,35.207-14.822,55.383-14.822c0-6.982,0-11.077,0-11.077V0C472.065,0,447.27,24.796,447.27,55.383z"/>
</g>
<path style="fill:#9575CD;" d="M336.504,55.383C336.504,24.796,311.708,0,281.121,0v66.46c20.176,0,39.091,5.397,55.383,14.822 c33.107,19.153,55.383,54.946,55.383,95.945V55.383H336.504z"/>
<path style="fill:#E8E0F5;" d="M391.887,209.772v-27.116c0-3.312,0-5.432,0-5.432c0-40.997-22.276-76.791-55.383-95.942 c-16.293-9.425-35.207-14.822-55.383-14.822v155.073C311.213,191.443,357.554,187.532,391.887,209.772z M314.351,143.996 c0-12.234,9.918-22.153,22.153-22.153s22.153,9.919,22.153,22.153c0,12.235-9.918,22.153-22.153,22.153 S314.351,156.231,314.351,143.996z"/>
<path style="fill:#D1C4E9;" d="M391.887,177.221v32.551c5.151,3.336,10.037,7.246,14.55,11.76h96.216V66.46 c-20.176,0-39.091,5.397-55.383,14.822C414.164,100.434,391.888,136.225,391.887,177.221z M469.423,143.996 c0,12.235-9.918,22.153-22.153,22.153s-22.153-9.918-22.153-22.153c0-12.234,9.918-22.153,22.153-22.153 C459.504,121.843,469.423,131.762,469.423,143.996z"/>
<path style="fill:#9575CD;" d="M281.121,221.532l-10.2,10.2L281.121,221.532z"/>
<path style="fill:#B39DDB;" d="M406.438,221.533c34.606,34.606,34.606,90.712,0,125.319c-69.21,69.21-181.422,69.21-250.633,0.002 l-31.329,31.33c27.348,26.923,62.896,48.02,101.221,57.892c17.714,4.562,36.285,6.989,55.423,6.989 c122.349,0,221.532-99.182,221.532-221.531C502.653,221.533,406.437,221.532,406.438,221.533z"/>
<path style="fill:#9575CD;" d="M281.121,221.532l125.318,125.319c34.606-34.606,34.606-90.713,0-125.319 c-4.515-4.514-9.401-8.425-14.551-11.761C357.554,187.532,311.213,191.443,281.121,221.532z"/>
<path style="fill:#7E57C2;" d="M406.438,346.851L281.12,221.533l-10.199,10.2L155.802,346.851 C225.017,416.062,337.228,416.061,406.438,346.851z"/>
<circle style="fill:#9575CD;" cx="336.507" cy="143.996" r="22.153"/>
<circle style="fill:#7E57C2;" cx="447.274" cy="143.996" r="22.153"/>
</svg>
<h1 class="title">STRIX</h1>
<p class="subtitle">Camera Stream Discovery</p>
</div>
<div class="form-group">
<label for="network-address" class="label">Network Address</label>
<input
type="text"
id="network-address"
class="input input-large"
placeholder="192.168.1.100"
autocomplete="off"
spellcheck="false"
>
<p class="hint">IP, hostname or full stream URL</p>
</div>
<button id="btn-check-address" class="btn btn-primary btn-large">
Check Address
</button>
<div class="examples">
<p class="examples-title">Examples</p>
<ul class="examples-list">
<li>192.168.1.100</li>
<li>camera.local</li>
<li>rtsp://user:pass@192.168.1.100/stream</li>
</ul>
</div>
</div>
</div>
<!-- Screen 2: Configuration Form -->
<div id="screen-config" class="screen">
<div class="container">
<button id="btn-back-to-address" class="btn-back">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M12 4L6 10l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
Back
</button>
<h2 class="screen-title">Camera Configuration</h2>
<div class="form-group">
<label for="address-validated" class="label">Network Address</label>
<div class="input-validated">
<input
type="text"
id="address-validated"
class="input"
readonly
>
<svg class="icon-check" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M4 10l4 4 8-8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</div>
</div>
<div class="form-group">
<label for="camera-model" class="label">Camera Model <span class="optional">(optional)</span></label>
<div class="autocomplete-wrapper">
<input
type="text"
id="camera-model"
class="input"
placeholder="Start typing..."
autocomplete="off"
spellcheck="false"
>
<div id="autocomplete-dropdown" class="autocomplete-dropdown hidden"></div>
</div>
<p id="model-disabled-hint" class="hint hidden">Detected from URL, continue below</p>
</div>
<div class="form-group">
<label for="username" class="label">Username</label>
<input
type="text"
id="username"
class="input"
placeholder="admin"
autocomplete="off"
>
</div>
<div class="form-group">
<label for="password" class="label">Password</label>
<div class="input-password-wrapper">
<input
type="password"
id="password"
class="input"
placeholder="••••••••"
autocomplete="off"
>
<button type="button" class="btn-toggle-password" aria-label="Toggle password visibility">
<svg class="icon-eye" width="20" height="20" viewBox="0 0 20 20" fill="none">
<circle cx="10" cy="10" r="3" stroke="currentColor" stroke-width="1.5"/>
<path d="M2 10c1.5-4 4-6.5 8-6.5s6.5 2.5 8 6.5c-1.5 4-4 6.5-8 6.5S3.5 14 2 10z" stroke="currentColor" stroke-width="1.5"/>
<circle cx="10" cy="10" r="1.5" fill="currentColor"/>
</svg>
</button>
</div>
</div>
<details class="advanced-section">
<summary class="advanced-toggle">Advanced</summary>
<div class="advanced-content">
<div class="form-group">
<label for="channel" class="label">Channel</label>
<input
type="number"
id="channel"
class="input"
value="0"
min="0"
max="255"
>
</div>
<div class="form-group">
<label class="label">Resolution <span class="optional">(optional)</span></label>
<div class="input-row">
<input
type="number"
id="width"
class="input"
placeholder="Width"
>
<span class="input-separator">×</span>
<input
type="number"
id="height"
class="input"
placeholder="Height"
>
</div>
</div>
<div class="form-group">
<label for="max-streams" class="label">Max Streams</label>
<input
type="number"
id="max-streams"
class="input"
value="10"
min="1"
max="50"
>
</div>
</div>
</details>
<button id="btn-discover" class="btn btn-primary btn-large">
Discover Streams
</button>
</div>
</div>
<!-- Screen 3: Stream Discovery -->
<div id="screen-discovery" class="screen">
<div class="container">
<button id="btn-back-to-config" class="btn-back">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M12 4L6 10l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
Back to Configuration
</button>
<h2 class="screen-title">Discovering Streams</h2>
<div class="progress-container">
<div class="progress-bar">
<div id="progress-fill" class="progress-fill"></div>
</div>
<p id="progress-text" class="progress-text">Starting scan...</p>
</div>
<div id="streams-section" class="streams-section hidden">
<h3 class="section-title">Found Connections</h3>
<div class="carousel-wrapper">
<button id="carousel-prev" class="carousel-arrow carousel-arrow-left" aria-label="Previous stream">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M15 18l-6-6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</button>
<div class="carousel">
<div id="carousel-track" class="carousel-track"></div>
</div>
<button id="carousel-next" class="carousel-arrow carousel-arrow-right" aria-label="Next stream">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</button>
</div>
<div class="carousel-info">
<p id="carousel-counter" class="carousel-counter">Stream 1 of 1</p>
<div id="carousel-dots" class="carousel-dots"></div>
</div>
</div>
</div>
</div>
<!-- Screen 4: Configuration Output -->
<div id="screen-output" class="screen">
<div class="container">
<button id="btn-back-to-streams" class="btn-back">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M12 4L6 10l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
Back to Streams
</button>
<h2 class="screen-title">Stream Configuration</h2>
<div class="stream-selection-container">
<div class="selected-stream-info">
<p class="stream-label">Main Stream</p>
<p id="selected-main-type" class="selected-type"></p>
<p id="selected-main-url" class="selected-url"></p>
</div>
<div id="sub-stream-info" class="selected-stream-info sub-stream hidden">
<p class="stream-label">Sub Stream</p>
<p id="selected-sub-type" class="selected-type"></p>
<p id="selected-sub-url" class="selected-url"></p>
<button id="btn-remove-sub" class="btn-remove-sub">Remove Sub Stream</button>
</div>
</div>
<div class="tabs">
<div class="tabs-scroll">
<button class="tab active" data-tab="url">URL</button>
<button class="tab" data-tab="go2rtc">Go2RTC</button>
<button class="tab" data-tab="frigate">Frigate</button>
</div>
</div>
<div class="tab-content">
<div class="tab-pane active" data-pane="url">
<pre id="config-url" class="config-code"></pre>
</div>
<div class="tab-pane" data-pane="go2rtc">
<pre id="config-go2rtc" class="config-code"></pre>
</div>
<div class="tab-pane" data-pane="frigate">
<!-- Input section for existing config -->
<div class="frigate-input-section">
<label class="frigate-label">
Your Current Frigate Config
<span class="hint">Paste your existing config.yml or leave the example below</span>
</label>
<textarea
id="existing-frigate-config"
class="textarea frigate-config-input"
rows="12"
placeholder="Paste your existing Frigate config here..."></textarea>
</div>
<!-- Generate button -->
<button id="btn-generate-frigate" class="btn btn-primary btn-generate">
Generate Config
</button>
<!-- Output section (hidden by default) -->
<div id="frigate-output-section" class="frigate-output-section hidden">
<label class="frigate-label">Updated Config (Camera Added)</label>
<pre id="config-frigate" class="config-code"></pre>
</div>
</div>
</div>
<div class="actions">
<button id="btn-copy-config" class="btn btn-secondary">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<rect x="6" y="6" width="10" height="10" rx="1" stroke="currentColor" stroke-width="1.5"/>
<path d="M4 4h10v2H5v9H4V4z" fill="currentColor"/>
</svg>
Copy
</button>
<button id="btn-download-config" class="btn btn-secondary">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M10 3v10m0 0l-4-4m4 4l4-4M4 17h12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
Download
</button>
</div>
<div class="secondary-actions">
<button id="btn-add-sub-stream" class="btn btn-primary">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M10 4v12M4 10h12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
Add Sub Stream
</button>
<button id="btn-new-search" class="btn btn-outline">
Add Another Camera
</button>
</div>
</div>
</div>
</div>
<!-- Toast Notification -->
<div id="toast" class="toast hidden"></div>
<script type="module" src="js/main.js"></script>
</body>
</html>