fix: 将所有emoji/Unicode特殊字符替换为ASCII安全字符

Windows cmd/PowerShell默认codepage下emoji显示为?号。
全局替换: 👋->移除, 💭->[Thinking], ⚙->*, ✓->[OK], ✗->[FAIL],
◆->>, ⚠->[!], ->[OK], •->|/-, →->>>, ✏️->[Edit]等。
❯保留(windows-vtp终端下正常显示)。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pull/1/head
liuzh 1 month ago
parent b7ad7956c0
commit eeceaaaea9
  1. 2
      src/main/java/com/claudecode/command/impl/ClearCommand.java
  2. 4
      src/main/java/com/claudecode/command/impl/CompactCommand.java
  3. 2
      src/main/java/com/claudecode/command/impl/ModelCommand.java
  4. 6
      src/main/java/com/claudecode/console/BannerPrinter.java
  5. 2
      src/main/java/com/claudecode/console/MarkdownRenderer.java
  6. 4
      src/main/java/com/claudecode/console/ThinkingRenderer.java
  7. 6
      src/main/java/com/claudecode/console/ToolStatusRenderer.java
  8. 2
      src/main/java/com/claudecode/repl/ClaudeCodeCompleter.java
  9. 10
      src/main/java/com/claudecode/repl/ReplSession.java
  10. 2
      src/main/java/com/claudecode/tool/impl/BashTool.java
  11. 4
      src/main/java/com/claudecode/tool/impl/FileEditTool.java
  12. 2
      src/main/java/com/claudecode/tool/impl/FileReadTool.java
  13. 6
      src/main/java/com/claudecode/tool/impl/FileWriteTool.java
  14. 2
      src/main/java/com/claudecode/tool/impl/GlobTool.java
  15. 2
      src/main/java/com/claudecode/tool/impl/GrepTool.java

@ -24,6 +24,6 @@ public class ClearCommand implements SlashCommand {
if (context.agentLoop() != null) { if (context.agentLoop() != null) {
context.agentLoop().reset(); context.agentLoop().reset();
} }
return AnsiStyle.green(" Conversation history cleared."); return AnsiStyle.green(" [OK] Conversation history cleared.");
} }
} }

@ -27,8 +27,8 @@ public class CompactCommand implements SlashCommand {
if (context.agentLoop() != null) { if (context.agentLoop() != null) {
int before = context.agentLoop().getMessageHistory().size(); int before = context.agentLoop().getMessageHistory().size();
context.agentLoop().reset(); context.agentLoop().reset();
return AnsiStyle.green(" Context compacted: " + before + " messages 1 (system prompt only)"); return AnsiStyle.green(" [OK] Context compacted: " + before + " messages -> 1 (system prompt only)");
} }
return AnsiStyle.yellow(" No active conversation to compact."); return AnsiStyle.yellow(" [!] No active conversation to compact.");
} }
} }

@ -38,7 +38,7 @@ public class ModelCommand implements SlashCommand {
if (args != null && !args.isBlank()) { if (args != null && !args.isBlank()) {
sb.append("\n"); sb.append("\n");
sb.append(AnsiStyle.yellow(" Model switching not yet implemented. Set AI_MODEL env variable.")); sb.append(AnsiStyle.yellow(" [!] Model switching not yet implemented. Set AI_MODEL env variable."));
} }
return sb.toString(); return sb.toString();

@ -34,7 +34,7 @@ public class BannerPrinter {
out.println(banner); out.println(banner);
out.println(AnsiStyle.bold(" Claude Code (Java)") + AnsiStyle.dim(" v" + VERSION)); out.println(AnsiStyle.bold(" Claude Code (Java)") + AnsiStyle.dim(" v" + VERSION));
out.println(AnsiStyle.dim(" Powered by Spring AI Type /help for commands")); out.println(AnsiStyle.dim(" Powered by Spring AI | Type /help for commands"));
out.println(); out.println();
} }
@ -43,9 +43,9 @@ public class BannerPrinter {
*/ */
public static void printCompact(PrintStream out) { public static void printCompact(PrintStream out) {
out.println(); out.println();
out.println(AnsiStyle.BRIGHT_CYAN + AnsiStyle.BOLD + " Claude Code (Java)" + AnsiStyle.RESET out.println(AnsiStyle.BRIGHT_CYAN + AnsiStyle.BOLD + " > Claude Code (Java)" + AnsiStyle.RESET
+ AnsiStyle.dim(" v" + VERSION)); + AnsiStyle.dim(" v" + VERSION));
out.println(AnsiStyle.dim(" Type /help for commands Ctrl+D to exit")); out.println(AnsiStyle.dim(" Type /help for commands | Ctrl+D to exit"));
out.println(); out.println();
} }
} }

@ -53,7 +53,7 @@ public class MarkdownRenderer {
} }
// 列表项 // 列表项
else if (line.stripLeading().startsWith("- ") || line.stripLeading().startsWith("* ")) { else if (line.stripLeading().startsWith("- ") || line.stripLeading().startsWith("* ")) {
out.println(" " + AnsiStyle.CYAN + "" + AnsiStyle.RESET + " " + renderInline(line.stripLeading().substring(2))); out.println(" " + AnsiStyle.CYAN + "-" + AnsiStyle.RESET + " " + renderInline(line.stripLeading().substring(2)));
} }
// 分隔线 // 分隔线
else if (line.strip().matches("^-{3,}$") || line.strip().matches("^\\*{3,}$")) { else if (line.strip().matches("^-{3,}$") || line.strip().matches("^\\*{3,}$")) {

@ -22,7 +22,7 @@ public class ThinkingRenderer {
} }
out.println(); out.println();
out.println(AnsiStyle.DIM + AnsiStyle.ITALIC + " 💭 Thinking..." + AnsiStyle.RESET); out.println(AnsiStyle.DIM + AnsiStyle.ITALIC + " [Thinking...]" + AnsiStyle.RESET);
// 显示 thinking 内容(缩进并用暗色) // 显示 thinking 内容(缩进并用暗色)
for (String line : thinkingContent.lines().toList()) { for (String line : thinkingContent.lines().toList()) {
@ -33,7 +33,7 @@ public class ThinkingRenderer {
/** 渲染 thinking 开始标记 */ /** 渲染 thinking 开始标记 */
public void renderStart() { public void renderStart() {
out.print(AnsiStyle.DIM + AnsiStyle.ITALIC + " 💭 Thinking..." + AnsiStyle.RESET); out.print(AnsiStyle.DIM + AnsiStyle.ITALIC + " [Thinking...]" + AnsiStyle.RESET);
} }
/** 渲染 thinking 结束标记 */ /** 渲染 thinking 结束标记 */

@ -18,7 +18,7 @@ public class ToolStatusRenderer {
/** 渲染工具调用开始 */ /** 渲染工具调用开始 */
public void renderStart(String toolName, String args) { public void renderStart(String toolName, String args) {
out.println(AnsiStyle.dim(" ─────────────────────────────────────────")); out.println(AnsiStyle.dim(" ─────────────────────────────────────────"));
out.print(AnsiStyle.YELLOW + " " + AnsiStyle.BOLD + toolName + AnsiStyle.RESET); out.print(AnsiStyle.YELLOW + " * " + AnsiStyle.BOLD + toolName + AnsiStyle.RESET);
out.println(AnsiStyle.dim(" running...")); out.println(AnsiStyle.dim(" running..."));
// 如果有简短参数,显示 // 如果有简短参数,显示
if (args != null && !args.isBlank()) { if (args != null && !args.isBlank()) {
@ -37,7 +37,7 @@ public class ToolStatusRenderer {
display = display.substring(0, 497) + "..."; display = display.substring(0, 497) + "...";
} }
out.println(AnsiStyle.GREEN + " " + AnsiStyle.BOLD + toolName + AnsiStyle.RESET out.println(AnsiStyle.GREEN + " [OK] " + AnsiStyle.BOLD + toolName + AnsiStyle.RESET
+ AnsiStyle.dim(" done")); + AnsiStyle.dim(" done"));
if (display != null && !display.isBlank()) { if (display != null && !display.isBlank()) {
// 缩进输出每一行 // 缩进输出每一行
@ -50,7 +50,7 @@ public class ToolStatusRenderer {
/** 渲染工具错误 */ /** 渲染工具错误 */
public void renderError(String toolName, String error) { public void renderError(String toolName, String error) {
out.println(AnsiStyle.RED + " " + AnsiStyle.BOLD + toolName + AnsiStyle.RESET out.println(AnsiStyle.RED + " [FAIL] " + AnsiStyle.BOLD + toolName + AnsiStyle.RESET
+ AnsiStyle.red(" error")); + AnsiStyle.red(" error"));
if (error != null) { if (error != null) {
out.println(AnsiStyle.red(" " + error)); out.println(AnsiStyle.red(" " + error));

@ -61,7 +61,7 @@ public class ClaudeCodeCompleter implements Completer {
if (alias.startsWith(prefix)) { if (alias.startsWith(prefix)) {
candidates.add(new Candidate( candidates.add(new Candidate(
"/" + alias, "/" + alias,
alias + " " + name, alias + " -> " + name,
"Aliases", "Aliases",
cmd.description(), cmd.description(),
null, null, true null, null, true

@ -154,7 +154,7 @@ public class ReplSession {
handleInput(input, cmdContext); handleInput(input, cmdContext);
} }
out.println(AnsiStyle.dim("\n Goodbye! 👋\n")); out.println(AnsiStyle.dim("\n Goodbye!\n"));
} }
} }
@ -174,8 +174,8 @@ public class ReplSession {
out.println(AnsiStyle.dim(" Terminal: " + termInfo)); out.println(AnsiStyle.dim(" Terminal: " + termInfo));
if (isDumb) { if (isDumb) {
out.println(AnsiStyle.yellow(" ⚠ Dumb 终端模式:Tab补全和行编辑可能受限")); out.println(AnsiStyle.yellow(" [!] Dumb terminal: Tab completion and line editing may be limited"));
out.println(AnsiStyle.yellow(" 建议在 Windows Terminal / PowerShell / cmd.exe 中运行")); out.println(AnsiStyle.yellow(" Run in Windows Terminal / PowerShell / cmd.exe for best experience"));
} else { } else {
out.println(AnsiStyle.dim(" Tip: Tab to complete commands, ↑↓ to browse history, Ctrl+D to exit")); out.println(AnsiStyle.dim(" Tip: Tab to complete commands, ↑↓ to browse history, Ctrl+D to exit"));
} }
@ -211,7 +211,7 @@ public class ReplSession {
handleInput(input, cmdContext); handleInput(input, cmdContext);
} }
out.println(AnsiStyle.dim("\n Goodbye! 👋\n")); out.println(AnsiStyle.dim("\n Goodbye!\n"));
} }
// ==================== 公共输入处理 ==================== // ==================== 公共输入处理 ====================
@ -237,7 +237,7 @@ public class ReplSession {
out.println(); out.println();
} catch (Exception e) { } catch (Exception e) {
spinner.stop(); spinner.stop();
out.println(AnsiStyle.red("\n ✗ Error: " + e.getMessage())); out.println(AnsiStyle.red("\n [ERROR] " + e.getMessage()));
log.error("Agent 循环异常", e); log.error("Agent 循环异常", e);
out.println(); out.println();
} }

@ -108,6 +108,6 @@ public class BashTool implements Tool {
if (cmd.length() > 60) { if (cmd.length() > 60) {
cmd = cmd.substring(0, 57) + "..."; cmd = cmd.substring(0, 57) + "...";
} }
return " " + cmd; return "> " + cmd;
} }
} }

@ -87,7 +87,7 @@ public class FileEditTool implements Tool {
long oldLines = oldString.lines().count(); long oldLines = oldString.lines().count();
long newLines = newString.lines().count(); long newLines = newString.lines().count();
return " Edited " + path + " (replaced " + oldLines + " lines with " + newLines + " lines)"; return "[OK] Edited " + path + " (replaced " + oldLines + " lines with " + newLines + " lines)";
} catch (IOException e) { } catch (IOException e) {
return "Error editing file: " + e.getMessage(); return "Error editing file: " + e.getMessage();
@ -96,6 +96,6 @@ public class FileEditTool implements Tool {
@Override @Override
public String activityDescription(Map<String, Object> input) { public String activityDescription(Map<String, Object> input) {
return " Editing " + input.getOrDefault("file_path", "file"); return "[Edit] " + input.getOrDefault("file_path", "file");
} }
} }

@ -118,6 +118,6 @@ public class FileReadTool implements Tool {
@Override @Override
public String activityDescription(Map<String, Object> input) { public String activityDescription(Map<String, Object> input) {
return "📄 Reading " + input.getOrDefault("file_path", "file"); return "[Read] " + input.getOrDefault("file_path", "file");
} }
} }

@ -67,9 +67,9 @@ public class FileWriteTool implements Tool {
long lines = content.lines().count(); long lines = content.lines().count();
if (existed) { if (existed) {
return " Updated " + path + " (" + lines + " lines)"; return "[OK] Updated " + path + " (" + lines + " lines)";
} else { } else {
return " Created " + path + " (" + lines + " lines)"; return "[OK] Created " + path + " (" + lines + " lines)";
} }
} catch (IOException e) { } catch (IOException e) {
@ -79,6 +79,6 @@ public class FileWriteTool implements Tool {
@Override @Override
public String activityDescription(Map<String, Object> input) { public String activityDescription(Map<String, Object> input) {
return " Writing " + input.getOrDefault("file_path", "file"); return "[Write] " + input.getOrDefault("file_path", "file");
} }
} }

@ -122,6 +122,6 @@ public class GlobTool implements Tool {
@Override @Override
public String activityDescription(Map<String, Object> input) { public String activityDescription(Map<String, Object> input) {
return "🔍 Searching " + input.getOrDefault("pattern", "files"); return "[Search] " + input.getOrDefault("pattern", "files");
} }
} }

@ -164,6 +164,6 @@ public class GrepTool implements Tool {
@Override @Override
public String activityDescription(Map<String, Object> input) { public String activityDescription(Map<String, Object> input) {
return "🔍 Searching for '" + input.getOrDefault("pattern", "...") + "'"; return "[Grep] '" + input.getOrDefault("pattern", "...") + "'";
} }
} }

Loading…
Cancel
Save