From f7987cd4bd232c5a1924f4081f59f1c0fa1d42b0 Mon Sep 17 00:00:00 2001 From: Beingpax Date: Wed, 6 Aug 2025 13:10:21 +0545 Subject: [PATCH] Redesign recorder views with reusable components --- VoiceInk.xcodeproj/project.pbxproj | 10 +-- .../Views/Recorder/MiniRecorderView.swift | 53 +-------------- .../Views/Recorder/NotchRecorderView.swift | 62 ++++-------------- .../Views/Recorder/RecorderComponents.swift | 65 +++++++++++++++++++ 4 files changed, 86 insertions(+), 104 deletions(-) diff --git a/VoiceInk.xcodeproj/project.pbxproj b/VoiceInk.xcodeproj/project.pbxproj index 07601eb..2e004b1 100644 --- a/VoiceInk.xcodeproj/project.pbxproj +++ b/VoiceInk.xcodeproj/project.pbxproj @@ -11,8 +11,8 @@ E1A261122CC143AC00B233D1 /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = E1A261112CC143AC00B233D1 /* KeyboardShortcuts */; }; E1ADD45A2CC5352A00303ECB /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = E1ADD4592CC5352A00303ECB /* LaunchAtLogin */; }; E1ADD45F2CC544F100303ECB /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = E1ADD45E2CC544F100303ECB /* Sparkle */; }; - E1B2DCAB2E3DE70A008DFD68 /* whisper.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1B2DCAA2E3DE70A008DFD68 /* whisper.xcframework */; }; - E1B2DCAC2E3DE70A008DFD68 /* whisper.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E1B2DCAA2E3DE70A008DFD68 /* whisper.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E1CE28782E4336150082B758 /* whisper.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1CE28772E4336150082B758 /* whisper.xcframework */; }; + E1CE28792E4336150082B758 /* whisper.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E1CE28772E4336150082B758 /* whisper.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E1D7EF992E35E16C00640029 /* MediaRemoteAdapter in Frameworks */ = {isa = PBXBuildFile; productRef = E1D7EF982E35E16C00640029 /* MediaRemoteAdapter */; }; E1D7EF9A2E35E19B00640029 /* MediaRemoteAdapter in Embed Frameworks */ = {isa = PBXBuildFile; productRef = E1D7EF982E35E16C00640029 /* MediaRemoteAdapter */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; E1F5FA7A2DA6CBF900B1FD8A /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = E1F5FA792DA6CBF900B1FD8A /* Zip */; }; @@ -42,7 +42,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - E1B2DCAC2E3DE70A008DFD68 /* whisper.xcframework in Embed Frameworks */, + E1CE28792E4336150082B758 /* whisper.xcframework in Embed Frameworks */, E1D7EF9A2E35E19B00640029 /* MediaRemoteAdapter in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -55,6 +55,7 @@ E11473C32CBE0F0B00318EE4 /* VoiceInkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VoiceInkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; E11473CD2CBE0F0B00318EE4 /* VoiceInkUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VoiceInkUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; E1B2DCAA2E3DE70A008DFD68 /* whisper.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = whisper.xcframework; path = "../Downloads/build-apple/whisper.xcframework"; sourceTree = ""; }; + E1CE28772E4336150082B758 /* whisper.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = whisper.xcframework; path = "../build-apple/whisper.xcframework"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -83,7 +84,7 @@ E1ADD45A2CC5352A00303ECB /* LaunchAtLogin in Frameworks */, E1D7EF992E35E16C00640029 /* MediaRemoteAdapter in Frameworks */, E10F06092E3F390600F7FBDC /* FluidAudio in Frameworks */, - E1B2DCAB2E3DE70A008DFD68 /* whisper.xcframework in Frameworks */, + E1CE28782E4336150082B758 /* whisper.xcframework in Frameworks */, E1ADD45F2CC544F100303ECB /* Sparkle in Frameworks */, E1A261122CC143AC00B233D1 /* KeyboardShortcuts in Frameworks */, E1F5FA7A2DA6CBF900B1FD8A /* Zip in Frameworks */, @@ -131,6 +132,7 @@ E114741C2CBE1DE200318EE4 /* Frameworks */ = { isa = PBXGroup; children = ( + E1CE28772E4336150082B758 /* whisper.xcframework */, E1B2DCAA2E3DE70A008DFD68 /* whisper.xcframework */, ); name = Frameworks; diff --git a/VoiceInk/Views/Recorder/MiniRecorderView.swift b/VoiceInk/Views/Recorder/MiniRecorderView.swift index b8f3f61..2882c7f 100644 --- a/VoiceInk/Views/Recorder/MiniRecorderView.swift +++ b/VoiceInk/Views/Recorder/MiniRecorderView.swift @@ -34,44 +34,7 @@ struct MiniRecorderView: View { ) } - private var rightButton: some View { - Group { - if !powerModeManager.enabledConfigurations.isEmpty && false { - RecorderToggleButton( - isEnabled: !powerModeManager.enabledConfigurations.isEmpty, - icon: powerModeManager.currentActiveConfiguration?.emoji ?? "⚙️", - color: .orange, - disabled: false - ) { - showPowerModePopover.toggle() - } - .frame(width: 24) - .padding(.trailing, 8) - .popover(isPresented: $showPowerModePopover, arrowEdge: .bottom) { - PowerModePopover() - } - } else { - RecorderToggleButton( - isEnabled: enhancementService.isEnhancementEnabled, - icon: enhancementService.activePrompt?.icon.rawValue ?? "brain", - color: .blue, - disabled: false - ) { - if enhancementService.isEnhancementEnabled { - showEnhancementPromptPopover.toggle() - } else { - enhancementService.isEnhancementEnabled = true - } - } - .frame(width: 24) - .padding(.trailing, 8) - .popover(isPresented: $showEnhancementPromptPopover, arrowEdge: .bottom) { - EnhancementPromptPopover() - .environmentObject(enhancementService) - } - } - } - } + var body: some View { Group { @@ -85,23 +48,13 @@ struct MiniRecorderView: View { } .overlay { HStack(spacing: 0) { - let isRecording = whisperState.recordingState == .recording - let isProcessing = whisperState.recordingState == .transcribing || whisperState.recordingState == .enhancing - - RecorderRecordButton( - isRecording: isRecording, - isProcessing: isProcessing - ) { - Task { await whisperState.toggleRecord() } - } - .frame(width: 24) - .padding(.leading, 8) + RecorderPromptButton(showPopover: $showEnhancementPromptPopover) statusView .frame(maxWidth: .infinity) .padding(.horizontal, 8) - rightButton + RecorderPowerModeButton(showPopover: $showPowerModePopover) } .padding(.vertical, 8) } diff --git a/VoiceInk/Views/Recorder/NotchRecorderView.swift b/VoiceInk/Views/Recorder/NotchRecorderView.swift index f0c8467..356b605 100644 --- a/VoiceInk/Views/Recorder/NotchRecorderView.swift +++ b/VoiceInk/Views/Recorder/NotchRecorderView.swift @@ -33,62 +33,24 @@ struct NotchRecorderView: View { private var leftSection: some View { HStack(spacing: 8) { - let isRecording = whisperState.recordingState == .recording - let isProcessing = whisperState.recordingState == .transcribing || whisperState.recordingState == .enhancing + RecorderPromptButton( + showPopover: $showEnhancementPromptPopover, + buttonSize: 22, + padding: EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0) + ) - RecorderRecordButton( - isRecording: isRecording, - isProcessing: isProcessing - ) { - Task { await whisperState.toggleRecord() } - } - .frame(width: 22) - - rightToggleButton + RecorderPowerModeButton( + showPopover: $showPowerModePopover, + buttonSize: 22, + padding: EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0) + ) Spacer() } - .frame(width: 64) + .frame(width: 84) .padding(.leading, 16) } - private var rightToggleButton: some View { - HStack(spacing: 4) { - if !powerModeManager.enabledConfigurations.isEmpty && false { - RecorderToggleButton( - isEnabled: !powerModeManager.enabledConfigurations.isEmpty, - icon: powerModeManager.currentActiveConfiguration?.emoji ?? "⚙️", - color: .orange, - disabled: false - ) { - showPowerModePopover.toggle() - } - .frame(width: 22) - .popover(isPresented: $showPowerModePopover, arrowEdge: .bottom) { - PowerModePopover() - } - } else { - RecorderToggleButton( - isEnabled: enhancementService.isEnhancementEnabled, - icon: enhancementService.activePrompt?.icon.rawValue ?? "brain", - color: .blue, - disabled: false - ) { - if enhancementService.isEnhancementEnabled { - showEnhancementPromptPopover.toggle() - } else { - enhancementService.isEnhancementEnabled = true - } - } - .frame(width: 22) - .popover(isPresented: $showEnhancementPromptPopover, arrowEdge: .bottom) { - EnhancementPromptPopover() - .environmentObject(enhancementService) - } - } - } - } - private var centerSection: some View { Rectangle() .fill(Color.clear) @@ -97,7 +59,7 @@ struct NotchRecorderView: View { } private var rightSection: some View { - HStack(spacing: 0) { + HStack(spacing: 8) { Spacer() statusDisplay } diff --git a/VoiceInk/Views/Recorder/RecorderComponents.swift b/VoiceInk/Views/Recorder/RecorderComponents.swift index 6ecf21b..dfa4836 100644 --- a/VoiceInk/Views/Recorder/RecorderComponents.swift +++ b/VoiceInk/Views/Recorder/RecorderComponents.swift @@ -99,6 +99,71 @@ struct ProcessingIndicator: View { } } +// MARK: - Prompt Button Component +struct RecorderPromptButton: View { + @EnvironmentObject private var enhancementService: AIEnhancementService + @Binding var showPopover: Bool + let buttonSize: CGFloat + let padding: EdgeInsets + + init(showPopover: Binding, buttonSize: CGFloat = 24, padding: EdgeInsets = EdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 0)) { + self._showPopover = showPopover + self.buttonSize = buttonSize + self.padding = padding + } + + var body: some View { + RecorderToggleButton( + isEnabled: enhancementService.isEnhancementEnabled, + icon: enhancementService.activePrompt?.icon.rawValue ?? "brain", + color: .blue, + disabled: false + ) { + if enhancementService.isEnhancementEnabled { + showPopover.toggle() + } else { + enhancementService.isEnhancementEnabled = true + } + } + .frame(width: buttonSize) + .padding(padding) + .popover(isPresented: $showPopover, arrowEdge: .bottom) { + EnhancementPromptPopover() + .environmentObject(enhancementService) + } + } +} + +// MARK: - Power Mode Button Component +struct RecorderPowerModeButton: View { + @ObservedObject private var powerModeManager = PowerModeManager.shared + @Binding var showPopover: Bool + let buttonSize: CGFloat + let padding: EdgeInsets + + init(showPopover: Binding, buttonSize: CGFloat = 24, padding: EdgeInsets = EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 8)) { + self._showPopover = showPopover + self.buttonSize = buttonSize + self.padding = padding + } + + var body: some View { + RecorderToggleButton( + isEnabled: !powerModeManager.enabledConfigurations.isEmpty, + icon: powerModeManager.currentActiveConfiguration?.emoji ?? "⚙️", + color: .orange, + disabled: powerModeManager.enabledConfigurations.isEmpty + ) { + showPopover.toggle() + } + .frame(width: buttonSize) + .padding(padding) + .popover(isPresented: $showPopover, arrowEdge: .bottom) { + PowerModePopover() + } + } +} + // MARK: - Status Display Component struct RecorderStatusDisplay: View { let currentState: RecordingState