├── 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 |
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 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
--------------------------------------------------------------------------------