_binary From 11a3a4d953f30f84182afae95d606bb6effd1e0a Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Mon, 10 Nov 2025 23:49:13 -0800
Subject: [PATCH 01/14] autoload messages WIP

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 .../components/SocialComponent.php            | 202 +++++++++++++++++-
 src/css/messages.css                          |  16 ++
 src/scripts/messages.js                       | 171 +++++++++++++++
 src/views/elements/UsermessagesElement.php    |   7 +-
 4 files changed, 392 insertions(+), 4 deletions(-)

diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index 2407f98d1..2b8322365 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -2371,11 +2371,14 @@ class SocialComponent extends Component implements CrawlConstants
         $message_actions = ['addcontact' => 'addContact',
             'blockcontact' => 'blockContact',
             'ignorecontact' => 'ignoreContact',
-            'newmessage' => 'newMessage', "status" =>
+            'newmessage' => 'newMessage',
+            'loadmessages' => 'loadMoreMessages', "status" =>
             "messagesStatus"];
         $arg = $_REQUEST['arg'] ?? "";
+        error_log("userMessages arg: " . $arg . ", available actions: " . implode(', ', array_keys($message_actions)));
         if (in_array($arg, array_keys($message_actions))) {
             $action = $message_actions[$arg];
+            error_log("Calling action: " . $action);
             if (!empty($contact_filter)) {
                 $data['CONTACT_ID'] = $user_model->getUserId($contact_filter);
                 $data['CONTACT_NAME'] = $contact_filter;
@@ -2402,7 +2405,9 @@ class SocialComponent extends Component implements CrawlConstants
         $data['SCRIPT'] .=  "window.tl = {".
             'social_component_no_longer_update:"'.
                 tl('social_component_no_longer_update').'"'.
-            '}; window.onload = doUpdate;';
+            '}; window.USERNAME = "' . $user_model->getUsername($user_id) . '";'.
+            'window.CSRF_TOKEN = "' . C\CSRF_TOKEN . '";'.
+            'window.onload = doUpdate;';
         $contact_ids = array_diff($group_model->getGroupUserIds(
             $user_messages_id), [$user_id]);
         $contacts = $this->marshallContactInfo($user_id, $contact_ids,
@@ -2446,7 +2451,7 @@ class SocialComponent extends Component implements CrawlConstants
                  ["pubdate", "", "", "DESC"]];
                 list(, $data['MESSAGES']) = $this->initializeFeedItems($data,
                     [], $user_id, $search_array, -2, "krsort", $limit,
-                    100);
+                    15);
             }
         }
         $this->initSocialBadges($user_id, $data);
@@ -2815,6 +2820,197 @@ class SocialComponent extends Component implements CrawlConstants
         }
         return $parent->redirectWithMessage("", ['contact_id']);
     }
+    /**
+     * Loads more messages for a conversation to support infinite scroll
+     * pagination. Returns JSON response with older messages.
+     *
+     * @param int $user_id id of user requesting more messages
+     * @param array $data current data array (not used but required by interface)
+     */
+    private function loadMoreMessages($user_id, $data)
+    {
+        $parent = $this->parent;
+        $group_model = $parent->model("group");
+        $user_model = $parent->model("user");
+
+        // Debug logging to verify the method is being called
+        error_log("loadMoreMessages called with user_id: $user_id");
+        error_log("REQUEST params: " . print_r($_REQUEST, true));
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "TIME: " . date('Y-m-d H:i:s') . "\n" .
+            "USER_ID: $user_id\n" .
+            "REQUEST: " . print_r($_REQUEST, true) . "\n" .
+            "METHOD: " . $_SERVER['REQUEST_METHOD'] . "\n" .
+            "URI: " . $_SERVER['REQUEST_URI'] . "\n\n",
+            FILE_APPEND);
+
+        $contact_id = $parent->clean($_REQUEST['contact_id'] ?? 0, 'int');
+        $before_timestamp = $parent->clean($_REQUEST['before_timestamp'] ?? 0, 'int');
+        $limit = $parent->clean($_REQUEST['limit'] ?? 20, 'int');
+
+        error_log("Parsed params - contact_id: $contact_id, before_timestamp: $before_timestamp, limit: $limit");
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "PARSED: contact_id=$contact_id, before_timestamp=$before_timestamp, limit=$limit\n",
+            FILE_APPEND);
+
+        if (empty($contact_id) || empty($before_timestamp)) {
+            error_log("Missing required params - contact_id: '$contact_id', before_timestamp: '$before_timestamp'");
+            file_put_contents('/tmp/debug_loadmessages.txt',
+                "ERROR: Missing params - contact_id=$contact_id, before_timestamp=$before_timestamp\n",
+                FILE_APPEND);
+            header('Content-Type: application/json');
+            echo json_encode(['error' => 'Missing required parameters',
+                'contact_id' => $contact_id, 'before_timestamp' => $before_timestamp]);
+            exit();
+        }
+
+        if ($limit > 100) {
+            $limit = 100;
+        }
+
+        $user_messages_id = $group_model->getPersonalGroupId($user_id);
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "Got user_messages_id: $user_messages_id\n", FILE_APPEND);
+
+        $messages_thread_title = $group_model->getMessagesThreadTitle(
+            [$user_id, $contact_id]);
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "Got messages_thread_title: $messages_thread_title\n", FILE_APPEND);
+
+        $message_thread_id = $group_model->getGroupThreadId($user_messages_id,
+            $user_id, $messages_thread_title);
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "Got message_thread_id: $message_thread_id\n", FILE_APPEND);
+
+        if (empty($message_thread_id)) {
+            file_put_contents('/tmp/debug_loadmessages.txt',
+                "ERROR: Empty message_thread_id\n", FILE_APPEND);
+            header('Content-Type: application/json');
+            echo json_encode(['error' => 'Invalid message thread']);
+            exit();
+        }
+
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "About to query messages...\n", FILE_APPEND);
+
+        $search_array = [
+            ["parent_id", "=", $message_thread_id, ""],
+            ["group_id", "=", $user_messages_id, ""],
+            ["pubdate", "<", $before_timestamp, "DESC"]
+        ];
+
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "Getting messages directly from GroupModel...\n", FILE_APPEND);
+
+        // Get messages directly using GroupModel instead of complex initializeFeedItems
+        $messages = $group_model->getGroupItems(0, $limit, $search_array, $user_id, -2);
+
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "Direct query completed successfully\n", FILE_APPEND);
+
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "Query returned " . count($messages) . " messages\n", FILE_APPEND);
+
+        $has_more = count($messages) == $limit;
+
+        // Render messages using the existing UsermessagesElement to get proper HTML
+        $element_data = [
+            'MESSAGES' => $messages,
+            'USERNAME' => $user_model->getUsername($user_id),
+            'CONTACT_ID' => $contact_id,
+            'CONTACT_NAME' => $user_model->getUsername($contact_id),
+            'CONTACT_ICON_URL' => $user_model->getUserIconUrl($contact_id),
+            'MOBILE' => false,
+            'LOCALE_DIR' => 'ltr'
+        ];
+
+        // Capture the rendered HTML from UsermessagesElement
+        ob_start();
+        $now = time();
+        $old_pubdate = $now;
+        $old_user = $messages[0]['USER_NAME'] ?? "";
+        $num_messages = count($messages);
+
+        // Use the same rendering logic as UsermessagesElement
+        for ($i = 0; $i < $num_messages; $i++) {
+            $message_info = $messages[$i];
+            $next_message_info = $messages[$i + 1] ?? $message_info;
+            $next_user_name = $next_message_info['USER_NAME'];
+            if (empty($message_info['DESCRIPTION'])) {
+                continue;
+            }
+            $pub_date = $message_info['PUBDATE'];
+            $next_pub_date = $next_message_info['PUBDATE'];
+            $pub_date_diff = abs($next_pub_date - $pub_date);
+            $pub_date_age = $now - $pub_date;
+            $user_change = $message_info['USER_NAME'] != $next_user_name;
+            $pub_data_change = $user_change ||($pub_date_age < C\ONE_HOUR &&
+                $pub_date_diff > 5 * C\ONE_MINUTE) ||
+                ($pub_date_age < C\ONE_DAY && $pub_date_diff > C\ONE_HOUR)||
+                ($pub_date_diff > C\ONE_DAY);
+            $pub_date_string = date('M j, Y g:i A', $pub_date);
+            $color = "back-light-gray";
+            $align = "align-same";
+            $is_user = false;
+            $user_string = "other";
+            $top_corner = " top-corner ";
+            if ($element_data['USERNAME'] == $message_info['USER_NAME']) {
+                $color = "back-light-blue";
+                $align = "align-opposite";
+                $is_user = true;
+                $user_string = "user";
+                $top_corner =  "";
+            }
+            $comma = "";
+            ?>
+            <div class="<?=$align ?>">
+            <div class="message-detail-outer">
+            <div class='message-<?=$user_string?>-time' >
+            <?php
+            if (!$is_user && ($pub_data_change || $user_change)) {
+                echo '<img class="message-icon float-same" src="'.
+                    $message_info['USER_ICON'].'" >&nbsp;';
+                $comma = ", ";
+                echo $message_info['USER_NAME'];
+            }
+            if ($pub_data_change) {
+                echo $comma . $pub_date_string;
+            }?>
+            </div>
+            <div class="message-detail <?=$color . $top_corner?>"><?=
+            $message_info['DESCRIPTION'];
+            ?>
+            </div>
+            </div>
+            </div>
+            <?php
+        }
+
+        $rendered_html = ob_get_clean();
+
+        // Get the oldest timestamp from the loaded messages
+        $oldest_timestamp = null;
+        if (!empty($messages)) {
+            $oldest_timestamp = $messages[count($messages) - 1]['PUBDATE'];
+        }
+
+        header('Content-Type: application/json');
+        $response = json_encode([
+            'html' => $rendered_html,
+            'has_more' => $has_more,
+            'message_count' => count($messages),
+            'oldest_timestamp' => $oldest_timestamp
+        ]);
+
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "About to return JSON response with HTML, length: " . strlen($response) . "\n", FILE_APPEND);
+
+        echo $response;
+
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "After echo, about to exit\n", FILE_APPEND);
+        exit();
+    }
     /**
      * Determines a list of posts that might need to reply to a post in
      * a group
diff --git a/src/css/messages.css b/src/css/messages.css
index 2468e8544..dfbc30f5e 100644
--- a/src/css/messages.css
+++ b/src/css/messages.css
@@ -657,3 +657,19 @@
     color: #d32f2f;
     font-style: italic;
 }
+
+/* Infinite scroll loading indicator */
+.loading-indicator {
+    text-align: center;
+    padding: 10px;
+    font-style: italic;
+    color: #666;
+    background-color: #f5f5f5;
+    border-bottom: 1px solid #ddd;
+    animation: pulse 1.5s ease-in-out infinite alternate;
+}
+
+@keyframes pulse {
+    from { opacity: 0.6; }
+    to { opacity: 1; }
+}
diff --git a/src/scripts/messages.js b/src/scripts/messages.js
index a2ae4bf6d..60e9f3e31 100644
--- a/src/scripts/messages.js
+++ b/src/scripts/messages.js
@@ -423,3 +423,174 @@ function icecandidate(local_stream)
     };
     have_set_local_ice_candidate = true;
 }
+
+// Infinite scroll variables
+var loading_messages = false;
+var has_more_messages = true;
+var last_scroll_time = 0;
+
+/**
+ * Handles scroll events on the conversation div to implement infinite scroll
+ */
+function handleConversationScroll()
+{
+    var conversation = elt('conversation');
+    if (!conversation || loading_messages || !has_more_messages) {
+        return;
+    }
+
+    // Throttle scroll events - only check every 500ms
+    var now = Date.now();
+    if (now - last_scroll_time < 500) {
+        return;
+    }
+    last_scroll_time = now;
+
+    // Check if scrolled near top (within 100px)
+    if (conversation.scrollTop < 100) {
+        loadMoreMessages();
+    }
+}
+
+/**
+ * Loads more messages from the server for infinite scroll
+ */
+function loadMoreMessages()
+{
+    if (loading_messages) {
+        return;
+    }
+
+    loading_messages = true;
+    var conversation = elt('conversation');
+    var contact_id = conversation.getAttribute('data-contact-id');
+    var loaded_count = parseInt(conversation.getAttribute('data-loaded-count'));
+    var oldest_timestamp = parseInt(conversation.getAttribute('data-oldest-timestamp'));
+
+    if (!contact_id || !oldest_timestamp) {
+        loading_messages = false;
+        return;
+    }
+
+    // Show loading indicator
+    showLoadingIndicator();
+
+    // Force social controller since messages are handled by SocialComponent
+    var controller = 'social';
+
+    // Build request parameters
+    var params = new URLSearchParams({
+        'c': controller,
+        'a': 'userMessages',
+        'arg': 'loadmessages',
+        'contact_id': contact_id,
+        'before_timestamp': oldest_timestamp,
+        'limit': 20
+    });
+
+    // Add CSRF token
+    var csrf_input = document.querySelector('input[name="' +
+        window.CSRF_TOKEN + '"]');
+    if (csrf_input) {
+        params.append(window.CSRF_TOKEN, csrf_input.value);
+    }
+
+    // Use the same URL pattern as the existing start_url but replace the arg
+    var base_url = window.start_url.replace('&arg=status', '&arg=loadmessages');
+
+    // Add our additional parameters to the URL
+    base_url += '&contact_id=' + contact_id;
+    base_url += '&before_timestamp=' + oldest_timestamp;
+    base_url += '&limit=10';
+
+    console.log('Making request to:', base_url);
+
+    fetch(base_url, {
+        method: 'GET',  // Use GET like EventSource
+        headers: {
+            'Accept': 'application/json',
+        }
+    })
+    .then(response => {
+        // Check if response is actually JSON
+        const contentType = response.headers.get('content-type');
+        if (!contentType || !contentType.includes('application/json')) {
+            return response.text().then(text => {
+                console.error('Expected JSON but received:', text);
+                throw new Error('Server returned HTML instead of JSON');
+            });
+        }
+        return response.json();
+    })
+    .then(data => {
+        hideLoadingIndicator();
+
+        if (data.error) {
+            console.error('Error loading messages:', data.error);
+            loading_messages = false;
+            return;
+        }
+
+        if (data.html && data.message_count > 0) {
+            // Append messages to the bottom (chronological order)
+            conversation.insertAdjacentHTML('beforeend', data.html);
+            has_more_messages = data.has_more;
+
+            // Update data attributes
+            var new_loaded_count = loaded_count + data.message_count;
+            conversation.setAttribute('data-loaded-count', new_loaded_count);
+
+            // Update oldest timestamp for next fetch
+            if (data.oldest_timestamp) {
+                conversation.setAttribute('data-oldest-timestamp', data.oldest_timestamp);
+            }
+
+            // Don't auto-scroll - let user stay where they are
+        } else {
+            has_more_messages = false;
+        }
+
+        loading_messages = false;
+    })
+    .catch(error => {
+        console.error('Error loading messages:', error);
+        hideLoadingIndicator();
+        loading_messages = false;
+    });
+}
+
+/**
+ * Prepends new messages to the conversation while maintaining scroll position
+ */
+// Message rendering functions removed - now using server-side HTML rendering
+
+/**
+ * Shows loading indicator at top of conversation
+ */
+function showLoadingIndicator()
+{
+    var conversation = elt('conversation');
+    var existing_indicator = elt('loading-indicator');
+
+    if (!existing_indicator) {
+        var indicator = document.createElement('div');
+        indicator.id = 'loading-indicator';
+        indicator.className = 'loading-indicator';
+        indicator.innerHTML = 'Loading more messages...';
+        indicator.style.cssText = 'text-align: center; padding: 10px; ' +
+            'font-style: italic; color: #666;';
+
+        conversation.insertBefore(indicator, conversation.firstChild);
+    }
+}
+
+/**
+ * Hides loading indicator
+ */
+function hideLoadingIndicator()
+{
+    var indicator = elt('loading-indicator');
+    if (indicator) {
+        indicator.remove();
+    }
+}
diff --git a/src/views/elements/UsermessagesElement.php b/src/views/elements/UsermessagesElement.php
index 79900e022..86b4dc227 100644
--- a/src/views/elements/UsermessagesElement.php
+++ b/src/views/elements/UsermessagesElement.php
@@ -243,7 +243,12 @@ class UsermessagesElement extends Element implements CrawlConstants
             </div>
             <div id="av-call"></div>
             <div id="conversation" class="conversation" data-time="<?=
-                $now?>" ><?php
+                $now?>" data-contact-id="<?= $data['CONTACT_ID'] ?? '' ?>"
+                data-loaded-count="<?= count($data['MESSAGES']) ?>"
+                data-oldest-timestamp="<?= isset($data['MESSAGES']) &&
+                count($data['MESSAGES']) > 0 ?
+                $data['MESSAGES'][count($data['MESSAGES']) - 1]['PUBDATE'] : $now ?>"
+                onscroll="handleConversationScroll()" ><?php
             $old_pubdate = $now;
             $old_user = $data['MESSAGES'][0]['USER_NAME'] ?? "";
             $num_messages = count($data['MESSAGES']);
-- 
2.39.5 (Apple Git-154)


From 450af5ee24b0674afc83a655d1b4e9de13670d8a Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Tue, 11 Nov 2025 00:31:09 -0800
Subject: [PATCH 02/14] complete auto load on scroll

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 .../components/SocialComponent.php            | 45 ++++++++++++-------
 1 file changed, 28 insertions(+), 17 deletions(-)

diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index 2b8322365..3414b43a5 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -2899,17 +2899,18 @@ class SocialComponent extends Component implements CrawlConstants
             ["pubdate", "<", $before_timestamp, "DESC"]
         ];
 
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "Getting messages directly from GroupModel...\n", FILE_APPEND);
-
-        // Get messages directly using GroupModel instead of complex initializeFeedItems
-        $messages = $group_model->getGroupItems(0, $limit, $search_array, $user_id, -2);
 
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "Direct query completed successfully\n", FILE_APPEND);
+        // Use initializeFeedItems for proper content processing
+        $temp_data = $data; // Don't modify the original data
+        $temp_limit = 0;
 
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "Query returned " . count($messages) . " messages\n", FILE_APPEND);
+        try {
+            list(, $messages) = $this->initializeFeedItems($temp_data, [], $user_id,
+                $search_array, -2, "krsort", $temp_limit, $limit);
+        } catch (Exception $e) {
+            // Fallback to direct query
+            $messages = $group_model->getGroupItems(0, $limit, $search_array, $user_id, -2);
+        }
 
         $has_more = count($messages) == $limit;
 
@@ -2932,13 +2933,29 @@ class SocialComponent extends Component implements CrawlConstants
         $num_messages = count($messages);
 
         // Use the same rendering logic as UsermessagesElement
+        $rendered_count = 0;
         for ($i = 0; $i < $num_messages; $i++) {
             $message_info = $messages[$i];
             $next_message_info = $messages[$i + 1] ?? $message_info;
             $next_user_name = $next_message_info['USER_NAME'];
-            if (empty($message_info['DESCRIPTION'])) {
+            // Check for processed content in various possible fields
+            $description_content = '';
+            if (!empty($message_info['DESCRIPTION'])) {
+                $description_content = $message_info['DESCRIPTION'];
+            } else {
+                // 't' contains the processed content field from initializeFeedItems
+                foreach (['t', 'DESCRIPTION', 'OLD_DESCRIPTION', 'SOURCE_NAME', 'TITLE'] as $field) {
+                    if (!empty($message_info[$field])) {
+                        $description_content = $message_info[$field];
+                        break;
+                    }
+                }
+            }
+
+            if (empty($description_content)) {
                 continue;
             }
+            $rendered_count++;
             $pub_date = $message_info['PUBDATE'];
             $next_pub_date = $next_message_info['PUBDATE'];
             $pub_date_diff = abs($next_pub_date - $pub_date);
@@ -2978,7 +2995,7 @@ class SocialComponent extends Component implements CrawlConstants
             }?>
             </div>
             <div class="message-detail <?=$color . $top_corner?>"><?=
-            $message_info['DESCRIPTION'];
+            $description_content;
             ?>
             </div>
             </div>
@@ -3002,13 +3019,7 @@ class SocialComponent extends Component implements CrawlConstants
             'oldest_timestamp' => $oldest_timestamp
         ]);
 
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "About to return JSON response with HTML, length: " . strlen($response) . "\n", FILE_APPEND);
-
         echo $response;
-
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "After echo, about to exit\n", FILE_APPEND);
         exit();
     }
     /**
-- 
2.39.5 (Apple Git-154)


From 09d1b2cbc358e8b3218f9ab0e526708f54df215b Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Tue, 11 Nov 2025 13:01:22 -0800
Subject: [PATCH 03/14] working auto translate in batches

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 src/configs/Config.php                        |   4 +-
 src/controllers/ApiController.php             |  84 ++++++-
 .../components/SocialComponent.php            | 218 +++++++++++++++++-
 src/css/messages.css                          |  78 +++++++
 src/locale/en_US/configure.ini                |   2 +
 src/models/GroupModel.php                     |  54 +++++
 src/scripts/messages.js                       |  35 ++-
 src/views/elements/UsermessagesElement.php    |  45 ++++
 8 files changed, 512 insertions(+), 8 deletions(-)

diff --git a/src/configs/Config.php b/src/configs/Config.php
index 577ea5d84..cb681fa34 100755
--- a/src/configs/Config.php
+++ b/src/configs/Config.php
@@ -359,9 +359,9 @@ nsdefine('MAX_SUMMARIZE_MESSAGES', 10000);
 /** URL endpoint for LLM API service used for AI summarization
  * For example, LM studio api url (http://localhost:1234/v1/chat/completions)
  */
-nsconddefine('LLM_API_URL', '');
+nsconddefine('LLM_API_URL', 'http://localhost:1234/v1/chat/completions');
 /** Model name to use for LLM API requests - for example, "aya-23-8b" */
-nsconddefine('LLM_MODEL', '');
+nsconddefine('LLM_MODEL', 'aya-23-8b');
 /** Whether to use conjunctive search queries or disjunctive */
 nsconddefine('USE_CONJUNCTIVE_QUERY', false);
 /** setting Profile.php to something else in LocalConfig.php allows one to have
diff --git a/src/controllers/ApiController.php b/src/controllers/ApiController.php
index 6a96afabc..8d36a1aef 100644
--- a/src/controllers/ApiController.php
+++ b/src/controllers/ApiController.php
@@ -55,7 +55,7 @@ class ApiController extends Controller implements CrawlConstants
      * These are the activities supported by this controller
      * @var array
      */
-    public $activities = ["summarize", "transcribe", "transcriptionStatus"];
+    public $activities = ["summarize", "transcribe", "transcriptionStatus", "translate"];
     /**
      * Used to process requests related to user group activities outside of
      * the admin panel setting. This either could be because the admin panel
@@ -423,6 +423,88 @@ class ApiController extends Controller implements CrawlConstants
         $this->returnJsonResponse($data);
         return $data;
     }
+    /**
+     * Handles a request to translate text from one language to another using LLM
+     *
+     * @return array $data containing translation results and status
+     */
+    public function translate()
+    {
+        $data = [];
+        $data['ELEMENT'] = 'translate';
+        if (empty($_REQUEST['text']) || empty($_REQUEST['target_lang'])) {
+            $data['errors'] = ["Missing required parameters: 'text' and 'target_lang' are required"];
+            return $data;
+        }
+        $text = $this->clean($_REQUEST['text'], "string");
+        $target_lang = $this->clean($_REQUEST['target_lang'], "string");
+
+        return $this->translateText($text, $target_lang);
+    }
+    /**
+     * Translates a given text to the specified target language.
+     *
+     * @param string $text The text to translate
+     * @param string $target_lang The target language code
+     * @return array Translation results with status and content
+     */
+    public function translateText($text, $target_lang)
+    {
+        $data = [];
+        $data['ELEMENT'] = 'translate';
+        if (empty($text) || empty($target_lang)) {
+            $data['errors'] = ["Missing required parameters: 'text' and 'target_lang' are required"];
+            return $data;
+        }
+        $api_url = C\LLM_API_URL ?? "http://localhost:1234/v1/chat/completions";
+        $model = C\LLM_MODEL ?? "aya-23-8b";
+        $language_map = [ "ar" => "Arabic", "bn" => "Bengali", "de" => "German",
+            "el_GR" => "Greek", "en_US" => "English (US)", "es" => "Spanish",
+            "fa" => "Persian", "fr_FR" => "French (France)", "he" => "Hebrew",
+            "hi" => "Hindi", "id" => "Indonesian", "it" => "Italian",
+            "ja" => "Japanese", "kn" => "Kannada", "ko" => "Korean",
+            "nl" => "Dutch", "pl" => "Polish", "pt" => "Portuguese",
+            "ru" => "Russian", "te" => "Telugu", "th" => "Thai",
+            "tl" => "Tagalog", "tr" => "Turkish", "vi_VN" => "Vietnamese",
+            "zh_CN" => "Chinese (Simplified)"
+        ];
+        $mapping_string = "Use the following language mapping for accurate translations:\n";
+        foreach ($language_map as $code => $lang) {
+            $mapping_string .= "$code → $lang\n";
+        }
+        $translate_prompt = "Translate the text that is in following sequence of <in></in> tags to the locale " .
+            $target_lang . ". Assume the context is text that might appear in a chat message. " .
+            "You should decode any html entities before doing the translation. " .
+            "If you see text like '%s' in the input treat it like a placeholder that should appear verbatim in the appropriate place in the output. " .
+            "Output the results between <out></out> tags. Do not add any explanations. Do not repeat the output translations. " .
+            "<in>" . $text . "</in>";
+        $request_body = [ "model" => $model, "messages" => [["role" => "system",
+            "content" => "You are a helpful assistant who translates text" .
+            "from one language to another. Use this predefined mapping ".
+            "for accurate language detection:\n" . $mapping_string],
+            ["role" => "user", "content" => $translate_prompt]],
+            "temperature" => 0.1, "max_tokens" => -1, "stream" => false];
+        $result = $this->sendLLMRequest($api_url, $request_body);
+        if (!$result) {
+            $data['errors'] = ["Failed to connect to translation service"];
+        } else {
+            $result_obj = json_decode($result, true);
+            if (isset($result_obj['choices'][0]['message']['content'])) {
+                $content = $result_obj['choices'][0]['message']['content'];
+                // Extract text between <out> tags
+                preg_match('/<out>([\s\S]*?)<\/out>/', $content, $matches);
+                if (isset($matches[1])) {
+                    $data['translation'] = trim($matches[1]);
+                    $data['status'] = 'success';
+                } else {
+                    $data['errors'] = ["Translation format not recognized"];
+                }
+            } else {
+                $data['errors'] = ["Invalid response from translation service"];
+            }
+        }
+        return $data;
+    }
     /**
      * Helper method to return JSON response with proper headers
      *
diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index 3414b43a5..70ae9210e 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -2368,11 +2368,15 @@ class SocialComponent extends Component implements CrawlConstants
         $data['CONTACT_ICON_URL'] = $user_model->getUserIconUrl($contact_id);
         $user_messages_id = $group_model->getPersonalGroupId($user_id);
         $data['USER_MESSAGES_ID'] = $user_messages_id;
+        if (!empty($contact_id)) {
+            $data['TRANSLATION_LANGUAGE'] = $group_model->getTranslationLanguage($user_id, $contact_id);
+        }
         $message_actions = ['addcontact' => 'addContact',
             'blockcontact' => 'blockContact',
             'ignorecontact' => 'ignoreContact',
             'newmessage' => 'newMessage',
-            'loadmessages' => 'loadMoreMessages', "status" =>
+            'loadmessages' => 'loadMoreMessages',
+            'settranslation' => 'setTranslationLanguage', "status" =>
             "messagesStatus"];
         $arg = $_REQUEST['arg'] ?? "";
         error_log("userMessages arg: " . $arg . ", available actions: " . implode(', ', array_keys($message_actions)));
@@ -2452,6 +2456,18 @@ class SocialComponent extends Component implements CrawlConstants
                 list(, $data['MESSAGES']) = $this->initializeFeedItems($data,
                     [], $user_id, $search_array, -2, "krsort", $limit,
                     15);
+
+                // Apply translation if enabled
+                file_put_contents('/tmp/debug_loadmessages.txt',
+                    "Initial load translation language: '" . ($data['TRANSLATION_LANGUAGE'] ?? 'not set') . "'\n", FILE_APPEND);
+                if (!empty($data['TRANSLATION_LANGUAGE'])) {
+                    file_put_contents('/tmp/debug_loadmessages.txt',
+                        "Starting initial translation to: " . $data['TRANSLATION_LANGUAGE'] . "\n", FILE_APPEND);
+                    $data['MESSAGES'] = $this->translateMessageBatch(
+                        $data['MESSAGES'], $data['TRANSLATION_LANGUAGE']);
+                    file_put_contents('/tmp/debug_loadmessages.txt',
+                        "Initial translation completed\n", FILE_APPEND);
+                }
             }
         }
         $this->initSocialBadges($user_id, $data);
@@ -2761,6 +2777,194 @@ class SocialComponent extends Component implements CrawlConstants
         return $parent->redirectWithMessage(
             tl('social_component_connection_request_blocked'));
     }
+    /**
+     * Sets the translation language for messages from a specific contact
+     *
+     * @param int $user_id id of user who will receive translated messages
+     * @param array $data current data to be sent to view after processing
+     */
+    private function setTranslationLanguage($user_id, $data)
+    {
+        $parent = $this->parent;
+        $group_model = $parent->model("group");
+        $contact_id = $data['CONTACT_ID'] ?? null;
+        $language = $parent->clean($_REQUEST['language'] ?? 'off', 'string');
+
+        if (empty($contact_id)) {
+            return $parent->redirectWithMessage(
+                tl('social_component_invalid_contact'),
+                ['contact_id']
+            );
+        }
+
+        $group_model->setTranslationLanguage($user_id, $contact_id, $language);
+
+        $message_key = ($language == 'off')
+            ? 'social_component_translation_disabled'
+            : 'social_component_translation_enabled';
+
+        return $parent->redirectWithMessage(tl($message_key), ['contact_id']);
+    }
+    /**
+     * Translates a batch of messages using the LLM API
+     *
+     * @param array $messages Array of message items to translate
+     * @param string $target_language Target language code
+     * @return array Array of messages with translated content
+     */
+    private function translateMessageBatch($messages, $target_language)
+    {
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "translateMessageBatch called with " . count($messages) . " messages, target: $target_language\n", FILE_APPEND);
+
+        if (empty($messages) || empty($target_language) || $target_language == 'off') {
+            file_put_contents('/tmp/debug_loadmessages.txt',
+                "translateMessageBatch: Early return - messages empty or language off\n", FILE_APPEND);
+            return $messages;
+        }
+
+        $translated_count = 0;
+        foreach ($messages as $index => &$message) {
+            file_put_contents('/tmp/debug_loadmessages.txt',
+                "Message $index: " . print_r(array_keys($message), true), FILE_APPEND);
+
+            // Check for content in 't' field (processed content) or 'DESCRIPTION' field
+            $content_field = null;
+            $original_text = null;
+
+            if (!empty($message['t'])) {
+                $content_field = 't';
+                $original_text = $message['t'];
+            } else if (!empty($message['DESCRIPTION'])) {
+                $content_field = 'DESCRIPTION';
+                $original_text = $message['DESCRIPTION'];
+            }
+
+            if ($content_field && $original_text) {
+                file_put_contents('/tmp/debug_loadmessages.txt',
+                    "Message $index has content in $content_field: " . substr(strip_tags($original_text), 0, 100) . "...\n", FILE_APPEND);
+
+                // Only translate if it's actual text content (more than just numbers or whitespace)
+                $clean_text = strip_tags($original_text);
+                $clean_text = trim($clean_text);
+
+                if (strlen($clean_text) > 2 && !ctype_digit($clean_text)) {
+                    file_put_contents('/tmp/debug_loadmessages.txt',
+                        "Translating message $index ($content_field): " . substr($clean_text, 0, 50) . "...\n", FILE_APPEND);
+
+                    $translation_result = $this->translateText($original_text, $target_language);
+
+                    if (!empty($translation_result['translation'])) {
+                        $message[$content_field] = $translation_result['translation'];
+                        $message['ORIGINAL_' . $content_field] = $original_text;
+                        $translated_count++;
+                    } else {
+                        // Keep original text if translation fails
+                        file_put_contents('/tmp/debug_loadmessages.txt',
+                            "Translation failed for message $index\n", FILE_APPEND);
+                    }
+                } else {
+                    file_put_contents('/tmp/debug_loadmessages.txt',
+                        "Skipping translation for message $index (short/numeric content)\n", FILE_APPEND);
+                }
+            } else {
+                file_put_contents('/tmp/debug_loadmessages.txt',
+                    "Message $index: No content field found\n", FILE_APPEND);
+            }
+        }
+
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "translateMessageBatch completed: translated $translated_count messages\n", FILE_APPEND);
+        unset($message); // Break reference
+
+        return $messages;
+    }
+    /**
+     * Translates a given text to the specified target language using LLM API
+     *
+     * @param string $text The text to translate
+     * @param string $target_lang The target language code
+     * @return array Translation results with status and content
+     */
+    private function translateText($text, $target_lang)
+    {
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "translateText called: '$text' -> '$target_lang'\n", FILE_APPEND);
+
+        if (empty($text) || empty($target_lang)) {
+            file_put_contents('/tmp/debug_loadmessages.txt',
+                "translateText: Missing parameters\n", FILE_APPEND);
+            return ['errors' => ["Missing required parameters"]];
+        }
+
+        $api_url = C\LLM_API_URL ?? "http://localhost:1234/v1/chat/completions";
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "translateText: API URL = $api_url\n", FILE_APPEND);
+        $model = C\LLM_MODEL ?? "aya-23-8b";
+
+        $language_map = [ "ar" => "Arabic", "bn" => "Bengali", "de" => "German",
+            "el_GR" => "Greek", "en_US" => "English (US)", "es" => "Spanish",
+            "fa" => "Persian", "fr_FR" => "French (France)", "he" => "Hebrew",
+            "hi" => "Hindi", "id" => "Indonesian", "it" => "Italian",
+            "ja" => "Japanese", "kn" => "Kannada", "ko" => "Korean",
+            "nl" => "Dutch", "pl" => "Polish", "pt" => "Portuguese",
+            "ru" => "Russian", "te" => "Telugu", "th" => "Thai",
+            "tl" => "Tagalog", "tr" => "Turkish", "vi_VN" => "Vietnamese",
+            "zh_CN" => "Chinese (Simplified)"
+        ];
+
+        $target_language_name = $language_map[$target_lang] ?? $target_lang;
+
+        $request_body = [
+            "model" => $model,
+            "messages" => [
+                [
+                    "role" => "system",
+                    "content" => "You are a helpful translation assistant. Translate text accurately while preserving meaning and tone."
+                ],
+                [
+                    "role" => "user",
+                    "content" => "Translate the following text to " . $target_language_name . ". Only return the translation without explanations:\n\n" . $text
+                ]
+            ],
+            "temperature" => 0.1,
+            "max_tokens" => 512,
+            "stream" => false
+        ];
+
+        $post_data = json_encode($request_body);
+        $headers = ['Content-Type: application/json'];
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "translateText: Making API call to $api_url\n", FILE_APPEND);
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "translateText: Request body = $post_data\n", FILE_APPEND);
+
+        $response = L\FetchUrl::getPage($api_url, $post_data, true, null,
+            C\SINGLE_PAGE_TIMEOUT, $headers);
+
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "translateText: Response = " . ($response === false ? 'FALSE' : substr($response, 0, 200)) . "\n", FILE_APPEND);
+
+        if ($response === false) {
+            file_put_contents('/tmp/debug_loadmessages.txt',
+                "translateText: API call failed\n", FILE_APPEND);
+            return ['errors' => ["Failed to connect to translation service"]];
+        }
+
+        $result_obj = json_decode($response, true);
+        if (isset($result_obj['response'])) {
+            // LMStudio format
+            $content = trim($result_obj['response']);
+            return ['translation' => $content, 'status' => 'success'];
+        } else if (isset($result_obj['choices'][0]['message']['content'])) {
+            // OpenAI format fallback
+            $content = trim($result_obj['choices'][0]['message']['content']);
+            return ['translation' => $content, 'status' => 'success'];
+        } else {
+            error_log("Translation API response: " . $response);
+            return ['errors' => ["Invalid response from translation service"]];
+        }
+    }
     /**
      * Sends a message cleaned $_REQUEST["description"] from user with id
      * $user_id to user with $data['CONTACT_ID']
@@ -2912,6 +3116,18 @@ class SocialComponent extends Component implements CrawlConstants
             $messages = $group_model->getGroupItems(0, $limit, $search_array, $user_id, -2);
         }
 
+        // Apply translation if enabled
+        $translation_language = $group_model->getTranslationLanguage($user_id, $contact_id);
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "Translation language for user $user_id, contact $contact_id: '$translation_language'\n", FILE_APPEND);
+        if (!empty($translation_language) && $translation_language !== 'off') {
+            file_put_contents('/tmp/debug_loadmessages.txt',
+                "Starting translation to: $translation_language\n", FILE_APPEND);
+            $messages = $this->translateMessageBatch($messages, $translation_language);
+            file_put_contents('/tmp/debug_loadmessages.txt',
+                "Translation completed for " . count($messages) . " messages\n", FILE_APPEND);
+        }
+
         $has_more = count($messages) == $limit;
 
         // Render messages using the existing UsermessagesElement to get proper HTML
diff --git a/src/css/messages.css b/src/css/messages.css
index dfbc30f5e..ca5b1616b 100644
--- a/src/css/messages.css
+++ b/src/css/messages.css
@@ -673,3 +673,81 @@
     from { opacity: 0.6; }
     to { opacity: 1; }
 }
+
+/* Translation UI styles */
+.translation-dropdown-container {
+    position: relative;
+    display: inline-block;
+    margin-left: 10px;
+}
+
+.translation-dropdown-container h2 {
+    display: flex;
+    align-items: center;
+}
+
+.translation-dropdown {
+    position: absolute;
+    top: 100%;
+    right: 0;
+    z-index: 1000;
+    background-color: white;
+    border: 1px solid #ccc;
+    border-radius: 5px;
+    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+    max-height: 200px;
+    overflow-y: auto;
+    min-width: 200px;
+}
+
+.translation-dropdown-list {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+}
+
+.translation-dropdown-list li {
+    border-bottom: 1px solid #eee;
+}
+
+.translation-dropdown-list li:last-child {
+    border-bottom: none;
+}
+
+.translation-dropdown-list a {
+    display: block;
+    padding: 8px 12px;
+    text-decoration: none;
+    color: #333;
+    font-size: 14px;
+}
+
+.translation-dropdown-list a:hover {
+    background-color: #f5f5f5;
+}
+
+.translation-dropdown-list a b {
+    font-weight: normal;
+    background-color: #e3f2fd;
+}
+
+.translation-button {
+    cursor: pointer;
+    opacity: 0.7;
+    transition: opacity 0.3s ease;
+}
+
+.translation-button:hover {
+    opacity: 1;
+}
+
+/* RTL support */
+.html-rtl .translation-dropdown-container {
+    right: auto;
+    left: 10px;
+}
+
+.html-rtl .translation-dropdown {
+    right: auto;
+    left: 0;
+}
diff --git a/src/locale/en_US/configure.ini b/src/locale/en_US/configure.ini
index c7e450e9b..99513da66 100644
--- a/src/locale/en_US/configure.ini
+++ b/src/locale/en_US/configure.ini
@@ -332,6 +332,8 @@ social_component_connection_requested = "Connection Requested!"
 social_component_connection_request_ignored = "Request Ignored!"
 social_component_connection_request_blocked = "Request Blocked!"
 social_component_invalid_message_thread = "Invalid Message Thread!"
+social_component_translation_enabled = "Translation Enabled"
+social_component_translation_disabled = "Translation Disabled"
 social_component_flagged_post = "[%s] %s"
 social_component_join_asc = "Join Date&amp;uarr;"
 social_component_join_desc = "Join Date&amp;darr;"
diff --git a/src/models/GroupModel.php b/src/models/GroupModel.php
index 91b0ae860..e04d4fb36 100644
--- a/src/models/GroupModel.php
+++ b/src/models/GroupModel.php
@@ -5123,4 +5123,58 @@ EOD;
             EVENT_TIME < ?";
         $result = $db->execute($sql, [$call_group_id, $timestamp]);
     }
+    /**
+     * Retrieves the translation language setting for a specific contact
+     *
+     * @param int $user_id id of the user who owns the setting
+     * @param int $contact_id id of the contact whose messages may be translated
+     * @return string|false translation language code or false if not set
+     */
+    public function getTranslationLanguage($user_id, $contact_id)
+    {
+        $db = $this->db;
+        $user_group_id = $this->getPersonalGroupId($user_id);
+        $settings_thread_title = "translate-settings-" . $user_id . "-" . $contact_id;
+        $thread_id = $this->getGroupThreadId($user_group_id, $user_id, $settings_thread_title);
+        if (!$thread_id) {
+            return false;
+        }
+        $sql = "SELECT DESCRIPTION FROM GROUP_ITEM WHERE PARENT_ID = ?";
+        $result = $db->execute($sql, [$thread_id]);
+        $row = $db->fetchArray($result);
+        if (isset($row['DESCRIPTION'])) {
+            $settings = json_decode($row['DESCRIPTION'], true);
+            return $settings['TRANSLATE_LANGUAGE'] ?? false;
+        }
+        return false;
+    }
+    /**
+     * Stores the translation language setting for a specific contact
+     *
+     * @param int $user_id id of the user who owns the setting
+     * @param int $contact_id id of the contact whose messages will be translated
+     * @param string $language language code (e.g., 'es', 'fr') or 'off' to disable
+     */
+    public function setTranslationLanguage($user_id, $contact_id, $language)
+    {
+        $db = $this->db;
+        $user_group_id = $this->getPersonalGroupId($user_id);
+        $settings_thread_title = "translate-settings-" . $user_id . "-" . $contact_id;
+        $thread_id = $this->getGroupThreadId($user_group_id, $user_id, $settings_thread_title);
+        $settings = json_encode(["TRANSLATE_LANGUAGE" => $language]);
+
+        if (!$thread_id) {
+            // Create a new settings entry
+            $this->addGroupItem(
+                0,
+                $user_group_id,
+                $user_id,
+                $settings_thread_title,
+                $settings
+            );
+        } else {
+            $sql = "UPDATE GROUP_ITEM SET DESCRIPTION = ? WHERE PARENT_ID = ?";
+            $db->execute($sql, [$settings, $thread_id]);
+        }
+    }
 }
diff --git a/src/scripts/messages.js b/src/scripts/messages.js
index 60e9f3e31..503c969e7 100644
--- a/src/scripts/messages.js
+++ b/src/scripts/messages.js
@@ -472,8 +472,7 @@ function loadMoreMessages()
         return;
     }
 
-    // Show loading indicator
-    showLoadingIndicator();
+    // Loading indicator removed per user request
 
     // Force social controller since messages are handled by SocialComponent
     var controller = 'social';
@@ -523,7 +522,7 @@ function loadMoreMessages()
         return response.json();
     })
     .then(data => {
-        hideLoadingIndicator();
+        // hideLoadingIndicator(); // Removed per user request
 
         if (data.error) {
             console.error('Error loading messages:', data.error);
@@ -554,7 +553,7 @@ function loadMoreMessages()
     })
     .catch(error => {
         console.error('Error loading messages:', error);
-        hideLoadingIndicator();
+        // hideLoadingIndicator(); // Removed per user request
         loading_messages = false;
     });
 }
@@ -594,3 +593,31 @@ function hideLoadingIndicator()
         indicator.remove();
     }
 }
+
+/**
+ * Initialize translation dropdown functionality
+ */
+function initializeTranslationDropdown()
+{
+    const translationButton = document.querySelector('.translation-button');
+    const translationDropdown = document.getElementById('translation-dropdown');
+
+    if (translationButton && translationDropdown) {
+        translationButton.addEventListener('click', function(e) {
+            e.preventDefault();
+            translationDropdown.classList.toggle('none');
+        });
+
+        // Close dropdown when clicking outside
+        document.addEventListener('click', function(e) {
+            if (!translationButton.contains(e.target) && !translationDropdown.contains(e.target)) {
+                translationDropdown.classList.add('none');
+            }
+        });
+    }
+}
+
+// Initialize translation dropdown when DOM is ready
+document.addEventListener('DOMContentLoaded', function() {
+    initializeTranslationDropdown();
+});
diff --git a/src/views/elements/UsermessagesElement.php b/src/views/elements/UsermessagesElement.php
index 86b4dc227..98b1f2391 100644
--- a/src/views/elements/UsermessagesElement.php
+++ b/src/views/elements/UsermessagesElement.php
@@ -239,6 +239,51 @@ class UsermessagesElement extends Element implements CrawlConstants
                 $data["CONTACT_ICON_URL"]; ?>"
                 alt="<?=tl('usermessages_element_user_icon'); ?>" >
                 <span><?= $data['CONTACT_NAME'] ?></span>
+
+                <!-- Translation Button and Dropdown -->
+                <div class="translation-dropdown-container">
+                <?= $this->view->helper("iconlink")->renderButton(
+                    "javascript:void(0);", "translate", "", false, "translation-button",
+                    "dropdown-toggle", false); ?>
+                <div class="translation-dropdown none" id="translation-dropdown">
+                <?php
+                $language_options = [
+                    "off" => "Translation Off",
+                    "ar" => "Arabic",
+                    "bn" => "Bengali",
+                    "de" => "German",
+                    "el_GR" => "Greek",
+                    "en_US" => "English (US)",
+                    "es" => "Spanish",
+                    "fa" => "Persian",
+                    "fr_FR" => "French (France)",
+                    "he" => "Hebrew",
+                    "hi" => "Hindi",
+                    "id" => "Indonesian",
+                    "it" => "Italian",
+                    "ja" => "Japanese",
+                    "kn" => "Kannada",
+                    "ko" => "Korean",
+                    "nl" => "Dutch",
+                    "pl" => "Polish",
+                    "pt" => "Portuguese",
+                    "ru" => "Russian",
+                    "te" => "Telugu",
+                    "th" => "Thai",
+                    "tl" => "Tagalog",
+                    "tr" => "Turkish",
+                    "vi_VN" => "Vietnamese",
+                    "zh_CN" => "Chinese (Simplified)"
+                ];
+                $current_language = $data['TRANSLATION_LANGUAGE'] ?? 'off';
+                $translation_url = $messages_url . "&amp;contact_id=" . $data['CONTACT_ID'] .
+                    "&amp;arg=settranslation&amp;language=";
+                $this->view->helper("options")->renderLinkDropDown(
+                    "translation-language", $language_options, $current_language,
+                    $translation_url, false, "translation-dropdown-list", "");
+                ?>
+                </div>
+                </div>
             </h2>
             </div>
             <div id="av-call"></div>
-- 
2.39.5 (Apple Git-154)


From 564a76f542dcfdf3b79c7f76293daec87a649929 Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Tue, 11 Nov 2025 13:09:28 -0800
Subject: [PATCH 04/14] finished batch translation

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 .../components/SocialComponent.php            | 25 ++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index 70ae9210e..7d6278c97 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -2844,11 +2844,28 @@ class SocialComponent extends Component implements CrawlConstants
                 file_put_contents('/tmp/debug_loadmessages.txt',
                     "Message $index has content in $content_field: " . substr(strip_tags($original_text), 0, 100) . "...\n", FILE_APPEND);
 
-                // Only translate if it's actual text content (more than just numbers or whitespace)
+                // Only translate if it's actual text content (not media/system messages)
                 $clean_text = strip_tags($original_text);
                 $clean_text = trim($clean_text);
 
-                if (strlen($clean_text) > 2 && !ctype_digit($clean_text)) {
+                // Skip translation for media content, short content, or system messages
+                $is_media_message = (
+                    strpos($original_text, '<audio') !== false ||
+                    strpos($original_text, '<video') !== false ||
+                    strpos($original_text, '<img') !== false ||
+                    strpos($original_text, 'audio-message-container') !== false ||
+                    strpos($original_text, 'video-message-container') !== false ||
+                    strpos($original_text, 'image-message-container') !== false ||
+                    strpos($original_text, 'audio-message') !== false ||
+                    strpos($original_text, 'video-message') !== false ||
+                    strpos($original_text, 'image-message') !== false ||
+                    strpos($clean_text, 'Description audio_') !== false ||
+                    strpos($clean_text, 'Description video_') !== false ||
+                    strpos($original_text, 'transcribe-btn') !== false ||
+                    strpos($original_text, 'transcript-container') !== false
+                );
+
+                if (strlen($clean_text) > 5 && !ctype_digit($clean_text) && !$is_media_message) {
                     file_put_contents('/tmp/debug_loadmessages.txt',
                         "Translating message $index ($content_field): " . substr($clean_text, 0, 50) . "...\n", FILE_APPEND);
 
@@ -2864,8 +2881,10 @@ class SocialComponent extends Component implements CrawlConstants
                             "Translation failed for message $index\n", FILE_APPEND);
                     }
                 } else {
+                    $reason = $is_media_message ? "media content detected" :
+                             (strlen($clean_text) <= 5 ? "short content" : "numeric content");
                     file_put_contents('/tmp/debug_loadmessages.txt',
-                        "Skipping translation for message $index (short/numeric content)\n", FILE_APPEND);
+                        "Skipping translation for message $index ($reason)\n", FILE_APPEND);
                 }
             } else {
                 file_put_contents('/tmp/debug_loadmessages.txt',
-- 
2.39.5 (Apple Git-154)


From a99176910000f4f053b73c589847c0e0046ab2a2 Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Wed, 12 Nov 2025 22:52:25 -0800
Subject: [PATCH 05/14] fix language selection dropdown

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 .../components/SocialComponent.php            | 23 ++++++
 src/css/messages.css                          | 74 +------------------
 src/scripts/messages.js                       | 27 -------
 src/views/elements/UsermessagesElement.php    | 58 ++++++---------
 4 files changed, 48 insertions(+), 134 deletions(-)

diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index 7d6278c97..5d1772f95 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -2371,6 +2371,10 @@ class SocialComponent extends Component implements CrawlConstants
         if (!empty($contact_id)) {
             $data['TRANSLATION_LANGUAGE'] = $group_model->getTranslationLanguage($user_id, $contact_id);
         }
+
+        // Get available locales with native names
+        $locale_model = $parent->model("locale");
+        $data['AVAILABLE_LOCALES'] = $this->getTranslationLocales($locale_model);
         $message_actions = ['addcontact' => 'addContact',
             'blockcontact' => 'blockContact',
             'ignorecontact' => 'ignoreContact',
@@ -2898,6 +2902,25 @@ class SocialComponent extends Component implements CrawlConstants
 
         return $messages;
     }
+    /**
+     * Gets available translation locales with their native names
+     *
+     * @param object $locale_model Locale model instance
+     * @return array Array of locale codes and their native names
+     */
+    private function getTranslationLocales($locale_model)
+    {
+        $locales = $locale_model->getLocaleList(true, true);
+        $translation_locales = ["off" => "Translation Off"];
+
+        foreach ($locales as $locale) {
+            if (!empty($locale['LOCALE_TAG']) && !empty($locale['LOCALE_NAME'])) {
+                $translation_locales[$locale['LOCALE_TAG']] = $locale['LOCALE_NAME'];
+            }
+        }
+
+        return $translation_locales;
+    }
     /**
      * Translates a given text to the specified target language using LLM API
      *
diff --git a/src/css/messages.css b/src/css/messages.css
index ca5b1616b..76184a84f 100644
--- a/src/css/messages.css
+++ b/src/css/messages.css
@@ -674,80 +674,14 @@
     to { opacity: 1; }
 }
 
-/* Translation UI styles */
-.translation-dropdown-container {
-    position: relative;
+/* Translation dropdown wrapper - reuse existing Yioop dropdown styles */
+.translation-dropdown-wrapper {
     display: inline-block;
     margin-left: 10px;
 }
 
-.translation-dropdown-container h2 {
-    display: flex;
-    align-items: center;
-}
-
-.translation-dropdown {
-    position: absolute;
-    top: 100%;
-    right: 0;
-    z-index: 1000;
-    background-color: white;
-    border: 1px solid #ccc;
-    border-radius: 5px;
-    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+.translation-dropdown-wrapper .dropdown-container ul.link-dropdown ul {
     max-height: 200px;
     overflow-y: auto;
-    min-width: 200px;
-}
-
-.translation-dropdown-list {
-    list-style: none;
-    margin: 0;
-    padding: 0;
-}
-
-.translation-dropdown-list li {
-    border-bottom: 1px solid #eee;
-}
-
-.translation-dropdown-list li:last-child {
-    border-bottom: none;
-}
-
-.translation-dropdown-list a {
-    display: block;
-    padding: 8px 12px;
-    text-decoration: none;
-    color: #333;
-    font-size: 14px;
-}
-
-.translation-dropdown-list a:hover {
-    background-color: #f5f5f5;
-}
-
-.translation-dropdown-list a b {
-    font-weight: normal;
-    background-color: #e3f2fd;
-}
-
-.translation-button {
-    cursor: pointer;
-    opacity: 0.7;
-    transition: opacity 0.3s ease;
-}
-
-.translation-button:hover {
-    opacity: 1;
-}
-
-/* RTL support */
-.html-rtl .translation-dropdown-container {
-    right: auto;
-    left: 10px;
-}
-
-.html-rtl .translation-dropdown {
-    right: auto;
-    left: 0;
+    min-width: 180px;
 }
diff --git a/src/scripts/messages.js b/src/scripts/messages.js
index 503c969e7..aa6eb8d8f 100644
--- a/src/scripts/messages.js
+++ b/src/scripts/messages.js
@@ -594,30 +594,3 @@ function hideLoadingIndicator()
     }
 }
 
-/**
- * Initialize translation dropdown functionality
- */
-function initializeTranslationDropdown()
-{
-    const translationButton = document.querySelector('.translation-button');
-    const translationDropdown = document.getElementById('translation-dropdown');
-
-    if (translationButton && translationDropdown) {
-        translationButton.addEventListener('click', function(e) {
-            e.preventDefault();
-            translationDropdown.classList.toggle('none');
-        });
-
-        // Close dropdown when clicking outside
-        document.addEventListener('click', function(e) {
-            if (!translationButton.contains(e.target) && !translationDropdown.contains(e.target)) {
-                translationDropdown.classList.add('none');
-            }
-        });
-    }
-}
-
-// Initialize translation dropdown when DOM is ready
-document.addEventListener('DOMContentLoaded', function() {
-    initializeTranslationDropdown();
-});
diff --git a/src/views/elements/UsermessagesElement.php b/src/views/elements/UsermessagesElement.php
index 98b1f2391..28f009324 100644
--- a/src/views/elements/UsermessagesElement.php
+++ b/src/views/elements/UsermessagesElement.php
@@ -240,50 +240,34 @@ class UsermessagesElement extends Element implements CrawlConstants
                 alt="<?=tl('usermessages_element_user_icon'); ?>" >
                 <span><?= $data['CONTACT_NAME'] ?></span>
 
-                <!-- Translation Button and Dropdown -->
-                <div class="translation-dropdown-container">
-                <?= $this->view->helper("iconlink")->renderButton(
-                    "javascript:void(0);", "translate", "", false, "translation-button",
-                    "dropdown-toggle", false); ?>
-                <div class="translation-dropdown none" id="translation-dropdown">
+                <!-- Translation Dropdown -->
                 <?php
-                $language_options = [
-                    "off" => "Translation Off",
-                    "ar" => "Arabic",
-                    "bn" => "Bengali",
-                    "de" => "German",
-                    "el_GR" => "Greek",
-                    "en_US" => "English (US)",
-                    "es" => "Spanish",
-                    "fa" => "Persian",
-                    "fr_FR" => "French (France)",
-                    "he" => "Hebrew",
-                    "hi" => "Hindi",
-                    "id" => "Indonesian",
-                    "it" => "Italian",
-                    "ja" => "Japanese",
-                    "kn" => "Kannada",
-                    "ko" => "Korean",
-                    "nl" => "Dutch",
-                    "pl" => "Polish",
-                    "pt" => "Portuguese",
-                    "ru" => "Russian",
-                    "te" => "Telugu",
-                    "th" => "Thai",
-                    "tl" => "Tagalog",
-                    "tr" => "Turkish",
-                    "vi_VN" => "Vietnamese",
-                    "zh_CN" => "Chinese (Simplified)"
-                ];
                 $current_language = $data['TRANSLATION_LANGUAGE'] ?? 'off';
                 $translation_url = $messages_url . "&amp;contact_id=" . $data['CONTACT_ID'] .
                     "&amp;arg=settranslation&amp;language=";
+                $available_locales = $data['AVAILABLE_LOCALES'] ?? ["off" => "Translation Off"];
+
+                // Move current selection to top (keeping "off" at top if selected)
+                if ($current_language !== 'off' && isset($available_locales[$current_language])) {
+                    $current_locale_name = $available_locales[$current_language];
+                    unset($available_locales[$current_language]);
+                    $reordered_locales = [$current_language => $current_locale_name] + $available_locales;
+                    $show_top_text = $current_locale_name;
+                } else {
+                    $reordered_locales = $available_locales;
+                    $show_top_text = "Translation Off";
+                }
+
+                ?>
+                <div class="translation-dropdown-wrapper">
+                <?php
                 $this->view->helper("options")->renderLinkDropDown(
-                    "translation-language", $language_options, $current_language,
-                    $translation_url, false, "translation-dropdown-list", "");
+                    "translation-language", $reordered_locales, $current_language,
+                    $translation_url, false, "",
+                    $show_top_text);
                 ?>
                 </div>
-                </div>
+
             </h2>
             </div>
             <div id="av-call"></div>
-- 
2.39.5 (Apple Git-154)


From e1001544ad28239357025ff1875cb6954a5ec3d5 Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Wed, 12 Nov 2025 23:02:50 -0800
Subject: [PATCH 06/14] styling

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 src/css/messages.css                       | 23 ++++++++++++++--------
 src/views/elements/UsermessagesElement.php |  4 ----
 2 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/src/css/messages.css b/src/css/messages.css
index 76184a84f..3837d39a1 100644
--- a/src/css/messages.css
+++ b/src/css/messages.css
@@ -674,14 +674,21 @@
     to { opacity: 1; }
 }
 
-/* Translation dropdown wrapper - reuse existing Yioop dropdown styles */
-.translation-dropdown-wrapper {
-    display: inline-block;
-    margin-left: 10px;
+/* Limit translation dropdown height for scrolling */
+#translation-language-list {
+    max-height: 150px;
+    overflow-y: auto;
+    border-top: 1px solid #999;
+    border-bottom: 1px solid #999;
+    background: white;
 }
 
-.translation-dropdown-wrapper .dropdown-container ul.link-dropdown ul {
-    max-height: 200px;
-    overflow-y: auto;
-    min-width: 180px;
+/* Ensure scrollable content doesn't interfere with borders */
+#translation-language-list li:first-child {
+    border-top: none;
+}
+
+#translation-language-list li:last-child {
+    border-bottom: none;
 }
+
diff --git a/src/views/elements/UsermessagesElement.php b/src/views/elements/UsermessagesElement.php
index 28f009324..900e73f4b 100644
--- a/src/views/elements/UsermessagesElement.php
+++ b/src/views/elements/UsermessagesElement.php
@@ -258,15 +258,11 @@ class UsermessagesElement extends Element implements CrawlConstants
                     $show_top_text = "Translation Off";
                 }
 
-                ?>
-                <div class="translation-dropdown-wrapper">
-                <?php
                 $this->view->helper("options")->renderLinkDropDown(
                     "translation-language", $reordered_locales, $current_language,
                     $translation_url, false, "",
                     $show_top_text);
                 ?>
-                </div>
 
             </h2>
             </div>
-- 
2.39.5 (Apple Git-154)


From eb92264ef0926105996aa6b9f2b7c2d5f64b8f38 Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Wed, 12 Nov 2025 23:09:06 -0800
Subject: [PATCH 07/14] finish styling

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 src/controllers/components/SocialComponent.php | 2 +-
 src/css/messages.css                           | 2 +-
 src/views/elements/UsermessagesElement.php     | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index 5d1772f95..5ecfbfb59 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -2911,7 +2911,7 @@ class SocialComponent extends Component implements CrawlConstants
     private function getTranslationLocales($locale_model)
     {
         $locales = $locale_model->getLocaleList(true, true);
-        $translation_locales = ["off" => "Translation Off"];
+        $translation_locales = ["off" => tl('toggle_helper_off')];
 
         foreach ($locales as $locale) {
             if (!empty($locale['LOCALE_TAG']) && !empty($locale['LOCALE_NAME'])) {
diff --git a/src/css/messages.css b/src/css/messages.css
index 3837d39a1..1c26ed0cf 100644
--- a/src/css/messages.css
+++ b/src/css/messages.css
@@ -674,7 +674,7 @@
     to { opacity: 1; }
 }
 
-/* Limit translation dropdown height for scrolling */
+/* Translation dropdown styling */
 #translation-language-list {
     max-height: 150px;
     overflow-y: auto;
diff --git a/src/views/elements/UsermessagesElement.php b/src/views/elements/UsermessagesElement.php
index 900e73f4b..e0d39c13e 100644
--- a/src/views/elements/UsermessagesElement.php
+++ b/src/views/elements/UsermessagesElement.php
@@ -245,7 +245,7 @@ class UsermessagesElement extends Element implements CrawlConstants
                 $current_language = $data['TRANSLATION_LANGUAGE'] ?? 'off';
                 $translation_url = $messages_url . "&amp;contact_id=" . $data['CONTACT_ID'] .
                     "&amp;arg=settranslation&amp;language=";
-                $available_locales = $data['AVAILABLE_LOCALES'] ?? ["off" => "Translation Off"];
+                $available_locales = $data['AVAILABLE_LOCALES'] ?? ["off" => tl('toggle_helper_off')];
 
                 // Move current selection to top (keeping "off" at top if selected)
                 if ($current_language !== 'off' && isset($available_locales[$current_language])) {
@@ -255,7 +255,7 @@ class UsermessagesElement extends Element implements CrawlConstants
                     $show_top_text = $current_locale_name;
                 } else {
                     $reordered_locales = $available_locales;
-                    $show_top_text = "Translation Off";
+                    $show_top_text = tl('toggle_helper_off');
                 }
 
                 $this->view->helper("options")->renderLinkDropDown(
-- 
2.39.5 (Apple Git-154)


From 75f92b09d412fc7f3aef9b938776f46b7a854bb9 Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Wed, 12 Nov 2025 23:15:38 -0800
Subject: [PATCH 08/14] auto translate to language set in profile

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 .../components/SocialComponent.php            | 53 ++++++++++++++-----
 src/views/elements/UsermessagesElement.php    | 16 +++++-
 2 files changed, 56 insertions(+), 13 deletions(-)

diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index 5ecfbfb59..4b648546e 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -2372,9 +2372,10 @@ class SocialComponent extends Component implements CrawlConstants
             $data['TRANSLATION_LANGUAGE'] = $group_model->getTranslationLanguage($user_id, $contact_id);
         }
 
-        // Get available locales with native names
+        // Get available locales with native names and user's current locale
         $locale_model = $parent->model("locale");
         $data['AVAILABLE_LOCALES'] = $this->getTranslationLocales($locale_model);
+        $data['CURRENT_LOCALE_TAG'] = L\getLocaleTag();
         $message_actions = ['addcontact' => 'addContact',
             'blockcontact' => 'blockContact',
             'ignorecontact' => 'ignoreContact',
@@ -2461,16 +2462,30 @@ class SocialComponent extends Component implements CrawlConstants
                     [], $user_id, $search_array, -2, "krsort", $limit,
                     15);
 
-                // Apply translation if enabled
+                // Apply translation if enabled or if user's locale differs from default
+                $translation_target = null;
+                if (!empty($data['TRANSLATION_LANGUAGE']) && $data['TRANSLATION_LANGUAGE'] !== 'off') {
+                    // Explicit translation language selected
+                    $translation_target = $data['TRANSLATION_LANGUAGE'];
+                } elseif (empty($data['TRANSLATION_LANGUAGE']) || $data['TRANSLATION_LANGUAGE'] === 'off') {
+                    // Check if user's locale requires auto-translation
+                    $user_locale = $data['CURRENT_LOCALE_TAG'] ?? 'en_US';
+                    if ($user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE) {
+                        $translation_target = $user_locale;
+                    }
+                }
+
                 file_put_contents('/tmp/debug_loadmessages.txt',
-                    "Initial load translation language: '" . ($data['TRANSLATION_LANGUAGE'] ?? 'not set') . "'\n", FILE_APPEND);
-                if (!empty($data['TRANSLATION_LANGUAGE'])) {
+                    "Translation target: '" . ($translation_target ?? 'none') . "', user locale: '" .
+                    ($data['CURRENT_LOCALE_TAG'] ?? 'not set') . "'\n", FILE_APPEND);
+
+                if (!empty($translation_target)) {
                     file_put_contents('/tmp/debug_loadmessages.txt',
-                        "Starting initial translation to: " . $data['TRANSLATION_LANGUAGE'] . "\n", FILE_APPEND);
+                        "Starting translation to: " . $translation_target . "\n", FILE_APPEND);
                     $data['MESSAGES'] = $this->translateMessageBatch(
-                        $data['MESSAGES'], $data['TRANSLATION_LANGUAGE']);
+                        $data['MESSAGES'], $translation_target);
                     file_put_contents('/tmp/debug_loadmessages.txt',
-                        "Initial translation completed\n", FILE_APPEND);
+                        "Translation completed\n", FILE_APPEND);
                 }
             }
         }
@@ -3158,14 +3173,28 @@ class SocialComponent extends Component implements CrawlConstants
             $messages = $group_model->getGroupItems(0, $limit, $search_array, $user_id, -2);
         }
 
-        // Apply translation if enabled
+        // Apply translation if enabled or if user's locale differs from default
         $translation_language = $group_model->getTranslationLanguage($user_id, $contact_id);
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "Translation language for user $user_id, contact $contact_id: '$translation_language'\n", FILE_APPEND);
+        $user_locale = L\getLocaleTag();
+        $translation_target = null;
+
         if (!empty($translation_language) && $translation_language !== 'off') {
+            // Explicit translation language selected
+            $translation_target = $translation_language;
+        } elseif (empty($translation_language) || $translation_language === 'off') {
+            // Check if user's locale requires auto-translation
+            if ($user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE) {
+                $translation_target = $user_locale;
+            }
+        }
+
+        file_put_contents('/tmp/debug_loadmessages.txt',
+            "Translation target: '" . ($translation_target ?? 'none') . "', user locale: '$user_locale'\n", FILE_APPEND);
+
+        if (!empty($translation_target)) {
             file_put_contents('/tmp/debug_loadmessages.txt',
-                "Starting translation to: $translation_language\n", FILE_APPEND);
-            $messages = $this->translateMessageBatch($messages, $translation_language);
+                "Starting translation to: $translation_target\n", FILE_APPEND);
+            $messages = $this->translateMessageBatch($messages, $translation_target);
             file_put_contents('/tmp/debug_loadmessages.txt',
                 "Translation completed for " . count($messages) . " messages\n", FILE_APPEND);
         }
diff --git a/src/views/elements/UsermessagesElement.php b/src/views/elements/UsermessagesElement.php
index e0d39c13e..529447687 100644
--- a/src/views/elements/UsermessagesElement.php
+++ b/src/views/elements/UsermessagesElement.php
@@ -247,13 +247,27 @@ class UsermessagesElement extends Element implements CrawlConstants
                     "&amp;arg=settranslation&amp;language=";
                 $available_locales = $data['AVAILABLE_LOCALES'] ?? ["off" => tl('toggle_helper_off')];
 
-                // Move current selection to top (keeping "off" at top if selected)
+                // Determine what to show - explicit setting, auto-translation, or off
+                $user_locale = $data['CURRENT_LOCALE_TAG'] ?? 'en_US';
+                $is_auto_translating = ($current_language === 'off' || empty($current_language)) &&
+                                      $user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE &&
+                                      isset($available_locales[$user_locale]);
+
                 if ($current_language !== 'off' && isset($available_locales[$current_language])) {
+                    // Explicit translation language selected
                     $current_locale_name = $available_locales[$current_language];
                     unset($available_locales[$current_language]);
                     $reordered_locales = [$current_language => $current_locale_name] + $available_locales;
                     $show_top_text = $current_locale_name;
+                } elseif ($is_auto_translating) {
+                    // Auto-translating to user's locale
+                    $current_locale_name = $available_locales[$user_locale];
+                    unset($available_locales[$user_locale]);
+                    $reordered_locales = [$user_locale => $current_locale_name] + $available_locales;
+                    $show_top_text = $current_locale_name;
+                    $current_language = $user_locale; // Update for proper selection
                 } else {
+                    // Translation off
                     $reordered_locales = $available_locales;
                     $show_top_text = tl('toggle_helper_off');
                 }
-- 
2.39.5 (Apple Git-154)


From 0132616c2b6a666bd67d729261393182f6388d81 Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Thu, 13 Nov 2025 00:25:25 -0800
Subject: [PATCH 09/14] do not show translate button if LLM not configured

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 .../components/SocialComponent.php            | 96 ++++++++++---------
 src/views/elements/UsermessagesElement.php    |  6 +-
 2 files changed, 55 insertions(+), 47 deletions(-)

diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index 4b648546e..23d83f57a 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -2372,10 +2372,12 @@ class SocialComponent extends Component implements CrawlConstants
             $data['TRANSLATION_LANGUAGE'] = $group_model->getTranslationLanguage($user_id, $contact_id);
         }
 
-        // Get available locales with native names and user's current locale
-        $locale_model = $parent->model("locale");
-        $data['AVAILABLE_LOCALES'] = $this->getTranslationLocales($locale_model);
-        $data['CURRENT_LOCALE_TAG'] = L\getLocaleTag();
+        // Get available locales with native names and user's current locale (only if LLM is configured)
+        if (!empty(C\LLM_API_URL) && !empty(C\LLM_MODEL)) {
+            $locale_model = $parent->model("locale");
+            $data['AVAILABLE_LOCALES'] = $this->getTranslationLocales($locale_model);
+            $data['CURRENT_LOCALE_TAG'] = L\getLocaleTag();
+        }
         $message_actions = ['addcontact' => 'addContact',
             'blockcontact' => 'blockContact',
             'ignorecontact' => 'ignoreContact',
@@ -2462,30 +2464,32 @@ class SocialComponent extends Component implements CrawlConstants
                     [], $user_id, $search_array, -2, "krsort", $limit,
                     15);
 
-                // Apply translation if enabled or if user's locale differs from default
-                $translation_target = null;
-                if (!empty($data['TRANSLATION_LANGUAGE']) && $data['TRANSLATION_LANGUAGE'] !== 'off') {
-                    // Explicit translation language selected
-                    $translation_target = $data['TRANSLATION_LANGUAGE'];
-                } elseif (empty($data['TRANSLATION_LANGUAGE']) || $data['TRANSLATION_LANGUAGE'] === 'off') {
-                    // Check if user's locale requires auto-translation
-                    $user_locale = $data['CURRENT_LOCALE_TAG'] ?? 'en_US';
-                    if ($user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE) {
-                        $translation_target = $user_locale;
+                // Apply translation if enabled or if user's locale differs from default (only if LLM configured)
+                if (!empty(C\LLM_API_URL) && !empty(C\LLM_MODEL)) {
+                    $translation_target = null;
+                    if (!empty($data['TRANSLATION_LANGUAGE']) && $data['TRANSLATION_LANGUAGE'] !== 'off') {
+                        // Explicit translation language selected
+                        $translation_target = $data['TRANSLATION_LANGUAGE'];
+                    } elseif (empty($data['TRANSLATION_LANGUAGE']) || $data['TRANSLATION_LANGUAGE'] === 'off') {
+                        // Check if user's locale requires auto-translation
+                        $user_locale = $data['CURRENT_LOCALE_TAG'] ?? 'en_US';
+                        if ($user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE) {
+                            $translation_target = $user_locale;
+                        }
                     }
-                }
-
-                file_put_contents('/tmp/debug_loadmessages.txt',
-                    "Translation target: '" . ($translation_target ?? 'none') . "', user locale: '" .
-                    ($data['CURRENT_LOCALE_TAG'] ?? 'not set') . "'\n", FILE_APPEND);
 
-                if (!empty($translation_target)) {
                     file_put_contents('/tmp/debug_loadmessages.txt',
-                        "Starting translation to: " . $translation_target . "\n", FILE_APPEND);
-                    $data['MESSAGES'] = $this->translateMessageBatch(
-                        $data['MESSAGES'], $translation_target);
-                    file_put_contents('/tmp/debug_loadmessages.txt',
-                        "Translation completed\n", FILE_APPEND);
+                        "Translation target: '" . ($translation_target ?? 'none') . "', user locale: '" .
+                        ($data['CURRENT_LOCALE_TAG'] ?? 'not set') . "'\n", FILE_APPEND);
+
+                    if (!empty($translation_target)) {
+                        file_put_contents('/tmp/debug_loadmessages.txt',
+                            "Starting translation to: " . $translation_target . "\n", FILE_APPEND);
+                        $data['MESSAGES'] = $this->translateMessageBatch(
+                            $data['MESSAGES'], $translation_target);
+                        file_put_contents('/tmp/debug_loadmessages.txt',
+                            "Translation completed\n", FILE_APPEND);
+                    }
                 }
             }
         }
@@ -3173,30 +3177,32 @@ class SocialComponent extends Component implements CrawlConstants
             $messages = $group_model->getGroupItems(0, $limit, $search_array, $user_id, -2);
         }
 
-        // Apply translation if enabled or if user's locale differs from default
-        $translation_language = $group_model->getTranslationLanguage($user_id, $contact_id);
-        $user_locale = L\getLocaleTag();
-        $translation_target = null;
+        // Apply translation if enabled or if user's locale differs from default (only if LLM configured)
+        if (!empty(C\LLM_API_URL) && !empty(C\LLM_MODEL)) {
+            $translation_language = $group_model->getTranslationLanguage($user_id, $contact_id);
+            $user_locale = L\getLocaleTag();
+            $translation_target = null;
 
-        if (!empty($translation_language) && $translation_language !== 'off') {
-            // Explicit translation language selected
-            $translation_target = $translation_language;
-        } elseif (empty($translation_language) || $translation_language === 'off') {
-            // Check if user's locale requires auto-translation
-            if ($user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE) {
-                $translation_target = $user_locale;
+            if (!empty($translation_language) && $translation_language !== 'off') {
+                // Explicit translation language selected
+                $translation_target = $translation_language;
+            } elseif (empty($translation_language) || $translation_language === 'off') {
+                // Check if user's locale requires auto-translation
+                if ($user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE) {
+                    $translation_target = $user_locale;
+                }
             }
-        }
 
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "Translation target: '" . ($translation_target ?? 'none') . "', user locale: '$user_locale'\n", FILE_APPEND);
-
-        if (!empty($translation_target)) {
-            file_put_contents('/tmp/debug_loadmessages.txt',
-                "Starting translation to: $translation_target\n", FILE_APPEND);
-            $messages = $this->translateMessageBatch($messages, $translation_target);
             file_put_contents('/tmp/debug_loadmessages.txt',
-                "Translation completed for " . count($messages) . " messages\n", FILE_APPEND);
+                "Translation target: '" . ($translation_target ?? 'none') . "', user locale: '$user_locale'\n", FILE_APPEND);
+
+            if (!empty($translation_target)) {
+                file_put_contents('/tmp/debug_loadmessages.txt',
+                    "Starting translation to: $translation_target\n", FILE_APPEND);
+                $messages = $this->translateMessageBatch($messages, $translation_target);
+                file_put_contents('/tmp/debug_loadmessages.txt',
+                    "Translation completed for " . count($messages) . " messages\n", FILE_APPEND);
+            }
         }
 
         $has_more = count($messages) == $limit;
diff --git a/src/views/elements/UsermessagesElement.php b/src/views/elements/UsermessagesElement.php
index 529447687..be29e93fd 100644
--- a/src/views/elements/UsermessagesElement.php
+++ b/src/views/elements/UsermessagesElement.php
@@ -240,12 +240,13 @@ class UsermessagesElement extends Element implements CrawlConstants
                 alt="<?=tl('usermessages_element_user_icon'); ?>" >
                 <span><?= $data['CONTACT_NAME'] ?></span>
 
-                <!-- Translation Dropdown -->
+                <!-- Translation Dropdown (only if LLM is configured) -->
                 <?php
+                if (!empty($data['AVAILABLE_LOCALES'])) {
                 $current_language = $data['TRANSLATION_LANGUAGE'] ?? 'off';
                 $translation_url = $messages_url . "&amp;contact_id=" . $data['CONTACT_ID'] .
                     "&amp;arg=settranslation&amp;language=";
-                $available_locales = $data['AVAILABLE_LOCALES'] ?? ["off" => tl('toggle_helper_off')];
+                $available_locales = $data['AVAILABLE_LOCALES'];
 
                 // Determine what to show - explicit setting, auto-translation, or off
                 $user_locale = $data['CURRENT_LOCALE_TAG'] ?? 'en_US';
@@ -276,6 +277,7 @@ class UsermessagesElement extends Element implements CrawlConstants
                     "translation-language", $reordered_locales, $current_language,
                     $translation_url, false, "",
                     $show_top_text);
+                }
                 ?>
 
             </h2>
-- 
2.39.5 (Apple Git-154)


From 92c17bead11eb726f167f4a8c8446e8a5b0c338c Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Thu, 13 Nov 2025 01:00:25 -0800
Subject: [PATCH 10/14] remove extra debugging logs

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 .../components/SocialComponent.php            | 88 -------------------
 1 file changed, 88 deletions(-)

diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index 23d83f57a..14485f4a4 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -2478,17 +2478,9 @@ class SocialComponent extends Component implements CrawlConstants
                         }
                     }
 
-                    file_put_contents('/tmp/debug_loadmessages.txt',
-                        "Translation target: '" . ($translation_target ?? 'none') . "', user locale: '" .
-                        ($data['CURRENT_LOCALE_TAG'] ?? 'not set') . "'\n", FILE_APPEND);
-
                     if (!empty($translation_target)) {
-                        file_put_contents('/tmp/debug_loadmessages.txt',
-                            "Starting translation to: " . $translation_target . "\n", FILE_APPEND);
                         $data['MESSAGES'] = $this->translateMessageBatch(
                             $data['MESSAGES'], $translation_target);
-                        file_put_contents('/tmp/debug_loadmessages.txt',
-                            "Translation completed\n", FILE_APPEND);
                     }
                 }
             }
@@ -2837,19 +2829,12 @@ class SocialComponent extends Component implements CrawlConstants
      */
     private function translateMessageBatch($messages, $target_language)
     {
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "translateMessageBatch called with " . count($messages) . " messages, target: $target_language\n", FILE_APPEND);
-
         if (empty($messages) || empty($target_language) || $target_language == 'off') {
-            file_put_contents('/tmp/debug_loadmessages.txt',
-                "translateMessageBatch: Early return - messages empty or language off\n", FILE_APPEND);
             return $messages;
         }
 
         $translated_count = 0;
         foreach ($messages as $index => &$message) {
-            file_put_contents('/tmp/debug_loadmessages.txt',
-                "Message $index: " . print_r(array_keys($message), true), FILE_APPEND);
 
             // Check for content in 't' field (processed content) or 'DESCRIPTION' field
             $content_field = null;
@@ -2864,8 +2849,6 @@ class SocialComponent extends Component implements CrawlConstants
             }
 
             if ($content_field && $original_text) {
-                file_put_contents('/tmp/debug_loadmessages.txt',
-                    "Message $index has content in $content_field: " . substr(strip_tags($original_text), 0, 100) . "...\n", FILE_APPEND);
 
                 // Only translate if it's actual text content (not media/system messages)
                 $clean_text = strip_tags($original_text);
@@ -2889,36 +2872,18 @@ class SocialComponent extends Component implements CrawlConstants
                 );
 
                 if (strlen($clean_text) > 5 && !ctype_digit($clean_text) && !$is_media_message) {
-                    file_put_contents('/tmp/debug_loadmessages.txt',
-                        "Translating message $index ($content_field): " . substr($clean_text, 0, 50) . "...\n", FILE_APPEND);
-
                     $translation_result = $this->translateText($original_text, $target_language);
 
                     if (!empty($translation_result['translation'])) {
                         $message[$content_field] = $translation_result['translation'];
                         $message['ORIGINAL_' . $content_field] = $original_text;
                         $translated_count++;
-                    } else {
-                        // Keep original text if translation fails
-                        file_put_contents('/tmp/debug_loadmessages.txt',
-                            "Translation failed for message $index\n", FILE_APPEND);
                     }
-                } else {
-                    $reason = $is_media_message ? "media content detected" :
-                             (strlen($clean_text) <= 5 ? "short content" : "numeric content");
-                    file_put_contents('/tmp/debug_loadmessages.txt',
-                        "Skipping translation for message $index ($reason)\n", FILE_APPEND);
                 }
-            } else {
-                file_put_contents('/tmp/debug_loadmessages.txt',
-                    "Message $index: No content field found\n", FILE_APPEND);
             }
         }
 
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "translateMessageBatch completed: translated $translated_count messages\n", FILE_APPEND);
         unset($message); // Break reference
-
         return $messages;
     }
     /**
@@ -2949,18 +2914,11 @@ class SocialComponent extends Component implements CrawlConstants
      */
     private function translateText($text, $target_lang)
     {
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "translateText called: '$text' -> '$target_lang'\n", FILE_APPEND);
-
         if (empty($text) || empty($target_lang)) {
-            file_put_contents('/tmp/debug_loadmessages.txt',
-                "translateText: Missing parameters\n", FILE_APPEND);
             return ['errors' => ["Missing required parameters"]];
         }
 
         $api_url = C\LLM_API_URL ?? "http://localhost:1234/v1/chat/completions";
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "translateText: API URL = $api_url\n", FILE_APPEND);
         $model = C\LLM_MODEL ?? "aya-23-8b";
 
         $language_map = [ "ar" => "Arabic", "bn" => "Bengali", "de" => "German",
@@ -2995,20 +2953,11 @@ class SocialComponent extends Component implements CrawlConstants
 
         $post_data = json_encode($request_body);
         $headers = ['Content-Type: application/json'];
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "translateText: Making API call to $api_url\n", FILE_APPEND);
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "translateText: Request body = $post_data\n", FILE_APPEND);
 
         $response = L\FetchUrl::getPage($api_url, $post_data, true, null,
             C\SINGLE_PAGE_TIMEOUT, $headers);
 
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "translateText: Response = " . ($response === false ? 'FALSE' : substr($response, 0, 200)) . "\n", FILE_APPEND);
-
         if ($response === false) {
-            file_put_contents('/tmp/debug_loadmessages.txt',
-                "translateText: API call failed\n", FILE_APPEND);
             return ['errors' => ["Failed to connect to translation service"]];
         }
 
@@ -3098,31 +3047,12 @@ class SocialComponent extends Component implements CrawlConstants
         $group_model = $parent->model("group");
         $user_model = $parent->model("user");
 
-        // Debug logging to verify the method is being called
-        error_log("loadMoreMessages called with user_id: $user_id");
-        error_log("REQUEST params: " . print_r($_REQUEST, true));
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "TIME: " . date('Y-m-d H:i:s') . "\n" .
-            "USER_ID: $user_id\n" .
-            "REQUEST: " . print_r($_REQUEST, true) . "\n" .
-            "METHOD: " . $_SERVER['REQUEST_METHOD'] . "\n" .
-            "URI: " . $_SERVER['REQUEST_URI'] . "\n\n",
-            FILE_APPEND);
 
         $contact_id = $parent->clean($_REQUEST['contact_id'] ?? 0, 'int');
         $before_timestamp = $parent->clean($_REQUEST['before_timestamp'] ?? 0, 'int');
         $limit = $parent->clean($_REQUEST['limit'] ?? 20, 'int');
 
-        error_log("Parsed params - contact_id: $contact_id, before_timestamp: $before_timestamp, limit: $limit");
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "PARSED: contact_id=$contact_id, before_timestamp=$before_timestamp, limit=$limit\n",
-            FILE_APPEND);
-
         if (empty($contact_id) || empty($before_timestamp)) {
-            error_log("Missing required params - contact_id: '$contact_id', before_timestamp: '$before_timestamp'");
-            file_put_contents('/tmp/debug_loadmessages.txt',
-                "ERROR: Missing params - contact_id=$contact_id, before_timestamp=$before_timestamp\n",
-                FILE_APPEND);
             header('Content-Type: application/json');
             echo json_encode(['error' => 'Missing required parameters',
                 'contact_id' => $contact_id, 'before_timestamp' => $before_timestamp]);
@@ -3134,30 +3064,19 @@ class SocialComponent extends Component implements CrawlConstants
         }
 
         $user_messages_id = $group_model->getPersonalGroupId($user_id);
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "Got user_messages_id: $user_messages_id\n", FILE_APPEND);
 
         $messages_thread_title = $group_model->getMessagesThreadTitle(
             [$user_id, $contact_id]);
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "Got messages_thread_title: $messages_thread_title\n", FILE_APPEND);
 
         $message_thread_id = $group_model->getGroupThreadId($user_messages_id,
             $user_id, $messages_thread_title);
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "Got message_thread_id: $message_thread_id\n", FILE_APPEND);
 
         if (empty($message_thread_id)) {
-            file_put_contents('/tmp/debug_loadmessages.txt',
-                "ERROR: Empty message_thread_id\n", FILE_APPEND);
             header('Content-Type: application/json');
             echo json_encode(['error' => 'Invalid message thread']);
             exit();
         }
 
-        file_put_contents('/tmp/debug_loadmessages.txt',
-            "About to query messages...\n", FILE_APPEND);
-
         $search_array = [
             ["parent_id", "=", $message_thread_id, ""],
             ["group_id", "=", $user_messages_id, ""],
@@ -3193,15 +3112,8 @@ class SocialComponent extends Component implements CrawlConstants
                 }
             }
 
-            file_put_contents('/tmp/debug_loadmessages.txt',
-                "Translation target: '" . ($translation_target ?? 'none') . "', user locale: '$user_locale'\n", FILE_APPEND);
-
             if (!empty($translation_target)) {
-                file_put_contents('/tmp/debug_loadmessages.txt',
-                    "Starting translation to: $translation_target\n", FILE_APPEND);
                 $messages = $this->translateMessageBatch($messages, $translation_target);
-                file_put_contents('/tmp/debug_loadmessages.txt',
-                    "Translation completed for " . count($messages) . " messages\n", FILE_APPEND);
             }
         }
 
-- 
2.39.5 (Apple Git-154)


From e8ad134ca937a75abb5f8e32f4ec9b8a7e95f678 Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Fri, 14 Nov 2025 09:38:33 -0800
Subject: [PATCH 11/14] working tl

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 src/configs/Config.php                        |   4 +-
 src/controllers/ApiController.php             |  35 +++++++++------
 .../components/SocialComponent.php            |  41 ++++++++++--------
 src/data/public_default.db-shm                | Bin 0 -> 32768 bytes
 src/data/public_default.db-wal                |   0
 src/scripts/messages.js                       |   9 ++--
 src/views/elements/UsermessagesElement.php    |  38 +++++++++-------
 7 files changed, 76 insertions(+), 51 deletions(-)
 create mode 100644 src/data/public_default.db-shm
 create mode 100644 src/data/public_default.db-wal

diff --git a/src/configs/Config.php b/src/configs/Config.php
index cb681fa34..577ea5d84 100755
--- a/src/configs/Config.php
+++ b/src/configs/Config.php
@@ -359,9 +359,9 @@ nsdefine('MAX_SUMMARIZE_MESSAGES', 10000);
 /** URL endpoint for LLM API service used for AI summarization
  * For example, LM studio api url (http://localhost:1234/v1/chat/completions)
  */
-nsconddefine('LLM_API_URL', 'http://localhost:1234/v1/chat/completions');
+nsconddefine('LLM_API_URL', '');
 /** Model name to use for LLM API requests - for example, "aya-23-8b" */
-nsconddefine('LLM_MODEL', 'aya-23-8b');
+nsconddefine('LLM_MODEL', '');
 /** Whether to use conjunctive search queries or disjunctive */
 nsconddefine('USE_CONJUNCTIVE_QUERY', false);
 /** setting Profile.php to something else in LocalConfig.php allows one to have
diff --git a/src/controllers/ApiController.php b/src/controllers/ApiController.php
index 8d36a1aef..917e91eb2 100644
--- a/src/controllers/ApiController.php
+++ b/src/controllers/ApiController.php
@@ -55,7 +55,8 @@ class ApiController extends Controller implements CrawlConstants
      * These are the activities supported by this controller
      * @var array
      */
-    public $activities = ["summarize", "transcribe", "transcriptionStatus", "translate"];
+    public $activities = ["summarize", "transcribe", "transcriptionStatus",
+        "translate"];
     /**
      * Used to process requests related to user group activities outside of
      * the admin panel setting. This either could be because the admin panel
@@ -424,7 +425,7 @@ class ApiController extends Controller implements CrawlConstants
         return $data;
     }
     /**
-     * Handles a request to translate text from one language to another using LLM
+     * Handles a request to translate text from one language to another
      *
      * @return array $data containing translation results and status
      */
@@ -433,7 +434,8 @@ class ApiController extends Controller implements CrawlConstants
         $data = [];
         $data['ELEMENT'] = 'translate';
         if (empty($_REQUEST['text']) || empty($_REQUEST['target_lang'])) {
-            $data['errors'] = ["Missing required parameters: 'text' and 'target_lang' are required"];
+            $data['errors'] = ["Missing required parameters: 'text' and " .
+                "'target_lang' are required"];
             return $data;
         }
         $text = $this->clean($_REQUEST['text'], "string");
@@ -453,12 +455,15 @@ class ApiController extends Controller implements CrawlConstants
         $data = [];
         $data['ELEMENT'] = 'translate';
         if (empty($text) || empty($target_lang)) {
-            $data['errors'] = ["Missing required parameters: 'text' and 'target_lang' are required"];
+            $data['errors'] = ["Missing required parameters: 'text' and " .
+                "'target_lang' are required"];
             return $data;
         }
-        $api_url = C\LLM_API_URL ?? "http://localhost:1234/v1/chat/completions";
+        $api_url = C\LLM_API_URL ??
+            "http://localhost:1234/v1/chat/completions";
         $model = C\LLM_MODEL ?? "aya-23-8b";
-        $language_map = [ "ar" => "Arabic", "bn" => "Bengali", "de" => "German",
+        $language_map = [
+            "ar" => "Arabic", "bn" => "Bengali", "de" => "German",
             "el_GR" => "Greek", "en_US" => "English (US)", "es" => "Spanish",
             "fa" => "Persian", "fr_FR" => "French (France)", "he" => "Hebrew",
             "hi" => "Hindi", "id" => "Indonesian", "it" => "Italian",
@@ -468,19 +473,23 @@ class ApiController extends Controller implements CrawlConstants
             "tl" => "Tagalog", "tr" => "Turkish", "vi_VN" => "Vietnamese",
             "zh_CN" => "Chinese (Simplified)"
         ];
-        $mapping_string = "Use the following language mapping for accurate translations:\n";
+        $mapping_string = "Use the following language mapping for " .
+            "accurate translations:\n";
         foreach ($language_map as $code => $lang) {
             $mapping_string .= "$code → $lang\n";
         }
-        $translate_prompt = "Translate the text that is in following sequence of <in></in> tags to the locale " .
-            $target_lang . ". Assume the context is text that might appear in a chat message. " .
+        $translate_prompt = "Translate the text that is in following " .
+            "sequence of <in></in> tags to the locale " . $target_lang .
+            ". Assume the context is text that might appear in a chat message. " .
             "You should decode any html entities before doing the translation. " .
-            "If you see text like '%s' in the input treat it like a placeholder that should appear verbatim in the appropriate place in the output. " .
-            "Output the results between <out></out> tags. Do not add any explanations. Do not repeat the output translations. " .
-            "<in>" . $text . "</in>";
+            "If you see text like '%s' in the input treat it like a " .
+            "placeholder that should appear verbatim in the appropriate place " .
+            "in the output. Output the results between <out></out> tags. " .
+            "Do not add any explanations. Do not repeat the output " .
+            "translations. <in>" . $text . "</in>";
         $request_body = [ "model" => $model, "messages" => [["role" => "system",
             "content" => "You are a helpful assistant who translates text" .
-            "from one language to another. Use this predefined mapping ".
+            " from one language to another. Use this predefined mapping " .
             "for accurate language detection:\n" . $mapping_string],
             ["role" => "user", "content" => $translate_prompt]],
             "temperature" => 0.1, "max_tokens" => -1, "stream" => false];
diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index 14485f4a4..dcfe8764b 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -2386,10 +2386,8 @@ class SocialComponent extends Component implements CrawlConstants
             'settranslation' => 'setTranslationLanguage', "status" =>
             "messagesStatus"];
         $arg = $_REQUEST['arg'] ?? "";
-        error_log("userMessages arg: " . $arg . ", available actions: " . implode(', ', array_keys($message_actions)));
         if (in_array($arg, array_keys($message_actions))) {
             $action = $message_actions[$arg];
-            error_log("Calling action: " . $action);
             if (!empty($contact_filter)) {
                 $data['CONTACT_ID'] = $user_model->getUserId($contact_filter);
                 $data['CONTACT_NAME'] = $contact_filter;
@@ -2464,16 +2462,18 @@ class SocialComponent extends Component implements CrawlConstants
                     [], $user_id, $search_array, -2, "krsort", $limit,
                     15);
 
-                // Apply translation if enabled or if user's locale differs from default (only if LLM configured)
+                // Apply translation if enabled or user locale differs from default
                 if (!empty(C\LLM_API_URL) && !empty(C\LLM_MODEL)) {
                     $translation_target = null;
-                    if (!empty($data['TRANSLATION_LANGUAGE']) && $data['TRANSLATION_LANGUAGE'] !== 'off') {
+                    $translation_lang = $data['TRANSLATION_LANGUAGE'] ?? '';
+                    if (!empty($translation_lang) && $translation_lang !== 'off') {
                         // Explicit translation language selected
-                        $translation_target = $data['TRANSLATION_LANGUAGE'];
-                    } elseif (empty($data['TRANSLATION_LANGUAGE']) || $data['TRANSLATION_LANGUAGE'] === 'off') {
+                        $translation_target = $translation_lang;
+                    } elseif (empty($translation_lang) || $translation_lang === 'off') {
                         // Check if user's locale requires auto-translation
                         $user_locale = $data['CURRENT_LOCALE_TAG'] ?? 'en_US';
-                        if ($user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE) {
+                        if ($user_locale !== 'en_US' &&
+                            $user_locale !== C\DEFAULT_LOCALE) {
                             $translation_target = $user_locale;
                         }
                     }
@@ -2829,14 +2829,15 @@ class SocialComponent extends Component implements CrawlConstants
      */
     private function translateMessageBatch($messages, $target_language)
     {
-        if (empty($messages) || empty($target_language) || $target_language == 'off') {
+        if (empty($messages) || empty($target_language) ||
+            $target_language == 'off') {
             return $messages;
         }
 
         $translated_count = 0;
         foreach ($messages as $index => &$message) {
 
-            // Check for content in 't' field (processed content) or 'DESCRIPTION' field
+            // Check for content in 't' field or 'DESCRIPTION' field
             $content_field = null;
             $original_text = null;
 
@@ -2850,11 +2851,11 @@ class SocialComponent extends Component implements CrawlConstants
 
             if ($content_field && $original_text) {
 
-                // Only translate if it's actual text content (not media/system messages)
+                // Only translate text content (not media/system messages)
                 $clean_text = strip_tags($original_text);
                 $clean_text = trim($clean_text);
 
-                // Skip translation for media content, short content, or system messages
+                // Skip translation for media/short/system messages
                 $is_media_message = (
                     strpos($original_text, '<audio') !== false ||
                     strpos($original_text, '<video') !== false ||
@@ -2871,11 +2872,14 @@ class SocialComponent extends Component implements CrawlConstants
                     strpos($original_text, 'transcript-container') !== false
                 );
 
-                if (strlen($clean_text) > 5 && !ctype_digit($clean_text) && !$is_media_message) {
-                    $translation_result = $this->translateText($original_text, $target_language);
+                if (strlen($clean_text) > 5 && !ctype_digit($clean_text) &&
+                    !$is_media_message) {
+                    $translation_result = $this->translateText(
+                        $original_text, $target_language);
 
                     if (!empty($translation_result['translation'])) {
-                        $message[$content_field] = $translation_result['translation'];
+                        $message[$content_field] =
+                            $translation_result['translation'];
                         $message['ORIGINAL_' . $content_field] = $original_text;
                         $translated_count++;
                     }
@@ -2939,11 +2943,15 @@ class SocialComponent extends Component implements CrawlConstants
             "messages" => [
                 [
                     "role" => "system",
-                    "content" => "You are a helpful translation assistant. Translate text accurately while preserving meaning and tone."
+                    "content" => "You are a helpful translation assistant. " .
+                        "Translate text accurately while preserving meaning."
                 ],
                 [
                     "role" => "user",
-                    "content" => "Translate the following text to " . $target_language_name . ". Only return the translation without explanations:\n\n" . $text
+                    "content" => "Translate the following text to " .
+                        $target_language_name .
+                        ". Only return the translation without explanations:\n\n" .
+                        $text
                 ]
             ],
             "temperature" => 0.1,
@@ -2971,7 +2979,6 @@ class SocialComponent extends Component implements CrawlConstants
             $content = trim($result_obj['choices'][0]['message']['content']);
             return ['translation' => $content, 'status' => 'success'];
         } else {
-            error_log("Translation API response: " . $response);
             return ['errors' => ["Invalid response from translation service"]];
         }
     }
diff --git a/src/data/public_default.db-shm b/src/data/public_default.db-shm
new file mode 100644
index 0000000000000000000000000000000000000000..fe9ac2845eca6fe6da8a63cd096d9cf9e24ece10
GIT binary patch
diff --git a/src/data/public_default.db-wal b/src/data/public_default.db-wal
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/scripts/messages.js b/src/scripts/messages.js
index aa6eb8d8f..19d4d6110 100644
--- a/src/scripts/messages.js
+++ b/src/scripts/messages.js
@@ -494,8 +494,9 @@ function loadMoreMessages()
         params.append(window.CSRF_TOKEN, csrf_input.value);
     }
 
-    // Use the same URL pattern as the existing start_url but replace the arg
-    var base_url = window.start_url.replace('&arg=status', '&arg=loadmessages');
+    // Use the same URL pattern as start_url but replace the arg
+    var base_url = window.start_url.replace('&arg=status',
+        '&arg=loadmessages');
 
     // Add our additional parameters to the URL
     base_url += '&contact_id=' + contact_id;
@@ -513,7 +514,8 @@ function loadMoreMessages()
     .then(response => {
         // Check if response is actually JSON
         const contentType = response.headers.get('content-type');
-        if (!contentType || !contentType.includes('application/json')) {
+        if (!contentType ||
+            !contentType.includes('application/json')) {
             return response.text().then(text => {
                 console.error('Expected JSON but received:', text);
                 throw new Error('Server returned HTML instead of JSON');
@@ -578,7 +580,6 @@ function showLoadingIndicator()
         indicator.innerHTML = 'Loading more messages...';
         indicator.style.cssText = 'text-align: center; padding: 10px; ' +
             'font-style: italic; color: #666;';
-
         conversation.insertBefore(indicator, conversation.firstChild);
     }
 }
diff --git a/src/views/elements/UsermessagesElement.php b/src/views/elements/UsermessagesElement.php
index be29e93fd..1eac9595e 100644
--- a/src/views/elements/UsermessagesElement.php
+++ b/src/views/elements/UsermessagesElement.php
@@ -244,29 +244,33 @@ class UsermessagesElement extends Element implements CrawlConstants
                 <?php
                 if (!empty($data['AVAILABLE_LOCALES'])) {
                 $current_language = $data['TRANSLATION_LANGUAGE'] ?? 'off';
-                $translation_url = $messages_url . "&amp;contact_id=" . $data['CONTACT_ID'] .
-                    "&amp;arg=settranslation&amp;language=";
+                $translation_url = $messages_url . "&amp;contact_id=" .
+                    $data['CONTACT_ID'] . "&amp;arg=settranslation&amp;language=";
                 $available_locales = $data['AVAILABLE_LOCALES'];
 
                 // Determine what to show - explicit setting, auto-translation, or off
                 $user_locale = $data['CURRENT_LOCALE_TAG'] ?? 'en_US';
-                $is_auto_translating = ($current_language === 'off' || empty($current_language)) &&
-                                      $user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE &&
-                                      isset($available_locales[$user_locale]);
+                $is_auto_translating = ($current_language === 'off' ||
+                    empty($current_language)) &&
+                    $user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE &&
+                    isset($available_locales[$user_locale]);
 
-                if ($current_language !== 'off' && isset($available_locales[$current_language])) {
+                if ($current_language !== 'off' &&
+                    isset($available_locales[$current_language])) {
                     // Explicit translation language selected
                     $current_locale_name = $available_locales[$current_language];
                     unset($available_locales[$current_language]);
-                    $reordered_locales = [$current_language => $current_locale_name] + $available_locales;
+                    $reordered_locales = [$current_language =>
+                        $current_locale_name] + $available_locales;
                     $show_top_text = $current_locale_name;
                 } elseif ($is_auto_translating) {
                     // Auto-translating to user's locale
                     $current_locale_name = $available_locales[$user_locale];
                     unset($available_locales[$user_locale]);
-                    $reordered_locales = [$user_locale => $current_locale_name] + $available_locales;
+                    $reordered_locales = [$user_locale =>
+                        $current_locale_name] + $available_locales;
                     $show_top_text = $current_locale_name;
-                    $current_language = $user_locale; // Update for proper selection
+                    $current_language = $user_locale;
                 } else {
                     // Translation off
                     $reordered_locales = $available_locales;
@@ -274,8 +278,8 @@ class UsermessagesElement extends Element implements CrawlConstants
                 }
 
                 $this->view->helper("options")->renderLinkDropDown(
-                    "translation-language", $reordered_locales, $current_language,
-                    $translation_url, false, "",
+                    "translation-language", $reordered_locales,
+                    $current_language, $translation_url, false, "",
                     $show_top_text);
                 }
                 ?>
@@ -288,14 +292,16 @@ class UsermessagesElement extends Element implements CrawlConstants
                 data-loaded-count="<?= count($data['MESSAGES']) ?>"
                 data-oldest-timestamp="<?= isset($data['MESSAGES']) &&
                 count($data['MESSAGES']) > 0 ?
-                $data['MESSAGES'][count($data['MESSAGES']) - 1]['PUBDATE'] : $now ?>"
+                $data['MESSAGES'][count($data['MESSAGES']) - 1]['PUBDATE'] :
+                $now ?>"
                 onscroll="handleConversationScroll()" ><?php
             $old_pubdate = $now;
             $old_user = $data['MESSAGES'][0]['USER_NAME'] ?? "";
             $num_messages = count($data['MESSAGES']);
             for ($i = 0; $i < $num_messages; $i++) {
                 $message_info = $data['MESSAGES'][$i];
-                $next_message_info = $data['MESSAGES'][$i + 1] ?? $message_info;
+                $next_message_info = $data['MESSAGES'][$i + 1] ??
+                    $message_info;
                 $next_user_name = $next_message_info['USER_NAME'];
                 if (empty($message_info[self::DESCRIPTION])) {
                     continue;
@@ -305,9 +311,11 @@ class UsermessagesElement extends Element implements CrawlConstants
                 $pub_date_diff = abs($next_pub_date - $pub_date);
                 $pub_date_age = $now - $pub_date;
                 $user_change = $message_info['USER_NAME'] != $next_user_name;
-                $pub_data_change = $user_change ||($pub_date_age < C\ONE_HOUR &&
+                $pub_data_change = $user_change ||
+                    ($pub_date_age < C\ONE_HOUR &&
                     $pub_date_diff > 5 * C\ONE_MINUTE) ||
-                    ($pub_date_age < C\ONE_DAY && $pub_date_diff > C\ONE_HOUR)||
+                    ($pub_date_age < C\ONE_DAY &&
+                    $pub_date_diff > C\ONE_HOUR) ||
                     ($pub_date_diff > C\ONE_DAY);
                 $pub_date_string =
                     $this->view->helper("feeds")->getPubdateString(
-- 
2.39.5 (Apple Git-154)


From a13d18b30f711b9336fdda52b482616908561078 Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Sun, 16 Nov 2025 23:14:14 -0800
Subject: [PATCH 12/14] coding guidelines for tl + auto-load page

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 src/controllers/ApiController.php             |  18 +-
 .../components/SocialComponent.php            | 159 +++++----------
 src/css/messages.css                          |  21 --
 src/models/GroupModel.php                     |  17 +-
 src/scripts/messages.js                       | 192 ++++--------------
 src/views/elements/UsermessagesElement.php    |  13 +-
 6 files changed, 106 insertions(+), 314 deletions(-)

diff --git a/src/controllers/ApiController.php b/src/controllers/ApiController.php
index 917e91eb2..047756f49 100644
--- a/src/controllers/ApiController.php
+++ b/src/controllers/ApiController.php
@@ -480,14 +480,15 @@ class ApiController extends Controller implements CrawlConstants
         }
         $translate_prompt = "Translate the text that is in following " .
             "sequence of <in></in> tags to the locale " . $target_lang .
-            ". Assume the context is text that might appear in a chat message. " .
-            "You should decode any html entities before doing the translation. " .
-            "If you see text like '%s' in the input treat it like a " .
-            "placeholder that should appear verbatim in the appropriate place " .
-            "in the output. Output the results between <out></out> tags. " .
-            "Do not add any explanations. Do not repeat the output " .
-            "translations. <in>" . $text . "</in>";
-        $request_body = [ "model" => $model, "messages" => [["role" => "system",
+            ". Assume the context is text that might appear in a " .
+            "chat message. You should decode any html entities before " .
+            "doing the translation. If you see text like '%s' in the " .
+            "input treat it like a placeholder that should appear " .
+            "verbatim in the appropriate place in the output. Output " .
+            "the results between <out></out> tags. Do not add any " .
+            "explanations. Do not repeat the output translations. " .
+            "<in>" . $text . "</in>";
+        $request_body = ["model" => $model, "messages" => [["role" => "system",
             "content" => "You are a helpful assistant who translates text" .
             " from one language to another. Use this predefined mapping " .
             "for accurate language detection:\n" . $mapping_string],
@@ -500,7 +501,6 @@ class ApiController extends Controller implements CrawlConstants
             $result_obj = json_decode($result, true);
             if (isset($result_obj['choices'][0]['message']['content'])) {
                 $content = $result_obj['choices'][0]['message']['content'];
-                // Extract text between <out> tags
                 preg_match('/<out>([\s\S]*?)<\/out>/', $content, $matches);
                 if (isset($matches[1])) {
                     $data['translation'] = trim($matches[1]);
diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index dcfe8764b..fa20eab72 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -2369,13 +2369,13 @@ class SocialComponent extends Component implements CrawlConstants
         $user_messages_id = $group_model->getPersonalGroupId($user_id);
         $data['USER_MESSAGES_ID'] = $user_messages_id;
         if (!empty($contact_id)) {
-            $data['TRANSLATION_LANGUAGE'] = $group_model->getTranslationLanguage($user_id, $contact_id);
+            $data['TRANSLATION_LANGUAGE'] =
+                $group_model->getTranslationLanguage($user_id, $contact_id);
         }
-
-        // Get available locales with native names and user's current locale (only if LLM is configured)
         if (!empty(C\LLM_API_URL) && !empty(C\LLM_MODEL)) {
             $locale_model = $parent->model("locale");
-            $data['AVAILABLE_LOCALES'] = $this->getTranslationLocales($locale_model);
+            $data['AVAILABLE_LOCALES'] =
+                $this->getTranslationLocales($locale_model);
             $data['CURRENT_LOCALE_TAG'] = L\getLocaleTag();
         }
         $message_actions = ['addcontact' => 'addContact',
@@ -2461,23 +2461,19 @@ class SocialComponent extends Component implements CrawlConstants
                 list(, $data['MESSAGES']) = $this->initializeFeedItems($data,
                     [], $user_id, $search_array, -2, "krsort", $limit,
                     15);
-
-                // Apply translation if enabled or user locale differs from default
                 if (!empty(C\LLM_API_URL) && !empty(C\LLM_MODEL)) {
                     $translation_target = null;
                     $translation_lang = $data['TRANSLATION_LANGUAGE'] ?? '';
                     if (!empty($translation_lang) && $translation_lang !== 'off') {
-                        // Explicit translation language selected
                         $translation_target = $translation_lang;
-                    } elseif (empty($translation_lang) || $translation_lang === 'off') {
-                        // Check if user's locale requires auto-translation
+                    } elseif (empty($translation_lang) ||
+                        $translation_lang === 'off') {
                         $user_locale = $data['CURRENT_LOCALE_TAG'] ?? 'en_US';
                         if ($user_locale !== 'en_US' &&
                             $user_locale !== C\DEFAULT_LOCALE) {
                             $translation_target = $user_locale;
                         }
                     }
-
                     if (!empty($translation_target)) {
                         $data['MESSAGES'] = $this->translateMessageBatch(
                             $data['MESSAGES'], $translation_target);
@@ -2804,20 +2800,16 @@ class SocialComponent extends Component implements CrawlConstants
         $group_model = $parent->model("group");
         $contact_id = $data['CONTACT_ID'] ?? null;
         $language = $parent->clean($_REQUEST['language'] ?? 'off', 'string');
-
         if (empty($contact_id)) {
             return $parent->redirectWithMessage(
                 tl('social_component_invalid_contact'),
                 ['contact_id']
             );
         }
-
         $group_model->setTranslationLanguage($user_id, $contact_id, $language);
-
         $message_key = ($language == 'off')
             ? 'social_component_translation_disabled'
             : 'social_component_translation_enabled';
-
         return $parent->redirectWithMessage(tl($message_key), ['contact_id']);
     }
     /**
@@ -2833,14 +2825,10 @@ class SocialComponent extends Component implements CrawlConstants
             $target_language == 'off') {
             return $messages;
         }
-
         $translated_count = 0;
         foreach ($messages as $index => &$message) {
-
-            // Check for content in 't' field or 'DESCRIPTION' field
             $content_field = null;
             $original_text = null;
-
             if (!empty($message['t'])) {
                 $content_field = 't';
                 $original_text = $message['t'];
@@ -2848,21 +2836,19 @@ class SocialComponent extends Component implements CrawlConstants
                 $content_field = 'DESCRIPTION';
                 $original_text = $message['DESCRIPTION'];
             }
-
             if ($content_field && $original_text) {
-
-                // Only translate text content (not media/system messages)
                 $clean_text = strip_tags($original_text);
                 $clean_text = trim($clean_text);
-
-                // Skip translation for media/short/system messages
                 $is_media_message = (
                     strpos($original_text, '<audio') !== false ||
                     strpos($original_text, '<video') !== false ||
                     strpos($original_text, '<img') !== false ||
-                    strpos($original_text, 'audio-message-container') !== false ||
-                    strpos($original_text, 'video-message-container') !== false ||
-                    strpos($original_text, 'image-message-container') !== false ||
+                    strpos($original_text, 'audio-message-container') !==
+                        false ||
+                    strpos($original_text, 'video-message-container') !==
+                        false ||
+                    strpos($original_text, 'image-message-container') !==
+                        false ||
                     strpos($original_text, 'audio-message') !== false ||
                     strpos($original_text, 'video-message') !== false ||
                     strpos($original_text, 'image-message') !== false ||
@@ -2876,7 +2862,6 @@ class SocialComponent extends Component implements CrawlConstants
                     !$is_media_message) {
                     $translation_result = $this->translateText(
                         $original_text, $target_language);
-
                     if (!empty($translation_result['translation'])) {
                         $message[$content_field] =
                             $translation_result['translation'];
@@ -2886,8 +2871,7 @@ class SocialComponent extends Component implements CrawlConstants
                 }
             }
         }
-
-        unset($message); // Break reference
+        unset($message);
         return $messages;
     }
     /**
@@ -2900,13 +2884,13 @@ class SocialComponent extends Component implements CrawlConstants
     {
         $locales = $locale_model->getLocaleList(true, true);
         $translation_locales = ["off" => tl('toggle_helper_off')];
-
         foreach ($locales as $locale) {
-            if (!empty($locale['LOCALE_TAG']) && !empty($locale['LOCALE_NAME'])) {
-                $translation_locales[$locale['LOCALE_TAG']] = $locale['LOCALE_NAME'];
+            if (!empty($locale['LOCALE_TAG']) &&
+                !empty($locale['LOCALE_NAME'])) {
+                $translation_locales[$locale['LOCALE_TAG']] =
+                    $locale['LOCALE_NAME'];
             }
         }
-
         return $translation_locales;
     }
     /**
@@ -2921,11 +2905,9 @@ class SocialComponent extends Component implements CrawlConstants
         if (empty($text) || empty($target_lang)) {
             return ['errors' => ["Missing required parameters"]];
         }
-
         $api_url = C\LLM_API_URL ?? "http://localhost:1234/v1/chat/completions";
         $model = C\LLM_MODEL ?? "aya-23-8b";
-
-        $language_map = [ "ar" => "Arabic", "bn" => "Bengali", "de" => "German",
+        $language_map = ["ar" => "Arabic", "bn" => "Bengali", "de" => "German",
             "el_GR" => "Greek", "en_US" => "English (US)", "es" => "Spanish",
             "fa" => "Persian", "fr_FR" => "French (France)", "he" => "Hebrew",
             "hi" => "Hindi", "id" => "Indonesian", "it" => "Italian",
@@ -2933,49 +2915,27 @@ class SocialComponent extends Component implements CrawlConstants
             "nl" => "Dutch", "pl" => "Polish", "pt" => "Portuguese",
             "ru" => "Russian", "te" => "Telugu", "th" => "Thai",
             "tl" => "Tagalog", "tr" => "Turkish", "vi_VN" => "Vietnamese",
-            "zh_CN" => "Chinese (Simplified)"
-        ];
-
+            "zh_CN" => "Chinese (Simplified)"];
         $target_language_name = $language_map[$target_lang] ?? $target_lang;
-
-        $request_body = [
-            "model" => $model,
-            "messages" => [
-                [
-                    "role" => "system",
-                    "content" => "You are a helpful translation assistant. " .
-                        "Translate text accurately while preserving meaning."
-                ],
-                [
-                    "role" => "user",
-                    "content" => "Translate the following text to " .
-                        $target_language_name .
-                        ". Only return the translation without explanations:\n\n" .
-                        $text
-                ]
-            ],
-            "temperature" => 0.1,
-            "max_tokens" => 512,
-            "stream" => false
-        ];
-
+        $request_body = ["model" => $model, "messages" => [["role" => "system",
+            "content" => "You are a helpful translation assistant. " .
+            "Translate text accurately while preserving meaning."],
+            ["role" => "user", "content" => "Translate the following text to " .
+            $target_language_name .
+            ". Only return the translation without explanations:\n\n" . $text]],
+            "temperature" => 0.1, "max_tokens" => 512, "stream" => false];
         $post_data = json_encode($request_body);
         $headers = ['Content-Type: application/json'];
-
         $response = L\FetchUrl::getPage($api_url, $post_data, true, null,
             C\SINGLE_PAGE_TIMEOUT, $headers);
-
         if ($response === false) {
             return ['errors' => ["Failed to connect to translation service"]];
         }
-
         $result_obj = json_decode($response, true);
         if (isset($result_obj['response'])) {
-            // LMStudio format
             $content = trim($result_obj['response']);
             return ['translation' => $content, 'status' => 'success'];
         } else if (isset($result_obj['choices'][0]['message']['content'])) {
-            // OpenAI format fallback
             $content = trim($result_obj['choices'][0]['message']['content']);
             return ['translation' => $content, 'status' => 'success'];
         } else {
@@ -3053,28 +3013,23 @@ class SocialComponent extends Component implements CrawlConstants
         $parent = $this->parent;
         $group_model = $parent->model("group");
         $user_model = $parent->model("user");
-
-
         $contact_id = $parent->clean($_REQUEST['contact_id'] ?? 0, 'int');
-        $before_timestamp = $parent->clean($_REQUEST['before_timestamp'] ?? 0, 'int');
+        $before_timestamp = $parent->clean(
+            $_REQUEST['before_timestamp'] ?? 0, 'int');
         $limit = $parent->clean($_REQUEST['limit'] ?? 20, 'int');
-
         if (empty($contact_id) || empty($before_timestamp)) {
             header('Content-Type: application/json');
             echo json_encode(['error' => 'Missing required parameters',
-                'contact_id' => $contact_id, 'before_timestamp' => $before_timestamp]);
+                'contact_id' => $contact_id,
+                'before_timestamp' => $before_timestamp]);
             exit();
         }
-
         if ($limit > 100) {
             $limit = 100;
         }
-
         $user_messages_id = $group_model->getPersonalGroupId($user_id);
-
         $messages_thread_title = $group_model->getMessagesThreadTitle(
             [$user_id, $contact_id]);
-
         $message_thread_id = $group_model->getGroupThreadId($user_messages_id,
             $user_id, $messages_thread_title);
 
@@ -3083,50 +3038,41 @@ class SocialComponent extends Component implements CrawlConstants
             echo json_encode(['error' => 'Invalid message thread']);
             exit();
         }
-
         $search_array = [
             ["parent_id", "=", $message_thread_id, ""],
             ["group_id", "=", $user_messages_id, ""],
             ["pubdate", "<", $before_timestamp, "DESC"]
         ];
-
-
-        // Use initializeFeedItems for proper content processing
-        $temp_data = $data; // Don't modify the original data
+        $temp_data = $data;
         $temp_limit = 0;
-
         try {
-            list(, $messages) = $this->initializeFeedItems($temp_data, [], $user_id,
-                $search_array, -2, "krsort", $temp_limit, $limit);
+            list(, $messages) = $this->initializeFeedItems($temp_data, [],
+                $user_id, $search_array, -2, "krsort", $temp_limit, $limit);
         } catch (Exception $e) {
-            // Fallback to direct query
-            $messages = $group_model->getGroupItems(0, $limit, $search_array, $user_id, -2);
+            $messages = $group_model->getGroupItems(0, $limit,
+                $search_array, $user_id, -2);
         }
-
-        // Apply translation if enabled or if user's locale differs from default (only if LLM configured)
         if (!empty(C\LLM_API_URL) && !empty(C\LLM_MODEL)) {
-            $translation_language = $group_model->getTranslationLanguage($user_id, $contact_id);
+            $translation_language =
+                $group_model->getTranslationLanguage($user_id, $contact_id);
             $user_locale = L\getLocaleTag();
             $translation_target = null;
-
-            if (!empty($translation_language) && $translation_language !== 'off') {
-                // Explicit translation language selected
+            if (!empty($translation_language) &&
+                $translation_language !== 'off') {
                 $translation_target = $translation_language;
-            } elseif (empty($translation_language) || $translation_language === 'off') {
-                // Check if user's locale requires auto-translation
-                if ($user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE) {
+            } elseif (empty($translation_language) ||
+                $translation_language === 'off') {
+                if ($user_locale !== 'en_US' &&
+                    $user_locale !== C\DEFAULT_LOCALE) {
                     $translation_target = $user_locale;
                 }
             }
-
             if (!empty($translation_target)) {
-                $messages = $this->translateMessageBatch($messages, $translation_target);
+                $messages = $this->translateMessageBatch($messages,
+                    $translation_target);
             }
         }
-
         $has_more = count($messages) == $limit;
-
-        // Render messages using the existing UsermessagesElement to get proper HTML
         $element_data = [
             'MESSAGES' => $messages,
             'USERNAME' => $user_model->getUsername($user_id),
@@ -3136,34 +3082,28 @@ class SocialComponent extends Component implements CrawlConstants
             'MOBILE' => false,
             'LOCALE_DIR' => 'ltr'
         ];
-
-        // Capture the rendered HTML from UsermessagesElement
         ob_start();
         $now = time();
         $old_pubdate = $now;
         $old_user = $messages[0]['USER_NAME'] ?? "";
         $num_messages = count($messages);
-
-        // Use the same rendering logic as UsermessagesElement
         $rendered_count = 0;
         for ($i = 0; $i < $num_messages; $i++) {
             $message_info = $messages[$i];
             $next_message_info = $messages[$i + 1] ?? $message_info;
             $next_user_name = $next_message_info['USER_NAME'];
-            // Check for processed content in various possible fields
             $description_content = '';
             if (!empty($message_info['DESCRIPTION'])) {
                 $description_content = $message_info['DESCRIPTION'];
             } else {
-                // 't' contains the processed content field from initializeFeedItems
-                foreach (['t', 'DESCRIPTION', 'OLD_DESCRIPTION', 'SOURCE_NAME', 'TITLE'] as $field) {
+                foreach (['t', 'DESCRIPTION', 'OLD_DESCRIPTION',
+                    'SOURCE_NAME', 'TITLE'] as $field) {
                     if (!empty($message_info[$field])) {
                         $description_content = $message_info[$field];
                         break;
                     }
                 }
             }
-
             if (empty($description_content)) {
                 continue;
             }
@@ -3214,15 +3154,11 @@ class SocialComponent extends Component implements CrawlConstants
             </div>
             <?php
         }
-
         $rendered_html = ob_get_clean();
-
-        // Get the oldest timestamp from the loaded messages
         $oldest_timestamp = null;
         if (!empty($messages)) {
             $oldest_timestamp = $messages[count($messages) - 1]['PUBDATE'];
         }
-
         header('Content-Type: application/json');
         $response = json_encode([
             'html' => $rendered_html,
@@ -3230,7 +3166,6 @@ class SocialComponent extends Component implements CrawlConstants
             'message_count' => count($messages),
             'oldest_timestamp' => $oldest_timestamp
         ]);
-
         echo $response;
         exit();
     }
diff --git a/src/css/messages.css b/src/css/messages.css
index 1c26ed0cf..a8be2ee61 100644
--- a/src/css/messages.css
+++ b/src/css/messages.css
@@ -658,23 +658,6 @@
     font-style: italic;
 }
 
-/* Infinite scroll loading indicator */
-.loading-indicator {
-    text-align: center;
-    padding: 10px;
-    font-style: italic;
-    color: #666;
-    background-color: #f5f5f5;
-    border-bottom: 1px solid #ddd;
-    animation: pulse 1.5s ease-in-out infinite alternate;
-}
-
-@keyframes pulse {
-    from { opacity: 0.6; }
-    to { opacity: 1; }
-}
-
-/* Translation dropdown styling */
 #translation-language-list {
     max-height: 150px;
     overflow-y: auto;
@@ -682,13 +665,9 @@
     border-bottom: 1px solid #999;
     background: white;
 }
-
-/* Ensure scrollable content doesn't interfere with borders */
 #translation-language-list li:first-child {
     border-top: none;
 }
-
 #translation-language-list li:last-child {
     border-bottom: none;
 }
-
diff --git a/src/models/GroupModel.php b/src/models/GroupModel.php
index e04d4fb36..edfd38363 100644
--- a/src/models/GroupModel.php
+++ b/src/models/GroupModel.php
@@ -5134,8 +5134,10 @@ EOD;
     {
         $db = $this->db;
         $user_group_id = $this->getPersonalGroupId($user_id);
-        $settings_thread_title = "translate-settings-" . $user_id . "-" . $contact_id;
-        $thread_id = $this->getGroupThreadId($user_group_id, $user_id, $settings_thread_title);
+        $settings_thread_title = "translate-settings-" . $user_id .
+            "-" . $contact_id;
+        $thread_id = $this->getGroupThreadId($user_group_id, $user_id,
+            $settings_thread_title);
         if (!$thread_id) {
             return false;
         }
@@ -5153,18 +5155,19 @@ EOD;
      *
      * @param int $user_id id of the user who owns the setting
      * @param int $contact_id id of the contact whose messages will be translated
-     * @param string $language language code (e.g., 'es', 'fr') or 'off' to disable
+     * @param string $language language code (e.g., 'es', 'fr') or 'off'
+     *  to disable
      */
     public function setTranslationLanguage($user_id, $contact_id, $language)
     {
         $db = $this->db;
         $user_group_id = $this->getPersonalGroupId($user_id);
-        $settings_thread_title = "translate-settings-" . $user_id . "-" . $contact_id;
-        $thread_id = $this->getGroupThreadId($user_group_id, $user_id, $settings_thread_title);
+        $settings_thread_title = "translate-settings-" . $user_id .
+            "-" . $contact_id;
+        $thread_id = $this->getGroupThreadId($user_group_id, $user_id,
+            $settings_thread_title);
         $settings = json_encode(["TRANSLATE_LANGUAGE" => $language]);
-
         if (!$thread_id) {
-            // Create a new settings entry
             $this->addGroupItem(
                 0,
                 $user_group_id,
diff --git a/src/scripts/messages.js b/src/scripts/messages.js
index 19d4d6110..c43dfd642 100644
--- a/src/scripts/messages.js
+++ b/src/scripts/messages.js
@@ -76,7 +76,6 @@ function doUpdate()
         event_source.send = sendMessage;
         event_source.onmessage = handleMessage;
     } catch (e) {
-        console.error("Could not create eventsource.", e);
     }
     setCallState(null, '');
     setTimeout("clearUpdate()", 20 * minute + sec);
@@ -88,9 +87,7 @@ function setCallState(state)
 {
     old_call_state = call_state;
     call_state = state;
-    console.log("state:" + call_state + "; old_state:" + old_call_state);
     if (!event_source) {
-        console.log("setCallState: no event source bailing!");
     }
     if (call_state && call_state != 'video-end' &&
         call_state != 'video-end-received') {
@@ -122,7 +119,6 @@ function setCallState(state)
           return;
         }
         navigator.mediaDevices.getUserMedia(constraints).then( (stream) => {
-            console.log('Got MediaStream: ', stream);
             local_av.srcObject = stream;
             local_stream = stream;
             if (old_call_state == 'calling') {
@@ -133,7 +129,6 @@ function setCallState(state)
                 };
             }
         }).catch (error => {
-            console.error('Error accessing media devices.', error);
         });
     } else {
         cancelCallSound();
@@ -198,7 +193,6 @@ async function post(url, json, callback)
  */
 function publish(type, data)
 {
-    console.log("publish sending: " , event);
     event_source.send({
         type: type,
         data: data
@@ -225,8 +219,6 @@ function handleMessage(message)
         let handler = allowed_messages[type];
         handler(type, data);
     } else {
-        console.error("messages.js can't handle message of type:" +
-            type);
     }
 }
 /**
@@ -235,15 +227,11 @@ function handleMessage(message)
 function handleClientAnswer(type, data)
 {
     if (peer_connection == null) {
-        console.error('Before processing the client-answer, ' +
-            'I need a client-offer');
         return;
     }
-    console.log("handleClientAnswer method");
     peer_connection.setRemoteDescription(
         new RTCSessionDescription(data),function() {},
         function(e) {
-            console.log("Problem while doing client-answer: ", e);
             return;
     });
 }
@@ -253,14 +241,10 @@ function handleClientAnswer(type, data)
 function handleClientCandidate(type, data)
 {
     if (peer_connection == null) {
-        console.error('Before processing the client-answer, '+
-            'I need a client-offer');
         return;
     }
-    console.log("handleClientCandidate method");
     peer_connection.addIceCandidate(new RTCIceCandidate(data), function(){},
         function(e) {
-            console.log("Problem adding ice candidate: " + e);
             return;
         }
     );
@@ -270,7 +254,6 @@ function handleClientCandidate(type, data)
  */
 function handleCallEnd(type, data)
 {
-    console.log("handleCallEnd method");
     call_state = type;
     setCallState('video-end-received');
 }
@@ -279,7 +262,6 @@ function handleCallEnd(type, data)
  */
 function handleClientCall(type, data)
 {
-    console.log("handleClientCall method");
     startCallSound();
     old_call_state = type;
     call_state = 'calling';
@@ -302,11 +284,9 @@ function makeOffer()
                     peer_connection.localDescription);
             }
         ).catch(function (event) {
-            console.log("Problem with publishing client offer: " + event);
             return;
         });
     }).catch(function (event) {
-        console.log("Problem while doing client-call: " + event);
         return;
     });
 }
@@ -322,16 +302,13 @@ function handleClientOffer(type, data)
             if (!answer) {
                 peer_connection.createAnswer(function (desc) {
                     peer_connection.setLocalDescription(desc, function () {
-                        console.log("publishing answer method");
                         publish('client-answer',
                             peer_connection.localDescription);
                     }, function(e) {
-                        console.log("Problem getting client answer: ",e);
                         return;
                     });
                 },
                 function(e) {
-                    console.log("Problem while doing client-offer: ", e);
                     return;
                 });
                 setDisplay('av-call-div', false);
@@ -339,7 +316,6 @@ function handleClientOffer(type, data)
             }
         },
         function(e) {
-            console.log("Problem while doing client-offer2: ", e);
             return;
         }
     );
@@ -352,7 +328,6 @@ function handleStatus(type, data)
     if (!data) {
         return;
     }
-    console.log("handleStatus method");
     let conversation_obj = elt('conversation');
     let conversation_time = (conversation_obj) ?
     parseInt(conversation_obj.getAttribute('data-time')) : 0;
@@ -366,8 +341,8 @@ function handleStatus(type, data)
     if (update_time) {
         conversation_obj.setAttribute('data-time', update_time);
     }
-    conversation_obj.innerHTML = conversation_obj.innerHTML +
-        new_conversation_obj.innerHTML;
+    conversation_obj.innerHTML = new_conversation_obj.innerHTML +
+        conversation_obj.innerHTML;
     setTimeout("resetBackground()", 0.5 * sec);
 }
 /**
@@ -375,14 +350,10 @@ function handleStatus(type, data)
  */
 function sendMessage(message)
 {
-    console.log("sendMessage method");
-    console.log("Sending via fetch api: ", message);
     post(start_url + "&type=call-event",
         message,
         (data) => {
             // Success function.
-            console.log("Successfully sent message:", message);
-            console.log("Data back from server:", data);
         }
     );
 }
@@ -392,17 +363,11 @@ function sendMessage(message)
 function icecandidate(local_stream)
 {
     if (have_set_local_ice_candidate) {
-        console.log("Already set ice candidate for local stream. Returning.");
         return;
     }
-    console.log("icecandidate method");
-    console.log(configuration);
     peer_connection = new RTCPeerConnection(configuration);
     peer_connection.onicecandidate = function (event) {
-        console.log("Got ice candidate");
-        console.log(event);
         if (event.candidate && event.candidate != sent_candidate) {
-            console.log("sending");
             sent_candidate = event.candidate;
             publish('client-candidate', event.candidate);
         }
@@ -415,7 +380,6 @@ function icecandidate(local_stream)
         }
     }
     peer_connection.ontrack = function (event) {
-        console.log("Trying to add stream to remote_av");
         remote_stream = event.streams[0];
         remote_av.srcObject = remote_stream;
         setDisplay('av-call-div', false);
@@ -423,175 +387,95 @@ function icecandidate(local_stream)
     };
     have_set_local_ice_candidate = true;
 }
-
-// Infinite scroll variables
-var loading_messages = false;
-var has_more_messages = true;
-var last_scroll_time = 0;
-
-/**
+var loadingMessages = false;
+var hasMoreMessages = true;
+var lastScrollTime = 0;
+/*
  * Handles scroll events on the conversation div to implement infinite scroll
  */
 function handleConversationScroll()
 {
     var conversation = elt('conversation');
-    if (!conversation || loading_messages || !has_more_messages) {
+    if (!conversation || loadingMessages || !hasMoreMessages) {
         return;
     }
-
-    // Throttle scroll events - only check every 500ms
     var now = Date.now();
-    if (now - last_scroll_time < 500) {
+    if (now - lastScrollTime < 500) {
         return;
     }
-    last_scroll_time = now;
-
-    // Check if scrolled near top (within 100px)
+    lastScrollTime = now;
     if (conversation.scrollTop < 100) {
         loadMoreMessages();
     }
 }
-
-/**
+/*
  * Loads more messages from the server for infinite scroll
  */
 function loadMoreMessages()
 {
-    if (loading_messages) {
+    if (loadingMessages) {
         return;
     }
-
-    loading_messages = true;
+    loadingMessages = true;
     var conversation = elt('conversation');
-    var contact_id = conversation.getAttribute('data-contact-id');
-    var loaded_count = parseInt(conversation.getAttribute('data-loaded-count'));
-    var oldest_timestamp = parseInt(conversation.getAttribute('data-oldest-timestamp'));
-
-    if (!contact_id || !oldest_timestamp) {
-        loading_messages = false;
+    var contactId = conversation.getAttribute('data-contact-id');
+    var loadedCount = parseInt(conversation.getAttribute('data-loaded-count'));
+    var oldestTimestamp = parseInt(conversation.getAttribute(
+        'data-oldest-timestamp'));
+    if (!contactId || !oldestTimestamp) {
+        loadingMessages = false;
         return;
     }
-
-    // Loading indicator removed per user request
-
-    // Force social controller since messages are handled by SocialComponent
     var controller = 'social';
-
-    // Build request parameters
     var params = new URLSearchParams({
-        'c': controller,
-        'a': 'userMessages',
-        'arg': 'loadmessages',
-        'contact_id': contact_id,
-        'before_timestamp': oldest_timestamp,
-        'limit': 20
-    });
-
-    // Add CSRF token
-    var csrf_input = document.querySelector('input[name="' +
+        'c': controller, 'a': 'userMessages', 'arg': 'loadmessages',
+        'contact_id': contactId, 'before_timestamp': oldestTimestamp,
+        'limit': 20});
+    var csrfInput = document.querySelector('input[name="' +
         window.CSRF_TOKEN + '"]');
-    if (csrf_input) {
-        params.append(window.CSRF_TOKEN, csrf_input.value);
+    if (csrfInput) {
+        params.append(window.CSRF_TOKEN, csrfInput.value);
     }
-
-    // Use the same URL pattern as start_url but replace the arg
-    var base_url = window.start_url.replace('&arg=status',
+    var baseUrl = window.start_url.replace('&arg=status',
         '&arg=loadmessages');
-
-    // Add our additional parameters to the URL
-    base_url += '&contact_id=' + contact_id;
-    base_url += '&before_timestamp=' + oldest_timestamp;
-    base_url += '&limit=10';
-
-    console.log('Making request to:', base_url);
-
-    fetch(base_url, {
-        method: 'GET',  // Use GET like EventSource
+    baseUrl += '&contact_id=' + contactId;
+    baseUrl += '&before_timestamp=' + oldestTimestamp;
+    baseUrl += '&limit=10';
+    fetch(baseUrl, {
+        method: 'GET',
         headers: {
             'Accept': 'application/json',
         }
     })
     .then(response => {
-        // Check if response is actually JSON
         const contentType = response.headers.get('content-type');
         if (!contentType ||
             !contentType.includes('application/json')) {
             return response.text().then(text => {
-                console.error('Expected JSON but received:', text);
                 throw new Error('Server returned HTML instead of JSON');
             });
         }
         return response.json();
     })
     .then(data => {
-        // hideLoadingIndicator(); // Removed per user request
-
         if (data.error) {
-            console.error('Error loading messages:', data.error);
-            loading_messages = false;
+            loadingMessages = false;
             return;
         }
-
         if (data.html && data.message_count > 0) {
-            // Append messages to the bottom (chronological order)
             conversation.insertAdjacentHTML('beforeend', data.html);
-            has_more_messages = data.has_more;
-
-            // Update data attributes
-            var new_loaded_count = loaded_count + data.message_count;
-            conversation.setAttribute('data-loaded-count', new_loaded_count);
-
-            // Update oldest timestamp for next fetch
+            hasMoreMessages = data.has_more;
+            var newLoadedCount = loadedCount + data.message_count;
+            conversation.setAttribute('data-loaded-count', newLoadedCount);
             if (data.oldest_timestamp) {
                 conversation.setAttribute('data-oldest-timestamp', data.oldest_timestamp);
             }
-
-            // Don't auto-scroll - let user stay where they are
         } else {
-            has_more_messages = false;
+            hasMoreMessages = false;
         }
-
-        loading_messages = false;
+        loadingMessages = false;
     })
     .catch(error => {
-        console.error('Error loading messages:', error);
-        // hideLoadingIndicator(); // Removed per user request
-        loading_messages = false;
+        loadingMessages = false;
     });
-}
-
-/**
- * Prepends new messages to the conversation while maintaining scroll position
- */
-// Message rendering functions removed - now using server-side HTML rendering
-
-/**
- * Shows loading indicator at top of conversation
- */
-function showLoadingIndicator()
-{
-    var conversation = elt('conversation');
-    var existing_indicator = elt('loading-indicator');
-
-    if (!existing_indicator) {
-        var indicator = document.createElement('div');
-        indicator.id = 'loading-indicator';
-        indicator.className = 'loading-indicator';
-        indicator.innerHTML = 'Loading more messages...';
-        indicator.style.cssText = 'text-align: center; padding: 10px; ' +
-            'font-style: italic; color: #666;';
-        conversation.insertBefore(indicator, conversation.firstChild);
-    }
-}
-
-/**
- * Hides loading indicator
- */
-function hideLoadingIndicator()
-{
-    var indicator = elt('loading-indicator');
-    if (indicator) {
-        indicator.remove();
-    }
-}
-
+}
\ No newline at end of file
diff --git a/src/views/elements/UsermessagesElement.php b/src/views/elements/UsermessagesElement.php
index 1eac9595e..7f2975f95 100644
--- a/src/views/elements/UsermessagesElement.php
+++ b/src/views/elements/UsermessagesElement.php
@@ -239,32 +239,26 @@ class UsermessagesElement extends Element implements CrawlConstants
                 $data["CONTACT_ICON_URL"]; ?>"
                 alt="<?=tl('usermessages_element_user_icon'); ?>" >
                 <span><?= $data['CONTACT_NAME'] ?></span>
-
-                <!-- Translation Dropdown (only if LLM is configured) -->
                 <?php
                 if (!empty($data['AVAILABLE_LOCALES'])) {
                 $current_language = $data['TRANSLATION_LANGUAGE'] ?? 'off';
                 $translation_url = $messages_url . "&amp;contact_id=" .
-                    $data['CONTACT_ID'] . "&amp;arg=settranslation&amp;language=";
+                    $data['CONTACT_ID'] .
+                    "&amp;arg=settranslation&amp;language=";
                 $available_locales = $data['AVAILABLE_LOCALES'];
-
-                // Determine what to show - explicit setting, auto-translation, or off
                 $user_locale = $data['CURRENT_LOCALE_TAG'] ?? 'en_US';
                 $is_auto_translating = ($current_language === 'off' ||
                     empty($current_language)) &&
                     $user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE &&
                     isset($available_locales[$user_locale]);
-
                 if ($current_language !== 'off' &&
                     isset($available_locales[$current_language])) {
-                    // Explicit translation language selected
                     $current_locale_name = $available_locales[$current_language];
                     unset($available_locales[$current_language]);
                     $reordered_locales = [$current_language =>
                         $current_locale_name] + $available_locales;
                     $show_top_text = $current_locale_name;
                 } elseif ($is_auto_translating) {
-                    // Auto-translating to user's locale
                     $current_locale_name = $available_locales[$user_locale];
                     unset($available_locales[$user_locale]);
                     $reordered_locales = [$user_locale =>
@@ -272,18 +266,15 @@ class UsermessagesElement extends Element implements CrawlConstants
                     $show_top_text = $current_locale_name;
                     $current_language = $user_locale;
                 } else {
-                    // Translation off
                     $reordered_locales = $available_locales;
                     $show_top_text = tl('toggle_helper_off');
                 }
-
                 $this->view->helper("options")->renderLinkDropDown(
                     "translation-language", $reordered_locales,
                     $current_language, $translation_url, false, "",
                     $show_top_text);
                 }
                 ?>
-
             </h2>
             </div>
             <div id="av-call"></div>
-- 
2.39.5 (Apple Git-154)


From 4a3d84dabd1095c52160c628473b22b43261c4d4 Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Sun, 16 Nov 2025 23:59:14 -0800
Subject: [PATCH 13/14] final patch

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 .../components/SocialComponent.php            |  3 +-
 src/models/GroupModel.php                     |  6 ++-
 src/scripts/messages.js                       | 43 +++++++++++++++++--
 src/views/elements/UsermessagesElement.php    |  3 +-
 4 files changed, 48 insertions(+), 7 deletions(-)

diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index fa20eab72..f261d8e80 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -2464,7 +2464,8 @@ class SocialComponent extends Component implements CrawlConstants
                 if (!empty(C\LLM_API_URL) && !empty(C\LLM_MODEL)) {
                     $translation_target = null;
                     $translation_lang = $data['TRANSLATION_LANGUAGE'] ?? '';
-                    if (!empty($translation_lang) && $translation_lang !== 'off') {
+                    if (!empty($translation_lang) &&
+                        $translation_lang !== 'off') {
                         $translation_target = $translation_lang;
                     } elseif (empty($translation_lang) ||
                         $translation_lang === 'off') {
diff --git a/src/models/GroupModel.php b/src/models/GroupModel.php
index edfd38363..44f196e49 100644
--- a/src/models/GroupModel.php
+++ b/src/models/GroupModel.php
@@ -5127,7 +5127,8 @@ EOD;
      * Retrieves the translation language setting for a specific contact
      *
      * @param int $user_id id of the user who owns the setting
-     * @param int $contact_id id of the contact whose messages may be translated
+     * @param int $contact_id id of the contact whose messages may be
+     *  translated
      * @return string|false translation language code or false if not set
      */
     public function getTranslationLanguage($user_id, $contact_id)
@@ -5154,7 +5155,8 @@ EOD;
      * Stores the translation language setting for a specific contact
      *
      * @param int $user_id id of the user who owns the setting
-     * @param int $contact_id id of the contact whose messages will be translated
+     * @param int $contact_id id of the contact whose messages will be
+     *  translated
      * @param string $language language code (e.g., 'es', 'fr') or 'off'
      *  to disable
      */
diff --git a/src/scripts/messages.js b/src/scripts/messages.js
index c43dfd642..bc1f92fea 100644
--- a/src/scripts/messages.js
+++ b/src/scripts/messages.js
@@ -76,6 +76,7 @@ function doUpdate()
         event_source.send = sendMessage;
         event_source.onmessage = handleMessage;
     } catch (e) {
+        console.error("Could not create eventsource.", e);
     }
     setCallState(null, '');
     setTimeout("clearUpdate()", 20 * minute + sec);
@@ -87,7 +88,9 @@ function setCallState(state)
 {
     old_call_state = call_state;
     call_state = state;
+    console.log("state:" + call_state + "; old_state:" + old_call_state);
     if (!event_source) {
+        console.log("setCallState: no event source bailing!");
     }
     if (call_state && call_state != 'video-end' &&
         call_state != 'video-end-received') {
@@ -119,6 +122,7 @@ function setCallState(state)
           return;
         }
         navigator.mediaDevices.getUserMedia(constraints).then( (stream) => {
+            console.log('Got MediaStream: ', stream);
             local_av.srcObject = stream;
             local_stream = stream;
             if (old_call_state == 'calling') {
@@ -129,6 +133,7 @@ function setCallState(state)
                 };
             }
         }).catch (error => {
+            console.error('Error accessing media devices.', error);
         });
     } else {
         cancelCallSound();
@@ -193,6 +198,7 @@ async function post(url, json, callback)
  */
 function publish(type, data)
 {
+    console.log("publish sending: " , event);
     event_source.send({
         type: type,
         data: data
@@ -219,6 +225,8 @@ function handleMessage(message)
         let handler = allowed_messages[type];
         handler(type, data);
     } else {
+        console.error("messages.js can't handle message of type:" +
+            type);
     }
 }
 /**
@@ -227,11 +235,15 @@ function handleMessage(message)
 function handleClientAnswer(type, data)
 {
     if (peer_connection == null) {
+        console.error('Before processing the client-answer, ' +
+            'I need a client-offer');
         return;
     }
+    console.log("handleClientAnswer method");
     peer_connection.setRemoteDescription(
         new RTCSessionDescription(data),function() {},
         function(e) {
+            console.log("Problem while doing client-answer: ", e);
             return;
     });
 }
@@ -241,10 +253,14 @@ function handleClientAnswer(type, data)
 function handleClientCandidate(type, data)
 {
     if (peer_connection == null) {
+        console.error('Before processing the client-answer, '+
+            'I need a client-offer');
         return;
     }
+    console.log("handleClientCandidate method");
     peer_connection.addIceCandidate(new RTCIceCandidate(data), function(){},
         function(e) {
+            console.log("Problem adding ice candidate: " + e);
             return;
         }
     );
@@ -254,6 +270,7 @@ function handleClientCandidate(type, data)
  */
 function handleCallEnd(type, data)
 {
+    console.log("handleCallEnd method");
     call_state = type;
     setCallState('video-end-received');
 }
@@ -262,6 +279,7 @@ function handleCallEnd(type, data)
  */
 function handleClientCall(type, data)
 {
+    console.log("handleClientCall method");
     startCallSound();
     old_call_state = type;
     call_state = 'calling';
@@ -284,9 +302,11 @@ function makeOffer()
                     peer_connection.localDescription);
             }
         ).catch(function (event) {
+            console.log("Problem with publishing client offer: " + event);
             return;
         });
     }).catch(function (event) {
+        console.log("Problem while doing client-call: " + event);
         return;
     });
 }
@@ -302,13 +322,16 @@ function handleClientOffer(type, data)
             if (!answer) {
                 peer_connection.createAnswer(function (desc) {
                     peer_connection.setLocalDescription(desc, function () {
+                        console.log("publishing answer method");
                         publish('client-answer',
                             peer_connection.localDescription);
                     }, function(e) {
+                        console.log("Problem getting client answer: ",e);
                         return;
                     });
                 },
                 function(e) {
+                    console.log("Problem while doing client-offer: ", e);
                     return;
                 });
                 setDisplay('av-call-div', false);
@@ -316,6 +339,7 @@ function handleClientOffer(type, data)
             }
         },
         function(e) {
+            console.log("Problem while doing client-offer2: ", e);
             return;
         }
     );
@@ -328,6 +352,7 @@ function handleStatus(type, data)
     if (!data) {
         return;
     }
+    console.log("handleStatus method");
     let conversation_obj = elt('conversation');
     let conversation_time = (conversation_obj) ?
     parseInt(conversation_obj.getAttribute('data-time')) : 0;
@@ -341,8 +366,8 @@ function handleStatus(type, data)
     if (update_time) {
         conversation_obj.setAttribute('data-time', update_time);
     }
-    conversation_obj.innerHTML = new_conversation_obj.innerHTML +
-        conversation_obj.innerHTML;
+    conversation_obj.innerHTML = conversation_obj.innerHTML +
+        new_conversation_obj.innerHTML;
     setTimeout("resetBackground()", 0.5 * sec);
 }
 /**
@@ -350,10 +375,14 @@ function handleStatus(type, data)
  */
 function sendMessage(message)
 {
+    console.log("sendMessage method");
+    console.log("Sending via fetch api: ", message);
     post(start_url + "&type=call-event",
         message,
         (data) => {
             // Success function.
+            console.log("Successfully sent message:", message);
+            console.log("Data back from server:", data);
         }
     );
 }
@@ -363,11 +392,17 @@ function sendMessage(message)
 function icecandidate(local_stream)
 {
     if (have_set_local_ice_candidate) {
+        console.log("Already set ice candidate for local stream. Returning.");
         return;
     }
+    console.log("icecandidate method");
+    console.log(configuration);
     peer_connection = new RTCPeerConnection(configuration);
     peer_connection.onicecandidate = function (event) {
+        console.log("Got ice candidate");
+        console.log(event);
         if (event.candidate && event.candidate != sent_candidate) {
+            console.log("sending");
             sent_candidate = event.candidate;
             publish('client-candidate', event.candidate);
         }
@@ -380,6 +415,7 @@ function icecandidate(local_stream)
         }
     }
     peer_connection.ontrack = function (event) {
+        console.log("Trying to add stream to remote_av");
         remote_stream = event.streams[0];
         remote_av.srcObject = remote_stream;
         setDisplay('av-call-div', false);
@@ -468,7 +504,8 @@ function loadMoreMessages()
             var newLoadedCount = loadedCount + data.message_count;
             conversation.setAttribute('data-loaded-count', newLoadedCount);
             if (data.oldest_timestamp) {
-                conversation.setAttribute('data-oldest-timestamp', data.oldest_timestamp);
+                conversation.setAttribute('data-oldest-timestamp',
+                    data.oldest_timestamp);
             }
         } else {
             hasMoreMessages = false;
diff --git a/src/views/elements/UsermessagesElement.php b/src/views/elements/UsermessagesElement.php
index 7f2975f95..7cd01545a 100644
--- a/src/views/elements/UsermessagesElement.php
+++ b/src/views/elements/UsermessagesElement.php
@@ -249,7 +249,8 @@ class UsermessagesElement extends Element implements CrawlConstants
                 $user_locale = $data['CURRENT_LOCALE_TAG'] ?? 'en_US';
                 $is_auto_translating = ($current_language === 'off' ||
                     empty($current_language)) &&
-                    $user_locale !== 'en_US' && $user_locale !== C\DEFAULT_LOCALE &&
+                    $user_locale !== 'en_US' &&
+                    $user_locale !== C\DEFAULT_LOCALE &&
                     isset($available_locales[$user_locale]);
                 if ($current_language !== 'off' &&
                     isset($available_locales[$current_language])) {
-- 
2.39.5 (Apple Git-154)


From 953ac0ecb7af0b671b8d5c7dd5ad58b091281bbd Mon Sep 17 00:00:00 2001
From: Aditya <thesyn3rman@gmail.com>
Date: Tue, 18 Nov 2025 09:40:10 -0800
Subject: [PATCH 14/14] variable names

Signed-off-by: Aditya <thesyn3rman@gmail.com>
---
 src/controllers/ApiController.php |  2 +-
 src/scripts/messages.js           | 64 +++++++++++++++----------------
 2 files changed, 33 insertions(+), 33 deletions(-)

diff --git a/src/controllers/ApiController.php b/src/controllers/ApiController.php
index 047756f49..9088792c9 100644
--- a/src/controllers/ApiController.php
+++ b/src/controllers/ApiController.php
@@ -476,7 +476,7 @@ class ApiController extends Controller implements CrawlConstants
         $mapping_string = "Use the following language mapping for " .
             "accurate translations:\n";
         foreach ($language_map as $code => $lang) {
-            $mapping_string .= "$code → $lang\n";
+            $mapping_string .= "$code -> $lang\n";
         }
         $translate_prompt = "Translate the text that is in following " .
             "sequence of <in></in> tags to the locale " . $target_lang .
diff --git a/src/scripts/messages.js b/src/scripts/messages.js
index bc1f92fea..077449673 100644
--- a/src/scripts/messages.js
+++ b/src/scripts/messages.js
@@ -423,23 +423,23 @@ function icecandidate(local_stream)
     };
     have_set_local_ice_candidate = true;
 }
-var loadingMessages = false;
-var hasMoreMessages = true;
-var lastScrollTime = 0;
+var loading_messages = false;
+var has_more_messages = true;
+var last_scroll_time = 0;
 /*
  * Handles scroll events on the conversation div to implement infinite scroll
  */
 function handleConversationScroll()
 {
     var conversation = elt('conversation');
-    if (!conversation || loadingMessages || !hasMoreMessages) {
+    if (!conversation || loading_messages || !has_more_messages) {
         return;
     }
     var now = Date.now();
-    if (now - lastScrollTime < 500) {
+    if (now - last_scroll_time < 500) {
         return;
     }
-    lastScrollTime = now;
+    last_scroll_time = now;
     if (conversation.scrollTop < 100) {
         loadMoreMessages();
     }
@@ -449,44 +449,44 @@ function handleConversationScroll()
  */
 function loadMoreMessages()
 {
-    if (loadingMessages) {
+    if (loading_messages) {
         return;
     }
-    loadingMessages = true;
+    loading_messages = true;
     var conversation = elt('conversation');
-    var contactId = conversation.getAttribute('data-contact-id');
-    var loadedCount = parseInt(conversation.getAttribute('data-loaded-count'));
-    var oldestTimestamp = parseInt(conversation.getAttribute(
+    var contact_id = conversation.getAttribute('data-contact-id');
+    var loaded_count = parseInt(conversation.getAttribute('data-loaded-count'));
+    var oldest_timestamp = parseInt(conversation.getAttribute(
         'data-oldest-timestamp'));
-    if (!contactId || !oldestTimestamp) {
-        loadingMessages = false;
+    if (!contact_id || !oldest_timestamp) {
+        loading_messages = false;
         return;
     }
     var controller = 'social';
     var params = new URLSearchParams({
         'c': controller, 'a': 'userMessages', 'arg': 'loadmessages',
-        'contact_id': contactId, 'before_timestamp': oldestTimestamp,
+        'contact_id': contact_id, 'before_timestamp': oldest_timestamp,
         'limit': 20});
-    var csrfInput = document.querySelector('input[name="' +
+    var csrf_input = document.querySelector('input[name="' +
         window.CSRF_TOKEN + '"]');
-    if (csrfInput) {
-        params.append(window.CSRF_TOKEN, csrfInput.value);
+    if (csrf_input) {
+        params.append(window.CSRF_TOKEN, csrf_input.value);
     }
-    var baseUrl = window.start_url.replace('&arg=status',
+    var base_url = window.start_url.replace('&arg=status',
         '&arg=loadmessages');
-    baseUrl += '&contact_id=' + contactId;
-    baseUrl += '&before_timestamp=' + oldestTimestamp;
-    baseUrl += '&limit=10';
-    fetch(baseUrl, {
+    base_url += '&contact_id=' + contact_id;
+    base_url += '&before_timestamp=' + oldest_timestamp;
+    base_url += '&limit=10';
+    fetch(base_url, {
         method: 'GET',
         headers: {
             'Accept': 'application/json',
         }
     })
     .then(response => {
-        const contentType = response.headers.get('content-type');
-        if (!contentType ||
-            !contentType.includes('application/json')) {
+        const content_type = response.headers.get('content-type');
+        if (!content_type ||
+            !content_type.includes('application/json')) {
             return response.text().then(text => {
                 throw new Error('Server returned HTML instead of JSON');
             });
@@ -495,24 +495,24 @@ function loadMoreMessages()
     })
     .then(data => {
         if (data.error) {
-            loadingMessages = false;
+            loading_messages = false;
             return;
         }
         if (data.html && data.message_count > 0) {
             conversation.insertAdjacentHTML('beforeend', data.html);
-            hasMoreMessages = data.has_more;
-            var newLoadedCount = loadedCount + data.message_count;
-            conversation.setAttribute('data-loaded-count', newLoadedCount);
+            has_more_messages = data.has_more;
+            var new_loaded_count = loaded_count + data.message_count;
+            conversation.setAttribute('data-loaded-count', new_loaded_count);
             if (data.oldest_timestamp) {
                 conversation.setAttribute('data-oldest-timestamp',
                     data.oldest_timestamp);
             }
         } else {
-            hasMoreMessages = false;
+            has_more_messages = false;
         }
-        loadingMessages = false;
+        loading_messages = false;
     })
     .catch(error => {
-        loadingMessages = false;
+        loading_messages = false;
     });
 }
\ No newline at end of file
-- 
2.39.5 (Apple Git-154)

