├── .ruby-version ├── Cartfile ├── Cartfile.resolved ├── Gemfile ├── ScriptDeckLogo.png ├── .github └── FUNDING.yml ├── RemoteAssets ├── step1.png ├── step2.png ├── step3.png ├── step4.gif ├── step5.png └── step6.png ├── ScriptDeck ├── Supporting Files │ └── Assets.xcassets │ │ ├── Contents.json │ │ ├── largeIcon.imageset │ │ ├── 32.png │ │ ├── 64.png │ │ ├── 128.png │ │ └── Contents.json │ │ ├── terminal.imageset │ │ ├── 16.png │ │ ├── 32.png │ │ ├── 16-1.png │ │ ├── 32-1.png │ │ ├── terminal16_dark.png │ │ ├── terminal32_dark.png │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ ├── icon_16@1x.png │ │ ├── icon_16@2x.png │ │ ├── icon_32@1x.png │ │ ├── icon_32@2x.png │ │ ├── icon_128@1x.png │ │ ├── icon_128@2x.png │ │ ├── icon_256@1x.png │ │ ├── icon_256@2x.png │ │ ├── icon_512@1x.png │ │ ├── icon_512@2x.png │ │ └── Contents.json ├── ShellScriptModel.swift ├── ScriptDeck.entitlements ├── Utilites │ ├── UserDefaults+ScriptDeck.swift │ ├── PreferencesStyle+UserDefaults.swift │ ├── WebViewPreloader.swift │ └── ScriptDeckStore.swift ├── AboutViewController.swift ├── Info.plist ├── AppDelegate.swift ├── GeneralPreferenceViewController.swift ├── OnboardingController.swift ├── ScriptListController.swift ├── StatusBarHandler.swift ├── ScriptEditorController.swift └── Views │ ├── AboutViewController.xib │ ├── GeneralPreferenceViewController.xib │ └── Base.lproj │ └── Main.storyboard ├── ScriptDeck.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved ├── xcshareddata │ └── xcschemes │ │ └── ScriptDeck.xcscheme └── project.pbxproj ├── fastlane ├── Appfile ├── README.md └── Fastfile ├── LICENSE ├── .gitignore ├── README.md └── Gemfile.lock /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.1 2 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "raspu/Highlightr" 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "raspu/Highlightr" "2.1.1" 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" 4 | -------------------------------------------------------------------------------- /ScriptDeckLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeckLogo.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | patreon: ravitripathi 4 | -------------------------------------------------------------------------------- /RemoteAssets/step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/RemoteAssets/step1.png -------------------------------------------------------------------------------- /RemoteAssets/step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/RemoteAssets/step2.png -------------------------------------------------------------------------------- /RemoteAssets/step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/RemoteAssets/step3.png -------------------------------------------------------------------------------- /RemoteAssets/step4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/RemoteAssets/step4.gif -------------------------------------------------------------------------------- /RemoteAssets/step5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/RemoteAssets/step5.png -------------------------------------------------------------------------------- /RemoteAssets/step6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/RemoteAssets/step6.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/largeIcon.imageset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/largeIcon.imageset/32.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/largeIcon.imageset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/largeIcon.imageset/64.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/16.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/32.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/largeIcon.imageset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/largeIcon.imageset/128.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/16-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/16-1.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/32-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/32-1.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_16@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_16@1x.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_16@2x.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_32@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_32@1x.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_32@2x.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_128@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_128@1x.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_128@2x.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_256@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_256@1x.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_256@2x.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_512@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_512@1x.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/icon_512@2x.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/terminal16_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/terminal16_dark.png -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/terminal32_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravitripathi/ScriptDeck/HEAD/ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/terminal32_dark.png -------------------------------------------------------------------------------- /ScriptDeck.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | # app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app 2 | # apple_id("[[APPLE_ID]]") # Your Apple email address 3 | 4 | 5 | # For more information about the Appfile, see: 6 | # https://docs.fastlane.tools/advanced/#appfile 7 | -------------------------------------------------------------------------------- /ScriptDeck/ShellScriptModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShellScriptModel.swift 3 | // ScriptDeck 4 | // 5 | // Created by Ravi Tripathi on 31/05/20. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ShellScriptModel: Codable { 11 | var name: String 12 | var filePath: String 13 | } 14 | -------------------------------------------------------------------------------- /ScriptDeck.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ScriptDeck/ScriptDeck.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-write 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ScriptDeck.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Preferences", 6 | "repositoryURL": "https://github.com/sindresorhus/Preferences", 7 | "state": { 8 | "branch": null, 9 | "revision": "73664ff9f9faeb40f4b8f2c02d21b4486c82cce9", 10 | "version": "2.0.0" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/largeIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "32.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "64.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "128.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ravi Tripathi 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 | -------------------------------------------------------------------------------- /ScriptDeck/Utilites/UserDefaults+ScriptDeck.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDefaults+ScriptDeck.swift 3 | // ScriptDeck 4 | // 5 | // Created by Ravi Tripathi on 20/06/20. 6 | // 7 | 8 | import Foundation 9 | 10 | extension UserDefaults 11 | { 12 | @objc dynamic var language: String 13 | { 14 | get { 15 | return string(forKey: "language") ?? "bash" 16 | } 17 | set { 18 | set(newValue, forKey: "language") 19 | } 20 | } 21 | 22 | @objc dynamic var theme: String 23 | { 24 | get { 25 | return string(forKey: "theme") ?? "solarized-dark" 26 | } 27 | set { 28 | set(newValue, forKey: "theme") 29 | } 30 | } 31 | 32 | var terminalPath: String { 33 | get { 34 | return string(forKey: "terminalPath") ?? "Terminal" 35 | } 36 | set { 37 | set(newValue, forKey: "terminalPath") 38 | } 39 | } 40 | 41 | var firstTimeLaunch: Bool { 42 | get { 43 | return bool(forKey: "firstTime") 44 | } 45 | set { 46 | set(newValue, forKey: "firstTime") 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ScriptDeck/AboutViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutViewController.swift 3 | // ScriptDeck 4 | // 5 | // Created by Ravi Tripathi on 08/06/20. 6 | // 7 | 8 | import Cocoa 9 | import Preferences 10 | 11 | final class AboutViewController: NSViewController, PreferencePane { 12 | 13 | @IBOutlet weak var repoURLField: NSTextField! 14 | 15 | let preferencePaneIdentifier = Preferences.PaneIdentifier.about 16 | let preferencePaneTitle = "About" 17 | let toolbarItemIcon = NSImage(named: NSImage.infoName)! 18 | override var nibName: NSNib.Name? { "AboutViewController" } 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | let attributes: [NSAttributedString.Key: Any] = [ 23 | NSAttributedString.Key.foregroundColor: NSColor.linkColor, 24 | NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue as AnyObject, 25 | NSAttributedString.Key.link : NSURL(string: "https://github.com/ravitripathi/ScriptDeck")! 26 | ] 27 | repoURLField.alignment = .center 28 | repoURLField.attributedStringValue = NSAttributedString(string: "https://github.com/ravitripathi/ScriptDeck", attributes: attributes) 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ScriptDeck/Utilites/PreferencesStyle+UserDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreferencesStyle+UserDefaults.swift 3 | // ScriptDeck 4 | // 5 | // Created by Ravi Tripathi on 07/06/20. 6 | // 7 | 8 | import Foundation 9 | import Preferences 10 | 11 | // Helpers to write styles to and read them from UserDefaults. 12 | 13 | extension Preferences.Style: RawRepresentable { 14 | public var rawValue: Int { 15 | switch self { 16 | case .toolbarItems: 17 | return 0 18 | case .segmentedControl: 19 | return 1 20 | } 21 | } 22 | 23 | public init?(rawValue: Int) { 24 | switch rawValue { 25 | case 0: 26 | self = .toolbarItems 27 | case 1: 28 | self = .segmentedControl 29 | default: 30 | return nil 31 | } 32 | } 33 | } 34 | 35 | extension Preferences.Style { 36 | static let userDefaultsKey = "preferencesStyle" 37 | 38 | static func preferencesStyleFromUserDefaults(_ userDefaults: UserDefaults = .standard) -> Self { 39 | Self(rawValue: userDefaults.integer(forKey: userDefaultsKey)) 40 | ?? .toolbarItems 41 | } 42 | 43 | func storeInUserDefaults(_ userDefaults: UserDefaults = .standard) { 44 | userDefaults.set(rawValue, forKey: Self.userDefaultsKey) 45 | } 46 | } 47 | 48 | 49 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | 5 | Make sure you have the latest version of the Xcode command line tools installed: 6 | 7 | ``` 8 | xcode-select --install 9 | ``` 10 | 11 | Install _fastlane_ using 12 | ``` 13 | [sudo] gem install fastlane -NV 14 | ``` 15 | or alternatively using `brew cask install fastlane` 16 | 17 | # Available Actions 18 | ## Mac 19 | ### mac shipIt 20 | ``` 21 | fastlane mac shipIt 22 | ``` 23 | Ship it!. bumpType: patch | minor | major 24 | ### mac buildApp 25 | ``` 26 | fastlane mac buildApp 27 | ``` 28 | Build the .app, and store it in ~/Desktop/ScriptDeckBuilds 29 | ### mac bumpVersion 30 | ``` 31 | fastlane mac bumpVersion 32 | ``` 33 | Set version number, create git tag. Types: patch | minor | major 34 | ### mac releaseOnGithub 35 | ``` 36 | fastlane mac releaseOnGithub 37 | ``` 38 | Release built app on github 39 | ### mac updateHomebrewTap 40 | ``` 41 | fastlane mac updateHomebrewTap 42 | ``` 43 | Update ravitripathi/tools homebrew tap 44 | 45 | ---- 46 | 47 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 48 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). 49 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 50 | -------------------------------------------------------------------------------- /ScriptDeck/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | LSApplicationCategoryType 24 | public.app-category.developer-tools 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | LSUIElement 28 | 29 | NSAppTransportSecurity 30 | 31 | NSAllowsArbitraryLoads 32 | 33 | 34 | NSMainStoryboardFile 35 | Main 36 | NSPrincipalClass 37 | NSApplication 38 | NSSupportsAutomaticTermination 39 | 40 | NSSupportsSuddenTermination 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/terminal.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "16.png", 5 | "idiom" : "mac", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "light" 13 | } 14 | ], 15 | "filename" : "16-1.png", 16 | "idiom" : "mac", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "appearances" : [ 21 | { 22 | "appearance" : "luminosity", 23 | "value" : "dark" 24 | } 25 | ], 26 | "filename" : "terminal16_dark.png", 27 | "idiom" : "mac", 28 | "scale" : "1x" 29 | }, 30 | { 31 | "filename" : "32.png", 32 | "idiom" : "mac", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "appearances" : [ 37 | { 38 | "appearance" : "luminosity", 39 | "value" : "light" 40 | } 41 | ], 42 | "filename" : "32-1.png", 43 | "idiom" : "mac", 44 | "scale" : "2x" 45 | }, 46 | { 47 | "appearances" : [ 48 | { 49 | "appearance" : "luminosity", 50 | "value" : "dark" 51 | } 52 | ], 53 | "filename" : "terminal32_dark.png", 54 | "idiom" : "mac", 55 | "scale" : "2x" 56 | } 57 | ], 58 | "info" : { 59 | "author" : "xcode", 60 | "version" : 1 61 | }, 62 | "properties" : { 63 | "template-rendering-intent" : "template" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ScriptDeck/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_16@1x.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "icon_16@2x.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "icon_32@1x.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "icon_32@2x.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "icon_128@1x.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "icon_128@2x.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "icon_256@1x.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "icon_256@2x.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "icon_512@1x.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "icon_512@2x.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ScriptDeck/Utilites/WebViewPreloader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebViewPreloader.swift 3 | // ScriptDeck 4 | // 5 | // Created by Ravi Tripathi on 14/06/20. 6 | // 7 | 8 | import WebKit 9 | 10 | class WebViewPreloader { 11 | 12 | static let shared = WebViewPreloader() 13 | 14 | var webviews = [URL: WKWebView]() 15 | let imagesUrls = [URL(string:"https://github.com/ravitripathi/ScriptDeck/raw/master/ScriptDeckLogo.png")!, 16 | URL(string: "https://github.com/ravitripathi/ScriptDeck/raw/master/RemoteAssets/step1.png")!, 17 | URL(string:"https://github.com/ravitripathi/ScriptDeck/raw/master/RemoteAssets/step2.png")!, 18 | URL(string:"https://github.com/ravitripathi/ScriptDeck/raw/master/RemoteAssets/step3.png")!, 19 | URL(string:"https://github.com/ravitripathi/ScriptDeck/raw/master/RemoteAssets/step4.gif")!, 20 | URL(string:"https://github.com/ravitripathi/ScriptDeck/raw/master/RemoteAssets/step5.png")!] 21 | 22 | /// Registers a web view for preloading. If an webview for that URL already 23 | /// exists, the web view reloads the request 24 | /// 25 | /// - Parameter url: the URL to preload 26 | func preload(url: URL) { 27 | webview(for: url).load(URLRequest(url: url)) 28 | } 29 | 30 | func preloadImages() { 31 | for url in self.imagesUrls { 32 | webview(for: url).load(URLRequest(url: url)) 33 | } 34 | } 35 | 36 | /// Creates or returns an already cached webview for the given URL. 37 | /// If the webview doesn't exist, it gets created and asked to load the URL 38 | /// 39 | /// - Parameter url: the URL to prefecth 40 | /// - Returns: a new or existing web view 41 | func webview(for url: URL) -> WKWebView { 42 | if let cachedWebView = webviews[url] { return cachedWebView } 43 | 44 | let webview = WKWebView(frame: .zero) 45 | webview.load(URLRequest(url: url)) 46 | webview.setValue(false, forKey: "drawsBackground") 47 | webviews[url] = webview 48 | return webview 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ScriptDeck/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ScriptDeck 4 | // 5 | // Created by Ravi Tripathi on 31/05/20. 6 | // 7 | 8 | import Cocoa 9 | import Preferences 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | static var windowController: NSWindowController = { 15 | let storyboard = NSStoryboard(name: "Main", bundle: nil) 16 | let windowController = storyboard.instantiateController(withIdentifier: "MainWindow") as! NSWindowController 17 | return windowController 18 | }() 19 | 20 | func applicationDidFinishLaunching(_ aNotification: Notification) { 21 | StatusBarHandler.shared.setImage() 22 | WebViewPreloader.shared.preloadImages() 23 | ScriptDeckStore.shared.setupFolder() 24 | ScriptDeckStore.shared.setFSObserver() 25 | StatusBarHandler.shared.setupFileSystemListener() 26 | 27 | if !UserDefaults.standard.firstTimeLaunch { 28 | UserDefaults.standard.firstTimeLaunch = true 29 | Onboarding.shared.show() 30 | } 31 | } 32 | } 33 | 34 | 35 | extension NSOpenPanel { 36 | var selectUrl: URL? { 37 | title = "Select Your Terminal App" 38 | allowsMultipleSelection = false 39 | canChooseDirectories = false 40 | canChooseFiles = true 41 | canCreateDirectories = false 42 | // allowedFileTypes = ["jpg","png","pdf","pct", "bmp", "tiff"] // to allow only images, just comment out this line to allow any file type to be selected 43 | return runModal() == .OK ? urls.first : nil 44 | } 45 | // var selectUrls: [URL]? { 46 | // title = "Select Images" 47 | // allowsMultipleSelection = true 48 | // canChooseDirectories = false 49 | // canChooseFiles = true 50 | // canCreateDirectories = false 51 | // allowedFileTypes = ["jpg","png","pdf","pct", "bmp", "tiff"] // to allow only images, just comment out this line to allow any file type to be selected 52 | // return runModal() == .OK ? urls : nil 53 | // } 54 | } 55 | 56 | extension Preferences.PaneIdentifier { 57 | static let general = Self("general") 58 | static let about = Self("about") 59 | } 60 | -------------------------------------------------------------------------------- /.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 | # Accio dependency management 68 | Dependencies/ 69 | .accio/ 70 | 71 | # fastlane 72 | # 73 | # It is recommended to not store the screenshots in the git repo. 74 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 75 | # For more information about the recommended setup visit: 76 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 77 | 78 | fastlane/report.xml 79 | fastlane/Preview.html 80 | fastlane/screenshots/**/*.png 81 | fastlane/test_output 82 | 83 | # Code Injection 84 | # 85 | # After new code Injection tools there's a generated folder /iOSInjectionProject 86 | # https://github.com/johnno1962/injectionforxcode 87 | 88 | iOSInjectionProject/ 89 | 90 | .env.secret 91 | -------------------------------------------------------------------------------- /ScriptDeck/Utilites/ScriptDeckStore.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScriptDeckStore.swift 3 | // ScriptDeck 4 | // 5 | // Created by Ravi Tripathi on 20/06/20. 6 | // 7 | // Responsibilities: 8 | // - Setup the ScriptDeckStore folder 9 | // - Keep the URL handy 10 | // - Observe Changes 11 | 12 | import Foundation 13 | 14 | class ScriptDeckStore: NSObject { 15 | 16 | static let shared = ScriptDeckStore() 17 | 18 | @Published 19 | var liveShellScripts = [ShellScriptModel]() 20 | var observer: DirectoryObserver? 21 | 22 | var url : URL = { 23 | return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("ScriptDeckStore") 24 | }() 25 | 26 | private override init() { 27 | super.init() 28 | fetchAllScript() 29 | } 30 | 31 | func fetchAllScript() { 32 | let enumerator = FileManager.default.enumerator(atPath: self.url.path) 33 | let filePaths = enumerator?.allObjects as! [String] 34 | 35 | var tempContainer = [ShellScriptModel]() 36 | 37 | for scriptPath in filePaths { 38 | if let attributes = try? FileManager.default.attributesOfItem(atPath: self.url.path.appending("/\(scriptPath)")) { 39 | // Check if the file is executable 40 | if (attributes[.posixPermissions] as? NSNumber) == 0o777 { 41 | tempContainer.append(ShellScriptModel(name: scriptPath, filePath: self.url.path.appending("/\(scriptPath)"))) 42 | } 43 | } 44 | } 45 | liveShellScripts = tempContainer 46 | } 47 | 48 | func setupFolder() { 49 | var isDirectory: ObjCBool = true 50 | do { 51 | if !FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) { 52 | try FileManager.default.createDirectory(at: url, withIntermediateDirectories: false, attributes: nil) 53 | } 54 | } catch { 55 | print("Could not create folder. Exiting") 56 | abort() 57 | } 58 | } 59 | 60 | func setFSObserver() { 61 | observer = DirectoryObserver(URL: url, block: fetchAllScript) 62 | } 63 | 64 | 65 | } 66 | 67 | 68 | class DirectoryObserver { 69 | 70 | private let fileDescriptor: CInt 71 | private let source: DispatchSourceProtocol 72 | 73 | deinit { 74 | 75 | self.source.cancel() 76 | close(fileDescriptor) 77 | } 78 | 79 | init(URL: URL, block: @escaping ()->Void) { 80 | 81 | self.fileDescriptor = open(URL.path, O_EVTONLY) 82 | self.source = DispatchSource.makeFileSystemObjectSource(fileDescriptor: self.fileDescriptor, eventMask: .all, queue: DispatchQueue.global()) 83 | self.source.setEventHandler { 84 | block() 85 | } 86 | self.source.resume() 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /ScriptDeck.xcodeproj/xcshareddata/xcschemes/ScriptDeck.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /ScriptDeck/GeneralPreferenceViewController.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import Preferences 3 | import Highlightr 4 | 5 | final class GeneralPreferenceViewController: NSViewController, PreferencePane { 6 | let preferencePaneIdentifier = Preferences.PaneIdentifier.general 7 | let preferencePaneTitle = "General" 8 | let toolbarItemIcon = NSImage(named: NSImage.preferencesGeneralName)! 9 | let codeAS = CodeAttributedString() 10 | var languageObserver: NSKeyValueObservation? 11 | var themeObserver: NSKeyValueObservation? 12 | override var nibName: NSNib.Name? { "GeneralPreferenceViewController" } 13 | 14 | @IBOutlet weak var languagePopUp: NSPopUpButton! 15 | @IBOutlet weak var themePopUp: NSPopUpButton! 16 | 17 | 18 | @IBOutlet weak var defaultTerminalButton: NSButton! 19 | @IBAction func pickDefaultTerminal(_ sender: NSButton) { 20 | if let url = NSOpenPanel().selectUrl { 21 | UserDefaults.standard.terminalPath = url.path 22 | sender.title = url.lastPathComponent 23 | } 24 | } 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | defaultTerminalButton.title = UserDefaults.standard.terminalPath 28 | for theme in codeAS.highlightr.availableThemes() { 29 | themePopUp.addItem(withTitle: theme) 30 | } 31 | for language in codeAS.highlightr.supportedLanguages() { 32 | languagePopUp.addItem(withTitle: language) 33 | } 34 | 35 | self.setPreferredValues() 36 | languagePopUp.action = #selector(self.didSelectLanguage(_:)) 37 | languagePopUp.target = self 38 | 39 | themePopUp.action = #selector(self.didSelectTheme(_:)) 40 | themePopUp.target = self 41 | 42 | languageObserver = UserDefaults.standard.observe(\.language, options: [.new,.old], changeHandler: { [weak self] (object, change) in 43 | self?.setPreferredValues() 44 | }) 45 | 46 | themeObserver = UserDefaults.standard.observe(\.theme, options: [.new,.old], changeHandler: { [weak self] (object, change) in 47 | self?.setPreferredValues() 48 | }) 49 | } 50 | 51 | func setPreferredValues() { 52 | if let bashIndex = codeAS.highlightr.supportedLanguages().firstIndex(where: { (string) -> Bool in 53 | string == UserDefaults.standard.language 54 | }) { 55 | languagePopUp.selectItem(at: bashIndex) 56 | } 57 | 58 | if let themeIndex = codeAS.highlightr.availableThemes().firstIndex(where: { (string) -> Bool in 59 | string == UserDefaults.standard.theme 60 | }) { 61 | themePopUp.selectItem(at: themeIndex) 62 | } 63 | } 64 | 65 | @objc func didSelectLanguage(_ sender: NSPopUpButton) { 66 | if let langString = sender.selectedItem?.title { 67 | UserDefaults.standard.language = langString 68 | } 69 | } 70 | 71 | @objc func didSelectTheme(_ sender: NSPopUpButton) { 72 | if let themeString = sender.selectedItem?.title { 73 | UserDefaults.standard.theme = themeString 74 | } 75 | } 76 | 77 | deinit { 78 | languageObserver?.invalidate() 79 | themeObserver?.invalidate() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![ScriptDeck](https://raw.githubusercontent.com/ravitripathi/ScriptDeck/master/ScriptDeckLogo.png) 2 | 3 |

4 | A macOS status bar app for adding and launching executable scripts 5 |

6 | 7 | 8 |

9 | Swift5 10 | Swift5 11 | 12 | 13 |

14 | 15 | Got scripts scattered across your mac? Tired of changing directories and hunting for that one shell script? ScriptDeck simplifies managing scripts by providing a single place to launch them from, and maintaining a single directory for storing them. 16 | 17 | # Installation 18 | Add the [tools](https://github.com/ravitripathi/homebrew-tools) homebrew tap and install 19 | 20 | ``` 21 | brew tap ravitripathi/tools 22 | brew cask install scriptdeck 23 | ``` 24 | 25 | # Screenshots 26 | 27 | 28 | # Features 29 | - Add shell scripts (or any other executable script) directly from your status bar 30 | - Scripts are saved in `~/Documents/ScriptDeckStore` with executable permissions. 31 | - Background mode runs the script without launching a terminal instance. macOS notifications are triggered at the start and completion of your script. 32 | - Ships with a editor with syntax highlighting, powered by [Highlightr](https://github.com/raspu/Highlightr) 33 | - ScriptDeck monitors files in ~/Documents/ScriptDeckStore and updates automatically when a file is added/removed from the folder. 34 | - Manage your existing scrips from the `Manage Scripts` option in the menu. 35 | - Scripts are launched with the default Terminal.app. If you prefer iTerm or any other terminal app, select it in the `Preferences` window. 36 | 37 | # Existing scripts 38 | 39 | For launching your existing shell scripts present in other directories, use the standard `source` command. 40 | 41 | `source /path/to/your/script` 42 | 43 | # Contributing 44 | 45 | File feature requests, bugs and fixes under [Issues](https://github.com/ravitripathi/ScriptDeck/issues). 46 | 47 | After cloning the repo, run `carthage bootstrap --platform macos` to make sure you have the `Highlighter.framework` and its corresponding dsym file. 48 | 49 | Shoutout to [Vaibhav](https://github.com/vshelke) for suggesting this great name! 50 | 51 | # License 52 | 53 | ScriptDeck is released under the MIT license. [See LICENSE](https://github.com/ravitripathi/ScriptDeck/blob/master/LICENSE) for details. 54 | -------------------------------------------------------------------------------- /ScriptDeck/OnboardingController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingController.swift 3 | // ScriptDeck 4 | // 5 | // Created by Ravi Tripathi on 14/06/20. 6 | // 7 | 8 | import Cocoa 9 | import WebKit 10 | 11 | class OnboardingController: NSViewController { 12 | 13 | 14 | @IBOutlet weak var containerView: NSView! 15 | @IBOutlet weak var descriptionLabel: NSTextField! 16 | @IBOutlet weak var previousButton: NSButton! 17 | @IBOutlet weak var nextButton: NSButton! 18 | 19 | var texts = [ 20 | "Hi There! Let's get started", 21 | "Add a new script by choosing the option from the menu. But before that, remember to grant the permission to access your Documents directory", 22 | "Mention the executable's name along with its extension. If nothing is provided, ScriptDeck will assume it to be .sh. On clicking save, the script is generated with the executable permissions.", 23 | "Click on it to execute it on Terminal. Selecting \"Run in background\" will run the script silently, and trigger macOS notifications on start and completion.", 24 | "That's it! A new terminal window launches with your script running in it", 25 | "Modify the default theme, language or application which executes your scripts from preferences. \n Enjoy using ScriptDeck!" 26 | ] 27 | 28 | var selectedIndex = 0 { 29 | didSet { 30 | previousButton.isHidden = (selectedIndex == 0) 31 | if selectedIndex == WebViewPreloader.shared.imagesUrls.count - 1 { 32 | nextButton.title = "Let's go!" 33 | nextButton.bezelColor = .systemBlue 34 | } 35 | descriptionLabel.stringValue = texts[selectedIndex] 36 | } 37 | } 38 | 39 | override func viewDidLoad() { 40 | super.viewDidLoad() 41 | nextButton.action = #selector(gotoNext) 42 | nextButton.target = self 43 | previousButton.action = #selector(gotoPrev) 44 | previousButton.target = self 45 | previousButton.isHidden = true 46 | descriptionLabel.stringValue = texts[selectedIndex] 47 | set(url: WebViewPreloader.shared.imagesUrls[selectedIndex]) 48 | } 49 | 50 | 51 | func set(url: URL) { 52 | for sub in containerView.subviews { 53 | sub.removeFromSuperview() 54 | } 55 | let cachedWebView = WebViewPreloader.shared.webview(for: url) 56 | cachedWebView.frame = containerView.bounds 57 | containerView.addSubview(cachedWebView) 58 | } 59 | 60 | @objc func gotoNext() { 61 | if selectedIndex < WebViewPreloader.shared.imagesUrls.count-1 { 62 | selectedIndex += 1 63 | set(url: WebViewPreloader.shared.imagesUrls[selectedIndex]) 64 | } else { 65 | Onboarding.shared.close() 66 | } 67 | } 68 | 69 | @objc func gotoPrev() { 70 | if selectedIndex > 0 { 71 | selectedIndex -= 1 72 | set(url: WebViewPreloader.shared.imagesUrls[selectedIndex]) 73 | } 74 | } 75 | } 76 | 77 | class Onboarding { 78 | 79 | static let shared = Onboarding() 80 | 81 | private var windowController: NSWindowController? 82 | 83 | func show() { 84 | let storyboard = NSStoryboard(name: "Main", bundle: nil) 85 | let newWindowController = storyboard.instantiateController(withIdentifier: "OnboardingWindow") as! NSWindowController 86 | windowController = newWindowController 87 | windowController?.showWindow(self) 88 | NSApp.activate(ignoringOtherApps: true) 89 | windowController?.window?.center() 90 | } 91 | 92 | func close() { 93 | windowController?.close() 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /ScriptDeck/ScriptListController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScriptListController.swift 3 | // ScriptDeck 4 | // 5 | // Created by Ravi Tripathi on 21/06/20. 6 | // 7 | 8 | import Cocoa 9 | import Combine 10 | 11 | class ScriptListController: NSViewController, NSTableViewDataSource, NSTableViewDelegate { 12 | 13 | @IBOutlet weak var tableView: NSTableView! 14 | 15 | var dataArray = [ShellScriptModel]() 16 | // let dataArray = ScriptDeckStore.shared.$liveShellScripts 17 | var observer: AnyCancellable? 18 | 19 | @IBAction func showItem(_ sender: NSButton) { 20 | if tableView.selectedRow < dataArray.count { 21 | let item = dataArray[tableView.selectedRow] 22 | let url = URL(fileURLWithPath: item.filePath) 23 | NSWorkspace.shared.open(url) 24 | } 25 | } 26 | 27 | 28 | @IBAction func deleteItem(_ sender: NSButton) { 29 | if tableView.selectedRow < dataArray.count { 30 | let item = dataArray[tableView.selectedRow] 31 | let url = URL(fileURLWithPath: item.filePath) 32 | try? FileManager.default.removeItem(at: url) 33 | } 34 | } 35 | 36 | 37 | override func viewDidLoad() { 38 | super.viewDidLoad() 39 | tableView.dataSource = self 40 | tableView.delegate = self 41 | tableView.usesAlternatingRowBackgroundColors = true 42 | tableView.doubleAction = #selector(doubleClick) 43 | observer = ScriptDeckStore.shared.$liveShellScripts.sink { [weak self] (scripts) in 44 | self?.dataArray = scripts 45 | DispatchQueue.main.async { 46 | self?.tableView.reloadData() 47 | } 48 | } 49 | } 50 | 51 | func numberOfRows(in tableView: NSTableView) -> Int { 52 | return dataArray.count 53 | } 54 | 55 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { 56 | if let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CodeCreatedTableCellView"), owner: self) as? NSTableCellView { 57 | 58 | cellView.textField!.stringValue = dataArray[row].name 59 | return cellView 60 | } else { 61 | // Create a text field for the cell 62 | let textField = NSTextField() 63 | textField.isEditable = false 64 | textField.backgroundColor = NSColor.clear 65 | textField.translatesAutoresizingMaskIntoConstraints = false 66 | textField.isBordered = false 67 | textField.controlSize = NSControl.ControlSize.regular 68 | textField.stringValue = dataArray[row].name 69 | // Create a cell 70 | let newCell = NSTableCellView() 71 | newCell.identifier = NSUserInterfaceItemIdentifier(rawValue: "CodeCreatedTableCellView") 72 | newCell.addSubview(textField) 73 | newCell.textField = textField 74 | 75 | // Constrain the text field within the cell 76 | newCell.addConstraints( 77 | NSLayoutConstraint.constraints(withVisualFormat: "H:|[textField]|", 78 | options: [], 79 | metrics: nil, 80 | views: ["textField" : textField])) 81 | 82 | newCell.addConstraints( 83 | NSLayoutConstraint.constraints(withVisualFormat: "V:|[textField]|", 84 | options: [], 85 | metrics: nil, 86 | views: ["textField" : textField])) 87 | return newCell 88 | } 89 | 90 | 91 | } 92 | 93 | @objc func doubleClick() { 94 | if tableView.selectedRow < dataArray.count { 95 | var command = "open \(dataArray[tableView.selectedRow].filePath) -a " 96 | command.append(contentsOf: UserDefaults.standard.terminalPath) 97 | StatusBarHandler.shared.shell(command) 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | fastlane_require 'dotenv' 2 | 3 | default_platform :mac 4 | 5 | platform :mac do 6 | 7 | before_all do 8 | sh("cp ~/Documents/Arc/Secrets/.env.secret ./ ") 9 | Dotenv.overload '.env.secret' 10 | ENV["BuildFolder"] = "~/Desktop/ScriptDeckBuilds" 11 | end 12 | 13 | desc "Ship it!. bumpType: patch | minor | major" 14 | lane :shipIt do |options| 15 | # check the semantic parameter entered 16 | if !options[:bumpType] 17 | UI.user_error!("Missing bumpType parameter. Allowed: patch | minor | major") 18 | end 19 | # bumpVersion(bumpType: options[:bumpType]) 20 | buildApp 21 | releaseOnGithub 22 | updateHomebrewTap 23 | end 24 | 25 | desc "Build the .app, and store it in ~/Desktop/ScriptDeckBuilds" 26 | lane :buildApp do 27 | gym( 28 | skip_package_pkg: true, 29 | output_directory: ENV["BuildFolder"] 30 | ) 31 | end 32 | 33 | desc "Set version number, create git tag. Types: patch | minor | major" 34 | lane :bumpVersion do |options| 35 | # ensure you are in master branch 36 | ensure_git_branch 37 | 38 | # ensure that master branch is clean 39 | ensure_git_status_clean 40 | 41 | # calculates the new version according to# the semantic version added 42 | type = options[:bumpType] 43 | old = last_git_tag 44 | version = old 45 | 46 | old[0] = '' 47 | oldArr = old.split('.').map{|v| v.to_i} 48 | 49 | if type == "patch" 50 | version = "#{oldArr[0]}.#{oldArr[1]}.#{oldArr[2] + 1}" 51 | elsif type == "minor" 52 | version = "#{oldArr[0]}.#{oldArr[1] + 1}.0" 53 | elsif type == "major" 54 | version = "#{oldArr[0] + 1}.0.0" 55 | end 56 | 57 | if version == old 58 | UI.user_error!("Wrong release type parameter. Enter: patch | minor | major") 59 | end 60 | 61 | # set the new version number 62 | increment_version_number( 63 | version_number: "#{version}", 64 | xcodeproj: "ScriptDeck.xcodeproj" 65 | ) 66 | end 67 | 68 | desc "Release built app on github" 69 | lane :releaseOnGithub do 70 | 71 | # get the last commit comments from Git history# and creates our changelog 72 | comments = changelog_from_git_commits( 73 | between: [last_git_tag, "HEAD"], 74 | pretty: "- %s", 75 | date_format: "short", 76 | match_lightweight_tag: false, 77 | merge_commit_filtering: "exclude_merges" 78 | ) 79 | 80 | # creates a bump version commit 81 | version = get_version_number() 82 | 83 | # commit_version_bump( 84 | # message: "Version bumped to #{version}", 85 | # xcodeproj: "ScriptDeck.xcodeproj", 86 | # no_verify: true, 87 | # force: true 88 | # ) 89 | 90 | # push bump commit 91 | push_to_git_remote( 92 | tags: false 93 | ) 94 | 95 | # create a local tag with the new version 96 | add_git_tag( 97 | message: comments, 98 | tag: "#{version}", 99 | build_number: version 100 | ) 101 | 102 | sh("cp -R #{ENV["BuildFolder"]}/ScriptDeck.app ../ ") 103 | github_release = set_github_release( 104 | repository_name: "ravitripathi/ScriptDeck", 105 | api_token: ENV["GITHUB_TOKEN"], 106 | name: "Release #{version}", 107 | tag_name: "#{version}", 108 | description: comments, 109 | upload_assets: ["ScriptDeck.app"] 110 | ) 111 | end 112 | 113 | desc "Update ravitripathi/tools homebrew tap" 114 | lane :updateHomebrewTap do 115 | sh("git clone https://github.com/ravitripathi/homebrew-tools.git") 116 | Dir.chdir("homebrew-tools") do 117 | sh("./update_scriptdeck.sh") 118 | end 119 | end 120 | 121 | error do |lane, exception| 122 | #print(exception.backtrace) 123 | reset_git_repo(force: true) 124 | sh("rm -rf homebrew-tools") 125 | end 126 | 127 | after_all do |lane, options| 128 | reset_git_repo(force: true) 129 | sh("rm -rf homebrew-tools") 130 | end 131 | end -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.2) 5 | addressable (2.8.0) 6 | public_suffix (>= 2.0.2, < 5.0) 7 | atomos (0.1.3) 8 | aws-eventstream (1.1.0) 9 | aws-partitions (1.329.0) 10 | aws-sdk-core (3.99.2) 11 | aws-eventstream (~> 1, >= 1.0.2) 12 | aws-partitions (~> 1, >= 1.239.0) 13 | aws-sigv4 (~> 1.1) 14 | jmespath (~> 1.0) 15 | aws-sdk-kms (1.34.1) 16 | aws-sdk-core (~> 3, >= 3.99.0) 17 | aws-sigv4 (~> 1.1) 18 | aws-sdk-s3 (1.68.1) 19 | aws-sdk-core (~> 3, >= 3.99.0) 20 | aws-sdk-kms (~> 1) 21 | aws-sigv4 (~> 1.1) 22 | aws-sigv4 (1.1.4) 23 | aws-eventstream (~> 1.0, >= 1.0.2) 24 | babosa (1.0.3) 25 | claide (1.0.3) 26 | colored (1.2) 27 | colored2 (3.1.2) 28 | commander-fastlane (4.4.6) 29 | highline (~> 1.7.2) 30 | declarative (0.0.10) 31 | declarative-option (0.1.0) 32 | digest-crc (0.5.1) 33 | domain_name (0.5.20190701) 34 | unf (>= 0.0.5, < 1.0.0) 35 | dotenv (2.7.5) 36 | emoji_regex (1.0.1) 37 | excon (0.74.0) 38 | faraday (1.0.1) 39 | multipart-post (>= 1.2, < 3) 40 | faraday-cookie_jar (0.0.6) 41 | faraday (>= 0.7.4) 42 | http-cookie (~> 1.0.0) 43 | faraday_middleware (1.0.0) 44 | faraday (~> 1.0) 45 | fastimage (2.1.7) 46 | fastlane (2.149.1) 47 | CFPropertyList (>= 2.3, < 4.0.0) 48 | addressable (>= 2.3, < 3.0.0) 49 | aws-sdk-s3 (~> 1.0) 50 | babosa (>= 1.0.2, < 2.0.0) 51 | bundler (>= 1.12.0, < 3.0.0) 52 | colored 53 | commander-fastlane (>= 4.4.6, < 5.0.0) 54 | dotenv (>= 2.1.1, < 3.0.0) 55 | emoji_regex (>= 0.1, < 2.0) 56 | excon (>= 0.71.0, < 1.0.0) 57 | faraday (>= 0.17, < 2.0) 58 | faraday-cookie_jar (~> 0.0.6) 59 | faraday_middleware (>= 0.13.1, < 2.0) 60 | fastimage (>= 2.1.0, < 3.0.0) 61 | gh_inspector (>= 1.1.2, < 2.0.0) 62 | google-api-client (>= 0.37.0, < 0.39.0) 63 | google-cloud-storage (>= 1.15.0, < 2.0.0) 64 | highline (>= 1.7.2, < 2.0.0) 65 | json (< 3.0.0) 66 | jwt (~> 2.1.0) 67 | mini_magick (>= 4.9.4, < 5.0.0) 68 | multi_xml (~> 0.5) 69 | multipart-post (~> 2.0.0) 70 | plist (>= 3.1.0, < 4.0.0) 71 | public_suffix (~> 2.0.0) 72 | rubyzip (>= 1.3.0, < 2.0.0) 73 | security (= 0.1.3) 74 | simctl (~> 1.6.3) 75 | slack-notifier (>= 2.0.0, < 3.0.0) 76 | terminal-notifier (>= 2.0.0, < 3.0.0) 77 | terminal-table (>= 1.4.5, < 2.0.0) 78 | tty-screen (>= 0.6.3, < 1.0.0) 79 | tty-spinner (>= 0.8.0, < 1.0.0) 80 | word_wrap (~> 1.0.0) 81 | xcodeproj (>= 1.13.0, < 2.0.0) 82 | xcpretty (~> 0.3.0) 83 | xcpretty-travis-formatter (>= 0.0.3) 84 | gh_inspector (1.1.3) 85 | google-api-client (0.38.0) 86 | addressable (~> 2.5, >= 2.5.1) 87 | googleauth (~> 0.9) 88 | httpclient (>= 2.8.1, < 3.0) 89 | mini_mime (~> 1.0) 90 | representable (~> 3.0) 91 | retriable (>= 2.0, < 4.0) 92 | signet (~> 0.12) 93 | google-cloud-core (1.5.0) 94 | google-cloud-env (~> 1.0) 95 | google-cloud-errors (~> 1.0) 96 | google-cloud-env (1.3.2) 97 | faraday (>= 0.17.3, < 2.0) 98 | google-cloud-errors (1.0.1) 99 | google-cloud-storage (1.26.2) 100 | addressable (~> 2.5) 101 | digest-crc (~> 0.4) 102 | google-api-client (~> 0.33) 103 | google-cloud-core (~> 1.2) 104 | googleauth (~> 0.9) 105 | mini_mime (~> 1.0) 106 | googleauth (0.12.0) 107 | faraday (>= 0.17.3, < 2.0) 108 | jwt (>= 1.4, < 3.0) 109 | memoist (~> 0.16) 110 | multi_json (~> 1.11) 111 | os (>= 0.9, < 2.0) 112 | signet (~> 0.14) 113 | highline (1.7.10) 114 | http-cookie (1.0.3) 115 | domain_name (~> 0.5) 116 | httpclient (2.8.3) 117 | jmespath (1.6.1) 118 | json (2.3.0) 119 | jwt (2.1.0) 120 | memoist (0.16.2) 121 | mini_magick (4.10.1) 122 | mini_mime (1.0.2) 123 | multi_json (1.14.1) 124 | multi_xml (0.6.0) 125 | multipart-post (2.0.0) 126 | nanaimo (0.2.6) 127 | naturally (2.2.0) 128 | os (1.1.0) 129 | plist (3.5.0) 130 | public_suffix (2.0.5) 131 | representable (3.0.4) 132 | declarative (< 0.1.0) 133 | declarative-option (< 0.2.0) 134 | uber (< 0.2.0) 135 | retriable (3.1.2) 136 | rouge (2.0.7) 137 | rubyzip (1.3.0) 138 | security (0.1.3) 139 | signet (0.14.0) 140 | addressable (~> 2.3) 141 | faraday (>= 0.17.3, < 2.0) 142 | jwt (>= 1.5, < 3.0) 143 | multi_json (~> 1.10) 144 | simctl (1.6.8) 145 | CFPropertyList 146 | naturally 147 | slack-notifier (2.3.2) 148 | terminal-notifier (2.0.0) 149 | terminal-table (1.8.0) 150 | unicode-display_width (~> 1.1, >= 1.1.1) 151 | tty-cursor (0.7.1) 152 | tty-screen (0.8.0) 153 | tty-spinner (0.9.3) 154 | tty-cursor (~> 0.7) 155 | uber (0.1.0) 156 | unf (0.1.4) 157 | unf_ext 158 | unf_ext (0.0.7.7) 159 | unicode-display_width (1.7.0) 160 | word_wrap (1.0.0) 161 | xcodeproj (1.16.0) 162 | CFPropertyList (>= 2.3.3, < 4.0) 163 | atomos (~> 0.1.3) 164 | claide (>= 1.0.2, < 2.0) 165 | colored2 (~> 3.1) 166 | nanaimo (~> 0.2.6) 167 | xcpretty (0.3.0) 168 | rouge (~> 2.0.7) 169 | xcpretty-travis-formatter (1.0.0) 170 | xcpretty (~> 0.2, >= 0.0.7) 171 | 172 | PLATFORMS 173 | ruby 174 | 175 | DEPENDENCIES 176 | fastlane 177 | 178 | BUNDLED WITH 179 | 2.1.4 180 | -------------------------------------------------------------------------------- /ScriptDeck/StatusBarHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatusBarHandler.swift 3 | // ScriptDeck 4 | // 5 | // Created by Ravi Tripathi on 01/06/20. 6 | // 7 | 8 | import Cocoa 9 | import Preferences 10 | import Combine 11 | 12 | class StatusBarHandler: NSObject { 13 | static let shared = StatusBarHandler() 14 | 15 | let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength) 16 | var runInBackground = false 17 | var runInBackgroundItem: NSMenuItem? 18 | var observer: AnyCancellable? 19 | var shellScripts = [ShellScriptModel]() 20 | 21 | var preferencesStyle: Preferences.Style { 22 | get { .preferencesStyleFromUserDefaults() } 23 | set { 24 | newValue.storeInUserDefaults() 25 | } 26 | } 27 | 28 | lazy var preferences: [PreferencePane] = [ 29 | GeneralPreferenceViewController(), 30 | AboutViewController() 31 | ] 32 | 33 | lazy var preferencesWindowController = PreferencesWindowController( 34 | preferencePanes: preferences, 35 | style: preferencesStyle, 36 | animated: true, 37 | hidesToolbarForSingleItem: false 38 | ) 39 | 40 | func setImage() { 41 | if let button = statusItem.button { 42 | button.image = NSImage(named: "terminal") 43 | } 44 | } 45 | 46 | func setupFileSystemListener() { 47 | observer = ScriptDeckStore.shared.$liveShellScripts.sink { (scripts) in 48 | self.shellScripts = scripts 49 | self.constructMenu() 50 | } 51 | } 52 | 53 | func constructMenu() { 54 | let menu = NSMenu() 55 | for (index, script) in shellScripts.enumerated() { 56 | let item = NSMenuItem(title: script.name, action: #selector(self.launchShell(_:)), keyEquivalent: "") 57 | item.tag = index 58 | item.target = self 59 | menu.addItem(item) 60 | } 61 | for item in getStaticStatusBarItems() { 62 | menu.addItem(item) 63 | } 64 | statusItem.menu = menu 65 | } 66 | 67 | func getStaticStatusBarItems() -> [NSMenuItem]{ 68 | var items = [NSMenuItem]() 69 | items.append(NSMenuItem.separator()) 70 | 71 | runInBackgroundItem = NSMenuItem(title: "Run in background", action: #selector(shouldRunInBackground), keyEquivalent: "B") 72 | runInBackgroundItem?.state = runInBackground ? .on : .off 73 | runInBackgroundItem?.target = self 74 | let addScript = NSMenuItem(title: "Add New Script", action: #selector(self.addNewScript), keyEquivalent: "A") 75 | addScript.target = self 76 | 77 | items.append(addScript) 78 | items.append(runInBackgroundItem!) 79 | items.append(NSMenuItem.separator()) 80 | let pref = NSMenuItem(title: "Preferences", action: #selector(self.showPreferences), keyEquivalent: ",") 81 | pref.target = self 82 | items.append(pref) 83 | 84 | let showList = NSMenuItem(title: "Manage Scripts", action: #selector(self.showList), keyEquivalent: "S") 85 | showList.target = self 86 | items.append(showList) 87 | 88 | let onboarding = NSMenuItem(title: "Show Onboarding", action: #selector(self.onboard), keyEquivalent: "O") 89 | onboarding.target = self 90 | items.append(onboarding) 91 | items.append(NSMenuItem(title: "Quit ScriptDeck", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "Q")) 92 | return items 93 | } 94 | 95 | @objc func showList() { 96 | let storyboard = NSStoryboard(name: "Main", bundle: nil) 97 | let newWindowController = storyboard.instantiateController(withIdentifier: "ScriptListWindow") as! NSWindowController 98 | newWindowController.showWindow(self) 99 | NSApp.activate(ignoringOtherApps: true) 100 | newWindowController.window?.center() 101 | } 102 | 103 | @objc func onboard() { 104 | Onboarding.shared.show() 105 | } 106 | 107 | @objc func showPreferences() { 108 | preferencesWindowController.show(preferencePane: .general) 109 | } 110 | 111 | @objc func shouldRunInBackground() { 112 | runInBackground = !runInBackground 113 | runInBackgroundItem?.state = runInBackground ? .on : .off 114 | } 115 | 116 | @objc func launchShell(_ sender: NSMenuItem) { 117 | let shellScript = shellScripts[sender.tag] 118 | 119 | if !self.runInBackground { 120 | var command = "open \(shellScript.filePath) -a " 121 | command.append(contentsOf: UserDefaults.standard.terminalPath) 122 | shell(command) 123 | } else { 124 | backgroundShell(shellScript) 125 | } 126 | 127 | } 128 | 129 | @objc func addNewScript() { 130 | AppDelegate.windowController.showWindow(self) 131 | NSApp.activate(ignoringOtherApps: true) 132 | AppDelegate.windowController.window?.center() 133 | } 134 | 135 | func checkItermInstalled() -> Bool { 136 | let task = Process() 137 | let pipe = Pipe() 138 | task.standardOutput = pipe 139 | task.arguments = ["-c", "mdfind -name \'kMDItemFSName==\"iTerm.app\"\'"] 140 | task.executableURL = URL(fileURLWithPath: "/bin/bash") 141 | 142 | try! task.run() 143 | let data = pipe.fileHandleForReading.readDataToEndOfFile() 144 | let output = String(data: data, encoding: .utf8)! 145 | return !output.isEmpty 146 | } 147 | 148 | func backgroundShell(_ shellScript: ShellScriptModel) { 149 | //Show notification 150 | let notification = NSUserNotification() 151 | notification.title = "Launched \(shellScript.name)" 152 | notification.soundName = NSUserNotificationDefaultSoundName 153 | NSUserNotificationCenter.default.deliver(notification) 154 | 155 | try! Process.run(URL(fileURLWithPath: "/bin/bash"), arguments: ["-c", shellScript.filePath]) { (process) in 156 | notification.title = "Finished \(shellScript.name)" 157 | notification.soundName = NSUserNotificationDefaultSoundName 158 | NSUserNotificationCenter.default.deliver(notification) 159 | } 160 | } 161 | 162 | 163 | func shell(_ command: String) { 164 | let task = Process() 165 | task.arguments = ["-c", command] 166 | task.executableURL = URL(fileURLWithPath: "/bin/bash") 167 | try! task.run() 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /ScriptDeck/ScriptEditorController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScriptEditorController.swift 3 | // ScriptDeck 4 | // 5 | // Created by Ravi Tripathi on 31/05/20. 6 | // 7 | 8 | import Cocoa 9 | import Highlightr 10 | 11 | class ScriptEditorController: NSViewController { 12 | 13 | @IBOutlet weak var fileNameTextField: NSTextField! 14 | 15 | var scriptTextField: NSTextView! 16 | 17 | @IBOutlet weak var saveButton: NSButton! 18 | let textStorage = CodeAttributedString() 19 | var languageObserver: NSKeyValueObservation? 20 | var themeObserver: NSKeyValueObservation? 21 | @IBOutlet weak var themeDropDown: NSPopUpButton! 22 | @IBOutlet weak var languageDropDown: NSPopUpButton! 23 | @IBOutlet weak var scriptContentTitleLabel: NSTextField! 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | 28 | let layoutManager = NSLayoutManager() 29 | textStorage.addLayoutManager(layoutManager) 30 | 31 | let textContainer = NSTextContainer(size: view.bounds.size) 32 | layoutManager.addTextContainer(textContainer) 33 | 34 | scriptTextField = NSTextView(frame: .zero, textContainer: textContainer) 35 | scriptTextField.translatesAutoresizingMaskIntoConstraints = false 36 | scriptTextField.appearance = NSAppearance(named: .aqua) 37 | scriptTextField.backgroundColor = textStorage.highlightr.theme.themeBackgroundColor 38 | self.view.addSubview(scriptTextField) 39 | 40 | 41 | scriptTextField.topAnchor.constraint(equalTo: scriptContentTitleLabel.bottomAnchor, constant: 10.0).isActive = true 42 | scriptTextField.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 16.0).isActive = true 43 | scriptTextField.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -16.0).isActive = true 44 | scriptTextField.bottomAnchor.constraint(equalTo: saveButton.topAnchor, constant: -16.0).isActive = true 45 | 46 | setupDropDowns() 47 | 48 | languageDropDown.action = #selector(self.didSelectLanguage(_:)) 49 | languageDropDown.target = self 50 | 51 | themeDropDown.action = #selector(self.didSelectTheme(_:)) 52 | themeDropDown.target = self 53 | 54 | languageObserver = UserDefaults.standard.observe(\.language, options: [.new,.old], changeHandler: { [weak self] (object, change) in 55 | self?.setPreferredValues() 56 | }) 57 | 58 | themeObserver = UserDefaults.standard.observe(\.theme, options: [.new,.old], changeHandler: { [weak self] (object, change) in 59 | self?.setPreferredValues() 60 | }) 61 | } 62 | 63 | func setupDropDowns() { 64 | for theme in self.textStorage.highlightr.availableThemes() { 65 | themeDropDown.addItem(withTitle: theme) 66 | } 67 | for language in self.textStorage.highlightr.supportedLanguages() { 68 | languageDropDown.addItem(withTitle: language) 69 | } 70 | setPreferredValues() 71 | } 72 | 73 | func setPreferredValues() { 74 | if let bashIndex = self.textStorage.highlightr.supportedLanguages().firstIndex(where: { (string) -> Bool in 75 | string == UserDefaults.standard.language 76 | }) { 77 | languageDropDown.selectItem(at: bashIndex) 78 | } 79 | 80 | if let themeIndex = self.textStorage.highlightr.availableThemes().firstIndex(where: { (string) -> Bool in 81 | string == UserDefaults.standard.theme 82 | }) { 83 | themeDropDown.selectItem(at: themeIndex) 84 | } 85 | textStorage.language = UserDefaults.standard.language 86 | textStorage.highlightr.setTheme(to: UserDefaults.standard.theme) 87 | scriptTextField.backgroundColor = textStorage.highlightr.theme.themeBackgroundColor 88 | } 89 | 90 | @objc func didSelectLanguage(_ sender: NSPopUpButton) { 91 | if let langString = sender.selectedItem?.title { 92 | textStorage.language = langString 93 | UserDefaults.standard.language = langString 94 | } 95 | } 96 | 97 | @objc func didSelectTheme(_ sender: NSPopUpButton) { 98 | if let themeString = sender.selectedItem?.title { 99 | textStorage.highlightr.setTheme(to: themeString) 100 | scriptTextField.backgroundColor = textStorage.highlightr.theme.themeBackgroundColor 101 | UserDefaults.standard.theme = themeString 102 | } 103 | } 104 | 105 | @IBAction func didTapSave(_ sender: NSButton) { 106 | let alert = NSAlert() 107 | guard !fileNameTextField.stringValue.isEmpty && !scriptTextField.string.isEmpty, 108 | var directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { 109 | alert.messageText = "Missing info" 110 | alert.informativeText = "The filename or the script text seems to be empty" 111 | alert.addButton(withTitle: "OK") 112 | alert.runModal() 113 | return 114 | } 115 | directory.appendPathComponent("ScriptDeckStore") 116 | var fileName = fileNameTextField.stringValue.filter { !$0.isWhitespace } 117 | 118 | // Check if extension, else .sh 119 | if (NSURL(fileURLWithPath: fileName).pathExtension ?? "").isEmpty { 120 | fileName.append(contentsOf: ".sh") 121 | } 122 | let scriptText = scriptTextField.string 123 | // var scriptText = scriptTextField.stringValue.appending("\r\n echo \"Press any key to exit\"") 124 | // scriptText.append("\r\n read -n 1") 125 | let scriptTextData = scriptText.data(using: .utf8) 126 | 127 | let url = directory.appendingPathComponent(fileName, isDirectory: false) 128 | do { 129 | if FileManager.default.fileExists(atPath: url.path) { 130 | try FileManager.default.removeItem(at: url) 131 | } 132 | FileManager.default.createFile(atPath: url.path, contents: scriptTextData, attributes: nil) 133 | var attributes = [FileAttributeKey : Any]() 134 | attributes[.posixPermissions] = 0o777 135 | try FileManager.default.setAttributes(attributes, ofItemAtPath: url.path) 136 | } catch let error { 137 | alert.messageText = "Error!" 138 | alert.informativeText = error.localizedDescription 139 | alert.addButton(withTitle: "OK") 140 | alert.runModal() 141 | return 142 | } 143 | AppDelegate.windowController.close() 144 | alert.messageText = "Done!" 145 | alert.messageText = "Saved your script at \(url.absoluteString)!" 146 | alert.addButton(withTitle: "OK") 147 | alert.runModal() 148 | } 149 | // override var representedObject: Any? { 150 | // didSet { 151 | // // Update the view, if already loaded. 152 | // } 153 | // } 154 | 155 | deinit { 156 | languageObserver?.invalidate() 157 | themeObserver?.invalidate() 158 | } 159 | 160 | } 161 | 162 | -------------------------------------------------------------------------------- /ScriptDeck/Views/AboutViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /ScriptDeck/Views/GeneralPreferenceViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /ScriptDeck.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 411FEF60249F9856004F6A09 /* ScriptListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 411FEF5F249F9856004F6A09 /* ScriptListController.swift */; }; 11 | 4141AD5224A8CF8700404C6C /* Highlightr.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = 4141AD5124A8CF8700404C6C /* Highlightr.framework.dSYM */; }; 12 | 414F4ACA2483C1D500E55291 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 414F4AC92483C1D500E55291 /* AppDelegate.swift */; }; 13 | 414F4ACE2483C1D800E55291 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 414F4ACD2483C1D800E55291 /* Assets.xcassets */; }; 14 | 414F4AD12483C1D800E55291 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 414F4ACF2483C1D800E55291 /* Main.storyboard */; }; 15 | 414F4ADA2484067C00E55291 /* ScriptEditorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 414F4AD92484067C00E55291 /* ScriptEditorController.swift */; }; 16 | 416DC524248D6B40009B01E7 /* GeneralPreferenceViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 416DC522248D6B40009B01E7 /* GeneralPreferenceViewController.xib */; }; 17 | 416DC525248D6B40009B01E7 /* GeneralPreferenceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416DC523248D6B40009B01E7 /* GeneralPreferenceViewController.swift */; }; 18 | 416DC528248D864D009B01E7 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416DC526248D864D009B01E7 /* AboutViewController.swift */; }; 19 | 416DC529248D864D009B01E7 /* AboutViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 416DC527248D864D009B01E7 /* AboutViewController.xib */; }; 20 | 41A45256248D5185008F845F /* Highlightr.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 41E90372248D25FA0060AB93 /* Highlightr.framework */; }; 21 | 41A45257248D5185008F845F /* Highlightr.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 41E90372248D25FA0060AB93 /* Highlightr.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 22 | 41A4525F248D620E008F845F /* PreferencesStyle+UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A4525E248D620E008F845F /* PreferencesStyle+UserDefaults.swift */; }; 23 | 41E9036D248CD0690060AB93 /* Preferences in Frameworks */ = {isa = PBXBuildFile; productRef = 41E9036C248CD0690060AB93 /* Preferences */; }; 24 | 41EB4F32248428700006EFDB /* ShellScriptModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41EB4F31248428700006EFDB /* ShellScriptModel.swift */; }; 25 | 41EB4F3424842F9D0006EFDB /* StatusBarHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41EB4F3324842F9D0006EFDB /* StatusBarHandler.swift */; }; 26 | 41F2D00824966B2E008FE9CF /* OnboardingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41F2D00724966B2E008FE9CF /* OnboardingController.swift */; }; 27 | 41F2D00A2496745F008FE9CF /* WebViewPreloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41F2D0092496745F008FE9CF /* WebViewPreloader.swift */; }; 28 | 41F31DBE249E413400ACD8AE /* UserDefaults+ScriptDeck.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41F31DBD249E413400ACD8AE /* UserDefaults+ScriptDeck.swift */; }; 29 | 41F31DC0249E497E00ACD8AE /* ScriptDeckStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41F31DBF249E497E00ACD8AE /* ScriptDeckStore.swift */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXCopyFilesBuildPhase section */ 33 | 414A0864248D4F4C003C5EDB /* Carthage Copy Files */ = { 34 | isa = PBXCopyFilesBuildPhase; 35 | buildActionMask = 2147483647; 36 | dstPath = ""; 37 | dstSubfolderSpec = 16; 38 | files = ( 39 | ); 40 | name = "Carthage Copy Files"; 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | 41A45258248D5185008F845F /* Embed Frameworks */ = { 44 | isa = PBXCopyFilesBuildPhase; 45 | buildActionMask = 2147483647; 46 | dstPath = ""; 47 | dstSubfolderSpec = 10; 48 | files = ( 49 | 41A45257248D5185008F845F /* Highlightr.framework in Embed Frameworks */, 50 | ); 51 | name = "Embed Frameworks"; 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXCopyFilesBuildPhase section */ 55 | 56 | /* Begin PBXFileReference section */ 57 | 411FEF5F249F9856004F6A09 /* ScriptListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptListController.swift; sourceTree = ""; }; 58 | 4141AD5124A8CF8700404C6C /* Highlightr.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = Highlightr.framework.dSYM; path = Carthage/Build/Mac/Highlightr.framework.dSYM; sourceTree = SOURCE_ROOT; }; 59 | 414A0862248D4E92003C5EDB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 60 | 414F4AC62483C1D500E55291 /* ScriptDeck.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ScriptDeck.app; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | 414F4AC92483C1D500E55291 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 62 | 414F4ACD2483C1D800E55291 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 63 | 414F4AD02483C1D800E55291 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 64 | 414F4AD32483C1D800E55291 /* ScriptDeck.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ScriptDeck.entitlements; sourceTree = ""; }; 65 | 414F4AD92484067C00E55291 /* ScriptEditorController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptEditorController.swift; sourceTree = ""; }; 66 | 416DC522248D6B40009B01E7 /* GeneralPreferenceViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = GeneralPreferenceViewController.xib; sourceTree = ""; }; 67 | 416DC523248D6B40009B01E7 /* GeneralPreferenceViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralPreferenceViewController.swift; sourceTree = ""; }; 68 | 416DC526248D864D009B01E7 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; 69 | 416DC527248D864D009B01E7 /* AboutViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AboutViewController.xib; sourceTree = ""; }; 70 | 41A4525E248D620E008F845F /* PreferencesStyle+UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PreferencesStyle+UserDefaults.swift"; sourceTree = ""; }; 71 | 41E90372248D25FA0060AB93 /* Highlightr.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Highlightr.framework; path = Carthage/Build/Mac/Highlightr.framework; sourceTree = ""; }; 72 | 41EB4F31248428700006EFDB /* ShellScriptModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellScriptModel.swift; sourceTree = ""; }; 73 | 41EB4F3324842F9D0006EFDB /* StatusBarHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarHandler.swift; sourceTree = ""; }; 74 | 41F2D00724966B2E008FE9CF /* OnboardingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingController.swift; sourceTree = ""; }; 75 | 41F2D0092496745F008FE9CF /* WebViewPreloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewPreloader.swift; sourceTree = ""; }; 76 | 41F31DBD249E413400ACD8AE /* UserDefaults+ScriptDeck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+ScriptDeck.swift"; sourceTree = ""; }; 77 | 41F31DBF249E497E00ACD8AE /* ScriptDeckStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptDeckStore.swift; sourceTree = ""; }; 78 | /* End PBXFileReference section */ 79 | 80 | /* Begin PBXFrameworksBuildPhase section */ 81 | 414F4AC32483C1D500E55291 /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 2147483647; 84 | files = ( 85 | 41A45256248D5185008F845F /* Highlightr.framework in Frameworks */, 86 | 41E9036D248CD0690060AB93 /* Preferences in Frameworks */, 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | /* End PBXFrameworksBuildPhase section */ 91 | 92 | /* Begin PBXGroup section */ 93 | 414F4ABD2483C1D500E55291 = { 94 | isa = PBXGroup; 95 | children = ( 96 | 414F4AC82483C1D500E55291 /* ScriptDeck */, 97 | 414F4AC72483C1D500E55291 /* Products */, 98 | 41E90371248D25FA0060AB93 /* Frameworks */, 99 | ); 100 | sourceTree = ""; 101 | }; 102 | 414F4AC72483C1D500E55291 /* Products */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 414F4AC62483C1D500E55291 /* ScriptDeck.app */, 106 | ); 107 | name = Products; 108 | sourceTree = ""; 109 | }; 110 | 414F4AC82483C1D500E55291 /* ScriptDeck */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 414A0862248D4E92003C5EDB /* Info.plist */, 114 | 414F4AD32483C1D800E55291 /* ScriptDeck.entitlements */, 115 | 414F4AC92483C1D500E55291 /* AppDelegate.swift */, 116 | 414F4AD92484067C00E55291 /* ScriptEditorController.swift */, 117 | 41EB4F31248428700006EFDB /* ShellScriptModel.swift */, 118 | 41EB4F3324842F9D0006EFDB /* StatusBarHandler.swift */, 119 | 416DC523248D6B40009B01E7 /* GeneralPreferenceViewController.swift */, 120 | 416DC526248D864D009B01E7 /* AboutViewController.swift */, 121 | 41F2D00724966B2E008FE9CF /* OnboardingController.swift */, 122 | 41A0BCFA249F584A00872A2D /* Views */, 123 | 41A0BCF9249F581700872A2D /* Utilites */, 124 | 41A0BCFB249F58D000872A2D /* Supporting Files */, 125 | 411FEF5F249F9856004F6A09 /* ScriptListController.swift */, 126 | ); 127 | path = ScriptDeck; 128 | sourceTree = ""; 129 | }; 130 | 41A0BCF9249F581700872A2D /* Utilites */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 41F2D0092496745F008FE9CF /* WebViewPreloader.swift */, 134 | 41F31DBD249E413400ACD8AE /* UserDefaults+ScriptDeck.swift */, 135 | 41F31DBF249E497E00ACD8AE /* ScriptDeckStore.swift */, 136 | 41A4525E248D620E008F845F /* PreferencesStyle+UserDefaults.swift */, 137 | ); 138 | path = Utilites; 139 | sourceTree = ""; 140 | }; 141 | 41A0BCFA249F584A00872A2D /* Views */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 416DC527248D864D009B01E7 /* AboutViewController.xib */, 145 | 416DC522248D6B40009B01E7 /* GeneralPreferenceViewController.xib */, 146 | 414F4ACF2483C1D800E55291 /* Main.storyboard */, 147 | ); 148 | path = Views; 149 | sourceTree = ""; 150 | }; 151 | 41A0BCFB249F58D000872A2D /* Supporting Files */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 414F4ACD2483C1D800E55291 /* Assets.xcassets */, 155 | 4141AD5124A8CF8700404C6C /* Highlightr.framework.dSYM */, 156 | ); 157 | path = "Supporting Files"; 158 | sourceTree = ""; 159 | }; 160 | 41E90371248D25FA0060AB93 /* Frameworks */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 41E90372248D25FA0060AB93 /* Highlightr.framework */, 164 | ); 165 | name = Frameworks; 166 | sourceTree = ""; 167 | }; 168 | /* End PBXGroup section */ 169 | 170 | /* Begin PBXNativeTarget section */ 171 | 414F4AC52483C1D500E55291 /* ScriptDeck */ = { 172 | isa = PBXNativeTarget; 173 | buildConfigurationList = 414F4AD62483C1D800E55291 /* Build configuration list for PBXNativeTarget "ScriptDeck" */; 174 | buildPhases = ( 175 | 414F4AC22483C1D500E55291 /* Sources */, 176 | 414F4AC32483C1D500E55291 /* Frameworks */, 177 | 414F4AC42483C1D500E55291 /* Resources */, 178 | 414A0864248D4F4C003C5EDB /* Carthage Copy Files */, 179 | 41A45258248D5185008F845F /* Embed Frameworks */, 180 | ); 181 | buildRules = ( 182 | ); 183 | dependencies = ( 184 | ); 185 | name = ScriptDeck; 186 | packageProductDependencies = ( 187 | 41E9036C248CD0690060AB93 /* Preferences */, 188 | ); 189 | productName = StatusScript; 190 | productReference = 414F4AC62483C1D500E55291 /* ScriptDeck.app */; 191 | productType = "com.apple.product-type.application"; 192 | }; 193 | /* End PBXNativeTarget section */ 194 | 195 | /* Begin PBXProject section */ 196 | 414F4ABE2483C1D500E55291 /* Project object */ = { 197 | isa = PBXProject; 198 | attributes = { 199 | LastSwiftUpdateCheck = 1150; 200 | LastUpgradeCheck = 1150; 201 | TargetAttributes = { 202 | 414F4AC52483C1D500E55291 = { 203 | CreatedOnToolsVersion = 11.5; 204 | }; 205 | }; 206 | }; 207 | buildConfigurationList = 414F4AC12483C1D500E55291 /* Build configuration list for PBXProject "ScriptDeck" */; 208 | compatibilityVersion = "Xcode 9.3"; 209 | developmentRegion = en; 210 | hasScannedForEncodings = 0; 211 | knownRegions = ( 212 | en, 213 | Base, 214 | ); 215 | mainGroup = 414F4ABD2483C1D500E55291; 216 | packageReferences = ( 217 | 41E9036B248CD0690060AB93 /* XCRemoteSwiftPackageReference "Preferences" */, 218 | ); 219 | productRefGroup = 414F4AC72483C1D500E55291 /* Products */; 220 | projectDirPath = ""; 221 | projectRoot = ""; 222 | targets = ( 223 | 414F4AC52483C1D500E55291 /* ScriptDeck */, 224 | ); 225 | }; 226 | /* End PBXProject section */ 227 | 228 | /* Begin PBXResourcesBuildPhase section */ 229 | 414F4AC42483C1D500E55291 /* Resources */ = { 230 | isa = PBXResourcesBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | 414F4ACE2483C1D800E55291 /* Assets.xcassets in Resources */, 234 | 416DC524248D6B40009B01E7 /* GeneralPreferenceViewController.xib in Resources */, 235 | 414F4AD12483C1D800E55291 /* Main.storyboard in Resources */, 236 | 4141AD5224A8CF8700404C6C /* Highlightr.framework.dSYM in Resources */, 237 | 416DC529248D864D009B01E7 /* AboutViewController.xib in Resources */, 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | }; 241 | /* End PBXResourcesBuildPhase section */ 242 | 243 | /* Begin PBXSourcesBuildPhase section */ 244 | 414F4AC22483C1D500E55291 /* Sources */ = { 245 | isa = PBXSourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 41F2D00A2496745F008FE9CF /* WebViewPreloader.swift in Sources */, 249 | 41EB4F3424842F9D0006EFDB /* StatusBarHandler.swift in Sources */, 250 | 41A4525F248D620E008F845F /* PreferencesStyle+UserDefaults.swift in Sources */, 251 | 414F4ADA2484067C00E55291 /* ScriptEditorController.swift in Sources */, 252 | 416DC525248D6B40009B01E7 /* GeneralPreferenceViewController.swift in Sources */, 253 | 41F31DBE249E413400ACD8AE /* UserDefaults+ScriptDeck.swift in Sources */, 254 | 414F4ACA2483C1D500E55291 /* AppDelegate.swift in Sources */, 255 | 416DC528248D864D009B01E7 /* AboutViewController.swift in Sources */, 256 | 41F2D00824966B2E008FE9CF /* OnboardingController.swift in Sources */, 257 | 41F31DC0249E497E00ACD8AE /* ScriptDeckStore.swift in Sources */, 258 | 411FEF60249F9856004F6A09 /* ScriptListController.swift in Sources */, 259 | 41EB4F32248428700006EFDB /* ShellScriptModel.swift in Sources */, 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | /* End PBXSourcesBuildPhase section */ 264 | 265 | /* Begin PBXVariantGroup section */ 266 | 414F4ACF2483C1D800E55291 /* Main.storyboard */ = { 267 | isa = PBXVariantGroup; 268 | children = ( 269 | 414F4AD02483C1D800E55291 /* Base */, 270 | ); 271 | name = Main.storyboard; 272 | sourceTree = ""; 273 | }; 274 | /* End PBXVariantGroup section */ 275 | 276 | /* Begin XCBuildConfiguration section */ 277 | 414F4AD42483C1D800E55291 /* Debug */ = { 278 | isa = XCBuildConfiguration; 279 | buildSettings = { 280 | ALWAYS_SEARCH_USER_PATHS = NO; 281 | CLANG_ANALYZER_NONNULL = YES; 282 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 283 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 284 | CLANG_CXX_LIBRARY = "libc++"; 285 | CLANG_ENABLE_MODULES = YES; 286 | CLANG_ENABLE_OBJC_ARC = YES; 287 | CLANG_ENABLE_OBJC_WEAK = YES; 288 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 289 | CLANG_WARN_BOOL_CONVERSION = YES; 290 | CLANG_WARN_COMMA = YES; 291 | CLANG_WARN_CONSTANT_CONVERSION = YES; 292 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 293 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 294 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 295 | CLANG_WARN_EMPTY_BODY = YES; 296 | CLANG_WARN_ENUM_CONVERSION = YES; 297 | CLANG_WARN_INFINITE_RECURSION = YES; 298 | CLANG_WARN_INT_CONVERSION = YES; 299 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 300 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 301 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 302 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 303 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 304 | CLANG_WARN_STRICT_PROTOTYPES = YES; 305 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 306 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 307 | CLANG_WARN_UNREACHABLE_CODE = YES; 308 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 309 | COPY_PHASE_STRIP = NO; 310 | DEBUG_INFORMATION_FORMAT = dwarf; 311 | ENABLE_STRICT_OBJC_MSGSEND = YES; 312 | ENABLE_TESTABILITY = YES; 313 | GCC_C_LANGUAGE_STANDARD = gnu11; 314 | GCC_DYNAMIC_NO_PIC = NO; 315 | GCC_NO_COMMON_BLOCKS = YES; 316 | GCC_OPTIMIZATION_LEVEL = 0; 317 | GCC_PREPROCESSOR_DEFINITIONS = ( 318 | "DEBUG=1", 319 | "$(inherited)", 320 | ); 321 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 322 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 323 | GCC_WARN_UNDECLARED_SELECTOR = YES; 324 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 325 | GCC_WARN_UNUSED_FUNCTION = YES; 326 | GCC_WARN_UNUSED_VARIABLE = YES; 327 | MACOSX_DEPLOYMENT_TARGET = 10.15; 328 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 329 | MTL_FAST_MATH = YES; 330 | ONLY_ACTIVE_ARCH = YES; 331 | SDKROOT = macosx; 332 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 333 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 334 | }; 335 | name = Debug; 336 | }; 337 | 414F4AD52483C1D800E55291 /* Release */ = { 338 | isa = XCBuildConfiguration; 339 | buildSettings = { 340 | ALWAYS_SEARCH_USER_PATHS = NO; 341 | CLANG_ANALYZER_NONNULL = YES; 342 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 343 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 344 | CLANG_CXX_LIBRARY = "libc++"; 345 | CLANG_ENABLE_MODULES = YES; 346 | CLANG_ENABLE_OBJC_ARC = YES; 347 | CLANG_ENABLE_OBJC_WEAK = YES; 348 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 349 | CLANG_WARN_BOOL_CONVERSION = YES; 350 | CLANG_WARN_COMMA = YES; 351 | CLANG_WARN_CONSTANT_CONVERSION = YES; 352 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 353 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 354 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 355 | CLANG_WARN_EMPTY_BODY = YES; 356 | CLANG_WARN_ENUM_CONVERSION = YES; 357 | CLANG_WARN_INFINITE_RECURSION = YES; 358 | CLANG_WARN_INT_CONVERSION = YES; 359 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 360 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 361 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 362 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 363 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 364 | CLANG_WARN_STRICT_PROTOTYPES = YES; 365 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 366 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 367 | CLANG_WARN_UNREACHABLE_CODE = YES; 368 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 369 | COPY_PHASE_STRIP = NO; 370 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 371 | ENABLE_NS_ASSERTIONS = NO; 372 | ENABLE_STRICT_OBJC_MSGSEND = YES; 373 | GCC_C_LANGUAGE_STANDARD = gnu11; 374 | GCC_NO_COMMON_BLOCKS = YES; 375 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 376 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 377 | GCC_WARN_UNDECLARED_SELECTOR = YES; 378 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 379 | GCC_WARN_UNUSED_FUNCTION = YES; 380 | GCC_WARN_UNUSED_VARIABLE = YES; 381 | MACOSX_DEPLOYMENT_TARGET = 10.15; 382 | MTL_ENABLE_DEBUG_INFO = NO; 383 | MTL_FAST_MATH = YES; 384 | SDKROOT = macosx; 385 | SWIFT_COMPILATION_MODE = wholemodule; 386 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 387 | }; 388 | name = Release; 389 | }; 390 | 414F4AD72483C1D800E55291 /* Debug */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 394 | CODE_SIGN_ENTITLEMENTS = ScriptDeck/ScriptDeck.entitlements; 395 | CODE_SIGN_IDENTITY = "-"; 396 | CODE_SIGN_STYLE = Automatic; 397 | COMBINE_HIDPI_IMAGES = YES; 398 | CURRENT_PROJECT_VERSION = 1; 399 | DEVELOPMENT_TEAM = FABQJ9SY29; 400 | FRAMEWORK_SEARCH_PATHS = ( 401 | "$(inherited)", 402 | "$(PROJECT_DIR)/Carthage/Build/Mac", 403 | ); 404 | INFOPLIST_FILE = ScriptDeck/Info.plist; 405 | LD_RUNPATH_SEARCH_PATHS = ( 406 | "$(inherited)", 407 | "@executable_path/../Frameworks", 408 | ); 409 | MARKETING_VERSION = 2.0.0; 410 | PRODUCT_BUNDLE_IDENTIFIER = ravitripathi.github.io.ScriptDeck; 411 | PRODUCT_NAME = "$(TARGET_NAME)"; 412 | PROVISIONING_PROFILE_SPECIFIER = ""; 413 | SWIFT_VERSION = 5.0; 414 | VERSIONING_SYSTEM = "apple-generic"; 415 | }; 416 | name = Debug; 417 | }; 418 | 414F4AD82483C1D800E55291 /* Release */ = { 419 | isa = XCBuildConfiguration; 420 | buildSettings = { 421 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 422 | CODE_SIGN_ENTITLEMENTS = ScriptDeck/ScriptDeck.entitlements; 423 | CODE_SIGN_IDENTITY = "-"; 424 | CODE_SIGN_STYLE = Automatic; 425 | COMBINE_HIDPI_IMAGES = YES; 426 | CURRENT_PROJECT_VERSION = 1; 427 | DEVELOPMENT_TEAM = FABQJ9SY29; 428 | FRAMEWORK_SEARCH_PATHS = ( 429 | "$(inherited)", 430 | "$(PROJECT_DIR)/Carthage/Build/Mac", 431 | ); 432 | INFOPLIST_FILE = ScriptDeck/Info.plist; 433 | LD_RUNPATH_SEARCH_PATHS = ( 434 | "$(inherited)", 435 | "@executable_path/../Frameworks", 436 | ); 437 | MARKETING_VERSION = 2.0.0; 438 | PRODUCT_BUNDLE_IDENTIFIER = ravitripathi.github.io.ScriptDeck; 439 | PRODUCT_NAME = "$(TARGET_NAME)"; 440 | PROVISIONING_PROFILE_SPECIFIER = ""; 441 | SWIFT_VERSION = 5.0; 442 | VERSIONING_SYSTEM = "apple-generic"; 443 | }; 444 | name = Release; 445 | }; 446 | /* End XCBuildConfiguration section */ 447 | 448 | /* Begin XCConfigurationList section */ 449 | 414F4AC12483C1D500E55291 /* Build configuration list for PBXProject "ScriptDeck" */ = { 450 | isa = XCConfigurationList; 451 | buildConfigurations = ( 452 | 414F4AD42483C1D800E55291 /* Debug */, 453 | 414F4AD52483C1D800E55291 /* Release */, 454 | ); 455 | defaultConfigurationIsVisible = 0; 456 | defaultConfigurationName = Release; 457 | }; 458 | 414F4AD62483C1D800E55291 /* Build configuration list for PBXNativeTarget "ScriptDeck" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 414F4AD72483C1D800E55291 /* Debug */, 462 | 414F4AD82483C1D800E55291 /* Release */, 463 | ); 464 | defaultConfigurationIsVisible = 0; 465 | defaultConfigurationName = Release; 466 | }; 467 | /* End XCConfigurationList section */ 468 | 469 | /* Begin XCRemoteSwiftPackageReference section */ 470 | 41E9036B248CD0690060AB93 /* XCRemoteSwiftPackageReference "Preferences" */ = { 471 | isa = XCRemoteSwiftPackageReference; 472 | repositoryURL = "https://github.com/sindresorhus/Preferences"; 473 | requirement = { 474 | kind = upToNextMajorVersion; 475 | minimumVersion = 2.0.0; 476 | }; 477 | }; 478 | /* End XCRemoteSwiftPackageReference section */ 479 | 480 | /* Begin XCSwiftPackageProductDependency section */ 481 | 41E9036C248CD0690060AB93 /* Preferences */ = { 482 | isa = XCSwiftPackageProductDependency; 483 | package = 41E9036B248CD0690060AB93 /* XCRemoteSwiftPackageReference "Preferences" */; 484 | productName = Preferences; 485 | }; 486 | /* End XCSwiftPackageProductDependency section */ 487 | }; 488 | rootObject = 414F4ABE2483C1D500E55291 /* Project object */; 489 | } 490 | -------------------------------------------------------------------------------- /ScriptDeck/Views/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | Default 530 | 531 | 532 | 533 | 534 | 535 | 536 | Left to Right 537 | 538 | 539 | 540 | 541 | 542 | 543 | Right to Left 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | Default 555 | 556 | 557 | 558 | 559 | 560 | 561 | Left to Right 562 | 563 | 564 | 565 | 566 | 567 | 568 | Right to Left 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 814 | 818 | 819 | 829 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 884 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | --------------------------------------------------------------------------------