@@ -27,7 +27,7 @@ struct ConnectionFormView: View { // swiftlint:disable:this type_body_length
2727 private var isNew : Bool { connectionId == nil }
2828
2929 private var availableDatabaseTypes : [ DatabaseType ] {
30- PluginManager . shared. availableDatabaseTypes
30+ PluginManager . shared. allAvailableDatabaseTypes
3131 }
3232
3333 private var additionalConnectionFields : [ ConnectionField ] {
@@ -126,6 +126,8 @@ struct ConnectionFormView: View { // swiftlint:disable:this type_body_length
126126 @State private var testSucceeded : Bool = false
127127
128128 @State private var pluginInstallConnection : DatabaseConnection ?
129+ @State private var isInstallingPlugin : Bool = false
130+ @State private var pluginInstallError : String ?
129131
130132 // Tab selection
131133 @State private var selectedTab : FormTab = . general
@@ -185,6 +187,8 @@ struct ConnectionFormView: View { // swiftlint:disable:this type_body_length
185187 if !visibleTabs. contains ( selectedTab) {
186188 selectedTab = . general
187189 }
190+ isInstallingPlugin = false
191+ pluginInstallError = nil
188192 }
189193 . pluginInstallPrompt ( connection: $pluginInstallConnection) { connection in
190194 connectAfterInstall ( connection)
@@ -237,9 +241,12 @@ struct ConnectionFormView: View { // swiftlint:disable:this type_body_length
237241 HStack {
238242 Text ( t. rawValue)
239243 if t. isDownloadablePlugin && !PluginManager. shared. isDriverLoaded ( for: t) {
240- Image ( systemName: " arrow.down.circle " )
241- . foregroundStyle ( . secondary)
244+ Text ( " Not Installed " )
242245 . font ( . caption)
246+ . foregroundStyle ( . secondary)
247+ . padding ( . horizontal, 4 )
248+ . padding ( . vertical, 1 )
249+ . background ( . quaternary, in: RoundedRectangle ( cornerRadius: 3 ) )
243250 }
244251 }
245252 } icon: {
@@ -251,6 +258,7 @@ struct ConnectionFormView: View { // swiftlint:disable:this type_body_length
251258 . tag ( t)
252259 }
253260 }
261+ . disabled ( isInstallingPlugin)
254262 TextField (
255263 String ( localized: " Name " ) ,
256264 text: $name,
@@ -263,7 +271,41 @@ struct ConnectionFormView: View { // swiftlint:disable:this type_body_length
263271 }
264272 }
265273
266- if PluginManager . shared. connectionMode ( for: type) == . fileBased {
274+ if type. isDownloadablePlugin && !PluginManager. shared. isDriverLoaded ( for: type) {
275+ Section {
276+ LabeledContent ( String ( localized: " Plugin " ) ) {
277+ if isInstallingPlugin {
278+ HStack ( spacing: 6 ) {
279+ ProgressView ( )
280+ . controlSize ( . small)
281+ Text ( " Installing… " )
282+ . foregroundStyle ( . secondary)
283+ }
284+ } else if let error = pluginInstallError {
285+ HStack ( spacing: 6 ) {
286+ Text ( error)
287+ . foregroundStyle ( . red)
288+ . font ( . caption)
289+ . lineLimit ( 2 )
290+ Button ( " Retry " ) {
291+ pluginInstallError = nil
292+ installPlugin ( for: type)
293+ }
294+ . controlSize ( . small)
295+ }
296+ } else {
297+ HStack ( spacing: 6 ) {
298+ Text ( " Not Installed " )
299+ . foregroundStyle ( . secondary)
300+ Button ( " Install " ) {
301+ installPlugin ( for: type)
302+ }
303+ . controlSize ( . small)
304+ }
305+ }
306+ }
307+ }
308+ } else if PluginManager . shared. connectionMode ( for: type) == . fileBased {
267309 Section ( String ( localized: " Database File " ) ) {
268310 HStack {
269311 TextField (
@@ -920,7 +962,7 @@ struct ConnectionFormView: View { // swiftlint:disable:this type_body_length
920962 Text ( " Test Connection " )
921963 }
922964 }
923- . disabled ( isTesting || !isValid)
965+ . disabled ( isTesting || isInstallingPlugin || !isValid)
924966
925967 Spacer ( )
926968
@@ -952,7 +994,7 @@ struct ConnectionFormView: View { // swiftlint:disable:this type_body_length
952994 }
953995 . keyboardShortcut ( . return)
954996 . buttonStyle ( . borderedProminent)
955- . disabled ( !isValid)
997+ . disabled ( isInstallingPlugin || !isValid)
956998 }
957999 . padding ( . horizontal, 16 )
9581000 . padding ( . vertical, 12 )
@@ -1454,6 +1496,25 @@ struct ConnectionFormView: View { // swiftlint:disable:this type_body_length
14541496 }
14551497 }
14561498
1499+ private func installPlugin( for databaseType: DatabaseType ) {
1500+ isInstallingPlugin = true
1501+ Task {
1502+ do {
1503+ try await PluginManager . shared. installMissingPlugin ( for: databaseType) { _ in }
1504+ if type == databaseType {
1505+ for field in PluginManager . shared. additionalConnectionFields ( for: databaseType) {
1506+ if additionalFieldValues [ field. id] == nil , let defaultValue = field. defaultValue {
1507+ additionalFieldValues [ field. id] = defaultValue
1508+ }
1509+ }
1510+ }
1511+ } catch {
1512+ pluginInstallError = error. localizedDescription
1513+ }
1514+ isInstallingPlugin = false
1515+ }
1516+ }
1517+
14571518 private func cleanupTestSecrets( for testId: UUID ) {
14581519 ConnectionStorage . shared. deletePassword ( for: testId)
14591520 ConnectionStorage . shared. deleteSSHPassword ( for: testId)
0 commit comments