diff --git a/.github/ISSUE_TEMPLATE/blank-template.md b/.github/ISSUE_TEMPLATE/blank-template.md new file mode 100644 index 000000000..efa3001a6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/blank-template.md @@ -0,0 +1,10 @@ +--- +name: Blank Template +about: Should be used for most issues +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/qa-run-template.md b/.github/ISSUE_TEMPLATE/qa-run-template.md new file mode 100644 index 000000000..01381764d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/qa-run-template.md @@ -0,0 +1,28 @@ +--- +name: QA Run Template +about: Use this template when doing QA +title: '' +labels: QA +assignees: '' + +--- + +There are three QA focus areas in Omnivore: + +1. Saving +2. Library Management +3. Reader Controls + + +## Saving + +Omnivore has three APIs involved in saving that should all be tested. URLs, Files, and Pages (captured content from the browser or the mobile) app can all be saved + +## Library Management + +The main library in `/home` has functionality for searching and organizing content. + + +## Reader functionality + +The reader has functionality for highlighting, annotating, and saving reading progress diff --git a/Makefile b/Makefile index 8ba6128b1..1b1e110ae 100644 --- a/Makefile +++ b/Makefile @@ -6,3 +6,7 @@ apple_graphql_gen: apple_extension_gen: $(MAKE) -C apple extension_gen + +apple_webview_gen: + yarn workspace @omnivore/appreader build + cp packages/appreader/build/bundle.js apple/OmnivoreKit/Sources/Views/Resources/bundle.js diff --git a/apple/Omnivore.xcodeproj/project.pbxproj b/apple/Omnivore.xcodeproj/project.pbxproj index d27efe2d4..a4cb48c35 100644 --- a/apple/Omnivore.xcodeproj/project.pbxproj +++ b/apple/Omnivore.xcodeproj/project.pbxproj @@ -1463,7 +1463,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.4.0; + MARKETING_VERSION = 1.4.1; PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app; PRODUCT_NAME = Omnivore; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1495,7 +1495,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.4.0; + MARKETING_VERSION = 1.4.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( @@ -1534,7 +1534,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.4.0; + MARKETING_VERSION = 1.4.1; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( "-framework", @@ -1696,7 +1696,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.4.0; + MARKETING_VERSION = 1.4.1; PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.share-extension"; PRODUCT_NAME = ShareExtension; SDKROOT = iphoneos; @@ -1750,7 +1750,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.4.0; + MARKETING_VERSION = 1.4.1; PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app; PRODUCT_NAME = Omnivore; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1778,7 +1778,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.4.0; + MARKETING_VERSION = 1.4.1; PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.share-extension"; PRODUCT_NAME = ShareExtension; SDKROOT = iphoneos; diff --git a/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift b/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift index 5bf694f5b..4240b9509 100644 --- a/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift @@ -57,7 +57,8 @@ public final class PDFViewerViewModel: ObservableObject { prefix: nil, suffix: nil, patch: patch, - annotation: nil + annotation: nil, + createdByMe: true ) ) @@ -94,7 +95,8 @@ public final class PDFViewerViewModel: ObservableObject { prefix: nil, suffix: nil, patch: patch, - annotation: nil + annotation: nil, + createdByMe: true ) ) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift index f9c45cc71..63919c49c 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift @@ -14,19 +14,21 @@ struct FeedCardNavigationLink: View { @ObservedObject var viewModel: HomeFeedViewModel var body: some View { - NavigationLink( - destination: LinkItemDetailView(viewModel: LinkItemDetailViewModel(item: item, homeFeedViewModel: viewModel)), - tag: item, - selection: $selectedLinkItem - ) { - EmptyView() + ZStack { + NavigationLink( + destination: LinkItemDetailView(viewModel: LinkItemDetailViewModel(item: item, homeFeedViewModel: viewModel)), + tag: item, + selection: $selectedLinkItem + ) { + EmptyView() + } + .opacity(0) + .buttonStyle(PlainButtonStyle()) + .onAppear { + viewModel.itemAppeared(item: item, searchQuery: searchQuery, dataService: dataService) + } + FeedCard(item: item) } - .opacity(0) - .buttonStyle(PlainButtonStyle()) - .onAppear { - viewModel.itemAppeared(item: item, searchQuery: searchQuery, dataService: dataService) - } - FeedCard(item: item) } } diff --git a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift index 0d0234d29..06f94ea3c 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift @@ -147,6 +147,8 @@ struct LinkItemDetailView: View { #if os(iOS) if viewModel.item.isPDF { fixedNavBarReader + } else if FeatureFlag.useLocalWebView { + WebReaderContainerView(item: viewModel.item, homeFeedViewModel: viewModel.homeFeedViewModel) } else { hidingNavBarReader } diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift index a50e5643a..ca2443130 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift @@ -11,6 +11,14 @@ final class ProfileContainerViewModel: ObservableObject { var subscriptions = Set() + var appVersionString: String { + if let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String { + return "Omnivore Version \(appVersion)" + } else { + return "" + } + } + func loadProfileData(dataService: DataService) { dataService.viewerPublisher().sink( receiveCompletion: { _ in }, @@ -81,7 +89,7 @@ struct ProfileView: View { #endif } - Section { + Section(footer: Text(viewModel.appVersionString)) { if FeatureFlag.showAccountDeletion { NavigationLink( destination: ManageAccountView(handleAccountDeletion: { diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift new file mode 100644 index 000000000..dbcff789b --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift @@ -0,0 +1,84 @@ +import Models +import SwiftUI +import Utils +import Views +import WebKit + +struct WebReader: UIViewRepresentable { + let articleContent: ArticleContent + let item: FeedItem + let openLinkAction: (URL) -> Void + let webViewActionHandler: (WKScriptMessage, WKScriptMessageReplyHandler?) -> Void + let navBarVisibilityRatioUpdater: (Double) -> Void + + @Binding var increaseFontActionID: UUID? + @Binding var decreaseFontActionID: UUID? + @Binding var annotationSaveTransactionID: UUID? + @Binding var annotation: String + + func makeCoordinator() -> WebReaderCoordinator { + WebReaderCoordinator() + } + + func fontSize() -> Int { + let storedSize = UserDefaults.standard.integer(forKey: UserDefaultKey.preferredWebFontSize.rawValue) + return storedSize <= 1 ? UITraitCollection.current.preferredWebFontSize : storedSize + } + + func makeUIView(context: Context) -> WKWebView { + let webView = WebViewManager.shared() + let contentController = WKUserContentController() + + webView.loadHTMLString( + WebReaderContent( + articleContent: articleContent, + item: item, + isDark: UITraitCollection.current.userInterfaceStyle == .dark, + fontSize: fontSize() + ) + .styledContent, + baseURL: ViewsPackage.bundleURL + ) + + webView.navigationDelegate = context.coordinator + webView.isOpaque = false + webView.backgroundColor = .clear + webView.configuration.userContentController = contentController + webView.scrollView.delegate = context.coordinator + webView.scrollView.contentInset.top = readerViewNavBarHeight + webView.scrollView.verticalScrollIndicatorInsets.top = readerViewNavBarHeight + + webView.configuration.userContentController.removeAllScriptMessageHandlers() + + for action in WebViewAction.allCases { + webView.configuration.userContentController.add(context.coordinator, name: action.rawValue) + } + + webView.configuration.userContentController.add(webView, name: "viewerAction") + + webView.configuration.userContentController.addScriptMessageHandler(context.coordinator, contentWorld: .page, name: "articleAction") + + context.coordinator.linkHandler = openLinkAction + context.coordinator.webViewActionHandler = webViewActionHandler + context.coordinator.updateNavBarVisibilityRatio = navBarVisibilityRatioUpdater + + return webView + } + + func updateUIView(_ webView: WKWebView, context: Context) { + if annotationSaveTransactionID != context.coordinator.lastSavedAnnotationID { + context.coordinator.lastSavedAnnotationID = annotationSaveTransactionID + (webView as? WebView)?.saveAnnotation(annotation: annotation) + } + + if increaseFontActionID != context.coordinator.previousIncreaseFontActionID { + context.coordinator.previousIncreaseFontActionID = increaseFontActionID + (webView as? WebView)?.increaseFontSize() + } + + if decreaseFontActionID != context.coordinator.previousDecreaseFontActionID { + context.coordinator.previousDecreaseFontActionID = decreaseFontActionID + (webView as? WebView)?.decreaseFontSize() + } + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift new file mode 100644 index 000000000..503f67417 --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift @@ -0,0 +1,282 @@ +import Combine +import Models +import Services +import SwiftUI +import Views +import WebKit + +struct WebReaderContainerView: View { + let item: FeedItem + let homeFeedViewModel: HomeFeedViewModel + + @State private var showFontSizePopover = false + @State var showHighlightAnnotationModal = false + @State var safariWebLink: SafariWebLink? + @State private var navBarVisibilityRatio = 1.0 + @State private var showDeleteConfirmation = false + @State private var showOverlay = true + @State var increaseFontActionID: UUID? + @State var decreaseFontActionID: UUID? + @State var annotationSaveTransactionID: UUID? + @State var annotation = String() + + @EnvironmentObject var dataService: DataService + @Environment(\.presentationMode) var presentationMode: Binding + @StateObject var viewModel = WebReaderViewModel() + + var fontAdjustmentPopoverView: some View { + FontSizeAdjustmentPopoverView( + increaseFontAction: { increaseFontActionID = UUID() }, + decreaseFontAction: { decreaseFontActionID = UUID() } + ) + } + + func webViewActionHandler(message: WKScriptMessage, replyHandler: WKScriptMessageReplyHandler?) { + if message.name == WebViewAction.readingProgressUpdate.rawValue { + let messageBody = message.body as? [String: Double] + + if let messageBody = messageBody, let progress = messageBody["progress"] { + homeFeedViewModel.updateProgress(itemID: item.id, progress: Double(progress)) + } + } + + if let replyHandler = replyHandler { + viewModel.webViewActionWithReplyHandler( + message: message, + replyHandler: replyHandler, + dataService: dataService + ) + return + } + + if message.name == WebViewAction.highlightAction.rawValue { + handleHighlightAction(message: message) + } + + if message.name == WebViewAction.readingProgressUpdate.rawValue { + guard let messageBody = message.body as? [String: Double] else { return } + guard let progress = messageBody["progress"] else { return } + homeFeedViewModel.updateProgress(itemID: item.id, progress: Double(progress)) + } + } + + private func handleHighlightAction(message: WKScriptMessage) { + guard let messageBody = message.body as? [String: String] else { return } + guard let actionID = messageBody["actionID"] else { return } + + switch actionID { + case "annotate": + annotation = messageBody["annotation"] ?? "" + showHighlightAnnotationModal = true + default: + break + } + } + + var navBariOS14: some View { + HStack(alignment: .center) { + Button( + action: { self.presentationMode.wrappedValue.dismiss() }, + label: { + Image(systemName: "chevron.backward") + .font(.appTitleTwo) + .foregroundColor(.appGrayTextContrast) + .padding(.horizontal) + } + ) + .scaleEffect(navBarVisibilityRatio) + Spacer() + Button( + action: { showFontSizePopover.toggle() }, + label: { + Image(systemName: "textformat.size") + .font(.appTitleTwo) + } + ) + .padding(.horizontal) + .scaleEffect(navBarVisibilityRatio) + } + .frame(height: readerViewNavBarHeight * navBarVisibilityRatio) + .opacity(navBarVisibilityRatio) + .background(Color.systemBackground) + .onTapGesture { + showFontSizePopover = false + } + } + + @available(macOS 12.0, *) + @available(iOS 15.0, *) + var navBar: some View { + HStack(alignment: .center) { + Button( + action: { self.presentationMode.wrappedValue.dismiss() }, + label: { + Image(systemName: "chevron.backward") + .font(.appTitleTwo) + .foregroundColor(.appGrayTextContrast) + .padding(.horizontal) + } + ) + .scaleEffect(navBarVisibilityRatio) + Spacer() + Button( + action: { showFontSizePopover.toggle() }, + label: { + Image(systemName: "textformat.size") + .font(.appTitleTwo) + } + ) + .padding(.horizontal) + .scaleEffect(navBarVisibilityRatio) + Menu( + content: { + Group { + Button( + action: { + homeFeedViewModel.setLinkArchived( + dataService: dataService, + linkId: item.id, + archived: !item.isArchived + ) + }, + label: { + Label( + item.isArchived ? "Unarchive" : "Archive", + systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox" + ) + } + ) + Button( + action: { showDeleteConfirmation = true }, + label: { Label("Delete", systemImage: "trash") } + ) + } + }, + label: { + Image.profile + .padding(.horizontal) + .scaleEffect(navBarVisibilityRatio) + } + ) + } + .frame(height: readerViewNavBarHeight * navBarVisibilityRatio) + .opacity(navBarVisibilityRatio) + .background(Color.systemBackground) + .onTapGesture { + showFontSizePopover = false + } + .alert("Are you sure?", isPresented: $showDeleteConfirmation) { + Button("Remove Link", role: .destructive) { + homeFeedViewModel.removeLink(dataService: dataService, linkId: item.id) + } + Button("Cancel", role: .cancel, action: {}) + } + } + + var body: some View { + ZStack { + if let articleContent = viewModel.articleContent { + WebReader( + articleContent: articleContent, + item: item, + openLinkAction: { + #if os(macOS) + NSWorkspace.shared.open($0) + #elseif os(iOS) + safariWebLink = SafariWebLink(id: UUID(), url: $0) + #endif + }, + webViewActionHandler: webViewActionHandler, + navBarVisibilityRatioUpdater: { + if $0 < 1 { + showFontSizePopover = false + } + navBarVisibilityRatio = $0 + }, + increaseFontActionID: $increaseFontActionID, + decreaseFontActionID: $decreaseFontActionID, + annotationSaveTransactionID: $annotationSaveTransactionID, + annotation: $annotation + ) + .overlay( + Group { + if showOverlay { + Color.systemBackground + .transition(.opacity) + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(250)) { + withAnimation(.linear(duration: 0.2)) { + showOverlay = false + } + } + } + } + } + ) + .sheet(item: $safariWebLink) { + SafariView(url: $0.url) + } + .sheet(isPresented: $showHighlightAnnotationModal) { + HighlightAnnotationSheet( + annotation: $annotation, + onSave: { + annotationSaveTransactionID = UUID() + showHighlightAnnotationModal = false + }, + onCancel: { + showHighlightAnnotationModal = false + } + ) + } + } else { + Color.clear + .contentShape(Rectangle()) + .onAppear { + if !viewModel.isLoading { + viewModel.loadContent(dataService: dataService, slug: item.slug) + } + } + } + if showFontSizePopover { + VStack { + Color.clear + .contentShape(Rectangle()) + .frame(height: LinkItemDetailView.navBarHeight) + HStack { + Spacer() + fontAdjustmentPopoverView + .background(Color.appButtonBackground) + .cornerRadius(8) + .padding(.trailing, 44) + } + Spacer() + } + .background( + Color.clear + .contentShape(Rectangle()) + .onTapGesture { + showFontSizePopover = false + } + ) + } + if #available(iOS 15.0, *) { + VStack(spacing: 0) { + navBar + Spacer() + } + .navigationBarHidden(true) + } else { + VStack(spacing: 0) { + navBariOS14 + Spacer() + } + .navigationBarHidden(true) + } + + }.onDisappear { + // Clear the shared webview content when exiting + WebViewManager.shared().loadHTMLString("", baseURL: nil) + } + .navigationBarHidden(true) + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift new file mode 100644 index 000000000..8be76c50d --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift @@ -0,0 +1,77 @@ +import Foundation +import Models +import Utils + +struct WebReaderContent { + let textFontSize: Int + let articleContent: ArticleContent + let item: FeedItem + let themeKey: String + + init( + articleContent: ArticleContent, + item: FeedItem, + isDark: Bool, + fontSize: Int + ) { + self.textFontSize = fontSize + self.articleContent = articleContent + self.item = item + self.themeKey = isDark ? "Gray" : "LightGray" + } + + // swiftlint:disable line_length + var styledContent: String { + """ + + + + + + + + +
+ + + + + + + + + """ + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderCoordinator.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderCoordinator.swift new file mode 100644 index 000000000..c4ad4d6d5 --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderCoordinator.swift @@ -0,0 +1,128 @@ +import Combine +import Models +import Services +import SwiftUI +import UIKit +import Utils +import Views +import WebKit + +typealias WKScriptMessageReplyHandler = (Any?, String?) -> Void + +final class WebReaderCoordinator: NSObject { + var webViewActionHandler: (WKScriptMessage, WKScriptMessageReplyHandler?) -> Void = { _, _ in } + var linkHandler: (URL) -> Void = { _ in } + var needsReload = true + var lastSavedAnnotationID: UUID? + var previousIncreaseFontActionID: UUID? + var previousDecreaseFontActionID: UUID? + var updateNavBarVisibilityRatio: (Double) -> Void = { _ in } + private var yOffsetAtStartOfDrag: Double? + private var lastYOffset: Double = 0 + private var hasDragged = false + private var isNavBarHidden = false + + override init() { + super.init() + } + + var navBarVisibilityRatio: Double = 1.0 { + didSet { + isNavBarHidden = navBarVisibilityRatio == 0 + updateNavBarVisibilityRatio(navBarVisibilityRatio) + } + } +} + +extension WebReaderCoordinator: WKScriptMessageHandler { + func userContentController(_: WKUserContentController, didReceive message: WKScriptMessage) { + webViewActionHandler(message, nil) + } +} + +extension WebReaderCoordinator: WKScriptMessageHandlerWithReply { + func userContentController( + _: WKUserContentController, + didReceive message: WKScriptMessage, + replyHandler: @escaping (Any?, String?) -> Void + ) { + webViewActionHandler(message, replyHandler) + } +} + +extension WebReaderCoordinator: WKNavigationDelegate { + // swiftlint:disable:next line_length + func webView(_: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { + if navigationAction.navigationType == .linkActivated { + if let linkURL = navigationAction.request.url { + linkHandler(linkURL) + } + decisionHandler(.cancel) + } else { + decisionHandler(.allow) + } + } + + func webView(_ webView: WKWebView, didFinish _: WKNavigation!) { + #if os(iOS) + webView.isOpaque = true + webView.backgroundColor = .systemBackground + #endif + } +} + +#if os(iOS) + extension WebReaderCoordinator: UIScrollViewDelegate { + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + hasDragged = true + yOffsetAtStartOfDrag = scrollView.contentOffset.y + scrollView.contentInset.top + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + guard hasDragged else { return } + + let yOffset = scrollView.contentOffset.y + + if yOffset == 0 { + scrollView.contentInset.top = readerViewNavBarHeight + navBarVisibilityRatio = 1 + return + } + + if yOffset < 0 { + navBarVisibilityRatio = 1 + scrollView.contentInset.top = readerViewNavBarHeight + return + } + + if yOffset < readerViewNavBarHeight { + let isScrollingUp = yOffsetAtStartOfDrag ?? 0 > yOffset + navBarVisibilityRatio = isScrollingUp || yOffset < 0 ? 1 : min(1, 1 - (yOffset / readerViewNavBarHeight)) + scrollView.contentInset.top = navBarVisibilityRatio * readerViewNavBarHeight + return + } + + guard let yOffsetAtStartOfDrag = yOffsetAtStartOfDrag else { return } + + if yOffset > yOffsetAtStartOfDrag, !isNavBarHidden { + let translation = yOffset - yOffsetAtStartOfDrag + let ratio = translation < readerViewNavBarHeight ? 1 - (translation / readerViewNavBarHeight) : 0 + navBarVisibilityRatio = min(ratio, 1) + scrollView.contentInset.top = navBarVisibilityRatio * readerViewNavBarHeight + } + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + if decelerate, scrollView.contentOffset.y + scrollView.contentInset.top < (yOffsetAtStartOfDrag ?? 0) { + scrollView.contentInset.top = readerViewNavBarHeight + navBarVisibilityRatio = 1 + } + } + + func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { + scrollView.contentInset.top = readerViewNavBarHeight + navBarVisibilityRatio = 1 + return false + } + } +#endif diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderViewModel.swift new file mode 100644 index 000000000..d5d0a2717 --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderViewModel.swift @@ -0,0 +1,165 @@ +import Combine +import Models +import Services +import SwiftUI +import WebKit + +struct SafariWebLink: Identifiable { + let id: UUID + let url: URL +} + +final class WebReaderViewModel: ObservableObject { + @Published var isLoading = false + @Published var articleContent: ArticleContent? + + var subscriptions = Set() + + func loadContent(dataService: DataService, slug: String) { + isLoading = true + + guard let viewer = dataService.currentViewer else { return } + + dataService.articleContentPublisher(username: viewer.username, slug: slug).sink( + receiveCompletion: { [weak self] completion in + guard case .failure = completion else { return } + self?.isLoading = false + }, + receiveValue: { [weak self] articleContent in + self?.articleContent = articleContent + } + ) + .store(in: &subscriptions) + } + + func createHighlight( + messageBody: [String: Any], + replyHandler: @escaping WKScriptMessageReplyHandler, + dataService: DataService + ) { + dataService.createHighlightPublisher( + shortId: messageBody["shortId"] as? String ?? "", + highlightID: messageBody["id"] as? String ?? "", + quote: messageBody["quote"] as? String ?? "", + patch: messageBody["patch"] as? String ?? "", + articleId: messageBody["articleId"] as? String ?? "" + ) + .sink { completion in + guard case .failure = completion else { return } + replyHandler(["result": false], nil) + } receiveValue: { _ in + replyHandler(["result": true], nil) + } + .store(in: &subscriptions) + } + + func deleteHighlight( + messageBody: [String: Any], + replyHandler: @escaping WKScriptMessageReplyHandler, + dataService: DataService + ) { + dataService.deleteHighlightPublisher( + highlightId: messageBody["highlightId"] as? String ?? "" + ) + .sink { completion in + guard case .failure = completion else { return } + replyHandler(["result": false], nil) + } receiveValue: { _ in + replyHandler(["result": true], nil) + } + .store(in: &subscriptions) + } + + func mergeHighlight( + messageBody: [String: Any], + replyHandler: @escaping WKScriptMessageReplyHandler, + dataService: DataService + ) { + dataService.mergeHighlightPublisher( + shortId: messageBody["shortId"] as? String ?? "", + highlightID: messageBody["id"] as? String ?? "", + quote: messageBody["quote"] as? String ?? "", + patch: messageBody["patch"] as? String ?? "", + articleId: messageBody["articleId"] as? String ?? "", + overlapHighlightIdList: messageBody["overlapHighlightIdList"] as? [String] ?? [] + ) + .sink { completion in + guard case .failure = completion else { return } + replyHandler(["result": false], nil) + } receiveValue: { _ in + replyHandler(["result": true], nil) + } + .store(in: &subscriptions) + } + + func updateHighlight( + messageBody: [String: Any], + replyHandler: @escaping WKScriptMessageReplyHandler, + dataService: DataService + ) { + dataService.updateHighlightAttributesPublisher( + highlightID: messageBody["highlightId"] as? String ?? "", + annotation: messageBody["annotation"] as? String ?? "", + sharedAt: nil + ) + .sink { completion in + guard case .failure = completion else { return } + replyHandler(["result": false], nil) + } receiveValue: { _ in + replyHandler(["result": true], nil) + } + .store(in: &subscriptions) + } + + func updateReadingProgress( + messageBody: [String: Any], + replyHandler: @escaping WKScriptMessageReplyHandler, + dataService: DataService + ) { + let itemID = messageBody["id"] as? String + let readingProgress = messageBody["readingProgressPercent"] as? Double + let anchorIndex = messageBody["readingProgressAnchorIndex"] as? Int + + guard let itemID = itemID, let readingProgress = readingProgress, let anchorIndex = anchorIndex else { + replyHandler(["result": false], nil) + return + } + + dataService.updateArticleReadingProgressPublisher( + itemID: itemID, + readingProgress: readingProgress, + anchorIndex: anchorIndex + ) + .sink { completion in + guard case .failure = completion else { return } + replyHandler(["result": false], nil) + } receiveValue: { _ in + replyHandler(["result": true], nil) + } + .store(in: &subscriptions) + } + + func webViewActionWithReplyHandler( + message: WKScriptMessage, + replyHandler: @escaping WKScriptMessageReplyHandler, + dataService: DataService + ) { + guard let messageBody = message.body as? [String: Any] else { return } + guard let actionID = messageBody["actionID"] as? String else { return } + + switch actionID { + case "deleteHighlight": + deleteHighlight(messageBody: messageBody, replyHandler: replyHandler, dataService: dataService) + case "createHighlight": + createHighlight(messageBody: messageBody, replyHandler: replyHandler, dataService: dataService) + case "mergeHighlight": + mergeHighlight(messageBody: messageBody, replyHandler: replyHandler, dataService: dataService) + case "updateHighlight": + updateHighlight(messageBody: messageBody, replyHandler: replyHandler, dataService: dataService) + case "articleReadingProgress": + updateReadingProgress(messageBody: messageBody, replyHandler: replyHandler, dataService: dataService) + default: + replyHandler(nil, "Unknown actionID: \(actionID)") + } + } +} diff --git a/apple/OmnivoreKit/Sources/Models/AppEnvironment.swift b/apple/OmnivoreKit/Sources/Models/AppEnvironment.swift index 36186bed3..e49cb03f9 100644 --- a/apple/OmnivoreKit/Sources/Models/AppEnvironment.swift +++ b/apple/OmnivoreKit/Sources/Models/AppEnvironment.swift @@ -27,7 +27,11 @@ private let prodBaseURL = "https://api-prod.omnivore.app" private let devWebURL = "https://web-dev.omnivore.app" private let demoWebURL = "https://demo.omnivore.app" -private let prodWebURL = "https://web-prod.omnivore.app" +private let prodWebURL = "https://omnivore.app" + +private let devHighlightsServerURL = "https://highlights-dev.omnivore.app" +private let demoHighlightsServerURL = "https://highlights-demo.omnivore.app" +private let prodHighlightsServerURL = "https://highlights.omnivore.app" public extension AppEnvironment { var graphqlPath: String { @@ -59,4 +63,17 @@ public extension AppEnvironment { return URL(string: "http://localhost:3000")! } } + + var highlightsServerBaseURL: URL { + switch self { + case .dev: + return URL(string: devHighlightsServerURL)! + case .demo: + return URL(string: demoHighlightsServerURL)! + case .prod: + return URL(string: prodHighlightsServerURL)! + case .test, .local: + return URL(string: "http://localhost:8080")! + } + } } diff --git a/apple/OmnivoreKit/Sources/Models/ArticleContent.swift b/apple/OmnivoreKit/Sources/Models/ArticleContent.swift new file mode 100644 index 000000000..bc55cf570 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Models/ArticleContent.swift @@ -0,0 +1,20 @@ +import Foundation + +public struct ArticleContent { + public let htmlContent: String + public let highlights: [Highlight] + + public init( + htmlContent: String, + highlights: [Highlight] + ) { + self.htmlContent = htmlContent + self.highlights = highlights + } + + public var highlightsJSONString: String { + let jsonData = try? JSONEncoder().encode(highlights) + guard let jsonData = jsonData else { return "[]" } + return String(data: jsonData, encoding: .utf8) ?? "[]" + } +} diff --git a/apple/OmnivoreKit/Sources/Models/Highlight.swift b/apple/OmnivoreKit/Sources/Models/Highlight.swift index cbd0c8aa6..477c33352 100644 --- a/apple/OmnivoreKit/Sources/Models/Highlight.swift +++ b/apple/OmnivoreKit/Sources/Models/Highlight.swift @@ -1,6 +1,6 @@ import Foundation -public struct Highlight: Identifiable, Hashable { +public struct Highlight: Identifiable, Hashable, Codable { public let id: String public let shortId: String public let quote: String @@ -8,6 +8,9 @@ public struct Highlight: Identifiable, Hashable { public let suffix: String? public let patch: String public let annotation: String? + public let createdAt: Date? + public let updatedAt: Date? + public let createdByMe: Bool public init( id: String, @@ -16,7 +19,10 @@ public struct Highlight: Identifiable, Hashable { prefix: String?, suffix: String?, patch: String, - annotation: String? + annotation: String?, + createdByMe: Bool, + createdAt: Date? = nil, + updatedAt: Date? = nil ) { self.id = id self.shortId = shortId @@ -25,5 +31,8 @@ public struct Highlight: Identifiable, Hashable { self.suffix = suffix self.patch = patch self.annotation = annotation + self.createdAt = createdAt + self.updatedAt = updatedAt + self.createdByMe = createdByMe } } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateHighlight.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateHighlight.swift index 94cee5d07..dbeffb2e2 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateHighlight.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateHighlight.swift @@ -9,7 +9,8 @@ public extension DataService { highlightID: String, quote: String, patch: String, - articleId: String + articleId: String, + annotation: String? = nil ) -> AnyPublisher { enum MutationResult { case saved(id: String) @@ -28,7 +29,12 @@ public extension DataService { let mutation = Selection.Mutation { try $0.createHighlight( input: InputObjects.CreateHighlightInput( - id: highlightID, shortId: shortId, articleId: articleId, patch: patch, quote: quote + id: highlightID, + shortId: shortId, + articleId: articleId, + patch: patch, + quote: quote, + annotation: OptionalArgument(annotation) ), selection: selection ) diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift new file mode 100644 index 000000000..ab2b3cc60 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift @@ -0,0 +1,72 @@ +import Combine +import Foundation +import Models +import SwiftGraphQL + +// swiftlint:disable:next function_body_length +public extension DataService { + func articleContentPublisher(username: String, slug: String) -> AnyPublisher { + enum QueryResult { + case success(result: ArticleContent) + case error(error: String) + } + + let highlightSelection = Selection.Highlight { + Highlight( + id: try $0.id(), + shortId: try $0.shortId(), + quote: try $0.quote(), + prefix: try $0.prefix(), + suffix: try $0.suffix(), + patch: try $0.patch(), + annotation: try $0.annotation(), + createdByMe: try $0.createdByMe() + ) + } + + let articleSelection = Selection.Article { + ArticleContent( + htmlContent: try $0.content(), + highlights: try $0.highlights(selection: highlightSelection.list) + ) + } + + let selection = Selection { + try $0.on( + articleSuccess: .init { + QueryResult.success(result: try $0.article(selection: articleSelection)) + }, + articleError: .init { + QueryResult.error(error: try $0.errorCodes().description) + } + ) + } + + let query = Selection.Query { + try $0.article(username: username, slug: slug, selection: selection) + } + + let path = appEnvironment.graphqlPath + let headers = networker.defaultHeaders + + return Deferred { + Future { promise in + send(query, to: path, headers: headers) { result in + switch result { + case let .success(payload): + switch payload.data { + case let .success(result: result): + promise(.success(result)) + case .error: + promise(.failure(.unknown)) + } + case .failure: + promise(.failure(.unknown)) + } + } + } + } + .receive(on: DispatchQueue.main) + .eraseToAnyPublisher() + } +} diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/PDFHighlightsQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/PDFHighlightsQuery.swift index 6b0c0d215..9dcacd325 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Queries/PDFHighlightsQuery.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/PDFHighlightsQuery.swift @@ -18,7 +18,8 @@ public extension DataService { prefix: try $0.prefix(), suffix: try $0.suffix(), patch: try $0.patch(), - annotation: try $0.annotation() + annotation: try $0.annotation(), + createdByMe: try $0.createdByMe() ) } diff --git a/apple/OmnivoreKit/Sources/Utils/BundleExtensions.swift b/apple/OmnivoreKit/Sources/Utils/BundleExtensions.swift index 3c572d344..c449dca8a 100644 --- a/apple/OmnivoreKit/Sources/Utils/BundleExtensions.swift +++ b/apple/OmnivoreKit/Sources/Utils/BundleExtensions.swift @@ -12,3 +12,10 @@ public extension Bundle { #endif } } + +// Convenience for locating package resources externally +public enum UtilsPackage { + public static var bundleURL: URL { + Bundle.module.bundleURL + } +} diff --git a/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift b/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift index 67a1faf37..a06f1d9e8 100644 --- a/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift +++ b/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift @@ -15,4 +15,5 @@ public enum FeatureFlag { public static let enableShareButton = false public static let enableSnooze = false public static let showFeedItemTags = false + public static let useLocalWebView = true } diff --git a/apple/OmnivoreKit/Sources/Views/Article/HighlightAnnotationSheet.swift b/apple/OmnivoreKit/Sources/Views/Article/HighlightAnnotationSheet.swift index 59cb5ef7c..30dc1def4 100644 --- a/apple/OmnivoreKit/Sources/Views/Article/HighlightAnnotationSheet.swift +++ b/apple/OmnivoreKit/Sources/Views/Article/HighlightAnnotationSheet.swift @@ -1,13 +1,13 @@ import Introspect import SwiftUI -struct HighlightAnnotationSheet: View { +public struct HighlightAnnotationSheet: View { @Binding var annotation: String let onSave: () -> Void let onCancel: () -> Void - init( + public init( annotation: Binding, onSave: @escaping () -> Void, onCancel: @escaping () -> Void @@ -17,7 +17,7 @@ struct HighlightAnnotationSheet: View { self.onCancel = onCancel } - var body: some View { + public var body: some View { VStack { HStack { Button("Cancel", action: onCancel) diff --git a/apple/OmnivoreKit/Sources/Views/Article/WebAppView.swift b/apple/OmnivoreKit/Sources/Views/Article/WebAppView.swift index f493a9e8f..f485ed446 100644 --- a/apple/OmnivoreKit/Sources/Views/Article/WebAppView.swift +++ b/apple/OmnivoreKit/Sources/Views/Article/WebAppView.swift @@ -5,6 +5,27 @@ import WebKit public let readerViewNavBarHeight = 50.0 +enum WebViewConfigurationManager { + private static let processPool = WKProcessPool() + static func create() -> WKWebViewConfiguration { + let config = WKWebViewConfiguration() + config.processPool = processPool + // config.limitsNavigationsToAppBoundDomains = true + return config + } +} + +public enum WebViewManager { + public static let sharedView = create() + public static func shared() -> WebView { + sharedView + } + + public static func create() -> WebView { + WebView(frame: CGRect.zero, configuration: WebViewConfigurationManager.create()) + } +} + #if os(iOS) struct WebAppView: UIViewRepresentable { let request: URLRequest @@ -28,7 +49,7 @@ public let readerViewNavBarHeight = 50.0 } func makeUIView(context: Context) -> WKWebView { - let webView = WebView(frame: CGRect.zero) + let webView = WebViewManager.create() let contentController = WKUserContentController() webView.navigationDelegate = context.coordinator diff --git a/apple/OmnivoreKit/Sources/Views/Article/WebAppWrapperView.swift b/apple/OmnivoreKit/Sources/Views/Article/WebAppWrapperView.swift index 7c16c83a8..e6cba3544 100644 --- a/apple/OmnivoreKit/Sources/Views/Article/WebAppWrapperView.swift +++ b/apple/OmnivoreKit/Sources/Views/Article/WebAppWrapperView.swift @@ -114,21 +114,32 @@ public struct WebAppWrapperView: View { } #if os(iOS) - struct SafariView: UIViewControllerRepresentable { + public struct SafariView: UIViewControllerRepresentable { let url: URL - func makeUIViewController(context _: UIViewControllerRepresentableContext) -> SFSafariViewController { + public init(url: URL) { + self.url = url + } + + public func makeUIViewController( + context _: UIViewControllerRepresentableContext + ) -> SFSafariViewController { SFSafariViewController(url: url) } // swiftlint:disable:next line_length - func updateUIViewController(_: SFSafariViewController, context _: UIViewControllerRepresentableContext) {} + public func updateUIViewController(_: SFSafariViewController, context _: UIViewControllerRepresentableContext) {} } #elseif os(macOS) - struct SafariView: View { + public struct SafariView: View { let url: URL - var body: some View { + + public init(url: URL) { + self.url = url + } + + public var body: some View { Color.clear } } diff --git a/apple/OmnivoreKit/Sources/Views/Article/WebView.swift b/apple/OmnivoreKit/Sources/Views/Article/WebView.swift index 44931a57d..9744d2f9a 100644 --- a/apple/OmnivoreKit/Sources/Views/Article/WebView.swift +++ b/apple/OmnivoreKit/Sources/Views/Article/WebView.swift @@ -2,12 +2,12 @@ import WebKit /// Describes actions that can be sent from the WebView back to native views. /// The names on the javascript side must match for an action to be handled. -enum WebViewAction: String, CaseIterable { +public enum WebViewAction: String, CaseIterable { case highlightAction case readingProgressUpdate } -final class WebView: WKWebView { +public final class WebView: WKWebView { #if os(iOS) private var panGestureRecognizer: UIPanGestureRecognizer? private var tapGestureRecognizer: UITapGestureRecognizer? @@ -26,11 +26,11 @@ final class WebView: WKWebView { fatalError("init(coder:) has not been implemented") } - func increaseFontSize() { + public func increaseFontSize() { dispatchEvent("increaseFontSize") } - func decreaseFontSize() { + public func decreaseFontSize() { dispatchEvent("decreaseFontSize") } @@ -43,7 +43,7 @@ final class WebView: WKWebView { } #if os(iOS) - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) guard previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle else { return } @@ -81,7 +81,7 @@ final class WebView: WKWebView { setDefaultMenu() } - func userContentController(_: WKUserContentController, didReceive message: WKScriptMessage) { + public func userContentController(_: WKUserContentController, didReceive message: WKScriptMessage) { guard let messageBody = message.body as? [String: Any] else { return } guard let actionID = messageBody["actionID"] as? String else { return } @@ -115,7 +115,7 @@ final class WebView: WKWebView { UIMenuController.shared.menuItems = [remove, /* share, */ annotate] } - override var canBecomeFirstResponder: Bool { + override public var canBecomeFirstResponder: Bool { true } @@ -123,11 +123,12 @@ final class WebView: WKWebView { setDefaultMenu() } - func gestureRecognizer(_: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith _: UIGestureRecognizer) -> Bool { + // swiftlint:disable:next line_length + public func gestureRecognizer(_: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith _: UIGestureRecognizer) -> Bool { true } - override func canPerformAction(_ action: Selector, withSender _: Any?) -> Bool { + override public func canPerformAction(_ action: Selector, withSender _: Any?) -> Bool { switch action { case #selector(annotateSelection): return true case #selector(highlightSelection): return true @@ -163,7 +164,7 @@ final class WebView: WKWebView { hideMenu() } - @objc override func copy(_ sender: Any?) { + @objc override public func copy(_ sender: Any?) { super.copy(sender) dispatchEvent("copyHighlight") hideMenu() @@ -209,7 +210,7 @@ final class WebView: WKWebView { UIMenuController.shared.showMenu(from: self, rect: rect) } - func saveAnnotation(annotation: String) { + public func saveAnnotation(annotation: String) { // swiftlint:disable:next line_length let dispatch = "var event = new Event('saveAnnotation');event.annotation = '\(annotation)';document.dispatchEvent(event);" evaluateJavaScript(dispatch) { obj, err in diff --git a/apple/OmnivoreKit/Sources/Views/BundleFinder.swift b/apple/OmnivoreKit/Sources/Views/BundleFinder.swift new file mode 100644 index 000000000..883b67bb4 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/BundleFinder.swift @@ -0,0 +1,8 @@ +import Foundation + +// Convenience for locating package resources externally +public enum ViewsPackage { + public static var bundleURL: URL { + Bundle.module.bundleURL + } +} diff --git a/apple/OmnivoreKit/Sources/Views/CustomFontRegistration.swift b/apple/OmnivoreKit/Sources/Views/CustomFontRegistration.swift index d972bbe24..11af6c929 100644 --- a/apple/OmnivoreKit/Sources/Views/CustomFontRegistration.swift +++ b/apple/OmnivoreKit/Sources/Views/CustomFontRegistration.swift @@ -3,15 +3,16 @@ import SwiftUI @discardableResult public func registerFonts() -> Bool { [ - registerFont(bundle: .module, fontName: "Inter-Black", fontExtension: "ttf"), - registerFont(bundle: .module, fontName: "Inter-ExtraBold", fontExtension: "ttf"), - registerFont(bundle: .module, fontName: "Inter-Bold", fontExtension: "ttf"), - registerFont(bundle: .module, fontName: "Inter-SemiBold", fontExtension: "ttf"), - registerFont(bundle: .module, fontName: "Inter-Medium", fontExtension: "ttf"), - registerFont(bundle: .module, fontName: "Inter-Regular", fontExtension: "ttf"), - registerFont(bundle: .module, fontName: "Inter-Light", fontExtension: "ttf"), - registerFont(bundle: .module, fontName: "Inter-ExtraLight", fontExtension: "ttf"), - registerFont(bundle: .module, fontName: "Inter-Thin", fontExtension: "ttf") + registerFont(bundle: .module, fontName: "Inter-Black-900", fontExtension: "ttf"), + registerFont(bundle: .module, fontName: "Inter-ExtraBold-800", fontExtension: "ttf"), + registerFont(bundle: .module, fontName: "Inter-Bold-700", fontExtension: "ttf"), + registerFont(bundle: .module, fontName: "Inter-SemiBold-600", fontExtension: "ttf"), + registerFont(bundle: .module, fontName: "Inter-Medium-500", fontExtension: "ttf"), + registerFont(bundle: .module, fontName: "Inter-Regular-400", fontExtension: "ttf"), + registerFont(bundle: .module, fontName: "Inter-Light-300", fontExtension: "ttf"), + registerFont(bundle: .module, fontName: "Inter-ExtraLight-200", fontExtension: "ttf"), + registerFont(bundle: .module, fontName: "Inter-Thin", fontExtension: "ttf"), + registerFont(bundle: .module, fontName: "SFMonoRegular", fontExtension: "otf") ] .allSatisfy { $0 } } diff --git a/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Black.ttf b/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Black-900.ttf similarity index 100% rename from apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Black.ttf rename to apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Black-900.ttf diff --git a/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Bold.ttf b/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Bold-700.ttf similarity index 100% rename from apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Bold.ttf rename to apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Bold-700.ttf diff --git a/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-ExtraBold.ttf b/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-ExtraBold-800.ttf similarity index 100% rename from apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-ExtraBold.ttf rename to apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-ExtraBold-800.ttf diff --git a/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-ExtraLight.ttf b/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-ExtraLight-200.ttf similarity index 100% rename from apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-ExtraLight.ttf rename to apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-ExtraLight-200.ttf diff --git a/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Light.ttf b/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Light-300.ttf similarity index 100% rename from apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Light.ttf rename to apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Light-300.ttf diff --git a/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Medium.ttf b/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Medium-500.ttf similarity index 100% rename from apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Medium.ttf rename to apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Medium-500.ttf diff --git a/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Regular.ttf b/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Regular-400.ttf similarity index 100% rename from apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Regular.ttf rename to apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-Regular-400.ttf diff --git a/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-SemiBold.ttf b/apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-SemiBold-600.ttf similarity index 100% rename from apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-SemiBold.ttf rename to apple/OmnivoreKit/Sources/Views/Resources/Fonts/Inter-SemiBold-600.ttf diff --git a/apple/OmnivoreKit/Sources/Views/Resources/Fonts/SFMonoRegular.otf b/apple/OmnivoreKit/Sources/Views/Resources/Fonts/SFMonoRegular.otf new file mode 100644 index 000000000..4c6f9f690 Binary files /dev/null and b/apple/OmnivoreKit/Sources/Views/Resources/Fonts/SFMonoRegular.otf differ diff --git a/apple/OmnivoreKit/Sources/Views/Resources/bundle.js b/apple/OmnivoreKit/Sources/Views/Resources/bundle.js new file mode 100644 index 000000000..4335743ca --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/Resources/bundle.js @@ -0,0 +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]"],u=ArrayBuffer.isView||function(e){return e&&l.indexOf(Object.prototype.toString.call(e))>-1};function s(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 h(e){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}function p(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=p(t);return t.readAsArrayBuffer(e),n}function v(e){if(e.slice)return e.slice(0);var t=new Uint8Array(e.byteLength);return t.set(new Uint8Array(e)),t.buffer}function m(){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=v(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):a&&(ArrayBuffer.prototype.isPrototypeOf(e)||u(e))?this._bodyArrayBuffer=v(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=h(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?h(this)||Promise.resolve(this._bodyArrayBuffer):this.blob().then(g)}),this.text=function(){var e,t,n,r=h(this);if(r)return r;if(this._bodyBlob)return e=this._bodyBlob,n=p(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})},m.call(b.prototype),m.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 _=[301,302,303,307,308];x.redirect=function(e,t){if(-1===_.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 u(){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",u),l.onreadystatechange=function(){4===l.readyState&&a.signal.removeEventListener("abort",u)}),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},3139:(e,t,n)=>{"use strict";n.d(t,{Z:()=>l});var r=n(9601),o=n.n(r),i=n(2609),a=n.n(i)()(o());a.push([e.id,".article-inner-css * {\n max-width: 100%;\n}\n\n:root {\n color-scheme: light dark;\n}\n\n.highlight {\n color: var(--colors-highlightText);\n background-color: var(--colors-highlightBackground);\n cursor: pointer;\n}\n\n.highlight_with_note {\n color: var(--colors-highlightText);\n border-bottom: 2px var(--colors-highlightBackground) solid;\n border-radius: 2px;\n cursor: pointer;\n}\n\n.article-inner-css .highlight_with_note .highlight_note_button {\n display: unset !important;\n margin: 0px !important;\n max-width: unset !important;\n height: unset !important;\n padding: 0px 8px;\n cursor: pointer;\n}\n/* \non smaller screens we display the note icon\n@media (max-width: 768px) {\n .highlight_with_note.last_element:after { \n content: url(/static/icons/highlight-note-icon.svg);\n padding: 0 3px;\n cursor: pointer;\n }\n} */\n\n.article-inner-css h1,\n.article-inner-css h2,\n.article-inner-css h3,\n.article-inner-css h4,\n.article-inner-css h5,\n.article-inner-css h6 {\n margin-block-start: 0.83em;\n margin-block-end: 0.83em;\n margin-inline-start: 0px;\n margin-inline-end: 0px;\n line-height: var(--line-height);\n font-size: var(--text-font-size);\n color: var(--headers-color);\n font-weight: bold;\n}\n.article-inner-css h1 {\n font-size: 1.5em !important;\n line-height: 1.4em !important;\n}\n.article-inner-css h2 {\n font-size: 1.43em !important;\n}\n.article-inner-css h3 {\n font-size: 1.25em !important;\n}\n.article-inner-css h4,\n.article-inner-css h5,\n.article-inner-css h6 {\n font-size: 1em !important;\n margin: 1em 0 !important;\n}\n\n.article-inner-css .scrollable {\n overflow: auto;\n}\n\n.article-inner-css div {\n line-height: var(--line-height);\n /* font-size: var(--text-font-size); */\n color: var(--font-color);\n}\n\n.article-inner-css p {\n font-family: var(--text-font-family);\n font-style: normal;\n font-weight: normal;\n\n color: var(--font-color);\n\n display: block;\n margin-block-start: 1em;\n margin-block-end: 1em;\n margin-inline-start: 0px;\n margin-inline-end: 0px;\n\n > img {\n display: block;\n margin: 0.5em auto !important;\n max-width: 100% !important;\n }\n\n > iframe {\n width: 100%;\n height: 350px;\n }\n}\n\n.article-inner-css section {\n line-height: 1.65em;\n font-size: var(--text-font-size);\n}\n\n.article-inner-css blockquote {\n display: block;\n border-left: 1px solid var(--font-color-transparent);\n padding-left: 16px;\n font-style: italic;\n margin-inline-start: 0px;\n > * {\n font-style: italic;\n }\n p:last-of-type {\n margin-bottom: 0;\n }\n}\n\n.article-inner-css a {\n color: var(--font-color-readerFont);\n}\n\n.article-inner-css .highlight a {\n color: var(--colors-highlightText);\n}\n\n.article-inner-css figure {\n * {\n color: var(--font-color-transparent);\n }\n\n margin: 30px 0;\n font-size: 0.75em;\n line-height: 1.5em;\n\n figcaption {\n color: var(--font-color);\n opacity: 0.7;\n margin: 10px 20px 10px 0;\n }\n\n figcaption * {\n color: var(--font-color);\n opacity: 0.7;\n }\n figcaption a {\n color: var(--font-color);\n /* margin: 10px 20px 10px 0; */\n opacity: 0.6;\n }\n\n > div {\n max-width: 100%;\n }\n}\n\n.article-inner-css figure[aria-label='media'] {\n margin: var(--figure-margin);\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n.article-inner-css hr {\n margin-bottom: var(--hr-margin);\n border: none;\n}\n\n.article-inner-css table {\n display: block;\n word-break: normal;\n white-space: nowrap;\n border: 1px solid rgb(216, 216, 216);\n border-spacing: 0;\n border-collapse: collapse;\n font-size: 0.8rem;\n margin: auto;\n line-height: 1.5em;\n font-size: 0.9em;\n max-width: -moz-fit-content;\n max-width: fit-content;\n margin: 0 auto;\n overflow-x: auto;\n caption {\n margin: 0.5em 0;\n }\n th {\n border: 1px solid rgb(216, 216, 216);\n background-color: var(--table-header-color);\n padding: 5px;\n }\n td {\n border: 1px solid rgb(216, 216, 216);\n padding: 0.5em;\n padding: 5px;\n }\n\n p {\n margin: 0;\n padding: 0;\n }\n\n img {\n display: block;\n margin: 0.5em auto !important;\n max-width: 100% !important;\n }\n}\n\n.article-inner-css ul,\n.article-inner-css ol {\n margin-block-start: 1em;\n margin-block-end: 1em;\n margin-inline-start: 0px;\n margin-inline-end: 0px;\n padding-inline-start: 40px;\n margin-top: 18px;\n\n font-family: var(--text-font-family);\n font-style: normal;\n font-weight: normal;\n\n line-height: var(--line-height);\n font-size: var(--text-font-size);\n\n color: var(--font-color);\n}\n\n.article-inner-css li {\n word-break: break-word;\n ol,\n ul {\n margin: 0;\n }\n}\n\n.article-inner-css sup,\n.article-inner-css sub {\n position: relative;\n a {\n color: inherit;\n pointer-events: none;\n }\n}\n\n.article-inner-css sup {\n top: -0.3em;\n}\n\n.article-inner-css sub {\n bottom: 0.3em;\n}\n\n.article-inner-css cite {\n font-style: normal;\n}\n\n.article-inner-css .page {\n width: 100%;\n}\n\n/* Collapse excess whitespace. */\n.page p > p:empty,\n.page div > p:empty,\n.page p > div:empty,\n.page div > div:empty,\n.page p + br,\n.page p > br:only-child,\n.page div > br:only-child,\n.page img + br {\n display: none;\n}\n\n.article-inner-css video {\n max-width: 100%;\n}\n\n.article-inner-css dl {\n display: block;\n margin-block-start: 1em;\n margin-block-end: 1em;\n margin-inline-start: 0px;\n margin-inline-end: 0px;\n}\n\n.article-inner-css dd {\n display: block;\n margin-inline-start: 40px;\n}\n\n.article-inner-css pre,\n.article-inner-css code {\n vertical-align: bottom;\n word-wrap: initial;\n font-family: 'SF Mono', monospace !important;\n white-space: pre;\n direction: ltr;\n unicode-bidi: embed;\n color: var(--font-color);\n max-width: -moz-fit-content;\n margin: 0;\n overflow-x: auto;\n word-wrap: normal;\n border-radius: 4px;\n}\n\n.article-inner-css img {\n display: block;\n margin: 0.5em auto !important;\n max-width: 100% !important;\n height: auto;\n}\n\n.article-inner-css .page {\n text-align: start;\n word-wrap: break-word;\n\n font-size: var(--text-font-size);\n line-height: var(--line-height);\n}\n\n.article-inner-css .omnivore-instagram-embed {\n img {\n margin: 0 !important;\n }\n}\n\n\n",""]);const l=a},8936:(e,t,n)=>{"use strict";n.d(t,{Z:()=>l});var r=n(9601),o=n.n(r),i=n(2609),a=n.n(i)()(o());a.push([e.id,"html,\nbody {\n padding: 0;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,\n Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n line-height: 1.6;\n font-size: 18px;\n}\n\n.disable-webkit-callout {\n -webkit-touch-callout: none;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\ndiv#appleid-signin {\n min-width: 300px;\n min-height: 40px;\n}\n\n@font-face {\n font-family: 'Inter';\n font-weight: 200;\n font-style: normal;\n src: url(Inter-ExtraLight-200.ttf);\n}\n\n@font-face {\n font-family: 'Inter';\n font-weight: 300;\n font-style: normal;\n src: url(Inter-Light-300.ttf);\n}\n\n@font-face {\n font-family: 'Inter';\n font-weight: 400;\n font-style: normal;\n src: url(Inter-Regular-400.ttf);\n}\n\n@font-face {\n font-family: 'Inter';\n font-weight: 500;\n font-style: normal;\n src: url(Inter-Medium-500.ttf);\n}\n\n@font-face {\n font-family: 'Inter';\n font-weight: 600;\n font-style: normal;\n src: url(Inter-SemiBold-600.ttf);\n}\n\n@font-face {\n font-family: 'Inter';\n font-weight: 700;\n font-style: normal;\n src: url(Inter-Bold-700.ttf);\n}\n\n@font-face {\n font-family: 'Inter';\n font-weight: 800;\n font-style: normal;\n src: url(Inter-ExtraBold-800.ttf);\n}\n\n@font-face {\n font-family: 'Inter';\n font-weight: 900;\n font-style: normal;\n src: url(Inter-Black-900.ttf);\n}\n\n@font-face {\n font-family: 'SF Mono';\n font-weight: 400;\n font-style: normal;\n src: url(SFMonoRegular.otf);\n}\n\n.dropdown-arrow {\n display: inline-block;\n width: 0;\n height: 0;\n vertical-align: middle;\n border-style: solid;\n border-width: 4px 4px 0;\n border-right-color: transparent;\n border-bottom-color: transparent;\n border-left-color: transparent;\n}\n",""]);const l=a},2609:e=>{"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n="",r=void 0!==t[5];return t[4]&&(n+="@supports (".concat(t[4],") {")),t[2]&&(n+="@media ".concat(t[2]," {")),r&&(n+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),n+=e(t),r&&(n+="}"),t[2]&&(n+="}"),t[4]&&(n+="}"),n})).join("")},t.i=function(e,n,r,o,i){"string"==typeof e&&(e=[[null,e,void 0]]);var a={};if(r)for(var l=0;l0?" ".concat(c[5]):""," {").concat(c[1],"}")),c[5]=i),n&&(c[2]?(c[1]="@media ".concat(c[2]," {").concat(c[1],"}"),c[2]=n):c[2]=n),o&&(c[4]?(c[1]="@supports (".concat(c[4],") {").concat(c[1],"}"),c[4]=o):c[4]="".concat(o)),t.push(c))}},t}},9601:e=>{"use strict";e.exports=function(e){return e[1]}},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),u=e.substring(0,l);e=e.substring(l),n=n.substring(l),l=this.diff_commonSuffix(e,n);var s=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 u&&c.unshift(new t.Diff(0,u)),s&&c.push(new t.Diff(0,s)),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,u=e.length>r.length?r:e,s=l.indexOf(u);if(-1!=s)return a=[new t.Diff(1,l.substring(0,s)),new t.Diff(0,u),new t.Diff(1,l.substring(s+u.length))],e.length>r.length&&(a[0][0]=a[2][0]=n),a;if(1==u.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],h=c[2],p=c[3],g=c[4],v=this.diff_main(f,h,o,i),m=this.diff_main(d,p,o,i);return v.concat([new t.Diff(0,g)],m)}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 u=0,s=0,c=0,f="",d="";u=1&&c>=1){l.splice(u-s-c,s+c),u=u-s-c;for(var h=this.diff_main(f,d,!1,o),p=h.length-1;p>=0;p--)l.splice(u,0,h[p]);u+=h.length}c=0,s=0,f="",d=""}u++}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),u=l,s=2*l,c=new Array(s),f=new Array(s),d=0;do);b++){for(var w=-b+g;w<=b-v;w+=2){for(var x=u+w,_=(O=w==-b||w!=b&&c[x-1]i)v+=2;else if(_>a)g+=2;else if(p&&(k=u+h-w)>=0&&k=(S=i-f[k]))return this.diff_bisectSplit_(e,r,O,_,o)}for(var E=-b+m;E<=b-y;E+=2){for(var S,k=u+E,C=(S=E==-b||E!=b&&f[k-1]i)y+=2;else if(C>a)m+=2;else if(!p){var O;if((x=u+h-E)>=0&&x=(S=i-S))return this.diff_bisectSplit_(e,r,O,_,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),u=t.substring(r),s=this.diff_main(i,a,!1,o),c=this.diff_main(l,u,!1,o);return s.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,u,s,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],u=a[1],s=a[2],c=a[3]):(s=a[0],c=a[1],l=a[2],u=a[3]),[l,u,s,c,a[4]]):null},t.prototype.diff_cleanupSemantic=function(e){for(var r=!1,o=[],i=0,a=null,l=0,u=0,s=0,c=0,f=0;l0?o[i-1]:-1,u=0,s=0,c=0,f=0,a=null,r=!0)),l++;for(r&&this.diff_cleanupMerge(e),this.diff_cleanupSemanticLossless(e),l=1;l=g?(p>=d.length/2||p>=h.length/2)&&(e.splice(l,0,new t.Diff(0,h.substring(0,p))),e[l-1][1]=d.substring(0,d.length-p),e[l+1][1]=h.substring(p),l++):(g>=d.length/2||g>=h.length/2)&&(e.splice(l,0,new t.Diff(0,d.substring(0,g))),e[l-1][0]=1,e[l-1][1]=h.substring(0,h.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_),u=a&&o.match(t.whitespaceRegex_),s=l&&r.match(t.linebreakRegex_),c=u&&o.match(t.linebreakRegex_),f=s&&e.match(t.blanklineEndRegex_),d=c&&n.match(t.blanklineStartRegex_);return f||d?5:s||c?4:i&&!l&&u?3:l||u?2:i||a?1:0}for(var r=1;r=d&&(d=h,s=o,c=i,f=a)}e[r-1][1]!=s&&(s?e[r-1][1]=s:(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,u=!1,s=!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="",u="";o1?(0!==i&&0!==a&&(0!==(r=this.diff_commonPrefix(u,l))&&(o-i-a>0&&0==e[o-i-a-1][0]?e[o-i-a-1][1]+=u.substring(0,r):(e.splice(0,0,new t.Diff(0,u.substring(0,r))),o++),u=u.substring(r),l=l.substring(r)),0!==(r=this.diff_commonSuffix(u,l))&&(e[o][1]=u.substring(u.length-r)+e[o][1],u=u.substring(0,u.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++),u.length&&(e.splice(o,0,new t.Diff(1,u)),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="",u=""}""===e[e.length-1][1]&&e.pop();var s=!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(u){case 1:t[l]=''+s+"";break;case n:t[l]=''+s+"";break;case 0:t[l]=""+s+""}}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 u,s,c=1<=p;m--){var y=r[e.charAt(m-1)];if(v[m]=0===h?(v[m+1]<<1|1)&y:(v[m+1]<<1|1)&y|(f[m+1]|f[m])<<1|1|f[m+1],v[m]&c){var b=i(h,m-1);if(b<=a){if(a=b,!((l=m-1)>n))break;p=Math.max(1,2*n-l)}}}if(i(h+1,n)>a)break;f=v}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=[],u=new t.patch_obj,s=0,c=0,f=0,d=i,h=i,p=0;p=2*this.Patch_Margin&&s&&(this.patch_addContext_(u,d),l.push(u),u=new t.patch_obj,s=0,d=h,c=f)}1!==g&&(c+=v.length),g!==n&&(f+=v.length)}return s&&(this.patch_addContext_(u,d),l.push(u)),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),s))&&(-1==(f=this.match_main(t,c.substring(c.length-this.Match_MaxBits),s+c.length-this.Match_MaxBits))||l>=f)&&(l=-1):l=this.match_main(t,c,s),-1==l)i[a]=!1,o-=e[a].length2-e[a].length1;else if(i[a]=!0,o=l-s,c==(u=-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,u,!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 h,p=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?(s.length1+=d.length,a+=d.length,c=!1,s.diffs.push(new t.Diff(f,d)),i.diffs.shift()):(d=d.substring(0,r-s.length1-this.Patch_Margin),s.length1+=d.length,a+=d.length,0===f?(s.length2+=d.length,l+=d.length):c=!1,s.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))}u=(u=this.diff_text2(s.diffs)).substring(u.length-this.Patch_Margin);var h=this.diff_text1(i.diffs).substring(0,this.Patch_Margin);""!==h&&(s.length1+=h.length,s.length2+=h.length,0!==s.diffs.length&&0===s.diffs[s.diffs.length-1][0]?s.diffs[s.diffs.length-1][1]+=h:s.diffs.push(new t.Diff(0,h))),c||e.splice(++o,0,s)}}},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 u=n?n+".":"";if("undefined"!=typeof FileList&&t instanceof FileList)i=Array.prototype.map.call(t,(function(e,t){return l([""+u+t],e),null}));else if(Array.isArray(t))i=t.map((function(t,n){var r=e(t,""+u+n,o);return r.files.forEach(l),r.clone}));else if(t&&t.constructor===Object)for(var s in i={},t){var c=e(t[s],""+u+s,o);c.files.forEach(l),i[s]=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},1170:function(e,t){var n,r;void 0===(r="function"==typeof(n=function(){var e=function(){},t={},n={},r={};function o(e,t){if(e){var o=r[e];if(n[e]=t,o)for(;o.length;)o[0](e,t),o.splice(0,1)}}function i(t,n){t.call&&(t={success:t}),n.length?(t.error||e)(n):(t.success||e)(t)}function a(t,n,r,o){var i,l,u=document,s=r.async,c=(r.numRetries||0)+1,f=r.before||e,d=t.replace(/[\?|#].*$/,""),h=t.replace(/^(css|img)!/,"");o=o||0,/(^css!|\.css$)/.test(d)?((l=u.createElement("link")).rel="stylesheet",l.href=h,(i="hideFocus"in l)&&l.relList&&(i=0,l.rel="preload",l.as="style")):/(^img!|\.(png|gif|jpg|svg|webp)$)/.test(d)?(l=u.createElement("img")).src=h:((l=u.createElement("script")).src=t,l.async=void 0===s||s),l.onload=l.onerror=l.onbeforeload=function(e){var u=e.type[0];if(i)try{l.sheet.cssText.length||(u="e")}catch(e){18!=e.code&&(u="e")}if("e"==u){if((o+=1)"']/g,K=RegExp(V.source),X=RegExp(q.source),G=/<%-([\s\S]+?)%>/g,Y=/<%([\s\S]+?)%>/g,Q=/<%=([\s\S]+?)%>/g,Z=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,J=/^\w*$/,ee=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,te=/[\\^$.*+?()[\]{}|]/g,ne=RegExp(te.source),re=/^\s+/,oe=/\s/,ie=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,ae=/\{\n\/\* \[wrapped with (.+)\] \*/,le=/,? & /,ue=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,se=/[()=,{}\[\]\/\s]/,ce=/\\(\\)?/g,fe=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,de=/\w*$/,he=/^[-+]0x[0-9a-f]+$/i,pe=/^0b[01]+$/i,ge=/^\[object .+?Constructor\]$/,ve=/^0o[0-7]+$/i,me=/^(?:0|[1-9]\d*)$/,ye=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,be=/($^)/,we=/['\n\r\u2028\u2029\\]/g,xe="\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff",_e="a-z\\xdf-\\xf6\\xf8-\\xff",Ee="A-Z\\xc0-\\xd6\\xd8-\\xde",Se="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",ke="["+Se+"]",Ce="["+xe+"]",Oe="\\d+",Pe="["+_e+"]",Te="[^\\ud800-\\udfff"+Se+Oe+"\\u2700-\\u27bf"+_e+Ee+"]",Re="\\ud83c[\\udffb-\\udfff]",je="[^\\ud800-\\udfff]",Ae="(?:\\ud83c[\\udde6-\\uddff]){2}",Le="[\\ud800-\\udbff][\\udc00-\\udfff]",Ie="["+Ee+"]",De="(?:"+Pe+"|"+Te+")",Ne="(?:"+Ie+"|"+Te+")",Me="(?:['’](?:d|ll|m|re|s|t|ve))?",ze="(?:['’](?:D|LL|M|RE|S|T|VE))?",Fe="(?:"+Ce+"|"+Re+")?",$e="[\\ufe0e\\ufe0f]?",Ue=$e+Fe+"(?:\\u200d(?:"+[je,Ae,Le].join("|")+")"+$e+Fe+")*",Be="(?:"+["[\\u2700-\\u27bf]",Ae,Le].join("|")+")"+Ue,He="(?:"+[je+Ce+"?",Ce,Ae,Le,"[\\ud800-\\udfff]"].join("|")+")",We=RegExp("['’]","g"),Ve=RegExp(Ce,"g"),qe=RegExp(Re+"(?="+Re+")|"+He+Ue,"g"),Ke=RegExp([Ie+"?"+Pe+"+"+Me+"(?="+[ke,Ie,"$"].join("|")+")",Ne+"+"+ze+"(?="+[ke,Ie+De,"$"].join("|")+")",Ie+"?"+De+"+"+Me,Ie+"+"+ze,"\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",Oe,Be].join("|"),"g"),Xe=RegExp("[\\u200d\\ud800-\\udfff"+xe+"\\ufe0e\\ufe0f]"),Ge=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,Ye=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],Qe=-1,Ze={};Ze[L]=Ze[I]=Ze[D]=Ze[N]=Ze[M]=Ze[z]=Ze[F]=Ze[$]=Ze[U]=!0,Ze[g]=Ze[v]=Ze[j]=Ze[m]=Ze[A]=Ze[y]=Ze[b]=Ze[w]=Ze[_]=Ze[E]=Ze[S]=Ze[C]=Ze[O]=Ze[P]=Ze[R]=!1;var Je={};Je[g]=Je[v]=Je[j]=Je[A]=Je[m]=Je[y]=Je[L]=Je[I]=Je[D]=Je[N]=Je[M]=Je[_]=Je[E]=Je[S]=Je[C]=Je[O]=Je[P]=Je[T]=Je[z]=Je[F]=Je[$]=Je[U]=!0,Je[b]=Je[w]=Je[R]=!1;var et={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},tt=parseFloat,nt=parseInt,rt="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,ot="object"==typeof self&&self&&self.Object===Object&&self,it=rt||ot||Function("return this")(),at=t&&!t.nodeType&&t,lt=at&&e&&!e.nodeType&&e,ut=lt&<.exports===at,st=ut&&rt.process,ct=function(){try{return lt&<.require&<.require("util").types||st&&st.binding&&st.binding("util")}catch(e){}}(),ft=ct&&ct.isArrayBuffer,dt=ct&&ct.isDate,ht=ct&&ct.isMap,pt=ct&&ct.isRegExp,gt=ct&&ct.isSet,vt=ct&&ct.isTypedArray;function mt(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}function yt(e,t,n,r){for(var o=-1,i=null==e?0:e.length;++o-1}function St(e,t,n){for(var r=-1,o=null==e?0:e.length;++r-1;);return n}function Kt(e,t){for(var n=e.length;n--&&Lt(t,e[n],0)>-1;);return n}function Xt(e,t){for(var n=e.length,r=0;n--;)e[n]===t&&++r;return r}var Gt=zt({À:"A",Á:"A",Â:"A",Ã:"A",Ä:"A",Å:"A",à:"a",á:"a",â:"a",ã:"a",ä:"a",å:"a",Ç:"C",ç:"c",Ð:"D",ð:"d",È:"E",É:"E",Ê:"E",Ë:"E",è:"e",é:"e",ê:"e",ë:"e",Ì:"I",Í:"I",Î:"I",Ï:"I",ì:"i",í:"i",î:"i",ï:"i",Ñ:"N",ñ:"n",Ò:"O",Ó:"O",Ô:"O",Õ:"O",Ö:"O",Ø:"O",ò:"o",ó:"o",ô:"o",õ:"o",ö:"o",ø:"o",Ù:"U",Ú:"U",Û:"U",Ü:"U",ù:"u",ú:"u",û:"u",ü:"u",Ý:"Y",ý:"y",ÿ:"y",Æ:"Ae",æ:"ae",Þ:"Th",þ:"th",ß:"ss",Ā:"A",Ă:"A",Ą:"A",ā:"a",ă:"a",ą:"a",Ć:"C",Ĉ:"C",Ċ:"C",Č:"C",ć:"c",ĉ:"c",ċ:"c",č:"c",Ď:"D",Đ:"D",ď:"d",đ:"d",Ē:"E",Ĕ:"E",Ė:"E",Ę:"E",Ě:"E",ē:"e",ĕ:"e",ė:"e",ę:"e",ě:"e",Ĝ:"G",Ğ:"G",Ġ:"G",Ģ:"G",ĝ:"g",ğ:"g",ġ:"g",ģ:"g",Ĥ:"H",Ħ:"H",ĥ:"h",ħ:"h",Ĩ:"I",Ī:"I",Ĭ:"I",Į:"I",İ:"I",ĩ:"i",ī:"i",ĭ:"i",į:"i",ı:"i",Ĵ:"J",ĵ:"j",Ķ:"K",ķ:"k",ĸ:"k",Ĺ:"L",Ļ:"L",Ľ:"L",Ŀ:"L",Ł:"L",ĺ:"l",ļ:"l",ľ:"l",ŀ:"l",ł:"l",Ń:"N",Ņ:"N",Ň:"N",Ŋ:"N",ń:"n",ņ:"n",ň:"n",ŋ:"n",Ō:"O",Ŏ:"O",Ő:"O",ō:"o",ŏ:"o",ő:"o",Ŕ:"R",Ŗ:"R",Ř:"R",ŕ:"r",ŗ:"r",ř:"r",Ś:"S",Ŝ:"S",Ş:"S",Š:"S",ś:"s",ŝ:"s",ş:"s",š:"s",Ţ:"T",Ť:"T",Ŧ:"T",ţ:"t",ť:"t",ŧ:"t",Ũ:"U",Ū:"U",Ŭ:"U",Ů:"U",Ű:"U",Ų:"U",ũ:"u",ū:"u",ŭ:"u",ů:"u",ű:"u",ų:"u",Ŵ:"W",ŵ:"w",Ŷ:"Y",ŷ:"y",Ÿ:"Y",Ź:"Z",Ż:"Z",Ž:"Z",ź:"z",ż:"z",ž:"z",IJ:"IJ",ij:"ij",Œ:"Oe",œ:"oe",ʼn:"'n",ſ:"s"}),Yt=zt({"&":"&","<":"<",">":">",'"':""","'":"'"});function Qt(e){return"\\"+et[e]}function Zt(e){return Xe.test(e)}function Jt(e){var t=-1,n=Array(e.size);return e.forEach((function(e,r){n[++t]=[r,e]})),n}function en(e,t){return function(n){return e(t(n))}}function tn(e,t){for(var n=-1,r=e.length,o=0,i=[];++n",""":'"',"'":"'"}),sn=function e(t){var n,r=(t=null==t?it:sn.defaults(it.Object(),t,sn.pick(it,Ye))).Array,oe=t.Date,xe=t.Error,_e=t.Function,Ee=t.Math,Se=t.Object,ke=t.RegExp,Ce=t.String,Oe=t.TypeError,Pe=r.prototype,Te=_e.prototype,Re=Se.prototype,je=t["__core-js_shared__"],Ae=Te.toString,Le=Re.hasOwnProperty,Ie=0,De=(n=/[^.]+$/.exec(je&&je.keys&&je.keys.IE_PROTO||""))?"Symbol(src)_1."+n:"",Ne=Re.toString,Me=Ae.call(Se),ze=it._,Fe=ke("^"+Ae.call(Le).replace(te,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),$e=ut?t.Buffer:o,Ue=t.Symbol,Be=t.Uint8Array,He=$e?$e.allocUnsafe:o,qe=en(Se.getPrototypeOf,Se),Xe=Se.create,et=Re.propertyIsEnumerable,rt=Pe.splice,ot=Ue?Ue.isConcatSpreadable:o,at=Ue?Ue.iterator:o,lt=Ue?Ue.toStringTag:o,st=function(){try{var e=ci(Se,"defineProperty");return e({},"",{}),e}catch(e){}}(),ct=t.clearTimeout!==it.clearTimeout&&t.clearTimeout,Rt=oe&&oe.now!==it.Date.now&&oe.now,zt=t.setTimeout!==it.setTimeout&&t.setTimeout,cn=Ee.ceil,fn=Ee.floor,dn=Se.getOwnPropertySymbols,hn=$e?$e.isBuffer:o,pn=t.isFinite,gn=Pe.join,vn=en(Se.keys,Se),mn=Ee.max,yn=Ee.min,bn=oe.now,wn=t.parseInt,xn=Ee.random,_n=Pe.reverse,En=ci(t,"DataView"),Sn=ci(t,"Map"),kn=ci(t,"Promise"),Cn=ci(t,"Set"),On=ci(t,"WeakMap"),Pn=ci(Se,"create"),Tn=On&&new On,Rn={},jn=Fi(En),An=Fi(Sn),Ln=Fi(kn),In=Fi(Cn),Dn=Fi(On),Nn=Ue?Ue.prototype:o,Mn=Nn?Nn.valueOf:o,zn=Nn?Nn.toString:o;function Fn(e){if(nl(e)&&!Va(e)&&!(e instanceof Hn)){if(e instanceof Bn)return e;if(Le.call(e,"__wrapped__"))return $i(e)}return new Bn(e)}var $n=function(){function e(){}return function(t){if(!tl(t))return{};if(Xe)return Xe(t);e.prototype=t;var n=new e;return e.prototype=o,n}}();function Un(){}function Bn(e,t){this.__wrapped__=e,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=o}function Hn(e){this.__wrapped__=e,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=h,this.__views__=[]}function Wn(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t=t?e:t)),e}function lr(e,t,n,r,i,a){var l,u=1&t,s=2&t,c=4&t;if(n&&(l=i?n(e,r,i,a):n(e)),l!==o)return l;if(!tl(e))return e;var f=Va(e);if(f){if(l=function(e){var t=e.length,n=new e.constructor(t);return t&&"string"==typeof e[0]&&Le.call(e,"index")&&(n.index=e.index,n.input=e.input),n}(e),!u)return Po(e,l)}else{var d=hi(e),h=d==w||d==x;if(Ga(e))return _o(e,u);if(d==S||d==g||h&&!i){if(l=s||h?{}:gi(e),!u)return s?function(e,t){return To(e,di(e),t)}(e,function(e,t){return e&&To(t,Ll(t),e)}(l,e)):function(e,t){return To(e,fi(e),t)}(e,rr(l,e))}else{if(!Je[d])return i?e:{};l=function(e,t,n){var r,o=e.constructor;switch(t){case j:return Eo(e);case m:case y:return new o(+e);case A:return function(e,t){var n=t?Eo(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}(e,n);case L:case I:case D:case N:case M:case z:case F:case $:case U:return So(e,n);case _:return new o;case E:case P:return new o(e);case C:return function(e){var t=new e.constructor(e.source,de.exec(e));return t.lastIndex=e.lastIndex,t}(e);case O:return new o;case T:return r=e,Mn?Se(Mn.call(r)):{}}}(e,d,u)}}a||(a=new Xn);var p=a.get(e);if(p)return p;a.set(e,l),ll(e)?e.forEach((function(r){l.add(lr(r,t,n,r,e,a))})):rl(e)&&e.forEach((function(r,o){l.set(o,lr(r,t,n,o,e,a))}));var v=f?o:(c?s?ri:ni:s?Ll:Al)(e);return bt(v||e,(function(r,o){v&&(r=e[o=r]),er(l,o,lr(r,t,n,o,e,a))})),l}function ur(e,t,n){var r=n.length;if(null==e)return!r;for(e=Se(e);r--;){var i=n[r],a=t[i],l=e[i];if(l===o&&!(i in e)||!a(l))return!1}return!0}function sr(e,t,n){if("function"!=typeof e)throw new Oe(i);return Ri((function(){e.apply(o,n)}),t)}function cr(e,t,n,r){var o=-1,i=Et,a=!0,l=e.length,u=[],s=t.length;if(!l)return u;n&&(t=kt(t,Ht(n))),r?(i=St,a=!1):t.length>=200&&(i=Vt,a=!1,t=new Kn(t));e:for(;++o-1},Vn.prototype.set=function(e,t){var n=this.__data__,r=tr(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},qn.prototype.clear=function(){this.size=0,this.__data__={hash:new Wn,map:new(Sn||Vn),string:new Wn}},qn.prototype.delete=function(e){var t=ui(this,e).delete(e);return this.size-=t?1:0,t},qn.prototype.get=function(e){return ui(this,e).get(e)},qn.prototype.has=function(e){return ui(this,e).has(e)},qn.prototype.set=function(e,t){var n=ui(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},Kn.prototype.add=Kn.prototype.push=function(e){return this.__data__.set(e,a),this},Kn.prototype.has=function(e){return this.__data__.has(e)},Xn.prototype.clear=function(){this.__data__=new Vn,this.size=0},Xn.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},Xn.prototype.get=function(e){return this.__data__.get(e)},Xn.prototype.has=function(e){return this.__data__.has(e)},Xn.prototype.set=function(e,t){var n=this.__data__;if(n instanceof Vn){var r=n.__data__;if(!Sn||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new qn(r)}return n.set(e,t),this.size=n.size,this};var fr=Ao(br),dr=Ao(wr,!0);function hr(e,t){var n=!0;return fr(e,(function(e,r,o){return n=!!t(e,r,o)})),n}function pr(e,t,n){for(var r=-1,i=e.length;++r0&&n(l)?t>1?vr(l,t-1,n,r,o):Ct(o,l):r||(o[o.length]=l)}return o}var mr=Lo(),yr=Lo(!0);function br(e,t){return e&&mr(e,t,Al)}function wr(e,t){return e&&yr(e,t,Al)}function xr(e,t){return _t(t,(function(t){return Za(e[t])}))}function _r(e,t){for(var n=0,r=(t=yo(t,e)).length;null!=e&&nt}function Cr(e,t){return null!=e&&Le.call(e,t)}function Or(e,t){return null!=e&&t in Se(e)}function Pr(e,t,n){for(var i=n?St:Et,a=e[0].length,l=e.length,u=l,s=r(l),c=1/0,f=[];u--;){var d=e[u];u&&t&&(d=kt(d,Ht(t))),c=yn(d.length,c),s[u]=!n&&(t||a>=120&&d.length>=120)?new Kn(u&&d):o}d=e[0];var h=-1,p=s[0];e:for(;++h=l?u:u*("desc"==n[r]?-1:1)}return e.index-t.index}(e,t,n)}));r--;)e[r]=e[r].value;return e}(o)}function Hr(e,t,n){for(var r=-1,o=t.length,i={};++r-1;)l!==e&&rt.call(l,u,1),rt.call(e,u,1);return e}function Vr(e,t){for(var n=e?t.length:0,r=n-1;n--;){var o=t[n];if(n==r||o!==i){var i=o;mi(o)?rt.call(e,o,1):so(e,o)}}return e}function qr(e,t){return e+fn(xn()*(t-e+1))}function Kr(e,t){var n="";if(!e||t<1||t>f)return n;do{t%2&&(n+=e),(t=fn(t/2))&&(e+=e)}while(t);return n}function Xr(e,t){return ji(ki(e,t,ou),e+"")}function Gr(e){return Yn(Ul(e))}function Yr(e,t){var n=Ul(e);return Ii(n,ar(t,0,n.length))}function Qr(e,t,n,r){if(!tl(e))return e;for(var i=-1,a=(t=yo(t,e)).length,l=a-1,u=e;null!=u&&++ii?0:i+t),(n=n>i?i:n)<0&&(n+=i),i=t>n?0:n-t>>>0,t>>>=0;for(var a=r(i);++o>>1,a=e[i];null!==a&&!sl(a)&&(n?a<=t:a=200){var s=t?null:Xo(e);if(s)return nn(s);a=!1,o=Vt,u=new Kn}else u=t?[]:l;e:for(;++r=r?e:to(e,t,n)}var xo=ct||function(e){return it.clearTimeout(e)};function _o(e,t){if(t)return e.slice();var n=e.length,r=He?He(n):new e.constructor(n);return e.copy(r),r}function Eo(e){var t=new e.constructor(e.byteLength);return new Be(t).set(new Be(e)),t}function So(e,t){var n=t?Eo(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}function ko(e,t){if(e!==t){var n=e!==o,r=null===e,i=e==e,a=sl(e),l=t!==o,u=null===t,s=t==t,c=sl(t);if(!u&&!c&&!a&&e>t||a&&l&&s&&!u&&!c||r&&l&&s||!n&&s||!i)return 1;if(!r&&!a&&!c&&e1?n[i-1]:o,l=i>2?n[2]:o;for(a=e.length>3&&"function"==typeof a?(i--,a):o,l&&yi(n[0],n[1],l)&&(a=i<3?o:a,i=1),t=Se(t);++r-1?i[a?t[l]:l]:o}}function zo(e){return ti((function(t){var n=t.length,r=n,a=Bn.prototype.thru;for(e&&t.reverse();r--;){var l=t[r];if("function"!=typeof l)throw new Oe(i);if(a&&!u&&"wrapper"==ii(l))var u=new Bn([],!0)}for(r=u?r:n;++r1&&b.reverse(),h&&fu))return!1;var c=a.get(e),f=a.get(t);if(c&&f)return c==t&&f==e;var d=-1,h=!0,p=2&n?new Kn:o;for(a.set(e,t),a.set(t,e);++d-1&&e%1==0&&e1?"& ":"")+t[r],t=t.join(n>2?", ":" "),e.replace(ie,"{\n/* [wrapped with "+t+"] */\n")}(r,function(e,t){return bt(p,(function(n){var r="_."+n[0];t&n[1]&&!Et(e,r)&&e.push(r)})),e.sort()}(function(e){var t=e.match(ae);return t?t[1].split(le):[]}(r),n)))}function Li(e){var t=0,n=0;return function(){var r=bn(),i=16-(r-n);if(n=r,i>0){if(++t>=800)return arguments[0]}else t=0;return e.apply(o,arguments)}}function Ii(e,t){var n=-1,r=e.length,i=r-1;for(t=t===o?r:t;++n1?e[t-1]:o;return n="function"==typeof n?(e.pop(),n):o,aa(e,n)}));function ha(e){var t=Fn(e);return t.__chain__=!0,t}function pa(e,t){return t(e)}var ga=ti((function(e){var t=e.length,n=t?e[0]:0,r=this.__wrapped__,i=function(t){return ir(t,e)};return!(t>1||this.__actions__.length)&&r instanceof Hn&&mi(n)?((r=r.slice(n,+n+(t?1:0))).__actions__.push({func:pa,args:[i],thisArg:o}),new Bn(r,this.__chain__).thru((function(e){return t&&!e.length&&e.push(o),e}))):this.thru(i)})),va=Ro((function(e,t,n){Le.call(e,n)?++e[n]:or(e,n,1)})),ma=Mo(Wi),ya=Mo(Vi);function ba(e,t){return(Va(e)?bt:fr)(e,li(t,3))}function wa(e,t){return(Va(e)?wt:dr)(e,li(t,3))}var xa=Ro((function(e,t,n){Le.call(e,n)?e[n].push(t):or(e,n,[t])})),_a=Xr((function(e,t,n){var o=-1,i="function"==typeof t,a=Ka(e)?r(e.length):[];return fr(e,(function(e){a[++o]=i?mt(t,e,n):Tr(e,t,n)})),a})),Ea=Ro((function(e,t,n){or(e,n,t)}));function Sa(e,t){return(Va(e)?kt:Mr)(e,li(t,3))}var ka=Ro((function(e,t,n){e[n?0:1].push(t)}),(function(){return[[],[]]})),Ca=Xr((function(e,t){if(null==e)return[];var n=t.length;return n>1&&yi(e,t[0],t[1])?t=[]:n>2&&yi(t[0],t[1],t[2])&&(t=[t[0]]),Br(e,vr(t,1),[])})),Oa=Rt||function(){return it.Date.now()};function Pa(e,t,n){return t=n?o:t,t=e&&null==t?e.length:t,Yo(e,s,o,o,o,o,t)}function Ta(e,t){var n;if("function"!=typeof t)throw new Oe(i);return e=gl(e),function(){return--e>0&&(n=t.apply(this,arguments)),e<=1&&(t=o),n}}var Ra=Xr((function(e,t,n){var r=1;if(n.length){var o=tn(n,ai(Ra));r|=u}return Yo(e,r,t,n,o)})),ja=Xr((function(e,t,n){var r=3;if(n.length){var o=tn(n,ai(ja));r|=u}return Yo(t,r,e,n,o)}));function Aa(e,t,n){var r,a,l,u,s,c,f=0,d=!1,h=!1,p=!0;if("function"!=typeof e)throw new Oe(i);function g(t){var n=r,i=a;return r=a=o,f=t,u=e.apply(i,n)}function v(e){return f=e,s=Ri(y,t),d?g(e):u}function m(e){var n=e-c;return c===o||n>=t||n<0||h&&e-f>=l}function y(){var e=Oa();if(m(e))return b(e);s=Ri(y,function(e){var n=t-(e-c);return h?yn(n,l-(e-f)):n}(e))}function b(e){return s=o,p&&r?g(e):(r=a=o,u)}function w(){var e=Oa(),n=m(e);if(r=arguments,a=this,c=e,n){if(s===o)return v(c);if(h)return xo(s),s=Ri(y,t),g(c)}return s===o&&(s=Ri(y,t)),u}return t=ml(t)||0,tl(n)&&(d=!!n.leading,l=(h="maxWait"in n)?mn(ml(n.maxWait)||0,t):l,p="trailing"in n?!!n.trailing:p),w.cancel=function(){s!==o&&xo(s),f=0,r=c=a=s=o},w.flush=function(){return s===o?u:b(Oa())},w}var La=Xr((function(e,t){return sr(e,1,t)})),Ia=Xr((function(e,t,n){return sr(e,ml(t)||0,n)}));function Da(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new Oe(i);var n=function(){var r=arguments,o=t?t.apply(this,r):r[0],i=n.cache;if(i.has(o))return i.get(o);var a=e.apply(this,r);return n.cache=i.set(o,a)||i,a};return n.cache=new(Da.Cache||qn),n}function Na(e){if("function"!=typeof e)throw new Oe(i);return function(){var t=arguments;switch(t.length){case 0:return!e.call(this);case 1:return!e.call(this,t[0]);case 2:return!e.call(this,t[0],t[1]);case 3:return!e.call(this,t[0],t[1],t[2])}return!e.apply(this,t)}}Da.Cache=qn;var Ma=bo((function(e,t){var n=(t=1==t.length&&Va(t[0])?kt(t[0],Ht(li())):kt(vr(t,1),Ht(li()))).length;return Xr((function(r){for(var o=-1,i=yn(r.length,n);++o=t})),Wa=Rr(function(){return arguments}())?Rr:function(e){return nl(e)&&Le.call(e,"callee")&&!et.call(e,"callee")},Va=r.isArray,qa=ft?Ht(ft):function(e){return nl(e)&&Sr(e)==j};function Ka(e){return null!=e&&el(e.length)&&!Za(e)}function Xa(e){return nl(e)&&Ka(e)}var Ga=hn||mu,Ya=dt?Ht(dt):function(e){return nl(e)&&Sr(e)==y};function Qa(e){if(!nl(e))return!1;var t=Sr(e);return t==b||"[object DOMException]"==t||"string"==typeof e.message&&"string"==typeof e.name&&!il(e)}function Za(e){if(!tl(e))return!1;var t=Sr(e);return t==w||t==x||"[object AsyncFunction]"==t||"[object Proxy]"==t}function Ja(e){return"number"==typeof e&&e==gl(e)}function el(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=f}function tl(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function nl(e){return null!=e&&"object"==typeof e}var rl=ht?Ht(ht):function(e){return nl(e)&&hi(e)==_};function ol(e){return"number"==typeof e||nl(e)&&Sr(e)==E}function il(e){if(!nl(e)||Sr(e)!=S)return!1;var t=qe(e);if(null===t)return!0;var n=Le.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&Ae.call(n)==Me}var al=pt?Ht(pt):function(e){return nl(e)&&Sr(e)==C},ll=gt?Ht(gt):function(e){return nl(e)&&hi(e)==O};function ul(e){return"string"==typeof e||!Va(e)&&nl(e)&&Sr(e)==P}function sl(e){return"symbol"==typeof e||nl(e)&&Sr(e)==T}var cl=vt?Ht(vt):function(e){return nl(e)&&el(e.length)&&!!Ze[Sr(e)]},fl=Vo(Nr),dl=Vo((function(e,t){return e<=t}));function hl(e){if(!e)return[];if(Ka(e))return ul(e)?an(e):Po(e);if(at&&e[at])return function(e){for(var t,n=[];!(t=e.next()).done;)n.push(t.value);return n}(e[at]());var t=hi(e);return(t==_?Jt:t==O?nn:Ul)(e)}function pl(e){return e?(e=ml(e))===c||e===-1/0?17976931348623157e292*(e<0?-1:1):e==e?e:0:0===e?e:0}function gl(e){var t=pl(e),n=t%1;return t==t?n?t-n:t:0}function vl(e){return e?ar(gl(e),0,h):0}function ml(e){if("number"==typeof e)return e;if(sl(e))return d;if(tl(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=tl(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=Bt(e);var n=pe.test(e);return n||ve.test(e)?nt(e.slice(2),n?2:8):he.test(e)?d:+e}function yl(e){return To(e,Ll(e))}function bl(e){return null==e?"":lo(e)}var wl=jo((function(e,t){if(_i(t)||Ka(t))To(t,Al(t),e);else for(var n in t)Le.call(t,n)&&er(e,n,t[n])})),xl=jo((function(e,t){To(t,Ll(t),e)})),_l=jo((function(e,t,n,r){To(t,Ll(t),e,r)})),El=jo((function(e,t,n,r){To(t,Al(t),e,r)})),Sl=ti(ir),kl=Xr((function(e,t){e=Se(e);var n=-1,r=t.length,i=r>2?t[2]:o;for(i&&yi(t[0],t[1],i)&&(r=1);++n1),t})),To(e,ri(e),n),r&&(n=lr(n,7,Jo));for(var o=t.length;o--;)so(n,t[o]);return n})),Ml=ti((function(e,t){return null==e?{}:function(e,t){return Hr(e,t,(function(t,n){return Pl(e,n)}))}(e,t)}));function zl(e,t){if(null==e)return{};var n=kt(ri(e),(function(e){return[e]}));return t=li(t),Hr(e,n,(function(e,n){return t(e,n[0])}))}var Fl=Go(Al),$l=Go(Ll);function Ul(e){return null==e?[]:Wt(e,Al(e))}var Bl=Do((function(e,t,n){return t=t.toLowerCase(),e+(n?Hl(t):t)}));function Hl(e){return Ql(bl(e).toLowerCase())}function Wl(e){return(e=bl(e))&&e.replace(ye,Gt).replace(Ve,"")}var Vl=Do((function(e,t,n){return e+(n?"-":"")+t.toLowerCase()})),ql=Do((function(e,t,n){return e+(n?" ":"")+t.toLowerCase()})),Kl=Io("toLowerCase"),Xl=Do((function(e,t,n){return e+(n?"_":"")+t.toLowerCase()})),Gl=Do((function(e,t,n){return e+(n?" ":"")+Ql(t)})),Yl=Do((function(e,t,n){return e+(n?" ":"")+t.toUpperCase()})),Ql=Io("toUpperCase");function Zl(e,t,n){return e=bl(e),(t=n?o:t)===o?function(e){return Ge.test(e)}(e)?function(e){return e.match(Ke)||[]}(e):function(e){return e.match(ue)||[]}(e):e.match(t)||[]}var Jl=Xr((function(e,t){try{return mt(e,o,t)}catch(e){return Qa(e)?e:new xe(e)}})),eu=ti((function(e,t){return bt(t,(function(t){t=zi(t),or(e,t,Ra(e[t],e))})),e}));function tu(e){return function(){return e}}var nu=zo(),ru=zo(!0);function ou(e){return e}function iu(e){return Ir("function"==typeof e?e:lr(e,1))}var au=Xr((function(e,t){return function(n){return Tr(n,e,t)}})),lu=Xr((function(e,t){return function(n){return Tr(e,n,t)}}));function uu(e,t,n){var r=Al(t),o=xr(t,r);null!=n||tl(t)&&(o.length||!r.length)||(n=t,t=e,e=this,o=xr(t,Al(t)));var i=!(tl(n)&&"chain"in n&&!n.chain),a=Za(e);return bt(o,(function(n){var r=t[n];e[n]=r,a&&(e.prototype[n]=function(){var t=this.__chain__;if(i||t){var n=e(this.__wrapped__),o=n.__actions__=Po(this.__actions__);return o.push({func:r,args:arguments,thisArg:e}),n.__chain__=t,n}return r.apply(e,Ct([this.value()],arguments))})})),e}function su(){}var cu=Bo(kt),fu=Bo(xt),du=Bo(Tt);function hu(e){return bi(e)?Mt(zi(e)):function(e){return function(t){return _r(t,e)}}(e)}var pu=Wo(),gu=Wo(!0);function vu(){return[]}function mu(){return!1}var yu,bu=Uo((function(e,t){return e+t}),0),wu=Ko("ceil"),xu=Uo((function(e,t){return e/t}),1),_u=Ko("floor"),Eu=Uo((function(e,t){return e*t}),1),Su=Ko("round"),ku=Uo((function(e,t){return e-t}),0);return Fn.after=function(e,t){if("function"!=typeof t)throw new Oe(i);return e=gl(e),function(){if(--e<1)return t.apply(this,arguments)}},Fn.ary=Pa,Fn.assign=wl,Fn.assignIn=xl,Fn.assignInWith=_l,Fn.assignWith=El,Fn.at=Sl,Fn.before=Ta,Fn.bind=Ra,Fn.bindAll=eu,Fn.bindKey=ja,Fn.castArray=function(){if(!arguments.length)return[];var e=arguments[0];return Va(e)?e:[e]},Fn.chain=ha,Fn.chunk=function(e,t,n){t=(n?yi(e,t,n):t===o)?1:mn(gl(t),0);var i=null==e?0:e.length;if(!i||t<1)return[];for(var a=0,l=0,u=r(cn(i/t));ai?0:i+n),(r=r===o||r>i?i:gl(r))<0&&(r+=i),r=n>r?0:vl(r);n>>0)?(e=bl(e))&&("string"==typeof t||null!=t&&!al(t))&&!(t=lo(t))&&Zt(e)?wo(an(e),0,n):e.split(t,n):[]},Fn.spread=function(e,t){if("function"!=typeof e)throw new Oe(i);return t=null==t?0:mn(gl(t),0),Xr((function(n){var r=n[t],o=wo(n,0,t);return r&&Ct(o,r),mt(e,this,o)}))},Fn.tail=function(e){var t=null==e?0:e.length;return t?to(e,1,t):[]},Fn.take=function(e,t,n){return e&&e.length?to(e,0,(t=n||t===o?1:gl(t))<0?0:t):[]},Fn.takeRight=function(e,t,n){var r=null==e?0:e.length;return r?to(e,(t=r-(t=n||t===o?1:gl(t)))<0?0:t,r):[]},Fn.takeRightWhile=function(e,t){return e&&e.length?fo(e,li(t,3),!1,!0):[]},Fn.takeWhile=function(e,t){return e&&e.length?fo(e,li(t,3)):[]},Fn.tap=function(e,t){return t(e),e},Fn.throttle=function(e,t,n){var r=!0,o=!0;if("function"!=typeof e)throw new Oe(i);return tl(n)&&(r="leading"in n?!!n.leading:r,o="trailing"in n?!!n.trailing:o),Aa(e,t,{leading:r,maxWait:t,trailing:o})},Fn.thru=pa,Fn.toArray=hl,Fn.toPairs=Fl,Fn.toPairsIn=$l,Fn.toPath=function(e){return Va(e)?kt(e,zi):sl(e)?[e]:Po(Mi(bl(e)))},Fn.toPlainObject=yl,Fn.transform=function(e,t,n){var r=Va(e),o=r||Ga(e)||cl(e);if(t=li(t,4),null==n){var i=e&&e.constructor;n=o?r?new i:[]:tl(e)&&Za(i)?$n(qe(e)):{}}return(o?bt:br)(e,(function(e,r,o){return t(n,e,r,o)})),n},Fn.unary=function(e){return Pa(e,1)},Fn.union=na,Fn.unionBy=ra,Fn.unionWith=oa,Fn.uniq=function(e){return e&&e.length?uo(e):[]},Fn.uniqBy=function(e,t){return e&&e.length?uo(e,li(t,2)):[]},Fn.uniqWith=function(e,t){return t="function"==typeof t?t:o,e&&e.length?uo(e,o,t):[]},Fn.unset=function(e,t){return null==e||so(e,t)},Fn.unzip=ia,Fn.unzipWith=aa,Fn.update=function(e,t,n){return null==e?e:co(e,t,mo(n))},Fn.updateWith=function(e,t,n,r){return r="function"==typeof r?r:o,null==e?e:co(e,t,mo(n),r)},Fn.values=Ul,Fn.valuesIn=function(e){return null==e?[]:Wt(e,Ll(e))},Fn.without=la,Fn.words=Zl,Fn.wrap=function(e,t){return za(mo(t),e)},Fn.xor=ua,Fn.xorBy=sa,Fn.xorWith=ca,Fn.zip=fa,Fn.zipObject=function(e,t){return go(e||[],t||[],er)},Fn.zipObjectDeep=function(e,t){return go(e||[],t||[],Qr)},Fn.zipWith=da,Fn.entries=Fl,Fn.entriesIn=$l,Fn.extend=xl,Fn.extendWith=_l,uu(Fn,Fn),Fn.add=bu,Fn.attempt=Jl,Fn.camelCase=Bl,Fn.capitalize=Hl,Fn.ceil=wu,Fn.clamp=function(e,t,n){return n===o&&(n=t,t=o),n!==o&&(n=(n=ml(n))==n?n:0),t!==o&&(t=(t=ml(t))==t?t:0),ar(ml(e),t,n)},Fn.clone=function(e){return lr(e,4)},Fn.cloneDeep=function(e){return lr(e,5)},Fn.cloneDeepWith=function(e,t){return lr(e,5,t="function"==typeof t?t:o)},Fn.cloneWith=function(e,t){return lr(e,4,t="function"==typeof t?t:o)},Fn.conformsTo=function(e,t){return null==t||ur(e,t,Al(t))},Fn.deburr=Wl,Fn.defaultTo=function(e,t){return null==e||e!=e?t:e},Fn.divide=xu,Fn.endsWith=function(e,t,n){e=bl(e),t=lo(t);var r=e.length,i=n=n===o?r:ar(gl(n),0,r);return(n-=t.length)>=0&&e.slice(n,i)==t},Fn.eq=Ua,Fn.escape=function(e){return(e=bl(e))&&X.test(e)?e.replace(q,Yt):e},Fn.escapeRegExp=function(e){return(e=bl(e))&&ne.test(e)?e.replace(te,"\\$&"):e},Fn.every=function(e,t,n){var r=Va(e)?xt:hr;return n&&yi(e,t,n)&&(t=o),r(e,li(t,3))},Fn.find=ma,Fn.findIndex=Wi,Fn.findKey=function(e,t){return jt(e,li(t,3),br)},Fn.findLast=ya,Fn.findLastIndex=Vi,Fn.findLastKey=function(e,t){return jt(e,li(t,3),wr)},Fn.floor=_u,Fn.forEach=ba,Fn.forEachRight=wa,Fn.forIn=function(e,t){return null==e?e:mr(e,li(t,3),Ll)},Fn.forInRight=function(e,t){return null==e?e:yr(e,li(t,3),Ll)},Fn.forOwn=function(e,t){return e&&br(e,li(t,3))},Fn.forOwnRight=function(e,t){return e&&wr(e,li(t,3))},Fn.get=Ol,Fn.gt=Ba,Fn.gte=Ha,Fn.has=function(e,t){return null!=e&&pi(e,t,Cr)},Fn.hasIn=Pl,Fn.head=Ki,Fn.identity=ou,Fn.includes=function(e,t,n,r){e=Ka(e)?e:Ul(e),n=n&&!r?gl(n):0;var o=e.length;return n<0&&(n=mn(o+n,0)),ul(e)?n<=o&&e.indexOf(t,n)>-1:!!o&&Lt(e,t,n)>-1},Fn.indexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var o=null==n?0:gl(n);return o<0&&(o=mn(r+o,0)),Lt(e,t,o)},Fn.inRange=function(e,t,n){return t=pl(t),n===o?(n=t,t=0):n=pl(n),function(e,t,n){return e>=yn(t,n)&&e=-9007199254740991&&e<=f},Fn.isSet=ll,Fn.isString=ul,Fn.isSymbol=sl,Fn.isTypedArray=cl,Fn.isUndefined=function(e){return e===o},Fn.isWeakMap=function(e){return nl(e)&&hi(e)==R},Fn.isWeakSet=function(e){return nl(e)&&"[object WeakSet]"==Sr(e)},Fn.join=function(e,t){return null==e?"":gn.call(e,t)},Fn.kebabCase=Vl,Fn.last=Qi,Fn.lastIndexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var i=r;return n!==o&&(i=(i=gl(n))<0?mn(r+i,0):yn(i,r-1)),t==t?function(e,t,n){for(var r=n+1;r--;)if(e[r]===t)return r;return r}(e,t,i):At(e,Dt,i,!0)},Fn.lowerCase=ql,Fn.lowerFirst=Kl,Fn.lt=fl,Fn.lte=dl,Fn.max=function(e){return e&&e.length?pr(e,ou,kr):o},Fn.maxBy=function(e,t){return e&&e.length?pr(e,li(t,2),kr):o},Fn.mean=function(e){return Nt(e,ou)},Fn.meanBy=function(e,t){return Nt(e,li(t,2))},Fn.min=function(e){return e&&e.length?pr(e,ou,Nr):o},Fn.minBy=function(e,t){return e&&e.length?pr(e,li(t,2),Nr):o},Fn.stubArray=vu,Fn.stubFalse=mu,Fn.stubObject=function(){return{}},Fn.stubString=function(){return""},Fn.stubTrue=function(){return!0},Fn.multiply=Eu,Fn.nth=function(e,t){return e&&e.length?Ur(e,gl(t)):o},Fn.noConflict=function(){return it._===this&&(it._=ze),this},Fn.noop=su,Fn.now=Oa,Fn.pad=function(e,t,n){e=bl(e);var r=(t=gl(t))?on(e):0;if(!t||r>=t)return e;var o=(t-r)/2;return Ho(fn(o),n)+e+Ho(cn(o),n)},Fn.padEnd=function(e,t,n){e=bl(e);var r=(t=gl(t))?on(e):0;return t&&rt){var r=e;e=t,t=r}if(n||e%1||t%1){var i=xn();return yn(e+i*(t-e+tt("1e-"+((i+"").length-1))),t)}return qr(e,t)},Fn.reduce=function(e,t,n){var r=Va(e)?Ot:Ft,o=arguments.length<3;return r(e,li(t,4),n,o,fr)},Fn.reduceRight=function(e,t,n){var r=Va(e)?Pt:Ft,o=arguments.length<3;return r(e,li(t,4),n,o,dr)},Fn.repeat=function(e,t,n){return t=(n?yi(e,t,n):t===o)?1:gl(t),Kr(bl(e),t)},Fn.replace=function(){var e=arguments,t=bl(e[0]);return e.length<3?t:t.replace(e[1],e[2])},Fn.result=function(e,t,n){var r=-1,i=(t=yo(t,e)).length;for(i||(i=1,e=o);++rf)return[];var n=h,r=yn(e,h);t=li(t),e-=h;for(var o=Ut(r,t);++n=a)return e;var u=n-on(r);if(u<1)return r;var s=l?wo(l,0,u).join(""):e.slice(0,u);if(i===o)return s+r;if(l&&(u+=s.length-u),al(i)){if(e.slice(u).search(i)){var c,f=s;for(i.global||(i=ke(i.source,bl(de.exec(i))+"g")),i.lastIndex=0;c=i.exec(f);)var d=c.index;s=s.slice(0,d===o?u:d)}}else if(e.indexOf(lo(i),u)!=u){var h=s.lastIndexOf(i);h>-1&&(s=s.slice(0,h))}return s+r},Fn.unescape=function(e){return(e=bl(e))&&K.test(e)?e.replace(V,un):e},Fn.uniqueId=function(e){var t=++Ie;return bl(e)+t},Fn.upperCase=Yl,Fn.upperFirst=Ql,Fn.each=ba,Fn.eachRight=wa,Fn.first=Ki,uu(Fn,(yu={},br(Fn,(function(e,t){Le.call(Fn.prototype,t)||(yu[t]=e)})),yu),{chain:!1}),Fn.VERSION="4.17.21",bt(["bind","bindKey","curry","curryRight","partial","partialRight"],(function(e){Fn[e].placeholder=Fn})),bt(["drop","take"],(function(e,t){Hn.prototype[e]=function(n){n=n===o?1:mn(gl(n),0);var r=this.__filtered__&&!t?new Hn(this):this.clone();return r.__filtered__?r.__takeCount__=yn(n,r.__takeCount__):r.__views__.push({size:yn(n,h),type:e+(r.__dir__<0?"Right":"")}),r},Hn.prototype[e+"Right"]=function(t){return this.reverse()[e](t).reverse()}})),bt(["filter","map","takeWhile"],(function(e,t){var n=t+1,r=1==n||3==n;Hn.prototype[e]=function(e){var t=this.clone();return t.__iteratees__.push({iteratee:li(e,3),type:n}),t.__filtered__=t.__filtered__||r,t}})),bt(["head","last"],(function(e,t){var n="take"+(t?"Right":"");Hn.prototype[e]=function(){return this[n](1).value()[0]}})),bt(["initial","tail"],(function(e,t){var n="drop"+(t?"":"Right");Hn.prototype[e]=function(){return this.__filtered__?new Hn(this):this[n](1)}})),Hn.prototype.compact=function(){return this.filter(ou)},Hn.prototype.find=function(e){return this.filter(e).head()},Hn.prototype.findLast=function(e){return this.reverse().find(e)},Hn.prototype.invokeMap=Xr((function(e,t){return"function"==typeof e?new Hn(this):this.map((function(n){return Tr(n,e,t)}))})),Hn.prototype.reject=function(e){return this.filter(Na(li(e)))},Hn.prototype.slice=function(e,t){e=gl(e);var n=this;return n.__filtered__&&(e>0||t<0)?new Hn(n):(e<0?n=n.takeRight(-e):e&&(n=n.drop(e)),t!==o&&(n=(t=gl(t))<0?n.dropRight(-t):n.take(t-e)),n)},Hn.prototype.takeRightWhile=function(e){return this.reverse().takeWhile(e).reverse()},Hn.prototype.toArray=function(){return this.take(h)},br(Hn.prototype,(function(e,t){var n=/^(?:filter|find|map|reject)|While$/.test(t),r=/^(?:head|last)$/.test(t),i=Fn[r?"take"+("last"==t?"Right":""):t],a=r||/^find/.test(t);i&&(Fn.prototype[t]=function(){var t=this.__wrapped__,l=r?[1]:arguments,u=t instanceof Hn,s=l[0],c=u||Va(t),f=function(e){var t=i.apply(Fn,Ct([e],l));return r&&d?t[0]:t};c&&n&&"function"==typeof s&&1!=s.length&&(u=c=!1);var d=this.__chain__,h=!!this.__actions__.length,p=a&&!d,g=u&&!h;if(!a&&c){t=g?t:new Hn(this);var v=e.apply(t,l);return v.__actions__.push({func:pa,args:[f],thisArg:o}),new Bn(v,d)}return p&&g?e.apply(this,l):(v=this.thru(f),p?r?v.value()[0]:v.value():v)})})),bt(["pop","push","shift","sort","splice","unshift"],(function(e){var t=Pe[e],n=/^(?:push|sort|unshift)$/.test(e)?"tap":"thru",r=/^(?:pop|shift)$/.test(e);Fn.prototype[e]=function(){var e=arguments;if(r&&!this.__chain__){var o=this.value();return t.apply(Va(o)?o:[],e)}return this[n]((function(n){return t.apply(Va(n)?n:[],e)}))}})),br(Hn.prototype,(function(e,t){var n=Fn[t];if(n){var r=n.name+"";Le.call(Rn,r)||(Rn[r]=[]),Rn[r].push({name:t,func:n})}})),Rn[Fo(o,2).name]=[{name:"wrapper",func:o}],Hn.prototype.clone=function(){var e=new Hn(this.__wrapped__);return e.__actions__=Po(this.__actions__),e.__dir__=this.__dir__,e.__filtered__=this.__filtered__,e.__iteratees__=Po(this.__iteratees__),e.__takeCount__=this.__takeCount__,e.__views__=Po(this.__views__),e},Hn.prototype.reverse=function(){if(this.__filtered__){var e=new Hn(this);e.__dir__=-1,e.__filtered__=!0}else(e=this.clone()).__dir__*=-1;return e},Hn.prototype.value=function(){var e=this.__wrapped__.value(),t=this.__dir__,n=Va(e),r=t<0,o=n?e.length:0,i=function(e,t,n){for(var r=-1,o=n.length;++r=this.__values__.length;return{done:e,value:e?o:this.__values__[this.__index__++]}},Fn.prototype.plant=function(e){for(var t,n=this;n instanceof Un;){var r=$i(n);r.__index__=0,r.__values__=o,t?i.__wrapped__=r:t=r;var i=r;n=n.__wrapped__}return i.__wrapped__=e,t},Fn.prototype.reverse=function(){var e=this.__wrapped__;if(e instanceof Hn){var t=e;return this.__actions__.length&&(t=new Hn(this)),(t=t.reverse()).__actions__.push({func:pa,args:[ta],thisArg:o}),new Bn(t,this.__chain__)}return this.thru(ta)},Fn.prototype.toJSON=Fn.prototype.valueOf=Fn.prototype.value=function(){return ho(this.__wrapped__,this.__actions__)},Fn.prototype.first=Fn.prototype.head,at&&(Fn.prototype[at]=function(){return this}),Fn}();it._=sn,(r=function(){return sn}.call(t,n,t,e))===o||(e.exports=r)}.call(this)},9410:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isEqualNode=o,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)})),s.forEach((e=>n.insertBefore(e,i))),i.content=(a-l.length+s.length).toString()}(e,i[e]||[])}))}))}}},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},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},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},7928:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.markAssetError=s,t.isAssetError=function(e){return e&&u 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,s(new Error("Failed to load client middleware manifest")))},t.createRouteLoader=function(e){const t=new Map,n=new Map,r=new Map,u=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(s(new Error(`Failed to load script: ${e}`))),t.crossOrigin=window.omnivoreEnv.__NEXT_CROSS_ORIGIN,t.src=e,document.body.appendChild(t)}))}(e)),t))}}function h(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 s(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),u.delete(e))}))},loadRoute(n,r){return a(n,u,(()=>c(d(e,n).then((({scripts:e,css:r})=>Promise.all([t.has(n)?[]:Promise.all(e.map(f)),Promise.all(r.map(h))]))).then((e=>this.whenEntrypoint(n).then((t=>({entrypoint:t,styles:e[1]}))))),i,s(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}}(),u=Symbol("ASSET_LOAD_ERROR");function s(e){return Object.defineProperty(e,u,{})}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,s(new Error("Failed to load client build manifest")))}function d(e,t){return f().then((n=>{if(!(t in n))throw s(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")))}}))}},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 s.router=new o.default(...e),s.readyCallbacks.forEach((e=>e())),s.readyCallbacks=[],s.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=u(n(2784)),o=u(n(6640)),i=n(6510),a=u(n(274)),l=u(n(9564));function u(e){return e&&e.__esModule?e:{default:e}}const s={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(!s.router)throw new Error('No router instance found.\nYou should only use "next/router" on the client side of your app.\n');return s.router}Object.defineProperty(s,"events",{get:()=>o.default.events}),c.forEach((e=>{Object.defineProperty(s,e,{get:()=>d()[e]})})),f.forEach((e=>{s[e]=(...t)=>d()[e](...t)})),["routeChangeStart","beforeHistoryChange","routeChangeComplete","routeChangeError","hashChangeStart","hashChangeComplete"].forEach((e=>{s.ready((()=>{o.default.events.on(e,((...t)=>{const n=`on${e.charAt(0).toUpperCase()}${e.substring(1)}`,r=s;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 h=s;t.default=h},9515:(e,t,n)=>{"use strict";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 u(e){for(var t=1;t{const{src:t,id:n,onLoad:r=(()=>{}),dangerouslySetInnerHTML:o,children:a="",strategy:l="afterInteractive",onError:u}=e,d=n||t;if(d&&c.has(d))return;if(s.has(t))return c.add(d),void s.get(t).then(r,u);const h=document.createElement("script"),p=new Promise(((e,t)=>{h.addEventListener("load",(function(t){e(),r&&r.call(this,t)})),h.addEventListener("error",(function(e){t(e)}))})).catch((function(e){u&&u(e)}));t&&s.set(t,p),c.add(d),o?h.innerHTML=o.__html||"":a?h.textContent="string"==typeof a?a:Array.isArray(a)?a.join(""):"":t&&(h.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();h.setAttribute(e,n)}h.setAttribute("data-nscript",l),document.body.appendChild(h)};t.default=function(e){const{src:t="",onLoad:n=(()=>{}),dangerouslySetInnerHTML:i,strategy:l="afterInteractive",onError:s}=e,f=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","dangerouslySetInnerHTML","strategy","onError"]),{updateScripts:h,scripts:p,getIsSsr:g}=r.useContext(o.HeadManagerContext);return r.useEffect((()=>{"afterInteractive"===l?d(e):"lazyOnload"===l&&function(e){"complete"===document.readyState?a.requestIdleCallback((()=>d(e))):window.addEventListener("load",(()=>{a.requestIdleCallback((()=>d(e)))}))}(e)}),[e,l]),"beforeInteractive"===l&&(h?(p.beforeInteractive=(p.beforeInteractive||[]).concat([u({src:t,onLoad:n,onError:s},f)]),h(p)):g&&g()?c.add(f.id||t):g&&!g()&&d(e)),null}},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)},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&&u<=57||u>=65&&u<=90||u>=97&&u<=122||95===u))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=[],u=0,s=0,c="",f=function(e){if(s-1:void 0===_;o||(g+="(?:"+p+"(?="+h+"))?"),E||(g+="(?="+p+"|"+h+")")}return new RegExp(g,a(n))}function u(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}},4863:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.normalizePathSep=o,t.denormalizePagePath=function(e){return(e=o(e)).startsWith("/index/")&&!r.isDynamicRoute(e)?e=e.slice(6):"/index"===e&&(e="/"),e};var r=n(9150);function o(e){return e.replace(/\\/g,"/")}},8058:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.escapeStringRegexp=function(e){return e.replace(/[|\\{}()[\]^$+*?.-]/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},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}}},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)}))}}}},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||l.normalizeLocalePath(e,n).detectedLocale;const o=y(r,void 0,t);return!!o&&`http${o.http?"":"s"}://${o.domain}${b||""}${t===o.defaultLocale?"":`/${t}`}${e}`}return!1},t.addLocale=_,t.delLocale=E,t.hasBasePath=k,t.addBasePath=C,t.delBasePath=O,t.isLocalURL=P,t.interpolateAs=T,t.resolveHref=j,t.default=void 0;var r=n(1119),o=n(7928),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(274)),a=n(4863),l=n(816),u=m(n(7471)),s=n(1624),c=n(7482),f=n(1577),d=n(646),h=m(n(5317)),p=n(3107),g=n(4794),v=n(2763);function m(e){return e&&e.__esModule?e:{default:e}}let y;window.omnivoreEnv.__NEXT_I18N_SUPPORT&&(y=n(927).D);const b=window.omnivoreEnv.__NEXT_ROUTER_BASEPATH||"";function w(){return Object.assign(new Error("Route Cancelled"),{cancelled:!0})}function x(e,t){if(!e.startsWith("/")||!t)return e;const n=S(e);return r.normalizePathTrailingSlash(`${t}${n}`)+e.substr(n.length)}function _(e,t,n){if(window.omnivoreEnv.__NEXT_I18N_SUPPORT){const r=S(e).toLowerCase(),o=t&&t.toLowerCase();return t&&t!==n&&!r.startsWith("/"+o+"/")&&r!=="/"+o?x(e,"/"+t):e}return e}function E(e,t){if(window.omnivoreEnv.__NEXT_I18N_SUPPORT){const n=S(e),r=n.toLowerCase(),o=t&&t.toLowerCase();return t&&(r.startsWith("/"+o+"/")||r==="/"+o)?(n.length===t.length+1?"/":"")+e.substr(t.length+1):e}return e}function S(e){const t=e.indexOf("?"),n=e.indexOf("#");return(t>-1||n>-1)&&(e=e.substring(0,t>-1?t:n)),e}function k(e){return(e=S(e))===b||e.startsWith(b+"/")}function C(e){return x(e,b)}function O(e){return(e=e.slice(b.length)).startsWith("/")||(e=`/${e}`),e}function P(e){if(e.startsWith("/")||e.startsWith("#")||e.startsWith("?"))return!0;try{const t=s.getLocationOrigin(),n=new URL(e,t);return n.origin===t&&k(n.pathname)}catch(e){return!1}}function T(e,t,n){let r="";const o=g.getRouteRegex(e),i=o.groups,a=(t!==e?p.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 R(e,t){const n={};return Object.keys(e).forEach((r=>{t.includes(r)||(n[r]=e[r])})),n}function j(e,t,n){let o,i="string"==typeof t?t:s.formatWithValidation(t);const a=i.match(/^[a-zA-Z]{1,}:\/\//),l=a?i.substr(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=s.normalizeRepeatedSlashes(l);i=(a?a[0]:"")+e}if(!P(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(c.isDynamicRoute(e.pathname)&&e.searchParams&&n){const n=d.searchParamsToUrlQuery(e.searchParams),{result:r,params:o}=T(e.pathname,e.pathname,n);r&&(t=s.formatWithValidation({pathname:r,hash:e.hash,query:R(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 A(e){const t=s.getLocationOrigin();return e.startsWith(t)?e.substring(t.length):e}function L(e,t,n){let[r,o]=j(e,t,!0);const i=s.getLocationOrigin(),a=r.startsWith(i),l=o&&o.startsWith(i);r=A(r),o=o?A(o):o;const u=a?r:C(r),c=n?A(j(e,n)):o||r;return{url:u,as:l?c:C(c)}}function I(e,t){const n=r.removePathTrailingSlash(a.denormalizePagePath(e));return"/404"===n||"/_error"===n?e:(t.includes(n)||t.some((t=>{if(c.isDynamicRoute(t)&&g.getRouteRegex(t).re.test(n))return e=t,!0})),r.removePathTrailingSlash(e))}const D=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){}}(),N=Symbol("SSG_DATA_NOT_FOUND");function M(e,t,n){return fetch(e,{credentials:"same-origin"}).then((r=>{if(!r.ok){if(t>1&&r.status>=500)return M(e,t-1,n);if(404===r.status)return r.json().then((e=>{if(e.notFound)return{notFound:N};throw new Error("Failed to load static props")}));throw new Error("Failed to load static props")}return n.text?r.text():r.json()}))}function z(e,t,n,r,i){const{href:a}=new URL(e,window.location.href);return void 0!==r[a]?r[a]:r[a]=M(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 F{constructor(e,t,n,{initialProps:o,pageLoader:i,App:a,wrapApp:l,Component:u,err:d,subscription:h,isFallback:p,locale:g,locales:v,defaultLocale:m,domainLocales:w,isPreview:x}){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",s.formatWithValidation({pathname:C(e),query:t}),s.getURL())}if(!t.__N)return;let n;const{url:r,as:o,options:i,idx:a}=t;if(window.omnivoreEnv.__NEXT_SCROLL_RESTORATION&&D&&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}=f.parseRelativeUrl(r);this.isSsr&&o===C(this.asPath)&&l===C(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 _=r.removePathTrailingSlash(e);var E;this.components={},"/_error"!==e&&(this.components[_]={Component:u,initial:!0,props:o,err:d,__N_SSG:o&&o.__N_SSG,__N_SSP:o&&o.__N_SSP,__N_RSC:!!(null===(E=u)||void 0===E?void 0:E.__next_rsc__)}),this.components["/_app"]={Component:a,styleSheets:[]},this.events=F.events,this.pageLoader=i;const S=c.isDynamicRoute(e)&&self.__NEXT_DATA__.autoExport;if(this.basePath=b,this.sub=h,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)&&(S||self.location.search||window.omnivoreEnv.__NEXT_HAS_REWRITES)),window.omnivoreEnv.__NEXT_I18N_SUPPORT&&(this.locales=v,this.defaultLocale=m,this.domainLocales=w,this.isLocaleDomain=!!y(w,self.location.hostname)),this.state={route:_,pathname:e,query:t,asPath:S?e:n,isPreview:!!x,locale:window.omnivoreEnv.__NEXT_I18N_SUPPORT?g:void 0,isFallback:p},"undefined"!=typeof window){if("//"!==n.substr(0,2)){const r={locale:g};r._shouldResolveHref=n!==e,this.changeState("replaceState",s.formatWithValidation({pathname:C(e),query:t}),s.getURL(),r)}window.addEventListener("popstate",this.onPopState),window.omnivoreEnv.__NEXT_SCROLL_RESTORATION&&D&&(window.history.scrollRestoration="manual")}}reload(){window.location.reload()}back(){window.history.back()}push(e,t,n={}){if(window.omnivoreEnv.__NEXT_SCROLL_RESTORATION&&D)try{sessionStorage.setItem("__next_scroll_"+this._idx,JSON.stringify({x:self.pageXOffset,y:self.pageYOffset}))}catch{}return({url:e,as:t}=L(this,e,t)),this.change("pushState",e,t,n)}replace(e,t,n={}){return({url:e,as:t}=L(this,e,t)),this.change("replaceState",e,t,n)}async change(e,t,n,a,u){if(!P(t))return window.location.href=t,!1;const d=a._h||a._shouldResolveHref||S(t)===S(n),v={...this.state};a._h&&(this.isReady=!0);const m=v.locale;if(window.omnivoreEnv.__NEXT_I18N_SUPPORT){v.locale=!1===a.locale?this.defaultLocale:a.locale||v.locale,void 0===a.locale&&(a.locale=v.locale);const e=f.parseRelativeUrl(k(n)?O(n):n),r=l.normalizeLocalePath(e.pathname,this.locales);r.detectedLocale&&(v.locale=r.detectedLocale,e.pathname=C(e.pathname),n=s.formatWithValidation(e),t=C(l.normalizeLocalePath(k(t)?O(t):t,this.locales).pathname));let o=!1;window.omnivoreEnv.__NEXT_I18N_SUPPORT&&((null===(W=this.locales)||void 0===W?void 0:W.includes(v.locale))||(e.pathname=_(e.pathname,v.locale),window.location.href=s.formatWithValidation(e),o=!0));const i=y(this.domainLocales,void 0,v.locale);if(window.omnivoreEnv.__NEXT_I18N_SUPPORT&&!o&&i&&this.isLocaleDomain&&self.location.hostname!==i.domain){const e=O(n);window.location.href=`http${i.http?"":"s"}://${i.domain}${C(`${v.locale===i.defaultLocale?"":`/${v.locale}`}${"/"===e?"":e}`||"/")}`,o=!0}if(o)return new Promise((()=>{}))}a._h||(this.isSsr=!1),s.ST&&performance.mark("routeChange");const{shallow:b=!1,scroll:w=!0}=a,x={shallow:b};this._inFlightRoute&&this.abortComponentLoad(this._inFlightRoute,x),n=C(_(k(n)?O(n):n,a.locale,this.defaultLocale));const j=E(k(n)?O(n):n,v.locale);this._inFlightRoute=n;let A=m!==v.locale;if(!a._h&&this.onlyAHashChange(j)&&!A)return v.asPath=j,F.events.emit("hashChangeStart",n,x),this.changeState(e,t,n,{...a,scroll:!1}),w&&this.scrollToHash(j),this.set(v,this.components[v.route],null),F.events.emit("hashChangeComplete",n,x),!0;let D,M,z=f.parseRelativeUrl(t),{pathname:$,query:U}=z;try{[D,{__rewrites:M}]=await Promise.all([this.pageLoader.getPageList(),o.getClientBuildManifest(),this.pageLoader.getMiddlewareList()])}catch(e){return window.location.href=n,!1}this.urlIsNew(j)||A||(e="replaceState");let B=n;if($=$?r.removePathTrailingSlash(O($)):$,d&&"/_error"!==$)if(a._shouldResolveHref=!0,window.omnivoreEnv.__NEXT_HAS_REWRITES&&n.startsWith("/")){const e=h.default(C(_(j,v.locale)),D,M,U,(e=>I(e,D)),this.locales);if(e.externalDest)return location.href=n,!0;B=e.asPath,e.matchedPage&&e.resolvedHref&&($=e.resolvedHref,z.pathname=C($),t=s.formatWithValidation(z))}else z.pathname=I($,D),z.pathname!==$&&($=z.pathname,z.pathname=C($),t=s.formatWithValidation(z));if(!P(n))return window.location.href=n,!1;if(B=E(O(B),v.locale),1!==a._h||c.isDynamicRoute(r.removePathTrailingSlash($))){const r=await this._preflightRequest({as:n,cache:!0,pages:D,pathname:$,query:U,locale:v.locale,isPreview:v.isPreview});if("rewrite"===r.type)U={...U,...r.parsedAs.query},B=r.asPath,$=r.resolvedHref,z.pathname=r.resolvedHref,t=s.formatWithValidation(z);else{if("redirect"===r.type&&r.newAs)return this.change(e,r.newUrl,r.newAs,a);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 H=r.removePathTrailingSlash($);if(c.isDynamicRoute(H)){const e=f.parseRelativeUrl(B),r=e.pathname,o=g.getRouteRegex(H),i=p.getRouteMatcher(o)(r),a=H===r,l=a?T(H,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 (${H}). `)+"Read more: https://nextjs.org/docs/messages/"+(a?"href-interpolation-failed":"incompatible-href-as"))}else a?n=s.formatWithValidation(Object.assign({},e,{pathname:l.result,query:R(U,l.params)})):Object.assign(U,i)}F.events.emit("routeChangeStart",n,x);try{var W,V;let r=await this.getRouteInfo(H,$,U,n,B,x,v.locale,v.isPreview),{error:o,props:i,__N_SSG:l,__N_SSP:s}=r;if((l||s)&&i){if(i.pageProps&&i.pageProps.__N_REDIRECT){const t=i.pageProps.__N_REDIRECT;if(t.startsWith("/")&&!1!==i.pageProps.__N_REDIRECT_BASE_PATH){const n=f.parseRelativeUrl(t);n.pathname=I(n.pathname,D);const{url:r,as:o}=L(this,t,t);return this.change(e,r,o,a)}return window.location.href=t,new Promise((()=>{}))}if(v.isPreview=!!i.__N_PREVIEW,i.notFound===N){let e;try{await this.fetchComponent("/404"),e="/404"}catch(t){e="/_error"}r=await this.getRouteInfo(e,e,U,n,B,{shallow:!1},v.locale,v.isPreview)}}F.events.emit("beforeHistoryChange",n,x),this.changeState(e,t,n,a),a._h&&"/_error"===$&&500===(null===(W=self.__NEXT_DATA__.props)||void 0===W||null===(V=W.pageProps)||void 0===V?void 0:V.statusCode)&&(null==i?void 0:i.pageProps)&&(i.pageProps.statusCode=500);const c=a.shallow&&v.route===H;var q;const d=(null!==(q=a.scroll)&&void 0!==q?q:!c)?{x:0,y:0}:null;if(await this.set({...v,route:H,pathname:$,query:U,asPath:j,isFallback:!1},r,null!=u?u:d).catch((e=>{if(!e.cancelled)throw e;o=o||e})),o)throw F.events.emit("routeChangeError",o,j,x),o;return window.omnivoreEnv.__NEXT_I18N_SUPPORT&&v.locale&&(document.documentElement.lang=v.locale),F.events.emit("routeChangeComplete",n,x),!0}catch(e){if(i.default(e)&&e.cancelled)return!1;throw e}}changeState(e,t,n,r={}){"pushState"===e&&s.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,a,l){if(e.cancelled)throw e;if(o.isAssetError(e)||l)throw F.events.emit("routeChangeError",e,r,a),window.location.href=r,w();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(i.default(e)?e:new Error(e+""),t,n,r,a,!0)}}async getRouteInfo(e,t,n,r,o,a,l,u){try{const i=this.components[e];if(a.shallow&&i&&this.route===e)return i;let c;i&&!("initial"in i)&&(c=i);const f=c||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.page.__next_rsc__}))),{Component:d,__N_SSG:h,__N_SSP:p,__N_RSC:g}=f;let v;(h||p||g)&&(v=this.pageLoader.getDataHref({href:s.formatWithValidation({pathname:t,query:n}),asPath:o,ssg:h,rsc:g,locale:l}));const m=await this._getData((()=>h||p?z(v,this.isSsr,!1,h?this.sdc:this.sdr,!!h&&!u):this.getInitialProps(d,{pathname:t,query:n,asPath:r,locale:l,locales:this.locales,defaultLocale:this.defaultLocale})));if(g){const{fresh:e,data:t}=await this._getData((()=>this._getFlightData(v)));m.pageProps=Object.assign(m.pageProps,{__flight_serialized__:t,__flight_fresh__:e})}return f.props=m,this.components[e]=f,f}catch(e){return this.handleRouteInfoError(i.getProperError(e),t,n,r,a)}}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=f.parseRelativeUrl(e),{pathname:a,query:u}=i;if(window.omnivoreEnv.__NEXT_I18N_SUPPORT&&!1===n.locale){a=l.normalizeLocalePath(a,this.locales).pathname,i.pathname=a,e=s.formatWithValidation(i);let r=f.parseRelativeUrl(t);const o=l.normalizeLocalePath(r.pathname,this.locales);r.pathname=o.pathname,n.locale=o.detectedLocale||this.defaultLocale,t=s.formatWithValidation(r)}const c=await this.pageLoader.getPageList();let d=t;if(window.omnivoreEnv.__NEXT_HAS_REWRITES&&t.startsWith("/")){let n;({__rewrites:n}=await o.getClientBuildManifest());const r=h.default(C(_(t,this.locale)),c,n,i.query,(e=>I(e,c)),this.locales);if(r.externalDest)return;d=E(O(r.asPath),this.locale),r.matchedPage&&r.resolvedHref&&(a=r.resolvedHref,i.pathname=a,e=s.formatWithValidation(i))}else i.pathname=I(i.pathname,c),i.pathname!==a&&(a=i.pathname,i.pathname=a,e=s.formatWithValidation(i));const p=await this._preflightRequest({as:C(t),cache:!0,pages:c,pathname:a,query:u,locale:this.locale,isPreview:this.isPreview});"rewrite"===p.type&&(i.pathname=p.resolvedHref,a=p.resolvedHref,u={...u,...p.parsedAs.query},d=p.asPath,e=s.formatWithValidation(i));const g=r.removePathTrailingSlash(a);await Promise.all([this.pageLoader._isSsg(g).then((t=>!!t&&z(this.pageLoader.getDataHref({href:e,asPath:d,ssg:!0,locale:void 0!==n.locale?n.locale:this.locale}),!1,!1,this.sdc,!0))),this.pageLoader[n.priority?"loadPage":"prefetch"](g)])}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 z(e,!0,!0,this.sdc,!1).then((e=>({fresh:!0,data:e})))}async _preflightRequest(e){const t=E(k(e.as)?O(e.as):e.as,e.locale);if(!(await this.pageLoader.getMiddlewareList()).some((([e,n])=>p.getRouteMatcher(v.getMiddlewareRegex(e,!n))(t))))return{type:"next"};const n=await this._getPreflightData({preflightHref:e.as,shouldCache:e.cache,isPreview:e.isPreview});if(n.rewrite){if(!n.rewrite.startsWith("/"))return{type:"redirect",destination:e.as};const t=f.parseRelativeUrl(l.normalizeLocalePath(k(n.rewrite)?O(n.rewrite):n.rewrite,this.locales).pathname),o=r.removePathTrailingSlash(t.pathname);let i,a;return e.pages.includes(o)?(i=!0,a=o):(a=I(o,e.pages),a!==t.pathname&&e.pages.includes(a)&&(i=!0)),{type:"rewrite",asPath:t.pathname,parsedAs:t,matchedPage:i,resolvedHref:a}}if(n.redirect){if(n.redirect.startsWith("/")){const e=r.removePathTrailingSlash(l.normalizeLocalePath(k(n.redirect)?O(n.redirect):n.redirect,this.locales).pathname),{url:t,as:o}=L(this,e,e);return{type:"redirect",newUrl:t,newAs:o}}return{type:"redirect",destination:n.redirect}}return n.refresh&&!n.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,s.loadGetInitialProps(n,{AppTree:r,Component:e,router:this,ctx:t})}abortComponentLoad(e,t){this.clc&&(F.events.emit("routeChangeError",w(),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}}F.events=u.default(),t.default=F},6555:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.formatUrl=function(e){let{auth:t,hostname:n}=e,i=e.protocol||"",a=e.pathname||"",l=e.hash||"",u=e.query||"",s=!1;t=t?encodeURIComponent(t).replace(/%3A/i,":")+"@":"",e.host?s=t+e.host:n&&(s=t+(~n.indexOf(":")?`[${n}]`:n),e.port&&(s+=":"+e.port)),u&&"object"==typeof u&&(u=String(r.urlQueryToSearchParams(u)));let c=e.search||u&&`?${u}`||"";return i&&":"!==i.substr(-1)&&(i+=":"),e.slashes||(!i||o.test(i))&&!1!==s?(s="//"+(s||""),a&&"/"!==a[0]&&(a="/"+a)):s||(s=""),l&&"#"!==l[0]&&(l="#"+l),c&&"?"!==c[0]&&(c="?"+c),a=a.replace(/[?#]/g,encodeURIComponent),c=c.replace("#","%23"),`${i}${s}${a}${c}${l}`};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/},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:u,hash:s,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:u,hash:s,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.default=t.customRouteMatcherOptions=t.matcherOptions=t.pathToRegexp=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(9264));t.pathToRegexp=r;const o={sensitive:!1,delimiter:"/"};t.matcherOptions=o;const i={...o,strict:!0};t.customRouteMatcherOptions=i,t.default=(e=!1)=>(t,n)=>{const a=[];let l=r.pathToRegexp(t,a,e?i:o);if(n){const e=n(l.source);l=new RegExp(e,l.flags)}const u=r.regexpToFunction(l,a);return(t,n)=>{const r=null!=t&&u(t);if(!r)return!1;if(e)for(const e of a)"number"==typeof e.name&&delete r.params[e.name];return{...n,...r.params}}}},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}))u=r,n=n.replace(new RegExp(`:${o.escapeStringRegexp(u)}`,"g"),`__ESC_COLON_${u}`);var u;const s=i.parseUrl(n),c=s.query,f=l(`${s.pathname}${s.hash||""}`),d=l(s.hostname||""),h=[],p=[];r.pathToRegexp(f,h),r.pathToRegexp(d,p);const g=[];h.forEach((e=>g.push(e.name))),p.forEach((e=>g.push(e.name)));const v=r.compile(f,{validate:!1}),m=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=v(e.params);const[t,n]=y.split("#");s.hostname=m(e.params),s.pathname=t,s.hash=`${n?"#":""}${n||""}`,delete s.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 s.query={...t,...s.query},{newUrl:y,parsedDestination:s}};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).substr(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,r,o,f){let d,h=!1,p=!1,g=u.parseRelativeUrl(e),v=a.removePathTrailingSlash(l.normalizeLocalePath(s.delBasePath(g.pathname),f).pathname);const m=n=>{let u=c(n.source)(g.pathname);if(n.has&&u){const e=i.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(u,e):u=!1}if(u){if(!n.destination)return p=!0,!0;const c=i.prepareDestination({appendParamsToQuery:!0,destination:n.destination,params:u,query:r});if(g=c.parsedDestination,e=c.newUrl,Object.assign(r,c.parsedDestination.query),v=a.removePathTrailingSlash(l.normalizeLocalePath(s.delBasePath(e),f).pathname),t.includes(v))return h=!0,d=v,!0;if(d=o(v),d!==e&&t.includes(d))return h=!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)=>{"use strict";function n(e){return e.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&")}function r(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 o(e){const t=(e.replace(/\/$/,"")||"/").slice(1).split("/"),o={};let i=1;const a=t.map((e=>{if(e.startsWith("[")&&e.endsWith("]")){const{key:t,optional:n,repeat:a}=r(e.slice(1,-1));return o[t]={pos:i++,repeat:a,optional:n},a?n?"(?:/(.+?))?":"/(.+?)":"/([^/]+?)"}return`/${n(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},u={};return{parameterizedRoute:a,namedParameterizedRoute:t.map((e=>{if(e.startsWith("[")&&e.endsWith("]")){const{key:t,optional:n,repeat:o}=r(e.slice(1,-1));let i=t.replace(/\W/g,""),a=!1;return(0===i.length||i.length>30)&&(a=!0),isNaN(parseInt(i.substr(0,1)))||(a=!0),a&&(i=l()),u[i]=t,o?n?`(?:/(?<${i}>.+?))?`:`/(?<${i}>.+?)`:`/(?<${i}>[^/]+?)`}return`/${n(e)}`})).join(""),groups:o,routeKeys:u}}return{parameterizedRoute:a,groups:o}}Object.defineProperty(t,"__esModule",{value:!0}),t.getParametrizedRoute=o,t.getRouteRegex=function(e){const t=o(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}}},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}}},1624:(e,t,n)=>{"use strict";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=i,t.getURL=function(){const{href:e}=window.location,t=i();return e.substring(t.length)},t.getDisplayName=a,t.isResSent=l,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 r=n.res||n.ctx&&n.ctx.res;if(!t.getInitialProps)return n.ctx&&n.Component?{pageProps:await e(n.Component,n.ctx)}:{};const o=await t.getInitialProps(n);if(r&&l(r))return o;if(!o){const e=`"${a(t)}.getInitialProps()" should resolve to an object. But found "${o}" instead.`;throw new Error(e)}return o},t.formatWithValidation=function(e){return o.formatUrl(e)},t.HtmlContext=t.ST=t.SP=t.urlObjectKeys=void 0;var r=n(2784),o=n(6555);function i(){const{protocol:e,hostname:t,port:n}=window.location;return`${e}//${t}${n?":"+n:""}`}function a(e){return"string"==typeof e?e:e.displayName||e.name||"Unknown"}function l(e){return e.finished||e.headersSent}t.urlObjectKeys=["auth","hash","host","hostname","href","path","pathname","port","protocol","query","search","slashes"];const u="undefined"!=typeof performance;t.SP=u;const s=u&&"function"==typeof performance.mark&&"function"==typeof performance.measure;t.ST=s;class c extends Error{}t.DecodeError=c;const f=r.createContext(null);t.HtmlContext=f},5632:(e,t,n)=>{e.exports=n(9518)},5847:(e,t,n)=>{e.exports=n(9515)},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,u=o(e),s=1;s{"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
\ No newline at end of file diff --git a/packages/readabilityjs/test/test-pages/city-journal/source.html b/packages/readabilityjs/test/test-pages/city-journal/source.html new file mode 100644 index 000000000..d00449b87 --- /dev/null +++ b/packages/readabilityjs/test/test-pages/city-journal/source.html @@ -0,0 +1,1473 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Will Western Sanctions Accelerate Russian Realignment? | City Journal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + + +
+
+
+ ERROR +
+
+ Main Error Mesage Here +
+
+ More detailed message would go here to provide context for the user and how to proceed +
+
+
+ OKAY +
+
+
+
+
+ ERROR +
+
+ Main Error Mesage Here +
+
+ More detailed message would go here to provide context for the user and how to proceed +
+
+ +
+
+
+ +
+ +
+ search + +
+ +
+
+ +
+
+
+ Close Nav +
+

+ Search +

+ + + + Close Search + +
+
+
+
+
+

+ The Great Eurasian Economic Realignment +

back to top +
+
+
+
+ +
+
+
+ eye on the news +

+ The Great Eurasian Economic Realignment +

Western sanctions may accelerate Russia’s economic integration with Asia. + March 17, 2022 +
+
+ Politics and law +
+
+ Economy, finance, and budgets +
+
+
+
+
+
+
+
+ + +
+ + +

+ Since Vladimir Putin ordered the Russian Armed Forces to launch a full-scale invasion of Ukraine on February 24, Western countries and their close allies have hit Russia with a raft of sweeping and unprecedented sanctions. Several major Russian banks were cut off from the Belgium-based SWIFT international financial messaging system. Transactions with Russia’s central bank were banned by the United States, United Kingdom, and European Union, effectively preventing Russia from utilizing much of its $640 billion of foreign exchange reserves. Targeted asset freezes and seizures were carried out against high-level members of Russia’s political and economic elite, Russian planes were denied access to essentially all European and North American airspace, and an unofficial boycott of Russia has seen over 300 international businesses suspend operations in or with the country, including major brands such as Apple, Disney, Zara, Visa, IKEA, and Coca-Cola. The Russian ruble has lost about 40 percent of its value against the U.S. dollar in a matter of weeks. +

+

+ This kind of economic warfare is designed to isolate Putin’s government and bring an end to the invasion. But Russia’s vast exports of oil, coal, and natural gas to Europe have been notably exempt from the sanctions so far. Europe heavily relies on Russia to meet its energy needs: Russia alone supplies 40 percent of the European Union’s natural gas imports, 46 percent of EU coal imports, and 27 percent of EU oil imports. Russia is also the world’s largest wheat exporter, a leading producer of fertilizer, and possesses vast mineral reserves. The sanctions and boycotts against Russia are thus perhaps better termed financial warfare. Despite the belief that financial pain for the Russian populace will translate into political change, the nature of the Russian government remains the same. And while such bureaucratic and accounting moves can certainly cause significant pain in the short and medium term, they cannot change the fact that Russia possesses valuable and useful natural resources. +

+

+ But as the energy-independent U.S. pushes for energy sanctions, Germany in particular finds itself in an increasingly difficult position. For decades, Germany’s political and business elites have been implementing a wide-ranging transition from reliance on fossil fuels and nuclear energy to wind, solar, and biofuels. This energy transition, called the Energiewende in German, was always a precariously ambitious strategy for Europe’s premier industrial and manufacturing power. Heavy industry requires consistent and abundant energy—something that photovoltaic solar panels could provide in theory but not in dim and cloudy Germany. With its nuclear plants now almost entirely shuttered, Germany’s energy transition has instead resulted in a reliance on Russian energy significantly higher than the EU average, with 55 percent of natural gas imports coming from Russia in 2020. +

+

+ Will that change? In the wake of Russia’s invasion of Ukraine, the German government briefly floated the possibility of delaying shutdowns of its few remaining nuclear plants, before ultimately deciding against it and instead emphasizing plans to increase liquefied natural gas (LNG) imports and development of renewables. While the U.S. has announced plans to ban Russian energy imports immediately, new European plans to reduce energy imports from Russia are less ambitious on paper but could prove harder to achieve. +

+

+ Political considerations may ultimately outweigh economic ones in Europe. All that is stopping European countries from eventually reducing energy imports from Russia to zero is their willingness to pay the costs of higher energy import prices, such as by importing LNG from the United States, the Middle East, or Africa, or by continuing to subsidize the construction and operation of renewable-energy installations. Much as Westerners overrate the effects of economic hardship on political outcomes in Russia, Iran, or North Korea, it would be a mistake for Russia to overrate the effects of economic hardship on political outcomes in Europe. For while Russia is Europe’s largest energy supplier, Europe is Russia’s largest trade partner. The complete loss of this trade relationship would have far harsher impacts on Russia’s economy and state coffers than even the current round of financial sanctions. +

+

+ What has not been widely considered, however, is the possibility that Russia welcomes this outcome. If Russia is betting on economic divorce from Europe, including in energy, then sanctions and boycotts counterintuitively support, rather than frustrate, Russian strategy. Indeed, Putin’s government is already planning to retaliate with economic warfare of its own: it has prepared its own list of “unfriendly” countries to which to ban exports of natural resources, and has threatened to shut down gas to Germany via the Nord Stream 1 pipeline in retaliation for Germany halting the certification of Nord Stream 2. +

+

+ Perhaps these moves can be written off as symbolic reactions, but they may fit a broader strategy of replacing Europe with Asia as Russia’s key customer for natural resources. Russia has been heavily investing in development and infrastructure in its Arctic and subarctic regions, which produce an overwhelming majority of its fossil fuels and a substantial portion of its valuable minerals. The Russian government has gone so far as to plan a “Northern Sea Route” to Asia across Russia’s vast Arctic coastline, normally considered far too icy and dangerous for regular maritime shipping. Such a route could prove geopolitically vital, even if uneconomical, in case Russian ships are denied access to Western ports, as the United Kingdom has already done. To this end, Russia has also revived its fleet of nuclear-powered icebreakers and built the world’s first floating nuclear power station to supply electricity to remote regions of Siberia, with more of each on the way. +

+

+ Russia already depends on natural resource exports. That won’t change. But over the coming decades, it will not be the stagnant economies of Europe that will have the greatest demand for energy and minerals. Rather, the maturing industrial economy of China, in addition to the still-industrializing economies of India and other Asian and African countries, will be the primary growth markets for Russia’s natural resources. Neither China, India, Indonesia, the Gulf States, nor any of the many countries in Africa and Latin America chose to sanction Russia over the invasion of Ukraine. Today’s economic disassociation may well have unintended consequences for Europe. +

+ +
+

+ Photo by ALEXEY DRUZHININ/SPUTNIK/AFP via Getty Images +

+
+
+ +
+
+
+ +
+
+ +
+ +
+
+
+ +
+ + +
+ +
+
+ Up Next + +
+ eye on the news +

+ Putin’s Bet +

Despite early struggles, a determined Ukrainian defense, and widespread condemnation, the Russian campaign in Ukraine may yet achieve its objectives. +
Samo Burja March 1, 2022 +
+
+ Politics and law +
+
+
+
+
+
+ +
+ +
+
+
+ + + + + +
+

+ Contact +

+
+

+ Send a question or comment using the form below. This message may be routed through support staff. +

+
+
+
+ +
+ + +
+
+
+
+ +
+ +
+
+
+
+
+ Cancel +
+
+
+
+
+
+
+
+
+ Saved! +
Close + +
+
+
+ + + + + + + + + + + +
+
+
+
+
+ +
+
+ + +
+
+
+ + diff --git a/packages/readabilityjs/test/test-pages/city-journal/url.txt b/packages/readabilityjs/test/test-pages/city-journal/url.txt new file mode 100644 index 000000000..1797b27de --- /dev/null +++ b/packages/readabilityjs/test/test-pages/city-journal/url.txt @@ -0,0 +1 @@ +https://www.city-journal.org/will-western-sanctions-accelerate-russian-realignment?wallit_nosession=1 \ No newline at end of file diff --git a/packages/readabilityjs/test/test-pages/gflownet/expected-metadata.json b/packages/readabilityjs/test/test-pages/gflownet/expected-metadata.json new file mode 100644 index 000000000..81947a8a7 --- /dev/null +++ b/packages/readabilityjs/test/test-pages/gflownet/expected-metadata.json @@ -0,0 +1,9 @@ +{ + "title": "Flow Network based Generative Models for Non-Iterative Diverse Candidate Generation", + "byline": null, + "dir": null, + "excerpt": "What follows is a high-level overview of this work, for more details refer to our paper. Given a reward and a deterministic episodic environment where episodes end with a ``generate '' action, how do we generate diverse and high-reward s?\n We propose to use Flow Networks to model discrete from which we can sample sequentially (like episodic RL, rather than iteratively as MCMC methods would). We show that our method, GFlowNet, is very useful on a combinatorial domain, drug molecule synthesis, because unlike RL methods it generates diverse s by design.", + "siteName": null, + "publishedDate": null, + "readerable": true +} diff --git a/packages/readabilityjs/test/test-pages/gflownet/expected.html b/packages/readabilityjs/test/test-pages/gflownet/expected.html new file mode 100644 index 000000000..b7ba06fcf --- /dev/null +++ b/packages/readabilityjs/test/test-pages/gflownet/expected.html @@ -0,0 +1,54 @@ +
+
+
+ [Home] +
+
+ Emmanuel Bengio, Moksh Jain, Maksym Korablyov, Doina Precup, Yoshua Bengio +

+
+ arXiv preprint, code
also see the GFlowNet Foundations paper
and a more recent (and thorough) tutorial on the framework. +
+

What follows is a high-level overview of this work, for more details refer to our paper. Given a reward and a deterministic episodic environment where episodes end with a ``generate '' action, how do we generate diverse and high-reward s?
We propose to use Flow Networks to model discrete from which we can sample sequentially (like episodic RL, rather than iteratively as MCMC methods would). We show that our method, GFlowNet, is very useful on a combinatorial domain, drug molecule synthesis, because unlike RL methods it generates diverse s by design.
+ +

+

Flow Networks

+

A flow network is a directed graph with sources and sinks, and edges carrying some amount of flow between them through intermediate nodes -- think of pipes of water. For our purposes, we define a flow network with a single source, the root or ; the sinks of the network correspond to the terminal states. We'll assign to each sink an ``out-flow'' .

+
+
+

Given the graph structure and the out-flow of the sinks, we wish to calculate a valid flow between nodes, e.g. how much water each pipe is carrying. Generally there can be infinite solutions, but this is not a problem here -- any valid solution will do. For example above, there is almost no flow between and that goes through , it all goes through , but the reverse solution would also be a valid flow.
Why is this useful? Such a construction corresponds to a generative model. If we follow the flow, we'll end up in a terminal state, a sink, with probability . On top of that, we'll have the property that the in-flow of --the flow of the unique source--is , the partition function. If we assign to each intermediate node a state and to each edge an action, we recover a useful MDP.
Let be the flow between and , where , i.e. is the (deterministic) state transitioned to from state and action . Let then following policy , starting from , leads to terminal state with probability (see the paper for proofs and more rigorous explanations).
+ +

+

Approximating Flow Networks

+

As you may suspect, there are only few scenarios in which we can build the above graph explicitly. For drug-like molecules, it would have around nodes!
Instead, we resort to function approximation, just like deep RL resorts to it when computing the (action-)value functions of MDPs.
Our goal here is to approximate the flow . Earlier we called a valid flow one that correctly routed all the flow from the source to the sinks through the intermediary nodes. Let's be more precise. For some node , let the in-flow be the sum of incoming flows: Here the set is the set of state-action pairs that lead to . Now, let the out-flow be the sum of outgoing flows--or the reward if is terminal: Note that we reused . This is because for a valid flow, the in-flow is equal to the out-flow, i.e. the flow through , . Here is the set of valid actions in state , which is the empty set when is a sink. is 0 unless is a sink, in which case .
We can thus call the set of these equalities for all states the flow consistency equations:

+
+
+

Here the set of parents is , and .
By now our RL senses should be tingling. We've defined a value function recursively, with two quantities that need to match.
+ +

+

A TD-Like Objective

+

Just like one can cast the Bellman equations into TD objectives, so do we cast the flow consistency equations into an objective. We want that minimizes the square difference between the two sides of the equations, but we add a few bells and whistles: First, we match the of each side, which is important since as intermediate nodes get closer to the root, their flow will become exponentially bigger (remember that ), but we care equally about all nodes. Second, we predict for the same reasons. Finally, we add an value inside the ; this doesn't change the minima of the objective, but gives more gradient weight to large values and less to small values.
We show in the paper that a minimizer of this objective achieves our desiderata, which is to have when sampling from as defined above.
+ +

+

GFlowNet as Amortized Sampling with an OOD Potential

+

It is interesting to compare GFlowNet with Monte-Carlo Markov Chain (MCMC) methods. MCMC methods can be used to sample from a distribution for which there is no analytical sampling formula but an energy function or unnormalized probability function is available. In our context, this unnormalized probability function is our reward function .
Like MCMC methods, GFlowNet can turn a given energy function into samples but it does it in an amortized way, converting the cost a lot of very expensive MCMC trajectories (to obtain each sample) into the cost training a generative model (in our case a generative policy which sequentially builds up ). Sampling from the generative model is then very cheap (e.g. adding one component at a time to a molecule) compared to an MCMC. But the most important gain may not be just computational, but in terms of the ability to discover new modes of the reward function.
MCMC methods are iterative, making many small noisy steps, which can converge in the neighborhood of a mode, and with some probability jump from one mode to a nearby one. However, if two modes are far from each other, MCMC can require exponential time to mix between the two. If in addition the modes occupy a tiny volume of the state space, the chances of initializing a chain near one of the unknown modes is also tiny, and the MCMC approach becomes unsatisfactory. Whereas such a situation seems hopeless with MCMC, GFlowNet has the potential to discover modes and jump there directly, if there is structure that relates the modes that it already knows, and if its inductive biases and training procedure make it possible to generalize there.
GFlowNet does not need to perfectly know where the modes are: it is sufficient to make guesses which occasionally work well. Like for MCMC methods, once a point in the region of new mode is discovered, further training of GFlowNet will sculpt that mode and zoom in on its peak.
Note that we can put to some power , a coefficient which acts like a temperature, and , making it possible to focus more or less on the highest modes (versus spreading probability mass more uniformly).
+ +

+

Generating molecule graphs

+

The motivation for this work is to be able to generate diverse molecules from a proxy reward that is imprecise because it comes from biochemical simulations that have a high uncertainty. As such, we do not care about the maximizer as RL methods would, but rather about a set of ``good enough'' candidates to send to a true biochemical assay.
Another motivation is to have diversity: by fitting the distribution of rewards rather than trying to maximize the expected reward, we're likely to find more modes than if we were being greedy after having found a good enough mode, which again and again we've found RL methods such as PPO to do.
Here we generate molecule graphs via a sequence of additive edits, i.e. we progressively build the graph by adding new leaf nodes to it. We also create molecules block-by-block rather than atom-by-atom.
We find experimentally that we get both good molecules, and diverse ones. We compare ourselves to PPO and MARS (an MCMC-based method).
Figure 3 shows that we're fitting a distribution that makes sense. If we change the reward by exponentiating it as with , this shifts the reward distribution to the right.
Figure 4 shows the top- found as a function of the number of episodes.

+
+ +
+

Finally, Figure 5 shows that using a biochemical measure of diversity to estimate the number of distinct modes found, GFlowNet finds much more varied candidates.

+
+ +

+

Active Learning experiments

+

The above experiments assume access to a reward that is cheap to evaluate. In fact it uses a neural network proxy trained from a large dataset of molecules. This setup isn't quite what we would get when interacting with biochemical assays, where we'd have access to much fewer data. To emulate such a setting, we consider our oracle to be a docking simulation (which is relatively expensive to run, ~30 cpu seconds).
In this setting, there is a limited budget for calls to the true oracle . We use a proxy initialized by training on a limited dataset of pairs , where is the true reward from the oracle. The generative model () is then trained to fit but as predicted by the proxy . We then sample a batch where , which is evaluated with the oracle . The proxy is updated with this newly acquired and labeled batch, and the process is repeated for iterations.
By doing this on the molecule setting we again find that we can generate better molecules. This showcases the importance of having these diverse candidates.

+
+ +
+

For more figures, experiments and explanations, check out the paper, or reach out to us!
+

+
+
\ No newline at end of file diff --git a/packages/readabilityjs/test/test-pages/gflownet/source.html b/packages/readabilityjs/test/test-pages/gflownet/source.html new file mode 100644 index 000000000..add15489a --- /dev/null +++ b/packages/readabilityjs/test/test-pages/gflownet/source.html @@ -0,0 +1,3142 @@ + + + + + + Flow Network based Generative Models for Non-Iterative Diverse Candidate Generation + + + + + + + + + +
+
+ [Home] +
+

+ Flow Network based Generative Models for Non-Iterative Diverse Candidate Generation +

+
+ Emmanuel Bengio, Moksh Jain, Maksym Korablyov, Doina Precup, Yoshua Bengio +

+
+ arXiv preprint, code
+ also see the GFlowNet Foundations paper
+ and a more recent (and thorough) tutorial on the framework. +

+ What follows is a high-level overview of this work, for more details refer to our paper. Given a reward + + + + R + + + ( + + + x + + + ) + + + + R(x) + + and a deterministic episodic environment where episodes end with a ``generate + + + + x + + + + x + + '' action, how do we generate diverse and high-reward + + + + x + + + + x + + s?
+ We propose to use Flow Networks to model discrete + + + + p + + + ( + + + x + + + ) + + + ∝ + + + R + + + ( + + + x + + + ) + + + + p(x) \propto R(x) + + from which we can sample sequentially (like episodic RL, rather than iteratively as MCMC methods would). We show that our method, GFlowNet, is very useful on a combinatorial domain, drug molecule synthesis, because unlike RL methods it generates diverse + + + + x + + + + x + + s by design.
+ +

+ Flow Networks +

A flow network is a directed graph with sources and sinks, and edges carrying some amount of flow between them through intermediate nodes -- think of pipes of water. For our purposes, we define a flow network with a single source, the root or + + + + + s + + + 0 + + + + + s_0 + + ; the sinks of the network correspond to the terminal states. We'll assign to each sink + + + + x + + + + x + + an ``out-flow'' + + + + R + + + ( + + + x + + + ) + + + + R(x) + + .
+
+
+
+ + + + + + s + + + 0 + + + + + s_{0} + + + + + + + s + + + 1 + + + + + s_{1} + + + + + + + s + + + 2 + + + + + s_{2} + + + + + + + s + + + 3 + + + + + s_{3} + + + + + + + x + + + 3 + + + + + x_{3} + + + + + + ⊤ + + + + \top + + + + + + + s + + + 5 + + + + + s_{5} + + + + + + + x + + + 5 + + + + + x_{5} + + + + + + ⊤ + + + + \top + + + + + + + s + + + 7 + + + + + s_{7} + + + + + + + s + + + 8 + + + + + s_{8} + + + + + + + x + + + 8 + + + + + x_{8} + + + + + + ⊤ + + + + \top + + + + + + + s + + + 10 + + + + + s_{10} + + + + + + + s + + + 11 + + + + + s_{11} + + + + + + + x + + + 11 + + + + + x_{11} + + + + + + ⊤ + + + + \top + + + + + + + s + + + 13 + + + + + s_{13} + + + + + + + x + + + 13 + + + + + x_{13} + + + + + + ⊤ + + + + \top + + + + + + + s + + + 15 + + + + + s_{15} + + + + + + + s + + + 16 + + + + + s_{16} + + + + + + + x + + + 16 + + + + + x_{16} + + + + + + ⊤ + + + + \top + + +
+
+ +
Given the graph structure and the out-flow of the sinks, we wish to calculate a valid flow between nodes, e.g. how much water each pipe is carrying. Generally there can be infinite solutions, but this is not a problem here -- any valid solution will do. For example above, there is almost no flow between + + + + + s + + + 7 + + + + + s_7 + + and + + + + + s + + + 13 + + + + + s_{13} + + that goes through + + + + + s + + + 11 + + + + + s_{11} + + , it all goes through + + + + + s + + + 10 + + + + + s_{10} + + , but the reverse solution would also be a valid flow.
+ Why is this useful? Such a construction corresponds to a generative model. If we follow the flow, we'll end up in a terminal state, a sink, with probability + + + + p + + + ( + + + x + + + ) + + + ∝ + + + R + + + ( + + + x + + + ) + + + + p(x) \propto R(x) + + . On top of that, we'll have the property that the in-flow of + + + + + s + + + 0 + + + + + s_0 + + --the flow of the unique source--is + + + + + ∑ + + + x + + + + R + + + ( + + + x + + + ) + + + = + + + Z + + + + \sum_x R(x)=Z + + , the partition function. If we assign to each intermediate node a state and to each edge an action, we recover a useful MDP.
+ Let + + + + F + + + ( + + + s + + + , + + + a + + + ) + + + = + + + f + + + ( + + + s + + + , + + + + s + + + ′ + + + + ) + + + + F(s,a)=f(s,s') + + be the flow between + + + + s + + + + s + + and + + + + + s + + + ′ + + + + + s' + + , where + + + + T + + + ( + + + s + + + , + + + a + + + ) + + + = + + + + s + + + ′ + + + + + T(s,a)=s' + + , i.e. + + + + + s + + + ′ + + + + + s' + + is the (deterministic) state transitioned to from state + + + + s + + + + s + + and action + + + + a + + + + a + + . Let + + + + + + + + π + + + ( + + + a + + + ∣ + + + s + + + ) + + + = + + + + + F + + + ( + + + s + + + , + + + a + + + ) + + + + + + ∑ + + + + a + + + ′ + + + + + F + + + ( + + + s + + + , + + + + a + + + ′ + + + + ) + + + + + + + + + + \begin{aligned}\pi(a|s) = \frac{F(s,a)}{\sum_{a'}F(s,a')}\end{aligned} + + then following policy + + + + π + + + + \pi + + , starting from + + + + + s + + + 0 + + + + + s_0 + + , leads to terminal state + + + + x + + + + x + + with probability + + + + R + + + ( + + + x + + + ) + + + + R(x) + + (see the paper for proofs and more rigorous explanations).
+ +

+ Approximating Flow Networks +

As you may suspect, there are only few scenarios in which we can build the above graph explicitly. For drug-like molecules, it would have around + + + + 1 + + + + 0 + + + 16 + + + + + 10^{16} + + nodes!
+ Instead, we resort to function approximation, just like deep RL resorts to it when computing the (action-)value functions of MDPs.
+ Our goal here is to approximate the flow + + + + F + + + ( + + + s + + + , + + + a + + + ) + + + + F(s,a) + + . Earlier we called a valid flow one that correctly routed all the flow from the source to the sinks through the intermediary nodes. Let's be more precise. For some node + + + + + s + + + ′ + + + + + s' + + , let the in-flow + + + + F + + + ( + + + + s + + + ′ + + + + ) + + + + F(s') + + be the sum of incoming flows: + + + + + + + + F + + + ( + + + + s + + + ′ + + + + ) + + + = + + + + ∑ + + + + s + + + , + + + a + + + : + + + T + + + ( + + + s + + + , + + + a + + + ) + + + = + + + + s + + + ′ + + + + + + F + + + ( + + + s + + + , + + + a + + + ) + + + + + + + + \begin{aligned}F(s') = \sum_{s,a:T(s,a)=s'} F(s,a)\end{aligned} + + Here the set + + + + { + + + s + + + , + + + a + + + : + + + T + + + ( + + + s + + + , + + + a + + + ) + + + = + + + + s + + + ′ + + + + } + + + + \{s,a:T(s,a)=s'\} + + is the set of state-action pairs that lead to + + + + + s + + + ′ + + + + + s' + + . Now, let the out-flow be the sum of outgoing flows--or the reward if + + + + + s + + + ′ + + + + + s' + + is terminal: + + + + + + + + F + + + ( + + + + s + + + ′ + + + + ) + + + = + + + R + + + ( + + + + s + + + ′ + + + + ) + + + + + + + + ∑ + + + + + a + + + ′ + + + + ∈ + + + A + + + ( + + + + s + + + ′ + + + + ) + + + + + F + + + ( + + + + s + + + ′ + + + + , + + + + a + + + ′ + + + + ) + + + . + + + + + + + + \begin{aligned}F(s') = R(s') + \sum_{a'\in\mathcal{A}(s')} F(s',a').\end{aligned} + + Note that we reused + + + + F + + + ( + + + + s + + + ′ + + + + ) + + + + F(s') + + . This is because for a valid flow, the in-flow is equal to the out-flow, i.e. the flow through + + + + + s + + + ′ + + + + + s' + + , + + + + F + + + ( + + + + s + + + ′ + + + + ) + + + + F(s') + + . Here + + + + A + + + ( + + + s + + + ) + + + + \mathcal{A}(s) + + is the set of valid actions in state + + + + s + + + + s + + , which is the empty set when + + + + s + + + + s + + is a sink. + + + + R + + + ( + + + s + + + ) + + + + R(s) + + is 0 unless + + + + s + + + + s + + is a sink, in which case + + + + R + + + ( + + + s + + + ) + + + > + + + 0 + + + + R(s)>0 + + .
+ We can thus call the set of these equalities for all states + + + + + s + + + ′ + + + + ≠ + + + + s + + + 0 + + + + + s'\neq s_0 + + the flow consistency equations: + + + + + + + + + ∑ + + + + s + + + , + + + a + + + : + + + T + + + ( + + + s + + + , + + + a + + + ) + + + = + + + + s + + + ′ + + + + + + F + + + ( + + + s + + + , + + + a + + + ) + + + = + + + R + + + ( + + + + s + + + ′ + + + + ) + + + + + + + + ∑ + + + + + a + + + ′ + + + + ∈ + + + A + + + ( + + + + s + + + ′ + + + + ) + + + + + F + + + ( + + + + s + + + ′ + + + + , + + + + a + + + ′ + + + + ) + + + . + + + + + + + + \begin{aligned}\sum_{s,a:T(s,a)=s'} F(s,a) = R(s') + \sum_{a'\in\mathcal{A}(s')} F(s',a').\end{aligned} + + +
+
+
+ + + + + + a + + + 1 + + + + + a_1 + + + + + + + a + + + 7 + + + + + a_7 + + + + + + + a + + + 3 + + + + + a_3 + + + + + + + a + + + 4 + + + + + a_4 + + + + + + + a + + + 2 + + + + + a_2 + + + + + + + a + + + 8 + + + + + a_8 + + + + + + + s + + + 0 + + + + + s_{0} + + + + + + + s + + + 1 + + + + + s_{1} + + + + + + + s + + + 2 + + + + + s_{2} + + + + + + + s + + + 3 + + + + + s_{3} + + + + + + + s + + + 4 + + + + + s_{4} + + + + + + + s + + + 5 + + + + + s_{5} + + + + + + + s + + + 6 + + + + + s_{6} + + +
+
+ +
Here the set of parents + + + + { + + + s + + + , + + + a + + + : + + + T + + + ( + + + s + + + , + + + a + + + ) + + + = + + + + s + + + 3 + + + + } + + + + \{s,a:T(s,a)=s_3\} + + is + + + + { + + + ( + + + + s + + + 0 + + + + , + + + + a + + + 1 + + + + ) + + + , + + + ( + + + + s + + + 1 + + + + , + + + + a + + + 7 + + + + ) + + + , + + + ( + + + + s + + + 2 + + + + , + + + + a + + + 3 + + + + ) + + + } + + + + \{(s_0, a_1), (s_1, a_7), (s_2, a_3)\} + + , and + + + + A + + + ( + + + + s + + + 3 + + + + ) + + + = + + + { + + + + a + + + 2 + + + + , + + + + a + + + 4 + + + + , + + + + a + + + 8 + + + + } + + + + \mathcal{A}(s_3)=\{a_2,a_4,a_8\} + + .
+ By now our RL senses should be tingling. We've defined a value function recursively, with two quantities that need to match.
+ +

+ A TD-Like Objective +

Just like one can cast the Bellman equations into TD objectives, so do we cast the flow consistency equations into an objective. We want + + + + + F + + + θ + + + + + F_\theta + + that minimizes the square difference between the two sides of the equations, but we add a few bells and whistles: + + + + + + + + + L + + + + θ + + + , + + + ϵ + + + + + ( + + + τ + + + ) + + + = + + + + ∑ + + + + + + s + + + ′ + + + + ∈ + + + τ + + + ≠ + + + + s + + + 0 + + + + + + +   + + + + + ( + + + log + + + ⁡ + + +  ⁣ + + + + [ + + + ϵ + + + + + + + + ∑ + + + + + s + + + , + + + a + + + : + + + T + + + ( + + + s + + + , + + + a + + + ) + + + = + + + + s + + + ′ + + + + + + + exp + + + ⁡ + + + + F + + + θ + + + log + + + ⁡ + + + + ( + + + s + + + , + + + a + + + ) + + + ] + + + + − + + + log + + + ⁡ + + +  ⁣ + + + + [ + + + ϵ + + + + + + + R + + + ( + + + + s + + + ′ + + + + ) + + + + + + + + ∑ + + + + + + a + + + ′ + + + + ∈ + + + A + + + ( + + + + s + + + ′ + + + + ) + + + + + + exp + + + ⁡ + + + + F + + + θ + + + log + + + ⁡ + + + + ( + + + + s + + + ′ + + + + , + + + + a + + + ′ + + + + ) + + + ] + + + + ) + + + + 2 + + + + . + + + + + + + + \begin{aligned}\mathcal{L}_{\theta,\epsilon}(\tau) = \sum_{\mathclap{s'\in\tau\neq s_0}}\,\left(\log\! \left[\epsilon+{\sum_{\mathclap{s,a:T(s,a)=s'}}} \exp F^{\log}_\theta(s,a)\right]- \log\! \left[\epsilon + R(s') + \sum_{\mathclap{a'\in{\cal A}(s')}} \exp F^{\log}_\theta(s',a')\right]\right)^2.\end{aligned} + + First, we match the + + + + log + + + ⁡ + + + + \log + + of each side, which is important since as intermediate nodes get closer to the root, their flow will become exponentially bigger (remember that + + + + F + + + ( + + + + s + + + 0 + + + + ) + + + = + + + Z + + + = + + + + ∑ + + + x + + + + R + + + ( + + + x + + + ) + + + + F(s_0) = Z = \sum_x R(x) + + ), but we care equally about all nodes. Second, we predict + + + + + F + + + θ + + + log + + + ⁡ + + + + ≈ + + + log + + + ⁡ + + + F + + + + F^{\log}_\theta\approx\log F + + for the same reasons. Finally, we add an + + + + ϵ + + + + \epsilon + + value inside the + + + + log + + + ⁡ + + + + \log + + ; this doesn't change the minima of the objective, but gives more gradient weight to large values and less to small values.
+ We show in the paper that a minimizer of this objective achieves our desiderata, which is to have + + + + p + + + ( + + + x + + + ) + + + ∝ + + + R + + + ( + + + x + + + ) + + + + p(x)\propto R(x) + + when sampling from + + + + π + + + ( + + + a + + + ∣ + + + s + + + ) + + + + \pi(a|s) + + as defined above.
+ +

+ GFlowNet as Amortized Sampling with an OOD Potential +

It is interesting to compare GFlowNet with Monte-Carlo Markov Chain (MCMC) methods. MCMC methods can be used to sample from a distribution for which there is no analytical sampling formula but an energy function or unnormalized probability function is available. In our context, this unnormalized probability function is our reward function + + + + R + + + ( + + + x + + + ) + + + = + + + + e + + + + − + + + e + + + n + + + e + + + r + + + g + + + y + + + ( + + + x + + + ) + + + + + + R(x)=e^{-energy(x)} + + .
+ Like MCMC methods, GFlowNet can turn a given energy function into samples but it does it in an amortized way, converting the cost a lot of very expensive MCMC trajectories (to obtain each sample) into the cost training a generative model (in our case a generative policy which sequentially builds up + + + + x + + + + x + + ). Sampling from the generative model is then very cheap (e.g. adding one component at a time to a molecule) compared to an MCMC. But the most important gain may not be just computational, but in terms of the ability to discover new modes of the reward function.
+ MCMC methods are iterative, making many small noisy steps, which can converge in the neighborhood of a mode, and with some probability jump from one mode to a nearby one. However, if two modes are far from each other, MCMC can require exponential time to mix between the two. If in addition the modes occupy a tiny volume of the state space, the chances of initializing a chain near one of the unknown modes is also tiny, and the MCMC approach becomes unsatisfactory. Whereas such a situation seems hopeless with MCMC, GFlowNet has the potential to discover modes and jump there directly, if there is structure that relates the modes that it already knows, and if its inductive biases and training procedure make it possible to generalize there.
+ GFlowNet does not need to perfectly know where the modes are: it is sufficient to make guesses which occasionally work well. Like for MCMC methods, once a point in the region of new mode is discovered, further training of GFlowNet will sculpt that mode and zoom in on its peak.
+ Note that we can put + + + + R + + + ( + + + x + + + ) + + + + R(x) + + to some power + + + + β + + + + \beta + + , a coefficient which acts like a temperature, and + + + + R + + + ( + + + x + + + + ) + + + β + + + + = + + + + e + + + + − + + + β + + +    + + + e + + + n + + + e + + + r + + + g + + + y + + + ( + + + x + + + ) + + + + + + R(x)^\beta = e^{-\beta\; energy(x)} + + , making it possible to focus more or less on the highest modes (versus spreading probability mass more uniformly).
+ +

+ Generating molecule graphs +

The motivation for this work is to be able to generate diverse molecules from a proxy reward + + + + R + + + + R + + that is imprecise because it comes from biochemical simulations that have a high uncertainty. As such, we do not care about the maximizer as RL methods would, but rather about a set of ``good enough'' candidates to send to a true biochemical assay.
+ Another motivation is to have diversity: by fitting the distribution of rewards rather than trying to maximize the expected reward, we're likely to find more modes than if we were being greedy after having found a good enough mode, which again and again we've found RL methods such as PPO to do.
+ Here we generate molecule graphs via a sequence of additive edits, i.e. we progressively build the graph by adding new leaf nodes to it. We also create molecules block-by-block rather than atom-by-atom.
+ We find experimentally that we get both good molecules, and diverse ones. We compare ourselves to PPO and MARS (an MCMC-based method).
+ Figure 3 shows that we're fitting a distribution that makes sense. If we change the reward by exponentiating it as + + + + + R + + + β + + + + + R^\beta + + with + + + + β + + + > + + + 1 + + + + \beta>1 + + , this shifts the reward distribution to the right.
+ Figure 4 shows the top- + + + + k + + + + k + + found as a function of the number of episodes.
+
+ +

+ Finally, Figure 5 shows that using a biochemical measure of diversity to estimate the number of distinct modes found, GFlowNet finds much more varied candidates.
+
+ +

+ +

+ Active Learning experiments +

The above experiments assume access to a reward + + + + R + + + + R + + that is cheap to evaluate. In fact it uses a neural network proxy trained from a large dataset of molecules. This setup isn't quite what we would get when interacting with biochemical assays, where we'd have access to much fewer data. To emulate such a setting, we consider our oracle to be a docking simulation (which is relatively expensive to run, ~30 cpu seconds).
+ In this setting, there is a limited budget for calls to the true oracle + + + + O + + + + O + + . We use a proxy + + + + M + + + + M + + initialized by training on a limited dataset of + + + + ( + + + x + + + , + + + R + + + ( + + + x + + + ) + + + ) + + + + (x, R(x)) + + pairs + + + + + D + + + 0 + + + + + D_0 + + , where + + + + R + + + ( + + + x + + + ) + + + + R(x) + + is the true reward from the oracle. The generative model ( + + + + + π + + + θ + + + + + \pi_{\theta} + + ) is then trained to fit + + + + R + + + + R + + but as predicted by the proxy + + + + M + + + + M + + . We then sample a batch + + + + B + + + = + + + { + + + + x + + + 1 + + + + , + + + + x + + + 2 + + + + , + + + … + + + + x + + + k + + + + } + + + + B=\{x_1, x_2, \dots x_k\} + + where + + + + + x + + + i + + + + ∼ + + + + π + + + θ + + + + + x_i\sim \pi_{\theta} + + , which is evaluated with the oracle + + + + O + + + + O + + . The proxy + + + + M + + + + M + + is updated with this newly acquired and labeled batch, and the process is repeated for + + + + N + + + + N + + iterations.
+ By doing this on the molecule setting we again find that we can generate better molecules. This showcases the importance of having these diverse candidates.
+
+ +

+ For more figures, experiments and explanations, check out the paper, or reach out to us!
+
+
+ + diff --git a/packages/readabilityjs/test/test-pages/gflownet/url.txt b/packages/readabilityjs/test/test-pages/gflownet/url.txt new file mode 100644 index 000000000..8fe8e3548 --- /dev/null +++ b/packages/readabilityjs/test/test-pages/gflownet/url.txt @@ -0,0 +1 @@ +http://folinoid.com/w/gflownet/ \ No newline at end of file diff --git a/packages/web/components/elements/Avatar.tsx b/packages/web/components/elements/Avatar.tsx index 01b0b2332..bce69524c 100644 --- a/packages/web/components/elements/Avatar.tsx +++ b/packages/web/components/elements/Avatar.tsx @@ -29,12 +29,18 @@ const StyledAvatar = styled(Root, { verticalAlign: 'middle', overflow: 'hidden', userSelect: 'none', + border: '1px solid $grayBorder', }) const StyledImage = styled(Image, { width: '100%', height: '100%', objectFit: 'cover', + opacity: '48%', + + '&:hover': { + opacity: '100%', + }, }) const StyledFallback = styled(Fallback, { @@ -43,5 +49,8 @@ const StyledFallback = styled(Fallback, { display: 'flex', alignItems: 'center', justifyContent: 'center', - backgroundColor: 'dodgerblue', + fontSize: '$2', + fontWeight: 700, + backgroundColor: '$avatarBg', + color: '$avatarFont', }) diff --git a/packages/web/components/elements/AvatarDropdown.tsx b/packages/web/components/elements/AvatarDropdown.tsx index 04f269ac2..f369d865a 100644 --- a/packages/web/components/elements/AvatarDropdown.tsx +++ b/packages/web/components/elements/AvatarDropdown.tsx @@ -12,7 +12,7 @@ export function AvatarDropdown(props: AvatarDropdownProps): JSX.Element { diff --git a/packages/web/components/elements/DropdownElements.tsx b/packages/web/components/elements/DropdownElements.tsx index c7703b5ee..7916b3bce 100644 --- a/packages/web/components/elements/DropdownElements.tsx +++ b/packages/web/components/elements/DropdownElements.tsx @@ -63,10 +63,17 @@ const StyledLabel = styled(Label, { cursor: 'default', }) +export type DropdownAlignment = + | 'start' + | 'end' + | 'center' + type DropdownProps = { labelText?: string triggerElement: React.ReactNode children: React.ReactNode + styledArrow?: boolean + align?: DropdownAlignment } export const DropdownSeparator = styled(Separator, { @@ -102,10 +109,11 @@ export function Dropdown(props: DropdownProps): JSX.Element { // remove focus from dropdown ;(document.activeElement as HTMLElement).blur() }} + align={props.align ? props.align : 'center'} > {props.labelText && {props.labelText}} {props.children} - + {props.styledArrow && } ) diff --git a/packages/web/components/elements/Tooltip.tsx b/packages/web/components/elements/Tooltip.tsx new file mode 100644 index 000000000..3af1b84c4 --- /dev/null +++ b/packages/web/components/elements/Tooltip.tsx @@ -0,0 +1,77 @@ +import React, { FC } from 'react'; +import { styled, keyframes } from '@stitches/react'; +import * as TooltipPrimitive from '@radix-ui/react-tooltip'; + +const slideUpAndFade = keyframes({ + '0%': { opacity: 0, transform: 'translateY(2px)' }, + '100%': { opacity: 1, transform: 'translateY(0)' }, +}); + +const slideRightAndFade = keyframes({ + '0%': { opacity: 0, transform: 'translateX(-2px)' }, + '100%': { opacity: 1, transform: 'translateX(0)' }, +}); + +const slideDownAndFade = keyframes({ + '0%': { opacity: 0, transform: 'translateY(-2px)' }, + '100%': { opacity: 1, transform: 'translateY(0)' }, +}); + +const slideLeftAndFade = keyframes({ + '0%': { opacity: 0, transform: 'translateX(2px)' }, + '100%': { opacity: 1, transform: 'translateX(0)' }, +}); + +const StyledContent = styled(TooltipPrimitive.Content, { + borderRadius: 4, + padding: '8px 13px', + fontSize: 12, + color: '#FFFFFF', + backgroundColor: '#1C1C1E', + '@media (prefers-reduced-motion: no-preference)': { + animationDuration: '400ms', + animationTimingFunction: 'cubic-bezier(0.16, 1, 0.3, 1)', + animationFillMode: 'forwards', + willChange: 'transform, opacity', + '&[data-state="delayed-open"]': { + '&[data-side="top"]': { animationName: slideDownAndFade }, + '&[data-side="right"]': { animationName: slideLeftAndFade }, + '&[data-side="bottom"]': { animationName: slideUpAndFade }, + '&[data-side="left"]': { animationName: slideRightAndFade }, + }, + }, +}); + +const StyledArrow = styled(TooltipPrimitive.Arrow, { + fill: '#1C1C1E', +}); + +export const TooltipProvider = TooltipPrimitive.Provider; +export const Tooltip = TooltipPrimitive.Root; +export const TooltipTrigger = TooltipPrimitive.Trigger; +export const TooltipContent = StyledContent; +export const TooltipArrow = StyledArrow; + + +type TooltipWrappedProps = { + tooltipContent: string, + tooltipSide ?: TooltipPrimitive.TooltipContentProps['side'], + align?: TooltipPrimitive.TooltipContentProps['align'], + alignOffset?: TooltipPrimitive.TooltipContentProps['alignOffset'] + arrowStyles?: TooltipPrimitive.TooltipArrowProps['style'] + style?: TooltipPrimitive.TooltipContentProps['style'] +} + +export const TooltipWrapped: FC = ({children, tooltipContent, tooltipSide, ...props}) => { + return ( + + + {children} + + + {tooltipContent} + + + + ) +} diff --git a/packages/web/components/templates/article/Article.tsx b/packages/web/components/templates/article/Article.tsx index 60bbe1ff1..6191a8db5 100644 --- a/packages/web/components/templates/article/Article.tsx +++ b/packages/web/components/templates/article/Article.tsx @@ -1,21 +1,22 @@ -import { Box } from './../../../components/elements/LayoutPrimitives' +import { Box } from '../../elements/LayoutPrimitives' import { useReadingProgressAnchor } from '../../../lib/hooks/useReadingProgressAnchor' import { - useScrollWatcher, ScrollOffsetChangeset, + useScrollWatcher, } from '../../../lib/hooks/useScrollWatcher' import { - useRef, - useState, - useEffect, MutableRefObject, useCallback, + useEffect, + useMemo, + useRef, + useState, } from 'react' -import { articleReadingProgressMutation } from '../../../lib/networking/mutations/articleReadingProgressMutation' import { Tweet } from 'react-twitter-widgets' import { render } from 'react-dom' import { isDarkTheme } from '../../../lib/themeUpdater' -import useDebounce from '../../../lib/hooks/useDebounce' +import { debounce } from 'lodash' +import { ArticleMutations } from '../../../lib/articleActions' export type ArticleProps = { articleId: string @@ -23,12 +24,15 @@ export type ArticleProps = { initialAnchorIndex: number initialReadingProgress?: number scrollElementRef: MutableRefObject + articleMutations: ArticleMutations } export function Article(props: ArticleProps): JSX.Element { const highlightTheme = isDarkTheme() ? 'dark' : 'default' - const [readingProgress, setReadingProgress] = useState(props.initialReadingProgress) + const [readingProgress, setReadingProgress] = useState( + props.initialReadingProgress + ) const [readingAnchorIndex, setReadingAnchorIndex] = useState( props.initialAnchorIndex @@ -41,14 +45,30 @@ export function Article(props: ArticleProps): JSX.Element { useReadingProgressAnchor(articleContentRef, setReadingAnchorIndex) - const debouncedReadingProgress = useDebounce(readingProgress, 1000); + const debouncedSetReadingProgress = useMemo( + () => + debounce((readingProgress: number) => { + console.log('setReadingProgress', readingProgress) + setReadingProgress(readingProgress) + }, 2000), + [] + ) + + // Stop the invocation of the debounced function + // after unmounting + useEffect(() => { + return () => { + debouncedSetReadingProgress.cancel() + } + }, []) useEffect(() => { ;(async () => { - if (!debouncedReadingProgress) return - await articleReadingProgressMutation({ + if (!readingProgress) return + await props.articleMutations.articleReadingProgressMutation({ id: props.articleId, - readingProgressPercent: debouncedReadingProgress, + // round reading progress to 100% if more than that + readingProgressPercent: readingProgress > 100 ? 100 : readingProgress, readingProgressAnchorIndex: readingAnchorIndex, }) })() @@ -56,20 +76,16 @@ export function Article(props: ArticleProps): JSX.Element { // We don't react to changes to readingAnchorIndex we // only care about the progress (scroll position) changed. // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - props.articleId, - debouncedReadingProgress, - readingAnchorIndex, - ]) + }, [props.articleId, readingProgress]) // Post message to webkit so apple app embeds get progress updates useEffect(() => { if (typeof window?.webkit != 'undefined') { window.webkit.messageHandlers.readingProgressUpdate?.postMessage({ - progress: debouncedReadingProgress, + progress: readingProgress, }) } - }, [readingProgress, debouncedReadingProgress]) + }, [readingProgress]) const setScrollWatchedElement = useScrollWatcher( (changeset: ScrollOffsetChangeset) => { @@ -79,13 +95,13 @@ export function Article(props: ArticleProps): JSX.Element { (changeset.current.y + scrollContainer.clientHeight) / scrollContainer.scrollHeight - setReadingProgress(newReadingProgress * 100) + debouncedSetReadingProgress(newReadingProgress * 100) } else if (window && window.document.scrollingElement) { const newReadingProgress = window.scrollY / window.document.scrollingElement.scrollHeight const adjustedReadingProgress = newReadingProgress > 0.92 ? 1 : newReadingProgress - setReadingProgress(adjustedReadingProgress * 100) + debouncedSetReadingProgress(adjustedReadingProgress * 100) } }, 1000 @@ -152,9 +168,15 @@ export function Article(props: ArticleProps): JSX.Element { } if (props.scrollElementRef.current) { - props.scrollElementRef.current?.scroll(0, calculateOffset(anchorElement)) + props.scrollElementRef.current?.scroll( + 0, + calculateOffset(anchorElement) + ) } else { - window.document.documentElement.scroll(0, calculateOffset(anchorElement)) + window.document.documentElement.scroll( + 0, + calculateOffset(anchorElement) + ) } } }, [ diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx index cbe9d85ad..f68dfc600 100644 --- a/packages/web/components/templates/article/ArticleContainer.tsx +++ b/packages/web/components/templates/article/ArticleContainer.tsx @@ -19,10 +19,12 @@ import { updateThemeLocally } from '../../../lib/themeUpdater' import { EditLabelsModal } from './EditLabelsModal' import Script from 'next/script' import { useRouter } from 'next/router' +import { ArticleMutations } from '../../../lib/articleActions' type ArticleContainerProps = { viewerUsername: string article: ArticleAttributes + articleMutations: ArticleMutations scrollElementRef: MutableRefObject isAppleAppEmbed: boolean highlightBarDisabled: boolean @@ -121,12 +123,16 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element { return ( <> -