| .. | .. |
|---|
| 874 | 874 | textCaption = ''; |
|---|
| 875 | 875 | } |
|---|
| 876 | 876 | |
|---|
| 877 | | - // Send everything as a single atomic bundle — the server will compose |
|---|
| 878 | | - // all attachments + caption into one terminal input (like dropping files + text) |
|---|
| 879 | 877 | final attachments = encodedImages.map((b64) => |
|---|
| 880 | 878 | <String, dynamic>{'data': b64, 'mimeType': 'image/jpeg'} |
|---|
| 881 | 879 | ).toList(); |
|---|
| 882 | 880 | |
|---|
| 883 | | - _ws?.send({ |
|---|
| 884 | | - 'type': 'bundle', |
|---|
| 885 | | - 'caption': textCaption, |
|---|
| 886 | | - if (voiceB64 != null) 'audioBase64': voiceB64, |
|---|
| 887 | | - 'attachments': attachments, |
|---|
| 888 | | - 'sessionId': targetSessionId, |
|---|
| 889 | | - }); |
|---|
| 890 | | - |
|---|
| 891 | | - // If voice caption, also send separately for Whisper transcription |
|---|
| 881 | + // Create voice bubble first to get messageId for transcript reflection |
|---|
| 882 | + String? voiceMessageId; |
|---|
| 892 | 883 | if (voiceB64 != null) { |
|---|
| 893 | 884 | final voiceMsg = Message.voice( |
|---|
| 894 | 885 | role: MessageRole.user, |
|---|
| 895 | 886 | audioUri: caption.substring('__voice__:'.length), |
|---|
| 896 | 887 | status: MessageStatus.sent, |
|---|
| 897 | 888 | ); |
|---|
| 889 | + voiceMessageId = voiceMsg.id; |
|---|
| 898 | 890 | ref.read(messagesProvider.notifier).addMessage(voiceMsg); |
|---|
| 899 | | - _ws?.send({ |
|---|
| 900 | | - 'type': 'voice', |
|---|
| 901 | | - 'audioBase64': voiceB64, |
|---|
| 902 | | - 'content': '', |
|---|
| 903 | | - 'messageId': voiceMsg.id, |
|---|
| 904 | | - 'sessionId': targetSessionId, |
|---|
| 905 | | - }); |
|---|
| 906 | 891 | } |
|---|
| 907 | 892 | |
|---|
| 893 | + // Send everything as a single atomic bundle |
|---|
| 894 | + _ws?.send({ |
|---|
| 895 | + 'type': 'bundle', |
|---|
| 896 | + 'caption': textCaption, |
|---|
| 897 | + if (voiceB64 != null) 'audioBase64': voiceB64, |
|---|
| 898 | + if (voiceMessageId != null) 'voiceMessageId': voiceMessageId, |
|---|
| 899 | + 'attachments': attachments, |
|---|
| 900 | + 'sessionId': targetSessionId, |
|---|
| 901 | + }); |
|---|
| 902 | + |
|---|
| 908 | 903 | // Show images in chat locally |
|---|
| 909 | 904 | for (var i = 0; i < encodedImages.length; i++) { |
|---|
| 910 | 905 | final message = Message.image( |
|---|