@@ -44,6 +44,7 @@ import androidx.compose.material.icons.rounded.Add
4444import androidx.compose.foundation.BorderStroke
4545import androidx.compose.foundation.shape.CircleShape
4646import androidx.compose.foundation.shape.RoundedCornerShape
47+ import androidx.compose.material.icons.automirrored.rounded.Undo
4748import androidx.compose.material3.AlertDialog
4849import androidx.compose.material3.Button
4950import androidx.compose.material3.ButtonDefaults
@@ -432,7 +433,22 @@ fun PhotoReasoningScreen(
432433 LazyColumn (state = listState, modifier = Modifier .fillMaxSize()) {
433434 items(messages) { message ->
434435 when (message.participant) {
435- PhotoParticipant .USER -> UserChatBubble (message.text, message.isPending, message.imageUris)
436+ PhotoParticipant .USER -> {
437+ // If index == 0, it's the first message, show the undo button
438+ val isFirstMessage = messages.indexOf(message) == 0
439+ UserChatBubble (
440+ text = message.text,
441+ isPending = message.isPending,
442+ imageUris = message.imageUris,
443+ showUndo = isFirstMessage,
444+ onUndoClicked = {
445+ // Set the text back to the input box
446+ onUserQuestionChanged(message.text)
447+ // Clear chat history
448+ onClearChatHistory()
449+ }
450+ )
451+ }
436452 PhotoParticipant .MODEL -> ModelChatBubble (message.text, message.isPending)
437453 PhotoParticipant .ERROR -> ErrorChatBubble (message.text)
438454 }
@@ -487,25 +503,26 @@ fun PhotoReasoningScreen(
487503 val showStopButton = modelName == " gemma-3n-e4b-it" || uiState is PhotoReasoningUiState .Loading
488504 Card (modifier = Modifier .fillMaxWidth()) {
489505 Column (modifier = Modifier .fillMaxWidth()) {
490- if (showStopButton) {
491- StopButton (onClick = onStopClicked)
492- }
493- Row (modifier = Modifier .padding(top = 16 .dp)) {
494- Column (modifier = Modifier .padding(all = 4 .dp).align(Alignment .CenterVertically )) {
495- IconButton (onClick = { pickMedia.launch(PickVisualMediaRequest (ActivityResultContracts .PickVisualMedia .ImageOnly )) }, modifier = Modifier .padding(bottom = 4 .dp)) {
496- Icon (Icons .Rounded .Add , stringResource(R .string.add_image))
506+ val isGenerating = (uiState is PhotoReasoningUiState .Loading ) && (messages.lastOrNull()?.isPending == true )
507+ val showTextFieldRow = ! isGenerating
508+
509+ if (showTextFieldRow) {
510+ Row (modifier = Modifier .padding(top = 16 .dp)) {
511+ Column (modifier = Modifier .padding(all = 4 .dp).align(Alignment .CenterVertically )) {
512+ IconButton (onClick = { pickMedia.launch(PickVisualMediaRequest (ActivityResultContracts .PickVisualMedia .ImageOnly )) }, modifier = Modifier .padding(bottom = 4 .dp)) {
513+ Icon (Icons .Rounded .Add , stringResource(R .string.add_image))
514+ }
515+ IconButton (onClick = onClearChatHistory, modifier = Modifier .padding(top = 4 .dp).drawBehind {
516+ drawCircle(color = Color .Black , radius = size.minDimension / 2 , style = androidx.compose.ui.graphics.drawscope.Stroke (width = 1 .dp.toPx()))
517+ }) { Text (" New" , style = MaterialTheme .typography.labelMedium, color = MaterialTheme .colorScheme.primary) }
497518 }
498- IconButton (onClick = onClearChatHistory, modifier = Modifier .padding(top = 4 .dp).drawBehind {
499- drawCircle(color = Color .Black , radius = size.minDimension / 2 , style = androidx.compose.ui.graphics.drawscope.Stroke (width = 1 .dp.toPx()))
500- }) { Text (" New" , style = MaterialTheme .typography.labelMedium, color = MaterialTheme .colorScheme.primary) }
501- }
502- OutlinedTextField (
503- value = userQuestion,
504- label = { Text (stringResource(R .string.reason_label)) },
505- placeholder = { Text (stringResource(R .string.reason_hint)) },
506- onValueChange = onUserQuestionChanged,
507- modifier = Modifier .weight(1f ).padding(end = 8 .dp)
508- )
519+ OutlinedTextField (
520+ value = userQuestion,
521+ label = { Text (stringResource(R .string.reason_label)) },
522+ placeholder = { Text (stringResource(R .string.reason_hint)) },
523+ onValueChange = onUserQuestionChanged,
524+ modifier = Modifier .weight(1f ).padding(end = 8 .dp)
525+ )
509526 IconButton (
510527 onClick = {
511528 val mainActivity = context as ? MainActivity
@@ -552,8 +569,14 @@ fun PhotoReasoningScreen(
552569 LazyRow (modifier = Modifier .padding(all = 8 .dp)) {
553570 items(imageUris) { uri -> AsyncImage (uri, null , Modifier .padding(4 .dp).requiredSize(72 .dp)) }
554571 }
555- } // Closes Card
556- }
572+ }
573+
574+ // Task 1: Stop button is independent and below the text field
575+ if (showStopButton) {
576+ StopButton (onClick = onStopClicked)
577+ }
578+ } // Closes Column
579+ } // Closes Card
557580 }
558581
559582 // Popups remain outside the main content flow, attached to the screen Column
@@ -1009,14 +1032,30 @@ fun OverwriteConfirmationDialog(
10091032fun UserChatBubble (
10101033 text : String ,
10111034 isPending : Boolean ,
1012- imageUris : List <String > = emptyList()
1035+ imageUris : List <String > = emptyList(),
1036+ showUndo : Boolean = false,
1037+ onUndoClicked : () -> Unit = {}
10131038) {
10141039 Row (
10151040 modifier = Modifier
10161041 .padding(vertical = 8 .dp, horizontal = 8 .dp)
10171042 .fillMaxWidth(),
10181043 verticalAlignment = Alignment .Top
10191044 ) {
1045+ if (showUndo) {
1046+ IconButton (
1047+ onClick = onUndoClicked,
1048+ modifier = Modifier
1049+ .padding(end = 8 .dp)
1050+ .align(Alignment .CenterVertically )
1051+ ) {
1052+ Icon (
1053+ imageVector = Icons .AutoMirrored .Rounded .Undo ,
1054+ contentDescription = " Undo" ,
1055+ tint = Color .Gray
1056+ )
1057+ }
1058+ }
10201059 Spacer (modifier = Modifier .weight(1f ))
10211060 Card (
10221061 shape = MaterialTheme .shapes.medium,
0 commit comments