diff --git a/FreeThinker/Core/Models/AppSettings.swift b/FreeThinker/Core/Models/AppSettings.swift index efaaaee..ea01b21 100644 --- a/FreeThinker/Core/Models/AppSettings.swift +++ b/FreeThinker/Core/Models/AppSettings.swift @@ -38,11 +38,16 @@ public enum ProvocationStylePreset: String, Codable, CaseIterable, Identifiable, public var instruction: String { switch self { case .contrarian: - return "Take a rigorous contrary angle. Surface weak premises and overconfidence. What might be wrong, incomplete, or deserving of skepticism? What counterarguments could be made?" + return "Take a rigorous contrary angle. Surface weak premises and overconfidence. " + + "What might be wrong, incomplete, or deserving of skepticism? What counterarguments could be made?" case .socratic: - return "Use Socratic questioning to challenge assumptions and reveal gaps in reasoning. Challenge the main assumptions or claims made here. What might be wrong, incomplete, or deserving of skepticism?" + return "Use Socratic questioning to challenge assumptions and reveal gaps in reasoning. " + + "Challenge the main assumptions or claims made here. " + + "What might be wrong, incomplete, or deserving of skepticism?" case .systemsThinking: - return "Analyze second-order effects, feedback loops, and systemic tradeoffs. What are the broader implications? What connections can you draw to other fields or concepts? How might this extend or apply in unexpected ways?" + return "Analyze second-order effects, feedback loops, and systemic tradeoffs. " + + "What are the broader implications? What connections can you draw to other fields or concepts? " + + "How might this extend or apply in unexpected ways?" } } } @@ -240,7 +245,8 @@ private extension HotkeyDisplayFormatter { 41: ";", 42: "\\", 43: ",", 44: "/", 45: "N", 46: "M", 47: ".", 48: "Tab", 49: "Space", 50: "`", 51: "Delete", 53: "Esc", 122: "F1", 120: "F2", 99: "F3", 118: "F4", 96: "F5", 97: "F6", 98: "F7", 100: "F8", 101: "F9", 109: "F10", - 103: "F11", 111: "F12", 105: "F13", 107: "F14", 113: "F15", 106: "F16", 64: "F17", 79: "F18", 80: "F19", 90: "F20", + 103: "F11", 111: "F12", 105: "F13", 107: "F14", 113: "F15", + 106: "F16", 64: "F17", 79: "F18", 80: "F19", 90: "F20", 123: "Left", 124: "Right", 125: "Down", 126: "Up" ] @@ -296,9 +302,11 @@ public struct AppSettings: Codable, Equatable, Sendable { /// Default key code for the global hotkey (P = 35). public static let defaultHotkeyKeyCode = 35 /// Default text for the first custom prompt template. - public static let defaultPrompt1 = "Identify hidden assumptions, unstated premises, or implicit biases in the following text." + public static let defaultPrompt1 = + "Identify hidden assumptions, unstated premises, or implicit biases in the following text." /// Default text for the second custom prompt template. - public static let defaultPrompt2 = "Provide a strong, well-reasoned counterargument or alternative perspective to the following claim." + public static let defaultPrompt2 = + "Provide a strong, well-reasoned counterargument or alternative perspective to the following claim." /// Maximum allowed length (in characters) for prompt1 and prompt2. public static let maxPromptLength = 1_000 /// Maximum allowed length (in characters) for `customStyleInstructions`. @@ -446,20 +454,36 @@ public extension AppSettings { prompt1 = try container.decodeIfPresent(String.self, forKey: .prompt1) ?? defaults.prompt1 prompt2 = try container.decodeIfPresent(String.self, forKey: .prompt2) ?? defaults.prompt2 launchAtLogin = try container.decodeIfPresent(Bool.self, forKey: .launchAtLogin) ?? defaults.launchAtLogin - selectedModel = try container.decodeIfPresent(ModelOption.self, forKey: .selectedModel) ?? defaults.selectedModel - showMenuBarIcon = try container.decodeIfPresent(Bool.self, forKey: .showMenuBarIcon) ?? defaults.showMenuBarIcon - dismissOnCopy = try container.decodeIfPresent(Bool.self, forKey: .dismissOnCopy) ?? defaults.dismissOnCopy - autoDismissSeconds = try container.decodeIfPresent(TimeInterval.self, forKey: .autoDismissSeconds) ?? defaults.autoDismissSeconds - fallbackCaptureEnabled = try container.decodeIfPresent(Bool.self, forKey: .fallbackCaptureEnabled) ?? defaults.fallbackCaptureEnabled - diagnosticsEnabled = try container.decodeIfPresent(Bool.self, forKey: .diagnosticsEnabled) ?? defaults.diagnosticsEnabled - hasSeenOnboarding = try container.decodeIfPresent(Bool.self, forKey: .hasSeenOnboarding) ?? defaults.hasSeenOnboarding - onboardingCompleted = try container.decodeIfPresent(Bool.self, forKey: .onboardingCompleted) ?? defaults.onboardingCompleted - hotkeyAwarenessConfirmed = try container.decodeIfPresent(Bool.self, forKey: .hotkeyAwarenessConfirmed) ?? defaults.hotkeyAwarenessConfirmed - provocationStylePreset = try container.decodeIfPresent(ProvocationStylePreset.self, forKey: .provocationStylePreset) ?? defaults.provocationStylePreset - customStyleInstructions = try container.decodeIfPresent(String.self, forKey: .customStyleInstructions) ?? defaults.customStyleInstructions - automaticallyCheckForUpdates = try container.decodeIfPresent(Bool.self, forKey: .automaticallyCheckForUpdates) ?? defaults.automaticallyCheckForUpdates - appUpdateChannel = try container.decodeIfPresent(AppUpdateChannel.self, forKey: .appUpdateChannel) ?? defaults.appUpdateChannel - aiTimeoutSeconds = try container.decodeIfPresent(TimeInterval.self, forKey: .aiTimeoutSeconds) ?? defaults.aiTimeoutSeconds + selectedModel = try container.decodeIfPresent(ModelOption.self, forKey: .selectedModel) + ?? defaults.selectedModel + showMenuBarIcon = try container.decodeIfPresent(Bool.self, forKey: .showMenuBarIcon) + ?? defaults.showMenuBarIcon + dismissOnCopy = try container.decodeIfPresent(Bool.self, forKey: .dismissOnCopy) + ?? defaults.dismissOnCopy + autoDismissSeconds = try container.decodeIfPresent(TimeInterval.self, forKey: .autoDismissSeconds) + ?? defaults.autoDismissSeconds + fallbackCaptureEnabled = try container.decodeIfPresent(Bool.self, forKey: .fallbackCaptureEnabled) + ?? defaults.fallbackCaptureEnabled + diagnosticsEnabled = try container.decodeIfPresent(Bool.self, forKey: .diagnosticsEnabled) + ?? defaults.diagnosticsEnabled + hasSeenOnboarding = try container.decodeIfPresent(Bool.self, forKey: .hasSeenOnboarding) + ?? defaults.hasSeenOnboarding + onboardingCompleted = try container.decodeIfPresent(Bool.self, forKey: .onboardingCompleted) + ?? defaults.onboardingCompleted + hotkeyAwarenessConfirmed = try container.decodeIfPresent(Bool.self, forKey: .hotkeyAwarenessConfirmed) + ?? defaults.hotkeyAwarenessConfirmed + provocationStylePreset = try container.decodeIfPresent( + ProvocationStylePreset.self, forKey: .provocationStylePreset + ) ?? defaults.provocationStylePreset + customStyleInstructions = try container.decodeIfPresent(String.self, forKey: .customStyleInstructions) + ?? defaults.customStyleInstructions + automaticallyCheckForUpdates = try container.decodeIfPresent( + Bool.self, forKey: .automaticallyCheckForUpdates + ) ?? defaults.automaticallyCheckForUpdates + appUpdateChannel = try container.decodeIfPresent(AppUpdateChannel.self, forKey: .appUpdateChannel) + ?? defaults.appUpdateChannel + aiTimeoutSeconds = try container.decodeIfPresent(TimeInterval.self, forKey: .aiTimeoutSeconds) + ?? defaults.aiTimeoutSeconds } } @@ -493,8 +517,12 @@ public extension AppSettings { result.hotkeyModifiers = Self.defaultHotkeyModifiers } - result.prompt1 = String(result.prompt1.trimmingCharacters(in: .whitespacesAndNewlines).prefix(Self.maxPromptLength)) - result.prompt2 = String(result.prompt2.trimmingCharacters(in: .whitespacesAndNewlines).prefix(Self.maxPromptLength)) + result.prompt1 = String( + result.prompt1.trimmingCharacters(in: .whitespacesAndNewlines).prefix(Self.maxPromptLength) + ) + result.prompt2 = String( + result.prompt2.trimmingCharacters(in: .whitespacesAndNewlines).prefix(Self.maxPromptLength) + ) if result.prompt1.isEmpty { result.prompt1 = Self.defaultPrompt1 diff --git a/FreeThinker/Core/Services/DefaultAIService.swift b/FreeThinker/Core/Services/DefaultAIService.swift index cbcf5bd..507b170 100644 --- a/FreeThinker/Core/Services/DefaultAIService.swift +++ b/FreeThinker/Core/Services/DefaultAIService.swift @@ -104,7 +104,8 @@ public actor DefaultAIService: AIServiceProtocol { let content = try parser.parse(rawOutput: rawOutput) Logger.info( - "Generated provocation requestId=\(request.id.uuidString) durationMs=\(Int(clock.now().timeIntervalSince(startedAt) * 1_000))", + "Generated provocation requestId=\(request.id.uuidString) " + + "durationMs=\(Int(clock.now().timeIntervalSince(startedAt) * 1_000))", category: .aiService ) diff --git a/FreeThinker/Core/Services/FoundationModelsAdapter.swift b/FreeThinker/Core/Services/FoundationModelsAdapter.swift index 87a24bd..26bfd64 100644 --- a/FreeThinker/Core/Services/FoundationModelsAdapter.swift +++ b/FreeThinker/Core/Services/FoundationModelsAdapter.swift @@ -94,7 +94,11 @@ public actor FoundationModelsAdapter: FoundationModelsAdapterProtocol { do { let response = try await session.respond(to: normalizedPrompt) - let trimmed = String(response.content.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).prefix(options.maximumOutputCharacters)) + let trimmed = String( + response.content + .trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + .prefix(options.maximumOutputCharacters) + ) guard !trimmed.isEmpty else { throw FreeThinkerError.generationFailed } diff --git a/FreeThinker/Core/Services/ProvocationOrchestrator.swift b/FreeThinker/Core/Services/ProvocationOrchestrator.swift index 4105497..1cf50d8 100644 --- a/FreeThinker/Core/Services/ProvocationOrchestrator.swift +++ b/FreeThinker/Core/Services/ProvocationOrchestrator.swift @@ -400,7 +400,10 @@ private extension ProvocationOrchestrator { if error == .cancelled || Task.isCancelled { metrics.cancellationCount += 1 let reason = pendingCancellationReason?.rawValue ?? "task-cancelled" - Logger.info("Pipeline cancelled source=\(source.rawValue) reason=\(reason)", category: .orchestrator) + Logger.info( + "Pipeline cancelled source=\(source.rawValue) reason=\(reason)", + category: .orchestrator + ) recordDiagnostic( stage: .aiGeneration, category: .warning, diff --git a/FreeThinker/Core/Services/ProvocationPromptComposer.swift b/FreeThinker/Core/Services/ProvocationPromptComposer.swift index 261bae2..0527598 100644 --- a/FreeThinker/Core/Services/ProvocationPromptComposer.swift +++ b/FreeThinker/Core/Services/ProvocationPromptComposer.swift @@ -32,7 +32,9 @@ public struct ProvocationPromptComposer: ProvocationPromptComposing, Sendable { let customInstruction = normalizedCustomInstruction(normalizedSettings.customStyleInstructions) Logger.debug( - "Composing prompt type=\(request.provocationType.rawValue) style=\(normalizedSettings.provocationStylePreset.rawValue) textChars=\(selectedText.count)", + "Composing prompt type=\(request.provocationType.rawValue) " + + "style=\(normalizedSettings.provocationStylePreset.rawValue) " + + "textChars=\(selectedText.count)", category: .promptComposer ) diff --git a/FreeThinker/Core/Utilities/ErrorPresentationMapper.swift b/FreeThinker/Core/Utilities/ErrorPresentationMapper.swift index 6f185c0..90040f7 100644 --- a/FreeThinker/Core/Utilities/ErrorPresentationMapper.swift +++ b/FreeThinker/Core/Utilities/ErrorPresentationMapper.swift @@ -79,10 +79,13 @@ public struct ErrorPresentationMapper: ErrorPresentationMapping { switch error { case .accessibilityPermissionDenied: let translocationHint = isTranslocatedProvider() - ? " FreeThinker appears to be running from a translocated location. Move it to /Applications, relaunch, then re-enable Accessibility once." + ? " FreeThinker appears to be running from a translocated location." + + " Move it to /Applications, relaunch, then re-enable Accessibility once." : "" return ErrorPresentation( - message: "FreeThinker needs Accessibility access. Open Settings -> Privacy & Security -> Accessibility, then enable FreeThinker.\(translocationHint)", + message: "FreeThinker needs Accessibility access. " + + "Open Settings -> Privacy & Security -> Accessibility, then enable FreeThinker." + + "\(translocationHint)", action: .openAccessibilitySettings, preferPanelPresentation: true ) @@ -110,14 +113,16 @@ public struct ErrorPresentationMapper: ErrorPresentationMapping { case .hotkeyRegistrationConflict: return ErrorPresentation( - message: "That shortcut is already used by another app. Open Settings to change or disable the FreeThinker hotkey.", + message: "That shortcut is already used by another app. " + + "Open Settings to change or disable the FreeThinker hotkey.", action: .openHotkeySettings, preferPanelPresentation: true ) case .hotkeyRegistrationFailed: return ErrorPresentation( - message: "FreeThinker could not register its global hotkey. Open Settings to retry or adjust the shortcut.", + message: "FreeThinker could not register its global hotkey. " + + "Open Settings to retry or adjust the shortcut.", action: .openHotkeySettings, preferPanelPresentation: true ) diff --git a/FreeThinker/UI/FloatingPanel/FloatingPanelView.swift b/FreeThinker/UI/FloatingPanel/FloatingPanelView.swift index 7523437..1f62790 100644 --- a/FreeThinker/UI/FloatingPanel/FloatingPanelView.swift +++ b/FreeThinker/UI/FloatingPanel/FloatingPanelView.swift @@ -131,8 +131,12 @@ public struct FloatingPanelView: View { .buttonStyle(.plain) .keyboardShortcut("p", modifiers: [.command]) .accessibilityIdentifier(FloatingPanelAccessibility.Identifier.pinButton) - .accessibilityLabel(viewModel.isPinned ? FloatingPanelAccessibility.Label.unpin : FloatingPanelAccessibility.Label.pin) - .accessibilityHint(viewModel.isPinned ? FloatingPanelAccessibility.Hint.unpin : FloatingPanelAccessibility.Hint.pin) + .accessibilityLabel( + viewModel.isPinned ? FloatingPanelAccessibility.Label.unpin : FloatingPanelAccessibility.Label.pin + ) + .accessibilityHint( + viewModel.isPinned ? FloatingPanelAccessibility.Hint.unpin : FloatingPanelAccessibility.Hint.pin + ) } private var closeButton: some View { diff --git a/FreeThinker/UI/Settings/AccessibilityHelpSettingsView.swift b/FreeThinker/UI/Settings/AccessibilityHelpSettingsView.swift index 6226b0c..bf3a83f 100644 --- a/FreeThinker/UI/Settings/AccessibilityHelpSettingsView.swift +++ b/FreeThinker/UI/Settings/AccessibilityHelpSettingsView.swift @@ -27,7 +27,8 @@ public struct AccessibilityHelpSettingsView: View { } .buttonStyle(.borderedProminent) - Text("If permission does not persist across relaunch, move FreeThinker to /Applications, relaunch, then enable Accessibility again.") + Text("If permission does not persist across relaunch, move FreeThinker to /Applications, " + + "relaunch, then enable Accessibility again.") .font(.footnote) .foregroundStyle(.secondary) } @@ -39,7 +40,8 @@ public struct AccessibilityHelpSettingsView: View { private extension AccessibilityHelpSettingsView { func openAccessibilitySettings() { - guard let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") else { + let urlString = "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" + guard let url = URL(string: urlString) else { return } NSWorkspace.shared.open(url) diff --git a/FreeThinker/UI/Settings/ProvocationSettingsView.swift b/FreeThinker/UI/Settings/ProvocationSettingsView.swift index c2e953f..fa69f3e 100644 --- a/FreeThinker/UI/Settings/ProvocationSettingsView.swift +++ b/FreeThinker/UI/Settings/ProvocationSettingsView.swift @@ -40,7 +40,9 @@ public struct ProvocationSettingsView: View { .frame(minHeight: 160) .font(.body) .focused($isCustomInstructionEditorFocused) - .accessibilityIdentifier(SettingsAccessibility.Identifier.provocationCustomInstructionEditor) + .accessibilityIdentifier( + SettingsAccessibility.Identifier.provocationCustomInstructionEditor + ) .onChange(of: draftCustomInstructions) { oldValue, newValue in scheduleCustomInstructionPersistence(newValue) } @@ -69,7 +71,8 @@ public struct ProvocationSettingsView: View { } if isOverCustomInstructionLimit { - Text("Limit is \(AppSettings.maxCustomInstructionLength) characters. Extra text is trimmed when saved.") + Text("Limit is \(AppSettings.maxCustomInstructionLength) characters. " + + "Extra text is trimmed when saved.") .font(.footnote) .foregroundStyle(.orange) }