@@ -98,6 +98,11 @@ type App struct {
9898 // Connection history
9999 connectionHistory * connection_history.Manager
100100
101+ // Password dialog for missing passwords
102+ showPasswordDialog bool
103+ passwordDialog * components.PasswordDialog
104+ pendingConnectionInfo * models.ConnectionHistoryEntry
105+
101106 // Search input
102107 showSearch bool
103108 searchInput * components.SearchInput
@@ -330,6 +335,7 @@ func New(cfg *config.Config) *App {
330335 favoritesManager : favoritesManager ,
331336 favoritesDialog : favoritesDialog ,
332337 connectionHistory : connectionHistory ,
338+ passwordDialog : components .NewPasswordDialog (th ),
333339 showSearch : false ,
334340 searchInput : searchInput ,
335341 executeSpinner : s ,
@@ -588,6 +594,34 @@ func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
588594 a .showError = false
589595 return a , nil
590596
597+ case components.PasswordSubmitMsg :
598+ // User submitted password from password dialog
599+ a .showPasswordDialog = false
600+ if a .pendingConnectionInfo != nil {
601+ // Create connection config with the entered password
602+ config := a .pendingConnectionInfo .ToConnectionConfig ()
603+ config .Password = msg .Password
604+
605+ // Try to save the password for future use
606+ if a .connectionHistory != nil {
607+ if err := a .connectionHistory .SavePassword (config .Host , config .Port , config .Database , config .User , config .Password ); err != nil {
608+ log .Printf ("Warning: Failed to save password: %v" , err )
609+ }
610+ }
611+
612+ a .pendingConnectionInfo = nil
613+ return a .performConnection (config )
614+ }
615+ return a , nil
616+
617+ case components.PasswordCancelMsg :
618+ // User cancelled password dialog
619+ a .showPasswordDialog = false
620+ a .pendingConnectionInfo = nil
621+ // Re-show connection dialog
622+ a .showConnectionDialog = true
623+ return a , nil
624+
591625 case components.CloseCommandPaletteMsg :
592626 a .showCommandPalette = false
593627 return a , nil
@@ -785,6 +819,13 @@ func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
785819 return a .handleConnectionDialog (msg )
786820 }
787821
822+ // Handle password dialog if visible
823+ if a .showPasswordDialog {
824+ var cmd tea.Cmd
825+ a .passwordDialog , cmd = a .passwordDialog .Update (msg )
826+ return a , cmd
827+ }
828+
788829 // Handle command palette if visible
789830 if a .showCommandPalette {
790831 return a .handleCommandPalette (msg )
@@ -1726,6 +1767,11 @@ func (a *App) View() string {
17261767 return zone .Scan (a .renderConnectionDialog ())
17271768 }
17281769
1770+ // If password dialog is showing, render it
1771+ if a .showPasswordDialog {
1772+ return zone .Scan (a .renderPasswordDialog ())
1773+ }
1774+
17291775 // If in help mode, show help overlay
17301776 if a .state .ViewMode == models .HelpMode {
17311777 return help .Render (a .state .Width , a .state .Height , lipgloss .NewStyle ())
@@ -2749,7 +2795,18 @@ func (a *App) connectToHistoryEntry(entry models.ConnectionHistoryEntry) (tea.Mo
27492795
27502796 // Convert history entry to connection config WITH password from keyring
27512797 if a .connectionHistory != nil {
2752- config = a .connectionHistory .GetConnectionConfigWithPassword (& entry )
2798+ result := a .connectionHistory .GetConnectionConfigWithPassword (& entry )
2799+ config = result .Config
2800+
2801+ // If password is missing, show password dialog
2802+ if result .PasswordMissing {
2803+ entryCopy := entry
2804+ a .pendingConnectionInfo = & entryCopy
2805+ a .passwordDialog .SetConnectionInfo (entry .Host , entry .Port , entry .Database , entry .User )
2806+ a .showPasswordDialog = true
2807+ a .showConnectionDialog = false
2808+ return a , a .passwordDialog .Init ()
2809+ }
27532810 } else {
27542811 config = entry .ToConnectionConfig ()
27552812 }
@@ -2799,9 +2856,13 @@ func (a *App) performConnection(config models.ConnectionConfig) (tea.Model, tea.
27992856
28002857 // Save to connection history (ignore errors)
28012858 if a .connectionHistory != nil {
2802- if err := a .connectionHistory .Add (config ); err != nil {
2859+ result , err := a .connectionHistory .Add (config )
2860+ if err != nil {
28032861 log .Printf ("Warning: Failed to save connection to history: %v" , err )
28042862 } else {
2863+ if result != nil && result .PasswordSaveError != nil {
2864+ log .Printf ("Warning: Failed to save password: %v" , result .PasswordSaveError )
2865+ }
28052866 // Reload history in dialog
28062867 history := a .connectionHistory .GetRecent (10 )
28072868 a .connectionDialog .SetHistoryEntries (history )
@@ -2965,9 +3026,13 @@ func (a *App) handleConnectionDialog(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
29653026
29663027 // Save to connection history (ignore errors)
29673028 if a .connectionHistory != nil {
2968- if err := a .connectionHistory .Add (config ); err != nil {
3029+ result , err := a .connectionHistory .Add (config )
3030+ if err != nil {
29693031 log .Printf ("Warning: Failed to save connection to history: %v" , err )
29703032 } else {
3033+ if result != nil && result .PasswordSaveError != nil {
3034+ log .Printf ("Warning: Failed to save password: %v" , result .PasswordSaveError )
3035+ }
29713036 // Reload history in dialog
29723037 history := a .connectionHistory .GetRecent (10 )
29733038 a .connectionDialog .SetHistoryEntries (history )
@@ -2993,7 +3058,18 @@ func (a *App) handleConnectionDialog(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
29933058
29943059 // Convert history entry to connection config WITH password from keyring
29953060 if a .connectionHistory != nil {
2996- config = a .connectionHistory .GetConnectionConfigWithPassword (historyEntry )
3061+ result := a .connectionHistory .GetConnectionConfigWithPassword (historyEntry )
3062+ config = result .Config
3063+
3064+ // If password is missing, show password dialog
3065+ if result .PasswordMissing {
3066+ entryCopy := * historyEntry
3067+ a .pendingConnectionInfo = & entryCopy
3068+ a .passwordDialog .SetConnectionInfo (historyEntry .Host , historyEntry .Port , historyEntry .Database , historyEntry .User )
3069+ a .showPasswordDialog = true
3070+ a .showConnectionDialog = false
3071+ return a , a .passwordDialog .Init ()
3072+ }
29973073 } else {
29983074 config = historyEntry .ToConnectionConfig ()
29993075 }
@@ -3045,9 +3121,13 @@ func (a *App) handleConnectionDialog(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
30453121
30463122 // Save to connection history (ignore errors)
30473123 if a .connectionHistory != nil {
3048- if err := a .connectionHistory .Add (config ); err != nil {
3124+ result , err := a .connectionHistory .Add (config )
3125+ if err != nil {
30493126 log .Printf ("Warning: Failed to save connection to history: %v" , err )
30503127 } else {
3128+ if result != nil && result .PasswordSaveError != nil {
3129+ log .Printf ("Warning: Failed to save password: %v" , result .PasswordSaveError )
3130+ }
30513131 // Reload history in dialog
30523132 history := a .connectionHistory .GetRecent (10 )
30533133 a .connectionDialog .SetHistoryEntries (history )
@@ -3323,6 +3403,33 @@ func (a *App) renderConnectionDialog() string {
33233403 return style .Render (dialog )
33243404}
33253405
3406+ func (a * App ) renderPasswordDialog () string {
3407+ // Center the dialog
3408+ dialogWidth := 50
3409+ dialogHeight := 12
3410+
3411+ a .passwordDialog .Width = dialogWidth
3412+ a .passwordDialog .Height = dialogHeight
3413+
3414+ dialog := a .passwordDialog .View ()
3415+
3416+ // Center it
3417+ verticalPadding := (a .state .Height - dialogHeight ) / 2
3418+ horizontalPadding := (a .state .Width - dialogWidth ) / 2
3419+
3420+ if verticalPadding < 0 {
3421+ verticalPadding = 0
3422+ }
3423+ if horizontalPadding < 0 {
3424+ horizontalPadding = 0
3425+ }
3426+
3427+ style := lipgloss .NewStyle ().
3428+ Padding (verticalPadding , 0 , 0 , horizontalPadding )
3429+
3430+ return style .Render (dialog )
3431+ }
3432+
33263433// triggerDiscovery runs discovery in the background and returns a command
33273434func (a * App ) triggerDiscovery () tea.Cmd {
33283435 return func () tea.Msg {
0 commit comments