import { LitElement, html } from 'https://unpkg.com/lit@2.7.5/index.js?module';
/**
*
* A compact on-screen keyboard for mobile devices. Emits 'key-press' events on window
* with detail { type: 'letter'|'navigate'|'backspace', value }
*/
export class MobileKeyboard extends LitElement {
static properties = {
// reflect so CSS attribute selectors can react to collapsed state
collapsed: { type: Boolean, reflect: true },
_isMobile: { type: Boolean, state: true },
_wideScreen: { type: Boolean, state: true },
};
// styles moved to webui/styles.css; render into light DOM so external CSS applies
constructor() {
super();
this.collapsed = true; // default collapsed; _onResize will flip for mobile
this._isMobile = false;
this._letters = 'abcdefghijklmnopqrstuvwxyz'.split('');
this._onResize = this._onResize.bind(this);
}
createRenderRoot() { return this; }
render() {
// simple QWERTY-like rows
const rows = [
'qwertyuiop'.split(''),
'asdfghjkl'.split(''),
'zxcvbnm'.split(''),
];
// compute the maximum number of columns across rows (account for backspace in second row now)
const counts = rows.map((r, idx) => r.length + (idx === 1 ? 1 : 0));
const arrowCols = 3; // reserve 3 columns on the right for [left][down][right]
const baseMax = Math.max(...counts, 10);
const maxCols = baseMax;
return html`
${html`
${this.collapsed ? '▲' : '▼'}
`}
${rows.map((r, idx) => {
// center the letter keys leaving the rightmost `arrowCols` for the arrow block
let rowClasses = 'row';
if (idx === 1) rowClasses += ' stagger'; // A row
if (idx === 2) rowClasses += ' stagger-deep'; // Z row needs a larger indent
return html`
${r.map(l => html``) }
${idx === 1 ? html`` : ''}
${Array.from({ length: arrowCols }).map((_, i) => {
if (idx === 2 && i === 1) return html``;
return html``;
})}
`;
})}
`;
} _emitLetter(l) {
this._vibrate();
this._emit({ type: 'letter', value: l });
}
_emitNavigate(dir) {
this._vibrate();
this._emit({ type: 'navigate', value: dir });
}
_emitBackspace() {
this._vibrate();
this._emit({ type: 'backspace' });
}
_emitSpace() {
this._vibrate();
this._emit({ type: 'letter', value: '' });
}
_emit(detail) {
window.dispatchEvent(new CustomEvent('key-press', { detail }));
}
_vibrate() {
// Use Vibration API for haptic feedback
try {
console.log('Attempting vibration... navigator.vibrate:', typeof navigator.vibrate);
if (navigator.vibrate) {
navigator.vibrate(10); // 10ms short buzz
console.log('Vibration sent!');
} else {
console.log('Vibration API not available on this device');
}
} catch (e) {
console.warn('Vibration API error:', e);
}
}
connectedCallback() {
super.connectedCallback();
window.addEventListener('resize', this._onResize);
this._onResize();
}
disconnectedCallback() {
super.disconnectedCallback();
window.removeEventListener('resize', this._onResize);
}
_onResize() {
const mobile = window.innerWidth <= 900;
this._isMobile = mobile;
this.classList.toggle('mobile', mobile);
this.classList.toggle('desktop', !mobile);
// decide wide-screen (landscape/tablet) to change layout behavior
const wide = (window.innerWidth / window.innerHeight) > 1.6;
this._wideScreen = wide;
this.classList.toggle('wide-screen', wide);
// collapsed default: expanded on mobile, collapsed on desktop
if (mobile) this.collapsed = false;
else this.collapsed = true;
}
_toggleCollapse() {
this.collapsed = !this.collapsed;
if (this.collapsed) this.setAttribute('collapsed', '');
else this.removeAttribute('collapsed');
}
}
customElements.define('mobile-keyboard', MobileKeyboard);