diff --git a/hive-ag3nt/assets/screen.html b/hive-ag3nt/assets/screen.html
index 338b5dc..b0dfdc1 100644
--- a/hive-ag3nt/assets/screen.html
+++ b/hive-ag3nt/assets/screen.html
@@ -30,6 +30,12 @@ html, body { height: 100%; background: var(--base); color: var(--text);
}
#toolbar a { color: var(--blue); text-decoration: none; font-size: 0.85rem; }
#toolbar a:hover { text-decoration: underline; }
+.tbtn {
+ padding: 0.15rem 0.5rem; font-size: 0.72rem; font-family: inherit;
+ background: var(--surface0); color: var(--subtext0);
+ border: 1px solid var(--surface1); border-radius: 4px; cursor: pointer;
+}
+.tbtn.active { color: var(--green); border-color: var(--green); }
#status { margin-left: auto; font-size: 0.75rem; color: var(--subtext0); }
#status.connected { color: var(--green); }
#status.error { color: var(--red); }
@@ -50,7 +56,12 @@ html, body { height: 100%; background: var(--base); color: var(--text);
width: 100%; height: calc(100% - 36px); overflow: auto;
background: var(--crust);
}
+/* Fit mode: centre the canvas and let CSS scale it down to the
+ viewport (intrinsic resolution unchanged — see canvas.fit). */
+#canvas-wrap.fit { align-items: center; overflow: hidden; }
canvas { display: block; cursor: default; }
+/* max-* on a replaced element shrinks it preserving aspect ratio. */
+canvas.fit { max-width: 100%; max-height: 100%; }
#msg {
position: fixed; bottom: 1rem; left: 50%; transform: translateX(-50%);
background: var(--surface0); color: var(--yellow); border-radius: 6px;
@@ -64,10 +75,8 @@ canvas { display: block; cursor: default; }
@@ -92,15 +101,36 @@ canvas { display: block; cursor: default; }
const msg = document.getElementById('msg');
const debugLog = document.getElementById('debug-log');
const debugBtn = document.getElementById('debug-toggle');
+ const fitBtn = document.getElementById('fit-toggle');
+ const canvasWrap = document.getElementById('canvas-wrap');
// --- Debug log ---
let debugVisible = false;
debugBtn.addEventListener('click', () => {
debugVisible = !debugVisible;
debugLog.style.display = debugVisible ? 'block' : 'none';
- debugBtn.style.color = debugVisible ? 'var(--green)' : 'var(--subtext0)';
+ debugBtn.classList.toggle('active', debugVisible);
});
+ // --- Fit-to-window toggle ---
+ // Scales the canvas down via CSS so the whole desktop is visible
+ // without scrolling. The canvas's intrinsic resolution is untouched;
+ // only its displayed size changes (see canvas.fit / #canvas-wrap.fit).
+ // Pointer coordinates are rescaled in sendPointer to stay accurate.
+ // The choice is persisted in localStorage; default is fit-on.
+ let fitMode = localStorage.getItem('screen-fit') !== 'off';
+ function applyFitMode() {
+ canvas.classList.toggle('fit', fitMode);
+ canvasWrap.classList.toggle('fit', fitMode);
+ fitBtn.classList.toggle('active', fitMode);
+ }
+ fitBtn.addEventListener('click', () => {
+ fitMode = !fitMode;
+ localStorage.setItem('screen-fit', fitMode ? 'on' : 'off');
+ applyFitMode();
+ });
+ applyFitMode();
+
function hex(bytes) {
return Array.from(bytes).map(b => b.toString(16).padStart(2,'0')).join(' ');
}
@@ -570,8 +600,12 @@ canvas { display: block; cursor: default; }
function sendPointer(ev) {
const r = canvas.getBoundingClientRect();
- const x = Math.max(0, Math.min(fbW-1, ev.clientX - r.left));
- const y = Math.max(0, Math.min(fbH-1, ev.clientY - r.top));
+ // In fit mode the canvas is CSS-scaled, so the rendered rect differs
+ // from the intrinsic resolution — map client coords back to fb pixels.
+ const sx = r.width ? canvas.width / r.width : 1;
+ const sy = r.height ? canvas.height / r.height : 1;
+ const x = Math.max(0, Math.min(fbW-1, Math.round((ev.clientX - r.left) * sx)));
+ const y = Math.max(0, Math.min(fbH-1, Math.round((ev.clientY - r.top) * sy)));
let mask = 0;
if (ev.buttons & 1) mask |= 1;
if (ev.buttons & 4) mask |= 2;