diff --git a/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx b/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx index 181c257cbc17569fc3b8dbfb84800869b293141f..cfb12bfd72a89a0695dab77dce4151babd97f997 100644 --- a/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx +++ b/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx @@ -1244,7 +1244,7 @@ export function NewTaskDraftScreen(props: { if ( attachmentBlockReason !== null || !modelSelection || - initialMessageText.length === 0 || + (initialMessageText.length === 0 && draft.attachments.length === 0) || flow.submitting || (workspaceMode === "worktree" && !selectedBranchName) ) { @@ -1397,7 +1397,7 @@ export function NewTaskDraftScreen(props: { !modelUnavailable && Boolean(flow.selectedProject) && Boolean(flow.selectedModel) && - flow.prompt.trim().length > 0 && + (flow.prompt.trim().length > 0 || flow.attachments.length > 0) && isIncomingShareReady && !isImportingShare && !flow.submitting && diff --git a/apps/mobile/src/features/threads/new-task-flow-provider.tsx b/apps/mobile/src/features/threads/new-task-flow-provider.tsx index 9a523c6bb0b0b0479a9f64e222ce3553722bac43..9b0699c0f740ce6996192c5fc3ad78ef83399dd7 100644 --- a/apps/mobile/src/features/threads/new-task-flow-provider.tsx +++ b/apps/mobile/src/features/threads/new-task-flow-provider.tsx @@ -1105,7 +1105,8 @@ export function NewTaskFlowProvider(props: React.PropsWithChildren) { selectedEnvironmentServerConfig, draft.modelSelection ?? null, ) ?? selectedModel; - if (text.length === 0 || !draftModelSelection) { + // A shared image or file is a task on its own; text is optional. + if ((text.length === 0 && draft.attachments.length === 0) || !draftModelSelection) { return null; } // A saved choice from before the project went no-project must not diff --git a/apps/mobile/src/state/thread-outbox-model.ts b/apps/mobile/src/state/thread-outbox-model.ts index 57a788cf5188f87e733a085e064a801c38264b55..3c1527e04131f5302426cc498bbafe5b7808bec7 100644 --- a/apps/mobile/src/state/thread-outbox-model.ts +++ b/apps/mobile/src/state/thread-outbox-model.ts @@ -247,7 +247,8 @@ export function isQueuedThreadCreationSendable(message: QueuedThreadMessage): bo if (!message.creation) { return false; } - if (message.text.trim().length === 0 || message.modelSelection === undefined) { + const hasContent = message.text.trim().length > 0 || message.attachments.length > 0; + if (!hasContent || message.modelSelection === undefined) { return false; } return message.creation.workspaceMode !== "worktree" || Boolean(message.creation.branch); diff --git a/apps/mobile/src/state/thread-outbox.test.ts b/apps/mobile/src/state/thread-outbox.test.ts index cbaffbbe50419fd659861423a60655d27151dd62..7cf10884f2934501405bd614f71c491f69c0fcb4 100644 --- a/apps/mobile/src/state/thread-outbox.test.ts +++ b/apps/mobile/src/state/thread-outbox.test.ts @@ -1430,6 +1430,26 @@ describe("thread outbox", () => { false, ); expect(isQueuedThreadCreationSendable(base)).toBe(false); + expect(isQueuedThreadCreationSendable({ ...creationMessage, text: " " })).toBe(false); + expect( + isQueuedThreadCreationSendable({ + ...creationMessage, + text: "", + attachments: [ + { + id: "image-1", + type: "image", + name: "photo.png", + mimeType: "image/png", + sizeBytes: 3, + fileUri: "file:///documents/t3-composer-attachments/photo.png", + previewUri: "file:///documents/t3-composer-attachments/photo.png", + uploadedAttachmentId: "pending-photo-png", + uploadEnvironmentId: EnvironmentId.make("environment-1"), + }, + ], + }), + ).toBe(true); }); it("retries transport failures but drops deterministic command failures", () => {