Merge pull request #132 from outis1one/claude/optimistic-clarke-27gpk1
Back/forward buttons: adjustable opacity; Upgrade refreshes the Web UI copy
This commit is contained in:
@@ -249,7 +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)
|
||||
- **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
|
||||
- **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.
|
||||
- **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, where you can also set how solid they look (opacity 10–100%, default 50).
|
||||
|
||||
### On-Screen Keyboard
|
||||
- **HTML-based keyboard** with full QWERTY layout
|
||||
|
||||
+7
-2
@@ -61,6 +61,7 @@ let enablePauseButton=true;
|
||||
let enableKeyboardButton=true;
|
||||
let enableNavButton=true;
|
||||
let enableHistoryButtons=false;
|
||||
let historyButtonsOpacity=50;
|
||||
let enablePasswordProtection=false;
|
||||
let lockoutPassword="";
|
||||
let lockoutTimeout=0;
|
||||
@@ -104,6 +105,10 @@ function loadConfig(){
|
||||
enableKeyboardButton=(config.enableKeyboardButton!==false);
|
||||
enableNavButton=(config.enableNavButton!==false);
|
||||
enableHistoryButtons=(config.enableHistoryButtons===true);
|
||||
{
|
||||
const pct=Number(config.historyButtonsOpacity);
|
||||
historyButtonsOpacity=(Number.isInteger(pct)&&pct>=10&&pct<=100)?pct:50;
|
||||
}
|
||||
enablePasswordProtection=(config.enablePasswordProtection===true);
|
||||
lockoutPassword=config.lockoutPassword||"";
|
||||
lockoutTimeout=(config.lockoutTimeout||0)*60000; // Convert minutes to ms
|
||||
@@ -124,7 +129,7 @@ function loadConfig(){
|
||||
console.log('[CONFIG] Navigation:',allowNavigation);
|
||||
console.log('[CONFIG] Pause button:',enablePauseButton);
|
||||
console.log('[CONFIG] Keyboard button:',enableKeyboardButton);
|
||||
console.log('[CONFIG] Back/forward buttons:',enableHistoryButtons);
|
||||
console.log('[CONFIG] Back/forward buttons:',enableHistoryButtons,'opacity',historyButtonsOpacity+'%');
|
||||
console.log('[CONFIG] Password protection:',enablePasswordProtection);
|
||||
console.log('[CONFIG] Lockout timeout:',lockoutTimeout/60000,'minutes');
|
||||
if(lockoutAtTime)console.log('[CONFIG] Lock at time:',lockoutAtTime);
|
||||
@@ -1640,7 +1645,7 @@ async function createWindow(){
|
||||
console.log('[MAIN] Page loaded - sending keyboard-button-enabled: '+enableKeyboardButton);
|
||||
|
||||
view.webContents.send('nav-button-enabled',enableNavButton);
|
||||
view.webContents.send('history-buttons-enabled',enableHistoryButtons);
|
||||
view.webContents.send('history-buttons-config',{enabled:enableHistoryButtons,opacity:historyButtonsOpacity});
|
||||
console.log('[MAIN] Page loaded - sending nav-button-enabled: '+enableNavButton);
|
||||
|
||||
console.log('[MAIN] Page loaded - resending pause-button-visibility: '+shouldShow+' for '+t.url);
|
||||
|
||||
+13
-8
@@ -120,11 +120,16 @@ ipcRenderer.on('keyboard-button-enabled',(event,enabled)=>{
|
||||
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';
|
||||
let historyOpacity=0.5; // 0.1 - 1.0, from the "opacity" setting (percent)
|
||||
const historyHoverOpacity=()=>Math.min(1,historyOpacity+0.4);
|
||||
ipcRenderer.on('history-buttons-config',(event,cfg)=>{
|
||||
historyButtonsEnabled=!!(cfg&&cfg.enabled===true);
|
||||
const pct=Number(cfg&&cfg.opacity);
|
||||
historyOpacity=(pct>=10&&pct<=100)?pct/100:0.5;
|
||||
console.log('[HISTORY-BTN] Back/forward buttons enabled: '+historyButtonsEnabled+', opacity '+historyOpacity);
|
||||
if(historyBar){
|
||||
historyBar.style.opacity=String(historyOpacity);
|
||||
if(!historyButtonsEnabled)historyBar.style.display='none';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -275,7 +280,7 @@ window.addEventListener('DOMContentLoaded',()=>{
|
||||
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;
|
||||
display:none;gap:14px;z-index:999999;opacity:${historyOpacity};
|
||||
transition:opacity 0.15s;user-select:none;
|
||||
`;
|
||||
const makeButton=(label,direction,path)=>{
|
||||
@@ -299,8 +304,8 @@ window.addEventListener('DOMContentLoaded',()=>{
|
||||
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';});
|
||||
historyBar.addEventListener('mouseenter',()=>{historyBar.style.opacity=String(historyHoverOpacity());});
|
||||
historyBar.addEventListener('mouseleave',()=>{historyBar.style.opacity=String(historyOpacity);});
|
||||
document.body.appendChild(historyBar);
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -80,6 +80,7 @@ ENABLE_PAUSE_BUTTON="true"
|
||||
ENABLE_KEYBOARD_BUTTON="true"
|
||||
ENABLE_NAV_BUTTON="true"
|
||||
ENABLE_HISTORY_BUTTONS="false"
|
||||
HISTORY_BUTTONS_OPACITY=50
|
||||
ENABLE_PASSWORD_PROTECTION="false"
|
||||
LOCKOUT_PASSWORD=""
|
||||
LOCKOUT_TIMEOUT=0
|
||||
@@ -217,6 +218,9 @@ load_existing_config() {
|
||||
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"
|
||||
|
||||
HISTORY_BUTTONS_OPACITY=$(sudo -u "$KIOSK_USER" jq -r '.historyButtonsOpacity // 50' "$CONFIG_PATH" 2>/dev/null || echo "50")
|
||||
[[ "$HISTORY_BUTTONS_OPACITY" =~ ^[0-9]+$ ]] && (( HISTORY_BUTTONS_OPACITY >= 10 && HISTORY_BUTTONS_OPACITY <= 100 )) || HISTORY_BUTTONS_OPACITY=50
|
||||
|
||||
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"
|
||||
|
||||
@@ -309,6 +313,7 @@ save_config() {
|
||||
--argjson enableKeyboardButton "$keyboard_btn_json" \
|
||||
--argjson enableNavButton "$nav_btn_json" \
|
||||
--argjson enableHistoryButtons "$history_btn_json" \
|
||||
--argjson historyButtonsOpacity "${HISTORY_BUTTONS_OPACITY:-50}" \
|
||||
--argjson enablePasswordProtection "$password_json" \
|
||||
--arg lockoutPassword "${LOCKOUT_PASSWORD:-}" \
|
||||
--argjson lockoutTimeout "${LOCKOUT_TIMEOUT:-0}" \
|
||||
@@ -322,7 +327,7 @@ save_config() {
|
||||
--arg autheliaURL "${AUTHELIA_URL:-}" \
|
||||
--arg autheliaUsername "${AUTHELIA_USERNAME:-}" \
|
||||
--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,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"
|
||||
'. + {autoswitch:$autoswitch,enableTouch:$enableTouch,dualSwipe:$dualSwipe,swipeMode:$swipeMode,allowNavigation:$allowNavigation,homeTabIndex:$homeTabIndex,inactivityTimeout:$inactivityTimeout,enablePauseButton:$enablePauseButton,enableKeyboardButton:$enableKeyboardButton,enableNavButton:$enableNavButton,enableHistoryButtons:$enableHistoryButtons,historyButtonsOpacity:$historyButtonsOpacity,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
|
||||
for idx in "${!URLS[@]}"; do
|
||||
|
||||
@@ -121,6 +121,18 @@ action_upgrade() {
|
||||
provision_configure_display
|
||||
provision_configure_firewall
|
||||
provision_configure_power_management
|
||||
|
||||
# The Web UI runs from its own copy (/opt/kiosk-webui), not from
|
||||
# this checkout - refresh it too, or new settings never show up
|
||||
# there. Port, service and sudo rules are left as they are.
|
||||
if [[ -f "$SYSTEMD_DIR/kiosk-webui.service" ]]; then
|
||||
log_info "Refreshing the Web UI..."
|
||||
if webui_install_app_files; then
|
||||
sudo systemctl restart kiosk-webui || log_warning "Web UI copied but did not restart - run: sudo systemctl restart kiosk-webui"
|
||||
else
|
||||
log_warning "Couldn't refresh the Web UI - the Web UI keeps its old version"
|
||||
fi
|
||||
fi
|
||||
log_success "Setup refreshed"
|
||||
|
||||
echo
|
||||
|
||||
+16
-1
@@ -20,7 +20,7 @@ display_status() {
|
||||
echo "Pause button: $(onoff "$ENABLE_PAUSE_BUTTON")"
|
||||
echo "Keyboard button: $(onoff "$ENABLE_KEYBOARD_BUTTON")"
|
||||
echo "Navigation button: $(onoff "$ENABLE_NAV_BUTTON")"
|
||||
echo "Back/forward btns: $(onoff "$ENABLE_HISTORY_BUTTONS")"
|
||||
echo "Back/forward btns: $(onoff "$ENABLE_HISTORY_BUTTONS") (opacity ${HISTORY_BUTTONS_OPACITY}%)"
|
||||
}
|
||||
|
||||
display_menu_builder() {
|
||||
@@ -31,6 +31,7 @@ display_menu_builder() {
|
||||
"Toggle on-screen keyboard button (currently: $(onoff "$ENABLE_KEYBOARD_BUTTON"))"
|
||||
"Toggle navigation/help button (currently: $(onoff "$ENABLE_NAV_BUTTON"))"
|
||||
"Toggle on-screen back/forward buttons (currently: $(onoff "$ENABLE_HISTORY_BUTTONS"))"
|
||||
"Back/forward button opacity (currently: ${HISTORY_BUTTONS_OPACITY}%)"
|
||||
)
|
||||
MENU_HANDLERS=(
|
||||
action_set_touch_mode
|
||||
@@ -39,6 +40,7 @@ display_menu_builder() {
|
||||
action_toggle_keyboard_button
|
||||
action_toggle_nav_button
|
||||
action_toggle_history_buttons
|
||||
action_set_history_opacity
|
||||
)
|
||||
}
|
||||
|
||||
@@ -149,3 +151,16 @@ action_toggle_history_buttons() {
|
||||
fi
|
||||
save_config
|
||||
}
|
||||
|
||||
# How see-through the back/forward buttons are: 100 = solid, 10 = faint.
|
||||
# They get a little more solid while the mouse is over them.
|
||||
action_set_history_opacity() {
|
||||
echo
|
||||
echo "How solid the on-screen back/forward buttons look (10 = faint, 100 = solid)."
|
||||
HISTORY_BUTTONS_OPACITY=$(ask_integer "Opacity percent" "$HISTORY_BUTTONS_OPACITY" 10 100)
|
||||
save_config
|
||||
log_success "Back/forward button opacity: ${HISTORY_BUTTONS_OPACITY}%"
|
||||
if [[ "$ENABLE_HISTORY_BUTTONS" != "true" ]]; then
|
||||
log_info "The buttons are off - turn them on with the entry above."
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ const SCALAR_DEFAULTS = {
|
||||
enableKeyboardButton: true,
|
||||
enableNavButton: true,
|
||||
enableHistoryButtons: false,
|
||||
historyButtonsOpacity: 50,
|
||||
enablePasswordProtection: false,
|
||||
lockoutTimeout: 0,
|
||||
lockoutAtTime: '',
|
||||
|
||||
@@ -174,6 +174,7 @@ displayForm.addEventListener('submit', async (e) => {
|
||||
enableKeyboardButton: fd.get('enableKeyboardButton') === 'on',
|
||||
enableNavButton: fd.get('enableNavButton') === 'on',
|
||||
enableHistoryButtons: fd.get('enableHistoryButtons') === 'on',
|
||||
historyButtonsOpacity: parseInt(fd.get('historyButtonsOpacity'), 10),
|
||||
homeTabIndex: parseInt(fd.get('homeTabIndex'), 10),
|
||||
inactivityTimeoutMinutes: parseInt(fd.get('inactivityTimeoutMinutes'), 10),
|
||||
};
|
||||
@@ -284,6 +285,7 @@ function populateAll(config) {
|
||||
displayForm.elements.enableKeyboardButton.checked = !!config.enableKeyboardButton;
|
||||
displayForm.elements.enableNavButton.checked = !!config.enableNavButton;
|
||||
displayForm.elements.enableHistoryButtons.checked = !!config.enableHistoryButtons;
|
||||
displayForm.elements.historyButtonsOpacity.value = config.historyButtonsOpacity;
|
||||
updateHomeTabOptions(config.tabs);
|
||||
homeTabSelect.value = String(config.homeTabIndex);
|
||||
displayForm.elements.inactivityTimeoutMinutes.value = Math.round(config.inactivityTimeout / 60);
|
||||
|
||||
@@ -105,6 +105,10 @@
|
||||
<option value="-1">Disabled</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Back/forward button opacity (10 = faint, 100 = solid)
|
||||
<input type="number" name="historyButtonsOpacity" min="10" max="100" step="5">
|
||||
</label>
|
||||
<label>
|
||||
Inactivity timeout (minutes)
|
||||
<input type="number" name="inactivityTimeoutMinutes" min="1" max="240" step="1">
|
||||
|
||||
@@ -71,6 +71,11 @@ app.put('/api/config', (req, res) => {
|
||||
if (body.enableKeyboardButton !== undefined) patch.enableKeyboardButton = !!body.enableKeyboardButton;
|
||||
if (body.enableNavButton !== undefined) patch.enableNavButton = !!body.enableNavButton;
|
||||
if (body.enableHistoryButtons !== undefined) patch.enableHistoryButtons = !!body.enableHistoryButtons;
|
||||
if (body.historyButtonsOpacity !== undefined) {
|
||||
const pct = Number(body.historyButtonsOpacity);
|
||||
if (!Number.isInteger(pct) || pct < 10 || pct > 100) return badRequest(res, 'Button opacity must be 10-100 percent');
|
||||
patch.historyButtonsOpacity = pct;
|
||||
}
|
||||
|
||||
let tabCount;
|
||||
if (body.tabs !== undefined) {
|
||||
|
||||
@@ -81,6 +81,9 @@ check('enableHistoryButtons defaults off and round-trips', () => {
|
||||
saveConfig({ enableHistoryButtons: true });
|
||||
assert.strictEqual(JSON.parse(fs.readFileSync(process.env.CONFIG_PATH, 'utf8')).enableHistoryButtons, true);
|
||||
assert.strictEqual(loadConfig().enableHistoryButtons, true);
|
||||
assert.strictEqual(loadConfig().historyButtonsOpacity, 50);
|
||||
saveConfig({ historyButtonsOpacity: 80 });
|
||||
assert.strictEqual(loadConfig().historyButtonsOpacity, 80);
|
||||
});
|
||||
|
||||
check('saveConfig tabs: new password gets hashed, never stored/returned as plaintext', () => {
|
||||
|
||||
Reference in New Issue
Block a user