Skip to content

Terminal Touch Selection (Mobile)

The app's terminal renders through xterm.js onto a <canvas>. On a touch device the browser has no native way to select canvas text - a finger drag is consumed by viewport scrolling, and there are no native selection handles - so Hive provides its own selection layer that mimics the native Android experience.

This applies only to coarse-pointer (touch) devices. On desktop, right-click still opens the regular terminal context menu (Copy / Paste / Select All / Find / Clear) and selection works with the mouse as usual.

How to use it

  1. Long-press anywhere in the terminal. The word under your finger is selected (or the whole line when you press on trailing whitespace), two draggable handles appear at the selection ends, and a floating toolbar opens.
  2. Drag either handle to grow or shrink the selection. Dragging across rows extends the selection; dragging into the top or bottom edge auto-scrolls the viewport so you can reach off-screen content.
  3. Tap a toolbar action:
    • Copy - copies the selection to the system clipboard and exits.
    • Paste - sends the clipboard contents to the session (bracketed-paste wrapped for multi-line text) and exits. This is the touch equivalent of Ctrl+V, which is otherwise hard to reach on a phone.
    • Select All - selects the entire scrollback buffer.
    • Cancel - clears the selection and exits.
  4. Tapping anywhere outside the handles or toolbar also exits selection mode.

Leaving selection mode restores focus to the terminal, so the on-screen keyboard returns and you can keep typing.

Why a custom layer

A few mobile-specific problems this design solves:

  • No vanishing menu. A long-press on Android emits a trailing pointer/click event right after contextmenu fires. A naive "close on tap outside" menu catches that trailing event and dismisses itself instantly. The selection toolbar ignores dismiss taps for a short window after it opens, so it stays up.
  • Keyboard returns after copy. The async clipboard write and the toolbar taps move focus off xterm's hidden input. On exit the terminal is explicitly refocused so the keyboard reappears.
  • Real range selection. Instead of only auto-selecting a word, the handles let you select an arbitrary multi-row range.

Implementation notes

The selection layer lives in crates/hive-app/src/components/session/TerminalSelectionLayer.vue and is driven entirely by xterm.js public API - it does not touch xterm internals:

  • The range is set with term.select(col, row, length). xterm's selection model wraps length across rows (computing the end column modulo cols), so a single flat cell count expresses an arbitrary multi-line span.
  • The canvas renderer draws the highlight; term.getSelection() yields the copy text; term.scrollLines() drives edge auto-scroll.
  • Selection boundaries are owned by the component in 0-based absolute buffer coordinates, and the handles are positioned with position: fixed recomputed each animation frame, so they follow scrolling and the soft-keyboard slide.

The component is mounted by PtyTerminal.vue, which enters touch-selection mode from the terminal's contextmenu (long-press) handler when isCoarsePointerDevice() is true.

Hive - remote AI coding agents over WebSocket.