-
+
);
}
@@ -176,6 +214,94 @@ function PreviewSessionBody({ session, taskId }: { session: TaskSession; taskId:
);
}
+/**
+ * PassthroughComposer is the kandev-controlled compose box rendered alongside
+ * the PTY in passthrough mode. Submitting forwards the typed text via the
+ * onSubmit prop (which posts `message.add` over WS); the backend's
+ * `Executor.Prompt` routes passthrough sessions to PTY stdin so the CLI agent
+ * actually receives it. Enter submits; Shift+Enter inserts a newline.
+ */
+// Cap matches the `max-h-32` Tailwind class on the textarea below — 128 px.
+const COMPOSER_MAX_HEIGHT_PX = 128;
+
+export function PassthroughComposer({
+ onSubmit,
+}: {
+ onSubmit: (content: string) => Promise
;
+}) {
+ const [value, setValue] = useState("");
+ const [isSending, setIsSending] = useState(false);
+ const textareaRef = useRef(null);
+ const trimmed = value.trim();
+ const canSubmit = trimmed.length > 0 && !isSending;
+
+ // Auto-grow the textarea with content (Shift+Enter newlines, long pastes)
+ // up to the max-h-32 cap. Done in JS so it works in browsers without
+ // CSS field-sizing support.
+ useLayoutEffect(() => {
+ const el = textareaRef.current;
+ if (!el) return;
+ el.style.height = "auto";
+ el.style.height = `${Math.min(el.scrollHeight, COMPOSER_MAX_HEIGHT_PX)}px`;
+ }, [value]);
+
+ const submit = useCallback(async () => {
+ if (!canSubmit) return;
+ setIsSending(true);
+ try {
+ await onSubmit(trimmed);
+ setValue(""); // only clear on success — preserve text so user can retry on send failure
+ } catch {
+ // onSubmit (PreviewSessionBody.handleSendPassthroughMessage) already
+ // surfaced the error via toast; the user's typed value stays in the
+ // textarea intentionally so they can retry without retyping.
+ } finally {
+ setIsSending(false);
+ }
+ }, [canSubmit, onSubmit, trimmed]);
+
+ const handleKeyDown = useCallback(
+ (e: KeyboardEvent) => {
+ if (e.key === "Enter" && !e.shiftKey) {
+ e.preventDefault();
+ void submit();
+ }
+ },
+ [submit],
+ );
+
+ return (
+
+
+ );
+}
+
function RunningSpinner() {
return ;
}