From b297210f84443bbdec522b957a97e3301302eca4 Mon Sep 17 00:00:00 2001 From: 3rdBaseWildcat Date: Fri, 31 Jan 2025 15:26:53 -0500 Subject: [PATCH 1/4] Create desktop.ini --- Tool/Sources/desktop.ini | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Tool/Sources/desktop.ini diff --git a/Tool/Sources/desktop.ini b/Tool/Sources/desktop.ini new file mode 100644 index 00000000..d957fd18 --- /dev/null +++ b/Tool/Sources/desktop.ini @@ -0,0 +1,4 @@ +[ViewState] +Mode= +Vid= +FolderType=Generic From b1fcfcf0a7630533f14bafd37ec8fa8d7c24278e Mon Sep 17 00:00:00 2001 From: 3rdBaseWildcat <187662658+3rdBaseWildcat@users.noreply.github.com> Date: Fri, 18 Apr 2025 22:27:20 -0400 Subject: [PATCH 2/4] Create desktop.ini --- Tool/Sources/desktop.ini | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Tool/Sources/desktop.ini diff --git a/Tool/Sources/desktop.ini b/Tool/Sources/desktop.ini new file mode 100644 index 00000000..d957fd18 --- /dev/null +++ b/Tool/Sources/desktop.ini @@ -0,0 +1,4 @@ +[ViewState] +Mode= +Vid= +FolderType=Generic From 63b5bcaaf0653c0400b0016fb4d8cdb05dc42339 Mon Sep 17 00:00:00 2001 From: 3rdBaseWildcat <187662658+3rdBaseWildcat@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:31:06 -0400 Subject: [PATCH 3/4] Pre-release 0.22.73 --- .../AccountSettings/GitHubCopilotView.swift | 267 ++++++++++++++++++ .../SuggesionSettingProxyView.swift | 88 ++++++ ...stionFeatureDisabledLanguageListView.swift | 115 ++++++++ ...gestionFeatureEnabledProjectListView.swift | 140 +++++++++ ...SuggestionSettingsGeneralSectionView.swift | 49 ++++ .../Suggestion/SuggestionSettingsView.swift | 22 ++ .../Sources/HostApp/FeatureSettingsView.swift | 21 ++ .../CodeHighlightThemePicker.swift | 71 +++++ .../HostApp/SharedComponents/SubSection.swift | 132 +++++++++ Docs/downloaded-from-internet.png | Bin 0 -> 252966 bytes ExtensionService/AuthStatusChecker.swift | 41 +++ Script/next-version.sh | 57 ++++ clipboard.txt | 70 +++++ 13 files changed, 1073 insertions(+) create mode 100644 Core/Sources/HostApp/AccountSettings/GitHubCopilotView.swift create mode 100644 Core/Sources/HostApp/FeatureSettings/Suggestion/SuggesionSettingProxyView.swift create mode 100644 Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionFeatureDisabledLanguageListView.swift create mode 100644 Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionFeatureEnabledProjectListView.swift create mode 100644 Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionSettingsGeneralSectionView.swift create mode 100644 Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionSettingsView.swift create mode 100644 Core/Sources/HostApp/FeatureSettingsView.swift create mode 100644 Core/Sources/HostApp/SharedComponents/CodeHighlightThemePicker.swift create mode 100644 Core/Sources/HostApp/SharedComponents/SubSection.swift create mode 100644 Docs/downloaded-from-internet.png create mode 100644 ExtensionService/AuthStatusChecker.swift create mode 100755 Script/next-version.sh create mode 100644 clipboard.txt diff --git a/Core/Sources/HostApp/AccountSettings/GitHubCopilotView.swift b/Core/Sources/HostApp/AccountSettings/GitHubCopilotView.swift new file mode 100644 index 00000000..e641760d --- /dev/null +++ b/Core/Sources/HostApp/AccountSettings/GitHubCopilotView.swift @@ -0,0 +1,267 @@ +import AppKit +import Client +import GitHubCopilotService +import Preferences +import SharedUIComponents +import SuggestionBasic +import SwiftUI + +struct SignInResponse { + let userCode: String + let verificationURL: URL +} + +struct GitHubCopilotView: View { + static var copilotAuthService: GitHubCopilotAuthServiceType? + + class Settings: ObservableObject { + @AppStorage("username") var username: String = "" + @AppStorage(\.disableGitHubCopilotSettingsAutoRefreshOnAppear) + var disableGitHubCopilotSettingsAutoRefreshOnAppear + init() {} + } + + @Environment(\.openURL) var openURL + @Environment(\.toast) var toast + @StateObject var settings = Settings() + + @State var status: GitHubCopilotAccountStatus? + @State var signInResponse: SignInResponse? + @State var version: String? + @State var isRunningAction: Bool = false + @State var isSignInAlertPresented = false + @State var xcodeBetaAccessAlert = false + @State var waitingForSignIn = false + + func getGitHubCopilotAuthService() throws -> GitHubCopilotAuthServiceType { + if let service = Self.copilotAuthService { return service } + let service = try GitHubCopilotService() + Self.copilotAuthService = service + return service + } + + var body: some View { + HStack { + VStack(alignment: .leading, spacing: 8) { + Text("Language Server Version: \(version ?? "Loading..")") + .alert(isPresented: $xcodeBetaAccessAlert) { + Alert( + title: Text("Xcode Beta Access Not Granted"), + message: Text( + "Logged in user does not have access to GitHub Copilot for Xcode" + ), + dismissButton: .default(Text("Close")) + ) + } + + if waitingForSignIn { + Text("Status: Waiting for GitHub authentication") + } else { + Text(""" + Status: \(status?.description ?? "Loading..")\ + \(xcodeBetaAccessAlert ? " - Xcode Beta Access Not Granted" : "") + """) + } + + HStack(alignment: .center) { + Button("Refresh") { + checkStatus() + } + if waitingForSignIn { + Button("Cancel") { cancelWaiting() } + } else if status == .notSignedIn { + Button("Sign In") { signIn() } + .alert( + signInResponse?.userCode ?? "", + isPresented: $isSignInAlertPresented, + presenting: signInResponse) { _ in + Button("Cancel", role: .cancel, action: {}) + Button("Copy Code and Open", action: copyAndOpen) + } message: { response in + Text(""" + Please enter the above code in the \ + GitHub website to authorize your \ + GitHub account with Copilot for Xcode. + + \(response?.verificationURL.absoluteString ?? "") + """) + } + } + if status == .ok || status == .alreadySignedIn || + status == .notAuthorized + { + Button("Sign Out") { signOut() } + } + if isRunningAction || waitingForSignIn { + ActivityIndicatorView() + } + } + .opacity(isRunningAction ? 0.8 : 1) + .disabled(isRunningAction) + } + .padding() + + Spacer() + } + .onAppear { + if isPreview { return } + if settings.disableGitHubCopilotSettingsAutoRefreshOnAppear { return } + checkStatus() + } + .textFieldStyle(.roundedBorder) + .onReceive(FeatureFlagNotifierImpl.shared.featureFlagsDidChange) { flags in + self.xcodeBetaAccessAlert = flags.x != true + } + } + + func checkStatus() { + Task { + isRunningAction = true + defer { isRunningAction = false } + do { + let service = try getGitHubCopilotAuthService() + status = try await service.checkStatus() + version = try await service.version() + isRunningAction = false + + if status != .ok, status != .notSignedIn { + toast( + "GitHub Copilot status is not \"ok\". Please check if you have a valid GitHub Copilot subscription.", + + .error + ) + } + } catch { + toast(error.localizedDescription, .error) + } + } + } + + func signIn() { + Task { + isRunningAction = true + defer { isRunningAction = false } + do { + let service = try getGitHubCopilotAuthService() + let (uri, userCode) = try await service.signInInitiate() + guard let url = URL(string: uri) else { + toast("Verification URI is incorrect.", .error) + return + } + self.signInResponse = .init(userCode: userCode, verificationURL: url) + isSignInAlertPresented = true + } catch { + toast(error.localizedDescription, .error) + } + } + } + + func copyAndOpen() { + waitingForSignIn = true + guard let signInResponse else { + toast("Missing sign in details.", .error) + return + } + // Copy the device code to the clipboard + let pasteboard = NSPasteboard.general + pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) + pasteboard.setString(signInResponse.userCode, forType: NSPasteboard.PasteboardType.string) + toast("Sign-in code \(signInResponse.userCode) copied", .info) + // Open verification URL in default browser + openURL(signInResponse.verificationURL) + // Wait for signInConfirm response + waitForSignIn() + } + + func waitForSignIn() { + Task { + do { + guard waitingForSignIn else { return } + guard let signInResponse else { + waitingForSignIn = false + return + } + let service = try getGitHubCopilotAuthService() + let (username, status) = try await service.signInConfirm(userCode: signInResponse.userCode) + waitingForSignIn = false + self.settings.username = username + self.status = status + } catch let error as GitHubCopilotError { + if case .languageServerError(.timeout) = error { + // TODO figure out how to extend the default timeout on an LSP request + // Until then, reissue request + waitForSignIn() + return + } + throw error + } catch { + toast(error.localizedDescription, .error) + } + } + + } + + func cancelWaiting() { + waitingForSignIn = false + } + + func signOut() { + Task { + isRunningAction = true + defer { isRunningAction = false } + do { + let service = try getGitHubCopilotAuthService() + status = try await service.signOut() + } catch { + toast(error.localizedDescription, .error) + } + } + } + + func refreshConfiguration() { + NotificationCenter.default.post( + name: .gitHubCopilotShouldRefreshEditorInformation, + object: nil + ) + + Task { + let service = try getService() + do { + try await service.postNotification( + name: Notification.Name + .gitHubCopilotShouldRefreshEditorInformation.rawValue + ) + } catch { + toast(error.localizedDescription, .error) + } + } + } +} + +struct ActivityIndicatorView: NSViewRepresentable { + func makeNSView(context _: Context) -> NSProgressIndicator { + let progressIndicator = NSProgressIndicator() + progressIndicator.style = .spinning + progressIndicator.controlSize = .small + progressIndicator.startAnimation(nil) + return progressIndicator + } + + func updateNSView(_: NSProgressIndicator, context _: Context) { + // No-op + } +} + +struct CopilotView_Previews: PreviewProvider { + static var previews: some View { + VStack(alignment: .leading, spacing: 8) { + GitHubCopilotView(status: .notSignedIn, version: "1.0.0") + GitHubCopilotView(status: .alreadySignedIn, isRunningAction: true) + GitHubCopilotView(settings: .init(), status: .alreadySignedIn, xcodeBetaAccessAlert: true) + GitHubCopilotView(settings: .init(), status: .notSignedIn, waitingForSignIn: true) + } + .padding(.all, 8) + .previewLayout(.sizeThatFits) + } +} + diff --git a/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggesionSettingProxyView.swift b/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggesionSettingProxyView.swift new file mode 100644 index 00000000..caa217b9 --- /dev/null +++ b/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggesionSettingProxyView.swift @@ -0,0 +1,88 @@ +import Preferences +import SharedUIComponents +import SwiftUI +import XPCShared +import Toast +import Client + +struct SuggesionSettingProxyView: View { + + class Settings: ObservableObject { + @AppStorage("username") var username: String = "" + @AppStorage(\.gitHubCopilotProxyHost) var gitHubCopilotProxyHost + @AppStorage(\.gitHubCopilotProxyPort) var gitHubCopilotProxyPort + @AppStorage(\.gitHubCopilotProxyUsername) var gitHubCopilotProxyUsername + @AppStorage(\.gitHubCopilotProxyPassword) var gitHubCopilotProxyPassword + @AppStorage(\.gitHubCopilotUseStrictSSL) var gitHubCopilotUseStrictSSL + @AppStorage(\.gitHubCopilotEnterpriseURI) var gitHubCopilotEnterpriseURI + + init() {} + } + + @StateObject var settings = Settings() + @Environment(\.toast) var toast + + var body: some View { + VStack(alignment: .leading) { + SettingsDivider("Enterprise") + + Form { + TextField( + text: $settings.gitHubCopilotEnterpriseURI, + prompt: Text("Leave it blank if none is available.") + ) { + Text("Auth provider URL") + } + } + + SettingsDivider("Proxy") + + Form { + TextField( + text: $settings.gitHubCopilotProxyHost, + prompt: Text("xxx.xxx.xxx.xxx, leave it blank to disable proxy.") + ) { + Text("Proxy host") + } + TextField(text: $settings.gitHubCopilotProxyPort, prompt: Text("80")) { + Text("Proxy port") + } + TextField(text: $settings.gitHubCopilotProxyUsername) { + Text("Proxy username") + } + SecureField(text: $settings.gitHubCopilotProxyPassword) { + Text("Proxy password") + } + Toggle("Proxy strict SSL", isOn: $settings.gitHubCopilotUseStrictSSL) + + Button("Refresh configurations") { + refreshConfiguration() + }.padding(.top, 6) + } + } + .textFieldStyle(.roundedBorder) + } + + func refreshConfiguration() { + NotificationCenter.default.post( + name: .gitHubCopilotShouldRefreshEditorInformation, + object: nil + ) + + Task { + let service = try getService() + do { + try await service.postNotification( + name: Notification.Name + .gitHubCopilotShouldRefreshEditorInformation.rawValue + ) + } catch { + toast(error.localizedDescription, .error) + } + } + } +} + +#Preview { + SuggesionSettingProxyView() +} diff --git a/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionFeatureDisabledLanguageListView.swift b/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionFeatureDisabledLanguageListView.swift new file mode 100644 index 00000000..fa3074e3 --- /dev/null +++ b/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionFeatureDisabledLanguageListView.swift @@ -0,0 +1,115 @@ +import SuggestionBasic +import SwiftUI +import SharedUIComponents + +extension List { + @ViewBuilder + func removeBackground() -> some View { + if #available(macOS 13.0, *) { + scrollContentBackground(.hidden) + .listRowBackground(EmptyView()) + } else { + background(Color.clear) + .listRowBackground(EmptyView()) + } + } +} + +struct SuggestionFeatureDisabledLanguageListView: View { + final class Settings: ObservableObject { + @AppStorage(\.suggestionFeatureDisabledLanguageList) + var suggestionFeatureDisabledLanguageList: [String] + + init(suggestionFeatureDisabledLanguageList: AppStorage<[String]>? = nil) { + if let list = suggestionFeatureDisabledLanguageList { + _suggestionFeatureDisabledLanguageList = list + } + } + } + + var isOpen: Binding + @State var isAddingNewProject = false + @StateObject var settings = Settings() + + var body: some View { + VStack(spacing: 0) { + HStack { + Button(action: { + self.isOpen.wrappedValue = false + }) { + Image(systemName: "xmark.circle.fill") + .foregroundStyle(.secondary) + .padding() + } + .buttonStyle(.plain) + Text("Disabled Languages") + Spacer() + } + .background(Color(nsColor: .separatorColor)) + + List { + ForEach( + settings.suggestionFeatureDisabledLanguageList, + id: \.self + ) { language in + HStack { + Text(language.capitalized) + .contextMenu { + Button("Remove") { + settings.suggestionFeatureDisabledLanguageList.removeAll( + where: { $0 == language } + ) + } + } + Spacer() + + Button(action: { + settings.suggestionFeatureDisabledLanguageList.removeAll( + where: { $0 == language } + ) + }) { + Image(systemName: "trash.fill") + .foregroundStyle(.secondary) + } + .buttonStyle(.plain) + } + } + .modify { view in + if #available(macOS 13.0, *) { + view.listRowSeparator(.hidden).listSectionSeparator(.hidden) + } else { + view + } + } + } + .removeBackground() + .overlay { + if settings.suggestionFeatureDisabledLanguageList.isEmpty { + Text(""" + Empty + Disable the language of a file by right clicking the circular widget. + """) + .multilineTextAlignment(.center) + .padding() + } + } + } + .focusable(false) + .frame(width: 300, height: 400) + .background(Color(nsColor: .windowBackgroundColor)) + } +} + +struct SuggestionFeatureDisabledLanguageListView_Preview: PreviewProvider { + static var previews: some View { + SuggestionFeatureDisabledLanguageListView( + isOpen: .constant(true), + settings: .init(suggestionFeatureDisabledLanguageList: .init(wrappedValue: [ + "hello/2", + "hello/3", + "hello/4", + ], "SuggestionFeatureDisabledLanguageListView_Preview")) + ) + } +} + diff --git a/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionFeatureEnabledProjectListView.swift b/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionFeatureEnabledProjectListView.swift new file mode 100644 index 00000000..f57cd5e4 --- /dev/null +++ b/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionFeatureEnabledProjectListView.swift @@ -0,0 +1,140 @@ +import SharedUIComponents +import SwiftUI + +struct SuggestionFeatureEnabledProjectListView: View { + final class Settings: ObservableObject { + @AppStorage(\.suggestionFeatureEnabledProjectList) + var suggestionFeatureEnabledProjectList: [String] + + init(suggestionFeatureEnabledProjectList: AppStorage<[String]>? = nil) { + if let list = suggestionFeatureEnabledProjectList { + _suggestionFeatureEnabledProjectList = list + } + } + } + + var isOpen: Binding + @State var isAddingNewProject = false + @StateObject var settings = Settings() + + var body: some View { + VStack(spacing: 0) { + HStack { + Button(action: { + self.isOpen.wrappedValue = false + }) { + Image(systemName: "xmark.circle.fill") + .foregroundStyle(.secondary) + .padding() + } + .buttonStyle(.plain) + Text("Enabled Projects") + Spacer() + Button(action: { + isAddingNewProject = true + }) { + Image(systemName: "plus.circle.fill") + .foregroundStyle(.secondary) + .padding() + } + .buttonStyle(.plain) + } + .background(Color(nsColor: .separatorColor)) + + List { + ForEach( + settings.suggestionFeatureEnabledProjectList, + id: \.self + ) { project in + HStack { + Text(project) + .contextMenu { + Button("Remove") { + settings.suggestionFeatureEnabledProjectList.removeAll( + where: { $0 == project } + ) + } + } + Spacer() + + Button(action: { + settings.suggestionFeatureEnabledProjectList.removeAll( + where: { $0 == project } + ) + }) { + Image(systemName: "trash.fill") + .foregroundStyle(.secondary) + } + .buttonStyle(.plain) + } + } + .modify { view in + if #available(macOS 13.0, *) { + view.listRowSeparator(.hidden).listSectionSeparator(.hidden) + } else { + view + } + } + } + .removeBackground() + .overlay { + if settings.suggestionFeatureEnabledProjectList.isEmpty { + Text(""" + Empty + Add project with "+" button + Or right clicking the circular widget + """) + .multilineTextAlignment(.center) + } + } + } + .focusable(false) + .frame(width: 300, height: 400) + .background(Color(nsColor: .windowBackgroundColor)) + .sheet(isPresented: $isAddingNewProject) { + SuggestionFeatureAddEnabledProjectView(isOpen: $isAddingNewProject, settings: settings) + } + } +} + +struct SuggestionFeatureAddEnabledProjectView: View { + var isOpen: Binding + var settings: SuggestionFeatureEnabledProjectListView.Settings + @State var rootPath = "" + + var body: some View { + VStack { + Text( + "Enter the root path of the project. Do not use `~` to replace /Users/yourUserName." + ) + TextField("Root path", text: $rootPath) + HStack { + Spacer() + Button("Cancel") { + isOpen.wrappedValue = false + } + Button("Add") { + settings.suggestionFeatureEnabledProjectList.append(rootPath) + isOpen.wrappedValue = false + } + } + } + .padding() + .frame(minWidth: 500) + .background(Color(nsColor: .windowBackgroundColor)) + } +} + +struct SuggestionFeatureEnabledProjectListView_Preview: PreviewProvider { + static var previews: some View { + SuggestionFeatureEnabledProjectListView( + isOpen: .constant(true), + settings: .init(suggestionFeatureEnabledProjectList: .init(wrappedValue: [ + "hello/2", + "hello/3", + "hello/4", + ], "SuggestionFeatureEnabledProjectListView_Preview")) + ) + } +} + diff --git a/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionSettingsGeneralSectionView.swift b/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionSettingsGeneralSectionView.swift new file mode 100644 index 00000000..35f7caa7 --- /dev/null +++ b/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionSettingsGeneralSectionView.swift @@ -0,0 +1,49 @@ +import Client +import Preferences +import SharedUIComponents +import SwiftUI +import XPCShared + +struct SuggestionSettingsGeneralSectionView: View { + final class Settings: ObservableObject { + @AppStorage(\.realtimeSuggestionToggle) + var realtimeSuggestionToggle + @AppStorage(\.suggestionFeatureEnabledProjectList) + var suggestionFeatureEnabledProjectList + @AppStorage(\.acceptSuggestionWithTab) + var acceptSuggestionWithTab + } + + @StateObject var settings = Settings() + @State var isSuggestionFeatureDisabledLanguageListViewOpen = false + + var body: some View { + Form { + Toggle(isOn: $settings.realtimeSuggestionToggle) { + Text("Request suggestions in real-time") + } + + Toggle(isOn: $settings.acceptSuggestionWithTab) { + HStack { + Text("Accept suggestions with Tab") + } + } + + HStack { + Button("Disabled language list") { + isSuggestionFeatureDisabledLanguageListViewOpen = true + } + }.sheet(isPresented: $isSuggestionFeatureDisabledLanguageListViewOpen) { + SuggestionFeatureDisabledLanguageListView( + isOpen: $isSuggestionFeatureDisabledLanguageListViewOpen + ) + } + } + } +} + +#Preview { + SuggestionSettingsGeneralSectionView() + .padding() +} + diff --git a/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionSettingsView.swift b/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionSettingsView.swift new file mode 100644 index 00000000..f5a99acd --- /dev/null +++ b/Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionSettingsView.swift @@ -0,0 +1,22 @@ +import Client +import Preferences +import SharedUIComponents +import SwiftUI +import XPCShared + +struct SuggestionSettingsView: View { + var body: some View { + ScrollView { + SuggestionSettingsGeneralSectionView() + SuggesionSettingProxyView() + }.padding() + } +} + +struct SuggestionSettingsView_Previews: PreviewProvider { + static var previews: some View { + SuggestionSettingsView() + .frame(width: 600, height: 500) + } +} + diff --git a/Core/Sources/HostApp/FeatureSettingsView.swift b/Core/Sources/HostApp/FeatureSettingsView.swift new file mode 100644 index 00000000..bee029b3 --- /dev/null +++ b/Core/Sources/HostApp/FeatureSettingsView.swift @@ -0,0 +1,21 @@ +import SwiftUI + +struct FeatureSettingsView: View { + var body: some View { + SuggestionSettingsView() + .sidebarItem( + tag: 0, + title: "Suggestion", + subtitle: "Generate suggestions for your code", + image: "lightbulb" + ) + } +} + +struct FeatureSettingsView_Previews: PreviewProvider { + static var previews: some View { + FeatureSettingsView() + .frame(width: 800) + } +} + diff --git a/Core/Sources/HostApp/SharedComponents/CodeHighlightThemePicker.swift b/Core/Sources/HostApp/SharedComponents/CodeHighlightThemePicker.swift new file mode 100644 index 00000000..9c20b7dd --- /dev/null +++ b/Core/Sources/HostApp/SharedComponents/CodeHighlightThemePicker.swift @@ -0,0 +1,71 @@ +import Foundation +import Preferences +import SwiftUI + +public struct CodeHighlightThemePicker: View { + public enum Scenario { + case suggestion + case promptToCode + case chat + } + + let scenario: Scenario + + public init(scenario: Scenario) { + self.scenario = scenario + } + + public var body: some View { + switch scenario { + case .suggestion: + SuggestionThemePicker() + case .promptToCode: + PromptToCodeThemePicker() + case .chat: + ChatThemePicker() + } + } + + struct SuggestionThemePicker: View { + @AppStorage(\.syncSuggestionHighlightTheme) var sync: Bool + var body: some View { + SyncToggle(sync: $sync) + } + } + + struct PromptToCodeThemePicker: View { + @AppStorage(\.syncPromptToCodeHighlightTheme) var sync: Bool + var body: some View { + SyncToggle(sync: $sync) + } + } + + struct ChatThemePicker: View { + @AppStorage(\.syncChatCodeHighlightTheme) var sync: Bool + var body: some View { + SyncToggle(sync: $sync) + } + } + + struct SyncToggle: View { + @Binding var sync: Bool + + var body: some View { + VStack(alignment: .leading) { + Toggle(isOn: $sync) { + Text("Sync color scheme with Xcode") + } + + Text("To refresh the theme, you must activate the extension service app once.") + .font(.footnote) + .foregroundColor(.secondary) + } + } + } +} + +#Preview { + @State var sync = false + return CodeHighlightThemePicker.SyncToggle(sync: $sync) +} + diff --git a/Core/Sources/HostApp/SharedComponents/SubSection.swift b/Core/Sources/HostApp/SharedComponents/SubSection.swift new file mode 100644 index 00000000..b294e3e4 --- /dev/null +++ b/Core/Sources/HostApp/SharedComponents/SubSection.swift @@ -0,0 +1,132 @@ +import SwiftUI + +struct SubSection: View { + let title: Title + let description: Description + @ViewBuilder let content: () -> Content + + init(title: Title, description: Description, @ViewBuilder content: @escaping () -> Content) { + self.title = title + self.description = description + self.content = content + } + + var body: some View { + VStack(alignment: .leading) { + if !(title is EmptyView && description is EmptyView) { + VStack(alignment: .leading, spacing: 8) { + title + .font(.system(size: 14).weight(.semibold)) + + description + .multilineTextAlignment(.leading) + .foregroundStyle(.secondary) + } + .frame(maxWidth: .infinity, alignment: .leading) + } + + if !(title is EmptyView && description is EmptyView) { + Divider().padding(.bottom, 4) + } + + content() + } + .padding() + .background { + RoundedRectangle(cornerRadius: 8) + .fill(Color.secondary.opacity(0.1)) + } + .overlay { + RoundedRectangle(cornerRadius: 8) + .strokeBorder(Color.secondary.opacity(0.2)) + } + } +} + +extension SubSection where Description == Text { + init(title: Title, description: String, @ViewBuilder content: @escaping () -> Content) { + self.init(title: title, description: Text(description), content: content) + } +} + +extension SubSection where Description == EmptyView { + init(title: Title, @ViewBuilder content: @escaping () -> Content) { + self.init(title: title, description: EmptyView(), content: content) + } +} + +extension SubSection where Title == EmptyView { + init(description: Description, @ViewBuilder content: @escaping () -> Content) { + self.init(title: EmptyView(), description: description, content: content) + } +} + +extension SubSection where Title == EmptyView, Description == EmptyView { + init(@ViewBuilder content: @escaping () -> Content) { + self.init(title: EmptyView(), description: EmptyView(), content: content) + } +} + +extension SubSection where Title == EmptyView, Description == Text { + init(description: String, @ViewBuilder content: @escaping () -> Content) { + self.init(title: EmptyView(), description: description, content: content) + } +} + +#Preview("Sub Section Default Style") { + SubSection(title: Text("Title"), description: "Description") { + Toggle(isOn: .constant(true), label: { + Text("Label") + }) + + Toggle(isOn: .constant(true), label: { + Text("Label") + }) + + Picker("Label", selection: .constant(0)) { + Text("Label").tag(0) + Text("Label").tag(1) + Text("Label").tag(2) + } + } + .padding() +} + +#Preview("Sub Section No Title") { + SubSection(description: "Description") { + Toggle(isOn: .constant(true), label: { + Text("Label") + }) + + Toggle(isOn: .constant(true), label: { + Text("Label") + }) + + Picker("Label", selection: .constant(0)) { + Text("Label").tag(0) + Text("Label").tag(1) + Text("Label").tag(2) + } + } + .padding() +} + +#Preview("Sub Section No Title or Description") { + SubSection { + Toggle(isOn: .constant(true), label: { + Text("Label") + }) + + Toggle(isOn: .constant(true), label: { + Text("Label") + }) + + Picker("Label", selection: .constant(0)) { + Text("Label").tag(0) + Text("Label").tag(1) + Text("Label").tag(2) + } + } + .padding() +} + diff --git a/Docs/downloaded-from-internet.png b/Docs/downloaded-from-internet.png new file mode 100644 index 0000000000000000000000000000000000000000..cd4b1ea7c01512b23c62ba54c7e0ca2cfb6a136e GIT binary patch literal 252966 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 e)sjghD zqyfGdHwRo@5fMP$L1NFqEKtbURFvI7{8HZ0Zb)M+*ppfjW}XM#i!vgwegS_27NGn>xK$ z{?>&&c2N0vg8Jk61BuUXK8s-u-*0=QK+)h}*?lsJdKn;zp`SrZl7EUMNh_#bP!3-C z5LepP&(axwnOzDod>!7(fA{g51$@rv(EX&4Rm9w2p>c`&Olj7L>%vuu04Cze-24+q zbkJVfgyXh#$8?5e9a;lRA-}9|-;cSux8?q`IMRvIFhj>7%b?WG=8V9}zS0o4UdxK9 zQX5+<`Y$OD(+TD8cZWz{9~p11W|V%Y)2v$i(wwsKvoyO!wejibqrG04 z^4PAHxRbyBD4Q2vV>iP7xWBya6Ttv%)@5z!UPW}8rSZMwwh7+-)O-M&;meQiNUw<} z_J!fYQSiYRy@#f+ZRzdW*fUeRYOG{2<@=_opD1#33VSuB_a4uWA^w*cz3La*J7bQ7 zt8sO}&_UFp*DdEWFQ9Xqo15Ooqakb0 zqHG-@bNf0 zQxd$fppB%o&Tto7ILhA02{G7h(}qDQ^JAe1j|A=et9F568TSRCDYYKVJGiQH? zf47B)6vz|bP{@R<4AMJxXvj%|y6d+Wz%Q@*a^LlLO305MQ8=Arvq`gxc_n_cTby^Z zsI=C#`rFp+E$tkp@N=?Yk!QX}sl*&!Uz+DOjITm7cE>wya=(jAV0pm&Lw;Z@eWtH% z5ABx3E77;L=eF(~=K8;zdKzHbBjv+Ey5c7O+4mFUHGSKy%8_KtE=N>s934(vM*GV^`6P)aA9FC)vv?ZsodyZ^)lk0I|jRhj73nfJc3&(}b_( zjLwmbLef_3cKixF2^?t1?m#Xms3kL<$PD(Pj=0d9=1$bM+iMgMn8qkCjDd9xC>v~O z5&7K^wXoaj`|^o0`=^lW9KFl!H_Y32+|48O%AdrHe(1=Ziw3yk44Qs+qWN3J#ckDQ z#LaLQN!N*^AA;NqKiO1>BLL{BOZEJW*#wqMwlj$r+A{fru#~G4UQN7c&~A5MaYo>A&sMtsm_F!kEH6H74mWnxL zcBsuAHR+yKGC#hX zxWo9sUa(WXt}-aPMX@*6GhDjFXT5es2oOTaG~%;fN@OtNq~SXo9X*L^TG+w+;o56D z7i9R}!#gz|{+n5{k9dfSEY6UF0^#t0T@L+HVaRV@ts2>veGg`9Y>*IWyR5v%(Kg z$NtrDzV>ITGe<<9Q6G z{P1$Kew42YXFRty1eustuXjt9ye>uj{k5;L0QAyIU>ou9YqE(O9{P1Z2K-dN7Hh%( z;;u{$x`s9xc<2!N+p^DzrH~gLbboNX&L*braOvpafpso;ZzW~?jc?u;`=CBu8zXl8 zT8E%{zT?)Ne!}W~$Hpn%Xd32KxaDg)%}%OlQ%iu;eW`pwy?t9~*2UH(c;Z=qH>98>V;oB%>*o`C=e3Dcq>#LMRed2-y|(-P{n zt{{jDK3OfVDxwM80?=7v6PK#vj(edhoD`WvVT{S52TwB%`BYqJm4aw9hbW{2gw>rA z9f`ArAIW;FL|p!ntQiThHMo2r1bBB-i$R0#6rh8=w6u3`g2g|hYVPZvqb!UokyQLmh5HMCv{rWYU+&AY%3q8W85)DGT3imgl}JSe%id*}ah zC>ub_o@=Z{Ri6=%;N4Xj>HmTPTsplIo38SV)hpCh#5yCHq9s2II-ufsLF7URD|-eA zI2qvP5m;C>x1Tdn>%gZ|=iy*&Z!%y!RIvU0T<>o5_^^d3uzdO?od3Ptfsky2be_FY zY*lb^(KgCdqx%3e-UH7&a(jL3(2K1c6tJ8m^hsQ`4c@(4G6v9yMszvSEDUN~_jC%E zxBoJh^hrBT_Iq71b2v^Tu6rJ}db+dHzJpj;2jT-hFe4Q3BL?{U>honkfETl00k?h< z;WyVhseW8ZX<`-AKY1K}XXpPicOqtd%&J#m{cmEycUYKVoD#XD+|b&vOy*4IBRa1gJx|JKf93kCE!@90XRh5Y7yTe& zQMC1WHOF$UW)x^WII`Q^-KYncm*lLg9P>H6%{y|kCL zgFhzFir9_Fw^b$Dkm%IHV0yPLn#5^!?1?JdR3acBy#5yG$#;Q6|Eue)W=B z27d(I4x>iQ<`7R$?S7HN*|qPDKZ$nc%cp>#l{@k zlUy?mpwV7C9V2a!myS>e+syGL_17E7*ff`~UXec!SlNu3yk# z>XL}kzlw;*HOBdmE13c;rV!%J|09HWjiaC3M8>~#9K{w4PSnXylD8)0$2%4zQc!19 zpbdt6!)7ISmLIv?gm1dp0zgzzH2>o&315U|wvEo}$~_kQrqUMN>cZc~+jbm( zxAdK*j*V~EP_rm}ox?J$(W)4h>wP+O;J>JTL%D&ui1mvIMr#&qDMSgr*@3L}{!r17 zi{kmD1L1093Xi*Za-~+8h3F#u1&CvS8@JN(*v9*!xo3`Y9tXtA%ttHan^}V=sVOnI zb&e_&yi<5PD2Gufw)bd~@?kzL&eO}Usij+y8Wz))JlbLV^^l1;s$$3On7e2pG0CWF z_M9n!BK8*+->uG-N#VCuQDOo4h;%5j2HtgVx;jDVWM>H*or_lu`NnvW9F>vg ztfneHGq`Ho{<~~+jO$FUq;w7k%l1FE?(;FE!oq``y_?MWT78l^U$d4i{Fi&)KAu2? z2JO6NnHXjl9Vs0vRdi`E7SbKTR{Z#0RdVLzFc#(S*qES=v#&mWO-CZ=ZD{wvKxG)M zX)bocb9sy*KU^y3=Ijwlw85>dxs6+!NPa7;S6!Bpyx8|8Pu@gW@-de!=<{{F(Y-?dUsEkAHIuenw908@Y>mE03c$phM5a* zuje-|$loA_;uaj|wG~thcKf4ZF3VnfvRv}NUH}t5EspF8{&|V$3j8~!90F4GO!{BF zt%==K)-j7`h_Uq;XexCSGspRT`u%haxip}I^?W5{8HWmWV-x8H}Rf9Fid*o1EShiOrtZ@xwhi9y(yvZfiC zX`*I1r&i)tlx7-&ep0%uEjGp7M-g{vIBNh4NEFhRd{LFmz`>UTnS*rI1DD0(wf|e> z-3uKc_x&lK^OF2C@+(_}U1r8rU2{1Lim3&$^kMqe4c%3xuLkIg-q1!X9k#RSCTUEz z+{VTwcQlu7KVr*aXu)qm3HcD6O&+>Z&azTjOGg2Ap=nPyqje!)yHJ`qM(q%DUeZA; z;T0ToZ5(%?_$W_I=9o9Xf5~P zH^+~jPBXJDCjG3(3oLlzoLb!<`4M8z^RR?=X*q02ZKTb3y#QZS^>Vsn;H;v;L_=eb zs*UFGTi>?=FM@?b27&UEzC8`i`Ns^|W?jE7MpdcV1y@^z%_nr(Eij;_`bOJ#^y7U%6K`BxqpoAt75fDKjq99#L z=)DH%p%;aO07(dVZf2Z!p7Y^-pYuC^!d z_pX_vB{#KJsOBeX)-)m}&1H8zlPVxK(h$tUjavz_=RFUDmR^}NHSX6o)+(>2ZMi%I z`8!PkWz!kk#`lw6t@mG4iMW0sxw=*{wDquZmqXVX5hd-+3MbC9wY(P`TqSfmP~w=Q z3K^JY+czCwx>OGCFxvr=Aav0*SA8;LS|hNb=@-BiBX3*7C${m-IZT)|D(-CpHPWE3 zHPWzdBtyOYNABZ&u6y)nU$ln#93>~dM^2b!UnP7LRbyMtvI!xD6L5Ir(he8~`5Lq% z6fb=*$Pq`|F5c_u*EAfj0~s&VaqskcYM8J}-mMF&zQQE=D`>*4#}s!GqAM3hwJG|A z0ooq_4q_XiC9Q;ttN#`wxj5NchJVdzBe#XXmRp#?O3JJ3pX+NkAR9_ZgSO?=(kGeT zRrR8R#>&wiU-3_S@9q{wc^L0{j6RMyDr*?v+0zp&7WBFoRcaaWUf?S6y?EBWO#EKl zg7$!N!_Uh}O7lL>ARd24v>+u+;_un}F}R zG9u&0cDq@vrM^+GuvlSp^zLo_h^Lo$*M?f~xr#R$h{~0m`>Z z`GYRm{_JOyv`CV8F#XcCb~@%s+3Um%32wwTy%=zR+RTnNuk^n;};PfMT^m`=w-5)!tG0rVdQ+yL2Cta)>C z7NDv|>EGoVM;(nf?uTyZXfs#Qoe~`&IrUjK&>LJuh8G1gx7^*5%%_%Z~x zn(tq%%Y0fRWHV^85BFhLe)7H@sd@f^Z0Oo{-dGNi zZ#u0qkDePflh6j|!_bKc6pTMin4XPQ6}`$T&Sspw2$9wuS+GCP9|{{32lK*w-zY=q zP~V$V$<8l@$*E;`jFCJY66SRn`mtF1(ev7%AQh0GWjid3>F5wxxOK}Xn>+opI;HH_ z&4+&Xpwm91&+B@vos=u*Gt9JYFe$zpuv@@Cm^c33C8AJrWMM7yYaDG;9_+%O5nF;* z#$O9x%-l6^->NytnQx*KoU#=%hdS_0UB?BQ9=^ZB27kBr0UA7dx5aFjE6D4uAgo&4-77 z?jZn|T%;DGCH_BLa^m>^aLK`#DG@d?x_tTKzLl%fl;lJcuM;T6=Q-}KE)OR07;$!7hi;*&EMD#`x#IVsCecBr>`@qAk9i87#P4@O>uoER?Q&}=*pYeO|a zFZhUg-M`k^k1XA9&$D-M7cvN^^t4FG2rBR4S53VjMmsmPb$xh#z<76$2-apbq)zT? zZ1v?rLn^AQ_AB2$Vq3au>f{bpNOJVyeJA;KUoPX2-%BMI>nBm>0L}>g6A^Pkn|!BU zc`|%sOvU`Xv+rwn^`gRh?84;jq|yn2+F>S5vN6cU6#JwlIbb^ zlx7p9O99I7-OP6{6h`lf=E7O4R|0&wKZ2@cii;0W)J1VXaljDv?O{FgGjMi915%kh zXadCdFZckHa!lH;CCz#z!0o&R>~IesCLY`2s{XRWwed2yH&(sEQZ5OXaBWi38eC+& z_ng_f(=BxClQk;*{EDRg#X;2wK}+b<6mUJ)Ym2okR-~N(aK$fJ(1*Y|d0(ClWqa;) zd(6~9bBmB8bspcJ&bsK%l=1Qnz5b)R`%I^im)2T)&$#JGpFGE#W2^N^SWAn~2zMQ@ zCf#Dq0xj5jS*U*NY!@G2hUIcRs>|1oKPzzOPQ1LC-D^vYpJEXx3}GikM7rq$1iZ&^3Cz zyNSke3VAw2>}6z%g~ChNw!z6^$R$u_mq03xL@O%;@=nx0S|uf4159mEz3I=^csCd0 z$}G0)ryNrdCISq6C1_~7J>WxNBD27^cyqMTt=&3Ez_m~0@q=8P_N;iw;!`=N3Ygj~ z|KeiDybi}S^4BB2R2@{-sFL^Lh(gx}fPVj;;vL#KZWC(!AieU5--;dR{dbEkSpC<) z3f0fPBR^Rdg0gNz51y53vUi`z{${FEbnO zG`%U>mhZdcNcXLhth8-m?#o*^a^Wg~`leeL)&(}oe)Wxy+fl4zz(tbFvL%xsLgUvX zI3P3u(AO}JfKrd_>0qktD4UQH4tz?H%P|iJ~g? z>BKnpdU-Ss36etFFa8-PWVimrFu@)sT(M;ssm)K53cLNM;2j?tr+NEGMW?21`i=N@ zVXnQ_4t>t`A4M^}IB`04`IdHF*$9sG&GV|{iCc2Xsha3crV-VF`yli}sv=t9d42%4 zN8D#U(%7U<7(E;KAxGo`8v7Hc@X?-;*F*A=%&`!)k(>oJ^ZFd`y0y>W0#MZlF{%yt z#Fo4MlGl34bYN1`e8a)dkCW_U`wMlxh3{O->kaEo2kD2D97CsiY7whzsrGH+-%cy` zRKkWftFE8ZHYSJe-guss=}sBDzvI%pIC=nTLg=j>gosr>nWIn5+`TiP- zJL&P{eqax*LN9b1pYFp)sd2;`kTKECmWZe($!q<;FD8p@AyvTFq_yhq(xV@FVaMI zGGx(a^$9Wa3&a^R4HnCaBvp$x@jMUetI}QQjs3Loj-M>bS1-D&v-WIgxvQLSYTY zd!z4Z6hy{xiw?Lbhfu&`?&ZEoY#V7x<2;S%h?QcdpNUNoWJYlj2`k~t==@5hU$}2* zafEz~8k$}6D>LQ6B)gxX>HVeOcHHSVjX1zlD=0r>7YGT)NSch@OAL*UM+CV#*C)$r zs!Rp9dyKw;f^DDeE%d_W62xH#*7oKv@jsgVZ3)YWoJ_{kd!NoI48r~Ew3LP%*=%)B z&?D8F3X4!157lzT3)yjA!#5bo<0=_9|a5ZtJRq8Sqf;4VCxd zL3oV9x-H4-Z7X6%B8R0ZHqOVXEDRG5b&Y~}z{+==A&p|pH+yk&il6XHY&UxWEW5vx zdWu8ldSTj3S~X1tSE_ri&Do5EKhFw)ZtTnWF{PRXJxqMS$*ZfSU?lcLwHnxp^u=dW z!#uY2wJ0QA6br2^t-GtyQAb>EkuKgq8)#ApDC~J5KKe;7OZA;z@m_UqU8?=4LiF!Q;9r2>2E)^$F5~wrr)M5@PJ5v%&F6^CIVL)7XLcw{Xny1X)xs0&^3R~%(Ac_ zZq`vV_crFh!^Klij(4snRQKe}?E^y~rpFX$dK@aaBvl+zIaz)$Do1ja!lIolU-!6c zw6#r_(Ix922F%Qo{^7pObS`8|3M{R2*DEts)G@xW9GgT&?nu`l8QeqqABX?fUX zfL~h?rd23lf|oh#g$)~X7$%IwBV^DMN~?NqQ{f`6EG2~h`4pUut2aiC?Cb_*cp838 zHip%_ns*#E%Hi@nY#XPWRITGq-k9XMU+<#Hcz7AK7oE}9AHBB8#xV4LcPn*aRY6EM zH?k&N+tb8=>Xd0uuaB52N^8n$iM3u~^PQ(F&oq+jRDR~RW4kRnbt|+VdK(&Gf&yvV zSXD*Tef=46lmY%_6b1?Yu-0G-=FLu3wmePw)BiefVg_)DAyf)y>t z{}-<%AV?)AzpoJ1%QVefzSSAR*=`k`%N(7phUx3o2rX@B$pb+m0zQdH!$aP-Dm$Hx zQd|$b#5qYdOSRZ^@e9>*hlzQ*$~Sj;U2hXWYB)4kEC7ThY0<&3-pyz+q1;*YO_V-s z`i0kT6}T(?6M(E0F|o835t5tQ4(@^`L`B}=0G^!Ki3ogp+Yq>khijg~rejvZQL}hX z#zLVMuG>1c&_Pw?sGS?eRupm^RqqF9`r=cWQ9)sL!6DVQIfhpf)7Ld{AAJG`W6r+Yzxm8yRI%qcqqwGFfHIzON!PJyle-J&o#|acQm` ztAoX=*{Pvmlryf0M3TyR2faAeMBkY=Bez#yR$pk73*F53{JAvp{6yBDzS*x$cETK| ztCs-8;Ps0=@-{EB|!~zj6QhI{WBc+YFPJx+N9B+=@)MDQFRO+OF-*XRO zy>p4f@pw>opc#5oJ5J6}bdGk^LsV+7Tn(=H5-_QflG4`XznNk)Nc(t|v`lkrq$g1l zC6DE*sMU~;h_?WlmD%>Xjwe)EeCS{&UcMEt8Lr>adgZZhm5_!-{PIn(U^$DayZ7+a z>JLFLSOtXe)PwDO-ssq+#3RQJ`M&ouy-MQb5qV5;;pHzBfe0MFX1F_}riwa9rl$Zu zNMZd*npdtJ0Ig2eoQ(9n(>e4>?uYHH}$Ml=qaYE2P_qoth)PMe1g{6ckByLaX* zI6`D9iFTW=MkWgG%PfZ>Mn{n{K3n3$%B+hQ*MHE*CFrH-I4A1QpTheE< za+d_fH;tvLF@*?ct4YMCe{FJl2eHNX!d9O9G2Dr$yA_v80^IZAKH?E&Q4Xr5%>MC= zsMK6+z>RDs?gL$Id2EU-^&n!;Iim81&g{=9JHwQUq4i?}c7@ForTNOw@8rizG-07> zLaHQ?n`Pd5MLBXMk1~6qHe=AJ2~6IVlUgpZVaXr+|Tk z_OKAZd|xw@hQMp>L#cynh#}$Ri3B)6JqDRQ*zhM(SA7TOee(qnxI#`=)aJm>Mep-k zVG>_Xx0@jIS2#h{9rZccaKeHEe_WPUzAL5GNcz!W9DjA$Guec^@fI;>^nqFAmKXmQ zIi41yq-+CcBL#sVijfryo+YMNhj}Bm9xK*Pi=<}Q;98<;o6 zlJg;_GvGsBul$N21GeVPJMZSlCv#UN`f=PEgQ{1H}mO+T3~f2l@E?3=V6B{ZZLE5gG0i%@wpHw4Fw!>H$F$2E|joc2r2D}lTm>K zkXTl?ElMKb1e-)EP7if*6~A$H=uzfH5CaWoN$0$6XCH&Pd$-HEzK6IXNfjS3dC_5f z>XJ8}P*?z42^MX%O36bQ#w7Rv@e6EhTQAx>X0sCr3oKIntYz(cyQ1dna)iWx%{92= z_TElUG^S*N5#?DMt~3-UcyHO8ey}`h%J%{A)R{Ufy_<(Y$0sRReCp&T;HLU3`16M# zu6iuM+D2(?tF#4iV^@lDUUXe?D9|N*7}-?OuDkUpfSU#MDJTdO6b{(gHwhcoFbG_w z?wZ#y^nvrWPPfg2Qd0APIFmq{E#O_jwG3c#s#A77ECoUCZ8@B^<2%BdtcpOKgb(*p zB1l(4qNS$z(S{Aa$*_2_xtRuZ93!_ zb_4#8+AGLUL`5cJiJO9H1$Wr8Dmt*;)H$|m5s|B;mi9ntU@Vc3yvxad(>92rw6RDc zv~)9HyJrT;fcR}w-jl=xHMP|@9Iv}&ctrr3p9mFD5(tzbO=ioxlejv*3n`GtN^Q2B zNlLj0M_tdQR~QlwzyA`4zg*xowl?o8p%OpVt}o0my8?0Kb@vgU;gJl7<=c@X&Aryy zhINpV^B9##y(S)(`XGEhL#JdR3llRP01qMjo3YFwe;!*Mu<2iRF$GtZGeMf35#UC4|TjubCYm- zVfbJ^)H(;;Ol=2u;ev%#@yXRgg&Z27rlCCSgnftH){e00udg1V$00$-HZ*t1@{iwqt51TS z{ZHa%y`UE`oN1Na&)S*zCvmePCRj!q*t%?$+VnAn-RALn;CgQjyX{B0=))9pK?+ov z;65Mvix`RKPSxWJh^ZsPUWBXyzlTj-10DJq_NumEf~l5BBiCYU+i+n&eM9@ZQj%DJ z9DdDF*F1NHx(RPRtAf7yMFhD0<%Jqu*6I5ydZ?1tj3Ilg&)absYA52^jQpBX@MlbZ zHsmPPZlas==Z(WF{rNzh!V%Oc<+oUc*%4QP>Pv#Niye2pxPn5IpQS_z2pUn%!0L2R zwHU!T0pAB+ZopI5DFB=j@XUZ_c8Puw`c%FBH^%iRdk1di|Ty! zwRpuFvhM`Fuge59ZH#02N?&I2fG=pDTX#g?=uc}?F`rz6t%%F08*;+^;@Xq?E!^Yz z)s=TC+aY53l2h`P?_Y5coy|NQaVG<$9+WUNH6Lz%%A3v3p4g+{O^bq>^*zy zCo4WQgv+263t%OAPQ%OTcSq2i=dl0v51RUQzW+-lkdA%%uec_Vn6a!~nGULro4IVu zK3==`GB*OYz&5SH!QJ_6C5p3gKezi+e%ftiE$tFeTw_!pm(@Yb-i2D8?M+<~4m;z{ zXUv!!knv0jvk}xVDDTv+2ATg~8FqkUB=PL3?|l{pvYl*rCQPgA(*5S`xDy*U&PGd% zMEGzyV0W7Fz47C+*Jm$q-k)zzqry^l-im^10SVnm)6b5YR$568ZM&#MRz#uzUZ~rJ z_~09LZGP>%K%D4LAT}A;QX1aAe*<99fQJY6$ldH6g?rhKz|mJ+t;_gb3AdhFKR*1Y z5*}XD8n_pg-Dc4knl|{pK?O{tQjs}HXbTR)IS=z;w-3GJ%1HfP!_qAN*yc=0R63Y#v4o5 zqbxFf(Y@{I!Snks{($Jo8HgB=NPA}$glb6K>}e-E#q3iCUo$d~uOvVaTk_iX1blqB zlI6*m?zj0fs|k-X7kh*l6$STpZQgdn0mdD%4l$FVeeTsZ!p|p|7%b>KiGwX&4Ho^& zKycg%9$_V4KsMEnv$>8*hyS%8{-qnPT?hD6=LDD1cn^(Uu_4ED=gqA9@Q{?&C;k}z z`DA%xZ?h3pE1(iEl;zO0CRn#EzFSG1(L1^Y<^)k4`wfM{-Kp5l49Gexk6C~zT88W> zdpEB&1qf9TD2e(g$QIE^rI}_ajFLE!uwk%I^N}IeSSSwN_bPHJMyT+ShL2p>QJuce zyA0!o1Uz$FdXVl*7L=IHQ7gpw5oshXn>Zgoz0ghz=4y`#8(Qdz3*B)^ODCK6x~H8;<}O zT;0~Bv5*=p;Iq_OhroS;_7(w+SBq$pJg7)qx%uPeaJNG#27-}6g@ip*T*V*m~ zi?p}`jO2jCbuuUbZwi*h`e)C~@KL~gOm9IKvs*i`5D{+}La^j2U`%T&RMUW$$MuV| z;n<*RK?;#;Ka%qR!Cr4V=d*08A7BaIYu?=zDQMH!jh$UNUp$^nUb1fy=d{z>c`Muw z_Sv#tj{ZmBdXW6^qkbRlUnv^L2VvW$nv-Rj_{X(?Ryi-l$0rwpi|MB4$N8gP8uVWqa9 ze+TLMJ^Y_+{Rd(9o&Xf&1~7_ev+hbKWvf&8>yqtcV^)X8clVr1z%H8@#(wB5YvZC& zA&Ldqa0`ewpxea+5-5CBzD^;}<#v<@zaXHyGetF~>JE7{2|c@(#rTIu$?yMEN? zsPG0r)EQ0__WiVG&2Yo11Ex*QN2!`~?4f^Sk1~<$hPvaF%bT46D?6Hxd>$V&@t_`K zt7zJ3Gn*WB#=}I#)`6B0eOz*eaa8g6Be#V7?Wh~~L$uF^fqFL`CQO4<*0z`CCGEZv zJ5$#lMmee$e@X9ki9j{%A$e4}-T!MUIxgx0gh@1&X=$)BU(j5dvkOUf}TlN$Ca zm)ngO-bq~y)#*8D_~wm@c&I_eGu3v;rni~WYn4;@#P3f(@^tr`_i*$g1wSMygTIg3 z4fB@rJm(LeiuaQ<4N!rnC&w(MnuPa$wVd%z@cz)*Q&lIM^;2i1QFT?NDWm*9pZYiA z`ahmpHZFk17Lgx(eb-&NHtfU*ck5WjX@n}y?N7`wBAu)x)ks=qfj9{|Cdm^X6!s&v z+MYTN3unYT%Y>u5T>3z0Mf|9tWy`~sZt!{+>LYb6Yex~O7=F8VSGl5!wubFfZ1;=P z`rhmkL0P7z4k;6oCPFIBd~ps@&u=h;W$ZwprJEe_vem&b=SWVok|1BY`!MG&LPwk^Gz^Y~bZdnNX zp>WLiI8AGx3uU)*d#(PK=h=P_w4Z$vMpx=UXJXfLoM_5O{`pibEclq6*nKb~EFCbn z_{U!?7Wu{57Iweh{jfk*ggIuHIYc5X5%9*``jo*$8%|uisgWS+teQFu*_8JbJd<67 zYv#x*RZREtADpi+hnRm)tw6|&S5>FUVe-rD4mku~N%CpGwsTK_2*@w`pZgT{Fl>1} z9qXvoK+N(h^L3d@_y0uC`gM{0`&IIsLwllx7sKnGcG_3QP40Q%>X06fKa3QNcnzH2 z5GtB@^^HB0p!d^I((XqXW^2#j)@4UsIoflt3%=Xf5`UcK*W~eCLG(vEG2VPu+LJ9j5x8CQl8w^mYj88ipi4{IM<-XrpcCp2y#rKVmbn02)AeEla{P=mtdSh zQlD;Y7Z1?qwyYFdIJ#dK_bz~EUS6<%=-maCcDf*=&BtGD;kV?Rr?*hIJZ~iqg`=~U zxV$lM6h8n>sy8?UyXn{Ilqb39P8j}zVAI?ng)&jXcpFy0 z>rH%5b<${XJTlO_P2SPwfm!Jj#->hQi0ard{;*o!Ip3*V6ZGthKFFQPzIVN~FWym+ zP$;PStS2Vy2SfX~7Ny(vDDJg<<`f}5>U$l|Ewepne#8@uUAhXN=ydWi?+_0tp!O2t zpQwIN55CQx?#n0{vO}fg6|ijwSzvh$LO06_oz@>;vdMQSu*GM!4@4$JuBan zCORpX?j+&mIxJ^LG97l1jn)BH&TeAymPhLucPhZzCF;;1t*%C@JF*}$+m|cq%A2{M zc*XNJH!4@1EO;%1CB+328RB9%8EEfiFA~hh?`O&{sph#V55GczZOQFC-O$&{i>@zF z=MWzlp-e}ZkDwhc=IBx^E*bOZ4@PZp<=z|YA{AQq>(=0BUQ<2sVAMW8_P7{q%$z9u z_FEMyI!|2k0O(!wo0XcV+25yO$H8+*K?zV_6`EYLRGy|V*2gw93&Qy@zd!KH=s}MilrYUIy3m;Rw>D z+~|A;P2PftHY@vPm5QCBrnCy4o!58Tsd>g)TH)>Z>&O>a6>>XZ~QEB@5^YhlX<7POu$Q1Oz?r+2)dA3mYn1`yn{b`y^Wy~gdoC!Sj^%&{{^c_}t8oodOMGU80!qfRK5P|0tWNd!*cY&kPi`GU8+BO>or83w-uY|)C>tWfrpAheifHM%_0%Etabd}~ks;P0<(`pz@T3~l+f&8@jz>~(j*iI0{z_dAWDE(G#B_NX@{g>k^G zX%^PQMQF3IPJ4cAnmdj_ECN=U$*z=Aw9W_tkLkS$IRPG%hbCi14zga^#XDQv|r6b zJSd#s97y%cGIVCJ>+LoeX}!pYbl!AGYR(tt||NIcG50X+8Z*vBSw;F(Xm^ z^*;IoB0*2N6HlDabiiR=n#*{<^?O9rC0mEecT6-Je{j@Nxe(sdqSB@Kh#E6|L+QKW z3zIpO0Go@UDsWyeq0BChcWRa8P5~O>(jhSCn<{zR#`x3oI8S*7^JD7E&)?<$i&KN2ocX}uPR_m-_>SK%C0DzRcCpbZzVY(0 zK$rC7ilU=)UaRJ~c=IU1fJnYjkabfZo!zW3tu{Z3*t-n@L)$oqSdY4GVc`L`DE5@Ys|p@ehyC^Kf8MF z#uY6q$9S4kZr?tisWUDwVlgsyKbm+ho5MvO%G>P`tOP0@71o20uUqIZ3~7zx-Z8<+ z7X5eem}lqAuls>x14j z)6Z&)bhhYg!mmT_Cz(h3D5ED2TFR=9!g zFZ=#Jd+O|WPn|U@wQ3lCBEEg>?Krm6uV>=P+o)>`w6TK)&StVkWc*Su(l~W0+ITae z)2`l8*p|=t%lk4dzjd9Dz30H|6UkV3n*|BCfMGV1Vx%mH9Uh6AW({OKpLIC~eS7?0 zzaQn*5aKVEO(RmL##n0&BPpBJzXr88kVz9Xsf+SKQOuXOdYaT=OD%6U$`xhcCc zZO1B)R9SwWAGU{mPHAj_0dRPDRG+hon(ETWaH@>+ISQLmCKeLVAW@y}WN`|ehg&Ob z%WQdeIcLIN&{?g905}5N(-)nv6{r1JMlUACkv?-#HFVFmSi))Iomr^L`TTqVBquoC zJGIVb!UE%IPE5SHBlGwZiAJl{IY!drdS81&>l4ZS_p-9q@I>wGrt#H@73GzQX7;Fr z%0)80#sZL95SS&PKC-^$-eOvJ^_hyt-y-wCe;z%WUoBYP8UQ)Ui?wXHzx&)cG8pk0 z`@7FQTe&874q@p>bGSCVx;b!tJg^#KQ%$*eU9isx+(#)`43YB**~#Pt^V>0b?y#Fw@@UJ6g3jsBRlHd0vvunVCUG^cw#Gm=yCdDvfz8Rba;;>6CB@S^dV0$RKDj-15XbTmvp54$-99r?9-2`uh`R? zpZ^_v1~h@&v4KyU^hQ$gca!sJ*8aBlyUBUPe0BNph`C0L+Ta2R8}6C8Cw*EgBHrwK z7sORF2$^lxi-1M5YeJ87HTJei)OFw0^C)>*fwpXvjJL)(%}{}_*GxVwt7m29k( zQ=+Uu!-Uw#dy`H40z-mA}`F7*Q&8y{dP8WEF4hjjcO{}qJ^LUeo{n7 z2Dp32hjj9u()8EZpb!xzcV1)Xc@Edl@@$&zr7RD44fygHBi-ixf`mg!gyMK+1#3EY z^LOF_Nzf@+0jIT8xLjhK_PRZ`IG_7oxEs5pO$F7jLrZ0yTyfW&x^lDzJ}g`b9f0nB zFdx+Rc|SU3Id*&mUi$@J1Ja=IGM+2Hdw|>bC#jqj0J5<)>$}|ZyAR6l2)Lks_d$mK z7$YuE@4+`sxRjK9^?EMS;=aqh#U|E8D*@($UHS-xv5#|Qdi%G6mmtro1 z_&@xl_C}L@c;5#T3niVZb*1>>cQ)axJ8Z>Ro`4NzNdFPedYGC0uoXgW4QHl&=H4+? z-uegbjW8vY&G{xBKR%SDUS~S-D=`1metlg##;>M+tgxyX>p?Q9OyO|y@%`z#hgqPv zLOB&}#{vaK+X}n9WsnpclqLn@&1uLG$iQ^Us-t|GaAeh;;%XFDowm`{A?5~kwfmKw z-C=6RYXRB@s~t(%j6^6ozJJVK7LiU|a+O{EG@pkPm33;1m#BSA<7!G>m3-WQK|C9I zq;WBb3zLVl;+uK3$TZd3r{3$1S+usz^XP#J1oTR?R!!dMg3Q)*m4VQ!iSk<_eP4#T zS+cWXTuZrI>tB`cF-Y;=WNjNtcm=f@2?GngfF9u!CyLQ)710IpdP7K=oY|zl z(8641J~!i@7$VM9Gd5z?NvHyE*rd872{WSzJHot}r%l4uFY4Bci})Oz=SYY*`)^as zZ;^_lBYYVD)m%O32et)ESn>~;FhWnX@T&#*mf~hbAl##^qi-4>Rax2MR@?s<;1}S8v)1peBLN<-I z^=%!ELXOBL0Esr*6=TCha(%<=_&jQLo61xA2JD@(Z*Aj#RgbOlpZd?HIXC)SX7GOy zU)DC1l!v{A1ywn^!XE?IdYYV7g*6)BbK7e@Y1UEw_ez$1E<}}#UpA3%MxwNvcXYNH zLF-pkw5Nm4bX}8;rc%mbMw@5D6FQl!!@U!H1{apz>5*K|=*>3lT&6v=ywaimE#2ml zk{Cq51J6I;^TIUC(p`g#!egNk6oT*ZIM)cv>{dkU@r9)mlJMIetYA2K$9m<-H6k@$ z@1CQEe_Qie`oR@{yxeMAbdC=F1VRkrQnt`@t3wBO5j<&^mLLk z3^#tXmenns6tccGl3&&dm4;&n--}n=a3Ac5kq@+39M?>aPb@6!rx$FqIfBRYi=pJ5 zYB~?o8+V_xRa!h^V5MGS6b?*B3Qo6!Btk1O~Bj5euEl*r@R=I3Gf z_t$Xcrxc+mEhc^*iJ5=00OUR?#_?x#GA2r0YDqG$+v$ZOuBuY7Ii@jsgjMf0F5(g< zZ@sx<)S~RLgAh|xn9@iQ+tc56=rU+SoE{f)lI*uIC*h8BRBOdwt@6p$UX`je=Hry( zI0H3>rAWSuQtrY$%P$KGxg(|0TT1oN$YHuCY?y|aHiFj0X;avFnKI_PP-PjQENdHk z0-Ij=b@K*XzG{4an~!+KXzRifO!2&+hEnM(+Y;nK%KKlj2aX(Dn;l9&{weTSCgM6A z2+xwr7hdf$@)8IYkD=E)uY@l7DA}1~(kTxS#e6M5U zn(OC@TAZ7)P z_L3kkq1JJNLOz;l z?bUtbYmIUsA>ba=Lb+^p&k71PJ3SCs2#kO(@CbhBn$95hnXpthOzU`Q> zN%$5Y{~>0IYZo6<0Q=0O@=|K01IZu;RsWDG5bZ8dMEP(RI$pD?3mln<E;&^ZbNVO$yYk{t`QuVR=4D|J;ul_7wfB@HZiv{crxUWkN7 zB{na2gD7_-Bl2vP^~3ZNHkE(E=8MBV59f~Ur>xOa7B2760;>%@r~f$m=JWsc=+6Tp zUUJwluunE7n%`{QP+oP*#&1(NDmRr27Y7Ar-dx+TSX+cGAUGq4xn`-R0Pl$RE8o>| zz&syHuKk4S&9-F>LifC{zhzdvZMsM3o;>yC!l1bO*a%95U zRbG#)@q)WJw=3~Fo@`cfaCv+FmvZjOTuX8-TzOu+x`JR8Olrs;3$i}i{G5Cz30B2X zLERiZff4}m^7Hc94JX=v%3u-15)S0-4~g6EXAqQf0zgk3av&$OKB?9Ie82b4fHMZn zb_ZO?x3U1U9k-Xr@2ebvgq7dmZai)G1faOyV8{WayY(SHbrC#WL(tX0^|&78E;dco z_P$Gp(V^p}77gyZ4{S|=1pcwSZ`-MCNdgegnS|$hZCOhzszVlf>gzFcBQdMEPRAaf zVLcoN-M!aK5aZM-Ph(w~WdyOstLG&hh(*bbsT$ zYzAlvx~i&cTJVGE2=6^>n@yXMWTTmt-$lPB@QwMV>qw;n8k(}RBKd8v*N?MJBDc(NOTb7{ewS*c8JiB`7%416*2^9K`|A~nURtePQ zi-EXrLR@<)(Mj$XimCh}l+1}ZTYEij+R7lOk%Y3d$VSrW>6o%gpP>co+3yH}xWwnG zeQWJvb?F$@ioj2E@$>YQBg3}=EdDCfX?qd>Ft@WO$~yiVsKFV&6=;O7T$X{LeAb|C z^X`LY12-~lL!v+lqJWa%;YiD9mhy_VyRSxr?k7vISsa!VIdAg{3Q!U_y+nlQ zhE+PLi7?Tl8f-R~n60O&&B!WsqX2Aj-x`98#wJZE>7>eq?``F~MGq5;0TscVxr0+s ze4u}j&3Sjcc!1|^y+&L*ro4J{`erk73aQv_8Al;H8LG1%yqhK4-gSdVESb1xqtc=&>o&Wkd_%Xc@sdb{QRr-Z~E5e7Zb4yo^ZUd z0m}V9(@gdw=JNXAKm4SSrxg&9MSG~;ADq-iaT$Ium}1qaqarzeH{kHPu|!sGq3=EJ zN91&tU+(bE-K+5V4m?|JtRkfuuZLL?m(3RF=yAH!-`ZE{6U%0zwE?>Ze=bhTSNDB> zZsR~~FP#n=4$>v}V$!i0vJpNTk+$vJ!`iV`vcy&*5RYHipR9YJKOp# zQd9M2JGBTo3o}Tp?AT_AT zm-`h9a}7qI>8~#b<&RlxsJ78+_q@wNZ!^l104pAJ^S-ZffQtNiOQ?wWEDqQv1Hyp} z2Ojuv#h0b|4qaso`)l-S+Y0(_E!(G!N8 zspeJD&Bu?N-Q@3&01Q4RkWbo)n7^P%nO}M;8Ig6aKW(N1LRah~L*1T+fjLUGIF1~{p!b*W>PPBZ=BZv&o zcej|=Jz4{{c!RX2aGk8J&bq}hqG$rk#V9Sja8$2tsf0$}H@ zY?->&?AVD419eXx@dqa+7+@S0CuzQ@PZ(xO*QtlY5Ls{6K6A%HLy3xtN{JK!ArPvxkfc9sRZy?VPzFt#tK77#GM7Fpt?5-$1u?04?5{ROiMFqy4f*HpE9O ze|+}sYqUM_9A4T3%*%GU>Ru{0(6>~0G9c2V!}%4Xr7#C1eN&V&S`{?UT+c~@2w{DB zG_XOh^~g|CPRQeJ-v5k|UHtwJ!?E4Sm*rfx-l_f;tk&?~tK$^B@**x(Cx_G=FTi%4 zrF*qs7>`Wq2t3Kif!ls}I$={02oC~$1xk7b6Ah)?G30wMHd^X)qa{6(>aMKI?_OUabo;kdOB?6=q8-f}1Z&TF; zYd_SwO*`E;Gxr7srww+es){*`522#N-9QPWTyMI99o!J{FS!sQS>ylD=#LkV#%?T1 z+yu}ChmlT)`F{$U4qh`Bl0C1+d9U9LSXu)o+?!#_wQ<^wmbu+c^QIgVr0*8E`zz7L~DY{D!)z~Y6Y1~#zxnr z0Jr}^_D)oLB54os4epoE@HDd6Xn#&lM}Fua_nOD!t_ilyXD!_~$@(_Xj^|8`FZ$Cd zgvR=43yPV!ro01hh}Nx72-U}M(9A}kmo5!*k)5}3o}E2pw_FQdC5Hp<$@qeZ%)y(@ z;{jd3r~JYef7g5e{N+CWk~NC|@t6NoyFWIxh{pW$anl)Hd9lqmHa5Hqc3ZJPKPT*z zd+dyF<`9h|&&ayWakc1ql1^`*V0rVuIZtJ^?*0VW)F7 z8Tu)x?m)@q&zE5{btNs43fiMja?GdodYH;=_+%o`TIt@=^G8zojQp!T=NwU2&PQ9x zzzez?)jywSIR*UYbzSYAuXwtLoU- z*b8zlqy4+t|8w|L1eldZByjE>c(!Ey4QXaBkzq~qW3 zP0P~Z#}~LJ$19?Y_lc3tKbNm^r`Uh8yW&|$NYU-vjTbesL00eg+F-OlUDtNt@p@ihKNku5Zk{cW;I%M5{9sJ~ zM2yU7kdyl(tVCI*LxX@dMWQmj5l69B(Q;AlyWY9gaP|7+Rimu1OGQm%c{=2_7I|1v zi9Ke8mC>jqRP*Zu*<#p7w)*b<^lFzy%y9|Cu2jFBJdfi8sVu;Ws5w5FzSFL%u*8Y) z{(18eq_z=W5owU8HGVOu9rmLka;KD>MM);AZ0|{7vi(^H<$@#y2qK_{&*&SlwEfk% zEzcAya2>}_I?FCB{%6(WzU|Ea0LnsF35B_(Z>YaHuCOG z{e=RzRR5=@a5MjUc}YA(`Jp+l7*{Hvr^uDFAv`DDgk3k8)=~{K_|}*{=#t+E)Lx~_ z_Dmm-3hCR_936MNIB`htwt z?M}HH{Y<&$2>%#tI(J{TaKRmnStf}3q5*d|*hnx<5~P5}SG>pXS#__TB&ZuP?x9xi z0b@m@#f*s}T*Sge24FD(tq+oyfr+SNs*b@ex3U?J)b=^D(+6rg3DZsn+Z^-%JwyFx zSKN0DAr;;6HTy3FA|DMt=;zTjYRLLxbT{ijZ3sQs(1pR#dFEDj5_mKAm$+-s!(*xw zv}Q7IH$$a}`49bqV7nr%klNu@PqTPhxa^|~ijojp;nn4+T(H+6g-D`ePyDjw%#bbR zdP27vhSP{MV0aF_O+qQEy?ZQm^s-c4&gG_@q~=(>23jdO{scU>rQGbAWu(1N>SR-_ z+I5VsrTwPI3R3;IsXcOYo)2HE=1dKL_pUWQ3TEcXA~gDJlz$zTR<5)MC@i)i>!KS- zbrG|9wDTNw$j^LL0>zMsMlhr>brT)qf({UD&0V}e#tI4IWd?94x+0y4c#5&iBF{my$4!ahVq2(Jx6>~bh)u(2WvtftYfrg5TUY@tI2!&?al zlyD&N1h5KZEDs`{uCj5kGY$gSeay_kF$FAB0ZbIMkfhYMN zT<@WYEx;;1eMfLw%3ge$b;mG-xkKe84r*?HwH+{!OeQscf}T?8JJj40?G^P@!N#$& z;pz9fZl!6|ug3lEQhFZ~n1h$NTA<;hl&?&%wL-TC#MJP?@Sxv=g&~G_&0~+_cF!gU z>(htm+)~=ptYbrQ^|Rwm@%qL_&I4Oi`GA3>F*2DX`@2ly&NSuHY-C@DnQa_HXL9IM zepOS*w3gnd*f?WJ;dYgL00tj`gw#p-5CZwDF0e)FjKms5@R;NqLd7qaxCzyXBDv`G z8R6wxSsRky?iAbyaqm|itMN7#4!mH(#Is_-1WcGK4s_;3(T`Iv{Ph&gf`e8k$7%6n zG{!MJ>zLHwELJoNCbHB?{;!oD2W=9ribdlvK|X1@-0a&6ZV)=Yj*S-!)Eh_qSEi>| zfz>vuS*s?kz2tpZ^TPfjC=(~uiIYcRSN2%_0w{`_^9EunR)8ae*Icm3e%^1np?m3x zgRe--cBJJARTxXP`4mo|hoS0qV&(LYs~l;PjLr|}LKKJ^Nn8cBYeEiQ)C0oOA;NdE z!sv*>=nm{&eJ%UkH@0k-_P>f9=1pGpYFL&U_)gqFAwkS1AZY#d4A#BoFW*OYI>d2p zuyb&YkzBK1Cu?m_1R~Z-5#ihsig4tyk3~hnKz;roA-U>r=rP*;v14@?hd&)1vxqnA z@DdKbP0yez_VYEjYy0bV(=Ricz9|qSU<n(l@@D_U>%LC zuO2qD5o)ircYkDf#B0b)f8Nk@kfB@m-s~hmxhaJ+8*?ZrD*5hOxATewjgYAD=F7~y zXz664e&cR9Rksr*Hr(i2WtKDmI$9@d&a-fHwhBi)Tjq|cVOJdOyszci( z*z+akrzGYlV=n~l^ogt&!PXnCZ4I5ZL`MgQM3c3%nQ^K!j_nBXJXXkni&|hezm5U3 zKUNvT*fkV_24!r5`4W#O`j65u^b`zzVo8uiN)FA&_H#@pA;5Z7bO~!otE(~y&UwU_ zuK0Sx#lQBJ!v1)!WGlgC^jsuyxo+x>A&H(5MGe`>IPk^W!BkufSt}-|pj%SfuNm*& z&gb9o&T?7(k%SY`aM|LRFOS#K3A}W2o8wBcM$g%To>v3{L)}>@PVfmEHL9U!5Ra`~ z5DQ7-RWf+}Mmsvbhx%`~$9>|x5+@kl9BgbXq{9)1?q&2NclN)5?rstTFfBLDO<>?J8|l6|;2V>@#SGTxvW5)^wa-t{Wl*g@!M5PP#z z3kq=Wu{yhukD}#J_Tej`5*1GdrHFZVI3*i`Gw ze~IF~Buhm%iI)-LWe}Kk0PG)?FJ$bwXi6<Y@nJU~I-#JY%ccJHh%M0m4#Ig7_R!iXaK$FHmo~Y@mhZ;6&YIF&`oJx;iR7 zp2yMR|5emi4d~gL)9L{I@<%*L7x9>D@jYSnC>$NG0kA5Exm zh==B2p*e27pAD`Sk2T(CJIQfky*r!Za5la;ZTgXxIi2qLHr5U^=<8Oy!&qe^Z!4u! z;jluFwAcQ7uJUl$HT!@HfWK|jR$8DMKP@RRo`^kf)_0QcRvmvsXe3{Js>6BvWa<-m z?6C=&UA?b7T9>Ad`gU;+0I^Ig$^$EIk(=x1BGEWk^!MZ>@C~5+==o z$yy*E#E&;v21rylAw^Li5e_ONdDe56D}0Xa1Yd4 z&Heq!p=Mz)5*WEy_Swhp*(gFXqE1ML3CIeK#=B6>w{~+c$o-8~sbru37cJ@v*l!gW zk+RnCA`fKBnP}%wKcoIEkW|gpBZM-0CZ>1T!Xk5JUE;K#Hdi(AZ(j;3dd+FPphhru z8}v_FzV_>^XyQmuktT+-MbiM27qu5H330!HE^Ql!?#5ox`1sS}*OlX@7uz?ZW{MIT zAz2%xxqeX}J=pTyZFd{Isx%7n`uk3QoXKP$suXtKFx z@~Z-@{jDLtNP!&pGq27PvPdITAE2zhg0piLp=G zxrzcDFBO+Is~4KAXYj*?xU@z@xkXfj0IIj3ibaWtc7%1gM8&cit9P?bf>j~p-L>JW zI=?%Ea!a`;zwSmOR_8IJb+uGlIB_<7W*Cf4V;4_J`BGOUSmDcn%y^(z&PWF%XvPXA zW zEh(o*PiZ^ylyS^Eokd!XHFoFGsmlX#vCjGmO`uiKmwpy+<0ti>-4+P`cQRb~5w^^* z!?d9cxTLtxJ<##k{#mt}fNmZudx@7Fy^2{{b$sM2*q5u*B6eI_c2u)X9+lzdkMr{- z!4l#naUTiUGZe5W**`nNN=;hG30TS+qh}>Fk0|88me|lG#`?|xtxOjtrDTppE*hQe z=NByT3t~4SgsQMpShZ;{(u&v1*}ULKPy5$aGXW-wp@bo$^ablL3Dl}N z3lwOpg4V2~lsUM0%u!s7SZSLXs6i%V3@9`cNyc}o+ZUunq9uKK>MyusvS^wlNXaOt zA+usd20eKh9q#V{uy~td(B40xudKlRH3nmP+v6+0d&YEJ)-`$I z*pjvzbXJc~x8sK7oFvC&L9nXI;UQ>D`M8JA8hmcZJ?=rY)~x$Wgh|Ilt5I5lb70uM0~Cw`;fvN4D`t0|_119!$%!dh;ej_ zG%wc1nLm5dJ!O_iVi6g|w<6tjax+?W=Wh1hWxu)qH~Zj7kv67@qF>3UlkbOD+#SoY~-=2T_3 z`A>!6DG@*STs-?e^v6N4ieL!)HYlRNjNiGj$}*A|RMW&|ro%g=!(Pqa&}!n1@JyT; zMD&8iH(Fh%t^Jg1?L6T$c!*Uoe1z@f`%q6+M4 zm~Fa8#EBXoo>)&ex7=s+7W!oMuS(%k*6bb472A{i$it31leUb=x zq+lZo2@wHCB36ll0_sttZCjY8_;-W;Xmf$?#b>b|UsCoF@5@=~X&%g0dAe!XtmkIM zV?{tJ*I6{gEsQ^xGA2Dx@5GdVBDHI)xcf_jzTP_zpxmTMdF|Rglg!AwmJ639$v!HQ zp9THLn=0IR^k<>S-q^b94otk!Xs$`HQb9lT%|8^ME8v8)BhKX!J%6MP7}d1RjkPKU z+HOS&)9lo`y(-|+$@P62wl`I6D?~Hnf{c=oMJxIPT{uB%d)Pe5gBUdLkD_INp4h6S znh!e@R|fnDmc8ySRl3Gjr5SINt$axPM8EURqz}7H5cR1B=9$72DJSQLpZB9yKyOXQ z`L6DT=#GyXYhS$n(Lz1?Q>t%+ug=qu-EjM9$HDS?|)x04^n-z0x$svtbF;}ovk?KqUbwkU4tHLwV}SDqI6;`6eieKpp=4$LjbW@1&m@h zAZN92hG|J}+Y|2;t6EN*N%AB2l+)zR)DkMYH#Vm>9=GP%1@e!zCpKt+RN^aR2uH4B zQYU*}&vlL?pFqkVq?==_oL_o1e6KTa?2LxOiemDe%0&#$0)(g|yp*VZl#2hTCM3g~ zuZ_7SOddYYHe+4Hb1q6v#Ucbi%Bn>06Es?TczPwp92PP`{q(IrrBnWuc73JZVN{KR zgR;6k;`pd%my_VBa9_M_)$OL-3Z%odz1~;5j{ftuU6j6}e#DFwa=xQG#k+d@U1jz` z-hh~ofQfJPo=+|9un9D#|89~hk%b5CU3Wh}FsW4Jol)fE*QQ1~GH|4QmG$B?okjV+ zDc&q2I2f6qz59VCb*YuvpR>DsE;!@GaH8hH@ng)uBprwMFT34qMyaM*0X@8a13egX zs$5v6C3wdzGDRRVkKWh&kU!8(2yYxv9=S5*;7z>bH-BA|{mac~^WN14mpc&~#KnP& zrO(9uVl8Z9fKgU+ctOiNw^l{1QuM4Sq3AGxG#s^50k$_rr-bRv>O-WkmU|(}%8b3(=3*VeN z*;JFPzxMsVz&8OKUCu|-U~KW)mS#wx=bUMx=z3 zDDA5heq!Kaizti44pv#(x_w}Q@@_Fvh!ro1l{Ld`wvjNZHxLnzjRsyRP*1VX_PrW* z0eg<*2&*vzONPffw0P?$glw95V;Fo;vu6;eHQA>bI2Mao=T^OOCe2o_!iuRj{;Ur@cOz23s4NoB7+lvfl#rObT8PuL% zIFo|z?$5vDLM0VE#Vv(ZT%(Mkgh#&e#me}SUZd5We-mW~?nM2~#!|)BXF;z)SKdH# zh;7(b{;7}^BqyqevhSS&E*_MP2&FOIc*`0?v6Lu1j`?)1-lfnMZ`3`^h3v`ak`kyg zvszhc2QSZjSKcr_sG#L^b7C}{$4iWNu3v}W$2fgg6AFQJka<`5I!c3x?MwSeUpD7-3CtM)$PVQyJOU?c}|yjys`(p#Gc@j&5B%A$z(VN_#KjB z5dn_1(-%?L-t(y4YCtY>^$%$bZm#*nEye|9L3LGiI*cJH`a03F#Gk5u|CH@!1h{4) zCqr|HPpXpqyECqc=Jg)t&(MS3nXcN?nYthIm+c53DTVGZ_-!wYCTqCZyYZIz>4=6# zdQzfiQ<)-%NtVImSJ{}cmc9mN=#P2DCJC3k%O3f5-7~#=O98 z_6D;P$PLa*2n{_QW-ql>E-7c`1z3>e6c$JN*!kmUZjfZ+aGfXIZEu&evN8i8nVpr* z1)Y_2avp?VKx)t=&V&h0eF~=!j7NSj4L)Jt2yT&d5!L>vgsg3{C3y<8C}~h`gm@GZ zgA>_Kmpe)s0u#Z48^hnOKNrF}lu3wxdnC`9w#O@W+*4pe84Vci=I`O(pWI^x$FPz? zV-Pde2S>2I=J~d1Yt3~+{Az=jB)|^>d4Yq=+EGV#_vQ9I_jESuXwPW}5i9R}XGT-> zLa%gGu~K7qUf*YUXyro+ww-)kTDVh5nUwf#h{mJkm|?@tQDpEyjl@-Vk0_o6|CF;` z^MuJk_A#so`2^!2MlwLNu}8zV-nO`Hw(VMtfimorrxQ1Z-WR{UPH*?{`*#eKus?G~#CFd%Pj+4;c4%kXRR2RuOT{N0U9RFp3umg|-4p&xiL7}gv%%RM5HLnE4 zlt*d3Pa|>4d~WnJh09GY$8$=BM-(~0R^X5enHin8NXTc72=Xv}gda7J52L{iNgY0+ z6MEnIBP!miSbD(XDtvaNx^|5g#(hLDdeJYapD^0-+u{ZUzGR7|x#Z6{+?(jxr7VR= zig?8lz{5ooFUI+;l5_Wt3C?flmt-%0CIbZ(60=?(<5HjRo+5ncQ70%R%OV>{G>Zj> zf(~sSY&u+J8rWovo>Jw&OWKnke5f^)b#9d1Q{{y}+@sJm(wL-8>SMDxB* z^rUIn?$fp_N^iR?UF?{v-?#;VS=mC7tkdIu$Q$GYQV$#j)$U=`v%k6cKkZ624*k*?e5TFR%-gMqew*fXoBosDs9mll zh`FmtX;AUwIJFuzk;^X%7}FF~mZVHK8=`VEp5D6`a@{X|h?y_<(h}aRh7Eh3wLMCj zy$i7`Xfw4t5HPG3SQVREROR_K{;Z^gF4ouh99^HB#hRitOGRH~Bo8j)YiL%-@If}k zea1i?a`pTf)RtKf;f#sna_!UbuFyYwgZ2$u$XZy~+NS{D3&h`7a4Y z#D^H=(t-f{<P^oEnh=~@`s4oE!t*~* zO_eE*FI7l;M}!Ivl45~F%s~7ykMH5t6>d_^KHR;QTg!I)2>;0JxA6GaD)%?}z1*fJ z_yLOu2yI9lerD^yPZ!X+g*lhuZn1-J5N(YK^_55YeqL zDpSl{GF+?$_GYP9_1-#6RX62vDrf-aT)gZ)1*n=(d8W6I!UXCMe zRhV7PA*hhS{ARSA`5MVNp>lvZWH$1H7?cpt zEaL{n1x5a~&Pbi}cBNT82G;Z947)*QjT>p)^(b$vY zTy*!|G^z)HhZS!YqXCc#!0NgQgTCLSfmTHaXJ}+{V=`W!rCP<3gVf$Uq4Uxx%}Uzf zSz5;#baA}b9fkSn^`h-$h?wyf%l#-faT^J z7J6Y}QE#30&|d@1T6eENe*S43ks4|#SCz-tUv8=iDV{=MYqzbxCF z;6L;TFAjPUQX^LO$->U>Qy;4gRdyMDg)q;b-)DQbtGvL~E`#)>mTE+#ww%(b9;N;& zQz$Qv|041)0oV9t8?Ho+-zX&5!4rmzXQ9r9vtw?&ee79l+s>?j`S7j?u7mhV(t!MG zE3Q{Au-Vyx#NMNU_@#R~_x{k*C$arZY2iMgR*wqx#gWFrH?U{6_lWbSmIa^Pv&M9( zI-S>iR?Bmi5w-vuUKuL_y`~@21bBZiEODLp53wk4KS<3ootb)S!s72BKB-k73v^E7 zQDe2|)Ry;E8&5H~lh5%QZ+dLg8lQuSM7THdiSjfY7rl4jTGr$nXL9{C*-Vq9k7NW6 zefnR+{u*wVG;XY%+;+_4{BJ$Brwu-xfu2;fj4k}I?!0j-E~WWzd?AStO&$V5+rSI~ zQoQdawswwR4t3(wrlzpLXE#R#)D{T?;}edN5b@x%Xk}`G;CI-}6^qi0>5vxS3^gMm zR9gpO9rrDU|A>&pDmos8UEflEhp$lGqTk^Dd@L<=yOFwhzt^7c}*0_&T{yi5rIH z@t%g29whxSr(f_9q-3|<@H1F-PQhvb_Ol+k*jN?Src1+^vR6f8y=nwQ3oG<7+_RqS_e8}GZv?S>nl zYJUQDaZSn9v0Nxp!E6t!xX`hleMnenVd2n~KUptw4CzkaT%ck4SM&LXLC;+pq8}r6 z%*H+$-C?=$!SN5ThRjbm9yl5vtLSpawt{<7cOwOqHiW<``YSdb2Ps35@yHd=?ENKJ zua;;mFOR^dPY>MADFj#)XkY92(G-I#;9aWK%XP7j2`k;!(YMAe-mB9B)XMZ|JpiIq z#D@2ekG<)^3b+1Htr^)^e7;~tRE=F-a_c#d`jh9QBUO+Zx>y+g4$FsIaYA!{(9hmUeyUI zfi}}L-~7jG!FW(20+YbyczyAj8!-wF8a1Gf7!2$d0mpIDY%?dc_m$mEGgj8K^cl&Z zPOI(y;}fYAAwm$_%t)E^>Qatn5}XXew{u_8qbtxyBgS)$4=?+P3PIvRvKQtY%_ zwwR_8fMVKucY(j9eCTwT9{=Q(uLSOxUE65a(fY&vdGqjI6=Wn7^D!tQ4l?{IkKO`@ zMr01(m`@-7K@Adh=LR5qy_xG@ZCR9SUJkP%C%5za*#rRItjL>6Bwg_q{*G?$Kq=P@ zgxA_K!aJ$Khg5nE^dk2ETHP?SgteroU|(I0>_p8UgD5nvtt@d@hG7dcSu+x}Hv@lQ zudzRTZ%YbFz;=wV*75=DLS<}8IEHEqyk4$HYs+V*C`Qx)=i5BBHepYf7Y)KOT0S?U zIQ=v$!^J%jL#~}adIC|#FE04d^G}uq`z_Wo7!W>_C6C~DB0jg&d}U48$!MRNRdvjeK)ux_-%X-Vc&0KJIG)GgRs9ya zUHa^g#}qbA58IDUa6Q_a^?@Dyo@Q0RAuvt$w~^I((NE?@Rp8|IqEq#2Kit0MIy+Ey zJ3pg#C_@=4qQZz*Q_`1$V%yG}_Lpz5f7CG}vO$DYpSbI5Z{j$fi&=b`mR~WCM`D3x z;6s@~RG@4Zr~@>^h_iL@=JD9FbT?>~(4l!G0@61uv< zSMbb4tXZFoM-%c5(9^b<=?OMW%vPw74D5_(H-}?)-h!5~y>r!@=hr$Fw(34AGIhUq zLX&-fS_)(S;2mEU2osG6SfvTV+bUYA?|ks@oreQ(qUMN^W(}W!;d&nv2|p>lIpB3X z65l8n7{Ai9ZdReBdKIYM>%Xg+s1jKlicDfIA8G_to=OvmH;n@#Y?$pI(34MJWW|Hi zPzFGh=;P{y?0N*p2zuO6#|>rDx0wPl-Y1=vKHp)6E^3H6Kg8Qhb&O+{v)Hg*jPqB2 zEBWIR)WvjuWAU*sdw3nDiwzIXo^+WAbj`Ujy|lj@012wUCY+K+0{_omglxgsq^<0} z?Cpq7Zh!eoj=6KrWPAX>Xsd#nW#ypJO~#OeDK>Ne`iu8Spsv+tcUn~ywPWR=BKEaU zLnWxUwo-N3edY3Oj4)z#G??Y0JjSWzp-kX4GZAsxta&&0`4&k9Ve1?9g;5K{2=n-a zArXf6svjj)A6d-|&t9(bbEKBJVhgbSCE2B+8LdxNIWd^ZfZpcev*r6U*S@FOL3?Q{ zFB8iY1F=wi!SVP))uqtB>{96WFhUa|Hv$+{HA`N{0d`^pNz4y519Elw>a#iG+Rmb# z)s2LKpBN9)&cgFR**-|7Jml_oh{a-_=xv|uC2sbbf00@gC-#MlO!LT#vw@5E(iqg} ze!%)>JnN{P9YD2f`K9+XI4n2V&_J(Y#k4m$q3ZB^v`@4m_GeR6q5=OqWbJz- zyJ$IqfSpCF#EBdUkD(`d{__yx&wiEFi8bHwoBnHYkXR!~|CGHQNgju|O=_>KXab-Y zbc;a!`@DSyi{%S{29)%@%_{uf+0~&p@H+u+4SKkj6i3GHGTH3#cIcfRjeH}?EHlo` zBvb!`!MJlq4^t-zII23k;jfk?oFM(!0Tl}xUg_nad$s3EbjSz%fL!^sGXy(0H={tT z>2NA0Cj?q!8;<@getUCw;p;BlsC@W&FZ+&n_7bFR+GM+$JTmp+?O32MkYmJ`Yd#Ux zaYsRtdAQo)Z9K>>vn7zDK?)mkYTTjpPhx2kpgpbKr|6-}lxFUf2DGR8_sSNWXMy(5 zs(}re`3Q*U-Br6}gt1E(FCU!7xWjuH@AK|*4xd@oT6L#yz#Q&Zl4G6!M$MRcad2#S zNOA|P&&MI#W)brv?^(G0X!GJz`Ej<%cnLSJJ~CeYWw^P}7rrW9tAD%ndj&#V96r6W z30oDU{s<)P%>B)2>`lHzt=>JaFV~wF0Ln>n2Jv7d#xKi|Hzw#9M~hCqrcOuOsnBrc z##d(tgL=cV!h>JjC}8pPk^`Ee4d5I1;%@|AUg!u|4DX+{hxC=J#wUSZh;T8~gRNZK zs#sP+*?NaQjgkRgV}kMXE8>}zluTPlh`L`K^Agk1B|<#4j#A8uQ#_SPdqlVAx{zV< z42yi?0K2d!KPAmiXLuFeO24<8S7I_o0*a<>;lQO0OJ2*G{g4aG}g-(xV=nD zQ;I#LK_OuutSDAL2eJ=)6^&ul(;u`W&6X_}km}L;6AdMrx(&G@&R+qatBhkdE5-ea zGMlZ{iqdRq=tT39?G%vnM>{KgxSRbdo;2O}eRz;v@odtS0_s2Lq?*c*Gmi$@p`jX2 z;6z`B>m&0a-q%?h5lc7t(zoec+1%6?7wLQP%c-a@?8CXLZQZY$6_g;^LEVXLxJrZ? zJ5HPlxE4FM^Vi*)f5)*F7rp{jC*s958Dp9<?RPzPEi1UWZ*%;p z)6b7DG6iXWbU%SUD@}CURHtAxWS^X6C4H`6p{1ofL^k~M_UjIAsS@KHq<5%(@ntK3 zz};yln%jknv$N#0(z~k)c9I=MJ2MECVXdI@i*iMOX~lj%R4YFjw$*6=$j~k6y+OB| z&NG$GWA{eTxp9~fgNfGhBjl24y&`Rim)VeQz^CcD!qtQ0q7YfD6K{-1^W+fFFvD{= zP)~c*ZMy(WV;DV9shJvaWlV{4H)UY~K(a@M9@LtS!yhXRZ6N%YnuW=2hUq?jH}z@5x%1xTX=+DS^vDIT2SVS6v`J#@Gl8%=Cc)%9HaH5H1F#Qq&lE8DB>^8eNf( zgAE%(k$}P#;E|qEDnQO}SS9RZFe9a1<+pAR{%m)>N}PO4@^%nP1@62yQk7atJhTmY zHpbz>l!UQ~F~)uiHvU6pd|Y%aelQ23QkQ}|fnYKP@vOE;y;;3h09l-=;Kyheyw(q# z&KRsL@Gp7I`pP`pxKOswwy%e&j^Xp<6%xWDq23RbM}g?Akx`)@ZSAA4>i?itRfwfz zMv0pCI@6py4>7;Bzpv}_s?ce=!mT@asy6!(6z{fDxbVTaC6LQv$U*G+Q!RmC>WVV@ zZne*7b-F?eO3!|8w_W$D+DKNXLDw2oyc;QSN2v22PGj0&8m-MshEg8%6?r8gqv_(! zGb;~MH@`e%>TSJ}}PKwi6IfTU@w5 zdl+D8NMb*j;KyRW!cN@%tfAnar+~}y{|WEvm_qhxFz%?Q+9I+ADAOeBdj0S6PXUzk z`0D6IV@J6G80|Gj$L;NsX^WfT9&UH8I{oC!%u!2gSW$CD#D3rRF;aHOs?OWQzVp!( zM-yjyV$!;Dm2Gd4I{Dp}fRVzr$|Yo5XKO)28=e8<(pO$Dr{%dg?rHT+ndbj{&r;V`}nuomv}=zx4(U_n+`eE zMv>PQPeyB$8#}YLRQGI(vbOOq(B!W?N>GkBV8KDS^jXBNQT(KTxJ`l))_mmO+~SYm zuj;G+<`!x3(>~Aso9a2hbALPR?Pq*SEec4c&(rQ?9&r!;2FAm;07BTGaECY_mExQ;RWr)f2#;%||6^*@#ld$LSsO{xaa zvR7n{ZuR@yLOb79V}5Jl9}QtW`+zLaYBi<;MzW(8xYymMRZB|D5Zy13#h0n^(Nw&Z zbdAd9x|qa*_i5goXoWWW6D4D2S&iSC?MIQ9nI*~d$*q@9K>dqxe)RuewEBNQw90+H z>7TULjZI}imJ3>94=Gjo<1gI|#*x~$`D&F>RfhJ5A&$hEkmZRyLR!I(Sh?$wB5n&2 z*4TJtaP)s3-);Y|Lh$7+pF16Yzp?3+#+l1r`)Veg7{Esd`u1RQ2K`NUHY_9R1q|G~ z4DT+i~q6}S3dN=WbgleMbMJRp?p{=ZYc z+Q>F{o^~hGN**g%_(aCTZ9n8~jY^11LRpkAvtFSa2-fJ*tMH&NDJKJ`{N=o^bj*KK zOEZb9Sd$Ahl8tvauZkdcD~F6wRf2)gj$`3BreGE(X0+N8{a=Tl-U{%tC~U}#Hoo%4 z_Y%Bt;j!R?yvm}>J!PKGe(>^Ta8SskEWToJjv$&NKx+ik5CpBj;eQi$)ISpL0CR6H zHC_<>oqHnN{7TN|yyYF!1iS(NuMa(~sME^zT%h&AgArjK_kIE?rFa=2rGziOurjGY z;xg8w3R0sv7ejEXAvp4SVy!XBF-4IRy_V{rBL*Tyc3>z=73NvaJ(+@(pG=z-;cd-iSQPW)fVKNI6V{%^7Gn@{jxzgf$@%)SNF z{WvpI5midGAjf4|1%~ae(PA~`)q#xa5=`*#wdYt%ehlnq9O))wcVTp8SKSkTGv$iiaNRw?0XbddM-`RJ=9j!+wk|-$8E2 zb5lS1b&o1Zzw_566#~!g*qgZX@@~D~H^`kkBeA2KvLtx32wPNAQ_Y*ZetmS>6(VL&PD{ixa;1Vk~OvJ>2q0=_dJ4_mJQjV}klO$lBAAP;G zmfv|>Gc`b)9G}{kQm))`SOIY=b`4nYzt-<`vcnK%Xvo!Cuf+fU>_da#u$Z8AQ*HmN z*}qJ+zeius^w%lsPHZ$Q`Mm1;rdw$t4-3p}qj#g1^^iQZBR?*a-DaZbT`3=PaXP?YliS|sW}i70x{M0h>At1*O>1FB&SeSGIV# zI4@6on}64%t&0?*EFiUkFPB;|ot;Mls?uI;G6sI8#|AkRnVoSi>%`u>W=?-eZp|nq z2XI#CmNR``A4~%aGm-bK`^`>2$+UzDEhmp)Wv%WlEdQ|}tCW@A#X*~L8y*cQ-dFiL z*@Ca|T~IXmaYzN(WeZFedudz*KVf`fd!JsFa_k~X3RQn4ZZ3H(U%qdvW@F77N> z=eWr-yq5OAG-+8i(zpI%w;-=BogaBXRUqURDo(hW{2BiFKPO5X$j zB2A1^r?PSlaGLj?K9IB!glx`YX`n)-0vjKEZeX6(E>gPc(LK|3(eR#6^>l*sW?k>H zgg5VV5<>5w?pd#D4WGu2zY-7Og+oA)jj_o*Vi4Yd2-V7vni<(zZuRl6I}JcL0`I`T zW2b+kYz|mF@f$%4Yxu4rsvN77P@eo6|-zL(GxkL8XbO1@U6@L zNgm!b`RQfq>3g5@`kN)*Y8vhcbv}25V0?0Ts$o74n7W2@u(1kas!A@@M>~FUIt}%K zxFfqiI*2}>oz&~zw+pSA6>#y{A>W6RY?=!*)^dZyTFlT}kC3h3rceLjRb{1Vn{Q(y z8*ebJGe~a1yVdq5rk>nl{fqf}U5Trhpc&YqF0GCqyYrZbyQV4E+-L1|1($J;waUr^*t_-Iw{Wv#zpqXtN#Wimv}f;0zi1~+N3U7xQvVfS=G*A{M~XQ>d; zBt(V;B$jLap-qgFEH(KBw@b~#;-PpHn)*qRzW&`0E}9u_j1dW#q~)4f6O^%d5wlti z0QD^@xQ9ETeb@dqk8HF_f}>J<<&An+q|(f8CA>Ax}(zu-t{x zNsyQUibilBBld_Emqb3SkLMyn*SI(_cLHDvn48d4rF;Y~7D5aOnYSCSx(DLdE(Q^p zV!oMPRd;HuA(7ulP$4CLs8o;0vm5oS0M-~bGId1nu*UURCrMJN5QWv>P zH75Af=@|Z_x#yQdN4)^Tb7HpawU+IzbZ7k?S=oOQrmjkLM1B4%h@yReC`K6&A-i&l z%$q?HuJ_^b58O62gMbi9U6IAud&K~Lem9HQ0KPf% zeBaYZxMEUMsrZ1QqzIzug11GBa} zrEH6={_)%mr|SU=fXiz8XH(3yI>KP+?*65aW+Q9kNitZ(cy;;4LlFAsCLjpgqt|g& z*mn+9XtCWg^K>ODtS6&>@hL^08aELQ^o$37)J|+i?ePg89`57_t=VU_4 z+&D9+|K?>%p%|{?**Q=b95E2Ap-T^X5pfjTnvAc}Y&f(k{yF*eyFoRFq+}kzT~D7C z$`Tp%QqdH#fP&{9u9v)11oJ$w^tI=A;AN|~H|t8hTp-~AJfvQ>Mn5yyxDwNJ!RBV@ zDU+?p`#+w$-@1msfeima|5M^&%R!vtaL8InPO)0^cww$%Cu_{l?;9> zp?}{^9qX|o;MfPYz~R}M+`%`hOMWc9WG%^kKg+i~T#&eipIVSTc|N)C;-e#ylZ73B zB&St8RlJ#MmQd95}kc!qCDFIl5T_Q&vw)bzK{cJf&#R zGT4>-@^%?3BX3VoCo6eFh3*_P*TUg7WNkfLR+JI&Vrtvwv<~K7s|AVkGjEM{zDW*T z*jk;FV_(Pb1`E3*)IOjbE#fB~AMl0mA|iX~0_sZFoWEd}*>;;G>x+CajrdIc$N3(#H__d81+s(T!HU>wOrVo#n=nqZeCV(RyeMKp#QqAg z>P4Q@9Z2AGmFu2;XMINZL)}bh!TU&#IlqYG>dKcrOL>qR$xDhHOiVkzZ`ds1D{f!c z26REMt+HM1GobP^{!I&JscFr6Cm-~XdxuVU$*>Qpblwzu_Zz50=YOK^J-?dXzO`== z0SiS%inO30*-DcxEr=+{rbI$5&a$fBV1 z&%6%J)%>fNE*l-;u1Zzqie5jsZ-EU~LRj1?51Kf%gzBDS$L`9xhB6yWz}9#onJtExrN&qo=TR=b*y|;s!`RDS>e0c{g1iqPMd9 zBF~G)r_#ho-+_#mnCm&>uyT-c2Hbn{QVE~dBFO}U>GUHAfaDY4U5ox*(>%)8r-fTJ zcn7>TTwL86lN6Op#euqz_2zroQ~PkqYq2Jek!s9lTV{ZJxSwnJbq8$&e+6mdyYc}o zC{qeWkYlWwaUpgg#e@-&^D#yAbF5O;&BYAlCvkc1-5ePNE;M^C_7DY)RZUg0hz7^{ zZ=xa~myIdcv&Naeaol|;c^g5v9iW6R`uKgu{eF+bHI}Qxgq}jd#ReJIjtl*_gr){w zc*r2PdhDIvRgQ>79}?pAxALGOt{DrCq*sdZSIofKoa$Sj7ndvLv)Ies?~D3d0Q2nA zv_u=-qsC?>O;o33iH=82ZV4!hG8+BgfmM$m554 z)ynNY7uk!AOD`^GEqWjuIs8>Y7W;O{M^Dc|k^B9@fYKQ%61{t8Cn}P$@Zp$~+oaDT zQ|jjRREd7ezO*s5)xW)13LVC>tJnU#DL-3v(I)Q^KS;!L>_L?928XeGuLBY`!S}0B z6{fN*fhcuOJs7vCW#RV;bQ_OJNG8fAZ$A@Q#+>t8Y<#wsFL#uM?U1)&62?E+4hLSA z8MYInHphtzMLxs5NAmS#(zXW{U%`KxoXg|wzTsq;-6KpqEow%e1s<2G9F)%0T`IpV z3g;ZE+%I38I$oU$I@R)8@_Jw}qWe=P2T1+i-|b^vmrPRYPhxaFXKn|LpTzAk`}M+q ztlq#5%F1Ce?=6o2O)oa1EqG>f3gHJGrv3=5b%U~==CfArf1^rT9$CT>+>qN#H^{1g z57>NP@AJpMi+&}~4m7zNn`+{aUQP=%;0g*L)d+R^D?xt?NG+NuB^f1Doc7)8dF?XSJ}f>71s zNS;)Sn@wQZj^SBB`Z^0$cHtH<{p9!GrSmLi2#2fnQ37?>z|@fd!eSy_81Ojf>3B_x zoHpA+ncBX>Y@kvXly-!mgdq)Mg8J+S*tfT$&C?a|j&Cn65J=~dbPa_HRr}9xz8ASyls73tcFywx$yh^#F_Z2hYLXvHxu9f3Qw=<{^X!bVwL<0Qb{)V_sR}> zEDva3z24g|IF~q?DuQ*)89_Pq%eR+u<5Qh#bm-(j$AE^X@2}Une*KmUiGjYip7+0H z5+zS%q>Z&wu6tno(PL{hxeCn5LbFwX5B(oe(vqXT@(X5{90W9GS z!PQvhH5Y+om97^%FJHlZ#-hm7y|J`|%QLOp0o#A5FB)BTJ2mSU%8TOk>GW5GD-G&W z^#@JgzFo&i#Q|&HvGGO1rw&%MJrYfB{Y6r#6s@%3(j>zFf)m^ zUv9A^aTc=$Glm$?Zw)KCqtaEr3C!u!fOzFWV9y;e1A$DM8*aM}#RehOx8!=iTXGlz z4{h|}W;QJWew6VW)K#NO)@+cyV3zizr73E&DNvg+*+M!afcaITjat;!dB2xN0B2!B`gDjho=45L5jy`2HOeuwPl{9-em1y20wK9`ag z!C~S(13ZVc%#L>-?&&xb=5HIk-jlwG8bL}uNSP})48$xYp{LMRz^sgV=h_=Zh|tUM zmbX2T%W;E7E4Bw+Cj{uR)td?EZju2<{hx#FH}y=ub+~(*%>K2<^~r9}lQ62g&%d2M zZ!{(Fg|3NX%x_L-t(0~{6iFe2OMKV#+gZ+1Q{_X} z*@mz+>x>IP!X>5jMtfv7nqCF{^IAs2kDT zu;OtQEMbv)NT!hnH9!I*tFI;|X^YY;`1Dw`6|rcAsYjIGeVlJ5S&kErh%zD&3HF39 zAYMiHp=7AV=OQ{)ufPPOo1by(&WiqnAG>IDn3dX^SrfyUe7Igo?hWvpRRFc1*N+9`jjl+XJ+C~H|x%Es{(`tje9$3)Y;(hiSlf{dNRf{R@R_8$0{yQcWvz1&a ziH|H}y(vfe2YulQsA9uF1g9IO-FQth(Q@G@GmvfPT9%8!z8;wK$mPO(y9VM`h{s8< z6k_+>!dj+)`#ulQ|GLz_h;JeP$@n|??u*m93Xb^Y>I55k+t77AV*iWusPh8d|gRR^z1 zpf2ohId!1Hm!vfN1N)D}f`Z;mXLu2jeW{1;4lnmBCI0%!3#oY9EwVvzBvaDirVij{ zo820gUj?N~nYvgcz9L!kQ=Ir8Cs`(*^H#_ZvL~GrV$>FMw-8#h?Kz&dWHoTuv~AE` zdksx6-Y4QSXLJzN$NU?beANW=P+dNSUzLO$8J`5(Kgpa`%Z{DTF7Z5$AZQ*_uneh< zvB@(+y;9|JsMpc-5zV)QGLxOq96DZlL>wBO3v)Ayyk9gWFhE)n$*bJu5pF8|sg?%=v z#{ILKa(WLoMReOCfjalSO|6v_e+dQK(^FShyno+Q@-zL>kBAY3-6k?kb&8xc0xdlD z9|82(L~r9qT<+ZtroMz(6YL+$d$DWjBkk)KivYuJwtuQ$zzVG0>gD(~d==1z%BLtX zr6bxJh8r2zlfUpaP#D8S8cGZFYv;I;$`=iGij`gj(F4Q%?HIQRj{*liW@%a05x^!6 zJ@=0X%zzRJVMti4T1P~x&rr2Rs<*=|I?l@PLDJB5^`g($qRv?nU-KVa8*D57 zaVw%PgYU_B7ytUk3(isc?AUdz3_@`oA^f5ahw z@yIvx%Z9}RapN;1-%%Q173FF4E0035#Sx1fYF}d#|(g-}yVxX;Q>*|z@tbX(>bjnWs;nn~&rmlJ{NEy3!G5|IE++Hki*X*dbie{_B_#}HWElRlEk zEW4n_VCFIh;(|?EcC!~W#@5RciVIwXCpxl?>@-(@^2G8fdOA4t0A)5H!p-ZQ*HU>x zsA^0BF~%f7x1^P{!f)1rkT@nQ4BtBP{Ey>bK2!M{MX;0=gmWuPruWTur=QoxiX@I( zVTY~6@-;gc7uj!eJEdtDu{zxKjI4uE-b>xjNmVG_y!5radQ!I*TOQgrFY}3OeQJc7|<6R)^C1Q#&LK?fMXFiwGx5ndyN{x;il7 z2jab;|3+D~tvoj~@q&Dk_Hi;}&BSex6CrYXfVGVBM^`6zHb=iMKO*_owWc=(k%CDO>dfxvEl8xFG!o4><41ok(! z^cAY`E&34ynqBm!+fmt=f!rcJ4cF%f_^m{m#fgB)Q&qvUC-2ow9eXd*^x8a=c}c#= zksAK-{xiXj6s^(zi7VvAJV)gXg&9WHsAh>KxF8UJl z50;QCa4xUno}9X6-2DZww>9q}kORB1w2BulR=PJtl&?a4^?N4HQ$0-gJVFP*&%Cy~ z#=)y#4)-^h%6HGQ9~t;Lly;jmw|j$Nbpv=Hf?WFUQyTeI*f8$2R>%xewY={|o$^5Z z(44o3s>*Pt$GRi)Xc|nxW>1Q&lD>8&6rny59+Y}em0Ba&GMWY&lyHMJt(fdpH$s2= z4f(Kec}7}}OCv#EsP*pP{~R`KYGwSvr=c{%QXrR6Yg#cq*>@4IU-C=VRt=bhiulT;Et!8V zd!px>PTLEvuQdS4l5=iJI|ST8PZ5uB>^D@|zv^%*C-KYB_^7krKo_}g%6vt=>%M9A zo5UJe5O|flI)D>LQ!0@QV-Hhucl?y-O}-KfVJhOZnC(R+S2b+)XEX0cl}1K{Mex4v zXTm>ZpoKV~r-Es=!8TBa9)8eEvHmnzMI}k(4~e88reHT9cj*8ZYweu#bUd;?^<6jm z33GJ4N$B_86fzq)Wr}M|UGe`p`a}!UQN#3Bua= zl*)%4*35rf>89#%xo_(3d)N6RB22DuyKkQrW5>Iez752z1$OAh%-5%UAOjVj$o4JoWyY&4kwH6(hj#`Pq6LmFEAG&g^ia5@s&KTkLd=F9LP$bRGK=5- zfR4@cDMAs#;!RgL4}7}gQr+3JB)Z%DteSn4p>y$<4GzsdX5O#85NBnzLK-QaX%d1A zLW&c7t=ofPt_^1*BE1cych#-#D(Xne*GPBR`nr}0LapM(C(p2LYXZg;Au#9V2wqUQ zPk1Siy3e1LkDh_jn2b)<>5obWo|euqM|^7-gOTx`o-3&Zvr%7p@&$zl3p{(Nsl*7=yw{QJ9h4XzJy}bsy8{QUj%m0y%ZBUVb zJ3B`AE@@s3E)bqP!(_H;rbPobF%gUwuX{!AbT3&qY9i&_-BG5iXI`EUfr|bR=U4bBq<+}S zkDdET>p(2MYvnCa9Ie8DQtEUNuqpp)(b>e#jDy{}OPI4feAzKZ!9vF*VUl<^WMv`T${4`)9XWvetcG z>DX`LFYX$IXxmB!Q}OorE&}eVZ87KKwri;$(m4TilV)gGyzjrz*<*V?qq<0<_}B&2 zO)AG(E{Al<2AG8gDbrX# z@b-Bm)?=#b1^SdNNO#EjtR5Knh4^?Z5xUms4b<=rbl6QMbq_%`@%!U$UB*?&8}(j4 zQ{6n57#ao)B8%Mm9=*>jc>8H5VLC@|O^fp5;~J&g5S5~+oHs4>v?vh^ka;F~4^Ey1 zqcxza>^-DtZbGqyW%xb5=RgJAx^Yv-;hrQ$-TRdaI<8l)_phe*BH4flOSEFDAZkFBTmY2;L6n8O`5D|lyM{l$&E_4ud;W{!8{L!X$hmtD3pDPu|QNBbb z>KVnVXLOJ;%k%i5@HS|ju7&RVqUGMNMq*1-*?FEoA;SWs;P`g-)ueQtp3U57G#gOc9vf9Ny*tGe4 z^`Euxnyi1@5SNl((AQ8J0+2wfe(58nq$${mAfmhT1`85|sQ8R5y)#n~*pYS}xD|Y_ zn6cqX-(HIUb@tkg0UQxv7g=f1Tq5e?D>-oHQ(sk;`x>#|%s(jCy`^n|tq&Dr2_ z6U7QY#J@gYP9s|kb+IDig&?WgAyEI3Vl1t6ND9jVMG(0~znC_SPSs9oW;C_zvCp^1 zIvLrq&}mV&bonA)lj*`8?CW9bLfU_9X!bUMyuwEDmbu zAN2vrw{0`CiicnW^vbe2N~0y+mjlHe&g`Oi`E=2|$((13#G}!$SsB;d@PcYH+3WrS z+3nF-X4vEQ>Af96m6}J7uY~@Y`;7#jDItJYuT&3DWh zV{5zUy_4Js>=f%DZizf`Sn_vExxdRjOzO`HGk62Rc%_LaGmE6Ue(GFuO32H*kri#@ zl=M?@eZw!{tdXBvO~!i;cSJX}_yE(=;x=l!>TJ`X>?Z!yxxj-f)_j93wmB`w)qLR zi7aHDDZ28p^mF>iM<)Uyl%9lU5jUO9pmO8HS-2uh=UcS8q_H{TF3UVugGH!Z@Jy^Ego5zLHP;dcK@#|0~hVQEQ^`{>uj8-Tzc}#zPF? zJ}ZK}HBU~NmLt{9><1z-mBtCSbGmgykIkz;@M_QnN zVsqKJOK{g#z@6*KCRyNaDq}y?zjAaAu=?No=>EyHmnBKfiNY$FPELihlD4d?xL zo-I7_uswbebJ%+eNod>*L`%7|hVRhz+b~-Rn@JwfTnLzbmNxAn(WXki3;(1S@-cwc z;#FGnO&e3NICCli@M1UhcNWkv6VG~;KEv}FctDzJ4?W1oXNqAwn4U>75~;(SIZ|b} z3iGCl-$=>gnL=qSKq-zp=7IWS^VkSp#inrivBZaCcCR``9Zo;{7~8!+Y;AdmUo8L0 zL|U7d+YP}>_;6MyT9KlL|HMewkc^lZVPe9%TjXM%e#;Jv?_Y2xMzS)$l6GqldKJSI%!y9(xbP?7?dd`mk|+e@<|w;VC;R1vxh3546ytn-1^h8#D2qf@9|fiEZ}k_;1tgU8_|u4}7(2|RQi^q)e3}<%X=UlTC3R$ys@qD1 ztjK8#AxHvAb%UbyZ7lmVYyY){9T6QZZ1^W65`mED_g^8A5I+C#vu@wfOtXo z3+tmmJ^k00aKB44p}?i|Fau5@N`ZXcUDI2vB4bqC=DCdlzgWvq>pk5cZGIj!^_$#xl+MEikI!T0Bar7HdP1{o2 z2I^7Fp*taMHpLnnnIx`I960~O6Qye1!_b$c(Ix9!vhg!YnBcyJe!~mol@vebeb3NP zHkybI_HyB$#_v`y^2}fKmPz^FVxogfF~gCnW92Mk-#?T*TYuD&D1I^QCN?oa{hCmq zCC8L@syHw!kb>6WQ1I8&q3R{RzRI8@#sI3N@JkRS@m%|V#VISE;YPifmx1~dRP%0? z*l93Fa|^LnEWk+TaG)v8D-S-`@v}Z*Y}gC^^39SPvP+S!w*B}f+MmeNAe|S^1Jxextb--PNH$6S0O^K9;#9;bu^b3i#Qf zWHuhyZBZ&U$}RH=plu0|)FHrcdRP|SH_KGV-w=|Wr8Nt^7YwYs+n8v@>6hZ5KYjV7 z*?h}szdHEyx_8KFR?$}+9P7jVw*23_LaJM@^^2%jiVMPt`%j;xt+e?WMSC(^Rdl#q zpLfQ{W~46TFFv}TYyu_Ho$iCFb+4Q)8hyDvgw*|~2Xr_d9}+j0(9r_Llr~M%xoE8E zvBL;XNy?@+4wveiU`qzuT*AgGb2Y0_mv)&hlys%jzqtQCL#OKu>6w)fZ^zKttf;T7 zOv?`+lyZbJ8M*+I=$GwsdxfaxpI@7o5Eh{^hh?nTXeVS zlLZ}?1!psINRH8|M$9f$Au&ec&Ct~!wn&N~Ln5BOe_J!KbI2^wN80s|sYmgZOM(D2 zkLeAkUxiYn2XdNTY&D09^Sp)=2RO82Mp=b{@j6lo0J_5wYUWY#&xrsQ|1bRp;qCwJ zu6z@!wVSpgARXmJX}G=Eu^?j|edzfCJTR=mRnJI*KG*vv5W;?=jdy5uvIOkaNHT8=> zcrSh@@{%Rm;@N$sgbRP=R#+B!%MwM@NwZBs5dXUdY0GoaZ1eeUhI7HX$IbbiqE4si z{33q?e-$ft>Qgx0x{z$^X3aX)+>9=5_y3(ORz6tNiXp!`xYY|_vZEJigeyOu?s(uj z(5O0VQeblz=qAL^sCWE(7eqTI5C7L)uzy!0=QdMyfxse9_dH|o^w}fbE>3A5uE%j= zvgtsFuU~^<1$wn2@~N9_X->CAYNo~9GU7g?Lb3UN8<%{a4B$ypUdQWK)d$U-#G{9~ zHc_f6TmpGJ(mTXF8?A_C*6RIl_RPOtv){mReATg?J^S|;RGR91FF#H#6l6>)z1ur8 zkF%OhRDYpOn>y}rm7^0B!lrUbozIxm5}<>5aBu0yXr2Tm&qjv_p|^{SX3yQU1ggKtg%nN-(PH?ao|3}lgM{G1tqk*{ zrnhoG_60+;P8}=xD@n7DEO2jP10HWlXjjuIF500qi}N^}h-OPzX6Acx-%s zNKs1V7yvANHZDv?js4c>ym+{Gay%6brF?+Citi2t8AVV(c?#nT8?F@HH|fllK7C1q z$qKzRom9}oIwd)CoB7baWZnQ0>=-qkGGK^M4__%U_)`~5pwoW8ScofePC9=HsN*z@ zd^u)xBWD_o|ItA7VYrD?Ljo^;+N@k>fVJm#z%LQ zdd7sH>bJj?QoZ>7w-lKkquDtp23XqmX_pWI|E zu_~)i;oh5}rb@NMN8$;%Lc5NoS7>|VmDf$D4RcwW2w8EjV9VCU{EnQV%G z1p8TZ93{8;U1NK2>gRlFoUK5($LtKP!#mC;>}*j=&t^+-5R*ryC-uH-Mg}z8`!mJ1 zgzSgInIsYNB@(?ml3zFHepYDfAvNc@!AKX(escGKZIc}ied{hfib2ik1gR+OH)phe z_Z!PfV3%R|6vo?jcDmPqOdQZnIkXknQjbv3ab*t^0ct~r+)Konfo~YX4Jq*Fszy>? z5?@GXcIyK$Bv$=Hv6qXpiIWS~^aRgy}ULM0yRNVl3dnTTJjjuJ>tXj`Jb-#}d` zl{usQScz)iSsbb}+ei;@A+<$=cP(0FWoH6M3hC5qRR2Sg2d-HPnv=94bfGp^4`njt z?~zG?z4H7hRz6+Cug0tg-kW{+()M?c5oMo?K8)Jf5tKk6}_6e22tjOxK?JjL^6s7SMFbzj^~uowal)zgFD<`{ze-GR=3*vLtCn& zTZ_4~a`^-=Gx(fC#kE1qpP0#|J~y}P+RC0hI^V4zh+yr!(YV+rAA3&oEZF;QB!s=b z?V8!oqBxyTL9qcwx&ptl#t5#1)lnJDF;aob3e^7mVTG%#^o=>n5ck7@^qioKOUB!u znjo8&{SVO~xJ5$%+%5puQgG~JHCM?{w{+UD=kjm2#Js7T+$pq?SNHC6OOe7KZ{ITA zy4BrE(BQv>FzysFe&%vbYKgD60uH+puwD$ibg*E<1i3aOPLJ~uE6XR zT#2N-tf(GzJ~@~|R^oJ=B+)>R^>}7O3gD8PYJk5J&$BH$Rq{Gplov2*Rt|<9{DN|? z0ysDav$zjVD+nOdyLpUsn1P`Vhn*XGDm!{jKu>;srVBvKos(CLY@Q#TPH)+G&rxf3 z>Qlwn&*m>#=2RjijDkLMK3XwFdT#;bYu>OIa?gte_zYw0j=I)x)@iFDA&3^`)S^Vm ze*|E&wcm)tk$mH;PF~!RUW_V-Gb}7qW981&*9y*|hT|5Mz#mm4bjog>qU}wmO&1ox zR0Dar_d_=TjW?dJJw=8QyEaH!QpmwLKx!eCl-7N^6Gup`_trbM`vGhPEVVe5dWcI6 zTbxRqBxh+x{8)T;?I^*292gx#QSsi%^Lsn??le;IZPj~yw)E|^H*o`~bjqg@-;l#o z{+`lede|}p0Pz)A?xl!q!_e9w{7ab$mR^(a#g*#qmyK(u-hn}R6~j>tMTdWi!hC)& z##u)?6S^YnB68%;IN$UVY`V+W?>Fv-O*@+CmRL14oIIG^V}`<)^|Col&=>%DhtFQ= z_HLD;Uo@S*!Q#{QWN}u82uJ`N2b!|||70@)iU3oExV>KpofR3%P7z=o)Lwtsj&=;m zw1f{7&Ee|AYuzeCmw>zI)5vW>H|+0{{_s6P_uFi zY;66N>u^%iYm%;AKm%&urO_qs=86s3%@75!s`q8U~NT_Sf z3y%K3Ve=xM^imT8)?=&QxuR!Kmywl=`+!2JFNSc=n{|V}m>E!^WitXUT1bfO4zX^U z12Mxv53784NxasXatLgcpP_i}1i68WM}x_yJG_*I-9bii*UXO=D9WzCn!vKKzwNO3 z)N1=$rbs!-p#VbUvC`SEWti!fUrd@Q1d95Fg$eyOknQ+TS#T-lY!YcyL`Ydo>Ev)b zSe)`+JTCqpMaJLg@!_@^a~41RM$yT#2a;2T44(o1m&Zj1+cwBunm!>jK8WU^mInrFZzCt$NJGJ5vN<7&B z+=DjMRLE+qk>0@69+}!x#lf2+-_>1}rWtwwTi^H&R(?x1-qXl9KAnf=C%g0?*S=HS z`R_Q^y?Rou;M9U_#dW@;9~zw`#K|5KNkOGjP)`>E4=2_Ud+`{XTD~3o`M6yUX1;L~ z4$sLU&zoUbvNhOCyZ(5k9+8=%QNg`gaOvIwsWkWi_z}em5hpwQiyoJ46L_%~M-bMX zN!=CoN2__hA3ZV1Z8_xjG_6UR%8I*Q2ng ztdcD&->djDCSls(xcp_-(Aw;s$5#AtB3o@{lJaC}R1v$RGE12LB|lkWbFMpUq-zO& zbB5&^EzkYZuPV}hh^_uxD#|{tvzDO|wOHJgb}Nr_z|_t2CEU;9(WTIaYdSB%R}y$a znq)8OSkDUCS($Umw`R?$>Aw?kjWZVNVs}ilFg1#OX4ozNChkdR3NW+oIR0~oeF;iR&!xU zHiWfN+^JTg^p(a)nthr~-@&`lHr_{pnCEH{OV3K{t)RQ7LD=cLVRi~ZYh|C1t>i-q zt*3-qmu@fbwrK#j@V8d_8bsR)d9Tsl~9d{E11V{)S4&&|{;LGYXif(om6= z5a=-_dS@%&WjS<}5lqp>OE^de#q&T^1JPl^2}yl!?+90 zI(*VUs;-*q@KZ+wA=e*IYz8xJ&dc6+-T`ZM5OT|{NVF(k&=v*t70>0l9jPpTR2o?Z zy0gEBJlR|2BbRd?v5|#FS!>?`m-6u(WpLG`wGdzJdLR$j6g8c4@1!@7e!uPVa2nDg z6Y*ECfc$JlwK6__TV1EAC>fDT2E3Nx#Zuz-qqV`DYEBc zxNwk3)xtD!_>a%sFAEbVNY-;~_WmCU$}gCFk8NUxOpQ&z#xF?!m^_*(jypLAD~_A* z-UwY?$1HeZSFL?C>%Z3|bhMrnS?;GXD4(x^IV-J5D1V|imNsIg`#)=^HO(D=ISIv1jaS)Xb@BEeLSc zgfwvg1D07olA^i7E}y-30K z)7psPL-3=;WIxx_{q#Qo{6gmc0pQQ>u#uKB1mwtnIrF=2_SL6RZ?VX8#h4!c1K?A@ zng~}~L2*bsx3UU9bhZt>-{a8vbiG+bLwv}!X#Y}w)M~nKJds%P?sW>IZU{$MpAYJpJ?vH`cuB{Tw>le32y@ zWp#O={m-Giz?OSoc)LJuR2|M3Hq+Et;BgjXaENqyRP!FD?ceVF902SqN8amg z@AoYE6JhTQWP$Y)=hf5CJ$3#n$^zBv{Hf5tw)<#!m`+j-wJysb>lD|aGw!_+qEWqd z5H-WMSBZ6=DmohFo!P91JhZoRdsKY~et9)A2R_C&E)s9)`xS$qG9a|&(u z5iKqupPD9ohid8NqB<`z8TlYcfl@k>$D}!K3`*T#s{wVz(4{Q^|3VDesTRw*FQ6ca zVA}x3u5EU|IKt7$D<1%2i`M?)nbauY1-*oCiQ$>13L+1e&iI&~{MZQIcDf9|`cvmD z=U>k^-~NT8%%}e(N=c&*{`RmG2?P1GBpqeeM0L6CbV&mpOLQ zLp_}Ptv?>qV~#u1Vd_}nH>-A3RWsv$lF$#tlDh{o0GBVoPig_Y_fGD)~eY`%49}`)5xQ`|Cugqw|{+m+~KBq87H5q4+dO zpHGR@?qay^H+;ZtjXk$Up3;QIKAt1X9d+sKv6wlffjGG@l@Xejn2W{ z2eZ6pr0$rbdfR&0=}~Ojt+Ie+LUbrkvVVBeR)%G8SM|0g<5n3z(dh|V>zpK&*D1KD zx<@_@_l_an+X7vD14^sanqs7M?^2Vv#0=prz=rW95(+kZ3#(H5 z{;Icd^b2#81hUM$^i(u>V_@}f%u(rlOK^AqY;zy>e0im>FD9EJHZ#FR&OCXCSwO>i zO>@nsO5D$*n}C<8P=?;$?tn)W47bDazd`%pqvUF!jiP@ENY~<^!=Q5zoLa1sLoZG(m_1ghObE)VQ^hKv zUsqsY_l_19rGx`YnbT5~5~*>3v?w}L!L*)TdLyT6a*F2a-kIdBI0iEzS^Y&I~PhB_xjwWnP&q1A^sKGWHq->pxu ztKZUx59)V5TTJ|LBuzXhj;9mv0F1zG!o$DAGIrnlZ@jYW*)CAEAiXI~wY>APK3idP z@AnR0^n}A|_wM1w#S_@U-K`UMol4ky>GFu2#WDR2Vq2!==z9Bc6o?wNIhGFm+Bu23 zO&z<0CCrC-=XXn~L$ij7`>G>4ZNrtVGJtKu%I&9XTIXv|)zPqlW_#uXmRrS!!N8}j z{wg1Lc-ji6cv3cJw!SBi&G>$5b3jpuo7s^jpJ}zRwW3LJtefSXqO3e^%Z*IQ{?l`x zPgyryHKo?}b9ImV+x*zW=1?ZC@4_Lh>&j{>z0mgi) zZsPTX)=pWgr2+geaQ2wsbw>$5N3Q+DP|C&e3PuQr148tA#VR2Ek|73{$?Emd&Ax^D zjXJN|oBH*6zy1s|&wT6oHGI|g=`mF+<)c}4AI5n^`lEJ#v1 zmunYe9u7;G%v`8JMjWk<&RuX5usO76of@un*QScXFS(VtX&v7p2|b50=4@o`D(xG_ zfE>SVBQNpvBZNb?8^V9H@#vkhVkQ8@9@HJeVlf0v62-omAmTD~ra%UXd!p)SW8K*rf?fL=#Fjf-`&LF6Qk+UDN z1+dw?C7@ep|n;`@3B} zkxdN3ed*l-gW0~3YT>0XN&r}x6%`JVkwd%8NXAkCi}iuneH>tP5QuM$#q587d93c* zNV7vD@eMjB#;#DNuYA4jP|Q)kqjmAfk(-?HwnwxajPe#q0G!s$?Su9;XiB#ACb&2Z zFYkjW&H|Qv>qGUu1@^Md1bN- zc%e%&!FU#jdxVMXQFp!+XUmI)O(Lnmm#BNg$8w6HbwnhF;prT|IM`{C>uc zMJiAO`e7^<_K2Nf5x%14qhnT2U& zpAd)W9QJ40yjZ)sx*F$M&(D=n7x9)f?SaLz9XjZ{2$X32C8pY0Mi!0G5K%Do-=3@5 zQ0DOn6r1X^e`$)vP--thTmR`)>E3?2n`Zx~+fi-mn_REy?JEVt{Lr~D>mpcsD5Uc? zX1`Rf?LJ6YvWiH_XeokN*+~NBoDAjbV`SB0HsQd*wnO@6pE!dULp1e8X>N`y-~fzYWCGMiA1x8 zV8^Y4yJp7(($X2OkXvhSxX7#Q99pjKO#8ZI^I8iceUXRGeNxO%Z~?`Rn+ywDNnk2Z zlqDt(RX-OuXn675zNGN_YSCkC(X8U+X2hIQ9_du7z+m_=#_X+53Q&1wDl~e3RJd-Q z-}wyw!dmfz;}xyx+)Ne(Tg+~u{?XM{7k_HjtjaO=q;louhg-;!Q8UpIk=01klDrbG z(vyvst3$mEZ+lv*4<__ecT2?>s7{Nc(NjAx=c5@M)ORb|?u)#N@V%@Wy~?$Y@x{hD43UrlCEBUal&tT;hwtqPKtIE@c9-^%gRlP`E;6CR z{v`$7>NA|e^Y84De^e5NMiBhf)Ty8nGRJ;0`Jpq{ym~~Z%RZOiu&M*KFH^k#e5fY7 ze7=TxQ)CLSe1gdC-N#BwTtn*{c~4dzN08M>0}O2s=IlENLM$PP_Z$Y}52La@A*3Ze zx#XvCbp1Z+tGt{m`JI$F`QrJX9Nz=91vTdkS*;#eZ3eQ%oZS>Mi;gNNwZR6lBvPIq zG}|r5i-IsX8rpl1**Cp-?x;qRG>$;(yPL6u8;n+pdV}$%ecqvwkJm^&{2wZbl6JSA zSgt9I&q_t5w+Q}d9f`zR(k)-Aab!>7EC&6Y>Km-NZ`$-Mx!CyhX+7xyb2tg@>se_ z%8YqKU~|^wZEU78S(6aYeN+ zCtg?ADeOhMhp)De1`jTJL2c9Bv{!LcEZ#x~eG+9~=dH|Cu+p1GBV9VAsK!&PW7&UH z_>Wc~-*=z{1K%Xa@T!L(PhcYKaDL|N-7h{~|8gDKVSrXlW^}PE9o+lhQj7bMsvx68 zD|g@MX7iO#a`$OpDdC?rpcn!PTH@@=#w4l`SU!~u*#%CKY(ro6??a)2U?r9F0;v{(Ucm^=R z*@GBif-kog`J;yED@J7p3GG3SvyJ?m$wEfQ%}^au4}eACdgD#kHJF>}QTi@~vJ23T z=HMl_k~G#PHgaBvI0}NPb4m7ChL?`^Ti*ziQla6q8e095>$6r1F; z{y*~GGpwn#TlW`HqS92Pm#8SH^xhK?f#ss2;t1AYw!15=bU%#>wG!a{{O%7Et5INGoCSi_kAD4e3B|h+gID} z9qZiM?F%733CUm80wb5j7MUq<BmTKm-OFQ(t?%-X?mLwtl z&^p*<(8sDIrb3k}u2EQT5jJ_KM2%38lsx z>o@#&i1`(M?$gXH0eYJxpZOHHA6(D2)QmKo?V07by7jtGO*Nn1C$LX>Ip8Sh(z z7SvHDw>enCWRUIeH|?SHEcF`9T?V&~j7DBE*dp3GC&1`oHmLco!7}|INa|_SZn-TI zY;w^u64F7GHj|k=fmM_@ zVXUl7qxst8C8x5b-!6iHR>R3yoqmxwWny}}3Q)z~ezM9~l?R*q{PC#k*4D8ED#J$a ze2y^0onpQAeD8|&FK+$z*U}ga0IvRY5!@;?_3xdJxPSb=wUi^r??0p*Q=fc}8*HBP zOY7F+w5Y5)Ww>NoE%&f!*w}sCWHthhkLb{e6lW*(Zx`KLLu||koh#iVeY>f=eZ^_4 z113`Xo2fDuotPNsNN&;?J)M6e7!fa{(;ODw?TJej4Yn9jAf9+cKr{DQ&75thu7B!8?G1 zBcsc=a>@!g1ERhomT@<8T15Y_$yX0z2=v&-E1#kA+*V}dPP4#k8#Fn#K*$@U%aF9@ zcnT?dZuhQb16I#7WAAK!{{{E1gpzn$i*9{TLIgiFE(Mdc@FBpCau!N*M=*A}giUj_ zM!7zyKbr4<+y|JJHLVPrFUlX>EN-b|I9PR2N$1UU(Ku>3h&=iW&=b&No=9=|4d@vR zM7?5|vaI=^#U4tL|82z{fTCemwxmTLFwuJ*Tp@RNj(QwP+C=2*Di|7so6VnUJbEA~ zGxLLWoyQGE0m87W?Y#q^Ku!UUH-Hi*@SGgXw(7SMMnz4;*!655yO&zh;+Z!Nx)lY> zKHFSgN{M4S0?N$q{PxbI7=|wkL(Er+Kukc)8LI%&?!2>}k5|AYMdEBbGQ{K45a2e` z6mZq^owDrVHC-w`0ZN+PZSCf+i_EJK_Uz-s8;1_T)sJS^oV((0!2}FL^-%NYY%a`( za&cTK>%xq7Bie*(KOxYZ;K{BtV~DH9j*6f+@q+D~i62jk_`FBBS%iIMq7R_TX|L$H zM1EiKg<+zZecEh7$08ieg2!e@d}ssC{>KZ6V71!o8US3Dpa+=D-7i$~N{G#zaIO7K z{6>@>6_K!~U0XG!OoYm5e=0#)4`uN8+3YA!olXRp)E zJ&=|s6c`r2sgT0q9|U1zGUgZQpUB)buZq-9l1Xt}xu~gw3h;QV76d#aPhRIp{rQA^ z%L5LcOD2S_?kyWRNYnW2K9}G3ICAUz+;}6^CM=?KwDt~Xo?+6{V+N#iX^|w2iXz7N zhEzu*L*{FlPnSPHAwOE~9g+7Uc3&@-`VaMoO=x=#!iBQj{z5F66u0?=jyH@s6>uh1 z^Z)iNTHq@m5t*TOLLlU(7vRsBsg-I>Rjk*VU;;1!u`G zOg!7Iq5)r`G?BoMMaSuU!p_}A^j_M1J9zGTcr1{RF#bZ`=h^Ntw($NNY{Fz*j~%8{ z+(TJ9$R_?WlwavxO+JPwxP&kuSSx+7u!-Z|dLp|NUlEffstUuNQp%**-Rsnjm8NAR z04iy%P{E3SCB9?+k@z+oxXoPJ8dV?b?hAd#=*t6__1)L8YD*i{)5y@U}cbGpOau`_YgNxxCHTZ zNpohue1XvVIS>Gj6=?Z1dYYMU21=M4oHzz3M~y z>W3Y04YVj(PHhi47!8>k6FF`A{*2Fy$e7(DV;B2>BBee(F25yF&)7|W+4Ek&st5pv z#xzCP+{0<@w4&dNpx(`wQhhWP--1j^o82*Za@u3r3gpT~Jj(U?yn?!q1fI6qyYFoCmH{65-XscI2-a2WrlnE5258$>4~oj@aZrE zSPG;Z&_?&=3P-sBKdyMo=)J-=_M>)?Ol+TIE#A$RXtaT7j`z9>oS50chH zwzQhivkxyi-tw3IgETm=!?`c2=jG=wow}QMn|-)NSRuV#YZGLH9(WqB!x>nHmB;U5 zPkB4pXoiF4d~5h?RnJ4_^9{V5ZmPW(bnXskLYV{dyam;Hwk(MJylY9eS*p64%EgKK zpDM7*qEIWPCIBIJAOm^XoJ=%A#i2Y*dbDZ#V+0NF3UlZ~NpGA|4FhnlJ|+d5-%^N| zuU^#x4&HRw#=H1T7 zfOK{e&1cw;U<+1BTurV8xM~IS$F0ZPid?-5*pq)U%btM6oItaernDt&NwWW53d>xw+03#~pRYFS}Y_R364G zpL%TkqUqNo>9S=+q&N}HCfJ0{?xbMy0I40NoeOO1H;FP_IfK;in;5&A=f|@?)^c9; z{+jHPUs-zun+X*-;46_-rS0tcKR*UX>6&rtI53)Yv)5n~bnQ3c|EV3R>V0qZM@DF4|q_A3A2{l~ou>A%Q*~-QWwq|Vq z1IiKd<5{c(L>Bf=9b;kd2P@~0z58QiW^};cNcH~A*@n~S{Zr8Dh3tO~3Eu>}DP79v zFd@IXIt~N}cB&U@#UJXOgxkj};Af`3r099fc&Oy4Kn|^#Dq@A!uO$3Ew9WoUIdBHKN~v2co?^kn>PnOqbPST zfxE6&vGJo%PIc@1_s^l_E6%2@`?m}fFBMO-hs7xFS-%VK5mGAD6`;Jbg0Os^FBJjt zeX>$(Y3=6i_%6(Q!0qJ_$nXAuZR|DYfr84dYp-C+nLG`aCBmb@^;4=H$>$`Jb?w|_ zdF1(6^bfo2uzZ^Vja6-P=kZ1j=Hg~0qZO{z&V1`99#v0}bNP4n)t)t~rs_p+j^yAx z9xb65!V&$(>?_I+;Ooum96V-4Nz4pkOm&te z)KO!_fG@7qF$!aYn`GH0Z0(e*%uMsi1>%^~$NP&__lpRAs(VyC(Qs!i2r^+)R*Hey zk2uNoJZUlWpKkYT+$5`(zsAmd5@xYFK0mQi-mYF4{P7Q-WXQC~9|rm|4RP0f&eyg| zopyeHhvJnm%cQKit5D6=`$%6?%KQ?q412{{)TQp-(C6;GV(pTs7UJbzz z=++C8FH$5y(vZjK*7qfIJmSz1?_1kCC$*#J+am=P7^5wT&p)Y-z0z!yOneV18;fK; zX^~QVQ|=|m;z!$<9vpP!$l+nHpn>k+*@}cI-%{`WdxOMG^MuJ~w~jtLO&{QP<=Vyb zBe(ckA=KBl{AXNUxd_Tt+!M@6eqy%V6YmtSU6nM>aJSAlqw?kU^>~(3&rYAXsdpkl z{rc1I75(J3O*7|p&uOE?^vNH+lgmeWmU1*Y!%ZXMl z=%fgNQsD>LWK9y6o%K4{EnRY6POBxcejR< zOA`QFh}Dk9dhc0oKt;1wa`3U)XTT+xmi)U$6UyxPpnlv#ecY!_quLjn$U`F3as$|u7x)eYe5>Fnk0tP61N;6^x1DFgstvAlCPERNzHE-AHg3M-1pDymU zNtt%Q*#QsfGHVD$L#~A`t%x#2Aa@&@zynAg^A%dcRtqFUAx{>?S>JQVYENhfcVjq9 zbGke9ZzR&Yk9~8L=B>V6(4mLrtJiqxdd$6$r@ffxbEF|En2gz=D-A9UCS6r~tps;p zZl^~BjO-(QtYt@*fB>f%dG&6z`v4Fb;K%nBpZ9BJ|_)rb=2;c0lJvRgMb%PBI4(%yWqoxdKz>ch$#5Fkqh0+t*1%LX7&Ym2X{Vu zzMu(w#)4^Fv(mT>F~Dt=%!JcCw}V(YPPsT__FX{20Ye5#?S3@D3bQJ1A$u;Q-bL}bQKdBbkCfe5&q z(tCZr;_Bgze)60L)=X^W77(gM>$G9$IYisloJ1~a&%?ksDg*LontI$A?Jx=HzKG)D&7*-z!@y-=g zjOT6G_nmzYDAj;~l&R*xff(vR^Wly!pbDI6*neZV_h#yDL>1u8Einb;tOR4!JPRWR z1C{j{nwNGcSyK};aF3$q*h4cQqGk%bOCMs5T4~@aW?joGnk#5pDe(Du#UOe!Yypxa z)Yw58bi4E01J+${3ue3okmu$@iy`x{hMo#KLe#*j?#eYUjuPNKZe%ggirZeE+xS6P z$ObD5w)&!?Bkr$Ktn1|kKI{gm3aAGYJq=p-y?%Wpu5)A-Tlm_*L9J);vgU5O@ci*% zC%BAG<(Q(gDCb3wBZ<%Zc$jxEwFypYY_0)n~-{#_Z!x zMwBj*(1yNTkvC6<=`<>Ct6nL#e?4_`twkT3!Imc?G_%6vZA&E$@&exej%A;r@%zC* zq@YdrgBX7xkp6PTQ6G<}2d^R;>KC?6#bC>!_jm1R-u?wik`^AwnqJ?*YP#T+C~?<~ z8bV9J2?{OAZc5jZl%OTSv|9}ADh`qhYzof9@JU+=Bd72lluwrBHiOWSM$egkb&V8jLrVx!c+zq*c{^!L@oE^1}!!M-(ubpl4N;oOv}PA~X-k z2=aYG@T>=~?|B^_0L5;4WGNF;3UuD2qJz|pl-P?SdLv>APX zP$$EWmCyDuhKE7T-wo#OAMV3v)+Y{n2Wqf0bY8gW2+LDRlhgQ)_Bou(<$Glc1~tbO zr=Nt;H=Gl#UAVAzUs-kDrWEdXAF3uM^NjQP**@+*i|~O(>FurME-?pAzDu0QSk^SF zpKkk$Nn5~w(rhu8lxUXSusfcf1&+&CB;5h06{)3Vj=hl(;0`2zH>|r^SrVovAeSu$ z_hZat=IJ_)oz=C}hi#^JX=XQYzHE3}ZWvpVVz+D479^KXBmLzH<`GY--}_Zj=z5X* zL}8CSB>F0p_ZGZKP7)eKVp%l(>9pT1!ILD>v`f(VktAG8eTSI5W`0sydlBAo%LXlbu~}tt39S{{2Q{AD`}MeYUw|8?~?AYTr4eXoGPBSQYH)hAgjz zdt%I=aFzm({rOx>N?XqaaOSwZgn2@m=|Lbr;3!&(sMXaUjU3i7&w}FqO7icJe)e>+ zGW;)1n8O)W5@jr6LWnzLo7`cTbI(|yh6@v=$a1m2-$a=IrwZv7UA@1da>6yv){;w>7rnu`wqTf(gm<|luzf{Y_W6D#Kd&feq-~_ zPCwi^Q)NAT>CDe)#2uYOJeXgrK4g1jvm&j7`J`e;9Fd$WHwjqSGnz1`9HP- z=Jp43I74bTw06Igb-z^WjqkV6rnTgpZ{R47=hH$9EA^pot-HmT+}impr;QWX19R=t zH_7~piY>aW2`bQD3#V`$^SuPew*1%CsU7ECe3pAP@+0WDtssQKZtDq`mV6Q88a{MO zx_6XOG@-{3oJ~HmEe2V+Ca;v5ChA4=uyA2BfC3(d-CEA6IF8iz%D?W2$Z8Nt* zbJQ1tJ`d$Q)_9@=d-jQZ$fwMWT=6>k`z)%C3uqx*&FLdQ3Hh3i4G|&ptjNiUcn70t zDS?^qVamZwu~Qsw6%4JZo2w!B~)Fw1$}9O`7f+Pt*4 zCP=~0+{aq2v7~6}SF&F>1=PDkFWok4R=tMO;tKEa}k8edh8J9(1a;DNQ~$aj15Rz|N5}F z+e$15-+HBU__&vb>|qdQ{t3lC|29&{t^YHGw}wJaf~E(3zL^EPxI>;OQN(Z{rj7y~ z*RgJ;iL-Z(9HhXa9okwJa}f6?G1hw^lz#ayEBdmmHO1k4+tC&?c-;?;StEfuQmIyQ z1dEus|HmYUv#Xw*x%JZl#$u0e#gbk^4}&(unh2s+XB$KI6W%51&He%Vd4yP-vEt!5 z1BO3hHsq`}-e!!LhSXfl- zAlvV#gQa6f;`+44tk`L?_5%yJI;uEqy(ax!qacB|{@*{uufBiv9v>D2Y^L@zhJ%=& zEnZ-Rirf1+oK*`YiiH7=^8D+5OF$CibJ*5U)@{I1KE=I*3yaeVTe&_gXA0hAli-z& zYu>YOU4~LBt~yYi>sh2!;M;(4U@&nEYou2mc->%$JI~|~t(T!pY8+inIYJ)dAFeB# z3R;<0VQpdMHbdM#avFSaN9jrp4hg85d9Y-c8#b|;RdHrJpot#q92l zHp36i_v+wq=5_SOW*l6|;}~=me>>AwuJAyoS?-|N2XwFiSzGXF+C3G-y~W!!$NSB! zPNk_}6Ve7WM%&s7LIInKjFmuVBvT(#H^RaPq_0|cPdP2>g3Q$BNgMBTAAFPUyi$>R z66wRxN@T0e@4LV}zBNS@Vq;uHiuYeZwR;jsvlTj=`2gyjdhZm65q!HZ*eREO>paaO z-rZ^h6KxEuB9KzMQYD%dy$Ic7pGFK>$L@$UQLxs%R;$8kb2IWXFE}m*%b&XHs3j>> z;uv)6NzfRVq{}D!D4BQ}yToGs+s4-knbzSx}`Z|h>%cr2F21c zzX*=0-oR97@owP2AQG&zMo=wv-Shq1@drwlBz!KipMG!qMbFom{?N$DS>ni(AEDoS z;(;cS+N#C&P5vePGCJhXV-O&m@-g-4TrbmMG&Dl-#SXS)?{2fx&IEkIZDRApA$QyZ z=!V8DO?hwYK+t`YmCn%VvSS8$~$=Qae;Pa-1%5?5?V%1MUhkMY1i24gX+WWnOJJR zzB9^fp?+nZUi`SOtDBeY{I4dKS9tO$-+D_c9j!AWp7s^vU&{_;IC%ZYe+bzeXTu?7 zWZIt*-X1SsGABk~TT~XdX-nT>O5im`2#5GiV32_QJ0^PDHhPOx>Hzuxnz`t}St=#N zo*VoInH=(_2Mye>KO|-_!+UisgGZc-jdYZ>_hDqQ&`m9Mx06Co)YV{ zoyYdANzE=y_Glk#%hr@eQne}Wf~Ld7`ShkV;Y5Gd)yrTiuB2&2IC&E-Z z&cq(b&5Jb6i}=upSk<&C{;9ius0ToU6PU}(7Li>2$__oPIL9&T2V!B_06wGGh#KeR z4W_!+l-G}xRCYLbp~YSd5C-%o8f0uqjO> zmbtKeHZDBRJ#9&2o!@lkEw|Pl#ZyVDj7p;0<@0O^tteOQG-R?VB zo?DI0FJam4#^ebCqBeQ2uz8$L2S`Gg&=z`-SRqXBcs{8{LMEHm_H-XpE^l2Zr{`p@ z0jy@M%J*p#P_n|{s8xA`NyRs)w_nzQ1ANh84c@uF%fIxV6?;g#E=)C3k`Fy3n%_|y zhoW|u4k@U0YEKARB5;{Dy$IS-30Yd7+}ydy)bg{t#1Lo?e(-AvhOeNO^};gcbN^n+ zaWCh3f*XH*nuIm*v>DrYrG7u{E11O~`0uCP&+3`XLtj^Gy=Oy7&r@YJ)V;UJxM?7B zW-?nfe)BOwRNBqYhu^Lzxwy9Xyr|GUkFn@a;b1it57~R$l2-%-n0p4?o|VOV$g-9h zfzKm4kWxMR0x!(?s#ZipNKQ*_Sxt4zg+tYpT$Jgm|3wFIP4H`0_0T|#BY=;kzOCEkFbsD>H%9I!iTqcV-72Ts zlQWx-u5i+0d5hNwyIj4UiW7q3%A8)878EEo%PkH;9S#J@_X@~&Je+s%>cM!Fm1-^1 z?mz@>4QbioRi(5Yy(_f@_kZ}+!dra@-Tz}iJIWWsLt)K>pDI7>-U6QIjPFcvJ#9?E zHBpv58n)wqC`cH0J%g1`EgsuX0eZm+-l-JDhK8a`BHJA&tB-W5JboOS49qE1+bwh> zRPYR|ONKp|2U$;ei-kRsxW%7vhe1?Xy7Bf2^QGRMyxDZ30)_04iPpE8?8~;iFq>ik z6>JYChq+7Oa>1-&yUV+8-Jl~{o#Hh2_0K5y*}hveC>U+)9MjkZh9rfYMbYIVvX7Kr zSBKbYd_Vf-Y9$xBPs8A+`MNU`_w+SmvdSM=<_fmjQhtAV7y=X~S$DylI&Z?J;`Q-W zBHtAC8yu?+yVY<;7#y)-!vW4)w*#VU`L|X>&W>meV!S@N976h)Hy)w3AJs<)1mI_z zKdx7Qa_$Ul91ZL{{GPW|hkT-AOQp5!D#&VccKDAH>PY>}o>|EETG#I?OWegEFJB4@ zaFHQQvRj4CO-3;JcEvt9Y*`vx5XHc0!;F5OgDHQjrTeT6op3?8i&5;Ek{zKPn)ojf28xGk|Q-(!h*iad>(#|p>tQ??(We7j5Q0=t+sK@4vAT- zCziT*uvef* z85n&>9(3YVcgKLmeWnBgzT^vE>i1=uelYmTANBa(SMK-U(?$mbk*YSO)Qm@RtT$JW zK8<+wUA*2uhm$w+VVYe;8_E_goy|MiTHZCDZg`cwWINA(cS$^LrZ z?&5YvQ=nn^w4vSmdzA8Ll@e;^_b6qvZSt-CZYo;A&>!M^Km5k#ae|(P$j8?41}PRi zMsFUI6s*hCRC;`jQ$@hF%Rrc+B^`mqnl(gDj&>MZNTzelV7)%7sx;iyA4+sQV@6#$ z>lSUMMsI*XNxJKRYmJE#WE~;;9SAwJHp}(CTMp zj$%btp>1;^2-IB`_X)u9+&ieM&k|Oi-o?Ee9ekSYB`V91yu2J*Gx8}smOmPV6EPsM zt*%&Ub84WA5chg?b6R-*BAjZk9!spyLGsSIoqq0YCtHt?oZQc$+b7N8L;x7}F5G|c zFQi(UVphR&&AWGA?DKg{xH3j}FtSa#A6t$#<;s9wl+7JSr|KtudPeOP+gBfW0 zxJoHAe*HSAk3H}|3eB&guiY)bwE1_@ho{s70A=0M58(g3$m{UpVfdrC8}uptq0Zkn z9HWaiY#BGO_Gd{!pSCbx!bt6juY1}|8VCuUh94SZ`PB-)*@Sm0CI&Q*vfNB5+1-v@ z%x4KmQ9Zb^=8YaOw1n80V$rEx++9744g=M}rAnui1nwGqI{Jn4r`P#N#@1#1WHmmu z`29RCWjXo{W~WbOJUs&@z4Cp@;CHcdYAYktgXtD>=T!E~IJ?J=EiVsZqGin>-B^X_ zHd3r}w2{8#3<*B8#iV@bqCR@e;nS~QzOh7|L`~4T?U^Qz!|(48FL;G@T7&+1aMibQ zd1x&=y^IN&om%vuS|U_A46mFxmRp2pxJB#*?9G!_Il%$|?rPgBr487dljl1Dd-L8u z9usHhm#hpFfoX8dsOUr1T$a9&m#?4}0upVx;$i=-+TdPk9M_`N6=jm>lA3p_= z5950iu`FV6frk!c<9G{zU6JXW5r(z%=y?RIA(n}NJ}1Jn7OID3BHOGyH^toLoGAm1 z&{$X7=#|2%`MUi~v2_2ItWvJ?`+ilqth^25P52=laN`VTizPA2Bo6A6ZIDrAH!z)j zz3=`fU`&@>#ar!d{k`Se6EpnZd!x8@+2148>STXJCGhv3_lZxN{2ffbgUmCmnL+~V zQR(2`TZ=qgDS_&$fYr@?*AjLdNA7@6Q7uh?%3TOVrY(iG8(ZBGU>drwm`fLJ+-5p+|f~(csH)) za>=0lXHcIH?1ujY{>?P&JFioDya&V)`cZ&&C4c=xp5qFXKala=du;?=lvc6})yu|C z*Gj+xB57?~c0uM&8zym-O*%kwu?wmccslD)cxctc+#s!dopQDQbrHQ8sETDW0AybK zALGku)BdN0KpWKSn(O-U_r#_;jr^sR3BUMs8G3E+efRR~M^}3aZIfs==L{~D-0e(S zG@EnLThWi*@w;&)s|&+KI_x4ZnNBTpM2}-2sfh<67iqu0yR{l2U?E@DxxQGil9g9kn@6+{`g{dGEPN9S8%{*G8T)oE zV}%QlY)U=5`#?iA&p$QQX>(^d2eLz^D<|C<`T)qaZSKW# zS1$)a1%O`w!bD}m&)AJolbzA0?T$q#+MO)k(EL;q%6uP|63iXO&3#Z=Ij-YvWh zS?tyksJx#LbGvbwxS`fNUBG2$VCTUO;-SMl`Gi|UzD=+aT;X}s6Ap8pcWX<8flDONlPtuRwvn|FO$WJ?MXjd$t0DE(8 zn!WiwO_?eDfTd`SX(>iK6Ws+WcCE|aT_*P3#9iS4D=l}{V97l*f-0*OY71!55Ma-Z zL1l<#NCe7uHB-Ej!=Gj>Ndo5PtwZt-bUy5uyf&CzsfMwEESJLBK1+bQLPtu3cMXtz-+-nMHfN*qC3M2$L8nIM}E+(&DE~RLk>CJ z4sQ>AjmgesYo7c4A^7}P%AsU7uPQX~{%0q=yjzM^uU0=#B{zrxP{ejE3~?M1$me)1 zJ5z3WS}MDT)(Rc=k9%!7%6oA%rJ<1qNU%$X@`s{~#AQ{pNTXG|6*@GJL&IPM4jU`=%v9O@!&ijbF%#^4{#-G9R$h(IA*6QM*CR4WBl8>fjxrj0` z?b@gdmcKmBzsxNoueTzv89v9{(K{ZkF%g>c&4<7~z+fR$-UD#0K`EkKKgh$}rsPe! zIXsEev)$3XXI(vN3cy-x5ixEt5ZHu>+ISWQYo@2FRKLw;WdYM^mf8l83F^^7vLvn44e0#rWco7_v*;*<~{$Q4wfT4B${ssN;izEkCj@? z=-u|@*uRhNm^%1nXl@6zwYCB>x2v30wmsww@49VcZDV$A;!JGI%d&2&?WbWrOYPl2 zHsPf}_r-KxV|8974t5-W`7S2AwJTSnI~Q%392|2qB00YgJCS`Y@419R`g{(VzKk?1 zN%F!VE>~-H=Bs=iX%A;&oj0su1GrT%v4G7&Y{ZEFM*=fe6j{f7n8YJLnO=VA}0fr1)5&1ZRWa3teF3ro_KAA&BT7L1JUw~H= zG0`z8@?r7o_0+Y<41ekEc{-pIS>$uqkV=o4M<6&0_BI>`9y0{mU@UQGp3#oR7yKEnEI^7+&jIiJb%!OC7S)n_yx^8UqVQ6;Iy z-7K#bFVo(3e*a7&(#J92e2E5ETz)Q)e4;{D`E6vromgDA+!Ylc9OhT&R8TBs=|5}i zJ@;frBZLGDd*5(xo`GCesQh)L=0FpsT7`9lnU9XHLQob2@VZiOOR#6iyBgW3yC7VUjX_i~Ta+7knPo0jGA z;Y-TT68N3^FS*>weyX!+C49c(-1`k{7c-#BiOIVhEjpRTQ~KydAJl?|mRfm~c2F?J zwq%4=G}(d4@PK>==?Cj*V5>)7&1zNQw<*MzNgOvz^Zf~Hh$gr4TU$~M=8DVM{TdhA&pI2zILEkj7; zPJpb{CbJn8eZ!3T8@dey_>A}2k_&_ZCj?p8`+{*CJx)8nL|gR=B(2cDB!hsyy2Ryq zcA&OEtMJT+Fs43(2KRNgKn5o9qkB(d)BD_ts$bi=@VUuQhL#aJqj{rMiB2qHFMvg8UMC%|> z`ejUb!&K}<@(sDyGVG15-=5G4{#p*b#oL`SV9ZmkP$%W?=WhnkGC;fyUF_emkl%~? zB$k2foy)f!bP67vljpmJA=ni8%VRNET?Xx7-{#>~k85p%X(2mFi(pAhRt&dmZB}0=ic|eOGeA^U09bV5p+d{ILDt zcdpC5{fi=|s_CU=&p z0UVCs*{RgQf%tK6bu)a^3&T#&8cW>d_xcQ`w1S@rsN51ZVC~Cl)Oem&^$eKKin}Pd z(wf~O`s%?aVF(P30l6^q=*90Xox~=`qoLo|-sejy(cHk>&%d=~SWwY+dTJ7<=V zJL2ynNq*6%Pwcba1^i7ppxpOa-1}r4rKLI)+3HK23z5w4#FgV3xVo{gK=x(^4YeO2 zSk%;;@liCAuM~lV7`MTKoFp8o@J`9ikaHnt#a_?qE+>d++?;(H;a_M=)Xh7BICz1% zBOAojg{#bAo{}H6_#T=oY{1~QAQwJ-V7XNg<5+3Gd zf2|@x=4u)<2JhfHXA60PiOQ4+5#bKr#sm3;T}Mz-3~9_6V9}5^xeN<`;h_v(p@l@N z6x0|;DnOV^u>?zfKtjt=w2+Jplj~?UBVS?w<{lR0GcTm6H;4ak_vNbd;EYnzbQI7#>zp zK*i=-G)?Jq=EFw-`)AC2wUv8nS}929Au#6n+1@_{)g4{;q+Rp>&8EZ~H03)i;Ez3n ztWiCbMfrC)z2ozjmN^Nd(|ou3tXn&{tZTHgBA)44cY<(OH&$}^oRt`J>*LL{f$}TY zzWrf1f6pL^A56{WnLW~HOm*C*^qPC&@`WXYA!c4AIq^d~>7`J5xQ<2~t;lBt(}|?P zCU3|posMvc5vp+K{pi$tqrZ&z#;G5O}2V0w{kf92C|#y*-Op`64dLJroPeM z{!A~23$*pWef90%hu~WlKJ%X>nyym5y7 zy2n81{s>iQS(J-)^&z2q8SwAi0`Y#I}~)$77DdvA93QOUNhlgdN(N?r(}nWW`#M5r(5V637%R?gOXY z@}LHBo->7X@ec{>C*$X!q*LeJHf=93XRw?|i<#GOcVSsI@Bq19T*{ENkRlt8zT@3Y zj+lPT*uLr>PfDyVAr%;kdN}v0lzFb&8Mhc1NEMRee0?Vc3Z9Gc-(p0;aQPswk+XTa zYiE*yE3eln{QQ4b0f3%bqK*9i3-nZzs8hHk=;~0$(B#8in7FMWDt6Mtr&Ys)h~Ld7 zzlz{v?s0RyX%(bg`4DmqT6_F6qNH(|Oy?gZX!zqbW;_=YslPWLUzXfVeUpA;U%3l= zn|^#gqIwXM{lr|Ir3pLIF5eufdhBzcMz_e7ee7=3&2S0%UK7tKjS|ya71x@${L-Tw zdcZA|v2(zEZ_hM?R6&PZGNa#Y6JEEDmD>QZ2%#uB56JU~na7rmxSPe$m5bF;Y|zN< z#?xUl+cJo{vtB;!ZhpVZ{uJFmkNheE+G+Fkhp7&tnpYeRoUh2W`*5@h4j_YTToQ;5 zC^rDjdWd_1h%ZvKQzzUOKTL@z=31ngf(p(K$8SpM#89!J8Ck(_w%Xa{u?@FZA!k>; z3FVhTeh}OMiY2b(JBpn;7j)*01l`2h7WLOrZrPU>B0w%o7EzVo7;-8M0*ONhF;grQv8$B$HG0f(9_gIn@ zUx7ImPtwOipvo#R!fWTVP%rYP+0p}07nzf_q{D6{nxjDRL4a4_PrJ-SsL~&XoeTqu zmx0~L#Q4N|w&B!ZD>H4lF{b=DnIF$@gR=eigZ||emc2G3h(mZdPYU5>E0( z{_78Z@Q*(jU^7c)I2$&eOKcH#mNFKd&&#sj+jW;}Q%S_rMZ0kW292wLvpaz8Ua`wX zZbwVY#&t%^bexxv;JL$@R@}Omg7O1CV9JOBqru@EN?&QyYQp;khFCFBR!l&23ZZNg zrr11`2P7M>U&}dWhc8p#W|xciyv_clm4`fEei>|=$d2eQEjT&9p_%xax!jMuJhu|2 z0T)Cy1h{QIcC2dYzEXZwBB=O`d6)PXIUTE)aWv(?{I%p&_HuhJ1gaaNQvkG+^@v~)UP zR_Jh=4;mjh&i5r-e}>-;K-#JJ`DoHj)lP9%NT3(ceQ?I5zO|OZIm}Gv%PXt1l7w|C zxOeVm_Bo>Md&E(+(FH=YGb>um{9r3o|Kog*Axj5k1fIi9b&&~k)cE3%8~4=sm@O%) z1H|4H5kogD1j|!oQ__@Q9`{n>eg(3i&bJT8vm+<3HES!rBez<^ykzTFtZkuQM>rG? z9UGnxWQmQ3N*pT2IjD5-FLwu_bj3zkdWZ=+;9xI-!D=1zJXbvFU&{N)%s-a*NsS$* zJ1nexzooXzp;R9lllq=(RWP_AL(oFphy3|A&4$kwLRu*> z_O#f)$h%sBt3DFWNf+G8BP;SQds)Y~V-xXK91wJhp99D0NcLM*g%RXqds|$leTjjG2^&i{) z=WQ;#U<1q|0^j0=!V4 znAb9|0WlXEh+rzTy_|@Mh2EzOMd;}Rn5jpVu2a`(wE3qv-oH1j#! zb5^`34zvNOnu(17{_b_i`>~+Fz{E@V~e;H+Ii%dV`?cl_X)|dC&K2 zcS4)}-?8>R0u1Q3+y`WGjO4f5v5OacR_N`9cBSlrRC{+sn{g)2&~i0sY}iC0Q*JLQ zWQ(mp%@9kVua?-NJMmcJU8wwDUxGT1xx5gCnm%BEQB9Yw=aZ$TA|Et{l^N%B{!0Qz z-Vg&Z%?7m>JR%eUnf97MragnbtXl2;&xwTx^J0=V5N4cBXi(coCOu{qT~xytkvGOk%bqb959$F`@rb zFuSE%^=%A6nX@Ge6S*bpPMkU-attfOQ7L{t^Ex7ah7T*tqS6xH)DbsVWb>*y?8daD zN<1R~ytY;ONuJ2%{e+W%%av)Gm;vr)t8S|_cQZ7vJIVcoggx=Wo&krK6hI$OQVnx< zrgqw=*nmOyl-W!`0RJ-V%LrW-xz}Nh7H~rV7{SriX^Q>fCXvBp_2*W6Dlu#8&6Km% z{`i#P!OKpbnU5_*UjaoEOCa73nimzaVuY#-1ae*ur+%s~Xae`CM=sUDd?lOX04%ka zJvEG?ZcY8<#^HPU^|t{nypIKtJ(MN2RJ-1^?*pZ@K~#P zYJ&Y=#*2Z%ip_#nAcgViUrVJJX^RO=@{I!uX}P3EHRO~XoIAM!t*61^CwRHKtD@|3 zn4&E?B`eur za)sF8n>3eC*YJXnjXp(S9s^1eDVIn6rtF9si&h0!?;Z8&6S7Yf3*c^HW{PyL%-R$5p zlR%#>uVr+UAXwj?{ zfy3$3mHT@ohkKoOp7-OBd+(6r2ejOTwGisSp@06zscrX$73Ut%TSM|o!_*x|^+Q@L zgV{{U9EB!ZTB2*51!8|@Dg%M2n9_YR^l)h%-ai0fbf`mGW>~=C9QDtA{AcrxgSUG( z9%oNaOhx0FaiX7rP2m7xTJD`M_WP_h|NkKGy`q}h-hXcp0hMN@NQq@5Zc&OzhXl6; z-Ac0|H3&#hO6UYg2#A7+5D<}0KvYyp1f)X%A%vQMNH3v>5&}tR2?5SxyMO<4&N$~> zoSXL@<9+YAV~ni1=6dEczn{;e{ZD+n%IuSiZdI7O0eULVP`DSXmOpb+wL=>5umUU} zq~^BoV{y9=>0FczSY-?wql~wBsjQ1!xvDhr8c*8@n_$H!rc3SnRQwfo3TC6-*kY`= z`JNB?NcV89+97{M_3bfn>0Ohtj*+%vFapIaz$W4mGYSyWfGp4 z4TRJ+EcG@|HBdnij`94!wh#ya>jDhWddQnATaYn6zuH-et@rGL3IV`JaD>#=8L`6x ze&jD>du`@A{KqR)eaA`#t0wT-%Nm(jAI?`|mn^J7X#*g|(CuN1H$TLqah$=6dT6Es zn#q;OU*Z%j4Q}mDbCIM!nBAT7l!iM3M{f>Mb*Td03%jwonepLRdf4DU6JXZ8KcdPH z|5uZG)b-tOX7S{D7KoF@g1XzTel=gTWw)Bk9j^|P9|{Q$5&;3tD(q{>e?>HO?@Q-( zNKb}jbo`OwHc-|$CS(T4OXzVbS zA(8k#Ad^L({G>a+HXl==nE>b{)LEw$>9k6R<#sW|%&-bvWA_$cw|l$dXX?_Z%jqUj zYu|6uD3H(!@GN9rz8Y zoh7;ze%ea(2cVWV$yiF;Y;*7ces+%1e=>%!9&sn&V$dQzVuZH*sA(sQ0kCX4U@}ei z3`JmN$Ypg|m=kEAdC>n=id7y-2FQZk9!-Z8%~YB}B4N(oBs5cME97gLW2vylQ`86n zLWJSaM4=g$Kt2~yzKDU0=zxculr;v7ri77A;FtSbO~^Ui6(57)e@$u3V+!m@zg1-* z7&p;N$eWfovlIE#kAK37)-sSyj9%7!`9u9;ip|PZI;txK1&Gp)fjTJXYW1A8YU3nu zX(GfP^E6K}!bwHSRg!N)s_0w5lV;j3*X9X0jB6(puprOz4SKNP))Dad5kC`bxDVem zB2O@|V7&%Q#53x>K}fpZ=h{U#_z(&(|8^jWuh;rhahK7^FmRL?m>`%&^&d$pJGP|5 zCJ!y@^gb4>Dxm~0mi`JhqgTmvDz^qDqip?DDvUEq$d54&zEGF}lzP7R{<4uh_Zf6q z$ZwYcz-+58Rv(YCIUO@V^35;MRY>HhEyU+GN}*kT)vLP%Y*{7x;^b_74QkD+9X6-< zNL5YIV1KrbBx^Vc{CgYnEOp_R6!58?U{-Wrq-Z@luXa1^kK=Vd0@lUhh7PVTKvp3N zn(?vJBcI6cXS<<+G1U#MShgl7DU1_}%z{B$Sz4g7|heOwB2H z;S_^|nao2Cr<6%oNd~sIT0!{5unx0k#%Hg?;<5M>-|7yZ{XFwQ`AH>h9LVQr5<0&~ zg}_aZ#PmE2#@m-Z!E+6K^XANOYvH4`uUY*An3@|+XKt88keogYFmIlzeI)bF-`XMV z=`;4b^{7v(WS0ZhjNbca*ngqAhM>V|}8KlO=d z!%p{&xqo=nm?+#+XL}wxg9L^i7HibT_Gyx-uRcQX-_MeEr&}!rwzh2r_zg1G=+-zVNh52;NB#EWqBylrm^Y=4|I z`}AWLQfg+ZZE0d!JHnQ>Vq0WFUx3@z1e6=)k3TqjD3PIVj|&`3)J1jno+gf6gaYDh zAWqNL3MAQ#;1AMd1n3`huLtft0gzz@JlJuNJl{6hJ#D&#+~2!U))(fIM%ZglM>T0-tO!@OuvW2UdjRwgXJK=0 zB41hrdKkF7l*;8BYVZBFaBZUF*ZYO&1QgS`U!0(ZsY6=&06R*M-j<~I>o{fo0Q`pP z8C@NA|ozCjiPnFVGCmSe$JTz?CWo0`bEoO@o#2 zo9v_z9nZlx`a#hX!y+o@YuyHOAjR0wH4&Ax>1fM6z)2j8G1RpN_Vsw3P9S* z0(Mt$x}E}HM)d$M+(~3M{Kr2#)x6Ep?8gQp;U4hjOkLYR#~V*gtE46Q7bR5E4Xi0o|*&bNe}{=}=eKqM|mCfB8rL)VjCzq!<4g1Kr< zEt!s!YWSKp;Br{2HVrMHN~{ySkHgrSBnAji)(e8tApJ9^jfve7VFVi6C+yRsOU@u7 z{zkBea0bFpT{j^50yZ1cEsoNx{nX=6lPsQBmIkMBVg;+kS3hi3qspBs4lOei(UTME zD>SjdHLm*q zbzq#`dqC9yw6EGTzVL#E1$@@Q#?kesxrzVaWH&}tp{yd@_0_G>;C3r#(*nSE*lzED z7P=C|DXFA6L&a2+zjOTfWRT|<(VcjaG52cC;_dj%PzK!tkjq5v76u*DD zF=AvEQm96jdrsZC`RfI1B*0+Fq!+HYkq~8x`uP?`H5o94;}H!n#yf0I!FvVJVcAud z6qY_EzHPfByw2k`)%dOEMSS>8%!uUq$~M>7MPp28n{;~CptSg`h_IW1haiSDqRNMT zw6C0X0jqgmhwqtY_cT<@&wEj+aX!o2-$I|B8F0kUc4PH`FGXm+a7#+3k?BOk@zDbX zCqshM<;oRv4e6PhjjtRG#E?>E%AvZ?zMJqDRK}G{uf{Ps^ zjh+TUpW}fZVTBx$eIz@YSV?KAhH^hO*OS3OR=9}DT zTB^A|zE3-*CqNjwc1QxBh;ow!sH2`)iR~Xyt@WRRRz#~MS!t9nQm?dh=Vp%sA)|FZ zwDvW7fxORLwi6>4HrJQ^4p%rW=qa(L{+hbchZ^Zy#Ot%h={Ex_A4FCl{Fg z>a0p!SZ?)b!5}rzUmp3JMfliU=0vL;N2Y9NpF%qYI9s+}Tu6Fc?fK((2yYnB%lL*{kDGu1C1%zW>$S$^NtVxL{Xg`bn(HBOmDt#_VaAVp{PnW5aM| zaSQfbbN$P{OS-{R$S#6to9f`Pu=e+2$4T+UMkeB7RPQP!k<1bBh@1bqzaAZxq;k6`;&PCdUx8#D(mAw`k8>ZhgAq<06pBHxu;T^b`Yo4&LC>=HQe6s`|n)6z9!!(@$V2(Dxk>v|B+iu?L=#X)qF z9);@-we;}KgHQV9;GYq0+J;C3i?avp9GI`Afb92H^1Y@qs{{LV*ORJjRv^V9n}e6` zI#E_#eta7_?J^m$IuBXvi*#U9$GRafHuf9Yq;G-0rXZB2g9}>#R?ttFuZ`4i9IHTP z>w%06q$V|3aKt+ft9ijcEi~%Qw9iGEn549J*LwWj7#X;WfLW3-Nr_TQPRy%UM>Bo8 z81YZBwS&Lz$Xu$BK`u{(*>{%oTh5$K(%0p&A{?J~$UshA&XpfV`;Y2@I3>!u_~lt; z|J_ET>J)HmV#<02cb1!coa49kU&_uy%!uiwtF1Ww#|mUg442Ri^hRRV+ zlSeDqLt7)WuYt9;C_)OES)B&P1*QqQ3g6t4uZuYE|5qriKxCDgt_Wfr0u=@U1I>#i zJ98J$5!Wk;jB5QjsGmV?t4r>r8Ze(D;u7dtb%tI8ZNpa3-NgX1E}3TAPO^1?6e6r= zs$FcTiwOD<6n+0V2=@dbx2a=0Y8<%ni{S7Vpf|vRcld$QRScCmcV(gw{BE*8B*VQO z(xl}lJVXiTYhS1qZAv?h>RQOxQR=3u&U~EvZIifVSabi1rb*o#5QXZzdamvDqt55N zwC@+qeH5hO$r0}d7#ImYx9fn_^hR&zZNqnvpi9>0`&}o6ME(zvPxuaU5T^d$BcF@2 zZ5guvcahIeT$RrclJdCRmR3mb!|i$36Xs!D#9HQH=kyqKcs`7XaBT)+61Kvo@Md;b zPni8A3(c4uhn&0U9H@reQYkmmPs(iY{E7kmre#1b6`iKvsi=C8Zm$><+uQ#=hhPa!81g95P z>ZBKPP=h%gSK$618QXu*4~c_xgkQ`Jj|hG5h!D9_HerRQGVgiJh9Q3pEABL}ZW_~X1qD$(_Q8->*!7;K#2hVsLKLb!i z8|bhjOkumUHUoc-pyM#Dx;R&L}saSVbfU|wpI#gn_2bJJ4?4@cPhJO zXE+O-$+Rb#^pX<)jF&wF zuK6qG(4^maSk!jp3%TqhSCDij=ij}goOo*V;?|#inSKI;+Pfd8I=_UI6Si=T_2WLm zaJq0+)O@sr^-N8zN_e4h{BAM9(Ga_i|5UsnSO zC>-@|^^rAt(^z1H!%!+>jg<qjr}>6hC$f-Eay`Wd!gAsuEqA zCDnEq+#1q?QG453Ks)*g=eKF=ry0D*1GrCzb#@#Izfl}}x21F6Bys#rTKg-K|KY`@ zcd)y{rSBFa2HxHh_R@=uck}FIzu{dw0p%3-+TZ7^wNFW4Mszsz+?A7zZ3|WIy-Ihv z@F2M3k!zG$f&EK!ecHwtZ>kT1lWn@3-Fo+t#<9D+4xJjApG#ze2*sECUelP5u7E&i zgs!8ueFPqCBMr1Fxda(Ngv->8wf8@0XG*u9w8|D6TKgMQ3e#YVA2A^nY&Duo40XX@ zZ@n?h0qSEr6>zuKKrO32#!2hsn(Kt(m|Rq6CZl2^j1_oMWtQ_kRx>iA&E2l5^k%e0h{8vuviqiZmd&K zIV_&l!dT_-(LZdu@JI@-$aU!~Su=nE#t_jnvhlex6eMYg96#_@3;K4bkz zgspyrZR@Xnmwam&5sJ^}tqGQs3Hu!Z4F7SaNbHsDf*hQY77(U|AxT01TC?OSK;HuV zVR^L(wql6f`_%$NVq=A~f%49VcS+lD#BhymBB8oYf2N%ju(lhWdwn?`Yu#+DVxs|2aBGheMK!QT{be26wUZaA=hsSJMOSTx zO8VG0thAi*`b5-odhgojN1J|{@pY8eLOjkOoB)Ck4*KzdYwE=b4l9vzs4fT5@0Kxt zwP4OEWJL>|HIu#^7H1gt21t7roa<&2^Et<`%GdMo1pb9S0YkoAH0(z*Ii0^=Dlc*7gbj_J8? zLp6QfCIU(w$0_a7{qEid=-qljOmDf4`{`UCBEqsJ{5D@W?DD33WYlO~flg*su_NNG z3pq=6&x3?px@ZM z^HFnfl|}izMbrrR=zoVJh7%<-#|>;ybH%#cry%r~7pU5U&*TJGkb0l$VS45(qp_b9 zMBKPI@7*K!w60ha?}+m2jm}Gd)!l|Qz@WzBH zF7E}$rdKzO;t|p5Urk;=*cr^;LQJ)2Q?C?2PcfU@_SKX&Eq!e7T;H^>tsvWadiB%0wzcHNgz?{t zq_qo97!MVcOTww;kTW5A{JtE-qt&K?x##9ZQaOZ}*U{SNGUU?#$r{-wyUYfC3OWVL zcwK|w>3C=2-G7zjrJWMpYGz&iuqVPWEPTqDN&DgYo^U=<>+(gvsR!(#Al2o$qSUL! zVz&j0K&;MauB+B|!`vy!RrwodJfdIRx{4KbE3UgPoZxoD(`VqviX(S$BG*0d6Fi?a_za=VHM{d42 zw2I)n^hd?Q!Yz1ARA?SA`+X8{x?IgNyvAh@$~t2#dEndlx;fFYLf9ZIy9Y8d!gNx8 zfIhI^Cv@dG>X~P3jj`C@3vF)&yx|{$3p}`(C=T<><=#=@@YB}64E_GuJzU#LM$1CG zvNZ*;g%KjUPg(U@k8a>rBlRT(eL%mEcEx=RYsPrvX$_K_isgK~ zLll?_M0NAlXxR=;p0 zww*o2nMh;wnSq#Fkb`41J{Fk-)mXPoUy?+x5ZkXL_c_-plcA8bvdM4C#!zG1(UnYX zw#bTaaG#pTJ2bFnf`6}>zJQu9{MMhy3h&Ih>%SMxt3r6ou&j^DR{ML(1`1&*g6nXI zcb1EhewbP%++HX13mIOT#?wV`sU!Pc93UetFOwr`Dh!1IYf?+YH0+94|Fc8gk>;{J zO`;>OuaYtCNcH)z7HJa+#m5zTNy6jmA0C z`fN7tNUQ~5eEwE?L0r02JXIsKcdwC1|E0zmY{AAvWSp}0LkEB;FXR%G!(qhBj9fwT znE=mMe`feExY`}WI1PEkNZdk}mourlam6T3VxYjtwf<|{s{H5D#-H~WN8&=Ux?8k| z5^{7nYQbuTx5Wk@ppp{vIkd&D_gia$=1#8gq4mJxLqNjUAc*Cq(4O8IZUK>?wpr9) zQ^l!eZmNtK{W{1uJmN#GI4+zHK5^;l1EOjkT5etC5?^7O`SvC7&&d!H{j2^MA7{(c zhwPty8TD5X-jCg#0^_jcAoB>QlR@8=hGH@4Ox;ES6+6JH`G!6}AE6wh@lrabvC|tx zub_AuNaRdL0f*mL8?%LCZv~20fws+L(8%QH0#+s^>(hGZ7m6;iZ(;WgJaT#IR*$_- z#A@Th4d|GIzv+Nn*pSRW4HWcY8+;S~3FCNGUr7Np;>#LdF`E`5T=Ym+8T1uWg#M0Vm1!~i$MvQ1># zuKIe6^rc!SE~*VF*8jd{PdG(MVeAU|K&&SQ>k7DtoX2viN9wzdXp^PF`+Xb-B;R$& zImrjMtzF-libvoKS?ZK;)6m=Bd+coVkaglOyY$}G2EJzz3>)56ezgIcy8Cbd%k@Nd zOUim{i*1@$jH&`7t;(|Tc$Q9cC#_L>9b5>j8#|S$JX=1%?G2RvySIs<(Ev)vBYfNf z-E_aK87&&b74oU`U<&5uei_>ae@{T3f~B9V2#|I5Y8n^CvS|lPix%Xn2z++f>EUV3zN3mhY@9^GN`!Q?TlL9oOOY< zu`q=l{mLv}MDa1A4GLbF$Y0ZGeGe{r;w*y-I@1%|g+|<~cF_N&8RSp>X{z(gn~eVI@LAK2FAPKV-AulQ zp>2d*dM1`gf4BaqPMaE5^2i3=hh~ol<&_4>QWoThg}rePaNf7Y7X7(KoP~e-DNMFY zG?mcyI-j_xgt9l-qE`*j~P!YnxhH0^KK}z|n_%IV0`RW4)N5HKYx3 zaIoa#LfaAJombOHPxCF{VkSX|wV8pSAp80k3j1*N!x1-d$u8{d`~m$sYwf!WH=_ zD-*c($Vc#l7Q=(xC7ya{f}r-~Z7rW!a7k=oSwF$tAFLv|V1QvVW7O6Ai;Jhe4GYB8 zj_cX@5$eJmxJbctCgWn}kfKLd@cdm9xwu*PuHl{|tGZk$+?IY%il(G{W7scl)Ujt= zWaW%{`8Vk`kx!GUt2Ohe^DASv>Z7;dl6(tU+$5}+V_y>KL%W6^dObFUC zT{e7nM#*39t&1q{)Ij~zX$?(+X4rUqgNr5C(OJ7uot)XyC^>nXhA$`tYh+f=8Zhnu zh&Pu@a?@taeC5QV{orkaZb(o!0Vulhe;0iM)(?W{fG9`~gaNS8x49gy0cNx;^da zbLLv{N$IWj3&?f_6{|wlG+aKLESC=T7L^)PYcePfWra<{%fpKmm;TU^)g!IF`4?Yd zhpCF{XBd?vwX~WTlhJ#TMQitBCdS$z9QxcI8o%MoXZ7+AHJ=w4Jyzp%J$d?V^q_6K z0QA!4w=_*(vX6Fy)a2;E+0b)`SqHT$HWaj4gPjvCEO{u&jX;jUdfCs0C zMtnTy&GpAk-d))CWGE_igJGR3@&JbJ_oT}6Ut;5hs1N^Wgyn8(-#HjbEM7yA9k}}m zYPqa0zwsq9W`&;5Ncv^OL6YhJ`r6b6(&PP~xXPXTs>pe)9M}~#1M8%uqXkEtE-Q@e_rL1p?BP`TAEG5%)aT(R11zoQ?5(WV7d>o-q)3n*POC5$Y zg-#bQvThbj|8FGB^j{dAQm2!QmNrP7cW{z*8uNbCz`iN-g}LGj61D}dcB&WHk4hhS zw(Rl2w+zT1(ZzGwMTOV3hrFEpLxh4eCHMX#VWxa)ke>`c1MhZweOsoyC%h?%aL$-L zWX=4uU9Rzh*zwmDuW)BfgEa^Wd_Cz9*GT0u{vX*83)MFD!8e6suhPW=g6)qD)w%Bs z#CO+jbOBxhCT(nTQEKJM(h;9#<8t+&b0hyrh2r=ok0E0!0qCpvd&Z<+KAz6n%*`D+ ziU6`Luw)SQm(eDY>@NFT@VC2QgfZnC2_X`2tb%0PBGrzFn$_rt7$tU)4_jbn@KZ5476C_NCI4&W_ zr7O|!Hq)y|sK{qF6TuL=9dPJ_#5V)CSD*m?*fMrutbnoL-j3^x%yNwgKZOcD%{Xu4 zq=F=5uFJ1zcH0$rIse33`3-i8gPD{w2NtwE#~(w+fm3m-N<^n<)60t2--Z!rq1i z`QsnAJ?IU9@Lx8l++%yRlR-CnbS2xKVs(GrY>&>oQYTu5s&ll2o_){spsDG-4@a8L z?!9)+fLC`p9ox(;NIT^6nC`j`rdBUVo%RB==BTe99;!`JwEvjRXS^SwsJ47NLLn?{ zWvJP#G^fUUEE&FLwKG*ReaKLMT-Uc=ot^taOh09s<8#R75^KPD=mN9Bm_ulak0>r8 zU$@#uMx`5pz1tldP>HpSSMhFz{rNYR+it|B6<-Jir@I+zRO()|bM#;k&eQExwr64S z=dDnB_6qCPCU%Kn*(x45d#+SB)FfcGwLu&zr&C=%)nl1)a&TJddhX~I-BnND74 z#UqMMR9ob3%2zf6aE);3%``z33BXL-_zM^7*E_7NWW{`?U`Tk+=Qj=i$#8V#wLr0F zOQs0YG!m|#7W>YI&FXoR!+I%{%597T#Ua9ud%1>63qlRj{FE-(-y}{ z>_)1(+s#Ew0~)7gVKH@iA~9x8NjE`D`+oL3le*xr0EZ2Cum?c<@}c6J);_T~-4!5ddro5+-d- z^jClF>BL~lUk|9Xm3;v2#sb=E%W6p`SpkjBwXMOGTUJafV}jbx5y)8voU}6z$kwM8 zpzA^oCK(wq>d6TTHi>B8omO)^vl0D^`vdSvG|NtdxX5*X^mHH$>2Yd|!dV+i0nOFg!g8w|1;nFDfv!v`VWP!Mv$S2W6?h~&u$a` z#(7-SIJ1W=_GHfLa)!cZJ;x1KzM{F@fTZ)l#4qGVVlC$@);gF+`f;lA7VU06*KPXe z_4zCIAH>R|Uf|X&Ou~;-s+taPg!aYopl(0Y_vQm?KwF=Q=W9iYHLh%B^lq}=7|%?= zr6K9w*s7!Zc!Y3)J-NoVoS%sq<;{H0p}-%;z#J8X?RNu#*To@i<>qxWT zNa?F~GRp-#q)cVyi#3JA)~H<&BC=vL46+esWm65eZsa9`wz@zzYeWE;!9pvSM9W80 zXXhTq*MsLOWmxclHY;Nw9=1I<6$avtou=;W4u>(`HFGLKM|Ob8?As*}hls-)!+^ba zdu&f0j!93?{LBQ!PjrapB>5#_hsxwOIr9P}9t8h|!p${KO*>W{6sb z{_tOk874&o*qUfzw_TjcSg-nFu~>47>okaFZ_=c(i!+&B+tC#!{nnplttX4E8ePpfYe#FNh!#jea z90HY2`>WY;0c+4fIp1QY6%U7TC^0^MhXaH~TNI&;R{neFwPLJ(`bR}Bs^yk#k#$mD z+G)ib3=VqL99`niav;b&+_x7EsDSkfEv^|VdF>|ci@3zPYaTBkVj!@!`(Y(D{d;Y_ zqvs}fC+nP74E2Em$cxG7&h|RI!+iw3+n=7e&QozRpi<9%^}}UqRwPQN_XA16rq7pG z$Aj)oWlbm1t(1s*vC-h=P%+w6)Yu{PE;fU$2Bx*!f@>d;M(lO&v?-TfXH$IJRIC9N5dF} zHruFIIhejyGW=6TjMwvs{x4#2gaz`hwKnp9w${29!`U!M;c=Fo1judj$5I>*=KTTZ z6Y?GY#+@O}%e0|2xx28&}173E4f$HRW*O%GcEgh zVe~ehCru+Yk(g&m3j)jn8m$< zG=8m=y~p|3GiI{Tpm?lt7NHrw-%C6~*8U2mF?Md~)%|V1!`k@r;>CkY$boMcMqSCG z{J*I(;x1Ljh=wm1#x!0hqz+A~?NVhWE3)S|>>pl6_wFc|DfTKg%bq{8_?9HE$va!e z+zr^KBR&w!X9jPOcJr^c4=`Xxq)A$PF7v@ch?^Zp4a>f*bZh7w=pZ9wy&9HN+`4`j zvU*oxLT=9z1kj~_(^FzK4tkr%63Z~>Me72*wJOFs1kV5}-P7czSW6oa?Qc?n z5X=ov=sh#kq!0mXO$dG|o{Z-{y@Dtj5Hl&b#{LX@my`QAV#;tpL0Mv#IfK;F+N@HI z0!!6w{-6w;9s9ah>p=gf&NJZ!Rqyj{#_lLd*BNRhG54IQyEu*51FGe;=hdJ6|gnvc2oN z4{+*>ZriO=lsD@{UT?qC%iiGtV+oQSG}RDx2I;nItcyA95%3+4yR|cnPPN(5W6L+f zQWZ7DN7cT0Jw^MpyDrpzhMrfJNa3Tnzoc#qkA7}fuSoU`o1E!P>Ie;BQ_q2@16b){(~Ho~HOnO_tKHX>WOwR7}8$ z+>u|Uf72M>VU}=iR12#$k#Rto1r>>nrpLf zRs?3_}7{L0MPw#w}QGM!iy{*UR{HMANm1jiV;QezYD^45}Rd ztx9i$$tp~MTD`7ULb4Y$l}d-SSmgt_mkp0E2|6mM{v`4JBQQ?cS9&OwrWhlPbz`tE zodC0o9B`QBv-+Sl_S@R(lEtH2%=pN=?Hq66z`8(^<^p?Op*t}ojr4CP7b;vMhLuw|9e=zP9Q z5OXa5rJ)7B+wSK}zprM&(|E{+U}oHik&ADKrdr*HfIUZfAr4Q_BaWgyxKGX*ir7cQ zFAkIElq1)wwK8XCq_?=h_&H|4lh+fcHD4#{&(EZd4|jntH8sth84yWKdTM_9@0|-| zZGGi06eEtuNL=DMsEPq(jH(q`u&0w<`RE%qq$QxBk^PJ3jr%#IfQfI8Z|4Qd42q-H z|3XP8;to?E5|J?o-P<{`qI+ieyH+r)l2qttmL^2qpbewJL~1-2vQ6B1*os&RqExMC zm{9Ee!%0MgBW}?G$-~^NT^bmy^-=VRBWRW$X7t%|)VfwfEwe$27obHZ2>x0`( z9WK3s(A?+tD<)Zx;6K`UT}0HR3AWa-tCBXl7*xp>sOkq)(owrAX^t!Jl=IrIN?Nr; z?>{Q(t1;wH??emT+S$hzG>PcLsL*&)@&3%9qkCh$y|y=q5PY5!Zy63mj;i#i*xAuk(j6_bm6iHDK!y=>t1z!FnXfQ!U)1VYeb{5d(J?=kef8&*<&bRh zSJY$f%pDtBQ=dG;shN~?6b`JZ^?*kf6mtu6g?;|&%+FR8Sssd>t~h=@sMeo1d*1C@ zb>*JfCV@1*$RT)u`^5}? zS~mNVz)GumjaS9oUb;!GHfM@-A;j1K#^3+qG?31fxXteJhj<;O+&1c{Y1Hqlz_&4!74j*fMB) z&C*n!6>)4WOhiw|mIR(zZ!g~;o$yd`4!}HJbc*_5{pb;VN_2Y+2 zi&elGp}r2zN!zo4z*`Sp4nUf-5&uA%hJ-%SKLT%Qh?RDu837uZf3eZd zI&tP4L?6rS?mp?6`$9xkHy?Zs>iGuyR1+;_bNyJ^1t&%2$UUjC_U4(v(m`Hp8v!S{ zyk>4kq{vFa-*lmR_E0d2{R%^cE7s~E+=9KK)m{R-25*++uED!rX_wMW04U9S0Hs;@ zHRCmu5wDCX_du5N_}JwiQ&Epfypl#$|LPXYAl`UoN59RZlBS=y%!>6qf zT(>1W-$QxI%nHyDoEczrKlSzHMb4b@0233$fURyiNM#K-X+dHayT+vC=A1UbQoWak zUU@drH!gkhuieydGl~m1kO^HE(1&Uo4zU%E^zl1vxs9R=+>J-o<~_K!exi#`+gzFX zBL5;}@g%?Z=2xu6A4@DSE|cu--Q%!m=D9Y5n6f1^s{Fl?HX);2DGbX?^EStzk^st` zWpyZxyqJ1b^B!noSxJFQNqalDD(jUo9Wj3U2CTLC-q>6%@i7kh1~8?<08{$GL=<33 z@7}^L&V~AB0Aa^b3^EYj=C_UZ4e9I^cU82>eYwNa#lom`4dpmh~`rs~BY8fhTY8oGp|1ek7*Cghr`KLinivcqG%(!Xv zG*!_lY^F`@KJguP^{Lp3leq|i6r2&|==y%eXT#Mg;{w)^$v0^kKy{0`Yh;O^-dbb; z7Ic`1f#$m_dtZXl)}Ql)PukHb+xgIEl~u=l>UgyT5$WG}ze;t=evKA3;J=rDmiPWp z!c-KunR?71x0zM^LrEzlKCtvej55#4^7x<2DQJ5-7}%anGD*m!ow_eVX>id}uT|f; zkF1Rtc5(}H^O~ejrAhh@oUR#0%E%S%Oc}bOSrk#%aHla8g38}X=2I*XY?m-omu+3r z_T22VuAIB-etg&`{aLNIUC~)02y@Ybjm1PD+i}o~10-*}w{G8RWnYcEg?6p8JmZY_ zteG-#JS*`_&!hX$QDB|G!D4b{lfuFuDkHsY19vtd->N)CGeMPwDb($QlmncT6#2e# zv-Pv#Tfu+lV4kqojP#d+mX0Icy+8M`9f7T7tJ(&}UsF*5eB)--9;AhN>%D@K8Jqts z5_h@b>dH_Je{hxiyXl%k6?<_1*;XdQ2f5LSSFJ8b6owf70x(Q7sO-NK>BMOT~&gqY?qb;~3Jw=EMdEBpGxg^bb-!nqucEjx(`p<7Z7c@1rlwpMHb zx5b6k{Z1|T?^$@P-TkO>G;PC4=pqW_ZVL%qhrj{dmdQlQU;1Fc-EW=1_OOC z-OX=Zi92Q3!1*r{T3st8>eJoNQX-509Pq`NrX7hAD#N+!D@}Orxfp+JM9{#0&w7GF z-4pqirwiE+?)7xzQpUvc&&ENoGXsZtZwTEZt?}APTs={ca3fPT)_eT^LiHXW5RbmW zM~&P9HC_*A-EH99W-x3Z^Bx$$hqYQOq;>|q9{Us=N|GCj#o@}trZXN#@T}w!p#twq zOMye*TN1-fK0siW#dtfCqBcbks#?I>is*M7{ZZIJ;#EkHKO-Ze1{83k); zVPOhtSp5^MY>@s=^fC`;jip6qTtl*an>9o5qmUd}j=jgLy^HYXWO2~eFUT;)?x4M8CXAjUSc+l zH?Yx-aKRJ{o`^v ze)~+Et`I(75S0Zn^HEzq$b;HQ??9dm&d-4#j;jMqT$8;>mpv4Mn#>|53Mrons^&%~ zz$=->ryZhTVajAXiJ#QdqiLtns!Unh;i%U&gGzt!DrLV)iE5;pXB|5w4u%j0olfXf z3Qga=B}p;ZRsSzrzP0hvxwvH?mw=6t=~#RRBVy8&@%<%U?Yrv#`;&J5V|v;{D~_ur zr4M%H|1NSsQ)*ZK4+G3+N^-%l;THbpqWAOfi-ZI7K3KV3^WWbYx)7XS-*Q|Fi@j@o zSZ;%_zarV=GV)jPyg|bK8x#T8TBd)YN0#6xG2R=it4Kz5rC-nej=yV_5G^J{w~9Be zKu}J?55_yWX|NORUw78b-TFCeb^V>(Tw%>sAs-S?4SLik0s8adfr=>;&1*v_L}Z)j zKC=2_^uEmXvvF|v4CN=8YC2SQ9;_H4C$BTrZOz;KlrNknhOJn{%?~?@vXUv$NeiQZ(}TD8T*XE^VM}-_wW8a&+$C>eH_p6 z{Ep-I{O3sj_@3YMbAG<(=ly=aT=CYykv`cOU$z?*kS(N_OgZ?WeExTzUK+i#QKhoE z?h|=U*lxh+!L1IbwQhMF)8P=WZV1-Es5Ni7FEF10A8Nnz>)$K@t(vfb5r@b{MDqWu{VT*xcG2%X3v7q$^9({OU2KbQS>mxFw~ z1DBA;2|@{{>q1%s_v#n4J~Ai$*F)5!u`GDJ*4ko^IA+#{#T$h}{;VIAcagWeTFpyu zeY`9Z;-ScSXlUQ|_@bJhI@9{hZa28XUGf*!P{PB*+IH)K#v!1#Ox+m^h}%6M(y>i* zaEfqas2g`OjgVVZQy}s~UV-sMYoElgXEXOHHop!oC4CSG2Ji|AR*oK2455%bfhRWb z@mRC64c8SDO^v;cmPXXlYf~$QH*9GUj}u5Z@e;XYf$+O9g|^Q{wR1U><+%e9(I((1 zJ!!LVpAH}q?hM2!8I5|wU#knfl6%wia``}?^ZTp4Ht&vlCj1n=$IOP2?FuK{cML0j zVRf4dn&h;;Kc8XphX$c zRzZVcagj3n=zMv0XE(qxsLavQkK0$xa@{W|I3ay{&cDN`z>GiG?#mYqVvd{+G%Fst zxFEnZDJUh*R*`&q%DB*vo|VA!D^I(Rh2QopTXU7RzHb|Z_%nhxCCRg}gjrTy@S>oP z!k8r;?#wgqbBhi__iC zYH6V=eDlXLTbEjYR-bDe6xbf?bLUjYj}uB8`j*pq*2Wl{${KZrl;99co}FLlt-@2S zkGPPhdP>T^5m=+9XH5*F?HS-h-E%Cb+y;MH#Ou{adO0{53_&~Rs#i$WD7p%JsxKEY zUTFn#&s_Gkb1KxV9xF;`JLBJ3m;N9G>gwh!7TkEIR!&E?*zDy%gJ;=`hIO1CQ>Ll> zA&nFQN2Z7^hzFZ+Vne6olH=u#5A?E1D-QF2PbZ$nkmT4C^%31=0_M{1i1p_9r6$Z+| z)GS+MG(;~W+m2f^CRgtOxv?%ZmlgZ*roV zQeJdwMj|r!tNwKu;?LAN5`CmR_ud<2aSNkP#}z& zj|{0*{{lP>sB1laWfdo4Q7I^E#S}zy=ZrvR+HY%t6`%i8hUB^zTCzcBjD9roX4CPd zWh3QbRv~CcS&PV6#r0=rmMkno#?KsWw*6oXVKq{e4GzKQ&UPC|*i28InXsS8Vdi`A z?26#(>zX^v$BS##2?QQyZ^0*prfuy`I!if&^b3vAX4`A5;#xkrwot*HLA7kxU_yT5 zJB6?m?>jc3(mOwoBAI5Ty$FWs zh?x->R_IrzgXl_woQ`1K*lw!`YS%x>Yw{7(2;^zlgor>NYUU>>yGqIRK*90@Nygn~4b?Ppk2zs)8JW|+n~p}LniTDE zfv{LOmB!PybcJ$RWzls?y(6vF{AgEqyYu0l#^` zi%4{j+bzy-4L<(txqnstS@qUhYaIY~{Dbfp0lPLe1;uWic$nW{5ftDt5hl)P;Pu%D z((a~C7unCdU*P+ev3K?50 z$!D@$^*$kmd35MJOOQEpDN}3`71>b(-uxC(@&RxFy_KBukgUvcjf2&Gf>w$L{QCLL zwe@&v@&HW#`+#I&gY!www%NSy2UQKtUKWOC)u9(KPqfP)h=ueYl)vChtbJ$vzYJQ& zR_#|!OP@!Dq!MO`xzIK8L{4oCho5=6C>lF2RD%^|;hd4pE)2)T=!g8$%6>-VY+lGR*{bvk%==b}em5pE;eRcczPA~35@79Hyt3eY?LFP5o z&ipFhTRb$-ayA!V0I}rTw!U6a9A!tBY_k0D3+}zZ{gSOWk2h?|59$tQM?ZTShw&h1 zct9|Zv|NKWzZ}P*ICqAr^P=T!vvE)2_a3w{&m=eu(t6~LYmR+W%v@elN;!C~Jo_}1 zGni`i^^w#|YB9{?XZ-XfCa`sUkLSu>N#y7mp^P{IkE4h~7xaahO6cGW{u6b$NW(6 zs^Eq{88rw$3$u~2({Zv*keg9@|E!5cEr$3~>Zx~1C2PUfF<+}Gu8JVm5NkqgX%ew0 ze$Q~1r)97`X2kII{l`PD1rN;3m)HE(z^^%(yx`E6zdRu3r44b0Q|KJQs#9g4ZGQ$JdJEDn~?7K^ksEN@{9JsOekZ`}OtC?&N&*{UEyE$(wO9xZ-5P`ah(a zw*aZ8h~b%QKbzkX2l#e?aCS#;FBDua1C(n4eeTGjTDrzG*E^B!$I5(BPY*waeoO{tF6TCTX4F`#(?Yvu z>Q1{f)mAIj0wREbp_?irRx+CGYMQKn%VKyUzPW$m_r3yTA`ZZpg{;PvHC3*2s_}dc zuVZqWetZ-);kt4QL1oz{@+6AQDDkf&2tT##Unf%kvb930tVhbFS*cS$=MEAke%@SX zw2c>9n0HQ|EXlyD-P1T8A=FIk@rvXDmSpRg%6F?9) zP=y&yObtzESxl#Ey;2bO+S`Y-ZyGrP569Qn>z`*+*MW$sNDKWgRu4!QWpSAP(&ugy zZcOWD-4n-BZ;F(NlUPdU+RB)dAM)x8e2?9wpb(M10h-Y5aJhZ^Nmu5D$vJ&^g3v@M z1AhWlf~@bC&SH>6C{gCs5AX%?QSmaI%Sq*_q>QPx3)FIEosEnh5+R{<29crHrjL5sd@_Rg&9Zvm$b zSVF^B^`94K3AFg^j3L0hc~g*^D|Z+e)#iOUY zUTt3yVyN6WQm(XpQ~-B+oAu)&U}vh__POZ+1h~dmOZwcY_hSz9S&UpQd>QjSHfVRg zOFz2#q4|=9t!?dgh?a&3G(E|K#!8+mlMRQc_d(6NFT@|HlmH7)QHE1 zY|z^S-;ck&Zna`5KJhAVcq977iDlGH`a2>GcDGAdR%iYtjpzLCkpmWmJ> ze)YuAjXble$8oyQ80S!IJ1!+_-++!iWeSeUh3*l3F@kn7YK$wZb;Jatl)7Ow0iReZHv`yNlQnP45lN+09nazr5VtiWgR_P z-dRjf#QD&w(m+OQ_>2{q4zqoWU0IL=LRN9kWe});m5Zf7p3xMSC~Ta^DO+g;)b~HD z3;U16GxOjY|2{Q-N-kMBvyId}Sw@gGv?f=u zu~^4~Yh8&dJd5BGD8FW*F^^Y@X zuNt{HfOPd|-`xft$1U}Xon8N-SH^I8=Z&@;9Fqrn<=x()8)b8&>Jti$6T*a{rh^ix z)*N&jh-_wBhn`B$%^l<5mS}Q#sHnJomXc?jPr4mfCZ%-GwJOXeQUaW#OE2ofsaJhH zz}!es-av03U~bq{uF;eaFgLfM#qbLI!RdjrO8%5fKgj7BHj1SSDcVq#DvPeF4B}!X z+nA{Vi%Q7DuwM7>O2Gajg;*_hUM)(i8 zdWvu=Zv6K1gJ!Mn=s&x__g#E~**|J^cM^@D4tO__PSliE0c4oeVBb_hX_M;o&D&<2 zE{B%NAEDwUqm3hEO3vw9daM?rjB5y!w1HL5VP2nCe=s*^{$Ot886Q6WgSjbzh&8MV zqKaeDn)Yu}3TsZ^j);#k560iMy)nmj$s}x2-Wm6t=q7X(XdTbG=dScr`|^-FWm{dkLhp&DCqP$HrNvT#qt+@~4^jsu?D>)ROR9 zvT_A$h77oFE?4P|1v2Cq{DMj^e@*_!@{_r|XGTf?%GLj$mVd!Frn4jG@*&X=*VUwk z*%09!gx-9qYH)4EodeL#_yOpqBuU~A=%$|g0CdBhWZp1mh~veYW~_c~a)~|WW3Stv zda4l3=)|c2*DW5?6Q)Qcy~@6E;Qo^unH<;GPLUy17as4;`AxN7ZM72O1@a?+~le1MTJw+o5ao za>kx(xgbv~k9po6H&FAqFXNqF`SEAoecDwRVYP!gxD>#i5oBI*aN_^8diKxq7dq;b zqyH^}Ef1GW%K@$aqEt%RxUdLc)Q=~jcTZR4-qhHlm6o`sIx>i4tKdih3;&9iHZxJh zvzwPvg1dChxwa$YV#W;Ub8WQ7JLTHoGx;#on4Ni$x(Z{cUHiI*SREXj070#ijU(Es zH{_27t3hwf-EOQpWck6Qh2uPSdoGtfyhOhjcaThF+m#^sNo9 z1)kXF$`$-Rt0yBLNbE4O-Ton%h1e*BYSD%oc86eu!&%=+gEA!ulC1HbD`ST$kC@=Z zjfRTrSEYp>$Whh&N7u4^R}Z3WWdKRII7v$iHpX8BoEGgXW|{xIJ{O5%YEZRcU%s7J z;eXeVEys(Qz}NM;o<^B&kv!%P4^!eq5dm^%raB_TAn*F0vTl2~e3sK!Sbvx~c~WX; zwO8K{5*Z{S@r*w`13t<|k`&ZMs(ld(8!1OQ{4SSkP^T?uGkKy)bN{-R0@f(y5hbmN(gehHdn(3=u)D^?y+6da( z(9FXKfSF?qjm7o}%bR&P&rxHOiXdQNK#zPUKwX6n9|c0C-CZP5S0&0HvZF!|F}{=J z+`;}`UX46qGbkdw<<{-+Q%*7r8++wN?wRj@fA*`NyB|&RJC!_+q)Qrat~Qq*{>` z=k2GccC#T?^iklTMy&Esv-@u1Kr%qyd9mEbHZWZx09k*WywI!X-SZ2U>ssBme60`) z*YsDD-GNt-aMc9jv=YWZW!RS{Qldr_h#rYL)DUwB3m!T?+e@5r%&n7Ry}>RB{WS?< zpNdDSV9W3rDIZVV#vz}%-crt$WRzRoiS4%$#hP#`>HW{>+qOj{;T)SK`-*wjuFrZR zNx7ONoVLWS3Yr49Js#9VJ$J`cu`SDw{kQSnv~kvhs~k-G2ig472UY>WQ4`yh=YkoG z1Kq#kXI{iOtIXy`g7`9*C^LO3b2~-MzHqxXK`P34VMOR3X842UyO?GK1VYHX{ZSDx-ZXM?P zp{ug%?~u2AGe_TYS4Br)t~}GceyvmDhGcq>0q)XIDSx{RFuT7$wV-yzn|YxKg=P?6 ze3~vnH!Vd5sgU|GxaMLT1M{pJ0OF&%b25>Va4#pCt5>$!F1^{G$t#p1d&^+==o47y zF2L|9@$GuHxX4MbbQ-kfyB`#bGArk$~=Yeh~^OEYAFI9MH zvh;RcT}H-Vo7`Q799uK04YX-|339`;XuB^TQu%vCpZtSX7w;IaG~>gu3>wJSqkNKp zxx#8x;=k2T8F2j?w7&jU*y%S$<9WpfN8QxK>kyS7*-=OPC$y<(rH%-{S2p6i6l%)_ zZJX=t=1*GQqJq8|yLv~8lZ@kFO7Vs2KfV5Nftod%Mzw$X!M0#_5hmC(a!#q^Q|y3GJu(>P_bcz&$4hR@EYUpqa-ot8}g0;v|+sAQ&B#m)HduU7%zTr|9` zU^eugKDBDGfA0rvNFCBk9}=q%W6ozKnreqq|KGZhI#>NtKMBQ#@!(UuW;$A=q2SMn z8L?=eJODQIt=J=>bCcM$$BA1}%#nYfQ~vyD8nUWI_S8pr6Gn8B?Sv!T;82#t@88wW zHH}UD+g6C|j=kn8Zd|!qopf8|6Yr}8$T&y*exEd5*RWG(Y`gj<<&u0%(Cz|Mo3@(LfLJvL94Z~R z!-l*=<_~N-&pH^k!v=oM3?b$5;DPfW}&8 zj9xTWt2v{%us`Fz=VZaQ6DqMCO8WR4@ctyqKpB&QDMEXi`!t-k|AN(6?Yfq>M#K1w zULQdDSk|Bo1n&+cYSR+6iM0Cd7I}#O_R7Soeh1}s@fz9zx&VZ0UgqMEsuKNUwx~Zm zXGzbl!nFd4ktrVl9_44N4>j15{OWE{D91tj}TYal3d z*Xqjx60|l{73Y95UmI6i(z8QpWAF!}Tj(|yolZdZ%oFyisl->}8^T>(v~lGU|IWJ+ zLHh?Lkq)q~Trgm>t4?(_cE|a5Re$MH9#aGfL&|4HR6ol}3ylG0Fkd;83#O6)8EdQd z8QTuO-G8A7voVXK#LkpB;`*uzSBm$5?s-U9%giZ&UFuO97Y>}N?t{=A~wkYq`v1zg-IwX6Wg^3Em<+HYUiy=a~$bKl>FX2b5`3+a?YT> zdmzMHG-Fjo`ylGT)%yrhbK2eI_$kIEL5YG(;mE}95TamM zQRW+No3l5-6>z}KbK-`$cG#q(Eu#4&?@E+p9t2H@)sxi@diK@03nC^grlHv#OZd=x z1_JL`c52+l5_=^z=bZo?*Fj0hPb^cf?Ydt5#QJim1=A0`PWJjUf}Voa!VLClCYe6A zH=;F!JtAtCc*ghYQcv=FsIML6z#=F3opaud*N#Qa*^A1S13SH*IY!HraYsyvq-03! zZ({^aYQ0-g5xf#vz_uGL+7;k-xnfDLm@$c50>z`!R5mZBn0QnOK^4dA@r&P)Z z1M%+8HIi$>v;&VoyG<)8RTaGj$Rs;psmZ*x>a&5;`*WYai~RiaNiHtSnynFs1dMDOE>cvD*1SvGETczG zJTdm&vtwt*Or$L-mXmG^f?V`6FV*zF4CB@Y6BDMeWdk{rb5oToJPq$=)Z34V+vh{B z3zTSME~)yTb@L#6E@xm1GkF8)^Lgc+XcB6-+4)x(keC$9*`Uv&8Feu`^zD@~@Kx3` z9ma)UkM^lDg&FuH{Da^c5_BmruR?4m>qb}5A_qQ{R-fxFV|{}qvZYejr=2etm$eqU z>w0y_n1I;ZtE+0ur&v>Q9&z#CQfmsdEcTNZ4_ZI@5CnT8lRDocJ-_7JkMw$OB}Jp6 zon>E`kwb0>UKv02I(A0idJnfx(|+Z<_$dAxQ>%jCLz?bNZ`^M+_Nd8Y?FUWweaGO0fsnV*geS%BZ^)pnSp35cFuE@$ z<|p-}e<@MzwF4H0QPl$g7s21!itB>GHd|gbXVq6QR0ke1PBvsggbPJ_%s$(}*d=Td z_6G2!yDgoiK}6UCp0RDFdyN(8PMGYeMpfT93!RP<^`IvRp4?uq&IN%?uUK03^CdMJ zzAl}a~TBmRo|DNvyQzsh*#Z|Cz>cBlpy?nW(y)zh=7GAB&SF&2MH1lS zBK+=diMZ4CsVxJ)eZP)JKV;k8MqTebGyhn9ab$j(D`O1Uia;b-HpnSRj0dPkn`RxB zRLhgFU=~x*4?yf(m%=HF&mh+_jp#AR$R%Kz<}Dres8y`6^rox9b* zzaYsab*%T8VuUPD>Y&Ug1Y3v_*(~2U9y9MS3PK+?b|F4laHkw&I=s%j4KOu2TVas3 zcWm9im2umPNF}IQQ0Gs^wIyI~XDqJi_mxSX$uEmlwq+hadMF!O$u4+Lhud$Y-%f&M zIV3A-UmRA4eX#5~M!-yVs(yAe3B|a9W&+a5H0h1n9pAlrIC-<1{M+Mcq$q2{3TMMt z(TD?QRdV_-VzDLGDFpp%Or#1^Wx7r)-OcDM1@tAwJUfI=$N2I~%ovnTrG7a?#VxCc zRx<^Y%3+9o%d>ZVFlK^m=%1 z%7WY_K4sREJ9}76GqUGB`a$ogrp1`!WWe>dzVDlsl=a3q+8n8qYa>Y zpp*X}xZ9$`w&%AzooWi>x!bE5^Q3|8**hM_f#i+up=n*p^k*}sT8~#3AogK|LM@Ge z+I4$!igm}*0EbSne1A-tzDhi)Z1b8soQEtwW;V@; z@~^DlJ-G{Pn&m|%PZgtNP%{X=9K64i5-l^rrB=+@P2;%Bddy8U1UydkU~ZNxbe{q{ z6}C}gIds0q^nI!_(`62bzm&~Hm(;l`!I#=3?B9pG6~I1f*z^o*&2mMYN)=TOu_sBe zSpxs>4*b)w*CP7m(?omxAae8#KX%~z(z5~f*?~{*^+iFSCjc`Y=Pb*3U!0p_f8DU^ zNMole2zk7(`eYm!GssBaGsDrmzqpZW_8#q|bUuTmv6GwV|?_dOB z3e8n-k@M#hNrEZppy8XHn9WB_-`83}zzf=%k}emPd~y zq~%1ISYTzjgT#^uF#1O`5OC6ffWuc#=pH>bdQqiI(`{3B@~LnqGx4vOiZz`-tTyTA zuFcXrQBpr)RtN}pq%oR8Sdh}Zg^LJ+kmTK6HS`)7j*} zND6w%&gi@$HwXG87MIKySu&4D6hM_%lvS8w-8j3g{1QG?=dvP% z>Eu_>iH&QI9J*%sbgI=yRSy%N1Ri>rEH)|6!fIuu{0YY$PGjkUsRQ;X1ON3$x5Xb( zXWO~DC7%71RDBI$?QtEhk$8$W;_f6`4~Uk-reWQ|>6%^?UB>;**mSkA{>MAXH^!vy zia+$IXU(5ua+yv~q*+BRP@sDQH0v_$>zyNyQ08M*O!G=Q(9K>><1t-ud>y22u5-;- zSZBH3Lww;fxtyUf1v9Ig+w{YlH_&klUY$~1!J#W%{9bu+4HiC;?$)ZA4Y~>_kFt#= z@jq1|Q8*JAhh|;fqD;ugbKiU+UlDh*{w{0(N-I})x$tC5a!|SuqEe+9P1vXm1qgAc zgJe9xGy%ZqH%Rz=yCW~w-Lp1NQzOVqxrMjqVif~_Zd9~7b()0FakVMu4x+(^5S4gS zqz}iZGdve@FNi0QXSq2dK4qszB!8d6! zZgv@F$8Eja4X`X(ki@zp50__Hc`I@knAStMFMAwrWF0g!8~K1h)-!yBne)u}@UNKe zF_{G6eXK%kzbo;jyBv+Nu5#xkLOHU5p@&1Ix>kdqg^eoKOK_((b!W3_7;G!*ptbs2 z>T(v~H%Lqgs;;#4-B-bx?^-)GxR!YK;$w#p)|YnXhO!!%NNWy5bK7r4hoGaPLn+Mb z^W%$3rRP+0&3=*SD{5l|cQQGx^fP21Hl&}i4`Q7a3pUpRecma!+hDHnkJN+bKf~z`I|@axiZykv-rB8 zPsN_!&pV@_>^I7Q<6Uj*ahsmd?3Dx^q zrCTn#ObG9h-Sn|x+s&GcMW@l~^Jv4G*|qteDl-P`;7pgrlMs6kUj=2^sR?(icwCliu+8eb}Fo^m+PAe;&EOnsq=vZTX}<6P(*G`7;_Zil(SRP&lC$M6Y*B=#~L!HH(R7R|mQT*BLB78kZSANY9wmUvtmGu?}j^t1cJK2I!~K#O#jP z<)xz@=D8>J9<|BN67@$9>516@n-HsVX<=3{J z2EsMYq24&`SgB$*>z|`{xip|^t$2loZ`PVqmCu)~*OJQ91FG_u%Ml%(rn@hJ!9yQ| z!XOVpP=RAA8>XMRSZ2G}03PRDEvChg?A}F-A>n+thI6FCP`V`E+Rj@03B3wM5m!6$ie8f;45aYH8Oc`2DL#^df4Ev$)S@u zSG%(%jp_i8{i=4Z->nZwz?Zs1vxT#PHz0S&VQ0O$w{-D^mc&pzoog%gHO*u6=~PsP z$S+hzmxu&75S+TSo_`PeT{PdVV)!(8pH{DtOj<7rcL2|e$TW^r`>l0S4dbHIf1xag z&#R3FMNf&mhlxH*8skU*$s5D88_3Q!ZVh$40aDjB2zKW=Y6P0J3ed?ho&EY>5ad+K zI!%SYWih;wBRs~n6lfX9_+ z56!we%!&k0^_pSqd=J2DAEZ}+Ybf$7qIBVD^tqC5_I3`OPgdlrO85Iw<^%9r=N=uO z>&imtgY{m2Lo}hy5r$r;r9DR9l-0JWq6umbd3H#tNXXecIOwv^JuZx{2TN+`UCW11 zoc4Qc=#)E)tElTX3?R|z*Da+6gPz6x2Vl11gy=ks?0V?2kY3K9H8%ECPY-YE)gqs| z?lX<;^XFFtwT@<&FZi}oL6c0wjGKwkr5J0b4>aLNo9Fpnj1`ajYc_Tum_1baTOU3; z`Cz$b2PpmiEPH-l=aQ{euI%@Xp7}Oz+;Gg$^;|#lD{tT9;DWU(CL0R+Nn@d4~*gp64V({T6Ds)UCIMT3f@QZ$W!6_eb#3o4M&UkN>>07nhI-AkIi|M<&SN4%Z$~I zh=Cl+o8E*u!RRjx@9D>G)tUz&G-^w6l0%g&`@5o39CfT@EHO`}u%xo`nvsmQ#hMyH zuXSuWTSCG^L8>jQM%QyS7b9;R^tF-C9L7 zeW*LC<9p{s@4O_F$Km14OUsgceeJ?e17`)DLY~HB+oT}#@0J1Gp95#nH@0o&kVhmp zDq(->xqJCFv9?2q5d#*y`9}_3@k_F8Bi9RlmJiMhyqx6&tbvokv*y;He-8W*UzlU( zyt#VwRa%kb<{7t5!OGCz!1d4rmoCxZ#zgSuwIV1_+MJ62lJi(WKBjtQH^M}7)&J>Q zB#8bp4QS*&+*WI*eIvi9bu5?TVZ$&yQ0Vi=SiE6V8w{`^3f)K{>&vTzKjL-}OP4@VxE@uzNcZ4|#vG0$mw_)PZi zH&4ntswdXYaekXI&H|zLOcqzo(=DUIWKzQ7ksSViY)c3o5kZX)mHaNrIR6#z)$tYs zm7#>(RTu1b7fs(+A2^osBcGh!;H~7=QMehb@fUxl>n>%3wbt*Y@=b`hPj=|6l#id) z-a(63f{Q>d$U>lt6aa0;QAc2^AqjFQ`?qkUk^P9DY_!%pj~&>SAiD-M7m^^Wd*KXS zw}O@A*s>oBL?Fu_LNdMjAD$&{%P`wsU_e^z>OSCgRS5?&17(;?3ffe#k<6)Dj#3TW zkL#SdBPOlSvLa$kzpv@-oY^UC$HSA3f908m;(ealaNK zUtJWuyAp0_r(~( zoqwi;1$z`4YhWC7&EVAa7M)IRuy*ZCHi1stZ>37`#}Xq4H8y~|@V}LXby>`?@&Btb zqK7v5udd1?b*Jxtwylhx(dQO#?K$JQ={>zcRoNr1U8wRAafnQ=_nticO?p3W(0koa z#am_9SwI`|eGjfQaL1J-n#WmnJbw_|7Z#=LP}2I#)~I&-V657k0+k)5XxH9G`j7_1 z&oUQZ_lFoVar<+ZD*#a z!NF2dCH_SGjU3Wi(y-P3~zDrN!o z3gy`9RQfdKl=1Goy~a*w>DWf!ws8*nq9*!mfJ#^{_+Nc#U^+8w;orAFOfd4F>lZ>@ z`M+-g%%X(~e!?z(JudatxKP@Qf z7gfx*q!ebG`+v)bP=4V2?Q-DN00!x(@qPErX|GuN9}JS0mhSiMAMpQ${s0WWx6+AD zef=_rz<(5$AXuZYpWcD8R*HCpX^N=(HV%cE&FC%6Y%Jm=o$y{ zjDH3E`k1Q+#eTglqM@kr^j=T^0=)s)|6Z{TbZ;4K0U5QzfmFUMx&MwXKlZy2eXdL- z$3|2A?QTYd+mbw>Ei+2#`67Q+F*KdvAF@8TVpt!_l*D~N_i>)XZY$3=f$l-0%Ww^^ z(id0OBgOe{OwY|?qcKK%JbXSkCFXi_TN8_G=h~tYf^;-(D(?@7mS_%E&HvU181Xa= z|9UU>!RCr^ApGAj*bXjll`%HxKPn&4)Bl?=SSL2ISd_*3ja`ja0YC0|?jDvn)-MyF z5MglI515}w{c&hL6SC^3V+75B4Tyz$8ZR{MhbRvx=p z0Ts{{e(KQZ+0?#B!_zAJ-YtU0F$io=6$w`bI3XGK8G9!&pR`f)$KpFPe7$`YT3GAL zdvfT0HLPU1{i_nWVg`-Jr_^{q9k@Va-=&&cxBen%{3o^XIHm6I+>p`F|GE**X{ba0 zbt8ayyikCGKwybo$DK_~JoHrs^_B^UFFo#(h@J4}Q^%em0&Wi*;g^qDd+APCm6skB z_zKEGHVeEuCf)(nqSEFuF0PYUD7vZG?^*QySbD*>U)hQ+ING8M49M(yqm@q#9mbi} z`%~splA9h~hniq7N;edZhP+XJx*OG!FE4HrHaf_I2f;L=V#Ly9bYi5nG6RTjC7`2- zxsYjT_}6Gd`g~@nw?_p&2fh0G6Nj>j+>V7&eDjPF=7*q!JW!ulx>NR_dm;Yv{$->; zZx`?V&qlCy$}V3AZT}ZiV|A^t(c3w3aaD~40&4dZDfUB~#MiJydEvB;B4mDVJJt!J zo|wq?A=+y1z2$1zLtakc9%}Ub~ecp>w~w#$_L9hK=*St@4^B4onV7wtOm)(;f{c=us!xD8x#s z_7VZbO_j}!LJbMI;}JGBFn|BKN!CpY9N}u#O5)AP?&JqLb*4y|_!3SA%y5>gpl0PgBUvNt_X$b1#Ky)e2yXJM=lyicP3aO%%PLY|L}6M4qg>>( zjSZviSRI`j;rt*5;6nZqfG)18^0r9iDDxxaY_@Kh9CvMU@rOH5Z|5z zw1i!*=39BNHu0Ng7{vQPQ2AU~{ohg#SP6=`{u@>TE_J@Ws#4~eTbw6da=kC}f0@1+ z1=Cg8X_7;XpOE{5-ne}Or3Gnt$|F3fglR1AnaNMmep=L7T(4U(-T#J)ElEZZR1_=A z^t?pr_V+jq%#i4H2JE+Se`@<3(cM)XS_93b(Zrl*H+UQ_^P>3bBc+|5cYTjFP6sKl zqWv~2r6se~2_rlQqFXg8Uebt$eS-noD*(M=`!9M!YIEB1B+AtXk3v*+RO7up(n@At zXj@d#F;EZT#II6;r2o9&tpD|bn{|Ir-|X^51O2zVb_WNJz>?&J;*#zeo6i2nQn31$yxf&zngY^NVa7k)fD<1*eO^OR7w@D2L~12S zsec%|wfn_^lk=6a?OqdzRyjTSw#3p->MhjN7=XC1liF{{<{2X8tgJk+XApmw8g)q8 z+9QW_61?Nxh^^I%R?AsCRv{I&U3MEQMK`}y_L0W6=AJ6$Y_L;ll<6LO3gQJZ;Tx$b z7i><&zi7whZ95j!t_=(}rwoNV!-C7~lU=ILIY{tpNUSoNFRC=UF#xE>eyiz}1V8P2 z@08N4gUVo7*nhZc0^fzwxX@=0X7F`|c7GrJz9}@JC(8d=h|IDMLdPa2VFJNV;1jB9 zIfhzsr$i9CaTSi0UvG>MF;FI6^}IyX4mAtmi!lLjy}whR0=%;iXr-r6>@D4$18^XrU` zldrL4_#DoPoxJ10&EtNqMhpng%TNBp(CF_37#h68XFLL}KQN|a3mNk#qK8!g%h^XC zMXP2bBLz4~(*4=6P~l5CvAdioI4NJ>8xM48!%`q%Qz)3WT@nXgg};}-O9!@O6ZhX( z{;x<3q5jfQ-Nvi8UMT&rX@q@?(F#)cOwYY7a!xQljyDVH-$u2hO(d_k)@-@JD^lMt^iW2{G4@mGe8(6A?!}Eit}hNDk@MYUrA0K~aAMQz)7ldj z?bY^=t>3w~dbAxDAvU5V)JjHI&#G$ApQ+~0-{V`HxyFSAl&M!_@969A{zvCm z3~MC%{+FF!y1w-&#Pb4YRw?uQ^BDr?+3YohWwW5Olw0%kXRx$7vNnLxK zCylJ>-4YT%+qT{L0Tq|QE2_McDCHTjN#pjx=R5ptC}QYV^p6H0O8<>UCvEOnitTZ31k3k_r*uyF%(XS;51dg%BM1|TPqjLp9cdY;sk=)Yj3 zPzDf=Uy#EH)l|SxgvHL-n238SOh^2TQXpvUfTgvDR-+wc1;MsPo-azmDQ)S#ke1U8 zKpJ^MQ-<}KD>5b83zI|dN!|K}PNi7FYYsasAu&4N)qb1b)GDiKiPBbnVOf-S>@4+X zigtsmxKCt8Fm)ZDYLH2_=g&_TAA3NYPfVm@QKBF79ddCqHnzt%sy3;@>k1-HEC1;P zsJ%w{FOU3m{&n9NL>!OWx$&Z3jY?u!Ts*(srSyqen???uD%AgfLN3M+fwq&NH2(kn zC>OK)4$F{nIgO40YRTC|$KM)~XC+OK2bg?lW_o^i?`>h^;R2oG-3`w}21XB4Z9VG! z`1$zclW{G4*Alexh3C)T9N^;vDL1WOyB4sV$QWB5Bv;SaxGa0+WhjA_eg;5LTbheo zZ?{GQH<4Xwm0QPthR=Ur?RZwp`-&c+hU}aiesxmiw;#*YkrT@U`^cQ7?+TxS@e#P;d{szuyj>f`v#D=q(gCNL zG7NwE>Vkb?K3Ws80{`kqC+lCHOUj~C3W`yqGFkI7-Pf8W3{z$-HxWc{=N}(``I>Y-%Dq1Df0 z=aztwVh+oS%e$*8U%C491VvB$f`oqJ_~t(AQf>-&b$P}%qBSd%{K&STL9*qNeco41 zFU7O{W&W{l!fmcVxT`4FXRjuhogrkyt5OUdml&T_^B1>grWEqoI7x3VBJ2K;RQkI& zQaj_iJ@_xAzUO~^IW?Ck79Kb7cJFs#4QR){klNHwE#msdN0qT)@4WK%19jj*S>7E6 z@$L3>6m$xHndkW>o(V?Hb888c`L79Kou@^D13<4guNv$&M;h<0r|c2&s&=_{Cpw^* z59U-e=LyMON$@0Y93%+-K7dqtyPIiQoL%AwpsPI(i@!@~Jt1`P0E+xs{P~xh*8y1$ ztDI#j(qH5O`+N>(+z}^KV;}3V>Ut&dLRD2|Eysun(%vFV%<05hk^t6;dy}in(a(z$ z$`Cf1d!||65*}V-ZyIf^X|bmsZfI;aFId`A-sHq{)U4P9zHUv7=Z%N*I@UaWTi$Kz zoS;k1N7sTSg!a~t3s;-CnvR`nE${qr_TAj2)@|z9Pn=(2r2m7k?+j~lYubGk1q($) z>188AbW=i+4v7s>DG^ZtDWM5l2rYC7phiK6fDoy{1Vltcy7VqJKKnr(6!Npy5{~UThRxry z{{VktsFWIATBgsLv9pq_6p8qK{mne4y*#biJ_$d7n-=-2=ap(KghPgR4setBdUk{=~+92IbFH z(I@Z7{dL@UekSs-^(P3m6!9bxITocPf+aYM$9!9_^Z}{K{z?Liq>5G$8>rdZYF@z2 z!vmS772Bf=!Pouv1~OZxDHki-M}e#6{PCB~?wkwY2{3YMQi(ywjk(+>yN4Vy$s>bH zXDV*&eJ<3}>`?LxUA|PJ(jcj5w;nchOHp0v-ez6JOl0e+1TNnTew))*wiGcV*E;JTX@0EDvAEHnxT_^(qW)qj$i-WMxo#Rb}o&OtT@F#@V7JJ{s%s zr*C8bGj!^{{&;u(OUAWi;c#bc$K1Fl?FIyQAo*$7O9P1T8?4gn zY{LHH>8O;6huDuP2OP8Hn6WENC(%Id*C5Bu*?s@Q_t=Ju^bK&hhRz3|d?*wW1(N^$ z?Rp`{tzrWEfe#mTH)^A|kJc$Z;CqZ<#~+>eB02^5MiRlnIRJIHe+b+aglWH*5zox9 z!pl0GDS2;`FmB7$9M3-E^=Ncj5%&h9QoccMeA?qd(?>SL^>ZR~YbMUAZ-isfBD&!{ zFqdesFeefHDq6dkVGJeHW#t(C99&YtTCvf{Zh_HSQIe&L`VYv`jNp8KaFePKngNr75;RZnm5$%TzqfNsf90%*> zOX6z9z1hb!_F|2LPoSr^M1`U7eI!WEM|joc+4V`gs-}#T6K&zJ9~<-k1Ohtfy$My$ z6S!rK?oV$P6G~q`OO>mbO0?cSt*>1dbPJCAXzOflc|$dofZ8OoF7vdtX?_0TkB)L4 zdhA2H9r^&$u0L<~tX`5;cV?DHuS@1KZQbYwqhwdlBRT@yr^2>8%aQ64pL7!oTF5qI@%r>7R*zMJ7=Atd(0#@Ds3(EA;+AocVibq2?v zcl8K39uO=<%J?9W*tsIGO2AC|P(jo9%w&n{c!ke(G3e!|hK=b8|HeaQSe>*-^loU) zAdB4nq<_3%L}WefUoXh)Lm0relrg388c_3i@gCI6z%TEMlj}#SjJOu+FL+|!bj9A1 zaTRA!qM{sIqhOr3Rr#{6sxTwSTddh&VJSVt3QB!*YT-#hgoS?f*it5#xl~0Y;w#hy zw;$UvjBA^vcwx{iE>&!AqyBt~LgHn^&4pq9%2)o^U(=o`d3|8}GVy^N@Jz{D2A8N2 zm14m8%&(^LVnL2LNK@;K+!oKRFy%kOmehjP8AX{-d9kJ*#5_?=AzKg|PiL6*K-O&R z@~=5?c&_kK>{q)#DX;JR6aGr5r~S5m83FenP^WXX5A4R$e%fr8&5Ph!A}z0ew! zkmQ`jtkoN0oC~ZoKmKwp*z8bPd+)Jr;qTCHVX}uQ&tTu)yu<6}de361TdAwHNiOPL ztXN(Fku4pnml7wq=@7C{w+SY9SUH&@U$@%!g$ZBTV$Morvc9WYJQ)fO>gJY{^S7PH zT}pj(g)}GM+tH1`B-rutvag`N@$+{gR)}iujfNWu94cGHD+Og$D_JIutLGXwW_h(z zH0-rJimirTr)Abs>bdb+?pZAfTJrWEG~8z%AgLw7CIfan3Ht@5rDdmt^HLLTxqp&~ z{QK#f(@s>`?tBG&zV_6o5C53`H!{E0(Z6O7B@NOau~fKuFoz2}RPo~R0#fM90@El~ zi=t3-JA{s~%%>vUJu(kZrx;>sLlVYqKBFfR%r-`$v$^2QJOjV@MEdhlj@}r=Z6n=Y^`@iqyAXHL=&?y31{T9%rNKc!NtWC_L`0 zq*S#oAAJ5Ud{M#Q@_>`~i%9-i6PA-=A|+8T)3T4Eyq5=as`geZaTvLZw?U(6DUD+> zUQ3rWyi9`xOy0oU8QSr)^ z0rALn4|+VdZyl;QG}StW{siP^>)!ggpBu=?eKl?MRw535O3QfI8jF+dvMyM}L<)!O z663ZPHb9`tkt_mA?S3s)R`GsLzoVlPW`Mb<917{UVPdYWh_}L!=Xly7*vqDGFB{l3 z*gn6DipcCiJYp9pfxPV}-N94}Jz!qzFlp)QIx78zCH+Aa zg{a+jD0+Z=4dNp3Zwq$GshXo~&*21ggBrUWpGjfx40@u)$&s1#7D9DFX3bN3bvws) ztET3JU3UbL?Uv!kOC`7Yl)lIKsh^kr!^bZWm+&9HOTppGQbngdo?kmT+!|gakS%%} zGxE^(r+|cGIuX0Ocv4q6inFDeV1dZ$3S-Dz`!GCJ)YVE9$-T3bi8n2wU)L?4ku}Lz zdo=*4{im3X`E$Y;^2k&W*RCH1!*k>R%7CpR|NZ*^XBP0y++qxUcoaVuq12{N9kx~b zt}ccfJ`U-+;!~0HEOjoOErE=H`p+q~T*i;Tindr@S;|yZ#8n`%9ttRL=tc%xz>*_; z=7AH)#03@vQ6y+AN|p#_@}9>wNevzv*S;0msFy17ygfC>Uw`+rz-^Q7CDL_b=5*U7 z!PZpVm!f+LDA79hY@HR+Q^5-IUm+I)RJyn@4+)&`7A&{sD+S6Ih^9FwELb6wsv)NN z?SPoX_NEh-wEij}=w)7f&UI%jUNP4=Jvh&wA+mil${oO)Kg)XmK1 zs*_{l+s}dAFKFfKzlPy{+!*WtBmw5H0z9vNflsi;zpU_zYtgU%CpZjZs)jx+@$yZ2 z0N`Lm(T@L`$LqksL}{cX&h^>4R6}@PkdBMv1>q;0!Jc~B*Z7Jt^VqX6xiLg~m#~O= zfZik#mPga=O8Fp2d_3hMcc=l@6fp!UEw{L$5X3CVr)7@%g1(K$pIDOJb& zuUMx)h{WWga~8?=dg*H4S-ellBkUi!T~^c**5AAhAfd;9$VzQ_e6aE;>4!y*8OB~+0g*qIny}L9*e^>Y6XrS7^z3THx_#S zH#i_CgQhN)=>kWve2gzczqSg%2Hr+Z%~MR6_0ll_G>+^)FZq%QY>dT&|I{-Vr~uSi z7%k@xIfo0v1r4+UU645{-;5{{=uxcT_Y2FGtt_&4HfYR?1FT;?!b_r zyZT|I87If=3SSk!%f3sPI6_|T)dWAtZCH?s$D%|KsHSXW*S&s9>E0)Uv}+%JMx#YLy*LTrud0vixpOS+?(jhnVEB&2=VA^f8zo~B+pm)uhISA*I&W< z|GxZ$F|@0d{O#m7TE>biz01?FA4w4@WLu{&l=%mV@>g%>v;B=Kk0oe#JyfpnQLU%}nBmrFlD z*489!OrkE*NdCDL$7lbIh#@vM9^p>5TZ5*_Ewbg-jP$UgH%=kD)9urk8`Cky1j_?q z`SDoQE#?FH9%Ie&Mc!|0e^n$hh*?P%)5t{;mA!C$jkPfrA-R*a+y+QQYk|!8)Hgy5 zPdmf&A6z&B;KJa9;=gg>&wt>7rMtfT-oNm`u%I|+;Kk}-3=eh5Ka~Gnj6bN09~tTk zp`C%t$1pDq@zd^Y7D{L#Q7cG|OC`c_DdiPiQk-1z(DIq+C|vj}gYW!jv*P^EL%rJYUmcS!okFk)Hzq7iUw@6|Y?->Z!IsGNaI)|cu8wm;jMRA{lHOqBAz5?megb2VbsGU`q?g=dLlsQfO2{yh?QM1neSkMI_i=|AlEhA)ik)x^N{hEdP7S0BfWrX(mALj-R zFT6FF5thN<+~0_C;T?XPrYLkq&NM{~55~#a>h%;&Pc1VvI6G%_mmJOwxuNz~ydgDO zf7svF_40ln%%cknQbZy1v)39@DU4q8(4!QtkfkZJ@C=Dbbj_&W$si>EuhU)O5<-77 zLnUpe{Vx8`DNJ17y#_Et5TE||m4EJE)@*=upK?Y+WA20Te%dF#gt(W=G*gA*1`pQ3 z4og-C%5bB@9dYv*4+y}Q?OS8AaLm?COcP1jR>WwtC8|A4f?u6mMzFapp^VO&FhQAu zttAa9LNgrwTc>qa0xn}7on5Oo znx8%QnjhbB^MTU$a2NOTTzrOU5K?F5FEgv>o5)fb*+`uh9&(AH^??X!chv2#)tZA5 zYVI=dx3!zur14VA(d8Z-4T+UrkQ<9Trft$ql5n`BRj5+z{5CRLHgPvXt@b3`&JIlN zgyq^*D}g}6ZPv14aMs0zrhaN`RRW&l9Py%zwqg|hBrX6^+~JmcAEIH4<&tDGlL~K?tfadcnD*~+S-lL7zO&zFQ9|VzKE?qNC}S{RSZM2z;`%5RM%sjHKE_aTq`V) zZ|if&vAC`mHG$Kk{b9E`sL=uR^kj)>t-|uH!O`SV*UB8TkReOb?iMzLK}aPE9qyKGu5aKP@BmHB-_AaAquQ8`!EdDj(?~p(>OW>a5&wR|g{8Y`AR5kO0tvpEvmDh%>YHP|tdEd>8ml9E0FGXfg z`24xn%eGRg=Q#zLo9xsH85;9wI{GO1Hr_6CU`VFoRq#VIf8T4j9ar0eW~hHOiVAmH zdFFA_#pQge;+t@Gwo0{cBR_UXIWAo&kn|~YzvKU@0n05gouvcHgvgi~_;_9rfT1?| zjB%O)eonm-T}wB#UKuHwB0C?Ar=U?vSBZ`q^Y5;{zvLGihUWAP#~85iqWqH4b`6e3 zme9f*CR%bTY-v{2Q;YIX)~9#x843??xZRq&W3zl5a;s0OPgYYmX$Ak%TBJW^bN>?sdS&gFl-wBH;h6+LDmnT zj43p(s8b`G*{oVq@K|v2V7*19RG3bu7D})FS$o$l?z~;^#)%q`Vcp}cJ<-BX!?dMn zWnZAD>Di>GH&=!Deq$j!b>f}Uh1oPl$0MuLRoFYBHrcZsSG+7_zK?}pB>2u!UZKMP z-k;npkLN@WDR&qwg|Q`6lm&n9=L5z-9QGOKpX_bE%m-Chc2_c6YEKBzS-G+$(0Bsv z2$Ro(H$Fd3=9d(}8+t7>xpkU6rwMeLzOxpiwjD_a9^V5Hu6!I){I&fEA3&LA9y1pluNi zM0oxx_S~6$#^*V@_(*f)W*bYDIO^{L3y@uH`yXc$cE3(5&I`&>Kia1yc(F)xLQ2Y~ zr{+~mEN6op{)^L1BY4!-Usxuy$&~V^pBKWHXLaH%oNmk6;v~Eq zmiXUBh z4SCeojXsyQwsTJ#OPIo}aDz?L6}1$tD`AOM^`S+Xd5zS3Pn`;bN5Ounu^?vCdk+t% z%wQMvbW51tB(h0~lWK3{^>Q`K-bnjwD>Q;X$-_-!i{>|$= z#Mj-}%W>N-XXKEyJ$F8XpRF2@9*@u1UJtovHX&kZpqoVKuuCG6J9Lv`7EooTJ{L5H zM>XIsc6=@PraPT+0gCri%!;OOO9uTicBM>9U|FxinSFce$CBG6wP{nWY0%Ep*PkIdW5$O zsE(ym_#DHh`J!jKI(RiL5HsYadQhl#xotz!-2OtANGCyKX4qId@cwG!g}Zu0zUmfr zzE2EEgHMS2!hwR;ATA+d-E%ZW_>o|oszPw))Hr=s}*;<<)O z0Vhb^DJ0o|M!Zp)wV;ETZD^`r&x8a$!S{n#bHf_j(B~6`SN!FdMW4Der|b1OnLJ@b z0h^;Co9jh=b2s+4>s=*b0I@K`Q@R2#@}u|Ys)mX9Bu?ypZupl7Nc%rdDTwf5?H+VK z#gn%y&#U8)jK>eHSgwl*jWh#1g2c?y0TVHgtZZTi=oYRIJ23$(vVSNJKiF ziZ&1y*=WK$D1LX882+wC`YGuM#5J^xw)T4^@)s+o*}-$tA;^KClX{6uQxB)$m9xcyc0@9o~+(ij+xs_))8YLuifYVbhXz;k0;I8 zZsIte&H2^>kd>)U-2pg)Uq>f$D_1MFS<*4X#5lI9)beLd$xWa6?OJ~GtUQL+d6k{c zoaL+IxEB@ycfB!skju%_ecjGc@^Jp^8&#gMvn|{>Qw4W>%=qRY{zklZ`TEqdaK6Jw zo2V(~zznck{RDP|Ze0XY`4A)FO`dxXRZ7>oVhX^RX3hj=?y9&t(`I?Z8?mye5P0w4 zyaq1TzQngM?E*|u!CSa|TW9cH^Q(+>;p@rIB#Io#k+S6;Kz5~Ph)M)0qwiU!7>KAN z@9H4t`ZF6imh&Zi%44}qluDlF()xqd3r)7^Kc%Dq9|;v>7ok29JHPe(*IWhLLD_7S zv(Kok@9KxBn;I_9FL^n2nAeFQ2mm=W(h@b|cYJi`^q5DB2uZ*(rQoZ9ML>6m%j$?f zL1&I1{oiAm1?A7I3gIMjnMIK-rN?dEPaZU7y!O>CGGldfBZfX!fhgkC-{PC1UP-p_ zb@G)*b;bZ&2ye!cwBLdo`R*Sy7B%$7>>|6dxDLE}c5}mtc09R1 zFDT~h_sA^TdKv`_+QMd^?1!FzH0op)dx8yj+zXN|_oP2c(OTn2D=~@s0xO+(`Bc2& zou&Gr1OBJWHL+k8x1}O3+67~0tjPg|lUH}+S?%IuOOsL~TVn~6YMZIytqb=#e30_t zzyudu5iezN3?W^nd~nomnfu@Czo+%bM48o`knvnae%iJGEX>IapWrw z$8a(~ZXRFCu={Vc2Z*M8<0S9silq=&(s>%y>NmJHc>zCIGO66zd#!HmAV+(zlgGzD zu6Y&ubJ0wonQ5(L3WB?Pp^sUzUlKam*mUR4Ix4)yh~8SS+ht7>MU?SrDC&oP=zowW zwNZZ=VydS23d62z;+UA1Sb_4^?|ki&{^2I=*zC~V;J%n8Q2jU zWIgm4gA4 z8(r%nfij$C^Jx@SiD1@$M^20K7!x7(x2^QStOp+FjeD)1zrvbv%lLig^=7A$6+9EA2>cgcQsXno;+@3mv?hA=I$(8^WdzO80^!i728(Z_q%UJLu~2y zsoAr3(p1-w%y{VqX@TZr)xxigMFGz#!Z^VTO+8xWL}|LU!TP8wmOA@qGdn6o8jRZmFE-5(&4=oUmIo9j`&~|k=0bxkc@3K zNBI5q1hrY;n8#LVq4r9`5n_yaH#OVU@zu4H?aQj9qHEfS6hZl&BktZk#Xhrl8?*Nw z@>zGaiQChPEe{dz-!Wo1Flit*M9_nJUe8forzv%^a3NXCZ|g>W7)4K52EJ4z6p96_ z1-BngZ}f)|%NyC+y9^B@DYJIEC@*D?T6S`Xt@~x(Mc70i`){RD)aUm+0)KyN^RXbd zVE2}sU+$p%6qbtesSn16?z9Z=ZjvapY0;l zFdwqb>w&%eK4=^MxW}%3NDj1k70IoC=IVL%0`Hf?{N^+6I6btNP-;sv^sJaLAw>oJ zrBh5_q4dytqk>hS37ilIfF*tIN_6LYc3s0R+vx!qyx>`_*V%A%<}y5Y3PUCq40u~N z@poXk#N0fthIJc>5jR0I{Wm-m;CT;8gQg|CSxv)EU8 zXj1F2a-vshJZ9zzyhuyzd`@nv&=1(3fR%tGT^Y)l_oR!V2DhVR%La{tDQzl&>-M*_ zFQqfmiw67`!&PS&t{ygaZzUw_tZ+1LL33A5N(IIe5NQ;>;0;t#0pr$6eLJ??K;NNA zkYIsq_{8XA5mVP+5`a$a!RJ)-<5XpM$&ep{sGQ;u) ze6aSi#sqo-0y%r=w{S~rL~Ao9I)%@ZD4B4LuulPH9tkd2acko32tnJ=fpN#dpGL{g zGd~Uz)AOfK0M^uCCX-2TwcAGEX^ewpU8sB4_M0GbyGdttF>`7@*9~$+YC4!3vY9ZN ztTD7MXY~T&_g1^-UYgq$0BFQr^d!rMcrsykXi`V75jBMq`F=EIM>LXU_lq_aBi0=f*moxKYZB;z1D-lh@NlP9 zfjSto2&GGq%{M~Hsv!$)4hQ3-WK7>XE8V_cLfw+{q2D6b_2wOuZjJ>I0aaMP`?|1$ zUmO|F{Q20&GfbR6$>clxI=+g^S<=`;+z8Kb#Gf&=@^Ad40TYqtlnW*=xF@x=A_vrJ z`rn5^rGX?qk~=|It!iSLo}!_H@b){wMu^+>LtZFocm|AYy=2P7N^H%BE&kW_J_Omv zpPSyg6ALBwngSk%*0d|L6hsx_wOLZHOfN(D0d<@OcZnZgz^zJk4c9wf5 zix2TsC3tPQ`dZ;=`;MV1>Dw(|XZ-qlHE#Tg-#tP%s0L|A8`(jrF16N%{WduIk~6QH zY|BB>GXZN7M`*n}?4eIds}5rR96#;U&t=JG;Y)H2&yX{`5eBRM28I>GQYOvDVqvFE z!sJXugaY%M#;03nVl*i93tG9syKTX-stWYRPz*w=Th3)7Iyp7EUdq za8?Hmi~V)`68iJj&cg_@P|4!@67edhB$rTm32jJuS*?7+>M(YpZEj!h6X4H9KRth1 z>EkjRtu{ahOs=&2m&#;7@yjUaA%mJ|7q!Tq01x)30gGV2_tl!b_P9RzLpK2Ma7?zt z!7mpDjf_F->&k)G@EEYy%uJxnt9KUs(qgyF3fYfMb*FqcWjn- zt4l*1tHv`RpVzqkNyQ$?F`PoGlV!7v zbT8Pn=e(b5>l7Z zN^v~AUbn|+70?X0yKg>HWy3jyZeJ+a5Wb7dyb-{w1(c|`qQ?Ia4B^&P-&$sI&q3x=E7PbA|MD_CD19hsFsYaEF0M`Kf{?2K!iEw1tx&D1qoVefylaL@YYeY ztelQmtwWEvT87JOL#>TQE6`a>fsjTC(H>zSe25%=1lEJ}hv|;Xd9K&9{f4Hdp4~Ut zicN;W)%0E}u+eVy!jnsk$JtMc)$^)0+j!TW;FO&QuZ0OC?|L3=4XFkTytlKXICZ3j ziH`7p3M$*0!GK+J%Vr|B!2_}vvTMqIS2fC+RNSZ)xNPcGlE>_en(lk9>wxD7Eh;O` zP_5o?rKaf?Pdbf`A@{~f(vwIJ&z_6ScNVp{TU_K~CJnApI2@8kY(IE2S)I--SG6F& ze6mt#OnaNYa|OJrsXQODv+dIgU8%Fg*_o{MjIE_w;>`69F?m(#%%8)h*K&Kox5HLS zKQRxB153o7Y&mR?n6$FCCD-xzi_H6i9%6JMG`8= zO&XxfD{QhF0u%%=OD#X`_5|`JL*wI_+B|yadoSNCYdrJUl`x)=h-N-5K=9|1u3|@S zj3GbZw0RPp1y=F)F!i_+)Db(qBs|PwzMY~qUC_>FzC;t>K+&xu$g2tm;oLf5Kj)L*SfFH+opp8j~G2j6{2i*!S!_i;h@ z*QrEUFfx`r%G(tw?GZ1ii0>-*}f+ z-dDRBmv`+qrqa~@8MNw2pf18f0{X8W{+=ZJ>jhjh)KMk)?hnXEj?Ec1`sCPq#0M4T z3C;;$9oi~(=m&W0m7)5UCaE@P`OC>!OOKH8E-|xUGkjg}_7Ll$KwU{A11)o4b|CN5Lb$$wlDBqb_rK@KM37WJeLGh_A1|BNKWQY)Nwz ztGU!2RlCX;^%q+JtI0MjxZZ}eTDiTvdy)b^JZz&%z{_edLT^aWm&!vmt~7M~AD?#L ze+5Q_Uv0mhtj@OMnj}8ltxexx(^W}-!&e}5dw9A~D~9}Yn3x5N2bJ$Hce9B!wcxc7 z-e95&=Y4qiEczmR=Y!wjdsEN&iN+RPLzIg&l{W$4Ao6HsM|D=*z5j^mN+ z>=BommR`-I&wd?b78ylSO5>-lsX4cLyAM~PDE$#~CbrDsB0}Rg5dql5RKIVWAOI_{ z*c=Fp_wvNzmBb2g!4-ec$69*|%Fkd3ZI@5UAt68ivAe(C1wrW2}1hp&Eo`P#&c zVh|-dpg%o1Op7A^(at!Obg(m7OVnO8fd_f3$_J(${9g4SH-!2*1WYlfw!;sR^SNyF z&axNsD;{d{U3^PIe?b5%@N%s^wNHT^+Glow*h8-J4+?E7!SQR#d!N|oEI|6Chga&- za;>Ef1#fn%^vlXZY0%Em&(}h>vUQc6IdNl4n$8h!kAs5nX*3eIT23&dARrm#yLOlAGnj^%bdM9es+j3JVyV+nZCKC!4+bZ!aCUaOk7ph?nq zyK6$?H1DN*k_Dknz3&+phSUO+FWhp-=DM_v_<+ClhjXH6ieJEP{V*e^-$de|&rT&PY@GBMkKaVSKiii!X-T|K;yJMAY37CIfWM>Uu` zvztLfOLw^)(QQb^A=aBV>s&#rBUD*WlOG_K-2-X+KGgQIszWz>`!wXJaD;bytH0xac{@P>TxbA3_@o*<3jeDNpqxmJhgypxYB=nmh){k z6Wvz}R3);7Aby7x38KT6H`sRXmy_mL2-PLk6}2V%iW{4bnmMo+Mfo>qN+yv~aRQeiCFd+ck$#HbJbTbt z8!JeM#JTp$Ys1)4@Hs?MWdwP8xG595C4teEAI1r<@TxQV&kJdp9jvfHYUt+rIUMnm z5h=S({Uc4Sz&2*Mp>%-`V$T*0=u)@tzIyW(8Y!LfDty-XkL^n($eBjd!{XU><+OXW^)TEw%$Xp-qHVP&#QMmU#lF9NE=$O%%J}+MhrhoAR1pryCXTm zMtrN6ElHNX23BsNEW3QQj3z2>a^K;mxfR!uYnF#IN5B+|7+KhN%p+%|MHjl;vwgV1 zA6iPWkLL2LV@du!g^~R`b3F_?HnA*1Q|tyz?A?)W~8!YTsH@Myp-T*jZzxyIFcU(}Y6&Jv{e5PT-~JL36>| zLwxMWt;5Wt^^|bC{qkW@KD9y>;nVT(1W=SIV>_5{Q}hay@=q;N6rMrggOcW{LcJh2 zc^<^eQkGn`jc`&?qHB_K3Gbai2Ao~! z%hQ^#Aw?ffQTWjP9_SU2-h)!I(fbCgh|YMSHz>afs3s?*G!3Fyl?!S3qA^m6DU zubbSY3)J)ITu^eD3QW!O4c8lo)fY-!;%W-=_R^3I+Xc1z2xNgI9>y-m71g1b`x(Rr z50J6N=mqgJ&slh>zfZ{;5^UotXN39lxUD{fN77|kd*<|m15h(f-OHwaYL8kP9J_Kn z?Vz#t5DOK_EY{eR?}=}wdN1Rurt>*F#Ou~$m_;#&<6&Xwd-#PcLd}`aP2I*4D$KcW zL0OJu9WQ=qh+pCE%3HZRhK*r#$DXaks&H&}CkQWU!EJ8IdD$?xAFVTU9vi^O0-P}; zEF$GxTe?zD{IATwd|FF5YL-}gxLhk&!8e&%6R@?kjk9R|tu1mh%7QY~Ctn1&i1=Px z^Y04$5-| zpjZeu33b_YBfdDyto3_@n@Q1gbGF)Tra6Au3lS}b(?+4n%x6qAq4JA=dsxk=*81Bx z($VhRfCxT__n>iB%A}iVak93Mowl$A*8EoaD!5mSFlzCzruf<2&1^KD`K>6>gd9dT zA{?CpPor&pYMwx?}t$AOYI$4Lv-!67<;3AOWlvJ}Qt$ zdk?!T8pQP1i6_}^!9MmoN@N;(X-C-TmW@0?WhDqmCmvr6h|$~{1_pPJ(VxmWWn;M8 z^Bjm3^(JD@5bxp4p1)$jcGl_(cpW~s*Tvp7WF^Rz{Gv10jjms+HvMDK%% zAFZ@c*&^kaK=7pn(#@GzjHsp@-+j3N+u_zla5bPE?d*q%FrHE~GhH`(vC`!O^Qx(( zd7oy*{`@mDMz{2B`#o6TNZ&{vUw^|#--+R>z45h)86Tg?pdBi(X35nT*M2jcD%71< zPEDq+9UZP^-n1n8=m?QiQ-?}P`jY(oKp3dgHYUX#cw8s=YZ*5KKQv+ko7O$M)IR&y zW2I^>2GQHi5l2r1atTjD5%g&!fq{ei`AZM6RP1#gwN%Qv{Av*6syPxRnFl|A?Cg`r z$UAC!^D9TC_OOa$hrng$uvoh_#HS43-6rK^i*|?X@6>41+75751>gM1-?oKNEysce zU%GyB`ORVQ{w;6)D;Zm5EO=$3HA&O@6}p%~e!%MCQBdE?hX@^DEIRO2s5OShf;=BO zZJs2M$<|GhCdjG9$L}oeiYoC`H`{luAbXiSe0U3Q*D}hVw!XYXaROt3c&wdQ%o3k; zqD#h#%cf!Obrh#s-LBrfPY@+45v)f5DaAgh? zyAzzLeuVT@pq$yY%GsZ-_1!+svWu6cCgo{3SG}6RH&TyG0}q5$p?{se^zGO<+Nw63 z{~bxt;Zw(Tdy}NaD-b0PA=k84WmUpkRct=yo&~q2n3=0uP-^4nyaiwlL;FTc7%seV zSz`N=&t5p}`>y*U`uh+aE1ZRv;4gf<7KxD4i=2wc!t@y161^ko$Duk`A#;gJkEk4A!jqVe2@Xzc4Qgveppuz z^PHR;y;WJxpGmnFlsHwiwxV1{39o(aHGeE!Zqn~$weU?{Ll>b`7A~!P=I;$7xjZ{Q zl`Vndn~x)`9`$5Gwz9SZe;uR|Kw8tMEfe>OYY%?8`A(vK8gVu0DW@Lw!ww$JwrTI~ z&qF7JX?ocRN|>AbhJoC|EU+L)>VW(6XJNn}HvUBQuB@VtDzE?u^4c`Aobvg3W@$65lRCU`U73ciwC~|WO&a@Dj6n4r>71IWK2Nko4?wpq9Fv5!*jX>K?& zu4$UN@7sHv;)+UnDdPI}UQmsO)ON>TP2&UdJFPtLfbJtoV*#)ZXnG}P?t-1DUbM~% z!rtesR58#=V~qt>zc<>{fsx2I45yDdy03UYjS!9(WGqett*8<-38B`ytOADhaKkjy zy7Bd=*(;3xpFDg*G6KZ8`>Lz?%>HkC_r*|I5{{dLT1-mbj-;GAt*+%ujqrNG#e#ti zdW&O*RZgZa)|>@SYJSjCxBkl+9V$O5yaHvb#*rJYm* zeU0GbL0in5=nno5eeGzyp42w(LtK;Eui8#|ZvFpI3&fn)zJ|WML2Kb{1Hys-`I{_H4YE6+(OR zVsWHRg08+F_oxOct-r}Al5jB-M6Lj?-58gOv{1C$ z+qr~MDkZ(ap2r^N`Bj`hU}>njUV_^`tJ7);wKBtP7R5e*___)*^{KuL3A~Fy z)i;4nLXnUSuiJy^Ziz3sBtb{dS8wbT`vUItL!bN8(NiJ-S4ol(-FTZTWTG^|?DXBv z4ffm(cAvyq8M}siY-zE8LssmgnMFXk-zPsKJwvd2dD`46M7AH!!NGj7^t%N1l{D-~ z7*FwN7Wfrry6m>a9Giy>1y}052=IYcbCrKWX-oJEcYplZ{CtCD7wlHm`Zm%3IF#`| zC>gpxoUTDI*9)eB!GF2dSglA@XvOc+3*Nmpi|z_{=xQ%!dJv~^JQw(LVcT15+Q|54zpnY zJ{+1N49rAHD(F?x-a=V;_dKIMF}Y8K7Ro(xP~oDo0PI^Z(dZ9zzc_{5pKN?d@K7U*FhfP{uS~c+-jt!nR$(c6x@&eO&70#JdP&rWKv8+1Og|c-?JHhyQn@ zaKlx%Ti2>0a+N2SW|qoc_KOKAt_;!v0pNVI4sVkyZaieu_WrDMHj_^oNY9^}?nwkWZB5L7@#1Lc(x)%xV? zsw6X3Eb4nrRqpLIpqRZs47jg^JC?#9W4Dz9sLZh5Jm z!0D^MPWjF<%=*_LoMQ1c$80CCOK@|(-H5AdYbVuhwY01iS{39_le~)A38IRZ<}*YY zkulI@N;K$A^D%8ufjk_bpWu9Q@&SBWFE3WE^zTz)B1Vv}^!cfTzF5!@ep;%qTyi6y(h(Fe4a*fY>G-G}tab1OrlGE( zFAEBBP%O4tfd=*aslCp$QnOfzLBpcl)mxX?a7C%vZncYfZ~Hz_`tTg_7w*Pr_(xp- zUzDA9T$5+N|JzbU%TlRqh>C)iEkfATQc=sQqOt;ED99%45kN&jWK~hZOsXh|KmZYB zCSVAN0m9xhkN^RJkc13K^1JENp7Wg7`Tg;nzwdvN>%Q*q^&Owj`;q`8=Di2Q&MpAP zT~h5^G-#q` zU{d#o&VcivvvS2|JVpdriSen+1)}gka-#F|pK*J%F0lU?+O@iN0eV<${N^9~cV+cn z6t>~_M6Wl*VRNIXa$=ACuvz`Jz@;7|MZuYq|-HgY3GQt1Dfp?dFIUZb5s!*sS;qpSb;3yCC*V924)VQj|B>~-wVi2@(A zA7-nvARh`JjW4*#n%Oe<3$o#KAgcUlxlsu4*jzwPZb_GUz#Qda>8#JYtTh5imHTt| zE}sZ-MQ^i~l|HV@Y|I>^ug~pzhxs>B&0d@HAC2@DD>X#Lx%6Ze?Pbh<)wNiEOHBNZ zp!er;q8dR%GkHt$%X!fPWs|3$(#EgDk!O*j$eO3$j&Gm-@_WHTz7ndTL%QaSe(o^Y zF1{s4(q(zU&iuAaST>we%;-*W*N9aVQPnh|bsr75A8xp`&^2;q(fd5D8xe~izDCu( z4J~_W!WiEC&i`8gL~=X&hu2OSBL!EAYeozT4$~wKkG2smCrd=VmAwJjYMGL!RDG86 zaorA^;LwI>=22NC2M17ez@>Gvm&7HdZAO)rUhS(i2Xo$OGoNQ%+E5ojA@O@@BJ?WQ zV*1ao31M?vq@QYC%ti6&plt9X(Jw(Dn#YiQP=km4FFD^ALqFED?|AXJ%s^tnvxdyt z#E(0VLB0e#_x)orFovcRO8q|5;5LhrApCW7{o~k^ogY}e-4z0Z)fe;GU)_o6C zuP5Ix*t3}PCG%Kr1G6|x94Dl7w>NmI!^LXYJR0jdr8Wbe!(bZgr!{p4w^3~aV9Xt- z{E!1P{u|w@sP)un5=4?BLrALz7dZpY?fICeJEzYF-_AX5zbgstsVqXa)TBx$(UpxL zQpN>&s-dJo$;?5E?E!yT%gpglYwY1im%4pRo)k94{uT_h{1dm^-VY?nZVzkyA(fKS z(R=#petJsUy88C6w*FDm^Xd;42tvJG@+HSbsK~M;pH7|L$Iu5ILm8TxyHsYy6&Umn>$7YrH*lPvh{Y2cYMi^kq^?M(kHBL z0m|Swv791}=a2wTi|spjq=8OA!~Gev2O6IK!GhnPs#PzVu8@B<*{4NR3!&P8I@n>O zt+bXG@O8nTr6jt72r}=@Dhk>@tPnPY3O*ls?*52|W-Z2hK-~)|a>v&5vVlv3Y)44O zgWdpPFWWNu9SSMUv|%a3?S#e|6a@ZQEH7@GaKJT_aeu7}X@^g3UxU=V5r$pqUAk=* zOyYEmaFWE9?G$P2WiJ#0XiqK56#v%ETc5h*_usPl+cLelGr^>3J26DYTl=v`(goa0 z>!GJrWEAe4wT<6l{AxQ_O*Ou&cLeHpsWZj2?X%O^=~_Z6p+JqfT9(^Xdq{s7=p{Kz zl?$(Z-Op?750M$gS^Jiz^?ij=SM<+7TGAD*0Ip91I+Nut5YbW995U_|B~>(^@>KLi z;>O`jkhDDglXOsI_{zypzb}fw6$9rC?V&BsZ#UFT&r~(!z3%TkR1$QSJ-&MG zh)X`jsscCgd+Ry=g#w#1!bqE^PaPko5j@_Q&AeMYR=~y2bmCr)lK}wCma~@MM`=U+ z0hh((n3Y3rg)jKs$REQY$aN7PhonMBg?20u(x+;3_d=Zie!s7T8LiM7AynT^tse9d z`%|ekW^r#08(|SfaRUGMtELMkZ?9kft~?mlJ*J*7-y`i^EK1CCSvebd9$J}Z1*eod z8Ek%vP%w??z-#3MU_8d4+_Qiyb*S0o7D?spRTCF)W!O`Xj@!uJ#3OGIV^)()dGk4Y z%wq_-)pL%qXKTt(3bfjC za|`XBQh+!nVo^V?LBD#?bEiclg)Ua(U9=>I&Yg6O=AH5J^Hw?5l~Wj6RxYu#M3{c= zHsL{jp+;M;)-zj{l&_F^F1M;2cfZnqtfq#bW-BTmbMd!loS!3TrzP#Xp87*?_uf6_ zXcX$cbb(;$O-yRbG3vhW68jsVRTn;3Q@oUlHaA@9E>eg&@(i52b!@P+M7W{wsCCHG zMG!^O>!myM8T7kj!r7*!4yNzZjC)gljdzYy=wG&dR}G-f#;^T#RuH0wi)C4Q$Tt0` z@4BYRI%=s?EJ~(^@Bl}CGjd+ zH~um@B6X!K5A!SqX_RVHCEEaK&Xa7AOUDV)t&b~_lj~B?2-_zIUM!~ks(AIJs?olF zdh2ky5LbL}KGS7t;V=EwzCs2%=YqlGTbH#8jU9C*&SyC>V)OJ~(T9@MJa@$lJPP{| z_rbuXm6}OE1rqhN6XUzX3^3!b7E=LF$w|y``!y1bNn>2mw^-1_ysgxrAIH8(V6Wvr z^!|-FB+viJv$^cTGhwyZ?dmkIESrs`EMoQAk!Pqv7fb5w=aY^x zpap=|8Imn_kB#(Lcb%XI@_ zP-%^gsqd@o{jnfpv8Wbfd}@E0qC5S7@I)yfB;Tni)(6~m@!#=_5_f7*uEQ#Q6Km36 zM6SDil6v7MsU%h z(mTV%*z3A+27&)2CEuTr_69XKnkM|*6!4EF!|{JuGT0*JK0vGCl|GD*RMn6A`eE!< zXnOl!G)S7HBaGPFOLdtW5IZfOp9rE)gYVL8eWi5l9RX8swg=n2C+KnS zRLpw#jpb8aeV1>Ue;nvgNqMxCa@9@mSrTA3j0(}qQLGt>)UWYYVfNm{O-IbKhp+Sk z17<2<5=0IASj7Ik{dD97VCOkr^Txe6N+IMV$?p7t_q3*Vw!gaUK|D%E{;8V$6O@>W z1)!?;tgV4uv~6*_t{Yn2eRJ0yG@5iq!rT}zBv|Q0w7$#gAfTqZ(`ri{Vs9O7rl0W9 zO1*~4P%>=oE>kg=|1ExwG{_HJ0gf-wPIXEz-+pT~98I<@VqhJ^RiCKH8{^2Sc-ZxXzb!0*k|pskw5o%=)IEY3q3 zWR67EB0T-B_VwKGTa_50QHRK%3vYGk1HWEu`j%HWVXF~O6EAC0l zRT6p<2AYUMPfx%$+~~HWd~h_$!ZtzQ|FIpq0}zYD0iEM7-j|ouV~&CXc9x!nBcE%- z8w{RvH!u9DwXC6eSpoKIEij6^>cAwfR|Lil!oSWQGqh_Hs{xo`Gxy}*(7vx1NE!f! z)dDNc#@pBA8zaXqUvT&uIsI=N$$;IbbwPNVLlz%zJrkKzsVegiH}TDv!*|XFYj$r3 zUDXeJDB8775o4j1Wu7;PR!3h+j}gne_BjTetXyhlSpN8Hp>nc@BR#{dqq?}`w&erz z$KSaFaknF*Yt|>MjvA93pva|%bty}o08OmyX@f^V-*Z#F+AdGObB&8xtzAM?S>Ye7 ze|U59s-%nb{AHTzC}r@3i})FRK;OO+BA#T^_nB5}d?WHl@dCFYd%p#mFpB>ysjn2H zY887*Rl+=O#;bbuxC+x=#E!powj?N+zR=O|#H|bKHFJKB^gfjQg))$p@PWU(blW-q zP>~#6Dnz*de?*}D_HEcyAipb?u!=6)@$H*iJaDG{`d-@fwpr(Az*)F1*-+{fKh<1t zoMoMe<+akZssOlDf5J7ur`mfaI(Zc!Eb9IkH>wUvc97AvtL{G2IXdnI7*M)X0Lc8r z6*PO|8KN-%c0%Q78iG=If6`F*sf@6*mP^wq_J~cc5paz5;y)-}bm5UO7 z;)Y8{NC&?h?p3^58uW8O{c}}9a@&7deE@$LB!>K=*U6`-?sP!|oK<=OyzxHrYG}sQ zzwl(-?{}OMgTm&F`ELxovGm@0+vSPtX#mESOKuE?#a!$5s^1b%EjR;EG6Eum&c%ha z-vv92rH$j?3unvld!86r1DKN$UT!MY=zMBKM0CT; z75vD|=*fd;vI_lPo5{APMl~PG?0AhG{XOBdRG-kV(gnPBW!}h-#)UF9Z}}asO%ATC zD?2t6wzkBkB-sh757|NbwBvSCp6odGxj-&70AMn37JNy$z%{b_>5pEKVAb$<_cTtY z4E@_)iurDBtST78c%!BFaSkbnaGN&UnDpJGd@@Bs-JU0F`KA$!EwccG**)-@aY1oV zTySG-!q7CVCxKNE=$bDXeqiQa!gjZ+d<74YCwqGH5AWkfLG@doNWI3oH@-@H`GR!} z9xP^CC`N{c>``PLhV7gCvRn43IOWLlQSzN^;|LPKk!y}^-}1ryGyuhD%%6NYljj_WMXD&szK3z&RhgBO|pMS8> zwMGGq^Y+#=$&$DnN0ak$SAwOUfb38UvK6ak#GKqy^%4fAoIzm-(1k6DG!QlSOcJrv zFta-n@eE80*Kt}u#2*HTZeM+WhN2}>>x-U)(0?{Ro4sc1wc%(~Clr905BWDDiJ4v7 zi!g_N#IqnsO?@MrrE2E}%Q;WW^*6b{NtQet9L{RxEoP{#3|-iD4XKYXiVaiWr!@%k z@ONa*dL0^~etSQdJS1AeyeFO=bz?lXJ8#kvWf3}cvqe|Va6X^ zC7sSf8wvyNa8;E8yhV&V)nt#M-l$QKEjf@>&Ky4CpWB-)oNQO9U|=1({gQ2Oyz^4R zmOrUg^+Nc*c__f&Fpj%o-fchjaN*;)MSDp?XnWzlT3w02LHs&&I)S~A0`?!R$jfa~ zw0>w?DWq>7`OUxK8WL5je*}pZ1&@t=ykU-|Kkehu!^n}ls#wMziC+X8S95EG-S3AhsJChp$E<((73+i%P=-%CvYQl@Mdu&VKK zYB*x4zGI;}2sTl>bqD(ZEgTN0%k?DS^Fjr#w{BZyr+93KJ(w6oDZTv%1Pq*eD9I2P zg5h%E5Cn!l?ozi};85YGImNu39~ZfOUH31FXh8Sj^&6B~i%qt5Ezxz^`ja#q=h8pX z8LUtff*84%5OxuHBd77(7{mqTJHKZYWn|41e7rc7-VUm$AEzyq8N!p9OL|%dB&=?w znR2|;N`f}5AOYIlS;{egv`EDTTnY%}9+5wV`x1=W!B}_0@-40cYjY6_=AzwW3Wzk4{+zc2?(E9GCwMIoav3+}D36RRX_8s-%U^EU+784eBC1(Gw+o; zS8u^Dty9j|SBU3H|VcCL0Qf;7se73|=y9ReA5gyQ9PEdL_ zSNKNa`6A$C*3V@hD`?#ltk1Ys1kK%?V+G3ZZejxitlKCz@BMoE*n>-ICl5)#IC1^b zyWej7_T$M@H^roH20dx`>4#G%PAZ@JiFS(l9rM%`o1`b7#meLc<$u3f_kF|Lw?Y?f zuEzNP(AgrJ4ihb**Y5Dm;!OO_cU#tLzY6Z7Jt}p(6c)h=Se<2O;^fdSmS~p2#yz%y z#>1Jvg2pb1z;z5CXgnGJ@y6WrX9~ov(sJsMa+2gG(7xD`LkBgM;;0ohWr}E3VNo&V zzP)G<-CW8lZmh~A>1kBA;f=K#XFW2a8NX(cDLe9ZL?mfd>qRnd0@J3gmyNaHNwl(B zH{+RJpxyD2D&SJYfHnjrYos!X+dRwMD35AcRCEZATXU(JWp{#u5@F<*rjfcqYf(>U z7*bKuUE(c;#`Bw5a7HnxmD`_U7S4>rvf`$Q`Ph}J>+qG|6o<{($yzPkEqOCKRkqX))&vZur~u$dnE! zpht22QGJKiW*hG|y0kdU8LVt)Fn{lp5$@Aw*8bw6AsQe^`L#ufdfCRg@>-Fjk1ocD zuHBoVE*@4G&SCo94uK6d-aUl8{rS{n+Q=VVz%pu6e|KCQxrKLm8&l90Y{9W^H7SC7 z#Te@Oe%RXX&G5u8Osz0V529{L;2gk>JG2+G+ziRlmy?f&-lTB#meb07vG1TSHN z#`YhZA-CHZXN|Xpg%`W;srnF&!+*wdQl6N#oEKmDQWR0BVREhrrQl2_M6i>?(9~~a zFBik@6oY7~(4h>M9$8@(6Ac|_Pq@x#o_Yea!DD-^Zqlz~$YrB5a6|ph?S}b|6GbJovDxvMJGBHqk!iHGWwLX5|OE^<8`k`V2{k*Y6AyMh# zW-@7GZT~UIj^iFXtQbud3N@eS_r#cfB7P{_^CIbT&q3bY=lEc7aBF8CW&417B(_Tfla zj%C5%!VRU%4cmSk-z|?X4plji^afeqbUaOl7%;OXa%5egHI!Op=Z9)b!!>gA@WN;4 zM)FZQSuPp zXzA$_TCTm^-~6&+H+KJVm%Ul8nWphphcKDH#LJt~?8<6jm!@D7T)z~`7lsw;nP^y2 zAPZe~xeet)u?9?X$QCU%itV-` z7ks1Kg$O>W@#&C6%1~im`F#fmUMN_%bUo0>w!wN$y3`Mic!NoDA z`Q=G?y)gVBbhS33^FS$@nM;&2s{p|!5+t|;w(d$f_Ri!wu{VATq`xp73B(|UxSmW! zgZ(wRJbr)8Ve*6#ut^TCADC&b`sjKa9DlK~cu%rUh-IeDQn5|R+I3bC4mVHYN38ua z`f`fX5%VSqP0wh2nyRWfyYOQ3S<=@h#<#ze#_$N(K~uL zI|XmeS8^#|Qp*hM7F_-`OhYAL@XjYB?X#0(#w}x<8Du9TDpk5PTBR>N@GgWh@&sQq zEm^O$RxG3+pi}4jrK@f_TLQW{@@OXEpd!1DpYnte;@vd6K51wpV(z|9naSBW)x4^) zvGvbNOx`!vs-?Ga)}LOG?)|s80h_z8YIXsUjVK-~$^^k^x+|EZjQ{|SuD)~?mU+I( zs6jI-4cFkF?uSdcDLvMK-b$|QY!M1p@JMHVM+~uXi&<8x>p7&h;n_(AU|*xT###7p zao}4b=F9#~G#PTp4Id;%reHcWwxZJ{lN@A1?jdVAFG**E+qJK-HTkE{n1O>x>`-lZ zMlYpVC(y9~Wg8O zc*^}vcL{&9HMXV-D{HQqe`Wx|{MZoS8WiXTjx$YUqv*-d+I3(pa4#~y?8gmYV;~i` zD5ifchA8`&?7w%#$(mwz-NgMk5E2-143w9{er73aVv>-C>bgF!kTRI)Fdx|jnS>fR zUDM}<_=caO0cO*E?461EUMM(VZU%uP$dy7dg#&qK-RK`aeU8}qY3|UJGopFni{2Al zTQb6n)E6*np*$UGmtf+@3Bj|!PJ|L|+OvB;9X)DONTd`Jj}g0|nsE8U%HFg0Z&qCD zrwrlzk&!LEWJk!hzI6*EzHMu1ks^8a){y@1^uyhV#g8EML4)7t7Rjis+l`YzI=1MuyFtIBcZ^xR{1X*d|&Qb%QTeNeWnao{*q*o|48PF$H!l3tsBj&T%TvSjC6 zsKb+>8=cGQH$Wa_cG1(NJQ=tloZ*UJ%${1yb4hu66MUIQHNtlLR3=hFX8luVL>yD4 zo_v0BrFY{%$mr_>KAw_OmhF!VY8h;)<=+XP!gKXdysj*Xl*#H)GQHVD@yS}Hp@MY^ zl^m1|&FppKkX9GVZKI? zb1%G^)eJmAgY~cr(76g*FP~DdHkq+(kre4#T95@H(x96Bkk(9-iNDuHX+voD*+v^aWi=?oa&MAM zb&1tNbhk3?Z!CPAvD>1k#7Rxt`#P5xEE~Q%p^+(rrC3FIyjN8792Mv+UaRFQ0%eh_jCRfv3gpaBM)+LWX-s>l(Mulj9iZCptu{a@W zb!6P5N~KKO7>z-xC?suBos5VZT)@5a+6q%b0d_>n$kq@dC2SyNb?+(^nVO=jADk|% zB|^w5u5sL}hSzD2LKE3q`L9ZyzS9GDOSac6Uw5z;(qwn$CrMiQH2RbzQ#uTW-(^)H zZ>_veCF0*@mE#&?nmm}0IZK(GiNC8fM(+iVf`Fi-Zf}2UX(- zM||Q(eO!Sc%p2XsZth};QT>9n2t$Q?JusV(j^?bd)?~=SRTAsh)LtW>XNDr25q zJIGabx9IguW`o+#g|w&kVp9n>bqqI)9{nSPAL9^qFCU96J=bN zbCQmkawVpqQaHL8NmWK~k94*E>7qsQAS~q3_Cowt(@NDwkutE}J#%(*B5M;vt~KAS zHD`?_^2VynwvVFlh>q~aiMoHK_x64NX?lMya=tGqLbX~^+zsdgmlN6P#0KZN54$fw z?E9cC#IIORNuu53+I0F2%)s>hG0uZcPs>w^=(HDIWKSAJzC8G~Uc|COa)L?UCFZaP zM9Y`5n9I)(%JsCAfv?KedNPdt2kupsb~f~=f}mn(?cJj)1zgYFn1+n?3yt$fx{rag z{sD9xQk4v^W4)W%v{*&-%|8V81*nJ zGR-oujc>}8fhzc5oNGuhq7m0u2B#iXE*VyKxJ!_O<$wj0Qp~dHCMONVZWBiH0n4cr z^jJBeAkuC~`fl*-pZ@v#>Fc*;#f*QIKn*SbMCChAQhwdU%bN=3&YaJ@2IZ%>h8XcOqM*5A<(aZRVkl;!5gW!-`rPz)FPfk+qA0Bfl1wJOXJqc`Vnbt(Pp1dqwV_UtQuR zpTwtQ@`{A}e6s$GvVgIe`;W6q;o7YifRcK_NFq0W8_-mZ7|(I~%0U7eNYIF4r=r+n zYK){c>Nl$j19j8ACWd`r(pmR3*`kXIK^#%E3AUv$7Lw86Xkh5Tn?LNLsP3<1t{~}S zi3mGt#%btS3Q3hs8!>jfmC=yfgrk;x3ZQY#p468Vn5g zzabs0v$%fWvW6U#KMH|fAXlalmt2IBOxh zPz$$cj#G3z_G-n*j5-~tC3gZ=8ovTLvf)ndKL>vGKU+(AW#;$k_}2Z$i|0VrS|h+Z zw)F|2n#7lb6L+Vo64)t;Y$yiEvvyZUb_La%<^rn8z{SzLVv9GmN9QJ9ZN5h&t--x} zOn@{VqmsXj8+yK}_4-M7mtjm{7UOzCBhYuTT8@qEy(o#13-c*v4|Vy+Z{tbFlS=0} zwbMTYHf1#U>g0}QV;s5KAI8`HsSZ&Ylf1lsO}PFe*iod7wD9O@wOd2@;og!ZjR(iU zA0Hd!%d@z&7glrd(aP1l%$T)%UO~=q<|R95px-#xvFxk&6KUQ2^0cm&N%7?cLh8_1 ziV>4cB|wVOWJg+kc%>D=?9Sl5PAy05^;IfwGf_aGmrJcNO2|VuSP~ZsVE^fHXH_Q@ zrBpq2Z|*){b~AzX0%ZYs)&%@SdI6SGAP8ad6DvgCCcU+ITZ%t3`?8h&LtrB%q)!EH zaO{Q1$7y?4%r(Up9iYINo3JcgKqez@y0i`$rj8WUAcx7ghZ6p7X7OR%PIPq7P-nYQ zHjQx82bo$nP*vmKSdh9o7N6Y|xc%d};b*7sIQFj($l>#h!fw%FcRM|v*ZtAx7-K^? zzU)vtU3)3A+Qtq!*Mdy1W*e(7R*t3G&5a){_chOn+lJNGlc>~_@r~k(tAuu&7UYsk zSiWa6Aub>DvwxU!mG6E~I`BGk z@bBVl`=9R+aZc%2>%W>5DR&k05)T4#T_{pML_3kyJWB>??5#w>yQ1KCGZKRCIkSQ$=gG9Y{O$|jCVRgI0F-<|n^mH6xXEb!&$(YNgt z`rwppz2ce+avBiL4RR1&-&<2UToq{Sy`F_bkE#CdPn!fD}6f1iBg zVh#Ukvi$}q;w8od=M^`jyGD%7$@b_3{&FH8D(DB`h{07D-hpd#b@cECg?o9gVt(G7 z-+=!pg-&W}uyW;C()!|S-HRCCRkI~G z3vc#34&HeO+^NMMUecwNx!AzNDw&w$eda&q*Q_d+83cT%cfuU+-FiS*{#zU=-9a6# z?WrCBY*aXJe#5vAFSknf*dEGVJEYYW>?WX)or6s%;=L7rz4wJ~%Aq9KZclB2ft}EMB5N#hJk0x-fQPHs5#159s22${BN7L;!p#-_vEfN)%@58*=H*Lg%Z_#eri+&up z&-k2OVe%>egx>~1*Z{@M{#W#Niz53F z$FOG;nCI4-mqC1-`QCg~A}dO;z@_DEI>ieiX- zs}SPbecZnSY*Y~&lY=p^Qkz{{*&xb|KeDsuXg@qvdH81-{UP2_?A?9eB)uPrq2Tfu zujqyg_XihB&1+V>rBl+TJn9RaMKj;rd5*(_^J`_-<|{A;>4tmhU1Q%u1E&{;j%W!> zvz8W1tHc3MIKv9d(Bc6B35*$!8W_W#*qIcRziOKPyEpbh_CL-HZUC9Vw{bHs5=|Le zdylXoObT;`p17@Sz8$s}^{0R^LYJr_<32b3GL!GboxdQkZB* zSWPF%kG(4I1oxA%R$DZlT913yHJ1l$kS6ZYatVwayw93FEakG{-Aj0*6ZcLE43Qc& zsEk;kxJ0cTD_~cv!GrGEo#%W$bmHC1D+D`r?4=$!6Q(gPYhID@$FgqmBg6T3Q49KC z8n^KyQTK{Ce(vN-FN*J{a9wh4G}n_#C7QS`R!DeNER%yQQDulO-*_sA2`Y`{Oh{t6 ztcuN7yMda)uAo6XR@e&Qh`_FCJyHMce>);}XnVhO8Gra6moD!V$4%XVs!r9FGeQ5@ z%B*z2?zT(tDXe%Ri@m`yhnNc1n@o=PX;}(?x2!Dcsd$9Tp&vKejd<(_QMC0j#lrX0 zKE(UXG(PIC-S&e3hs|dP-MGV3d3`48&_Kx7-&*rG>Ar?x3mzjw%jF24T1=RNtD|9w zWja~jHK232VVIcTaEm>C$}nrT?~A;0-ghKdsh(h(F6NVqi8wvzvE@kH6fkqT@ed0? zp;*XIzI9r|`?kU0TX6@jBF2LSt7GRWnDeSF6-*Cld!}b8i+P!lMo14wKv;ID9h=%Q zPs!gi3t#I35ko&Z8_}C-(er|ArkXT1RB_N;pbX-CUQ-z45nfeIKxIal|J28iQ2*%| z=la*S=(fGP1R!xA{R&czrK?r#t+Em}S^O_ttrfu(m4&Jx$bK4;?a1r2_v}mebV|${ zcIw!)DZNnRXN-`oB~kj6ecutKyAG^*cJ?`CC-L`!%md=v2#ff+pbz3k9KsBI+AkgL z8$D8OP}IB!deo4yDi0fpGQHsvbg^$It8+Nh*o|#0wPNe}z2BdYa_Qwo-)Ve+*U2q{ z>$~+7Sq}tP?QnBA6Y2Y~=1W>A6eL6y_M^kJ+HPfL-6FeE3@@ z#{VVgo4BuG^P#Pr7hq^>d^`)T*jJUnxSYsXPN4WCPy}wR{H!%@Vo>bP4FlyKn35T} z@TE*o$8p3W>#i~45gy+|&cNpnxo5z-(-sQ!VMVCnaQz2a#+pq07ikkPpNZv}5-=tU?k@X%fUGms?fkhcZSs=xX8ABncc9j2s0sai6TA@l6>_ zB~Z_YU6QJ`h{*xuyn)QA^})h}A)o6S;~*)70~6I$zn(+E^xc|_?dV15M&uQnDLCnF zKcY(#9Rt3Sp_W}Wnw8pck?TRH7NmEtOy@$NdR(~a?XiJcvRNQ}> z!xP>MCA=n!p%}?uFFWrQ|B6QKbtlq91ViS6WkDn__KtZNAB!d!cILgJi(+;ALq9e& zw$LAed|=7)1!Rg)pnxOc=s1TI)pf*6(jyclGO zp^t=ulQckX8u9FKpzdS9(6#_z$)uMr(J>5N&=@nK>WME(YS2{gAA6V@qJLJ&jwAQf zUzZ?@TO4hXN%Rc-#@}n$I&(`KF2x$oYA)2-Fa>Dq?pwJIE_0NimZ$BVd67CY3Z#+J zg_nQznU*RCb+-}@vh4&5rhj(DN zh74*vU8#kEcejh8*wMWi)fsyqLf+Xx16xLJ2#1|RVOr#Jt5>V~i=1oxGDb?Y?56PU zgWxK2FaIOhyH6q3v)|)>4RF)llj;n{l|P*VkGGVlRv95=LSFwr>Et{v5@d%vlL?G28CosIgPg}BT!vYc}gG$;;7VE<`w3H`?nuFA-^s2;`6F)S>USpshL z-Oc2z&27bEf8zkuS&*O~)FR+*3LMHm3V4+56;9BFFmavDe*cZbw4zz1YdXsZRL0Iw z-sONm)*NyWB%U=M^AOYt4}DyaRvS&dj7+7o@a9nis)ep<15I?O(kAKaHNk*+AHSr^mw$-tw;vDeYh)V>W9%uNBM9 z_n%IH--$;|uV`-=Qxw1K#zTLR&#qLJ?ciy5;o9YoQl*NEN$l#qxMnn4xl zqg}HSm$9gLE${FJZ-#4y8%acnxI`u$(@i8cVeYnmk0H^`Ht7ih0(Or86k1TmCDcbg zED3CMFV3(UQ?PO8L=gO0`&wGM}xIWt!NEHtCn-48?g%a{6aqD zv?erW5xBuSDq@Q-6B7he1zrMkaTIou)0`lHo0ti}Rrpz*kI?g@1*_h3!C_;YBUY%y zO&tRw7B=~Y8}@K=k>>-|a@rj;rVR2CaT9aqYx3S8-Xkdc5tLts;+C0n$N=8&V^x)p zdEMwb5OCFAiud##b=7}u@k(b-RwP+3QU)wV&!y31dR?Qv#Zl4-1IRcP=s$;$q8(Fi zb3Mu1<$9aPti~n|YDxO6;vPRjnJL((71K+dEv@3?ALF5YkU&}|br2P8;yYYA-YUg* zYqNPEqpW{m1Pp#}h^V^e1^zzM>A6KojoW+xB!Xb7XasYRMC4|iA0-xkOtsWg^g}d{ z?3E#DP9g;4!LGc{%9u*yA}3&sFomc%=}^eMHq=JjAVt*S9Cfrbc+ z%fWkLhGP#$e&g2iF|t+?v+&(9C$L7Z6E>HN9Iz~&5#O7Vu45kX<86AyzoZ6f zSnJ2E7Wnu~t6_Yn(}5PV3cCARuYv?`Dn5`niE4}rLv_?{bQi`YlkycjCiJgniH<8?YhAX2yFAmHtctmoou~VfNRX>JCX~4bpt^$H%=@;>)Of+8k?dHzesz^EGJcuK?{SIsSv*q2QMXu*ier_@aSTw-CU7G zX%6OTYOO(r=PJGYCA3l}UM8QiN?()BNo{=h$cB*6Mo4RyaUEGvwpLIJC*8xzb~^Tk z+QIkY#M)~o-K>S2NP3gMxP+&@Q*mW0iJN8npIjIoc#MTTvMu3+B4)K_E-(ie`oqU_q`;0(s#P^JYi#&b+21LQ8$>t^wZLE52NUb{zrYWMh?{@KLvoqlF{SpQye#Dru@5PuWB z#u{bHuW`u{JW5l7zboceq}u|k?pkGxvUQ1FW9$V~1fzGPUPUJ?S*@3y;R*Y?xFCHS zms>igTRiG>DxSk&F-aFlcOftBH+|bEk0O+0@+!scV6oWeHk zMzERxld7$3u`MU{?Z@T*#ZMMV&F==Pac%|m)4lI}f9wg??VW0%x>P{qMi!2Q9WUN& zo9KK}yIoyj0L5;3GV)?tWe9i?+!D1Nf?Zv_39e$SIcdg2eBWSyQ59?4v1mb3FIvw= zt(K#Vw&HlljhJd5$GRMP48}kxBk78Aro+cyT&g-l{AxKZpLJPKQ;a8t-sm`_;Q)#a zt3!>nx6kJnH#oM?9@q5&g9ygrTQJ>ob!_>!q5!IN3o!av9!LQycx>W2t48pVxJuh| z-E5kzMOqFW^;j)6oljKfP0WMc+e za`mQ5p<)4R%Zm}IcdF65qY}iyOw|_1T8(Y|f`1m-+nl|WTiDoAB^Vj{ye*ICtj=F4 z=a%WfDlgd}WX#seHp$3}e0%rJ6S21nL{v-a@x$RYlm>i{-eF=@-`QXx(<9mxb+M+A zG@^}8FwL^@0=og?xh*O!;vas`8f#=wx4;;O*cLA6A+)^R%xe=K*-1%dVuVj*#wH(j z(8S1=rdU{ibxVAtALKQf@D_HjREzS&B8X}d$n%>LL=UH}&V5d&kpg)3M7bXF!eTjC zImx#b&(+RNu>(m#9g7QhQL~}ozWCM#N22$dc9nSz0}(fhogS{rvf}#o`^VE>#NFai zC3s$ftmVxedLo>Up0ksP7ewc@=%RM`%?v(GvtV})YR15LwJyfa#bV!6_GS~~zfxVx zZb4~dD6^(dao&Fv< zJ(MAv&Sl_L+zLm)Eolj6)_RsIB_-2*j=$EBgAkG7fTeo$z;L;A7T32U&kTdKF)>&( z@A+Y7pp>T0_47cLNQA9`b`wn!yOuX~X3bkgfR(4UFoR+TcQ0o$0c1MX37aU;ml!hE zt1Of7%5-tfCzx`!NnH03Dpdp<>lJrm?BvaKd`6Xm(ye$pAR1t>qkGoMXIHjH`Zopc zuU$0bm6f8o+a9W8KcE=wQ2ROf#wBj0j;*qdLo0rWiz9umvXjY4FfH|Y?!|2n{Lx3Z z^dpr_p$GS=C`q@tQN_UNjfFZIbiffiI_`E8`%8h@p1oK*-SL!VNxOmbpD}4~uNK=( zczIV|A{B{bL4!i`d<-Dlt9>;IP3NeZl@|QLrr{x+lVXK|q!rFYT{PQa>Sce+IAcMp zQY5Nw`|BHLl^l#trWJQfdt`&zOP){|LI9t$-stBQ`szoYN^IQQlIc(|)D@<)r`>75 zS=C~tTNL&x;dkh%zGASFs|cHTnHa5SJJINi7(-BrTrp4k{iqIeL2DIfhi>IB88Cu3 zS<|(D?Q`~CtNtcvvr09d-%`88+BAnruhsU8l1FBFGFV1xsS2E z{`533dayE~&sUpE%g22l2{wfB`f&cSm6tQ|17}C&25pLk!XcfZ)YU~B^TL=|ZNGei z&f*y;+HAwcJbkiJFnSe*gtYeTVn3;Fd`Qb%yo*hrNrd2;IVg-{QwA-smQu?%3TrNn zcI~-KyxTLWXS!NiHwLbmN-}n0cn=4nY=X&z_+~=J8zRx-9hs-J?aohSvb@e}|xPXUP{|86;2Ywtb zniV6#BdXb-@@loTQ2M!+f_-nJE74#{*=TLM#?qOdyj?JL9KUw=@M0FH(9M;cUbyh| zp4T7~wOfnR;9%{T-aAxZ^Q0ZR=;2~X#cAHNae;3OFY3Do8?Bty`j(zLHM=!b6~7^4 zZ^@{J0y#HFc9`Zwv5Yf$glVUcg1{0GjZnTr9PV`?a=F^i3Z8so$=Qs#_R9}XnV9ib z61~7iw(qt#Wq-TRTK@QhQO7_sGYDM?IMtW0-!3j`tp5b)v{BzHb;ai@u9q%4LEO?y z&J$!mdsAv~gOf4C5&C!hC4wwA;r0BEChqxU@7|FMrpyJ5Az9~kfF1>BoVxBM+D152g*ueUWv@|5iAA-!kj^y+Xm{%;CTp93zyKN9UajoMFljvHf-0PCzW?x8(Q z^Y-m7v3(MWD#;+7y+497_riXyQR-x>)xPx_-A_AwrmE=C}Q z^^S~%dcPpV2%%+YFrb6BG+$B*ArF!yjGS*1$bm(bP4Y+8(RVwqcA;m6vwu8mKcm)5 zfDft?BL*{+n%R*~65lgR($3l4x_HpicGx2}8+y!xw{-TqU3m3*6YBii0k+V(1oy8J z^!#Ig!Z!HjuKhbOw`))*a*t?a{QJD)Ts0}1%KOl6qWMmsrb0N!IyIrw99efbR*7}b zhQ*&;XKtuJ?IJS)x%pMRKJXarl2cwol7oB>-R2*ll`yy$POR-$83Zy0YQ4si1*t5w zS#h9|7p}IkOHzoAQyUME(8N@Vlx-%t;(I0;n7|s*0^6%0!l0{(09ZDe=8x;UGgpE& zZyW-mqS#wB2qn`6Ds9v-(dow}JcoxPvbB^G@|9_$Ez4qFc`K+(>perZwviAAoCnp8 z%-J8`MD4VJdd|#3>@gdXx!@85LSsEwOfF+8AE1VDo z`YP7KGxa0ab;WKy`;St8uOn;XdoK>=M0H2lq-Kgb>Yw!&9O_F=|C}du*~V)mV(N>w z#pNK?mJh9I8`J!I-T`$5?j|vf2QIQs{6)#6W>HT=&SOU~xOhJ*88EzCayZxFVD& z=E6ah78KS;GPi1!Xh+fin-(*XuOIlXm8QcRl0gqh)h7*$#@C$xL**qSOS~1iPRvN9 z5r;wY6(S{sYlKwjEdq`f9N-knHTFmKNLp=4Qwsum_dNTjAchNvH>r z8&&Y~R|UYrr{grYgAEG`LCY@qK<9``gex z`dB?I1Wi6^=%-l!@8voHA+H5fU>)^rDyptE252kGgVy~YJy|F6@fLsEGh@Bba*w`b zl<-bbUD-=p-jHKpaAVE6IM{K?!wK1PZb;@J`P%@X_0+}1qacw?;NchTY|6Zg8}*a! z>FX!PZZ0M?NTy`_kmPm$(saI~NOlz?m+yqWhJR$b6@ zY@Yql&v$J&!g25m%;nX1#?^s0jBhGJB0cz0B#E$@e0zUS%6t~1p| zqW+fdjOmDUn{5#^se2N9hk#OM!Y`x}ypvD)78FWazRL?mkysWqqMfD6v*`EyRD&u#0#3~1 zA@*AH%3dz`C)2EB9cqBSby~z(9#D+7)T=b>9>LI^#V)#F58=mWctpkTKuiSGUAxB? zAefY2!Vehk6BDXmk-ZXf1MxWnu{$k195u-q(NX9^CirZeh+C1hQM(wLEJ&9!^C*Oa z#mySxX2XLJp}CrWqAN$sR_r5GgOA%>(dp|%pDwo|@6GMIS&uXue8x7uA`aF!Q$jPN z2FB++t_L;x(eq!43f1r580B9?yV)do#HR24V(W1-APmtF<)9k804tYJ)rq^3sStD9 zQA`ilmdG<0|Gi*Gu!l`xG?n{(+pBd+LrW+}`T(lXC?+{~4cfk8PFUB z^bxTkJegn+iEcz)r4Y_`>H*Tuns2G(pylt#ZE3Q;e5-oBu#T2@vSZ3vt+AY5ue+(G z{gRd@xfX&5&OGHzIV!iRE7S`B<^M})CU8X5>=>M{Y}y#w2X#ubhedg0%YbJ4QH(cS zBe;GS9n+NWa5jtrjItMnp&O3NzL)kBYRm>kGu5?cAc~%En1Xduz+EooHE?g(E? z9Re=-8gdDiNKvlKT*tw%f6L;l`vS(j_`>;P|Zsrm(E=mOwHN%I(s$YP)g0^ z;XdkoBy!>O3Io~&Df`KrsHHEh5`)Yw3%WgHQBU?&$(B`PfXsXy)v}z zk5EXN%YIbzz_!~d6HtkFQjj)g2-08slex05Ic`Dyg&LMyjEYS3)#w@gi{Vn)j1X7irD zu}n>U8#ITCr7W}_@5-Eb(>Pcr{~CLZ0z~yJTp%w1j}mpj9fdW62I#nA9ON1jArqJN z(~=ppauD>Dj5^=uN-&RSCYe2p0H;&yPsPBF7Qy&Nd|=P<@aO|2ej$X0skNOH#e{B_ zhagHLpiS#7@Pn$tL#Hfb*LCMRCw8YRZuT>maO|O|4}u}@b5^&_PLB(l&N?K{y!u;! zuzVwqe}7S3vCwvF5o5{XOc(N>*7}+{yfM4Sal_6x4eg;bM?ET!zRD@rxui?O!>98v zqDO#~R+E6;YdJ&>!bd?DZntYE4?6=o3TwgaiIGN_Ct#ZI>KS9#AXk|eLF-ToLhpy3 z3roXg-p7b~yJ~uJA2gbqshl=Qfljk_KePcBLMODt)L8A#l2s-a|A8Sq6^+=%gWDEa zzbWF0k%T?Lns8*nze9`8%;y%V3a`S#_k$j5KPeZ}v>(E_MGK=oMDE1?)3J?yVXVy> zuakWndR2D#+F!6-95tqKzy?hliwsXQbW5g68PS;BskLdR>n|Is&$>`7$0CU1VQFU< zT5Wj-*|`oF73}3Cb<}Fn!)HyVc@_Gxy-TMh2D5JwF$iII%eWHDdA^eKjNfP|!WH2!BFHI^Wr*l7!|$0b zh(+LwpndCGQvH@+#N9N?*0z54v5QkAD)2-`oB)DEnEsMx;C^K9N;y>4o+&NsnS^K* zI~uKyn}$1PUY6a(LjIG-$uLiQGjNu4UL=(XWN{sN!YcLJ>BmHC+Zvq+UJH{yUnl0{ z_C*+yXCY(Hzu#qMOdf4Nc8B*4qK7}nx3QJIxPb0hzcX}v7zZ_MNd3C_MR29(%4B=eXIK+|HY43 z(rWa2gj2dzLw~h5`(<%6ho$-l?x&p)_eW+OdwOOX8y9Z^Jg}b!52t0} z9p^@SwPn9}w-oz#s)yc_j?eNE*n$HI&K|^H9!F$1nR_r@d#z8@413$5uO0)!*}&x6 zVGST(Q!ITLIKQ56ZpZS!YDEnkw@tYsLAv1-2OnAPcl8~Q;b*qB@BNBS8+-==BXa*y zHK|3V{#3uTd7arB8eo$^)6Riii4r{+F7J)NRdn(KPGe|Vt--Uz^2R$yVp;J{1g~%= z60BH6^@>Xc>~kzd2@uYO$XB)kN(qOOd&O7;M}$@H5cmH}zFYdn?4OY7DAZEuy7rfI z>M2f-nKXpj&xoX?4`;=D-oujmc5h6Qazo&TUH8$r z-r>RC8~VaAPY{b}Y(top*}q|J^~cRL7Y3u7`(2Kv5AA^pc+=)YCooF;XSknDWNgzd zH(7DxO@|#wv@7eX`A zV1ka_{66!xcV6fXa6^H(wD7lfTfH01oERU+)CI>_I_hzb8RnO?GsGRQyJC;kqf0k@ zyUss;dID=#pQjqvyc~;ubhxF#HV$&Gk~ivx*Cjtdg7KM&Gps3HHewos4GTJi-7hF? zieYE<$MQq4Gja2?N2yw2`mt)1K{K>SrJeY*F0n!W`+nB$!;EZO{;xskX&e;c`~d2AIpl2ehG3n;DD!T5rLKs7G;1$tzg=(WOi?z2 zTe=EPi0r^Sb*O5!@k#bYkSoJqBSH;p<`M!k1=G#nBDpWkCGFPamc+AAEi~W79)i;d z_s~Qc2U=7{HK=z3`jfW2IznnuC$6}^`cv8i=8edRWBZVPg!=!%+;yjA#;?4;#GlA_ z@*Ha8Yx-B`ivPzkmg1R|dulxDTtM9P(1Y88ykgC*eQt;Y>k`&m7eM=0WE}k`c%Pg% zA6A|j^JNL@Qf_Cr-A)Ljnms2lsm^&a$c(8j-IR2veWqcutS4|#3d{o+Gsa%L(Vu?& zMg3@-J;{4|(+=pHU`tP%kB2*=Iq0HR4;$6TX0o+J%F(puZK36VH)j4pB0nKo0CX^z z|A1rdXsbAqM>J|l;!lUv`qPP5c1>mrM|`A9n;{SP@swzfQw-AAaSTRC2-<&-@?Mmr z9U}h<(9FuU1O(6B1zk}PkjX7+aKL-{1bPu*6Sv{Gecqb6cWvxBYUbE)*lT62w6U3d zoW!y0c--+=<5ZMjzRqadPYsHOAu9JWiB{q9RI`9hIfXKhBJly)ho)Y~E}c>Q-ZD6N ze2%sDMfA1d`YTtwZ3Tfjc0y)q<-x~W=6xaNNDf9=ku2fY;kkAP+zoF+G>0N2a$~z| zgjpaKc-}$pi2Cm+HGf`-5(|+Dgi|kM*PQPczHLcok|Zf=tKrxHJ1tp$em7X zR{`DX`3A6H(|zg%jVKsG`s6u7wcr?OiZ@Ax5&FI0n3P)mqX!T$mMxkHGGwzC+P{~+ zqDU#>6wzJcHj%@z=mFArCC>urt7e1pkrpZMTL-Uuy#Y~GM3}ElM*sN8^*A3>Ul)qtjkLy@+=2lvWSbM%KGHh6i-fVG%dt>T#Ef+gV6M($goiUU%Bl z_xffXFATO(xrT~PP~MV|EMc@X!nfxk^hcJX@HL1&M`V%9=_j}Vk|j{>NKlF-D9u-< z#->{e^-T~0IezYR~Tbdi-jH_9UnL4{+!Q@9qBK6p(pu z7VM^W3siZ)I955GRcL*E1!**muC{^~M`Y$j<1#+y+ZCfNje|g#^DqCG?s^mwyP8e{yw%a`D;(n?)XqeM(OCbz* z-kz98X7lYgA5r;|MJg^|!(?tfvNFSeFnUhB-P&av1j`{hwjmy6PQt5WRvs=(y2kLMHPILSa(n&M?g=+xH z(8;cWyA|63iS~W?OdHRFG@m(^GY7%O>=q^?cD)4Sia3#_Y_tz-QVWXX6ff&g68n=pV}jy~)2igz2vL13wnE7GH~ z>t}e+(k(Z#xf?mT=l(8aW(;^`b<~ej?Fqy2sodBLU0xY6d|bosn{fTWEQDg1v-M{c zlCZZag~t+*v$j=)CH|up5arTL;rzo4Kdd;mqN?3{^LnP^+ z@PzX3fyvDu-l2o$0Ngh@MbGtas45wikC&r1PG7ZX0QELLG<7mw5xtO)flA@9;yM z@rwK05A7?gRvN0PoD_nGm$96{De`&A-0<+FgWIv_VVu+HN1d!2K{R~YNokE!n2b?P zDDZR0?cxrtR;BCJY*`h}hB{K(N}FTV!%+qKfTfal)*huFp_%@$Yjo)V3J3%zSgO>* zLW?Hrzw-Vf=kF9EE_s?PCFp`pM9qAVt^Fokv#K;bhs4}k_Io=2!QIh9- zNs#Mv6!Fh9#7F!Oag}&Mun1&0qH`9E9l1V!DqKRktX|tv?yb8ZtWRKKHO5=rkF&QK@4eNBIP* zl(?b~LlF%C40F}vcKZlc4I15%s}yurCKe3toF9PB!@FOGJoqV(Ik;NITsZgPUHc_phy5vCE=_IR8bU*0pXOid4i{FT zKRm=0b{IqpEmd&0>1nx!alLl6b0ZR%T3_0$VY*Y`^Z^yYchst$u#St=n zBE6h4GM|NCAR6N09_I76WZ(2@A_&~RmaM?UM2gD$TjRppQ>}^FX>pT zkUt*|Yp>`XC@9>RZnrP)^c{xcQ{JBoq@mWu{zNar7+cs=YF8Hz$;xP@`7nVSs(i0b$#w`7=@T*UU-V&SXu|4hic!e z9?57XRbkDsP}5%fh!-Y|sGN2#3vUlS;F6vV^zU*J8X^BjOlJ}&VJ30-KR=70(qc7( zVlRw0xh3B+t+F+Xhpa3Mh{K}@Ih4GA$XWJ!le{3K2S_6zBiZBnETP!gpO-F2=j+!) zCf4cQ8NsQXGIa0^rtej$fKy=Tra3y6cvV8RRCi3_#9>b6}{)~0WNcPV~Q15&bsmTv59ZdnJmm9 zG*uKgub{MmsWhwGH*2x<{Gw>Nq&2PMvBNToxAV4&)8nDiYfSAD61^KDi?FxN`f-` zB<&bc$E%$2*U@$$u#;VDP^YaGVg}kdwRkk%klDNwqS(@L;oLM=BI)8PNQ;(V1lvJm}kRt}xbJvk0tntl4dI(&;MZ5Z&EtNo(pM(|&GxM$ONMiE1 ztLP?BoQaLMsiEn7m7@Dl=^&ATSf20RitbGGb%=CS>bnv}JzCtn8@8vv@EZi8IM;bKWuzya!L+JZ?pfF=I(qKyF zKS1~uy5SQ>`aEDIj0N;Zw=uiGYH0R}LJOnRk~mFtOuK zD6OYXNFHb*^l_Tdh!R>1Y>$q)k1^z1b_kZD(CwE%MS-_!wgW^)mj4Slf@917qhNGc#6DxAQylZPr~P5v;-1a=>R z$P@yKEgXCOJ|vW0m*J_E0If{sG`KcPO8s^2#GWB}3+aIq1-5{nWSipw@1EZ@-ibf3 z#cetPw394|(Fd&KQ$S=H2gYyKaZI_Qw}??s%U-Qs^Np_JdstEnL;42}uebE1|6G^; zh2~g_fG_TUYYLW@T<^X=8fH#r-ek-{sxc<@);t}j3XC|`uQ(E0e&L*DovwkFNNS8;8kVqxjUM&{z`S`{e$S(4wS(&KHJL7=qE(<@FnvJ zCic`KQNvbr^LJsy!dul%ByW^Pa>nCXqDXT<_T(Gz3_x(2sv~)YQF%>$o+^^o@P{+A zXy-(Ki9Fxptk%mKxkfo)^I=&`un?aAL+us$+|E$G{Av_J>0#jmBr$7r-0ng?N>W{nN!pRq>+pDEZMq=v zk;OB-DljFraf4j+&s_&@;A?(f?zuDTzsE)RR4l$+si?1?by<0rcB}7F<|B!uULeVM z7Ys>5RHrm;%+*2u9vfo_j7k-aqs$H$2ba+%X2w>(`D;c;F$wJ4l*mVIiL&Urom zHX8TK&H~9BuTp3B)Y?_?s34EBxnXh?-47mMG^6i!YWLoo_=VGa=W)uq)d$vl#)kjz z((o=z6h`n>qgwDKZ}m1o#_~Bxd&SpE`e#bE`Y9!ZAso#4|FJoSk!4pw!8&5EOMCBZ zxO`pzhk@-qtD8~Y*iW=J`)GNunpqQdHEC7#pw=L1Ll)aM`Br4@0;XbQh)>6* zO02Cvlz*5CH$NI{%N|{PyJKpxSGthfyz;jh;xY3(D>*F$*zF)<7y3};0yT5OV6bVj zFF*leiU?5Z+BA;sG7F|jGgW`ZGW;l)9KiM6*EWv2g8R1ywc=E6K7qjS{i>FpMQ?RecB{5O-VS{1r zLiq3maI$&|Y}@Utt5eW(R76d(epf0?QdRH+1Jo1^usGYGsUn=wzD@z;DP2hnX-{ijqDg%gDq zk+B2Cp%xhBG5(rNwwBs(_VUQ9b!&9I`Q=Ys6y6|$Mrgv2sob&2x%-d=S=7#*6tgH3 zu$=Hyoo^~?7SSj88!~Fct>YW&K%k=u8*APy4nyWijq193$HEO z!4t`}VW(6xz(YiW?U90cN{WcJY^f7?wvf=Y8b8Wtz|uzre`uoYq_C)*kmTV8hu#m} zf@xj>!8VtXO@{Q#QKIzwx7x~voM^t>pT2PqcH{+)F`9t_1t%@TbgL^mXh7x=493@a zZd4aVxpozXwbGpDBJXx`hsn(O=G*Y#9x3HKD6p|ko%wn8NGp0+v&Ha{Q&v&*!LsXK z0ih~&G`#?Dvw35!vbH|HY(y`3DobDZ&N(PI_1C9IKR@X zr5mkTU0gF=%LBG;!-*t0$S8wpLDroWjim`KN1G{_hQ?`?oYn9Db-?tFgj*tw_YQSS z$@eCvzyqDe&sOHgRY}EFhY)mKT&-g}1+h`Wd$sB<7JB0kRXf@bTa=?E(}qsU29>BN z^CA(swpA6+d@RyD>qBvNFj_c;Y*^Jtut-!(ZU4?m1$bs_(pS$E76oHX`2xEsD(-nT za+RgN6K$B}$Qggy>iy``ua!s7qu>5GGE6VBLm>9lrh3p^v|9{Kt#9t{x~RB)0rY)l zoa06P(V_*KY597*_F)I(-={pkbWjMz7n01+O5FXe-IbD%B}P1zQ22%Y^}Z+Vn7t+t z6Q`o|@Lh=T2_0JzjRA`~2(;~qNMg@~_w+gZ;hOD*E3sC;E4EFAQ!i`W37L}ekBKP` z&a+FgpUu>DBM-==F$wAew(nskl>OHaV+G?doqwvSg*1ja=1GDW`E|j$_PwE##%HjJ~eO+H?sjlkV+ES#WKkpjVb^Z7`o;Lh|)7ev>&eXwHCyv_ee{afZ-+N5~U9W5{kw1eD(0}>% z$9svh?38`9x|b2bjxD)^KG%H~kNjIJ2*ULQjwO;qQlIQ9g$%ft5;kuiS8rk$>EnF< z@_9+;1yP*uZ2jV?l+W>kyQ=rSBu0f_0yvZfGR5^O?Co8ny%u<9Q{l|oqVN7!t?0cK zmK|kXV@_2--n3>Z7r__ClNcPFR4eY-R1WjP#N#7ZT=98=#Vj- z>RE>o(^5*08rK3^8@l(!`q3P|o7dM0cE$2%<;bt&CL-SjJvm-wi!jv1u>;Q(%;@uV zNVn{<@N#=6gd^d}ap3yQRbZ5y;i*q7N}+dd=IFLz~_DJge3+Ld9UZNN`W z>+_G$x-a@gP9ac+97VsV3KT7aJ~)2e+HnoO;x(dEeTXsTI1lu;b_)Eb?NPFfa*Rex z9^X9Wn*BKbyTgSl+KNa@rRo=YGs^WR=WIO4Qcv4*N zIQFILtNXuyoYs|e@q9@A&t(v$y*SSM*Fj%aHN9sdq`n~EWW2>jM4Kv1OV2emvD}>T zty)VJ9op1N+tq^ES6+>IJ;y3l6<|WoX=3inUPhghUV92n+a3sDI%?XN5(;k}U3>x+ zG#N0~1@uVVbUswki)`~S#H*?yPM@cVcE8kz5+3UuT@k!L-|4%xiMU|U35!U?SBW^y z4OIXGjLyt>CZg}f??s^1t$29akQHpi$m!K`CY*@QwyKMFmsKq5x_N(bMNayVwfxFD zkt%cF9Kw}XeLa_8telSjnb45-RMs}N?@YP zdod>EXZ;xu#o&Edq-;V$S5h@)lc06LP&tw?&Ixsa#0;eNVwrPDME3B<&NWK*<=ExU z0j#a|B6$#GS11{?wkmF z+jj1}vad$35mX{ZbRS#d6|Y@I-r?|TFjH3lp{4@8rs+e=Zrn>f(0cnDCs$KPA7<9C z{Ln3k)=wb_DMt`Q2Ssm@64BB>EOP2^H9W^FRh&xOE}x{6M4wGNdhQatf?w8o=Unc~ z9#`GwBrCu%Mpc-9Ki%&Rh=OCclXl9j|Keq^!!1(VDo(U1Gu^Ug-5Xmc8cpVOEmE5V ztoZ`*E@&ynhcJX3@g&l^c?KJU(;_^MvZyjFD55^)^*{-dAqpthNgG)L#aPl~H%DDRMnvc;cXe-4JtqHLhGVg;8 z(vnfm*-(XO!;x8qqqZ*+a&-EM@O18mF#CRbz_NavLw->(7G%A9_(k};PNgX~3o3rS zB1uiU%&iBnsnz z3E5wFW=7bV#!u`U@+w8FZ62<&CS%%t;oR;UK9j9}2Oe*$6|H5wLL~-?(7)cgVB%ge zTssfCpfYeuI%%z}UjeUQojU%i zm`XGswc~h=eCIGZU&a9YHe|uRv5zq=B*4CZrKq|MO8i`}FBpSQm3UA@-P4TEi7f=n zHG~(J=rj&B>#p${)`q=`Mo#m(XI0=9FM`9p2$gPH?0+O^%o76)3;YiM)klU8o*i}@ zy!SYs;M`?9Mgcfg8f(T79X)Ueq+0MP=-=P!NA&>Om zBJ~W`m3gz!u_T>szs>bbA*r~N_S-pB6~Yl6fs@i39@0CT6lgMy*A^vlCvMa8Yed`kpiLm{#_{*)@nznH@tYM;olg}3kNMcw zwF+(8gplZ~%rjsrpI;K`jME>^{yI&hP=VnySzgCZCn_P-2?OsLCGGimogt4M{vz)k zUS`*g+>W2dW`1oA>sD-kueN76)y~Z$H&BnO%d$dvjb@^Chlmm5D5*=xeK>Q|&#&ol~danWAfJ zy?bLFmXi)p81|r|uYo}<469;v3R-G97>; zbwc4(SZZN5O`&C&{uOhr!q)i+)eFBGcs4@v0Aa;FS7hLG9d@g9>f#8-mt#R_vO2CF zN$9EKHB1{o8mkmg>*yw_`RvEuqgz<^JdtvJ$> ze7#>!g{_&1%a|hT2o1BR&kjQg^M;9=>TUDl$f|#%7VXnzM2z$p%&rG_4m?yc%3Ucr zCczmw!8Uw#UO&_Q(v_>L7=JgcL4FXOf=u!K)qQ&MHmp%oZs#a|&H=*{0v0%-7nZ69 z#+oTSuCx`cXOJ~U%LE5O$=PstZ$#((q&6(upwrJd2}*MJ%=WB1*Amh~H{UCH{fO+P zru_O8@lMu}1O1@ye8MjM#q;uOuMr==nDb-*wrLl&*he4eioKctxt@_K=$$HaCLg}j zI*=3iWRmEH{b<5w=N*M>CezM$W%DMm8bJNAWp~bT<9~gjjrm6ofgF^tFRb&y7@B80 z$LWr4k`xqd+7_O{PgPXX`y6cVq807*a}*fyRKo>1h=7BB=`(=@5q(px>J-r>m!_X1 zk^5cUPQ`dh-sD4i63Kf*`9IU2H*c;Kn>symZ zd(thmUqAdC`-@-#pQ2E)fv>ekcS2pbn_xLZ_-n5Z0I?B7mGHvN(lU?A!Wc77lDG~n z+=4B9flVtX8l0tNYY}V_`m=ZG(H~rR^?vUqpC2oAmoQC>o60H9yI3hxNxgWQfcR=- zuc@bD>}bL|apff4tF&rn^_0tjnvEJv@#NxL-r`fXSO0{>KC@O{ATwFaV?eH|-qnRz zq*u^-h4K?o{FEb6E~%mMODV*HQnbFE^X2A4E?p_OmP@q?mm@qbJo|+g6)NoA` zZ(u5duXwKis?grP%b&Muo%4^=m;yht!P;v{`7+>7aBDy9Am%WxWMv zxw)DWL4uzBT*e9HtvV15$=?~XZms48vFFd6SC2<~AC2vaPe!SZV>~J=BC2pJ*Cfh* zUsf?`9P8%MP;1M}FOL;H=S|gT=IHGa`i&RiM2F7IYnj2|BeAs8ym9fl`3vI{81oI{ zUNQJ^(Cc3s)7d4UlSKhJ-n>5RLdrPrL?FituQHd(4wtm*ya@#p;j1W+C>ob{XB(q;o?FSI@aCf#AI6QJbdEMnT;ab^ z$o*4eW8ya#3MU#B$gW6hWJA6MhJ&PKB+ z1bz(*9}NDYnySOzdYAS#YU7@$Hsxnc>M=a?girO_Gs+Fc+u*jyi4#sPlu~;H+9AW( z^W_B{>oyFiR@h}pD81l%__o)`I8%_($TWyb(_1QuTPd2vhXg_(k^8aRP2#NoJy_0`!C z6`raJBEKhuXQT^}+|2u*joN1AR&byPIgY_YZEcIGW-yB$LSvK2= zom{Qs*{z46Ktbv#0qTTm{})>qG70!F-OF42u}wxLIA*m(Isw{eqDAJ!f2mkV3l2}! zF^{{_HJ@cD#s2!`Uk4uWBFv^u+yt6>Q=CA4O*VBWT7k%1WXj+}#2|8kN%$Ni6jx(4 zB~?VPfkfnTS(-CBmnK4ce4-IP-T&P|Qq!euHYSd%&Up`@uPZis4W*x)PPC+Yei@*j zm~(mm+>(HuCuK|>3sua;Hh2*nry4-zlb~a;09l44z>dhD9<0GJ#*2%Ty`{4EMW^qq zzdLox!{}uWPgkfutnmCYKW z>Ca%iN%TI2Gf{s2z+q6V;FKkPa~7vU7eye-fpk6l;SNAH#qtmUPVQJTx$tc34f%Qs zN>1MuH!i~5U5X;ODpS23&Fm?x8c%^`GFl+EtU^O=FH%w>J`o0LQK#M>vg< z$B`_;1fplWaRG@OXFRY1ed`GY0+u}jpOXDGSuJwT)~hWf$>~ufAHuqXs!^Xtr<|%S zKoXV!vSgiKQa|3`B>;zO(k;~h6Z#(pX|bq#h@l(eA!gqEc0mhTiPrLt$MV3Bcd*di z)u!==ae9|3S`eiLKdb@;=64$}kq@eDLis06(6e@pI6^lI;PG!Q9+FApvqS%km6YcVl1@ zB)XZGY1szdA{-OD#rEt54JV$y(amg&`ND2y}#9b>oc?@C+^8!zk( z7fb{EA7jjXzCDpdA!AMPuU7>ao1p1%75!VQh-c;*<^jSpIc>eP$)E2@o@BS{qZ+3 zuBVC6_=^O7z?FFBB_|NT{E32^|4jQJ=PkzFn#jM#9Nbus8fEKMJub0-XHSyI$U>Q^ ztZ#y6bG?42Cib5==yFSCnB(vEcF2^3vx1w8wr-|4F|Y8|14_j;%uFCZsc|ME7B?ME zI6FqR?W&#HY{-Vke;Kg2pg}%1)f<0$^CSC2K`$_0a*FX~p;aWARy^%+KKq3&q(Niu zt-4JV9oa|MEfz-e1h#T#qTlOZQ!K)?kR63RkT2q;ob?#E!cGL}o3#{|XtY`&Ia{?!yn7$3Iiw zd6d`uQ?+18oR{&}>cP`XK*`ko+Eoe1D`Sa~;vaY|n;t*HvUssPF9O!*p_Ih5-v&!! zVs;JhNKRD27SGpT`Ipy^YWHvVZZ%zupZPbbm$Rjtj?A?_P$!@>$auswmFwm}3C%)c z9f_A(PCv8nsN(U$<1UKo8#;OKJmflWuDs;W4FYh@R0Wrpw+9>TpP-UWH^HgO^?TwG z4Q)d*g#mX^No()g#e$`FBxi;v7uXXTw2XroY~JDBc48ON$G8-ZnV zo|!xO&bo-4H=ZN9nyoL4Q8DYzu1N6gn8o9>o_biYLm<4a0>~qNE!T@BREqYIb`Zmj z_t#|_irqxFG;0C;Ch_kKc}wm1C*Sp33b8JJ!7rJ&ZK;-C;^dt?!U|3u)blb@ zy+Wt`6jD~n|lWASn+73Y}9_4CoBpTSC{}a3~PILJ$=}YCSuy+ zM}eqEFoow7^rCVEFL3@tvu>!Ljz>!^fl_^o%&m!u35{59uW1&*##Pp%8i0<>Af%~Y zdMTJu%I&Bjo^+D)SlhmJ+I-oV$SZm9xoKr8a!vlKSj>x=A-p?JCqmY7*z7K^6NZC} zyJ7lzyz{VN@@ShEWx7>*B$%j=;R^eNQIl^FHEL|lF5^vbT(@N_SR1Evm!DJ117{wm zdkFV@3V-pZ-7mM##x*4s{)?A8PGQZ&yzl9DJ3MN65FwYi#U8ETk%JjV#^meN-e5Ij z?1gt$W{u(_zSbjp1J<~HO=}zvNq*rL+fy6Z!)Wyw_YCeL#Uy9AlIa;#gS~u-xK0Q* zoh~$RcGLFF-3(E@#yJY}1=54ODd;rCV^H%B5NfMkT28+ycvX25tcGq2Z$p7T_+vW( zPF%N?Dlek09`>SSV)!e)T6p%zvu!kfw{{dw|F$8nkgw*`H&qb_IVDt>l3FIZoW6(r zvT1(~wG3aCA_RRx2Vf%_E=IUNbHeRd8!%z=;Ne|lehNF{FZvyE*XdouWvS7(PFcohMkYyqqmP z3c}hGp{@_Z5n&^<*YeMISaz{MbG1--qVHn}FS*2{g{L|7h9UDx$0P9JMa<0clSG53 z<$06Kfz6mU%r^48=W!*^P;5e@;mV~Q0*e(p0-QMztUB9w@Qhz|>^{tZ@rm`Ka=2W( z?Tw-?57K@=VmX?KB+6Z{T0e^5bX5Lz`&0J6dXAhCoDKHw<`j{;OOD$|297+JZ({`u zo+a*h+0^pZET)z6{cB@j@?{0LD6}L|8Q1M5NJBbo%C5B+ka*{Ahufh#^o~Fy2VX??4J&SPvVZYt=xX>7C%imsG!~@PR#J+Te};N>enZ19+|M^ zjh5`evO8BlNmlag+n-ZU;b=QlVo1Kk1W>}^8X3b65uEwIgtz@PKvW9P4(ZLeKXsJ~N; zREs{bpM5Uhn9t1f`yG^dHcAv*SA!am(9`6@l#Ds{(<6?Xf8r6{%u z(^`BTdy#7qa#p{^Hke>(gY$V0*JL4mf%>g^e70nXsS5WP|0qRwF{L5;Y^(31Ybx0w z`LYIa(P<`@G`k);mU|$P{Bv&1UHW?Qyzy7FWOL@-mKKy3@Um>+Lt$EY55PtgZ6bS1R$kLxHL1I?wDI zr`;p>bJ~DlFG7Qg)!mJprpUcW(pTby4ch*Mrl~IoB}dYg9UU`O7TEWPu0X#Q|7Y?w zIx?@z1*&-Ce7qPpU_&TgwBYIBrC}qvluTS;z|g;D0^00NE-ceP3r>i%O<`MPR?=t| z-e^*z(qNbxQ}Yq&JubN{)g6y*8n6iG6r>}}BBRHcn7nl+P66z(FAAOE1pI4wCV>!h>WO zNa`k!+-dr#m~r+-L2`#OpBG5@JwR=R*X1@S@a?WOf`I8w+9UY9t*tNw&H7aNH4 z?Gl`KARY5@3g06sO>uB`w)~D;a*@4P+I;LDY`$Zh*vOYcCY@qqG5%UHf=|Q!i(g^` zR*hk8k@|ik>z&>twDQrzwg4S#xnLTG+@S%XXU!uiDy6E1h8u)Of_ z>zI_606CAb&PEw!>y9UJ3W+l<9z$*uVC^r-I&44XvRU~}tF6murJ)$hL5kl{p30V7 zvb&VE?YO}4yoCQ$JpYC7^}$z+W3T=bJQ?fbYdiqYv6YLPl68^xVyUaEDBR@h-$X|| z0vS1Ru15ws%+m*%o$9lVSB87cxF_ZEISNcg9jU07G=kWUZpeeMi0$EZxX0AmJu8ne zJlc&|bx>S(re|zKUEM~Hw{AeRC(NOZa zETU?h76i_I@>pW6Z_dN2t)@ZiV%r~`DCwsXeBz>G+(F#ZMc!o0#B#uSCK zS3%e2ems$sxV0iKZ&DN-Um>56EP|zm6aWKafl9-4dzX5+_5#)5<0Q(P1=7XgrvE2 zD4eNn`$g*3oc1I>OHombWQmR7uU% zy#Uask0#UC}zJ+%QW3lGIREA9`0lQH`p5%1rnK(P|1- zQ-mXUsve`jw)zj;1J8#hGx4jDD7@E+1w_GJ{v{rhQ)w(2^1CM`Q=u-%C6Dps-)gxJ zEtBJ)3xjL2AKTn5srrZ!n&A%Maoovo-^z$rk|vgsS;2j_UQtaJ$`aYz6S}e81=Z zE#~oGx!mRK$NnVSeU4NF`5o*i(T29%B<H0K5Z*l;|BSCk?>hr8I%@Nnop4|7EW*>XoTrZypey!`s*k|h0&#GFKXEg0(Y-}@6TsWhaT0Gh`N{>alpO2#6y1Xz2^ljx){L#KmUof&byKH#NZ&?r1kwM? z(uF(YGs$Vbt-tx6J^SEX zSs!H)RDr=Z(WAX|1QISvoJPr)fg(gU4J&-D(qIjq;T9w3bxO-L69p6lsn-FNZ}ilP zW24d7lXeBmw?1G^XO-ZQ$hk|WX5gJ@oodmP4DY0z(N(dM`)K5=;{bolDv`?j)Y;^>z`%G0{Z8626~xJ$4R9nH8+>C8n37J z#YaCRq=lFb(TFlKM$0CxrW+{#%>!IN#A~R_n5ezmhw2<9P13)8UE5DV@Sxje!&=aA z7!QF(N!YY=z|rI(P?gpEf4I}Sze@X}^{Y(suo0j%EJMO;1c~7(Hc&cdJxN@&xVK@f zEZYYKs%dn0NE0|!zeZSXkl#MUzIL&Q)}jokHNRGD08+B1;oQ%KmZN%Kv74*aQ&2UT z^>*7q#JYYDX-CR5!=dt&(WBIFC73mQFFCHSawg9F)I;S!DXg|F7z&7W5nWnEi?6Nb z?+50nA7TIK+w=iz`Qe5S^KX9{k`8B<;XdE zq`W!W!J_)LxiV3^{C4h|ps`!Dg!ls5lhen%z*{6~`xSy;xEHl~;7T&)YfL_AcTief zKjy}BRKE_y>R=ZZb}wUK>Q{=DA}~OPiNMw~;gfFoUDRGZ;j2N#rcxx~{{Y;6lOz~C z(^z1I=si1|cK1e_TGDW4AL_#Gq-|l-k#|};GA^B!kN;l8^DiRaHkh-a#N0b}Phk&{ zgvUoE$uE7mi+ntffKPUKs%pk=7wpz*U*yL*@LyXIKZdkzOK36`$w+=^h}2Wv&*VwE zx^8z{sGB1_le2T>KSbwve+1^uYm4y0o#SCwRj+CW|B>PY*Vg-}#QPo<(O38^oxFeX zU|X*~8zPBP->=w9g3=oD!5-U!r4@cJ?|k+!CY1H0NWXE;yl88rKTlhy?=lr)99}5% z3!TU}Ew=8a;wb3YfwXp=FFf=3&j&1)tnUCSOY9@f-f5bLEXo7HT740sx_7jj5NV!= zvg38VL>WQAVTn2i*leVl`z@kt68sUKj#G?nkqg^?0X>TcO*{3CWSqy*O{P$*We}BW zs!dXL462n_d79^%GyhT-#`rPamaw#SI1<}S^b$+l)ijfMR-q6d2_DPl(3JLVJfgss zTawz^KJ0H{j8~qF7mDY{ec`ra?(&;JqF;7nNqbFw9{zo_?k?HBspdAPU=h#kW?P)L zK$*l|%~p_lr8Xv}C(_$(E$%5y{_F&tY z`R1|flaxSop40Y>M=EOUtY{NE<4vcIw*8CW(9w6&X>=_Ropxg}uDk0pCJSg9SM4O< z+M06{Sa$x&T1d76A3NU}nMmXaRc?WGeqoh)c4QhNfw9N;f zM$6OhnQE=zJMkz;mUA)*?~7Hh;bo5Un^9OV#jCCT&f12AfRJ3Y<^<1#*S1tm?sA+) zfh5M*3Tzf1eV$nd5y>YAJ5I=Q z055wms-+6XxE z)I_)}Q#16|gL!&Pxrm@L$JH>0X1Of}rqv12Ii!LF={eH$A0ns(auq_0xYx zQIrcZT$&56_cRp@>?{ymt1j3_m5{Rmo*BOUG+n2y?J+q+4RoZYb1euf+caBP#&asl ztU3BU?TrB5#Rc?|bngNm@9_A2XxnUtzSuxIL-d8StL2&pcOrH~H`L4<+u=1*vuFb& zu#Ym+K2GQIKOc_y7N$FpHJ}M^@-<=fMgl@LgZO!V+8LSE zG>Z14y-u%Chn_GEz&KaB(s$&!)bs+C9(mWHdpMdVI7eDvPm52HleCOVl5`jxbgI>?6E^?L6>qp?PxLKL28XUITh>Ea@awBzK(Obcp@fOf`>**TLokC43SSF_YpKz54hP|lDJ0>X=O8TyIlP%g^t-J(bOki6~(Rq92o1V9nKNe`gN}kvdDn5Ld$|$$)!94T5|xpPtW%8FU#6;H;-t4k zx<`{2g*e13xorHoElpYAi%xI@u+w&&pK-ZJ&oA8(qrJw~#2h2&_9+0xZE z52vI;T|YivB_C%)x`Hr2#zeiYS7MPgUuiGV_I712mjUe|En_U zHM|fLAN{InH^FsUeX{APxN*x^Jqsl2&I^#-E&0Yl`ruA#6l~j9y;@5pny_}=7ykLy zU!if%CAmTklkmle7?D#shi2d-GonKY;eNjxav;aE*CGxglLJ)dL0~$Jv;yo1Esz8j z`Kw*j@~=0T0y-HlZXaez65hP3|7TV$ye5csW5wd%_QrSHROYHEk6^>63jag(d)s{s z?ob-AFASU4vJ+CY@1W{3q+wtkVbe?~G=vcqtW%9S*UMOiwJBGH`ZNDvAyaLK(Z0hd z9*{^yrP2l&D8nCPE;Bzxq9(IOfOi7Z72qw<2uK+hp7LJxaV48616c_;{Gx5k83ArX1P|=Iq|oEVJ2a4`HOxI5 z2-stL$4e}=JyJrLhrhCi8mYQ4aT9J9Ng>QW046#`oE2O#4}vLQz>~i>#>_onIZi5U z!yNCZO+z~`VLl%@Wj}imQg=I{+-(;v`a*AYLbCpTjeg_2Oi<#HlTRQBbJqn>=uRpA zY8kIHv|d*?6sS#Ga-s0+*vJyvje}&AL z$0UB9W#%@&L-m1EfYXAr%&X%Wn(d2^j53NA?2hlOY6BBI zPqcXB>$jJXh;>5I$c654!(FBx9*GmX?@Wq@le>pCoh^04JoJS^nLv_40gRY=qbwIypUFQsb6tA+CJtuuw;)R`3 z>$~plKpwq6`*DKMwScl*b!Ifal;-gBzNpY*w`Jry5=ZcikvkJ}dxh&*zwLN~1O4fa zZ7a(%SsiE5y?t3au{R78lj?MFe5@gNLtZEO@^OWA8NTtYIllU%9{LmyrRYly1$K-e zm;C121C=}?=gm2_yDxVHZv8QFOmz;bS`P@iU9dd647l*9BOkna}B55Xp@+=?qPw6_OLuWZSF9FSY3ud`|bq>$sutNFH{of^)Tu=bD} z*IGH^2x4J&Eh_Vy-sBDp1+j#LZoeiDbck4rCatu(t=Kz%U}vN3P0WAD@2Z}Te-x?r z2*b~s*`>5?@!{~m0CwI!CbP9}Y-h1Nu-gkiw=95?oa&q`;ZA;F-GZG{kxnx2yLicE z@IIlruG+v+0$TH&TUP54H1*CL;BytmUaMpxdFd{S2I+2JLObncgT{jH^R|jLS>i3LMi1JrmAZhaFQO)q7-SG|V$xlt@$z3Jd z$YOq`ZtvM^aorki9eV!23n)4c-*AW(^OMAfGSBcFG@A|#(6hz!D>dI{?j--dcX;h-a;@QOgVcdgWHYj9_UGu)nJBE;Z&<8jVXz^OSC=psJG+Wnjk&WRz$RQBWJHK3QiCO*~tLcvRv6FkM`X%dP(x zi~85~Xu`_yzkLGwGfmChN#&V+{Z4@zIoBvJ-Fu9up5zqR4?@ zzuH{;w!!>c^0%S8u%1t$bMv2`K26>?EBI|)Xvk6f-rZlm%-(&ptu$qgto_5&DSKGK zlki66s&siCycibZ0_ViQAGRC$9ZNthY^Q6q;#?scj{U|!h97K5aikZ4%yV`sr64T>`n3c4BIfEQF zZC@*(kZ#5H<854^YOQoNu-S8BEoPy#Sg4prSs+*SqYJR=p(&F3#Hw!!h%t))ek`zV ztGIL${@vQ|gw4Qp)X^r~wjd1nsd_BE-yWKUjDd1h9F_GBl>b_1fu$Mx_n|aoyenHM zCyD1*L1~3rKB~532}_kasWV;$W%%FZWM1Ej(G7Zi`-y$!HV{=NRoCCVYEH5XT$z72 zB)&88tj}G0>by>p{n&m-%MFjKcd4`#KgUF$?Ku4x@%-VP?s(JZ}~~RAxe6J`YekFL=TvxVIvSTpcyw9gG^-HW0r}V zZ_wnCj@mVf`u<)*XcMBm+~7c=xpOT5#O1`ZNNN+ggrHsg5-;`yvJ?~K{&QTC7(u;Y z3CGKgDyIa`wPsf0NDx9DJr_fOvHPy9Z*m5{S=PylMo)sbCTO>0BdJ-}1de%iPiHoi zS^{Ib9S33KOV`nP{Y$rsy&b{m&~I#uYUt!xfYuz&4uB<{^E{Ty9UzXZ9dK=EpwD#n zP!frI{f0;y>y&Pn5&GO%Ymeb3p zE8wnpJv37^y2}a&EJVz@f`%JkqmXeTbQmn?(wP-RDk|(9n0a2~8(fFCEBi|uSs>gE zKC9(r`*{WljkF4$y%{xegCc_SPV%ChLX+*rb`n^vCJPj;PT@>!m6F&|0Yz%y4%h+2b~ z{BCaZDARIm-7!Y4T*4*3==>n&1{kp9cbEoC?=fL1p9(d|Q`WTAdKekqbc%?14XR<& z_7Z|to%_+r=PHv*iI!~#TjL5Gz?4Tl@Wad!f*Nsj z14=fq;u}u@gmC9+GLPxeclYB!>o@qWzc{Z3V)i+-ayzzupK{(OE{HPQ{-$6AkD!kH zHAX7uP=;k^ABm+lZh(=P7@jwILQjO$p*9}nyvCIw?Av&mCEze{&CIK{yda|!BxXPiPXzidp{M<2H=P=DsQ9Pc&^3Cc zx3u0Y6RnGV{e8zd|MG&yWF3`Ue-p?d%m-epg@yBDy=pdP^rGhI_Gv$nx2-7#o!1u7 zqqLFs#QCzCx<*?NsjcSz9LaQt2(EBoTt1#2-X#hYjz^Np9kAz8b|RKQI**&MX#rno z{Kr2W*}J~^>JYNvBCbdcc`Ip2QMFjhvBz6w4I19G={wT=>?qo097Ow@xBYU)WEEh& zcAf%=$f0!$GxaFdoh8KQa^rIQX!9S}ib8{Zf|LJzOX=v^NQ~dzBJ+bK8UsK%&pNII z=TT*3u*hd$_c=irzV^csdCccC^`WV|YWBvzVvq-0mKO*L8ifBgGG2R3mb1gjlDo%#e!$HaO18ZdX}y5Tzeze#)|G4g#as6r*xGw$t0n{yJyn2wmd_Vj z&V74s!fOd{5+G?OT#$_C%CNaEx*l)(zy5Oyh|T|@`H6nB+xoby*Ozymm`ICzB!d@0 z6?%ojQZhBxwjSyB9Ocd2;F441*-;J2NVq`@wHLSB3mf8yA+xrc{b=I&)kb!mj#Jc_ zUDrcV&JY4sdDDe!Xy=i{o?OV6~`N^`%_P{gR@5vjrh&L|Hi z$h&J^Sccz~f<-;7&x8d~!=mv&Fz*V}T$e3&*CHRM@}=VW^EHxQc_qFUtHat{s3;$r zp=s*~|2G|`;=T(BR})UOdm*BjF|!ZpsRR|>%u2^(*3f7QiZMe}{wZ#-Pi8r57Vs}& zf-*F{Aqc?~SsaSaQlAy9GByqm)lnwvrONTJ$X|JGV=VaiM zBB@9l3g2zVZ!r1;1s{`d&0lY6hhM5oDBcbt!y{j!RdoyEj51$k}H>SQd4nPA9LV#7tCMDit}<>vdy6UBGl!C99B% z^gV&)>DX(|Xg_syVSgP_BG&{spZRw52kswgnUzL#e3kPoX5SIe_}`{yA#!j{NBt}) zFQ6KEIIH+og8ngXe%4-$4060)DuI+A>ts^R3c^D&kk89)w@uJV{h^z@Gtb-a*viez zz+lnd8Q^3v|*y~ekzAsgOAB~JXy3}`@vQqsV zz>SiyD$-l!zv5N9Lg!W(&8?pkjUE0`te^;~|Nl-$lepdvp5m(uR+b2dU~Y&1^rj?3a)kq9)TQ%%+yf;>XZ)`R%I%^2;%}OD?&Uh zs7p+ucCxZrd$w^Wk~~3XsbH>^=k?he+RrhB&_!=4@%g&=R_II4m#)2}yn@iHH2sUm zp5ykr?ICXvDB%F*zOiK4&K~pRRbO%i9Z~+*x&n_CYKsXy!D8k;z#~{uecTIw^^twT zQ#E7u>dZ=vjyTOzI{Bj#e0t%bT;L1PVK~L!?yl7oRssz3#xz4e$TLPQSdG5Y7@zfA zswvXQy%6{QAQqS$Pghy#4bi&GBlWoZG&_L)LGhc@yhHYpwnL;Xt1Kb*+E86m4*W&Y ztGCK+^MvB#uDwvRJR-FHV=K&oi&6zx(#G^jV$=?f|5MkX$o7YwW^tjCgkrzlef`RA^sZjKqwE2X zU@O_f&G>-u0yz2s*K92#I&&1zgMRXqLyaxd;Phmjj1MMIZ2EXxobrI?tArSEK(Lwo zM_Tb!-)Nr%GX2aIA69&=D8laiw)e=6dhc>l2GUkki&V$9J7DdPR%o=O^4(5cGu*CU z27nBu?g}Bz^89w<;rRdDTf+-(r2Y}&Mr1PCax2zQ z2Pv$*zGE&XsAN=+cH%{v&|oFCB@8O7wRRYr|C{$^kA?<3sgtgoH2|#46`snI(ZgQI zIMVU&aaa6X*caWO3?AHjI)ZYn_)hK%?#_f1@@?BY#EO3DON5r+Cb5din}hEaCeA0- zB#QpXj1=3g4%E*tFw95}6)|iOPSJuCiaWb0n8{4-c44te9gzkxkwY=>#QQB_*dRMY zX6XeNgK6s&g9zV5VGa_p8AWM+R=hND8~9G2h7+!|bD%UvX3yM5+?y_E>sw6U#{Gjd zU3cwbF;-y#PvDNw>sK31LY&3~=Z@%0YCCBCb6|wx`MHs1@9^_{+|ZKL#H2#FRigg!bW%4zm-*XP;f~_=lj~J}1aHb1 z*MxlTbj_|bLod^^hqmQ)%6~ke&@5o=n-_r7)~?Wf z?`lu~n9?D|2Z_!OdNQwmiic=_`|%GvcBAgeY$V~L{~a>8VA&2mBtX^z2!D24`h&}w zzB6_P-O$C9_}Yz0HCa9=!6gv~MDm}xuY%))e2rN)B ziSNw31qHY5Pe}6}t;F47;3Y1>(-veiC6*xfokhWSd)4n6KHd8A_0E&fXbRp08Zqkt z)~lzCfSNLf5p6R+Gle*2+Co8Mxd|U6;J_%Ww-8Fl^`_rECw<$8ai!PvrdwMw+j&xo z@8=fi-#+gbk%yzu=!ZT_PT3XJXWp*&SxqbX!k09&sn?z)CvYH8+)V0AO^QniEta*k z$h&Mwvk`=hA@)znB;#Ry6moDEWpBvttp#0mC?Qa&za`IH>*`pYqXTwiw z2R{nt<_-r&u3=AJmxs+M-hTgQuD-(De*s+v1wU)FRNfK#>ME(ZL}}0vGqIMHSNuQ2 zwBA6JcUhL;^Z1M&6mXkN>+l2)dhS0Ucs+O!f%V-C@oJ1W8dDF2Rxgskgs43omF)X`)NO+=8s); zE5L3Q2bDEm6W!wxiNn2E##QSo1g!}>`1nS@mw{v5gSz8%E4~cieZ#lQ#NP)Q9TNOy z?*FtOI=o+h&59QhxUgLy1cx7kk$B%!%=#=Z4XepC3XSfPtA#0J4=s%ykI(g@UqX(q z>TNs&0i6cyc_^CpA7td5@h5!tD!*^=GpEZwdmSEFhJKi=9?D62xWhAnTunclIliUm zwU_TyZOgOgvWG=?cK)xgN8Ubp%96)p!JU zfu&DqU7s}@ouMI^=U`$Ey+Dl*oVFsTTGuZetf(Syw~*KoQCMaQwc(mMw27tk=Yu9l zLy}7wxEfWH2A2poxkHZDhy9DEi*|dro$5IgtQf@Y9{Cu6{qw-LJ+DbgmwobUm&>>} zeR>96pVz+_+c{pGur0V_F4yVn_;7}+x?AK z`)s`z;_>``;K{P7T>};2dh}lyB^p;jvx}Gm8eCa6K#;}l1SR%zX&p3)RhChhm{NP# z6p9z9W{ug?BSBV_Cii`QEu3D4qCY}W?IQ|mm!{gPJ{u%j!PWqqZks5#d_o;k&KzPE zIoy@(pzTX6%NmgHtdCB#kuj>Eo3`P;Vs`khosvd#Ea8mRQoG3btbNuoW{uR7eO)p}bd47?coa zP!2KXey(TXH`0Y!UoBPxmw8iuHDkLv27ZtmbUL z&F|TM1d}C-dNky!<4kjxaPAk)bNM;k z!eUKWtuHJAAt}grtcKH5>EA)N^mO*5ZX{2>jp7pIZ$-c2@k@yQ(S^8P<1DyYmP%eP;&}n>usnS^0a^R6AhN(7*QM|bn5`G0eV)-+CL1@KX35pdW zhIZ?Bmh+K(eNElkwqgiq(ht_>*mUo(HI(Jqyt>=rQ}{SnSyY^KF=k#SDokJU_DJ@H z;9(zfv0Ih7+RAD76lD8ljqBQW&uYc#h9<0tf4H$4sX;k^9+gtzF7eJ;tpdG?Kuwqg zlH;Q|G>Z`hkSQ1xl15+9AyCQ!g;S_C@20{t2*rB&a>_Ju<3t%pCLMO;i6~q;cGxy@ z04?{^Q}nQupYR$Kt_c0|&-HJA8M=`Yf3jEVv#~kN;o4|PSxu42rD^XTUuK?CLS}8l zoVtmK55rkocbHSidO!6~m1zDT(nqcbeuUCl_vn-NgIVP`cod~t!G4-=l&Os_m9ua% z(`wg)6A-Og-C}DnN@|Eg#Q>SC{q+VfwLHi&s)5ok=4beF&3aFjuZGklq-h=itcJ+Z zxBo5pLy0=QUAc1dmvFuV-w?PYX)ZJQ=?}}PJ+YDbDyN)E<8yik56oa4GvU_*qSsWD zv<24ZT=74vM?Jvn|4(~^@%pw%5Aji@&6GiMM^Pc|F{bW|KLU_{yhYdFa5TEW_sj1NccKS#*ur zk^a}ozyN1$jVoBlu&gKKxa&QO%gqF1#BtI35R~UNJ07-@V8p)`W1B(k_>~nEwMhTp zb?ic^*XYtju#DfnHKKJId7UOy{!U1KcawsmH`(kpU7p&G>o3t_dp9dXrpJi!KvGYrt1R7ooskj zWIb%6Y37=R_!L8BwQhz4^-9jRV~ftfqc&Bzn;ZM7>ul#}Vf2xdx;p##3H4kSJ2Cp- zaP76T68JLdh;fH;17rFuCqO~fgU()8lY(qbYl)U)+Ah`}nxTk>)yqM2lxV*|;}JDN zDrv^rZ$E*VH0dz*ES`n8Kodm-QbB_CD7QX&{AFg35J#RcViy~2=gEnxMZX#Tnrz-X z>yeh(vs@}`j)=GIty~mW%J+*a?fEL&b0D}Cycqp{`LK1xKX{BApz6RwB{9PKf1B;! zM^sn+u~9Bz@GzlP-IiIpzEsW|?c*6yjrGhcH#M!x_xvw=&AFdAWTpNGN}ApJy}nU( zTl&X~Q0P?3_sBDQ^`F+rj5SWbZY^y~wGUag>HIk&zRy*xWwCE-COioh%;Z7YdZEs* z$@Ge(QbEFAIL44^ydU#ofhYanIoVuR%)|vK+L+F5*!`@9qPtHEeh2B%gTma|h%Ype z`o@zy!->1Z4+((cY_s_bt)A$Z{jhus@Yjzq&uza?x|(lePL5YNBw2hwu&&%~(Bk>W zRP4{=D*C%)wY~JWDv|aek>)Y&FVGwXm^lC0;76t5TX#ui`Oi$jnn!$If8T z%ogT@imZba$>6h5@c+I1u8#O~murcde)MHQ*6+TCzLMM-H2vKLVEQogsI@=1_-)peY;BNJ&HR5QP92#M$`F9Z$PFTt&xZpxzNG84hRM1l zY~i7LsBSNMOVmiz+2JqGmzM)0!2~~28?w`sNWag#8BD^gh+w`lc(RKzFKJ@Q#7MOS z{&@xtg(}5;Y#xO8rSIV}!k$DXk+Yqz^j{IGM|BQo{pUIt(8A&fuhk z?=Y{W*Fn$8oYdqh%S`^=-5aV-DS#7tN^`9heEun?*8i;Jj+9>r2}cDfTOw!d}_1ANlb-+w#*!pCZTD zM+@Jr(f2iZA>~5&&zQKeIvmu%Y*|0DxOVsc*Q!DFB|igl z_1&l5S*_nm)5j%gYedJ3F-GfNDSsd%2liHnT*~e6Ht0HOZVhY!Pu~X|y;)WM^~#Ht(_j7+o4yWc_mkOk|IfcsUz&3h6p-B3 z4~kv|t=glv#`0XHc=A6t36(?@wb&)UJIj-&y~3Ybq#t*xzZ?QbL?_o zw<|;|znND8%;NiP&A&(gPQG=k7`#y^E@OTKu$ce4Z-sg9-MycE-v0jn{C)iUnTKy4@MOi?0Iz z?yQO}{R5f|14s8ercL*je<``ES?s(2*KYq$XVdO{KYy`BcQ>>A+XiQ#gCAI1Uuf-3 z{#W0WkoSM*k-zmP9xDF%Y%G?4@7MqB$?TduhYdtn-`BtXB+mbu;chC=!A+{_20MB^ zKVH+{w^sb=d#Wzp$Pz8(mxIW literal 0 HcmV?d00001 diff --git a/ExtensionService/AuthStatusChecker.swift b/ExtensionService/AuthStatusChecker.swift new file mode 100644 index 00000000..d144ed94 --- /dev/null +++ b/ExtensionService/AuthStatusChecker.swift @@ -0,0 +1,41 @@ +// +// AuthStatusChecker.swift +// ExtensionService +// +// Responsible for checking the logged in status of the user. +// + +import Foundation +import GitHubCopilotService + +class AuthStatusChecker { + var authService: GitHubCopilotAuthServiceType? + + public func updateStatusInBackground(notify: @escaping (_ status: String, _ isOk: Bool) -> Void) { + Task { + do { + let status = try await self.getCurrentAuthStatus() + DispatchQueue.main.async { + notify(status.description, status == .ok) + } + } catch { + DispatchQueue.main.async { + notify("\(error)", false) + } + } + } + } + + func getCurrentAuthStatus() async throws -> GitHubCopilotAccountStatus { + let service = try getAuthService() + let status = try await service.checkStatus() + return status + } + + func getAuthService() throws -> GitHubCopilotAuthServiceType { + if let service = authService { return service } + let service = try GitHubCopilotService() + authService = service + return service + } +} diff --git a/Script/next-version.sh b/Script/next-version.sh new file mode 100755 index 00000000..07864c61 --- /dev/null +++ b/Script/next-version.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# +# Returns the next version number for a release. +# +# Can be run four different ways. +# +# Return the next major version number, e.g. 1.0.0: +# ./next-version.sh -m +# +# Return the next minor version number (switch is optional), e.g. 0.1.0: +# ./next-version.sh +# ./next-version.sh -n +# +# Return the next version number with a supplied patch value, e.g. 0.0.123: +# ./next-version.sh -p 123 +# +# Return the current version number: +# ./next-version.sh -c + +set -e + +# Ensure we're in the root of the repo so that gh works as expected +cd "$(dirname "$0")/.." + +LATEST_VERSION=$(gh release list --exclude-drafts --exclude-pre-releases --json tagName --limit 1 | jq -r '.[0].tagName') +if [ "$LATEST_VERSION" == "null" ]; then + # this will be the first release + LATEST_VERSION="0.0.0" +fi + +if [ "$1" == "-c" ]; then + echo $LATEST_VERSION + exit 0 +fi + +MAJOR_VERSION=$(echo "$LATEST_VERSION" | sed -En 's/^([0-9]+)\.[0-9]+\.[0-9]+$/\1/p') +MINOR_VERSION=$(echo "$LATEST_VERSION" | sed -En 's/^[0-9]+\.([0-9]+)\.[0-9]+$/\1/p') + +if [ "$1" == "-m" ]; then + echo $((MAJOR_VERSION + 1)).0.0 + exit 0 +fi +if [ "$1" == "-n" ] || [ -z "$1" ]; then + echo $MAJOR_VERSION.$((MINOR_VERSION + 1)).0 + exit 0 +fi +if [ "$1" == "-p" ] && [ ! -z "$2" ]; then + echo $MAJOR_VERSION.$MINOR_VERSION.$2 + exit 0 +fi + +echo "Usage: $0 [-c | -m | -n | -p ]" 1>&2 +echo " -c Return the current version number" 1>&2 +echo " -m Return the next major version number" 1>&2 +echo " -n Return the next minor version number (the default)" 1>&2 +echo " -p Return the next version number using the supplied patch number" 1>&2 +exit 1 diff --git a/clipboard.txt b/clipboard.txt new file mode 100644 index 00000000..d781f161 --- /dev/null +++ b/clipboard.txt @@ -0,0 +1,70 @@ +import SwiftUI + +struct ContentView: View { + @State private var output = "Ready to uninstall Copilot for Xcode.\n" + @State private var isRunning = false + @State private var dryRun = true + + var body: some View { + VStack(spacing: 20) { + Text("Copilot for Xcode Uninstaller") + .font(.title2) + + Toggle("Dry Run (Preview Only)", isOn: $dryRun) + .padding(.horizontal) + + ScrollView { + Text(output) + .frame(maxWidth: .infinity, alignment: .leading) + .padding() + .background(Color(.systemGray6)) + .cornerRadius(8) + } + .frame(height: 300) + + Button(action: runScript) { + Text(isRunning ? "Running..." : "Uninstall") + .frame(maxWidth: .infinity) + } + .disabled(isRunning) + .buttonStyle(.borderedProminent) + .padding(.horizontal) + + Spacer() + } + .padding() + .frame(width: 500, height: 400) + } + + func runScript() { + isRunning = true + output += "\nStarting \(dryRun ? "dry run" : "uninstall")...\n" + + guard let scriptPath = Bundle.main.path(forResource: "uninstall-app", ofType: "sh") else { + output += "Script not found.\n" + isRunning = false + return + } + + let command = dryRun ? "\(scriptPath) --dry-run" : "\(scriptPath)" + let appleScript = """ + do shell script "\(command)" with administrator privileges + """ + + DispatchQueue.global(qos: .userInitiated).async { + if let scriptObject = NSAppleScript(source: appleScript) { + var error: NSDictionary? + let result = scriptObject.executeAndReturnError(&error) + + DispatchQueue.main.async { + if let error = error { + output += "Error: \(error)\n" + } else { + output += result.stringValue ?? "Completed.\n" + } + isRunning = false + } + } + } + } +} From 6395097d892200209cbff69cea4a20ca0eef8e26 Mon Sep 17 00:00:00 2001 From: 3rdBaseWildcat <187662658+3rdBaseWildcat@users.noreply.github.com> Date: Fri, 5 Sep 2025 00:04:22 -0400 Subject: [PATCH 4/4] Pre-release 0.32.110 --- Copilot for Xcode.xcodeproj/project.pbxproj | 6 + .../xcshareddata/swiftpm/Package.resolved | 8 +- Copilot for Xcode/App.swift | 4 + .../ChatIcon.imageset/ChatIcon.svg | 3 + .../ChatIcon.imageset/Contents.json | 15 + Copilot-for-Xcode-Info.plist | 26 ++ Core/Package.swift | 2 + .../Skills/ConversationSkill.swift | 10 + .../ProblemsInActiveDocumentSkill.swift | 52 +++ .../Skills/ProjectContextSkill.swift | 63 +++ .../ConversationTab/ContextUtils.swift | 19 + .../Views/ConversationProgressStepView.swift | 73 ++++ Core/Sources/HostApp/General.swift | 19 + Core/Sources/HostApp/GeneralView.swift | 240 +++++++++++ Core/Sources/HostApp/TabContainer.swift | 23 + .../TabToAcceptSuggestion.swift | 19 + .../LaunchAgentManager.swift | 6 + .../Extensions/ChatMessage+Storage.swift | 74 ++++ Core/Sources/Service/XPCService.swift | 2 + .../SuggestionWidget/ChatPanelWindow.swift | 1 + .../ChatWindow/ChatLoginView.swift | 91 ++++ .../ChatWindow/ChatNoAXPermissionView.swift | 55 +++ .../ChatWindow/ChatNoWorkspaceView.swift | 48 +++ .../ChatWindow/CopilotIntroView.swift | 110 +++++ .../FeatureReducers/WidgetFeature.swift | 5 + .../SuggestionWidget/ModuleDependency.swift | 7 + .../WidgetWindowsController.swift | 7 + .../Sources/UpdateChecker/UpdateChecker.swift | 21 + .../PreferenceKey+Theme.swift | 1 + .../XcodeThemeController.swift | 18 + .../ChatServiceTests/ChatServiceTests.swift | 22 + .../TabToAcceptSuggestionTests.swift | 23 + DEVELOPMENT.md | 18 + ExtensionService/AppDelegate+Menu.swift | 42 ++ ExtensionService/AppDelegate.swift | 26 ++ .../MenuBarIcon.imageset/copilot-16.png | Bin 1863 -> 1756 bytes .../MenuBarIcon.imageset/copilot-32.png | Bin 2663 -> 2557 bytes README.md | 47 +++ Script/clipboard.txt | 70 ++++ Script/export-options-local.plist | 10 + Script/localbuild-app.sh | 42 ++ Script/uninstall-app.sh | 2 + Server/package-lock.json | 7 + Server/package.json | 1 + Tool/Package.swift | 1 + Tool/Sources/AXExtension/AXUIElement.swift | 1 + .../LSPTypes.swift | 60 +++ .../Conversation/WatchedFilesHandler.swift | 17 + .../LanguageServer/GitHubCopilotRequest.swift | 66 +++ .../LanguageServer/GitHubCopilotService.swift | 36 ++ .../LanguageServer/ServerRequestHandler.swift | 55 +++ .../Services/FeatureFlagNotifier.swift | 1 + .../HostAppActivator/HostAppActivator.swift | 121 ++++++ Tool/Sources/Preferences/Keys.swift | 27 ++ .../SharedUIComponents/AsyncCodeBlock.swift | 25 ++ .../SharedUIComponents/InstructionView.swift | 42 ++ .../SyntaxHighlighting.swift | 4 + .../FileChangeWatcher/FSEventProvider.swift | 59 +++ .../FileChangeWatcher/FileChangeWatcher.swift | 392 ++++++++++++++++++ .../WorkspaceFileProvider.swift | 29 ++ Tool/Sources/Workspace/WorkspaceFile.swift | 183 ++++++++ Tool/Sources/Workspace/WorkspacePool.swift | 6 + .../XPCShared/XPCExtensionService.swift | 1 + .../XPCShared/XPCServiceProtocol.swift | 3 + .../Apps/XcodeAppInstanceInspector.swift | 5 + .../Sources/XcodeInspector/SourceEditor.swift | 1 + .../XcodeInspector/XcodeInspector.swift | 1 + .../FileChangeWatcherTests.swift | 345 +++++++++++++++ 68 files changed, 2815 insertions(+), 4 deletions(-) create mode 100644 Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/ChatIcon.svg create mode 100644 Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/Contents.json create mode 100644 Core/Sources/ChatService/Skills/ConversationSkill.swift create mode 100644 Core/Sources/ChatService/Skills/ProblemsInActiveDocumentSkill.swift create mode 100644 Core/Sources/ChatService/Skills/ProjectContextSkill.swift create mode 100644 Core/Sources/ConversationTab/ContextUtils.swift create mode 100644 Core/Sources/ConversationTab/Views/ConversationProgressStepView.swift create mode 100644 Core/Sources/PersistMiddleware/Extensions/ChatMessage+Storage.swift create mode 100644 Core/Sources/SuggestionWidget/ChatWindow/ChatLoginView.swift create mode 100644 Core/Sources/SuggestionWidget/ChatWindow/ChatNoAXPermissionView.swift create mode 100644 Core/Sources/SuggestionWidget/ChatWindow/ChatNoWorkspaceView.swift create mode 100644 Core/Sources/SuggestionWidget/ChatWindow/CopilotIntroView.swift create mode 100644 Core/Tests/ChatServiceTests/ChatServiceTests.swift create mode 100644 Script/clipboard.txt create mode 100644 Script/export-options-local.plist create mode 100644 Script/localbuild-app.sh create mode 100644 Tool/Sources/ConversationServiceProvider/LSPTypes.swift create mode 100644 Tool/Sources/GitHubCopilotService/Conversation/WatchedFilesHandler.swift create mode 100644 Tool/Sources/GitHubCopilotService/LanguageServer/ServerRequestHandler.swift create mode 100644 Tool/Sources/HostAppActivator/HostAppActivator.swift create mode 100644 Tool/Sources/SharedUIComponents/InstructionView.swift create mode 100644 Tool/Sources/Workspace/FileChangeWatcher/FSEventProvider.swift create mode 100644 Tool/Sources/Workspace/FileChangeWatcher/FileChangeWatcher.swift create mode 100644 Tool/Sources/Workspace/FileChangeWatcher/WorkspaceFileProvider.swift create mode 100644 Tool/Sources/Workspace/WorkspaceFile.swift create mode 100644 Tool/Tests/WorkspaceTests/FileChangeWatcherTests.swift diff --git a/Copilot for Xcode.xcodeproj/project.pbxproj b/Copilot for Xcode.xcodeproj/project.pbxproj index 56e21c33..5c575785 100644 --- a/Copilot for Xcode.xcodeproj/project.pbxproj +++ b/Copilot for Xcode.xcodeproj/project.pbxproj @@ -17,6 +17,8 @@ 5EC511E42C90CE9800632BAB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8189B1D2938973000C9DCDA /* Assets.xcassets */; }; 5EC511E52C90CFD600632BAB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C861E6142994F6080056CB02 /* Assets.xcassets */; }; 5EC511E62C90CFD700632BAB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C861E6142994F6080056CB02 /* Assets.xcassets */; }; + 427C63282C6E868B000E557C /* OpenSettingsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427C63272C6E868B000E557C /* OpenSettingsCommand.swift */; }; + 42888D512C66B10100DEF835 /* AuthStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42888D502C66B10100DEF835 /* AuthStatusChecker.swift */; }; C8009BFF2941C551007AA7E8 /* ToggleRealtimeSuggestionsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8009BFE2941C551007AA7E8 /* ToggleRealtimeSuggestionsCommand.swift */; }; C8009C032941C576007AA7E8 /* SyncTextSettingsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8009C022941C576007AA7E8 /* SyncTextSettingsCommand.swift */; }; C800DBB1294C624D00B04CAC /* PrefetchSuggestionsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C800DBB0294C624D00B04CAC /* PrefetchSuggestionsCommand.swift */; }; @@ -191,6 +193,8 @@ 3ABBEA2A2C8BA00300C61D61 /* copilot-language-server-arm64 */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = "copilot-language-server-arm64"; path = "Server/node_modules/@github/copilot-language-server/native/darwin-arm64/copilot-language-server-arm64"; sourceTree = SOURCE_ROOT; }; 424ACA202CA4697200FA20F2 /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; }; 427C63272C6E868B000E557C /* OpenSettingsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSettingsCommand.swift; sourceTree = ""; }; + 427C63272C6E868B000E557C /* OpenSettingsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSettingsCommand.swift; sourceTree = ""; }; + 42888D502C66B10100DEF835 /* AuthStatusChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthStatusChecker.swift; sourceTree = ""; }; C8009BFE2941C551007AA7E8 /* ToggleRealtimeSuggestionsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleRealtimeSuggestionsCommand.swift; sourceTree = ""; }; C8009C022941C576007AA7E8 /* SyncTextSettingsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncTextSettingsCommand.swift; sourceTree = ""; }; C800DBB0294C624D00B04CAC /* PrefetchSuggestionsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefetchSuggestionsCommand.swift; sourceTree = ""; }; @@ -423,6 +427,7 @@ C81291D52994FE6900196E12 /* Main.storyboard */, C861E6142994F6080056CB02 /* Assets.xcassets */, C861E6192994F6080056CB02 /* ExtensionService.entitlements */, + 42888D502C66B10100DEF835 /* AuthStatusChecker.swift */, ); path = ExtensionService; sourceTree = ""; @@ -766,6 +771,7 @@ C89E75C32A46FB32000DD64F /* AppDelegate+Menu.swift in Sources */, C8738B712BE4F8B700609E7F /* XPCController.swift in Sources */, C861E6202994F63A0056CB02 /* ServiceDelegate.swift in Sources */, + 42888D512C66B10100DEF835 /* AuthStatusChecker.swift in Sources */, C861E6112994F6070056CB02 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Copilot for Xcode.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Copilot for Xcode.xcworkspace/xcshareddata/swiftpm/Package.resolved index 71d9e185..9d2675db 100644 --- a/Copilot for Xcode.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Copilot for Xcode.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/devm33/CGEventOverride", "state" : { - "branch" : "devm33/fix-stale-AXIsProcessTrusted", - "revision" : "06a9bf1f8f8d47cca221344101cc0274f04cc513" + "revision" : "571d36d63e68fac30e4a350600cd186697936f74", + "version" : "1.2.3" } }, { @@ -131,8 +131,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/sparkle-project/Sparkle", "state" : { - "revision" : "0ef1ee0220239b3776f433314515fd849025673f", - "version" : "2.6.4" + "revision" : "87e4fcbac39912f9cdb9a9acf205cad60e1ca3bc", + "version" : "2.4.2" } }, { diff --git a/Copilot for Xcode/App.swift b/Copilot for Xcode/App.swift index cc07bc2b..9ea64689 100644 --- a/Copilot for Xcode/App.swift +++ b/Copilot for Xcode/App.swift @@ -29,6 +29,8 @@ class AppUpdateCheckerDelegate: UpdateCheckerDelegate { struct CopilotForXcodeApp: App { @NSApplicationDelegateAdaptor private var appDelegate: AppDelegate +@main +struct CopilotForXcodeApp: App { var body: some Scene { WindowGroup { TabContainer() @@ -42,8 +44,10 @@ struct CopilotForXcodeApp: App { hostBundle: Bundle.main, checkerDelegate: AppUpdateCheckerDelegate() )) + .environment(\.updateChecker, UpdateChecker(hostBundle: Bundle.main)) } } } var isPreview: Bool { ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" } + diff --git a/Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/ChatIcon.svg b/Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/ChatIcon.svg new file mode 100644 index 00000000..74239992 --- /dev/null +++ b/Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/ChatIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/Contents.json b/Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/Contents.json new file mode 100644 index 00000000..329dae48 --- /dev/null +++ b/Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "ChatIcon.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Copilot-for-Xcode-Info.plist b/Copilot-for-Xcode-Info.plist index b45f6d1d..ab294fcd 100644 --- a/Copilot-for-Xcode-Info.plist +++ b/Copilot-for-Xcode-Info.plist @@ -29,4 +29,30 @@ TEAM_ID_PREFIX $(TeamIdentifierPrefix) + + + $(AppIdentifierPrefix) + APPLICATION_SUPPORT_FOLDER + $(APPLICATION_SUPPORT_FOLDER) + BUNDLE_IDENTIFIER_BASE + $(BUNDLE_IDENTIFIER_BASE) + EXTENSION_BUNDLE_NAME + $(EXTENSION_BUNDLE_NAME) + HOST_APP_NAME + $(HOST_APP_NAME) + LANGUAGE_SERVER_PATH + $(LANGUAGE_SERVER_PATH) + NODE_PATH + $(NODE_PATH) + SUEnableAutomaticChecks + YES + SUEnableJavaScript + NO + SUFeedURL + $(SPARKLE_FEED_URL) + SUPublicEDKey + $(SPARKLE_PUBLIC_KEY) + TEAM_ID_PREFIX + $(TeamIdentifierPrefix) + diff --git a/Core/Package.swift b/Core/Package.swift index 385746cf..ab008517 100644 --- a/Core/Package.swift +++ b/Core/Package.swift @@ -51,6 +51,7 @@ let package = Package( // https://github.com/sindresorhus/KeyboardShortcuts .package(url: "https://github.com/devm33/KeyboardShortcuts", branch: "main"), .package(url: "https://github.com/devm33/CGEventOverride", branch: "devm33/fix-stale-AXIsProcessTrusted"), + .package(url: "https://github.com/devm33/CGEventOverride", from: "1.2.1"), .package(url: "https://github.com/devm33/Highlightr", branch: "master"), ], targets: [ @@ -216,6 +217,7 @@ let package = Package( .product(name: "Logger", package: "Tool"), ] ), + .target(name: "LaunchAgentManager"), .target( name: "UpdateChecker", dependencies: [ diff --git a/Core/Sources/ChatService/Skills/ConversationSkill.swift b/Core/Sources/ChatService/Skills/ConversationSkill.swift new file mode 100644 index 00000000..d7883b8e --- /dev/null +++ b/Core/Sources/ChatService/Skills/ConversationSkill.swift @@ -0,0 +1,10 @@ +import JSONRPC +import GitHubCopilotService + +public typealias JSONRPCResponseHandler = (AnyJSONRPCResponse) -> Void + +public protocol ConversationSkill { + var id: String { get } + func applies(params: ConversationContextParams) -> Bool + func resolveSkill(request: ConversationContextRequest, completion: @escaping JSONRPCResponseHandler) +} diff --git a/Core/Sources/ChatService/Skills/ProblemsInActiveDocumentSkill.swift b/Core/Sources/ChatService/Skills/ProblemsInActiveDocumentSkill.swift new file mode 100644 index 00000000..203872db --- /dev/null +++ b/Core/Sources/ChatService/Skills/ProblemsInActiveDocumentSkill.swift @@ -0,0 +1,52 @@ +import ConversationServiceProvider +import Foundation +import GitHubCopilotService +import JSONRPC +import XcodeInspector + +public class ProblemsInActiveDocumentSkill: ConversationSkill { + public static let ID = "problems-in-active-document" + public var id: String { + return ProblemsInActiveDocumentSkill.ID + } + + public init() { + } + + public func applies(params: ConversationContextParams) -> Bool { + return params.skillId == self.id + } + + public func resolveSkill(request: ConversationContextRequest, completion: @escaping JSONRPCResponseHandler) { + Task { + let editor = await XcodeInspector.shared.getFocusedEditorContent() + let result: JSONValue = JSONValue.hash([ + "uri": JSONValue.string(editor?.documentURL.absoluteString ?? ""), + "problems": JSONValue.array(editor?.editorContent?.lineAnnotations.map { annotation in + JSONValue.hash([ + "message": JSONValue.string(annotation.message), + "range": JSONValue.hash([ + "start": JSONValue.hash([ + "line": JSONValue.number(Double(annotation.line)), + "character": JSONValue.number(0) + ]), + "end": JSONValue.hash([ + "line": JSONValue.number(Double(annotation.line)), + "character": JSONValue.number(0) + ]) + ]) + ]) + } ?? []) + ]) + + completion( + AnyJSONRPCResponse(id: request.id, + result: JSONValue.array([ + result, + JSONValue.null + ])) + ) + } + } +} + diff --git a/Core/Sources/ChatService/Skills/ProjectContextSkill.swift b/Core/Sources/ChatService/Skills/ProjectContextSkill.swift new file mode 100644 index 00000000..fa564882 --- /dev/null +++ b/Core/Sources/ChatService/Skills/ProjectContextSkill.swift @@ -0,0 +1,63 @@ +import Foundation +import Workspace +import GitHubCopilotService +import JSONRPC +import XcodeInspector + +/* + * project-context is different from others + * 1. The CLS only request this skill once `after initialized` instead of during conversation / turn. + * 2. After resolved skill, a file watcher needs to be start for syncing file modification to CLS + */ +public class ProjectContextSkill { + public static let ID = "project-context" + + public static var resolvedWorkspace: Set = Set() + + public static func isWorkspaceResolved(_ path: String) -> Bool { + return ProjectContextSkill.resolvedWorkspace.contains(path) + } + + public init() { } + + /* + * The request from CLS only contain the projectPath (a initialization paramter for CLS) + * whereas to get files for xcode workspace, the workspacePath is needed. + */ + public static func resolveSkill( + request: WatchedFilesRequest, + workspacePath: String, + completion: JSONRPCResponseHandler + ) { + guard !ProjectContextSkill.isWorkspaceResolved(workspacePath) else {return } + + let params = request.params! + + guard params.workspaceUri != "/" else { return } + + /// build workspace URL + let workspaceURL = URL(fileURLWithPath: workspacePath) + /// refer to `init` in `Workspace` + let projectURL = WorkspaceXcodeWindowInspector.extractProjectURL( + workspaceURL: workspaceURL, + documentURL: nil + ) ?? workspaceURL + + /// ignore invalid resolve request + guard projectURL.path == params.workspaceUri else { return } + + let files = WorkspaceFile.getWatchedFiles( + workspaceURL: workspaceURL, + projectURL: projectURL, + excludeGitIgnoredFiles: params.excludeGitignoredFiles, + excludeIDEIgnoredFiles: params.excludeIDEIgnoredFiles + ) + + let jsonResult = try? JSONEncoder().encode(["files": files]) + let jsonValue = (try? JSONDecoder().decode(JSONValue.self, from: jsonResult ?? Data())) ?? JSONValue.null + + completion(AnyJSONRPCResponse(id: request.id, result: jsonValue)) + + ProjectContextSkill.resolvedWorkspace.insert(workspacePath) + } +} diff --git a/Core/Sources/ConversationTab/ContextUtils.swift b/Core/Sources/ConversationTab/ContextUtils.swift new file mode 100644 index 00000000..84517df2 --- /dev/null +++ b/Core/Sources/ConversationTab/ContextUtils.swift @@ -0,0 +1,19 @@ +import ConversationServiceProvider +import XcodeInspector +import Foundation +import Logger +import Workspace + +public struct ContextUtils { + + public static func getFilesInActiveWorkspace() -> [FileReference] { + guard let workspaceURL = XcodeInspector.shared.realtimeActiveWorkspaceURL, + let workspaceRootURL = XcodeInspector.shared.realtimeActiveProjectURL else { + return [] + } + + let files = WorkspaceFile.getFilesInActiveWorkspace(workspaceURL: workspaceURL, workspaceRootURL: workspaceRootURL) + + return files + } +} diff --git a/Core/Sources/ConversationTab/Views/ConversationProgressStepView.swift b/Core/Sources/ConversationTab/Views/ConversationProgressStepView.swift new file mode 100644 index 00000000..28b39da3 --- /dev/null +++ b/Core/Sources/ConversationTab/Views/ConversationProgressStepView.swift @@ -0,0 +1,73 @@ +import SwiftUI +import ConversationServiceProvider +import ComposableArchitecture +import Combine + +struct ProgressStep: View { + let steps: [ConversationProgressStep] + + var body: some View { + WithPerceptionTracking { + VStack(alignment: .leading, spacing: 4) { + ForEach(steps) { StatusItemView(step: $0) } + } + .foregroundStyle(.secondary) + } + } +} + + +struct StatusItemView: View { + + let step: ConversationProgressStep + + @AppStorage(\.chatFontSize) var chatFontSize + + var statusIcon: some View { + Group { + switch step.status { + case .running: + ProgressView() + .controlSize(.small) + .scaleEffect(0.7) + case .completed: + Image(systemName: "checkmark") + .foregroundColor(.green.opacity(0.5)) + case .failed: + Image(systemName: "xmark.circle") + .foregroundColor(.red.opacity(0.5)) + case .cancelled: + Image(systemName: "slash.circle") + .foregroundColor(.gray.opacity(0.5)) + } + } + } + + var body: some View { + WithPerceptionTracking { + HStack(spacing: 4) { + statusIcon + .frame(width: 16, height: 16) + + Text(step.title) + .font(.system(size: chatFontSize)) + .lineLimit(1) + + Spacer() + } + } + } +} + +struct ProgressStep_Preview: PreviewProvider { + static let steps: [ConversationProgressStep] = [ + .init(id: "001", title: "running step", description: "this is running step", status: .running, error: nil), + .init(id: "002", title: "completed step", description: "this is completed step", status: .completed, error: nil), + .init(id: "003", title: "failed step", description: "this is failed step", status: .failed, error: nil), + .init(id: "004", title: "cancelled step", description: "this is cancelled step", status: .cancelled, error: nil) + ] + static var previews: some View { + ProgressStep(steps: steps) + .frame(width: 300, height: 300) + } +} diff --git a/Core/Sources/HostApp/General.swift b/Core/Sources/HostApp/General.swift index 5bcfe18b..406cb9f1 100644 --- a/Core/Sources/HostApp/General.swift +++ b/Core/Sources/HostApp/General.swift @@ -6,6 +6,8 @@ import Status import SwiftUI import XPCShared import Logger +import SwiftUI +import XPCShared @Reducer struct General { @@ -13,6 +15,7 @@ struct General { struct State: Equatable { var xpcServiceVersion: String? var isAccessibilityPermissionGranted: ObservedAXStatus = .unknown + var isAccessibilityPermissionGranted: Bool? var isReloading = false } @@ -24,6 +27,8 @@ struct General { case finishReloading(xpcServiceVersion: String, permissionGranted: ObservedAXStatus) case failedReloading case retryReloading + case finishReloading(xpcServiceVersion: String, permissionGranted: Bool) + case failedReloading } @Dependency(\.toast) var toast @@ -39,6 +44,12 @@ struct General { for await _ in DistributedNotificationCenter.default().notifications(named: .serviceStatusDidChange) { await send(.reloadStatus) } + Task { + for await _ in DistributedNotificationCenter.default().notifications(named: NSNotification.Name("com.apple.accessibility.api")) { + await send(.reloadStatus) + } + } + await send(.setupLaunchAgentIfNeeded) } case .setupLaunchAgentIfNeeded: @@ -58,6 +69,11 @@ struct General { await send(.reloadStatus) } #endif + toast(error.localizedDescription, .error) + } + } + #endif + await send(.reloadStatus) } case .openExtensionManager: @@ -94,6 +110,9 @@ struct General { } } catch let error as XPCCommunicationBridgeError { Logger.ui.error("Failed to reach communication bridge. \(error.localizedDescription)") + await send(.reloadStatus) + } + } catch let error as XPCCommunicationBridgeError { toast( "Failed to reach communication bridge. \(error.localizedDescription)", .error diff --git a/Core/Sources/HostApp/GeneralView.swift b/Core/Sources/HostApp/GeneralView.swift index b4f5d8ad..ca0ab1f1 100644 --- a/Core/Sources/HostApp/GeneralView.swift +++ b/Core/Sources/HostApp/GeneralView.swift @@ -1,4 +1,10 @@ import ComposableArchitecture +import Client +import ComposableArchitecture +import KeyboardShortcuts +import LaunchAgentManager +import Preferences +import SharedUIComponents import SwiftUI struct GeneralView: View { @@ -40,3 +46,237 @@ struct GeneralView: View { GeneralView(store: .init(initialState: .init(), reducer: { General() })) .frame(width: 800, height: 600) } + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + AppInfoView(store: store) + SettingsDivider() + GitHubCopilotView() + SettingsDivider() + ExtensionServiceView(store: store) + SettingsDivider() + LaunchAgentView() + SettingsDivider() + GeneralSettingsView() + } + } + .onAppear { + store.send(.appear) + } + } +} + +struct AppInfoView: View { + @State var appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String + @Environment(\.updateChecker) var updateChecker + let store: StoreOf + + var body: some View { + VStack(alignment: .leading) { + HStack(alignment: .top) { + Text( + Bundle.main + .object(forInfoDictionaryKey: "HOST_APP_NAME") as? String + ?? "GitHub Copilot for Xcode" + ) + .font(.title) + Text(appVersion ?? "") + .font(.footnote) + .foregroundColor(.secondary) + + Spacer() + + Button(action: { + updateChecker.checkForUpdates() + }) { + HStack(spacing: 2) { + Image(systemName: "arrow.up.right.circle.fill") + Text("Check for Updates") + } + } + } + }.padding() + } +} + +struct ExtensionServiceView: View { + @Perception.Bindable var store: StoreOf + + var body: some View { + WithPerceptionTracking { + VStack(alignment: .leading) { + Text("Extension Service Version: \(store.xpcServiceVersion ?? "Loading..")") + + let grantedStatus: String = { + guard let granted = store.isAccessibilityPermissionGranted + else { return "Loading.." } + return granted ? "Granted" : "Not Granted" + }() + Text("Accessibility Permission: \(grantedStatus)") + + HStack { + Button(action: { store.send(.reloadStatus) }) { + Text("Refresh") + }.disabled(store.isReloading) + + Button(action: { + Task { + let workspace = NSWorkspace.shared + let url = Bundle.main.bundleURL + .appendingPathComponent("Contents") + .appendingPathComponent("Applications") + .appendingPathComponent("GitHub Copilot for Xcode Extension.app") + workspace.activateFileViewerSelecting([url]) + } + }) { + Text("Reveal Extension Service in Finder") + } + + Button(action: { + let url = URL( + string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" + )! + NSWorkspace.shared.open(url) + }) { + Text("Accessibility Settings") + } + + Button(action: { + let url = URL( + string: "x-apple.systempreferences:com.apple.ExtensionsPreferences" + )! + NSWorkspace.shared.open(url) + }) { + Text("Extensions Settings") + } + } + } + } + .padding() + } +} + +struct LaunchAgentView: View { + @Environment(\.toast) var toast + @State var isDidRemoveLaunchAgentAlertPresented = false + @State var isDidSetupLaunchAgentAlertPresented = false + @State var isDidRestartLaunchAgentAlertPresented = false + + var body: some View { + VStack(alignment: .leading) { + HStack { + Button(action: { + Task { + do { + try await LaunchAgentManager().setupLaunchAgent() + isDidSetupLaunchAgentAlertPresented = true + } catch { + toast(error.localizedDescription, .error) + } + } + }) { + Text("Set Up Launch Agent") + } + .alert(isPresented: $isDidSetupLaunchAgentAlertPresented) { + .init( + title: Text("Finished Launch Agent Setup"), + message: Text( + "Please refresh the Copilot status. (The first refresh may fail)" + ), + dismissButton: .default(Text("OK")) + ) + } + + Button(action: { + Task { + do { + try await LaunchAgentManager().removeLaunchAgent() + isDidRemoveLaunchAgentAlertPresented = true + } catch { + toast(error.localizedDescription, .error) + } + } + }) { + Text("Remove Launch Agent") + } + .alert(isPresented: $isDidRemoveLaunchAgentAlertPresented) { + .init( + title: Text("Launch Agent Removed"), + dismissButton: .default(Text("OK")) + ) + } + + Button(action: { + Task { + do { + try await LaunchAgentManager().reloadLaunchAgent() + isDidRestartLaunchAgentAlertPresented = true + } catch { + toast(error.localizedDescription, .error) + } + } + }) { + Text("Reload Launch Agent") + }.alert(isPresented: $isDidRestartLaunchAgentAlertPresented) { + .init( + title: Text("Launch Agent Reloaded"), + dismissButton: .default(Text("OK")) + ) + } + } + } + .padding() + } +} + +struct GeneralSettingsView: View { + class Settings: ObservableObject { + @AppStorage(\.quitXPCServiceOnXcodeAndAppQuit) + var quitXPCServiceOnXcodeAndAppQuit + @AppStorage(\.suggestionWidgetPositionMode) + var suggestionWidgetPositionMode + @AppStorage(\.widgetColorScheme) + var widgetColorScheme + @AppStorage(\.preferWidgetToStayInsideEditorWhenWidthGreaterThan) + var preferWidgetToStayInsideEditorWhenWidthGreaterThan + @AppStorage(\.showHideWidgetShortcutGlobally) + var showHideWidgetShortcutGlobally + @AppStorage(\.installPrereleases) + var installPrereleases + } + + @StateObject var settings = Settings() + @Environment(\.updateChecker) var updateChecker + @State var automaticallyCheckForUpdate: Bool? + + var body: some View { + Form { + Toggle(isOn: $settings.quitXPCServiceOnXcodeAndAppQuit) { + Text("Quit service when Xcode and host app are terminated") + } + + Toggle(isOn: .init( + get: { automaticallyCheckForUpdate ?? updateChecker.automaticallyChecksForUpdates }, + set: { + updateChecker.automaticallyChecksForUpdates = $0 + automaticallyCheckForUpdate = $0 + } + )) { + Text("Automatically Check for Updates") + } + + Toggle(isOn: $settings.installPrereleases) { + Text("Install pre-releases") + } + }.padding() + } +} + +struct GeneralView_Previews: PreviewProvider { + static var previews: some View { + GeneralView(store: .init(initialState: .init(), reducer: { General() })) + .frame(height: 800) + } +} + diff --git a/Core/Sources/HostApp/TabContainer.swift b/Core/Sources/HostApp/TabContainer.swift index 0d3f0a87..dac068f2 100644 --- a/Core/Sources/HostApp/TabContainer.swift +++ b/Core/Sources/HostApp/TabContainer.swift @@ -30,6 +30,9 @@ public struct TabContainer: View { VStack(spacing: 0) { TabBar(tag: $tag, tabBarItems: tabBarItems) .padding(.bottom, 8) + + Divider() + ZStack(alignment: .center) { GeneralView(store: store.scope(state: \.general, action: \.general)) .tabBarItem( @@ -42,6 +45,12 @@ public struct TabContainer: View { tag: 2, title: "Advanced", image: "gearshape.2.fill" + image: "gearshape" + ) + FeatureSettingsView().tabBarItem( + tag: 2, + title: "Feature", + image: "star.square" ) } .environment(\.tabBarTabTag, tag) @@ -75,6 +84,7 @@ struct TabBar: View { title: tab.title, image: tab.image, isSystemImage: tab.isSystemImage + image: tab.image ) } } @@ -110,6 +120,12 @@ struct TabBarButton: View { Text(title) } .foregroundColor(isSelected ? .blue : .gray) + Image(systemName: image) + .resizable() + .scaledToFit() + .frame(height: 18) + Text(title) + } .font(.body) .padding(.horizontal, 12) .padding(.vertical, 4) @@ -153,6 +169,7 @@ private struct TabBarTabViewWrapper: View { .preference( key: TabBarItemPreferenceKey.self, value: [.init(tag: tag, title: title, image: image, isSystemImage: isSystemImage)] + value: [.init(tag: tag, title: title, image: image)] ) } } @@ -163,6 +180,7 @@ private extension View { title: String, image: String, isSystemImage: Bool = true + image: String ) -> some View { TabBarTabViewWrapper( tag: tag, @@ -206,6 +224,11 @@ struct UpdateCheckerKey: EnvironmentKey { public extension EnvironmentValues { var updateChecker: UpdateCheckerProtocol { + static var defaultValue: UpdateChecker = .init(hostBundle: nil) +} + +public extension EnvironmentValues { + var updateChecker: UpdateChecker { get { self[UpdateCheckerKey.self] } set { self[UpdateCheckerKey.self] = newValue } } diff --git a/Core/Sources/KeyBindingManager/TabToAcceptSuggestion.swift b/Core/Sources/KeyBindingManager/TabToAcceptSuggestion.swift index f2d4c147..1fbc0583 100644 --- a/Core/Sources/KeyBindingManager/TabToAcceptSuggestion.swift +++ b/Core/Sources/KeyBindingManager/TabToAcceptSuggestion.swift @@ -130,6 +130,11 @@ final class TabToAcceptSuggestion { Logger.service.debug("TabToAcceptSuggestion: \(accept ? "" : "not") accepting due to: \(reason)") } if accept { + if Self.shouldAcceptSuggestion( + event: event, + workspacePool: workspacePool, + xcodeInspector: ThreadSafeAccessToXcodeInspector.shared + ) { acceptSuggestion() return .discarded } @@ -176,6 +181,20 @@ extension TabToAcceptSuggestion { return (false, "No suggestion") } return (true, nil) + ) -> Bool { + let keycode = Int(event.getIntegerValueField(.keyboardEventKeycode)) + let tab = 48 + guard keycode == tab else { return false } + guard let fileURL = xcodeInspector.activeDocumentURL else { return false } + if event.flags.contains(.maskHelp) { return false } + if event.flags.contains(.maskShift) { return false } + if event.flags.contains(.maskControl) { return false } + if event.flags.contains(.maskCommand) { return false } + guard xcodeInspector.hasActiveXcode else { return false } + guard xcodeInspector.hasFocusedEditor else { return false } + guard let filespace = workspacePool.fetchFilespaceIfExisted(fileURL: fileURL) else { return false } + if filespace.presentingSuggestion == nil { return false } + return true } } diff --git a/Core/Sources/LaunchAgentManager/LaunchAgentManager.swift b/Core/Sources/LaunchAgentManager/LaunchAgentManager.swift index 2dfa2695..202819d3 100644 --- a/Core/Sources/LaunchAgentManager/LaunchAgentManager.swift +++ b/Core/Sources/LaunchAgentManager/LaunchAgentManager.swift @@ -41,6 +41,9 @@ public struct LaunchAgentManager { try bridgeLaunchAgent.register() } else { Logger.client.info("Creating and loading bridge launch agent") + let bridgeLaunchAgent = SMAppService.agent(plistName: "bridgeLaunchAgent.plist") + try bridgeLaunchAgent.register() + } else { let content = """ @@ -87,6 +90,9 @@ public struct LaunchAgentManager { try await bridgeLaunchAgent.unregister() } else { Logger.client.info("Unloading and removing bridge launch agent") + let bridgeLaunchAgent = SMAppService.agent(plistName: "bridgeLaunchAgent.plist") + try await bridgeLaunchAgent.unregister() + } else { try await launchctl("unload", launchAgentPath) try FileManager.default.removeItem(atPath: launchAgentPath) } diff --git a/Core/Sources/PersistMiddleware/Extensions/ChatMessage+Storage.swift b/Core/Sources/PersistMiddleware/Extensions/ChatMessage+Storage.swift new file mode 100644 index 00000000..ac60fe09 --- /dev/null +++ b/Core/Sources/PersistMiddleware/Extensions/ChatMessage+Storage.swift @@ -0,0 +1,74 @@ +import Foundation +import ChatAPIService +import Persist +import Logger +import ConversationServiceProvider + +extension ChatMessage { + + struct TurnItemData: Codable { + var content: String + var rating: ConversationRating + var references: [ConversationReference] + var followUp: ConversationFollowUp? + var suggestedTitle: String? + var errorMessage: String? + var steps: [ConversationProgressStep] + } + + func toTurnItem() -> TurnItem { + let turnItemData = TurnItemData( + content: self.content, + rating: self.rating, + references: self.references, + followUp: self.followUp, + suggestedTitle: self.suggestedTitle, + errorMessage: self.errorMessage, + steps: self.steps + ) + + // TODO: handle exception + let encoder = JSONEncoder() + let encodeData = (try? encoder.encode(turnItemData)) ?? Data() + let data = String(data: encodeData, encoding: .utf8) ?? "{}" + + return TurnItem(id: self.id, conversationID: self.chatTabID, CLSTurnID: self.clsTurnID, role: role.rawValue, data: data, createdAt: self.createdAt, updatedAt: self.updatedAt) + } + + static func from(_ turnItem: TurnItem) -> ChatMessage? { + var chatMessage: ChatMessage? = nil + + do { + if let jsonData = turnItem.data.data(using: .utf8) { + let decoder = JSONDecoder() + let turnItemData = try decoder.decode(TurnItemData.self, from: jsonData) + + chatMessage = .init( + id: turnItem.id, + chatTabID: turnItem.conversationID, + clsTurnID: turnItem.CLSTurnID, + role: ChatMessage.Role(rawValue: turnItem.role)!, + content: turnItemData.content, + references: turnItemData.references, + followUp: turnItemData.followUp, + suggestedTitle: turnItemData.suggestedTitle, + errorMessage: turnItemData.errorMessage, + rating: turnItemData.rating, + steps: turnItemData.steps, + createdAt: turnItem.createdAt, + updatedAt: turnItem.updatedAt + ) + } + } catch { + Logger.client.error("Failed to restore chat message: \(error)") + } + + return chatMessage + } +} + +extension Array where Element == ChatMessage { + func toTurnItems() -> [TurnItem] { + return self.map { $0.toTurnItem() } + } +} diff --git a/Core/Sources/Service/XPCService.swift b/Core/Sources/Service/XPCService.swift index a2ef2ca7..0f86a8cd 100644 --- a/Core/Sources/Service/XPCService.swift +++ b/Core/Sources/Service/XPCService.swift @@ -21,6 +21,8 @@ public class XPCService: NSObject, XPCServiceProtocol { Task { reply(await Status.shared.getAXStatus()) } + public func getXPCServiceAccessibilityPermission(withReply reply: @escaping (Bool) -> Void) { + reply(AXIsProcessTrusted()) } // MARK: - Suggestion diff --git a/Core/Sources/SuggestionWidget/ChatPanelWindow.swift b/Core/Sources/SuggestionWidget/ChatPanelWindow.swift index 3f1b772c..55137fb5 100644 --- a/Core/Sources/SuggestionWidget/ChatPanelWindow.swift +++ b/Core/Sources/SuggestionWidget/ChatPanelWindow.swift @@ -39,6 +39,7 @@ final class ChatPanelWindow: NSWindow { isOpaque = false backgroundColor = .clear level = widgetLevel(1) + level = .init(NSWindow.Level.floating.rawValue + 1) collectionBehavior = [ .fullScreenAuxiliary, .transient, diff --git a/Core/Sources/SuggestionWidget/ChatWindow/ChatLoginView.swift b/Core/Sources/SuggestionWidget/ChatWindow/ChatLoginView.swift new file mode 100644 index 00000000..871dd24e --- /dev/null +++ b/Core/Sources/SuggestionWidget/ChatWindow/ChatLoginView.swift @@ -0,0 +1,91 @@ +import SwiftUI +import Perception +import GitHubCopilotViewModel +import SharedUIComponents + +struct ChatLoginView: View { + @StateObject var viewModel: GitHubCopilotViewModel + @Environment(\.openURL) private var openURL + + var body: some View { + WithPerceptionTracking { + VStack(spacing: 0){ + VStack(spacing: 24) { + Spacer() + VStack(spacing: 8) { + Image("CopilotLogo") + .resizable() + .renderingMode(.template) + .scaledToFill() + .frame(width: 60.0, height: 60.0) + .foregroundColor(.secondary) + + Text("Welcome to Copilot") + .font(.largeTitle) + .multilineTextAlignment(.center) + + Text("Your AI-powered coding assistant") + .font(.body) + .multilineTextAlignment(.center) + } + + CopilotIntroView() + + VStack(spacing: 8) { + Button("Sign Up for Copilot Free") { + if let url = URL(string: "https://github.com/features/copilot/plans") { + openURL(url) + } + } + .buttonStyle(.borderedProminent) + + HStack{ + Text("Already have an account?") + Button("Sign In") { viewModel.signIn() } + .buttonStyle(.borderless) + .foregroundColor(Color("TextLinkForegroundColor")) + + if viewModel.isRunningAction || viewModel.waitingForSignIn { + ProgressView() + .controlSize(.small) + } + } + } + .padding(.top, 16) + + Spacer() + Text("Copilot Free and Copilot Pro may show [public code](https://aka.ms/github-copilot-match-public-code) suggestions and collect telemetry. You can change these [GitHub settings](https://aka.ms/github-copilot-settings) at any time. By continuing, you agree to our [terms](https://github.com/customer-terms/github-copilot-product-specific-terms) and [privacy policy](https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement).") + .font(.system(size: 12)) + } + .padding() + .frame( + maxWidth: .infinity, + maxHeight: .infinity + ) + } + .xcodeStyleFrame(cornerRadius: 10) + .ignoresSafeArea(edges: .top) + .alert( + viewModel.signInResponse?.userCode ?? "", + isPresented: $viewModel.isSignInAlertPresented, + presenting: viewModel.signInResponse + ) { _ in + Button("Cancel", role: .cancel, action: {}) + Button("Copy Code and Open", action: viewModel.copyAndOpen) + } message: { response in + Text(""" + Please enter the above code in the GitHub website \ + to authorize your GitHub account with Copilot for Xcode. + + \(response?.verificationURL.absoluteString ?? "") + """) + } + } + } +} + +struct ChatLoginView_Previews: PreviewProvider { + static var previews: some View { + ChatLoginView(viewModel: GitHubCopilotViewModel.shared) + } +} diff --git a/Core/Sources/SuggestionWidget/ChatWindow/ChatNoAXPermissionView.swift b/Core/Sources/SuggestionWidget/ChatWindow/ChatNoAXPermissionView.swift new file mode 100644 index 00000000..299c46cc --- /dev/null +++ b/Core/Sources/SuggestionWidget/ChatWindow/ChatNoAXPermissionView.swift @@ -0,0 +1,55 @@ +import SwiftUI +import Perception +import SharedUIComponents + +struct ChatNoAXPermissionView: View { + @Environment(\.openURL) private var openURL + + var body: some View { + WithPerceptionTracking { + VStack(spacing: 0) { + VStack(alignment: .center, spacing: 20) { + Spacer() + Image("CopilotError") + .resizable() + .renderingMode(.template) + .scaledToFill() + .frame(width: 64.0, height: 64.0) + .foregroundColor(.primary) + + Text("Accessibility Permission Required") + .font(.largeTitle) + .multilineTextAlignment(.center) + + Text("Please grant accessibility permission for Github Copilot to work with Xcode.") + .font(.body) + .multilineTextAlignment(.center) + + HStack{ + Button("Open Permission Settings") { + if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") { + openURL(url) + } + } + .buttonStyle(.borderedProminent) + } + + Spacer() + } + .padding() + .frame( + maxWidth: .infinity, + maxHeight: .infinity + ) + } + .xcodeStyleFrame(cornerRadius: 10) + .ignoresSafeArea(edges: .top) + } + } +} + +struct ChatNoAXPermission_Previews: PreviewProvider { + static var previews: some View { + ChatNoAXPermissionView() + } +} diff --git a/Core/Sources/SuggestionWidget/ChatWindow/ChatNoWorkspaceView.swift b/Core/Sources/SuggestionWidget/ChatWindow/ChatNoWorkspaceView.swift new file mode 100644 index 00000000..8d7cbf60 --- /dev/null +++ b/Core/Sources/SuggestionWidget/ChatWindow/ChatNoWorkspaceView.swift @@ -0,0 +1,48 @@ +import SwiftUI +import Perception +import SharedUIComponents + +struct ChatNoWorkspaceView: View { + var body: some View { + WithPerceptionTracking { + VStack(spacing: 0) { + VStack(alignment: .center, spacing: 32) { + Spacer() + VStack (alignment: .center, spacing: 8) { + Image("CopilotLogo") + .resizable() + .renderingMode(.template) + .scaledToFill() + .frame(width: 64.0, height: 64.0) + .foregroundColor(.secondary) + + Text("No Active Xcode Workspace") + .font(.largeTitle) + .multilineTextAlignment(.center) + + Text("To use Copilot, open Xcode with an active workspace in focus") + .font(.body) + .multilineTextAlignment(.center) + } + + CopilotIntroView() + + Spacer() + } + .padding() + .frame( + maxWidth: .infinity, + maxHeight: .infinity + ) + } + .xcodeStyleFrame(cornerRadius: 10) + .ignoresSafeArea(edges: .top) + } + } +} + +struct ChatNoWorkspace_Previews: PreviewProvider { + static var previews: some View { + ChatNoWorkspaceView() + } +} diff --git a/Core/Sources/SuggestionWidget/ChatWindow/CopilotIntroView.swift b/Core/Sources/SuggestionWidget/ChatWindow/CopilotIntroView.swift new file mode 100644 index 00000000..5fcd5d2e --- /dev/null +++ b/Core/Sources/SuggestionWidget/ChatWindow/CopilotIntroView.swift @@ -0,0 +1,110 @@ +import SwiftUI +import Perception +import SharedUIComponents + +struct CopilotIntroView: View { + var body: some View { + WithPerceptionTracking { + VStack(alignment: .center, spacing: 8) { + CopilotIntroItemView( + imageName: "CopilotLogo", + title: "Inline Code Suggestion", + description: "Receive context-aware code suggestions and text completion in Xcode. Press Tab ⇥ to accept." + ) + + CopilotIntroItemView( + systemImage: "option", + title: "Full Suggestions", + description: "Press Option ⌥ for multi-line suggestions (first line is inline). Use Copilot Chat to refine and explain." + ) + + CopilotIntroItemView( + imageName: "ChatIcon", + title: "Chat", + description: "Get real-time coding assistance, debug issues, and generate code snippets directly within Xcode." + ) + + CopilotIntroItemView( + imageName: "GitHubMark", + title: "GitHub Context", + description: "Copilot gives smarter code suggestions with GitHub and project context. Use chat to discuss, debug, and explain your code." + ) + } + .padding(0) + .frame(maxWidth: .infinity, alignment: .center) + } + } +} + +struct CopilotIntroItemView: View { + let image: Image + let title: String + let description: String + + public init(imageName: String, title: String, description: String) { + self.init( + imageObject: Image(imageName), + title: title, + description: description + ) + } + + public init(systemImage: String, title: String, description: String) { + self.init( + imageObject: Image(systemName: systemImage), + title: title, + description: description + ) + } + + public init(imageObject: Image, title: String, description: String) { + self.image = imageObject + self.title = title + self.description = description + } + + var body: some View { + WithPerceptionTracking { + VStack(alignment: .leading, spacing: 0){ + HStack(alignment: .center, spacing: 8) { + image + .resizable() + .renderingMode(.template) + .scaledToFill() + .frame(width: 12, height: 12) + .foregroundColor(.primary) + .padding(.leading, 8) + + Text(title) + .font(.body) + .kerning(0.096) + .multilineTextAlignment(.center) + .foregroundColor(.primary) + } + .frame(maxWidth: .infinity, alignment: .leading) + + Text(description) + .font(.body) + .foregroundColor(.secondary) + .padding(.leading, 28) + .padding(.top, 4) + .frame(maxWidth: .infinity, alignment: .topLeading) + } + .padding(8) + .frame(maxWidth: 360, alignment: .top) + .background(.primary.opacity(0.1)) + .cornerRadius(2) + .overlay( + RoundedRectangle(cornerRadius: 2) + .inset(by: 0.5) + .stroke(lineWidth: 0) + ) + } + } +} + +struct CopilotIntroView_Previews: PreviewProvider { + static var previews: some View { + CopilotIntroView() + } +} diff --git a/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift b/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift index be6c2e68..64a8d804 100644 --- a/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift +++ b/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift @@ -282,6 +282,7 @@ public struct WidgetFeature { await send(.updateColorScheme) let stream = AsyncStream { continuation in userDefaultsObservers.xcodeColorSchemeChangeObserver.onChange = { + userDefaultsObservers.colorSchemeChangeObserver.onChange = { continuation.yield() } @@ -297,6 +298,8 @@ public struct WidgetFeature { continuation.onTermination = { _ in userDefaultsObservers.xcodeColorSchemeChangeObserver.onChange = {} + continuation.onTermination = { _ in + userDefaultsObservers.colorSchemeChangeObserver.onChange = {} userDefaultsObservers.systemColorSchemeChangeObserver.onChange = {} } } @@ -315,12 +318,14 @@ public struct WidgetFeature { let xcodePref = UserDefaults(suiteName: "com.apple.dt.Xcode")! .value(forKey: "IDEAppearance") as? Int ?? 0 let xcodeColorScheme: XcodeColorScheme = .init(rawValue: xcodePref) ?? .system + let widgetColorScheme = UserDefaults.shared.value(for: \.widgetColorScheme) let systemColorScheme: ColorScheme = NSApp.effectiveAppearance.name == .darkAqua ? .dark : .light let scheme: ColorScheme = { switch (xcodeColorScheme, systemColorScheme) { + switch (widgetColorScheme, systemColorScheme) { case (.system, .dark), (.dark, _): return .dark case (.system, .light), (.light, _): diff --git a/Core/Sources/SuggestionWidget/ModuleDependency.swift b/Core/Sources/SuggestionWidget/ModuleDependency.swift index fb0652b2..bafc01a3 100644 --- a/Core/Sources/SuggestionWidget/ModuleDependency.swift +++ b/Core/Sources/SuggestionWidget/ModuleDependency.swift @@ -34,6 +34,13 @@ public final class WidgetUserDefaultsObservers { object: UserDefaults.standard, forKeyPaths: ["AppleInterfaceStyle"], context: nil + let colorSchemeChangeObserver = UserDefaultsObserver( + object: UserDefaults.shared, forKeyPaths: [ + UserDefaultPreferenceKeys().widgetColorScheme.key, + ], context: nil + ) + let systemColorSchemeChangeObserver = UserDefaultsObserver( + object: UserDefaults.standard, forKeyPaths: ["AppleInterfaceStyle"], context: nil ) public init() {} diff --git a/Core/Sources/SuggestionWidget/WidgetWindowsController.swift b/Core/Sources/SuggestionWidget/WidgetWindowsController.swift index 72ad0576..f5798262 100644 --- a/Core/Sources/SuggestionWidget/WidgetWindowsController.swift +++ b/Core/Sources/SuggestionWidget/WidgetWindowsController.swift @@ -635,6 +635,7 @@ public final class WidgetWindows { it.backgroundColor = .clear it.level = .floating it.collectionBehavior = [.fullScreenAuxiliary, .transient, .canJoinAllSpaces] + it.collectionBehavior = [.fullScreenAuxiliary, .transient] it.hasShadow = true it.contentView = NSHostingView( rootView: WidgetView( @@ -662,6 +663,8 @@ public final class WidgetWindows { it.backgroundColor = .clear it.level = widgetLevel(2) it.collectionBehavior = [.fullScreenAuxiliary, .transient, .canJoinAllSpaces] + it.level = .init(NSWindow.Level.floating.rawValue + 2) + it.collectionBehavior = [.fullScreenAuxiliary, .transient] it.hasShadow = true it.contentView = NSHostingView( rootView: SharedPanelView( @@ -696,6 +699,8 @@ public final class WidgetWindows { it.backgroundColor = .clear it.level = widgetLevel(2) it.collectionBehavior = [.fullScreenAuxiliary, .transient, .canJoinAllSpaces] + it.level = .init(NSWindow.Level.floating.rawValue + 2) + it.collectionBehavior = [.fullScreenAuxiliary, .transient] it.hasShadow = false it.contentView = NSHostingView( rootView: SuggestionPanelView( @@ -743,6 +748,8 @@ public final class WidgetWindows { it.backgroundColor = .clear it.level = widgetLevel(0) it.collectionBehavior = [.fullScreenAuxiliary, .transient, .canJoinAllSpaces] + it.level = .floating + it.collectionBehavior = [.fullScreenAuxiliary, .transient] it.hasShadow = false it.contentView = NSHostingView( rootView: ToastPanelView(store: store.scope( diff --git a/Core/Sources/UpdateChecker/UpdateChecker.swift b/Core/Sources/UpdateChecker/UpdateChecker.swift index e477817d..6c9aa772 100644 --- a/Core/Sources/UpdateChecker/UpdateChecker.swift +++ b/Core/Sources/UpdateChecker/UpdateChecker.swift @@ -31,6 +31,24 @@ public final class UpdateChecker: UpdateCheckerProtocol { delegate: delegate ) delegate.updateCheckerDelegate = checkerDelegate +public final class UpdateChecker { + let updater: SPUUpdater + let hostBundleFound: Bool + let delegate = UpdaterDelegate() + + public init(hostBundle: Bundle?) { + if hostBundle == nil { + hostBundleFound = false + Logger.updateChecker.error("Host bundle not found") + } else { + hostBundleFound = true + } + updater = SPUUpdater( + hostBundle: hostBundle ?? Bundle.main, + applicationBundle: Bundle.main, + userDriver: SPUStandardUserDriver(hostBundle: hostBundle ?? Bundle.main, delegate: nil), + delegate: delegate + ) do { try updater.start() } catch { @@ -53,6 +71,9 @@ public final class UpdateChecker: UpdateCheckerProtocol { public func setAutomaticallyChecksForUpdates(_ value: Bool) { updater.automaticallyChecksForUpdates = value + public var automaticallyChecksForUpdates: Bool { + get { updater.automaticallyChecksForUpdates } + set { updater.automaticallyChecksForUpdates = newValue } } } diff --git a/Core/Sources/XcodeThemeController/PreferenceKey+Theme.swift b/Core/Sources/XcodeThemeController/PreferenceKey+Theme.swift index 0c696384..86e789fb 100644 --- a/Core/Sources/XcodeThemeController/PreferenceKey+Theme.swift +++ b/Core/Sources/XcodeThemeController/PreferenceKey+Theme.swift @@ -18,6 +18,7 @@ public extension UserDefaultPreferenceKeys { var darkXcodeTheme: PreferenceKey> { .init(defaultValue: .init(nil), key: "DarkXcodeTheme") + .init(defaultValue: .init(nil), key: "LightXcodeTheme") } var lastSyncedHighlightJSThemeCreatedAt: PreferenceKey { diff --git a/Core/Sources/XcodeThemeController/XcodeThemeController.swift b/Core/Sources/XcodeThemeController/XcodeThemeController.swift index 2f4f0fc1..de81c073 100644 --- a/Core/Sources/XcodeThemeController/XcodeThemeController.swift +++ b/Core/Sources/XcodeThemeController/XcodeThemeController.swift @@ -8,6 +8,13 @@ public class XcodeThemeController { var syncTriggerTask: Task? // to be removed public init() { +import XcodeInspector + +public class XcodeThemeController { + var syncTriggerTask: Task? + + public init(syncTriggerTask: Task? = nil) { + self.syncTriggerTask = syncTriggerTask } public func start() { @@ -24,6 +31,9 @@ public class XcodeThemeController { return } + syncXcodeThemeIfNeeded() + + syncTriggerTask?.cancel() syncTriggerTask = Task { [weak self] in let notifications = NSWorkspace.shared.notificationCenter .notifications(named: NSWorkspace.didActivateApplicationNotification) @@ -50,6 +60,7 @@ public class XcodeThemeController { extension XcodeThemeController { func syncXcodeThemeIfNeeded(forceRefresh: Bool = false) { + func syncXcodeThemeIfNeeded() { guard UserDefaults.shared.value(for: \.syncSuggestionHighlightTheme) || UserDefaults.shared.value(for: \.syncPromptToCodeHighlightTheme) || UserDefaults.shared.value(for: \.syncChatCodeHighlightTheme) @@ -73,6 +84,7 @@ extension XcodeThemeController { light: false, in: directories.themeDirectory, forceRefresh: forceRefresh + in: directories.themeDirectory ) } @@ -84,6 +96,7 @@ extension XcodeThemeController { light: true, in: directories.themeDirectory, forceRefresh: forceRefresh + in: directories.themeDirectory ) } } @@ -99,6 +112,10 @@ extension XcodeThemeController { Logger.service.error("Xcode theme not found: \(xcodeThemeName)") return } + in directoryURL: URL + ) { + let targetName = light ? "highlightjs-light" : "highlightjs-dark" + guard let xcodeThemeURL = locateXcodeTheme(named: xcodeThemeName) else { return } let targetThemeURL = directoryURL.appendingPathComponent(targetName) let lastSyncTimestamp = UserDefaults.shared .value(for: \.lastSyncedHighlightJSThemeCreatedAt) @@ -177,6 +194,7 @@ extension XcodeThemeController { } } catch { Logger.service.error("Failed to sync Xcode theme \"\(xcodeThemeName)\": \(error)") + print(error.localizedDescription) } } } diff --git a/Core/Tests/ChatServiceTests/ChatServiceTests.swift b/Core/Tests/ChatServiceTests/ChatServiceTests.swift new file mode 100644 index 00000000..f8b5ec26 --- /dev/null +++ b/Core/Tests/ChatServiceTests/ChatServiceTests.swift @@ -0,0 +1,22 @@ +import XCTest + +@testable import ChatService + +final class ReplaceFirstWordTests: XCTestCase { + func test_replace_first_word() { + let cases: [(String, String)] = [ + ("", ""), + ("workspace 001", "workspace 001"), + ("workspace001", "workspace001"), + ("@workspace", "@project"), + ("@workspace001", "@workspace001"), + ("@workspace 001", "@project 001"), + ] + + for (input, expected) in cases { + let result = replaceFirstWord(in: input, from: "@workspace", to: "@project") + XCTAssertEqual(result, expected, "Input: \(input), Expected: \(expected), Result: \(result)") + } + } +} + diff --git a/Core/Tests/KeyBindingManagerTests/TabToAcceptSuggestionTests.swift b/Core/Tests/KeyBindingManagerTests/TabToAcceptSuggestionTests.swift index 70469700..ac9bd922 100644 --- a/Core/Tests/KeyBindingManagerTests/TabToAcceptSuggestionTests.swift +++ b/Core/Tests/KeyBindingManagerTests/TabToAcceptSuggestionTests.swift @@ -16,11 +16,13 @@ class TabToAcceptSuggestionTests: XCTestCase { hasFocusedEditor: true ) assertEqual( + XCTAssertTrue( TabToAcceptSuggestion.shouldAcceptSuggestion( event: CGEvent(keyboardEventSource: nil, virtualKey: 48, keyDown: true)!, workspacePool: workspacePool, xcodeInspector: xcodeInspector ), (true, nil) + ) ) } @@ -35,6 +37,7 @@ class TabToAcceptSuggestionTests: XCTestCase { hasFocusedEditor: true ) assertEqual( + XCTAssertFalse( TabToAcceptSuggestion.shouldAcceptSuggestion( event: CGEvent(keyboardEventSource: nil, virtualKey: 48, keyDown: true)!, workspacePool: workspacePool, @@ -58,6 +61,7 @@ class TabToAcceptSuggestionTests: XCTestCase { workspacePool: workspacePool, xcodeInspector: xcodeInspector ), (false, "No filespace") + ) ) } @@ -72,11 +76,13 @@ class TabToAcceptSuggestionTests: XCTestCase { hasFocusedEditor: false ) assertEqual( + XCTAssertFalse( TabToAcceptSuggestion.shouldAcceptSuggestion( event: CGEvent(keyboardEventSource: nil, virtualKey: 48, keyDown: true)!, workspacePool: workspacePool, xcodeInspector: xcodeInspector ), (false, "No focused editor") + ) ) } @@ -91,11 +97,13 @@ class TabToAcceptSuggestionTests: XCTestCase { hasFocusedEditor: true ) assertEqual( + XCTAssertFalse( TabToAcceptSuggestion.shouldAcceptSuggestion( event: createEvent(48), workspacePool: workspacePool, xcodeInspector: xcodeInspector ), (false, "No active Xcode") + ) ) } @@ -110,11 +118,13 @@ class TabToAcceptSuggestionTests: XCTestCase { hasFocusedEditor: true ) assertEqual( + XCTAssertFalse( TabToAcceptSuggestion.shouldAcceptSuggestion( event: createEvent(48), workspacePool: workspacePool, xcodeInspector: xcodeInspector ), (false, "No active document") + ) ) } @@ -129,11 +139,13 @@ class TabToAcceptSuggestionTests: XCTestCase { hasFocusedEditor: true ) assertEqual( + XCTAssertFalse( TabToAcceptSuggestion.shouldAcceptSuggestion( event: createEvent(48, flags: .maskShift), workspacePool: workspacePool, xcodeInspector: xcodeInspector ), (false, nil) + ) ) } @@ -148,11 +160,13 @@ class TabToAcceptSuggestionTests: XCTestCase { hasFocusedEditor: true ) assertEqual( + XCTAssertFalse( TabToAcceptSuggestion.shouldAcceptSuggestion( event: createEvent(48, flags: .maskCommand), workspacePool: workspacePool, xcodeInspector: xcodeInspector ), (false, nil) + ) ) } @@ -167,11 +181,13 @@ class TabToAcceptSuggestionTests: XCTestCase { hasFocusedEditor: true ) assertEqual( + XCTAssertFalse( TabToAcceptSuggestion.shouldAcceptSuggestion( event: createEvent(48, flags: .maskControl), workspacePool: workspacePool, xcodeInspector: xcodeInspector ), (false, nil) + ) ) } @@ -186,11 +202,13 @@ class TabToAcceptSuggestionTests: XCTestCase { hasFocusedEditor: true ) assertEqual( + XCTAssertFalse( TabToAcceptSuggestion.shouldAcceptSuggestion( event: createEvent(48, flags: .maskHelp), workspacePool: workspacePool, xcodeInspector: xcodeInspector ), (false, nil) + ) ) } @@ -205,11 +223,13 @@ class TabToAcceptSuggestionTests: XCTestCase { hasFocusedEditor: true ) assertEqual( + XCTAssertFalse( TabToAcceptSuggestion.shouldAcceptSuggestion( event: createEvent(50), workspacePool: workspacePool, xcodeInspector: xcodeInspector ), (false, nil) + ) ) } } @@ -244,6 +264,9 @@ private class FakeWorkspacePool: WorkspacePool { self.fileURL = fileURL self.filespace = Filespace(fileURL: fileURL, onSave: {_ in }, onClose: {_ in }) if skipSuggestion { return } + func setTestFile(fileURL: URL) { + self.fileURL = fileURL + self.filespace = Filespace(fileURL: fileURL, onSave: {_ in }, onClose: {_ in }) guard let filespace = self.filespace else { return } filespace.setSuggestions([.init(id: "id", text: "test", position: .zero, range: .zero)]) } diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 877cdd92..69c1ecb4 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -11,6 +11,23 @@ sudo ln -s `which node` /usr/local/bin For context, this is used by an Xcode run script as part of the build. Run scripts use a very limited path to resolve commands. +``` + +## Local Language Server + +To run the language server locally create a `Config.local.xcconfig` file with two config values: + +```xcconfig +LANGUAGE_SERVER_PATH=~/code/copilot-client +NODE_PATH=/opt/path/to/node +``` + +`LANGUAGE_SERVER_PATH` should point to the path where the copilot-client repo is +checked out and `$(LANGUAGE_SERVER_PATH)/dist/language-server.js` must exist +(run `npm run build`). + +`NODE_PATH` should point to where node is installed. It can be omitted if +`/usr/bin/env node` will resolves directly. ## Targets @@ -68,3 +85,4 @@ The source code mostly follows the [Ray Wenderlich Style Guide](https://github.c ## App Versioning The app version and all targets' version in controlled by `Version.xcconfig`. +The app version and all targets' version in controlled by `Version.xcconfig`. diff --git a/ExtensionService/AppDelegate+Menu.swift b/ExtensionService/AppDelegate+Menu.swift index 9453d310..9691d2dc 100644 --- a/ExtensionService/AppDelegate+Menu.swift +++ b/ExtensionService/AppDelegate+Menu.swift @@ -15,6 +15,10 @@ extension AppDelegate { .init("xcodeInspectorDebugMenu") } + fileprivate var accessibilityAPIPermissionMenuItemIdentifier: NSUserInterfaceItemIdentifier { + .init("accessibilityAPIPermissionMenuItem") + } + fileprivate var sourceEditorDebugMenu: NSUserInterfaceItemIdentifier { .init("sourceEditorDebugMenu") } @@ -25,6 +29,8 @@ extension AppDelegate { fileprivate var toggleIgnoreLanguageMenuItemIdentifier: NSUserInterfaceItemIdentifier { .init("toggleIgnoreLanguageMenuItem") + fileprivate var copilotStatusMenuItemIdentifier: NSUserInterfaceItemIdentifier { + .init("copilotStatusMenuItem") } @MainActor @@ -49,6 +55,7 @@ extension AppDelegate { ) let openCopilotForXcodeItem = NSMenuItem( + let openCopilotForXcode = NSMenuItem( title: "Open \(hostAppName) Settings", action: #selector(openCopilotForXcode), keyEquivalent: "" @@ -71,6 +78,12 @@ extension AppDelegate { keyEquivalent: "" ) statusMenuItem.isHidden = true + let accessibilityAPIPermission = NSMenuItem( + title: "Accessibility Permission: N/A", + action: nil, + keyEquivalent: "" + ) + accessibilityAPIPermission.identifier = accessibilityAPIPermissionMenuItemIdentifier let quitItem = NSMenuItem( title: "Quit", @@ -98,6 +111,12 @@ extension AppDelegate { action: #selector(openCopilotForXcode), keyEquivalent: "" ) + let copilotStatus = NSMenuItem( + title: "Copilot Connection: Checking...", + action: nil, + keyEquivalent: "" + ) + copilotStatus.identifier = copilotStatusMenuItemIdentifier let openDocs = NSMenuItem( title: "View Copilot Documentation...", @@ -119,6 +138,13 @@ extension AppDelegate { statusBarMenu.addItem(.separator()) statusBarMenu.addItem(authMenuItem) statusBarMenu.addItem(statusMenuItem) + statusBarMenu.addItem(openCopilotForXcode) + statusBarMenu.addItem(.separator()) + statusBarMenu.addItem(checkForUpdate) + statusBarMenu.addItem(toggleCompletions) + statusBarMenu.addItem(.separator()) + statusBarMenu.addItem(copilotStatus) + statusBarMenu.addItem(accessibilityAPIPermission) statusBarMenu.addItem(.separator()) statusBarMenu.addItem(openDocs) statusBarMenu.addItem(openForum) @@ -160,6 +186,22 @@ extension AppDelegate: NSMenuDelegate { } } + if let accessibilityAPIPermission = menu.items.first(where: { item in + item.identifier == accessibilityAPIPermissionMenuItemIdentifier + }) { + AXIsProcessTrusted() + accessibilityAPIPermission.title = + "Accessibility Permission: \(AXIsProcessTrusted() ? "Granted" : "Not Granted")" + } + + statusChecker.updateStatusInBackground(notify: { (status: String, isOk: Bool) in + if let statusItem = menu.items.first(where: { item in + item.identifier == self.copilotStatusMenuItemIdentifier + }) { + statusItem.title = "Copilot Connection: \(isOk ? "Connected" : status)" + } + }) + case xcodeInspectorDebugMenuIdentifier: let inspector = XcodeInspector.shared menu.items.removeAll() diff --git a/ExtensionService/AppDelegate.swift b/ExtensionService/AppDelegate.swift index dd4e8889..a8989e07 100644 --- a/ExtensionService/AppDelegate.swift +++ b/ExtensionService/AppDelegate.swift @@ -42,6 +42,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { var xpcExtensionService: XPCExtensionService? private var cancellables = Set() private var progressView: NSProgressIndicator? + var xpcController: XPCController? + let updateChecker = + UpdateChecker( + hostBundle: locateHostBundleURL(url: Bundle.main.bundleURL) + .flatMap(Bundle.init(url:)) + ) + let statusChecker: AuthStatusChecker = AuthStatusChecker() + var xpcExtensionService: XPCExtensionService? + private var cancellables = Set() func applicationDidFinishLaunching(_: Notification) { if ProcessInfo.processInfo.environment["IS_UNIT_TEST"] == "YES" { return } @@ -52,6 +61,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { ] as CFDictionary) setupQuitOnUpdate() setupQuitOnUserTerminated() + setupQuitOnFeatureFlag() xpcController = .init() Logger.service.info("XPC Service started.") NSApp.setActivationPolicy(.accessory) @@ -77,6 +87,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { task.arguments = [appPath.absoluteString] task.launch() task.waitUntilExit() + if let appPath = locateHostBundleURL(url: Bundle.main.bundleURL)?.absoluteString { + task.launchPath = "/usr/bin/open" + task.arguments = [appPath] + task.launch() + task.waitUntilExit() + } } @objc func openGlobalChat() { @@ -138,6 +154,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { } } + func setupQuitOnFeatureFlag() { + FeatureFlagNotifierImpl.shared.featureFlagsDidChange.sink { [weak self] (flags) in + if flags.x != true { + Logger.service.info("Xcode feature flag not granted, quitting.") + self?.quit() + } + }.store(in: &cancellables) + } + func requestAccessoryAPIPermission() { AXIsProcessTrustedWithOptions([ kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true, @@ -262,6 +287,7 @@ extension NSRunningApplication { } func locateHostBundleURL(url: URL) -> URL { +func locateHostBundleURL(url: URL) -> URL? { var nextURL = url while nextURL.path != "/" { nextURL = nextURL.deletingLastPathComponent() diff --git a/ExtensionService/Assets.xcassets/MenuBarIcon.imageset/copilot-16.png b/ExtensionService/Assets.xcassets/MenuBarIcon.imageset/copilot-16.png index 6add79dba31a7fe7b8a7e550203dcec34fa9e187..d6daae313f886525369c5889cb5586091b9b07f1 100644 GIT binary patch literal 1756 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47?U!codYtHlYs&gODF90J{%x& z)PHr*1*;A|Ss9~F=PQ@E$_g{IMO+O+SXT$ns^iS9QPB)46b{$?As=xtvS33;NI_wN zfD60e-8oj8J2EQnO%J)Hbnp1nzZP?fcg7r$4v^IT#-#Cb+C-ltOSWFw^`yGFeO~aY zv`s4(>2Uq~yZiO$i(l{gcSQQ`E)%%vA2a=&-t@A0Ou_YkW3SFXIZNvA{VR^Wl41`g zc23Rh=DepWXPBY+Xw`!~r;j&X)|+oVZ)(b%AJZZ(ab>mb@YGpu{iTL|dROY&t<5*? z&A%Ex{iw{Vz%z-$Y7$)mfsHJz59|M2?z8XHEecNv(CcT?IpyLhD7@*&=L-uHg^UV0 z9A8b?QT)1&f9>y+xw-E}XXF-~)4z4giP>WA!pT=}->P7dkYdPtM3jL1G$3_8Ni*SN`doKH9_Y5|Nq}`_T(&}&jU(={D5f`1laBC=GNNj zE&sgC@x-i*f;-HMOL|MWwmVD`IU6E)Qb4r--QoPCts7$&RY=U-^h=?Nm&xXqJkyFJ z>cT6pT%n*SKP@vSRiUJ^AXNdJdn|gV21oWi zHQ@O3UGo{ojH{M?+dYc^bMxGrXSDWv)LoB>!UtRfCw$m`?L+#E32YK=hM6V?7r1O} z7x?bpxKKe_xk+WGigH8g*Md9ePdB-#Dl_*nF~#^lc>TS+torH)m%Ns@2Cu*7%?w&T zrDg4;ZHJbA%-#8P#)p4gVs$|u6C)#!dSn*g>^T|zc^glzrf?xU%b6HOi@MwYEJ9{y ztv%1(XTQRhd8WU^|J?zXm^*42x&znjX5e#sC44z{=X{5&{Nj5)urFA4^~sk%n~WP) zEsAw{VQ_0X^M?Jazo*G7@J?QjE_F7c;L5}{TGvIjObO_S5mwW z-4*+++08g-uB}W^DX*8|_YU~OYqLFa-}#s39=6YX@y6f3DVrypW@_THF@g1)19ft znT$W31b^sE-Pe$$$gdT?YHO9V>Fia3>qE6ugYIO#_S=5_b=j}CjM`G0J{^$hJ+3Sq z*W7+2rzY8G>zbD=_Qq+mKA&lBjnn(ebWimB$xE+$GjBKK&Yrz?8^`Xy4_Ej-|LiHt z|6BG>?)2Ah>-NX$|9|b$e?0z@{9W!3}Y!c78C$;j%Qx7o{j?0wMI|2 z?4JYFBL%Vtq$ee{IJqdZpd>RtkHICgxF9F7QX#;&zjF5;pehDWS3j3^P6ic7 literal 1863 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 Lw4>IVyPy9XIR?jC literal 2663 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 N*5(doWhUsj{{a&TeQE#z diff --git a/README.md b/README.md index 586350b8..25f0f59d 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,16 @@ extension that provides inline coding suggestions as you type. ## Beta Preview Policy Use of the GitHub Copilot Xcode Extension is subject to [GitHub's Pre-Release Terms](https://docs.github.com/en/site-policy/github-terms/github-pre-release-license-terms). We want to remind you that: +# GitHub Copilot For Xcode + +Demo of GitHub Copilot for Xcode + +GitHub Copilot for Xcode is macOS application and Xcode extension that enables +using GitHub Copilot code completions in Xcode. + +## Beta Preview Policy + +As per [GitHub's Terms of Service](https://docs.github.com/en/github/site-policy/github-terms-of-service#j-beta-previews) we want to remind you that: > Beta Previews may not be supported or may change at any time. You may receive confidential information through those programs that must remain confidential while the program is private. We'd love your feedback to make our Beta Previews better. @@ -97,6 +107,39 @@ Use of the GitHub Copilot Xcode Extension is subject to [GitHub's Pre-Release Te

Screenshot of welcome screen

+1. Download the latest `dmg` from: https://github.com/github/copilot-xcode/releases/latest/download/GitHubCopilotForXcode.dmg + Updates can be downloaded and installed by the app. + +1. Open the `dmg` and drag the `GitHub Copilot for Xcode.app` into the `Applications` folder. +
+ Screenshot of opened dmg + +1. On the first opening the application it will warn that it was downloaded from the internet. Click `Open` to proceed. +
+ Screenshot of downloaded from the internet warning + +1. A background item will be added for the application to be able to start itself when Xcode starts. +
+ Screenshot of background item + +1. Two important permissions are required for the application to operate well: `Accessibility` and `Xcode Source Editor Extension`. The first time the application is run these permissions should be requested. You may need to click `Refresh` in the settings if not prompted. +
+ Screenshot of accessibility permission request + Screenshot of accessibility permission + Screenshot of extension permission + +1. After granting the extension permission, please restart Xcode so the `Github Copilot` menu is available under the Xcode `Editor` menu. +
+
+ Screenshot of Xcode Editor GitHub Copilot menu item + +1. To sign into GitHub Copilot, click the `Sign in` button in the settings application. This will open a browser window and copy a code to the clipboard. Paste the code into the GitHub login page and authorize the application. +
+ Screenshot of sign-in popup + +1. To install updates, click `Check for Updates` from the menu item or in the settings application. After installing a new version, Xcode must be restarted to use the new version correctly. New versions can also be installed from `dmg` files downloaded from the releases page. When installing a new version via `dmg`, the application must be run manually the first time to accept the downloaded from the internet warning. +
+ Screenshot of update message ## License @@ -110,6 +153,7 @@ We follow responsible practices in accordance with our To get the latest security fixes, please use the latest version of the GitHub Copilot for Xcode. +refer to [MIT](./LICENSE.txt) for the full terms. ## Support @@ -123,3 +167,6 @@ Thank you to @intitni for creating the original project that this is based on. Attributions can be found under About when running the app or in [Credits.rtf](./Copilot%20for%20Xcode/Credits.rtf). +## Acknowledgement + +Thank you to @intitni for creating the original that this project is based on. diff --git a/Script/clipboard.txt b/Script/clipboard.txt new file mode 100644 index 00000000..d781f161 --- /dev/null +++ b/Script/clipboard.txt @@ -0,0 +1,70 @@ +import SwiftUI + +struct ContentView: View { + @State private var output = "Ready to uninstall Copilot for Xcode.\n" + @State private var isRunning = false + @State private var dryRun = true + + var body: some View { + VStack(spacing: 20) { + Text("Copilot for Xcode Uninstaller") + .font(.title2) + + Toggle("Dry Run (Preview Only)", isOn: $dryRun) + .padding(.horizontal) + + ScrollView { + Text(output) + .frame(maxWidth: .infinity, alignment: .leading) + .padding() + .background(Color(.systemGray6)) + .cornerRadius(8) + } + .frame(height: 300) + + Button(action: runScript) { + Text(isRunning ? "Running..." : "Uninstall") + .frame(maxWidth: .infinity) + } + .disabled(isRunning) + .buttonStyle(.borderedProminent) + .padding(.horizontal) + + Spacer() + } + .padding() + .frame(width: 500, height: 400) + } + + func runScript() { + isRunning = true + output += "\nStarting \(dryRun ? "dry run" : "uninstall")...\n" + + guard let scriptPath = Bundle.main.path(forResource: "uninstall-app", ofType: "sh") else { + output += "Script not found.\n" + isRunning = false + return + } + + let command = dryRun ? "\(scriptPath) --dry-run" : "\(scriptPath)" + let appleScript = """ + do shell script "\(command)" with administrator privileges + """ + + DispatchQueue.global(qos: .userInitiated).async { + if let scriptObject = NSAppleScript(source: appleScript) { + var error: NSDictionary? + let result = scriptObject.executeAndReturnError(&error) + + DispatchQueue.main.async { + if let error = error { + output += "Error: \(error)\n" + } else { + output += result.stringValue ?? "Completed.\n" + } + isRunning = false + } + } + } + } +} diff --git a/Script/export-options-local.plist b/Script/export-options-local.plist new file mode 100644 index 00000000..9c4fb9f7 --- /dev/null +++ b/Script/export-options-local.plist @@ -0,0 +1,10 @@ + + + + + method + debugging + signingStyle + automatic + + \ No newline at end of file diff --git a/Script/localbuild-app.sh b/Script/localbuild-app.sh new file mode 100644 index 00000000..177c20fe --- /dev/null +++ b/Script/localbuild-app.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# Determine paths relative to script location +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_ROOT="$( cd "${SCRIPT_DIR}/.." && pwd )" +PROJECT_NAME=$(basename "${PROJECT_ROOT}") + +# Define build directory +BUILD_DIR="${PROJECT_ROOT}/build" +mkdir -p "${BUILD_DIR}" + +# Set variables +APP_NAME="CopiloForXcode" +SCHEME_NAME="Copilot for Xcode" +CONFIGURATION="Release" +ARCHIVE_PATH="${BUILD_DIR}/Archives/${APP_NAME}.xcarchive" +XCWORKSPACE_PATH="${PROJECT_ROOT}/Copilot for Xcode.xcworkspace" +EXPORT_PATH="${BUILD_DIR}/Export" +EXPORT_OPTIONS_PLIST="${PROJECT_ROOT}/Script/export-options-local.plist" + +# Clean and build archive +xcodebuild \ + -scheme "${SCHEME_NAME}" \ + -quiet \ + -archivePath "${ARCHIVE_PATH}" \ + -configuration "${CONFIGURATION}" \ + -skipMacroValidation \ + -showBuildTimingSummary \ + -disableAutomaticPackageResolution \ + -workspace "${XCWORKSPACE_PATH}" -verbose -arch arm64 \ + archive \ + APP_VERSION='0.0.0' + +# Export archive to .app +xcodebuild -exportArchive \ + -archivePath "${ARCHIVE_PATH}" \ + -exportOptionsPlist "${EXPORT_OPTIONS_PLIST}" \ + -exportPath "${EXPORT_PATH}" + +echo "App packaged successfully at ${EXPORT_PATH}/${APP_NAME}.app" + +open "${EXPORT_PATH}" \ No newline at end of file diff --git a/Script/uninstall-app.sh b/Script/uninstall-app.sh index 3cf092d6..87830bfc 100755 --- a/Script/uninstall-app.sh +++ b/Script/uninstall-app.sh @@ -32,4 +32,6 @@ defaults delete VEKTX9H2N7.group.com.github.CopilotForXcode.prefs defaults delete VEKTX9H2N7.group.dev.com.github.CopilotForXcode.prefs echo 'Finished uninstalling Copilot for Xcode' +>>>>>>>+HEAD +d' diff --git a/Server/package-lock.json b/Server/package-lock.json index 88a089c0..1e0b3f0e 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -15,6 +15,13 @@ "version": "1.245.0", "resolved": "https://registry.npmjs.org/@github/copilot-language-server/-/copilot-language-server-1.245.0.tgz", "integrity": "sha512-K/PSxLMQFhnM8CIhL0mF/FAEzB2EYqOEu0Ai0gvFEy+fMl3FvhxFkagb6w1qaQ0dQJSY5qExT88sABn3wDyHiA==", + "@github/copilot-language-server": "^1.232.0" + } + }, + "node_modules/@github/copilot-language-server": { + "version": "1.232.0", + "resolved": "https://registry.npmjs.org/@github/copilot-language-server/-/copilot-language-server-1.232.0.tgz", + "integrity": "sha512-m+CCOjpRNPRRixgFBurE7vsDmHVpCHisz+R2/V9bLwuQJ891/1LzbtjhA/162tRBLw3QmOWPSPheFLdp8D6axQ==", "bin": { "copilot-language-server": "dist/language-server.js" } diff --git a/Server/package.json b/Server/package.json index 68b97a16..08e150b2 100644 --- a/Server/package.json +++ b/Server/package.json @@ -5,5 +5,6 @@ "private": true, "dependencies": { "@github/copilot-language-server": "^1.245.0" + "@github/copilot-language-server": "^1.232.0" } } diff --git a/Tool/Package.swift b/Tool/Package.swift index 7b97c58b..bca6fd2e 100644 --- a/Tool/Package.swift +++ b/Tool/Package.swift @@ -70,6 +70,7 @@ let package = Package( // MARK: - Helpers .target(name: "XPCShared", dependencies: ["SuggestionBasic", "Logger", "Status"]), + .target(name: "XPCShared", dependencies: ["SuggestionBasic", "Logger"]), .target(name: "Configs"), diff --git a/Tool/Sources/AXExtension/AXUIElement.swift b/Tool/Sources/AXExtension/AXUIElement.swift index f32f4d44..accc5985 100644 --- a/Tool/Sources/AXExtension/AXUIElement.swift +++ b/Tool/Sources/AXExtension/AXUIElement.swift @@ -22,6 +22,7 @@ public extension AXUIElement { (try? copyValue(key: kAXValueAttribute)) ?? "" } + var intValue: Int? { (try? copyValue(key: kAXValueAttribute)) } diff --git a/Tool/Sources/ConversationServiceProvider/LSPTypes.swift b/Tool/Sources/ConversationServiceProvider/LSPTypes.swift new file mode 100644 index 00000000..b73265b3 --- /dev/null +++ b/Tool/Sources/ConversationServiceProvider/LSPTypes.swift @@ -0,0 +1,60 @@ + +// MARK: Conversation template +public struct ChatTemplate: Codable, Equatable { + public var id: String + public var description: String + public var shortDescription: String + public var scopes: [PromptTemplateScope] + + public init(id: String, description: String, shortDescription: String, scopes: [PromptTemplateScope]=[]) { + self.id = id + self.description = description + self.shortDescription = shortDescription + self.scopes = scopes + } +} + +public enum PromptTemplateScope: String, Codable, Equatable { + case chatPanel = "chat-panel" + case editPanel = "edit-panel" + case editor = "editor" + case inline = "inline" + case completion = "completion" +} + +public struct CopilotLanguageServerError: Codable { + public var code: Int? + public var message: String + public var responseIsIncomplete: Bool? + public var responseIsFiltered: Bool? +} + +// MARK: Copilot Model +public struct CopilotModel: Codable, Equatable { + public let modelFamily: String + public let modelName: String + public let id: String + public let modelPolicy: CopilotModelPolicy? + public let scopes: [PromptTemplateScope] + public let preview: Bool +} + +public struct CopilotModelPolicy: Codable, Equatable { + public let state: String + public let terms: String +} + +// MARK: Conversation Agents +public struct ChatAgent: Codable, Equatable { + public let slug: String + public let name: String + public let description: String + public let avatarUrl: String? + + public init(slug: String, name: String, description: String, avatarUrl: String?) { + self.slug = slug + self.name = name + self.description = description + self.avatarUrl = avatarUrl + } +} diff --git a/Tool/Sources/GitHubCopilotService/Conversation/WatchedFilesHandler.swift b/Tool/Sources/GitHubCopilotService/Conversation/WatchedFilesHandler.swift new file mode 100644 index 00000000..7cb38b49 --- /dev/null +++ b/Tool/Sources/GitHubCopilotService/Conversation/WatchedFilesHandler.swift @@ -0,0 +1,17 @@ +import JSONRPC +import Combine + +public protocol WatchedFilesHandler { + var onWatchedFiles: PassthroughSubject<(WatchedFilesRequest, (AnyJSONRPCResponse) -> Void), Never> { get } + func handleWatchedFiles(_ request: WatchedFilesRequest, completion: @escaping (AnyJSONRPCResponse) -> Void) +} + +public final class WatchedFilesHandlerImpl: WatchedFilesHandler { + public static let shared = WatchedFilesHandlerImpl() + + public let onWatchedFiles: PassthroughSubject<(WatchedFilesRequest, (AnyJSONRPCResponse) -> Void), Never> = .init() + + public func handleWatchedFiles(_ request: WatchedFilesRequest, completion: @escaping (AnyJSONRPCResponse) -> Void) { + onWatchedFiles.send((request, completion)) + } +} diff --git a/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotRequest.swift b/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotRequest.swift index 9b5bd1f3..bd1b110d 100644 --- a/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotRequest.swift +++ b/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotRequest.swift @@ -89,6 +89,72 @@ public func editorConfiguration() -> JSONValue { } enum GitHubCopilotRequest { +enum GitHubCopilotRequest { + // TODO migrate from setEditorInfo to didConfigurationChange + struct SetEditorInfo: GitHubCopilotRequestType { + struct Response: Codable {} + + let versionNumber = JSONValue(stringLiteral: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "") + let xcodeVersion = JSONValue(stringLiteral: SystemInfo().xcodeVersion() ?? "") + + var networkProxy: JSONValue? { + let host = UserDefaults.shared.value(for: \.gitHubCopilotProxyHost) + if host.isEmpty { return nil } + var port = UserDefaults.shared.value(for: \.gitHubCopilotProxyPort) + if port.isEmpty { port = "80" } + let username = UserDefaults.shared.value(for: \.gitHubCopilotProxyUsername) + if username.isEmpty { + return .hash([ + "host": .string(host), + "port": .number(Double(Int(port) ?? 80)), + "rejectUnauthorized": .bool(UserDefaults.shared + .value(for: \.gitHubCopilotUseStrictSSL)), + ]) + } else { + return .hash([ + "host": .string(host), + "port": .number(Double(Int(port) ?? 80)), + "rejectUnauthorized": .bool(UserDefaults.shared + .value(for: \.gitHubCopilotUseStrictSSL)), + "username": .string(username), + "password": .string(UserDefaults.shared + .value(for: \.gitHubCopilotProxyPassword)), + + ]) + } + } + + var authProvider: JSONValue? { + var dict: [String: JSONValue] = [:] + let enterpriseURI = UserDefaults.shared.value(for: \.gitHubCopilotEnterpriseURI) + if !enterpriseURI.isEmpty { + dict["url"] = .string(enterpriseURI) + } + + if dict.isEmpty { return nil } + return .hash(dict) + } + + var request: ClientRequest { + var dict: [String: JSONValue] = [ + "editorInfo": .hash([ + "name": "Xcode", + "version": xcodeVersion, + ]), + "editorPluginInfo": .hash([ + "name": "copilot-xcode", + "version": versionNumber, + ]), + ] + + dict["authProvider"] = authProvider + dict["networkProxy"] = networkProxy + + return .custom("setEditorInfo", .hash(dict)) + } + + } + struct GetVersion: GitHubCopilotRequestType { struct Response: Codable { var version: String diff --git a/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift b/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift index e6d64c1f..bd40b4cf 100644 --- a/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift +++ b/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift @@ -152,6 +152,7 @@ public class GitHubCopilotBaseService { } else { ["HOME": home] } + let environment: [String: String] = ["HOME": home] #endif let executionParams = Process.ExecutionParameters( @@ -180,6 +181,10 @@ public class GitHubCopilotBaseService { return InitializeParams( processId: Int(ProcessInfo.processInfo.processIdentifier), + clientInfo: .init( + name: "copilot-xcode", + version: "1.5.0.5206-nightly" + ), locale: nil, rootPath: projectRootURL.path, rootUri: projectRootURL.path, @@ -192,6 +197,7 @@ public class GitHubCopilotBaseService { "name": "copilot-xcode", "version": versionNumber, ] + ], ], capabilities: capabilities, trace: .off, @@ -224,6 +230,11 @@ public class GitHubCopilotBaseService { .init(settings: editorConfiguration()) ) ) + _ = try? await server.sendRequest(GitHubCopilotRequest.SetEditorInfo()) + + for await _ in notifications { + guard self != nil else { return } + _ = try? await server.sendRequest(GitHubCopilotRequest.SetEditorInfo()) } } } @@ -242,6 +253,7 @@ public class GitHubCopilotBaseService { Bundle.main .object(forInfoDictionaryKey: "APPLICATION_SUPPORT_FOLDER") as? String ?? "com.github.CopilotForXcode" + .object(forInfoDictionaryKey: "APPLICATION_SUPPORT_FOLDER") as! String ) else { throw CancellationError() } @@ -313,6 +325,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, func sendRequest(maxTry: Int = 5) async throws -> [CodeSuggestion] { do { let completions = try await self + let completions = try await server .sendRequest(GitHubCopilotRequest.InlineCompletion(doc: .init( textDocument: .init(uri: fileURL.path, version: 1), position: cursorPosition, @@ -411,6 +424,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, workspaceFolder: workspaceFolder) do { let _ = try await sendRequest( + let _ = try await server.sendRequest( GitHubCopilotRequest.CreateConversation(params: params) ) } catch { @@ -424,6 +438,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, do { let params = TurnCreateParams(workDoneToken: workDoneToken, conversationId: conversationId, message: message, doc: doc) let _ = try await sendRequest( + let _ = try await server.sendRequest( GitHubCopilotRequest.CreateTurn(params: params) ) } catch { @@ -437,6 +452,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, do { let params = ConversationRatingParams(turnId: turnId, rating: rating) let _ = try await sendRequest( + let _ = try await server.sendRequest( GitHubCopilotRequest.ConversationRating(params: params) ) } catch { @@ -449,6 +465,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, let params = CopyCodeParams(turnId: turnId, codeBlockIndex: codeBlockIndex, copyType: copyType, copiedCharacters: copiedCharacters, totalCharacters: totalCharacters, copiedText: copiedText) do { let _ = try await sendRequest( + let _ = try await server.sendRequest( GitHubCopilotRequest.CopyCode(params: params) ) } catch { @@ -472,6 +489,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, @GitHubCopilotSuggestionActor public func notifyShown(_ completion: CodeSuggestion) async { _ = try? await sendRequest( + _ = try? await server.sendRequest( GitHubCopilotRequest.NotifyShown(completionUUID: completion.id) ) } @@ -479,6 +497,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, @GitHubCopilotSuggestionActor public func notifyAccepted(_ completion: CodeSuggestion, acceptedLength: Int? = nil) async { _ = try? await sendRequest( + _ = try? await server.sendRequest( GitHubCopilotRequest.NotifyAccepted(completionUUID: completion.id, acceptedLength: acceptedLength) ) } @@ -486,6 +505,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, @GitHubCopilotSuggestionActor public func notifyRejected(_ completions: [CodeSuggestion]) async { _ = try? await sendRequest( + _ = try? await server.sendRequest( GitHubCopilotRequest.NotifyRejected(completionUUIDs: completions.map(\.id)) ) } @@ -498,6 +518,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, let languageId = languageIdentifierFromFileURL(fileURL) let uri = "file://\(fileURL.path)" // Logger.service.debug("Open \(uri), \(content.count)") +// Logger.service.debug("Open \(uri), \(content.count)") try await server.sendNotification( .didOpenTextDocument( DidOpenTextDocumentParams( @@ -520,6 +541,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, ) async throws { let uri = "file://\(fileURL.path)" // Logger.service.debug("Change \(uri), \(content.count)") +// Logger.service.debug("Change \(uri), \(content.count)") try await server.sendNotification( .didChangeTextDocument( DidChangeTextDocumentParams( @@ -539,6 +561,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, public func notifySaveTextDocument(fileURL: URL) async throws { let uri = "file://\(fileURL.path)" // Logger.service.debug("Save \(uri)") +// Logger.service.debug("Save \(uri)") try await server.sendNotification(.didSaveTextDocument(.init(uri: uri))) } @@ -546,6 +569,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, public func notifyCloseTextDocument(fileURL: URL) async throws { let uri = "file://\(fileURL.path)" // Logger.service.debug("Close \(uri)") +// Logger.service.debug("Close \(uri)") try await server.sendNotification(.didCloseTextDocument(.init(uri: uri))) } @@ -560,6 +584,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, let response = try await sendRequest(GitHubCopilotRequest.CheckStatus()) await updateServiceAuthStatus(response) return response.status + return try await server.sendRequest(GitHubCopilotRequest.CheckStatus()).status } catch let error as ServerError { throw GitHubCopilotError.languageServerError(error) } catch { @@ -599,6 +624,10 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, public func signInInitiate() async throws -> (verificationUri: String, userCode: String) { do { let result = try await sendRequest(GitHubCopilotRequest.SignInInitiate()) + @GitHubCopilotSuggestionActor + public func signInInitiate() async throws -> (verificationUri: String, userCode: String) { + do { + let result = try await server.sendRequest(GitHubCopilotRequest.SignInInitiate()) return (result.verificationUri, result.userCode) } catch let error as ServerError { throw GitHubCopilotError.languageServerError(error) @@ -613,6 +642,11 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, { do { let result = try await sendRequest(GitHubCopilotRequest.SignInConfirm(userCode: userCode)) + -> (username: String, status: GitHubCopilotAccountStatus) + { + do { + let result = try await server + .sendRequest(GitHubCopilotRequest.SignInConfirm(userCode: userCode)) return (result.user, result.status) } catch let error as ServerError { throw GitHubCopilotError.languageServerError(error) @@ -625,6 +659,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, public func signOut() async throws -> GitHubCopilotAccountStatus { do { return try await sendRequest(GitHubCopilotRequest.SignOut()).status + return try await server.sendRequest(GitHubCopilotRequest.SignOut()).status } catch let error as ServerError { throw GitHubCopilotError.languageServerError(error) } catch { @@ -636,6 +671,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, public func version() async throws -> String { do { return try await sendRequest(GitHubCopilotRequest.GetVersion()).version + return try await server.sendRequest(GitHubCopilotRequest.GetVersion()).version } catch let error as ServerError { throw GitHubCopilotError.languageServerError(error) } catch { diff --git a/Tool/Sources/GitHubCopilotService/LanguageServer/ServerRequestHandler.swift b/Tool/Sources/GitHubCopilotService/LanguageServer/ServerRequestHandler.swift new file mode 100644 index 00000000..4fc5bb81 --- /dev/null +++ b/Tool/Sources/GitHubCopilotService/LanguageServer/ServerRequestHandler.swift @@ -0,0 +1,55 @@ +import Foundation +import Combine +import JSONRPC +import LanguageClient +import LanguageServerProtocol +import Logger + +protocol ServerRequestHandler { + func handleRequest(_ request: AnyJSONRPCRequest, callback: @escaping (AnyJSONRPCResponse) -> Void) +} + +class ServerRequestHandlerImpl : ServerRequestHandler { + public static let shared = ServerRequestHandlerImpl() + private let conversationContextHandler: ConversationContextHandler = ConversationContextHandlerImpl.shared + private let watchedFilesHandler: WatchedFilesHandler = WatchedFilesHandlerImpl.shared + + func handleRequest(_ request: AnyJSONRPCRequest, callback: @escaping (AnyJSONRPCResponse) -> Void) { + let methodName = request.method + do { + switch methodName { + case "conversation/context": + let params = try JSONEncoder().encode(request.params) + let contextParams = try JSONDecoder().decode(ConversationContextParams.self, from: params) + conversationContextHandler.handleConversationContext( + ConversationContextRequest(id: request.id, method: request.method, params: contextParams), + completion: callback) + + case "copilot/watchedFiles": + let params = try JSONEncoder().encode(request.params) + let watchedFilesParams = try JSONDecoder().decode(WatchedFilesParams.self, from: params) + watchedFilesHandler.handleWatchedFiles(WatchedFilesRequest(id: request.id, method: request.method, params: watchedFilesParams), completion: callback) + + default: + break + } + } catch { + handleError(request, error: error, callback: callback) + } + } + + private func handleError(_ request: AnyJSONRPCRequest, error: Error, callback: @escaping (AnyJSONRPCResponse) -> Void) { + callback( + AnyJSONRPCResponse( + id: request.id, + result: JSONValue.array([ + JSONValue.null, + JSONValue.hash([ + "code": .number(-32602/* Invalid params */), + "message": .string("Error: \(error.localizedDescription)")]) + ]) + ) + ) + Logger.gitHubCopilot.error(error) + } +} diff --git a/Tool/Sources/GitHubCopilotService/Services/FeatureFlagNotifier.swift b/Tool/Sources/GitHubCopilotService/Services/FeatureFlagNotifier.swift index f14572ac..67297873 100644 --- a/Tool/Sources/GitHubCopilotService/Services/FeatureFlagNotifier.swift +++ b/Tool/Sources/GitHubCopilotService/Services/FeatureFlagNotifier.swift @@ -5,6 +5,7 @@ public struct FeatureFlags: Hashable, Codable { public var rt: Bool public var sn: Bool public var chat: Bool + public var x: Bool? public var xc: Bool? } diff --git a/Tool/Sources/HostAppActivator/HostAppActivator.swift b/Tool/Sources/HostAppActivator/HostAppActivator.swift new file mode 100644 index 00000000..0f540ac9 --- /dev/null +++ b/Tool/Sources/HostAppActivator/HostAppActivator.swift @@ -0,0 +1,121 @@ +import Foundation +import AppKit +import Logger + +public let HostAppURL = locateHostBundleURL(url: Bundle.main.bundleURL) + +public extension Notification.Name { + static let openSettingsWindowRequest = Notification + .Name("com.github.CopilotForXcode.OpenSettingsWindowRequest") +} + +public enum GitHubCopilotForXcodeSettingsLaunchError: Error, LocalizedError { + case appNotFound + case openFailed(errorDescription: String) + + public var errorDescription: String? { + switch self { + case .appNotFound: + return "\(hostAppName()) settings application not found" + case let .openFailed(errorDescription): + return "Failed to launch \(hostAppName()) settings (\(errorDescription))" + } + } +} + +public func getRunningHostApp() -> NSRunningApplication? { + return NSWorkspace.shared.runningApplications.first(where: { + $0.bundleIdentifier == (Bundle.main.object(forInfoDictionaryKey: "BUNDLE_IDENTIFIER_BASE") as! String) + }) +} + +public func launchHostAppSettings() throws { + // Try the AppleScript approach first, but only if app is already running + if let hostApp = getRunningHostApp() { + let activated = hostApp.activate(options: [.activateIgnoringOtherApps]) + Logger.ui.info("\(hostAppName()) activated: \(activated)") + + let scriptSuccess = tryLaunchWithAppleScript() + + // If AppleScript fails, fall back to notification center + if !scriptSuccess { + DistributedNotificationCenter.default().postNotificationName( + .openSettingsWindowRequest, + object: nil + ) + Logger.ui.info("\(hostAppName()) settings notification sent after activation") + return + } + } else { + // If app is not running, launch it with the settings flag + try launchHostAppWithArgs(args: ["--settings"]) + } +} + +private func tryLaunchWithAppleScript() -> Bool { + // Try to launch settings using AppleScript + let script = """ + tell application "\(hostAppName())" + activate + tell application "System Events" + keystroke "," using command down + end tell + end tell + """ + + var error: NSDictionary? + if let scriptObject = NSAppleScript(source: script) { + scriptObject.executeAndReturnError(&error) + + // Log the result + if let error = error { + Logger.ui.info("\(hostAppName()) settings script error: \(error)") + return false + } + + Logger.ui.info("\(hostAppName()) settings opened successfully via AppleScript") + return true + } + + return false +} + +public func launchHostAppDefault() throws { + try launchHostAppWithArgs(args: nil) +} + +func launchHostAppWithArgs(args: [String]?) throws { + guard let appURL = HostAppURL else { + throw GitHubCopilotForXcodeSettingsLaunchError.appNotFound + } + + Task { + let configuration = NSWorkspace.OpenConfiguration() + if let args { + configuration.arguments = args + } + configuration.activates = true + + try await NSWorkspace.shared + .openApplication(at: appURL, configuration: configuration) + } +} + +func locateHostBundleURL(url: URL) -> URL? { + var nextURL = url + while nextURL.path != "/" { + nextURL = nextURL.deletingLastPathComponent() + if nextURL.lastPathComponent.hasSuffix(".app") { + return nextURL + } + } + let devAppURL = url + .deletingLastPathComponent() + .appendingPathComponent("GitHub Copilot for Xcode Dev.app") + return devAppURL +} + +func hostAppName() -> String { + return Bundle.main.object(forInfoDictionaryKey: "HOST_APP_NAME") as? String + ?? "GitHub Copilot for Xcode" +} diff --git a/Tool/Sources/Preferences/Keys.swift b/Tool/Sources/Preferences/Keys.swift index 3e4a4c1a..bf0adacd 100644 --- a/Tool/Sources/Preferences/Keys.swift +++ b/Tool/Sources/Preferences/Keys.swift @@ -554,4 +554,31 @@ public extension UserDefaultPreferenceKeys { var verboseLoggingEnabled: PreferenceKey { .init(defaultValue: false, key: "VerboseLoggingEnabled") } +// MARK: - Feature + +public extension UserDefaultPreferenceKeys { + + var gitHubCopilotProxyHost: PreferenceKey { + .init(defaultValue: "", key: "GitHubCopilotProxyHost") + } + + var gitHubCopilotProxyPort: PreferenceKey { + .init(defaultValue: "", key: "GitHubCopilotProxyPort") + } + + var gitHubCopilotUseStrictSSL: PreferenceKey { + .init(defaultValue: true, key: "GitHubCopilotUseStrictSSL") + } + + var gitHubCopilotProxyUsername: PreferenceKey { + .init(defaultValue: "", key: "GitHubCopilotProxyUsername") + } + + var gitHubCopilotProxyPassword: PreferenceKey { + .init(defaultValue: "", key: "GitHubCopilotProxyPassword") + } + + var gitHubCopilotEnterpriseURI: PreferenceKey { + .init(defaultValue: "", key: "GitHubCopilotEnterpriseURI") + } } diff --git a/Tool/Sources/SharedUIComponents/AsyncCodeBlock.swift b/Tool/Sources/SharedUIComponents/AsyncCodeBlock.swift index 250f198a..ea01c848 100644 --- a/Tool/Sources/SharedUIComponents/AsyncCodeBlock.swift +++ b/Tool/Sources/SharedUIComponents/AsyncCodeBlock.swift @@ -166,6 +166,7 @@ public struct AsyncCodeBlock: View { @ScaledMetric var iconPadding: CGFloat = 9.0 @ScaledMetric var iconSpacing: CGFloat = 6.0 @ScaledMetric var optionPadding: CGFloat = 0.5 + @ScaledMetric var ellipsisPadding: CGFloat = 5 @ViewBuilder var contentView: some View { @@ -210,6 +211,30 @@ public struct AsyncCodeBlock: View { .frame(height: lineHeight) .background(lineBackgroundShape(lines.count > 1)) .padding(.leading, firstLineIndent) + Image(systemName: "ellipsis") + .renderingMode(.template) + .foregroundColor(foregroundTextColor) + .padding(.horizontal, ellipsisPadding) + .background( + RoundedRectangle(cornerRadius: 12) + .fill(Color.gray.opacity(isExpanded ? 0.1 : 0.4)) + .frame(height: fontHeight * 0.75) + ) + .popover(isPresented: $isHovering) { + Text(hintText) + .font(.body) + .padding(8) + .fixedSize() + } + .task { + isHovering = !completionHintShown + completionHintShown = true + } + } + } + .background(currentLineBackgroundColor ?? backgroundColor) + .padding(.leading, firstLineIndent) + .frame(minHeight: lineHeight) .onHover { hovering in guard hovering != isHovering else { return } withAnimation { diff --git a/Tool/Sources/SharedUIComponents/InstructionView.swift b/Tool/Sources/SharedUIComponents/InstructionView.swift new file mode 100644 index 00000000..87eea3d0 --- /dev/null +++ b/Tool/Sources/SharedUIComponents/InstructionView.swift @@ -0,0 +1,42 @@ +import ComposableArchitecture +import SwiftUI + +public struct Instruction: View { + public init() {} + + public var body: some View { + WithPerceptionTracking { + VStack { + VStack(spacing: 24) { + + VStack(spacing: 16) { + Image("CopilotLogo") + .resizable() + .renderingMode(.template) + .scaledToFill() + .frame(width: 60.0, height: 60.0) + .foregroundColor(.secondary) + + Text("Copilot is powered by AI, so mistakes are possible. Review output carefully before use.") + .font(.system(size: 14, weight: .light)) + .multilineTextAlignment(.center) + .lineSpacing(4) + } + + VStack(alignment: .leading, spacing: 8) { + Label("to reference context", systemImage: "paperclip") + .foregroundColor(Color("DescriptionForegroundColor")) + .font(.system(size: 14)) + Text("@ to chat with extensions") + .foregroundColor(Color("DescriptionForegroundColor")) + .font(.system(size: 14)) + Text("Type / to use commands") + .foregroundColor(Color("DescriptionForegroundColor")) + .font(.system(size: 14)) + } + } + }.frame(maxWidth: 350) + } + } +} + diff --git a/Tool/Sources/SharedUIComponents/SyntaxHighlighting.swift b/Tool/Sources/SharedUIComponents/SyntaxHighlighting.swift index 1de8b686..adf63e56 100644 --- a/Tool/Sources/SharedUIComponents/SyntaxHighlighting.swift +++ b/Tool/Sources/SharedUIComponents/SyntaxHighlighting.swift @@ -88,6 +88,10 @@ public enum CodeHighlighting { in: line, options: [], range: NSMakeRange(0, ns.length) + if regex.firstMatch( + in: line, + options: [], + range: NSMakeRange(0, line.utf16.count) ) != nil { return true } diff --git a/Tool/Sources/Workspace/FileChangeWatcher/FSEventProvider.swift b/Tool/Sources/Workspace/FileChangeWatcher/FSEventProvider.swift new file mode 100644 index 00000000..8057b106 --- /dev/null +++ b/Tool/Sources/Workspace/FileChangeWatcher/FSEventProvider.swift @@ -0,0 +1,59 @@ +import Foundation + +protocol FSEventProvider { + func createEventStream( + paths: CFArray, + latency: CFTimeInterval, + flags: UInt32, + callback: @escaping FSEventStreamCallback, + context: UnsafeMutablePointer + ) -> FSEventStreamRef? + + func startStream(_ stream: FSEventStreamRef) + func stopStream(_ stream: FSEventStreamRef) + func invalidateStream(_ stream: FSEventStreamRef) + func releaseStream(_ stream: FSEventStreamRef) + func setDispatchQueue(_ stream: FSEventStreamRef, queue: DispatchQueue) +} + +class FileChangeWatcherFSEventProvider: FSEventProvider { + init() {} + + func createEventStream( + paths: CFArray, + latency: CFTimeInterval, + flags: UInt32, + callback: @escaping FSEventStreamCallback, + context: UnsafeMutablePointer + ) -> FSEventStreamRef? { + return FSEventStreamCreate( + kCFAllocatorDefault, + callback, + context, + paths, + FSEventStreamEventId(kFSEventStreamEventIdSinceNow), + latency, + flags + ) + } + + func startStream(_ stream: FSEventStreamRef) { + FSEventStreamStart(stream) + } + + func stopStream(_ stream: FSEventStreamRef) { + FSEventStreamStop(stream) + } + + func invalidateStream(_ stream: FSEventStreamRef) { + FSEventStreamInvalidate(stream) + } + + func releaseStream(_ stream: FSEventStreamRef) { + FSEventStreamRelease(stream) + } + + func setDispatchQueue(_ stream: FSEventStreamRef, queue: DispatchQueue) { + FSEventStreamSetDispatchQueue(stream, queue) + } +} diff --git a/Tool/Sources/Workspace/FileChangeWatcher/FileChangeWatcher.swift b/Tool/Sources/Workspace/FileChangeWatcher/FileChangeWatcher.swift new file mode 100644 index 00000000..fbc181a6 --- /dev/null +++ b/Tool/Sources/Workspace/FileChangeWatcher/FileChangeWatcher.swift @@ -0,0 +1,392 @@ +import Foundation +import System +import Logger +import CoreServices +import LanguageServerProtocol +import XcodeInspector + +public typealias PublisherType = (([FileEvent]) -> Void) + +protocol FileChangeWatcher { + func onFileCreated(file: URL) + func onFileChanged(file: URL) + func onFileDeleted(file: URL) + + func addPaths(_ paths: [URL]) + func removePaths(_ paths: [URL]) +} + +public final class BatchingFileChangeWatcher: FileChangeWatcher { + private var watchedPaths: [URL] + private let changePublisher: PublisherType + private let publishInterval: TimeInterval + + private var pendingEvents: [FileEvent] = [] + private var timer: Timer? + private let eventQueue: DispatchQueue + private let fsEventQueue: DispatchQueue + private var eventStream: FSEventStreamRef? + private(set) public var isWatching = false + + // Dependencies injected for testing + private let fsEventProvider: FSEventProvider + + public var paths: [URL] { watchedPaths } + + init( + watchedPaths: [URL], + changePublisher: @escaping PublisherType, + publishInterval: TimeInterval = 3.0, + fsEventProvider: FSEventProvider = FileChangeWatcherFSEventProvider() + ) { + self.watchedPaths = watchedPaths + self.changePublisher = changePublisher + self.publishInterval = publishInterval + self.fsEventProvider = fsEventProvider + self.eventQueue = DispatchQueue(label: "com.github.copilot.filechangewatcher") + self.fsEventQueue = DispatchQueue(label: "com.github.copilot.filechangewatcherfseventstream", qos: .utility) + + self.start() + } + + private func updateWatchedPaths(_ paths: [URL]) { + guard isWatching, paths != watchedPaths else { return } + stopWatching() + watchedPaths = paths + _ = startWatching() + } + + public func addPaths(_ paths: [URL]) { + let newPaths = paths.filter { !watchedPaths.contains($0) } + if !newPaths.isEmpty { + let updatedPaths = watchedPaths + newPaths + updateWatchedPaths(updatedPaths) + } + } + + public func removePaths(_ paths: [URL]) { + let updatedPaths = watchedPaths.filter { !paths.contains($0) } + if updatedPaths.count != watchedPaths.count { + updateWatchedPaths(updatedPaths) + } + } + + internal func start() { + guard !isWatching else { return } + + guard self.startWatching() else { + Logger.client.info("Failed to start watching for: \(watchedPaths)") + return + } + self.startPublishTimer() + isWatching = true + } + + deinit { + stopWatching() + self.timer?.invalidate() + } + + internal func startPublishTimer() { + guard self.timer == nil else { return } + + Task { @MainActor [weak self] in + guard let self else { return } + self.timer = Timer.scheduledTimer(withTimeInterval: self.publishInterval, repeats: true) { [weak self] _ in + self?.publishChanges() + } + } + } + + internal func addEvent(file: URL, type: FileChangeType) { + eventQueue.async { + self.pendingEvents.append(FileEvent(uri: file.absoluteString, type: type)) + } + } + + public func onFileCreated(file: URL) { + addEvent(file: file, type: .created) + } + + public func onFileChanged(file: URL) { + addEvent(file: file, type: .changed) + } + + public func onFileDeleted(file: URL) { + addEvent(file: file, type: .deleted) + } + + private func publishChanges() { + eventQueue.async { + guard !self.pendingEvents.isEmpty else { return } + + var compressedEvent: [String: FileEvent] = [:] + for event in self.pendingEvents { + let existingEvent = compressedEvent[event.uri] + + guard existingEvent != nil else { + compressedEvent[event.uri] = event + continue + } + + if event.type == .deleted { /// file deleted. Cover created and changed event + compressedEvent[event.uri] = event + } else if event.type == .created { /// file created. Cover deleted and changed event + compressedEvent[event.uri] = event + } else if event.type == .changed { + if existingEvent?.type != .created { /// file changed. Won't cover created event + compressedEvent[event.uri] = event + } + } + } + + let changes = Array(compressedEvent.values) + self.pendingEvents.removeAll() + + if !changes.isEmpty { + DispatchQueue.main.async { + self.changePublisher(changes) + } + } + } + } + + /// Starts watching for file changes in the project + private func startWatching() -> Bool { + var isEventStreamStarted = false + + var context = FSEventStreamContext() + context.info = Unmanaged.passUnretained(self).toOpaque() + + let paths = watchedPaths.map { $0.path } as CFArray + let flags = UInt32( + kFSEventStreamCreateFlagFileEvents | + kFSEventStreamCreateFlagNoDefer | + kFSEventStreamCreateFlagWatchRoot + ) + + eventStream = fsEventProvider.createEventStream( + paths: paths, + latency: 1, // 1 second latency, + flags: flags, + callback: { _, clientCallbackInfo, numEvents, eventPaths, eventFlags, _ in + guard let clientCallbackInfo = clientCallbackInfo else { return } + let watcher = Unmanaged.fromOpaque(clientCallbackInfo).takeUnretainedValue() + watcher.processEvent(numEvents: numEvents, eventPaths: eventPaths, eventFlags: eventFlags) + }, + context: &context + ) + + if let eventStream = eventStream { + fsEventProvider.setDispatchQueue(eventStream, queue: fsEventQueue) + fsEventProvider.startStream(eventStream) + isEventStreamStarted = true + } + + return isEventStreamStarted + } + + /// Stops watching for file changes + internal func stopWatching() { + guard isWatching, let eventStream = eventStream else { return } + + fsEventProvider.stopStream(eventStream) + fsEventProvider.invalidateStream(eventStream) + fsEventProvider.releaseStream(eventStream) + self.eventStream = nil + isWatching = false + + Logger.client.info("Stoped watching for file changes in \(watchedPaths)") + } + + public func processEvent(numEvents: CFIndex, eventPaths: UnsafeRawPointer, eventFlags: UnsafePointer) { + let pathsPtr = eventPaths.bindMemory(to: UnsafeMutableRawPointer.self, capacity: numEvents) + + for i in 0.. Bool { + if let resourceValues = try? url.resourceValues(forKeys: [.isRegularFileKey, .isDirectoryKey]), + resourceValues.isDirectory == true { return true } + + if supportedFileExtensions.contains(url.pathExtension.lowercased()) == false { return true } + + if WorkspaceFile.isXCProject(url) || WorkspaceFile.isXCWorkspace(url) { return true } + + if WorkspaceFile.matchesPatterns(url, patterns: skipPatterns) { return true } + + // TODO: check if url is ignored by git / ide + + return false + } +} + +public class FileChangeWatcherService { + internal var watcher: BatchingFileChangeWatcher? + /// for watching projects added or removed + private var timer: Timer? + private var projectWatchingInterval: TimeInterval = 3.0 + + private(set) public var workspaceURL: URL + private(set) public var publisher: PublisherType + + // Dependencies injected for testing + internal let workspaceFileProvider: WorkspaceFileProvider + internal let watcherFactory: ([URL], @escaping PublisherType) -> BatchingFileChangeWatcher + + public init( + _ workspaceURL: URL, + publisher: @escaping PublisherType, + publishInterval: TimeInterval = 3.0, + projectWatchingInterval: TimeInterval = 3.0, + workspaceFileProvider: WorkspaceFileProvider = FileChangeWatcherWorkspaceFileProvider(), + watcherFactory: (([URL], @escaping PublisherType) -> BatchingFileChangeWatcher)? = nil + ) { + self.workspaceURL = workspaceURL + self.publisher = publisher + self.workspaceFileProvider = workspaceFileProvider + self.watcherFactory = watcherFactory ?? { projectURLs, publisher in + BatchingFileChangeWatcher(watchedPaths: projectURLs, changePublisher: publisher, publishInterval: publishInterval) + } + } + + deinit { + self.watcher = nil + self.timer?.invalidate() + } + + internal func startWatchingProject() { + guard timer == nil else { return } + + Task { @MainActor [weak self] in + guard let self else { return } + + self.timer = Timer.scheduledTimer(withTimeInterval: self.projectWatchingInterval, repeats: true) { [weak self] _ in + guard let self, let watcher = self.watcher else { return } + + let watchingProjects = Set(watcher.paths) + let projects = Set(self.workspaceFileProvider.getSubprojectURLs(in: self.workspaceURL)) + + /// find added projects + let addedProjects = projects.subtracting(watchingProjects) + self.onProjectAdded(Array(addedProjects)) + + /// find removed projects + let removedProjects = watchingProjects.subtracting(projects) + self.onProjectRemoved(Array(removedProjects)) + } + } + } + + public func startWatching() { + guard workspaceURL.path != "/" else { return } + + guard watcher == nil else { return } + + let projects = workspaceFileProvider.getSubprojectURLs(in: workspaceURL) + + watcher = watcherFactory(projects, publisher) + Logger.client.info("Started watching for file changes in \(projects)") + + startWatchingProject() + } + + internal func onProjectAdded(_ projectURLs: [URL]) { + guard let watcher = watcher, projectURLs.count > 0 else { return } + + watcher.addPaths(projectURLs) + + Logger.client.info("Started watching for file changes in \(projectURLs)") + + /// sync all the files as created in the project when added + for projectURL in projectURLs { + let files = workspaceFileProvider.getFilesInActiveWorkspace( + workspaceURL: projectURL, + workspaceRootURL: projectURL + ) + publisher(files.map { .init(uri: $0.url.absoluteString, type: .created) }) + } + } + + internal func onProjectRemoved(_ projectURLs: [URL]) { + guard let watcher = watcher, projectURLs.count > 0 else { return } + + watcher.removePaths(projectURLs) + + Logger.client.info("Stopped watching for file changes in \(projectURLs)") + + /// sync all the files as deleted in the project when removed + for projectURL in projectURLs { + let files = workspaceFileProvider.getFilesInActiveWorkspace(workspaceURL: projectURL, workspaceRootURL: projectURL) + publisher(files.map { .init(uri: $0.url.absoluteString, type: .deleted) }) + } + } +} + +@globalActor +public enum PoolActor: GlobalActor { + public actor Actor {} + public static let shared = Actor() +} + +public class FileChangeWatcherServicePool { + + public static let shared = FileChangeWatcherServicePool() + private var servicePool: [URL: FileChangeWatcherService] = [:] + + private init() {} + + @PoolActor + public func watch(for workspaceURL: URL, publisher: @escaping PublisherType) { + guard workspaceURL.path != "/" else { return } + + var validWorkspaceURL: URL? = nil + if WorkspaceFile.isXCWorkspace(workspaceURL) { + validWorkspaceURL = workspaceURL + } else if WorkspaceFile.isXCProject(workspaceURL) { + validWorkspaceURL = WorkspaceFile.getWorkspaceByProject(workspaceURL) + } + + guard let validWorkspaceURL else { return } + + guard servicePool[workspaceURL] == nil else { return } + + let watcherService = FileChangeWatcherService(validWorkspaceURL, publisher: publisher) + watcherService.startWatching() + + servicePool[workspaceURL] = watcherService + } +} diff --git a/Tool/Sources/Workspace/FileChangeWatcher/WorkspaceFileProvider.swift b/Tool/Sources/Workspace/FileChangeWatcher/WorkspaceFileProvider.swift new file mode 100644 index 00000000..65a3c56b --- /dev/null +++ b/Tool/Sources/Workspace/FileChangeWatcher/WorkspaceFileProvider.swift @@ -0,0 +1,29 @@ +import Foundation +import ConversationServiceProvider + +public protocol WorkspaceFileProvider { + func getSubprojectURLs(in workspaceURL: URL) -> [URL] + func getFilesInActiveWorkspace(workspaceURL: URL, workspaceRootURL: URL) -> [FileReference] + func isXCProject(_ url: URL) -> Bool + func isXCWorkspace(_ url: URL) -> Bool +} + +public class FileChangeWatcherWorkspaceFileProvider: WorkspaceFileProvider { + public init() {} + + public func getSubprojectURLs(in workspaceURL: URL) -> [URL] { + return WorkspaceFile.getSubprojectURLs(in: workspaceURL) + } + + public func getFilesInActiveWorkspace(workspaceURL: URL, workspaceRootURL: URL) -> [FileReference] { + return WorkspaceFile.getFilesInActiveWorkspace(workspaceURL: workspaceURL, workspaceRootURL: workspaceRootURL) + } + + public func isXCProject(_ url: URL) -> Bool { + return WorkspaceFile.isXCProject(url) + } + + public func isXCWorkspace(_ url: URL) -> Bool { + return WorkspaceFile.isXCWorkspace(url) + } +} diff --git a/Tool/Sources/Workspace/WorkspaceFile.swift b/Tool/Sources/Workspace/WorkspaceFile.swift new file mode 100644 index 00000000..dcc8dc39 --- /dev/null +++ b/Tool/Sources/Workspace/WorkspaceFile.swift @@ -0,0 +1,183 @@ +import Foundation +import Logger +import ConversationServiceProvider + +public let supportedFileExtensions: Set = ["swift", "m", "mm", "h", "cpp", "c", "js", "py", "rb", "java", "applescript", "scpt", "plist", "entitlements", "md", "json", "xml", "txt", "yaml", "yml"] +public let skipPatterns: [String] = [ + ".git", + ".svn", + ".hg", + "CVS", + ".DS_Store", + "Thumbs.db", + "node_modules", + "bower_components" +] + + +public struct WorkspaceFile { + + static func isXCWorkspace(_ url: URL) -> Bool { + return url.pathExtension == "xcworkspace" && FileManager.default.fileExists(atPath: url.appendingPathComponent("contents.xcworkspacedata").path) + } + + static func isXCProject(_ url: URL) -> Bool { + return url.pathExtension == "xcodeproj" && FileManager.default.fileExists(atPath: url.appendingPathComponent("project.pbxproj").path) + } + + static func getWorkspaceByProject(_ url: URL) -> URL? { + guard isXCProject(url) else { return nil } + let workspaceURL = url.appendingPathComponent("project.xcworkspace") + + return isXCWorkspace(workspaceURL) ? workspaceURL : nil + } + + static func getSubprojectURLs(workspaceURL: URL, data: Data) -> [URL] { + var subprojectURLs: [URL] = [] + do { + let xml = try XMLDocument(data: data) + let fileRefs = try xml.nodes(forXPath: "//FileRef") + for fileRef in fileRefs { + if let fileRefElement = fileRef as? XMLElement, + let location = fileRefElement.attribute(forName: "location")?.stringValue { + var path = "" + if location.starts(with: "group:") { + path = location.replacingOccurrences(of: "group:", with: "") + } else if location.starts(with: "container:") { + path = location.replacingOccurrences(of: "container:", with: "") + } else if location.starts(with: "self:") { + // Handle "self:" referece - refers to the containing project directory + var workspaceURLCopy = workspaceURL + workspaceURLCopy.deleteLastPathComponent() + if !subprojectURLs.contains(workspaceURLCopy) { + subprojectURLs.append(workspaceURLCopy) + continue + } + + } else { + // Skip absolute paths such as absolute:/path/to/project + continue + } + + if path.hasSuffix(".xcodeproj") { + path = (path as NSString).deletingLastPathComponent + } + let subprojectURL = path.isEmpty ? workspaceURL.deletingLastPathComponent() : workspaceURL.deletingLastPathComponent().appendingPathComponent(path) + if !subprojectURLs.contains(subprojectURL) { + subprojectURLs.append(subprojectURL) + } + } + } + } catch { + Logger.client.error("Failed to parse workspace file: \(error)") + } + + return subprojectURLs + } + + static func getSubprojectURLs(in workspaceURL: URL) -> [URL] { + let workspaceFile = workspaceURL.appendingPathComponent("contents.xcworkspacedata") + guard let data = try? Data(contentsOf: workspaceFile) else { + Logger.client.error("Failed to read workspace file at \(workspaceFile.path)") + return [] + } + + return getSubprojectURLs(workspaceURL: workspaceURL, data: data) + } + + static func matchesPatterns(_ url: URL, patterns: [String]) -> Bool { + let fileName = url.lastPathComponent + for pattern in patterns { + if fnmatch(pattern, fileName, 0) == 0 { + return true + } + } + return false + } + + public static func getFilesInActiveWorkspace( + workspaceURL: URL, + workspaceRootURL: URL, + shouldExcludeFile: ((URL) -> Bool)? = nil + ) -> [FileReference] { + var files: [FileReference] = [] + do { + let fileManager = FileManager.default + var subprojects: [URL] = [] + if isXCWorkspace(workspaceURL) { + subprojects = getSubprojectURLs(in: workspaceURL) + } else { + subprojects.append(workspaceRootURL) + } + for subproject in subprojects { + guard FileManager.default.fileExists(atPath: subproject.path) else { + continue + } + + let enumerator = fileManager.enumerator( + at: subproject, + includingPropertiesForKeys: [.isRegularFileKey, .isDirectoryKey], + options: [.skipsHiddenFiles] + ) + + while let fileURL = enumerator?.nextObject() as? URL { + // Skip items matching the specified pattern + if matchesPatterns(fileURL, patterns: skipPatterns) || isXCWorkspace(fileURL) || + isXCProject(fileURL) { + enumerator?.skipDescendants() + continue + } + + let resourceValues = try fileURL.resourceValues(forKeys: [.isRegularFileKey, .isDirectoryKey]) + // Handle directories if needed + if resourceValues.isDirectory == true { + continue + } + + guard resourceValues.isRegularFile == true else { continue } + if supportedFileExtensions.contains(fileURL.pathExtension.lowercased()) == false { + continue + } + + // Apply the custom file exclusion check if provided + if let shouldExcludeFile = shouldExcludeFile, + shouldExcludeFile(fileURL) { continue } + + let relativePath = fileURL.path.replacingOccurrences(of: workspaceRootURL.path, with: "") + let fileName = fileURL.lastPathComponent + + let file = FileReference(url: fileURL, relativePath: relativePath, fileName: fileName) + files.append(file) + } + } + } catch { + Logger.client.error("Failed to get files in workspace: \(error)") + } + + return files + } + + /* + used for `project-context` skill. Get filed for watching for syncing to CLS + */ + public static func getWatchedFiles( + workspaceURL: URL, + projectURL: URL, + excludeGitIgnoredFiles: Bool, + excludeIDEIgnoredFiles: Bool + ) -> [String] { + // Directly return for invalid workspace + guard workspaceURL.path != "/" else { return [] } + + // TODO: implement + let shouldExcludeFile: ((URL) -> Bool)? = nil + + let files = getFilesInActiveWorkspace( + workspaceURL: workspaceURL, + workspaceRootURL: projectURL, + shouldExcludeFile: shouldExcludeFile + ) + + return files.map { $0.url.absoluteString } + } +} diff --git a/Tool/Sources/Workspace/WorkspacePool.swift b/Tool/Sources/Workspace/WorkspacePool.swift index 96627858..b475cfbe 100644 --- a/Tool/Sources/Workspace/WorkspacePool.swift +++ b/Tool/Sources/Workspace/WorkspacePool.swift @@ -68,6 +68,12 @@ public class WorkspacePool { Logger.workspacePool.info("Multiple workspaces found with file: \(fileURL)") // If multiple workspaces are found, return the first with a suggestion return filespaces.first { $0.presentingSuggestion != nil } + for workspace in workspaces.values { + if let filespace = workspace.filespaces[fileURL] { + return filespace + } + } + return nil } @WorkspaceActor diff --git a/Tool/Sources/XPCShared/XPCExtensionService.swift b/Tool/Sources/XPCShared/XPCExtensionService.swift index 3541ab6a..6a829570 100644 --- a/Tool/Sources/XPCShared/XPCExtensionService.swift +++ b/Tool/Sources/XPCShared/XPCExtensionService.swift @@ -50,6 +50,7 @@ public class XPCExtensionService { } public func getXPCServiceAccessibilityPermission() async throws -> ObservedAXStatus { + public func getXPCServiceAccessibilityPermission() async throws -> Bool { try await withXPCServiceConnected { service, continuation in service.getXPCServiceAccessibilityPermission { isGranted in diff --git a/Tool/Sources/XPCShared/XPCServiceProtocol.swift b/Tool/Sources/XPCShared/XPCServiceProtocol.swift index c59d70b6..b859421b 100644 --- a/Tool/Sources/XPCShared/XPCServiceProtocol.swift +++ b/Tool/Sources/XPCShared/XPCServiceProtocol.swift @@ -58,6 +58,9 @@ public protocol XPCServiceProtocol { func postNotification(name: String, withReply reply: @escaping () -> Void) func send(endpoint: String, requestBody: Data, reply: @escaping (Data?, Error?) -> Void) func quit(reply: @escaping () -> Void) + func getXPCServiceAccessibilityPermission(withReply reply: @escaping (Bool) -> Void) + func postNotification(name: String, withReply reply: @escaping () -> Void) + func send(endpoint: String, requestBody: Data, reply: @escaping (Data?, Error?) -> Void) } public struct NoResponse: Codable { diff --git a/Tool/Sources/XcodeInspector/Apps/XcodeAppInstanceInspector.swift b/Tool/Sources/XcodeInspector/Apps/XcodeAppInstanceInspector.swift index 29964b12..5b45c816 100644 --- a/Tool/Sources/XcodeInspector/Apps/XcodeAppInstanceInspector.swift +++ b/Tool/Sources/XcodeInspector/Apps/XcodeAppInstanceInspector.swift @@ -375,6 +375,11 @@ extension XcodeAppInstanceInspector { return .skipDescendants } return .continueSearching + let tabBars = editArea.children { $0.description == "tab bar" } + for tabBar in tabBars { + let tabs = tabBar.children { $0.roleDescription == "tab" } + for tab in tabs { + allTabs.insert(tab.title) } } return allTabs diff --git a/Tool/Sources/XcodeInspector/SourceEditor.swift b/Tool/Sources/XcodeInspector/SourceEditor.swift index 953cee35..6c735404 100644 --- a/Tool/Sources/XcodeInspector/SourceEditor.swift +++ b/Tool/Sources/XcodeInspector/SourceEditor.swift @@ -56,6 +56,7 @@ public class SourceEditor { /// ranges. public func getContent() -> Content { let content = getElementValueAndRecordStatus() + let content = element.value let selectionRange = element.selectedTextRange let (lines, selections) = cache.get(content: content, selectedTextRange: selectionRange) diff --git a/Tool/Sources/XcodeInspector/XcodeInspector.swift b/Tool/Sources/XcodeInspector/XcodeInspector.swift index daa66c6c..347de21f 100644 --- a/Tool/Sources/XcodeInspector/XcodeInspector.swift +++ b/Tool/Sources/XcodeInspector/XcodeInspector.swift @@ -301,6 +301,7 @@ public final class XcodeInspector: ObservableObject { } focusedElement = getFocusedElementAndRecordStatus(xcode.appElement) + focusedElement = xcode.appElement.focusedElement if let editorElement = focusedElement, editorElement.isSourceEditor { focusedEditor = .init( runningApplication: xcode.runningApplication, diff --git a/Tool/Tests/WorkspaceTests/FileChangeWatcherTests.swift b/Tool/Tests/WorkspaceTests/FileChangeWatcherTests.swift new file mode 100644 index 00000000..f69da9ad --- /dev/null +++ b/Tool/Tests/WorkspaceTests/FileChangeWatcherTests.swift @@ -0,0 +1,345 @@ +import XCTest +import Foundation +import CoreServices +import LanguageServerProtocol +import ConversationServiceProvider +@testable import Workspace + +// MARK: - Mocks for Testing + +class MockFSEventProvider: FSEventProvider { + var createdStream: FSEventStreamRef? + var didStartStream = false + var didStopStream = false + var didInvalidateStream = false + var didReleaseStream = false + var didSetDispatchQueue = false + var registeredCallback: FSEventStreamCallback? + var registeredContext: UnsafeMutablePointer? + + var simulatedFiles: [String] = [] + + func createEventStream( + paths: CFArray, + latency: CFTimeInterval, + flags: UInt32, + callback: @escaping FSEventStreamCallback, + context: UnsafeMutablePointer + ) -> FSEventStreamRef? { + registeredCallback = callback + registeredContext = context + let stream = unsafeBitCast(1, to: FSEventStreamRef.self) + createdStream = stream + return stream + } + + func startStream(_ stream: FSEventStreamRef) { + didStartStream = true + } + + func stopStream(_ stream: FSEventStreamRef) { + didStopStream = true + } + + func invalidateStream(_ stream: FSEventStreamRef) { + didInvalidateStream = true + } + + func releaseStream(_ stream: FSEventStreamRef) { + didReleaseStream = true + } + + func setDispatchQueue(_ stream: FSEventStreamRef, queue: DispatchQueue) { + didSetDispatchQueue = true + } +} + +class MockWorkspaceFileProvider: WorkspaceFileProvider { + + var subprojects: [URL] = [] + var filesInWorkspace: [FileReference] = [] + var xcProjectPaths: Set = [] + var xcWorkspacePaths: Set = [] + + func getSubprojectURLs(in workspace: URL) -> [URL] { + return subprojects + } + + func getFilesInActiveWorkspace(workspaceURL: URL, workspaceRootURL: URL) -> [FileReference] { + return filesInWorkspace + } + + func isXCProject(_ url: URL) -> Bool { + return xcProjectPaths.contains(url.path) + } + + func isXCWorkspace(_ url: URL) -> Bool { + return xcWorkspacePaths.contains(url.path) + } +} + +// MARK: - Tests for BatchingFileChangeWatcher + +final class BatchingFileChangeWatcherTests: XCTestCase { + var mockFSEventProvider: MockFSEventProvider! + var publishedEvents: [[FileEvent]] = [] + + override func setUp() { + super.setUp() + mockFSEventProvider = MockFSEventProvider() + publishedEvents = [] + } + + func createWatcher(projectURL: URL = URL(fileURLWithPath: "/test/project")) -> BatchingFileChangeWatcher { + return BatchingFileChangeWatcher( + watchedPaths: [projectURL], + changePublisher: { [weak self] events in + self?.publishedEvents.append(events) + }, + publishInterval: 0.1, + fsEventProvider: mockFSEventProvider + ) + } + + func testInitSetsUpTimerAndFileWatching() { + let _ = createWatcher() + + XCTAssertNotNil(mockFSEventProvider.createdStream) + XCTAssertTrue(mockFSEventProvider.didStartStream) + } + + func testDeinitCleansUpResources() { + var watcher: BatchingFileChangeWatcher? = createWatcher() + weak var weakWatcher = watcher + + watcher = nil + + // Wait for the watcher to be deallocated + let startTime = Date() + let timeout: TimeInterval = 1.0 + + while weakWatcher != nil && Date().timeIntervalSince(startTime) < timeout { + RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01)) + } + + XCTAssertTrue(mockFSEventProvider.didStopStream) + XCTAssertTrue(mockFSEventProvider.didInvalidateStream) + XCTAssertTrue(mockFSEventProvider.didReleaseStream) + } + + func testAddingEventsAndPublishing() { + let watcher = createWatcher() + let fileURL = URL(fileURLWithPath: "/test/project/file.swift") + + watcher.onFileCreated(file: fileURL) + + // No events should be published yet + XCTAssertTrue(publishedEvents.isEmpty) + + XCTAssertTrue(waitForPublishedEvents(), "No events were published within timeout") + + // Only verify array contents if we have events + guard !publishedEvents.isEmpty else { return } + + XCTAssertEqual(publishedEvents[0].count, 1) + XCTAssertEqual(publishedEvents[0][0].uri, fileURL.absoluteString) + XCTAssertEqual(publishedEvents[0][0].type, .created) + } + + func testProcessingFSEvents() { + let watcher = createWatcher() + let fileURL = URL(fileURLWithPath: "/test/project/file.swift") + + // Test file creation - directly call methods instead of simulating FS events + watcher.onFileCreated(file: fileURL) + XCTAssertTrue(waitForPublishedEvents(), "No events were published within timeout") + + guard !publishedEvents.isEmpty else { return } + XCTAssertEqual(publishedEvents[0].count, 1) + XCTAssertEqual(publishedEvents[0][0].type, .created) + + // Test file modification + publishedEvents = [] + watcher.onFileChanged(file: fileURL) + + XCTAssertTrue(waitForPublishedEvents(), "No events were published within timeout") + + guard !publishedEvents.isEmpty else { return } + XCTAssertEqual(publishedEvents[0].count, 1) + XCTAssertEqual(publishedEvents[0][0].type, .changed) + + // Test file deletion + publishedEvents = [] + watcher.onFileDeleted(file: fileURL) + XCTAssertTrue(waitForPublishedEvents(), "No events were published within timeout") + + guard !publishedEvents.isEmpty else { return } + XCTAssertEqual(publishedEvents[0].count, 1) + XCTAssertEqual(publishedEvents[0][0].type, .deleted) + } +} + +extension BatchingFileChangeWatcherTests { + func waitForPublishedEvents(timeout: TimeInterval = 1.0) -> Bool { + let start = Date() + while publishedEvents.isEmpty && Date().timeIntervalSince(start) < timeout { + RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1)) + } + return !publishedEvents.isEmpty + } +} + +// MARK: - Tests for FileChangeWatcherService + +final class FileChangeWatcherServiceTests: XCTestCase { + var mockWorkspaceFileProvider: MockWorkspaceFileProvider! + var publishedEvents: [[FileEvent]] = [] + var createdWatchers: [[URL]: BatchingFileChangeWatcher] = [:] + + override func setUp() { + super.setUp() + mockWorkspaceFileProvider = MockWorkspaceFileProvider() + publishedEvents = [] + createdWatchers = [:] + } + + func createService(workspaceURL: URL = URL(fileURLWithPath: "/test/workspace")) -> FileChangeWatcherService { + return FileChangeWatcherService( + workspaceURL, + publisher: { [weak self] events in + self?.publishedEvents.append(events) + }, + publishInterval: 0.1, + projectWatchingInterval: 0.1, + workspaceFileProvider: mockWorkspaceFileProvider, + watcherFactory: { projectURLs, publisher in + let watcher = BatchingFileChangeWatcher( + watchedPaths: projectURLs, + changePublisher: publisher, + fsEventProvider: MockFSEventProvider() + ) + self.createdWatchers[projectURLs] = watcher + return watcher + } + ) + } + + func testStartWatchingCreatesWatchersForProjects() { + let project1 = URL(fileURLWithPath: "/test/workspace/project1") + let project2 = URL(fileURLWithPath: "/test/workspace/project2") + mockWorkspaceFileProvider.subprojects = [project1, project2] + + let service = createService() + service.startWatching() + + XCTAssertEqual(createdWatchers.count, 1) + XCTAssertNotNil(createdWatchers[[project1, project2]]) + } + + func testStartWatchingDoesNotCreateWatcherForRootDirectory() { + let service = createService(workspaceURL: URL(fileURLWithPath: "/")) + service.startWatching() + + XCTAssertTrue(createdWatchers.isEmpty) + } + + func testProjectMonitoringDetectsAddedProjects() { + let workspace = URL(fileURLWithPath: "/test/workspace") + let project1 = URL(fileURLWithPath: "/test/workspace/project1") + mockWorkspaceFileProvider.subprojects = [project1] + + let service = createService(workspaceURL: workspace) + service.startWatching() + + XCTAssertEqual(createdWatchers.count, 1) + + // Simulate adding a new project + let project2 = URL(fileURLWithPath: "/test/workspace/project2") + mockWorkspaceFileProvider.subprojects = [project1, project2] + + // Set up mock files for the added project + let file1URL = URL(fileURLWithPath: "/test/workspace/project2/file1.swift") + let file1 = FileReference( + url: file1URL, + relativePath: file1URL.relativePath, + fileName: file1URL.lastPathComponent + ) + let file2URL = URL(fileURLWithPath: "/test/workspace/project2/file2.swift") + let file2 = FileReference( + url: file2URL, + relativePath: file2URL.relativePath, + fileName: file2URL.lastPathComponent + ) + mockWorkspaceFileProvider.filesInWorkspace = [file1, file2] + + XCTAssertTrue(waitForPublishedEvents(), "No events were published within timeout") + + XCTAssertEqual(createdWatchers.count, 1) + + guard !publishedEvents.isEmpty else { return } + + // Verify file events were published + XCTAssertEqual(publishedEvents[0].count, 2) + + // Verify both files were reported as created + XCTAssertEqual(publishedEvents[0][0].type, .created) + XCTAssertEqual(publishedEvents[0][1].type, .created) + } + + func testProjectMonitoringDetectsRemovedProjects() { + let workspace = URL(fileURLWithPath: "/test/workspace") + let project1 = URL(fileURLWithPath: "/test/workspace/project1") + let project2 = URL(fileURLWithPath: "/test/workspace/project2") + mockWorkspaceFileProvider.subprojects = [project1, project2] + + let service = createService(workspaceURL: workspace) + service.startWatching() + + XCTAssertEqual(createdWatchers.count, 1) + + // Simulate removing a project + mockWorkspaceFileProvider.subprojects = [project1] + + // Set up mock files for the removed project + let file1URL = URL(fileURLWithPath: "/test/workspace/project2/file1.swift") + let file1 = FileReference( + url: file1URL, + relativePath: file1URL.relativePath, + fileName: file1URL.lastPathComponent + ) + let file2URL = URL(fileURLWithPath: "/test/workspace/project2/file2.swift") + let file2 = FileReference( + url: file2URL, + relativePath: file2URL.relativePath, + fileName: file2URL.lastPathComponent + ) + mockWorkspaceFileProvider.filesInWorkspace = [file1, file2] + + // Clear published events from setup + publishedEvents = [] + + XCTAssertTrue(waitForPublishedEvents(), "No events were published within timeout") + + guard !publishedEvents.isEmpty else { return } + + // Verify the watcher was removed + XCTAssertEqual(createdWatchers.count, 1) + + // Verify file events were published + XCTAssertEqual(publishedEvents[0].count, 2) + + // Verify both files were reported as deleted + XCTAssertEqual(publishedEvents[0][0].type, .deleted) + XCTAssertEqual(publishedEvents[0][1].type, .deleted) + } +} + +extension FileChangeWatcherServiceTests { + func waitForPublishedEvents(timeout: TimeInterval = 3.0) -> Bool { + let start = Date() + while publishedEvents.isEmpty && Date().timeIntervalSince(start) < timeout { + RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1)) + } + return !publishedEvents.isEmpty + } +}