Restore complete control layout and window interaction
This commit is contained in:
@@ -96,7 +96,7 @@ $$('dialog').forEach(d=>{
|
|||||||
if(e.clientX<r.left||e.clientX>r.right||e.clientY<r.top||e.clientY>r.bottom)closeModal(d);
|
if(e.clientX<r.left||e.clientX>r.right||e.clientY<r.top||e.clientY>r.bottom)closeModal(d);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$('#window-min').onclick=()=>api('minimize_window');$('#window-max').onclick=async()=>{const max=await api('toggle_maximize_window');document.body.classList.toggle('maximized',!!max)};$('#window-close').onclick=()=>{if(document.body.classList.contains('window-exiting'))return;document.body.classList.add('window-exiting');setTimeout(()=>api('close_window'),360)};$('.topbar').ondblclick=async e=>{if(!e.target.closest('button,select,input')){const max=await api('toggle_maximize_window');document.body.classList.toggle('maximized',!!max)}};
|
$('#window-min').onclick=()=>api('minimize_window');$('#window-max').onclick=async()=>{const max=await api('toggle_maximize_window');document.body.classList.toggle('maximized',!!max)};$('#window-close').onclick=()=>{if(document.body.classList.contains('window-exiting'))return;document.body.classList.add('window-exiting');setTimeout(()=>api('close_window'),360)};const windowBar=$('.workspace-topbar');if(windowBar)windowBar.ondblclick=async e=>{if(!e.target.closest('button,select,input')){const max=await api('toggle_maximize_window');document.body.classList.toggle('maximized',!!max)}};
|
||||||
const modelInput=$('#model-input'), modelButton=$('#load-model'), rovStage=$('.rov-stage');
|
const modelInput=$('#model-input'), modelButton=$('#load-model'), rovStage=$('.rov-stage');
|
||||||
|
|
||||||
async function handleStlFile(file){
|
async function handleStlFile(file){
|
||||||
|
|||||||
+124
-56
@@ -4,92 +4,160 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Sub Control</title>
|
<title>Sub Control</title>
|
||||||
<link rel="stylesheet" href="styles.css?v=20260826-2">
|
<link rel="stylesheet" href="styles.css?v=20260826-stage2-fix1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main class="app-shell">
|
<main class="app-shell">
|
||||||
<aside class="vehicle-rail">
|
|
||||||
<header class="brand pywebview-drag-region">
|
|
||||||
<span>Sub Control</span>
|
|
||||||
</header>
|
|
||||||
<div class="eyebrow">live vehicle</div>
|
|
||||||
<div class="rov-stage" aria-label="Top view of three-thruster ROV">
|
|
||||||
<div class="water-ring r1"></div><div class="water-ring r2"></div>
|
|
||||||
<canvas id="model-canvas" aria-label="Interactive STL vehicle model"></canvas>
|
|
||||||
<div id="model-status" class="model-status">Initializing…</div>
|
|
||||||
<div class="model-tools">
|
|
||||||
<button id="load-model">Load STL</button>
|
|
||||||
<input id="model-input" type="file" accept=".stl,.STL" hidden>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="vehicle-name"><small>vehicle</small><strong>SimpleROV-3</strong><span>3 thrusters • differential drive</span></div>
|
|
||||||
<div class="rail-bottom">
|
|
||||||
<button class="battery-card ghost" data-open="battery-modal">
|
|
||||||
<span><small>Battery</small><strong id="battery-title">—</strong></span><i id="battery-percent">waiting</i>
|
|
||||||
<b><em id="battery-fill"></em></b>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<section class="workspace">
|
<!-- ── DECK (Connection, 3D Model, Battery, Deck Controls, Status) ── -->
|
||||||
<header class="topbar">
|
<aside class="sidebar-deck pywebview-drag-region" aria-label="Vehicle Deck and Status">
|
||||||
<div class="connection">
|
<!-- Connection Controls -->
|
||||||
|
<div class="deck-connection">
|
||||||
|
<div class="port-row">
|
||||||
<select id="port" aria-label="Serial port">
|
<select id="port" aria-label="Serial port">
|
||||||
<option value="demo" selected>Demo Mode (no hardware)</option>
|
<option value="demo" selected>Demo Mode (no hardware)</option>
|
||||||
</select>
|
</select>
|
||||||
<button id="refresh" class="icon-button" title="Refresh ports" aria-label="Refresh ports"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 7v5h-5M4 17v-5h5M18.5 9A7 7 0 0 0 6.8 6.8L4 10m16 4-2.8 3.2A7 7 0 0 1 5.5 15"/></svg></button>
|
<button id="refresh" class="icon-button" title="Refresh ports" aria-label="Refresh ports">
|
||||||
<button class="icon-button" data-open="link-modal" title="Link settings" aria-label="Link settings"><svg class="dots-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="5" cy="12" r="1.25"/><circle cx="12" cy="12" r="1.25"/><circle cx="19" cy="12" r="1.25"/></svg></button>
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 7v5h-5M4 17v-5h5M18.5 9A7 7 0 0 0 6.8 6.8L4 10m16 4-2.8 3.2A7 7 0 0 1 5.5 15"/></svg>
|
||||||
<button id="connect" class="button connect-button" aria-label="Toggle connection"><span class="btn-spinner" aria-hidden="true"></span><span class="btn-label">Connect</span></button>
|
</button>
|
||||||
|
<button class="icon-button" data-open="link-modal" title="Link settings" aria-label="Link settings">
|
||||||
|
<svg class="dots-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="5" cy="12" r="1.25"/><circle cx="12" cy="12" r="1.25"/><circle cx="19" cy="12" r="1.25"/></svg>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="topbar-spacer pywebview-drag-region"></div>
|
<button id="connect" class="button connect-button" aria-label="Toggle connection">
|
||||||
|
<span class="btn-spinner" aria-hidden="true"></span>
|
||||||
|
<span class="btn-label">Connect</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Telemetry / Status Zone -->
|
||||||
|
<section class="telemetry-zone" aria-label="Vehicle Telemetry and Status">
|
||||||
|
<div class="deck-card model-card">
|
||||||
|
<div class="eyebrow">live vehicle</div>
|
||||||
|
<div class="rov-stage" aria-label="Top view of three-thruster ROV">
|
||||||
|
<div class="water-ring r1"></div><div class="water-ring r2"></div>
|
||||||
|
<canvas id="model-canvas" aria-label="Interactive STL vehicle model"></canvas>
|
||||||
|
<div id="model-status" class="model-status">Initializing…</div>
|
||||||
|
<div class="model-tools">
|
||||||
|
<button id="load-model">Load STL</button>
|
||||||
|
<input id="model-input" type="file" accept=".stl,.STL" hidden>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="vehicle-name">
|
||||||
|
<small>vehicle</small>
|
||||||
|
<strong>SimpleROV-3</strong>
|
||||||
|
<span>3 thrusters • differential drive</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Battery Status -->
|
||||||
|
<button class="battery-card ghost" data-open="battery-modal">
|
||||||
|
<span><small>Battery</small><strong id="battery-title">—</strong></span><i id="battery-percent">waiting</i>
|
||||||
|
<b><em id="battery-fill"></em></b>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="deck-status-bar">
|
||||||
|
<div class="status-line">
|
||||||
|
<span id="status-dot" class="status-dot"></span>
|
||||||
|
<p id="status-text">Disconnected — select a port and connect</p>
|
||||||
|
<a id="logs" class="logs-link" title="Open session logs" aria-label="Open session logs">logs</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Output Limit & Motor Setup -->
|
||||||
|
<div class="deck-card controls-deck">
|
||||||
|
<label class="output-limit">
|
||||||
|
<span>output limit <b id="gain-value">20%</b></span>
|
||||||
|
<div class="range-pill">
|
||||||
|
<button id="gain-down" type="button" aria-label="Decrease output limit">‹</button>
|
||||||
|
<input id="gain" type="range" min="5" max="50" value="20">
|
||||||
|
<button id="gain-up" type="button" aria-label="Increase output limit">›</button>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<button class="button subtle config-btn" data-open="motors-modal">Motor Setup</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- ── PRIMARY CONTROL ZONE ── -->
|
||||||
|
<section class="controls-workspace" aria-label="Primary Vehicle Controls">
|
||||||
|
<!-- Frameless Drag & Window Controls Header -->
|
||||||
|
<div class="workspace-topbar">
|
||||||
|
<div class="workspace-drag-area pywebview-drag-region"></div>
|
||||||
<div class="window-actions">
|
<div class="window-actions">
|
||||||
<button id="window-min" aria-label="Minimize" title="Minimize"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"/></svg></button>
|
<button id="window-min" aria-label="Minimize" title="Minimize"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"/></svg></button>
|
||||||
<button id="window-max" aria-label="Maximize" title="Maximize"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="5" y="5" width="14" height="14" rx="1.5"/></svg></button>
|
<button id="window-max" aria-label="Maximize" title="Maximize"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="5" y="5" width="14" height="14" rx="1.5"/></svg></button>
|
||||||
<button id="window-close" class="window-close" aria-label="Close" title="Close"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg></button>
|
<button id="window-close" class="window-close" aria-label="Close" title="Close"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg></button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</div>
|
||||||
|
|
||||||
<section class="drive-grid">
|
<section class="primary-control-zone" aria-label="Primary Control Zone">
|
||||||
|
<!-- Reserved Engage / Disarm row -->
|
||||||
|
<div class="engage-header-zone">
|
||||||
|
<span class="zone-label">vehicle control</span>
|
||||||
|
<button id="engage" class="button subtle engage-button" aria-label="Engage" title="Engage (F2)">
|
||||||
|
<span class="arm-copy">
|
||||||
|
<span id="engage-label" class="arm-label">Engage</span>
|
||||||
|
<span class="arm-holding-label">Engaging…</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Main Joysticks & Thrust Control Grid -->
|
||||||
|
<div class="controls-grid">
|
||||||
|
<!-- Steering / Throttle Joystick -->
|
||||||
<article class="control-card steer-card">
|
<article class="control-card steer-card">
|
||||||
<header><div><small>steer + throttle</small><strong>differential drive</strong></div><span class="key-hint">w a s d</span></header>
|
<header>
|
||||||
<div id="joystick" class="joystick" touch-action="none">
|
<div><small>steer + throttle</small><strong>differential drive</strong></div>
|
||||||
<canvas id="joystick-canvas" aria-label="3D Interactive Spherical Joystick"></canvas>
|
<span class="key-hint">w a s d</span>
|
||||||
|
</header>
|
||||||
|
<div class="joystick-pad">
|
||||||
|
<div id="joystick" class="joystick" touch-action="none">
|
||||||
|
<canvas id="joystick-canvas" aria-label="3D Interactive Spherical Joystick"></canvas>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer><span class="steer-readout-left">surge <b id="surge-readout">0</b></span><span class="steer-readout-right"><b id="yaw-readout">0</b> turn</span></footer>
|
<footer>
|
||||||
|
<span class="steer-readout-left">surge <b id="surge-readout">—</b></span>
|
||||||
|
<span class="steer-readout-right"><b id="yaw-readout">—</b> turn</span>
|
||||||
|
</footer>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
<!-- Vertical Thruster Slider -->
|
||||||
<article class="control-card vertical-card">
|
<article class="control-card vertical-card">
|
||||||
<header><div><small>vertical</small><strong>center thruster</strong></div><span class="key-hint">r / f</span></header>
|
<header>
|
||||||
<div class="vertical-control">
|
<div><small>vertical</small><strong>center thruster</strong></div>
|
||||||
<span>rise</span>
|
<span class="key-hint">r / f</span>
|
||||||
<div id="heave" class="heave-track" touch-action="none"><div class="heave-center"></div><div id="heave-fill" class="heave-fill"></div><div id="heave-knob" class="heave-knob"></div></div>
|
</header>
|
||||||
<span>dive</span>
|
<div class="heave-pad">
|
||||||
|
<div class="vertical-control">
|
||||||
|
<span>rise</span>
|
||||||
|
<div id="heave" class="heave-track" touch-action="none">
|
||||||
|
<div class="heave-center"></div>
|
||||||
|
<div id="heave-fill" class="heave-fill"></div>
|
||||||
|
<div id="heave-knob" class="heave-knob"></div>
|
||||||
|
</div>
|
||||||
|
<span>dive</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer><span><b id="heave-readout">0</b> vertical</span></footer>
|
<footer>
|
||||||
|
<span><b id="heave-readout">—</b> vertical</span>
|
||||||
|
</footer>
|
||||||
</article>
|
</article>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="bottom-actions">
|
|
||||||
<section class="command-bar">
|
|
||||||
<div class="commands">
|
|
||||||
<button id="engage" class="button subtle" aria-label="Engage" title="Engage"><span class="arm-copy"><span id="engage-label" class="arm-label">Engage</span><span class="arm-holding-label">Engaging…</span></span></button>
|
|
||||||
</div>
|
|
||||||
<label class="output-limit"><span>output limit <b id="gain-value">20%</b></span><div class="range-pill"><button id="gain-down" type="button" aria-label="Decrease output limit">‹</button><input id="gain" type="range" min="5" max="50" value="20"><button id="gain-up" type="button" aria-label="Increase output limit">›</button></div></label>
|
|
||||||
<button class="button subtle" data-open="motors-modal">Motor Setup</button>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
<div class="status-line"><span id="status-dot" class="status-dot"></span><p id="status-text">Disconnected — select a port and connect</p><a id="logs" class="logs-link" title="Open session logs" aria-label="Open session logs">logs</a></div>
|
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="toast" class="toast"></div>
|
<div id="toast" class="toast"></div>
|
||||||
|
|
||||||
|
<!-- Modals -->
|
||||||
<dialog id="link-modal" class="modal"><button class="modal-close" data-close>×</button><small>connection</small><h2>Link Settings</h2><div class="form-grid two"><label><span>Baud rate</span><select id="baud"><option>9600</option><option>19200</option><option>38400</option><option selected>57600</option><option>115200</option></select></label><label><span>Control rate</span><select id="rate"><option>10</option><option selected>20</option></select></label></div><div style="margin-top:14px;"><label style="display:flex;flex-direction:column;gap:6px;"><span style="font-size:11px;font-weight:500;color:var(--muted)">Custom Port / Endpoint</span><div style="display:flex;gap:8px;"><input id="custom-port-input" placeholder="e.g. COM3 or tcp:127.0.0.1:5760" style="flex:1;height:32px;padding:0 12px;font-size:11px;"><button type="button" id="custom-port-btn" class="button subtle" style="white-space:nowrap;">Use Port</button></div></label></div><p class="note" style="margin-top:14px;">20 Hz is recommended for smooth intermediate commands on this link.</p></dialog>
|
<dialog id="link-modal" class="modal"><button class="modal-close" data-close>×</button><small>connection</small><h2>Link Settings</h2><div class="form-grid two"><label><span>Baud rate</span><select id="baud"><option>9600</option><option>19200</option><option>38400</option><option selected>57600</option><option>115200</option></select></label><label><span>Control rate</span><select id="rate"><option>10</option><option selected>20</option></select></label></div><div style="margin-top:14px;"><label style="display:flex;flex-direction:column;gap:6px;"><span style="font-size:11px;font-weight:500;color:var(--muted)">Custom Port / Endpoint</span><div style="display:flex;gap:8px;"><input id="custom-port-input" placeholder="e.g. COM3 or tcp:127.0.0.1:5760" style="flex:1;height:32px;padding:0 12px;font-size:11px;"><button type="button" id="custom-port-btn" class="button subtle" style="white-space:nowrap;">Use Port</button></div></label></div><p class="note" style="margin-top:14px;">20 Hz is recommended for smooth intermediate commands on this link.</p></dialog>
|
||||||
<dialog id="battery-modal" class="modal"><button class="modal-close" data-close>×</button><small>Power</small><h2>Battery Settings</h2><form id="battery-form" class="form-grid three"><label><span>Cells (S)</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="cells" type="number" min="1" step="1"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>Capacity (mAh)</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="capacity_mah" type="number" min="1" step="50"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>C rating</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="c_rating" type="number" min="1" step=".1"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>Full V / cell</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="full_v_per_cell" type="number" step=".01"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>Empty V / cell</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="empty_v_per_cell" type="number" step=".01"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>Warning %</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="warning_percent" type="number" min="0" max="100" step="1"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><div class="modal-actions wide"><button type="button" class="button subtle" data-close>Cancel</button><button class="button connect-button">Save Settings</button></div></form></dialog>
|
<dialog id="battery-modal" class="modal"><button class="modal-close" data-close>×</button><small>Power</small><h2>Battery Settings</h2><form id="battery-form" class="form-grid three"><label><span>Cells (S)</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="cells" type="number" min="1" step="1"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>Capacity (mAh)</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="capacity_mah" type="number" min="1" step="50"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>C rating</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="c_rating" type="number" min="1" step=".1"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>Full V / cell</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="full_v_per_cell" type="number" step=".01"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>Empty V / cell</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="empty_v_per_cell" type="number" step=".01"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>Warning %</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="warning_percent" type="number" min="0" max="100" step="1"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><div class="modal-actions wide"><button type="button" class="button subtle" data-close>Cancel</button><button class="button connect-button">Save Settings</button></div></form></dialog>
|
||||||
<dialog id="motors-modal" class="modal motor-modal"><button class="modal-close" data-close>×</button><small>configuration</small><h2>Motor Outputs</h2><p class="note">Function and direction are read one parameter at a time to protect the low-bandwidth link.</p><div class="motor-head"><span>Output</span><span>Function</span><span>Direction</span><span></span></div><div id="motor-list" class="motor-list"></div></dialog>
|
<dialog id="motors-modal" class="modal motor-modal"><button class="modal-close" data-close>×</button><small>configuration</small><h2>Motor Outputs</h2><p class="note">Function and direction are read one parameter at a time to protect the low-bandwidth link.</p><div class="motor-head"><span>Output</span><span>Function</span><span>Direction</span><span></span></div><div id="motor-list" class="motor-list"></div></dialog>
|
||||||
|
|
||||||
<script src="model-viewer.js?v=20260826-2"></script>
|
<script src="model-viewer.js?v=20260826-stage2-fix1"></script>
|
||||||
<script src="joystick-3d.js?v=20260826-2"></script>
|
<script src="joystick-3d.js?v=20260826-stage2-fix1"></script>
|
||||||
<script src="app.js?v=20260826-2"></script>
|
<script src="app.js?v=20260826-stage2-fix1"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+560
-634
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user