diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionViewModel.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionViewModel.swift index 9fb741e57..b3f2fa632 100644 --- a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionViewModel.swift @@ -1,12 +1,13 @@ import CoreData import Models +import Services import SwiftUI import Utils import Views public class ShareExtensionViewModel: ObservableObject { @Published public var status: ShareExtensionStatus = .processing - @Published public var title: String? + @Published public var title: String = "" @Published public var url: String? @Published public var iconURL: String? @Published public var linkedItem: LinkedItem? @@ -42,6 +43,22 @@ public class ShareExtensionViewModel: ObservableObject { } } + func setLinkArchived(dataService: DataService, objectID: NSManagedObjectID, archived: Bool) { + dataService.archiveLink(objectID: objectID, archived: archived) + } + + func removeLink(dataService: DataService, objectID: NSManagedObjectID) { + dataService.removeLink(objectID: objectID) + } + + func submitTitleEdit(dataService: DataService, itemID: String, title: String, description: String) { + dataService.updateLinkedItemTitleAndDescription( + itemID: itemID, + title: title, + description: description + ) + } + #if os(iOS) func queueSaveOperation(_ payload: PageScrapePayload) { ProcessInfo().performExpiringActivity(withReason: "app.omnivore.SaveActivity") { [self] expiring in @@ -72,7 +89,7 @@ public class ShareExtensionViewModel: ObservableObject { switch payload.contentType { case let .html(html: _, title: title, iconURL: iconURL): - self.title = title + self.title = title ?? "" self.iconURL = iconURL self.url = hostname case .none: diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift index a4d58cc84..c4a0ea82d 100644 --- a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift +++ b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift @@ -6,10 +6,22 @@ import Views public struct ShareExtensionView: View { let extensionContext: NSExtensionContext? + @EnvironmentObject var dataService: DataService + @StateObject var labelsViewModel = LabelsViewModel() @StateObject private var viewModel = ShareExtensionViewModel() @State var reminderTime: ReminderTime? @State var hideUntilReminded = false + @State var editingTitle = false + @State var editingLabels = false + @State var previousLabels: [LinkedItemLabel]? + @State var messageText: String? + + enum FocusField: Hashable { + case titleEditor + } + + @FocusState private var focusedField: FocusField? private func handleReminderTimeSelection(_ selectedTime: ReminderTime) { if selectedTime == reminderTime { @@ -134,61 +146,331 @@ public struct ShareExtensionView: View { .cornerRadius(8) } - public var body: some View { - VStack(alignment: .leading) { - Text(titleText) - .foregroundColor(.appGrayTextContrast) - .font(Font.system(size: 17, weight: .semibold)) - .frame(maxWidth: .infinity, alignment: .center) - .padding(.top, 23) - .padding(.bottom, 12) + var isSynced: Bool { + switch viewModel.status { + case .synced: + return true + default: + return false + } + } - Rectangle() - .foregroundColor(.appGrayText) - .frame(maxWidth: .infinity, maxHeight: 1) - .opacity(0.06) - .padding(.top, 0) - .padding(.bottom, 18) + var titleBar: some View { + HStack { + Spacer() - previewCard - .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) + Image(systemName: "checkmark.circle") + .frame(width: 15, height: 15) + .foregroundColor(.appGreenSuccess) + .opacity(isSynced ? 1.0 : 0.0) - if let item = viewModel.linkedItem { - ApplyLabelsListView(linkedItem: item) + Text(messageText ?? titleText) + .font(.appSubheadline) + .foregroundColor(isSynced ? .appGreenSuccess : .appGrayText) + + Spacer() + } + } + + public var titleBox: some View { + VStack(alignment: .trailing) { + Button(action: {}, label: { + Text("Edit") + .font(.appFootnote) + .padding(.trailing, 8) + .onTapGesture { + editingTitle = true + } + }) + .disabled(editingTitle) + .opacity(editingTitle ? 0.0 : 1.0) + + VStack(alignment: .leading) { + if !editingTitle { + Text(self.viewModel.title) + .font(.appSubheadline) + .foregroundColor(.appGrayTextContrast) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + + Text(self.viewModel.url ?? "") + .font(.appFootnote) + .foregroundColor(.appGrayText) + .frame(maxWidth: .infinity, alignment: .leading) + } else {} + } + .frame(maxWidth: .infinity, maxHeight: 60) + .padding() + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.appGrayBorder, lineWidth: 1) + ) + } + } + + var labelsSection: some View { + HStack { + if !editingLabels { + ZStack { + Circle() + .foregroundColor(Color.blue) + .frame(width: 34, height: 34) + + Image(systemName: "tag") + .font(.appCallout) + .frame(width: 34, height: 34) + } + .padding(.trailing, 8) + + VStack { + Text("Labels") + .font(.appSubheadline) + .foregroundColor(Color.appGrayTextContrast) + .frame(maxWidth: .infinity, alignment: .leading) + + let labelCount = labelsViewModel.selectedLabels.count + Text(labelCount > 0 ? + "\(labelCount) label\(labelCount > 1 ? "s" : "") selected" + : "Add labels to your saved link") + .font(.appFootnote) + .foregroundColor(Color.appGrayText) + .frame(maxWidth: .infinity, alignment: .leading) + } + + Spacer() + + Image(systemName: "chevron.right") + .font(.appCallout) } else { + ScrollView { + LabelsMasonaryView(labels: labelsViewModel.labels, + selectedLabels: labelsViewModel.selectedLabels, + onLabelTap: onLabelTap) + }.background(Color.appButtonBackground) + .cornerRadius(8) + } + } + .padding(16) + .frame(maxWidth: .infinity, maxHeight: self.editingLabels ? .infinity : 60) + .background(Color.appButtonBackground) + .cornerRadius(8) + } + + func onLabelTap(label: LinkedItemLabel, textChip _: TextChip) { + if let selectedIndex = labelsViewModel.selectedLabels.firstIndex(of: label) { + labelsViewModel.selectedLabels.remove(at: selectedIndex) + } else { + labelsViewModel.selectedLabels.append(label) + } + + if let linkedItem = viewModel.linkedItem { + labelsViewModel.saveItemLabelChanges(itemID: linkedItem.unwrappedID, dataService: viewModel.services.dataService) + } + } + + var primaryButtons: some View { + HStack { + Button( + action: { viewModel.handleReadNowAction(extensionContext: extensionContext) }, + label: { + Label("Read Now", systemImage: "book") + .padding(16) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + ) + .foregroundColor(.appGrayTextContrast) + .background(Color.appButtonBackground) + .frame(height: 52) + .cornerRadius(8) + + Spacer(minLength: 8) + + Button( + action: { + extensionContext?.completeRequest(returningItems: [], completionHandler: nil) + }, + label: { + Label("Read Later", systemImage: "text.book.closed.fill") + .padding(16) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + ) + .foregroundColor(.black) + .background(Color.appBackground) + .frame(height: 52) + .cornerRadius(8) + } + } + + var moreActionsMenu: some View { + Menu { + Button( + action: {}, + label: { + Button(action: {}, label: { Label("Dismiss", systemImage: "arrow.down.to.line") }) + } + ) + Button(action: { + if let linkedItem = self.viewModel.linkedItem { + self.viewModel.setLinkArchived(dataService: self.viewModel.services.dataService, + objectID: linkedItem.objectID, + archived: true) + messageText = "Link Archived" + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) { + extensionContext?.completeRequest(returningItems: [], completionHandler: nil) + } + } + }, label: { + Label( + "Archive", + systemImage: "archivebox" + ) + }) + Button( + action: { + if let linkedItem = self.viewModel.linkedItem { + self.viewModel.removeLink(dataService: self.viewModel.services.dataService, objectID: linkedItem.objectID) + messageText = "Link Removed" + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) { + extensionContext?.completeRequest(returningItems: [], completionHandler: nil) + } + } + }, + label: { + Label("Remove", systemImage: "trash") + } + ) + } label: { + Text("More Actions") + .font(.appFootnote) + .foregroundColor(Color.blue) + .frame(maxWidth: .infinity) + .padding(8) + .padding(.bottom, 8) + } + } + + public var body: some View { + VStack(alignment: .center) { + Capsule() + .fill(.gray) + .frame(width: 60, height: 4) + .padding(.top, 10) + + if !editingLabels, !editingTitle { + titleBar + .padding(.top, 10) + .padding(.bottom, 12) + } else { + ZStack { + Button(action: { + withAnimation { + if editingLabels { + if let linkedItem = self.viewModel.linkedItem { + self.labelsViewModel.selectedLabels = previousLabels ?? [] + self.labelsViewModel.saveItemLabelChanges(itemID: linkedItem.unwrappedID, + dataService: self.viewModel.services.dataService) + } + } + editingTitle = false + editingLabels = false + } + }, label: { Text("Cancel") }) + .frame(maxWidth: .infinity, alignment: .leading) + + Text(editingTitle ? "Edit Title" : "Labels").bold() + .frame(maxWidth: .infinity, alignment: .center) + + Button(action: { + withAnimation { + editingTitle = false + editingLabels = false + + if editingTitle { + if let linkedItem = self.viewModel.linkedItem { + viewModel.submitTitleEdit(dataService: self.viewModel.services.dataService, + itemID: linkedItem.unwrappedID, + title: self.viewModel.title, + description: linkedItem.description) + } + } + } + }, label: { Text("Done").bold() }) + .frame(maxWidth: .infinity, alignment: .trailing) + } + .padding(8) + .padding(.bottom, 4) + } + + if !editingLabels, !editingTitle { + titleBox + } + + if editingTitle { + ScrollView(showsIndicators: false) { + VStack(alignment: .center, spacing: 16) { + VStack(alignment: .leading, spacing: 6) { + TextEditor(text: $viewModel.title) + .lineSpacing(6) + .accentColor(.appGraySolid) + .foregroundColor(.appGrayTextContrast) + .font(.appSubheadline) + .padding(8) + .background( + RoundedRectangle(cornerRadius: 8) + .strokeBorder(Color.appGrayBorder, lineWidth: 1) + .background(RoundedRectangle(cornerRadius: 8).fill(Color.systemBackground)) + ) + .frame(height: 100) + .focused($focusedField, equals: .titleEditor) + .task { + self.focusedField = .titleEditor + } + } + } + .padding(8) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + Spacer() } - HStack { - Button( - action: { viewModel.handleReadNowAction(extensionContext: extensionContext) }, - label: { Text("Read Now").frame(maxWidth: .infinity) } - ) - .buttonStyle(RoundedRectButtonStyle()) - - Button( - action: { - extensionContext?.completeRequest(returningItems: [], completionHandler: nil) - }, - label: { - Text("Read Later") - .frame(maxWidth: .infinity) + if !editingTitle { + labelsSection + .padding(.top, 12) + .onTapGesture { + withAnimation { + previousLabels = self.labelsViewModel.selectedLabels + editingLabels = true + } } - ) - .buttonStyle(RoundedRectButtonStyle()) } - .padding(.horizontal) - .padding(.bottom) + + Spacer() + + if !editingLabels, !editingTitle { + Divider() + .padding(.bottom, 20) + + primaryButtons + + moreActionsMenu + } } .frame( maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading ) + .padding(.horizontal, 16) .onAppear { viewModel.savePage(extensionContext: extensionContext) } .environmentObject(viewModel.services.dataService) + .task { + await labelsViewModel.loadLabelsFromStore(dataService: viewModel.services.dataService) + } } } diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ExpandedPlayer.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ExpandedPlayer.swift index 39384a6cf..9a2aaa2bb 100644 --- a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ExpandedPlayer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ExpandedPlayer.swift @@ -14,10 +14,6 @@ @State var showVoiceSheet = false @State var tabIndex: Int = 0 - var isPresented: Bool { - audioController.itemAudioProperties != nil && audioController.state != .stopped - } - var playPauseButtonImage: String { switch audioController.state { case .playing: @@ -146,7 +142,7 @@ + Text(audioController.unreadText) .font(.textToSpeechRead.leading(.loose)) - .foregroundColor(Color.appGrayText) + .foregroundColor(audioController.useUltraRealisticVoices ? Color.appGrayTextContrast : Color.appGrayText) } } } @@ -345,7 +341,7 @@ } public var body: some View { - if let itemAudioProperties = self.audioController.itemAudioProperties, isPresented { + if let itemAudioProperties = self.audioController.itemAudioProperties { playerContent(itemAudioProperties) .tint(.appGrayTextContrast) } else { diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift index 3021943de..9eb1082eb 100644 --- a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift @@ -20,10 +20,6 @@ self.presentingView = AnyView(presentingView) } - var isPresented: Bool { - audioController.itemAudioProperties != nil && audioController.state != .stopped - } - var playPauseButtonImage: String { switch audioController.state { case .playing: @@ -158,7 +154,7 @@ public var body: some View { ZStack(alignment: .center) { presentingView - if let itemAudioProperties = self.audioController.itemAudioProperties, isPresented { + if let itemAudioProperties = self.audioController.itemAudioProperties { ZStack(alignment: .bottom) { Color.systemBackground.edgesIgnoringSafeArea(.bottom) .frame(height: expanded ? 0 : 88, alignment: .bottom) @@ -172,6 +168,9 @@ } } } + }.alert("There was an error playing back your audio.", + isPresented: $audioController.playbackError) { + Button("Dismiss", role: .none) {} } } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 2337f76bb..448271929 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -215,56 +215,118 @@ import Views @ObservedObject var viewModel: HomeFeedViewModel var filtersHeader: some View { - ScrollView(.horizontal, showsIndicators: false) { - HStack { - if viewModel.searchTerm.count > 0 { - TextChipButton.makeSearchFilterButton(title: viewModel.searchTerm) { - viewModel.searchTerm = "" + GeometryReader { reader in + ScrollView(.horizontal, showsIndicators: false) { + HStack { + if viewModel.searchTerm.count > 0 { + TextChipButton.makeSearchFilterButton(title: viewModel.searchTerm) { + viewModel.searchTerm = "" + }.frame(maxWidth: reader.size.width * 0.66) + } else { + Menu( + content: { + ForEach(LinkedItemFilter.allCases, id: \.self) { filter in + Button(filter.displayName, action: { viewModel.appliedFilter = filter.rawValue }) + } + }, + label: { + TextChipButton.makeMenuButton( + title: LinkedItemFilter(rawValue: viewModel.appliedFilter)?.displayName ?? "Filter" + ) + } + ) } - } else { Menu( content: { - ForEach(LinkedItemFilter.allCases, id: \.self) { filter in - Button(filter.displayName, action: { viewModel.appliedFilter = filter.rawValue }) + ForEach(LinkedItemSort.allCases, id: \.self) { sort in + Button(sort.displayName, action: { viewModel.appliedSort = sort.rawValue }) } }, label: { TextChipButton.makeMenuButton( - title: LinkedItemFilter(rawValue: viewModel.appliedFilter)?.displayName ?? "Filter" + title: LinkedItemSort(rawValue: viewModel.appliedSort)?.displayName ?? "Sort" ) } ) - } - Menu( - content: { - ForEach(LinkedItemSort.allCases, id: \.self) { sort in - Button(sort.displayName, action: { viewModel.appliedSort = sort.rawValue }) + TextChipButton.makeAddLabelButton { + viewModel.showLabelsSheet = true + } + ForEach(viewModel.selectedLabels, id: \.self) { label in + TextChipButton.makeRemovableLabelButton(feedItemLabel: label, negated: false) { + viewModel.selectedLabels.removeAll { $0.id == label.id } } + } + ForEach(viewModel.negatedLabels, id: \.self) { label in + TextChipButton.makeRemovableLabelButton(feedItemLabel: label, negated: true) { + viewModel.negatedLabels.removeAll { $0.id == label.id } + } + } + Spacer() + } + .padding(0) + } + .listRowSeparator(.hidden) + } + } + + func menuItems(for item: LinkedItem) -> some View { + Group { + if (item.highlights?.count ?? 0) > 0 { + Button( + action: { viewModel.itemForHighlightsView = item }, + label: { Label("View Highlights & Notes", systemImage: "highlighter") } + ) + } + Button( + action: { viewModel.itemUnderTitleEdit = item }, + label: { Label("Edit Title/Description", systemImage: "textbox") } + ) + Button( + action: { viewModel.itemUnderLabelEdit = item }, + label: { Label(item.labels?.count == 0 ? "Add Labels" : "Edit Labels", systemImage: "tag") } + ) + Button(action: { + withAnimation(.linear(duration: 0.4)) { + viewModel.setLinkArchived( + dataService: dataService, + objectID: item.objectID, + archived: !item.isArchived + ) + } + }, label: { + Label( + item.isArchived ? "Unarchive" : "Archive", + systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox" + ) + }) + Button( + action: { + itemToRemove = item + confirmationShown = true + }, + label: { + Label("Remove Item", systemImage: "trash") + } + ).tint(.red) + if FeatureFlag.enableSnooze { + Button { + viewModel.itemToSnoozeID = item.id + viewModel.snoozePresented = true + } label: { + Label { Text("Snooze") } icon: { Image.moon } + } + } + if let author = item.author { + Button( + action: { + viewModel.searchTerm = "author:\"\(author)\"" }, label: { - TextChipButton.makeMenuButton( - title: LinkedItemSort(rawValue: viewModel.appliedSort)?.displayName ?? "Sort" - ) + Label(String("More by \(author)"), systemImage: "person") } ) - TextChipButton.makeAddLabelButton { - viewModel.showLabelsSheet = true - } - ForEach(viewModel.selectedLabels, id: \.self) { label in - TextChipButton.makeRemovableLabelButton(feedItemLabel: label, negated: false) { - viewModel.selectedLabels.removeAll { $0.id == label.id } - } - } - ForEach(viewModel.negatedLabels, id: \.self) { label in - TextChipButton.makeRemovableLabelButton(feedItemLabel: label, negated: true) { - viewModel.negatedLabels.removeAll { $0.id == label.id } - } - } - Spacer() } - .padding(0) } - .listRowSeparator(.hidden) } var body: some View { @@ -292,59 +354,7 @@ import Views viewModel: viewModel ) .contextMenu { - Button( - action: { viewModel.itemForHighlightsView = item }, - label: { Label("View Highlights & Notes", systemImage: "highlighter") } - ) - Button( - action: { viewModel.itemUnderTitleEdit = item }, - label: { Label("Edit Title/Description", systemImage: "textbox") } - ) - Button( - action: { viewModel.itemUnderLabelEdit = item }, - label: { Label(item.labels?.count == 0 ? "Add Labels" : "Edit Labels", systemImage: "tag") } - ) - Button(action: { - withAnimation(.linear(duration: 0.4)) { - viewModel.setLinkArchived( - dataService: dataService, - objectID: item.objectID, - archived: !item.isArchived - ) - } - }, label: { - Label( - item.isArchived ? "Unarchive" : "Archive", - systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox" - ) - }) - Button( - action: { - itemToRemove = item - confirmationShown = true - }, - label: { - Label("Remove Item", systemImage: "trash") - } - ).tint(.red) - if FeatureFlag.enableSnooze { - Button { - viewModel.itemToSnoozeID = item.id - viewModel.snoozePresented = true - } label: { - Label { Text("Snooze") } icon: { Image.moon } - } - } - if let author = item.author { - Button( - action: { - viewModel.searchTerm = "author:\"\(author)\"" - }, - label: { - Label(String("More by \(author)"), systemImage: "person") - } - ) - } + menuItems(for: item) } .swipeActions(edge: .trailing, allowsFullSwipe: true) { if !item.isArchived { @@ -428,8 +438,6 @@ import Views viewModel.itemUnderLabelEdit = item case .editTitle: viewModel.itemUnderTitleEdit = item - case .downloadAudio: - viewModel.downloadAudio(audioController: audioController, item: item) } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift index c2b824287..7a58090ba 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift @@ -50,7 +50,7 @@ import Views ) Button( action: { viewModel.itemUnderLabelEdit = item }, - label: { Label("Edit Labels", systemImage: "tag") } + label: { Label(item.labels?.count == 0 ? "Add Labels" : "Edit Labels", systemImage: "tag") } ) Button(action: { withAnimation(.linear(duration: 0.4)) { diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index 8b3b1dda2..55ff72e95 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -175,14 +175,6 @@ import Views showLoadingBar = false } - func downloadAudio(audioController: AudioController, item: LinkedItem) { - Snackbar.show(message: "Downloading Offline Audio") - Task { - let downloaded = await audioController.downloadForOffline(itemID: item.unwrappedID) - Snackbar.show(message: downloaded ? "Audio file downloaded" : "Error downloading audio") - } - } - private var fetchRequest: NSFetchRequest { let fetchRequest: NSFetchRequest = LinkedItem.fetchRequest() diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsMasonaryView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsMasonaryView.swift new file mode 100644 index 000000000..1c3139bbb --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsMasonaryView.swift @@ -0,0 +1,99 @@ +// +// LabelsMasonaryView.swift +// +// +// Created by Jackson Harper on 11/9/22. +// + +import Foundation +import SwiftUI + +import Models +import Views + +struct LabelsMasonaryView: View { + // var allLabels: [LinkedItemLabel] + // var selectedLabels: [LinkedItemLabel] + var onLabelTap: (LinkedItemLabel, TextChip) -> Void + + var iteration = UUID().uuidString + + @State private var totalHeight = CGFloat.zero + private var labelItems: [(label: LinkedItemLabel, selected: Bool)] + + init(labels allLabels: [LinkedItemLabel], + selectedLabels: [LinkedItemLabel], + onLabelTap: @escaping (LinkedItemLabel, TextChip) -> Void) + { + self.onLabelTap = onLabelTap + + let selected = selectedLabels.map { (label: $0, selected: true) } + let unselected = allLabels.filter { !selectedLabels.contains($0) }.map { (label: $0, selected: false) } + labelItems = (selected + unselected).sorted(by: { left, right in + (left.label.name ?? "") < (right.label.name ?? "") + }) + } + + var body: some View { + VStack { + GeometryReader { geometry in + self.generateContent(in: geometry) + } + } + .frame(height: totalHeight) + } + + private func generateContent(in geom: GeometryProxy) -> some View { + var width = CGFloat.zero + var height = CGFloat.zero + + return ZStack(alignment: .topLeading) { + ForEach(self.labelItems, id: \.label.self) { label in + self.item(for: label) + .padding([.horizontal, .vertical], 6) + .alignmentGuide(.leading, computeValue: { dim in + if abs(width - dim.width) > geom.size.width { + width = 0 + height -= dim.height + } + let result = width + if label == self.labelItems.last! { + width = 0 // last item + } else { + width -= dim.width + } + return result + }) + .alignmentGuide(.top, computeValue: { _ in + let result = height + if label == self.labelItems.last! { + height = 0 // last item + } + return result + }) + } + } + .background(viewHeightReader($totalHeight)) + } + + private func item(for item: (label: LinkedItemLabel, selected: Bool)) -> some View { + if item.selected { + print(" -- SELECTED LABEL", item.label.name) + } + print("GETTING ITERATION", iteration) + let chip = TextChip(feedItemLabel: item.label, negated: false, checked: item.selected) { chip in + onLabelTap(item.label, chip) + } + return chip + } + + private func viewHeightReader(_ binding: Binding) -> some View { + GeometryReader { geometry -> Color in + let rect = geometry.frame(in: .local) + DispatchQueue.main.async { + binding.wrappedValue = rect.size.height + } + return .clear + } + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/TextToSpeechVoiceSelectionView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/TextToSpeechVoiceSelectionView.swift index 34549ef02..f0ddaf888 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/TextToSpeechVoiceSelectionView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/TextToSpeechVoiceSelectionView.swift @@ -2,10 +2,15 @@ import Models import Services import SwiftUI + import Utils import Views struct TextToSpeechVoiceSelectionView: View { @EnvironmentObject var audioController: AudioController + @EnvironmentObject var dataService: DataService + + @StateObject var viewModel = TextToSpeechVoiceSelectionViewModel() + let language: VoiceLanguage let showLanguageChanger: Bool @@ -17,55 +22,140 @@ var body: some View { Group { Form { - if showLanguageChanger { - Section("Language") { - NavigationLink(destination: TextToSpeechLanguageView().navigationTitle("Language")) { - Text(audioController.currentVoiceLanguage.name) + if FeatureFlag.enableUltraRealisticVoices, language.key == "en" { + if viewModel.waitingForRealisticVoices { + HStack { + Text("Signing up for beta") + Spacer() + ProgressView() } + } else { + Toggle("Use Ultra Realistic Voices", isOn: $viewModel.realisticVoicesToggle) + .accentColor(Color.green) + } + + if !viewModel.waitingForRealisticVoices, !audioController.ultraRealisticFeatureKey.isEmpty { + Text("You are in the ultra realistic voices beta. During the beta you can listen to 10,000 words of audio per day.") + .multilineTextAlignment(.leading) + } else if audioController.ultraRealisticFeatureRequested { + Text("Your request to join the ultra realistic voices demo has been received. You will be informed by email when a spot is available.") + .multilineTextAlignment(.leading) + } else { + Text("Ultra realistic voices are currently in limited beta. Enabling the feature will add you to the beta queue.") + .multilineTextAlignment(.leading) } } - innerBody + + if audioController.useUltraRealisticVoices { + ultraRealisticVoices + } else { + if showLanguageChanger { + Section("Language") { + NavigationLink(destination: TextToSpeechLanguageView().navigationTitle("Language")) { + Text(audioController.currentVoiceLanguage.name) + } + } + } + standardVoices + } } } .navigationTitle("Choose a Voice") + .onAppear { + viewModel.realisticVoicesToggle = (audioController.useUltraRealisticVoices && !audioController.ultraRealisticFeatureKey.isEmpty) + }.onChange(of: viewModel.realisticVoicesToggle) { value in + if value, audioController.ultraRealisticFeatureKey.isEmpty { + // User wants to sign up + viewModel.waitingForRealisticVoices = true + Task { + await viewModel.requestUltraRealisticFeatureAccess( + dataService: self.dataService, + audioController: audioController + ) + } + } else if value, !audioController.ultraRealisticFeatureKey.isEmpty { + audioController.useUltraRealisticVoices = true + } else if !value { + audioController.useUltraRealisticVoices = false + } + } } - private var innerBody: some View { + private var standardVoices: some View { ForEach(language.categories, id: \.self) { category in Section(category.rawValue) { ForEach(audioController.voiceList?.filter { $0.category == category } ?? [], id: \.key.self) { voice in - HStack { - // Voice samples are not working yet -// Button(action: { -// audioController.playVoiceSample(voice: voice.key) -// }) { -// Image(systemName: "play.circle").font(.appTitleTwo) -// } -// .buttonStyle(PlainButtonStyle()) - - Button(action: { - audioController.setPreferredVoice(voice.key, forLanguage: language.key) - audioController.currentVoice = voice.key - }) { - HStack { - Text(voice.name) - Spacer() - - if voice.selected { - if audioController.isPlaying, audioController.isLoading { - ProgressView() - } else { - Image(systemName: "checkmark") - } - } - } - .contentShape(Rectangle()) - } - .buttonStyle(PlainButtonStyle()) - } + voiceRow(for: voice) } } } } + + private var ultraRealisticVoices: some View { + ForEach([VoiceCategory.enUS, VoiceCategory.enCA, VoiceCategory.enUK], id: \.self) { category in + Section(category.rawValue) { + ForEach(audioController.realisticVoiceList?.filter { $0.category == category } ?? [], id: \.key.self) { voice in + voiceRow(for: voice) + } + } + } + } + + func voiceRow(for voice: VoiceItem) -> some View { + HStack { + Button(action: { + if audioController.isPlayingSample(voice: voice.key) { + viewModel.playbackSample = nil + audioController.stopVoiceSample() + } else { + viewModel.playbackSample = voice.key + audioController.playVoiceSample(voice: voice.key) + Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { timer in + let playing = audioController.isPlayingSample(voice: voice.key) + if playing { + viewModel.playbackSample = voice.key + } else if !playing { + // If the playback sample is something else, its taken ownership + // of the value so we just ignore it and shut down our timer. + if viewModel.playbackSample == voice.key { + viewModel.playbackSample = nil + } + timer.invalidate() + } + } + } + }, label: { + if viewModel.playbackSample == voice.key { + Image(systemName: "stop.circle") + .font(.appTitleTwo) + .padding(.trailing, 16) + } else { + Image(systemName: "play.circle") + .font(.appTitleTwo) + .padding(.trailing, 16) + } + }) + + Button(action: { + audioController.setPreferredVoice(voice.key, forLanguage: language.key) + audioController.currentVoice = voice.key + }, label: { + HStack { + Text(voice.name) + Spacer() + + if voice.selected { + if audioController.isPlaying, audioController.isLoading { + ProgressView() + } else { + Image(systemName: "checkmark") + } + } + } + .contentShape(Rectangle()) + }) + .buttonStyle(PlainButtonStyle()) + } + } } #endif diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/TextToSpeechVoiceSelectionViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/TextToSpeechVoiceSelectionViewModel.swift new file mode 100644 index 000000000..609883940 --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/TextToSpeechVoiceSelectionViewModel.swift @@ -0,0 +1,55 @@ +// +// TextToSpeechVoiceSelectionViewModel.swift +// +// +// Created by Jackson Harper on 11/10/22. +// + +import CoreData +import Foundation +import Models +import Services +import SwiftUI +import Views + +@MainActor final class TextToSpeechVoiceSelectionViewModel: ObservableObject { + @Published var playbackSample: String? + @Published var realisticVoicesToggle: Bool = false + @Published var waitingForRealisticVoices: Bool = false + + func requestUltraRealisticFeatureAccess( + dataService: DataService, + audioController: AudioController + ) async { + do { + let feature = try await dataService.optInFeature(name: "ultra-realistic-voice") + DispatchQueue.main.async { + if let feature = feature { + audioController.useUltraRealisticVoices = true + audioController.ultraRealisticFeatureRequested = true + audioController.ultraRealisticFeatureKey = feature.granted ? feature.token : "" + if feature.granted, !Voices.isUltraRealisticVoice(audioController.currentVoice) { + // Attempt to set to an ultra voice + if let voice = Voices.UltraPairs.first { + audioController.currentVoice = voice.firstKey + } + } + self.realisticVoicesToggle = true + } else { + audioController.useUltraRealisticVoices = false + audioController.ultraRealisticFeatureKey = "" + audioController.ultraRealisticFeatureRequested = false + self.realisticVoicesToggle = false + } + self.waitingForRealisticVoices = false + } + } catch { + print("ERROR OPTING INTO FEATURE", error) + audioController.useUltraRealisticVoices = false + realisticVoicesToggle = false + waitingForRealisticVoices = false + audioController.ultraRealisticFeatureRequested = false + Snackbar.show(message: "Error signing up for beta. Please try again.") + } + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift index dd70144b7..74f1238e6 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift @@ -153,6 +153,60 @@ struct WebReaderContainerView: View { }.foregroundColor(.appGrayTextContrast) } + func menuItems(for item: LinkedItem) -> some View { + let hasLabels = item.labels?.count == 0 + let hasHighlights = (item.highlights?.count ?? 0) > 0 + return Group { + if hasHighlights { + Button( + action: { showHighlightsView = true }, + label: { Label("View Highlights & Notes", systemImage: "highlighter") } + ) + } + Button( + action: { showTitleEdit = true }, + label: { Label("Edit Title/Description", systemImage: "textbox") } + ) + Button( + action: editLabels, + label: { Label(hasLabels ? "Edit Labels" : "Add Labels", systemImage: "tag") } + ) + Button( + action: { + archive() + }, + label: { + Label( + item.isArchived ? "Unarchive" : "Archive", + systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox" + ) + } + ) + Button( + action: { + dataService.updateLinkReadingProgress(itemID: item.unwrappedID, readingProgress: 0, anchorIndex: 0) + }, + label: { Label("Reset Read Location", systemImage: "arrow.counterclockwise.circle") } + ) + Button( + action: { + viewModel.downloadAudio(audioController: audioController, item: item) + }, + label: { Label("Download Audio", systemImage: "icloud.and.arrow.down") } + ) + if viewModel.hasOriginalUrl(item) { + Button( + action: share, + label: { Label("Share Original", systemImage: "square.and.arrow.up") } + ) + } + Button( + action: delete, + label: { Label("Delete", systemImage: "trash") } + ) + } + } + var navBar: some View { HStack(alignment: .center) { #if os(iOS) @@ -183,49 +237,7 @@ struct WebReaderContainerView: View { #endif Menu( content: { - Group { - Button( - action: { showHighlightsView = true }, - label: { Label("View Highlights & Notes", systemImage: "highlighter") } - ) - Button( - action: { showTitleEdit = true }, - label: { Label("Edit Title/Description", systemImage: "textbox") } - ) - Button( - action: editLabels, - label: { Label("Edit Labels", systemImage: "tag") } - ) - Button( - action: { - archive() - }, - label: { - Label( - item.isArchived ? "Unarchive" : "Archive", - systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox" - ) - } - ) - Button( - action: { - dataService.updateLinkReadingProgress(itemID: item.unwrappedID, readingProgress: 0, anchorIndex: 0) - }, - label: { Label("Reset Read Location", systemImage: "arrow.counterclockwise.circle") } - ) - Button( - action: { /* viewModel.downloadAudio(audioController: audioController, item: item) */ }, - label: { Label("Download Audio", systemImage: "icloud.and.arrow.down") } - ) - Button( - action: share, - label: { Label("Share Original", systemImage: "square.and.arrow.up") } - ) - Button( - action: delete, - label: { Label("Delete", systemImage: "trash") } - ) - } + menuItems(for: item) }, label: { #if os(iOS) diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderViewModel.swift index 18852a450..8cf73084e 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderViewModel.swift @@ -1,6 +1,7 @@ import Models import Services import SwiftUI +import Views import WebKit struct SafariWebLink: Identifiable { @@ -12,6 +13,24 @@ struct SafariWebLink: Identifiable { @Published var articleContent: ArticleContent? @Published var errorMessage: String? + func hasOriginalUrl(_ item: LinkedItem) -> Bool { + if let pageURLString = item.pageURLString, let host = URL(string: pageURLString)?.host { + if host == "omnivore.app" { + return false + } + return true + } + return false + } + + func downloadAudio(audioController: AudioController, item: LinkedItem) { + Snackbar.show(message: "Downloading Offline Audio") + Task { + let downloaded = await audioController.downloadForOffline(itemID: item.unwrappedID) + Snackbar.show(message: downloaded ? "Audio file downloaded" : "Error downloading audio") + } + } + func loadContent(dataService: DataService, username: String, itemID: String, retryCount: Int = 0) async { errorMessage = nil diff --git a/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift b/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift index e8785777e..18a464152 100644 --- a/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift +++ b/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift @@ -62,11 +62,22 @@ public extension LinkedItem { return (pageURLString ?? "").hasSuffix("pdf") } + func hideHost(_ host: String) -> Bool { + switch host { + case "storage.googleapis.com": + return true + case "omnivore.app": + return true + default: + return false + } + } + var publisherDisplayName: String? { if let siteName = siteName { return siteName } - if let host = URL(string: publisherURLString ?? pageURLString ?? "")?.host, host != "storage.googleapis.com" { + if let host = URL(string: publisherURLString ?? pageURLString ?? "")?.host, !hideHost(host) { return host } return nil diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift index fdb4a686a..24bf26a2c 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift @@ -27,172 +27,6 @@ case high } - // Somewhat based on: https://github.com/neekeetab/CachingPlayerItem/blob/master/CachingPlayerItem.swift - class SpeechPlayerItem: AVPlayerItem { - let resourceLoaderDelegate = ResourceLoaderDelegate() - let session: AudioController - let speechItem: SpeechItem - var speechMarks: [SpeechMark]? - - let completed: () -> Void - - var observer: Any? - - init(session: AudioController, speechItem: SpeechItem, completed: @escaping () -> Void) { - self.speechItem = speechItem - self.session = session - self.completed = completed - - guard let fakeUrl = URL(string: "app.omnivore.speech://\(speechItem.localAudioURL.path).mp3") else { - fatalError("internal inconsistency") - } - - let asset = AVURLAsset(url: fakeUrl) - asset.resourceLoader.setDelegate(resourceLoaderDelegate, queue: DispatchQueue.main) - - super.init(asset: asset, automaticallyLoadedAssetKeys: nil) - - resourceLoaderDelegate.owner = self - - self.observer = observe(\.status, options: [.new]) { item, _ in - if item.status == .readyToPlay { - let duration = CMTimeGetSeconds(item.duration) - item.session.updateDuration(forItem: item.speechItem, newDuration: duration) - } - } - - NotificationCenter.default.addObserver( - forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, - object: self, queue: OperationQueue.main - ) { [weak self] _ in - guard let self = self else { return } - self.completed() - } - } - - deinit { - observer = nil - resourceLoaderDelegate.session?.invalidateAndCancel() - } - - open func download() { - if resourceLoaderDelegate.session == nil { - resourceLoaderDelegate.startDataRequest(with: speechItem.urlRequest) - } - } - - @objc func playbackStalledHandler() { - print("playback stalled...") - } - - class ResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate { - var session: URLSession? - var mediaData: Data? - var pendingRequests = Set() - weak var owner: SpeechPlayerItem? - - func resourceLoader(_: AVAssetResourceLoader, - shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool - { - if owner == nil { - return true - } - - if session == nil { - guard let initialUrl = owner?.speechItem.urlRequest else { - fatalError("internal inconsistency") - } - - startDataRequest(with: initialUrl) - } - - pendingRequests.insert(loadingRequest) - processPendingRequests() - return true - } - - func startDataRequest(with _: URLRequest) { - let configuration = URLSessionConfiguration.default - configuration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData - session = URLSession(configuration: configuration) - - Task { - guard let speechItem = self.owner?.speechItem else { - // This probably can't happen, but if it does, just returning should - // let AVPlayer try again. - print("No speech item found: ", self.owner?.speechItem) - return - } - - // TODO: how do we want to propogate this and handle it in the player - let speechData = try? await SpeechSynthesizer.download(speechItem: speechItem, session: self.session) - DispatchQueue.main.async { - if speechData == nil { - self.session = nil - } - if let owner = self.owner, let speechData = speechData { - owner.speechMarks = speechData.speechMarks - } - self.mediaData = speechData?.audioData - - self.processPendingRequests() - } - } - } - - func resourceLoader(_: AVAssetResourceLoader, didCancel loadingRequest: AVAssetResourceLoadingRequest) { - pendingRequests.remove(loadingRequest) - } - - func processPendingRequests() { - let requestsFulfilled = Set(pendingRequests.compactMap { - self.fillInContentInformationRequest($0.contentInformationRequest) - if self.haveEnoughDataToFulfillRequest($0.dataRequest!) { - $0.finishLoading() - return $0 - } - return nil - }) - - // remove fulfilled requests from pending requests - _ = requestsFulfilled.map { self.pendingRequests.remove($0) } - } - - func fillInContentInformationRequest(_ contentInformationRequest: AVAssetResourceLoadingContentInformationRequest?) { - contentInformationRequest?.contentType = UTType.mp3.identifier - - if let mediaData = mediaData { - contentInformationRequest?.isByteRangeAccessSupported = true - contentInformationRequest?.contentLength = Int64(mediaData.count) - } - } - - func haveEnoughDataToFulfillRequest(_ dataRequest: AVAssetResourceLoadingDataRequest) -> Bool { - let requestedOffset = Int(dataRequest.requestedOffset) - let requestedLength = dataRequest.requestedLength - let currentOffset = Int(dataRequest.currentOffset) - - guard let songDataUnwrapped = mediaData, - songDataUnwrapped.count > currentOffset - else { - // Don't have any data at all for this request. - return false - } - - let bytesToRespond = min(songDataUnwrapped.count - currentOffset, requestedLength) - let range = Range(uncheckedBounds: (currentOffset, currentOffset + bytesToRespond)) - let dataToRespond = songDataUnwrapped.subdata(in: range) - dataRequest.respond(with: dataToRespond) - - return songDataUnwrapped.count >= requestedLength + requestedOffset - } - - deinit { - session?.invalidateAndCancel() - } - } - } - // swiftlint:disable all public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate { @Published public var state: AudioControllerState = .stopped @@ -205,10 +39,13 @@ @Published public var duration: TimeInterval = 0 @Published public var timeElapsedString: String? @Published public var durationString: String? - @Published public var voiceList: [(name: String, key: String, category: VoiceCategory, selected: Bool)]? + @Published public var voiceList: [VoiceItem]? + @Published public var realisticVoiceList: [VoiceItem]? @Published public var textItems: [String]? + @Published public var playbackError: Bool = false + let dataService: DataService var timer: Timer? @@ -219,11 +56,14 @@ var durations: [Double]? var lastReadUpdate = 0.0 + var samplePlayer: AVAudioPlayer? + public init(dataService: DataService) { self.dataService = dataService super.init() self.voiceList = generateVoiceList() + self.realisticVoiceList = generateRealisticVoiceList() } deinit { @@ -277,11 +117,25 @@ } } - public func generateVoiceList() -> [(name: String, key: String, category: VoiceCategory, selected: Bool)] { + public func stopWithError() { + stop() + playbackError = true + } + + public func generateVoiceList() -> [VoiceItem] { Voices.Pairs.flatMap { voicePair in [ - (name: voicePair.firstName, key: voicePair.firstKey, category: voicePair.category, selected: voicePair.firstKey == currentVoice), - (name: voicePair.secondName, key: voicePair.secondKey, category: voicePair.category, selected: voicePair.secondKey == currentVoice) + VoiceItem(name: voicePair.firstName, key: voicePair.firstKey, category: voicePair.category, selected: voicePair.firstKey == currentVoice), + VoiceItem(name: voicePair.secondName, key: voicePair.secondKey, category: voicePair.category, selected: voicePair.secondKey == currentVoice) + ] + }.sorted { $0.name.lowercased() < $1.name.lowercased() } + } + + public func generateRealisticVoiceList() -> [VoiceItem] { + Voices.UltraPairs.flatMap { voicePair in + [ + VoiceItem(name: voicePair.firstName, key: voicePair.firstKey, category: voicePair.category, selected: voicePair.firstKey == currentVoice), + VoiceItem(name: voicePair.secondName, key: voicePair.secondKey, category: voicePair.category, selected: voicePair.secondKey == currentVoice) ] }.sorted { $0.name.lowercased() < $1.name.lowercased() } } @@ -293,7 +147,7 @@ for itemID in itemIDs { if let document = try? await downloadSpeechFile(itemID: itemID, priority: .low) { - let synthesizer = SpeechSynthesizer(appEnvironment: dataService.appEnvironment, networker: dataService.networker, document: document) + let synthesizer = SpeechSynthesizer(appEnvironment: dataService.appEnvironment, networker: dataService.networker, document: document, speechAuthHeader: speechAuthHeader) do { try await synthesizer.preload() return true @@ -307,7 +161,7 @@ public func downloadForOffline(itemID: String) async -> Bool { if let document = try? await downloadSpeechFile(itemID: itemID, priority: .low) { - let synthesizer = SpeechSynthesizer(appEnvironment: dataService.appEnvironment, networker: dataService.networker, document: document) + let synthesizer = SpeechSynthesizer(appEnvironment: dataService.appEnvironment, networker: dataService.networker, document: document, speechAuthHeader: speechAuthHeader) for item in synthesizer.createPlayerItems(from: 0) { do { _ = try await SpeechSynthesizer.download(speechItem: item, redownloadCached: true) @@ -419,6 +273,18 @@ @AppStorage(UserDefaultKey.textToSpeechPreloadEnabled.rawValue) public var preloadEnabled = false + @AppStorage(UserDefaultKey.textToSpeechUseUltraRealisticVoices.rawValue) public var useUltraRealisticVoices = false + + @AppStorage(UserDefaultKey.textToSpeechUltraRealisticFeatureKey.rawValue) public var ultraRealisticFeatureKey: String = "" + @AppStorage(UserDefaultKey.textToSpeechUltraRealisticFeatureRequested.rawValue) public var ultraRealisticFeatureRequested: Bool = false + + var speechAuthHeader: String? { + if Voices.isUltraRealisticVoice(currentVoice), !ultraRealisticFeatureKey.isEmpty { + return ultraRealisticFeatureKey + } + return nil + } + public var currentVoiceLanguage: VoiceLanguage { Voices.Languages.first(where: { $0.key == currentLanguage }) ?? Voices.English } @@ -458,6 +324,7 @@ set { _currentVoice = newValue voiceList = generateVoiceList() + realisticVoiceList = generateRealisticVoiceList() var currentIdx = 0 var currentOffset = 0.0 @@ -520,7 +387,7 @@ // Sometimes we get negatives currentItemOffset = max(currentItemOffset, 0) - let idx = item.speechItem.audioIdx + let idx = currentAudioIndex // item.speechItem.audioIdx let currentItem = document?.utterances[idx].text ?? "" let currentReadIndex = currentItem.index(currentItem.startIndex, offsetBy: min(currentItemOffset, currentItem.count)) let lastItem = String(currentItem[.. Bool { + if let samplePlayer = self.samplePlayer, let url = Bundle(url: UtilsPackage.bundleURL)?.url(forResource: voice, withExtension: "mp3") { + return samplePlayer.url == url && samplePlayer.isPlaying + } + return false + } + + public func stopVoiceSample() { + if let samplePlayer = self.samplePlayer { + samplePlayer.stop() + self.samplePlayer = nil + } + } + private func updateDurations(oldPlayback: Double, newPlayback: Double) { if let oldDurations = durations { durations = oldDurations.map { $0 * oldPlayback / newPlayback } @@ -684,10 +571,11 @@ if let player = player { observer = player.observe(\.currentItem, options: [.new]) { _, _ in self.currentAudioIndex = (player.currentItem as? SpeechPlayerItem)?.speechItem.audioIdx ?? 0 + self.updateReadText() } } - let synthesizer = SpeechSynthesizer(appEnvironment: dataService.appEnvironment, networker: dataService.networker, document: document) + let synthesizer = SpeechSynthesizer(appEnvironment: dataService.appEnvironment, networker: dataService.networker, document: document, speechAuthHeader: speechAuthHeader) durations = synthesizer.estimatedDurations(forSpeed: playbackRate) self.synthesizer = synthesizer @@ -696,9 +584,12 @@ func synthesizeFrom(start: Int, playWhenReady: Bool, atOffset: Double = 0.0) { if let synthesizer = self.synthesizer, let items = self.synthesizer?.createPlayerItems(from: start) { + let prefetchQueue = OperationQueue() + prefetchQueue.maxConcurrentOperationCount = 5 + for speechItem in items { let isLast = speechItem.audioIdx == synthesizer.document.utterances.count - 1 - let playerItem = SpeechPlayerItem(session: self, speechItem: speechItem) { + let playerItem = SpeechPlayerItem(session: self, prefetchQueue: prefetchQueue, speechItem: speechItem) { if isLast { self.player?.pause() self.state = .reachedEnd @@ -731,6 +622,7 @@ } public func unpause() { + stopVoiceSample() if let player = player { player.rate = Float(playbackRate) state = .playing @@ -780,6 +672,14 @@ case .reset: if let playerItem = player.currentItem as? SpeechPlayerItem { let itemElapsed = playerItem.status == .readyToPlay ? CMTimeGetSeconds(playerItem.currentTime()) : 0 + if itemElapsed >= CMTimeGetSeconds(playerItem.duration) + 0.5 { + // Occasionally AV wont send an event for a new item starting for ~3s, if this + // happens we can try to manually update the time + if playerItem.speechItem.audioIdx + 1 < (document?.utterances.count ?? 0) { + currentAudioIndex = playerItem.speechItem.audioIdx + 1 + } + } + timeElapsed = durationBefore(playerIndex: playerItem.speechItem.audioIdx) + itemElapsed timeElapsedString = formatTimeInterval(timeElapsed) diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/PrefetchSpeechItemOperation.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/PrefetchSpeechItemOperation.swift new file mode 100644 index 000000000..cfbc8ea50 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/PrefetchSpeechItemOperation.swift @@ -0,0 +1,76 @@ +// +// PrefetchSpeechItemOperation.swift +// +// +// Created by Jackson Harper on 11/9/22. +// + +import Foundation +import Models +import Utils + +final class PrefetchSpeechItemOperation: Operation, URLSessionDelegate { + let speechItem: SpeechItem + let session: URLSession + + enum State: Int { + case created + case started + case finished + } + + init(speechItem: SpeechItem) { + self.speechItem = speechItem + self.state = .created + + let configuration = URLSessionConfiguration.default + configuration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData + self.session = URLSession(configuration: configuration) + } + + public var state: State = .created { + willSet { + willChangeValue(forKey: "isReady") + willChangeValue(forKey: "isExecuting") + willChangeValue(forKey: "isFinished") + willChangeValue(forKey: "isCancelled") + } + didSet { + didChangeValue(forKey: "isCancelled") + didChangeValue(forKey: "isFinished") + didChangeValue(forKey: "isExecuting") + didChangeValue(forKey: "isReady") + } + } + + override var isAsynchronous: Bool { + true + } + + override var isReady: Bool { + true + } + + override var isExecuting: Bool { + self.state == .started + } + + override var isFinished: Bool { + self.state == .finished + } + + override func start() { + guard !isCancelled else { return } + state = .started + + Task { + _ = try await SpeechSynthesizer.download(speechItem: speechItem, session: session) + state = .finished + } + } + + override func cancel() { + session.invalidateAndCancel() + super.cancel() + } +} diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/SpeechPlayerItem.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/SpeechPlayerItem.swift new file mode 100644 index 000000000..954fed78e --- /dev/null +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/SpeechPlayerItem.swift @@ -0,0 +1,208 @@ +// +// SpeechPlayerItem.swift +// +// +// Created by Jackson Harper on 11/9/22. +// + +import AVFoundation +import Foundation + +import Models + +// Somewhat based on: https://github.com/neekeetab/CachingPlayerItem/blob/master/CachingPlayerItem.swift +class SpeechPlayerItem: AVPlayerItem { + let resourceLoaderDelegate = ResourceLoaderDelegate() + let session: AudioController + let speechItem: SpeechItem + var speechMarks: [SpeechMark]? + var prefetchOperation: PrefetchSpeechItemOperation? + + let completed: () -> Void + + var observer: Any? + + init(session: AudioController, prefetchQueue: OperationQueue, speechItem: SpeechItem, completed: @escaping () -> Void) { + self.speechItem = speechItem + self.session = session + self.completed = completed + + guard let fakeUrl = URL(string: "app.omnivore.speech://\(speechItem.localAudioURL.path).mp3") else { + fatalError("internal inconsistency") + } + + let asset = AVURLAsset(url: fakeUrl) + asset.resourceLoader.setDelegate(resourceLoaderDelegate, queue: DispatchQueue.main) + + super.init(asset: asset, automaticallyLoadedAssetKeys: nil) + + resourceLoaderDelegate.owner = self + + self.observer = observe(\.status, options: [.new]) { item, _ in + if item.status == .readyToPlay { + let duration = CMTimeGetSeconds(item.duration) + item.session.updateDuration(forItem: item.speechItem, newDuration: duration) + } + if item.status == .failed { + item.session.stopWithError() + } + } + + NotificationCenter.default.addObserver( + forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, + object: self, queue: OperationQueue.main + ) { [weak self] _ in + guard let self = self else { return } + self.completed() + } + + self.prefetchOperation = PrefetchSpeechItemOperation(speechItem: speechItem) + if let prefetchOperation = self.prefetchOperation { + prefetchQueue.addOperation(prefetchOperation) + } + } + + deinit { + observer = nil + prefetchOperation?.cancel() + resourceLoaderDelegate.session?.invalidateAndCancel() + } + + open func download() { + if resourceLoaderDelegate.session == nil { + resourceLoaderDelegate.startDataRequest(with: speechItem.urlRequest) + } + } + + @objc func playbackStalledHandler() { + print("playback stalled...") + } + + class ResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate { + var session: URLSession? + var mediaData: Data? + var pendingRequests = Set() + weak var owner: SpeechPlayerItem? + + func resourceLoader(_: AVAssetResourceLoader, + shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool + { + if owner == nil { + return true + } + + if session == nil { + guard let initialUrl = owner?.speechItem.urlRequest else { + fatalError("internal inconsistency") + } + + startDataRequest(with: initialUrl) + } + + pendingRequests.insert(loadingRequest) + processPendingRequests() + return true + } + + func startDataRequest(with _: URLRequest) { + let configuration = URLSessionConfiguration.default + configuration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData + session = URLSession(configuration: configuration) + + Task { + guard let speechItem = self.owner?.speechItem else { + // This probably can't happen, but if it does, just returning should + DispatchQueue.main.async { + self.processPlaybackError(error: BasicError.message(messageText: "No speech item found.")) + } + return + } + + do { + let speechData = try await SpeechSynthesizer.download(speechItem: speechItem, session: self.session ?? URLSession.shared) + + DispatchQueue.main.async { + if speechData == nil { + self.session = nil + self.processPlaybackError(error: BasicError.message(messageText: "Unable to download speech data.")) + return + } + + if let owner = self.owner, let speechData = speechData { + owner.speechMarks = speechData.speechMarks + } + self.mediaData = speechData?.audioData + + self.processPendingRequests() + } + } catch URLError.cancelled { + print("cancelled request error being ignored") + } catch { + DispatchQueue.main.async { + self.processPlaybackError(error: error) + } + } + } + } + + func resourceLoader(_: AVAssetResourceLoader, didCancel loadingRequest: AVAssetResourceLoadingRequest) { + pendingRequests.remove(loadingRequest) + } + + func processPendingRequests() { + let requestsFulfilled = Set(pendingRequests.compactMap { + self.fillInContentInformationRequest($0.contentInformationRequest) + if self.haveEnoughDataToFulfillRequest($0.dataRequest!) { + $0.finishLoading() + return $0 + } + return nil + }) + + // remove fulfilled requests from pending requests + _ = requestsFulfilled.map { self.pendingRequests.remove($0) } + } + + func processPlaybackError(error: Error?) { + let requestsFulfilled = Set(pendingRequests.compactMap { + $0.finishLoading(with: error) + return nil + }) + + _ = requestsFulfilled.map { self.pendingRequests.remove($0) } + } + + func fillInContentInformationRequest(_ contentInformationRequest: AVAssetResourceLoadingContentInformationRequest?) { + contentInformationRequest?.contentType = UTType.mp3.identifier + + if let mediaData = mediaData { + contentInformationRequest?.isByteRangeAccessSupported = true + contentInformationRequest?.contentLength = Int64(mediaData.count) + } + } + + func haveEnoughDataToFulfillRequest(_ dataRequest: AVAssetResourceLoadingDataRequest) -> Bool { + let requestedOffset = Int(dataRequest.requestedOffset) + let requestedLength = dataRequest.requestedLength + let currentOffset = Int(dataRequest.currentOffset) + + guard let songDataUnwrapped = mediaData, + songDataUnwrapped.count > currentOffset + else { + // Don't have any data at all for this request. + return false + } + + let bytesToRespond = min(songDataUnwrapped.count - currentOffset, requestedLength) + let range = Range(uncheckedBounds: (currentOffset, currentOffset + bytesToRespond)) + let dataToRespond = songDataUnwrapped.subdata(in: range) + dataRequest.respond(with: dataToRespond) + + return songDataUnwrapped.count >= requestedLength + requestedOffset + } + + deinit { + session?.invalidateAndCancel() + } + } +} diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/SpeechSynthesizer.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/SpeechSynthesizer.swift index 370d1f8e9..dcac71919 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/SpeechSynthesizer.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/SpeechSynthesizer.swift @@ -16,6 +16,7 @@ struct UtteranceRequest: Codable { let voice: String let language: String let rate: String + let isUltraRealisticVoice: Bool } struct Utterance: Decodable { @@ -26,10 +27,12 @@ struct Utterance: Decodable { public let wordCount: Double func toSSML(document: SpeechDocument) throws -> Data? { + let usedVoice = voice ?? document.defaultVoice let request = UtteranceRequest(text: text, - voice: voice ?? document.defaultVoice, + voice: usedVoice, language: document.language, - rate: "1.1") + rate: "1.1", + isUltraRealisticVoice: Voices.isUltraRealisticVoice(usedVoice)) return try JSONEncoder().encode(request) } } @@ -78,11 +81,13 @@ struct SpeechSynthesizer { let document: SpeechDocument let appEnvironment: AppEnvironment let networker: Networker + let speechAuthHeader: String? - init(appEnvironment: AppEnvironment, networker: Networker, document: SpeechDocument) { + init(appEnvironment: AppEnvironment, networker: Networker, document: SpeechDocument, speechAuthHeader: String?) { self.appEnvironment = appEnvironment self.networker = networker self.document = document + self.speechAuthHeader = speechAuthHeader } func estimatedDurations(forSpeed speed: Double) -> [Double] { @@ -120,7 +125,7 @@ struct SpeechSynthesizer { func createPlayerItems(from: Int) -> [SpeechItem] { var result: [SpeechItem] = [] - for idx in from ..< min(7, document.utterances.count) { + for idx in from ..< document.utterances.count { let utterance = document.utterances[idx] let voiceStr = utterance.voice ?? document.defaultVoice let segmentStr = String(format: "%04d", arguments: [idx]) @@ -156,34 +161,52 @@ struct SpeechSynthesizer { request.setValue(value, forHTTPHeaderField: header) } + if let speechAuthHeader = speechAuthHeader { + request.setValue(speechAuthHeader, forHTTPHeaderField: "Authorization") + } + return request } + static func downloadData(session: URLSession, request: URLRequest) async throws -> Data { + do { + let result: (Data, URLResponse)? = try await session.data(for: request) + guard let httpResponse = result?.1 as? HTTPURLResponse, 200 ..< 300 ~= httpResponse.statusCode else { + print("error: ", result?.1) + throw BasicError.message(messageText: "audioFetch failed. no response or bad status code.") + } + + guard let data = result?.0 else { + throw BasicError.message(messageText: "audioFetch failed. no data received.") + } + + return data + } catch URLError.cancelled { + print("cancled request error being ignored") + return Data() + } catch { + print("ERROR DOWNLOADING AUDIO DATA", error) + throw error + } + } + static func download(speechItem: SpeechItem, redownloadCached: Bool = false, - session: URLSession? = URLSession.shared) async throws -> SynthesizeData? + session: URLSession = URLSession.shared) async throws -> SynthesizeData? { let decoder = JSONDecoder() if !redownloadCached { - if let speechMarksData = try? Data(contentsOf: speechItem.localSpeechURL), - let speechMarks = try? decoder.decode([SpeechMark].self, from: speechMarksData), - let localData = try? Data(contentsOf: speechItem.localAudioURL) - { + if let localData = try? Data(contentsOf: speechItem.localAudioURL) { + var speechMarks: [SpeechMark]? + if let speechMarksData = try? Data(contentsOf: speechItem.localSpeechURL) { + speechMarks = try? decoder.decode([SpeechMark].self, from: speechMarksData) + } return SynthesizeData(audioData: localData, speechMarks: speechMarks) } } - let request = speechItem.urlRequest - let result: (Data, URLResponse)? = try? await (session ?? URLSession.shared).data(for: request) - guard let httpResponse = result?.1 as? HTTPURLResponse, 200 ..< 300 ~= httpResponse.statusCode else { - print("error: ", result?.1 as Any) - throw BasicError.message(messageText: "audioFetch failed. no response or bad status code.") - } - - guard let data = result?.0 else { - throw BasicError.message(messageText: "audioFetch failed. no data received.") - } + let data = try await downloadData(session: session, request: speechItem.urlRequest) let tempPath = FileManager.default .urls(for: .cachesDirectory, in: .userDomainMask)[0] @@ -204,8 +227,6 @@ struct SpeechSynthesizer { try? FileManager.default.removeItem(at: speechItem.localAudioURL) try FileManager.default.moveItem(at: tempPath, to: speechItem.localAudioURL) - let savedData = try? Data(contentsOf: speechItem.localAudioURL) - let encoder = JSONEncoder() let speechMarksData = try encoder.encode(jsonData.speechMarks) try speechMarksData.write(to: tempSMPath) @@ -214,6 +235,7 @@ struct SpeechSynthesizer { return SynthesizeData(audioData: audioData, speechMarks: jsonData.speechMarks) } catch { + print("ERROR DOWNLOADING SPEECH DATA:", error) let errorMessage = "audioFetch failed. could not write MP3 data to disk" throw BasicError.message(messageText: errorMessage) } @@ -222,12 +244,12 @@ struct SpeechSynthesizer { struct SynthesizeResult: Decodable { let audioData: String - let speechMarks: [SpeechMark] + let speechMarks: [SpeechMark]? } struct SynthesizeData: Decodable { let audioData: Data - let speechMarks: [SpeechMark] + let speechMarks: [SpeechMark]? } extension Data { diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/Voices.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/Voices.swift index 71c09b185..84286fdf2 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/Voices.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/Voices.swift @@ -14,6 +14,13 @@ public struct VoiceLanguage { public let categories: [VoiceCategory] } +public struct VoiceItem { + public let name: String + public let key: String + public let category: VoiceCategory + public let selected: Bool +} + public enum VoiceCategory: String, CaseIterable { case enUS = "English (US)" case enAU = "English (Australia)" @@ -29,10 +36,10 @@ public enum VoiceCategory: String, CaseIterable { } public struct VoicePair { - let firstKey: String + public let firstKey: String let secondKey: String - let firstName: String + public let firstName: String let secondName: String let language: String @@ -40,6 +47,12 @@ public struct VoicePair { } public enum Voices { + public static func isUltraRealisticVoice(_ voiceKey: String) -> Bool { + UltraPairs.contains(where: { voice in + voice.firstKey == voiceKey || voice.secondKey == voiceKey + }) + } + public static let English = VoiceLanguage(key: "en", name: "English", defaultVoice: "en-US-ChristopherNeural", @@ -72,4 +85,20 @@ public enum Voices { VoicePair(firstKey: "de-DE-ChristophNeural", secondKey: "de-DE-LouisaNeural", firstName: "Christoph", secondName: "Louisa", language: "de-DE", category: .deDE), VoicePair(firstKey: "ja-JP-NanamiNeural", secondKey: "ja-JP-KeitaNeural", firstName: "Nanami", secondName: "Keita", language: "ja-JP", category: .jaJP) ] + + public static let UltraPairs = [ + VoicePair(firstKey: "Larry", secondKey: "Susan", firstName: "Larry", secondName: "Susan", language: "en-US", category: .enUS), + VoicePair(firstKey: "Jordan", secondKey: "William", firstName: "Jordan", secondName: "William", language: "en-US", category: .enUS), + VoicePair(firstKey: "Evelyn", secondKey: "Axel", firstName: "Evelyn", secondName: "Axel", language: "en-US", category: .enUS), + VoicePair(firstKey: "Nova", secondKey: "Owen", firstName: "Nova", secondName: "Owen", language: "en-US", category: .enUS), + VoicePair(firstKey: "Frankie", secondKey: "Natalie", firstName: "Frankie", secondName: "Natalie", language: "en-US", category: .enUS), + + VoicePair(firstKey: "Daniel", secondKey: "Charlotte", firstName: "Daniel", secondName: "Charlotte", language: "en-CA", category: .enCA), + VoicePair(firstKey: "Lillian", secondKey: "Aurora", firstName: "Lillian", secondName: "Aurora", language: "en-UK", category: .enUK), + + VoicePair(firstKey: "Oliver", secondKey: "Arthur", firstName: "Oliver", secondName: "Arthur", language: "en-UK", category: .enUK), + VoicePair(firstKey: "Frederick", secondKey: "Hunter", firstName: "Frederick", secondName: "Hunter", language: "en-UK", category: .enUK), + VoicePair(firstKey: "Nolan", secondKey: "Phoebe", firstName: "Nolan", secondName: "Phoebe", language: "en-UK", category: .enUK), + VoicePair(firstKey: "Daisy", secondKey: "Stella", firstName: "Daisy", secondName: "Stella", language: "en-UK", category: .enUK) + ] } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/OptIntoFeature.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/OptIntoFeature.swift new file mode 100644 index 000000000..2202b95db --- /dev/null +++ b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/OptIntoFeature.swift @@ -0,0 +1,57 @@ +// +// File.swift +// +// +// Created by Jackson Harper on 11/10/22. +// + +import Foundation +import Models +import SwiftGraphQL + +public struct Feature { + public let name: String + public let token: String + public let granted: Bool +} + +public extension DataService { + func optInFeature(name: String) async throws -> Feature? { + enum MutationResult { + case success(feature: Feature) + case error(errorCode: Enums.OptInFeatureErrorCode) + } + + let featureSelection = Selection.Feature { Feature(name: try $0.name(), token: try $0.token(), granted: try $0.grantedAt() != nil) } + let selection = Selection { + try $0.on( + optInFeatureError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) }, + optInFeatureSuccess: .init { .success(feature: try $0.feature(selection: featureSelection)) } + ) + } + + let mutation = Selection.Mutation { + try $0.optInFeature(input: InputObjects.OptInFeatureInput(name: name), + selection: selection) + } + + let path = appEnvironment.graphqlPath + let headers = networker.defaultHeaders + + return try await withCheckedThrowingContinuation { continuation in + send(mutation, to: path, headers: headers) { queryResult in + guard let payload = try? queryResult.get() else { + continuation.resume(throwing: BasicError.message(messageText: "network error")) + return + } + + switch payload.data { + case let .success(feature: feature): + continuation.resume(returning: feature) + case let .error(errorCode: errorCode): + continuation.resume(throwing: BasicError.message(messageText: errorCode.rawValue)) + } + } + } + } +} diff --git a/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift b/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift index 622ded52e..b4254d035 100644 --- a/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift +++ b/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift @@ -14,5 +14,5 @@ public enum FeatureFlag { public static let enableShareButton = false public static let enableSnooze = false public static let enableGridCardsOnPhone = false - public static let enableHighlightsView = true + public static let enableUltraRealisticVoices = false } diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Adrian.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Adrian.mp3 new file mode 100644 index 000000000..35cf1575a Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Adrian.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Alexander.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Alexander.mp3 new file mode 100644 index 000000000..5fcdb83ca Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Alexander.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Anthony.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Anthony.mp3 new file mode 100644 index 000000000..fc29a45a7 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Anthony.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Arthur.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Arthur.mp3 new file mode 100644 index 000000000..603474106 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Arthur.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Aurora.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Aurora.mp3 new file mode 100644 index 000000000..39d303ca6 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Aurora.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Axel.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Axel.mp3 new file mode 100644 index 000000000..7bde1f4f2 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Axel.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Carter.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Carter.mp3 new file mode 100644 index 000000000..e6317d3b2 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Carter.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Charlotte.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Charlotte.mp3 new file mode 100644 index 000000000..0bdf7bc87 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Charlotte.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Daisy.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Daisy.mp3 new file mode 100644 index 000000000..762324745 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Daisy.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Daniel.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Daniel.mp3 new file mode 100644 index 000000000..d9364bddc Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Daniel.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Darcie.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Darcie.mp3 new file mode 100644 index 000000000..002677b61 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Darcie.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Ellie.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Ellie.mp3 new file mode 100644 index 000000000..09998540d Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Ellie.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Evelyn.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Evelyn.mp3 new file mode 100644 index 000000000..6aa6f5ca4 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Evelyn.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Frankie.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Frankie.mp3 new file mode 100644 index 000000000..e944729c1 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Frankie.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Frederick.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Frederick.mp3 new file mode 100644 index 000000000..914182ff2 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Frederick.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Harrison.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Harrison.mp3 new file mode 100644 index 000000000..535d548d4 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Harrison.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Hudson.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Hudson.mp3 new file mode 100644 index 000000000..b05a4eb1a Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Hudson.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Hunter.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Hunter.mp3 new file mode 100644 index 000000000..515b3df19 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Hunter.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Jordan.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Jordan.mp3 new file mode 100644 index 000000000..0de8cb8c5 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Jordan.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Julian.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Julian.mp3 new file mode 100644 index 000000000..79d4a3d43 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Julian.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Larry.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Larry.mp3 new file mode 100644 index 000000000..622705657 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Larry.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Lillian.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Lillian.mp3 new file mode 100644 index 000000000..0b527682f Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Lillian.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Lottie.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Lottie.mp3 new file mode 100644 index 000000000..b1cd0ca06 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Lottie.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Maverick.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Maverick.mp3 new file mode 100644 index 000000000..a1fd739fa Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Maverick.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Natalie.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Natalie.mp3 new file mode 100644 index 000000000..3f11f9d6b Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Natalie.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Nolan.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Nolan.mp3 new file mode 100644 index 000000000..98268b2cf Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Nolan.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Nova.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Nova.mp3 new file mode 100644 index 000000000..d1f548bc4 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Nova.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Oliver.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Oliver.mp3 new file mode 100644 index 000000000..17c7c04be Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Oliver.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Owen.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Owen.mp3 new file mode 100644 index 000000000..41b222436 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Owen.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Phoebe.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Phoebe.mp3 new file mode 100644 index 000000000..b88d8b7d8 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Phoebe.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Stella.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Stella.mp3 new file mode 100644 index 000000000..0e852c031 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Stella.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Susan.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Susan.mp3 new file mode 100644 index 000000000..427044135 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Susan.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Theodore.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Theodore.mp3 new file mode 100644 index 000000000..c36f47017 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/Theodore.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/William.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/William.mp3 new file mode 100644 index 000000000..cef541b01 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/William.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-CH-LeniNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-CH-LeniNeural.mp3 new file mode 100644 index 000000000..165d8354a Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-CH-LeniNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-AmalaNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-AmalaNeural.mp3 new file mode 100644 index 000000000..ed406cc94 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-AmalaNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-BerndNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-BerndNeural.mp3 new file mode 100644 index 000000000..10abc327e Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-BerndNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-ChristophNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-ChristophNeural.mp3 new file mode 100644 index 000000000..7344e1945 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-ChristophNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-KatjaNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-KatjaNeural.mp3 new file mode 100644 index 000000000..9c8a630c1 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-KatjaNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-LouisaNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-LouisaNeural.mp3 new file mode 100644 index 000000000..976b98d5a Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/de-DE-LouisaNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-AU-NatashaNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-AU-NatashaNeural.mp3 new file mode 100644 index 000000000..3e950e298 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-AU-NatashaNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-AU-WilliamNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-AU-WilliamNeural.mp3 new file mode 100644 index 000000000..1ed498f63 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-AU-WilliamNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-CA-ClaraNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-CA-ClaraNeural.mp3 new file mode 100644 index 000000000..7d7963d15 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-CA-ClaraNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-CA-LiamNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-CA-LiamNeural.mp3 new file mode 100644 index 000000000..d1387c1c6 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-CA-LiamNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-GB-EthanNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-GB-EthanNeural.mp3 new file mode 100644 index 000000000..fdb55cb03 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-GB-EthanNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-GB-LibbyNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-GB-LibbyNeural.mp3 new file mode 100644 index 000000000..b4db2eb6d Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-GB-LibbyNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-IE-ConnorNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-IE-ConnorNeural.mp3 new file mode 100644 index 000000000..652050533 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-IE-ConnorNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-IE-EmilyNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-IE-EmilyNeural.mp3 new file mode 100644 index 000000000..0213ab9db Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-IE-EmilyNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-IN-NeerjaNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-IN-NeerjaNeural.mp3 new file mode 100644 index 000000000..0bfe98954 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-IN-NeerjaNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-IN-PrabhatNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-IN-PrabhatNeural.mp3 new file mode 100644 index 000000000..c7c034521 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-IN-PrabhatNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-SG-LunaNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-SG-LunaNeural.mp3 new file mode 100644 index 000000000..d4e09c122 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-SG-LunaNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-SG-WayneNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-SG-WayneNeural.mp3 new file mode 100644 index 000000000..fbcd8f1e4 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-SG-WayneNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-BrandonNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-BrandonNeural.mp3 new file mode 100644 index 000000000..894cc48d1 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-BrandonNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-ChristopherNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-ChristopherNeural.mp3 new file mode 100644 index 000000000..019580fc4 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-ChristopherNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-CoraNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-CoraNeural.mp3 new file mode 100644 index 000000000..34278e3e2 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-CoraNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-ElizabethNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-ElizabethNeural.mp3 new file mode 100644 index 000000000..61f071abe Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-ElizabethNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-EricNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-EricNeural.mp3 new file mode 100644 index 000000000..518ff4ddf Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-EricNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-JennyNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-JennyNeural.mp3 new file mode 100644 index 000000000..168bac663 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/en-US-JennyNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/es-ES-AlvaroNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/es-ES-AlvaroNeural.mp3 new file mode 100644 index 000000000..a68103765 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/es-ES-AlvaroNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/es-ES-ElviraNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/es-ES-ElviraNeural.mp3 new file mode 100644 index 000000000..7cce30ad7 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/es-ES-ElviraNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/ja-JP-KeitaNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/ja-JP-KeitaNeural.mp3 new file mode 100644 index 000000000..c49d90c15 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/ja-JP-KeitaNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/ja-JP-NanamiNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/ja-JP-NanamiNeural.mp3 new file mode 100644 index 000000000..53f81a660 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/ja-JP-NanamiNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/zh-CN-XiaochenNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/zh-CN-XiaochenNeural.mp3 new file mode 100644 index 000000000..031ce0ae4 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/zh-CN-XiaochenNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/zh-CN-XiaohanNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/zh-CN-XiaohanNeural.mp3 new file mode 100644 index 000000000..7b746a4a6 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/zh-CN-XiaohanNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/zh-CN-XiaoxiaoNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/zh-CN-XiaoxiaoNeural.mp3 new file mode 100644 index 000000000..868d0f757 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/zh-CN-XiaoxiaoNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/zh-CN-YunyangNeural.mp3 b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/zh-CN-YunyangNeural.mp3 new file mode 100644 index 000000000..a6dd6e2ad Binary files /dev/null and b/apple/OmnivoreKit/Sources/Utils/Resources/VoiceSamples/zh-CN-YunyangNeural.mp3 differ diff --git a/apple/OmnivoreKit/Sources/Utils/UserDefaultKeys.swift b/apple/OmnivoreKit/Sources/Utils/UserDefaultKeys.swift index f61b0742c..ed93b0045 100644 --- a/apple/OmnivoreKit/Sources/Utils/UserDefaultKeys.swift +++ b/apple/OmnivoreKit/Sources/Utils/UserDefaultKeys.swift @@ -17,6 +17,9 @@ public enum UserDefaultKey: String { case textToSpeechPreferredVoice case textToSpeechDefaultLanguage case textToSpeechPreloadEnabled + case textToSpeechUseUltraRealisticVoices + case textToSpeechUltraRealisticFeatureKey + case textToSpeechUltraRealisticFeatureRequested case recentSearchTerms case audioPlayerExpanded case themeName diff --git a/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift b/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift index a7aaa4f53..cdb0cf5a5 100644 --- a/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift +++ b/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift @@ -3,6 +3,7 @@ import SwiftUI public extension Color { static var appBackground: Color { Color("_background", bundle: .module) } static var appDeepBackground: Color { Color("_deepBackground", bundle: .module) } + static var appGreenSuccess: Color { Color("_appGreenSuccess", bundle: .module) } // GrayScale -- adapted from Radix Colors static var appGrayBorder: Color { Color("_grayBorder", bundle: .module) } diff --git a/apple/OmnivoreKit/Sources/Views/Colors/Colors.xcassets/_appGreenSuccess.colorset/Contents.json b/apple/OmnivoreKit/Sources/Views/Colors/Colors.xcassets/_appGreenSuccess.colorset/Contents.json new file mode 100644 index 000000000..6f5bd0010 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/Colors/Colors.xcassets/_appGreenSuccess.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.294", + "green" : "0.843", + "red" : "0.196" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.294", + "green" : "0.843", + "red" : "0.196" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift index b3ac2411e..c30663b1b 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift @@ -7,7 +7,6 @@ public enum GridCardAction { case delete case editLabels case editTitle - case downloadAudio case viewHighlights } @@ -46,17 +45,19 @@ public struct GridCard: View { var contextMenuView: some View { Group { - Button( - action: { menuActionHandler(.viewHighlights) }, - label: { Label("View Highlights & Notes", systemImage: "highlighter") } - ) + if (item.highlights?.count ?? 0) > 0 { + Button( + action: { menuActionHandler(.viewHighlights) }, + label: { Label("View Highlights & Notes", systemImage: "highlighter") } + ) + } Button( action: { menuActionHandler(.editTitle) }, label: { Label("Edit Title/Description", systemImage: "textbox") } ) Button( action: { menuActionHandler(.editLabels) }, - label: { Label("Edit Labels", systemImage: "tag") } + label: { Label(item.labels?.count == 0 ? "Add Labels" : "Edit Labels", systemImage: "tag") } ) Button( action: { menuActionHandler(.toggleArchiveStatus) }, diff --git a/apple/OmnivoreKit/Sources/Views/Resources/bundle.js b/apple/OmnivoreKit/Sources/Views/Resources/bundle.js index 24f94c582..b883e45cd 100644 --- a/apple/OmnivoreKit/Sources/Views/Resources/bundle.js +++ b/apple/OmnivoreKit/Sources/Views/Resources/bundle.js @@ -1,2 +1,2 @@ /*! For license information please see bundle.js.LICENSE.txt */ -(()=>{var e,t,n={7162:(e,t,n)=>{e.exports=n(5047)},6279:function(e,t){var n="undefined"!=typeof self?self:this,r=function(){function e(){this.fetch=!1,this.DOMException=n.DOMException}return e.prototype=n,new e}();!function(e){!function(t){var n="URLSearchParams"in e,r="Symbol"in e&&"iterator"in Symbol,o="FileReader"in e&&"Blob"in e&&function(){try{return new Blob,!0}catch(e){return!1}}(),i="FormData"in e,a="ArrayBuffer"in e;if(a)var l=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],s=ArrayBuffer.isView||function(e){return e&&l.indexOf(Object.prototype.toString.call(e))>-1};function u(e){if("string"!=typeof e&&(e=String(e)),/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(e))throw new TypeError("Invalid character in header field name");return e.toLowerCase()}function c(e){return"string"!=typeof e&&(e=String(e)),e}function f(e){var t={next:function(){var t=e.shift();return{done:void 0===t,value:t}}};return r&&(t[Symbol.iterator]=function(){return t}),t}function d(e){this.map={},e instanceof d?e.forEach((function(e,t){this.append(t,e)}),this):Array.isArray(e)?e.forEach((function(e){this.append(e[0],e[1])}),this):e&&Object.getOwnPropertyNames(e).forEach((function(t){this.append(t,e[t])}),this)}function p(e){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}function h(e){return new Promise((function(t,n){e.onload=function(){t(e.result)},e.onerror=function(){n(e.error)}}))}function g(e){var t=new FileReader,n=h(t);return t.readAsArrayBuffer(e),n}function m(e){if(e.slice)return e.slice(0);var t=new Uint8Array(e.byteLength);return t.set(new Uint8Array(e)),t.buffer}function v(){return this.bodyUsed=!1,this._initBody=function(e){var t;this._bodyInit=e,e?"string"==typeof e?this._bodyText=e:o&&Blob.prototype.isPrototypeOf(e)?this._bodyBlob=e:i&&FormData.prototype.isPrototypeOf(e)?this._bodyFormData=e:n&&URLSearchParams.prototype.isPrototypeOf(e)?this._bodyText=e.toString():a&&o&&(t=e)&&DataView.prototype.isPrototypeOf(t)?(this._bodyArrayBuffer=m(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):a&&(ArrayBuffer.prototype.isPrototypeOf(e)||s(e))?this._bodyArrayBuffer=m(e):this._bodyText=e=Object.prototype.toString.call(e):this._bodyText="",this.headers.get("content-type")||("string"==typeof e?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):n&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},o&&(this.blob=function(){var e=p(this);if(e)return e;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error("could not read FormData body as blob");return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){return this._bodyArrayBuffer?p(this)||Promise.resolve(this._bodyArrayBuffer):this.blob().then(g)}),this.text=function(){var e,t,n,r=p(this);if(r)return r;if(this._bodyBlob)return e=this._bodyBlob,n=h(t=new FileReader),t.readAsText(e),n;if(this._bodyArrayBuffer)return Promise.resolve(function(e){for(var t=new Uint8Array(e),n=new Array(t.length),r=0;r-1?r:n),this.mode=t.mode||this.mode||null,this.signal=t.signal||this.signal,this.referrer=null,("GET"===this.method||"HEAD"===this.method)&&o)throw new TypeError("Body not allowed for GET or HEAD requests");this._initBody(o)}function w(e){var t=new FormData;return e.trim().split("&").forEach((function(e){if(e){var n=e.split("="),r=n.shift().replace(/\+/g," "),o=n.join("=").replace(/\+/g," ");t.append(decodeURIComponent(r),decodeURIComponent(o))}})),t}function x(e,t){t||(t={}),this.type="default",this.status=void 0===t.status?200:t.status,this.ok=this.status>=200&&this.status<300,this.statusText="statusText"in t?t.statusText:"OK",this.headers=new d(t.headers),this.url=t.url||"",this._initBody(e)}b.prototype.clone=function(){return new b(this,{body:this._bodyInit})},v.call(b.prototype),v.call(x.prototype),x.prototype.clone=function(){return new x(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new d(this.headers),url:this.url})},x.error=function(){var e=new x(null,{status:0,statusText:""});return e.type="error",e};var k=[301,302,303,307,308];x.redirect=function(e,t){if(-1===k.indexOf(t))throw new RangeError("Invalid status code");return new x(null,{status:t,headers:{location:e}})},t.DOMException=e.DOMException;try{new t.DOMException}catch(e){t.DOMException=function(e,t){this.message=e,this.name=t;var n=Error(e);this.stack=n.stack},t.DOMException.prototype=Object.create(Error.prototype),t.DOMException.prototype.constructor=t.DOMException}function E(e,n){return new Promise((function(r,i){var a=new b(e,n);if(a.signal&&a.signal.aborted)return i(new t.DOMException("Aborted","AbortError"));var l=new XMLHttpRequest;function s(){l.abort()}l.onload=function(){var e,t,n={status:l.status,statusText:l.statusText,headers:(e=l.getAllResponseHeaders()||"",t=new d,e.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach((function(e){var n=e.split(":"),r=n.shift().trim();if(r){var o=n.join(":").trim();t.append(r,o)}})),t)};n.url="responseURL"in l?l.responseURL:n.headers.get("X-Request-URL");var o="response"in l?l.response:l.responseText;r(new x(o,n))},l.onerror=function(){i(new TypeError("Network request failed"))},l.ontimeout=function(){i(new TypeError("Network request failed"))},l.onabort=function(){i(new t.DOMException("Aborted","AbortError"))},l.open(a.method,a.url,!0),"include"===a.credentials?l.withCredentials=!0:"omit"===a.credentials&&(l.withCredentials=!1),"responseType"in l&&o&&(l.responseType="blob"),a.headers.forEach((function(e,t){l.setRequestHeader(t,e)})),a.signal&&(a.signal.addEventListener("abort",s),l.onreadystatechange=function(){4===l.readyState&&a.signal.removeEventListener("abort",s)}),l.send(void 0===a._bodyInit?null:a._bodyInit)}))}E.polyfill=!0,e.fetch||(e.fetch=E,e.Headers=d,e.Request=b,e.Response=x),t.Headers=d,t.Request=b,t.Response=x,t.fetch=E,Object.defineProperty(t,"__esModule",{value:!0})}({})}(r),r.fetch.ponyfill=!0,delete r.fetch.polyfill;var o=r;(t=o.fetch).default=o.fetch,t.fetch=o.fetch,t.Headers=o.Headers,t.Request=o.Request,t.Response=o.Response,e.exports=t},1427:e=>{var t=function(){this.Diff_Timeout=1,this.Diff_EditCost=4,this.Match_Threshold=.5,this.Match_Distance=1e3,this.Patch_DeleteThreshold=.5,this.Patch_Margin=4,this.Match_MaxBits=32},n=-1;t.Diff=function(e,t){return[e,t]},t.prototype.diff_main=function(e,n,r,o){void 0===o&&(o=this.Diff_Timeout<=0?Number.MAX_VALUE:(new Date).getTime()+1e3*this.Diff_Timeout);var i=o;if(null==e||null==n)throw new Error("Null input. (diff_main)");if(e==n)return e?[new t.Diff(0,e)]:[];void 0===r&&(r=!0);var a=r,l=this.diff_commonPrefix(e,n),s=e.substring(0,l);e=e.substring(l),n=n.substring(l),l=this.diff_commonSuffix(e,n);var u=e.substring(e.length-l);e=e.substring(0,e.length-l),n=n.substring(0,n.length-l);var c=this.diff_compute_(e,n,a,i);return s&&c.unshift(new t.Diff(0,s)),u&&c.push(new t.Diff(0,u)),this.diff_cleanupMerge(c),c},t.prototype.diff_compute_=function(e,r,o,i){var a;if(!e)return[new t.Diff(1,r)];if(!r)return[new t.Diff(n,e)];var l=e.length>r.length?e:r,s=e.length>r.length?r:e,u=l.indexOf(s);if(-1!=u)return a=[new t.Diff(1,l.substring(0,u)),new t.Diff(0,s),new t.Diff(1,l.substring(u+s.length))],e.length>r.length&&(a[0][0]=a[2][0]=n),a;if(1==s.length)return[new t.Diff(n,e),new t.Diff(1,r)];var c=this.diff_halfMatch_(e,r);if(c){var f=c[0],d=c[1],p=c[2],h=c[3],g=c[4],m=this.diff_main(f,p,o,i),v=this.diff_main(d,h,o,i);return m.concat([new t.Diff(0,g)],v)}return o&&e.length>100&&r.length>100?this.diff_lineMode_(e,r,i):this.diff_bisect_(e,r,i)},t.prototype.diff_lineMode_=function(e,r,o){var i=this.diff_linesToChars_(e,r);e=i.chars1,r=i.chars2;var a=i.lineArray,l=this.diff_main(e,r,!1,o);this.diff_charsToLines_(l,a),this.diff_cleanupSemantic(l),l.push(new t.Diff(0,""));for(var s=0,u=0,c=0,f="",d="";s=1&&c>=1){l.splice(s-u-c,u+c),s=s-u-c;for(var p=this.diff_main(f,d,!1,o),h=p.length-1;h>=0;h--)l.splice(s,0,p[h]);s+=p.length}c=0,u=0,f="",d=""}s++}return l.pop(),l},t.prototype.diff_bisect_=function(e,r,o){for(var i=e.length,a=r.length,l=Math.ceil((i+a)/2),s=l,u=2*l,c=new Array(u),f=new Array(u),d=0;do);b++){for(var w=-b+g;w<=b-m;w+=2){for(var x=s+w,k=(C=w==-b||w!=b&&c[x-1]i)m+=2;else if(k>a)g+=2;else if(h&&(_=s+p-w)>=0&&_=(S=i-f[_]))return this.diff_bisectSplit_(e,r,C,k,o)}for(var E=-b+v;E<=b-y;E+=2){for(var S,_=s+E,O=(S=E==-b||E!=b&&f[_-1]i)y+=2;else if(O>a)v+=2;else if(!h){var C;if((x=s+p-E)>=0&&x=(S=i-S))return this.diff_bisectSplit_(e,r,C,k,o)}}}return[new t.Diff(n,e),new t.Diff(1,r)]},t.prototype.diff_bisectSplit_=function(e,t,n,r,o){var i=e.substring(0,n),a=t.substring(0,r),l=e.substring(n),s=t.substring(r),u=this.diff_main(i,a,!1,o),c=this.diff_main(l,s,!1,o);return u.concat(c)},t.prototype.diff_linesToChars_=function(e,t){var n=[],r={};function o(e){for(var t="",o=0,a=-1,l=n.length;ar?e=e.substring(n-r):nt.length?e:t,r=e.length>t.length?t:e;if(n.length<4||2*r.length=e.length?[r,i,a,l,c]:null}var a,l,s,u,c,f=i(n,r,Math.ceil(n.length/4)),d=i(n,r,Math.ceil(n.length/2));return f||d?(a=d?f&&f[4].length>d[4].length?f:d:f,e.length>t.length?(l=a[0],s=a[1],u=a[2],c=a[3]):(u=a[0],c=a[1],l=a[2],s=a[3]),[l,s,u,c,a[4]]):null},t.prototype.diff_cleanupSemantic=function(e){for(var r=!1,o=[],i=0,a=null,l=0,s=0,u=0,c=0,f=0;l0?o[i-1]:-1,s=0,u=0,c=0,f=0,a=null,r=!0)),l++;for(r&&this.diff_cleanupMerge(e),this.diff_cleanupSemanticLossless(e),l=1;l=g?(h>=d.length/2||h>=p.length/2)&&(e.splice(l,0,new t.Diff(0,p.substring(0,h))),e[l-1][1]=d.substring(0,d.length-h),e[l+1][1]=p.substring(h),l++):(g>=d.length/2||g>=p.length/2)&&(e.splice(l,0,new t.Diff(0,d.substring(0,g))),e[l-1][0]=1,e[l-1][1]=p.substring(0,p.length-g),e[l+1][0]=n,e[l+1][1]=d.substring(g),l++),l++}l++}},t.prototype.diff_cleanupSemanticLossless=function(e){function n(e,n){if(!e||!n)return 6;var r=e.charAt(e.length-1),o=n.charAt(0),i=r.match(t.nonAlphaNumericRegex_),a=o.match(t.nonAlphaNumericRegex_),l=i&&r.match(t.whitespaceRegex_),s=a&&o.match(t.whitespaceRegex_),u=l&&r.match(t.linebreakRegex_),c=s&&o.match(t.linebreakRegex_),f=u&&e.match(t.blanklineEndRegex_),d=c&&n.match(t.blanklineStartRegex_);return f||d?5:u||c?4:i&&!l&&s?3:l||s?2:i||a?1:0}for(var r=1;r=d&&(d=p,u=o,c=i,f=a)}e[r-1][1]!=u&&(u?e[r-1][1]=u:(e.splice(r-1,1),r--),e[r][1]=c,f?e[r+1][1]=f:(e.splice(r+1,1),r--))}r++}},t.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/,t.whitespaceRegex_=/\s/,t.linebreakRegex_=/[\r\n]/,t.blanklineEndRegex_=/\n\r?\n$/,t.blanklineStartRegex_=/^\r?\n\r?\n/,t.prototype.diff_cleanupEfficiency=function(e){for(var r=!1,o=[],i=0,a=null,l=0,s=!1,u=!1,c=!1,f=!1;l0?o[i-1]:-1,c=f=!1),r=!0)),l++;r&&this.diff_cleanupMerge(e)},t.prototype.diff_cleanupMerge=function(e){e.push(new t.Diff(0,""));for(var r,o=0,i=0,a=0,l="",s="";o1?(0!==i&&0!==a&&(0!==(r=this.diff_commonPrefix(s,l))&&(o-i-a>0&&0==e[o-i-a-1][0]?e[o-i-a-1][1]+=s.substring(0,r):(e.splice(0,0,new t.Diff(0,s.substring(0,r))),o++),s=s.substring(r),l=l.substring(r)),0!==(r=this.diff_commonSuffix(s,l))&&(e[o][1]=s.substring(s.length-r)+e[o][1],s=s.substring(0,s.length-r),l=l.substring(0,l.length-r))),o-=i+a,e.splice(o,i+a),l.length&&(e.splice(o,0,new t.Diff(n,l)),o++),s.length&&(e.splice(o,0,new t.Diff(1,s)),o++),o++):0!==o&&0==e[o-1][0]?(e[o-1][1]+=e[o][1],e.splice(o,1)):o++,a=0,i=0,l="",s=""}""===e[e.length-1][1]&&e.pop();var u=!1;for(o=1;ot));r++)a=o,l=i;return e.length!=r&&e[r][0]===n?l:l+(t-a)},t.prototype.diff_prettyHtml=function(e){for(var t=[],r=/&/g,o=//g,a=/\n/g,l=0;l");switch(s){case 1:t[l]=''+u+"";break;case n:t[l]=''+u+"";break;case 0:t[l]=""+u+""}}return t.join("")},t.prototype.diff_text1=function(e){for(var t=[],n=0;nthis.Match_MaxBits)throw new Error("Pattern too long for this browser.");var r=this.match_alphabet_(t),o=this;function i(e,r){var i=e/t.length,a=Math.abs(n-r);return o.Match_Distance?i+a/o.Match_Distance:a?1:i}var a=this.Match_Threshold,l=e.indexOf(t,n);-1!=l&&(a=Math.min(i(0,l),a),-1!=(l=e.lastIndexOf(t,n+t.length))&&(a=Math.min(i(0,l),a)));var s,u,c=1<=h;v--){var y=r[e.charAt(v-1)];if(m[v]=0===p?(m[v+1]<<1|1)&y:(m[v+1]<<1|1)&y|(f[v+1]|f[v])<<1|1|f[v+1],m[v]&c){var b=i(p,v-1);if(b<=a){if(a=b,!((l=v-1)>n))break;h=Math.max(1,2*n-l)}}}if(i(p+1,n)>a)break;f=m}return l},t.prototype.match_alphabet_=function(e){for(var t={},n=0;n2&&(this.diff_cleanupSemantic(a),this.diff_cleanupEfficiency(a));else if(e&&"object"==typeof e&&void 0===r&&void 0===o)a=e,i=this.diff_text1(a);else if("string"==typeof e&&r&&"object"==typeof r&&void 0===o)i=e,a=r;else{if("string"!=typeof e||"string"!=typeof r||!o||"object"!=typeof o)throw new Error("Unknown call format to patch_make.");i=e,a=o}if(0===a.length)return[];for(var l=[],s=new t.patch_obj,u=0,c=0,f=0,d=i,p=i,h=0;h=2*this.Patch_Margin&&u&&(this.patch_addContext_(s,d),l.push(s),s=new t.patch_obj,u=0,d=p,c=f)}1!==g&&(c+=m.length),g!==n&&(f+=m.length)}return u&&(this.patch_addContext_(s,d),l.push(s)),l},t.prototype.patch_deepCopy=function(e){for(var n=[],r=0;rthis.Match_MaxBits?-1!=(l=this.match_main(t,c.substring(0,this.Match_MaxBits),u))&&(-1==(f=this.match_main(t,c.substring(c.length-this.Match_MaxBits),u+c.length-this.Match_MaxBits))||l>=f)&&(l=-1):l=this.match_main(t,c,u),-1==l)i[a]=!1,o-=e[a].length2-e[a].length1;else if(i[a]=!0,o=l-u,c==(s=-1==f?t.substring(l,l+c.length):t.substring(l,f+this.Match_MaxBits)))t=t.substring(0,l)+this.diff_text2(e[a].diffs)+t.substring(l+c.length);else{var d=this.diff_main(c,s,!1);if(c.length>this.Match_MaxBits&&this.diff_levenshtein(d)/c.length>this.Patch_DeleteThreshold)i[a]=!1;else{this.diff_cleanupSemanticLossless(d);for(var p,h=0,g=0;ga[0][1].length){var l=n-a[0][1].length;a[0][1]=r.substring(a[0][1].length)+a[0][1],i.start1-=l,i.start2-=l,i.length1+=l,i.length2+=l}return 0==(a=(i=e[e.length-1]).diffs).length||0!=a[a.length-1][0]?(a.push(new t.Diff(0,r)),i.length1+=n,i.length2+=n):n>a[a.length-1][1].length&&(l=n-a[a.length-1][1].length,a[a.length-1][1]+=r.substring(0,l),i.length1+=l,i.length2+=l),r},t.prototype.patch_splitMax=function(e){for(var r=this.Match_MaxBits,o=0;o2*r?(u.length1+=d.length,a+=d.length,c=!1,u.diffs.push(new t.Diff(f,d)),i.diffs.shift()):(d=d.substring(0,r-u.length1-this.Patch_Margin),u.length1+=d.length,a+=d.length,0===f?(u.length2+=d.length,l+=d.length):c=!1,u.diffs.push(new t.Diff(f,d)),d==i.diffs[0][1]?i.diffs.shift():i.diffs[0][1]=i.diffs[0][1].substring(d.length))}s=(s=this.diff_text2(u.diffs)).substring(s.length-this.Patch_Margin);var p=this.diff_text1(i.diffs).substring(0,this.Patch_Margin);""!==p&&(u.length1+=p.length,u.length2+=p.length,0!==u.diffs.length&&0===u.diffs[u.diffs.length-1][0]?u.diffs[u.diffs.length-1][1]+=p:u.diffs.push(new t.Diff(0,p))),c||e.splice(++o,0,u)}}},t.prototype.patch_toText=function(e){for(var t=[],n=0;n{"use strict";e.exports=function(e){var t=e.uri,n=e.name,r=e.type;this.uri=t,this.name=n,this.type=r}},2929:(e,t,n)=>{"use strict";var r=n(1278);e.exports=function e(t,n,o){var i;void 0===n&&(n=""),void 0===o&&(o=r);var a=new Map;function l(e,t){var n=a.get(t);n?n.push.apply(n,e):a.set(t,e)}if(o(t))i=null,l([n],t);else{var s=n?n+".":"";if("undefined"!=typeof FileList&&t instanceof FileList)i=Array.prototype.map.call(t,(function(e,t){return l([""+s+t],e),null}));else if(Array.isArray(t))i=t.map((function(t,n){var r=e(t,""+s+n,o);return r.files.forEach(l),r.clone}));else if(t&&t.constructor===Object)for(var u in i={},t){var c=e(t[u],""+s+u,o);c.files.forEach(l),i[u]=c.clone}else i=t}return{clone:i,files:a}}},9384:(e,t,n)=>{"use strict";t.ReactNativeFile=n(7570),t.extractFiles=n(2929),t.isExtractableFile=n(1278)},1278:(e,t,n)=>{"use strict";var r=n(7570);e.exports=function(e){return"undefined"!=typeof File&&e instanceof File||"undefined"!=typeof Blob&&e instanceof Blob||e instanceof r}},1688:e=>{e.exports="object"==typeof self?self.FormData:window.FormData},8749:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(9384),i=r(n(1688)),a=function(e){return o.isExtractableFile(e)||null!==e&&"object"==typeof e&&"function"==typeof e.pipe};t.default=function(e,t,n){var r=o.extractFiles({query:e,variables:t,operationName:n},"",a),l=r.clone,s=r.files;if(0===s.size){if(!Array.isArray(e))return JSON.stringify(l);if(void 0!==t&&!Array.isArray(t))throw new Error("Cannot create request body with given variable type, array expected");var u=e.reduce((function(e,n,r){return e.push({query:n,variables:t?t[r]:void 0}),e}),[]);return JSON.stringify(u)}var c=new("undefined"==typeof FormData?i.default:FormData);c.append("operations",JSON.stringify(l));var f={},d=0;return s.forEach((function(e){f[++d]=e})),c.append("map",JSON.stringify(f)),d=0,s.forEach((function(e,t){c.append(""+ ++d,t)})),c}},6647:function(e,t,n){"use strict";var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=e.prototype.toJSON;"function"==typeof t||(0,r.default)(0),e.prototype.inspect=t,o.default&&(e.prototype[o.default]=t)};var r=i(n(5006)),o=i(n(8019));function i(e){return e&&e.__esModule?e:{default:e}}},8048:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return a(e,[])};var r,o=(r=n(8019))&&r.__esModule?r:{default:r};function i(e){return i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},i(e)}function a(e,t){switch(i(e)){case"string":return JSON.stringify(e);case"function":return e.name?"[function ".concat(e.name,"]"):"[function]";case"object":return null===e?"null":function(e,t){if(-1!==t.indexOf(e))return"[Circular]";var n=[].concat(t,[e]),r=function(e){var t=e[String(o.default)];return"function"==typeof t?t:"function"==typeof e.inspect?e.inspect:void 0}(e);if(void 0!==r){var i=r.call(e);if(i!==e)return"string"==typeof i?i:a(i,n)}else if(Array.isArray(e))return function(e,t){if(0===e.length)return"[]";if(t.length>2)return"[Array]";for(var n=Math.min(10,e.length),r=e.length-n,o=[],i=0;i1&&o.push("... ".concat(r," more items")),"["+o.join(", ")+"]"}(e,n);return function(e,t){var n=Object.keys(e);return 0===n.length?"{}":t.length>2?"["+function(e){var t=Object.prototype.toString.call(e).replace(/^\[object /,"").replace(/]$/,"");if("Object"===t&&"function"==typeof e.constructor){var n=e.constructor.name;if("string"==typeof n&&""!==n)return n}return t}(e)+"]":"{ "+n.map((function(n){return n+": "+a(e[n],t)})).join(", ")+" }"}(e,n)}(e,t);default:return String(e)}}},5006:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){if(!Boolean(e))throw new Error(null!=t?t:"Unexpected invariant triggered.")}},8019:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):void 0;t.default=n},4560:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isNode=function(e){return null!=e&&"string"==typeof e.kind},t.Token=t.Location=void 0;var r,o=(r=n(2678))&&r.__esModule?r:{default:r},i=function(){function e(e,t,n){this.start=e.start,this.end=t.end,this.startToken=e,this.endToken=t,this.source=n}return e.prototype.toJSON=function(){return{start:this.start,end:this.end}},e}();t.Location=i,(0,o.default)(i);var a=function(){function e(e,t,n,r,o,i,a){this.kind=e,this.start=t,this.end=n,this.line=r,this.column=o,this.value=a,this.prev=i,this.next=null}return e.prototype.toJSON=function(){return{kind:this.kind,value:this.value,line:this.line,column:this.column}},e}();t.Token=a,(0,o.default)(a)},9501:(e,t)=>{"use strict";function n(e){for(var t=0;ta&&n(t[l-1]);)--l;return t.slice(a,l).join("\n")},t.getBlockStringIndentation=r,t.printBlockString=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=-1===e.indexOf("\n"),o=" "===e[0]||"\t"===e[0],i='"'===e[e.length-1],a="\\"===e[e.length-1],l=!r||i||a||n,s="";return!l||r&&o||(s+="\n"+t),s+=t?e.replace(/\n/g,"\n"+t):e,l&&(s+="\n"),'"""'+s.replace(/"""/g,'\\"""')+'"""'}},3083:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.print=function(e){return(0,r.visit)(e,{leave:i})};var r=n(2624),o=n(9501),i={Name:function(e){return e.value},Variable:function(e){return"$"+e.name},Document:function(e){return l(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=u("(",l(e.variableDefinitions,", "),")"),o=l(e.directives," "),i=e.selectionSet;return n||o||r||"query"!==t?l([t,l([n,r]),o,i]," "):i},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,o=e.directives;return t+": "+n+u(" = ",r)+u(" ",l(o," "))},SelectionSet:function(e){return s(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,o=e.directives,i=e.selectionSet,a=u("",t,": ")+n,s=a+u("(",l(r,", "),")");return s.length>80&&(s=a+u("(\n",c(l(r,"\n")),"\n)")),l([s,l(o," "),i]," ")},Argument:function(e){return e.name+": "+e.value},FragmentSpread:function(e){return"..."+e.name+u(" ",l(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return l(["...",u("on ",t),l(n," "),r]," ")},FragmentDefinition:function(e){var t=e.name,n=e.typeCondition,r=e.variableDefinitions,o=e.directives,i=e.selectionSet;return"fragment ".concat(t).concat(u("(",l(r,", "),")")," ")+"on ".concat(n," ").concat(u("",l(o," ")," "))+i},IntValue:function(e){return e.value},FloatValue:function(e){return e.value},StringValue:function(e,t){var n=e.value;return e.block?(0,o.printBlockString)(n,"description"===t?"":" "):JSON.stringify(n)},BooleanValue:function(e){return e.value?"true":"false"},NullValue:function(){return"null"},EnumValue:function(e){return e.value},ListValue:function(e){return"["+l(e.values,", ")+"]"},ObjectValue:function(e){return"{"+l(e.fields,", ")+"}"},ObjectField:function(e){return e.name+": "+e.value},Directive:function(e){return"@"+e.name+u("(",l(e.arguments,", "),")")},NamedType:function(e){return e.name},ListType:function(e){return"["+e.type+"]"},NonNullType:function(e){return e.type+"!"},SchemaDefinition:a((function(e){var t=e.directives,n=e.operationTypes;return l(["schema",l(t," "),s(n)]," ")})),OperationTypeDefinition:function(e){return e.operation+": "+e.type},ScalarTypeDefinition:a((function(e){return l(["scalar",e.name,l(e.directives," ")]," ")})),ObjectTypeDefinition:a((function(e){var t=e.name,n=e.interfaces,r=e.directives,o=e.fields;return l(["type",t,u("implements ",l(n," & ")),l(r," "),s(o)]," ")})),FieldDefinition:a((function(e){var t=e.name,n=e.arguments,r=e.type,o=e.directives;return t+(d(n)?u("(\n",c(l(n,"\n")),"\n)"):u("(",l(n,", "),")"))+": "+r+u(" ",l(o," "))})),InputValueDefinition:a((function(e){var t=e.name,n=e.type,r=e.defaultValue,o=e.directives;return l([t+": "+n,u("= ",r),l(o," ")]," ")})),InterfaceTypeDefinition:a((function(e){var t=e.name,n=e.interfaces,r=e.directives,o=e.fields;return l(["interface",t,u("implements ",l(n," & ")),l(r," "),s(o)]," ")})),UnionTypeDefinition:a((function(e){var t=e.name,n=e.directives,r=e.types;return l(["union",t,l(n," "),r&&0!==r.length?"= "+l(r," | "):""]," ")})),EnumTypeDefinition:a((function(e){var t=e.name,n=e.directives,r=e.values;return l(["enum",t,l(n," "),s(r)]," ")})),EnumValueDefinition:a((function(e){return l([e.name,l(e.directives," ")]," ")})),InputObjectTypeDefinition:a((function(e){var t=e.name,n=e.directives,r=e.fields;return l(["input",t,l(n," "),s(r)]," ")})),DirectiveDefinition:a((function(e){var t=e.name,n=e.arguments,r=e.repeatable,o=e.locations;return"directive @"+t+(d(n)?u("(\n",c(l(n,"\n")),"\n)"):u("(",l(n,", "),")"))+(r?" repeatable":"")+" on "+l(o," | ")})),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return l(["extend schema",l(t," "),s(n)]," ")},ScalarTypeExtension:function(e){return l(["extend scalar",e.name,l(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,o=e.fields;return l(["extend type",t,u("implements ",l(n," & ")),l(r," "),s(o)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,o=e.fields;return l(["extend interface",t,u("implements ",l(n," & ")),l(r," "),s(o)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return l(["extend union",t,l(n," "),r&&0!==r.length?"= "+l(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return l(["extend enum",t,l(n," "),s(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return l(["extend input",t,l(n," "),s(r)]," ")}};function a(e){return function(t){return l([t.description,e(t)],"\n")}}function l(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return null!==(t=null==e?void 0:e.filter((function(e){return e})).join(n))&&void 0!==t?t:""}function s(e){return u("{\n",c(l(e,"\n")),"\n}")}function u(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return null!=t&&""!==t?e+t+n:""}function c(e){return u(" ",e.replace(/\n/g,"\n "))}function f(e){return-1!==e.indexOf("\n")}function d(e){return null!=e&&e.some(f)}},2624:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.visit=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:a,r=void 0,u=Array.isArray(e),c=[e],f=-1,d=[],p=void 0,h=void 0,g=void 0,m=[],v=[],y=e;do{var b=++f===c.length,w=b&&0!==d.length;if(b){if(h=0===v.length?void 0:m[m.length-1],p=g,g=v.pop(),w){if(u)p=p.slice();else{for(var x={},k=0,E=Object.keys(p);k{var r=n(7772).Symbol;e.exports=r},3366:(e,t,n)=>{var r=n(857),o=n(2107),i=n(7157),a=r?r.toStringTag:void 0;e.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":a&&a in Object(e)?o(e):i(e)}},1704:(e,t,n)=>{var r=n(2153),o=/^\s+/;e.exports=function(e){return e?e.slice(0,r(e)+1).replace(o,""):e}},1242:(e,t,n)=>{var r="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g;e.exports=r},2107:(e,t,n)=>{var r=n(857),o=Object.prototype,i=o.hasOwnProperty,a=o.toString,l=r?r.toStringTag:void 0;e.exports=function(e){var t=i.call(e,l),n=e[l];try{e[l]=void 0;var r=!0}catch(e){}var o=a.call(e);return r&&(t?e[l]=n:delete e[l]),o}},7157:e=>{var t=Object.prototype.toString;e.exports=function(e){return t.call(e)}},7772:(e,t,n)=>{var r=n(1242),o="object"==typeof self&&self&&self.Object===Object&&self,i=r||o||Function("return this")();e.exports=i},2153:e=>{var t=/\s/;e.exports=function(e){for(var n=e.length;n--&&t.test(e.charAt(n)););return n}},4073:(e,t,n)=>{var r=n(9259),o=n(1100),i=n(7642),a=Math.max,l=Math.min;e.exports=function(e,t,n){var s,u,c,f,d,p,h=0,g=!1,m=!1,v=!0;if("function"!=typeof e)throw new TypeError("Expected a function");function y(t){var n=s,r=u;return s=u=void 0,h=t,f=e.apply(r,n)}function b(e){return h=e,d=setTimeout(x,t),g?y(e):f}function w(e){var n=e-p;return void 0===p||n>=t||n<0||m&&e-h>=c}function x(){var e=o();if(w(e))return k(e);d=setTimeout(x,function(e){var n=t-(e-p);return m?l(n,c-(e-h)):n}(e))}function k(e){return d=void 0,v&&s?y(e):(s=u=void 0,f)}function E(){var e=o(),n=w(e);if(s=arguments,u=this,p=e,n){if(void 0===d)return b(p);if(m)return clearTimeout(d),d=setTimeout(x,t),y(p)}return void 0===d&&(d=setTimeout(x,t)),f}return t=i(t)||0,r(n)&&(g=!!n.leading,c=(m="maxWait"in n)?a(i(n.maxWait)||0,t):c,v="trailing"in n?!!n.trailing:v),E.cancel=function(){void 0!==d&&clearTimeout(d),h=0,s=p=u=d=void 0},E.flush=function(){return void 0===d?f:k(o())},E}},9259:e=>{e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},5125:e=>{e.exports=function(e){return null!=e&&"object"==typeof e}},4795:(e,t,n)=>{var r=n(3366),o=n(5125);e.exports=function(e){return"symbol"==typeof e||o(e)&&"[object Symbol]"==r(e)}},1100:(e,t,n)=>{var r=n(7772);e.exports=function(){return r.Date.now()}},7642:(e,t,n)=>{var r=n(1704),o=n(9259),i=n(4795),a=/^[-+]0x[0-9a-f]+$/i,l=/^0b[01]+$/i,s=/^0o[0-7]+$/i,u=parseInt;e.exports=function(e){if("number"==typeof e)return e;if(i(e))return NaN;if(o(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=o(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=r(e);var n=l.test(e);return n||s.test(e)?u(e.slice(2),n?2:8):a.test(e)?NaN:+e}},9410:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){let e=null;return{mountedInstances:new Set,updateHead:t=>{const n=e=Promise.resolve().then((()=>{if(n!==e)return;e=null;const i={};t.forEach((e=>{if("link"===e.type&&e.props["data-optimized-fonts"]){if(document.querySelector(`style[data-href="${e.props["data-href"]}"]`))return;e.props.href=e.props["data-href"],e.props["data-href"]=void 0}const t=i[e.type]||[];t.push(e),i[e.type]=t}));const a=i.title?i.title[0]:null;let l="";if(a){const{children:e}=a.props;l="string"==typeof e?e:Array.isArray(e)?e.join(""):""}l!==document.title&&(document.title=l),["meta","base","link","style","script"].forEach((e=>{!function(e,t){const n=document.getElementsByTagName("head")[0],i=n.querySelector("meta[name=next-head-count]"),a=Number(i.content),l=[];for(let t=0,n=i.previousElementSibling;t{for(let t=0,n=l.length;t{var t;return null===(t=e.parentNode)||void 0===t?void 0:t.removeChild(e)})),u.forEach((e=>n.insertBefore(e,i))),i.content=(a-l.length+u.length).toString()}(e,i[e]||[])}))}))}}},t.isEqualNode=o,t.DOMAttributeNames=void 0;const n={acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv",noModule:"noModule"};function r({type:e,props:t}){const r=document.createElement(e);for(const o in t){if(!t.hasOwnProperty(o))continue;if("children"===o||"dangerouslySetInnerHTML"===o)continue;if(void 0===t[o])continue;const i=n[o]||o.toLowerCase();"script"!==e||"async"!==i&&"defer"!==i&&"noModule"!==i?r.setAttribute(i,t[o]):r[i]=!!t[o]}const{children:o,dangerouslySetInnerHTML:i}=t;return i?r.innerHTML=i.__html||"":o&&(r.textContent="string"==typeof o?o:Array.isArray(o)?o.join(""):""),r}function o(e,t){if(e instanceof HTMLElement&&t instanceof HTMLElement){const n=t.getAttribute("nonce");if(n&&!e.getAttribute("nonce")){const r=t.cloneNode(!0);return r.setAttribute("nonce",""),r.nonce=n,n===e.nonce&&e.isEqualNode(r)}}return e.isEqualNode(t)}t.DOMAttributeNames=n,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},104:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var{src:t,sizes:n,unoptimized:r=!1,priority:o=!1,loading:c,lazyRoot:f=null,lazyBoundary:v="200px",className:k,quality:S,width:_,height:O,style:C,objectFit:P,objectPosition:j,onLoadingComplete:L,placeholder:T="empty",blurDataURL:R}=e,A=p(e,["src","sizes","unoptimized","priority","loading","lazyRoot","lazyBoundary","className","quality","width","height","style","objectFit","objectPosition","onLoadingComplete","placeholder","blurDataURL"]);const M=i.useContext(u.ImageConfigContext),I=i.useMemo((()=>{const e=h||M||l.imageConfigDefault,t=[...e.deviceSizes,...e.imageSizes].sort(((e,t)=>e-t)),n=e.deviceSizes.sort(((e,t)=>e-t));return d({},e,{allSizes:t,deviceSizes:n})}),[M]);let D=A,N=n?"responsive":"intrinsic";"layout"in D&&(D.layout&&(N=D.layout),delete D.layout);let z=x;if("loader"in D){if(D.loader){const e=D.loader;z=t=>{const{config:n}=t,r=p(t,["config"]);return e(r)}}delete D.loader}let F="";if(function(e){return"object"==typeof e&&(y(e)||function(e){return void 0!==e.src}(e))}(t)){const e=y(t)?t.default:t;if(!e.src)throw new Error(`An object should only be passed to the image component src parameter if it comes from a static image import. It must include src. Received ${JSON.stringify(e)}`);if(R=R||e.blurDataURL,F=e.src,!(N&&"fill"===N||(O=O||e.height,_=_||e.width,e.height&&e.width)))throw new Error(`An object should only be passed to the image component src parameter if it comes from a static image import. It must include height and width. Received ${JSON.stringify(e)}`)}t="string"==typeof t?t:F;const $=w(_),B=w(O),W=w(S);let H=!o&&("lazy"===c||void 0===c);(t.startsWith("data:")||t.startsWith("blob:"))&&(r=!0,H=!1),"undefined"!=typeof window&&g.has(t)&&(H=!1);const[U,V]=i.useState(!1),[q,X,G]=s.useIntersection({rootRef:f,rootMargin:v,disabled:!H}),Y=!H||X,Q={boxSizing:"border-box",display:"block",overflow:"hidden",width:"initial",height:"initial",background:"none",opacity:1,border:0,margin:0,padding:0},K={boxSizing:"border-box",display:"block",width:"initial",height:"initial",background:"none",opacity:1,border:0,margin:0,padding:0};let Z,J=!1;const ee={position:"absolute",top:0,left:0,bottom:0,right:0,boxSizing:"border-box",padding:0,border:"none",margin:"auto",display:"block",width:0,height:0,minWidth:"100%",maxWidth:"100%",minHeight:"100%",maxHeight:"100%",objectFit:P,objectPosition:j},te=Object.assign({},C,"raw"===N?{}:ee),ne="blur"!==T||U?{}:{filter:"blur(20px)",backgroundSize:P||"cover",backgroundImage:`url("${R}")`,backgroundPosition:j||"0% 0%"};if("fill"===N)Q.display="block",Q.position="absolute",Q.top=0,Q.left=0,Q.bottom=0,Q.right=0;else if(void 0!==$&&void 0!==B){const e=B/$,t=isNaN(e)?"100%":100*e+"%";"responsive"===N?(Q.display="block",Q.position="relative",J=!0,K.paddingTop=t):"intrinsic"===N?(Q.display="inline-block",Q.position="relative",Q.maxWidth="100%",J=!0,K.maxWidth="100%",Z=`data:image/svg+xml,%3csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20version=%271.1%27%20width=%27${$}%27%20height=%27${B}%27/%3e`):"fixed"===N&&(Q.display="inline-block",Q.position="relative",Q.width=$,Q.height=B)}let re={src:m,srcSet:void 0,sizes:void 0};Y&&(re=b({config:I,src:t,unoptimized:r,layout:N,width:$,quality:W,sizes:n,loader:z}));let oe=t,ie="imagesrcset",ae="imagesizes";window.omnivoreEnv.__NEXT_REACT_ROOT&&(ie="imageSrcSet",ae="imageSizes");const le={[ie]:re.srcSet,[ae]:re.sizes},se="undefined"==typeof window?i.default.useEffect:i.default.useLayoutEffect,ue=i.useRef(L),ce=i.useRef(t);i.useEffect((()=>{ue.current=L}),[L]),se((()=>{ce.current!==t&&(G(),ce.current=t)}),[G,t]);const fe=d({isLazy:H,imgAttributes:re,heightInt:B,widthInt:$,qualityInt:W,layout:N,className:k,imgStyle:te,blurStyle:ne,loading:c,config:I,unoptimized:r,placeholder:T,loader:z,srcString:oe,onLoadingCompleteRef:ue,setBlurComplete:V,setIntersection:q,isVisible:Y},D);return i.default.createElement(i.default.Fragment,null,"raw"===N?i.default.createElement(E,Object.assign({},fe)):i.default.createElement("span",{style:Q},J?i.default.createElement("span",{style:K},Z?i.default.createElement("img",{style:{display:"block",maxWidth:"100%",width:"initial",height:"initial",background:"none",opacity:1,border:0,margin:0,padding:0},alt:"","aria-hidden":!0,src:Z}):null):null,i.default.createElement(E,Object.assign({},fe))),o?i.default.createElement(a.default,null,i.default.createElement("link",Object.assign({key:"__nimg-"+re.src+re.srcSet+re.sizes,rel:"preload",as:"image",href:re.srcSet?void 0:re.src},le))):null)};var r,o,i=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(2784)),a=(r=n(5977))&&r.__esModule?r:{default:r},l=n(8467),s=n(3321),u=n(4329),c=(n(1624),n(1119));function f(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function d(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}null===(o=window.omnivoreEnv.__NEXT_IMAGE_OPTS)||void 0===o||o.experimentalLayoutRaw;const h=window.omnivoreEnv.__NEXT_IMAGE_OPTS,g=new Set;new Map;const m="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";"undefined"==typeof window&&(n.g.__NEXT_IMAGE_IMPORTED=!0);const v=new Map([["default",function({config:e,src:t,width:n,quality:r}){return t.endsWith(".svg")&&!e.dangerouslyAllowSVG?t:`${c.normalizePathTrailingSlash(e.path)}?url=${encodeURIComponent(t)}&w=${n}&q=${r||75}`}],["imgix",function({config:e,src:t,width:n,quality:r}){const o=new URL(`${e.path}${S(t)}`),i=o.searchParams;return i.set("auto",i.get("auto")||"format"),i.set("fit",i.get("fit")||"max"),i.set("w",i.get("w")||n.toString()),r&&i.set("q",r.toString()),o.href}],["cloudinary",function({config:e,src:t,width:n,quality:r}){const o=["f_auto","c_limit","w_"+n,"q_"+(r||"auto")].join(",")+"/";return`${e.path}${o}${S(t)}`}],["akamai",function({config:e,src:t,width:n}){return`${e.path}${S(t)}?imwidth=${n}`}],["custom",function({src:e}){throw new Error(`Image with src "${e}" is missing "loader" prop.\nRead more: https://nextjs.org/docs/messages/next-image-missing-loader`)}]]);function y(e){return void 0!==e.default}function b({config:e,src:t,unoptimized:n,layout:r,width:o,quality:i,sizes:a,loader:l}){if(n)return{src:t,srcSet:void 0,sizes:void 0};const{widths:s,kind:u}=function({deviceSizes:e,allSizes:t},n,r,o){if(o&&("fill"===r||"responsive"===r||"raw"===r)){const n=/(^|\s)(1?\d?\d)vw/g,r=[];for(let e;e=n.exec(o);e)r.push(parseInt(e[2]));if(r.length){const n=.01*Math.min(...r);return{widths:t.filter((t=>t>=e[0]*n)),kind:"w"}}return{widths:t,kind:"w"}}return"number"!=typeof n||"fill"===r||"responsive"===r?{widths:e,kind:"w"}:{widths:[...new Set([n,2*n].map((e=>t.find((t=>t>=e))||t[t.length-1])))],kind:"x"}}(e,o,r,a),c=s.length-1;return{sizes:a||"w"!==u?a:"100vw",srcSet:s.map(((n,r)=>`${l({config:e,src:t,quality:i,width:n})} ${"w"===u?n:r+1}${u}`)).join(", "),src:l({config:e,src:t,quality:i,width:s[c]})}}function w(e){return"number"==typeof e?e:"string"==typeof e?parseInt(e,10):void 0}function x(e){var t;const n=(null===(t=e.config)||void 0===t?void 0:t.loader)||"default",r=v.get(n);if(r)return r(e);throw new Error(`Unknown "loader" found in "next.config.js". Expected: ${l.VALID_LOADERS.join(", ")}. Received: ${n}`)}function k(e,t,n,r,o,i){e&&e.src!==m&&e["data-loaded-src"]!==t&&(e["data-loaded-src"]=t,("decode"in e?e.decode():Promise.resolve()).catch((()=>{})).then((()=>{if(e.parentNode&&(g.add(t),"blur"===r&&i(!0),null==o?void 0:o.current)){const{naturalWidth:t,naturalHeight:n}=e;o.current({naturalWidth:t,naturalHeight:n})}})))}const E=e=>{var{imgAttributes:t,heightInt:n,widthInt:r,qualityInt:o,layout:a,className:l,imgStyle:s,blurStyle:u,isLazy:c,placeholder:f,loading:h,srcString:g,config:m,unoptimized:v,loader:y,onLoadingCompleteRef:w,setBlurComplete:x,setIntersection:E,onLoad:S,onError:_,isVisible:O}=e,C=p(e,["imgAttributes","heightInt","widthInt","qualityInt","layout","className","imgStyle","blurStyle","isLazy","placeholder","loading","srcString","config","unoptimized","loader","onLoadingCompleteRef","setBlurComplete","setIntersection","onLoad","onError","isVisible"]);return i.default.createElement(i.default.Fragment,null,i.default.createElement("img",Object.assign({},C,t,"raw"===a?{height:n,width:r}:{},{decoding:"async","data-nimg":a,className:l,style:d({},s,u),ref:i.useCallback((e=>{E(e),(null==e?void 0:e.complete)&&k(e,g,0,f,w,x)}),[E,g,a,f,w,x]),onLoad:e=>{k(e.currentTarget,g,0,f,w,x),S&&S(e)},onError:e=>{"blur"===f&&x(!0),_&&_(e)}})),(c||"blur"===f)&&i.default.createElement("noscript",null,i.default.createElement("img",Object.assign({},C,b({config:m,src:g,unoptimized:v,layout:a,width:r,quality:o,sizes:t.sizes,loader:y}),"raw"===a?{height:n,width:r}:{},{decoding:"async","data-nimg":a,style:s,className:l,loading:h||"lazy"}))))};function S(e){return"/"===e[0]?e.slice(1):e}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},4529:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,o=(r=n(2784))&&r.__esModule?r:{default:r},i=n(6640),a=n(9518),l=n(3321);const s={};function u(e,t,n,r){if("undefined"==typeof window||!e)return;if(!i.isLocalURL(t))return;e.prefetch(t,n,r).catch((e=>{}));const o=r&&void 0!==r.locale?r.locale:e&&e.locale;s[t+"%"+n+(o?"%"+o:"")]=!0}var c=o.default.forwardRef(((e,t)=>{const{legacyBehavior:n=!0!==Boolean(window.omnivoreEnv.__NEXT_NEW_LINK_BEHAVIOR)}=e;let r;const{href:c,as:f,children:d,prefetch:p,passHref:h,replace:g,shallow:m,scroll:v,locale:y,onClick:b,onMouseEnter:w}=e,x=function(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}(e,["href","as","children","prefetch","passHref","replace","shallow","scroll","locale","onClick","onMouseEnter"]);r=d,n&&"string"==typeof r&&(r=o.default.createElement("a",null,r));const k=!1!==p,E=a.useRouter(),{href:S,as:_}=o.default.useMemo((()=>{const[e,t]=i.resolveHref(E,c,!0);return{href:e,as:f?i.resolveHref(E,f):t||e}}),[E,c,f]),O=o.default.useRef(S),C=o.default.useRef(_);let P;n&&(P=o.default.Children.only(r));const j=n?P&&"object"==typeof P&&P.ref:t,[L,T,R]=l.useIntersection({rootMargin:"200px"}),A=o.default.useCallback((e=>{C.current===_&&O.current===S||(R(),C.current=_,O.current=S),L(e),j&&("function"==typeof j?j(e):"object"==typeof j&&(j.current=e))}),[_,j,S,R,L]);o.default.useEffect((()=>{const e=T&&k&&i.isLocalURL(S),t=void 0!==y?y:E&&E.locale,n=s[S+"%"+_+(t?"%"+t:"")];e&&!n&&u(E,S,_,{locale:t})}),[_,S,T,y,k,E]);const M={ref:A,onClick:e=>{n||"function"!=typeof b||b(e),n&&P.props&&"function"==typeof P.props.onClick&&P.props.onClick(e),e.defaultPrevented||function(e,t,n,r,o,a,l,s){const{nodeName:u}=e.currentTarget;("A"!==u.toUpperCase()||!function(e){const{target:t}=e.currentTarget;return t&&"_self"!==t||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||e.nativeEvent&&2===e.nativeEvent.which}(e)&&i.isLocalURL(n))&&(e.preventDefault(),t[o?"replace":"push"](n,r,{shallow:a,locale:s,scroll:l}))}(e,E,S,_,g,m,v,y)},onMouseEnter:e=>{n||"function"!=typeof w||w(e),n&&P.props&&"function"==typeof P.props.onMouseEnter&&P.props.onMouseEnter(e),i.isLocalURL(S)&&u(E,S,_,{priority:!0})}};if(!n||h||"a"===P.type&&!("href"in P.props)){const e=void 0!==y?y:E&&E.locale,t=E&&E.isLocaleDomain&&i.getDomainLocale(_,e,E&&E.locales,E&&E.domainLocales);M.href=t||i.addBasePath(i.addLocale(_,e,E&&E.defaultLocale))}return n?o.default.cloneElement(P,M):o.default.createElement("a",Object.assign({},x,M),r)}));t.default=c,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},1119:(e,t)=>{"use strict";function n(e){return e.endsWith("/")&&"/"!==e?e.slice(0,-1):e}Object.defineProperty(t,"__esModule",{value:!0}),t.removePathTrailingSlash=n,t.normalizePathTrailingSlash=void 0;const r=window.omnivoreEnv.__NEXT_TRAILING_SLASH?e=>/\.[^/]+\/?$/.test(e)?n(e):e.endsWith("/")?e:e+"/":n;t.normalizePathTrailingSlash=r,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},1976:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.cancelIdleCallback=t.requestIdleCallback=void 0;const n="undefined"!=typeof self&&self.requestIdleCallback&&self.requestIdleCallback.bind(window)||function(e){let t=Date.now();return setTimeout((function(){e({didTimeout:!1,timeRemaining:function(){return Math.max(0,50-(Date.now()-t))}})}),1)};t.requestIdleCallback=n;const r="undefined"!=typeof self&&self.cancelIdleCallback&&self.cancelIdleCallback.bind(window)||function(e){return clearTimeout(e)};t.cancelIdleCallback=r,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},7928:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.markAssetError=u,t.isAssetError=function(e){return e&&s in e},t.getClientBuildManifest=f,t.getMiddlewareManifest=function(){return self.__MIDDLEWARE_MANIFEST?Promise.resolve(self.__MIDDLEWARE_MANIFEST):c(new Promise((e=>{const t=self.__MIDDLEWARE_MANIFEST_CB;self.__MIDDLEWARE_MANIFEST_CB=()=>{e(self.__MIDDLEWARE_MANIFEST),t&&t()}})),i,u(new Error("Failed to load client middleware manifest")))},t.createRouteLoader=function(e){const t=new Map,n=new Map,r=new Map,s=new Map;function f(e){{let t=n.get(e);return t||(document.querySelector(`script[src^="${e}"]`)?Promise.resolve():(n.set(e,t=function(e,t){return new Promise(((n,r)=>{(t=document.createElement("script")).onload=n,t.onerror=()=>r(u(new Error(`Failed to load script: ${e}`))),t.crossOrigin=window.omnivoreEnv.__NEXT_CROSS_ORIGIN,t.src=e,document.body.appendChild(t)}))}(e)),t))}}function p(e){let t=r.get(e);return t||(r.set(e,t=fetch(e).then((t=>{if(!t.ok)throw new Error(`Failed to load stylesheet: ${e}`);return t.text().then((t=>({href:e,content:t})))})).catch((e=>{throw u(e)}))),t)}return{whenEntrypoint:e=>a(e,t),onEntrypoint(e,n){(n?Promise.resolve().then((()=>n())).then((e=>({component:e&&e.default||e,exports:e})),(e=>({error:e}))):Promise.resolve(void 0)).then((n=>{const r=t.get(e);r&&"resolve"in r?n&&(t.set(e,n),r.resolve(n)):(n?t.set(e,n):t.delete(e),s.delete(e))}))},loadRoute(n,r){return a(n,s,(()=>c(d(e,n).then((({scripts:e,css:r})=>Promise.all([t.has(n)?[]:Promise.all(e.map(f)),Promise.all(r.map(p))]))).then((e=>this.whenEntrypoint(n).then((t=>({entrypoint:t,styles:e[1]}))))),i,u(new Error(`Route did not complete loading: ${n}`))).then((({entrypoint:e,styles:t})=>{const n=Object.assign({styles:t},e);return"error"in e?e:n})).catch((e=>{if(r)throw e;return{error:e}})).finally((()=>{}))))},prefetch(t){let n;return(n=navigator.connection)&&(n.saveData||/2g/.test(n.effectiveType))?Promise.resolve():d(e,t).then((e=>Promise.all(l?e.scripts.map((e=>{return t=e,n="script",new Promise(((e,o)=>{const i=`\n link[rel="prefetch"][href^="${t}"],\n link[rel="preload"][href^="${t}"],\n script[src^="${t}"]`;if(document.querySelector(i))return e();(r=document.createElement("link")).as=n,r.rel="prefetch",r.crossOrigin=window.omnivoreEnv.__NEXT_CROSS_ORIGIN,r.onload=e,r.onerror=o,r.href=t,document.head.appendChild(r)}));var t,n,r})):[]))).then((()=>{o.requestIdleCallback((()=>this.loadRoute(t,!0).catch((()=>{}))))})).catch((()=>{}))}}},(r=n(9983))&&r.__esModule;var r,o=n(1976);const i=3800;function a(e,t,n){let r,o=t.get(e);if(o)return"future"in o?o.future:Promise.resolve(o);const i=new Promise((e=>{r=e}));return t.set(e,o={resolve:r,future:i}),n?n().then((e=>(r(e),e))).catch((n=>{throw t.delete(e),n})):i}const l=function(e){try{return e=document.createElement("link"),!!window.MSInputMethodContext&&!!document.documentMode||e.relList.supports("prefetch")}catch(e){return!1}}(),s=Symbol("ASSET_LOAD_ERROR");function u(e){return Object.defineProperty(e,s,{})}function c(e,t,n){return new Promise(((r,i)=>{let a=!1;e.then((e=>{a=!0,r(e)})).catch(i),o.requestIdleCallback((()=>setTimeout((()=>{a||i(n)}),t)))}))}function f(){return self.__BUILD_MANIFEST?Promise.resolve(self.__BUILD_MANIFEST):c(new Promise((e=>{const t=self.__BUILD_MANIFEST_CB;self.__BUILD_MANIFEST_CB=()=>{e(self.__BUILD_MANIFEST),t&&t()}})),i,u(new Error("Failed to load client build manifest")))}function d(e,t){return f().then((n=>{if(!(t in n))throw u(new Error(`Failed to lookup route: ${t}`));const r=n[t].map((t=>e+"/_next/"+encodeURI(t)));return{scripts:r.filter((e=>e.endsWith(".js"))),css:r.filter((e=>e.endsWith(".css")))}}))}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},9518:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"Router",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(t,"withRouter",{enumerable:!0,get:function(){return l.default}}),t.useRouter=function(){return r.default.useContext(i.RouterContext)},t.createRouter=function(...e){return u.router=new o.default(...e),u.readyCallbacks.forEach((e=>e())),u.readyCallbacks=[],u.router},t.makePublicRouterInstance=function(e){const t=e,n={};for(const e of c)"object"!=typeof t[e]?n[e]=t[e]:n[e]=Object.assign(Array.isArray(t[e])?[]:{},t[e]);return n.events=o.default.events,f.forEach((e=>{n[e]=(...n)=>t[e](...n)})),n},t.default=void 0;var r=s(n(2784)),o=s(n(6640)),i=n(6510),a=s(n(274)),l=s(n(9564));function s(e){return e&&e.__esModule?e:{default:e}}const u={router:null,readyCallbacks:[],ready(e){if(this.router)return e();"undefined"!=typeof window&&this.readyCallbacks.push(e)}},c=["pathname","route","query","asPath","components","isFallback","basePath","locale","locales","defaultLocale","isReady","isPreview","isLocaleDomain","domainLocales"],f=["push","replace","reload","back","prefetch","beforePopState"];function d(){if(!u.router)throw new Error('No router instance found.\nYou should only use "next/router" on the client side of your app.\n');return u.router}Object.defineProperty(u,"events",{get:()=>o.default.events}),c.forEach((e=>{Object.defineProperty(u,e,{get:()=>d()[e]})})),f.forEach((e=>{u[e]=(...t)=>d()[e](...t)})),["routeChangeStart","beforeHistoryChange","routeChangeComplete","routeChangeError","hashChangeStart","hashChangeComplete"].forEach((e=>{u.ready((()=>{o.default.events.on(e,((...t)=>{const n=`on${e.charAt(0).toUpperCase()}${e.substring(1)}`,r=u;if(r[n])try{r[n](...t)}catch(e){console.error(`Error when running the Router event: ${n}`),console.error(a.default(e)?`${e.message}\n${e.stack}`:e+"")}}))}))}));var p=u;t.default=p,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},9515:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.handleClientScriptLoad=p,t.initScriptLoader=function(e){e.forEach(p),[...document.querySelectorAll('[data-nscript="beforeInteractive"]'),...document.querySelectorAll('[data-nscript="beforePageRender"]')].forEach((e=>{const t=e.id||e.getAttribute("src");c.add(t)}))},t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(2784)),o=n(7177),i=n(9410),a=n(1976);function l(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function s(e){for(var t=1;t{const{src:t,id:n,onLoad:r=(()=>{}),dangerouslySetInnerHTML:o,children:a="",strategy:l="afterInteractive",onError:s}=e,d=n||t;if(d&&c.has(d))return;if(u.has(t))return c.add(d),void u.get(t).then(r,s);const p=document.createElement("script"),h=new Promise(((e,t)=>{p.addEventListener("load",(function(t){e(),r&&r.call(this,t)})),p.addEventListener("error",(function(e){t(e)}))})).catch((function(e){s&&s(e)}));t&&u.set(t,h),c.add(d),o?p.innerHTML=o.__html||"":a?p.textContent="string"==typeof a?a:Array.isArray(a)?a.join(""):"":t&&(p.src=t);for(const[t,n]of Object.entries(e)){if(void 0===n||f.includes(t))continue;const e=i.DOMAttributeNames[t]||t.toLowerCase();p.setAttribute(e,n)}"worker"===l&&p.setAttribute("type","text/partytown"),p.setAttribute("data-nscript",l),document.body.appendChild(p)};function p(e){const{strategy:t="afterInteractive"}=e;"lazyOnload"===t?window.addEventListener("load",(()=>{a.requestIdleCallback((()=>d(e)))})):d(e)}t.default=function(e){const{src:t="",onLoad:n=(()=>{}),strategy:i="afterInteractive",onError:l}=e,u=function(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}(e,["src","onLoad","strategy","onError"]),{updateScripts:f,scripts:p,getIsSsr:h}=r.useContext(o.HeadManagerContext);return r.useEffect((()=>{"afterInteractive"===i?d(e):"lazyOnload"===i&&function(e){"complete"===document.readyState?a.requestIdleCallback((()=>d(e))):window.addEventListener("load",(()=>{a.requestIdleCallback((()=>d(e)))}))}(e)}),[e,i]),"beforeInteractive"!==i&&"worker"!==i||(f?(p[i]=(p[i]||[]).concat([s({src:t,onLoad:n,onError:l},u)]),f(p)):h&&h()?c.add(u.id||t):h&&!h()&&d(e)),null},("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},3321:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.useIntersection=function({rootRef:e,rootMargin:t,disabled:n}){const s=n||!i,u=r.useRef(),[c,f]=r.useState(!1),[d,p]=r.useState(e?e.current:null),h=r.useCallback((e=>{u.current&&(u.current(),u.current=void 0),s||c||e&&e.tagName&&(u.current=function(e,t,n){const{id:r,observer:o,elements:i}=function(e){const t={root:e.root||null,margin:e.rootMargin||""};let n,r=l.find((e=>e.root===t.root&&e.margin===t.margin));if(r?n=a.get(r):(n=a.get(t),l.push(t)),n)return n;const o=new Map,i=new IntersectionObserver((e=>{e.forEach((e=>{const t=o.get(e.target),n=e.isIntersecting||e.intersectionRatio>0;t&&n&&t(n)}))}),e);return a.set(t,n={id:t,observer:i,elements:o}),n}(n);return i.set(e,(e=>e&&f(e))),o.observe(e),function(){if(i.delete(e),o.unobserve(e),0===i.size){o.disconnect(),a.delete(r);let e=l.findIndex((e=>e.root===r.root&&e.margin===r.margin));e>-1&&l.splice(e,1)}}}(e,0,{root:d,rootMargin:t}))}),[s,d,t,c]),g=r.useCallback((()=>{f(!1)}),[]);return r.useEffect((()=>{if(!i&&!c){const e=o.requestIdleCallback((()=>f(!0)));return()=>o.cancelIdleCallback(e)}}),[c]),r.useEffect((()=>{e&&p(e.current)}),[e]),[h,c,g]};var r=n(2784),o=n(1976);const i="undefined"!=typeof IntersectionObserver,a=new Map,l=[];("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},9564:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){function t(t){return o.default.createElement(e,Object.assign({router:i.useRouter()},t))}return t.getInitialProps=e.getInitialProps,t.origGetInitialProps=e.origGetInitialProps,t};var r,o=(r=n(2784))&&r.__esModule?r:{default:r},i=n(9518);("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},9264:(e,t)=>{"use strict";function n(e,t){void 0===t&&(t={});for(var n=function(e){for(var t=[],n=0;n=48&&s<=57||s>=65&&s<=90||s>=97&&s<=122||95===s))break;a+=e[l++]}if(!a)throw new TypeError("Missing parameter name at "+n);t.push({type:"NAME",index:n,value:a}),n=l}else t.push({type:"CLOSE",index:n,value:e[n++]});else t.push({type:"OPEN",index:n,value:e[n++]});else t.push({type:"ESCAPED_CHAR",index:n++,value:e[n++]});else t.push({type:"MODIFIER",index:n,value:e[n++]})}return t.push({type:"END",index:n,value:""}),t}(e),r=t.prefixes,o=void 0===r?"./":r,a="[^"+i(t.delimiter||"/#?")+"]+?",l=[],s=0,u=0,c="",f=function(e){if(u-1:void 0===k;o||(g+="(?:"+h+"(?="+p+"))?"),E||(g+="(?="+h+"|"+p+")")}return new RegExp(g,a(n))}function s(e,t,r){return e instanceof RegExp?function(e,t){if(!t)return e;var n=e.source.match(/\((?!\?)/g);if(n)for(var r=0;r{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=o,t.getProperError=function(e){return o(e)?e:new Error(r.isPlainObject(e)?JSON.stringify(e):e+"")};var r=n(9910);function o(e){return"object"==typeof e&&null!==e&&"name"in e&&"message"in e}},1719:(e,t,n)=>{"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),t.AmpStateContext=void 0;const o=((r=n(2784))&&r.__esModule?r:{default:r}).default.createContext({});t.AmpStateContext=o},9739:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isInAmpMode=a,t.useAmp=function(){return a(o.default.useContext(i.AmpStateContext))};var r,o=(r=n(2784))&&r.__esModule?r:{default:r},i=n(1719);function a({ampFirst:e=!1,hybrid:t=!1,hasQuery:n=!1}={}){return e||t&&n}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},8058:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.escapeStringRegexp=function(e){return n.test(e)?e.replace(r,"\\$&"):e};const n=/[|\\{}()[\]^$+*?.-]/,r=/[|\\{}()[\]^$+*?.-]/g},7177:(e,t,n)=>{"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),t.HeadManagerContext=void 0;const o=((r=n(2784))&&r.__esModule?r:{default:r}).default.createContext({});t.HeadManagerContext=o},5977:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.defaultHead=u,t.default=void 0;var r,o=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(2784)),i=(r=n(1889))&&r.__esModule?r:{default:r},a=n(1719),l=n(7177),s=n(9739);function u(e=!1){const t=[o.default.createElement("meta",{charSet:"utf-8"})];return e||t.push(o.default.createElement("meta",{name:"viewport",content:"width=device-width"})),t}function c(e,t){return"string"==typeof t||"number"==typeof t?e:t.type===o.default.Fragment?e.concat(o.default.Children.toArray(t.props.children).reduce(((e,t)=>"string"==typeof t||"number"==typeof t?e:e.concat(t)),[])):e.concat(t)}n(1624);const f=["name","httpEquiv","charSet","itemProp"];function d(e,t){return e.reduce(((e,t)=>{const n=o.default.Children.toArray(t.props.children);return e.concat(n)}),[]).reduce(c,[]).reverse().concat(u(t.inAmpMode)).filter(function(){const e=new Set,t=new Set,n=new Set,r={};return o=>{let i=!0,a=!1;if(o.key&&"number"!=typeof o.key&&o.key.indexOf("$")>0){a=!0;const t=o.key.slice(o.key.indexOf("$")+1);e.has(t)?i=!1:e.add(t)}switch(o.type){case"title":case"base":t.has(o.type)?i=!1:t.add(o.type);break;case"meta":for(let e=0,t=f.length;e{const r=e.key||n;if(window.omnivoreEnv.__NEXT_OPTIMIZE_FONTS&&!t.inAmpMode&&"link"===e.type&&e.props.href&&["https://fonts.googleapis.com/css","https://use.typekit.net/"].some((t=>e.props.href.startsWith(t)))){const t={...e.props||{}};return t["data-href"]=t.href,t.href=void 0,t["data-optimized-fonts"]=!0,o.default.cloneElement(e,t)}return o.default.cloneElement(e,{key:r})}))}t.default=function({children:e}){const t=o.useContext(a.AmpStateContext),n=o.useContext(l.HeadManagerContext);return o.default.createElement(i.default,{reduceComponentsToState:d,headManager:n,inAmpMode:s.isInAmpMode(t)},e)},("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},927:(e,t)=>{"use strict";t.D=function(e,t,n){let r;if(e){n&&(n=n.toLowerCase());for(const a of e){var o,i;if(t===(null===(o=a.domain)||void 0===o?void 0:o.split(":")[0].toLowerCase())||n===a.defaultLocale.toLowerCase()||(null===(i=a.locales)||void 0===i?void 0:i.some((e=>e.toLowerCase()===n)))){r=a;break}}}return r}},816:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.normalizeLocalePath=function(e,t){let n;const r=e.split("/");return(t||[]).some((t=>!(!r[1]||r[1].toLowerCase()!==t.toLowerCase()||(n=t,r.splice(1,1),e=r.join("/")||"/",0)))),{pathname:e,detectedLocale:n}}},4329:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ImageConfigContext=void 0;var r,o=(r=n(2784))&&r.__esModule?r:{default:r},i=n(8467);const a=o.default.createContext(i.imageConfigDefault);t.ImageConfigContext=a},8467:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.imageConfigDefault=t.VALID_LOADERS=void 0,t.VALID_LOADERS=["default","imgix","cloudinary","akamai","custom"];t.imageConfigDefault={deviceSizes:[640,750,828,1080,1200,1920,2048,3840],imageSizes:[16,32,48,64,96,128,256,384],path:"/_next/image",loader:"default",domains:[],disableStaticImages:!1,minimumCacheTTL:60,formats:["image/webp"],dangerouslyAllowSVG:!1,contentSecurityPolicy:"script-src 'none'; frame-src 'none'; sandbox;"}},9910:(e,t)=>{"use strict";function n(e){return Object.prototype.toString.call(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.getObjectClassLabel=n,t.isPlainObject=function(e){if("[object Object]"!==n(e))return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}},7471:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){const e=Object.create(null);return{on(t,n){(e[t]||(e[t]=[])).push(n)},off(t,n){e[t]&&e[t].splice(e[t].indexOf(n)>>>0,1)},emit(t,...n){(e[t]||[]).slice().map((e=>{e(...n)}))}}}},997:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.denormalizePagePath=function(e){let t=o.normalizePathSep(e);return t.startsWith("/index/")&&!r.isDynamicRoute(t)?t.slice(6):"/index"!==t?t:"/"};var r=n(9150),o=n(9356)},9356:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.normalizePathSep=function(e){return e.replace(/\\/g,"/")}},6510:(e,t,n)=>{"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),t.RouterContext=void 0;const o=((r=n(2784))&&r.__esModule?r:{default:r}).default.createContext(null);t.RouterContext=o},6640:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getDomainLocale=function(e,t,n,r){if(window.omnivoreEnv.__NEXT_I18N_SUPPORT){t=t||s.normalizeLocalePath(e,n).detectedLocale;const o=w(r,void 0,t);return!!o&&`http${o.http?"":"s"}://${o.domain}${x||""}${t===o.defaultLocale?"":`/${t}`}${e}`}return!1},t.addLocale=_,t.delLocale=O,t.hasBasePath=P,t.addBasePath=j,t.delBasePath=L,t.isLocalURL=T,t.interpolateAs=R,t.resolveHref=M,t.default=void 0;var r=n(1119),o=n(7928),i=n(9515),a=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(274)),l=n(997),s=n(816),u=b(n(7471)),c=n(1624),f=n(7482),d=n(1577),p=n(646),h=b(n(5317)),g=n(3107),m=n(4794),v=n(2763),y=n(6555);function b(e){return e&&e.__esModule?e:{default:e}}let w;window.omnivoreEnv.__NEXT_I18N_SUPPORT&&(w=n(927).D);const x=window.omnivoreEnv.__NEXT_ROUTER_BASEPATH||"";function k(){return Object.assign(new Error("Route Cancelled"),{cancelled:!0})}function E(e,t){if(!e.startsWith("/")||!t)return e;const n=C(e);return r.normalizePathTrailingSlash(`${t}${n}`)+e.slice(n.length)}function S(e,t){return(e=C(e))===t||e.startsWith(t+"/")}function _(e,t,n){if(window.omnivoreEnv.__NEXT_I18N_SUPPORT&&t&&t!==n){const n=C(e).toLowerCase();if(!S(n,"/"+t.toLowerCase())&&!S(n,"/api"))return E(e,"/"+t)}return e}function O(e,t){if(window.omnivoreEnv.__NEXT_I18N_SUPPORT){const n=C(e),r=n.toLowerCase(),o=t&&t.toLowerCase();return t&&(r.startsWith("/"+o+"/")||r==="/"+o)?(n.length===t.length+1?"/":"")+e.slice(t.length+1):e}return e}function C(e){const t=e.indexOf("?"),n=e.indexOf("#");return(t>-1||n>-1)&&(e=e.substring(0,t>-1?t:n)),e}function P(e){return S(e,x)}function j(e){return E(e,x)}function L(e){return(e=e.slice(x.length)).startsWith("/")||(e=`/${e}`),e}function T(e){if(e.startsWith("/")||e.startsWith("#")||e.startsWith("?"))return!0;try{const t=c.getLocationOrigin(),n=new URL(e,t);return n.origin===t&&P(n.pathname)}catch(e){return!1}}function R(e,t,n){let r="";const o=m.getRouteRegex(e),i=o.groups,a=(t!==e?g.getRouteMatcher(o)(t):"")||n;r=e;const l=Object.keys(i);return l.every((e=>{let t=a[e]||"";const{repeat:n,optional:o}=i[e];let l=`[${n?"...":""}${e}]`;return o&&(l=`${t?"":"/"}[${l}]`),n&&!Array.isArray(t)&&(t=[t]),(o||e in a)&&(r=r.replace(l,n?t.map((e=>encodeURIComponent(e))).join("/"):encodeURIComponent(t))||"/")}))||(r=""),{params:l,result:r}}function A(e,t){const n={};return Object.keys(e).forEach((r=>{t.includes(r)||(n[r]=e[r])})),n}function M(e,t,n){let o,i="string"==typeof t?t:y.formatWithValidation(t);const a=i.match(/^[a-zA-Z]{1,}:\/\//),l=a?i.slice(a[0].length):i;if((l.split("?")[0]||"").match(/(\/\/|\\)/)){console.error(`Invalid href passed to next/router: ${i}, repeated forward-slashes (//) or backslashes \\ are not valid in the href`);const e=c.normalizeRepeatedSlashes(l);i=(a?a[0]:"")+e}if(!T(i))return n?[i]:i;try{o=new URL(i.startsWith("#")?e.asPath:e.pathname,"http://n")}catch(e){o=new URL("/","http://n")}try{const e=new URL(i,o);e.pathname=r.normalizePathTrailingSlash(e.pathname);let t="";if(f.isDynamicRoute(e.pathname)&&e.searchParams&&n){const n=p.searchParamsToUrlQuery(e.searchParams),{result:r,params:o}=R(e.pathname,e.pathname,n);r&&(t=y.formatWithValidation({pathname:r,hash:e.hash,query:A(n,o)}))}const a=e.origin===o.origin?e.href.slice(e.origin.length):e.href;return n?[a,t||a]:a}catch(e){return n?[i]:i}}function I(e){const t=c.getLocationOrigin();return e.startsWith(t)?e.substring(t.length):e}function D(e,t,n){let[r,o]=M(e,t,!0);const i=c.getLocationOrigin(),a=r.startsWith(i),l=o&&o.startsWith(i);r=I(r),o=o?I(o):o;const s=a?r:j(r),u=n?I(M(e,n)):o||r;return{url:s,as:l?u:j(u)}}function N(e,t){const n=r.removePathTrailingSlash(l.denormalizePagePath(e));return"/404"===n||"/_error"===n?e:(t.includes(n)||t.some((t=>{if(f.isDynamicRoute(t)&&m.getRouteRegex(t).re.test(n))return e=t,!0})),r.removePathTrailingSlash(e))}const z=window.omnivoreEnv.__NEXT_SCROLL_RESTORATION&&"undefined"!=typeof window&&"scrollRestoration"in window.history&&!!function(){try{let e="__next";return sessionStorage.setItem(e,e),sessionStorage.removeItem(e),!0}catch(e){}}(),F=Symbol("SSG_DATA_NOT_FOUND");function $(e,t,n){return fetch(e,{credentials:"same-origin"}).then((r=>{if(!r.ok){if(t>1&&r.status>=500)return $(e,t-1,n);if(404===r.status)return r.json().then((e=>{if(e.notFound)return{notFound:F};throw new Error("Failed to load static props")}));throw new Error("Failed to load static props")}return n.text?r.text():r.json()}))}function B(e,t,n,r,i){const{href:a}=new URL(e,window.location.href);return void 0!==r[a]?r[a]:r[a]=$(e,t?3:1,{text:n}).catch((e=>{throw t||o.markAssetError(e),e})).then((e=>(i||delete r[a],e))).catch((e=>{throw delete r[a],e}))}class W{constructor(e,t,n,{initialProps:o,pageLoader:i,App:a,wrapApp:l,Component:s,err:u,subscription:p,isFallback:h,locale:g,locales:m,defaultLocale:v,domainLocales:b,isPreview:k,isRsc:E}){this.sdc={},this.sdr={},this.sde={},this._idx=0,this.onPopState=e=>{const t=e.state;if(!t){const{pathname:e,query:t}=this;return void this.changeState("replaceState",y.formatWithValidation({pathname:j(e),query:t}),c.getURL())}if(!t.__N)return;let n;const{url:r,as:o,options:i,idx:a}=t;if(window.omnivoreEnv.__NEXT_SCROLL_RESTORATION&&z&&this._idx!==a){try{sessionStorage.setItem("__next_scroll_"+this._idx,JSON.stringify({x:self.pageXOffset,y:self.pageYOffset}))}catch{}try{const e=sessionStorage.getItem("__next_scroll_"+a);n=JSON.parse(e)}catch{n={x:0,y:0}}}this._idx=a;const{pathname:l}=d.parseRelativeUrl(r);this.isSsr&&o===j(this.asPath)&&l===j(this.pathname)||this._bps&&!this._bps(t)||this.change("replaceState",r,o,Object.assign({},i,{shallow:i.shallow&&this._shallow,locale:i.locale||this.defaultLocale}),n)};const S=r.removePathTrailingSlash(e);this.components={},"/_error"!==e&&(this.components[S]={Component:s,initial:!0,props:o,err:u,__N_SSG:o&&o.__N_SSG,__N_SSP:o&&o.__N_SSP,__N_RSC:!!E}),this.components["/_app"]={Component:a,styleSheets:[]},this.events=W.events,this.pageLoader=i;const _=f.isDynamicRoute(e)&&self.__NEXT_DATA__.autoExport;if(this.basePath=x,this.sub=p,this.clc=null,this._wrapApp=l,this.isSsr=!0,this.isLocaleDomain=!1,this.isReady=!(!(self.__NEXT_DATA__.gssp||self.__NEXT_DATA__.gip||self.__NEXT_DATA__.appGip&&!self.__NEXT_DATA__.gsp)&&(_||self.location.search||window.omnivoreEnv.__NEXT_HAS_REWRITES)),window.omnivoreEnv.__NEXT_I18N_SUPPORT&&(this.locales=m,this.defaultLocale=v,this.domainLocales=b,this.isLocaleDomain=!!w(b,self.location.hostname)),this.state={route:S,pathname:e,query:t,asPath:_?e:n,isPreview:!!k,locale:window.omnivoreEnv.__NEXT_I18N_SUPPORT?g:void 0,isFallback:h},"undefined"!=typeof window){if(!n.startsWith("//")){const r={locale:g};r._shouldResolveHref=n!==e,this.changeState("replaceState",y.formatWithValidation({pathname:j(e),query:t}),c.getURL(),r)}window.addEventListener("popstate",this.onPopState),window.omnivoreEnv.__NEXT_SCROLL_RESTORATION&&z&&(window.history.scrollRestoration="manual")}}reload(){window.location.reload()}back(){window.history.back()}push(e,t,n={}){if(window.omnivoreEnv.__NEXT_SCROLL_RESTORATION&&z)try{sessionStorage.setItem("__next_scroll_"+this._idx,JSON.stringify({x:self.pageXOffset,y:self.pageYOffset}))}catch{}return({url:e,as:t}=D(this,e,t)),this.change("pushState",e,t,n)}replace(e,t,n={}){return({url:e,as:t}=D(this,e,t)),this.change("replaceState",e,t,n)}async change(e,t,n,l,u){if(!T(t))return window.location.href=t,!1;const p=l._h||l._shouldResolveHref||C(t)===C(n),v={...this.state};l._h&&(this.isReady=!0);const b=v.locale;if(window.omnivoreEnv.__NEXT_I18N_SUPPORT){v.locale=!1===l.locale?this.defaultLocale:l.locale||v.locale,void 0===l.locale&&(l.locale=v.locale);const e=d.parseRelativeUrl(P(n)?L(n):n),r=s.normalizeLocalePath(e.pathname,this.locales);r.detectedLocale&&(v.locale=r.detectedLocale,e.pathname=j(e.pathname),n=y.formatWithValidation(e),t=j(s.normalizeLocalePath(P(t)?L(t):t,this.locales).pathname));let o=!1;var x;window.omnivoreEnv.__NEXT_I18N_SUPPORT&&((null===(x=this.locales)||void 0===x?void 0:x.includes(v.locale))||(e.pathname=_(e.pathname,v.locale),window.location.href=y.formatWithValidation(e),o=!0));const i=w(this.domainLocales,void 0,v.locale);if(window.omnivoreEnv.__NEXT_I18N_SUPPORT&&!o&&i&&this.isLocaleDomain&&self.location.hostname!==i.domain){const e=L(n);window.location.href=`http${i.http?"":"s"}://${i.domain}${j(`${v.locale===i.defaultLocale?"":`/${v.locale}`}${"/"===e?"":e}`||"/")}`,o=!0}if(o)return new Promise((()=>{}))}l._h||(this.isSsr=!1),c.ST&&performance.mark("routeChange");const{shallow:k=!1,scroll:E=!0}=l,S={shallow:k};this._inFlightRoute&&this.abortComponentLoad(this._inFlightRoute,S),n=j(_(P(n)?L(n):n,l.locale,this.defaultLocale));const M=O(P(n)?L(n):n,v.locale);this._inFlightRoute=n;let I=b!==v.locale;if(!l._h&&this.onlyAHashChange(M)&&!I)return v.asPath=M,W.events.emit("hashChangeStart",n,S),this.changeState(e,t,n,{...l,scroll:!1}),E&&this.scrollToHash(M),this.set(v,this.components[v.route],null),W.events.emit("hashChangeComplete",n,S),!0;let z,$,B=d.parseRelativeUrl(t),{pathname:H,query:U}=B;try{[z,{__rewrites:$}]=await Promise.all([this.pageLoader.getPageList(),o.getClientBuildManifest(),this.pageLoader.getMiddlewareList()])}catch(e){return window.location.href=n,!1}this.urlIsNew(M)||I||(e="replaceState");let V=n;if(H=H?r.removePathTrailingSlash(L(H)):H,p&&"/_error"!==H)if(l._shouldResolveHref=!0,window.omnivoreEnv.__NEXT_HAS_REWRITES&&n.startsWith("/")){const e=h.default(j(_(M,v.locale)),z,$,U,(e=>N(e,z)),this.locales);if(e.externalDest)return location.href=n,!0;V=e.asPath,e.matchedPage&&e.resolvedHref&&(H=e.resolvedHref,B.pathname=j(H),t=y.formatWithValidation(B))}else B.pathname=N(H,z),B.pathname!==H&&(H=B.pathname,B.pathname=j(H),t=y.formatWithValidation(B));if(!T(n))return window.location.href=n,!1;if(V=O(L(V),v.locale),(!l.shallow||1===l._h)&&(1!==l._h||f.isDynamicRoute(r.removePathTrailingSlash(H)))){const r=await this._preflightRequest({as:n,cache:!0,pages:z,pathname:H,query:U,locale:v.locale,isPreview:v.isPreview});if("rewrite"===r.type)U={...U,...r.parsedAs.query},V=r.asPath,H=r.resolvedHref,B.pathname=r.resolvedHref,t=y.formatWithValidation(B);else{if("redirect"===r.type&&r.newAs)return this.change(e,r.newUrl,r.newAs,l);if("redirect"===r.type&&r.destination)return window.location.href=r.destination,new Promise((()=>{}));if("refresh"===r.type&&n!==window.location.pathname)return window.location.href=n,new Promise((()=>{}))}}const q=r.removePathTrailingSlash(H);if(f.isDynamicRoute(q)){const e=d.parseRelativeUrl(V),r=e.pathname,o=m.getRouteRegex(q),i=g.getRouteMatcher(o)(r),a=q===r,l=a?R(q,r,U):{};if(!i||a&&!l.result){const e=Object.keys(o.groups).filter((e=>!U[e]));if(e.length>0)throw new Error((a?`The provided \`href\` (${t}) value is missing query values (${e.join(", ")}) to be interpolated properly. `:`The provided \`as\` value (${r}) is incompatible with the \`href\` value (${q}). `)+"Read more: https://nextjs.org/docs/messages/"+(a?"href-interpolation-failed":"incompatible-href-as"))}else a?n=y.formatWithValidation(Object.assign({},e,{pathname:l.result,query:A(U,l.params)})):Object.assign(U,i)}W.events.emit("routeChangeStart",n,S);try{var X,G;let r=await this.getRouteInfo(q,H,U,n,V,S,v.locale,v.isPreview),{error:o,props:a,__N_SSG:s,__N_SSP:c}=r;const f=r.Component;if(f&&f.unstable_scriptLoader&&[].concat(f.unstable_scriptLoader()).forEach((e=>{i.handleClientScriptLoad(e.props)})),(s||c)&&a){if(a.pageProps&&a.pageProps.__N_REDIRECT){const t=a.pageProps.__N_REDIRECT;if(t.startsWith("/")&&!1!==a.pageProps.__N_REDIRECT_BASE_PATH){const n=d.parseRelativeUrl(t);n.pathname=N(n.pathname,z);const{url:r,as:o}=D(this,t,t);return this.change(e,r,o,l)}return window.location.href=t,new Promise((()=>{}))}if(v.isPreview=!!a.__N_PREVIEW,a.notFound===F){let e;try{await this.fetchComponent("/404"),e="/404"}catch(t){e="/_error"}r=await this.getRouteInfo(e,e,U,n,V,{shallow:!1},v.locale,v.isPreview)}}W.events.emit("beforeHistoryChange",n,S),this.changeState(e,t,n,l),l._h&&"/_error"===H&&500===(null===(X=self.__NEXT_DATA__.props)||void 0===X||null===(G=X.pageProps)||void 0===G?void 0:G.statusCode)&&(null==a?void 0:a.pageProps)&&(a.pageProps.statusCode=500);const p=l.shallow&&v.route===q;var Y;const h=(null!==(Y=l.scroll)&&void 0!==Y?Y:!p)?{x:0,y:0}:null;if(await this.set({...v,route:q,pathname:H,query:U,asPath:M,isFallback:!1},r,null!=u?u:h).catch((e=>{if(!e.cancelled)throw e;o=o||e})),o)throw W.events.emit("routeChangeError",o,M,S),o;return window.omnivoreEnv.__NEXT_I18N_SUPPORT&&v.locale&&(document.documentElement.lang=v.locale),W.events.emit("routeChangeComplete",n,S),!0}catch(e){if(a.default(e)&&e.cancelled)return!1;throw e}}changeState(e,t,n,r={}){"pushState"===e&&c.getURL()===n||(this._shallow=r.shallow,window.history[e]({url:t,as:n,options:r,__N:!0,idx:this._idx="pushState"!==e?this._idx:this._idx+1},"",n))}async handleRouteInfoError(e,t,n,r,i,l){if(e.cancelled)throw e;if(o.isAssetError(e)||l)throw W.events.emit("routeChangeError",e,r,i),window.location.href=r,k();try{let r,o,i;void 0!==r&&void 0!==o||({page:r,styleSheets:o}=await this.fetchComponent("/_error"));const a={props:i,Component:r,styleSheets:o,err:e,error:e};if(!a.props)try{a.props=await this.getInitialProps(r,{err:e,pathname:t,query:n})}catch(e){console.error("Error in error page `getInitialProps`: ",e),a.props={}}return a}catch(e){return this.handleRouteInfoError(a.default(e)?e:new Error(e+""),t,n,r,i,!0)}}async getRouteInfo(e,t,n,r,o,i,l,s){try{const a=this.components[e];if(i.shallow&&a&&this.route===e)return a;let u;a&&!("initial"in a)&&(u=a);const c=u||await this.fetchComponent(e).then((e=>({Component:e.page,styleSheets:e.styleSheets,__N_SSG:e.mod.__N_SSG,__N_SSP:e.mod.__N_SSP,__N_RSC:!!e.mod.__next_rsc__}))),{Component:f,__N_SSG:d,__N_SSP:p,__N_RSC:h}=c;let g;const m=p&&h;(d||p||h)&&(g=this.pageLoader.getDataHref({href:y.formatWithValidation({pathname:t,query:n}),asPath:o,ssg:d,flight:m,locale:l}));const v=await this._getData((()=>(d||p||h)&&!m?B(g,this.isSsr,!1,d?this.sdc:this.sdr,!!d&&!s):this.getInitialProps(f,{pathname:t,query:n,asPath:r,locale:l,locales:this.locales,defaultLocale:this.defaultLocale})));if(h)if(m){const{data:e}=await this._getData((()=>this._getFlightData(g)));v.pageProps=Object.assign(v.pageProps,{__flight__:e})}else{const{__flight__:e}=v;v.pageProps=Object.assign({},v.pageProps,{__flight__:e})}return c.props=v,this.components[e]=c,c}catch(e){return this.handleRouteInfoError(a.getProperError(e),t,n,r,i)}}set(e,t,n){return this.state=e,this.sub(t,this.components["/_app"].Component,n)}beforePopState(e){this._bps=e}onlyAHashChange(e){if(!this.asPath)return!1;const[t,n]=this.asPath.split("#"),[r,o]=e.split("#");return!(!o||t!==r||n!==o)||t===r&&n!==o}scrollToHash(e){const[,t=""]=e.split("#");if(""===t||"top"===t)return void window.scrollTo(0,0);const n=document.getElementById(t);if(n)return void n.scrollIntoView();const r=document.getElementsByName(t)[0];r&&r.scrollIntoView()}urlIsNew(e){return this.asPath!==e}async prefetch(e,t=e,n={}){let i=d.parseRelativeUrl(e),{pathname:a,query:l}=i;if(window.omnivoreEnv.__NEXT_I18N_SUPPORT&&!1===n.locale){a=s.normalizeLocalePath(a,this.locales).pathname,i.pathname=a,e=y.formatWithValidation(i);let r=d.parseRelativeUrl(t);const o=s.normalizeLocalePath(r.pathname,this.locales);r.pathname=o.pathname,n.locale=o.detectedLocale||this.defaultLocale,t=y.formatWithValidation(r)}const u=await this.pageLoader.getPageList();let c=t;if(window.omnivoreEnv.__NEXT_HAS_REWRITES&&t.startsWith("/")){let n;({__rewrites:n}=await o.getClientBuildManifest());const r=h.default(j(_(t,this.locale)),u,n,i.query,(e=>N(e,u)),this.locales);if(r.externalDest)return;c=O(L(r.asPath),this.locale),r.matchedPage&&r.resolvedHref&&(a=r.resolvedHref,i.pathname=a,e=y.formatWithValidation(i))}else i.pathname=N(i.pathname,u),i.pathname!==a&&(a=i.pathname,i.pathname=a,e=y.formatWithValidation(i));const f=await this._preflightRequest({as:j(t),cache:!0,pages:u,pathname:a,query:l,locale:this.locale,isPreview:this.isPreview});"rewrite"===f.type&&(i.pathname=f.resolvedHref,a=f.resolvedHref,l={...l,...f.parsedAs.query},c=f.asPath,e=y.formatWithValidation(i));const p=r.removePathTrailingSlash(a);await Promise.all([this.pageLoader._isSsg(p).then((t=>!!t&&B(this.pageLoader.getDataHref({href:e,asPath:c,ssg:!0,locale:void 0!==n.locale?n.locale:this.locale}),!1,!1,this.sdc,!0))),this.pageLoader[n.priority?"loadPage":"prefetch"](p)])}async fetchComponent(e){let t=!1;const n=this.clc=()=>{t=!0},r=()=>{if(t){const t=new Error(`Abort fetching component for route: "${e}"`);throw t.cancelled=!0,t}n===this.clc&&(this.clc=null)};try{const t=await this.pageLoader.loadPage(e);return r(),t}catch(e){throw r(),e}}_getData(e){let t=!1;const n=()=>{t=!0};return this.clc=n,e().then((e=>{if(n===this.clc&&(this.clc=null),t){const e=new Error("Loading initial props cancelled");throw e.cancelled=!0,e}return e}))}_getFlightData(e){return B(e,!0,!0,this.sdc,!1).then((e=>({data:e})))}async _preflightRequest(e){const t=C(e.as),n=O(P(t)?L(t):t,e.locale);if(!(await this.pageLoader.getMiddlewareList()).some((([e,t])=>g.getRouteMatcher(v.getMiddlewareRegex(e,!t))(n))))return{type:"next"};const o=_(e.as,e.locale);let i;try{i=await this._getPreflightData({preflightHref:o,shouldCache:e.cache,isPreview:e.isPreview})}catch(t){return{type:"redirect",destination:e.as}}if(i.rewrite){if(!i.rewrite.startsWith("/"))return{type:"redirect",destination:e.as};const t=d.parseRelativeUrl(s.normalizeLocalePath(P(i.rewrite)?L(i.rewrite):i.rewrite,this.locales).pathname),n=r.removePathTrailingSlash(t.pathname);let o,a;return e.pages.includes(n)?(o=!0,a=n):(a=N(n,e.pages),a!==t.pathname&&e.pages.includes(a)&&(o=!0)),{type:"rewrite",asPath:t.pathname,parsedAs:t,matchedPage:o,resolvedHref:a}}if(i.redirect){if(i.redirect.startsWith("/")){const e=r.removePathTrailingSlash(s.normalizeLocalePath(P(i.redirect)?L(i.redirect):i.redirect,this.locales).pathname),{url:t,as:n}=D(this,e,e);return{type:"redirect",newUrl:t,newAs:n}}return{type:"redirect",destination:i.redirect}}return i.refresh&&!i.ssr?{type:"refresh"}:{type:"next"}}_getPreflightData(e){const{preflightHref:t,shouldCache:n=!1,isPreview:r}=e,{href:o}=new URL(t,window.location.href);return!r&&n&&this.sde[o]?Promise.resolve(this.sde[o]):fetch(t,{method:"HEAD",credentials:"same-origin",headers:{"x-middleware-preflight":"1"}}).then((e=>{if(!e.ok)throw new Error("Failed to preflight request");return{cache:e.headers.get("x-middleware-cache"),redirect:e.headers.get("Location"),refresh:e.headers.has("x-middleware-refresh"),rewrite:e.headers.get("x-middleware-rewrite"),ssr:!!e.headers.get("x-middleware-ssr")}})).then((e=>(n&&"no-cache"!==e.cache&&(this.sde[o]=e),e))).catch((e=>{throw delete this.sde[o],e}))}getInitialProps(e,t){const{Component:n}=this.components["/_app"],r=this._wrapApp(n);return t.AppTree=r,c.loadGetInitialProps(n,{AppTree:r,Component:e,router:this,ctx:t})}abortComponentLoad(e,t){this.clc&&(W.events.emit("routeChangeError",k(),e,t),this.clc(),this.clc=null)}get route(){return this.state.route}get pathname(){return this.state.pathname}get query(){return this.state.query}get asPath(){return this.state.asPath}get locale(){return this.state.locale}get isFallback(){return this.state.isFallback}get isPreview(){return this.state.isPreview}}t.default=W,W.events=u.default()},6555:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.formatUrl=i,t.formatWithValidation=function(e){return i(e)},t.urlObjectKeys=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(646));const o=/https?|ftp|gopher|file/;function i(e){let{auth:t,hostname:n}=e,i=e.protocol||"",a=e.pathname||"",l=e.hash||"",s=e.query||"",u=!1;t=t?encodeURIComponent(t).replace(/%3A/i,":")+"@":"",e.host?u=t+e.host:n&&(u=t+(~n.indexOf(":")?`[${n}]`:n),e.port&&(u+=":"+e.port)),s&&"object"==typeof s&&(s=String(r.urlQueryToSearchParams(s)));let c=e.search||s&&`?${s}`||"";return i&&!i.endsWith(":")&&(i+=":"),e.slashes||(!i||o.test(i))&&!1!==u?(u="//"+(u||""),a&&"/"!==a[0]&&(a="/"+a)):u||(u=""),l&&"#"!==l[0]&&(l="#"+l),c&&"?"!==c[0]&&(c="?"+c),a=a.replace(/[?#]/g,encodeURIComponent),c=c.replace("#","%23"),`${i}${u}${a}${c}${l}`}t.urlObjectKeys=["auth","hash","host","hostname","href","path","pathname","port","protocol","query","search","slashes"]},9983:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t=""){return("/"===e?"/index":/^\/index(\/|$)/.test(e)?`/index${e}`:`${e}`)+t}},2763:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getMiddlewareRegex=function(e,t=!0){const n=r.getParametrizedRoute(e);let o=t?"(?!_next).*":"",i=t?"(?:(/.*)?)":"";return"routeKeys"in n?"/"===n.parameterizedRoute?{groups:{},namedRegex:`^/${o}$`,re:new RegExp(`^/${o}$`),routeKeys:{}}:{groups:n.groups,namedRegex:`^${n.namedParameterizedRoute}${i}$`,re:new RegExp(`^${n.parameterizedRoute}${i}$`),routeKeys:n.routeKeys}:"/"===n.parameterizedRoute?{groups:{},re:new RegExp(`^/${o}$`)}:{groups:{},re:new RegExp(`^${n.parameterizedRoute}${i}$`)}};var r=n(4794)},9150:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"getMiddlewareRegex",{enumerable:!0,get:function(){return r.getMiddlewareRegex}}),Object.defineProperty(t,"getRouteMatcher",{enumerable:!0,get:function(){return o.getRouteMatcher}}),Object.defineProperty(t,"getRouteRegex",{enumerable:!0,get:function(){return i.getRouteRegex}}),Object.defineProperty(t,"getSortedRoutes",{enumerable:!0,get:function(){return a.getSortedRoutes}}),Object.defineProperty(t,"isDynamicRoute",{enumerable:!0,get:function(){return l.isDynamicRoute}});var r=n(2763),o=n(3107),i=n(4794),a=n(9036),l=n(7482)},7482:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isDynamicRoute=function(e){return n.test(e)};const n=/\/\[[^/]+?\](?=\/|$)/},1577:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parseRelativeUrl=function(e,t){const n=new URL("undefined"==typeof window?"http://n":r.getLocationOrigin()),i=t?new URL(t,n):n,{pathname:a,searchParams:l,search:s,hash:u,href:c,origin:f}=new URL(e,i);if(f!==n.origin)throw new Error(`invariant: invalid relative URL, router received ${e}`);return{pathname:a,query:o.searchParamsToUrlQuery(l),search:s,hash:u,href:c.slice(n.origin.length)}};var r=n(1624),o=n(646)},2011:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parseUrl=function(e){if(e.startsWith("/"))return o.parseRelativeUrl(e);const t=new URL(e);return{hash:t.hash,hostname:t.hostname,href:t.href,pathname:t.pathname,port:t.port,protocol:t.protocol,query:r.searchParamsToUrlQuery(t.searchParams),search:t.search}};var r=n(646),o=n(1577)},1095:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getPathMatch=function(e,t){const n=[],o=r.pathToRegexp(e,n,{delimiter:"/",sensitive:!1,strict:null==t?void 0:t.strict}),i=r.regexpToFunction((null==t?void 0:t.regexModifier)?new RegExp(t.regexModifier(o.source),o.flags):o,n);return(e,r)=>{const o=null!=e&&i(e);if(!o)return!1;if(null==t?void 0:t.removeUnnamedParams)for(const e of n)"number"==typeof e.name&&delete o.params[e.name];return{...r,...o.params}}};var r=n(9264)},9716:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.matchHas=function(e,t,n){const r={};return!!t.every((t=>{let o,i=t.key;switch(t.type){case"header":i=i.toLowerCase(),o=e.headers[i];break;case"cookie":o=e.cookies[t.key];break;case"query":o=n[i];break;case"host":{const{host:t}=(null==e?void 0:e.headers)||{};o=null==t?void 0:t.split(":")[0].toLowerCase();break}}if(!t.value&&o)return r[function(e){let t="";for(let n=0;n64&&r<91||r>96&&r<123)&&(t+=e[n])}return t}(i)]=o,!0;if(o){const e=new RegExp(`^${t.value}$`),n=Array.isArray(o)?o.slice(-1)[0].match(e):o.match(e);if(n)return Array.isArray(n)&&(n.groups?Object.keys(n.groups).forEach((e=>{r[e]=n.groups[e]})):"host"===t.type&&n[0]&&(r.host=n[0])),!0}return!1}))&&r},t.compileNonPath=a,t.prepareDestination=function(e){const t=Object.assign({},e.query);delete t.__nextLocale,delete t.__nextDefaultLocale;let n=e.destination;for(const r of Object.keys({...e.params,...t}))s=r,n=n.replace(new RegExp(`:${o.escapeStringRegexp(s)}`,"g"),`__ESC_COLON_${s}`);var s;const u=i.parseUrl(n),c=u.query,f=l(`${u.pathname}${u.hash||""}`),d=l(u.hostname||""),p=[],h=[];r.pathToRegexp(f,p),r.pathToRegexp(d,h);const g=[];p.forEach((e=>g.push(e.name))),h.forEach((e=>g.push(e.name)));const m=r.compile(f,{validate:!1}),v=r.compile(d,{validate:!1});for(const[t,n]of Object.entries(c))Array.isArray(n)?c[t]=n.map((t=>a(l(t),e.params))):c[t]=a(l(n),e.params);let y,b=Object.keys(e.params).filter((e=>"nextInternalLocale"!==e));if(e.appendParamsToQuery&&!b.some((e=>g.includes(e))))for(const t of b)t in c||(c[t]=e.params[t]);try{y=m(e.params);const[t,n]=y.split("#");u.hostname=v(e.params),u.pathname=t,u.hash=`${n?"#":""}${n||""}`,delete u.search}catch(e){if(e.message.match(/Expected .*? to not repeat, but got an array/))throw new Error("To use a multi-match in the destination you must add `*` at the end of the param name to signify it should repeat. https://nextjs.org/docs/messages/invalid-multi-match");throw e}return u.query={...t,...u.query},{newUrl:y,destQuery:c,parsedDestination:u}};var r=n(9264),o=n(8058),i=n(2011);function a(e,t){if(!e.includes(":"))return e;for(const n of Object.keys(t))e.includes(`:${n}`)&&(e=e.replace(new RegExp(`:${n}\\*`,"g"),`:${n}--ESCAPED_PARAM_ASTERISKS`).replace(new RegExp(`:${n}\\?`,"g"),`:${n}--ESCAPED_PARAM_QUESTION`).replace(new RegExp(`:${n}\\+`,"g"),`:${n}--ESCAPED_PARAM_PLUS`).replace(new RegExp(`:${n}(?!\\w)`,"g"),`--ESCAPED_PARAM_COLON${n}`));return e=e.replace(/(:|\*|\?|\+|\(|\)|\{|\})/g,"\\$1").replace(/--ESCAPED_PARAM_PLUS/g,"+").replace(/--ESCAPED_PARAM_COLON/g,":").replace(/--ESCAPED_PARAM_QUESTION/g,"?").replace(/--ESCAPED_PARAM_ASTERISKS/g,"*"),r.compile(`/${e}`,{validate:!1})(t).slice(1)}function l(e){return e.replace(/__ESC_COLON_/gi,":")}},646:(e,t)=>{"use strict";function n(e){return"string"==typeof e||"number"==typeof e&&!isNaN(e)||"boolean"==typeof e?String(e):""}Object.defineProperty(t,"__esModule",{value:!0}),t.searchParamsToUrlQuery=function(e){const t={};return e.forEach(((e,n)=>{void 0===t[n]?t[n]=e:Array.isArray(t[n])?t[n].push(e):t[n]=[t[n],e]})),t},t.urlQueryToSearchParams=function(e){const t=new URLSearchParams;return Object.entries(e).forEach((([e,r])=>{Array.isArray(r)?r.forEach((r=>t.append(e,n(r)))):t.set(e,n(r))})),t},t.assign=function(e,...t){return t.forEach((t=>{Array.from(t.keys()).forEach((t=>e.delete(t))),t.forEach(((t,n)=>e.append(n,t)))})),e}},5317:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t,n,u,c,f){let d,p=!1,h=!1,g=l.parseRelativeUrl(e),m=i.removePathTrailingSlash(a.normalizeLocalePath(s.delBasePath(g.pathname),f).pathname);const v=n=>{let l=r.getPathMatch(n.source,{removeUnnamedParams:!0,strict:!0})(g.pathname);if(n.has&&l){const e=o.matchHas({headers:{host:document.location.hostname},cookies:document.cookie.split("; ").reduce(((e,t)=>{const[n,...r]=t.split("=");return e[n]=r.join("="),e}),{})},n.has,g.query);e?Object.assign(l,e):l=!1}if(l){if(!n.destination)return h=!0,!0;const r=o.prepareDestination({appendParamsToQuery:!0,destination:n.destination,params:l,query:u});if(g=r.parsedDestination,e=r.newUrl,Object.assign(u,r.parsedDestination.query),m=i.removePathTrailingSlash(a.normalizeLocalePath(s.delBasePath(e),f).pathname),t.includes(m))return p=!0,d=m,!0;if(d=c(m),d!==e&&t.includes(d))return p=!0,!0}};let y=!1;for(let e=0;e{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getRouteMatcher=function(e){const{re:t,groups:n}=e;return e=>{const o=t.exec(e);if(!o)return!1;const i=e=>{try{return decodeURIComponent(e)}catch(e){throw new r.DecodeError("failed to decode param")}},a={};return Object.keys(n).forEach((e=>{const t=n[e],r=o[t.pos];void 0!==r&&(a[e]=~r.indexOf("/")?r.split("/").map((e=>i(e))):t.repeat?[i(r)]:i(r))})),a}};var r=n(1624)},4794:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getParametrizedRoute=i,t.getRouteRegex=function(e){const t=i(e);return"routeKeys"in t?{re:new RegExp(`^${t.parameterizedRoute}(?:/)?$`),groups:t.groups,routeKeys:t.routeKeys,namedRegex:`^${t.namedParameterizedRoute}(?:/)?$`}:{re:new RegExp(`^${t.parameterizedRoute}(?:/)?$`),groups:t.groups}};var r=n(8058);function o(e){const t=e.startsWith("[")&&e.endsWith("]");t&&(e=e.slice(1,-1));const n=e.startsWith("...");return n&&(e=e.slice(3)),{key:e,repeat:n,optional:t}}function i(e){const t=(e.replace(/\/$/,"")||"/").slice(1).split("/"),n={};let i=1;const a=t.map((e=>{if(e.startsWith("[")&&e.endsWith("]")){const{key:t,optional:r,repeat:a}=o(e.slice(1,-1));return n[t]={pos:i++,repeat:a,optional:r},a?r?"(?:/(.+?))?":"/(.+?)":"/([^/]+?)"}return`/${r.escapeStringRegexp(e)}`})).join("");if("undefined"==typeof window){let e=97,i=1;const l=()=>{let t="";for(let n=0;n122&&(i++,e=97);return t},s={};return{parameterizedRoute:a,namedParameterizedRoute:t.map((e=>{if(e.startsWith("[")&&e.endsWith("]")){const{key:t,optional:n,repeat:r}=o(e.slice(1,-1));let i=t.replace(/\W/g,""),a=!1;return(0===i.length||i.length>30)&&(a=!0),isNaN(parseInt(i.slice(0,1)))||(a=!0),a&&(i=l()),s[i]=t,r?n?`(?:/(?<${i}>.+?))?`:`/(?<${i}>.+?)`:`/(?<${i}>[^/]+?)`}return`/${r.escapeStringRegexp(e)}`})).join(""),groups:n,routeKeys:s}}return{parameterizedRoute:a,groups:n}}},9036:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getSortedRoutes=function(e){const t=new n;return e.forEach((e=>t.insert(e))),t.smoosh()};class n{insert(e){this._insert(e.split("/").filter(Boolean),[],!1)}smoosh(){return this._smoosh()}_smoosh(e="/"){const t=[...this.children.keys()].sort();null!==this.slugName&&t.splice(t.indexOf("[]"),1),null!==this.restSlugName&&t.splice(t.indexOf("[...]"),1),null!==this.optionalRestSlugName&&t.splice(t.indexOf("[[...]]"),1);const n=t.map((t=>this.children.get(t)._smoosh(`${e}${t}/`))).reduce(((e,t)=>[...e,...t]),[]);if(null!==this.slugName&&n.push(...this.children.get("[]")._smoosh(`${e}[${this.slugName}]/`)),!this.placeholder){const t="/"===e?"/":e.slice(0,-1);if(null!=this.optionalRestSlugName)throw new Error(`You cannot define a route with the same specificity as a optional catch-all route ("${t}" and "${t}[[...${this.optionalRestSlugName}]]").`);n.unshift(t)}return null!==this.restSlugName&&n.push(...this.children.get("[...]")._smoosh(`${e}[...${this.restSlugName}]/`)),null!==this.optionalRestSlugName&&n.push(...this.children.get("[[...]]")._smoosh(`${e}[[...${this.optionalRestSlugName}]]/`)),n}_insert(e,t,r){if(0===e.length)return void(this.placeholder=!1);if(r)throw new Error("Catch-all must be the last part of the URL.");let o=e[0];if(o.startsWith("[")&&o.endsWith("]")){let i=o.slice(1,-1),a=!1;if(i.startsWith("[")&&i.endsWith("]")&&(i=i.slice(1,-1),a=!0),i.startsWith("...")&&(i=i.substring(3),r=!0),i.startsWith("[")||i.endsWith("]"))throw new Error(`Segment names may not start or end with extra brackets ('${i}').`);if(i.startsWith("."))throw new Error(`Segment names may not start with erroneous periods ('${i}').`);function l(e,n){if(null!==e&&e!==n)throw new Error(`You cannot use different slug names for the same dynamic path ('${e}' !== '${n}').`);t.forEach((e=>{if(e===n)throw new Error(`You cannot have the same slug name "${n}" repeat within a single dynamic path`);if(e.replace(/\W/g,"")===o.replace(/\W/g,""))throw new Error(`You cannot have the slug names "${e}" and "${n}" differ only by non-word symbols within a single dynamic path`)})),t.push(n)}if(r)if(a){if(null!=this.restSlugName)throw new Error(`You cannot use both an required and optional catch-all route at the same level ("[...${this.restSlugName}]" and "${e[0]}" ).`);l(this.optionalRestSlugName,i),this.optionalRestSlugName=i,o="[[...]]"}else{if(null!=this.optionalRestSlugName)throw new Error(`You cannot use both an optional and required catch-all route at the same level ("[[...${this.optionalRestSlugName}]]" and "${e[0]}").`);l(this.restSlugName,i),this.restSlugName=i,o="[...]"}else{if(a)throw new Error(`Optional route parameters are not yet supported ("${e[0]}").`);l(this.slugName,i),this.slugName=i,o="[]"}}this.children.has(o)||this.children.set(o,new n),this.children.get(o)._insert(e.slice(1),t,r)}constructor(){this.placeholder=!0,this.children=new Map,this.slugName=null,this.restSlugName=null,this.optionalRestSlugName=null}}},1889:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(2784));const o="undefined"==typeof window;class i extends r.Component{constructor(e){super(e),this.emitChange=()=>{this._hasHeadManager&&this.props.headManager.updateHead(this.props.reduceComponentsToState([...this.props.headManager.mountedInstances],this.props))},this._hasHeadManager=this.props.headManager&&this.props.headManager.mountedInstances,o&&this._hasHeadManager&&(this.props.headManager.mountedInstances.add(this),this.emitChange())}componentDidMount(){this._hasHeadManager&&this.props.headManager.mountedInstances.add(this),this.emitChange()}componentDidUpdate(){this.emitChange()}componentWillUnmount(){this._hasHeadManager&&this.props.headManager.mountedInstances.delete(this),this.emitChange()}render(){return null}}t.default=i},1624:(e,t)=>{"use strict";function n(){const{protocol:e,hostname:t,port:n}=window.location;return`${e}//${t}${n?":"+n:""}`}function r(e){return"string"==typeof e?e:e.displayName||e.name||"Unknown"}function o(e){return e.finished||e.headersSent}Object.defineProperty(t,"__esModule",{value:!0}),t.execOnce=function(e){let t,n=!1;return(...r)=>(n||(n=!0,t=e(...r)),t)},t.getLocationOrigin=n,t.getURL=function(){const{href:e}=window.location,t=n();return e.substring(t.length)},t.getDisplayName=r,t.isResSent=o,t.normalizeRepeatedSlashes=function(e){const t=e.split("?");return t[0].replace(/\\/g,"/").replace(/\/\/+/g,"/")+(t[1]?`?${t.slice(1).join("?")}`:"")},t.loadGetInitialProps=async function e(t,n){const i=n.res||n.ctx&&n.ctx.res;if(!t.getInitialProps)return n.ctx&&n.Component?{pageProps:await e(n.Component,n.ctx)}:{};const a=await t.getInitialProps(n);if(i&&o(i))return a;if(!a){const e=`"${r(t)}.getInitialProps()" should resolve to an object. But found "${a}" instead.`;throw new Error(e)}return a},t.ST=t.SP=t.warnOnce=void 0,t.warnOnce=e=>{};const i="undefined"!=typeof performance;t.SP=i;const a=i&&"function"==typeof performance.mark&&"function"==typeof performance.measure;t.ST=a;class l extends Error{}t.DecodeError=l;class s extends Error{}t.NormalizeError=s},6577:(e,t,n)=>{n(104)},9097:(e,t,n)=>{n(4529)},5632:(e,t,n)=>{e.exports=n(9518)},7320:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;function o(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,i){for(var a,l,s=o(e),u=1;u{"use strict";var r=n(2784),o=n(7320),i=n(4616);function a(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n