BrowserNavigateTool.tsx

  1import React, { useState } from "react";
  2import { LLMContent } from "../types";
  3
  4interface BrowserNavigateToolProps {
  5  toolInput?: unknown; // { url: string }
  6  isRunning?: boolean;
  7  toolResult?: LLMContent[];
  8  hasError?: boolean;
  9  executionTime?: string;
 10}
 11
 12function BrowserNavigateTool({
 13  toolInput,
 14  isRunning,
 15  toolResult,
 16  hasError,
 17  executionTime,
 18}: BrowserNavigateToolProps) {
 19  const [isExpanded, setIsExpanded] = useState(false);
 20
 21  // Extract URL from toolInput
 22  const url =
 23    typeof toolInput === "object" &&
 24    toolInput !== null &&
 25    "url" in toolInput &&
 26    typeof toolInput.url === "string"
 27      ? toolInput.url
 28      : typeof toolInput === "string"
 29        ? toolInput
 30        : "";
 31
 32  // Extract output from toolResult
 33  const output =
 34    toolResult && toolResult.length > 0 && toolResult[0].Text ? toolResult[0].Text : "";
 35
 36  // Truncate URL for display
 37  const truncateUrl = (urlStr: string, maxLen: number = 300) => {
 38    if (urlStr.length <= maxLen) return urlStr;
 39    return urlStr.substring(0, maxLen) + "...";
 40  };
 41
 42  const displayUrl = truncateUrl(url);
 43  const isComplete = !isRunning && toolResult !== undefined;
 44
 45  return (
 46    <div className="tool" data-testid={isComplete ? "tool-call-completed" : "tool-call-running"}>
 47      <div className="tool-header" onClick={() => setIsExpanded(!isExpanded)}>
 48        <div className="tool-summary">
 49          <span className={`tool-emoji ${isRunning ? "running" : ""}`}>🌐</span>
 50          <span className="tool-command">{displayUrl}</span>
 51          {isComplete && hasError && <span className="tool-error"></span>}
 52          {isComplete && !hasError && <span className="tool-success"></span>}
 53        </div>
 54        <button
 55          className="tool-toggle"
 56          aria-label={isExpanded ? "Collapse" : "Expand"}
 57          aria-expanded={isExpanded}
 58        >
 59          <svg
 60            width="12"
 61            height="12"
 62            viewBox="0 0 12 12"
 63            fill="none"
 64            xmlns="http://www.w3.org/2000/svg"
 65            style={{
 66              transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)",
 67              transition: "transform 0.2s",
 68            }}
 69          >
 70            <path
 71              d="M4.5 3L7.5 6L4.5 9"
 72              stroke="currentColor"
 73              strokeWidth="1.5"
 74              strokeLinecap="round"
 75              strokeLinejoin="round"
 76            />
 77          </svg>
 78        </button>
 79      </div>
 80
 81      {isExpanded && (
 82        <div className="tool-details">
 83          <div className="tool-section">
 84            <div className="tool-label">URL:</div>
 85            <div className="tool-code">
 86              <a href={url} target="_blank" rel="noopener noreferrer">
 87                {url}
 88              </a>
 89            </div>
 90          </div>
 91
 92          {isComplete && output && (
 93            <div className="tool-section">
 94              <div className="tool-label">
 95                Output{hasError ? " (Error)" : ""}:
 96                {executionTime && <span className="tool-time">{executionTime}</span>}
 97              </div>
 98              <pre className={`tool-code ${hasError ? "error" : ""}`}>{output}</pre>
 99            </div>
100          )}
101        </div>
102      )}
103    </div>
104  );
105}
106
107export default BrowserNavigateTool;