From 69c37c43074ad20ab9c7a5b7f4464863c4d298d4 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Sun, 22 Mar 2026 15:58:16 +0100
Subject: [PATCH] fix: image flicker, screenshot indicator, cross-session message storage

---
 lib/widgets/message_bubble.dart |   17 ++++++++++++-----
 lib/screens/chat_screen.dart    |   20 ++++++++++++++++++++
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart
index e60e02a..d0f67d1 100644
--- a/lib/screens/chat_screen.dart
+++ b/lib/screens/chat_screen.dart
@@ -13,6 +13,7 @@
 import '../models/server_config.dart';
 import '../providers/providers.dart';
 import '../services/audio_service.dart';
+import '../services/message_store.dart';
 import '../services/websocket_service.dart';
 import '../theme/app_theme.dart';
 import '../widgets/command_bar.dart';
@@ -264,6 +265,8 @@
 
     final activeId = ref.read(activeSessionIdProvider);
     if (sessionId != null && sessionId != activeId) {
+      // Store message for the other session so it's there when user switches
+      _storeForSession(sessionId, message);
       _incrementUnread(sessionId);
       final sessions = ref.read(sessionsProvider);
       final session = sessions.firstWhere(
@@ -304,6 +307,7 @@
 
     final activeId = ref.read(activeSessionIdProvider);
     if (sessionId != null && sessionId != activeId) {
+      _storeForSession(sessionId, message);
       _incrementUnread(sessionId);
       final sessions = ref.read(sessionsProvider);
       final session = sessions.firstWhere(
@@ -366,6 +370,13 @@
     ref.read(messagesProvider.notifier).addMessage(message);
     ref.read(isTypingProvider.notifier).state = false;
     _scrollToBottom();
+  }
+
+  /// Store a message for a non-active session so it persists when the user switches to it.
+  void _storeForSession(String sessionId, Message message) {
+    MessageStore.loadAll(sessionId).then((existing) {
+      MessageStore.save(sessionId, [...existing, message]);
+    });
   }
 
   void _incrementUnread(String sessionId) {
@@ -525,6 +536,15 @@
   void _requestScreenshot() {
     _screenshotForChat = true;
     _sendCommand('screenshot');
+    if (mounted) {
+      ScaffoldMessenger.of(context).showSnackBar(
+        const SnackBar(
+          content: Text('Capturing screenshot...'),
+          duration: Duration(seconds: 2),
+          behavior: SnackBarBehavior.floating,
+        ),
+      );
+    }
   }
 
   void _navigateToTerminal() {
diff --git a/lib/widgets/message_bubble.dart b/lib/widgets/message_bubble.dart
index e9c4882..c5df417 100644
--- a/lib/widgets/message_bubble.dart
+++ b/lib/widgets/message_bubble.dart
@@ -1,5 +1,6 @@
 import 'dart:convert';
 import 'dart:math';
+import 'dart:typed_data';
 
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
@@ -8,6 +9,9 @@
 import '../models/message.dart';
 import '../theme/app_theme.dart';
 import 'image_viewer.dart';
+
+// Cache decoded image bytes to prevent flicker on widget rebuild
+final Map<String, Uint8List> _imageCache = {};
 
 /// Chat message bubble with support for text, voice, and image types.
 class MessageBubble extends StatelessWidget {
@@ -208,11 +212,13 @@
       return const Text('Image unavailable');
     }
 
-    final bytes = base64Decode(
-      message.imageBase64!.contains(',')
-          ? message.imageBase64!.split(',').last
-          : message.imageBase64!,
-    );
+    // Cache decoded bytes to prevent flicker on rebuild
+    final bytes = _imageCache.putIfAbsent(message.id, () {
+      final raw = message.imageBase64!;
+      return Uint8List.fromList(base64Decode(
+        raw.contains(',') ? raw.split(',').last : raw,
+      ));
+    });
 
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
@@ -232,6 +238,7 @@
               width: 260,
               height: 180,
               fit: BoxFit.cover,
+              gaplessPlayback: true,
               errorBuilder: (_, e, st) => const SizedBox(
                 width: 260,
                 height: 60,

--
Gitblit v1.3.1