/** * public/keybar.ts — Mobile touch key bar for terminal input (T10). * * Exports pure key-to-byte mappings and a mount function that binds DOM buttons * to onSend callback on touchstart (bypassing xterm to avoid soft keyboard popup). * * Key bytes (ARCHITECTURE §5): * Esc → \x1b (0x1B) * Shift+Tab → \x1b[Z * ↑ → \x1b[A * ↓ → \x1b[B * ← → \x1b[D * → → \x1b[C * Enter → \r (0x0D, not \n) * Ctrl+C → \x03 (0x03) * Tab → \t (0x09) */ import type { MountKeybar } from '../src/types.js' /** Key name → byte string mapping (pure data, for testing). */ export const KEY_MAP = { esc: '\x1b', shiftTab: '\x1b[Z', arrowUp: '\x1b[A', arrowDown: '\x1b[B', arrowLeft: '\x1b[D', arrowRight: '\x1b[C', enter: '\r', ctrlC: '\x03', tab: '\t', } as const export type KeyName = keyof typeof KEY_MAP /** Mount the key bar: create buttons in #keybar, bind touchstart → onSend(bytes) + preventDefault. */ export const mountKeybar: MountKeybar = (onSend) => { const keybarEl = document.getElementById('keybar') if (!keybarEl) return // Button labels and their key map entries const buttons: Array<{ label: string keyName: KeyName }> = [ { label: 'Esc', keyName: 'esc' }, { label: 'Shift+Tab', keyName: 'shiftTab' }, { label: '↑', keyName: 'arrowUp' }, { label: '↓', keyName: 'arrowDown' }, { label: '←', keyName: 'arrowLeft' }, { label: '→', keyName: 'arrowRight' }, { label: 'Enter', keyName: 'enter' }, { label: 'Ctrl+C', keyName: 'ctrlC' }, { label: 'Tab', keyName: 'tab' }, ] for (const { label, keyName } of buttons) { const btn = document.createElement('button') btn.textContent = label btn.classList.add('keybar-btn') btn.dataset.key = keyName const bytes = KEY_MAP[keyName] // touchstart: send immediately, prevent default to avoid soft keyboard btn.addEventListener('touchstart', (e) => { e.preventDefault() onSend(bytes) }) // For desktop testing/fallback: click also sends btn.addEventListener('click', (e) => { e.preventDefault() onSend(bytes) }) keybarEl.appendChild(btn) } }