From ceb0ffbf1456765b3ec90d8fdb8a6e8d38232db1 Mon Sep 17 00:00:00 2001 From: onevcat Date: Sun, 5 Apr 2026 21:59:25 +0900 Subject: [PATCH] fix: show CLI install feedback via toolbar toast for all entry points MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Command Palette's "Install Command Line Tool" action executed the install but showed no result feedback because the alert was only attached to the Settings window (which might not be open). Move install result notification to the main window's toolbar toast (StatusToast) so every entry point — Command Palette, menu bar, and Settings — gets consistent feedback. Settings buttons still show an alert in addition to the toast since the Settings window sits above the main window. Closes #146 --- supacode/App/supacodeApp.swift | 3 +-- supacode/Features/App/Reducer/AppFeature.swift | 11 +++++++++-- .../Settings/Reducer/SettingsFeature.swift | 13 ++++++++++--- supacodeTests/AppFeatureCLIInstallTests.swift | 14 +++++++++----- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/supacode/App/supacodeApp.swift b/supacode/App/supacodeApp.swift index 8453ac7f..7b20b770 100644 --- a/supacode/App/supacodeApp.swift +++ b/supacode/App/supacodeApp.swift @@ -617,8 +617,7 @@ struct SupacodeApp: App { ) } CommandGroup(after: .appSettings) { - Button("Install Command Line Tool...") { - SettingsWindowManager.shared.show() + Button("Install Command Line Tool") { store.send(.settings(.installCLIButtonTapped)) } .help("Install the prowl command line tool to /usr/local/bin") diff --git a/supacode/Features/App/Reducer/AppFeature.swift b/supacode/Features/App/Reducer/AppFeature.swift index 4747e0db..d252c4b7 100644 --- a/supacode/Features/App/Reducer/AppFeature.swift +++ b/supacode/Features/App/Reducer/AppFeature.swift @@ -472,8 +472,15 @@ struct AppFeature { case .settings(.delegate(.terminalFontSizeChanged)): return .none - case .settings(.delegate(.cliInstallStatusChanged)): - return .none + case .settings(.delegate(.cliInstallCompleted(let result))): + switch result { + case .installed(let path): + return .send(.repositories(.showToast(.success("prowl installed at \(path)")))) + case .uninstalled: + return .send(.repositories(.showToast(.success("prowl command line tool removed")))) + case .failed(let message): + return .send(.repositories(.showToast(.warning("CLI install failed: \(message)")))) + } case .settings(.delegate(.terminalLayoutSnapshotCleared(let success))): if success { diff --git a/supacode/Features/Settings/Reducer/SettingsFeature.swift b/supacode/Features/Settings/Reducer/SettingsFeature.swift index e8f5b7ce..032d7fe1 100644 --- a/supacode/Features/Settings/Reducer/SettingsFeature.swift +++ b/supacode/Features/Settings/Reducer/SettingsFeature.swift @@ -113,12 +113,18 @@ struct SettingsFeature { case openSystemNotificationSettings } + enum CLIInstallResultMessage: Equatable { + case installed(path: String) + case uninstalled + case failed(message: String) + } + @CasePathable enum Delegate: Equatable { case settingsChanged(GlobalSettings) case terminalFontSizeChanged(Float32?) case terminalLayoutSnapshotCleared(success: Bool) - case cliInstallStatusChanged + case cliInstallCompleted(CLIInstallResultMessage) } @Dependency(AnalyticsClient.self) private var analyticsClient @@ -259,7 +265,8 @@ struct SettingsFeature { } } state.cliInstallStatus = cliInstallClient.installationStatus(cliDefaultInstallPath) - return .send(.delegate(.cliInstallStatusChanged)) + let result: CLIInstallResultMessage = path.isEmpty ? .uninstalled : .installed(path: path) + return .send(.delegate(.cliInstallCompleted(result))) case .cliInstallCompleted(.failure(let error)): state.alert = AlertState { @@ -270,7 +277,7 @@ struct SettingsFeature { TextState(error.message) } state.cliInstallStatus = cliInstallClient.installationStatus(cliDefaultInstallPath) - return .send(.delegate(.cliInstallStatusChanged)) + return .send(.delegate(.cliInstallCompleted(.failed(message: error.message)))) case .refreshCLIInstallStatus: state.cliInstallStatus = cliInstallClient.installationStatus(cliDefaultInstallPath) diff --git a/supacodeTests/AppFeatureCLIInstallTests.swift b/supacodeTests/AppFeatureCLIInstallTests.swift index 9455a056..83b5d535 100644 --- a/supacodeTests/AppFeatureCLIInstallTests.swift +++ b/supacodeTests/AppFeatureCLIInstallTests.swift @@ -31,7 +31,7 @@ struct SettingsFeatureCLIInstallTests { } $0.cliInstallStatus = .installed(path: "/usr/local/bin/prowl") } - await store.receive(\.delegate.cliInstallStatusChanged) + await store.receive(\.delegate.cliInstallCompleted) #expect(installed.value == true) } @@ -58,7 +58,7 @@ struct SettingsFeatureCLIInstallTests { TextState("Permission denied") } } - await store.receive(\.delegate.cliInstallStatusChanged) + await store.receive(\.delegate.cliInstallCompleted) } @Test(.dependencies) func uninstallShowsSuccessAlert() async { @@ -84,12 +84,12 @@ struct SettingsFeatureCLIInstallTests { TextState("The prowl command line tool has been removed.") } } - await store.receive(\.delegate.cliInstallStatusChanged) + await store.receive(\.delegate.cliInstallCompleted) #expect(uninstalled.value == true) } - @Test(.dependencies) func commandPaletteInstallRoutesToSettings() async { + @Test(.dependencies) func commandPaletteInstallRoutesToSettingsAndShowsToast() async { let installed = LockIsolated(false) let store = TestStore( initialState: AppFeature.State(settings: SettingsFeature.State()) @@ -101,6 +101,7 @@ struct SettingsFeatureCLIInstallTests { } $0.cliInstallClient.installationStatus = { _ in .installed(path: "/usr/local/bin/prowl") } } + store.exhaustivity = .off await store.send(.commandPalette(.delegate(.installCLI))) await store.receive(\.settings.installCLIButtonTapped) @@ -114,7 +115,10 @@ struct SettingsFeatureCLIInstallTests { } $0.settings.cliInstallStatus = .installed(path: "/usr/local/bin/prowl") } - await store.receive(\.settings.delegate.cliInstallStatusChanged) + await store.receive(\.settings.delegate.cliInstallCompleted) + await store.receive(\.repositories.showToast) { + $0.repositories.statusToast = .success("prowl installed at /usr/local/bin/prowl") + } #expect(installed.value == true) } -- 2.54.0