authorgravatar for 139932571+Claudesaul@users.noreply.github.comClaudesaul <139932571+Claudesaul@users.noreply.github.com> 2026-10-10 07:37:26-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-10 06:37:26-05:00
log02612c28564056ddb7ae6b06cedd31dafc235ffd
tree8ba7a31c30772a1daf4c63add9747fa2e7255aac
parent71f6f1db89a50f0108663ebf1724cf3a1a4134ed
signature Signed by PGP key B5690EEEBB952194

fix(mobile): allow starting a task with only an image (#17409)


4 files changed, 26 insertions(+), 4 deletions(-)

apps/mobile/src/features/threads/NewTaskDraftScreen.tsx+2-2
......@@ -1244,7 +1244,7 @@ export function NewTaskDraftScreen(props: {
12441244 if (
12451245 attachmentBlockReason !== null ||
12461246 !modelSelection ||
1247 initialMessageText.length === 0 ||
1247 (initialMessageText.length === 0 && draft.attachments.length === 0) ||
12481248 flow.submitting ||
12491249 (workspaceMode === "worktree" && !selectedBranchName)
12501250 ) {
......@@ -1397,7 +1397,7 @@ export function NewTaskDraftScreen(props: {
13971397 !modelUnavailable &&
13981398 Boolean(flow.selectedProject) &&
13991399 Boolean(flow.selectedModel) &&
1400 flow.prompt.trim().length > 0 &&
1400 (flow.prompt.trim().length > 0 || flow.attachments.length > 0) &&
14011401 isIncomingShareReady &&
14021402 !isImportingShare &&
14031403 !flow.submitting &&
apps/mobile/src/features/threads/new-task-flow-provider.tsx+2-1
......@@ -1105,7 +1105,8 @@ export function NewTaskFlowProvider(props: React.PropsWithChildren) {
11051105 selectedEnvironmentServerConfig,
11061106 draft.modelSelection ?? null,
11071107 ) ?? selectedModel;
1108 if (text.length === 0 || !draftModelSelection) {
1108 // A shared image or file is a task on its own; text is optional.
1109 if ((text.length === 0 && draft.attachments.length === 0) || !draftModelSelection) {
11091110 return null;
11101111 }
11111112 // A saved choice from before the project went no-project must not
apps/mobile/src/state/thread-outbox-model.ts+2-1
......@@ -247,7 +247,8 @@ export function isQueuedThreadCreationSendable(message: QueuedThreadMessage): bo
247247 if (!message.creation) {
248248 return false;
249249 }
250 if (message.text.trim().length === 0 || message.modelSelection === undefined) {
250 const hasContent = message.text.trim().length > 0 || message.attachments.length > 0;
251 if (!hasContent || message.modelSelection === undefined) {
251252 return false;
252253 }
253254 return message.creation.workspaceMode !== "worktree" || Boolean(message.creation.branch);
apps/mobile/src/state/thread-outbox.test.ts+20
......@@ -1430,6 +1430,26 @@ describe("thread outbox", () => {
14301430 false,
14311431 );
14321432 expect(isQueuedThreadCreationSendable(base)).toBe(false);
1433 expect(isQueuedThreadCreationSendable({ ...creationMessage, text: " " })).toBe(false);
1434 expect(
1435 isQueuedThreadCreationSendable({
1436 ...creationMessage,
1437 text: "",
1438 attachments: [
1439 {
1440 id: "image-1",
1441 type: "image",
1442 name: "photo.png",
1443 mimeType: "image/png",
1444 sizeBytes: 3,
1445 fileUri: "file:///documents/t3-composer-attachments/photo.png",
1446 previewUri: "file:///documents/t3-composer-attachments/photo.png",
1447 uploadedAttachmentId: "pending-photo-png",
1448 uploadEnvironmentId: EnvironmentId.make("environment-1"),
1449 },
1450 ],
1451 }),
1452 ).toBe(true);
14331453 });
14341454
14351455 it("retries transport failures but drops deterministic command failures", () => {