Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
acharneski committed Nov 28, 2024
1 parent 8033ba5 commit 4b11c27
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 133 deletions.
2 changes: 1 addition & 1 deletion webapp/chat-app/src/components/ChatInterface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const ChatInterface: React.FC<ChatInterfaceProps> = ({
timestamp: data.timestamp,
isHtml: true,
rawHtml: data.data,
version: '1.0',
version: data.timestamp.toString(),
sanitized: false
};
if (isComponentMounted) {
Expand Down
77 changes: 53 additions & 24 deletions webapp/chat-app/src/components/MessageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,35 @@ import {useSelector} from 'react-redux';
import {RootState} from '../store';
import {logger} from '../utils/logger';
import {Message} from '../types';
import {updateTabs, resetTabState} from '../utils/tabHandling';
import {resetTabState, updateTabs} from '../utils/tabHandling';
import WebSocketService from "../services/websocket";

const expandMessageReferences = (content: string, messages: Message[]): string => {
export const expandMessageReferences = (content: string, messages: Message[]): string => {
if (!content) return '';
return content.replace(/\{([^}]+)}/g, (match, messageId) => {
const referencedMessage = messages.find(m => m.id === messageId);
if (referencedMessage) {
return expandMessageReferences(referencedMessage.content, messages);
// Create a temporary div to parse HTML content
const tempDiv = document.createElement('div');
tempDiv.innerHTML = content;
// Process all elements with IDs that match message references
const processNode = (node: HTMLElement) => {
if (node.id && node.id.startsWith('z')) {
const referencedMessage = messages.find(m => m.id === node.id);
if (referencedMessage) {
logger.debug('Expanding referenced message', {id: node.id, contentLength: referencedMessage.content.length});
node.innerHTML = expandMessageReferences(referencedMessage.content, messages);
} else {
logger.debug('Referenced message not found', {id: node.id});
}
}
return match;
});
// Recursively process child elements
Array.from(node.children).forEach(child => {
if (child instanceof HTMLElement) {
processNode(child);
}
});
};
logger.debug('Expanding message references', {content});
processNode(tempDiv);
return tempDiv.innerHTML;
};

const MessageListContainer = styled.div`
Expand All @@ -31,9 +48,26 @@ const MessageContent = styled.div`
.href-link, .play-button, .regen-button, .cancel-button, .text-submit-button {
cursor: pointer;
user-select: none;
display: inline-block;
padding: 2px 8px;
margin: 2px;
border-radius: 4px;
background-color: rgba(0, 0, 0, 0.1);
&:hover {
opacity: 0.8;
background-color: rgba(0, 0, 0, 0.2);
}
}
.referenced-message {
cursor: pointer;
padding: 4px;
margin: 4px 0;
border-left: 3px solid #ccc;
&.expanded {
background-color: rgba(0, 0, 0, 0.05);
}
}
`;
Expand Down Expand Up @@ -164,27 +198,22 @@ const MessageList: React.FC<MessageListProps> = ({messages: propMessages}) => {
});
// Process tabs after messages update
requestAnimationFrame(() => {
try {
updateTabs();
// Process message references in visible tab content
document.querySelectorAll('.tab-content.active').forEach(tab => {
const content = tab.innerHTML;
const expandedContent = processMessageContent(content);
if (content !== expandedContent) {
tab.innerHTML = expandedContent;
}
});
} catch (error) {
logger.error('Error processing tabs:', error);
// Reset tab state on error
resetTabState();
}
try {
updateTabs();
} catch (error) {
logger.error('Error processing tabs:', error);
// Reset tab state on error
resetTabState();
}
});
}, [messages]);

return (
<MessageListContainer>
{messages.map((message) => {
{messages
.filter((message) => message.id && !message.id.startsWith("z"))
.filter((message) => message.content && message.content.length > 0)
.map((message) => {
logger.debug('MessageList - Rendering message', {
id: message.id,
type: message.type,
Expand Down
23 changes: 1 addition & 22 deletions webapp/chat-app/src/hooks/useWebSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,7 @@ export const useWebSocket = (sessionId: string) => {
console.warn('[WebSocket] Received message missing required fields:', message);
return;
}

if (message.isHtml) {
console.log('[WebSocket] Processing HTML message');
const htmlMessage = {
id: message.id,
content: message.content,
type: 'response' as const,
timestamp: message.timestamp,
isHtml: true,
rawHtml: message.rawHtml,
version: message.version,
sanitized: false,
};
console.log('[WebSocket] Dispatching HTML message to store:', {
id: htmlMessage.id,
version: htmlMessage.version,
type: htmlMessage.type
});
dispatch(addMessage(htmlMessage));
} else {
console.log('[WebSocket] Received non-HTML message, skipping processing');
}
dispatch(addMessage(message));
};

const handleConnectionChange = (connected: boolean) => {
Expand Down
33 changes: 21 additions & 12 deletions webapp/chat-app/src/services/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export class WebSocketService {
private errorHandlers: ((error: Error) => void)[] = [];
private isReconnecting = false;
private connectionTimeout: NodeJS.Timeout | null = null;
private messageQueue: string[] = [];

public getSessionId(): string {
console.debug('[WebSocket] Getting session ID:', this.sessionId);
Expand Down Expand Up @@ -200,24 +199,29 @@ export class WebSocketService {
console.debug('[WebSocket] Sending initial connect message');
};
this.ws.onmessage = (event) => {
// Only log message receipt in debug mode
this.debugLog('Message received');
// Parse message data
const [id, version, content] = event.data.split(',');
if (!id || !version) {
// Find the first two comma positions to extract id and version
const firstComma = event.data.indexOf(',');
const secondComma = event.data.indexOf(',', firstComma + 1);
if (firstComma === -1 || secondComma === -1) {
console.warn('[WebSocket] Received malformed message:', event.data);
return;
}
const id = event.data.substring(0, firstComma);
const version = event.data.substring(firstComma + 1, secondComma);
const content = event.data.substring(secondComma + 1);

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

const isHtml = typeof content === 'string' && (/<[a-z][\s\S]*>/i.test(content));
if (isHtml) {
console.debug('[WebSocket] HTML content detected, preserving markup');
}
Expand All @@ -228,10 +232,15 @@ export class WebSocketService {
version,
content,
isHtml,
rawHtml: isHtml ? content : null,
rawHtml: content,
timestamp: Date.now(),
sanitized: false
};

if (message.isHtml) {
console.log('[WebSocket] Processing HTML message');
}

this.messageHandlers.forEach((handler) => handler(message));
};

Expand Down
25 changes: 15 additions & 10 deletions webapp/chat-app/src/store/slices/messageSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ const sanitizeHtmlContent = (content: string): string => {
return DOMPurify.sanitize(content, {
ALLOWED_TAGS: ['div', 'span', 'p', 'br', 'b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li', 'code', 'pre', 'table', 'tr', 'td', 'th', 'thead', 'tbody',
'button', 'input', 'label', 'select', 'option', 'textarea', 'code', 'pre', 'div', 'section'],
ALLOWED_ATTR: ['class', 'href', 'target', 'data-tab', 'data-for-tab', 'style', 'type', 'value', 'id', 'name'],
ALLOWED_ATTR: ['class', 'href', 'target', 'data-tab', 'data-for-tab', 'style', 'type', 'value', 'id', 'name',
'data-message-id', 'data-id', 'data-message-action', 'data-action', 'data-ref-id', 'data-version'],
});
};

Expand All @@ -41,6 +42,7 @@ const messageSlice = createSlice({
const messageId = action.payload.id;
const messageVersion = action.payload.version;
const existingVersion = state.messageVersions[messageId];
// Skip processing if message is older or duplicate
if (existingVersion && existingVersion >= messageVersion) {
console.debug(`${LOG_PREFIX} Ignoring older/duplicate message version:`, {
id: messageId,
Expand All @@ -49,11 +51,20 @@ const messageSlice = createSlice({
});
return;
}
// Update version tracking
state.messageVersions[messageId] = messageVersion;

// Store reference messages separately
if (messageId.startsWith('z')) {
state.referenceMessages[messageId] = action.payload;
}

console.debug(`${LOG_PREFIX} Adding message:`, {
id: messageId,
version: messageVersion,
type: action.payload.type,
isHtml: action.payload.isHtml
isHtml: action.payload.isHtml,
// payload: action.payload,
});
state.messageVersions[messageId] = messageVersion;
if (existingVersion) {
Expand All @@ -66,14 +77,8 @@ const messageSlice = createSlice({
action.payload.content = sanitizeHtmlContent(action.payload.rawHtml);
action.payload.sanitized = true;
console.debug(`${LOG_PREFIX} HTML content sanitized for message ${action.payload.id}`);
const observer = new MutationObserver(() => {
updateTabs();
observer.disconnect();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
// Use requestAnimationFrame for smoother updates
requestAnimationFrame(() => { updateTabs(); });
}
state.messages.push(action.payload);
console.debug(`${LOG_PREFIX} Messages updated, total count: ${state.messages.length}`);
Expand Down
Loading

0 comments on commit 4b11c27

Please sign in to comment.