From a6994601c44280bf30a1445918a49eb91a98b355 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 2 Dec 2025 18:16:25 +0000 Subject: [PATCH] Complete version 0.9.8: Named Sites & Navigation Menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug Fixes: - Fixed virtual console menu display (now checks both getty and X11 DontVTSwitch) - Fixed complete_uninstall to properly remove LMS/Squeezelite services - Fixed full_reinstall to clean all addons and settings (except saved VPN/VNC) Named Websites Feature: - Added 'name' field to config.json schema for all sites - Added NAMES array throughout codebase for site name management - Added update_site_names() function with menu option (Sites → option 3) - Names prompted during site addition (both new installs and adding sites) - Site listings now display as "Name" - URL when name is provided - All management functions (add, update, delete, reorder) handle names properly - Names fully integrated with save/load config operations Navigation Menu Feature: - Added enableNavButton config option (defaults to true) - Added navigation button at top-left (purple key icon 🔑) - Button shows on user interaction (same logic as pause/keyboard buttons) - Navigation menu overlay with 2-column layout: * Column 1: Clickable list of all non-hidden sites (uses names if available) * Column 2: Touch gesture cheat sheet and keyboard shortcuts reference - Menu accessible via key icon click - IPC handlers added: get-config and navigate-to-tab - Menu excludes hidden sites (duration === -1) as requested - Optional feature configurable via Optional Buttons menu README Updates: - Updated all version references to 0.9.8 - Updated installation commands to use install_kiosk_0.9.8.sh - Added comprehensive "Why Use Named Sites?" section with use cases: * Home/Family kiosks examples * Business kiosks examples * Digital signage examples * Multi-location setups examples - Updated Multi-Site Management section to include named sites and navigation menu - Updated Touch Controls section to reflect correct gesture (3-finger DOWN toggle) - Updated Project Status to version 0.9.8 - Updated menu access commands throughout document Technical Implementation: - preload.js: Added nav button/menu variables, create/show/hide functions - preload.js: Added IPC listener for 'nav-button-enabled' - preload.js: Integration with user interaction handlers - main.js: Added enableNavButton variable and config loading - main.js: Added 'get-config' and 'navigate-to-tab' IPC handlers - main.js: Send nav-button-enabled state on page load - Bash script: Added site name prompts in add_new_sites() and add_new_sites_simple() - Bash script: Added update_site_names() function for updating existing site names - Bash script: Updated configure_optional_buttons() to include navigation button - Bash script: Updated all save/load config operations to handle NAMES array Script now at 9607 lines (242 lines added for navigation menu feature) All syntax validated with bash -n --- Readme.md | 60 ++++++++-- install_kiosk_0.9.8.sh | 251 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 295 insertions(+), 16 deletions(-) diff --git a/Readme.md b/Readme.md index d3b8829..9f07a05 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,6 @@ # Ubuntu Based Kiosk (UBK) -**Current Version:** 0.9.7-5 (check script header for latest version) +**Current Version:** 0.9.8 (check script header for latest version) **Built with Claude Sonnet 4/.5 AI assistance** **License:** GPL v3 - Keep derivatives open source **Repository:** https://github.com/outis1one/ubk/ @@ -47,9 +47,9 @@ Home/office kiosk for reusing old hardware, displaying: # Enable SSH during installation # Download and run installer -wget https://github.com/outis1one/ubk/raw/main/install_kiosk_0.9.7-5.sh -chmod +x install_kiosk_0.9.7-5.sh -./install_kiosk_0.9.7-5.sh +wget https://github.com/outis1one/ubk/raw/main/install_kiosk_0.9.8.sh +chmod +x install_kiosk_0.9.8.sh +./install_kiosk_0.9.8.sh ``` The installer will guide you through configuration during setup. @@ -60,18 +60,20 @@ The installer will guide you through configuration during setup. ### Multi-Site Management - **Single or multiple sites** with independent configurations +- **Named sites** - Optional friendly names for easy identification in navigation menu - **Auto-rotation** - Sites rotate automatically based on duration - **Manual sites** - Duration = 0, accessible via swipe only, trigger inactivity timeout - **Hidden sites** - Duration = -1, PIN-protected access, trigger inactivity timeout - **Home URL** - Auto-return after inactivity on manual or hidden sites - **Pause functionality** - Temporarily pause rotation (configurable per-site) +- **Navigation menu** - Quick access to all sites via key icon (top-left hot corner) ### Touch Controls - **2-finger horizontal swipe** - Switch between sites -- **3-finger up swipe** - Access hidden tabs (PIN required) -- **3-finger down swipe** - Return to normal tabs (from hidden tabs) +- **3-finger down swipe** - Toggle hidden tabs (PIN required to show, swipe again to hide) - **1-finger swipe** (dual mode) - Navigate within page (arrow keys) - **On-screen keyboard** - Auto-shows on text fields or click keyboard icon +- **Navigation menu** - Click key icon (top-left) to access site list and gesture cheat sheet ### On-Screen Keyboard - **HTML-based keyboard** with full QWERTY layout @@ -382,7 +384,7 @@ smb://WORKGROUP/COMPUTER/PrinterName ```bash # Run installer script again to access menu -./install_kiosk_0.9.7-5.sh +./install_kiosk_0.9.8.sh # Menu structure: # 1. Core Settings - Sites, WiFi, schedules, passwords, full reinstall, complete uninstall @@ -564,6 +566,44 @@ Hidden tabs are perfect for scenarios where you need access to sensitive or priv The hidden tab system provides a balance between public accessibility and private functionality without needing to physically access a terminal or reconfigure the system. +#### Why Use Named Sites? + +Named sites provide user-friendly labels that make navigation and management easier, especially when dealing with multiple similar URLs or complex web addresses. + +**Benefits:** + +- **Easier Navigation:** Click "Photo Gallery" instead of remembering "https://immich.mydomain.com:2283" +- **Better Organization:** Quickly identify sites in the navigation menu without parsing URLs +- **User-Friendly:** Non-technical users can find sites by name instead of domain +- **Cleaner Display:** "Home Assistant" is more readable than "http://192.168.1.50:8123" +- **Professional Appearance:** Business kiosks benefit from descriptive names over technical URLs + +**Use Cases:** + +**Home/Family Kiosks:** +- "Photo Albums" instead of "https://photoprism.local:2342" +- "Weather" instead of "https://weather.com" +- "Security Cameras" instead of "http://192.168.1.100:8000" +- "Smart Home" instead of "http://homeassistant.local:8123" + +**Business Kiosks:** +- "Employee Portal" instead of "https://portal.company.com/employees" +- "Time Clock" instead of "https://timekeeping.company.com/punch" +- "Inventory System" instead of "http://10.0.0.50:8080/inventory" +- "Customer Service" instead of "https://crm.company.com/support" + +**Digital Signage:** +- "Dashboard 1" through "Dashboard 5" for rotating content +- "Announcements" instead of "https://cms.local/public/announcements" +- "Menu Board" instead of "http://192.168.1.75/menus/today" + +**Multi-Location Setups:** +- "Building A Reception" and "Building B Reception" for identical URL structures +- "Floor 1 Display" through "Floor 10 Display" for elevator kiosks +- "East Wing" and "West Wing" for hospital navigation + +Names are completely optional - if left blank, the URL will be displayed as usual. Configure names during initial setup or update them later via: Core Settings → Sites → Update site names. + ### Inactivity Extensions When "Are you still here?" prompt appears (on manual or hidden sites): @@ -727,7 +767,7 @@ Full system cleanup that removes all kiosk components and restores the system to **Access:** ```bash # Core Settings menu → option 11 -./install_kiosk_0.9.7-5.sh +./install_kiosk_0.9.8.sh # Choose: Core Settings → Complete Uninstall ``` @@ -889,7 +929,7 @@ See the LICENSE file in the repository for full terms. ## Project Status & Future Plans -**Current Version:** 0.9.7-5 - Maintenance & Polish Update +**Current Version:** 0.9.8 - Named Sites & Navigation Menu **Planned Features:** - Web-based GUI configuration interface @@ -932,4 +972,4 @@ Special thanks to the maintainers of all upstream projects that make UBK possibl --- *Last Updated: December 2, 2025* -*Version: 0.9.7-5* +*Version: 0.9.8* diff --git a/install_kiosk_0.9.8.sh b/install_kiosk_0.9.8.sh index af59a10..a6cda92 100644 --- a/install_kiosk_0.9.8.sh +++ b/install_kiosk_0.9.8.sh @@ -3979,6 +3979,7 @@ let inactivityTimeout=120000; let allowNavigation='same-origin'; let enablePauseButton=true; let enableKeyboardButton=true; +let enableNavButton=true; let enablePasswordProtection=false; let lockoutPassword=""; let lockoutTimeout=0; @@ -4010,6 +4011,7 @@ function loadConfig(){ allowNavigation=config.allowNavigation||'same-origin'; enablePauseButton=(config.enablePauseButton!==false); enableKeyboardButton=(config.enableKeyboardButton!==false); + enableNavButton=(config.enableNavButton!==false); enablePasswordProtection=(config.enablePasswordProtection===true); lockoutPassword=config.lockoutPassword||""; lockoutTimeout=(config.lockoutTimeout||0)*60000; // Convert minutes to ms @@ -5225,6 +5227,10 @@ function createWindow(){ view.webContents.send('keyboard-button-enabled',enableKeyboardButton); console.log('[MAIN] Page loaded - sending keyboard-button-enabled: '+enableKeyboardButton); + + view.webContents.send('nav-button-enabled',enableNavButton); + console.log('[MAIN] Page loaded - sending nav-button-enabled: '+enableNavButton); + console.log('[MAIN] Page loaded - resending pause-button-visibility: '+shouldShow+' for '+t.url); }); @@ -5275,6 +5281,22 @@ function createWindow(){ } } }); + + ipcMain.on('get-config',(event)=>{ + event.sender.send('config-data',config); + }); + + ipcMain.on('navigate-to-tab',(event,tabIndex)=>{ + console.log('[NAV] Navigate to tab '+tabIndex); + if(showingHidden){ + forceReturnToTabs(); + } + const viewIndex=tabIndexToViewIndex[tabIndex]; + if(viewIndex!==undefined&&viewIndex>=0&&viewIndex{ markKeyboardActivity(); @@ -6074,12 +6096,13 @@ INACTHTML sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/preload.js" > /dev/null <<'PRELOAD' const {contextBridge,ipcRenderer}=require('electron'); -console.log('═════════════════════════════════════════════════════════════'); +console.log('════════════════════════════════════════════════════════════'); console.log(' Gestures:'); console.log(' 3-finger DOWN: Toggle hidden tabs (PIN required)'); console.log(' 2-finger HORIZONTAL: Switch between sites'); -console.log(' Keyboard: Auto-shows on text fields or click icon'); -console.log('╚═══════════════════════════════════════════════════════════╝'); +console.log(' 1-finger HORIZONTAL: Navigate within page'); +console.log(' Navigation: Top-left key icon for site menu'); +console.log('════════════════════════════════════════════════════════════'); contextBridge.exposeInMainWorld('electronAPI', { notifyActivity: () => ipcRenderer.send('user-activity'), @@ -6154,6 +6177,10 @@ function hidePauseButton(){ let keyboardButtonEnabled=true; let keyboardVisible=false; let keyboardIcon=null; +let navButtonEnabled=true; +let navButton=null; +let navMenu=null; +let navMenuVisible=false; // Listen for pause button visibility control from main process // CRITICAL: This must be outside DOMContentLoaded so it doesn't reset on page load @@ -6179,6 +6206,17 @@ ipcRenderer.on('keyboard-button-enabled',(event,enabled)=>{ } }); +ipcRenderer.on('nav-button-enabled',(event,enabled)=>{ + navButtonEnabled=enabled; + console.log('[NAV-BTN] Navigation button enabled: '+enabled); + if(navButton&&!enabled){ + navButton.style.display='none'; + } + if(navMenu&&!enabled){ + navMenu.style.display='none'; + } +}); + window.addEventListener('DOMContentLoaded',()=>{ document.addEventListener('contextmenu',e=>e.preventDefault()); @@ -6275,7 +6313,200 @@ window.addEventListener('DOMContentLoaded',()=>{ function hideKeyboardIcon(){ if(keyboardIcon)keyboardIcon.style.display='none'; } - + + function createNavButton(){ + if(navButton||!navButtonEnabled)return; + + navButton=document.createElement('div'); + navButton.id='electron-nav-button'; + navButton.innerHTML='🔑'; + navButton.title='Navigation Menu'; + navButton.style.cssText=` + position:fixed;top:20px;left:20px;width:60px;height:60px; + background:rgba(155,89,182,0.95);border:3px solid rgba(255,255,255,0.9); + border-radius:50%;display:none;align-items:center;justify-content:center; + font-size:32px;cursor:pointer;z-index:999999; + box-shadow:0 4px 12px rgba(0,0,0,0.4);user-select:none; + `; + + navButton.addEventListener('click',(e)=>{ + e.preventDefault(); + e.stopPropagation(); + toggleNavMenu(); + }); + + document.body.appendChild(navButton); + } + + function showNavButton(){ + if(!navButtonEnabled)return; + if(!navButton)createNavButton(); + if(navButton)navButton.style.display='flex'; + } + + function hideNavButton(){ + if(navButton)navButton.style.display='none'; + } + + function createNavMenu(){ + if(navMenu)return; + + navMenu=document.createElement('div'); + navMenu.id='electron-nav-menu'; + navMenu.style.cssText=` + position:fixed;top:0;left:0;width:100%;height:100%; + background:rgba(0,0,0,0.9);display:none;align-items:center;justify-content:center; + z-index:999998; + `; + + const content=document.createElement('div'); + content.style.cssText=` + background:rgba(44,62,80,0.98);border-radius:20px;padding:40px; + max-width:90%;max-height:90%;overflow-y:auto; + box-shadow:0 10px 40px rgba(0,0,0,0.5); + `; + + const closeBtn=document.createElement('div'); + closeBtn.innerHTML='✕'; + closeBtn.style.cssText=` + position:absolute;top:20px;right:20px;font-size:32px;color:white; + cursor:pointer;width:40px;height:40px;display:flex;align-items:center; + justify-content:center;border-radius:50%;background:rgba(231,76,60,0.8); + `; + closeBtn.addEventListener('click',hideNavMenu); + content.appendChild(closeBtn); + + const columns=document.createElement('div'); + columns.style.cssText='display:flex;gap:40px;margin-top:20px;'; + + // Column 1: Sites + const sitesCol=document.createElement('div'); + sitesCol.style.cssText='flex:1;min-width:300px;'; + sitesCol.innerHTML='

Sites

'; + const sitesList=document.createElement('div'); + sitesList.id='nav-sites-list'; + sitesList.style.cssText='display:flex;flex-direction:column;gap:10px;'; + sitesCol.appendChild(sitesList); + + // Column 2: Gesture Cheat Sheet + const cheatCol=document.createElement('div'); + cheatCol.style.cssText='flex:1;min-width:300px;'; + cheatCol.innerHTML=` +

Touch Gestures

+
+
+
2-Finger Horizontal Swipe
+
Switch between sites
+
+
+
1-Finger Horizontal Swipe
+
Navigate within page (arrow keys)
+
+
+
3-Finger Down Swipe
+
Toggle hidden tabs (PIN required)
+
+
+
Keyboard Shortcuts
+
+
+
Ctrl+Tab or Ctrl+] Next tab
+
+
+
Ctrl+Shift+Tab or Ctrl+[ Previous tab
+
+
+
F10 or Ctrl+H Toggle hidden tabs
+
+
+
Escape Return to normal tabs
+
+
+
Ctrl+Alt+Delete or Ctrl+Alt+P Power menu
+
+
+
Ctrl+K Toggle keyboard
+
+
+ `; + + columns.appendChild(sitesCol); + columns.appendChild(cheatCol); + content.appendChild(columns); + navMenu.appendChild(content); + + navMenu.addEventListener('click',(e)=>{ + if(e.target===navMenu){ + hideNavMenu(); + } + }); + + document.body.appendChild(navMenu); + } + + function toggleNavMenu(){ + if(navMenuVisible){ + hideNavMenu(); + }else{ + showNavMenu(); + } + } + + function showNavMenu(){ + if(!navMenu)createNavMenu(); + loadSitesIntoNav(); + navMenu.style.display='flex'; + navMenuVisible=true; + } + + function hideNavMenu(){ + if(navMenu)navMenu.style.display='none'; + navMenuVisible=false; + } + + function loadSitesIntoNav(){ + const sitesList=document.getElementById('nav-sites-list'); + if(!sitesList)return; + + // Request config from main process + ipcRenderer.send('get-config'); + } + + ipcRenderer.on('config-data',(event,config)=>{ + const sitesList=document.getElementById('nav-sites-list'); + if(!sitesList||!config||!config.tabs)return; + + sitesList.innerHTML=''; + + config.tabs.forEach((tab,index)=>{ + // Skip hidden tabs (duration === -1) + if(tab.duration===-1)return; + + const siteBtn=document.createElement('div'); + const displayName=tab.name||tab.url; + siteBtn.innerHTML=displayName; + siteBtn.style.cssText=` + padding:15px 20px;background:rgba(52,152,219,0.8);color:white; + border-radius:10px;cursor:pointer;font-size:18px; + transition:all 0.2s;border:2px solid transparent; + `; + siteBtn.addEventListener('mouseenter',()=>{ + siteBtn.style.background='rgba(52,152,219,1)'; + siteBtn.style.borderColor='rgba(255,255,255,0.5)'; + }); + siteBtn.addEventListener('mouseleave',()=>{ + siteBtn.style.background='rgba(52,152,219,0.8)'; + siteBtn.style.borderColor='transparent'; + }); + siteBtn.addEventListener('click',()=>{ + ipcRenderer.send('navigate-to-tab',index); + hideNavMenu(); + }); + + sitesList.appendChild(siteBtn); + }); + }); + function isTextInput(el){ if(!el)return false; const tag=(el.tagName||'').toLowerCase(); @@ -6380,6 +6611,11 @@ window.addEventListener('DOMContentLoaded',()=>{ } showPauseButton(); // This will reset the hide timer } + + // Always show navigation button on user interaction (if enabled) + if(navButtonEnabled){ + showNavButton(); + } } // Show pause button on any user interaction @@ -8314,8 +8550,11 @@ disable_keyboard_autoshow() { const {contextBridge,ipcRenderer}=require('electron'); console.log('════════════════════════════════════════════════════════════'); -console.log(' Hidden Tab: 3-finger DOWN swipe (PIN protected)'); -console.log(' Keyboard: 2-finger DOWN swipe (always available)'); +console.log(' Gestures:'); +console.log(' 3-finger DOWN: Toggle hidden tabs (PIN required)'); +console.log(' 2-finger HORIZONTAL: Switch between sites'); +console.log(' 1-finger HORIZONTAL: Navigate within page'); +console.log(' Navigation: Top-left key icon for site menu'); console.log('════════════════════════════════════════════════════════════'); contextBridge.exposeInMainWorld('electronAPI', {