diff --git a/CLAUDE.md b/CLAUDE.md index 91bfcb3..1fa7428 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -104,10 +104,11 @@ src/components/ **TypeScript Standards:** -- Enable strict mode - no `any` types allowed +- Enable strict mode - no `any` types allowed (linter will catch violations) - Use interfaces for all props and data structures - Prefer union types over enums for string constants - Use optional chaining (`?.`) and nullish coalescing (`??`) +- NEVER use `any` - always use proper types or `unknown` when type is truly unknown **Error Handling:** diff --git a/README.md b/README.md index 64b2349..d6521b5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![Claude Runner Logo](https://raw.githubusercontent.com/codingworkflow/claude-runner/main/assets/icon.png) -Create and run multi-step tasks in your VS Code. Create workflows & save them to reuse. +Chat, create and run multi-step tasks in your VS Code. Create workflows & save them to reuse. Get cost usage (estimate if you use subscription), and check conversation history. ## Key Features @@ -13,12 +13,15 @@ Get cost usage (estimate if you use subscription), and check conversation histor Create and execute sophisticated multi-step workflows: +- Chat directly in VScode - Chain multiple Claude Code tasks together - Mix different Claude models per task or keep in Auto mode - Session continuity between tasks - Save and reuse pipelines - Format similar to Claude Code GitHub action +![Chat](https://raw.githubusercontent.com/codingworkflow/claude-runner/main/assets/chatui.png) + ![Create Workflow](https://raw.githubusercontent.com/codingworkflow/claude-runner/main/assets/pipeline.png) ![Save Workflow](https://raw.githubusercontent.com/codingworkflow/claude-runner/main/assets/savepipeline.png) diff --git a/VERSION b/VERSION index 9fc80f9..60a2d3e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.2 \ No newline at end of file +0.4.0 \ No newline at end of file diff --git a/assets/chatui.png b/assets/chatui.png new file mode 100644 index 0000000..135f49b Binary files /dev/null and b/assets/chatui.png differ diff --git a/package.json b/package.json index 6e0d766..0624ee6 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "claude-runner", "displayName": "Claude Runner", "description": "Execute Claude Code commands directly from VS Code with an intuitive interface", - "version": "0.3.2", + "version": "0.4.0", "publisher": "Codingworkflow", "private": false, "license": "GPL-3.0", diff --git a/src/components/panels/ChatPanel.tsx b/src/components/panels/ChatPanel.tsx index 09e7e56..87fcd59 100644 --- a/src/components/panels/ChatPanel.tsx +++ b/src/components/panels/ChatPanel.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState, useRef, useEffect } from "react"; import Card from "../common/Card"; import Button from "../common/Button"; import Toggle from "../common/Toggle"; @@ -11,11 +11,31 @@ interface ChatPanelProps { disabled: boolean; } +interface ChatMessage { + role: "user" | "assistant"; + content: string; + timestamp: string; +} + const ChatPanel: React.FC = ({ disabled }) => { const { state, actions } = useExtension(); const { main, claude } = state; + const [chatMode, setChatMode] = useState<"terminal" | "extension">( + "extension", + ); + const [messages, setMessages] = useState([]); + const [inputMessage, setInputMessage] = useState(""); + const messagesEndRef = useRef(null); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }; + + useEffect(() => { + scrollToBottom(); + }, [messages]); - const handleStartChat = () => { + const handleStartTerminalChat = () => { if (main.showChatPrompt && main.chatPrompt.trim()) { actions.startInteractive(main.chatPrompt.trim()); } else { @@ -23,9 +43,59 @@ const ChatPanel: React.FC = ({ disabled }) => { } }; + const handleSendMessage = async () => { + if (!inputMessage.trim() || main.chatSending) { + return; + } + + const messageText = inputMessage.trim(); + const userMessage: ChatMessage = { + role: "user", + content: messageText, + timestamp: new Date().toISOString(), + }; + + // Immediately add user message to local state for responsive UI + setMessages((prev) => [...prev, userMessage]); + setInputMessage(""); + + // Set chatSending to true to show the spinner + actions.updateMainState({ chatSending: true }); + + try { + // Send to extension host - this will handle full conversation + actions.sendChatMessage( + messageText, + (main.chatMessages?.length ?? 0) === 0, + ); + } catch (error) { + console.error("Error sending message:", error); + // Reset chatSending on error + actions.updateMainState({ chatSending: false }); + } + }; + + const handleClearChat = () => { + setMessages([]); + actions.clearChatSession(); + }; + + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + handleSendMessage(); + } + }; + + // Sync with extension state - this receives the full conversation from the extension host + useEffect(() => { + if (main.chatMessages !== undefined) { + setMessages(main.chatMessages); + } + }, [main.chatMessages]); + return (
- {/* Claude Version Display - At the very top */} = ({ disabled }) => { isLoading={claude.loading} /> - {/* Root Path */} - -
-
-

- Start an interactive Claude chat session in the VS Code terminal - using the selected model and configuration. -

+ +
+
+ +
+
- {/* Model Selection */} - - - {/* Tool Permissions */} - - -
-
- {!main.showChatPrompt ? ( -
- -
- ) : ( - <> + {chatMode === "terminal" ? ( +
+
+

+ Start an interactive Claude chat session in the VS Code terminal + using the selected model and configuration. +

+
+ + + + + +
+
+ {!main.showChatPrompt ? (
-