From 8a98f3501ee7aedbc98d638f7395a6617d252275 Mon Sep 17 00:00:00 2001 From: Arjun <6592213+arkml@users.noreply.github.com> Date: Wed, 13 May 2026 14:08:50 +0530 Subject: [PATCH] render html emails --- apps/x/apps/renderer/src/App.css | 9 ++ .../renderer/src/components/email-view.tsx | 82 +++++++++++- .../packages/core/src/knowledge/sync_gmail.ts | 125 +++++++++++++----- apps/x/packages/shared/src/blocks.ts | 1 + 4 files changed, 186 insertions(+), 31 deletions(-) diff --git a/apps/x/apps/renderer/src/App.css b/apps/x/apps/renderer/src/App.css index d14a85cb..2babd40d 100644 --- a/apps/x/apps/renderer/src/App.css +++ b/apps/x/apps/renderer/src/App.css @@ -349,6 +349,15 @@ white-space: pre-wrap; } +.gmail-message-iframe { + display: block; + width: 100%; + max-width: 820px; + margin-top: 14px; + border: 0; + background: #fff; +} + .gmail-thread-actions { display: flex; gap: 8px; diff --git a/apps/x/apps/renderer/src/components/email-view.tsx b/apps/x/apps/renderer/src/components/email-view.tsx index af978bfe..9c4fee0d 100644 --- a/apps/x/apps/renderer/src/components/email-view.tsx +++ b/apps/x/apps/renderer/src/components/email-view.tsx @@ -90,6 +90,86 @@ function latestMessage(thread: GmailThread): GmailThreadMessage | undefined { return thread.messages[thread.messages.length - 1] } +function escapeHtml(text: string): string { + return text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + +function buildEmailDocument(html: string): string { + return ` + + + + +${html}` +} + +function MessageBody({ message }: { message: GmailThreadMessage }) { + const iframeRef = useRef(null) + const observerRef = useRef(null) + const [height, setHeight] = useState(80) + + const srcDoc = useMemo(() => { + if (message.bodyHtml && message.bodyHtml.trim()) { + return buildEmailDocument(message.bodyHtml) + } + const text = (message.body || '(No message body)').trim() + return buildEmailDocument(`
${escapeHtml(text)}
`) + }, [message.bodyHtml, message.body]) + + const handleLoad = useCallback(() => { + const iframe = iframeRef.current + const doc = iframe?.contentDocument + if (!doc?.body) return + const measure = () => { + const next = Math.max(40, doc.documentElement.scrollHeight) + setHeight((current) => (current === next ? current : next)) + } + measure() + observerRef.current?.disconnect() + if (typeof ResizeObserver !== 'undefined') { + observerRef.current = new ResizeObserver(measure) + observerRef.current.observe(doc.body) + } + }, []) + + useEffect(() => () => observerRef.current?.disconnect(), []) + + return ( +