diff --git a/Fuwari.xcodeproj/project.pbxproj b/Fuwari.xcodeproj/project.pbxproj index be5a681..6587485 100644 --- a/Fuwari.xcodeproj/project.pbxproj +++ b/Fuwari.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 7E08825A1DFE246400CF0F37 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 7E0882591DFE246400CF0F37 /* dsa_pub.pem */; }; BE23E42F1DFA1623004DA6B7 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE23E42E1DFA1623004DA6B7 /* Constants.swift */; }; BE23E4321DFB8BF4004DA6B7 /* Magnet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE23E4311DFB8BF4004DA6B7 /* Magnet.framework */; }; BE5896971DFCE965007CE7AC /* LoginServiceKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE5896961DFCE965007CE7AC /* LoginServiceKit.framework */; }; @@ -16,6 +15,7 @@ BE5896A51DFD0E8C007CE7AC /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE5896A31DFD0E8C007CE7AC /* Fabric.framework */; }; BE5896A81DFD0EF0007CE7AC /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE5896A71DFD0EF0007CE7AC /* Crashlytics.framework */; }; BE5896AA1DFD1076007CE7AC /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE5896A91DFD1076007CE7AC /* Sparkle.framework */; }; + BE6473101E05FD91005E0AB0 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = BE64730F1E05FD91005E0AB0 /* dsa_pub.pem */; }; BE7E82591DF72E7A00F3F2F8 /* FullScreenWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE7E82581DF72E7A00F3F2F8 /* FullScreenWindow.swift */; }; BE7E825B1DF74A2D00F3F2F8 /* FloatWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE7E825A1DF74A2D00F3F2F8 /* FloatWindow.swift */; }; BEC844F71DED859300A4A57A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEC844F61DED859300A4A57A /* AppDelegate.swift */; }; @@ -32,7 +32,6 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 7E0882591DFE246400CF0F37 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsa_pub.pem; sourceTree = ""; }; BE23E42E1DFA1623004DA6B7 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; BE23E4311DFB8BF4004DA6B7 /* Magnet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Magnet.framework; path = Carthage/Build/Mac/Magnet.framework; sourceTree = ""; }; BE5896961DFCE965007CE7AC /* LoginServiceKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LoginServiceKit.framework; path = Carthage/Build/Mac/LoginServiceKit.framework; sourceTree = ""; }; @@ -42,6 +41,7 @@ BE5896A31DFD0E8C007CE7AC /* Fabric.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Fabric.framework; sourceTree = SOURCE_ROOT; }; BE5896A71DFD0EF0007CE7AC /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = SOURCE_ROOT; }; BE5896A91DFD1076007CE7AC /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Carthage/Build/Mac/Sparkle.framework; sourceTree = ""; }; + BE64730F1E05FD91005E0AB0 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsa_pub.pem; sourceTree = ""; }; BE7E82581DF72E7A00F3F2F8 /* FullScreenWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullScreenWindow.swift; sourceTree = ""; }; BE7E825A1DF74A2D00F3F2F8 /* FloatWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatWindow.swift; sourceTree = ""; }; BEC844F31DED859300A4A57A /* Fuwari.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Fuwari.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -160,7 +160,7 @@ isa = PBXGroup; children = ( BEC844FF1DED859300A4A57A /* Info.plist */, - 7E0882591DFE246400CF0F37 /* dsa_pub.pem */, + BE64730F1E05FD91005E0AB0 /* dsa_pub.pem */, ); name = "Supprting Files"; sourceTree = ""; @@ -228,8 +228,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + BE6473101E05FD91005E0AB0 /* dsa_pub.pem in Resources */, BEC844FB1DED859300A4A57A /* Assets.xcassets in Resources */, - 7E08825A1DFE246400CF0F37 /* dsa_pub.pem in Resources */, BE58969A1DFCEB07007CE7AC /* Localizable.strings in Resources */, BEF57FF21DFD5CB6006595B6 /* PreferencesWindowController.xib in Resources */, BEC844FE1DED859300A4A57A /* Main.storyboard in Resources */, diff --git a/Fuwari/AppDelegate.swift b/Fuwari/AppDelegate.swift index 35c2c4d..33b0677 100644 --- a/Fuwari/AppDelegate.swift +++ b/Fuwari/AppDelegate.swift @@ -25,12 +25,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { configureMenu() - if let keyCombo = KeyCombo(keyCode: kVK_ANSI_5, cocoaModifiers: [.shift, .command]) { + if let keyCombo = KeyCombo(keyCode: kVK_ANSI_6, cocoaModifiers: [.shift, .command]) { HotKey(identifier: "Capture", keyCombo: keyCombo, target: self, action: #selector(capture)).register() } - if let keyCombo = KeyCombo(keyCode: kVK_ANSI_Comma, cocoaModifiers: [.command]) { - HotKey(identifier: "Preferences", keyCombo: keyCombo, target: self, action: #selector(openPreferences)).register() - } // Show Login Item if !defaults.bool(forKey: Constants.UserDefaults.loginItem) && !defaults.bool(forKey: Constants.UserDefaults.suppressAlertForLoginItem) { @@ -61,10 +58,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { } @objc private func openPreferences() { + NSApp.activate(ignoringOtherApps: true) PreferencesWindowController.shared.showWindow(self) } @objc private func capture() { + NSApp.activate(ignoringOtherApps: true) NotificationCenter.default.post(name: NSNotification.Name(rawValue: Constants.Notification.capture), object: nil) eventMonitor = NSEvent.addGlobalMonitorForEvents(matching: [.mouseMoved, .leftMouseUp], handler: { (event: NSEvent) in diff --git a/Fuwari/CaptureGuideView.swift b/Fuwari/CaptureGuideView.swift index 94f76d6..a655fe7 100644 --- a/Fuwari/CaptureGuideView.swift +++ b/Fuwari/CaptureGuideView.swift @@ -27,6 +27,12 @@ class CaptureGuideView: NSView { private let cursorFont = NSFont.systemFont(ofSize: 10.0) private let cursorSize = CGFloat(25.0) private let cursorGuideWidth = CGFloat(1.0) + private lazy var coordinateLabelShadow: NSShadow = { + let shadow = NSShadow() + shadow.shadowColor = NSColor(red: 1, green: 1, blue: 1, alpha: 0.5) + shadow.shadowOffset = NSSize(width: 1, height: -1) + return shadow + }() override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) @@ -61,8 +67,8 @@ class CaptureGuideView: NSView { let path = NSBezierPath(ovalIn: cursorCenter) path.fill() - (Int(cursorPoint.x).description as NSString).draw(at: NSPoint(x: cursorPoint.x + cursorSize / 2, y: cursorPoint.y - cursorSize / 2), withAttributes: [NSFontAttributeName : cursorFont]) - (Int(cursorPoint.y).description as NSString).draw(at: NSPoint(x: cursorPoint.x + cursorSize / 2, y: cursorPoint.y - cursorSize), withAttributes: [NSFontAttributeName : cursorFont]) + (Int(cursorPoint.x).description as NSString).draw(at: NSPoint(x: cursorPoint.x + cursorSize / 2, y: cursorPoint.y - cursorSize / 2), withAttributes: [NSFontAttributeName : cursorFont, NSShadowAttributeName : coordinateLabelShadow]) + (Int(frame.height - cursorPoint.y).description as NSString).draw(at: NSPoint(x: cursorPoint.x + cursorSize / 2, y: cursorPoint.y - cursorSize), withAttributes: [NSFontAttributeName : cursorFont, NSShadowAttributeName : coordinateLabelShadow]) } func reset() { diff --git a/Fuwari/FloatWindow.swift b/Fuwari/FloatWindow.swift index e89312e..5945538 100644 --- a/Fuwari/FloatWindow.swift +++ b/Fuwari/FloatWindow.swift @@ -38,8 +38,25 @@ class FloatWindow: NSWindow { if event.modifierFlags.rawValue & NSEventModifierFlags.command.rawValue != 0 { switch event.keyCode { case UInt16(kVK_ANSI_S): - if let image = image { - floatDelegate?.save(floatWindow: self, image: image) + let saveLabel = NSTextField(frame: NSRect(x: 10, y: 10, width: 80, height: 26)) + saveLabel.stringValue = "Save" + saveLabel.textColor = .white + saveLabel.font = NSFont.boldSystemFont(ofSize: 20) + saveLabel.alignment = .center + saveLabel.drawsBackground = true + saveLabel.backgroundColor = NSColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.4) + saveLabel.wantsLayer = true + saveLabel.layer?.cornerRadius = 10.0 + saveLabel.isBordered = false + saveLabel.isEditable = false + saveLabel.isSelectable = false + contentView?.addSubview(saveLabel) + + DispatchQueue.main.asyncAfter(deadline: .now()) { + if let image = self.image { + saveLabel.removeFromSuperview() + self.floatDelegate?.save(floatWindow: self, image: image) + } } case UInt16(kVK_ANSI_W): fade(isIn: false) { @@ -48,6 +65,10 @@ class FloatWindow: NSWindow { default: break } + } else if event.keyCode == UInt16(kVK_Escape) { + fade(isIn: false) { + self.floatDelegate?.close(floatWindow: self) + } } } diff --git a/Fuwari/FullScreenWindow.swift b/Fuwari/FullScreenWindow.swift index 7c5f82b..8d8e77d 100644 --- a/Fuwari/FullScreenWindow.swift +++ b/Fuwari/FullScreenWindow.swift @@ -7,23 +7,19 @@ // import Cocoa +import Carbon class FullScreenWindow: NSWindow { var captureDelegate: CaptureDelegate? private lazy var captureGuideView: CaptureGuideView = { - guard let frame = NSScreen.main()?.frame else { return CaptureGuideView() } - let captureGuideView = CaptureGuideView(frame: frame) + let captureGuideView = CaptureGuideView(frame: self.frame) return captureGuideView }() override init(contentRect: NSRect, styleMask style: NSWindowStyleMask, backing bufferingType: NSBackingStoreType, defer flag: Bool) { - guard let frame = NSScreen.main()?.frame else { - super.init(contentRect: contentRect, styleMask: style, backing: bufferingType, defer: flag) - return - } - super.init(contentRect: frame, styleMask: .borderless, backing: .buffered, defer: false) + super.init(contentRect: contentRect, styleMask: .borderless, backing: .buffered, defer: false) isReleasedWhenClosed = true displaysWhenScreenProfileChanges = true @@ -38,24 +34,32 @@ class FullScreenWindow: NSWindow { contentView = captureGuideView NotificationCenter.default.addObserver(self, selector: #selector(mouseMoved(with:)), name: Notification.Name(rawValue: Constants.Notification.mouseMoved), object: nil) + + NSEvent.addLocalMonitorForEvents(matching: .keyDown) { + (event: NSEvent) -> NSEvent? in + if event.keyCode == UInt16(kVK_Escape) { + self.captureDelegate?.didCanceled() + } + return event + } } func startCapture() { orderBack(nil) - captureGuideView.cursorPoint = NSEvent.mouseLocation() + captureGuideView.cursorPoint = NSPoint(x: NSEvent.mouseLocation().x - frame.origin.x, y: NSEvent.mouseLocation().y) } override func mouseMoved(with event: NSEvent) { - captureGuideView.cursorPoint = NSEvent.mouseLocation() + captureGuideView.cursorPoint = NSPoint(x: NSEvent.mouseLocation().x - frame.origin.x, y: NSEvent.mouseLocation().y - frame.origin.y) } override func mouseDown(with event: NSEvent) { - captureGuideView.startPoint = NSEvent.mouseLocation() - captureGuideView.cursorPoint = NSEvent.mouseLocation() + captureGuideView.startPoint = NSPoint(x: NSEvent.mouseLocation().x - frame.origin.x, y: NSEvent.mouseLocation().y - frame.origin.y) + captureGuideView.cursorPoint = NSPoint(x: NSEvent.mouseLocation().x - frame.origin.x, y: NSEvent.mouseLocation().y - frame.origin.y) } override func mouseDragged(with event: NSEvent) { - captureGuideView.cursorPoint = NSEvent.mouseLocation() + captureGuideView.cursorPoint = NSPoint(x: NSEvent.mouseLocation().x - frame.origin.x, y: NSEvent.mouseLocation().y - frame.origin.y) } override func mouseUp(with event: NSEvent) { @@ -63,20 +67,38 @@ class FullScreenWindow: NSWindow { } private func capture(rect: NSRect) { - let windowId = NSApplication.shared().windows[0].windowNumber + var upRightPoint = NSPoint.zero + + NSScreen.screens()?.forEach { + upRightPoint = NSPoint(x: max(upRightPoint.x, $0.frame.origin.x + $0.frame.width), y: max(upRightPoint.y, $0.frame.origin.y + $0.frame.height)) + } - let cgRect = CGRect(x: rect.origin.x, y: frame.height - rect.origin.y - rect.height, width: rect.width, height: rect.height) - guard let cgImage = CGWindowListCreateImage(cgRect, .optionOnScreenBelowWindow, CGWindowID(windowId), .bestResolution) else { + var originY = CGFloat(0) + if frame.origin.y > 0 { + originY = upRightPoint.y - frame.origin.y - frame.height - (rect.height + rect.origin.y) + } else { + originY = NSScreen.main()!.frame.height - frame.origin.y - (rect.origin.y + rect.height) + } + + let cgRect = CGRect(x: rect.origin.x + frame.origin.x, y: originY, width: rect.width, height: rect.height) + guard let cgImage = CGWindowListCreateImage(cgRect, .optionOnScreenBelowWindow, CGWindowID(windowNumber), .bestResolution) else { return } captureGuideView.reset() - orderOut(nil) - captureDelegate?.didCaptured(rect: rect, image: cgImage) + + var captureOffsetY = CGFloat(0) + if frame.origin.y > 0 { + captureOffsetY = frame.origin.y - (cgRect.origin.y + cgRect.height) + } else { + captureOffsetY = -(cgRect.origin.y - upRightPoint.y + rect.height) + } + captureDelegate?.didCaptured(rect: CGRect(x: cgRect.origin.x, y: captureOffsetY, width: cgRect.width, height: cgRect.height), image: cgImage) } } protocol CaptureDelegate { + func didCanceled() func didCaptured(rect: NSRect, image: CGImage) } diff --git a/Fuwari/Info.plist b/Fuwari/Info.plist index 1279199..af77741 100644 --- a/Fuwari/Info.plist +++ b/Fuwari/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.1 + 0.2 CFBundleVersion - 1 + 2 Fabric APIKey @@ -40,6 +40,8 @@ + LSApplicationCategoryType + public.app-category.utilities LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement @@ -50,9 +52,9 @@ Main NSPrincipalClass NSApplication - SUPublicDSAKeyFile - dsa_pub.pem SUFeedURL https://fuwari-app.com/appcast.xml + SUPublicDSAKeyFile + dsa_pub.pem diff --git a/Fuwari/ViewController.swift b/Fuwari/ViewController.swift index e65fc3d..780ea7b 100644 --- a/Fuwari/ViewController.swift +++ b/Fuwari/ViewController.swift @@ -13,16 +13,23 @@ class ViewController: NSViewController { @IBOutlet private weak var versionTextField: NSTextField! fileprivate var windowControllers = [NSWindowController]() - private var fullScreenWindow = FullScreenWindow() + fileprivate var fullScreenWindows = [FullScreenWindow]() override func viewDidLoad() { super.viewDidLoad() - fullScreenWindow.captureDelegate = self - let controller = NSWindowController(window: fullScreenWindow) - controller.showWindow(nil) - windowControllers.append(controller) - fullScreenWindow.orderOut(nil) + for (i, screen) in NSScreen.screens()!.enumerated() { + fullScreenWindows.append(FullScreenWindow()) + fullScreenWindows[i] = FullScreenWindow(contentRect: NSRect(x: screen.frame.origin.x, y: screen.frame.origin.y, width: screen.frame.width, height: screen.frame.height), styleMask: .borderless, backing: .buffered, defer: false) + fullScreenWindows[i].captureDelegate = self + + print(screen.frame, fullScreenWindows[i].frame) + fullScreenWindows.append(fullScreenWindows[i]) + let controller = NSWindowController(window: fullScreenWindows[i]) + controller.showWindow(nil) + windowControllers.append(controller) + fullScreenWindows[i].orderOut(nil) + } NotificationCenter.default.addObserver(self, selector: #selector(didSelectCaptureButton(_:)), name: Notification.Name(rawValue: Constants.Notification.capture), object: nil) @@ -42,7 +49,9 @@ class ViewController: NSViewController { @IBAction private func didSelectCaptureButton(_: NSButton) { NSCursor.hide() - fullScreenWindow.startCapture() + fullScreenWindows.forEach { fullScreenWindow in + fullScreenWindow.startCapture() + } } @IBAction private func didSelectPreferencesButton(_: NSButton) { @@ -56,9 +65,18 @@ class ViewController: NSViewController { extension ViewController: CaptureDelegate { func didCaptured(rect: NSRect, image: CGImage) { - print(rect.width, rect.height, image.width, image.height) createFloatWindow(rect: rect, image: image) NSCursor.unhide() + fullScreenWindows.forEach { + $0.orderOut(nil) + } + } + + func didCanceled() { + NSCursor.unhide() + fullScreenWindows.forEach { + $0.orderOut(nil) + } } } @@ -88,6 +106,8 @@ extension ViewController: FloatDelegate { } catch { print(error.localizedDescription) } + } else { + floatWindow.level = Int(CGWindowLevelForKey(.maximumWindow)) } } } diff --git a/Fuwari/dsa_pub.pem b/Fuwari/dsa_pub.pem index df74bc0..27746ce 100644 --- a/Fuwari/dsa_pub.pem +++ b/Fuwari/dsa_pub.pem @@ -1,36 +1,36 @@ -----BEGIN PUBLIC KEY----- -MIIGOjCCBC0GByqGSM44BAEwggQgAoICAQDsxsKuPmrbZJZuHwJt4qbUgyZj8AaC -mfkOhx42IIk6i2OtlKXhlMVWcAZgjwUVg5T4IPjE2AklbYQFAWio1rSMmr6SrNsp -f7IYZdQNIoIr6tdSeald4sdVobTZwmvb7IwejFxOteMoYtIDJrz/vRRnnH2PS739 -Rsv0rWoAn0ypeBxwl/RURYPH+LV+I+6mFfsxe2cW+PI53w3+g6oP/Ye0FJicq4KI -ONSRFv3dB2udSSlSu7IgbkjhbAE1RYSgOa+s5B1eG+S/qELKutoYqWjhfTRgaDr/ -JKmzcfiZAF6VaOak0J8yXUy79ZKKRMnlw81LJoTR/G5ZGKKr/WNoBRcrhNnXFwO2 -4wjFHuDv1Rab79GkwyzuF0jTB6o8i40KE5PcYTXzNHxxE/ebqllOvfKkXOOl2D6n -WbmTiDPw53xeTXryBPsQ6m25I0wlTd8TT4sQNG7/SJVYkgJCz2NiRSkn2LHAmGw6 -KYPzbef7vEVjlw2aYDwGEiQzj05rfSWDNa/b1eFCf1IB79b/FY8hwqYxSq5Hp+wr -xRiEOUbgdpmvWBqvsIHf+KuF94za0V3rHjCAOjah1XSXZWSJzDKYvfqRj4lt2Wqf -VpxXjzw9PXPX3+UDAGCzkt0U6QqqkRKm1SyLsICB1MruUmnNMYfk/6vKH8X49VvA -wg8v1Bw32npr4wIVAL1ozRxFKtR1xLOIBdPsESoq9E4FAoICAAXRvxGEx/WNRZHg -VRXgvp03393HACJ+E+uTFZUjwVYxH/BI/Huskw/RZhNHwWEk/CDf9dsPTAjeTfF2 -XL4GngNAehMEW824xzWV25FFi8drXHuuwyoARpU3uvq03qnYYV09/PtncdqMhOkO -QpN2rl+WvllVISXbfk5RF1Z8kZBY/gJwpvUg0c9+ZWOdyMGI7BYxsSs7nSgQOxRj -OVB6Uv/2HNEUSsQ9YrSZuc6Ro/zFQkUgeqEdLjM68tRngMFk/3uV9DY4Y0TW7p3H -inEThGIwIbaPuq+m3Z/bgwELQgWNL3Hki+gpwf5t1MnyOePsiBnbvmcIFBcyMZ0J -Scg7xDVAiIdDxdNZsPz7q8GhSaKrE01P3/XttebiQMf5VkPedu/UB1h5yiIov4qu -4brTHQWDefgBzYfFlDHcffRxzaszGUiPMQvExVbq03BxhfeTyuP6GZpkT3Q2qYlp -I8OoXv13PGCIh03pmfjMi383mvGHDeTBPkPT6LNrABgfn7YCdROcwo+/M/99HBFy -d2nM9EBPfCHDF+J+wEjSC7e0eDfD56hZUqZ7gcfyNyNjJMunoMDQpnBhi1YR6/5v -qRZVJwoChqZvBlMQnSqIVJX5u5I7m9Wl8ozrCyMSOLwrGpy6vjACvD6EO/u0noHC -h9/qoMzWeFWPEUVofSrnBLPuL8sTA4ICBQACggIALyVE/mWQ4wD4/ON0yavvxqx5 -jFqDVyg5PYXjh+QXibu/vos/LNskEhn8npvb7HCzmJTcYCHuHds8qW/crbT6H0dP -cq4VVg+uoZD2IIYj+gzuKP+cAc5KDUA07yK6l5vviPG5ItYP5GWIk1Hg/cJjoTWS -jIpzepEYiN3Wkgj8pSzZBuyHH1WBgVtqcXnbyy61POFRTZcQ1/3QyFQ/mHzoT9FZ -bq1GB3VbwQg2jF0bd1d/WpGWmp7ISQwytXqUSfKhn123yS/RWfqih2mmNPKXDuBH -DiwYr6c/Gp2w/z4l9W2KIaXpd97wN6+1e9/KdqNG9VSocGJ5NgG3Kf0XQw5wRzDP -z528IoLPkbQC9msieRkVLUI368txp3Zlzp0TAZUxbY4bg/PMcu3BDj7aNKd8+akZ -LhAJV8e9WeBG+2GCw+bDgjCXTmozxwG6apjdPpTwpUF2mT8XoxNUNJ5sATjGEgfl -0sjsF7hozspbOO1sYAEfdzBXd353oubsy9PO393Rm8Ii83vzyOePSSIOlnV/AuhN -VHmgjGzZ30pFWqS1qsG/nDMioU33lexuy3z71nVTApcj9VePSDP2lKHIda+OtJ0l -mZb9AYkknMiw6gXWnqv/cWsk7RQADwL5fdcOtd31YEfUo7UT91mpTDKz+AiLi6GS -MPxa/8nfCB47VOVuf0o= +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 +lQ7q2gpJJ8Dcd20eek+H -----END PUBLIC KEY-----