fix: Space/Left/Right passthrough in host form, key/snippet refresh on save/delete, WindowSize to all tabs, footer improvements

This commit is contained in:
swanadiva
2026-06-23 18:48:50 +07:00
parent 9d0338595d
commit 7d41391e13
8 changed files with 118 additions and 11 deletions
+5 -2
View File
@@ -159,7 +159,10 @@ func (t *HostFormTab) Update(msg tea.Msg) (Tab, tea.Cmd) {
t.toggleAuthType()
return t, nil
}
fallthrough
// pass through to text input (allow typing spaces, cursor nav)
var cmd tea.Cmd
t.inputs[t.focus], cmd = t.inputs[t.focus].Update(msg)
return t, cmd
case "tab", "down":
t.nextField()
@@ -356,7 +359,7 @@ func (t *HostFormTab) View() string {
inner.WriteString("\n")
inner.WriteString(lipgloss.PlaceHorizontal(contentW, lipgloss.Center,
SubtitleStyle.Render("Ctrl+Tab:switch Ctrl+Q:close Tab:next Enter:next Ctrl+S:save Esc:cancel")))
SubtitleStyle.Render("Ctrl+Tab:switch Ctrl+Q:close Tab:next Shift+Tab:prev ↑↓:nav Enter:next Ctrl+S:save Esc:cancel")))
box := BorderStyle.Render(inner.String())
var b strings.Builder
+1 -1
View File
@@ -236,7 +236,7 @@ func (t *KeyFormTab) View() string {
inner.WriteString("\n")
inner.WriteString(lipgloss.PlaceHorizontal(contentW, lipgloss.Center,
SubtitleStyle.Render("Ctrl+Tab:switch Ctrl+Q:close Tab:next Enter:next Ctrl+S:save Esc:cancel")))
SubtitleStyle.Render("Ctrl+Tab:switch Ctrl+Q:close Tab:next Shift+Tab:prev ↑↓:nav Enter:next Ctrl+S:save Esc:cancel")))
box := BorderStyle.Render(inner.String())
var b strings.Builder
+18
View File
@@ -229,6 +229,24 @@ func (t *KeyListTab) View() string {
func (t *KeyListTab) Close() {}
// SetKeys updates the key list data directly (used for refresh)
func (t *KeyListTab) SetKeys(keys []*models.KeyPair) {
t.mu.Lock()
defer t.mu.Unlock()
t.state = keyListReady
t.keys = keys
}
// FindKeyListTab finds the first KeyListTab in a list of tabs
func FindKeyListTab(tabs []Tab) *KeyListTab {
for _, tab := range tabs {
if kt, ok := tab.(*KeyListTab); ok {
return kt
}
}
return nil
}
// keyListLoadedMsg carries the loaded key list
type keyListLoadedMsg struct {
keys []*models.KeyPair
+1 -1
View File
@@ -567,7 +567,7 @@ func (t *SFTPBrowserTab) View() string {
}
b.WriteString("\n" + lipgloss.PlaceHorizontal(t.width, lipgloss.Center,
SubtitleStyle.Render("Ctrl+Tab:switch Ctrl+Q:close Tab:pane ↑↓:nav /:filter C:copy D:delete N:mkdir R:refresh Esc:close")))
SubtitleStyle.Render("Ctrl+Tab:switch Ctrl+Q:close Tab:pane ↑↓:nav Enter/→:open ←/Backspace:up /:filter C:copy D:delete N:mkdir R:refresh Esc:close")))
return b.String()
}
+1 -1
View File
@@ -244,7 +244,7 @@ func (t *SnippetFormTab) View() string {
inner.WriteString("\n")
inner.WriteString(lipgloss.PlaceHorizontal(contentW, lipgloss.Center,
SubtitleStyle.Render("Ctrl+Tab:switch Ctrl+Q:close Tab:next Enter:next Ctrl+S:save Esc:cancel")))
SubtitleStyle.Render("Ctrl+Tab:switch Ctrl+Q:close Tab:next Shift+Tab:prev ↑↓:nav Enter:next Ctrl+S:save Esc:cancel")))
box := BorderStyle.Render(inner.String())
var b strings.Builder
+18
View File
@@ -229,6 +229,24 @@ func (t *SnippetListTab) View() string {
func (t *SnippetListTab) Close() {}
// SetSnippets updates the snippet list data directly (used for refresh)
func (t *SnippetListTab) SetSnippets(snippets []*models.Snippet) {
t.mu.Lock()
defer t.mu.Unlock()
t.state = snippetListReady
t.snippets = snippets
}
// FindSnippetListTab finds the first SnippetListTab in a list of tabs
func FindSnippetListTab(tabs []Tab) *SnippetListTab {
for _, tab := range tabs {
if st, ok := tab.(*SnippetListTab); ok {
return st
}
}
return nil
}
// snippetListLoadedMsg carries the loaded snippet list
type snippetListLoadedMsg struct {
snippets []*models.Snippet
+6 -1
View File
@@ -146,9 +146,14 @@ func (tm *TabManager) Update(msg tea.Msg) (tea.Cmd, error) {
}
}
// Handle window resize
// Handle window resize — forward to ALL tabs
if wsMsg, ok := msg.(tea.WindowSizeMsg); ok {
tm.SetSize(wsMsg.Width, wsMsg.Height)
for i, t := range tm.tabs {
updated, _ := t.Update(msg)
tm.tabs[i] = updated
}
return nil, nil
}
updated, cmd := tm.tabs[tm.active].Update(msg)
+66 -3
View File
@@ -151,33 +151,96 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.Error = msg.err
return m, nil
}
// Close form tab and return to list (which auto-refreshes via Init)
// Close form tab and switch back to list
if m.tabs.Len() > 1 {
m.tabs.CloseActive()
}
return m, nil
// Reload keys
return m, func() tea.Msg {
store, err := storage.NewJSONStorage(m.dataDir)
if err != nil {
return nil
}
keys, err := store.ListKeyPairs(context.Background())
if err != nil {
return nil
}
return keyListLoadedMsg{keys: keys}
}
case deleteKeyResultMsg:
if msg.err != nil {
m.Error = msg.err
}
// Reload keys
return m, func() tea.Msg {
store, err := storage.NewJSONStorage(m.dataDir)
if err != nil {
return nil
}
keys, err := store.ListKeyPairs(context.Background())
if err != nil {
return nil
}
return keyListLoadedMsg{keys: keys}
}
case keyListLoadedMsg:
if msg.err != nil {
m.Error = msg.err
return m, nil
}
if kt := FindKeyListTab(m.tabs.tabs); kt != nil {
kt.SetKeys(msg.keys)
}
case saveSnippetResultMsg:
if msg.err != nil {
m.Error = msg.err
return m, nil
}
// Close form tab and switch back to list
if m.tabs.Len() > 1 {
m.tabs.CloseActive()
}
return m, nil
// Reload snippets
return m, func() tea.Msg {
store, err := storage.NewJSONStorage(m.dataDir)
if err != nil {
return nil
}
snippets, err := store.ListSnippets(context.Background())
if err != nil {
return nil
}
return snippetListLoadedMsg{snippets: snippets}
}
case deleteSnippetResultMsg:
if msg.err != nil {
m.Error = msg.err
}
// Reload snippets
return m, func() tea.Msg {
store, err := storage.NewJSONStorage(m.dataDir)
if err != nil {
return nil
}
snippets, err := store.ListSnippets(context.Background())
if err != nil {
return nil
}
return snippetListLoadedMsg{snippets: snippets}
}
case snippetListLoadedMsg:
if msg.err != nil {
m.Error = msg.err
return m, nil
}
if st := FindSnippetListTab(m.tabs.tabs); st != nil {
st.SetSnippets(msg.snippets)
}
}