diff --git a/package.json b/package.json
index c4bb5b1b..11208546 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@vibe-kit/grok-cli",
- "version": "0.0.8",
+ "version": "0.0.9",
"description": "An open-source AI agent that brings the power of Grok directly into your terminal.",
"main": "dist/index.js",
"bin": {
diff --git a/src/ui/components/chat-history.tsx b/src/ui/components/chat-history.tsx
index c1011c99..076be161 100644
--- a/src/ui/components/chat-history.tsx
+++ b/src/ui/components/chat-history.tsx
@@ -9,46 +9,44 @@ interface ChatHistoryProps {
isConfirmationActive?: boolean;
}
-export function ChatHistory({
- entries,
- isConfirmationActive = false,
-}: ChatHistoryProps) {
- const renderDiff = (diffContent: string, filename?: string) => {
- return (
-
- );
- };
-
- const renderFileContent = (content: string) => {
- const lines = content.split("\n");
-
- // Calculate minimum indentation like DiffRenderer does
- let baseIndentation = Infinity;
- for (const line of lines) {
- if (line.trim() === "") continue;
- const firstCharIndex = line.search(/\S/);
- const currentIndent = firstCharIndex === -1 ? 0 : firstCharIndex;
- baseIndentation = Math.min(baseIndentation, currentIndent);
- }
- if (!isFinite(baseIndentation)) {
- baseIndentation = 0;
- }
-
- return lines.map((line, index) => {
- const displayContent = line.substring(baseIndentation);
+// Memoized ChatEntry component to prevent unnecessary re-renders
+const MemoizedChatEntry = React.memo(
+ ({ entry, index }: { entry: ChatEntry; index: number }) => {
+ const renderDiff = (diffContent: string, filename?: string) => {
return (
-
- {displayContent}
-
+
);
- });
- };
+ };
+
+ const renderFileContent = (content: string) => {
+ const lines = content.split("\n");
+
+ // Calculate minimum indentation like DiffRenderer does
+ let baseIndentation = Infinity;
+ for (const line of lines) {
+ if (line.trim() === "") continue;
+ const firstCharIndex = line.search(/\S/);
+ const currentIndent = firstCharIndex === -1 ? 0 : firstCharIndex;
+ baseIndentation = Math.min(baseIndentation, currentIndent);
+ }
+ if (!isFinite(baseIndentation)) {
+ baseIndentation = 0;
+ }
+
+ return lines.map((line, index) => {
+ const displayContent = line.substring(baseIndentation);
+ return (
+
+ {displayContent}
+
+ );
+ });
+ };
- const renderChatEntry = (entry: ChatEntry, index: number) => {
switch (entry.type) {
case "user":
return (
@@ -103,39 +101,38 @@ export function ChatHistory({
}
};
- const getToolFilePath = (toolCall: any) => {
+ const toolName = entry.toolCall?.function?.name || "unknown";
+ const actionName = getToolActionName(toolName);
+
+ const getFilePath = (toolCall: any) => {
if (toolCall?.function?.arguments) {
try {
const args = JSON.parse(toolCall.function.arguments);
- // Handle todo tools specially - they don't have file paths
- if (
- toolCall.function.name === "create_todo_list" ||
- toolCall.function.name === "update_todo_list"
- ) {
- return "";
- }
-
- // Handle search tool specially - show the query
if (toolCall.function.name === "search") {
- return args.query || "unknown";
+ return args.query;
}
-
- return args.path || args.file_path || args.command || "unknown";
+ return args.path || args.file_path || args.command || "";
} catch {
- return "unknown";
+ return "";
}
}
- return "unknown";
+ return "";
};
- const toolName = entry.toolCall?.function?.name || "unknown";
- const actionName = getToolActionName(toolName);
- const filePath = getToolFilePath(entry.toolCall);
-
+ const filePath = getFilePath(entry.toolCall);
+ const isExecuting = entry.type === "tool_call" || !entry.toolResult;
const shouldShowDiff =
- toolName === "str_replace_editor" || toolName === "create_file";
- const shouldShowFileContent = toolName === "view_file";
- const isExecuting = entry.type === "tool_call";
+ entry.toolCall?.function?.name === "str_replace_editor" &&
+ entry.toolResult?.success &&
+ entry.content.includes("Updated") &&
+ entry.content.includes("---") &&
+ entry.content.includes("+++");
+
+ const shouldShowFileContent =
+ (entry.toolCall?.function?.name === "view_file" ||
+ entry.toolCall?.function?.name === "create_file") &&
+ entry.toolResult?.success &&
+ !shouldShowDiff;
return (
@@ -174,8 +171,15 @@ export function ChatHistory({
default:
return null;
}
- };
+ }
+);
+MemoizedChatEntry.displayName = "MemoizedChatEntry";
+
+export function ChatHistory({
+ entries,
+ isConfirmationActive = false,
+}: ChatHistoryProps) {
// Filter out tool_call entries with "Executing..." when confirmation is active
const filteredEntries = isConfirmationActive
? entries.filter(
@@ -186,7 +190,13 @@ export function ChatHistory({
return (
- {filteredEntries.slice(-20).map(renderChatEntry)}
+ {filteredEntries.slice(-20).map((entry, index) => (
+
+ ))}
);
}
diff --git a/src/ui/components/chat-interface.tsx b/src/ui/components/chat-interface.tsx
index 52ee3c56..527bb6e5 100644
--- a/src/ui/components/chat-interface.tsx
+++ b/src/ui/components/chat-interface.tsx
@@ -57,7 +57,16 @@ function ChatInterfaceWithAgent({ agent }: { agent: GrokAgent }) {
});
useEffect(() => {
- console.clear();
+ // Only clear console on non-Windows platforms or if not PowerShell
+ // Windows PowerShell can have issues with console.clear() causing flickering
+ const isWindows = process.platform === "win32";
+ const isPowerShell =
+ process.env.ComSpec?.toLowerCase().includes("powershell") ||
+ process.env.PSModulePath !== undefined;
+
+ if (!isWindows || !isPowerShell) {
+ console.clear();
+ }
// Add top padding
console.log(" ");
diff --git a/src/ui/components/loading-spinner.tsx b/src/ui/components/loading-spinner.tsx
index 8baa8049..9b341519 100644
--- a/src/ui/components/loading-spinner.tsx
+++ b/src/ui/components/loading-spinner.tsx
@@ -25,7 +25,11 @@ const loadingTexts = [
"Downloading...",
];
-export function LoadingSpinner({ isActive, processingTime, tokenCount }: LoadingSpinnerProps) {
+export function LoadingSpinner({
+ isActive,
+ processingTime,
+ tokenCount,
+}: LoadingSpinnerProps) {
const [spinnerFrame, setSpinnerFrame] = useState(0);
const [loadingTextIndex, setLoadingTextIndex] = useState(0);
@@ -33,9 +37,10 @@ export function LoadingSpinner({ isActive, processingTime, tokenCount }: Loading
if (!isActive) return;
const spinnerFrames = ["/", "-", "\\", "|"];
+ // Reduced frequency: 500ms instead of 250ms to reduce flickering on Windows
const interval = setInterval(() => {
setSpinnerFrame((prev) => (prev + 1) % spinnerFrames.length);
- }, 250);
+ }, 500);
return () => clearInterval(interval);
}, [isActive]);
@@ -45,9 +50,10 @@ export function LoadingSpinner({ isActive, processingTime, tokenCount }: Loading
setLoadingTextIndex(Math.floor(Math.random() * loadingTexts.length));
+ // Increased interval: 4s instead of 2s to reduce state changes
const interval = setInterval(() => {
setLoadingTextIndex(Math.floor(Math.random() * loadingTexts.length));
- }, 2000);
+ }, 4000);
return () => clearInterval(interval);
}, [isActive]);
@@ -62,8 +68,9 @@ export function LoadingSpinner({ isActive, processingTime, tokenCount }: Loading
{spinnerFrames[spinnerFrame]} {loadingTexts[loadingTextIndex]}{" "}
- ({processingTime}s · ↑ {formatTokenCount(tokenCount)} tokens · esc to interrupt)
+ ({processingTime}s · ↑ {formatTokenCount(tokenCount)} tokens · esc to
+ interrupt)
);
-}
\ No newline at end of file
+}
diff --git a/test.py b/test.py
new file mode 100644
index 00000000..c18c0369
--- /dev/null
+++ b/test.py
@@ -0,0 +1,6 @@
+def dummy_function():
+ """A dummy function for testing purposes."""
+ return "Hello from dummy function!"
+
+if __name__ == "__main__":
+ print(dummy_function())
\ No newline at end of file