├── README.md ├── macos ├── plop │ ├── Constants.swift │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── atul.imageset │ │ │ ├── atul.png │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── atul-1.png │ │ │ ├── atul-2.png │ │ │ ├── atul_1024.png │ │ │ ├── atul_128.png │ │ │ ├── atul_16.png │ │ │ ├── atul_32 1.png │ │ │ ├── atul_32.png │ │ │ ├── atul_512.png │ │ │ ├── atul_64.png │ │ │ ├── atul_512 1.png │ │ │ └── Contents.json │ │ └── atul_white.imageset │ │ │ ├── atul_white.png │ │ │ └── Contents.json │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── Views │ │ ├── Buttons │ │ │ ├── DebugButton.swift │ │ │ ├── StatisticsButton.swift │ │ │ ├── ConfigurationButton.swift │ │ │ ├── RecordButton.swift │ │ │ ├── CopyButton.swift │ │ │ ├── OpenButton.swift │ │ │ ├── ScreenshotButton.swift │ │ │ └── UploadButton.swift │ │ ├── UploadProgressView.swift │ │ ├── BlobListCell.swift │ │ ├── StatusBar.swift │ │ └── BlobPreview.swift │ ├── BlobGlobalState.swift │ ├── plop.entitlements │ ├── plopRelease.entitlements │ ├── Info.plist │ ├── BlobPopover.swift │ ├── Notifications.swift │ ├── Utils.swift │ ├── AppDelegate.swift │ └── Base.lproj │ │ └── Main.storyboard ├── BlobTests │ └── BlobTests.swift └── Blob.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved │ └── project.pbxproj ├── LICENSE ├── .github └── workflows │ └── macos_build.yaml └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | # blob 2 | 3 | blob for macOS screenshot 4 | -------------------------------------------------------------------------------- /macos/plop/Constants.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | let USERNAME = "atul" 4 | let GCLOUD_STORAGE_BUCKET = "blob.sh/" 5 | -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /macos/BlobTests/BlobTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | final class BlobTests: XCTestCase { 4 | 5 | func testExample() throws { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/atul.imageset/atul.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulsmadhugiri/Blob/HEAD/macos/plop/Assets.xcassets/atul.imageset/atul.png -------------------------------------------------------------------------------- /macos/plop/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/AppIcon.appiconset/atul-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulsmadhugiri/Blob/HEAD/macos/plop/Assets.xcassets/AppIcon.appiconset/atul-1.png -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/AppIcon.appiconset/atul-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulsmadhugiri/Blob/HEAD/macos/plop/Assets.xcassets/AppIcon.appiconset/atul-2.png -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/AppIcon.appiconset/atul_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulsmadhugiri/Blob/HEAD/macos/plop/Assets.xcassets/AppIcon.appiconset/atul_1024.png -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/AppIcon.appiconset/atul_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulsmadhugiri/Blob/HEAD/macos/plop/Assets.xcassets/AppIcon.appiconset/atul_128.png -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/AppIcon.appiconset/atul_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulsmadhugiri/Blob/HEAD/macos/plop/Assets.xcassets/AppIcon.appiconset/atul_16.png -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/AppIcon.appiconset/atul_32 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulsmadhugiri/Blob/HEAD/macos/plop/Assets.xcassets/AppIcon.appiconset/atul_32 1.png -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/AppIcon.appiconset/atul_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulsmadhugiri/Blob/HEAD/macos/plop/Assets.xcassets/AppIcon.appiconset/atul_32.png -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/AppIcon.appiconset/atul_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulsmadhugiri/Blob/HEAD/macos/plop/Assets.xcassets/AppIcon.appiconset/atul_512.png -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/AppIcon.appiconset/atul_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulsmadhugiri/Blob/HEAD/macos/plop/Assets.xcassets/AppIcon.appiconset/atul_64.png -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/AppIcon.appiconset/atul_512 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulsmadhugiri/Blob/HEAD/macos/plop/Assets.xcassets/AppIcon.appiconset/atul_512 1.png -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/atul_white.imageset/atul_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulsmadhugiri/Blob/HEAD/macos/plop/Assets.xcassets/atul_white.imageset/atul_white.png -------------------------------------------------------------------------------- /macos/Blob.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /macos/plop/Views/Buttons/DebugButton.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct DebugButton: View { 4 | var body: some View { 5 | Button(action: { 6 | print("Debug button pressed.") 7 | }) { 8 | Image(systemName: "ladybug") 9 | Text("Debug") 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /macos/plop/Views/Buttons/StatisticsButton.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct StatisticsButton: View { 4 | var body: some View { 5 | Button(action: { 6 | print("Statistics button pressed.") 7 | }) { 8 | Image(systemName: "chart.pie") 9 | Text("Statistics") 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /macos/plop/Views/Buttons/ConfigurationButton.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ConfigurationButton: View { 4 | var body: some View { 5 | Button(action: { 6 | print("Configuration button pressed.") 7 | }) { 8 | Image(systemName: "gear") 9 | Text("Configuration") 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /macos/Blob.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Blob.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/plop/Views/Buttons/RecordButton.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct RecordButton: View { 4 | var body: some View { 5 | Button(action: {}) { 6 | Image(systemName: "dot.circle.viewfinder") 7 | Text("Record") 8 | } 9 | } 10 | } 11 | 12 | struct RecordButton_Previews: PreviewProvider { 13 | static var previews: some View { 14 | RecordButton() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /macos/plop/Views/UploadProgressView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct UploadProgressView: View { 4 | var uploadProgress: Double 5 | 6 | var body: some View { 7 | ProgressView("", value: uploadProgress, total: 1.0).padding(.horizontal, 20) 8 | } 9 | } 10 | 11 | struct UploadProgressView_Previews: PreviewProvider { 12 | static var previews: some View { 13 | UploadProgressView(uploadProgress: 0.5) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/atul.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "atul.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/atul_white.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "atul_white.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /macos/plop/Views/Buttons/CopyButton.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct CopyButton: View { 4 | var previousUploadURL: String 5 | 6 | var body: some View { 7 | Button(action: { 8 | replaceClipboard(with: previousUploadURL) 9 | }) { 10 | Image(systemName: "square.on.square") 11 | } 12 | } 13 | } 14 | 15 | struct CopyButton_Previews: PreviewProvider { 16 | static var previews: some View { 17 | CopyButton(previousUploadURL: "https://google.com") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /macos/plop/Views/Buttons/OpenButton.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct OpenButton: View { 4 | var previousUploadURL: String 5 | 6 | var body: some View { 7 | Button(action: { 8 | if let url: URL = URL(string: previousUploadURL) { 9 | NSWorkspace.shared.open(url) 10 | } 11 | }) { 12 | Image(systemName: "arrow.up.right.square") 13 | } 14 | } 15 | } 16 | 17 | struct OpenButton_Previews: PreviewProvider { 18 | static var previews: some View { 19 | OpenButton(previousUploadURL: "https://google.com") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /macos/plop/Views/BlobListCell.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct BlobListCell: View { 4 | var blobEntry: BlobEntry 5 | 6 | var body: some View { 7 | HStack { 8 | BlobPreview(previousUploadLocalPath: blobEntry.uploadLocalPath, width: 96, height: 64) 9 | VStack(alignment: .leading) { 10 | Text(blobEntry.uploadLocalPath?.lastPathComponent ?? "").font(.headline) 11 | HStack { 12 | Text(blobEntry.fileSize ?? "[filesize]").font(.caption2.monospaced()) 13 | Text("[\(blobEntry.mimeType ?? "mime")]").font(.caption2.monospaced()).foregroundColor( 14 | .gray) 15 | } 16 | Text(getFormattedDateTime(fromDate: blobEntry.uploadedAt)) 17 | .font(.caption.monospaced()) 18 | } 19 | Spacer() 20 | VStack { 21 | CopyButton(previousUploadURL: blobEntry.uploadURL) 22 | OpenButton(previousUploadURL: blobEntry.uploadURL) 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /macos/plop/BlobGlobalState.swift: -------------------------------------------------------------------------------- 1 | import FirebaseStorage 2 | import Foundation 3 | import Observation 4 | import SwiftData 5 | 6 | @Model final class BlobEntry: Identifiable { 7 | var id = UUID() 8 | var uploadURL: String = "" 9 | var uploadLocalPath: URL? = nil 10 | var fileSize: String? = nil 11 | var mimeType: String? = nil 12 | var uploadedAt: Date = Date() 13 | 14 | init( 15 | id: UUID = UUID(), 16 | uploadURL: String, 17 | uploadLocalPath: URL? = nil 18 | ) { 19 | self.id = id 20 | self.uploadURL = uploadURL 21 | self.uploadLocalPath = uploadLocalPath 22 | 23 | self.fileSize = 24 | (uploadLocalPath != nil) 25 | ? getFormattedFileSize(fromURL: uploadLocalPath!) 26 | : "0kb" 27 | 28 | self.mimeType = 29 | (uploadLocalPath != nil) 30 | ? getMIMEType(fromURL: uploadLocalPath!) 31 | : "text/plain" 32 | 33 | self.uploadedAt = Date() 34 | } 35 | } 36 | 37 | @Observable final class BlobGlobalState { 38 | var uploadProgress: Double = 1.0 39 | } 40 | -------------------------------------------------------------------------------- /macos/plop/plop.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.aps-environment 6 | development 7 | com.apple.developer.icloud-container-identifiers 8 | 9 | iCloud.atul.Blob 10 | 11 | com.apple.developer.icloud-services 12 | 13 | CloudKit 14 | 15 | com.apple.security.assets.pictures.read-write 16 | 17 | com.apple.security.device.usb 18 | 19 | com.apple.security.files.downloads.read-write 20 | 21 | com.apple.security.files.user-selected.read-write 22 | 23 | com.apple.security.network.client 24 | 25 | com.apple.security.network.server 26 | 27 | com.apple.security.temporary-exception.mach-register.global-name 28 | com.apple.screencapture.interactive 29 | 30 | 31 | -------------------------------------------------------------------------------- /macos/plop/plopRelease.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.aps-environment 6 | development 7 | com.apple.developer.icloud-container-identifiers 8 | 9 | iCloud.atul.Blob 10 | 11 | com.apple.developer.icloud-services 12 | 13 | CloudKit 14 | 15 | com.apple.security.assets.pictures.read-write 16 | 17 | com.apple.security.device.usb 18 | 19 | com.apple.security.files.downloads.read-write 20 | 21 | com.apple.security.files.user-selected.read-write 22 | 23 | com.apple.security.network.client 24 | 25 | com.apple.security.network.server 26 | 27 | com.apple.security.temporary-exception.mach-register.global-name 28 | com.apple.screencapture.interactive 29 | 30 | 31 | -------------------------------------------------------------------------------- /macos/plop/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LSUIElement 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/plop/Views/Buttons/ScreenshotButton.swift: -------------------------------------------------------------------------------- 1 | import SwiftData 2 | import SwiftUI 3 | 4 | struct ScreenshotButton: View { 5 | @Binding var uploadProgress: Double 6 | @Environment(\.modelContext) private var modelContext 7 | 8 | var body: some View { 9 | Button(action: { 10 | let filepath = captureScreenshot() 11 | let (destinationURL, uploadTask, localPath) = uploadBlob(filepath: filepath) 12 | 13 | uploadTask.observe(.progress) { snapshot in 14 | uploadProgress = snapshot.progress?.fractionCompleted ?? 0 15 | } 16 | uploadTask.observe(.success) { _ in 17 | NSSound(named: "Funk")?.play() 18 | replaceClipboard(with: destinationURL) 19 | let blobEntry = BlobEntry(uploadURL: destinationURL, uploadLocalPath: localPath) 20 | successfulBlobNotification(blobEntry: blobEntry) 21 | modelContext.insert(blobEntry) 22 | } 23 | }) { 24 | Image(systemName: "viewfinder") 25 | Text("Screenshot") 26 | } 27 | } 28 | } 29 | 30 | struct ScreenshotButton_Previews: PreviewProvider { 31 | static var previews: some View { 32 | ScreenshotButton( 33 | uploadProgress: .constant(1.0)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /macos/plop/Views/Buttons/UploadButton.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct UploadButton: View { 4 | @Binding var uploadProgress: Double 5 | 6 | @State private var fileSelectionDialogPresented = false 7 | 8 | var body: some View { 9 | Button(action: { 10 | fileSelectionDialogPresented = true 11 | }) { 12 | Image(systemName: "icloud.and.arrow.up") 13 | Text("Upload") 14 | }.fileImporter( 15 | isPresented: $fileSelectionDialogPresented, 16 | allowedContentTypes: [.image, .audio, .video, .text, .data] 17 | ) { result in 18 | do { 19 | let filepath = try result.get().path 20 | let (destinationURL, uploadTask, _) = uploadBlob(filepath: filepath) 21 | 22 | uploadTask.observe(.progress) { snapshot in 23 | uploadProgress = snapshot.progress?.fractionCompleted ?? 0 24 | } 25 | uploadTask.observe(.success) { _ in 26 | NSSound(named: "Funk")?.play() 27 | replaceClipboard(with: destinationURL) 28 | } 29 | } catch { 30 | print("Could not get filepath for selected file.") 31 | } 32 | } 33 | } 34 | } 35 | 36 | struct UploadButton_Previews: PreviewProvider { 37 | static var previews: some View { 38 | UploadButton(uploadProgress: .constant(1.0)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /macos/plop/Views/StatusBar.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | 3 | // Credit: AnaghSharma (MIT Licence) 4 | // https://github.com/AnaghSharma/Ambar-SwiftUI/blob/master/Ambar/Helpers/StatusBarController.swift 5 | class StatusBar { 6 | private var statusBar: NSStatusBar 7 | private var statusItem: NSStatusItem 8 | private var popover: NSPopover 9 | 10 | init(_ popover: NSPopover) { 11 | self.popover = popover 12 | statusBar = NSStatusBar.system 13 | statusItem = statusBar.statusItem(withLength: 28.0) 14 | 15 | if let statusBarButton = statusItem.button { 16 | statusBarButton.image = #imageLiteral(resourceName: "atul_white") 17 | statusBarButton.image?.size = NSSize(width: 18.0, height: 18.0) 18 | statusBarButton.image?.isTemplate = true 19 | statusBarButton.action = #selector(togglePopover(sender:)) 20 | statusBarButton.target = self 21 | } 22 | } 23 | 24 | @objc func togglePopover(sender: AnyObject) { 25 | if popover.isShown { 26 | hidePopover(sender) 27 | } else { 28 | showPopover(sender) 29 | } 30 | } 31 | 32 | func showPopover(_: AnyObject) { 33 | if let statusBarButton = statusItem.button { 34 | popover.show( 35 | relativeTo: statusBarButton.bounds, of: statusBarButton, preferredEdge: NSRectEdge.maxY) 36 | } 37 | } 38 | 39 | func hidePopover(_ sender: AnyObject) { 40 | popover.performClose(sender) 41 | } 42 | 43 | func mouseEventHandler(_ event: NSEvent?) { 44 | if popover.isShown { 45 | hidePopover(event!) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /macos/plop/BlobPopover.swift: -------------------------------------------------------------------------------- 1 | import SwiftData 2 | import SwiftUI 3 | 4 | struct BlobPopover: View { 5 | @Bindable var blobGlobalState: BlobGlobalState 6 | @Query var entries: [BlobEntry] 7 | 8 | var body: some View { 9 | VStack { 10 | HStack { 11 | ScreenshotButton(uploadProgress: $blobGlobalState.uploadProgress) 12 | RecordButton() 13 | UploadButton(uploadProgress: $blobGlobalState.uploadProgress) 14 | } 15 | 16 | Divider() 17 | 18 | BlobPreview(previousUploadLocalPath: entries.last?.uploadLocalPath) 19 | 20 | UploadProgressView(uploadProgress: blobGlobalState.uploadProgress) 21 | 22 | HStack { 23 | TextField("", text: .constant(entries.last?.uploadURL ?? "")).frame( 24 | width: 260 25 | ).textFieldStyle(.roundedBorder) 26 | CopyButton(previousUploadURL: entries.last?.uploadURL ?? "") 27 | OpenButton(previousUploadURL: entries.last?.uploadURL ?? "") 28 | } 29 | 30 | Divider() 31 | 32 | List { 33 | ForEach(entries.reversed(), id: \.id) { blobEntry in 34 | BlobListCell(blobEntry: blobEntry) 35 | } 36 | }.listStyle(.sidebar) 37 | 38 | Divider() 39 | 40 | HStack { 41 | StatisticsButton() 42 | ConfigurationButton() 43 | DebugButton() 44 | } 45 | 46 | }.padding(.all, 10).padding(.top, 10) 47 | } 48 | } 49 | 50 | struct ContentView_Previews: PreviewProvider { 51 | static var previews: some View { 52 | BlobPopover(blobGlobalState: BlobGlobalState()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /macos/plop/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "atul_16.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "atul_32.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "atul_32 1.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "atul_64.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "atul_128.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "atul-2.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "atul-1.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "atul_512 1.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "atul_512.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "atul_1024.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Atul Madhugiri 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /.github/workflows/macos_build.yaml: -------------------------------------------------------------------------------- 1 | name: macOS Build 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | env: 8 | DEVELOPER_DIR: /Applications/Xcode_15.0.app/Contents/Developer 9 | 10 | jobs: 11 | build: 12 | name: macOS Build 13 | runs-on: macos-13 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v3 18 | 19 | - name: Install Apple Developer certificate 20 | env: 21 | BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} 22 | P12_PASSWORD: ${{ secrets.P12_PASSWORD }} 23 | KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} 24 | run: | 25 | CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 26 | KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db 27 | 28 | echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH 29 | 30 | security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH 31 | security set-keychain-settings -lut 21600 $KEYCHAIN_PATH 32 | security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH 33 | 34 | security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH 35 | security list-keychain -d user -s $KEYCHAIN_PATH 36 | 37 | - name: Include GoogleServiceInfoPlist 38 | working-directory: ./macos 39 | env: 40 | GOOGLE_SERVICE_INFO_PLIST: ${{ secrets.GOOGLE_SERVICE_INFO_PLIST }} 41 | run: echo $GOOGLE_SERVICE_INFO_PLIST | base64 --decode > GoogleService-Info.plist 42 | 43 | - name: xcodebuild 44 | working-directory: ./macos 45 | run: xcodebuild -project plop.xcodeproj -scheme plop CODE_SIGNING_REQUIRED=NO 46 | -------------------------------------------------------------------------------- /macos/plop/Notifications.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UserNotifications 3 | 4 | func successfulBlobNotification(blobEntry: BlobEntry) { 5 | let notifCenter = UNUserNotificationCenter.current() 6 | notifCenter.getNotificationSettings { (settings) in 7 | switch settings.authorizationStatus { 8 | case .authorized, .provisional: 9 | let notifContent = UNMutableNotificationContent() 10 | 11 | notifContent.title = blobEntry.uploadLocalPath?.lastPathComponent ?? "FILE NAME" 12 | notifContent.subtitle = blobEntry.mimeType ?? "FILE TYPE" 13 | notifContent.body = blobEntry.fileSize ?? "FILE SIZE" 14 | 15 | do { 16 | let filename = blobEntry.uploadLocalPath!.lastPathComponent 17 | let temporaryPath: String = "\(NSTemporaryDirectory())notifAttach/\(filename)" 18 | let temporaryPathURL: URL = URL(fileURLWithPath: temporaryPath) 19 | 20 | try FileManager().createDirectory( 21 | at: temporaryPathURL.deletingLastPathComponent(), 22 | withIntermediateDirectories: true 23 | ) 24 | 25 | try FileManager().copyItem( 26 | at: blobEntry.uploadLocalPath!, 27 | to: temporaryPathURL 28 | ) 29 | 30 | let notifAttachment = try UNNotificationAttachment( 31 | identifier: "image", 32 | url: temporaryPathURL 33 | ) 34 | notifContent.attachments = [notifAttachment] 35 | } catch { 36 | print("Unable to add attachment to notification.") 37 | } 38 | 39 | let notifRequest = UNNotificationRequest( 40 | identifier: UUID().uuidString, 41 | content: notifContent, 42 | trigger: nil 43 | ) 44 | notifCenter.add(notifRequest) 45 | 46 | case .denied, .notDetermined: 47 | print("Notification permission denied.") 48 | 49 | @unknown default: 50 | print("Unknown case introduced in new version of macOS.") 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /macos/plop/Views/BlobPreview.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct BlobPreview: View { 4 | var previousUploadLocalPath: URL? 5 | var width: CGFloat = 360 6 | var height: CGFloat = 240 7 | 8 | var body: some View { 9 | AsyncImage(url: previousUploadLocalPath) { image in 10 | image.interpolation(.none).resizable().scaledToFit().cornerRadius(8).frame( 11 | width: width, height: height 12 | ).id( 13 | previousUploadLocalPath 14 | ).transition(.opacity.animation(.default)).onDrag { 15 | if let previousUploadLocalPath { 16 | let temporaryPathString = 17 | "\(NSTemporaryDirectory())onDrag/\(previousUploadLocalPath.lastPathComponent)" 18 | let temporaryPath = URL(fileURLWithPath: temporaryPathString) 19 | if FileManager().fileExists(atPath: temporaryPathString) { 20 | if let provider = NSItemProvider(contentsOf: temporaryPath) { 21 | provider.suggestedName = previousUploadLocalPath.lastPathComponent 22 | return provider 23 | } 24 | } 25 | do { 26 | try FileManager().createDirectory( 27 | at: temporaryPath.deletingLastPathComponent(), withIntermediateDirectories: true) 28 | try FileManager().copyItem(at: previousUploadLocalPath, to: temporaryPath) 29 | if let provider = NSItemProvider(contentsOf: temporaryPath) { 30 | provider.suggestedName = previousUploadLocalPath.lastPathComponent 31 | return provider 32 | } 33 | } catch { 34 | print("Error creating temporary file in .onDrag") 35 | } 36 | } 37 | return NSItemProvider() 38 | } 39 | } placeholder: { 40 | Color.gray.opacity(0.1).cornerRadius(8) 41 | } 42 | } 43 | } 44 | 45 | struct BlobPreview_Previews: PreviewProvider { 46 | static var previews: some View { 47 | BlobPreview(previousUploadLocalPath: nil) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | GoogleService-Info.plist 92 | macos/Pods 93 | -------------------------------------------------------------------------------- /macos/plop/Utils.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | import FirebaseStorage 3 | import Foundation 4 | import UniformTypeIdentifiers 5 | 6 | func replaceClipboard(with newString: String) { 7 | let pasteboard = NSPasteboard.general 8 | pasteboard.clearContents() 9 | pasteboard.writeObjects([newString as NSString]) 10 | } 11 | 12 | func generateFileName(nameLength: Int = 6, fileExtension: String = ".png") -> String { 13 | let randomString = UUID().uuidString.suffix(nameLength).lowercased() 14 | return "\(randomString)\(fileExtension)" 15 | } 16 | 17 | func captureScreenshot() -> String { 18 | let temporaryDirectory = URL.homeDirectory.appending(path: ".blob", directoryHint: .isDirectory) 19 | 20 | do { 21 | try FileManager().createDirectory( 22 | at: temporaryDirectory, 23 | withIntermediateDirectories: true 24 | ) 25 | } catch { 26 | print("Unable to create $HOME/.blob/ directory.") 27 | } 28 | 29 | let destinationPath = "\(temporaryDirectory.path())\(generateFileName())" 30 | 31 | let screenCaptureTask = Process() 32 | screenCaptureTask.launchPath = "/usr/sbin/screencapture" 33 | screenCaptureTask.arguments = ["-i", "-r", destinationPath] 34 | 35 | screenCaptureTask.launch() 36 | screenCaptureTask.waitUntilExit() 37 | 38 | return destinationPath 39 | } 40 | 41 | func uploadBlob(filepath: String) -> ( 42 | destinationURL: String, uploadTask: StorageUploadTask, localPath: URL 43 | ) { 44 | let filename: String = URL(fileURLWithPath: filepath).lastPathComponent 45 | 46 | let storageBucket = Storage.storage(url: "gs://\(GCLOUD_STORAGE_BUCKET)").reference() 47 | let destinationRef = storageBucket.child("\(filename)") 48 | let destinationURL = "https://\(GCLOUD_STORAGE_BUCKET)\(filename)" 49 | 50 | let uploadTask: StorageUploadTask = destinationRef.putFile(from: URL(fileURLWithPath: filepath)) { 51 | _, error in 52 | if let error { 53 | print("Error uploading file to Google Cloud Storage: \(error)") 54 | } 55 | } 56 | 57 | return (destinationURL, uploadTask, URL(fileURLWithPath: filepath)) 58 | } 59 | 60 | func getFormattedFileSize(fromURL url: URL) -> String? { 61 | do { 62 | let resourceValues = try url.resourceValues(forKeys: [.fileSizeKey]) 63 | let fileSize = resourceValues.fileSize! 64 | return ByteCountFormatter().string(fromByteCount: Int64(fileSize)) 65 | } catch { 66 | print("Error getting formatted fileSize from URL.") 67 | } 68 | return nil 69 | } 70 | 71 | func getMIMEType(fromURL url: URL) -> String? { 72 | if let pathSuffix = UTType(filenameExtension: url.pathExtension) { 73 | return pathSuffix.preferredMIMEType 74 | } 75 | return nil 76 | } 77 | 78 | func getFormattedDateTime(fromDate date: Date) -> String { 79 | let fmt = DateFormatter() 80 | fmt.dateStyle = .medium 81 | fmt.timeStyle = .short 82 | return fmt.string(from: date) 83 | } 84 | -------------------------------------------------------------------------------- /macos/plop/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import FirebaseCore 2 | import HotKey 3 | import SwiftData 4 | import SwiftUI 5 | import UserNotifications 6 | 7 | @NSApplicationMain 8 | class AppDelegate: NSObject, NSApplicationDelegate { 9 | var statusBar: StatusBar? 10 | var popover = NSPopover() 11 | var blobGlobalState = BlobGlobalState() 12 | 13 | var screenshotHotKey = HotKey(key: .z, modifiers: [.command, .shift]) 14 | var uploadHotKey = HotKey(key: .u, modifiers: [.command, .shift]) 15 | 16 | var blobEntryContainer: ModelContainer? 17 | 18 | func applicationDidFinishLaunching(_: Notification) { 19 | FirebaseApp.configure() 20 | configureScreenshotHotKey() 21 | configureUploadHotKey() 22 | 23 | let notificationCenter = UNUserNotificationCenter.current() 24 | notificationCenter.requestAuthorization(options: [.alert, .badge]) { (granted, error: Error?) in 25 | if let error = error { 26 | print("Error: \(error.localizedDescription)") 27 | } 28 | 29 | print( 30 | granted 31 | ? "Notification permission granted." 32 | : "Notification permission denied." 33 | ) 34 | } 35 | 36 | do { 37 | // We use NSPopover in place of NSWindow so icon appears in menubar. 38 | // Credit: Anagh Sharma (https://github.com/AnaghSharma) 39 | blobEntryContainer = try ModelContainer(for: BlobEntry.self) 40 | let contentView = BlobPopover(blobGlobalState: blobGlobalState).modelContainer( 41 | blobEntryContainer!) 42 | popover.contentSize = NSSize(width: 400, height: 800) 43 | popover.contentViewController = NSHostingController(rootView: contentView) 44 | popover.animates = false 45 | popover.behavior = NSPopover.Behavior.transient 46 | 47 | statusBar = StatusBar(popover) 48 | } catch { 49 | print("Failed to instantiate ModelContainer for BlobEntry") 50 | } 51 | } 52 | 53 | @MainActor 54 | func configureScreenshotHotKey() { 55 | screenshotHotKey.keyDownHandler = { 56 | NSApp.activate(ignoringOtherApps: true) 57 | let filepath = captureScreenshot() 58 | let (destinationURL, uploadTask, localPath) = uploadBlob(filepath: filepath) 59 | 60 | uploadTask.observe(.progress) { snapshot in 61 | self.blobGlobalState.uploadProgress = 62 | snapshot.progress?.fractionCompleted ?? 0 63 | } 64 | 65 | uploadTask.observe(.success) { _ in 66 | NSSound(named: "Funk")?.play() 67 | replaceClipboard(with: destinationURL) 68 | let blobEntry = BlobEntry(uploadURL: destinationURL, uploadLocalPath: localPath) 69 | successfulBlobNotification(blobEntry: blobEntry) 70 | self.blobEntryContainer?.mainContext.insert(blobEntry) 71 | } 72 | } 73 | } 74 | 75 | @MainActor 76 | func configureUploadHotKey() { 77 | uploadHotKey.keyDownHandler = { 78 | NSApp.activate(ignoringOtherApps: true) 79 | let fileSelectionDialog = NSOpenPanel() 80 | if fileSelectionDialog.runModal() == NSApplication.ModalResponse.OK { 81 | if let filepath = fileSelectionDialog.url { 82 | let (destinationURL, uploadTask, localPath) = uploadBlob( 83 | filepath: filepath.path) 84 | 85 | uploadTask.observe(.progress) { snapshot in 86 | self.blobGlobalState.uploadProgress = 87 | snapshot.progress?.fractionCompleted ?? 0 88 | } 89 | 90 | uploadTask.observe(.success) { _ in 91 | NSSound(named: "Funk")?.play() 92 | replaceClipboard(with: destinationURL) 93 | let blobEntry = BlobEntry(uploadURL: destinationURL, uploadLocalPath: localPath) 94 | successfulBlobNotification(blobEntry: blobEntry) 95 | self.blobEntryContainer?.mainContext.insert(blobEntry) 96 | } 97 | } 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /macos/Blob.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "abseil-cpp-binary", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/google/abseil-cpp-binary.git", 7 | "state" : { 8 | "revision" : "bfc0b6f81adc06ce5121eb23f628473638d67c5c", 9 | "version" : "1.2022062300.0" 10 | } 11 | }, 12 | { 13 | "identity" : "app-check", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/google/app-check.git", 16 | "state" : { 17 | "revision" : "3e464dad87dad2d29bb29a97836789bf0f8f67d2", 18 | "version" : "10.18.1" 19 | } 20 | }, 21 | { 22 | "identity" : "firebase-ios-sdk", 23 | "kind" : "remoteSourceControl", 24 | "location" : "https://github.com/firebase/firebase-ios-sdk.git", 25 | "state" : { 26 | "revision" : "fe09d61a539e11fdbe24f269bba10144b6145fe2", 27 | "version" : "10.22.0" 28 | } 29 | }, 30 | { 31 | "identity" : "googleappmeasurement", 32 | "kind" : "remoteSourceControl", 33 | "location" : "https://github.com/google/GoogleAppMeasurement.git", 34 | "state" : { 35 | "revision" : "bf3bb24f6b60a7acedaef504e9ce97154203217a", 36 | "version" : "10.22.0" 37 | } 38 | }, 39 | { 40 | "identity" : "googledatatransport", 41 | "kind" : "remoteSourceControl", 42 | "location" : "https://github.com/google/GoogleDataTransport.git", 43 | "state" : { 44 | "revision" : "a637d318ae7ae246b02d7305121275bc75ed5565", 45 | "version" : "9.4.0" 46 | } 47 | }, 48 | { 49 | "identity" : "googleutilities", 50 | "kind" : "remoteSourceControl", 51 | "location" : "https://github.com/google/GoogleUtilities.git", 52 | "state" : { 53 | "revision" : "830ffa9276e10267881f2697283c2fcd867603fd", 54 | "version" : "7.13.0" 55 | } 56 | }, 57 | { 58 | "identity" : "grpc-binary", 59 | "kind" : "remoteSourceControl", 60 | "location" : "https://github.com/google/grpc-binary.git", 61 | "state" : { 62 | "revision" : "a673bc2937fbe886dd1f99c401b01b6d977a9c98", 63 | "version" : "1.49.1" 64 | } 65 | }, 66 | { 67 | "identity" : "gtm-session-fetcher", 68 | "kind" : "remoteSourceControl", 69 | "location" : "https://github.com/google/gtm-session-fetcher.git", 70 | "state" : { 71 | "revision" : "76135c9f4e1ac85459d5fec61b6f76ac47ab3a4c", 72 | "version" : "3.3.1" 73 | } 74 | }, 75 | { 76 | "identity" : "hotkey", 77 | "kind" : "remoteSourceControl", 78 | "location" : "https://github.com/soffes/HotKey", 79 | "state" : { 80 | "revision" : "31234dfbb4330b90ee10d89fbacb77316781cc06", 81 | "version" : "0.2.0" 82 | } 83 | }, 84 | { 85 | "identity" : "interop-ios-for-google-sdks", 86 | "kind" : "remoteSourceControl", 87 | "location" : "https://github.com/google/interop-ios-for-google-sdks.git", 88 | "state" : { 89 | "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648", 90 | "version" : "100.0.0" 91 | } 92 | }, 93 | { 94 | "identity" : "leveldb", 95 | "kind" : "remoteSourceControl", 96 | "location" : "https://github.com/firebase/leveldb.git", 97 | "state" : { 98 | "revision" : "43aaef65e0c665daadf848761d560e446d350d3d", 99 | "version" : "1.22.4" 100 | } 101 | }, 102 | { 103 | "identity" : "nanopb", 104 | "kind" : "remoteSourceControl", 105 | "location" : "https://github.com/firebase/nanopb.git", 106 | "state" : { 107 | "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", 108 | "version" : "2.30910.0" 109 | } 110 | }, 111 | { 112 | "identity" : "promises", 113 | "kind" : "remoteSourceControl", 114 | "location" : "https://github.com/google/promises.git", 115 | "state" : { 116 | "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", 117 | "version" : "2.4.0" 118 | } 119 | }, 120 | { 121 | "identity" : "swift-protobuf", 122 | "kind" : "remoteSourceControl", 123 | "location" : "https://github.com/apple/swift-protobuf.git", 124 | "state" : { 125 | "revision" : "65e8f29b2d63c4e38e736b25c27b83e012159be8", 126 | "version" : "1.25.2" 127 | } 128 | } 129 | ], 130 | "version" : 2 131 | } 132 | -------------------------------------------------------------------------------- /macos/Blob.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2DDA0D25EDF888380547E8C7 /* BlobGlobalState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DDA0DC01E5C360C974AF4A3 /* BlobGlobalState.swift */; }; 11 | B703FDFE24D2A89500ADC7A0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B703FDFD24D2A89500ADC7A0 /* AppDelegate.swift */; }; 12 | B703FE0024D2A89500ADC7A0 /* BlobPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = B703FDFF24D2A89500ADC7A0 /* BlobPopover.swift */; }; 13 | B703FE0224D2A89500ADC7A0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B703FE0124D2A89500ADC7A0 /* Assets.xcassets */; }; 14 | B703FE0524D2A89500ADC7A0 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B703FE0424D2A89500ADC7A0 /* Preview Assets.xcassets */; }; 15 | B703FE0824D2A89500ADC7A0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B703FE0624D2A89500ADC7A0 /* Main.storyboard */; }; 16 | B707557028444646005D4C51 /* ScreenshotButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B707556F28444646005D4C51 /* ScreenshotButton.swift */; }; 17 | B70755722844483C005D4C51 /* UploadButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70755712844483C005D4C51 /* UploadButton.swift */; }; 18 | B70755742844499E005D4C51 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70755732844499E005D4C51 /* Utils.swift */; }; 19 | B70A98BA24DBFD88007DBA7F /* StatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70A98B924DBFD88007DBA7F /* StatusBar.swift */; }; 20 | B71ABFA0290EF74F0033E3E6 /* BlobTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B71ABF9F290EF74F0033E3E6 /* BlobTests.swift */; }; 21 | B71ABFA7290EF9300033E3E6 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70755732844499E005D4C51 /* Utils.swift */; }; 22 | B71ABFA9290EFD310033E3E6 /* RecordButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B71ABFA8290EFD310033E3E6 /* RecordButton.swift */; }; 23 | B721CFB7285F833B00B945C9 /* FirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = B721CFB6285F833B00B945C9 /* FirebaseStorage */; }; 24 | B721CFBA285F839900B945C9 /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = B721CFB9285F839900B945C9 /* HotKey */; }; 25 | B76CD614276E5716005F1A93 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B76CD613276E5716005F1A93 /* GoogleService-Info.plist */; }; 26 | B7B5AB3D285F8C7500C98B70 /* UploadProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7B5AB3C285F8C7500C98B70 /* UploadProgressView.swift */; }; 27 | B7B5D62B285EF2F500E8BC8A /* CopyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7B5D62A285EF2F500E8BC8A /* CopyButton.swift */; }; 28 | B7B5D62D285EF39400E8BC8A /* OpenButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7B5D62C285EF39400E8BC8A /* OpenButton.swift */; }; 29 | B7B5D62F285EF8D200E8BC8A /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7B5D62E285EF8D200E8BC8A /* Constants.swift */; }; 30 | B7BDCC60286D69B700B668CC /* BlobPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7BDCC5F286D69B700B668CC /* BlobPreview.swift */; }; 31 | B7E7B3C02A2BB7380042F7D5 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7E7B3BF2A2BB7380042F7D5 /* Notifications.swift */; }; 32 | B7E7B3C22A2BE3B80042F7D5 /* StatisticsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7E7B3C12A2BE3B80042F7D5 /* StatisticsButton.swift */; }; 33 | B7E7B3C42A2BE53C0042F7D5 /* ConfigurationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7E7B3C32A2BE53C0042F7D5 /* ConfigurationButton.swift */; }; 34 | B7E7B3C62A2BE6140042F7D5 /* DebugButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7E7B3C52A2BE6140042F7D5 /* DebugButton.swift */; }; 35 | B7F0B23C28F3897800BECCBA /* BlobListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F0B23B28F3897800BECCBA /* BlobListCell.swift */; }; 36 | /* End PBXBuildFile section */ 37 | 38 | /* Begin PBXContainerItemProxy section */ 39 | B71ABFA1290EF74F0033E3E6 /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = B703FDF224D2A89500ADC7A0 /* Project object */; 42 | proxyType = 1; 43 | remoteGlobalIDString = B703FDF924D2A89500ADC7A0; 44 | remoteInfo = plop; 45 | }; 46 | /* End PBXContainerItemProxy section */ 47 | 48 | /* Begin PBXCopyFilesBuildPhase section */ 49 | B7BB43482568EBB400C46F41 /* Embed Frameworks */ = { 50 | isa = PBXCopyFilesBuildPhase; 51 | buildActionMask = 2147483647; 52 | dstPath = ""; 53 | dstSubfolderSpec = 10; 54 | files = ( 55 | ); 56 | name = "Embed Frameworks"; 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXCopyFilesBuildPhase section */ 60 | 61 | /* Begin PBXFileReference section */ 62 | 2DDA0DC01E5C360C974AF4A3 /* BlobGlobalState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlobGlobalState.swift; sourceTree = ""; }; 63 | B703FDFA24D2A89500ADC7A0 /* Blob.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Blob.app; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | B703FDFD24D2A89500ADC7A0 /* AppDelegate.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 65 | B703FDFF24D2A89500ADC7A0 /* BlobPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlobPopover.swift; sourceTree = ""; }; 66 | B703FE0124D2A89500ADC7A0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 67 | B703FE0424D2A89500ADC7A0 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 68 | B703FE0724D2A89500ADC7A0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 69 | B703FE0924D2A89500ADC7A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | B703FE0A24D2A89500ADC7A0 /* plop.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = plop.entitlements; sourceTree = ""; }; 71 | B707556F28444646005D4C51 /* ScreenshotButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotButton.swift; sourceTree = ""; }; 72 | B70755712844483C005D4C51 /* UploadButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadButton.swift; sourceTree = ""; }; 73 | B70755732844499E005D4C51 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 74 | B70A98B924DBFD88007DBA7F /* StatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBar.swift; sourceTree = ""; }; 75 | B71ABF9D290EF74F0033E3E6 /* BlobTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BlobTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | B71ABF9F290EF74F0033E3E6 /* BlobTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlobTests.swift; sourceTree = ""; }; 77 | B71ABFA8290EFD310033E3E6 /* RecordButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordButton.swift; sourceTree = ""; }; 78 | B76CD613276E5716005F1A93 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info.plist"; sourceTree = SOURCE_ROOT; }; 79 | B7ADAA442B27794100072CCD /* plopRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = plopRelease.entitlements; sourceTree = ""; }; 80 | B7B5AB3C285F8C7500C98B70 /* UploadProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadProgressView.swift; sourceTree = ""; }; 81 | B7B5D62A285EF2F500E8BC8A /* CopyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyButton.swift; sourceTree = ""; }; 82 | B7B5D62C285EF39400E8BC8A /* OpenButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenButton.swift; sourceTree = ""; }; 83 | B7B5D62E285EF8D200E8BC8A /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 84 | B7BDCC5F286D69B700B668CC /* BlobPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlobPreview.swift; sourceTree = ""; }; 85 | B7E7B3BF2A2BB7380042F7D5 /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = ""; }; 86 | B7E7B3C12A2BE3B80042F7D5 /* StatisticsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticsButton.swift; sourceTree = ""; }; 87 | B7E7B3C32A2BE53C0042F7D5 /* ConfigurationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationButton.swift; sourceTree = ""; }; 88 | B7E7B3C52A2BE6140042F7D5 /* DebugButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugButton.swift; sourceTree = ""; }; 89 | B7F0B23B28F3897800BECCBA /* BlobListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlobListCell.swift; sourceTree = ""; }; 90 | /* End PBXFileReference section */ 91 | 92 | /* Begin PBXFrameworksBuildPhase section */ 93 | B703FDF724D2A89500ADC7A0 /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | B721CFBA285F839900B945C9 /* HotKey in Frameworks */, 98 | B721CFB7285F833B00B945C9 /* FirebaseStorage in Frameworks */, 99 | ); 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | B71ABF9A290EF74F0033E3E6 /* Frameworks */ = { 103 | isa = PBXFrameworksBuildPhase; 104 | buildActionMask = 2147483647; 105 | files = ( 106 | ); 107 | runOnlyForDeploymentPostprocessing = 0; 108 | }; 109 | /* End PBXFrameworksBuildPhase section */ 110 | 111 | /* Begin PBXGroup section */ 112 | B703FDF124D2A89500ADC7A0 = { 113 | isa = PBXGroup; 114 | children = ( 115 | B703FDFC24D2A89500ADC7A0 /* plop */, 116 | B71ABF9E290EF74F0033E3E6 /* BlobTests */, 117 | B703FDFB24D2A89500ADC7A0 /* Products */, 118 | ); 119 | sourceTree = ""; 120 | }; 121 | B703FDFB24D2A89500ADC7A0 /* Products */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | B703FDFA24D2A89500ADC7A0 /* Blob.app */, 125 | B71ABF9D290EF74F0033E3E6 /* BlobTests.xctest */, 126 | ); 127 | name = Products; 128 | sourceTree = ""; 129 | }; 130 | B703FDFC24D2A89500ADC7A0 /* plop */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | B7ADAA442B27794100072CCD /* plopRelease.entitlements */, 134 | B703FE0124D2A89500ADC7A0 /* Assets.xcassets */, 135 | B703FE0324D2A89500ADC7A0 /* Preview Content */, 136 | B703FE0A24D2A89500ADC7A0 /* plop.entitlements */, 137 | B703FE0624D2A89500ADC7A0 /* Main.storyboard */, 138 | B703FE0924D2A89500ADC7A0 /* Info.plist */, 139 | B76CD613276E5716005F1A93 /* GoogleService-Info.plist */, 140 | B7ADAA452B277C1500072CCD /* Views */, 141 | B703FDFD24D2A89500ADC7A0 /* AppDelegate.swift */, 142 | B7B5D62E285EF8D200E8BC8A /* Constants.swift */, 143 | B70755732844499E005D4C51 /* Utils.swift */, 144 | 2DDA0DC01E5C360C974AF4A3 /* BlobGlobalState.swift */, 145 | B7E7B3BF2A2BB7380042F7D5 /* Notifications.swift */, 146 | B703FDFF24D2A89500ADC7A0 /* BlobPopover.swift */, 147 | ); 148 | path = plop; 149 | sourceTree = ""; 150 | }; 151 | B703FE0324D2A89500ADC7A0 /* Preview Content */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | B703FE0424D2A89500ADC7A0 /* Preview Assets.xcassets */, 155 | ); 156 | path = "Preview Content"; 157 | sourceTree = ""; 158 | }; 159 | B71ABF9E290EF74F0033E3E6 /* BlobTests */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | B71ABF9F290EF74F0033E3E6 /* BlobTests.swift */, 163 | ); 164 | path = BlobTests; 165 | sourceTree = ""; 166 | }; 167 | B7ADAA452B277C1500072CCD /* Views */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | B7ADAA462B277C4900072CCD /* Buttons */, 171 | B7B5AB3C285F8C7500C98B70 /* UploadProgressView.swift */, 172 | B70A98B924DBFD88007DBA7F /* StatusBar.swift */, 173 | B7F0B23B28F3897800BECCBA /* BlobListCell.swift */, 174 | B7BDCC5F286D69B700B668CC /* BlobPreview.swift */, 175 | ); 176 | path = Views; 177 | sourceTree = ""; 178 | }; 179 | B7ADAA462B277C4900072CCD /* Buttons */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | B707556F28444646005D4C51 /* ScreenshotButton.swift */, 183 | B70755712844483C005D4C51 /* UploadButton.swift */, 184 | B71ABFA8290EFD310033E3E6 /* RecordButton.swift */, 185 | B7B5D62A285EF2F500E8BC8A /* CopyButton.swift */, 186 | B7B5D62C285EF39400E8BC8A /* OpenButton.swift */, 187 | B7E7B3C52A2BE6140042F7D5 /* DebugButton.swift */, 188 | B7E7B3C12A2BE3B80042F7D5 /* StatisticsButton.swift */, 189 | B7E7B3C32A2BE53C0042F7D5 /* ConfigurationButton.swift */, 190 | ); 191 | path = Buttons; 192 | sourceTree = ""; 193 | }; 194 | /* End PBXGroup section */ 195 | 196 | /* Begin PBXNativeTarget section */ 197 | B703FDF924D2A89500ADC7A0 /* Blob */ = { 198 | isa = PBXNativeTarget; 199 | buildConfigurationList = B703FE0D24D2A89500ADC7A0 /* Build configuration list for PBXNativeTarget "Blob" */; 200 | buildPhases = ( 201 | B703FDF624D2A89500ADC7A0 /* Sources */, 202 | B703FDF724D2A89500ADC7A0 /* Frameworks */, 203 | B703FDF824D2A89500ADC7A0 /* Resources */, 204 | B7BB43482568EBB400C46F41 /* Embed Frameworks */, 205 | ); 206 | buildRules = ( 207 | ); 208 | dependencies = ( 209 | ); 210 | name = Blob; 211 | packageProductDependencies = ( 212 | B721CFB6285F833B00B945C9 /* FirebaseStorage */, 213 | B721CFB9285F839900B945C9 /* HotKey */, 214 | ); 215 | productName = plop; 216 | productReference = B703FDFA24D2A89500ADC7A0 /* Blob.app */; 217 | productType = "com.apple.product-type.application"; 218 | }; 219 | B71ABF9C290EF74F0033E3E6 /* BlobTests */ = { 220 | isa = PBXNativeTarget; 221 | buildConfigurationList = B71ABFA3290EF74F0033E3E6 /* Build configuration list for PBXNativeTarget "BlobTests" */; 222 | buildPhases = ( 223 | B71ABF99290EF74F0033E3E6 /* Sources */, 224 | B71ABF9A290EF74F0033E3E6 /* Frameworks */, 225 | B71ABF9B290EF74F0033E3E6 /* Resources */, 226 | ); 227 | buildRules = ( 228 | ); 229 | dependencies = ( 230 | B71ABFA2290EF74F0033E3E6 /* PBXTargetDependency */, 231 | ); 232 | name = BlobTests; 233 | productName = BlobTests; 234 | productReference = B71ABF9D290EF74F0033E3E6 /* BlobTests.xctest */; 235 | productType = "com.apple.product-type.bundle.unit-test"; 236 | }; 237 | /* End PBXNativeTarget section */ 238 | 239 | /* Begin PBXProject section */ 240 | B703FDF224D2A89500ADC7A0 /* Project object */ = { 241 | isa = PBXProject; 242 | attributes = { 243 | BuildIndependentTargetsInParallel = YES; 244 | LastSwiftUpdateCheck = 1400; 245 | LastUpgradeCheck = 1510; 246 | TargetAttributes = { 247 | B703FDF924D2A89500ADC7A0 = { 248 | CreatedOnToolsVersion = 12.0; 249 | }; 250 | B71ABF9C290EF74F0033E3E6 = { 251 | CreatedOnToolsVersion = 14.0.1; 252 | TestTargetID = B703FDF924D2A89500ADC7A0; 253 | }; 254 | }; 255 | }; 256 | buildConfigurationList = B703FDF524D2A89500ADC7A0 /* Build configuration list for PBXProject "Blob" */; 257 | compatibilityVersion = "Xcode 9.3"; 258 | developmentRegion = en; 259 | hasScannedForEncodings = 0; 260 | knownRegions = ( 261 | en, 262 | Base, 263 | ); 264 | mainGroup = B703FDF124D2A89500ADC7A0; 265 | packageReferences = ( 266 | B721CFB5285F833B00B945C9 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, 267 | B721CFB8285F839900B945C9 /* XCRemoteSwiftPackageReference "HotKey" */, 268 | ); 269 | productRefGroup = B703FDFB24D2A89500ADC7A0 /* Products */; 270 | projectDirPath = ""; 271 | projectRoot = ""; 272 | targets = ( 273 | B703FDF924D2A89500ADC7A0 /* Blob */, 274 | B71ABF9C290EF74F0033E3E6 /* BlobTests */, 275 | ); 276 | }; 277 | /* End PBXProject section */ 278 | 279 | /* Begin PBXResourcesBuildPhase section */ 280 | B703FDF824D2A89500ADC7A0 /* Resources */ = { 281 | isa = PBXResourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | B703FE0824D2A89500ADC7A0 /* Main.storyboard in Resources */, 285 | B76CD614276E5716005F1A93 /* GoogleService-Info.plist in Resources */, 286 | B703FE0524D2A89500ADC7A0 /* Preview Assets.xcassets in Resources */, 287 | B703FE0224D2A89500ADC7A0 /* Assets.xcassets in Resources */, 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | B71ABF9B290EF74F0033E3E6 /* Resources */ = { 292 | isa = PBXResourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | /* End PBXResourcesBuildPhase section */ 299 | 300 | /* Begin PBXSourcesBuildPhase section */ 301 | B703FDF624D2A89500ADC7A0 /* Sources */ = { 302 | isa = PBXSourcesBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | B7E7B3C02A2BB7380042F7D5 /* Notifications.swift in Sources */, 306 | B703FE0024D2A89500ADC7A0 /* BlobPopover.swift in Sources */, 307 | B7BDCC60286D69B700B668CC /* BlobPreview.swift in Sources */, 308 | B7B5D62D285EF39400E8BC8A /* OpenButton.swift in Sources */, 309 | B70A98BA24DBFD88007DBA7F /* StatusBar.swift in Sources */, 310 | B70755722844483C005D4C51 /* UploadButton.swift in Sources */, 311 | B703FDFE24D2A89500ADC7A0 /* AppDelegate.swift in Sources */, 312 | B7B5D62B285EF2F500E8BC8A /* CopyButton.swift in Sources */, 313 | B7E7B3C22A2BE3B80042F7D5 /* StatisticsButton.swift in Sources */, 314 | B707557028444646005D4C51 /* ScreenshotButton.swift in Sources */, 315 | B70755742844499E005D4C51 /* Utils.swift in Sources */, 316 | B7E7B3C42A2BE53C0042F7D5 /* ConfigurationButton.swift in Sources */, 317 | B7B5AB3D285F8C7500C98B70 /* UploadProgressView.swift in Sources */, 318 | B7E7B3C62A2BE6140042F7D5 /* DebugButton.swift in Sources */, 319 | B71ABFA9290EFD310033E3E6 /* RecordButton.swift in Sources */, 320 | B7F0B23C28F3897800BECCBA /* BlobListCell.swift in Sources */, 321 | B7B5D62F285EF8D200E8BC8A /* Constants.swift in Sources */, 322 | 2DDA0D25EDF888380547E8C7 /* BlobGlobalState.swift in Sources */, 323 | ); 324 | runOnlyForDeploymentPostprocessing = 0; 325 | }; 326 | B71ABF99290EF74F0033E3E6 /* Sources */ = { 327 | isa = PBXSourcesBuildPhase; 328 | buildActionMask = 2147483647; 329 | files = ( 330 | B71ABFA0290EF74F0033E3E6 /* BlobTests.swift in Sources */, 331 | B71ABFA7290EF9300033E3E6 /* Utils.swift in Sources */, 332 | ); 333 | runOnlyForDeploymentPostprocessing = 0; 334 | }; 335 | /* End PBXSourcesBuildPhase section */ 336 | 337 | /* Begin PBXTargetDependency section */ 338 | B71ABFA2290EF74F0033E3E6 /* PBXTargetDependency */ = { 339 | isa = PBXTargetDependency; 340 | target = B703FDF924D2A89500ADC7A0 /* Blob */; 341 | targetProxy = B71ABFA1290EF74F0033E3E6 /* PBXContainerItemProxy */; 342 | }; 343 | /* End PBXTargetDependency section */ 344 | 345 | /* Begin PBXVariantGroup section */ 346 | B703FE0624D2A89500ADC7A0 /* Main.storyboard */ = { 347 | isa = PBXVariantGroup; 348 | children = ( 349 | B703FE0724D2A89500ADC7A0 /* Base */, 350 | ); 351 | name = Main.storyboard; 352 | sourceTree = ""; 353 | }; 354 | /* End PBXVariantGroup section */ 355 | 356 | /* Begin XCBuildConfiguration section */ 357 | B703FE0B24D2A89500ADC7A0 /* Debug */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | ALWAYS_SEARCH_USER_PATHS = NO; 361 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 362 | CLANG_ANALYZER_NONNULL = YES; 363 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 364 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 365 | CLANG_CXX_LIBRARY = "libc++"; 366 | CLANG_ENABLE_MODULES = YES; 367 | CLANG_ENABLE_OBJC_ARC = YES; 368 | CLANG_ENABLE_OBJC_WEAK = YES; 369 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 370 | CLANG_WARN_BOOL_CONVERSION = YES; 371 | CLANG_WARN_COMMA = YES; 372 | CLANG_WARN_CONSTANT_CONVERSION = YES; 373 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 374 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 375 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 376 | CLANG_WARN_EMPTY_BODY = YES; 377 | CLANG_WARN_ENUM_CONVERSION = YES; 378 | CLANG_WARN_INFINITE_RECURSION = YES; 379 | CLANG_WARN_INT_CONVERSION = YES; 380 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 381 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 382 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 383 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 384 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 385 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 386 | CLANG_WARN_STRICT_PROTOTYPES = YES; 387 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 388 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 389 | CLANG_WARN_UNREACHABLE_CODE = YES; 390 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 391 | COPY_PHASE_STRIP = NO; 392 | DEAD_CODE_STRIPPING = YES; 393 | DEBUG_INFORMATION_FORMAT = dwarf; 394 | ENABLE_STRICT_OBJC_MSGSEND = YES; 395 | ENABLE_TESTABILITY = YES; 396 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 397 | GCC_C_LANGUAGE_STANDARD = gnu11; 398 | GCC_DYNAMIC_NO_PIC = NO; 399 | GCC_NO_COMMON_BLOCKS = YES; 400 | GCC_OPTIMIZATION_LEVEL = 0; 401 | GCC_PREPROCESSOR_DEFINITIONS = ( 402 | "DEBUG=1", 403 | "$(inherited)", 404 | ); 405 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 406 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 407 | GCC_WARN_UNDECLARED_SELECTOR = YES; 408 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 409 | GCC_WARN_UNUSED_FUNCTION = YES; 410 | GCC_WARN_UNUSED_VARIABLE = YES; 411 | MACOSX_DEPLOYMENT_TARGET = 14.0; 412 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 413 | MTL_FAST_MATH = YES; 414 | ONLY_ACTIVE_ARCH = YES; 415 | SDKROOT = macosx; 416 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 417 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 418 | }; 419 | name = Debug; 420 | }; 421 | B703FE0C24D2A89500ADC7A0 /* Release */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | ALWAYS_SEARCH_USER_PATHS = NO; 425 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 426 | CLANG_ANALYZER_NONNULL = YES; 427 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 428 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 429 | CLANG_CXX_LIBRARY = "libc++"; 430 | CLANG_ENABLE_MODULES = YES; 431 | CLANG_ENABLE_OBJC_ARC = YES; 432 | CLANG_ENABLE_OBJC_WEAK = YES; 433 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 434 | CLANG_WARN_BOOL_CONVERSION = YES; 435 | CLANG_WARN_COMMA = YES; 436 | CLANG_WARN_CONSTANT_CONVERSION = YES; 437 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 438 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 439 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 440 | CLANG_WARN_EMPTY_BODY = YES; 441 | CLANG_WARN_ENUM_CONVERSION = YES; 442 | CLANG_WARN_INFINITE_RECURSION = YES; 443 | CLANG_WARN_INT_CONVERSION = YES; 444 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 445 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 446 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 447 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 448 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 449 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 450 | CLANG_WARN_STRICT_PROTOTYPES = YES; 451 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 452 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 453 | CLANG_WARN_UNREACHABLE_CODE = YES; 454 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 455 | COPY_PHASE_STRIP = NO; 456 | DEAD_CODE_STRIPPING = YES; 457 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 458 | ENABLE_NS_ASSERTIONS = NO; 459 | ENABLE_STRICT_OBJC_MSGSEND = YES; 460 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 461 | GCC_C_LANGUAGE_STANDARD = gnu11; 462 | GCC_NO_COMMON_BLOCKS = YES; 463 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 464 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 465 | GCC_WARN_UNDECLARED_SELECTOR = YES; 466 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 467 | GCC_WARN_UNUSED_FUNCTION = YES; 468 | GCC_WARN_UNUSED_VARIABLE = YES; 469 | MACOSX_DEPLOYMENT_TARGET = 14.0; 470 | MTL_ENABLE_DEBUG_INFO = NO; 471 | MTL_FAST_MATH = YES; 472 | SDKROOT = macosx; 473 | SWIFT_COMPILATION_MODE = wholemodule; 474 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 475 | }; 476 | name = Release; 477 | }; 478 | B703FE0E24D2A89500ADC7A0 /* Debug */ = { 479 | isa = XCBuildConfiguration; 480 | buildSettings = { 481 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 482 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 483 | CODE_SIGN_ENTITLEMENTS = plop/plop.entitlements; 484 | CODE_SIGN_IDENTITY = "Apple Development"; 485 | CODE_SIGN_STYLE = Automatic; 486 | COMBINE_HIDPI_IMAGES = YES; 487 | CURRENT_PROJECT_VERSION = 1; 488 | DEAD_CODE_STRIPPING = YES; 489 | DEVELOPMENT_ASSET_PATHS = "\"plop/Preview Content\""; 490 | DEVELOPMENT_TEAM = HM6Y2RQS2R; 491 | ENABLE_HARDENED_RUNTIME = YES; 492 | ENABLE_PREVIEWS = YES; 493 | INFOPLIST_FILE = plop/Info.plist; 494 | INFOPLIST_KEY_CFBundleDisplayName = Blob; 495 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; 496 | LD_RUNPATH_SEARCH_PATHS = ( 497 | "$(inherited)", 498 | "@executable_path/../Frameworks", 499 | ); 500 | MACOSX_DEPLOYMENT_TARGET = 14.0; 501 | MARKETING_VERSION = 0.1; 502 | PRODUCT_BUNDLE_IDENTIFIER = atul.plop; 503 | PRODUCT_NAME = "$(TARGET_NAME)"; 504 | SWIFT_VERSION = 5.0; 505 | }; 506 | name = Debug; 507 | }; 508 | B703FE0F24D2A89500ADC7A0 /* Release */ = { 509 | isa = XCBuildConfiguration; 510 | buildSettings = { 511 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 512 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 513 | CODE_SIGN_ENTITLEMENTS = plop/plopRelease.entitlements; 514 | CODE_SIGN_IDENTITY = "Apple Development"; 515 | CODE_SIGN_STYLE = Automatic; 516 | COMBINE_HIDPI_IMAGES = YES; 517 | CURRENT_PROJECT_VERSION = 1; 518 | DEAD_CODE_STRIPPING = YES; 519 | DEVELOPMENT_ASSET_PATHS = "\"plop/Preview Content\""; 520 | DEVELOPMENT_TEAM = HM6Y2RQS2R; 521 | ENABLE_HARDENED_RUNTIME = YES; 522 | ENABLE_PREVIEWS = YES; 523 | INFOPLIST_FILE = plop/Info.plist; 524 | INFOPLIST_KEY_CFBundleDisplayName = Blob; 525 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; 526 | LD_RUNPATH_SEARCH_PATHS = ( 527 | "$(inherited)", 528 | "@executable_path/../Frameworks", 529 | ); 530 | MACOSX_DEPLOYMENT_TARGET = 14.0; 531 | MARKETING_VERSION = 0.1; 532 | PRODUCT_BUNDLE_IDENTIFIER = atul.plop; 533 | PRODUCT_NAME = "$(TARGET_NAME)"; 534 | SWIFT_VERSION = 5.0; 535 | }; 536 | name = Release; 537 | }; 538 | B71ABFA4290EF74F0033E3E6 /* Debug */ = { 539 | isa = XCBuildConfiguration; 540 | buildSettings = { 541 | BUNDLE_LOADER = "$(TEST_HOST)"; 542 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 543 | CODE_SIGN_STYLE = Automatic; 544 | CURRENT_PROJECT_VERSION = 1; 545 | DEVELOPMENT_TEAM = HM6Y2RQS2R; 546 | GENERATE_INFOPLIST_FILE = YES; 547 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 548 | MACOSX_DEPLOYMENT_TARGET = 14.0; 549 | MARKETING_VERSION = 1.0; 550 | PRODUCT_BUNDLE_IDENTIFIER = atul.BlobTests; 551 | PRODUCT_NAME = "$(TARGET_NAME)"; 552 | SDKROOT = auto; 553 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; 554 | SWIFT_EMIT_LOC_STRINGS = NO; 555 | SWIFT_VERSION = 5.0; 556 | TARGETED_DEVICE_FAMILY = "1,2"; 557 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Blob.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Blob"; 558 | }; 559 | name = Debug; 560 | }; 561 | B71ABFA5290EF74F0033E3E6 /* Release */ = { 562 | isa = XCBuildConfiguration; 563 | buildSettings = { 564 | BUNDLE_LOADER = "$(TEST_HOST)"; 565 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 566 | CODE_SIGN_STYLE = Automatic; 567 | CURRENT_PROJECT_VERSION = 1; 568 | DEVELOPMENT_TEAM = HM6Y2RQS2R; 569 | GENERATE_INFOPLIST_FILE = YES; 570 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 571 | MACOSX_DEPLOYMENT_TARGET = 14.0; 572 | MARKETING_VERSION = 1.0; 573 | PRODUCT_BUNDLE_IDENTIFIER = atul.BlobTests; 574 | PRODUCT_NAME = "$(TARGET_NAME)"; 575 | SDKROOT = auto; 576 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; 577 | SWIFT_EMIT_LOC_STRINGS = NO; 578 | SWIFT_VERSION = 5.0; 579 | TARGETED_DEVICE_FAMILY = "1,2"; 580 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Blob.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Blob"; 581 | }; 582 | name = Release; 583 | }; 584 | /* End XCBuildConfiguration section */ 585 | 586 | /* Begin XCConfigurationList section */ 587 | B703FDF524D2A89500ADC7A0 /* Build configuration list for PBXProject "Blob" */ = { 588 | isa = XCConfigurationList; 589 | buildConfigurations = ( 590 | B703FE0B24D2A89500ADC7A0 /* Debug */, 591 | B703FE0C24D2A89500ADC7A0 /* Release */, 592 | ); 593 | defaultConfigurationIsVisible = 0; 594 | defaultConfigurationName = Release; 595 | }; 596 | B703FE0D24D2A89500ADC7A0 /* Build configuration list for PBXNativeTarget "Blob" */ = { 597 | isa = XCConfigurationList; 598 | buildConfigurations = ( 599 | B703FE0E24D2A89500ADC7A0 /* Debug */, 600 | B703FE0F24D2A89500ADC7A0 /* Release */, 601 | ); 602 | defaultConfigurationIsVisible = 0; 603 | defaultConfigurationName = Release; 604 | }; 605 | B71ABFA3290EF74F0033E3E6 /* Build configuration list for PBXNativeTarget "BlobTests" */ = { 606 | isa = XCConfigurationList; 607 | buildConfigurations = ( 608 | B71ABFA4290EF74F0033E3E6 /* Debug */, 609 | B71ABFA5290EF74F0033E3E6 /* Release */, 610 | ); 611 | defaultConfigurationIsVisible = 0; 612 | defaultConfigurationName = Release; 613 | }; 614 | /* End XCConfigurationList section */ 615 | 616 | /* Begin XCRemoteSwiftPackageReference section */ 617 | B721CFB5285F833B00B945C9 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { 618 | isa = XCRemoteSwiftPackageReference; 619 | repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git"; 620 | requirement = { 621 | kind = upToNextMajorVersion; 622 | minimumVersion = 10.14.0; 623 | }; 624 | }; 625 | B721CFB8285F839900B945C9 /* XCRemoteSwiftPackageReference "HotKey" */ = { 626 | isa = XCRemoteSwiftPackageReference; 627 | repositoryURL = "https://github.com/soffes/HotKey"; 628 | requirement = { 629 | kind = upToNextMajorVersion; 630 | minimumVersion = 0.1.3; 631 | }; 632 | }; 633 | /* End XCRemoteSwiftPackageReference section */ 634 | 635 | /* Begin XCSwiftPackageProductDependency section */ 636 | B721CFB6285F833B00B945C9 /* FirebaseStorage */ = { 637 | isa = XCSwiftPackageProductDependency; 638 | package = B721CFB5285F833B00B945C9 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; 639 | productName = FirebaseStorage; 640 | }; 641 | B721CFB9285F839900B945C9 /* HotKey */ = { 642 | isa = XCSwiftPackageProductDependency; 643 | package = B721CFB8285F839900B945C9 /* XCRemoteSwiftPackageReference "HotKey" */; 644 | productName = HotKey; 645 | }; 646 | /* End XCSwiftPackageProductDependency section */ 647 | }; 648 | rootObject = B703FDF224D2A89500ADC7A0 /* Project object */; 649 | } 650 | -------------------------------------------------------------------------------- /macos/plop/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | Default 529 | 530 | 531 | 532 | 533 | 534 | 535 | Left to Right 536 | 537 | 538 | 539 | 540 | 541 | 542 | Right to Left 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | Default 554 | 555 | 556 | 557 | 558 | 559 | 560 | Left to Right 561 | 562 | 563 | 564 | 565 | 566 | 567 | Right to Left 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | --------------------------------------------------------------------------------