remote trackpad and keyboard server
git clone https://git.lucas.co/cce-remote.git
fix: fit the whole layout above the keyboard, not just the text preview
350f373 floated the wrong element — the request was for the trackpad / live
window view to stay visible while typing, not the text strip. Instead of
positioning one element, the body now fits itself to the visual viewport when
the keyboard is up: height = visualViewport.height (translateY(offsetTop)
when iOS slides a standalone page), so the flex column reflows and everything
— bar, preview, pad, click buttons — sits above the keyboard, the pad
shrinking to absorb the difference. Restored to normal on keyboard close.
The preview float is gone with it: in a layout that fits the visible strip,
the in-flow preview is already above the keyboard, and a fixed overlay would
just cover the shrunken pad. Tap mapping and the cursor marker need no
changes — both read getBoundingClientRect, which reflects the reflow.
Co-Authored-By: Claude Fable 5 <[email protected]>
index.html | 33 +++++++++++++++------------------
1 file changed, 15 insertions(+), 18 deletions(-)
diff --git a/index.html b/index.html
index b7c1165..a69e7bc 100644
--- a/index.html
+++ b/index.html
@@ -62,12 +62,6 @@
background: #191b23; border: 1px solid #2e3140; border-radius: 8px;
font-size: 15px; color: #cfe8e8; white-space: pre-wrap; word-break: break-all; }
#preview.show { display: block; }
- /* While the software keyboard is up, the preview floats with its bottom
- just above the keyboard (placed by JS from visualViewport) — eyes are on
- the keyboard, not the top of the screen. Display-only: never intercept
- pad gestures underneath it. */
- #preview.float { position: fixed; left: 8px; right: 8px; margin: 0; z-index: 6;
- pointer-events: none; background: rgba(25,27,35,0.94); }
#preview.show::after { content: "▍"; color: #7dffff; animation: blink 1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }
#status { position: fixed; top: 4px; right: 10px; font-size: 10px; color: #4a4d5c; }
@@ -492,26 +486,29 @@ function armKb() { kb.value = SENTINEL; kb.setSelectionRange(SENTINEL.length, SE
const preview = document.getElementById("preview");
let typed = "";
// The iOS keyboard overlays the page without any CSS-visible layout change;
-// visualViewport is the only honest signal (its height shrinks, and offsetTop
-// compensates for iOS sliding a standalone page). Reposition on viewport
-// changes AND on every text change, since wrapping alters the preview height.
+// visualViewport is the only honest signal (its height shrinks by the
+// keyboard, and offsetTop tracks iOS sliding a standalone page). While the
+// keyboard is up, fit the WHOLE layout into the visible strip above it: the
+// flex column reflows, so the trackpad / live view shrinks but stays fully
+// visible — its bottom edge, and the click buttons, ride the keyboard top.
+// The 0.85 guard keeps ordinary browser-chrome changes from counting as a
+// keyboard. Tap mapping and the cursor marker need no adjustment: both read
+// getBoundingClientRect, which reflects the reflow.
const vv = window.visualViewport;
-function placePreview() {
- const kbOpen = vv && document.activeElement === kb
- && vv.height < window.innerHeight * 0.85;
- preview.classList.toggle("float", !!kbOpen);
- preview.style.top = kbOpen
- ? Math.max(0, vv.offsetTop + vv.height - preview.offsetHeight - 8) + "px"
+function fitViewport() {
+ const short = vv && vv.height < window.innerHeight * 0.85;
+ document.body.style.height = short ? vv.height + "px" : "";
+ document.body.style.transform = short && vv.offsetTop > 0
+ ? "translateY(" + vv.offsetTop + "px)"
: "";
}
if (vv) {
- vv.addEventListener("resize", placePreview);
- vv.addEventListener("scroll", placePreview);
+ vv.addEventListener("resize", fitViewport);
+ vv.addEventListener("scroll", fitViewport);
}
function updPreview() {
preview.textContent = typed;
preview.classList.toggle("show", document.activeElement === kb);
- placePreview();
}
kb.addEventListener("focus", () => { armKb(); updPreview(); });
kb.addEventListener("blur", () => { typed = ""; updPreview(); });