feat: textarea布局优化 - 全宽/自适应高度(最多7行)/深色背景/窗口尺寸响应
This commit is contained in:
@@ -63,6 +63,7 @@
|
|||||||
|
|
||||||
**改进内容**:
|
**改进内容**:
|
||||||
- ✅ 模块7: 多行输入 - textarea组件替换textinput
|
- ✅ 模块7: 多行输入 - textarea组件替换textinput
|
||||||
|
- ✅ 模块7补充: 布局和样式优化 - 全宽/自适应高度/深色背景
|
||||||
- ⏳ 模块8: 弹出框组件 - 通用modal
|
- ⏳ 模块8: 弹出框组件 - 通用modal
|
||||||
- ⏳ 模块9: 斜杠命令菜单 - / 命令选择器
|
- ⏳ 模块9: 斜杠命令菜单 - / 命令选择器
|
||||||
- ⏳ 模块10: 翻译结果滚动 - viewport
|
- ⏳ 模块10: 翻译结果滚动 - viewport
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user