Kaynağa Gözat

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 ay önce
ebeveyn
işleme
00b4386e90

+ 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.
 - **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 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 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.
 - **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
 ### Fixed

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

@@ -26,6 +26,30 @@ export function VirtualKeyboard() {
   const keyboardRef = useRef<ReturnType<typeof Keyboard> | null>(null);
   const keyboardRef = useRef<ReturnType<typeof Keyboard> | null>(null);
   const containerRef = useRef<HTMLDivElement>(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) => {
   const handleFocusIn = useCallback((e: FocusEvent) => {
     if (closingRef.current) return;
     if (closingRef.current) return;
     const target = e.target as HTMLElement;
     const target = e.target as HTMLElement;
@@ -47,11 +71,12 @@ export function VirtualKeyboard() {
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     (keyboardRef.current as any)?.setInput?.(activeInput.current.value);
     (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(() => {
     setTimeout(() => {
       target.scrollIntoView({ behavior: 'smooth', block: 'center' });
       target.scrollIntoView({ behavior: 'smooth', block: 'center' });
     }, 100);
     }, 100);
-  }, []);
+  }, [addScrollPadding]);
 
 
   const handleFocusOut = useCallback(() => {
   const handleFocusOut = useCallback(() => {
     // Delay to allow click on keyboard buttons to register
     // Delay to allow click on keyboard buttons to register
@@ -64,10 +89,11 @@ export function VirtualKeyboard() {
       ) {
       ) {
         return;
         return;
       }
       }
+      removeScrollPadding();
       setVisible(false);
       setVisible(false);
       activeInput.current = null;
       activeInput.current = null;
     }, 150);
     }, 150);
-  }, []);
+  }, [removeScrollPadding]);
 
 
   useEffect(() => {
   useEffect(() => {
     document.addEventListener('focusin', handleFocusIn);
     document.addEventListener('focusin', handleFocusIn);
@@ -83,6 +109,7 @@ export function VirtualKeyboard() {
   const dismiss = useCallback(() => {
   const dismiss = useCallback(() => {
     closingRef.current = true;
     closingRef.current = true;
     setClosing(true);
     setClosing(true);
+    removeScrollPadding();
     activeInput.current?.blur();
     activeInput.current?.blur();
     activeInput.current = null;
     activeInput.current = null;
     setTimeout(() => {
     setTimeout(() => {
@@ -90,7 +117,7 @@ export function VirtualKeyboard() {
       setClosing(false);
       setClosing(false);
       closingRef.current = false;
       closingRef.current = false;
     }, 400);
     }, 400);
-  }, []);
+  }, [removeScrollPadding]);
 
 
   const onKeyPress = useCallback((button: string) => {
   const onKeyPress = useCallback((button: string) => {
     const input = activeInput.current;
     const input = activeInput.current;

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
static/assets/index-C7t_l7cK.js


+ 1 - 1
static/index.html

@@ -23,7 +23,7 @@
 
 
     <!-- Splash screens for iOS -->
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
     <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">
     <link rel="stylesheet" crossorigin href="/assets/index-CZLTApPU.css">
   </head>
   </head>
   <body>
   <body>

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor