add menu item to set pin for hidden site
3 finger up to get to hidden site 3 finger down to get to regular site return to home/rotation works on manual and hidden sites
This commit is contained in:
+183
-150
@@ -3,10 +3,7 @@
|
|||||||
### Ubuntu Based Kiosk (UBK) v0.9.7 ###
|
### Ubuntu Based Kiosk (UBK) v0.9.7 ###
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# RELEASE v0.9.7 - Site-Specific Extension Fix
|
# RELEASE v0.9.7 - phase one complete
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Built with Claude Sonnet 4/.5 AI assistance
|
# Built with Claude Sonnet 4/.5 AI assistance
|
||||||
# License: GPL v3 - Keep derivatives open sour
|
# License: GPL v3 - Keep derivatives open sour
|
||||||
@@ -432,7 +429,7 @@ show_system_status() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show_addon_status() {
|
show_addon_status() {
|
||||||
echo " ╔══ INSTALLED ADDONS ══╗"
|
echo " ═══ INSTALLED ADDONS ═══"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
local any_addon=false
|
local any_addon=false
|
||||||
@@ -991,6 +988,83 @@ configure_password_protection() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configure_hidden_site_pin() {
|
||||||
|
echo
|
||||||
|
echo " ═══ HIDDEN SITE PIN ═══"
|
||||||
|
echo
|
||||||
|
echo "The PIN protects access to hidden sites (duration: -1)"
|
||||||
|
echo "Hidden sites can be accessed via:"
|
||||||
|
echo " • F10 key"
|
||||||
|
echo " • 3-finger UP swipe"
|
||||||
|
echo
|
||||||
|
|
||||||
|
local pin_file="$KIOSK_DIR/.jitsi-pin"
|
||||||
|
local current_pin=""
|
||||||
|
|
||||||
|
if sudo -u "$KIOSK_USER" test -f "$pin_file" 2>/dev/null; then
|
||||||
|
current_pin=$(sudo -u "$KIOSK_USER" cat "$pin_file" 2>/dev/null)
|
||||||
|
if [[ "$current_pin" == "NOPIN" ]]; then
|
||||||
|
echo "Current: No PIN (hidden sites disabled)"
|
||||||
|
else
|
||||||
|
echo "Current: PIN is set (${#current_pin} digits)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Current: Not configured (default: 1234)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Options:"
|
||||||
|
echo " 1. Set new PIN (4-8 digits)"
|
||||||
|
echo " 2. Disable PIN (allow access without PIN)"
|
||||||
|
echo " 3. Reset to default (1234)"
|
||||||
|
echo " 0. Cancel"
|
||||||
|
echo
|
||||||
|
read -r -p "Choose [0-3]: " pin_choice
|
||||||
|
|
||||||
|
case "$pin_choice" in
|
||||||
|
1)
|
||||||
|
echo
|
||||||
|
while true; do
|
||||||
|
read -r -p "Enter new PIN (4-8 digits): " new_pin
|
||||||
|
|
||||||
|
if [[ ! "$new_pin" =~ ^[0-9]{4,8}$ ]]; then
|
||||||
|
echo "❌ PIN must be 4-8 digits"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r -p "Confirm PIN: " confirm_pin
|
||||||
|
|
||||||
|
if [[ "$new_pin" == "$confirm_pin" ]]; then
|
||||||
|
echo "$new_pin" | sudo -u "$KIOSK_USER" tee "$pin_file" > /dev/null
|
||||||
|
sudo -u "$KIOSK_USER" chmod 600 "$pin_file"
|
||||||
|
log_success "PIN updated"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "❌ PINs don't match, try again"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
echo "NOPIN" | sudo -u "$KIOSK_USER" tee "$pin_file" > /dev/null
|
||||||
|
sudo -u "$KIOSK_USER" chmod 600 "$pin_file"
|
||||||
|
log_success "PIN disabled - hidden sites accessible without PIN"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
echo "1234" | sudo -u "$KIOSK_USER" tee "$pin_file" > /dev/null
|
||||||
|
sudo -u "$KIOSK_USER" chmod 600 "$pin_file"
|
||||||
|
log_success "PIN reset to default (1234)"
|
||||||
|
;;
|
||||||
|
0)
|
||||||
|
log_info "Cancelled"
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "ℹ️ Restart kiosk for changes to take effect"
|
||||||
|
pause
|
||||||
|
}
|
||||||
|
|
||||||
configure_sites() {
|
configure_sites() {
|
||||||
while true; do
|
while true; do
|
||||||
echo " ═══ SITES CONFIGURATION ═══"
|
echo " ═══ SITES CONFIGURATION ═══"
|
||||||
@@ -3582,16 +3656,19 @@ function loadConfig(){
|
|||||||
lockoutActiveEnd=config.lockoutActiveEnd||"";
|
lockoutActiveEnd=config.lockoutActiveEnd||"";
|
||||||
requirePasswordOnBoot=(config.requirePasswordOnBoot===true);
|
requirePasswordOnBoot=(config.requirePasswordOnBoot===true);
|
||||||
|
|
||||||
console.log('[CONFIG] ═══════════════════════════');
|
console.log('[CONFIG] ═════════════════════════════════');
|
||||||
console.log('[CONFIG] Home tab index:',homeTabIndex);
|
console.log('[CONFIG] Home tab index:',homeTabIndex);
|
||||||
console.log('[CONFIG] Inactivity timeout:',inactivityTimeout/1000,'seconds');
|
console.log('[CONFIG] Inactivity timeout:',inactivityTimeout/1000,'seconds');
|
||||||
console.log('[CONFIG] Navigation:',allowNavigation);
|
console.log('[CONFIG] Navigation:',allowNavigation);
|
||||||
console.log('[CONFIG] Pause button:',enablePauseButton); console.log('[CONFIG] Keyboard button:',enableKeyboardButton); console.log('[CONFIG] Password protection:',enablePasswordProtection); console.log('[CONFIG] Lockout timeout:',lockoutTimeout/60000,'minutes');
|
console.log('[CONFIG] Pause button:',enablePauseButton);
|
||||||
|
console.log('[CONFIG] Keyboard button:',enableKeyboardButton);
|
||||||
|
console.log('[CONFIG] Password protection:',enablePasswordProtection);
|
||||||
|
console.log('[CONFIG] Lockout timeout:',lockoutTimeout/60000,'minutes');
|
||||||
if(lockoutAtTime)console.log('[CONFIG] Lock at time:',lockoutAtTime);
|
if(lockoutAtTime)console.log('[CONFIG] Lock at time:',lockoutAtTime);
|
||||||
if(lockoutActiveStart&&lockoutActiveEnd)console.log('[CONFIG] Active hours:',lockoutActiveStart,'-',lockoutActiveEnd);
|
if(lockoutActiveStart&&lockoutActiveEnd)console.log('[CONFIG] Active hours:',lockoutActiveStart,'-',lockoutActiveEnd);
|
||||||
console.log('[CONFIG] Require password on boot:',requirePasswordOnBoot);
|
console.log('[CONFIG] Require password on boot:',requirePasswordOnBoot);
|
||||||
console.log('[CONFIG] Sites:',config.tabs?.length||0);
|
console.log('[CONFIG] Sites:',config.tabs?.length||0);
|
||||||
console.log('[CONFIG] ═══════════════════════════');
|
console.log('[CONFIG] ╚═══════════════════════════════╝');
|
||||||
|
|
||||||
return config.tabs||[];
|
return config.tabs||[];
|
||||||
}catch(e){
|
}catch(e){
|
||||||
@@ -3616,18 +3693,6 @@ function markActivity(){
|
|||||||
promptWindow.close();
|
promptWindow.close();
|
||||||
promptWindow=null;
|
promptWindow=null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRITICAL FIX: Don't clear extensions on activity
|
|
||||||
// Extensions should only expire naturally or be explicitly cancelled
|
|
||||||
// If user is active, they're using the current site - extension should remain
|
|
||||||
// inactivityExtensionUntil=0; // REMOVED - was breaking extensions
|
|
||||||
|
|
||||||
// CRITICAL: Do NOT reset lockout timer on activity
|
|
||||||
// Lockout timer should only reset when:
|
|
||||||
// 1. User unlocks the screen (in unlockScreen function)
|
|
||||||
// 2. User selects a time extension (in showPauseDialog/showInactivityPrompt)
|
|
||||||
// If we reset it here, ANY activity (mouse move, click, etc.) prevents lockout
|
|
||||||
// REMOVED: lockoutActivityTime reset - was preventing inactivity lockout from ever triggering
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function markKeyboardActivity(){
|
function markKeyboardActivity(){
|
||||||
@@ -3769,51 +3834,43 @@ function unlockScreen(){
|
|||||||
lockoutWindow=null;
|
lockoutWindow=null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore current view using proper method
|
// Restore current view
|
||||||
// For boot unlock, always use attachView for regular views (safe path)
|
|
||||||
if(showingHidden&&hiddenViews[currentHiddenIndex]){
|
if(showingHidden&&hiddenViews[currentHiddenIndex]){
|
||||||
try{
|
try{
|
||||||
const[w,h]=mainWindow.getContentSize();
|
const[w,h]=mainWindow.getContentSize();
|
||||||
// Ensure view is added to window before setting as top
|
|
||||||
mainWindow.addBrowserView(hiddenViews[currentHiddenIndex]);
|
mainWindow.addBrowserView(hiddenViews[currentHiddenIndex]);
|
||||||
mainWindow.setTopBrowserView(hiddenViews[currentHiddenIndex]);
|
mainWindow.setTopBrowserView(hiddenViews[currentHiddenIndex]);
|
||||||
hiddenViews[currentHiddenIndex].setBounds({x:0,y:0,width:w,height:h});
|
hiddenViews[currentHiddenIndex].setBounds({x:0,y:0,width:w,height:h});
|
||||||
}catch(e){
|
}catch(e){
|
||||||
console.error('[LOCKOUT] Error restoring hidden view:',e);
|
console.error('[LOCKOUT] Error restoring hidden view:',e);
|
||||||
// Fallback to regular views
|
|
||||||
if(views.length>0){
|
if(views.length>0){
|
||||||
showingHidden=false;
|
showingHidden=false;
|
||||||
attachView(0);
|
attachView(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else if(views.length>0){
|
}else if(views.length>0){
|
||||||
// Use valid index or default to 0
|
|
||||||
const idx=(currentIndex>=0&¤tIndex<views.length)?currentIndex:0;
|
const idx=(currentIndex>=0&¤tIndex<views.length)?currentIndex:0;
|
||||||
attachView(idx);
|
attachView(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start master timer if not already running (handles boot password case)
|
|
||||||
if(!masterTimer){
|
if(!masterTimer){
|
||||||
startMasterTimer();
|
startMasterTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset activity time
|
|
||||||
lockoutActivityTime=Date.now();
|
lockoutActivityTime=Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper: Check if current time is within active hours
|
|
||||||
function isWithinActiveHours(){
|
function isWithinActiveHours(){
|
||||||
if(!lockoutActiveStart||!lockoutActiveEnd)return true; // No restriction
|
if(!lockoutActiveStart||!lockoutActiveEnd)return true;
|
||||||
|
|
||||||
const now=new Date();
|
const now=new Date();
|
||||||
const currentTime=now.getHours()*60+now.getMinutes(); // Current time in minutes
|
const currentTime=now.getHours()*60+now.getMinutes();
|
||||||
|
|
||||||
const[startH,startM]=lockoutActiveStart.split(':').map(Number);
|
const[startH,startM]=lockoutActiveStart.split(':').map(Number);
|
||||||
const[endH,endM]=lockoutActiveEnd.split(':').map(Number);
|
const[endH,endM]=lockoutActiveEnd.split(':').map(Number);
|
||||||
const startMinutes=startH*60+startM;
|
const startMinutes=startH*60+startM;
|
||||||
const endMinutes=endH*60+endM;
|
const endMinutes=endH*60+endM;
|
||||||
|
|
||||||
// Handle overnight ranges (e.g., 22:00-06:00)
|
|
||||||
if(startMinutes>endMinutes){
|
if(startMinutes>endMinutes){
|
||||||
return currentTime>=startMinutes||currentTime<endMinutes;
|
return currentTime>=startMinutes||currentTime<endMinutes;
|
||||||
}
|
}
|
||||||
@@ -3821,7 +3878,6 @@ function isWithinActiveHours(){
|
|||||||
return currentTime>=startMinutes&¤tTime<endMinutes;
|
return currentTime>=startMinutes&¤tTime<endMinutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper: Check if it's time to auto-lock
|
|
||||||
function checkScheduledLockTime(){
|
function checkScheduledLockTime(){
|
||||||
if(!lockoutAtTime){
|
if(!lockoutAtTime){
|
||||||
return;
|
return;
|
||||||
@@ -3833,11 +3889,9 @@ function checkScheduledLockTime(){
|
|||||||
const currentMin=now.getMinutes();
|
const currentMin=now.getMinutes();
|
||||||
const[targetH,targetM]=lockoutAtTime.split(':').map(Number);
|
const[targetH,targetM]=lockoutAtTime.split(':').map(Number);
|
||||||
|
|
||||||
// Check once per minute to avoid multiple locks
|
|
||||||
const currentMinute=currentHour*60+currentMin;
|
const currentMinute=currentHour*60+currentMin;
|
||||||
const targetMinute=targetH*60+targetM;
|
const targetMinute=targetH*60+targetM;
|
||||||
|
|
||||||
// Debug log every 60 seconds
|
|
||||||
if(Math.floor(Date.now()/60000)!==Math.floor((Date.now()-1000)/60000)){
|
if(Math.floor(Date.now()/60000)!==Math.floor((Date.now()-1000)/60000)){
|
||||||
const timeStr=String(currentHour).padStart(2,'0')+':'+String(currentMin).padStart(2,'0');
|
const timeStr=String(currentHour).padStart(2,'0')+':'+String(currentMin).padStart(2,'0');
|
||||||
console.log('[LOCKOUT-SCHED] Current: '+timeStr+' ('+currentMinute+' min) | Target: '+lockoutAtTime+' ('+targetMinute+' min) | Match: '+(currentMinute===targetMinute));
|
console.log('[LOCKOUT-SCHED] Current: '+timeStr+' ('+currentMinute+' min) | Target: '+lockoutAtTime+' ('+targetMinute+' min) | Match: '+(currentMinute===targetMinute));
|
||||||
@@ -3858,44 +3912,35 @@ function checkLockoutTimer(){
|
|||||||
|
|
||||||
const now=Date.now();
|
const now=Date.now();
|
||||||
|
|
||||||
// Check for scheduled lock time
|
|
||||||
checkScheduledLockTime();
|
checkScheduledLockTime();
|
||||||
|
|
||||||
// Check inactivity-based lockout
|
|
||||||
if(lockoutTimeout>0){
|
if(lockoutTimeout>0){
|
||||||
// Check if within active hours (if configured)
|
|
||||||
if(!isWithinActiveHours()){
|
if(!isWithinActiveHours()){
|
||||||
// Log every 60 seconds when outside active hours
|
|
||||||
if(Math.floor(now/60000)!==Math.floor((now-1000)/60000)){
|
if(Math.floor(now/60000)!==Math.floor((now-1000)/60000)){
|
||||||
console.log('[LOCKOUT] Outside active hours, lockout disabled');
|
console.log('[LOCKOUT] Outside active hours, lockout disabled');
|
||||||
}
|
}
|
||||||
return; // Outside active hours, don't enforce lockout
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRITICAL: Respect time extensions from pause button and manual site timeout
|
|
||||||
if(inactivityExtensionUntil>0&&now<inactivityExtensionUntil){
|
if(inactivityExtensionUntil>0&&now<inactivityExtensionUntil){
|
||||||
// Still within extension period - don't lock
|
|
||||||
const remaining=Math.floor((inactivityExtensionUntil-now)/1000);
|
const remaining=Math.floor((inactivityExtensionUntil-now)/1000);
|
||||||
const remMin=Math.floor(remaining/60);
|
const remMin=Math.floor(remaining/60);
|
||||||
const remSec=remaining%60;
|
const remSec=remaining%60;
|
||||||
|
|
||||||
// Log every 30 seconds during extension
|
|
||||||
if(Math.floor(now/30000)!==Math.floor((now-1000)/30000)){
|
if(Math.floor(now/30000)!==Math.floor((now-1000)/30000)){
|
||||||
console.log('[LOCKOUT-INACT] ⏸ Extension active: '+remMin+'m '+remSec+'s remaining - lockout paused');
|
console.log('[LOCKOUT-INACT] ⸻ Extension active: '+remMin+'m '+remSec+'s remaining - lockout paused');
|
||||||
}
|
}
|
||||||
return; // Skip lockout check during extension
|
return;
|
||||||
}else if(inactivityExtensionUntil>0&&now>=inactivityExtensionUntil){
|
}else if(inactivityExtensionUntil>0&&now>=inactivityExtensionUntil){
|
||||||
// Extension expired - reset lockout activity timer
|
|
||||||
console.log('[LOCKOUT-INACT] ⏰ Extension expired - resetting lockout timer');
|
console.log('[LOCKOUT-INACT] ⏰ Extension expired - resetting lockout timer');
|
||||||
inactivityExtensionUntil=0;
|
inactivityExtensionUntil=0;
|
||||||
lockoutActivityTime=now; // CRITICAL: Reset so lockout doesn't trigger immediately
|
lockoutActivityTime=now;
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeSinceActivity=now-lockoutActivityTime;
|
const timeSinceActivity=now-lockoutActivityTime;
|
||||||
const minutesSinceActivity=Math.floor(timeSinceActivity/60000);
|
const minutesSinceActivity=Math.floor(timeSinceActivity/60000);
|
||||||
const lockoutMinutes=Math.floor(lockoutTimeout/60000);
|
const lockoutMinutes=Math.floor(lockoutTimeout/60000);
|
||||||
|
|
||||||
// Log every 30 seconds
|
|
||||||
if(Math.floor(timeSinceActivity/30000)!==Math.floor((timeSinceActivity-1000)/30000)){
|
if(Math.floor(timeSinceActivity/30000)!==Math.floor((timeSinceActivity-1000)/30000)){
|
||||||
console.log('[LOCKOUT-INACT] Idle: '+minutesSinceActivity+'m / '+lockoutMinutes+'m');
|
console.log('[LOCKOUT-INACT] Idle: '+minutesSinceActivity+'m / '+lockoutMinutes+'m');
|
||||||
}
|
}
|
||||||
@@ -4028,7 +4073,7 @@ function checkMediaPlayback(){
|
|||||||
lastMediaStateChange=now;
|
lastMediaStateChange=now;
|
||||||
}else{
|
}else{
|
||||||
if(wasPlaying){
|
if(wasPlaying){
|
||||||
console.log('[MEDIA] ⏸ Stopped');
|
console.log('[MEDIA] ⸻ Stopped');
|
||||||
}
|
}
|
||||||
mediaIsPlaying=false;
|
mediaIsPlaying=false;
|
||||||
if(wasPlaying){
|
if(wasPlaying){
|
||||||
@@ -4043,7 +4088,7 @@ function startMasterTimer(){
|
|||||||
clearInterval(masterTimer);
|
clearInterval(masterTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[TIMER] ╔═══ MASTER TIMER STARTED ════╗');
|
console.log('[TIMER] ════ MASTER TIMER STARTED ════');
|
||||||
console.log('[TIMER] Home tab index:',homeTabIndex);
|
console.log('[TIMER] Home tab index:',homeTabIndex);
|
||||||
console.log('[TIMER] Inactivity timeout:',inactivityTimeout/1000,'seconds');
|
console.log('[TIMER] Inactivity timeout:',inactivityTimeout/1000,'seconds');
|
||||||
console.log('[TIMER] Password protection:',enablePasswordProtection);
|
console.log('[TIMER] Password protection:',enablePasswordProtection);
|
||||||
@@ -4052,7 +4097,7 @@ function startMasterTimer(){
|
|||||||
if(lockoutAtTime)console.log('[TIMER] Scheduled lock time:',lockoutAtTime);
|
if(lockoutAtTime)console.log('[TIMER] Scheduled lock time:',lockoutAtTime);
|
||||||
if(lockoutActiveStart&&lockoutActiveEnd)console.log('[TIMER] Active hours:',lockoutActiveStart,'-',lockoutActiveEnd);
|
if(lockoutActiveStart&&lockoutActiveEnd)console.log('[TIMER] Active hours:',lockoutActiveStart,'-',lockoutActiveEnd);
|
||||||
}
|
}
|
||||||
console.log('[TIMER] ╚═══════════════════════════════════╝');
|
console.log('[TIMER] ╚═══════════════════════════════╝');
|
||||||
|
|
||||||
siteStartTime=Date.now();
|
siteStartTime=Date.now();
|
||||||
lastUserInteraction=Date.now();
|
lastUserInteraction=Date.now();
|
||||||
@@ -4114,26 +4159,21 @@ function startMasterTimer(){
|
|||||||
|
|
||||||
// 6. SITE ROTATION
|
// 6. SITE ROTATION
|
||||||
if(!showingHidden&&views.length>1){
|
if(!showingHidden&&views.length>1){
|
||||||
// CRITICAL: Don't rotate while pause dialog is open (user is selecting time)
|
|
||||||
if(pauseWindow&&!pauseWindow.isDestroyed()){
|
if(pauseWindow&&!pauseWindow.isDestroyed()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRITICAL: Don't rotate while inactivity prompt is open (user is responding)
|
|
||||||
if(promptWindow&&!promptWindow.isDestroyed()){
|
if(promptWindow&&!promptWindow.isDestroyed()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip rotation if user is in an extension period (they confirmed they're still there)
|
|
||||||
if(inactivityExtensionUntil>0&&now<inactivityExtensionUntil){
|
if(inactivityExtensionUntil>0&&now<inactivityExtensionUntil){
|
||||||
// Log every 30 seconds during extension
|
|
||||||
if(Math.floor(now/30000)!==Math.floor((now-1000)/30000)){
|
if(Math.floor(now/30000)!==Math.floor((now-1000)/30000)){
|
||||||
const remaining=Math.floor((inactivityExtensionUntil-now)/1000);
|
const remaining=Math.floor((inactivityExtensionUntil-now)/1000);
|
||||||
const remMin=Math.floor(remaining/60);
|
const remMin=Math.floor(remaining/60);
|
||||||
const remSec=remaining%60;
|
const remSec=remaining%60;
|
||||||
console.log('[ROTATION] ⏸ Paused - Extension active: '+remMin+'m '+remSec+'s remaining');
|
console.log('[ROTATION] ⸻ Paused - Extension active: '+remMin+'m '+remSec+'s remaining');
|
||||||
}
|
}
|
||||||
// Don't rotate during extension period
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4147,70 +4187,72 @@ function startMasterTimer(){
|
|||||||
|
|
||||||
if(timeOnSite>=siteDuration*1000){
|
if(timeOnSite>=siteDuration*1000){
|
||||||
rotateToNextSite();
|
rotateToNextSite();
|
||||||
return; // CRITICAL: Don't run HOME RETURN CHECK after rotating - site was timed
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. HOME RETURN CHECK (manual sites only)
|
// 7. HOME RETURN CHECK (manual and hidden sites)
|
||||||
if(homeTabIndex>=0&&!showingHidden){
|
if(homeTabIndex>=0){
|
||||||
const homeViewIdx=getHomeViewIndex();
|
if(pauseWindow&&!pauseWindow.isDestroyed()){
|
||||||
const currentTabIdx=viewIndexToTabIndex(currentIndex);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Only show home return prompt for manual sites (duration = 0)
|
if(promptWindow&&!promptWindow.isDestroyed()){
|
||||||
// Timed sites use the pause button instead
|
return;
|
||||||
if(homeViewIdx>=0&¤tIndex!==homeViewIdx&¤tTabIdx>=0&&tabs[currentTabIdx]){
|
}
|
||||||
const siteDuration=parseInt(tabs[currentTabIdx].duration)||0;
|
|
||||||
|
|
||||||
// Skip home return check for timed rotation sites
|
let needsInactivityCheck=false;
|
||||||
if(siteDuration>0){
|
let currentSiteDuration=-999;
|
||||||
// This is a timed site - pause button is used instead
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CRITICAL: Don't show prompt while pause dialog is open
|
if(showingHidden){
|
||||||
if(pauseWindow&&!pauseWindow.isDestroyed()){
|
needsInactivityCheck=true;
|
||||||
return;
|
currentSiteDuration=-1;
|
||||||
}
|
}else{
|
||||||
|
const homeViewIdx=getHomeViewIndex();
|
||||||
|
const currentTabIdx=viewIndexToTabIndex(currentIndex);
|
||||||
|
|
||||||
// CRITICAL: Don't show prompt while inactivity prompt is already open
|
if(homeViewIdx>=0&¤tIndex!==homeViewIdx&¤tTabIdx>=0&&tabs[currentTabIdx]){
|
||||||
if(promptWindow&&!promptWindow.isDestroyed()){
|
currentSiteDuration=parseInt(tabs[currentTabIdx].duration)||0;
|
||||||
return;
|
|
||||||
|
if(currentSiteDuration===0){
|
||||||
|
needsInactivityCheck=true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(needsInactivityCheck){
|
||||||
const idleTime=now-lastUserInteraction;
|
const idleTime=now-lastUserInteraction;
|
||||||
|
|
||||||
// CRITICAL FIX: Use absolute time check for extensions
|
|
||||||
let effectiveTimeout=inactivityTimeout;
|
let effectiveTimeout=inactivityTimeout;
|
||||||
if(inactivityExtensionUntil>0&&now<inactivityExtensionUntil){
|
if(inactivityExtensionUntil>0&&now<inactivityExtensionUntil){
|
||||||
// Still within extension period - don't timeout
|
|
||||||
if(Math.floor(idleTime/15000)!==Math.floor((idleTime-1000)/15000)){
|
if(Math.floor(idleTime/15000)!==Math.floor((idleTime-1000)/15000)){
|
||||||
const remaining=Math.floor((inactivityExtensionUntil-now)/1000);
|
const remaining=Math.floor((inactivityExtensionUntil-now)/1000);
|
||||||
const remMin=Math.floor(remaining/60);
|
const remMin=Math.floor(remaining/60);
|
||||||
const remSec=remaining%60;
|
const remSec=remaining%60;
|
||||||
console.log('[HOME] ⏰ Extended mode: '+remMin+'m '+remSec+'s remaining');
|
console.log('[HOME] ⏰ Extended mode: '+remMin+'m '+remSec+'s remaining');
|
||||||
}
|
}
|
||||||
return; // Skip timeout check during extension
|
return;
|
||||||
}else if(inactivityExtensionUntil>0&&now>=inactivityExtensionUntil){
|
}else if(inactivityExtensionUntil>0&&now>=inactivityExtensionUntil){
|
||||||
// Extension expired - clear it and reset inactivity timer
|
|
||||||
console.log('[HOME] ⏰ Extension expired - resetting inactivity timer');
|
console.log('[HOME] ⏰ Extension expired - resetting inactivity timer');
|
||||||
inactivityExtensionUntil=0;
|
inactivityExtensionUntil=0;
|
||||||
lastUserInteraction=now; // CRITICAL: Reset interaction time so prompt doesn't show immediately
|
lastUserInteraction=now;
|
||||||
siteStartTime=now; // Also reset rotation timer
|
siteStartTime=now;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log every 15 seconds
|
|
||||||
if(Math.floor(idleTime/15000)!==Math.floor((idleTime-1000)/15000)){
|
if(Math.floor(idleTime/15000)!==Math.floor((idleTime-1000)/15000)){
|
||||||
const idleMinutes=Math.floor(idleTime/60000);
|
const idleMinutes=Math.floor(idleTime/60000);
|
||||||
const idleSeconds=Math.floor((idleTime%60000)/1000);
|
const idleSeconds=Math.floor((idleTime%60000)/1000);
|
||||||
const timeoutMinutes=Math.floor(effectiveTimeout/60000);
|
const timeoutMinutes=Math.floor(effectiveTimeout/60000);
|
||||||
const timeoutSeconds=Math.floor((effectiveTimeout%60000)/1000);
|
const timeoutSeconds=Math.floor((effectiveTimeout%60000)/1000);
|
||||||
console.log('[HOME] 🏠 IDLE: '+idleMinutes+'m '+idleSeconds+'s / '+timeoutMinutes+'m '+timeoutSeconds+'s');
|
const siteType=currentSiteDuration===-1?'HIDDEN':'MANUAL';
|
||||||
|
console.log('[HOME] 🏠 '+siteType+' IDLE: '+idleMinutes+'m '+idleSeconds+'s / '+timeoutMinutes+'m '+timeoutSeconds+'s');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(idleTime>=effectiveTimeout){
|
if(idleTime>=effectiveTimeout){
|
||||||
console.log('[HOME] 🔔 *** SHOWING PROMPT NOW ***');
|
console.log('[HOME] 🔔 *** SHOWING PROMPT NOW ('+
|
||||||
|
(currentSiteDuration===-1?'hidden tab':'manual site')+') ***');
|
||||||
showInactivityPrompt();
|
showInactivityPrompt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4250,7 +4292,7 @@ function rotateToNextSite(){
|
|||||||
if(found&&nextIdx!==currentIndex){
|
if(found&&nextIdx!==currentIndex){
|
||||||
currentIndex=nextIdx;
|
currentIndex=nextIdx;
|
||||||
attachView(currentIndex);
|
attachView(currentIndex);
|
||||||
inactivityExtensionUntil=0; // Clear extension when rotating to a new site
|
inactivityExtensionUntil=0;
|
||||||
console.log('[ROTATION] Extension cleared - rotated to new site');
|
console.log('[ROTATION] Extension cleared - rotated to new site');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4258,7 +4300,6 @@ function rotateToNextSite(){
|
|||||||
function attachView(i){
|
function attachView(i){
|
||||||
closeHTMLKeyboard();
|
closeHTMLKeyboard();
|
||||||
|
|
||||||
// Don't change views while locked out
|
|
||||||
if(isLockedOut){
|
if(isLockedOut){
|
||||||
console.log('[MAIN] Blocked attachView - screen is locked');
|
console.log('[MAIN] Blocked attachView - screen is locked');
|
||||||
return;
|
return;
|
||||||
@@ -4267,7 +4308,6 @@ function attachView(i){
|
|||||||
if(!mainWindow||!views[i]||showingHidden)return;
|
if(!mainWindow||!views[i]||showingHidden)return;
|
||||||
|
|
||||||
currentIndex=i;
|
currentIndex=i;
|
||||||
// Ensure view is added to window before setting as top (prevents "not attached" error after lockout)
|
|
||||||
try{
|
try{
|
||||||
mainWindow.addBrowserView(views[i]);
|
mainWindow.addBrowserView(views[i]);
|
||||||
}catch(e){
|
}catch(e){
|
||||||
@@ -4287,8 +4327,6 @@ function attachView(i){
|
|||||||
views[i].webContents.loadURL(configuredUrl);
|
views[i].webContents.loadURL(configuredUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Control pause button visibility based on site duration
|
|
||||||
// Show on rotation sites (duration > 0), hide on manual sites (duration = 0)
|
|
||||||
const siteDuration=parseInt(tabs[tabIdx].duration)||0;
|
const siteDuration=parseInt(tabs[tabIdx].duration)||0;
|
||||||
const shouldShow=enablePauseButton&&siteDuration>0;
|
const shouldShow=enablePauseButton&&siteDuration>0;
|
||||||
console.log('[MAIN] Sending pause-button-visibility to tab '+tabIdx+' ('+tabs[tabIdx].url+') - duration='+siteDuration+'s, shouldShow='+shouldShow);
|
console.log('[MAIN] Sending pause-button-visibility to tab '+tabIdx+' ('+tabs[tabIdx].url+') - duration='+siteDuration+'s, shouldShow='+shouldShow);
|
||||||
@@ -4307,7 +4345,7 @@ function nextTab(){
|
|||||||
currentIndex=(currentIndex+1)%views.length;
|
currentIndex=(currentIndex+1)%views.length;
|
||||||
attachView(currentIndex);
|
attachView(currentIndex);
|
||||||
markActivity();
|
markActivity();
|
||||||
inactivityExtensionUntil=0; // Clear extension on manual navigation
|
inactivityExtensionUntil=0;
|
||||||
console.log('[MANUAL] Extension cleared due to manual tab switch');
|
console.log('[MANUAL] Extension cleared due to manual tab switch');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4319,7 +4357,7 @@ function prevTab(){
|
|||||||
currentIndex=(currentIndex-1+views.length)%views.length;
|
currentIndex=(currentIndex-1+views.length)%views.length;
|
||||||
attachView(currentIndex);
|
attachView(currentIndex);
|
||||||
markActivity();
|
markActivity();
|
||||||
inactivityExtensionUntil=0; // Clear extension on manual navigation
|
inactivityExtensionUntil=0;
|
||||||
console.log('[MANUAL] Extension cleared due to manual tab switch');
|
console.log('[MANUAL] Extension cleared due to manual tab switch');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4353,7 +4391,6 @@ function returnToHome(){
|
|||||||
|
|
||||||
inactivityExtensionUntil=0;
|
inactivityExtensionUntil=0;
|
||||||
|
|
||||||
// Reset lockout timer on activity
|
|
||||||
if(enablePasswordProtection&&lockoutTimeout>0&&!isLockedOut){
|
if(enablePasswordProtection&&lockoutTimeout>0&&!isLockedOut){
|
||||||
lockoutActivityTime=Date.now();
|
lockoutActivityTime=Date.now();
|
||||||
}
|
}
|
||||||
@@ -4395,35 +4432,27 @@ function showInactivityPrompt(){
|
|||||||
promptWindow=null;
|
promptWindow=null;
|
||||||
|
|
||||||
if(minutes===-1){
|
if(minutes===-1){
|
||||||
// User clicked "No, go home"
|
|
||||||
inactivityExtensionUntil=0;
|
inactivityExtensionUntil=0;
|
||||||
|
|
||||||
// Reset lockout timer on activity
|
|
||||||
if(enablePasswordProtection&&lockoutTimeout>0&&!isLockedOut){
|
if(enablePasswordProtection&&lockoutTimeout>0&&!isLockedOut){
|
||||||
lockoutActivityTime=Date.now();
|
lockoutActivityTime=Date.now();
|
||||||
}
|
}
|
||||||
returnToHome();
|
returnToHome();
|
||||||
}else if(minutes===0){
|
}else if(minutes===0){
|
||||||
// User clicked "Yes, I'm still here" - reset everything
|
|
||||||
inactivityExtensionUntil=0;
|
inactivityExtensionUntil=0;
|
||||||
|
|
||||||
// Reset lockout timer on activity
|
|
||||||
if(enablePasswordProtection&&lockoutTimeout>0&&!isLockedOut){
|
if(enablePasswordProtection&&lockoutTimeout>0&&!isLockedOut){
|
||||||
lockoutActivityTime=Date.now();
|
lockoutActivityTime=Date.now();
|
||||||
}
|
}
|
||||||
markActivity();
|
markActivity();
|
||||||
// CRITICAL: Reset site rotation timer so they stay on current page
|
|
||||||
siteStartTime=Date.now();
|
siteStartTime=Date.now();
|
||||||
console.log('[PROMPT] User confirmed presence - rotation timer reset');
|
console.log('[PROMPT] User confirmed presence - rotation timer reset');
|
||||||
}else{
|
}else{
|
||||||
// User selected a time extension
|
|
||||||
const now=Date.now();
|
const now=Date.now();
|
||||||
inactivityExtensionUntil=now+(minutes*60*1000);
|
inactivityExtensionUntil=now+(minutes*60*1000);
|
||||||
lastUserInteraction=now;
|
lastUserInteraction=now;
|
||||||
// CRITICAL: Reset site rotation timer during extension
|
|
||||||
siteStartTime=now;
|
siteStartTime=now;
|
||||||
|
|
||||||
// CRITICAL: Reset lockout timer during extension
|
|
||||||
if(enablePasswordProtection&&lockoutTimeout>0&&!isLockedOut){
|
if(enablePasswordProtection&&lockoutTimeout>0&&!isLockedOut){
|
||||||
lockoutActivityTime=now;
|
lockoutActivityTime=now;
|
||||||
console.log('[PROMPT] Lockout timer also reset during extension');
|
console.log('[PROMPT] Lockout timer also reset during extension');
|
||||||
@@ -4461,16 +4490,13 @@ function showPauseDialog(){
|
|||||||
pauseWindow=null;
|
pauseWindow=null;
|
||||||
|
|
||||||
if(minutes===0){
|
if(minutes===0){
|
||||||
// Cancel - do nothing
|
|
||||||
console.log('[PAUSE] Cancelled');
|
console.log('[PAUSE] Cancelled');
|
||||||
}else{
|
}else{
|
||||||
// Set extension time
|
|
||||||
const now=Date.now();
|
const now=Date.now();
|
||||||
inactivityExtensionUntil=now+(minutes*60*1000);
|
inactivityExtensionUntil=now+(minutes*60*1000);
|
||||||
lastUserInteraction=now;
|
lastUserInteraction=now;
|
||||||
siteStartTime=now;
|
siteStartTime=now;
|
||||||
|
|
||||||
// CRITICAL: Reset lockout timer during extension
|
|
||||||
if(enablePasswordProtection&&lockoutTimeout>0&&!isLockedOut){
|
if(enablePasswordProtection&&lockoutTimeout>0&&!isLockedOut){
|
||||||
lockoutActivityTime=now;
|
lockoutActivityTime=now;
|
||||||
console.log('[PAUSE] Lockout timer also reset during extension');
|
console.log('[PAUSE] Lockout timer also reset during extension');
|
||||||
@@ -4686,10 +4712,8 @@ function showPowerMenu(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show limited menu when locked out (no Reload option to prevent bypass)
|
|
||||||
if(isLockedOut){
|
if(isLockedOut){
|
||||||
console.log('[SECURITY] Showing limited power menu - system is locked out');
|
console.log('[SECURITY] Showing limited power menu - system is locked out');
|
||||||
// Show power menu on lockoutWindow (solid screen) instead of mainWindow
|
|
||||||
const targetWindow=(lockoutWindow&&!lockoutWindow.isDestroyed())?lockoutWindow:mainWindow;
|
const targetWindow=(lockoutWindow&&!lockoutWindow.isDestroyed())?lockoutWindow:mainWindow;
|
||||||
const r=dialog.showMessageBoxSync(targetWindow,{
|
const r=dialog.showMessageBoxSync(targetWindow,{
|
||||||
type:'question',
|
type:'question',
|
||||||
@@ -4705,7 +4729,6 @@ function showPowerMenu(){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Full menu when unlocked
|
|
||||||
const r=dialog.showMessageBoxSync(mainWindow,{
|
const r=dialog.showMessageBoxSync(mainWindow,{
|
||||||
type:'question',
|
type:'question',
|
||||||
buttons:['Shutdown','Restart','Reload','Cancel'],
|
buttons:['Shutdown','Restart','Reload','Cancel'],
|
||||||
@@ -4815,12 +4838,10 @@ function createWindow(){
|
|||||||
});
|
});
|
||||||
`).catch(()=>{});
|
`).catch(()=>{});
|
||||||
|
|
||||||
// Send pause button visibility on every page load to handle reloads
|
|
||||||
const siteDuration=parseInt(t.duration)||0;
|
const siteDuration=parseInt(t.duration)||0;
|
||||||
const shouldShow=enablePauseButton&&siteDuration>0;
|
const shouldShow=enablePauseButton&&siteDuration>0;
|
||||||
view.webContents.send('pause-button-visibility',shouldShow);
|
view.webContents.send('pause-button-visibility',shouldShow);
|
||||||
|
|
||||||
// Send keyboard button enabled status
|
|
||||||
view.webContents.send('keyboard-button-enabled',enableKeyboardButton);
|
view.webContents.send('keyboard-button-enabled',enableKeyboardButton);
|
||||||
console.log('[MAIN] Page loaded - sending keyboard-button-enabled: '+enableKeyboardButton);
|
console.log('[MAIN] Page loaded - sending keyboard-button-enabled: '+enableKeyboardButton);
|
||||||
console.log('[MAIN] Page loaded - resending pause-button-visibility: '+shouldShow+' for '+t.url);
|
console.log('[MAIN] Page loaded - resending pause-button-visibility: '+shouldShow+' for '+t.url);
|
||||||
@@ -4842,15 +4863,12 @@ function createWindow(){
|
|||||||
|
|
||||||
if(views.length){
|
if(views.length){
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
// Check for boot flag FIRST - require password on boot if configured
|
|
||||||
const bootFlag=path.join(__dirname,'.boot-flag');
|
const bootFlag=path.join(__dirname,'.boot-flag');
|
||||||
if(enablePasswordProtection&&lockoutPassword&&requirePasswordOnBoot&&fs.existsSync(bootFlag)){
|
if(enablePasswordProtection&&lockoutPassword&&requirePasswordOnBoot&&fs.existsSync(bootFlag)){
|
||||||
console.log('[LOCKOUT] Boot detected, requiring password BEFORE showing sites');
|
console.log('[LOCKOUT] Boot detected, requiring password BEFORE showing sites');
|
||||||
fs.unlinkSync(bootFlag);
|
fs.unlinkSync(bootFlag);
|
||||||
showLockoutScreen();
|
showLockoutScreen();
|
||||||
// Don't attach view or start timer - wait for password unlock
|
|
||||||
}else{
|
}else{
|
||||||
// Normal startup - show sites
|
|
||||||
attachView(startIndex);
|
attachView(startIndex);
|
||||||
startMasterTimer();
|
startMasterTimer();
|
||||||
}
|
}
|
||||||
@@ -4861,12 +4879,21 @@ function createWindow(){
|
|||||||
ipcMain.on('swipe-right',()=>{prevTab();});
|
ipcMain.on('swipe-right',()=>{prevTab();});
|
||||||
ipcMain.on('show-power-menu',showPowerMenu);
|
ipcMain.on('show-power-menu',showPowerMenu);
|
||||||
ipcMain.on('toggle-hidden',toggleHidden);
|
ipcMain.on('toggle-hidden',toggleHidden);
|
||||||
|
ipcMain.on('return-to-tabs',forceReturnToTabs);
|
||||||
ipcMain.on('user-activity',markActivity);
|
ipcMain.on('user-activity',markActivity);
|
||||||
ipcMain.on('show-keyboard',()=>{showHTMLKeyboard();});
|
ipcMain.on('show-keyboard',()=>{showHTMLKeyboard();});
|
||||||
ipcMain.on('close-keyboard',()=>{closeHTMLKeyboard();});
|
ipcMain.on('close-keyboard',()=>{closeHTMLKeyboard();});
|
||||||
ipcMain.on('keyboard-activity',()=>{markKeyboardActivity();});
|
ipcMain.on('keyboard-activity',()=>{markKeyboardActivity();});
|
||||||
ipcMain.on('show-pause-dialog',()=>{showPauseDialog();});
|
ipcMain.on('show-pause-dialog',()=>{showPauseDialog();});
|
||||||
ipcMain.on('check-lockout-password',(event,hash)=>{ if(hash===lockoutPassword){ unlockScreen(); }else{ if(lockoutWindow&&!lockoutWindow.isDestroyed()){ lockoutWindow.webContents.send('password-incorrect'); } } });
|
ipcMain.on('check-lockout-password',(event,hash)=>{
|
||||||
|
if(hash===lockoutPassword){
|
||||||
|
unlockScreen();
|
||||||
|
}else{
|
||||||
|
if(lockoutWindow&&!lockoutWindow.isDestroyed()){
|
||||||
|
lockoutWindow.webContents.send('password-incorrect');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.on('keyboard-type',(event,key)=>{
|
ipcMain.on('keyboard-type',(event,key)=>{
|
||||||
markKeyboardActivity();
|
markKeyboardActivity();
|
||||||
@@ -5657,21 +5684,29 @@ echo "[15/27] Creating inactivity prompt..."
|
|||||||
</html>
|
</html>
|
||||||
INACTHTML
|
INACTHTML
|
||||||
|
|
||||||
# echo "1234" | sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/.jitsi-pin" >/dev/null
|
echo "1234" | sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/.jitsi-pin" >/dev/null
|
||||||
# sudo -u "$KIOSK_USER" chmod 600 "$KIOSK_DIR/.jitsi-pin"
|
sudo -u "$KIOSK_USER" chmod 600 "$KIOSK_DIR/.jitsi-pin"
|
||||||
# log_success "Default PIN: 1234"
|
log_success "Default PIN: 1234"
|
||||||
###########################################################################
|
###########################################################################
|
||||||
############################start-preload##################################
|
############################start-preload##################################
|
||||||
###########################################################################
|
###########################################################################
|
||||||
sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/preload.js" > /dev/null <<'PRELOAD'
|
sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/preload.js" > /dev/null <<'PRELOAD'
|
||||||
const {contextBridge,ipcRenderer}=require('electron');
|
const {contextBridge,ipcRenderer}=require('electron');
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld('electronAPI',{
|
console.log('═════════════════════════════════════════════════════════════');
|
||||||
notifyActivity:()=>ipcRenderer.send('user-activity'),
|
console.log(' Gestures:');
|
||||||
showKeyboard:()=>ipcRenderer.send('show-keyboard'),
|
console.log(' 3-finger UP: Show hidden tabs (PIN required)');
|
||||||
closeKeyboard:()=>ipcRenderer.send('close-keyboard'),
|
console.log(' 3-finger DOWN: Return to normal tabs');
|
||||||
keyboardActivity:()=>ipcRenderer.send('keyboard-activity'),
|
console.log(' 2-finger HORIZONTAL: Switch between sites');
|
||||||
showPauseDialog:()=>ipcRenderer.send('show-pause-dialog')
|
console.log(' Keyboard: Auto-shows on text fields or click icon');
|
||||||
|
console.log('╚═══════════════════════════════════════════════════════════╝');
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
|
notifyActivity: () => ipcRenderer.send('user-activity'),
|
||||||
|
showKeyboard: () => ipcRenderer.send('show-keyboard'),
|
||||||
|
closeKeyboard: () => ipcRenderer.send('close-keyboard'),
|
||||||
|
keyboardActivity: () => ipcRenderer.send('keyboard-activity'),
|
||||||
|
showPauseDialog: () => ipcRenderer.send('show-pause-dialog')
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pause button state (MUST be outside DOMContentLoaded to persist across page loads)
|
// Pause button state (MUST be outside DOMContentLoaded to persist across page loads)
|
||||||
@@ -5924,32 +5959,28 @@ window.addEventListener('DOMContentLoaded',()=>{
|
|||||||
const absX=Math.abs(deltaX);
|
const absX=Math.abs(deltaX);
|
||||||
const absY=Math.abs(deltaY);
|
const absY=Math.abs(deltaY);
|
||||||
|
|
||||||
|
// 3-finger UP = show hidden tabs
|
||||||
if(fingerCount===3&&absY>SWIPE_THRESHOLD&&absX<SWIPE_TOLERANCE&&deltaY<0){
|
if(fingerCount===3&&absY>SWIPE_THRESHOLD&&absX<SWIPE_TOLERANCE&&deltaY<0){
|
||||||
ipcRenderer.send('toggle-hidden');
|
ipcRenderer.send('toggle-hidden');
|
||||||
}
|
}
|
||||||
else if(fingerCount===2&&absY>SWIPE_THRESHOLD&&absX<SWIPE_TOLERANCE&&deltaY>0){
|
// 3-finger DOWN = return to normal tabs (close hidden)
|
||||||
if(keyboardButtonEnabled){
|
else if(fingerCount===3&&absY>SWIPE_THRESHOLD&&absX<SWIPE_TOLERANCE&&deltaY>0){
|
||||||
keyboardAutoClosedThisSession=false;
|
console.log('[TOUCH] 3-finger DOWN - return to normal tabs');
|
||||||
if(keyboardVisible){
|
ipcRenderer.send('return-to-tabs');
|
||||||
ipcRenderer.send('close-keyboard');
|
|
||||||
}else{
|
|
||||||
ipcRenderer.send('show-keyboard');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// 2-finger HORIZONTAL = change tabs
|
||||||
else if(fingerCount===2&&absX>SWIPE_THRESHOLD&&absY<SWIPE_TOLERANCE){
|
else if(fingerCount===2&&absX>SWIPE_THRESHOLD&&absY<SWIPE_TOLERANCE){
|
||||||
ipcRenderer.send(deltaX>0?'swipe-right':'swipe-left');
|
ipcRenderer.send(deltaX>0?'swipe-right':'swipe-left');
|
||||||
}
|
}
|
||||||
else if(fingerCount===1&&absX<30&&absY<30){
|
// 1-finger HORIZONTAL = arrow keys
|
||||||
const target=document.elementFromPoint(touchEndX,touchEndY);
|
else if(fingerCount===1&&absX>SWIPE_THRESHOLD&&absY<SWIPE_TOLERANCE){
|
||||||
if(keyboardButtonEnabled&&target&&isTextInput(target)&&!keyboardVisible){
|
const key=deltaX>0?'ArrowRight':'ArrowLeft';
|
||||||
keyboardAutoClosedThisSession=false;
|
const keyCode=deltaX>0?39:37;
|
||||||
const now=Date.now();
|
['keydown','keyup'].forEach(eventType=>{
|
||||||
if(now-lastKeyboardRequest>KEYBOARD_REQUEST_THROTTLE){
|
document.dispatchEvent(new KeyboardEvent(eventType,{
|
||||||
lastKeyboardRequest=now;
|
key:key,code:key,keyCode:keyCode,which:keyCode,bubbles:true,cancelable:true
|
||||||
setTimeout(()=>ipcRenderer.send('show-keyboard'),50);
|
}));
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},{passive:true});
|
},{passive:true});
|
||||||
@@ -8637,9 +8668,9 @@ show_main_menu() {
|
|||||||
core_menu() {
|
core_menu() {
|
||||||
while true; do
|
while true; do
|
||||||
clear
|
clear
|
||||||
echo "════════════════════════════════════════════════════════════"
|
echo "══════════════════════════════════════════════════════════"
|
||||||
echo " CORE SETTINGS "
|
echo " CORE SETTINGS "
|
||||||
echo "╚════════════════════════════════════════════════════════════╝"
|
echo "══════════════════════════════════════════════════════════"
|
||||||
echo
|
echo
|
||||||
show_current_config
|
show_current_config
|
||||||
echo
|
echo
|
||||||
@@ -8652,11 +8683,12 @@ core_menu() {
|
|||||||
echo " 6. Power/Display/Quiet Hours"
|
echo " 6. Power/Display/Quiet Hours"
|
||||||
echo " 7. Optional Features (Pause/Keyboard)"
|
echo " 7. Optional Features (Pause/Keyboard)"
|
||||||
echo " 8. Password Protection & Lockout"
|
echo " 8. Password Protection & Lockout"
|
||||||
echo " 9. Full reinstall"
|
echo " 9. Hidden Site PIN" # <- ADD THIS
|
||||||
echo " 10. Complete uninstall"
|
echo " 10. Full reinstall"
|
||||||
|
echo " 11. Complete uninstall"
|
||||||
echo " 0. Return"
|
echo " 0. Return"
|
||||||
echo
|
echo
|
||||||
read -r -p "Choose [0-10]: " choice
|
read -r -p "Choose [0-11]: " choice # <- UPDATE THIS
|
||||||
|
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
1) configure_timezone; pause ;;
|
1) configure_timezone; pause ;;
|
||||||
@@ -8667,8 +8699,9 @@ core_menu() {
|
|||||||
6) configure_power_display_quiet ;;
|
6) configure_power_display_quiet ;;
|
||||||
7) load_existing_config; if configure_optional_features; then save_config; fi ;;
|
7) load_existing_config; if configure_optional_features; then save_config; fi ;;
|
||||||
8) load_existing_config; if configure_password_protection; then save_config; fi ;;
|
8) load_existing_config; if configure_password_protection; then save_config; fi ;;
|
||||||
9) full_reinstall; return ;;
|
9) configure_hidden_site_pin ;; # <- ADD THIS
|
||||||
10) complete_uninstall; return ;;
|
10) full_reinstall; return ;; # <- UPDATE THESE
|
||||||
|
11) complete_uninstall; return ;; # <- UPDATE THESE
|
||||||
0) return ;;
|
0) return ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
@@ -8850,7 +8883,7 @@ audio_diagnostics() {
|
|||||||
|
|
||||||
fix_squeezelite_audio() {
|
fix_squeezelite_audio() {
|
||||||
clear
|
clear
|
||||||
echo "╔══ FIX SQUEEZELITE AUDIO ══╗"
|
echo "═══ FIX SQUEEZELITE AUDIO ═══"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
echo "This will attempt to fix Squeezelite audio issues by:"
|
echo "This will attempt to fix Squeezelite audio issues by:"
|
||||||
|
|||||||
Reference in New Issue
Block a user