From 9105cba38054c5759cae45d936b67f46d8f15a3e Mon Sep 17 00:00:00 2001 From: Axodouble Date: Fri, 15 May 2026 00:40:01 +0000 Subject: [PATCH] Updated TUI field sizing --- internal/tui/forms.go | 29 +++++++++++++++++++++++++++-- internal/tui/tui.go | 25 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/internal/tui/forms.go b/internal/tui/forms.go index ef88911..015915a 100644 --- a/internal/tui/forms.go +++ b/internal/tui/forms.go @@ -63,10 +63,27 @@ type form struct { cursor int busy bool err string + width int // current terminal width; inputs resize to fill it submit func(values []string) tea.Cmd } +// defaultFieldWidth is the fallback input width used before the first +// WindowSizeMsg has arrived. Once we know the terminal size, inputs +// grow to fill the available horizontal space. +const defaultFieldWidth = 40 + +// fieldWidthFor derives the per-input visible width from the terminal +// width. It subtracts the modal's border+padding (6) and the form's +// label indent (2), then a couple of chars of safety margin. +func fieldWidthFor(termWidth int) int { + w := termWidth - 12 + if w < defaultFieldWidth { + return defaultFieldWidth + } + return w +} + func newForm(title string, fields []formField, submit func([]string) tea.Cmd) *form { for i := range fields { fields[i].input.Prompt = "" @@ -89,7 +106,7 @@ func textField(label, hint string, required bool) formField { // contents and can tweak instead of retyping everything. func textFieldWithValue(label, hint, value string, required bool) formField { ti := textinput.New() - ti.Width = 40 + ti.Width = defaultFieldWidth ti.Placeholder = hint if value != "" { ti.SetValue(value) @@ -106,7 +123,7 @@ func passwordField(label, hint string) formField { // the actual value leaking on-screen. func passwordFieldWithValue(label, hint, value string) formField { ti := textinput.New() - ti.Width = 40 + ti.Width = defaultFieldWidth ti.Placeholder = hint ti.EchoMode = textinput.EchoPassword ti.EchoCharacter = '•' @@ -148,6 +165,14 @@ func (f *form) View() string { func (f *form) Update(msg tea.Msg) (modal, tea.Cmd) { switch msg := msg.(type) { + case tea.WindowSizeMsg: + f.width = msg.Width + w := fieldWidthFor(msg.Width) + for i := range f.fields { + f.fields[i].input.Width = w + } + return f, nil + case formSubmitErr: f.busy = false f.err = string(msg) diff --git a/internal/tui/tui.go b/internal/tui/tui.go index d865d96..cdc933f 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -132,6 +132,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.WindowSizeMsg: m.width, m.height = msg.Width, msg.Height m.resizeTabs() + if m.modal != nil { + m.modal, _ = m.modal.Update(msg) + } return m, nil case tickMsg: @@ -175,8 +178,15 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Modal grabs all input while open. if m.modal != nil { + prev := m.modal newModal, cmd := m.modal.Update(msg) m.modal = newModal + // If the modal handed off to a different modal (e.g. picker → + // form), seed the new one with the current terminal size so its + // text inputs can size themselves on first paint. + if newModal != nil && newModal != prev { + m.seedModalSize() + } return m, cmd } @@ -223,6 +233,7 @@ func (m model) handleKey(km tea.KeyMsg) (tea.Model, tea.Cmd) { return m, tea.Batch(loadStatusCmd(), loadConfigCmd()) case "a": m.modal = m.openAddPicker() + m.seedModalSize() return m, nil case "d": return m.openRemoveConfirm() @@ -501,9 +512,20 @@ func (m model) openRemoveConfirm() (tea.Model, tea.Cmd) { return m, nil } m.modal = newConfirm(prompt, run) + m.seedModalSize() return m, nil } +// seedModalSize forwards the current terminal dimensions to the modal +// so its inputs can size themselves on first paint. Called whenever a +// new modal is installed. +func (m *model) seedModalSize() { + if m.modal == nil || m.width == 0 { + return + } + m.modal, _ = m.modal.Update(tea.WindowSizeMsg{Width: m.width, Height: m.height}) +} + // openEditForm dispatches to the right pre-filled edit form based on the // active tab and the row under the cursor. Looks up the full record in // m.peersFull / m.checksFull / m.alerts (populated by loadConfigCmd) so @@ -519,6 +541,7 @@ func (m model) openEditForm() (tea.Model, tea.Cmd) { for i := range m.peersFull { if m.peersFull[i].NodeID == id { m.modal = newEditNodeForm(m.peersFull[i]) + m.seedModalSize() return m, nil } } @@ -534,6 +557,7 @@ func (m model) openEditForm() (tea.Model, tea.Cmd) { for i := range m.checksFull { if m.checksFull[i].ID == id { m.modal = newEditCheckForm(m.checksFull[i]) + m.seedModalSize() return m, nil } } @@ -559,6 +583,7 @@ func (m model) openEditForm() (tea.Model, tea.Cmd) { m.setFlash("unsupported alert type", flashError) return m, nil } + m.seedModalSize() return m, nil } m.setFlash("alert not found in local cluster.yaml", flashError)