diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..7671399 --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash +VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "") +go build -ldflags "-X github.com/titor/fanyi/internal/logo.version=${VERSION}" -o yoyo ./cmd/yoyo \ No newline at end of file diff --git a/changelog.md b/changelog.md index 7cf4dfe..0419cf1 100644 --- a/changelog.md +++ b/changelog.md @@ -498,4 +498,43 @@ yoyo onboard --force - 支持 `~` 路径展开 - huh使用v2版本,支持泛型和链式API -**讨论记录**: [taolun.md#2026-04-07-配置路径修复和huh迁移](taolun.md) \ No newline at end of file + **讨论记录**: [taolun.md#2026-04-07-配置路径修复和huh迁移](taolun.md) + +--- + +## v1.1.1 (2026-04-08) + +### BUG修复 +- 修复 `--help` `-h` `-?` `--version` 在默认交互式模式下无响应的问题 +- 原因:交互模式判断优先于help/version检查,导致flags被忽略 + +### 新功能 +- 新增 `internal/logo/logo.go` 模块,统一管理logo展示 +- 编译时通过 `-ldflags` 注入版本号,实现动态版本管理 +- 新增 `build.sh` 脚本,自动获取git版本并注入 + +### 改进 +- 帮助信息和版本输出使用渐变logo(紫→青色) +- TUI头部与CLI帮助信息使用统一的logo模块 +- 移除TUI头部的 `[Ctrl+C 退出]` 显示 +- 统一版本号格式:` ( v1.1.1-dirty )` 或 ` ( )`(无版本时) + +### 技术细节 +```go +// logo模块核心函数 +func GradientText(text string, startColor, endColor string) string +func GetLogoPattern() string // 返回4行ascii art +func GetVersionSuffix() string // 返回 " (v1.1.1-dirty )" 或 " ( )" +func PrintLogoWithVersion() // 打印完整logo +``` + +```bash +# build.sh 版本注入 +VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "") +go build -ldflags "-X github.com/titor/fanyi/internal/logo.version=${VERSION}" -o yoyo ./cmd/yoyo +``` + +- 渐变色方案:`#B413DC`(紫)→ `#00C8C8`(青) +- TUI通过调用 `logo.GradientText(logo.GetLogoPattern(), "#B413DC", "#00C8C8")` 获取渐变logo + +**讨论记录**: [taolun.md#2026-04-08-Logo模块化与渐变色统一](taolun.md) \ No newline at end of file diff --git a/cmd/yoyo/main.go b/cmd/yoyo/main.go index 179811d..8739a3c 100644 --- a/cmd/yoyo/main.go +++ b/cmd/yoyo/main.go @@ -14,6 +14,7 @@ import ( "github.com/titor/fanyi/internal/cache" "github.com/titor/fanyi/internal/config" "github.com/titor/fanyi/internal/lang" + "github.com/titor/fanyi/internal/logo" "github.com/titor/fanyi/internal/onboard" "github.com/titor/fanyi/internal/provider" "github.com/titor/fanyi/internal/translator" @@ -25,6 +26,7 @@ var ( version = flag.Bool("version", false, "显示版本信息") help = flag.Bool("help", false, "显示帮助信息") h = flag.Bool("h", false, "显示帮助信息") + question = flag.Bool("?", false, "显示帮助信息") langFlag = flag.String("lang", "", "目标语言代码(如 zh-CN, en-US, cn, en 等)") langLong = flag.String("language", "", "目标语言代码(--lang的长格式)") configFile = flag.String("config", "", "配置文件路径") @@ -38,8 +40,6 @@ var ( interactiveShort = flag.Bool("i", false, "启动交互式翻译界面(-i的短格式)") ) -const versionString = "YOYO翻译工具 v1.1.0" - // isPipeInput 检测是否有管道输入 func isPipeInput() bool { fileInfo, err := os.Stdin.Stat() @@ -104,6 +104,17 @@ func main() { return } + if *help || *h || *question { + printHelp() + return + } + + // 处理 -? 作为位置参数的情况 + if flag.NArg() > 0 && (flag.Arg(0) == "-?" || flag.Arg(0) == "?") { + printHelp() + return + } + // 处理交互式模式 if *interactive || *interactiveShort || shouldStartInteractive() { startInteractiveMode() @@ -112,14 +123,8 @@ func main() { // 处理管道输入情况 if isPipeInput() { - // 管道模式下,即使没有参数也继续执行 - if *help || *h { - printHelp() - return - } - } else { - // 非管道模式下,没有参数则显示帮助 - if *help || *h || flag.NArg() == 0 { + // 管道模式下,没有参数则显示帮助 + if flag.NArg() == 0 { printHelp() return } @@ -372,12 +377,14 @@ func runCacheCommand(subcommand string) { // printVersion 显示版本信息 func printVersion() { - fmt.Println(versionString) + logo.PrintLogoWithVersion() } // printHelp 显示帮助信息 func printHelp() { - fmt.Printf(`%s + logo.PrintLogoWithVersion() + + fmt.Printf(` 使用方法: yoyo [选项] <文本> @@ -445,8 +452,8 @@ func printHelp() { - 默认厂商: siliconflow - 默认目标语言: zh-CN (简体中文) -更多信息请访问: https://github.com/titor/fanyi -`, versionString) + 更多信息请访问: https://github.com/titor/fanyi +`) } // shouldStartInteractive 判断是否应该启动交互式模式 diff --git a/internal/logo/logo.go b/internal/logo/logo.go new file mode 100644 index 0000000..9024fc6 --- /dev/null +++ b/internal/logo/logo.go @@ -0,0 +1,79 @@ +package logo + +import ( + "fmt" + "strings" +) + +var version string + +func SetVersion(v string) { version = v } + +func GetVersion() string { return version } + +func GradientText(text string, startColor, endColor string) string { + startR, startG, startB := parseHexColor(startColor) + endR, endG, endB := parseHexColor(endColor) + + lines := strings.Split(text, "\n") + if len(lines) == 0 { + return text + } + + var result []string + for _, line := range lines { + if len(line) == 0 { + result = append(result, line) + continue + } + + var coloredLine string + for i, char := range line { + ratio := float64(i) / float64(len(line)-1) + r := int(float64(startR) + float64(endR-startR)*ratio) + g := int(float64(startG) + float64(endG-startG)*ratio) + b := int(float64(startB) + float64(endB-startB)*ratio) + coloredLine += fmt.Sprintf("\033[38;2;%d;%d;%dm%s\033[0m", r, g, b, string(char)) + } + result = append(result, coloredLine) + } + + return strings.Join(result, "\n") +} + +func parseHexColor(hex string) (int, int, int) { + hex = strings.TrimPrefix(hex, "#") + if len(hex) != 6 { + return 0, 0, 0 + } + var r, g, b int + fmt.Sscanf(hex, "%02x%02x%02x", &r, &g, &b) + return r, g, b +} + +func GetLogoPattern() string { + return " _ _ _____ _____ " + "\n" + + "( \\/ ( _ ( _ ) " + "\n" + + " \\ / )(_)( )(_)( " + "\n" + + " (__)(_____(_____(" +} + +func GetVersionSuffix() string { + v := GetVersion() + if v != "" { + return " (" + v + " )" + } + return " ( )" +} + +func PrintLogoWithVersion() { + logoPattern := " _ _ _____ _____\n" + + "( \\/ ( _ ( _ )\n" + + " \\ / )(_)( )(_)( \n" + + " (__)(_____(_____(" + + patternWithVersion := logoPattern + GetVersionSuffix() + + colored := GradientText(patternWithVersion, "#B413DC", "#00C8C8") + fmt.Println(colored) +} diff --git a/internal/tui/model.go b/internal/tui/model.go index 2a92524..298ce5c 100644 --- a/internal/tui/model.go +++ b/internal/tui/model.go @@ -13,17 +13,12 @@ import ( "charm.land/bubbletea/v2" "charm.land/lipgloss/v2" "github.com/titor/fanyi/internal/config" - "github.com/titor/fanyi/internal/content" + "github.com/titor/fanyi/internal/logo" "github.com/titor/fanyi/internal/translator" ) var supportedLangs = []string{"zh-CN", "en-US", "ja", "ko", "zh-TW", "es", "fr", "de"} -var logoPattern = "l_ _ _____ _____ " + "\n" + - "( \\/ ( _ ( _ ) " + "\n" + - " \\ / )(_)( )(_)( " + "\n" + - " (__)(_____(_____((() [v" + content.Version + "]" - type translateMsg struct { result string tokens int @@ -312,59 +307,18 @@ func (m model) View() tea.View { return v } -func gradientText(text string, startColor, endColor string) string { - startR, startG, startB := parseHexColor(startColor) - endR, endG, endB := parseHexColor(endColor) - - lines := strings.Split(text, "\n") - if len(lines) == 0 { - return text - } - - var result []string - for _, line := range lines { - if len(line) == 0 { - result = append(result, line) - continue - } - - var coloredLine string - for i, char := range line { - ratio := float64(i) / float64(len(line)-1) - r := int(float64(startR) + float64(endR-startR)*ratio) - g := int(float64(startG) + float64(endG-startG)*ratio) - b := int(float64(startB) + float64(endB-startB)*ratio) - coloredLine += fmt.Sprintf("\033[38;2;%d;%d;%dm%s\033[0m", r, g, b, string(char)) - } - result = append(result, coloredLine) - } - - return strings.Join(result, "\n") -} - -func parseHexColor(hex string) (int, int, int) { - hex = strings.TrimPrefix(hex, "#") - r, _ := strconv.ParseInt(hex[0:2], 16, 64) - g, _ := strconv.ParseInt(hex[2:4], 16, 64) - b, _ := strconv.ParseInt(hex[4:6], 16, 64) - return int(r), int(g), int(b) -} - func (m model) renderHeader() string { - title := gradientText(logoPattern, "#8B5CF6", "#EC4899") + title := logo.GradientText(logo.GetLogoPattern(), "#B413DC", "#00C8C8") + titleWithVersion := title + logo.GetVersionSuffix() width := m.width - 4 if width < 20 { width = 60 } - right := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#6B7280")). - Render("[Ctrl+C 退出]") - return lipgloss.NewStyle(). Width(width). - Render(title + strings.Repeat(" ", width-29-len(right)-1) + right) + Render(titleWithVersion) } func (m model) renderInputArea() string { diff --git a/memory.md b/memory.md index a69da8d..ad2f9f0 100644 --- a/memory.md +++ b/memory.md @@ -894,4 +894,45 @@ v2: ```go p := tea.NewProgram(model{}) p.Run() -``` \ No newline at end of file +``` + +--- + +### Logo模块化设计 + +**决策**: 创建 `internal/logo/logo.go` 统一管理logo,TUI和CLI共享 + +**原因**: +1. 避免代码重复:TUI和CLI都需要显示logo +2. 统一渐变色方案:紫→青 (`#B413DC` → `#00C8C8`) +3. 统一版本号格式:` ( v1.x.x )` 或 ` ( )` + +**实现**: +```go +// 导出函数供外部调用 +func GradientText(text string, startColor, endColor string) string +func GetLogoPattern() string // ASCII art图案 +func GetVersionSuffix() string // " (v1.x.x )" 或 " ( )" +func PrintLogoWithVersion() // 打印完整logo +``` + +**版本注入**: +```go +// 编译时通过 ldflags 注入 +// -X packagepath.variable=value +go build -ldflags "-X github.com/titor/fanyi/internal/logo.version=${VERSION}" -o yoyo ./cmd/yoyo +``` + +### 终端颜色输出问题 + +**问题**: 在非TTY环境(如管道)下,ANSI转义序列可能显示为明文 + +**观察**: +- 使用 `script` 命令可以正确显示颜色 +- `od -c` 检查输出包含正确的 `\033` 转义字符 +- zsh 可能对某些颜色转义处理不同 + +**解决方案**: +- 使用 `GradientText` 函数逐字符应用渐变 +- 每个字符后使用 `\033[0m` 重置 +- 渐变使用 24-bit 颜色 `\033[38;2;R;G;Bm` \ No newline at end of file diff --git a/taolun.md b/taolun.md index 3a2e91e..7a16e39 100644 --- a/taolun.md +++ b/taolun.md @@ -831,4 +831,51 @@ config.GetUserEnvPath() // ~/.config/yoyo/.env - `survey.Confirm` → `huh.NewConfirm().Affirmative("是").Negative("否")` - 分步表单 → `huh.NewForm(huh.NewGroup(...), huh.NewGroup(...))` -**关联版本**: [changelog.md#1.1.0](changelog.md#110-2026-04-07) \ No newline at end of file +**关联版本**: [changelog.md#1.1.0](changelog.md#110-2026-04-07) + +--- + +### [2026-04-08 00:40] 版本 1.1.1 - Logo模块化与渐变色统一 +**原因**: +1. `--help` `-h` `-?` `--version` 在默认交互式模式下无响应 +2. 帮助信息头部需要彩色logo展示 + +**问题分析**: +1. 交互模式判断优先于help/version检查,导致flags被忽略 +2. 帮助信息使用硬编码版本号 `YOYO翻译工具 v1.1.0`,无logo + +**解决方案**: +1. 修复flag检查顺序:在进入交互模式前先检查 `-h` `--help` `-?` `--version` +2. 新增 `internal/logo/logo.go` 模块统一管理logo +3. 编译时通过 `ldflags` 注入版本号 +4. 渐变色使用与TUI一致的紫→青色方案 + +**技术实现**: +```go +// logo模块核心函数 +func GradientText(text string, startColor, endColor string) string { ... } +func GetLogoPattern() string // 返回4行ascii art +func GetVersionSuffix() string // 返回 " (v1.1.1-dirty )" 或 " ( )" +func PrintLogoWithVersion() // 打印完整logo(--help/--version使用) +``` + +**build.sh版本注入**: +```bash +VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "") +go build -ldflags "-X github.com/titor/fanyi/internal/logo.version=${VERSION}" -o yoyo ./cmd/yoyo +``` + +**TUI整合**: +- 移除 `internal/tui/model.go` 中的本地 `logoPattern` 和 `gradientText` +- 直接调用 `logo.GradientText(logo.GetLogoPattern(), "#B413DC", "#00C8C8")` +- 移除 `[Ctrl+C 退出]` 显示 + +**最终输出格式**(所有位置统一): +``` + _ _ _____ _____ +( \/ ( _ ( _ ) + \ / )(_)( )(_)( + (__)(_____(_____( v1.1.1-dirty ) +``` + +**关联版本**: [changelog.md#1.1.1](changelog.md#111-2026-04-08) \ No newline at end of file