diff --git a/src/main/java/com/claudecode/tui/ClaudeCodeComponent.java b/src/main/java/com/claudecode/tui/ClaudeCodeComponent.java index 793774d..376f180 100644 --- a/src/main/java/com/claudecode/tui/ClaudeCodeComponent.java +++ b/src/main/java/com/claudecode/tui/ClaudeCodeComponent.java @@ -87,8 +87,10 @@ public class ClaudeCodeComponent extends Component private static final String TITLE_STATIC_PREFIX = "✳"; private volatile int spinnerFrame = 0; private volatile Thread spinnerThread; - /** 终端标题(从首条用户消息推断) */ + /** 终端标题(从用户消息推断,每次新消息都更新) */ private volatile String sessionTitle = null; + /** 当前正在执行的工具名(用于标题显示) */ + private volatile String currentToolName = null; private volatile boolean titleInitialized = false; /** 权限确认回调(由权限请求设置,用户输入后调用) */ @@ -1125,7 +1127,7 @@ public class ClaudeCodeComponent extends Component onFirstUserInput.accept(text); onFirstUserInput = null; // 只触发一次 } - inferSessionTitle(text); // 从首条用户消息推断终端标题 + updateSessionTitle(text); // 从用户消息更新终端标题 addMessage(new UserMsg(text)); setState(new TuiState("", getState().messages, 0, true, "")); runAgent(text); @@ -1266,6 +1268,7 @@ public class ClaudeCodeComponent extends Component /** 更新最后一个工具调用消息的结果 */ public void completeLastToolCall(String result) { + currentToolName = null; // 工具执行完成,清除工具名 synchronized (stateLock) { TuiState s = getState(); List msgs = new ArrayList<>(s.messages); @@ -1300,6 +1303,11 @@ public class ClaudeCodeComponent extends Component } } + /** 设置当前正在执行的工具名(用于终端标题显示) */ + public void setCurrentToolName(String toolName) { + this.currentToolName = toolName; + } + /** 设置简单文本输入回调(用于无选项的 AskUser) */ public void requestTextInput(Consumer callback) { this.askOptions = null; @@ -1535,22 +1543,30 @@ public class ClaudeCodeComponent extends Component * 匹配官方 AnimatedTerminalTitle 组件的行为: * - 空闲: "✳ Claude Code" 或 "✳ " * - 工作中: "⠂ " / "⠐ <title>" (交替动画) + * - 等待审批: "✳ <title>" (暂停动画) + * - 工具执行中: "⠂ <toolName> · <title>" */ private String computeTerminalTitle() { String title = sessionTitle != null ? sessionTitle : "Claude Code"; - if (agentRunning.get()) { + boolean isWaiting = permissionCallback != null; + if (agentRunning.get() && !isWaiting) { String frame = TITLE_ANIMATION_FRAMES[spinnerFrame % TITLE_ANIMATION_FRAMES.length]; + String toolName = currentToolName; + if (toolName != null && !toolName.isEmpty()) { + return frame + " " + toolName + " · " + title; + } return frame + " " + title; } return TITLE_STATIC_PREFIX + " " + title; } /** - * 从首条用户消息推断会话标题(简化版,不调用 AI)。 + * 从用户消息更新会话标题。 * 官方使用 Haiku 生成 3-7 词标题,这里取前 40 字符作为简化实现。 + * 每次新用户消息都更新标题,使其反映当前对话主题。 */ - private void inferSessionTitle(String userInput) { - if (sessionTitle != null || userInput == null || userInput.isBlank()) return; + private void updateSessionTitle(String userInput) { + if (userInput == null || userInput.isBlank()) return; if (userInput.startsWith("/")) return; // 跳过斜杠命令 String trimmed = userInput.strip(); if (trimmed.length() > 40) { diff --git a/src/main/java/com/claudecode/tui/JinkReplSession.java b/src/main/java/com/claudecode/tui/JinkReplSession.java index 45d3802..66a27e1 100644 --- a/src/main/java/com/claudecode/tui/JinkReplSession.java +++ b/src/main/java/com/claudecode/tui/JinkReplSession.java @@ -116,6 +116,7 @@ public class JinkReplSession { case START -> { // 完成当前流式消息(如果有) finishCurrentStreaming(); + component.setCurrentToolName(event.toolName()); component.addMessage(new ToolCallMsg( event.toolName(), event.arguments(),