fix: kiosk cursor, Esc dead-end, PWA prompt, password overlay, gamepad Enter
- Kiosk: show cursor when active (removed -nocursor from Xorg), unclutter hides after 3s idle. X11 on VT7 for Ctrl+Alt+F1/F7 switching. - Kiosk: keep getty@tty1 running so MOTD is accessible via Ctrl+Alt+F1 - Kiosk: disable Chromium password save overlay (--password-store=basic) - Esc: don't navigate back from top-level pages (dashboard, login, kiosk) to prevent dead-end at root redirect - PWA: suppress install prompt in kiosk mode (/kiosk path) - Gamepad: Enter in text fields moves focus to next element (submit button) instead of submitting the form Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
13b832fdd3
commit
e4bdc775e4
@ -1599,10 +1599,13 @@ CLAUDESVC
|
|||||||
# Not enabled by default; toggle via: sudo archipelago-kiosk enable/disable
|
# Not enabled by default; toggle via: sudo archipelago-kiosk enable/disable
|
||||||
cat > /mnt/target/usr/local/bin/archipelago-kiosk-launcher <<'KIOSKLAUNCHER'
|
cat > /mnt/target/usr/local/bin/archipelago-kiosk-launcher <<'KIOSKLAUNCHER'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Start X server in the background
|
# Start X server on VT7 (VT1 stays on MOTD/console)
|
||||||
/usr/bin/Xorg :0 -nocursor vt1 -nolisten tcp -keeptty &
|
/usr/bin/Xorg :0 vt7 -nolisten tcp -keeptty &
|
||||||
XPID=$!
|
XPID=$!
|
||||||
sleep 2
|
sleep 3
|
||||||
|
|
||||||
|
# Switch to kiosk display
|
||||||
|
chvt 7 2>/dev/null || true
|
||||||
|
|
||||||
if ! kill -0 $XPID 2>/dev/null; then
|
if ! kill -0 $XPID 2>/dev/null; then
|
||||||
echo 'ERROR: Xorg failed to start'
|
echo 'ERROR: Xorg failed to start'
|
||||||
@ -1639,6 +1642,8 @@ while true; do
|
|||||||
--disable-session-crashed-bubble \
|
--disable-session-crashed-bubble \
|
||||||
--disable-save-password-bubble \
|
--disable-save-password-bubble \
|
||||||
--disable-suggestions-service \
|
--disable-suggestions-service \
|
||||||
|
--password-store=basic \
|
||||||
|
--disable-features=TranslateUI,PasswordManagerOnboarding \
|
||||||
--disable-component-update \
|
--disable-component-update \
|
||||||
--user-data-dir=/home/archipelago/.config/chromium-kiosk
|
--user-data-dir=/home/archipelago/.config/chromium-kiosk
|
||||||
sleep 3
|
sleep 3
|
||||||
@ -1654,7 +1659,6 @@ Description=Archipelago Kiosk (X11 + Chromium)
|
|||||||
After=archipelago.service
|
After=archipelago.service
|
||||||
Wants=archipelago.service
|
Wants=archipelago.service
|
||||||
ConditionPathExists=/usr/local/bin/archipelago-kiosk-launcher
|
ConditionPathExists=/usr/local/bin/archipelago-kiosk-launcher
|
||||||
Conflicts=getty@tty1.service
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
|
|||||||
@ -43,7 +43,8 @@ let deferredPrompt: { prompt: () => Promise<{ outcome: string }> } | null = null
|
|||||||
const DISMISS_KEY = 'archipelago_pwa_install_dismissed'
|
const DISMISS_KEY = 'archipelago_pwa_install_dismissed'
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// Don't show if already dismissed this session or if already installed
|
// Don't show in kiosk mode, if already dismissed, or if already installed
|
||||||
|
if (window.location.pathname.startsWith('/kiosk')) return
|
||||||
if (sessionStorage.getItem(DISMISS_KEY) === '1') return
|
if (sessionStorage.getItem(DISMISS_KEY) === '1') return
|
||||||
if (window.matchMedia('(display-mode: standalone)').matches) return
|
if (window.matchMedia('(display-mode: standalone)').matches) return
|
||||||
if ((window.navigator as Navigator & { standalone?: boolean }).standalone) return
|
if ((window.navigator as Navigator & { standalone?: boolean }).standalone) return
|
||||||
|
|||||||
@ -149,6 +149,18 @@ export function useControllerNav(containerRef?: { value: HTMLElement | null }) {
|
|||||||
|
|
||||||
const target = e.target as HTMLElement
|
const target = e.target as HTMLElement
|
||||||
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {
|
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {
|
||||||
|
// Enter in text field: blur and move to next focusable element (e.g., submit button)
|
||||||
|
if (e.key === 'Enter' && target.tagName === 'INPUT' && (target as HTMLInputElement).type !== 'submit') {
|
||||||
|
e.preventDefault()
|
||||||
|
const root = containerRef?.value ?? document
|
||||||
|
const all = getFocusableElements(root)
|
||||||
|
const idx = all.indexOf(target as HTMLElement)
|
||||||
|
if (idx >= 0 && idx < all.length - 1) {
|
||||||
|
all[idx + 1].focus()
|
||||||
|
all[idx + 1].scrollIntoView({ block: 'nearest', behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
if (e.key !== 'Escape') return
|
if (e.key !== 'Escape') return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,9 +219,13 @@ export function useControllerNav(containerRef?: { value: HTMLElement | null }) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
playNavSound('back')
|
// Don't navigate back from top-level pages — it leads to a dead end
|
||||||
window.history.back()
|
const topLevel = ['/', '/dashboard', '/login', '/kiosk']
|
||||||
e.preventDefault()
|
if (!topLevel.some(p => route.path === p || route.path.startsWith('/dashboard'))) {
|
||||||
|
playNavSound('back')
|
||||||
|
window.history.back()
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user