feat(frontend): terminal pilotable au doigt sur téléphone (#69)
Lots 3 et 4 du ticket #69. Sans ça un agent CLI est indriveable depuis un téléphone : le clavier virtuel n'a ni Esc, ni Tab, ni Ctrl, ni flèches, donc on peut taper un prompt mais pas l'interrompre, compléter un chemin, sortir d'un éditeur ou rappeler l'historique. - `TerminalView` expose un `onReady(api)` optionnel (donc desktop inchangé, et inerte quand xterm ne monte pas). `api.send` passe par `term.input()` : le *même* chemin qu'une frappe réelle, donc le relais PTY, le comptage de lignes et la suspension du write-portal s'appliquent à l'identique. Écrire sur le handle aurait court-circuité le portal. - `TerminalKeyBar` (web-only) : Esc/Tab/Ctrl-C/Ctrl-D/flèches en chips tactiles 44px, scroll horizontal. Chaque tap annule le déplacement de focus et refocalise xterm — sinon le clavier virtuel se referme à chaque touche. - La cellule agent passe de `h-64` fixe (une letterbox d'~20 lignes) à `h-[60dvh]` sur téléphone, `sm:h-80` au-delà. - Tests : séquences d'octets émises, préservation du focus, état désactivé avant montage de xterm, et garde de non-régression sur la frontière — le client web ne monte aucun layout-grid/split/dock desktop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -27,7 +27,8 @@ import type {
|
||||
TerminalHandle,
|
||||
} from "@/ports";
|
||||
import { useGateways } from "@/app/di";
|
||||
import { TerminalView } from "@/features/terminals";
|
||||
import { TerminalView, type TerminalInputApi } from "@/features/terminals";
|
||||
import { TerminalKeyBar } from "./TerminalKeyBar";
|
||||
|
||||
interface WebAgentCellProps {
|
||||
/** Owning project id (resolved server-side by `agent.launch`). */
|
||||
@ -43,6 +44,10 @@ interface WebAgentCellProps {
|
||||
export function WebAgentCell({ projectId, agentId, cwd, nodeId }: WebAgentCellProps) {
|
||||
const { agent } = useGateways();
|
||||
const [sessionId, setSessionId] = useState<string | null>(null);
|
||||
// Input API of the mounted xterm (#69), used by the mobile key bar. Stays
|
||||
// `null` in headless renders where xterm cannot mount — the bar then renders
|
||||
// disabled rather than throwing.
|
||||
const [inputApi, setInputApi] = useState<TerminalInputApi | null>(null);
|
||||
|
||||
const open = useCallback(
|
||||
(options: OpenTerminalOptions, onData: (bytes: Uint8Array) => void): Promise<TerminalHandle> =>
|
||||
@ -62,15 +67,27 @@ export function WebAgentCell({ projectId, agentId, cwd, nodeId }: WebAgentCellPr
|
||||
);
|
||||
|
||||
return (
|
||||
<div data-testid="web-agent-cell" className="h-64 w-full">
|
||||
<TerminalView
|
||||
cwd={cwd}
|
||||
agentMode
|
||||
open={open}
|
||||
reattach={reattach}
|
||||
sessionId={sessionId}
|
||||
onSessionId={setSessionId}
|
||||
/>
|
||||
// #69 — the cell was a flat `h-64` (256px), which on a phone left the agent
|
||||
// a letterbox roughly 20 lines tall. It now takes 60% of the *dynamic*
|
||||
// viewport on phones (so the collapsing URL bar can't clip it) and keeps a
|
||||
// fixed, desktop-like height from `sm` up. `min-h-0` on the terminal row
|
||||
// lets it shrink inside the flex column instead of pushing the key bar off.
|
||||
<div
|
||||
data-testid="web-agent-cell"
|
||||
className="flex h-[60dvh] min-h-64 w-full flex-col overflow-hidden rounded-md border border-border sm:h-80"
|
||||
>
|
||||
<div className="min-h-0 flex-1">
|
||||
<TerminalView
|
||||
cwd={cwd}
|
||||
agentMode
|
||||
open={open}
|
||||
reattach={reattach}
|
||||
sessionId={sessionId}
|
||||
onSessionId={setSessionId}
|
||||
onReady={setInputApi}
|
||||
/>
|
||||
</div>
|
||||
<TerminalKeyBar api={inputApi} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user