From cd2a6657f6d7628e18617df8abce164f6023e792 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 13 May 2026 13:05:25 -0700 Subject: [PATCH 1/5] Update mobile selector labels and icons --- docs/specs/mobile-ui.md | 25 +++++++++++---------- lib/src/components/MobileTerminalUi.tsx | 29 +++++++++++++++++-------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/docs/specs/mobile-ui.md b/docs/specs/mobile-ui.md index 5b92e0e..4de48c6 100644 --- a/docs/specs/mobile-ui.md +++ b/docs/specs/mobile-ui.md @@ -89,11 +89,11 @@ Touch modes: | --- | --- | --- | --- | --- | | Gestures | `Gestures` | `HandPointingIcon` | Always available | Touch drags generate arrow keys. Drag left sends left, drag right sends right, drag up sends up, and drag down sends down. | | Text selection | `Select` | `CursorTextIcon` | Always available | Touches are reserved for terminal text selection and copy/paste. If the TUI is capturing mouse events, MouseTerm activates mouse override for the active pane. | -| Cursor | `Cursor` | `CursorClickIcon` | Only when the active TUI is capturing mouse events | Touches are passed through as terminal mouse/cursor input. | +| Mouse | `Mouse` | `CursorClickIcon` | Only when the active TUI is capturing mouse events | Touches are passed through as terminal mouse input. | Default touch mode is **Gestures**. -If Cursor mode is active and the active pane stops capturing mouse events, the +If Mouse mode is active and the active pane stops capturing mouse events, the selector must fall back to Gestures. ## 5. Keyboard Mode Selector @@ -106,17 +106,18 @@ Recent | Type | Draft | Keys ``` The selector must be self-labeling. It should use a compact left-side `Input` -label plus segmented text buttons. The label describes the reserve area's +label plus segmented buttons that include both an icon and a short mode label. +The label describes the reserve area's purpose without adding a longer instruction line. Keyboard modes: -| Mode | Reserve area content | -| --- | --- | -| Recent | The entire reserve area displays `Recent - WIP`. | -| Type | The reserve area focuses the hidden terminal input. Every typed key is echoed into the terminal as it happens. | -| Draft | The entire reserve area displays `Draft - WIP`. | -| Keys | The entire reserve area displays terminal key buttons. | +| Mode | Button label | Icon | Reserve area content | +| --- | --- | --- | --- | +| Recent | `Recent` | `ClockCounterClockwiseIcon` | The entire reserve area displays `Recent - WIP`. | +| Type | `Type` | `TextTIcon` | The reserve area focuses the hidden terminal input. Every typed key is echoed into the terminal as it happens. | +| Draft | `Draft` | `ArticleNyTimesIcon` | The entire reserve area displays `Draft - WIP`. | +| Keys | `Keys` | `KeyboardIcon` | The entire reserve area displays terminal key buttons. | Default keyboard mode is **Type**. @@ -231,7 +232,7 @@ Required interactions: * Tap key buttons in Keys mode. * Drag in Gestures mode to send arrow keys. * Use Text selection mode for terminal selection and copy/paste. -* Use Cursor mode for terminal mouse/cursor input when a TUI requests mouse reporting. +* Use Mouse mode for terminal mouse input when a TUI requests mouse reporting. Pane-content touches must never open the native keyboard. The pane content area may focus the terminal internally for key routing or mouse handling, but the @@ -270,7 +271,7 @@ Build exactly this: * Touch mode selector: ```text -Touch Gestures | Select | Cursor +Touch Gestures | Select | Mouse ``` * Keyboard mode selector: @@ -300,7 +301,7 @@ The prototype should answer these questions: 2. Is the touch mode selector understandable and reachable? 3. Are gesture arrows usable enough for command history and cursor movement? 4. Is text selection discoverable and reliable on mobile? -5. Is Cursor mode useful when a TUI captures mouse events? +5. Is Mouse mode useful when a TUI captures mouse events? 6. Does native keyboard Type mode feel acceptable for terminal text entry? 7. Does the stable keyboard reserve feel better than resizing the whole UI? 8. Is the UI too cramped in portrait orientation? diff --git a/lib/src/components/MobileTerminalUi.tsx b/lib/src/components/MobileTerminalUi.tsx index a04f1c6..7ef8cf8 100644 --- a/lib/src/components/MobileTerminalUi.tsx +++ b/lib/src/components/MobileTerminalUi.tsx @@ -10,15 +10,20 @@ import { type ReactNode, } from 'react'; import { + ArticleNyTimesIcon, + ClockCounterClockwiseIcon, CursorClickIcon, CursorTextIcon, HandPointingIcon, + KeyboardIcon, + TextTIcon, } from '@phosphor-icons/react'; import { clsx } from 'clsx'; export type MobileTerminalKeyboardMode = 'recent' | 'type' | 'draft' | 'keys'; export type MobileTerminalSection = MobileTerminalKeyboardMode; export type MobileTerminalTouchMode = 'gestures' | 'selection' | 'cursor'; +type PhosphorIcon = ComponentType<{ size?: number; weight?: 'regular' | 'bold' | 'duotone' | 'fill' }>; export const MOBILE_TERMINAL_KEY_SEQUENCES = { ctrlC: '\x03', @@ -50,11 +55,11 @@ const TERMINAL_KEYS: TerminalKey[] = [ { id: 'right', label: '\u2192', title: 'Right arrow' }, ]; -const KEYBOARD_MODES: { id: MobileTerminalKeyboardMode; label: string }[] = [ - { id: 'recent', label: 'Recent' }, - { id: 'type', label: 'Type' }, - { id: 'draft', label: 'Draft' }, - { id: 'keys', label: 'Keys' }, +const KEYBOARD_MODES: Array<{ id: MobileTerminalKeyboardMode; label: string; Icon: PhosphorIcon }> = [ + { id: 'recent', label: 'Recent', Icon: ClockCounterClockwiseIcon }, + { id: 'type', label: 'Type', Icon: TextTIcon }, + { id: 'draft', label: 'Draft', Icon: ArticleNyTimesIcon }, + { id: 'keys', label: 'Keys', Icon: KeyboardIcon }, ]; const TOUCH_MODES: Array<{ @@ -62,11 +67,11 @@ const TOUCH_MODES: Array<{ label: string; shortLabel: string; title: string; - Icon: ComponentType<{ size?: number; weight?: 'regular' | 'bold' | 'duotone' | 'fill' }>; + Icon: PhosphorIcon; }> = [ { id: 'gestures', label: 'Gestures', shortLabel: 'Gestures', title: 'Gestures: drags send arrow keys', Icon: HandPointingIcon }, { id: 'selection', label: 'Text selection', shortLabel: 'Select', title: 'Text selection: touches select terminal text', Icon: CursorTextIcon }, - { id: 'cursor', label: 'Cursor', shortLabel: 'Cursor', title: 'Cursor: touches send terminal mouse events', Icon: CursorClickIcon }, + { id: 'cursor', label: 'Mouse', shortLabel: 'Mouse', title: 'Mouse: touches send terminal mouse events', Icon: CursorClickIcon }, ]; export interface MobileTerminalUiProps { @@ -148,12 +153,14 @@ function KeyButton({ function KeyboardModeButton({ id, label, + Icon, selected, disabled, onSelect, }: { id: MobileTerminalKeyboardMode; label: string; + Icon: PhosphorIcon; selected: boolean; disabled: boolean; onSelect: (mode: MobileTerminalKeyboardMode) => void; @@ -167,14 +174,17 @@ function KeyboardModeButton({ aria-current={selected ? 'page' : undefined} onClick={() => onSelect(id)} className={clsx( - 'min-w-0 rounded px-1.5 py-1 font-mono text-xs leading-none transition-colors focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-focus-ring', + 'flex min-w-0 items-center justify-center gap-1 rounded px-1.5 py-1 font-mono text-xs leading-none transition-colors focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-focus-ring', 'disabled:pointer-events-none disabled:opacity-60', selected ? 'bg-header-active-bg text-header-active-fg shadow-[inset_0_0_0_1px_var(--color-focus-ring)]' : 'text-muted hover:bg-header-inactive-bg hover:text-foreground', )} > - {label} + + {label} ); } @@ -258,6 +268,7 @@ function KeyboardModeSelector({ key={item.id} id={item.id} label={item.label} + Icon={item.Icon} selected={item.id === mode} disabled={disabled} onSelect={onSelect} From eb4034f267e1e350ff5c5aa9990ac0cdff0c0f4f Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 13 May 2026 13:33:41 -0700 Subject: [PATCH 2/5] Tighten mobile selector spacing --- docs/specs/mobile-ui.md | 2 ++ lib/src/components/MobileTerminalUi.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/specs/mobile-ui.md b/docs/specs/mobile-ui.md index 4de48c6..8e2c45e 100644 --- a/docs/specs/mobile-ui.md +++ b/docs/specs/mobile-ui.md @@ -68,6 +68,8 @@ The mobile UI is split into fixed and flexible regions: The pane title and pane content come from the embedded `Wall` terminal pane. The mobile wrapper owns the two selectors and the fixed-height keyboard reserve. +The selector block should use one divider between the Touch and Input rows, with +no divider above Touch and no divider below Input. The root height must not be recalculated from `window.visualViewport` on every keyboard resize. The reserve area is intentionally stable so the terminal region diff --git a/lib/src/components/MobileTerminalUi.tsx b/lib/src/components/MobileTerminalUi.tsx index 7ef8cf8..bfea611 100644 --- a/lib/src/components/MobileTerminalUi.tsx +++ b/lib/src/components/MobileTerminalUi.tsx @@ -191,7 +191,7 @@ function KeyboardModeButton({ function SelectorLabel({ children }: { children: ReactNode }) { return ( -
+
{children}
); @@ -211,7 +211,7 @@ function TouchModeSelector({ return (
Touch
@@ -259,7 +259,7 @@ function KeyboardModeSelector({ return (
Input