├── screenshot.png ├── images └── screenshot.png ├── clip-save ├── Assets.xcassets │ ├── Contents.json │ ├── AppIcon.appiconset │ │ ├── app-bar.png │ │ └── Contents.json │ ├── screenshoot.imageset │ │ ├── screenshot.png │ │ └── Contents.json │ └── AccentColor.colorset │ │ └── Contents.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── clip_save.entitlements ├── clip_saveApp.swift ├── ShortcutStorage.swift ├── TrayDropView.swift ├── AboutView.swift ├── ShortcutBarView.swift ├── SavedTextListView.swift ├── AppDelegate.swift └── ContentView.swift ├── clip-save.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcuserdata │ └── leandrosimoes.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── xcshareddata │ └── xcschemes │ │ └── clip-save.xcscheme └── project.pbxproj ├── README.md └── LICENSE /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesimoes/clip-save/HEAD/screenshot.png -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesimoes/clip-save/HEAD/images/screenshot.png -------------------------------------------------------------------------------- /clip-save/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /clip-save/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /clip-save/Assets.xcassets/AppIcon.appiconset/app-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesimoes/clip-save/HEAD/clip-save/Assets.xcassets/AppIcon.appiconset/app-bar.png -------------------------------------------------------------------------------- /clip-save/Assets.xcassets/screenshoot.imageset/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesimoes/clip-save/HEAD/clip-save/Assets.xcassets/screenshoot.imageset/screenshot.png -------------------------------------------------------------------------------- /clip-save.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /clip-save/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /clip-save/clip_save.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /clip-save/clip_saveApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // clip_saveApp.swift 3 | // clip-save 4 | // 5 | // Created by Leandro Simoes on 05/04/25. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct MenuBarApp: App { 12 | @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate 13 | 14 | var body: some Scene { 15 | Settings { 16 | EmptyView() 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /clip-save/Assets.xcassets/screenshoot.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "screenshot.png", 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 | -------------------------------------------------------------------------------- /clip-save/ShortcutStorage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShortcutStorage.swift 3 | // clip-save 4 | // 5 | // Created by Leandro Simoes on 12/04/25. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | class ShortcutStorage: ObservableObject { 12 | static let shared = ShortcutStorage() 13 | 14 | @Published var shortcuts: [FileShortcut] = [] 15 | 16 | func addShortcut(_ url: URL) { 17 | let shortcut = FileShortcut(path: url.path) 18 | if !shortcuts.contains(shortcut) && shortcuts.count < 4 { 19 | shortcuts.append(shortcut) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ClipSave – Menu Bar App for macOS 3 | 4 | **ClipSave** is a lightweight macOS menu bar app that lets you quickly save texts, create app shortcuts, and access them with a single click. 5 | 6 | ![Screenshot do app](https://github.com/lesimoes/clip-save/blob/main/images/screenshot.png?raw=true) 7 | 8 | 9 | ## How to use 10 | 11 | 12 | 13 | [![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/Meg_nb1YQe8/0.jpg)](https://www.youtube.com/watch?v=Meg_nb1YQe8) 14 | 15 | 16 | ## Features 17 | 18 | - Save texts and webpage links 19 | - Drag and drop apps and files to create quick shortcuts 20 | 21 | 22 | -------------------------------------------------------------------------------- /clip-save.xcodeproj/xcuserdata/leandrosimoes.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | clip-save.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 9EB0A05E2DA1BC1800C5B145 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /clip-save/TrayDropView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrayDropView.swift 3 | // clip-save 4 | // 5 | // Created by Leandro Simoes on 12/04/25. 6 | // 7 | 8 | import SwiftUI 9 | import AppKit 10 | import UniformTypeIdentifiers 11 | 12 | class TrayDropView: NSView { 13 | var onDropFile: (([URL]) -> Void)? 14 | 15 | override init(frame frameRect: NSRect) { 16 | super.init(frame: frameRect) 17 | registerForDraggedTypes([.fileURL]) 18 | } 19 | 20 | required init?(coder: NSCoder) { 21 | super.init(coder: coder) 22 | registerForDraggedTypes([.fileURL]) 23 | } 24 | 25 | override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { 26 | return .copy 27 | } 28 | 29 | override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { 30 | guard let items = sender.draggingPasteboard.readObjects(forClasses: [NSURL.self], options: nil) as? [URL] else { 31 | return false 32 | } 33 | 34 | onDropFile?(items) 35 | return true 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Leandro Simões 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /clip-save/AboutView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutView.swift 3 | // clip-save 4 | // 5 | // Created by Leandro Simoes on 06/04/25. 6 | // 7 | 8 | import SwiftUI 9 | import AppKit 10 | import UniformTypeIdentifiers 11 | 12 | struct AboutView: View { 13 | 14 | var body: some View { 15 | VStack(spacing: 16) { 16 | Text("Clipsave") 17 | .font(.title2) 18 | .bold() 19 | Text("Version 2.0.1") 20 | .font(.subheadline) 21 | .foregroundColor(.secondary) 22 | Text("A simple free and open source app to save snippets, file and apps") 23 | .multilineTextAlignment(.center) 24 | .font(.footnote) 25 | .fixedSize(horizontal: false, vertical: true) 26 | 27 | Divider() 28 | Text("Created by lesimoes") 29 | .font(.subheadline) 30 | .bold() 31 | Link("Source code", destination: URL(string: "https://github.com/lesimoes/clip-save")!) 32 | .font(.footnote) 33 | } 34 | .padding() 35 | .frame(width: 280, height: 240) 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /clip-save/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "filename" : "app-bar.png", 40 | "idiom" : "mac", 41 | "scale" : "2x", 42 | "size" : "256x256" 43 | }, 44 | { 45 | "idiom" : "mac", 46 | "scale" : "1x", 47 | "size" : "512x512" 48 | }, 49 | { 50 | "idiom" : "mac", 51 | "scale" : "2x", 52 | "size" : "512x512" 53 | } 54 | ], 55 | "info" : { 56 | "author" : "xcode", 57 | "version" : 1 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /clip-save/ShortcutBarView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import AppKit 3 | import UniformTypeIdentifiers 4 | 5 | struct ShortcutBarView: View { 6 | @ObservedObject var storage = ShortcutStorage.shared 7 | 8 | var body: some View { 9 | VStack(alignment: .leading, spacing: 6) { 10 | Text("Drop apps/files here") 11 | .font(.caption) 12 | .foregroundColor(.secondary) 13 | 14 | HStack(spacing: 12) { 15 | ForEach(storage.shortcuts) { shortcut in 16 | Button { 17 | NSWorkspace.shared.open(URL(fileURLWithPath: shortcut.path)) 18 | } label: { 19 | if let icon = shortcut.icon { 20 | Image(nsImage: icon) 21 | .resizable() 22 | .aspectRatio(contentMode: .fit) 23 | .frame(width: 28, height: 28) 24 | .clipShape(RoundedRectangle(cornerRadius: 6)) 25 | } 26 | } 27 | .buttonStyle(PlainButtonStyle()) 28 | .contextMenu { 29 | Button { 30 | let fileURL = URL(fileURLWithPath: shortcut.path) 31 | NSWorkspace.shared.activateFileViewerSelecting([fileURL]) 32 | } label: { 33 | Label("Open Folder", systemImage: "folder") 34 | } 35 | Button(role: .destructive) { 36 | storage.shortcuts.removeAll { $0 == shortcut } 37 | } label: { 38 | Label("Remove", systemImage: "trash") 39 | } 40 | } 41 | } 42 | } 43 | .frame(height: 36) 44 | .padding(6) 45 | .frame(maxWidth: .infinity) 46 | .background( 47 | RoundedRectangle(cornerRadius: 8) 48 | .stroke(style: StrokeStyle(lineWidth: 1, dash: [5])) 49 | .foregroundColor(.gray.opacity(0.4)) 50 | ) 51 | .onDrop(of: [UTType.fileURL.identifier], isTargeted: nil) { providers in 52 | providers.forEach { provider in 53 | provider.loadItem(forTypeIdentifier: UTType.fileURL.identifier, options: nil) { (item, error) in 54 | DispatchQueue.main.async { 55 | if let data = item as? Data, 56 | let url = NSURL(absoluteURLWithDataRepresentation: data, relativeTo: nil) as URL? { 57 | ShortcutStorage.shared.addShortcut(url) 58 | } 59 | } 60 | } 61 | } 62 | return true 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /clip-save.xcodeproj/xcshareddata/xcschemes/clip-save.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 16 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /clip-save/SavedTextListView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import AppKit 3 | import UniformTypeIdentifiers 4 | 5 | struct SavedTextListView: View { 6 | @Binding var inputText: String 7 | @Binding var savedTexts: [String] 8 | var onRunCommand: (String) -> Void 9 | var openWeb: (String) -> Void 10 | 11 | var body: some View { 12 | VStack(alignment: .leading, spacing: 8) { 13 | HStack { 14 | TextField("Type here...", text: $inputText) 15 | .textFieldStyle(RoundedBorderTextFieldStyle()) 16 | .onSubmit { addText() } 17 | 18 | Button("Save") { 19 | addText() 20 | } 21 | } 22 | 23 | Divider() 24 | 25 | List { 26 | ForEach(savedTexts, id: \.self) { item in 27 | HStack { 28 | Text(item) 29 | .lineLimit(1) 30 | .truncationMode(.tail) 31 | Spacer() 32 | 33 | if item.lowercased().hasPrefix("sh:") { 34 | Button { 35 | onRunCommand(item) 36 | } label: { 37 | Image(systemName: "play.circle") 38 | } 39 | .buttonStyle(BorderlessButtonStyle()) 40 | } 41 | 42 | if item.lowercased().hasPrefix("web:") || item.lowercased().hasPrefix("http://") || item.lowercased().hasPrefix("https://") { 43 | Button { 44 | openWeb(item) 45 | } label: { 46 | Image(systemName: "play.circle") 47 | } 48 | .buttonStyle(BorderlessButtonStyle()) 49 | } 50 | 51 | Button { 52 | let pasteboard = NSPasteboard.general 53 | pasteboard.clearContents() 54 | pasteboard.setString(item, forType: .string) 55 | } label: { 56 | Image(systemName: "doc.on.doc") 57 | } 58 | .buttonStyle(BorderlessButtonStyle()) 59 | 60 | Button { 61 | savedTexts.removeAll { $0 == item } 62 | } label: { 63 | Image(systemName: "trash") 64 | } 65 | .buttonStyle(BorderlessButtonStyle()) 66 | } 67 | } 68 | .onMove(perform: moveItem) 69 | } 70 | .frame(height: 150) 71 | } 72 | } 73 | 74 | func addText() { 75 | let trimmed = inputText.trimmingCharacters(in: .whitespacesAndNewlines) 76 | if !trimmed.isEmpty { 77 | savedTexts.insert(trimmed, at: 0) 78 | inputText = "" 79 | } 80 | } 81 | 82 | func moveItem(from source: IndexSet, to destination: Int) { 83 | savedTexts.move(fromOffsets: source, toOffset: destination) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /clip-save/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // clip-save 4 | // 5 | // Created by Leandro Simoes on 05/04/25. 6 | // 7 | 8 | 9 | import Cocoa 10 | import SwiftUI 11 | 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | var statusItem: NSStatusItem? 14 | var popover = NSPopover() 15 | 16 | func applicationDidFinishLaunching(_ notification: Notification) { 17 | statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) 18 | 19 | if let button = statusItem?.button { 20 | button.image = NSImage(systemSymbolName: "paperclip", accessibilityDescription: "App") 21 | button.action = #selector(togglePopover(_:)) 22 | button.target = self 23 | button.sendAction(on: [.leftMouseUp, .rightMouseUp]) 24 | 25 | // Adicione a subview de arrastar e soltar 26 | let dropView = TrayDropView(frame: button.bounds) 27 | dropView.onDropFile = { urls in 28 | DispatchQueue.main.async { 29 | urls.forEach { url in 30 | ShortcutStorage.shared.addShortcut(url) 31 | } 32 | } 33 | } 34 | button.addSubview(dropView) 35 | } 36 | 37 | let contentView = ContentView() 38 | popover.contentSize = NSSize(width: 250, height: 400) 39 | popover.behavior = .transient 40 | popover.contentViewController = NSHostingController(rootView: contentView) 41 | } 42 | 43 | 44 | @objc func togglePopover(_ sender: AnyObject?) { 45 | let event = NSApp.currentEvent 46 | 47 | if event?.type == .rightMouseUp { 48 | showContextMenu() 49 | return 50 | } 51 | 52 | if let button = statusItem?.button { 53 | if popover.isShown { 54 | popover.performClose(sender) 55 | } else { 56 | popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) 57 | } 58 | } 59 | } 60 | 61 | func closePopover () { 62 | popover.close() 63 | } 64 | 65 | func showContextMenu() { 66 | closePopover() 67 | let menu = NSMenu() 68 | 69 | let aboutItem = NSMenuItem(title: "About", action: #selector(aboutPopup), keyEquivalent: "") 70 | aboutItem.target = self; 71 | menu.addItem(aboutItem) 72 | 73 | let exitItem = NSMenuItem(title: "Exit", action: #selector(exitApp), keyEquivalent: "") 74 | exitItem.target = self 75 | menu.addItem(exitItem) 76 | 77 | 78 | if let button = statusItem?.button { 79 | NSMenu.popUpContextMenu(menu, with: NSApp.currentEvent!, for: button) 80 | } 81 | } 82 | 83 | @objc func exitApp() { 84 | NSApp.terminate(nil) 85 | } 86 | 87 | @objc func aboutPopup() { 88 | let aboutPopup = NSWindow( 89 | contentRect: NSRect(x:0, y:0, width: 300, height: 240), 90 | styleMask: [.titled, .closable], 91 | backing: .buffered, 92 | defer: false 93 | ) 94 | 95 | aboutPopup.center() 96 | aboutPopup.title = "About" 97 | aboutPopup.isReleasedWhenClosed = false; 98 | aboutPopup.contentView = NSHostingView(rootView: AboutView()) 99 | aboutPopup.makeKeyAndOrderFront(nil) 100 | } 101 | } 102 | 103 | 104 | -------------------------------------------------------------------------------- /clip-save/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // clip-save 4 | // 5 | // Created by Leandro Simoes on 05/04/25. 6 | // 7 | 8 | import SwiftUI 9 | import AppKit 10 | import UniformTypeIdentifiers 11 | 12 | struct FileShortcut: Identifiable, Equatable { 13 | let id = UUID() 14 | let path: String 15 | 16 | var icon: NSImage? { 17 | NSWorkspace.shared.icon(forFile: path) 18 | } 19 | 20 | var name: String { 21 | URL(fileURLWithPath: path).lastPathComponent 22 | } 23 | } 24 | 25 | struct ContentView: View { 26 | @State private var inputText: String = "" 27 | @State private var savedTexts: [String] = [] 28 | @State private var shortcuts: [FileShortcut] = [] 29 | 30 | var body: some View { 31 | VStack(alignment: .leading, spacing: 12) { 32 | SavedTextListView( 33 | inputText: $inputText, 34 | savedTexts: $savedTexts, 35 | onRunCommand: runTerminalCommand, 36 | openWeb: openWeb 37 | ) 38 | 39 | Spacer() 40 | 41 | ShortcutBarView() 42 | } 43 | .padding() 44 | .frame(width: 250, height: 320) 45 | } 46 | 47 | func addText() { 48 | let trimmed = inputText.trimmingCharacters(in: .whitespacesAndNewlines) 49 | if !trimmed.isEmpty { 50 | savedTexts.insert(trimmed, at: 0) 51 | inputText = "" 52 | } 53 | } 54 | 55 | func handleDrop(providers: [NSItemProvider]) -> Bool { 56 | for provider in providers { 57 | provider.loadItem(forTypeIdentifier: UTType.fileURL.identifier, options: nil) { (item, _) in 58 | DispatchQueue.main.async { 59 | if let data = item as? Data, 60 | let url = NSURL(absoluteURLWithDataRepresentation: data, relativeTo: nil) as URL? { 61 | if shortcuts.count < 4 && !shortcuts.contains(where: { $0.path == url.path }) { 62 | shortcuts.append(FileShortcut(path: url.path)) 63 | } 64 | } 65 | } 66 | } 67 | } 68 | return true 69 | } 70 | 71 | func openWeb(_ item: String) { 72 | var urlString = item.trimmingCharacters(in: .whitespacesAndNewlines) 73 | 74 | if urlString.lowercased().hasPrefix("web:") { 75 | urlString = String(urlString.dropFirst(4)).trimmingCharacters(in: .whitespacesAndNewlines) 76 | } 77 | 78 | if !urlString.hasPrefix("http://") && !urlString.hasPrefix("https://") { 79 | urlString = "https://" + urlString 80 | } 81 | 82 | guard let url = URL(string: urlString), NSWorkspace.shared.open(url) else { 83 | print("Erro: Invalid URL - \(urlString)") 84 | return 85 | } 86 | 87 | } 88 | 89 | func runTerminalCommand(_ item: String) { 90 | guard item.lowercased().hasPrefix("sh:") else { return } 91 | 92 | let command = item.dropFirst(3).trimmingCharacters(in: .whitespacesAndNewlines) 93 | .replacingOccurrences(of: "\"", with: "\\\"") // escapa aspas 94 | 95 | let appleScript = """ 96 | tell application "Terminal" 97 | if not running then launch 98 | activate 99 | end tell 100 | 101 | delay 0.8 102 | 103 | tell application "System Events" 104 | keystroke "\(command)" 105 | key code 36 106 | end tell 107 | """ 108 | 109 | let process = Process() 110 | process.executableURL = URL(fileURLWithPath: "/usr/bin/osascript") 111 | process.arguments = ["-e", appleScript] 112 | 113 | do { 114 | try process.run() 115 | } catch { 116 | print("Erro ao executar AppleScript: \(error)") 117 | } 118 | 119 | } 120 | } 121 | 122 | -------------------------------------------------------------------------------- /clip-save.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 77; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9EB0A0762DA1C63600C5B145 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 9EB0A0742DA1C63100C5B145 /* README.md */; }; 11 | 9EB0A07F2DA1CBE200C5B145 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 9EB0A07E2DA1CBDD00C5B145 /* LICENSE */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXFileReference section */ 15 | 9EB0A05F2DA1BC1800C5B145 /* clip-save.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "clip-save.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 16 | 9EB0A0742DA1C63100C5B145 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 17 | 9EB0A07E2DA1CBDD00C5B145 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 18 | /* End PBXFileReference section */ 19 | 20 | /* Begin PBXFileSystemSynchronizedRootGroup section */ 21 | 9EB0A0612DA1BC1800C5B145 /* clip-save */ = { 22 | isa = PBXFileSystemSynchronizedRootGroup; 23 | path = "clip-save"; 24 | sourceTree = ""; 25 | }; 26 | /* End PBXFileSystemSynchronizedRootGroup section */ 27 | 28 | /* Begin PBXFrameworksBuildPhase section */ 29 | 9EB0A05C2DA1BC1800C5B145 /* Frameworks */ = { 30 | isa = PBXFrameworksBuildPhase; 31 | buildActionMask = 2147483647; 32 | files = ( 33 | ); 34 | runOnlyForDeploymentPostprocessing = 0; 35 | }; 36 | /* End PBXFrameworksBuildPhase section */ 37 | 38 | /* Begin PBXGroup section */ 39 | 9EB0A0562DA1BC1800C5B145 = { 40 | isa = PBXGroup; 41 | children = ( 42 | 9EB0A07E2DA1CBDD00C5B145 /* LICENSE */, 43 | 9EB0A0612DA1BC1800C5B145 /* clip-save */, 44 | 9EB0A0602DA1BC1800C5B145 /* Products */, 45 | 9EB0A0742DA1C63100C5B145 /* README.md */, 46 | ); 47 | sourceTree = ""; 48 | }; 49 | 9EB0A0602DA1BC1800C5B145 /* Products */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | 9EB0A05F2DA1BC1800C5B145 /* clip-save.app */, 53 | ); 54 | name = Products; 55 | sourceTree = ""; 56 | }; 57 | /* End PBXGroup section */ 58 | 59 | /* Begin PBXNativeTarget section */ 60 | 9EB0A05E2DA1BC1800C5B145 /* clip-save */ = { 61 | isa = PBXNativeTarget; 62 | buildConfigurationList = 9EB0A06E2DA1BC1900C5B145 /* Build configuration list for PBXNativeTarget "clip-save" */; 63 | buildPhases = ( 64 | 9EB0A05B2DA1BC1800C5B145 /* Sources */, 65 | 9EB0A05C2DA1BC1800C5B145 /* Frameworks */, 66 | 9EB0A05D2DA1BC1800C5B145 /* Resources */, 67 | ); 68 | buildRules = ( 69 | ); 70 | dependencies = ( 71 | ); 72 | fileSystemSynchronizedGroups = ( 73 | 9EB0A0612DA1BC1800C5B145 /* clip-save */, 74 | ); 75 | name = "clip-save"; 76 | packageProductDependencies = ( 77 | ); 78 | productName = "clip-save"; 79 | productReference = 9EB0A05F2DA1BC1800C5B145 /* clip-save.app */; 80 | productType = "com.apple.product-type.application"; 81 | }; 82 | /* End PBXNativeTarget section */ 83 | 84 | /* Begin PBXProject section */ 85 | 9EB0A0572DA1BC1800C5B145 /* Project object */ = { 86 | isa = PBXProject; 87 | attributes = { 88 | BuildIndependentTargetsInParallel = 1; 89 | LastSwiftUpdateCheck = 1620; 90 | LastUpgradeCheck = 1620; 91 | TargetAttributes = { 92 | 9EB0A05E2DA1BC1800C5B145 = { 93 | CreatedOnToolsVersion = 16.2; 94 | }; 95 | }; 96 | }; 97 | buildConfigurationList = 9EB0A05A2DA1BC1800C5B145 /* Build configuration list for PBXProject "clip-save" */; 98 | developmentRegion = en; 99 | hasScannedForEncodings = 0; 100 | knownRegions = ( 101 | en, 102 | Base, 103 | ); 104 | mainGroup = 9EB0A0562DA1BC1800C5B145; 105 | minimizedProjectReferenceProxies = 1; 106 | preferredProjectObjectVersion = 77; 107 | productRefGroup = 9EB0A0602DA1BC1800C5B145 /* Products */; 108 | projectDirPath = ""; 109 | projectRoot = ""; 110 | targets = ( 111 | 9EB0A05E2DA1BC1800C5B145 /* clip-save */, 112 | ); 113 | }; 114 | /* End PBXProject section */ 115 | 116 | /* Begin PBXResourcesBuildPhase section */ 117 | 9EB0A05D2DA1BC1800C5B145 /* Resources */ = { 118 | isa = PBXResourcesBuildPhase; 119 | buildActionMask = 2147483647; 120 | files = ( 121 | 9EB0A0762DA1C63600C5B145 /* README.md in Resources */, 122 | 9EB0A07F2DA1CBE200C5B145 /* LICENSE in Resources */, 123 | ); 124 | runOnlyForDeploymentPostprocessing = 0; 125 | }; 126 | /* End PBXResourcesBuildPhase section */ 127 | 128 | /* Begin PBXSourcesBuildPhase section */ 129 | 9EB0A05B2DA1BC1800C5B145 /* Sources */ = { 130 | isa = PBXSourcesBuildPhase; 131 | buildActionMask = 2147483647; 132 | files = ( 133 | ); 134 | runOnlyForDeploymentPostprocessing = 0; 135 | }; 136 | /* End PBXSourcesBuildPhase section */ 137 | 138 | /* Begin XCBuildConfiguration section */ 139 | 9EB0A06C2DA1BC1900C5B145 /* Debug */ = { 140 | isa = XCBuildConfiguration; 141 | buildSettings = { 142 | ALWAYS_SEARCH_USER_PATHS = NO; 143 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 144 | CLANG_ANALYZER_NONNULL = YES; 145 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 146 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 147 | CLANG_ENABLE_MODULES = YES; 148 | CLANG_ENABLE_OBJC_ARC = YES; 149 | CLANG_ENABLE_OBJC_WEAK = YES; 150 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 151 | CLANG_WARN_BOOL_CONVERSION = YES; 152 | CLANG_WARN_COMMA = YES; 153 | CLANG_WARN_CONSTANT_CONVERSION = YES; 154 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 155 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 156 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 157 | CLANG_WARN_EMPTY_BODY = YES; 158 | CLANG_WARN_ENUM_CONVERSION = YES; 159 | CLANG_WARN_INFINITE_RECURSION = YES; 160 | CLANG_WARN_INT_CONVERSION = YES; 161 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 162 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 163 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 164 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 165 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 166 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 167 | CLANG_WARN_STRICT_PROTOTYPES = YES; 168 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 169 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 170 | CLANG_WARN_UNREACHABLE_CODE = YES; 171 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 172 | COPY_PHASE_STRIP = NO; 173 | DEBUG_INFORMATION_FORMAT = dwarf; 174 | ENABLE_STRICT_OBJC_MSGSEND = YES; 175 | ENABLE_TESTABILITY = YES; 176 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 177 | GCC_C_LANGUAGE_STANDARD = gnu17; 178 | GCC_DYNAMIC_NO_PIC = NO; 179 | GCC_NO_COMMON_BLOCKS = YES; 180 | GCC_OPTIMIZATION_LEVEL = 0; 181 | GCC_PREPROCESSOR_DEFINITIONS = ( 182 | "DEBUG=1", 183 | "$(inherited)", 184 | ); 185 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 186 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 187 | GCC_WARN_UNDECLARED_SELECTOR = YES; 188 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 189 | GCC_WARN_UNUSED_FUNCTION = YES; 190 | GCC_WARN_UNUSED_VARIABLE = YES; 191 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 192 | MACOSX_DEPLOYMENT_TARGET = 15.2; 193 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 194 | MTL_FAST_MATH = YES; 195 | ONLY_ACTIVE_ARCH = YES; 196 | SDKROOT = macosx; 197 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; 198 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 199 | }; 200 | name = Debug; 201 | }; 202 | 9EB0A06D2DA1BC1900C5B145 /* Release */ = { 203 | isa = XCBuildConfiguration; 204 | buildSettings = { 205 | ALWAYS_SEARCH_USER_PATHS = NO; 206 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 207 | CLANG_ANALYZER_NONNULL = YES; 208 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 209 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 210 | CLANG_ENABLE_MODULES = YES; 211 | CLANG_ENABLE_OBJC_ARC = YES; 212 | CLANG_ENABLE_OBJC_WEAK = YES; 213 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 214 | CLANG_WARN_BOOL_CONVERSION = YES; 215 | CLANG_WARN_COMMA = YES; 216 | CLANG_WARN_CONSTANT_CONVERSION = YES; 217 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 218 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 219 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 220 | CLANG_WARN_EMPTY_BODY = YES; 221 | CLANG_WARN_ENUM_CONVERSION = YES; 222 | CLANG_WARN_INFINITE_RECURSION = YES; 223 | CLANG_WARN_INT_CONVERSION = YES; 224 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 225 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 226 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 227 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 228 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 229 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 230 | CLANG_WARN_STRICT_PROTOTYPES = YES; 231 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 232 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 233 | CLANG_WARN_UNREACHABLE_CODE = YES; 234 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 235 | COPY_PHASE_STRIP = NO; 236 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 237 | ENABLE_NS_ASSERTIONS = NO; 238 | ENABLE_STRICT_OBJC_MSGSEND = YES; 239 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 240 | GCC_C_LANGUAGE_STANDARD = gnu17; 241 | GCC_NO_COMMON_BLOCKS = YES; 242 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 243 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 244 | GCC_WARN_UNDECLARED_SELECTOR = YES; 245 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 246 | GCC_WARN_UNUSED_FUNCTION = YES; 247 | GCC_WARN_UNUSED_VARIABLE = YES; 248 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 249 | MACOSX_DEPLOYMENT_TARGET = 15.2; 250 | MTL_ENABLE_DEBUG_INFO = NO; 251 | MTL_FAST_MATH = YES; 252 | SDKROOT = macosx; 253 | SWIFT_COMPILATION_MODE = wholemodule; 254 | }; 255 | name = Release; 256 | }; 257 | 9EB0A06F2DA1BC1900C5B145 /* Debug */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 261 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 262 | CODE_SIGN_ENTITLEMENTS = "clip-save/clip_save.entitlements"; 263 | CODE_SIGN_STYLE = Automatic; 264 | COMBINE_HIDPI_IMAGES = YES; 265 | CURRENT_PROJECT_VERSION = 4; 266 | DEVELOPMENT_ASSET_PATHS = "\"clip-save/Preview Content\""; 267 | ENABLE_PREVIEWS = YES; 268 | GENERATE_INFOPLIST_FILE = YES; 269 | INFOPLIST_KEY_LSUIElement = YES; 270 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 271 | LD_RUNPATH_SEARCH_PATHS = ( 272 | "$(inherited)", 273 | "@executable_path/../Frameworks", 274 | ); 275 | MACOSX_DEPLOYMENT_TARGET = 12.4; 276 | MARKETING_VERSION = 2.0.1; 277 | PRODUCT_BUNDLE_IDENTIFIER = "lesimoes.com.clip-save"; 278 | PRODUCT_NAME = "$(TARGET_NAME)"; 279 | SWIFT_EMIT_LOC_STRINGS = YES; 280 | SWIFT_VERSION = 5.0; 281 | }; 282 | name = Debug; 283 | }; 284 | 9EB0A0702DA1BC1900C5B145 /* Release */ = { 285 | isa = XCBuildConfiguration; 286 | buildSettings = { 287 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 288 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 289 | CODE_SIGN_ENTITLEMENTS = "clip-save/clip_save.entitlements"; 290 | CODE_SIGN_STYLE = Automatic; 291 | COMBINE_HIDPI_IMAGES = YES; 292 | CURRENT_PROJECT_VERSION = 4; 293 | DEVELOPMENT_ASSET_PATHS = "\"clip-save/Preview Content\""; 294 | ENABLE_PREVIEWS = YES; 295 | GENERATE_INFOPLIST_FILE = YES; 296 | INFOPLIST_KEY_LSUIElement = YES; 297 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 298 | LD_RUNPATH_SEARCH_PATHS = ( 299 | "$(inherited)", 300 | "@executable_path/../Frameworks", 301 | ); 302 | MACOSX_DEPLOYMENT_TARGET = 12.4; 303 | MARKETING_VERSION = 2.0.1; 304 | PRODUCT_BUNDLE_IDENTIFIER = "lesimoes.com.clip-save"; 305 | PRODUCT_NAME = "$(TARGET_NAME)"; 306 | SWIFT_EMIT_LOC_STRINGS = YES; 307 | SWIFT_VERSION = 5.0; 308 | }; 309 | name = Release; 310 | }; 311 | /* End XCBuildConfiguration section */ 312 | 313 | /* Begin XCConfigurationList section */ 314 | 9EB0A05A2DA1BC1800C5B145 /* Build configuration list for PBXProject "clip-save" */ = { 315 | isa = XCConfigurationList; 316 | buildConfigurations = ( 317 | 9EB0A06C2DA1BC1900C5B145 /* Debug */, 318 | 9EB0A06D2DA1BC1900C5B145 /* Release */, 319 | ); 320 | defaultConfigurationIsVisible = 0; 321 | defaultConfigurationName = Release; 322 | }; 323 | 9EB0A06E2DA1BC1900C5B145 /* Build configuration list for PBXNativeTarget "clip-save" */ = { 324 | isa = XCConfigurationList; 325 | buildConfigurations = ( 326 | 9EB0A06F2DA1BC1900C5B145 /* Debug */, 327 | 9EB0A0702DA1BC1900C5B145 /* Release */, 328 | ); 329 | defaultConfigurationIsVisible = 0; 330 | defaultConfigurationName = Release; 331 | }; 332 | /* End XCConfigurationList section */ 333 | }; 334 | rootObject = 9EB0A0572DA1BC1800C5B145 /* Project object */; 335 | } 336 | --------------------------------------------------------------------------------