diff --git a/frontend/src/components/thread/tool-call-side-panel.tsx b/frontend/src/components/thread/tool-call-side-panel.tsx index 651f6e56..ceac7070 100644 --- a/frontend/src/components/thread/tool-call-side-panel.tsx +++ b/frontend/src/components/thread/tool-call-side-panel.tsx @@ -17,6 +17,7 @@ import { BrowserToolView } from "./tool-views/BrowserToolView"; import { WebSearchToolView } from "./tool-views/WebSearchToolView"; import { WebCrawlToolView } from "./tool-views/WebCrawlToolView"; import { DataProviderToolView } from "./tool-views/DataProviderToolView"; +import { ExposePortToolView } from "./tool-views/ExposePortToolView"; // Simple input interface export interface ToolCallInput { @@ -73,6 +74,17 @@ function getToolView( isSuccess={isSuccess} /> ); + case 'expose-port': + return ( + + ); case 'create-file': case 'full-file-rewrite': case 'delete-file': diff --git a/frontend/src/components/thread/tool-views/ExposePortToolView.tsx b/frontend/src/components/thread/tool-views/ExposePortToolView.tsx new file mode 100644 index 00000000..4f5d251e --- /dev/null +++ b/frontend/src/components/thread/tool-views/ExposePortToolView.tsx @@ -0,0 +1,194 @@ +import React from "react"; +import { ToolViewProps } from "./types"; +import { formatTimestamp } from "./utils"; +import { ExternalLink, CheckCircle, AlertTriangle } from "lucide-react"; +import { Markdown } from "@/components/ui/markdown"; +import { cn } from "@/lib/utils"; + +export function ExposePortToolView({ + name = 'expose-port', + assistantContent, + toolContent, + isSuccess = true, + isStreaming = false, + assistantTimestamp, + toolTimestamp +}: ToolViewProps) { + console.log('ExposePortToolView:', { + name, + assistantContent, + toolContent, + isSuccess, + isStreaming, + assistantTimestamp, + toolTimestamp + }); + + // Parse the assistant content + const parsedAssistantContent = React.useMemo(() => { + if (!assistantContent) return null; + try { + const parsed = JSON.parse(assistantContent); + return parsed.content; + } catch (e) { + console.error('Failed to parse assistant content:', e); + return null; + } + }, [assistantContent]); + + // Parse the tool result + const toolResult = React.useMemo(() => { + if (!toolContent) return null; + try { + // First parse the outer JSON + const parsed = JSON.parse(toolContent); + // Then extract the tool result content + const match = parsed.content.match(/output='(.*?)'/); + if (match) { + const jsonStr = match[1] + .replace(/\\n/g, '') + .replace(/\\"/g, '"'); + return JSON.parse(jsonStr); + } + return null; + } catch (e) { + console.error('Failed to parse tool content:', e); + return null; + } + }, [toolContent]); + + // Extract port number from assistant content + const portNumber = React.useMemo(() => { + if (!parsedAssistantContent) return null; + try { + const match = parsedAssistantContent.match(/\s*(\d+)\s*<\/expose-port>/); + return match ? match[1] : null; + } catch (e) { + console.error('Failed to extract port number:', e); + return null; + } + }, [parsedAssistantContent]); + + // If we have no content to show, render a placeholder + if (!portNumber && !toolResult && !isStreaming) { + return ( +
+
+ No port exposure information available +
+
+ ); + } + + return ( +
+
+ {/* Assistant Content */} + {portNumber && !isStreaming && ( +
+
+
Port to Expose
+ {assistantTimestamp && ( +
{formatTimestamp(assistantTimestamp)}
+ )} +
+
+
+
Port
+
+ {portNumber} +
+
+
+
+ )} + + {/* Tool Result */} + {toolResult && ( +
+
+
+ {isStreaming ? "Processing" : "Exposed URL"} +
+ {toolTimestamp && !isStreaming && ( +
{formatTimestamp(toolTimestamp)}
+ )} +
+
+ {isStreaming ? ( +
+ Exposing port {portNumber}... +
+ ) : ( +
+ +
+
Port
+
+ {toolResult.port} +
+
+
+ {toolResult.message} +
+
+ Note: This URL might only be temporarily available and could expire after some time. +
+
+ )} +
+
+ )} +
+ + {/* Footer */} +
+
+ {!isStreaming && ( +
+ {isSuccess ? ( + + ) : ( + + )} + + {isSuccess ? 'Port exposed successfully' : 'Failed to expose port'} + +
+ )} + + {isStreaming && ( +
+ Exposing port... +
+ )} + +
+ {toolTimestamp && !isStreaming + ? formatTimestamp(toolTimestamp) + : assistantTimestamp + ? formatTimestamp(assistantTimestamp) + : ''} +
+
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/thread/tool-views/GenericToolView.tsx b/frontend/src/components/thread/tool-views/GenericToolView.tsx index bf5e441a..03d095c9 100644 --- a/frontend/src/components/thread/tool-views/GenericToolView.tsx +++ b/frontend/src/components/thread/tool-views/GenericToolView.tsx @@ -59,7 +59,7 @@ export function GenericToolView({ )}
-
{formattedAssistantContent}
+ {formattedAssistantContent}
)} @@ -89,7 +89,7 @@ export function GenericToolView({ Executing {toolTitle.toLowerCase()}... ) : ( -
{formattedToolContent}
+ {formattedToolContent} )}