mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-15 10:59:32 +01:00
fix(mobile): keep the keyboard up when an accessory key is pressed
An accessory key that acts on the terminal (arrows, Tab, Esc, Ctrl, scroll, clear input) took focus on press, so every tap blurred the terminal and the click handler had to focus it again: a blur/focus pair per tap that started a keyboard dismissal and shifted the toolbar each time. Such a key now cancels the mousedown default instead, the same pattern as the Attach input, so the terminal never loses focus on the press; the click-time refocus stays as the fallback. A first tap on clear/compact only arms the confirm and is treated the same way.
This commit is contained in:
1 parent
39077281d1
commit
d56b90b7c6
4 files changed
+127
-3
No files matched your search
+1
-1
@@ -76,7 +76,7 @@ Frontend JS modules have `@fileoverview` with `@dependency`/`@loadorder` tags. L
|
||||
|
||||
**WebSocket output-gap reconcile** (`_wsOutputGapSession`, app.js): output frames carry no sequence number, so an unintentional WS close while SSE stays up marks the session and the next open reconciles. ⚠️ The marker is cleared only once a repaint actually happened (`_markTerminalBufferReconciled()`, never in a `finally`). → [architecture-invariants#websocket-output-gap-reconcile](../../docs/architecture-invariants.md#websocket-output-gap-reconcile)
|
||||
|
||||
**Dismissing the on-screen keyboard** (`terminal-ui.js`): two gestures blur the terminal's hidden textarea. (1) `_installMobileKeyboardDismiss()`, a document `touchend` that must never fire inside `#terminalContainer` or on a control (`MOBILE_KEYBOARD_DISMISS_EXEMPT_SELECTOR`, via `closest()`; roving-tabindex items sit at `tabindex=-1`, so the grouped rail's are listed as `[role="treeitem"]`). (2) In `_handleMobileTerminalTap`, a second tap on inert `content` blurs; the prompt row keeps focus-then-position. ⚠️ A scroll also ends in `touchend`: both classifiers must share one threshold (`TAP_THRESHOLD` reads `MOBILE_KEYBOARD_DISMISS_TAP_SLOP`), and multi-touch is never a tap. ⚠️ CI cannot see the only test for (1): run `npm run test:mobile -- test/mobile/keyboard.test.ts` by hand and diff the FAIL list against master. → [architecture-invariants#dismissing-the-on-screen-keyboard](../../docs/architecture-invariants.md#dismissing-the-on-screen-keyboard)
|
||||
**Dismissing the on-screen keyboard** (`terminal-ui.js`): two gestures blur the terminal's hidden textarea. (1) `_installMobileKeyboardDismiss()`, a document `touchend` that must never fire inside `#terminalContainer` or on a control (`MOBILE_KEYBOARD_DISMISS_EXEMPT_SELECTOR`, via `closest()`; roving-tabindex items sit at `tabindex=-1`, so the grouped rail's are listed as `[role="treeitem"]`). (2) In `_handleMobileTerminalTap`, a second tap on inert `content` blurs; the prompt row keeps focus-then-position. ⚠️ A scroll also ends in `touchend`: both classifiers must share one threshold (`TAP_THRESHOLD` reads `MOBILE_KEYBOARD_DISMISS_TAP_SLOP`), and multi-touch is never a tap. ⚠️ An accessory key that keeps the keyboard up (`ACCESSORY_REFOCUS_ACTIONS`, keyboard-accessory.js) cancels its `mousedown` default, so the press does not blur the terminal; the click-time `terminal.focus()` stays as the fallback. Letting the tap blur and refocusing afterwards made every such key start a dismissal, a blur/focus pair per tap. A first tap on clear/compact only arms the confirm and is treated the same (`test/accessory-keeps-terminal-focus.test.ts`). ⚠️ CI cannot see the only test for (1): run `npm run test:mobile -- test/mobile/keyboard.test.ts` by hand and diff the FAIL list against master. → [architecture-invariants#dismissing-the-on-screen-keyboard](../../docs/architecture-invariants.md#dismissing-the-on-screen-keyboard)
|
||||
|
||||
**Phone toolbar: Enter replaces Shell** (post-1.8.0): inside `@media (max-width: 599px)` `btn-shell` is `display:none` and `btn-enter` takes its slot (`order: 4`); starting a shell moved into the Run dropdown (`Terminal / Shell` → `setRunMode('shell')` → `run()` → `runShell()`, button label "Run SH"). `runMode` is `z.string().max(20)` server-side, so new modes need no schema change. Desktop and tablet keep the green Run Shell button unchanged.
|
||||
|
||||
|
||||
@@ -647,6 +647,9 @@ const COMPOSER_INPUT_FRAME_LIMIT = 64 * 1024;
|
||||
const COMPOSER_PASTE_START = '\x1b[200~';
|
||||
const COMPOSER_PASTE_END = '\x1b[201~';
|
||||
|
||||
// Accessory keys that act on the terminal and leave the keyboard up.
|
||||
const ACCESSORY_REFOCUS_ACTIONS = new Set(['scroll-up', 'scroll-down', 'arrow-left', 'arrow-right', 'tab', 'shift-tab', 'shift-left', 'shift-right', 'ctrl', 'ctrl-o', 'opt-enter', 'esc', 'effort-max', 'clear-input']);
|
||||
|
||||
/**
|
||||
* KeyboardAccessoryBar - Quick action buttons shown above keyboard when typing.
|
||||
*/
|
||||
@@ -802,6 +805,21 @@ const KeyboardAccessoryBar = {
|
||||
this.syncCodexKeys();
|
||||
this._syncComposerDraftIndicator();
|
||||
|
||||
// A key that keeps the keyboard up must not take focus on press at all.
|
||||
// Letting the tap blur the terminal and refocusing it in the click handler
|
||||
// (kept below as the fallback) made every such key start a keyboard
|
||||
// dismissal: a blur/focus pair per tap. Same pattern as the Attach input
|
||||
// (image-input.js). A first tap on clear/compact only arms the confirm.
|
||||
this.element.addEventListener('mousedown', (e) => {
|
||||
const btn = e.target.closest('.accessory-btn');
|
||||
if (!btn) return;
|
||||
const action = btn.dataset.action;
|
||||
if (ACCESSORY_REFOCUS_ACTIONS.has(action) ||
|
||||
((action === 'clear' || action === 'compact') && this._confirmAction !== action)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// Add click handlers — preventDefault stops event from reaching terminal
|
||||
this.element.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('.accessory-btn');
|
||||
@@ -813,8 +831,7 @@ const KeyboardAccessoryBar = {
|
||||
this.handleAction(action, btn);
|
||||
|
||||
// Refocus terminal so keyboard stays open (tap blurs terminal → keyboard dismisses → toolbar shifts)
|
||||
const refocusActions = new Set(['scroll-up', 'scroll-down', 'arrow-left', 'arrow-right', 'tab', 'shift-tab', 'shift-left', 'shift-right', 'ctrl', 'ctrl-o', 'opt-enter', 'esc', 'effort-max', 'clear-input']);
|
||||
if (refocusActions.has(action) ||
|
||||
if (ACCESSORY_REFOCUS_ACTIONS.has(action) ||
|
||||
((action === 'clear' || action === 'compact') && this._confirmAction)) {
|
||||
if (typeof app !== 'undefined' && app.terminal) {
|
||||
app.terminal.focus();
|
||||
|
||||
Reference in new issue
Block a user