diff --git a/src/components/tools/BashToolCall.module.css b/src/components/tools/BashToolCall.module.css
new file mode 100644
index 0000000000..21aa67d860
--- /dev/null
+++ b/src/components/tools/BashToolCall.module.css
@@ -0,0 +1,59 @@
+.scriptPreview {
+ color: var(--color-text);
+ font-family: var(--font-monospace);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 400px;
+}
+
+.outputBlock {
+ margin: 0;
+ padding: 6px 8px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 3px;
+ border-left: 2px solid #4caf50;
+ font-size: 11px;
+ line-height: 1.4;
+ white-space: pre-wrap;
+ word-break: break-word;
+ max-height: 200px;
+ overflow-y: auto;
+}
+
+.exitCodeBadge {
+ display: inline-block;
+ padding: 2px 6px;
+ color: white;
+ border-radius: 3px;
+ font-size: 10px;
+ font-weight: 500;
+ margin-left: 8px;
+}
+
+.exitCodeBadge.success {
+ background: #4caf50;
+}
+
+.exitCodeBadge.failure {
+ background: #f44336;
+}
+
+.timeoutInfo {
+ color: var(--color-text-secondary);
+ font-size: 10px;
+ margin-left: 8px;
+}
+
+.timeoutInfo.active {
+ color: var(--color-pending);
+}
+
+.errorMessage {
+ color: #f44336;
+ font-size: 11px;
+ padding: 6px 8px;
+ background: rgba(244, 67, 54, 0.1);
+ border-radius: 3px;
+ border-left: 2px solid #f44336;
+}
diff --git a/src/components/tools/BashToolCall.tsx b/src/components/tools/BashToolCall.tsx
index c4815fa4ec..fbf8e641cc 100644
--- a/src/components/tools/BashToolCall.tsx
+++ b/src/components/tools/BashToolCall.tsx
@@ -1,5 +1,4 @@
import React, { useState, useEffect, useRef } from "react";
-import styled from "@emotion/styled";
import type { BashToolArgs, BashToolResult } from "@/types/tools";
import {
ToolContainer,
@@ -14,65 +13,7 @@ import {
LoadingDots,
} from "./shared/ToolPrimitives";
import { useToolExpansion, getStatusDisplay, type ToolStatus } from "./shared/toolUtils";
-
-// Bash-specific styled components
-
-const ScriptPreview = styled.span`
- color: var(--color-text);
- font-family: var(--font-monospace);
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 400px;
-`;
-
-const OutputBlock = styled.pre`
- margin: 0;
- padding: 6px 8px;
- background: rgba(0, 0, 0, 0.2);
- border-radius: 3px;
- border-left: 2px solid #4caf50;
- font-size: 11px;
- line-height: 1.4;
- white-space: pre-wrap;
- word-break: break-word;
- max-height: 200px;
- overflow-y: auto;
-`;
-
-const ExitCodeBadge = styled.span<{ exitCode: number }>`
- display: inline-block;
- padding: 2px 6px;
- background: ${(props) => (props.exitCode === 0 ? "#4caf50" : "#f44336")};
- color: white;
- border-radius: 3px;
- font-size: 10px;
- font-weight: 500;
- margin-left: 8px;
-`;
-
-const TimeoutInfo = styled.span<{ status?: ToolStatus }>`
- color: ${({ status }) => {
- switch (status) {
- case "executing":
- case "pending":
- return "var(--color-pending)";
- default:
- return "var(--color-text-secondary)";
- }
- }};
- font-size: 10px;
- margin-left: 8px;
-`;
-
-const ErrorMessage = styled.div`
- color: #f44336;
- font-size: 11px;
- padding: 6px 8px;
- background: rgba(244, 67, 54, 0.1);
- border-radius: 3px;
- border-left: 2px solid #f44336;
-`;
+import styles from "./BashToolCall.module.css";
interface BashToolCallProps {
args: BashToolArgs;
@@ -113,13 +54,19 @@ export const BashToolCall: React.FC
= ({ args, result, status
▶
bash
- {args.script}
-
+ {args.script}
+
timeout: {args.timeout_secs}s
{result && ` • took ${formatDuration(result.wall_duration_ms)}`}
{!result && isPending && elapsedTime > 0 && ` • ${formatDuration(elapsedTime)}`}
-
- {result && {result.exitCode}}
+
+ {result && (
+
+ {result.exitCode}
+
+ )}
{getStatusDisplay(status)}
@@ -135,14 +82,14 @@ export const BashToolCall: React.FC = ({ args, result, status
{result.success === false && result.error && (
Error
- {result.error}
+ {result.error}
)}
{result.output && (
Output
- {result.output}
+ {result.output}
)}
>
diff --git a/src/components/tools/FileEditToolCall.module.css b/src/components/tools/FileEditToolCall.module.css
new file mode 100644
index 0000000000..9eee16a0fb
--- /dev/null
+++ b/src/components/tools/FileEditToolCall.module.css
@@ -0,0 +1,82 @@
+.compactHeader {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 1;
+ font-size: 11px;
+ color: var(--color-text);
+}
+
+.editIcon {
+ font-size: 14px;
+}
+
+.filePath {
+ font-family: var(--font-monospace);
+ color: var(--color-text);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 400px;
+}
+
+.editCount {
+ color: var(--color-text-secondary);
+ font-size: 10px;
+ margin-left: 4px;
+}
+
+.tokenCount {
+ color: var(--color-text-secondary);
+ font-size: 10px;
+ margin-left: 4px;
+}
+
+.diffBlock {
+ margin: 0;
+ padding: 6px 8px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 3px;
+ border-left: 2px solid #ff9800;
+ font-size: 11px;
+ line-height: 1.4;
+ white-space: pre-wrap;
+ word-break: break-word;
+ max-height: 400px;
+ overflow-y: auto;
+}
+
+.errorMessage {
+ color: #f44336;
+ font-size: 11px;
+ padding: 6px 8px;
+ background: rgba(244, 67, 54, 0.1);
+ border-radius: 3px;
+ border-left: 2px solid #f44336;
+}
+
+.styledToolHeader {
+ cursor: default;
+}
+
+.styledToolHeader:hover {
+ color: var(--color-text-secondary);
+}
+
+.leftContent {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 1;
+ cursor: pointer;
+}
+
+.leftContent:hover {
+ color: var(--color-text);
+}
+
+.buttonGroup {
+ display: flex;
+ gap: 6px;
+ margin-right: 8px;
+}
diff --git a/src/components/tools/FileEditToolCall.tsx b/src/components/tools/FileEditToolCall.tsx
index 919cb20bed..6e9ac6f91c 100644
--- a/src/components/tools/FileEditToolCall.tsx
+++ b/src/components/tools/FileEditToolCall.tsx
@@ -195,7 +195,7 @@ interface FileEditToolCallProps {
function renderDiff(diff: string): React.ReactNode {
try {
const patches = parsePatch(diff);
- if (patches.length === 0) {
+ if (!patches || patches.length === 0) {
return (
No changes
@@ -203,57 +203,78 @@ function renderDiff(diff: string): React.ReactNode {
);
}
- return patches.map((patch, patchIdx) => (
-
- {patch.hunks.map((hunk, hunkIdx) => {
- let oldLineNum = hunk.oldStart;
- let newLineNum = hunk.newStart;
-
- return (
-
-
- {/* Empty for alignment */}
- {hunkIdx > 0 ? "⋮" : ""}
-
- @@ -{hunk.oldStart},{hunk.oldLines} +{hunk.newStart},{hunk.newLines} @@
-
-
- {hunk.lines.map((line, lineIdx) => {
- const firstChar = line[0];
- const content = line.slice(1); // Remove the +/- prefix
- let type: DiffLineType = "context";
- let lineNumDisplay = "";
-
- if (firstChar === "+") {
- type = "add";
- lineNumDisplay = `${newLineNum}`;
- newLineNum++;
- } else if (firstChar === "-") {
- type = "remove";
- lineNumDisplay = `${oldLineNum}`;
- oldLineNum++;
- } else {
- // Context line
- lineNumDisplay = `${oldLineNum}`;
- oldLineNum++;
- newLineNum++;
- }
-
- return (
-
- {firstChar}
- {lineNumDisplay}
- {content}
-
- );
- })}
-
- );
- })}
-
- ));
+ return patches.map((patch, patchIdx) => {
+ if (!patch?.hunks) {
+ return null;
+ }
+
+ return (
+
+ {patch.hunks.map((hunk, hunkIdx) => {
+ if (!hunk?.lines) {
+ return null;
+ }
+
+ let oldLineNum = hunk.oldStart ?? 0;
+ let newLineNum = hunk.newStart ?? 0;
+
+ return (
+
+
+ {/* Empty for alignment */}
+ {hunkIdx > 0 ? "⋮" : ""}
+
+ @@ -{hunk.oldStart ?? 0},{hunk.oldLines ?? 0} +{hunk.newStart ?? 0},
+ {hunk.newLines ?? 0} @@
+
+
+ {hunk.lines.map((line, lineIdx) => {
+ if (!line || typeof line !== "string") {
+ return null;
+ }
+
+ const firstChar = line[0];
+ const content = line.slice(1); // Remove the +/- prefix
+ let type: DiffLineType = "context";
+ let lineNumDisplay = "";
+
+ if (firstChar === "+") {
+ type = "add";
+ lineNumDisplay = `${newLineNum}`;
+ newLineNum++;
+ } else if (firstChar === "-") {
+ type = "remove";
+ lineNumDisplay = `${oldLineNum}`;
+ oldLineNum++;
+ } else {
+ // Context line
+ lineNumDisplay = `${oldLineNum}`;
+ oldLineNum++;
+ newLineNum++;
+ }
+
+ return (
+
+ {firstChar}
+ {lineNumDisplay}
+ {content}
+
+ );
+ })}
+
+ );
+ })}
+
+ );
+ });
} catch (error) {
- return Failed to parse diff: {String(error)};
+ console.error("Failed to parse diff:", error);
+ // Fallback to raw diff display
+ return (
+
+ {diff}
+
+ );
}
}
diff --git a/src/components/tools/FileReadToolCall.module.css b/src/components/tools/FileReadToolCall.module.css
new file mode 100644
index 0000000000..c56c14c391
--- /dev/null
+++ b/src/components/tools/FileReadToolCall.module.css
@@ -0,0 +1,84 @@
+.compactHeader {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 1;
+ font-size: 11px;
+ color: var(--color-text);
+}
+
+.searchIcon {
+ font-size: 14px;
+}
+
+.filePath {
+ font-family: var(--font-monospace);
+ color: var(--color-text);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 400px;
+}
+
+.tokenCount {
+ color: var(--color-text-secondary);
+ font-size: 10px;
+ margin-left: 4px;
+}
+
+.contentBlock {
+ margin: 0;
+ padding: 6px 8px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 3px;
+ border-left: 2px solid #2196f3;
+ font-size: 11px;
+ line-height: 1.4;
+ white-space: pre-wrap;
+ word-break: break-word;
+ max-height: 400px;
+ overflow-y: auto;
+}
+
+.metadataRow {
+ display: flex;
+ gap: 16px;
+ font-size: 10px;
+ color: var(--color-text-secondary);
+ padding: 4px 0;
+}
+
+.errorMessage {
+ color: #f44336;
+ font-size: 11px;
+ padding: 6px 8px;
+ background: rgba(244, 67, 54, 0.1);
+ border-radius: 3px;
+ border-left: 2px solid #f44336;
+}
+
+.styledToolHeader {
+ cursor: default;
+}
+
+.styledToolHeader:hover {
+ color: var(--color-text-secondary);
+}
+
+.leftContent {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 1;
+ cursor: pointer;
+}
+
+.leftContent:hover {
+ color: var(--color-text);
+}
+
+.buttonGroup {
+ display: flex;
+ gap: 6px;
+ margin-right: 8px;
+}
diff --git a/src/components/tools/FileReadToolCall.tsx b/src/components/tools/FileReadToolCall.tsx
new file mode 100644
index 0000000000..8e11bc1166
--- /dev/null
+++ b/src/components/tools/FileReadToolCall.tsx
@@ -0,0 +1,134 @@
+import React, { useState } from "react";
+import type { FileReadToolArgs, FileReadToolResult } from "@/types/tools";
+import {
+ ToolContainer,
+ ToolHeader,
+ ToolDetails,
+ DetailSection,
+ DetailLabel,
+ DetailContent,
+ LoadingDots,
+ HeaderButton,
+} from "./shared/ToolPrimitives";
+import { useToolExpansion, type ToolStatus } from "./shared/toolUtils";
+import styles from "./FileReadToolCall.module.css";
+
+interface FileReadToolCallProps {
+ args: FileReadToolArgs;
+ result?: FileReadToolResult;
+ status?: ToolStatus;
+}
+
+// Estimate token count (rough approximation: ~4 chars per token)
+function estimateTokens(content: string): number {
+ return Math.ceil(content.length / 4);
+}
+
+function formatFileSize(bytes: number): string {
+ if (bytes < 1024) return `${bytes}B`;
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
+}
+
+export const FileReadToolCall: React.FC = ({
+ args,
+ result,
+ status = "pending",
+}) => {
+ const { expanded, toggleExpanded } = useToolExpansion(false);
+ const [copied, setCopied] = useState(false);
+
+ const filePath = args.filePath;
+ const tokenCount = result?.success ? estimateTokens(result.content) : null;
+
+ const handleCopyContent = async () => {
+ if (result?.success) {
+ try {
+ await navigator.clipboard.writeText(result.content);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ } catch (err) {
+ console.error("Failed to copy:", err);
+ }
+ }
+ };
+
+ // Compact display when collapsed
+ if (!expanded) {
+ return (
+
+
+
+ 🔍
+ {filePath}
+ {tokenCount !== null && ~{tokenCount} tokens}
+
+
+
+ );
+ }
+
+ // Full display when expanded
+ return (
+
+
+
+ 🔍
+ {filePath}
+ {tokenCount !== null && ~{tokenCount} tokens}
+
+ {result && result.success && (
+
+ {
+ e.stopPropagation();
+ void handleCopyContent();
+ }}
+ >
+ {copied ? "✓ Copied" : "Copy Content"}
+
+
+ )}
+
+
+ {expanded && (
+
+ {result && (
+ <>
+ {result.success === false && result.error && (
+
+ Error
+ {result.error}
+
+ )}
+
+ {result.success && (
+ <>
+
+ Lines: {result.lines_read}
+ Size: {formatFileSize(result.file_size)}
+ Modified: {new Date(result.modifiedTime).toLocaleString()}
+
+
+
+ Content
+ {result.content}
+
+ >
+ )}
+ >
+ )}
+
+ {status === "executing" && !result && (
+
+
+ Reading file
+
+
+
+ )}
+
+ )}
+
+ );
+};
diff --git a/src/components/tools/ProposePlanToolCall.module.css b/src/components/tools/ProposePlanToolCall.module.css
new file mode 100644
index 0000000000..45692a0325
--- /dev/null
+++ b/src/components/tools/ProposePlanToolCall.module.css
@@ -0,0 +1,120 @@
+.planContainer {
+ padding: 12px;
+ background: linear-gradient(
+ 135deg,
+ color-mix(in srgb, var(--color-plan-mode), transparent 92%) 0%,
+ color-mix(in srgb, var(--color-plan-mode), transparent 95%) 100%
+ );
+ border-radius: 6px;
+ border: 1px solid color-mix(in srgb, var(--color-plan-mode), transparent 70%);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+}
+
+.planHeader {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 12px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid color-mix(in srgb, var(--color-plan-mode), transparent 80%);
+}
+
+.planHeaderLeft {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 1;
+}
+
+.planHeaderRight {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.planIcon {
+ font-size: 16px;
+}
+
+.planTitle {
+ font-size: 13px;
+ font-weight: 600;
+ color: var(--color-plan-mode);
+ font-family: var(--font-monospace);
+}
+
+.planButton {
+ padding: 4px 8px;
+ font-size: 10px;
+ font-family: var(--font-monospace);
+ color: #888;
+ background: transparent;
+ border: 1px solid rgba(136, 136, 136, 0.3);
+ border-radius: 3px;
+ cursor: pointer;
+ transition: all 0.15s ease;
+}
+
+.planButton.active {
+ color: var(--color-plan-mode);
+ background: color-mix(in srgb, var(--color-plan-mode), transparent 90%);
+ border-color: color-mix(in srgb, var(--color-plan-mode), transparent 70%);
+}
+
+.planButton:hover {
+ background: color-mix(in srgb, var(--color-plan-mode), transparent 85%);
+ color: var(--color-plan-mode);
+ border-color: color-mix(in srgb, var(--color-plan-mode), transparent 60%);
+}
+
+.rawContent {
+ font-family: var(--font-monospace);
+ font-size: 11px;
+ line-height: 1.6;
+ color: var(--color-text);
+ white-space: pre-wrap;
+ word-break: break-word;
+ margin: 0;
+ padding: 8px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 3px;
+}
+
+.planContent {
+ font-family: var(--font-primary);
+ font-size: 12px;
+ line-height: 1.6;
+}
+
+.guidanceText {
+ color: var(--color-text-secondary);
+ font-size: 11px;
+ font-style: italic;
+ margin: 8px 0;
+}
+
+.keybindDisplay {
+ font-family: var(--font-monospace);
+ font-weight: 600;
+}
+
+.planBlock {
+ margin: 0;
+ padding: 8px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 3px;
+ border-left: 2px solid #9c27b0;
+ font-size: 11px;
+ line-height: 1.6;
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
+.errorMessage {
+ color: #f44336;
+ font-size: 11px;
+ padding: 6px 8px;
+ background: rgba(244, 67, 54, 0.1);
+ border-radius: 3px;
+ border-left: 2px solid #f44336;
+}
diff --git a/src/components/tools/shared/ToolPrimitives.module.css b/src/components/tools/shared/ToolPrimitives.module.css
new file mode 100644
index 0000000000..cd408be213
--- /dev/null
+++ b/src/components/tools/shared/ToolPrimitives.module.css
@@ -0,0 +1,142 @@
+/**
+ * Shared CSS for tool UI primitives
+ * These styles provide consistent styling across all tool components
+ */
+
+.toolContainer {
+ margin: 8px 0;
+ padding: 4px 12px;
+ background: rgba(100, 100, 100, 0.05);
+ border-radius: 4px;
+ font-family: var(--font-monospace);
+ font-size: 11px;
+ transition: all 0.2s ease;
+}
+
+.toolContainer.expanded {
+ padding: 8px 12px;
+}
+
+.toolHeader {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ cursor: pointer;
+ user-select: none;
+ color: var(--color-text-secondary);
+}
+
+.toolHeader:hover {
+ color: var(--color-text);
+}
+
+.expandIcon {
+ display: inline-block;
+ transition: transform 0.2s ease;
+ transform: rotate(0deg);
+ font-size: 10px;
+}
+
+.expandIcon.expanded {
+ transform: rotate(90deg);
+}
+
+.toolName {
+ font-weight: 500;
+}
+
+.statusIndicator {
+ font-size: 10px;
+ margin-left: auto;
+ opacity: 0.8;
+}
+
+.statusIndicator.executing {
+ color: var(--color-pending);
+}
+
+.statusIndicator.completed {
+ color: #4caf50;
+}
+
+.statusIndicator.failed {
+ color: #f44336;
+}
+
+.statusIndicator.interrupted {
+ color: var(--color-interrupted);
+}
+
+.statusIndicator.pending {
+ color: var(--color-text-secondary);
+}
+
+.toolDetails {
+ margin-top: 8px;
+ padding-top: 8px;
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
+ color: var(--color-text);
+}
+
+.detailSection {
+ margin: 6px 0;
+}
+
+.detailLabel {
+ font-size: 10px;
+ color: var(--color-text-secondary);
+ margin-bottom: 4px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.detailContent {
+ margin: 0;
+ padding: 6px 8px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 3px;
+ font-size: 11px;
+ line-height: 1.4;
+ white-space: pre-wrap;
+ word-break: break-word;
+ max-height: 200px;
+ overflow-y: auto;
+}
+
+.loadingDots::after {
+ content: "...";
+ animation: dots 1.5s infinite;
+}
+
+@keyframes dots {
+ 0%, 20% {
+ content: ".";
+ }
+ 40% {
+ content: "..";
+ }
+ 60%, 100% {
+ content: "...";
+ }
+}
+
+.headerButton {
+ background: none;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ color: #cccccc;
+ padding: 2px 8px;
+ border-radius: 3px;
+ cursor: pointer;
+ font-size: 10px;
+ transition: all 0.2s ease;
+ white-space: nowrap;
+}
+
+.headerButton.active {
+ background: rgba(255, 255, 255, 0.1);
+}
+
+.headerButton:hover {
+ background: rgba(255, 255, 255, 0.1);
+ border-color: rgba(255, 255, 255, 0.3);
+}
diff --git a/src/components/tools/shared/ToolPrimitives.tsx b/src/components/tools/shared/ToolPrimitives.tsx
index 118add963a..7852c25da5 100644
--- a/src/components/tools/shared/ToolPrimitives.tsx
+++ b/src/components/tools/shared/ToolPrimitives.tsx
@@ -1,130 +1,99 @@
-import styled from "@emotion/styled";
+import React from "react";
+import styles from "./ToolPrimitives.module.css";
/**
- * Shared styled components for tool UI
+ * Shared components for tool UI
* These primitives provide consistent styling across all tool components
*/
-export const ToolContainer = styled.div<{ expanded: boolean }>`
- margin: 8px 0;
- padding: ${(props) => (props.expanded ? "8px 12px" : "4px 12px")};
- background: rgba(100, 100, 100, 0.05);
- border-radius: 4px;
- font-family: var(--font-monospace);
- font-size: 11px;
- transition: all 0.2s ease;
-`;
-
-export const ToolHeader = styled.div`
- display: flex;
- align-items: center;
- gap: 8px;
- cursor: pointer;
- user-select: none;
- color: var(--color-text-secondary);
-
- &:hover {
- color: var(--color-text);
- }
-`;
-
-export const ExpandIcon = styled.span<{ expanded: boolean }>`
- display: inline-block;
- transition: transform 0.2s ease;
- transform: ${(props) => (props.expanded ? "rotate(90deg)" : "rotate(0deg)")};
- font-size: 10px;
-`;
-
-export const ToolName = styled.span`
- font-weight: 500;
-`;
-
-export const StatusIndicator = styled.span<{ status: string }>`
- font-size: 10px;
- margin-left: auto;
- opacity: 0.8;
- color: ${({ status }) => {
- switch (status) {
- case "executing":
- return "var(--color-pending)";
- case "completed":
- return "#4caf50";
- case "failed":
- return "#f44336";
- case "interrupted":
- return "var(--color-interrupted)";
- default:
- return "var(--color-text-secondary)";
- }
- }};
-`;
-
-export const ToolDetails = styled.div`
- margin-top: 8px;
- padding-top: 8px;
- border-top: 1px solid rgba(255, 255, 255, 0.05);
- color: var(--color-text);
-`;
-
-export const DetailSection = styled.div`
- margin: 6px 0;
-`;
-
-export const DetailLabel = styled.div`
- font-size: 10px;
- color: var(--color-text-secondary);
- margin-bottom: 4px;
- text-transform: uppercase;
- letter-spacing: 0.5px;
-`;
-
-export const DetailContent = styled.pre`
- margin: 0;
- padding: 6px 8px;
- background: rgba(0, 0, 0, 0.2);
- border-radius: 3px;
- font-size: 11px;
- line-height: 1.4;
- white-space: pre-wrap;
- word-break: break-word;
- max-height: 200px;
- overflow-y: auto;
-`;
-
-export const LoadingDots = styled.span`
- &::after {
- content: "...";
- animation: dots 1.5s infinite;
- }
-
- @keyframes dots {
- 0%,
- 20% {
- content: ".";
- }
- 40% {
- content: "..";
- }
- 60%,
- 100% {
- content: "...";
- }
- }
-`;
-
-export const HeaderButton = styled.button<{ active?: boolean }>`
- background: ${(props) => (props.active ? "rgba(255, 255, 255, 0.1)" : "none")};
- border: 1px solid rgba(255, 255, 255, 0.2);
- color: #cccccc;
- padding: 2px 8px;
- border-radius: 3px;
- cursor: pointer;
- font-size: 10px;
- transition: all 0.2s ease;
- white-space: nowrap;
-
- &:hover {
- background: rgba(255, 255, 255, 0.1);
- border-color: rgba(255, 255, 255, 0.3);
- }
-`;
+interface ToolContainerProps {
+ expanded: boolean;
+ children: React.ReactNode;
+}
+
+export const ToolContainer: React.FC = ({ expanded, children }) => (
+ {children}
+);
+
+interface ToolHeaderProps {
+ onClick: () => void;
+ children: React.ReactNode;
+}
+
+export const ToolHeader: React.FC = ({ onClick, children }) => (
+
+ {children}
+
+);
+
+interface ExpandIconProps {
+ expanded: boolean;
+ children: React.ReactNode;
+}
+
+export const ExpandIcon: React.FC = ({ expanded, children }) => (
+ {children}
+);
+
+interface ToolNameProps {
+ children: React.ReactNode;
+}
+
+export const ToolName: React.FC = ({ children }) => (
+ {children}
+);
+
+interface StatusIndicatorProps {
+ status: string;
+ children: React.ReactNode;
+}
+
+export const StatusIndicator: React.FC = ({ status, children }) => (
+ {children}
+);
+
+interface ToolDetailsProps {
+ children: React.ReactNode;
+}
+
+export const ToolDetails: React.FC = ({ children }) => (
+ {children}
+);
+
+interface DetailSectionProps {
+ children: React.ReactNode;
+}
+
+export const DetailSection: React.FC = ({ children }) => (
+ {children}
+);
+
+interface DetailLabelProps {
+ children: React.ReactNode;
+}
+
+export const DetailLabel: React.FC = ({ children }) => (
+ {children}
+);
+
+interface DetailContentProps {
+ children: React.ReactNode;
+}
+
+export const DetailContent: React.FC = ({ children }) => (
+ {children}
+);
+
+export const LoadingDots: React.FC = () => ;
+
+interface HeaderButtonProps {
+ active?: boolean;
+ onClick?: () => void;
+ children: React.ReactNode;
+}
+
+export const HeaderButton: React.FC = ({ active, onClick, children }) => (
+
+);
diff --git a/tsconfig.json b/tsconfig.json
index 1a86bada21..d5d3064391 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -5,6 +5,7 @@
"module": "ESNext",
"moduleResolution": "node",
"jsx": "react-jsx",
+ "jsxImportSource": "@emotion/react",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
diff --git a/vite.config.ts b/vite.config.ts
index cb463253aa..7a71f755d1 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -8,7 +8,16 @@ import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export default defineConfig({
- plugins: [react(), wasm(), topLevelAwait()],
+ plugins: [
+ react({
+ jsxImportSource: '@emotion/react',
+ babel: {
+ plugins: ['@emotion/babel-plugin'],
+ },
+ }),
+ wasm(),
+ topLevelAwait(),
+ ],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),