Skip to content

Commit 1b97d31

Browse files
Devin/1746400423 issue 5 (#275)
* Extract text rendering logic into textRenderer utility Co-Authored-By: Hal Seki <[email protected]> * Remove old textRenderer.ts file Co-Authored-By: Hal Seki <[email protected]> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent 5878042 commit 1b97d31

File tree

3 files changed

+180
-308
lines changed

3 files changed

+180
-308
lines changed

frontend/src/pages/integration/TeamAnalysisResultPage.tsx

Lines changed: 1 addition & 297 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ import {
4444
FiShare2,
4545
} from 'react-icons/fi'
4646
import { Link, useNavigate } from 'react-router-dom'
47-
import MessageText from '../../components/slack/MessageText'
4847
import ErrorBoundary from '../../components/common/ErrorBoundary'
4948
import { useAnalysisData } from '../../hooks'
5049
import { ServiceResource } from '../../lib/integrationService'
50+
import { renderPlainText, extractSectionContent, isObviouslyNotJson } from '../../utils/textRenderer'
5151

5252
interface ChannelAnalysisListProps {
5353
title: string
@@ -73,148 +73,6 @@ const ChannelAnalysisList: FC<ChannelAnalysisListProps> = ({
7373
emptyMessage = 'No information available.',
7474
}) => {
7575
const navigate = useNavigate()
76-
const renderPlainText = (
77-
text: string | unknown,
78-
workspaceUuid: string | undefined
79-
) => {
80-
const textStr = typeof text === 'string' ? text : String(text || '')
81-
if (!textStr || textStr.trim().length === 0) {
82-
return <Text color="gray.500">No content available</Text>
83-
}
84-
85-
let cleanedText = textStr
86-
87-
if (/^\s*\{\s*\}\s*$/.test(cleanedText)) {
88-
return <Text color="gray.500">No content available</Text>
89-
}
90-
91-
cleanedText = cleanedText.replace(/\\n/g, '\n')
92-
93-
const isLikelyPlainText =
94-
/^[A-Za-z]/.test(cleanedText.trim()) &&
95-
!cleanedText.includes('```json') &&
96-
!(cleanedText.trim().startsWith('{') && cleanedText.trim().endsWith('}'))
97-
98-
if (isLikelyPlainText) {
99-
return (
100-
<Box className="formatted-text">
101-
{cleanedText.split('\n').map((paragraph, index) => (
102-
<Box key={index} mb={2}>
103-
{paragraph.trim() ? (
104-
<MessageText
105-
text={paragraph}
106-
workspaceUuid={workspaceUuid ?? ''}
107-
resolveMentions={true}
108-
fallbackToSimpleFormat={true}
109-
/>
110-
) : (
111-
<Box height="0.7em" />
112-
)}
113-
</Box>
114-
))}
115-
</Box>
116-
)
117-
}
118-
119-
if (
120-
cleanedText.includes('{') &&
121-
cleanedText.includes('}') &&
122-
cleanedText.includes('"')
123-
) {
124-
try {
125-
const contentMatch = cleanedText.match(/"[^"]+"\s*:\s*"([^"]*)"/)
126-
if (contentMatch && contentMatch[1]) {
127-
cleanedText = contentMatch[1].replace(/\\n/g, '\n')
128-
} else {
129-
cleanedText = cleanedText
130-
.replace(/[{}"]/g, '') // Remove braces and quotes
131-
.replace(/[\w_]+\s*:/g, '') // Remove field names
132-
.replace(/,\s*/g, '\n') // Replace commas with newlines
133-
.trim()
134-
}
135-
} catch (e) {
136-
console.warn('Error cleaning text content:', e)
137-
}
138-
}
139-
140-
const hasMarkdownHeaders = /^#+\s+.+$/m.test(cleanedText)
141-
142-
return (
143-
<Box className="formatted-text">
144-
{cleanedText.split('\n').map((paragraph, index) => {
145-
if (!paragraph.trim()) {
146-
return <Box key={index} height="0.7em" />
147-
}
148-
149-
if (hasMarkdownHeaders && /^(#+)\s+(.+)$/.test(paragraph)) {
150-
const match = paragraph.match(/^(#+)\s+(.+)$/)
151-
if (match) {
152-
const level = match[1].length
153-
const headerText = match[2]
154-
155-
const isTabHeader = [
156-
'Summary',
157-
'Topics',
158-
'Contributors',
159-
'Highlights',
160-
].some((tab) =>
161-
headerText.toLowerCase().includes(tab.toLowerCase())
162-
)
163-
164-
if (isTabHeader) {
165-
return <Box key={index} height="0.5em" /> // Skip this header
166-
}
167-
168-
const size = level === 1 ? 'lg' : level === 2 ? 'md' : 'sm'
169-
return (
170-
<Heading
171-
as={`h${Math.min(level, 6)}` as React.ElementType}
172-
size={size}
173-
mt={4}
174-
mb={2}
175-
key={index}
176-
>
177-
{headerText}
178-
</Heading>
179-
)
180-
}
181-
}
182-
183-
if (
184-
paragraph.trim().startsWith('- ') ||
185-
paragraph.trim().startsWith('* ')
186-
) {
187-
return (
188-
<Box key={index} mb={2} pl={4} display="flex">
189-
<Box as="span" mr={2}>
190-
191-
</Box>
192-
<Box flex="1">
193-
<MessageText
194-
text={paragraph.trim().substring(2)}
195-
workspaceUuid={workspaceUuid ?? ''}
196-
resolveMentions={true}
197-
fallbackToSimpleFormat={true}
198-
/>
199-
</Box>
200-
</Box>
201-
)
202-
}
203-
204-
return (
205-
<Box key={index} mb={2}>
206-
<MessageText
207-
text={paragraph}
208-
workspaceUuid={workspaceUuid ?? ''}
209-
resolveMentions={true}
210-
fallbackToSimpleFormat={true}
211-
/>
212-
</Box>
213-
)
214-
})}
215-
</Box>
216-
)
217-
}
21876

21977
const filteredAnalyses =
22078
reportResult?.resource_analyses &&
@@ -490,153 +348,7 @@ Generated using Toban Contribution Viewer with ${modelUsed}
490348
})
491349
}
492350

493-
/**
494-
* Render plain text with proper formatting and support for markdown-like syntax
495-
*/
496-
const renderPlainText = (text: string | unknown, workspace_uuid: string) => {
497-
const textStr = typeof text === 'string' ? text : String(text || '')
498-
if (!textStr || textStr.trim().length === 0) {
499-
return <Text color="gray.500">No content available</Text>
500-
}
501351

502-
let cleanedText = textStr
503-
504-
if (/^\s*\{\s*\}\s*$/.test(cleanedText)) {
505-
return <Text color="gray.500">No content available</Text>
506-
}
507-
508-
cleanedText = cleanedText.replace(/\\n/g, '\n')
509-
510-
const isLikelyPlainText =
511-
/^[A-Za-z]/.test(cleanedText.trim()) &&
512-
!cleanedText.includes('```json') &&
513-
!(cleanedText.trim().startsWith('{') && cleanedText.trim().endsWith('}'))
514-
515-
if (isLikelyPlainText) {
516-
console.log('Content appears to be plain text, rendering directly')
517-
return (
518-
<Box className="formatted-text">
519-
{cleanedText.split('\n').map((paragraph, index) => (
520-
<Box key={index} mb={2}>
521-
{paragraph.trim() ? (
522-
<MessageText
523-
text={paragraph}
524-
resolveMentions={true}
525-
fallbackToSimpleFormat={true}
526-
workspaceUuid={workspace_uuid ?? ''}
527-
/>
528-
) : (
529-
<Box height="0.7em" />
530-
)}
531-
</Box>
532-
))}
533-
</Box>
534-
)
535-
}
536-
537-
if (
538-
cleanedText.includes('{') &&
539-
cleanedText.includes('}') &&
540-
cleanedText.includes('"')
541-
) {
542-
try {
543-
const contentMatch = cleanedText.match(/"[^"]+"\s*:\s*"([^"]*)"/)
544-
if (contentMatch && contentMatch[1]) {
545-
cleanedText = contentMatch[1].replace(/\\n/g, '\n')
546-
} else {
547-
cleanedText = cleanedText
548-
.replace(/[{}"]/g, '') // Remove braces and quotes
549-
.replace(/[\w_]+\s*:/g, '') // Remove field names
550-
.replace(/,\s*/g, '\n') // Replace commas with newlines
551-
.trim()
552-
}
553-
} catch (e) {
554-
console.warn('Error cleaning text content:', e)
555-
}
556-
}
557-
558-
const hasMarkdownHeaders = /^#+\s+.+$/m.test(cleanedText)
559-
560-
return (
561-
<Box className="formatted-text">
562-
{cleanedText.split('\n').map((paragraph, index) => {
563-
if (!paragraph.trim()) {
564-
return <Box key={index} height="0.7em" />
565-
}
566-
567-
if (hasMarkdownHeaders && /^(#+)\s+(.+)$/.test(paragraph)) {
568-
const match = paragraph.match(/^(#+)\s+(.+)$/)
569-
if (match) {
570-
const level = match[1].length
571-
const headerText = match[2]
572-
573-
const isTabHeader = [
574-
'Summary',
575-
'Topics',
576-
'Contributors',
577-
'Highlights',
578-
].some((tab) =>
579-
headerText.toLowerCase().includes(tab.toLowerCase())
580-
)
581-
582-
if (isTabHeader) {
583-
return <Box key={index} height="0.5em" /> // Skip this header
584-
}
585-
586-
const size = level === 1 ? 'lg' : level === 2 ? 'md' : 'sm'
587-
return (
588-
<Heading
589-
as={`h${Math.min(level, 6)}` as React.ElementType}
590-
size={size}
591-
mt={4}
592-
mb={2}
593-
key={index}
594-
>
595-
{headerText}
596-
</Heading>
597-
)
598-
}
599-
}
600-
601-
if (
602-
paragraph.trim().startsWith('- ') ||
603-
paragraph.trim().startsWith('* ')
604-
) {
605-
return (
606-
<Box key={index} mb={2} pl={4} display="flex">
607-
<Box as="span" mr={2}>
608-
609-
</Box>
610-
<Box flex="1">
611-
<MessageText
612-
text={paragraph.trim().substring(2)}
613-
resolveMentions={true}
614-
fallbackToSimpleFormat={true}
615-
workspaceUuid={workspace_uuid ?? ''}
616-
/>
617-
</Box>
618-
</Box>
619-
)
620-
}
621-
622-
return (
623-
<Box key={index} mb={2}>
624-
<MessageText
625-
text={paragraph}
626-
resolveMentions={true}
627-
fallbackToSimpleFormat={true}
628-
workspaceUuid={workspace_uuid ?? ''}
629-
/>
630-
</Box>
631-
)
632-
})}
633-
</Box>
634-
)
635-
}
636-
637-
const isObviouslyNotJson = (str: string) => {
638-
return !str.includes('{') && !str.includes('"') && !str.includes(':')
639-
}
640352

641353
const fixedAnalysis = analysis
642354
? {
@@ -667,14 +379,6 @@ Generated using Toban Contribution Viewer with ${modelUsed}
667379
fixedAnalysis.contributor_insights
668380
}
669381

670-
const extractSectionContent = (text: string, sectionName: string) => {
671-
const regex = new RegExp(
672-
`#+\\s*${sectionName}\\s*\\n([\\s\\S]*?)(?=#+\\s*|$)`,
673-
'i'
674-
)
675-
const match = text.match(regex)
676-
return match ? match[1].trim() : ''
677-
}
678382

679383
if (analysis && fixedAnalysis) {
680384
if (

frontend/src/utils/textRenderer.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)