diff --git a/packages/app/src/app/agent/new.tsx b/packages/app/src/app/agent/new.tsx
index 9f08d48d1..be05705c1 100644
--- a/packages/app/src/app/agent/new.tsx
+++ b/packages/app/src/app/agent/new.tsx
@@ -972,14 +972,16 @@ export default function DraftAgentScreen() {
) : null}
-
+
+
+
@@ -1024,6 +1026,9 @@ const styles = StyleSheet.create((theme) => ({
contentContainer: {
flex: 1,
},
+ inputAreaWrapper: {
+ backgroundColor: theme.colors.background,
+ },
configScrollContent: {
flexGrow: 1,
},
diff --git a/packages/app/src/components/agent-input-area.tsx b/packages/app/src/components/agent-input-area.tsx
index 14db31a34..c32d29247 100644
--- a/packages/app/src/components/agent-input-area.tsx
+++ b/packages/app/src/components/agent-input-area.tsx
@@ -171,10 +171,6 @@ export function AgentInputArea({
const agentIdRef = useRef(agentId);
const sendAgentMessageRef = useRef(sendAgentMessage);
const onSubmitMessageRef = useRef(onSubmitMessage);
- const agentStatusRef = useRef(undefined);
- const updateQueueRef = useRef<
- ((updater: (current: QueuedMessage[]) => QueuedMessage[]) => void) | null
- >(null);
const submitMessage = useCallback(
async (text: string, images?: Array<{ uri: string; mimeType: string }>) => {
if (onSubmitMessageRef.current) {
@@ -190,33 +186,12 @@ export function AgentInputArea({
if (!text) {
return;
}
- const shouldQueue = agentStatusRef.current === "running";
- if (shouldQueue) {
- updateQueueRef.current?.((current) => [
- ...current,
- {
- id: generateMessageId(),
- text,
- },
- ]);
- return;
- }
- void (async () => {
- try {
- await submitMessage(text);
- } catch (error) {
- console.error("[AgentInput] Failed to send transcribed message:", error);
- updateQueueRef.current?.((current) => [
- ...current,
- {
- id: generateMessageId(),
- text,
- },
- ]);
- }
- })();
+ const current = userInput;
+ const shouldPad = current.length > 0 && !/\s$/.test(current);
+ const nextValue = `${current}${shouldPad ? " " : ""}${text}`;
+ setUserInput(nextValue);
},
- [submitMessage]
+ [setUserInput, userInput]
);
const handleDictationError = useCallback((error: Error) => {
@@ -225,24 +200,24 @@ export function AgentInputArea({
const canStartDictation = useCallback(() => {
const socketConnected = wsOrInert.getConnectionState ? wsOrInert.getConnectionState().isConnected : wsOrInert.isConnected;
- const allowed = !isRealtimeMode && socketConnected && Boolean(agent);
+ const allowed = !isRealtimeMode && socketConnected && (Boolean(agent) || Boolean(onSubmitMessage));
console.log("[AgentInput] canStartDictation", {
allowed,
isRealtimeMode,
wsConnected: socketConnected,
});
return allowed;
- }, [agent, isRealtimeMode, wsOrInert]);
+ }, [agent, isRealtimeMode, onSubmitMessage, wsOrInert]);
const canConfirmDictation = useCallback(() => {
const socketConnected = wsOrInert.getConnectionState ? wsOrInert.getConnectionState().isConnected : wsOrInert.isConnected;
- const allowed = socketConnected && Boolean(agent);
+ const allowed = socketConnected && (Boolean(agent) || Boolean(onSubmitMessage));
console.log("[AgentInput] canConfirmDictation", {
allowed,
wsConnected: socketConnected,
});
return allowed;
- }, [agent, wsOrInert]);
+ }, [agent, onSubmitMessage, wsOrInert]);
const {
isRecording: isDictating,
@@ -657,13 +632,22 @@ export function AgentInputArea({
});
}
+ const handleNativeSizerLayout = useCallback(
+ (event: { nativeEvent: { layout: { height: number } } }) => {
+ if (IS_WEB) {
+ return;
+ }
+ applyMeasuredHeight(event.nativeEvent.layout.height, "native-sizer");
+ },
+ [applyMeasuredHeight]
+ );
+
const isAgentRunning = agent?.status === "running";
- agentStatusRef.current = agent?.status;
const hasText = userInput.trim().length > 0;
const hasImages = selectedImages.length > 0;
const hasSendableContent = hasText || hasImages;
const shouldShowSendButton = !isAgentRunning && hasSendableContent;
- const shouldShowVoiceControls = !hasSendableContent;
+ const shouldShowVoiceControls = true;
const shouldHandleDesktopSubmit = IS_WEB;
const updateQueue = useCallback(
@@ -676,11 +660,6 @@ export function AgentInputArea({
},
[agentId, serverId, setQueuedMessages],
);
- useEffect(() => {
- updateQueueRef.current = updateQueue;
- }, [updateQueue]);
-
-
useLayoutEffect(() => {
if (!IS_WEB) {
return;
@@ -1003,6 +982,13 @@ export function AgentInputArea({
editable={!isDictating && wsOrInert.isConnected}
onKeyPress={shouldHandleDesktopSubmit ? handleDesktopSubmitKeyPress : undefined}
/>
+ {!IS_WEB && (
+
+
+ {userInput.length > 0 ? userInput : " "}
+
+
+ )}
{/* Button row below input */}
@@ -1058,27 +1044,30 @@ export function AgentInputArea({
)}
>
- ) : shouldShowSendButton ? (
-
- {isSubmitLoading ? (
-
- ) : (
-
+ ) : (
+ <>
+ {shouldShowSendButton && (
+
+ {isSubmitLoading ? (
+
+ ) : (
+
+ )}
+
)}
-
- ) : shouldShowVoiceControls ? (
- <>
- {voiceButton}
- {realtimeButton}
- >
- ) : null}
+ {shouldShowVoiceControls && voiceButton}
+ {realtimeButton}
+ >
+ )}
@@ -1197,6 +1186,20 @@ const styles = StyleSheet.create(((theme: any) => ({
}
: {}),
},
+ textInputSizerContainer: {
+ position: "absolute",
+ left: 0,
+ right: 0,
+ opacity: 0,
+ zIndex: -1,
+ },
+ textInputSizer: {
+ width: "100%",
+ paddingHorizontal: theme.spacing[4],
+ paddingVertical: theme.spacing[3],
+ fontSize: theme.fontSize.lg,
+ lineHeight: theme.fontSize.lg * 1.4,
+ },
buttonRow: {
flexDirection: "row",
alignItems: "center",
diff --git a/packages/app/src/hooks/use-dictation.ts b/packages/app/src/hooks/use-dictation.ts
index 794d54677..d1ae39241 100644
--- a/packages/app/src/hooks/use-dictation.ts
+++ b/packages/app/src/hooks/use-dictation.ts
@@ -320,6 +320,7 @@ export function useDictation(options: UseDictationOptions): UseDictationResult {
pendingRequestIdRef.current = null;
setPendingRequestId(null);
setIsProcessing(false);
+ setDuration(0);
setStatus("idle");
setRetryAttempt(0);
setRetryInfo(null);
@@ -493,7 +494,6 @@ export function useDictation(options: UseDictationOptions): UseDictationResult {
setError(null);
stopDurationTracking();
- setDuration(0);
setIsProcessing(true);
setRetryInfo(null);
setRetryAttempt(0);
@@ -566,6 +566,7 @@ export function useDictation(options: UseDictationOptions): UseDictationResult {
pendingRequestIdRef.current = null;
setPendingRequestId(null);
setIsProcessing(false);
+ setDuration(0);
setFailedRecording(null);
setStatus("idle");
setRetryAttempt(0);