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:
@@ -159,7 +159,10 @@ func (t *HostFormTab) Update(msg tea.Msg) (Tab, tea.Cmd) {
|
|||||||
t.toggleAuthType()
|
t.toggleAuthType()
|
||||||
return t, nil
|
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":
|
case "tab", "down":
|
||||||
t.nextField()
|
t.nextField()
|
||||||
@@ -356,7 +359,7 @@ func (t *HostFormTab) View() string {
|
|||||||
|
|
||||||
inner.WriteString("\n")
|
inner.WriteString("\n")
|
||||||
inner.WriteString(lipgloss.PlaceHorizontal(contentW, lipgloss.Center,
|
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())
|
box := BorderStyle.Render(inner.String())
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ func (t *KeyFormTab) View() string {
|
|||||||
|
|
||||||
inner.WriteString("\n")
|
inner.WriteString("\n")
|
||||||
inner.WriteString(lipgloss.PlaceHorizontal(contentW, lipgloss.Center,
|
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())
|
box := BorderStyle.Render(inner.String())
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
|
|||||||
@@ -229,6 +229,24 @@ func (t *KeyListTab) View() string {
|
|||||||
|
|
||||||
func (t *KeyListTab) Close() {}
|
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
|
// keyListLoadedMsg carries the loaded key list
|
||||||
type keyListLoadedMsg struct {
|
type keyListLoadedMsg struct {
|
||||||
keys []*models.KeyPair
|
keys []*models.KeyPair
|
||||||
|
|||||||
@@ -567,7 +567,7 @@ func (t *SFTPBrowserTab) View() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.WriteString("\n" + lipgloss.PlaceHorizontal(t.width, lipgloss.Center,
|
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()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ func (t *SnippetFormTab) View() string {
|
|||||||
|
|
||||||
inner.WriteString("\n")
|
inner.WriteString("\n")
|
||||||
inner.WriteString(lipgloss.PlaceHorizontal(contentW, lipgloss.Center,
|
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())
|
box := BorderStyle.Render(inner.String())
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
|
|||||||
@@ -229,6 +229,24 @@ func (t *SnippetListTab) View() string {
|
|||||||
|
|
||||||
func (t *SnippetListTab) Close() {}
|
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
|
// snippetListLoadedMsg carries the loaded snippet list
|
||||||
type snippetListLoadedMsg struct {
|
type snippetListLoadedMsg struct {
|
||||||
snippets []*models.Snippet
|
snippets []*models.Snippet
|
||||||
|
|||||||
+6
-1
@@ -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 {
|
if wsMsg, ok := msg.(tea.WindowSizeMsg); ok {
|
||||||
tm.SetSize(wsMsg.Width, wsMsg.Height)
|
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)
|
updated, cmd := tm.tabs[tm.active].Update(msg)
|
||||||
|
|||||||
+66
-3
@@ -151,33 +151,96 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.Error = msg.err
|
m.Error = msg.err
|
||||||
return m, nil
|
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 {
|
if m.tabs.Len() > 1 {
|
||||||
m.tabs.CloseActive()
|
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:
|
case deleteKeyResultMsg:
|
||||||
if msg.err != nil {
|
if msg.err != nil {
|
||||||
m.Error = msg.err
|
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
|
return m, nil
|
||||||
|
}
|
||||||
|
if kt := FindKeyListTab(m.tabs.tabs); kt != nil {
|
||||||
|
kt.SetKeys(msg.keys)
|
||||||
|
}
|
||||||
|
|
||||||
case saveSnippetResultMsg:
|
case saveSnippetResultMsg:
|
||||||
if msg.err != nil {
|
if msg.err != nil {
|
||||||
m.Error = msg.err
|
m.Error = msg.err
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
// Close form tab and switch back to list
|
||||||
if m.tabs.Len() > 1 {
|
if m.tabs.Len() > 1 {
|
||||||
m.tabs.CloseActive()
|
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:
|
case deleteSnippetResultMsg:
|
||||||
if msg.err != nil {
|
if msg.err != nil {
|
||||||
m.Error = msg.err
|
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
|
return m, nil
|
||||||
|
}
|
||||||
|
if st := FindSnippetListTab(m.tabs.tabs); st != nil {
|
||||||
|
st.SetSnippets(msg.snippets)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user