11import React , { useEffect , useState , useCallback } from "react"
2+ import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react" // Import ProgressRing
3+ import { useExtensionState } from "../../../context/ExtensionStateContext"
24import LinkPreview from "./LinkPreview"
35import ImagePreview from "./ImagePreview"
46import styled from "styled-components"
@@ -28,6 +30,10 @@ const ResponseHeader = styled.div`
2830 text-overflow: ellipsis;
2931 margin-right: 8px;
3032 }
33+
34+ .header-icon {
35+ margin-right: 6px;
36+ }
3137`
3238
3339const ToggleSwitch = styled . div `
@@ -111,7 +117,9 @@ interface UrlMatch {
111117}
112118
113119const McpResponseDisplay : React . FC < McpResponseDisplayProps > = ( { responseText } ) => {
114- const [ isLoading , setIsLoading ] = useState ( true )
120+ const { mcpResponsesCollapsed } = useExtensionState ( ) // Get setting from context
121+ const [ isExpanded , setIsExpanded ] = useState ( ! mcpResponsesCollapsed ) // Initialize with context setting
122+ const [ isLoading , setIsLoading ] = useState ( false ) // Initial loading state for rich content
115123 const [ displayMode , setDisplayMode ] = useState < "rich" | "plain" > ( ( ) => {
116124 // Get saved preference from localStorage, default to 'rich'
117125 const savedMode = localStorage . getItem ( "mcpDisplayMode" )
@@ -124,41 +132,46 @@ const McpResponseDisplay: React.FC<McpResponseDisplayProps> = ({ responseText })
124132
125133 const toggleDisplayMode = useCallback ( ( ) => {
126134 const newMode = displayMode === "rich" ? "plain" : "rich"
127-
128135 // Force an immediate re-render
129136 setForceUpdateCounter ( ( prev ) => prev + 1 )
130-
131137 // Update display mode and save preference
132138 setDisplayMode ( newMode )
133139 localStorage . setItem ( "mcpDisplayMode" , newMode )
134-
135140 // If switching to plain mode, cancel any ongoing processing
136141 if ( newMode === "plain" ) {
137142 console . log ( "Switching to plain mode - cancelling URL processing" )
138143 setUrlMatches ( [ ] ) // Clear any existing matches when switching to plain mode
139144 } else {
140145 // If switching to rich mode, the useEffect will re-run and fetch data
141146 console . log ( "Switching to rich mode - will start URL processing" )
147+ setUrlMatches ( [ ] )
142148 }
143149 } , [ displayMode ] )
144150
151+ const toggleExpand = useCallback ( ( ) => {
152+ setIsExpanded ( ( prev ) => ! prev )
153+ } , [ ] )
154+
155+ // Effect to update isExpanded if mcpResponsesCollapsed changes from context
156+ useEffect ( ( ) => {
157+ setIsExpanded ( ! mcpResponsesCollapsed )
158+ } , [ ] )
159+
145160 // Find all URLs in the text and determine if they're images
146161 useEffect ( ( ) => {
147162 // Skip all processing if in plain mode
148- if ( displayMode === "plain" ) {
163+ if ( ! isExpanded || displayMode === "plain" ) {
149164 setIsLoading ( false )
150165 setUrlMatches ( [ ] ) // Clear any existing matches when in plain mode
151166 return
152167 }
153168
154169 // Use a direct boolean for cancellation that's scoped to this effect run
155170 let processingCanceled = false
156-
157171 const processResponse = async ( ) => {
158172 console . log ( "Processing MCP response for URL extraction" )
159173 setIsLoading ( true )
160174 setError ( null )
161-
162175 try {
163176 const text = responseText || ""
164177 const matches : UrlMatch [ ] = [ ]
@@ -267,12 +280,24 @@ const McpResponseDisplay: React.FC<McpResponseDisplayProps> = ({ responseText })
267280 processingCanceled = true
268281 console . log ( "Cleaning up URL processing" )
269282 }
270- } , [ responseText , displayMode , forceUpdateCounter ] )
283+ } , [ responseText , displayMode , forceUpdateCounter , isExpanded ] )
271284
272285 // Function to render content based on display mode
273286 const renderContent = ( ) => {
287+ if ( ! isExpanded ) {
288+ return null // Don't render content if not expanded
289+ }
290+
291+ if ( isLoading && displayMode === "rich" ) {
292+ return (
293+ < div style = { { display : "flex" , justifyContent : "center" , alignItems : "center" , height : "50px" } } >
294+ < VSCodeProgressRing />
295+ </ div >
296+ )
297+ }
298+
274299 // For plain text mode, just show the text
275- if ( displayMode === "plain" || isLoading ) {
300+ if ( displayMode === "plain" ) {
276301 return < UrlText > { responseText } </ UrlText >
277302 }
278303
@@ -287,7 +312,7 @@ const McpResponseDisplay: React.FC<McpResponseDisplayProps> = ({ responseText })
287312 }
288313
289314 // For rich display mode, show the text with embedded content
290- if ( ! isLoading ) {
315+ if ( displayMode === "rich" ) {
291316 // We already know displayMode is "rich" if we get here
292317 // Create an array of text segments and embedded content
293318 const segments : JSX . Element [ ] = [ ]
@@ -385,30 +410,48 @@ const McpResponseDisplay: React.FC<McpResponseDisplayProps> = ({ responseText })
385410 try {
386411 return (
387412 < ResponseContainer >
388- < ResponseHeader >
389- < span className = "header-title" > Response</ span >
390- < ToggleSwitch >
391- < span className = "toggle-label" > { displayMode === "rich" ? "Rich Display" : "Plain Text" } </ span >
392- < div className = { `toggle-container ${ displayMode === "rich" ? "active" : "" } ` } onClick = { toggleDisplayMode } >
393- < div className = "toggle-handle" > </ div >
394- </ div >
395- </ ToggleSwitch >
413+ < ResponseHeader
414+ onClick = { toggleExpand }
415+ style = { {
416+ borderBottom : isExpanded ? "1px dashed var(--vscode-editorGroup-border)" : "none" ,
417+ marginBottom : isExpanded ? "8px" : "0px" ,
418+ } } >
419+ < div className = "header-title" >
420+ < span className = { `codicon codicon-chevron-${ isExpanded ? "down" : "right" } header-icon` } > </ span >
421+ Response
422+ </ div >
423+ < div style = { { minWidth : isExpanded ? "auto" : "0" , visibility : isExpanded ? "visible" : "hidden" } } >
424+ < ToggleSwitch onClick = { ( e ) => e . stopPropagation ( ) } >
425+ < span className = "toggle-label" > { displayMode === "rich" ? "Rich Display" : "Plain Text" } </ span >
426+ < div
427+ className = { `toggle-container ${ displayMode === "rich" ? "active" : "" } ` }
428+ onClick = { toggleDisplayMode } >
429+ < div className = "toggle-handle" > </ div >
430+ </ div >
431+ </ ToggleSwitch >
432+ </ div >
396433 </ ResponseHeader >
397434
398- < div className = "response-content" > { renderContent ( ) } </ div >
435+ { isExpanded && < div className = "response-content" > { renderContent ( ) } </ div > }
399436 </ ResponseContainer >
400437 )
401438 } catch ( error ) {
402- console . log ( "Error rendering MCP response - falling back to plain text" )
439+ console . log ( "Error rendering MCP response - falling back to plain text" ) // Restored comment
440+ // Fallback for critical rendering errors
403441 return (
404442 < ResponseContainer >
405- < ResponseHeader >
406- < span className = "header-title" > Response</ span >
443+ < ResponseHeader onClick = { toggleExpand } >
444+ < div className = "header-title" >
445+ < span className = { `codicon codicon-chevron-${ isExpanded ? "down" : "right" } header-icon` } > </ span >
446+ Response (Error)
447+ </ div >
407448 </ ResponseHeader >
408- < div className = "response-content" >
409- < div > Error parsing response:</ div >
410- < UrlText > { responseText } </ UrlText >
411- </ div >
449+ { isExpanded && (
450+ < div className = "response-content" >
451+ < div style = { { color : "var(--vscode-errorForeground)" } } > Error parsing response:</ div >
452+ < UrlText > { responseText } </ UrlText >
453+ </ div >
454+ ) }
412455 </ ResponseContainer >
413456 )
414457 }
0 commit comments