diff --git a/claude-design/SethMux_4-24-26.zip b/claude-design/SethMux_4-24-26.zip new file mode 100644 index 0000000..1e3b785 Binary files /dev/null and b/claude-design/SethMux_4-24-26.zip differ diff --git a/claude-design/design_handoff_sethmux_toolbar/README.md b/claude-design/design_handoff_sethmux_toolbar/README.md new file mode 100644 index 0000000..ab5d02b --- /dev/null +++ b/claude-design/design_handoff_sethmux_toolbar/README.md @@ -0,0 +1,145 @@ +# Handoff: sethmux mobile toolbar — Google Workspace dark refresh + compose bar + +## Overview + +This handoff covers two related changes to the sethmux web terminal's mobile UI (`static/toolbar.js`): + +1. **Theme refresh** — re-skin the existing 2-row mobile button bar to match the Google Workspace dark vocabulary (palette, typography, hairline borders, hover behavior) while keeping sethmux's orange `#D35400` as the accent identity. +2. **Mobile autocorrect workaround** — add an opt-in compose bar (third row, toggled by a "Type" button) that gives Gboard / iOS keyboards a real `` to operate on. Typed text is flushed to the terminal stdin on Enter / Send so swipe, autocorrect, and predictions all work despite xterm.js's per-keystroke input model. + +## About the Design Files + +The files in this bundle are **design references** — `toolbar.js` is the single source file to ship; the `preview*.html` files are visual mockups demonstrating intended look and behavior in a Chrome window and an Android phone frame, and the `*.jsx` files are throwaway frame components used only by the previews. + +The task is to drop the new `toolbar.js` into the existing sethmux project at `static/toolbar.js`, replacing the current file. No build step required; no other static files change. The previews are not meant to be served in production. + +## Fidelity + +**High-fidelity.** All colors, type sizes, spacings, radii, and behaviors are final. Implement to spec. + +## What changed in toolbar.js + +### Visual system (Google Workspace dark) + +| Token | Hex | Usage | +|------------------|-----------|-----------------------------------------------| +| toolbar bg | `#202124` | bar background | +| button surface | `#303134` | button face at rest | +| border | `#3c4043` | 1px hairlines, group dividers | +| primary text | `#e8eaed` | button labels | +| secondary text | `#9aa0a6` | mono-class buttons at rest | +| tertiary text | `#5f6368` | placeholder, disabled | +| accent (sethmux) | `#D35400` | active/toggled fill, send button, focus ring | +| accent at rest | `#f0a36b` | text color of `.hi` (orange-tinted) buttons | +| accent bg-1 | `#2a1f15` | `.hi` button background at rest | +| accent bg-2 | `#3a2a1a` | hover wash (subtle orange tint) | +| accent border | `#5a3a22` | `.hi` border at rest | +| success at rest | `#81c995` | Save button text | +| success filled | `#1e8e3e` | Save button after confirm | + +### Typography + +- Primary: `Roboto, 'Helvetica Neue', Arial, sans-serif`, 12px / 500, letter-spacing 0.1px +- Mono (chord and arrow keys, terminal input): `'Roboto Mono', 'SF Mono', ui-monospace, Menlo, Consolas, monospace`, 14px / 400 in the compose input, 12px / 400 on `.mono` buttons + +### Geometry + +- Bar padding: `6px 8px 7px` +- Button: `height: 32px`, `min-width: 40px`, `padding: 0 10px`, `border-radius: 4px`, `border: 1px solid #3c4043` +- Narrow-phone breakpoint at `max-width: 380px`: button `height: 30px`, `min-width: 34px`, `padding: 0 7px`, `font-size: 11.5px` +- Compose input: `height: 36px`, `padding: 0 10px`, `border-radius: 4px`, orange caret + orange focus border `#D35400` +- Send button: `height: 36px`, `min-width: 54px`, filled `#D35400` on text `#0a0a0a` +- Newline button (`↵`): same height as Send, `min-width: 38px`, neutral surface (`#303134` / `#3c4043`) +- Group dividers (`.sep`): 1px wide × 20px tall, `#3c4043`, 4px horizontal margin (2px on narrow phones) +- Bar shadow: `0 -1px 0 rgba(0,0,0,.4), 0 -8px 24px rgba(0,0,0,.35)` +- Transitions: `background .15s ease, border-color .15s ease, color .15s ease` (no bounces) + +### Button states + +- Default: `#303134` bg, `#3c4043` border, `#e8eaed` text +- Hover: `#3a2a1a` bg, `#fff` text +- Active (mouse-down): full orange fill `#D35400` on `#0a0a0a` +- `.hi` (accent at rest): orange-tinted bg `#2a1f15`, border `#5a3a22`, text `#f0a36b`. Hover deepens both +- `.on` (toggled, e.g. selection mode active, Type active): full `#D35400` fill, hover lightens to `#e26416` +- `.mono` (chord keys): mono font, `#9aa0a6` text → `#e8eaed` on hover +- `.grn` (Save): `#81c995` text at rest; `.grn.on` (post-confirm) fills `#1e8e3e` and shows "✓ Saved" for 1500ms + +### Layout / structure + +The bar is a single `position: fixed; bottom: 0` element with `flex-direction: column`. + +**Row 1** (always visible): +``` +[+ Tab] [Next] [Prev] | [^C] [^D] [Clr] | [Esc] [Tab] [▲] [▼] +``` +- `+ Tab` is `.hi`, the rest neutral, `^C ^D Esc Tab ▲ ▼` are `.mono`. + +**Row 2** (always visible): +``` +[Sel] [Paste] [Zoom] [Save] | [V.Spl] [H.Spl] [Pane] [Kill] | [Type] +``` +- `Sel`, `Paste`, `Type` are `.hi`. `Save` is `.grn`. The `Type` button is the new entry point for the compose bar. + +**Row 3 — compose** (only visible when `#mb` has class `composing`): +``` +[ input field ] [↵] [Send] +``` +- The input is full-width-flex, monospace, autocorrect on. `↵` and `Send` both flush. + +### Interactions / behavior + +**Send to stdin (`send(k)`):** unchanged. Tries `term._core.coreService.triggerDataEvent(k)` first, falls back to `term._core._onData.fire(k)`, then `term.input(k)`. Always re-focuses the terminal afterward. + +**Selection mode (`Sel`):** unchanged. Toggles `body.selmode` which adds `pointer-events: none` to `.xterm-screen` and forces text selection. While in selmode the body also gets `filter: brightness(.92)` for a subtle visual cue. Sending any key auto-exits selmode. + +**Paste:** unchanged. Reads `navigator.clipboard.readText()` and feeds the result through `send()`. Alerts if HTTPS clipboard is unavailable. + +**Save:** unchanged behavior — sends `\x01S` (`Ctrl-A S`, the tmux capture binding). Adds the `.on` class and label `✓ Saved` for 1500ms, then reverts. + +**Compose / Type (new):** +- Tapping `Type` toggles the `composing` class on `#mb`. +- On open, `#mb-compose` gets focused via `setTimeout(...,0)` so iOS/Android keyboards open reliably. +- Input attributes: `autocomplete="on" autocorrect="on" autocapitalize="sentences" spellcheck="true" enterkeyhint="send" inputmode="text"`. These are required — the whole point is to let the OS keyboard's correction layer mutate the input value, which xterm.js's hidden textarea cannot do. +- Pressing `Enter` (or tapping `Send` or `↵`) calls `flushCompose(true)`: reads the input value, clears it, sends the value as a string to stdin, then sends `\r`. Re-focuses the input so the user can keep typing. +- Other toolbar buttons remain active while composing. If a chord/arrow/etc. button is tapped with non-empty input text, the typed text is flushed first (preserving order), then the chord is sent. **The compose bar stays open**; the user is mid-thought. + +**Layout reflow (`relayout()`):** The terminal pane (`.xterm`) is sized via `height: calc(100vh - {barHeight + 4}px)` whenever the bar's height changes. This runs on: +- DOM mutations (existing `MutationObserver`) +- Window resize +- Toggling the `composing` class (called from `toggleType()`) + +The previous implementation hard-coded `calc(100vh - 92px)`; the new code measures `bar.offsetHeight` so opening the third row reflows correctly without overlap. + +**Helper textarea hardening:** unchanged. Still sets `autocomplete=off`, `autocorrect=off`, `autocapitalize=off`, `spellcheck=false` on `.xterm-helper-textarea` once it appears. This is the standard xterm.js mitigation and is still useful — it prevents Gboard from trying to munge the per-keystroke stream when the user happens to type *outside* the compose bar. + +### Mobile breakpoint + +The toolbar shows at `@media (max-width: 900px)`. Unchanged. Above that, sethmux assumes a real keyboard and the bar stays hidden. + +## Why the compose bar is necessary + +xterm.js reads from a hidden `