Shows full message content when hovering over contacts in the chat list
Size
7.6 KB
Version
1.0.1
Created
Jan 8, 2026
Updated
14 days ago
1// ==UserScript==
2// @name Telegram Message Preview on Hover
3// @description Shows full message content when hovering over contacts in the chat list
4// @version 1.0.1
5// @match https://*.web.telegram.org/*
6// @icon https://web.telegram.org/a/favicon.ico
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('Telegram Message Preview on Hover - Extension loaded');
12
13 // Add styles for the tooltip
14 TM_addStyle(`
15 .telegram-message-tooltip {
16 position: fixed;
17 background: rgba(0, 0, 0, 0.95);
18 color: #ffffff;
19 padding: 12px 16px;
20 border-radius: 8px;
21 max-width: 400px;
22 font-size: 14px;
23 line-height: 1.5;
24 z-index: 10000;
25 pointer-events: none;
26 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
27 word-wrap: break-word;
28 white-space: pre-wrap;
29 opacity: 0;
30 transition: opacity 0.2s ease-in-out;
31 }
32
33 .telegram-message-tooltip.visible {
34 opacity: 1;
35 }
36
37 .telegram-message-tooltip .tooltip-header {
38 font-weight: bold;
39 margin-bottom: 8px;
40 padding-bottom: 8px;
41 border-bottom: 1px solid rgba(255, 255, 255, 0.2);
42 color: #8ab4f8;
43 }
44
45 .telegram-message-tooltip .tooltip-content {
46 color: #e8eaed;
47 }
48 `);
49
50 let tooltip = null;
51 let currentHoveredElement = null;
52 let showTimeout = null;
53
54 // Create tooltip element
55 function createTooltip() {
56 if (tooltip) return tooltip;
57
58 tooltip = document.createElement('div');
59 tooltip.className = 'telegram-message-tooltip';
60 document.body.appendChild(tooltip);
61 console.log('Tooltip element created');
62 return tooltip;
63 }
64
65 // Get message content from chat item
66 function getMessageContent(chatItem) {
67 try {
68 // Find the last message summary
69 const messageSummary = chatItem.querySelector('.last-message-summary');
70 if (!messageSummary) {
71 console.log('No message summary found');
72 return null;
73 }
74
75 // Get the text content, excluding media preview labels
76 let messageText = '';
77 const textNodes = [];
78
79 // Walk through all text nodes
80 const walker = document.createTreeWalker(
81 messageSummary,
82 NodeFilter.SHOW_TEXT,
83 null,
84 false
85 );
86
87 let node;
88 while (node = walker.nextNode()) {
89 const text = node.textContent.trim();
90 if (text) {
91 textNodes.push(text);
92 }
93 }
94
95 messageText = textNodes.join(' ');
96
97 // Get chat name
98 const chatName = chatItem.querySelector('.fullName')?.textContent || 'Unknown';
99
100 console.log('Extracted message:', messageText.substring(0, 50) + '...');
101
102 return {
103 chatName: chatName,
104 message: messageText
105 };
106 } catch (error) {
107 console.error('Error extracting message content:', error);
108 return null;
109 }
110 }
111
112 // Position tooltip near cursor
113 function positionTooltip(event) {
114 if (!tooltip) return;
115
116 const padding = 15;
117 const tooltipRect = tooltip.getBoundingClientRect();
118 let x = event.clientX + padding;
119 let y = event.clientY + padding;
120
121 // Adjust if tooltip goes off screen
122 if (x + tooltipRect.width > window.innerWidth) {
123 x = event.clientX - tooltipRect.width - padding;
124 }
125 if (y + tooltipRect.height > window.innerHeight) {
126 y = event.clientY - tooltipRect.height - padding;
127 }
128
129 tooltip.style.left = x + 'px';
130 tooltip.style.top = y + 'px';
131 }
132
133 // Show tooltip
134 function showTooltip(chatItem, event) {
135 const content = getMessageContent(chatItem);
136 if (!content || !content.message) {
137 console.log('No content to show');
138 return;
139 }
140
141 createTooltip();
142
143 tooltip.innerHTML = `
144 <div class="tooltip-header">${content.chatName}</div>
145 <div class="tooltip-content">${content.message}</div>
146 `;
147
148 positionTooltip(event);
149
150 // Show tooltip with slight delay
151 setTimeout(() => {
152 if (tooltip && currentHoveredElement === chatItem) {
153 tooltip.classList.add('visible');
154 }
155 }, 50);
156 }
157
158 // Hide tooltip
159 function hideTooltip() {
160 if (tooltip) {
161 tooltip.classList.remove('visible');
162 }
163 currentHoveredElement = null;
164 if (showTimeout) {
165 clearTimeout(showTimeout);
166 showTimeout = null;
167 }
168 }
169
170 // Handle mouse enter on chat item
171 function handleMouseEnter(event) {
172 const chatItem = event.currentTarget;
173 currentHoveredElement = chatItem;
174
175 // Show tooltip after a short delay
176 showTimeout = setTimeout(() => {
177 if (currentHoveredElement === chatItem) {
178 showTooltip(chatItem, event);
179 }
180 }, 300);
181 }
182
183 // Handle mouse move to update tooltip position
184 function handleMouseMove(event) {
185 if (tooltip && tooltip.classList.contains('visible')) {
186 positionTooltip(event);
187 }
188 }
189
190 // Handle mouse leave
191 function handleMouseLeave() {
192 hideTooltip();
193 }
194
195 // Attach event listeners to chat items
196 function attachListeners() {
197 const chatItems = document.querySelectorAll('.ListItem.Chat');
198 console.log(`Found ${chatItems.length} chat items`);
199
200 chatItems.forEach(chatItem => {
201 // Check if already has listeners
202 if (chatItem.dataset.tooltipAttached) return;
203
204 chatItem.addEventListener('mouseenter', handleMouseEnter);
205 chatItem.addEventListener('mousemove', handleMouseMove);
206 chatItem.addEventListener('mouseleave', handleMouseLeave);
207 chatItem.dataset.tooltipAttached = 'true';
208 });
209 }
210
211 // Debounce function
212 function debounce(func, wait) {
213 let timeout;
214 return function executedFunction(...args) {
215 const later = () => {
216 clearTimeout(timeout);
217 func(...args);
218 };
219 clearTimeout(timeout);
220 timeout = setTimeout(later, wait);
221 };
222 }
223
224 // Observe DOM changes to handle dynamically loaded chats
225 function observeChatList() {
226 const chatListContainer = document.querySelector('.chat-list');
227 if (!chatListContainer) {
228 console.log('Chat list container not found, retrying...');
229 setTimeout(observeChatList, 1000);
230 return;
231 }
232
233 console.log('Chat list container found, setting up observer');
234
235 const debouncedAttach = debounce(attachListeners, 500);
236
237 const observer = new MutationObserver(debouncedAttach);
238 observer.observe(chatListContainer, {
239 childList: true,
240 subtree: true
241 });
242
243 // Initial attachment
244 attachListeners();
245 }
246
247 // Initialize
248 function init() {
249 console.log('Initializing Telegram Message Preview extension');
250
251 TM_runBody(() => {
252 // Wait for chat list to load
253 setTimeout(() => {
254 observeChatList();
255 }, 1000);
256 });
257 }
258
259 init();
260})();