feat: 实现模块6 - TUI集成翻译 (Enter翻译/异步执行/加载状态)
This commit is contained in:
15
changelog.md
15
changelog.md
@@ -30,8 +30,9 @@
|
|||||||
- [x] 模块3: 翻译显示区 ✅ 已完成
|
- [x] 模块3: 翻译显示区 ✅ 已完成
|
||||||
- [x] 模块4: 状态栏/主题 ✅ 已完成
|
- [x] 模块4: 状态栏/主题 ✅ 已完成
|
||||||
- [x] 模块5: 快捷键系统 ✅ 已完成
|
- [x] 模块5: 快捷键系统 ✅ 已完成
|
||||||
|
- [x] 模块6: 集成翻译 ✅ 已完成
|
||||||
|
|
||||||
## TUI界面实现计划 (v0.6.0)
|
## TUI界面实现计划 (v0.6.0) - 全部完成!
|
||||||
| 步骤 | 模块 | 内容 | 状态 |
|
| 步骤 | 模块 | 内容 | 状态 |
|
||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| 1 | TUI框架搭建 | bubbletea基础App结构、运行循环 | ✅ 已完成 |
|
| 1 | TUI框架搭建 | bubbletea基础App结构、运行循环 | ✅ 已完成 |
|
||||||
@@ -39,16 +40,16 @@
|
|||||||
| 3 | 翻译显示区 | 结果展示、格式化、滚动 | ✅ 已完成 |
|
| 3 | 翻译显示区 | 结果展示、格式化、滚动 | ✅ 已完成 |
|
||||||
| 4 | 状态栏/主题 | 底部状态栏、语言选择、主题配色 | ✅ 已完成 |
|
| 4 | 状态栏/主题 | 底部状态栏、语言选择、主题配色 | ✅ 已完成 |
|
||||||
| 5 | 快捷键系统 | 退出、清空、切换语言等 | ✅ 已完成 |
|
| 5 | 快捷键系统 | 退出、清空、切换语言等 | ✅ 已完成 |
|
||||||
| 6 | 集成翻译 | 对接现有Translator、加载动画 | ⏳ 待实现 |
|
| 6 | 集成翻译 | 对接现有Translator、加载动画 | ✅ 已完成 |
|
||||||
|
|
||||||
## 待修复BUG
|
## 待修复BUG
|
||||||
- 无
|
- 无
|
||||||
|
|
||||||
## 版本历史
|
## 版本历史
|
||||||
|
|
||||||
### 0.6.0 (2026-04-06) - TUI交互界面 (开发中)
|
### 0.6.0 (2026-04-06) - TUI交互界面
|
||||||
**类型**: 功能版本
|
**类型**: 功能版本
|
||||||
**状态**: 开发中
|
**状态**: 已完成
|
||||||
|
|
||||||
**变更内容**:
|
**变更内容**:
|
||||||
- ✅ 模块1: TUI框架搭建 - 添加bubbletea依赖,实现基础App结构
|
- ✅ 模块1: TUI框架搭建 - 添加bubbletea依赖,实现基础App结构
|
||||||
@@ -56,7 +57,7 @@
|
|||||||
- ✅ 模块3: 翻译显示区 - 结果显示区域、样式定义
|
- ✅ 模块3: 翻译显示区 - 结果显示区域、样式定义
|
||||||
- ✅ 模块4: 状态栏/主题 - 底部状态栏、语言显示、配色完善
|
- ✅ 模块4: 状态栏/主题 - 底部状态栏、语言显示、配色完善
|
||||||
- ✅ 模块5: 快捷键系统 - Ctrl+L清空、Ctrl+T切换语言
|
- ✅ 模块5: 快捷键系统 - Ctrl+L清空、Ctrl+T切换语言
|
||||||
- ⏳ 模块6: 集成翻译 - 待实现
|
- ✅ 模块6: 集成翻译 - Enter触发翻译、异步执行、加载状态、错误处理
|
||||||
|
|
||||||
**技术实现**:
|
**技术实现**:
|
||||||
- 使用 `github.com/charmbracelet/bubbletea` v1.3.10
|
- 使用 `github.com/charmbracelet/bubbletea` v1.3.10
|
||||||
@@ -68,7 +69,9 @@
|
|||||||
- [TUI界面模块拆分计划](taolun.md#2026-04-06-1000-版本-060---tui界面模块拆分计划)
|
- [TUI界面模块拆分计划](taolun.md#2026-04-06-1000-版本-060---tui界面模块拆分计划)
|
||||||
|
|
||||||
**下一步**:
|
**下一步**:
|
||||||
- 实现模块6: 集成翻译
|
- 测试TUI交互界面
|
||||||
|
- 优化用户体验
|
||||||
|
- 添加更多功能(如复制翻译结果)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/textinput"
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
"github.com/charmbracelet/bubbletea"
|
"github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
@@ -13,8 +15,15 @@ type model struct {
|
|||||||
translator *translator.Translator
|
translator *translator.Translator
|
||||||
textInput textinput.Model
|
textInput textinput.Model
|
||||||
result string
|
result string
|
||||||
|
errMsg string
|
||||||
targetLang string
|
targetLang string
|
||||||
langIndex int
|
langIndex int
|
||||||
|
loading bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type translateMsg struct {
|
||||||
|
result string
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -29,6 +38,9 @@ var (
|
|||||||
resultStyle = lipgloss.NewStyle().
|
resultStyle = lipgloss.NewStyle().
|
||||||
Foreground(lipgloss.Color("#98FB98")).
|
Foreground(lipgloss.Color("#98FB98")).
|
||||||
Background(lipgloss.Color("#0D1B2A"))
|
Background(lipgloss.Color("#0D1B2A"))
|
||||||
|
errorStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#FF6B6B")).
|
||||||
|
Background(lipgloss.Color("#1A1A2E"))
|
||||||
helpStyle = lipgloss.NewStyle().
|
helpStyle = lipgloss.NewStyle().
|
||||||
Foreground(lipgloss.Color("#888888"))
|
Foreground(lipgloss.Color("#888888"))
|
||||||
statusBarStyle = lipgloss.NewStyle().
|
statusBarStyle = lipgloss.NewStyle().
|
||||||
@@ -40,6 +52,8 @@ var (
|
|||||||
Bold(true)
|
Bold(true)
|
||||||
keyStyle = lipgloss.NewStyle().
|
keyStyle = lipgloss.NewStyle().
|
||||||
Foreground(lipgloss.Color("#60A5FA"))
|
Foreground(lipgloss.Color("#60A5FA"))
|
||||||
|
loadingStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#60A5FA"))
|
||||||
)
|
)
|
||||||
|
|
||||||
var supportedLangs = []string{"zh-CN", "en-US", "ja", "ko", "zh-TW", "es", "fr", "de"}
|
var supportedLangs = []string{"zh-CN", "en-US", "ja", "ko", "zh-TW", "es", "fr", "de"}
|
||||||
@@ -72,22 +86,45 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
|
case translateMsg:
|
||||||
|
m.loading = false
|
||||||
|
if msg.err != nil {
|
||||||
|
m.errMsg = msg.err.Error()
|
||||||
|
m.result = ""
|
||||||
|
} else {
|
||||||
|
m.result = msg.result
|
||||||
|
m.errMsg = ""
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
switch msg.Type {
|
switch msg.Type {
|
||||||
case tea.KeyEnter:
|
case tea.KeyEnter:
|
||||||
// 回车键处理,后续模块会添加翻译逻辑
|
if m.loading {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
text := m.textInput.Value()
|
||||||
|
if text == "" {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
m.loading = true
|
||||||
|
m.errMsg = ""
|
||||||
|
return m, m.doTranslate(text, m.targetLang)
|
||||||
|
|
||||||
case tea.KeyCtrlC:
|
case tea.KeyCtrlC:
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
|
|
||||||
case tea.KeyCtrlL:
|
case tea.KeyCtrlL:
|
||||||
// Ctrl+L: 清空输入和结果
|
|
||||||
m.textInput.SetValue("")
|
m.textInput.SetValue("")
|
||||||
m.result = ""
|
m.result = ""
|
||||||
|
m.errMsg = ""
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|
||||||
case tea.KeyCtrlT:
|
case tea.KeyCtrlT:
|
||||||
// Ctrl+T: 切换语言
|
|
||||||
m.langIndex = (m.langIndex + 1) % len(supportedLangs)
|
m.langIndex = (m.langIndex + 1) % len(supportedLangs)
|
||||||
m.targetLang = supportedLangs[m.langIndex]
|
m.targetLang = supportedLangs[m.langIndex]
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|
||||||
case tea.KeyEsc:
|
case tea.KeyEsc:
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
@@ -97,6 +134,23 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m model) doTranslate(text, toLang string) tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
result, err := m.translator.Translate(
|
||||||
|
context.Background(),
|
||||||
|
text,
|
||||||
|
&translator.TranslateOptions{
|
||||||
|
ToLang: toLang,
|
||||||
|
PromptName: "simple",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return translateMsg{err: err}
|
||||||
|
}
|
||||||
|
return translateMsg{result: result.Translated}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m model) View() string {
|
func (m model) View() string {
|
||||||
resultBox := m.renderResult()
|
resultBox := m.renderResult()
|
||||||
helpText := helpStyle.Render("\n " +
|
helpText := helpStyle.Render("\n " +
|
||||||
@@ -116,6 +170,12 @@ func (m model) View() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m model) renderResult() string {
|
func (m model) renderResult() string {
|
||||||
|
if m.loading {
|
||||||
|
return " " + loadingStyle.Render("正在翻译...") + "\n"
|
||||||
|
}
|
||||||
|
if m.errMsg != "" {
|
||||||
|
return " " + errorStyle.Render("错误: "+m.errMsg) + "\n"
|
||||||
|
}
|
||||||
if m.result == "" {
|
if m.result == "" {
|
||||||
return " " + helpStyle.Render("翻译结果将显示在这里...") + "\n"
|
return " " + helpStyle.Render("翻译结果将显示在这里...") + "\n"
|
||||||
}
|
}
|
||||||
|
|||||||
53
memory.md
53
memory.md
@@ -643,4 +643,57 @@ func (m model) renderResult() string {
|
|||||||
}
|
}
|
||||||
return " " + resultStyle.Render(m.result) + "\n"
|
return " " + resultStyle.Render(m.result) + "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
### 异步命令和消息模式
|
||||||
|
```go
|
||||||
|
// 定义自定义消息类型
|
||||||
|
type translateMsg struct {
|
||||||
|
result string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步执行函数
|
||||||
|
func (m model) doTranslate(text string) tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
result, err := translate(text)
|
||||||
|
if err != nil {
|
||||||
|
return translateMsg{err: err}
|
||||||
|
}
|
||||||
|
return translateMsg{result: result}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update中处理消息
|
||||||
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case translateMsg:
|
||||||
|
if msg.err != nil {
|
||||||
|
m.errMsg = msg.err.Error()
|
||||||
|
} else {
|
||||||
|
m.result = msg.result
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 加载状态处理
|
||||||
|
```go
|
||||||
|
type model struct {
|
||||||
|
loading bool
|
||||||
|
errMsg string
|
||||||
|
}
|
||||||
|
|
||||||
|
// View中显示loading
|
||||||
|
func (m model) View() string {
|
||||||
|
if m.loading {
|
||||||
|
return "正在翻译..."
|
||||||
|
}
|
||||||
|
if m.errMsg != "" {
|
||||||
|
return "错误: " + m.errMsg
|
||||||
|
}
|
||||||
|
return m.result
|
||||||
|
}
|
||||||
|
```
|
||||||
```
|
```
|
||||||
39
taolun.md
39
taolun.md
@@ -543,5 +543,44 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
|
|
||||||
**下一步**: 实现模块6: 集成翻译
|
**下一步**: 实现模块6: 集成翻译
|
||||||
|
|
||||||
|
**关联文档**:
|
||||||
|
- [changelog.md#0.6.0](changelog.md#060)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### [2026-04-06 13:00] 版本 0.6.0 - 模块6: 集成翻译 (已完成)
|
||||||
|
**原因**: 将Translator集成到TUI,实现真正的翻译功能
|
||||||
|
**分析**:
|
||||||
|
- 需要在Enter键时调用翻译API
|
||||||
|
- 需要异步执行避免阻塞UI
|
||||||
|
- 需要显示loading状态和错误处理
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
1. 添加translateMsg消息类型处理异步结果
|
||||||
|
2. 添加loading和errMsg字段
|
||||||
|
3. 实现doTranslate()函数执行异步翻译
|
||||||
|
4. Update中处理translateMsg消息
|
||||||
|
5. View中显示loading状态或错误信息
|
||||||
|
|
||||||
|
**技术实现**:
|
||||||
|
```go
|
||||||
|
type translateMsg struct {
|
||||||
|
result string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m model) doTranslate(text, toLang string) tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
result, err := m.translator.Translate(...)
|
||||||
|
if err != nil {
|
||||||
|
return translateMsg{err: err}
|
||||||
|
}
|
||||||
|
return translateMsg{result: result.Translated}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**下一步**: 测试TUI界面、优化体验
|
||||||
|
|
||||||
**关联文档**:
|
**关联文档**:
|
||||||
- [changelog.md#0.6.0](changelog.md#060)
|
- [changelog.md#0.6.0](changelog.md#060)
|
||||||
Reference in New Issue
Block a user