Browse Source

Fix virtual keyboard overlaying input fields on SpoolBuddy settings

  When the virtual keyboard opened on the SpoolBuddy device tab, the
  Backend URL and API Token fields were hidden behind the keyboard
  overlay. The keyboard now adds temporary bottom padding to the
  scrollable container so inputs scroll above it, and removes the
  padding on dismiss.
maziggy 2 months ago
parent
commit
00b4386e90
4 changed files with 33 additions and 5 deletions
  1. 1 0
      CHANGELOG.md
  2. 31 4
      frontend/src/components/VirtualKeyboard.tsx
  3. 0 0
      static/assets/index-C7t_l7cK.js
  4. 1 1
      static/index.html

+ 1 - 0
CHANGELOG.md

@@ -21,6 +21,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **Spool Assignment Changes Sync Across Tabs** — Assigning or unassigning a spool now broadcasts a WebSocket event to all connected clients. Other open browser tabs and the SpoolBuddy frontend update automatically without requiring a page reload.
 - **SpoolBuddy Auto-Navigate on Tag Scan** — When an NFC tag is detected while the SpoolBuddy UI is on a non-dashboard page (Settings, AMS, Write Tag, etc.), the frontend automatically navigates back to the main dashboard to show the scanned spool. Also wakes the screen if the display was blanked.
 - **SpoolBuddy Swipe to Switch Printers** — Swiping left/right on the SpoolBuddy touchscreen now cycles through online printers instead of triggering browser back/forward navigation. The selected printer updates in the top bar dropdown. Requires at least two online printers; single-printer setups are unaffected.
+- **SpoolBuddy Virtual Keyboard No Longer Overlays Input Fields** — The virtual keyboard now adds temporary scroll padding to the content area when it opens, ensuring the focused input field scrolls above the keyboard instead of being hidden behind it. Fixes text entry on the SpoolBuddy Settings device tab (backend URL, API token fields).
 - **SpoolBuddy System Tab** — Added a "System" tab to SpoolBuddy Settings showing live OS stats from the Raspberry Pi: CPU temperature, core count, load average, memory usage, disk usage, OS distro/kernel/architecture, Python version, and system uptime. Stats are collected by the daemon every heartbeat (10s) using stdlib-only reads from `/proc` and `/sys` — no additional dependencies required. Usage bars turn amber at 70% and red at 90%; CPU temperature is color-coded green/amber/red.
 
 ### Fixed

+ 31 - 4
frontend/src/components/VirtualKeyboard.tsx

@@ -26,6 +26,30 @@ export function VirtualKeyboard() {
   const keyboardRef = useRef<ReturnType<typeof Keyboard> | null>(null);
   const containerRef = useRef<HTMLDivElement>(null);
 
+  // Add bottom padding to the scrollable ancestor so inputs can scroll above the keyboard
+  const paddedParentRef = useRef<HTMLElement | null>(null);
+
+  const addScrollPadding = useCallback((target: HTMLElement) => {
+    // Find nearest scrollable ancestor
+    let el: HTMLElement | null = target.parentElement;
+    while (el) {
+      const style = getComputedStyle(el);
+      if (style.overflowY === 'auto' || style.overflowY === 'scroll') break;
+      el = el.parentElement;
+    }
+    if (!el) return;
+    paddedParentRef.current = el;
+    // Keyboard is ~260px tall; add generous padding
+    el.style.paddingBottom = '280px';
+  }, []);
+
+  const removeScrollPadding = useCallback(() => {
+    if (paddedParentRef.current) {
+      paddedParentRef.current.style.paddingBottom = '';
+      paddedParentRef.current = null;
+    }
+  }, []);
+
   const handleFocusIn = useCallback((e: FocusEvent) => {
     if (closingRef.current) return;
     const target = e.target as HTMLElement;
@@ -47,11 +71,12 @@ export function VirtualKeyboard() {
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     (keyboardRef.current as any)?.setInput?.(activeInput.current.value);
 
-    // Scroll input into view above the keyboard
+    // Add scroll padding then scroll input into view above the keyboard
+    addScrollPadding(target);
     setTimeout(() => {
       target.scrollIntoView({ behavior: 'smooth', block: 'center' });
     }, 100);
-  }, []);
+  }, [addScrollPadding]);
 
   const handleFocusOut = useCallback(() => {
     // Delay to allow click on keyboard buttons to register
@@ -64,10 +89,11 @@ export function VirtualKeyboard() {
       ) {
         return;
       }
+      removeScrollPadding();
       setVisible(false);
       activeInput.current = null;
     }, 150);
-  }, []);
+  }, [removeScrollPadding]);
 
   useEffect(() => {
     document.addEventListener('focusin', handleFocusIn);
@@ -83,6 +109,7 @@ export function VirtualKeyboard() {
   const dismiss = useCallback(() => {
     closingRef.current = true;
     setClosing(true);
+    removeScrollPadding();
     activeInput.current?.blur();
     activeInput.current = null;
     setTimeout(() => {
@@ -90,7 +117,7 @@ export function VirtualKeyboard() {
       setClosing(false);
       closingRef.current = false;
     }, 400);
-  }, []);
+  }, [removeScrollPadding]);
 
   const onKeyPress = useCallback((button: string) => {
     const input = activeInput.current;

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-C7t_l7cK.js


+ 1 - 1
static/index.html

@@ -23,7 +23,7 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-B2-uBp6-.js"></script>
+    <script type="module" crossorigin src="/assets/index-C7t_l7cK.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-CZLTApPU.css">
   </head>
   <body>

Some files were not shown because too many files changed in this diff