From d8a71713254a51a4e52d41372141ee8c32f7206b Mon Sep 17 00:00:00 2001 From: liuzh Date: Wed, 1 Apr 2026 23:50:48 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Banner=E8=BE=B9=E6=A1=86=E4=B8=8A?= =?UTF-8?q?=E4=B8=8B=E5=AF=B9=E9=BD=90=20-=20=E5=8A=A0=E5=85=A5ANSI?= =?UTF-8?q?=E5=8F=AF=E8=A7=81=E5=AE=BD=E5=BA=A6=E8=AE=A1=E7=AE=97+?= =?UTF-8?q?=E5=8F=B3=E4=BE=A7=E9=97=AD=E5=90=88=E2=94=82+=E7=B2=BE?= =?UTF-8?q?=E7=A1=AEpadding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 顶部边框公式修正: innerWidth - titleVisibleLen - 每行body加右侧闭合│边框 - visibleLength()去除ANSI转义计算真实宽度 - 右侧信息区域自动pad到固定宽度 - Logo宽度调整为20字符 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../com/claudecode/console/BannerPrinter.java | 98 ++++++++++--------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/src/main/java/com/claudecode/console/BannerPrinter.java b/src/main/java/com/claudecode/console/BannerPrinter.java index 364e91d..d6d5f04 100644 --- a/src/main/java/com/claudecode/console/BannerPrinter.java +++ b/src/main/java/com/claudecode/console/BannerPrinter.java @@ -1,6 +1,7 @@ package com.claudecode.console; import java.io.PrintStream; +import java.util.regex.Pattern; /** * Banner 打印器 —— 对应 claude-code/src/components/Banner.tsx。 @@ -12,54 +13,30 @@ public class BannerPrinter { private static final String VERSION = "0.1.0-SNAPSHOT"; - // 边框字符 - private static final String TL = "╭", TR = "╮", BL = "╰", BR = "╯"; - private static final String H = "─", V = "│"; + // 匹配 ANSI 转义序列的正则 + private static final Pattern ANSI_PATTERN = Pattern.compile("\033\\[[0-9;]*m"); /** * 打印带边框的启动 Banner。 - * - * @param out 输出流 - * @param provider API 提供者名称 - * @param model 模型名称 - * @param baseUrl API URL - * @param workDir 工作目录 - * @param toolCount 工具数量 - * @param cmdCount 命令数量 - * @param termInfo 终端信息 */ public static void printBoxed(PrintStream out, String provider, String model, String baseUrl, String workDir, int toolCount, int cmdCount, String termInfo) { - int boxWidth = 90; - String hr = H.repeat(boxWidth - 2); + // 内容宽度(不含左右边框 │) + int innerWidth = 88; - // Logo(ASCII 冒烟咖啡杯 — 每行精确 16 字符宽) + // Logo(ASCII 冒烟咖啡杯 — 每行精确 20 字符宽,含左右空格) String[] logo = { - " ) ) ) ", - " ╭────────╮ ", - " │ ~~~~~~ │ ", - " │ CLAUDE │ ", - " │ CODE │ ", - " ╰─┬────┬─╯ " + " ) ) ) ", + " ╭────────╮ ", + " │ ~~~~~~ │─╮ ", + " │ CLAUDE │ │ ", + " │ CODE │─╯ ", + " ╰─┬────┬─╯ " }; + int logoVisualWidth = 20; - // 右侧信息 - String titleLine = "Claude Code Java v" + VERSION; - String descLine = "Describe a task to get started."; - String tipLine = "Tip: /help for commands, Tab to complete"; - - // 打印顶部边框 - out.println(); - out.println(" " + TL + "─── " + AnsiStyle.BOLD + AnsiStyle.BRIGHT_CYAN - + "Claude Code Java" + AnsiStyle.RESET + AnsiStyle.DIM + " v" + VERSION - + AnsiStyle.RESET + " " + H.repeat(boxWidth - 28 - VERSION.length()) + TR); - - // Logo + 右侧信息(双列布局) - int logoWidth = 16; // logo 视觉宽度 - int rightStart = logoWidth + 4; - int contentWidth = boxWidth - 4; // 边框内可用宽度 - + // 右侧信息(纯可见文本 + ANSI 颜色) String[] rightInfo = { "", AnsiStyle.BOLD + "Welcome!" + AnsiStyle.RESET, @@ -70,23 +47,50 @@ public class BannerPrinter { AnsiStyle.DIM + "Tools: " + toolCount + " | Commands: " + cmdCount + " | " + termInfo + AnsiStyle.RESET, }; + // ── 顶部边框 ── + // 格式: ╭─── Claude Code Java v0.1.0-SNAPSHOT ───...───╮ + String title = "Claude Code Java"; + String versionStr = " v" + VERSION + " "; + String prefix = "─── "; + // 可见宽度: prefix + title + versionStr + 补充的 ─ + int titleVisibleLen = prefix.length() + title.length() + versionStr.length(); + int trailingDashes = innerWidth - titleVisibleLen; + if (trailingDashes < 1) trailingDashes = 1; + + out.println(); + out.println(" ╭" + prefix + + AnsiStyle.BOLD + AnsiStyle.BRIGHT_CYAN + title + AnsiStyle.RESET + + AnsiStyle.DIM + versionStr + AnsiStyle.RESET + + "─".repeat(trailingDashes) + "╮"); + + // ── 内容行(双列布局) ── + // 每行格式: │ [logo] │ [rightInfo] ... padding ... │ + // 中间分隔符 " │ " 占 3 个可见字符 + int separatorWidth = 3; + int rightAreaWidth = innerWidth - logoVisualWidth - separatorWidth; + int maxRows = Math.max(logo.length, rightInfo.length); for (int i = 0; i < maxRows; i++) { String leftPart = i < logo.length ? logo[i] : ""; String rightPart = i < rightInfo.length ? rightInfo[i] : ""; - // 左侧 logo 部分(固定宽度,无 ANSI 所以直接 pad) - String paddedLeft = padRight(leftPart, logoWidth); + // 左侧 logo(纯文本,直接 pad) + String paddedLeft = padRight(leftPart, logoVisualWidth); + + // 右侧信息需要计算可见长度,补齐空格到 rightAreaWidth + int rightVisible = visibleLength(rightPart); + int rightPadding = rightAreaWidth - rightVisible; + if (rightPadding < 0) rightPadding = 0; - // 输出行 - out.println(" " + V + " " + out.println(" │" + AnsiStyle.BRIGHT_CYAN + paddedLeft + AnsiStyle.RESET + AnsiStyle.DIM + " │ " + AnsiStyle.RESET - + rightPart); + + rightPart + " ".repeat(rightPadding) + + "│"); } - // 底部边框 - out.println(" " + BL + hr + BR); + // ── 底部边框 ── + out.println(" ╰" + "─".repeat(innerWidth) + "╯"); } /** @@ -100,6 +104,12 @@ public class BannerPrinter { out.println(); } + /** 计算去除 ANSI 转义后的可见字符宽度 */ + private static int visibleLength(String s) { + if (s == null || s.isEmpty()) return 0; + return ANSI_PATTERN.matcher(s).replaceAll("").length(); + } + /** 右侧补空格到指定视觉宽度 */ private static String padRight(String s, int width) { int len = s.length();