feat: Logo模块化与渐变色统一
- 修复 --help/-h/-?/--version 在交互模式下无响应的问题 - 新增 internal/logo/logo.go 统一管理logo展示 - 新增 build.sh 自动注入git版本号 - TUI头部与CLI使用统一logo模块 - 移除TUI头部的 [Ctrl+C 退出] 显示 - 统一版本号格式: ( v1.1.1-dirty )
This commit is contained in:
3
build.sh
Executable file
3
build.sh
Executable file
@@ -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
|
||||
41
changelog.md
41
changelog.md
@@ -498,4 +498,43 @@ yoyo onboard --force
|
||||
- 支持 `~` 路径展开
|
||||
- huh使用v2版本,支持泛型和链式API
|
||||
|
||||
**讨论记录**: [taolun.md#2026-04-07-配置路径修复和huh迁移](taolun.md)
|
||||
**讨论记录**: [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)
|
||||
@@ -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 判断是否应该启动交互式模式
|
||||
|
||||
79
internal/logo/logo.go
Normal file
79
internal/logo/logo.go
Normal file
@@ -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)
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
43
memory.md
43
memory.md
@@ -894,4 +894,45 @@ v2:
|
||||
```go
|
||||
p := tea.NewProgram(model{})
|
||||
p.Run()
|
||||
```
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 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`
|
||||
49
taolun.md
49
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)
|
||||
**关联版本**: [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)
|
||||
Reference in New Issue
Block a user