UNPKG

giga-code

Version:

A personal AI CLI assistant powered by Grok for local development.

164 lines 9.77 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ChatHistory = void 0; const react_1 = __importDefault(require("react")); const ink_1 = require("ink"); const diff_renderer_1 = require("./diff-renderer"); const markdown_renderer_1 = require("../utils/markdown-renderer"); function ChatHistory({ entries }) { const renderDiff = (diffContent, filename) => { return (react_1.default.createElement(diff_renderer_1.DiffRenderer, { diffContent: diffContent, filename: filename, terminalWidth: 80 })); }; const renderFileContent = (content) => { 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 (react_1.default.createElement(ink_1.Text, { key: index, color: "gray" }, displayContent)); }); }; const renderChatEntry = (entry, index) => { switch (entry.type) { case "user": return (react_1.default.createElement(ink_1.Box, { key: index, flexDirection: "column", marginTop: 1 }, react_1.default.createElement(ink_1.Box, null, react_1.default.createElement(ink_1.Text, { color: "gray" }, ">", " ", entry.content)))); case "assistant": return (react_1.default.createElement(ink_1.Box, { key: index, flexDirection: "column", marginTop: 1 }, react_1.default.createElement(ink_1.Box, { flexDirection: "row", alignItems: "flex-start" }, react_1.default.createElement(ink_1.Text, { color: "white" }, "\u23FA "), react_1.default.createElement(ink_1.Box, { flexDirection: "column", flexGrow: 1 }, entry.toolCalls ? ( // If there are tool calls, just show plain text react_1.default.createElement(ink_1.Text, { color: "white" }, entry.content.trim())) : ( // If no tool calls, render as markdown react_1.default.createElement(markdown_renderer_1.MarkdownRenderer, { content: entry.content.trim() })), entry.isStreaming && react_1.default.createElement(ink_1.Text, { color: "cyan" }, "\u2588"), entry.metrics && !entry.isStreaming && (react_1.default.createElement(ink_1.Box, { marginTop: 1 }, react_1.default.createElement(ink_1.Text, { color: "blue" }, "prefill - ", entry.metrics.prefillTimeMs, "ms"), react_1.default.createElement(ink_1.Text, { color: "white" }, " | "), react_1.default.createElement(ink_1.Text, { color: "yellow" }, "decode - ", entry.metrics.tokensPerSecond, " toks/sec (", entry.metrics.outputTokens, " out / ", entry.metrics.decodeTimeMs, "ms)"))))))); case "tool_result": const getToolActionName = (toolName) => { switch (toolName) { case "view_file": return "Read"; case "str_replace_editor": return "Update"; case "create_file": return "Create"; case "bash": return "Bash"; case "create_todo_list": return "Created Todo"; case "update_todo_list": return "Updated Todo"; case "call_mcp_tool": return "MCP"; default: // Handle dynamic MCP tools (mcp_{serverName}_{toolName}) if (toolName.startsWith("mcp_")) { const parts = toolName.split("_"); if (parts.length >= 3) { const serverName = parts[1]; const actualToolName = parts.slice(2).join("_"); return `MCP:${serverName}`; } } return "Tool"; } }; const getToolFilePath = (toolCall) => { if (toolCall?.function?.arguments) { try { const args = JSON.parse(toolCall.function.arguments); // Handle todo tools and search tools specially - they don't have file paths if (toolCall.function.name === "create_todo_list" || toolCall.function.name === "update_todo_list") { return ""; } // Handle MCP tools if (toolCall.function.name === "call_mcp_tool") { return args.tool_name || "unknown_tool"; } // Handle dynamic MCP tools (mcp_{serverName}_{toolName}) if (toolCall.function.name.startsWith("mcp_")) { const parts = toolCall.function.name.split("_"); if (parts.length >= 3) { const actualToolName = parts.slice(2).join("_"); // Try to get a meaningful parameter for display const displayParam = args.libraryName || args.library || args.query || args.path || args.url || actualToolName; return displayParam; } } return args.path || args.file_path || args.command || "unknown"; } catch { return "unknown"; } } return "unknown"; }; const toolName = entry.toolCall?.function?.name || "unknown"; const actionName = getToolActionName(toolName); const filePath = getToolFilePath(entry.toolCall); const shouldShowDiff = toolName === "str_replace_editor" || toolName === "create_file"; const shouldShowFileContent = toolName === "view_file"; return (react_1.default.createElement(ink_1.Box, { key: index, flexDirection: "column", marginTop: 1 }, react_1.default.createElement(ink_1.Box, null, react_1.default.createElement(ink_1.Text, { color: "magenta" }, "\u23FA"), react_1.default.createElement(ink_1.Text, { color: "white" }, " ", filePath ? `${actionName}(${filePath})` : actionName)), react_1.default.createElement(ink_1.Box, { marginLeft: 2, flexDirection: "column" }, shouldShowFileContent ? (react_1.default.createElement(ink_1.Box, { flexDirection: "column" }, react_1.default.createElement(ink_1.Text, { color: "gray" }, "\u23BF File contents:"), react_1.default.createElement(ink_1.Box, { marginLeft: 2, flexDirection: "column" }, renderFileContent(entry.content)))) : shouldShowDiff ? ( // For diff results, show only the summary line, not the raw content react_1.default.createElement(ink_1.Text, { color: "gray" }, "\u23BF ", entry.content.split('\n')[0])) : (react_1.default.createElement(ink_1.Text, { color: "gray" }, "\u23BF ", truncateContent(entry.content)))), shouldShowDiff && (react_1.default.createElement(ink_1.Box, { marginLeft: 4, flexDirection: "column" }, renderDiff(entry.content, filePath))))); default: return null; } }; const truncateContent = (content, maxLines = 5) => { const lines = content.split('\n'); if (lines.length > maxLines) { return lines.slice(0, maxLines).join('\n') + `\n... (truncated ${lines.length - maxLines} more lines)`; } return content; }; return (react_1.default.createElement(ink_1.Box, { flexDirection: "column" }, entries.map(renderChatEntry))); } exports.ChatHistory = ChatHistory; //# sourceMappingURL=chat-history.js.map