diff --git a/package-lock.json b/package-lock.json index e8e7c03..74f3ca3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -147,7 +147,6 @@ "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -590,7 +589,6 @@ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.1.tgz", "integrity": "sha512-RmTOkE7hRU3OVREqFVITWHz6ocgBjv08GoePscAakgVQfciA3SGCEk7mb9IzwW61cKKmlTpHXG6DUE5Ubx+MGQ==", "license": "MIT", - "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", @@ -2200,7 +2198,6 @@ "integrity": "sha512-jOT8V1Ba5BdC79sKrRWDdMT5l1R+XNHTPR6CPWzUP2EcfAcvIHZWF0eAbmRcpOOP5gVIwnqNg0C4nvh6Abc3OA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.1", @@ -2916,7 +2913,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -3052,8 +3048,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/abbrev": { "version": "2.0.0", @@ -3548,7 +3543,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -8454,7 +8448,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -8827,7 +8820,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -8840,7 +8832,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -10676,7 +10667,6 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -10920,7 +10910,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -11314,7 +11303,6 @@ "integrity": "sha512-oBXvfSHEOL8jF+R9Am7h59Up07kVVGH1NrFGFoEG6bPDZP3tGpQhvkBpy5x7U6+E6wZCu9OihsWgJqDbQIm8LQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -11408,7 +11396,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, diff --git a/src/components/ChatInterface.jsx b/src/components/ChatInterface.jsx index 0767696..981ab7b 100644 --- a/src/components/ChatInterface.jsx +++ b/src/components/ChatInterface.jsx @@ -416,21 +416,76 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile
{message.isToolUse && !['Read', 'TodoWrite', 'TodoRead'].includes(message.toolName) ? ( -
-
-
-
- + (() => { + // Minimize Grep and Glob tools since they happen frequently + const isSearchTool = ['Grep', 'Glob'].includes(message.toolName); + + if (isSearchTool) { + return ( + <> +
+
+
+ + + + {message.toolName} + + {message.toolInput && (() => { + try { + const input = JSON.parse(message.toolInput); + return ( + + {input.pattern && pattern: {input.pattern}} + {input.path && in: {input.path}} + + ); + } catch (e) { + return null; + } + })()} +
+ {message.toolResult && ( + + results + + + + + )} +
+
+ + ); + } + + // Full display for other tools + return ( +
+ {/* Decorative gradient overlay */} +
+ +
+
+
+ + {/* Subtle pulse animation */} +
+
+
+ + {message.toolName} + + + {message.toolId} +
- - Using {message.toolName} - - - {message.toolId} -
{onShowSettings && ( -
-
-
+
+
+
- + Diff
@@ -558,11 +616,14 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile
{showRawParameters && ( -
- +
+ + + + View raw parameters -
+                                
                                   {message.toolInput}
                                 
@@ -575,11 +636,14 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile // Fall back to raw display if parsing fails } return ( -
- +
+ + + + View input parameters -
+                      
                         {message.toolInput}
                       
@@ -602,12 +666,15 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile if (input.file_path && input.content !== undefined) { return ( -
- - +
+ + - 📄 Creating new file: +
+ 📄 + Creating new file: +
-
-
-
+
+
+
- + New File
@@ -698,11 +765,14 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile
{showRawParameters && ( -
- +
+ + + + View raw parameters -
+                                  
                                     {message.toolInput}
                                   
@@ -722,21 +792,27 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile const input = JSON.parse(message.toolInput); if (input.todos && Array.isArray(input.todos)) { return ( -
- - +
+ + - Updating Todo List +
+ + Updating Todo List +
{showRawParameters && ( -
- +
+ + + + View raw parameters -
+                                  
                                     {message.toolInput}
                                   
@@ -755,42 +831,17 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile try { const input = JSON.parse(message.toolInput); return ( -
- - - - - Running command - -
-
-
- - - - Terminal -
-
- $ {input.command} -
-
- {input.description && ( -
- {input.description} -
- )} - {showRawParameters && ( -
- - View raw parameters - -
-                                  {message.toolInput}
-                                
-
- )} +
+
+ $ + {input.command}
-
+ {input.description && ( +
+ {input.description} +
+ )} +
); } catch (e) { // Fall back to regular display @@ -849,14 +900,14 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile // Regular tool input display for other tools return ( -
- - +
+ + View input parameters -
+                      
                         {message.toolInput}
                       
@@ -864,35 +915,57 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile })()} {/* Tool Result Section */} - {message.toolResult && ( -
-
-
{ + // Hide tool results for Edit/Write/Bash unless there's an error + const shouldHideResult = !message.toolResult.isError && + (message.toolName === 'Edit' || message.toolName === 'Write' || message.toolName === 'ApplyPatch' || message.toolName === 'Bash'); + + if (shouldHideResult) { + return null; + } + + return ( +
+ {/* Decorative gradient overlay */} +
+ +
+
- + {message.toolResult.isError ? ( - + ) : ( - + )}
- {message.toolResult.isError ? 'Tool Error' : 'Tool Result'}
- -
{(() => { const content = String(message.toolResult.content || ''); @@ -957,6 +1030,57 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile } } + // Special handling for Grep/Glob results with structured data + if ((message.toolName === 'Grep' || message.toolName === 'Glob') && message.toolResult?.toolUseResult) { + const toolData = message.toolResult.toolUseResult; + + // Handle files_with_matches mode or any tool result with filenames array + if (toolData.filenames && Array.isArray(toolData.filenames) && toolData.filenames.length > 0) { + return ( +
+
+ + Found {toolData.numFiles || toolData.filenames.length} {(toolData.numFiles === 1 || toolData.filenames.length === 1) ? 'file' : 'files'} + +
+
+ {toolData.filenames.map((filePath, index) => { + const fileName = filePath.split('/').pop(); + const dirPath = filePath.substring(0, filePath.lastIndexOf('/')); + + return ( +
{ + if (onFileOpen) { + onFileOpen(filePath); + } + }} + className="group flex items-center gap-2 px-2 py-1.5 rounded hover:bg-green-100/50 dark:hover:bg-green-800/20 cursor-pointer transition-colors" + > + + + +
+
+ {fileName} +
+
+ {dirPath} +
+
+ + + +
+ ); + })} +
+
+ ); + } + } + // Special handling for interactive prompts if (content.includes('Do you want to proceed?') && message.toolName === 'Bash') { const lines = content.split('\n'); @@ -1193,8 +1317,11 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile })()}
- )} + ); + })()}
+ ); + })() ) : message.isInteractivePrompt ? ( // Special handling for interactive prompts
@@ -1286,21 +1413,31 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile if (input.file_path) { const filename = input.file_path.split('/').pop(); return ( -
- 📖 Read{' '} - +
+
+ + + + Read + +
); } } catch (e) { return ( -
- 📖 Read file +
+
+ + + + Read file +
); } @@ -1312,9 +1449,12 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile const input = JSON.parse(message.toolInput); if (input.todos && Array.isArray(input.todos)) { return ( -
-
- 📝 Update todo list +
+
+ + + + Update todo list
@@ -1322,16 +1462,26 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile } } catch (e) { return ( -
- 📝 Update todo list +
+
+ + + + Update todo list +
); } })() ) : message.isToolUse && message.toolName === 'TodoRead' ? ( // Simple TodoRead tool indicator -
- 📋 Read todo list +
+
+ + + + Read todo list +
) : (
@@ -2328,7 +2478,9 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess toolResults.set(part.tool_use_id, { content: part.content, isError: part.is_error, - timestamp: new Date(msg.timestamp || Date.now()) + timestamp: new Date(msg.timestamp || Date.now()), + // Extract structured tool result data (e.g., for Grep, Glob) + toolUseResult: msg.toolUseResult || null }); } } @@ -2404,7 +2556,7 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess } else if (part.type === 'tool_use') { // Get the corresponding tool result const toolResult = toolResults.get(part.id); - + converted.push({ type: 'assistant', content: '', @@ -2412,7 +2564,11 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess isToolUse: true, toolName: part.name, toolInput: JSON.stringify(part.input), - toolResult: toolResult ? (typeof toolResult.content === 'string' ? toolResult.content : JSON.stringify(toolResult.content)) : null, + toolResult: toolResult ? { + content: typeof toolResult.content === 'string' ? toolResult.content : JSON.stringify(toolResult.content), + isError: toolResult.isError, + toolUseResult: toolResult.toolUseResult + } : null, toolError: toolResult?.isError || false, toolResultTimestamp: toolResult?.timestamp || new Date() }); @@ -4549,19 +4705,20 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess /> -
- {/* Hint text */} -
- {sendByCtrlEnter - ? "Ctrl+Enter to send (IME safe) • Shift+Enter for new line • Tab to change modes • Type / for slash commands" - : "Press Enter to send • Shift+Enter for new line • Tab to change modes • Type / for slash commands"} -
-
- {sendByCtrlEnter - ? "Ctrl+Enter to send (IME safe) • Tab for modes • @ for files • / for commands" - : "Enter to send • Tab for modes • @ for files • / for commands"} + + {/* Hint text inside input box at bottom */} +
+ {sendByCtrlEnter + ? "Ctrl+Enter to send • Shift+Enter for new line • Tab to change modes • / for slash commands" + : "Enter to send • Shift+Enter for new line • Tab to change modes • / for slash commands"} +
+
+ {sendByCtrlEnter + ? "Ctrl+Enter to send • Tab for modes • / for commands" + : "Enter to send • Tab for modes • / for commands"} +
diff --git a/src/index.css b/src/index.css index 06f3e7f..d47186a 100644 --- a/src/index.css +++ b/src/index.css @@ -825,4 +825,16 @@ background-color: rgb(31 41 55) !important; color: rgb(243 244 246) !important; } + + /* Tool details chevron animation */ + details[open] .details-chevron, + details[open] summary svg[class*="group-open"] { + transform: rotate(180deg); + } + + /* Smooth chevron transition */ + .details-chevron, + summary svg[class*="transition-transform"] { + transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1); + } }