diff --git a/.gitignore b/.gitignore index c1e4501..b8026d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ sunhpc +testgui diff --git a/cmd/test/main.go b/cmd/test/main.go new file mode 100644 index 0000000..214d6fe --- /dev/null +++ b/cmd/test/main.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + + tea "github.com/charmbracelet/bubbletea" +) + +// model 定义应用的状态 +type model struct { + items []string // 列表数据 + selectedIdx int // 当前选中的索引 +} + +// Init 初始化模型,返回初始命令(这里不需要,返回 nil) +func (m model) Init() tea.Cmd { + return nil +} + +// Update 处理用户输入和状态更新 +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + // 处理键盘输入 + case tea.KeyMsg: + switch msg.String() { + // Tab 键:切换选中项(循环切换) + case "tab": + m.selectedIdx = (m.selectedIdx + 1) % len(m.items) + // Ctrl+C 或 q 键:退出程序 + case "ctrl+c", "q": + return m, tea.Quit + } + } + return m, nil +} + +// View 渲染界面 +func (m model) View() string { + s := "网络接口列表(按 Tab 切换选择,按 q 退出)\n\n" + + // 遍历列表项,渲染每一项 + for i, item := range m.items { + // 标记当前选中的项 + if i == m.selectedIdx { + s += fmt.Sprintf("→ %s (选中)\n", item) + } else { + s += fmt.Sprintf(" %s\n", item) + } + } + + return s +} + +func main() { + // 初始化模型,设置列表数据 + initialModel := model{ + items: []string{"eth0", "eth1", "eth2", "eth3"}, + selectedIdx: 0, // 默认选中第一个项 + } + + // 启动 Bubble Tea 程序 + p := tea.NewProgram(initialModel) + if _, err := p.Run(); err != nil { + fmt.Printf("程序运行出错: %v\n", err) + } +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 9a0852e..4943824 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -31,3 +31,8 @@ func GenerateID() (string, error) { func GetTimestamp() string { return time.Now().Format("2006-01-02 15:04:05") } + +// 定义短语 +const ( + NoAvailableNetworkInterfaces = "No available network interfaces" +) diff --git a/pkg/wizard/config.go b/pkg/wizard/config.go index a2e97c9..f3bdde0 100644 --- a/pkg/wizard/config.go +++ b/pkg/wizard/config.go @@ -3,8 +3,10 @@ package wizard import ( "encoding/json" "fmt" + "net" "os" "path/filepath" + "sunhpc/pkg/utils" "github.com/charmbracelet/bubbles/textinput" ) @@ -66,31 +68,30 @@ func (m *model) saveCurrentPage() { func (m *model) saveDataPage() { if len(m.textInputs) >= 8 { - m.config.Hostname = m.textInputs[0].Value() - m.config.Country = m.textInputs[1].Value() - m.config.Region = m.textInputs[2].Value() - m.config.Timezone = m.textInputs[3].Value() - m.config.HomePage = m.textInputs[4].Value() + m.config.HomePage = m.textInputs[0].Value() + m.config.Hostname = m.textInputs[1].Value() + m.config.Country = m.textInputs[2].Value() + m.config.Region = m.textInputs[3].Value() + m.config.Timezone = m.textInputs[4].Value() m.config.DBAddress = m.textInputs[5].Value() - m.config.DBName = m.textInputs[6].Value() - m.config.DataAddress = m.textInputs[7].Value() + m.config.DataAddress = m.textInputs[6].Value() } } func (m *model) savePublicNetworkPage() { if len(m.textInputs) >= 4 { m.config.PublicInterface = m.textInputs[0].Value() - m.config.IPAddress = m.textInputs[1].Value() - m.config.Netmask = m.textInputs[2].Value() - m.config.Gateway = m.textInputs[3].Value() + m.config.PublicIPAddress = m.textInputs[1].Value() + m.config.PublicNetmask = m.textInputs[2].Value() + m.config.PublicGateway = m.textInputs[3].Value() } } func (m *model) saveInternalNetworkPage() { if len(m.textInputs) >= 3 { m.config.InternalInterface = m.textInputs[0].Value() - m.config.InternalIP = m.textInputs[1].Value() - m.config.InternalMask = m.textInputs[2].Value() + m.config.InternalIPAddress = m.textInputs[1].Value() + m.config.InternalNetmask = m.textInputs[2].Value() } } @@ -108,20 +109,18 @@ func (m *model) initPageInputs() { switch m.currentPage { case PageData: fields := []struct{ label, value string }{ - {"主机名:", m.config.Hostname}, - {"国家:", m.config.Country}, - {"地区:", m.config.Region}, - {"时区:", m.config.Timezone}, - {"主页:", m.config.HomePage}, - {"数据库地址:", m.config.DBAddress}, - {"数据库名称:", m.config.DBName}, - {"Data 地址:", m.config.DataAddress}, + {"Homepage", m.config.HomePage}, + {"Hostname", m.config.Hostname}, + {"Country", m.config.Country}, + {"Region", m.config.Region}, + {"Timezone", m.config.Timezone}, + {"DB Path", m.config.DBAddress}, + {"Software", m.config.DataAddress}, } for _, f := range fields { ti := textinput.New() ti.Placeholder = "" ti.Placeholder = f.label - //ti.Placeholder = "请输入" + f.label[:len(f.label)-1] ti.SetValue(f.value) ti.Width = 50 m.textInputs = append(m.textInputs, ti) @@ -130,18 +129,19 @@ func (m *model) initPageInputs() { if len(m.textInputs) > 0 { m.textInputs[0].Focus() } - m.inputLabels = []string{"Hostname", "Country", "Region", "Timezone", "Homepage", "DBPath", "DBName", "Software"} + m.inputLabels = []string{"Homepage", "Hostname", "Country", "Region", "Timezone", "DBPath", "Software"} case PagePublicNetwork: fields := []struct{ label, value string }{ - {"公网接口:", m.config.PublicInterface}, - {"IP 地址:", m.config.IPAddress}, - {"子网掩码:", m.config.Netmask}, - {"网关:", m.config.Gateway}, + {"Public Interface", m.config.PublicInterface}, + {"Public IP Address", m.config.PublicIPAddress}, + {"Public Netmask", m.config.PublicNetmask}, + {"Public Gateway", m.config.PublicGateway}, } for _, f := range fields { ti := textinput.New() ti.Placeholder = "" + ti.Placeholder = f.label ti.SetValue(f.value) ti.Width = 50 m.textInputs = append(m.textInputs, ti) @@ -150,17 +150,17 @@ func (m *model) initPageInputs() { if len(m.textInputs) > 0 { m.textInputs[0].Focus() } - m.inputLabels = []string{"Wan iface", "IPAddress", "Netmask", "Gateway"} + m.inputLabels = []string{"Public Interface", "Public IP Address", "Public Netmask", "Public Gateway"} case PageInternalNetwork: fields := []struct{ label, value string }{ - {"内网接口:", m.config.InternalInterface}, - {"内网 IP:", m.config.InternalIP}, - {"内网掩码:", m.config.InternalMask}, + {"Internal Interface", m.config.InternalInterface}, + {"Internal IP Address", m.config.InternalIPAddress}, + {"Internal Netmask", m.config.InternalNetmask}, } for _, f := range fields { ti := textinput.New() - ti.Placeholder = "" + ti.Placeholder = f.label ti.SetValue(f.value) ti.Width = 50 m.textInputs = append(m.textInputs, ti) @@ -169,16 +169,16 @@ func (m *model) initPageInputs() { if len(m.textInputs) > 0 { m.textInputs[0].Focus() } - m.inputLabels = []string{"Lan iface", "IPAddress", "Netmask"} + m.inputLabels = []string{"Internal Interface", "Internal IP", "Internal Mask"} case PageDNS: fields := []struct{ label, value string }{ - {"主 DNS:", m.config.DNSPrimary}, - {"备 DNS:", m.config.DNSSecondary}, + {"Primary DNS", m.config.DNSPrimary}, + {"Secondary DNS", m.config.DNSSecondary}, } for _, f := range fields { ti := textinput.New() - ti.Placeholder = "" + ti.Placeholder = f.label ti.SetValue(f.value) ti.Width = 50 m.textInputs = append(m.textInputs, ti) @@ -187,6 +187,31 @@ func (m *model) initPageInputs() { if len(m.textInputs) > 0 { m.textInputs[0].Focus() } - m.inputLabels = []string{"DNSPrimary", "DNSSecondary"} + m.inputLabels = []string{"Pri DNS", "Sec DNS"} } } + +// 获取系统网络接口 +func getNetworkInterfaces() []string { + // 实现获取系统网络接口的逻辑 + // 例如:使用 net.Interface() 函数获取系统网络接口 + // 返回一个字符串切片,包含系统网络接口的名称 + interfaces, err := net.Interfaces() + if err != nil { + return []string{utils.NoAvailableNetworkInterfaces} + } + + var result []string + for _, iface := range interfaces { + // 跳过 loopback 接口 + if iface.Flags&net.FlagLoopback != 0 { + continue + } + result = append(result, iface.Name) + } + + if len(result) == 0 { + return []string{utils.NoAvailableNetworkInterfaces} + } + return result +} diff --git a/pkg/wizard/model.go b/pkg/wizard/model.go index d1305e0..9faf11e 100644 --- a/pkg/wizard/model.go +++ b/pkg/wizard/model.go @@ -1,6 +1,8 @@ package wizard import ( + "fmt" + "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" ) @@ -17,19 +19,18 @@ type Config struct { Timezone string `json:"timezone"` HomePage string `json:"homepage"` DBAddress string `json:"db_address"` - DBName string `json:"db_name"` DataAddress string `json:"data_address"` // 公网设置 PublicInterface string `json:"public_interface"` - IPAddress string `json:"ip_address"` - Netmask string `json:"netmask"` - Gateway string `json:"gateway"` + PublicIPAddress string `json:"ip_address"` + PublicNetmask string `json:"netmask"` + PublicGateway string `json:"gateway"` // 内网配置 InternalInterface string `json:"internal_interface"` - InternalIP string `json:"internal_ip"` - InternalMask string `json:"internal_mask"` + InternalIPAddress string `json:"internal_ip"` + InternalNetmask string `json:"internal_mask"` // DNS 配置 DNSPrimary string `json:"dns_primary"` @@ -56,40 +57,60 @@ const ( // model TUI 主模型 type model struct { - config Config - currentPage PageType - totalPages int - textInputs []textinput.Model - inputLabels []string // 存储标签 - focusIndex int - focusType int // 0=输入框, 1=上一步按钮, 2=下一步按钮 - agreementIdx int // 0=拒绝,1=接受 - width int - height int - err error - quitting bool - done bool - force bool + config Config + currentPage PageType + totalPages int + networkInterfaces []string // 所有系统网络接口 + textInputs []textinput.Model + inputLabels []string // 存储标签 + focusIndex int + focusType int // 0=输入框, 1=上一步按钮, 2=下一步按钮 + agreementIdx int // 0=拒绝,1=接受 + width int + height int + err error + quitting bool + done bool + force bool } // defaultConfig 返回默认配置 func defaultConfig() Config { + var ( + defaultPublicInterface string + defaultInternalInterface string + ) + interfaces := getNetworkInterfaces() + switch len(interfaces) { + case 0: + defaultPublicInterface = "" + defaultInternalInterface = "" + case 1: + defaultPublicInterface = interfaces[0] + defaultInternalInterface = fmt.Sprintf("%s:0", interfaces[0]) + case 2: + defaultPublicInterface = interfaces[0] + defaultInternalInterface = interfaces[1] + default: + defaultPublicInterface = interfaces[0] + defaultInternalInterface = interfaces[1] + } + return Config{ - Hostname: "sunhpc01", + Hostname: "cluster.hpc.org", Country: "China", Region: "Beijing", Timezone: "Asia/Shanghai", - HomePage: "https://sunhpc.example.com", - DBAddress: "127.0.0.1", - DBName: "sunhpc_db", - DataAddress: "/data/sunhpc", - PublicInterface: "eth0", - InternalInterface: "eth1", - IPAddress: "192.168.1.100", - Netmask: "255.255.255.0", - Gateway: "192.168.1.1", - InternalIP: "10.0.0.100", - InternalMask: "255.255.255.0", + HomePage: "www.sunhpc.com", + DBAddress: "/var/lib/sunhpc/sunhpc.db", + DataAddress: "/export/sunhpc", + PublicInterface: defaultPublicInterface, + PublicIPAddress: "", + PublicNetmask: "", + PublicGateway: "", + InternalInterface: defaultInternalInterface, + InternalIPAddress: "172.16.9.254", + InternalNetmask: "255.255.255.0", DNSPrimary: "8.8.8.8", DNSSecondary: "8.8.4.4", } diff --git a/pkg/wizard/pages.go b/pkg/wizard/pages.go index f9ca2d0..93c91cf 100644 --- a/pkg/wizard/pages.go +++ b/pkg/wizard/pages.go @@ -45,43 +45,39 @@ func (m model) View() string { // agreementView 协议页面 func (m model) agreementView() string { - title := titleStyle.Render("SunHPC 系统初始化向导") - subtitle := subTitleStyle.Render("请先阅读并同意以下协议") + title := titleStyle.Render("SunHPC Software License Agreement") agreement := agreementBox.Render(` ┌─────────────────────────────────────────────────────────────┐ - │ SunHPC 软件许可协议 │ + │ SunHPC License Agreement │ └─────────────────────────────────────────────────────────────┘ - - 1. 许可授予 - 本软件授予您非独占、不可转让的使用许可。 - - 2. 使用限制 - - 不得用于非法目的 - - 不得反向工程或反编译 - - 不得移除版权标识 - - 3. 免责声明 - 本软件按"原样"提供,不提供任何明示或暗示的保证。 - - 4. 责任限制 - 在任何情况下,作者不对因使用本软件造成的任何损失负责。 - - 5. 协议终止 - 如违反本协议条款,许可将自动终止。 - + 1. License Grant + This software grants you a non-exclusive, non-transferable + license to use it. + 2. Disclaimer of Warranties + This software is provided "as is" without any warranties, + whether express or implied. + 3. Limitation of Liability + This software is provided without warranty of any kind, + either express or implied. + In no event shall the author be liable for any damages + arising out of the use of this software. + 4. Termination of Agreement + If you violate any of the terms of this agreement, + the license will automatically terminate. ─────────────────────────────────────────────────────────────── - 请仔细阅读以上条款,点击"接受"表示您同意并遵守本协议。 + PLEASE READ THE ABOVE TERMS CAREFULLY AND CLICK "ACCEPT" + TO AGREE AND FOLLOW THIS AGREEMENT. ─────────────────────────────────────────────────────────────── `) var acceptBtn, rejectBtn string if m.agreementIdx == 0 { - rejectBtn = selectedButton.Render(">> 拒绝 <<") - acceptBtn = selectedButton.Render(" 同意 ") + rejectBtn = selectedButton.Render(">> Reject <<") + acceptBtn = " Accept " } else { - rejectBtn = selectedButton.Render(" 拒绝 ") - acceptBtn = selectedButton.Render(">> 同意 <<") + rejectBtn = " Reject " + acceptBtn = selectedButton.Render(">> Accept <<") } buttonGroup := lipgloss.JoinHorizontal( @@ -92,11 +88,9 @@ func (m model) agreementView() string { // debugInfo := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")). // Render(fmt.Sprintf("[DEBUG: idx=%d]", m.agreementIdx),) - hint := hintStyle.Render("使用 <- -> 或 Tab 选择,Enter 确认") - + hint := hintStyle.Render("Use Up/Down OR Tab Change,Enter Confirm") return lipgloss.JoinVertical(lipgloss.Center, title, "", - subtitle, "", agreement, "", buttonGroup, "", // debugInfo, "", // ✅ 显示调试信息 @@ -106,8 +100,7 @@ func (m model) agreementView() string { // dataView 数据接收页面 func (m model) dataView() string { - title := titleStyle.Render("集群基础配置") - subtitle := subTitleStyle.Render("请填写系统基本信息") + title := titleStyle.Render("Cluster Information") var inputs strings.Builder for i, ti := range m.textInputs { @@ -117,11 +110,9 @@ func (m model) dataView() string { } buttons := m.renderNavButtons() - - hint := hintStyle.Render("使用 Up/Down 或 Tab 切换、Enter 确认") + hint := hintStyle.Render("Use Up/Down OR Tab Change,Enter Confirm") return lipgloss.JoinVertical(lipgloss.Center, title, "", - subtitle, "", inputs.String(), "", buttons, "", hint, @@ -130,25 +121,24 @@ func (m model) dataView() string { // publicNetworkView 公网设置页面 func (m model) publicNetworkView() string { - title := titleStyle.Render("公网配置") - subtitle := subTitleStyle.Render("请配置网络接口信息") + title := titleStyle.Render("Public Network Configuration") - autoDetect := infoStyle.Render("[*] 自动检测网络接口: eth0, eth1, ens33") + networkInterfaces := getNetworkInterfaces() + autoDetect := infoStyle.Render( + "[*] Auto Detect Network Interfaces: " + strings.Join(networkInterfaces, ", ")) var inputs strings.Builder for i, ti := range m.textInputs { - info := fmt.Sprintf("%-10s|", m.inputLabels[i]) + info := fmt.Sprintf("%-20s|", m.inputLabels[i]) input := inputBox.Render(info + ti.View()) inputs.WriteString(input + "\n") } buttons := m.renderNavButtons() - - hint := hintStyle.Render("使用 Up/Down 或 Tab 切换、Enter 确认") + hint := hintStyle.Render("Use Up/Down OR Tab Change,Enter Confirm") return lipgloss.JoinVertical(lipgloss.Center, title, "", - subtitle, "", autoDetect, "", inputs.String(), "", buttons, "", @@ -158,22 +148,25 @@ func (m model) publicNetworkView() string { // internalNetworkView 内网配置页面 func (m model) internalNetworkView() string { - title := titleStyle.Render("内网配置") - subtitle := subTitleStyle.Render("请配置内网信息") + title := titleStyle.Render("Internal Network Configuration") + + networkInterfaces := getNetworkInterfaces() + autoDetect := infoStyle.Render( + "[*] Auto Detect Network Interfaces: " + strings.Join(networkInterfaces, ", ")) var inputs strings.Builder for i, ti := range m.textInputs { - info := fmt.Sprintf("%-10s|", m.inputLabels[i]) + info := fmt.Sprintf("%-20s|", m.inputLabels[i]) input := inputBox.Render(info + ti.View()) inputs.WriteString(input + "\n") } buttons := m.renderNavButtons() - hint := hintStyle.Render("使用 Up/Down 或 Tab 切换、Enter 确认") + hint := hintStyle.Render("Use Up/Down OR Tab Change,Enter Confirm") return lipgloss.JoinVertical(lipgloss.Center, title, "", - subtitle, "", + autoDetect, "", inputs.String(), "", buttons, "", hint, @@ -182,8 +175,7 @@ func (m model) internalNetworkView() string { // dnsView DNS 配置页面 func (m model) dnsView() string { - title := titleStyle.Render("DNS 配置") - subtitle := subTitleStyle.Render("请配置 DNS 服务器") + title := titleStyle.Render("DNS Configuration") var inputs strings.Builder for i, ti := range m.textInputs { @@ -193,12 +185,10 @@ func (m model) dnsView() string { } buttons := m.renderNavButtons() - - hint := hintStyle.Render("使用 Up/Down 或 Tab 切换、Enter 确认") + hint := hintStyle.Render("Use Up/Down OR Tab Change,Enter Confirm") return lipgloss.JoinVertical(lipgloss.Center, title, "", - subtitle, "", inputs.String(), "", buttons, "", hint, @@ -207,71 +197,71 @@ func (m model) dnsView() string { // summaryView 总结页面 func (m model) summaryView() string { - title := titleStyle.Render("配置总结") - subtitle := subTitleStyle.Render("请确认以下配置信息") + title := titleStyle.Render("Summary") + subtitle := subTitleStyle.Render("Please confirm the following configuration information") summary := summaryBox.Render(fmt.Sprintf(` -+---------------------------------------------+ - 基本信息 -+---------------------------------------------+ - 主机名:%-35s - 国 家: %-31s - 地 区:%-31s - 时 区:%-38s - 主 页:%-38s -+---------------------------------------------+ - 数据库 -+---------------------------------------------+ - 地 址:%-38s - 名 称:%-38s - 软 件:%-33s -+---------------------------------------------+ - 公网配置 -+---------------------------------------------+ - 接 口:%-38s - 地 址: %-41s - 掩 码:%-38s - 网 关:%-38s -+---------------------------------------------+ - 内网配置 -+---------------------------------------------+ - 接 口:%-38s - 地 址: %-41s - 掩 码:%-38s -+---------------------------------------------+ - DNS -+---------------------------------------------+ - 主 DNS: %-37s - 备 DNS: %-37s -+---------------------------------------------+ ++----------------------------------------------------+ + Basic Information ++----------------------------------------------------+ + Homepage : %-38s + Hostname : %-35s + Country : %-31s + Region : %-31s + Timezone : %-38s + Homepage : %-38s ++----------------------------------------------------+ + Database Configuration ++----------------------------------------------------+ + Database Path : %-38s + Software : %-33s ++----------------------------------------------------+ + Public Network Configuration ++----------------------------------------------------+ + Public Interface : %-38s + Public IP : %-41s + Public Netmask : %-38s + Public Gateway : %-38s ++----------------------------------------------------+ + Internal Network Configuration ++----------------------------------------------------+ + Internal Interface: %-38s + Internal IP : %-41s + Internal Netmask : %-38s ++----------------------------------------------------+ + DNS Configuration ++----------------------------------------------------+ + Primary DNS : %-37s + Secondary DNS : %-37s ++----------------------------------------------------+ `, + m.config.HomePage, m.config.Hostname, m.config.Country, m.config.Region, m.config.Timezone, m.config.HomePage, m.config.DBAddress, - m.config.DBName, m.config.DataAddress, m.config.PublicInterface, - m.config.IPAddress, - m.config.Netmask, - m.config.Gateway, + m.config.PublicIPAddress, + m.config.PublicNetmask, + m.config.PublicGateway, m.config.InternalInterface, - m.config.InternalIP, - m.config.InternalMask, + m.config.InternalIPAddress, + m.config.InternalNetmask, m.config.DNSPrimary, m.config.DNSSecondary, )) var buttons string if m.focusIndex == 0 { - buttons = selectedButton.Render("[>] 执行初始化") + " " + normalButton.Render("[ ] 取消") + buttons = selectedButton.Render("[>] Start Initialization") + " " + normalButton.Render("[ ] Cancel") } else { - buttons = normalButton.Render("[>] 执行初始化") + " " + selectedButton.Render("[ ] 取消") + buttons = normalButton.Render("[>] Start Initialization") + " " + selectedButton.Render("[ ] Cancel") } - hint := hintStyle.Render("使用 <- -> 或 Tab 选择,Enter 确认") + hint := hintStyle.Render("Use Up/Down OR Tab Change,Enter Confirm") return lipgloss.JoinVertical(lipgloss.Center, title, "", @@ -297,7 +287,7 @@ func progressView(current PageType, total int) string { progress += " " } } - labels := []string{"协议", "数据", "公网", "内网", "DNS", "总结"} + labels := []string{"License", "Data", "Network", "Network", "DNS", "Summary"} label := labelStyle.Render(labels[current]) return progressStyle.Render(progress) + " " + label } @@ -305,26 +295,26 @@ func progressView(current PageType, total int) string { // successView 成功视图 func successView() string { return containerStyle.Render(lipgloss.JoinVertical(lipgloss.Center, - successTitle.Render("初始化完成!"), "", - successMsg.Render("系统配置已保存,正在初始化..."), "", - hintStyle.Render("按任意键退出"), + successTitle.Render("Initialization Completed!"), "", + successMsg.Render("System configuration has been saved, and the system is initializing..."), "", + hintStyle.Render("Press any key to exit"), )) } // quitView 退出视图 func quitView() string { return containerStyle.Render(lipgloss.JoinVertical(lipgloss.Center, - errorTitle.Render("已取消"), "", - errorMsg.Render("初始化已取消,未保存任何配置"), + errorTitle.Render("Canceled"), "", + errorMsg.Render("Initialization canceled, no configuration saved"), )) } // errorView 错误视图 func errorView(err error) string { return containerStyle.Render(lipgloss.JoinVertical(lipgloss.Center, - errorTitle.Render("错误"), "", + errorTitle.Render("Error"), "", errorMsg.Render(err.Error()), "", - hintStyle.Render("按 Ctrl+C 退出"), + hintStyle.Render("Press Ctrl+C to exit"), )) } @@ -332,9 +322,9 @@ func errorView(err error) string { func navButtons(m model, prev, next string) string { var btns string if m.currentPage == 0 { - btns = normalButton.Render(prev) + " " + selectedButton.Render(next) + btns = normalButton.Render(next) + " " + selectedButton.Render(prev) } else { - btns = selectedButton.Render(prev) + " " + normalButton.Render(next) + btns = selectedButton.Render(next) + " " + normalButton.Render(prev) } return btns } @@ -345,22 +335,22 @@ func (m model) renderNavButtons() string { switch m.focusType { case FocusTypePrev: // 焦点在"上一步" - prevBtn = selectedButton.Render("<< 上一步 >>") - nextBtn = normalButton.Render("下一步 >>") + nextBtn = normalButton.Render(" Next ") + prevBtn = selectedButton.Render(" << Prev >>") case FocusTypeNext: // 焦点在"下一步" - prevBtn = normalButton.Render("<< 上一步") - nextBtn = selectedButton.Render("<< 下一步 >>") + nextBtn = selectedButton.Render(" << Next >>") + prevBtn = normalButton.Render(" Prev ") default: // 焦点在输入框 - prevBtn = normalButton.Render("<< 上一步") - nextBtn = normalButton.Render("下一步 >>") + nextBtn = normalButton.Render(" Next ") + prevBtn = normalButton.Render(" Prev ") } return lipgloss.JoinHorizontal( lipgloss.Center, - prevBtn, - " ", nextBtn, + " ", + prevBtn, ) } diff --git a/pkg/wizard/styles.go b/pkg/wizard/styles.go index 3350e2d..b7bcd94 100644 --- a/pkg/wizard/styles.go +++ b/pkg/wizard/styles.go @@ -41,8 +41,6 @@ var normalButton = lipgloss.NewStyle(). Foreground(lipgloss.Color("#666666")) // 深灰色,更暗 var selectedButton = lipgloss.NewStyle(). - Padding(0, 2). - Foreground(lipgloss.Color("#3d4747ff")). // 亮绿色 Bold(true) // 输入框样式