Merge pull request #131 from outis1one/claude/optimistic-clarke-27gpk1
Page back/forward (Alt+Left/Right, mouse side buttons, optional on-screen buttons) and CLAUDE.md
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
## No user data in the repo
|
||||||
|
|
||||||
|
Never write anything specific to a person's kiosk, network or accounts into
|
||||||
|
this repository - not in code, tests, fixtures, docs, comments, commit
|
||||||
|
messages, branch names or pull request text.
|
||||||
|
|
||||||
|
That includes:
|
||||||
|
|
||||||
|
- WiFi network names (SSIDs), passwords, keys and tokens
|
||||||
|
- MAC addresses, IP addresses, hostnames and interface names taken from a
|
||||||
|
real machine
|
||||||
|
- usernames, email addresses, PINs and passwords
|
||||||
|
- the URLs of someone's own sites or services
|
||||||
|
- log output, screenshots or terminal output pasted in by a user
|
||||||
|
|
||||||
|
When an example, test or fixture needs a value like these, use an obvious
|
||||||
|
placeholder (`ExampleNet`, `192.0.2.10`, `aa:bb:cc:dd:ee:ff`, `wlan0`,
|
||||||
|
`example.com`) instead of a real one. When debugging from output a user
|
||||||
|
shares, keep it in the conversation or the scratchpad, not in a file in this
|
||||||
|
repo, and leave it out of commit messages.
|
||||||
@@ -249,6 +249,7 @@ The kiosk machine still needs a working internet connection (ethernet, or WiFi c
|
|||||||
- **1-finger swipe** (dual mode) - Navigate within page (arrow keys)
|
- **1-finger swipe** (dual mode) - Navigate within page (arrow keys)
|
||||||
- **On-screen keyboard** - Auto-shows on text fields or click keyboard icon
|
- **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
|
- **Navigation menu** - Click key icon (top-left) to access site list and gesture cheat sheet
|
||||||
|
- **Back/forward buttons** (optional, off by default) - two half-transparent ◀ ▶ buttons at the top middle of the screen, for kiosks with no keyboard or mouse side buttons. They appear on a tap, click or mouse movement and fade after a few seconds. Turn on in Core Settings → Display & Interaction, or the Web UI.
|
||||||
|
|
||||||
### On-Screen Keyboard
|
### On-Screen Keyboard
|
||||||
- **HTML-based keyboard** with full QWERTY layout
|
- **HTML-based keyboard** with full QWERTY layout
|
||||||
@@ -775,7 +776,9 @@ smb://WORKGROUP/COMPUTER/PrinterName
|
|||||||
**Keyboard Shortcuts:**
|
**Keyboard Shortcuts:**
|
||||||
- `Ctrl+Tab` or `Ctrl+]` - Next tab
|
- `Ctrl+Tab` or `Ctrl+]` - Next tab
|
||||||
- `Ctrl+Shift+Tab` or `Ctrl+[` - Previous tab
|
- `Ctrl+Shift+Tab` or `Ctrl+[` - Previous tab
|
||||||
- `Alt+Right/Left` - Next/Previous tab
|
- `Ctrl+PageDown` / `Ctrl+PageUp` - Next/Previous tab (the standard browser keys)
|
||||||
|
- `Alt+Right` / `Alt+Left` - Page forward / back within the current site (the standard browser keys; they used to switch tabs)
|
||||||
|
- Mouse side buttons (back/forward) - the same page forward/back
|
||||||
- `F10` or `Ctrl+H` - Toggle hidden tabs
|
- `F10` or `Ctrl+H` - Toggle hidden tabs
|
||||||
- `Escape` - Return to normal tabs (from hidden)
|
- `Escape` - Return to normal tabs (from hidden)
|
||||||
- `Ctrl+Alt+Delete` or `Ctrl+Alt+P` - Power menu
|
- `Ctrl+Alt+Delete` or `Ctrl+Alt+P` - Power menu
|
||||||
|
|||||||
+41
-2
@@ -60,6 +60,7 @@ let allowNavigation='same-origin';
|
|||||||
let enablePauseButton=true;
|
let enablePauseButton=true;
|
||||||
let enableKeyboardButton=true;
|
let enableKeyboardButton=true;
|
||||||
let enableNavButton=true;
|
let enableNavButton=true;
|
||||||
|
let enableHistoryButtons=false;
|
||||||
let enablePasswordProtection=false;
|
let enablePasswordProtection=false;
|
||||||
let lockoutPassword="";
|
let lockoutPassword="";
|
||||||
let lockoutTimeout=0;
|
let lockoutTimeout=0;
|
||||||
@@ -102,6 +103,7 @@ function loadConfig(){
|
|||||||
enablePauseButton=(config.enablePauseButton!==false);
|
enablePauseButton=(config.enablePauseButton!==false);
|
||||||
enableKeyboardButton=(config.enableKeyboardButton!==false);
|
enableKeyboardButton=(config.enableKeyboardButton!==false);
|
||||||
enableNavButton=(config.enableNavButton!==false);
|
enableNavButton=(config.enableNavButton!==false);
|
||||||
|
enableHistoryButtons=(config.enableHistoryButtons===true);
|
||||||
enablePasswordProtection=(config.enablePasswordProtection===true);
|
enablePasswordProtection=(config.enablePasswordProtection===true);
|
||||||
lockoutPassword=config.lockoutPassword||"";
|
lockoutPassword=config.lockoutPassword||"";
|
||||||
lockoutTimeout=(config.lockoutTimeout||0)*60000; // Convert minutes to ms
|
lockoutTimeout=(config.lockoutTimeout||0)*60000; // Convert minutes to ms
|
||||||
@@ -122,6 +124,7 @@ function loadConfig(){
|
|||||||
console.log('[CONFIG] Navigation:',allowNavigation);
|
console.log('[CONFIG] Navigation:',allowNavigation);
|
||||||
console.log('[CONFIG] Pause button:',enablePauseButton);
|
console.log('[CONFIG] Pause button:',enablePauseButton);
|
||||||
console.log('[CONFIG] Keyboard button:',enableKeyboardButton);
|
console.log('[CONFIG] Keyboard button:',enableKeyboardButton);
|
||||||
|
console.log('[CONFIG] Back/forward buttons:',enableHistoryButtons);
|
||||||
console.log('[CONFIG] Password protection:',enablePasswordProtection);
|
console.log('[CONFIG] Password protection:',enablePasswordProtection);
|
||||||
console.log('[CONFIG] Lockout timeout:',lockoutTimeout/60000,'minutes');
|
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);
|
||||||
@@ -1145,6 +1148,27 @@ function returnToTabs(){
|
|||||||
markActivity();
|
markActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Page back/forward in the site that's on screen (Alt+Left / Alt+Right, the
|
||||||
|
// mouse's side buttons, and the optional on-screen buttons). Not while the
|
||||||
|
// screen is locked or a dialog (PIN, WiFi, power...) is open on top - the
|
||||||
|
// keys would silently navigate the page hidden behind it.
|
||||||
|
function historyGo(direction){
|
||||||
|
if(isLockedOut)return;
|
||||||
|
for(const w of [pinWindow,wifiWindow,promptWindow,pauseWindow]){
|
||||||
|
if(w&&!w.isDestroyed())return;
|
||||||
|
}
|
||||||
|
const view=showingHidden?hiddenViews[currentHiddenIndex]:views[currentIndex];
|
||||||
|
if(!view||!view.webContents||view.webContents.isDestroyed())return;
|
||||||
|
const wc=view.webContents;
|
||||||
|
const nav=wc.navigationHistory;
|
||||||
|
if(direction==='back'){
|
||||||
|
if(nav?nav.canGoBack():wc.canGoBack()){nav?nav.goBack():wc.goBack();}
|
||||||
|
}else{
|
||||||
|
if(nav?nav.canGoForward():wc.canGoForward()){nav?nav.goForward():wc.goForward();}
|
||||||
|
}
|
||||||
|
markActivity();
|
||||||
|
}
|
||||||
|
|
||||||
function showPinEntry(){
|
function showPinEntry(){
|
||||||
if(pinWindow&&!pinWindow.isDestroyed()){
|
if(pinWindow&&!pinWindow.isDestroyed()){
|
||||||
pinWindow.focus();
|
pinWindow.focus();
|
||||||
@@ -1526,6 +1550,11 @@ async function createWindow(){
|
|||||||
|
|
||||||
mainWindow.on('focus',()=>markActivity());
|
mainWindow.on('focus',()=>markActivity());
|
||||||
mainWindow.webContents.on('before-input-event',()=>markActivity());
|
mainWindow.webContents.on('before-input-event',()=>markActivity());
|
||||||
|
// Mouse side buttons (back/forward). Reported by the window on Linux.
|
||||||
|
mainWindow.on('app-command',(event,command)=>{
|
||||||
|
if(command==='browser-backward')historyGo('back');
|
||||||
|
else if(command==='browser-forward')historyGo('forward');
|
||||||
|
});
|
||||||
|
|
||||||
if(!tabs.length){
|
if(!tabs.length){
|
||||||
mainWindow.loadURL('data:text/html,<body>No Sites Configured</body>');
|
mainWindow.loadURL('data:text/html,<body>No Sites Configured</body>');
|
||||||
@@ -1611,6 +1640,7 @@ async function createWindow(){
|
|||||||
console.log('[MAIN] Page loaded - sending keyboard-button-enabled: '+enableKeyboardButton);
|
console.log('[MAIN] Page loaded - sending keyboard-button-enabled: '+enableKeyboardButton);
|
||||||
|
|
||||||
view.webContents.send('nav-button-enabled',enableNavButton);
|
view.webContents.send('nav-button-enabled',enableNavButton);
|
||||||
|
view.webContents.send('history-buttons-enabled',enableHistoryButtons);
|
||||||
console.log('[MAIN] Page loaded - sending 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);
|
console.log('[MAIN] Page loaded - resending pause-button-visibility: '+shouldShow+' for '+t.url);
|
||||||
@@ -1654,6 +1684,9 @@ async function createWindow(){
|
|||||||
else if(action==='reload'){app.relaunch();app.quit();}
|
else if(action==='reload'){app.relaunch();app.quit();}
|
||||||
});
|
});
|
||||||
ipcMain.on('toggle-hidden',toggleHidden);
|
ipcMain.on('toggle-hidden',toggleHidden);
|
||||||
|
ipcMain.on('history-nav',(event,direction)=>{
|
||||||
|
if(direction==='back'||direction==='forward')historyGo(direction);
|
||||||
|
});
|
||||||
ipcMain.on('return-to-tabs',forceReturnToTabs);
|
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();});
|
||||||
@@ -1843,9 +1876,15 @@ async function createWindow(){
|
|||||||
globalShortcut.register('Control+Shift+Tab',()=>{prevTab();});
|
globalShortcut.register('Control+Shift+Tab',()=>{prevTab();});
|
||||||
globalShortcut.register('Control+]',()=>{nextTab();});
|
globalShortcut.register('Control+]',()=>{nextTab();});
|
||||||
globalShortcut.register('Control+[',()=>{prevTab();});
|
globalShortcut.register('Control+[',()=>{prevTab();});
|
||||||
globalShortcut.register('Alt+Right',()=>{nextTab();});
|
// The standard browser keys for switching tabs. (Alt+Left/Right used to
|
||||||
globalShortcut.register('Alt+Left',()=>{prevTab();});
|
// switch sites; they are now page back/forward, below.)
|
||||||
|
globalShortcut.register('Control+PageDown',()=>{nextTab();});
|
||||||
|
globalShortcut.register('Control+PageUp',()=>{prevTab();});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Standard browser keys for page back/forward.
|
||||||
|
globalShortcut.register('Alt+Left',()=>{historyGo('back');});
|
||||||
|
globalShortcut.register('Alt+Right',()=>{historyGo('forward');});
|
||||||
|
|
||||||
globalShortcut.register('F10',toggleHidden);
|
globalShortcut.register('F10',toggleHidden);
|
||||||
globalShortcut.register('Control+H',toggleHidden);
|
globalShortcut.register('Control+H',toggleHidden);
|
||||||
|
|||||||
@@ -115,6 +115,19 @@ ipcRenderer.on('keyboard-button-enabled',(event,enabled)=>{
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// On-screen back/forward buttons (top middle, half transparent) - an
|
||||||
|
// option for screens with no keyboard or mouse side buttons.
|
||||||
|
let historyButtonsEnabled=false;
|
||||||
|
let historyBar=null;
|
||||||
|
let historyBarHideTimer=null;
|
||||||
|
ipcRenderer.on('history-buttons-enabled',(event,enabled)=>{
|
||||||
|
historyButtonsEnabled=enabled===true;
|
||||||
|
console.log('[HISTORY-BTN] Back/forward buttons enabled: '+historyButtonsEnabled);
|
||||||
|
if(historyBar&&!historyButtonsEnabled){
|
||||||
|
historyBar.style.display='none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcRenderer.on('nav-button-enabled',(event,enabled)=>{
|
ipcRenderer.on('nav-button-enabled',(event,enabled)=>{
|
||||||
navButtonEnabled=enabled;
|
navButtonEnabled=enabled;
|
||||||
console.log('[NAV-BTN] Navigation button enabled: '+enabled);
|
console.log('[NAV-BTN] Navigation button enabled: '+enabled);
|
||||||
@@ -253,6 +266,56 @@ window.addEventListener('DOMContentLoaded',()=>{
|
|||||||
document.body.appendChild(navButton);
|
document.body.appendChild(navButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Back/forward buttons, top middle. The corners are often all taken by the
|
||||||
|
// menu, pause, keyboard and power buttons, so these sit between them.
|
||||||
|
const HISTORY_BAR_HIDE_DELAY=5000;
|
||||||
|
function createHistoryBar(){
|
||||||
|
if(historyBar)return;
|
||||||
|
historyBar=document.createElement('div');
|
||||||
|
historyBar.id='electron-history-bar';
|
||||||
|
historyBar.style.cssText=`
|
||||||
|
position:fixed;top:12px;left:50%;transform:translateX(-50%);
|
||||||
|
display:none;gap:14px;z-index:999999;opacity:0.5;
|
||||||
|
transition:opacity 0.15s;user-select:none;
|
||||||
|
`;
|
||||||
|
const makeButton=(label,direction,path)=>{
|
||||||
|
const b=document.createElement('div');
|
||||||
|
b.title=label;
|
||||||
|
b.innerHTML='<svg width="30" height="30" viewBox="0 0 24 24" fill="white"><path d="'+path+'"/></svg>';
|
||||||
|
b.style.cssText=`
|
||||||
|
width:56px;height:56px;background:rgba(44,62,80,0.9);
|
||||||
|
border:2px solid rgba(255,255,255,0.9);border-radius:50%;
|
||||||
|
display:flex;align-items:center;justify-content:center;cursor:pointer;
|
||||||
|
`;
|
||||||
|
b.addEventListener('click',(e)=>{
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
ipcRenderer.send('history-nav',direction);
|
||||||
|
showHistoryBar();
|
||||||
|
});
|
||||||
|
return b;
|
||||||
|
};
|
||||||
|
historyBar.append(
|
||||||
|
makeButton('Back','back','M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z'),
|
||||||
|
makeButton('Forward','forward','M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z')
|
||||||
|
);
|
||||||
|
historyBar.addEventListener('mouseenter',()=>{historyBar.style.opacity='0.9';});
|
||||||
|
historyBar.addEventListener('mouseleave',()=>{historyBar.style.opacity='0.5';});
|
||||||
|
document.body.appendChild(historyBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showHistoryBar(){
|
||||||
|
if(!historyButtonsEnabled)return;
|
||||||
|
if(!historyBar)createHistoryBar();
|
||||||
|
if(!historyBar)return;
|
||||||
|
historyBar.style.display='flex';
|
||||||
|
if(historyBarHideTimer)clearTimeout(historyBarHideTimer);
|
||||||
|
historyBarHideTimer=setTimeout(()=>{
|
||||||
|
if(historyBar)historyBar.style.display='none';
|
||||||
|
historyBarHideTimer=null;
|
||||||
|
},HISTORY_BAR_HIDE_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
// Power button in top-right corner (follows same show/hide logic as nav button)
|
// Power button in top-right corner (follows same show/hide logic as nav button)
|
||||||
let powerButton=null;
|
let powerButton=null;
|
||||||
let powerButtonHideTimer=null;
|
let powerButtonHideTimer=null;
|
||||||
@@ -918,11 +981,23 @@ window.addEventListener('DOMContentLoaded',()=>{
|
|||||||
if(navButtonEnabled){
|
if(navButtonEnabled){
|
||||||
showNavButton();
|
showNavButton();
|
||||||
}
|
}
|
||||||
|
showHistoryBar();
|
||||||
|
|
||||||
// Always show power button on user interaction
|
// Always show power button on user interaction
|
||||||
showPowerButton();
|
showPowerButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Back/forward buttons also appear on plain mouse movement, for screens
|
||||||
|
// with a mouse but no touch (the other buttons wait for a click/keypress).
|
||||||
|
let lastHistoryMove=0;
|
||||||
|
document.addEventListener('mousemove',()=>{
|
||||||
|
if(!historyButtonsEnabled)return;
|
||||||
|
const now=Date.now();
|
||||||
|
if(now-lastHistoryMove<300)return;
|
||||||
|
lastHistoryMove=now;
|
||||||
|
showHistoryBar();
|
||||||
|
},{passive:true,capture:true});
|
||||||
|
|
||||||
// Show pause button on any user interaction
|
// Show pause button on any user interaction
|
||||||
const pauseButtonTriggers=['mousedown','touchstart','keydown'];
|
const pauseButtonTriggers=['mousedown','touchstart','keydown'];
|
||||||
pauseButtonTriggers.forEach(eventType=>{
|
pauseButtonTriggers.forEach(eventType=>{
|
||||||
|
|||||||
+10
-2
@@ -79,6 +79,7 @@ INACTIVITY_TIMEOUT=120
|
|||||||
ENABLE_PAUSE_BUTTON="true"
|
ENABLE_PAUSE_BUTTON="true"
|
||||||
ENABLE_KEYBOARD_BUTTON="true"
|
ENABLE_KEYBOARD_BUTTON="true"
|
||||||
ENABLE_NAV_BUTTON="true"
|
ENABLE_NAV_BUTTON="true"
|
||||||
|
ENABLE_HISTORY_BUTTONS="false"
|
||||||
ENABLE_PASSWORD_PROTECTION="false"
|
ENABLE_PASSWORD_PROTECTION="false"
|
||||||
LOCKOUT_PASSWORD=""
|
LOCKOUT_PASSWORD=""
|
||||||
LOCKOUT_TIMEOUT=0
|
LOCKOUT_TIMEOUT=0
|
||||||
@@ -203,7 +204,7 @@ load_existing_config() {
|
|||||||
ALLOW_NAVIGATION=$(sudo -u "$KIOSK_USER" jq -r '.allowNavigation // "same-origin"' "$CONFIG_PATH" 2>/dev/null || echo "same-origin")
|
ALLOW_NAVIGATION=$(sudo -u "$KIOSK_USER" jq -r '.allowNavigation // "same-origin"' "$CONFIG_PATH" 2>/dev/null || echo "same-origin")
|
||||||
SWIPE_MODE=$(sudo -u "$KIOSK_USER" jq -r '.swipeMode // "dual"' "$CONFIG_PATH" 2>/dev/null || echo "dual")
|
SWIPE_MODE=$(sudo -u "$KIOSK_USER" jq -r '.swipeMode // "dual"' "$CONFIG_PATH" 2>/dev/null || echo "dual")
|
||||||
|
|
||||||
local pause_btn keyboard_btn nav_btn password_enabled boot_password
|
local pause_btn keyboard_btn nav_btn history_btn password_enabled boot_password
|
||||||
pause_btn=$(sudo -u "$KIOSK_USER" jq -r '.enablePauseButton // true' "$CONFIG_PATH" 2>/dev/null)
|
pause_btn=$(sudo -u "$KIOSK_USER" jq -r '.enablePauseButton // true' "$CONFIG_PATH" 2>/dev/null)
|
||||||
[[ "$pause_btn" == "true" ]] && ENABLE_PAUSE_BUTTON="true" || ENABLE_PAUSE_BUTTON="false"
|
[[ "$pause_btn" == "true" ]] && ENABLE_PAUSE_BUTTON="true" || ENABLE_PAUSE_BUTTON="false"
|
||||||
|
|
||||||
@@ -213,6 +214,9 @@ load_existing_config() {
|
|||||||
nav_btn=$(sudo -u "$KIOSK_USER" jq -r '.enableNavButton // true' "$CONFIG_PATH" 2>/dev/null)
|
nav_btn=$(sudo -u "$KIOSK_USER" jq -r '.enableNavButton // true' "$CONFIG_PATH" 2>/dev/null)
|
||||||
[[ "$nav_btn" == "true" ]] && ENABLE_NAV_BUTTON="true" || ENABLE_NAV_BUTTON="false"
|
[[ "$nav_btn" == "true" ]] && ENABLE_NAV_BUTTON="true" || ENABLE_NAV_BUTTON="false"
|
||||||
|
|
||||||
|
history_btn=$(sudo -u "$KIOSK_USER" jq -r '.enableHistoryButtons // false' "$CONFIG_PATH" 2>/dev/null)
|
||||||
|
[[ "$history_btn" == "true" ]] && ENABLE_HISTORY_BUTTONS="true" || ENABLE_HISTORY_BUTTONS="false"
|
||||||
|
|
||||||
password_enabled=$(sudo -u "$KIOSK_USER" jq -r '.enablePasswordProtection // false' "$CONFIG_PATH" 2>/dev/null)
|
password_enabled=$(sudo -u "$KIOSK_USER" jq -r '.enablePasswordProtection // false' "$CONFIG_PATH" 2>/dev/null)
|
||||||
[[ "$password_enabled" == "true" ]] && ENABLE_PASSWORD_PROTECTION="true" || ENABLE_PASSWORD_PROTECTION="false"
|
[[ "$password_enabled" == "true" ]] && ENABLE_PASSWORD_PROTECTION="true" || ENABLE_PASSWORD_PROTECTION="false"
|
||||||
|
|
||||||
@@ -263,6 +267,9 @@ save_config() {
|
|||||||
local nav_btn_json="true"
|
local nav_btn_json="true"
|
||||||
[[ "$ENABLE_NAV_BUTTON" == "false" ]] && nav_btn_json="false"
|
[[ "$ENABLE_NAV_BUTTON" == "false" ]] && nav_btn_json="false"
|
||||||
|
|
||||||
|
local history_btn_json="false"
|
||||||
|
[[ "$ENABLE_HISTORY_BUTTONS" == "true" ]] && history_btn_json="true"
|
||||||
|
|
||||||
local password_json="false"
|
local password_json="false"
|
||||||
[[ "$ENABLE_PASSWORD_PROTECTION" == "true" ]] && password_json="true"
|
[[ "$ENABLE_PASSWORD_PROTECTION" == "true" ]] && password_json="true"
|
||||||
|
|
||||||
@@ -301,6 +308,7 @@ save_config() {
|
|||||||
--argjson enablePauseButton "$pause_btn_json" \
|
--argjson enablePauseButton "$pause_btn_json" \
|
||||||
--argjson enableKeyboardButton "$keyboard_btn_json" \
|
--argjson enableKeyboardButton "$keyboard_btn_json" \
|
||||||
--argjson enableNavButton "$nav_btn_json" \
|
--argjson enableNavButton "$nav_btn_json" \
|
||||||
|
--argjson enableHistoryButtons "$history_btn_json" \
|
||||||
--argjson enablePasswordProtection "$password_json" \
|
--argjson enablePasswordProtection "$password_json" \
|
||||||
--arg lockoutPassword "${LOCKOUT_PASSWORD:-}" \
|
--arg lockoutPassword "${LOCKOUT_PASSWORD:-}" \
|
||||||
--argjson lockoutTimeout "${LOCKOUT_TIMEOUT:-0}" \
|
--argjson lockoutTimeout "${LOCKOUT_TIMEOUT:-0}" \
|
||||||
@@ -314,7 +322,7 @@ save_config() {
|
|||||||
--arg autheliaURL "${AUTHELIA_URL:-}" \
|
--arg autheliaURL "${AUTHELIA_URL:-}" \
|
||||||
--arg autheliaUsername "${AUTHELIA_USERNAME:-}" \
|
--arg autheliaUsername "${AUTHELIA_USERNAME:-}" \
|
||||||
--arg autheliaEncryptedPassword "${AUTHELIA_ENCRYPTED_PASSWORD:-}" \
|
--arg autheliaEncryptedPassword "${AUTHELIA_ENCRYPTED_PASSWORD:-}" \
|
||||||
'. + {autoswitch:$autoswitch,enableTouch:$enableTouch,dualSwipe:$dualSwipe,swipeMode:$swipeMode,allowNavigation:$allowNavigation,homeTabIndex:$homeTabIndex,inactivityTimeout:$inactivityTimeout,enablePauseButton:$enablePauseButton,enableKeyboardButton:$enableKeyboardButton,enableNavButton:$enableNavButton,enablePasswordProtection:$enablePasswordProtection,lockoutPassword:$lockoutPassword,lockoutTimeout:$lockoutTimeout,lockoutAtTime:$lockoutAtTime,lockoutActiveStart:$lockoutActiveStart,lockoutActiveEnd:$lockoutActiveEnd,requirePasswordOnBoot:$requirePasswordOnBoot,lockoutCodeType:$lockoutCodeType,bootPassword:$bootPassword,bootCodeType:$bootCodeType,autheliaURL:$autheliaURL,autheliaUsername:$autheliaUsername,autheliaEncryptedPassword:$autheliaEncryptedPassword,tabs:[]}' > "$tmp"
|
'. + {autoswitch:$autoswitch,enableTouch:$enableTouch,dualSwipe:$dualSwipe,swipeMode:$swipeMode,allowNavigation:$allowNavigation,homeTabIndex:$homeTabIndex,inactivityTimeout:$inactivityTimeout,enablePauseButton:$enablePauseButton,enableKeyboardButton:$enableKeyboardButton,enableNavButton:$enableNavButton,enableHistoryButtons:$enableHistoryButtons,enablePasswordProtection:$enablePasswordProtection,lockoutPassword:$lockoutPassword,lockoutTimeout:$lockoutTimeout,lockoutAtTime:$lockoutAtTime,lockoutActiveStart:$lockoutActiveStart,lockoutActiveEnd:$lockoutActiveEnd,requirePasswordOnBoot:$requirePasswordOnBoot,lockoutCodeType:$lockoutCodeType,bootPassword:$bootPassword,bootCodeType:$bootCodeType,autheliaURL:$autheliaURL,autheliaUsername:$autheliaUsername,autheliaEncryptedPassword:$autheliaEncryptedPassword,tabs:[]}' > "$tmp"
|
||||||
|
|
||||||
if [[ ${#URLS[@]} -gt 0 ]]; then
|
if [[ ${#URLS[@]} -gt 0 ]]; then
|
||||||
for idx in "${!URLS[@]}"; do
|
for idx in "${!URLS[@]}"; do
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ display_status() {
|
|||||||
echo "Pause button: $(onoff "$ENABLE_PAUSE_BUTTON")"
|
echo "Pause button: $(onoff "$ENABLE_PAUSE_BUTTON")"
|
||||||
echo "Keyboard button: $(onoff "$ENABLE_KEYBOARD_BUTTON")"
|
echo "Keyboard button: $(onoff "$ENABLE_KEYBOARD_BUTTON")"
|
||||||
echo "Navigation button: $(onoff "$ENABLE_NAV_BUTTON")"
|
echo "Navigation button: $(onoff "$ENABLE_NAV_BUTTON")"
|
||||||
|
echo "Back/forward btns: $(onoff "$ENABLE_HISTORY_BUTTONS")"
|
||||||
}
|
}
|
||||||
|
|
||||||
display_menu_builder() {
|
display_menu_builder() {
|
||||||
@@ -29,6 +30,7 @@ display_menu_builder() {
|
|||||||
"Toggle pause button (currently: $(onoff "$ENABLE_PAUSE_BUTTON"))"
|
"Toggle pause button (currently: $(onoff "$ENABLE_PAUSE_BUTTON"))"
|
||||||
"Toggle on-screen keyboard button (currently: $(onoff "$ENABLE_KEYBOARD_BUTTON"))"
|
"Toggle on-screen keyboard button (currently: $(onoff "$ENABLE_KEYBOARD_BUTTON"))"
|
||||||
"Toggle navigation/help button (currently: $(onoff "$ENABLE_NAV_BUTTON"))"
|
"Toggle navigation/help button (currently: $(onoff "$ENABLE_NAV_BUTTON"))"
|
||||||
|
"Toggle on-screen back/forward buttons (currently: $(onoff "$ENABLE_HISTORY_BUTTONS"))"
|
||||||
)
|
)
|
||||||
MENU_HANDLERS=(
|
MENU_HANDLERS=(
|
||||||
action_set_touch_mode
|
action_set_touch_mode
|
||||||
@@ -36,6 +38,7 @@ display_menu_builder() {
|
|||||||
action_toggle_pause_button
|
action_toggle_pause_button
|
||||||
action_toggle_keyboard_button
|
action_toggle_keyboard_button
|
||||||
action_toggle_nav_button
|
action_toggle_nav_button
|
||||||
|
action_toggle_history_buttons
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,3 +134,18 @@ action_toggle_nav_button() {
|
|||||||
fi
|
fi
|
||||||
save_config
|
save_config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Page back/forward: Alt+Left / Alt+Right and the mouse's side buttons
|
||||||
|
# always work; this adds clickable buttons (top middle, half transparent,
|
||||||
|
# shown on mouse movement or a tap) for screens with no keyboard or mouse
|
||||||
|
# buttons. Off by default.
|
||||||
|
action_toggle_history_buttons() {
|
||||||
|
if [[ "$ENABLE_HISTORY_BUTTONS" == "true" ]]; then
|
||||||
|
ENABLE_HISTORY_BUTTONS="false"
|
||||||
|
log_warning "On-screen back/forward buttons disabled"
|
||||||
|
else
|
||||||
|
ENABLE_HISTORY_BUTTONS="true"
|
||||||
|
log_success "On-screen back/forward buttons enabled"
|
||||||
|
fi
|
||||||
|
save_config
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const SCALAR_DEFAULTS = {
|
|||||||
enablePauseButton: true,
|
enablePauseButton: true,
|
||||||
enableKeyboardButton: true,
|
enableKeyboardButton: true,
|
||||||
enableNavButton: true,
|
enableNavButton: true,
|
||||||
|
enableHistoryButtons: false,
|
||||||
enablePasswordProtection: false,
|
enablePasswordProtection: false,
|
||||||
lockoutTimeout: 0,
|
lockoutTimeout: 0,
|
||||||
lockoutAtTime: '',
|
lockoutAtTime: '',
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ displayForm.addEventListener('submit', async (e) => {
|
|||||||
enablePauseButton: fd.get('enablePauseButton') === 'on',
|
enablePauseButton: fd.get('enablePauseButton') === 'on',
|
||||||
enableKeyboardButton: fd.get('enableKeyboardButton') === 'on',
|
enableKeyboardButton: fd.get('enableKeyboardButton') === 'on',
|
||||||
enableNavButton: fd.get('enableNavButton') === 'on',
|
enableNavButton: fd.get('enableNavButton') === 'on',
|
||||||
|
enableHistoryButtons: fd.get('enableHistoryButtons') === 'on',
|
||||||
homeTabIndex: parseInt(fd.get('homeTabIndex'), 10),
|
homeTabIndex: parseInt(fd.get('homeTabIndex'), 10),
|
||||||
inactivityTimeoutMinutes: parseInt(fd.get('inactivityTimeoutMinutes'), 10),
|
inactivityTimeoutMinutes: parseInt(fd.get('inactivityTimeoutMinutes'), 10),
|
||||||
};
|
};
|
||||||
@@ -282,6 +283,7 @@ function populateAll(config) {
|
|||||||
displayForm.elements.enablePauseButton.checked = !!config.enablePauseButton;
|
displayForm.elements.enablePauseButton.checked = !!config.enablePauseButton;
|
||||||
displayForm.elements.enableKeyboardButton.checked = !!config.enableKeyboardButton;
|
displayForm.elements.enableKeyboardButton.checked = !!config.enableKeyboardButton;
|
||||||
displayForm.elements.enableNavButton.checked = !!config.enableNavButton;
|
displayForm.elements.enableNavButton.checked = !!config.enableNavButton;
|
||||||
|
displayForm.elements.enableHistoryButtons.checked = !!config.enableHistoryButtons;
|
||||||
updateHomeTabOptions(config.tabs);
|
updateHomeTabOptions(config.tabs);
|
||||||
homeTabSelect.value = String(config.homeTabIndex);
|
homeTabSelect.value = String(config.homeTabIndex);
|
||||||
displayForm.elements.inactivityTimeoutMinutes.value = Math.round(config.inactivityTimeout / 60);
|
displayForm.elements.inactivityTimeoutMinutes.value = Math.round(config.inactivityTimeout / 60);
|
||||||
|
|||||||
@@ -95,6 +95,7 @@
|
|||||||
<label class="checkbox"><input type="checkbox" name="enablePauseButton"> Pause button</label>
|
<label class="checkbox"><input type="checkbox" name="enablePauseButton"> Pause button</label>
|
||||||
<label class="checkbox"><input type="checkbox" name="enableKeyboardButton"> On-screen keyboard button</label>
|
<label class="checkbox"><input type="checkbox" name="enableKeyboardButton"> On-screen keyboard button</label>
|
||||||
<label class="checkbox"><input type="checkbox" name="enableNavButton"> Navigation/help button</label>
|
<label class="checkbox"><input type="checkbox" name="enableNavButton"> Navigation/help button</label>
|
||||||
|
<label class="checkbox"><input type="checkbox" name="enableHistoryButtons"> On-screen back/forward buttons (top middle)</label>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Home page</legend>
|
<legend>Home page</legend>
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ app.put('/api/config', (req, res) => {
|
|||||||
if (body.enablePauseButton !== undefined) patch.enablePauseButton = !!body.enablePauseButton;
|
if (body.enablePauseButton !== undefined) patch.enablePauseButton = !!body.enablePauseButton;
|
||||||
if (body.enableKeyboardButton !== undefined) patch.enableKeyboardButton = !!body.enableKeyboardButton;
|
if (body.enableKeyboardButton !== undefined) patch.enableKeyboardButton = !!body.enableKeyboardButton;
|
||||||
if (body.enableNavButton !== undefined) patch.enableNavButton = !!body.enableNavButton;
|
if (body.enableNavButton !== undefined) patch.enableNavButton = !!body.enableNavButton;
|
||||||
|
if (body.enableHistoryButtons !== undefined) patch.enableHistoryButtons = !!body.enableHistoryButtons;
|
||||||
|
|
||||||
let tabCount;
|
let tabCount;
|
||||||
if (body.tabs !== undefined) {
|
if (body.tabs !== undefined) {
|
||||||
|
|||||||
@@ -75,6 +75,14 @@ check('saveConfig merge preserves fields this app never tracks (the previously-f
|
|||||||
assert.strictEqual(onDisk.enableNavButton, false);
|
assert.strictEqual(onDisk.enableNavButton, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
check('enableHistoryButtons defaults off and round-trips', () => {
|
||||||
|
fs.rmSync(process.env.CONFIG_PATH, { force: true });
|
||||||
|
assert.strictEqual(loadConfig().enableHistoryButtons, false);
|
||||||
|
saveConfig({ enableHistoryButtons: true });
|
||||||
|
assert.strictEqual(JSON.parse(fs.readFileSync(process.env.CONFIG_PATH, 'utf8')).enableHistoryButtons, true);
|
||||||
|
assert.strictEqual(loadConfig().enableHistoryButtons, true);
|
||||||
|
});
|
||||||
|
|
||||||
check('saveConfig tabs: new password gets hashed, never stored/returned as plaintext', () => {
|
check('saveConfig tabs: new password gets hashed, never stored/returned as plaintext', () => {
|
||||||
const result = saveConfig({
|
const result = saveConfig({
|
||||||
tabs: [{ url: 'https://a.example.com', duration: 30, name: 'A', username: 'bob', password: 'hunter2' }],
|
tabs: [{ url: 'https://a.example.com', duration: 30, name: 'A', username: 'bob', password: 'hunter2' }],
|
||||||
|
|||||||
Reference in New Issue
Block a user