Skip to content

Commit 4b11c27

Browse files
committed
wip
1 parent 8033ba5 commit 4b11c27

File tree

6 files changed

+146
-133
lines changed

6 files changed

+146
-133
lines changed

webapp/chat-app/src/components/ChatInterface.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ const ChatInterface: React.FC<ChatInterfaceProps> = ({
9595
timestamp: data.timestamp,
9696
isHtml: true,
9797
rawHtml: data.data,
98-
version: '1.0',
98+
version: data.timestamp.toString(),
9999
sanitized: false
100100
};
101101
if (isComponentMounted) {

webapp/chat-app/src/components/MessageList.tsx

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,35 @@ import {useSelector} from 'react-redux';
44
import {RootState} from '../store';
55
import {logger} from '../utils/logger';
66
import {Message} from '../types';
7-
import {updateTabs, resetTabState} from '../utils/tabHandling';
7+
import {resetTabState, updateTabs} from '../utils/tabHandling';
88
import WebSocketService from "../services/websocket";
99

10-
const expandMessageReferences = (content: string, messages: Message[]): string => {
10+
export const expandMessageReferences = (content: string, messages: Message[]): string => {
1111
if (!content) return '';
12-
return content.replace(/\{([^}]+)}/g, (match, messageId) => {
13-
const referencedMessage = messages.find(m => m.id === messageId);
14-
if (referencedMessage) {
15-
return expandMessageReferences(referencedMessage.content, messages);
12+
// Create a temporary div to parse HTML content
13+
const tempDiv = document.createElement('div');
14+
tempDiv.innerHTML = content;
15+
// Process all elements with IDs that match message references
16+
const processNode = (node: HTMLElement) => {
17+
if (node.id && node.id.startsWith('z')) {
18+
const referencedMessage = messages.find(m => m.id === node.id);
19+
if (referencedMessage) {
20+
logger.debug('Expanding referenced message', {id: node.id, contentLength: referencedMessage.content.length});
21+
node.innerHTML = expandMessageReferences(referencedMessage.content, messages);
22+
} else {
23+
logger.debug('Referenced message not found', {id: node.id});
24+
}
1625
}
17-
return match;
18-
});
26+
// Recursively process child elements
27+
Array.from(node.children).forEach(child => {
28+
if (child instanceof HTMLElement) {
29+
processNode(child);
30+
}
31+
});
32+
};
33+
logger.debug('Expanding message references', {content});
34+
processNode(tempDiv);
35+
return tempDiv.innerHTML;
1936
};
2037

2138
const MessageListContainer = styled.div`
@@ -31,9 +48,26 @@ const MessageContent = styled.div`
3148
.href-link, .play-button, .regen-button, .cancel-button, .text-submit-button {
3249
cursor: pointer;
3350
user-select: none;
51+
display: inline-block;
52+
padding: 2px 8px;
53+
margin: 2px;
54+
border-radius: 4px;
55+
background-color: rgba(0, 0, 0, 0.1);
3456
3557
&:hover {
3658
opacity: 0.8;
59+
background-color: rgba(0, 0, 0, 0.2);
60+
}
61+
}
62+
63+
.referenced-message {
64+
cursor: pointer;
65+
padding: 4px;
66+
margin: 4px 0;
67+
border-left: 3px solid #ccc;
68+
69+
&.expanded {
70+
background-color: rgba(0, 0, 0, 0.05);
3771
}
3872
}
3973
`;
@@ -164,27 +198,22 @@ const MessageList: React.FC<MessageListProps> = ({messages: propMessages}) => {
164198
});
165199
// Process tabs after messages update
166200
requestAnimationFrame(() => {
167-
try {
168-
updateTabs();
169-
// Process message references in visible tab content
170-
document.querySelectorAll('.tab-content.active').forEach(tab => {
171-
const content = tab.innerHTML;
172-
const expandedContent = processMessageContent(content);
173-
if (content !== expandedContent) {
174-
tab.innerHTML = expandedContent;
175-
}
176-
});
177-
} catch (error) {
178-
logger.error('Error processing tabs:', error);
179-
// Reset tab state on error
180-
resetTabState();
181-
}
201+
try {
202+
updateTabs();
203+
} catch (error) {
204+
logger.error('Error processing tabs:', error);
205+
// Reset tab state on error
206+
resetTabState();
207+
}
182208
});
183209
}, [messages]);
184210

185211
return (
186212
<MessageListContainer>
187-
{messages.map((message) => {
213+
{messages
214+
.filter((message) => message.id && !message.id.startsWith("z"))
215+
.filter((message) => message.content && message.content.length > 0)
216+
.map((message) => {
188217
logger.debug('MessageList - Rendering message', {
189218
id: message.id,
190219
type: message.type,

webapp/chat-app/src/hooks/useWebSocket.ts

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,28 +29,7 @@ export const useWebSocket = (sessionId: string) => {
2929
console.warn('[WebSocket] Received message missing required fields:', message);
3030
return;
3131
}
32-
33-
if (message.isHtml) {
34-
console.log('[WebSocket] Processing HTML message');
35-
const htmlMessage = {
36-
id: message.id,
37-
content: message.content,
38-
type: 'response' as const,
39-
timestamp: message.timestamp,
40-
isHtml: true,
41-
rawHtml: message.rawHtml,
42-
version: message.version,
43-
sanitized: false,
44-
};
45-
console.log('[WebSocket] Dispatching HTML message to store:', {
46-
id: htmlMessage.id,
47-
version: htmlMessage.version,
48-
type: htmlMessage.type
49-
});
50-
dispatch(addMessage(htmlMessage));
51-
} else {
52-
console.log('[WebSocket] Received non-HTML message, skipping processing');
53-
}
32+
dispatch(addMessage(message));
5433
};
5534

5635
const handleConnectionChange = (connected: boolean) => {

webapp/chat-app/src/services/websocket.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ export class WebSocketService {
1313
private errorHandlers: ((error: Error) => void)[] = [];
1414
private isReconnecting = false;
1515
private connectionTimeout: NodeJS.Timeout | null = null;
16-
private messageQueue: string[] = [];
1716

1817
public getSessionId(): string {
1918
console.debug('[WebSocket] Getting session ID:', this.sessionId);
@@ -200,24 +199,29 @@ export class WebSocketService {
200199
console.debug('[WebSocket] Sending initial connect message');
201200
};
202201
this.ws.onmessage = (event) => {
203-
// Only log message receipt in debug mode
204202
this.debugLog('Message received');
205-
// Parse message data
206-
const [id, version, content] = event.data.split(',');
207-
if (!id || !version) {
203+
// Find the first two comma positions to extract id and version
204+
const firstComma = event.data.indexOf(',');
205+
const secondComma = event.data.indexOf(',', firstComma + 1);
206+
if (firstComma === -1 || secondComma === -1) {
208207
console.warn('[WebSocket] Received malformed message:', event.data);
209208
return;
210209
}
210+
const id = event.data.substring(0, firstComma);
211+
const version = event.data.substring(firstComma + 1, secondComma);
212+
const content = event.data.substring(secondComma + 1);
211213

212-
// Enhanced HTML detection and handling
213-
const isHtml = typeof content === 'string' &&
214-
(/<[a-z][\s\S]*>/i.test(content));
215-
// Ignore connect messages
216-
if (content.includes('"type":"connect"')) {
217-
console.debug('[WebSocket] Ignoring connect message');
214+
if (!id || !version) {
215+
console.warn('[WebSocket] Received malformed message:', event.data);
218216
return;
219217
}
218+
this.debugLog('Parsed message parts:', {
219+
id,
220+
version,
221+
contentLength: content.length
222+
});
220223

224+
const isHtml = typeof content === 'string' && (/<[a-z][\s\S]*>/i.test(content));
221225
if (isHtml) {
222226
console.debug('[WebSocket] HTML content detected, preserving markup');
223227
}
@@ -228,10 +232,15 @@ export class WebSocketService {
228232
version,
229233
content,
230234
isHtml,
231-
rawHtml: isHtml ? content : null,
235+
rawHtml: content,
232236
timestamp: Date.now(),
233237
sanitized: false
234238
};
239+
240+
if (message.isHtml) {
241+
console.log('[WebSocket] Processing HTML message');
242+
}
243+
235244
this.messageHandlers.forEach((handler) => handler(message));
236245
};
237246

webapp/chat-app/src/store/slices/messageSlice.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ const sanitizeHtmlContent = (content: string): string => {
2929
return DOMPurify.sanitize(content, {
3030
ALLOWED_TAGS: ['div', 'span', 'p', 'br', 'b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li', 'code', 'pre', 'table', 'tr', 'td', 'th', 'thead', 'tbody',
3131
'button', 'input', 'label', 'select', 'option', 'textarea', 'code', 'pre', 'div', 'section'],
32-
ALLOWED_ATTR: ['class', 'href', 'target', 'data-tab', 'data-for-tab', 'style', 'type', 'value', 'id', 'name'],
32+
ALLOWED_ATTR: ['class', 'href', 'target', 'data-tab', 'data-for-tab', 'style', 'type', 'value', 'id', 'name',
33+
'data-message-id', 'data-id', 'data-message-action', 'data-action', 'data-ref-id', 'data-version'],
3334
});
3435
};
3536

@@ -41,6 +42,7 @@ const messageSlice = createSlice({
4142
const messageId = action.payload.id;
4243
const messageVersion = action.payload.version;
4344
const existingVersion = state.messageVersions[messageId];
45+
// Skip processing if message is older or duplicate
4446
if (existingVersion && existingVersion >= messageVersion) {
4547
console.debug(`${LOG_PREFIX} Ignoring older/duplicate message version:`, {
4648
id: messageId,
@@ -49,11 +51,20 @@ const messageSlice = createSlice({
4951
});
5052
return;
5153
}
54+
// Update version tracking
55+
state.messageVersions[messageId] = messageVersion;
56+
57+
// Store reference messages separately
58+
if (messageId.startsWith('z')) {
59+
state.referenceMessages[messageId] = action.payload;
60+
}
61+
5262
console.debug(`${LOG_PREFIX} Adding message:`, {
5363
id: messageId,
5464
version: messageVersion,
5565
type: action.payload.type,
56-
isHtml: action.payload.isHtml
66+
isHtml: action.payload.isHtml,
67+
// payload: action.payload,
5768
});
5869
state.messageVersions[messageId] = messageVersion;
5970
if (existingVersion) {
@@ -66,14 +77,8 @@ const messageSlice = createSlice({
6677
action.payload.content = sanitizeHtmlContent(action.payload.rawHtml);
6778
action.payload.sanitized = true;
6879
console.debug(`${LOG_PREFIX} HTML content sanitized for message ${action.payload.id}`);
69-
const observer = new MutationObserver(() => {
70-
updateTabs();
71-
observer.disconnect();
72-
});
73-
observer.observe(document.body, {
74-
childList: true,
75-
subtree: true
76-
});
80+
// Use requestAnimationFrame for smoother updates
81+
requestAnimationFrame(() => { updateTabs(); });
7782
}
7883
state.messages.push(action.payload);
7984
console.debug(`${LOG_PREFIX} Messages updated, total count: ${state.messages.length}`);

0 commit comments

Comments
 (0)