When switching ports via the console dropdown:
- Input (keyboard, mouse, wheel) is suspended immediately to prevent
stray events from interfering with the OSCAR hotkey sequence
- A semi-transparent overlay with spinner and countdown timer appears
over the console canvas
- Duration is calculated from the actual key_pause_duration setting
multiplied by the number of * pause tokens in the port's hotkey,
plus a 500ms buffer
- Input resumes and overlay disappears when the timer expires
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On load, check sessionStorage for stored credentials (saved during
login). If present, auto-authenticate and skip the login form. Falls
back to showing login if the stored credentials fail.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three fixes for KVM port management:
1. HTML unescape scraped values — the > character in hotkey strings
(e.g., PrintScreen>0>1) gets entity-encoded to > in the device's
HTML. Added html_unescape() to the scraper so hotkeys round-trip
correctly.
2. Send hotkeys over WebSocket — port switching via the Belkin web form
only changes the active port number, it doesn't send the hotkey
sequence to the downstream KVM. Now when switching ports from the
console dropdown, blekin parses the Belkin hotkey syntax and sends
the key press/release sequence over the existing WebSocket connection.
3. New hotkey.ts parser — converts Belkin hotkey syntax to scancode
sequences:
- > and -> = sequential (tap each key)
- + = simultaneous (hold all, release in reverse)
- Supports all key names: PrintScreen, Ctrl, Alt, Shift, F1-F12,
letters, digits, navigation keys, numpad
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the port count dropdown changes, the table now re-renders with
the new number of rows, preserving existing values for ports that
were already visible. This allows configuring ports > 16 after
switching to a higher port count.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a "Send Key" dropdown menu to the console toolbar for keys that
browsers intercept before JavaScript can capture them (Print Screen,
Scroll Lock, Pause/Break). Also includes Escape, Tab, Caps Lock,
Num Lock, and Ctrl+Alt+Del.
Each menu item sends a press+release scancode pair directly over the
WebSocket, bypassing browser key capture entirely. This enables
activating downstream KVM switch menus (e.g., Avocent OSCAR via
Print Screen) from the blekin interface.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend (crates/ericrfb-proxy):
- Session cookie now persisted in AppState for device API calls
- New kvm.rs with three REST endpoints:
GET /api/kvm/ports — scrapes kvm.asp, returns port config as JSON
PUT /api/kvm/ports — saves port names, hotkeys, visibility, count
POST /api/kvm/switch — switches active KVM port via home2.asp
- HTML scraping extracts form values from predictable firmware HTML
Frontend (crates/ericrfb-frontend):
- New shell.ts: sidebar navigation with page routing pattern
(Console, Ports — extensible for Virtual Media, Users, etc.)
- Console refactored into pages/console.ts with mount/unmount lifecycle
- Port switcher dropdown in toolbar (fetches port list, switches on change)
- WebSocket auto-reconnects after port switch
- New pages/ports.ts: editable port configuration table
- Port count, key pause duration, per-port name/hotkey/show-in-console
- Save, reload, and per-port switch buttons
- Active port highlighted
- Dark theme sidebar with active state indicators
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The mapping was built assuming keynr followed physical keyboard order
with Escape=0. In reality, KeyTranslator.java maps Java VK_* codes to
keynr values with a different layout:
- keynr 0 = Backquote (not Escape)
- keynr 59 = Escape
- keynr 27 = Enter (not 40)
- keynr 40 = Backslash (not 42)
This caused the number row and QWERTY row to be off by 1 (Escape
was inserted at position 0, pushing everything). The home row
(CapsLock=28, A=29...L=37) happened to align by coincidence, which
is why 'g'(33) worked but 'r'(should be 18, was 19=T) didn't.
Also fixes: F1-F12 off by 1, PrintScreen/ScrollLock/Pause off by 1,
numpad operator keys swapped with numpad 7/8/9.
Corrected both Rust (input.rs) and TypeScript (input.ts) mappings
to match the authoritative KeyTranslator.java VK→keynr table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Frontend reconnection:
- WebSocket auto-reconnects with exponential backoff (1s → 30s)
- Re-authenticates with OmniView to get fresh APPLET_ID on reconnect
- Credentials stored in sessionStorage for automatic re-login
- Status bar shows connection state and reconnect countdown
- Disconnect button returns to login screen
Encoding 10 (Raw with tile interleave):
- codec/raw_tile.rs: decodes encoding 10 per ByteColorRFBRenderer.for()
- Flag byte bit 0 selects plain raw vs 16x16 tile-interleaved data
- Deinterleave handles edge tiles smaller than 16x16
- Wired into session dispatch
- 2 unit tests
39 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>