diff --git a/Core/Sources/ChatGPTChatTab/Chat.swift b/Core/Sources/ChatGPTChatTab/Chat.swift index 4d722d68..9b7f2ca8 100644 --- a/Core/Sources/ChatGPTChatTab/Chat.swift +++ b/Core/Sources/ChatGPTChatTab/Chat.swift @@ -226,9 +226,13 @@ struct Chat: ReducerProtocol { cancellable.cancel() } } - for await _ in stream { + let debouncedHistoryChange = TimedDebounceFunction(duration: 0.2) { await send(.historyChanged) } + + for await _ in stream { + await debouncedHistoryChange() + } }.cancellable(id: CancelID.observeHistoryChange(id), cancelInFlight: true) case .observeIsReceivingMessageChange: @@ -450,3 +454,33 @@ struct ChatMenu: ReducerProtocol { } } +private actor TimedDebounceFunction { + let duration: TimeInterval + let block: () async -> Void + + var task: Task? + var lastFireTime: Date = .init(timeIntervalSince1970: 0) + + init(duration: TimeInterval, block: @escaping () async -> Void) { + self.duration = duration + self.block = block + } + + func callAsFunction() async { + task?.cancel() + if lastFireTime.timeIntervalSinceNow < -duration { + await fire() + task = nil + } else { + task = Task.detached { [weak self, duration] in + try await Task.sleep(nanoseconds: UInt64(duration * 1_000_000_000)) + await self?.fire() + } + } + } + + func fire() async { + lastFireTime = Date() + await block() + } +} diff --git a/Core/Sources/ChatGPTChatTab/ChatPanel.swift b/Core/Sources/ChatGPTChatTab/ChatPanel.swift index 2729b5e4..d66c0d25 100644 --- a/Core/Sources/ChatGPTChatTab/ChatPanel.swift +++ b/Core/Sources/ChatGPTChatTab/ChatPanel.swift @@ -18,7 +18,7 @@ public struct ChatPanel: View { Divider() ChatPanelInputArea(chat: chat) } - .background(.regularMaterial) + .background(.clear) .onAppear { chat.send(.appear) } } } @@ -86,13 +86,23 @@ struct ChatPanelMessages: View { } .modify { view in if #available(macOS 13.0, *) { - view.listRowSeparator(.hidden).listSectionSeparator(.hidden) + view + .listRowSeparator(.hidden) + .listSectionSeparator(.hidden) } else { view } } } .listStyle(.plain) + .listRowBackground(EmptyView()) + .modify { view in + if #available(macOS 13.0, *) { + view.scrollContentBackground(.hidden) + } else { + view + } + } .coordinateSpace(name: scrollSpace) .preference( key: ListHeightPreferenceKey.self, @@ -218,7 +228,10 @@ struct ChatPanelMessages: View { if isInitialLoad { isInitialLoad = false } - scrollToBottom() + Task { + await Task.yield() + scrollToBottom() + } } } } diff --git a/Core/Sources/ChatGPTChatTab/Styles.swift b/Core/Sources/ChatGPTChatTab/Styles.swift index ed1c981c..3f8d40c4 100644 --- a/Core/Sources/ChatGPTChatTab/Styles.swift +++ b/Core/Sources/ChatGPTChatTab/Styles.swift @@ -9,7 +9,7 @@ extension Color { if appearance.isDarkMode { return #colorLiteral(red: 0.1580096483, green: 0.1730263829, blue: 0.2026666105, alpha: 1) } - return .white + return #colorLiteral(red: 0.9896564803, green: 0.9896564803, blue: 0.9896564803, alpha: 1) })) } @@ -18,7 +18,7 @@ extension Color { if appearance.isDarkMode { return #colorLiteral(red: 0.2284317913, green: 0.2145925438, blue: 0.3214019983, alpha: 1) } - return #colorLiteral(red: 0.896820749, green: 0.8709097223, blue: 0.9766687925, alpha: 1) + return #colorLiteral(red: 0.9458052187, green: 0.9311983998, blue: 0.9906365955, alpha: 1) })) } } @@ -92,6 +92,60 @@ extension MarkdownUI.Theme { } } + static func instruction(fontSize: Double) -> MarkdownUI.Theme { + .gitHub.text { + ForegroundColor(.primary) + BackgroundColor(Color.clear) + FontSize(fontSize) + } + .code { + FontFamilyVariant(.monospaced) + FontSize(.em(0.85)) + BackgroundColor(Color.secondary.opacity(0.2)) + } + .codeBlock { configuration in + let wrapCode = UserDefaults.shared.value(for: \.wrapCodeInChatCodeBlock) + + if wrapCode { + configuration.label + .codeBlockLabelStyle() + .codeBlockStyle(configuration) + } else { + ScrollView(.horizontal) { + configuration.label + .codeBlockLabelStyle() + } + .workaroundForVerticalScrollingBugInMacOS() + .codeBlockStyle(configuration) + } + } + .table { configuration in + configuration.label + .fixedSize(horizontal: false, vertical: true) + .markdownTableBorderStyle(.init( + color: .init(nsColor: .separatorColor), + strokeStyle: .init(lineWidth: 1) + )) + .markdownTableBackgroundStyle( + .alternatingRows(Color.secondary.opacity(0.1), Color.secondary.opacity(0.2)) + ) + .markdownMargin(top: 0, bottom: 16) + } + .tableCell { configuration in + configuration.label + .markdownTextStyle { + if configuration.row == 0 { + FontWeight(.semibold) + } + BackgroundColor(nil) + } + .fixedSize(horizontal: false, vertical: true) + .padding(.vertical, 6) + .padding(.horizontal, 13) + .relativeLineSpacing(.em(0.25)) + } + } + static func functionCall(fontSize: Double) -> MarkdownUI.Theme { .gitHub.text { ForegroundColor(.secondary) diff --git a/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift b/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift index c8efac88..67457bf1 100644 --- a/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift +++ b/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift @@ -66,7 +66,7 @@ struct BotMessage: View { .stroke(Color(nsColor: .separatorColor), lineWidth: 1) } .padding(.leading, 8) - .shadow(color: .black.opacity(0.1), radius: 2) + .shadow(color: .black.opacity(0.05), radius: 6) .contextMenu { Button("Copy") { NSPasteboard.general.clearContents() diff --git a/Core/Sources/ChatGPTChatTab/Views/Instructions.swift b/Core/Sources/ChatGPTChatTab/Views/Instructions.swift index 2244a236..35097d08 100644 --- a/Core/Sources/ChatGPTChatTab/Views/Instructions.swift +++ b/Core/Sources/ChatGPTChatTab/Views/Instructions.swift @@ -71,7 +71,7 @@ struct Instruction: View { func body(content: Content) -> some View { content .textSelection(.enabled) - .markdownTheme(.custom(fontSize: chatFontSize)) + .markdownTheme(.instruction(fontSize: chatFontSize)) .opacity(0.8) .frame(maxWidth: .infinity, alignment: .leading) .padding() diff --git a/Core/Sources/ChatGPTChatTab/Views/UserMessage.swift b/Core/Sources/ChatGPTChatTab/Views/UserMessage.swift index 2fd67f42..c470cc4e 100644 --- a/Core/Sources/ChatGPTChatTab/Views/UserMessage.swift +++ b/Core/Sources/ChatGPTChatTab/Views/UserMessage.swift @@ -34,7 +34,7 @@ struct UserMessage: View { } .padding(.leading) .padding(.trailing, 8) - .shadow(color: .black.opacity(0.1), radius: 2) + .shadow(color: .black.opacity(0.05), radius: 6) .frame(maxWidth: .infinity, alignment: .trailing) .contextMenu { Button("Copy") { diff --git a/Core/Sources/HostApp/DebugView.swift b/Core/Sources/HostApp/DebugView.swift index 08d46bbc..cba2be49 100644 --- a/Core/Sources/HostApp/DebugView.swift +++ b/Core/Sources/HostApp/DebugView.swift @@ -25,6 +25,8 @@ final class DebugSettings: ObservableObject { var restartXcodeInspectorIfAccessibilityAPIIsMalfunctioningNoTimer @AppStorage(\.toastForTheReasonWhyXcodeInspectorNeedsToBeRestarted) var toastForTheReasonWhyXcodeInspectorNeedsToBeRestarted + @AppStorage(\.observeToAXNotificationWithDefaultMode) + var observeToAXNotificationWithDefaultMode init() {} } @@ -116,8 +118,17 @@ struct DebugSettingsView: View { UserDefaults.shared.set(nil, forKey: "ChatModels") UserDefaults.shared.set(nil, forKey: "EmbeddingModels") } + + Group { + Toggle( + isOn: $settings.observeToAXNotificationWithDefaultMode + ) { + Text("Observe to AXNotification with default mode") + } + } } } + .frame(maxWidth: .infinity) .padding() } } diff --git a/Core/Sources/HostApp/FeatureSettings/ChatSettingsView.swift b/Core/Sources/HostApp/FeatureSettings/ChatSettingsView.swift index 633cc5ee..aaeb7af4 100644 --- a/Core/Sources/HostApp/FeatureSettings/ChatSettingsView.swift +++ b/Core/Sources/HostApp/FeatureSettings/ChatSettingsView.swift @@ -313,7 +313,7 @@ struct ChatSettingsView: View { "Preferred Chat Model", selection: $settings.preferredChatModelIdForSenseScope ) { - Text("None").tag("") + Text("Use the default model").tag("") if !settings.chatModels .contains(where: { @@ -364,7 +364,7 @@ struct ChatSettingsView: View { "Preferred Chat Model", selection: $settings.preferredChatModelIdForProjectScope ) { - Text("None").tag("") + Text("Use the default model").tag("") if !settings.chatModels .contains(where: { @@ -390,7 +390,7 @@ struct ChatSettingsView: View { SubSection( title: Text("Web Scope"), - description: "Allow the bot to search on Bing or read a web page." + description: "Allow the bot to search on Bing or read a web page. The current implementation requires function calling." ) { Form { Toggle(isOn: $settings.enableWebScopeByDefaultInChatContext) { @@ -401,7 +401,7 @@ struct ChatSettingsView: View { "Preferred Chat Model", selection: $settings.preferredChatModelIdForWebScope ) { - Text("None").tag("") + Text("Use the default model").tag("") if !settings.chatModels .contains(where: { diff --git a/Core/Sources/Service/SuggestionCommandHandler/WindowBaseCommandHandler.swift b/Core/Sources/Service/SuggestionCommandHandler/WindowBaseCommandHandler.swift index 2bf83cfe..d4f0d52e 100644 --- a/Core/Sources/Service/SuggestionCommandHandler/WindowBaseCommandHandler.swift +++ b/Core/Sources/Service/SuggestionCommandHandler/WindowBaseCommandHandler.swift @@ -46,14 +46,6 @@ struct WindowBaseCommandHandler: SuggestionCommandHandler { try Task.checkCancellation() - let snapshot = FilespaceSuggestionSnapshot( - linesHash: editor.lines.hashValue, - cursorPosition: editor.cursorPosition - ) - - // There is no need to regenerate suggestions for the same editor content. - guard filespace.suggestionSourceSnapshot != snapshot else { return } - try await workspace.generateSuggestions( forFileAt: fileURL, editor: editor diff --git a/Core/Sources/SuggestionWidget/ChatWindowView.swift b/Core/Sources/SuggestionWidget/ChatWindowView.swift index cda1a21d..2855f142 100644 --- a/Core/Sources/SuggestionWidget/ChatWindowView.swift +++ b/Core/Sources/SuggestionWidget/ChatWindowView.swift @@ -43,7 +43,6 @@ struct ChatWindowView: View { } .xcodeStyleFrame(cornerRadius: 10) .ignoresSafeArea(edges: .top) - .background(.regularMaterial) .onChange(of: viewStore.state.isPanelDisplayed) { isDisplayed in toggleVisibility(isDisplayed) } diff --git a/Core/Sources/SuggestionWidget/FeatureReducers/PanelFeature.swift b/Core/Sources/SuggestionWidget/FeatureReducers/PanelFeature.swift index 48d92447..954c5743 100644 --- a/Core/Sources/SuggestionWidget/FeatureReducers/PanelFeature.swift +++ b/Core/Sources/SuggestionWidget/FeatureReducers/PanelFeature.swift @@ -95,8 +95,6 @@ public struct PanelFeature: ReducerProtocol { return .none case .switchToAnotherEditorAndUpdateContent: - state.content.error = nil - state.content.suggestion = nil return .run { send in guard let fileURL = await xcodeInspector.safe.realtimeActiveDocumentURL else { return } diff --git a/Core/Sources/SuggestionWidget/FeatureReducers/PromptToCode.swift b/Core/Sources/SuggestionWidget/FeatureReducers/PromptToCode.swift index ccc094b7..490c9cc6 100644 --- a/Core/Sources/SuggestionWidget/FeatureReducers/PromptToCode.swift +++ b/Core/Sources/SuggestionWidget/FeatureReducers/PromptToCode.swift @@ -187,6 +187,7 @@ public struct PromptToCode: ReducerProtocol { generateDescriptionRequirement: copiedState .generateDescriptionRequirement ) + #warning("TODO: make the action call debounced.") for try await fragment in stream { try Task.checkCancellation() await send(.modifyCodeChunkReceived( diff --git a/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift b/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift index ba4a81bc..cee3d93c 100644 --- a/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift +++ b/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift @@ -295,11 +295,7 @@ public struct WidgetFeature: ReducerProtocol { }.cancellable(id: CancelID.observeUserDefaults, cancelInFlight: true) case .updateActiveApplication: - return .run { send in - if let app = await xcodeInspector.safe.activeApplication, app.isXcode { - await send(.panel(.switchToAnotherEditorAndUpdateContent)) - } - } + return .none case .updateColorScheme: let widgetColorScheme = UserDefaults.shared.value(for: \.widgetColorScheme) diff --git a/Core/Sources/SuggestionWidget/Styles.swift b/Core/Sources/SuggestionWidget/Styles.swift index 8aa87817..9f63b0f4 100644 --- a/Core/Sources/SuggestionWidget/Styles.swift +++ b/Core/Sources/SuggestionWidget/Styles.swift @@ -30,7 +30,7 @@ extension Color { if appearance.isDarkMode { return #colorLiteral(red: 0.2284317913, green: 0.2145925438, blue: 0.3214019983, alpha: 1) } - return #colorLiteral(red: 0.896820749, green: 0.8709097223, blue: 0.9766687925, alpha: 1) + return #colorLiteral(red: 0.9458052187, green: 0.9311983998, blue: 0.9906365955, alpha: 1) })) } } @@ -45,12 +45,16 @@ extension NSAppearance { } } -extension View { - func xcodeStyleFrame(cornerRadius: Double = 8) -> some View { - clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)) - .overlay( +struct XcodeLikeFrame: View { + @Environment(\.colorScheme) var colorScheme + let content: Content + let cornerRadius: Double + + var body: some View { + content.clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)) + .background( RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) - .stroke(Color.black.opacity(0.3), style: .init(lineWidth: 1)) + .fill(Material.bar) ) .overlay( RoundedRectangle(cornerRadius: max(0, cornerRadius - 1), style: .continuous) @@ -60,6 +64,12 @@ extension View { } } +extension View { + func xcodeStyleFrame(cornerRadius: Double? = nil) -> some View { + XcodeLikeFrame(content: self, cornerRadius: cornerRadius ?? 10) + } +} + extension MarkdownUI.Theme { static func custom(fontSize: Double) -> MarkdownUI.Theme { .gitHub.text { diff --git a/Core/Sources/SuggestionWidget/SuggestionPanelContent/CodeBlockSuggestionPanel.swift b/Core/Sources/SuggestionWidget/SuggestionPanelContent/CodeBlockSuggestionPanel.swift index b42e37ea..b5f60d67 100644 --- a/Core/Sources/SuggestionWidget/SuggestionPanelContent/CodeBlockSuggestionPanel.swift +++ b/Core/Sources/SuggestionWidget/SuggestionPanelContent/CodeBlockSuggestionPanel.swift @@ -6,6 +6,7 @@ struct CodeBlockSuggestionPanel: View { @Environment(\.colorScheme) var colorScheme @AppStorage(\.suggestionCodeFontSize) var fontSize @AppStorage(\.suggestionDisplayCompactMode) var suggestionDisplayCompactMode + @AppStorage(\.suggestionPresentationMode) var suggestionPresentationMode struct ToolBar: View { @ObservedObject var suggestion: CodeSuggestionProvider @@ -112,7 +113,12 @@ struct CodeBlockSuggestionPanel: View { ToolBar(suggestion: suggestion) } } - .xcodeStyleFrame() + .xcodeStyleFrame(cornerRadius: { + switch suggestionPresentationMode { + case .nearbyTextCursor: 6 + case .floatingWidget: nil + } + }()) } } @@ -143,13 +149,7 @@ struct CodeBlockSuggestionPanel: View { }() )) .frame(width: 450, height: 400) - .background { - HStack { - Color.red - Color.green - Color.blue - } - } + .padding() } #Preview("Code Block Suggestion Panel Compact Mode") { @@ -178,13 +178,7 @@ struct CodeBlockSuggestionPanel: View { )) .preferredColorScheme(.light) .frame(width: 450, height: 400) - .background { - HStack { - Color.red - Color.green - Color.blue - } - } + .padding() } #Preview("Code Block Suggestion Panel Highlight ObjC") { @@ -201,12 +195,6 @@ struct CodeBlockSuggestionPanel: View { )) .preferredColorScheme(.light) .frame(width: 450, height: 400) - .background { - HStack { - Color.red - Color.green - Color.blue - } - } + .padding() } diff --git a/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift b/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift index 0c1cc709..87cbf182 100644 --- a/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift +++ b/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift @@ -51,7 +51,11 @@ public extension SuggestionWidgetController { } func discardSuggestion() { - store.send(.panel(.discardSuggestion)) + store.withState { state in + if state.panelState.content.suggestion != nil { + store.send(.panel(.discardSuggestion)) + } + } } #warning("TODO: Make a progress controller that doesn't use TCA.") diff --git a/Core/Sources/SuggestionWidget/WidgetWindowsController.swift b/Core/Sources/SuggestionWidget/WidgetWindowsController.swift index 454c79ee..60ff0b18 100644 --- a/Core/Sources/SuggestionWidget/WidgetWindowsController.swift +++ b/Core/Sources/SuggestionWidget/WidgetWindowsController.swift @@ -58,7 +58,7 @@ actor WidgetWindowsController: NSObject { xcodeInspector.$focusedEditor.sink { [weak self] editor in guard let editor else { return } - Task { [weak self] in await self?.observe(to: editor) } + Task { [weak self] in await self?.observe(toEditor: editor) } }.store(in: &cancellable) xcodeInspector.$completionPanel.sink { [weak self] newValue in @@ -278,17 +278,16 @@ private extension WidgetWindowsController { } guard currentApplicationProcessIdentifier != app.processIdentifier else { return } currentApplicationProcessIdentifier = app.processIdentifier - observe(to: app) + observe(toApp: app) } - func observe(to app: AppInstanceInspector) { + func observe(toApp app: AppInstanceInspector) { guard let app = app as? XcodeAppInstanceInspector else { return } let notifications = app.axNotifications observeToAppTask?.cancel() observeToAppTask = Task { await windows.orderFront() - let documentURL = await MainActor.run { store.withState { $0.focusingDocumentURL } } for await notification in await notifications.notifications() { try Task.checkCancellation() @@ -296,12 +295,17 @@ private extension WidgetWindowsController { /// so the transition looks better. func hideWidgetForTransitions() async { let newDocumentURL = await xcodeInspector.safe.realtimeActiveDocumentURL + let documentURL = await MainActor.run { store.withState { $0.focusingDocumentURL } } if documentURL != newDocumentURL { await send(.panel(.removeDisplayedContent)) await hidePanelWindows() } await send(.updateFocusingDocumentURL) } + + func removeContent() async { + await send(.panel(.removeDisplayedContent)) + } func updateWidgetsAndNotifyChangeOfEditor(immediately: Bool) async { await send(.panel(.switchToAnotherEditorAndUpdateContent)) @@ -309,9 +313,9 @@ private extension WidgetWindowsController { updateWindowOpacity(immediately: immediately) } - func updateWidgets() async { - updateWindowLocation(animated: false, immediately: false) - updateWindowOpacity(immediately: false) + func updateWidgets(immediately: Bool) async { + updateWindowLocation(animated: false, immediately: immediately) + updateWindowOpacity(immediately: immediately) } switch notification.kind { @@ -319,8 +323,10 @@ private extension WidgetWindowsController { await hideWidgetForTransitions() await updateWidgetsAndNotifyChangeOfEditor(immediately: true) case .applicationActivated: + await removeContent() await updateWidgetsAndNotifyChangeOfEditor(immediately: false) case .mainWindowChanged: + await removeContent() await updateWidgetsAndNotifyChangeOfEditor(immediately: false) case .moved, .resized, @@ -328,7 +334,7 @@ private extension WidgetWindowsController { .windowResized, .windowMiniaturized, .windowDeminiaturized: - await updateWidgets() + await updateWidgets(immediately: false) case .created, .uiElementDestroyed, .xcodeCompletionPanelChanged, .applicationDeactivated: continue @@ -337,7 +343,7 @@ private extension WidgetWindowsController { } } - func observe(to editor: SourceEditor) { + func observe(toEditor editor: SourceEditor) { observeToFocusedEditorTask?.cancel() observeToFocusedEditorTask = Task { let selectionRangeChange = await editor.axNotifications.notifications() diff --git a/Pro b/Pro index a3792303..a26917fd 160000 --- a/Pro +++ b/Pro @@ -1 +1 @@ -Subproject commit a37923038f72208000a3829683d7bdfb8d939e28 +Subproject commit a26917fd375041c5ddec59f1d4458c0fdd04df01 diff --git a/Tool/Package.swift b/Tool/Package.swift index 49950fa8..61b90ca5 100644 --- a/Tool/Package.swift +++ b/Tool/Package.swift @@ -160,6 +160,7 @@ let package = Package( .target( name: "AXNotificationStream", dependencies: [ + "Preferences", "Logger", ] ), diff --git a/Tool/Sources/AXNotificationStream/AXNotificationStream.swift b/Tool/Sources/AXNotificationStream/AXNotificationStream.swift index 6287944a..89fca015 100644 --- a/Tool/Sources/AXNotificationStream/AXNotificationStream.swift +++ b/Tool/Sources/AXNotificationStream/AXNotificationStream.swift @@ -2,6 +2,7 @@ import AppKit import ApplicationServices import Foundation import Logger +import Preferences public final class AXNotificationStream: AsyncSequence { public typealias Stream = AsyncStream @@ -53,6 +54,12 @@ public final class AXNotificationStream: AsyncSequence { self.file = file self.line = line self.function = function + + let mode: CFRunLoopMode = UserDefaults.shared + .value(for: \.observeToAXNotificationWithDefaultMode) ? .defaultMode : .commonModes + + let runLoop: CFRunLoop = CFRunLoopGetMain() + var cont: Continuation! stream = Stream { continuation in cont = continuation @@ -88,17 +95,17 @@ public final class AXNotificationStream: AsyncSequence { AXObserverRemoveNotification(observer, observingElement, name as CFString) } CFRunLoopRemoveSource( - CFRunLoopGetMain(), + runLoop, AXObserverGetRunLoopSource(observer), - .commonModes + mode ) } Task { @MainActor [weak self] in CFRunLoopAddSource( - CFRunLoopGetMain(), + runLoop, AXObserverGetRunLoopSource(observer), - .commonModes + mode ) var pendingRegistrationNames = Set(notificationNames) var retry = 0 diff --git a/Tool/Sources/Preferences/Keys.swift b/Tool/Sources/Preferences/Keys.swift index d1257dbf..d020accf 100644 --- a/Tool/Sources/Preferences/Keys.swift +++ b/Tool/Sources/Preferences/Keys.swift @@ -604,5 +604,9 @@ public extension UserDefaultPreferenceKeys { key: "FeatureFlag-ToastForTheReasonWhyXcodeInspectorNeedsToBeRestarted" ) } + + var observeToAXNotificationWithDefaultMode: FeatureFlag { + .init(defaultValue: false, key: "FeatureFlag-observeToAXNotificationWithDefaultMode") + } } diff --git a/Version.xcconfig b/Version.xcconfig index c8e46a98..efa04634 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -1,3 +1,3 @@ -APP_VERSION = 0.31.1 -APP_BUILD = 334 +APP_VERSION = 0.31.2 +APP_BUILD = 340 diff --git a/appcast.xml b/appcast.xml index 234647e1..294301d0 100644 --- a/appcast.xml +++ b/appcast.xml @@ -2,6 +2,18 @@ Copilot for Xcode + + 0.31.2 + Thu, 14 Mar 2024 22:26:51 +0800 + 340 + 0.31.2 + 12.0 + + https://github.com/intitni/CopilotForXcode/releases/tag/0.31.2 + + + + 0.31.1 Wed, 13 Mar 2024 21:24:28 +0800