feat: textarea布局优化 - 全宽/自适应高度(最多7行)/深色背景/窗口尺寸响应

This commit is contained in:
2026-04-06 05:50:11 +08:00
parent 7539987877
commit b35508e623
2 changed files with 68 additions and 25 deletions

View File

@@ -63,6 +63,7 @@
**改进内容**: **改进内容**:
- ✅ 模块7: 多行输入 - textarea组件替换textinput - ✅ 模块7: 多行输入 - textarea组件替换textinput
- ✅ 模块7补充: 布局和样式优化 - 全宽/自适应高度/深色背景
- ⏳ 模块8: 弹出框组件 - 通用modal - ⏳ 模块8: 弹出框组件 - 通用modal
- ⏳ 模块9: 斜杠命令菜单 - / 命令选择器 - ⏳ 模块9: 斜杠命令菜单 - / 命令选择器
- ⏳ 模块10: 翻译结果滚动 - viewport - ⏳ 模块10: 翻译结果滚动 - viewport

View File

@@ -2,6 +2,7 @@ package tui
import ( import (
"context" "context"
"strings"
"github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/bubbles/textarea"
"github.com/charmbracelet/bubbletea" "github.com/charmbracelet/bubbletea"
@@ -19,6 +20,8 @@ type model struct {
targetLang string targetLang string
langIndex int langIndex int
loading bool loading bool
width int
height int
} }
type translateMsg struct { type translateMsg struct {
@@ -32,10 +35,6 @@ var (
Bold(true) Bold(true)
dividerStyle = lipgloss.NewStyle(). dividerStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#00D9FF")) Foreground(lipgloss.Color("#00D9FF"))
inputStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#FAFAFA")).
Background(lipgloss.Color("#1A1A2E")).
Width(50)
resultStyle = lipgloss.NewStyle(). resultStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#98FB98")). Foreground(lipgloss.Color("#98FB98")).
Background(lipgloss.Color("#0D1B2A")) Background(lipgloss.Color("#0D1B2A"))
@@ -46,13 +45,10 @@ var (
Foreground(lipgloss.Color("#888888")) Foreground(lipgloss.Color("#888888"))
statusBarStyle = lipgloss.NewStyle(). statusBarStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF")). Foreground(lipgloss.Color("#FFFFFF")).
Background(lipgloss.Color("#1F2937")). Background(lipgloss.Color("#1F2937"))
Width(60)
langStyle = lipgloss.NewStyle(). langStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#FBBF24")). Foreground(lipgloss.Color("#FBBF24")).
Bold(true) Bold(true)
keyStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#60A5FA"))
loadingStyle = lipgloss.NewStyle(). loadingStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#60A5FA")) Foreground(lipgloss.Color("#60A5FA"))
) )
@@ -69,9 +65,12 @@ func NewApp(cfg *config.Config, t *translator.Translator) *tea.Program {
ta.Placeholder = "输入要翻译的文本..." ta.Placeholder = "输入要翻译的文本..."
ta.Focus() ta.Focus()
ta.Prompt = "" ta.Prompt = ""
ta.SetWidth(50)
ta.SetHeight(5)
ta.ShowLineNumbers = false ta.ShowLineNumbers = false
ta.SetHeight(3)
ta.FocusedStyle.Base = lipgloss.NewStyle().
Background(lipgloss.Color("#1A1A2E")).
Foreground(lipgloss.Color("#FAFAFA"))
return tea.NewProgram(model{ return tea.NewProgram(model{
config: cfg, config: cfg,
@@ -89,6 +88,11 @@ 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 tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
m.updateTextAreaWidth()
case translateMsg: case translateMsg:
m.loading = false m.loading = false
if msg.err != nil { if msg.err != nil {
@@ -98,6 +102,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.result = msg.result m.result = msg.result
m.errMsg = "" m.errMsg = ""
} }
m.updateTextAreaHeight()
return m, nil return m, nil
case tea.KeyMsg: case tea.KeyMsg:
@@ -114,10 +119,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.errMsg = "" m.errMsg = ""
return m, m.doTranslate(text, m.targetLang) return m, m.doTranslate(text, m.targetLang)
case tea.KeyCtrlJ:
m.textArea, cmd = m.textArea.Update(msg)
return m, cmd
case tea.KeyCtrlC: case tea.KeyCtrlC:
return m, tea.Quit return m, tea.Quit
@@ -138,9 +139,35 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} }
m.textArea, cmd = m.textArea.Update(msg) m.textArea, cmd = m.textArea.Update(msg)
m.updateTextAreaHeight()
return m, cmd return m, cmd
} }
func (m model) updateTextAreaWidth() {
if m.width > 0 {
margin := 4
width := m.width - margin
if width < 20 {
width = 60
}
m.textArea.SetWidth(width)
}
}
func (m model) updateTextAreaHeight() {
lines := strings.Count(m.textArea.Value(), "\n") + 1
if lines < 1 {
lines = 1
}
if lines > 7 {
lines = 7
}
if lines < 3 {
lines = 3
}
m.textArea.SetHeight(lines)
}
func (m model) doTranslate(text, toLang string) tea.Cmd { func (m model) doTranslate(text, toLang string) tea.Cmd {
return func() tea.Msg { return func() tea.Msg {
result, err := m.translator.Translate( result, err := m.translator.Translate(
@@ -159,34 +186,49 @@ func (m model) doTranslate(text, toLang string) tea.Cmd {
} }
func (m model) View() string { func (m model) View() string {
margin := " "
resultBox := m.renderResult() resultBox := m.renderResult()
return "\n" + return "\n" +
" " + headerStyle.Render("YOYO翻译") + "\n" + margin + headerStyle.Render("YOYO翻译") + "\n" +
" " + dividerStyle.Render("─────────────────────") + "\n\n" + margin + dividerStyle.Render(getDivider(m.width-2)) + "\n\n" +
m.textArea.View() + "\n\n" + margin + m.textArea.View() + "\n\n" +
resultBox + margin + resultBox +
margin + dividerStyle.Render(getDivider(m.width-2)) + "\n" +
m.renderStatusBar() m.renderStatusBar()
} }
func getDivider(width int) string {
if width < 10 {
width = 40
}
result := ""
for i := 0; i < width-4; i++ {
result += "─"
}
return result
}
func (m model) renderResult() string { func (m model) renderResult() string {
if m.loading { if m.loading {
return " " + loadingStyle.Render("正在翻译...") + "\n\n" return loadingStyle.Render("正在翻译...") + "\n\n"
} }
if m.errMsg != "" { if m.errMsg != "" {
return " " + errorStyle.Render("错误: "+m.errMsg) + "\n\n" return errorStyle.Render("错误: "+m.errMsg) + "\n\n"
} }
if m.result == "" { if m.result == "" {
return " " + helpStyle.Render("翻译结果将显示在这里...") + "\n\n" return helpStyle.Render("翻译结果将显示在这里...") + "\n\n"
} }
return " " + resultStyle.Render(m.result) + "\n\n" return resultStyle.Render(m.result) + "\n\n"
} }
func (m model) renderStatusBar() string { func (m model) renderStatusBar() string {
divider := dividerStyle.Render("─") width := m.width - 4
if width < 30 {
width = 60
}
langInfo := langStyle.Render("目标: " + m.targetLang) langInfo := langStyle.Render("目标: " + m.targetLang)
hint := helpStyle.Render("按 / 显示命令") hint := helpStyle.Render("按 / 显示命令")
return " " + divider + "\n" + return " " + statusBarStyle.Render(" "+langInfo+" ") + " " + hint
" " + statusBarStyle.Render(" "+langInfo+" ") + " " + hint
} }