Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions components/download-report-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,20 @@ export const DownloadReportButton = () => {

setShowTemplate(true)

// Wait for React to render the template
await new Promise(resolve => setTimeout(resolve, 500))
// Poll until the portal has committed to the DOM, up to 3 seconds
await new Promise<void>((resolve, reject) => {
const start = Date.now()
const check = () => {
if (document.getElementById('report-template')) {
resolve()
} else if (Date.now() - start > 3000) {
reject(new Error('Element with id report-template not found'))
} else {
requestAnimationFrame(check)
}
}
requestAnimationFrame(check)
})

let chatTitle = 'Untitled Chat'
if (aiState.messages.length > 0) {
Expand Down
27 changes: 21 additions & 6 deletions components/report-template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@ import { AIMessage } from '@/lib/types'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'

// AIMessage content can be a string or an array of content parts ({type, text} | {type, image, ...})
function getContentString(content: AIMessage['content']): string {
if (typeof content === 'string') {
return content
}
if (Array.isArray(content)) {
return content
.filter((part: any) => part.type === 'text')
.map((part: any) => part.text ?? '')
.join('\n')
}
return ''
}

export interface ReportTemplateProps {
messages: AIMessage[]
drawnFeatures?: Array<{
Expand Down Expand Up @@ -50,11 +64,12 @@ export const ReportTemplate: React.FC<ReportTemplateProps> = ({
{filteredMessages.map((message, index) => {
if (message.type === 'input' || message.type === 'input_related') {
let content = ''
const rawContent = getContentString(message.content)
try {
const json = JSON.parse(message.content as string)
content = message.type === 'input' ? json.input : json.related_query
const json = JSON.parse(rawContent)
content = message.type === 'input' ? (json.input ?? rawContent) : (json.related_query ?? rawContent)
} catch (e) {
content = message.content as string
content = rawContent
}
return (
<div key={index} className="bg-gray-50 p-4 rounded-lg border-l-4 border-blue-500">
Expand All @@ -67,17 +82,17 @@ export const ReportTemplate: React.FC<ReportTemplateProps> = ({
<div key={index} className="prose prose-sm max-w-none">
<p className="text-sm font-bold text-green-600 mb-1">AI Response</p>
<ReactMarkdown remarkPlugins={[remarkGfm]}>
{message.content as string}
{getContentString(message.content)}
</ReactMarkdown>
</div>
)
} else if (message.type === 'resolution_search_result') {
try {
const result = JSON.parse(message.content as string)
const result = JSON.parse(getContentString(message.content))
return (
<div key={index} className="space-y-4">
<p className="text-sm font-bold text-purple-600 mb-1">Analysis Result</p>
{result.summary && (

<div className="bg-purple-50 p-4 rounded-lg text-gray-800">
{result.summary}
</div>
Expand Down